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.

2662 lines
79 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "render_pch.h"
  7. #include "r_decal.h"
  8. #include "client.h"
  9. #include <materialsystem/imaterialsystemhardwareconfig.h>
  10. #include "decal.h"
  11. #include "tier0/vprof.h"
  12. #include "materialsystem/materialsystem_config.h"
  13. #include "icliententity.h"
  14. #include "icliententitylist.h"
  15. #include "tier2/tier2.h"
  16. #include "tier1/callqueue.h"
  17. #include "tier1/memstack.h"
  18. #include "mempool.h"
  19. #include "vstdlib/random.h"
  20. #include "getintersectingsurfaces_struct.h"
  21. #include "surfinfo.h"
  22. #include "collisionutils.h"
  23. #include "engine/decal_flags.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define DECAL_DISTANCE 4
  27. // Empirically determined constants for minimizing overalpping decals
  28. ConVar r_decal_overlap_count("r_decal_overlap_count", "3");
  29. ConVar r_decal_overlap_area("r_decal_overlap_area", "0.4");
  30. // if a new decal covers more than this many old decals, retire until this count remains
  31. ConVar r_decal_cover_count("r_decal_cover_count", "4" );
  32. static unsigned int s_DecalScaleVarCache = 0;
  33. static unsigned int s_DecalScaleVariationVarCache = 0;
  34. static unsigned int s_DecalFadeVarCache = 0;
  35. static unsigned int s_DecalFadeTimeVarCache = 0;
  36. static unsigned int s_DecalSecondPassVarCache = 0;
  37. inline int R_ConvertToPrivateDecalFlags( int nPublicDecalFlags )
  38. {
  39. int nPrivateFlags = 0;
  40. if ( ( nPublicDecalFlags & EDF_PLAYERSPRAY ) != 0 ) { nPublicDecalFlags &= ~EDF_PLAYERSPRAY; nPrivateFlags |= FDECAL_PLAYERSPRAY; }
  41. if ( ( nPublicDecalFlags & EDF_IMMEDIATECLEANUP ) != 0 ) { nPublicDecalFlags &= ~EDF_IMMEDIATECLEANUP; nPrivateFlags |= FDECAL_IMMEDIATECLEANUP; }
  42. // If this hits, we are missing conversion here.
  43. Assert( nPublicDecalFlags == 0 );
  44. return nPrivateFlags;
  45. }
  46. // This structure contains the information used to create new decals
  47. struct decalinfo_t
  48. {
  49. Vector m_Position; // world coordinates of the decal center
  50. Vector m_SAxis; // the s axis for the decal in world coordinates
  51. model_t* m_pModel; // the model the decal is going to be applied in
  52. worldbrushdata_t *m_pBrush; // The shared brush data for this model
  53. IMaterial* m_pMaterial; // The decal material
  54. float m_Size; // Size of the decal (in world coords)
  55. int m_Flags;
  56. int m_Entity; // Entity the decal is applied to.
  57. float m_scale;
  58. float m_flFadeDuration;
  59. float m_flFadeStartTime;
  60. int m_decalWidth;
  61. int m_decalHeight;
  62. color32 m_Color;
  63. Vector m_Basis[3];
  64. void *m_pUserData;
  65. const Vector *m_pNormal;
  66. CUtlVector<SurfaceHandle_t> m_aApplySurfs;
  67. };
  68. typedef struct
  69. {
  70. CDecalVert decalVert[4];
  71. } decalcache_t;
  72. // UNDONE: Compress this??? 256K here?
  73. static CClassMemoryPool<decal_t> g_DecalAllocator( 128 ); // 128 decals per block.
  74. static int g_nDynamicDecals = 0;
  75. static int g_nStaticDecals = 0;
  76. static int g_iLastReplacedDynamic = -1;
  77. CUtlVector<decal_t*> s_aDecalPool;
  78. const int DECALCACHE_ENTRY_COUNT = 1024;
  79. const int INVALID_CACHE_ENTRY = 0xFFFF;
  80. class ALIGN16 CDecalVertCache
  81. {
  82. enum decalindex_ordinal
  83. {
  84. DECAL_INDEX = 0, // set this and use this to free the whole decal's list on compact
  85. NEXT_VERT_BLOCK_INDEX = 1,
  86. IS_FREE_INDEX = 2,
  87. FRAME_COUNT_INDEX = 3,
  88. };
  89. public:
  90. void Init();
  91. CDecalVert *GetCachedVerts( decal_t *pDecal );
  92. void FreeCachedVerts( decal_t *pDecal );
  93. void StoreVertsInCache( decal_t *pDecal, CDecalVert *pList );
  94. private:
  95. inline int GetIndex( decalcache_t *pBlock, decalindex_ordinal index )
  96. {
  97. return pBlock->decalVert[index].m_decalIndex;
  98. }
  99. inline void SetIndex( decalcache_t *pBlock, decalindex_ordinal index, int value )
  100. {
  101. pBlock->decalVert[index].m_decalIndex = value;
  102. }
  103. inline void SetNext( int iCur, int iNext )
  104. {
  105. SetIndex(m_cache + iCur, NEXT_VERT_BLOCK_INDEX, iNext);
  106. }
  107. inline void SetFree( int iBlock, bool bFree )
  108. {
  109. SetIndex( m_cache + iBlock, IS_FREE_INDEX, bFree );
  110. }
  111. inline bool IsFree(int iBlock)
  112. {
  113. return GetIndex(m_cache+iBlock, IS_FREE_INDEX) != 0;
  114. }
  115. decalcache_t *NextBlock( decalcache_t *pCache );
  116. void FreeBlock(int cacheIndex);
  117. // search for blocks not used this frame and free them
  118. // this way we don't manage an LRU but get similar behavior
  119. void FindFreeBlocks( int blockCount );
  120. int AllocBlock();
  121. int AllocBlocks( int blockCount );
  122. ALIGN16 decalcache_t m_cache[DECALCACHE_ENTRY_COUNT];
  123. int m_freeBlockCount;
  124. int m_firstFree;
  125. int m_frameBlocks;
  126. int m_lastFrameCount;
  127. int m_freeTestIndex;
  128. };
  129. static CDecalVertCache ALIGN16 g_DecalVertCache;
  130. //
  131. // <vitaliy> (Hi John)
  132. // Destroying decals is an ancient system that we reused for player graffiti decal previews.
  133. // There is a new system for single-frame decals that are scheduled for immediate cleanup
  134. // on the same frame that they are inserted. Sometimes these decals don't finish cleaning
  135. // themselves up until they render for the second frame. That is okay for rendering purposes,
  136. // but it is a problem if they end up in the cleanup list twice because it can create circular
  137. // loops in the list or accessing free'd memory. The flags are uint16 and all used up at this
  138. // point, so we cannot add a flag whether the decal is in the cleanup list already.
  139. // In order not to increase the size of the decal structure I am making the cleanup list to
  140. // be terminated by a non-NULL known not valid pointer. This allows all the code to know
  141. // whether a specific decal_t object is already in the destroy list by checking its pDestroyList
  142. // member for being non-NULL, and only the per-frame destroy list walking code needs to be aware
  143. // of stopping at the non-NULL special terminator entry.
  144. // The "easiest" spot to recreate a circular deadlock was on overpass T-side of monster in the
  145. // left corner of monster wall and slanted wall to just spam the +spray_menu key and have a
  146. // conditional breakpoint in R_DecalAddToDestroyList with condition pDecal == s_pDecalDestroyList.
  147. // That condition will essentially create a loop of decal pointing to itself as next.
  148. //
  149. static decal_t * const kpDecalDestroyListNonNullTerminator = reinterpret_cast< decal_t * >( 1 );
  150. static decal_t *s_pDecalDestroyList = kpDecalDestroyListNonNullTerminator;
  151. int g_nMaxDecals = 0;
  152. //
  153. // ConVars that control distance-based decal scaling
  154. //
  155. static ConVar r_dscale_nearscale( "r_dscale_nearscale", "1", FCVAR_CHEAT );
  156. static ConVar r_dscale_neardist( "r_dscale_neardist", "100", FCVAR_CHEAT );
  157. static ConVar r_dscale_farscale( "r_dscale_farscale", "4", FCVAR_CHEAT );
  158. static ConVar r_dscale_fardist( "r_dscale_fardist", "2000", FCVAR_CHEAT );
  159. static ConVar r_dscale_basefov( "r_dscale_basefov", "90", FCVAR_CHEAT );
  160. // This makes sure all the decals got freed before the engine is shutdown.
  161. static class CDecalChecker
  162. {
  163. public:
  164. ~CDecalChecker()
  165. {
  166. Assert( g_nDynamicDecals == 0 );
  167. }
  168. } g_DecalChecker;
  169. // used for decal LOD
  170. VMatrix g_BrushToWorldMatrix;
  171. static CUtlVector<SurfaceHandle_t> s_DecalSurfaces[ MAX_MAT_SORT_GROUPS + 1 ];
  172. static ConVar r_drawdecals( "r_drawdecals", "1", FCVAR_CHEAT, "Render decals." );
  173. static ConVar r_drawbatchdecals( "r_drawbatchdecals", "1", 0, "Render decals batched." );
  174. static void R_DecalCreate( decalinfo_t* pDecalInfo, SurfaceHandle_t surfID, float x, float y, bool bForceForDisplacement );
  175. void R_DecalShoot( int textureIndex, int entity, const model_t *model, const Vector &position, const float *saxis, int flags, const color32 &rgbaColor, const Vector *pNormal );
  176. static bool R_DecalUnProject( decal_t *pdecal, decallist_t *entry);
  177. void R_DecalSortInit( void );
  178. bool DecalUpdate( decal_t* pDecal );
  179. void R_DecalAddToDestroyList( decal_t *pDecal );
  180. inline bool ShouldSkipDecalRender( decal_t *pDecal )
  181. {
  182. if ( !pDecal )
  183. return true;
  184. const bool cbRequiresDynamicUpdate = ( pDecal->flags & FDECAL_DYNAMIC ) && !( pDecal->flags & FDECAL_HASUPDATED );
  185. const bool cbRequiresCleanupUpdate = ( pDecal->flags & FDECAL_IMMEDIATECLEANUP ) != 0 ;
  186. // Add the decal to the list of decals to be destroyed if need be.
  187. if ( cbRequiresDynamicUpdate || cbRequiresCleanupUpdate )
  188. {
  189. if ( cbRequiresDynamicUpdate )
  190. pDecal->flags |= FDECAL_HASUPDATED;
  191. if ( DecalUpdate( pDecal ) )
  192. {
  193. R_DecalAddToDestroyList( pDecal );
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. static void r_printdecalinfo_f()
  200. {
  201. int nPermanent = 0;
  202. int nDynamic = 0;
  203. for ( int i=0; i < g_nMaxDecals; i++ )
  204. {
  205. if ( s_aDecalPool[i] )
  206. {
  207. if ( s_aDecalPool[i]->flags & FDECAL_PERMANENT )
  208. ++nPermanent;
  209. else
  210. ++nDynamic;
  211. }
  212. }
  213. Assert( nDynamic == g_nDynamicDecals );
  214. Msg( "%d decals: %d permanent, %d dynamic\nr_decals: %d\n", nPermanent+nDynamic, nPermanent, nDynamic, r_decals.GetInt() );
  215. }
  216. static ConCommand r_printdecalinfo( "r_printdecalinfo", r_printdecalinfo_f );
  217. void CDecalVertCache::StoreVertsInCache( decal_t *pDecal, CDecalVert *pList )
  218. {
  219. int vertCount = pDecal->clippedVertCount;
  220. int blockCount = (vertCount+3)>>2;
  221. FindFreeBlocks(blockCount);
  222. if ( blockCount > m_freeBlockCount )
  223. return;
  224. int cacheHandle = AllocBlocks(blockCount);
  225. pDecal->cacheHandle = cacheHandle;
  226. decalcache_t *pCache = &m_cache[cacheHandle];
  227. while ( blockCount )
  228. {
  229. Assert(GetIndex(pCache, DECAL_INDEX) == -1 );
  230. // don't memcpy here it overwrites the indices we're storing in the m_decalIndex data
  231. for ( int i = 0; i < 4; i++ )
  232. {
  233. pCache->decalVert[i].m_vPos = pList[i].m_vPos;
  234. pCache->decalVert[i].m_ctCoords = pList[i].m_ctCoords;
  235. pCache->decalVert[i].m_cLMCoords = pList[i].m_cLMCoords;
  236. }
  237. pList += 4;
  238. blockCount--;
  239. SetIndex( pCache, DECAL_INDEX, pDecal->m_iDecalPool );
  240. SetIndex( pCache, FRAME_COUNT_INDEX, r_framecount );
  241. pCache = NextBlock(pCache);
  242. }
  243. }
  244. void CDecalVertCache::FreeCachedVerts( decal_t *pDecal )
  245. {
  246. // walk the list
  247. int nextIndex = INVALID_CACHE_ENTRY;
  248. for ( int cacheHandle = pDecal->cacheHandle; cacheHandle != INVALID_CACHE_ENTRY; cacheHandle = nextIndex )
  249. {
  250. decalcache_t *pCache = m_cache + cacheHandle;
  251. nextIndex = GetIndex(pCache, NEXT_VERT_BLOCK_INDEX);
  252. Assert(GetIndex(pCache,DECAL_INDEX)==pDecal->m_iDecalPool);
  253. FreeBlock(cacheHandle);
  254. }
  255. pDecal->cacheHandle = INVALID_CACHE_ENTRY;
  256. pDecal->clippedVertCount = 0;
  257. }
  258. CDecalVert *CDecalVertCache::GetCachedVerts( decal_t *pDecal )
  259. {
  260. int cacheHandle = pDecal->cacheHandle;
  261. // track blocks used this frame to avoid thrashing
  262. if ( r_framecount != m_lastFrameCount )
  263. {
  264. m_frameBlocks = 0;
  265. m_lastFrameCount = r_framecount;
  266. }
  267. if ( cacheHandle == INVALID_CACHE_ENTRY )
  268. return NULL;
  269. decalcache_t *pCache = &m_cache[cacheHandle];
  270. for ( int i = cacheHandle; i != INVALID_CACHE_ENTRY; i = GetIndex(&m_cache[i], NEXT_VERT_BLOCK_INDEX) )
  271. {
  272. SetIndex( pCache, FRAME_COUNT_INDEX, r_framecount );
  273. Assert( GetIndex(pCache, DECAL_INDEX) == pDecal->m_iDecalPool);
  274. }
  275. int vertCount = pDecal->clippedVertCount;
  276. int blockCount = (vertCount+3)>>2;
  277. m_frameBlocks += blockCount;
  278. // Make linked vert lists contiguous by copying to the clip buffer
  279. if ( blockCount > 1 )
  280. {
  281. int indexOut = 0;
  282. while ( blockCount )
  283. {
  284. Plat_FastMemcpy( &g_DecalClipVerts[indexOut], pCache, sizeof(*pCache) );
  285. indexOut += 4;
  286. blockCount --;
  287. pCache = NextBlock(pCache);
  288. }
  289. return g_DecalClipVerts;
  290. }
  291. // only one block, no need to copy
  292. return pCache->decalVert;
  293. }
  294. void CDecalVertCache::Init()
  295. {
  296. m_firstFree = 0;
  297. m_freeTestIndex = 0;
  298. for ( int i = 0; i < DECALCACHE_ENTRY_COUNT; i++ )
  299. {
  300. SetNext( i, i+1 );
  301. SetIndex( &m_cache[i], DECAL_INDEX, -1 );
  302. SetFree( i, true );
  303. }
  304. SetNext( DECALCACHE_ENTRY_COUNT-1, INVALID_CACHE_ENTRY );
  305. m_freeBlockCount = DECALCACHE_ENTRY_COUNT;
  306. }
  307. decalcache_t *CDecalVertCache::NextBlock( decalcache_t *pCache )
  308. {
  309. int nextIndex = GetIndex(pCache, NEXT_VERT_BLOCK_INDEX);
  310. if ( nextIndex == INVALID_CACHE_ENTRY )
  311. return NULL;
  312. return m_cache + nextIndex;
  313. }
  314. void CDecalVertCache::FreeBlock(int cacheIndex)
  315. {
  316. SetFree( cacheIndex, true );
  317. SetNext( cacheIndex, m_firstFree );
  318. SetIndex( &m_cache[cacheIndex], DECAL_INDEX, -1 );
  319. m_firstFree = cacheIndex;
  320. m_freeBlockCount++;
  321. }
  322. // search for blocks not used this frame and free them
  323. // this way we don't manage an LRU but get similar behavior
  324. void CDecalVertCache::FindFreeBlocks( int blockCount )
  325. {
  326. if ( blockCount <= m_freeBlockCount )
  327. return;
  328. int possibleFree = DECALCACHE_ENTRY_COUNT - m_frameBlocks;
  329. if ( blockCount > possibleFree )
  330. return;
  331. // limit the search for performance to 16 entries
  332. int lastTest = (m_freeTestIndex + 16) & (DECALCACHE_ENTRY_COUNT-1);
  333. for ( ; m_freeTestIndex != lastTest; m_freeTestIndex = (m_freeTestIndex+1)&(DECALCACHE_ENTRY_COUNT-1) )
  334. {
  335. if ( !IsFree(m_freeTestIndex) )
  336. {
  337. int lastFrame = GetIndex(&m_cache[m_freeTestIndex], FRAME_COUNT_INDEX);
  338. if ( (r_framecount - lastFrame) > 1 )
  339. {
  340. int iDecal = GetIndex(&m_cache[m_freeTestIndex], DECAL_INDEX);
  341. FreeCachedVerts(s_aDecalPool[iDecal]);
  342. }
  343. }
  344. if ( m_freeBlockCount >= blockCount )
  345. break;
  346. }
  347. }
  348. int CDecalVertCache::AllocBlock()
  349. {
  350. if ( !m_freeBlockCount )
  351. return INVALID_CACHE_ENTRY;
  352. Assert(IsFree(m_firstFree));
  353. int nextFree = GetIndex(m_cache+m_firstFree, NEXT_VERT_BLOCK_INDEX );
  354. int cacheIndex = m_firstFree;
  355. SetFree( cacheIndex, false );
  356. m_firstFree = nextFree;
  357. m_freeBlockCount--;
  358. return cacheIndex;
  359. }
  360. int CDecalVertCache::AllocBlocks( int blockCount )
  361. {
  362. if ( blockCount > m_freeBlockCount )
  363. return INVALID_CACHE_ENTRY;
  364. int firstBlock = AllocBlock();
  365. Assert(firstBlock!=INVALID_CACHE_ENTRY);
  366. int blockHandle = firstBlock;
  367. for ( int i = 1; i < blockCount; i++ )
  368. {
  369. int nextBlock = AllocBlock();
  370. Assert(nextBlock!=INVALID_CACHE_ENTRY);
  371. SetIndex( m_cache + blockHandle, NEXT_VERT_BLOCK_INDEX, nextBlock );
  372. blockHandle = nextBlock;
  373. }
  374. SetIndex( m_cache + blockHandle, NEXT_VERT_BLOCK_INDEX, INVALID_CACHE_ENTRY );
  375. return firstBlock;
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Computes the offset for a decal polygon
  379. //-----------------------------------------------------------------------------
  380. float ComputeDecalLightmapOffset( SurfaceHandle_t surfID )
  381. {
  382. float flOffset;
  383. if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  384. {
  385. int nWidth, nHeight;
  386. materials->GetLightmapPageSize(
  387. SortInfoToLightmapPage( MSurf_MaterialSortID( surfID ) ), &nWidth, &nHeight );
  388. int nXExtent = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  389. flOffset = ( nWidth != 0 ) ? (float)nXExtent / (float)nWidth : 0.0f;
  390. }
  391. else
  392. {
  393. flOffset = 0.0f;
  394. }
  395. return flOffset;
  396. }
  397. static VertexFormat_t GetUncompressedFormat( const IMaterial * pMaterial )
  398. {
  399. // FIXME: IMaterial::GetVertexFormat() should do this stripping (add a separate 'SupportsCompression' accessor)
  400. return ( pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED );
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Draws a decal polygon
  404. //-----------------------------------------------------------------------------
  405. void Shader_DecalDrawPoly( CDecalVert *v, IMaterial *pMaterial, SurfaceHandle_t surfID, int vertCount, decal_t *pdecal, float flFade )
  406. {
  407. #ifndef DEDICATED
  408. int vertexFormat = 0;
  409. CMatRenderContextPtr pRenderContext( materials );
  410. #ifdef USE_CONVARS
  411. if( ShouldDrawInWireFrameMode() || r_drawdecals.GetInt() == 2 )
  412. {
  413. pRenderContext->Bind( g_materialDecalWireframe );
  414. }
  415. else
  416. #endif
  417. {
  418. Assert( MSurf_MaterialSortID( surfID ) >= 0 &&
  419. MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  420. pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID );
  421. pRenderContext->Bind( pMaterial, pdecal->userdata );
  422. vertexFormat = GetUncompressedFormat( pMaterial );
  423. }
  424. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  425. CMeshBuilder meshBuilder;
  426. meshBuilder.Begin( pMesh, MATERIAL_POLYGON, vertCount );
  427. byte color[4] = {pdecal->color.r,pdecal->color.g,pdecal->color.b,pdecal->color.a};
  428. if ( flFade != 1.0f )
  429. {
  430. color[3] = (byte)( color[3] * flFade );
  431. }
  432. // Deal with fading out... (should this be done in the shader?)
  433. // Note that we do it with per-vertex color even though the translucency
  434. // is constant so as to not change any rendering state (like the constant
  435. // alpha value)
  436. if ( pdecal->flags & FDECAL_DYNAMIC )
  437. {
  438. float fadeval;
  439. // Negative fadeDuration value means to fade in
  440. if (pdecal->fadeDuration < 0)
  441. {
  442. fadeval = - (GetBaseLocalClient().GetTime() - pdecal->fadeStartTime) / pdecal->fadeDuration;
  443. }
  444. else
  445. {
  446. fadeval = 1.0 - (GetBaseLocalClient().GetTime() - pdecal->fadeStartTime) / pdecal->fadeDuration;
  447. }
  448. fadeval = clamp( fadeval, 0.0f, 1.0f );
  449. color[3] = (byte) (color[3] * fadeval);
  450. }
  451. Vector normal(0,0,1), tangentS(1,0,0), tangentT(0,1,0);
  452. if ( vertexFormat & (VERTEX_NORMAL|VERTEX_TANGENT_SPACE) )
  453. {
  454. normal = MSurf_Plane( surfID ).normal;
  455. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  456. {
  457. Vector tVect;
  458. bool negate = TangentSpaceSurfaceSetup( surfID, tVect );
  459. TangentSpaceComputeBasis( tangentS, tangentT, normal, tVect, negate );
  460. }
  461. }
  462. float flOffset = pdecal->lightmapOffset;
  463. for( int i = 0; i < vertCount; i++, v++ )
  464. {
  465. meshBuilder.Position3f( VectorExpand( v->m_vPos ) );
  466. if ( vertexFormat & VERTEX_NORMAL )
  467. {
  468. meshBuilder.Normal3fv( normal.Base() );
  469. }
  470. meshBuilder.Color4ubv( color );
  471. // Check to see if we are in a material page.
  472. meshBuilder.TexCoord2f( 0, Vector2DExpand( v->m_ctCoords ) );
  473. meshBuilder.TexCoord2f( 1, Vector2DExpand( v->m_cLMCoords ) );
  474. meshBuilder.TexCoord1f( 2, flOffset );
  475. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  476. {
  477. meshBuilder.TangentS3fv( tangentS.Base() );
  478. meshBuilder.TangentT3fv( tangentT.Base() );
  479. }
  480. meshBuilder.AdvanceVertex();
  481. }
  482. meshBuilder.End();
  483. pMesh->Draw();
  484. #endif
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Gets the decal material and radius based on the decal index
  488. //-----------------------------------------------------------------------------
  489. void R_DecalGetMaterialAndSize( int decalIndex, IMaterial*& pDecalMaterial, float& w, float& h )
  490. {
  491. pDecalMaterial = Draw_DecalMaterial( decalIndex );
  492. if (!pDecalMaterial)
  493. return;
  494. float scale = 1.0f;
  495. // Compute scale of surface
  496. // FIXME: cache this?
  497. IMaterialVar *pDecalScaleVar = pDecalMaterial->FindVarFast( "$decalScale", &s_DecalScaleVarCache );
  498. if ( pDecalScaleVar )
  499. {
  500. scale = pDecalScaleVar->GetFloatValue();
  501. }
  502. pDecalScaleVar = pDecalMaterial->FindVarFast( "$decalScaleVariation", &s_DecalScaleVariationVarCache );
  503. if ( pDecalScaleVar )
  504. {
  505. float variation = pDecalScaleVar->GetFloatValue();
  506. variation = clamp( variation, 0.0f, 0.99f );
  507. variation = RandomFloat( -variation, variation );
  508. scale += variation * scale;
  509. }
  510. // compute the decal dimensions in world space
  511. w = pDecalMaterial->GetMappingWidth() * scale;
  512. h = pDecalMaterial->GetMappingHeight() * scale;
  513. }
  514. #ifndef DEDICATED
  515. static inline decal_t *MSurf_DecalPointer( SurfaceHandle_t surfID )
  516. {
  517. WorldDecalHandle_t handle = MSurf_Decals(surfID );
  518. if ( handle == WORLD_DECAL_HANDLE_INVALID )
  519. return NULL;
  520. return s_aDecalPool[handle];
  521. }
  522. static WorldDecalHandle_t DecalToHandle( decal_t *pDecal )
  523. {
  524. if ( !pDecal )
  525. return WORLD_DECAL_HANDLE_INVALID;
  526. int decalIndex = pDecal->m_iDecalPool;
  527. Assert( decalIndex >= 0 && decalIndex < g_nMaxDecals );
  528. return static_cast<WorldDecalHandle_t> (decalIndex);
  529. }
  530. // Init the decal pool
  531. void R_DecalInit( void )
  532. {
  533. g_nMaxDecals = r_decals.GetInt();
  534. g_nMaxDecals = MAX(64, g_nMaxDecals);
  535. Assert( g_DecalAllocator.Count() == 0 );
  536. g_nDynamicDecals = 0;
  537. g_nStaticDecals = 0;
  538. g_iLastReplacedDynamic = -1;
  539. s_aDecalPool.Purge();
  540. s_aDecalPool.SetSize( g_nMaxDecals );
  541. int i;
  542. // Traverse all surfaces of map and throw away current decals
  543. //
  544. // sort the surfaces into the sort arrays
  545. if ( host_state.worldbrush )
  546. {
  547. for( i = 0; i < host_state.worldbrush->numsurfaces; i++ )
  548. {
  549. SurfaceHandle_t surfID = SurfaceHandleFromIndex(i);
  550. MSurf_Decals( surfID ) = WORLD_DECAL_HANDLE_INVALID;
  551. }
  552. }
  553. for( int iDecal = 0; iDecal < g_nMaxDecals; ++iDecal )
  554. {
  555. s_aDecalPool[iDecal] = NULL;
  556. }
  557. g_DecalVertCache.Init();
  558. R_DecalSortInit();
  559. }
  560. void R_DecalTerm( worldbrushdata_t *pBrushData, bool term_permanent_decals, bool term_player_decals )
  561. {
  562. if( !pBrushData )
  563. return;
  564. for( int i = 0; i < pBrushData->numsurfaces; i++ )
  565. {
  566. decal_t *pNext;
  567. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, pBrushData );
  568. for( decal_t *pDecal=MSurf_DecalPointer( surfID ); pDecal; pDecal=pNext )
  569. {
  570. pNext = pDecal->pnext;
  571. /* if( pDecal->flags & FDECAL_PLAYERSPRAY )
  572. {
  573. extern IBaseClientDLL *g_ClientDLL;
  574. if ( term_player_decals || g_ClientDLL->CanRetirePlayerDecal( pDecal->userdata ) )
  575. R_DecalUnlink( pDecal, pBrushData ); // safe to unlink
  576. }
  577. else */
  578. if ( term_permanent_decals || !(pDecal->flags & FDECAL_PERMANENT) )
  579. {
  580. R_DecalUnlink( pDecal, pBrushData );
  581. }
  582. }
  583. if ( term_permanent_decals && term_player_decals )
  584. {
  585. Assert( MSurf_DecalPointer( surfID ) == NULL );
  586. }
  587. }
  588. }
  589. void R_DecalTermNew( worldbrushdata_t *pBrushData, int nTick )
  590. {
  591. if ( !pBrushData )
  592. return;
  593. for ( int i = 0; i < pBrushData->numsurfaces; i++ )
  594. {
  595. decal_t *pNext;
  596. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i, pBrushData );
  597. for ( decal_t *pDecal = MSurf_DecalPointer( surfID ); pDecal; pDecal = pNext )
  598. {
  599. pNext = pDecal->pnext;
  600. if ( !( pDecal->flags & ( FDECAL_PERMANENT | FDECAL_PLAYERSPRAY ) ) && pDecal->m_nTickCreated > nTick )
  601. {
  602. R_DecalUnlink( pDecal, pBrushData );
  603. }
  604. }
  605. }
  606. }
  607. void R_DecalTermAll()
  608. {
  609. for ( int i = 0; i<s_aDecalPool.Count(); i++ )
  610. {
  611. R_DecalUnlink( s_aDecalPool[i], host_state.worldbrush );
  612. }
  613. }
  614. static int R_DecalIndex( decal_t *pdecal )
  615. {
  616. return pdecal->m_iDecalPool;
  617. }
  618. // Release the cache entry for this decal
  619. static void R_DecalCacheClear( decal_t *pdecal )
  620. {
  621. g_DecalVertCache.FreeCachedVerts( pdecal );
  622. }
  623. void R_DecalFlushDestroyList( bool bImmediateCleanup )
  624. {
  625. decal_t* pNewDestroyList = kpDecalDestroyListNonNullTerminator;
  626. decal_t *pDecal = s_pDecalDestroyList;
  627. while ( pDecal != kpDecalDestroyListNonNullTerminator )
  628. {
  629. decal_t *pNext = pDecal->pDestroyList;
  630. if ( ( 0 == ( pDecal->flags & FDECAL_IMMEDIATECLEANUP ) ) || bImmediateCleanup )
  631. {
  632. R_DecalUnlink( pDecal, host_state.worldbrush );
  633. }
  634. else
  635. {
  636. // These are only cleaned up once per frame, and we're not there, so push this out
  637. // for later.
  638. pDecal->pDestroyList = pNewDestroyList;
  639. pNewDestroyList = pDecal;
  640. }
  641. pDecal = pNext;
  642. }
  643. s_pDecalDestroyList = pNewDestroyList;
  644. }
  645. void R_DecalAddToDestroyList( decal_t *pDecal )
  646. {
  647. if ( !pDecal->pDestroyList )
  648. {
  649. pDecal->pDestroyList = s_pDecalDestroyList;
  650. s_pDecalDestroyList = pDecal;
  651. }
  652. }
  653. // Unlink pdecal from any surface it's attached to
  654. void R_DecalUnlink( decal_t *pdecal, worldbrushdata_t *pData )
  655. {
  656. if ( !pdecal )
  657. return;
  658. decal_t *tmp;
  659. R_DecalCacheClear( pdecal );
  660. if ( IS_SURF_VALID( pdecal->surfID ) )
  661. {
  662. if ( MSurf_DecalPointer( pdecal->surfID ) == pdecal )
  663. {
  664. MSurf_Decals( pdecal->surfID ) = DecalToHandle( pdecal->pnext );
  665. }
  666. else
  667. {
  668. tmp = MSurf_DecalPointer( pdecal->surfID );
  669. if ( !tmp )
  670. Sys_Error("Bad decal list");
  671. while ( tmp->pnext )
  672. {
  673. if ( tmp->pnext == pdecal )
  674. {
  675. tmp->pnext = pdecal->pnext;
  676. break;
  677. }
  678. tmp = tmp->pnext;
  679. }
  680. }
  681. // Tell the displacement surface.
  682. if( SurfaceHasDispInfo( pdecal->surfID ) )
  683. {
  684. IDispInfo * pDispInfo = MSurf_DispInfo( pdecal->surfID, pData );
  685. if ( pDispInfo )
  686. pDispInfo->NotifyRemoveDecal( pdecal->m_DispDecal );
  687. }
  688. }
  689. pdecal->surfID = SURFACE_HANDLE_INVALID;
  690. if ( !(pdecal->flags & FDECAL_PERMANENT) )
  691. {
  692. --g_nDynamicDecals;
  693. Assert( g_nDynamicDecals >= 0 );
  694. }
  695. else
  696. {
  697. --g_nStaticDecals;
  698. Assert( g_nStaticDecals >= 0 );
  699. }
  700. // Free the decal.
  701. Assert( s_aDecalPool[pdecal->m_iDecalPool] == pdecal );
  702. s_aDecalPool[pdecal->m_iDecalPool] = NULL;
  703. g_DecalAllocator.Free( pdecal );
  704. }
  705. int R_FindFreeDecalSlot()
  706. {
  707. for ( int i=0; i < g_nMaxDecals; i++ )
  708. {
  709. if ( !s_aDecalPool[i] )
  710. return i;
  711. }
  712. return -1;
  713. }
  714. // Uncomment this to spew decals if we run out of space!!!
  715. // #define SPEW_DECALS
  716. #if defined( SPEW_DECALS )
  717. void SpewDecals()
  718. {
  719. static bool spewdecals = true;
  720. if ( spewdecals )
  721. {
  722. sspewdecals = false;
  723. int i = 0;
  724. for ( i = 0 ; i < g_nMaxDecals; ++i )
  725. {
  726. decal_t *decal = s_aDecalPool[ i ];
  727. Assert( decal );
  728. if ( decal )
  729. {
  730. bool permanent = ( decal->flags & FDECAL_PERMANENT ) ? true : false;
  731. Msg( "%i == %s on %i perm %i at %.2f %.2f %.2f on surf %i (%.2f %.2f %2.f)\n",
  732. i,
  733. decal->material->GetName(),
  734. (int)decal->entityIndex,
  735. permanent ? 1 : 0,
  736. decal->position.x, decal->position.y, decal->position.z,
  737. (int)decal->surfID,
  738. decal->dx,
  739. decal->dy,
  740. decal->scale );
  741. }
  742. }
  743. }
  744. }
  745. #endif
  746. int R_FindDynamicDecalSlot( int iStartAt )
  747. {
  748. if ( (iStartAt >= g_nMaxDecals) || (iStartAt < 0) )
  749. {
  750. iStartAt = 0;
  751. }
  752. int i = iStartAt;
  753. do
  754. {
  755. // don't deallocate player sprays or permanent decals
  756. if ( s_aDecalPool[i] &&
  757. !(s_aDecalPool[i]->flags & FDECAL_PERMANENT) &&
  758. !(s_aDecalPool[i]->flags & FDECAL_PLAYERSPRAY) )
  759. return i;
  760. ++i;
  761. if ( i >= g_nMaxDecals )
  762. i = 0;
  763. }
  764. while ( i != iStartAt );
  765. DevMsg("R_FindDynamicDecalSlot: no slot available.\n");
  766. #if defined( SPEW_DECALS )
  767. SpewDecals();
  768. #endif
  769. return -1;
  770. }
  771. // Just reuse next decal in list
  772. // A decal that spans multiple surfaces will use multiple decal_t pool entries, as each surface needs
  773. // it's own.
  774. static decal_t *R_DecalAlloc( int flags )
  775. {
  776. static bool bWarningOnce = false;
  777. bool bPermanent = (flags & FDECAL_PERMANENT) != 0;
  778. int dynamicDecalLimit = MIN( r_decals.GetInt(), g_nMaxDecals );
  779. // Now find a slot. Unless it's dynamic and we're at the limit of dynamic decals,
  780. // we can look for a free slot.
  781. int iSlot = -1;
  782. if ( bPermanent || (g_nDynamicDecals < dynamicDecalLimit) )
  783. {
  784. iSlot = R_FindFreeDecalSlot();
  785. }
  786. if ( iSlot == -1 )
  787. {
  788. iSlot = R_FindDynamicDecalSlot( g_iLastReplacedDynamic+1 );
  789. if ( iSlot == -1 )
  790. {
  791. if ( !bWarningOnce )
  792. {
  793. // Can't find a free slot. Just kill the first one.
  794. DevWarning( 1, "Exceeded MAX_DECALS (%d).\n", g_nMaxDecals );
  795. bWarningOnce = true;
  796. }
  797. iSlot = 0;
  798. }
  799. R_DecalUnlink( s_aDecalPool[iSlot], host_state.worldbrush );
  800. g_iLastReplacedDynamic = iSlot;
  801. }
  802. // Setup the new decal.
  803. decal_t *pDecal = g_DecalAllocator.Alloc();
  804. s_aDecalPool[iSlot] = pDecal;
  805. pDecal->pDestroyList = NULL;
  806. pDecal->m_iDecalPool = iSlot;
  807. pDecal->surfID = SURFACE_HANDLE_INVALID;
  808. pDecal->cacheHandle = INVALID_CACHE_ENTRY;
  809. pDecal->clippedVertCount = 0;
  810. if ( !bPermanent )
  811. {
  812. ++g_nDynamicDecals;
  813. }
  814. else
  815. {
  816. ++g_nStaticDecals;
  817. }
  818. return pDecal;
  819. }
  820. // The world coordinate system is right handed with Z up.
  821. //
  822. // ^ Z
  823. // |
  824. // |
  825. // |
  826. //X<----|
  827. // \
  828. // \
  829. // \ Y
  830. void R_DecalSurface( SurfaceHandle_t surfID, decalinfo_t *decalinfo, bool bForceForDisplacement )
  831. {
  832. // If we are a player spray, we should have a normal.
  833. Assert( ( ( decalinfo->m_Flags & FDECAL_PLAYERSPRAY ) == 0 ) || decalinfo->m_pNormal != NULL );
  834. if ( decalinfo->m_pNormal )
  835. {
  836. const float cosTheta = DotProduct( MSurf_Plane( surfID ).normal, *(decalinfo->m_pNormal) );
  837. const float fComp = ( decalinfo->m_Flags & FDECAL_PLAYERSPRAY )
  838. ? 0.5f // 60 degrees.
  839. : 0.0f;
  840. if ( cosTheta < fComp )
  841. return;
  842. }
  843. // Get the texture associated with this surface
  844. mtexinfo_t* tex = MSurf_TexInfo( surfID );
  845. Vector4D &textureU = tex->textureVecsTexelsPerWorldUnits[0];
  846. Vector4D &textureV = tex->textureVecsTexelsPerWorldUnits[1];
  847. // project decal center into the texture space of the surface
  848. float s = DotProduct( decalinfo->m_Position, textureU.AsVector3D() ) +
  849. textureU.w - MSurf_TextureMins( surfID )[0];
  850. float t = DotProduct( decalinfo->m_Position, textureV.AsVector3D() ) +
  851. textureV.w - MSurf_TextureMins( surfID )[1];
  852. // Determine the decal basis (measured in world space)
  853. // Note that the decal basis vectors 0 and 1 will always lie in the same
  854. // plane as the texture space basis vectors textureVecsTexelsPerWorldUnits.
  855. R_DecalComputeBasis( MSurf_Plane( surfID ).normal,
  856. (decalinfo->m_Flags & FDECAL_USESAXIS) ? &decalinfo->m_SAxis : 0,
  857. decalinfo->m_Basis );
  858. // Compute an effective width and height (axis aligned) in the parent texture space
  859. // How does this work? decalBasis[0] represents the u-direction (width)
  860. // of the decal measured in world space, decalBasis[1] represents the
  861. // v-direction (height) measured in world space.
  862. // textureVecsTexelsPerWorldUnits[0] represents the u direction of
  863. // the surface's texture space measured in world space (with the appropriate
  864. // scale factor folded in), and textureVecsTexelsPerWorldUnits[1]
  865. // represents the texture space v direction. We want to find the dimensions (w,h)
  866. // of a square measured in texture space, axis aligned to that coordinate system.
  867. // All we need to do is to find the components of the decal edge vectors
  868. // (decalWidth * decalBasis[0], decalHeight * decalBasis[1])
  869. // in texture coordinates:
  870. float w = fabs( decalinfo->m_decalWidth * DotProduct( textureU.AsVector3D(), decalinfo->m_Basis[0] ) ) +
  871. fabs( decalinfo->m_decalHeight * DotProduct( textureU.AsVector3D(), decalinfo->m_Basis[1] ) );
  872. float h = fabs( decalinfo->m_decalWidth * DotProduct( textureV.AsVector3D(), decalinfo->m_Basis[0] ) ) +
  873. fabs( decalinfo->m_decalHeight * DotProduct( textureV.AsVector3D(), decalinfo->m_Basis[1] ) );
  874. // move s,t to upper left corner
  875. s -= ( w * 0.5 );
  876. t -= ( h * 0.5 );
  877. // Is this rect within the surface? -- tex width & height are unsigned
  878. if( !bForceForDisplacement )
  879. {
  880. if ( s <= -w || t <= -h ||
  881. s > (MSurf_TextureExtents( surfID )[0]+w) || t > (MSurf_TextureExtents( surfID )[1]+h) )
  882. {
  883. return; // nope
  884. }
  885. }
  886. // stamp it
  887. R_DecalCreate( decalinfo, surfID, s, t, bForceForDisplacement );
  888. }
  889. //-----------------------------------------------------------------------------
  890. // iterate over all surfaces on a node, looking for surfaces to decal
  891. //-----------------------------------------------------------------------------
  892. static void R_DecalNodeSurfaces( mnode_t* node, decalinfo_t *decalinfo )
  893. {
  894. // iterate over all surfaces in the node
  895. SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface );
  896. for ( int i=0; i<node->numsurfaces ; ++i, ++surfID)
  897. {
  898. if ( MSurf_Flags( surfID ) & SURFDRAW_NODECALS )
  899. continue;
  900. // Displacement surfaces get decals in R_DecalLeaf.
  901. if ( SurfaceHasDispInfo( surfID ) )
  902. continue;
  903. R_DecalSurface( surfID, decalinfo, false );
  904. }
  905. }
  906. void R_DecalLeaf( mleaf_t *pLeaf, decalinfo_t *decalinfo )
  907. {
  908. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  909. for ( int i = 0; i < pLeaf->nummarksurfaces; i++ )
  910. {
  911. SurfaceHandle_t surfID = pHandle[i];
  912. // only process leaf surfaces
  913. if ( MSurf_Flags( surfID ) & (SURFDRAW_NODE|SURFDRAW_NODECALS) )
  914. continue;
  915. if ( decalinfo->m_aApplySurfs.Find( surfID ) != -1 )
  916. continue;
  917. Assert( !MSurf_DispInfo( surfID ) );
  918. float dist = fabs( DotProduct(decalinfo->m_Position, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist);
  919. if ( dist < DECAL_DISTANCE )
  920. {
  921. R_DecalSurface( surfID, decalinfo, false );
  922. }
  923. }
  924. // Add the decal to each displacement in the leaf it touches.
  925. for ( int i = 0; i < pLeaf->dispCount; i++ )
  926. {
  927. IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  928. SurfaceHandle_t surfID = pDispInfo->GetParent();
  929. if ( MSurf_Flags( surfID ) & SURFDRAW_NODECALS )
  930. continue;
  931. // Make sure the decal hasn't already been added to it.
  932. if( pDispInfo->GetTag() )
  933. continue;
  934. pDispInfo->SetTag();
  935. // Trivial bbox reject.
  936. Vector bbMin, bbMax;
  937. pDispInfo->GetBoundingBox( bbMin, bbMax );
  938. if( decalinfo->m_Position.x - decalinfo->m_Size < bbMax.x && decalinfo->m_Position.x + decalinfo->m_Size > bbMin.x &&
  939. decalinfo->m_Position.y - decalinfo->m_Size < bbMax.y && decalinfo->m_Position.y + decalinfo->m_Size > bbMin.y &&
  940. decalinfo->m_Position.z - decalinfo->m_Size < bbMax.z && decalinfo->m_Position.z + decalinfo->m_Size > bbMin.z )
  941. {
  942. R_DecalSurface( pDispInfo->GetParent(), decalinfo, true );
  943. }
  944. }
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Recursive routine to find surface to apply a decal to. World coordinates of
  948. // the decal are passed in r_recalpos like the rest of the engine. This should
  949. // be called through R_DecalShoot()
  950. //-----------------------------------------------------------------------------
  951. static void R_DecalNode( mnode_t *node, decalinfo_t* decalinfo )
  952. {
  953. cplane_t *splitplane;
  954. float dist;
  955. if (!node )
  956. return;
  957. if ( node->contents >= 0 )
  958. {
  959. R_DecalLeaf( (mleaf_t *)node, decalinfo );
  960. return;
  961. }
  962. splitplane = node->plane;
  963. dist = DotProduct (decalinfo->m_Position, splitplane->normal) - splitplane->dist;
  964. // This is arbitrarily set to 10 right now. In an ideal world we'd have the
  965. // exact surface but we don't so, this tells me which planes are "sort of
  966. // close" to the gunshot -- the gunshot is actually 4 units in front of the
  967. // wall (see dlls\weapons.cpp). We also need to check to see if the decal
  968. // actually intersects the texture space of the surface, as this method tags
  969. // parallel surfaces in the same node always.
  970. // JAY: This still tags faces that aren't correct at edges because we don't
  971. // have a surface normal
  972. if (dist > decalinfo->m_Size)
  973. {
  974. R_DecalNode (node->children[0], decalinfo);
  975. }
  976. else if (dist < -decalinfo->m_Size)
  977. {
  978. R_DecalNode (node->children[1], decalinfo);
  979. }
  980. else
  981. {
  982. if ( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
  983. R_DecalNodeSurfaces( node, decalinfo );
  984. R_DecalNode (node->children[0], decalinfo);
  985. R_DecalNode (node->children[1], decalinfo);
  986. }
  987. }
  988. //-----------------------------------------------------------------------------
  989. // Purpose:
  990. // Input : *pList -
  991. // count -
  992. // Output : static int
  993. //-----------------------------------------------------------------------------
  994. static int DecalListAdd( decallist_t *pList, int count )
  995. {
  996. int i;
  997. Vector tmp;
  998. decallist_t *pdecal;
  999. pdecal = pList + count;
  1000. for ( i = 0; i < count; i++ )
  1001. {
  1002. if ( !Q_strcmp( pdecal->name, pList[i].name ) &&
  1003. pdecal->entityIndex == pList[i].entityIndex )
  1004. {
  1005. VectorSubtract( pdecal->position, pList[i].position, tmp ); // Merge
  1006. if ( VectorLength( tmp ) < 2 ) // UNDONE: Tune this '2' constant
  1007. return count;
  1008. }
  1009. }
  1010. // This is a new decal
  1011. return count + 1;
  1012. }
  1013. typedef int (__cdecl *qsortFunc_t)( const void *, const void * );
  1014. static int __cdecl DecalDepthCompare( const decallist_t *elem1, const decallist_t *elem2 )
  1015. {
  1016. if ( elem1->depth > elem2->depth )
  1017. return -1;
  1018. if ( elem1->depth < elem2->depth )
  1019. return 1;
  1020. return 0;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose: Called by CSaveRestore::SaveClientState
  1024. // Input : *pList -
  1025. // Output : int
  1026. //-----------------------------------------------------------------------------
  1027. int DecalListCreate( decallist_t *pList )
  1028. {
  1029. int total = 0;
  1030. int i, depth;
  1031. if ( host_state.worldmodel )
  1032. {
  1033. for ( i = 0; i < g_nMaxDecals; i++ )
  1034. {
  1035. decal_t *decal = s_aDecalPool[i];
  1036. // Decal is in use and is not a custom decal
  1037. if ( !decal ||
  1038. !IS_SURF_VALID( decal->surfID ) ||
  1039. (decal->flags & ( FDECAL_CUSTOM | FDECAL_DONTSAVE ) ) )
  1040. continue;
  1041. decal_t *pdecals;
  1042. IMaterial *pMaterial;
  1043. // compute depth
  1044. depth = 0;
  1045. pdecals = MSurf_DecalPointer( decal->surfID );
  1046. while ( pdecals && pdecals != decal )
  1047. {
  1048. depth++;
  1049. pdecals = pdecals->pnext;
  1050. }
  1051. pList[total].depth = depth;
  1052. pList[total].flags = decal->flags;
  1053. R_DecalUnProject( decal, &pList[total] );
  1054. pMaterial = decal->material;
  1055. Q_strncpy( pList[total].name, pMaterial->GetName(), sizeof( pList[total].name ) );
  1056. // Check to see if the decal should be added
  1057. total = DecalListAdd( pList, total );
  1058. }
  1059. }
  1060. // Sort the decals lowest depth first, so they can be re-applied in order
  1061. qsort( pList, total, sizeof(decallist_t), ( qsortFunc_t )DecalDepthCompare );
  1062. return total;
  1063. }
  1064. // ---------------------------------------------------------
  1065. static bool R_DecalUnProject( decal_t *pdecal, decallist_t *entry )
  1066. {
  1067. if ( !pdecal || !IS_SURF_VALID( pdecal->surfID ) )
  1068. return false;
  1069. VectorCopy( pdecal->position, entry->position );
  1070. entry->entityIndex = pdecal->entityIndex;
  1071. // Grab surface plane equation
  1072. cplane_t plane = MSurf_Plane( pdecal->surfID );
  1073. VectorCopy( plane.normal, entry->impactPlaneNormal );
  1074. return true;
  1075. }
  1076. // Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords
  1077. static void R_DecalShoot_( IMaterial *pMaterial, int entity, const model_t *model,
  1078. const Vector &position, const Vector *saxis, int flags, const color32 &rgbaColor, const Vector *pNormal, void *userdata = 0 )
  1079. {
  1080. decalinfo_t decalInfo;
  1081. VectorCopy( position, decalInfo.m_Position ); // Pass position in global
  1082. if ( !model || model->type != mod_brush || !pMaterial )
  1083. return;
  1084. decalInfo.m_pModel = (model_t *)model;
  1085. decalInfo.m_pBrush = model->brush.pShared;
  1086. // Deal with the s axis if one was passed in
  1087. if (saxis)
  1088. {
  1089. flags |= FDECAL_USESAXIS;
  1090. VectorCopy( *saxis, decalInfo.m_SAxis );
  1091. }
  1092. // More state used by R_DecalNode()
  1093. decalInfo.m_pMaterial = pMaterial;
  1094. decalInfo.m_pUserData = userdata;
  1095. decalInfo.m_Flags = flags;
  1096. decalInfo.m_Entity = entity;
  1097. decalInfo.m_Size = pMaterial->GetMappingWidth() >> 1;
  1098. if ( (int)(pMaterial->GetMappingHeight() >> 1) > decalInfo.m_Size )
  1099. decalInfo.m_Size = pMaterial->GetMappingHeight() >> 1;
  1100. // Compute scale of surface
  1101. // FIXME: cache this?
  1102. float scale = 1.0f;
  1103. IMaterialVar *pDecalScaleVar = decalInfo.m_pMaterial->FindVarFast( "$decalScale", &s_DecalScaleVarCache );
  1104. if ( pDecalScaleVar )
  1105. {
  1106. scale = pDecalScaleVar->GetFloatValue();
  1107. }
  1108. pDecalScaleVar = decalInfo.m_pMaterial->FindVarFast( "$decalScaleVariation", &s_DecalScaleVariationVarCache );
  1109. if ( pDecalScaleVar )
  1110. {
  1111. float variation = pDecalScaleVar->GetFloatValue();
  1112. variation = clamp( variation, 0.0f, 0.99f );
  1113. variation = RandomFloat( -variation, variation );
  1114. scale += variation * scale;
  1115. }
  1116. if ( scale != 1.0f && scale != 0.0f )
  1117. {
  1118. decalInfo.m_scale = 1.0f / scale;
  1119. decalInfo.m_Size *= scale;
  1120. }
  1121. else
  1122. {
  1123. decalInfo.m_scale = 1.0f;
  1124. }
  1125. decalInfo.m_flFadeDuration = 0.0f;
  1126. IMaterialVar* pFadeVar = pMaterial->FindVarFast( "$decalFadeDuration", &s_DecalFadeVarCache );
  1127. if ( pFadeVar )
  1128. {
  1129. decalInfo.m_flFadeDuration = pFadeVar->GetFloatValue();
  1130. pFadeVar = pMaterial->FindVarFast( "$decalFadeTime", &s_DecalFadeTimeVarCache );
  1131. decalInfo.m_flFadeStartTime = pFadeVar ? pFadeVar->GetFloatValue() : 0.0f;
  1132. }
  1133. IMaterialVar *pSecondPassVar = pMaterial->FindVarFast( "$decalSecondPass", &s_DecalSecondPassVarCache );
  1134. if ( pSecondPassVar )
  1135. {
  1136. decalInfo.m_Flags |= FDECAL_SECONDPASS;
  1137. }
  1138. // compute the decal dimensions in world space
  1139. decalInfo.m_decalWidth = pMaterial->GetMappingWidth() / decalInfo.m_scale;
  1140. decalInfo.m_decalHeight = pMaterial->GetMappingHeight() / decalInfo.m_scale;
  1141. decalInfo.m_Color = rgbaColor;
  1142. // McJohn -- Get surface normal
  1143. decalInfo.m_pNormal = pNormal;
  1144. decalInfo.m_aApplySurfs.Purge();
  1145. // Clear the displacement tags because we use them in R_DecalNode.
  1146. DispInfo_ClearAllTags( decalInfo.m_pBrush->hDispInfos );
  1147. mnode_t *pnodes = decalInfo.m_pBrush->nodes + decalInfo.m_pModel->brush.firstnode;
  1148. R_DecalNode( pnodes, &decalInfo );
  1149. }
  1150. // Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords
  1151. // This is called from cl_parse.cpp, cl_tent.cpp
  1152. void R_DecalShoot( int textureIndex, int entity, const model_t *model, const Vector &position, const Vector *saxis, int flags, const color32 &rgbaColor, const Vector *pNormal, int nAdditionalDecalFlags )
  1153. {
  1154. void *userdata = NULL;
  1155. IMaterial* pMaterial = Draw_DecalMaterial( textureIndex );
  1156. flags |= R_ConvertToPrivateDecalFlags( nAdditionalDecalFlags );
  1157. // Player decal custom handling:
  1158. if ( flags & FDECAL_PLAYERSPRAY )
  1159. {
  1160. userdata = ( void * ) textureIndex;
  1161. }
  1162. R_DecalShoot_( pMaterial, entity, model, position, saxis, flags, rgbaColor, pNormal, userdata );
  1163. }
  1164. //-----------------------------------------------------------------------------
  1165. // Purpose:
  1166. // Input : *material -
  1167. // playerIndex -
  1168. // entity -
  1169. // *model -
  1170. // position -
  1171. // *saxis -
  1172. // flags -
  1173. // &rgbaColor -
  1174. //-----------------------------------------------------------------------------
  1175. void R_PlayerDecalShoot( IMaterial *material, void *userdata, int entity, const model_t *model,
  1176. const Vector& position, const Vector *saxis, int flags, const color32 &rgbaColor, int nAdditionalDecalFlags )
  1177. {
  1178. // The userdata that is passed in is actually the sticker kit ID
  1179. // it cannot be zero
  1180. Assert( userdata != 0 );
  1181. #if 0 // <vitaliy>: decals are implemented differently in CS:GO and time out by game rules
  1182. //
  1183. // Linear search through decal pool to retire any other decals this
  1184. // player has sprayed. It appears that multiple decals can be
  1185. // allocated for a single spray due to the way they are mapped to
  1186. // surfaces. We need to run through and clean them all up. This
  1187. // seems like the cleanest way to manage this - especially since
  1188. // it doesn't happen that often.
  1189. //
  1190. int i;
  1191. CUtlVector<decal_t *> decalVec;
  1192. for ( i = 0; i<s_aDecalPool.Count(); i++ )
  1193. {
  1194. decal_t * decal = s_aDecalPool[i];
  1195. if( decal &&
  1196. decal->flags & FDECAL_PLAYERSPRAY &&
  1197. decal->userdata == userdata )
  1198. {
  1199. decalVec.AddToTail( decal );
  1200. }
  1201. }
  1202. // remove all the sprays we found
  1203. for ( i = 0; i < decalVec.Count(); i++ )
  1204. {
  1205. R_DecalUnlink( decalVec[i], host_state.worldbrush );
  1206. }
  1207. #endif
  1208. // set this to be a player spray so it is timed out appropriately.
  1209. flags |= FDECAL_PLAYERSPRAY;
  1210. flags |= R_ConvertToPrivateDecalFlags( nAdditionalDecalFlags );
  1211. R_DecalShoot_( material, entity, model, position, saxis, flags, rgbaColor, NULL, userdata );
  1212. }
  1213. struct decalcontext_t
  1214. {
  1215. Vector vModelOrg;
  1216. Vector sAxis;
  1217. float sOffset;
  1218. Vector tAxis;
  1219. float tOffset;
  1220. float sScale;
  1221. float tScale;
  1222. IMatRenderContext *pRenderContext;
  1223. SurfaceHandle_t pSurf;
  1224. decalcontext_t( IMatRenderContext *pContext, const Vector &modelorg )
  1225. {
  1226. pRenderContext = pContext;
  1227. vModelOrg = modelorg;
  1228. pSurf = NULL;
  1229. }
  1230. void InitSurface( SurfaceHandle_t surfID )
  1231. {
  1232. if ( pSurf == surfID )
  1233. return;
  1234. pSurf = surfID;
  1235. mtexinfo_t* pTexInfo = MSurf_TexInfo( surfID );
  1236. int lightmapPageWidth, lightmapPageHeight;
  1237. materials->GetLightmapPageSize( SortInfoToLightmapPage(MSurf_MaterialSortID( surfID )),&lightmapPageWidth, &lightmapPageHeight );
  1238. sScale = 1.0f / float(lightmapPageWidth);
  1239. tScale = 1.0f / float(lightmapPageHeight);
  1240. msurfacelighting_t *pSurfacelighting = SurfaceLighting(surfID);
  1241. sOffset = pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3] - pSurfacelighting->m_LightmapMins[0] +
  1242. pSurfacelighting->m_OffsetIntoLightmapPage[0] + 0.5f;
  1243. tOffset = pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3] - pSurfacelighting->m_LightmapMins[1] +
  1244. pSurfacelighting->m_OffsetIntoLightmapPage[1] + 0.5f;
  1245. sAxis = pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D();
  1246. tAxis = pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D();
  1247. }
  1248. inline float ComputeS( const Vector &pos ) const
  1249. {
  1250. return sScale * (DotProduct(pos, sAxis) + sOffset);
  1251. }
  1252. inline float ComputeT( const Vector &pos ) const
  1253. {
  1254. return tScale * (DotProduct(pos, tAxis) + tOffset);
  1255. }
  1256. };
  1257. // Generate lighting coordinates at each vertex for decal vertices v[] on surface psurf
  1258. static void R_DecalVertsLight( CDecalVert* v, const decalcontext_t &context, SurfaceHandle_t surfID, int vertCount )
  1259. {
  1260. for ( int j = 0; j < vertCount; j++, v++ )
  1261. {
  1262. v->m_cLMCoords.x = context.ComputeS(v->m_vPos);
  1263. v->m_cLMCoords.y = context.ComputeT(v->m_vPos);
  1264. }
  1265. }
  1266. //-----------------------------------------------------------------------------
  1267. // Purpose: Check for intersecting decals on this surface
  1268. // Input : *psurf -
  1269. // *pcount -
  1270. // x -
  1271. // y -
  1272. // Output : static decal_t
  1273. //-----------------------------------------------------------------------------
  1274. // UNDONE: This probably doesn't work quite right any more
  1275. // we should base overlap on the new decal basis matrix
  1276. // decal basis is constant per plane, perhaps we should store it (unscaled) in the shared plane struct
  1277. // BRJ: Note, decal basis is not constant when decals need to specify an s direction
  1278. // but that certainly isn't the majority case
  1279. static decal_t *R_DecalFindOverlappingDecals( decalinfo_t* decalinfo, SurfaceHandle_t surfID )
  1280. {
  1281. decal_t *plast = NULL;
  1282. // (Same as R_SetupDecalClip).
  1283. IMaterial *pMaterial = decalinfo->m_pMaterial;
  1284. int count = 0;
  1285. // Precalculate the extents of decalinfo's decal in world space.
  1286. int mapSize[2] = {pMaterial->GetMappingWidth(), pMaterial->GetMappingHeight()};
  1287. Vector decalExtents[2];
  1288. // this is half the width in world space of the decal.
  1289. float minProjectedWidth = (mapSize[0] / decalinfo->m_scale) * 0.5;
  1290. decalExtents[0] = decalinfo->m_Basis[0] * minProjectedWidth;
  1291. decalExtents[1] = decalinfo->m_Basis[1] * (mapSize[1] / decalinfo->m_scale) * 0.5f;
  1292. float areaThreshold = r_decal_overlap_area.GetFloat();
  1293. float lastArea = 0;
  1294. bool bFullMatch = false;
  1295. decal_t *pDecal = MSurf_DecalPointer( surfID );
  1296. CUtlVectorFixedGrowable<decal_t *,32> coveredList;
  1297. while ( pDecal )
  1298. {
  1299. pMaterial = pDecal->material;
  1300. // Don't steal bigger decals and replace them with smaller decals
  1301. // Don't steal permanent decals, or player sprays
  1302. if ( !(pDecal->flags & FDECAL_PERMANENT) &&
  1303. !(pDecal->flags & FDECAL_PLAYERSPRAY) && pMaterial )
  1304. {
  1305. Vector testBasis[3];
  1306. float testWorldScale[2];
  1307. R_SetupDecalTextureSpaceBasis( pDecal, MSurf_Plane( surfID ).normal, pMaterial, testBasis, testWorldScale );
  1308. // Here, we project the min and max extents of the decal that got passed in into
  1309. // this decal's (pDecal's) [0,0,1,1] clip space, just like we would if we were
  1310. // clipping a triangle into pDecal's clip space.
  1311. Vector2D vDecalMin(
  1312. DotProduct( decalinfo->m_Position - decalExtents[0], testBasis[0] ) - pDecal->dx + 0.5f,
  1313. DotProduct( decalinfo->m_Position - decalExtents[1], testBasis[1] ) - pDecal->dy + 0.5f );
  1314. Vector2D vDecalMax(
  1315. DotProduct( decalinfo->m_Position + decalExtents[0], testBasis[0] ) - pDecal->dx + 0.5f,
  1316. DotProduct( decalinfo->m_Position + decalExtents[1], testBasis[1] ) - pDecal->dy + 0.5f );
  1317. // Now figure out the part of the projection that intersects pDecal's
  1318. // clip box [0,0,1,1].
  1319. Vector2D vUnionMin( fpmax( vDecalMin.x, 0.0f ), fpmax( vDecalMin.y, 0.0f ) );
  1320. Vector2D vUnionMax( fpmin( vDecalMax.x, 1.0f ), fpmin( vDecalMax.y, 1.0f ) );
  1321. // if the decal is less than half the width of the one we're applying, don't test for overlap
  1322. // test for complete coverage
  1323. float projectWidthTestedDecal = pDecal->material->GetMappingWidth() / pDecal->scale;
  1324. float sizex = vUnionMax.x - vUnionMin.x;
  1325. float sizey = vUnionMax.y - vUnionMin.y;
  1326. if( sizex >= 0 && sizey >= 0)
  1327. {
  1328. // Figure out how much of this intersects the (0,0) - (1,1) bbox.
  1329. float flArea = sizex * sizey;
  1330. if ( projectWidthTestedDecal < minProjectedWidth )
  1331. {
  1332. if ( flArea > 0.999f )
  1333. {
  1334. coveredList.AddToTail(pDecal);
  1335. }
  1336. }
  1337. else
  1338. {
  1339. if( flArea > areaThreshold )
  1340. {
  1341. // once you pass the threshold, scale the area by the decal size to select the largest
  1342. // decal above the threshold
  1343. float flAreaScaled = flArea * projectWidthTestedDecal;
  1344. count++;
  1345. if ( !plast || flAreaScaled > lastArea )
  1346. {
  1347. plast = pDecal;
  1348. lastArea = flAreaScaled;
  1349. // go ahead and remove even if you're not at the max overlap count yet because this is a very similar decal
  1350. bFullMatch = ( flArea >= 0.9f ) ? true : false;
  1351. }
  1352. }
  1353. }
  1354. }
  1355. }
  1356. pDecal = pDecal->pnext;
  1357. }
  1358. if ( plast )
  1359. {
  1360. if ( count < r_decal_overlap_count.GetInt() && !bFullMatch )
  1361. {
  1362. plast = NULL;
  1363. }
  1364. }
  1365. if ( coveredList.Count() > r_decal_cover_count.GetInt() )
  1366. {
  1367. int last = coveredList.Count() - r_decal_cover_count.GetInt();
  1368. for ( int i = 0; i < last; i++ )
  1369. {
  1370. R_DecalUnlink( coveredList[i], host_state.worldbrush );
  1371. }
  1372. }
  1373. return plast;
  1374. }
  1375. // Add the decal to the surface's list of decals.
  1376. // If the surface is a displacement, let the displacement precalculate data for the decal.
  1377. static void R_AddDecalToSurface(
  1378. decal_t *pdecal,
  1379. SurfaceHandle_t surfID,
  1380. decalinfo_t *decalinfo )
  1381. {
  1382. pdecal->pnext = NULL;
  1383. decal_t *pold = MSurf_DecalPointer( surfID );
  1384. if ( pold )
  1385. {
  1386. while ( pold->pnext )
  1387. pold = pold->pnext;
  1388. pold->pnext = pdecal;
  1389. }
  1390. else
  1391. {
  1392. MSurf_Decals( surfID ) = DecalToHandle(pdecal);
  1393. }
  1394. // Tag surface
  1395. pdecal->surfID = surfID;
  1396. pdecal->flSize = decalinfo->m_Size;
  1397. pdecal->lightmapOffset = ComputeDecalLightmapOffset( surfID );
  1398. // Let the dispinfo reclip the decal if need be.
  1399. if( SurfaceHasDispInfo( surfID ) )
  1400. {
  1401. pdecal->m_DispDecal = MSurf_DispInfo( surfID )->NotifyAddDecal( pdecal, decalinfo->m_Size );
  1402. }
  1403. // Add surface to list.
  1404. decalinfo->m_aApplySurfs.AddToTail( surfID );
  1405. }
  1406. //=============================================================================
  1407. //
  1408. // Decal batches for rendering.
  1409. //
  1410. CUtlVector<DecalSortVertexFormat_t> g_aDecalFormats;
  1411. CUtlVector<DecalSortTrees_t> g_aDecalSortTrees;
  1412. CUtlFixedLinkedList<decal_t*> g_aDecalSortPool;
  1413. int g_nDecalSortCheckCount;
  1414. int g_nBrushModelDecalSortCheckCount;
  1415. CUtlFixedLinkedList<decal_t*> g_aDispDecalSortPool;
  1416. CUtlVector<DecalSortTrees_t> g_aDispDecalSortTrees;
  1417. int g_nDispDecalSortCheckCount;
  1418. //-----------------------------------------------------------------------------
  1419. // Purpose:
  1420. //-----------------------------------------------------------------------------
  1421. void R_DecalSortInit( void )
  1422. {
  1423. g_aDecalFormats.Purge();
  1424. g_aDecalSortTrees.Purge();
  1425. g_aDecalSortPool.Purge();
  1426. g_aDecalSortPool.EnsureCapacity( g_nMaxDecals );
  1427. g_aDecalSortPool.SetGrowSize( 128 );
  1428. g_nDecalSortCheckCount = 0;
  1429. g_nBrushModelDecalSortCheckCount = 0;
  1430. g_aDispDecalSortTrees.Purge();
  1431. g_aDispDecalSortPool.Purge();
  1432. g_aDispDecalSortPool.EnsureCapacity( g_nMaxDecals );
  1433. g_aDispDecalSortPool.SetGrowSize( 128 );
  1434. g_nDispDecalSortCheckCount = 0;
  1435. }
  1436. //-----------------------------------------------------------------------------
  1437. // Purpose:
  1438. //-----------------------------------------------------------------------------
  1439. void DecalSurfacesInit( bool bBrushModel )
  1440. {
  1441. if ( !bBrushModel )
  1442. {
  1443. // Only clear the pool once per frame.
  1444. g_aDecalSortPool.RemoveAll();
  1445. ++g_nDecalSortCheckCount;
  1446. }
  1447. else
  1448. {
  1449. ++g_nBrushModelDecalSortCheckCount;
  1450. }
  1451. }
  1452. //-----------------------------------------------------------------------------
  1453. // Purpose:
  1454. //-----------------------------------------------------------------------------
  1455. static void R_DecalMaterialSort( decal_t *pDecal, SurfaceHandle_t surfID )
  1456. {
  1457. // Setup the decal material sort data.
  1458. DecalMaterialSortData_t sort;
  1459. if ( pDecal->material->InMaterialPage() )
  1460. {
  1461. sort.m_pMaterial = pDecal->material->GetMaterialPage();
  1462. }
  1463. else
  1464. {
  1465. sort.m_pMaterial = pDecal->material;
  1466. }
  1467. sort.m_iLightmapPage = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  1468. // Does this vertex type exist?
  1469. VertexFormat_t vertexFormat = GetUncompressedFormat( sort.m_pMaterial );
  1470. int iFormat = 0;
  1471. int nFormatCount = g_aDecalFormats.Count();
  1472. for ( ; iFormat < nFormatCount; ++iFormat )
  1473. {
  1474. if ( g_aDecalFormats[iFormat].m_VertexFormat == vertexFormat )
  1475. break;
  1476. }
  1477. // A new vertex format type.
  1478. if ( iFormat == nFormatCount )
  1479. {
  1480. iFormat = g_aDecalFormats.AddToTail();
  1481. g_aDecalFormats[iFormat].m_VertexFormat = vertexFormat;
  1482. int iSortTree = g_aDecalSortTrees.AddToTail();
  1483. g_aDispDecalSortTrees.AddToTail();
  1484. g_aDecalFormats[iFormat].m_iSortTree = iSortTree;
  1485. }
  1486. // Get an index for the current sort tree.
  1487. int iSortTree = g_aDecalFormats[iFormat].m_iSortTree;
  1488. int iTreeType = -1;
  1489. // Lightmapped.
  1490. if ( sort.m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ) )
  1491. {
  1492. // Permanent lightmapped decals.
  1493. if ( pDecal->flags & FDECAL_PERMANENT )
  1494. {
  1495. iTreeType = PERMANENT_LIGHTMAP;
  1496. }
  1497. // Non-permanent lightmapped decals.
  1498. else
  1499. {
  1500. iTreeType = LIGHTMAP;
  1501. }
  1502. }
  1503. // Non-lightmapped decals.
  1504. else
  1505. {
  1506. iTreeType = NONLIGHTMAP;
  1507. sort.m_iLightmapPage = -1;
  1508. }
  1509. int iSort = g_aDecalSortTrees[iSortTree].m_pTrees[iTreeType]->Find( sort );
  1510. if ( iSort == -1 )
  1511. {
  1512. int iBucket = g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[0][iTreeType].AddToTail();
  1513. g_aDispDecalSortTrees[iSortTree].m_aDecalSortBuckets[0][iTreeType].AddToTail();
  1514. g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[0][iTreeType].Element( iBucket ).m_nCheckCount = -1;
  1515. g_aDispDecalSortTrees[iSortTree].m_aDecalSortBuckets[0][iTreeType].Element( iBucket ).m_nCheckCount = -1;
  1516. for ( int iGroup = 1; iGroup < ( MAX_MAT_SORT_GROUPS + 1 ); ++iGroup )
  1517. {
  1518. g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].AddToTail();
  1519. g_aDispDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].AddToTail();
  1520. g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_nCheckCount = -1;
  1521. g_aDispDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_nCheckCount = -1;
  1522. }
  1523. sort.m_iBucket = iBucket;
  1524. g_aDecalSortTrees[iSortTree].m_pTrees[iTreeType]->Insert( sort );
  1525. g_aDispDecalSortTrees[iSortTree].m_pTrees[iTreeType]->Insert( sort );
  1526. pDecal->m_iSortTree = iSortTree;
  1527. pDecal->m_iSortMaterial = sort.m_iBucket;
  1528. }
  1529. else
  1530. {
  1531. pDecal->m_iSortTree = iSortTree;
  1532. pDecal->m_iSortMaterial = g_aDecalSortTrees[iSortTree].m_pTrees[iTreeType]->Element( iSort ).m_iBucket;
  1533. }
  1534. }
  1535. //-----------------------------------------------------------------------------
  1536. // Purpose:
  1537. //-----------------------------------------------------------------------------
  1538. void R_DecalReSortMaterials( void ) //X
  1539. {
  1540. R_DecalSortInit();
  1541. int nDecalCount = s_aDecalPool.Count();
  1542. for ( int iDecal = 0; iDecal < nDecalCount; ++iDecal )
  1543. {
  1544. decal_t *pDecal = s_aDecalPool.Element( iDecal );
  1545. if ( pDecal )
  1546. {
  1547. SurfaceHandle_t surfID = pDecal->surfID;
  1548. R_DecalMaterialSort( pDecal, surfID );
  1549. }
  1550. }
  1551. }
  1552. // Allocate and initialize a decal from the pool, on surface with offsets x, y
  1553. // UNDONE: offsets are not really meaningful in new decal coordinate system
  1554. // the clipping code will recalc the offsets
  1555. static void R_DecalCreate(
  1556. decalinfo_t* decalinfo,
  1557. SurfaceHandle_t surfID,
  1558. float x,
  1559. float y,
  1560. bool bForceForDisplacement )
  1561. {
  1562. decal_t *pdecal;
  1563. if( !IS_SURF_VALID( surfID ) )
  1564. {
  1565. ConMsg( "psurface NULL in R_DecalCreate!\n" );
  1566. return;
  1567. }
  1568. decal_t *pold = R_DecalFindOverlappingDecals( decalinfo, surfID );
  1569. if ( pold )
  1570. {
  1571. R_DecalUnlink( pold, host_state.worldbrush );
  1572. pold = NULL;
  1573. }
  1574. pdecal = R_DecalAlloc( decalinfo->m_Flags );
  1575. pdecal->m_nTickCreated = GetBaseLocalClient().GetClientTickCount();
  1576. pdecal->flags = decalinfo->m_Flags;
  1577. pdecal->color = decalinfo->m_Color;
  1578. VectorCopy( decalinfo->m_Position, pdecal->position );
  1579. if (pdecal->flags & FDECAL_USESAXIS)
  1580. VectorCopy( decalinfo->m_SAxis, pdecal->saxis );
  1581. pdecal->dx = x;
  1582. pdecal->dy = y;
  1583. pdecal->material = decalinfo->m_pMaterial;
  1584. Assert( pdecal->material );
  1585. pdecal->userdata = decalinfo->m_pUserData;
  1586. // Set scaling
  1587. pdecal->scale = decalinfo->m_scale;
  1588. pdecal->entityIndex = decalinfo->m_Entity;
  1589. // Get dynamic information from the material (fade start, fade time)
  1590. if ( decalinfo->m_flFadeDuration > 0.0f )
  1591. {
  1592. pdecal->flags |= FDECAL_DYNAMIC;
  1593. pdecal->fadeDuration = decalinfo->m_flFadeDuration;
  1594. pdecal->fadeStartTime = decalinfo->m_flFadeStartTime;
  1595. pdecal->fadeStartTime += GetBaseLocalClient().GetTime();
  1596. }
  1597. // check for a player spray
  1598. if( pdecal->flags & FDECAL_PLAYERSPRAY )
  1599. {
  1600. // set the time when this decal appered, and allow to self-destruct after enough time passed
  1601. pdecal->fadeStartTime = 0;
  1602. // Force the scale to 1 for player sprays.
  1603. pdecal->scale = 1.0f;
  1604. }
  1605. if( !bForceForDisplacement )
  1606. {
  1607. // Check to see if the decal actually intersects the surface
  1608. // if not, then remove the decal
  1609. R_DecalVertsClip( NULL, pdecal, surfID, decalinfo->m_pMaterial );
  1610. if ( !pdecal->clippedVertCount )
  1611. {
  1612. R_DecalUnlink( pdecal, host_state.worldbrush );
  1613. return;
  1614. }
  1615. }
  1616. // Add to the surface's list
  1617. R_AddDecalToSurface( pdecal, surfID, decalinfo );
  1618. // Add decal material/lightmap to sort list.
  1619. R_DecalMaterialSort( pdecal, surfID );
  1620. }
  1621. //-----------------------------------------------------------------------------
  1622. // Updates all decals, returns true if the decal should be retired
  1623. //-----------------------------------------------------------------------------
  1624. bool DecalUpdate( decal_t* pDecal )
  1625. {
  1626. if ( ( pDecal->flags & FDECAL_IMMEDIATECLEANUP ) != 0 )
  1627. {
  1628. // This is ugly. We want to queue the decal for destruction, but we don't want to skip rendering it.
  1629. // It will actually be cleaned up at the end of the frame.
  1630. R_DecalAddToDestroyList( pDecal );
  1631. return false;
  1632. }
  1633. // retire the decal if it's time has come
  1634. if (pDecal->fadeDuration > 0)
  1635. {
  1636. return (GetBaseLocalClient().GetTime() >= pDecal->fadeStartTime + pDecal->fadeDuration);
  1637. }
  1638. return false;
  1639. }
  1640. #define NEXT_MULTIPLE_OF_4(P) ( ((P) + ((4)-1)) & (~((4)-1)) )
  1641. // Build the vertex list for a decal on a surface and clip it to the surface.
  1642. // This is a template so it can work on world surfaces and dynamic displacement
  1643. // triangles the same way.
  1644. CDecalVert* R_DecalSetupVerts( decalcontext_t &context, decal_t *pDecal, SurfaceHandle_t surfID, IMaterial *pMaterial )
  1645. {
  1646. //
  1647. // Do not scale playersprays
  1648. //
  1649. if( pDecal->flags & FDECAL_DISTANCESCALE )
  1650. {
  1651. if( !(pDecal->flags & FDECAL_PLAYERSPRAY) )
  1652. {
  1653. float scaleFactor = 1.0f;
  1654. float nearScale, farScale, nearDist, farDist;
  1655. nearScale = r_dscale_nearscale.GetFloat();
  1656. nearDist = r_dscale_neardist.GetFloat();
  1657. farScale = r_dscale_farscale.GetFloat();
  1658. farDist = r_dscale_fardist.GetFloat();
  1659. Vector playerOrigin = CurrentViewOrigin();
  1660. float dist = 0;
  1661. if ( pDecal->entityIndex == 0 )
  1662. {
  1663. dist = (playerOrigin - pDecal->position).Length();
  1664. }
  1665. else
  1666. {
  1667. Vector worldSpaceCenter;
  1668. Vector3DMultiplyPosition(g_BrushToWorldMatrix, pDecal->position, worldSpaceCenter );
  1669. dist = (playerOrigin - worldSpaceCenter).Length();
  1670. }
  1671. float fov = g_EngineRenderer->GetFov();
  1672. //
  1673. // If the player is zoomed in, we adjust the nearScale and farScale
  1674. //
  1675. if ( fov != r_dscale_basefov.GetFloat() && fov > 0 && r_dscale_basefov.GetFloat() > 0 )
  1676. {
  1677. float fovScale = fov / r_dscale_basefov.GetFloat();
  1678. nearScale *= fovScale;
  1679. farScale *= fovScale;
  1680. if ( nearScale < 1.0f )
  1681. nearScale = 1.0f;
  1682. if ( farScale < 1.0f )
  1683. farScale = 1.0f;
  1684. }
  1685. //
  1686. // Scaling works like this:
  1687. //
  1688. // 0->nearDist scale = 1.0
  1689. // nearDist -> farDist scale = LERP(nearScale, farScale)
  1690. // farDist->inf scale = farScale
  1691. //
  1692. // scaling in the rest of the code appears to be more of an
  1693. // attenuation factor rather than a scale, so we compute 1/scale
  1694. // to account for this.
  1695. //
  1696. if ( dist < nearDist )
  1697. scaleFactor = 1.0f;
  1698. else if( dist >= farDist )
  1699. scaleFactor = farScale;
  1700. else
  1701. {
  1702. float percent = (dist - nearDist) / (farDist - nearDist);
  1703. scaleFactor = nearScale + percent * (farScale - nearScale);
  1704. }
  1705. //
  1706. // scaling in the rest of the code appears to be more of an
  1707. // attenuation factor rather than a scale, so we compute 1/scale
  1708. // to account for this.
  1709. //
  1710. scaleFactor = 1.0f / scaleFactor;
  1711. float originalScale = pDecal->scale;
  1712. float scaledScale = pDecal->scale * scaleFactor;
  1713. pDecal->scale = scaledScale;
  1714. context.InitSurface( pDecal->surfID );
  1715. CDecalVert *v = R_DecalVertsClip( NULL, pDecal, surfID, pMaterial );
  1716. if ( v )
  1717. {
  1718. R_DecalVertsLight( v, context, surfID, pDecal->clippedVertCount );
  1719. }
  1720. pDecal->scale = originalScale;
  1721. return v;
  1722. }
  1723. }
  1724. // find in cache?
  1725. CDecalVert *v = g_DecalVertCache.GetCachedVerts(pDecal);
  1726. if ( !v )
  1727. {
  1728. // not in cache, clip & light
  1729. context.InitSurface( pDecal->surfID );
  1730. v = R_DecalVertsClip( NULL, pDecal, surfID, pMaterial );
  1731. if ( pDecal->clippedVertCount )
  1732. {
  1733. #if _DEBUG
  1734. // squash vector copy asserts in debug
  1735. int nextVert = NEXT_MULTIPLE_OF_4(pDecal->clippedVertCount);
  1736. if ( (nextVert - pDecal->clippedVertCount) < 4 )
  1737. {
  1738. for ( int i = pDecal->clippedVertCount; i < nextVert; i++ )
  1739. {
  1740. v[i].m_cLMCoords.Init();
  1741. v[i].m_ctCoords.Init();
  1742. v[i].m_vPos.Init();
  1743. }
  1744. }
  1745. #endif
  1746. R_DecalVertsLight( v, context, surfID, pDecal->clippedVertCount );
  1747. g_DecalVertCache.StoreVertsInCache( pDecal, v );
  1748. }
  1749. }
  1750. return v;
  1751. }
  1752. //-----------------------------------------------------------------------------
  1753. // Renders a single decal, *could retire the decal!!*
  1754. //-----------------------------------------------------------------------------
  1755. void DecalUpdateAndDrawSingle( decalcontext_t &context, SurfaceHandle_t surfID, decal_t* pDecal )
  1756. {
  1757. if( !pDecal->material )
  1758. return;
  1759. // Update dynamic decals
  1760. bool retire = false;
  1761. if ( pDecal->flags & ( FDECAL_DYNAMIC | FDECAL_IMMEDIATECLEANUP ) )
  1762. retire = DecalUpdate( pDecal );
  1763. if( SurfaceHasDispInfo( surfID ) )
  1764. {
  1765. // Dispinfos generate lists of tris for decals when the decal is first
  1766. // created.
  1767. }
  1768. else
  1769. {
  1770. CDecalVert *v = R_DecalSetupVerts( context, pDecal, surfID, pDecal->material );
  1771. if ( v )
  1772. Shader_DecalDrawPoly( v, pDecal->material, surfID, pDecal->clippedVertCount, pDecal, 1.0f );
  1773. }
  1774. if( retire )
  1775. {
  1776. R_DecalUnlink( pDecal, host_state.worldbrush );
  1777. }
  1778. }
  1779. //-----------------------------------------------------------------------------
  1780. // Renders all decals on a single surface
  1781. //-----------------------------------------------------------------------------
  1782. void DrawDecalsOnSingleSurface_NonQueued( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID, const Vector &vModelOrg)
  1783. {
  1784. decal_t* plist = MSurf_DecalPointer( surfID );
  1785. decalcontext_t context(pRenderContext, vModelOrg);
  1786. context.InitSurface(surfID);
  1787. while ( plist )
  1788. {
  1789. // Store off the next pointer, DecalUpdateAndDrawSingle could unlink
  1790. decal_t* pnext = plist->pnext;
  1791. if (!(plist->flags & FDECAL_SECONDPASS))
  1792. {
  1793. DecalUpdateAndDrawSingle( context, surfID, plist );
  1794. }
  1795. plist = pnext;
  1796. }
  1797. while ( plist )
  1798. {
  1799. // Store off the next pointer, DecalUpdateAndDrawSingle could unlink
  1800. decal_t* pnext = plist->pnext;
  1801. if ((plist->flags & FDECAL_SECONDPASS))
  1802. {
  1803. DecalUpdateAndDrawSingle( context, surfID, plist );
  1804. }
  1805. plist = pnext;
  1806. }
  1807. }
  1808. void DrawDecalsOnSingleSurface( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID )
  1809. {
  1810. DrawDecalsOnSingleSurface_NonQueued( pRenderContext, surfID, modelorg );
  1811. }
  1812. //-----------------------------------------------------------------------------
  1813. //
  1814. //-----------------------------------------------------------------------------
  1815. void R_DrawDecalsAllImmediate( IMatRenderContext *pRenderContext, int iGroup, int iTreeType, const Vector &vModelOrg, int nCheckCount, float flFade )
  1816. {
  1817. SurfaceHandle_t lastSurf = NULL;
  1818. decalcontext_t context(pRenderContext, vModelOrg);
  1819. int nSortTreeCount = g_aDecalSortTrees.Count();
  1820. bool bWireframe = ShouldDrawInWireFrameMode() || (r_drawdecals.GetInt() == 2);
  1821. for ( int iSortTree = 0; iSortTree < nSortTreeCount; ++iSortTree )
  1822. {
  1823. int nBucketCount = g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Count();
  1824. for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket )
  1825. {
  1826. if ( g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_nCheckCount != nCheckCount )
  1827. continue;
  1828. intp iHead = g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_iHead;
  1829. int nCount;
  1830. intp iElement = iHead;
  1831. while ( iElement != g_aDecalSortPool.InvalidIndex() )
  1832. {
  1833. decal_t *pDecal = g_aDecalSortPool.Element( iElement );
  1834. iElement = g_aDecalSortPool.Next( iElement );
  1835. if ( ShouldSkipDecalRender( pDecal ) )
  1836. continue;
  1837. if ( pDecal->surfID != lastSurf )
  1838. {
  1839. lastSurf = pDecal->surfID;
  1840. }
  1841. CDecalVert *pVerts = R_DecalSetupVerts( context, pDecal, pDecal->surfID, pDecal->material );
  1842. if ( !pVerts )
  1843. continue;
  1844. nCount = pDecal->clippedVertCount;
  1845. // Bind texture.
  1846. VertexFormat_t vertexFormat = 0;
  1847. if( bWireframe )
  1848. {
  1849. pRenderContext->Bind( g_materialDecalWireframe );
  1850. }
  1851. else
  1852. {
  1853. pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( pDecal->surfID )].lightmapPageID );
  1854. pRenderContext->Bind( pDecal->material, pDecal->userdata );
  1855. vertexFormat = GetUncompressedFormat( pDecal->material );
  1856. }
  1857. IMesh *pMesh = NULL;
  1858. pMesh = pRenderContext->GetDynamicMesh();
  1859. CMeshBuilder meshBuilder;
  1860. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nCount, ( ( nCount - 2 ) * 3 ) );
  1861. // Set base color.
  1862. byte color[4] = { pDecal->color.r, pDecal->color.g, pDecal->color.b, pDecal->color.a };
  1863. if ( flFade != 1.0f )
  1864. {
  1865. color[3] = (byte)( color[3] * flFade );
  1866. }
  1867. // Dynamic decals - fading.
  1868. if ( pDecal->flags & FDECAL_DYNAMIC )
  1869. {
  1870. float flFadeValue;
  1871. // Negative fadeDuration value means to fade in
  1872. if ( pDecal->fadeDuration < 0 )
  1873. {
  1874. flFadeValue = -( GetBaseLocalClient().GetTime() - pDecal->fadeStartTime ) / pDecal->fadeDuration;
  1875. }
  1876. else
  1877. {
  1878. flFadeValue = 1.0 - ( GetBaseLocalClient().GetTime() - pDecal->fadeStartTime ) / pDecal->fadeDuration;
  1879. }
  1880. flFadeValue = clamp( flFadeValue, 0.0f, 1.0f );
  1881. color[3] = ( byte )( color[3] * flFadeValue );
  1882. }
  1883. // Compute normal and tangent space if necessary.
  1884. Vector vecNormal( 0.0f, 0.0f, 1.0f ), vecTangentS( 1.0f, 0.0f, 0.0f ), vecTangentT( 0.0f, 1.0f, 0.0f );
  1885. if ( vertexFormat & ( VERTEX_NORMAL | VERTEX_TANGENT_SPACE ) )
  1886. {
  1887. vecNormal = MSurf_Plane( pDecal->surfID ).normal;
  1888. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  1889. {
  1890. Vector tVect;
  1891. bool bNegate = TangentSpaceSurfaceSetup( pDecal->surfID, tVect );
  1892. TangentSpaceComputeBasis( vecTangentS, vecTangentT, vecNormal, tVect, bNegate );
  1893. }
  1894. }
  1895. // Setup verts.
  1896. float flOffset = pDecal->lightmapOffset;
  1897. for ( int iVert = 0; iVert < nCount; ++iVert, ++pVerts )
  1898. {
  1899. meshBuilder.Position3fv( pVerts->m_vPos.Base() );
  1900. if ( vertexFormat & VERTEX_NORMAL )
  1901. {
  1902. meshBuilder.Normal3fv( vecNormal.Base() );
  1903. }
  1904. meshBuilder.Color4ubv( color );
  1905. meshBuilder.TexCoord2f( 0, pVerts->m_ctCoords.x, pVerts->m_ctCoords.y );
  1906. meshBuilder.TexCoord2f( 1, pVerts->m_cLMCoords.x, pVerts->m_cLMCoords.y );
  1907. meshBuilder.TexCoord1f( 2, flOffset );
  1908. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  1909. {
  1910. meshBuilder.TangentS3fv( vecTangentS.Base() );
  1911. meshBuilder.TangentT3fv( vecTangentT.Base() );
  1912. }
  1913. meshBuilder.AdvanceVertexF<VTX_HAVEPOS|VTX_HAVENORMAL|VTX_HAVECOLOR,3>();
  1914. }
  1915. // Setup indices.
  1916. int nTriCount = ( nCount - 2 );
  1917. CIndexBuilder &indexBuilder = meshBuilder;
  1918. indexBuilder.FastPolygon( 0, nTriCount );
  1919. meshBuilder.End();
  1920. pMesh->Draw();
  1921. }
  1922. }
  1923. }
  1924. }
  1925. //-----------------------------------------------------------------------------
  1926. //
  1927. //-----------------------------------------------------------------------------
  1928. inline void R_DrawDecalMeshList( DecalMeshList_t &meshList )
  1929. {
  1930. CMatRenderContextPtr pRenderContext( materials );
  1931. int nBatchCount = meshList.m_aBatches.Count();
  1932. for ( int iBatch = 0; iBatch < nBatchCount; ++iBatch )
  1933. {
  1934. if ( g_pMaterialSystemConfig->nFullbright == 1 )
  1935. {
  1936. pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE );
  1937. }
  1938. else
  1939. {
  1940. pRenderContext->BindLightmapPage( meshList.m_aBatches[iBatch].m_iLightmapPage );
  1941. }
  1942. pRenderContext->Bind( meshList.m_aBatches[iBatch].m_pMaterial, meshList.m_aBatches[iBatch].m_pProxy );
  1943. meshList.m_pMesh->Draw( meshList.m_aBatches[iBatch].m_iStartIndex, meshList.m_aBatches[iBatch].m_nIndexCount );
  1944. }
  1945. }
  1946. void R_DrawDecalsAll( IMatRenderContext *pRenderContext, int iGroup, int iTreeType, const Vector &vModelOrg, int nCheckCount, float flFade )
  1947. {
  1948. int nSortTreeCount = g_aDecalSortTrees.Count();
  1949. if ( !nSortTreeCount )
  1950. return;
  1951. DecalMeshList_t meshList;
  1952. CMeshBuilder meshBuilder;
  1953. SurfaceHandle_t lastSurf = NULL;
  1954. decalcontext_t context(pRenderContext, vModelOrg);
  1955. Vector vecNormal( 0.0f, 0.0f, 1.0f ), vecTangentS( 1.0f, 0.0f, 0.0f ), vecTangentT( 0.0f, 1.0f, 0.0f );
  1956. int nVertCount = 0;
  1957. int nIndexCount = 0;
  1958. int nDecalSortMaxVerts = g_nMaxDecals * 5;
  1959. int nDecalSortMaxIndices = nDecalSortMaxVerts * 3;
  1960. // NOTE: This is sort of a hack. The decal wireframe material is 20 bytes; this assumes
  1961. // we're going to render vertices no larger than 40 bytes/vert.
  1962. nDecalSortMaxVerts = MIN( nDecalSortMaxVerts, pRenderContext->GetMaxVerticesToRender( g_materialDecalWireframe ) / 4 );
  1963. nDecalSortMaxIndices = MIN( nDecalSortMaxIndices, pRenderContext->GetMaxIndicesToRender() );
  1964. bool bWireframe = ShouldDrawInWireFrameMode() || (r_drawdecals.GetInt() == 2);
  1965. float localClientTime = GetBaseLocalClient().GetTime();
  1966. for ( int iSortTree = 0; iSortTree < nSortTreeCount; ++iSortTree )
  1967. {
  1968. // Reset the mesh list.
  1969. bool bMeshInit = true;
  1970. uint16 unPlayerDecalStickerKitID = 0; // player decals must be split into separate calls by actual basetexture, but overall keep the state so they are bucketed by same material
  1971. DecalSortTrees_t &sortTree = g_aDecalSortTrees[iSortTree];
  1972. int nBucketCount = sortTree.m_aDecalSortBuckets[iGroup][iTreeType].Count();
  1973. for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket )
  1974. {
  1975. DecalMaterialBucket_t &bucket = sortTree.m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket );
  1976. if ( bucket.m_nCheckCount != nCheckCount )
  1977. continue;
  1978. intp iHead = bucket.m_iHead;
  1979. if ( !g_aDecalSortPool.IsValidIndex( iHead ) )
  1980. continue;
  1981. decal_t *pDecalHead = g_aDecalSortPool.Element( iHead );
  1982. Assert( pDecalHead->material );
  1983. if ( !pDecalHead->material )
  1984. continue;
  1985. // Vertex format.
  1986. VertexFormat_t vertexFormat = GetUncompressedFormat( pDecalHead->material );
  1987. if ( vertexFormat == 0 )
  1988. continue;
  1989. // New bucket = new batch.
  1990. DecalBatchList_t *pBatch = NULL;
  1991. bool bBatchInit = true;
  1992. int nCount;
  1993. intp iElement = iHead;
  1994. while ( iElement != g_aDecalSortPool.InvalidIndex() )
  1995. {
  1996. decal_t *pDecal = g_aDecalSortPool.Element( iElement );
  1997. iElement = g_aDecalSortPool.Next( iElement );
  1998. if ( ShouldSkipDecalRender( pDecal ) )
  1999. continue;
  2000. float flOffset = 0;
  2001. if ( pDecal->surfID != lastSurf )
  2002. {
  2003. lastSurf = pDecal->surfID;
  2004. flOffset = pDecal->lightmapOffset;
  2005. // Compute normal and tangent space if necessary.
  2006. if ( vertexFormat & ( VERTEX_NORMAL | VERTEX_TANGENT_SPACE ) )
  2007. {
  2008. vecNormal = MSurf_Plane( pDecal->surfID ).normal;
  2009. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  2010. {
  2011. Vector tVect;
  2012. bool bNegate = TangentSpaceSurfaceSetup( pDecal->surfID, tVect );
  2013. TangentSpaceComputeBasis( vecTangentS, vecTangentT, vecNormal, tVect, bNegate );
  2014. }
  2015. }
  2016. }
  2017. CDecalVert *pVerts = R_DecalSetupVerts( context, pDecal, pDecal->surfID, pDecal->material );
  2018. if ( !pVerts )
  2019. continue;
  2020. nCount = pDecal->clippedVertCount;
  2021. if ( ( nCount > nDecalSortMaxVerts ) || ( ( nCount - 2 ) > nDecalSortMaxIndices ) )
  2022. {
  2023. // The overflow case below causes a crash if the overflow occurs on the first iteration
  2024. // due to the meshBuilder.End() not getting setup with an m_pMesh. Instead, just ignore this
  2025. // decal's clipped vertexes.
  2026. if ( bMeshInit )
  2027. {
  2028. // this would have caused otherwise caused a crash because the meshbuilder has not been setup yet
  2029. DevWarning( "R_DrawDecalsAll: Overflow of mesh builder (%d, %d, %d, %d %d), skipping this decal!\n", nVertCount, nIndexCount, nCount, nDecalSortMaxVerts, nDecalSortMaxIndices );
  2030. }
  2031. continue;
  2032. }
  2033. // Overflow - new mesh, batch.
  2034. if ( ( ( nVertCount + nCount ) > nDecalSortMaxVerts ) || ( nIndexCount + ( nCount - 2 ) > nDecalSortMaxIndices )
  2035. || ( !bMeshInit && ( pDecal->flags & FDECAL_PLAYERSPRAY ) && ( uint16( reinterpret_cast< uintp >( pDecal->userdata ) ) != unPlayerDecalStickerKitID ) ) )
  2036. {
  2037. // Finish this batch.
  2038. if ( pBatch )
  2039. {
  2040. pBatch->m_nIndexCount = ( nIndexCount - pBatch->m_iStartIndex );
  2041. }
  2042. // End the mesh building phase and render.
  2043. meshBuilder.End();
  2044. R_DrawDecalMeshList( meshList );
  2045. // Reset.
  2046. bMeshInit = true;
  2047. pBatch = NULL;
  2048. bBatchInit = true;
  2049. unPlayerDecalStickerKitID = 0;
  2050. }
  2051. // Create the mesh.
  2052. if ( bMeshInit )
  2053. {
  2054. // Reset the mesh list.
  2055. meshList.m_pMesh = NULL;
  2056. meshList.m_aBatches.RemoveAll();
  2057. // Create a mesh for this vertex format (vertex format per SortTree).
  2058. if ( bWireframe )
  2059. {
  2060. meshList.m_pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, g_materialDecalWireframe );
  2061. }
  2062. else
  2063. {
  2064. meshList.m_pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, pDecalHead->material );
  2065. }
  2066. meshBuilder.Begin( meshList.m_pMesh, MATERIAL_TRIANGLES, nDecalSortMaxVerts, nDecalSortMaxIndices );
  2067. nVertCount = 0;
  2068. nIndexCount = 0;
  2069. bMeshInit = false;
  2070. unPlayerDecalStickerKitID = ( pDecal->flags & FDECAL_PLAYERSPRAY )
  2071. ? uint16( reinterpret_cast< uintp >( pDecal->userdata ) )
  2072. : 0; // Keep track of playerdecal proxy state for batches roll over
  2073. }
  2074. // Create the batch.
  2075. if ( bBatchInit )
  2076. {
  2077. // Create a batch for this bucket = material/lightmap pair.
  2078. // Todo: we also could flush it right here and continue.
  2079. if ( meshList.m_aBatches.Count() + 1 > meshList.m_aBatches.NumAllocated() )
  2080. {
  2081. Warning( "R_DrawDecalsAll: overflowing m_aBatches. Reduce # of decals in the scene.\n" );
  2082. meshBuilder.End();
  2083. R_DrawDecalMeshList( meshList );
  2084. return;
  2085. }
  2086. int iBatchList = meshList.m_aBatches.AddToTail();
  2087. pBatch = &meshList.m_aBatches[iBatchList];
  2088. pBatch->m_iStartIndex = nIndexCount;
  2089. if ( bWireframe )
  2090. {
  2091. pBatch->m_pMaterial = g_materialDecalWireframe;
  2092. }
  2093. else
  2094. {
  2095. pBatch->m_pMaterial = pDecalHead->material;
  2096. if ( pDecal->flags & FDECAL_PLAYERSPRAY )
  2097. pBatch->m_pProxy = pDecal->userdata; // Player sprays must use individual proxies, probably all materials can, but this is a safe change
  2098. else
  2099. pBatch->m_pProxy = pDecalHead->userdata;
  2100. pBatch->m_iLightmapPage = materialSortInfoArray[MSurf_MaterialSortID( pDecalHead->surfID )].lightmapPageID;
  2101. }
  2102. bBatchInit = false;
  2103. }
  2104. Assert ( pBatch );
  2105. // Set base color.
  2106. byte color[4] = { pDecal->color.r, pDecal->color.g, pDecal->color.b, pDecal->color.a };
  2107. if ( flFade != 1.0f )
  2108. {
  2109. color[3] = (byte)( color[3] * flFade );
  2110. }
  2111. // Dynamic decals - fading.
  2112. if ( pDecal->flags & FDECAL_DYNAMIC )
  2113. {
  2114. float flFadeValue;
  2115. // Negative fadeDuration value means to fade in
  2116. if ( pDecal->fadeDuration < 0 )
  2117. {
  2118. flFadeValue = -( localClientTime - pDecal->fadeStartTime ) / pDecal->fadeDuration;
  2119. }
  2120. else
  2121. {
  2122. flFadeValue = 1.0 - ( localClientTime - pDecal->fadeStartTime ) / pDecal->fadeDuration;
  2123. }
  2124. flFadeValue = clamp( flFadeValue, 0.0f, 1.0f );
  2125. color[3] = ( byte )( color[3] * flFadeValue );
  2126. }
  2127. // Setup verts.
  2128. for ( int iVert = 0; iVert < nCount; ++iVert, ++pVerts )
  2129. {
  2130. meshBuilder.Position3fv( pVerts->m_vPos.Base() );
  2131. if ( vertexFormat & VERTEX_NORMAL )
  2132. {
  2133. meshBuilder.Normal3fv( vecNormal.Base() );
  2134. }
  2135. meshBuilder.Color4ubv( color );
  2136. meshBuilder.TexCoord2f( 0, pVerts->m_ctCoords.x, pVerts->m_ctCoords.y );
  2137. meshBuilder.TexCoord2f( 1, pVerts->m_cLMCoords.x, pVerts->m_cLMCoords.y );
  2138. meshBuilder.TexCoord1f( 2, flOffset );
  2139. if ( vertexFormat & VERTEX_TANGENT_SPACE )
  2140. {
  2141. meshBuilder.TangentS3fv( vecTangentS.Base() );
  2142. meshBuilder.TangentT3fv( vecTangentT.Base() );
  2143. }
  2144. meshBuilder.AdvanceVertexF<VTX_HAVEALL, 3>();
  2145. }
  2146. // Setup indices.
  2147. int nTriCount = nCount - 2;
  2148. CIndexBuilder &indexBuilder = meshBuilder;
  2149. indexBuilder.FastPolygon( nVertCount, nTriCount );
  2150. // Update counters.
  2151. nVertCount += nCount;
  2152. nIndexCount += ( nTriCount * 3 );
  2153. }
  2154. if ( pBatch )
  2155. {
  2156. pBatch->m_nIndexCount = ( nIndexCount - pBatch->m_iStartIndex );
  2157. }
  2158. }
  2159. if ( !bMeshInit )
  2160. {
  2161. meshBuilder.End();
  2162. R_DrawDecalMeshList( meshList );
  2163. nVertCount = 0;
  2164. nIndexCount = 0;
  2165. }
  2166. }
  2167. }
  2168. //-----------------------------------------------------------------------------
  2169. // Purpose:
  2170. //-----------------------------------------------------------------------------
  2171. void DecalSurfaceDraw( IMatRenderContext *pRenderContext, int renderGroup, float flFade )
  2172. {
  2173. // VPROF_BUDGET( "Decals", "Decals" );
  2174. VPROF( "DecalsDraw" );
  2175. if( !r_drawdecals.GetBool() )
  2176. {
  2177. return;
  2178. }
  2179. int nCheckCount = g_nDecalSortCheckCount;
  2180. if ( renderGroup == MAX_MAT_SORT_GROUPS )
  2181. {
  2182. // Brush Model
  2183. nCheckCount = g_nBrushModelDecalSortCheckCount;
  2184. }
  2185. if ( r_drawbatchdecals.GetBool() )
  2186. {
  2187. // Draw world decals.
  2188. R_DrawDecalsAll( pRenderContext, renderGroup, PERMANENT_LIGHTMAP, modelorg, nCheckCount, flFade );
  2189. // Draw lightmapped non-world decals.
  2190. R_DrawDecalsAll( pRenderContext, renderGroup, LIGHTMAP, modelorg, nCheckCount, flFade );
  2191. // Draw non-lit(mod2x) decals.
  2192. R_DrawDecalsAll( pRenderContext, renderGroup, NONLIGHTMAP, modelorg, nCheckCount, flFade );
  2193. }
  2194. else
  2195. {
  2196. // Draw world decals.
  2197. R_DrawDecalsAllImmediate( pRenderContext, renderGroup, PERMANENT_LIGHTMAP, modelorg, nCheckCount, flFade );
  2198. // Draw lightmapped non-world decals.
  2199. R_DrawDecalsAllImmediate( pRenderContext, renderGroup, LIGHTMAP, modelorg, nCheckCount, flFade );
  2200. // Draw non-lit(mod2x) decals.
  2201. R_DrawDecalsAllImmediate( pRenderContext, renderGroup, NONLIGHTMAP, modelorg, nCheckCount, flFade );
  2202. }
  2203. }
  2204. //-----------------------------------------------------------------------------
  2205. // Purpose: Add decals to sorted decal list.
  2206. //-----------------------------------------------------------------------------
  2207. void DecalSurfaceAdd( SurfaceHandle_t surfID, int iGroup )
  2208. {
  2209. // Performance analysis.
  2210. // VPROF_BUDGET( "Decals", "Decals" );
  2211. VPROF( "DecalsBatch" );
  2212. // Go through surfaces decal list and add them to the correct lists.
  2213. decal_t *pDecalList = MSurf_DecalPointer( surfID );
  2214. if ( !pDecalList )
  2215. return;
  2216. int nCheckCount = g_nDecalSortCheckCount;
  2217. if ( iGroup == MAX_MAT_SORT_GROUPS )
  2218. {
  2219. // Brush Model
  2220. nCheckCount = g_nBrushModelDecalSortCheckCount;
  2221. }
  2222. int iTreeType = -1;
  2223. decal_t *pNext = NULL;
  2224. for ( decal_t *pDecal = pDecalList; pDecal; pDecal = pNext )
  2225. {
  2226. // Get the next pointer.
  2227. pNext = pDecal->pnext;
  2228. // Lightmap decals.
  2229. if ( pDecal->material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ) )
  2230. {
  2231. // Permanent lightmapped decals.
  2232. if ( pDecal->flags & FDECAL_PERMANENT )
  2233. {
  2234. iTreeType = PERMANENT_LIGHTMAP;
  2235. }
  2236. // Non-permanent lightmapped decals.
  2237. else
  2238. {
  2239. iTreeType = LIGHTMAP;
  2240. }
  2241. }
  2242. // Non-lightmapped decals.
  2243. else
  2244. {
  2245. iTreeType = NONLIGHTMAP;
  2246. }
  2247. pDecal->flags &= ~FDECAL_HASUPDATED;
  2248. intp iPool = g_aDecalSortPool.Alloc( true );
  2249. if ( iPool != g_aDecalSortPool.InvalidIndex() )
  2250. {
  2251. g_aDecalSortPool[iPool] = pDecal;
  2252. DecalSortTrees_t &sortTree = g_aDecalSortTrees[ pDecal->m_iSortTree ];
  2253. DecalMaterialBucket_t &bucket = sortTree.m_aDecalSortBuckets[iGroup][iTreeType].Element( pDecal->m_iSortMaterial );
  2254. if ( bucket.m_nCheckCount == nCheckCount )
  2255. {
  2256. // Brush decals render front-to-back, so link after tail
  2257. intp iTail = bucket.m_iTail;
  2258. g_aDecalSortPool.LinkAfter( iTail, iPool );
  2259. bucket.m_iTail = iPool;
  2260. }
  2261. else
  2262. {
  2263. bucket.m_iHead = bucket.m_iTail = iPool;
  2264. bucket.m_nCheckCount = nCheckCount;
  2265. }
  2266. }
  2267. }
  2268. }
  2269. #pragma check_stack(off)
  2270. #endif // DEDICATED