1 #include <msp/geometry/box.h>
2 #include <msp/geometry/transformedshape.h>
5 #include "terraintype.h"
12 Terrain::Terrain(Layout &l, const TerrainType &t):
28 Terrain *Terrain::clone(Layout *to_layout) const
30 Terrain *terrain = new Terrain((to_layout ? *to_layout : layout), type);
31 terrain->set_size(width, height);
32 for(unsigned i=0; i<tiles.size(); ++i)
33 terrain->tiles[i] = tiles[i];
37 void Terrain::set_position(const Vector &p)
43 void Terrain::set_rotation(const Angle &r)
49 void Terrain::set_size(unsigned w, unsigned h)
52 throw invalid_argument("Terrain::set_size");
54 vector<Tile> new_tiles(w*h);
55 for(unsigned y=0; (y<h && y<height); ++y)
56 for(unsigned x=0; (x<w && x<width); ++x)
57 new_tiles[x+y*w] = tiles[x+y*width];
61 swap(tiles, new_tiles);
64 float ts = type.get_tile_size();
65 Vector dim(width*ts, height*ts, ts);
66 shape = new Geometry::TransformedShape<float, 3>(
67 Geometry::Box<float>(dim),
68 Transform::translation(dim/2.0f));
70 signal_size_changed.emit(width, height);
73 const Terrain::Tile &Terrain::get_tile(unsigned x, unsigned y) const
75 if(x>=width || y>=height)
76 throw out_of_range("Terrain::get_tile");
77 return tiles[x+y*width];
80 void Terrain::set_node_elevation(const NodeCoordinates &c, float elev, bool joined)
82 if(c.x>=width || c.y>=height || c.i>=4)
83 throw out_of_range("Terrain::set_node_elevation");
85 float eg = type.get_elevation_granularity();
86 elev = int(elev/eg+0.5)*eg;
90 float ref = tiles[c.x+c.y*width].nodes[c.i].elevation;
91 for(unsigned i=0; i<4; ++i)
93 unsigned x = c.x+c.i%2-i%2;
94 unsigned y = c.y+c.i/2-i/2;
95 if(x<width && y<height)
97 Tile &tile = tiles[x+y*width];
98 if(tile.nodes[i].elevation==ref)
100 tile.set_node_elevation(i, elev);
101 signal_tile_changed.emit(x, y);
108 tiles[c.x+c.y*width].set_node_elevation(c.i, elev);
109 signal_tile_changed.emit(c.x, c.y);
113 float Terrain::get_node_elevation(const NodeCoordinates &c) const
115 if(c.x>=width || c.y>=height || c.i>=4)
116 throw out_of_range("Terrain::get_node_elevation");
118 return tiles[c.x+c.y*width].nodes[c.i].elevation;
121 Vector Terrain::get_node_position(const NodeCoordinates &c) const
123 if(c.x>=width || c.y>=height || c.i>=4)
124 throw out_of_range("Terrain::get_node_position");
126 const Tile &tile = tiles[c.x+c.y*width];
127 float tile_size = type.get_tile_size();
128 Transform trans = Transform::translation(position)*
129 Transform::rotation(rotation, Vector(0, 0, 1));
130 return trans.transform(Vector((c.x+c.i%2)*tile_size, (c.y+c.i/2)*tile_size, tile.nodes[c.i].elevation));
133 Terrain::NodeCoordinates Terrain::get_closest_node(const Ray &ray) const
135 Transform reverse_trans = Transform::rotation(rotation, Vector(0, 0, -1))*
136 Transform::translation(-position);
137 Ray local_ray = reverse_trans.transform(ray);
139 float ts = type.get_tile_size();
140 NodeCoordinates coords;
141 float closest_dist = -1;
142 for(unsigned y=0; y<height; ++y)
143 for(unsigned x=0; x<width; ++x)
144 for(unsigned i=0; i<4; ++i)
146 NodeCoordinates c(x, y, i);
147 Vector node_pos((x+0.25+(i%2)*0.5)*ts, (y+0.25+(i/2)*0.5)*ts, get_node_elevation(c));
148 Vector v = node_pos-local_ray.get_start();
149 float dist = (v-local_ray.get_direction()*dot(local_ray.get_direction(), v)).norm();
150 if(closest_dist<0 || dist<closest_dist)
160 void Terrain::save(list<DataFile::Statement> &st) const
162 st.push_back((DataFile::Statement("size"), width, height));
163 for(vector<Tile>::const_iterator i=tiles.begin(); i!=tiles.end(); ++i)
165 DataFile::Statement ss("tile");
172 Terrain::Node::Node():
178 void Terrain::Node::save(list<DataFile::Statement> &st) const
180 st.push_back((DataFile::Statement("elevation"), elevation));
181 st.push_back((DataFile::Statement("ground"), ground));
185 Terrain::Tile::Tile():
186 secondary_axis(false)
189 void Terrain::Tile::set_node_elevation(unsigned i, float e)
191 nodes[i].elevation = e;
192 secondary_axis = ((nodes[1].elevation+nodes[2].elevation) < (nodes[0].elevation+nodes[3].elevation));
195 void Terrain::Tile::save(list<DataFile::Statement> &st) const
198 for(unsigned i=1; (flat && i<4); ++i)
199 flat = (nodes[i].elevation==nodes[0].elevation && nodes[i].ground==nodes[0].ground && nodes[i].wall==nodes[0].wall);
202 st.push_back((DataFile::Statement("elevation"), nodes[0].elevation));
203 st.push_back((DataFile::Statement("ground"), nodes[0].ground));
207 for(unsigned i=0; i<4; ++i)
209 DataFile::Statement ss("node");
211 nodes[i].save(ss.sub);
218 Terrain::NodeCoordinates::NodeCoordinates():
224 Terrain::NodeCoordinates::NodeCoordinates(unsigned x_, unsigned y_, unsigned i_):
231 Terrain::Loader::Loader(Terrain &t):
232 DataFile::ObjectLoader<Terrain>(t),
235 add("position", &Loader::position);
236 add("rotation", &Loader::rotation);
237 add("size", &Loader::size);
238 add("tile", &Loader::tile);
239 add("tile", &Loader::tile_coords);
242 void Terrain::Loader::position(float x, float y, float z)
244 obj.set_position(Vector(x, y, z));
247 void Terrain::Loader::rotation(float a)
249 obj.set_rotation(Angle::from_radians(a));
252 void Terrain::Loader::size(unsigned w, unsigned h)
257 void Terrain::Loader::tile()
259 if(next_tile>=obj.tiles.size())
260 throw runtime_error("Terrain::Loader::tile");
262 Tile &t = obj.tiles[next_tile];
263 Tile::Loader ldr(obj, t);
265 unsigned x = next_tile%obj.width;
266 unsigned y = next_tile/obj.width;
268 obj.signal_tile_changed.emit(x, y);
271 void Terrain::Loader::tile_coords(unsigned x, unsigned y)
273 if(x>=obj.width || y>=obj.height)
274 throw out_of_range("Terrain::Loader::tile");
275 next_tile = x+y*obj.width;
280 Terrain::Node::Loader::Loader(Terrain &t, Node &n):
281 DataFile::ObjectLoader<Node>(n),
284 add("ground", &Loader::ground);
285 add("elevation", &Node::elevation);
288 void Terrain::Node::Loader::ground(unsigned g)
290 if(g>=terrain.type.get_n_surface_types())
291 throw out_of_range("Tile::Loader::surface");
296 Terrain::Tile::Loader::Loader(Terrain &t, Tile &l):
297 DataFile::ObjectLoader<Tile>(l),
300 add("ground", &Loader::ground);
301 add("elevation", &Loader::elevation);
302 add("node", &Loader::node);
305 void Terrain::Tile::Loader::ground(unsigned g)
307 if(g>=terrain.type.get_n_surface_types())
308 throw out_of_range("Tile::Loader::surface");
309 for(unsigned i=0; i<4; ++i)
310 obj.nodes[i].ground = g;
313 void Terrain::Tile::Loader::elevation(float h)
315 for(unsigned i=0; i<4; ++i)
316 obj.nodes[i].elevation = h;
319 void Terrain::Tile::Loader::node(unsigned i)
322 throw out_of_range("Tile::Loader::node");
323 Node::Loader ldr(terrain, obj.nodes[i]);