//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "studio.h" #include "datacache/idatacache.h" #include "datacache/imdlcache.h" #include "convar.h" #include "tier1/utlmap.h" #include "tier0/vprof.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- mstudioanimdesc_t &studiohdr_t::pAnimdesc( int i ) const { if (numincludemodels == 0) { return *pLocalAnimdesc( i ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_anim[i].group ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); return *pStudioHdr->pLocalAnimdesc( pVModel->m_anim[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- byte *mstudioanimdesc_t::pAnimBlock( int block, int index ) const { if (block == -1) { return (byte *)NULL; } if (block == 0) { return (((byte *)this) + index); } byte *pAnimBlock = pStudiohdr()->GetAnimBlock( block ); if ( pAnimBlock ) { return pAnimBlock + index; } return (byte *)NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- static ConVar mod_load_showstall( "mod_load_showstall", "0", 0, "1 - show hitches , 2 - show stalls" ); byte *mstudioanimdesc_t::pAnim( int *piFrame ) const { float flStall; return pAnim( piFrame, flStall ); } byte *mstudioanimdesc_t::pAnim( int *piFrame, float &flStall ) const { byte *panim = NULL; int block = animblock; int index = animindex; int section = 0; if (sectionframes != 0) { if (numframes > sectionframes && *piFrame == numframes - 1) { // last frame on long anims is stored separately *piFrame = 0; section = (numframes / sectionframes) + 1; } else { section = *piFrame / sectionframes; *piFrame -= section * sectionframes; } block = pSection( section )->animblock; index = pSection( section )->animindex; } if (block == -1) { // model needs to be recompiled return NULL; } panim = pAnimBlock( block, index ); // force a preload on the next block if ( sectionframes != 0 ) { int count = ( numframes / sectionframes) + 2; for ( int i = section + 1; i < count; i++ ) { if ( pSection( i )->animblock != block ) { pAnimBlock( pSection( i )->animblock, pSection( i )->animindex ); break; } } } if (panim == NULL) { if (section > 0 && mod_load_showstall.GetInt() > 0) { Msg("[%8.3f] hitch on %s:%s:%d:%d\n", Plat_FloatTime(), pStudiohdr()->pszName(), pszName(), section, block ); } // back up until a previously loaded block is found while (--section >= 0) { block = pSection( section )->animblock; index = pSection( section )->animindex; panim = pAnimBlock( block, index ); if (panim) { // set it to the last frame in the last valid section *piFrame = sectionframes - 1; break; } } } // try to guess a valid stall time interval (tuned for the X360) flStall = 0.0f; if (panim == NULL && section <= 0) { zeroframestalltime = Plat_FloatTime(); flStall = 1.0f; } else if (panim != NULL && zeroframestalltime != 0.0f) { float dt = Plat_FloatTime() - zeroframestalltime; if (dt >= 0.0) { flStall = SimpleSpline( clamp( (0.200f - dt) * 5.0, 0.0f, 1.0f ) ); } if (flStall == 0.0f) { // disable stalltime zeroframestalltime = 0.0f; } else if (mod_load_showstall.GetInt() > 1) { Msg("[%8.3f] stall blend %.2f on %s:%s:%d:%d\n", Plat_FloatTime(), flStall, pStudiohdr()->pszName(), pszName(), section, block ); } } if (panim == NULL && mod_load_showstall.GetInt() > 1) { Msg("[%8.3f] stall on %s:%s:%d:%d\n", Plat_FloatTime(), pStudiohdr()->pszName(), pszName(), section, block ); } return panim; } mstudioikrule_t *mstudioanimdesc_t::pIKRule( int i ) const { if (numikrules) { if (ikruleindex) { return (mstudioikrule_t *)(((byte *)this) + ikruleindex) + i; } else { if (animblock == 0) { AssertOnce(0); // Should never happen return (mstudioikrule_t *)(((byte *)this) + animblockikruleindex) + i; } else { byte *pAnimBlock = pStudiohdr()->GetAnimBlock( animblock ); if ( pAnimBlock ) { return (mstudioikrule_t *)(pAnimBlock + animblockikruleindex) + i; } } } } return NULL; } mstudiolocalhierarchy_t *mstudioanimdesc_t::pHierarchy( int i ) const { if (localhierarchyindex) { if (animblock == 0) { return (mstudiolocalhierarchy_t *)(((byte *)this) + localhierarchyindex) + i; } else { byte *pAnimBlock = pStudiohdr()->GetAnimBlock( animblock ); if ( pAnimBlock ) { return (mstudiolocalhierarchy_t *)(pAnimBlock + localhierarchyindex) + i; } } } return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool studiohdr_t::SequencesAvailable() const { if (numincludemodels == 0) { return true; } return ( GetVirtualModel() != NULL ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetNumSeq( void ) const { if (numincludemodels == 0) { return numlocalseq; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_seq.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- mstudioseqdesc_t &studiohdr_t::pSeqdesc( int i ) const { if (numincludemodels == 0) { return *pLocalSeqdesc( i ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); if ( !pVModel ) { return *pLocalSeqdesc( i ); } virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[i].group ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); return *pStudioHdr->pLocalSeqdesc( pVModel->m_seq[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::iRelativeAnim( int baseseq, int relanim ) const { if (numincludemodels == 0) { return relanim; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[baseseq].group ]; return pGroup->masterAnim[ relanim ]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::iRelativeSeq( int baseseq, int relseq ) const { if (numincludemodels == 0) { return relseq; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[baseseq].group ]; return pGroup->masterSeq[ relseq ]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetNumPoseParameters( void ) const { if (numincludemodels == 0) { return numlocalposeparameters; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_pose.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const mstudioposeparamdesc_t &studiohdr_t::pPoseParameter( int i ) { if (numincludemodels == 0) { return *pLocalPoseParameter( i ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); if ( pVModel->m_pose[i].group == 0) return *pLocalPoseParameter( pVModel->m_pose[i].index ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_pose[i].group ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); return *pStudioHdr->pLocalPoseParameter( pVModel->m_pose[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetSharedPoseParameter( int iSequence, int iLocalPose ) const { if (numincludemodels == 0) { return iLocalPose; } if (iLocalPose == -1) return iLocalPose; virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; return pGroup->masterPose[iLocalPose]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::EntryNode( int iSequence ) { mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); if (numincludemodels == 0 || seqdesc.localentrynode == 0) { return seqdesc.localentrynode; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; return pGroup->masterNode[seqdesc.localentrynode-1]+1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::ExitNode( int iSequence ) { mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); if (numincludemodels == 0 || seqdesc.localexitnode == 0) { return seqdesc.localexitnode; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; return pGroup->masterNode[seqdesc.localexitnode-1]+1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetNumAttachments( void ) const { if (numincludemodels == 0) { return numlocalattachments; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_attachment.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const mstudioattachment_t &studiohdr_t::pAttachment( int i ) const { if (numincludemodels == 0) { return *pLocalAttachment( i ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[i].group ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); return *pStudioHdr->pLocalAttachment( pVModel->m_attachment[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetAttachmentBone( int i ) { const mstudioattachment_t &attachment = pAttachment( i ); // remap bone virtualmodel_t *pVModel = GetVirtualModel(); if (pVModel) { virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[i].group ]; int iBone = pGroup->masterBone[attachment.localbone]; if (iBone == -1) return 0; return iBone; } return attachment.localbone; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void studiohdr_t::SetAttachmentBone( int iAttachment, int iBone ) { mstudioattachment_t &attachment = (mstudioattachment_t &)pAttachment( iAttachment ); // remap bone virtualmodel_t *pVModel = GetVirtualModel(); if (pVModel) { virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[iAttachment].group ]; iBone = pGroup->boneMap[iBone]; } attachment.localbone = iBone; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- char *studiohdr_t::pszNodeName( int iNode ) { if (numincludemodels == 0) { return pszLocalNodeName( iNode ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); if ( pVModel->m_node.Count() <= iNode-1 ) return "Invalid node"; return pVModel->m_group[ pVModel->m_node[iNode-1].group ].GetStudioHdr()->pszLocalNodeName( pVModel->m_node[iNode-1].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetTransition( int iFrom, int iTo ) const { if (numincludemodels == 0) { return *pLocalTransition( (iFrom-1)*numlocalnodes + (iTo - 1) ); } return iTo; /* FIXME: not connected virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_transition.Element( iFrom ).Element( iTo ); */ } int studiohdr_t::GetActivityListVersion( void ) { if (numincludemodels == 0) { return activitylistversion; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); int version = activitylistversion; int i; for (i = 1; i < pVModel->m_group.Count(); i++) { virtualgroup_t *pGroup = &pVModel->m_group[ i ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); version = MIN( version, pStudioHdr->activitylistversion ); } return version; } void studiohdr_t::SetActivityListVersion( int version ) const { activitylistversion = version; if (numincludemodels == 0) { return; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); int i; for (i = 1; i < pVModel->m_group.Count(); i++) { virtualgroup_t *pGroup = &pVModel->m_group[ i ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); pStudioHdr->SetActivityListVersion( version ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int studiohdr_t::GetNumIKAutoplayLocks( void ) const { if (numincludemodels == 0) { return numlocalikautoplaylocks; } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_iklock.Count(); } const mstudioiklock_t &studiohdr_t::pIKAutoplayLock( int i ) { if (numincludemodels == 0) { return *pLocalIKAutoplayLock( i ); } virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_iklock[i].group ]; const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); Assert( pStudioHdr ); return *pStudioHdr->pLocalIKAutoplayLock( pVModel->m_iklock[i].index ); } int studiohdr_t::CountAutoplaySequences() const { int count = 0; for (int i = 0; i < GetNumSeq(); i++) { mstudioseqdesc_t &seqdesc = pSeqdesc( i ); if (seqdesc.flags & STUDIO_AUTOPLAY) { count++; } } return count; } int studiohdr_t::CopyAutoplaySequences( unsigned short *pOut, int outCount ) const { int outIndex = 0; for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) { mstudioseqdesc_t &seqdesc = pSeqdesc( i ); if (seqdesc.flags & STUDIO_AUTOPLAY) { pOut[outIndex] = i; outIndex++; } } return outIndex; } //----------------------------------------------------------------------------- // Purpose: maps local sequence bone to global bone //----------------------------------------------------------------------------- int studiohdr_t::RemapSeqBone( int iSequence, int iLocalBone ) const { // remap bone virtualmodel_t *pVModel = GetVirtualModel(); if (pVModel) { const virtualgroup_t *pSeqGroup = pVModel->pSeqGroup( iSequence ); return pSeqGroup->masterBone[iLocalBone]; } return iLocalBone; } int studiohdr_t::RemapAnimBone( int iAnim, int iLocalBone ) const { // remap bone virtualmodel_t *pVModel = GetVirtualModel(); if (pVModel) { const virtualgroup_t *pAnimGroup = pVModel->pAnimGroup( iAnim ); return pAnimGroup->masterBone[iLocalBone]; } return iLocalBone; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CStudioHdr::CStudioHdr( void ) { // set pointer to bogus value m_nFrameUnlockCounter = 0; m_pFrameUnlockCounter = &m_nFrameUnlockCounter; Init( NULL ); } CStudioHdr::CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) { // preset pointer to bogus value (it may be overwritten with legitimate data later) m_nFrameUnlockCounter = 0; m_pFrameUnlockCounter = &m_nFrameUnlockCounter; Init( pStudioHdr, mdlcache ); } // extern IDataCache *g_pDataCache; void CStudioHdr::Init( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) { m_pStudioHdr = pStudioHdr; m_pVModel = NULL; m_pStudioHdrCache.RemoveAll(); if (m_pStudioHdr == NULL) { return; } if ( mdlcache ) { m_pFrameUnlockCounter = mdlcache->GetFrameUnlockCounterPtr( MDLCACHE_STUDIOHDR ); m_nFrameUnlockCounter = *m_pFrameUnlockCounter - 1; } if (m_pStudioHdr->numincludemodels != 0) { ResetVModel( m_pStudioHdr->GetVirtualModel() ); } m_boneFlags.EnsureCount( numbones() ); m_boneParent.EnsureCount( numbones() ); for (int i = 0; i < numbones(); i++) { m_boneFlags[i] = pBone( i )->flags; m_boneParent[i] = pBone( i )->parent; } m_pActivityToSequence = NULL; } void CStudioHdr::Term() { CActivityToSequenceMapping::ReleaseMapping( m_pActivityToSequence ); m_pActivityToSequence = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CStudioHdr::SequencesAvailable() const { if (m_pStudioHdr->numincludemodels == 0) { return true; } if (m_pVModel == NULL) { // repoll m_pVModel return (ResetVModel( m_pStudioHdr->GetVirtualModel() ) != NULL); } else return true; } const virtualmodel_t * CStudioHdr::ResetVModel( const virtualmodel_t *pVModel ) const { if (pVModel != NULL) { m_pVModel = (virtualmodel_t *)pVModel; #if !defined( POSIX ) Assert( !pVModel->m_Lock.GetOwnerId() ); #endif m_pStudioHdrCache.SetCount( m_pVModel->m_group.Count() ); int i; for (i = 0; i < m_pStudioHdrCache.Count(); i++) { m_pStudioHdrCache[ i ] = NULL; } return const_cast(pVModel); } else { m_pVModel = NULL; return NULL; } } const studiohdr_t *CStudioHdr::GroupStudioHdr( int i ) { if ( !this ) { ExecuteNTimes( 5, Warning( "Call to NULL CStudioHdr::GroupStudioHdr()\n" ) ); } if ( m_nFrameUnlockCounter != *m_pFrameUnlockCounter ) { m_FrameUnlockCounterMutex.Lock(); if ( *m_pFrameUnlockCounter != m_nFrameUnlockCounter ) // i.e., this thread got the mutex { memset( m_pStudioHdrCache.Base(), 0, m_pStudioHdrCache.Count() * sizeof(studiohdr_t *) ); m_nFrameUnlockCounter = *m_pFrameUnlockCounter; } m_FrameUnlockCounterMutex.Unlock(); } if ( !m_pStudioHdrCache.IsValidIndex( i ) ) { const char *pszName = ( m_pStudioHdr ) ? m_pStudioHdr->pszName() : "<>"; ExecuteNTimes( 5, Warning( "Invalid index passed to CStudioHdr(%s)::GroupStudioHdr(): %d [%d]\n", pszName, i, m_pStudioHdrCache.Count() ) ); DebuggerBreakIfDebugging(); return m_pStudioHdr; // return something known to probably exist, certainly things will be messed up, but hopefully not crash before the warning is noticed } const studiohdr_t *pStudioHdr = m_pStudioHdrCache[ i ]; if (pStudioHdr == NULL) { #if !defined( POSIX ) Assert( !m_pVModel->m_Lock.GetOwnerId() ); #endif virtualgroup_t *pGroup = &m_pVModel->m_group[ i ]; pStudioHdr = pGroup->GetStudioHdr(); m_pStudioHdrCache[ i ] = pStudioHdr; } Assert( pStudioHdr ); return pStudioHdr; } const studiohdr_t *CStudioHdr::pSeqStudioHdr( int sequence ) { if (m_pVModel == NULL) { return m_pStudioHdr; } const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_seq[sequence].group ); return pStudioHdr; } const studiohdr_t *CStudioHdr::pAnimStudioHdr( int animation ) { if (m_pVModel == NULL) { return m_pStudioHdr; } const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_anim[animation].group ); return pStudioHdr; } mstudioanimdesc_t &CStudioHdr::pAnimdesc( int i ) { if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalAnimdesc( i ); } const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_anim[i].group ); return *pStudioHdr->pLocalAnimdesc( m_pVModel->m_anim[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetNumSeq( void ) const { if (m_pVModel == NULL) { return m_pStudioHdr->numlocalseq; } return m_pVModel->m_seq.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- mstudioseqdesc_t &CStudioHdr::pSeqdesc( int i ) { Assert( i >= 0 && i < GetNumSeq() ); if ( i < 0 || i >= GetNumSeq() ) { // Avoid reading random memory. i = 0; } if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalSeqdesc( i ); } const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_seq[i].group ); return *pStudioHdr->pLocalSeqdesc( m_pVModel->m_seq[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::iRelativeAnim( int baseseq, int relanim ) const { if (m_pVModel == NULL) { return relanim; } virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[baseseq].group ]; return pGroup->masterAnim[ relanim ]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::iRelativeSeq( int baseseq, int relseq ) const { if (m_pVModel == NULL) { return relseq; } Assert( m_pVModel ); virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[baseseq].group ]; return pGroup->masterSeq[ relseq ]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetNumPoseParameters( void ) const { if (m_pVModel == NULL) { return m_pStudioHdr->numlocalposeparameters; } Assert( m_pVModel ); return m_pVModel->m_pose.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const mstudioposeparamdesc_t &CStudioHdr::pPoseParameter( int i ) { if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalPoseParameter( i ); } if ( m_pVModel->m_pose[i].group == 0) return *m_pStudioHdr->pLocalPoseParameter( m_pVModel->m_pose[i].index ); const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_pose[i].group ); return *pStudioHdr->pLocalPoseParameter( m_pVModel->m_pose[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetSharedPoseParameter( int iSequence, int iLocalPose ) const { if (m_pVModel == NULL) { return iLocalPose; } if (iLocalPose == -1) return iLocalPose; Assert( m_pVModel ); virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; return pGroup->masterPose[iLocalPose]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::EntryNode( int iSequence ) { mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); if (m_pVModel == NULL || seqdesc.localentrynode == 0) { return seqdesc.localentrynode; } Assert( m_pVModel ); virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; return pGroup->masterNode[seqdesc.localentrynode-1]+1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::ExitNode( int iSequence ) { mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); if (m_pVModel == NULL || seqdesc.localexitnode == 0) { return seqdesc.localexitnode; } Assert( m_pVModel ); virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; return pGroup->masterNode[seqdesc.localexitnode-1]+1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetNumAttachments( void ) const { if (m_pVModel == NULL) { return m_pStudioHdr->numlocalattachments; } Assert( m_pVModel ); return m_pVModel->m_attachment.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const mstudioattachment_t &CStudioHdr::pAttachment( int i ) { if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalAttachment( i ); } Assert( m_pVModel ); const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_attachment[i].group ); return *pStudioHdr->pLocalAttachment( m_pVModel->m_attachment[i].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetAttachmentBone( int i ) { if (m_pVModel == 0) { return m_pStudioHdr->pLocalAttachment( i )->localbone; } virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_attachment[i].group ]; const mstudioattachment_t &attachment = pAttachment( i ); int iBone = pGroup->masterBone[attachment.localbone]; if (iBone == -1) return 0; return iBone; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CStudioHdr::SetAttachmentBone( int iAttachment, int iBone ) { mstudioattachment_t &attachment = (mstudioattachment_t &)m_pStudioHdr->pAttachment( iAttachment ); // remap bone if (m_pVModel) { virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_attachment[iAttachment].group ]; iBone = pGroup->boneMap[iBone]; } attachment.localbone = iBone; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- char *CStudioHdr::pszNodeName( int iNode ) { if (m_pVModel == NULL) { return m_pStudioHdr->pszLocalNodeName( iNode ); } if ( m_pVModel->m_node.Count() <= iNode-1 ) return "Invalid node"; const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_node[iNode-1].group ); return pStudioHdr->pszLocalNodeName( m_pVModel->m_node[iNode-1].index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetTransition( int iFrom, int iTo ) const { if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalTransition( (iFrom-1)*m_pStudioHdr->numlocalnodes + (iTo - 1) ); } return iTo; /* FIXME: not connected virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); Assert( pVModel ); return pVModel->m_transition.Element( iFrom ).Element( iTo ); */ } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetActivityListVersion( void ) { if (m_pVModel == NULL) { return m_pStudioHdr->activitylistversion; } int version = m_pStudioHdr->activitylistversion; int i; for (i = 1; i < m_pVModel->m_group.Count(); i++) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); version = MIN( version, pStudioHdr->activitylistversion ); } return version; } void CStudioHdr::SetActivityListVersion( int version ) { m_pStudioHdr->activitylistversion = version; if (m_pVModel == NULL) { return; } int i; for (i = 1; i < m_pVModel->m_group.Count(); i++) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); pStudioHdr->SetActivityListVersion( version ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetEventListVersion( void ) { if (m_pVModel == NULL) { return m_pStudioHdr->eventsindexed; } int version = m_pStudioHdr->eventsindexed; int i; for (i = 1; i < m_pVModel->m_group.Count(); i++) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); version = MIN( version, pStudioHdr->eventsindexed ); } return version; } void CStudioHdr::SetEventListVersion( int version ) { m_pStudioHdr->eventsindexed = version; if (m_pVModel == NULL) { return; } int i; for (i = 1; i < m_pVModel->m_group.Count(); i++) { const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); Assert( pStudioHdr ); pStudioHdr->eventsindexed = version; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CStudioHdr::GetNumIKAutoplayLocks( void ) const { if (m_pVModel == NULL) { return m_pStudioHdr->numlocalikautoplaylocks; } return m_pVModel->m_iklock.Count(); } const mstudioiklock_t &CStudioHdr::pIKAutoplayLock( int i ) { if (m_pVModel == NULL) { return *m_pStudioHdr->pLocalIKAutoplayLock( i ); } const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_iklock[i].group ); Assert( pStudioHdr ); return *pStudioHdr->pLocalIKAutoplayLock( m_pVModel->m_iklock[i].index ); } #if 0 int CStudioHdr::CountAutoplaySequences() const { int count = 0; for (int i = 0; i < GetNumSeq(); i++) { mstudioseqdesc_t &seqdesc = pSeqdesc( i ); if (seqdesc.flags & STUDIO_AUTOPLAY) { count++; } } return count; } int CStudioHdr::CopyAutoplaySequences( unsigned short *pOut, int outCount ) const { int outIndex = 0; for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) { mstudioseqdesc_t &seqdesc = pSeqdesc( i ); if (seqdesc.flags & STUDIO_AUTOPLAY) { pOut[outIndex] = i; outIndex++; } } return outIndex; } #endif //----------------------------------------------------------------------------- // Purpose: maps local sequence bone to global bone //----------------------------------------------------------------------------- int CStudioHdr::RemapSeqBone( int iSequence, int iLocalBone ) const { // remap bone if (m_pVModel) { const virtualgroup_t *pSeqGroup = m_pVModel->pSeqGroup( iSequence ); return pSeqGroup->masterBone[iLocalBone]; } return iLocalBone; } int CStudioHdr::RemapAnimBone( int iAnim, int iLocalBone ) const { // remap bone if (m_pVModel) { const virtualgroup_t *pAnimGroup = m_pVModel->pAnimGroup( iAnim ); return pAnimGroup->masterBone[iLocalBone]; } return iLocalBone; } //----------------------------------------------------------------------------- // Purpose: run the interpreted FAC's expressions, converting flex_controller // values into FAC weights //----------------------------------------------------------------------------- void CStudioHdr::RunFlexRulesOld( const float *src, float *dest ) { int i, j; // FIXME: this shouldn't be needed, flex without rules should be stripped in studiomdl for (i = 0; i < numflexdesc(); i++) { dest[i] = 0; } for (i = 0; i < numflexrules(); i++) { float stack[32]; int k = 0; mstudioflexrule_t *prule = pFlexRule( i ); mstudioflexop_t *pops = prule->iFlexOp( 0 ); // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), i + 1, 0, "%2d:%d\n", i, prule->flex ); for (j = 0; j < prule->numops; j++) { switch (pops->op) { case STUDIO_ADD: stack[k-2] = stack[k-2] + stack[k-1]; k--; break; case STUDIO_SUB: stack[k-2] = stack[k-2] - stack[k-1]; k--; break; case STUDIO_MUL: stack[k-2] = stack[k-2] * stack[k-1]; k--; break; case STUDIO_DIV: if (stack[k-1] > 0.0001) { stack[k-2] = stack[k-2] / stack[k-1]; } else { stack[k-2] = 0; } k--; break; case STUDIO_NEG: stack[k-1] = -stack[k-1]; break; case STUDIO_MAX: stack[k-2] = MAX( stack[k-2], stack[k-1] ); k--; break; case STUDIO_MIN: stack[k-2] = MIN( stack[k-2], stack[k-1] ); k--; break; case STUDIO_CONST: stack[k] = pops->d.value; k++; break; case STUDIO_FETCH1: { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index)->localToGlobal; stack[k] = src[m]; k++; break; } case STUDIO_FETCH2: { stack[k] = dest[pops->d.index]; k++; break; } case STUDIO_COMBO: { int m = pops->d.index; int km = k - m; for ( int i = km + 1; i < k; ++i ) { stack[ km ] *= stack[ i ]; } k = k - m + 1; } break; case STUDIO_DOMINATE: { int m = pops->d.index; int km = k - m; float dv = stack[ km ]; for ( int i = km + 1; i < k; ++i ) { dv *= stack[ i ]; } stack[ km - 1 ] *= 1.0f - dv; k -= m; } break; case STUDIO_2WAY_0: { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; stack[ k ] = RemapValClamped( src[m], -1.0f, 0.0f, 1.0f, 0.0f ); k++; } break; case STUDIO_2WAY_1: { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; stack[ k ] = RemapValClamped( src[m], 0.0f, 1.0f, 0.0f, 1.0f ); k++; } break; case STUDIO_NWAY: { LocalFlexController_t valueControllerIndex = static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ); int m = pFlexcontroller( valueControllerIndex )->localToGlobal; float flValue = src[ m ]; int v = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; const Vector4D filterRamp( stack[ k - 5 ], stack[ k - 4 ], stack[ k - 3 ], stack[ k - 2 ] ); // Apply multicontrol remapping if ( flValue <= filterRamp.x || flValue >= filterRamp.w ) { flValue = 0.0f; } else if ( flValue < filterRamp.y ) { flValue = RemapValClamped( flValue, filterRamp.x, filterRamp.y, 0.0f, 1.0f ); } else if ( flValue > filterRamp.z ) { flValue = RemapValClamped( flValue, filterRamp.z, filterRamp.w, 1.0f, 0.0f ); } else { flValue = 1.0f; } stack[ k - 5 ] = flValue * src[ v ]; k -= 4; } break; case STUDIO_DME_LOWER_EYELID: { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ) ); const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); int nBlinkIndex = static_cast< int >( stack[ k - 2 ] ); float flBlink = 0.0f; if ( nBlinkIndex >= 0 ) { const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 2 ] ) ); flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); } int nEyeUpDownIndex = static_cast< int >( stack[ k - 3 ] ); float flEyeUpDown = 0.0f; if ( nEyeUpDownIndex >= 0 ) { const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 3 ] ) ); flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); } if ( flEyeUpDown > 0.0 ) { stack [ k - 3 ] = ( 1.0f - flEyeUpDown ) * ( 1.0f - flCloseLidV ) * flCloseLid; } else { stack [ k - 3 ] = ( 1.0f - flCloseLidV ) * flCloseLid; } k -= 2; } break; case STUDIO_DME_UPPER_EYELID: { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ) ); const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); int nBlinkIndex = static_cast< int >( stack[ k - 2 ] ); float flBlink = 0.0f; if ( nBlinkIndex >= 0 ) { const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 2 ] ) ); flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); } int nEyeUpDownIndex = static_cast< int >( stack[ k - 3 ] ); float flEyeUpDown = 0.0f; if ( nEyeUpDownIndex >= 0 ) { const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 3 ] ) ); flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); } if ( flEyeUpDown < 0.0f ) { stack [ k - 3 ] = ( 1.0f + flEyeUpDown ) * flCloseLidV * flCloseLid; } else { stack [ k - 3 ] = flCloseLidV * flCloseLid; } k -= 2; } break; } pops++; } dest[prule->flex] = stack[0]; } } void CStudioHdr::RunFlexRulesNew( const float *src, float *dest ) { // FIXME: this shouldn't be needed, flex without rules should be stripped in studiomdl memset( dest, 0, sizeof( dest[0] ) * numflexdesc() ); for (int i = 0; i < numflexrules(); i++) { float stack[32]; float *pSP = stack + ARRAYSIZE( stack ); mstudioflexrule_t *prule = pFlexRule( i ); mstudioflexop_t *pops = prule->iFlexOp( 0 ); int nOps = prule->numops; float flTOS = 0.; if ( nOps ) do { switch (pops->op) { case STUDIO_ADD: flTOS += *(pSP++); break; case STUDIO_SUB: flTOS = *(pSP++) - flTOS; break; case STUDIO_MUL: flTOS *= *(pSP++); break; case STUDIO_DIV: if (flTOS > 0.0001) { flTOS = *(pSP) / flTOS; } else { flTOS = 0.; } pSP++; break; case STUDIO_NEG: flTOS = -flTOS; break; case STUDIO_MAX: { float flNos = *(pSP++); flTOS = MAX( flTOS, flNos ); break; } case STUDIO_MIN: { float flNos = *(pSP++); flTOS = MIN( flTOS, flNos); break; } case STUDIO_CONST: *(--pSP) = flTOS; flTOS = pops->d.value; break; case STUDIO_FETCH1: { *(--pSP ) = flTOS; int m = pFlexcontroller( (LocalFlexController_t)pops->d.index)->localToGlobal; flTOS = src[m]; break; } case STUDIO_FETCH2: { *(--pSP) = flTOS; flTOS = dest[pops->d.index]; break; } case STUDIO_COMBO: { // tos = prod( top m elements on stack) int m = pops->d.index; while( --m ) { flTOS *= *(pSP++); } break; } break; case STUDIO_DOMINATE: { // tos *= 1-prod( next top m elements on stack) int m = pops->d.index; float dv = *(pSP++); while( --m ) { dv *= *(pSP++); } flTOS *= 1.0 - dv; break; } break; case STUDIO_2WAY_0: { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; *(--pSP) = flTOS; flTOS = RemapValClamped( src[m], -1.0f, 0.0f, 1.0f, 0.0f ); } break; case STUDIO_2WAY_1: { int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; *(--pSP) = flTOS; flTOS = RemapValClamped( src[m], 0.0f, 1.0f, 0.0f, 1.0f ); } break; case STUDIO_NWAY: { LocalFlexController_t valueControllerIndex = static_cast< LocalFlexController_t >( (int) flTOS ); int m = pFlexcontroller( valueControllerIndex )->localToGlobal; float flValue = src[ m ]; int v = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; const Vector4D filterRamp( pSP[3], pSP[2], pSP[1], pSP[0] ); // Apply multicontrol remapping if ( flValue <= filterRamp.x || flValue >= filterRamp.w ) { flValue = 0.0f; } else if ( flValue < filterRamp.y ) { flValue = RemapValClamped( flValue, filterRamp.x, filterRamp.y, 0.0f, 1.0f ); } else if ( flValue > filterRamp.z ) { flValue = RemapValClamped( flValue, filterRamp.z, filterRamp.w, 1.0f, 0.0f ); } else { flValue = 1.0f; } pSP+= 4; flTOS = flValue * src[ v ]; } break; case STUDIO_DME_LOWER_EYELID: { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)flTOS ) ); const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); int nBlinkIndex = static_cast< int >( pSP[0] ); float flBlink = 0.0f; if ( nBlinkIndex >= 0 ) { const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( nBlinkIndex ) ); flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); } int nEyeUpDownIndex = static_cast< int >( pSP[1] ); float flEyeUpDown = 0.0f; if ( nEyeUpDownIndex >= 0 ) { const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( nEyeUpDownIndex ) ); flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); } if ( flEyeUpDown > 0.0 ) { flTOS = ( 1.0f - flEyeUpDown ) * ( 1.0f - flCloseLidV ) * flCloseLid; } else { flTOS = ( 1.0f - flCloseLidV ) * flCloseLid; } pSP += 2; } break; case STUDIO_DME_UPPER_EYELID: { const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)flTOS ) ); const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); int nBlinkIndex = static_cast< int >( pSP[0] ); float flBlink = 0.0f; if ( nBlinkIndex >= 0 ) { const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( nBlinkIndex ) ); flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); } int nEyeUpDownIndex = static_cast< int >( pSP[1] ); float flEyeUpDown = 0.0f; if ( nEyeUpDownIndex >= 0 ) { const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( nEyeUpDownIndex ) ); flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); } if ( flEyeUpDown < 0.0f ) { flTOS = ( 1.0f + flEyeUpDown ) * flCloseLidV * flCloseLid; } else { flTOS = flCloseLidV * flCloseLid; } pSP += 2; } break; } pops++; } while( --nOps ); dest[prule->flex] = flTOS; } } #define USE_OLD_FLEX_RULES_INTERPRETER void CStudioHdr::RunFlexRules( const float *src, float *dest ) { #ifndef USE_OLD_FLEX_RULES_INTERPRETER RunFlexRulesNew( src, dest ); #else RunFlexRulesOld( src, dest ); #endif #if defined(_DEBUG) && !defined(USE_OLD_FLEX_RULES_INTERPRETER) float d1[ MAXSTUDIOFLEXDESC ]; RunFlexRulesOld( src, d1 ); for ( int i =0; i < numflexdesc(); i++) { if ( fabs( d1[i] - dest[i] ) > 0.001 ) { Warning("bad %d old =%f new=%f\n", i, dest[i], d1[i] ); } } #endif // _DEBUG } //----------------------------------------------------------------------------- // CODE PERTAINING TO ACTIVITY->SEQUENCE MAPPING SUBCLASS //----------------------------------------------------------------------------- #define iabs(i) (( (i) >= 0 ) ? (i) : -(i) ) CUtlSymbolTable g_ActivityModifiersTable; extern void SetActivityForSequence( CStudioHdr *pstudiohdr, int i ); void CStudioHdr::CActivityToSequenceMapping::Initialize( const CStudioHdr * __restrict pstudiohdr ) { VPROF( "CStudioHdr::CActivityToSequenceMapping::Initialize" ); // Algorithm: walk through every sequence in the model, determine to which activity // it corresponds, and keep a count of sequences per activity. Once the total count // is available, allocate an array large enough to contain them all, update the // starting indices for every activity's section in the array, and go back through, // populating the array with its data. m_pStudioHdr = pstudiohdr->m_pStudioHdr; AssertMsg1( m_pSequenceTuples == NULL, "Tried to double-initialize sequence mapping for %s", pstudiohdr->pszName() ); if ( m_pSequenceTuples != NULL ) return; // don't double initialize. SetValidation(pstudiohdr); if ( ! pstudiohdr->SequencesAvailable() ) return; // nothing to do. // Some studio headers have no activities at all. In those // cases we can avoid a lot of this effort. bool bFoundOne = false; // for each sequence in the header... const int NumSeq = pstudiohdr->GetNumSeq(); for ( int i = 0 ; i < NumSeq ; ++i ) { const mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pstudiohdr)->pSeqdesc( i ); #if defined(SERVER_DLL) || defined(CLIENT_DLL) || defined(GAME_DLL) if (!(seqdesc.flags & STUDIO_ACTIVITY)) { // AssertMsg2( false, "Sequence %d on studiohdr %s didn't have its activity initialized!", i, pstudiohdr->pszName() ); SetActivityForSequence( (CStudioHdr *)pstudiohdr, i ); } #endif // is there an activity associated with this sequence? if (seqdesc.activity >= 0) { bFoundOne = true; // look up if we already have an entry. First we need to make a speculative one -- HashValueType entry(seqdesc.activity, 0, 1, iabs(seqdesc.actweight)); UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); if ( m_ActToSeqHash.IsValidHandle(handle) ) { // we already have an entry and must update it by incrementing count HashValueType * __restrict toUpdate = &m_ActToSeqHash.Element(handle); toUpdate->count += 1; toUpdate->totalWeight += iabs(seqdesc.actweight); } else { // we do not have an entry yet; create one. m_ActToSeqHash.Insert(entry); } } } // if we found nothing, don't bother with any other initialization! if (!bFoundOne) return; // Now, create starting indices for each activity. For an activity n, // the starting index is of course the sum of counts [0..n-1]. register int sequenceCount = 0; int topActivity = 0; // this will store the highest seen activity number (used later to make an ad hoc map on the stack) for ( UtlHashHandle_t handle = m_ActToSeqHash.GetFirstHandle() ; m_ActToSeqHash.IsValidHandle(handle) ; handle = m_ActToSeqHash.GetNextHandle(handle) ) { HashValueType &element = m_ActToSeqHash[handle]; element.startingIdx = sequenceCount; sequenceCount += element.count; topActivity = MAX(topActivity, element.activityIdx); } // Allocate the actual array of sequence information. Note the use of restrict; // this is an important optimization, but means that you must never refer to this // array through m_pSequenceTuples in the scope of this function. SequenceTuple * __restrict tupleList = new SequenceTuple[sequenceCount]; m_pSequenceTuples = tupleList; // save it off -- NEVER USE m_pSequenceTuples in this function! m_iSequenceTuplesCount = sequenceCount; // Now we're going to actually populate that list with the relevant data. // First, create an array on the stack to store how many sequences we've written // so far for each activity. (This is basically a very simple way of doing a map.) // This stack may potentially grow very large; so if you have problems with it, // go to a utlmap or similar structure. unsigned int allocsize = (topActivity + 1) * sizeof(int); #define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression allocsize = ALIGN_VALUE(allocsize,16); int * __restrict seqsPerAct = static_cast(stackalloc(allocsize)); memset(seqsPerAct, 0, allocsize); // okay, walk through all the sequences again, and write the relevant data into // our little table. for ( int i = 0 ; i < NumSeq ; ++i ) { const mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pstudiohdr)->pSeqdesc( i ); if (seqdesc.activity >= 0) { const HashValueType &element = m_ActToSeqHash[m_ActToSeqHash.Find(HashValueType(seqdesc.activity, 0, 0, 0))]; // If this assert trips, we've written more sequences per activity than we allocated // (therefore there must have been a miscount in the first for loop above). int tupleOffset = seqsPerAct[seqdesc.activity]; Assert( tupleOffset < element.count ); if ( seqdesc.numactivitymodifiers > 0 ) { // add entries for this model's activity modifiers (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers = new CUtlSymbol[ seqdesc.numactivitymodifiers ]; (tupleList + element.startingIdx + tupleOffset)->iNumActivityModifiers = seqdesc.numactivitymodifiers; for ( int k = 0; k < seqdesc.numactivitymodifiers; k++ ) { (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers[ k ] = g_ActivityModifiersTable.AddString( seqdesc.pActivityModifier( k )->pszName() ); } } else { (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers = NULL; (tupleList + element.startingIdx + tupleOffset)->iNumActivityModifiers = 0; } // You might be tempted to collapse this pointer math into a single pointer -- // don't! the tuple list is marked __restrict above. (tupleList + element.startingIdx + tupleOffset)->seqnum = i; // store sequence number (tupleList + element.startingIdx + tupleOffset)->weight = iabs(seqdesc.actweight); seqsPerAct[seqdesc.activity] += 1; } } #ifdef DBGFLAG_ASSERT // double check that we wrote exactly the right number of sequences. unsigned int chkSequenceCount = 0; for (int j = 0 ; j <= topActivity ; ++j) { chkSequenceCount += seqsPerAct[j]; } Assert(chkSequenceCount == m_iSequenceTuplesCount); #endif } /// Force Initialize() to occur again, even if it has already occured. void CStudioHdr::CActivityToSequenceMapping::Reinitialize( CStudioHdr *pstudiohdr ) { if (m_pSequenceTuples) { delete m_pSequenceTuples; m_pSequenceTuples = NULL; } m_ActToSeqHash.RemoveAll(); Initialize(pstudiohdr); } // Look up relevant data for an activity's sequences. This isn't terribly efficient, due to the // load-hit-store on the output parameters, so the most common case -- SelectWeightedSequence -- // is specially implemented. const CStudioHdr::CActivityToSequenceMapping::SequenceTuple *CStudioHdr::CActivityToSequenceMapping::GetSequences( int forActivity, int * __restrict outSequenceCount, int * __restrict outTotalWeight ) { // Construct a dummy entry so we can do a hash lookup (the UtlHash does not divorce keys from values) HashValueType entry(forActivity, 0, 0, 0); UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); if (m_ActToSeqHash.IsValidHandle(handle)) { const HashValueType &element = m_ActToSeqHash[handle]; const SequenceTuple *retval = m_pSequenceTuples + element.startingIdx; *outSequenceCount = element.count; *outTotalWeight = element.totalWeight; return retval; } else { // invalid handle; return NULL. // this is actually a legit use case, so no need to assert. return NULL; } } int CStudioHdr::CActivityToSequenceMapping::NumSequencesForActivity( int forActivity ) { // If this trips, you've called this function on something that doesn't // have activities. //Assert(m_pSequenceTuples != NULL); if ( m_pSequenceTuples == NULL ) return 0; HashValueType entry(forActivity, 0, 0, 0); UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); if (m_ActToSeqHash.IsValidHandle(handle)) { return m_ActToSeqHash[handle].count; } else { return 0; } } static CStudioHdr::CActivityToSequenceMapping emptyMapping; // double-check that the data I point to hasn't changed bool CStudioHdr::CActivityToSequenceMapping::ValidateAgainst( const CStudioHdr * RESTRICT pstudiohdr ) RESTRICT { return ( this == &emptyMapping || ( m_pStudioHdr == pstudiohdr->m_pStudioHdr && m_expectedVModel == pstudiohdr->GetVirtualModel() ) ); } void CStudioHdr::CActivityToSequenceMapping::SetValidation( const CStudioHdr *RESTRICT pstudiohdr ) RESTRICT { m_expectedVModel = pstudiohdr->GetVirtualModel(); } struct StudioHdrToActivityMapEntry_t { long checksum; char name[64]; int nRefs; CStudioHdr::CActivityToSequenceMapping *pMap; }; CUtlMap g_StudioHdrToActivityMaps( DefLessFunc( const studiohdr_t * ) ); CThreadFastMutex g_StudioHdrToActivityMapsLock; CStudioHdr::CActivityToSequenceMapping *CStudioHdr::CActivityToSequenceMapping::FindMapping( const CStudioHdr *pHdr ) { VPROF( "CStudioHdr::CActivityToSequenceMapping::FindMapping" ); if ( !pHdr->SequencesAvailable() || pHdr->GetNumSeq() <= 1 ) { return &emptyMapping; } Assert( !pHdr->m_pActivityToSequence ); AUTO_LOCK( g_StudioHdrToActivityMapsLock ); const studiohdr_t *pRealHdr = pHdr->m_pStudioHdr; int i = g_StudioHdrToActivityMaps.Find( pRealHdr ); if ( i != g_StudioHdrToActivityMaps.InvalidIndex() ) { if ( !IsX360() && ( g_StudioHdrToActivityMaps[i].checksum != pRealHdr->checksum || Q_strcmp( g_StudioHdrToActivityMaps[i].name, pRealHdr->name ) != 0 ) ) { AssertFatal( g_StudioHdrToActivityMaps[i].nRefs == 0 ); delete g_StudioHdrToActivityMaps[i].pMap; g_StudioHdrToActivityMaps.RemoveAt( i ); } else { Assert( g_StudioHdrToActivityMaps[i].checksum == pRealHdr->checksum && Q_strcmp( g_StudioHdrToActivityMaps[i].name, pRealHdr->name ) == 0 ); g_StudioHdrToActivityMaps[i].nRefs++; return g_StudioHdrToActivityMaps[i].pMap; } } i = g_StudioHdrToActivityMaps.Insert( pRealHdr ); g_StudioHdrToActivityMaps[i].checksum = pRealHdr->checksum; Q_strncpy( g_StudioHdrToActivityMaps[i].name, pRealHdr->name, 64 ); g_StudioHdrToActivityMaps[i].nRefs = 1; g_StudioHdrToActivityMaps[i].pMap = new CStudioHdr::CActivityToSequenceMapping; g_StudioHdrToActivityMaps[i].pMap->Initialize( pHdr ); return g_StudioHdrToActivityMaps[i].pMap; } void CStudioHdr::CActivityToSequenceMapping::ReleaseMapping( CActivityToSequenceMapping *pMap ) { if ( pMap && pMap != &emptyMapping) { VPROF( "CStudioHdr::CActivityToSequenceMapping::ReleaseMapping" ); AUTO_LOCK( g_StudioHdrToActivityMapsLock ); int i = g_StudioHdrToActivityMaps.Find( pMap->m_pStudioHdr ); if ( i != g_StudioHdrToActivityMaps.InvalidIndex() ) { Assert( g_StudioHdrToActivityMaps[i].nRefs > 0 ); g_StudioHdrToActivityMaps[i].nRefs--; } else { Assert( 0 ); } } } void CStudioHdr::CActivityToSequenceMapping::ResetMappings() { for ( int i = g_StudioHdrToActivityMaps.FirstInorder(); i != g_StudioHdrToActivityMaps.InvalidIndex(); i = g_StudioHdrToActivityMaps.NextInorder( i ) ) { if ( g_StudioHdrToActivityMaps[i].nRefs == 0 ) { delete g_StudioHdrToActivityMaps[i].pMap; } else { Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); Msg( "************* DO NOT IGNORE ME *******************************\n" ); Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); Warning( "Studio activity sequence mapping leak! (%s, %d)\n", g_StudioHdrToActivityMaps[i].name, g_StudioHdrToActivityMaps[i].nRefs ); Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); Msg( "****************************************************************\n" ); } } g_StudioHdrToActivityMaps.RemoveAll(); }