You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1606 lines
55 KiB
1606 lines
55 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#ifdef _WIN32 |
|
#pragma warning (disable:4127) |
|
#pragma warning (disable:4244) |
|
#endif |
|
|
|
#include "cbase.h" |
|
#include "ivp_controller.hxx" |
|
#include "ivp_cache_object.hxx" |
|
#include "ivp_car_system.hxx" |
|
#include "ivp_constraint_car.hxx" |
|
#include "ivp_material.hxx" |
|
|
|
#include "vphysics/vehicles.h" |
|
#include "vphysics/friction.h" |
|
#include "physics_vehicle.h" |
|
#include "physics_controller_raycast_vehicle.h" |
|
#include "physics_airboat.h" |
|
#include "ivp_car_system.hxx" |
|
#include "ivp_listener_object.hxx" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#define THROTTLE_OPPOSING_FORCE_EPSILON 5.0f |
|
#define VEHICLE_SKID_EPSILON 0.1f |
|
|
|
// y in/s = x miles/hour * (5280 * 12 (in / mile)) * (1 / 3600 (hour / sec) ) |
|
//#define MPH2INS(x) ( (x) * 5280.0f * 12.0f / 3600.0f ) |
|
//#define INS2MPH(x) ( (x) * 3600 * (1/5280.0f) * (1/12.0f) ) |
|
|
|
#define MPH_TO_METERSPERSECOND 0.44707f |
|
#define METERSPERSECOND_TO_MPH (1.0f / MPH_TO_METERSPERSECOND) |
|
|
|
#define MPH_TO_GAMEVEL(x) (ConvertDistanceToHL( (x) * MPH_TO_METERSPERSECOND )) |
|
#define GAMEVEL_TO_MPH(x) (ConvertDistanceToIVP(x) * METERSPERSECOND_TO_MPH) |
|
|
|
|
|
#define FVEHICLE_THROTTLE_STOPPED 0x00000001 |
|
#define FVEHICLE_HANDBRAKE_ON 0x00000002 |
|
|
|
struct vphysics_save_cvehiclecontroller_t |
|
{ |
|
CPhysicsObject *m_pCarBody; |
|
int m_wheelCount; |
|
vehicleparams_t m_vehicleData; |
|
vehicle_operatingparams_t m_currentState; |
|
float m_wheelRadius; |
|
float m_bodyMass; |
|
float m_totalWheelMass; |
|
float m_gravityLength; |
|
float m_torqueScale; |
|
CPhysicsObject *m_pWheels[VEHICLE_MAX_WHEEL_COUNT]; |
|
Vector m_wheelPosition_Bs[VEHICLE_MAX_WHEEL_COUNT]; |
|
Vector m_tracePosition_Bs[VEHICLE_MAX_WHEEL_COUNT]; |
|
int m_vehicleFlags; |
|
unsigned int m_nTireType; |
|
unsigned int m_nVehicleType; |
|
bool m_bTraceData; |
|
bool m_bOccupied; |
|
bool m_bEngineDisable; |
|
float m_flVelocity[3]; |
|
|
|
DECLARE_SIMPLE_DATADESC(); |
|
}; |
|
|
|
|
|
BEGIN_SIMPLE_DATADESC( vphysics_save_cvehiclecontroller_t ) |
|
DEFINE_VPHYSPTR( m_pCarBody ), |
|
DEFINE_FIELD( m_wheelCount, FIELD_INTEGER ), |
|
DEFINE_EMBEDDED( m_vehicleData ), |
|
DEFINE_EMBEDDED( m_currentState ), |
|
DEFINE_FIELD( m_wheelCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bodyMass, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_totalWheelMass, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_gravityLength, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_torqueScale, FIELD_FLOAT ), |
|
|
|
DEFINE_VPHYSPTR_ARRAY( m_pWheels, VEHICLE_MAX_WHEEL_COUNT ), |
|
DEFINE_ARRAY( m_wheelPosition_Bs, FIELD_VECTOR, VEHICLE_MAX_WHEEL_COUNT ), |
|
DEFINE_ARRAY( m_tracePosition_Bs, FIELD_VECTOR, VEHICLE_MAX_WHEEL_COUNT ), |
|
DEFINE_FIELD( m_vehicleFlags, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_nTireType, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_nVehicleType, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bTraceData, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bOccupied, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bEngineDisable, FIELD_BOOLEAN ), |
|
DEFINE_ARRAY( m_flVelocity, FIELD_FLOAT, 3 ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_operatingparams_t ) |
|
DEFINE_FIELD( speed, FIELD_FLOAT ), |
|
DEFINE_FIELD( engineRPM, FIELD_FLOAT ), |
|
DEFINE_FIELD( gear, FIELD_INTEGER ), |
|
DEFINE_FIELD( boostDelay, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostTimeLeft, FIELD_INTEGER ), |
|
DEFINE_FIELD( skidSpeed, FIELD_FLOAT ), |
|
DEFINE_CUSTOM_FIELD( skidMaterial, MaterialIndexDataOps() ), |
|
DEFINE_FIELD( steeringAngle, FIELD_FLOAT ), |
|
DEFINE_FIELD( wheelsInContact, FIELD_INTEGER ), |
|
DEFINE_FIELD( wheelsNotInContact,FIELD_INTEGER ), |
|
DEFINE_FIELD( isTorqueBoosting, FIELD_BOOLEAN ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_bodyparams_t ) |
|
DEFINE_FIELD( massCenterOverride, FIELD_VECTOR ), |
|
DEFINE_FIELD( massOverride, FIELD_FLOAT ), |
|
DEFINE_FIELD( addGravity, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxAngularVelocity, FIELD_FLOAT ), |
|
DEFINE_FIELD( tiltForce, FIELD_FLOAT ), |
|
DEFINE_FIELD( tiltForceHeight, FIELD_FLOAT ), |
|
DEFINE_FIELD( counterTorqueFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( keepUprightTorque, FIELD_FLOAT ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_wheelparams_t ) |
|
DEFINE_FIELD( radius, FIELD_FLOAT ), |
|
DEFINE_FIELD( mass, FIELD_FLOAT ), |
|
DEFINE_FIELD( inertia, FIELD_FLOAT ), |
|
DEFINE_FIELD( damping, FIELD_FLOAT ), |
|
DEFINE_FIELD( rotdamping, FIELD_FLOAT ), |
|
DEFINE_FIELD( frictionScale, FIELD_FLOAT ), |
|
DEFINE_CUSTOM_FIELD( materialIndex, MaterialIndexDataOps() ), |
|
DEFINE_CUSTOM_FIELD( brakeMaterialIndex, MaterialIndexDataOps() ), |
|
DEFINE_CUSTOM_FIELD( skidMaterialIndex, MaterialIndexDataOps() ), |
|
DEFINE_FIELD( springAdditionalLength, FIELD_FLOAT ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_suspensionparams_t ) |
|
DEFINE_FIELD( springConstant, FIELD_FLOAT ), |
|
DEFINE_FIELD( springDamping, FIELD_FLOAT ), |
|
DEFINE_FIELD( stabilizerConstant, FIELD_FLOAT ), |
|
DEFINE_FIELD( springDampingCompression, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxBodyForce, FIELD_FLOAT ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_axleparams_t ) |
|
DEFINE_FIELD( offset, FIELD_VECTOR ), |
|
DEFINE_FIELD( wheelOffset, FIELD_VECTOR ), |
|
DEFINE_FIELD( raytraceCenterOffset, FIELD_VECTOR ), |
|
DEFINE_FIELD( raytraceOffset, FIELD_VECTOR ), |
|
DEFINE_EMBEDDED( wheels ), |
|
DEFINE_EMBEDDED( suspension ), |
|
DEFINE_FIELD( torqueFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( brakeFactor, FIELD_FLOAT ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_steeringparams_t ) |
|
DEFINE_FIELD( degreesSlow, FIELD_FLOAT ), |
|
DEFINE_FIELD( degreesFast, FIELD_FLOAT ), |
|
DEFINE_FIELD( degreesBoost, FIELD_FLOAT ), |
|
DEFINE_FIELD( steeringRateSlow, FIELD_FLOAT ), |
|
DEFINE_FIELD( steeringRateFast, FIELD_FLOAT ), |
|
DEFINE_FIELD( steeringRestRateSlow, FIELD_FLOAT ), |
|
DEFINE_FIELD( steeringRestRateFast, FIELD_FLOAT ), |
|
DEFINE_FIELD( throttleSteeringRestRateFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostSteeringRestRateFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostSteeringRateFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( steeringExponent, FIELD_FLOAT ), |
|
DEFINE_FIELD( speedSlow, FIELD_FLOAT ), |
|
DEFINE_FIELD( speedFast, FIELD_FLOAT ), |
|
DEFINE_FIELD( turnThrottleReduceSlow, FIELD_FLOAT ), |
|
DEFINE_FIELD( turnThrottleReduceFast, FIELD_FLOAT ), |
|
DEFINE_FIELD( powerSlideAccel, FIELD_FLOAT ), |
|
DEFINE_FIELD( brakeSteeringRateFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( isSkidAllowed, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( dustCloud, FIELD_BOOLEAN ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicle_engineparams_t ) |
|
DEFINE_FIELD( horsepower, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxSpeed, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxRevSpeed, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxRPM, FIELD_FLOAT ), |
|
DEFINE_FIELD( axleRatio, FIELD_FLOAT ), |
|
DEFINE_FIELD( throttleTime, FIELD_FLOAT ), |
|
DEFINE_FIELD( maxRPM, FIELD_FLOAT ), |
|
DEFINE_FIELD( isAutoTransmission, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( gearCount, FIELD_INTEGER ), |
|
DEFINE_AUTO_ARRAY( gearRatio, FIELD_FLOAT ), |
|
DEFINE_FIELD( shiftUpRPM, FIELD_FLOAT ), |
|
DEFINE_FIELD( shiftDownRPM, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostForce, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostDuration, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostDelay, FIELD_FLOAT ), |
|
DEFINE_FIELD( boostMaxSpeed, FIELD_FLOAT ), |
|
DEFINE_FIELD( autobrakeSpeedGain, FIELD_FLOAT ), |
|
DEFINE_FIELD( autobrakeSpeedFactor, FIELD_FLOAT ), |
|
DEFINE_FIELD( torqueBoost, FIELD_BOOLEAN ), |
|
END_DATADESC() |
|
|
|
BEGIN_SIMPLE_DATADESC( vehicleparams_t ) |
|
DEFINE_FIELD( axleCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( wheelsPerAxle, FIELD_INTEGER ), |
|
DEFINE_EMBEDDED( body ), |
|
DEFINE_EMBEDDED_AUTO_ARRAY( axles ), |
|
DEFINE_EMBEDDED( engine ), |
|
DEFINE_EMBEDDED( steering ), |
|
END_DATADESC() |
|
|
|
|
|
bool IsVehicleWheel( IVP_Real_Object *pivp ) |
|
{ |
|
CPhysicsObject *pObject = static_cast<CPhysicsObject *>(pivp->client_data); |
|
|
|
// FIXME: Check why this is null! It occurs when jumping the ravine in seafloor |
|
if (!pObject) |
|
return false; |
|
|
|
return (pObject->CallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL) ? true : false; |
|
} |
|
|
|
inline bool IsMoveable( IVP_Real_Object *pObject ) |
|
{ |
|
IVP_Core *pCore = pObject->get_core(); |
|
if ( pCore->pinned || pCore->physical_unmoveable ) |
|
return false; |
|
return true; |
|
} |
|
|
|
inline bool IVPFloatPointIsZero( const IVP_U_Float_Point &test ) |
|
{ |
|
const float eps = 1e-4f; |
|
return test.quad_length() < eps ? true : false; |
|
} |
|
|
|
bool ShouldOverrideWheelContactFriction( float *pFrictionOut, IVP_Real_Object *pivp0, IVP_Real_Object *pivp1, IVP_U_Float_Point *pNormal ) |
|
{ |
|
if ( !pivp0->get_core()->car_wheel && !pivp1->get_core()->car_wheel ) |
|
return false; |
|
|
|
if ( !IsVehicleWheel(pivp0) ) |
|
{ |
|
if ( !IsVehicleWheel(pivp1) ) |
|
return false; |
|
// swap so pivp0 is a wheel |
|
IVP_Real_Object *pTmp = pivp0; |
|
pivp0 = pivp1; |
|
pivp1 = pTmp; |
|
} |
|
|
|
// if we got here then pivp0 is a car wheel object |
|
// BUGBUG: IVP sometimes sends us a bogus normal |
|
// when doing a material realc on existing contacts! |
|
if ( !IVPFloatPointIsZero(pNormal) ) |
|
{ |
|
IVP_U_Float_Point normalWheelSpace; |
|
pivp0->get_core()->get_m_world_f_core_PSI()->vimult3( pNormal, &normalWheelSpace ); |
|
if ( fabs(normalWheelSpace.k[0]) > 0.2588f ) // 15 degree wheel cone |
|
{ |
|
// adjust friction here, this isn't a valid part of the wheel for contact, set friction to zero |
|
//Vector tmp;ConvertDirectionToHL( normalWheelSpace, tmp );Msg("Wheel sliding on surface %.2f %.2f %.2f\n", tmp.x, tmp.y, tmp.z ); |
|
*pFrictionOut = 0; |
|
return true; |
|
} |
|
} |
|
// was car wheel, but didn't adjust - use default friction |
|
return false; |
|
} |
|
|
|
|
|
class CVehicleController : public IPhysicsVehicleController, public IVP_Listener_Object |
|
{ |
|
public: |
|
CVehicleController( ); |
|
CVehicleController( const vehicleparams_t ¶ms, CPhysicsEnvironment *pEnv, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace ); |
|
~CVehicleController(); |
|
|
|
// CVehicleController |
|
void InitCarSystem( CPhysicsObject *pBodyObject ); |
|
|
|
// IPhysicsVehicleController |
|
void Update( float dt, vehicle_controlparams_t &controls ); |
|
float UpdateBooster( float dt ); |
|
void SetSpringLength(int wheelIndex, float length); |
|
const vehicle_operatingparams_t &GetOperatingParams() { return m_currentState; } |
|
const vehicleparams_t &GetVehicleParams() { return m_vehicleData; } |
|
vehicleparams_t &GetVehicleParamsForChange() { return m_vehicleData; } |
|
int GetWheelCount(void) { return m_wheelCount; }; |
|
IPhysicsObject* GetWheel(int index); |
|
virtual bool GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps ); |
|
void SetWheelFriction(int wheelIndex, float friction); |
|
|
|
void SetEngineDisabled( bool bDisable ) { m_bEngineDisable = bDisable; } |
|
bool IsEngineDisabled( void ) { return m_bEngineDisable; } |
|
|
|
// Save/load |
|
void WriteToTemplate( vphysics_save_cvehiclecontroller_t &controllerTemplate ); |
|
void InitFromTemplate( CPhysicsEnvironment *pEnv, void *pGameData, IPhysicsGameTrace *pGameTrace, const vphysics_save_cvehiclecontroller_t &controllerTemplate ); |
|
void VehicleDataReload(); |
|
|
|
// Debug |
|
void GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem ); |
|
|
|
// IVP_Listener_Object |
|
// Object listener, only hook delete |
|
virtual void event_object_deleted( IVP_Event_Object *); |
|
virtual void event_object_created( IVP_Event_Object *) {} |
|
virtual void event_object_revived( IVP_Event_Object *) {} |
|
virtual void event_object_frozen ( IVP_Event_Object *) {} |
|
|
|
// Entry/Exit |
|
void OnVehicleEnter( void ); |
|
void OnVehicleExit( void ); |
|
|
|
protected: |
|
void CreateIVPObjects( ); |
|
void ShutdownCarSystem(); |
|
|
|
void InitVehicleData( const vehicleparams_t ¶ms ); |
|
void InitCarSystemBody( IVP_Template_Car_System &ivpVehicleData ); |
|
void InitCarSystemWheels( IVP_Template_Car_System &ivpVehicleData ); |
|
|
|
void AttachListener(); |
|
|
|
IVP_Real_Object *CreateWheel( int wheelIndex, vehicle_axleparams_t &axle ); |
|
void CreateTraceData( int wheelIndex, vehicle_axleparams_t &axle ); |
|
|
|
// Update. |
|
void UpdateSteering( const vehicle_controlparams_t &controls, float flDeltaTime, float flSpeed ); |
|
void UpdatePowerslide( const vehicle_controlparams_t &controls, bool bPowerslide, float flSpeed ); |
|
void UpdateEngine( const vehicle_controlparams_t &controls, float flDeltaTime, float flThrottle, float flBrake, bool bHandbrake, bool bPowerslide ); |
|
bool UpdateEngineTurboStart( const vehicle_controlparams_t &controls, float flDeltaTime ); |
|
void UpdateEngineTurboFinish( void ); |
|
void UpdateHandbrake( const vehicle_controlparams_t &controls, float flThrottle, bool bHandbrake, bool bPowerslide ); |
|
void UpdateSkidding( bool bHandbrake ); |
|
void UpdateExtraForces( void ); |
|
void UpdateWheelPositions( void ); |
|
float CalcSteering( float dt, float speed, float steering, bool bAnalog ); |
|
void CalcEngine( float throttle, float brake_val, bool handbrake, float steeringVal, bool torqueBoost ); |
|
void CalcEngineTransmission( float flThrottle ); |
|
|
|
virtual bool IsBoosting( void ); |
|
|
|
private: |
|
void ResetState(); |
|
IVP_Car_System *m_pCarSystem; |
|
CPhysicsObject *m_pCarBody; |
|
CPhysicsEnvironment *m_pEnv; |
|
IPhysicsGameTrace *m_pGameTrace; |
|
int m_wheelCount; |
|
vehicleparams_t m_vehicleData; |
|
vehicle_operatingparams_t m_currentState; |
|
float m_wheelRadius; |
|
float m_bodyMass; |
|
float m_totalWheelMass; |
|
float m_gravityLength; |
|
float m_torqueScale; |
|
CPhysicsObject *m_pWheels[VEHICLE_MAX_WHEEL_COUNT]; |
|
IVP_U_Float_Point m_wheelPosition_Bs[VEHICLE_MAX_WHEEL_COUNT]; |
|
IVP_U_Float_Point m_tracePosition_Bs[VEHICLE_MAX_WHEEL_COUNT]; |
|
int m_vehicleFlags; |
|
unsigned int m_nTireType; |
|
unsigned int m_nVehicleType; |
|
bool m_bTraceData; |
|
bool m_bOccupied; |
|
bool m_bEngineDisable; |
|
float m_flVelocity[3]; |
|
}; |
|
|
|
CVehicleController::CVehicleController( const vehicleparams_t ¶ms, CPhysicsEnvironment *pEnv, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace ) |
|
{ |
|
m_pEnv = pEnv; |
|
m_pGameTrace = pGameTrace; |
|
m_nVehicleType = nVehicleType; |
|
InitVehicleData( params ); |
|
ResetState(); |
|
} |
|
|
|
|
|
CVehicleController::CVehicleController() |
|
{ |
|
ResetState(); |
|
} |
|
|
|
|
|
void CVehicleController::ResetState() |
|
{ |
|
m_pCarSystem = NULL; |
|
m_flVelocity[0] = m_flVelocity[1]= m_flVelocity[2] = 0.0f; |
|
for ( int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; i++ ) |
|
{ |
|
m_pWheels[i] = NULL; |
|
} |
|
m_pCarBody = NULL; |
|
m_torqueScale = 1; |
|
m_wheelCount = 0; |
|
m_wheelRadius = 0; |
|
memset( &m_currentState, 0, sizeof(m_currentState) ); |
|
m_bodyMass = 0; |
|
m_vehicleFlags = 0; |
|
memset( m_wheelPosition_Bs, 0, sizeof(m_wheelPosition_Bs) ); |
|
memset( m_tracePosition_Bs, 0, sizeof(m_tracePosition_Bs) ); |
|
|
|
m_bTraceData = false; |
|
if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST ) |
|
{ |
|
m_bTraceData = true; |
|
} |
|
|
|
m_nTireType = VEHICLE_TIRE_NORMAL; |
|
|
|
m_bOccupied = false; |
|
m_bEngineDisable = false; |
|
} |
|
|
|
CVehicleController::~CVehicleController() |
|
{ |
|
ShutdownCarSystem(); |
|
} |
|
|
|
IPhysicsObject* CVehicleController::GetWheel( int index ) |
|
{ |
|
// TODO: This is getting messy. |
|
if ( m_nVehicleType == VEHICLE_TYPE_CAR_WHEELS ) |
|
{ |
|
return m_pWheels[index]; |
|
} |
|
else if ( m_nVehicleType == VEHICLE_TYPE_CAR_RAYCAST && m_pCarSystem ) |
|
{ |
|
return static_cast<CPhysics_Car_System_Raycast_Wheels*>( m_pCarSystem )->GetWheel( index ); |
|
} |
|
else if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST && m_pCarSystem ) |
|
{ |
|
return static_cast<CPhysics_Airboat*>( m_pCarSystem )->GetWheel( index ); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
void CVehicleController::SetWheelFriction(int wheelIndex, float friction) |
|
{ |
|
CPhysics_Airboat *pAirboat = static_cast<CPhysics_Airboat*>( m_pCarSystem ); |
|
if ( !pAirboat ) |
|
return; |
|
|
|
pAirboat->SetWheelFriction( wheelIndex, friction ); |
|
} |
|
|
|
bool CVehicleController::GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps ) |
|
{ |
|
bool bSet = false; |
|
if ( index < m_wheelCount ) |
|
{ |
|
IPhysicsFrictionSnapshot *pSnapshot = m_pWheels[index]->CreateFrictionSnapshot(); |
|
float forceMax = -1.0f; |
|
m_pWheels[index]->GetPosition( pContactPoint, NULL ); |
|
while ( pSnapshot->IsValid() ) |
|
{ |
|
float thisForce = pSnapshot->GetNormalForce(); |
|
if ( thisForce > forceMax ) |
|
{ |
|
forceMax = thisForce; |
|
if ( pContactPoint ) |
|
{ |
|
pSnapshot->GetContactPoint( *pContactPoint ); |
|
} |
|
if ( pSurfaceProps ) |
|
{ |
|
*pSurfaceProps = pSnapshot->GetMaterial(1); |
|
} |
|
bSet = true; |
|
} |
|
pSnapshot->NextFrictionData(); |
|
} |
|
m_pWheels[index]->DestroyFrictionSnapshot(pSnapshot); |
|
} |
|
else |
|
{ |
|
if ( pContactPoint ) |
|
{ |
|
pContactPoint->Init(); |
|
} |
|
if ( pSurfaceProps ) |
|
{ |
|
*pSurfaceProps = 0; |
|
} |
|
|
|
} |
|
return bSet; |
|
} |
|
|
|
void CVehicleController::AttachListener() |
|
{ |
|
m_pCarBody->GetObject()->add_listener_object( this ); |
|
} |
|
|
|
void CVehicleController::event_object_deleted( IVP_Event_Object *pEvent ) |
|
{ |
|
// the car system's constraint solver is going to delete itself now, so NULL the car system. |
|
|
|
m_pCarSystem->event_object_deleted( pEvent ); |
|
m_pCarSystem = NULL; |
|
ShutdownCarSystem(); |
|
} |
|
|
|
IVP_Real_Object *CVehicleController::CreateWheel( int wheelIndex, vehicle_axleparams_t &axle ) |
|
{ |
|
if ( wheelIndex >= VEHICLE_MAX_WHEEL_COUNT ) |
|
return NULL; |
|
|
|
// HACKHACK: In Save/load, the wheel was reloaded, so pretend to create it |
|
// ALSO NOTE: Save/load puts the results into m_pWheels regardless of vehicle type!!! |
|
// That's why I'm not calling GetWheel(). |
|
if ( m_pWheels[wheelIndex] ) |
|
{ |
|
CPhysicsObject *pWheelObject = static_cast<CPhysicsObject *>(m_pWheels[wheelIndex]); |
|
return pWheelObject->GetObject(); |
|
} |
|
|
|
objectparams_t params; |
|
memset( ¶ms, 0, sizeof(params) ); |
|
|
|
Vector bodyPosition; |
|
QAngle bodyAngles; |
|
m_pCarBody->GetPosition( &bodyPosition, &bodyAngles ); |
|
matrix3x4_t matrix; |
|
AngleMatrix( bodyAngles, bodyPosition, matrix ); |
|
|
|
Vector position = axle.offset; |
|
|
|
// BUGBUG: This only works with 2 wheels per axle |
|
if ( wheelIndex & 1 ) |
|
{ |
|
position += axle.wheelOffset; |
|
} |
|
else |
|
{ |
|
position -= axle.wheelOffset; |
|
} |
|
|
|
Vector wheelPositionHL; |
|
VectorTransform( position, matrix, wheelPositionHL ); |
|
|
|
params.damping = axle.wheels.damping; |
|
params.dragCoefficient = 0; |
|
params.enableCollisions = false; |
|
params.inertia = axle.wheels.inertia; |
|
params.mass = axle.wheels.mass; |
|
params.pGameData = m_pCarBody->GetGameData(); |
|
params.pName = "VehicleWheel"; |
|
params.rotdamping = axle.wheels.rotdamping; |
|
params.rotInertiaLimit = 0; |
|
params.massCenterOverride = NULL; |
|
// needs to be in HL units because we're calling through the "outer" interface to create |
|
// the wheels |
|
float radius = axle.wheels.radius; |
|
float r3 = radius * radius * radius; |
|
params.volume = (4 / 3) * M_PI * r3; |
|
|
|
CPhysicsObject *pWheel = (CPhysicsObject *)m_pEnv->CreateSphereObject( radius, axle.wheels.materialIndex, wheelPositionHL, bodyAngles, ¶ms, false ); |
|
pWheel->Wake(); |
|
|
|
// UNDONE: only mask off some of these flags? |
|
unsigned int flags = pWheel->CallbackFlags(); |
|
flags = 0; |
|
pWheel->SetCallbackFlags( flags ); |
|
// copy the body's game flags |
|
pWheel->SetGameFlags( m_pCarBody->GetGameFlags() ); |
|
// cache the wheel object pointer |
|
m_pWheels[wheelIndex] = pWheel; |
|
|
|
IVP_U_Point wheelPositionIVP, wheelPositionBs; |
|
ConvertPositionToIVP( wheelPositionHL, wheelPositionIVP ); |
|
TransformIVPToLocal( wheelPositionIVP, wheelPositionBs, m_pCarBody->GetObject(), true ); |
|
m_wheelPosition_Bs[wheelIndex].set_to_zero(); |
|
m_wheelPosition_Bs[wheelIndex].set( &wheelPositionBs ); |
|
|
|
pWheel->AddCallbackFlags( CALLBACK_IS_VEHICLE_WHEEL ); |
|
|
|
return pWheel->GetObject(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::CreateTraceData( int wheelIndex, vehicle_axleparams_t &axle ) |
|
{ |
|
if ( wheelIndex >= VEHICLE_MAX_WHEEL_COUNT ) |
|
return; |
|
|
|
objectparams_t params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
Vector bodyPosition; |
|
QAngle bodyAngles; |
|
matrix3x4_t matrix; |
|
m_pCarBody->GetPosition( &bodyPosition, &bodyAngles ); |
|
AngleMatrix( bodyAngles, bodyPosition, matrix ); |
|
|
|
Vector tracePosition = axle.raytraceCenterOffset; |
|
// BUGBUG: This only works with 2 wheels per axle |
|
if ( wheelIndex & 1 ) |
|
{ |
|
tracePosition += axle.raytraceOffset; |
|
} |
|
else |
|
{ |
|
tracePosition -= axle.raytraceOffset; |
|
} |
|
|
|
Vector tracePositionHL; |
|
VectorTransform( tracePosition, matrix, tracePositionHL ); |
|
|
|
IVP_U_Point tracePositionIVP, tracePositionBs; |
|
ConvertPositionToIVP( tracePositionHL, tracePositionIVP ); |
|
TransformIVPToLocal( tracePositionIVP, tracePositionBs, m_pCarBody->GetObject(), true ); |
|
m_tracePosition_Bs[wheelIndex].set_to_zero(); |
|
m_tracePosition_Bs[wheelIndex].set( &tracePositionBs ); |
|
} |
|
|
|
void CVehicleController::CreateIVPObjects( ) |
|
{ |
|
// Initialize the car system (body and wheels). |
|
IVP_Template_Car_System ivpVehicleData( m_wheelCount, m_vehicleData.axleCount ); |
|
InitCarSystemBody( ivpVehicleData ); |
|
|
|
InitCarSystemWheels( ivpVehicleData ); |
|
|
|
BEGIN_IVP_ALLOCATION(); |
|
|
|
// Raycast Car |
|
switch ( m_nVehicleType ) |
|
{ |
|
case VEHICLE_TYPE_CAR_WHEELS: |
|
m_pCarSystem = new IVP_Car_System_Real_Wheels( m_pEnv->GetIVPEnvironment(), &ivpVehicleData ); |
|
break |
|
; |
|
case VEHICLE_TYPE_CAR_RAYCAST: |
|
m_pCarSystem = new CPhysics_Car_System_Raycast_Wheels( m_pEnv->GetIVPEnvironment(), &ivpVehicleData ); |
|
break; |
|
|
|
case VEHICLE_TYPE_AIRBOAT_RAYCAST: |
|
m_pCarSystem = new CPhysics_Airboat( m_pEnv->GetIVPEnvironment(), &ivpVehicleData, m_pGameTrace ); |
|
break; |
|
} |
|
|
|
AttachListener(); |
|
|
|
END_IVP_ALLOCATION(); |
|
} |
|
|
|
|
|
void CVehicleController::InitCarSystem( CPhysicsObject *pBodyObject ) |
|
{ |
|
if ( m_pCarSystem ) |
|
{ |
|
ShutdownCarSystem(); |
|
} |
|
|
|
// Car body. |
|
m_pCarBody = pBodyObject; |
|
m_bodyMass = m_pCarBody->GetMass(); |
|
m_gravityLength = m_pEnv->GetIVPEnvironment()->get_gravity()->real_length(); |
|
// Setup axle/wheel counts. |
|
m_wheelCount = m_vehicleData.axleCount * m_vehicleData.wheelsPerAxle; |
|
CreateIVPObjects(); |
|
|
|
if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST ) |
|
{ |
|
float flDampSpeed = 1.0f; |
|
float flDampRotSpeed = 1.0f; |
|
m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed ); |
|
} |
|
} |
|
|
|
void CVehicleController::VehicleDataReload() |
|
{ |
|
// compute torque normalization factor |
|
m_torqueScale = 1; |
|
// Clear accumulation. |
|
float totalTorqueDistribution = 0.0f; |
|
for ( int i = 0; i < m_vehicleData.axleCount; i++ ) |
|
{ |
|
totalTorqueDistribution += m_vehicleData.axles[i].torqueFactor; |
|
} |
|
|
|
if ( totalTorqueDistribution > 0 ) |
|
{ |
|
m_torqueScale /= totalTorqueDistribution; |
|
} |
|
// input speed is in miles/hour. Convert to in/s |
|
m_vehicleData.engine.maxSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.maxSpeed); |
|
m_vehicleData.engine.maxRevSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.maxRevSpeed); |
|
m_vehicleData.engine.boostMaxSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.boostMaxSpeed); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Setup the body parameters. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::InitCarSystemBody( IVP_Template_Car_System &ivpVehicleData ) |
|
{ |
|
ivpVehicleData.car_body = m_pCarBody->GetObject(); |
|
|
|
ivpVehicleData.index_x = IVP_INDEX_X; |
|
ivpVehicleData.index_y = IVP_INDEX_Y; |
|
ivpVehicleData.index_z = IVP_INDEX_Z; |
|
|
|
ivpVehicleData.body_counter_torque_factor = m_vehicleData.body.counterTorqueFactor; |
|
ivpVehicleData.body_down_force_vertical_offset = ConvertDistanceToIVP( m_vehicleData.body.tiltForceHeight ); |
|
ivpVehicleData.extra_gravity_force_value = m_vehicleData.body.addGravity * m_gravityLength * m_bodyMass; |
|
ivpVehicleData.extra_gravity_height_offset = 0; |
|
|
|
#if 0 |
|
// HACKHACK: match example |
|
ivpVehicleData.extra_gravity_force_value = 1.2; |
|
ivpVehicleData.body_down_force_vertical_offset = 2; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Setup the wheel paramters. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::InitCarSystemWheels( IVP_Template_Car_System &ivpVehicleData ) |
|
{ |
|
int wheelIndex = 0; |
|
|
|
m_wheelRadius = 0; |
|
m_totalWheelMass = 0; |
|
|
|
int i; |
|
for ( i = 0; i < m_vehicleData.axleCount; i++ ) |
|
{ |
|
for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ ) |
|
{ |
|
IVP_Real_Object *pWheel = CreateWheel( wheelIndex, m_vehicleData.axles[i] ); |
|
if ( pWheel ) |
|
{ |
|
// Create ray trace data for wheel. |
|
if ( m_bTraceData ) |
|
{ |
|
CreateTraceData( wheelIndex, m_vehicleData.axles[i] ); |
|
} |
|
|
|
ivpVehicleData.car_wheel[wheelIndex] = pWheel; |
|
ivpVehicleData.wheel_radius[wheelIndex] = pWheel->get_core()->upper_limit_radius; |
|
ivpVehicleData.wheel_reversed_sign[wheelIndex] = 1.0; |
|
// only for raycast car |
|
|
|
ivpVehicleData.friction_of_wheel[wheelIndex] = m_vehicleData.axles[i].wheels.frictionScale; |
|
ivpVehicleData.spring_constant[wheelIndex] = m_vehicleData.axles[i].suspension.springConstant * m_bodyMass; |
|
ivpVehicleData.spring_dampening[wheelIndex] = m_vehicleData.axles[i].suspension.springDamping * m_bodyMass; |
|
ivpVehicleData.spring_dampening_compression[wheelIndex] = m_vehicleData.axles[i].suspension.springDampingCompression * m_bodyMass; |
|
ivpVehicleData.max_body_force[wheelIndex] = m_vehicleData.axles[i].suspension.maxBodyForce * m_bodyMass; |
|
ivpVehicleData.spring_pre_tension[wheelIndex] = -ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.springAdditionalLength ); |
|
|
|
ivpVehicleData.wheel_pos_Bos[wheelIndex] = m_wheelPosition_Bs[wheelIndex]; |
|
if ( m_bTraceData ) |
|
{ |
|
ivpVehicleData.trace_pos_Bos[wheelIndex] = m_tracePosition_Bs[wheelIndex]; |
|
} |
|
|
|
m_totalWheelMass += m_vehicleData.axles[i].wheels.mass; |
|
} |
|
} |
|
|
|
ivpVehicleData.stabilizer_constant[i] = m_vehicleData.axles[i].suspension.stabilizerConstant * m_bodyMass; |
|
// this should output in radians per second |
|
float radius = ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius ); |
|
float totalMaxSpeed = max( m_vehicleData.engine.boostMaxSpeed, m_vehicleData.engine.maxSpeed ); |
|
ivpVehicleData.wheel_max_rotation_speed[i] = totalMaxSpeed / radius; |
|
if ( radius > m_wheelRadius ) |
|
{ |
|
m_wheelRadius = radius; |
|
} |
|
} |
|
|
|
for ( i = 0; i < m_wheelCount; i++ ) |
|
{ |
|
m_pWheels[i]->EnableCollisions( true ); |
|
} |
|
} |
|
|
|
void CVehicleController::ShutdownCarSystem() |
|
{ |
|
delete m_pCarSystem; |
|
m_pCarSystem = NULL; |
|
for ( int i = 0; i < m_wheelCount; i++ ) |
|
{ |
|
if ( m_pWheels[i] ) |
|
{ |
|
m_pEnv->DestroyObject( m_pWheels[i] ); |
|
} |
|
m_pWheels[i] = NULL; |
|
} |
|
} |
|
|
|
|
|
void CVehicleController::InitVehicleData( const vehicleparams_t ¶ms ) |
|
{ |
|
m_vehicleData = params; |
|
VehicleDataReload(); |
|
} |
|
|
|
void CVehicleController::SetSpringLength(int wheelIndex, float length) |
|
{ |
|
m_pCarSystem->change_spring_length((IVP_POS_WHEEL)wheelIndex, length); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allows booster timer to run, |
|
// Returns: true if time still exists |
|
// false if timer has run out (i.e. can use boost again) |
|
//----------------------------------------------------------------------------- |
|
float CVehicleController::UpdateBooster( float dt ) |
|
{ |
|
m_pCarSystem->update_booster( dt ); |
|
m_currentState.boostDelay = m_pCarSystem->get_booster_delay(); |
|
return m_currentState.boostDelay; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Are whe boosting? |
|
//----------------------------------------------------------------------------- |
|
bool CVehicleController::IsBoosting( void ) |
|
{ |
|
return ( m_pCarSystem->get_booster_time_to_go() > 0.0f ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the vehicle controller. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::Update( float dt, vehicle_controlparams_t &controlsIn ) |
|
{ |
|
vehicle_controlparams_t controls = controlsIn; |
|
// Speed. |
|
m_currentState.speed = ConvertDistanceToHL( m_pCarSystem->get_body_speed() ); |
|
float flSpeed = GAMEVEL_TO_MPH( m_currentState.speed ); |
|
float flAbsSpeed = fabsf( flSpeed ); |
|
|
|
// Calculate the throttle and brake values. |
|
float flThrottle = controls.throttle; |
|
bool bHandbrake = controls.handbrake; |
|
float flBrake = controls.brake; |
|
bool bPowerslide = bHandbrake && ( flAbsSpeed > 18.0f ); |
|
|
|
if ( bHandbrake ) |
|
{ |
|
flThrottle = 0.0f; |
|
} |
|
|
|
if ( IsBoosting() ) |
|
{ |
|
controls.boost = true; |
|
flThrottle = flThrottle < 0.0f ? -1.0f : 1.0f; |
|
} |
|
|
|
if ( flThrottle == 0.0f && flBrake == 0.0f && !bHandbrake ) |
|
{ |
|
flBrake = 0.1f; |
|
} |
|
|
|
// Update steering. |
|
UpdateSteering( controls, dt, flAbsSpeed ); |
|
|
|
// Update powerslide. |
|
UpdatePowerslide( controls, bPowerslide, flSpeed ); |
|
|
|
// Update engine. |
|
UpdateEngine( controls, dt, flThrottle, flBrake, bHandbrake, bPowerslide ); |
|
|
|
// Update handbrake. |
|
UpdateHandbrake( controls, flThrottle, bHandbrake, bPowerslide ); |
|
|
|
// Update skidding. |
|
UpdateSkidding( bHandbrake ); |
|
|
|
// Apply the extra forces to the car (downward, counter-torque, etc.) |
|
UpdateExtraForces(); |
|
|
|
// Update the physical position of the wheels for raycast vehicles. |
|
UpdateWheelPositions(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the steering on the vehicle. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateSteering( const vehicle_controlparams_t &controls, float flDeltaTime, float flSpeed ) |
|
{ |
|
// Steering - IVP steering is in radians. |
|
float flSteeringAngle = CalcSteering( flDeltaTime, flSpeed, controls.steering, controls.bAnalogSteering ); |
|
m_pCarSystem->do_steering( DEG2RAD( flSteeringAngle ), controls.bAnalogSteering ); |
|
m_currentState.steeringAngle = flSteeringAngle; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the powerslide state (wheel materials). |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdatePowerslide( const vehicle_controlparams_t &controls, bool bPowerslide, float flSpeed ) |
|
{ |
|
// Only allow skidding if it is allowed by the vehicle type. |
|
if ( !m_vehicleData.steering.isSkidAllowed ) |
|
return; |
|
|
|
// Check to see if the vehicle is occupied. |
|
if ( !m_bOccupied ) |
|
return; |
|
|
|
// Set the powerslide left/right. |
|
bool bPowerslideLeft = bPowerslide && controls.handbrakeLeft; |
|
bool bPowerslideRight = bPowerslide && controls.handbrakeRight; |
|
|
|
int iWheel = 0; |
|
unsigned int newTireType = VEHICLE_TIRE_NORMAL; |
|
if ( bPowerslideLeft || bPowerslideRight ) |
|
{ |
|
newTireType = VEHICLE_TIRE_POWERSLIDE; |
|
} |
|
else if ( bPowerslide ) |
|
{ |
|
newTireType = VEHICLE_TIRE_BRAKING; |
|
} |
|
|
|
if ( newTireType != m_nTireType ) |
|
{ |
|
for ( int iAxle = 0; iAxle < m_vehicleData.axleCount; ++iAxle ) |
|
{ |
|
int materialIndex = m_vehicleData.axles[iAxle].wheels.materialIndex; |
|
if ( newTireType == VEHICLE_TIRE_POWERSLIDE && ( m_vehicleData.axles[iAxle].wheels.skidMaterialIndex != - 1 ) ) |
|
{ |
|
materialIndex = m_vehicleData.axles[iAxle].wheels.skidMaterialIndex; |
|
} |
|
else if ( newTireType == VEHICLE_TIRE_BRAKING && ( m_vehicleData.axles[iAxle].wheels.brakeMaterialIndex != -1 ) ) |
|
{ |
|
materialIndex = m_vehicleData.axles[iAxle].wheels.brakeMaterialIndex; |
|
} |
|
|
|
for ( int iAxleWheel = 0; iAxleWheel < m_vehicleData.wheelsPerAxle; ++iAxleWheel, ++iWheel ) |
|
{ |
|
m_pWheels[iWheel]->SetMaterialIndex( materialIndex ); |
|
} |
|
|
|
m_nTireType = newTireType; |
|
} |
|
} |
|
|
|
// Push the car a little. |
|
float flFrontAccel = 0.0f; |
|
float flRearAccel = 0.0f; |
|
if ( flSpeed > 0 && (bPowerslideLeft != bPowerslideRight) ) |
|
{ |
|
// NOTE: positive acceleration is to the left |
|
float powerSlide = RemapValClamped( flSpeed, m_vehicleData.steering.speedSlow, m_vehicleData.steering.speedFast, 0, 1 ); |
|
float powerSlideAccel = ConvertDistanceToIVP( m_vehicleData.steering.powerSlideAccel); |
|
if ( bPowerslideLeft ) |
|
{ |
|
flFrontAccel = powerSlideAccel * powerSlide; |
|
flRearAccel = -powerSlideAccel * powerSlide; |
|
} |
|
else |
|
{ |
|
flFrontAccel = -powerSlideAccel * powerSlide; |
|
flRearAccel = powerSlideAccel * powerSlide; |
|
} |
|
} |
|
m_pCarSystem->set_powerslide( flFrontAccel, flRearAccel ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateEngine( const vehicle_controlparams_t &controls, float flDeltaTime, |
|
float flThrottle, float flBrake, bool bHandbrake, bool bPowerslide ) |
|
{ |
|
bool bTorqueBoost = UpdateEngineTurboStart( controls, flDeltaTime ); |
|
|
|
CalcEngine( flThrottle, flBrake, bHandbrake, controls.steering, bTorqueBoost ); |
|
|
|
UpdateEngineTurboFinish(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CVehicleController::UpdateEngineTurboStart( const vehicle_controlparams_t &controls, float flDeltaTime ) |
|
{ |
|
bool bTorqueBoost = false; |
|
if ( controls.boost > 0 ) |
|
{ |
|
if ( m_vehicleData.engine.torqueBoost ) |
|
{ |
|
// Turbo will be applied at the engine level. |
|
bTorqueBoost = true; |
|
m_pCarSystem->activate_booster( 0.0f, m_vehicleData.engine.boostDuration, m_vehicleData.engine.boostDelay ); |
|
} |
|
else |
|
{ |
|
// Activate the turbo force booster - applied to vehicle body. |
|
m_pCarSystem->activate_booster( m_vehicleData.engine.boostForce * controls.boost, m_vehicleData.engine.boostDuration, m_vehicleData.engine.boostDelay ); |
|
} |
|
} |
|
|
|
m_pCarSystem->update_booster( flDeltaTime ); |
|
m_currentState.boostDelay = m_pCarSystem->get_booster_delay(); |
|
m_currentState.isTorqueBoosting = bTorqueBoost; |
|
|
|
return bTorqueBoost; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateEngineTurboFinish( void ) |
|
{ |
|
if ( m_vehicleData.engine.boostDuration + m_vehicleData.engine.boostDelay > 0 ) // watch out for div by zero |
|
{ |
|
if ( m_currentState.boostDelay > 0 ) |
|
{ |
|
m_currentState.boostTimeLeft = 100 - 100 * ( m_currentState.boostDelay / ( m_vehicleData.engine.boostDuration + m_vehicleData.engine.boostDelay ) ); |
|
} |
|
else |
|
{ |
|
m_currentState.boostTimeLeft = 100; // ready to go any time |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the handbrake. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateHandbrake( const vehicle_controlparams_t &controls, float flThrottle, bool bHandbrake, bool bPowerslide ) |
|
{ |
|
// Get the current vehicle speed. |
|
m_currentState.speed = ConvertDistanceToHL( m_pCarSystem->get_body_speed() ); |
|
if ( !bPowerslide ) |
|
{ |
|
// HACK! Allowing you to overcome gravity at low throttle. |
|
if ( ( flThrottle < 0.0f && m_currentState.speed > THROTTLE_OPPOSING_FORCE_EPSILON ) || |
|
( flThrottle > 0.0f && m_currentState.speed < -THROTTLE_OPPOSING_FORCE_EPSILON ) ) |
|
{ |
|
bHandbrake = true; |
|
} |
|
} |
|
|
|
if ( bHandbrake ) |
|
{ |
|
// HACKHACK: only allow the handbrake when the wheels have contact with something |
|
// otherwise they will affect the car in an undesirable way |
|
bHandbrake = false; |
|
for ( int iWheel = 0; iWheel < m_wheelCount; ++iWheel ) |
|
{ |
|
if ( m_pWheels[iWheel]->GetContactPoint(NULL, NULL) ) |
|
{ |
|
bHandbrake = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
bool currentHandbrake = (m_vehicleFlags & FVEHICLE_HANDBRAKE_ON) ? true : false; |
|
if ( bHandbrake != currentHandbrake ) |
|
{ |
|
if ( bHandbrake ) |
|
{ |
|
m_vehicleFlags |= FVEHICLE_HANDBRAKE_ON; |
|
} |
|
else |
|
{ |
|
m_vehicleFlags &= ~FVEHICLE_HANDBRAKE_ON; |
|
} |
|
|
|
for ( int iWheel = 0; iWheel < m_wheelCount; ++iWheel ) |
|
{ |
|
m_pCarSystem->fix_wheel( ( IVP_POS_WHEEL )iWheel, bHandbrake ? IVP_TRUE : IVP_FALSE ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateSkidding( bool bHandbrake ) |
|
{ |
|
m_currentState.skidSpeed = 0.0f; |
|
m_currentState.skidMaterial = 0; |
|
m_currentState.wheelsInContact = m_wheelCount; |
|
m_currentState.wheelsNotInContact = 0; |
|
if ( m_vehicleData.steering.isSkidAllowed ) |
|
{ |
|
// Estimate rot speed based on current speed and the front wheels radius |
|
float flAbsSpeed = fabs( m_currentState.speed ); |
|
|
|
Vector contact; |
|
Vector velocity; |
|
int surfaceProps; |
|
m_currentState.wheelsInContact = 0; |
|
m_currentState.wheelsNotInContact = 0; |
|
|
|
for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel ) |
|
{ |
|
if ( GetWheelContactPoint( iWheel, &contact, &surfaceProps ) ) |
|
{ |
|
// NOTE: The wheel should be translating by the negative of the speed a point in contact with the surface |
|
// is moving. So the net velocity on the surface is zero if that wheel is 100% engaged in driving the car |
|
// any velocity in excess of this gets compared against the threshold for skidding |
|
m_pWheels[iWheel]->GetVelocityAtPoint( contact, &velocity ); |
|
float speed = velocity.Length(); |
|
if ( speed > m_currentState.skidSpeed || m_currentState.skidSpeed <= 0.0f ) |
|
{ |
|
m_currentState.skidSpeed = speed; |
|
m_currentState.skidMaterial = surfaceProps; |
|
} |
|
m_currentState.wheelsInContact++; |
|
} |
|
else |
|
{ |
|
m_currentState.wheelsNotInContact++; |
|
} |
|
} |
|
// Check for locked wheels. |
|
if ( bHandbrake && ( flAbsSpeed > 30 ) ) |
|
{ |
|
m_currentState.skidSpeed = flAbsSpeed; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Apply extra forces to the vehicle. The downward force, counter- |
|
// torque etc. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateExtraForces( void ) |
|
{ |
|
// Extra downward force. |
|
IVP_Cache_Object *co = m_pCarBody->GetObject()->get_cache_object(); |
|
float y_val = co->m_world_f_object.get_elem( IVP_INDEX_Y, IVP_INDEX_Y ); |
|
if ( fabs( y_val ) < 0.05 ) |
|
{ |
|
m_pCarSystem->change_body_downforce( m_vehicleData.body.tiltForce * m_gravityLength * m_bodyMass ); |
|
} |
|
else |
|
{ |
|
m_pCarSystem->change_body_downforce( 0.0 ); |
|
} |
|
co->remove_reference(); |
|
|
|
// Counter-torque. |
|
if ( m_nVehicleType == VEHICLE_TYPE_CAR_WHEELS ) |
|
{ |
|
m_pCarSystem->update_body_countertorque(); |
|
} |
|
|
|
// if the car has a global angular velocity limit, apply that constraint |
|
AngularImpulse angVel; |
|
m_pCarBody->GetVelocity( NULL, &angVel ); |
|
if ( m_vehicleData.body.maxAngularVelocity > 0 && angVel.Length() > m_vehicleData.body.maxAngularVelocity ) |
|
{ |
|
VectorNormalize(angVel); |
|
angVel *= m_vehicleData.body.maxAngularVelocity; |
|
m_pCarBody->SetVelocityInstantaneous( NULL, &angVel ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the physical position of the wheels for raycast vehicles. |
|
// NOTE: Raycast boat doesn't have wheels. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::UpdateWheelPositions( void ) |
|
{ |
|
if ( m_nVehicleType == VEHICLE_TYPE_CAR_RAYCAST ) |
|
{ |
|
m_pCarSystem->update_wheel_positions(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CVehicleController::CalcSteering( float dt, float speed, float steering, bool bAnalog ) |
|
{ |
|
float degrees = RemapValClamped( speed, m_vehicleData.steering.speedSlow, m_vehicleData.steering.speedFast, m_vehicleData.steering.degreesSlow, m_vehicleData.steering.degreesFast ); |
|
float speedGame = MPH_TO_GAMEVEL(speed); |
|
if ( speedGame > m_vehicleData.engine.maxSpeed ) |
|
{ |
|
degrees = RemapValClamped( speedGame, m_vehicleData.engine.maxSpeed, m_vehicleData.engine.boostMaxSpeed, m_vehicleData.steering.degreesFast, m_vehicleData.steering.degreesBoost ); |
|
} |
|
if ( m_vehicleData.steering.steeringExponent != 0 ) |
|
{ |
|
float sign = steering < 0 ? -1 : 1; |
|
float absSteering = fabs(steering); |
|
if ( bAnalog ) |
|
{ |
|
// analog steering is directly mapped, not integrated, so go ahead and map the full range using the exponent |
|
// then clamp to the output cone - keeps stick position:turn rate constant |
|
// NOTE: Also hardcode exponent to 2 because we can't add a script entry at this point |
|
float output = pow(absSteering, 2.0f) * sign * m_vehicleData.steering.degreesSlow; |
|
return clamp(output, -degrees, degrees ); |
|
} |
|
// digital steering is integrated, keep time to full turn rate constant |
|
return pow(absSteering, m_vehicleData.steering.steeringExponent) * sign * degrees; |
|
} |
|
return steering * degrees; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::CalcEngineTransmission( float flThrottle ) |
|
{ |
|
// Automatic Transmission? |
|
if ( !m_vehicleData.engine.isAutoTransmission ) |
|
return; |
|
|
|
// Calculate the average rotational speed of the vehicle's wheels. |
|
float flAvgRotSpeed = 0.0; |
|
for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel ) |
|
{ |
|
float flRotSpeed = fabs( m_pCarSystem->get_wheel_angular_velocity( IVP_POS_WHEEL( iWheel ) ) ); |
|
flAvgRotSpeed += flRotSpeed; |
|
} |
|
flAvgRotSpeed *= 0.5f / ( float )IVP_PI / m_wheelCount; |
|
|
|
float flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60; |
|
|
|
// Only shift up when going forward (throttling). |
|
if ( flThrottle > 0.0f ) |
|
{ |
|
// Shift up?, top gear is gearcount-1 (0 based) |
|
while ( ( flEstEngineRPM > m_vehicleData.engine.shiftUpRPM ) && ( m_currentState.gear < m_vehicleData.engine.gearCount-1 ) ) |
|
{ |
|
m_currentState.gear++; |
|
flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60; |
|
} |
|
} |
|
|
|
// Downshift? |
|
while ( ( flEstEngineRPM < m_vehicleData.engine.shiftDownRPM ) && ( m_currentState.gear > 0 ) ) |
|
{ |
|
m_currentState.gear--; |
|
flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60; |
|
} |
|
|
|
m_currentState.engineRPM = flEstEngineRPM; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// throttle goes forward and backward, [-1, 1] |
|
// brake_val [0..1] |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::CalcEngine( float throttle, float brake_val, bool handbrake, float steeringVal, bool torqueBoost ) |
|
{ |
|
// Update the engine transmission. |
|
CalcEngineTransmission( throttle ); |
|
|
|
// Get the speed of the vehicle. |
|
float flAbsSpeed = fabs( m_currentState.speed ); |
|
|
|
// Speed governor |
|
if ( IsPC() ) |
|
{ |
|
float maxSpeed = torqueBoost ? m_vehicleData.engine.boostMaxSpeed : m_vehicleData.engine.maxSpeed; |
|
maxSpeed = max(1.f,maxSpeed); // make sure this is non-zero before the divide |
|
if ( (throttle > 0) && (flAbsSpeed > maxSpeed) ) |
|
{ |
|
float frac = flAbsSpeed / maxSpeed; |
|
if ( frac > m_vehicleData.engine.autobrakeSpeedGain ) |
|
{ |
|
throttle = 0; |
|
brake_val = (frac - 1.0f) * m_vehicleData.engine.autobrakeSpeedFactor; |
|
if ( m_currentState.wheelsInContact == 0 ) |
|
{ |
|
brake_val = 0; |
|
} |
|
} |
|
throttle *= 0.1f; |
|
} |
|
} |
|
else // consoles |
|
{ |
|
if ( ( throttle > 0 ) && ( ( !torqueBoost && flAbsSpeed > (m_vehicleData.engine.maxSpeed * throttle) ) || |
|
( torqueBoost && flAbsSpeed > m_vehicleData.engine.boostMaxSpeed) ) ) |
|
{ |
|
throttle *= 0.1f; |
|
} |
|
} |
|
|
|
// Check for reverse - both of these "governors" or horrible and need to be redone before we ship! |
|
if ( ( throttle < 0 ) && ( !torqueBoost && ( flAbsSpeed > m_vehicleData.engine.maxRevSpeed ) ) ) |
|
{ |
|
throttle *= 0.1f; |
|
} |
|
|
|
if ( throttle != 0.0 ) |
|
{ |
|
m_vehicleFlags &= ~FVEHICLE_THROTTLE_STOPPED; |
|
// calculate the force that propels the car |
|
const float watt_per_hp = 745.0f; |
|
const float seconds_per_minute = 60.0f; |
|
|
|
float wheel_force_by_throttle = throttle * |
|
m_vehicleData.engine.horsepower * (watt_per_hp * seconds_per_minute) * |
|
m_vehicleData.engine.gearRatio[m_currentState.gear] * m_vehicleData.engine.axleRatio / |
|
(m_vehicleData.engine.maxRPM * m_wheelRadius * (2 * IVP_PI)); |
|
|
|
if ( m_currentState.engineRPM >= m_vehicleData.engine.maxRPM ) |
|
{ |
|
wheel_force_by_throttle = 0; |
|
} |
|
|
|
int wheelIndex = 0; |
|
for ( int i = 0; i < m_vehicleData.axleCount; i++ ) |
|
{ |
|
float axleFactor = m_vehicleData.axles[i].torqueFactor * m_torqueScale; |
|
|
|
float boostFactor = 0.5f; |
|
if ( torqueBoost && IsBoosting() ) |
|
{ |
|
// reduce the boost at low speeds and high turns since this usually just makes the tires spin |
|
// this means you only get the full boost when travelling in a straight line at high speed |
|
float speedFactor = RemapValClamped( flAbsSpeed, 0, m_vehicleData.engine.maxSpeed, 0.1f, 1.0f ); |
|
float turnFactor = 1.0f - (fabs(steeringVal) * 0.95f); |
|
float dampedBoost = m_vehicleData.engine.boostForce * speedFactor * turnFactor; |
|
if ( dampedBoost > boostFactor ) |
|
{ |
|
boostFactor = dampedBoost; |
|
} |
|
//Msg("Boost applied %.2f, speed %.2f, turn %.2f\n", boostFactor, speedFactor, turnFactor ); |
|
} |
|
float axleTorque = boostFactor * wheel_force_by_throttle * axleFactor * ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius ); |
|
|
|
for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ ) |
|
{ |
|
float torqueVal = axleTorque; |
|
m_pCarSystem->change_wheel_torque((IVP_POS_WHEEL)wheelIndex, torqueVal); |
|
} |
|
} |
|
} |
|
else if ( brake_val != 0 ) |
|
{ |
|
m_vehicleFlags &= ~FVEHICLE_THROTTLE_STOPPED; |
|
|
|
// Brake to slow down the wheel. |
|
float wheel_force_by_brake = brake_val * m_gravityLength * ( m_bodyMass + m_totalWheelMass ); |
|
|
|
float sign = m_currentState.speed >= 0.0f ? -1.0f : 1.0f; |
|
int wheelIndex = 0; |
|
for ( int i = 0; i < m_vehicleData.axleCount; i++ ) |
|
{ |
|
float torque_val = 0.5 * sign * wheel_force_by_brake * m_vehicleData.axles[i].brakeFactor * ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius ); |
|
for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ ) |
|
{ |
|
m_pCarSystem->change_wheel_torque( ( IVP_POS_WHEEL )wheelIndex, torque_val ); |
|
} |
|
} |
|
} |
|
else if ( !(m_vehicleFlags & FVEHICLE_THROTTLE_STOPPED) ) |
|
{ |
|
m_vehicleFlags |= FVEHICLE_THROTTLE_STOPPED; |
|
|
|
for ( int w = 0; w < m_wheelCount; w++ ) |
|
{ |
|
m_pCarSystem->change_wheel_torque((IVP_POS_WHEEL)w, 0); |
|
} |
|
} |
|
|
|
// Update the throttle - primarily for the airboat! |
|
m_pCarSystem->update_throttle( throttle ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get debug rendering data from the ipion physics system. |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem ) |
|
{ |
|
IVP_CarSystemDebugData_t carSystemDebugData; |
|
memset(&carSystemDebugData,0,sizeof(carSystemDebugData)); |
|
m_pCarSystem->GetCarSystemDebugData( carSystemDebugData ); |
|
|
|
// Raycast car wheel trace data. |
|
for ( int iWheel = 0; iWheel < VEHICLE_DEBUGRENDERDATA_MAX_WHEELS; ++iWheel ) |
|
{ |
|
debugCarSystem.vecWheelRaycasts[iWheel][0].x = carSystemDebugData.wheelRaycasts[iWheel][0].k[0]; |
|
debugCarSystem.vecWheelRaycasts[iWheel][0].y = carSystemDebugData.wheelRaycasts[iWheel][0].k[1]; |
|
debugCarSystem.vecWheelRaycasts[iWheel][0].z = carSystemDebugData.wheelRaycasts[iWheel][0].k[2]; |
|
|
|
debugCarSystem.vecWheelRaycasts[iWheel][1].x = carSystemDebugData.wheelRaycasts[iWheel][1].k[0]; |
|
debugCarSystem.vecWheelRaycasts[iWheel][1].y = carSystemDebugData.wheelRaycasts[iWheel][1].k[1]; |
|
debugCarSystem.vecWheelRaycasts[iWheel][1].z = carSystemDebugData.wheelRaycasts[iWheel][1].k[2]; |
|
|
|
debugCarSystem.vecWheelRaycastImpacts[iWheel] = debugCarSystem.vecWheelRaycasts[iWheel][0] + ( carSystemDebugData.wheelRaycastImpacts[iWheel] * |
|
( debugCarSystem.vecWheelRaycasts[iWheel][1] - debugCarSystem.vecWheelRaycasts[iWheel][0] ) ); |
|
} |
|
|
|
ConvertPositionToHL( carSystemDebugData.backActuatorLeft, debugCarSystem.vecAxlePos[0] ); |
|
ConvertPositionToHL( carSystemDebugData.backActuatorRight, debugCarSystem.vecAxlePos[1] ); |
|
ConvertPositionToHL( carSystemDebugData.frontActuatorLeft, debugCarSystem.vecAxlePos[2] ); |
|
// vecAxlePos only has three elements so this line is illegal. The mapping of actuators |
|
// to axles seems dodgy anyway. |
|
//ConvertPositionToHL( carSystemDebugData.frontActuatorRight, debugCarSystem.vecAxlePos[3] ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Save/load |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::WriteToTemplate( vphysics_save_cvehiclecontroller_t &controllerTemplate ) |
|
{ |
|
// Get rid of the handbrake flag. The car keeps the flag and will reset it fixing wheels, |
|
// else the system thinks it already fixed the wheels on load and the car roles. |
|
m_vehicleFlags &= ~FVEHICLE_HANDBRAKE_ON; |
|
|
|
controllerTemplate.m_pCarBody = m_pCarBody; |
|
controllerTemplate.m_wheelCount = m_wheelCount; |
|
controllerTemplate.m_wheelRadius = m_wheelRadius; |
|
controllerTemplate.m_bodyMass = m_bodyMass; |
|
controllerTemplate.m_totalWheelMass = m_totalWheelMass; |
|
controllerTemplate.m_gravityLength = m_gravityLength; |
|
controllerTemplate.m_torqueScale = m_torqueScale; |
|
controllerTemplate.m_vehicleFlags = m_vehicleFlags; |
|
controllerTemplate.m_nTireType = m_nTireType; |
|
controllerTemplate.m_nVehicleType = m_nVehicleType; |
|
controllerTemplate.m_bTraceData = m_bTraceData; |
|
controllerTemplate.m_bOccupied = m_bOccupied; |
|
controllerTemplate.m_bEngineDisable = m_bEngineDisable; |
|
memcpy( &controllerTemplate.m_currentState, &m_currentState, sizeof(m_currentState) ); |
|
memcpy( &controllerTemplate.m_vehicleData, &m_vehicleData, sizeof(m_vehicleData) ); |
|
for (int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; ++i ) |
|
{ |
|
controllerTemplate.m_pWheels[i] = m_pWheels[i]; |
|
ConvertPositionToHL( m_wheelPosition_Bs[i], controllerTemplate.m_wheelPosition_Bs[i] ); |
|
ConvertPositionToHL( m_tracePosition_Bs[i], controllerTemplate.m_tracePosition_Bs[i] ); |
|
} |
|
m_flVelocity[0] = m_flVelocity[1] = m_flVelocity[2] = 0.0f; |
|
if ( m_pCarBody ) |
|
{ |
|
IVP_U_Float_Point &speed = m_pCarBody->GetObject()->get_core()->speed; |
|
controllerTemplate.m_flVelocity[0] = speed.k[0]; |
|
controllerTemplate.m_flVelocity[1] = speed.k[1]; |
|
controllerTemplate.m_flVelocity[2] = speed.k[2]; |
|
} |
|
|
|
} |
|
|
|
// JAY: Keep this around for now while we still have a bunch of games saved with the old |
|
// vehicle controls. We won't ship this, but it lets us debug |
|
#define OLD_SAVED_GAME 1 |
|
|
|
#if OLD_SAVED_GAME |
|
#define SET_DEFAULT(x,y) { if ( x == 0 ) x = y; } |
|
#endif |
|
|
|
void CVehicleController::InitFromTemplate( CPhysicsEnvironment *pEnv, void *pGameData, |
|
IPhysicsGameTrace *pGameTrace, const vphysics_save_cvehiclecontroller_t &controllerTemplate ) |
|
{ |
|
m_pEnv = pEnv; |
|
m_pGameTrace = pGameTrace; |
|
m_pCarBody = controllerTemplate.m_pCarBody; |
|
m_wheelCount = controllerTemplate.m_wheelCount; |
|
m_wheelRadius = controllerTemplate.m_wheelRadius; |
|
m_bodyMass = controllerTemplate.m_bodyMass; |
|
m_totalWheelMass = controllerTemplate.m_totalWheelMass; |
|
m_gravityLength = controllerTemplate.m_gravityLength; |
|
m_torqueScale = controllerTemplate.m_torqueScale; |
|
m_vehicleFlags = controllerTemplate.m_vehicleFlags; |
|
m_nTireType = controllerTemplate.m_nTireType; |
|
m_nVehicleType = controllerTemplate.m_nVehicleType; |
|
m_bTraceData = controllerTemplate.m_bTraceData; |
|
m_bOccupied = controllerTemplate.m_bOccupied; |
|
m_bEngineDisable = controllerTemplate.m_bEngineDisable; |
|
m_pCarSystem = NULL; |
|
memcpy( &m_currentState, &controllerTemplate.m_currentState, sizeof(m_currentState) ); |
|
memcpy( &m_vehicleData, &controllerTemplate.m_vehicleData, sizeof(m_vehicleData) ); |
|
memcpy( &m_flVelocity, controllerTemplate.m_flVelocity, sizeof(m_flVelocity) ); |
|
|
|
#if OLD_SAVED_GAME |
|
SET_DEFAULT( m_torqueScale, 1.0 ); |
|
SET_DEFAULT( m_vehicleData.steering.steeringRateSlow, 4.5 ); |
|
SET_DEFAULT( m_vehicleData.steering.steeringRateFast, 0.5 ); |
|
SET_DEFAULT( m_vehicleData.steering.steeringRestRateSlow, 3.0 ); |
|
SET_DEFAULT( m_vehicleData.steering.steeringRestRateFast, 1.8 ); |
|
SET_DEFAULT( m_vehicleData.steering.speedSlow, m_vehicleData.engine.maxSpeed*0.25 ); |
|
SET_DEFAULT( m_vehicleData.steering.speedFast, m_vehicleData.engine.maxSpeed*0.75 ); |
|
SET_DEFAULT( m_vehicleData.steering.degreesSlow, 50 ); |
|
SET_DEFAULT( m_vehicleData.steering.degreesFast, 18 ); |
|
SET_DEFAULT( m_vehicleData.steering.degreesBoost, 10 ); |
|
|
|
|
|
SET_DEFAULT( m_vehicleData.steering.turnThrottleReduceSlow, 0.3 ); |
|
SET_DEFAULT( m_vehicleData.steering.turnThrottleReduceFast, 3 ); |
|
SET_DEFAULT( m_vehicleData.steering.brakeSteeringRateFactor, 6 ); |
|
SET_DEFAULT( m_vehicleData.steering.throttleSteeringRestRateFactor, 2 ); |
|
SET_DEFAULT( m_vehicleData.steering.boostSteeringRestRateFactor, 1 ); |
|
SET_DEFAULT( m_vehicleData.steering.boostSteeringRateFactor, 1 ); |
|
SET_DEFAULT( m_vehicleData.steering.powerSlideAccel, 200 ); |
|
|
|
SET_DEFAULT( m_vehicleData.engine.autobrakeSpeedGain, 1.0 ); |
|
SET_DEFAULT( m_vehicleData.engine.autobrakeSpeedFactor, 2.0 ); |
|
#endif |
|
|
|
for (int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; ++i ) |
|
{ |
|
m_pWheels[i] = controllerTemplate.m_pWheels[i]; |
|
ConvertPositionToIVP( controllerTemplate.m_wheelPosition_Bs[i], m_wheelPosition_Bs[i] ); |
|
ConvertPositionToIVP( controllerTemplate.m_tracePosition_Bs[i], m_tracePosition_Bs[i] ); |
|
} |
|
|
|
CreateIVPObjects( ); |
|
|
|
// HACKHACK: vehicle wheels don't have valid friction at startup, clearing the body's angular velocity keeps |
|
// this fact from affecting the vehicle dynamics in any noticeable way |
|
// using growFriction will re-establish the contact point with moveable objects, but the friction that |
|
// occurs afterward is not the same across the save even when that is extended to include static objects |
|
if ( m_pCarBody ) |
|
{ |
|
// clear angVel |
|
m_pCarBody->SetVelocity( NULL, &vec3_origin ); |
|
m_pCarBody->GetObject()->get_core()->speed_change.set( m_flVelocity[0], m_flVelocity[1], m_flVelocity[2] ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::OnVehicleEnter( void ) |
|
{ |
|
m_bOccupied = true; |
|
|
|
if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST ) |
|
{ |
|
float flDampSpeed = 0.0f; |
|
float flDampRotSpeed = 0.0f; |
|
m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVehicleController::OnVehicleExit( void ) |
|
{ |
|
m_bOccupied = false; |
|
|
|
// Reset the vehicle tires when exiting the vehicle. |
|
if ( m_vehicleData.steering.isSkidAllowed ) |
|
{ |
|
int iWheel = 0; |
|
for ( int iAxle = 0; iAxle < m_vehicleData.axleCount; ++iAxle ) |
|
{ |
|
for ( int iAxleWheel = 0; iAxleWheel < m_vehicleData.wheelsPerAxle; ++iAxleWheel, ++iWheel ) |
|
{ |
|
// Change back to normal tires. |
|
if ( m_nTireType != VEHICLE_TIRE_NORMAL ) |
|
{ |
|
m_pWheels[iWheel]->SetMaterialIndex( m_vehicleData.axles[iAxle].wheels.materialIndex ); |
|
} |
|
|
|
m_pCarSystem->fix_wheel( ( IVP_POS_WHEEL )iWheel, IVP_TRUE ); |
|
} |
|
} |
|
m_nTireType = VEHICLE_TIRE_NORMAL; |
|
m_currentState.skidSpeed = 0.0f; |
|
} |
|
|
|
if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST ) |
|
{ |
|
float flDampSpeed = 1.0f; |
|
float flDampRotSpeed = 1.0f; |
|
m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed ); |
|
} |
|
|
|
SetEngineDisabled( false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Class factory |
|
//----------------------------------------------------------------------------- |
|
IPhysicsVehicleController *CreateVehicleController( CPhysicsEnvironment *pEnv, CPhysicsObject *pBodyObject, const vehicleparams_t ¶ms, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace ) |
|
{ |
|
CVehicleController *pController = new CVehicleController( params, pEnv, nVehicleType, pGameTrace ); |
|
pController->InitCarSystem( pBodyObject ); |
|
return pController; |
|
} |
|
|
|
bool SavePhysicsVehicleController( const physsaveparams_t ¶ms, CVehicleController *pVehicleController ) |
|
{ |
|
vphysics_save_cvehiclecontroller_t controllerTemplate; |
|
memset( &controllerTemplate, 0, sizeof(controllerTemplate) ); |
|
|
|
pVehicleController->WriteToTemplate( controllerTemplate ); |
|
params.pSave->WriteAll( &controllerTemplate ); |
|
|
|
return true; |
|
} |
|
|
|
bool RestorePhysicsVehicleController( const physrestoreparams_t ¶ms, CVehicleController **ppVehicleController ) |
|
{ |
|
*ppVehicleController = new CVehicleController; |
|
|
|
vphysics_save_cvehiclecontroller_t controllerTemplate; |
|
memset( &controllerTemplate, 0, sizeof(controllerTemplate) ); |
|
params.pRestore->ReadAll( &controllerTemplate ); |
|
|
|
(*ppVehicleController)->InitFromTemplate( static_cast<CPhysicsEnvironment *>(params.pEnvironment), |
|
params.pGameData, params.pGameTrace, controllerTemplate ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
|