]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trackpart.cpp
Use skylight for nicer lighting
[r2c2.git] / source / libr2c2 / trackpart.cpp
1 #include <cmath>
2 #include <msp/geometry/box.h>
3 #include <msp/geometry/circle.h>
4 #include <msp/geometry/extrudedshape.h>
5 #include <msp/geometry/halfspace.h>
6 #include <msp/geometry/intersection.h>
7 #include <msp/geometry/negation.h>
8 #include <msp/geometry/transformedshape.h>
9 #include "trackpart.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace R2C2 {
15
16 TrackPart::TrackPart():
17         length(0),
18         radius(0),
19         path(0),
20         dead_end(false),
21         shape(0)
22 {
23         links[0] = 0;
24         links[1] = 0;
25 }
26
27 TrackPart::~TrackPart()
28 {
29         delete shape;
30 }
31
32 float TrackPart::get_length() const
33 {
34         if(radius)
35                 return abs(radius)*length;
36         else
37                 return length;
38 }
39
40 OrientedPoint TrackPart::get_point(float d) const
41 {
42         OrientedPoint result;
43
44         if(radius)
45         {
46                 Angle a = Angle::from_radians(d/radius);
47                 result.position = transform.transform(Vector(sin(a)*radius, (1-cos(a))*radius, 0));
48                 result.rotation = dir+a;
49         }
50         else
51         {
52                 result.position = transform.transform(Vector(d, 0, 0));
53                 result.rotation = dir;
54         }
55
56         return result;
57 }
58
59 OrientedPoint TrackPart::get_nearest_point(const Vector &p) const
60 {
61         OrientedPoint result;
62         if(radius)
63         {
64                 Vector v = p-transform.transform(Vector(0, radius, 0));
65                 Angle a = Geometry::atan2(v.y, v.x)+Angle::quarter_turn()-dir;
66                 if(radius<0)
67                         a = Angle::half_turn()-a;
68                 a.wrap_with_base(Angle::from_radians(length/2)-Angle::half_turn());
69                 a = min(max(a, Angle::zero()), Angle::from_radians(length));
70                 if(radius<0)    
71                         a = -a;
72                 result.position = transform.transform(Vector(sin(a)*radius, (1-cos(a))*radius, 0));
73                 result.rotation = dir+a;
74         }
75         else
76         {
77                 Vector v = p-pos;
78                 Vector dir_vec = transform.transform_linear(Vector(1, 0, 0));
79                 float d = min(max(dot(dir_vec, v), 0.0f), length);
80                 result.position = pos+dir_vec*d;
81                 result.rotation = dir;
82         }
83         return result;
84 }
85
86 void TrackPart::check_link(TrackPart &other)
87 {
88         unsigned n_eps = (dead_end ? 1 : 2);
89         unsigned n_other_eps = (other.is_dead_end() ? 1 : 2);
90         for(unsigned i=0; i<n_eps; ++i)
91         {
92                 OrientedPoint p1 = get_point(i ? get_length() : 0);
93                 for(unsigned j=0; j<n_other_eps; ++j)
94                 {
95                         OrientedPoint p2 = other.get_point(j ? other.get_length() : 0);
96
97                         Vector span = p2.position-p1.position;
98
99                         Angle da = wrap_balanced(p2.rotation-p1.rotation+Angle::half_turn()*float((i+j+1)%2));
100
101                         if(dot(span, span)<1e-6 && abs(da).radians()<=0.01)
102                         {
103                                 links[i] = &other;
104                                 other.links[j] = this;
105                                 return;
106                         }
107                 }
108         }
109 }
110
111 TrackPart *TrackPart::get_link(unsigned i) const
112 {
113         if(i>=2)
114                 throw out_of_range("TrackPart::get_link");
115         return links[i];
116 }
117
118 void TrackPart::create_shape()
119 {
120         if(radius)
121         {
122                 Geometry::ExtrudedShape<float, 3> ring(
123                         Geometry::Intersection<float, 2>(
124                                 Geometry::Circle<float>(abs(radius)+0.02),
125                                 Geometry::Negation<float, 2>(Geometry::Circle<float>(abs(radius)-0.02))),
126                         0.01);
127                 Geometry::HalfSpace<float, 3> start_wall(Vector(-1, 0, 0));
128                 Geometry::HalfSpace<float, 3> end_wall(rotated_vector(Vector(1, 0, 0),
129                         Angle::from_radians(length*(radius<0 ? -1 : 1))));
130                 /* This box only exists so the bounding boxes of curves make sense.  It
131                 can be removed when libmspmath produces better bounding boxes. */
132                 float w = sin(length)*(abs(radius)+0.02);
133                 float h = (1-cos(length))*abs(radius-0.02)+0.04;
134                 Geometry::TransformedShape<float, 3> bounds(
135                         Geometry::Box<float>(w, h, 0.01),
136                         Transform::translation(Vector(w/2, (h/2-0.02)*(radius<0 ? -1 : 1)-radius, 0.005)));
137                 vector<const Shape *> shapes;
138                 shapes.push_back(&ring);
139                 shapes.push_back(&start_wall);
140                 shapes.push_back(&end_wall);
141                 shapes.push_back(&bounds);
142                 shape = new Geometry::TransformedShape<float, 3>(
143                         Geometry::Intersection<float, 3>::from_iterator_range(shapes.begin(), shapes.end()),
144                         transform*Transform::translation(Vector(0, radius, 0.005)));
145         }
146         else
147         {
148                 // TODO Get the track profile dimensions from somewhere
149                 shape = new Geometry::TransformedShape<float, 3>(
150                         Geometry::Box<float>(length, 0.04, 0.01),
151                         transform*Transform::translation(Vector(length/2, 0, 0.005)));
152         }
153 }
154
155
156 TrackPart::Loader::Loader(TrackPart &p):
157         Msp::DataFile::ObjectLoader<TrackPart>(p)
158 {
159         add("start",    &Loader::start);
160         add("length",   &TrackPart::length);
161         add("radius",   &TrackPart::radius);
162         add("path",     &TrackPart::path);
163         add("dead_end", &TrackPart::dead_end);
164 }
165
166 void TrackPart::Loader::finish()
167 {
168         if(obj.radius)
169         {
170                 obj.length *= M_PI/180;
171                 obj.radius /= 1000;
172         }
173         else
174                 obj.length /= 1000;
175
176         obj.pos /= 1000;
177
178         obj.transform = Transform::translation(obj.pos)*
179                 Transform::rotation(obj.dir, Vector(0, 0, 1));
180 }
181
182 void TrackPart::Loader::start(float x, float y, float d)
183 {
184         obj.pos = Vector(x, y, 0);
185         obj.dir = Angle::from_degrees(d);
186 }
187
188 } // namespace R2C2