]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/terrain.cpp
Properly round negative z coordinates for Terrain
[r2c2.git] / source / libr2c2 / terrain.cpp
index e84c7cdc1c360102a89fb60d92af868363e27d2f..ccfd912a2fd65e5e1048aa5c14b759fe7382bc3c 100644 (file)
@@ -1,3 +1,4 @@
+#include <cmath>
 #include <msp/geometry/box.h>
 #include <msp/geometry/transformedshape.h>
 #include "layout.h"
@@ -37,6 +38,8 @@ Terrain *Terrain::clone(Layout *to_layout) const
 void Terrain::set_position(const Vector &p)
 {
        position = p;
+       float eg = type.get_elevation_granularity();
+       position.z = round(position.z/eg)*eg;
        signal_moved.emit();
 }
 
@@ -62,7 +65,7 @@ void Terrain::set_size(unsigned w, unsigned h)
 
        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));
@@ -83,7 +86,7 @@ void Terrain::set_node_elevation(const NodeCoordinates &c, float elev, bool join
                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)
        {
@@ -97,7 +100,7 @@ void Terrain::set_node_elevation(const NodeCoordinates &c, float elev, bool join
                                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);
                                }
                        }
@@ -105,7 +108,7 @@ void Terrain::set_node_elevation(const NodeCoordinates &c, float elev, bool join
        }
        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);
        }
 }
@@ -132,6 +135,11 @@ Vector Terrain::get_node_position(const NodeCoordinates &c) const
 
 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)
@@ -139,22 +147,23 @@ Terrain::NodeCoordinates Terrain::get_closest_node(const Ray &ray) const
                        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)
        {
@@ -182,28 +191,51 @@ Terrain::Tile::Tile():
        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),
@@ -252,6 +284,7 @@ void Terrain::Loader::tile()
        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;