1 //
2 //  Copyright (C) 2009-2014  Nick Gasson
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 //
17 
18 #include "IMap.hpp"
19 #include "IQuadTree.hpp"
20 #include "Maths.hpp"
21 #include "ILogger.hpp"
22 #include "ITrackSegment.hpp"
23 #include "IFog.hpp"
24 #include "IXMLParser.hpp"
25 #include "XMLBuilder.hpp"
26 #include "IMesh.hpp"
27 #include "IStation.hpp"
28 #include "IResource.hpp"
29 #include "IScenery.hpp"
30 #include "IConfig.hpp"
31 #include "OpenGLHelper.hpp"
32 #include "ClipVolume.hpp"
33 
34 #include <stdexcept>
35 #include <sstream>
36 #include <cassert>
37 #include <fstream>
38 #include <set>
39 #include <map>
40 
41 #include <boost/filesystem.hpp>
42 #include <boost/cstdint.hpp>
43 #include <boost/lexical_cast.hpp>
44 
45 // A single piece of track, scenery, etc. may be connected to
46 // more than one tile. Anchor<T> is the association class
47 template <class T>
48 class Anchor {
49    typedef shared_ptr<T> TPtr;
50 public:
Anchor(TPtr obj,PointI origin)51    Anchor(TPtr obj, PointI origin)
52       : owned_(obj),
53         last_frame_(-1),
54         origin_(origin)
55    {}
56 
57    // These numbers don't necessarily refer to frames
rendered_on(int f)58    void rendered_on(int f) { last_frame_ = f; }
needs_rendering(int f) const59    bool needs_rendering(int f) const { return f != last_frame_; }
60 
origin() const61    const PointI& origin() const { return origin_; }
62 
get()63    inline TPtr get() { return owned_; }
64 private:
65    TPtr   owned_;
66    int    last_frame_;
67    PointI origin_;
68 };
69 
70 typedef shared_ptr<Anchor<ITrackSegment> > TrackAnchor;
71 typedef shared_ptr<Anchor<IScenery> > SceneryAnchor;
72 
73 class Map : public IMap,
74             public ISectorRenderable,
75             public enable_shared_from_this<Map> {
76    friend class MapLoader;
77 public:
78    Map(IResourcePtr a_res);
79    ~Map();
80 
81    // IMap interface
width() const82    int width() const { return my_width; }
depth() const83    int depth() const { return my_depth; }
height_at() const84    double height_at() const { return 0.0; }
85 
name() const86    string name() const { return resource->name(); }
87 
88    void set_start(int x, int y);
89    void set_start(int x, int y, int dirX, int dirY);
90    void set_grid(bool on_off);
set_pick_mode(bool on_off)91    void set_pick_mode(bool on_off) { in_pick_mode = on_off; }
92 
93    track::Connection start() const;
94    ITrackSegmentPtr track_at(const PointI& a_point) const;
95    IStationPtr station_at(PointI a_point) const;
96    void set_track_at(const PointI& a_point, ITrackSegmentPtr a_track);
97    bool is_valid_track(const PointI& a_point) const;
98    void render(IGraphicsPtr a_context) const;
99    void highlight_tile(PointI point, Colour colour) const;
100    void highlight_vertex(PointI point, Colour colour) const;
101    void reset_map(int a_width, int a_depth);
102    void erase_tile(int x, int y);
103    bool empty_tile(PointI tile) const;
104 
105    void raise_area(const PointI& a_start_pos,
106                    const PointI& a_finish_pos);
107    void lower_area(const PointI& a_start_pos,
108                    const PointI& a_finish_pos);
109    void level_area(PointI a_start_pos, PointI a_finish_pos);
110    void smooth_area(PointI start, PointI finish);
111 
112    void save();
113 
114    IStationPtr extend_station(PointI a_start_pos,
115                               PointI a_finish_pos);
116    float height_at(float x, float y) const;
117    float height_at(PointI where) const;
118    VectorF slope_at(PointI where, track::Direction axis, bool& level) const;
119    VectorF slope_before(PointI where,
120                         track::Direction axis, bool &valid) const;
121    VectorF slope_after(PointI where,
122                        track::Direction axis, bool &valid) const;
123    void add_scenery(PointI where, ISceneryPtr s);
124 
125    // ISectorRenderable interface
126    void render_sector(IGraphicsPtr a_context, int id,
127                       PointI bot_left, PointI top_right);
128    void post_render_sector(IGraphicsPtr a_context, int id,
129                            PointI bot_left, PointI top_right);
130 
131 private:
132    // Tiles on the map
133    struct Tile {
134       // TODO: Better to use a boost::variant here?
135       TrackAnchor   track;     // Track at this location, if any
136       IStationPtr   station;   // Station on this tile, if any
137       SceneryAnchor scenery;   // Scenery, if any
138    } *tiles;
139 
140    // Vertices on the terrain
141    struct HeightMap {
142       VectorF pos, normal;
143 
144       // How many track segments are locking the height at this node
145       int lock_count;
146    } *height_map;
147 
148    static const unsigned TILE_NAME_BASE	= 1000;	 // Base of tile naming
149    static const unsigned NULL_OBJECT	= 0;	 // Non-existent object
150    static const float TILE_HEIGHT;	         // Standard height increment
151 
152    // Meshes for each terrain sector
153    vector<IMeshPtr> terrain_meshes;
154 
index(int x,int y) const155    inline int index(int x, int y) const
156    {
157       assert(x < my_width && y < my_depth && x >= 0 && y >= 0);
158       return x + y*my_width;
159    }
160 
tile_name(int x,int z) const161    inline int tile_name(int x, int z) const
162    {
163       return TILE_NAME_BASE + index(x, z);
164    }
165 
tile_at(int x,int z) const166    inline Tile& tile_at(int x, int z) const
167    {
168       return tiles[index(x, z)];
169    }
170 
tile_at(const PointI & p) const171    inline Tile& tile_at(const PointI& p) const
172    {
173       return tile_at(p.x, p.y);
174    }
175 
height_at(int i) const176    inline HeightMap& height_at(int i) const
177    {
178       assert(i >= 0 && i < (my_width + 1) * (my_depth + 1));
179       return height_map[i];
180    }
181 
is_valid_tileName(unsigned a_name) const182    bool is_valid_tileName(unsigned a_name) const
183    {
184       return a_name >= TILE_NAME_BASE
185          && a_name < TILE_NAME_BASE + my_width * my_depth;
186    }
187 
pick_position(unsigned a_name) const188    PointI pick_position(unsigned a_name) const
189    {
190       assert(is_valid_tileName(a_name));
191 
192       int a = a_name - TILE_NAME_BASE;
193       return make_point(a % my_width, a / my_width);
194    }
195 
196    void write_height_map() const;
197    void save_to(ostream& of);
198    void read_height_map(IResource::Handle a_handle);
199    void tile_vertices(int x, int y, int* indexes) const;
200    void render_pick_sector(PointI bot_left, PointI top_right);
201    void draw_start_location() const;
202    void set_station_at(PointI point, IStationPtr a_station);
203    void render_highlighted_tiles() const;
204    void lock_height_at(PointI p);
205    void unlock_height_at(PointI p);
206 
207    // Mesh modification
208    void build_mesh(int id, PointI bot_left, PointI top_right);
209    bool have_mesh(int id, PointI bot_left, PointI top_right);
210    void dirty_tile(int x, int y);
211 
212    // Terrain modification
213    void change_area_height(const PointI& a_start_pos,
214       const PointI& a_finish_pos, float a_height_delta);
215    void raise_tile(int x, int y, float delta_height);
216    void set_tile_height(int x, int y, float h);
217    void fix_normals(int x, int y);
218    bool raise_will_cover_track(int x, int y) const;
219 
220    int           my_width, my_depth;
221    PointI        start_location;
222    track::Direction start_direction;
223    IQuadTreePtr  quad_tree;
224    IFogPtr       fog;
225    bool          should_draw_grid_lines, in_pick_mode;
226    list<PointI>  dirty_tiles;
227    IResourcePtr  resource;
228    vector<bool>  sea_sectors;
229 
230    // Variables used during rendering
231    mutable int frame_num;
232    mutable vector<tuple<PointI, Colour> > highlighted_tiles;
233 };
234 
235 const float Map::TILE_HEIGHT(0.2f);
236 
Map(IResourcePtr a_res)237 Map::Map(IResourcePtr a_res)
238    : tiles(NULL), height_map(NULL), my_width(0), my_depth(0),
239      start_location(make_point(1, 1)),
240      start_direction(axis::X),
241      should_draw_grid_lines(false), in_pick_mode(false),
242      resource(a_res), frame_num(0)
243 {
244    float far_clip;
245    get_config()->get("FarClip", far_clip);
246    fog = make_fog(0.005f,                  // Density
247                   3.0f * far_clip / 4.0f,  // Start
248                   far_clip);               // End distance
249 }
250 
~Map()251 Map::~Map()
252 {
253    delete tiles;
254 }
255 
track_at(const PointI & point) const256 ITrackSegmentPtr Map::track_at(const PointI& point) const
257 {
258    TrackAnchor ptr = tile_at(point.x, point.y).track;
259    if (ptr)
260       return ptr->get();
261    else {
262       ostringstream ss;
263       ss << "No track segment at " << point;
264       throw runtime_error(ss.str());
265    }
266 }
267 
station_at(PointI point) const268 IStationPtr Map::station_at(PointI point) const
269 {
270    return tile_at(point.x, point.y).station;
271 }
272 
set_station_at(PointI point,IStationPtr station)273 void Map::set_station_at(PointI point, IStationPtr station)
274 {
275    tile_at(point).station = station;
276 }
277 
erase_tile(int x,int y)278 void Map::erase_tile(int x, int y)
279 {
280    Tile& tile = tile_at(x, y);
281 
282    if (tile.track) {
283       // We have to be a bit careful since a piece of track has multiple
284       // endpoints
285 
286       PointList locked;
287       tile.track->get()->get_height_locked(locked);
288 
289       for (auto& p : locked)
290          unlock_height_at(p);
291 
292       PointList covers;
293       tile.track->get()->get_endpoints(covers);
294       tile.track->get()->get_covers(covers);
295 
296       for (auto& p : covers) {
297          tile_at(p.x, p.y).track.reset();
298          dirty_tile(p.x, p.y);
299       }
300    }
301 
302    if (tile.scenery) {
303       // Like track, scenery may cover multiple tiles
304 
305       const PointI size = tile.scenery->get()->size();
306       const PointI& where = tile.scenery->origin();
307 
308       for (int x = 0; x < size.x; x++) {
309          for (int y = 0; y < size.y; y++) {
310             tile_at(where.x + x, where.y + y).scenery.reset();
311             dirty_tile(where.x + x, where.y + y);
312          }
313       }
314    }
315 
316    if (tile.station) {
317       tile.station.reset();
318       dirty_tile(x, y);
319    }
320 }
321 
empty_tile(PointI point) const322 bool Map::empty_tile(PointI point) const
323 {
324    Tile& tile = tile_at(point);
325 
326    return !(tile.track || tile.scenery);
327 }
328 
set_track_at(const PointI & where,ITrackSegmentPtr track)329 void Map::set_track_at(const PointI& where, ITrackSegmentPtr track)
330 {
331    int indexes[4];
332    tile_vertices(where.x, where.y, indexes);
333 
334    float lowest_height = 1.0e20f;
335    for (int i = 0; i < 4; i++)
336       lowest_height = min(height_map[indexes[i]].pos.y, lowest_height);
337 
338    track->set_origin(where.x, where.y, lowest_height);
339 
340    TrackAnchor node(new Anchor<ITrackSegment>(track, where));
341 
342    // Attach the track node to every tile it covers
343    PointList covers;
344    track->get_endpoints(covers);
345    track->get_covers(covers);
346 
347    for (PointList::iterator it = covers.begin();
348         it != covers.end(); ++it) {
349       tile_at((*it).x, (*it).y).track = node;
350 
351       dirty_tile((*it).x, (*it).y);
352    }
353 
354    // Lock every height node touched by this track segment
355    PointList locked;
356    track->get_height_locked(locked);
357 
358    for (PointList::iterator it = locked.begin();
359         it != locked.end(); ++it)
360       lock_height_at(*it);
361 }
362 
is_valid_track(const PointI & where) const363 bool Map::is_valid_track(const PointI& where) const
364 {
365    if (where.x < 0 || where.y < 0
366       || where.x >= my_width || where.y >= my_depth)
367       return false;
368 
369    return static_cast<bool>(tile_at(where.x, where.y).track);
370 }
371 
372 // Return a location where the train may start
start() const373 track::Connection Map::start() const
374 {
375    return make_pair(start_location, start_direction);
376 }
377 
378 // Try to place the train on this tile
set_start(int x,int y)379 void Map::set_start(int x, int y)
380 {
381    const track::Direction poss_dirs[] = {
382       axis::X, axis::Y, -axis::X, -axis::Y
383    };
384    static int next_dir = 0;
385 
386    TrackAnchor track_node = tile_at(x, y).track;
387    if (!track_node) {
388       warn() << "Must place start on track";
389       return;
390    }
391 
392    ITrackSegmentPtr track = track_node->get();
393 
394    int tried = 0;
395    do {
396       if (track->is_valid_direction(poss_dirs[next_dir]))
397          break;
398       else
399          next_dir = (next_dir + 1) % 4;
400    } while (++tried < 4);
401 
402    if (tried == 4) {
403       warn() << "Cannot find suitable initial direction for this track";
404       return;
405    }
406 
407    start_location = make_point(x, y);
408    start_direction = poss_dirs[next_dir];
409 
410    next_dir = (next_dir + 1) % 4;
411 }
412 
413 // Force the train to start on this tile
set_start(int x,int y,int dirX,int dirY)414 void Map::set_start(int x, int y, int dirX, int dirY)
415 {
416    start_location = make_point(x, y);
417    start_direction = make_vector(dirX, 0, dirY);
418 }
419 
set_grid(bool on_off)420 void Map::set_grid(bool on_off)
421 {
422    should_draw_grid_lines = on_off;
423 }
424 
reset_map(int a_width,int a_depth)425 void Map::reset_map(int a_width, int a_depth)
426 {
427    my_width = a_width;
428    my_depth = a_depth;
429 
430    // Allocate memory
431    if (tiles)
432       delete[] tiles;
433    tiles = new Tile[a_width * a_depth];
434 
435    if (height_map)
436       delete[] height_map;
437    height_map = new HeightMap[(a_width + 1) * (a_depth + 1)];
438 
439    // Make a flat map
440    for (int x = 0; x <= a_width; x++) {
441       for (int y = 0; y <= a_depth; y++) {
442          HeightMap& v = height_map[x + y*(a_width+1)];
443 
444          const float xf = static_cast<float>(x) - 0.5f;
445          const float yf = static_cast<float>(y) - 0.5f;
446 
447          v.pos = make_vector(xf, 0.0f, yf);
448          v.normal = make_vector(0.0f, 1.0f, 0.0f);
449          v.lock_count = 0;
450       }
451    }
452 
453    // Create quad tree
454    quad_tree = make_quad_tree(shared_from_this(), my_width, my_depth);
455 }
456 
highlight_vertex(PointI point,Colour colour) const457 void Map::highlight_vertex(PointI point, Colour colour) const
458 {
459    assert(point.x >= 0 && point.x < my_width
460           && point.y >= 0 && point.y < my_depth);
461 
462    int index = point.x + (point.y * (my_width + 1));
463 
464    gl::colour(colour);
465    glPointSize(5.0f);
466    gl::point(make_vector_f(point.x - 0.5f,
467                            height_map[index].pos.y + 0.01f,
468                            point.y - 0.5f));
469 }
470 
highlight_tile(PointI point,Colour colour) const471 void Map::highlight_tile(PointI point, Colour colour) const
472 {
473    highlighted_tiles.push_back(make_tuple(point, colour));
474 }
475 
render_highlighted_tiles() const476 void Map::render_highlighted_tiles() const
477 {
478    // At the end of the render loop, draw the highlighted tiles over
479    // the top of all others - this is to get the transparency working
480 
481    glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
482 
483    glDisable(GL_TEXTURE_2D);
484    glEnable(GL_BLEND);
485    glDisable(GL_LIGHTING);
486 
487    glDepthMask(GL_FALSE);
488 
489    vector<tuple<PointI, Colour> >::const_iterator it;
490    for (it = highlighted_tiles.begin(); it != highlighted_tiles.end(); ++it) {
491 
492       const PointI& point = get<0>(*it);
493       Colour colour = get<1>(*it);
494 
495       // User should be able to click on the highlight as well
496       glPushName(tile_name(point.x, point.y));
497 
498       colour.a = 0.5f;
499       gl::colour(colour);
500       glBegin(GL_POLYGON);
501 
502       int indexes[4];
503       tile_vertices(point.x, point.y, indexes);
504 
505       for (int i = 0; i < 4; i++) {
506          HeightMap& v = height_map[indexes[i]];
507          gl::normal(v.normal);
508          gl::vertex(v.pos + make_vector(0.0f, 0.1f, 0.0f));
509       }
510 
511       glEnd();
512 
513       glPopName();
514    }
515 
516    glPopAttrib();
517 
518    highlighted_tiles.clear();
519 }
520 
render(IGraphicsPtr a_context) const521 void Map::render(IGraphicsPtr a_context) const
522 {
523    // The `frame_num' counter is used to ensure we draw each
524    // track segment at most once per frame
525    frame_num++;
526 
527    fog->apply();
528 
529    glPushAttrib(GL_ALL_ATTRIB_BITS);
530 
531    // Thick lines for grid
532    glLineWidth(2.0f);
533 
534    // Use the value of glColor rather than materials
535    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
536    glEnable(GL_COLOR_MATERIAL);
537 
538    glDisable(GL_TEXTURE_2D);
539    glEnable(GL_CULL_FACE);
540 
541    glPushMatrix();
542    quad_tree->render(a_context);
543    glPopMatrix();
544 
545    render_highlighted_tiles();
546 
547    glPopAttrib();
548 }
549 
550 // Draw an arrow on the start location
draw_start_location() const551 void Map::draw_start_location() const
552 {
553    glPushAttrib(GL_ENABLE_BIT);
554    glPushMatrix();
555 
556    glEnable(GL_BLEND);
557    glDisable(GL_TEXTURE_2D);
558 
559    int indexes[4];
560    tile_vertices(start_location.x, start_location.y, indexes);
561 
562    float avg_height = 0.0f;
563    for (int i = 0; i < 4; i++)
564       avg_height += height_map[indexes[i]].pos.y;
565    avg_height /= 4.0f;
566 
567    glTranslatef(start_location.x,
568                 avg_height + 0.1f,
569                 start_location.y);
570 
571    if (start_direction == axis::X)
572       glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
573    else if (start_direction == -axis::Y)
574       glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
575    else if (start_direction == -axis::X)
576       glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
577 
578    glColor4f(0.0f, 0.9f, 0.0f, 0.8f);
579 
580    glBegin(GL_TRIANGLES);
581    glNormal3f(0.0f, 1.0f, 0.0f);
582    glVertex3f(0.5f, 0.0f, -0.5f);
583    glVertex3f(-0.5f, 0.0f, -0.5f);
584    glVertex3f(0.0f, 0.0f, 0.5f);
585    glEnd();
586 
587    glPopMatrix();
588    glPopAttrib();
589 }
590 
591 // Check to see if the given id contains a valid mesh and ensure the
592 // array is large enough to hold it
have_mesh(int id,PointI bot_left,PointI top_right)593 bool Map::have_mesh(int id, PointI bot_left, PointI top_right)
594 {
595    if (id >= static_cast<int>(terrain_meshes.size()))
596       terrain_meshes.resize(id + 1);
597 
598    bool ok = static_cast<bool>(terrain_meshes[id]);
599    list<PointI>::iterator it = dirty_tiles.begin();
600 
601    while (it != dirty_tiles.end()) {
602       bool covered =
603          (*it).x >= bot_left.x
604          && (*it).x <= top_right.x
605          && (*it).y >= bot_left.y
606          && (*it).y <= top_right.y;
607 
608       if (covered) {
609          ok = false;
610          it = dirty_tiles.erase(it);
611       }
612       else
613          ++it;
614    }
615 
616    return ok;
617 }
618 
619 // Record that the mesh containing a tile needs rebuilding
dirty_tile(int x,int y)620 void Map::dirty_tile(int x, int y)
621 {
622    dirty_tiles.push_back(make_point(x, y));
623 
624    // Push its neighbours as well since the vertices of a tile sit
625    // on mesh boundaries
626    dirty_tiles.push_back(make_point(x, y + 1));
627    dirty_tiles.push_back(make_point(x, y - 1));
628    dirty_tiles.push_back(make_point(x + 1, y));
629    dirty_tiles.push_back(make_point(x - 1, y));
630 }
631 
632 // Generate a terrain mesh for a particular sector
build_mesh(int id,PointI bot_left,PointI top_right)633 void Map::build_mesh(int id, PointI bot_left, PointI top_right)
634 {
635    static const tuple<float, Colour> colour_map[] = {
636       //          Start height         colour
637       make_tuple(    7.0f,     make_rgb(238, 233, 233) ),
638       make_tuple(    5.0f,     make_rgb(124, 113, 36) ),
639       make_tuple(    3.0f,     make_rgb(129, 142, 57) ),
640       make_tuple(    0.0f,     make_rgb(103, 142, 57) ),
641       make_tuple(   -2.0f,     make_rgb(208, 207, 104)),
642       make_tuple(   -1e10f,    make_rgb(177, 176, 96) )
643    };
644 
645    IMeshBufferPtr buf = make_mesh_buffer();
646 
647    // Incrementing the frame counter here ensures that any track which spans
648    // multiple sectors will be merged with each applicable mesh even when
649    // the meshes are built on the same frame
650    ++frame_num;
651 
652    static ITexturePtr noise = make_noise_texture(25, 512, 190, 15);
653    buf->bind(noise);
654 
655    const float tmul = 1.0f / float(top_right.x - bot_left.x + 1);
656 
657    for (int x = top_right.x-1; x >= bot_left.x; x--) {
658       for (int y = bot_left.y; y < top_right.y; y++) {
659          int indexes[4];
660          tile_vertices(x, y, indexes);
661 
662          const int order[6] = {
663             indexes[1], indexes[2], indexes[3],
664             indexes[3], indexes[0], indexes[1]
665          };
666 
667          const IMeshBuffer::TexCoord tex_coords[4] = {
668             make_point(x * tmul, (y + 1) * tmul),
669             make_point((x + 1) * tmul, (y + 1) * tmul),
670             make_point((x + 1) * tmul, y * tmul),
671             make_point(x * tmul, y * tmul)
672          };
673 
674          const IMeshBuffer::TexCoord tex_order[6] = {
675             tex_coords[1], tex_coords[2], tex_coords[3],
676             tex_coords[3], tex_coords[0], tex_coords[1]
677          };
678 
679          for (int i = 0; i < 6; i++) {
680             const HeightMap& v = height_map[order[i]];
681 
682             const float h = v.pos.y;
683             tuple<float, Colour> hcol;
684             int j = 0;
685             do {
686                hcol = colour_map[j++];
687             } while (get<0>(hcol) > h);
688 
689             buf->add(v.pos, v.normal, get<1>(hcol), tex_order[i]);
690          }
691       }
692    }
693 
694    // Merge any static scenery
695    for (int x = top_right.x-1; x >= bot_left.x; x--) {
696       for (int y = bot_left.y; y < top_right.y; y++) {
697          Tile& tile = tile_at(x, y);
698 
699          if (tile.scenery && tile.scenery->needs_rendering(frame_num)) {
700             tile.scenery->get()->merge(buf);
701             tile.scenery->rendered_on(frame_num);
702          }
703 
704          // Draw the track, if any
705          if (tile.track && tile.track->needs_rendering(frame_num)) {
706             tile.track->get()->merge(buf);
707             tile.track->rendered_on(frame_num);
708          }
709       }
710    }
711 
712    // Draw the sides of the map if this is an edge sector
713    const float x1 = static_cast<float>(bot_left.x) - 0.5f;
714    const float x2 = static_cast<float>(top_right.x) - 0.5f;
715    const float y1 = static_cast<float>(bot_left.y) - 0.5f;
716    const float y2 = static_cast<float>(top_right.y) - 0.5f;
717 
718    const Colour brown = make_rgb(104, 57, 12);
719    const float depth = -3.0f;
720 
721    buf->bind(ITexturePtr());   // No texture on sides
722 
723    int index[4];
724 
725    if (bot_left.x == 0) {
726       for (int y = bot_left.y; y < top_right.y; y++) {
727          const float yf = static_cast<float>(y) - 0.5f;
728 
729          tile_vertices(0, y, index);
730 
731          const float h1 = height_at(index[3]).pos.y;
732          const float h2 = height_at(index[0]).pos.y;
733 
734          buf->add_quad(make_vector(x1, h1, yf),
735             make_vector(x1, depth, yf),
736             make_vector(x1, depth, yf + 1.0f),
737             make_vector(x1, h2, yf + 1.0f),
738             brown);
739       }
740    }
741 
742    if (top_right.x == my_width) {
743       for (int y = bot_left.y; y < top_right.y; y++) {
744          const float yf = static_cast<float>(y) - 0.5f;
745 
746          tile_vertices(my_width - 1, y, index);
747 
748          const float h1 = height_at(index[2]).pos.y;
749          const float h2 = height_at(index[1]).pos.y;
750 
751          buf->add_quad(make_vector(x2, depth, yf),
752             make_vector(x2, h1, yf),
753             make_vector(x2, h2, yf + 1.0f),
754             make_vector(x2, depth, yf + 1.0f),
755             brown);
756       }
757    }
758 
759    if (bot_left.y == 0) {
760       for (int x = bot_left.x; x < top_right.x; x++) {
761          const float xf = static_cast<float>(x) - 0.5f;
762 
763          tile_vertices(x, 0, index);
764 
765          const float h1 = height_at(index[3]).pos.y;
766          const float h2 = height_at(index[2]).pos.y;
767 
768          buf->add_quad(make_vector(xf, depth, y1),
769             make_vector(xf, h1, y1),
770             make_vector(xf + 1.0f, h2, y1),
771             make_vector(xf + 1.0f, depth, y1),
772             brown);
773       }
774    }
775 
776    if (top_right.y == my_depth) {
777       for (int x = bot_left.x; x < top_right.x; x++) {
778          const float xf = static_cast<float>(x) - 0.5f;
779 
780          tile_vertices(x, my_depth - 1, index);
781 
782          const float h1 = height_at(index[0]).pos.y;
783          const float h2 = height_at(index[1]).pos.y;
784 
785          buf->add_quad(make_vector(xf, h1, y2),
786             make_vector(xf, depth, y2),
787             make_vector(xf + 1.0f, depth, y2),
788             make_vector(xf + 1.0f, h2, y2),
789             brown);
790       }
791    }
792 
793    terrain_meshes[id] = make_mesh(buf);
794 
795    // Check if this sector needs a sea quad drawn
796    bool below_sea_level = false;
797    for (int x = top_right.x-1; x >= bot_left.x; x--) {
798       for (int y = bot_left.y; y < top_right.y; y++) {
799          int index[4];
800          tile_vertices(x, y, index);
801 
802          below_sea_level |=
803             height_at(index[0]).pos.y < 0.0f
804             || height_at(index[1]).pos.y < 0.0f
805             || height_at(index[2]).pos.y < 0.0f
806             || height_at(index[3]).pos.y < 0.0f;
807 
808          if (below_sea_level)
809             goto below_sea_levelOut;
810       }
811    }
812 
813  below_sea_levelOut:
814 
815    size_t min_size = id + 1;
816    if (sea_sectors.size() < min_size)
817       sea_sectors.resize(min_size);
818    sea_sectors.at(id) = below_sea_level;
819 
820    // Make sure we don't rebuild this mesh if any of the tiles are dirty
821    have_mesh(id, bot_left, top_right);
822 }
823 
824 // A special rendering mode when selecting tiles
render_pick_sector(PointI bot_left,PointI top_right)825 void Map::render_pick_sector(PointI bot_left, PointI top_right)
826 {
827    glColor3f(1.0f, 1.0f, 1.0f);
828 
829    for (int x = top_right.x-1; x >= bot_left.x; x--) {
830       for (int y = bot_left.y; y < top_right.y; y++) {
831          // Name this tile
832          glPushName(tile_name(x, y));
833 
834          int indexes[4];
835          tile_vertices(x, y, indexes);
836 
837          glBegin(GL_QUADS);
838          for (int i = 0; i < 4; i++) {
839             const HeightMap& v = height_map[indexes[i]];
840             glNormal3f(v.normal.x, v.normal.y, v.normal.z);
841             glVertex3f(v.pos.x, v.pos.y, v.pos.z);
842          }
843          glEnd();
844 
845          glPopName();
846       }
847    }
848 }
849 
850 // Render a small part of the map as directed by the quad tree
render_sector(IGraphicsPtr a_context,int id,PointI bot_left,PointI top_right)851 void Map::render_sector(IGraphicsPtr a_context, int id,
852                         PointI bot_left, PointI top_right)
853 {
854    if (in_pick_mode) {
855       render_pick_sector(bot_left, top_right);
856       return;
857    }
858 
859    if (!have_mesh(id, bot_left, top_right))
860       build_mesh(id, bot_left, top_right);
861 
862    {
863       // Parts of track may extend outside the sector so these
864       // are clipped off
865 
866       const float x = bot_left.x - 0.5f;
867       const float w = quad_tree->leaf_size();
868       const float z = bot_left.y - 0.5f;
869       const float d = quad_tree->leaf_size();
870       ClipVolume clip(x, w, z, d);
871 
872       terrain_meshes[id]->render();
873    }
874 
875    // Draw the overlays
876    for (int x = top_right.x-1; x >= bot_left.x; x--) {
877       for (int y = bot_left.y; y < top_right.y; y++) {
878          //for (int i = 0; i < 4; i++) {
879          //   const Vertex& v = height_map[indexes[i]];
880          //   draw_normal(v.pos, v.normal);
881          //}
882 
883          if (should_draw_grid_lines) {
884             // Render grid lines
885             glColor3f(0.0f, 0.0f, 0.0f);
886             glBegin(GL_LINE_LOOP);
887 
888             int indexes[4];
889             tile_vertices(x, y, indexes);
890             for (int i = 0; i < 4; i++) {
891                const HeightMap& v = height_map[indexes[i]];
892                gl::vertex(v.pos);
893             }
894 
895             glEnd();
896          }
897 
898          Tile& tile = tile_at(x, y);
899 
900          if (tile.track && tile.track->needs_rendering(frame_num)) {
901 #if 0
902             // Draw the endpoints for debugging
903             vector<PointI > tiles;
904             tile.track->get()->get_endpoints(tiles);
905             for_each(tiles.begin(), tiles.end(),
906                     bind(&Map::highlight_tile, this, placeholders::_1,
907                           make_colour(0.9f, 0.1f, 0.1f)));
908 
909             tiles.clear();
910             tile.track->get()->get_covers(tiles);
911             for_each(tiles.begin(), tiles.end(),
912                     bind(&Map::highlight_tile, this, placeholders::_1,
913                           make_colour(0.4f, 0.7f, 0.1f)));
914 #endif
915 
916 #if 0
917             // Draw vertices covered by track
918             vector<PointI> vertices;
919             tile.track->get()->get_height_locked(vertices);
920             for_each(vertices.begin(), vertices.end(),
921                      bind(&Map::highlight_vertex, this, placeholders::_1,
922                           make_colour(1.0f, 0.0f, 0.0f)));
923 #endif
924 
925             // Draw track highlights
926             tile.track->get()->render();
927 
928             tile.track->rendered_on(frame_num);
929          }
930 
931 #if 0
932          // Highlight tiles covered by scenery
933          if (tile.scenery)
934             highlight_tile(make_point(x, y), colour::WHITE);
935 #endif
936 
937          // Draw the station, if any
938          if (tile.station
939              && (should_draw_grid_lines || tile.station->highlight_visible()))
940             highlight_tile(make_point(x, y), tile.station->highlight_colour());
941 
942          // Draw the start location if it's on this tile
943          if (start_location.x == x && start_location.y == y
944             && should_draw_grid_lines)
945             draw_start_location();
946       }
947    }
948 
949    assert(glGetError() == GL_NO_ERROR);
950 }
951 
952 // Render the semi-transparent overlays such as water
post_render_sector(IGraphicsPtr a_context,int id,PointI bot_left,PointI top_right)953 void Map::post_render_sector(IGraphicsPtr a_context, int id,
954                              PointI bot_left, PointI top_right)
955 {
956    // Draw the water
957    if (!in_pick_mode && sea_sectors.at(id)) {
958       glPushAttrib(GL_ENABLE_BIT);
959 
960       glEnable(GL_BLEND);
961       glDisable(GL_TEXTURE_2D);
962 
963       const float blX = static_cast<float>(bot_left.x);
964       const float blY = static_cast<float>(bot_left.y);
965       const float trX = static_cast<float>(top_right.x);
966       const float trY = static_cast<float>(top_right.y);
967 
968       static const float sea_level = -0.6f;
969       gl::colour(make_rgb(0, 80, 160, 150));
970       glNormal3f(0.0f, 1.0f, 0.0f);
971       glBegin(GL_QUADS);
972       glVertex3f(blX - 0.5f, sea_level, blY - 0.5f);
973       glVertex3f(blX - 0.5f, sea_level, trY - 0.5f);
974       glVertex3f(trX - 0.5f, sea_level, trY - 0.5f);
975       glVertex3f(trX - 0.5f, sea_level, blY - 0.5f);
976       glEnd();
977 
978       glPopAttrib();
979    }
980 }
981 
982 // Called when we've changed the height of part of a tile
983 // This readjusts all the normals and those of its neighbours
984 // to point in the right direction
fix_normals(int x,int y)985 void Map::fix_normals(int x, int y)
986 {
987    if ((x < 0) || (y < 0) || (x >= my_width) || (y >= my_depth))
988       return;
989 
990    int indexes[4];
991    tile_vertices(x, y, indexes);
992 
993    for (int n = 0; n < 4; n++) {
994       const int i = indexes[n];
995       HeightMap& v = height_map[i];
996 
997       VectorF west, north, east, south;
998       bool have_west = true, have_north = true,
999          have_east = true, have_south = true;
1000 
1001       if (i > 0 && i % (my_width + 1) > 0)
1002          west = height_at(i-1).pos;
1003       else
1004          have_west = false;
1005 
1006       if (i < (my_width + 1) * my_depth - 1)
1007          north = height_at(i + (my_width + 1)).pos;
1008       else
1009          have_north = false;
1010 
1011       if (i < (my_width + 1) * (my_depth + 1) - 1
1012          && i % (my_width + 1) < my_width)
1013          east = height_at(i + 1).pos;
1014       else
1015          have_east = false;
1016 
1017       if (i > (my_width + 1))
1018          south = height_at(i - (my_width + 1)).pos;
1019       else
1020          have_south = false;
1021 
1022       float count = 4.0f;
1023       VectorF avg = make_vector(0.0f, 0.0f, 0.0f);
1024 
1025       if (have_west && have_north)
1026          avg += surface_normal(north, v.pos, west);
1027       else
1028          count -= 1.0f;
1029 
1030       if (have_east && have_north)
1031          avg += surface_normal(east, v.pos, north);
1032       else
1033          count -= 1.0f;
1034 
1035       if (have_south && have_east)
1036          avg += surface_normal(south, v.pos, east);
1037       else
1038          count -= 1.0f;
1039 
1040       if (have_west && have_south)
1041          avg += surface_normal(west, v.pos, south);
1042       else
1043          count -= 1.0f;
1044 
1045       v.normal = avg / count;
1046    }
1047 }
1048 
1049 // Find the terrain vertices that border a tile
tile_vertices(int x,int y,int * indexes) const1050 void Map::tile_vertices(int x, int y, int* indexes) const
1051 {
1052    assert(x >= 0 && x < my_width && y >= 0 && y < my_depth);
1053 
1054    indexes[3] = x + (y * (my_width+1));         // (X, Y)
1055    indexes[2] = (x+1) + (y * (my_width+1));     // (X+1, Y)
1056    indexes[1] = (x+1) + ((y+1) * (my_width+1)); // (X+1, Y+1)
1057    indexes[0] = x + ((y+1) * (my_width+1));     // (X, Y+1)
1058 }
1059 
1060 // True if changing the height of this tile will affect
1061 // a piece of track
raise_will_cover_track(int x,int y) const1062 bool Map::raise_will_cover_track(int x, int y) const
1063 {
1064 #if 0
1065    return tile_at(x, y).track
1066       || (x < my_width - 1 && tile_at(x + 1, y).track)
1067       || (x > 0 && tile_at(x - 1, y).track)
1068       || (y < my_depth - 1 && tile_at(x, y + 1).track)
1069       || (y > 0 && tile_at(x, y - 1).track)
1070       || (x < my_width - 1 && y < my_depth - 1 && tile_at(x + 1, y + 1).track)
1071       || (x > 0 && y < my_depth - 1 && tile_at(x - 1, y + 1).track)
1072       || (x > 0 && y > 0 && tile_at(x - 1, y - 1).track)
1073       || (x < my_width - 1 && y > 0 && tile_at(x + 1, y - 1).track);
1074 #else
1075    int indexes[4];
1076    tile_vertices(x, y, indexes);
1077 
1078    bool ok = true;
1079    for (int i = 0; i < 4; i++)
1080       ok &= height_map[indexes[i]].lock_count == 0;
1081 
1082    return !ok;
1083 #endif
1084 }
1085 
1086 // Changes the height of a complete tile
raise_tile(int x,int y,float delta_height)1087 void Map::raise_tile(int x, int y, float delta_height)
1088 {
1089    if (raise_will_cover_track(x, y)) {
1090       warn() << "Cannot raise terrain over track";
1091       return;
1092    }
1093 
1094    int indexes[4];
1095    tile_vertices(x, y, indexes);
1096 
1097    for (int i = 0; i < 4; i++)
1098       height_map[indexes[i]].pos.y += delta_height;
1099 
1100    fix_normals(x, y);
1101    dirty_tile(x, y);
1102 }
1103 
lock_height_at(PointI p)1104 void Map::lock_height_at(PointI p)
1105 {
1106    assert(p.x <= my_width);
1107    assert(p.y <= my_depth);
1108 
1109    height_map[p.x + (p.y * (my_width+1))].lock_count++;
1110 }
1111 
unlock_height_at(PointI p)1112 void Map::unlock_height_at(PointI p)
1113 {
1114    assert(p.x <= my_width);
1115    assert(p.y <= my_depth);
1116 
1117    HeightMap& h = height_map[p.x + (p.y * (my_width+1))];
1118 
1119    assert(h.lock_count > 0);
1120    h.lock_count--;
1121 }
1122 
1123 // Sets the absolute height of a tile
set_tile_height(int x,int y,float h)1124 void Map::set_tile_height(int x, int y, float h)
1125 {
1126    bool track_affected = raise_will_cover_track(x, y);
1127 
1128    int indexes[4];
1129    tile_vertices(x, y, indexes);
1130 
1131    for (int i = 0; i < 4; i++) {
1132       if (track_affected
1133          && abs(height_map[indexes[i]].pos.y - h) > 0.01f) {
1134          warn() << "Cannot level terrain under track";
1135          return;
1136       }
1137       else
1138          height_map[indexes[i]].pos.y = h;
1139    }
1140 
1141    fix_normals(x, y);
1142    dirty_tile(x, y);
1143 }
1144 
height_at(float x,float y) const1145 float Map::height_at(float x, float y) const
1146 {
1147    const int x_floor = static_cast<int>(floorf(x));
1148    const int y_floor = static_cast<int>(floorf(y));
1149 
1150    return height_at(make_point(x_floor, y_floor));
1151 }
1152 
height_at(PointI where) const1153 float Map::height_at(PointI where) const
1154 {
1155    if (where.x < 0 || where.y < 0
1156       || where.x >= my_width || where.y >= my_depth)
1157       return 0.0f;
1158 
1159    int indexes[4];
1160    tile_vertices(where.x, where.y, indexes);
1161 
1162    float avg = 0.0f;
1163    for (int i = 0; i < 4; i++)
1164       avg += height_map[indexes[i]].pos.y;
1165 
1166    return avg / 4.0f;
1167 }
1168 
slope_at(PointI where,track::Direction axis,bool & level) const1169 VectorF Map::slope_at(PointI where, track::Direction axis, bool& level) const
1170 {
1171    int indexes[4];
1172    tile_vertices(where.x, where.y, indexes);
1173 
1174    // This is slightly consfusing since the track Y axis
1175    // is the Z axis in the height map
1176 
1177    VectorF v1, v2;
1178 
1179    if (axis == axis::X) {
1180       v1 = height_map[indexes[2]].pos - height_map[indexes[3]].pos;
1181       v2 = height_map[indexes[1]].pos - height_map[indexes[0]].pos;
1182    }
1183    else {
1184       v1 = height_map[indexes[0]].pos - height_map[indexes[3]].pos;
1185       v2 = height_map[indexes[1]].pos - height_map[indexes[2]].pos;
1186    }
1187 
1188    level = (v1 == v2);
1189 
1190 #if 0
1191    debug() << "slope_at where=" << where
1192            << " axis=" << axis
1193            << " v1=" << v1 << " v2=" << v2
1194            << " level=" << level;
1195 #endif
1196 
1197    return v1;
1198 }
1199 
slope_before(PointI where,track::Direction axis,bool & valid) const1200 VectorF Map::slope_before(PointI where,
1201                           track::Direction axis, bool& valid) const
1202 {
1203    PointI before;
1204 
1205    if (axis == axis::X)
1206       before = where + make_point(-1, 0);
1207    else
1208       before = where + make_point(0, -1);
1209 
1210    const bool off_edge =
1211       (axis == axis::X && before.x < 0)
1212       || (axis == axis::Y && before.y < 0);
1213 
1214    valid = !off_edge;
1215 
1216    if (off_edge)
1217       return make_vector(0.0f, 0.0f, 0.0f);
1218    else {
1219       bool ignored;
1220       return slope_at(before, axis, ignored);
1221    }
1222 }
1223 
slope_after(PointI where,track::Direction axis,bool & valid) const1224 VectorF Map::slope_after(PointI where,
1225                          track::Direction axis, bool &valid) const
1226 {
1227    PointI after;
1228 
1229    if (axis == axis::X)
1230       after = where + make_point(1, 0);
1231    else
1232       after = where + make_point(0, 1);
1233 
1234    const bool off_edge =
1235       (axis == axis::X && after.x >= width())
1236       || (axis == axis::Y && after.y >= depth());
1237 
1238    valid = !off_edge;
1239 
1240    if (off_edge)
1241       return make_vector(0.0f, 0.0f, 0.0f);
1242    else {
1243       bool ignored;
1244       return slope_at(after, axis, ignored);
1245    }
1246 }
1247 
change_area_height(const PointI & a_start_pos,const PointI & a_finish_pos,float a_height_delta)1248 void Map::change_area_height(const PointI& a_start_pos,
1249                              const PointI& a_finish_pos,
1250                              float a_height_delta)
1251 {
1252    const int xmin = min(a_start_pos.x, a_finish_pos.x);
1253    const int xmax = max(a_start_pos.x, a_finish_pos.x);
1254 
1255    const int ymin = min(a_start_pos.y, a_finish_pos.y);
1256    const int ymax = max(a_start_pos.y, a_finish_pos.y);
1257 
1258    for (int x = xmin; x <= xmax; x++) {
1259       for (int y = ymin; y <= ymax; y++)
1260          raise_tile(x, y, a_height_delta);
1261    }
1262 }
1263 
level_area(PointI a_start_pos,PointI a_finish_pos)1264 void Map::level_area(PointI a_start_pos, PointI a_finish_pos)
1265 {
1266    const int xmin = min(a_start_pos.x, a_finish_pos.x);
1267    const int xmax = max(a_start_pos.x, a_finish_pos.x);
1268 
1269    const int ymin = min(a_start_pos.y, a_finish_pos.y);
1270    const int ymax = max(a_start_pos.y, a_finish_pos.y);
1271 
1272    int indexes[4];
1273    tile_vertices(a_start_pos.x, a_start_pos.y, indexes);
1274 
1275    float avg_height = 0.0f;
1276    for (int i = 0; i < 4; i++)
1277       avg_height += height_map[indexes[i]].pos.y;
1278    avg_height /= 4.0f;
1279 
1280    for (int x = xmin; x <= xmax; x++) {
1281       for (int y = ymin; y <= ymax; y++)
1282          set_tile_height(x, y, avg_height);
1283    }
1284 }
1285 
smooth_area(PointI start,PointI finish)1286 void Map::smooth_area(PointI start, PointI finish)
1287 {
1288    const int xmin = min(start.x, finish.x);
1289    const int xmax = max(start.x, finish.x);
1290 
1291    const int ymin = min(start.y, finish.y);
1292    const int ymax = max(start.y, finish.y);
1293 
1294    PointI step;
1295    if (xmin == xmax)
1296       step = make_point(0, 1);
1297    else if (ymin == ymax)
1298       step = make_point(1, 0);
1299    else {
1300       warn() << "Can only smooth strips";
1301       return;
1302    }
1303 
1304    const PointI abs_start = make_point(xmin, ymin);
1305    const PointI abs_finish = make_point(xmax, ymax);
1306 
1307    const float height_start = height_at(abs_start);
1308    const float height_finish = height_at(abs_finish);
1309 
1310    set_tile_height(abs_start.x, abs_start.y, height_start);
1311    set_tile_height(abs_finish.x, abs_finish.y, height_finish);
1312 
1313    const float drop =
1314       (height_start - height_finish) / (xmax + ymax - xmin - ymin);
1315    debug() << "drop=" << drop;
1316 
1317    int i = 0;
1318    for (PointI it = abs_start; it != abs_finish; i++, it += step) {
1319       const bool track_affected = raise_will_cover_track(it.x, it.y);
1320 
1321       int indexes[4];
1322       tile_vertices(it.x, it.y, indexes);
1323 
1324       int targets[2];
1325       if (xmin == xmax) {
1326          targets[0] = 0;
1327          targets[1] = 1;
1328       }
1329       else {
1330          targets[0] = 1;
1331          targets[1] = 2;
1332       }
1333 
1334       for (int j = 0; j < 2; j++) {
1335          const float new_height = height_start - (i * drop);
1336 
1337          if (track_affected
1338             && abs(height_map[indexes[targets[j]]].pos.y - new_height) > 0.01f) {
1339             warn() << "Cannot change terrain under track";
1340             return;
1341          }
1342          else
1343             height_map[indexes[targets[j]]].pos.y = new_height;
1344       }
1345 
1346       fix_normals(it.x, it.y);
1347       dirty_tile(it.x, it.y);
1348    }
1349 
1350 }
1351 
raise_area(const PointI & a_start_pos,const PointI & a_finish_pos)1352 void Map::raise_area(const PointI& a_start_pos,
1353                      const PointI& a_finish_pos)
1354 {
1355    change_area_height(a_start_pos, a_finish_pos, 0.1f);
1356 }
1357 
lower_area(const PointI & a_start_pos,const PointI & a_finish_pos)1358 void Map::lower_area(const PointI& a_start_pos,
1359                      const PointI& a_finish_pos)
1360 {
1361    change_area_height(a_start_pos, a_finish_pos, -0.1f);
1362 }
1363 
add_scenery(PointI where,ISceneryPtr s)1364 void Map::add_scenery(PointI where, ISceneryPtr s)
1365 {
1366    if (tile_at(where.x, where.y).track)
1367       warn() << "Cannot place scenery on track";
1368    else {
1369       SceneryAnchor indirect(new Anchor<IScenery>(s, where));
1370 
1371       const PointI size = s->size();
1372 
1373       for (int x = 0; x < size.x; x++) {
1374          for (int y = 0; y < size.y; y++) {
1375             tile_at(where.x + x, where.y + y).scenery = indirect;
1376             dirty_tile(where.x, where.y);
1377          }
1378       }
1379 
1380       s->set_position(static_cast<float>(where.x),
1381          height_at(where),
1382          static_cast<float>(where.y));
1383    }
1384 }
1385 
1386 // Either extend an existing station which borders this area
1387 // or build a new station
extend_station(PointI a_start_pos,PointI a_finish_pos)1388 IStationPtr Map::extend_station(PointI a_start_pos, PointI a_finish_pos)
1389 {
1390    const int xmin = min(a_start_pos.x, a_finish_pos.x);
1391    const int xmax = max(a_start_pos.x, a_finish_pos.x);
1392 
1393    const int ymin = min(a_start_pos.y, a_finish_pos.y);
1394    const int ymax = max(a_start_pos.y, a_finish_pos.y);
1395 
1396    // Find all the tiles containing track in this region
1397    typedef list<PointI > PointList;
1398    PointList track_in_area;
1399    for (int x = xmin; x <= xmax; x++) {
1400       for (int y = ymin; y <= ymax; y++) {
1401          if (tile_at(x, y).track)
1402             track_in_area.push_back(make_point(x, y));
1403       }
1404    }
1405 
1406    if (track_in_area.empty()) {
1407       warn() << "Stations must be placed on track";
1408       return IStationPtr();
1409    }
1410 
1411    IStationPtr station;
1412 
1413    // See if any of these track segments are adjacent to a station
1414    for (PointList::const_iterator it = track_in_area.begin();
1415         it != track_in_area.end(); ++it) {
1416 
1417       const PointI near[] = {
1418          make_point(0, 0),
1419          make_point(1, 0),
1420          make_point(0, 1),
1421          make_point(-1, 0),
1422          make_point(0, -1)
1423       };
1424 
1425       for (int i = 0; i < 5; i++) {
1426          PointI neighbour = *it + near[i];
1427          if (neighbour.x >= 0 && neighbour.x < my_width
1428             && neighbour.y >= 0 && neighbour.y < my_depth
1429             && tile_at(neighbour.x, neighbour.y).station) {
1430 
1431             IStationPtr candidate = tile_at(neighbour.x, neighbour.y).station;
1432 
1433             // Maybe extend this station
1434             if (station && station != candidate) {
1435                warn() << "Cannot merge stations";
1436                return IStationPtr();
1437             }
1438             else
1439                station = candidate;
1440          }
1441       }
1442    }
1443 
1444    if (station) {
1445       // Found a station to extend
1446       debug() << "Found station to extend";
1447    }
1448    else {
1449       debug() << "Creating new station";
1450 
1451       station = make_station();
1452    }
1453 
1454    for (PointList::iterator it = track_in_area.begin();
1455         it != track_in_area.end(); ++it)
1456       tile_at((*it).x, (*it).y).station = station;
1457 
1458    return station;
1459 }
1460 
1461 // Write the terrain height map into a binary file
1462 // Binary file format is very simple:
1463 //   Bytes 0-3   Width of map
1464 //   Bytes 4-7   Depth of map
1465 //   Bytes 8+    Raw height data
write_height_map() const1466 void Map::write_height_map() const
1467 {
1468    using namespace boost;
1469 
1470    IResource::Handle h = resource->write_file(resource->name() + ".bin");
1471 
1472    log() << "Writing terrain height map to " << h.file_name();
1473 
1474    try {
1475       std::ofstream& of = h.wstream();
1476 
1477       const int32_t wl = static_cast<int32_t>(my_width);
1478       const int32_t dl = static_cast<int32_t>(my_depth);
1479       of.write(reinterpret_cast<const char*>(&wl), sizeof(int32_t));
1480       of.write(reinterpret_cast<const char*>(&dl), sizeof(int32_t));
1481 
1482       for (int i = 0; i < (my_width + 1) * (my_depth + 1); i++)
1483          of.write(reinterpret_cast<const char*>(&height_map[i].pos.y),
1484             sizeof(float));
1485    }
1486    catch (std::exception& e) {
1487       h.rollback();
1488       throw e;
1489    }
1490 }
1491 
1492 // Read the height data back out of a binary file
read_height_map(IResource::Handle a_handle)1493 void Map::read_height_map(IResource::Handle a_handle)
1494 {
1495    using namespace boost;
1496 
1497    log() << "Reading height map from " << a_handle.file_name();
1498 
1499    istream& is = a_handle.rstream();
1500 
1501    // Check the dimensions of the binary file match the XML file
1502    int32_t wl, dl;
1503    is.read(reinterpret_cast<char*>(&wl), sizeof(int32_t));
1504    is.read(reinterpret_cast<char*>(&dl), sizeof(int32_t));
1505 
1506    if (wl != my_width || dl != my_depth) {
1507       error() << "Expected width " << my_width << " got " << wl;
1508       error() << "Expected height " << my_depth << " got " << dl;
1509       throw runtime_error
1510          ("Binary file " + a_handle.file_name() + " dimensions are incorrect");
1511    }
1512 
1513    for (int i = 0; i < (my_width + 1) * (my_depth + 1); i++) {
1514       is.read(reinterpret_cast<char*>(&height_map[i].pos.y),
1515               sizeof(float));
1516       height_map[i].lock_count = 0;
1517    }
1518 
1519    for (int x = 0; x < my_width; x++) {
1520       for (int y = 0; y < my_depth; y++)
1521          fix_normals(x, y);
1522    }
1523 }
1524 
save_to(ostream & of)1525 void Map::save_to(ostream& of)
1526 {
1527    xml::element root("map");
1528    root.add_attribute("width", my_width);
1529    root.add_attribute("height", my_depth);
1530 
1531    root.add_child(xml::element("name").add_text("No Name"));
1532 
1533    root.add_child
1534       (xml::element("start")
1535          .add_attribute("x", start_location.x)
1536          .add_attribute("y", start_location.y)
1537          .add_attribute("dirX", start_direction.x)
1538          .add_attribute("dirY", start_direction.z));
1539 
1540    // Write out all the stations
1541    set<IStationPtr> seen_stations;
1542 
1543    for (int x = 0; x < my_width; x++) {
1544       for (int y = 0; y < my_depth; y++) {
1545          IStationPtr s = tile_at(x, y).station;
1546 
1547          if (s && seen_stations.find(s) == seen_stations.end()) {
1548             // Not seen this station before
1549             root.add_child
1550                (xml::element("station")
1551                   .add_attribute("id", s->id())
1552                   .add_child(xml::element("name").add_text(s->name())));
1553 
1554             seen_stations.insert(s);
1555          }
1556       }
1557    }
1558 
1559    // Generate the height map
1560    write_height_map();
1561 
1562    root.add_child
1563       (xml::element("heightmap")
1564          .add_text(resource->name() + ".bin"));
1565 
1566    xml::element tileset("tileset");
1567 
1568    // We abuse the frame number to ensure all scenery, etc. is
1569    // only written out once
1570    ++frame_num;
1571 
1572    for (int x = 0; x < my_width; x++) {
1573       for (int y = 0; y < my_depth; y++) {
1574          const Tile& tile = tile_at(x, y);
1575 
1576          bool useful = false;
1577          xml::element tile_xml("tile");
1578 
1579          tile_xml.add_attribute("x", x);
1580          tile_xml.add_attribute("y", y);
1581 
1582          if (tile.track
1583              && tile.track->origin() == make_point(x, y)) {
1584 
1585             tile_xml.add_child(tile.track->get()->to_xml());
1586             useful = true;
1587          }
1588 
1589          if (tile.station) {
1590             tile_xml.add_child
1591                (xml::element("station-part")
1592                   .add_attribute("id", tile.station->id()));
1593             useful = true;
1594          }
1595 
1596          if (tile.scenery && tile.scenery->needs_rendering(frame_num)) {
1597             tile_xml.add_child(tile.scenery->get()->to_xml());
1598             tile.scenery->rendered_on(frame_num);
1599             useful = true;
1600          }
1601 
1602          if (useful)
1603             tileset.add_child(tile_xml);
1604       }
1605    }
1606 
1607    root.add_child(tileset);
1608 
1609    of << xml::document(root);
1610 }
1611 
1612 // Turn the map into XML
save()1613 void Map::save()
1614 {
1615    using namespace boost::filesystem;
1616 
1617    IResource::Handle h = resource->write_file(resource->name() + ".xml");
1618 
1619    log() << "Saving map to " << h.file_name();
1620 
1621    std::ofstream& of = h.wstream();
1622 
1623    try {
1624       save_to(of);
1625    }
1626    catch (exception& e) {
1627       h.rollback();
1628       throw e;
1629    }
1630 }
1631 
make_empty_map(const string & a_res_id,int a_width,int a_depth)1632 IMapPtr make_empty_map(const string& a_res_id, int a_width, int a_depth)
1633 {
1634    IResourcePtr res = make_new_resource(a_res_id, "maps");
1635 
1636    shared_ptr<Map> ptr(new Map(res));
1637    ptr->reset_map(a_width, a_depth);
1638    ptr->save();
1639    return IMapPtr(ptr);
1640 }
1641 
1642 // Build a map through XML callbacks
1643 class MapLoader : public IXMLCallback {
1644 public:
MapLoader(shared_ptr<Map> a_map,IResourcePtr a_res)1645    MapLoader(shared_ptr<Map> a_map, IResourcePtr a_res)
1646       : my_map(a_map), tile(make_point(0, 0)),
1647         resource(a_res) {}
1648 
1649    void start_element(const string& local_name, const AttributeSet& attrs);
1650    void end_element(const string& local_name);
1651    void text(const string& local_name, const string& a_string);
1652 
1653 private:
1654    void handle_map(const AttributeSet& attrs);
1655    void handle_building(const AttributeSet& attrs);
1656    void handle_tree(const AttributeSet& attrs);
1657    void handle_station(const AttributeSet& attrs);
1658    void handle_start(const AttributeSet& attrs);
1659    void handle_tile(const AttributeSet& attrs);
1660    void handle_station_part(const AttributeSet& attrs);
1661    void handle_straight_track(const AttributeSet& attrs);
1662    void handle_slope_track(const AttributeSet& attrs);
1663    void handle_points(const AttributeSet& attrs);
1664    void handle_crossover_track(const AttributeSet& attrs);
1665    void handle_spline_track(const AttributeSet& attrs);
1666 
1667    shared_ptr<Map> my_map;
1668    map<int, IStationPtr> my_stations;
1669    IStationPtr my_active_station;
1670    PointI tile;
1671 
1672    IResourcePtr resource;
1673 };
1674 
start_element(const string & local_name,const AttributeSet & attrs)1675 void MapLoader::start_element(const string& local_name,
1676                               const AttributeSet& attrs)
1677 {
1678    if (local_name == "map")
1679       handle_map(attrs);
1680    else if (local_name == "tile")
1681       handle_tile(attrs);
1682    else if (local_name == "start")
1683       handle_start(attrs);
1684    else if (local_name == "straight-track")
1685       handle_straight_track(attrs);
1686    else if (local_name == "crossover-track")
1687       handle_crossover_track(attrs);
1688    else if (local_name == "gen-track"
1689             || local_name == "spline-track")
1690       handle_spline_track(attrs);
1691    else if (local_name == "points")
1692       handle_points(attrs);
1693    else if (local_name == "slope-track")
1694       handle_slope_track(attrs);
1695    else if (local_name == "station-part")
1696       handle_station_part(attrs);
1697    else if (local_name == "station")
1698       handle_station(attrs);
1699    else if (local_name == "building")
1700       handle_building(attrs);
1701    else if (local_name == "tree")
1702       handle_tree(attrs);
1703 }
1704 
end_element(const string & local_name)1705 void MapLoader::end_element(const string& local_name)
1706 {
1707    if (local_name == "station")
1708       my_active_station.reset();
1709 }
1710 
text(const string & local_name,const string & a_string)1711 void MapLoader::text(const string& local_name, const string& a_string)
1712 {
1713    if (local_name == "heightmap")
1714       my_map->read_height_map(resource->open_file(a_string));
1715    else if (my_active_station) {
1716       if (local_name == "name")
1717          my_active_station->set_name(a_string);
1718    }
1719 }
1720 
handle_map(const AttributeSet & attrs)1721 void MapLoader::handle_map(const AttributeSet& attrs)
1722 {
1723    int width, height;
1724    attrs.get("width", width);
1725    attrs.get("height", height);
1726 
1727    my_map->reset_map(width, height);
1728 }
1729 
handle_building(const AttributeSet & attrs)1730 void MapLoader::handle_building(const AttributeSet& attrs)
1731 {
1732    my_map->add_scenery(tile, load_building(attrs));
1733 }
1734 
handle_tree(const AttributeSet & attrs)1735 void MapLoader::handle_tree(const AttributeSet& attrs)
1736 {
1737    my_map->add_scenery(tile, load_tree(attrs));
1738 }
1739 
handle_station(const AttributeSet & attrs)1740 void MapLoader::handle_station(const AttributeSet& attrs)
1741 {
1742    my_active_station = make_station();
1743 
1744    int id;
1745    attrs.get("id", id);
1746    my_active_station->set_id(id);
1747 
1748    my_stations[id] = my_active_station;
1749 }
1750 
handle_start(const AttributeSet & attrs)1751 void MapLoader::handle_start(const AttributeSet& attrs)
1752 {
1753    int x, y, dirX, dirY;
1754    attrs.get("x", x);
1755    attrs.get("y", y);
1756    attrs.get("dirX", dirX);
1757    attrs.get("dirY", dirY);
1758 
1759    my_map->set_start(x, y, dirX, dirY);
1760 }
1761 
handle_tile(const AttributeSet & attrs)1762 void MapLoader::handle_tile(const AttributeSet& attrs)
1763 {
1764    attrs.get("x", tile.x);
1765    attrs.get("y", tile.y);
1766 }
1767 
handle_station_part(const AttributeSet & attrs)1768 void MapLoader::handle_station_part(const AttributeSet& attrs)
1769 {
1770    int id;
1771    attrs.get("id", id);
1772 
1773    map<int, IStationPtr>::iterator it = my_stations.find(id);
1774    if (it == my_stations.end())
1775       throw runtime_error("No station definition for ID "
1776                           + boost::lexical_cast<string>(id));
1777    else
1778       my_map->set_station_at(tile, (*it).second);
1779 }
1780 
handle_straight_track(const AttributeSet & attrs)1781 void MapLoader::handle_straight_track(const AttributeSet& attrs)
1782 {
1783    string align;
1784    attrs.get("align", align);
1785 
1786    track::Direction axis = align == "x" ? axis::X : axis::Y;
1787 
1788    my_map->set_track_at(tile, make_straight_track(axis));
1789 }
1790 
handle_slope_track(const AttributeSet & attrs)1791 void MapLoader::handle_slope_track(const AttributeSet& attrs)
1792 {
1793    string align;
1794    attrs.get("align", align);
1795 
1796    track::Direction axis = align == "x" ? axis::X : axis::Y;
1797 
1798    bool level;
1799    VectorF slope = my_map->slope_at(tile, axis, level);
1800 
1801    bool a_valid, b_valid;
1802    VectorF slope_before = my_map->slope_before(tile, axis, b_valid);
1803    VectorF slope_after = my_map->slope_after(tile, axis, a_valid);
1804 
1805    if (!a_valid || !b_valid || !level)
1806       throw runtime_error("SlopeTrack in invalid location");
1807 
1808    my_map->set_track_at(tile,
1809                         make_slope_track(axis, slope, slope_before, slope_after));
1810 }
1811 
handle_points(const AttributeSet & attrs)1812 void MapLoader::handle_points(const AttributeSet& attrs)
1813 {
1814    string align;
1815    attrs.get("align", align);
1816 
1817    bool reflect;
1818    attrs.get("reflect", reflect);
1819 
1820    track::Direction dir =
1821       align == "x" ? axis::X
1822       : (align == "-x" ? -axis::X
1823          : (align == "y" ? axis::Y : -axis::Y));
1824 
1825    my_map->set_track_at(tile, make_points(dir, reflect));
1826 }
1827 
handle_crossover_track(const AttributeSet & attrs)1828 void MapLoader::handle_crossover_track(const AttributeSet& attrs)
1829 {
1830    my_map->set_track_at(tile, make_crossover_track());
1831 }
1832 
handle_spline_track(const AttributeSet & attrs)1833 void MapLoader::handle_spline_track(const AttributeSet& attrs)
1834 {
1835    VectorI delta;
1836    track::Direction entry_dir, exit_dir;
1837 
1838    attrs.get("delta-x", delta.x);
1839    attrs.get("delta-y", delta.y);
1840 
1841    attrs.get("entry-dir-x", entry_dir.x);
1842    attrs.get("entry-dir-y", entry_dir.z);
1843 
1844    attrs.get("exit-dir-x", exit_dir.x);
1845    attrs.get("exit-dir-y", exit_dir.z);
1846 
1847    my_map->set_track_at(tile, make_spline_track(delta, entry_dir, exit_dir));
1848 }
1849 
load_map(const string & a_res_id)1850 IMapPtr load_map(const string& a_res_id)
1851 {
1852    IResourcePtr res = find_resource(a_res_id, "maps");
1853 
1854    shared_ptr<Map> map(new Map(res));
1855 
1856    log() << "Loading map from file " << res->xml_file_name();
1857 
1858    static IXMLParserPtr xml_parser = make_xml_parser("schemas/map.xsd");
1859 
1860    MapLoader loader(map, res);
1861    xml_parser->parse(res->xml_file_name(), loader);
1862 
1863    return IMapPtr(map);
1864 }
1865