Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3038 lines
88 KiB

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