Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

603 lines
16 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "enginesprite.h"
  9. #include "hud.h"
  10. #include "materialsystem/imesh.h"
  11. #include "materialsystem/imaterial.h"
  12. #include "materialsystem/imaterialvar.h"
  13. #include "c_sprite.h"
  14. #include "tier1/callqueue.h"
  15. #include "tier1/keyvalues.h"
  16. #include "tier2/tier2.h"
  17. #include "filesystem.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. // Sprites are clipped to this rectangle (x,y,width,height) if ScissorTest is enabled
  21. static int scissor_x = 0;
  22. static int scissor_y = 0;
  23. static int scissor_width = 0;
  24. static int scissor_height = 0;
  25. static bool giScissorTest = false;
  26. //-----------------------------------------------------------------------------
  27. // Purpose:
  28. // Set the scissor
  29. // the coordinate system for gl is upsidedown (inverted-y) as compared to software, so the
  30. // specified clipping rect must be flipped
  31. // Input : x -
  32. // y -
  33. // width -
  34. // height -
  35. //-----------------------------------------------------------------------------
  36. void EnableScissorTest( int x, int y, int width, int height )
  37. {
  38. x = clamp( x, 0, ScreenWidth() );
  39. y = clamp( y, 0, ScreenHeight() );
  40. width = clamp( width, 0, ScreenWidth() - x );
  41. height = clamp( height, 0, ScreenHeight() - y );
  42. scissor_x = x;
  43. scissor_width = width;
  44. scissor_y = y;
  45. scissor_height = height;
  46. giScissorTest = true;
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. void DisableScissorTest( void )
  52. {
  53. scissor_x = 0;
  54. scissor_width = 0;
  55. scissor_y = 0;
  56. scissor_height = 0;
  57. giScissorTest = false;
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Verify that this is a valid, properly ordered rectangle.
  61. // Input : *prc -
  62. // Output : int
  63. //-----------------------------------------------------------------------------
  64. static int ValidateWRect(const wrect_t *prc)
  65. {
  66. if (!prc)
  67. return false;
  68. if ((prc->left >= prc->right) || (prc->top >= prc->bottom))
  69. {
  70. //!!!UNDONE Dev only warning msg
  71. return false;
  72. }
  73. return true;
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Purpose: classic interview question
  77. // Input : *prc1 -
  78. // *prc2 -
  79. // *prc -
  80. // Output : int
  81. //-----------------------------------------------------------------------------
  82. static int IntersectWRect(const wrect_t *prc1, const wrect_t *prc2, wrect_t *prc)
  83. {
  84. wrect_t rc;
  85. if (!prc)
  86. prc = &rc;
  87. prc->left = MAX(prc1->left, prc2->left);
  88. prc->right = MIN(prc1->right, prc2->right);
  89. if (prc->left < prc->right)
  90. {
  91. prc->top = MAX(prc1->top, prc2->top);
  92. prc->bottom = MIN(prc1->bottom, prc2->bottom);
  93. if (prc->top < prc->bottom)
  94. return 1;
  95. }
  96. return 0;
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose:
  100. // Input : x -
  101. // y -
  102. // width -
  103. // height -
  104. // u0 -
  105. // v0 -
  106. // u1 -
  107. // v1 -
  108. // Output : static bool
  109. //-----------------------------------------------------------------------------
  110. static bool Scissor( int& x, int& y, int& width, int& height, float& u0, float& v0, float& u1, float& v1 )
  111. {
  112. // clip sub rect to sprite
  113. if ((width == 0) || (height == 0))
  114. return false;
  115. if ((x + width <= scissor_x) || (x >= scissor_x + scissor_width) ||
  116. (y + height <= scissor_y) || (y >= scissor_y + scissor_height))
  117. return false;
  118. float dudx = (u1-u0) / width;
  119. float dvdy = (v1-v0) / height;
  120. if (x < scissor_x)
  121. {
  122. u0 += (scissor_x - x) * dudx;
  123. width -= scissor_x - x;
  124. x = scissor_x;
  125. }
  126. if (x + width > scissor_x + scissor_width)
  127. {
  128. u1 -= (x + width - (scissor_x + scissor_width)) * dudx;
  129. width = scissor_x + scissor_width - x;
  130. }
  131. if (y < scissor_y)
  132. {
  133. v0 += (scissor_y - y) * dvdy;
  134. height -= scissor_y - y;
  135. y = scissor_y;
  136. }
  137. if (y + height > scissor_y + scissor_height)
  138. {
  139. v1 -= (y + height - (scissor_y + scissor_height)) * dvdy;
  140. height = scissor_y + scissor_height - y;
  141. }
  142. return true;
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. // Input : *pSprite -
  147. // frame -
  148. // *pfLeft -
  149. // *pfRight -
  150. // *pfTop -
  151. // *pfBottom -
  152. // *pw -
  153. // *ph -
  154. // *prcSubRect -
  155. // Output : static void
  156. //-----------------------------------------------------------------------------
  157. static void AdjustSubRect(CEngineSprite *pSprite, int frame, float *pfLeft, float *pfRight, float *pfTop,
  158. float *pfBottom, int *pw, int *ph, const wrect_t *prcSubRect)
  159. {
  160. wrect_t rc;
  161. float f;
  162. if (!ValidateWRect(prcSubRect))
  163. return;
  164. // clip sub rect to sprite
  165. rc.top = rc.left = 0;
  166. rc.right = *pw;
  167. rc.bottom = *ph;
  168. if (!IntersectWRect(prcSubRect, &rc, &rc))
  169. return;
  170. *pw = rc.right - rc.left;
  171. *ph = rc.bottom - rc.top;
  172. f = 1.0 / (float)pSprite->GetWidth();;
  173. *pfLeft = ((float)rc.left + 0.5) * f;
  174. *pfRight = ((float)rc.right - 0.5) * f;
  175. f = 1.0 / (float)pSprite->GetHeight();
  176. *pfTop = ((float)rc.top + 0.5) * f;
  177. *pfBottom = ((float)rc.bottom - 0.5) * f;
  178. return;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. static unsigned int spriteOriginCache = 0;
  184. static unsigned int spriteOrientationCache = 0;
  185. bool CEngineSprite::Init( const char *pName )
  186. {
  187. m_hAVIMaterial = AVIMATERIAL_INVALID;
  188. m_hBIKMaterial = BIKMATERIAL_INVALID;
  189. m_width = m_height = m_numFrames = 1;
  190. const char *pExt = Q_GetFileExtension( pName );
  191. bool bIsAVI = pExt && !Q_stricmp( pExt, "avi" );
  192. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  193. bool bIsBIK = pExt && !Q_stricmp( pExt, "bik" );
  194. #endif
  195. if ( bIsAVI && IsPC() )
  196. {
  197. m_hAVIMaterial = avi->CreateAVIMaterial( pName, pName, "GAME" );
  198. if ( m_hAVIMaterial == AVIMATERIAL_INVALID )
  199. return false;
  200. IMaterial *pMaterial = avi->GetMaterial( m_hAVIMaterial );
  201. avi->GetFrameSize( m_hAVIMaterial, &m_width, &m_height );
  202. m_numFrames = avi->GetFrameCount( m_hAVIMaterial );
  203. for ( int i = 0; i < kRenderModeCount; ++i )
  204. {
  205. m_material[i] = pMaterial;
  206. pMaterial->IncrementReferenceCount();
  207. }
  208. }
  209. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  210. else if ( bIsBIK )
  211. {
  212. m_hBIKMaterial = bik->CreateMaterial( pName, pName, "GAME" );
  213. if ( m_hBIKMaterial == BIKMATERIAL_INVALID )
  214. return false;
  215. IMaterial *pMaterial = bik->GetMaterial( m_hBIKMaterial );
  216. bik->GetFrameSize( m_hBIKMaterial, &m_width, &m_height );
  217. m_numFrames = bik->GetFrameCount( m_hBIKMaterial );
  218. for ( int i = 0; i < kRenderModeCount; ++i )
  219. {
  220. m_material[i] = pMaterial;
  221. pMaterial->IncrementReferenceCount();
  222. }
  223. }
  224. #endif
  225. else
  226. {
  227. char pTemp[MAX_PATH];
  228. char pMaterialName[MAX_PATH];
  229. char pMaterialPath[MAX_PATH];
  230. Q_StripExtension( pName, pTemp, sizeof(pTemp) );
  231. Q_strlower( pTemp );
  232. Q_FixSlashes( pTemp, '/' );
  233. // Check to see if this is a UNC-specified material name
  234. bool bIsUNC = pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/';
  235. if ( !bIsUNC )
  236. {
  237. Q_strncpy( pMaterialName, "materials/", sizeof(pMaterialName) );
  238. Q_strncat( pMaterialName, pTemp, sizeof(pMaterialName), COPY_ALL_CHARACTERS );
  239. }
  240. else
  241. {
  242. Q_strncpy( pMaterialName, pTemp, sizeof(pMaterialName) );
  243. }
  244. Q_strncpy( pMaterialPath, pMaterialName, sizeof(pMaterialPath) );
  245. Q_SetExtension( pMaterialPath, ".vmt", sizeof(pMaterialPath) );
  246. for ( int i = 0; i < kRenderModeCount; ++i )
  247. {
  248. m_material[i] = NULL;
  249. }
  250. KeyValues *kv = new KeyValues( "vmt" );
  251. if ( !kv->LoadFromFile( g_pFullFileSystem, pMaterialPath, "GAME" ) )
  252. {
  253. Warning( "Unable to load sprite material %s!\n", pMaterialPath );
  254. return false;
  255. }
  256. for ( int i = 0; i < kRenderModeCount; ++i )
  257. {
  258. if ( i == kRenderNone || i == kRenderEnvironmental )
  259. {
  260. continue;
  261. }
  262. // strip possible materials/
  263. Q_snprintf( pMaterialPath, sizeof(pMaterialPath), "%s_rendermode_%d", pMaterialName + ( bIsUNC ? 0 : 10 ), i );
  264. KeyValues *pMaterialKV = kv->MakeCopy();
  265. pMaterialKV->SetInt( "$spriteRenderMode", i );
  266. m_material[i] = g_pMaterialSystem->FindProceduralMaterial( pMaterialPath, TEXTURE_GROUP_CLIENT_EFFECTS, pMaterialKV );
  267. m_material[i]->IncrementReferenceCount();
  268. }
  269. kv->deleteThis();
  270. m_width = m_material[0]->GetMappingWidth();
  271. m_height = m_material[0]->GetMappingHeight();
  272. m_numFrames = m_material[0]->GetNumAnimationFrames();
  273. }
  274. for ( int i = 0; i < kRenderModeCount; ++i )
  275. {
  276. if ( i == kRenderNone || i == kRenderEnvironmental )
  277. continue;
  278. if ( !m_material[i] )
  279. return false;
  280. }
  281. IMaterialVar *orientationVar = m_material[0]->FindVarFast( "$spriteorientation", &spriteOrientationCache );
  282. m_orientation = orientationVar ? orientationVar->GetIntValue() : C_SpriteRenderer::SPR_VP_PARALLEL_UPRIGHT;
  283. IMaterialVar *originVar = m_material[0]->FindVarFast( "$spriteorigin", &spriteOriginCache );
  284. Vector origin, originVarValue;
  285. if( !originVar || ( originVar->GetType() != MATERIAL_VAR_TYPE_VECTOR ) )
  286. {
  287. origin[0] = -m_width * 0.5f;
  288. origin[1] = m_height * 0.5f;
  289. }
  290. else
  291. {
  292. originVar->GetVecValue( &originVarValue[0], 3 );
  293. origin[0] = -m_width * originVarValue[0];
  294. origin[1] = m_height * originVarValue[1];
  295. }
  296. up = origin[1];
  297. down = origin[1] - m_height;
  298. left = origin[0];
  299. right = m_width + origin[0];
  300. return true;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose:
  304. //-----------------------------------------------------------------------------
  305. void CEngineSprite::Shutdown( void )
  306. {
  307. if ( m_hAVIMaterial != AVIMATERIAL_INVALID )
  308. {
  309. avi->DestroyAVIMaterial( m_hAVIMaterial );
  310. m_hAVIMaterial = AVIMATERIAL_INVALID;
  311. }
  312. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  313. if ( m_hBIKMaterial != BIKMATERIAL_INVALID )
  314. {
  315. bik->DestroyMaterial( m_hBIKMaterial );
  316. m_hBIKMaterial = BIKMATERIAL_INVALID;
  317. }
  318. #endif
  319. UnloadMaterial();
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Is the sprite an AVI?
  323. //-----------------------------------------------------------------------------
  324. bool CEngineSprite::IsAVI()
  325. {
  326. return ( m_hAVIMaterial != AVIMATERIAL_INVALID );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Is the sprite an BIK?
  330. //-----------------------------------------------------------------------------
  331. bool CEngineSprite::IsBIK()
  332. {
  333. return ( m_hBIKMaterial != BIKMATERIAL_INVALID );
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Returns the texture coordinate range used to draw the sprite
  337. //-----------------------------------------------------------------------------
  338. void CEngineSprite::GetTexCoordRange( float *pMinU, float *pMinV, float *pMaxU, float *pMaxV )
  339. {
  340. *pMaxU = 1.0f;
  341. *pMaxV = 1.0f;
  342. if ( IsAVI() )
  343. {
  344. avi->GetTexCoordRange( m_hAVIMaterial, pMaxU, pMaxV );
  345. }
  346. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  347. if ( IsBIK() )
  348. {
  349. bik->GetTexCoordRange( m_hBIKMaterial, pMaxU, pMaxV );
  350. }
  351. #endif
  352. float flOOWidth = ( m_width != 0 ) ? 1.0f / m_width : 1.0f;
  353. float flOOHeight = ( m_height!= 0 ) ? 1.0f / m_height : 1.0f;
  354. *pMinU = 0.5f * flOOWidth;
  355. *pMinV = 0.5f * flOOHeight;
  356. *pMaxU = (*pMaxU) - (*pMinU);
  357. *pMaxV = (*pMaxV) - (*pMinV);
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose:
  361. //-----------------------------------------------------------------------------
  362. void CEngineSprite::SetColor( float r, float g, float b )
  363. {
  364. Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) );
  365. Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) );
  366. m_hudSpriteColor[0] = r;
  367. m_hudSpriteColor[1] = g;
  368. m_hudSpriteColor[2] = b;
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. void CEngineSprite::GetHUDSpriteColor( float* color )
  374. {
  375. VectorCopy( m_hudSpriteColor, color );
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Returns the material
  379. //-----------------------------------------------------------------------------
  380. static unsigned int frameCache = 0;
  381. IMaterial *CEngineSprite::GetMaterial( RenderMode_t nRenderMode, int nFrame )
  382. {
  383. if ( nRenderMode == kRenderNone || nRenderMode == kRenderEnvironmental )
  384. return NULL;
  385. if ( IsAVI() )
  386. {
  387. avi->SetFrame( m_hAVIMaterial, nFrame );
  388. return m_material[ 0 ]; // render mode is ignored for avi
  389. }
  390. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  391. if ( IsBIK() )
  392. {
  393. bik->SetFrame( m_hBIKMaterial, nFrame );
  394. return m_material[ 0 ]; // render mode is ignored for bink
  395. }
  396. #endif
  397. IMaterial *pMaterial = m_material[nRenderMode];
  398. Assert( pMaterial );
  399. if ( pMaterial == NULL )
  400. return NULL;
  401. IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache );
  402. if ( pFrameVar )
  403. {
  404. pFrameVar->SetIntValue( nFrame );
  405. }
  406. return pMaterial;
  407. }
  408. void CEngineSprite::SetFrame( RenderMode_t nRenderMode, int nFrame )
  409. {
  410. if ( IsAVI() )
  411. {
  412. avi->SetFrame( m_hAVIMaterial, nFrame );
  413. return;
  414. }
  415. #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
  416. if ( IsBIK() )
  417. {
  418. bik->SetFrame( m_hBIKMaterial, nFrame );
  419. return;
  420. }
  421. #endif
  422. IMaterial *pMaterial = m_material[nRenderMode];
  423. if ( !pMaterial )
  424. return;
  425. IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache );
  426. if ( pFrameVar )
  427. {
  428. pFrameVar->SetIntValue( nFrame );
  429. }
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose:
  433. // Output : int
  434. //-----------------------------------------------------------------------------
  435. int CEngineSprite::GetOrientation( void )
  436. {
  437. return m_orientation;
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. //-----------------------------------------------------------------------------
  442. void CEngineSprite::UnloadMaterial( void )
  443. {
  444. for ( int i = 0; i < kRenderModeCount; ++i )
  445. {
  446. if( m_material[i] )
  447. {
  448. m_material[i]->DecrementReferenceCount();
  449. m_material[i] = NULL;
  450. }
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose:
  455. //-----------------------------------------------------------------------------
  456. void CEngineSprite::DrawFrame( RenderMode_t nRenderMode, int frame, int x, int y, const wrect_t *prcSubRect )
  457. {
  458. DrawFrameOfSize( nRenderMode, frame, x, y, GetWidth(), GetHeight(), prcSubRect );
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Purpose:
  462. // Input : frame -
  463. // x -
  464. // y -
  465. // *prcSubRect -
  466. //-----------------------------------------------------------------------------
  467. void CEngineSprite::DrawFrameOfSize( RenderMode_t nRenderMode, int frame, int x, int y, int iWidth, int iHeight, const wrect_t *prcSubRect )
  468. {
  469. // FIXME: If we ever call this with AVIs, need to have it call GetTexCoordRange and make that work
  470. Assert( !IsAVI() && !IsBIK() );
  471. float fLeft = 0;
  472. float fRight = 1;
  473. float fTop = 0;
  474. float fBottom = 1;
  475. if ( prcSubRect )
  476. {
  477. AdjustSubRect( this, frame, &fLeft, &fRight, &fTop, &fBottom, &iWidth, &iHeight, prcSubRect );
  478. }
  479. if ( giScissorTest && !Scissor( x, y, iWidth, iHeight, fLeft, fTop, fRight, fBottom ) )
  480. return;
  481. SetFrame( nRenderMode, frame );
  482. CMatRenderContextPtr pRenderContext( materials );
  483. IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, GetMaterial( nRenderMode ) );
  484. CMeshBuilder meshBuilder;
  485. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  486. float color[3];
  487. GetHUDSpriteColor( color );
  488. meshBuilder.Color3fv( color );
  489. meshBuilder.TexCoord2f( 0, fLeft, fTop );
  490. meshBuilder.Position3f( x, y, 0.0f );
  491. meshBuilder.AdvanceVertex();
  492. meshBuilder.Color3fv( color );
  493. meshBuilder.TexCoord2f( 0, fRight, fTop );
  494. meshBuilder.Position3f( x + iWidth, y, 0.0f );
  495. meshBuilder.AdvanceVertex();
  496. meshBuilder.Color3fv( color );
  497. meshBuilder.TexCoord2f( 0, fRight, fBottom );
  498. meshBuilder.Position3f( x + iWidth, y + iHeight, 0.0f );
  499. meshBuilder.AdvanceVertex();
  500. meshBuilder.Color3fv( color );
  501. meshBuilder.TexCoord2f( 0, fLeft, fBottom );
  502. meshBuilder.Position3f( x, y + iHeight, 0.0f );
  503. meshBuilder.AdvanceVertex();
  504. meshBuilder.End();
  505. pMesh->Draw();
  506. }