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