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