Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_ai_basenpc.h"
  9. #include "engine/ivmodelinfo.h"
  10. #include "rope_physics.h"
  11. #include "materialsystem/imaterialsystem.h"
  12. #include "fx_line.h"
  13. #include "engine/ivdebugoverlay.h"
  14. #include "bone_setup.h"
  15. #include "model_types.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #define BARNACLE_TONGUE_POINTS 7
  19. //-----------------------------------------------------------------------------
  20. // Purpose:
  21. //-----------------------------------------------------------------------------
  22. class C_NPC_Barnacle : public C_AI_BaseNPC
  23. {
  24. public:
  25. DECLARE_CLASS( C_NPC_Barnacle, C_AI_BaseNPC );
  26. DECLARE_CLIENTCLASS();
  27. C_NPC_Barnacle( void );
  28. virtual void GetRenderBounds( Vector &theMins, Vector &theMaxs )
  29. {
  30. BaseClass::GetRenderBounds( theMins, theMaxs );
  31. // Extend our bounding box downwards the length of the tongue
  32. theMins -= Vector( 0, 0, m_flAltitude );
  33. }
  34. // Purpose: Initialize absmin & absmax to the appropriate box
  35. virtual void ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  36. {
  37. // Extend our bounding box downwards the length of the tongue
  38. CollisionProp()->WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs );
  39. // We really care about the tongue tip. The altitude is not really relevant.
  40. VectorMin( *pVecWorldMins, m_vecTip, *pVecWorldMins );
  41. VectorMax( *pVecWorldMaxs, m_vecTip, *pVecWorldMaxs );
  42. // pVecWorldMins->z -= m_flAltitude;
  43. }
  44. void OnDataChanged( DataUpdateType_t updateType );
  45. void InitTonguePhysics( void );
  46. void ClientThink( void );
  47. void StandardBlendingRules( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], float currentTime, int boneMask );
  48. void SetVecTip( const float *pPosition );
  49. void SetAltitude( float flAltitude );
  50. // Purpose:
  51. void ComputeVisualTipPoint( Vector *pTip );
  52. protected:
  53. Vector m_vecTipPrevious;
  54. Vector m_vecRoot;
  55. Vector m_vecTip;
  56. Vector m_vecTipDrawOffset;
  57. private:
  58. // Tongue points
  59. float m_flAltitude;
  60. Vector m_vecTonguePoints[BARNACLE_TONGUE_POINTS];
  61. CRopePhysics<BARNACLE_TONGUE_POINTS> m_TonguePhysics;
  62. // Tongue physics delegate
  63. class CBarnaclePhysicsDelegate : public CSimplePhysics::IHelper
  64. {
  65. public:
  66. virtual void GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel );
  67. virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes );
  68. C_NPC_Barnacle *m_pBarnacle;
  69. };
  70. friend class CBarnaclePhysicsDelegate;
  71. CBarnaclePhysicsDelegate m_PhysicsDelegate;
  72. private:
  73. C_NPC_Barnacle( const C_NPC_Barnacle & ); // not defined, not accessible
  74. };
  75. static void RecvProxy_VecTip( const CRecvProxyData *pData, void *pStruct, void *pOut )
  76. {
  77. ((C_NPC_Barnacle*)pStruct)->SetVecTip( pData->m_Value.m_Vector );
  78. }
  79. IMPLEMENT_CLIENTCLASS_DT( C_NPC_Barnacle, DT_Barnacle, CNPC_Barnacle )
  80. RecvPropFloat( RECVINFO( m_flAltitude ) ),
  81. RecvPropVector( RECVINFO( m_vecRoot ) ),
  82. RecvPropVector( RECVINFO( m_vecTip ), 0, RecvProxy_VecTip ),
  83. RecvPropVector( RECVINFO( m_vecTipDrawOffset ) ),
  84. END_RECV_TABLE()
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. C_NPC_Barnacle::C_NPC_Barnacle( void )
  89. {
  90. m_PhysicsDelegate.m_pBarnacle = this;
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose:
  94. //-----------------------------------------------------------------------------
  95. void C_NPC_Barnacle::OnDataChanged( DataUpdateType_t updateType )
  96. {
  97. BaseClass::OnDataChanged( updateType );
  98. if ( updateType == DATA_UPDATE_CREATED )
  99. {
  100. InitTonguePhysics();
  101. // We want to think every frame.
  102. SetNextClientThink( CLIENT_THINK_ALWAYS );
  103. return;
  104. }
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Sets the tongue altitude
  108. //-----------------------------------------------------------------------------
  109. void C_NPC_Barnacle::SetAltitude( float flAltitude )
  110. {
  111. m_flAltitude = flAltitude;
  112. }
  113. void C_NPC_Barnacle::SetVecTip( const float *pPosition )
  114. {
  115. Vector vecNewTip;
  116. vecNewTip.Init( pPosition[0], pPosition[1], pPosition[2] );
  117. if ( vecNewTip != m_vecTip )
  118. {
  119. m_vecTip = vecNewTip;
  120. CollisionProp()->MarkSurroundingBoundsDirty();
  121. }
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose:
  125. //-----------------------------------------------------------------------------
  126. void C_NPC_Barnacle::InitTonguePhysics( void )
  127. {
  128. // Init tongue spline
  129. // First point is at the top
  130. m_TonguePhysics.SetupSimulation( m_flAltitude / (BARNACLE_TONGUE_POINTS-1), &m_PhysicsDelegate );
  131. m_TonguePhysics.Restart();
  132. // Initialize the positions of the nodes.
  133. m_TonguePhysics.GetFirstNode()->m_vPos = m_vecRoot;
  134. m_TonguePhysics.GetFirstNode()->m_vPrevPos = m_TonguePhysics.GetFirstNode()->m_vPos;
  135. float flAltitude = m_flAltitude;
  136. for( int i = 1; i < m_TonguePhysics.NumNodes(); i++ )
  137. {
  138. flAltitude *= 0.5;
  139. CSimplePhysics::CNode *pNode = m_TonguePhysics.GetNode( i );
  140. pNode->m_vPos = m_TonguePhysics.GetNode(i-1)->m_vPos - Vector(0,0,flAltitude);
  141. pNode->m_vPrevPos = pNode->m_vPos;
  142. // Set the length of the node's spring
  143. //m_TonguePhysics.ResetNodeSpringLength( i-1, flAltitude );
  144. }
  145. m_vecTipPrevious = m_vecTip;
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose:
  149. //-----------------------------------------------------------------------------
  150. void C_NPC_Barnacle::ClientThink( void )
  151. {
  152. m_TonguePhysics.Simulate( gpGlobals->frametime );
  153. // Set the spring's length to that of the tongue's extension
  154. m_TonguePhysics.ResetSpringLength( m_flAltitude / (BARNACLE_TONGUE_POINTS-1) );
  155. // Necessary because ComputeVisualTipPoint depends on m_vecTipPrevious
  156. Vector vecTemp;
  157. ComputeVisualTipPoint( &vecTemp );
  158. m_vecTipPrevious = vecTemp;
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose:
  162. //-----------------------------------------------------------------------------
  163. void C_NPC_Barnacle::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  164. {
  165. BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
  166. if ( !hdr )
  167. return;
  168. int firstBone = Studio_BoneIndexByName( hdr, "Barnacle.tongue1" );
  169. Vector vecPrevRight;
  170. GetVectors( NULL, &vecPrevRight, NULL );
  171. Vector vecPrev = pos[Studio_BoneIndexByName( hdr, "Barnacle.base" )];
  172. Vector vecCurr = vec3_origin;
  173. Vector vecForward;
  174. for ( int i = 0; i <= BARNACLE_TONGUE_POINTS; i++ )
  175. {
  176. // We double up the bones at the last node.
  177. if ( i == BARNACLE_TONGUE_POINTS )
  178. {
  179. vecCurr = m_TonguePhysics.GetLastNode()->m_vPos;
  180. }
  181. else
  182. {
  183. vecCurr = m_TonguePhysics.GetNode(i)->m_vPos;
  184. }
  185. //debugoverlay->AddBoxOverlay( vecCurr, -Vector(2,2,2), Vector(2,2,2), vec3_angle, 0,255,0, 128, 0.1 );
  186. // Fill out the positions in local space
  187. VectorITransform( vecCurr, EntityToWorldTransform(), pos[firstBone+i] );
  188. vecCurr = pos[firstBone+i];
  189. // Disallow twist in the tongue visually
  190. // Forward vector has to follow the tongue, right + up have to minimize twist from
  191. // the previous bone
  192. // Fill out the angles
  193. if ( i != BARNACLE_TONGUE_POINTS )
  194. {
  195. vecForward = (vecCurr - vecPrev);
  196. if ( VectorNormalize( vecForward ) < 1e-3 )
  197. {
  198. vecForward.Init( 0, 0, 1 );
  199. }
  200. }
  201. // Project the previous vecRight into a plane perpendicular to vecForward
  202. // that's the vector closest to what we want...
  203. Vector vecRight, vecUp;
  204. VectorMA( vecPrevRight, -DotProduct( vecPrevRight, vecForward ), vecForward, vecRight );
  205. VectorNormalize( vecRight );
  206. CrossProduct( vecForward, vecRight, vecUp );
  207. BasisToQuaternion( vecForward, vecRight, vecUp, q[firstBone+i] );
  208. vecPrev = vecCurr;
  209. vecPrevRight = vecRight;
  210. }
  211. }
  212. //===============================================================================================================================
  213. // BARNACLE TONGUE PHYSICS
  214. //===============================================================================================================================
  215. #define TONGUE_GRAVITY 0, 0, -1000
  216. //-----------------------------------------------------------------------------
  217. // Purpose:
  218. //-----------------------------------------------------------------------------
  219. void C_NPC_Barnacle::CBarnaclePhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel )
  220. {
  221. // Gravity.
  222. pAccel->Init( TONGUE_GRAVITY );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. //-----------------------------------------------------------------------------
  227. #define TIP_SNAP_FACTOR 200
  228. // Todo: this really ought to be SIMD.
  229. void C_NPC_Barnacle::ComputeVisualTipPoint( Vector *pTip )
  230. {
  231. float flTipMove = TIP_SNAP_FACTOR * gpGlobals->frametime;
  232. Vector tipIdeal;
  233. VectorAdd(m_vecTip, m_vecTipDrawOffset, tipIdeal);
  234. if ( tipIdeal.DistToSqr( m_vecTipPrevious ) > (flTipMove * flTipMove) )
  235. {
  236. // Inch the visual tip toward the actual tip
  237. VectorSubtract( tipIdeal, m_vecTipPrevious, *pTip );
  238. VectorNormalize( *pTip );
  239. *pTip *= flTipMove;
  240. *pTip += m_vecTipPrevious;
  241. }
  242. else
  243. {
  244. *pTip = tipIdeal;
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. //-----------------------------------------------------------------------------
  250. void C_NPC_Barnacle::CBarnaclePhysicsDelegate::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes )
  251. {
  252. // Startpoint always stays at the root
  253. pNodes[0].m_vPos = m_pBarnacle->m_vecRoot;
  254. // Endpoint always stays at the tip
  255. m_pBarnacle->ComputeVisualTipPoint( &pNodes[nNodes-1].m_vPos );
  256. }