]> git.tdb.fi Git - r2c2.git/blob - source/3d/tracktype.cpp
b9ebc73c31708615536549b3e4a771c2a56b5ec1
[r2c2.git] / source / 3d / tracktype.cpp
1 #include <algorithm>
2 #include <cmath>
3 #include <msp/gl/technique.h>
4 #include "catalogue.h"
5 #include "tracktype.h"
6
7 using namespace std;
8 using namespace Msp;
9
10 namespace R2C2 {
11
12 TrackType3D::TrackType3D(Catalogue3D &c, const TrackType &tt):
13         ObjectType3D(c),
14         mesh(0),
15         object(0),
16         own_data(false)
17 {
18         const TrackAppearance &appearance = tt.get_appearance();
19         const vector<TrackPart> &parts = tt.get_parts();
20
21         const Profile &ballast_profile = appearance.get_ballast_profile();
22         const Vector &ballast_min = ballast_profile.get_min_coords();
23         const Vector &ballast_max = ballast_profile.get_max_coords();
24         float ballast_h = ballast_max.y-ballast_min.y;
25
26         const Profile &rail_profile = appearance.get_rail_profile();
27         const Vector &rail_min = rail_profile.get_min_coords();
28         const Vector &rail_max = rail_profile.get_max_coords();
29         float rail_h = rail_max.y-rail_min.y;
30
31         const Profile &tie_profile = appearance.get_tie_profile();
32         const Vector &tie_min = tie_profile.get_min_coords();
33         const Vector &tie_max = tie_profile.get_max_coords();
34         float tie_h = tie_max.y-tie_min.y;
35
36         float gauge = appearance.get_gauge();
37
38         string obj_name = tt.get_object();
39         if(!obj_name.empty())
40                 object = &catalogue.get<GL::Object>(obj_name);
41         else
42         {
43                 mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
44                 mesh->set_winding(&GL::WindingTest::counterclockwise());
45                 GL::MeshBuilder bld(*mesh);
46
47                 unsigned index = 0;
48                 bld.texcoord(0.17, 0.5);
49                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
50                         build_part(*i, ballast_profile, Vector(0, 0, -ballast_min.y), false, bld, index);
51
52                 float y = ballast_h-tie_min.y;
53                 bld.texcoord(0.83, 0.5);
54                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
55                         build_ties(*i, tie_profile, appearance.get_tie_length(), appearance.get_tie_spacing(), Vector(0, 0, y), bld, index);
56
57                 bld.texcoord(0.5, 0.5);
58                 y = ballast_h+tie_h-rail_min.y;
59                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
60                         build_part(*i, rail_profile, Vector(0, gauge/2, y), true, bld, index);
61                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
62                         build_part(*i, rail_profile, Vector(0, -gauge/2, y), false, bld, index);
63
64                 object = new GL::Object;
65                 object->set_mesh(mesh);
66                 object->set_technique(&catalogue.get<GL::Technique>(appearance.get_technique()));
67
68                 own_data = true;
69         }
70
71         unsigned paths = tt.get_paths();
72         for(int i=-1; i<=1; ++i)
73         {
74                 // TODO Make profile width configurable
75                 Profile profile;
76                 if(i==0)
77                 {
78                         float rail_w = (rail_max.x-rail_min.x)*2;
79                         profile.append_vertex(Vector(rail_w*-0.5, 0, 0), false);
80                         profile.append_vertex(Vector(rail_w*0.5, 0, 0), false);
81                 }
82                 else
83                 {
84                         profile.append_vertex(Vector(i*(gauge*0.5+rail_min.x*2), 0, 0), false);
85                         profile.append_vertex(Vector(i*(gauge*0.5+rail_max.x*2), 0, 0), false);
86                 }
87
88                 // TODO Avoid generating redundant meshes for single-path tracks
89                 for(int j=-1; j<=4; ++j)
90                 {
91                         if(j>=0 && !((paths>>j)&1))
92                                 continue;
93
94                         GL::Mesh *m = new GL::Mesh(GL::VERTEX3);
95                         GL::MeshBuilder bld(*m);
96                         unsigned index = 0;
97                         for(vector<TrackPart>::const_iterator k=parts.begin(); k!=parts.end(); ++k)
98                                 if(j<0 || k->get_path()==static_cast<unsigned>(j))
99                                         build_part(*k, profile, Vector(0, 0, ballast_h+1.5*rail_h), false, bld, index);
100                         path_meshes[(j&0xFF)|((i&3)<<8)] = m;
101                 }
102         }
103 }
104
105 TrackType3D::~TrackType3D()
106 {
107         for(map<unsigned, GL::Mesh *>::iterator i=path_meshes.begin(); i!=path_meshes.end(); ++i)
108                 delete i->second;
109         if(own_data)
110         {
111                 delete object;
112                 delete mesh;
113         }
114 }
115
116 const GL::Mesh &TrackType3D::get_path_mesh(int p, int s) const
117 {
118         unsigned key = (p<0 ? 0xFF : p) | ((s&3)<<8);
119         return *get_item(path_meshes, key);
120 }
121
122 void TrackType3D::build_part(const TrackPart &part, const Profile &profile, const Vector &offset, bool mirror, GL::MeshBuilder &bld, unsigned &base_index)
123 {
124         float plen = part.get_length();
125         unsigned nsegs = (part.is_curved() ? static_cast<unsigned>(plen*32)+1 : 1);
126
127         unsigned n_vertices = profile.get_n_vertices();
128         for(unsigned i=0; i<=nsegs; ++i)
129         {
130                 OrientedPoint basep = part.get_point(i*plen/nsegs);
131                 Transform trans = Transform::rotation(basep.rotation, Vector(0, 0, 1));
132
133                 for(unsigned j=0; j<n_vertices; ++j)
134                 {
135                         const Profile::Vertex &v = profile.get_vertex(mirror ? n_vertices-1-j : j);
136                         Vector p(0, -v.pos.x, v.pos.y);
137                         if(mirror)
138                                 p.y = -p.y;
139                         p = basep.position+trans.transform(offset+p);
140
141                         Vector n(0, -v.normal.x, v.normal.y);
142                         if(mirror)
143                                 n.y = -n.y;
144                         n = trans.transform(n);
145
146                         bld.normal(n.x, n.y, n.z);
147                         bld.vertex(p.x, p.y, p.z);
148                 }
149         }
150
151         for(unsigned i=0; i+1<n_vertices; )
152         {
153                 bld.begin(GL::TRIANGLE_STRIP);
154                 for(unsigned j=0; j<=nsegs; ++j)
155                 {
156                         unsigned k = j*n_vertices+i;
157                         bld.element(base_index+k+1);
158                         bld.element(base_index+k);
159                 }
160                 bld.end();
161
162                 ++i;
163                 if(!profile.get_vertex(i).smooth)
164                         ++i;
165         }
166
167         base_index += (nsegs+1)*n_vertices;
168 }
169
170 void TrackType3D::build_ties(const TrackPart &part, const Profile &profile, float length, float spacing, const Vector &offset, GL::MeshBuilder &bld, unsigned &base_index)
171 {
172         float plen = part.get_length();
173         unsigned count = plen/spacing+0.5;
174         (void)length;
175         (void)offset;
176         (void)profile;
177
178         unsigned n_vertices = profile.get_n_vertices();
179         float min_y = profile.get_min_coords().y;
180         vector<Vector> cap_vertices;
181         for(unsigned i=0; i+1<n_vertices; )
182         {
183                 const Profile::Vertex &v = profile.get_vertex(i);
184                 if(cap_vertices.size()>=2)
185                 {
186                         if(i+2<n_vertices)
187                                 cap_vertices.push_back(Vector(v.pos.x, min_y, 0));
188                         else
189                                 cap_vertices.push_back(profile.get_vertex(n_vertices-1).pos);
190                 }
191                 cap_vertices.push_back(v.pos);
192
193                 ++i;
194                 if(!profile.get_vertex(i).smooth)
195                         ++i;
196         }
197
198         for(unsigned i=0; i<count; ++i)
199         {
200                 OrientedPoint basep = part.get_point((i+0.5)*plen/count);
201                 Transform trans = Transform::rotation(basep.rotation, Vector(0, 0, 1));
202
203                 unsigned strip = 0;
204                 for(unsigned j=0; j<n_vertices; ++j)
205                 {
206                         const Profile::Vertex &v = profile.get_vertex(j);
207
208                         Vector n = trans.transform(Vector(0, -v.normal.x, v.normal.y));
209                         bld.normal(n.x, n.y, n.z);
210
211                         if(!strip)
212                                 bld.begin(GL::TRIANGLE_STRIP);
213
214                         for(unsigned k=0; k<2; ++k)
215                         {
216                                 Vector p((k-0.5)*length, -v.pos.x, v.pos.y);
217                                 p = basep.position+trans.transform(offset+p);
218                                 bld.vertex(p.x, p.y, p.z);
219                         }
220
221                         if(++strip>=2 && !profile.get_vertex(j).smooth)
222                         {
223                                 bld.end();
224                                 strip = 0;
225                         }
226                 }
227                 if(strip)
228                         bld.end();
229
230                 for(unsigned j=0; j<2; ++j)
231                 {
232                         Vector n = trans.transform(Vector(j*2.0-1, 0, 0));
233                         bld.normal(n.x, n.y, n.z);
234
235                         bld.begin(GL::TRIANGLE_STRIP);
236                         for(unsigned k=0; k<cap_vertices.size(); ++k)
237                         {
238                                 const Vector &v = cap_vertices[k^j];
239                                 Vector p((j-0.5)*length, -v.x, v.y);
240                                 p = basep.position+trans.transform(offset+p);
241                                 bld.vertex(p.x, p.y, p.z);
242                         }
243                         bld.end();
244                 }
245
246                 base_index += 2*(n_vertices+cap_vertices.size());
247         }
248 }
249
250 } // namespace R2C2