Added bone merge; still a lot to fix

This commit is contained in:
Kamay Xutax 2024-08-21 05:04:20 +02:00
parent 9b270322f7
commit 95146c3867
18 changed files with 333 additions and 434 deletions

View file

@ -12,6 +12,7 @@
#include "convar.h"
#include "iconvar.h"
#include "interpolatedvar.h"
#include "mathlib/mathlib.h"
#include "model_types.h"
#include "bone_setup.h"
#include "ivrenderview.h"
@ -1498,7 +1499,7 @@ void C_BaseAnimating::BuildTransformations( CStudioHdr *hdr, Vector *pos, Quater
m_pBoneMergeCache = new CBoneMergeCache;
m_pBoneMergeCache->Init( this );
}
m_pBoneMergeCache->MergeMatchingBones( boneMask );
m_pBoneMergeCache->MergeMatchingBones( boneMask, m_BoneAccessor.GetBoneArrayForWrite() );
}
else
{
@ -1890,7 +1891,7 @@ void C_BaseAnimating::AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Qua
void C_BaseAnimating::ChildLayerBlend( Vector pos[], Quaternion q[], float currentTime, int boneMask )
{
return;
return;
Vector childPos[MAXSTUDIOBONES];
Quaternion childQ[MAXSTUDIOBONES];
@ -2743,82 +2744,6 @@ void C_BaseAnimating::ThreadedBoneSetup()
g_PreviousBoneSetups.RemoveAll();
}
void C_BaseAnimating::BuildMatricesWithBoneMerge(
const CStudioHdr *pStudioHdr,
const QAngle& angles,
const Vector& origin,
const Vector pos[MAXSTUDIOBONES],
const Quaternion q[MAXSTUDIOBONES],
matrix3x4_t bonetoworld[MAXSTUDIOBONES],
C_BaseAnimating *pParent,
CBoneCache *pParentCache
)
{
CStudioHdr *fhdr = pParent->GetModelPtr();
mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
matrix3x4_t rotationmatrix; // model to world transformation
AngleMatrix( angles, origin, rotationmatrix);
for ( int i=0; i < pStudioHdr->numbones(); i++ )
{
// Now find the bone in the parent entity.
bool merged = false;
int parentBoneIndex = Studio_BoneIndexByName( fhdr, pbones[i].pszName() );
if ( parentBoneIndex >= 0 )
{
matrix3x4_t *pMat = pParentCache->GetCachedBone( parentBoneIndex );
if ( pMat )
{
MatrixCopy( *pMat, bonetoworld[ i ] );
merged = true;
}
}
if ( !merged )
{
// If we get down here, then the bone wasn't merged.
matrix3x4_t bonematrix;
QuaternionMatrix( q[i], pos[i], bonematrix );
if (pbones[i].parent == -1)
{
ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]);
}
else
{
ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]);
}
}
}
}
void C_BaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask, float currentTime )
{
if(!pStudioHdr)
{
Assert(!"C_BaseAnimating::GetSkeleton() without a model");
return;
}
IBoneSetup boneSetup( pStudioHdr, boneMask, m_flPoseParameter );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, currentTime, m_pIk );
if ( m_pIk )
{
CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetRenderAngles(), GetRenderOrigin(), currentTime, 0, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, currentTime, &auto_ik );
}
else
{
boneSetup.CalcAutoplaySequences( pos, q, currentTime, NULL );
}
boneSetup.CalcBoneAdj( pos, q, m_flEncodedController );
}
bool C_BaseAnimating::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
{
VPROF_BUDGET( "C_BaseAnimating::SetupBones", VPROF_BUDGETGROUP_CLIENT_ANIMATION );

View file

@ -144,11 +144,6 @@ public:
virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax );
// model specific
void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles,
const Vector& origin, const Vector pos[MAXSTUDIOBONES],
const Quaternion q[MAXSTUDIOBONES], matrix3x4_t bonetoworld[MAXSTUDIOBONES],
CBaseAnimating *pParent, CBoneCache *pParentCache );
virtual void GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask, float currentTime );
virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime );
virtual void UpdateIKLocks( float currentTime );
virtual void CalculateIKLocks( float currentTime );

View file

@ -370,66 +370,3 @@ CStudioHdr *C_BaseAnimatingOverlay::OnNewModel()
return hdr;
}
void C_BaseAnimatingOverlay::GetSkeleton( CStudioHdr* pStudioHdr, Vector pos[], Quaternion q[], int boneMask, float currentTime )
{
if(!pStudioHdr)
{
Assert(!"C_BaseAnimating::GetSkeleton() without a model");
return;
}
if (!pStudioHdr->SequencesAvailable())
{
return;
}
float poseparameters[MAXSTUDIOPOSEPARAM];
GetPoseParameters(pStudioHdr, poseparameters);
IBoneSetup boneSetup( pStudioHdr, boneMask, poseparameters );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, currentTime, m_pIk );
// sort the layers
int layer[MAX_OVERLAYS] = {};
int i;
for (i = 0; i < m_AnimOverlay.Count(); i++)
{
layer[i] = MAX_OVERLAYS;
}
for (i = 0; i < m_AnimOverlay.Count(); i++)
{
CAnimationLayer &pLayer = m_AnimOverlay[i];
if( (pLayer.m_flWeight > 0) && pLayer.IsActive() && pLayer.m_nOrder >= 0 && pLayer.m_nOrder < m_AnimOverlay.Count())
{
layer[pLayer.m_nOrder] = i;
}
}
for (i = 0; i < m_AnimOverlay.Count(); i++)
{
if (layer[i] >= 0 && layer[i] < m_AnimOverlay.Count())
{
CAnimationLayer &pLayer = m_AnimOverlay[layer[i]];
// UNDONE: Is it correct to use overlay weight for IK too?
boneSetup.AccumulatePose( pos, q, pLayer.m_nSequence, pLayer.m_flCycle, pLayer.m_flWeight, currentTime, m_pIk );
}
}
if ( m_pIk )
{
CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetRenderAngles(), GetRenderOrigin(), currentTime, 0, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, currentTime, &auto_ik );
}
else
{
boneSetup.CalcAutoplaySequences(pos, q, currentTime, NULL);
}
float controllers[MAXSTUDIOBONECTRLS];
GetBoneControllers(controllers);
boneSetup.CalcBoneAdj( pos, q, controllers );
}

View file

@ -38,8 +38,6 @@ public:
// model specific
virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime );
virtual void DoAnimationEvents( CStudioHdr *pStudioHdr );
virtual void GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask, float currentTime );
enum
{
MAX_OVERLAYS = 15,

View file

@ -204,7 +204,7 @@ $Project
$File "beamdraw.cpp"
$File "$SRCDIR\game\shared\beam_shared.cpp"
$File "$SRCDIR\public\bone_accessor.cpp"
$File "bone_merge_cache.cpp"
$File "$SRCDIR\game\shared\bone_merge_cache.cpp"
$File "c_ai_basehumanoid.cpp"
$File "c_ai_basenpc.cpp"
$File "c_baseanimating.cpp"
@ -674,7 +674,7 @@ $Project
$File "baseanimatedtextureproxy.h"
$File "baseclientrendertargets.h"
$File "beamdraw.h"
$File "bone_merge_cache.h"
$File "$SRCDIR\game\shared\bone_merge_cache.h"
$File "c_ai_basenpc.h"
$File "c_baseanimating.h"
$File "c_baseanimatingoverlay.h"

View file

@ -2598,103 +2598,12 @@ float C_CSPlayer::GetDeathCamInterpolationTime()
return spec_freeze_time.GetFloat();
else
return CS_DEATH_ANIMATION_TIME;
}
ConVar cl_server_setup_bones("cl_server_setup_bones", "1");
bool C_CSPlayer::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
{
if (cl_server_setup_bones.GetBool())
{
AUTO_LOCK( m_BoneSetupLock );
MDLCACHE_CRITICAL_SECTION();
Assert( GetModelPtr() );
CStudioHdr *pStudioHdr = GetModelPtr( );
if(!pStudioHdr)
{
Assert(!"C_BaseAnimating::GetSkeleton() without a model");
return false;
}
Assert( !IsEFlagSet( EFL_SETTING_UP_BONES ) );
AddEFlags( EFL_SETTING_UP_BONES );
Vector pos[MAXSTUDIOBONES];
Quaternion q[MAXSTUDIOBONES];
// adjust hit boxes based on IK driven offset
Vector adjOrigin = GetRenderOrigin();
if ( m_pIk )
{
// FIXME: pass this into Studio_BuildMatrices to skip transforms
CBoneBitList boneComputed;
m_iIKCounter++;
m_pIk->Init( pStudioHdr, GetRenderAngles(), adjOrigin, currentTime, m_iIKCounter, boneMask );
GetSkeleton( pStudioHdr, pos, q, boneMask, currentTime );
m_pIk->UpdateTargets( pos, q, m_BoneAccessor.GetBoneArrayForWrite(), boneComputed );
CalculateIKLocks( currentTime );
m_pIk->SolveDependencies( pos, q, m_BoneAccessor.GetBoneArrayForWrite(), boneComputed );
}
else
{
// Msg( "%.03f : %s:%s\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
GetSkeleton( pStudioHdr, pos, q, boneMask, currentTime );
}
CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
if ( pParent )
{
// We're doing bone merging, so do special stuff here.
CBoneCache *pParentCache = pParent->GetBoneCache(pParent->GetModelPtr());
if ( pParentCache )
{
BuildMatricesWithBoneMerge(
pStudioHdr,
GetRenderAngles(),
adjOrigin,
pos,
q,
m_BoneAccessor.GetBoneArrayForWrite(),
pParent,
pParentCache );
RemoveEFlags( EFL_SETTING_UP_BONES );
return true;
}
}
Studio_BuildMatrices(
pStudioHdr,
GetRenderAngles(),
adjOrigin,
pos,
q,
-1,
GetModelScale(), // Scaling
m_BoneAccessor.GetBoneArrayForWrite(),
boneMask );
RemoveEFlags(EFL_SETTING_UP_BONES);
if ( pBoneToWorldOut )
{
memcpy( pBoneToWorldOut, m_BoneAccessor.GetBoneArrayForWrite(), sizeof( matrix3x4_t ) * MAXSTUDIOBONES );
}
return true;
}
else
{
return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );
}
return BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );
}
//=============================================================================

View file

@ -447,7 +447,12 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
return;
}
IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() );
float flPoseParams[MAXSTUDIOPOSEPARAM];
float flEncodedParams[MAXSTUDIOBONECTRLS];
GetPoseParameters( pStudioHdr, flPoseParams );
IBoneSetup boneSetup( pStudioHdr, boneMask, flPoseParams );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, gpGlobals->curtime, m_pIk );
@ -486,12 +491,12 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
else
{
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, NULL );
}
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
}
GetEncodedControllers( pStudioHdr, flEncodedParams );
boneSetup.CalcBoneAdj( pos, q, flEncodedParams );
}
//-----------------------------------------------------------------------------
// Purpose: zero's out all non-restore safe fields
// Output :

View file

@ -14,6 +14,8 @@
#include "enginecallback.h"
#include "entitylist_base.h"
#include "mathlib/vector.h"
#include "mathlib/vmatrix.h"
#include "player.h"
#include "shareddefs.h"
#include "studio.h"
#include "bone_setup.h"
@ -289,6 +291,8 @@ CBaseAnimating::CBaseAnimating()
m_fadeMaxDist = 0;
m_flFadeScale = 0.0f;
m_fBoneCacheFlags = 0;
m_pBoneMergeCache = NULL;
m_pBoneCache = NULL;
}
CBaseAnimating::~CBaseAnimating()
@ -1693,6 +1697,32 @@ void CBaseAnimating::Teleport( const Vector *newPosition, const QAngle *newAngle
}
void CBaseAnimating::GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM])
{
if ( !pStudioHdr )
return;
// interpolate pose parameters
int i;
for( i=0; i < pStudioHdr->GetNumPoseParameters(); i++)
{
poseParameter[i] = m_flPoseParameter[i];
}
}
void CBaseAnimating::GetEncodedControllers(CStudioHdr* pStudioHdr, float encodedControllers[MAXSTUDIOBONECTRLS])
{
if ( !pStudioHdr )
return;
// interpolate pose parameters
int i;
for( i=0; i < pStudioHdr->GetNumPoseParameters(); i++)
{
encodedControllers[i] = m_flEncodedController[i];
}
}
//-----------------------------------------------------------------------------
// Purpose: build matrices first from the parent, then from the passed in arrays if the bone doesn't exist on the parent
//-----------------------------------------------------------------------------
@ -1704,45 +1734,56 @@ void CBaseAnimating::BuildMatricesWithBoneMerge(
const Vector pos[MAXSTUDIOBONES],
const Quaternion q[MAXSTUDIOBONES],
matrix3x4_t bonetoworld[MAXSTUDIOBONES],
CBaseAnimating *pParent,
CBoneCache *pParentCache
int boneMask
)
{
CStudioHdr *fhdr = pParent->GetModelPtr();
mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
matrix3x4_t rotationmatrix; // model to world transformation
AngleMatrix( angles, origin, rotationmatrix);
for ( int i=0; i < pStudioHdr->numbones(); i++ )
bool boneMerge = IsEffectActive(EF_BONEMERGE);
if ( boneMerge || m_pBoneMergeCache )
{
// Now find the bone in the parent entity.
bool merged = false;
int parentBoneIndex = Studio_BoneIndexByName( fhdr, pbones[i].pszName() );
if ( parentBoneIndex >= 0 )
if ( boneMerge )
{
matrix3x4_t *pMat = pParentCache->GetCachedBone( parentBoneIndex );
if ( pMat )
if ( !m_pBoneMergeCache )
{
MatrixCopy( *pMat, bonetoworld[ i ] );
merged = true;
m_pBoneMergeCache = new CBoneMergeCache;
m_pBoneMergeCache->Init( this );
}
m_pBoneMergeCache->MergeMatchingBones( boneMask, bonetoworld );
}
if ( !merged )
else
{
// If we get down here, then the bone wasn't merged.
matrix3x4_t bonematrix;
QuaternionMatrix( q[i], pos[i], bonematrix );
delete m_pBoneMergeCache;
m_pBoneMergeCache = NULL;
}
}
for ( int i=0; i < pStudioHdr->numbones(); i++ )
{
if (!(pStudioHdr->boneFlags(i) & boneMask))
{
continue;
}
if (m_pBoneMergeCache && m_pBoneMergeCache->IsBoneMerged(i))
{
continue;
}
if (pbones[i].parent == -1)
{
ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]);
}
else
{
ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]);
}
// If we get down here, then the bone wasn't merged.
matrix3x4_t bonematrix;
QuaternionMatrix( q[i], pos[i], bonematrix );
if (pbones[i].parent == -1)
{
ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]);
}
else
{
ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]);
}
}
}
@ -1772,24 +1813,56 @@ inline bool CBaseAnimating::CanSkipAnimation( void )
}
void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
void CBaseAnimating::SetupBones( CStudioHdr* pStudioHdr, matrix3x4_t *pBoneToWorld, int boneMask )
{
static constexpr auto flDebugDuration = 60.f;
AUTO_LOCK( m_BoneSetupMutex );
VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
MDLCACHE_CRITICAL_SECTION();
Assert( GetModelPtr() );
m_pBoneCache = Studio_GetBoneCache(m_boneCacheHandle);
CStudioHdr *pStudioHdr = GetModelPtr( );
if ( m_pBoneCache )
{
if ( m_pBoneCache->IsValid( gpGlobals->curtime ) && (m_pBoneCache->m_boneMask & boneMask) == boneMask && m_pBoneCache->m_timeValid <= gpGlobals->curtime)
{
// Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid );
// in memory and still valid, use it!
m_pBoneCache->ReadCachedBones(pBoneToWorld);
return;
}
// in memory, but missing some of the bone masks
if ( (m_pBoneCache->m_boneMask & boneMask) != boneMask )
{
Studio_DestroyBoneCache( m_boneCacheHandle );
m_boneCacheHandle = 0;
m_pBoneCache = NULL;
}
}
if ( !m_pBoneCache )
{
bonecacheparams_t params;
params.pStudioHdr = pStudioHdr;
params.pBoneToWorld = pBoneToWorld;
params.curtime = gpGlobals->curtime;
params.boneMask = boneMask;
m_boneCacheHandle = Studio_CreateBoneCache(params);
m_pBoneCache = Studio_GetBoneCache(m_boneCacheHandle);
}
Assert( pStudioHdr );
if(!pStudioHdr)
{
Assert(!"CBaseAnimating::GetSkeleton() without a model");
return;
}
Assert( !IsEFlagSet( EFL_SETTING_UP_BONES ) );
AddEFlags( EFL_SETTING_UP_BONES );
@ -1797,8 +1870,8 @@ void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
Vector pos[MAXSTUDIOBONES];
Quaternion q[MAXSTUDIOBONES];
// adjust hit boxes based on IK driven offset
Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
// Remove IK to respect more the client hitboxes.
Vector adjOrigin = GetAbsOrigin();
if ( CanSkipAnimation() )
{
@ -1825,49 +1898,16 @@ void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
// Msg( "%.03f : %s:%s\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
GetSkeleton( pStudioHdr, pos, q, boneMask );
}
}
CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
if ( pParent )
{
// We're doing bone merging, so do special stuff here.
CBoneCache *pParentCache = pParent->GetBoneCache();
if ( pParentCache )
{
BuildMatricesWithBoneMerge(
pStudioHdr,
GetAbsAngles(),
adjOrigin,
pos,
q,
pBoneToWorld,
pParent,
pParentCache );
RemoveEFlags( EFL_SETTING_UP_BONES );
if (ai_setupbones_debug.GetBool())
{
DrawRawSkeleton( pBoneToWorld, boneMask, true, 0.11 );
}
return;
}
}
}
Studio_BuildMatrices(
pStudioHdr,
GetAbsAngles(),
adjOrigin,
pos,
q,
-1,
GetModelScale(), // Scaling
pBoneToWorld,
boneMask );
BuildMatricesWithBoneMerge(pStudioHdr, GetAbsAngles(), adjOrigin, pos, q, pBoneToWorld, boneMask);
m_pBoneCache->UpdateBones(pBoneToWorld, pStudioHdr->numbones(), gpGlobals->curtime);
if (ai_setupbones_debug.GetBool())
{
// Msg("%s:%s:%s (%x)\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask );
DrawRawSkeleton( pBoneToWorld, boneMask, true, 0.11 );
DrawRawSkeleton( pBoneToWorld, boneMask, true, flDebugDuration );
}
RemoveEFlags( EFL_SETTING_UP_BONES );
}
@ -2588,51 +2628,33 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
CStudioHdr *pStudioHdr = GetModelPtr( );
Assert(pStudioHdr);
CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle );
int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT;
int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT | BONE_USED_BY_BONE_MERGE;
// TF queries these bones to position weapons when players are killed
#if defined( TF_DLL )
boneMask |= BONE_USED_BY_BONE_MERGE;
#endif
if ( pcache )
{
if ( pcache->IsValid( gpGlobals->curtime ) && (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid <= gpGlobals->curtime)
{
// Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid );
// in memory and still valid, use it!
return pcache;
}
// in memory, but missing some of the bone masks
if ( (pcache->m_boneMask & boneMask) != boneMask )
{
Studio_DestroyBoneCache( m_boneCacheHandle );
m_boneCacheHandle = 0;
pcache = NULL;
}
}
matrix3x4_t bonetoworld[MAXSTUDIOBONES];
SetupBones( bonetoworld, boneMask );
SetupBones( GetModelPtr(), bonetoworld, boneMask );
if ( pcache )
{
// still in memory but out of date, refresh the bones.
pcache->UpdateBones( bonetoworld, pStudioHdr->numbones(), gpGlobals->curtime );
}
else
{
bonecacheparams_t params;
params.pStudioHdr = pStudioHdr;
params.pBoneToWorld = bonetoworld;
params.curtime = gpGlobals->curtime;
params.boneMask = boneMask;
for (auto pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer())
{
auto pChildAnimating = dynamic_cast<CBaseAnimating*>(pChild);
m_boneCacheHandle = Studio_CreateBoneCache( params );
pcache = Studio_GetBoneCache( m_boneCacheHandle );
}
Assert(pcache);
return pcache;
if (!pChildAnimating || !pChildAnimating->GetModelPtr())
{
continue;
}
printf("animating: %i %s %s\n", pChildAnimating->entindex(), pChildAnimating->GetDebugName(), pChildAnimating->GetModelName().ToCStr());
matrix3x4_t childbones[MAXSTUDIOBONES];
pChildAnimating->SetupBones(pChildAnimating->GetModelPtr(), childbones, boneMask);
}
Assert(m_pBoneCache);
return m_pBoneCache;
}
@ -2810,7 +2832,12 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
return;
}
IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() );
float flPoseParams[MAXSTUDIOPOSEPARAM];
float flEncodedParams[MAXSTUDIOBONECTRLS];
GetPoseParameters( pStudioHdr, flPoseParams );
IBoneSetup boneSetup( pStudioHdr, boneMask, flPoseParams );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, GetSequence(), GetCycle(), 1.0, gpGlobals->curtime, m_pIk );
@ -2825,7 +2852,8 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
{
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, NULL );
}
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
GetEncodedControllers( pStudioHdr, flEncodedParams );
boneSetup.CalcBoneAdj( pos, q, flEncodedParams );
}
int CBaseAnimating::DrawDebugTextOverlays(void)
@ -3067,8 +3095,14 @@ void CBaseAnimating::DrawRawSkeleton( matrix3x4_t boneToWorld[], int boneMask, b
int i;
int r = 255;
int g = 255;
int b = monocolor ? 255 : 0;
int b = monocolor ? 255 : 0;
if (boneMask & 0x00080000)
{
r = 0;
g = 0;
b = 255;
}
for (i = 0; i < pStudioHdr->numbones(); i++)
{
@ -3080,6 +3114,14 @@ void CBaseAnimating::DrawRawSkeleton( matrix3x4_t boneToWorld[], int boneMask, b
{
Vector p2;
MatrixPosition( boneToWorld[pStudioHdr->pBone( i )->parent], p2 );
if (pStudioHdr->pBone(i)->flags & BONE_USED_BY_BONE_MERGE)
{
r = 255;
}
else
{
r = 0;
}
NDebugOverlay::Line( p1, p2, r, g, b, noDepthTest, duration );
}
}
@ -3605,6 +3647,12 @@ CStudioHdr *CBaseAnimating::OnNewModel()
{
(void) BaseClass::OnNewModel();
if ( m_pBoneMergeCache )
{
delete m_pBoneMergeCache;
m_pBoneMergeCache = NULL;
}
// TODO: if dynamic, validate m_Sequence and apply queued body group settings?
if ( IsDynamicModelLoading() )
{

View file

@ -18,6 +18,7 @@
#include "studio.h"
#include "datacache/idatacache.h"
#include "tier0/threadtools.h"
#include "bone_merge_cache.h"
class CBasePlayer;
struct animevent_t;
@ -136,7 +137,7 @@ public:
virtual void GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], int boneMask );
virtual void GetBoneTransform( int iBone, matrix3x4_t &pBoneToWorld );
virtual void SetupBones( matrix3x4_t *pBoneToWorld, int boneMask );
virtual void SetupBones( CStudioHdr *pStudioHdr, matrix3x4_t *pBoneToWorld, int boneMask );
virtual void CalculateIKLocks( float currentTime );
virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
@ -160,7 +161,8 @@ public:
bool HasPoseParameter( int iSequence, const char *szName );
bool HasPoseParameter( int iSequence, int iParameter );
float EdgeLimitPoseParameter( int iParameter, float flValue, float flBase = 0.0f );
virtual void GetPoseParameters(CStudioHdr* pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM]);
virtual void GetEncodedControllers(CStudioHdr* pStudioHdr, float encodedControllers[MAXSTUDIOBONECTRLS]);
protected:
// The modus operandi for pose parameters is that you should not use the const char * version of the functions
// in general code -- it causes many many string comparisons, which is slower than you think. Better is to
@ -326,8 +328,7 @@ public:
void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles,
const Vector& origin, const Vector pos[MAXSTUDIOBONES],
const Quaternion q[MAXSTUDIOBONES], matrix3x4_t bonetoworld[MAXSTUDIOBONES],
CBaseAnimating *pParent, CBoneCache *pParentCache );
const Quaternion q[MAXSTUDIOBONES], matrix3x4_t bonetoworld[MAXSTUDIOBONES], int boneMask );
void SetFadeDistance( float minFadeDist, float maxFadeDist );
@ -336,10 +337,10 @@ public:
inline void ClearBoneCacheFlags( unsigned short fFlag ) { m_fBoneCacheFlags &= ~fFlag; }
bool PrefetchSequence( int iSequence );
virtual void LockStudioHdr();
virtual void UnlockStudioHdr();
private:
void LockStudioHdr();
void UnlockStudioHdr();
void StudioFrameAdvanceInternal( CStudioHdr *pStudioHdr, float flInterval );
void InputSetLightingOriginRelative( inputdata_t &inputdata );
@ -422,10 +423,12 @@ protected:
public:
COutputEvent m_OnIgnite;
private:
public:
CStudioHdr *m_pStudioHdr;
CThreadFastMutex m_StudioHdrInitLock;
CThreadFastMutex m_BoneSetupMutex;
CBoneCache *m_pBoneCache;
CBoneMergeCache *m_pBoneMergeCache;
// FIXME: necessary so that cyclers can hack m_bSequenceFinished
friend class CFlexCycler;

View file

@ -1100,13 +1100,13 @@ void CNPC_Barnacle::LiftRagdoll( float flBiteZOffset )
// Get the current bone matrix
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), pBoneToWorld, BONE_USED_BY_ANYTHING );
// Apply the forces to the ragdoll
RagdollApplyAnimationAsVelocity( *(m_hRagdoll->GetRagdoll()), m_pRagdollBones, pBoneToWorld, 0.2 );
// Store off the current bone matrix for next time
pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), m_pRagdollBones, BONE_USED_BY_ANYTHING );
}
}
}
@ -1540,7 +1540,7 @@ You can use this stanza to try to counterplace the constraint on the player's he
UpdateTongue();
// Store off the current bone matrix so we have it next frame
pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), m_pRagdollBones, BONE_USED_BY_ANYTHING );
}

View file

@ -174,7 +174,7 @@ void CRagdollProp::Spawn( void )
}
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
BaseClass::SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // FIXME: shouldn't this be a subset of the bones
BaseClass::SetupBones( GetModelPtr(), pBoneToWorld, BONE_USED_BY_ANYTHING ); // FIXME: shouldn't this be a subset of the bones
// this is useless info after the initial conditions are set
SetAbsAngles( vec3_angle );
int collisionGroup = (m_spawnflags & SF_RAGDOLLPROP_DEBRIS) ? COLLISION_GROUP_DEBRIS : COLLISION_GROUP_NONE;
@ -827,7 +827,7 @@ void CRagdollProp::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
// no ragdoll, fall through to base class
if ( !m_ragdoll.listCount )
{
BaseClass::SetupBones( pBoneToWorld, boneMask );
BaseClass::SetupBones( GetModelPtr(), pBoneToWorld, boneMask );
return;
}
@ -1338,10 +1338,10 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con
float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f);
float fCurCycle = pAnimating->GetCycle();
// Get current bones positions
pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), pBoneToWorldNext, BONE_USED_BY_ANYTHING );
// Get previous bones positions
pAnimating->SetCycle( fPreviousCycle );
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), pBoneToWorld, BONE_USED_BY_ANYTHING );
// Restore current cycle
pAnimating->SetCycle( fCurCycle );
@ -1579,7 +1579,7 @@ CRagdollProp *CreateServerRagdollAttached( CBaseAnimating *pAnimating, const Vec
pRagdoll->InitRagdollAnimation();
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
pAnimating->SetupBones( pAnimating->GetModelPtr(), pBoneToWorld, BONE_USED_BY_ANYTHING );
pRagdoll->InitRagdollAttached( pAttached, vecForce, forceBone, pBoneToWorld, pBoneToWorld, 0.1, collisionGroup, pParentEntity, boneAttach, boneOrigin, parentBoneAttach, originAttached );
return pRagdoll;

View file

@ -32,7 +32,7 @@
ConVar sv_unlag( "sv_unlag", "1", FCVAR_DEVELOPMENTONLY, "Enables player lag compensation" );
ConVar sv_maxunlag( "sv_maxunlag", "1.0", FCVAR_DEVELOPMENTONLY, "Maximum lag compensation in seconds", true, 0.0f, true, 1.0f );
ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "1", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" );
ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "0", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" );
ConVar sv_unlag_fixstuck( "sv_unlag_fixstuck", "0", FCVAR_DEVELOPMENTONLY, "Disallow backtracking a player for lag compensation if it will cause them to become stuck" );
//-----------------------------------------------------------------------------

View file

@ -78,6 +78,7 @@ $Project
$Folder "Source Files"
{
$File "$SRCDIR\game\shared\bone_merge_cache.cpp"
$File "$SRCDIR\game\shared\achievement_saverestore.cpp"
$File "$SRCDIR\game\shared\achievement_saverestore.h"
$File "$SRCDIR\game\shared\achievementmgr.cpp"
@ -785,6 +786,7 @@ $Project
$Folder "Header Files"
{
$File "$SRCDIR\game\shared\bone_merge_cache.h"
$File "$SRCDIR\public\mathlib\amd3dx.h"
$File "$SRCDIR\game\shared\ammodef.h"
$File "$SRCDIR\game\shared\base_playeranimstate.h"

View file

@ -15,6 +15,8 @@
// NVNT start extra includes
#include "haptics/haptic_utils.h"
#include "studio.h"
#include "tier3/tier3.h"
#ifdef CLIENT_DLL
#include "prediction.h"
#endif
@ -90,6 +92,7 @@ CBaseCombatWeapon::CBaseCombatWeapon() : BASECOMBATWEAPON_DERIVED_FROM()
#if !defined( CLIENT_DLL )
m_pConstraint = NULL;
OnBaseCombatWeaponCreated( this );
m_pStudioWorldHdr = NULL;
#endif
m_hWeaponFileInfo = GetInvalidWeaponInfoHandle();
@ -117,10 +120,76 @@ CBaseCombatWeapon::~CBaseCombatWeapon( void )
physenv->DestroyConstraint( m_pConstraint );
m_pConstraint = NULL;
}
OnBaseCombatWeaponDestroyed( this );
OnBaseCombatWeaponDestroyed(this);
delete m_pStudioWorldHdr;
#endif
}
#ifndef CLIENT_DLL
void CBaseCombatWeapon::LockStudioHdr()
{
BaseClass::LockStudioHdr();
AUTO_LOCK( m_StudioHdrInitLock );
const auto worldModel = modelinfo->GetModel(m_iWorldModelIndex);
if (worldModel)
{
MDLHandle_t hStudioHdr = modelinfo->GetCacheHandle( worldModel );
if ( hStudioHdr != MDLHANDLE_INVALID )
{
const studiohdr_t *pStudioHdr = mdlcache->LockStudioHdr( hStudioHdr );
CStudioHdr *pStudioHdrContainer = NULL;
if ( !m_pStudioWorldHdr )
{
if ( pStudioHdr )
{
pStudioHdrContainer = new CStudioHdr;
pStudioHdrContainer->Init( pStudioHdr, mdlcache );
}
}
else
{
pStudioHdrContainer = m_pStudioWorldHdr;
}
Assert( ( pStudioHdr == NULL && pStudioHdrContainer == NULL ) || pStudioHdrContainer->GetRenderHdr() == pStudioHdr );
if ( pStudioHdrContainer && pStudioHdrContainer->GetVirtualModel() )
{
MDLHandle_t hVirtualModel = VoidPtrToMDLHandle( pStudioHdrContainer->GetRenderHdr()->VirtualModel() );
mdlcache->LockStudioHdr( hVirtualModel );
}
m_pStudioWorldHdr = pStudioHdrContainer; // must be last to ensure virtual model correctly set up
}
}
}
void CBaseCombatWeapon::UnlockStudioHdr()
{
BaseClass::UnlockStudioHdr();
if ( m_pStudioWorldHdr )
{
const auto worldModel = modelinfo->GetModel(m_iWorldModelIndex);
if (worldModel)
{
mdlcache->UnlockStudioHdr( modelinfo->GetCacheHandle( worldModel ) );
if ( m_pStudioWorldHdr->GetVirtualModel() )
{
MDLHandle_t hVirtualModel = VoidPtrToMDLHandle( m_pStudioWorldHdr->GetRenderHdr()->VirtualModel() );
mdlcache->UnlockStudioHdr( hVirtualModel );
}
}
}
}
void CBaseCombatWeapon::SetupBones(CStudioHdr* pStudioHdr, matrix3x4_t* pBoneToWorld, int boneMask)
{
// hooked m_pStudioHdr to world model instead (m_pStudioWorldHdr)
BaseClass::SetupBones(m_pStudioWorldHdr, pBoneToWorld, boneMask);
}
#endif
void CBaseCombatWeapon::Activate( void )
{
BaseClass::Activate();

View file

@ -6,6 +6,7 @@
#ifndef COMBATWEAPON_SHARED_H
#define COMBATWEAPON_SHARED_H
#include "studio.h"
#ifdef _WIN32
#pragma once
#endif
@ -162,7 +163,11 @@ public:
CBaseCombatWeapon();
virtual ~CBaseCombatWeapon();
#ifndef CLIENT_DLL
virtual void LockStudioHdr();
virtual void UnlockStudioHdr();
virtual void SetupBones( CStudioHdr* pStudioHdr, matrix3x4_t *pBoneToWorld, int boneMask );
#endif
virtual bool IsBaseCombatWeapon( void ) const { return true; }
virtual CBaseCombatWeapon *MyCombatWeaponPointer( void ) { return this; }
@ -612,9 +617,9 @@ private:
bool m_bReloadHudHintDisplayed; // Have we displayed a reload HUD hint since this weapon was deployed?
float m_flHudHintPollTime; // When to poll the weapon again for whether it should display a hud hint.
float m_flHudHintMinDisplayTime; // if the hint is squelched before this, reset my counter so we'll display it again.
// Server only
#if !defined( CLIENT_DLL )
CStudioHdr *m_pStudioWorldHdr;
// Outputs
protected:

View file

@ -11,6 +11,8 @@
#include "bone_setup.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "mathlib/mathlib.h"
#include "studio.h"
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
@ -27,7 +29,7 @@ CBoneMergeCache::CBoneMergeCache()
m_nFollowBoneSetupMask = 0;
}
void CBoneMergeCache::Init( C_BaseAnimating *pOwner )
void CBoneMergeCache::Init( CBaseAnimating *pOwner )
{
m_pOwner = pOwner;
m_pFollow = NULL;
@ -40,7 +42,7 @@ void CBoneMergeCache::Init( C_BaseAnimating *pOwner )
void CBoneMergeCache::UpdateCache()
{
CStudioHdr *pOwnerHdr = m_pOwner ? m_pOwner->GetModelPtr() : NULL;
if ( !pOwnerHdr )
if ( !pOwnerHdr || !m_pOwner->GetFollowedEntity())
{
if ( m_pOwnerHdr )
{
@ -56,7 +58,11 @@ void CBoneMergeCache::UpdateCache()
return;
}
C_BaseAnimating *pTestFollow = m_pOwner->FindFollowedEntity();
#ifdef CLIENT_DLL
C_BaseAnimating* pTestFollow = m_pOwner->FindFollowedEntity();
#else
CBaseAnimating* pTestFollow = dynamic_cast<CBaseAnimating*>(m_pOwner->GetFollowedEntity());
#endif
CStudioHdr *pTestHdr = (pTestFollow ? pTestFollow->GetModelPtr() : NULL);
const studiohdr_t *pTestStudioHDR = (pTestHdr ? pTestHdr->GetRenderHdr() : NULL);
if ( pTestFollow != m_pFollow || pTestHdr != m_pFollowHdr || pTestStudioHDR != m_pFollowRenderHdr || pOwnerHdr != m_pOwnerHdr )
@ -83,7 +89,9 @@ void CBoneMergeCache::UpdateCache()
int parentBoneIndex = Studio_BoneIndexByName( m_pFollowHdr, pOwnerBones[i].pszName() );
if ( parentBoneIndex < 0 )
continue;
#ifdef CLIENT_DLL
printf("bone attach: (%i - %i) (%s - %s) (%s - %s)\n", m_pFollow->entindex(), m_pOwner->entindex(), m_pFollow->GetDebugName(), m_pOwner->GetDebugName(), m_pFollow->GetModelName(), m_pOwner->GetModelName());
#endif
// Add a merged bone here.
CMergedBone mergedBone;
mergedBone.m_iMyBone = i;
@ -122,7 +130,7 @@ void CBoneMergeCache::UpdateCache()
ConVar r_captain_canteen_is_angry ( "r_captain_canteen_is_angry", "1" );
#endif
void CBoneMergeCache::MergeMatchingBones( int boneMask )
void CBoneMergeCache::MergeMatchingBones( int boneMask , matrix3x4_t mergedbones[MAXSTUDIOBONES] )
{
UpdateCache();
@ -130,55 +138,29 @@ void CBoneMergeCache::MergeMatchingBones( int boneMask )
if ( !m_pOwnerHdr || m_MergedBones.Count() == 0 )
return;
// Have the entity we're following setup its bones.
bool bWorked = m_pFollow->SetupBones( NULL, -1, m_nFollowBoneSetupMask, gpGlobals->curtime );
// We suspect there's some cases where SetupBones couldn't do its thing, and then this causes Captain Canteen.
Assert ( bWorked );
if ( !bWorked )
{
// Usually this means your parent is invisible or gone or whatever.
// This routine has no way to tell its caller not to draw itself unfortunately.
// But we can shrink all the bones down to zero size.
// But it might still spawn particle systems? :-(
matrix3x4_t NewBone;
MatrixScaleByZero ( NewBone );
MatrixSetTranslation ( Vector ( 0.0f, 0.0f, 0.0f ), NewBone );
#ifdef STAGING_ONLY
if ( r_captain_canteen_is_angry.GetBool() )
{
// We actually want to see when Captain Canteen happened, and make it really obvious that (a) he was here and (b) this code would have fixed him.
float HowAngry = 20.0f; // Leon's getting larger!
MatrixSetColumn ( Vector ( HowAngry, 0.0f, 0.0f ), 0, NewBone );
MatrixSetColumn ( Vector ( 0.0f, HowAngry, 0.0f ), 1, NewBone );
MatrixSetColumn ( Vector ( 0.0f, 0.0f, HowAngry ), 2, NewBone );
}
matrix3x4_t bones[MAXSTUDIOBONES];
// Have the entity we're following setup its bones.
#ifdef CLIENT_DLL
m_pFollow->SetupBones(bones, MAXSTUDIOBONES, m_nFollowBoneSetupMask, gpGlobals->curtime);
#else
m_pFollow->SetupBones(m_pFollow->GetModelPtr(), bones, m_nFollowBoneSetupMask);
#endif
for ( int i=0; i < m_MergedBones.Count(); i++ )
{
int iOwnerBone = m_MergedBones[i].m_iMyBone;
// Only update bones reference by the bone mask.
if ( !( m_pOwnerHdr->boneFlags( iOwnerBone ) & boneMask ) )
continue;
m_pOwner->GetBoneForWrite( iOwnerBone ) = NewBone;
}
}
else
// Now copy the bone matrices.
for ( int i=0; i < m_MergedBones.Count(); i++ )
{
// Now copy the bone matrices.
for ( int i=0; i < m_MergedBones.Count(); i++ )
{
int iOwnerBone = m_MergedBones[i].m_iMyBone;
int iParentBone = m_MergedBones[i].m_iParentBone;
int iOwnerBone = m_MergedBones[i].m_iMyBone;
int iParentBone = m_MergedBones[i].m_iParentBone;
// Only update bones reference by the bone mask.
if ( !( m_pOwnerHdr->boneFlags( iOwnerBone ) & boneMask ) )
continue;
// Only update bones reference by the bone mask.
if ( !( m_pOwnerHdr->boneFlags( iOwnerBone ) & boneMask ) )
continue;
MatrixCopy( m_pFollow->GetBone( iParentBone ), m_pOwner->GetBoneForWrite( iOwnerBone ) );
}
#ifdef CLIENT_DLL
MatrixCopy( bones[ iParentBone ], mergedbones[ iOwnerBone ] );
#else
MatrixCopy( bones[ iParentBone ], mergedbones[ iOwnerBone ] );
#endif
}
}
@ -255,8 +237,14 @@ bool CBoneMergeCache::GetAimEntOrigin( Vector *pAbsOrigin, QAngle *pAbsAngles )
// all over the place, then this won't get the right results.
// Get mFollowBone.
m_pFollow->SetupBones( NULL, -1, m_nFollowBoneSetupMask, gpGlobals->curtime );
const matrix3x4_t &mFollowBone = m_pFollow->GetBone( m_MergedBones[0].m_iParentBone );
matrix3x4_t bones[MAXSTUDIOBONES];
// Have the entity we're following setup its bones.
#ifdef CLIENT_DLL
m_pFollow->SetupBones(bones, MAXSTUDIOBONES, m_nFollowBoneSetupMask, gpGlobals->curtime);
#else
m_pFollow->SetupBones(m_pFollow->GetModelPtr(), bones, m_nFollowBoneSetupMask);
#endif
const matrix3x4_t &mFollowBone = bones[ m_MergedBones[0].m_iParentBone ];
// Get Inverse( mBoneLocal )
matrix3x4_t mBoneLocal, mBoneLocalInv;
@ -280,8 +268,14 @@ bool CBoneMergeCache::GetRootBone( matrix3x4_t &rootBone )
return false;
// Get mFollowBone.
m_pFollow->SetupBones( NULL, -1, m_nFollowBoneSetupMask, gpGlobals->curtime );
rootBone = m_pFollow->GetBone( m_MergedBones[0].m_iParentBone );
matrix3x4_t bones[MAXSTUDIOBONES];
// Have the entity we're following setup its bones.
#ifdef CLIENT_DLL
m_pFollow->SetupBones(bones, MAXSTUDIOBONES, m_nFollowBoneSetupMask, gpGlobals->curtime);
#else
m_pFollow->SetupBones(m_pFollow->GetModelPtr(), bones, m_nFollowBoneSetupMask);
#endif
rootBone = bones[ m_MergedBones[0].m_iParentBone ];
return true;
}

View file

@ -6,12 +6,21 @@
#ifndef BONE_MERGE_CACHE_H
#define BONE_MERGE_CACHE_H
#include "mathlib/mathlib.h"
#include "studio.h"
#ifdef _WIN32
#pragma once
#endif
#ifdef CLIENT_DLL
class C_BaseAnimating;
#ifndef CBaseAnimating
#define CBaseAnimating C_BaseAnimating
#endif
#else
class CBaseAnimating;
#endif
class CStudioHdr;
@ -24,14 +33,14 @@ public:
CBoneMergeCache();
void Init( C_BaseAnimating *pOwner );
void Init( CBaseAnimating *pOwner );
// Updates the lookups that let it merge bones quickly.
void UpdateCache();
// This copies the transform from all bones in the followed entity that have
// names that match our bones.
void MergeMatchingBones( int boneMask );
void MergeMatchingBones( int boneMask, matrix3x4_t mergedbones[MAXSTUDIOBONES] );
// copy bones instead of matrices
void CopyParentToChild( const Vector parentPos[], const Quaternion parentQ[], Vector childPos[], Quaternion childQ[], int boneMask );
@ -48,11 +57,11 @@ public:
private:
// This is the entity that we're keeping the cache updated for.
C_BaseAnimating *m_pOwner;
CBaseAnimating *m_pOwner;
// All the cache data is based off these. When they change, the cache data is regenerated.
// These are either all valid pointers or all NULL.
C_BaseAnimating *m_pFollow;
CBaseAnimating *m_pFollow;
CStudioHdr *m_pFollowHdr;
const studiohdr_t *m_pFollowRenderHdr;
CStudioHdr *m_pOwnerHdr;

View file

@ -419,7 +419,7 @@ public:
float m_timeValid;
int m_boneMask;
private:
public:
matrix3x4_t *BoneArray();
short *StudioToCached();
short *CachedToStudio();