]> git.tdb.fi Git - r2c2.git/blobdiff - source/3d/vehicletype.cpp
Don't crash if a train has no router
[r2c2.git] / source / 3d / vehicletype.cpp
index a808185afd942776868de45f14d9646c5e9d545d..34f2be630a9bc587680aaf2b4691888b6941eaf0 100644 (file)
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010  Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
+#include <msp/gl/material.h>
 #include <msp/gl/meshbuilder.h>
+#include <msp/gl/technique.h>
+#include <msp/gl/texture2d.h>
 #include <msp/gl/vector.h>
+#include "catalogue.h"
 #include "vehicletype.h"
 
+using namespace std;
 using namespace Msp;
 
-namespace Marklin {
+namespace {
+
+template<typename T>
+T get(const map<string, string> &params, const string &key, T def = T())
+{
+       map<string, string>::const_iterator i = params.find(key);
+       if(i==params.end())
+               return def;
+
+       return lexical_cast<T>(i->second);
+}
+
+}
+
+namespace R2C2 {
+
+VehicleType3D::VehicleType3D(Catalogue3D &c, const VehicleType &t):
+       ObjectType3D(c),
+       type(t),
+       body_object(0)
+{
+       body_object = get_object(type.get_object());
+
+       const vector<VehicleType::Axle> &axles = type.get_axles();
+       for(vector<VehicleType::Axle>::const_iterator i=axles.begin(); i!=axles.end(); ++i)
+               axle_objects.push_back(get_object(i->object));
+
+       const vector<VehicleType::Bogie> &bogies = type.get_bogies();
+       for(vector<VehicleType::Bogie>::const_iterator i=bogies.begin(); i!=bogies.end(); ++i)
+               bogie_objects.push_back(get_object(i->object));
+
+       const vector<VehicleType::Rod> &rods = type.get_rods();
+       for(vector<VehicleType::Rod>::const_iterator i=rods.begin(); i!=rods.end(); ++i)
+               rod_objects.push_back(get_object(i->object));
+}
+
+VehicleType3D::~VehicleType3D()
+{
+       for(map<string, GL::Object *>::iterator i=objects.begin(); i!=objects.end(); ++i)
+               delete i->second;
+}
+
+const GL::Object *VehicleType3D::get_axle_object(unsigned i) const
+{
+       if(i>=axle_objects.size())
+               throw out_of_range("VehicleType3D::get_fixed_axle_object");
+       return axle_objects[i];
+}
+
+const GL::Object *VehicleType3D::get_bogie_object(unsigned i) const
+{
+       if(i>=bogie_objects.size())
+               throw out_of_range("VehicleType3D::get_bogie_object");
+       return bogie_objects[i];
+}
+
+const GL::Object *VehicleType3D::get_rod_object(unsigned i) const
+{
+       if(i>=rod_objects.size())
+               throw out_of_range("VehicleType3D::get_rod_object");
+       return rod_objects[i];
+}
+
+GL::Object *VehicleType3D::get_object(const string &name)
+{
+       if(name.empty())
+               return 0;
+
+       GL::Object *&ptr = objects[name];
+       if(!ptr)
+       {
+               if(name[0]==':')
+               {
+                       string::size_type colon = name.find(':', 1);
+                       string kind = name.substr(1, colon-1);
+
+                       map<string, string> params;
+                       params["length"] = lexical_cast<string>(type.get_length()*1000);
+                       params["width"] = lexical_cast<string>(type.get_width()*1000);
+                       params["height"] = lexical_cast<string>(type.get_height()*1000);
+                       if(colon!=string::npos)
+                       {
+                               string::size_type start = colon+1;
+                               while(1)
+                               {
+                                       string::size_type equals = name.find('=', start);
+                                       string::size_type comma = name.find(',', start);
+                                       if(equals<comma)
+                                               params[name.substr(start, equals-start)] = name.substr(equals+1, comma-equals-1);
+                                       else
+                                               params[name.substr(start, comma-start)] = string();
+
+                                       if(comma==string::npos)
+                                               break;
+                                       start = comma+1;
+                               }
+                       }
+
+                       GL::Mesh *mesh = 0;
+                       if(kind=="openwagon")
+                               mesh = create_open_wagon(params);
+                       else if(kind=="coveredwagon")
+                               mesh = create_covered_wagon(params);
+                       else if(kind=="flatwagon")
+                               mesh = create_flat_wagon(params);
+
+                       if(mesh)
+                       {
+                               ptr = new GL::Object;
+                               ptr->set_mesh(mesh);
+                               ptr->set_technique(create_technique(params));
+                       }
+               }
+               else
+                       return &catalogue.get<GL::Object>(name);
+       }
+       return ptr;
+}
+
+GL::Technique *VehicleType3D::create_technique(const map<string, string> &params)
+{
+       string color_str = get<string>(params, "color", "808080");
+       unsigned color = lexical_cast<unsigned>(color_str, "x");
+       string color2_str = get<string>(params, "color2", color_str);
+       unsigned color2 = lexical_cast<unsigned>(color2_str, "x");
+
+       GL::Technique *tech = new GL::Technique;
+       GL::RenderPass &pass = tech->add_pass(GL::Tag());
+       GL::Material *mat = new GL::Material;
+       mat->set_diffuse(GL::Color(1));
+       pass.set_material(mat);
+       GL::Texture2D *tex = new GL::Texture2D;
+       tex->storage(GL::RGB, 2, 1);
+       tex->set_min_filter(GL::NEAREST);
+       tex->set_mag_filter(GL::NEAREST);
+       unsigned char data[6];
+       data[0] = color>>16;  data[1] = color>>8;  data[2] = color;
+       data[3] = color2>>16; data[4] = color2>>8; data[5] = color2;
+       tex->image(0, GL::RGB, GL::UNSIGNED_BYTE, data);
+       pass.set_texture(0, tex);
+
+       return tech;
+}
+
+GL::Mesh *VehicleType3D::create_open_wagon(const map<string, string> &params)
+{
+       float length = get<float>(params, "length")/1000;
+       float width = get<float>(params, "width")/1000;
+       float height = get<float>(params, "height")/1000;
+       float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
+       float border = get<float>(params, "border", 40*catalogue.get_catalogue().get_scale())/1000;
+
+       GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
+       GL::MeshBuilder bld(*mesh);
+
+       for(unsigned i=0; i<16; ++i)
+       {
+               float x = length/2;
+               float y = width/2;
+               if(i>=8)
+               {
+                       x -= border;
+                       y -= border;
+               }
+               if((i+1)%4<2)
+                       x = -x;
+               if(i%4<2)
+                       y = -y;
+
+               float z = height;
+               if(i<4)
+                       z = clearance;
+               else if(i>=12)
+                       z = clearance+0.1*catalogue.get_catalogue().get_scale();
+
+               bld.texcoord((i>=12 ? 0.75 : 0.25), 0.5);
+               bld.normal(0, 0, (i<4 ? -1 : 1));
+               bld.vertex(x, y, z);
+               bld.texcoord((i>=8 ? 0.75 : 0.25), 0.5);
+               bld.normal(((x<0)==(i<8) ? -1 : 1), 0, 0);
+               bld.vertex(x, y, z);
+               bld.normal(0, ((y<0)==(i<8) ? -1 : 1), 0);
+               bld.vertex(x, y, z);
+       }
+
+       bld.begin(GL::QUADS);
+       for(unsigned i=0; i<3; ++i)
+               for(unsigned j=0; j<4; ++j)
+               {
+                       unsigned k = (i==1 ? 0 : 2-j%2);
+                       bld.element((i*4+j)*3+k);
+                       bld.element((i*4+(j+1)%4)*3+k);
+                       bld.element(((i+1)*4+(j+1)%4)*3+k);
+                       bld.element(((i+1)*4+j)*3+k);
+               }
+       for(unsigned i=4; i--;)
+               bld.element(i*3);
+       for(unsigned i=0; i<4; ++i)
+               bld.element((12+i)*3);
+       bld.end();
+
+       return mesh;
+}
 
-VehicleType3D::VehicleType3D(Catalogue3D &, const VehicleType &vt):
-       body_mesh((GL::NORMAL3, GL::VERTEX3))
+GL::Mesh *VehicleType3D::create_covered_wagon(const map<string, string> &params)
 {
-       GL::Vector3 corner(vt.get_length()/2, vt.get_width()/2, vt.get_height());
+       float length = get<float>(params, "length")/1000;
+       float width = get<float>(params, "width")/1000;
+       float height = get<float>(params, "height")/1000;
+       float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
+       float roof = get<float>(params, "roof", width*250)/1000;
+
+       GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
+       GL::MeshBuilder bld(*mesh);
+
+       for(unsigned i=0; i<12; ++i)
+       {
+               float x = length/2;
+               float y = width/2;
+               if(i>=8)
+                       y /= 3;
+               if((i+1)%4<2)
+                       x = -x;
+               if(i%4<2)
+                       y = -y;
+
+               float z = height;
+               if(i<4)
+                       z = clearance;
+               else if(i<8)
+                       z -= roof;
+
+               if(i<4)
+               {
+                       bld.normal(0, 0, -1);
+                       bld.texcoord(0.25, 0.5);
+               }
+               else
+               {
+                       float a = atan2(roof/(i>=8 ? 3 : 1), width/2);
+                       if(y<0)
+                               a = -a;
+                       bld.normal(0, sin(a), cos(a));
+                       bld.texcoord(0.75, 0.5);
+               }
+               bld.vertex(x, y, z);
+
+               bld.texcoord(0.25, 0.5);
+               bld.normal((x<0 ? -1 : 1), 0, 0);
+               bld.vertex(x, y, z);
+               bld.normal(0, (y<0 ? -1 : 1), 0);
+               bld.vertex(x, y, z);
+       }
 
-       GL::MeshBuilder bld(body_mesh);
        bld.begin(GL::QUADS);
-       bld.normal(1, 0, 0);
-       bld.vertex(corner.x, -corner.y, 0);
-       bld.vertex(corner.x, corner.y, 0);
-       bld.vertex(corner.x, corner.y, corner.z);
-       bld.vertex(corner.x, -corner.y, corner.z);
-       bld.normal(-1, 0, 0);
-       bld.vertex(-corner.x, corner.y, 0);
-       bld.vertex(-corner.x, -corner.y, 0);
-       bld.vertex(-corner.x, -corner.y, corner.z);
-       bld.vertex(-corner.x, corner.y, corner.z);
-       bld.normal(0, 1, 0);
-       bld.vertex(corner.x, corner.y, 0);
-       bld.vertex(-corner.x, corner.y, 0);
-       bld.vertex(-corner.x, corner.y, corner.z);
-       bld.vertex(corner.x, corner.y, corner.z);
-       bld.normal(0, -1, 0);
-       bld.vertex(-corner.x, -corner.y, 0);
-       bld.vertex(corner.x, -corner.y, 0);
-       bld.vertex(corner.x, -corner.y, corner.z);
-       bld.vertex(-corner.x, -corner.y, corner.z);
-       bld.normal(0, 0, 1);
-       bld.vertex(-corner.x, -corner.y, corner.z);
-       bld.vertex(corner.x, -corner.y, corner.z);
-       bld.vertex(corner.x, corner.y, corner.z);
-       bld.vertex(-corner.x, corner.y, corner.z);
+       for(unsigned i=0; i<2; ++i)
+               for(unsigned j=0; j<4; ++j)
+               {
+                       unsigned k = ((i==1 && j%2==0) ? 0 : 2-j%2);
+                       bld.element((i*4+j)*3+k);
+                       bld.element((i*4+(j+1)%4)*3+k);
+                       bld.element(((i+1)*4+(j+1)%4)*3+k);
+                       bld.element(((i+1)*4+j)*3+k);
+               }
+       for(unsigned i=4; i--;)
+               bld.element(i*3);
+       for(unsigned i=0; i<4; ++i)
+               bld.element((8+i)*3);
        bld.end();
+
+       return mesh;
+}
+
+GL::Mesh *VehicleType3D::create_flat_wagon(const map<string, string> &params)
+{
+       float length = get<float>(params, "length")/1000;
+       float width = get<float>(params, "width")/1000;
+       float height = get<float>(params, "height")/1000;
+       float clearance = get<float>(params, "clearance", 900*catalogue.get_catalogue().get_scale())/1000;
+
+       GL::Mesh *mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
+       GL::MeshBuilder bld(*mesh);
+
+       for(unsigned i=0; i<8; ++i)
+       {
+               float x = length/2;
+               float y = width/2;
+               if((i+1)%4<2)
+                       x = -x;
+               if(i%4<2)
+                       y = -y;
+
+               float z = (i<4 ? clearance : height);
+
+               bld.texcoord((i>=4 ? 0.75 : 0.25), 0.5);
+               bld.normal(0, 0, (i<4 ? -1 : 1));
+               bld.vertex(x, y, z);
+               bld.texcoord(0.25, 0.5);
+               bld.normal(((x<0)==(i<8) ? -1 : 1), 0, 0);
+               bld.vertex(x, y, z);
+               bld.normal(0, ((y<0)==(i<8) ? -1 : 1), 0);
+               bld.vertex(x, y, z);
+       }
+
+       bld.begin(GL::QUADS);
+       for(unsigned i=0; i<4; ++i)
+       {
+               unsigned j = 2-i%2;
+               bld.element(i*3+j);
+               bld.element(((i+1)%4)*3+j);
+               bld.element((4+(i+1)%4)*3+j);
+               bld.element((4+i)*3+j);
+       }
+       for(unsigned i=4; i--;)
+               bld.element(i*3);
+       for(unsigned i=0; i<4; ++i)
+               bld.element((4+i)*3);
+       bld.end();
+
+       return mesh;
 }
 
-} // namespace Marklin
+} // namespace R2C2