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.

749 lines
22 KiB

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