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.

655 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "tier0/dbg.h"
  9. #include "tier0/platform.h"
  10. #include "mathlib/mathlib.h"
  11. #include "tier0/tslist.h"
  12. #include "tier1/utlmap.h"
  13. #include "tier1/convar.h"
  14. #include "bone_setup.h"
  15. #include "con_nprint.h"
  16. #include "cdll_int.h"
  17. #include "globalvars_base.h"
  18. #include "posedebugger.h"
  19. #include "iclientnetworkable.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. extern IVEngineClient *engine;
  23. extern CGlobalVarsBase *gpGlobals;
  24. static ConVar ui_posedebug_fade_in_time( "ui_posedebug_fade_in_time", "0.2",
  25. FCVAR_CHEAT | FCVAR_DONTRECORD,
  26. "Time during which a new pose activity layer is shown in green in +posedebug UI" );
  27. static ConVar ui_posedebug_fade_out_time( "ui_posedebug_fade_out_time", "0.8",
  28. FCVAR_CHEAT | FCVAR_DONTRECORD,
  29. "Time to keep a no longer active pose activity layer in red until removing it from +posedebug UI" );
  30. //////////////////////////////////////////////////////////////////////////
  31. //
  32. // CPoseDebuggerStub : IPoseDebugger
  33. // empty interface implementation
  34. //
  35. //////////////////////////////////////////////////////////////////////////
  36. class CPoseDebuggerStub : public IPoseDebugger
  37. {
  38. public:
  39. virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) { }
  40. virtual void AccumulatePose(
  41. const CStudioHdr *pStudioHdr,
  42. CIKContext *pIKContext,
  43. Vector pos[],
  44. Quaternion q[],
  45. int sequence,
  46. float cycle,
  47. const float poseParameter[],
  48. int boneMask,
  49. float flWeight,
  50. float flTime
  51. ) { }
  52. };
  53. static CPoseDebuggerStub s_PoseDebuggerStub;
  54. IPoseDebugger *g_pPoseDebugger = &s_PoseDebuggerStub;
  55. //////////////////////////////////////////////////////////////////////////
  56. //
  57. // CPoseDebuggerImpl : IPoseDebugger
  58. // Purpose: Main implementation of the pose debugger
  59. // Declaration
  60. //
  61. //////////////////////////////////////////////////////////////////////////
  62. class ModelPoseDebugInfo
  63. {
  64. public:
  65. ModelPoseDebugInfo() : m_iEntNum( 0 ), m_iCurrentText( 0 ) { }
  66. public:
  67. // Entity number
  68. int m_iEntNum;
  69. // Currently processed text
  70. int m_iCurrentText;
  71. // Info Text Flags
  72. enum InfoTextFlags
  73. {
  74. F_SEEN_THIS_FRAME = 1 << 0,
  75. F_SEEN_LAST_FRAME = 1 << 1,
  76. };
  77. struct InfoText
  78. {
  79. InfoText() { memset( this, 0, sizeof( *this ) ); }
  80. // Flags
  81. uint32 m_uiFlags;
  82. // Time seen
  83. float m_flTimeToLive, m_flTimeAlive;
  84. // Activity/label
  85. int m_iActivity;
  86. char m_chActivity[100];
  87. char m_chLabel[100];
  88. // Text
  89. char m_chTextLines[4][256];
  90. enum
  91. {
  92. MAX_TEXT_LINES = 4
  93. };
  94. };
  95. CCopyableUtlVector< InfoText > m_arrTxt;
  96. public:
  97. // Add an info text
  98. void AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld );
  99. // Lookup an info text
  100. InfoText *LookupInfoText( InfoText *x );
  101. // Print pending info text
  102. void PrintPendingInfoText( int &rnPosPrint );
  103. };
  104. void ModelPoseDebugInfo::AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld )
  105. {
  106. if ( x )
  107. {
  108. // Try to set the proper flags on the info text
  109. x->m_uiFlags &= ~F_SEEN_LAST_FRAME;
  110. x->m_uiFlags |= F_SEEN_THIS_FRAME;
  111. }
  112. // If we have smth to compare against
  113. if ( pOld )
  114. {
  115. // Search for the same activity/label in the other model pose debug info
  116. ModelPoseDebugInfo &o = *pOld;
  117. int k = o.m_iCurrentText;
  118. if ( x )
  119. {
  120. for ( ; k < o.m_arrTxt.Count(); ++ k )
  121. {
  122. InfoText &txt = o.m_arrTxt[k];
  123. if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) &&
  124. !stricmp( x->m_chActivity, txt.m_chActivity ) &&
  125. !stricmp( x->m_chLabel, txt.m_chLabel ) &&
  126. ( x->m_iActivity == txt.m_iActivity ) )
  127. {
  128. x->m_flTimeAlive = txt.m_flTimeAlive;
  129. break;
  130. }
  131. }
  132. }
  133. else
  134. {
  135. k = o.m_arrTxt.Count();
  136. }
  137. // Range of finished activities
  138. int iFinishedRange[2] = { o.m_iCurrentText, k };
  139. // Check whether this is a new message
  140. if ( k == o.m_arrTxt.Count() )
  141. {
  142. if ( !x )
  143. {
  144. o.m_iCurrentText = k;
  145. }
  146. else
  147. {
  148. // Don't update the current when insertion happens and don't have finished commands
  149. iFinishedRange[1] = iFinishedRange[0];
  150. }
  151. }
  152. else
  153. {
  154. o.m_iCurrentText = k + 1;
  155. if ( x )
  156. {
  157. x->m_uiFlags |= F_SEEN_LAST_FRAME;
  158. x->m_flTimeAlive += gpGlobals->frametime;
  159. }
  160. }
  161. // Everything before finished
  162. for ( int iFinished = iFinishedRange[0]; iFinished < iFinishedRange[1]; ++ iFinished )
  163. {
  164. InfoText &txtFinished = o.m_arrTxt[ iFinished ];
  165. if ( txtFinished.m_uiFlags & F_SEEN_THIS_FRAME )
  166. txtFinished.m_uiFlags |= F_SEEN_LAST_FRAME;
  167. txtFinished.m_uiFlags &= ~F_SEEN_THIS_FRAME;
  168. txtFinished.m_flTimeToLive -= gpGlobals->frametime;
  169. txtFinished.m_flTimeAlive += gpGlobals->frametime;
  170. if ( txtFinished.m_flTimeToLive >= 0.0f )
  171. m_arrTxt.AddToTail( txtFinished );
  172. }
  173. }
  174. if ( x )
  175. {
  176. // Now add it to the array
  177. x->m_flTimeToLive = ui_posedebug_fade_out_time.GetFloat();
  178. m_arrTxt.AddToTail( *x );
  179. }
  180. }
  181. ModelPoseDebugInfo::InfoText * ModelPoseDebugInfo::LookupInfoText( InfoText *x )
  182. {
  183. int k = m_iCurrentText;
  184. if ( x )
  185. {
  186. for ( ; k < m_arrTxt.Count(); ++ k )
  187. {
  188. InfoText &txt = m_arrTxt[k];
  189. if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) &&
  190. !stricmp( x->m_chActivity, txt.m_chActivity ) &&
  191. !stricmp( x->m_chLabel, txt.m_chLabel ) &&
  192. ( x->m_iActivity == txt.m_iActivity ) )
  193. {
  194. return &txt;
  195. }
  196. }
  197. }
  198. return NULL;
  199. }
  200. void ModelPoseDebugInfo::PrintPendingInfoText( int &rnPosPrint )
  201. {
  202. con_nprint_s nxPrn = { 0 };
  203. nxPrn.time_to_live = -1;
  204. nxPrn.color[0] = 1.0f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 1.0f;
  205. nxPrn.fixed_width_font = true;
  206. float const flFadeInTime = ui_posedebug_fade_in_time.GetFloat();
  207. float const flFadeOutTime = ui_posedebug_fade_out_time.GetFloat();
  208. // Now print all the accumulated spew
  209. for ( int k = m_iCurrentText; k < m_arrTxt.Count(); ++ k )
  210. {
  211. InfoText &prntxt = m_arrTxt[k];
  212. switch( prntxt.m_uiFlags & ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) )
  213. {
  214. case ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) :
  215. nxPrn.color[0] = 1.f;
  216. nxPrn.color[1] = 1.f;
  217. nxPrn.color[2] = 1.f;
  218. if ( prntxt.m_flTimeAlive > flFadeInTime )
  219. break;
  220. else
  221. NULL; // Fall-through to keep showing in green
  222. case F_SEEN_THIS_FRAME :
  223. if ( flFadeInTime > 0.f )
  224. {
  225. nxPrn.color[0] = 1.f * prntxt.m_flTimeAlive / flFadeInTime;
  226. nxPrn.color[1] = 1.f;
  227. nxPrn.color[2] = 1.f * prntxt.m_flTimeAlive / flFadeInTime;
  228. }
  229. else
  230. {
  231. nxPrn.color[0] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f;
  232. nxPrn.color[1] = 1.f;
  233. nxPrn.color[2] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f;
  234. }
  235. break;
  236. case F_SEEN_LAST_FRAME :
  237. case 0:
  238. if ( flFadeOutTime > 0.f )
  239. nxPrn.color[0] = 1.f * prntxt.m_flTimeToLive / flFadeOutTime;
  240. else
  241. nxPrn.color[0] = ( prntxt.m_flTimeToLive > 0.0f ) ? 1.f : 0.f;
  242. nxPrn.color[1] = 0.f;
  243. nxPrn.color[2] = 0.f;
  244. break;
  245. }
  246. nxPrn.index = ( rnPosPrint += 1 );
  247. engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[0] );
  248. for ( int iLine = 1; iLine < ModelPoseDebugInfo::InfoText::MAX_TEXT_LINES; ++ iLine)
  249. {
  250. if ( !prntxt.m_chTextLines[iLine][0] )
  251. break;
  252. nxPrn.index = ( rnPosPrint += 1 );
  253. engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[iLine] );
  254. }
  255. }
  256. m_iCurrentText = m_arrTxt.Count();
  257. }
  258. class CPoseDebuggerImpl : public IPoseDebugger
  259. {
  260. public:
  261. CPoseDebuggerImpl();
  262. ~CPoseDebuggerImpl();
  263. public:
  264. void ShowAllModels( bool bShow );
  265. void ShowModel( int iEntNum, bool bShow );
  266. bool IsModelShown( int iEntNum ) const;
  267. public:
  268. virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr );
  269. virtual void AccumulatePose(
  270. const CStudioHdr *pStudioHdr,
  271. CIKContext *pIKContext,
  272. Vector pos[],
  273. Quaternion q[],
  274. int sequence,
  275. float cycle,
  276. const float poseParameter[],
  277. int boneMask,
  278. float flWeight,
  279. float flTime
  280. );
  281. protected:
  282. typedef CUtlMap< CStudioHdr const *, ModelPoseDebugInfo > MapModel;
  283. MapModel m_mapModel, m_mapModelOld;
  284. int m_nPosPrint;
  285. CBitVec< MAX_EDICTS > m_uiMaskShowModels;
  286. CStudioHdr const *m_pLastModel;
  287. };
  288. static CPoseDebuggerImpl s_PoseDebuggerImpl;
  289. //////////////////////////////////////////////////////////////////////////
  290. //
  291. // CPoseDebuggerImpl
  292. // Implementation
  293. //
  294. //////////////////////////////////////////////////////////////////////////
  295. CPoseDebuggerImpl::CPoseDebuggerImpl() :
  296. m_mapModel( DefLessFunc( CStudioHdr const * ) ),
  297. m_mapModelOld( DefLessFunc( CStudioHdr const * ) ),
  298. m_nPosPrint( 0 ),
  299. m_pLastModel( NULL )
  300. {
  301. m_uiMaskShowModels.SetAll();
  302. }
  303. CPoseDebuggerImpl::~CPoseDebuggerImpl()
  304. {
  305. // g_pPoseDebugger = &s_PoseDebuggerStub;
  306. }
  307. void CPoseDebuggerImpl::ShowAllModels( bool bShow )
  308. {
  309. bShow ? m_uiMaskShowModels.SetAll() : m_uiMaskShowModels.ClearAll();
  310. }
  311. void CPoseDebuggerImpl::ShowModel( int iEntNum, bool bShow )
  312. {
  313. Assert( iEntNum >= 0 && iEntNum < MAX_EDICTS );
  314. if ( iEntNum >= 0 && iEntNum < MAX_EDICTS )
  315. m_uiMaskShowModels.Set( iEntNum, bShow );
  316. }
  317. bool CPoseDebuggerImpl::IsModelShown( int iEntNum ) const
  318. {
  319. Assert( iEntNum >= 0 && iEntNum < MAX_EDICTS );
  320. if ( iEntNum >= 0 && iEntNum < MAX_EDICTS )
  321. return m_uiMaskShowModels.IsBitSet( iEntNum );
  322. else
  323. return false;
  324. }
  325. void CPoseDebuggerImpl::StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr )
  326. {
  327. // virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel();
  328. // if ( !pVMdl )
  329. // return;
  330. // If we are starting a new model then finalize the previous one
  331. if ( pStudioHdr != m_pLastModel && m_pLastModel )
  332. {
  333. MapModel::IndexType_t idx = m_mapModel.Find( m_pLastModel );
  334. if ( idx != m_mapModel.InvalidIndex() )
  335. {
  336. ModelPoseDebugInfo &mpi = m_mapModel.Element( idx );
  337. ModelPoseDebugInfo *pMpiOld = NULL;
  338. MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( m_pLastModel );
  339. if ( idxMapModelOld != m_mapModelOld.InvalidIndex() )
  340. {
  341. pMpiOld = &m_mapModelOld.Element( idxMapModelOld );
  342. }
  343. mpi.AddInfoText( NULL, pMpiOld );
  344. mpi.PrintPendingInfoText( m_nPosPrint );
  345. }
  346. }
  347. m_pLastModel = pStudioHdr;
  348. // Go ahead with the new model
  349. studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr();
  350. if ( !pRMdl ||
  351. !pRMdl->numincludemodels )
  352. return;
  353. // Entity number
  354. int iEntNum = pEntity->entindex();
  355. if ( !IsModelShown( iEntNum ) )
  356. return;
  357. // Check if we saw the model
  358. if ( m_mapModel.Find( pStudioHdr ) != m_mapModel.InvalidIndex() )
  359. {
  360. // Initialize the printing position
  361. m_nPosPrint = 9;
  362. // Swap the maps
  363. m_mapModelOld.RemoveAll();
  364. m_mapModelOld.Swap( m_mapModel );
  365. // Zero out the text on the old map
  366. for ( int k = m_mapModelOld.FirstInorder();
  367. k != m_mapModelOld.InvalidIndex();
  368. k = m_mapModelOld.NextInorder( k ) )
  369. {
  370. ModelPoseDebugInfo &mpi = m_mapModelOld[k];
  371. mpi.m_iCurrentText = 0;
  372. }
  373. }
  374. else
  375. {
  376. // Next model
  377. m_nPosPrint += 3;
  378. }
  379. ModelPoseDebugInfo mpi;
  380. mpi.m_iEntNum = iEntNum;
  381. m_mapModel.Insert( pStudioHdr, mpi );
  382. con_nprint_s nxPrn = { 0 };
  383. nxPrn.index = m_nPosPrint;
  384. nxPrn.time_to_live = -1;
  385. nxPrn.color[0] = 0.9f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 0.9f;
  386. nxPrn.fixed_width_font = false;
  387. engine->Con_NXPrintf( &nxPrn, "[ %2d ] Model: %s", iEntNum, pRMdl->pszName() );
  388. m_nPosPrint += 3;
  389. }
  390. void CPoseDebuggerImpl::AccumulatePose( const CStudioHdr *pStudioHdr, CIKContext *pIKContext,
  391. Vector pos[], Quaternion q[], int sequence, float cycle,
  392. const float poseParameter[], int boneMask,
  393. float flWeight, float flTime )
  394. {
  395. // virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel();
  396. // if ( !pVMdl )
  397. // return;
  398. studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr();
  399. if ( !pRMdl ||
  400. !pRMdl->numincludemodels )
  401. return;
  402. MapModel::IndexType_t idxMapModel = m_mapModel.Find( pStudioHdr );
  403. if ( idxMapModel == m_mapModel.InvalidIndex() )
  404. return;
  405. ModelPoseDebugInfo &mpi = m_mapModel.Element( idxMapModel );
  406. if ( !IsModelShown( mpi.m_iEntNum ) )
  407. return;
  408. ModelPoseDebugInfo *pMpiOld = NULL;
  409. MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( pStudioHdr );
  410. if ( idxMapModelOld != m_mapModelOld.InvalidIndex() )
  411. {
  412. pMpiOld = &m_mapModelOld.Element( idxMapModelOld );
  413. }
  414. //
  415. // Actual processing
  416. //
  417. mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence );
  418. if ( sequence >= pStudioHdr->GetNumSeq() )
  419. {
  420. sequence = 0;
  421. seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence );
  422. }
  423. enum
  424. {
  425. widthActivity = 35,
  426. widthLayer = 35,
  427. widthIks = 60,
  428. widthPercent = 6,
  429. };
  430. // Prepare the text
  431. char chBuffer[256];
  432. ModelPoseDebugInfo::InfoText txt;
  433. int numLines = 0;
  434. txt.m_iActivity = seqdesc.activity;
  435. sprintf( txt.m_chActivity, "%s", seqdesc.pszActivityName() );
  436. sprintf( txt.m_chLabel, "%s", seqdesc.pszLabel() );
  437. if ( !txt.m_chActivity[0] )
  438. {
  439. // Try to find the last seen activity and re-use it
  440. for ( int iLast = mpi.m_arrTxt.Count(); iLast --> 0; )
  441. {
  442. ModelPoseDebugInfo::InfoText &lastSeenTxt = mpi.m_arrTxt[iLast];
  443. if ( lastSeenTxt.m_uiFlags & ModelPoseDebugInfo::F_SEEN_THIS_FRAME &&
  444. lastSeenTxt.m_chActivity[0] )
  445. {
  446. sprintf( txt.m_chActivity, "%s", lastSeenTxt.m_chActivity );
  447. break;
  448. }
  449. }
  450. }
  451. // The layer information
  452. ModelPoseDebugInfo::InfoText *pOldTxt = pMpiOld ? pMpiOld->LookupInfoText( &txt ) : NULL;
  453. sprintf( txt.m_chTextLines[numLines],
  454. "%-*s %-*s %*.2f %*.1f/%-*d %*.0f%% ",
  455. widthActivity,
  456. seqdesc.pszActivityName(),
  457. widthLayer,
  458. seqdesc.pszLabel(),
  459. 7,
  460. pOldTxt ? pOldTxt->m_flTimeAlive : 0.f,
  461. 5,
  462. cycle * ( ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes - 1 ),
  463. 3,
  464. ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes,
  465. widthPercent,
  466. flWeight * 100.0f
  467. );
  468. ++ numLines;
  469. if ( seqdesc.numiklocks )
  470. {
  471. sprintf( chBuffer,
  472. "iklocks : %-2d : ",
  473. seqdesc.numiklocks );
  474. for ( int k = 0; k < seqdesc.numiklocks; ++ k )
  475. {
  476. mstudioiklock_t *plock = seqdesc.pIKLock( k );
  477. mstudioikchain_t *pchain = pStudioHdr->pIKChain( plock->chain );
  478. sprintf( chBuffer + strlen( chBuffer ), "%s ", pchain->pszName() );
  479. // plock->flPosWeight;
  480. // plock->flLocalQWeight;
  481. }
  482. sprintf( txt.m_chTextLines[numLines],
  483. "%-*s",
  484. widthIks,
  485. chBuffer
  486. );
  487. ++ numLines;
  488. }
  489. if ( seqdesc.numikrules )
  490. {
  491. sprintf( chBuffer, "ikrules : %-2d",
  492. seqdesc.numikrules );
  493. sprintf( txt.m_chTextLines[numLines],
  494. "%-*s",
  495. widthIks,
  496. chBuffer
  497. );
  498. ++ numLines;
  499. }
  500. // Now add the accumulated text into the container
  501. mpi.AddInfoText( &txt, pMpiOld );
  502. mpi.PrintPendingInfoText( m_nPosPrint );
  503. }
  504. //////////////////////////////////////////////////////////////////////////
  505. //
  506. // Con-commands
  507. //
  508. //////////////////////////////////////////////////////////////////////////
  509. static void IN_PoseDebuggerStart( const CCommand &args )
  510. {
  511. if ( args.ArgC() <= 1 )
  512. {
  513. // No args, enable all
  514. s_PoseDebuggerImpl.ShowAllModels( true );
  515. }
  516. else
  517. {
  518. // If explicitly showing the pose debugger when it was disabled
  519. if ( g_pPoseDebugger != &s_PoseDebuggerImpl )
  520. {
  521. s_PoseDebuggerImpl.ShowAllModels( false );
  522. }
  523. // Show only specific models
  524. for ( int k = 1; k < args.ArgC(); ++ k )
  525. {
  526. int iEntNum = atoi( args.Arg( k ) );
  527. s_PoseDebuggerImpl.ShowModel( iEntNum, true );
  528. }
  529. }
  530. g_pPoseDebugger = &s_PoseDebuggerImpl;
  531. }
  532. static void IN_PoseDebuggerEnd( const CCommand &args )
  533. {
  534. if ( args.ArgC() <= 1 )
  535. {
  536. // No args, disable all
  537. s_PoseDebuggerImpl.ShowAllModels( false );
  538. // Set the stub pointer
  539. g_pPoseDebugger = &s_PoseDebuggerStub;
  540. }
  541. else
  542. {
  543. // Hide only specific models
  544. for ( int k = 1; k < args.ArgC(); ++ k )
  545. {
  546. int iEntNum = atoi( args.Arg( k ) );
  547. s_PoseDebuggerImpl.ShowModel( iEntNum, false );
  548. }
  549. }
  550. }
  551. static ConCommand posedebuggerstart( "+posedebug", IN_PoseDebuggerStart, "Turn on pose debugger or add ents to pose debugger UI", FCVAR_CHEAT );
  552. static ConCommand posedebuggerend ( "-posedebug", IN_PoseDebuggerEnd, "Turn off pose debugger or hide ents from pose debugger UI", FCVAR_CHEAT );