summaryrefslogtreecommitdiff
path: root/vgui2/matsys_controls/mdlpanel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vgui2/matsys_controls/mdlpanel.cpp')
-rw-r--r--vgui2/matsys_controls/mdlpanel.cpp902
1 files changed, 902 insertions, 0 deletions
diff --git a/vgui2/matsys_controls/mdlpanel.cpp b/vgui2/matsys_controls/mdlpanel.cpp
new file mode 100644
index 0000000..b99301d
--- /dev/null
+++ b/vgui2/matsys_controls/mdlpanel.cpp
@@ -0,0 +1,902 @@
+//========= 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();
+}