]> git.tdb.fi Git - r2c2.git/blob - source/3d/track.cpp
5857e52d8813b8c29053e7dd107d882d7bf0f35c
[r2c2.git] / source / 3d / track.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <cmath>
9 #include <GL/gl.h>
10 #include <msp/gl/misc.h>
11 #include "libmarklin/tracktype.h"
12 #include "track.h"
13
14 using namespace std;
15 using namespace Msp;
16
17 namespace Marklin {
18
19 Track3D::Track3D(Track &t, unsigned q):
20         track(t),
21         color(1, 1, 1),
22         varray((GL::NORMAL3, GL::VERTEX3)),
23         quality(q)
24 {
25         build_object();
26 }
27
28 void Track3D::set_quality(unsigned q)
29 {
30         quality=q;
31         build_object();
32 }
33
34 void Track3D::get_bounds(float angle, Point &minp, Point &maxp) const
35 {
36         const Point &pos=track.get_position();
37         float rot=track.get_rotation();
38
39         float c=cos(-angle);
40         float s=sin(-angle);
41
42         minp.x=maxp.x=c*pos.x-s*pos.y;
43         minp.y=maxp.y=s*pos.x+c*pos.y;
44
45         float c2=cos(rot-angle);
46         float s2=sin(rot-angle);
47
48         for(vector<Point>::const_iterator i=border.begin(); i!=border.end(); ++i)
49         {
50                 float x=c*pos.x-s*pos.y + c2*i->x-s2*i->y;
51                 float y=s*pos.x+c*pos.y + s2*i->x+c2*i->y;
52
53                 minp.x=min(minp.x, x);
54                 minp.y=min(minp.y, y);
55                 maxp.x=max(maxp.x, x);
56                 maxp.y=max(maxp.y, y);
57         }
58 }
59
60 void Track3D::render() const
61 {
62         prepare_render();
63
64         glPushName(reinterpret_cast<unsigned>(this));
65
66         varray.apply();
67         glColor4f(0.25*color.r, 0.25*color.g, 0.25*color.b, 1);
68         glDrawElements(GL_QUADS, base_seq.size(), GL_UNSIGNED_INT, &base_seq[0]);
69         if(quality>1)
70         {
71                 glColor4f(0.85*color.r, 0.85*color.g, 0.85*color.b, 1);
72                 glDrawElements(GL_QUADS, rail_seq.size(), GL_UNSIGNED_INT, &rail_seq[0]);
73         }
74
75         glPopName();
76         glPopMatrix();
77 }
78
79 void Track3D::render_endpoints() const
80 {
81         prepare_render();
82
83         const vector<Endpoint> &endpoints=track.get_type().get_endpoints();
84         for(unsigned i=0; i<endpoints.size(); ++i)
85         {
86                 const Endpoint &ep=endpoints[i];
87                 GL::set(GL_CULL_FACE, track.get_link(i));
88                 if(track.get_link(i))
89                         glColor4f(0.5, 0, 1, 0.5);
90                 else
91                         glColor4f(1, 0, 0.5, 0.5);
92
93                 float c=cos(ep.dir);
94                 float s=sin(ep.dir);
95
96                 glBegin(GL_QUADS);
97                 glVertex3f(ep.pos.x-s*0.025, ep.pos.y+c*0.025, 0);
98                 glVertex3f(ep.pos.x+s*0.025, ep.pos.y-c*0.025, 0);
99                 glVertex3f(ep.pos.x+s*0.025, ep.pos.y-c*0.025, 0.02);
100                 glVertex3f(ep.pos.x-s*0.025, ep.pos.y+c*0.025, 0.02);
101                 glEnd();
102         }
103
104         glPopMatrix();
105 }
106
107 void Track3D::render_route(int route) const
108 {
109         prepare_render();
110
111         varray.apply();
112         if(route>=0 && static_cast<unsigned>(route)<route_seq.size())
113                 glDrawElements(GL_QUADS, route_seq[route].size(), GL_UNSIGNED_INT, &route_seq[route][0]);
114         else
115         {
116                 for(unsigned i=0; i<route_seq.size(); ++i)
117                         glDrawElements(GL_QUADS, route_seq[i].size(), GL_UNSIGNED_INT, &route_seq[i][0]);
118         }
119
120         glPopMatrix();
121 }
122
123 void Track3D::prepare_render() const
124 {
125         const Point &pos=track.get_position();
126         float rot=track.get_rotation();
127
128         glPushMatrix();
129         glTranslatef(pos.x, pos.y, pos.z);
130         glRotatef(rot*180/M_PI, 0, 0, 1);
131 }
132
133 void Track3D::build_object()
134 {
135         varray.clear();
136         GL::VertexArrayBuilder builder(varray);
137
138         base_seq.clear();
139         rail_seq.clear();
140         route_seq.clear();
141         route_seq.resize(track.get_type().get_n_routes());
142
143         const vector<TrackPart> &parts=track.get_type().get_parts();
144         unsigned index=0;
145         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
146                 build_part(*i, builder, index);
147 }
148
149 void Track3D::build_part(const TrackPart &part, GL::VertexArrayBuilder &va_builder, unsigned &base_index)
150 {
151         static vector<Point> profile;
152         if(profile.empty())
153         {
154                 profile.push_back(Point(0, -0.02, 0));
155                 profile.push_back(Point(0, -0.014, 0.008));
156                 profile.push_back(Point(0, -0.014, 0.008));
157                 profile.push_back(Point(0, 0.014, 0.008));
158                 profile.push_back(Point(0, 0.014, 0.008));
159                 profile.push_back(Point(0, 0.02, 0));
160                 for(unsigned i=0; i<2; ++i)
161                 {
162                         profile.push_back(Point(0, -0.009+i*0.017, 0.008));
163                         profile.push_back(Point(0, -0.009+i*0.017, 0.0103));
164                         profile.push_back(Point(0, -0.009+i*0.017, 0.0103));
165                         profile.push_back(Point(0, -0.008+i*0.017, 0.0103));
166                         profile.push_back(Point(0, -0.008+i*0.017, 0.0103));
167                         profile.push_back(Point(0, -0.008+i*0.017, 0.008));
168                 }
169                 profile.push_back(Point(0, -0.002, 0.012));
170                 profile.push_back(Point(0, 0.002, 0.012));
171         }
172         static unsigned psize=profile.size();
173
174         unsigned nsegs=(part.radius ? static_cast<unsigned>(part.length*(1<<quality))+1 : 1);
175         float plen=part.length;
176         if(part.radius)
177                 plen*=abs(part.radius);
178
179         for(unsigned i=0; i<=nsegs; ++i)
180         {
181                 float a=part.dir+(part.radius ? i*plen/nsegs/part.radius : 0);
182                 float c=cos(a);
183                 float s=sin(a);
184                 Point p=part.get_point(i*plen/nsegs);
185
186                 for(unsigned j=0; j<profile.size(); ++j)
187                 {
188                         unsigned k=j&~1;
189                         float dy=profile[k+1].y-profile[k].y;
190                         float dz=profile[k+1].z-profile[k].z;
191                         float d=sqrt(dy*dy+dz*dz);
192                         va_builder.normal(s*dz/d, -c*dz/d, dy/d);
193
194                         Point v(p.x+c*profile[j].x-s*profile[j].y, p.y+c*profile[j].y+s*profile[j].x, profile[j].z+i*track.get_slope()/nsegs);
195                         va_builder.vertex(v.x, v.y, v.z);
196                         if(profile[j].z==0)
197                                 border.push_back(v);
198                 }
199         }
200
201         for(unsigned i=0; i<nsegs; ++i)
202         {
203                 for(unsigned j=0; j<3; ++j)
204                 {
205                         base_seq.push_back(base_index+i*psize+j*2);
206                         base_seq.push_back(base_index+(i+1)*psize+j*2);
207                         base_seq.push_back(base_index+(i+1)*psize+1+j*2);
208                         base_seq.push_back(base_index+i*psize+1+j*2);
209                 }
210                 for(unsigned j=3; j<9; ++j)
211                 {
212                         rail_seq.push_back(base_index+i*psize+j*2);
213                         rail_seq.push_back(base_index+(i+1)*psize+j*2);
214                         rail_seq.push_back(base_index+(i+1)*psize+1+j*2);
215                         rail_seq.push_back(base_index+i*psize+1+j*2);
216                 }
217                 route_seq[part.route].push_back(base_index+i*psize+18);
218                 route_seq[part.route].push_back(base_index+(i+1)*psize+18);
219                 route_seq[part.route].push_back(base_index+(i+1)*psize+19);
220                 route_seq[part.route].push_back(base_index+i*psize+19);
221         }
222
223         base_index+=(nsegs+1)*psize;
224 }
225
226 } // namespace Marklin