Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "matsys_controls/mdlpanel.h"
  7. #include "materialsystem/imaterialsystem.h"
  8. #include "materialsystem/imaterialsystemhardwareconfig.h"
  9. #include "materialsystem/imesh.h"
  10. #include "vgui/IVGui.h"
  11. #include "tier1/KeyValues.h"
  12. #include "vgui_controls/Frame.h"
  13. #include "tier1/convar.h"
  14. #include "tier0/dbg.h"
  15. #include "tier1/fmtstr.h"
  16. #include "istudiorender.h"
  17. #include "matsys_controls/matsyscontrols.h"
  18. #include "vcollide.h"
  19. #include "vcollide_parse.h"
  20. #include "bone_setup.h"
  21. #include "vphysics_interface.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. using namespace vgui;
  25. DECLARE_BUILD_FACTORY( CMDLPanel );
  26. static const int THUMBNAIL_SAFE_ZONE_SIZE = 512;
  27. static const int THUMBNAIL_SAFE_ZONE_HEIGHT = 92;
  28. static const float THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE = (float)THUMBNAIL_SAFE_ZONE_HEIGHT / THUMBNAIL_SAFE_ZONE_SIZE;
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Keeps a global clock to autoplay sequences to run from
  31. // Also deals with speedScale changes
  32. //-----------------------------------------------------------------------------
  33. float GetAutoPlayTime( void )
  34. {
  35. static int g_prevTicks;
  36. static float g_time;
  37. int ticks = Plat_MSTime();
  38. // limit delta so that float time doesn't overflow
  39. if (g_prevTicks == 0)
  40. {
  41. g_prevTicks = ticks;
  42. }
  43. g_time += ( ticks - g_prevTicks ) / 1000.0f;
  44. g_prevTicks = ticks;
  45. return g_time;
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Constructor, destructor
  49. //-----------------------------------------------------------------------------
  50. CMDLPanel::CMDLPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
  51. {
  52. SetVisible( true );
  53. // Used to poll input
  54. vgui::ivgui()->AddTickSignal( GetVPanel() );
  55. // Deal with the default cubemap
  56. ITexture *pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap", NULL, true );
  57. m_DefaultEnvCubemap.Init( pCubemapTexture );
  58. pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap.hdr", NULL, true );
  59. m_DefaultHDREnvCubemap.Init( pCubemapTexture );
  60. SetIdentityMatrix( m_RootMDL.m_MDLToWorld );
  61. m_bDrawCollisionModel = false;
  62. m_bWireFrame = false;
  63. m_bGroundGrid = false;
  64. m_bLockView = false;
  65. m_bLookAtCamera = true;
  66. m_bThumbnailSafeZone = false;
  67. m_nNumSequenceLayers = 0;
  68. ResetAnimationEventState( &m_EventState );
  69. }
  70. CMDLPanel::~CMDLPanel()
  71. {
  72. m_aMergeMDLs.Purge();
  73. m_DefaultEnvCubemap.Shutdown( );
  74. m_DefaultHDREnvCubemap.Shutdown();
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Scheme settings
  78. //-----------------------------------------------------------------------------
  79. void CMDLPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  80. {
  81. BaseClass::ApplySchemeSettings( pScheme );
  82. SetBackgroundColor( GetBgColor() );
  83. SetBorder( pScheme->GetBorder( "MenuBorder") );
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Rendering options
  87. //-----------------------------------------------------------------------------
  88. void CMDLPanel::SetCollsionModel( bool bVisible )
  89. {
  90. m_bDrawCollisionModel = bVisible;
  91. }
  92. void CMDLPanel::SetGroundGrid( bool bVisible )
  93. {
  94. m_bGroundGrid = bVisible;
  95. }
  96. void CMDLPanel::SetWireFrame( bool bVisible )
  97. {
  98. m_bWireFrame = bVisible;
  99. }
  100. void CMDLPanel::SetLockView( bool bLocked )
  101. {
  102. m_bLockView = bLocked;
  103. }
  104. void CMDLPanel::SetLookAtCamera( bool bLookAtCamera )
  105. {
  106. m_bLookAtCamera = bLookAtCamera;
  107. }
  108. void CMDLPanel::SetIgnoreDoubleClick( bool bState )
  109. {
  110. m_bIgnoreDoubleClick = bState;
  111. }
  112. void CMDLPanel::SetThumbnailSafeZone( bool bVisible )
  113. {
  114. m_bThumbnailSafeZone = bVisible;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Stores the clip
  118. //-----------------------------------------------------------------------------
  119. void CMDLPanel::SetMDL( MDLHandle_t handle, void *pProxyData )
  120. {
  121. m_RootMDL.m_MDL.SetMDL( handle );
  122. m_RootMDL.m_MDL.m_pProxyData = pProxyData;
  123. Vector vecMins, vecMaxs;
  124. GetMDLBoundingBox( &vecMins, &vecMaxs, handle, m_RootMDL.m_MDL.m_nSequence );
  125. m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = false;
  126. m_RootMDL.m_MDL.m_vecViewTarget.Init( 100.0f, 0.0f, vecMaxs.z );
  127. m_RootMDL.m_flCycleStartTime = 0.f;
  128. // Set the pose parameters to the default for the mdl
  129. SetPoseParameters( NULL, 0 );
  130. // Clear any sequence layers
  131. SetSequenceLayers( NULL, 0 );
  132. ResetAnimationEventState( &m_EventState );
  133. }
  134. //-----------------------------------------------------------------------------
  135. // An MDL was selected
  136. //-----------------------------------------------------------------------------
  137. void CMDLPanel::SetMDL( const char *pMDLName, void *pProxyData )
  138. {
  139. MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName );
  140. MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID;
  141. if ( vgui::MDLCache()->IsErrorModel( hMDL ) )
  142. {
  143. hMDL = MDLHANDLE_INVALID;
  144. }
  145. SetMDL( hMDL, pProxyData );
  146. // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference.
  147. int nRef = vgui::MDLCache()->Release( hMDLFindResult );
  148. (void)nRef; // Avoid unreferenced variable warning
  149. AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMDL referenced a model that has a zero ref count." );
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose: Returns a model bounding box.
  153. //-----------------------------------------------------------------------------
  154. bool CMDLPanel::GetBoundingBox( Vector &vecBoundsMin, Vector &vecBoundsMax )
  155. {
  156. // Check to see if we have a valid model to look at.
  157. if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
  158. return false;
  159. GetMDLBoundingBox( &vecBoundsMin, &vecBoundsMax, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
  160. return true;
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose: Returns a more accurate bounding sphere
  164. //-----------------------------------------------------------------------------
  165. bool CMDLPanel::GetBoundingSphere( Vector &vecCenter, float &flRadius )
  166. {
  167. // Check to see if we have a valid model to look at.
  168. if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
  169. return false;
  170. Vector vecEngineCenter;
  171. GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence );
  172. VectorTransform( vecEngineCenter, m_RootMDL.m_MDLToWorld, vecCenter );
  173. return true;
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose:
  177. //-----------------------------------------------------------------------------
  178. void CMDLPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos )
  179. {
  180. SetIdentityMatrix( m_RootMDL.m_MDLToWorld );
  181. AngleMatrix( angRot, vecPos, m_RootMDL.m_MDLToWorld );
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Sets the camera to look at the model
  185. //-----------------------------------------------------------------------------
  186. void CMDLPanel::LookAtMDL()
  187. {
  188. // Check to see if we have a valid model to look at.
  189. if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
  190. return;
  191. if ( m_bLockView )
  192. return;
  193. float flRadius;
  194. Vector vecCenter;
  195. GetBoundingSphere( vecCenter, flRadius );
  196. LookAt( vecCenter, flRadius );
  197. }
  198. //-----------------------------------------------------------------------------
  199. // FIXME: This should be moved into studiorender
  200. //-----------------------------------------------------------------------------
  201. static ConVar r_showenvcubemap( "r_showenvcubemap", "0", FCVAR_CHEAT );
  202. static ConVar r_eyegloss ( "r_eyegloss", "1", FCVAR_ARCHIVE ); // wet eyes
  203. static ConVar r_eyemove ( "r_eyemove", "1", FCVAR_ARCHIVE ); // look around
  204. static ConVar r_eyeshift_x ( "r_eyeshift_x", "0", FCVAR_ARCHIVE ); // eye X position
  205. static ConVar r_eyeshift_y ( "r_eyeshift_y", "0", FCVAR_ARCHIVE ); // eye Y position
  206. static ConVar r_eyeshift_z ( "r_eyeshift_z", "0", FCVAR_ARCHIVE ); // eye Z position
  207. static ConVar r_eyesize ( "r_eyesize", "0", FCVAR_ARCHIVE ); // adjustment to iris textures
  208. static ConVar mat_softwareskin( "mat_softwareskin", "0", FCVAR_CHEAT );
  209. static ConVar r_nohw ( "r_nohw", "0", FCVAR_CHEAT );
  210. static ConVar r_nosw ( "r_nosw", "0", FCVAR_CHEAT );
  211. static ConVar r_teeth ( "r_teeth", "1" );
  212. static ConVar r_drawentities ( "r_drawentities", "1", FCVAR_CHEAT );
  213. static ConVar r_flex ( "r_flex", "1" );
  214. static ConVar r_eyes ( "r_eyes", "1" );
  215. static ConVar r_skin ( "r_skin","0", FCVAR_CHEAT );
  216. static ConVar r_maxmodeldecal ( "r_maxmodeldecal", "50" );
  217. static ConVar r_modelwireframedecal ( "r_modelwireframedecal", "0", FCVAR_CHEAT );
  218. static ConVar mat_normals ( "mat_normals", "0", FCVAR_CHEAT );
  219. static ConVar r_eyeglintlodpixels ( "r_eyeglintlodpixels", "0", FCVAR_CHEAT );
  220. static ConVar r_rootlod ( "r_rootlod", "0" );
  221. static StudioRenderConfig_t s_StudioRenderConfig;
  222. void CMDLPanel::UpdateStudioRenderConfig( void )
  223. {
  224. memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) );
  225. s_StudioRenderConfig.bEyeMove = !!r_eyemove.GetInt();
  226. s_StudioRenderConfig.fEyeShiftX = r_eyeshift_x.GetFloat();
  227. s_StudioRenderConfig.fEyeShiftY = r_eyeshift_y.GetFloat();
  228. s_StudioRenderConfig.fEyeShiftZ = r_eyeshift_z.GetFloat();
  229. s_StudioRenderConfig.fEyeSize = r_eyesize.GetFloat();
  230. if( mat_softwareskin.GetInt() || m_bWireFrame )
  231. {
  232. s_StudioRenderConfig.bSoftwareSkin = true;
  233. }
  234. else
  235. {
  236. s_StudioRenderConfig.bSoftwareSkin = false;
  237. }
  238. s_StudioRenderConfig.bNoHardware = !!r_nohw.GetInt();
  239. s_StudioRenderConfig.bNoSoftware = !!r_nosw.GetInt();
  240. s_StudioRenderConfig.bTeeth = !!r_teeth.GetInt();
  241. s_StudioRenderConfig.drawEntities = r_drawentities.GetInt();
  242. s_StudioRenderConfig.bFlex = !!r_flex.GetInt();
  243. s_StudioRenderConfig.bEyes = !!r_eyes.GetInt();
  244. s_StudioRenderConfig.bWireframe = m_bWireFrame;
  245. s_StudioRenderConfig.bDrawNormals = mat_normals.GetBool();
  246. s_StudioRenderConfig.skin = r_skin.GetInt();
  247. s_StudioRenderConfig.maxDecalsPerModel = r_maxmodeldecal.GetInt();
  248. s_StudioRenderConfig.bWireframeDecals = r_modelwireframedecal.GetInt() != 0;
  249. s_StudioRenderConfig.fullbright = false;
  250. s_StudioRenderConfig.bSoftwareLighting = false;
  251. s_StudioRenderConfig.bShowEnvCubemapOnly = r_showenvcubemap.GetInt() ? true : false;
  252. s_StudioRenderConfig.fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels.GetFloat();
  253. StudioRender()->UpdateConfig( s_StudioRenderConfig );
  254. }
  255. void CMDLPanel::DrawCollisionModel()
  256. {
  257. vcollide_t *pCollide = MDLCache()->GetVCollide( m_RootMDL.m_MDL.GetMDL() );
  258. if ( !pCollide || pCollide->solidCount <= 0 )
  259. return;
  260. static color32 color = {255,0,0,0};
  261. IVPhysicsKeyParser *pParser = g_pPhysicsCollision->VPhysicsKeyParserCreate( pCollide->pKeyValues );
  262. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
  263. matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
  264. m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, MAXSTUDIOBONES, pBoneToWorld );
  265. // PERFORMANCE: Just parse the script each frame. It's fast enough for tools. If you need
  266. // this to go faster then cache off the bone index mapping in an array like HLMV does
  267. while ( !pParser->Finished() )
  268. {
  269. const char *pBlock = pParser->GetCurrentBlockName();
  270. if ( !stricmp( pBlock, "solid" ) )
  271. {
  272. solid_t solid;
  273. pParser->ParseSolid( &solid, NULL );
  274. int boneIndex = Studio_BoneIndexByName( &studioHdr, solid.name );
  275. Vector *outVerts;
  276. int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[solid.index], &outVerts );
  277. if ( vertCount )
  278. {
  279. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  280. pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
  281. // NOTE: assumes these have been set up already by the model render code
  282. // So this is a little bit of a back door to a cache of the bones
  283. // this code wouldn't work unless you draw the model this frame before calling
  284. // this routine. CMDLPanel always does this, but it's worth noting.
  285. // A better solution would be to move the ragdoll visulization into the CDmeMdl
  286. // and either draw it there or make it queryable and query/draw here.
  287. matrix3x4_t xform;
  288. SetIdentityMatrix( xform );
  289. if ( boneIndex >= 0 )
  290. {
  291. MatrixCopy( pBoneToWorld[ boneIndex ], xform );
  292. }
  293. IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_Wireframe );
  294. CMeshBuilder meshBuilder;
  295. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertCount/3 );
  296. for ( int j = 0; j < vertCount; j++ )
  297. {
  298. Vector out;
  299. VectorTransform( outVerts[j].Base(), xform, out.Base() );
  300. meshBuilder.Position3fv( out.Base() );
  301. meshBuilder.Color4ub( color.r, color.g, color.b, color.a );
  302. meshBuilder.TexCoord2f( 0, 0, 0 );
  303. meshBuilder.AdvanceVertex();
  304. }
  305. meshBuilder.End();
  306. pMesh->Draw();
  307. }
  308. g_pPhysicsCollision->DestroyDebugMesh( vertCount, outVerts );
  309. }
  310. else
  311. {
  312. pParser->SkipBlock();
  313. }
  314. }
  315. g_pPhysicsCollision->VPhysicsKeyParserDestroy( pParser );
  316. }
  317. void CMDLPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight )
  318. {
  319. int iWidth = nDisplayWidth;
  320. int iHeight = nDisplayHeight;
  321. if ( m_bThumbnailSafeZone )
  322. {
  323. iWidth = THUMBNAIL_SAFE_ZONE_SIZE;
  324. iHeight = THUMBNAIL_SAFE_ZONE_SIZE;
  325. }
  326. BaseClass::SetupRenderState( iWidth, iHeight );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // paint it!
  330. //-----------------------------------------------------------------------------
  331. void CMDLPanel::OnPaint3D()
  332. {
  333. if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
  334. return;
  335. // FIXME: Move this call into DrawModel in StudioRender
  336. StudioRenderConfig_t oldStudioRenderConfig;
  337. StudioRender()->GetCurrentConfig( oldStudioRenderConfig );
  338. UpdateStudioRenderConfig();
  339. CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
  340. if ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE )
  341. {
  342. ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( false ) : m_DefaultEnvCubemap;
  343. pRenderContext->BindLocalCubemap( pMyCube );
  344. }
  345. else
  346. {
  347. ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( true ) : m_DefaultHDREnvCubemap;
  348. pRenderContext->BindLocalCubemap( pMyCube );
  349. }
  350. PrePaint3D( pRenderContext );
  351. if ( m_bGroundGrid )
  352. {
  353. DrawGrid();
  354. }
  355. if ( m_bLookAtCamera )
  356. {
  357. matrix3x4_t worldToCamera;
  358. ComputeCameraTransform( &worldToCamera );
  359. Vector vecPosition;
  360. MatrixGetColumn( worldToCamera, 3, vecPosition );
  361. m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = true;
  362. m_RootMDL.m_MDL.m_vecViewTarget = vecPosition;
  363. }
  364. // Draw the MDL
  365. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
  366. SetupFlexWeights();
  367. matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( studioHdr.numbones() );
  368. m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, studioHdr.numbones(), pBoneToWorld, m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers );
  369. g_pStudioRender->UnlockBoneMatrices();
  370. IMaterial* pOverrideMaterial = GetOverrideMaterial( m_RootMDL.m_MDL.GetMDL() );
  371. if ( pOverrideMaterial != NULL )
  372. g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial );
  373. m_RootMDL.m_MDL.Draw( m_RootMDL.m_MDLToWorld, pBoneToWorld );
  374. if ( pOverrideMaterial != NULL )
  375. g_pStudioRender->ForcedMaterialOverride( NULL );
  376. pOverrideMaterial = NULL;
  377. // Draw the merge MDLs.
  378. matrix3x4_t matMergeBoneToWorld[MAXSTUDIOBONES];
  379. int nMergeCount = m_aMergeMDLs.Count();
  380. for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
  381. {
  382. if ( m_aMergeMDLs[iMerge].m_bDisabled )
  383. continue;
  384. // Get the merge studio header.
  385. studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( m_aMergeMDLs[iMerge].m_MDL.GetMDL() );
  386. matrix3x4_t *pMergeBoneToWorld = &matMergeBoneToWorld[0];
  387. // If we have a valid mesh, bonemerge it. If we have an invalid mesh we can't bonemerge because
  388. // it'll crash trying to pull data from the missing header.
  389. if ( pStudioHdr != NULL )
  390. {
  391. CStudioHdr mergeHdr( pStudioHdr, g_pMDLCache );
  392. m_aMergeMDLs[iMerge].m_MDL.SetupBonesWithBoneMerge( &mergeHdr, pMergeBoneToWorld, &studioHdr, pBoneToWorld, m_RootMDL.m_MDLToWorld );
  393. pOverrideMaterial = GetOverrideMaterial( m_aMergeMDLs[iMerge].m_MDL.GetMDL() );
  394. if ( pOverrideMaterial != NULL )
  395. g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial );
  396. m_aMergeMDLs[iMerge].m_MDL.Draw( m_aMergeMDLs[iMerge].m_MDLToWorld, pMergeBoneToWorld );
  397. if ( pOverrideMaterial != NULL )
  398. g_pStudioRender->ForcedMaterialOverride( NULL );
  399. // Notify of model render
  400. RenderingMergedModel( pRenderContext, &mergeHdr, m_aMergeMDLs[iMerge].m_MDL.GetMDL(), pMergeBoneToWorld );
  401. }
  402. }
  403. RenderingRootModel( pRenderContext, &studioHdr, m_RootMDL.m_MDL.GetMDL(), pBoneToWorld );
  404. PostPaint3D( pRenderContext );
  405. if ( m_bDrawCollisionModel )
  406. {
  407. DrawCollisionModel();
  408. }
  409. pRenderContext->Flush();
  410. StudioRender()->UpdateConfig( oldStudioRenderConfig );
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Sets the current LOD
  414. //-----------------------------------------------------------------------------
  415. void CMDLPanel::SetLOD( int nLOD )
  416. {
  417. m_RootMDL.m_MDL.m_nLOD = nLOD;
  418. int nMergeCount = m_aMergeMDLs.Count();
  419. for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
  420. {
  421. m_aMergeMDLs[iMerge].m_MDL.m_nLOD = nLOD;
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Sets the current sequence
  426. //-----------------------------------------------------------------------------
  427. void CMDLPanel::SetSequence( int nSequence, bool bResetSequence )
  428. {
  429. m_RootMDL.m_MDL.m_nSequence = nSequence;
  430. if ( bResetSequence )
  431. {
  432. m_RootMDL.m_flCycleStartTime = GetAutoPlayTime();
  433. }
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Set the current pose parameters. If NULL the pose parameters will be reset
  437. // to the default values.
  438. //-----------------------------------------------------------------------------
  439. void CMDLPanel::SetPoseParameters( const float *pPoseParameters, int nCount )
  440. {
  441. if ( pPoseParameters )
  442. {
  443. int nParameters = MIN( MAXSTUDIOPOSEPARAM, nCount );
  444. for ( int iParam = 0; iParam < nParameters; ++iParam )
  445. {
  446. m_PoseParameters[ iParam ] = pPoseParameters[ iParam ];
  447. }
  448. }
  449. else if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID )
  450. {
  451. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
  452. Studio_CalcDefaultPoseParameters( &studioHdr, m_PoseParameters, MAXSTUDIOPOSEPARAM );
  453. }
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Set a pose parameter by name
  457. //-----------------------------------------------------------------------------
  458. bool CMDLPanel::SetPoseParameterByName( const char *pszName, float fValue )
  459. {
  460. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
  461. int nPoseCount = studioHdr.GetNumPoseParameters();
  462. for ( int i = 0; i < nPoseCount; ++i )
  463. {
  464. const mstudioposeparamdesc_t &Pose = studioHdr.pPoseParameter( i );
  465. if ( V_strcasecmp( pszName, Pose.pszName() ) == 0 )
  466. {
  467. m_PoseParameters[ i ] = fValue;
  468. return true;
  469. }
  470. }
  471. return false;
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Set the overlay sequence layers
  475. //-----------------------------------------------------------------------------
  476. void CMDLPanel::SetSequenceLayers( const MDLSquenceLayer_t *pSequenceLayers, int nCount )
  477. {
  478. if ( pSequenceLayers )
  479. {
  480. m_nNumSequenceLayers = MIN( MAX_SEQUENCE_LAYERS, nCount );
  481. for ( int iLayer = 0; iLayer < m_nNumSequenceLayers; ++iLayer )
  482. {
  483. m_SequenceLayers[ iLayer ] = pSequenceLayers[ iLayer ];
  484. ResetAnimationEventState( &m_SequenceLayerEventState[ iLayer ] );
  485. }
  486. }
  487. else
  488. {
  489. m_nNumSequenceLayers = 0;
  490. V_memset( m_SequenceLayers, 0, sizeof( m_SequenceLayers ) );
  491. }
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Set the current skin
  495. //-----------------------------------------------------------------------------
  496. void CMDLPanel::SetSkin( int nSkin )
  497. {
  498. m_RootMDL.m_MDL.m_nSkin = nSkin;
  499. }
  500. //-----------------------------------------------------------------------------
  501. // called when we're ticked...
  502. //-----------------------------------------------------------------------------
  503. void CMDLPanel::OnTick()
  504. {
  505. BaseClass::OnTick();
  506. if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID )
  507. {
  508. m_RootMDL.m_MDL.m_flTime = ( GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime );
  509. DoAnimationEvents();
  510. }
  511. }
  512. void CMDLPanel::Paint()
  513. {
  514. BaseClass::Paint();
  515. if ( m_bThumbnailSafeZone )
  516. {
  517. int iWidth, iHeight;
  518. GetSize( iWidth, iHeight );
  519. CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() );
  520. IMaterial *safezone = materials->FindMaterial( "vgui/thumbnails_safezone", TEXTURE_GROUP_VGUI, true );
  521. if ( safezone )
  522. {
  523. safezone->IncrementReferenceCount();
  524. }
  525. int screenposx = 0;
  526. int screenposy = 0;
  527. LocalToScreen( screenposx, screenposy );
  528. int iScaledHeight = THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE * iHeight;
  529. int iRemappedHeight = RemapVal( iScaledHeight, 0, THUMBNAIL_SAFE_ZONE_HEIGHT, 0, iScaledHeight );
  530. pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight,
  531. 0, 0,
  532. THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT,
  533. THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE );
  534. screenposx = 0;
  535. screenposy = iHeight - iRemappedHeight;
  536. LocalToScreen( screenposx, screenposy );
  537. pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight,
  538. 0, 0,
  539. THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT,
  540. THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE );
  541. if ( safezone )
  542. {
  543. safezone->DecrementReferenceCount();
  544. }
  545. }
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Purpose:
  549. //-----------------------------------------------------------------------------
  550. void CMDLPanel::DoAnimationEvents()
  551. {
  552. CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_RootMDL.m_MDL.GetMDL() ), g_pMDLCache );
  553. // If we don't have any sequences, don't do anything
  554. if ( studioHdr.GetNumSeq() < 1 )
  555. {
  556. Assert( studioHdr.GetNumSeq() >= 1 );
  557. return;
  558. }
  559. DoAnimationEvents( &studioHdr, m_RootMDL.m_MDL.m_nSequence, m_RootMDL.m_MDL.m_flTime, false, &m_EventState );
  560. for ( int i = 0; i < m_nNumSequenceLayers; ++i )
  561. {
  562. float flTime = m_RootMDL.m_MDL.m_flTime - m_SequenceLayers[ i ].m_flCycleBeganAt;
  563. //Plat_DebugString( CFmtStr("Animation: time = %f, started = %f, delta = %f\n",m_RootMDL.m_MDL.m_flTime,m_SequenceLayers[ i ].m_flCycleBeganAt,flTime ) );
  564. DoAnimationEvents( &studioHdr, m_SequenceLayers[ i ].m_nSequenceIndex, flTime, m_SequenceLayers[ i ].m_bNoLoop, &m_SequenceLayerEventState[ i ] );
  565. }
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose:
  569. //-----------------------------------------------------------------------------
  570. void CMDLPanel::DoAnimationEvents( CStudioHdr *pStudioHdr, int nSeqNum, float flTime, bool bNoLoop, MDLAnimEventState_t *pEventState )
  571. {
  572. if ( nSeqNum < 0 || nSeqNum >= pStudioHdr->GetNumSeq() )
  573. {
  574. return;
  575. }
  576. mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSeqNum );
  577. if ( seqdesc.numevents == 0 )
  578. {
  579. return;
  580. }
  581. mstudioevent_t *pevent = seqdesc.pEvent( 0 );
  582. int nFrameCount = Studio_MaxFrame( pStudioHdr, nSeqNum, m_PoseParameters );
  583. if ( nFrameCount == 0 )
  584. {
  585. nFrameCount = 1;
  586. }
  587. float flEventCycle = ( flTime * m_RootMDL.m_MDL.m_flPlaybackRate ) / nFrameCount;
  588. //Plat_DebugString( CFmtStr("Event cycle: %f, playback rate: %f, frame count: %d\n", flEventCycle, m_RootMDL.m_MDL.m_flPlaybackRate, nFrameCount ) );
  589. if ( bNoLoop )
  590. {
  591. flEventCycle = MIN(flEventCycle, 1.0f);
  592. }
  593. else
  594. {
  595. flEventCycle -= (int)(flEventCycle);
  596. }
  597. if ( pEventState->m_nEventSequence != nSeqNum )
  598. {
  599. pEventState->m_nEventSequence = nSeqNum;
  600. flEventCycle = 0.0f;
  601. pEventState->m_flPrevEventCycle = -0.01f; // back up to get 0'th frame animations
  602. }
  603. if ( flEventCycle == pEventState->m_flPrevEventCycle )
  604. {
  605. return;
  606. }
  607. // check for looping
  608. BOOL bLooped = (flEventCycle < pEventState->m_flPrevEventCycle);
  609. // This makes sure events that occur at the end of a sequence occur are
  610. // sent before events that occur at the beginning of a sequence.
  611. if (bLooped)
  612. {
  613. for (int i = 0; i < (int)seqdesc.numevents; i++)
  614. {
  615. if ( pevent[i].cycle <= pEventState->m_flPrevEventCycle )
  616. continue;
  617. FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() );
  618. }
  619. // Necessary to get the next loop working
  620. pEventState->m_flPrevEventCycle = -0.01f;
  621. }
  622. for (int i = 0; i < (int)seqdesc.numevents; i++)
  623. {
  624. if ( (pevent[i].cycle > pEventState->m_flPrevEventCycle && pevent[i].cycle <= flEventCycle) )
  625. {
  626. FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() );
  627. }
  628. }
  629. pEventState->m_flPrevEventCycle = flEventCycle;
  630. }
  631. void CMDLPanel::FireEvent( const char *pszEventName, const char *pszEventOptions )
  632. {
  633. KeyValues* pKVEvent = new KeyValues( "AnimEvent" );
  634. pKVEvent->SetString( "name", pszEventName );
  635. pKVEvent->SetString( "options", pszEventOptions );
  636. PostActionSignal( pKVEvent );
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Purpose:
  640. //-----------------------------------------------------------------------------
  641. void CMDLPanel::ResetAnimationEventState( MDLAnimEventState_t *pEventState )
  642. {
  643. pEventState->m_nEventSequence = -1;
  644. pEventState->m_flPrevEventCycle = -0.01f;
  645. }
  646. //-----------------------------------------------------------------------------
  647. // input
  648. //-----------------------------------------------------------------------------
  649. void CMDLPanel::OnMouseDoublePressed( vgui::MouseCode code )
  650. {
  651. if ( m_bIgnoreDoubleClick )
  652. return;
  653. float flRadius;
  654. Vector vecCenter;
  655. GetBoundingSphere( vecCenter, flRadius );
  656. LookAt( vecCenter, flRadius );
  657. BaseClass::OnMouseDoublePressed( code );
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. //-----------------------------------------------------------------------------
  662. void CMDLPanel::SetMergeMDL( MDLHandle_t handle, void *pProxyData, int nSkin /*= -1 */ )
  663. {
  664. // Verify that we have a root model to merge to.
  665. if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID )
  666. return;
  667. int iIndex = m_aMergeMDLs.AddToTail();
  668. if ( !m_aMergeMDLs.IsValidIndex( iIndex ) )
  669. return;
  670. m_aMergeMDLs[iIndex].m_MDL.SetMDL( handle );
  671. if ( nSkin != -1 )
  672. {
  673. m_aMergeMDLs[iIndex].m_MDL.m_nSkin = nSkin;
  674. }
  675. m_aMergeMDLs[iIndex].m_MDL.m_nLOD = m_RootMDL.m_MDL.m_nLOD;
  676. m_aMergeMDLs[iIndex].m_MDL.m_pProxyData = pProxyData;
  677. SetIdentityMatrix( m_aMergeMDLs[iIndex].m_MDLToWorld );
  678. m_aMergeMDLs[iIndex].m_bDisabled = false;
  679. // Need to invalidate the layout so the panel will adjust is LookAt for the new model.
  680. InvalidateLayout();
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose:
  684. //-----------------------------------------------------------------------------
  685. MDLHandle_t CMDLPanel::SetMergeMDL( const char *pMDLName, void *pProxyData, int nSkin /*= -1 */ )
  686. {
  687. MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName );
  688. MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID;
  689. if ( vgui::MDLCache()->IsErrorModel( hMDL ) )
  690. {
  691. hMDL = MDLHANDLE_INVALID;
  692. }
  693. SetMergeMDL( hMDL, pProxyData, nSkin );
  694. // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference.
  695. int nRef = vgui::MDLCache()->Release( hMDLFindResult );
  696. (void)nRef; // Avoid unreferenced variable warning
  697. AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMergeMDL referenced a model that has a zero ref count." );
  698. return hMDL;
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose:
  702. //-----------------------------------------------------------------------------
  703. int CMDLPanel::GetMergeMDLIndex( void *pProxyData )
  704. {
  705. int nMergeCount = m_aMergeMDLs.Count();
  706. for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
  707. {
  708. if ( m_aMergeMDLs[iMerge].m_MDL.m_pProxyData == pProxyData )
  709. return iMerge;
  710. }
  711. return -1;
  712. }
  713. //-----------------------------------------------------------------------------
  714. // Purpose:
  715. //-----------------------------------------------------------------------------
  716. int CMDLPanel::GetMergeMDLIndex( MDLHandle_t handle )
  717. {
  718. int nMergeCount = m_aMergeMDLs.Count();
  719. for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
  720. {
  721. if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
  722. return iMerge;
  723. }
  724. return -1;
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose:
  728. //-----------------------------------------------------------------------------
  729. CMDL *CMDLPanel::GetMergeMDL( MDLHandle_t handle )
  730. {
  731. int nMergeCount = m_aMergeMDLs.Count();
  732. for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge )
  733. {
  734. if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle )
  735. return (&m_aMergeMDLs[iMerge].m_MDL);
  736. }
  737. return NULL;
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose:
  741. //-----------------------------------------------------------------------------
  742. void CMDLPanel::ClearMergeMDLs( void )
  743. {
  744. m_aMergeMDLs.Purge();
  745. }