]> git.tdb.fi Git - r2c2.git/blob - source/3d/tracktype.cpp
d664c3948ef7a1c9bf54998a4abe661c45b2568e
[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 Catalogue &cat = catalogue.get_catalogue();
20         const vector<TrackPart> &parts = tt.get_parts();
21
22         const Profile &ballast_profile = appearance.get_ballast_profile();
23         const Vector &ballast_min = ballast_profile.get_min_coords();
24         const Vector &ballast_max = ballast_profile.get_max_coords();
25         float ballast_h = ballast_max.y-ballast_min.y;
26
27         const Profile &rail_profile = appearance.get_rail_profile();
28         const Vector &rail_min = rail_profile.get_min_coords();
29         const Vector &rail_max = rail_profile.get_max_coords();
30         float rail_h = rail_max.y-rail_min.y;
31
32         float gauge = cat.get_gauge();
33
34         string obj_name = tt.get_object();
35         if(!obj_name.empty())
36                 object = &catalogue.get<GL::Object>(obj_name);
37         else
38         {
39                 mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
40                 mesh->set_winding(&GL::WindingTest::counterclockwise());
41                 GL::MeshBuilder bld(*mesh);
42
43                 unsigned index = 0;
44                 bld.texcoord(0.25, 0.5);
45                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
46                         build_part(*i, ballast_profile, Vector(0, -ballast_min.y, 0), false, bld, index);
47
48                 bld.texcoord(0.75, 0.5);
49                 float y = ballast_h-rail_min.y;
50                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
51                         build_part(*i, rail_profile, Vector(0, gauge/2, y), true, bld, index);
52                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
53                         build_part(*i, rail_profile, Vector(0, -gauge/2, y), false, bld, index);
54
55                 object = new GL::Object;
56                 object->set_mesh(mesh);
57                 object->set_technique(&catalogue.get<GL::Technique>(appearance.get_technique()));
58
59                 own_data = true;
60         }
61
62         unsigned paths = tt.get_paths();
63         for(int i=-1; i<=1; ++i)
64         {
65                 // TODO Make profile width configurable
66                 Profile profile;
67                 if(i==0)
68                 {
69                         float rail_w = (rail_max.x-rail_min.x)*2;
70                         profile.append_vertex(Vector(rail_w*-0.5, 0, 0), false);
71                         profile.append_vertex(Vector(rail_w*0.5, 0, 0), false);
72                 }
73                 else
74                 {
75                         profile.append_vertex(Vector(i*(gauge*0.5+rail_min.x*2), 0, 0), false);
76                         profile.append_vertex(Vector(i*(gauge*0.5+rail_max.x*2), 0, 0), false);
77                 }
78
79                 // TODO Avoid generating redundant meshes for single-path tracks
80                 for(int j=-1; j<=4; ++j)
81                 {
82                         if(j>=0 && !((paths>>j)&1))
83                                 continue;
84
85                         GL::Mesh *m = new GL::Mesh(GL::VERTEX3);
86                         GL::MeshBuilder bld(*m);
87                         unsigned index = 0;
88                         for(vector<TrackPart>::const_iterator k=parts.begin(); k!=parts.end(); ++k)
89                                 if(j<0 || k->get_path()==static_cast<unsigned>(j))
90                                         build_part(*k, profile, Vector(0, 0, ballast_h+1.5*rail_h), false, bld, index);
91                         path_meshes[(j&0xFF)|((i&3)<<8)] = m;
92                 }
93         }
94 }
95
96 TrackType3D::~TrackType3D()
97 {
98         for(map<unsigned, GL::Mesh *>::iterator i=path_meshes.begin(); i!=path_meshes.end(); ++i)
99                 delete i->second;
100         if(own_data)
101         {
102                 delete object;
103                 delete mesh;
104         }
105 }
106
107 const GL::Mesh &TrackType3D::get_path_mesh(int p, int s) const
108 {
109         unsigned key = (p<0 ? 0xFF : p) | ((s&3)<<8);
110         return *get_item(path_meshes, key);
111 }
112
113 void TrackType3D::build_part(const TrackPart &part, const Profile &profile, const Vector &offset, bool mirror, GL::MeshBuilder &bld, unsigned &base_index)
114 {
115         float plen = part.get_length();
116         unsigned nsegs = (part.is_curved() ? static_cast<unsigned>(plen*32)+1 : 1);
117
118         unsigned n_vertices = profile.get_n_vertices();
119         for(unsigned i=0; i<=nsegs; ++i)
120         {
121                 OrientedPoint basep = part.get_point(i*plen/nsegs);
122                 Transform trans = Transform::rotation(basep.rotation, Vector(0, 0, 1));
123
124                 for(unsigned j=0; j<n_vertices; ++j)
125                 {
126                         const Profile::Vertex &v = profile.get_vertex(mirror ? n_vertices-1-j : j);
127                         Vector p(0, -v.pos.x, v.pos.y);
128                         if(mirror)
129                                 p.y = -p.y;
130                         p = basep.position+trans.transform(offset+p);
131
132                         Vector n(0, -v.normal.x, v.normal.y);
133                         if(mirror)
134                                 n.y = -n.y;
135                         n = trans.transform(n);
136
137                         bld.normal(n.x, n.y, n.z);
138                         bld.vertex(p.x, p.y, p.z);
139                 }
140         }
141
142         for(unsigned i=0; i+1<n_vertices; )
143         {
144                 bld.begin(GL::TRIANGLE_STRIP);
145                 for(unsigned j=0; j<=nsegs; ++j)
146                 {
147                         unsigned k = j*n_vertices+i;
148                         bld.element(base_index+k+1);
149                         bld.element(base_index+k);
150                 }
151                 bld.end();
152
153                 ++i;
154                 if(!profile.get_vertex(i).smooth)
155                         ++i;
156         }
157
158         base_index += (nsegs+1)*n_vertices;
159 }
160
161 } // namespace R2C2