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.

555 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A panel that display particle systems
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <KeyValues.h>
  8. #include <vgui/IScheme.h>
  9. #include <vgui/ISurface.h>
  10. #include <vgui_controls/EditablePanel.h>
  11. #include "vgui/IVGui.h"
  12. #include "tf_particlepanel.h"
  13. #include "matsys_controls/matsyscontrols.h"
  14. #include "VGuiMatSurface/IMatSystemSurface.h"
  15. #include "tier2/renderutils.h"
  16. #include "renderparm.h"
  17. using namespace vgui;
  18. CTFParticlePanel::ParticleEffect_t::ParticleEffect_t()
  19. : m_bLoop( true )
  20. , m_pParticleSystem( NULL )
  21. , m_flLastTime( FLT_MAX )
  22. , m_ParticleSystemName( NULL )
  23. , m_bStartActivated( true )
  24. , m_flScale( 1.f )
  25. , m_flEndTime( FLT_MAX )
  26. , m_nXPos( 0 )
  27. , m_nYPos( 0 )
  28. , m_Angles( 0.f, 0.f, 0.f )
  29. , m_pParent( NULL )
  30. , m_bForceStopped( false )
  31. , m_bAutoDelete( false )
  32. , m_bStarted( false )
  33. {}
  34. DECLARE_BUILD_FACTORY( CTFParticlePanel );
  35. //-----------------------------------------------------------------------------
  36. // Constructor, destructor
  37. //-----------------------------------------------------------------------------
  38. CTFParticlePanel::CTFParticlePanel( vgui::Panel *pParent, const char *pName )
  39. : BaseClass( pParent, pName )
  40. {
  41. m_Camera.m_flZNear = 3.0f;
  42. m_Camera.m_flZFar = 16384.0f * 1.73205080757f;
  43. m_Camera.m_flFOV = 30.0f;
  44. m_Camera.m_origin = Vector(0,0,0);
  45. m_Camera.m_angles = QAngle(0,0,0);
  46. m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" );
  47. m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true );
  48. }
  49. CTFParticlePanel::~CTFParticlePanel()
  50. {
  51. m_pLightmapTexture.Shutdown();
  52. m_DefaultEnvCubemap.Shutdown();
  53. m_vecParticleEffects.PurgeAndDeleteElements();
  54. }
  55. void CTFParticlePanel::ApplySettings( KeyValues *inResourceData )
  56. {
  57. BaseClass::ApplySettings( inResourceData );
  58. KeyValues *pKVParticleEffects = inResourceData->FindKey( "ParticleEffects" );
  59. if ( pKVParticleEffects )
  60. {
  61. FOR_EACH_SUBKEY( pKVParticleEffects, pKVEffect )
  62. {
  63. m_vecParticleEffects[ m_vecParticleEffects.AddToTail() ] = new ParticleEffect_t();
  64. ParticleEffect_t* pEffect = m_vecParticleEffects.Tail();
  65. // get the position
  66. int alignScreenWide = GetWide(), alignScreenTall = GetTall(); // screen dimensions used for pinning in splitscreen
  67. int x, y;
  68. GetPos(x, y);
  69. const char *xstr = pKVEffect->GetString( "particle_xpos", NULL );
  70. const char *ystr = pKVEffect->GetString( "particle_ypos", NULL );
  71. if (xstr)
  72. {
  73. bool bRightAlign = false;
  74. bool bCenterAlign = false;
  75. // look for alignment flags
  76. if (xstr[0] == 'r' || xstr[0] == 'R')
  77. {
  78. bRightAlign = true;
  79. xstr++;
  80. }
  81. else if (xstr[0] == 'c' || xstr[0] == 'C')
  82. {
  83. bCenterAlign = true;
  84. xstr++;
  85. }
  86. // get the value
  87. x = atoi(xstr);
  88. // scale the x up to our screen co-ords
  89. if ( IsProportional() )
  90. {
  91. x = scheme()->GetProportionalScaledValueEx(GetScheme(), x);
  92. }
  93. // now correct the alignment
  94. if ( bRightAlign )
  95. {
  96. x = alignScreenWide - x;
  97. }
  98. else if ( bCenterAlign )
  99. {
  100. x = (alignScreenWide / 2) + x;
  101. }
  102. }
  103. if (ystr)
  104. {
  105. bool bBottomAlign = false;
  106. bool bCenterAlign = false;
  107. // look for alignment flags
  108. if (ystr[0] == 'r' || ystr[0] == 'R')
  109. {
  110. bBottomAlign = true;
  111. ystr++;
  112. }
  113. else if (ystr[0] == 'c' || ystr[0] == 'C')
  114. {
  115. bCenterAlign = true;
  116. ystr++;
  117. }
  118. y = atoi(ystr);
  119. if (IsProportional())
  120. {
  121. // scale the y up to our screen co-ords
  122. y = scheme()->GetProportionalScaledValueEx(GetScheme(), y);
  123. }
  124. // now correct the alignment
  125. if ( bBottomAlign )
  126. {
  127. y = alignScreenTall - y;
  128. }
  129. else if ( bCenterAlign )
  130. {
  131. y = (alignScreenTall / 2) + y;
  132. }
  133. }
  134. pEffect->m_nXPos = x;
  135. pEffect->m_nYPos = y;
  136. pEffect->m_flScale = pKVEffect->GetFloat( "particle_scale", 1.f );
  137. // Scale the scale factor the same way we do the XY position coordinates
  138. if( IsProportional() )
  139. {
  140. int wide, tall;
  141. surface()->GetScreenSize( wide, tall );
  142. int proH, proW;
  143. surface()->GetProportionalBase( proW, proH );
  144. double scale = (double)tall / (double)proH;
  145. pEffect->m_flScale *= scale;
  146. }
  147. pEffect->m_pParent = this;
  148. pEffect->m_bLoop = pKVEffect->GetBool( "loop", true );
  149. pEffect->m_bStartActivated = pKVEffect->GetBool( "start_activated", true );
  150. pEffect->SetParticleSystem( pKVEffect->GetString( "particleName" ) );
  151. // Read angles for the particle system
  152. {
  153. float x1,y1,z1;
  154. const char* pszAngles = pKVEffect->GetString( "angles" );
  155. if( *pszAngles )
  156. {
  157. if( pEffect->m_pParticleSystem && sscanf( pszAngles, "%f %f %f", &x1, &y1, &z1 ) == 3 )
  158. {
  159. pEffect->m_Angles = QAngle( x1, y1, z1 );
  160. Quaternion q;
  161. AngleQuaternion( pEffect->m_Angles , q );
  162. pEffect->m_pParticleSystem->SetControlPointOrientation( 0, q );
  163. }
  164. }
  165. }
  166. pEffect->SetControlPointValue( 0, Vector(0,0,0) );
  167. // Read all control point values
  168. const char* pszControlPoint = NULL;
  169. int nControlPointNumber = 0;
  170. do
  171. {
  172. pszControlPoint = pKVEffect->GetString( VarArgs("control_point%d", nControlPointNumber), "" );
  173. if ( *pszControlPoint )
  174. {
  175. float x2,y2,z2;
  176. if (sscanf(pszControlPoint, "%f %f %f", &x2, &y2, &z2 ) == 3)
  177. {
  178. pEffect->SetControlPointValue( nControlPointNumber, Vector( x2, y2, z2 ) );
  179. }
  180. }
  181. ++nControlPointNumber;
  182. }
  183. while( *pszControlPoint );
  184. }
  185. }
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Scheme
  189. //-----------------------------------------------------------------------------
  190. void CTFParticlePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  191. {
  192. BaseClass::ApplySchemeSettings( pScheme );
  193. SetMouseInputEnabled( false );
  194. SetKeyBoardInputEnabled( false );
  195. }
  196. void CTFParticlePanel::OnCommand( const char *command )
  197. {
  198. if ( !Q_strnicmp( command, "start", ARRAYSIZE("start") - 1 ) )
  199. {
  200. // Check if they specified a particular one to turn on
  201. const char* pszNum = command + ARRAYSIZE( "start" ) - 1;
  202. int nIndex = m_vecParticleEffects.InvalidIndex();
  203. if( pszNum && pszNum[0] )
  204. {
  205. nIndex = atoi(pszNum);
  206. Assert( nIndex >= 0 && nIndex < m_vecParticleEffects.Count() );
  207. }
  208. FOR_EACH_VEC( m_vecParticleEffects, i )
  209. {
  210. if ( nIndex != m_vecParticleEffects.InvalidIndex() && i != nIndex )
  211. continue;
  212. ParticleEffect_t* pEffect = m_vecParticleEffects[ i ];
  213. if( !pEffect->m_pParticleSystem )
  214. {
  215. pEffect->SetParticleSystem( pEffect->m_ParticleSystemName );
  216. }
  217. if( !pEffect->m_pParticleSystem )
  218. continue;
  219. pEffect->StartupParticleCollection();
  220. pEffect->m_pParticleSystem->StartEmission();
  221. pEffect->m_bForceStopped = false;
  222. }
  223. }
  224. else if ( !Q_strnicmp( command, "stop", ARRAYSIZE("stop") - 1 ) )
  225. {
  226. // Check if they specified a specific one to turn off
  227. const char* pszNum = command + ARRAYSIZE( "start" ) - 1;
  228. if( pszNum && pszNum[0] )
  229. {
  230. int iIndex = atoi(pszNum);
  231. Assert( iIndex >= 0 && iIndex < m_vecParticleEffects.Count() );
  232. ParticleEffect_t* pEffect = m_vecParticleEffects[iIndex];
  233. if( pEffect->m_pParticleSystem )
  234. {
  235. pEffect->m_pParticleSystem->StopEmission();
  236. pEffect->m_bForceStopped = true;
  237. }
  238. }
  239. else
  240. {
  241. // Turn them ALL off
  242. FOR_EACH_VEC( m_vecParticleEffects, i )
  243. {
  244. ParticleEffect_t* pEffect = m_vecParticleEffects[ i ];
  245. if( pEffect->m_pParticleSystem )
  246. {
  247. pEffect->m_pParticleSystem->StopEmission();
  248. pEffect->m_bForceStopped = true;
  249. }
  250. }
  251. }
  252. }
  253. }
  254. void CTFParticlePanel::FireParticleEffect( const char *pszName, int xPos, int yPos, float flScale, bool bLoop, float flEndTime )
  255. {
  256. m_vecParticleEffects[ m_vecParticleEffects.AddToTail() ] = new ParticleEffect_t();
  257. ParticleEffect_t* pEffect = m_vecParticleEffects.Tail();
  258. int iParentAbsX, iParentAbsY;
  259. vgui::ipanel()->GetAbsPos( GetParent()->GetVPanel(), iParentAbsX, iParentAbsY );
  260. pEffect->m_pParent = this;
  261. pEffect->m_nXPos = xPos - iParentAbsX;
  262. pEffect->m_nYPos = yPos - iParentAbsY;
  263. pEffect->m_flScale = flScale;
  264. pEffect->m_bLoop = bLoop;
  265. pEffect->m_bAutoDelete = true; // This will get automatically deleted once it stops
  266. pEffect->m_bStartActivated = true;
  267. pEffect->m_flEndTime = gpGlobals->curtime + flEndTime;
  268. if( IsProportional() )
  269. {
  270. int wide, tall;
  271. surface()->GetScreenSize( wide, tall );
  272. int proH, proW;
  273. surface()->GetProportionalBase( proW, proH );
  274. double scale = (double)tall / (double)proH;
  275. pEffect->m_flScale *= scale;
  276. }
  277. for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
  278. {
  279. pEffect->SetControlPointValue( i, Vector( 0, 0, 10.0f * i ) );
  280. }
  281. pEffect->SetParticleSystem( pszName );
  282. }
  283. static bool IsValidHierarchy( CParticleCollection *pCollection )
  284. {
  285. if ( !pCollection->IsValid() )
  286. return false;
  287. for( CParticleCollection *pChild = pCollection->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
  288. {
  289. if ( !IsValidHierarchy( pChild ) )
  290. return false;
  291. }
  292. return true;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Simulate the particle system
  296. //-----------------------------------------------------------------------------
  297. void CTFParticlePanel::OnTick()
  298. {
  299. BaseClass::OnTick();
  300. float flTime = engine->Time();
  301. bool bAnyActive = false;
  302. // Update all particles
  303. FOR_EACH_VEC_BACK( m_vecParticleEffects, i )
  304. {
  305. bAnyActive |= m_vecParticleEffects[i]->Update( flTime );
  306. // If this effect is done and should auto-delete, then now is when we delete
  307. if( m_vecParticleEffects[i]->m_pParticleSystem == NULL && m_vecParticleEffects[i]->m_bAutoDelete )
  308. {
  309. delete m_vecParticleEffects[i];
  310. m_vecParticleEffects.FastRemove( i );
  311. }
  312. }
  313. if ( !bAnyActive )
  314. {
  315. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  316. }
  317. }
  318. void CTFParticlePanel::Paint()
  319. {
  320. // This needs calling to reset various counters.
  321. g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime );
  322. // No particles? Do nothing.
  323. if( m_vecParticleEffects.Count() == 0 )
  324. return;
  325. int screenW, screenH;
  326. vgui::surface()->GetScreenSize( screenW, screenH );
  327. vgui::MatSystemSurface()->Begin3DPaint( 0, 0, screenW, screenH, false );
  328. VMatrix view, projection;
  329. ComputeViewMatrix( &view, m_Camera );
  330. ComputeProjectionMatrix( &projection, m_Camera, screenW, screenH );
  331. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  332. pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
  333. pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, false );
  334. pRenderContext->MatrixMode( MATERIAL_MODEL );
  335. pRenderContext->LoadIdentity( );
  336. pRenderContext->MatrixMode( MATERIAL_VIEW );
  337. pRenderContext->LoadMatrix( view );
  338. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  339. pRenderContext->LoadMatrix( projection );
  340. int iXOffset, iYOffset;
  341. vgui::ipanel()->GetAbsPos( GetVPanel(), iXOffset, iYOffset );
  342. if ( iXOffset > 0 )
  343. iXOffset = 0;
  344. if ( iYOffset > 0 )
  345. iYOffset = 0;
  346. float flXScale = 1.f;
  347. if ( GetWide() > screenW )
  348. flXScale = (float)screenW / GetWide();
  349. float flYScale = 1.f;
  350. if ( GetTall() > screenH )
  351. flYScale = (float)screenH / GetTall();
  352. FOR_EACH_VEC( m_vecParticleEffects, i )
  353. {
  354. m_vecParticleEffects[i]->Paint( pRenderContext, iXOffset, iYOffset, flXScale, flYScale, screenW, screenH );
  355. }
  356. pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
  357. vgui::MatSystemSurface()->End3DPaint();
  358. }
  359. bool CTFParticlePanel::ParticleEffect_t::Update( float flTime )
  360. {
  361. if ( !m_pParticleSystem || !m_bStarted )
  362. return false;
  363. if ( m_flLastTime == FLT_MAX )
  364. {
  365. m_flLastTime = flTime;
  366. }
  367. float flDt = flTime - m_flLastTime;
  368. m_flLastTime = flTime;
  369. Quaternion q;
  370. AngleQuaternion( m_Angles, q );
  371. for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
  372. {
  373. if ( !m_pParticleSystem->ReadsControlPoint( i ) )
  374. continue;
  375. m_pParticleSystem->SetControlPoint( i, m_pControlPointValue[i] );
  376. m_pParticleSystem->SetControlPointOrientation( i, q );
  377. m_pParticleSystem->SetControlPointParent( i, i );
  378. }
  379. // Restart the particle system if it's finished
  380. bool bIsInvalid = !IsValidHierarchy( m_pParticleSystem );
  381. if ( !bIsInvalid )
  382. {
  383. m_pParticleSystem->Simulate( flDt, false );
  384. }
  385. // Past our end time?
  386. bool bEnd = gpGlobals->curtime >= m_flEndTime;
  387. if ( m_pParticleSystem->IsFinished() || bIsInvalid || bEnd )
  388. {
  389. delete m_pParticleSystem;
  390. m_pParticleSystem = NULL;
  391. // Loop if we're supposed to
  392. if ( m_bLoop && m_ParticleSystemName.Length() && !m_bForceStopped )
  393. {
  394. m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemName );
  395. }
  396. if ( bIsInvalid && m_pParent )
  397. {
  398. m_pParent->PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) );
  399. }
  400. m_flLastTime = FLT_MAX;
  401. }
  402. return m_pParticleSystem != NULL;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Startup, shutdown particle collection
  406. //-----------------------------------------------------------------------------
  407. void CTFParticlePanel::ParticleEffect_t::StartupParticleCollection()
  408. {
  409. if ( m_pParticleSystem && m_pParent )
  410. {
  411. vgui::ivgui()->AddTickSignal( m_pParent->GetVPanel(), 0 );
  412. }
  413. m_flLastTime = FLT_MAX;
  414. m_bStarted = true;
  415. }
  416. void CTFParticlePanel::ParticleEffect_t::ShutdownParticleCollection()
  417. {
  418. if ( m_pParticleSystem && m_pParent )
  419. {
  420. delete m_pParticleSystem;
  421. m_pParticleSystem = NULL;
  422. }
  423. m_bStarted = false;
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Set the particle system to draw
  427. //-----------------------------------------------------------------------------
  428. void CTFParticlePanel::ParticleEffect_t::SetParticleSystem( const char* pszParticleSystemName )
  429. {
  430. ShutdownParticleCollection();
  431. if( !g_pParticleSystemMgr->IsParticleSystemDefined( pszParticleSystemName ) )
  432. {
  433. AssertMsg1( false, "%s is not a valid particle system name", pszParticleSystemName );
  434. return;
  435. }
  436. m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( pszParticleSystemName );
  437. m_ParticleSystemName = pszParticleSystemName;
  438. m_pParent->PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) );
  439. if( m_bStartActivated )
  440. {
  441. StartupParticleCollection();
  442. }
  443. }
  444. void CTFParticlePanel::ParticleEffect_t::Paint( CMatRenderContextPtr& pRenderContext, int iXOffset, int iYOffset, float flXScale, float flYScale, int screenW, int screenH )
  445. {
  446. if ( !m_pParticleSystem || !m_bStarted )
  447. return;
  448. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  449. pRenderContext->PushMatrix();
  450. pRenderContext->LoadIdentity();
  451. pRenderContext->Ortho( 0, 0, screenW, screenH, -9999, 9999 );
  452. pRenderContext->Translate( flXScale * ( m_nXPos + iXOffset ), screenH - flYScale * ( m_nYPos + iYOffset ), 0.f );
  453. pRenderContext->Scale( m_flScale, m_flScale, m_flScale );
  454. // Render Particles
  455. pRenderContext->MatrixMode( MATERIAL_MODEL );
  456. pRenderContext->PushMatrix();
  457. pRenderContext->LoadIdentity( );
  458. m_pParticleSystem->Render( pRenderContext );
  459. pRenderContext->MatrixMode( MATERIAL_MODEL );
  460. pRenderContext->PopMatrix();
  461. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  462. pRenderContext->PopMatrix();
  463. }