summaryrefslogtreecommitdiff
path: root/tier3
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier3
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'tier3')
-rw-r--r--tier3/choreoutils.cpp347
-rw-r--r--tier3/mdlutils.cpp459
-rw-r--r--tier3/scenetokenprocessor.cpp201
-rw-r--r--tier3/studiohdrstub.cpp47
-rw-r--r--tier3/tier3.cpp154
-rw-r--r--tier3/tier3.vpc29
6 files changed, 1237 insertions, 0 deletions
diff --git a/tier3/choreoutils.cpp b/tier3/choreoutils.cpp
new file mode 100644
index 0000000..613103e
--- /dev/null
+++ b/tier3/choreoutils.cpp
@@ -0,0 +1,347 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Helper methods + classes for file access
+//
+//===========================================================================//
+
+#include "tier3/choreoutils.h"
+#include "tier3/tier3.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "studio.h"
+#include "../game/shared/choreoscene.h"
+#include "../game/shared/choreoevent.h"
+#include "tier1/KeyValues.h"
+#include "bone_setup.h"
+#include "soundchars.h"
+
+
+//-----------------------------------------------------------------------------
+// Find sequence by name
+//-----------------------------------------------------------------------------
+static int LookupSequence( CStudioHdr *pStudioHdr, const char *pSequenceName )
+{
+ for ( int i = 0; i < pStudioHdr->GetNumSeq(); i++ )
+ {
+ if ( !Q_stricmp( pSequenceName, pStudioHdr->pSeqdesc( i ).pszLabel() ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns sequence flags
+//-----------------------------------------------------------------------------
+static int GetSequenceFlags( CStudioHdr *pStudioHdr, int nSequence )
+{
+ if ( !pStudioHdr || nSequence < 0 || nSequence >= pStudioHdr->GetNumSeq() )
+ return 0;
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSequence );
+ return seqdesc.flags;
+}
+
+
+//-----------------------------------------------------------------------------
+// Does a sequence loop?
+//-----------------------------------------------------------------------------
+static bool DoesSequenceLoop( CStudioHdr *pStudioHdr, int nSequence )
+{
+ int nFlags = GetSequenceFlags( pStudioHdr, nSequence );
+ bool bLooping = ( nFlags & STUDIO_LOOPING ) ? true : false;
+ return bLooping;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool AutoAddGestureKeys( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly )
+{
+ int iSequence = LookupSequence( pStudioHdr, e->GetParameters() );
+ if ( iSequence < 0 )
+ return false;
+
+ KeyValues *pSeqKeyValues = new KeyValues( "" );
+ if ( !pSeqKeyValues->LoadFromBuffer( pStudioHdr->pszName(), Studio_GetKeyValueText( pStudioHdr, iSequence ) ) )
+ {
+ pSeqKeyValues->deleteThis();
+ return false;
+ }
+
+ // Do we have a build point section?
+ KeyValues *pKVAllFaceposer = pSeqKeyValues->FindKey("faceposer");
+ if ( !pKVAllFaceposer )
+ {
+ pSeqKeyValues->deleteThis();
+ return false;
+ }
+
+ int nMaxFrame = Studio_MaxFrame( pStudioHdr, iSequence, pPoseParameters ) - 1;
+
+ // Start grabbing the sounds and slotting them in
+ KeyValues *pkvFaceposer;
+ char szStartLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "loop" };
+ char szEndLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "end" };
+ char szEntry[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "apex" };
+ char szExit[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "end" };
+
+ for ( pkvFaceposer = pKVAllFaceposer->GetFirstSubKey(); pkvFaceposer; pkvFaceposer = pkvFaceposer->GetNextKey() )
+ {
+ if ( !Q_stricmp( pkvFaceposer->GetName(), "startloop" ) )
+ {
+ Q_strncpy( szStartLoop, pkvFaceposer->GetString(), sizeof(szStartLoop) );
+ continue;
+ }
+
+ if ( !Q_stricmp( pkvFaceposer->GetName(), "endloop" ) )
+ {
+ Q_strncpy( szEndLoop, pkvFaceposer->GetString(), sizeof(szEndLoop) );
+ continue;
+ }
+
+ if ( !Q_stricmp( pkvFaceposer->GetName(), "entrytag" ) )
+ {
+ Q_strncpy( szEntry, pkvFaceposer->GetString(), sizeof(szEntry) );
+ continue;
+ }
+
+ if ( !Q_stricmp( pkvFaceposer->GetName(), "exittag" ) )
+ {
+ Q_strncpy( szExit, pkvFaceposer->GetString(), sizeof(szExit) );
+ continue;
+ }
+
+ if ( !Q_stricmp( pkvFaceposer->GetName(), "tags" ) )
+ {
+ if ( nMaxFrame <= 0 )
+ continue;
+
+ KeyValues *pkvTags;
+ for ( pkvTags = pkvFaceposer->GetFirstSubKey(); pkvTags; pkvTags = pkvTags->GetNextKey() )
+ {
+ float flPercentage = (float)pkvTags->GetInt() / nMaxFrame;
+
+ CEventAbsoluteTag *ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, pkvTags->GetName() );
+ if (ptag)
+ {
+ // reposition tag
+ ptag->SetPercentage( flPercentage );
+ }
+ else
+ {
+ e->AddAbsoluteTag( CChoreoEvent::ORIGINAL, pkvTags->GetName(), flPercentage );
+ e->AddAbsoluteTag( CChoreoEvent::PLAYBACK, pkvTags->GetName(), flPercentage );
+ }
+ // lock the original tags so they can't be edited
+ ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, pkvTags->GetName() );
+ Assert( ptag );
+ ptag->SetLocked( true );
+ }
+ e->VerifyTagOrder();
+ e->PreventTagOverlap();
+ continue;
+ }
+ }
+
+ // FIXME: lookup linear tags in sequence data
+ {
+ CEventAbsoluteTag *ptag;
+ ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szStartLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szStartLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szEndLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szEndLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+
+ ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szEntry );
+ if (ptag)
+ {
+ ptag->SetEntry( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szEntry );
+ if (ptag)
+ {
+ ptag->SetEntry( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szExit );
+ if (ptag)
+ {
+ ptag->SetExit( true );
+ }
+ ptag = e->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szExit );
+ if (ptag)
+ {
+ ptag->SetExit( true );
+ }
+ }
+
+ pSeqKeyValues->deleteThis();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool UpdateGestureLength( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ return false;
+
+ int iSequence = LookupSequence( pStudioHdr, e->GetParameters() );
+ if ( iSequence < 0 )
+ return false;
+
+ bool bChanged = false;
+ float flSeqDuration = Studio_Duration( pStudioHdr, iSequence, pPoseParameters );
+ float flCurDuration;
+ e->GetGestureSequenceDuration( flCurDuration );
+ if ( flSeqDuration != 0.0f && flSeqDuration != flCurDuration )
+ {
+ bChanged = true;
+ if ( !bCheckOnly )
+ {
+ e->SetGestureSequenceDuration( flSeqDuration );
+ }
+ }
+
+ return bChanged;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool UpdateSequenceLength( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly, bool bVerbose )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::SEQUENCE )
+ {
+ if ( bVerbose )
+ {
+ ConMsg( "UpdateSequenceLength: called on non-SEQUENCE event %s\n", e->GetName() );
+ }
+ return false;
+ }
+
+ int iSequence = LookupSequence( pStudioHdr, e->GetParameters() );
+ if ( iSequence < 0 )
+ return false;
+
+ bool bChanged = false;
+ bool bLooping = DoesSequenceLoop( pStudioHdr, iSequence );
+ float flSeqDuration = Studio_Duration( pStudioHdr, iSequence, pPoseParameters );
+
+ if ( bLooping )
+ {
+ if ( e->IsFixedLength() )
+ {
+ if ( bCheckOnly )
+ return true;
+
+ if ( bVerbose )
+ {
+ ConMsg( "UpdateSequenceLength: %s is looping, removing fixed length flag\n", e->GetName() );
+ }
+ bChanged = true;
+ }
+ e->SetFixedLength( false );
+
+ if ( !e->HasEndTime() )
+ {
+ if ( bCheckOnly )
+ return true;
+
+ if ( bVerbose )
+ {
+ ConMsg( "CheckSequenceLength: %s is looping, setting default end time\n", e->GetName() );
+ }
+ e->SetEndTime( e->GetStartTime() + flSeqDuration );
+ bChanged = true;
+ }
+
+ return bChanged;
+ }
+
+ if ( !e->IsFixedLength() )
+ {
+ if ( bCheckOnly )
+ return true;
+
+ if ( bVerbose )
+ {
+ ConMsg( "CheckSequenceLength: %s is fixed length, removing looping flag\n", e->GetName() );
+ }
+ bChanged = true;
+ }
+ e->SetFixedLength( true );
+
+ if ( e->HasEndTime() )
+ {
+ float dt = e->GetDuration();
+ if ( fabs( dt - flSeqDuration ) > 0.01f )
+ {
+ if ( bCheckOnly )
+ return true;
+ if ( bVerbose )
+ {
+ ConMsg( "CheckSequenceLength: %s has wrong duration, changing length from %f to %f seconds\n",
+ e->GetName(), dt, flSeqDuration );
+ }
+ bChanged = true;
+ }
+ }
+ else
+ {
+ if ( bCheckOnly )
+ return true;
+ if ( bVerbose )
+ {
+ ConMsg( "CheckSequenceLength: %s has wrong duration, changing length to %f seconds\n",
+ e->GetName(), flSeqDuration );
+ }
+ bChanged = true;
+ }
+
+ if ( !bCheckOnly )
+ {
+ e->SetEndTime( e->GetStartTime() + flSeqDuration );
+ }
+
+ return bChanged;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds sound files associated with events
+//-----------------------------------------------------------------------------
+const char *GetSoundForEvent( CChoreoEvent *pEvent, CStudioHdr *pStudioHdr )
+{
+ const char *pSoundName = pEvent->GetParameters();
+ if ( Q_stristr( pSoundName, ".wav" ) )
+ return PSkipSoundChars( pSoundName );
+
+ const char *pFileName = g_pSoundEmitterSystem->GetWavFileForSound( pSoundName, ( pStudioHdr && pStudioHdr->IsValid() ) ? pStudioHdr->pszName() : NULL );
+ return PSkipSoundChars( pFileName );
+}
diff --git a/tier3/mdlutils.cpp b/tier3/mdlutils.cpp
new file mode 100644
index 0000000..afe8729
--- /dev/null
+++ b/tier3/mdlutils.cpp
@@ -0,0 +1,459 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Utility methods for mdl files
+//
+//===========================================================================//
+
+#include "tier3/mdlutils.h"
+#include "tier0/dbg.h"
+#include "tier1/callqueue.h"
+#include "tier3/tier3.h"
+#include "studio.h"
+#include "istudiorender.h"
+#include "bone_setup.h"
+
+
+//-----------------------------------------------------------------------------
+// Returns the bounding box for the model
+//-----------------------------------------------------------------------------
+void GetMDLBoundingBox( Vector *pMins, Vector *pMaxs, MDLHandle_t h, int nSequence )
+{
+ if ( h == MDLHANDLE_INVALID || !g_pMDLCache )
+ {
+ pMins->Init();
+ pMaxs->Init();
+ return;
+ }
+
+ pMins->Init( FLT_MAX, FLT_MAX );
+ pMaxs->Init( -FLT_MAX, -FLT_MAX );
+
+ studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( h );
+ if ( !VectorCompare( vec3_origin, pStudioHdr->view_bbmin ) || !VectorCompare( vec3_origin, pStudioHdr->view_bbmax ))
+ {
+ // look for view clip
+ *pMins = pStudioHdr->view_bbmin;
+ *pMaxs = pStudioHdr->view_bbmax;
+ }
+ else if ( !VectorCompare( vec3_origin, pStudioHdr->hull_min ) || !VectorCompare( vec3_origin, pStudioHdr->hull_max ))
+ {
+ // look for hull
+ *pMins = pStudioHdr->hull_min;
+ *pMaxs = pStudioHdr->hull_max;
+ }
+
+ // Else use the sequence box
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSequence );
+ VectorMin( seqdesc.bbmin, *pMins, *pMins );
+ VectorMax( seqdesc.bbmax, *pMaxs, *pMaxs );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the radius of the model as measured from the origin
+//-----------------------------------------------------------------------------
+float GetMDLRadius( MDLHandle_t h, int nSequence )
+{
+ Vector vecMins, vecMaxs;
+ GetMDLBoundingBox( &vecMins, &vecMaxs, h, nSequence );
+ float flRadius = vecMaxs.Length();
+ float flRadius2 = vecMins.Length();
+ if ( flRadius2 > flRadius )
+ {
+ flRadius = flRadius2;
+ }
+ return flRadius;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a more accurate bounding sphere
+//-----------------------------------------------------------------------------
+void GetMDLBoundingSphere( Vector *pVecCenter, float *pRadius, MDLHandle_t h, int nSequence )
+{
+ Vector vecMins, vecMaxs;
+ GetMDLBoundingBox( &vecMins, &vecMaxs, h, nSequence );
+ VectorAdd( vecMins, vecMaxs, *pVecCenter );
+ *pVecCenter *= 0.5f;
+ *pRadius = vecMaxs.DistTo( *pVecCenter );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CMDL::CMDL()
+{
+ m_MDLHandle = MDLHANDLE_INVALID;
+ m_Color.SetColor( 255, 255, 255, 255 );
+ m_nSkin = 0;
+ m_nBody = 0;
+ m_nSequence = 0;
+ m_nLOD = 0;
+ m_flPlaybackRate = 30.0f;
+ m_flTime = 0.0f;
+ m_vecViewTarget.Init( 0, 0, 0 );
+ m_bWorldSpaceViewTarget = false;
+ memset( m_pFlexControls, 0, sizeof(m_pFlexControls) );
+ m_pProxyData = NULL;
+}
+
+CMDL::~CMDL()
+{
+ UnreferenceMDL();
+}
+
+void CMDL::SetMDL( MDLHandle_t h )
+{
+ UnreferenceMDL();
+ m_MDLHandle = h;
+ if ( m_MDLHandle != MDLHANDLE_INVALID )
+ {
+ g_pMDLCache->AddRef( m_MDLHandle );
+
+ studiohdr_t *pHdr = g_pMDLCache->LockStudioHdr( m_MDLHandle );
+
+ if ( pHdr )
+ {
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < pHdr->numflexcontrollers; ++i )
+ {
+ if ( pHdr->pFlexcontroller( i )->localToGlobal == -1 )
+ {
+ pHdr->pFlexcontroller( i )->localToGlobal = i;
+ }
+ }
+ }
+ }
+}
+
+MDLHandle_t CMDL::GetMDL() const
+{
+ return m_MDLHandle;
+}
+
+
+//-----------------------------------------------------------------------------
+// Release the MDL handle
+//-----------------------------------------------------------------------------
+void CMDL::UnreferenceMDL()
+{
+ if ( !g_pMDLCache )
+ return;
+
+ if ( m_MDLHandle != MDLHANDLE_INVALID )
+ {
+ // XXX need to figure out where it is safe to flush the queue during map change to not crash
+#if 0
+ if ( ICallQueue *pCallQueue = materials->GetRenderContext()->GetCallQueue() )
+ {
+ // Parallel rendering: don't unlock model data until end of rendering
+ pCallQueue->QueueCall( g_pMDLCache, &IMDLCache::UnlockStudioHdr, m_MDLHandle );
+ pCallQueue->QueueCall( g_pMDLCache, &IMDLCache::Release, m_MDLHandle );
+ }
+ else
+#endif
+ {
+ // Immediate-mode rendering, can unlock immediately
+ g_pMDLCache->UnlockStudioHdr( m_MDLHandle );
+ g_pMDLCache->Release( m_MDLHandle );
+ }
+ m_MDLHandle = MDLHANDLE_INVALID;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the studiohdr
+//-----------------------------------------------------------------------------
+studiohdr_t *CMDL::GetStudioHdr()
+{
+ if ( !g_pMDLCache )
+ return NULL;
+ return g_pMDLCache->GetStudioHdr( m_MDLHandle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CMDL::Draw( const matrix3x4_t& rootToWorld, const matrix3x4_t *pBoneToWorld )
+{
+ if ( !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender )
+ return;
+
+ if ( m_MDLHandle == MDLHANDLE_INVALID )
+ return;
+
+ // Color + alpha modulation
+ Vector white( m_Color.r() / 255.0f, m_Color.g() / 255.0f, m_Color.b() / 255.0f );
+ g_pStudioRender->SetColorModulation( white.Base() );
+ g_pStudioRender->SetAlphaModulation( m_Color.a() / 255.0f );
+
+ DrawModelInfo_t info;
+ info.m_pStudioHdr = g_pMDLCache->GetStudioHdr( m_MDLHandle );
+ info.m_pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle );
+ info.m_Decals = STUDIORENDER_DECAL_INVALID;
+ info.m_Skin = m_nSkin;
+ info.m_Body = m_nBody;
+ info.m_HitboxSet = 0;
+ info.m_pClientEntity = m_pProxyData;
+ info.m_pColorMeshes = NULL;
+ info.m_bStaticLighting = false;
+ info.m_Lod = m_nLOD;
+
+ Vector vecWorldViewTarget;
+ if ( m_bWorldSpaceViewTarget )
+ {
+ vecWorldViewTarget = m_vecViewTarget;
+ }
+ else
+ {
+ VectorTransform( m_vecViewTarget, rootToWorld, vecWorldViewTarget );
+ }
+ g_pStudioRender->SetEyeViewTarget( info.m_pStudioHdr, info.m_Body, vecWorldViewTarget );
+
+ // FIXME: Why is this necessary!?!?!?
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ // Set default flex values
+ float *pFlexWeights = NULL;
+ const int nFlexDescCount = info.m_pStudioHdr->numflexdesc;
+ if ( nFlexDescCount )
+ {
+ CStudioHdr cStudioHdr( info.m_pStudioHdr, g_pMDLCache );
+
+ g_pStudioRender->LockFlexWeights( info.m_pStudioHdr->numflexdesc, &pFlexWeights );
+ cStudioHdr.RunFlexRules( m_pFlexControls, pFlexWeights );
+ g_pStudioRender->UnlockFlexWeights();
+ }
+
+ Vector vecModelOrigin;
+ MatrixGetColumn( rootToWorld, 3, vecModelOrigin );
+ g_pStudioRender->DrawModel( NULL, info, const_cast<matrix3x4_t*>( pBoneToWorld ),
+ pFlexWeights, NULL, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL );
+}
+
+void CMDL::Draw( const matrix3x4_t &rootToWorld )
+{
+ if ( !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender )
+ return;
+
+ if ( m_MDLHandle == MDLHANDLE_INVALID )
+ return;
+
+ studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( m_MDLHandle );
+
+ matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( pStudioHdr->numbones );
+ SetUpBones( rootToWorld, pStudioHdr->numbones, pBoneToWorld );
+ g_pStudioRender->UnlockBoneMatrices();
+
+ Draw( rootToWorld, pBoneToWorld );
+}
+
+
+void CMDL::SetUpBones( const matrix3x4_t& rootToWorld, int nMaxBoneCount, matrix3x4_t *pBoneToWorld, const float *pPoseParameters, MDLSquenceLayer_t *pSequenceLayers, int nNumSequenceLayers )
+{
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache );
+
+ float pPoseParameter[MAXSTUDIOPOSEPARAM];
+ if ( pPoseParameters )
+ {
+ V_memcpy( pPoseParameter, pPoseParameters, sizeof(pPoseParameter) );
+ }
+ else
+ {
+ // Default to middle of the pose parameter range
+ int nPoseCount = studioHdr.GetNumPoseParameters();
+ for ( int i = 0; i < MAXSTUDIOPOSEPARAM; ++i )
+ {
+ pPoseParameter[i] = 0.5f;
+ if ( i < nPoseCount )
+ {
+ const mstudioposeparamdesc_t &Pose = studioHdr.pPoseParameter( i );
+
+ // Want to try for a zero state. If one doesn't exist set it to .5 by default.
+ if ( Pose.start < 0.0f && Pose.end > 0.0f )
+ {
+ float flPoseDelta = Pose.end - Pose.start;
+ pPoseParameter[i] = -Pose.start / flPoseDelta;
+ }
+ }
+ }
+ }
+
+ int nFrameCount = Studio_MaxFrame( &studioHdr, m_nSequence, pPoseParameter );
+ if ( nFrameCount == 0 )
+ {
+ nFrameCount = 1;
+ }
+ float flCycle = ( m_flTime * m_flPlaybackRate ) / nFrameCount;
+
+ // FIXME: We're always wrapping; may want to determing if we should clamp
+ flCycle -= (int)(flCycle);
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ IBoneSetup boneSetup( &studioHdr, BONE_USED_BY_ANYTHING_AT_LOD( m_nLOD ), pPoseParameter, NULL );
+ boneSetup.InitPose( pos, q );
+ boneSetup.AccumulatePose( pos, q, m_nSequence, flCycle, 1.0f, m_flTime, NULL );
+
+ // Accumulate the additional layers if specified.
+ if ( pSequenceLayers )
+ {
+ int nNumSeq = studioHdr.GetNumSeq();
+ for ( int i = 0; i < nNumSequenceLayers; ++i )
+ {
+ int nSeqIndex = pSequenceLayers[ i ].m_nSequenceIndex;
+ if ( ( nSeqIndex >= 0 ) && ( nSeqIndex < nNumSeq ) )
+ {
+ float flWeight = pSequenceLayers[ i ].m_flWeight;
+
+ float flLayerCycle;
+ int nLayerFrameCount = MAX( 1, Studio_MaxFrame( &studioHdr, nSeqIndex, pPoseParameter ) );
+
+ if ( pSequenceLayers[i].m_bNoLoop )
+ {
+ if ( pSequenceLayers[i].m_flCycleBeganAt == 0 )
+ {
+ pSequenceLayers[i].m_flCycleBeganAt = m_flTime;
+ }
+
+ float flElapsedTime = m_flTime - pSequenceLayers[i].m_flCycleBeganAt;
+ flLayerCycle = ( flElapsedTime * m_flPlaybackRate ) / nLayerFrameCount;
+
+ // Should we keep playing layers that have ended?
+ //if ( flLayerCycle >= 1.0 )
+ //continue;
+ }
+ else
+ {
+ flLayerCycle = ( m_flTime * m_flPlaybackRate ) / nLayerFrameCount;
+
+ // FIXME: We're always wrapping; may want to determing if we should clamp
+ flLayerCycle -= (int)(flLayerCycle);
+ }
+
+ boneSetup.AccumulatePose( pos, q, nSeqIndex, flLayerCycle, flWeight, m_flTime, NULL );
+ }
+ }
+ }
+
+ // FIXME: Try enabling this?
+ // CalcAutoplaySequences( pStudioHdr, NULL, pos, q, pPoseParameter, BONE_USED_BY_VERTEX_AT_LOD( m_nLOD ), flTime );
+
+ matrix3x4_t temp;
+
+ if ( nMaxBoneCount > studioHdr.numbones() )
+ {
+ nMaxBoneCount = studioHdr.numbones();
+ }
+
+ for ( int i = 0; i < nMaxBoneCount; i++ )
+ {
+ // If it's not being used, fill with NAN for errors
+#ifdef _DEBUG
+ if ( !(studioHdr.pBone( i )->flags & BONE_USED_BY_ANYTHING_AT_LOD( m_nLOD ) ) )
+ {
+ int j, k;
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ pBoneToWorld[i][j][k] = VEC_T_NAN;
+ }
+ }
+ continue;
+ }
+#endif
+
+ matrix3x4_t boneMatrix;
+ QuaternionMatrix( q[i], boneMatrix );
+ MatrixSetColumn( pos[i], 3, boneMatrix );
+
+ if ( studioHdr.pBone(i)->parent == -1 )
+ {
+ ConcatTransforms( rootToWorld, boneMatrix, pBoneToWorld[i] );
+ }
+ else
+ {
+ ConcatTransforms( pBoneToWorld[ studioHdr.pBone(i)->parent ], boneMatrix, pBoneToWorld[i] );
+ }
+ }
+ Studio_RunBoneFlexDrivers( m_pFlexControls, &studioHdr, pos, pBoneToWorld, rootToWorld );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMDL::SetupBonesWithBoneMerge( const CStudioHdr *pMergeHdr, matrix3x4_t *pMergeBoneToWorld,
+ const CStudioHdr *pFollow, const matrix3x4_t *pFollowBoneToWorld,
+ const matrix3x4_t &matModelToWorld )
+{
+ // Default to middle of the pose parameter range
+ int nPoseCount = pMergeHdr->GetNumPoseParameters();
+ float pPoseParameter[MAXSTUDIOPOSEPARAM];
+ for ( int i = 0; i < MAXSTUDIOPOSEPARAM; ++i )
+ {
+ pPoseParameter[i] = 0.5f;
+ if ( i < nPoseCount )
+ {
+ const mstudioposeparamdesc_t &Pose = ((CStudioHdr *)pMergeHdr)->pPoseParameter( i );
+
+ // Want to try for a zero state. If one doesn't exist set it to .5 by default.
+ if ( Pose.start < 0.0f && Pose.end > 0.0f )
+ {
+ float flPoseDelta = Pose.end - Pose.start;
+ pPoseParameter[i] = -Pose.start / flPoseDelta;
+ }
+ }
+ }
+
+ int nFrameCount = Studio_MaxFrame( pMergeHdr, m_nSequence, pPoseParameter );
+ if ( nFrameCount == 0 )
+ {
+ nFrameCount = 1;
+ }
+ float flCycle = ( m_flTime * m_flPlaybackRate ) / nFrameCount;
+
+ // FIXME: We're always wrapping; may want to determing if we should clamp
+ flCycle -= (int)(flCycle);
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ IBoneSetup boneSetup( pMergeHdr, BONE_USED_BY_ANYTHING_AT_LOD( m_nLOD ), pPoseParameter );
+ boneSetup.InitPose( pos, q );
+ boneSetup.AccumulatePose( pos, q, m_nSequence, flCycle, 1.0f, m_flTime, NULL );
+
+ // Get the merge bone list.
+ mstudiobone_t *pMergeBones = pMergeHdr->pBone( 0 );
+ for ( int iMergeBone = 0; iMergeBone < pMergeHdr->numbones(); ++iMergeBone )
+ {
+ // Now find the bone in the parent entity.
+ bool bMerged = false;
+ int iParentBoneIndex = Studio_BoneIndexByName( pFollow, pMergeBones[iMergeBone].pszName() );
+ if ( iParentBoneIndex >= 0 )
+ {
+ MatrixCopy( pFollowBoneToWorld[iParentBoneIndex], pMergeBoneToWorld[iMergeBone] );
+ bMerged = true;
+ }
+
+ if ( !bMerged )
+ {
+ // If we get down here, then the bone wasn't merged.
+ matrix3x4_t matBone;
+ QuaternionMatrix( q[iMergeBone], pos[iMergeBone], matBone );
+
+ if ( pMergeBones[iMergeBone].parent == -1 )
+ {
+ ConcatTransforms( matModelToWorld, matBone, pMergeBoneToWorld[iMergeBone] );
+ }
+ else
+ {
+ ConcatTransforms( pMergeBoneToWorld[pMergeBones[iMergeBone].parent], matBone, pMergeBoneToWorld[iMergeBone] );
+ }
+ }
+ }
+}
+
diff --git a/tier3/scenetokenprocessor.cpp b/tier3/scenetokenprocessor.cpp
new file mode 100644
index 0000000..b1730a5
--- /dev/null
+++ b/tier3/scenetokenprocessor.cpp
@@ -0,0 +1,201 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "../game/shared/choreoscene.h"
+#include "../game/shared/choreoactor.h"
+#include "../game/shared/choreochannel.h"
+#include "../game/shared/choreoevent.h"
+#include "../game/shared/iscenetokenprocessor.h"
+#include "characterset.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for parsing scene data file
+//-----------------------------------------------------------------------------
+class CSceneTokenProcessor : public ISceneTokenProcessor
+{
+public:
+ CSceneTokenProcessor();
+
+ const char *CurrentToken( void );
+ bool GetToken( bool crossline );
+ bool TokenAvailable( void );
+ void Error( const char *fmt, ... );
+ void SetBuffer( char *buffer );
+private:
+
+ const char *ParseNextToken (const char *data);
+
+ const char *m_pBuffer;
+ char m_szToken[ 1024 ];
+
+ characterset_t m_BreakSetIncludingColons;
+};
+
+CSceneTokenProcessor::CSceneTokenProcessor()
+{
+ CharacterSetBuild( &m_BreakSetIncludingColons, "{}()':" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CSceneTokenProcessor::CurrentToken( void )
+{
+ return m_szToken;
+}
+
+const char *CSceneTokenProcessor::ParseNextToken (const char *data)
+{
+ unsigned char c;
+ int len;
+ characterset_t *breaks;
+
+ breaks = &m_BreakSetIncludingColons;
+
+ len = 0;
+ m_szToken[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ return NULL; // end of file;
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ while (1)
+ {
+ c = *data++;
+ if (c=='\"' || !c)
+ {
+ m_szToken[len] = 0;
+ return data;
+ }
+ m_szToken[len] = c;
+ len++;
+ }
+ }
+
+// parse single characters
+ if ( IN_CHARACTERSET( *breaks, c ) )
+ {
+ m_szToken[len] = c;
+ len++;
+ m_szToken[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ m_szToken[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if ( IN_CHARACTERSET( *breaks, c ) )
+ break;
+ } while (c>32);
+
+ m_szToken[len] = 0;
+ return data;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : crossline -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneTokenProcessor::GetToken( bool crossline )
+{
+ // NOTE: crossline is ignored here, may need to implement if needed
+ m_pBuffer = ParseNextToken( m_pBuffer );
+ if ( m_szToken[0] )
+ return true;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneTokenProcessor::TokenAvailable( void )
+{
+ const char *search_p = m_pBuffer;
+
+ while ( *search_p <= 32)
+ {
+ if (*search_p == '\n')
+ return false;
+ search_p++;
+ if ( !*search_p )
+ return false;
+
+ }
+
+ if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field
+ (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CSceneTokenProcessor::Error( const char *fmt, ... )
+{
+ char string[ 2048 ];
+ va_list argptr;
+ va_start( argptr, fmt );
+ Q_vsnprintf( string, sizeof(string), fmt, argptr );
+ va_end( argptr );
+
+ Warning( "%s", string );
+ Assert(0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *buffer -
+//-----------------------------------------------------------------------------
+void CSceneTokenProcessor::SetBuffer( char *buffer )
+{
+ m_pBuffer = buffer;
+}
+
+CSceneTokenProcessor g_TokenProcessor;
+
+ISceneTokenProcessor *GetTokenProcessor()
+{
+ return &g_TokenProcessor;
+}
+
+void SetTokenProcessorBuffer( const char *buf )
+{
+ g_TokenProcessor.SetBuffer( (char *)buf );
+}
+
diff --git a/tier3/studiohdrstub.cpp b/tier3/studiohdrstub.cpp
new file mode 100644
index 0000000..44d05ad
--- /dev/null
+++ b/tier3/studiohdrstub.cpp
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+//#include "studio.h"
+#include "studio.h"
+#include "datacache/imdlcache.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "istudiorender.h"
+#include "bone_setup.h"
+#include "tier3/tier3.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// FIXME: This trashy glue code is really not acceptable. Figure out a way of making it unnecessary.
+//-----------------------------------------------------------------------------
+const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const
+{
+ MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName );
+ *cache = (void*)(uintp)handle;
+ return g_pMDLCache->GetStudioHdr( handle );
+}
+
+virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
+{
+ return g_pMDLCache->GetVirtualModel( (MDLHandle_t)((int)virtualModel&0xffff) );
+}
+
+byte *studiohdr_t::GetAnimBlock( int i ) const
+{
+ return g_pMDLCache->GetAnimBlock( (MDLHandle_t)((int)virtualModel&0xffff), i );
+}
+
+int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
+{
+ return g_pMDLCache->GetAutoplayList( (MDLHandle_t)((int)virtualModel&0xffff), pOut );
+}
+
+const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( (MDLHandle_t)((int)cache&0xffff) );
+}
+
diff --git a/tier3/tier3.cpp b/tier3/tier3.cpp
new file mode 100644
index 0000000..20241a6
--- /dev/null
+++ b/tier3/tier3.cpp
@@ -0,0 +1,154 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A higher level link library for general use in the game and tools.
+//
+//===========================================================================//
+
+#include "tier3/tier3.h"
+#include "tier0/dbg.h"
+#include "istudiorender.h"
+#include "vgui/IVGui.h"
+#include "vgui/IInput.h"
+#include "vgui/IPanel.h"
+#include "vgui/ISurface.h"
+#include "vgui/ILocalize.h"
+#include "vgui/IScheme.h"
+#include "vgui/ISystem.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "datacache/idatacache.h"
+#include "datacache/imdlcache.h"
+#include "video/ivideoservices.h"
+#include "movieobjects/idmemakefileutils.h"
+#include "vphysics_interface.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "ivtex.h"
+
+
+//-----------------------------------------------------------------------------
+// These tier3 libraries must be set by any users of this library.
+// They can be set by calling ConnectTier3Libraries.
+// It is hoped that setting this, and using this library will be the common mechanism for
+// allowing link libraries to access tier3 library interfaces
+//-----------------------------------------------------------------------------
+IStudioRender *g_pStudioRender = 0;
+IStudioRender *studiorender = 0;
+IMatSystemSurface *g_pMatSystemSurface = 0;
+vgui::IInput *g_pVGuiInput = 0;
+vgui::ISurface *g_pVGuiSurface = 0;
+vgui::IPanel *g_pVGuiPanel = 0;
+vgui::IVGui *g_pVGui = 0;
+vgui::ILocalize *g_pVGuiLocalize = 0;
+vgui::ISchemeManager *g_pVGuiSchemeManager = 0;
+vgui::ISystem *g_pVGuiSystem = 0;
+IDataCache *g_pDataCache = 0;
+IMDLCache *g_pMDLCache = 0;
+IMDLCache *mdlcache = 0;
+IVideoServices *g_pVideo = NULL;
+IDmeMakefileUtils *g_pDmeMakefileUtils = 0;
+IPhysicsCollision *g_pPhysicsCollision = 0;
+ISoundEmitterSystemBase *g_pSoundEmitterSystem = 0;
+IVTex *g_pVTex = 0;
+
+
+//-----------------------------------------------------------------------------
+// Call this to connect to all tier 3 libraries.
+// It's up to the caller to check the globals it cares about to see if ones are missing
+//-----------------------------------------------------------------------------
+void ConnectTier3Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount )
+{
+ // Don't connect twice..
+ Assert( !g_pStudioRender && !studiorender && !g_pMatSystemSurface && !g_pVGui && !g_pVGuiPanel && !g_pVGuiInput &&
+ !g_pVGuiSurface && !g_pDataCache && !g_pMDLCache && !mdlcache && !g_pVideo &&
+ !g_pDmeMakefileUtils && !g_pPhysicsCollision && !g_pVGuiLocalize && !g_pSoundEmitterSystem &&
+ !g_pVGuiSchemeManager && !g_pVGuiSystem );
+
+ for ( int i = 0; i < nFactoryCount; ++i )
+ {
+ if ( !g_pStudioRender )
+ {
+ g_pStudioRender = studiorender = ( IStudioRender * )pFactoryList[i]( STUDIO_RENDER_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGui )
+ {
+ g_pVGui = (vgui::IVGui*)pFactoryList[i]( VGUI_IVGUI_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiInput )
+ {
+ g_pVGuiInput = (vgui::IInput*)pFactoryList[i]( VGUI_INPUT_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiPanel )
+ {
+ g_pVGuiPanel = (vgui::IPanel*)pFactoryList[i]( VGUI_PANEL_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiSurface )
+ {
+ g_pVGuiSurface = (vgui::ISurface*)pFactoryList[i]( VGUI_SURFACE_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiSchemeManager )
+ {
+ g_pVGuiSchemeManager = (vgui::ISchemeManager*)pFactoryList[i]( VGUI_SCHEME_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiSystem )
+ {
+ g_pVGuiSystem = (vgui::ISystem*)pFactoryList[i]( VGUI_SYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVGuiLocalize )
+ {
+ g_pVGuiLocalize = (vgui::ILocalize*)pFactoryList[i]( VGUI_LOCALIZE_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pMatSystemSurface )
+ {
+ g_pMatSystemSurface = ( IMatSystemSurface * )pFactoryList[i]( MAT_SYSTEM_SURFACE_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pDataCache )
+ {
+ g_pDataCache = (IDataCache*)pFactoryList[i]( DATACACHE_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pMDLCache )
+ {
+ g_pMDLCache = mdlcache = (IMDLCache*)pFactoryList[i]( MDLCACHE_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVideo )
+ {
+ g_pVideo = (IVideoServices *)pFactoryList[i](VIDEO_SERVICES_INTERFACE_VERSION, NULL);
+ }
+ if ( !g_pDmeMakefileUtils )
+ {
+ g_pDmeMakefileUtils = (IDmeMakefileUtils*)pFactoryList[i]( DMEMAKEFILE_UTILS_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pPhysicsCollision )
+ {
+ g_pPhysicsCollision = ( IPhysicsCollision* )pFactoryList[i]( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pSoundEmitterSystem )
+ {
+ g_pSoundEmitterSystem = ( ISoundEmitterSystemBase* )pFactoryList[i]( SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pVTex )
+ {
+ g_pVTex = ( IVTex * )pFactoryList[i]( IVTEX_VERSION_STRING, NULL );
+ }
+ }
+}
+
+void DisconnectTier3Libraries()
+{
+ g_pStudioRender = 0;
+ studiorender = 0;
+ g_pVGui = 0;
+ g_pVGuiInput = 0;
+ g_pVGuiPanel = 0;
+ g_pVGuiSurface = 0;
+ g_pVGuiLocalize = 0;
+ g_pVGuiSchemeManager = 0;
+ g_pVGuiSystem = 0;
+ g_pMatSystemSurface = 0;
+ g_pDataCache = 0;
+ g_pMDLCache = 0;
+ mdlcache = 0;
+ g_pVideo = NULL;
+ g_pPhysicsCollision = 0;
+ g_pDmeMakefileUtils = NULL;
+ g_pSoundEmitterSystem = 0;
+ g_pVTex = NULL;
+}
diff --git a/tier3/tier3.vpc b/tier3/tier3.vpc
new file mode 100644
index 0000000..ccec731
--- /dev/null
+++ b/tier3/tier3.vpc
@@ -0,0 +1,29 @@
+//-----------------------------------------------------------------------------
+// TIER3.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR ".."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Project "tier3"
+{
+ $Folder "Source Files"
+ {
+ $File "tier3.cpp"
+ $File "mdlutils.cpp"
+ $File "choreoutils.cpp"
+ $File "scenetokenprocessor.cpp"
+ $File "studiohdrstub.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\tier3\tier3.h"
+ $File "$SRCDIR\public\tier3\mdlutils.h"
+ $File "$SRCDIR\public\tier3\choreoutils.h"
+ $File "$SRCDIR\public\tier3\scenetokenprocessor.h"
+ }
+}