361 lines
No EOL
11 KiB
C++
361 lines
No EOL
11 KiB
C++
/*
|
|
** 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/>.
|
|
*/
|
|
|
|
#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 |