2 #include <msp/geometry/box.h>
3 #include <msp/geometry/transformedshape.h>
6 #include "terraintype.h"
13 Terrain::Terrain(Layout &l, const TerrainType &t):
29 Terrain *Terrain::clone(Layout *to_layout) const
31 Terrain *terrain = new Terrain((to_layout ? *to_layout : layout), type);
32 terrain->set_size(width, height);
33 for(unsigned i=0; i<tiles.size(); ++i)
34 terrain->tiles[i] = tiles[i];
38 void Terrain::set_position(const Vector &p)
41 float eg = type.get_elevation_granularity();
42 position.z = int(position.z/eg+0.5)*eg;
46 void Terrain::set_rotation(const Angle &r)
52 void Terrain::set_size(unsigned w, unsigned h)
55 throw invalid_argument("Terrain::set_size");
57 vector<Tile> new_tiles(w*h);
58 for(unsigned y=0; (y<h && y<height); ++y)
59 for(unsigned x=0; (x<w && x<width); ++x)
60 new_tiles[x+y*w] = tiles[x+y*width];
64 swap(tiles, new_tiles);
67 float ts = type.get_tile_size();
68 Vector dim(width*ts, height*ts, ts/10.0f);
69 shape = new Geometry::TransformedShape<float, 3>(
70 Geometry::Box<float>(dim),
71 Transform::translation(dim/2.0f));
73 signal_size_changed.emit(width, height);
76 const Terrain::Tile &Terrain::get_tile(unsigned x, unsigned y) const
78 if(x>=width || y>=height)
79 throw out_of_range("Terrain::get_tile");
80 return tiles[x+y*width];
83 void Terrain::set_node_elevation(const NodeCoordinates &c, float elev, bool joined)
85 if(c.x>=width || c.y>=height || c.i>=4)
86 throw out_of_range("Terrain::set_node_elevation");
88 float eg = type.get_elevation_granularity();
89 elev = floor(elev/eg+0.5)*eg;
93 float ref = tiles[c.x+c.y*width].nodes[c.i].elevation;
94 for(unsigned i=0; i<4; ++i)
96 unsigned x = c.x+c.i%2-i%2;
97 unsigned y = c.y+c.i/2-i/2;
98 if(x<width && y<height)
100 Tile &tile = tiles[x+y*width];
101 if(tile.nodes[i].elevation==ref)
103 tile.set_node_elevation(i, elev);
104 signal_tile_changed.emit(x, y);
111 tiles[c.x+c.y*width].set_node_elevation(c.i, elev);
112 signal_tile_changed.emit(c.x, c.y);
116 float Terrain::get_node_elevation(const NodeCoordinates &c) const
118 if(c.x>=width || c.y>=height || c.i>=4)
119 throw out_of_range("Terrain::get_node_elevation");
121 return tiles[c.x+c.y*width].nodes[c.i].elevation;
124 Vector Terrain::get_node_position(const NodeCoordinates &c) const
126 if(c.x>=width || c.y>=height || c.i>=4)
127 throw out_of_range("Terrain::get_node_position");
129 const Tile &tile = tiles[c.x+c.y*width];
130 float tile_size = type.get_tile_size();
131 Transform trans = Transform::translation(position)*
132 Transform::rotation(rotation, Vector(0, 0, 1));
133 return trans.transform(Vector((c.x+c.i%2)*tile_size, (c.y+c.i/2)*tile_size, tile.nodes[c.i].elevation));
136 Terrain::NodeCoordinates Terrain::get_closest_node(const Ray &ray) const
138 Transform reverse_trans = Transform::rotation(rotation, Vector(0, 0, -1))*
139 Transform::translation(-position);
140 Ray local_ray = reverse_trans.transform(ray);
142 float ts = type.get_tile_size();
143 NodeCoordinates coords;
144 float closest_dist = -1;
145 for(unsigned y=0; y<height; ++y)
146 for(unsigned x=0; x<width; ++x)
147 for(unsigned i=0; i<4; ++i)
149 NodeCoordinates c(x, y, i);
150 Vector node_pos((x+0.25+(i%2)*0.5)*ts, (y+0.25+(i/2)*0.5)*ts, get_node_elevation(c));
151 Vector v = node_pos-local_ray.get_start();
152 float dist = (v-local_ray.get_direction()*dot(local_ray.get_direction(), v)).norm();
153 if(closest_dist<0 || dist<closest_dist)
163 void Terrain::save(list<DataFile::Statement> &st) const
165 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
166 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
167 st.push_back((DataFile::Statement("size"), width, height));
168 for(vector<Tile>::const_iterator i=tiles.begin(); i!=tiles.end(); ++i)
170 DataFile::Statement ss("tile");
177 Terrain::Node::Node():
183 void Terrain::Node::save(list<DataFile::Statement> &st) const
185 st.push_back((DataFile::Statement("elevation"), elevation));
186 st.push_back((DataFile::Statement("ground"), ground));
190 Terrain::Tile::Tile():
191 secondary_axis(false)
194 void Terrain::Tile::set_node_elevation(unsigned i, float e)
196 nodes[i].elevation = e;
200 void Terrain::Tile::update_axis()
202 secondary_axis = (abs(nodes[1].elevation-nodes[2].elevation) < abs(nodes[0].elevation-nodes[3].elevation));
205 void Terrain::Tile::save(list<DataFile::Statement> &st) const
207 float elevation_mode = find_mode(&Node::elevation);
208 unsigned ground_mode = find_mode(&Node::ground);
210 st.push_back((DataFile::Statement("elevation"), elevation_mode));
212 st.push_back((DataFile::Statement("ground"), ground_mode));
214 for(unsigned i=0; i<4; ++i)
216 if(nodes[i].elevation==elevation_mode && nodes[i].ground==ground_mode)
219 DataFile::Statement ss("node");
221 if(nodes[i].elevation!=elevation_mode)
222 ss.sub.push_back((DataFile::Statement("elevation"), nodes[i].elevation));
223 if(nodes[i].ground!=ground_mode)
224 ss.sub.push_back((DataFile::Statement("ground"), nodes[i].ground));
230 T Terrain::Tile::find_mode(T Node::*member) const
232 for(unsigned i=0; i<3; ++i)
233 for(unsigned j=i+1; j<4; ++j)
234 if(nodes[i].*member==nodes[j].*member)
235 return nodes[i].*member;
240 Terrain::NodeCoordinates::NodeCoordinates():
246 Terrain::NodeCoordinates::NodeCoordinates(unsigned x_, unsigned y_, unsigned i_):
253 Terrain::Loader::Loader(Terrain &t):
254 DataFile::ObjectLoader<Terrain>(t),
257 add("position", &Loader::position);
258 add("rotation", &Loader::rotation);
259 add("size", &Loader::size);
260 add("tile", &Loader::tile);
261 add("tile", &Loader::tile_coords);
264 void Terrain::Loader::position(float x, float y, float z)
266 obj.set_position(Vector(x, y, z));
269 void Terrain::Loader::rotation(float a)
271 obj.set_rotation(Angle::from_radians(a));
274 void Terrain::Loader::size(unsigned w, unsigned h)
279 void Terrain::Loader::tile()
281 if(next_tile>=obj.tiles.size())
282 throw runtime_error("Terrain::Loader::tile");
284 Tile &t = obj.tiles[next_tile];
285 Tile::Loader ldr(obj, t);
288 unsigned x = next_tile%obj.width;
289 unsigned y = next_tile/obj.width;
291 obj.signal_tile_changed.emit(x, y);
294 void Terrain::Loader::tile_coords(unsigned x, unsigned y)
296 if(x>=obj.width || y>=obj.height)
297 throw out_of_range("Terrain::Loader::tile");
298 next_tile = x+y*obj.width;
303 Terrain::Node::Loader::Loader(Terrain &t, Node &n):
304 DataFile::ObjectLoader<Node>(n),
307 add("ground", &Loader::ground);
308 add("elevation", &Node::elevation);
311 void Terrain::Node::Loader::ground(unsigned g)
313 if(g>=terrain.type.get_n_surface_types())
314 throw out_of_range("Tile::Loader::surface");
319 Terrain::Tile::Loader::Loader(Terrain &t, Tile &l):
320 DataFile::ObjectLoader<Tile>(l),
323 add("ground", &Loader::ground);
324 add("elevation", &Loader::elevation);
325 add("node", &Loader::node);
328 void Terrain::Tile::Loader::ground(unsigned g)
330 if(g>=terrain.type.get_n_surface_types())
331 throw out_of_range("Tile::Loader::surface");
332 for(unsigned i=0; i<4; ++i)
333 obj.nodes[i].ground = g;
336 void Terrain::Tile::Loader::elevation(float h)
338 for(unsigned i=0; i<4; ++i)
339 obj.nodes[i].elevation = h;
342 void Terrain::Tile::Loader::node(unsigned i)
345 throw out_of_range("Tile::Loader::node");
346 Node::Loader ldr(terrain, obj.nodes[i]);