+#include <cmath>
#include <msp/geometry/box.h>
#include <msp/geometry/transformedshape.h>
#include "layout.h"
void Terrain::set_position(const Vector &p)
{
position = p;
+ float eg = type.get_elevation_granularity();
+ position.z = int(position.z/eg+0.5)*eg;
signal_moved.emit();
}
delete shape;
float ts = type.get_tile_size();
- Vector dim(width*ts, height*ts, ts);
+ Vector dim(width*ts, height*ts, ts/10.0f);
shape = new Geometry::TransformedShape<float, 3>(
Geometry::Box<float>(dim),
Transform::translation(dim/2.0f));
throw out_of_range("Terrain::set_node_elevation");
float eg = type.get_elevation_granularity();
- elev = int(elev/eg+0.5)*eg;
+ elev = floor(elev/eg+0.5)*eg;
if(joined)
{
Tile &tile = tiles[x+y*width];
if(tile.nodes[i].elevation==ref)
{
- tile.nodes[i].elevation = elev;
+ tile.set_node_elevation(i, elev);
signal_tile_changed.emit(x, y);
}
}
}
else
{
- tiles[c.x+c.y*width].nodes[c.i].elevation = elev;
+ tiles[c.x+c.y*width].set_node_elevation(c.i, elev);
signal_tile_changed.emit(c.x, c.y);
}
}
Terrain::NodeCoordinates Terrain::get_closest_node(const Ray &ray) const
{
+ Transform reverse_trans = Transform::rotation(rotation, Vector(0, 0, -1))*
+ Transform::translation(-position);
+ Ray local_ray = reverse_trans.transform(ray);
+
+ float ts = type.get_tile_size();
NodeCoordinates coords;
float closest_dist = -1;
for(unsigned y=0; y<height; ++y)
for(unsigned i=0; i<4; ++i)
{
NodeCoordinates c(x, y, i);
- /* XXX This is not very efficient. Should transform the ray to
- local coordinate system. */
- Vector node_pos = get_node_position(c);
- Vector v = node_pos-ray.get_start();
- float dist = (v-ray.get_direction()*dot(ray.get_direction(), v)).norm();
+ Vector node_pos((x+0.25+(i%2)*0.5)*ts, (y+0.25+(i/2)*0.5)*ts, get_node_elevation(c));
+ Vector v = node_pos-local_ray.get_start();
+ float dist = (v-local_ray.get_direction()*dot(local_ray.get_direction(), v)).norm();
if(closest_dist<0 || dist<closest_dist)
{
coords = c;
closest_dist = dist;
}
}
+
return coords;
}
void Terrain::save(list<DataFile::Statement> &st) const
{
+ st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
+ st.push_back((DataFile::Statement("rotation"), rotation.radians()));
st.push_back((DataFile::Statement("size"), width, height));
for(vector<Tile>::const_iterator i=tiles.begin(); i!=tiles.end(); ++i)
{
secondary_axis(false)
{ }
+void Terrain::Tile::set_node_elevation(unsigned i, float e)
+{
+ nodes[i].elevation = e;
+ update_axis();
+}
+
+void Terrain::Tile::update_axis()
+{
+ secondary_axis = (abs(nodes[1].elevation-nodes[2].elevation) < abs(nodes[0].elevation-nodes[3].elevation));
+}
+
void Terrain::Tile::save(list<DataFile::Statement> &st) const
{
- bool flat = true;
- for(unsigned i=1; (flat && i<4); ++i)
- flat = (nodes[i].elevation==nodes[0].elevation && nodes[i].ground==nodes[0].ground && nodes[i].wall==nodes[0].wall);
- if(flat)
- {
- st.push_back((DataFile::Statement("elevation"), nodes[0].elevation));
- st.push_back((DataFile::Statement("ground"), nodes[0].ground));
- }
- else
+ float elevation_mode = find_mode(&Node::elevation);
+ unsigned ground_mode = find_mode(&Node::ground);
+ if(elevation_mode)
+ st.push_back((DataFile::Statement("elevation"), elevation_mode));
+ if(ground_mode)
+ st.push_back((DataFile::Statement("ground"), ground_mode));
+
+ for(unsigned i=0; i<4; ++i)
{
- for(unsigned i=0; i<4; ++i)
- {
- DataFile::Statement ss("node");
- ss.append(i);
- nodes[i].save(ss.sub);
- st.push_back(ss);
- }
+ if(nodes[i].elevation==elevation_mode && nodes[i].ground==ground_mode)
+ continue;
+
+ DataFile::Statement ss("node");
+ ss.append(i);
+ if(nodes[i].elevation!=elevation_mode)
+ ss.sub.push_back((DataFile::Statement("elevation"), nodes[i].elevation));
+ if(nodes[i].ground!=ground_mode)
+ ss.sub.push_back((DataFile::Statement("ground"), nodes[i].ground));
+ st.push_back(ss);
}
}
+template<typename T>
+T Terrain::Tile::find_mode(T Node::*member) const
+{
+ for(unsigned i=0; i<3; ++i)
+ for(unsigned j=i+1; j<4; ++j)
+ if(nodes[i].*member==nodes[j].*member)
+ return nodes[i].*member;
+ return T();
+}
+
Terrain::NodeCoordinates::NodeCoordinates():
x(0),
Tile &t = obj.tiles[next_tile];
Tile::Loader ldr(obj, t);
load_sub_with(ldr);
+ t.update_axis();
unsigned x = next_tile%obj.width;
unsigned y = next_tile/obj.width;
++next_tile;