/* ** 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 . */ #ifndef RBODY_H #define RBODY_H #include "always.h" #include "movephys.h" #include "ode.h" #include "aabox.h" #include "quat.h" #include "physcon.h" #include "wwdebug.h" class RigidBodyDefClass; class OBBoxRenderObjClass; class PaddedBoxClass; class OctBoxClass; class RBodyHistoryClass; struct CastResultStruct; const int MAX_CONTACT_POINTS = 10; const float RBODY_SLEEP_DELAY = 0.5f; // if still for this many seconds, shut off simulation /** ** RigidBodyStateStruct ** This is the state vector for a RigidBodyClass. */ struct RigidBodyStateStruct { RigidBodyStateStruct(void) {} RigidBodyStateStruct(const RigidBodyStateStruct & that); RigidBodyStateStruct & operator = (const RigidBodyStateStruct & that); void Reset(void); void To_Vector(StateVectorClass & vec); int From_Vector(const StateVectorClass & vec,int index); static void Lerp(const RigidBodyStateStruct & s0,const RigidBodyStateStruct & s1,float fraction,RigidBodyStateStruct * res); bool Is_Valid(void) { return (Position.Is_Valid() && Orientation.Is_Valid() && LMomentum.Is_Valid() && AMomentum.Is_Valid()); } Vector3 Position; Quaternion Orientation; Vector3 LMomentum; Vector3 AMomentum; }; /** ** RigidBodyClass ** Simulation of a rigid body. The shape is limited to be an oriented box. */ class RigidBodyClass : public MoveablePhysClass , public ODESystemClass { public: RigidBodyClass(void); virtual ~RigidBodyClass(void); virtual RigidBodyClass * As_RigidBodyClass(void) { return this; } RigidBodyDefClass * Get_RigidBodyDef(void) { return (RigidBodyDefClass *)Definition; } void Init(const RigidBodyDefClass & definition); virtual const AABoxClass & Get_Bounding_Box(void) const; virtual const Matrix3D & Get_Transform(void) const; virtual void Set_Transform(const Matrix3D & m); virtual bool Cast_Ray(PhysRayCollisionTestClass & raytest); virtual bool Cast_AABox(PhysAABoxCollisionTestClass & boxtest); virtual bool Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest); virtual bool Intersection_Test(PhysAABoxIntersectionTestClass & test); virtual bool Intersection_Test(PhysOBBoxIntersectionTestClass & test); virtual bool Intersection_Test(PhysMeshIntersectionTestClass & test); /* ** Teleport support */ virtual bool Can_Teleport(const Matrix3D &new_tm, bool check_dyn_only = false,NonRefPhysListClass * result_list = NULL); virtual bool Can_Teleport_And_Stand(const Matrix3D &new_tm, Matrix3D *out); /* ** State */ void Get_Orientation(Quaternion * set_q) const { *set_q = State.Orientation; } virtual void Get_Velocity(Vector3 * set_vel) const; virtual void Get_Angular_Velocity(Vector3 * set_avel) const; virtual void Set_Velocity(const Vector3 & newvel); virtual void Set_Angular_Velocity(const Vector3 & newavel); virtual void Apply_Impulse(const Vector3 & imp); virtual void Apply_Impulse(const Vector3 & imp, const Vector3 & wpos); virtual void Timestep(float dt); void Compute_Point_Velocity(const Vector3 & p,Vector3 * pdot); float Get_Last_Timestep(void) { return LastTimestep; } /* ** Networking code. ** Network_Interpolate_State_Update - interpolate towards the given state ** Network_Latency_State_Update - client player update, interpolate, considering latency. */ void Network_Interpolate_State_Update( const Vector3 & new_pos, const Quaternion & new_orientation, const Vector3 & new_vel, const Vector3 & new_avel, float blend_fraction); void Network_Latency_State_Update( const Vector3 & new_pos, const Quaternion & new_orientation, const Vector3 & new_vel, const Vector3 & new_avel); static void Set_Correction_Time(float time) { _CorrectionTime = time; } static float Get_Correction_Time(void) { return _CorrectionTime; } /* ** Properties */ virtual void Set_Model(RenderObjClass * model); virtual void Set_Mass(float mass); virtual void Get_Inertia_Inv(Matrix3 * set_I_inv); void Set_Inertia(const Matrix3 & I); void Get_Inertia(Matrix3 * I); void Set_Contact_Parameters(float length); void Get_Contact_Parameters(float * stiffness,float * damping,float * length); float Get_Weight(void) { return GravScale * Mass * (-PhysicsConstants::GravityAcceleration.Z); } /* ** Shadow support */ virtual void Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box); /* ** MoveablePhysClass Push interface. RigidBody's try to move when they are pushed. */ virtual bool Push(const Vector3 & move); /* ** Save-Load System */ virtual const PersistFactoryClass & Get_Factory (void) const; virtual bool Save (ChunkSaveClass &csave); virtual bool Load (ChunkLoadClass &cload); virtual void On_Post_Load(void); protected: /* ** Integration system */ virtual void Get_State(StateVectorClass & set_state); virtual int Set_State(const StateVectorClass & new_state,int start_index); void Set_State(const RigidBodyStateStruct & new_state); virtual int Compute_Derivatives(float t,StateVectorClass * test_state,StateVectorClass * set_derivs,int start_index); void Integrate(float time); /* ** Internal functions */ void Update_Cached_Model_Parameters(void); virtual void Compute_Inertia(void); void Update_Auxiliary_State(void); virtual void Compute_Force_And_Torque(Vector3 * force,Vector3 * torque); void Compute_Impact(const CastResultStruct & result,Vector3 * impulse); void Compute_Impact(const Vector3 & point,const Vector3 & normal,Vector3 * impulse); bool Is_Colliding(const Vector3 & point, const Vector3 & normal); void Set_Moving_Collision_Region(float dt); void Set_Stationary_Collision_Region(void); virtual bool Can_Go_To_Sleep(float dt); bool Find_State_In_Contact_Zone(const RigidBodyStateStruct & oldstate); bool Push_Phys3_Object_Away(Phys3Class * p3obj,const CastResultStruct & contact); void Network_Latency_Error_Correction(float dt); void Assert_State_Valid(void) const; void Assert_Not_Intersecting(void); void Dump_State(void) const; /* ** Physical Description: */ OBBoxRenderObjClass * Box; // World-Space Box Matrix3 IBody; // Body space inertia tensor Matrix3 IBodyInv; // Inverse of the body space inertia tensor OctBoxClass * ContactBox; // datastructure for finding contact points float ContactThickness; // Dimension of the contact region /* ** State vector */ RigidBodyStateStruct State; // current state vector /* ** Quantities derived from (dependent on) the current state */ Matrix3 Rotation; Matrix3 IInv; Vector3 Velocity; Vector3 AngularVelocity; /* ** Other Member variables */ bool IsInContact; int ContactCount; Vector3 ContactPoint[MAX_CONTACT_POINTS]; Vector3 ContactNormal; int StickCount; float LastTimestep; float GoToSleepTimer; /* ** Network history for latency handling */ RBodyHistoryClass * History; RigidBodyStateStruct LatencyError; RigidBodyStateStruct LastKnownState; static float _CorrectionTime; private: // not implemented RigidBodyClass(const RigidBodyClass &); RigidBodyClass & operator = (const RigidBodyClass &); }; inline RigidBodyStateStruct::RigidBodyStateStruct(const RigidBodyStateStruct & that) { *this = that; } inline RigidBodyStateStruct & RigidBodyStateStruct::operator = (const RigidBodyStateStruct & that) { Position = that.Position; Orientation = that.Orientation; LMomentum = that.LMomentum; AMomentum = that.AMomentum; return *this; } inline void RigidBodyStateStruct::Reset(void) { Position.Set(0,0,0); Orientation.Make_Identity(); LMomentum.Set(0,0,0); AMomentum.Set(0,0,0); } inline void RigidBodyStateStruct::To_Vector(StateVectorClass & vec) { vec.Add(Position[0]); vec.Add(Position[1]); vec.Add(Position[2]); vec.Add(Orientation[0]); vec.Add(Orientation[1]); vec.Add(Orientation[2]); vec.Add(Orientation[3]); vec.Add(LMomentum[0]); vec.Add(LMomentum[1]); vec.Add(LMomentum[2]); vec.Add(AMomentum[0]); vec.Add(AMomentum[1]); vec.Add(AMomentum[2]); } inline int RigidBodyStateStruct::From_Vector(const StateVectorClass & vec,int index) { Position[0] = vec[index++]; Position[1] = vec[index++]; Position[2] = vec[index++]; Orientation[0] = vec[index++]; Orientation[1] = vec[index++]; Orientation[2] = vec[index++]; Orientation[3] = vec[index++]; LMomentum[0] = vec[index++]; LMomentum[1] = vec[index++]; LMomentum[2] = vec[index++]; AMomentum[0] = vec[index++]; AMomentum[1] = vec[index++]; AMomentum[2] = vec[index++]; return index; } inline void RigidBodyStateStruct::Lerp ( const RigidBodyStateStruct & s0, const RigidBodyStateStruct & s1, float fraction, RigidBodyStateStruct * res ) { Vector3::Lerp(s0.Position,s1.Position,fraction,&(res->Position)); Vector3::Lerp(s0.LMomentum,s1.LMomentum,fraction,&(res->LMomentum)); Vector3::Lerp(s0.AMomentum,s1.AMomentum,fraction,&(res->AMomentum)); ::Fast_Slerp(res->Orientation,s0.Orientation,s1.Orientation,fraction); } /** ** RigidBodyDefClass ** Initialization data structure for RigidBodyClass */ class RigidBodyDefClass : public MoveablePhysDefClass { public: RigidBodyDefClass(void); // From DefinitionClass virtual uint32 Get_Class_ID (void) const; virtual PersistClass * Create(void) const; // From PhysDefClass virtual const char * Get_Type_Name(void) { return "RigidBodyDef"; } 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); // In-Game Editing (DEBUGGING/TESTING ONLY) float Get_Aerodynamic_Drag(void) { return AerodynamicDragCoefficient; } void Set_Aerodynamic_Drag(float new_drag) { AerodynamicDragCoefficient = new_drag; } // Editable interface requirements DECLARE_EDITABLE(RigidBodyDefClass,MoveablePhysDefClass); protected: // Variables float AerodynamicDragCoefficient; bool CollisionDisabled; friend class RigidBodyClass; }; #endif