Counter Strike : Global Offensive Source Code
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.

1093 lines
29 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "entitylist.h"
  9. #include "physics.h"
  10. #include "vphysics/constraints.h"
  11. #include "physics_saverestore.h"
  12. #include "phys_controller.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. class CPhysThruster;
  16. //-----------------------------------------------------------------------------
  17. // Purpose: This class only implements the IMotionEvent-specific behavior
  18. // It keeps track of the forces so they can be integrated
  19. //-----------------------------------------------------------------------------
  20. class CConstantForceController : public IMotionEvent
  21. {
  22. DECLARE_SIMPLE_DATADESC();
  23. public:
  24. void Init( IMotionEvent::simresult_e controlType )
  25. {
  26. m_controlType = controlType;
  27. }
  28. void SetConstantForce( const Vector &linear, const AngularImpulse &angular );
  29. void ScaleConstantForce( float scale );
  30. IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  31. IMotionEvent::simresult_e m_controlType;
  32. Vector m_linear;
  33. AngularImpulse m_angular;
  34. Vector m_linearSave;
  35. AngularImpulse m_angularSave;
  36. };
  37. BEGIN_SIMPLE_DATADESC( CConstantForceController )
  38. DEFINE_FIELD( m_controlType, FIELD_INTEGER ),
  39. DEFINE_FIELD( m_linear, FIELD_VECTOR ),
  40. DEFINE_FIELD( m_angular, FIELD_VECTOR ),
  41. DEFINE_FIELD( m_linearSave, FIELD_VECTOR ),
  42. DEFINE_FIELD( m_angularSave, FIELD_VECTOR ),
  43. END_DATADESC()
  44. void CConstantForceController::SetConstantForce( const Vector &linear, const AngularImpulse &angular )
  45. {
  46. m_linear = linear;
  47. m_angular = angular;
  48. // cache these for scaling later
  49. m_linearSave = linear;
  50. m_angularSave = angular;
  51. }
  52. void CConstantForceController::ScaleConstantForce( float scale )
  53. {
  54. m_linear = m_linearSave * scale;
  55. m_angular = m_angularSave * scale;
  56. }
  57. IMotionEvent::simresult_e CConstantForceController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  58. {
  59. linear = m_linear;
  60. angular = m_angular;
  61. return m_controlType;
  62. }
  63. // UNDONE: Make these logical entities
  64. //-----------------------------------------------------------------------------
  65. // Purpose: This is a general entity that has a force/motion controller that
  66. // simply integrates a constant linear/angular acceleration
  67. //-----------------------------------------------------------------------------
  68. abstract_class CPhysForce : public CPointEntity
  69. {
  70. public:
  71. DECLARE_CLASS( CPhysForce, CPointEntity );
  72. CPhysForce();
  73. ~CPhysForce();
  74. DECLARE_DATADESC();
  75. virtual void OnRestore( );
  76. void Spawn( void );
  77. void Activate( void );
  78. void ForceOn( void );
  79. void ForceOff( void );
  80. void ActivateForce( void );
  81. void SetAttachedObject( EHANDLE hObject ) { m_attachedObject = hObject; }
  82. // Input handlers
  83. void InputActivate( inputdata_t &inputdata );
  84. void InputDeactivate( inputdata_t &inputdata );
  85. void InputForceScale( inputdata_t &inputdata );
  86. void SaveForce( void );
  87. void ScaleForce( float scale );
  88. // MUST IMPLEMENT THIS IN DERIVED CLASS
  89. virtual void SetupForces( IPhysicsObject *pPhys, Vector &linear, AngularImpulse &angular ) = 0;
  90. // optional
  91. virtual void OnActivate( void ) {}
  92. protected:
  93. IPhysicsMotionController *m_pController;
  94. string_t m_nameAttach;
  95. float m_force;
  96. float m_forceTime;
  97. EHANDLE m_attachedObject;
  98. bool m_wasRestored;
  99. CConstantForceController m_integrator;
  100. };
  101. BEGIN_DATADESC( CPhysForce )
  102. DEFINE_PHYSPTR( m_pController ),
  103. DEFINE_KEYFIELD( m_nameAttach, FIELD_STRING, "attach1" ),
  104. DEFINE_KEYFIELD( m_force, FIELD_FLOAT, "force" ),
  105. DEFINE_KEYFIELD( m_forceTime, FIELD_FLOAT, "forcetime" ),
  106. DEFINE_FIELD( m_attachedObject, FIELD_EHANDLE ),
  107. //DEFINE_FIELD( m_wasRestored, FIELD_BOOLEAN ), // NOTE: DO NOT save/load this - it's used to detect loads
  108. DEFINE_EMBEDDED( m_integrator ),
  109. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  110. DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
  111. DEFINE_INPUTFUNC( FIELD_FLOAT, "scale", InputForceScale ),
  112. // Function Pointers
  113. DEFINE_FUNCTION( ForceOff ),
  114. END_DATADESC()
  115. CPhysForce::CPhysForce( void )
  116. {
  117. m_pController = NULL;
  118. m_wasRestored = false;
  119. }
  120. CPhysForce::~CPhysForce()
  121. {
  122. if ( m_pController )
  123. {
  124. physenv->DestroyMotionController( m_pController );
  125. }
  126. }
  127. void CPhysForce::Spawn( void )
  128. {
  129. if ( m_spawnflags & SF_THRUST_LOCAL_ORIENTATION )
  130. {
  131. m_integrator.Init( IMotionEvent::SIM_LOCAL_ACCELERATION );
  132. }
  133. else
  134. {
  135. m_integrator.Init( IMotionEvent::SIM_GLOBAL_ACCELERATION );
  136. }
  137. }
  138. void CPhysForce::OnRestore( )
  139. {
  140. BaseClass::OnRestore();
  141. if ( m_pController )
  142. {
  143. m_pController->SetEventHandler( &m_integrator );
  144. }
  145. m_wasRestored = true;
  146. }
  147. void CPhysForce::Activate( void )
  148. {
  149. BaseClass::Activate();
  150. if ( m_pController )
  151. {
  152. m_pController->WakeObjects();
  153. }
  154. if ( m_wasRestored )
  155. return;
  156. if ( m_attachedObject == NULL )
  157. {
  158. m_attachedObject = gEntList.FindEntityByName( NULL, m_nameAttach );
  159. }
  160. // Let the derived class set up before we throw the switch
  161. OnActivate();
  162. if ( m_spawnflags & SF_THRUST_STARTACTIVE )
  163. {
  164. ForceOn();
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. //-----------------------------------------------------------------------------
  170. void CPhysForce::InputActivate( inputdata_t &inputdata )
  171. {
  172. ForceOn();
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose:
  176. //-----------------------------------------------------------------------------
  177. void CPhysForce::InputDeactivate( inputdata_t &inputdata )
  178. {
  179. ForceOff();
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CPhysForce::InputForceScale( inputdata_t &inputdata )
  185. {
  186. ScaleForce( inputdata.value.Float() );
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. //-----------------------------------------------------------------------------
  191. void CPhysForce::ForceOn( void )
  192. {
  193. if ( m_pController )
  194. return;
  195. ActivateForce();
  196. if ( m_forceTime )
  197. {
  198. SetNextThink( gpGlobals->curtime + m_forceTime );
  199. SetThink( &CPhysForce::ForceOff );
  200. }
  201. }
  202. void CPhysForce::ActivateForce( void )
  203. {
  204. IPhysicsObject *pPhys = NULL;
  205. if ( m_attachedObject )
  206. {
  207. pPhys = m_attachedObject->VPhysicsGetObject();
  208. }
  209. if ( !pPhys )
  210. return;
  211. Vector linear;
  212. AngularImpulse angular;
  213. SetupForces( pPhys, linear, angular );
  214. m_integrator.SetConstantForce( linear, angular );
  215. m_pController = physenv->CreateMotionController( &m_integrator );
  216. m_pController->AttachObject( pPhys, true );
  217. // Make sure the object is simulated
  218. pPhys->Wake();
  219. }
  220. void CPhysForce::ForceOff( void )
  221. {
  222. if ( !m_pController )
  223. return;
  224. physenv->DestroyMotionController( m_pController );
  225. m_pController = NULL;
  226. SetThink( NULL );
  227. SetNextThink( TICK_NEVER_THINK );
  228. IPhysicsObject *pPhys = NULL;
  229. if ( m_attachedObject )
  230. {
  231. pPhys = m_attachedObject->VPhysicsGetObject();
  232. if ( pPhys )
  233. {
  234. pPhys->Wake();
  235. }
  236. }
  237. }
  238. void CPhysForce::ScaleForce( float scale )
  239. {
  240. if ( !m_pController )
  241. ForceOn();
  242. m_integrator.ScaleConstantForce( scale );
  243. m_pController->WakeObjects();
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: A rocket-engine/thruster based on the force controller above
  247. // Calculate the force (and optional torque) that the engine would create
  248. //-----------------------------------------------------------------------------
  249. class CPhysThruster : public CPhysForce
  250. {
  251. DECLARE_CLASS( CPhysThruster, CPhysForce );
  252. public:
  253. DECLARE_DATADESC();
  254. virtual void OnActivate( void );
  255. virtual void SetupForces( IPhysicsObject *pPhys, Vector &linear, AngularImpulse &angular );
  256. private:
  257. friend CBaseEntity *CreatePhysThruster( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, float flForce, float flForcetime, bool bActive, int nFlags );
  258. Vector m_localOrigin;
  259. };
  260. LINK_ENTITY_TO_CLASS( phys_thruster, CPhysThruster );
  261. BEGIN_DATADESC( CPhysThruster )
  262. DEFINE_FIELD( m_localOrigin, FIELD_VECTOR ),
  263. END_DATADESC()
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Use this to spawn a keepupright controller via code instead of map-placed
  266. //-----------------------------------------------------------------------------
  267. CBaseEntity *CreatePhysThruster( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, float flForce, float flForcetime, bool bActive, int nFlags )
  268. {
  269. CPhysThruster *pThruster = ( CPhysThruster* )CBaseEntity::Create( "phys_thruster", vecOrigin, vecAngles, pOwner );
  270. if ( pThruster )
  271. {
  272. pThruster->AddSpawnFlags( nFlags );
  273. pThruster->m_attachedObject = pOwner;
  274. pThruster->m_force = flForce;
  275. pThruster->m_forceTime = flForcetime;
  276. pThruster->Spawn();
  277. pThruster->Activate();
  278. if ( bActive )
  279. pThruster->ForceOn();
  280. }
  281. return pThruster;
  282. }
  283. void CPhysThruster::OnActivate( void )
  284. {
  285. if ( m_attachedObject != NULL )
  286. {
  287. matrix3x4_t worldToAttached, thrusterToAttached;
  288. MatrixInvert( m_attachedObject->EntityToWorldTransform(), worldToAttached );
  289. ConcatTransforms( worldToAttached, EntityToWorldTransform(), thrusterToAttached );
  290. MatrixGetColumn( thrusterToAttached, 3, m_localOrigin );
  291. if ( HasSpawnFlags( SF_THRUST_LOCAL_ORIENTATION ) )
  292. {
  293. QAngle angles;
  294. MatrixAngles( thrusterToAttached, angles );
  295. SetLocalAngles( angles );
  296. }
  297. // maintain the local relationship with this entity
  298. // it may move before the thruster is activated
  299. if ( HasSpawnFlags( SF_THRUST_IGNORE_POS ) )
  300. {
  301. m_localOrigin.Init();
  302. }
  303. }
  304. }
  305. // utility function to duplicate this call in local space
  306. void CalculateVelocityOffsetLocal( IPhysicsObject *pPhys, const Vector &forceLocal, const Vector &positionLocal, Vector &outVelLocal, AngularImpulse &outAngular )
  307. {
  308. Vector posWorld, forceWorld;
  309. pPhys->LocalToWorld( &posWorld, positionLocal );
  310. pPhys->LocalToWorldVector( &forceWorld, forceLocal );
  311. Vector velWorld;
  312. pPhys->CalculateVelocityOffset( forceWorld, posWorld, &velWorld, &outAngular );
  313. pPhys->WorldToLocalVector( &outVelLocal, velWorld );
  314. }
  315. void CPhysThruster::SetupForces( IPhysicsObject *pPhys, Vector &linear, AngularImpulse &angular )
  316. {
  317. Vector thrustVector;
  318. AngleVectors( GetLocalAngles(), &thrustVector );
  319. thrustVector *= m_force;
  320. // multiply the force by mass (it's actually just an acceleration)
  321. if ( m_spawnflags & SF_THRUST_MASS_INDEPENDENT )
  322. {
  323. thrustVector *= pPhys->GetMass();
  324. }
  325. if ( m_spawnflags & SF_THRUST_LOCAL_ORIENTATION )
  326. {
  327. CalculateVelocityOffsetLocal( pPhys, thrustVector, m_localOrigin, linear, angular );
  328. }
  329. else
  330. {
  331. Vector position;
  332. VectorTransform( m_localOrigin, m_attachedObject->EntityToWorldTransform(), position );
  333. pPhys->CalculateVelocityOffset( thrustVector, position, &linear, &angular );
  334. }
  335. if ( !(m_spawnflags & SF_THRUST_FORCE) )
  336. {
  337. // clear out force
  338. linear.Init();
  339. }
  340. if ( !(m_spawnflags & SF_THRUST_TORQUE) )
  341. {
  342. // clear out torque
  343. angular.Init();
  344. }
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: A controllable motor - exerts torque
  348. //-----------------------------------------------------------------------------
  349. class CPhysTorque : public CPhysForce
  350. {
  351. DECLARE_CLASS( CPhysTorque, CPhysForce );
  352. public:
  353. DECLARE_DATADESC();
  354. void Spawn( void );
  355. virtual void SetupForces( IPhysicsObject *pPhys, Vector &linear, AngularImpulse &angular );
  356. private:
  357. Vector m_axis;
  358. };
  359. BEGIN_DATADESC( CPhysTorque )
  360. DEFINE_KEYFIELD( m_axis, FIELD_VECTOR, "axis" ),
  361. END_DATADESC()
  362. LINK_ENTITY_TO_CLASS( phys_torque, CPhysTorque );
  363. void CPhysTorque::Spawn( void )
  364. {
  365. // force spawnflags to agree with implementation of this class
  366. m_spawnflags |= SF_THRUST_TORQUE | SF_THRUST_MASS_INDEPENDENT;
  367. m_spawnflags &= ~SF_THRUST_FORCE;
  368. m_axis -= GetAbsOrigin();
  369. VectorNormalize(m_axis);
  370. UTIL_SnapDirectionToAxis( m_axis );
  371. BaseClass::Spawn();
  372. }
  373. void CPhysTorque::SetupForces( IPhysicsObject *pPhys, Vector &linear, AngularImpulse &angular )
  374. {
  375. // clear out force
  376. linear.Init();
  377. matrix3x4_t matrix;
  378. pPhys->GetPositionMatrix( &matrix );
  379. // transform motor axis to local space
  380. Vector axis_ls;
  381. VectorIRotate( m_axis, matrix, axis_ls );
  382. // Set torque to be around selected axis
  383. angular = axis_ls * m_force;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: This class only implements the IMotionEvent-specific behavior
  387. // It keeps track of the forces so they can be integrated
  388. //-----------------------------------------------------------------------------
  389. class CMotorController : public IMotionEvent
  390. {
  391. DECLARE_SIMPLE_DATADESC();
  392. public:
  393. IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  394. float m_speed;
  395. float m_maxTorque;
  396. Vector m_axis;
  397. float m_inertiaFactor;
  398. float m_lastSpeed;
  399. float m_lastAcceleration;
  400. float m_lastForce;
  401. float m_restistanceDamping;
  402. };
  403. BEGIN_SIMPLE_DATADESC( CMotorController )
  404. DEFINE_FIELD( m_speed, FIELD_FLOAT ),
  405. DEFINE_FIELD( m_maxTorque, FIELD_FLOAT ),
  406. DEFINE_KEYFIELD( m_axis, FIELD_VECTOR, "axis" ),
  407. DEFINE_KEYFIELD( m_inertiaFactor, FIELD_FLOAT, "inertiafactor" ),
  408. DEFINE_FIELD( m_lastSpeed, FIELD_FLOAT ),
  409. DEFINE_FIELD( m_lastAcceleration, FIELD_FLOAT ),
  410. DEFINE_FIELD( m_lastForce, FIELD_FLOAT ),
  411. DEFINE_FIELD( m_restistanceDamping, FIELD_FLOAT ),
  412. END_DATADESC()
  413. IMotionEvent::simresult_e CMotorController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  414. {
  415. linear = vec3_origin;
  416. angular = vec3_origin;
  417. if ( m_speed == 0 )
  418. return SIM_NOTHING;
  419. matrix3x4_t matrix;
  420. pObject->GetPositionMatrix( &matrix );
  421. AngularImpulse currentRotAxis;
  422. // currentRotAxis is in local space
  423. pObject->GetVelocity( NULL, &currentRotAxis );
  424. // transform motor axis to local space
  425. Vector motorAxis_ls;
  426. VectorIRotate( m_axis, matrix, motorAxis_ls );
  427. float currentSpeed = DotProduct( currentRotAxis, motorAxis_ls );
  428. float inertia = DotProductAbs( pObject->GetInertia(), motorAxis_ls );
  429. // compute absolute acceleration, don't integrate over the timestep
  430. float accel = m_speed - currentSpeed;
  431. float rotForce = accel * inertia * m_inertiaFactor;
  432. // BUGBUG: This heuristic is a little flaky
  433. // UNDONE: Make a better heuristic for speed control
  434. if ( fabsf(m_lastAcceleration) > 0 )
  435. {
  436. float deltaSpeed = currentSpeed - m_lastSpeed;
  437. // make sure they are going the same way
  438. if ( deltaSpeed * accel > 0 )
  439. {
  440. float factor = deltaSpeed / m_lastAcceleration;
  441. factor = 1 - clamp( factor, 0, 1 );
  442. rotForce += m_lastForce * factor * m_restistanceDamping;
  443. }
  444. else
  445. {
  446. if ( currentSpeed != 0 )
  447. {
  448. // have we reached a steady state that isn't our target?
  449. float increase = deltaSpeed / m_lastAcceleration;
  450. if ( fabsf(increase) < 0.05 )
  451. {
  452. rotForce += m_lastForce * m_restistanceDamping;
  453. }
  454. }
  455. }
  456. }
  457. // -------------------------------------------------------
  458. if ( m_maxTorque != 0 )
  459. {
  460. if ( rotForce > m_maxTorque )
  461. {
  462. rotForce = m_maxTorque;
  463. }
  464. else if ( rotForce < -m_maxTorque )
  465. {
  466. rotForce = -m_maxTorque;
  467. }
  468. }
  469. m_lastForce = rotForce;
  470. m_lastAcceleration = (rotForce / inertia);
  471. m_lastSpeed = currentSpeed;
  472. // this is in local space
  473. angular = motorAxis_ls * rotForce;
  474. return SIM_LOCAL_FORCE;
  475. }
  476. #define SF_MOTOR_START_ON 0x0001 // starts on by default
  477. #define SF_MOTOR_NOCOLLIDE 0x0002 // don't collide with world geometry
  478. #define SF_MOTOR_HINGE 0x0004 // motor also acts as a hinge constraining the object to this axis
  479. // NOTE: THIS DOESN'T WORK YET
  480. #define SF_MOTOR_LOCAL 0x0008 // Maintain local relationship with the attached object
  481. class CPhysMotor : public CLogicalEntity
  482. {
  483. DECLARE_CLASS( CPhysMotor, CLogicalEntity );
  484. public:
  485. ~CPhysMotor();
  486. DECLARE_DATADESC();
  487. void Spawn( void );
  488. void Activate( void );
  489. void Think( void );
  490. void TurnOn( void );
  491. void TargetSpeedChanged( void );
  492. void OnRestore();
  493. void InputSetTargetSpeed( inputdata_t &inputdata );
  494. void InputTurnOn( inputdata_t &inputdata );
  495. void InputTurnOff( inputdata_t &inputdata );
  496. void CalculateAcceleration();
  497. string_t m_nameAttach;
  498. EHANDLE m_attachedObject;
  499. float m_spinUp;
  500. float m_additionalAcceleration;
  501. float m_angularAcceleration;
  502. float m_lastTime;
  503. // FIXME: can we remove m_flSpeed from CBaseEntity?
  504. //float m_flSpeed;
  505. IPhysicsConstraint *m_pHinge;
  506. IPhysicsMotionController *m_pController;
  507. CMotorController m_motor;
  508. };
  509. BEGIN_DATADESC( CPhysMotor )
  510. DEFINE_KEYFIELD( m_nameAttach, FIELD_STRING, "attach1" ),
  511. DEFINE_FIELD( m_attachedObject, FIELD_EHANDLE ),
  512. DEFINE_KEYFIELD( m_spinUp, FIELD_FLOAT, "spinup" ),
  513. DEFINE_KEYFIELD( m_additionalAcceleration, FIELD_FLOAT, "addangaccel" ),
  514. DEFINE_FIELD( m_angularAcceleration, FIELD_FLOAT ),
  515. DEFINE_FIELD( m_lastTime, FIELD_TIME ),
  516. DEFINE_PHYSPTR( m_pHinge ),
  517. DEFINE_PHYSPTR( m_pController ),
  518. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetTargetSpeed ),
  519. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  520. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  521. DEFINE_EMBEDDED( m_motor ),
  522. END_DATADESC()
  523. LINK_ENTITY_TO_CLASS( phys_motor, CPhysMotor );
  524. void CPhysMotor::CalculateAcceleration()
  525. {
  526. if ( m_spinUp )
  527. {
  528. m_angularAcceleration = fabsf(m_flSpeed / m_spinUp);
  529. }
  530. else
  531. {
  532. m_angularAcceleration = fabsf(m_flSpeed);
  533. }
  534. }
  535. //-----------------------------------------------------------------------------
  536. // Purpose: Input handler that sets a speed to spin up or down to.
  537. //-----------------------------------------------------------------------------
  538. void CPhysMotor::InputSetTargetSpeed( inputdata_t &inputdata )
  539. {
  540. if ( m_flSpeed == inputdata.value.Float() )
  541. return;
  542. m_flSpeed = inputdata.value.Float();
  543. TargetSpeedChanged();
  544. CalculateAcceleration();
  545. }
  546. void CPhysMotor::TargetSpeedChanged( void )
  547. {
  548. SetNextThink( gpGlobals->curtime );
  549. m_lastTime = gpGlobals->curtime;
  550. m_pController->WakeObjects();
  551. }
  552. //------------------------------------------------------------------------------
  553. // Purpose: Input handler that turns the motor on.
  554. //------------------------------------------------------------------------------
  555. void CPhysMotor::InputTurnOn( inputdata_t &inputdata )
  556. {
  557. TurnOn();
  558. }
  559. //------------------------------------------------------------------------------
  560. // Purpose: Input handler that turns the motor off.
  561. //------------------------------------------------------------------------------
  562. void CPhysMotor::InputTurnOff( inputdata_t &inputdata )
  563. {
  564. m_motor.m_speed = 0;
  565. SetNextThink( TICK_NEVER_THINK );
  566. }
  567. CPhysMotor::~CPhysMotor()
  568. {
  569. if ( m_attachedObject && m_pHinge )
  570. {
  571. IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject();
  572. if ( pPhys )
  573. {
  574. PhysClearGameFlags(pPhys, FVPHYSICS_NO_PLAYER_PICKUP);
  575. }
  576. }
  577. physenv->DestroyConstraint( m_pHinge );
  578. physenv->DestroyMotionController( m_pController );
  579. }
  580. void CPhysMotor::Spawn( void )
  581. {
  582. m_motor.m_axis -= GetLocalOrigin();
  583. float axisLength = VectorNormalize(m_motor.m_axis);
  584. // double check that the axis is at least a unit long. If not, warn and self-destruct.
  585. if ( axisLength > 1.0f )
  586. {
  587. UTIL_SnapDirectionToAxis( m_motor.m_axis );
  588. }
  589. else
  590. {
  591. Warning("phys_motor %s does not have a valid axis helper, and self-destructed!\n", GetDebugName());
  592. m_motor.m_speed = 0;
  593. SetNextThink( TICK_NEVER_THINK );
  594. UTIL_Remove(this);
  595. }
  596. }
  597. void CPhysMotor::TurnOn( void )
  598. {
  599. CBaseEntity *pAttached = m_attachedObject;
  600. if ( !pAttached )
  601. return;
  602. IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
  603. if ( pPhys )
  604. {
  605. m_pController->WakeObjects();
  606. // If the current speed is zero, the objects can run a tick without getting torque'd and go back to sleep
  607. // so force a think now and have some acceleration happen before the controller gets called.
  608. m_lastTime = gpGlobals->curtime - TICK_INTERVAL;
  609. Think();
  610. }
  611. }
  612. void CPhysMotor::Activate( void )
  613. {
  614. BaseClass::Activate();
  615. // This gets called after all objects spawn and after all objects restore
  616. if ( m_attachedObject == NULL )
  617. {
  618. CBaseEntity *pAttach = gEntList.FindEntityByName( NULL, m_nameAttach );
  619. if ( pAttach && pAttach->GetMoveType() == MOVETYPE_VPHYSICS )
  620. {
  621. m_attachedObject = pAttach;
  622. IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject();
  623. CalculateAcceleration();
  624. matrix3x4_t matrix;
  625. pPhys->GetPositionMatrix( &matrix );
  626. Vector motorAxis_ls;
  627. VectorIRotate( m_motor.m_axis, matrix, motorAxis_ls );
  628. float inertia = DotProductAbs( pPhys->GetInertia(), motorAxis_ls );
  629. m_motor.m_maxTorque = inertia * m_motor.m_inertiaFactor * (m_angularAcceleration + m_additionalAcceleration);
  630. m_motor.m_restistanceDamping = 1.0f;
  631. }
  632. }
  633. if ( m_attachedObject )
  634. {
  635. IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject();
  636. // create a hinge constraint for this object?
  637. if ( m_spawnflags & SF_MOTOR_HINGE )
  638. {
  639. // UNDONE: Don't do this on restore?
  640. if ( !m_pHinge )
  641. {
  642. constraint_hingeparams_t hingeParams;
  643. hingeParams.Defaults();
  644. hingeParams.worldAxisDirection = m_motor.m_axis;
  645. hingeParams.worldPosition = GetLocalOrigin();
  646. m_pHinge = physenv->CreateHingeConstraint( g_PhysWorldObject, pPhys, NULL, hingeParams );
  647. m_pHinge->SetGameData( (void *)this );
  648. // can't grab this object
  649. PhysSetGameFlags(pPhys, FVPHYSICS_NO_PLAYER_PICKUP);
  650. }
  651. if ( m_spawnflags & SF_MOTOR_NOCOLLIDE )
  652. {
  653. PhysDisableEntityCollisions( g_PhysWorldObject, pPhys );
  654. }
  655. }
  656. else
  657. {
  658. m_pHinge = NULL;
  659. }
  660. // NOTE: On restore, this path isn't run because m_pController will not be NULL
  661. if ( !m_pController )
  662. {
  663. m_pController = physenv->CreateMotionController( &m_motor );
  664. m_pController->AttachObject( m_attachedObject->VPhysicsGetObject(), false );
  665. if ( m_spawnflags & SF_MOTOR_START_ON )
  666. {
  667. TurnOn();
  668. }
  669. }
  670. }
  671. }
  672. void CPhysMotor::OnRestore()
  673. {
  674. BaseClass::OnRestore();
  675. // Need to do this on restore since there's no good way to save this
  676. if ( m_pController )
  677. {
  678. m_pController->SetEventHandler( &m_motor );
  679. }
  680. }
  681. void CPhysMotor::Think( void )
  682. {
  683. // angular acceleration is always positive - it should be treated as a magnitude - the controller
  684. // will apply it in the proper direction
  685. Assert(m_angularAcceleration>=0);
  686. m_motor.m_speed = UTIL_Approach( m_flSpeed, m_motor.m_speed, m_angularAcceleration*(gpGlobals->curtime-m_lastTime) );
  687. m_lastTime = gpGlobals->curtime;
  688. if ( m_motor.m_speed != m_flSpeed )
  689. {
  690. SetNextThink( gpGlobals->curtime );
  691. }
  692. }
  693. //======================================================================================
  694. // KEEPUPRIGHT CONTROLLER
  695. //======================================================================================
  696. class CKeepUpright : public CPointEntity, public IMotionEvent
  697. {
  698. DECLARE_CLASS( CKeepUpright, CPointEntity );
  699. public:
  700. DECLARE_DATADESC();
  701. CKeepUpright();
  702. ~CKeepUpright();
  703. void Spawn();
  704. void Activate();
  705. // IMotionEvent
  706. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  707. // Inputs
  708. void InputTurnOn( inputdata_t &inputdata )
  709. {
  710. m_bActive = true;
  711. }
  712. void InputTurnOff( inputdata_t &inputdata )
  713. {
  714. m_bActive = false;
  715. }
  716. void InputSetAngularLimit( inputdata_t &inputdata )
  717. {
  718. m_angularLimit = inputdata.value.Float();
  719. }
  720. private:
  721. friend CBaseEntity *CreateKeepUpright( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, float flAngularLimit, bool bActive );
  722. Vector m_worldGoalAxis;
  723. Vector m_localTestAxis;
  724. IPhysicsMotionController *m_pController;
  725. string_t m_nameAttach;
  726. EHANDLE m_attachedObject;
  727. float m_angularLimit;
  728. bool m_bActive;
  729. bool m_bDampAllRotation;
  730. };
  731. #define SF_KEEPUPRIGHT_START_INACTIVE 0x0001
  732. LINK_ENTITY_TO_CLASS( phys_keepupright, CKeepUpright );
  733. BEGIN_DATADESC( CKeepUpright )
  734. DEFINE_FIELD( m_worldGoalAxis, FIELD_VECTOR ),
  735. DEFINE_FIELD( m_localTestAxis, FIELD_VECTOR ),
  736. DEFINE_PHYSPTR( m_pController ),
  737. DEFINE_KEYFIELD( m_nameAttach, FIELD_STRING, "attach1" ),
  738. DEFINE_FIELD( m_attachedObject, FIELD_EHANDLE ),
  739. DEFINE_KEYFIELD( m_angularLimit, FIELD_FLOAT, "angularlimit" ),
  740. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  741. DEFINE_FIELD( m_bDampAllRotation, FIELD_BOOLEAN ),
  742. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  743. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  744. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAngularLimit", InputSetAngularLimit ),
  745. END_DATADESC()
  746. CKeepUpright::CKeepUpright()
  747. {
  748. // by default, recover from up to 15 degrees / sec angular velocity
  749. m_angularLimit = 15;
  750. m_attachedObject = NULL;
  751. m_bDampAllRotation = false;
  752. }
  753. CKeepUpright::~CKeepUpright()
  754. {
  755. if ( m_pController )
  756. {
  757. physenv->DestroyMotionController( m_pController );
  758. m_pController = NULL;
  759. }
  760. }
  761. void CKeepUpright::Spawn()
  762. {
  763. // align the object's local Z axis
  764. m_localTestAxis.Init( 0, 0, 1 );
  765. // Use our Up axis so mapmakers can orient us arbitrarily
  766. GetVectors( NULL, NULL, &m_worldGoalAxis );
  767. SetMoveType( MOVETYPE_NONE );
  768. if ( m_spawnflags & SF_KEEPUPRIGHT_START_INACTIVE )
  769. {
  770. m_bActive = false;
  771. }
  772. else
  773. {
  774. m_bActive = true;
  775. }
  776. }
  777. void CKeepUpright::Activate()
  778. {
  779. BaseClass::Activate();
  780. if ( !m_pController )
  781. {
  782. // This case occurs when spawning
  783. IPhysicsObject *pPhys;
  784. if ( m_attachedObject )
  785. {
  786. pPhys = m_attachedObject->VPhysicsGetObject();
  787. }
  788. else
  789. {
  790. pPhys = FindPhysicsObjectByName( STRING(m_nameAttach), this );
  791. }
  792. if ( !pPhys )
  793. {
  794. UTIL_Remove(this);
  795. return;
  796. }
  797. // HACKHACK: Due to changes in the vehicle simulator the keepupright controller used in coast_01 is unstable
  798. // force it to have perfect damping to compensate.
  799. // detect it using the hack of angular limit == 150, attached to a vehicle
  800. // Fixing it in the code is the simplest course of action presently
  801. #ifdef HL2_DLL
  802. if ( m_angularLimit == 150.0f )
  803. {
  804. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pPhys->GetGameData());
  805. if ( pEntity && pEntity->GetServerVehicle() && Q_stristr( gpGlobals->mapname.ToCStr(), "d2_coast_01" ) )
  806. {
  807. m_bDampAllRotation = true;
  808. }
  809. }
  810. #endif
  811. m_pController = physenv->CreateMotionController( (IMotionEvent *)this );
  812. m_pController->AttachObject( pPhys, false );
  813. }
  814. else
  815. {
  816. // This case occurs when restoring
  817. m_pController->SetEventHandler( this );
  818. }
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Purpose: Use this to spawn a keepupright controller via code instead of map-placed
  822. //-----------------------------------------------------------------------------
  823. CBaseEntity *CreateKeepUpright( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, float flAngularLimit, bool bActive )
  824. {
  825. CKeepUpright *pKeepUpright = (CKeepUpright*)CBaseEntity::Create( "phys_keepupright", vecOrigin, vecAngles, pOwner );
  826. if ( pKeepUpright )
  827. {
  828. pKeepUpright->m_attachedObject = pOwner;
  829. pKeepUpright->m_angularLimit = flAngularLimit;
  830. if ( !bActive )
  831. {
  832. pKeepUpright->AddSpawnFlags( SF_KEEPUPRIGHT_START_INACTIVE );
  833. }
  834. pKeepUpright->Spawn();
  835. pKeepUpright->Activate();
  836. }
  837. return pKeepUpright;
  838. }
  839. IMotionEvent::simresult_e CKeepUpright::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  840. {
  841. if ( !m_bActive )
  842. return SIM_NOTHING;
  843. linear.Init();
  844. AngularImpulse angVel;
  845. pObject->GetVelocity( NULL, &angVel );
  846. matrix3x4_t matrix;
  847. // get the object's local to world transform
  848. pObject->GetPositionMatrix( &matrix );
  849. // Get the alignment axis in object space
  850. Vector currentLocalTargetAxis;
  851. VectorIRotate( m_worldGoalAxis, matrix, currentLocalTargetAxis );
  852. float invDeltaTime = (1/deltaTime);
  853. if ( m_bDampAllRotation )
  854. {
  855. angular = ComputeRotSpeedToAlignAxes( m_localTestAxis, currentLocalTargetAxis, angVel, 0, invDeltaTime, m_angularLimit );
  856. angular -= angVel;
  857. angular *= invDeltaTime;
  858. return SIM_LOCAL_ACCELERATION;
  859. }
  860. angular = ComputeRotSpeedToAlignAxes( m_localTestAxis, currentLocalTargetAxis, angVel, 1.0, invDeltaTime, m_angularLimit );
  861. angular *= invDeltaTime;
  862. #if 0
  863. Vector position, out, worldAxis;
  864. MatrixGetColumn( matrix, 3, position );
  865. out = angular * 0.1;
  866. VectorRotate( m_localTestAxis, matrix, worldAxis );
  867. NDebugOverlay::Line( position, position + worldAxis * 100, 255, 0, 0, 0, 0 );
  868. NDebugOverlay::Line( position, position + m_worldGoalAxis * 100, 255, 0, 0, 0, 0 );
  869. NDebugOverlay::Line( position, position + out, 255, 255, 0, 0, 0 );
  870. #endif
  871. return SIM_LOCAL_ACCELERATION;
  872. }
  873. // computes the torque necessary to align testAxis with alignAxis
  874. AngularImpulse ComputeRotSpeedToAlignAxes( const Vector &testAxis, const Vector &alignAxis, const AngularImpulse &currentSpeed, float damping, float scale, float maxSpeed )
  875. {
  876. Vector rotationAxis = CrossProduct( testAxis, alignAxis );
  877. // atan2() is well defined, so do a Dot & Cross instead of asin(Cross)
  878. float cosine = DotProduct( testAxis, alignAxis );
  879. float sine = VectorNormalize( rotationAxis );
  880. float angle = atan2( sine, cosine );
  881. angle = RAD2DEG(angle);
  882. AngularImpulse angular = rotationAxis * scale * angle;
  883. angular -= rotationAxis * damping * DotProduct( currentSpeed, rotationAxis );
  884. float len = VectorNormalize( angular );
  885. if ( len > maxSpeed )
  886. {
  887. len = maxSpeed;
  888. }
  889. return angular * len;
  890. }