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.
1073 lines
34 KiB
1073 lines
34 KiB
//===== Copyright � 2005-2013, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Utility methods for mdl files
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "tier3/mdlutils.h"
|
|
#include "tier0/dbg.h"
|
|
#include "tier3/tier3.h"
|
|
#include "studio.h"
|
|
#include "istudiorender.h"
|
|
#include "bone_setup.h"
|
|
#include "bone_accessor.h"
|
|
#include "materialsystem/imaterialvar.h"
|
|
#include "vcollide_parse.h"
|
|
#include "renderparm.h"
|
|
#include "tier2/renderutils.h"
|
|
#include "mathlib/camera.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.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 );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Determines which pose parameters are used by the specified sequence
|
|
//-----------------------------------------------------------------------------
|
|
void FindSequencePoseParameters( CStudioHdr &hdr, int nSequence, bool *pPoseParameters, int nCount )
|
|
{
|
|
if ( ( nSequence < 0 ) && ( nSequence >= hdr.GetNumSeq() ) )
|
|
return;
|
|
|
|
const mstudioseqdesc_t &seqdesc = hdr.pSeqdesc( nSequence );
|
|
|
|
// Add the pose parameters that are directly referenced by this sequence
|
|
int nParamIndex;
|
|
nParamIndex = hdr.GetSharedPoseParameter( nSequence, seqdesc.paramindex[ 0 ] );
|
|
if ( ( nParamIndex >= 0 ) && ( nParamIndex < nCount ) )
|
|
{
|
|
pPoseParameters[ nParamIndex ] = true;
|
|
}
|
|
|
|
nParamIndex = hdr.GetSharedPoseParameter( nSequence, seqdesc.paramindex[ 1 ] );
|
|
if ( ( nParamIndex >= 0 ) && ( nParamIndex < nCount ) )
|
|
{
|
|
pPoseParameters[ nParamIndex ] = true;
|
|
}
|
|
|
|
if ( seqdesc.flags & STUDIO_CYCLEPOSE )
|
|
{
|
|
nParamIndex = hdr.GetSharedPoseParameter( nSequence, seqdesc.cycleposeindex );
|
|
if ( ( nParamIndex >= 0 ) && ( nParamIndex < nCount ) )
|
|
{
|
|
pPoseParameters[ nParamIndex ] = true;
|
|
}
|
|
}
|
|
|
|
// Now recursively add the parameters for the auto layers
|
|
for ( int i = 0; i < seqdesc.numautolayers; ++i )
|
|
{
|
|
const mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i );
|
|
int nLayerSequence = hdr.iRelativeSeq( nSequence, pLayer->iSequence );
|
|
if ( nLayerSequence != nSequence )
|
|
{
|
|
FindSequencePoseParameters( hdr, nLayerSequence, pPoseParameters, nCount );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
m_bUseSequencePlaybackFPS = false;
|
|
m_flTimeBasisAdjustment = 0.0f;
|
|
|
|
// Deal with the default cubemap
|
|
ITexture *pCubemapTexture = g_pMaterialSystem->FindTexture( "editor/cubemap", NULL, true );
|
|
m_DefaultEnvCubemap.Init( pCubemapTexture );
|
|
pCubemapTexture = g_pMaterialSystem->FindTexture( "editor/cubemap.hdr", NULL, true );
|
|
m_DefaultHDREnvCubemap.Init( pCubemapTexture );
|
|
|
|
m_pSimpleMaterialOverride = NULL;
|
|
}
|
|
|
|
CMDL::~CMDL()
|
|
{
|
|
m_DefaultEnvCubemap.Shutdown( );
|
|
m_DefaultHDREnvCubemap.Shutdown();
|
|
|
|
if ( m_pSimpleMaterialOverride != NULL )
|
|
{
|
|
m_pSimpleMaterialOverride.Shutdown();
|
|
m_pSimpleMaterialOverride = NULL;
|
|
}
|
|
|
|
UnreferenceMDL();
|
|
}
|
|
|
|
ITexture *CMDL::GetEnvCubeMap()
|
|
{
|
|
if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE )
|
|
{
|
|
return m_DefaultEnvCubemap;
|
|
}
|
|
else
|
|
{
|
|
return m_DefaultHDREnvCubemap;
|
|
}
|
|
}
|
|
|
|
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->GetStudioHdr( 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;
|
|
}
|
|
}
|
|
|
|
if ( m_Attachments.Count() != pHdr->GetNumAttachments() )
|
|
{
|
|
m_Attachments.SetSize( pHdr->GetNumAttachments() );
|
|
|
|
// This is to make sure we don't use the attachment before its been set up
|
|
for ( int i=0; i < m_Attachments.Count(); i++ )
|
|
{
|
|
m_Attachments[i].m_bValid = false;
|
|
#ifdef _DEBUG
|
|
m_Attachments[i].m_AttachmentToWorld.Invalidate();
|
|
#endif
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MDLHandle_t CMDL::GetMDL() const
|
|
{
|
|
return m_MDLHandle;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Release the MDL handle
|
|
//-----------------------------------------------------------------------------
|
|
void CMDL::UnreferenceMDL()
|
|
{
|
|
if ( !g_pMDLCache )
|
|
return;
|
|
|
|
if ( m_MDLHandle != MDLHANDLE_INVALID )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
|
|
void CMDL::SetSimpleMaterialOverride( IMaterial *pNewMaterial )
|
|
{
|
|
m_pSimpleMaterialOverride.Init( pNewMaterial );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws the mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CMDL::Draw( const matrix3x4_t& rootToWorld, const matrix3x4_t *pBoneToWorld, int flags )
|
|
{
|
|
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 );
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
CMatRenderData< float > rdFlexWeights( pRenderContext );
|
|
|
|
// Set default flex values
|
|
float *pFlexWeights = NULL;
|
|
const int nFlexDescCount = info.m_pStudioHdr->numflexdesc;
|
|
if ( nFlexDescCount )
|
|
{
|
|
CStudioHdr cStudioHdr( info.m_pStudioHdr, g_pMDLCache );
|
|
pFlexWeights = rdFlexWeights.Lock( info.m_pStudioHdr->numflexdesc );
|
|
cStudioHdr.RunFlexRules( m_pFlexControls, pFlexWeights );
|
|
}
|
|
|
|
Vector vecModelOrigin;
|
|
MatrixGetColumn( rootToWorld, 3, vecModelOrigin );
|
|
|
|
|
|
bool bOverride = false;
|
|
static ConVarRef cl_custom_material_override( "cl_custom_material_override" );
|
|
if ( cl_custom_material_override.IsValid() && cl_custom_material_override.GetBool() && !g_pStudioRender->IsForcedMaterialOverride() )
|
|
{
|
|
for ( int i = 0; i < GetCustomMaterialCount(); i++ )
|
|
{
|
|
if ( IsCustomMaterialValid( i ) )
|
|
{
|
|
g_pStudioRender->ForcedMaterialOverride( GetCustomMaterial( i )->GetMaterial(), OVERRIDE_SELECTIVE, i );
|
|
bOverride = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_pSimpleMaterialOverride != NULL )
|
|
{
|
|
bOverride = true;
|
|
g_pStudioRender->ForcedMaterialOverride( m_pSimpleMaterialOverride );
|
|
}
|
|
|
|
g_pStudioRender->DrawModel( NULL, info, const_cast<matrix3x4_t*>( pBoneToWorld ),
|
|
pFlexWeights, NULL, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL | flags );
|
|
|
|
if ( bOverride )
|
|
{
|
|
g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
}
|
|
}
|
|
|
|
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 );
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
CMatRenderData< matrix3x4_t > rdBoneToWorld( pRenderContext, pStudioHdr->numbones );
|
|
SetUpBones( rootToWorld, pStudioHdr->numbones, rdBoneToWorld.Base() );
|
|
Draw( rootToWorld, rdBoneToWorld.Base() );
|
|
}
|
|
|
|
|
|
void CMDL::SetUpBones( const matrix3x4_t& rootToWorld, int nMaxBoneCount, matrix3x4_t *pBoneToWorld, const float *pPoseParameters, MDLSquenceLayer_t *pSequenceLayers, int nNumSequenceLayers )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache );
|
|
|
|
// Default to middle of the pose parameter range
|
|
float defaultPoseParameters[MAXSTUDIOPOSEPARAM];
|
|
if ( pPoseParameters == NULL )
|
|
{
|
|
Studio_CalcDefaultPoseParameters( &studioHdr, defaultPoseParameters, MAXSTUDIOPOSEPARAM );
|
|
pPoseParameters = defaultPoseParameters;
|
|
}
|
|
|
|
int nFrameCount = Studio_MaxFrame( &studioHdr, m_nSequence, pPoseParameters );
|
|
if ( nFrameCount == 0 )
|
|
{
|
|
nFrameCount = 1;
|
|
}
|
|
float flPlaybackRate = m_bUseSequencePlaybackFPS ? Studio_FPS( &studioHdr, m_nSequence, pPoseParameters ) : m_flPlaybackRate;
|
|
float flAdjustedTime = m_flTime - m_flTimeBasisAdjustment;
|
|
float flCycle = ( flAdjustedTime * flPlaybackRate ) / nFrameCount;
|
|
m_flCurrentAnimEndTime = flPlaybackRate > 0.0f ? float( nFrameCount ) / flPlaybackRate : float( nFrameCount );
|
|
|
|
if ( flCycle > 1.0f )
|
|
{
|
|
// We need to rollover into the next sequence followup
|
|
if ( flPlaybackRate > 0.0f && flAdjustedTime < float(flPlaybackRate) )
|
|
m_flTimeBasisAdjustment += float( nFrameCount ) / float( flPlaybackRate );
|
|
else
|
|
m_flTimeBasisAdjustment = m_flTime;
|
|
|
|
if ( m_arrSequenceFollowLoop.Count() )
|
|
{
|
|
m_nSequence = m_arrSequenceFollowLoop.Head();
|
|
m_arrSequenceFollowLoop.RemoveMultipleFromHead( 1 );
|
|
// Recurse with the updated sequence
|
|
SetUpBones( rootToWorld, nMaxBoneCount, pBoneToWorld, pPoseParameters, pSequenceLayers, nNumSequenceLayers );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
flAdjustedTime = m_flTime - m_flTimeBasisAdjustment;
|
|
flCycle = ( flAdjustedTime * flPlaybackRate ) / nFrameCount;
|
|
}
|
|
}
|
|
|
|
// FIXME: We're always wrapping; may want to determing if we should clamp
|
|
flCycle = SubtractIntegerPart(flCycle);
|
|
|
|
BoneVector pos[MAXSTUDIOBONES];
|
|
BoneQuaternionAligned q[MAXSTUDIOBONES];
|
|
|
|
IBoneSetup boneSetup( &studioHdr, BONE_USED_BY_ANYTHING_AT_LOD( m_nLOD ), pPoseParameters, NULL );
|
|
boneSetup.InitPose( pos, q );
|
|
boneSetup.AccumulatePose( pos, q, m_nSequence, flCycle, 1.0f, flAdjustedTime, 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;
|
|
|
|
int nFrameCount = MAX( 1, Studio_MaxFrame( &studioHdr, nSeqIndex, pPoseParameters ) );
|
|
float flLayerCycle = ( flAdjustedTime * flPlaybackRate ) / nFrameCount;
|
|
|
|
// FIXME: We're always wrapping; may want to determing if we should clamp
|
|
flLayerCycle = SubtractIntegerPart(flLayerCycle);
|
|
|
|
boneSetup.AccumulatePose( pos, q, nSeqIndex, flLayerCycle, flWeight, flAdjustedTime, 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 );
|
|
|
|
// WARNING: converting from matrix3x4_t to matrix3x4a_t is going to asplode on a console.
|
|
// Calculate helper bones!
|
|
AssertAligned( pBoneToWorld );
|
|
CBoneAccessor tempCBoneAccessor( ( matrix3x4a_t * )pBoneToWorld );
|
|
if ( CalcProceduralBone( &studioHdr, i, tempCBoneAccessor ) )
|
|
{
|
|
}
|
|
else 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 );
|
|
|
|
SetupBones_AttachmentHelper( &studioHdr, pBoneToWorld );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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
|
|
float flPoseParameter[MAXSTUDIOPOSEPARAM];
|
|
Studio_CalcDefaultPoseParameters( pMergeHdr, flPoseParameter, MAXSTUDIOPOSEPARAM );
|
|
|
|
int nFrameCount = Studio_MaxFrame( pMergeHdr, m_nSequence, flPoseParameter );
|
|
if ( nFrameCount == 0 )
|
|
{
|
|
nFrameCount = 1;
|
|
}
|
|
float flPlaybackRate = m_bUseSequencePlaybackFPS ? Studio_FPS( pMergeHdr, m_nSequence, flPoseParameter ) : m_flPlaybackRate;
|
|
float flAdjustedTime = m_flTime - m_flTimeBasisAdjustment;
|
|
float flCycle = ( flAdjustedTime * flPlaybackRate ) / nFrameCount;
|
|
m_flCurrentAnimEndTime = flPlaybackRate > 0.0f ? float( nFrameCount ) / flPlaybackRate : float( nFrameCount );
|
|
|
|
if ( flCycle > 1.0f )
|
|
{
|
|
// We need to rollover into the next sequence followup
|
|
if ( flPlaybackRate > 0.0f && flAdjustedTime < float(flPlaybackRate) )
|
|
m_flTimeBasisAdjustment += float( nFrameCount ) / float( flPlaybackRate );
|
|
else
|
|
m_flTimeBasisAdjustment = m_flTime;
|
|
|
|
if ( m_arrSequenceFollowLoop.Count() )
|
|
{
|
|
m_nSequence = m_arrSequenceFollowLoop.Head();
|
|
m_arrSequenceFollowLoop.RemoveMultipleFromHead( 1 );
|
|
// Recurse with the updated sequence
|
|
SetupBonesWithBoneMerge( pMergeHdr, pMergeBoneToWorld, pFollow, pFollowBoneToWorld, matModelToWorld );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
flAdjustedTime = m_flTime - m_flTimeBasisAdjustment;
|
|
flCycle = ( flAdjustedTime * flPlaybackRate ) / nFrameCount;
|
|
}
|
|
}
|
|
|
|
// FIXME: We're always wrapping; may want to determing if we should clamp
|
|
flCycle = SubtractIntegerPart(flCycle);
|
|
|
|
BoneVector pos[MAXSTUDIOBONES];
|
|
BoneQuaternionAligned q[MAXSTUDIOBONES];
|
|
|
|
IBoneSetup boneSetup( pMergeHdr, BONE_USED_BY_ANYTHING_AT_LOD( m_nLOD ), flPoseParameter );
|
|
boneSetup.InitPose( pos, q );
|
|
boneSetup.AccumulatePose( pos, q, m_nSequence, flCycle, 1.0f, flAdjustedTime, NULL );
|
|
|
|
// Get the merge bone list.
|
|
const 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] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMDL::SetupBones_AttachmentHelper( CStudioHdr *hdr, matrix3x4_t *pBoneToWorld )
|
|
{
|
|
if ( !hdr || !hdr->GetNumAttachments() )
|
|
return;
|
|
|
|
// calculate attachment points
|
|
matrix3x4_t world;
|
|
for (int i = 0; i < hdr->GetNumAttachments(); i++)
|
|
{
|
|
const mstudioattachment_t &pattachment = hdr->pAttachment( i );
|
|
int iBone = hdr->GetAttachmentBone( i );
|
|
|
|
if ( (pattachment.flags & ATTACHMENT_FLAG_WORLD_ALIGN) == 0 )
|
|
{
|
|
ConcatTransforms( pBoneToWorld[iBone], pattachment.local, world );
|
|
}
|
|
else
|
|
{
|
|
Vector vecLocalBonePos, vecWorldBonePos;
|
|
MatrixGetColumn( pattachment.local, 3, vecLocalBonePos );
|
|
VectorTransform( vecLocalBonePos, pBoneToWorld[iBone], vecWorldBonePos );
|
|
|
|
SetIdentityMatrix( world );
|
|
MatrixSetColumn( vecWorldBonePos, 3, world );
|
|
}
|
|
|
|
PutAttachment( i + 1, world );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Put a value into an attachment point by index
|
|
// Input : number - which point
|
|
// Output : float * - the attachment point
|
|
//-----------------------------------------------------------------------------
|
|
bool CMDL::PutAttachment( int number, const matrix3x4_t &attachmentToWorld )
|
|
{
|
|
if ( number < 1 || number > m_Attachments.Count() )
|
|
return false;
|
|
|
|
CMDLAttachmentData *pAtt = &m_Attachments[number-1];
|
|
pAtt->m_AttachmentToWorld = attachmentToWorld;
|
|
pAtt->m_bValid = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMDL::GetAttachment( int number, matrix3x4_t& matrix )
|
|
{
|
|
if ( number < 1 || number > m_Attachments.Count() )
|
|
return false;
|
|
|
|
if ( !m_Attachments[number-1].m_bValid )
|
|
return false;
|
|
|
|
matrix = m_Attachments[number-1].m_AttachmentToWorld;
|
|
return true;
|
|
}
|
|
|
|
bool CMDL::GetAttachment( const char *pszAttachment, matrix3x4_t& matrixOut )
|
|
{
|
|
if ( GetMDL() == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( GetMDL() ), g_pMDLCache );
|
|
|
|
int iAttachmentNum = Studio_FindAttachment( &studioHdr, pszAttachment );
|
|
if ( iAttachmentNum == -1 )
|
|
return false;
|
|
|
|
return GetAttachment( iAttachmentNum + 1, matrixOut );
|
|
}
|
|
|
|
bool CMDL::GetBoundingSphere( Vector &vecCenter, float &flRadius )
|
|
{
|
|
// Check to see if we have a valid model to look at.
|
|
if ( m_MDLHandle == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
GetMDLBoundingSphere( &vecCenter, &flRadius, m_MDLHandle, m_nSequence );
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMDL::AdjustTime( float flAmount )
|
|
{
|
|
m_flTime += flAmount;
|
|
}
|
|
|
|
MDLData_t::MDLData_t()
|
|
{
|
|
SetIdentityMatrix( m_MDLToWorld );
|
|
m_bRequestBoneMergeTakeover = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CMergedMDL::CMergedMDL()
|
|
{
|
|
m_nNumSequenceLayers = 0;
|
|
}
|
|
|
|
CMergedMDL::~CMergedMDL()
|
|
{
|
|
m_aMergeMDLs.Purge();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stores the clip
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetMDL( MDLHandle_t handle, CCustomMaterialOwner* pCustomMaterialOwner, void *pProxyData )
|
|
{
|
|
m_RootMDL.m_MDL.SetMDL( handle );
|
|
m_RootMDL.m_MDL.m_pProxyData = pProxyData;
|
|
|
|
Vector vecMins, vecMaxs;
|
|
GetMDLBoundingBox( &vecMins, &vecMaxs, handle, m_RootMDL.m_MDL.m_nSequence );
|
|
|
|
m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = false;
|
|
m_RootMDL.m_MDL.m_vecViewTarget.Init( 100.0f, 0.0f, vecMaxs.z );
|
|
|
|
if ( pCustomMaterialOwner )
|
|
{
|
|
pCustomMaterialOwner->DuplicateCustomMaterialsToOther( &m_RootMDL.m_MDL );
|
|
}
|
|
|
|
// Set the pose parameters to the default for the mdl
|
|
SetPoseParameters( NULL, 0 );
|
|
|
|
// Clear any sequence layers
|
|
SetSequenceLayers( NULL, 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// An MDL was selected
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetMDL( const char *pMDLName, CCustomMaterialOwner* pCustomMaterialOwner, void *pProxyData )
|
|
{
|
|
MDLHandle_t hMDL = pMDLName ? g_pMDLCache->FindMDL( pMDLName ) : MDLHANDLE_INVALID;
|
|
if ( g_pMDLCache->IsErrorModel( hMDL ) )
|
|
{
|
|
hMDL = MDLHANDLE_INVALID;
|
|
}
|
|
|
|
SetMDL( hMDL, pCustomMaterialOwner, pProxyData );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns a model bounding box.
|
|
//-----------------------------------------------------------------------------
|
|
bool CMergedMDL::GetBoundingBox( Vector &vecBoundsMin, Vector &vecBoundsMax )
|
|
{
|
|
// Check to see if we have a valid model to look at.
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
GetMDLBoundingBox( &vecBoundsMin, &vecBoundsMax, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns a more accurate bounding sphere
|
|
//-----------------------------------------------------------------------------
|
|
bool CMergedMDL::GetBoundingSphere( Vector &vecCenter, float &flRadius )
|
|
{
|
|
// Check to see if we have a valid model to look at.
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
Vector vecEngineCenter;
|
|
GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
|
|
VectorTransform( vecEngineCenter, m_RootMDL.m_MDLToWorld, vecCenter );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CMergedMDL::GetAttachment( const char *pszAttachment, matrix3x4_t& matrixOut )
|
|
{
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
|
|
|
|
int iAttachmentNum = Studio_FindAttachment( &studioHdr, pszAttachment );
|
|
if ( iAttachmentNum == -1 )
|
|
return false;
|
|
|
|
return GetAttachment( iAttachmentNum + 1, matrixOut );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CMergedMDL::GetAttachment( int iAttachmentNum, matrix3x4_t& matrixOut )
|
|
{
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return false;
|
|
|
|
return m_RootMDL.m_MDL.GetAttachment( iAttachmentNum, matrixOut );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos )
|
|
{
|
|
SetIdentityMatrix( m_RootMDL.m_MDLToWorld );
|
|
AngleMatrix( angRot, vecPos, m_RootMDL.m_MDLToWorld );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetupBonesForAttachmentQueries( void )
|
|
{
|
|
if ( !g_pMDLCache )
|
|
return;
|
|
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return;
|
|
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
|
|
CStudioHdr *pRootStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
|
|
CMatRenderData< matrix3x4_t > rdBoneToWorld( pRenderContext, pRootStudioHdr->numbones() );
|
|
m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, pRootStudioHdr->numbones(), rdBoneToWorld.Base(), m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers );
|
|
|
|
delete pRootStudioHdr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// paint it!
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::Draw()
|
|
{
|
|
if ( !g_pMDLCache )
|
|
return;
|
|
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return;
|
|
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
|
|
// Draw the MDL
|
|
CStudioHdr *pRootStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
|
|
CMatRenderData< matrix3x4_t > rdBoneToWorld( pRenderContext, pRootStudioHdr->numbones() );
|
|
const matrix3x4_t *pRootMergeHdrModelToWorld = &m_RootMDL.m_MDLToWorld;
|
|
const matrix3x4_t *pFollowBoneToWorld = rdBoneToWorld.Base();
|
|
m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, pRootStudioHdr->numbones(), rdBoneToWorld.Base(), m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers );
|
|
|
|
OnPostSetUpBonesPreDraw();
|
|
|
|
int nFlags = STUDIORENDER_DRAW_NO_SHADOWS;
|
|
|
|
OnModelDrawPassStart( 0, pRootStudioHdr, nFlags );
|
|
m_RootMDL.m_MDL.Draw( m_RootMDL.m_MDLToWorld, rdBoneToWorld.Base(), nFlags );
|
|
OnModelDrawPassFinished( 0, pRootStudioHdr, nFlags );
|
|
|
|
// Draw the merge MDLs.
|
|
matrix3x4_t *pStackCopyOfRootMergeHdrModelToWorld = NULL;
|
|
matrix3x4_t matMergeBoneToWorld[MAXSTUDIOBONES];
|
|
int nMergeCount = m_aMergeMDLs.Count();
|
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
|
|
{
|
|
matrix3x4_t *pMergeBoneToWorld = &matMergeBoneToWorld[0];
|
|
|
|
// Get the merge studio header.
|
|
CStudioHdr *pMergeHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_aMergeMDLs[iMerge].m_MDL.GetMDL() ), g_pMDLCache );
|
|
m_aMergeMDLs[iMerge].m_MDL.SetupBonesWithBoneMerge( pMergeHdr, pMergeBoneToWorld, pRootStudioHdr, pFollowBoneToWorld, *pRootMergeHdrModelToWorld );
|
|
|
|
OnModelDrawPassStart( 0, pMergeHdr, nFlags );
|
|
m_aMergeMDLs[iMerge].m_MDL.Draw( m_aMergeMDLs[iMerge].m_MDLToWorld, pMergeBoneToWorld, nFlags );
|
|
OnModelDrawPassFinished( 0, pMergeHdr, nFlags );
|
|
|
|
if ( m_aMergeMDLs[iMerge].m_bRequestBoneMergeTakeover && ( iMerge + 1 < nMergeCount ) )
|
|
{
|
|
// This model is requesting bonemerge takeover and we have more models to render after it
|
|
delete pRootStudioHdr;
|
|
pRootStudioHdr = pMergeHdr;
|
|
pRootMergeHdrModelToWorld = &m_aMergeMDLs[iMerge].m_MDLToWorld;
|
|
|
|
// Make a copy of bone to world transforms in a separate stack buffer and repoint root transforms
|
|
// for future bonemerge into that buffer
|
|
if ( !pStackCopyOfRootMergeHdrModelToWorld )
|
|
pStackCopyOfRootMergeHdrModelToWorld = ( matrix3x4_t * ) stackalloc( sizeof( matMergeBoneToWorld ) );
|
|
Q_memcpy( pStackCopyOfRootMergeHdrModelToWorld, matMergeBoneToWorld, sizeof( matMergeBoneToWorld ) );
|
|
pFollowBoneToWorld = pStackCopyOfRootMergeHdrModelToWorld;
|
|
}
|
|
else
|
|
{
|
|
delete pMergeHdr;
|
|
}
|
|
}
|
|
rdBoneToWorld.Release();
|
|
|
|
delete pRootStudioHdr;
|
|
}
|
|
|
|
|
|
void CMergedMDL::Draw( const matrix3x4_t &rootToWorld )
|
|
{
|
|
m_RootMDL.m_MDLToWorld = rootToWorld;
|
|
Draw();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the current sequence
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetSequence( int nSequence, bool bUseSequencePlaybackFPS )
|
|
{
|
|
m_RootMDL.m_MDL.m_nSequence = nSequence;
|
|
m_RootMDL.m_MDL.m_bUseSequencePlaybackFPS = bUseSequencePlaybackFPS;
|
|
m_RootMDL.m_MDL.m_flTimeBasisAdjustment = m_RootMDL.m_MDL.m_flTime;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Add a follow loop sequence
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::AddSequenceFollowLoop( int nSequence, bool bUseSequencePlaybackFPS )
|
|
{
|
|
Assert( bUseSequencePlaybackFPS == m_RootMDL.m_MDL.m_bUseSequencePlaybackFPS );
|
|
m_RootMDL.m_MDL.m_arrSequenceFollowLoop.AddToTail( nSequence );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Clear any follow loop sequences
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::ClearSequenceFollowLoop()
|
|
{
|
|
m_RootMDL.m_MDL.m_arrSequenceFollowLoop.RemoveAll();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Set the current pose parameters. If NULL the pose parameters will be reset
|
|
// to the default values.
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetPoseParameters( const float *pPoseParameters, int nCount )
|
|
{
|
|
if ( pPoseParameters )
|
|
{
|
|
int nParameters = MIN( MAXSTUDIOPOSEPARAM, nCount );
|
|
for ( int iParam = 0; iParam < nParameters; ++iParam )
|
|
{
|
|
m_PoseParameters[ iParam ] = pPoseParameters[ iParam ];
|
|
}
|
|
}
|
|
else if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID )
|
|
{
|
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
|
|
Studio_CalcDefaultPoseParameters( &studioHdr, m_PoseParameters, MAXSTUDIOPOSEPARAM );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Set the overlay sequence layers
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetSequenceLayers( const MDLSquenceLayer_t *pSequenceLayers, int nCount )
|
|
{
|
|
if ( pSequenceLayers )
|
|
{
|
|
m_nNumSequenceLayers = MIN( MAX_SEQUENCE_LAYERS, nCount );
|
|
for ( int iLayer = 0; iLayer < m_nNumSequenceLayers; ++iLayer )
|
|
{
|
|
m_SequenceLayers[ iLayer ] = pSequenceLayers[ iLayer ];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nNumSequenceLayers = 0;
|
|
V_memset( m_SequenceLayers, 0, sizeof( m_SequenceLayers ) );
|
|
}
|
|
}
|
|
|
|
void CMergedMDL::SetSkin( int nSkin )
|
|
{
|
|
m_RootMDL.m_MDL.m_nSkin = nSkin;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::SetMergeMDL( MDLHandle_t handle, CCustomMaterialOwner* pCustomMaterialOwner, void *pProxyData, bool bRequestBonemergeTakeover )
|
|
{
|
|
// Verify that we have a root model to merge to.
|
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
|
|
return;
|
|
|
|
int iIndex = m_aMergeMDLs.AddToTail();
|
|
if ( !m_aMergeMDLs.IsValidIndex( iIndex ) )
|
|
return;
|
|
|
|
m_aMergeMDLs[iIndex].m_MDL.SetMDL( handle );
|
|
m_aMergeMDLs[iIndex].m_MDL.m_pProxyData = pProxyData;
|
|
m_aMergeMDLs[iIndex].m_bRequestBoneMergeTakeover = bRequestBonemergeTakeover;
|
|
|
|
if ( pCustomMaterialOwner )
|
|
{
|
|
pCustomMaterialOwner->DuplicateCustomMaterialsToOther( &m_aMergeMDLs[iIndex].m_MDL );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
MDLHandle_t CMergedMDL::SetMergeMDL( const char *pMDLName, CCustomMaterialOwner* pCustomMaterialOwner, void *pProxyData, bool bRequestBonemergeTakeover )
|
|
{
|
|
if ( g_pMDLCache == NULL )
|
|
return MDLHANDLE_INVALID;
|
|
|
|
MDLHandle_t hMDL = pMDLName ? g_pMDLCache->FindMDL( pMDLName ) : MDLHANDLE_INVALID;
|
|
if ( g_pMDLCache->IsErrorModel( hMDL ) )
|
|
{
|
|
hMDL = MDLHANDLE_INVALID;
|
|
}
|
|
|
|
SetMergeMDL( hMDL, pCustomMaterialOwner, pProxyData, bRequestBonemergeTakeover );
|
|
return hMDL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CMergedMDL::GetMergeMDLIndex( MDLHandle_t handle )
|
|
{
|
|
int nMergeCount = m_aMergeMDLs.Count();
|
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
|
|
{
|
|
if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
|
|
return iMerge;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CMDL *CMergedMDL::GetMergeMDL( MDLHandle_t handle )
|
|
{
|
|
int nMergeCount = m_aMergeMDLs.Count();
|
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
|
|
{
|
|
if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
|
|
return (&m_aMergeMDLs[iMerge].m_MDL);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::ClearMergeMDLs( void )
|
|
{
|
|
m_aMergeMDLs.Purge();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CMergedMDL::UpdateModelCustomMaterials( MDLHandle_t handle, CCustomMaterialOwner* pCustomMaterialOwner )
|
|
{
|
|
CMDL* pMDL = (handle != MDLHANDLE_INVALID) ? GetMergeMDL( handle ) : NULL;
|
|
|
|
if ( pMDL )
|
|
{
|
|
if ( pCustomMaterialOwner )
|
|
{
|
|
pCustomMaterialOwner->DuplicateCustomMaterialsToOther( pMDL );
|
|
}
|
|
else
|
|
{
|
|
pMDL->ClearCustomMaterials();
|
|
}
|
|
}
|
|
}
|