| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- /*
- ** 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/motorvehicle.cpp $*
- * *
- * Author:: Greg Hjelstrom *
- * *
- * $Modtime:: 10/23/01 1:56p $*
- * *
- * $Revision:: 37 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "motorvehicle.h"
- #include "physcontrol.h"
- #include "persistfactory.h"
- #include "wwphysids.h"
- #include "wwhack.h"
- #include "wwprofile.h"
- #include "simplevec.h"
- #include "ode.h"
- #include "lookuptable.h"
- DECLARE_FORCE_LINK(motorvehicle);
- const float GEAR_SHIFT_DELAY = 1.0f;
- const float MIN_BRAKING_SPEED = 1.5f; // below this forward speed, we drive in reverse
- /***********************************************************************************************
- **
- ** MotorVehicleClass Implementation
- **
- ***********************************************************************************************/
- /*
- ** Chunk Ids used by MotorVehicleClass
- */
- enum
- {
- MOTO_CHUNK_RIGIDBODY = 0x01234500, // used to be derived from RigidBody
- MOTO_CHUNK_VARIABLES,
- MOTO_CHUNK_VEHICLEPHYS, // now derived from VehiclePhys
- OBSOLETE_MOTO_VARIABLE_CURENGINETORQUE = 0x00,
- MOTO_VARIABLE_ENGINEANGULARVELOCITY,
- MOTO_VARIABLE_CURRENTGEAR,
- MOTO_VARIABLE_SHIFTIMER,
- };
-
- MotorVehicleClass::MotorVehicleClass(void) :
- EngineAngularVelocity(0.0f),
- CurrentGear(0),
- ShiftTimer(0),
- AcceleratorFraction(0.0f),
- IsBraking(false)
- {
- }
- void MotorVehicleClass::Init(const MotorVehicleDefClass & def)
- {
- VehiclePhysClass::Init(def);
- }
- MotorVehicleClass::~MotorVehicleClass(void)
- {
- }
- void MotorVehicleClass::Timestep(float dt)
- {
- {
- WWPROFILE("MotorVehicle::Timestep");
-
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- // Update the accelerator and braking state according to the current inputs
- AcceleratorFraction = 0.0f;
- IsBraking = false;
- if (Controller) {
- Vector3 objvel;
- Matrix3D::Inverse_Rotate_Vector(Get_Transform(),Velocity,&objvel);
- if ((Controller->Get_Move_Forward() < 0.0f) && (objvel.X > MIN_BRAKING_SPEED)) {
- IsBraking = true;
- } else if ((Controller->Get_Move_Forward() > 0) && (objvel.X < -MIN_BRAKING_SPEED)) {
- IsBraking = true;
- } else {
- AcceleratorFraction = Controller->Get_Move_Forward();
- }
- }
- // Engine simulation revvs up if the drive wheels are not in contact.
- if (!Drive_Wheels_In_Contact()) {
- EngineAngularVelocity += dt * Compute_Engine_Angular_Acceleration();
- }
- // Update our current gear if we are in contact with the ground
- if ((ShiftTimer <= 0.0f) && (Drive_Wheels_In_Contact() == true)) {
- // if we're near red-line and our shift timer has expired, shift up.
- // if we're at very low rpms, shift down.
- if ((EngineAngularVelocity > def->ShiftUpAvel) && (CurrentGear < def->GearCount)) {
- Shift_Up();
- } else if ((EngineAngularVelocity < def->ShiftDownAvel) && (CurrentGear > 0)) {
- Shift_Down();
- }
- } else if (ShiftTimer > 0.0f) {
- ShiftTimer -= dt;
- }
- }
-
- VehiclePhysClass::Timestep(dt);
- }
- /***********************************************************************************************
- **
- ** Engine Simulation code for MotorVehicleClass
- **
- ***********************************************************************************************/
- int MotorVehicleClass::Set_State(const StateVectorClass & new_state,int start_index)
- {
- // Whenever we get a new state from the integrator, make sure we update our
- // engine's angular velocity. This will ensure that the torque output from the
- // engine is in sync with the vehicle's motion. (even though we're not really
- // "simulating" the angular velocity of the engine...)
- start_index = VehiclePhysClass::Set_State(new_state,start_index);
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
-
- if (Drive_Wheels_In_Contact() == true) {
- float wheel_avel = Get_Ideal_Drive_Axle_Angular_Velocity();
- EngineAngularVelocity = wheel_avel * def->GearRatio[CurrentGear] * def->FinalDriveGearRatio;
- }
- return start_index;
- }
- float MotorVehicleClass::Get_Engine_Torque(void)
- {
- // Torque output by the engine depends on the torque curve, how much gas is being given, and
- // the engine's maximum torque. The maximum torque is used to scale the normalized engine
- // torque curve so that we can just re-use the same table for many vehicles.
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- float normalized_torque = 0;
- if (def->EngineTorqueCurve != NULL) {
- normalized_torque = def->EngineTorqueCurve->Get_Value(WWMath::Fabs(EngineAngularVelocity));
- }
- return def->MaxEngineTorque * AcceleratorFraction * normalized_torque;
- }
- float MotorVehicleClass::Get_Axle_Angular_Velocity(void)
- {
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- return EngineAngularVelocity / (def->GearRatio[CurrentGear] * def->FinalDriveGearRatio);
- }
- float MotorVehicleClass::Get_Axle_Torque(void)
- {
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- return Get_Engine_Torque() * def->GearRatio[CurrentGear] * def->FinalDriveGearRatio;
- }
- float MotorVehicleClass::Get_Normalized_Engine_RPM(void)
- {
- return Get_Engine_RPM() / Get_MotorVehicleDef()->ShiftUpRpm;
- }
- float MotorVehicleClass::Get_Max_Engine_Torque(void)
- {
- return Get_MotorVehicleDef()->MaxEngineTorque;
- }
- float MotorVehicleClass::Compute_Engine_Angular_Acceleration(void)
- {
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- return Get_Engine_Torque() / def->DriveTrainInertia;
- }
- void MotorVehicleClass::Shift_Up(void)
- {
- const MotorVehicleDefClass * def = Get_MotorVehicleDef();
- if (CurrentGear < def->GearCount-1) {
- CurrentGear++;
- ShiftTimer = GEAR_SHIFT_DELAY;
- }
- }
- void MotorVehicleClass::Shift_Down(void)
- {
- if (CurrentGear > 0) {
- CurrentGear--;
- ShiftTimer = GEAR_SHIFT_DELAY;
- }
- }
- /***********************************************************************************************
- **
- ** Save-Load for MotorVehicleClass
- ** Note: MotorVehicleClass contains pure virtuals so it does not instantiate a PersistFactory...
- **
- ***********************************************************************************************/
- bool MotorVehicleClass::Save (ChunkSaveClass &csave)
- {
- csave.Begin_Chunk(MOTO_CHUNK_VEHICLEPHYS);
- VehiclePhysClass::Save(csave);
- csave.End_Chunk();
- csave.Begin_Chunk(MOTO_CHUNK_VARIABLES);
- WRITE_MICRO_CHUNK(csave,MOTO_VARIABLE_ENGINEANGULARVELOCITY,EngineAngularVelocity);
- WRITE_MICRO_CHUNK(csave,MOTO_VARIABLE_CURRENTGEAR,CurrentGear);
- WRITE_MICRO_CHUNK(csave,MOTO_VARIABLE_SHIFTIMER,ShiftTimer);
- csave.End_Chunk();
- return true;
- }
- bool MotorVehicleClass::Load (ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk()) {
-
- switch(cload.Cur_Chunk_ID())
- {
- case MOTO_CHUNK_RIGIDBODY: // used to be derived directly from RigidBody... Obsolete now
- RigidBodyClass::Load(cload);
- break;
-
- case MOTO_CHUNK_VEHICLEPHYS:
- VehiclePhysClass::Load(cload);
- break;
- case MOTO_CHUNK_VARIABLES:
- while (cload.Open_Micro_Chunk()) {
- switch(cload.Cur_Micro_Chunk_ID()) {
- READ_MICRO_CHUNK(cload,MOTO_VARIABLE_ENGINEANGULARVELOCITY,EngineAngularVelocity);
- READ_MICRO_CHUNK(cload,MOTO_VARIABLE_CURRENTGEAR,CurrentGear);
- READ_MICRO_CHUNK(cload,MOTO_VARIABLE_SHIFTIMER,ShiftTimer);
- }
- cload.Close_Micro_Chunk();
- }
- break;
-
- default:
- WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
- break;
- }
-
- cload.Close_Chunk();
- }
- return true;
- }
- /***********************************************************************************************
- **
- ** MotorVehicleDefClass Implementation
- **
- ***********************************************************************************************/
- /*
- ** Declare a PersistFactory for MotorVehicleClasses
- */
- SimplePersistFactoryClass<MotorVehicleDefClass,PHYSICS_CHUNKID_MOTORVEHICLEDEF> _MotorVehicleDefFactory;
- /*
- ** Chunk ID's used by MotorVehicleDefClass
- */
- enum
- {
- MOTORVEHICLEDEF_CHUNK_RIGIDBODYDEF = 0x00516000, // (old parent class)
- MOTORVEHICLEDEF_CHUNK_VARIABLES,
- MOTORVEHICLEDEF_CHUNK_VEHICLEPHYSDEF, // (current parent class)
- MOTORVEHICLEDEF_VARIABLE_MAXENGINETORQUE = 0x00,
- MOTORVEHICLEDEF_VARIABLE_ENGINETORQUECURVEFILENAME,
-
- MOTORVEHICLEDEF_VARIABLE_GEARCOUNT,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO1,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO2,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO3,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO4,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO5,
- MOTORVEHICLEDEF_VARIABLE_GEARRATIO6,
- MOTORVEHICLEDEF_VARIABLE_FINALDRIVEGEARRATIO,
- OBSOLETE_MOTORVEHICLEDEF_VARIABLE_ENGINEINERTIA,
- OBSOLETE_MOTORVEHICLEDEF_VARIABLE_TRANSMISSIONINERTIA,
- OBSOLETE_MOTORVEHICLEDEF_VARIABLE_FINALDRIVEINERTIA,
- MOTORVEHICLEDEF_VARIABLE_SHIFTUPRPM,
- MOTORVEHICLEDEF_VARIABLE_SHIFTDOWNRPM,
- MOTORVEHICLEDEF_VARIABLE_DRIVETRAININERTIA,
- };
- MotorVehicleDefClass::MotorVehicleDefClass(void) :
- MaxEngineTorque(5.0f),
- EngineTorqueCurveFilename("Vehicles\\PhysicsTables\\DefaultEngineTorque.tbl"),
- EngineTorqueCurve(NULL),
- GearCount(4),
- FinalDriveGearRatio(2.92f),
- ShiftUpRpm(7000),
- ShiftDownRpm(2000),
- DriveTrainInertia(0.1f)
- {
-
- GearRatio[0] = 12.01f; // 1989 Ford Taurus gear ratios :-)
- GearRatio[1] = 7.82f;
- GearRatio[2] = 5.16f;
- GearRatio[3] = 3.81f;
- GearRatio[4] = 2.79f;
- GearRatio[5] = 1.0f;
- ShiftUpAvel = RPM_TO_RADS(ShiftUpRpm);
- ShiftDownAvel = RPM_TO_RADS(ShiftDownRpm);
- // make our parameters editable
- FLOAT_UNITS_PARAM(MotorVehicleDefClass, MaxEngineTorque, 0.01f, 100000.0f,"N*m");
- FILENAME_PARAM(MotorVehicleDefClass, EngineTorqueCurveFilename, "Table Files", ".tbl");
- INT_EDITABLE_PARAM(MotorVehicleDefClass,GearCount,1,6);
-
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[0],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[1],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[2],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[3],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[4],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,GearRatio[5],1.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,FinalDriveGearRatio,0.0f,100.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,DriveTrainInertia,0.001f,100000.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,ShiftUpRpm,1.0f,100000.0f);
- FLOAT_EDITABLE_PARAM(MotorVehicleDefClass,ShiftDownRpm,1.0f,100000.0f);
- }
- MotorVehicleDefClass::~MotorVehicleDefClass(void)
- {
- REF_PTR_RELEASE(EngineTorqueCurve);
- }
- uint32 MotorVehicleDefClass::Get_Class_ID (void) const
- {
- return CLASSID_MOTORVEHICLEDEF;
- }
- const PersistFactoryClass & MotorVehicleDefClass::Get_Factory (void) const
- {
- return _MotorVehicleDefFactory;
- }
- bool MotorVehicleDefClass::Save(ChunkSaveClass &csave)
- {
- csave.Begin_Chunk(MOTORVEHICLEDEF_CHUNK_VEHICLEPHYSDEF);
- VehiclePhysDefClass::Save(csave);
- csave.End_Chunk();
- ShiftUpAvel = RPM_TO_RADS(ShiftUpRpm);
- ShiftDownAvel = RPM_TO_RADS(ShiftDownRpm);
- csave.Begin_Chunk(MOTORVEHICLEDEF_CHUNK_VARIABLES);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_MAXENGINETORQUE,MaxEngineTorque);
- WRITE_MICRO_CHUNK_WWSTRING(csave,MOTORVEHICLEDEF_VARIABLE_ENGINETORQUECURVEFILENAME,EngineTorqueCurveFilename);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARCOUNT,GearCount);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO1,GearRatio[0]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO2,GearRatio[1]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO3,GearRatio[2]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO4,GearRatio[3]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO5,GearRatio[4]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_GEARRATIO6,GearRatio[5]);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_FINALDRIVEGEARRATIO,FinalDriveGearRatio);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_SHIFTUPRPM,ShiftUpRpm);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_SHIFTDOWNRPM,ShiftDownRpm);
- WRITE_MICRO_CHUNK(csave,MOTORVEHICLEDEF_VARIABLE_DRIVETRAININERTIA,DriveTrainInertia);
- csave.End_Chunk();
- return true;
- }
- bool MotorVehicleDefClass::Load(ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk()) {
- switch(cload.Cur_Chunk_ID()) {
- case MOTORVEHICLEDEF_CHUNK_RIGIDBODYDEF: // old parent class
- RigidBodyDefClass::Load(cload);
- break;
- case MOTORVEHICLEDEF_CHUNK_VEHICLEPHYSDEF: // current parent class
- VehiclePhysDefClass::Load(cload);
- break;
- case MOTORVEHICLEDEF_CHUNK_VARIABLES:
- while (cload.Open_Micro_Chunk()) {
- switch(cload.Cur_Micro_Chunk_ID()) {
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_MAXENGINETORQUE,MaxEngineTorque);
- READ_MICRO_CHUNK_WWSTRING(cload,MOTORVEHICLEDEF_VARIABLE_ENGINETORQUECURVEFILENAME,EngineTorqueCurveFilename);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARCOUNT,GearCount);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO1,GearRatio[0]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO2,GearRatio[1]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO3,GearRatio[2]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO4,GearRatio[3]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO5,GearRatio[4]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_GEARRATIO6,GearRatio[5]);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_FINALDRIVEGEARRATIO,FinalDriveGearRatio);
- OBSOLETE_MICRO_CHUNK(OBSOLETE_MOTORVEHICLEDEF_VARIABLE_ENGINEINERTIA);
- OBSOLETE_MICRO_CHUNK(OBSOLETE_MOTORVEHICLEDEF_VARIABLE_TRANSMISSIONINERTIA);
- OBSOLETE_MICRO_CHUNK(OBSOLETE_MOTORVEHICLEDEF_VARIABLE_FINALDRIVEINERTIA);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_SHIFTUPRPM,ShiftUpRpm);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_SHIFTDOWNRPM,ShiftDownRpm);
- READ_MICRO_CHUNK(cload,MOTORVEHICLEDEF_VARIABLE_DRIVETRAININERTIA,DriveTrainInertia);
- }
- cload.Close_Micro_Chunk();
- }
- break;
- default:
- WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
- break;
- }
- cload.Close_Chunk();
- }
- ShiftUpAvel = RPM_TO_RADS(ShiftUpRpm);
- ShiftDownAvel = RPM_TO_RADS(ShiftDownRpm);
- REF_PTR_RELEASE(EngineTorqueCurve);
- if (!EngineTorqueCurveFilename.Is_Empty()) {
- // strip the path off the filename
- char * fname = strrchr(EngineTorqueCurveFilename,'\\');
- if (fname == NULL) {
- EngineTorqueCurve = LookupTableMgrClass::Get_Table(EngineTorqueCurveFilename);
- } else {
- EngineTorqueCurve = LookupTableMgrClass::Get_Table(fname + 1);
- }
- }
- if (EngineTorqueCurve == NULL) {
- WWDEBUG_SAY(("Missing EngineTorqueCurve Table file: %s\r\n",EngineTorqueCurveFilename));
- EngineTorqueCurve = LookupTableMgrClass::Get_Table("DefaultTable");
- }
- return true;
- }
- bool MotorVehicleDefClass::Is_Type(const char * type_name)
- {
- if (stricmp(type_name,MotorVehicleDefClass::Get_Type_Name()) == 0) {
- return true;
- } else {
- return VehiclePhysDefClass::Is_Type(type_name);
- }
- }
|