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.

386 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "bone_setup.h"
  9. #include "c_ai_basenpc.h"
  10. #include "engine/ivdebugoverlay.h"
  11. #include "tier0/vprof.h"
  12. #include "soundinfo.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //-----------------------------------------------------------------------------
  16. // Purpose:
  17. //-----------------------------------------------------------------------------
  18. class C_NPC_Hydra : public C_AI_BaseNPC
  19. {
  20. public:
  21. DECLARE_CLASS( C_NPC_Hydra, C_AI_BaseNPC );
  22. DECLARE_CLIENTCLASS();
  23. DECLARE_INTERPOLATION();
  24. C_NPC_Hydra();
  25. virtual ~C_NPC_Hydra();
  26. // model specific
  27. virtual void OnLatchInterpolatedVariables( int flags );
  28. virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime );
  29. virtual void StandardBlendingRules( Vector pos[], Quaternion q[], float currentTime, int boneMask );
  30. void CalcBoneChain( Vector pos[], const Vector chain[] );
  31. void CalcBoneAngles( const Vector pos[], Quaternion q[] );
  32. virtual bool GetSoundSpatialization( SpatializationInfo_t& info );
  33. virtual void ResetLatched();
  34. #define CHAIN_LINKS 32
  35. bool m_bNewChain;
  36. int m_fLatchFlags;
  37. Vector m_vecChain[CHAIN_LINKS];
  38. Vector m_vecHeadDir;
  39. CInterpolatedVar< Vector > m_iv_vecHeadDir;
  40. //Vector m_vecInterpHeadDir;
  41. float m_flRelaxedLength;
  42. Vector *m_vecPos; // current animation
  43. CInterpolatedVar< Vector > *m_iv_vecPos;
  44. int m_numHydraBones;
  45. float *m_boneLength;
  46. float m_maxPossibleLength;
  47. private:
  48. C_NPC_Hydra( const C_NPC_Hydra & ); // not defined, not accessible
  49. };
  50. IMPLEMENT_CLIENTCLASS_DT(C_NPC_Hydra, DT_NPC_Hydra, CNPC_Hydra)
  51. RecvPropVector ( RECVINFO( m_vecChain[0] ) ),
  52. RecvPropVector ( RECVINFO( m_vecChain[1] ) ),
  53. RecvPropVector ( RECVINFO( m_vecChain[2] ) ),
  54. RecvPropVector ( RECVINFO( m_vecChain[3] ) ),
  55. RecvPropVector ( RECVINFO( m_vecChain[4] ) ),
  56. RecvPropVector ( RECVINFO( m_vecChain[5] ) ),
  57. RecvPropVector ( RECVINFO( m_vecChain[6] ) ),
  58. RecvPropVector ( RECVINFO( m_vecChain[7] ) ),
  59. RecvPropVector ( RECVINFO( m_vecChain[8] ) ),
  60. RecvPropVector ( RECVINFO( m_vecChain[9] ) ),
  61. RecvPropVector ( RECVINFO( m_vecChain[10] ) ),
  62. RecvPropVector ( RECVINFO( m_vecChain[11] ) ),
  63. RecvPropVector ( RECVINFO( m_vecChain[12] ) ),
  64. RecvPropVector ( RECVINFO( m_vecChain[13] ) ),
  65. RecvPropVector ( RECVINFO( m_vecChain[14] ) ),
  66. RecvPropVector ( RECVINFO( m_vecChain[15] ) ),
  67. RecvPropVector ( RECVINFO( m_vecChain[16] ) ),
  68. RecvPropVector ( RECVINFO( m_vecChain[17] ) ),
  69. RecvPropVector ( RECVINFO( m_vecChain[18] ) ),
  70. RecvPropVector ( RECVINFO( m_vecChain[19] ) ),
  71. RecvPropVector ( RECVINFO( m_vecChain[20] ) ),
  72. RecvPropVector ( RECVINFO( m_vecChain[21] ) ),
  73. RecvPropVector ( RECVINFO( m_vecChain[22] ) ),
  74. RecvPropVector ( RECVINFO( m_vecChain[23] ) ),
  75. RecvPropVector ( RECVINFO( m_vecChain[24] ) ),
  76. RecvPropVector ( RECVINFO( m_vecChain[25] ) ),
  77. RecvPropVector ( RECVINFO( m_vecChain[26] ) ),
  78. RecvPropVector ( RECVINFO( m_vecChain[27] ) ),
  79. RecvPropVector ( RECVINFO( m_vecChain[28] ) ),
  80. RecvPropVector ( RECVINFO( m_vecChain[29] ) ),
  81. RecvPropVector ( RECVINFO( m_vecChain[30] ) ),
  82. RecvPropVector ( RECVINFO( m_vecChain[31] ) ),
  83. RecvPropVector ( RECVINFO( m_vecHeadDir ) ),
  84. RecvPropFloat ( RECVINFO( m_flRelaxedLength ) ),
  85. END_RECV_TABLE()
  86. C_NPC_Hydra::C_NPC_Hydra() : m_iv_vecHeadDir( "C_NPC_Hydra::m_iv_vecHeadDir" )
  87. {
  88. AddVar( &m_vecHeadDir, &m_iv_vecHeadDir, LATCH_ANIMATION_VAR );
  89. m_numHydraBones = 0;
  90. m_boneLength = NULL;
  91. m_maxPossibleLength = 1;
  92. m_vecPos = NULL;
  93. m_iv_vecPos = NULL;
  94. }
  95. C_NPC_Hydra::~C_NPC_Hydra()
  96. {
  97. delete m_boneLength;
  98. delete m_vecPos;
  99. delete[] m_iv_vecPos;
  100. m_iv_vecPos = NULL;
  101. }
  102. void C_NPC_Hydra::OnLatchInterpolatedVariables( int flags )
  103. {
  104. m_bNewChain = true;
  105. m_fLatchFlags = flags;
  106. BaseClass::OnLatchInterpolatedVariables( flags );
  107. }
  108. void C_NPC_Hydra::ResetLatched()
  109. {
  110. for (int i = 0; i < m_numHydraBones; i++)
  111. {
  112. m_iv_vecPos[i].Reset();
  113. }
  114. BaseClass::ResetLatched();
  115. }
  116. bool C_NPC_Hydra::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
  117. {
  118. return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );
  119. }
  120. void C_NPC_Hydra::StandardBlendingRules( Vector pos[], Quaternion q[], float currentTime, int boneMask )
  121. {
  122. VPROF( "C_NPC_Hydra::StandardBlendingRules" );
  123. studiohdr_t *hdr = GetModelPtr();
  124. if ( !hdr )
  125. {
  126. return;
  127. }
  128. int i;
  129. // check for changing model memory requirements
  130. bool bNewlyInited = false;
  131. if (m_numHydraBones != hdr->numbones)
  132. {
  133. m_numHydraBones = hdr->numbones;
  134. // build root animation
  135. float poseparam[MAXSTUDIOPOSEPARAM];
  136. for (i = 0; i < hdr->GetNumPoseParameters(); i++)
  137. {
  138. poseparam[i] = 0;
  139. }
  140. CalcPose( hdr, NULL, pos, q, 0.0f, 0.0f, poseparam, BONE_USED_BY_ANYTHING );
  141. // allocate arrays
  142. if (m_boneLength)
  143. {
  144. delete[] m_boneLength;
  145. }
  146. m_boneLength = new float [m_numHydraBones];
  147. if (m_vecPos)
  148. {
  149. delete[] m_vecPos;
  150. }
  151. m_vecPos = new Vector [m_numHydraBones];
  152. if (m_iv_vecPos)
  153. {
  154. delete m_iv_vecPos;
  155. }
  156. m_iv_vecPos = new CInterpolatedVar< Vector >[m_numHydraBones];
  157. for ( i = 0; i < m_numHydraBones; i++ )
  158. {
  159. m_iv_vecPos[ i ].Setup( &m_vecPos[ i ], LATCH_SIMULATION_VAR | EXCLUDE_AUTO_LATCH | EXCLUDE_AUTO_INTERPOLATE );
  160. }
  161. // calc models bone lengths
  162. m_maxPossibleLength = 0;
  163. for (i = 0; i < m_numHydraBones-1; i++)
  164. {
  165. m_boneLength[i] = (pos[i+1] - pos[i]).Length();
  166. m_maxPossibleLength += m_boneLength[i];
  167. }
  168. m_boneLength[i] = 0.0f;
  169. bNewlyInited = true;
  170. }
  171. // calc new bone setup if networked.
  172. if (m_bNewChain)
  173. {
  174. CalcBoneChain( m_vecPos, m_vecChain );
  175. for (i = 0; i < m_numHydraBones; i++)
  176. {
  177. // debugoverlay->AddLineOverlay( m_vecPos[i], m_vecPos[i<m_numHydraBones-1?i+1:m_numHydraBones-1], 0, 255, 0, false, 0.1 );
  178. m_vecPos[i] = m_vecPos[i] - GetAbsOrigin();
  179. if ( m_fLatchFlags & LATCH_SIMULATION_VAR )
  180. {
  181. m_iv_vecPos[i].NoteChanged( currentTime, true );
  182. }
  183. }
  184. m_bNewChain = false;
  185. }
  186. // if just allocated, initialize bones
  187. if (bNewlyInited)
  188. {
  189. for (i = 0; i < m_numHydraBones; i++)
  190. {
  191. m_iv_vecPos[i].Reset();
  192. }
  193. }
  194. for (i = 0; i < m_numHydraBones; i++)
  195. {
  196. m_iv_vecPos[i].Interpolate( currentTime );
  197. pos[ i ] = m_vecPos[ i ];
  198. }
  199. // calculate bone angles
  200. CalcBoneAngles( pos, q );
  201. // rotate the last bone of the hydra 90 degrees since it's oriented differently than the others
  202. Quaternion qTmp;
  203. AngleQuaternion( QAngle( 0, -90, 0) , qTmp );
  204. QuaternionMult( q[m_numHydraBones - 1], qTmp, q[m_numHydraBones - 1] );
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Fits skeleton of hydra to the variable segment length "chain" array
  208. // Adjusts overall hydra so that "m_flRelaxedLength" of texture fits over
  209. // the actual length of the chain
  210. //-----------------------------------------------------------------------------
  211. void C_NPC_Hydra::CalcBoneChain( Vector pos[], const Vector chain[] )
  212. {
  213. int i, j;
  214. // Find the dist chain link that's not zero length
  215. i = CHAIN_LINKS-1;
  216. while (i > 0)
  217. {
  218. if ((chain[i] - chain[i-1]).LengthSqr() > 0.0)
  219. {
  220. break;
  221. }
  222. i--;
  223. }
  224. // initialize the last bone to the last bone
  225. j = m_numHydraBones - 1;
  226. // clamp length
  227. float totalLength = 0;
  228. for (int k = i; k > 0; k--)
  229. {
  230. // debugoverlay->AddLineOverlay( chain[k], chain[k-1], 255, 255, 255, false, 0 );
  231. totalLength += (chain[k] - chain[k-1]).Length();
  232. }
  233. totalLength = clamp( totalLength, 1.0, m_maxPossibleLength );
  234. float scale = m_flRelaxedLength / totalLength;
  235. // starting from the head, fit the hydra skeleton onto the chain spline
  236. float dist = -16;
  237. while (j >= 0 && i > 0)
  238. {
  239. // debugoverlay->AddLineOverlay( chain[i], chain[i-1], 255, 255, 255, false, 0 );
  240. float dt = (chain[i] - chain[i-1]).Length() * scale;
  241. float dx = dt;
  242. while (j >= 0 && dist + dt >= m_boneLength[j])
  243. {
  244. float s = (dx - (dt - (m_boneLength[j] - dist))) / dx;
  245. if (s < 0 || s > 1.)
  246. s = 0;
  247. // pos[j] = chain[i] * (1 - s) + chain[i-1] * s;
  248. Catmull_Rom_Spline( chain[(i<CHAIN_LINKS-1)?i+1:CHAIN_LINKS-1], chain[i], chain[(i>0)?i-1:0], chain[(i>1)?i-2:0], s, pos[j] );
  249. // debugoverlay->AddLineOverlay( pos[j], chain[i], 0, 255, 0, false, 0 );
  250. // debugoverlay->AddLineOverlay( pos[j], chain[i-1], 0, 255, 0, false, 0 );
  251. dt = dt - (m_boneLength[j] - dist);
  252. j--;
  253. dist = 0;
  254. }
  255. dist += dt;
  256. i--;
  257. }
  258. while (j >= 0)
  259. {
  260. pos[j] = chain[0];
  261. j--;
  262. }
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Minimize the amount of twist between bone segments
  266. //-----------------------------------------------------------------------------
  267. void C_NPC_Hydra::CalcBoneAngles( const Vector pos[], Quaternion q[] )
  268. {
  269. int i;
  270. matrix3x4_t bonematrix;
  271. for (i = m_numHydraBones - 1; i >= 0; i--)
  272. {
  273. Vector forward;
  274. Vector left2;
  275. if (i != m_numHydraBones - 1)
  276. {
  277. QuaternionMatrix( q[i+1], bonematrix );
  278. MatrixGetColumn( bonematrix, 1, left2 );
  279. forward = (pos[i+1] - pos[i]) /* + (pos[i] - pos[i-1])*/;
  280. float length = VectorNormalize( forward );
  281. if (length == 0.0)
  282. {
  283. q[i] = q[i+1];
  284. continue;
  285. }
  286. }
  287. else
  288. {
  289. forward = m_vecHeadDir;
  290. VectorNormalize( forward );
  291. VectorMatrix( forward, bonematrix );
  292. MatrixGetColumn( bonematrix, 1, left2 );
  293. }
  294. Vector up = CrossProduct( forward, left2 );
  295. VectorNormalize( up );
  296. Vector left = CrossProduct( up, forward );
  297. MatrixSetColumn( forward, 0, bonematrix );
  298. MatrixSetColumn( left, 1, bonematrix );
  299. MatrixSetColumn( up, 2, bonematrix );
  300. // MatrixQuaternion( bonematrix, q[i] );
  301. QAngle angles;
  302. MatrixAngles( bonematrix, angles );
  303. AngleQuaternion( angles, q[i] );
  304. }
  305. }
  306. bool C_NPC_Hydra::GetSoundSpatialization( SpatializationInfo_t& info )
  307. {
  308. bool bret = BaseClass::GetSoundSpatialization( info );
  309. // Default things it's audible, put it at a better spot?
  310. if ( bret )
  311. {
  312. // TODO: Note, this is where you could override the sound position and orientation and use
  313. // an attachment points position as the sound source
  314. // You might have to issue C_BaseAnimating::AllowBoneAccess( true, false ); to allow
  315. // bone setup during sound spatialization if you run into asserts...
  316. }
  317. return bret;
  318. }