]> git.tdb.fi Git - r2c2.git/blob - source/3d/vehicletype.cpp
2ddd34db08cc4515753f1eeb3171acee973e57d2
[r2c2.git] / source / 3d / vehicletype.cpp
1 /* $Id$
2
3 This file is part of R²C²
4 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <msp/gl/meshbuilder.h>
9 #include <msp/gl/technique.h>
10 #include <msp/gl/texture2d.h>
11 #include <msp/gl/vector.h>
12 #include "catalogue.h"
13 #include "vehicletype.h"
14
15 using namespace std;
16 using namespace Msp;
17
18 namespace {
19
20 template<typename T>
21 T get(const map<string, string> &params, const string &key, T def = T())
22 {
23         map<string, string>::const_iterator i = params.find(key);
24         if(i==params.end())
25                 return def;
26
27         return lexical_cast<T>(i->second);
28 }
29
30 }
31
32 namespace R2C2 {
33
34 VehicleType3D::VehicleType3D(const Catalogue3D &c, const VehicleType &t):
35         catalogue(c),
36         type(t),
37         body_object(0),
38         axle_objects(1)
39 {
40         body_object = get_object(type.get_object());
41
42         const vector<VehicleType::Axle> &axles = type.get_axles();
43         for(vector<VehicleType::Axle>::const_iterator i=axles.begin(); i!=axles.end(); ++i)
44                 axle_objects[0].push_back(get_object(i->object));
45
46         const vector<VehicleType::Bogie> &bogies = type.get_bogies();
47         for(vector<VehicleType::Bogie>::const_iterator i=bogies.begin(); i!=bogies.end(); ++i)
48         {
49                 bogie_objects.push_back(get_object(i->object));
50                 axle_objects.push_back(vector<GL::Object *>());
51                 for(vector<VehicleType::Axle>::const_iterator j=i->axles.begin(); j!=i->axles.end(); ++j)
52                         axle_objects.back().push_back(get_object(j->object));
53         }
54
55         const vector<VehicleType::Rod> &rods = type.get_rods();
56         for(vector<VehicleType::Rod>::const_iterator i=rods.begin(); i!=rods.end(); ++i)
57                 rod_objects.push_back(get_object(i->object));
58 }
59
60 VehicleType3D::~VehicleType3D()
61 {
62         for(map<string, GL::Object *>::iterator i=objects.begin(); i!=objects.end(); ++i)
63                 delete i->second;
64 }
65
66 const GL::Object *VehicleType3D::get_axle_object(unsigned i) const
67 {
68         if(i>=axle_objects[0].size())
69                 throw InvalidParameterValue("Axle index out of range");
70         return axle_objects[0][i];
71 }
72
73 const GL::Object *VehicleType3D::get_bogie_object(unsigned i) const
74 {
75         if(i>=bogie_objects.size())
76                 throw InvalidParameterValue("Bogie index out of range");
77         return bogie_objects[i];
78 }
79
80 const GL::Object *VehicleType3D::get_bogie_axle_object(unsigned i, unsigned j) const
81 {
82         if(i>=bogie_objects.size())
83                 throw InvalidParameterValue("Bogie index out of range");
84         if(j>=axle_objects[i+1].size())
85                 throw InvalidParameterValue("Axle index out of range");
86         return axle_objects[i+1][j];
87 }
88
89 const GL::Object *VehicleType3D::get_rod_object(unsigned i) const
90 {
91         if(i>=rod_objects.size())
92                 throw InvalidParameterValue("Rod index out of range");
93         return rod_objects[i];
94 }
95
96 GL::Object *VehicleType3D::get_object(const string &name)
97 {
98         if(name.empty())
99                 return 0;
100
101         GL::Object *&ptr = objects[name];
102         if(!ptr)
103         {
104                 if(name[0]==':')
105                 {
106                         string::size_type colon = name.find(':', 1);
107                         string kind = name.substr(1, colon-1);
108
109                         map<string, string> params;
110                         params["length"] = lexical_cast(type.get_length()*1000);
111                         params["width"] = lexical_cast(type.get_width()*1000);
112                         params["height"] = lexical_cast(type.get_height()*1000);
113                         if(colon!=string::npos)
114                         {
115                                 string::size_type start = colon+1;
116                                 while(1)
117                                 {
118                                         string::size_type equals = name.find('=', start);
119                                         string::size_type comma = name.find(',', start);
120                                         if(equals<comma)
121                                                 params[name.substr(start, equals-start)] = name.substr(equals+1, comma-equals-1);
122                                         else
123                                                 params[name.substr(start, comma-start)] = string();
124
125                                         if(comma==string::npos)
126                                                 break;
127                                         start = comma+1;
128                                 }
129                         }
130
131                         GL::Mesh *mesh = 0;
132                         if(kind=="openwagon")
133                                 mesh = create_open_wagon(params);
134                         else if(kind=="coveredwagon")
135                                 mesh = create_covered_wagon(params);
136                         else if(kind=="flatwagon")
137                                 mesh = create_flat_wagon(params);
138
139                         if(mesh)
140                         {
141                                 ptr = new GL::Object;
142                                 ptr->set_mesh(mesh);
143                                 ptr->set_technique(create_technique(params));
144                         }
145                 }
146                 else
147                 {
148                         ptr = new GL::Object;
149                         DataFile::load(*ptr, name);
150                 }
151         }
152         return ptr;
153 }
154
155 GL::Technique *VehicleType3D::create_technique(const map<string, string> &params)
156 {
157         string color_str = get<string>(params, "color", "808080");
158         unsigned color = lexical_cast<unsigned>(color_str, "x");
159         string color2_str = get<string>(params, "color2", color_str);
160         unsigned color2 = lexical_cast<unsigned>(color2_str, "x");
161
162         GL::Technique *tech = new GL::Technique;
163         GL::RenderPass &pass = tech->add_pass(GL::Tag());
164         GL::Material *mat = new GL::Material;
165         mat->set_diffuse(GL::Color(1));
166         pass.set_material(mat);
167         GL::Texture2D *tex = new GL::Texture2D;
168         tex->storage(GL::RGB, 2, 1);
169         tex->set_min_filter(GL::NEAREST);
170         tex->set_mag_filter(GL::NEAREST);
171         unsigned char data[6] = { color>>16, color>>8, color, color2>>16, color2>>8, color2 };
172         tex->image(0, GL::RGB, GL::UNSIGNED_BYTE, data);
173         pass.set_texture(0, tex);
174
175         return tech;
176 }
177
178 GL::Mesh *VehicleType3D::create_open_wagon(const map<string, string> &params)
179 {
180         float length = get<float>(params, "length")/1000;
181         float width = get<float>(params, "width")/1000;
182         float height = get<float>(params, "height")/1000;
183         float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
184         float border = get<float>(params, "border", 40*catalogue.get_catalogue().get_scale())/1000;
185
186         GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
187         GL::MeshBuilder bld(*mesh);
188
189         for(unsigned i=0; i<16; ++i)
190         {
191                 float x = length/2;
192                 float y = width/2;
193                 if(i>=8)
194                 {
195                         x -= border;
196                         y -= border;
197                 }
198                 if((i+1)%4<2)
199                         x = -x;
200                 if(i%4<2)
201                         y = -y;
202
203                 float z = height;
204                 if(i<4)
205                         z = clearance;
206                 else if(i>=12)
207                         z = clearance+0.1*catalogue.get_catalogue().get_scale();
208
209                 bld.texcoord((i>=12 ? 0.75 : 0.25), 0.5);
210                 bld.normal(0, 0, (i<4 ? -1 : 1));
211                 bld.vertex(x, y, z);
212                 bld.texcoord((i>=8 ? 0.75 : 0.25), 0.5);
213                 bld.normal(((x<0)==(i<8) ? -1 : 1), 0, 0);
214                 bld.vertex(x, y, z);
215                 bld.normal(0, ((y<0)==(i<8) ? -1 : 1), 0);
216                 bld.vertex(x, y, z);
217         }
218
219         bld.begin(GL::QUADS);
220         for(unsigned i=0; i<3; ++i)
221                 for(unsigned j=0; j<4; ++j)
222                 {
223                         unsigned k = (i==1 ? 0 : 2-j%2);
224                         bld.element((i*4+j)*3+k);
225                         bld.element((i*4+(j+1)%4)*3+k);
226                         bld.element(((i+1)*4+(j+1)%4)*3+k);
227                         bld.element(((i+1)*4+j)*3+k);
228                 }
229         for(unsigned i=4; i--;)
230                 bld.element(i*3);
231         for(unsigned i=0; i<4; ++i)
232                 bld.element((12+i)*3);
233         bld.end();
234
235         return mesh;
236 }
237
238 GL::Mesh *VehicleType3D::create_covered_wagon(const map<string, string> &params)
239 {
240         float length = get<float>(params, "length")/1000;
241         float width = get<float>(params, "width")/1000;
242         float height = get<float>(params, "height")/1000;
243         float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
244         float roof = get<float>(params, "roof", width*250)/1000;
245
246         GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
247         GL::MeshBuilder bld(*mesh);
248
249         for(unsigned i=0; i<12; ++i)
250         {
251                 float x = length/2;
252                 float y = width/2;
253                 if(i>=8)
254                         y /= 3;
255                 if((i+1)%4<2)
256                         x = -x;
257                 if(i%4<2)
258                         y = -y;
259
260                 float z = height;
261                 if(i<4)
262                         z = clearance;
263                 else if(i<8)
264                         z -= roof;
265
266                 if(i<4)
267                 {
268                         bld.normal(0, 0, -1);
269                         bld.texcoord(0.25, 0.5);
270                 }
271                 else
272                 {
273                         float a = atan2(roof/(i>=8 ? 3 : 1), width/2);
274                         if(y<0)
275                                 a = -a;
276                         bld.normal(0, sin(a), cos(a));
277                         bld.texcoord(0.75, 0.5);
278                 }
279                 bld.vertex(x, y, z);
280
281                 bld.texcoord(0.25, 0.5);
282                 bld.normal((x<0 ? -1 : 1), 0, 0);
283                 bld.vertex(x, y, z);
284                 bld.normal(0, (y<0 ? -1 : 1), 0);
285                 bld.vertex(x, y, z);
286         }
287
288         bld.begin(GL::QUADS);
289         for(unsigned i=0; i<2; ++i)
290                 for(unsigned j=0; j<4; ++j)
291                 {
292                         unsigned k = ((i==1 && j%2==0) ? 0 : 2-j%2);
293                         bld.element((i*4+j)*3+k);
294                         bld.element((i*4+(j+1)%4)*3+k);
295                         bld.element(((i+1)*4+(j+1)%4)*3+k);
296                         bld.element(((i+1)*4+j)*3+k);
297                 }
298         for(unsigned i=4; i--;)
299                 bld.element(i*3);
300         for(unsigned i=0; i<4; ++i)
301                 bld.element((8+i)*3);
302         bld.end();
303
304         return mesh;
305 }
306
307 GL::Mesh *VehicleType3D::create_flat_wagon(const map<string, string> &params)
308 {
309         float length = get<float>(params, "length")/1000;
310         float width = get<float>(params, "width")/1000;
311         float height = get<float>(params, "height")/1000;
312         float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
313
314         GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
315         GL::MeshBuilder bld(*mesh);
316
317         for(unsigned i=0; i<8; ++i)
318         {
319                 float x = length/2;
320                 float y = width/2;
321                 if((i+1)%4<2)
322                         x = -x;
323                 if(i%4<2)
324                         y = -y;
325
326                 float z = (i<4 ? clearance : height);
327
328                 bld.texcoord((i>=4 ? 0.75 : 0.25), 0.5);
329                 bld.normal(0, 0, (i<4 ? -1 : 1));
330                 bld.vertex(x, y, z);
331                 bld.texcoord(0.25, 0.5);
332                 bld.normal(((x<0)==(i<8) ? -1 : 1), 0, 0);
333                 bld.vertex(x, y, z);
334                 bld.normal(0, ((y<0)==(i<8) ? -1 : 1), 0);
335                 bld.vertex(x, y, z);
336         }
337
338         bld.begin(GL::QUADS);
339         for(unsigned i=0; i<4; ++i)
340         {
341                 unsigned j = 2-i%2;
342                 bld.element(i*3+j);
343                 bld.element(((i+1)%4)*3+j);
344                 bld.element((4+(i+1)%4)*3+j);
345                 bld.element((4+i)*3+j);
346         }
347         for(unsigned i=4; i--;)
348                 bld.element(i*3);
349         for(unsigned i=0; i<4; ++i)
350                 bld.element((4+i)*3);
351         bld.end();
352
353         return mesh;
354 }
355
356 } // namespace R2C2