Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifdef _WIN32
  8. #pragma warning (disable:4127)
  9. #pragma warning (disable:4244)
  10. #endif
  11. #include "cbase.h"
  12. #include "ivp_controller.hxx"
  13. #include "ivp_cache_object.hxx"
  14. #include "ivp_car_system.hxx"
  15. #include "ivp_constraint_car.hxx"
  16. #include "ivp_material.hxx"
  17. #include "vphysics/vehicles.h"
  18. #include "vphysics/friction.h"
  19. #include "physics_vehicle.h"
  20. #include "physics_controller_raycast_vehicle.h"
  21. #include "physics_airboat.h"
  22. #include "ivp_car_system.hxx"
  23. #include "ivp_listener_object.hxx"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define THROTTLE_OPPOSING_FORCE_EPSILON 5.0f
  27. #define VEHICLE_SKID_EPSILON 0.1f
  28. // y in/s = x miles/hour * (5280 * 12 (in / mile)) * (1 / 3600 (hour / sec) )
  29. //#define MPH2INS(x) ( (x) * 5280.0f * 12.0f / 3600.0f )
  30. //#define INS2MPH(x) ( (x) * 3600 * (1/5280.0f) * (1/12.0f) )
  31. #define MPH_TO_METERSPERSECOND 0.44707f
  32. #define METERSPERSECOND_TO_MPH (1.0f / MPH_TO_METERSPERSECOND)
  33. #define MPH_TO_GAMEVEL(x) (ConvertDistanceToHL( (x) * MPH_TO_METERSPERSECOND ))
  34. #define GAMEVEL_TO_MPH(x) (ConvertDistanceToIVP(x) * METERSPERSECOND_TO_MPH)
  35. #define FVEHICLE_THROTTLE_STOPPED 0x00000001
  36. #define FVEHICLE_HANDBRAKE_ON 0x00000002
  37. struct vphysics_save_cvehiclecontroller_t
  38. {
  39. CPhysicsObject *m_pCarBody;
  40. int m_wheelCount;
  41. vehicleparams_t m_vehicleData;
  42. vehicle_operatingparams_t m_currentState;
  43. float m_wheelRadius;
  44. float m_bodyMass;
  45. float m_totalWheelMass;
  46. float m_gravityLength;
  47. float m_torqueScale;
  48. CPhysicsObject *m_pWheels[VEHICLE_MAX_WHEEL_COUNT];
  49. Vector m_wheelPosition_Bs[VEHICLE_MAX_WHEEL_COUNT];
  50. Vector m_tracePosition_Bs[VEHICLE_MAX_WHEEL_COUNT];
  51. int m_vehicleFlags;
  52. unsigned int m_nTireType;
  53. unsigned int m_nVehicleType;
  54. bool m_bTraceData;
  55. bool m_bOccupied;
  56. bool m_bEngineDisable;
  57. float m_flVelocity[3];
  58. DECLARE_SIMPLE_DATADESC();
  59. };
  60. BEGIN_SIMPLE_DATADESC( vphysics_save_cvehiclecontroller_t )
  61. DEFINE_VPHYSPTR( m_pCarBody ),
  62. DEFINE_FIELD( m_wheelCount, FIELD_INTEGER ),
  63. DEFINE_EMBEDDED( m_vehicleData ),
  64. DEFINE_EMBEDDED( m_currentState ),
  65. DEFINE_FIELD( m_wheelCount, FIELD_INTEGER ),
  66. DEFINE_FIELD( m_bodyMass, FIELD_FLOAT ),
  67. DEFINE_FIELD( m_totalWheelMass, FIELD_FLOAT ),
  68. DEFINE_FIELD( m_gravityLength, FIELD_FLOAT ),
  69. DEFINE_FIELD( m_torqueScale, FIELD_FLOAT ),
  70. DEFINE_VPHYSPTR_ARRAY( m_pWheels, VEHICLE_MAX_WHEEL_COUNT ),
  71. DEFINE_ARRAY( m_wheelPosition_Bs, FIELD_VECTOR, VEHICLE_MAX_WHEEL_COUNT ),
  72. DEFINE_ARRAY( m_tracePosition_Bs, FIELD_VECTOR, VEHICLE_MAX_WHEEL_COUNT ),
  73. DEFINE_FIELD( m_vehicleFlags, FIELD_INTEGER ),
  74. DEFINE_FIELD( m_nTireType, FIELD_INTEGER ),
  75. DEFINE_FIELD( m_nVehicleType, FIELD_INTEGER ),
  76. DEFINE_FIELD( m_bTraceData, FIELD_BOOLEAN ),
  77. DEFINE_FIELD( m_bOccupied, FIELD_BOOLEAN ),
  78. DEFINE_FIELD( m_bEngineDisable, FIELD_BOOLEAN ),
  79. DEFINE_ARRAY( m_flVelocity, FIELD_FLOAT, 3 ),
  80. END_DATADESC()
  81. BEGIN_SIMPLE_DATADESC( vehicle_operatingparams_t )
  82. DEFINE_FIELD( speed, FIELD_FLOAT ),
  83. DEFINE_FIELD( engineRPM, FIELD_FLOAT ),
  84. DEFINE_FIELD( gear, FIELD_INTEGER ),
  85. DEFINE_FIELD( boostDelay, FIELD_FLOAT ),
  86. DEFINE_FIELD( boostTimeLeft, FIELD_INTEGER ),
  87. DEFINE_FIELD( skidSpeed, FIELD_FLOAT ),
  88. DEFINE_CUSTOM_FIELD( skidMaterial, MaterialIndexDataOps() ),
  89. DEFINE_FIELD( steeringAngle, FIELD_FLOAT ),
  90. DEFINE_FIELD( wheelsInContact, FIELD_INTEGER ),
  91. DEFINE_FIELD( wheelsNotInContact,FIELD_INTEGER ),
  92. DEFINE_FIELD( isTorqueBoosting, FIELD_BOOLEAN ),
  93. END_DATADESC()
  94. BEGIN_SIMPLE_DATADESC( vehicle_bodyparams_t )
  95. DEFINE_FIELD( massCenterOverride, FIELD_VECTOR ),
  96. DEFINE_FIELD( massOverride, FIELD_FLOAT ),
  97. DEFINE_FIELD( addGravity, FIELD_FLOAT ),
  98. DEFINE_FIELD( maxAngularVelocity, FIELD_FLOAT ),
  99. DEFINE_FIELD( tiltForce, FIELD_FLOAT ),
  100. DEFINE_FIELD( tiltForceHeight, FIELD_FLOAT ),
  101. DEFINE_FIELD( counterTorqueFactor, FIELD_FLOAT ),
  102. DEFINE_FIELD( keepUprightTorque, FIELD_FLOAT ),
  103. END_DATADESC()
  104. BEGIN_SIMPLE_DATADESC( vehicle_wheelparams_t )
  105. DEFINE_FIELD( radius, FIELD_FLOAT ),
  106. DEFINE_FIELD( mass, FIELD_FLOAT ),
  107. DEFINE_FIELD( inertia, FIELD_FLOAT ),
  108. DEFINE_FIELD( damping, FIELD_FLOAT ),
  109. DEFINE_FIELD( rotdamping, FIELD_FLOAT ),
  110. DEFINE_FIELD( frictionScale, FIELD_FLOAT ),
  111. DEFINE_CUSTOM_FIELD( materialIndex, MaterialIndexDataOps() ),
  112. DEFINE_CUSTOM_FIELD( brakeMaterialIndex, MaterialIndexDataOps() ),
  113. DEFINE_CUSTOM_FIELD( skidMaterialIndex, MaterialIndexDataOps() ),
  114. DEFINE_FIELD( springAdditionalLength, FIELD_FLOAT ),
  115. END_DATADESC()
  116. BEGIN_SIMPLE_DATADESC( vehicle_suspensionparams_t )
  117. DEFINE_FIELD( springConstant, FIELD_FLOAT ),
  118. DEFINE_FIELD( springDamping, FIELD_FLOAT ),
  119. DEFINE_FIELD( stabilizerConstant, FIELD_FLOAT ),
  120. DEFINE_FIELD( springDampingCompression, FIELD_FLOAT ),
  121. DEFINE_FIELD( maxBodyForce, FIELD_FLOAT ),
  122. END_DATADESC()
  123. BEGIN_SIMPLE_DATADESC( vehicle_axleparams_t )
  124. DEFINE_FIELD( offset, FIELD_VECTOR ),
  125. DEFINE_FIELD( wheelOffset, FIELD_VECTOR ),
  126. DEFINE_FIELD( raytraceCenterOffset, FIELD_VECTOR ),
  127. DEFINE_FIELD( raytraceOffset, FIELD_VECTOR ),
  128. DEFINE_EMBEDDED( wheels ),
  129. DEFINE_EMBEDDED( suspension ),
  130. DEFINE_FIELD( torqueFactor, FIELD_FLOAT ),
  131. DEFINE_FIELD( brakeFactor, FIELD_FLOAT ),
  132. END_DATADESC()
  133. BEGIN_SIMPLE_DATADESC( vehicle_steeringparams_t )
  134. DEFINE_FIELD( degreesSlow, FIELD_FLOAT ),
  135. DEFINE_FIELD( degreesFast, FIELD_FLOAT ),
  136. DEFINE_FIELD( degreesBoost, FIELD_FLOAT ),
  137. DEFINE_FIELD( steeringRateSlow, FIELD_FLOAT ),
  138. DEFINE_FIELD( steeringRateFast, FIELD_FLOAT ),
  139. DEFINE_FIELD( steeringRestRateSlow, FIELD_FLOAT ),
  140. DEFINE_FIELD( steeringRestRateFast, FIELD_FLOAT ),
  141. DEFINE_FIELD( throttleSteeringRestRateFactor, FIELD_FLOAT ),
  142. DEFINE_FIELD( boostSteeringRestRateFactor, FIELD_FLOAT ),
  143. DEFINE_FIELD( boostSteeringRateFactor, FIELD_FLOAT ),
  144. DEFINE_FIELD( steeringExponent, FIELD_FLOAT ),
  145. DEFINE_FIELD( speedSlow, FIELD_FLOAT ),
  146. DEFINE_FIELD( speedFast, FIELD_FLOAT ),
  147. DEFINE_FIELD( turnThrottleReduceSlow, FIELD_FLOAT ),
  148. DEFINE_FIELD( turnThrottleReduceFast, FIELD_FLOAT ),
  149. DEFINE_FIELD( powerSlideAccel, FIELD_FLOAT ),
  150. DEFINE_FIELD( brakeSteeringRateFactor, FIELD_FLOAT ),
  151. DEFINE_FIELD( isSkidAllowed, FIELD_BOOLEAN ),
  152. DEFINE_FIELD( dustCloud, FIELD_BOOLEAN ),
  153. END_DATADESC()
  154. BEGIN_SIMPLE_DATADESC( vehicle_engineparams_t )
  155. DEFINE_FIELD( horsepower, FIELD_FLOAT ),
  156. DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
  157. DEFINE_FIELD( maxRevSpeed, FIELD_FLOAT ),
  158. DEFINE_FIELD( maxRPM, FIELD_FLOAT ),
  159. DEFINE_FIELD( axleRatio, FIELD_FLOAT ),
  160. DEFINE_FIELD( throttleTime, FIELD_FLOAT ),
  161. DEFINE_FIELD( maxRPM, FIELD_FLOAT ),
  162. DEFINE_FIELD( isAutoTransmission, FIELD_BOOLEAN ),
  163. DEFINE_FIELD( gearCount, FIELD_INTEGER ),
  164. DEFINE_AUTO_ARRAY( gearRatio, FIELD_FLOAT ),
  165. DEFINE_FIELD( shiftUpRPM, FIELD_FLOAT ),
  166. DEFINE_FIELD( shiftDownRPM, FIELD_FLOAT ),
  167. DEFINE_FIELD( boostForce, FIELD_FLOAT ),
  168. DEFINE_FIELD( boostDuration, FIELD_FLOAT ),
  169. DEFINE_FIELD( boostDelay, FIELD_FLOAT ),
  170. DEFINE_FIELD( boostMaxSpeed, FIELD_FLOAT ),
  171. DEFINE_FIELD( autobrakeSpeedGain, FIELD_FLOAT ),
  172. DEFINE_FIELD( autobrakeSpeedFactor, FIELD_FLOAT ),
  173. DEFINE_FIELD( torqueBoost, FIELD_BOOLEAN ),
  174. END_DATADESC()
  175. BEGIN_SIMPLE_DATADESC( vehicleparams_t )
  176. DEFINE_FIELD( axleCount, FIELD_INTEGER ),
  177. DEFINE_FIELD( wheelsPerAxle, FIELD_INTEGER ),
  178. DEFINE_EMBEDDED( body ),
  179. DEFINE_EMBEDDED_AUTO_ARRAY( axles ),
  180. DEFINE_EMBEDDED( engine ),
  181. DEFINE_EMBEDDED( steering ),
  182. END_DATADESC()
  183. bool IsVehicleWheel( IVP_Real_Object *pivp )
  184. {
  185. CPhysicsObject *pObject = static_cast<CPhysicsObject *>(pivp->client_data);
  186. // FIXME: Check why this is null! It occurs when jumping the ravine in seafloor
  187. if (!pObject)
  188. return false;
  189. return (pObject->CallbackFlags() & CALLBACK_IS_VEHICLE_WHEEL) ? true : false;
  190. }
  191. inline bool IsMoveable( IVP_Real_Object *pObject )
  192. {
  193. IVP_Core *pCore = pObject->get_core();
  194. if ( pCore->pinned || pCore->physical_unmoveable )
  195. return false;
  196. return true;
  197. }
  198. inline bool IVPFloatPointIsZero( const IVP_U_Float_Point &test )
  199. {
  200. const float eps = 1e-4f;
  201. return test.quad_length() < eps ? true : false;
  202. }
  203. bool ShouldOverrideWheelContactFriction( float *pFrictionOut, IVP_Real_Object *pivp0, IVP_Real_Object *pivp1, IVP_U_Float_Point *pNormal )
  204. {
  205. if ( !pivp0->get_core()->car_wheel && !pivp1->get_core()->car_wheel )
  206. return false;
  207. if ( !IsVehicleWheel(pivp0) )
  208. {
  209. if ( !IsVehicleWheel(pivp1) )
  210. return false;
  211. // swap so pivp0 is a wheel
  212. IVP_Real_Object *pTmp = pivp0;
  213. pivp0 = pivp1;
  214. pivp1 = pTmp;
  215. }
  216. // if we got here then pivp0 is a car wheel object
  217. // BUGBUG: IVP sometimes sends us a bogus normal
  218. // when doing a material realc on existing contacts!
  219. if ( !IVPFloatPointIsZero(pNormal) )
  220. {
  221. IVP_U_Float_Point normalWheelSpace;
  222. pivp0->get_core()->get_m_world_f_core_PSI()->vimult3( pNormal, &normalWheelSpace );
  223. if ( fabs(normalWheelSpace.k[0]) > 0.2588f ) // 15 degree wheel cone
  224. {
  225. // adjust friction here, this isn't a valid part of the wheel for contact, set friction to zero
  226. //Vector tmp;ConvertDirectionToHL( normalWheelSpace, tmp );Msg("Wheel sliding on surface %.2f %.2f %.2f\n", tmp.x, tmp.y, tmp.z );
  227. *pFrictionOut = 0;
  228. return true;
  229. }
  230. }
  231. // was car wheel, but didn't adjust - use default friction
  232. return false;
  233. }
  234. class CVehicleController : public IPhysicsVehicleController, public IVP_Listener_Object
  235. {
  236. public:
  237. CVehicleController( );
  238. CVehicleController( const vehicleparams_t &params, CPhysicsEnvironment *pEnv, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace );
  239. ~CVehicleController();
  240. // CVehicleController
  241. void InitCarSystem( CPhysicsObject *pBodyObject );
  242. // IPhysicsVehicleController
  243. void Update( float dt, vehicle_controlparams_t &controls );
  244. float UpdateBooster( float dt );
  245. void SetSpringLength(int wheelIndex, float length);
  246. const vehicle_operatingparams_t &GetOperatingParams() { return m_currentState; }
  247. const vehicleparams_t &GetVehicleParams() { return m_vehicleData; }
  248. vehicleparams_t &GetVehicleParamsForChange() { return m_vehicleData; }
  249. int GetWheelCount(void) { return m_wheelCount; };
  250. IPhysicsObject* GetWheel(int index);
  251. virtual bool GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps );
  252. void SetWheelFriction(int wheelIndex, float friction);
  253. void SetEngineDisabled( bool bDisable ) { m_bEngineDisable = bDisable; }
  254. bool IsEngineDisabled( void ) { return m_bEngineDisable; }
  255. // Save/load
  256. void WriteToTemplate( vphysics_save_cvehiclecontroller_t &controllerTemplate );
  257. void InitFromTemplate( CPhysicsEnvironment *pEnv, void *pGameData, IPhysicsGameTrace *pGameTrace, const vphysics_save_cvehiclecontroller_t &controllerTemplate );
  258. void VehicleDataReload();
  259. // Debug
  260. void GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem );
  261. // IVP_Listener_Object
  262. // Object listener, only hook delete
  263. virtual void event_object_deleted( IVP_Event_Object *);
  264. virtual void event_object_created( IVP_Event_Object *) {}
  265. virtual void event_object_revived( IVP_Event_Object *) {}
  266. virtual void event_object_frozen ( IVP_Event_Object *) {}
  267. // Entry/Exit
  268. void OnVehicleEnter( void );
  269. void OnVehicleExit( void );
  270. protected:
  271. void CreateIVPObjects( );
  272. void ShutdownCarSystem();
  273. void InitVehicleData( const vehicleparams_t &params );
  274. void InitCarSystemBody( IVP_Template_Car_System &ivpVehicleData );
  275. void InitCarSystemWheels( IVP_Template_Car_System &ivpVehicleData );
  276. void AttachListener();
  277. IVP_Real_Object *CreateWheel( int wheelIndex, vehicle_axleparams_t &axle );
  278. void CreateTraceData( int wheelIndex, vehicle_axleparams_t &axle );
  279. // Update.
  280. void UpdateSteering( const vehicle_controlparams_t &controls, float flDeltaTime, float flSpeed );
  281. void UpdatePowerslide( const vehicle_controlparams_t &controls, bool bPowerslide, float flSpeed );
  282. void UpdateEngine( const vehicle_controlparams_t &controls, float flDeltaTime, float flThrottle, float flBrake, bool bHandbrake, bool bPowerslide );
  283. bool UpdateEngineTurboStart( const vehicle_controlparams_t &controls, float flDeltaTime );
  284. void UpdateEngineTurboFinish( void );
  285. void UpdateHandbrake( const vehicle_controlparams_t &controls, float flThrottle, bool bHandbrake, bool bPowerslide );
  286. void UpdateSkidding( bool bHandbrake );
  287. void UpdateExtraForces( void );
  288. void UpdateWheelPositions( void );
  289. float CalcSteering( float dt, float speed, float steering, bool bAnalog );
  290. void CalcEngine( float throttle, float brake_val, bool handbrake, float steeringVal, bool torqueBoost );
  291. void CalcEngineTransmission( float flThrottle );
  292. virtual bool IsBoosting( void );
  293. private:
  294. void ResetState();
  295. IVP_Car_System *m_pCarSystem;
  296. CPhysicsObject *m_pCarBody;
  297. CPhysicsEnvironment *m_pEnv;
  298. IPhysicsGameTrace *m_pGameTrace;
  299. int m_wheelCount;
  300. vehicleparams_t m_vehicleData;
  301. vehicle_operatingparams_t m_currentState;
  302. float m_wheelRadius;
  303. float m_bodyMass;
  304. float m_totalWheelMass;
  305. float m_gravityLength;
  306. float m_torqueScale;
  307. CPhysicsObject *m_pWheels[VEHICLE_MAX_WHEEL_COUNT];
  308. IVP_U_Float_Point m_wheelPosition_Bs[VEHICLE_MAX_WHEEL_COUNT];
  309. IVP_U_Float_Point m_tracePosition_Bs[VEHICLE_MAX_WHEEL_COUNT];
  310. int m_vehicleFlags;
  311. unsigned int m_nTireType;
  312. unsigned int m_nVehicleType;
  313. bool m_bTraceData;
  314. bool m_bOccupied;
  315. bool m_bEngineDisable;
  316. float m_flVelocity[3];
  317. };
  318. CVehicleController::CVehicleController( const vehicleparams_t &params, CPhysicsEnvironment *pEnv, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace )
  319. {
  320. m_pEnv = pEnv;
  321. m_pGameTrace = pGameTrace;
  322. m_nVehicleType = nVehicleType;
  323. InitVehicleData( params );
  324. ResetState();
  325. }
  326. CVehicleController::CVehicleController()
  327. {
  328. ResetState();
  329. }
  330. void CVehicleController::ResetState()
  331. {
  332. m_pCarSystem = NULL;
  333. m_flVelocity[0] = m_flVelocity[1]= m_flVelocity[2] = 0.0f;
  334. for ( int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; i++ )
  335. {
  336. m_pWheels[i] = NULL;
  337. }
  338. m_pCarBody = NULL;
  339. m_torqueScale = 1;
  340. m_wheelCount = 0;
  341. m_wheelRadius = 0;
  342. memset( &m_currentState, 0, sizeof(m_currentState) );
  343. m_bodyMass = 0;
  344. m_vehicleFlags = 0;
  345. memset( m_wheelPosition_Bs, 0, sizeof(m_wheelPosition_Bs) );
  346. memset( m_tracePosition_Bs, 0, sizeof(m_tracePosition_Bs) );
  347. m_bTraceData = false;
  348. if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
  349. {
  350. m_bTraceData = true;
  351. }
  352. m_nTireType = VEHICLE_TIRE_NORMAL;
  353. m_bOccupied = false;
  354. m_bEngineDisable = false;
  355. }
  356. CVehicleController::~CVehicleController()
  357. {
  358. ShutdownCarSystem();
  359. }
  360. IPhysicsObject* CVehicleController::GetWheel( int index )
  361. {
  362. // TODO: This is getting messy.
  363. if ( m_nVehicleType == VEHICLE_TYPE_CAR_WHEELS )
  364. {
  365. return m_pWheels[index];
  366. }
  367. else if ( m_nVehicleType == VEHICLE_TYPE_CAR_RAYCAST && m_pCarSystem )
  368. {
  369. return static_cast<CPhysics_Car_System_Raycast_Wheels*>( m_pCarSystem )->GetWheel( index );
  370. }
  371. else if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST && m_pCarSystem )
  372. {
  373. return static_cast<CPhysics_Airboat*>( m_pCarSystem )->GetWheel( index );
  374. }
  375. return NULL;
  376. }
  377. void CVehicleController::SetWheelFriction(int wheelIndex, float friction)
  378. {
  379. CPhysics_Airboat *pAirboat = static_cast<CPhysics_Airboat*>( m_pCarSystem );
  380. if ( !pAirboat )
  381. return;
  382. pAirboat->SetWheelFriction( wheelIndex, friction );
  383. }
  384. bool CVehicleController::GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps )
  385. {
  386. bool bSet = false;
  387. if ( index < m_wheelCount )
  388. {
  389. IPhysicsFrictionSnapshot *pSnapshot = m_pWheels[index]->CreateFrictionSnapshot();
  390. float forceMax = -1.0f;
  391. m_pWheels[index]->GetPosition( pContactPoint, NULL );
  392. while ( pSnapshot->IsValid() )
  393. {
  394. float thisForce = pSnapshot->GetNormalForce();
  395. if ( thisForce > forceMax )
  396. {
  397. forceMax = thisForce;
  398. if ( pContactPoint )
  399. {
  400. pSnapshot->GetContactPoint( *pContactPoint );
  401. }
  402. if ( pSurfaceProps )
  403. {
  404. *pSurfaceProps = pSnapshot->GetMaterial(1);
  405. }
  406. bSet = true;
  407. }
  408. pSnapshot->NextFrictionData();
  409. }
  410. m_pWheels[index]->DestroyFrictionSnapshot(pSnapshot);
  411. }
  412. else
  413. {
  414. if ( pContactPoint )
  415. {
  416. pContactPoint->Init();
  417. }
  418. if ( pSurfaceProps )
  419. {
  420. *pSurfaceProps = 0;
  421. }
  422. }
  423. return bSet;
  424. }
  425. void CVehicleController::AttachListener()
  426. {
  427. m_pCarBody->GetObject()->add_listener_object( this );
  428. }
  429. void CVehicleController::event_object_deleted( IVP_Event_Object *pEvent )
  430. {
  431. // the car system's constraint solver is going to delete itself now, so NULL the car system.
  432. m_pCarSystem->event_object_deleted( pEvent );
  433. m_pCarSystem = NULL;
  434. ShutdownCarSystem();
  435. }
  436. IVP_Real_Object *CVehicleController::CreateWheel( int wheelIndex, vehicle_axleparams_t &axle )
  437. {
  438. if ( wheelIndex >= VEHICLE_MAX_WHEEL_COUNT )
  439. return NULL;
  440. // HACKHACK: In Save/load, the wheel was reloaded, so pretend to create it
  441. // ALSO NOTE: Save/load puts the results into m_pWheels regardless of vehicle type!!!
  442. // That's why I'm not calling GetWheel().
  443. if ( m_pWheels[wheelIndex] )
  444. {
  445. CPhysicsObject *pWheelObject = static_cast<CPhysicsObject *>(m_pWheels[wheelIndex]);
  446. return pWheelObject->GetObject();
  447. }
  448. objectparams_t params;
  449. memset( &params, 0, sizeof(params) );
  450. Vector bodyPosition;
  451. QAngle bodyAngles;
  452. m_pCarBody->GetPosition( &bodyPosition, &bodyAngles );
  453. matrix3x4_t matrix;
  454. AngleMatrix( bodyAngles, bodyPosition, matrix );
  455. Vector position = axle.offset;
  456. // BUGBUG: This only works with 2 wheels per axle
  457. if ( wheelIndex & 1 )
  458. {
  459. position += axle.wheelOffset;
  460. }
  461. else
  462. {
  463. position -= axle.wheelOffset;
  464. }
  465. Vector wheelPositionHL;
  466. VectorTransform( position, matrix, wheelPositionHL );
  467. params.damping = axle.wheels.damping;
  468. params.dragCoefficient = 0;
  469. params.enableCollisions = false;
  470. params.inertia = axle.wheels.inertia;
  471. params.mass = axle.wheels.mass;
  472. params.pGameData = m_pCarBody->GetGameData();
  473. params.pName = "VehicleWheel";
  474. params.rotdamping = axle.wheels.rotdamping;
  475. params.rotInertiaLimit = 0;
  476. params.massCenterOverride = NULL;
  477. // needs to be in HL units because we're calling through the "outer" interface to create
  478. // the wheels
  479. float radius = axle.wheels.radius;
  480. float r3 = radius * radius * radius;
  481. params.volume = (4 / 3) * M_PI * r3;
  482. CPhysicsObject *pWheel = (CPhysicsObject *)m_pEnv->CreateSphereObject( radius, axle.wheels.materialIndex, wheelPositionHL, bodyAngles, &params, false );
  483. pWheel->Wake();
  484. // UNDONE: only mask off some of these flags?
  485. unsigned int flags = pWheel->CallbackFlags();
  486. flags = 0;
  487. pWheel->SetCallbackFlags( flags );
  488. // copy the body's game flags
  489. pWheel->SetGameFlags( m_pCarBody->GetGameFlags() );
  490. // cache the wheel object pointer
  491. m_pWheels[wheelIndex] = pWheel;
  492. IVP_U_Point wheelPositionIVP, wheelPositionBs;
  493. ConvertPositionToIVP( wheelPositionHL, wheelPositionIVP );
  494. TransformIVPToLocal( wheelPositionIVP, wheelPositionBs, m_pCarBody->GetObject(), true );
  495. m_wheelPosition_Bs[wheelIndex].set_to_zero();
  496. m_wheelPosition_Bs[wheelIndex].set( &wheelPositionBs );
  497. pWheel->AddCallbackFlags( CALLBACK_IS_VEHICLE_WHEEL );
  498. return pWheel->GetObject();
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose:
  502. //-----------------------------------------------------------------------------
  503. void CVehicleController::CreateTraceData( int wheelIndex, vehicle_axleparams_t &axle )
  504. {
  505. if ( wheelIndex >= VEHICLE_MAX_WHEEL_COUNT )
  506. return;
  507. objectparams_t params;
  508. memset( &params, 0, sizeof( params ) );
  509. Vector bodyPosition;
  510. QAngle bodyAngles;
  511. matrix3x4_t matrix;
  512. m_pCarBody->GetPosition( &bodyPosition, &bodyAngles );
  513. AngleMatrix( bodyAngles, bodyPosition, matrix );
  514. Vector tracePosition = axle.raytraceCenterOffset;
  515. // BUGBUG: This only works with 2 wheels per axle
  516. if ( wheelIndex & 1 )
  517. {
  518. tracePosition += axle.raytraceOffset;
  519. }
  520. else
  521. {
  522. tracePosition -= axle.raytraceOffset;
  523. }
  524. Vector tracePositionHL;
  525. VectorTransform( tracePosition, matrix, tracePositionHL );
  526. IVP_U_Point tracePositionIVP, tracePositionBs;
  527. ConvertPositionToIVP( tracePositionHL, tracePositionIVP );
  528. TransformIVPToLocal( tracePositionIVP, tracePositionBs, m_pCarBody->GetObject(), true );
  529. m_tracePosition_Bs[wheelIndex].set_to_zero();
  530. m_tracePosition_Bs[wheelIndex].set( &tracePositionBs );
  531. }
  532. void CVehicleController::CreateIVPObjects( )
  533. {
  534. // Initialize the car system (body and wheels).
  535. IVP_Template_Car_System ivpVehicleData( m_wheelCount, m_vehicleData.axleCount );
  536. InitCarSystemBody( ivpVehicleData );
  537. InitCarSystemWheels( ivpVehicleData );
  538. BEGIN_IVP_ALLOCATION();
  539. // Raycast Car
  540. switch ( m_nVehicleType )
  541. {
  542. case VEHICLE_TYPE_CAR_WHEELS:
  543. m_pCarSystem = new IVP_Car_System_Real_Wheels( m_pEnv->GetIVPEnvironment(), &ivpVehicleData );
  544. break
  545. ;
  546. case VEHICLE_TYPE_CAR_RAYCAST:
  547. m_pCarSystem = new CPhysics_Car_System_Raycast_Wheels( m_pEnv->GetIVPEnvironment(), &ivpVehicleData );
  548. break;
  549. case VEHICLE_TYPE_AIRBOAT_RAYCAST:
  550. m_pCarSystem = new CPhysics_Airboat( m_pEnv->GetIVPEnvironment(), &ivpVehicleData, m_pGameTrace );
  551. break;
  552. }
  553. AttachListener();
  554. END_IVP_ALLOCATION();
  555. }
  556. void CVehicleController::InitCarSystem( CPhysicsObject *pBodyObject )
  557. {
  558. if ( m_pCarSystem )
  559. {
  560. ShutdownCarSystem();
  561. }
  562. // Car body.
  563. m_pCarBody = pBodyObject;
  564. m_bodyMass = m_pCarBody->GetMass();
  565. m_gravityLength = m_pEnv->GetIVPEnvironment()->get_gravity()->real_length();
  566. // Setup axle/wheel counts.
  567. m_wheelCount = m_vehicleData.axleCount * m_vehicleData.wheelsPerAxle;
  568. CreateIVPObjects();
  569. if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
  570. {
  571. float flDampSpeed = 1.0f;
  572. float flDampRotSpeed = 1.0f;
  573. m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed );
  574. }
  575. }
  576. void CVehicleController::VehicleDataReload()
  577. {
  578. // compute torque normalization factor
  579. m_torqueScale = 1;
  580. // Clear accumulation.
  581. float totalTorqueDistribution = 0.0f;
  582. for ( int i = 0; i < m_vehicleData.axleCount; i++ )
  583. {
  584. totalTorqueDistribution += m_vehicleData.axles[i].torqueFactor;
  585. }
  586. if ( totalTorqueDistribution > 0 )
  587. {
  588. m_torqueScale /= totalTorqueDistribution;
  589. }
  590. // input speed is in miles/hour. Convert to in/s
  591. m_vehicleData.engine.maxSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.maxSpeed);
  592. m_vehicleData.engine.maxRevSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.maxRevSpeed);
  593. m_vehicleData.engine.boostMaxSpeed = MPH_TO_GAMEVEL(m_vehicleData.engine.boostMaxSpeed);
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Purpose: Setup the body parameters.
  597. //-----------------------------------------------------------------------------
  598. void CVehicleController::InitCarSystemBody( IVP_Template_Car_System &ivpVehicleData )
  599. {
  600. ivpVehicleData.car_body = m_pCarBody->GetObject();
  601. ivpVehicleData.index_x = IVP_INDEX_X;
  602. ivpVehicleData.index_y = IVP_INDEX_Y;
  603. ivpVehicleData.index_z = IVP_INDEX_Z;
  604. ivpVehicleData.body_counter_torque_factor = m_vehicleData.body.counterTorqueFactor;
  605. ivpVehicleData.body_down_force_vertical_offset = ConvertDistanceToIVP( m_vehicleData.body.tiltForceHeight );
  606. ivpVehicleData.extra_gravity_force_value = m_vehicleData.body.addGravity * m_gravityLength * m_bodyMass;
  607. ivpVehicleData.extra_gravity_height_offset = 0;
  608. #if 0
  609. // HACKHACK: match example
  610. ivpVehicleData.extra_gravity_force_value = 1.2;
  611. ivpVehicleData.body_down_force_vertical_offset = 2;
  612. #endif
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Purpose: Setup the wheel paramters.
  616. //-----------------------------------------------------------------------------
  617. void CVehicleController::InitCarSystemWheels( IVP_Template_Car_System &ivpVehicleData )
  618. {
  619. int wheelIndex = 0;
  620. m_wheelRadius = 0;
  621. m_totalWheelMass = 0;
  622. int i;
  623. for ( i = 0; i < m_vehicleData.axleCount; i++ )
  624. {
  625. for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ )
  626. {
  627. IVP_Real_Object *pWheel = CreateWheel( wheelIndex, m_vehicleData.axles[i] );
  628. if ( pWheel )
  629. {
  630. // Create ray trace data for wheel.
  631. if ( m_bTraceData )
  632. {
  633. CreateTraceData( wheelIndex, m_vehicleData.axles[i] );
  634. }
  635. ivpVehicleData.car_wheel[wheelIndex] = pWheel;
  636. ivpVehicleData.wheel_radius[wheelIndex] = pWheel->get_core()->upper_limit_radius;
  637. ivpVehicleData.wheel_reversed_sign[wheelIndex] = 1.0;
  638. // only for raycast car
  639. ivpVehicleData.friction_of_wheel[wheelIndex] = m_vehicleData.axles[i].wheels.frictionScale;
  640. ivpVehicleData.spring_constant[wheelIndex] = m_vehicleData.axles[i].suspension.springConstant * m_bodyMass;
  641. ivpVehicleData.spring_dampening[wheelIndex] = m_vehicleData.axles[i].suspension.springDamping * m_bodyMass;
  642. ivpVehicleData.spring_dampening_compression[wheelIndex] = m_vehicleData.axles[i].suspension.springDampingCompression * m_bodyMass;
  643. ivpVehicleData.max_body_force[wheelIndex] = m_vehicleData.axles[i].suspension.maxBodyForce * m_bodyMass;
  644. ivpVehicleData.spring_pre_tension[wheelIndex] = -ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.springAdditionalLength );
  645. ivpVehicleData.wheel_pos_Bos[wheelIndex] = m_wheelPosition_Bs[wheelIndex];
  646. if ( m_bTraceData )
  647. {
  648. ivpVehicleData.trace_pos_Bos[wheelIndex] = m_tracePosition_Bs[wheelIndex];
  649. }
  650. m_totalWheelMass += m_vehicleData.axles[i].wheels.mass;
  651. }
  652. }
  653. ivpVehicleData.stabilizer_constant[i] = m_vehicleData.axles[i].suspension.stabilizerConstant * m_bodyMass;
  654. // this should output in radians per second
  655. float radius = ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius );
  656. float totalMaxSpeed = max( m_vehicleData.engine.boostMaxSpeed, m_vehicleData.engine.maxSpeed );
  657. ivpVehicleData.wheel_max_rotation_speed[i] = totalMaxSpeed / radius;
  658. if ( radius > m_wheelRadius )
  659. {
  660. m_wheelRadius = radius;
  661. }
  662. }
  663. for ( i = 0; i < m_wheelCount; i++ )
  664. {
  665. m_pWheels[i]->EnableCollisions( true );
  666. }
  667. }
  668. void CVehicleController::ShutdownCarSystem()
  669. {
  670. delete m_pCarSystem;
  671. m_pCarSystem = NULL;
  672. for ( int i = 0; i < m_wheelCount; i++ )
  673. {
  674. if ( m_pWheels[i] )
  675. {
  676. m_pEnv->DestroyObject( m_pWheels[i] );
  677. }
  678. m_pWheels[i] = NULL;
  679. }
  680. }
  681. void CVehicleController::InitVehicleData( const vehicleparams_t &params )
  682. {
  683. m_vehicleData = params;
  684. VehicleDataReload();
  685. }
  686. void CVehicleController::SetSpringLength(int wheelIndex, float length)
  687. {
  688. m_pCarSystem->change_spring_length((IVP_POS_WHEEL)wheelIndex, length);
  689. }
  690. //-----------------------------------------------------------------------------
  691. // Purpose: Allows booster timer to run,
  692. // Returns: true if time still exists
  693. // false if timer has run out (i.e. can use boost again)
  694. //-----------------------------------------------------------------------------
  695. float CVehicleController::UpdateBooster( float dt )
  696. {
  697. m_pCarSystem->update_booster( dt );
  698. m_currentState.boostDelay = m_pCarSystem->get_booster_delay();
  699. return m_currentState.boostDelay;
  700. }
  701. //-----------------------------------------------------------------------------
  702. // Purpose: Are whe boosting?
  703. //-----------------------------------------------------------------------------
  704. bool CVehicleController::IsBoosting( void )
  705. {
  706. return ( m_pCarSystem->get_booster_time_to_go() > 0.0f );
  707. }
  708. //-----------------------------------------------------------------------------
  709. // Purpose: Update the vehicle controller.
  710. //-----------------------------------------------------------------------------
  711. void CVehicleController::Update( float dt, vehicle_controlparams_t &controlsIn )
  712. {
  713. vehicle_controlparams_t controls = controlsIn;
  714. // Speed.
  715. m_currentState.speed = ConvertDistanceToHL( m_pCarSystem->get_body_speed() );
  716. float flSpeed = GAMEVEL_TO_MPH( m_currentState.speed );
  717. float flAbsSpeed = fabsf( flSpeed );
  718. // Calculate the throttle and brake values.
  719. float flThrottle = controls.throttle;
  720. bool bHandbrake = controls.handbrake;
  721. float flBrake = controls.brake;
  722. bool bPowerslide = bHandbrake && ( flAbsSpeed > 18.0f );
  723. if ( bHandbrake )
  724. {
  725. flThrottle = 0.0f;
  726. }
  727. if ( IsBoosting() )
  728. {
  729. controls.boost = true;
  730. flThrottle = flThrottle < 0.0f ? -1.0f : 1.0f;
  731. }
  732. if ( flThrottle == 0.0f && flBrake == 0.0f && !bHandbrake )
  733. {
  734. flBrake = 0.1f;
  735. }
  736. // Update steering.
  737. UpdateSteering( controls, dt, flAbsSpeed );
  738. // Update powerslide.
  739. UpdatePowerslide( controls, bPowerslide, flSpeed );
  740. // Update engine.
  741. UpdateEngine( controls, dt, flThrottle, flBrake, bHandbrake, bPowerslide );
  742. // Update handbrake.
  743. UpdateHandbrake( controls, flThrottle, bHandbrake, bPowerslide );
  744. // Update skidding.
  745. UpdateSkidding( bHandbrake );
  746. // Apply the extra forces to the car (downward, counter-torque, etc.)
  747. UpdateExtraForces();
  748. // Update the physical position of the wheels for raycast vehicles.
  749. UpdateWheelPositions();
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose: Update the steering on the vehicle.
  753. //-----------------------------------------------------------------------------
  754. void CVehicleController::UpdateSteering( const vehicle_controlparams_t &controls, float flDeltaTime, float flSpeed )
  755. {
  756. // Steering - IVP steering is in radians.
  757. float flSteeringAngle = CalcSteering( flDeltaTime, flSpeed, controls.steering, controls.bAnalogSteering );
  758. m_pCarSystem->do_steering( DEG2RAD( flSteeringAngle ), controls.bAnalogSteering );
  759. m_currentState.steeringAngle = flSteeringAngle;
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Purpose: Update the powerslide state (wheel materials).
  763. //-----------------------------------------------------------------------------
  764. void CVehicleController::UpdatePowerslide( const vehicle_controlparams_t &controls, bool bPowerslide, float flSpeed )
  765. {
  766. // Only allow skidding if it is allowed by the vehicle type.
  767. if ( !m_vehicleData.steering.isSkidAllowed )
  768. return;
  769. // Check to see if the vehicle is occupied.
  770. if ( !m_bOccupied )
  771. return;
  772. // Set the powerslide left/right.
  773. bool bPowerslideLeft = bPowerslide && controls.handbrakeLeft;
  774. bool bPowerslideRight = bPowerslide && controls.handbrakeRight;
  775. int iWheel = 0;
  776. unsigned int newTireType = VEHICLE_TIRE_NORMAL;
  777. if ( bPowerslideLeft || bPowerslideRight )
  778. {
  779. newTireType = VEHICLE_TIRE_POWERSLIDE;
  780. }
  781. else if ( bPowerslide )
  782. {
  783. newTireType = VEHICLE_TIRE_BRAKING;
  784. }
  785. if ( newTireType != m_nTireType )
  786. {
  787. for ( int iAxle = 0; iAxle < m_vehicleData.axleCount; ++iAxle )
  788. {
  789. int materialIndex = m_vehicleData.axles[iAxle].wheels.materialIndex;
  790. if ( newTireType == VEHICLE_TIRE_POWERSLIDE && ( m_vehicleData.axles[iAxle].wheels.skidMaterialIndex != - 1 ) )
  791. {
  792. materialIndex = m_vehicleData.axles[iAxle].wheels.skidMaterialIndex;
  793. }
  794. else if ( newTireType == VEHICLE_TIRE_BRAKING && ( m_vehicleData.axles[iAxle].wheels.brakeMaterialIndex != -1 ) )
  795. {
  796. materialIndex = m_vehicleData.axles[iAxle].wheels.brakeMaterialIndex;
  797. }
  798. for ( int iAxleWheel = 0; iAxleWheel < m_vehicleData.wheelsPerAxle; ++iAxleWheel, ++iWheel )
  799. {
  800. m_pWheels[iWheel]->SetMaterialIndex( materialIndex );
  801. }
  802. m_nTireType = newTireType;
  803. }
  804. }
  805. // Push the car a little.
  806. float flFrontAccel = 0.0f;
  807. float flRearAccel = 0.0f;
  808. if ( flSpeed > 0 && (bPowerslideLeft != bPowerslideRight) )
  809. {
  810. // NOTE: positive acceleration is to the left
  811. float powerSlide = RemapValClamped( flSpeed, m_vehicleData.steering.speedSlow, m_vehicleData.steering.speedFast, 0, 1 );
  812. float powerSlideAccel = ConvertDistanceToIVP( m_vehicleData.steering.powerSlideAccel);
  813. if ( bPowerslideLeft )
  814. {
  815. flFrontAccel = powerSlideAccel * powerSlide;
  816. flRearAccel = -powerSlideAccel * powerSlide;
  817. }
  818. else
  819. {
  820. flFrontAccel = -powerSlideAccel * powerSlide;
  821. flRearAccel = powerSlideAccel * powerSlide;
  822. }
  823. }
  824. m_pCarSystem->set_powerslide( flFrontAccel, flRearAccel );
  825. }
  826. //-----------------------------------------------------------------------------
  827. // Purpose:
  828. //-----------------------------------------------------------------------------
  829. void CVehicleController::UpdateEngine( const vehicle_controlparams_t &controls, float flDeltaTime,
  830. float flThrottle, float flBrake, bool bHandbrake, bool bPowerslide )
  831. {
  832. bool bTorqueBoost = UpdateEngineTurboStart( controls, flDeltaTime );
  833. CalcEngine( flThrottle, flBrake, bHandbrake, controls.steering, bTorqueBoost );
  834. UpdateEngineTurboFinish();
  835. }
  836. //-----------------------------------------------------------------------------
  837. // Purpose:
  838. //-----------------------------------------------------------------------------
  839. bool CVehicleController::UpdateEngineTurboStart( const vehicle_controlparams_t &controls, float flDeltaTime )
  840. {
  841. bool bTorqueBoost = false;
  842. if ( controls.boost > 0 )
  843. {
  844. if ( m_vehicleData.engine.torqueBoost )
  845. {
  846. // Turbo will be applied at the engine level.
  847. bTorqueBoost = true;
  848. m_pCarSystem->activate_booster( 0.0f, m_vehicleData.engine.boostDuration, m_vehicleData.engine.boostDelay );
  849. }
  850. else
  851. {
  852. // Activate the turbo force booster - applied to vehicle body.
  853. m_pCarSystem->activate_booster( m_vehicleData.engine.boostForce * controls.boost, m_vehicleData.engine.boostDuration, m_vehicleData.engine.boostDelay );
  854. }
  855. }
  856. m_pCarSystem->update_booster( flDeltaTime );
  857. m_currentState.boostDelay = m_pCarSystem->get_booster_delay();
  858. m_currentState.isTorqueBoosting = bTorqueBoost;
  859. return bTorqueBoost;
  860. }
  861. //-----------------------------------------------------------------------------
  862. // Purpose:
  863. //-----------------------------------------------------------------------------
  864. void CVehicleController::UpdateEngineTurboFinish( void )
  865. {
  866. if ( m_vehicleData.engine.boostDuration + m_vehicleData.engine.boostDelay > 0 ) // watch out for div by zero
  867. {
  868. if ( m_currentState.boostDelay > 0 )
  869. {
  870. m_currentState.boostTimeLeft = 100 - 100 * ( m_currentState.boostDelay / ( m_vehicleData.engine.boostDuration + m_vehicleData.engine.boostDelay ) );
  871. }
  872. else
  873. {
  874. m_currentState.boostTimeLeft = 100; // ready to go any time
  875. }
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // Purpose: Update the handbrake.
  880. //-----------------------------------------------------------------------------
  881. void CVehicleController::UpdateHandbrake( const vehicle_controlparams_t &controls, float flThrottle, bool bHandbrake, bool bPowerslide )
  882. {
  883. // Get the current vehicle speed.
  884. m_currentState.speed = ConvertDistanceToHL( m_pCarSystem->get_body_speed() );
  885. if ( !bPowerslide )
  886. {
  887. // HACK! Allowing you to overcome gravity at low throttle.
  888. if ( ( flThrottle < 0.0f && m_currentState.speed > THROTTLE_OPPOSING_FORCE_EPSILON ) ||
  889. ( flThrottle > 0.0f && m_currentState.speed < -THROTTLE_OPPOSING_FORCE_EPSILON ) )
  890. {
  891. bHandbrake = true;
  892. }
  893. }
  894. if ( bHandbrake )
  895. {
  896. // HACKHACK: only allow the handbrake when the wheels have contact with something
  897. // otherwise they will affect the car in an undesirable way
  898. bHandbrake = false;
  899. for ( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  900. {
  901. if ( m_pWheels[iWheel]->GetContactPoint(NULL, NULL) )
  902. {
  903. bHandbrake = true;
  904. break;
  905. }
  906. }
  907. }
  908. bool currentHandbrake = (m_vehicleFlags & FVEHICLE_HANDBRAKE_ON) ? true : false;
  909. if ( bHandbrake != currentHandbrake )
  910. {
  911. if ( bHandbrake )
  912. {
  913. m_vehicleFlags |= FVEHICLE_HANDBRAKE_ON;
  914. }
  915. else
  916. {
  917. m_vehicleFlags &= ~FVEHICLE_HANDBRAKE_ON;
  918. }
  919. for ( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  920. {
  921. m_pCarSystem->fix_wheel( ( IVP_POS_WHEEL )iWheel, bHandbrake ? IVP_TRUE : IVP_FALSE );
  922. }
  923. }
  924. }
  925. //-----------------------------------------------------------------------------
  926. // Purpose:
  927. //-----------------------------------------------------------------------------
  928. void CVehicleController::UpdateSkidding( bool bHandbrake )
  929. {
  930. m_currentState.skidSpeed = 0.0f;
  931. m_currentState.skidMaterial = 0;
  932. m_currentState.wheelsInContact = m_wheelCount;
  933. m_currentState.wheelsNotInContact = 0;
  934. if ( m_vehicleData.steering.isSkidAllowed )
  935. {
  936. // Estimate rot speed based on current speed and the front wheels radius
  937. float flAbsSpeed = fabs( m_currentState.speed );
  938. Vector contact;
  939. Vector velocity;
  940. int surfaceProps;
  941. m_currentState.wheelsInContact = 0;
  942. m_currentState.wheelsNotInContact = 0;
  943. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  944. {
  945. if ( GetWheelContactPoint( iWheel, &contact, &surfaceProps ) )
  946. {
  947. // NOTE: The wheel should be translating by the negative of the speed a point in contact with the surface
  948. // is moving. So the net velocity on the surface is zero if that wheel is 100% engaged in driving the car
  949. // any velocity in excess of this gets compared against the threshold for skidding
  950. m_pWheels[iWheel]->GetVelocityAtPoint( contact, &velocity );
  951. float speed = velocity.Length();
  952. if ( speed > m_currentState.skidSpeed || m_currentState.skidSpeed <= 0.0f )
  953. {
  954. m_currentState.skidSpeed = speed;
  955. m_currentState.skidMaterial = surfaceProps;
  956. }
  957. m_currentState.wheelsInContact++;
  958. }
  959. else
  960. {
  961. m_currentState.wheelsNotInContact++;
  962. }
  963. }
  964. // Check for locked wheels.
  965. if ( bHandbrake && ( flAbsSpeed > 30 ) )
  966. {
  967. m_currentState.skidSpeed = flAbsSpeed;
  968. }
  969. }
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose: Apply extra forces to the vehicle. The downward force, counter-
  973. // torque etc.
  974. //-----------------------------------------------------------------------------
  975. void CVehicleController::UpdateExtraForces( void )
  976. {
  977. // Extra downward force.
  978. IVP_Cache_Object *co = m_pCarBody->GetObject()->get_cache_object();
  979. float y_val = co->m_world_f_object.get_elem( IVP_INDEX_Y, IVP_INDEX_Y );
  980. if ( fabs( y_val ) < 0.05 )
  981. {
  982. m_pCarSystem->change_body_downforce( m_vehicleData.body.tiltForce * m_gravityLength * m_bodyMass );
  983. }
  984. else
  985. {
  986. m_pCarSystem->change_body_downforce( 0.0 );
  987. }
  988. co->remove_reference();
  989. // Counter-torque.
  990. if ( m_nVehicleType == VEHICLE_TYPE_CAR_WHEELS )
  991. {
  992. m_pCarSystem->update_body_countertorque();
  993. }
  994. // if the car has a global angular velocity limit, apply that constraint
  995. AngularImpulse angVel;
  996. m_pCarBody->GetVelocity( NULL, &angVel );
  997. if ( m_vehicleData.body.maxAngularVelocity > 0 && angVel.Length() > m_vehicleData.body.maxAngularVelocity )
  998. {
  999. VectorNormalize(angVel);
  1000. angVel *= m_vehicleData.body.maxAngularVelocity;
  1001. m_pCarBody->SetVelocityInstantaneous( NULL, &angVel );
  1002. }
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose: Update the physical position of the wheels for raycast vehicles.
  1006. // NOTE: Raycast boat doesn't have wheels.
  1007. //-----------------------------------------------------------------------------
  1008. void CVehicleController::UpdateWheelPositions( void )
  1009. {
  1010. if ( m_nVehicleType == VEHICLE_TYPE_CAR_RAYCAST )
  1011. {
  1012. m_pCarSystem->update_wheel_positions();
  1013. }
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Purpose:
  1017. //-----------------------------------------------------------------------------
  1018. float CVehicleController::CalcSteering( float dt, float speed, float steering, bool bAnalog )
  1019. {
  1020. float degrees = RemapValClamped( speed, m_vehicleData.steering.speedSlow, m_vehicleData.steering.speedFast, m_vehicleData.steering.degreesSlow, m_vehicleData.steering.degreesFast );
  1021. float speedGame = MPH_TO_GAMEVEL(speed);
  1022. if ( speedGame > m_vehicleData.engine.maxSpeed )
  1023. {
  1024. degrees = RemapValClamped( speedGame, m_vehicleData.engine.maxSpeed, m_vehicleData.engine.boostMaxSpeed, m_vehicleData.steering.degreesFast, m_vehicleData.steering.degreesBoost );
  1025. }
  1026. if ( m_vehicleData.steering.steeringExponent != 0 )
  1027. {
  1028. float sign = steering < 0 ? -1 : 1;
  1029. float absSteering = fabs(steering);
  1030. if ( bAnalog )
  1031. {
  1032. // analog steering is directly mapped, not integrated, so go ahead and map the full range using the exponent
  1033. // then clamp to the output cone - keeps stick position:turn rate constant
  1034. // NOTE: Also hardcode exponent to 2 because we can't add a script entry at this point
  1035. float output = pow(absSteering, 2.0f) * sign * m_vehicleData.steering.degreesSlow;
  1036. return clamp(output, -degrees, degrees );
  1037. }
  1038. // digital steering is integrated, keep time to full turn rate constant
  1039. return pow(absSteering, m_vehicleData.steering.steeringExponent) * sign * degrees;
  1040. }
  1041. return steering * degrees;
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Purpose:
  1045. //-----------------------------------------------------------------------------
  1046. void CVehicleController::CalcEngineTransmission( float flThrottle )
  1047. {
  1048. // Automatic Transmission?
  1049. if ( !m_vehicleData.engine.isAutoTransmission )
  1050. return;
  1051. // Calculate the average rotational speed of the vehicle's wheels.
  1052. float flAvgRotSpeed = 0.0;
  1053. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  1054. {
  1055. float flRotSpeed = fabs( m_pCarSystem->get_wheel_angular_velocity( IVP_POS_WHEEL( iWheel ) ) );
  1056. flAvgRotSpeed += flRotSpeed;
  1057. }
  1058. flAvgRotSpeed *= 0.5f / ( float )IVP_PI / m_wheelCount;
  1059. float flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60;
  1060. // Only shift up when going forward (throttling).
  1061. if ( flThrottle > 0.0f )
  1062. {
  1063. // Shift up?, top gear is gearcount-1 (0 based)
  1064. while ( ( flEstEngineRPM > m_vehicleData.engine.shiftUpRPM ) && ( m_currentState.gear < m_vehicleData.engine.gearCount-1 ) )
  1065. {
  1066. m_currentState.gear++;
  1067. flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60;
  1068. }
  1069. }
  1070. // Downshift?
  1071. while ( ( flEstEngineRPM < m_vehicleData.engine.shiftDownRPM ) && ( m_currentState.gear > 0 ) )
  1072. {
  1073. m_currentState.gear--;
  1074. flEstEngineRPM = flAvgRotSpeed * m_vehicleData.engine.axleRatio * m_vehicleData.engine.gearRatio[m_currentState.gear] * 60;
  1075. }
  1076. m_currentState.engineRPM = flEstEngineRPM;
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Purpose:
  1080. // throttle goes forward and backward, [-1, 1]
  1081. // brake_val [0..1]
  1082. //-----------------------------------------------------------------------------
  1083. void CVehicleController::CalcEngine( float throttle, float brake_val, bool handbrake, float steeringVal, bool torqueBoost )
  1084. {
  1085. // Update the engine transmission.
  1086. CalcEngineTransmission( throttle );
  1087. // Get the speed of the vehicle.
  1088. float flAbsSpeed = fabs( m_currentState.speed );
  1089. // Speed governor
  1090. if ( IsPC() )
  1091. {
  1092. float maxSpeed = torqueBoost ? m_vehicleData.engine.boostMaxSpeed : m_vehicleData.engine.maxSpeed;
  1093. maxSpeed = max(1.f,maxSpeed); // make sure this is non-zero before the divide
  1094. if ( (throttle > 0) && (flAbsSpeed > maxSpeed) )
  1095. {
  1096. float frac = flAbsSpeed / maxSpeed;
  1097. if ( frac > m_vehicleData.engine.autobrakeSpeedGain )
  1098. {
  1099. throttle = 0;
  1100. brake_val = (frac - 1.0f) * m_vehicleData.engine.autobrakeSpeedFactor;
  1101. if ( m_currentState.wheelsInContact == 0 )
  1102. {
  1103. brake_val = 0;
  1104. }
  1105. }
  1106. throttle *= 0.1f;
  1107. }
  1108. }
  1109. else // consoles
  1110. {
  1111. if ( ( throttle > 0 ) && ( ( !torqueBoost && flAbsSpeed > (m_vehicleData.engine.maxSpeed * throttle) ) ||
  1112. ( torqueBoost && flAbsSpeed > m_vehicleData.engine.boostMaxSpeed) ) )
  1113. {
  1114. throttle *= 0.1f;
  1115. }
  1116. }
  1117. // Check for reverse - both of these "governors" or horrible and need to be redone before we ship!
  1118. if ( ( throttle < 0 ) && ( !torqueBoost && ( flAbsSpeed > m_vehicleData.engine.maxRevSpeed ) ) )
  1119. {
  1120. throttle *= 0.1f;
  1121. }
  1122. if ( throttle != 0.0 )
  1123. {
  1124. m_vehicleFlags &= ~FVEHICLE_THROTTLE_STOPPED;
  1125. // calculate the force that propels the car
  1126. const float watt_per_hp = 745.0f;
  1127. const float seconds_per_minute = 60.0f;
  1128. float wheel_force_by_throttle = throttle *
  1129. m_vehicleData.engine.horsepower * (watt_per_hp * seconds_per_minute) *
  1130. m_vehicleData.engine.gearRatio[m_currentState.gear] * m_vehicleData.engine.axleRatio /
  1131. (m_vehicleData.engine.maxRPM * m_wheelRadius * (2 * IVP_PI));
  1132. if ( m_currentState.engineRPM >= m_vehicleData.engine.maxRPM )
  1133. {
  1134. wheel_force_by_throttle = 0;
  1135. }
  1136. int wheelIndex = 0;
  1137. for ( int i = 0; i < m_vehicleData.axleCount; i++ )
  1138. {
  1139. float axleFactor = m_vehicleData.axles[i].torqueFactor * m_torqueScale;
  1140. float boostFactor = 0.5f;
  1141. if ( torqueBoost && IsBoosting() )
  1142. {
  1143. // reduce the boost at low speeds and high turns since this usually just makes the tires spin
  1144. // this means you only get the full boost when travelling in a straight line at high speed
  1145. float speedFactor = RemapValClamped( flAbsSpeed, 0, m_vehicleData.engine.maxSpeed, 0.1f, 1.0f );
  1146. float turnFactor = 1.0f - (fabs(steeringVal) * 0.95f);
  1147. float dampedBoost = m_vehicleData.engine.boostForce * speedFactor * turnFactor;
  1148. if ( dampedBoost > boostFactor )
  1149. {
  1150. boostFactor = dampedBoost;
  1151. }
  1152. //Msg("Boost applied %.2f, speed %.2f, turn %.2f\n", boostFactor, speedFactor, turnFactor );
  1153. }
  1154. float axleTorque = boostFactor * wheel_force_by_throttle * axleFactor * ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius );
  1155. for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ )
  1156. {
  1157. float torqueVal = axleTorque;
  1158. m_pCarSystem->change_wheel_torque((IVP_POS_WHEEL)wheelIndex, torqueVal);
  1159. }
  1160. }
  1161. }
  1162. else if ( brake_val != 0 )
  1163. {
  1164. m_vehicleFlags &= ~FVEHICLE_THROTTLE_STOPPED;
  1165. // Brake to slow down the wheel.
  1166. float wheel_force_by_brake = brake_val * m_gravityLength * ( m_bodyMass + m_totalWheelMass );
  1167. float sign = m_currentState.speed >= 0.0f ? -1.0f : 1.0f;
  1168. int wheelIndex = 0;
  1169. for ( int i = 0; i < m_vehicleData.axleCount; i++ )
  1170. {
  1171. float torque_val = 0.5 * sign * wheel_force_by_brake * m_vehicleData.axles[i].brakeFactor * ConvertDistanceToIVP( m_vehicleData.axles[i].wheels.radius );
  1172. for ( int w = 0; w < m_vehicleData.wheelsPerAxle; w++, wheelIndex++ )
  1173. {
  1174. m_pCarSystem->change_wheel_torque( ( IVP_POS_WHEEL )wheelIndex, torque_val );
  1175. }
  1176. }
  1177. }
  1178. else if ( !(m_vehicleFlags & FVEHICLE_THROTTLE_STOPPED) )
  1179. {
  1180. m_vehicleFlags |= FVEHICLE_THROTTLE_STOPPED;
  1181. for ( int w = 0; w < m_wheelCount; w++ )
  1182. {
  1183. m_pCarSystem->change_wheel_torque((IVP_POS_WHEEL)w, 0);
  1184. }
  1185. }
  1186. // Update the throttle - primarily for the airboat!
  1187. m_pCarSystem->update_throttle( throttle );
  1188. }
  1189. //-----------------------------------------------------------------------------
  1190. // Purpose: Get debug rendering data from the ipion physics system.
  1191. //-----------------------------------------------------------------------------
  1192. void CVehicleController::GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem )
  1193. {
  1194. IVP_CarSystemDebugData_t carSystemDebugData;
  1195. memset(&carSystemDebugData,0,sizeof(carSystemDebugData));
  1196. m_pCarSystem->GetCarSystemDebugData( carSystemDebugData );
  1197. // Raycast car wheel trace data.
  1198. for ( int iWheel = 0; iWheel < VEHICLE_DEBUGRENDERDATA_MAX_WHEELS; ++iWheel )
  1199. {
  1200. debugCarSystem.vecWheelRaycasts[iWheel][0].x = carSystemDebugData.wheelRaycasts[iWheel][0].k[0];
  1201. debugCarSystem.vecWheelRaycasts[iWheel][0].y = carSystemDebugData.wheelRaycasts[iWheel][0].k[1];
  1202. debugCarSystem.vecWheelRaycasts[iWheel][0].z = carSystemDebugData.wheelRaycasts[iWheel][0].k[2];
  1203. debugCarSystem.vecWheelRaycasts[iWheel][1].x = carSystemDebugData.wheelRaycasts[iWheel][1].k[0];
  1204. debugCarSystem.vecWheelRaycasts[iWheel][1].y = carSystemDebugData.wheelRaycasts[iWheel][1].k[1];
  1205. debugCarSystem.vecWheelRaycasts[iWheel][1].z = carSystemDebugData.wheelRaycasts[iWheel][1].k[2];
  1206. debugCarSystem.vecWheelRaycastImpacts[iWheel] = debugCarSystem.vecWheelRaycasts[iWheel][0] + ( carSystemDebugData.wheelRaycastImpacts[iWheel] *
  1207. ( debugCarSystem.vecWheelRaycasts[iWheel][1] - debugCarSystem.vecWheelRaycasts[iWheel][0] ) );
  1208. }
  1209. ConvertPositionToHL( carSystemDebugData.backActuatorLeft, debugCarSystem.vecAxlePos[0] );
  1210. ConvertPositionToHL( carSystemDebugData.backActuatorRight, debugCarSystem.vecAxlePos[1] );
  1211. ConvertPositionToHL( carSystemDebugData.frontActuatorLeft, debugCarSystem.vecAxlePos[2] );
  1212. // vecAxlePos only has three elements so this line is illegal. The mapping of actuators
  1213. // to axles seems dodgy anyway.
  1214. //ConvertPositionToHL( carSystemDebugData.frontActuatorRight, debugCarSystem.vecAxlePos[3] );
  1215. }
  1216. //-----------------------------------------------------------------------------
  1217. // Save/load
  1218. //-----------------------------------------------------------------------------
  1219. void CVehicleController::WriteToTemplate( vphysics_save_cvehiclecontroller_t &controllerTemplate )
  1220. {
  1221. // Get rid of the handbrake flag. The car keeps the flag and will reset it fixing wheels,
  1222. // else the system thinks it already fixed the wheels on load and the car roles.
  1223. m_vehicleFlags &= ~FVEHICLE_HANDBRAKE_ON;
  1224. controllerTemplate.m_pCarBody = m_pCarBody;
  1225. controllerTemplate.m_wheelCount = m_wheelCount;
  1226. controllerTemplate.m_wheelRadius = m_wheelRadius;
  1227. controllerTemplate.m_bodyMass = m_bodyMass;
  1228. controllerTemplate.m_totalWheelMass = m_totalWheelMass;
  1229. controllerTemplate.m_gravityLength = m_gravityLength;
  1230. controllerTemplate.m_torqueScale = m_torqueScale;
  1231. controllerTemplate.m_vehicleFlags = m_vehicleFlags;
  1232. controllerTemplate.m_nTireType = m_nTireType;
  1233. controllerTemplate.m_nVehicleType = m_nVehicleType;
  1234. controllerTemplate.m_bTraceData = m_bTraceData;
  1235. controllerTemplate.m_bOccupied = m_bOccupied;
  1236. controllerTemplate.m_bEngineDisable = m_bEngineDisable;
  1237. memcpy( &controllerTemplate.m_currentState, &m_currentState, sizeof(m_currentState) );
  1238. memcpy( &controllerTemplate.m_vehicleData, &m_vehicleData, sizeof(m_vehicleData) );
  1239. for (int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; ++i )
  1240. {
  1241. controllerTemplate.m_pWheels[i] = m_pWheels[i];
  1242. ConvertPositionToHL( m_wheelPosition_Bs[i], controllerTemplate.m_wheelPosition_Bs[i] );
  1243. ConvertPositionToHL( m_tracePosition_Bs[i], controllerTemplate.m_tracePosition_Bs[i] );
  1244. }
  1245. m_flVelocity[0] = m_flVelocity[1] = m_flVelocity[2] = 0.0f;
  1246. if ( m_pCarBody )
  1247. {
  1248. IVP_U_Float_Point &speed = m_pCarBody->GetObject()->get_core()->speed;
  1249. controllerTemplate.m_flVelocity[0] = speed.k[0];
  1250. controllerTemplate.m_flVelocity[1] = speed.k[1];
  1251. controllerTemplate.m_flVelocity[2] = speed.k[2];
  1252. }
  1253. }
  1254. // JAY: Keep this around for now while we still have a bunch of games saved with the old
  1255. // vehicle controls. We won't ship this, but it lets us debug
  1256. #define OLD_SAVED_GAME 1
  1257. #if OLD_SAVED_GAME
  1258. #define SET_DEFAULT(x,y) { if ( x == 0 ) x = y; }
  1259. #endif
  1260. void CVehicleController::InitFromTemplate( CPhysicsEnvironment *pEnv, void *pGameData,
  1261. IPhysicsGameTrace *pGameTrace, const vphysics_save_cvehiclecontroller_t &controllerTemplate )
  1262. {
  1263. m_pEnv = pEnv;
  1264. m_pGameTrace = pGameTrace;
  1265. m_pCarBody = controllerTemplate.m_pCarBody;
  1266. m_wheelCount = controllerTemplate.m_wheelCount;
  1267. m_wheelRadius = controllerTemplate.m_wheelRadius;
  1268. m_bodyMass = controllerTemplate.m_bodyMass;
  1269. m_totalWheelMass = controllerTemplate.m_totalWheelMass;
  1270. m_gravityLength = controllerTemplate.m_gravityLength;
  1271. m_torqueScale = controllerTemplate.m_torqueScale;
  1272. m_vehicleFlags = controllerTemplate.m_vehicleFlags;
  1273. m_nTireType = controllerTemplate.m_nTireType;
  1274. m_nVehicleType = controllerTemplate.m_nVehicleType;
  1275. m_bTraceData = controllerTemplate.m_bTraceData;
  1276. m_bOccupied = controllerTemplate.m_bOccupied;
  1277. m_bEngineDisable = controllerTemplate.m_bEngineDisable;
  1278. m_pCarSystem = NULL;
  1279. memcpy( &m_currentState, &controllerTemplate.m_currentState, sizeof(m_currentState) );
  1280. memcpy( &m_vehicleData, &controllerTemplate.m_vehicleData, sizeof(m_vehicleData) );
  1281. memcpy( &m_flVelocity, controllerTemplate.m_flVelocity, sizeof(m_flVelocity) );
  1282. #if OLD_SAVED_GAME
  1283. SET_DEFAULT( m_torqueScale, 1.0 );
  1284. SET_DEFAULT( m_vehicleData.steering.steeringRateSlow, 4.5 );
  1285. SET_DEFAULT( m_vehicleData.steering.steeringRateFast, 0.5 );
  1286. SET_DEFAULT( m_vehicleData.steering.steeringRestRateSlow, 3.0 );
  1287. SET_DEFAULT( m_vehicleData.steering.steeringRestRateFast, 1.8 );
  1288. SET_DEFAULT( m_vehicleData.steering.speedSlow, m_vehicleData.engine.maxSpeed*0.25 );
  1289. SET_DEFAULT( m_vehicleData.steering.speedFast, m_vehicleData.engine.maxSpeed*0.75 );
  1290. SET_DEFAULT( m_vehicleData.steering.degreesSlow, 50 );
  1291. SET_DEFAULT( m_vehicleData.steering.degreesFast, 18 );
  1292. SET_DEFAULT( m_vehicleData.steering.degreesBoost, 10 );
  1293. SET_DEFAULT( m_vehicleData.steering.turnThrottleReduceSlow, 0.3 );
  1294. SET_DEFAULT( m_vehicleData.steering.turnThrottleReduceFast, 3 );
  1295. SET_DEFAULT( m_vehicleData.steering.brakeSteeringRateFactor, 6 );
  1296. SET_DEFAULT( m_vehicleData.steering.throttleSteeringRestRateFactor, 2 );
  1297. SET_DEFAULT( m_vehicleData.steering.boostSteeringRestRateFactor, 1 );
  1298. SET_DEFAULT( m_vehicleData.steering.boostSteeringRateFactor, 1 );
  1299. SET_DEFAULT( m_vehicleData.steering.powerSlideAccel, 200 );
  1300. SET_DEFAULT( m_vehicleData.engine.autobrakeSpeedGain, 1.0 );
  1301. SET_DEFAULT( m_vehicleData.engine.autobrakeSpeedFactor, 2.0 );
  1302. #endif
  1303. for (int i = 0; i < VEHICLE_MAX_WHEEL_COUNT; ++i )
  1304. {
  1305. m_pWheels[i] = controllerTemplate.m_pWheels[i];
  1306. ConvertPositionToIVP( controllerTemplate.m_wheelPosition_Bs[i], m_wheelPosition_Bs[i] );
  1307. ConvertPositionToIVP( controllerTemplate.m_tracePosition_Bs[i], m_tracePosition_Bs[i] );
  1308. }
  1309. CreateIVPObjects( );
  1310. // HACKHACK: vehicle wheels don't have valid friction at startup, clearing the body's angular velocity keeps
  1311. // this fact from affecting the vehicle dynamics in any noticeable way
  1312. // using growFriction will re-establish the contact point with moveable objects, but the friction that
  1313. // occurs afterward is not the same across the save even when that is extended to include static objects
  1314. if ( m_pCarBody )
  1315. {
  1316. // clear angVel
  1317. m_pCarBody->SetVelocity( NULL, &vec3_origin );
  1318. m_pCarBody->GetObject()->get_core()->speed_change.set( m_flVelocity[0], m_flVelocity[1], m_flVelocity[2] );
  1319. }
  1320. }
  1321. //-----------------------------------------------------------------------------
  1322. // Purpose:
  1323. //-----------------------------------------------------------------------------
  1324. void CVehicleController::OnVehicleEnter( void )
  1325. {
  1326. m_bOccupied = true;
  1327. if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
  1328. {
  1329. float flDampSpeed = 0.0f;
  1330. float flDampRotSpeed = 0.0f;
  1331. m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed );
  1332. }
  1333. }
  1334. //-----------------------------------------------------------------------------
  1335. // Purpose:
  1336. //-----------------------------------------------------------------------------
  1337. void CVehicleController::OnVehicleExit( void )
  1338. {
  1339. m_bOccupied = false;
  1340. // Reset the vehicle tires when exiting the vehicle.
  1341. if ( m_vehicleData.steering.isSkidAllowed )
  1342. {
  1343. int iWheel = 0;
  1344. for ( int iAxle = 0; iAxle < m_vehicleData.axleCount; ++iAxle )
  1345. {
  1346. for ( int iAxleWheel = 0; iAxleWheel < m_vehicleData.wheelsPerAxle; ++iAxleWheel, ++iWheel )
  1347. {
  1348. // Change back to normal tires.
  1349. if ( m_nTireType != VEHICLE_TIRE_NORMAL )
  1350. {
  1351. m_pWheels[iWheel]->SetMaterialIndex( m_vehicleData.axles[iAxle].wheels.materialIndex );
  1352. }
  1353. m_pCarSystem->fix_wheel( ( IVP_POS_WHEEL )iWheel, IVP_TRUE );
  1354. }
  1355. }
  1356. m_nTireType = VEHICLE_TIRE_NORMAL;
  1357. m_currentState.skidSpeed = 0.0f;
  1358. }
  1359. if ( m_nVehicleType == VEHICLE_TYPE_AIRBOAT_RAYCAST )
  1360. {
  1361. float flDampSpeed = 1.0f;
  1362. float flDampRotSpeed = 1.0f;
  1363. m_pCarBody->SetDamping( &flDampSpeed, &flDampRotSpeed );
  1364. }
  1365. SetEngineDisabled( false );
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. // Class factory
  1369. //-----------------------------------------------------------------------------
  1370. IPhysicsVehicleController *CreateVehicleController( CPhysicsEnvironment *pEnv, CPhysicsObject *pBodyObject, const vehicleparams_t &params, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace )
  1371. {
  1372. CVehicleController *pController = new CVehicleController( params, pEnv, nVehicleType, pGameTrace );
  1373. pController->InitCarSystem( pBodyObject );
  1374. return pController;
  1375. }
  1376. bool SavePhysicsVehicleController( const physsaveparams_t &params, CVehicleController *pVehicleController )
  1377. {
  1378. vphysics_save_cvehiclecontroller_t controllerTemplate;
  1379. memset( &controllerTemplate, 0, sizeof(controllerTemplate) );
  1380. pVehicleController->WriteToTemplate( controllerTemplate );
  1381. params.pSave->WriteAll( &controllerTemplate );
  1382. return true;
  1383. }
  1384. bool RestorePhysicsVehicleController( const physrestoreparams_t &params, CVehicleController **ppVehicleController )
  1385. {
  1386. *ppVehicleController = new CVehicleController;
  1387. vphysics_save_cvehiclecontroller_t controllerTemplate;
  1388. memset( &controllerTemplate, 0, sizeof(controllerTemplate) );
  1389. params.pRestore->ReadAll( &controllerTemplate );
  1390. (*ppVehicleController)->InitFromTemplate( static_cast<CPhysicsEnvironment *>(params.pEnvironment),
  1391. params.pGameData, params.pGameTrace, controllerTemplate );
  1392. return true;
  1393. }