]> git.tdb.fi Git - r2c2.git/blob - source/3d/tracktype.cpp
Make use of the mspmath library
[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 {
11
12 bool compare_z(const R2C2::Vector &p1, const R2C2::Vector &p2)
13 {
14         return p1.z<p2.z;
15 }
16
17 template<typename Iter>
18 Iter graham_scan(Iter begin, Iter end)
19 {
20         // http://en.wikipedia.org/wiki/Graham_scan
21
22         // Find point with lowest X coordinate
23         R2C2::Vector lowest = *begin;
24         for(Iter i=begin; i!=end; ++i)
25                 if(i->x<lowest.x || (i->x==lowest.x && i->y>lowest.y))
26                         lowest = *i;
27
28         // Compute tangents and sort points
29         for(Iter i=begin; i!=end; ++i)
30                 i->z = (i->x==lowest.x ? 1e5/(i->y-lowest.y-1) : (i->y-lowest.y)/(i->x-lowest.x));
31         sort(begin, end, compare_z);
32
33         for(Iter k=begin, i=k++, j=k++;; )
34         {
35                 // Compute winding by cross product
36                 float turn = cross(*j-*i, *k-*j).z;
37
38                 if(turn<1e-5)
39                 {
40                         // Right turn - throw the middle point away
41                         // Special case for collinear vertices in the beginning
42                         if(i==begin)
43                                 j = k++;
44                         else
45                                 j = i--;
46                 }
47                 else
48                 {
49                         // Left turn - store the middle point and advance
50                         if(++i!=j)
51                                 *i = *j;
52                         j = k++;
53                 }
54
55                 // Cycle back to beginning and terminate after checking the last point
56                 if(k==end)
57                         k = begin;
58                 else if(j==begin)
59                         return ++i;
60         }
61 }
62
63 }
64
65 namespace R2C2 {
66
67 TrackType3D::TrackType3D(Catalogue3D &cat3d, const TrackType &tt):
68         catalogue(cat3d),
69         mesh(0),
70         object(0)
71 {
72         const Catalogue &cat = cat3d.get_catalogue();
73         const vector<TrackPart> &parts = tt.get_parts();
74
75         const Profile &ballast_profile = cat.get_ballast_profile();
76         const Vector &ballast_min = ballast_profile.get_min_coords();
77         const Vector &ballast_max = ballast_profile.get_max_coords();
78         float ballast_h = ballast_max.y-ballast_min.y;
79
80         const Profile &rail_profile = cat.get_rail_profile();
81         const Vector &rail_min = rail_profile.get_min_coords();
82         const Vector &rail_max = rail_profile.get_max_coords();
83         float rail_h = rail_max.y-rail_min.y;
84
85         float gauge = cat.get_gauge();
86
87         string obj_name = tt.get_object();
88         if(!obj_name.empty())
89         {
90                 object = &catalogue.get<GL::Object>(obj_name);
91                 const GL::Mesh *m = object->get_mesh();
92                 const GL::VertexArray &vertices = m->get_vertices();
93                 int vertex_offs = vertices.get_format().offset(GL::VERTEX2);
94                 if(vertex_offs>=0)
95                 {
96                         for(unsigned i=0; i<vertices.size(); ++i)
97                         {
98                                 const float *v = vertices[i]+vertex_offs;
99                                 border.push_back(Vector(v[0], v[1], 0));
100                         }
101                 }
102         }
103         else
104         {
105                 mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
106                 mesh->set_winding(&GL::WindingTest::counterclockwise());
107                 GL::MeshBuilder bld(*mesh);
108
109                 unsigned index = 0;
110                 bld.texcoord(0.25, 0.5);
111                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
112                         build_part(*i, ballast_profile, Vector(0, -ballast_min.y, 0), false, bld, index);
113
114                 bld.texcoord(0.75, 0.5);
115                 float y = ballast_h-rail_min.y;
116                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
117                         build_part(*i, rail_profile, Vector(0, gauge/2, y), true, bld, index);
118                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
119                         build_part(*i, rail_profile, Vector(0, -gauge/2, y), false, bld, index);
120
121                 object = new GL::Object;
122                 object->set_mesh(mesh);
123                 object->set_technique(&catalogue.get<GL::Technique>(cat.get_track_technique()));
124         }
125  
126         unsigned paths = tt.get_paths();
127         for(unsigned i=0; paths; ++i, paths>>=1)
128         {
129                 GL::Mesh *m = 0;
130                 if(paths&1)
131                 {
132                         m = new GL::Mesh(GL::VERTEX3);
133                         GL::MeshBuilder bld(*m);
134                         unsigned index = 0;
135                         for(vector<TrackPart>::const_iterator j=parts.begin(); j!=parts.end(); ++j)
136                                 if(j->get_path()==i)
137                                         build_part(*j, cat.get_path_profile(), Vector(0, 0, ballast_h+1.5*rail_h), false, bld, index);
138                 }
139                 path_meshes.push_back(m);
140         }
141
142         min_z = max_z = border.front().z;
143         for(vector<Vector>::iterator i=border.begin(); i!=border.end(); ++i)
144         {
145                 min_z = min(min_z, i->z);
146                 max_z = max(max_z, i->z);
147         }
148         border.erase(graham_scan(border.begin(), border.end()), border.end());
149 }
150
151 TrackType3D::~TrackType3D()
152 {
153         for(vector<GL::Mesh *>::iterator i=path_meshes.begin(); i!=path_meshes.end(); ++i)
154                 delete *i;
155 }
156
157 void TrackType3D::get_bounds(const Angle &angle, Vector &minp, Vector &maxp) const
158 {
159         Transform trans = Transform::rotation(-angle, Vector(0, 0, 1));
160
161         minp = maxp = Vector();
162         minp.z = min_z;
163         maxp.z = max_z;
164
165         for(vector<Vector>::const_iterator i=border.begin(); i!=border.end(); ++i)
166         {
167                 Vector v = trans.transform(*i);
168
169                 minp.x = min(minp.x, v.x);
170                 minp.y = min(minp.y, v.y);
171                 maxp.x = max(maxp.x, v.x);
172                 maxp.y = max(maxp.y, v.y);
173         }
174 }
175
176 const GL::Mesh &TrackType3D::get_path_mesh(unsigned p) const
177 {
178         if(p>=path_meshes.size())
179                 throw out_of_range("TrackType3D::get_path_mesh");
180         if(!path_meshes[p])
181                 throw invalid_argument("TrackType3D::get_path_mesh");
182         return *path_meshes[p];
183 }
184
185 void TrackType3D::build_part(const TrackPart &part, const Profile &profile, const Vector &offset, bool mirror, GL::MeshBuilder &bld, unsigned &base_index)
186 {
187         float plen = part.get_length();
188         unsigned nsegs = (part.is_curved() ? static_cast<unsigned>(plen*32)+1 : 1);
189
190         unsigned n_vertices = profile.get_n_vertices();
191         for(unsigned i=0; i<=nsegs; ++i)
192         {
193                 TrackPoint basep = part.get_point(i*plen/nsegs);
194                 Transform trans = Transform::rotation(basep.dir, Vector(0, 0, 1));
195
196                 for(unsigned j=0; j<n_vertices; ++j)
197                 {
198                         const Profile::Vertex &v = profile.get_vertex(mirror ? n_vertices-1-j : j);
199                         Vector p(0, -v.pos.x, v.pos.y);
200                         if(mirror)
201                                 p.y = -p.y;
202                         p = basep.pos+trans.transform(offset+p);
203
204                         Vector n(0, -v.normal.x, v.normal.y);
205                         if(mirror)
206                                 n.y = -n.y;
207                         n = trans.transform(n);
208
209                         bld.normal(n.x, n.y, n.z);
210                         bld.vertex(p.x, p.y, p.z);
211
212                         border.push_back(p);
213                 }
214         }
215
216         for(unsigned i=0; i+1<n_vertices; )
217         {
218                 bld.begin(GL::TRIANGLE_STRIP);
219                 for(unsigned j=0; j<=nsegs; ++j)
220                 {
221                         unsigned k = j*n_vertices+i;
222                         bld.element(base_index+k+1);
223                         bld.element(base_index+k);
224                 }
225                 bld.end();
226
227                 ++i;
228                 if(!profile.get_vertex(i).smooth)
229                         ++i;
230         }
231
232         base_index += (nsegs+1)*n_vertices;
233 }
234
235 } // namespace R2C2