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.
902 lines
29 KiB
902 lines
29 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "matsys_controls/mdlpanel.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/imaterialsystemhardwareconfig.h" |
|
#include "materialsystem/imesh.h" |
|
#include "vgui/IVGui.h" |
|
#include "tier1/KeyValues.h" |
|
#include "vgui_controls/Frame.h" |
|
#include "tier1/convar.h" |
|
#include "tier0/dbg.h" |
|
#include "tier1/fmtstr.h" |
|
#include "istudiorender.h" |
|
#include "matsys_controls/matsyscontrols.h" |
|
#include "vcollide.h" |
|
#include "vcollide_parse.h" |
|
#include "bone_setup.h" |
|
#include "vphysics_interface.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
using namespace vgui; |
|
|
|
DECLARE_BUILD_FACTORY( CMDLPanel ); |
|
|
|
static const int THUMBNAIL_SAFE_ZONE_SIZE = 512; |
|
static const int THUMBNAIL_SAFE_ZONE_HEIGHT = 92; |
|
static const float THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE = (float)THUMBNAIL_SAFE_ZONE_HEIGHT / THUMBNAIL_SAFE_ZONE_SIZE; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Keeps a global clock to autoplay sequences to run from |
|
// Also deals with speedScale changes |
|
//----------------------------------------------------------------------------- |
|
float GetAutoPlayTime( void ) |
|
{ |
|
static int g_prevTicks; |
|
static float g_time; |
|
|
|
int ticks = Plat_MSTime(); |
|
|
|
// limit delta so that float time doesn't overflow |
|
if (g_prevTicks == 0) |
|
{ |
|
g_prevTicks = ticks; |
|
} |
|
|
|
g_time += ( ticks - g_prevTicks ) / 1000.0f; |
|
g_prevTicks = ticks; |
|
|
|
return g_time; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CMDLPanel::CMDLPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) |
|
{ |
|
SetVisible( true ); |
|
|
|
// Used to poll input |
|
vgui::ivgui()->AddTickSignal( GetVPanel() ); |
|
|
|
// Deal with the default cubemap |
|
ITexture *pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap", NULL, true ); |
|
m_DefaultEnvCubemap.Init( pCubemapTexture ); |
|
pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap.hdr", NULL, true ); |
|
m_DefaultHDREnvCubemap.Init( pCubemapTexture ); |
|
|
|
SetIdentityMatrix( m_RootMDL.m_MDLToWorld ); |
|
m_bDrawCollisionModel = false; |
|
m_bWireFrame = false; |
|
m_bGroundGrid = false; |
|
m_bLockView = false; |
|
m_bLookAtCamera = true; |
|
m_bThumbnailSafeZone = false; |
|
m_nNumSequenceLayers = 0; |
|
ResetAnimationEventState( &m_EventState ); |
|
} |
|
|
|
CMDLPanel::~CMDLPanel() |
|
{ |
|
m_aMergeMDLs.Purge(); |
|
m_DefaultEnvCubemap.Shutdown( ); |
|
m_DefaultHDREnvCubemap.Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Scheme settings |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
SetBackgroundColor( GetBgColor() ); |
|
SetBorder( pScheme->GetBorder( "MenuBorder") ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Rendering options |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetCollsionModel( bool bVisible ) |
|
{ |
|
m_bDrawCollisionModel = bVisible; |
|
} |
|
|
|
void CMDLPanel::SetGroundGrid( bool bVisible ) |
|
{ |
|
m_bGroundGrid = bVisible; |
|
} |
|
|
|
void CMDLPanel::SetWireFrame( bool bVisible ) |
|
{ |
|
m_bWireFrame = bVisible; |
|
} |
|
|
|
void CMDLPanel::SetLockView( bool bLocked ) |
|
{ |
|
m_bLockView = bLocked; |
|
} |
|
|
|
void CMDLPanel::SetLookAtCamera( bool bLookAtCamera ) |
|
{ |
|
m_bLookAtCamera = bLookAtCamera; |
|
} |
|
|
|
void CMDLPanel::SetIgnoreDoubleClick( bool bState ) |
|
{ |
|
m_bIgnoreDoubleClick = bState; |
|
} |
|
|
|
void CMDLPanel::SetThumbnailSafeZone( bool bVisible ) |
|
{ |
|
m_bThumbnailSafeZone = bVisible; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Stores the clip |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetMDL( MDLHandle_t handle, 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 ); |
|
|
|
m_RootMDL.m_flCycleStartTime = 0.f; |
|
|
|
// Set the pose parameters to the default for the mdl |
|
SetPoseParameters( NULL, 0 ); |
|
|
|
// Clear any sequence layers |
|
SetSequenceLayers( NULL, 0 ); |
|
|
|
ResetAnimationEventState( &m_EventState ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// An MDL was selected |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetMDL( const char *pMDLName, void *pProxyData ) |
|
{ |
|
MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName ); |
|
MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID; |
|
if ( vgui::MDLCache()->IsErrorModel( hMDL ) ) |
|
{ |
|
hMDL = MDLHANDLE_INVALID; |
|
} |
|
|
|
SetMDL( hMDL, pProxyData ); |
|
|
|
// FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference. |
|
int nRef = vgui::MDLCache()->Release( hMDLFindResult ); |
|
(void)nRef; // Avoid unreferenced variable warning |
|
AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMDL referenced a model that has a zero ref count." ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns a model bounding box. |
|
//----------------------------------------------------------------------------- |
|
bool CMDLPanel::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 CMDLPanel::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: |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ) |
|
{ |
|
SetIdentityMatrix( m_RootMDL.m_MDLToWorld ); |
|
AngleMatrix( angRot, vecPos, m_RootMDL.m_MDLToWorld ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the camera to look at the model |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::LookAtMDL() |
|
{ |
|
// Check to see if we have a valid model to look at. |
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) |
|
return; |
|
|
|
if ( m_bLockView ) |
|
return; |
|
|
|
float flRadius; |
|
Vector vecCenter; |
|
GetBoundingSphere( vecCenter, flRadius ); |
|
LookAt( vecCenter, flRadius ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// FIXME: This should be moved into studiorender |
|
//----------------------------------------------------------------------------- |
|
static ConVar r_showenvcubemap( "r_showenvcubemap", "0", FCVAR_CHEAT ); |
|
static ConVar r_eyegloss ( "r_eyegloss", "1", FCVAR_ARCHIVE ); // wet eyes |
|
static ConVar r_eyemove ( "r_eyemove", "1", FCVAR_ARCHIVE ); // look around |
|
static ConVar r_eyeshift_x ( "r_eyeshift_x", "0", FCVAR_ARCHIVE ); // eye X position |
|
static ConVar r_eyeshift_y ( "r_eyeshift_y", "0", FCVAR_ARCHIVE ); // eye Y position |
|
static ConVar r_eyeshift_z ( "r_eyeshift_z", "0", FCVAR_ARCHIVE ); // eye Z position |
|
static ConVar r_eyesize ( "r_eyesize", "0", FCVAR_ARCHIVE ); // adjustment to iris textures |
|
static ConVar mat_softwareskin( "mat_softwareskin", "0", FCVAR_CHEAT ); |
|
static ConVar r_nohw ( "r_nohw", "0", FCVAR_CHEAT ); |
|
static ConVar r_nosw ( "r_nosw", "0", FCVAR_CHEAT ); |
|
static ConVar r_teeth ( "r_teeth", "1" ); |
|
static ConVar r_drawentities ( "r_drawentities", "1", FCVAR_CHEAT ); |
|
static ConVar r_flex ( "r_flex", "1" ); |
|
static ConVar r_eyes ( "r_eyes", "1" ); |
|
static ConVar r_skin ( "r_skin","0", FCVAR_CHEAT ); |
|
static ConVar r_maxmodeldecal ( "r_maxmodeldecal", "50" ); |
|
static ConVar r_modelwireframedecal ( "r_modelwireframedecal", "0", FCVAR_CHEAT ); |
|
static ConVar mat_normals ( "mat_normals", "0", FCVAR_CHEAT ); |
|
static ConVar r_eyeglintlodpixels ( "r_eyeglintlodpixels", "0", FCVAR_CHEAT ); |
|
static ConVar r_rootlod ( "r_rootlod", "0" ); |
|
|
|
static StudioRenderConfig_t s_StudioRenderConfig; |
|
|
|
void CMDLPanel::UpdateStudioRenderConfig( void ) |
|
{ |
|
memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) ); |
|
|
|
s_StudioRenderConfig.bEyeMove = !!r_eyemove.GetInt(); |
|
s_StudioRenderConfig.fEyeShiftX = r_eyeshift_x.GetFloat(); |
|
s_StudioRenderConfig.fEyeShiftY = r_eyeshift_y.GetFloat(); |
|
s_StudioRenderConfig.fEyeShiftZ = r_eyeshift_z.GetFloat(); |
|
s_StudioRenderConfig.fEyeSize = r_eyesize.GetFloat(); |
|
if( mat_softwareskin.GetInt() || m_bWireFrame ) |
|
{ |
|
s_StudioRenderConfig.bSoftwareSkin = true; |
|
} |
|
else |
|
{ |
|
s_StudioRenderConfig.bSoftwareSkin = false; |
|
} |
|
s_StudioRenderConfig.bNoHardware = !!r_nohw.GetInt(); |
|
s_StudioRenderConfig.bNoSoftware = !!r_nosw.GetInt(); |
|
s_StudioRenderConfig.bTeeth = !!r_teeth.GetInt(); |
|
s_StudioRenderConfig.drawEntities = r_drawentities.GetInt(); |
|
s_StudioRenderConfig.bFlex = !!r_flex.GetInt(); |
|
s_StudioRenderConfig.bEyes = !!r_eyes.GetInt(); |
|
s_StudioRenderConfig.bWireframe = m_bWireFrame; |
|
s_StudioRenderConfig.bDrawNormals = mat_normals.GetBool(); |
|
s_StudioRenderConfig.skin = r_skin.GetInt(); |
|
s_StudioRenderConfig.maxDecalsPerModel = r_maxmodeldecal.GetInt(); |
|
s_StudioRenderConfig.bWireframeDecals = r_modelwireframedecal.GetInt() != 0; |
|
|
|
s_StudioRenderConfig.fullbright = false; |
|
s_StudioRenderConfig.bSoftwareLighting = false; |
|
|
|
s_StudioRenderConfig.bShowEnvCubemapOnly = r_showenvcubemap.GetInt() ? true : false; |
|
s_StudioRenderConfig.fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels.GetFloat(); |
|
|
|
StudioRender()->UpdateConfig( s_StudioRenderConfig ); |
|
} |
|
|
|
void CMDLPanel::DrawCollisionModel() |
|
{ |
|
vcollide_t *pCollide = MDLCache()->GetVCollide( m_RootMDL.m_MDL.GetMDL() ); |
|
|
|
if ( !pCollide || pCollide->solidCount <= 0 ) |
|
return; |
|
|
|
static color32 color = {255,0,0,0}; |
|
|
|
IVPhysicsKeyParser *pParser = g_pPhysicsCollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); |
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache ); |
|
|
|
matrix3x4_t pBoneToWorld[MAXSTUDIOBONES]; |
|
m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, MAXSTUDIOBONES, pBoneToWorld ); |
|
|
|
// PERFORMANCE: Just parse the script each frame. It's fast enough for tools. If you need |
|
// this to go faster then cache off the bone index mapping in an array like HLMV does |
|
while ( !pParser->Finished() ) |
|
{ |
|
const char *pBlock = pParser->GetCurrentBlockName(); |
|
if ( !stricmp( pBlock, "solid" ) ) |
|
{ |
|
solid_t solid; |
|
|
|
pParser->ParseSolid( &solid, NULL ); |
|
int boneIndex = Studio_BoneIndexByName( &studioHdr, solid.name ); |
|
Vector *outVerts; |
|
int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[solid.index], &outVerts ); |
|
|
|
if ( vertCount ) |
|
{ |
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); |
|
pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); |
|
// NOTE: assumes these have been set up already by the model render code |
|
// So this is a little bit of a back door to a cache of the bones |
|
// this code wouldn't work unless you draw the model this frame before calling |
|
// this routine. CMDLPanel always does this, but it's worth noting. |
|
// A better solution would be to move the ragdoll visulization into the CDmeMdl |
|
// and either draw it there or make it queryable and query/draw here. |
|
matrix3x4_t xform; |
|
SetIdentityMatrix( xform ); |
|
if ( boneIndex >= 0 ) |
|
{ |
|
MatrixCopy( pBoneToWorld[ boneIndex ], xform ); |
|
} |
|
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_Wireframe ); |
|
|
|
CMeshBuilder meshBuilder; |
|
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertCount/3 ); |
|
|
|
for ( int j = 0; j < vertCount; j++ ) |
|
{ |
|
Vector out; |
|
VectorTransform( outVerts[j].Base(), xform, out.Base() ); |
|
meshBuilder.Position3fv( out.Base() ); |
|
meshBuilder.Color4ub( color.r, color.g, color.b, color.a ); |
|
meshBuilder.TexCoord2f( 0, 0, 0 ); |
|
meshBuilder.AdvanceVertex(); |
|
} |
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
} |
|
|
|
g_pPhysicsCollision->DestroyDebugMesh( vertCount, outVerts ); |
|
} |
|
else |
|
{ |
|
pParser->SkipBlock(); |
|
} |
|
} |
|
g_pPhysicsCollision->VPhysicsKeyParserDestroy( pParser ); |
|
} |
|
|
|
void CMDLPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight ) |
|
{ |
|
int iWidth = nDisplayWidth; |
|
int iHeight = nDisplayHeight; |
|
if ( m_bThumbnailSafeZone ) |
|
{ |
|
iWidth = THUMBNAIL_SAFE_ZONE_SIZE; |
|
iHeight = THUMBNAIL_SAFE_ZONE_SIZE; |
|
} |
|
BaseClass::SetupRenderState( iWidth, iHeight ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// paint it! |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::OnPaint3D() |
|
{ |
|
if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) |
|
return; |
|
|
|
// FIXME: Move this call into DrawModel in StudioRender |
|
StudioRenderConfig_t oldStudioRenderConfig; |
|
StudioRender()->GetCurrentConfig( oldStudioRenderConfig ); |
|
|
|
UpdateStudioRenderConfig(); |
|
|
|
CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() ); |
|
if ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE ) |
|
{ |
|
ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( false ) : m_DefaultEnvCubemap; |
|
pRenderContext->BindLocalCubemap( pMyCube ); |
|
} |
|
else |
|
{ |
|
ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( true ) : m_DefaultHDREnvCubemap; |
|
pRenderContext->BindLocalCubemap( pMyCube ); |
|
} |
|
|
|
PrePaint3D( pRenderContext ); |
|
|
|
if ( m_bGroundGrid ) |
|
{ |
|
DrawGrid(); |
|
} |
|
|
|
if ( m_bLookAtCamera ) |
|
{ |
|
matrix3x4_t worldToCamera; |
|
ComputeCameraTransform( &worldToCamera ); |
|
|
|
Vector vecPosition; |
|
MatrixGetColumn( worldToCamera, 3, vecPosition ); |
|
m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = true; |
|
m_RootMDL.m_MDL.m_vecViewTarget = vecPosition; |
|
} |
|
|
|
// Draw the MDL |
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache ); |
|
|
|
SetupFlexWeights(); |
|
|
|
matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( studioHdr.numbones() ); |
|
m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, studioHdr.numbones(), pBoneToWorld, m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers ); |
|
g_pStudioRender->UnlockBoneMatrices(); |
|
|
|
IMaterial* pOverrideMaterial = GetOverrideMaterial( m_RootMDL.m_MDL.GetMDL() ); |
|
if ( pOverrideMaterial != NULL ) |
|
g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial ); |
|
|
|
m_RootMDL.m_MDL.Draw( m_RootMDL.m_MDLToWorld, pBoneToWorld ); |
|
|
|
if ( pOverrideMaterial != NULL ) |
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
|
|
pOverrideMaterial = NULL; |
|
|
|
// Draw the merge MDLs. |
|
matrix3x4_t matMergeBoneToWorld[MAXSTUDIOBONES]; |
|
int nMergeCount = m_aMergeMDLs.Count(); |
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) |
|
{ |
|
if ( m_aMergeMDLs[iMerge].m_bDisabled ) |
|
continue; |
|
|
|
// Get the merge studio header. |
|
studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( m_aMergeMDLs[iMerge].m_MDL.GetMDL() ); |
|
matrix3x4_t *pMergeBoneToWorld = &matMergeBoneToWorld[0]; |
|
|
|
// If we have a valid mesh, bonemerge it. If we have an invalid mesh we can't bonemerge because |
|
// it'll crash trying to pull data from the missing header. |
|
if ( pStudioHdr != NULL ) |
|
{ |
|
CStudioHdr mergeHdr( pStudioHdr, g_pMDLCache ); |
|
m_aMergeMDLs[iMerge].m_MDL.SetupBonesWithBoneMerge( &mergeHdr, pMergeBoneToWorld, &studioHdr, pBoneToWorld, m_RootMDL.m_MDLToWorld ); |
|
|
|
pOverrideMaterial = GetOverrideMaterial( m_aMergeMDLs[iMerge].m_MDL.GetMDL() ); |
|
if ( pOverrideMaterial != NULL ) |
|
g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial ); |
|
|
|
m_aMergeMDLs[iMerge].m_MDL.Draw( m_aMergeMDLs[iMerge].m_MDLToWorld, pMergeBoneToWorld ); |
|
|
|
if ( pOverrideMaterial != NULL ) |
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
|
|
// Notify of model render |
|
RenderingMergedModel( pRenderContext, &mergeHdr, m_aMergeMDLs[iMerge].m_MDL.GetMDL(), pMergeBoneToWorld ); |
|
} |
|
} |
|
|
|
RenderingRootModel( pRenderContext, &studioHdr, m_RootMDL.m_MDL.GetMDL(), pBoneToWorld ); |
|
|
|
PostPaint3D( pRenderContext ); |
|
|
|
if ( m_bDrawCollisionModel ) |
|
{ |
|
DrawCollisionModel(); |
|
} |
|
|
|
pRenderContext->Flush(); |
|
StudioRender()->UpdateConfig( oldStudioRenderConfig ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the current LOD |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetLOD( int nLOD ) |
|
{ |
|
m_RootMDL.m_MDL.m_nLOD = nLOD; |
|
|
|
int nMergeCount = m_aMergeMDLs.Count(); |
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) |
|
{ |
|
m_aMergeMDLs[iMerge].m_MDL.m_nLOD = nLOD; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the current sequence |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetSequence( int nSequence, bool bResetSequence ) |
|
{ |
|
m_RootMDL.m_MDL.m_nSequence = nSequence; |
|
|
|
if ( bResetSequence ) |
|
{ |
|
m_RootMDL.m_flCycleStartTime = GetAutoPlayTime(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Set the current pose parameters. If NULL the pose parameters will be reset |
|
// to the default values. |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::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 a pose parameter by name |
|
//----------------------------------------------------------------------------- |
|
bool CMDLPanel::SetPoseParameterByName( const char *pszName, float fValue ) |
|
{ |
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache ); |
|
int nPoseCount = studioHdr.GetNumPoseParameters(); |
|
|
|
for ( int i = 0; i < nPoseCount; ++i ) |
|
{ |
|
const mstudioposeparamdesc_t &Pose = studioHdr.pPoseParameter( i ); |
|
if ( V_strcasecmp( pszName, Pose.pszName() ) == 0 ) |
|
{ |
|
m_PoseParameters[ i ] = fValue; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Set the overlay sequence layers |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::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 ]; |
|
ResetAnimationEventState( &m_SequenceLayerEventState[ iLayer ] ); |
|
} |
|
} |
|
else |
|
{ |
|
m_nNumSequenceLayers = 0; |
|
V_memset( m_SequenceLayers, 0, sizeof( m_SequenceLayers ) ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Set the current skin |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetSkin( int nSkin ) |
|
{ |
|
m_RootMDL.m_MDL.m_nSkin = nSkin; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// called when we're ticked... |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::OnTick() |
|
{ |
|
BaseClass::OnTick(); |
|
if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID ) |
|
{ |
|
m_RootMDL.m_MDL.m_flTime = ( GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime ); |
|
|
|
DoAnimationEvents(); |
|
} |
|
} |
|
|
|
void CMDLPanel::Paint() |
|
{ |
|
BaseClass::Paint(); |
|
|
|
if ( m_bThumbnailSafeZone ) |
|
{ |
|
int iWidth, iHeight; |
|
GetSize( iWidth, iHeight ); |
|
|
|
CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() ); |
|
|
|
IMaterial *safezone = materials->FindMaterial( "vgui/thumbnails_safezone", TEXTURE_GROUP_VGUI, true ); |
|
if ( safezone ) |
|
{ |
|
safezone->IncrementReferenceCount(); |
|
} |
|
|
|
int screenposx = 0; |
|
int screenposy = 0; |
|
LocalToScreen( screenposx, screenposy ); |
|
|
|
int iScaledHeight = THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE * iHeight; |
|
int iRemappedHeight = RemapVal( iScaledHeight, 0, THUMBNAIL_SAFE_ZONE_HEIGHT, 0, iScaledHeight ); |
|
|
|
pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight, |
|
0, 0, |
|
THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT, |
|
THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE ); |
|
|
|
screenposx = 0; |
|
screenposy = iHeight - iRemappedHeight; |
|
LocalToScreen( screenposx, screenposy ); |
|
pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight, |
|
0, 0, |
|
THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT, |
|
THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE ); |
|
|
|
if ( safezone ) |
|
{ |
|
safezone->DecrementReferenceCount(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::DoAnimationEvents() |
|
{ |
|
CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache ); |
|
|
|
// If we don't have any sequences, don't do anything |
|
if ( studioHdr.GetNumSeq() < 1 ) |
|
{ |
|
Assert( studioHdr.GetNumSeq() >= 1 ); |
|
return; |
|
} |
|
|
|
DoAnimationEvents( &studioHdr, m_RootMDL.m_MDL.m_nSequence, m_RootMDL.m_MDL.m_flTime, false, &m_EventState ); |
|
|
|
for ( int i = 0; i < m_nNumSequenceLayers; ++i ) |
|
{ |
|
float flTime = m_RootMDL.m_MDL.m_flTime - m_SequenceLayers[ i ].m_flCycleBeganAt; |
|
//Plat_DebugString( CFmtStr("Animation: time = %f, started = %f, delta = %f\n",m_RootMDL.m_MDL.m_flTime,m_SequenceLayers[ i ].m_flCycleBeganAt,flTime ) ); |
|
DoAnimationEvents( &studioHdr, m_SequenceLayers[ i ].m_nSequenceIndex, flTime, m_SequenceLayers[ i ].m_bNoLoop, &m_SequenceLayerEventState[ i ] ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::DoAnimationEvents( CStudioHdr *pStudioHdr, int nSeqNum, float flTime, bool bNoLoop, MDLAnimEventState_t *pEventState ) |
|
{ |
|
if ( nSeqNum < 0 || nSeqNum >= pStudioHdr->GetNumSeq() ) |
|
{ |
|
return; |
|
} |
|
|
|
mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSeqNum ); |
|
if ( seqdesc.numevents == 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
mstudioevent_t *pevent = seqdesc.pEvent( 0 ); |
|
|
|
int nFrameCount = Studio_MaxFrame( pStudioHdr, nSeqNum, m_PoseParameters ); |
|
if ( nFrameCount == 0 ) |
|
{ |
|
nFrameCount = 1; |
|
} |
|
float flEventCycle = ( flTime * m_RootMDL.m_MDL.m_flPlaybackRate ) / nFrameCount; |
|
//Plat_DebugString( CFmtStr("Event cycle: %f, playback rate: %f, frame count: %d\n", flEventCycle, m_RootMDL.m_MDL.m_flPlaybackRate, nFrameCount ) ); |
|
if ( bNoLoop ) |
|
{ |
|
flEventCycle = MIN(flEventCycle, 1.0f); |
|
} |
|
else |
|
{ |
|
flEventCycle -= (int)(flEventCycle); |
|
} |
|
|
|
if ( pEventState->m_nEventSequence != nSeqNum ) |
|
{ |
|
pEventState->m_nEventSequence = nSeqNum; |
|
flEventCycle = 0.0f; |
|
pEventState->m_flPrevEventCycle = -0.01f; // back up to get 0'th frame animations |
|
} |
|
|
|
if ( flEventCycle == pEventState->m_flPrevEventCycle ) |
|
{ |
|
return; |
|
} |
|
|
|
// check for looping |
|
BOOL bLooped = (flEventCycle < pEventState->m_flPrevEventCycle); |
|
|
|
// This makes sure events that occur at the end of a sequence occur are |
|
// sent before events that occur at the beginning of a sequence. |
|
if (bLooped) |
|
{ |
|
for (int i = 0; i < (int)seqdesc.numevents; i++) |
|
{ |
|
if ( pevent[i].cycle <= pEventState->m_flPrevEventCycle ) |
|
continue; |
|
|
|
FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() ); |
|
} |
|
|
|
// Necessary to get the next loop working |
|
pEventState->m_flPrevEventCycle = -0.01f; |
|
} |
|
|
|
for (int i = 0; i < (int)seqdesc.numevents; i++) |
|
{ |
|
if ( (pevent[i].cycle > pEventState->m_flPrevEventCycle && pevent[i].cycle <= flEventCycle) ) |
|
{ |
|
FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() ); |
|
} |
|
} |
|
|
|
pEventState->m_flPrevEventCycle = flEventCycle; |
|
} |
|
|
|
void CMDLPanel::FireEvent( const char *pszEventName, const char *pszEventOptions ) |
|
{ |
|
KeyValues* pKVEvent = new KeyValues( "AnimEvent" ); |
|
pKVEvent->SetString( "name", pszEventName ); |
|
pKVEvent->SetString( "options", pszEventOptions ); |
|
PostActionSignal( pKVEvent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::ResetAnimationEventState( MDLAnimEventState_t *pEventState ) |
|
{ |
|
pEventState->m_nEventSequence = -1; |
|
pEventState->m_flPrevEventCycle = -0.01f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// input |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::OnMouseDoublePressed( vgui::MouseCode code ) |
|
{ |
|
if ( m_bIgnoreDoubleClick ) |
|
return; |
|
|
|
float flRadius; |
|
Vector vecCenter; |
|
GetBoundingSphere( vecCenter, flRadius ); |
|
LookAt( vecCenter, flRadius ); |
|
|
|
BaseClass::OnMouseDoublePressed( code ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMDLPanel::SetMergeMDL( MDLHandle_t handle, void *pProxyData, int nSkin /*= -1 */ ) |
|
{ |
|
// 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 ); |
|
|
|
if ( nSkin != -1 ) |
|
{ |
|
m_aMergeMDLs[iIndex].m_MDL.m_nSkin = nSkin; |
|
} |
|
|
|
m_aMergeMDLs[iIndex].m_MDL.m_nLOD = m_RootMDL.m_MDL.m_nLOD; |
|
|
|
m_aMergeMDLs[iIndex].m_MDL.m_pProxyData = pProxyData; |
|
SetIdentityMatrix( m_aMergeMDLs[iIndex].m_MDLToWorld ); |
|
|
|
m_aMergeMDLs[iIndex].m_bDisabled = false; |
|
|
|
// Need to invalidate the layout so the panel will adjust is LookAt for the new model. |
|
InvalidateLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
MDLHandle_t CMDLPanel::SetMergeMDL( const char *pMDLName, void *pProxyData, int nSkin /*= -1 */ ) |
|
{ |
|
MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName ); |
|
MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID; |
|
if ( vgui::MDLCache()->IsErrorModel( hMDL ) ) |
|
{ |
|
hMDL = MDLHANDLE_INVALID; |
|
} |
|
|
|
SetMergeMDL( hMDL, pProxyData, nSkin ); |
|
|
|
// FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference. |
|
int nRef = vgui::MDLCache()->Release( hMDLFindResult ); |
|
(void)nRef; // Avoid unreferenced variable warning |
|
AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMergeMDL referenced a model that has a zero ref count." ); |
|
|
|
return hMDL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CMDLPanel::GetMergeMDLIndex( void *pProxyData ) |
|
{ |
|
int nMergeCount = m_aMergeMDLs.Count(); |
|
for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) |
|
{ |
|
if ( m_aMergeMDLs[iMerge].m_MDL.m_pProxyData == pProxyData ) |
|
return iMerge; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CMDLPanel::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 *CMDLPanel::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 CMDLPanel::ClearMergeMDLs( void ) |
|
{ |
|
m_aMergeMDLs.Purge(); |
|
}
|
|
|