Fixed server side setupbones

This commit is contained in:
Kamay Xutax 2024-08-22 23:55:21 +02:00
parent 259c05ac75
commit 1da7d763fe
9 changed files with 63 additions and 272 deletions

View file

@ -1972,8 +1972,10 @@ void C_BaseAnimating::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quat
// debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 0, "%30s %6.2f : %6.2f", hdr->pSeqdesc( GetSequence() )->pszLabel( ), fCycle, 1.0 );
// TODO_ENHANCED: Do that only for client side animations
// if (m_bClientSideAnimation)
if (m_bClientSideAnimation)
{
MaintainSequenceTransitions( boneSetup, fCycle, currentTime, pos, q );
}
AccumulateLayers( boneSetup, pos, q, currentTime );
@ -2743,82 +2745,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 );
@ -2972,8 +2898,10 @@ bool C_BaseAnimating::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, i
// NOTE: For model scaling, we need to opt out of IK because it will mark the bones as already being calculated
if ( !IsModelScaled() )
{
// only allocate an ik block if the npc can use it
if ( !m_pIk && hdr->numikchains() > 0 && !(m_EntClientFlags & ENTCLIENTFLAG_DONTUSEIK) )
// only allocate an ik block if the npc can use it
// The flag is now completely ignored to match server bones!
// If it doesn't work well, blame models.
if ( !m_pIk && hdr->numikchains() > 0 /* && !(m_EntClientFlags & ENTCLIENTFLAG_DONTUSEIK) */ )
{
m_pIk = new CIKContext;
}

View file

@ -99,8 +99,8 @@ public:
enum
{
NUM_POSEPAREMETERS = 24,
NUM_BONECTRLS = 4
NUM_POSEPAREMETERS = MAXSTUDIOPOSEPARAM,
NUM_BONECTRLS = MAXSTUDIOBONECTRLS
};
C_BaseAnimating();
@ -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

@ -369,67 +369,4 @@ 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,7 +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
{

View file

@ -4293,12 +4293,13 @@ void C_BaseEntity::CalcAbsolutePosition( )
NormalizeAngles( m_angAbsRotation );
return;
}
if ( IsEffectActive(EF_BONEMERGE) )
{
MoveToAimEnt();
return;
}
// TODO_ENHANCED: this should be safe to remove.
// if ( IsEffectActive(EF_BONEMERGE) )
// {
// MoveToAimEnt();
// return;
// }
// Construct the entity-to-world matrix
// Start with making an entity-to-parent matrix

View file

@ -202,9 +202,10 @@ C_CHostage::C_CHostage()
m_flDeadOrRescuedTime = 0.0;
m_flLastBodyYaw = 0;
m_createdLowViolenceRagdoll = false;
// TODO: Get IK working on the steep slopes CS has, then enable it on characters.
m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;
// TODO: Get IK working on the steep slopes CS has, then enable it on characters.
// Breaks server side setup bones !
// m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;
// set the model so the PlayerAnimState uses the Hostage activities/sequences
SetModelName( "models/Characters/Hostage_01.mdl" );

View file

@ -2601,100 +2601,9 @@ float C_CSPlayer::GetDeathCamInterpolationTime()
}
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

@ -1800,31 +1800,46 @@ void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
// adjust hit boxes based on IK driven offset
Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
if ( CanSkipAnimation() )
// NOTE: For model scaling, we need to opt out of IK because it will mark the bones as already being calculated
if ( !IsModelScaled() )
{
IBoneSetup boneSetup( pStudioHdr, boneMask, GetPoseParameterArray() );
boneSetup.InitPose( pos, q );
// Msg( "%.03f : %s:%s not in pvs\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
// only allocate an ik block if the npc can use it
if ( !m_pIk && pStudioHdr->numikchains() > 0 )
{
m_pIk = new CIKContext;
}
}
else
else
{
// Reset the IK
if ( m_pIk )
{
// FIXME: pass this into Studio_BuildMatrices to skip transforms
CBoneBitList boneComputed;
m_iIKCounter++;
m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
GetSkeleton( pStudioHdr, pos, q, boneMask );
delete m_pIk;
m_pIk = NULL;
}
}
m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
CalculateIKLocks( gpGlobals->curtime );
m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
}
else
{
// Msg( "%.03f : %s:%s\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
GetSkeleton( pStudioHdr, pos, q, boneMask );
}
if ( m_pIk )
{
m_pIk->Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask );
}
if ( m_pIk )
{
// FIXME: pass this into Studio_BuildMatrices to skip transforms
CBoneBitList boneComputed;
m_iIKCounter++;
m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
GetSkeleton( pStudioHdr, pos, q, boneMask );
m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
CalculateIKLocks( gpGlobals->curtime );
m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
}
else
{
// Msg( "%.03f : %s:%s\n", gpGlobals->curtime, GetClassname(), GetEntityName().ToCStr() );
GetSkeleton( pStudioHdr, pos, q, boneMask );
}
CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );

View file

@ -41,8 +41,8 @@ public:
enum
{
NUM_POSEPAREMETERS = 24,
NUM_BONECTRLS = 4
NUM_POSEPAREMETERS = MAXSTUDIOPOSEPARAM,
NUM_BONECTRLS = MAXSTUDIOBONECTRLS
};
DECLARE_DATADESC();
@ -161,6 +161,8 @@ public:
bool HasPoseParameter( int iSequence, int iParameter );
float EdgeLimitPoseParameter( int iParameter, float flValue, float flBase = 0.0f );
inline bool IsModelScaled() const;
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
@ -518,6 +520,10 @@ inline void CBaseAnimating::SetCycle( float flCycle )
m_flCycle = flCycle;
}
inline bool CBaseAnimating::IsModelScaled() const
{
return ( m_flModelScale > 1.0f+FLT_EPSILON || m_flModelScale < 1.0f-FLT_EPSILON );
}
EXTERN_SEND_TABLE(DT_BaseAnimating);