/* ** 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 . */ /*********************************************************************************************** *** 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.h $* * * * Author:: Greg Hjelstrom * * * * $Modtime:: 10/22/01 5:52p $* * * * $Revision:: 18 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #if defined(_MSC_VER) #pragma once #endif #ifndef MOTORVEHICLE_H #define MOTORVEHICLE_H #include "vehiclephys.h" class MotorVehicleDefClass; class LookupTableClass; #define RPM_TO_RADS(x) ((x) * (2.0f * WWMATH_PI) / 60.0f) #define RADS_TO_RPM(x) ((x) * 60.0f / (2.0f * WWMATH_PI)) /** ** MotorVehicleClass ** This is simply a RigidBodyClass with an internal "engine". In derived ** classes, the engine will propel the object in some way. ** ** NOTES: ** March 13,2000: ** -------------- ** Adding an engine model which simulates the interaction between the engine speeding up ** and the wheel forces resisting, etc. The goal is to get the following behaviours: ** - when wheels aren't contacting the ground, the engine revs up ** - if the engine is revved up and the wheels come in contact, there is a reaction force ** - wheels can "peel out" in low gears if the parameters are set correctly ** - we have a parameter (EngineAngularVelocity) which can nicely drive a realistic engine sound ** ** To do this, I've added an EngineTorqueCurve which relates the rpm's of the engine ** to the torque it can output. This is transmitted to the drive wheels (equally distributed ** between them). The drive wheels "use up" as much of this torque as they can through ** friction forces on the ground. The net result of this is the amount that the engine ** angular velocity accelerates. In first gear for some vehicles, I'd like the engine to ** normally overpower friction and peel out but tune second and above to stay within the ** friction limits. ** ** (EngineTorque(EngineAngularVel) - ReactionTorque) / DriveTrainInertia = EngineAngularAccel ** ReactionTorque = MIN(EngineTorque,FrictionLimit) ** At higher speeds, something (DriveTrainInertia?) must bring EngineTorque down below ** FrictionLimit or we'll be peeling out again! ** Maybe just fake this: if (gear>1) roll wheels according to actual movement ** ** March 21, 2000: ** Driving engine RPMs the other direction. Normally, set the engine rpm to match the ** actual wheel speed. */ class MotorVehicleClass : public VehiclePhysClass { public: MotorVehicleClass(void); virtual ~MotorVehicleClass(void); virtual MotorVehicleClass * As_MotorVehicleClass(void) { return this; } void Init(const MotorVehicleDefClass & def); virtual void Timestep(float dt); // Accessors for the current state float Get_Engine_Angular_Velocity(void) { return EngineAngularVelocity; } float Get_Engine_RPM(void) { return RADS_TO_RPM(EngineAngularVelocity); } float Get_Engine_Torque(void); float Get_Axle_Angular_Velocity(void); float Get_Axle_Torque(void); float Get_Normalized_Engine_RPM(void); // for sound effects int Get_Current_Gear(void) { return CurrentGear; } bool Is_Braking(void) { return IsBraking; } // Access to the constants for this motor vehicle const MotorVehicleDefClass * Get_MotorVehicleDef(void); float Get_Max_Engine_Torque(void); // Save-Load system virtual bool Save(ChunkSaveClass &csave); virtual bool Load(ChunkLoadClass &cload); protected: // Actual vehicles must provide these methods virtual float Get_Ideal_Drive_Axle_Angular_Velocity(void) = 0; virtual bool Drive_Wheels_In_Contact(void) = 0; // Integration system virtual int Set_State(const StateVectorClass & new_state,int start_index); // Internal engine simulation methods float Compute_Engine_Angular_Acceleration(void); void Shift_Up(void); void Shift_Down(void); // Variables float EngineAngularVelocity; // This is a state variable to be integrated with the others int CurrentGear; float ShiftTimer; // delay in seconds between allowed shifting float AcceleratorFraction; // amount of gas being given to the engine bool IsBraking; // braking state private: // Not implemented.. MotorVehicleClass(const MotorVehicleClass &); MotorVehicleClass & operator = (const MotorVehicleClass &); }; inline const MotorVehicleDefClass * MotorVehicleClass::Get_MotorVehicleDef(void) { // All motor vehicles have to have definitions... Most of their parameters are only // stored in the definition now... WWASSERT(Definition != NULL); return (MotorVehicleDefClass *)Definition; } /** ** MotorVehicleDefClass ** Initialization Structure/Factory/Editor Integration for a MotorVehicleClass */ class MotorVehicleDefClass : public VehiclePhysDefClass { public: MotorVehicleDefClass(void); virtual ~MotorVehicleDefClass(void); // From PersistClass virtual uint32 Get_Class_ID (void) const; // From PhysDefClass virtual const char * Get_Type_Name(void) { return "MotorVehicleDef"; } virtual bool Is_Type(const char *); // From PersistClass virtual const PersistFactoryClass & Get_Factory (void) const; virtual bool Save(ChunkSaveClass &csave); virtual bool Load(ChunkLoadClass &cload); // Read/Write access (DEBUGGING/TESTING ONLY) float Get_Max_Engine_Torque(void) const { return MaxEngineTorque; } int Get_Gear_Count(void) const { return GearCount; } float Get_Gear_Ratio(int gear) const { return GearRatio[gear]; } float Get_Shift_Up_Rpm(void) const { return ShiftUpRpm; } float Get_Shift_Down_Rpm(void) const { return ShiftDownRpm; } void Set_Max_Engine_Torque(float t) { MaxEngineTorque = t; } void Set_Gear_Count(int count) { if ((count >= 0) && (count <= 5)) GearCount = count; } void Set_Gear_Ratio(int gear,float ratio) { if ((gear >= 0) && (gear <= 5))GearRatio[gear] = ratio; } void Set_Shift_Up_Rpm(float rpm) { ShiftUpRpm = rpm; ShiftUpAvel = RPM_TO_RADS(ShiftUpRpm); } void Set_Shift_Down_Rpm(float rpm) { ShiftDownRpm = rpm; ShiftDownAvel = RPM_TO_RADS(ShiftDownRpm); } // Editable interface requirements DECLARE_EDITABLE(MotorVehicleDefClass,VehiclePhysDefClass); protected: // Engine response float MaxEngineTorque; StringClass EngineTorqueCurveFilename; LookupTableClass * EngineTorqueCurve; // pointer to the torque curve // Transmission and Differential Gear Ratios enum { MAX_GEAR_COUNT = 6 }; int GearCount; float GearRatio[MAX_GEAR_COUNT]; float PeakEngineTorque[MAX_GEAR_COUNT]; float FinalDriveGearRatio; // Inertias in the drive train float DriveTrainInertia; // Shifting variables float ShiftUpRpm; float ShiftDownRpm; float ShiftUpAvel; // same as ShiftUpRpm but in radians per second float ShiftDownAvel; // same as ShiftDownRpm but in radians per second friend class MotorVehicleClass; }; #endif