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
655 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
#include "tier0/dbg.h" |
|
#include "tier0/platform.h" |
|
#include "mathlib/mathlib.h" |
|
#include "tier0/tslist.h" |
|
#include "tier1/utlmap.h" |
|
#include "tier1/convar.h" |
|
|
|
#include "bone_setup.h" |
|
|
|
#include "con_nprint.h" |
|
#include "cdll_int.h" |
|
#include "globalvars_base.h" |
|
|
|
#include "posedebugger.h" |
|
|
|
#include "iclientnetworkable.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
extern IVEngineClient *engine; |
|
extern CGlobalVarsBase *gpGlobals; |
|
|
|
static ConVar ui_posedebug_fade_in_time( "ui_posedebug_fade_in_time", "0.2", |
|
FCVAR_CHEAT | FCVAR_DONTRECORD, |
|
"Time during which a new pose activity layer is shown in green in +posedebug UI" ); |
|
static ConVar ui_posedebug_fade_out_time( "ui_posedebug_fade_out_time", "0.8", |
|
FCVAR_CHEAT | FCVAR_DONTRECORD, |
|
"Time to keep a no longer active pose activity layer in red until removing it from +posedebug UI" ); |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// |
|
// CPoseDebuggerStub : IPoseDebugger |
|
// empty interface implementation |
|
// |
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
class CPoseDebuggerStub : public IPoseDebugger |
|
{ |
|
public: |
|
virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) { } |
|
virtual void AccumulatePose( |
|
const CStudioHdr *pStudioHdr, |
|
CIKContext *pIKContext, |
|
Vector pos[], |
|
Quaternion q[], |
|
int sequence, |
|
float cycle, |
|
const float poseParameter[], |
|
int boneMask, |
|
float flWeight, |
|
float flTime |
|
) { } |
|
}; |
|
|
|
static CPoseDebuggerStub s_PoseDebuggerStub; |
|
IPoseDebugger *g_pPoseDebugger = &s_PoseDebuggerStub; |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// |
|
// CPoseDebuggerImpl : IPoseDebugger |
|
// Purpose: Main implementation of the pose debugger |
|
// Declaration |
|
// |
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
class ModelPoseDebugInfo |
|
{ |
|
public: |
|
ModelPoseDebugInfo() : m_iEntNum( 0 ), m_iCurrentText( 0 ) { } |
|
|
|
|
|
public: |
|
// Entity number |
|
int m_iEntNum; |
|
|
|
// Currently processed text |
|
int m_iCurrentText; |
|
|
|
// Info Text Flags |
|
enum InfoTextFlags |
|
{ |
|
F_SEEN_THIS_FRAME = 1 << 0, |
|
F_SEEN_LAST_FRAME = 1 << 1, |
|
}; |
|
|
|
struct InfoText |
|
{ |
|
InfoText() { memset( this, 0, sizeof( *this ) ); } |
|
|
|
// Flags |
|
uint32 m_uiFlags; |
|
|
|
// Time seen |
|
float m_flTimeToLive, m_flTimeAlive; |
|
|
|
// Activity/label |
|
int m_iActivity; |
|
char m_chActivity[100]; |
|
char m_chLabel[100]; |
|
|
|
// Text |
|
char m_chTextLines[4][256]; |
|
enum |
|
{ |
|
MAX_TEXT_LINES = 4 |
|
}; |
|
}; |
|
|
|
CCopyableUtlVector< InfoText > m_arrTxt; |
|
|
|
|
|
public: |
|
// Add an info text |
|
void AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld ); |
|
|
|
// Lookup an info text |
|
InfoText *LookupInfoText( InfoText *x ); |
|
|
|
// Print pending info text |
|
void PrintPendingInfoText( int &rnPosPrint ); |
|
}; |
|
|
|
void ModelPoseDebugInfo::AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld ) |
|
{ |
|
if ( x ) |
|
{ |
|
// Try to set the proper flags on the info text |
|
x->m_uiFlags &= ~F_SEEN_LAST_FRAME; |
|
x->m_uiFlags |= F_SEEN_THIS_FRAME; |
|
} |
|
|
|
// If we have smth to compare against |
|
if ( pOld ) |
|
{ |
|
// Search for the same activity/label in the other model pose debug info |
|
ModelPoseDebugInfo &o = *pOld; |
|
int k = o.m_iCurrentText; |
|
if ( x ) |
|
{ |
|
for ( ; k < o.m_arrTxt.Count(); ++ k ) |
|
{ |
|
InfoText &txt = o.m_arrTxt[k]; |
|
if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) && |
|
!stricmp( x->m_chActivity, txt.m_chActivity ) && |
|
!stricmp( x->m_chLabel, txt.m_chLabel ) && |
|
( x->m_iActivity == txt.m_iActivity ) ) |
|
{ |
|
x->m_flTimeAlive = txt.m_flTimeAlive; |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
k = o.m_arrTxt.Count(); |
|
} |
|
|
|
// Range of finished activities |
|
int iFinishedRange[2] = { o.m_iCurrentText, k }; |
|
|
|
// Check whether this is a new message |
|
if ( k == o.m_arrTxt.Count() ) |
|
{ |
|
if ( !x ) |
|
{ |
|
o.m_iCurrentText = k; |
|
} |
|
else |
|
{ |
|
// Don't update the current when insertion happens and don't have finished commands |
|
iFinishedRange[1] = iFinishedRange[0]; |
|
} |
|
} |
|
else |
|
{ |
|
o.m_iCurrentText = k + 1; |
|
if ( x ) |
|
{ |
|
x->m_uiFlags |= F_SEEN_LAST_FRAME; |
|
x->m_flTimeAlive += gpGlobals->frametime; |
|
} |
|
} |
|
|
|
// Everything before finished |
|
for ( int iFinished = iFinishedRange[0]; iFinished < iFinishedRange[1]; ++ iFinished ) |
|
{ |
|
InfoText &txtFinished = o.m_arrTxt[ iFinished ]; |
|
|
|
if ( txtFinished.m_uiFlags & F_SEEN_THIS_FRAME ) |
|
txtFinished.m_uiFlags |= F_SEEN_LAST_FRAME; |
|
|
|
txtFinished.m_uiFlags &= ~F_SEEN_THIS_FRAME; |
|
|
|
txtFinished.m_flTimeToLive -= gpGlobals->frametime; |
|
txtFinished.m_flTimeAlive += gpGlobals->frametime; |
|
|
|
if ( txtFinished.m_flTimeToLive >= 0.0f ) |
|
m_arrTxt.AddToTail( txtFinished ); |
|
} |
|
} |
|
|
|
if ( x ) |
|
{ |
|
// Now add it to the array |
|
x->m_flTimeToLive = ui_posedebug_fade_out_time.GetFloat(); |
|
m_arrTxt.AddToTail( *x ); |
|
} |
|
} |
|
|
|
ModelPoseDebugInfo::InfoText * ModelPoseDebugInfo::LookupInfoText( InfoText *x ) |
|
{ |
|
int k = m_iCurrentText; |
|
if ( x ) |
|
{ |
|
for ( ; k < m_arrTxt.Count(); ++ k ) |
|
{ |
|
InfoText &txt = m_arrTxt[k]; |
|
if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) && |
|
!stricmp( x->m_chActivity, txt.m_chActivity ) && |
|
!stricmp( x->m_chLabel, txt.m_chLabel ) && |
|
( x->m_iActivity == txt.m_iActivity ) ) |
|
{ |
|
return &txt; |
|
} |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
void ModelPoseDebugInfo::PrintPendingInfoText( int &rnPosPrint ) |
|
{ |
|
con_nprint_s nxPrn = { 0 }; |
|
nxPrn.time_to_live = -1; |
|
nxPrn.color[0] = 1.0f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 1.0f; |
|
nxPrn.fixed_width_font = true; |
|
|
|
float const flFadeInTime = ui_posedebug_fade_in_time.GetFloat(); |
|
float const flFadeOutTime = ui_posedebug_fade_out_time.GetFloat(); |
|
|
|
// Now print all the accumulated spew |
|
for ( int k = m_iCurrentText; k < m_arrTxt.Count(); ++ k ) |
|
{ |
|
InfoText &prntxt = m_arrTxt[k]; |
|
|
|
switch( prntxt.m_uiFlags & ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) ) |
|
{ |
|
case ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) : |
|
nxPrn.color[0] = 1.f; |
|
nxPrn.color[1] = 1.f; |
|
nxPrn.color[2] = 1.f; |
|
if ( prntxt.m_flTimeAlive > flFadeInTime ) |
|
break; |
|
else |
|
NULL; // Fall-through to keep showing in green |
|
case F_SEEN_THIS_FRAME : |
|
if ( flFadeInTime > 0.f ) |
|
{ |
|
nxPrn.color[0] = 1.f * prntxt.m_flTimeAlive / flFadeInTime; |
|
nxPrn.color[1] = 1.f; |
|
nxPrn.color[2] = 1.f * prntxt.m_flTimeAlive / flFadeInTime; |
|
} |
|
else |
|
{ |
|
nxPrn.color[0] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f; |
|
nxPrn.color[1] = 1.f; |
|
nxPrn.color[2] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f; |
|
} |
|
break; |
|
case F_SEEN_LAST_FRAME : |
|
case 0: |
|
if ( flFadeOutTime > 0.f ) |
|
nxPrn.color[0] = 1.f * prntxt.m_flTimeToLive / flFadeOutTime; |
|
else |
|
nxPrn.color[0] = ( prntxt.m_flTimeToLive > 0.0f ) ? 1.f : 0.f; |
|
nxPrn.color[1] = 0.f; |
|
nxPrn.color[2] = 0.f; |
|
break; |
|
} |
|
|
|
nxPrn.index = ( rnPosPrint += 1 ); |
|
engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[0] ); |
|
|
|
for ( int iLine = 1; iLine < ModelPoseDebugInfo::InfoText::MAX_TEXT_LINES; ++ iLine) |
|
{ |
|
if ( !prntxt.m_chTextLines[iLine][0] ) |
|
break; |
|
|
|
nxPrn.index = ( rnPosPrint += 1 ); |
|
engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[iLine] ); |
|
} |
|
} |
|
|
|
m_iCurrentText = m_arrTxt.Count(); |
|
} |
|
|
|
|
|
|
|
class CPoseDebuggerImpl : public IPoseDebugger |
|
{ |
|
public: |
|
CPoseDebuggerImpl(); |
|
~CPoseDebuggerImpl(); |
|
|
|
public: |
|
void ShowAllModels( bool bShow ); |
|
void ShowModel( int iEntNum, bool bShow ); |
|
bool IsModelShown( int iEntNum ) const; |
|
|
|
public: |
|
virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ); |
|
virtual void AccumulatePose( |
|
const CStudioHdr *pStudioHdr, |
|
CIKContext *pIKContext, |
|
Vector pos[], |
|
Quaternion q[], |
|
int sequence, |
|
float cycle, |
|
const float poseParameter[], |
|
int boneMask, |
|
float flWeight, |
|
float flTime |
|
); |
|
|
|
protected: |
|
typedef CUtlMap< CStudioHdr const *, ModelPoseDebugInfo > MapModel; |
|
MapModel m_mapModel, m_mapModelOld; |
|
int m_nPosPrint; |
|
|
|
CBitVec< MAX_EDICTS > m_uiMaskShowModels; |
|
|
|
CStudioHdr const *m_pLastModel; |
|
}; |
|
|
|
static CPoseDebuggerImpl s_PoseDebuggerImpl; |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// |
|
// CPoseDebuggerImpl |
|
// Implementation |
|
// |
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
CPoseDebuggerImpl::CPoseDebuggerImpl() : |
|
m_mapModel( DefLessFunc( CStudioHdr const * ) ), |
|
m_mapModelOld( DefLessFunc( CStudioHdr const * ) ), |
|
m_nPosPrint( 0 ), |
|
m_pLastModel( NULL ) |
|
{ |
|
m_uiMaskShowModels.SetAll(); |
|
} |
|
|
|
CPoseDebuggerImpl::~CPoseDebuggerImpl() |
|
{ |
|
// g_pPoseDebugger = &s_PoseDebuggerStub; |
|
} |
|
|
|
void CPoseDebuggerImpl::ShowAllModels( bool bShow ) |
|
{ |
|
bShow ? m_uiMaskShowModels.SetAll() : m_uiMaskShowModels.ClearAll(); |
|
} |
|
|
|
void CPoseDebuggerImpl::ShowModel( int iEntNum, bool bShow ) |
|
{ |
|
Assert( iEntNum >= 0 && iEntNum < MAX_EDICTS ); |
|
if ( iEntNum >= 0 && iEntNum < MAX_EDICTS ) |
|
m_uiMaskShowModels.Set( iEntNum, bShow ); |
|
} |
|
|
|
bool CPoseDebuggerImpl::IsModelShown( int iEntNum ) const |
|
{ |
|
Assert( iEntNum >= 0 && iEntNum < MAX_EDICTS ); |
|
if ( iEntNum >= 0 && iEntNum < MAX_EDICTS ) |
|
return m_uiMaskShowModels.IsBitSet( iEntNum ); |
|
else |
|
return false; |
|
} |
|
|
|
void CPoseDebuggerImpl::StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) |
|
{ |
|
// virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel(); |
|
// if ( !pVMdl ) |
|
// return; |
|
|
|
// If we are starting a new model then finalize the previous one |
|
if ( pStudioHdr != m_pLastModel && m_pLastModel ) |
|
{ |
|
MapModel::IndexType_t idx = m_mapModel.Find( m_pLastModel ); |
|
if ( idx != m_mapModel.InvalidIndex() ) |
|
{ |
|
ModelPoseDebugInfo &mpi = m_mapModel.Element( idx ); |
|
ModelPoseDebugInfo *pMpiOld = NULL; |
|
MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( m_pLastModel ); |
|
if ( idxMapModelOld != m_mapModelOld.InvalidIndex() ) |
|
{ |
|
pMpiOld = &m_mapModelOld.Element( idxMapModelOld ); |
|
} |
|
mpi.AddInfoText( NULL, pMpiOld ); |
|
mpi.PrintPendingInfoText( m_nPosPrint ); |
|
} |
|
} |
|
m_pLastModel = pStudioHdr; |
|
|
|
// Go ahead with the new model |
|
studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr(); |
|
if ( !pRMdl || |
|
!pRMdl->numincludemodels ) |
|
return; |
|
|
|
// Entity number |
|
int iEntNum = pEntity->entindex(); |
|
if ( !IsModelShown( iEntNum ) ) |
|
return; |
|
|
|
// Check if we saw the model |
|
if ( m_mapModel.Find( pStudioHdr ) != m_mapModel.InvalidIndex() ) |
|
{ |
|
// Initialize the printing position |
|
m_nPosPrint = 9; |
|
|
|
// Swap the maps |
|
m_mapModelOld.RemoveAll(); |
|
m_mapModelOld.Swap( m_mapModel ); |
|
|
|
// Zero out the text on the old map |
|
for ( int k = m_mapModelOld.FirstInorder(); |
|
k != m_mapModelOld.InvalidIndex(); |
|
k = m_mapModelOld.NextInorder( k ) ) |
|
{ |
|
ModelPoseDebugInfo &mpi = m_mapModelOld[k]; |
|
mpi.m_iCurrentText = 0; |
|
} |
|
} |
|
else |
|
{ |
|
// Next model |
|
m_nPosPrint += 3; |
|
} |
|
|
|
ModelPoseDebugInfo mpi; |
|
mpi.m_iEntNum = iEntNum; |
|
m_mapModel.Insert( pStudioHdr, mpi ); |
|
|
|
con_nprint_s nxPrn = { 0 }; |
|
nxPrn.index = m_nPosPrint; |
|
nxPrn.time_to_live = -1; |
|
nxPrn.color[0] = 0.9f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 0.9f; |
|
nxPrn.fixed_width_font = false; |
|
|
|
engine->Con_NXPrintf( &nxPrn, "[ %2d ] Model: %s", iEntNum, pRMdl->pszName() ); |
|
m_nPosPrint += 3; |
|
} |
|
|
|
void CPoseDebuggerImpl::AccumulatePose( const CStudioHdr *pStudioHdr, CIKContext *pIKContext, |
|
Vector pos[], Quaternion q[], int sequence, float cycle, |
|
const float poseParameter[], int boneMask, |
|
float flWeight, float flTime ) |
|
{ |
|
// virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel(); |
|
// if ( !pVMdl ) |
|
// return; |
|
|
|
studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr(); |
|
if ( !pRMdl || |
|
!pRMdl->numincludemodels ) |
|
return; |
|
|
|
MapModel::IndexType_t idxMapModel = m_mapModel.Find( pStudioHdr ); |
|
if ( idxMapModel == m_mapModel.InvalidIndex() ) |
|
return; |
|
|
|
ModelPoseDebugInfo &mpi = m_mapModel.Element( idxMapModel ); |
|
if ( !IsModelShown( mpi.m_iEntNum ) ) |
|
return; |
|
|
|
ModelPoseDebugInfo *pMpiOld = NULL; |
|
MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( pStudioHdr ); |
|
if ( idxMapModelOld != m_mapModelOld.InvalidIndex() ) |
|
{ |
|
pMpiOld = &m_mapModelOld.Element( idxMapModelOld ); |
|
} |
|
|
|
|
|
// |
|
// Actual processing |
|
// |
|
|
|
mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence ); |
|
|
|
if ( sequence >= pStudioHdr->GetNumSeq() ) |
|
{ |
|
sequence = 0; |
|
seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence ); |
|
} |
|
|
|
enum |
|
{ |
|
widthActivity = 35, |
|
widthLayer = 35, |
|
widthIks = 60, |
|
widthPercent = 6, |
|
}; |
|
|
|
// Prepare the text |
|
char chBuffer[256]; |
|
ModelPoseDebugInfo::InfoText txt; |
|
int numLines = 0; |
|
|
|
txt.m_iActivity = seqdesc.activity; |
|
sprintf( txt.m_chActivity, "%s", seqdesc.pszActivityName() ); |
|
sprintf( txt.m_chLabel, "%s", seqdesc.pszLabel() ); |
|
|
|
if ( !txt.m_chActivity[0] ) |
|
{ |
|
// Try to find the last seen activity and re-use it |
|
for ( int iLast = mpi.m_arrTxt.Count(); iLast --> 0; ) |
|
{ |
|
ModelPoseDebugInfo::InfoText &lastSeenTxt = mpi.m_arrTxt[iLast]; |
|
if ( lastSeenTxt.m_uiFlags & ModelPoseDebugInfo::F_SEEN_THIS_FRAME && |
|
lastSeenTxt.m_chActivity[0] ) |
|
{ |
|
sprintf( txt.m_chActivity, "%s", lastSeenTxt.m_chActivity ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// The layer information |
|
ModelPoseDebugInfo::InfoText *pOldTxt = pMpiOld ? pMpiOld->LookupInfoText( &txt ) : NULL; |
|
sprintf( txt.m_chTextLines[numLines], |
|
"%-*s %-*s %*.2f %*.1f/%-*d %*.0f%% ", |
|
widthActivity, |
|
seqdesc.pszActivityName(), |
|
widthLayer, |
|
seqdesc.pszLabel(), |
|
7, |
|
pOldTxt ? pOldTxt->m_flTimeAlive : 0.f, |
|
5, |
|
cycle * ( ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes - 1 ), |
|
3, |
|
((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes, |
|
widthPercent, |
|
flWeight * 100.0f |
|
); |
|
++ numLines; |
|
|
|
if ( seqdesc.numiklocks ) |
|
{ |
|
sprintf( chBuffer, |
|
"iklocks : %-2d : ", |
|
seqdesc.numiklocks ); |
|
|
|
for ( int k = 0; k < seqdesc.numiklocks; ++ k ) |
|
{ |
|
mstudioiklock_t *plock = seqdesc.pIKLock( k ); |
|
mstudioikchain_t *pchain = pStudioHdr->pIKChain( plock->chain ); |
|
|
|
sprintf( chBuffer + strlen( chBuffer ), "%s ", pchain->pszName() ); |
|
// plock->flPosWeight; |
|
// plock->flLocalQWeight; |
|
} |
|
|
|
sprintf( txt.m_chTextLines[numLines], |
|
"%-*s", |
|
widthIks, |
|
chBuffer |
|
); |
|
++ numLines; |
|
} |
|
|
|
if ( seqdesc.numikrules ) |
|
{ |
|
sprintf( chBuffer, "ikrules : %-2d", |
|
seqdesc.numikrules ); |
|
|
|
sprintf( txt.m_chTextLines[numLines], |
|
"%-*s", |
|
widthIks, |
|
chBuffer |
|
); |
|
++ numLines; |
|
} |
|
|
|
|
|
// Now add the accumulated text into the container |
|
mpi.AddInfoText( &txt, pMpiOld ); |
|
mpi.PrintPendingInfoText( m_nPosPrint ); |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Con-commands |
|
// |
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
static void IN_PoseDebuggerStart( const CCommand &args ) |
|
{ |
|
if ( args.ArgC() <= 1 ) |
|
{ |
|
// No args, enable all |
|
s_PoseDebuggerImpl.ShowAllModels( true ); |
|
} |
|
else |
|
{ |
|
// If explicitly showing the pose debugger when it was disabled |
|
if ( g_pPoseDebugger != &s_PoseDebuggerImpl ) |
|
{ |
|
s_PoseDebuggerImpl.ShowAllModels( false ); |
|
} |
|
|
|
// Show only specific models |
|
for ( int k = 1; k < args.ArgC(); ++ k ) |
|
{ |
|
int iEntNum = atoi( args.Arg( k ) ); |
|
s_PoseDebuggerImpl.ShowModel( iEntNum, true ); |
|
} |
|
} |
|
|
|
g_pPoseDebugger = &s_PoseDebuggerImpl; |
|
} |
|
|
|
static void IN_PoseDebuggerEnd( const CCommand &args ) |
|
{ |
|
if ( args.ArgC() <= 1 ) |
|
{ |
|
// No args, disable all |
|
s_PoseDebuggerImpl.ShowAllModels( false ); |
|
|
|
// Set the stub pointer |
|
g_pPoseDebugger = &s_PoseDebuggerStub; |
|
} |
|
else |
|
{ |
|
// Hide only specific models |
|
for ( int k = 1; k < args.ArgC(); ++ k ) |
|
{ |
|
int iEntNum = atoi( args.Arg( k ) ); |
|
s_PoseDebuggerImpl.ShowModel( iEntNum, false ); |
|
} |
|
} |
|
} |
|
|
|
static ConCommand posedebuggerstart( "+posedebug", IN_PoseDebuggerStart, "Turn on pose debugger or add ents to pose debugger UI", FCVAR_CHEAT ); |
|
static ConCommand posedebuggerend ( "-posedebug", IN_PoseDebuggerEnd, "Turn off pose debugger or hide ents from pose debugger UI", FCVAR_CHEAT );
|
|
|