Counter Strike : Global Offensive Source Code
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

//===== 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();
}
}
}