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.

710 lines
21 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "replay_ragdoll.h"
  9. #include "tier1/mempool.h"
  10. #include "debugoverlay_shared.h"
  11. #include "FileSystem.h"
  12. //--------------------------------------------------------------------------------
  13. // TODO: mempool
  14. // A reasonable mem pool might be 8 MB. 28 bytes (1 vec + 1 quat) * MAXSTUDIOBONES * 20 fps * 5 seconds
  15. // (conservative estimate of average ragdoll life) * 20 ragdolls (estimate of how many ragdolls you might see
  16. // in a 60 second period)
  17. //CMemoryPool g_mempool;
  18. static matrix3x4_t gs_BoneCache[ MAXSTUDIOBONES ];
  19. //--------------------------------------------------------------------------------
  20. void DrawBones( matrix3x4_t const* pBones, int nNumBones, ragdoll_t const* pRagdoll,
  21. int nRed, int nGreen, int nBlue, C_BaseAnimating* pBaseAnimating )
  22. {
  23. Assert( pBones );
  24. Assert( pRagdoll );
  25. Assert( pBaseAnimating );
  26. Vector from, to;
  27. for ( int i = 0; i < nNumBones; ++i )
  28. {
  29. debugoverlay->AddCoordFrameOverlay( pBones[ i ], 3.0f );
  30. int const iRagdollParentIndex = pRagdoll->list[ i ].parentIndex;
  31. if ( iRagdollParentIndex < 0 )
  32. continue;
  33. int iBoneIndex = pRagdoll->boneIndex[ i ];
  34. int iParentIndex = pRagdoll->boneIndex[ iRagdollParentIndex ];
  35. MatrixPosition( pBones[ iParentIndex ], from );
  36. MatrixPosition( pBones[ iBoneIndex ], to );
  37. debugoverlay->AddLineOverlay( from, to, nRed, nGreen, nBlue, true, 0.0f );
  38. }
  39. }
  40. //--------------------------------------------------------------------------------
  41. inline int GetServerTickCount()
  42. {
  43. int nTick = TIME_TO_TICKS( engine->GetLastTimeStamp() );
  44. return nTick;
  45. }
  46. //--------------------------------------------------------------------------------
  47. /*static*/ RagdollSimulationFrame_t* RagdollSimulationFrame_t::Alloc( int nNumBones )
  48. {
  49. // TODO: use allocator
  50. RagdollSimulationFrame_t* pNew = new RagdollSimulationFrame_t();
  51. pNew->pPositions = new Vector[ nNumBones ];
  52. pNew->pAngles = new QAngle[ nNumBones ];
  53. return pNew;
  54. }
  55. //--------------------------------------------------------------------------------
  56. RagdollSimulationData_t::RagdollSimulationData_t( C_BaseAnimating* pEntity, int nStartTick, int nNumBones )
  57. : m_pEntity( pEntity ),
  58. m_nEntityIndex( -1 ),
  59. m_nStartTick( nStartTick ),
  60. m_nNumBones( nNumBones ),
  61. m_nDuration( -1 )
  62. {
  63. if ( pEntity )
  64. {
  65. m_nEntityIndex = pEntity->entindex();
  66. }
  67. Assert( nNumBones >= 0 && nNumBones < MAXSTUDIOBONES );
  68. }
  69. bool _ComputeRagdollBones( const ragdoll_t *pRagdoll, matrix3x4_t &parentTransform, matrix3x4_t *pBones, Vector *pPositions, QAngle *pAngles )
  70. {
  71. matrix3x4_t inverted, output;
  72. #ifdef _DEBUG
  73. CBitVec<MAXSTUDIOBONES> vBonesComputed;
  74. vBonesComputed.ClearAll();
  75. #endif
  76. for ( int i = 0; i < pRagdoll->listCount; ++i )
  77. {
  78. const ragdollelement_t& element = pRagdoll->list[ i ];
  79. // during restore if a model has changed since the file was saved, this could be NULL
  80. if ( !element.pObject )
  81. return false;
  82. int const boneIndex = pRagdoll->boneIndex[ i ];
  83. if ( boneIndex < 0 )
  84. {
  85. AssertMsg( 0, "Replay: No mapping for ragdoll bone\n" );
  86. return false;
  87. }
  88. // Get global transform and put it into the bone cache
  89. element.pObject->GetPositionMatrix( &pBones[ boneIndex ] );
  90. // Ensure a fixed translation from the parent (no stretching)
  91. if ( element.parentIndex >= 0 && !pRagdoll->allowStretch )
  92. {
  93. int parentIndex = pRagdoll->boneIndex[ element.parentIndex ];
  94. #ifdef _DEBUG
  95. // Make sure we computed the parent already
  96. Assert( vBonesComputed.IsBitSet(parentIndex) );
  97. #endif
  98. // overwrite the position from physics to force rigid attachment
  99. // NOTE: On the client we actually override this with the proper parent bone in each LOD
  100. Vector out;
  101. VectorTransform( element.originParentSpace, pBones[ parentIndex ], out );
  102. MatrixSetColumn( out, 3, pBones[ boneIndex ] );
  103. MatrixInvert( pBones[ parentIndex ], inverted );
  104. }
  105. else if ( element.parentIndex == - 1 )
  106. {
  107. // Decompose into parent space
  108. MatrixInvert( parentTransform, inverted );
  109. }
  110. #ifdef _DEBUG
  111. vBonesComputed.Set( boneIndex, true );
  112. #endif
  113. // Compute local transform and put into 'output'
  114. ConcatTransforms( inverted, pBones[ boneIndex ], output );
  115. // Cache as Euler/position
  116. MatrixAngles( output, pAngles[ i ], pPositions[ i ] );
  117. }
  118. return true;
  119. }
  120. void RagdollSimulationData_t::Record()
  121. {
  122. Assert( m_pEntity->m_pRagdoll );
  123. // Allocate a frame
  124. RagdollSimulationFrame_t* pNewFrame = RagdollSimulationFrame_t::Alloc( m_nNumBones );
  125. if ( !pNewFrame )
  126. return;
  127. // Set the current tick
  128. pNewFrame->nTick = GetServerTickCount();
  129. // Add new frame to list of frames
  130. m_lstFrames.AddToTail( pNewFrame );
  131. // Compute parent transform
  132. matrix3x4_t parentTransform;
  133. Vector vRootPosition = m_pEntity->GetRenderOrigin();
  134. QAngle angRootAngles = m_pEntity->GetRenderAngles();
  135. AngleMatrix( angRootAngles, vRootPosition, parentTransform );
  136. debugoverlay->AddCoordFrameOverlay( parentTransform, 100 );
  137. // Cache off root position/orientation
  138. pNewFrame->vRootPosition = vRootPosition;
  139. pNewFrame->angRootAngles = angRootAngles;
  140. // Compute actual ragdoll bones
  141. matrix3x4_t* pBones = gs_BoneCache;
  142. _ComputeRagdollBones( m_pEntity->m_pRagdoll->GetRagdoll(), parentTransform, pBones, pNewFrame->pPositions, pNewFrame->pAngles );
  143. // Draw bones
  144. DrawBones( pBones, m_pEntity->m_pRagdoll->RagdollBoneCount(), m_pEntity->m_pRagdoll->GetRagdoll(), 255, 0, 0, m_pEntity );
  145. }
  146. //--------------------------------------------------------------------------------
  147. CReplayRagdollRecorder::CReplayRagdollRecorder()
  148. : m_bIsRecording(false)
  149. {}
  150. CReplayRagdollRecorder::~CReplayRagdollRecorder()
  151. {
  152. }
  153. /*static*/ CReplayRagdollRecorder& CReplayRagdollRecorder::Instance()
  154. {
  155. static CReplayRagdollRecorder s_instance;
  156. return s_instance;
  157. }
  158. void CReplayRagdollRecorder::Init()
  159. {
  160. Assert( !m_bIsRecording );
  161. m_bIsRecording = true;
  162. }
  163. void CReplayRagdollRecorder::Shutdown()
  164. {
  165. if ( !m_bIsRecording )
  166. return;
  167. m_lstRagdolls.PurgeAndDeleteElements();
  168. // RemoveAll() purges, and there is no UnlinkAll() - is there an easier way to do this?
  169. Iterator_t i = m_lstRagdollsToRecord.Head();
  170. while ( i != m_lstRagdollsToRecord.InvalidIndex() )
  171. {
  172. m_lstRagdollsToRecord.Unlink( i );
  173. i = m_lstRagdollsToRecord.Head();
  174. }
  175. Assert( m_bIsRecording );
  176. m_bIsRecording = false;
  177. }
  178. void CReplayRagdollRecorder::RemoveExpiredRagdollEntries()
  179. {
  180. engine->Con_NPrintf( 8, "time: %d", GetServerTickCount() );
  181. FOR_EACH_LL( m_lstRagdolls, i )
  182. {
  183. engine->Con_NPrintf( 10 + i, "entity %d: start time=%d duration=%d num bones=%d", m_lstRagdolls[i]->m_nEntityIndex, m_lstRagdolls[i]->m_nStartTick, m_lstRagdolls[i]->m_nDuration, m_lstRagdolls[i]->m_nNumBones );
  184. }
  185. ConVar* pReplayMovieLength = (ConVar*)cvar->FindVar( "replay_movielength" );
  186. if ( !pReplayMovieLength || m_lstRagdolls.Count() == 0 )
  187. return;
  188. Iterator_t nCurIndex = m_lstRagdolls.Head();
  189. while ( nCurIndex != m_lstRagdolls.InvalidIndex() &&
  190. m_lstRagdolls[nCurIndex]->m_nDuration > 0 &&
  191. m_lstRagdolls[nCurIndex]->m_nStartTick + m_lstRagdolls[nCurIndex]->m_nDuration < GetServerTickCount() - TIME_TO_TICKS( pReplayMovieLength->GetFloat() ) )
  192. {
  193. m_lstRagdolls.Remove( nCurIndex );
  194. nCurIndex = m_lstRagdolls.Head();
  195. DevMsg( "%d: Releasing ragdoll.\n", GetServerTickCount() );
  196. }
  197. }
  198. void CReplayRagdollRecorder::AddEntry( C_BaseAnimating* pEntity, int nStartTick, int nNumBones )
  199. {
  200. DevMsg( "Replay: Processing Ragdoll at time %d\n", nStartTick );
  201. Assert( pEntity );
  202. RagdollSimulationData_t* pNewEntry = new RagdollSimulationData_t( pEntity, nStartTick, nNumBones );
  203. m_lstRagdolls.AddToTail( pNewEntry );
  204. // Also add to list of ragdolls to record
  205. m_lstRagdollsToRecord.AddToTail( pNewEntry );
  206. }
  207. void CReplayRagdollRecorder::StopRecordingRagdoll( C_BaseAnimating* pEntity )
  208. {
  209. Assert( pEntity );
  210. // Find the entry in the recording list
  211. Iterator_t nIndex;
  212. if ( !FindEntryInRecordingList( pEntity, nIndex ) )
  213. return;
  214. StopRecordingRagdollAtIndex( nIndex );
  215. }
  216. void CReplayRagdollRecorder::StopRecordingRagdollAtIndex( Iterator_t nIndex )
  217. {
  218. // No longer recording - compute duration
  219. RagdollSimulationData_t* pData = m_lstRagdollsToRecord[ nIndex ];
  220. // Does duration need to be set?
  221. if ( pData->m_nDuration < 0 )
  222. {
  223. pData->m_nDuration = GetServerTickCount() - pData->m_nStartTick; Assert( pData->m_nDuration > 0 );
  224. }
  225. // Remove it from the recording list
  226. m_lstRagdollsToRecord.Unlink( nIndex );
  227. }
  228. void CReplayRagdollRecorder::StopRecordingSleepingRagdolls()
  229. {
  230. Iterator_t i = m_lstRagdollsToRecord.Head();
  231. while ( i != m_lstRagdollsToRecord.InvalidIndex() )
  232. {
  233. if ( RagdollIsAsleep( *m_lstRagdollsToRecord[ i ]->m_pEntity->m_pRagdoll->GetRagdoll() ) )
  234. {
  235. DevMsg( "entity %d: Removing sleeping ragdoll\n", m_lstRagdollsToRecord[ i ]->m_nEntityIndex );
  236. StopRecordingRagdollAtIndex( i );
  237. i = m_lstRagdollsToRecord.Head();
  238. }
  239. else
  240. {
  241. i = m_lstRagdollsToRecord.Next( i );
  242. }
  243. }
  244. }
  245. bool CReplayRagdollRecorder::FindEntryInRecordingList( C_BaseAnimating* pEntity,
  246. CReplayRagdollRecorder::Iterator_t& nOutIndex )
  247. {
  248. // Find the entry
  249. FOR_EACH_LL( m_lstRagdollsToRecord, i )
  250. {
  251. if ( m_lstRagdollsToRecord[ i ]->m_pEntity == pEntity )
  252. {
  253. nOutIndex = i;
  254. return true;
  255. }
  256. }
  257. nOutIndex = m_lstRagdollsToRecord.InvalidIndex();
  258. return false;
  259. }
  260. void CReplayRagdollRecorder::Record()
  261. {
  262. ConVar* pReplayEnable = (ConVar*)cvar->FindVar( "replay_enable" );
  263. if ( !pReplayEnable || !pReplayEnable->GetInt() )
  264. return;
  265. FOR_EACH_LL( m_lstRagdollsToRecord, i )
  266. {
  267. Assert( m_lstRagdollsToRecord[ i ]->m_pEntity->IsRagdoll() );
  268. m_lstRagdollsToRecord[ i ]->Record();
  269. }
  270. }
  271. void CReplayRagdollRecorder::Think()
  272. {
  273. if ( !IsRecording() )
  274. return;
  275. StopRecordingSleepingRagdolls();
  276. RemoveExpiredRagdollEntries();
  277. Record();
  278. }
  279. void CReplayRagdollRecorder::CleanupStartupTicksAndDurations( int nStartTick )
  280. {
  281. FOR_EACH_LL( m_lstRagdolls, i )
  282. {
  283. RagdollSimulationData_t* pRagdollData = m_lstRagdolls[ i ];
  284. // Offset start tick with start tick, sent over from server
  285. pRagdollData->m_nStartTick -= nStartTick; Assert( pRagdollData->m_nStartTick >= 0 );
  286. // Setup duration
  287. pRagdollData->m_nDuration = GetServerTickCount() - nStartTick; Assert( pRagdollData->m_nDuration > 0 );
  288. // Go through all frames and subtract the start tick
  289. FOR_EACH_LL( pRagdollData->m_lstFrames, j )
  290. {
  291. pRagdollData->m_lstFrames[ j ]->nTick -= nStartTick;
  292. }
  293. }
  294. }
  295. BEGIN_DMXELEMENT_UNPACK( RagdollSimulationData_t )
  296. DMXELEMENT_UNPACK_FIELD( "nEntityIndex", "0", int, m_nEntityIndex )
  297. DMXELEMENT_UNPACK_FIELD( "nStartTick", "0", int, m_nStartTick )
  298. DMXELEMENT_UNPACK_FIELD( "nDuration", "0", int, m_nDuration )
  299. DMXELEMENT_UNPACK_FIELD( "nNumBones", "0", int, m_nNumBones )
  300. END_DMXELEMENT_UNPACK( RagdollSimulationData_t, s_RagdollSimulationDataUnpack )
  301. bool CReplayRagdollRecorder::DumpRagdollsToDisk( char const* pszFilename ) const
  302. {
  303. MEM_ALLOC_CREDIT();
  304. DECLARE_DMX_CONTEXT();
  305. CDmxElement* pSimulations = CreateDmxElement( "Simulations" );
  306. CDmxElementModifyScope modify( pSimulations );
  307. int const nNumRagdolls = m_lstRagdolls.Count();
  308. pSimulations->SetValue( "iNumRagdolls", nNumRagdolls );
  309. CDmxAttribute* pRagdolls = pSimulations->AddAttribute( "ragdolls" );
  310. CUtlVector< CDmxElement* >& ragdolls = pRagdolls->GetArrayForEdit< CDmxElement* >();
  311. modify.Release();
  312. char name[32];
  313. FOR_EACH_LL( m_lstRagdolls, i )
  314. {
  315. RagdollSimulationData_t const* pData = m_lstRagdolls[ i ];
  316. // Make sure we've setup all durations properly
  317. Assert( pData->m_nDuration >= 0 );
  318. CDmxElement* pRagdoll = CreateDmxElement( "ragdoll" );
  319. ragdolls.AddToTail( pRagdoll );
  320. V_snprintf( name, sizeof(name), "ragdoll %d", i );
  321. pRagdoll->SetValue( "name", name );
  322. CDmxElementModifyScope modifyClass( pRagdoll );
  323. pRagdoll->AddAttributesFromStructure( pData, s_RagdollSimulationDataUnpack );
  324. CDmxAttribute* pFrames = pRagdoll->AddAttribute( "frames" );
  325. CUtlVector< CDmxElement* >& frames = pFrames->GetArrayForEdit< CDmxElement* >();
  326. FOR_EACH_LL( pData->m_lstFrames, j )
  327. {
  328. CDmxElement* pFrame = CreateDmxElement( "frame" );
  329. frames.AddToTail( pFrame );
  330. V_snprintf( name, sizeof(name), "frame %d", j );
  331. pFrame->SetValue( "name", name );
  332. // Store tick
  333. pFrame->SetValue( "tick", pData->m_lstFrames[ j ]->nTick );
  334. // Store root pos/orientation
  335. pFrame->SetValue( "root_pos" , pData->m_lstFrames[ j ]->vRootPosition );
  336. pFrame->SetValue( "root_angles", pData->m_lstFrames[ j ]->angRootAngles );
  337. for ( int k = 0; k < pData->m_nNumBones; ++k )
  338. {
  339. CDmxAttribute* pPositions = pFrame->AddAttribute( "positions" );
  340. CUtlVector< Vector >& positions = pPositions->GetArrayForEdit< Vector >();
  341. CDmxAttribute* pAngles = pFrame->AddAttribute( "angles" );
  342. CUtlVector< QAngle >& angles = pAngles->GetArrayForEdit< QAngle >();
  343. positions.AddToTail( pData->m_lstFrames[ j ]->pPositions[ k ] );
  344. angles.AddToTail( pData->m_lstFrames[ j ]->pAngles[ k ] );
  345. }
  346. }
  347. }
  348. {
  349. MEM_ALLOC_CREDIT();
  350. if ( !SerializeDMX( pszFilename, "GAME", false, pSimulations ) )
  351. {
  352. Warning( "Replay: Failed to write ragdoll cache, %s.\n", pszFilename );
  353. return false;
  354. }
  355. }
  356. CleanupDMX( pSimulations );
  357. Msg( "Replay: Cached ragdoll data.\n" );
  358. return true;
  359. }
  360. //--------------------------------------------------------------------------------
  361. CReplayRagdollCache::CReplayRagdollCache()
  362. : m_bInit( false )
  363. {
  364. }
  365. /*static*/ CReplayRagdollCache& CReplayRagdollCache::Instance()
  366. {
  367. static CReplayRagdollCache s_instance;
  368. return s_instance;
  369. }
  370. bool CReplayRagdollCache::Init( char const* pszFilename )
  371. {
  372. Assert( !m_bInit );
  373. // Make sure valid filename
  374. if ( !pszFilename || pszFilename[0] == 0 )
  375. return false;
  376. DECLARE_DMX_CONTEXT();
  377. // Attempt to read from disk
  378. CDmxElement* pRagdolls = NULL;
  379. if ( !UnserializeDMX( pszFilename, "GAME", false, &pRagdolls ) )
  380. return false;
  381. CUtlVector< CDmxElement* > const& ragdolls = pRagdolls->GetArray< CDmxElement* >( "ragdolls" );
  382. for ( int i = 0; i < ragdolls.Count(); ++i )
  383. {
  384. CDmxElement* pCurRagdollInput = ragdolls[ i ];
  385. // Create a new ragdoll entry and add to list
  386. RagdollSimulationData_t* pNewSimData = new RagdollSimulationData_t();
  387. m_lstRagdolls.AddToTail( pNewSimData );
  388. // Read
  389. pCurRagdollInput->UnpackIntoStructure( pNewSimData, s_RagdollSimulationDataUnpack );
  390. // NOTE: Entity ptr doesn't get linked up here because it doesn't necessarily exist at this point
  391. // Read frames
  392. CUtlVector< CDmxElement* > const& frames = pCurRagdollInput->GetArray< CDmxElement* >( "frames" );
  393. for ( int j = 0; j < frames.Count(); ++j )
  394. {
  395. CDmxElement* pCurFrameInput = frames[ j ];
  396. // Create a new frame and add it to list of frames
  397. RagdollSimulationFrame_t* pNewFrame = RagdollSimulationFrame_t::Alloc( pNewSimData->m_nNumBones );
  398. pNewSimData->m_lstFrames.AddToTail( pNewFrame );
  399. // Read tick
  400. pNewFrame->nTick = pCurFrameInput->GetValue( "tick", -1 ); Assert( pNewFrame->nTick != -1 );
  401. // Read root pos/orientation
  402. pNewFrame->vRootPosition = pCurFrameInput->GetValue( "root_pos" , vec3_origin );
  403. pNewFrame->angRootAngles = pCurFrameInput->GetValue( "root_angles", vec3_angle );
  404. CUtlVector< Vector > const& positions = pCurFrameInput->GetArray< Vector >( "positions" );
  405. CUtlVector< QAngle > const& angles = pCurFrameInput->GetArray< QAngle >( "angles" );
  406. for ( int k = 0; k < pNewSimData->m_nNumBones; ++k )
  407. {
  408. pNewFrame->pPositions[ k ] = positions[ k ];
  409. pNewFrame->pAngles[ k ] = angles[ k ];
  410. }
  411. }
  412. }
  413. // Cleanup
  414. CleanupDMX( pRagdolls );
  415. m_bInit = true;
  416. return true;
  417. }
  418. void CReplayRagdollCache::Shutdown()
  419. {
  420. if ( !m_bInit )
  421. return;
  422. m_lstRagdolls.PurgeAndDeleteElements();
  423. m_bInit = false;
  424. }
  425. ConVar replay_ragdoll_blending( "replay_ragdoll_blending", "1" );
  426. ConVar replay_ragdoll_tickoffset( "replay_ragdoll_tickoffset", "0" );
  427. bool CReplayRagdollCache::GetFrame( C_BaseAnimating* pEntity, int nTick, bool* pBoneSimulated, CBoneAccessor* pBoneAccessor ) const
  428. {
  429. nTick += replay_ragdoll_tickoffset.GetInt();
  430. Assert( pEntity );
  431. Assert( pBoneSimulated );
  432. Assert( pEntity->m_pRagdoll );
  433. // Find ragdoll for the given entity - will return NULL if nTick is out of the entry's time window
  434. const RagdollSimulationData_t* pRagdollEntry = FindRagdollEntry( pEntity, nTick );
  435. if ( !pRagdollEntry )
  436. return false;
  437. // Find frame for the given tick
  438. RagdollSimulationFrame_t* pFrame;
  439. RagdollSimulationFrame_t* pNextFrame;
  440. if ( !FindFrame( pFrame, pNextFrame, pRagdollEntry, nTick ) )
  441. return false;
  442. // Compute root transform
  443. matrix3x4_t rootTransform;
  444. float flInterpAmount = gpGlobals->interpolation_amount;
  445. if ( pNextFrame )
  446. {
  447. AngleMatrix(
  448. (const QAngle &)Lerp( flInterpAmount, pFrame->angRootAngles, pNextFrame->angRootAngles ), // Actually does a slerp
  449. Lerp( flInterpAmount, pFrame->vRootPosition, pNextFrame->vRootPosition ),
  450. rootTransform
  451. );
  452. }
  453. else
  454. {
  455. AngleMatrix( pFrame->angRootAngles, pFrame->vRootPosition, rootTransform );
  456. }
  457. // Compute each bone
  458. ragdoll_t* pRagdoll = pEntity->m_pRagdoll->GetRagdoll(); Assert( pRagdoll );
  459. for ( int k = 0; k < pRagdoll->listCount; ++k )
  460. {
  461. int objectIndex = k;
  462. const ragdollelement_t& element = pRagdoll->list[ objectIndex ];
  463. int const boneIndex = pRagdoll->boneIndex[ objectIndex ]; Assert( boneIndex >= 0 );
  464. // Compute blended transform if possible
  465. matrix3x4_t localTransform;
  466. if ( pNextFrame && replay_ragdoll_blending.GetInt() )
  467. {
  468. // Get blended Eular angles - NOTE: The Lerp() here actually calls Lerp<QAngle>() which converts to quats and back
  469. float flInterpAmount = gpGlobals->interpolation_amount; Assert( flInterpAmount >= 0.0f && flInterpAmount <= 1.0f );
  470. AngleMatrix(
  471. (const QAngle &)Lerp( flInterpAmount, pFrame->pAngles [ objectIndex ], pNextFrame->pAngles [ objectIndex ] ),
  472. Lerp( flInterpAmount, pFrame->pPositions[ objectIndex ], pNextFrame->pPositions[ objectIndex ] ),
  473. localTransform
  474. );
  475. }
  476. else
  477. {
  478. // Last frame
  479. AngleMatrix( pFrame->pAngles[ objectIndex ], pFrame->pPositions[ objectIndex ], localTransform );
  480. }
  481. matrix3x4_t& boneMatrix = pBoneAccessor->GetBoneForWrite( boneIndex );
  482. if ( element.parentIndex < 0 )
  483. {
  484. ConcatTransforms( rootTransform, localTransform, boneMatrix );
  485. }
  486. else
  487. {
  488. int parentBoneIndex = pRagdoll->boneIndex[ element.parentIndex ]; Assert( parentBoneIndex >= 0 );
  489. Assert( pBoneSimulated[ parentBoneIndex ] );
  490. matrix3x4_t const& parentMatrix = pBoneAccessor->GetBone( parentBoneIndex );
  491. ConcatTransforms( parentMatrix, localTransform, boneMatrix );
  492. }
  493. // Simulated this bone
  494. pBoneSimulated[ boneIndex ] = true;
  495. }
  496. DrawBones( pBoneAccessor->GetBoneArrayForWrite(), pRagdollEntry->m_nNumBones, pRagdoll, 0, 0, 255, pEntity );
  497. return true;
  498. }
  499. RagdollSimulationData_t* CReplayRagdollCache::FindRagdollEntry( C_BaseAnimating* pEntity, int nTick )
  500. {
  501. Assert( pEntity );
  502. int const nEntIndex = pEntity->entindex();
  503. FOR_EACH_LL( m_lstRagdolls, i )
  504. {
  505. RagdollSimulationData_t* pRagdollData = m_lstRagdolls[ i ];
  506. // If not the right entity or the tick is out range, continue.
  507. if ( pRagdollData->m_nEntityIndex != nEntIndex )
  508. continue;
  509. // We've got the ragdoll, but only return it if nTick is in the window
  510. if ( nTick < pRagdollData->m_nStartTick ||
  511. nTick > pRagdollData->m_nStartTick + pRagdollData->m_nDuration )
  512. return NULL;
  513. return pRagdollData;
  514. }
  515. return NULL;
  516. }
  517. bool CReplayRagdollCache::FindFrame( RagdollSimulationFrame_t*& pFrameOut, RagdollSimulationFrame_t*& pNextFrameOut,
  518. const RagdollSimulationData_t* pRagdollEntry, int nTick )
  519. {
  520. // Look for the appropriate frame
  521. FOR_EACH_LL( pRagdollEntry->m_lstFrames, j )
  522. {
  523. RagdollSimulationFrame_t* pFrame = pRagdollEntry->m_lstFrames[ j ];
  524. // Get next frame if possible
  525. int const nNext = pRagdollEntry->m_lstFrames.Next( j );
  526. RagdollSimulationFrame_t* pNextFrame =
  527. nNext == pRagdollEntry->m_lstFrames.InvalidIndex() ? NULL : pRagdollEntry->m_lstFrames[ nNext ];
  528. // Use this frame?
  529. if ( nTick >= pFrame->nTick &&
  530. ( (pNextFrame && nTick <= pNextFrame->nTick) || !pNextFrame ) ) // Use the last frame if the tick is past the range of frames -
  531. { // this is the "sleeping" ragdoll frame
  532. pFrameOut = pFrame;
  533. pNextFrameOut = pNextFrame;
  534. return true;
  535. }
  536. }
  537. pFrameOut = NULL;
  538. pNextFrameOut = NULL;
  539. return false;
  540. }
  541. void CReplayRagdollCache::Think()
  542. {
  543. // TODO: Add IsPlayingReplayDemo() to engine interface
  544. engine->Con_NPrintf( 8, "time: %d", engine->GetDemoPlaybackTick() );
  545. FOR_EACH_LL( m_lstRagdolls, i )
  546. {
  547. engine->Con_NPrintf( 10 + i, "entity %d: start time=%d duration=%d num bones=%d", m_lstRagdolls[i]->m_nEntityIndex, m_lstRagdolls[i]->m_nStartTick, m_lstRagdolls[i]->m_nDuration, m_lstRagdolls[i]->m_nNumBones );
  548. }
  549. }
  550. //--------------------------------------------------------------------------------
  551. bool Replay_CacheRagdolls( const char* pFilename, int nStartTick )
  552. {
  553. CReplayRagdollRecorder::Instance().CleanupStartupTicksAndDurations( nStartTick );
  554. return CReplayRagdollRecorder::Instance().DumpRagdollsToDisk( pFilename );
  555. }
  556. #endif