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