1 // Suspension.cc - the suspension component for a wheel.
2 //
3 // Copyright (C) 2001--2004 Sam Varner
4 //
5 // This file is part of Vamos Automotive Simulator.
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21 #include "../media/Ac3d.h"
22 #include "../geometry/Conversions.h"
23 #include "../geometry/Numeric.h"
24 #include "Suspension.h"
25
26 #include <cmath>
27 #include <cassert>
28
29 using namespace Vamos_Body;
30 using namespace Vamos_Geometry;
31
32 //* Static Member
33
34 // The axis of rotation for steering and toe adjustments.
35 const Three_Vector Vamos_Body::Suspension::
36 STEER_AXIS = Three_Vector (0.0, 0.0, 1.0);
37
38 // Note that all angles are stored as right-hand rotations. As a
39 // result, m_caster for a wheel on the right side of the car follows
40 // the common convention that positive camber means that the wheel
41 // leans away from the centerline. For the wheel on the left,
42 // m_caster is contrary to convention.
Hinge(const Three_Vector & position,const Frame * parent)43 Hinge::Hinge (const Three_Vector& position,
44 const Frame* parent)
45 : Particle (0.0, position, parent)
46 {
47 }
48
49 void
input(const Three_Vector & torque,const Three_Vector & radius)50 Hinge::input (const Three_Vector& torque, const Three_Vector& radius)
51 {
52 set_force (torque.magnitude ()
53 / radius.magnitude () * (torque.cross (radius).unit ()));
54 }
55
56 //* Struct Suspension_Model
57 struct Vamos_Body::Suspension_Model
58 {
59 GLuint display_list;
60 double x;
61 double y;
62 double z;
63
Suspension_ModelVamos_Body::Suspension_Model64 Suspension_Model (GLuint list_id, const Three_Vector& position)
65 : display_list (list_id),
66 x (position.x),
67 y (position.y),
68 z (position.z)
69 {
70 };
71 };
72
73 //* Class Suspension
74
75 //** Constructor
Suspension(const Three_Vector & pos,const Three_Vector & center_of_translation,Direction side_of_car,double spring_constant,double bounce,double rebound,double travel,double max_compression_velocity,const Frame * parent)76 Suspension::Suspension (const Three_Vector& pos,
77 const Three_Vector& center_of_translation,
78 Direction side_of_car,
79 double spring_constant,
80 double bounce,
81 double rebound,
82 double travel,
83 double max_compression_velocity,
84 const Frame* parent)
85 : Particle (0.0, pos, parent),
86 mp_hinge (new Hinge (center_of_translation)),
87 m_radius (center_of_translation - pos),
88 m_initial_radius (m_radius),
89 m_radius_magnitude (m_radius.magnitude ()),
90 m_initial_z (pos.z),
91 m_spring_constant (spring_constant),
92 m_bounce (bounce),
93 m_rebound (rebound),
94 m_travel (travel),
95 m_displacement (0.0),
96 m_time_step (0.0),
97 m_compression_velocity (0.0),
98 m_max_compression_velocity (max_compression_velocity),
99 m_bottomed_out (false),
100 m_anti_roll_k (0.0),
101 m_anti_roll_suspension (0),
102 m_steer_angle (0.0),
103 m_camber (0.0),
104 m_caster (0.0),
105 m_toe (0.0),
106 m_side (side_of_car),
107 m_normal (Three_Vector (0.0, 0.0, 1.0)),
108 m_hinge_axis (m_radius.cross (Three_Vector::Z).unit ())
109 {
110 }
111
~Suspension()112 Suspension::~Suspension ()
113 {
114 for (std::vector <Suspension_Model*>::iterator it = m_models.begin ();
115 it != m_models.end ();
116 it++)
117 {
118 delete *it;
119 }
120 }
121
122 // Specify the suspension component that is attached to this one with
123 // an anti-roll bar. The anti-roll bar will have a spring constant of
124 // SPRING_CONSTANT.
125 void
anti_roll(Suspension * other,double spring_constant)126 Suspension::anti_roll (Suspension* other, double spring_constant)
127 {
128 m_anti_roll_suspension = other;
129 m_anti_roll_k = spring_constant;
130
131 m_anti_roll_suspension->m_anti_roll_suspension = this;
132 m_anti_roll_suspension->m_anti_roll_k = m_anti_roll_k;
133 }
134
135 // Displace this suspension component by DISTANCE. A positive
136 // DISTANCE means compression.
137 void
displace(double distance)138 Suspension::displace (double distance)
139 {
140 const double last_displacement = m_displacement;
141 m_displacement = distance;
142 if (m_displacement > m_travel)
143 {
144 m_bottomed_out = true;
145 m_displacement = m_travel;
146 }
147 else
148 {
149 m_bottomed_out = false;
150 }
151
152 set_position (get_position ());
153
154 // The radius points from position () to the hinge.
155 m_radius = mp_hinge->position () - position ();
156
157 m_compression_velocity = (m_displacement - last_displacement) / m_time_step;
158 }
159
160 // Return the suspension position for the current displacement.
161 Three_Vector
get_position() const162 Suspension::get_position () const
163 {
164 const Three_Vector& hinge_pos = mp_hinge->position ();
165 const double z = hinge_pos.z - m_initial_z - m_displacement;
166 assert (z <= m_radius_magnitude);
167 const double angle = asin (z / m_radius_magnitude);
168 return hinge_pos - m_initial_radius.rotate (angle * m_hinge_axis);
169 }
170
171 void
input(const Three_Vector & wheel_force,const Three_Vector & normal)172 Suspension::input (const Three_Vector& wheel_force,
173 const Three_Vector& normal)
174 {
175 m_wheel_force = wheel_force;
176 m_normal = rotate_to_parent (normal);
177 }
178
179 void
torque(double wheel_torque)180 Suspension::torque (double wheel_torque)
181 {
182 mp_hinge->input (Three_Vector (0.0, -wheel_torque, 0.0), m_radius);
183 }
184
185 // Calculate the force exerted by the suspension in its current state.
186 void
find_forces()187 Suspension::find_forces ()
188 {
189 double anti_roll_force = 0.0;
190 if (m_anti_roll_suspension)
191 {
192 anti_roll_force = m_anti_roll_k *
193 (m_displacement - m_anti_roll_suspension->m_displacement);
194 }
195
196 // Use `m_bounce' for compression, `m_rebound' for decompression.
197 double damp = (m_compression_velocity > 0.0) ? m_bounce : m_rebound;
198
199 if (m_displacement <= 0.0)
200 {
201 // Don't exert a force if this suspension is not compressed.
202 reset ();
203 }
204 else
205 {
206 // If the suspension is moving at a speed > m_max_compression_velocity,
207 // the damper 'locks up' due to turbulence in the fluid. The effect
208 // is the same as bottoming out.
209 if (std::abs (m_compression_velocity) > m_max_compression_velocity)
210 {
211 m_bottomed_out = true;
212 }
213
214 double spring_force = m_spring_constant * m_displacement;
215 double damp_force = damp * m_compression_velocity;
216 set_force (rotate_from_parent (m_normal
217 * (spring_force + damp_force + anti_roll_force)));
218 }
219 }
220
221 // Advance this suspension component forward in time by TIME.
222 void
propagate(double time)223 Suspension::propagate (double time)
224 {
225 m_time_step = time;
226
227 // Start with the static orientation.
228 set_orientation (m_static_orientation);
229 rotate (m_steer_angle * STEER_AXIS);
230 }
231
232 // Undo the last propagation.
233 void
rewind()234 Suspension::rewind ()
235 {
236 }
237
238 // Set the steering angle.
239 void
steer(double degree_angle)240 Suspension::steer (double degree_angle)
241 {
242 m_steer_angle = deg_to_rad (degree_angle);
243 }
244
245 // Set the camber angle.
246 void
camber(double degree_angle)247 Suspension::camber (double degree_angle)
248 {
249 if (m_side == LEFT)
250 degree_angle *= -1.0;
251
252 // Undo the current camber setting before applying the new one.
253 m_static_orientation.rotate (Three_Vector (-m_camber, 0.0, 0.0));
254 m_camber = deg_to_rad (degree_angle);
255 m_static_orientation.rotate (Three_Vector (m_camber, 0.0, 0.0));
256 }
257
258 // Set the caster angle.
259 void
caster(double degree_angle)260 Suspension::caster (double degree_angle)
261 {
262 // The caster rotation is in the same direction for both sides.
263
264 // Undo the current caster setting before applying the new one.
265 m_static_orientation.rotate (Three_Vector (0.0, -m_caster, 0.0));
266 m_caster = -deg_to_rad (degree_angle);
267 m_static_orientation.rotate (Three_Vector (0.0, m_caster, 0.0));
268 }
269
270 // Set the toe angle.
271 void
toe(double degree_angle)272 Suspension::toe (double degree_angle)
273 {
274 if (m_side == LEFT)
275 degree_angle *= -1.0;
276
277 // Undo the current toe setting before applying the new one.
278 m_static_orientation.rotate (-m_toe * STEER_AXIS);
279 m_toe = deg_to_rad (degree_angle);
280 m_static_orientation.rotate (m_toe * STEER_AXIS);
281 }
282
283 // Return the camber angle in radians for a suspension displacement of
284 // DISPLACEMENT.
285 double
camber_function(double displacement) const286 Suspension::camber_function (double displacement) const
287 {
288 return 0.0;
289 }
290
291 double
current_camber(double normal_y) const292 Suspension::current_camber (double normal_y) const
293 {
294 return Vamos_Geometry::clip (normal_y, -0.5, 0.5);;
295 }
296
297 // Return this suspension component to equilibrium.
298 void
reset()299 Suspension::reset ()
300 {
301 Particle::reset ();
302 m_displacement = 0.0;
303 }
304
305
306 void
set_model(std::string file_name,double scale,const Three_Vector & translation,const Three_Vector & rotation)307 Suspension::set_model (std::string file_name,
308 double scale,
309 const Three_Vector& translation,
310 const Three_Vector& rotation)
311 {
312 Three_Vector position = translation;
313 Three_Vector orientation = rotation;
314 if (m_side == LEFT)
315 {
316 // Make the right and left sides symmetric.
317 position.y *= -1.0;
318 orientation.x *= -1.0;
319 orientation.y *= -1.0;
320 }
321
322 Vamos_Media::Ac3d* model =
323 new Vamos_Media::Ac3d (file_name, scale, Three_Vector (), orientation);
324 m_models.push_back (new Suspension_Model (model->build (), position));
325 delete model;
326 }
327
328 void
draw()329 Suspension::draw ()
330 {
331 for (std::vector <Suspension_Model*>::iterator it = m_models.begin ();
332 it != m_models.end ();
333 it++)
334 {
335 glPushMatrix ();
336
337 glTranslatef (position ().x + (*it)->x,
338 position ().y + (*it)->y,
339 position ().z + (*it)->z - m_displacement);
340
341 double angle = rad_to_deg (std::atan2 (-m_displacement, (*it)->y));
342 glRotatef (angle, 1.0, 0.0, 0.0);
343
344 glCallList ((*it)->display_list);
345 glPopMatrix ();
346 }
347 }
348