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.

592 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "stdafx.h"
  8. #include "const.h"
  9. #include "Sprite.h"
  10. #include "Material.h" // FIXME: we need to work only with IEditorTexture!
  11. #include "materialsystem/imaterial.h"
  12. #include "materialsystem/imaterialsystem.h"
  13. #include "Render3d.h"
  14. #include "camera.h"
  15. #include "tier1/utldict.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. class CSpriteDataCache
  19. {
  20. public:
  21. CMaterial *m_pMaterial;
  22. IMaterialVar *m_pFrameVar;
  23. IMaterialVar *m_pRenderModeVar;
  24. IMaterialVar *m_pOrientationVar;
  25. IMaterialVar *m_pOriginVar;
  26. int m_Width;
  27. int m_Height;
  28. bool m_bOriginVarFound;
  29. bool m_bOrientationVarFound;
  30. };
  31. CUtlDict<CSpriteDataCache*, int> g_SpriteDataCache;
  32. SpriteCache_t CSpriteCache::m_Cache[SPRITE_CACHE_SIZE];
  33. int CSpriteCache::m_nItems = 0;
  34. //-----------------------------------------------------------------------------
  35. // Purpose: Returns an instance of a particular studio model. If the model is
  36. // in the cache, a pointer to that model is returned. If not, a new one
  37. // is created and added to the cache.
  38. // Input : pszModelPath - Full path of the .MDL file.
  39. //-----------------------------------------------------------------------------
  40. CSpriteModel *CSpriteCache::CreateSprite(const char *pszSpritePath)
  41. {
  42. //
  43. // First look for the sprite in the cache. If it's there, increment the
  44. // reference count and return a pointer to the cached sprite.
  45. //
  46. for (int i = 0; i < m_nItems; i++)
  47. {
  48. if (!stricmp(pszSpritePath, m_Cache[i].pszPath))
  49. {
  50. m_Cache[i].nRefCount++;
  51. return(m_Cache[i].pSprite);
  52. }
  53. }
  54. //
  55. // If it isn't there, try to create one.
  56. //
  57. CSpriteModel *pSprite = new CSpriteModel;
  58. if (pSprite != NULL)
  59. {
  60. if (!pSprite->LoadSprite(pszSpritePath))
  61. {
  62. delete pSprite;
  63. pSprite = NULL;
  64. }
  65. }
  66. //
  67. // If we successfully created it, add it to the cache.
  68. //
  69. if (pSprite != NULL)
  70. {
  71. CSpriteCache::AddSprite(pSprite, pszSpritePath);
  72. }
  73. return(pSprite);
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Purpose: Adds the model to the cache, setting the reference count to one.
  77. // Input : pModel - Model to add to the cache.
  78. // pszSpritePath - The full path of the .MDL file, which is used as a
  79. // key in the sprite cache.
  80. // Output : Returns TRUE if the sprite was successfully added, FALSE if we ran
  81. // out of memory trying to add the sprite to the cache.
  82. //-----------------------------------------------------------------------------
  83. bool CSpriteCache::AddSprite(CSpriteModel *pSprite, const char *pszSpritePath)
  84. {
  85. //
  86. // Copy the sprite pointer.
  87. //
  88. m_Cache[m_nItems].pSprite = pSprite;
  89. //
  90. // Allocate space for and copy the model path.
  91. //
  92. m_Cache[m_nItems].pszPath = new char [strlen(pszSpritePath) + 1];
  93. if (m_Cache[m_nItems].pszPath != NULL)
  94. {
  95. strcpy(m_Cache[m_nItems].pszPath, pszSpritePath);
  96. }
  97. else
  98. {
  99. return(false);
  100. }
  101. m_Cache[m_nItems].nRefCount = 1;
  102. m_nItems++;
  103. return(true);
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Increments the reference count on a sprite in the cache. Called by
  107. // client code when a pointer to the sprite is copied, making that
  108. // reference independent.
  109. // Input : pModel - Sprite for which to increment the reference count.
  110. //-----------------------------------------------------------------------------
  111. void CSpriteCache::AddRef(CSpriteModel *pSprite)
  112. {
  113. for (int i = 0; i < m_nItems; i++)
  114. {
  115. if (m_Cache[i].pSprite == pSprite)
  116. {
  117. m_Cache[i].nRefCount++;
  118. return;
  119. }
  120. }
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose: Called by client code to release an instance of a model. If the
  124. // model's reference count is zero, the model is freed.
  125. // Input : pModel - Pointer to the model to release.
  126. //-----------------------------------------------------------------------------
  127. void CSpriteCache::Release(CSpriteModel *pSprite)
  128. {
  129. for (int i = 0; i < m_nItems; i++)
  130. {
  131. if (m_Cache[i].pSprite == pSprite)
  132. {
  133. m_Cache[i].nRefCount--;
  134. Assert(m_Cache[i].nRefCount >= 0);
  135. //
  136. // If this model is no longer referenced, free it and remove it
  137. // from the cache.
  138. //
  139. if (m_Cache[i].nRefCount <= 0)
  140. {
  141. //
  142. // Free the path, which was allocated by AddModel.
  143. //
  144. delete [] m_Cache[i].pszPath;
  145. delete m_Cache[i].pSprite;
  146. //
  147. // Decrement the item count and copy the last element in the cache over
  148. // this element.
  149. //
  150. m_nItems--;
  151. m_Cache[i].pSprite = m_Cache[m_nItems].pSprite;
  152. m_Cache[i].pszPath = m_Cache[m_nItems].pszPath;
  153. m_Cache[i].nRefCount = m_Cache[m_nItems].nRefCount;
  154. }
  155. break;
  156. }
  157. }
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: Constructor.
  161. //-----------------------------------------------------------------------------
  162. CSpriteModel::CSpriteModel(void) :
  163. m_pMaterial(0), m_NumFrames(-1), m_fScale(1.0), m_Origin(0,0,0), m_UL(0,0), m_LR(0,0), m_TexUL(0,1), m_TexLR(1,0), m_bInvert(false)
  164. {
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Destructor. Frees the sprite image and descriptor.
  168. //-----------------------------------------------------------------------------
  169. CSpriteModel::~CSpriteModel(void)
  170. {
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Sets the render mode
  174. //-----------------------------------------------------------------------------
  175. void CSpriteModel::SetRenderMode( const int mode )
  176. {
  177. if (m_pMaterial && m_pRenderModeVar)
  178. {
  179. if ( mode != m_pRenderModeVar->GetIntValue() )
  180. {
  181. m_pRenderModeVar->SetIntValue( mode );
  182. m_pMaterial->GetMaterial()->RecomputeStateSnapshots();
  183. }
  184. }
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. // Input : pEntity -
  189. // type -
  190. // forward -
  191. // right -
  192. // up -
  193. //-----------------------------------------------------------------------------
  194. void CSpriteModel::GetSpriteAxes(QAngle& Angles, int type, Vector& forward, Vector& right, Vector& up, Vector& ViewUp, Vector& ViewRight, Vector& ViewForward)
  195. {
  196. int i;
  197. float dot, angle, sr, cr;
  198. Vector tvec;
  199. // Automatically roll parallel sprites if requested
  200. if (Angles[2] != 0 && type == SPR_VP_PARALLEL )
  201. {
  202. type = SPR_VP_PARALLEL_ORIENTED;
  203. }
  204. switch (type)
  205. {
  206. case SPR_FACING_UPRIGHT:
  207. {
  208. // generate the sprite's axes, with vup straight up in worldspace, and
  209. // r_spritedesc.vright perpendicular to modelorg.
  210. // This will not work if the view direction is very close to straight up or
  211. // down, because the cross product will be between two nearly parallel
  212. // vectors and starts to approach an undefined state, so we don't draw if
  213. // the two vectors are less than 1 degree apart
  214. tvec[0] = -m_Origin[0];
  215. tvec[1] = -m_Origin[1];
  216. tvec[2] = -m_Origin[2];
  217. VectorNormalize (tvec);
  218. dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
  219. // r_spritedesc.vup is 0, 0, 1
  220. if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
  221. return;
  222. up[0] = 0;
  223. up[1] = 0;
  224. up[2] = 1;
  225. right[0] = tvec[1];
  226. // CrossProduct(r_spritedesc.vup, -modelorg,
  227. right[1] = -tvec[0];
  228. // r_spritedesc.vright)
  229. right[2] = 0;
  230. VectorNormalize (right);
  231. forward[0] = -right[1];
  232. forward[1] = right[0];
  233. forward[2] = 0;
  234. // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
  235. // r_spritedesc.vpn)
  236. break;
  237. }
  238. case SPR_VP_PARALLEL:
  239. {
  240. // generate the sprite's axes, completely parallel to the viewplane. There
  241. // are no problem situations, because the sprite is always in the same
  242. // position relative to the viewer
  243. for (i=0 ; i<3 ; i++)
  244. {
  245. up[i] = ViewUp[i];
  246. right[i] = ViewRight[i];
  247. forward[i] = ViewForward[i];
  248. }
  249. break;
  250. }
  251. case SPR_VP_PARALLEL_UPRIGHT:
  252. {
  253. // generate the sprite's axes, with vup straight up in worldspace, and
  254. // r_spritedesc.vright parallel to the viewplane.
  255. // This will not work if the view direction is very close to straight up or
  256. // down, because the cross product will be between two nearly parallel
  257. // vectors and starts to approach an undefined state, so we don't draw if
  258. // the two vectors are less than 1 degree apart
  259. dot = ViewForward[2]; // same as DotProduct (vpn, r_spritedesc.vup) because
  260. // r_spritedesc.vup is 0, 0, 1
  261. if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
  262. return;
  263. up[0] = 0;
  264. up[1] = 0;
  265. up[2] = 1;
  266. right[0] = ViewForward[1];
  267. right[1] = -ViewForward[0];
  268. right[2] = 0;
  269. VectorNormalize (right);
  270. forward[0] = -right[1];
  271. forward[1] = right[0];
  272. forward[2] = 0;
  273. break;
  274. }
  275. case SPR_ORIENTED:
  276. {
  277. // generate the sprite's axes, according to the sprite's world orientation
  278. AngleVectors(Angles, &forward, &right, &up);
  279. break;
  280. }
  281. case SPR_VP_PARALLEL_ORIENTED:
  282. {
  283. // generate the sprite's axes, parallel to the viewplane, but rotated in
  284. // that plane around the center according to the sprite entity's roll
  285. // angle. So vpn stays the same, but vright and vup rotate
  286. angle = Angles[ROLL] * (M_PI*2 / 360);
  287. sr = sin(angle);
  288. cr = cos(angle);
  289. for (i=0 ; i<3 ; i++)
  290. {
  291. forward[i] = ViewForward[i];
  292. right[i] = ViewRight[i] * cr + ViewUp[i] * sr;
  293. up[i] = ViewRight[i] * -sr + ViewUp[i] * cr;
  294. }
  295. break;
  296. }
  297. default:
  298. {
  299. //Sys_Error ("R_DrawSprite: Bad sprite type %d", type);
  300. break;
  301. }
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Sets the sprite's scale
  306. //-----------------------------------------------------------------------------
  307. void CSpriteModel::SetScale( const float fScale )
  308. {
  309. m_fScale = fScale;
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Sets the sprite's origin
  313. //-----------------------------------------------------------------------------
  314. void CSpriteModel::SetOrigin( const Vector &v )
  315. {
  316. m_Origin = v;
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Sets the sprite's origin
  320. //-----------------------------------------------------------------------------
  321. void CSpriteModel::GetOrigin( Vector &v )
  322. {
  323. v = m_Origin;
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Sets the sprite's vertical inversion
  327. //-----------------------------------------------------------------------------
  328. void CSpriteModel::SetInvert( const bool b )
  329. {
  330. m_bInvert = b;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Sets the Euler angles for the model.
  334. // Input : fAngles - A pointer to engine PITCH, YAW, and ROLL angles.
  335. //-----------------------------------------------------------------------------
  336. void CSpriteModel::SetAngles( const QAngle& pfAngles )
  337. {
  338. m_Angles[PITCH] = pfAngles[PITCH];
  339. m_Angles[YAW] = pfAngles[YAW];
  340. m_Angles[ROLL] = pfAngles[ROLL];
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Sets the material's primative type
  344. //-----------------------------------------------------------------------------
  345. void CSpriteModel::SetMaterialPrimitiveType( const MaterialPrimitiveType_t type )
  346. {
  347. m_MaterialPrimitiveType = type;
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Renders the sprite in 3D mode
  351. //-----------------------------------------------------------------------------
  352. void CSpriteModel::DrawSprite3D( CRender3D *pRender, unsigned char color[3] )
  353. {
  354. Vector corner, spritex, spritey, spritez;
  355. Vector ViewUp;
  356. Vector ViewRight;
  357. Vector ViewForward;
  358. pRender->GetViewUp( ViewUp );
  359. pRender->GetViewRight( ViewRight );
  360. pRender->GetViewForward( ViewForward );
  361. GetSpriteAxes(m_Angles, GetType(), spritez, spritex, spritey, ViewUp, ViewRight, ViewForward);
  362. Vector2D ul, lr;
  363. Vector2DMultiply( m_UL, m_fScale, ul );
  364. Vector2DMultiply( m_LR, m_fScale, lr );
  365. VectorMA( m_Origin, ul.x, spritex, corner );
  366. VectorMA( corner, lr.y, spritey, corner );
  367. spritex *= (lr.x - ul.x);
  368. spritey *= (ul.y - lr.y);
  369. Vector2D texul, texlr;
  370. texul.x = m_TexUL.x;
  371. texul.y = m_bInvert ? m_TexLR.y : m_TexUL.y;
  372. texlr.x = m_TexLR.x;
  373. texlr.y = m_bInvert ? m_TexUL.y : m_TexLR.y;
  374. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  375. pRender->BindTexture( m_pMaterial );
  376. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  377. CMeshBuilder meshBuilder;
  378. meshBuilder.Begin( pMesh, m_MaterialPrimitiveType, 4 );
  379. meshBuilder.Position3fv(corner.Base());
  380. meshBuilder.TexCoord2f(0, texul.x, texul.y);
  381. meshBuilder.Color3ub( color[0], color[1], color[2] );
  382. meshBuilder.AdvanceVertex();
  383. corner += spritey;
  384. meshBuilder.Position3fv(corner.Base());
  385. meshBuilder.TexCoord2f(0, texul.x, texlr.y);
  386. meshBuilder.Color3ub( color[0], color[1], color[2] );
  387. meshBuilder.AdvanceVertex();
  388. corner += spritex;
  389. meshBuilder.Position3fv(corner.Base());
  390. meshBuilder.TexCoord2f(0, texlr.x, texlr.y);
  391. meshBuilder.Color3ub( color[0], color[1], color[2] );
  392. meshBuilder.AdvanceVertex();
  393. corner -= spritey;
  394. meshBuilder.Position3fv(corner.Base());
  395. meshBuilder.TexCoord2f(0, texlr.x, texul.y);
  396. meshBuilder.Color3ub( color[0], color[1], color[2] );
  397. meshBuilder.AdvanceVertex();
  398. meshBuilder.End();
  399. pMesh->Draw();
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Binds a sprite
  403. //-----------------------------------------------------------------------------
  404. void CSpriteModel::Bind( CRender* pRender, int frame )
  405. {
  406. if (m_pMaterial && m_pFrameVar)
  407. {
  408. m_pFrameVar->SetIntValue( frame );
  409. pRender->BindTexture( m_pMaterial );
  410. }
  411. }
  412. CSpriteDataCache* LookupSpriteDataCache( const char *pSpritePath )
  413. {
  414. char filename[MAX_PATH];
  415. V_strncpy( filename, pSpritePath, sizeof( filename ) );
  416. V_FixSlashes( filename );
  417. CSpriteDataCache *pData;
  418. int i = g_SpriteDataCache.Find( filename );
  419. if ( i == g_SpriteDataCache.InvalidIndex() )
  420. {
  421. pData = new CSpriteDataCache;
  422. memset( pData, 0, sizeof( *pData ) );
  423. g_SpriteDataCache.Insert( filename, pData );
  424. pData->m_pMaterial = CMaterial::CreateMaterial( filename, true );
  425. if ( pData->m_pMaterial && pData->m_pMaterial->GetMaterial() )
  426. {
  427. pData->m_Width = pData->m_pMaterial->GetWidth();
  428. pData->m_Height = pData->m_pMaterial->GetHeight();
  429. pData->m_pFrameVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteFrame", 0 );
  430. pData->m_pRenderModeVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriterendermode", 0 );
  431. pData->m_pOrientationVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteOrientation", &pData->m_bOrientationVarFound, false );
  432. pData->m_pOriginVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteorigin", &pData->m_bOriginVarFound );
  433. }
  434. }
  435. else
  436. {
  437. pData = g_SpriteDataCache[i];
  438. }
  439. return pData;
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: Loads a sprite material.
  443. // Input : pszSpritePath -
  444. // Output : Returns true on success, false on failure.
  445. //-----------------------------------------------------------------------------
  446. bool CSpriteModel::LoadSprite(const char *pszSpritePath)
  447. {
  448. CSpriteDataCache *pCache = LookupSpriteDataCache( pszSpritePath );
  449. m_pMaterial = pCache->m_pMaterial;
  450. if( m_pMaterial && m_pMaterial->GetMaterial() )
  451. {
  452. m_Width = pCache->m_Width;
  453. m_Height = pCache->m_Height;
  454. // FIXME: m_NumFrames = m_pMaterial->GetMaterial()->GetNumAnimationFrames();
  455. m_pFrameVar = pCache->m_pFrameVar;
  456. m_pRenderModeVar = pCache->m_pRenderModeVar;
  457. IMaterialVar *orientationVar = pCache->m_pOrientationVar;
  458. bool found = pCache->m_bOrientationVarFound;
  459. if( found )
  460. {
  461. m_Type = orientationVar->GetIntValue();
  462. }
  463. else
  464. {
  465. m_Type = SPR_VP_PARALLEL_UPRIGHT;
  466. }
  467. IMaterialVar *pOriginVar = pCache->m_pOriginVar;
  468. Vector origin;
  469. found = pCache->m_bOriginVarFound;
  470. if( !found || ( pOriginVar->GetType() != MATERIAL_VAR_TYPE_VECTOR ) )
  471. {
  472. origin[0] = -m_Width * 0.5f;
  473. origin[1] = m_Height * 0.5f;
  474. }
  475. else
  476. {
  477. Vector originVarValue;
  478. pOriginVar->GetVecValue( originVarValue.Base(), 3);
  479. origin[0] = -m_Width * originVarValue[0];
  480. origin[1] = m_Height * originVarValue[1];
  481. }
  482. m_UL.y = origin[1];
  483. m_LR.y = origin[1] - m_Height;
  484. m_UL.x = origin[0];
  485. m_LR.x = m_Width + origin[0];
  486. return true;
  487. }
  488. else
  489. {
  490. return false;
  491. }
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Kind of a hack...
  495. //-----------------------------------------------------------------------------
  496. int CSpriteModel::GetFrameCount()
  497. {
  498. // FIXME: Figure out the correct time to cache in this info
  499. if ((m_NumFrames < 0) && m_pMaterial)
  500. {
  501. m_NumFrames = m_pMaterial->GetMaterial()->GetNumAnimationFrames();
  502. }
  503. return (m_NumFrames < 0) ? 0 : m_NumFrames;
  504. }