]> git.tdb.fi Git - r2c2.git/blob - source/3d/terrain.cpp
Add editable terrain objects
[r2c2.git] / source / 3d / terrain.cpp
1 #include <msp/gl/renderer.h>
2 #include "libr2c2/terraintype.h"
3 #include "layout.h"
4 #include "terrain.h"
5
6 using namespace Msp;
7
8 namespace R2C2 {
9
10 unsigned Terrain3D::surface_indices[] =
11 {
12         0, 1, 3, 0, 3, 2,
13         1, 3, 2, 1, 2, 0
14 };
15
16 unsigned Terrain3D::wall_indices[] =
17 {
18         1, 4, 5, 1, 5, 3,
19         3, 7, 6, 3, 6, 2
20 };
21
22 Terrain3D::Terrain3D(Layout3D &l, Terrain &t):
23         Object3D(l, t),
24         terrain(t),
25         mesh((GL::VERTEX3, GL::NORMAL3)),
26         tech(layout.get_catalogue().get<GL::Technique>("terrain.tech"))
27 {
28         terrain.signal_size_changed.connect(sigc::mem_fun(this, &Terrain3D::size_changed));
29         terrain.signal_tile_changed.connect(sigc::bind<2>(sigc::mem_fun(this, &Terrain3D::tile_changed), true));
30
31         create_mesh();
32
33         layout.get_scene().add(*this);
34 }
35
36 Terrain3D::~Terrain3D()
37 {
38         layout.get_scene().remove(*this);
39 }
40
41 Vector Terrain3D::get_node() const
42 {
43         float ts = terrain.get_type().get_tile_size();
44         return terrain.get_position()+Vector(terrain.get_width()*ts/2, terrain.get_height()*ts/2, 0);
45 }
46
47 void Terrain3D::create_mesh()
48 {
49         mesh.clear();
50
51         GL::MeshBuilder bld(mesh);
52         unsigned width = terrain.get_width();
53         unsigned height = terrain.get_height();
54         bld.begin(GL::TRIANGLES);
55         for(unsigned y=0; y<height; ++y)
56                 for(unsigned x=0; x<width; ++x)
57                 {
58                         const Terrain::Tile &tile = terrain.get_tile(x, y);
59                         Vector nodes[8];
60                         gather_nodes(x, y, nodes);
61
62                         const unsigned *indices = surface_indices+tile.secondary_axis*6;
63                         build_triangle(bld, nodes, indices);
64                         build_triangle(bld, nodes, indices+3);
65
66                         if(x+1<width)
67                         {
68                                 build_triangle(bld, nodes, wall_indices);
69                                 build_triangle(bld, nodes, wall_indices+3);
70                         }
71
72                         if(y+1<height)
73                         {
74                                 build_triangle(bld, nodes, wall_indices+6);
75                                 build_triangle(bld, nodes, wall_indices+9);
76                         }
77                 }
78         bld.end();
79 }
80
81 void Terrain3D::gather_nodes(unsigned x, unsigned y, Vector *nodes)
82 {
83         const Terrain::Tile &tile = terrain.get_tile(x, y);
84         float ts = terrain.get_type().get_tile_size();
85
86         for(unsigned i=0; i<4; ++i)
87                 nodes[i] = Vector((x+i%2)*ts, (y+i/2)*ts, tile.nodes[i].elevation);
88
89         if(x+1<terrain.get_width())
90         {
91                 const Terrain::Tile &neighbor = terrain.get_tile(x+1, y);
92                 nodes[4] = Vector((x+1)*ts, y*ts, neighbor.nodes[0].elevation);
93                 nodes[5] = Vector((x+1)*ts, (y+1)*ts, neighbor.nodes[2].elevation);
94         }
95
96         if(y+1<terrain.get_height())
97         {
98                 const Terrain::Tile &neighbor = terrain.get_tile(x, y+1);
99                 nodes[6] = Vector(x*ts, (y+1)*ts, neighbor.nodes[0].elevation);
100                 nodes[7] = Vector((x+1)*ts, (y+1)*ts, neighbor.nodes[1].elevation);
101         }
102 }
103
104 void Terrain3D::build_triangle(GL::PrimitiveBuilder &bld, const Vector *nodes, const unsigned *indices)
105 {
106         bld.normal(normalize(cross(nodes[indices[1]]-nodes[indices[0]], nodes[indices[2]]-nodes[indices[0]])));
107         for(unsigned i=0; i<3; ++i)
108                 bld.vertex(nodes[indices[i]]);
109 }
110
111 void Terrain3D::update_triangle(unsigned offset, const Vector *nodes, const unsigned *indices)
112 {
113         const GL::VertexFormat &format = mesh.get_vertices().get_format();
114         unsigned position_offset = format.offset(GL::VERTEX3);
115         unsigned normal_offset = format.offset(GL::NORMAL3);
116
117         Vector normal = normalize(cross(nodes[indices[1]]-nodes[indices[0]], nodes[indices[2]]-nodes[indices[0]]));
118         for(unsigned i=0; i<3; ++i)
119         {
120                 float *vertex = mesh.modify_vertex(offset+i);
121                 for(unsigned j=0; j<3; ++j)
122                 {
123                         vertex[position_offset+j] = nodes[indices[i]][j];
124                         vertex[normal_offset+j] = normal[j];
125                 }
126         }
127 }
128
129 void Terrain3D::size_changed(unsigned, unsigned)
130 {
131         create_mesh();
132 }
133
134 void Terrain3D::tile_changed(unsigned x, unsigned y, bool propagate)
135 {
136         Vector nodes[8];
137         gather_nodes(x, y, nodes);
138
139         unsigned base = y*(terrain.get_width()*18-6);
140         if(y+1==terrain.get_height())
141                 base += x*12;
142         else
143                 base += x*18;
144
145         const Terrain::Tile &tile = terrain.get_tile(x, y);
146         const unsigned *indices = surface_indices+tile.secondary_axis*6;
147         update_triangle(base, nodes, indices);
148         update_triangle(base+3, nodes, indices+3);
149
150         if(x+1<terrain.get_width())
151         {
152                 base += 6;
153                 update_triangle(base, nodes, wall_indices);
154                 update_triangle(base+3, nodes, wall_indices+3);
155         }
156
157         if(y+1<terrain.get_height())
158         {
159                 base += 6;
160                 update_triangle(base, nodes, wall_indices+6);
161                 update_triangle(base+3, nodes, wall_indices+9);
162         }
163
164         if(propagate)
165         {
166                 if(x>0)
167                         tile_changed(x-1, y, false);
168                 if(y>0)
169                         tile_changed(x, y-1, false);
170         }
171 }
172
173 void Terrain3D::render(GL::Renderer &renderer, const GL::Tag &tag) const
174 {
175         if(!tech.has_pass(tag))
176                 return;
177
178         GL::Renderer::Push push(renderer);
179
180         renderer.matrix_stack() *= matrix;
181
182         const GL::RenderPass &pass = tech.get_pass(tag);
183         renderer.set_material(pass.get_material());
184         renderer.set_texturing(pass.get_texturing());
185         renderer.set_shader_program(pass.get_shader_program(), pass.get_shader_data());
186
187         mesh.draw(renderer);
188 }
189
190 } // namespace R2C2