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.

2009 lines
58 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Physics constraint entities
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "physics.h"
  9. #include "entityoutput.h"
  10. #include "engine/IEngineSound.h"
  11. #include "vphysics/constraints.h"
  12. #include "igamesystem.h"
  13. #include "physics_saverestore.h"
  14. #include "vcollide_parse.h"
  15. #include "positionwatcher.h"
  16. #include "fmtstr.h"
  17. #include "physics_prop_ragdoll.h"
  18. #define HINGE_NOTIFY HL2_EPISODIC
  19. #if HINGE_NOTIFY
  20. #include "physconstraint_sounds.h"
  21. #endif
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. #define SF_CONSTRAINT_DISABLE_COLLISION 0x0001
  25. #define SF_SLIDE_LIMIT_ENDS 0x0002
  26. #define SF_PULLEY_RIGID 0x0002
  27. #define SF_LENGTH_RIGID 0x0002
  28. #define SF_RAGDOLL_FREEMOVEMENT 0x0002
  29. #define SF_CONSTRAINT_START_INACTIVE 0x0004
  30. #define SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY 0x0008
  31. #define SF_CONSTRAINT_NO_CONNECT_UNTIL_ACTIVATED 0x0010 // Will only check the two attached entities at activation
  32. ConVar g_debug_constraint_sounds ( "g_debug_constraint_sounds", "0", FCVAR_CHEAT, "Enable debug printing about constraint sounds.");
  33. struct hl_constraint_info_t
  34. {
  35. hl_constraint_info_t()
  36. {
  37. pObjects[0] = pObjects[1] = NULL;
  38. pGroup = NULL;
  39. anchorPosition[0].Init();
  40. anchorPosition[1].Init();
  41. swapped = false;
  42. massScale[0] = massScale[1] = 1.0f;
  43. }
  44. Vector anchorPosition[2];
  45. IPhysicsObject *pObjects[2];
  46. IPhysicsConstraintGroup *pGroup;
  47. float massScale[2];
  48. bool swapped;
  49. };
  50. struct constraint_anchor_t
  51. {
  52. Vector localOrigin;
  53. EHANDLE hEntity;
  54. int parentAttachment;
  55. string_t name;
  56. float massScale;
  57. };
  58. class CAnchorList : public CAutoGameSystem
  59. {
  60. public:
  61. CAnchorList( char const *name ) : CAutoGameSystem( name )
  62. {
  63. }
  64. void LevelShutdownPostEntity()
  65. {
  66. m_list.Purge();
  67. }
  68. void AddToList( CBaseEntity *pEntity, float massScale )
  69. {
  70. int index = m_list.AddToTail();
  71. constraint_anchor_t *pAnchor = &m_list[index];
  72. pAnchor->hEntity = pEntity->GetParent();
  73. pAnchor->parentAttachment = pEntity->GetParentAttachment();
  74. pAnchor->name = pEntity->GetEntityName();
  75. pAnchor->localOrigin = pEntity->GetLocalOrigin();
  76. pAnchor->massScale = massScale;
  77. }
  78. constraint_anchor_t *Find( string_t name )
  79. {
  80. for ( int i = m_list.Count()-1; i >=0; i-- )
  81. {
  82. if ( FStrEq( STRING(m_list[i].name), STRING(name) ) )
  83. {
  84. return &m_list[i];
  85. }
  86. }
  87. return NULL;
  88. }
  89. private:
  90. CUtlVector<constraint_anchor_t> m_list;
  91. };
  92. static CAnchorList g_AnchorList( "CAnchorList" );
  93. class CConstraintAnchor : public CPointEntity
  94. {
  95. DECLARE_CLASS( CConstraintAnchor, CPointEntity );
  96. public:
  97. CConstraintAnchor()
  98. {
  99. m_massScale = 1.0f;
  100. }
  101. void Spawn( void )
  102. {
  103. if ( GetParent() )
  104. {
  105. g_AnchorList.AddToList( this, m_massScale );
  106. UTIL_Remove( this );
  107. }
  108. }
  109. DECLARE_DATADESC();
  110. float m_massScale;
  111. };
  112. BEGIN_DATADESC( CConstraintAnchor )
  113. DEFINE_KEYFIELD( m_massScale, FIELD_FLOAT, "massScale" ),
  114. END_DATADESC()
  115. LINK_ENTITY_TO_CLASS( info_constraint_anchor, CConstraintAnchor );
  116. class CPhysConstraintSystem : public CLogicalEntity
  117. {
  118. DECLARE_CLASS( CPhysConstraintSystem, CLogicalEntity );
  119. public:
  120. void Spawn();
  121. ~CPhysConstraintSystem();
  122. IPhysicsConstraintGroup *GetVPhysicsGroup() { return m_pMachine; }
  123. DECLARE_DATADESC();
  124. private:
  125. IPhysicsConstraintGroup *m_pMachine;
  126. int m_additionalIterations;
  127. };
  128. BEGIN_DATADESC( CPhysConstraintSystem )
  129. DEFINE_PHYSPTR( m_pMachine ),
  130. DEFINE_KEYFIELD( m_additionalIterations, FIELD_INTEGER, "additionaliterations" ),
  131. END_DATADESC()
  132. void CPhysConstraintSystem::Spawn()
  133. {
  134. constraint_groupparams_t group;
  135. group.Defaults();
  136. group.additionalIterations = m_additionalIterations;
  137. m_pMachine = physenv->CreateConstraintGroup( group );
  138. }
  139. CPhysConstraintSystem::~CPhysConstraintSystem()
  140. {
  141. physenv->DestroyConstraintGroup( m_pMachine );
  142. }
  143. LINK_ENTITY_TO_CLASS( phys_constraintsystem, CPhysConstraintSystem );
  144. void PhysTeleportConstrainedEntity( CBaseEntity *pTeleportSource, IPhysicsObject *pObject0, IPhysicsObject *pObject1, const Vector &prevPosition, const QAngle &prevAngles, bool physicsRotate )
  145. {
  146. // teleport the other object
  147. CBaseEntity *pEntity0 = static_cast<CBaseEntity *> (pObject0->GetGameData());
  148. CBaseEntity *pEntity1 = static_cast<CBaseEntity *> (pObject1->GetGameData());
  149. if ( !pEntity0 || !pEntity1 )
  150. return;
  151. // figure out which entity needs to be fixed up (the one that isn't pTeleportSource)
  152. CBaseEntity *pFixup = pEntity1;
  153. // teleport the other object
  154. if ( pTeleportSource != pEntity0 )
  155. {
  156. if ( pTeleportSource != pEntity1 )
  157. {
  158. Msg("Bogus teleport notification!!\n");
  159. return;
  160. }
  161. pFixup = pEntity0;
  162. }
  163. // constraint doesn't move this entity
  164. if ( pFixup->GetMoveType() != MOVETYPE_VPHYSICS )
  165. return;
  166. if ( !pFixup->VPhysicsGetObject() || !pFixup->VPhysicsGetObject()->IsMoveable() )
  167. return;
  168. QAngle oldAngles = prevAngles;
  169. if ( !physicsRotate )
  170. {
  171. oldAngles = pTeleportSource->GetAbsAngles();
  172. }
  173. matrix3x4_t startCoord, startInv, endCoord, xform;
  174. AngleMatrix( oldAngles, prevPosition, startCoord );
  175. MatrixInvert( startCoord, startInv );
  176. ConcatTransforms( pTeleportSource->EntityToWorldTransform(), startInv, xform );
  177. QAngle fixupAngles;
  178. Vector fixupPos;
  179. ConcatTransforms( xform, pFixup->EntityToWorldTransform(), endCoord );
  180. MatrixAngles( endCoord, fixupAngles, fixupPos );
  181. pFixup->Teleport( &fixupPos, &fixupAngles, NULL );
  182. }
  183. static void DrawPhysicsBounds( IPhysicsObject *pObject, int r, int g, int b, int a )
  184. {
  185. const CPhysCollide *pCollide = pObject->GetCollide();
  186. Vector pos;
  187. QAngle angles;
  188. pObject->GetPosition( &pos, &angles );
  189. Vector mins, maxs;
  190. physcollision->CollideGetAABB( &mins, &maxs, pCollide, vec3_origin, vec3_angle );
  191. // don't fight the z-buffer
  192. mins -= Vector(1,1,1);
  193. maxs += Vector(1,1,1);
  194. NDebugOverlay::BoxAngles( pos, mins, maxs, angles, r, g, b, a, 0 );
  195. }
  196. static void DrawConstraintObjectsAxes(CBaseEntity *pConstraintEntity, IPhysicsConstraint *pConstraint)
  197. {
  198. if ( !pConstraint || !pConstraintEntity )
  199. return;
  200. matrix3x4_t xformRef, xformAtt;
  201. bool bXform = pConstraint->GetConstraintTransform( &xformRef, &xformAtt );
  202. IPhysicsObject *pRef = pConstraint->GetReferenceObject();
  203. if ( pRef && !pRef->IsStatic() )
  204. {
  205. if ( bXform )
  206. {
  207. Vector pos, posWorld;
  208. QAngle angles;
  209. MatrixAngles( xformRef, angles, pos );
  210. pRef->LocalToWorld( &posWorld, pos );
  211. NDebugOverlay::Axis( posWorld, vec3_angle, 12, false, 0 );
  212. }
  213. DrawPhysicsBounds( pRef, 0, 255, 0, 12 );
  214. }
  215. IPhysicsObject *pAttach = pConstraint->GetAttachedObject();
  216. if ( pAttach && !pAttach->IsStatic() )
  217. {
  218. if ( bXform )
  219. {
  220. Vector pos, posWorld;
  221. QAngle angles;
  222. MatrixAngles( xformAtt, angles, pos );
  223. pAttach->LocalToWorld( &posWorld, pos );
  224. NDebugOverlay::Axis( posWorld, vec3_angle, 12, false, 0 );
  225. }
  226. DrawPhysicsBounds( pAttach, 255, 0, 0, 12 );
  227. }
  228. }
  229. abstract_class CPhysConstraint : public CLogicalEntity
  230. {
  231. DECLARE_CLASS( CPhysConstraint, CLogicalEntity );
  232. public:
  233. CPhysConstraint();
  234. ~CPhysConstraint();
  235. DECLARE_DATADESC();
  236. void Spawn( void );
  237. void Precache( void );
  238. void Activate( void );
  239. void ClearStaticFlag( IPhysicsObject *pObj )
  240. {
  241. if ( !pObj )
  242. return;
  243. PhysClearGameFlags( pObj, FVPHYSICS_CONSTRAINT_STATIC );
  244. }
  245. virtual void Deactivate()
  246. {
  247. if ( !m_pConstraint )
  248. return;
  249. m_pConstraint->Deactivate();
  250. ClearStaticFlag( m_pConstraint->GetReferenceObject() );
  251. ClearStaticFlag( m_pConstraint->GetAttachedObject() );
  252. if ( m_spawnflags & SF_CONSTRAINT_DISABLE_COLLISION )
  253. {
  254. // constraint may be getting deactivated because an object got deleted, so check them here.
  255. IPhysicsObject *pRef = m_pConstraint->GetReferenceObject();
  256. IPhysicsObject *pAtt = m_pConstraint->GetAttachedObject();
  257. if ( pRef && pAtt )
  258. {
  259. PhysEnableEntityCollisions( pRef, pAtt );
  260. }
  261. }
  262. }
  263. void OnBreak( void )
  264. {
  265. Deactivate();
  266. if ( m_breakSound != NULL_STRING )
  267. {
  268. CBroadcastRecipientFilter filter;
  269. Vector origin = GetAbsOrigin();
  270. Vector refPos = origin, attachPos = origin;
  271. IPhysicsObject *pRef = m_pConstraint->GetReferenceObject();
  272. if ( pRef && (pRef != g_PhysWorldObject) )
  273. {
  274. pRef->GetPosition( &refPos, NULL );
  275. attachPos = refPos;
  276. }
  277. IPhysicsObject *pAttach = m_pConstraint->GetAttachedObject();
  278. if ( pAttach && (pAttach != g_PhysWorldObject) )
  279. {
  280. pAttach->GetPosition( &attachPos, NULL );
  281. if ( !pRef || (pRef == g_PhysWorldObject) )
  282. {
  283. refPos = attachPos;
  284. }
  285. }
  286. VectorAdd( refPos, attachPos, origin );
  287. origin *= 0.5f;
  288. EmitSound_t ep;
  289. ep.m_nChannel = CHAN_STATIC;
  290. ep.m_pSoundName = STRING(m_breakSound);
  291. ep.m_flVolume = VOL_NORM;
  292. ep.m_SoundLevel = ATTN_TO_SNDLVL( ATTN_STATIC );
  293. ep.m_pOrigin = &origin;
  294. EmitSound( filter, entindex(), ep );
  295. }
  296. m_OnBreak.FireOutput( this, this );
  297. // queue this up to be deleted at the end of physics
  298. // The Deactivate() call should make sure we don't get more of these callbacks.
  299. PhysCallbackRemove( this->NetworkProp() );
  300. }
  301. void InputBreak( inputdata_t &inputdata )
  302. {
  303. if ( m_pConstraint )
  304. m_pConstraint->Deactivate();
  305. OnBreak();
  306. }
  307. void InputOnBreak( inputdata_t &inputdata )
  308. {
  309. OnBreak();
  310. }
  311. void InputTurnOn( inputdata_t &inputdata )
  312. {
  313. if ( HasSpawnFlags( SF_CONSTRAINT_NO_CONNECT_UNTIL_ACTIVATED ) )
  314. {
  315. ActivateConstraint();
  316. }
  317. if ( !m_pConstraint || !m_pConstraint->GetReferenceObject() || !m_pConstraint->GetAttachedObject() )
  318. return;
  319. m_pConstraint->Activate();
  320. m_pConstraint->GetReferenceObject()->Wake();
  321. m_pConstraint->GetAttachedObject()->Wake();
  322. }
  323. void InputTurnOff( inputdata_t &inputdata )
  324. {
  325. Deactivate();
  326. }
  327. int DrawDebugTextOverlays()
  328. {
  329. int pos = BaseClass::DrawDebugTextOverlays();
  330. if ( m_pConstraint && (m_debugOverlays & OVERLAY_TEXT_BIT) )
  331. {
  332. constraint_breakableparams_t params;
  333. Q_memset(&params,0,sizeof(params));
  334. m_pConstraint->GetConstraintParams( &params );
  335. if ( (params.bodyMassScale[0] != 1.0f && params.bodyMassScale[0] != 0.0f) || (params.bodyMassScale[1] != 1.0f && params.bodyMassScale[1] != 0.0f) )
  336. {
  337. CFmtStr str("mass ratio %.4f:%.4f\n", params.bodyMassScale[0], params.bodyMassScale[1] );
  338. NDebugOverlay::EntityTextAtPosition( GetAbsOrigin(), pos, str.Access(), 0, 255, 255, 0, 255 );
  339. }
  340. pos++;
  341. }
  342. return pos;
  343. }
  344. void DrawDebugGeometryOverlays()
  345. {
  346. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  347. {
  348. DrawConstraintObjectsAxes(this, m_pConstraint);
  349. }
  350. BaseClass::DrawDebugGeometryOverlays();
  351. }
  352. void GetBreakParams( constraint_breakableparams_t &params, const hl_constraint_info_t &info )
  353. {
  354. params.Defaults();
  355. params.forceLimit = lbs2kg(m_forceLimit);
  356. params.torqueLimit = lbs2kg(m_torqueLimit);
  357. params.isActive = HasSpawnFlags( SF_CONSTRAINT_START_INACTIVE ) ? false : true;
  358. params.bodyMassScale[0] = info.massScale[0];
  359. params.bodyMassScale[1] = info.massScale[1];
  360. }
  361. // the notify system calls this on the constrained entities - used to detect & follow teleports
  362. void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
  363. // gets called at setup time on first init and restore
  364. virtual void OnConstraintSetup( hl_constraint_info_t &info );
  365. // return the internal constraint object (used by sound gadgets)
  366. inline IPhysicsConstraint *GetPhysConstraint() { return m_pConstraint; }
  367. protected:
  368. void GetConstraintObjects( hl_constraint_info_t &info );
  369. void SetupTeleportationHandling( hl_constraint_info_t &info );
  370. bool ActivateConstraint( void );
  371. virtual IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info ) = 0;
  372. IPhysicsConstraint *m_pConstraint;
  373. // These are "template" values used to construct the hinge
  374. string_t m_nameAttach1;
  375. string_t m_nameAttach2;
  376. string_t m_breakSound;
  377. string_t m_nameSystem;
  378. float m_forceLimit;
  379. float m_torqueLimit;
  380. unsigned int m_teleportTick;
  381. float m_minTeleportDistance;
  382. COutputEvent m_OnBreak;
  383. };
  384. BEGIN_DATADESC( CPhysConstraint )
  385. DEFINE_PHYSPTR( m_pConstraint ),
  386. DEFINE_KEYFIELD( m_nameSystem, FIELD_STRING, "constraintsystem" ),
  387. DEFINE_KEYFIELD( m_nameAttach1, FIELD_STRING, "attach1" ),
  388. DEFINE_KEYFIELD( m_nameAttach2, FIELD_STRING, "attach2" ),
  389. DEFINE_KEYFIELD( m_breakSound, FIELD_SOUNDNAME, "breaksound" ),
  390. DEFINE_KEYFIELD( m_forceLimit, FIELD_FLOAT, "forcelimit" ),
  391. DEFINE_KEYFIELD( m_torqueLimit, FIELD_FLOAT, "torquelimit" ),
  392. DEFINE_KEYFIELD( m_minTeleportDistance, FIELD_FLOAT, "teleportfollowdistance" ),
  393. // DEFINE_FIELD( m_teleportTick, FIELD_INTEGER ),
  394. DEFINE_OUTPUT( m_OnBreak, "OnBreak" ),
  395. DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ),
  396. DEFINE_INPUTFUNC( FIELD_VOID, "ConstraintBroken", InputOnBreak ),
  397. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  398. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  399. END_DATADESC()
  400. CPhysConstraint::CPhysConstraint( void )
  401. {
  402. m_pConstraint = NULL;
  403. m_nameAttach1 = NULL_STRING;
  404. m_nameAttach2 = NULL_STRING;
  405. m_forceLimit = 0;
  406. m_torqueLimit = 0;
  407. m_teleportTick = 0xFFFFFFFF;
  408. m_minTeleportDistance = 0.0f;
  409. }
  410. CPhysConstraint::~CPhysConstraint()
  411. {
  412. Deactivate();
  413. physenv->DestroyConstraint( m_pConstraint );
  414. }
  415. void CPhysConstraint::Precache( void )
  416. {
  417. if ( m_breakSound != NULL_STRING )
  418. {
  419. PrecacheScriptSound( STRING(m_breakSound) );
  420. }
  421. }
  422. void CPhysConstraint::Spawn( void )
  423. {
  424. BaseClass::Spawn();
  425. Precache();
  426. }
  427. // debug function - slow, uses dynamic_cast<> - use this to query the attached objects
  428. // physics_debug_entity toggles the constraint system for an object using this
  429. bool GetConstraintAttachments( CBaseEntity *pEntity, CBaseEntity *pAttachOut[2], IPhysicsObject *pAttachVPhysics[2] )
  430. {
  431. CPhysConstraint *pConstraintEntity = dynamic_cast<CPhysConstraint *>(pEntity);
  432. if ( pConstraintEntity )
  433. {
  434. IPhysicsConstraint *pConstraint = pConstraintEntity->GetPhysConstraint();
  435. if ( pConstraint )
  436. {
  437. IPhysicsObject *pRef = pConstraint->GetReferenceObject();
  438. pAttachVPhysics[0] = pRef;
  439. pAttachOut[0] = pRef ? static_cast<CBaseEntity *>(pRef->GetGameData()) : NULL;
  440. IPhysicsObject *pAttach = pConstraint->GetAttachedObject();
  441. pAttachVPhysics[1] = pAttach;
  442. pAttachOut[1] = pAttach ? static_cast<CBaseEntity *>(pAttach->GetGameData()) : NULL;
  443. return true;
  444. }
  445. }
  446. return false;
  447. }
  448. void DebugConstraint(CBaseEntity *pEntity)
  449. {
  450. CPhysConstraint *pConstraintEntity = dynamic_cast<CPhysConstraint *>(pEntity);
  451. if ( pConstraintEntity )
  452. {
  453. IPhysicsConstraint *pConstraint = pConstraintEntity->GetPhysConstraint();
  454. if ( pConstraint )
  455. {
  456. pConstraint->OutputDebugInfo();
  457. }
  458. }
  459. }
  460. void FindPhysicsAnchor( string_t name, hl_constraint_info_t &info, int index, CBaseEntity *pErrorEntity )
  461. {
  462. constraint_anchor_t *pAnchor = g_AnchorList.Find( name );
  463. if ( pAnchor )
  464. {
  465. CBaseEntity *pEntity = pAnchor->hEntity;
  466. if ( pEntity )
  467. {
  468. info.massScale[index] = pAnchor->massScale;
  469. bool bWroteAttachment = false;
  470. if ( pAnchor->parentAttachment > 0 )
  471. {
  472. CBaseAnimating *pAnim = pAnchor->hEntity->GetBaseAnimating();
  473. if ( pAnim )
  474. {
  475. IPhysicsObject *list[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  476. int listCount = pAnchor->hEntity->VPhysicsGetObjectList( list, ARRAYSIZE(list) );
  477. int iPhysicsBone = pAnim->GetPhysicsBone( pAnim->GetAttachmentBone( pAnchor->parentAttachment ) );
  478. if ( iPhysicsBone < listCount )
  479. {
  480. Vector pos;
  481. info.pObjects[index] = list[iPhysicsBone];
  482. pAnim->GetAttachment( pAnchor->parentAttachment, pos );
  483. list[iPhysicsBone]->WorldToLocal( &info.anchorPosition[index], pos );
  484. bWroteAttachment = true;
  485. }
  486. }
  487. }
  488. if ( !bWroteAttachment )
  489. {
  490. info.anchorPosition[index] = pAnchor->localOrigin;
  491. info.pObjects[index] = pAnchor->hEntity->VPhysicsGetObject();
  492. }
  493. }
  494. else
  495. {
  496. pAnchor = NULL;
  497. }
  498. }
  499. if ( !pAnchor )
  500. {
  501. info.anchorPosition[index] = vec3_origin;
  502. info.pObjects[index] = FindPhysicsObjectByName( STRING(name), pErrorEntity );
  503. info.massScale[index] = 1.0f;
  504. }
  505. }
  506. void CPhysConstraint::OnConstraintSetup( hl_constraint_info_t &info )
  507. {
  508. if ( info.pObjects[0] && info.pObjects[1] )
  509. {
  510. SetupTeleportationHandling( info );
  511. }
  512. if ( m_spawnflags & SF_CONSTRAINT_DISABLE_COLLISION )
  513. {
  514. PhysDisableEntityCollisions( info.pObjects[0], info.pObjects[1] );
  515. }
  516. }
  517. void CPhysConstraint::SetupTeleportationHandling( hl_constraint_info_t &info )
  518. {
  519. CBaseEntity *pEntity0 = (CBaseEntity *)info.pObjects[0]->GetGameData();
  520. if ( pEntity0 )
  521. {
  522. g_pNotify->AddEntity( this, pEntity0 );
  523. }
  524. CBaseEntity *pEntity1 = (CBaseEntity *)info.pObjects[1]->GetGameData();
  525. if ( pEntity1 )
  526. {
  527. g_pNotify->AddEntity( this, pEntity1 );
  528. }
  529. }
  530. static IPhysicsConstraintGroup *GetRagdollConstraintGroup( IPhysicsObject *pObj )
  531. {
  532. if ( pObj )
  533. {
  534. CBaseEntity *pEntity = static_cast<CBaseEntity *>(pObj->GetGameData());
  535. ragdoll_t *pRagdoll = Ragdoll_GetRagdoll(pEntity);
  536. if ( pRagdoll )
  537. return pRagdoll->pGroup;
  538. }
  539. return NULL;
  540. }
  541. void CPhysConstraint::GetConstraintObjects( hl_constraint_info_t &info )
  542. {
  543. FindPhysicsAnchor( m_nameAttach1, info, 0, this );
  544. FindPhysicsAnchor( m_nameAttach2, info, 1, this );
  545. // Missing one object, assume the world instead
  546. if ( info.pObjects[0] == NULL && info.pObjects[1] )
  547. {
  548. if ( Q_strlen(STRING(m_nameAttach1)) )
  549. {
  550. Warning("Bogus constraint %s (attaches ENTITY NOT FOUND:%s to %s)\n", GetDebugName(), STRING(m_nameAttach1), STRING(m_nameAttach2));
  551. #ifdef HL2_EPISODIC
  552. info.pObjects[0] = info.pObjects[1] = NULL;
  553. return;
  554. #endif // HL2_EPISODIC
  555. }
  556. info.pObjects[0] = g_PhysWorldObject;
  557. info.massScale[0] = info.massScale[1] = 1.0f; // no mass scale on world constraint
  558. }
  559. else if ( info.pObjects[0] && !info.pObjects[1] )
  560. {
  561. if ( Q_strlen(STRING(m_nameAttach2)) )
  562. {
  563. Warning("Bogus constraint %s (attaches %s to ENTITY NOT FOUND:%s)\n", GetDebugName(), STRING(m_nameAttach1), STRING(m_nameAttach2));
  564. #ifdef HL2_EPISODIC
  565. info.pObjects[0] = info.pObjects[1] = NULL;
  566. return;
  567. #endif // HL2_EPISODIC
  568. }
  569. info.pObjects[1] = info.pObjects[0];
  570. info.pObjects[0] = g_PhysWorldObject; // Try to make the world object consistently object0 for ease of implementation
  571. info.massScale[0] = info.massScale[1] = 1.0f; // no mass scale on world constraint
  572. info.swapped = true;
  573. }
  574. info.pGroup = GetRagdollConstraintGroup(info.pObjects[0]);
  575. if ( !info.pGroup )
  576. {
  577. info.pGroup = GetRagdollConstraintGroup(info.pObjects[1]);
  578. }
  579. }
  580. void CPhysConstraint::Activate( void )
  581. {
  582. BaseClass::Activate();
  583. if ( HasSpawnFlags( SF_CONSTRAINT_NO_CONNECT_UNTIL_ACTIVATED ) == false )
  584. {
  585. if ( !ActivateConstraint() )
  586. {
  587. UTIL_Remove(this);
  588. }
  589. }
  590. }
  591. IPhysicsConstraintGroup *GetConstraintGroup( string_t systemName )
  592. {
  593. CBaseEntity *pMachine = gEntList.FindEntityByName( NULL, systemName );
  594. if ( pMachine )
  595. {
  596. CPhysConstraintSystem *pGroup = dynamic_cast<CPhysConstraintSystem *>(pMachine);
  597. if ( pGroup )
  598. {
  599. return pGroup->GetVPhysicsGroup();
  600. }
  601. }
  602. return NULL;
  603. }
  604. bool CPhysConstraint::ActivateConstraint( void )
  605. {
  606. // A constraint attaches two objects to each other.
  607. // The constraint is specified in the coordinate frame of the "reference" object
  608. // and constrains the "attached" object
  609. hl_constraint_info_t info;
  610. if ( m_pConstraint )
  611. {
  612. // already have a constraint, don't make a new one
  613. info.pObjects[0] = m_pConstraint->GetReferenceObject();
  614. info.pObjects[1] = m_pConstraint->GetAttachedObject();
  615. OnConstraintSetup(info);
  616. return true;
  617. }
  618. GetConstraintObjects( info );
  619. if ( !info.pObjects[0] && !info.pObjects[1] )
  620. return false;
  621. if ( info.pObjects[0]->IsStatic() && info.pObjects[1]->IsStatic() )
  622. {
  623. Warning("Constraint (%s) attached to two static objects (%s and %s)!!!\n", STRING(GetEntityName()), STRING(m_nameAttach1), m_nameAttach2 == NULL_STRING ? "world" : STRING(m_nameAttach2) );
  624. return false;
  625. }
  626. if ( info.pObjects[0]->GetShadowController() && info.pObjects[1]->GetShadowController() )
  627. {
  628. Warning("Constraint (%s) attached to two shadow objects (%s and %s)!!!\n", STRING(GetEntityName()), STRING(m_nameAttach1), m_nameAttach2 == NULL_STRING ? "world" : STRING(m_nameAttach2) );
  629. return false;
  630. }
  631. IPhysicsConstraintGroup *pGroup = GetConstraintGroup( m_nameSystem );
  632. if ( !pGroup )
  633. {
  634. pGroup = info.pGroup;
  635. }
  636. m_pConstraint = CreateConstraint( pGroup, info );
  637. if ( !m_pConstraint )
  638. return false;
  639. m_pConstraint->SetGameData( (void *)this );
  640. if ( pGroup )
  641. {
  642. pGroup->Activate();
  643. }
  644. OnConstraintSetup(info);
  645. return true;
  646. }
  647. void CPhysConstraint::NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  648. {
  649. // don't recurse
  650. if ( eventType != NOTIFY_EVENT_TELEPORT || (unsigned int)gpGlobals->tickcount == m_teleportTick )
  651. return;
  652. float distance = (params.pTeleport->prevOrigin - pNotify->GetAbsOrigin()).Length();
  653. // no need to follow a small teleport
  654. if ( distance <= m_minTeleportDistance )
  655. return;
  656. m_teleportTick = gpGlobals->tickcount;
  657. PhysTeleportConstrainedEntity( pNotify, m_pConstraint->GetReferenceObject(), m_pConstraint->GetAttachedObject(), params.pTeleport->prevOrigin, params.pTeleport->prevAngles, params.pTeleport->physicsRotate );
  658. }
  659. class CPhysHinge : public CPhysConstraint, public IVPhysicsWatcher
  660. {
  661. DECLARE_CLASS( CPhysHinge, CPhysConstraint );
  662. public:
  663. void Spawn( void );
  664. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  665. {
  666. if ( m_hinge.worldAxisDirection == vec3_origin )
  667. {
  668. DevMsg("ERROR: Hinge with bad data!!!\n" );
  669. return NULL;
  670. }
  671. GetBreakParams( m_hinge.constraint, info );
  672. m_hinge.constraint.strength = 1.0;
  673. // BUGBUG: These numbers are very hard to edit
  674. // Scale by 1000 to make things easier
  675. // CONSIDER: Unify the units of torque around something other
  676. // than HL units (kg * in^2 / s ^2)
  677. m_hinge.hingeAxis.SetAxisFriction( 0, 0, m_hingeFriction * 1000 );
  678. int hingeAxis = 0;
  679. if ( IsWorldHinge( info, &hingeAxis ) )
  680. {
  681. info.pObjects[1]->BecomeHinged( hingeAxis );
  682. }
  683. else
  684. {
  685. RemoveSpawnFlags( SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY );
  686. }
  687. return physenv->CreateHingeConstraint( info.pObjects[0], info.pObjects[1], pGroup, m_hinge );
  688. }
  689. void DrawDebugGeometryOverlays()
  690. {
  691. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  692. {
  693. NDebugOverlay::Line(m_hinge.worldPosition, m_hinge.worldPosition + 48 * m_hinge.worldAxisDirection, 0, 255, 0, false, 0 );
  694. }
  695. BaseClass::DrawDebugGeometryOverlays();
  696. }
  697. void InputSetVelocity( inputdata_t &inputdata )
  698. {
  699. if ( !m_pConstraint || !m_pConstraint->GetReferenceObject() || !m_pConstraint->GetAttachedObject() )
  700. return;
  701. float speed = inputdata.value.Float();
  702. float massLoad = 1;
  703. int numMasses = 0;
  704. if ( m_pConstraint->GetReferenceObject()->IsMoveable() )
  705. {
  706. massLoad = m_pConstraint->GetReferenceObject()->GetInertia().Length();
  707. numMasses++;
  708. m_pConstraint->GetReferenceObject()->Wake();
  709. }
  710. if ( m_pConstraint->GetAttachedObject()->IsMoveable() )
  711. {
  712. massLoad += m_pConstraint->GetAttachedObject()->GetInertia().Length();
  713. numMasses++;
  714. m_pConstraint->GetAttachedObject()->Wake();
  715. }
  716. if ( numMasses > 0 )
  717. {
  718. massLoad /= (float)numMasses;
  719. }
  720. float loadscale = m_systemLoadScale != 0 ? m_systemLoadScale : 1;
  721. m_pConstraint->SetAngularMotor( speed, speed * loadscale * massLoad * loadscale * (1.0/TICK_INTERVAL) );
  722. }
  723. void InputSetHingeFriction( inputdata_t &inputdata )
  724. {
  725. m_hingeFriction = inputdata.value.Float();
  726. Msg("Setting hinge friction to %f\n", m_hingeFriction );
  727. m_hinge.hingeAxis.SetAxisFriction( 0, 0, m_hingeFriction * 1000 );
  728. }
  729. virtual void Deactivate()
  730. {
  731. if ( HasSpawnFlags( SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY ) )
  732. {
  733. if ( m_pConstraint && m_pConstraint->GetAttachedObject() )
  734. {
  735. // NOTE: RemoveHinged() is always safe
  736. m_pConstraint->GetAttachedObject()->RemoveHinged();
  737. }
  738. }
  739. BaseClass::Deactivate();
  740. }
  741. void NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
  742. {
  743. #if HINGE_NOTIFY
  744. Assert(m_pConstraint);
  745. if (!m_pConstraint)
  746. return;
  747. // if something woke up, start thinking. If everything is asleep, stop thinking.
  748. if ( bAwake )
  749. {
  750. // Did something wake up when I was not thinking?
  751. if ( GetNextThink() == TICK_NEVER_THINK )
  752. {
  753. m_soundInfo.StartThinking(this,
  754. VelocitySampler::GetRelativeAngularVelocity(m_pConstraint->GetAttachedObject(), m_pConstraint->GetReferenceObject()) ,
  755. m_hinge.worldAxisDirection
  756. );
  757. SetThink(&CPhysHinge::SoundThink);
  758. SetNextThink(gpGlobals->curtime + m_soundInfo.getThinkRate());
  759. }
  760. }
  761. else
  762. {
  763. // Is everything asleep? If so, stop thinking.
  764. if ( GetNextThink() != TICK_NEVER_THINK &&
  765. m_pConstraint->GetAttachedObject()->IsAsleep() &&
  766. m_pConstraint->GetReferenceObject()->IsAsleep() )
  767. {
  768. m_soundInfo.StopThinking(this);
  769. SetNextThink(TICK_NEVER_THINK);
  770. }
  771. }
  772. #endif
  773. }
  774. #if HINGE_NOTIFY
  775. virtual void OnConstraintSetup( hl_constraint_info_t &info )
  776. {
  777. CBaseEntity *pEntity0 = info.pObjects[0] ? static_cast<CBaseEntity *>(info.pObjects[0]->GetGameData()) : NULL;
  778. if ( pEntity0 && !info.pObjects[0]->IsStatic() )
  779. {
  780. WatchVPhysicsStateChanges( this, pEntity0 );
  781. }
  782. CBaseEntity *pEntity1 = info.pObjects[1] ? static_cast<CBaseEntity *>(info.pObjects[1]->GetGameData()) : NULL;
  783. if ( pEntity1 && !info.pObjects[1]->IsStatic() )
  784. {
  785. WatchVPhysicsStateChanges( this, pEntity1 );
  786. }
  787. BaseClass::OnConstraintSetup(info);
  788. }
  789. void SoundThink( void );
  790. // void Spawn( void );
  791. void Activate( void );
  792. void Precache( void );
  793. #endif
  794. DECLARE_DATADESC();
  795. #if HINGE_NOTIFY
  796. protected:
  797. ConstraintSoundInfo m_soundInfo;
  798. #endif
  799. private:
  800. constraint_hingeparams_t m_hinge;
  801. float m_hingeFriction;
  802. float m_systemLoadScale;
  803. bool IsWorldHinge( const hl_constraint_info_t &info, int *pAxisOut );
  804. };
  805. BEGIN_DATADESC( CPhysHinge )
  806. // Quiet down classcheck
  807. // DEFINE_FIELD( m_hinge, FIELD_??? ),
  808. DEFINE_KEYFIELD( m_hingeFriction, FIELD_FLOAT, "hingefriction" ),
  809. DEFINE_FIELD( m_hinge.worldPosition, FIELD_POSITION_VECTOR ),
  810. DEFINE_KEYFIELD( m_hinge.worldAxisDirection, FIELD_VECTOR, "hingeaxis" ),
  811. DEFINE_KEYFIELD( m_systemLoadScale, FIELD_FLOAT, "systemloadscale" ),
  812. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetAngularVelocity", InputSetVelocity ),
  813. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetHingeFriction", InputSetHingeFriction ),
  814. #if HINGE_NOTIFY
  815. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_keyPoints[SimpleConstraintSoundProfile::kMIN_THRESHOLD] , FIELD_FLOAT, "minSoundThreshold" ),
  816. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_keyPoints[SimpleConstraintSoundProfile::kMIN_FULL] , FIELD_FLOAT, "maxSoundThreshold" ),
  817. DEFINE_KEYFIELD( m_soundInfo.m_iszTravelSoundFwd, FIELD_SOUNDNAME, "slidesoundfwd" ),
  818. DEFINE_KEYFIELD( m_soundInfo.m_iszTravelSoundBack, FIELD_SOUNDNAME, "slidesoundback" ),
  819. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[0], FIELD_SOUNDNAME, "reversalsoundSmall" ),
  820. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[1], FIELD_SOUNDNAME, "reversalsoundMedium" ),
  821. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[2], FIELD_SOUNDNAME, "reversalsoundLarge" ),
  822. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[0] , FIELD_FLOAT, "reversalsoundthresholdSmall" ),
  823. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[1], FIELD_FLOAT, "reversalsoundthresholdMedium" ),
  824. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[2] , FIELD_FLOAT, "reversalsoundthresholdLarge" ),
  825. DEFINE_THINKFUNC( SoundThink ),
  826. #endif
  827. END_DATADESC()
  828. LINK_ENTITY_TO_CLASS( phys_hinge, CPhysHinge );
  829. void CPhysHinge::Spawn( void )
  830. {
  831. m_hinge.worldPosition = GetLocalOrigin();
  832. m_hinge.worldAxisDirection -= GetLocalOrigin();
  833. VectorNormalize(m_hinge.worldAxisDirection);
  834. UTIL_SnapDirectionToAxis( m_hinge.worldAxisDirection );
  835. m_hinge.hingeAxis.SetAxisFriction( 0, 0, 0 );
  836. if ( HasSpawnFlags( SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY ) )
  837. {
  838. masscenteroverride_t params;
  839. if ( m_nameAttach1 == NULL_STRING )
  840. {
  841. params.SnapToAxis( m_nameAttach2, m_hinge.worldPosition, m_hinge.worldAxisDirection );
  842. PhysSetMassCenterOverride( params );
  843. }
  844. else if ( m_nameAttach2 == NULL_STRING )
  845. {
  846. params.SnapToAxis( m_nameAttach1, m_hinge.worldPosition, m_hinge.worldAxisDirection );
  847. PhysSetMassCenterOverride( params );
  848. }
  849. else
  850. {
  851. RemoveSpawnFlags( SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY );
  852. }
  853. }
  854. Precache();
  855. }
  856. #if HINGE_NOTIFY
  857. void CPhysHinge::Activate( void )
  858. {
  859. BaseClass::Activate();
  860. m_soundInfo.OnActivate(this);
  861. if (m_pConstraint)
  862. {
  863. m_soundInfo.StartThinking(this,
  864. VelocitySampler::GetRelativeAngularVelocity(m_pConstraint->GetAttachedObject(), m_pConstraint->GetReferenceObject()) ,
  865. m_hinge.worldAxisDirection
  866. );
  867. SetThink(&CPhysHinge::SoundThink);
  868. SetNextThink( gpGlobals->curtime + m_soundInfo.getThinkRate() );
  869. }
  870. }
  871. void CPhysHinge::Precache( void )
  872. {
  873. BaseClass::Precache();
  874. return m_soundInfo.OnPrecache(this);
  875. }
  876. #endif
  877. static int GetUnitAxisIndex( const Vector &axis )
  878. {
  879. bool valid = false;
  880. int index = -1;
  881. for ( int i = 0; i < 3; i++ )
  882. {
  883. if ( axis[i] != 0 )
  884. {
  885. if ( fabs(axis[i]) == 1 )
  886. {
  887. if ( index < 0 )
  888. {
  889. index = i;
  890. valid = true;
  891. continue;
  892. }
  893. }
  894. valid = false;
  895. }
  896. }
  897. return valid ? index : -1;
  898. }
  899. bool CPhysHinge::IsWorldHinge( const hl_constraint_info_t &info, int *pAxisOut )
  900. {
  901. if ( HasSpawnFlags( SF_CONSTRAINT_ASSUME_WORLD_GEOMETRY ) && info.pObjects[0] == g_PhysWorldObject )
  902. {
  903. Vector localHinge;
  904. info.pObjects[1]->WorldToLocalVector( &localHinge, m_hinge.worldAxisDirection );
  905. UTIL_SnapDirectionToAxis( localHinge );
  906. int hingeAxis = GetUnitAxisIndex( localHinge );
  907. if ( hingeAxis >= 0 )
  908. {
  909. *pAxisOut = hingeAxis;
  910. return true;
  911. }
  912. }
  913. return false;
  914. }
  915. #if HINGE_NOTIFY
  916. void CPhysHinge::SoundThink( void )
  917. {
  918. Assert(m_pConstraint);
  919. if (!m_pConstraint)
  920. return;
  921. IPhysicsObject * pAttached = m_pConstraint->GetAttachedObject(), *pReference = m_pConstraint->GetReferenceObject();
  922. Assert( pAttached && pReference );
  923. if (pAttached && pReference)
  924. {
  925. Vector relativeVel = VelocitySampler::GetRelativeAngularVelocity(pAttached,pReference);
  926. if (g_debug_constraint_sounds.GetBool())
  927. {
  928. NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + (relativeVel), 255, 255, 0, true, 0.1f );
  929. }
  930. m_soundInfo.OnThink( this, relativeVel );
  931. SetNextThink(gpGlobals->curtime + m_soundInfo.getThinkRate());
  932. }
  933. }
  934. #endif
  935. class CPhysBallSocket : public CPhysConstraint
  936. {
  937. public:
  938. DECLARE_CLASS( CPhysBallSocket, CPhysConstraint );
  939. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  940. {
  941. constraint_ballsocketparams_t ballsocket;
  942. ballsocket.Defaults();
  943. for ( int i = 0; i < 2; i++ )
  944. {
  945. info.pObjects[i]->WorldToLocal( &ballsocket.constraintPosition[i], GetAbsOrigin() );
  946. }
  947. GetBreakParams( ballsocket.constraint, info );
  948. ballsocket.constraint.torqueLimit = 0;
  949. return physenv->CreateBallsocketConstraint( info.pObjects[0], info.pObjects[1], pGroup, ballsocket );
  950. }
  951. };
  952. LINK_ENTITY_TO_CLASS( phys_ballsocket, CPhysBallSocket );
  953. class CPhysSlideConstraint : public CPhysConstraint, public IVPhysicsWatcher
  954. {
  955. public:
  956. DECLARE_CLASS( CPhysSlideConstraint, CPhysConstraint );
  957. DECLARE_DATADESC();
  958. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info );
  959. void InputSetVelocity( inputdata_t &inputdata )
  960. {
  961. if ( !m_pConstraint || !m_pConstraint->GetReferenceObject() || !m_pConstraint->GetAttachedObject() )
  962. return;
  963. float speed = inputdata.value.Float();
  964. float massLoad = 1;
  965. int numMasses = 0;
  966. if ( m_pConstraint->GetReferenceObject()->IsMoveable() )
  967. {
  968. massLoad = m_pConstraint->GetReferenceObject()->GetMass();
  969. numMasses++;
  970. m_pConstraint->GetReferenceObject()->Wake();
  971. }
  972. if ( m_pConstraint->GetAttachedObject()->IsMoveable() )
  973. {
  974. massLoad += m_pConstraint->GetAttachedObject()->GetMass();
  975. numMasses++;
  976. m_pConstraint->GetAttachedObject()->Wake();
  977. }
  978. if ( numMasses > 0 )
  979. {
  980. massLoad /= (float)numMasses;
  981. }
  982. float loadscale = m_systemLoadScale != 0 ? m_systemLoadScale : 1;
  983. m_pConstraint->SetLinearMotor( speed, speed * loadscale * massLoad * (1.0/TICK_INTERVAL) );
  984. }
  985. void DrawDebugGeometryOverlays()
  986. {
  987. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  988. {
  989. NDebugOverlay::Box( GetAbsOrigin(), -Vector(8,8,8), Vector(8,8,8), 0, 255, 0, 0, 0 );
  990. NDebugOverlay::Box( m_axisEnd, -Vector(4,4,4), Vector(4,4,4), 0, 0, 255, 0, 0 );
  991. NDebugOverlay::Line( GetAbsOrigin(), m_axisEnd, 255, 255, 0, false, 0 );
  992. }
  993. BaseClass::DrawDebugGeometryOverlays();
  994. }
  995. void NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
  996. {
  997. #if HINGE_NOTIFY
  998. Assert(m_pConstraint);
  999. if (!m_pConstraint)
  1000. return;
  1001. // if something woke up, start thinking. If everything is asleep, stop thinking.
  1002. if ( bAwake )
  1003. {
  1004. // Did something wake up when I was not thinking?
  1005. if ( GetNextThink() == TICK_NEVER_THINK )
  1006. {
  1007. Vector axisDirection = m_axisEnd - GetAbsOrigin();
  1008. VectorNormalize( axisDirection );
  1009. UTIL_SnapDirectionToAxis( axisDirection );
  1010. m_soundInfo.StartThinking(this,
  1011. VelocitySampler::GetRelativeVelocity(m_pConstraint->GetAttachedObject(), m_pConstraint->GetReferenceObject()),
  1012. axisDirection
  1013. );
  1014. SetThink(&CPhysSlideConstraint::SoundThink);
  1015. SetNextThink(gpGlobals->curtime + m_soundInfo.getThinkRate());
  1016. }
  1017. }
  1018. else
  1019. {
  1020. // Is everything asleep? If so, stop thinking.
  1021. if ( GetNextThink() != TICK_NEVER_THINK &&
  1022. m_pConstraint->GetAttachedObject()->IsAsleep() &&
  1023. m_pConstraint->GetReferenceObject()->IsAsleep() )
  1024. {
  1025. m_soundInfo.StopThinking(this);
  1026. SetNextThink(TICK_NEVER_THINK);
  1027. }
  1028. }
  1029. #endif
  1030. }
  1031. #if HINGE_NOTIFY
  1032. virtual void OnConstraintSetup( hl_constraint_info_t &info )
  1033. {
  1034. CBaseEntity *pEntity0 = info.pObjects[0] ? static_cast<CBaseEntity *>(info.pObjects[0]->GetGameData()) : NULL;
  1035. if ( pEntity0 && !info.pObjects[0]->IsStatic() )
  1036. {
  1037. WatchVPhysicsStateChanges( this, pEntity0 );
  1038. }
  1039. CBaseEntity *pEntity1 = info.pObjects[1] ? static_cast<CBaseEntity *>(info.pObjects[1]->GetGameData()) : NULL;
  1040. if ( pEntity1 && !info.pObjects[1]->IsStatic() )
  1041. {
  1042. WatchVPhysicsStateChanges( this, pEntity1 );
  1043. }
  1044. BaseClass::OnConstraintSetup(info);
  1045. }
  1046. void SoundThink( void );
  1047. // void Spawn( void );
  1048. void Activate( void );
  1049. void Precache( void );
  1050. #endif
  1051. Vector m_axisEnd;
  1052. float m_slideFriction;
  1053. float m_systemLoadScale;
  1054. #if HINGE_NOTIFY
  1055. protected:
  1056. ConstraintSoundInfo m_soundInfo;
  1057. #endif
  1058. };
  1059. LINK_ENTITY_TO_CLASS( phys_slideconstraint, CPhysSlideConstraint );
  1060. BEGIN_DATADESC( CPhysSlideConstraint )
  1061. DEFINE_KEYFIELD( m_axisEnd, FIELD_POSITION_VECTOR, "slideaxis" ),
  1062. DEFINE_KEYFIELD( m_slideFriction, FIELD_FLOAT, "slidefriction" ),
  1063. DEFINE_KEYFIELD( m_systemLoadScale, FIELD_FLOAT, "systemloadscale" ),
  1064. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetVelocity", InputSetVelocity ),
  1065. #if HINGE_NOTIFY
  1066. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_keyPoints[SimpleConstraintSoundProfile::kMIN_THRESHOLD] , FIELD_FLOAT, "minSoundThreshold" ),
  1067. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_keyPoints[SimpleConstraintSoundProfile::kMIN_FULL] , FIELD_FLOAT, "maxSoundThreshold" ),
  1068. DEFINE_KEYFIELD( m_soundInfo.m_iszTravelSoundFwd, FIELD_SOUNDNAME, "slidesoundfwd" ),
  1069. DEFINE_KEYFIELD( m_soundInfo.m_iszTravelSoundBack, FIELD_SOUNDNAME, "slidesoundback" ),
  1070. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[0], FIELD_SOUNDNAME, "reversalsoundSmall" ),
  1071. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[1], FIELD_SOUNDNAME, "reversalsoundMedium" ),
  1072. DEFINE_KEYFIELD( m_soundInfo.m_iszReversalSounds[2], FIELD_SOUNDNAME, "reversalsoundLarge" ),
  1073. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[0] , FIELD_FLOAT, "reversalsoundthresholdSmall" ),
  1074. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[1], FIELD_FLOAT, "reversalsoundthresholdMedium" ),
  1075. DEFINE_KEYFIELD( m_soundInfo.m_soundProfile.m_reversalSoundThresholds[2] , FIELD_FLOAT, "reversalsoundthresholdLarge" ),
  1076. DEFINE_THINKFUNC( SoundThink ),
  1077. #endif
  1078. END_DATADESC()
  1079. IPhysicsConstraint *CPhysSlideConstraint::CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  1080. {
  1081. constraint_slidingparams_t sliding;
  1082. sliding.Defaults();
  1083. GetBreakParams( sliding.constraint, info );
  1084. sliding.constraint.strength = 1.0;
  1085. Vector axisDirection = m_axisEnd - GetAbsOrigin();
  1086. VectorNormalize( axisDirection );
  1087. UTIL_SnapDirectionToAxis( axisDirection );
  1088. sliding.InitWithCurrentObjectState( info.pObjects[0], info.pObjects[1], axisDirection );
  1089. sliding.friction = m_slideFriction;
  1090. if ( m_spawnflags & SF_SLIDE_LIMIT_ENDS )
  1091. {
  1092. Vector position;
  1093. info.pObjects[1]->GetPosition( &position, NULL );
  1094. sliding.limitMin = DotProduct( axisDirection, GetAbsOrigin() );
  1095. sliding.limitMax = DotProduct( axisDirection, m_axisEnd );
  1096. if ( sliding.limitMax < sliding.limitMin )
  1097. {
  1098. V_swap( sliding.limitMin, sliding.limitMax );
  1099. }
  1100. // expand limits to make initial position of the attached object valid
  1101. float limit = DotProduct( position, axisDirection );
  1102. if ( limit < sliding.limitMin )
  1103. {
  1104. sliding.limitMin = limit;
  1105. }
  1106. else if ( limit > sliding.limitMax )
  1107. {
  1108. sliding.limitMax = limit;
  1109. }
  1110. // offset so that the current position is 0
  1111. sliding.limitMin -= limit;
  1112. sliding.limitMax -= limit;
  1113. }
  1114. return physenv->CreateSlidingConstraint( info.pObjects[0], info.pObjects[1], pGroup, sliding );
  1115. }
  1116. #if HINGE_NOTIFY
  1117. void CPhysSlideConstraint::SoundThink( void )
  1118. {
  1119. Assert(m_pConstraint);
  1120. if (!m_pConstraint)
  1121. return;
  1122. IPhysicsObject * pAttached = m_pConstraint->GetAttachedObject(), *pReference = m_pConstraint->GetReferenceObject();
  1123. Assert( pAttached && pReference );
  1124. if (pAttached && pReference)
  1125. {
  1126. Vector relativeVel = VelocitySampler::GetRelativeVelocity(pAttached,pReference);
  1127. // project velocity onto my primary axis.:
  1128. Vector axisDirection = m_axisEnd - GetAbsOrigin();
  1129. relativeVel = m_axisEnd * relativeVel.Dot(m_axisEnd)/m_axisEnd.Dot(m_axisEnd);
  1130. m_soundInfo.OnThink( this, relativeVel );
  1131. SetNextThink(gpGlobals->curtime + m_soundInfo.getThinkRate());
  1132. }
  1133. }
  1134. void CPhysSlideConstraint::Activate( void )
  1135. {
  1136. BaseClass::Activate();
  1137. m_soundInfo.OnActivate(this);
  1138. Vector axisDirection = m_axisEnd - GetAbsOrigin();
  1139. VectorNormalize( axisDirection );
  1140. UTIL_SnapDirectionToAxis( axisDirection );
  1141. if ( m_pConstraint )
  1142. {
  1143. m_soundInfo.StartThinking(this,
  1144. VelocitySampler::GetRelativeVelocity(m_pConstraint->GetAttachedObject(), m_pConstraint->GetReferenceObject()),
  1145. axisDirection
  1146. );
  1147. SetThink(&CPhysSlideConstraint::SoundThink);
  1148. SetNextThink(gpGlobals->curtime + m_soundInfo.getThinkRate());
  1149. }
  1150. }
  1151. void CPhysSlideConstraint::Precache()
  1152. {
  1153. m_soundInfo.OnPrecache(this);
  1154. }
  1155. #endif
  1156. //-----------------------------------------------------------------------------
  1157. // Purpose: Fixed breakable constraint
  1158. //-----------------------------------------------------------------------------
  1159. class CPhysFixed : public CPhysConstraint
  1160. {
  1161. DECLARE_CLASS( CPhysFixed, CPhysConstraint );
  1162. public:
  1163. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info );
  1164. // just for debugging - move to the position of the reference entity
  1165. void MoveToRefPosition()
  1166. {
  1167. if ( m_pConstraint )
  1168. {
  1169. matrix3x4_t xformRef;
  1170. m_pConstraint->GetConstraintTransform( &xformRef, NULL );
  1171. IPhysicsObject *pObj = m_pConstraint->GetReferenceObject();
  1172. if ( pObj && pObj->IsMoveable() )
  1173. {
  1174. Vector pos, posWorld;
  1175. MatrixPosition( xformRef, pos );
  1176. pObj->LocalToWorld(&posWorld, pos);
  1177. SetAbsOrigin(posWorld);
  1178. }
  1179. }
  1180. }
  1181. int DrawDebugTextOverlays()
  1182. {
  1183. if ( m_debugOverlays & OVERLAY_TEXT_BIT )
  1184. {
  1185. MoveToRefPosition();
  1186. }
  1187. return BaseClass::DrawDebugTextOverlays();
  1188. }
  1189. void DrawDebugGeometryOverlays()
  1190. {
  1191. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  1192. {
  1193. MoveToRefPosition();
  1194. }
  1195. BaseClass::DrawDebugGeometryOverlays();
  1196. }
  1197. };
  1198. LINK_ENTITY_TO_CLASS( phys_constraint, CPhysFixed );
  1199. //-----------------------------------------------------------------------------
  1200. // Purpose: Activate/create the constraint
  1201. //-----------------------------------------------------------------------------
  1202. IPhysicsConstraint *CPhysFixed::CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  1203. {
  1204. constraint_fixedparams_t fixed;
  1205. fixed.Defaults();
  1206. fixed.InitWithCurrentObjectState( info.pObjects[0], info.pObjects[1] );
  1207. GetBreakParams( fixed.constraint, info );
  1208. // constraining to the world means object 1 is fixed
  1209. if ( info.pObjects[0] == g_PhysWorldObject )
  1210. {
  1211. PhysSetGameFlags( info.pObjects[1], FVPHYSICS_CONSTRAINT_STATIC );
  1212. }
  1213. return physenv->CreateFixedConstraint( info.pObjects[0], info.pObjects[1], pGroup, fixed );
  1214. }
  1215. //-----------------------------------------------------------------------------
  1216. // Purpose: Breakable pulley w/ropes constraint
  1217. //-----------------------------------------------------------------------------
  1218. class CPhysPulley : public CPhysConstraint
  1219. {
  1220. DECLARE_CLASS( CPhysPulley, CPhysConstraint );
  1221. public:
  1222. DECLARE_DATADESC();
  1223. void DrawDebugGeometryOverlays()
  1224. {
  1225. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  1226. {
  1227. Vector origin = GetAbsOrigin();
  1228. Vector refPos = origin, attachPos = origin;
  1229. IPhysicsObject *pRef = m_pConstraint->GetReferenceObject();
  1230. if ( pRef )
  1231. {
  1232. matrix3x4_t matrix;
  1233. pRef->GetPositionMatrix( &matrix );
  1234. VectorTransform( m_offset[0], matrix, refPos );
  1235. }
  1236. IPhysicsObject *pAttach = m_pConstraint->GetAttachedObject();
  1237. if ( pAttach )
  1238. {
  1239. matrix3x4_t matrix;
  1240. pAttach->GetPositionMatrix( &matrix );
  1241. VectorTransform( m_offset[1], matrix, attachPos );
  1242. }
  1243. NDebugOverlay::Line( refPos, origin, 0, 255, 0, false, 0 );
  1244. NDebugOverlay::Line( origin, m_position2, 128, 128, 128, false, 0 );
  1245. NDebugOverlay::Line( m_position2, attachPos, 0, 255, 0, false, 0 );
  1246. NDebugOverlay::Box( origin, -Vector(8,8,8), Vector(8,8,8), 128, 255, 128, 32, 0 );
  1247. NDebugOverlay::Box( m_position2, -Vector(8,8,8), Vector(8,8,8), 255, 128, 128, 32, 0 );
  1248. }
  1249. BaseClass::DrawDebugGeometryOverlays();
  1250. }
  1251. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info );
  1252. private:
  1253. Vector m_position2;
  1254. Vector m_offset[2];
  1255. float m_addLength;
  1256. float m_gearRatio;
  1257. };
  1258. BEGIN_DATADESC( CPhysPulley )
  1259. DEFINE_KEYFIELD( m_position2, FIELD_POSITION_VECTOR, "position2" ),
  1260. DEFINE_AUTO_ARRAY( m_offset, FIELD_VECTOR ),
  1261. DEFINE_KEYFIELD( m_addLength, FIELD_FLOAT, "addlength" ),
  1262. DEFINE_KEYFIELD( m_gearRatio, FIELD_FLOAT, "gearratio" ),
  1263. END_DATADESC()
  1264. LINK_ENTITY_TO_CLASS( phys_pulleyconstraint, CPhysPulley );
  1265. //-----------------------------------------------------------------------------
  1266. // Purpose: Activate/create the constraint
  1267. //-----------------------------------------------------------------------------
  1268. IPhysicsConstraint *CPhysPulley::CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  1269. {
  1270. constraint_pulleyparams_t pulley;
  1271. pulley.Defaults();
  1272. pulley.pulleyPosition[0] = GetAbsOrigin();
  1273. pulley.pulleyPosition[1] = m_position2;
  1274. matrix3x4_t matrix;
  1275. Vector world[2];
  1276. info.pObjects[0]->GetPositionMatrix( &matrix );
  1277. VectorTransform( info.anchorPosition[0], matrix, world[0] );
  1278. info.pObjects[1]->GetPositionMatrix( &matrix );
  1279. VectorTransform( info.anchorPosition[1], matrix, world[1] );
  1280. for ( int i = 0; i < 2; i++ )
  1281. {
  1282. pulley.objectPosition[i] = info.anchorPosition[i];
  1283. m_offset[i] = info.anchorPosition[i];
  1284. }
  1285. pulley.totalLength = m_addLength +
  1286. (world[0] - pulley.pulleyPosition[0]).Length() +
  1287. ((world[1] - pulley.pulleyPosition[1]).Length() * m_gearRatio);
  1288. if ( m_gearRatio != 0 )
  1289. {
  1290. pulley.gearRatio = m_gearRatio;
  1291. }
  1292. GetBreakParams( pulley.constraint, info );
  1293. if ( m_spawnflags & SF_PULLEY_RIGID )
  1294. {
  1295. pulley.isRigid = true;
  1296. }
  1297. return physenv->CreatePulleyConstraint( info.pObjects[0], info.pObjects[1], pGroup, pulley );
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose: Breakable rope/length constraint
  1301. //-----------------------------------------------------------------------------
  1302. class CPhysLength : public CPhysConstraint
  1303. {
  1304. DECLARE_CLASS( CPhysLength, CPhysConstraint );
  1305. public:
  1306. DECLARE_DATADESC();
  1307. void DrawDebugGeometryOverlays()
  1308. {
  1309. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  1310. {
  1311. Vector origin = GetAbsOrigin();
  1312. Vector refPos = origin, attachPos = origin;
  1313. IPhysicsObject *pRef = m_pConstraint->GetReferenceObject();
  1314. if ( pRef )
  1315. {
  1316. matrix3x4_t matrix;
  1317. pRef->GetPositionMatrix( &matrix );
  1318. VectorTransform( m_offset[0], matrix, refPos );
  1319. }
  1320. IPhysicsObject *pAttach = m_pConstraint->GetAttachedObject();
  1321. if ( pAttach )
  1322. {
  1323. matrix3x4_t matrix;
  1324. pAttach->GetPositionMatrix( &matrix );
  1325. VectorTransform( m_offset[1], matrix, attachPos );
  1326. }
  1327. Vector dir = attachPos - refPos;
  1328. float len = VectorNormalize(dir);
  1329. if ( len > m_totalLength )
  1330. {
  1331. Vector mid = refPos + dir * m_totalLength;
  1332. NDebugOverlay::Line( refPos, mid, 0, 255, 0, false, 0 );
  1333. NDebugOverlay::Line( mid, attachPos, 255, 0, 0, false, 0 );
  1334. }
  1335. else
  1336. {
  1337. NDebugOverlay::Line( refPos, attachPos, 0, 255, 0, false, 0 );
  1338. }
  1339. }
  1340. BaseClass::DrawDebugGeometryOverlays();
  1341. }
  1342. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info );
  1343. private:
  1344. Vector m_offset[2];
  1345. Vector m_vecAttach;
  1346. float m_addLength;
  1347. float m_minLength;
  1348. float m_totalLength;
  1349. };
  1350. BEGIN_DATADESC( CPhysLength )
  1351. DEFINE_AUTO_ARRAY( m_offset, FIELD_VECTOR ),
  1352. DEFINE_KEYFIELD( m_addLength, FIELD_FLOAT, "addlength" ),
  1353. DEFINE_KEYFIELD( m_minLength, FIELD_FLOAT, "minlength" ),
  1354. DEFINE_KEYFIELD( m_vecAttach, FIELD_POSITION_VECTOR, "attachpoint" ),
  1355. DEFINE_FIELD( m_totalLength, FIELD_FLOAT ),
  1356. END_DATADESC()
  1357. LINK_ENTITY_TO_CLASS( phys_lengthconstraint, CPhysLength );
  1358. //-----------------------------------------------------------------------------
  1359. // Purpose: Activate/create the constraint
  1360. //-----------------------------------------------------------------------------
  1361. IPhysicsConstraint *CPhysLength::CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  1362. {
  1363. constraint_lengthparams_t length;
  1364. length.Defaults();
  1365. Vector position[2];
  1366. position[0] = GetAbsOrigin();
  1367. position[1] = m_vecAttach;
  1368. int index = info.swapped ? 1 : 0;
  1369. length.InitWorldspace( info.pObjects[0], info.pObjects[1], position[index], position[!index] );
  1370. length.totalLength += m_addLength;
  1371. length.minLength = m_minLength;
  1372. m_totalLength = length.totalLength;
  1373. if ( HasSpawnFlags(SF_LENGTH_RIGID) )
  1374. {
  1375. length.minLength = length.totalLength;
  1376. }
  1377. for ( int i = 0; i < 2; i++ )
  1378. {
  1379. m_offset[i] = length.objectPosition[i];
  1380. }
  1381. GetBreakParams( length.constraint, info );
  1382. return physenv->CreateLengthConstraint( info.pObjects[0], info.pObjects[1], pGroup, length );
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. // Purpose: Limited ballsocket constraint with toggle-able translation constraints
  1386. //-----------------------------------------------------------------------------
  1387. class CRagdollConstraint : public CPhysConstraint
  1388. {
  1389. DECLARE_CLASS( CRagdollConstraint, CPhysConstraint );
  1390. public:
  1391. DECLARE_DATADESC();
  1392. #if 0
  1393. void DrawDebugGeometryOverlays()
  1394. {
  1395. if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
  1396. {
  1397. NDebugOverlay::Line( refPos, attachPos, 0, 255, 0, false, 0 );
  1398. }
  1399. BaseClass::DrawDebugGeometryOverlays();
  1400. }
  1401. #endif
  1402. IPhysicsConstraint *CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info );
  1403. private:
  1404. float m_xmin; // constraint limits in degrees
  1405. float m_xmax;
  1406. float m_ymin;
  1407. float m_ymax;
  1408. float m_zmin;
  1409. float m_zmax;
  1410. float m_xfriction;
  1411. float m_yfriction;
  1412. float m_zfriction;
  1413. };
  1414. BEGIN_DATADESC( CRagdollConstraint )
  1415. DEFINE_KEYFIELD( m_xmin, FIELD_FLOAT, "xmin" ),
  1416. DEFINE_KEYFIELD( m_xmax, FIELD_FLOAT, "xmax" ),
  1417. DEFINE_KEYFIELD( m_ymin, FIELD_FLOAT, "ymin" ),
  1418. DEFINE_KEYFIELD( m_ymax, FIELD_FLOAT, "ymax" ),
  1419. DEFINE_KEYFIELD( m_zmin, FIELD_FLOAT, "zmin" ),
  1420. DEFINE_KEYFIELD( m_zmax, FIELD_FLOAT, "zmax" ),
  1421. DEFINE_KEYFIELD( m_xfriction, FIELD_FLOAT, "xfriction" ),
  1422. DEFINE_KEYFIELD( m_yfriction, FIELD_FLOAT, "yfriction" ),
  1423. DEFINE_KEYFIELD( m_zfriction, FIELD_FLOAT, "zfriction" ),
  1424. END_DATADESC()
  1425. LINK_ENTITY_TO_CLASS( phys_ragdollconstraint, CRagdollConstraint );
  1426. //-----------------------------------------------------------------------------
  1427. // Purpose: Activate/create the constraint
  1428. //-----------------------------------------------------------------------------
  1429. IPhysicsConstraint *CRagdollConstraint::CreateConstraint( IPhysicsConstraintGroup *pGroup, const hl_constraint_info_t &info )
  1430. {
  1431. constraint_ragdollparams_t ragdoll;
  1432. ragdoll.Defaults();
  1433. matrix3x4_t entityToWorld, worldToEntity;
  1434. info.pObjects[0]->GetPositionMatrix( &entityToWorld );
  1435. MatrixInvert( entityToWorld, worldToEntity );
  1436. ConcatTransforms( worldToEntity, EntityToWorldTransform(), ragdoll.constraintToReference );
  1437. info.pObjects[1]->GetPositionMatrix( &entityToWorld );
  1438. MatrixInvert( entityToWorld, worldToEntity );
  1439. ConcatTransforms( worldToEntity, EntityToWorldTransform(), ragdoll.constraintToAttached );
  1440. ragdoll.onlyAngularLimits = HasSpawnFlags( SF_RAGDOLL_FREEMOVEMENT ) ? true : false;
  1441. // FIXME: Why are these friction numbers in different units from what the hinge uses?
  1442. ragdoll.axes[0].SetAxisFriction( m_xmin, m_xmax, m_xfriction );
  1443. ragdoll.axes[1].SetAxisFriction( m_ymin, m_ymax, m_yfriction );
  1444. ragdoll.axes[2].SetAxisFriction( m_zmin, m_zmax, m_zfriction );
  1445. if ( HasSpawnFlags( SF_CONSTRAINT_START_INACTIVE ) )
  1446. {
  1447. ragdoll.isActive = false;
  1448. }
  1449. return physenv->CreateRagdollConstraint( info.pObjects[0], info.pObjects[1], pGroup, ragdoll );
  1450. }
  1451. class CPhysConstraintEvents : public IPhysicsConstraintEvent
  1452. {
  1453. void ConstraintBroken( IPhysicsConstraint *pConstraint )
  1454. {
  1455. CBaseEntity *pEntity = (CBaseEntity *)pConstraint->GetGameData();
  1456. if ( pEntity )
  1457. {
  1458. IPhysicsConstraintEvent *pConstraintEvent = dynamic_cast<IPhysicsConstraintEvent*>( pEntity );
  1459. //Msg("Constraint broken %s\n", pEntity->GetDebugName() );
  1460. if ( pConstraintEvent )
  1461. {
  1462. pConstraintEvent->ConstraintBroken( pConstraint );
  1463. }
  1464. else
  1465. {
  1466. variant_t emptyVariant;
  1467. pEntity->AcceptInput( "ConstraintBroken", NULL, NULL, emptyVariant, 0 );
  1468. }
  1469. }
  1470. }
  1471. };
  1472. static CPhysConstraintEvents constraintevents;
  1473. // registered in physics.cpp
  1474. IPhysicsConstraintEvent *g_pConstraintEvents = &constraintevents;
  1475. #if HINGE_NOTIFY
  1476. //-----------------------------------------------------------------------------
  1477. // Code for sampler
  1478. //-----------------------------------------------------------------------------
  1479. /// Call this in spawn(). (Not a constructor because those are difficult to use in entities.)
  1480. void VelocitySampler::Initialize(float samplerate)
  1481. {
  1482. m_fIdealSampleRate = samplerate;
  1483. }
  1484. // This is an old style approach to reversal sounds, from when there was only one.
  1485. #if 0
  1486. bool VelocitySampler::HasReversed(const Vector &relativeVelocity, float thresholdAcceleration)
  1487. {
  1488. // first, make sure the velocity has reversed (is more than 90deg off) from last time, or is zero now.
  1489. // float rVsq = relativeVelocity.LengthSqr();
  1490. float vDot = relativeVelocity.Dot(m_prevSample);
  1491. if (vDot <= 0) // there is a reversal in direction. compute the magnitude of acceleration.
  1492. {
  1493. // find the scalar projection of the relative acceleration this fame onto the previous frame's
  1494. // velocity, and compare that to the threshold.
  1495. Vector accel = relativeVelocity - m_prevSample;
  1496. float prevSampleLength = m_prevSample.Length();
  1497. float projection = 0;
  1498. // divide through by dt to get the accel per sec
  1499. if (prevSampleLength)
  1500. {
  1501. projection = -(accel.Dot(m_prevSample) / prevSampleLength) / (gpGlobals->curtime - m_fPrevSampleTime);
  1502. }
  1503. else
  1504. {
  1505. projection = accel.Length() / (gpGlobals->curtime - m_fPrevSampleTime);
  1506. }
  1507. if (g_debug_constraint_sounds.GetBool())
  1508. {
  1509. Msg("Reversal accel is %f/%f\n",projection,thresholdAcceleration);
  1510. }
  1511. return ((projection) > thresholdAcceleration); // the scalar projection is negative because the acceleration is against vel
  1512. }
  1513. else
  1514. {
  1515. return false;
  1516. }
  1517. }
  1518. #endif
  1519. /// Looks at the force of reversal and compares it to a ladder of thresholds.
  1520. /// Returns the index of the highest threshold exceeded by the reversal velocity.
  1521. int VelocitySampler::HasReversed(const Vector &relativeVelocity, const float thresholdAcceleration[], const unsigned short numThresholds)
  1522. {
  1523. // first, make sure the velocity has reversed (is more than 90deg off) from last time, or is zero now.
  1524. // float rVsq = relativeVelocity.LengthSqr();
  1525. float vDot = relativeVelocity.Dot(m_prevSample);
  1526. if (vDot <= 0) // there is a reversal in direction. compute the magnitude of acceleration.
  1527. {
  1528. // find the scalar projection of the relative acceleration this fame onto the previous frame's
  1529. // velocity, and compare that to the threshold.
  1530. Vector accel = relativeVelocity - m_prevSample;
  1531. float prevSampleLength = m_prevSample.Length();
  1532. float projection = 0;
  1533. // divide through by dt to get the accel per sec
  1534. if (prevSampleLength)
  1535. {
  1536. // the scalar projection is negative because the acceleration is against vel
  1537. projection = -(accel.Dot(m_prevSample) / prevSampleLength) / (gpGlobals->curtime - m_fPrevSampleTime);
  1538. }
  1539. else
  1540. {
  1541. projection = accel.Length() / (gpGlobals->curtime - m_fPrevSampleTime);
  1542. }
  1543. if (g_debug_constraint_sounds.GetBool())
  1544. {
  1545. Msg("Reversal accel is %f/%f\n", projection, thresholdAcceleration[0]);
  1546. }
  1547. // now find the threshold crossed.
  1548. int retval;
  1549. for (retval = numThresholds - 1; retval >= 0 ; --retval)
  1550. {
  1551. if (projection > thresholdAcceleration[retval])
  1552. break;
  1553. }
  1554. return retval;
  1555. }
  1556. else
  1557. {
  1558. return -1;
  1559. }
  1560. }
  1561. /// small helper function used just below (technique copy-pasted from sound.cpp)
  1562. inline static bool IsEmpty (const string_t &str)
  1563. {
  1564. return (!str || strlen(str.ToCStr()) < 1 );
  1565. }
  1566. void ConstraintSoundInfo::OnActivate( CPhysConstraint *pOuter )
  1567. {
  1568. m_pTravelSound = NULL;
  1569. m_vSampler.Initialize( getThinkRate() );
  1570. ValidateInternals( pOuter );
  1571. // make sure sound filenames are not empty
  1572. m_bPlayTravelSound = !IsEmpty(m_iszTravelSoundFwd) || !IsEmpty(m_iszTravelSoundBack);
  1573. m_bPlayReversalSound = false;
  1574. for (int i = 0; i < SimpleConstraintSoundProfile::kREVERSAL_SOUND_ARRAY_SIZE ; ++i)
  1575. {
  1576. if ( !IsEmpty(m_iszReversalSounds[i]) )
  1577. {
  1578. // if there is at least one filled sound field, we should try
  1579. // to play reversals
  1580. m_bPlayReversalSound = true;
  1581. break;
  1582. }
  1583. }
  1584. /*
  1585. SetThink(&CPhysSlideConstraint::SoundThink);
  1586. SetNextThink(gpGlobals->curtime + m_vSampler.getSampleRate());
  1587. */
  1588. }
  1589. /// Maintain consistency of internal datastructures on start
  1590. void ConstraintSoundInfo::ValidateInternals( CPhysConstraint *pOuter )
  1591. {
  1592. // Make sure the reversal sound thresholds are strictly increasing.
  1593. for (int i = 1 ; i < SimpleConstraintSoundProfile::kREVERSAL_SOUND_ARRAY_SIZE ; ++i)
  1594. {
  1595. // if decreases from small to medium, promote small to medium and warn.
  1596. if (m_soundProfile.m_reversalSoundThresholds[i] < m_soundProfile.m_reversalSoundThresholds[i-1])
  1597. {
  1598. Warning("Constraint reversal sounds for %s are out of order!", pOuter->GetDebugName() );
  1599. m_soundProfile.m_reversalSoundThresholds[i] = m_soundProfile.m_reversalSoundThresholds[i-1];
  1600. m_iszReversalSounds[i] = m_iszReversalSounds[i-1];
  1601. }
  1602. }
  1603. }
  1604. void ConstraintSoundInfo::OnPrecache( CPhysConstraint *pOuter )
  1605. {
  1606. pOuter->PrecacheScriptSound( m_iszTravelSoundFwd.ToCStr() );
  1607. pOuter->PrecacheScriptSound( m_iszTravelSoundBack.ToCStr() );
  1608. for (int i = 0 ; i < SimpleConstraintSoundProfile::kREVERSAL_SOUND_ARRAY_SIZE; ++i )
  1609. {
  1610. pOuter->PrecacheScriptSound( m_iszReversalSounds[i].ToCStr() );
  1611. }
  1612. }
  1613. void ConstraintSoundInfo::OnThink( CPhysConstraint *pOuter, const Vector &relativeVelocity )
  1614. {
  1615. // have we had a hard reversal?
  1616. int playReversal = m_vSampler.HasReversed( relativeVelocity, m_soundProfile.m_reversalSoundThresholds, SimpleConstraintSoundProfile::kREVERSAL_SOUND_ARRAY_SIZE );
  1617. float relativeVelMag = relativeVelocity.Length(); //< magnitude of relative velocity
  1618. CBaseEntity *pChildEntity = static_cast<CBaseEntity *>(pOuter->GetPhysConstraint()->GetAttachedObject()->GetGameData());
  1619. // compute sound level
  1620. float soundVol = this->m_soundProfile.GetVolume(relativeVelMag);
  1621. if (g_debug_constraint_sounds.GetBool())
  1622. {
  1623. char tempstr[512];
  1624. Q_snprintf(tempstr,sizeof(tempstr),"Velocity: %.3f", relativeVelMag );
  1625. pChildEntity->EntityText( 0, tempstr, m_vSampler.getSampleRate() );
  1626. Q_snprintf(tempstr,sizeof(tempstr),"Sound volume: %.3f", soundVol );
  1627. pChildEntity->EntityText( 1, tempstr, m_vSampler.getSampleRate() );
  1628. if (playReversal >= 0)
  1629. {
  1630. Q_snprintf(tempstr,sizeof(tempstr),"Reversal [%d]", playReversal );
  1631. pChildEntity->EntityText(2,tempstr,m_vSampler.getSampleRate());
  1632. }
  1633. }
  1634. // if we loaded a travel sound
  1635. if (m_bPlayTravelSound)
  1636. {
  1637. if (soundVol > 0)
  1638. {
  1639. // if we want to play a sound...
  1640. if ( m_pTravelSound )
  1641. { // if a sound exists, modify it
  1642. CSoundEnvelopeController::GetController().SoundChangeVolume( m_pTravelSound, soundVol, 0.1f );
  1643. }
  1644. else
  1645. { // if a sound does not exist, create it
  1646. bool travellingForward = relativeVelocity.Dot(m_forwardAxis) > 0;
  1647. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1648. CBroadcastRecipientFilter filter;
  1649. m_pTravelSound = controller.SoundCreate( filter, pChildEntity->entindex(),
  1650. (travellingForward ? m_iszTravelSoundFwd : m_iszTravelSoundBack).ToCStr() );
  1651. controller.Play( m_pTravelSound, soundVol, 100 );
  1652. }
  1653. }
  1654. else
  1655. {
  1656. // if we want to not play sound
  1657. if ( m_pTravelSound )
  1658. { // and it exists, kill it
  1659. CSoundEnvelopeController::GetController().SoundDestroy( m_pTravelSound );
  1660. m_pTravelSound = NULL;
  1661. }
  1662. }
  1663. }
  1664. if (m_bPlayReversalSound && (playReversal >= 0))
  1665. {
  1666. pChildEntity->EmitSound(m_iszReversalSounds[playReversal].ToCStr());
  1667. }
  1668. m_vSampler.AddSample( relativeVelocity );
  1669. }
  1670. void ConstraintSoundInfo::StartThinking( CPhysConstraint *pOuter, const Vector &relativeVelocity, const Vector &forwardVector )
  1671. {
  1672. m_forwardAxis = forwardVector;
  1673. m_vSampler.BeginSampling( relativeVelocity );
  1674. /*
  1675. IPhysicsConstraint *pConstraint = pOuter->GetPhysConstraint();
  1676. Assert(pConstraint);
  1677. if (pConstraint)
  1678. {
  1679. IPhysicsObject * pAttached = pConstraint->GetAttachedObject(), *pReference = pConstraint->GetReferenceObject();
  1680. m_vSampler.BeginSampling( VelocitySampler::GetRelativeVelocity(pAttached,pReference) );
  1681. }
  1682. */
  1683. }
  1684. void ConstraintSoundInfo::StopThinking( CPhysConstraint *pOuter )
  1685. {
  1686. DeleteAllSounds();
  1687. }
  1688. ConstraintSoundInfo::~ConstraintSoundInfo()
  1689. {
  1690. DeleteAllSounds();
  1691. }
  1692. // Any sounds envelopes that are active, kill.
  1693. void ConstraintSoundInfo::DeleteAllSounds()
  1694. {
  1695. if ( m_pTravelSound )
  1696. {
  1697. CSoundEnvelopeController::GetController().SoundDestroy( m_pTravelSound );
  1698. m_pTravelSound = NULL;
  1699. }
  1700. }
  1701. #endif