You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1224 lines
27 KiB
1224 lines
27 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// updates: |
|
// 1-4-99 fixed file texture load and file read bug |
|
|
|
//////////////////////////////////////////////////////////////////////// |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <malloc.h> |
|
#include "StudioModel.h" |
|
#include "vphysics/constraints.h" |
|
#include "physmesh.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "ViewerSettings.h" |
|
#include "bone_setup.h" |
|
#include "UtlMemory.h" |
|
#include "mxtk/mx.h" |
|
#include "filesystem.h" |
|
#include "IStudioRender.h" |
|
#include "materialsystem/IMaterialSystemHardwareConfig.h" |
|
#include "MDLViewer.h" |
|
#include "optimize.h" |
|
|
|
extern char g_appTitle[]; |
|
Vector *StudioModel::m_AmbientLightColors; |
|
|
|
#pragma warning( disable : 4244 ) // double to float |
|
|
|
|
|
static StudioModel g_studioModel; |
|
static StudioModel *g_pActiveModel; |
|
|
|
// Expose it to the rest of the app |
|
StudioModel *g_pStudioModel = &g_studioModel; |
|
StudioModel *g_pStudioExtraModel[HLMV_MAX_MERGED_MODELS]; |
|
|
|
StudioModel::StudioModel() |
|
{ |
|
m_MDLHandle = MDLHANDLE_INVALID; |
|
ClearLookTargets(); |
|
} |
|
|
|
void StudioModel::Init() |
|
{ |
|
m_AmbientLightColors = new Vector[g_pStudioRender->GetNumAmbientLightSamples()]; |
|
|
|
// JasonM & garymcthack - should really only do this once a frame and at init time. |
|
UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false, |
|
g_viewerSettings.showNormals, |
|
g_viewerSettings.showTangentFrame ); |
|
} |
|
|
|
void StudioModel::Shutdown( void ) |
|
{ |
|
g_pStudioModel->FreeModel( false ); |
|
delete [] m_AmbientLightColors; |
|
} |
|
|
|
void StudioModel::SetCurrentModel() |
|
{ |
|
// track the correct model |
|
g_pActiveModel = this; |
|
} |
|
|
|
void StudioModel::ReleaseStudioModel() |
|
{ |
|
SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel ); |
|
g_pStudioModel->FreeModel( true ); |
|
} |
|
|
|
void StudioModel::RestoreStudioModel() |
|
{ |
|
// should view settings be loaded before the model is loaded? |
|
if ( g_pStudioModel->LoadModel( g_pStudioModel->m_pModelName ) ) |
|
{ |
|
g_pStudioModel->PostLoadModel( g_pStudioModel->m_pModelName ); |
|
} |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Frees the model data and releases textures from OpenGL. |
|
//----------------------------------------------------------------------------- |
|
void StudioModel::FreeModel( bool bReleasing ) |
|
{ |
|
if ( m_pStudioHdr ) |
|
{ |
|
delete m_pStudioHdr; |
|
m_pStudioHdr = NULL; |
|
} |
|
|
|
if ( m_MDLHandle != MDLHANDLE_INVALID ) |
|
{ |
|
g_pMDLCache->Release( m_MDLHandle ); |
|
m_MDLHandle = MDLHANDLE_INVALID; |
|
} |
|
|
|
if ( !bReleasing ) |
|
{ |
|
if (m_pModelName) |
|
{ |
|
delete[] m_pModelName; |
|
m_pModelName = NULL; |
|
} |
|
} |
|
|
|
m_SurfaceProps.Purge(); |
|
|
|
DestroyPhysics( m_pPhysics ); |
|
m_pPhysics = NULL; |
|
} |
|
|
|
void *StudioModel::operator new( size_t stAllocateBlock ) |
|
{ |
|
// call into engine to get memory |
|
Assert( stAllocateBlock != 0 ); |
|
return calloc( 1, stAllocateBlock ); |
|
} |
|
|
|
void StudioModel::operator delete( void *pMem ) |
|
{ |
|
#ifdef _DEBUG |
|
// set the memory to a known value |
|
int size = _msize( pMem ); |
|
memset( pMem, 0xcd, size ); |
|
#endif |
|
|
|
// get the engine to free the memory |
|
free( pMem ); |
|
} |
|
|
|
void *StudioModel::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine ) |
|
{ |
|
// call into engine to get memory |
|
Assert( stAllocateBlock != 0 ); |
|
return calloc( 1, stAllocateBlock ); |
|
} |
|
|
|
void StudioModel::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ) |
|
{ |
|
#ifdef _DEBUG |
|
// set the memory to a known value |
|
int size = _msize( pMem ); |
|
memset( pMem, 0xcd, size ); |
|
#endif |
|
// get the engine to free the memory |
|
free( pMem ); |
|
} |
|
|
|
bool StudioModel::LoadModel( const char *pModelName ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
if (!pModelName) |
|
return 0; |
|
|
|
// In the case of restore, m_pModelName == modelname |
|
if (m_pModelName != pModelName) |
|
{ |
|
// Copy over the model name; we'll need it later... |
|
if (m_pModelName) |
|
{ |
|
delete[] m_pModelName; |
|
} |
|
m_pModelName = new char[Q_strlen(pModelName) + 1]; |
|
strcpy( m_pModelName, pModelName ); |
|
} |
|
|
|
m_MDLHandle = g_pMDLCache->FindMDL( pModelName ); |
|
|
|
// allocate a pool for a studiohdr cache |
|
if (m_pStudioHdr != NULL) |
|
{ |
|
delete m_pStudioHdr; |
|
} |
|
m_pStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache ); |
|
|
|
// manadatory to access correct verts |
|
SetCurrentModel(); |
|
|
|
m_pPhysics = LoadPhysics( m_MDLHandle ); |
|
|
|
// Copy over all of the hitboxes; we may add and remove elements |
|
m_HitboxSets.RemoveAll(); |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
|
|
int i; |
|
for ( int s = 0; s < pStudioHdr->numhitboxsets(); s++ ) |
|
{ |
|
mstudiohitboxset_t *pSrcSet = pStudioHdr->pHitboxSet( s ); |
|
if ( !pSrcSet ) |
|
continue; |
|
|
|
int j = m_HitboxSets.AddToTail(); |
|
HitboxSet_t &set = m_HitboxSets[j]; |
|
set.m_Name = pSrcSet->pszName(); |
|
|
|
for ( i = 0; i < pSrcSet->numhitboxes; ++i ) |
|
{ |
|
mstudiobbox_t *pHit = pSrcSet->pHitbox(i); |
|
int nIndex = set.m_Hitboxes.AddToTail( ); |
|
HitboxInfo_t &hitbox = set.m_Hitboxes[nIndex]; |
|
|
|
hitbox.m_Name = pHit->pszHitboxName(); |
|
hitbox.m_BBox = *pHit; |
|
|
|
// Blat out bbox name index so we don't use it by mistake... |
|
hitbox.m_BBox.szhitboxnameindex = 0; |
|
} |
|
} |
|
|
|
// Copy over all of the surface props; we may change them... |
|
for ( i = 0; i < pStudioHdr->numbones(); ++i ) |
|
{ |
|
mstudiobone_t* pBone = pStudioHdr->pBone(i); |
|
|
|
CUtlSymbol prop( pBone->pszSurfaceProp() ); |
|
m_SurfaceProps.AddToTail( prop ); |
|
} |
|
|
|
m_physPreviewBone = -1; |
|
|
|
bool forceOpaque = (pStudioHdr->flags() & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0; |
|
bool vertexLit = false; |
|
m_bIsTransparent = false; |
|
m_bHasProxy = false; |
|
|
|
studiohwdata_t *pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle ); |
|
if ( !pHardwareData ) |
|
{ |
|
Assert( 0 ); |
|
return false; |
|
} |
|
|
|
for( int lodID = pHardwareData->m_RootLOD; lodID < pHardwareData->m_NumLODs; lodID++ ) |
|
{ |
|
studioloddata_t *pLODData = &pHardwareData->m_pLODs[lodID]; |
|
for ( i = 0; i < pLODData->numMaterials; ++i ) |
|
{ |
|
if (pLODData->ppMaterials[i]->IsVertexLit()) |
|
{ |
|
vertexLit = true; |
|
} |
|
if ((!forceOpaque) && pLODData->ppMaterials[i]->IsTranslucent()) |
|
{ |
|
m_bIsTransparent = true; |
|
//Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pStudioHdr->name ); |
|
} |
|
if (pLODData->ppMaterials[i]->HasProxy()) |
|
{ |
|
m_bHasProxy = true; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
bool StudioModel::PostLoadModel( const char *modelname ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (pStudioHdr == NULL) |
|
return false; |
|
|
|
SetSequence (0); |
|
SetController (0, 0.0f); |
|
SetController (1, 0.0f); |
|
SetController (2, 0.0f); |
|
SetController (3, 0.0f); |
|
SetBlendTime( DEFAULT_BLEND_TIME ); |
|
// SetHeadTurn( 1.0f ); // FIXME:!!! |
|
|
|
int n; |
|
for (n = 0; n < pStudioHdr->numbodyparts(); n++) |
|
{ |
|
SetBodygroup (n, 0); |
|
} |
|
|
|
SetSkin (0); |
|
|
|
/* |
|
Vector mins, maxs; |
|
ExtractBbox (mins, maxs); |
|
if (mins[2] < 5.0f) |
|
m_origin[2] = -mins[2]; |
|
*/ |
|
return true; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Returns true if the model has at least one body part with model data, false if not. |
|
//------------------------------------------------------------------------------ |
|
bool StudioModel::HasModel() |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
for ( int i = 0; i < pStudioHdr->numbodyparts(); i++ ) |
|
{ |
|
if ( pStudioHdr->pBodypart(i)->nummodels ) |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
int StudioModel::GetSequence( ) |
|
{ |
|
return m_sequence; |
|
} |
|
|
|
int StudioModel::SetSequence( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0; |
|
|
|
if (iSequence < 0) |
|
return 0; |
|
|
|
if (iSequence > pStudioHdr->GetNumSeq()) |
|
return m_sequence; |
|
|
|
m_prevsequence = m_sequence; |
|
m_sequence = iSequence; |
|
m_cycle = 0; |
|
m_sequencetime = 0.0; |
|
|
|
return m_sequence; |
|
} |
|
|
|
const char* StudioModel::GetSequenceName( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return NULL; |
|
|
|
if (iSequence < 0) |
|
return NULL; |
|
|
|
if (iSequence > pStudioHdr->GetNumSeq()) |
|
return NULL; |
|
|
|
return pStudioHdr->pSeqdesc( iSequence ).pszLabel(); |
|
} |
|
|
|
void StudioModel::ClearOverlaysSequences( void ) |
|
{ |
|
ClearAnimationLayers( ); |
|
memset( m_Layer, 0, sizeof( m_Layer ) ); |
|
} |
|
|
|
void StudioModel::ClearAnimationLayers( void ) |
|
{ |
|
m_iActiveLayers = 0; |
|
} |
|
|
|
int StudioModel::GetNewAnimationLayer( int iPriority ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0; |
|
|
|
if ( m_iActiveLayers >= MAXSTUDIOANIMLAYERS ) |
|
{ |
|
Assert( 0 ); |
|
return MAXSTUDIOANIMLAYERS - 1; |
|
} |
|
|
|
m_Layer[m_iActiveLayers].m_priority = iPriority; |
|
|
|
return m_iActiveLayers++; |
|
} |
|
|
|
int StudioModel::SetOverlaySequence( int iLayer, int iSequence, float flWeight ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0; |
|
|
|
if (iSequence < 0) |
|
return 0; |
|
|
|
if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) |
|
{ |
|
Assert(0); |
|
return 0; |
|
} |
|
|
|
if (iSequence > pStudioHdr->GetNumSeq()) |
|
return m_Layer[iLayer].m_sequence; |
|
|
|
m_Layer[iLayer].m_sequence = iSequence; |
|
m_Layer[iLayer].m_weight = flWeight; |
|
m_Layer[iLayer].m_playbackrate = 1.0; |
|
|
|
return iSequence; |
|
} |
|
|
|
float StudioModel::SetOverlayRate( int iLayer, float flCycle, float flPlaybackRate ) |
|
{ |
|
if (iLayer >= 0 && iLayer < MAXSTUDIOANIMLAYERS) |
|
{ |
|
m_Layer[iLayer].m_cycle = flCycle; |
|
m_Layer[iLayer].m_playbackrate = flPlaybackRate; |
|
} |
|
return flCycle; |
|
} |
|
|
|
|
|
int StudioModel::GetOverlaySequence( int iLayer ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return -1; |
|
|
|
if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) |
|
{ |
|
Assert(0); |
|
return 0; |
|
} |
|
|
|
return m_Layer[iLayer].m_sequence; |
|
} |
|
|
|
|
|
float StudioModel::GetOverlaySequenceWeight( int iLayer ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return -1; |
|
|
|
if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) |
|
{ |
|
Assert(0); |
|
return 0; |
|
} |
|
|
|
return m_Layer[iLayer].m_weight; |
|
} |
|
|
|
|
|
int StudioModel::LookupSequence( const char *szSequence ) |
|
{ |
|
int i; |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return -1; |
|
|
|
for (i = 0; i < pStudioHdr->GetNumSeq(); i++) |
|
{ |
|
if (!stricmp( szSequence, pStudioHdr->pSeqdesc( i ).pszLabel() )) |
|
{ |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
int StudioModel::LookupActivity( const char *szActivity ) |
|
{ |
|
int i; |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return -1; |
|
|
|
for (i = 0; i < pStudioHdr->GetNumSeq(); i++) |
|
{ |
|
if (!stricmp( szActivity, pStudioHdr->pSeqdesc( i ).pszActivityName() )) |
|
{ |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
int StudioModel::SetSequence( const char *szSequence ) |
|
{ |
|
return SetSequence( LookupSequence( szSequence ) ); |
|
} |
|
|
|
void StudioModel::StartBlending( void ) |
|
{ |
|
// Switch back to old sequence ( this will oscillate between this one and the last one ) |
|
SetSequence( m_prevsequence ); |
|
} |
|
|
|
void StudioModel::SetBlendTime( float blendtime ) |
|
{ |
|
if ( blendtime > 0.0f ) |
|
{ |
|
m_blendtime = blendtime; |
|
} |
|
} |
|
|
|
float StudioModel::GetTransitionAmount( void ) |
|
{ |
|
if ( g_viewerSettings.blendSequenceChanges && |
|
m_sequencetime < m_blendtime && m_prevsequence != m_sequence ) |
|
{ |
|
float s; |
|
s = ( m_sequencetime / m_blendtime ); |
|
return s; |
|
} |
|
|
|
return 0.0f; |
|
} |
|
|
|
LocalFlexController_t StudioModel::LookupFlexController( char *szName ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return LocalFlexController_t(0); |
|
|
|
for (LocalFlexController_t iFlex = LocalFlexController_t(0); iFlex < pStudioHdr->numflexcontrollers(); iFlex++) |
|
{ |
|
if (stricmp( szName, pStudioHdr->pFlexcontroller( iFlex )->pszName() ) == 0) |
|
{ |
|
return iFlex; |
|
} |
|
} |
|
return LocalFlexController_t(-1); |
|
} |
|
|
|
|
|
void StudioModel::SetFlexController( char *szName, float flValue ) |
|
{ |
|
SetFlexController( LookupFlexController( szName ), flValue ); |
|
} |
|
|
|
void StudioModel::SetFlexController( LocalFlexController_t iFlex, float flValue ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return; |
|
|
|
if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) |
|
{ |
|
mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); |
|
|
|
if (pflex->min != pflex->max) |
|
{ |
|
flValue = (flValue - pflex->min) / (pflex->max - pflex->min); |
|
} |
|
m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f ); |
|
} |
|
} |
|
|
|
|
|
void StudioModel::SetFlexControllerRaw( LocalFlexController_t iFlex, float flValue ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return; |
|
|
|
if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) |
|
{ |
|
// mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); |
|
m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f ); |
|
} |
|
} |
|
|
|
float StudioModel::GetFlexController( char *szName ) |
|
{ |
|
return GetFlexController( LookupFlexController( szName ) ); |
|
} |
|
|
|
float StudioModel::GetFlexController( LocalFlexController_t iFlex ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0.0f; |
|
|
|
if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) |
|
{ |
|
mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); |
|
|
|
float flValue = m_flexweight[iFlex]; |
|
|
|
if (pflex->min != pflex->max) |
|
{ |
|
flValue = flValue * (pflex->max - pflex->min) + pflex->min; |
|
} |
|
return flValue; |
|
} |
|
return 0.0; |
|
} |
|
|
|
|
|
float StudioModel::GetFlexControllerRaw( LocalFlexController_t iFlex ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0.0f; |
|
|
|
if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) |
|
{ |
|
// mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); |
|
return m_flexweight[iFlex]; |
|
} |
|
return 0.0; |
|
} |
|
|
|
int StudioModel::GetNumLODs() const |
|
{ |
|
return g_pStudioRender->GetNumLODs( *GetHardwareData() ); |
|
} |
|
|
|
float StudioModel::GetLODSwitchValue( int lod ) const |
|
{ |
|
return g_pStudioRender->GetLODSwitchValue( *GetHardwareData(), lod ); |
|
} |
|
|
|
void StudioModel::SetLODSwitchValue( int lod, float switchValue ) |
|
{ |
|
g_pStudioRender->SetLODSwitchValue( *GetHardwareData(), lod, switchValue ); |
|
} |
|
|
|
void StudioModel::ExtractBbox( Vector &mins, Vector &maxs ) |
|
{ |
|
studiohdr_t *pStudioHdr = GetStudioRenderHdr(); |
|
if ( !pStudioHdr ) |
|
return; |
|
|
|
// look for hull |
|
if ( ((Vector)pStudioHdr->hull_min).Length() != 0 ) |
|
{ |
|
mins = pStudioHdr->hull_min; |
|
maxs = pStudioHdr->hull_max; |
|
} |
|
// look for view clip |
|
else if (((Vector)pStudioHdr->view_bbmin).Length() != 0) |
|
{ |
|
mins = pStudioHdr->view_bbmin; |
|
maxs = pStudioHdr->view_bbmax; |
|
} |
|
else |
|
{ |
|
mstudioseqdesc_t &pseqdesc = pStudioHdr->pSeqdesc( m_sequence ); |
|
|
|
mins = pseqdesc.bbmin; |
|
maxs = pseqdesc.bbmax; |
|
} |
|
} |
|
|
|
|
|
|
|
void StudioModel::GetSequenceInfo( int iSequence, float *pflFrameRate, float *pflGroundSpeed ) |
|
{ |
|
float t = GetDuration( iSequence ); |
|
|
|
if (t > 0) |
|
{ |
|
*pflFrameRate = 1.0 / t; |
|
} |
|
else |
|
{ |
|
*pflFrameRate = 1.0; |
|
} |
|
*pflGroundSpeed = GetGroundSpeed( iSequence ); |
|
} |
|
|
|
void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed ) |
|
{ |
|
GetSequenceInfo( m_sequence, pflFrameRate, pflGroundSpeed ); |
|
} |
|
|
|
float StudioModel::GetFPS( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0.0f; |
|
|
|
return Studio_FPS( pStudioHdr, iSequence, m_poseparameter ); |
|
} |
|
|
|
float StudioModel::GetFPS( void ) |
|
{ |
|
return GetFPS( m_sequence ); |
|
} |
|
|
|
float StudioModel::GetDuration( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return 0.0f; |
|
|
|
return Studio_Duration( pStudioHdr, iSequence, m_poseparameter ); |
|
} |
|
|
|
|
|
int StudioModel::GetNumFrames( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr || iSequence < 0 || iSequence >= pStudioHdr->GetNumSeq() ) |
|
{ |
|
return 1; |
|
} |
|
|
|
return Studio_MaxFrame( pStudioHdr, iSequence, m_poseparameter ); |
|
} |
|
|
|
static int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence ) |
|
{ |
|
if ( !pstudiohdr || |
|
sequence < 0 || |
|
sequence >= pstudiohdr->GetNumSeq() ) |
|
{ |
|
return 0; |
|
} |
|
|
|
mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence ); |
|
|
|
return seqdesc.flags; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : iSequence - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool StudioModel::GetSequenceLoops( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return false; |
|
|
|
int flags = GetSequenceFlags( pStudioHdr, iSequence ); |
|
bool looping = flags & STUDIO_LOOPING ? true : false; |
|
return looping; |
|
} |
|
|
|
float StudioModel::GetDuration( ) |
|
{ |
|
return GetDuration( m_sequence ); |
|
} |
|
|
|
|
|
void StudioModel::GetMovement( float prevcycle[5], Vector &vecPos, QAngle &vecAngles ) |
|
{ |
|
vecPos.Init(); |
|
vecAngles.Init(); |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return; |
|
|
|
// assume that changes < -0.5 are loops.... |
|
if (m_cycle - prevcycle[0] < -0.5) |
|
{ |
|
prevcycle[0] = prevcycle[0] - 1.0; |
|
} |
|
|
|
Studio_SeqMovement( pStudioHdr, m_sequence, prevcycle[0], m_cycle, m_poseparameter, vecPos, vecAngles ); |
|
prevcycle[0] = m_cycle; |
|
|
|
int i; |
|
for (i = 0; i < 4; i++) |
|
{ |
|
Vector vecTmp; |
|
QAngle angTmp; |
|
|
|
if (m_Layer[i].m_cycle - prevcycle[i+1] < -0.5) |
|
{ |
|
prevcycle[i+1] = prevcycle[i+1] - 1.0; |
|
} |
|
|
|
if (m_Layer[i].m_weight > 0.0) |
|
{ |
|
vecTmp.Init(); |
|
angTmp.Init(); |
|
if (Studio_SeqMovement( pStudioHdr, m_Layer[i].m_sequence, prevcycle[i+1], m_Layer[i].m_cycle, m_poseparameter, vecTmp, angTmp )) |
|
{ |
|
vecPos = vecPos * ( 1.0 - m_Layer[i].m_weight ) + vecTmp * m_Layer[i].m_weight; |
|
} |
|
} |
|
prevcycle[i+1] = m_Layer[i].m_cycle; |
|
} |
|
|
|
return; |
|
} |
|
|
|
|
|
void StudioModel::GetMovement( int iSequence, float prevCycle, float nextCycle, Vector &vecPos, QAngle &vecAngles ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
{ |
|
vecPos.Init(); |
|
vecAngles.Init(); |
|
return; |
|
} |
|
|
|
// FIXME: this doesn't consider layers |
|
Studio_SeqMovement( pStudioHdr, iSequence, prevCycle, nextCycle, m_poseparameter, vecPos, vecAngles ); |
|
|
|
return; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the ground speed of the specifed sequence. |
|
//----------------------------------------------------------------------------- |
|
float StudioModel::GetGroundSpeed( int iSequence ) |
|
{ |
|
Vector vecMove; |
|
QAngle vecAngles; |
|
GetMovement( iSequence, 0, 1, vecMove, vecAngles ); |
|
|
|
float t = GetDuration( iSequence ); |
|
|
|
float flGroundSpeed = 0; |
|
if (t > 0) |
|
{ |
|
flGroundSpeed = vecMove.Length() / t; |
|
} |
|
|
|
return flGroundSpeed; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the ground speed of the current sequence. |
|
//----------------------------------------------------------------------------- |
|
float StudioModel::GetGroundSpeed( void ) |
|
{ |
|
return GetGroundSpeed( m_sequence ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the ground speed of the current sequence. |
|
//----------------------------------------------------------------------------- |
|
float StudioModel::GetCurrentVelocity( void ) |
|
{ |
|
Vector vecVelocity; |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (pStudioHdr && Studio_SeqVelocity( pStudioHdr, m_sequence, m_cycle, m_poseparameter, vecVelocity )) |
|
{ |
|
return vecVelocity.Length(); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the the sequence should be hidden or not |
|
//----------------------------------------------------------------------------- |
|
bool StudioModel::IsHidden( int iSequence ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (pStudioHdr->pSeqdesc( iSequence ).flags & STUDIO_HIDDEN) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
void StudioModel::GetSeqAnims( int iSequence, mstudioanimdesc_t *panim[4], float *weight ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return; |
|
|
|
mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); |
|
Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, m_poseparameter, panim, weight ); |
|
} |
|
|
|
void StudioModel::GetSeqAnims( mstudioanimdesc_t *panim[4], float *weight ) |
|
{ |
|
GetSeqAnims( m_sequence, panim, weight ); |
|
} |
|
|
|
|
|
float StudioModel::SetController( int iController, float flValue ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return 0.0f; |
|
|
|
return Studio_SetController( pStudioHdr, iController, flValue, m_controller[iController] ); |
|
} |
|
|
|
|
|
|
|
int StudioModel::LookupPoseParameter( char const *szName ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return false; |
|
|
|
for (int iParameter = 0; iParameter < pStudioHdr->GetNumPoseParameters(); iParameter++) |
|
{ |
|
if (stricmp( szName, pStudioHdr->pPoseParameter( iParameter ).pszName() ) == 0) |
|
{ |
|
return iParameter; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
float StudioModel::SetPoseParameter( char const *szName, float flValue ) |
|
{ |
|
return SetPoseParameter( LookupPoseParameter( szName ), flValue ); |
|
} |
|
|
|
float StudioModel::SetPoseParameter( int iParameter, float flValue ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return 0.0f; |
|
|
|
return Studio_SetPoseParameter( pStudioHdr, iParameter, flValue, m_poseparameter[iParameter] ); |
|
} |
|
|
|
float StudioModel::GetPoseParameter( char const *szName ) |
|
{ |
|
return GetPoseParameter( LookupPoseParameter( szName ) ); |
|
} |
|
|
|
float* StudioModel::GetPoseParameters() |
|
{ |
|
return m_poseparameter; |
|
} |
|
|
|
float StudioModel::GetPoseParameter( int iParameter ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return 0.0f; |
|
|
|
return Studio_GetPoseParameter( pStudioHdr, iParameter, m_poseparameter[iParameter] ); |
|
} |
|
|
|
bool StudioModel::GetPoseParameterRange( int iParameter, float *pflMin, float *pflMax ) |
|
{ |
|
*pflMin = 0; |
|
*pflMax = 0; |
|
|
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return false; |
|
|
|
if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) |
|
return false; |
|
|
|
const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iParameter ); |
|
|
|
*pflMin = Pose.start; |
|
*pflMax = Pose.end; |
|
|
|
return true; |
|
} |
|
|
|
int StudioModel::LookupAttachment( char const *szName ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if ( !pStudioHdr ) |
|
return -1; |
|
|
|
for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) |
|
{ |
|
if (stricmp( pStudioHdr->pAttachment( i ).pszName(), szName ) == 0) |
|
{ |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
|
|
|
|
int StudioModel::SetBodygroup( int iGroup, int iValue /*= -1*/ ) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return 0; |
|
|
|
if (iGroup > pStudioHdr->numbodyparts()) |
|
return -1; |
|
|
|
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( iGroup ); |
|
|
|
int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels; |
|
|
|
// if the submodel index is not specified or out of range, just use the current value |
|
if ( iValue < 0 || 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()) |
|
{ |
|
return m_skinnum; |
|
} |
|
|
|
m_skinnum = iValue; |
|
|
|
return iValue; |
|
} |
|
|
|
|
|
|
|
void StudioModel::scaleMeshes (float scale) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return; |
|
|
|
int i, j, k; |
|
|
|
// manadatory to access correct verts |
|
SetCurrentModel(); |
|
|
|
// scale verts |
|
int tmp = m_bodynum; |
|
for (i = 0; i < pStudioHdr->numbodyparts(); i++) |
|
{ |
|
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( i ); |
|
for (j = 0; j < pbodypart->nummodels; j++) |
|
{ |
|
SetBodygroup (i, j); |
|
SetupModel (i); |
|
|
|
const mstudio_modelvertexdata_t *vertData = m_pmodel->GetVertexData(); |
|
Assert( vertData ); // This can only return NULL on X360 for now |
|
|
|
for (k = 0; k < m_pmodel->numvertices; k++) |
|
{ |
|
*vertData->Position(k) *= scale; |
|
} |
|
} |
|
} |
|
|
|
m_bodynum = tmp; |
|
|
|
// scale complex hitboxes |
|
int hitboxset = g_MDLViewer->GetCurrentHitboxSet(); |
|
|
|
mstudiobbox_t *pbboxes = pStudioHdr->pHitbox( 0, hitboxset ); |
|
for (i = 0; i < pStudioHdr->iHitboxCount( hitboxset ); i++) |
|
{ |
|
VectorScale (pbboxes[i].bbmin, scale, pbboxes[i].bbmin); |
|
VectorScale (pbboxes[i].bbmax, scale, pbboxes[i].bbmax); |
|
} |
|
|
|
// scale bounding boxes |
|
for (i = 0; i < pStudioHdr->GetNumSeq(); i++) |
|
{ |
|
mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( i ); |
|
Vector tmp; |
|
|
|
tmp = seqdesc.bbmin; |
|
VectorScale( tmp, scale, tmp ); |
|
seqdesc.bbmin = tmp; |
|
|
|
tmp = seqdesc.bbmax; |
|
VectorScale( tmp, scale, tmp ); |
|
seqdesc.bbmax = tmp; |
|
|
|
} |
|
|
|
// maybe scale exeposition, pivots, attachments |
|
} |
|
|
|
|
|
|
|
void StudioModel::scaleBones (float scale) |
|
{ |
|
CStudioHdr *pStudioHdr = GetStudioHdr(); |
|
if (!pStudioHdr) |
|
return; |
|
|
|
mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); |
|
for (int i = 0; i < pStudioHdr->numbones(); i++) |
|
{ |
|
pbones[i].pos *= scale; |
|
pbones[i].posscale *= scale; |
|
} |
|
} |
|
|
|
int StudioModel::Physics_GetBoneCount( void ) |
|
{ |
|
return m_pPhysics->Count(); |
|
} |
|
|
|
|
|
const char *StudioModel::Physics_GetBoneName( int index ) |
|
{ |
|
CPhysmesh *pmesh = m_pPhysics->GetMesh( index ); |
|
|
|
if ( !pmesh ) |
|
return NULL; |
|
|
|
return pmesh->m_boneName; |
|
} |
|
|
|
|
|
void StudioModel::Physics_GetData( int boneIndex, hlmvsolid_t *psolid, constraint_ragdollparams_t *pConstraint ) const |
|
{ |
|
CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex ); |
|
|
|
if ( !pMesh ) |
|
return; |
|
|
|
if ( psolid ) |
|
{ |
|
memcpy( psolid, &pMesh->m_solid, sizeof(*psolid) ); |
|
} |
|
|
|
if ( pConstraint ) |
|
{ |
|
*pConstraint = pMesh->m_constraint; |
|
} |
|
} |
|
|
|
void StudioModel::Physics_SetData( int boneIndex, const hlmvsolid_t *psolid, const constraint_ragdollparams_t *pConstraint ) |
|
{ |
|
CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex ); |
|
|
|
if ( !pMesh ) |
|
return; |
|
|
|
if ( psolid ) |
|
{ |
|
memcpy( &pMesh->m_solid, psolid, sizeof(*psolid) ); |
|
} |
|
|
|
if ( pConstraint ) |
|
{ |
|
pMesh->m_constraint = *pConstraint; |
|
} |
|
} |
|
|
|
|
|
float StudioModel::Physics_GetMass( void ) |
|
{ |
|
return m_pPhysics->GetMass(); |
|
} |
|
|
|
void StudioModel::Physics_SetMass( float mass ) |
|
{ |
|
m_physMass = mass; |
|
} |
|
|
|
|
|
char *StudioModel::Physics_DumpQC( void ) |
|
{ |
|
return m_pPhysics->DumpQC(); |
|
} |
|
|
|
const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData ) |
|
{ |
|
Assert( pModelData == NULL ); |
|
Assert( g_pActiveModel ); |
|
|
|
return g_pStudioDataCache->CacheVertexData( g_pActiveModel->GetStudioRenderHdr() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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*)handle; |
|
return g_pMDLCache->GetStudioHdr( handle ); |
|
} |
|
|
|
virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const |
|
{ |
|
return g_pMDLCache->GetVirtualModel( (MDLHandle_t)virtualModel ); |
|
} |
|
|
|
byte *studiohdr_t::GetAnimBlock( int i ) const |
|
{ |
|
return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i ); |
|
} |
|
|
|
int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const |
|
{ |
|
return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut ); |
|
} |
|
|
|
const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const |
|
{ |
|
return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache ); |
|
}
|
|
|