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.

543 lines
14 KiB

  1. //========= Copyright 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_VideoMaterial = NULL;
  188. for ( int i = 0; i < kRenderModeCount; ++i )
  189. {
  190. m_material[ i ] = NULL;
  191. }
  192. m_width = m_height = m_numFrames = 1;
  193. Assert( g_pVideo != NULL );
  194. if ( g_pVideo != NULL && g_pVideo->LocateVideoSystemForPlayingFile( pName ) != VideoSystem::NONE )
  195. {
  196. m_VideoMaterial = g_pVideo->CreateVideoMaterial( pName, pName, "GAME", VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, VideoSystem::DETERMINE_FROM_FILE_EXTENSION, false );
  197. if ( m_VideoMaterial == NULL )
  198. return false;
  199. IMaterial *pMaterial = m_VideoMaterial->GetMaterial();
  200. m_VideoMaterial->GetVideoImageSize( &m_width, &m_height );
  201. m_numFrames = m_VideoMaterial->GetFrameCount();
  202. for ( int i = 0; i < kRenderModeCount; ++i )
  203. {
  204. m_material[i] = pMaterial;
  205. pMaterial->IncrementReferenceCount();
  206. }
  207. }
  208. else
  209. {
  210. char pTemp[MAX_PATH];
  211. char pMaterialName[MAX_PATH];
  212. char pMaterialPath[MAX_PATH];
  213. Q_StripExtension( pName, pTemp, sizeof(pTemp) );
  214. Q_strlower( pTemp );
  215. Q_FixSlashes( pTemp, '/' );
  216. // Check to see if this is a UNC-specified material name
  217. bool bIsUNC = pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/';
  218. if ( !bIsUNC )
  219. {
  220. Q_strncpy( pMaterialName, "materials/", sizeof(pMaterialName) );
  221. Q_strncat( pMaterialName, pTemp, sizeof(pMaterialName), COPY_ALL_CHARACTERS );
  222. }
  223. else
  224. {
  225. Q_strncpy( pMaterialName, pTemp, sizeof(pMaterialName) );
  226. }
  227. Q_strncpy( pMaterialPath, pMaterialName, sizeof(pMaterialPath) );
  228. Q_SetExtension( pMaterialPath, ".vmt", sizeof(pMaterialPath) );
  229. KeyValues *kv = new KeyValues( "vmt" );
  230. if ( !kv->LoadFromFile( g_pFullFileSystem, pMaterialPath, "GAME" ) )
  231. {
  232. Warning( "Unable to load sprite material %s!\n", pMaterialPath );
  233. return false;
  234. }
  235. for ( int i = 0; i < kRenderModeCount; ++i )
  236. {
  237. if ( i == kRenderNone || i == kRenderEnvironmental )
  238. {
  239. m_material[i] = NULL;
  240. continue;
  241. }
  242. Q_snprintf( pMaterialPath, sizeof(pMaterialPath), "%s_rendermode_%d", pMaterialName, i );
  243. KeyValues *pMaterialKV = kv->MakeCopy();
  244. pMaterialKV->SetInt( "$spriteRenderMode", i );
  245. m_material[i] = g_pMaterialSystem->FindProceduralMaterial( pMaterialPath, TEXTURE_GROUP_CLIENT_EFFECTS, pMaterialKV );
  246. m_material[ i ]->IncrementReferenceCount();
  247. }
  248. kv->deleteThis();
  249. m_width = m_material[0]->GetMappingWidth();
  250. m_height = m_material[0]->GetMappingHeight();
  251. m_numFrames = m_material[0]->GetNumAnimationFrames();
  252. }
  253. for ( int i = 0; i < kRenderModeCount; ++i )
  254. {
  255. if ( i == kRenderNone || i == kRenderEnvironmental )
  256. continue;
  257. if ( !m_material[i] )
  258. return false;
  259. }
  260. IMaterialVar *orientationVar = m_material[0]->FindVarFast( "$spriteorientation", &spriteOrientationCache );
  261. m_orientation = orientationVar ? orientationVar->GetIntValue() : C_SpriteRenderer::SPR_VP_PARALLEL_UPRIGHT;
  262. IMaterialVar *originVar = m_material[0]->FindVarFast( "$spriteorigin", &spriteOriginCache );
  263. Vector origin, originVarValue;
  264. if( !originVar || ( originVar->GetType() != MATERIAL_VAR_TYPE_VECTOR ) )
  265. {
  266. origin[0] = -m_width * 0.5f;
  267. origin[1] = m_height * 0.5f;
  268. }
  269. else
  270. {
  271. originVar->GetVecValue( &originVarValue[0], 3 );
  272. origin[0] = -m_width * originVarValue[0];
  273. origin[1] = m_height * originVarValue[1];
  274. }
  275. up = origin[1];
  276. down = origin[1] - m_height;
  277. left = origin[0];
  278. right = m_width + origin[0];
  279. return true;
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose:
  283. //-----------------------------------------------------------------------------
  284. void CEngineSprite::Shutdown( void )
  285. {
  286. if ( g_pVideo != NULL && m_VideoMaterial != NULL )
  287. {
  288. g_pVideo->DestroyVideoMaterial( m_VideoMaterial );
  289. m_VideoMaterial = NULL;
  290. }
  291. UnloadMaterial();
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Is the sprite a video sprite?
  295. //-----------------------------------------------------------------------------
  296. bool CEngineSprite::IsVideo()
  297. {
  298. return ( m_VideoMaterial != NULL );
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Returns the texture coordinate range used to draw the sprite
  302. //-----------------------------------------------------------------------------
  303. void CEngineSprite::GetTexCoordRange( float *pMinU, float *pMinV, float *pMaxU, float *pMaxV )
  304. {
  305. *pMaxU = 1.0f;
  306. *pMaxV = 1.0f;
  307. if ( IsVideo() )
  308. {
  309. m_VideoMaterial->GetVideoTexCoordRange( pMaxU, pMaxV );
  310. }
  311. float flOOWidth = ( m_width != 0 ) ? 1.0f / m_width : 1.0f;
  312. float flOOHeight = ( m_height!= 0 ) ? 1.0f / m_height : 1.0f;
  313. *pMinU = 0.5f * flOOWidth;
  314. *pMinV = 0.5f * flOOHeight;
  315. *pMaxU = (*pMaxU) - (*pMinU);
  316. *pMaxV = (*pMaxV) - (*pMinV);
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose:
  320. //-----------------------------------------------------------------------------
  321. void CEngineSprite::SetColor( float r, float g, float b )
  322. {
  323. Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) );
  324. Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) );
  325. m_hudSpriteColor[0] = r;
  326. m_hudSpriteColor[1] = g;
  327. m_hudSpriteColor[2] = b;
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Purpose:
  331. //-----------------------------------------------------------------------------
  332. void CEngineSprite::GetHUDSpriteColor( float* color )
  333. {
  334. VectorCopy( m_hudSpriteColor, color );
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Returns the material
  338. //-----------------------------------------------------------------------------
  339. static unsigned int frameCache = 0;
  340. IMaterial *CEngineSprite::GetMaterial( RenderMode_t nRenderMode, int nFrame )
  341. {
  342. if ( nRenderMode == kRenderNone || nRenderMode == kRenderEnvironmental )
  343. return NULL;
  344. if ( IsVideo() )
  345. {
  346. m_VideoMaterial->SetFrame( nFrame );
  347. }
  348. IMaterial *pMaterial = m_material[nRenderMode];
  349. if ( pMaterial )
  350. {
  351. IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache );
  352. if ( pFrameVar )
  353. {
  354. pFrameVar->SetIntValue( nFrame );
  355. }
  356. }
  357. return pMaterial;
  358. }
  359. void CEngineSprite::SetFrame( RenderMode_t nRenderMode, int nFrame )
  360. {
  361. if ( IsVideo() )
  362. {
  363. m_VideoMaterial->SetFrame( nFrame );
  364. return;
  365. }
  366. IMaterial *pMaterial = m_material[nRenderMode];
  367. if ( !pMaterial )
  368. return;
  369. IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache );
  370. if ( pFrameVar )
  371. {
  372. pFrameVar->SetIntValue( nFrame );
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose:
  377. // Output : int
  378. //-----------------------------------------------------------------------------
  379. int CEngineSprite::GetOrientation( void )
  380. {
  381. return m_orientation;
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void CEngineSprite::UnloadMaterial( void )
  387. {
  388. for ( int i = 0; i < kRenderModeCount; ++i )
  389. {
  390. if( m_material[i] )
  391. {
  392. m_material[i]->DecrementReferenceCount();
  393. m_material[i] = NULL;
  394. }
  395. }
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. //-----------------------------------------------------------------------------
  400. void CEngineSprite::DrawFrame( RenderMode_t nRenderMode, int frame, int x, int y, const wrect_t *prcSubRect )
  401. {
  402. DrawFrameOfSize( nRenderMode, frame, x, y, GetWidth(), GetHeight(), prcSubRect );
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. // Input : frame -
  407. // x -
  408. // y -
  409. // *prcSubRect -
  410. //-----------------------------------------------------------------------------
  411. void CEngineSprite::DrawFrameOfSize( RenderMode_t nRenderMode, int frame, int x, int y, int iWidth, int iHeight, const wrect_t *prcSubRect )
  412. {
  413. // FIXME: If we ever call this with AVIs, need to have it call GetTexCoordRange and make that work
  414. Assert( !IsVideo() );
  415. float fLeft = 0;
  416. float fRight = 1;
  417. float fTop = 0;
  418. float fBottom = 1;
  419. if ( prcSubRect )
  420. {
  421. AdjustSubRect( this, frame, &fLeft, &fRight, &fTop, &fBottom, &iWidth, &iHeight, prcSubRect );
  422. }
  423. if ( giScissorTest && !Scissor( x, y, iWidth, iHeight, fLeft, fTop, fRight, fBottom ) )
  424. return;
  425. SetFrame( nRenderMode, frame );
  426. CMatRenderContextPtr pRenderContext( materials );
  427. IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, GetMaterial( nRenderMode ) );
  428. CMeshBuilder meshBuilder;
  429. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  430. float color[3];
  431. GetHUDSpriteColor( color );
  432. meshBuilder.Color3fv( color );
  433. meshBuilder.TexCoord2f( 0, fLeft, fTop );
  434. meshBuilder.Position3f( x, y, 0.0f );
  435. meshBuilder.AdvanceVertex();
  436. meshBuilder.Color3fv( color );
  437. meshBuilder.TexCoord2f( 0, fRight, fTop );
  438. meshBuilder.Position3f( x + iWidth, y, 0.0f );
  439. meshBuilder.AdvanceVertex();
  440. meshBuilder.Color3fv( color );
  441. meshBuilder.TexCoord2f( 0, fRight, fBottom );
  442. meshBuilder.Position3f( x + iWidth, y + iHeight, 0.0f );
  443. meshBuilder.AdvanceVertex();
  444. meshBuilder.Color3fv( color );
  445. meshBuilder.TexCoord2f( 0, fLeft, fBottom );
  446. meshBuilder.Position3f( x, y + iHeight, 0.0f );
  447. meshBuilder.AdvanceVertex();
  448. meshBuilder.End();
  449. pMesh->Draw();
  450. }