| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : WWPhys *
- * *
- * $Archive:: /Commando/Code/wwphys/wheel.cpp $*
- * *
- * Original Author:: Greg Hjelstrom *
- * *
- * $Author:: Byon_g $*
- * *
- * $Modtime:: 3/19/02 2:32p $*
- * *
- * $Revision:: 27 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * SuspensionElementClass::SuspensionElementClass -- Constructor *
- * SuspensionElementClass::~SuspensionElementClass -- Destructor *
- * SuspensionElementClass::Init -- Initialization, pass in bone indices for this wheel *
- * SuspensionElementClass::Intersect_Spring -- Intersect the suspension spring with the terr *
- * SuspensionElementClass::Update_Model -- Update the wheel transforms in the model. *
- * SuspensionElementClass::Translate_Wheel_On_Axis -- Translates the wheel to the contact po *
- * SuspensionElementClass::Translate_Wheel -- Translates the wheel to the contact point *
- * SuspensionElementClass::Rotate_Fork -- Rotates the "fork" until the wheel touches *
- * WheelClass::WheelClass -- Constructor *
- * WheelClass::~WheelClass -- Destructor *
- * WheelClass::Init -- Initialize the wheel object *
- * WheelClass::Compute_Force_And_Torque -- Computes wheel forces *
- * WheelClass::Compute_Suspension_Force -- Computes the suspension force *
- * WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity -- Computes the current angular veloci *
- * WheelClass::Apply_Forces -- Computes the final forces and adds them *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "wheel.h"
- #include "wheelvehicle.h"
- #include "trackedvehicle.h"
- #include "lineseg.h"
- #include "physcoltest.h"
- #include "physcon.h"
- #include "pscene.h"
- #include "wwprofile.h"
- // Wheel friction is a proportional controller, this is the constant.
- // TODO: Should we make this an editable parameter of VehiclePhysDefClass?
- const float WHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 7.5f;
- const float BIKE_WHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 3.0f;
- const float TRACKWHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 5.0f;
- const float VTOLWHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 3.0f;
- // Debugging
- const Vector3 SUSPENSION_FORCE_COLOR = Vector3(0,0,1);
- const Vector3 LATERAL_FORCE_COLOR = Vector3(0,1,0);
- const Vector3 TRACTIVE_FORCE_COLOR = Vector3(1,0,0);
- // Maximum total acceleration the suspension will exert on the parent vehicle (in m/s^2)
- // Note, each "real" wheel gets an equal fraction of this, so if you have 4 wheels, the max
- // acceleration any single wheel will exert is one quarter of the total.
- const float MAX_SUSPENSION_ACCEL = 50.0f;
- /*
- ** Wheels: An instance of this object will contain information on each
- ** wheel detected in the vehicle model. Wheels use a pair of named bones.
- ** The "position" bone is used to move the wheel up and down according to
- ** the suspension system and to rotate if this is a "steering" wheel. The
- ** "rotation" bone is used to roll the wheel on the terrain.
- **
- ** More Info on the "Position" and "Center" bones:
- ** - The "center" bone is only used in computing the radius of the wheel and
- ** it is rotated about its z-axis to make the wheel appear to roll
- ** - The "position" bone is used in conjunction with the length parameter
- ** to define the suspension springs.
- **
- ** Defining the "spring-segment" using the position bone:
- ** - The default location of the position bone is considered the point of maximum-
- ** compression. (this point should be *inside* the collision box for the model!)
- ** - The spring will extend down the -z axis in the position bone's coordinate system
- **
- ** Graphical constraints for the wheel
- ** - One of three methods can be used to graphically constrain the wheel with the
- ** ground: Translation along the z-axis of the position bone, or rotation of
- ** a rotation constraint bone.
- ** - Normal case: There are two bones for the wheel: position and center. The position
- ** bone is moved along its Z-axis to the point of collision with the ground
- ** - Translation: There is an additional translation bone (WheelTxx) which defines
- ** the axis that the wheel is to be translated along.
- ** - Fork/Rotation: a "fork" bone (WheelFxx) is rotated such that the Z-coordinate (in
- ** the fork's coordinate system) meets the ground.
- **
- ** Wheel Flags:
- ** - 'E' Engine. this wheel is connected to the engine and should exert its force
- ** - 'S' Steering: The position bone for this wheel rotates about its Z-axis for steering
- ** - 'L' Left Track: this wheel is part of the left track of a tracked vehicle
- ** - 'R' Right Track: this wheel is part of the right track of a tracked vehicle
- */
- /*********************************************************************************************************
- SuspensionElementClass Implementation
- *********************************************************************************************************/
- /***********************************************************************************************
- * SuspensionElementClass::SuspensionElementClass -- Constructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- *=============================================================================================*/
- SuspensionElementClass::SuspensionElementClass(void):
- Parent(NULL),
- Flags(DEFAULT_FLAGS),
- PositionBone(-1),
- AxisBone(-1),
- ForkBone(-1),
- ObjWheelTM(1),
- SpringConstant(DEFAULT_SPRING_CONSTANT),
- DampingCoefficient(DEFAULT_DAMPING_COEFFICIENT),
- SpringLength(DEFAULT_SPRING_LENGTH),
- SteeringAngle(0.0f),
- SuspensionForce(0,0,0),
- WheelTM(1),
- WheelP0(0,0,0),
- Contact(0,0,0),
- Normal(0,0,0),
- ContactSurface(0),
- LastPoint(0,0,0),
- TranslationScale(1.0f),
- ObjAxis(0,0,-1),
- ObjForkTM(1),
- ForkLength(0.0f),
- ForkZ(0.0f),
- ForkSin0(0.0f),
- ForkCos0(0.0f),
- ForkA(0.0f),
- ForkB(0.0f),
- SpringEndP0(0.0f,0.0f,0.0f),
- SpringEndP1(0.0f,0.0f,0.0f)
- {
- }
- /***********************************************************************************************
- * SuspensionElementClass::~SuspensionElementClass -- Destructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- SuspensionElementClass::~SuspensionElementClass(void)
- {
- if (Parent != NULL) {
- RenderObjClass * model = Parent->Peek_Model();
- if (model != NULL) {
- if (PositionBone != -1) {
- model->Release_Bone(PositionBone);
- }
- if (ForkBone != -1) {
- model->Release_Bone(ForkBone);
- }
- if (AxisBone != -1) {
- model->Release_Bone(AxisBone);
- }
- }
- }
- }
- /***********************************************************************************************
- * SuspensionElementClass::Init -- Initialization, pass in bone indices for this wheel *
- * *
- * INPUT: *
- * obj - parent vehicle physics object *
- * pbone - index of the position bone (position bone is the contact point for the wheel) *
- * rbone - index of the center bone (center of rotation) *
- * fbone - index of the fork bone for a "fork" constraint (bad name, this is the back wheel of *
- * a motorcycle) *
- * abone - index of the bone for an axis constraint. (like the front wheel of a motorcycle) *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Init(VehiclePhysClass * obj,int pbone,int rbone,int fbone,int abone)
- {
- WWASSERT(obj != NULL); // have to have a valid object
- WWASSERT(pbone != -1); // have to have a position bone at least
- WWASSERT(obj->Peek_Model() != NULL); // have to have a valid model
- // Store a pointer to the vehicle. Not referenced because this class meant to be a private
- // member of wheeled vehicle. A reference here would be circular.
- Parent = obj;
- // Grab a pointer to the render model
- RenderObjClass * model = obj->Peek_Model();
- // Store the bone indices and capture the bones
- PositionBone = pbone;
- ForkBone = fbone;
- AxisBone = abone;
- WWASSERT(PositionBone != -1);
- model->Capture_Bone(PositionBone);
- if (ForkBone != -1) {
- model->Capture_Bone(ForkBone);
- }
- if (AxisBone != -1) {
- model->Capture_Bone(AxisBone);
- }
- // Compute the object-space transform of the wheel
- Matrix3D position_bone,rootinv;
- const Matrix3D & root = model->Get_Transform();
- root.Get_Orthogonal_Inverse(rootinv);
- position_bone = model->Get_Bone_Transform(PositionBone);
- Matrix3D::Multiply(rootinv,position_bone,&ObjWheelTM);
- WheelTM = position_bone;
- WheelTM.Get_Translation(&WheelP0);
- // Initialize the last position of the wheel
- LastPoint = position_bone.Get_Translation();
- // If a fork is constraint being used, compute the constants for it
- if (ForkBone != -1) {
- Matrix3D::Multiply(rootinv,model->Get_Bone_Transform(ForkBone),&ObjForkTM);
- Vector3 r = ObjWheelTM.Get_Translation() - ObjForkTM.Get_Translation();
- ForkLength = r.Length();
- Vector3 pos;
- ObjWheelTM.Get_Translation(&pos);
- Matrix3D::Inverse_Transform_Vector(ObjForkTM,pos,&pos);
- WWASSERT(WWMath::Fabs(ForkLength - pos.Length()) < 0.1f);
- ForkZ = pos.Z;
- ForkSin0 = pos.Z / ForkLength;
- ForkCos0 = pos.X / ForkLength;
- ForkA = ForkCos0 / ForkSin0;
- ForkB = -(ForkCos0 * ForkCos0) / ForkSin0 - ForkSin0;
- }
- // If a translation axis constraint is being used, compute the axis
- if (AxisBone != -1) {
- Matrix3D axistm;
- Matrix3D::Multiply(rootinv,model->Get_Bone_Transform(AxisBone),&axistm);
- axistm.Get_Z_Vector(&ObjAxis);
- Vector3 springdir;
- position_bone.Get_Z_Vector(&springdir);
- TranslationScale = 1.0f / Vector3::Dot_Product(ObjAxis,springdir);
- }
- }
- /***********************************************************************************************
- * SuspensionElementClass::Intersect_Spring -- Intersect the suspension spring with the terrai *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Intersect_Spring(void)
- {
- WWASSERT(Parent != NULL);
- // transform the wheel coordinate system into world space.
- Matrix3D::Multiply(Parent->Get_Transform(),ObjWheelTM,&WheelTM);
- // compute WheelP0 and temporary p1 (endpoints of the spring)
- Vector3 p1;
- WheelTM.Get_Translation(&WheelP0);
- Matrix3D::Transform_Vector(WheelTM,Vector3(0,0,-SpringLength),&p1);
- // If the spring endpoints haven't changed don't do raycast (very expensive)
- // TODO: Ensure we are not on top of an animated mesh!
- if (p1==SpringEndP1 && WheelP0==SpringEndP0) return;
- SpringEndP1=p1;
- SpringEndP0=WheelP0;
- // cast a ray, colliding with "physical" objects
- LineSegClass line(WheelP0,p1);
- CastResultStruct result;
- PhysRayCollisionTestClass raytest(line,&result,Parent->Get_Collision_Group(),COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE);
- PhysicsSceneClass::Get_Instance()->Cast_Ray(raytest,false);
- // evaluate the result of the raycast
- if (result.Fraction >= 1.0f) {
- Set_Flag(INCONTACT,false);
- WheelTM.Set_Translation(p1);
- Contact = p1;
- } else {
- Set_Flag(INCONTACT,true);
- line.Compute_Point(result.Fraction,&Contact);
- WheelTM.Set_Translation(Contact);
- Normal = result.Normal;
- ContactSurface = result.SurfaceType;
- // TODO: Low-level collision routines should handle this?
- if (Vector3::Dot_Product(Normal,line.Get_Dir()) > 0.0f) {
- Normal = -Normal;
- }
- }
- }
- /***********************************************************************************************
- * SuspensionElementClass::Non_Physical_Intersect_Spring -- used in ecyclopedia viewer... *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- *=============================================================================================*/
- void SuspensionElementClass::Non_Physical_Intersect_Spring(float suspension_fraction)
- {
- WWASSERT(Parent != NULL);
- // transform the wheel coordinate system into world space.
- Matrix3D::Multiply(Parent->Get_Transform(),ObjWheelTM,&WheelTM);
- // compute WheelP0 and temporary p1 (endpoints of the spring)
- Vector3 p1;
- WheelTM.Get_Translation(&WheelP0);
- Matrix3D::Transform_Vector(WheelTM,Vector3(0,0,-SpringLength),&p1);
- // Form a ray to compute the contact point.
- Set_Flag(INCONTACT,true);
- LineSegClass line(WheelP0,p1);
- line.Compute_Point(suspension_fraction,&Contact);
- WheelTM.Set_Translation(Contact);
- Normal.Set(0.0f,0.0f,1.0f);
- ContactSurface = 0;
- }
- /***********************************************************************************************
- * SuspensionElementClass::Update_Model -- Update the wheel transforms in the model. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Update_Model(void)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if (model == NULL) return;
- // update the contact point.
- Intersect_Spring();
-
- // use the constraints to make the wheel touch the ground
- if (ForkBone != -1) {
- Rotate_Fork(model);
- } else if (AxisBone != -1) {
- Translate_Wheel_On_Axis(model);
- } else {
- Translate_Wheel(model);
- }
- }
- /***********************************************************************************************
- * SuspensionElementClass::Non_Physical_Update -- Update the wheel transforms in the model. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Non_Physical_Update(float suspension_fraction,float rotation)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if (model == NULL) return;
- // Set the wheel up as if it had an intersection at 'fraction' of its length
- Non_Physical_Intersect_Spring(suspension_fraction);
- // Update the "constraints"
- if (ForkBone != -1) {
- Rotate_Fork(model);
- } else if (AxisBone != -1) {
- Translate_Wheel_On_Axis(model);
- } else {
- Translate_Wheel(model);
- }
- }
- /***********************************************************************************************
- * SuspensionElementClass::Translate_Wheel_On_Axis -- Translates the wheel to the contact poin *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Translate_Wheel_On_Axis(RenderObjClass * model)
- {
- // This function handles moving the wheel down to touch the ground
- // when there is a translation bone. We just translate down its -z axis
- Matrix3D position_tm(1);
- position_tm.Rotate_Z(SteeringAngle);
- if (Get_Flag(INCONTACT)) {
- Vector3 dp;
- Vector3 springdir;
- Vector3::Subtract(Contact,WheelP0,&dp);
- WheelTM.Get_Z_Vector(&springdir);
- position_tm.Adjust_Z_Translation(TranslationScale * Vector3::Dot_Product(dp,springdir));
- } else {
- position_tm.Adjust_Z_Translation(TranslationScale * -SpringLength);
- }
- model->Control_Bone(AxisBone,position_tm);
-
- }
- /***********************************************************************************************
- * SuspensionElementClass::Translate_Wheel -- Translates the wheel to the contact point *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Translate_Wheel(RenderObjClass * model)
- {
- // This function handles moving the wheel down to touch the ground
- // when there is no "fork" present. We just translate down the -z axis
- Matrix3D position_tm(1);
- position_tm.Rotate_Z(SteeringAngle);
- if (Get_Flag(INCONTACT)) {
- Vector3 dp;
- Vector3 springdir;
- Vector3::Subtract(Contact,WheelP0,&dp);
- WheelTM.Get_Z_Vector(&springdir);
- position_tm.Adjust_Z_Translation(Vector3::Dot_Product(dp,springdir));
- } else {
- position_tm.Adjust_Z_Translation(-SpringLength);
- }
- model->Control_Bone(PositionBone,position_tm);
- }
- /***********************************************************************************************
- * SuspensionElementClass::Rotate_Fork -- Rotates the "fork" until the wheel touches *
- * *
- * Again, "fork" was the wrong name for this. This is a "constraint" like the back wheel *
- * of a motorcycle. The "fork" bone will be rotated about its Z axis until the WheelP *
- * bone is at the Z-position of the contact point. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void SuspensionElementClass::Rotate_Fork(RenderObjClass * model)
- {
- WWASSERT(ForkBone != -1);
-
- float fork_sin = 0.0f;
- float fork_cos = 1.0f;
- // transform the contact point into the fork coordinate system
- // - first, transform into object space
- // - then transform into "fork" space
- Vector3 p1;
- Matrix3D::Inverse_Transform_Vector(Parent->Get_Transform(),Contact,&p1);
- Matrix3D::Inverse_Transform_Vector(ObjForkTM,p1,&p1);
- // Only concerned with the x and z coordinates now. And x is a function of z
- float x2 = ForkLength*ForkLength - p1.Z * p1.Z;
- if (x2 < 0.0f) {
- return;
- }
- p1.X = WWMath::Sqrt(x2);
- // Compute the sine and cosine of the new rotation (without calling atan,sine or cosine!)
- float ooforklen = 1.0f / ForkLength;
- fork_sin = -(p1.X * ooforklen - ForkA * p1.Z * ooforklen) / ForkB;
- fork_cos = (p1.Z * ooforklen) / ForkSin0 + ForkA * fork_sin;
- #if 0
- float angle1 = atan2(p1.Z,p1.X);
- float angledelta = angle1 - atan2(ForkSin0,ForkCos0);
- fork_sin = -WWMath::Sin(angledelta);
- fork_cos = WWMath::Cos(angledelta);
- #endif
-
- Matrix3D fork_rotation(1);
- fork_rotation.Rotate_Y(fork_sin,fork_cos);
- model->Control_Bone(ForkBone,fork_rotation);
- }
- /*********************************************************************************************************
- WheelClass Implementation
- *********************************************************************************************************/
- /***********************************************************************************************
- * WheelClass::WheelClass -- Constructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- *=============================================================================================*/
- WheelClass::WheelClass(void) :
- Radius(1.0f),
- RotationBone(-1),
- Rotation(0.0f),
- RotationDelta(0.0f),
- AxleTorque(0.0f),
- TractiveFrictionForce(0,0,0),
- LateralFrictionForce(0,0,0),
- SlipFactor(1.0f),
- IdealAngularVelocity(0.0f)
- {
- }
- /***********************************************************************************************
- * WheelClass::~WheelClass -- Destructor *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- WheelClass::~WheelClass(void)
- {
- if (Parent != NULL) {
- RenderObjClass * model = Parent->Peek_Model();
- if (model != NULL) {
- if (RotationBone != -1) {
- model->Release_Bone(RotationBone);
- }
- }
- }
- }
- /***********************************************************************************************
- * WheelClass::Init -- Initialize the wheel object *
- * *
- * INPUT: *
- * obj - parent vehicle physics object *
- * position_bone - WHEELP, contact point, wheel position *
- * rotation_bone - WHEELC, center of rotation for the wheel *
- * fork_bone - bone for a constraint like the back wheel of a motorcycle (rotates about Z) *
- * axis_bone - bone for a constraint like the front wheel of a motorcycle (translates along Z) *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void WheelClass::Init
- (
- VehiclePhysClass * obj,
- int position_bone,
- int rotation_bone,
- int fork_bone,
- int axis_bone
- )
- {
- // Allow the base class to init
- SuspensionElementClass::Init(obj,position_bone,rotation_bone,fork_bone,axis_bone);
-
- // Grab a pointer to the render model
- RenderObjClass * model = obj->Peek_Model();
- WWASSERT(model != NULL);
- // Store the bone indices and capture the bones
- RotationBone = rotation_bone;
- if (RotationBone != -1) {
- model->Capture_Bone(RotationBone);
- }
- // Compute the radius of the wheel
- Matrix3D rotation_tm(1);
- if (RotationBone != -1) {
- rotation_tm = model->Get_Bone_Transform(RotationBone);
- Radius = (WheelP0 - rotation_tm.Get_Translation()).Length();
- }
- }
- /***********************************************************************************************
- * WheelClass::Compute_Force_And_Torque -- Computes wheel forces *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void WheelClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
- {
- WWPROFILE("WheelClass::Compute_Force_And_Torque");
- if (Get_Flag(FAKE)) return;
- // Reset variables
- SlipFactor = 1.0f;
- SuspensionForce.Set(0,0,0);
- TractiveFrictionForce.Set(0,0,0);
- LateralFrictionForce.Set(0,0,0);
- IdealAngularVelocity = 0.0f;
-
- // Intersect the spring with the world to find our contact point
- // If it doesn't hit anything, we are done!
- {
- WWPROFILE("Intersect_Spring");
- Intersect_Spring();
- }
- if (!Get_Flag(INCONTACT)) {
- return;
- }
-
- // Rotate the wheel coordinate system by the steering angle
- WheelTM.Rotate_Z(SteeringAngle);
- // Grab the 'Definition' for our parent for quick access to the constants
- const VehiclePhysDefClass * vehicle_def = Parent->Get_VehiclePhysDef();
- // Compute the velocity of the contact point both in world space and in
- // the wheel's coordinate system
- Vector3 pdot,local_pdot;
- Parent->Compute_Point_Velocity(Contact,&pdot);
- local_pdot = pdot - Vector3::Dot_Product(pdot,Normal)*Normal;
- Matrix3D::Inverse_Rotate_Vector(WheelTM,local_pdot,&local_pdot);
- IdealAngularVelocity = local_pdot.X / Radius;
-
- // Suspension Force:
- Compute_Suspension_Force(pdot,local_pdot,&SuspensionForce);
- // Get the load on this wheel. I'm not using the spring force since it is too
- // sloppy; instead, I'll just give each wheel an equal share of the vehicle weight.
- // Also, compute the lateral components of gravitational acceleration so that
- // the wheel can attempt to cancel them.
- // TODO: load distribution?
- Vector3 gravity(0.0f,0.0f,-Parent->Get_Weight());
- gravity /= Parent->Get_Real_Wheel_Count();
- Vector3 local_gravity;
- Matrix3D::Inverse_Rotate_Vector(WheelTM,gravity,&local_gravity);
-
- float wheel_normal_force = -local_gravity.Z;
- wheel_normal_force *= vehicle_def->Get_Traction_Multiplier();
-
- // Friction / Traction Forces (initialized so that they cancel gravity):
- // (gth, 8/19/2001) scale the gravity cancellation forces down a bit so they don't cause
- // vehicles to slide uphill!
- const float GRAVITATION_CANCELLATION_FACTOR = 1.0f;
- float tractive_force = -local_gravity.X * GRAVITATION_CANCELLATION_FACTOR;
- float lateral_force = -local_gravity.Y * GRAVITATION_CANCELLATION_FACTOR;
- // testing:
- #if 0
- Vector3 fs,ft,fn;
- Matrix3D::Rotate_Vector(WheelTM,Vector3(0,0,-local_gravity.Z),&fs);
- Matrix3D::Rotate_Vector(WheelTM,Vector3(tractive_force,0,0),&ft);
- Matrix3D::Rotate_Vector(WheelTM,Vector3(0,lateral_force,0),&fn);
- Vector3 sum = fs+ft+fn-gravity;
- if (sum.Length2() < 0.001f) {
- int test=0;
- }
- #endif
- Compute_Traction_Forces(local_pdot,wheel_normal_force,&lateral_force,&tractive_force);
- // Compute the world-space traction forces
- Matrix3D::Rotate_Vector(WheelTM,Vector3(tractive_force,0,0),&TractiveFrictionForce);
- Matrix3D::Rotate_Vector(WheelTM,Vector3(0,lateral_force,0),&LateralFrictionForce);
-
- // Apply the final forces and torques
- Apply_Forces(force,torque);
- }
- /***********************************************************************************************
- * WheelClass::Compute_Suspension_Force -- Computes the suspension force *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void WheelClass::Compute_Suspension_Force(const Vector3 & pdot,const Vector3 & local_pdot,Vector3 * suspension_force)
- {
- WWPROFILE("WheelClass::Compute_Suspension_Force");
- // -----------------------------------------------------------------------------
- // Suspension Force:
- // - Compute the displacement of the wheel along the spring axis
- // - Compute the velocity of the wheel projected onto the spring axis
- // - Compute the spring/damper force
- // -----------------------------------------------------------------------------
- Vector3 spring_dir;
- WheelTM.Get_Z_Vector(&spring_dir); // spring_dir points from the wheel up to the body
-
- float dv = Vector3::Dot_Product(pdot,spring_dir);
- float dx = Vector3::Dot_Product(WheelP0 - Contact,spring_dir) - SpringLength;
- float sforce = - SpringConstant*dx - DampingCoefficient*dv;
- // Clamp the suspension force to produce at most an acceleration of MAX_SUSPENSION_ACCEL
- float max_sforce = MAX_SUSPENSION_ACCEL * Parent->Get_Mass() / WWMath::Max(Parent->Get_Real_Wheel_Count(),1.0f);
- sforce = WWMath::Clamp(sforce,-max_sforce,max_sforce);
-
- *suspension_force = Normal;
- *suspension_force *= sforce;
- }
- /***********************************************************************************************
- * WheelClass::Apply_Forces -- Computes the final forces and adds them in. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- void WheelClass::Apply_Forces(Vector3 * force,Vector3 * torque)
- {
- WWPROFILE("WheelClass::Apply_Forces");
- // Grab the 'Definition' for our parent for quick access to the constants
- const VehiclePhysDefClass * vehicle_def = Parent->Get_VehiclePhysDef();
- // -----------------------------------------------------------------------------
- // Compute the final forces and torques
- // I'm applying the suspension force and lateral friction forces in the X-Y plane of
- // the CM of the vehicle since we really don't like vehicles that tip over. May even
- // need to add a self-balancing force to keep the vehicle from *ever* fliping over...
- // -----------------------------------------------------------------------------
- Vector3 wheel_pos;
- Vector3 r_lateral; // moment arm for lateral forces
- Vector3 r_tractive; // moment arm for tractive forces
-
- ObjWheelTM.Get_Translation(&wheel_pos);
- wheel_pos.Z = -vehicle_def->Get_Lateral_Moment_Arm();
- Matrix3D::Rotate_Vector(Parent->Get_Transform(),wheel_pos,&r_lateral);
- wheel_pos.Z = -vehicle_def->Get_Tractive_Moment_Arm();
- Matrix3D::Rotate_Vector(Parent->Get_Transform(),wheel_pos,&r_tractive);
- // Apply the suspension force in the x-y plane of the CM (actually doesn't matter...)
- *force += SuspensionForce;
- *torque += Vector3::Cross_Product(r_lateral,SuspensionForce);
- // Apply the lateral friction force
- *force += LateralFrictionForce;
- *torque += Vector3::Cross_Product(r_lateral,LateralFrictionForce);
- // Apply the tractive friction force
- *force += TractiveFrictionForce;
- *torque += Vector3::Cross_Product(r_tractive,TractiveFrictionForce);
- // Debugging
- #ifdef WWDEBUG
- Vector3 position;
- Parent->Get_Position(&position);
- Parent->Add_Debug_Vector(position + r_lateral,SuspensionForce / Parent->Get_Mass(),SUSPENSION_FORCE_COLOR);
- Parent->Add_Debug_Vector(position + r_lateral,LateralFrictionForce / Parent->Get_Mass(),LATERAL_FORCE_COLOR);
- Parent->Add_Debug_Vector(position + r_tractive,TractiveFrictionForce / Parent->Get_Mass(),TRACTIVE_FORCE_COLOR);
- #endif
- }
- /***********************************************************************************************
- * WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity -- Computes the current angular velocity *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 12/18/2000 gth : Created. *
- *=============================================================================================*/
- float WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity(float max_avel)
- {
- if (Get_Flag(INCONTACT)) {
- if (IdealAngularVelocity > max_avel) {
- return IdealAngularVelocity;
- }
- }
- return max_avel;
- }
- /*************************************************************************************
- WVWheelClass Implementation (Wheeled-Vehicle-Wheel)
- *************************************************************************************/
- void WVWheelClass::Compute_Traction_Forces
- (
- const Vector3 & local_pdot,
- float wheel_normal_force,
- float * set_lateral_force,
- float * set_tractive_force
- )
- {
- WWPROFILE("WVWheelClass::Compute_Traction_Forces");
- if (wheel_normal_force > 0.0f) {
- // Look up the friction coefficient and drag coefficient for this surface
- float drag_coefficient = PhysicsConstants::Get_Contact_Drag_Coefficient(
- PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
- ContactSurface );
- float friction_coefficient = PhysicsConstants::Get_Contact_Friction_Coefficient(
- PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
- ContactSurface );
-
- // Friction force from sliding should act like a proportional controller while the
- // engine torque or braking force should just be added in (later to be clamped to the
- // friction circle)
- float tractive_friction_coefficient = 0.001f;
- float lateral_friction_coefficient = friction_coefficient;
- if ((Get_Flag(BRAKING) == true) || (Parent->Is_Engine_Enabled() == false)) {
- tractive_friction_coefficient = lateral_friction_coefficient;
- }
-
- // Friction "penalty" force, proportional to the velocity, tuned so that the force
- // will be small when the velocity is small. The lateral and tractive friction forces
- // will be set to the MIN between this proportional force and the maximum friction force.
- float penalty_k = WHEEL_FRICTION_PENALTY_CONSTANT;
- if (Parent->Get_Real_Wheel_Count() < 4) {
- penalty_k = BIKE_WHEEL_FRICTION_PENALTY_CONSTANT;
- }
- float tractive_force = WWMath::Min (
- (penalty_k * wheel_normal_force * WWMath::Fabs(local_pdot.X)),
- (wheel_normal_force * tractive_friction_coefficient)
- );
- float lateral_force = WWMath::Min (
- (penalty_k * wheel_normal_force * WWMath::Fabs(local_pdot.Y)),
- (wheel_normal_force * lateral_friction_coefficient)
- );
- tractive_force *= -WWMath::Sign(local_pdot.X);
- lateral_force *= -WWMath::Sign(local_pdot.Y);
- tractive_force -= drag_coefficient * local_pdot.X * local_pdot.X;
- if (Get_Flag(ENGINE) && !Get_Flag(BRAKING)) {
- tractive_force += AxleTorque / Radius;
- }
- // Add in the initial lateral and tractive force (gravitation cancelling)
- lateral_force += *set_lateral_force;
- tractive_force += *set_tractive_force;
- // Clamp to the friction circle
- Vector2 friction_force(tractive_force,lateral_force);
- float max_friction = friction_coefficient * wheel_normal_force;
- float max_friction2 = max_friction * max_friction;
- float friction2 = friction_force.Length2();
- if (friction2 > max_friction2) {
- float friction = WWMath::Sqrt(friction2);
- friction_force *= max_friction / friction;
- SlipFactor = friction / max_friction;
- }
- // Set the output
- *set_tractive_force = friction_force.X;
- *set_lateral_force = friction_force.Y;
- } else {
- *set_tractive_force = 0.0f;
- *set_lateral_force = 0.0f;
- SlipFactor = 1.0f;
- }
- }
- void WVWheelClass::Update_Model(void)
- {
- WheelClass::Update_Model();
- Roll_Wheel();
- }
- void WVWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if (model == NULL) return;
- // Set up the position and constraints
- WheelClass::Non_Physical_Update(suspension_fraction,rotation);
- // Set the rotation
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- }
- void WVWheelClass::Roll_Wheel(void)
- {
- RenderObjClass * model = Parent->Peek_Model();
- WheeledVehicleClass * wv_parent = Parent->As_WheeledVehicleClass();
-
- if ((model == NULL) || (wv_parent == NULL) || (RotationBone == -1)) {
- return;
- }
- // if we're braking, the wheels lock (not realistic but fun!)
- if (wv_parent->Is_Braking() == false) {
-
- if (Get_Flag(INCONTACT) || (Get_Flag(ENGINE) /*&& Not_In_Neutral*/)) {
- // Rotate the wheel based on movement
- Vector3 forward;
- Vector3 move;
-
- WheelTM.Get_X_Vector(&forward);
- Vector3::Subtract(Contact,LastPoint,&move);
- float dist = Vector3::Dot_Product(move,forward);
-
- RotationDelta = -dist/Radius;
-
- if (Get_Flag(ENGINE) && (SlipFactor > 1.0f) && (SlipFactor < 100.0f)) {
- RotationDelta *= 2.0f * SlipFactor;
- }
-
- Rotation += RotationDelta;
- WWASSERT(WWMath::Is_Valid_Float(RotationDelta));
- WWASSERT(WWMath::Is_Valid_Float(Rotation));
- } else {
-
- // Rotate the wheel at the rate it was last rolling and slow it down a little
- Rotation += RotationDelta;
- RotationDelta *= 0.97f;
- WWASSERT(WWMath::Is_Valid_Float(RotationDelta));
- WWASSERT(WWMath::Is_Valid_Float(Rotation));
- }
- }
-
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(Rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
- else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
- LastPoint = Contact;
- }
- /*************************************************************************************
- TrackWheelClass Implementation
- These wheels differ from WVWheelClass in that they do not roll independently.
- They ask their TrackedVehicle parent how much their track has rolled and roll
- with it. Also, their friction forces may not behave the same way as wheeled
- vehicle wheels... Tanks probably shouldn't slide very much.
- *************************************************************************************/
- void TrackWheelClass::Compute_Traction_Forces
- (
- const Vector3 & local_pdot,
- float wheel_normal_force,
- float * set_lateral_force,
- float * set_tractive_force
- )
- {
- WWPROFILE("TrackWheelClass::Compute_Traction_Forces");
- // Look up the friction coefficient for this surface
- float friction_coefficient = PhysicsConstants::Get_Contact_Friction_Coefficient(
- PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
- ContactSurface );
-
- // Friction force from sliding should act like a proportional controller while the
- // engine torque or braking force should just be added in (later to be clamped to the
- // friction circle)
- float tractive_friction_coefficient = 0.0001f;
- float lateral_friction_coefficient = friction_coefficient;
- // Try to stop if our engine is off
- if (Parent->Is_Engine_Enabled() == false) {
- tractive_friction_coefficient = lateral_friction_coefficient;
- }
- // Friction "penalty" force, proportional to the velocity, tuned so that the force
- // will be small when the velocity is small. The lateral and tractive friction forces
- // will be set to the MIN between this proportional force and the maximum friction force.
- #if 0
- float tractive_force = WWMath::Min (
- (TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.X)),
- (wheel_normal_force * tractive_friction_coefficient) );
- float lateral_force = WWMath::Min (
- (TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.Y)),
- (wheel_normal_force * lateral_friction_coefficient) );
- #else
- float tractive_force = TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.X);
- float lateral_force = TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.Y);
- #endif
- tractive_force *= -WWMath::Sign(local_pdot.X);
- tractive_force += AxleTorque / Radius;
- tractive_force += *set_tractive_force;
-
- lateral_force *= -WWMath::Sign(local_pdot.Y);
- lateral_force += *set_lateral_force;
- // Clamp to the friction circle
- Vector2 wheel_force(tractive_force,lateral_force);
- float max_friction = 0.9f * wheel_normal_force;
- float max_friction2 = max_friction * max_friction;
- float wheel_force2 = wheel_force.Length2();
- if (wheel_force2 > max_friction2) {
- float initial_wheel_force = WWMath::Sqrt(wheel_force2);
- wheel_force *= max_friction / initial_wheel_force;
- SlipFactor = initial_wheel_force / max_friction;
- }
- // Return the result
- *set_tractive_force = wheel_force.X;
- *set_lateral_force = wheel_force.Y;
- }
- void TrackWheelClass::Update_Model(void)
- {
- WheelClass::Update_Model();
- Roll_Wheel();
- }
- void TrackWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if (model == NULL) return;
- // Set up the position and constraints
- WheelClass::Non_Physical_Update(suspension_fraction,rotation);
- // Set the rotation
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- }
- void TrackWheelClass::Roll_Wheel(void)
- {
- // roll the wheel according to the contact point motion
- RenderObjClass * model = Parent->Peek_Model();
- TrackedVehicleClass * track_parent = Parent->As_TrackedVehicleClass();
- if ((model == NULL) || (track_parent == NULL) || (RotationBone == -1)) {
- return;
- }
- float dist;
- if (Get_Flag(LEFT_TRACK)) {
- dist = SlipFactor * track_parent->Get_Left_Track_Movement();
- } else {
- dist = SlipFactor * track_parent->Get_Right_Track_Movement();
- }
-
- RotationDelta = -dist/Radius;
- Rotation += RotationDelta;
- // apply the rotation to the wheel's center bone
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(Rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
- else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
- LastPoint = Contact;
- }
- /*************************************************************************************
- VTOLWheelClass Implemention
- These wheels just don't like to move. Their purpose is to just hold the vehicle
- up off the ground when it lands.
- *************************************************************************************/
- void VTOLWheelClass::Update_Model(void)
- {
- WheelClass::Update_Model();
- Roll_Wheel();
- }
- void VTOLWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if (model == NULL) return;
- // Set up the position and constraints
- WheelClass::Non_Physical_Update(suspension_fraction,rotation);
- // Set the rotation
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- }
- void VTOLWheelClass::Compute_Traction_Forces
- (
- const Vector3 & local_pdot,
- float normal_force,
- float * set_lateral_force,
- float * set_tractive_force
- )
- {
- WWPROFILE("VTOLWheelClass::Compute_Traction_Forces");
- // Friction "penalty" force, proportional to the velocity, tuned so that the force
- // will be small when the velocity is small. The lateral and tractive friction forces
- *set_tractive_force -= VTOLWHEEL_FRICTION_PENALTY_CONSTANT * normal_force * local_pdot.X;
- *set_lateral_force -= VTOLWHEEL_FRICTION_PENALTY_CONSTANT * normal_force * local_pdot.Y;
- }
- void VTOLWheelClass::Roll_Wheel(void)
- {
- RenderObjClass * model = Parent->Peek_Model();
- if ((RotationBone != -1) && (model != NULL)) {
- if (Get_Flag(INCONTACT)) {
-
- // Rotate the wheel based on movement
- Vector3 forward;
- Vector3 move;
-
- WheelTM.Get_X_Vector(&forward);
- Vector3::Subtract(Contact,LastPoint,&move);
- float dist = Vector3::Dot_Product(move,forward);
-
- RotationDelta = -dist/Radius;
- Rotation += RotationDelta;
- } else {
-
- // Rotate the wheel at the rate it was last rolling and slow it down a little
- Rotation += RotationDelta;
- RotationDelta *= 0.97f;
-
- }
-
- Matrix3D rotation_tm(1);
- rotation_tm.Rotate_Z(Rotation);
- model->Control_Bone(RotationBone,rotation_tm);
- if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
- else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
- LastPoint = Contact;
- }
- }
|