summaryrefslogtreecommitdiff
path: root/hammer/studiomodel.cpp
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 /hammer/studiomodel.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/studiomodel.cpp')
-rw-r--r--hammer/studiomodel.cpp1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/hammer/studiomodel.cpp b/hammer/studiomodel.cpp
new file mode 100644
index 0000000..a16735f
--- /dev/null
+++ b/hammer/studiomodel.cpp
@@ -0,0 +1,1175 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "stdafx.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include "mapdoc.h"
+#include "MapWorld.h"
+#include "Material.h"
+#include "Render2D.h"
+#include "Render3D.h"
+#include "StudioModel.h"
+#include "ViewerSettings.h"
+#include "materialsystem/imesh.h"
+#include "TextureSystem.h"
+#include "bone_setup.h"
+#include "IStudioRender.h"
+#include "GlobalFunctions.h"
+#include "UtlMemory.h"
+#include "utldict.h"
+#include "bone_accessor.h"
+#include "optimize.h"
+#include "filesystem.h"
+#include "Hammer.h"
+#include "HammerVGui.h"
+#include <VGuiMatSurface/IMatSystemSurface.h>
+#include "mapview2d.h"
+#include "mapdefs.h"
+#include "camera.h"
+#include "options.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+#pragma warning(disable : 4244) // double to float
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Monitors the filesystem for changes to model files and flushes
+// any stuff in memory for the model if necessary.
+//-----------------------------------------------------------------------------
+class CStudioFileChangeWatcher : private CFileChangeWatcher::ICallbacks
+{
+public:
+ void Init();
+ void Update(); // Call this periodically to update.
+
+private:
+ // CFileChangeWatcher::ICallbacks..
+ virtual void OnFileChange( const char *pRelativeFilename, const char *pFullFilename );
+
+private:
+ CFileChangeWatcher m_Watcher;
+ CUtlDict<int,int> m_ChangedModels;
+};
+static CStudioFileChangeWatcher g_StudioFileChangeWatcher;
+
+
+
+Vector g_lightvec; // light vector in model reference frame
+Vector g_blightvec[MAXSTUDIOBONES]; // light vectors in bone reference frames
+int g_ambientlight; // ambient world light
+float g_shadelight; // direct world light
+Vector g_lightcolor;
+bool g_bUpdateBones2D = true;
+
+//-----------------------------------------------------------------------------
+// Model meshes themselves are cached to avoid redundancy. There should never be
+// more than one copy of a given studio model in memory at once.
+//-----------------------------------------------------------------------------
+ModelCache_t CStudioModelCache::m_Cache[1024];
+int CStudioModelCache::m_nItems = 0;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Find a model in the cache. Returns null if it's not in the cache.
+//-----------------------------------------------------------------------------
+StudioModel *CStudioModelCache::FindModel(const char *pszModelPath)
+{
+ char testPath[MAX_PATH];
+ V_strncpy( testPath, pszModelPath, sizeof( testPath ) );
+ V_FixSlashes( testPath );
+
+ //
+ // First look for the model in the cache. If it's there, increment the
+ // reference count and return a pointer to the cached model.
+ //
+ for (int i = 0; i < m_nItems; i++)
+ {
+ char testPath2[MAX_PATH];
+ V_strncpy( testPath2, m_Cache[i].pszPath, sizeof( testPath2 ) );
+ V_FixSlashes( testPath2 );
+
+ if (!stricmp(testPath, testPath2))
+ {
+ m_Cache[i].nRefCount++;
+ return(m_Cache[i].pModel);
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns an instance of a particular studio model. If the model is
+// in the cache, a pointer to that model is returned. If not, a new one
+// is created and added to the cache.
+// Input : pszModelPath - Full path of the .MDL file.
+//-----------------------------------------------------------------------------
+StudioModel *CStudioModelCache::CreateModel(const char *pszModelPath)
+{
+ StudioModel *pTest = FindModel( pszModelPath );
+ if ( pTest )
+ return pTest;
+
+ //
+ // If it isn't there, try to create one.
+ //
+ StudioModel *pModel = new StudioModel;
+
+ if (pModel != NULL)
+ {
+ bool bLoaded = pModel->LoadModel(pszModelPath);
+
+ if (bLoaded)
+ {
+ bLoaded = pModel->PostLoadModel(pszModelPath);
+ }
+
+ if (!bLoaded)
+ {
+ delete pModel;
+ pModel = NULL;
+ }
+ }
+
+ //
+ // If we successfully created it, add it to the cache.
+ //
+ if (pModel != NULL)
+ {
+ CStudioModelCache::AddModel(pModel, pszModelPath);
+ }
+
+ return(pModel);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the model to the cache, setting the reference count to one.
+// Input : pModel - Model to add to the cache.
+// pszModelPath - The full path of the .MDL file, which is used as a
+// key in the model cache.
+// Output : Returns TRUE if the model was successfully added, FALSE if we ran
+// out of memory trying to add the model to the cache.
+//-----------------------------------------------------------------------------
+BOOL CStudioModelCache::AddModel(StudioModel *pModel, const char *pszModelPath)
+{
+ //
+ // Copy the model pointer.
+ //
+ m_Cache[m_nItems].pModel = pModel;
+
+ //
+ // Allocate space for and copy the model path.
+ //
+ m_Cache[m_nItems].pszPath = new char [strlen(pszModelPath) + 1];
+ if (m_Cache[m_nItems].pszPath != NULL)
+ {
+ strcpy(m_Cache[m_nItems].pszPath, pszModelPath);
+ }
+ else
+ {
+ return(FALSE);
+ }
+
+ m_Cache[m_nItems].nRefCount = 1;
+
+ m_nItems++;
+
+ return(TRUE);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Advances the animation of all models in the cache for the given interval.
+// Input : flInterval - delta time in seconds.
+//-----------------------------------------------------------------------------
+void CStudioModelCache::AdvanceAnimation(float flInterval)
+{
+ for (int i = 0; i < m_nItems; i++)
+ {
+ m_Cache[i].pModel->AdvanceFrame(flInterval);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Increments the reference count on a model in the cache. Called by
+// client code when a pointer to the model is copied, making that
+// reference independent.
+// Input : pModel - Model for which to increment the reference count.
+//-----------------------------------------------------------------------------
+void CStudioModelCache::AddRef(StudioModel *pModel)
+{
+ for (int i = 0; i < m_nItems; i++)
+ {
+ if (m_Cache[i].pModel == pModel)
+ {
+ m_Cache[i].nRefCount++;
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called by client code to release an instance of a model. If the
+// model's reference count is zero, the model is freed.
+// Input : pModel - Pointer to the model to release.
+//-----------------------------------------------------------------------------
+void CStudioModelCache::Release(StudioModel *pModel)
+{
+ for (int i = 0; i < m_nItems; i++)
+ {
+ if (m_Cache[i].pModel == pModel)
+ {
+ m_Cache[i].nRefCount--;
+ Assert(m_Cache[i].nRefCount >= 0);
+
+ //
+ // If this model is no longer referenced, free it and remove it
+ // from the cache.
+ //
+ if (m_Cache[i].nRefCount <= 0)
+ {
+ //
+ // Free the path, which was allocated by AddModel.
+ //
+ delete [] m_Cache[i].pszPath;
+ delete m_Cache[i].pModel;
+
+ //
+ // Decrement the item count and copy the last element in the cache over
+ // this element.
+ //
+ m_nItems--;
+
+ m_Cache[i].pModel = m_Cache[m_nItems].pModel;
+ m_Cache[i].pszPath = m_Cache[m_nItems].pszPath;
+ m_Cache[i].nRefCount = m_Cache[m_nItems].nRefCount;
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Watch for changes to studio models and reload them if necessary.
+//-----------------------------------------------------------------------------
+void CStudioFileChangeWatcher::Init()
+{
+ m_Watcher.Init( this );
+
+ char searchPaths[1024 * 16];
+ if ( g_pFullFileSystem->GetSearchPath( "GAME", false, searchPaths, sizeof( searchPaths ) ) > 0 )
+ {
+ CUtlVector<char*> searchPathList;
+ V_SplitString( searchPaths, ";", searchPathList );
+
+ for ( int i=0; i < searchPathList.Count(); i++ )
+ {
+ m_Watcher.AddDirectory( searchPathList[i], "models", true );
+ }
+
+ searchPathList.PurgeAndDeleteElements();
+ }
+ else
+ {
+ Warning( "Error in GetSearchPath. Hammer will not automatically reload modified models." );
+ }
+}
+
+void CStudioFileChangeWatcher::OnFileChange( const char *pRelativeFilename, const char *pFullFilename )
+{
+ char relativeFilename[MAX_PATH];
+ V_ComposeFileName( "models", pRelativeFilename, relativeFilename, sizeof( relativeFilename ) );
+ V_FixSlashes( relativeFilename );
+
+ // Check the cache.
+ const char *pExt = V_GetFileExtension( relativeFilename );
+ if ( !pExt )
+ return;
+
+ if ( V_stricmp( pExt, "mdl" ) == 0 ||
+ V_stricmp( pExt, "vtx" ) == 0 ||
+ V_stricmp( pExt, "phy" ) == 0 ||
+ V_stricmp( pExt, "vvd" ) == 0 )
+ {
+ // Ok, it's at least related to a model. Flush out the model.
+ char tempFilename[MAX_PATH];
+ V_strncpy( tempFilename, relativeFilename, pExt - relativeFilename );
+
+ // Now it might have a "dx80" or "dx90" or some other extension. Get rid of that too.
+ const char *pTestFilename = V_UnqualifiedFileName( tempFilename );
+ pExt = V_GetFileExtension( pTestFilename );
+ char filename[MAX_PATH];
+ if ( pExt )
+ V_strncpy( filename, tempFilename, pExt - tempFilename );
+ else
+ V_strncpy( filename, tempFilename, sizeof( filename ) );
+
+ // Now we've got the filename with any extension or "dx80"-type stuff at the end.
+ V_strncat( filename, ".mdl", sizeof( filename ) );
+
+ // Queue up the list of changes because if they copied all the files for a model,
+ // we'd like to only reload it once.
+ if ( m_ChangedModels.Find( filename ) == m_ChangedModels.InvalidIndex() )
+ m_ChangedModels.Insert( filename );
+ }
+}
+
+void CStudioFileChangeWatcher::Update()
+{
+ if ( !g_pMDLCache )
+ return;
+
+ m_Watcher.Update();
+
+ if ( m_ChangedModels.Count() > 0 )
+ {
+ // Reload whatever models were changed.
+ for ( int i=m_ChangedModels.First(); i != m_ChangedModels.InvalidIndex(); i=m_ChangedModels.Next( i ) )
+ {
+ const char *pName = m_ChangedModels.GetElementName( i );
+
+ MDLHandle_t hModel = g_pMDLCache->FindMDL( pName );
+ g_pMDLCache->Flush( hModel );
+ g_pMDLCache->ResetErrorModelStatus( hModel );
+
+ // If we have it in the StudioModel cache, flush its data.
+ StudioModel *pTest = CStudioModelCache::FindModel( pName );
+ if ( pTest )
+ {
+ pTest->FreeModel();
+ pTest->LoadModel( pName );
+ }
+ }
+
+ m_ChangedModels.Purge();
+
+ for ( int i=0; i < CMapDoc::GetDocumentCount(); i++ )
+ {
+ CMapDoc *pDoc = CMapDoc::GetDocument( i );
+ pDoc->GetMapWorld()->CalcBounds( true );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads up the IStudioRender interface.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool StudioModel::Initialize()
+{
+ return true;
+}
+
+
+void StudioModel::Shutdown( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+StudioModel::StudioModel(void) : m_pModelName(0)
+{
+ int i;
+
+ m_origin.Init();
+ m_angles.Init();
+ m_sequence = 0;
+ m_cycle = 0;
+ m_bodynum = 0;
+ m_skinnum = 0;
+
+ for (i = 0; i < sizeof(m_controller) / sizeof(m_controller[0]); i++)
+ {
+ m_controller[i] = 0;
+ }
+
+ for (i = 0; i < sizeof(m_poseParameter) / sizeof(m_poseParameter[0]); i++)
+ {
+ m_poseParameter[i] = 0;
+ }
+
+ m_mouth = 0;
+
+ m_MDLHandle = MDLHANDLE_INVALID;
+ m_pModel = NULL;
+ m_pStudioHdr = NULL;
+ m_pPosePos = NULL;
+ m_pPoseAng = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Frees dynamically allocated data.
+//-----------------------------------------------------------------------------
+StudioModel::~StudioModel(void)
+{
+ FreeModel();
+ if (m_pModelName)
+ {
+ delete[] m_pModelName;
+ }
+ delete m_pStudioHdr;
+
+ delete []m_pPosePos;
+ delete []m_pPoseAng;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the Euler angles for the model.
+// Input : fAngles - A pointer to engine PITCH, YAW, and ROLL angles.
+//-----------------------------------------------------------------------------
+void StudioModel::SetAngles(QAngle& pfAngles)
+{
+ m_angles[PITCH] = pfAngles[PITCH];
+ m_angles[YAW] = pfAngles[YAW];
+ m_angles[ROLL] = pfAngles[ROLL];
+}
+
+
+void StudioModel::AdvanceFrame( float dt )
+{
+ if (dt > 0.1)
+ dt = 0.1f;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ float t = Studio_Duration( pStudioHdr, m_sequence, m_poseParameter );
+
+ if (t > 0)
+ {
+ m_cycle += dt / t;
+
+ // wrap
+ m_cycle -= (int)(m_cycle);
+ }
+ else
+ {
+ m_cycle = 0;
+ }
+}
+
+void StudioModel::SetUpBones( bool bUpdatePose, matrix3x4_t *pBoneToWorld )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+
+ if ( m_pPosePos == NULL )
+ {
+ bUpdatePose = true;
+ m_pPosePos = new Vector[pStudioHdr->numbones()] ;
+ m_pPoseAng = new Quaternion[pStudioHdr->numbones()];
+ }
+
+ if ( bUpdatePose )
+ {
+ IBoneSetup boneSetup( pStudioHdr, BONE_USED_BY_ANYTHING, m_poseParameter );
+ boneSetup.InitPose( m_pPosePos, m_pPoseAng );
+ boneSetup.AccumulatePose( m_pPosePos, m_pPoseAng, m_sequence, m_cycle, 1.0f, 0.0f, NULL );
+ }
+
+ mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
+
+ matrix3x4_t cameraTransform;
+ AngleMatrix( m_angles, cameraTransform );
+ cameraTransform[0][3] = m_origin[0];
+ cameraTransform[1][3] = m_origin[1];
+ cameraTransform[2][3] = m_origin[2];
+
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if ( CalcProceduralBone( pStudioHdr, i, CBoneAccessor( pBoneToWorld ) ))
+ continue;
+
+ matrix3x4_t bonematrix;
+
+ QuaternionMatrix( m_pPoseAng[i], bonematrix );
+
+ bonematrix[0][3] = m_pPosePos[i][0];
+ bonematrix[1][3] = m_pPosePos[i][1];
+ bonematrix[2][3] = m_pPosePos[i][2];
+
+ if (pbones[i].parent == -1)
+ {
+ ConcatTransforms( cameraTransform, bonematrix, pBoneToWorld[ i ] );
+ }
+ else
+ {
+ ConcatTransforms ( pBoneToWorld[ pbones[i].parent ], bonematrix, pBoneToWorld[ i ] );
+ }
+ }
+}
+
+/*
+=================
+StudioModel::SetupModel
+ based on the body part, figure out which mesh it should be using.
+inputs:
+ currententity
+outputs:
+ pstudiomesh
+ pmdl
+=================
+*/
+
+void StudioModel::SetupModel ( int bodypart )
+{
+ int index;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (bodypart > pStudioHdr->numbodyparts())
+ {
+ // Con_DPrintf ("StudioModel::SetupModel: no such bodypart %d\n", bodypart);
+ bodypart = 0;
+ }
+
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( bodypart );
+
+ index = m_bodynum / pbodypart->base;
+ index = index % pbodypart->nummodels;
+
+ m_pModel = pbodypart->pModel( index );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void StudioModel::DrawModel3D( CRender3D *pRender, float flAlpha, bool bWireframe )
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ if (!pStudioHdr)
+ return;
+
+ if (pStudioHdr->numbodyparts == 0)
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ DrawModelInfo_t info;
+ info.m_pStudioHdr = pStudioHdr;
+ info.m_pHardwareData = GetHardwareData();
+ info.m_Decals = STUDIORENDER_DECAL_INVALID;
+ info.m_Skin = m_skinnum;
+ info.m_Body = m_bodynum;
+ info.m_HitboxSet = 0;
+
+ info.m_pClientEntity = NULL;
+ info.m_Lod = -1;
+ info.m_pColorMeshes = NULL;
+
+ if ( pRender->IsInLocalTransformMode() )
+ {
+ // WHACKY HACKY
+ Vector orgOrigin = m_origin;
+ QAngle orgAngles = m_angles;
+
+ VMatrix matrix;
+ pRender->GetLocalTranform(matrix);
+
+ // baseclass rotates the origin
+ matrix.V3Mul( orgOrigin, m_origin );
+
+ matrix3x4_t fCurrentMatrix,fMatrixNew;
+ AngleMatrix(m_angles, fCurrentMatrix);
+ ConcatTransforms(matrix.As3x4(), fCurrentMatrix, fMatrixNew);
+
+ QAngle newAngles;
+ MatrixAngles(fMatrixNew, m_angles);
+
+ matrix3x4_t boneToWorld[MAXSTUDIOBONES];
+ SetUpBones( false, boneToWorld );
+ pRender->DrawModel( &info, boneToWorld, m_origin, flAlpha, bWireframe );
+
+ m_origin = orgOrigin;
+ m_angles = orgAngles;
+ }
+ else
+ {
+ matrix3x4_t boneToWorld[MAXSTUDIOBONES];
+ SetUpBones( true, boneToWorld );
+ pRender->DrawModel( &info, boneToWorld, m_origin, flAlpha, bWireframe );
+
+ if ( Options.general.bShowCollisionModels )
+ {
+ VMatrix mViewMatrix = SetupMatrixOrgAngles( m_origin, m_angles );
+ pRender->DrawCollisionModel( m_MDLHandle, mViewMatrix );
+ }
+ }
+}
+
+void StudioModel::DrawModel2D( CRender2D *pRender, float flAlpha, bool bWireFrame )
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ if (!pStudioHdr)
+ return;
+
+ if (pStudioHdr->numbodyparts == 0)
+ return;
+
+ Vector orgOrigin = m_origin;
+ QAngle orgAngles = m_angles;
+
+
+ DrawModelInfo_t info;
+ info.m_pStudioHdr = pStudioHdr;
+ info.m_pHardwareData = GetHardwareData();
+ info.m_Decals = STUDIORENDER_DECAL_INVALID;
+ info.m_Skin = m_skinnum;
+ info.m_Body = m_bodynum;
+ info.m_HitboxSet = 0;
+
+ info.m_pClientEntity = NULL;
+ info.m_Lod = -1;
+ info.m_pColorMeshes = NULL;
+
+ bool bTransform = pRender->IsInLocalTransformMode();
+
+ if ( bTransform )
+ {
+ // WHACKY HACKY
+ VMatrix matrix; pRender->GetLocalTranform(matrix);
+
+ // baseclass rotates the origin
+ matrix.V3Mul( orgOrigin, m_origin );
+
+ matrix3x4_t fCurrentMatrix,fMatrixNew;
+ AngleMatrix(m_angles, fCurrentMatrix);
+ ConcatTransforms(matrix.As3x4(), fCurrentMatrix, fMatrixNew);
+
+ QAngle newAngles;
+ MatrixAngles(fMatrixNew, m_angles);
+ }
+
+ if ( Options.general.bShowCollisionModels )
+ {
+ VMatrix mViewMatrix = SetupMatrixOrgAngles( orgOrigin, orgAngles );
+ pRender->DrawCollisionModel( m_MDLHandle, mViewMatrix );
+ }
+ else
+ {
+ matrix3x4_t boneToWorld[MAXSTUDIOBONES];
+ SetUpBones( false, boneToWorld );
+ pRender->DrawModel( &info, boneToWorld, m_origin, flAlpha, bWireFrame );
+ }
+
+
+ if ( bTransform )
+ {
+ // restore original position and angles
+ m_origin = orgOrigin;
+ m_angles = orgAngles;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// It's translucent if all its materials are translucent
+//-----------------------------------------------------------------------------
+bool StudioModel::IsTranslucent()
+{
+ // garymcthack - shouldn't crack hardwaredata
+ studiohwdata_t *pHardwareData = GetHardwareData();
+ if ( pHardwareData == NULL )
+ return false;
+
+ int lodID;
+ for( lodID = pHardwareData->m_RootLOD; lodID < pHardwareData->m_NumLODs; lodID++ )
+ {
+ for (int i = 0; i < pHardwareData->m_pLODs[lodID].numMaterials; ++i)
+ {
+ if (!pHardwareData->m_pLODs[lodID].ppMaterials[i]->IsTranslucent())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Frees the model data and releases textures from OpenGL.
+//-----------------------------------------------------------------------------
+void StudioModel::FreeModel(void)
+{
+ /*int nRef = */g_pMDLCache->Release( m_MDLHandle );
+// Assert( nRef == 0 );
+ m_MDLHandle = MDLHANDLE_INVALID;
+ m_pModel = NULL;
+}
+
+CStudioHdr *StudioModel::GetStudioHdr() const
+{
+ // return g_pMDLCache->GetStudioHdr( m_MDLHandle );
+
+ if (m_pStudioHdr->IsValid())
+ return m_pStudioHdr;
+
+ studiohdr_t *hdr = g_pMDLCache->GetStudioHdr( m_MDLHandle );
+
+ m_pStudioHdr->Init( hdr );
+
+ Assert(m_pStudioHdr->IsValid());
+
+ return m_pStudioHdr;
+}
+
+
+studiohdr_t *StudioModel::GetStudioRenderHdr() const
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+
+ if (pStudioHdr)
+ {
+ return (studiohdr_t *)pStudioHdr->GetRenderHdr();
+ }
+ return NULL;
+}
+
+studiohwdata_t* StudioModel::GetHardwareData()
+{
+ return g_pMDLCache->GetHardwareData( m_MDLHandle );
+}
+
+
+bool StudioModel::LoadModel( const char *modelname )
+{
+ // Load the MDL file data
+ Assert( m_MDLHandle == MDLHANDLE_INVALID );
+
+ // for easier fall through cleanup
+ m_MDLHandle = MDLHANDLE_INVALID;
+
+ if ( !g_pStudioRender || !modelname )
+ return false;
+
+ // In the case of restore, m_pModelName == modelname
+ if (m_pModelName != modelname)
+ {
+ // Copy over the model name; we'll need it later...
+ if (m_pModelName)
+ {
+ delete[] m_pModelName;
+ }
+
+ m_pModelName = new char[strlen(modelname) + 1];
+ strcpy( m_pModelName, modelname );
+ }
+
+ m_MDLHandle = g_pMDLCache->FindMDL( modelname );
+ if (m_MDLHandle == MDLHANDLE_INVALID)
+ return false;
+
+ // Cache a bunch of stuff into memory
+ g_pMDLCache->GetStudioHdr( m_MDLHandle );
+ g_pMDLCache->GetHardwareData( m_MDLHandle );
+
+ if (m_pStudioHdr)
+ {
+ delete m_pStudioHdr;
+ m_pStudioHdr = NULL;
+ }
+
+ m_pStudioHdr = new CStudioHdr;
+
+ return true;
+}
+
+
+
+bool StudioModel::PostLoadModel(const char *modelname)
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (pStudioHdr == NULL)
+ {
+ return(false);
+ }
+
+ SetSequence (0);
+
+ for (int n = 0; n < pStudioHdr->numbodyparts(); n++)
+ {
+ if (SetBodygroup (n, 0) < 0)
+ {
+ return false;
+ }
+ }
+
+ SetSkin (0);
+
+
+/*
+ Vector mins, maxs;
+ ExtractBbox (mins, maxs);
+ if (mins[2] < 5.0f)
+ m_origin[2] = -mins[2];
+*/
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int StudioModel::GetSequenceCount( void )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return pStudioHdr->GetNumSeq();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nIndex -
+// szName -
+//-----------------------------------------------------------------------------
+void StudioModel::GetSequenceName( int nIndex, char *szName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (nIndex < pStudioHdr->GetNumSeq())
+ {
+ strcpy(szName, pStudioHdr->pSeqdesc(nIndex).pszLabel());
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index of the current sequence.
+//-----------------------------------------------------------------------------
+int StudioModel::GetSequence( )
+{
+ return m_sequence;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the current sequence by index.
+//-----------------------------------------------------------------------------
+int StudioModel::SetSequence( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (iSequence > pStudioHdr->GetNumSeq())
+ return m_sequence;
+
+ m_sequence = iSequence;
+ m_cycle = 0;
+
+ return m_sequence;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Rotates the given bounding box by the given angles and computes the
+// bounds of the rotated box. This is used to take the rotation angles
+// into consideration when returning the bounding box. Note that this
+// can produce a larger than optimal bounding box.
+// Input : Mins -
+// Maxs -
+// Angles -
+//-----------------------------------------------------------------------------
+void StudioModel::RotateBbox(Vector &Mins, Vector &Maxs, const QAngle &Angles)
+{
+ Vector Points[8];
+
+ PointsFromBox( Mins, Maxs, Points );
+
+ //
+ // Rotate the corner points by the specified angles, in the same
+ // order that our Render code uses.
+ //
+ VMatrix mMatrix;
+ mMatrix.SetupMatrixOrgAngles( vec3_origin, Angles );
+ matrix3x4_t fMatrix2 = mMatrix.As3x4();
+
+ Vector RotatedPoints[8];
+ for (int i = 0; i < 8; i++)
+ {
+ VectorRotate(Points[i], fMatrix2, RotatedPoints[i]);
+ }
+
+ //
+ // Calculate the new mins and maxes.
+ //
+ for (int i = 0; i < 8; i++)
+ {
+ for (int nDim = 0; nDim < 3; nDim++)
+ {
+ if ((i == 0) || (RotatedPoints[i][nDim] < Mins[nDim]))
+ {
+ Mins[nDim] = RotatedPoints[i][nDim];
+ }
+
+ if ((i == 0) || (RotatedPoints[i][nDim] > Maxs[nDim]))
+ {
+ Maxs[nDim] = RotatedPoints[i][nDim];
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mins -
+// maxs -
+//-----------------------------------------------------------------------------
+void StudioModel::ExtractBbox(Vector &mins, Vector &maxs)
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( m_sequence );
+
+ mins = seqdesc.bbmin;
+
+ maxs = seqdesc.bbmax;
+
+ RotateBbox(mins, maxs, m_angles);
+}
+
+
+void StudioModel::ExtractClippingBbox( Vector& mins, Vector& maxs )
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ mins[0] = pStudioHdr->view_bbmin[0];
+ mins[1] = pStudioHdr->view_bbmin[1];
+ mins[2] = pStudioHdr->view_bbmin[2];
+
+ maxs[0] = pStudioHdr->view_bbmax[0];
+ maxs[1] = pStudioHdr->view_bbmax[1];
+ maxs[2] = pStudioHdr->view_bbmax[2];
+}
+
+
+void StudioModel::ExtractMovementBbox( Vector& mins, Vector& maxs )
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ mins[0] = pStudioHdr->hull_min[0];
+ mins[1] = pStudioHdr->hull_min[1];
+ mins[2] = pStudioHdr->hull_min[2];
+
+ maxs[0] = pStudioHdr->hull_max[0];
+ maxs[1] = pStudioHdr->hull_max[1];
+ maxs[2] = pStudioHdr->hull_max[2];
+}
+
+
+void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ float t = Studio_Duration( pStudioHdr, m_sequence, m_poseParameter );
+
+ if (t > 0)
+ {
+ *pflFrameRate = 1.0 / t;
+ *pflGroundSpeed = 0; // sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] );
+ // *pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1);
+ }
+ else
+ {
+ *pflFrameRate = 1.0;
+ *pflGroundSpeed = 0.0;
+ }
+}
+
+
+void StudioModel::SetOrigin( float x, float y, float z )
+{
+ m_origin[0] = x;
+ m_origin[1] = y;
+ m_origin[2] = z;
+}
+
+
+void StudioModel::SetOrigin( const Vector &v )
+{
+ m_origin = v;
+}
+
+
+void StudioModel::GetOrigin( float &x, float &y, float &z )
+{
+ x = m_origin[0];
+ y = m_origin[1];
+ z = m_origin[2];
+}
+
+void StudioModel::GetOrigin( Vector &v )
+{
+ v = m_origin;
+}
+
+int StudioModel::SetBodygroup( int iGroup, int iValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ if (iGroup > pStudioHdr->numbodyparts())
+ return -1;
+
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( iGroup );
+
+ if ((pbodypart->base == 0) || (pbodypart->nummodels == 0))
+ {
+ return -1;
+ }
+
+ int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels;
+
+ if (iValue >= pbodypart->nummodels)
+ return iCurrent;
+
+ m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
+
+ return iValue;
+}
+
+int StudioModel::SetSkin( int iValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ if (iValue >= pStudioHdr->numskinfamilies())
+ {
+ iValue = 0;
+ }
+
+ m_skinnum = iValue;
+
+ return iValue;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pRender -
+//-----------------------------------------------------------------------------
+
+/*void StudioModel::DrawModel2D(CRender2D *pRender)
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ CMapView2D *pView = (CMapView2D*) pRender->GetView();
+
+ DrawModelInfo_t info;
+ ZeroMemory(&info, sizeof(info));
+
+ info.m_pStudioHdr = pStudioHdr;
+ info.m_pHardwareData = GetHardwareData();
+
+ info.m_Decals = STUDIORENDER_DECAL_INVALID;
+ info.m_Skin = m_skinnum;
+ info.m_Body = m_bodynum;
+ info.m_HitboxSet = 0;
+
+ info.m_pClientEntity = NULL;
+ info.m_Lod = -1;
+ info.m_ppColorMeshes = NULL;
+
+ if ( pView->m_fZoom < 3 )
+ info.m_Lod = 3;
+
+ matrix3x4_t *pBoneToWorld = SetUpBones( g_bUpdateBones2D );
+
+ GetTriangles_Output_t tris;
+ g_pStudioRender->GetTriangles( info, tris );
+
+ for ( int batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ int numStrips = materialBatch.m_TriListIndices.Count() / 3;
+ int numVertices = materialBatch.m_Verts.Count();
+
+ POINT *points = (POINT*)_alloca( sizeof(POINT) * numVertices );
+
+ // translate all vertices
+ for ( int vertID = 0; vertID < numVertices; vertID++)
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+
+ Vector newPos(0,0,0);
+
+ for ( int k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ newPos += vert.m_BoneWeight[k] * tmp;
+ }
+
+ pView->WorldToClient( points[vertID], newPos );
+// pRender->TransformPoint3D( points[vertID], newPos );
+ }
+
+ // Send the vertices down to the hardware.
+
+ int stripIndex = 0;
+
+ for ( int strip = 0; strip < numStrips; strip++ )
+ {
+ int ptx[3];
+ int pty[3];
+
+ int numPoints = 0;
+ POINT lastPt; lastPt.x = lastPt.y = -99999;
+
+ for ( int i = 0; i<3; i++ )
+ {
+ POINT pt = points[ materialBatch.m_TriListIndices[stripIndex++] ];
+
+ if ( pt.x == lastPt.x && pt.y == lastPt.y )
+ continue;
+
+ ptx[numPoints] = pt.x;
+ pty[numPoints] = pt.y;
+ lastPt = pt;
+ numPoints++;
+ }
+
+ // for performance sake bypass the renderer interface, buuuhhh
+
+ if ( numPoints == 2 )
+ {
+ g_pMatSystemSurface->DrawLine( ptx[0], pty[0], ptx[1], pty[1] );
+ }
+ else if ( numPoints == 3 )
+ {
+ g_pMatSystemSurface->DrawPolyLine( ptx, pty, 3 );
+ }
+ }
+ }
+} */
+
+
+void InitStudioFileChangeWatcher()
+{
+ g_StudioFileChangeWatcher.Init();
+}
+
+
+void UpdateStudioFileChangeWatcher()
+{
+ g_StudioFileChangeWatcher.Update();
+}
+
+
+