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

2460 lines
82 KiB

  1. //===== Copyright 1996-2008, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "studiorender.h"
  7. #include "studiorendercontext.h"
  8. #include "materialsystem/imaterialsystem.h"
  9. #include "materialsystem/imaterialsystemhardwareconfig.h"
  10. #include "materialsystem/imesh.h"
  11. #include "materialsystem/imaterial.h"
  12. #include "mathlib/mathlib.h"
  13. #include "optimize.h"
  14. #include "cmodel.h"
  15. #include "materialsystem/imaterialvar.h"
  16. #include "convar.h"
  17. #include "engine/decal_flags.h"
  18. #include "tier0/vprof.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. static int g_nTotalDecalVerts;
  22. //-----------------------------------------------------------------------------
  23. // Decal triangle clip flags
  24. //-----------------------------------------------------------------------------
  25. enum
  26. {
  27. DECAL_CLIP_MINUSU = 0x1,
  28. DECAL_CLIP_MINUSV = 0x2,
  29. DECAL_CLIP_PLUSU = 0x4,
  30. DECAL_CLIP_PLUSV = 0x8,
  31. };
  32. enum DECAL_FLAGS
  33. {
  34. DECAL_PLAYERSPRAY = 0x1,
  35. DECAL_IMMEDIATECLEANUP = 0x2,
  36. DECAL_HASDRAWN = 0x4,
  37. };
  38. static inline int ConvertToPrivateDecalFlags( int nPublicDecalFlags )
  39. {
  40. int nPrivateFlags = 0;
  41. if ( ( nPublicDecalFlags & EDF_PLAYERSPRAY ) != 0 ) { nPublicDecalFlags &= ~EDF_PLAYERSPRAY; nPrivateFlags |= DECAL_PLAYERSPRAY; }
  42. if ( ( nPublicDecalFlags & EDF_IMMEDIATECLEANUP ) != 0 ) { nPublicDecalFlags &= ~EDF_IMMEDIATECLEANUP; nPrivateFlags |= DECAL_IMMEDIATECLEANUP; }
  43. // If this hits, we are missing conversion here.
  44. Assert( nPublicDecalFlags == 0 );
  45. return nPrivateFlags;
  46. }
  47. #define MAX_DECAL_INDICES_PER_MODEL 2048
  48. //-----------------------------------------------------------------------------
  49. // Triangle clipping state
  50. //-----------------------------------------------------------------------------
  51. struct DecalClipState_t
  52. {
  53. // Number of used vertices
  54. int m_VertCount;
  55. // Indices into the clip verts array of the used vertices
  56. int m_Indices[2][7];
  57. // Helps us avoid copying the m_Indices array by using double-buffering
  58. bool m_Pass;
  59. // Add vertices we've started with and had to generate due to clipping
  60. int m_ClipVertCount;
  61. DecalVertex_t m_ClipVerts[16];
  62. // Union of the decal triangle clip flags above for each vert
  63. int m_ClipFlags[16];
  64. DecalClipState_t() {}
  65. private:
  66. // Copy constructors are not allowed
  67. DecalClipState_t( const DecalClipState_t& src );
  68. };
  69. //-----------------------------------------------------------------------------
  70. //
  71. // Lovely decal code begins here... ABANDON ALL HOPE YE WHO ENTER!!!
  72. //
  73. //-----------------------------------------------------------------------------
  74. //-----------------------------------------------------------------------------
  75. // Functions to make vertex opaque
  76. //-----------------------------------------------------------------------------
  77. #ifdef COMPACT_DECAL_VERT
  78. #define GetVecTexCoord( v ) (v.operator Vector2D())
  79. #define GetVecNormal( v ) (v.operator Vector())
  80. #else
  81. #define GetVecTexCoord( v ) v
  82. #define GetVecNormal( v ) v
  83. #endif
  84. //-----------------------------------------------------------------------------
  85. // Remove decal from LRU
  86. //-----------------------------------------------------------------------------
  87. void CStudioRender::RemoveDecalListFromLRU( StudioDecalHandle_t h )
  88. {
  89. DecalLRUListIndex_t i, next;
  90. for ( i = m_DecalLRU.Head(); i != m_DecalLRU.InvalidIndex(); i = next )
  91. {
  92. next = m_DecalLRU.Next(i);
  93. if ( m_DecalLRU[i].m_hDecalHandle == h )
  94. {
  95. m_DecalLRU.Remove( i );
  96. }
  97. }
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Create, destroy list of decals for a particular model
  101. //-----------------------------------------------------------------------------
  102. StudioDecalHandle_t CStudioRender::CreateDecalList( studiohwdata_t *pHardwareData )
  103. {
  104. if ( !pHardwareData || pHardwareData->m_NumLODs <= 0 )
  105. return STUDIORENDER_DECAL_INVALID;
  106. // NOTE: This function is called directly without queueing
  107. m_DecalMutex.Lock();
  108. auto handle = m_DecalList.AddToTail();
  109. m_DecalMutex.Unlock();
  110. m_DecalList[handle].m_pHardwareData = pHardwareData;
  111. m_DecalList[handle].m_pLod = new DecalLod_t[pHardwareData->m_NumLODs];
  112. m_DecalList[handle].m_nLods = pHardwareData->m_NumLODs;
  113. for (int i = 0; i < pHardwareData->m_NumLODs; i++)
  114. {
  115. m_DecalList[handle].m_pLod[i].m_FirstMaterial = m_DecalMaterial.InvalidIndex();
  116. }
  117. return (StudioDecalHandle_t)handle;
  118. }
  119. void CStudioRender::DestroyDecalList( StudioDecalHandle_t hDecal )
  120. {
  121. if ( hDecal == STUDIORENDER_DECAL_INVALID )
  122. return;
  123. RemoveDecalListFromLRU( hDecal );
  124. intp h = (intp)hDecal;
  125. // Clean up
  126. for (int i = 0; i < m_DecalList[h].m_nLods; i++ )
  127. {
  128. // Blat out all geometry associated with all materials
  129. unsigned short mat = m_DecalList[h].m_pLod[i].m_FirstMaterial;
  130. unsigned short next;
  131. while (mat != m_DecalMaterial.InvalidIndex())
  132. {
  133. next = m_DecalMaterial.Next(mat);
  134. g_nTotalDecalVerts -= m_DecalMaterial[mat].m_Vertices.Count();
  135. m_DecalMaterial.Free(mat);
  136. mat = next;
  137. }
  138. }
  139. delete[] m_DecalList[h].m_pLod;
  140. m_DecalList[h].m_pLod = NULL;
  141. m_DecalMutex.Lock();
  142. m_DecalList.Remove( h );
  143. m_DecalMutex.Unlock();
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Transformation/Rotation for decals
  147. //-----------------------------------------------------------------------------
  148. inline bool CStudioRender::IsFrontFacing( DecalBuildInfo_t& build, const Vector * pnorm, const mstudioboneweight_t * pboneweight )
  149. {
  150. // NOTE: This only works to rotate normals if there's no scale in the
  151. // pose to world transforms. If we ever add scale, we'll need to
  152. // multiply by the inverse transpose of the pose to decal
  153. float z;
  154. if (pboneweight->numbones == 1)
  155. {
  156. z = DotProduct( pnorm->Base(), m_PoseToDecal[pboneweight->bone[0]][2] );
  157. }
  158. else
  159. {
  160. float zbone;
  161. z = 0;
  162. for (int i = 0; i < pboneweight->numbones; i++)
  163. {
  164. zbone = DotProduct( pnorm->Base(), m_PoseToDecal[pboneweight->bone[i]][2] );
  165. z += zbone * pboneweight->weight[i];
  166. }
  167. }
  168. return ( z >= build.m_flFrontFacingCosineCheck );
  169. }
  170. inline bool CStudioRender::IsDecalStartPointWithinZLimit( DecalBuildInfo_t& build, const Vector * ppos, const mstudioboneweight_t * pboneweight )
  171. {
  172. if ( build.m_AllowBehindPointOfImpact && !build.m_bEnforceProjectionRadiusZ )
  173. return true; // caller doesn't care about Z-projection
  174. // NOTE: This only works to rotate positions if there's no scale in the
  175. // pose to world transforms. If we ever add scale, we'll need to
  176. // multiply by the inverse transpose of the pose to decal
  177. float z = 0.0f;
  178. if ( pboneweight->numbones == 1 )
  179. {
  180. z = DotProduct( ppos->Base(), m_PoseToDecal[pboneweight->bone[0]][2] ) +
  181. m_PoseToDecal[pboneweight->bone[0]][2][3];
  182. }
  183. else
  184. {
  185. for (int i = 0; i < pboneweight->numbones; i++)
  186. {
  187. float zbone = DotProduct( ppos->Base(), m_PoseToDecal[pboneweight->bone[i]][2] ) +
  188. m_PoseToDecal[pboneweight->bone[i]][2][3];
  189. z += zbone * pboneweight->weight[i];
  190. }
  191. }
  192. if ( !build.m_AllowBehindPointOfImpact &&
  193. ( z > 0.0f ) ) // behind impact not allowed, but z is behind: prevent
  194. return false;
  195. if ( build.m_bEnforceProjectionRadiusZ && // enforcing Z, but too far from the origin of hit
  196. ( ( z < -build.m_Radius ) || ( z > build.m_Radius/2 ) ) )
  197. return false;
  198. // Z-checks passed
  199. return true;
  200. }
  201. inline bool CStudioRender::TransformToDecalSpace( DecalBuildInfo_t& build, const Vector& pos,
  202. mstudioboneweight_t *pboneweight, Vector2D& uv )
  203. {
  204. // NOTE: This only works to rotate normals if there's no scale in the
  205. // pose to world transforms. If we ever add scale, we'll need to
  206. // multiply by the inverse transpose of the pose to world
  207. if (pboneweight->numbones == 1)
  208. {
  209. uv.x = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[0]][0] ) +
  210. m_PoseToDecal[pboneweight->bone[0]][0][3];
  211. uv.y = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[0]][1] ) +
  212. m_PoseToDecal[pboneweight->bone[0]][1][3];
  213. }
  214. else
  215. {
  216. uv.x = uv.y = 0;
  217. float ubone, vbone;
  218. for (int i = 0; i < pboneweight->numbones; i++)
  219. {
  220. ubone = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[i]][0] ) +
  221. m_PoseToDecal[pboneweight->bone[i]][0][3];
  222. vbone = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[i]][1] ) +
  223. m_PoseToDecal[pboneweight->bone[i]][1][3];
  224. uv.x += ubone * pboneweight->weight[i];
  225. uv.y += vbone * pboneweight->weight[i];
  226. }
  227. }
  228. if (!build.m_NoPokeThru)
  229. return true;
  230. // No poke thru? do culling....
  231. float z;
  232. if (pboneweight->numbones == 1)
  233. {
  234. z = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[0]][2] ) +
  235. m_PoseToDecal[pboneweight->bone[0]][2][3];
  236. }
  237. else
  238. {
  239. z = 0;
  240. float zbone;
  241. for (int i = 0; i < pboneweight->numbones; i++)
  242. {
  243. zbone = DotProduct( pos.Base(), m_PoseToDecal[pboneweight->bone[i]][2] ) +
  244. m_PoseToDecal[pboneweight->bone[i]][2][3];
  245. z += zbone * pboneweight->weight[i];
  246. }
  247. }
  248. return (fabs(z) < build.m_flMaxDepth );
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Projects a decal onto a mesh
  252. //-----------------------------------------------------------------------------
  253. bool CStudioRender::ProjectDecalOntoMesh( DecalBuildInfo_t& build, DecalBuildVertexInfo_t* pVertexInfo, mstudiomesh_t *pMesh )
  254. {
  255. float invRadius = (build.m_Radius != 0.0f) ? 1.0f / build.m_Radius : 1.0f;
  256. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( build.m_pStudioHdr );
  257. const thinModelVertices_t *thinVertData = NULL;
  258. if ( !vertData )
  259. {
  260. // For most models (everything that's not got flex data), the vertex data is 'thinned' on load to save memory
  261. thinVertData = pMesh->GetThinVertexData( build.m_pStudioHdr );
  262. if ( !thinVertData )
  263. return false;
  264. }
  265. // For this to work, the plane and intercept must have been transformed
  266. // into pose space. Also, we'll not be bothering with flexes.
  267. for ( int j=0; j < pMesh->numvertices; ++j )
  268. {
  269. mstudioboneweight_t localBoneWeights;
  270. Vector localPosition;
  271. Vector localNormal;
  272. Vector * vecPosition;
  273. Vector * vecNormal;
  274. mstudioboneweight_t * boneWeights;
  275. if ( vertData )
  276. {
  277. mstudiovertex_t &vert = *vertData->Vertex( j );
  278. vecPosition = &vert.m_vecPosition;
  279. vecNormal = &vert.m_vecNormal;
  280. boneWeights = &vert.m_BoneWeights;
  281. }
  282. else
  283. {
  284. thinVertData->GetMeshPosition( pMesh, j, &localPosition );
  285. vecPosition = &localPosition;
  286. thinVertData->GetMeshNormal( pMesh, j, &localNormal );
  287. vecNormal = &localNormal;
  288. thinVertData->GetMeshBoneWeights( pMesh, j, &localBoneWeights );
  289. boneWeights = &localBoneWeights;
  290. }
  291. // No decal vertex yet...
  292. pVertexInfo[j].m_VertexIndex = 0xFFFF;
  293. pVertexInfo[j].m_UniqueID = 0xFF;
  294. pVertexInfo[j].m_Flags = 0;
  295. // We need to know if the normal is pointing in the negative direction
  296. // if so, blow off all triangles connected to that vertex.
  297. if ( !IsFrontFacing( build, vecNormal, boneWeights ) )
  298. continue;
  299. // We also need to check to make sure the source of the decal is not past this vertex.
  300. // This is for blood splatters that start from a player that is inside a large prop.
  301. if ( !IsDecalStartPointWithinZLimit( build, vecPosition, boneWeights ) )
  302. continue;
  303. pVertexInfo[j].m_Flags |= DecalBuildVertexInfo_t::FRONT_FACING;
  304. bool inValidArea = TransformToDecalSpace( build, *vecPosition, boneWeights, pVertexInfo[j].m_UV );
  305. pVertexInfo[j].m_Flags |= ( inValidArea << 1 );
  306. pVertexInfo[j].m_UV *= invRadius * 0.5f;
  307. pVertexInfo[j].m_UV[0] += 0.5f;
  308. pVertexInfo[j].m_UV[1] += 0.5f;
  309. }
  310. return true;
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Computes clip flags
  314. //-----------------------------------------------------------------------------
  315. inline int ComputeClipFlags( Vector2D const& uv )
  316. {
  317. // Otherwise we gotta do the test
  318. int flags = 0;
  319. if (uv.x < 0.0f)
  320. flags |= DECAL_CLIP_MINUSU;
  321. else if (uv.x > 1.0f)
  322. flags |= DECAL_CLIP_PLUSU;
  323. if (uv.y < 0.0f)
  324. flags |= DECAL_CLIP_MINUSV;
  325. else if (uv.y > 1.0f )
  326. flags |= DECAL_CLIP_PLUSV;
  327. return flags;
  328. }
  329. inline int CStudioRender::ComputeClipFlags( DecalBuildVertexInfo_t* pVertexInfo, int i )
  330. {
  331. return ::ComputeClipFlags( pVertexInfo[i].m_UV );
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Creates a new vertex where the edge intersects the plane
  335. //-----------------------------------------------------------------------------
  336. static int IntersectPlane( DecalClipState_t& state, int start, int end,
  337. int normalInd, float val )
  338. {
  339. DecalVertex_t& startVert = state.m_ClipVerts[start];
  340. DecalVertex_t& endVert = state.m_ClipVerts[end];
  341. Vector2D dir;
  342. Vector2DSubtract( endVert.m_TexCoord, startVert.m_TexCoord, dir );
  343. Assert( dir[normalInd] != 0.0f );
  344. float t = (val - GetVecTexCoord( startVert.m_TexCoord )[normalInd]) / dir[normalInd];
  345. // Allocate a clipped vertex
  346. DecalVertex_t& out = state.m_ClipVerts[state.m_ClipVertCount];
  347. int newVert = state.m_ClipVertCount++;
  348. // The clipped vertex has no analogue in the original mesh
  349. out.m_MeshVertexIndex = 0xFFFF;
  350. out.m_Mesh = 0xFFFF;
  351. out.m_Model = ( sizeof(out.m_Model) == 1 ) ? 0xFF : 0xFFFF;
  352. out.m_Body = ( sizeof(out.m_Body) == 1 ) ? 0xFF : 0xFFFF;
  353. // Interpolate position
  354. out.m_Position[0] = startVert.m_Position[0] * (1.0 - t) + endVert.m_Position[0] * t;
  355. out.m_Position[1] = startVert.m_Position[1] * (1.0 - t) + endVert.m_Position[1] * t;
  356. out.m_Position[2] = startVert.m_Position[2] * (1.0 - t) + endVert.m_Position[2] * t;
  357. // Interpolate normal
  358. Vector vNormal;
  359. // FIXME: this is a bug (it's using position data to compute interpolated normals!)... not seeing any obvious artifacts, though
  360. vNormal[0] = startVert.m_Position[0] * (1.0 - t) + endVert.m_Position[0] * t;
  361. vNormal[1] = startVert.m_Position[1] * (1.0 - t) + endVert.m_Position[1] * t;
  362. vNormal[2] = startVert.m_Position[2] * (1.0 - t) + endVert.m_Position[2] * t;
  363. VectorNormalize( vNormal );
  364. out.m_Normal = vNormal;
  365. // Interpolate texture coord
  366. Vector2D vTexCoord;
  367. Vector2DLerp( GetVecTexCoord( startVert.m_TexCoord ), GetVecTexCoord( endVert.m_TexCoord ), t, vTexCoord );
  368. out.m_TexCoord = vTexCoord;
  369. // Compute the clip flags baby...
  370. state.m_ClipFlags[newVert] = ComputeClipFlags( out.m_TexCoord );
  371. return newVert;
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Clips a triangle against a plane, use clip flags to speed it up
  375. //-----------------------------------------------------------------------------
  376. static void ClipTriangleAgainstPlane( DecalClipState_t& state, int normalInd, int flag, float val )
  377. {
  378. // FIXME: Could compute the & of all the clip flags of all the verts
  379. // as we go through the loop to do another early out
  380. // Ye Olde Sutherland-Hodgman clipping algorithm
  381. int outVertCount = 0;
  382. int start = state.m_Indices[state.m_Pass][state.m_VertCount - 1];
  383. bool startInside = (state.m_ClipFlags[start] & flag) == 0;
  384. for (int i = 0; i < state.m_VertCount; ++i)
  385. {
  386. int end = state.m_Indices[state.m_Pass][i];
  387. bool endInside = (state.m_ClipFlags[end] & flag) == 0;
  388. if (endInside)
  389. {
  390. if (!startInside)
  391. {
  392. int clipVert = IntersectPlane( state, start, end, normalInd, val );
  393. state.m_Indices[!state.m_Pass][outVertCount++] = clipVert;
  394. }
  395. state.m_Indices[!state.m_Pass][outVertCount++] = end;
  396. }
  397. else
  398. {
  399. if (startInside)
  400. {
  401. int clipVert = IntersectPlane( state, start, end, normalInd, val );
  402. state.m_Indices[!state.m_Pass][outVertCount++] = clipVert;
  403. }
  404. }
  405. start = end;
  406. startInside = endInside;
  407. }
  408. state.m_Pass = !state.m_Pass;
  409. state.m_VertCount = outVertCount;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Converts a mesh index to a DecalVertex_t
  413. //-----------------------------------------------------------------------------
  414. void CStudioRender::ConvertMeshVertexToDecalVertex( DecalBuildInfo_t& build,
  415. int meshIndex, DecalVertex_t& decalVertex, int nGroupIndex )
  416. {
  417. // Copy over the data;
  418. // get the texture coords from the decal planar projection
  419. Assert( meshIndex < MAXSTUDIOVERTS );
  420. if ( build.m_pMeshVertexData )
  421. {
  422. VectorCopy( *build.m_pMeshVertexData->Position( meshIndex ), decalVertex.m_Position );
  423. VectorCopy( *build.m_pMeshVertexData->Normal( meshIndex ), GetVecNormal( decalVertex.m_Normal ) );
  424. }
  425. else
  426. {
  427. // At this point in the code, we should definitely have either compressed or uncompressed vertex data
  428. Assert( build.m_pMeshThinVertexData );
  429. Vector position;
  430. Vector normal;
  431. build.m_pMeshThinVertexData->GetMeshPosition( build.m_pMesh, meshIndex, &position );
  432. build.m_pMeshThinVertexData->GetMeshNormal( build.m_pMesh, meshIndex, &normal );
  433. VectorCopy( position, decalVertex.m_Position );
  434. VectorCopy( normal, GetVecNormal( decalVertex.m_Normal ) );
  435. }
  436. Vector2DCopy( build.m_pVertexInfo[meshIndex].m_UV, GetVecTexCoord( decalVertex.m_TexCoord ) );
  437. decalVertex.m_MeshVertexIndex = meshIndex;
  438. decalVertex.m_Mesh = build.m_Mesh;
  439. Assert( decalVertex.m_Mesh < 100 );
  440. decalVertex.m_Model = build.m_Model;
  441. decalVertex.m_Body = build.m_Body;
  442. decalVertex.m_Group = build.m_Group;
  443. decalVertex.m_GroupIndex = nGroupIndex;
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Adds a vertex to the list of vertices for this material
  447. //-----------------------------------------------------------------------------
  448. inline unsigned short CStudioRender::AddVertexToDecal( DecalBuildInfo_t& build, int nMeshIndex, int nGroupIndex )
  449. {
  450. DecalBuildVertexInfo_t* pVertexInfo = build.m_pVertexInfo;
  451. // If we've never seen this vertex before, we need to add a new decal vert
  452. if ( pVertexInfo[nMeshIndex].m_UniqueID != build.m_nGlobalMeshIndex )
  453. {
  454. pVertexInfo[nMeshIndex].m_UniqueID = build.m_nGlobalMeshIndex;
  455. DecalVertexList_t& decalVertexList = build.m_pDecalMaterial->m_Vertices;
  456. DecalVertexList_t::IndexType_t v;
  457. v = decalVertexList.AddToTail();
  458. g_nTotalDecalVerts++;
  459. // Copy over the data;
  460. ConvertMeshVertexToDecalVertex( build, nMeshIndex, build.m_pDecalMaterial->m_Vertices[v], nGroupIndex );
  461. #ifdef _DEBUG
  462. // Make sure clipped vertices are in the right range...
  463. if (build.m_UseClipVert)
  464. {
  465. Assert( (decalVertexList[v].m_TexCoord[0] >= -1e-3) && (decalVertexList[v].m_TexCoord[0] - 1.0f < 1e-3) );
  466. Assert( (decalVertexList[v].m_TexCoord[1] >= -1e-3) && (decalVertexList[v].m_TexCoord[1] - 1.0f < 1e-3) );
  467. }
  468. #endif
  469. // Store off the index of this vertex so we can reference it again
  470. pVertexInfo[nMeshIndex].m_VertexIndex = build.m_VertexCount;
  471. ++build.m_VertexCount;
  472. if (build.m_FirstVertex == decalVertexList.InvalidIndex())
  473. {
  474. build.m_FirstVertex = v;
  475. }
  476. }
  477. return pVertexInfo[nMeshIndex].m_VertexIndex;
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Adds a vertex to the list of vertices for this material
  481. //-----------------------------------------------------------------------------
  482. inline unsigned short CStudioRender::AddVertexToDecal( DecalBuildInfo_t& build, DecalVertex_t& vert )
  483. {
  484. // This creates a unique vertex
  485. DecalVertexList_t& decalVertexList = build.m_pDecalMaterial->m_Vertices;
  486. // Try to see if the clipped vertex already exists in our decal list...
  487. // Only search for matches with verts appearing in the current decal
  488. DecalVertexList_t::IndexType_t i;
  489. unsigned short vertexCount = 0;
  490. for ( i = build.m_FirstVertex; i != decalVertexList.InvalidIndex();
  491. i = decalVertexList.Next(i), ++vertexCount )
  492. {
  493. // Only bother to check against clipped vertices
  494. if ( decalVertexList[i].GetMesh( build.m_pStudioHdr ) )
  495. continue;
  496. // They must have the same position, and normal
  497. // texcoord will fall right out if the positions match
  498. Vector temp;
  499. VectorSubtract( decalVertexList[i].m_Position, vert.m_Position, temp );
  500. if ( (fabs(temp[0]) > 1e-3) || (fabs(temp[1]) > 1e-3) || (fabs(temp[2]) > 1e-3) )
  501. continue;
  502. VectorSubtract( decalVertexList[i].m_Normal, vert.m_Normal, temp );
  503. if ( (fabs(temp[0]) > 1e-3) || (fabs(temp[1]) > 1e-3) || (fabs(temp[2]) > 1e-3) )
  504. continue;
  505. return vertexCount;
  506. }
  507. // This path is the path taken by clipped vertices
  508. Assert( (vert.m_TexCoord[0] >= -1e-3) && (vert.m_TexCoord[0] - 1.0f < 1e-3) );
  509. Assert( (vert.m_TexCoord[1] >= -1e-3) && (vert.m_TexCoord[1] - 1.0f < 1e-3) );
  510. // Must create a new vertex...
  511. DecalVertexList_t::IndexType_t idx = decalVertexList.AddToTail(vert);
  512. g_nTotalDecalVerts++;
  513. if (build.m_FirstVertex == decalVertexList.InvalidIndex())
  514. build.m_FirstVertex = idx;
  515. Assert( vertexCount == build.m_VertexCount );
  516. return build.m_VertexCount++;
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Adds the clipped triangle to the decal
  520. //-----------------------------------------------------------------------------
  521. void CStudioRender::AddClippedDecalToTriangle( DecalBuildInfo_t& build, DecalClipState_t& clipState )
  522. {
  523. // FIXME: Clipped vertices will almost always be shared. We
  524. // need a way of associating clipped vertices with edges so we can share
  525. // the clipped vertices quickly
  526. Assert( clipState.m_VertCount <= 7 );
  527. // Yeah baby yeah!! Add this sucka
  528. int i;
  529. unsigned short indices[7];
  530. for ( i = 0; i < clipState.m_VertCount; ++i)
  531. {
  532. // First add the vertices
  533. int vertIdx = clipState.m_Indices[clipState.m_Pass][i];
  534. if (vertIdx < 3)
  535. {
  536. indices[i] = AddVertexToDecal( build, clipState.m_ClipVerts[vertIdx].m_MeshVertexIndex );
  537. }
  538. else
  539. {
  540. indices[i] = AddVertexToDecal( build, clipState.m_ClipVerts[vertIdx] );
  541. }
  542. }
  543. // Add a trifan worth of triangles
  544. for ( i = 1; i < clipState.m_VertCount - 1; ++i)
  545. {
  546. MEM_ALLOC_CREDIT();
  547. build.m_pDecalMaterial->m_Indices.AddToTail( indices[0] );
  548. build.m_pDecalMaterial->m_Indices.AddToTail( indices[i] );
  549. build.m_pDecalMaterial->m_Indices.AddToTail( indices[i+1] );
  550. }
  551. }
  552. //-----------------------------------------------------------------------------
  553. // Clips the triangle to +/- radius
  554. //-----------------------------------------------------------------------------
  555. bool CStudioRender::ClipDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int *pClipFlags )
  556. {
  557. int i;
  558. DecalClipState_t clipState;
  559. clipState.m_VertCount = 3;
  560. ConvertMeshVertexToDecalVertex( build, i1, clipState.m_ClipVerts[0] );
  561. ConvertMeshVertexToDecalVertex( build, i2, clipState.m_ClipVerts[1] );
  562. ConvertMeshVertexToDecalVertex( build, i3, clipState.m_ClipVerts[2] );
  563. clipState.m_ClipVertCount = 3;
  564. for ( i = 0; i < 3; ++i)
  565. {
  566. clipState.m_ClipFlags[i] = pClipFlags[i];
  567. clipState.m_Indices[0][i] = i;
  568. }
  569. clipState.m_Pass = 0;
  570. // Clip against each plane
  571. ClipTriangleAgainstPlane( clipState, 0, DECAL_CLIP_MINUSU, 0.0f );
  572. if (clipState.m_VertCount < 3)
  573. return false;
  574. ClipTriangleAgainstPlane( clipState, 0, DECAL_CLIP_PLUSU, 1.0f );
  575. if (clipState.m_VertCount < 3)
  576. return false;
  577. ClipTriangleAgainstPlane( clipState, 1, DECAL_CLIP_MINUSV, 0.0f );
  578. if (clipState.m_VertCount < 3)
  579. return false;
  580. ClipTriangleAgainstPlane( clipState, 1, DECAL_CLIP_PLUSV, 1.0f );
  581. if (clipState.m_VertCount < 3)
  582. return false;
  583. // Only add the clipped decal to the triangle if it's one bone
  584. // otherwise just return if it was clipped
  585. if ( build.m_UseClipVert )
  586. {
  587. AddClippedDecalToTriangle( build, clipState );
  588. }
  589. return true;
  590. }
  591. //-----------------------------------------------------------------------------
  592. // Adds a decal to a triangle, but only if it should
  593. //-----------------------------------------------------------------------------
  594. void CStudioRender::AddTriangleToDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int gi1, int gi2, int gi3 )
  595. {
  596. DecalBuildVertexInfo_t* pVertexInfo = build.m_pVertexInfo;
  597. // All must be front-facing for a decal to be added
  598. // FIXME: Could make it work if not all are front-facing, need clipping for that
  599. int nAllFrontFacing = pVertexInfo[i1].m_Flags & pVertexInfo[i2].m_Flags & pVertexInfo[i3].m_Flags;
  600. if ( ( nAllFrontFacing & DecalBuildVertexInfo_t::FRONT_FACING ) == 0 )
  601. return;
  602. // This is used to prevent poke through; if the points are too far away
  603. // from the contact point, then don't add the decal
  604. int nAllNotInValidArea = pVertexInfo[i1].m_Flags | pVertexInfo[i2].m_Flags | pVertexInfo[i3].m_Flags;
  605. if ( ( nAllNotInValidArea & DecalBuildVertexInfo_t::VALID_AREA ) == 0 )
  606. return;
  607. // Clip to +/- radius
  608. int clipFlags[3];
  609. clipFlags[0] = ComputeClipFlags( pVertexInfo, i1 );
  610. clipFlags[1] = ComputeClipFlags( pVertexInfo, i2 );
  611. clipFlags[2] = ComputeClipFlags( pVertexInfo, i3 );
  612. // Cull... The result is non-zero if they're all outside the same plane
  613. if ( (clipFlags[0] & (clipFlags[1] & clipFlags[2]) ) != 0)
  614. return;
  615. bool doClip = true;
  616. // Trivial accept for skinned polys... if even one vert is inside
  617. // the draw region, accept
  618. if ((!build.m_UseClipVert) && ( !clipFlags[0] || !clipFlags[1] || !clipFlags[2] ))
  619. {
  620. doClip = false;
  621. }
  622. // Trivial accept... no clip flags set means all in
  623. // Don't clip if we have more than one bone... we'll need to do skinning
  624. // and we can't clip the bone indices
  625. // We *do* want to clip in the one bone case though; useful for large
  626. // static props.
  627. if ( doClip && ( clipFlags[0] || clipFlags[1] || clipFlags[2] ))
  628. {
  629. bool validTri = ClipDecal( build, i1, i2, i3, clipFlags );
  630. // Don't add the triangle if we culled the triangle or if
  631. // we had one or less bones
  632. if (build.m_UseClipVert || (!validTri))
  633. return;
  634. }
  635. // Add the vertices to the decal since there was no clipping
  636. i1 = AddVertexToDecal( build, i1, gi1 );
  637. i2 = AddVertexToDecal( build, i2, gi2 );
  638. i3 = AddVertexToDecal( build, i3, gi3 );
  639. MEM_ALLOC_CREDIT();
  640. build.m_pDecalMaterial->m_Indices.AddToTail(i1);
  641. build.m_pDecalMaterial->m_Indices.AddToTail(i2);
  642. build.m_pDecalMaterial->m_Indices.AddToTail(i3);
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Adds a decal to a mesh
  646. //-----------------------------------------------------------------------------
  647. void CStudioRender::AddDecalToMesh( DecalBuildInfo_t& build )
  648. {
  649. MeshVertexInfo_t &vertexInfo = build.m_pMeshVertices[ build.m_nGlobalMeshIndex ];
  650. if ( vertexInfo.m_nIndex < 0 )
  651. return;
  652. build.m_pVertexInfo = &build.m_pVertexBuffer[ vertexInfo.m_nIndex ];
  653. // Draw all the various mesh groups...
  654. for ( int j = 0; j < build.m_pMeshData->m_NumGroup; ++j )
  655. {
  656. build.m_Group = j;
  657. studiomeshgroup_t* pGroup = &build.m_pMeshData->m_pMeshGroup[j];
  658. // Must add decal to each strip in the strip group
  659. // We do this so we can re-use all of the bone state change
  660. // info associated with the strips
  661. for (int k = 0; k < pGroup->m_NumStrips; ++k)
  662. {
  663. OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[k];
  664. // JasonM TODO - handle quads/subds (just skip them for now)
  665. if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
  666. {
  667. for (int i = 0; i < pStrip->numIndices; i += 3)
  668. {
  669. int idx = pStrip->indexOffset + i;
  670. int i1 = pGroup->MeshIndex(idx);
  671. int i2 = pGroup->MeshIndex(idx+1);
  672. int i3 = pGroup->MeshIndex(idx+2);
  673. AddTriangleToDecal( build, i1, i2, i3, pGroup->m_pIndices[idx], pGroup->m_pIndices[idx+1], pGroup->m_pIndices[idx+2] );
  674. }
  675. }
  676. }
  677. }
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Adds a decal to a mesh
  681. //-----------------------------------------------------------------------------
  682. bool CStudioRender::AddDecalToModel( DecalBuildInfo_t& buildInfo )
  683. {
  684. // FIXME: We need to do some high-level culling to figure out exactly
  685. // which meshes we need to add the decals to
  686. // Turns out this solution may also be good for mesh sorting
  687. // we need to know the center of each mesh, could also store a
  688. // bounding radius for each mesh and test the ray against each sphere.
  689. for ( int i = 0; i < m_pSubModel->nummeshes; ++i)
  690. {
  691. buildInfo.m_Mesh = i;
  692. buildInfo.m_pMesh = m_pSubModel->pMesh(i);
  693. buildInfo.m_pMeshData = &m_pStudioMeshes[buildInfo.m_pMesh->meshid];
  694. Assert(buildInfo.m_pMeshData);
  695. // Grab either fat or thin vertex data
  696. buildInfo.m_pMeshVertexData = buildInfo.m_pMesh->GetVertexData( buildInfo.m_pStudioHdr );
  697. if ( buildInfo.m_pMeshVertexData == NULL )
  698. {
  699. buildInfo.m_pMeshThinVertexData = buildInfo.m_pMesh->GetThinVertexData( buildInfo.m_pStudioHdr );
  700. if ( !buildInfo.m_pMeshThinVertexData )
  701. return false;
  702. }
  703. AddDecalToMesh( buildInfo );
  704. ++buildInfo.m_nGlobalMeshIndex;
  705. }
  706. return true;
  707. }
  708. //-----------------------------------------------------------------------------
  709. // Computes the pose to decal plane transform
  710. //-----------------------------------------------------------------------------
  711. bool CStudioRender::ComputePoseToDecal( const Ray_t& ray, const Vector& up )
  712. {
  713. // Create a transform that projects world coordinates into a
  714. // basis for the decal
  715. matrix3x4_t worldToDecal;
  716. Vector decalU, decalV, decalN;
  717. // Get the z axis
  718. VectorMultiply( ray.m_Delta, -1.0f, decalN );
  719. if (VectorNormalize( decalN ) == 0.0f)
  720. return false;
  721. // Deal with the u axis
  722. CrossProduct( up, decalN, decalU );
  723. if ( VectorNormalize( decalU ) < 1e-3 )
  724. {
  725. // if up parallel or antiparallel to ray, deal...
  726. Vector fixup( up.y, up.z, up.x );
  727. CrossProduct( fixup, decalN, decalU );
  728. if ( VectorNormalize( decalU ) < 1e-3 )
  729. return false;
  730. }
  731. CrossProduct( decalU, decalN, decalV );
  732. // Since I want world-to-decal, I gotta take the inverse of the decal
  733. // to world. Assuming post-multiplying column vectors, the decal to world =
  734. // [ Ux Vx Nx | ray.m_Start[0] ]
  735. // [ Uy Vy Ny | ray.m_Start[1] ]
  736. // [ Uz Vz Nz | ray.m_Start[2] ]
  737. VectorCopy( decalU.Base(), worldToDecal[0] );
  738. VectorCopy( decalV.Base(), worldToDecal[1] );
  739. VectorCopy( decalN.Base(), worldToDecal[2] );
  740. worldToDecal[0][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[0] );
  741. worldToDecal[1][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[1] );
  742. worldToDecal[2][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[2] );
  743. // Compute transforms from pose space to decal plane space
  744. for ( int i = 0; i < m_pStudioHdr->numbones; i++)
  745. {
  746. ConcatTransforms( worldToDecal, m_PoseToWorld[i], m_PoseToDecal[i] );
  747. }
  748. return true;
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Gets the list of triangles for a particular material and lod
  752. //-----------------------------------------------------------------------------
  753. int CStudioRender::GetDecalMaterial( DecalLod_t& decalLod, IMaterial* pDecalMaterial, void *pvProxyUserData )
  754. {
  755. // Grab the material for this lod...
  756. unsigned short j;
  757. for ( j = decalLod.m_FirstMaterial; j != m_DecalMaterial.InvalidIndex(); j = m_DecalMaterial.Next(j) )
  758. {
  759. if ( (m_DecalMaterial[j].m_pMaterial == pDecalMaterial)
  760. && ( m_DecalMaterial[j].m_pvProxyUserData == pvProxyUserData ) )
  761. {
  762. return j;
  763. }
  764. }
  765. // If we got here, this must be the first time we saw this material
  766. j = m_DecalMaterial.Alloc( true );
  767. // Link it into the list of data for this lod
  768. if (decalLod.m_FirstMaterial != m_DecalMaterial.InvalidIndex() )
  769. m_DecalMaterial.LinkBefore( decalLod.m_FirstMaterial, j );
  770. decalLod.m_FirstMaterial = j;
  771. m_DecalMaterial[j].m_pMaterial = pDecalMaterial;
  772. m_DecalMaterial[j].m_pvProxyUserData = pvProxyUserData;
  773. return j;
  774. }
  775. //-----------------------------------------------------------------------------
  776. // Purpose:
  777. //-----------------------------------------------------------------------------
  778. void CStudioRender::RetireDecal( DecalModelList_t &list, DecalId_t nRetireID, int iLOD, int iMaxLOD )
  779. {
  780. {
  781. for ( DecalLRUListIndex_t i = m_DecalLRU.Head(); i != m_DecalLRU.InvalidIndex(); i = m_DecalLRU.Next( i ) )
  782. {
  783. if ( nRetireID == m_DecalLRU[ i ].m_nDecalId )
  784. {
  785. RetireDecalAtAddress( list, i, iLOD, iMaxLOD );
  786. return;
  787. }
  788. }
  789. }
  790. Assert( !"CStudioRender::RetireDecal - Never should get here." );
  791. }
  792. void CStudioRender::RetireDecalAtAddress( DecalModelList_t &list, DecalLRUListIndex_t lruAddress, int iLOD, int iMaxLOD )
  793. {
  794. // Remove it from the global LRU...
  795. Assert( m_DecalLRU.IsValidIndex( lruAddress ) );
  796. DecalId_t nRetireID = m_DecalLRU[ lruAddress ].m_nDecalId;
  797. m_DecalLRU.Remove( lruAddress );
  798. // Find the id to retire and retire all the decals with this id across all LODs.
  799. DecalHistoryList_t *pHistoryList = &list.m_pLod[iLOD].m_DecalHistory;
  800. Assert( pHistoryList->Count() );
  801. if ( !pHistoryList->Count() )
  802. return;
  803. DecalHistory_t *pDecalHistory = &pHistoryList->Element( pHistoryList->Head() );
  804. // Retire this decal in all lods.
  805. for ( int iLod = ( iMaxLOD - 1 ); iLod >= list.m_pHardwareData->m_RootLOD; --iLod )
  806. {
  807. pHistoryList = &list.m_pLod[iLod].m_DecalHistory;
  808. if ( !pHistoryList )
  809. continue;
  810. unsigned short iList = pHistoryList->Head();
  811. unsigned short iNext = pHistoryList->InvalidIndex();
  812. while ( iList != pHistoryList->InvalidIndex() )
  813. {
  814. iNext = pHistoryList->Next( iList );
  815. pDecalHistory = &pHistoryList->Element( iList );
  816. if ( !pDecalHistory || pDecalHistory->m_nId != nRetireID )
  817. {
  818. iList = iNext;
  819. continue;
  820. }
  821. // Find the decal material for the decal to remove
  822. DecalMaterial_t *pMaterial = &m_DecalMaterial[pDecalHistory->m_Material];
  823. if ( pMaterial )
  824. {
  825. // @Note!! Decals must be removed in the reverse order they are added. This code
  826. // assumes that the decal to remove is the oldest one on the model, and therefore
  827. // its vertices start at the head of the list
  828. DecalVertexList_t &vertices = pMaterial->m_Vertices;
  829. Decal_t &decalToRemove = pMaterial->m_Decals[pDecalHistory->m_Decal];
  830. // Now clear out the vertices referenced by the indices....
  831. DecalVertexList_t::IndexType_t next;
  832. DecalVertexList_t::IndexType_t vert = vertices.Head();
  833. Assert( vertices.Count() >= decalToRemove.m_VertexCount );
  834. int vertsToRemove = decalToRemove.m_VertexCount;
  835. while ( vertsToRemove > 0 )
  836. {
  837. // blat out the vertices
  838. next = vertices.Next( vert );
  839. vertices.Remove( vert );
  840. vert = next;
  841. g_nTotalDecalVerts--;
  842. --vertsToRemove;
  843. }
  844. if ( vertices.Count() == 0 )
  845. {
  846. vertices.Purge();
  847. }
  848. // FIXME: This does a memmove. How expensive is it?
  849. pMaterial->m_Indices.RemoveMultiple( 0, decalToRemove.m_IndexCount );
  850. if ( pMaterial->m_Indices.Count() == 0)
  851. {
  852. pMaterial->m_Indices.Purge();
  853. }
  854. // Remove the decal
  855. pMaterial->m_Decals.Remove( pDecalHistory->m_Decal );
  856. if ( pMaterial->m_Decals.Count() == 0)
  857. {
  858. #if 1
  859. pMaterial->m_Decals.Purge();
  860. #else
  861. if ( list.m_pLod[iLOD].m_FirstMaterial == pDecalHistory->m_Material )
  862. {
  863. list.m_pLod[iLOD].m_FirstMaterial = m_DecalMaterial.Next( pDecalHistory->m_Material );
  864. }
  865. m_DecalMaterial.Free( pDecalHistory->m_Material );
  866. #endif
  867. }
  868. }
  869. // Clear the decal out of the history
  870. pHistoryList->Remove( iList );
  871. // Next element.
  872. iList = iNext;
  873. }
  874. }
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Adds a decal to the history list
  878. //-----------------------------------------------------------------------------
  879. int CStudioRender::AddDecalToMaterialList( DecalMaterial_t* pMaterial )
  880. {
  881. DecalList_t& decalList = pMaterial->m_Decals;
  882. return decalList.AddToTail();
  883. }
  884. //-----------------------------------------------------------------------------
  885. // Total number of meshes we have to deal with
  886. //-----------------------------------------------------------------------------
  887. int CStudioRender::ComputeTotalMeshCount( int iRootLOD, int iMaxLOD, int body ) const
  888. {
  889. int nMeshCount = 0;
  890. for ( int k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  891. {
  892. mstudiomodel_t *pSubModel;
  893. R_StudioSetupModel( k, body, &pSubModel, m_pStudioHdr );
  894. nMeshCount += pSubModel->nummeshes;
  895. }
  896. nMeshCount *= iMaxLOD-iRootLOD+1;
  897. return nMeshCount;
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Set up the locations for vertices to use
  901. //-----------------------------------------------------------------------------
  902. int CStudioRender::ComputeVertexAllocation( int iMaxLOD, int body, studiohwdata_t *pHardwareData, MeshVertexInfo_t *pMeshVertices )
  903. {
  904. bool bSuppressTlucDecal = (m_pStudioHdr->flags & STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS) != 0;
  905. int nCurrMesh = 0;
  906. int nVertexCount = 0;
  907. for ( int i = iMaxLOD-1; i >= pHardwareData->m_RootLOD; i--)
  908. {
  909. IMaterial **ppMaterials = pHardwareData->m_pLODs[i].ppMaterials;
  910. for ( int k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  911. {
  912. mstudiomodel_t *pSubModel;
  913. R_StudioSetupModel( k, body, &pSubModel, m_pStudioHdr );
  914. for ( int meshID = 0; meshID < pSubModel->nummeshes; ++meshID, ++nCurrMesh)
  915. {
  916. mstudiomesh_t *pMesh = pSubModel->pMesh(meshID);
  917. pMeshVertices[nCurrMesh].m_pMesh = pMesh;
  918. int n;
  919. for ( n = nCurrMesh; --n >= 0; )
  920. {
  921. if ( pMeshVertices[n].m_pMesh == pMesh )
  922. {
  923. pMeshVertices[nCurrMesh].m_nIndex = pMeshVertices[n].m_nIndex;
  924. break;
  925. }
  926. }
  927. if ( n >= 0 )
  928. continue;
  929. // Don't add to the mesh if the mesh has a translucent material
  930. short *pSkinRef = m_pStudioHdr->pSkinref( 0 );
  931. IMaterial *pMaterial = ppMaterials[pSkinRef[pMesh->material]];
  932. if (bSuppressTlucDecal)
  933. {
  934. if (pMaterial->IsTranslucent())
  935. {
  936. pMeshVertices[nCurrMesh].m_nIndex = -1;
  937. continue;
  938. }
  939. }
  940. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SUPPRESS_DECALS ) || pMesh->numvertices == 0 )
  941. {
  942. pMeshVertices[nCurrMesh].m_nIndex = -1;
  943. continue;
  944. }
  945. pMeshVertices[nCurrMesh].m_nIndex = nVertexCount;
  946. nVertexCount += pMesh->numvertices;
  947. }
  948. }
  949. }
  950. return nVertexCount;
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Project decals onto all meshes
  954. //-----------------------------------------------------------------------------
  955. void CStudioRender::ProjectDecalsOntoMeshes( DecalBuildInfo_t& build, int nMeshCount )
  956. {
  957. int nMaxVertexIndex = -1;
  958. for ( int i = 0; i < nMeshCount; ++i )
  959. {
  960. int nIndex = build.m_pMeshVertices[i].m_nIndex;
  961. // No mesh, or have we already projected this?
  962. if (( nIndex < 0 ) || ( nIndex <= nMaxVertexIndex ))
  963. continue;
  964. nMaxVertexIndex = nIndex;
  965. // Project all vertices for this group into decal space
  966. ProjectDecalOntoMesh( build, &build.m_pVertexBuffer[ nIndex ], build.m_pMeshVertices[i].m_pMesh );
  967. }
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Tries to retire a decal and returns true if successfully retired one
  971. //-----------------------------------------------------------------------------
  972. bool CStudioRender::BTryToRetireDecal( StudioDecalHandle_t hDecal, DecalHistoryList_t *pHistoryList, bool bCanRetirePlayerSpray )
  973. {
  974. intp h = ( intp ) hDecal;
  975. DecalModelList_t& list = m_DecalList[ h ];
  976. int nRootLOD = list.m_pHardwareData->m_RootLOD;
  977. int nFinalLOD = list.m_pHardwareData->m_NumLODs;
  978. for ( unsigned short iList = pHistoryList->Head();
  979. iList != pHistoryList->InvalidIndex();
  980. iList = pHistoryList->Next( iList ) )
  981. {
  982. DecalHistory_t *pDecalHistory = &pHistoryList->Element( iList );
  983. if ( !pDecalHistory )
  984. continue;
  985. DecalLRUListIndex_t i = m_DecalLRU.Head();
  986. for ( ; i != m_DecalLRU.InvalidIndex(); i = m_DecalLRU.Next( i ) )
  987. {
  988. if ( pDecalHistory->m_nId == m_DecalLRU[ i ].m_nDecalId )
  989. {
  990. if ( !bCanRetirePlayerSpray && ( ( m_DecalLRU[ i ].m_nFlags & DECAL_PLAYERSPRAY ) != 0 ) )
  991. break; // the history entry is a player spray, do not retire it
  992. RetireDecalAtAddress( list, i, nRootLOD, nFinalLOD );
  993. return true;
  994. }
  995. }
  996. // Assert that we found and inspected that model decal
  997. Assert( i != m_DecalLRU.InvalidIndex() );
  998. }
  999. return false;
  1000. }
  1001. //-----------------------------------------------------------------------------
  1002. // Add decals to a decal list by doing a planar projection along the ray
  1003. //-----------------------------------------------------------------------------
  1004. static unsigned int s_DecalScaleVarCache = 0;
  1005. void CStudioRender::AddDecal( StudioDecalHandle_t hDecal, const StudioRenderContext_t& rc, matrix3x4_t *pBoneToWorld,
  1006. studiohdr_t *pStudioHdr, const Ray_t& ray, const Vector& decalUp, IMaterial* pDecalMaterial,
  1007. float radius, int body, bool noPokethru, int maxLODToDecal, void *pvProxyUserData, int nAdditionalDecalFlags )
  1008. {
  1009. VPROF( "CStudioRender::AddDecal" );
  1010. if ( IsPS3() ) // FIXME: <vitaliy> disabling decals on models since on PS3 vertex data is packed for EDGE!
  1011. return;
  1012. if ( hDecal == STUDIORENDER_DECAL_INVALID )
  1013. return;
  1014. // For each lod, build the decal list
  1015. intp h = (intp)hDecal;
  1016. DecalModelList_t& list = m_DecalList[h];
  1017. if ( list.m_pHardwareData->m_NumStudioMeshes == 0 )
  1018. return;
  1019. m_pRC = const_cast< StudioRenderContext_t* >( &rc );
  1020. m_pStudioHdr = pStudioHdr;
  1021. m_pBoneToWorld = pBoneToWorld;
  1022. // Bone to world must be set before calling AddDecal; it uses that here
  1023. // UNDONE: Use current LOD to cull matrices here?
  1024. ComputePoseToWorld( m_PoseToWorld, pStudioHdr, BONE_USED_BY_ANYTHING, m_pRC->m_ViewOrigin, m_pBoneToWorld );
  1025. // Compute transforms from pose space to decal plane space
  1026. if (!ComputePoseToDecal( ray, decalUp ))
  1027. {
  1028. m_pStudioHdr = NULL;
  1029. m_pRC = NULL;
  1030. m_pBoneToWorld = NULL;
  1031. return;
  1032. }
  1033. // Since we're adding this to a studio model, check the decal to see if
  1034. // there's an alternate form used for static props...
  1035. bool found;
  1036. IMaterialVar* pModelMaterialVar = pDecalMaterial->FindVar( "$modelmaterial", &found, false );
  1037. if (found)
  1038. {
  1039. IMaterial* pModelMaterial = g_pMaterialSystem->FindMaterial( pModelMaterialVar->GetStringValue(), TEXTURE_GROUP_DECAL, false );
  1040. if ( !IsErrorMaterial( pModelMaterial ) )
  1041. {
  1042. pDecalMaterial = pModelMaterial;
  1043. float scale = 1.0f;
  1044. // Compute scale of surface
  1045. IMaterialVar *pDecalScaleVar = pDecalMaterial->FindVarFast( "$decalScaleForModelMaterial", &s_DecalScaleVarCache );
  1046. if ( pDecalScaleVar )
  1047. {
  1048. scale = pDecalScaleVar->GetFloatValue();
  1049. radius *= scale;
  1050. }
  1051. }
  1052. }
  1053. // If this is a splatter decal, then we don't allow splattering on the faces in front of the source of the decal.
  1054. // This is mainly for large props where the player my splatter blood from inside a prop.
  1055. bool isPlayerSpray = ( EDF_PLAYERSPRAY & nAdditionalDecalFlags );
  1056. bool isSplatterDecal = false;
  1057. IMaterialVar* pSplatterVar = pDecalMaterial->FindVar( "$splatter", &found, false );
  1058. if (found)
  1059. {
  1060. isSplatterDecal = (pSplatterVar->GetIntValue() > 0);
  1061. }
  1062. // Get dynamic information from the material (fade start, fade time)
  1063. float fadeStartTime = 0.0f;
  1064. float fadeDuration = 0.0f;
  1065. // This sucker is state needed only when building decals
  1066. DecalBuildInfo_t buildInfo;
  1067. buildInfo.m_Radius = radius;
  1068. buildInfo.m_flMaxDepth = MAX( radius, 8 );
  1069. buildInfo.m_flFrontFacingCosineCheck = isPlayerSpray ? 0.25f /*75deg*/ : 0.1f /*86deg*/;
  1070. buildInfo.m_NoPokeThru = noPokethru;
  1071. buildInfo.m_pStudioHdr = pStudioHdr;
  1072. buildInfo.m_UseClipVert = ( m_pStudioHdr->numbones <= 1 ) && ( m_pStudioHdr->numflexdesc == 0 );
  1073. buildInfo.m_nGlobalMeshIndex = 0;
  1074. buildInfo.m_pMeshVertexData = NULL;
  1075. buildInfo.m_AllowBehindPointOfImpact = !isSplatterDecal;
  1076. buildInfo.m_bEnforceProjectionRadiusZ = isPlayerSpray; // <vitaliy> not sure why we don't always enforce Z depth restriction when projecting?
  1077. // Find out which LODs we're defacing
  1078. int iMaxLOD;
  1079. if ( maxLODToDecal == ADDDECAL_TO_ALL_LODS )
  1080. {
  1081. iMaxLOD = list.m_pHardwareData->m_NumLODs;
  1082. }
  1083. else
  1084. {
  1085. iMaxLOD = MIN( list.m_pHardwareData->m_NumLODs, maxLODToDecal );
  1086. }
  1087. // Allocate space for all projected mesh vertices. We do this to prevent
  1088. // re-projection of the same meshes when they appear in multiple LODs
  1089. int nMeshCount = ComputeTotalMeshCount( list.m_pHardwareData->m_RootLOD, iMaxLOD-1, body );
  1090. // NOTE: This is a consequence of the sizeof (m_UniqueID)
  1091. if ( nMeshCount >= 255 )
  1092. {
  1093. Warning("Unable to apply decals to model (%s), it has more than 255 unique meshes!\n", m_pStudioHdr->pszName() );
  1094. m_pStudioHdr = NULL;
  1095. m_pRC = NULL;
  1096. m_pBoneToWorld = NULL;
  1097. return;
  1098. }
  1099. if ( !IsGameConsole() )
  1100. {
  1101. buildInfo.m_pMeshVertices = (MeshVertexInfo_t*)stackalloc( nMeshCount * sizeof(MeshVertexInfo_t) );
  1102. int nVertexCount = ComputeVertexAllocation( iMaxLOD, body, list.m_pHardwareData, buildInfo.m_pMeshVertices );
  1103. buildInfo.m_pVertexBuffer = (DecalBuildVertexInfo_t*)stackalloc( nVertexCount * sizeof(DecalBuildVertexInfo_t) );
  1104. }
  1105. else
  1106. {
  1107. // Don't allocate on the stack
  1108. buildInfo.m_pMeshVertices = (MeshVertexInfo_t*)malloc( nMeshCount * sizeof(MeshVertexInfo_t) );
  1109. int nVertexCount = ComputeVertexAllocation( iMaxLOD, body, list.m_pHardwareData, buildInfo.m_pMeshVertices );
  1110. buildInfo.m_pVertexBuffer = (DecalBuildVertexInfo_t*)malloc( nVertexCount * sizeof(DecalBuildVertexInfo_t) );
  1111. }
  1112. // Project all mesh vertices
  1113. ProjectDecalsOntoMeshes( buildInfo, nMeshCount );
  1114. if ( IsGameConsole() )
  1115. {
  1116. while ( g_nTotalDecalVerts * sizeof(DecalVertex_t) > 256*1024 && m_DecalLRU.Head() != m_DecalLRU.InvalidIndex() )
  1117. {
  1118. DecalId_t nRetireID = m_DecalLRU[ m_DecalLRU.Head() ].m_nDecalId;
  1119. StudioDecalHandle_t hRetire = m_DecalLRU[ m_DecalLRU.Head() ].m_hDecalHandle;
  1120. DecalModelList_t &modelList = m_DecalList[(intp)hRetire];
  1121. RetireDecal( modelList, nRetireID, modelList.m_pHardwareData->m_RootLOD, modelList.m_pHardwareData->m_NumLODs );
  1122. }
  1123. }
  1124. // Global list of decals trimming
  1125. if ( m_DecalLRU.Count() >= MAX( m_pRC->m_Config.maxDecalsPerModel * 10, 150 ) )
  1126. {
  1127. for ( DecalLRUListIndex_t i = m_DecalLRU.Head(); i != m_DecalLRU.InvalidIndex(); i = m_DecalLRU.Next( i ) )
  1128. {
  1129. if ( ( m_DecalLRU[ i ].m_nFlags & DECAL_PLAYERSPRAY ) != 0 )
  1130. continue; // do not retire player sprays globally
  1131. StudioDecalHandle_t hRetire = m_DecalLRU[ i ].m_hDecalHandle;
  1132. DecalModelList_t &modelList = m_DecalList[ ( intp ) hRetire ];
  1133. RetireDecalAtAddress( modelList, i, modelList.m_pHardwareData->m_RootLOD, modelList.m_pHardwareData->m_NumLODs );
  1134. break;
  1135. }
  1136. }
  1137. // Check to see if we have too many decals on this model
  1138. // This assumes that every decal is applied to the root lod at least
  1139. DecalHistoryList_t *pHistoryList = &list.m_pLod[list.m_pHardwareData->m_RootLOD].m_DecalHistory;
  1140. if ( pHistoryList->Count() >= m_pRC->m_Config.maxDecalsPerModel )
  1141. {
  1142. ( void ) ( BTryToRetireDecal( hDecal, pHistoryList, false ) ||
  1143. BTryToRetireDecal( hDecal, pHistoryList, true ) );
  1144. }
  1145. // Search all LODs for an overflow condition and retire those also
  1146. for ( int i = iMaxLOD-1; i >= list.m_pHardwareData->m_RootLOD; i-- )
  1147. {
  1148. // Grab the list of all decals using the same material for this lod...
  1149. int materialIdx = GetDecalMaterial( list.m_pLod[i], pDecalMaterial, pvProxyUserData );
  1150. // Check to see if we should retire the decal
  1151. DecalMaterial_t *pDecalMaterial = &m_DecalMaterial[materialIdx];
  1152. while ( pDecalMaterial->m_Indices.Count() > MAX_DECAL_INDICES_PER_MODEL )
  1153. {
  1154. DecalHistoryList_t *pHistoryList = &list.m_pLod[i].m_DecalHistory;
  1155. if ( !( BTryToRetireDecal( hDecal, pHistoryList, false ) ||
  1156. BTryToRetireDecal( hDecal, pHistoryList, true ) ) )
  1157. break; // cannot retire any more!
  1158. }
  1159. }
  1160. // Gotta do this for all LODs
  1161. bool bAddedDecals = false;
  1162. for ( int i = iMaxLOD-1; i >= list.m_pHardwareData->m_RootLOD; i-- )
  1163. {
  1164. // Grab the list of all decals using the same material for this lod...
  1165. int materialIdx = GetDecalMaterial( list.m_pLod[i], pDecalMaterial, pvProxyUserData );
  1166. buildInfo.m_pDecalMaterial = &m_DecalMaterial[materialIdx];
  1167. // Grab the meshes for this lod
  1168. m_pStudioMeshes = list.m_pHardwareData->m_pLODs[i].m_pMeshData;
  1169. // Don't decal on meshes that are translucent if it's twopass
  1170. buildInfo.m_ppMaterials = list.m_pHardwareData->m_pLODs[i].ppMaterials;
  1171. // Set up info needed for vertex sharing
  1172. buildInfo.m_FirstVertex = buildInfo.m_pDecalMaterial->m_Vertices.InvalidIndex();
  1173. buildInfo.m_VertexCount = 0;
  1174. int prevIndexCount = buildInfo.m_pDecalMaterial->m_Indices.Count();
  1175. // Step over all body parts + add decals to em all!
  1176. int k;
  1177. for ( k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  1178. {
  1179. // Grab the model for this body part
  1180. int model = R_StudioSetupModel( k, body, &m_pSubModel, m_pStudioHdr );
  1181. buildInfo.m_Body = k;
  1182. buildInfo.m_Model = model;
  1183. if ( !AddDecalToModel( buildInfo ) )
  1184. break;
  1185. }
  1186. if ( k != m_pStudioHdr->numbodyparts )
  1187. continue;
  1188. int nDecalIndexCount = buildInfo.m_pDecalMaterial->m_Indices.Count() - prevIndexCount;
  1189. // Add this to the list of decals in this material
  1190. // Since vertices can be shared it is possible to have a decal with only indices (to verts already in the buffer)
  1191. if ( buildInfo.m_VertexCount || nDecalIndexCount )
  1192. {
  1193. if ( nDecalIndexCount > 16384 )
  1194. {
  1195. // don't add this decal
  1196. Warning( "Decal has more than 16384 indices! (%d) Not adding to %s.\n", nDecalIndexCount, m_pStudioHdr->pszName() );
  1197. // remove thiss decal from the decal materal
  1198. DecalMaterial_t *pMaterial = buildInfo.m_pDecalMaterial;
  1199. if ( pMaterial )
  1200. {
  1201. // the decal we just added is at the head of the lists, so we can just remove the proper count from the head of the lists to clean it up
  1202. DecalVertexList_t &vertices = pMaterial->m_Vertices;
  1203. // Now clear out the vertices referenced by the indices....
  1204. DecalVertexList_t::IndexType_t next;
  1205. DecalVertexList_t::IndexType_t vert = vertices.Head();
  1206. Assert( vertices.Count() >= buildInfo.m_VertexCount );
  1207. int vertsToRemove = buildInfo.m_VertexCount;
  1208. while ( vertsToRemove > 0 )
  1209. {
  1210. // blat out the vertices
  1211. next = vertices.Next( vert );
  1212. vertices.Remove( vert );
  1213. vert = next;
  1214. g_nTotalDecalVerts--;
  1215. --vertsToRemove;
  1216. }
  1217. if ( vertices.Count() == 0 )
  1218. {
  1219. vertices.Purge();
  1220. }
  1221. // FIXME: This does a memmove. How expensive is it?
  1222. pMaterial->m_Indices.RemoveMultiple( 0, nDecalIndexCount );
  1223. if ( pMaterial->m_Indices.Count() == 0)
  1224. {
  1225. pMaterial->m_Indices.Purge();
  1226. }
  1227. }
  1228. }
  1229. else
  1230. {
  1231. bAddedDecals = true;
  1232. Assert(nDecalIndexCount > 0);
  1233. int decalIndex = AddDecalToMaterialList( buildInfo.m_pDecalMaterial );
  1234. Decal_t& decal = buildInfo.m_pDecalMaterial->m_Decals[decalIndex];
  1235. decal.m_VertexCount = buildInfo.m_VertexCount;
  1236. decal.m_IndexCount = nDecalIndexCount;
  1237. decal.m_FadeStartTime = fadeStartTime;
  1238. decal.m_FadeDuration = fadeDuration;
  1239. // Add this decal to the history...
  1240. int h = list.m_pLod[i].m_DecalHistory.AddToTail();
  1241. list.m_pLod[i].m_DecalHistory[h].m_Material = materialIdx;
  1242. list.m_pLod[i].m_DecalHistory[h].m_Decal = decalIndex;
  1243. list.m_pLod[i].m_DecalHistory[h].m_nId = m_nDecalId;
  1244. list.m_pLod[i].m_DecalHistory[h].m_nPad = 0;
  1245. }
  1246. }
  1247. }
  1248. // Add to LRU
  1249. if ( bAddedDecals )
  1250. {
  1251. DecalLRUListIndex_t h = m_DecalLRU.AddToTail();
  1252. m_DecalLRU[h].m_nDecalId = m_nDecalId;
  1253. m_DecalLRU[h].m_hDecalHandle = hDecal;
  1254. m_DecalLRU[h].m_nFlags = ConvertToPrivateDecalFlags( nAdditionalDecalFlags );
  1255. // Increment count.
  1256. ++m_nDecalId;
  1257. }
  1258. if ( IsGameConsole() )
  1259. {
  1260. free( buildInfo.m_pMeshVertices );
  1261. free( buildInfo.m_pVertexBuffer );
  1262. }
  1263. m_pStudioHdr = NULL;
  1264. m_pRC = NULL;
  1265. m_pBoneToWorld = NULL;
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. //
  1269. // This code here is all about rendering the decals
  1270. //
  1271. //-----------------------------------------------------------------------------
  1272. //-----------------------------------------------------------------------------
  1273. // Inner loop for rendering decals that have a single bone
  1274. //-----------------------------------------------------------------------------
  1275. void CStudioRender::DrawSingleBoneDecals( CMeshBuilder& meshBuilder, const DecalMaterial_t& decalMaterial )
  1276. {
  1277. // We don't got no bones, so yummy yummy yum, just copy the data out
  1278. // Static props should go though this code path
  1279. const DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1280. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1281. {
  1282. const DecalVertex_t& vertex = verts[i];
  1283. meshBuilder.Position3fv( vertex.m_Position.Base() );
  1284. meshBuilder.Normal3fv( GetVecNormal( vertex.m_Normal ).Base() );
  1285. #if 0
  1286. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1287. {
  1288. float offset[2], scale[2];
  1289. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1290. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1291. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1292. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1293. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1294. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1295. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1296. }
  1297. else
  1298. #endif
  1299. {
  1300. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1301. }
  1302. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1303. // In this case, we use the model matrix to send down the single transform,
  1304. // so we don't have to send down any bone info per vertex.
  1305. Assert( meshBuilder.m_NumBoneWeights == 0 );
  1306. /*
  1307. meshBuilder.BoneWeight( 0, 1.0f );
  1308. meshBuilder.BoneWeight( 1, 0.0f );
  1309. meshBuilder.BoneMatrix( 0, 0 );
  1310. meshBuilder.BoneMatrix( 1, 0 );
  1311. meshBuilder.BoneMatrix( 2, 0 );
  1312. meshBuilder.BoneMatrix( 3, 0 );
  1313. */
  1314. meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR, 1>();
  1315. }
  1316. }
  1317. void CStudioRender::DrawSingleBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial )
  1318. {
  1319. // We don't got no bones, so yummy yummy yum, just copy the data out
  1320. // Static props should go though this code path
  1321. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1322. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1323. {
  1324. DecalVertex_t& vertex = verts[i];
  1325. // Clipped verts shouldn't come through here, only static props should use clipped
  1326. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1327. if (m_VertexCache.IsVertexFlexed( vertex.m_MeshVertexIndex ))
  1328. {
  1329. CachedPosNormTan_t* pFlexedVertex = m_VertexCache.GetFlexVertex( vertex.m_MeshVertexIndex );
  1330. meshBuilder.Position3fv( pFlexedVertex->m_Position.Base() );
  1331. meshBuilder.Normal3fv( pFlexedVertex->m_Normal.Base() );
  1332. }
  1333. else
  1334. {
  1335. meshBuilder.Position3fv( vertex.m_Position.Base() );
  1336. meshBuilder.Normal3fv( GetVecNormal( vertex.m_Normal ).Base() );
  1337. }
  1338. #if 0
  1339. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1340. {
  1341. float offset[2], scale[2];
  1342. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1343. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1344. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1345. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1346. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1347. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1348. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1349. }
  1350. else
  1351. #endif
  1352. {
  1353. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1354. }
  1355. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1356. // In this case, we use the model matrix to send down the single transform,
  1357. // so we don't have to send down any bone info per vertex.
  1358. Assert( meshBuilder.m_NumBoneWeights == 0 );
  1359. meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR, 1>();
  1360. }
  1361. }
  1362. //-----------------------------------------------------------------------------
  1363. // Inner loop for rendering decals that have multiple bones
  1364. //-----------------------------------------------------------------------------
  1365. bool CStudioRender::DrawMultiBoneDecals( CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr )
  1366. {
  1367. const thinModelVertices_t *thinVertData = NULL;
  1368. const mstudio_meshvertexdata_t *vertData = NULL;
  1369. mstudiomesh_t *pLastMesh = NULL;
  1370. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1371. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1372. {
  1373. DecalVertex_t& vertex = verts[i];
  1374. int n = vertex.m_MeshVertexIndex;
  1375. Assert( n < MAXSTUDIOVERTS );
  1376. mstudiomesh_t *pMesh = vertex.GetMesh( pStudioHdr );
  1377. Assert( pMesh );
  1378. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1379. if (m_VertexCache.IsVertexPositionCached( n ))
  1380. {
  1381. CachedPosNorm_t* pCachedVert = m_VertexCache.GetWorldVertex( n );
  1382. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1383. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1384. }
  1385. else
  1386. {
  1387. // Prevent the computation of this again....
  1388. m_VertexCache.SetupComputation(pMesh);
  1389. CachedPosNorm_t* pCachedVert = m_VertexCache.CreateWorldVertex( n );
  1390. if ( pLastMesh != pMesh )
  1391. {
  1392. // only if the mesh changes
  1393. pLastMesh = pMesh;
  1394. vertData = pMesh->GetVertexData( pStudioHdr );
  1395. if ( vertData )
  1396. thinVertData = NULL;
  1397. else
  1398. thinVertData = pMesh->GetThinVertexData( pStudioHdr );
  1399. }
  1400. if ( vertData )
  1401. {
  1402. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1403. // FIXME: could be faster to blend the matrices and then transform the pos+norm by the same matrix
  1404. R_StudioTransform( *vertData->Position( n ), pBoneWeights, m_PoseToWorld, pCachedVert->m_Position.AsVector3D() );
  1405. R_StudioRotate( *vertData->Normal( n ), pBoneWeights, m_PoseToWorld, pCachedVert->m_Normal.AsVector3D() );
  1406. }
  1407. else if ( thinVertData )
  1408. {
  1409. // Using compressed vertex data
  1410. mstudioboneweight_t boneWeights;
  1411. Vector position;
  1412. Vector normal;
  1413. thinVertData->GetMeshBoneWeights( pMesh, n, &boneWeights );
  1414. thinVertData->GetMeshPosition( pMesh, n, &position );
  1415. thinVertData->GetMeshNormal( pMesh, n, &normal );
  1416. R_StudioTransform( position, &boneWeights, m_PoseToWorld, pCachedVert->m_Position.AsVector3D() );
  1417. R_StudioRotate( normal, &boneWeights, m_PoseToWorld, pCachedVert->m_Normal.AsVector3D() );
  1418. }
  1419. else
  1420. {
  1421. return false;
  1422. }
  1423. // Add a little extra offset for hardware skinning; in that case
  1424. // we're doing software skinning for decals and it might not be quite right
  1425. VectorMA( pCachedVert->m_Position.AsVector3D(), 0.1, pCachedVert->m_Normal.AsVector3D(), pCachedVert->m_Position.AsVector3D() );
  1426. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1427. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1428. }
  1429. #if 0
  1430. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1431. {
  1432. float offset[2], scale[2];
  1433. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1434. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1435. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1436. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1437. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1438. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1439. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1440. }
  1441. else
  1442. #endif
  1443. {
  1444. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1445. }
  1446. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1447. // In this case, we use the model matrix to send down the single transform,
  1448. // so we don't have to send down any bone info per vertex.
  1449. Assert( meshBuilder.m_NumBoneWeights == 0 );
  1450. /*
  1451. meshBuilder.BoneWeight( 0, 1.0f );
  1452. meshBuilder.BoneWeight( 1, 0.0f );
  1453. meshBuilder.BoneWeight( 2, 0.0f );
  1454. meshBuilder.BoneWeight( 3, 0.0f );
  1455. meshBuilder.BoneMatrix( 0, 0 );
  1456. meshBuilder.BoneMatrix( 1, 0 );
  1457. meshBuilder.BoneMatrix( 2, 0 );
  1458. meshBuilder.BoneMatrix( 3, 0 );
  1459. */
  1460. meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR, 1>();
  1461. }
  1462. return true;
  1463. }
  1464. bool CStudioRender::DrawMultiBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder,
  1465. DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD )
  1466. {
  1467. int *pBoneRemap = pStudioLOD ? pStudioLOD->m_pHWMorphDecalBoneRemap : NULL;
  1468. mstudiomesh_t *pLastMesh = NULL;
  1469. const mstudio_meshvertexdata_t *vertData = NULL;
  1470. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1471. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1472. {
  1473. DecalVertex_t& vertex = verts[i];
  1474. int n = vertex.m_MeshVertexIndex;
  1475. mstudiomesh_t *pMesh = vertex.GetMesh( pStudioHdr );
  1476. Assert( pMesh );
  1477. if ( pLastMesh != pMesh )
  1478. {
  1479. // only if the mesh changes
  1480. pLastMesh = pMesh;
  1481. vertData = pMesh->GetVertexData( pStudioHdr );
  1482. }
  1483. if ( !vertData )
  1484. return false;
  1485. IMorph *pMorph = pBoneRemap ? vertex.GetMorph( m_pStudioHdr, m_pStudioMeshes ) : NULL;
  1486. Vector2D morphUV;
  1487. if ( pMorph )
  1488. {
  1489. Assert( pBoneRemap );
  1490. Assert( vertex.m_GroupIndex != 0xFFFF );
  1491. if ( !pRenderContext->GetMorphAccumulatorTexCoord( &morphUV, pMorph, vertex.m_GroupIndex ) )
  1492. {
  1493. pMorph = NULL;
  1494. }
  1495. }
  1496. if ( !pMorph )
  1497. {
  1498. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1499. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1500. if ( m_VertexCache.IsVertexPositionCached( n ) )
  1501. {
  1502. CachedPosNorm_t* pCachedVert = m_VertexCache.GetWorldVertex( n );
  1503. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1504. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1505. }
  1506. else
  1507. {
  1508. // Prevent the computation of this again....
  1509. m_VertexCache.SetupComputation(pMesh);
  1510. CachedPosNorm_t* pCachedVert = m_VertexCache.CreateWorldVertex( n );
  1511. if (m_VertexCache.IsThinVertexFlexed( n ))
  1512. {
  1513. CachedPosNorm_t* pFlexedVertex = m_VertexCache.GetThinFlexVertex( n );
  1514. Vector vecPosition, vecNormal;
  1515. VectorAdd( *vertData->Position( n ), pFlexedVertex->m_Position.AsVector3D(), vecPosition );
  1516. VectorAdd( *vertData->Normal( n ), pFlexedVertex->m_Normal.AsVector3D(), vecNormal );
  1517. R_StudioTransform( vecPosition, pBoneWeights, m_PoseToWorld, pCachedVert->m_Position.AsVector3D() );
  1518. R_StudioRotate( vecNormal, pBoneWeights, m_PoseToWorld, pCachedVert->m_Normal.AsVector3D() );
  1519. VectorNormalize( pCachedVert->m_Normal.AsVector3D() );
  1520. }
  1521. else if (m_VertexCache.IsVertexFlexed( n ))
  1522. {
  1523. CachedPosNormTan_t* pFlexedVertex = m_VertexCache.GetFlexVertex( n );
  1524. R_StudioTransform( pFlexedVertex->m_Position.AsVector3D(), pBoneWeights, m_PoseToWorld, pCachedVert->m_Position.AsVector3D() );
  1525. R_StudioRotate( pFlexedVertex->m_Normal.AsVector3D(), pBoneWeights, m_PoseToWorld, pCachedVert->m_Normal.AsVector3D() );
  1526. }
  1527. else
  1528. {
  1529. Assert( pMesh );
  1530. R_StudioTransform( *vertData->Position( n ), pBoneWeights, m_PoseToWorld, pCachedVert->m_Position.AsVector3D() );
  1531. R_StudioRotate( *vertData->Normal( n ), pBoneWeights, m_PoseToWorld, pCachedVert->m_Normal.AsVector3D() );
  1532. }
  1533. // Add a little extra offset for hardware skinning; in that case
  1534. // we're doing software skinning for decals and it might not be quite right
  1535. VectorMA( pCachedVert->m_Position.AsVector3D(), 0.1, pCachedVert->m_Normal.AsVector3D(), pCachedVert->m_Position.AsVector3D() );
  1536. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1537. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1538. }
  1539. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1540. meshBuilder.TexCoord2fv( 0, GetVecTexCoord( vertex.m_TexCoord ).Base() );
  1541. meshBuilder.TexCoord3f( 2, 0.0f, 0.0f, 0.0f );
  1542. // In this case, we use the model matrix to send down the single transform,
  1543. // so we don't have to send down any bone info per vertex.
  1544. Assert( meshBuilder.m_NumBoneWeights == 0 );
  1545. // NOTE: Even if HW morphing is active, since we're using bone 0, it will multiply by identity in the shader
  1546. /*
  1547. meshBuilder.BoneWeight( 0, 1.0f );
  1548. meshBuilder.BoneWeight( 1, 0.0f );
  1549. meshBuilder.BoneWeight( 2, 0.0f );
  1550. meshBuilder.BoneWeight( 3, 0.0f );
  1551. meshBuilder.BoneMatrix( 0, 0 );
  1552. meshBuilder.BoneMatrix( 1, 0 );
  1553. meshBuilder.BoneMatrix( 2, 0 );
  1554. meshBuilder.BoneMatrix( 3, 0 );
  1555. */
  1556. }
  1557. else
  1558. {
  1559. // HARDWARE MORPH CASE!
  1560. meshBuilder.Position3fv( vertData->Position( n )->Base() );
  1561. meshBuilder.Normal3fv( vertData->Normal( n )->Base() );
  1562. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1563. meshBuilder.TexCoord2fv( 0, GetVecTexCoord( vertex.m_TexCoord ).Base() );
  1564. meshBuilder.TexCoord3f( 2, morphUV.x, morphUV.y, 1.0f );
  1565. // NOTE: We should be renormalizing bone weights here like R_AddVertexToMesh does..
  1566. // It's too expensive. Tough noogies.
  1567. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1568. Assert( meshBuilder.m_NumBoneWeights > 0 );
  1569. Assert( pBoneWeights->numbones <= 3 );
  1570. meshBuilder.BoneWeight( 0, pBoneWeights->weight[ 0 ] );
  1571. meshBuilder.BoneWeight( 1, pBoneWeights->weight[ 1 ] );
  1572. meshBuilder.BoneWeight( 2, 1.0f - pBoneWeights->weight[ 1 ] - pBoneWeights->weight[ 0 ] );
  1573. meshBuilder.BoneWeight( 3, 0.0f );
  1574. meshBuilder.BoneMatrix( 0, pBoneRemap[ pBoneWeights->bone[0] ] );
  1575. meshBuilder.BoneMatrix( 1, pBoneRemap[ pBoneWeights->bone[1] ] );
  1576. meshBuilder.BoneMatrix( 2, pBoneRemap[ pBoneWeights->bone[2] ] );
  1577. meshBuilder.BoneMatrix( 3, BONE_MATRIX_INDEX_INVALID );
  1578. }
  1579. meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR, 3>();
  1580. }
  1581. return true;
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. // Draws all the decals using a particular material
  1585. //-----------------------------------------------------------------------------
  1586. void CStudioRender::BuildDecalIndices( CMeshBuilder &meshBuilder, const DecalMaterial_t& decalMaterial )
  1587. {
  1588. // Set the indices
  1589. // This is a little tricky. Because we can retire decals, the indices
  1590. // for each decal start at 0. We output all the vertices in order of
  1591. // each decal, and then fix up the indices based on how many vertices
  1592. // we wrote out for the decals
  1593. int indexCount = decalMaterial.m_Indices.Count();
  1594. unsigned short h = decalMaterial.m_Decals.Head();
  1595. const Decal_t& decal = decalMaterial.m_Decals[ h ];
  1596. int indicesRemaining = decal.m_IndexCount;
  1597. int nVertexCount = decal.m_VertexCount;
  1598. int vertexOffset = 0;
  1599. for ( int i = 0; i < indexCount; ++i)
  1600. {
  1601. meshBuilder.FastIndex( decalMaterial.m_Indices[i] + vertexOffset );
  1602. if (--indicesRemaining > 0)
  1603. continue;
  1604. vertexOffset += nVertexCount;
  1605. h = decalMaterial.m_Decals.Next( h );
  1606. if (h != decalMaterial.m_Decals.InvalidIndex())
  1607. {
  1608. const Decal_t& decalNext = decalMaterial.m_Decals[ h ];
  1609. indicesRemaining = decalNext.m_IndexCount;
  1610. nVertexCount = decalNext.m_VertexCount;
  1611. }
  1612. #ifdef _DEBUG
  1613. else
  1614. {
  1615. Assert( i + 1 == indexCount );
  1616. }
  1617. #endif
  1618. }
  1619. }
  1620. //-----------------------------------------------------------------------------
  1621. // Inner loop for rendering instanced decals that have multiple bones
  1622. //-----------------------------------------------------------------------------
  1623. enum
  1624. {
  1625. DECAL_BATCH_SIZE = 64
  1626. };
  1627. void ComputeSkinMatrixToMemorySSE( mstudioboneweight_t &boneweights, matrix3x4_t *pPoseToWorld, matrix3x4_t &result );
  1628. void CStudioRender::DrawInstancedMultiBoneDecals( CMeshBuilder& meshBuilder, const DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, matrix3x4_t *pPoseToWorld )
  1629. {
  1630. ALIGN16 matrix3x4_t skinMatrix[ DECAL_BATCH_SIZE ] ALIGN16_POST;
  1631. Vector4DAligned vecPosition[ DECAL_BATCH_SIZE ];
  1632. Vector4DAligned vecNormal[ DECAL_BATCH_SIZE ];
  1633. Vector2D vecTexcoord[ DECAL_BATCH_SIZE ];
  1634. Vector4DAligned vecWorldPosition[ DECAL_BATCH_SIZE ];
  1635. Vector4DAligned vecWorldNormal[ DECAL_BATCH_SIZE ];
  1636. const thinModelVertices_t *thinVertData = NULL;
  1637. const mstudio_meshvertexdata_t *vertData = NULL;
  1638. mstudiomesh_t *pLastMesh = NULL;
  1639. mstudioboneweight_t boneWeights;
  1640. const DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1641. DecalVertexList_t::IndexLocalType_t i = verts.Head();
  1642. do
  1643. {
  1644. // This loop snacks up the vertices to skin + computes the skin matrices
  1645. int nCount = 0;
  1646. for ( ; i != verts.InvalidIndex(); i = verts.Next(i), ++nCount )
  1647. {
  1648. if ( nCount == DECAL_BATCH_SIZE )
  1649. break;
  1650. const DecalVertex_t& vertex = verts[i];
  1651. // prefetch the next one into L2
  1652. {
  1653. DecalVertexList_t::IndexLocalType_t j = verts.Next(i);
  1654. if ( j != verts.InvalidIndex() )
  1655. {
  1656. const DecalVertex_t& nextVertex = verts[j];
  1657. #ifdef _X360
  1658. PREFETCH360( &nextVertex, 0 );
  1659. #elif _SSE1
  1660. _mm_prefetch( reinterpret_cast<const char *>(&nextVertex) , _MM_HINT_T0 );
  1661. #endif
  1662. }
  1663. }
  1664. int n = vertex.m_MeshVertexIndex;
  1665. Assert( n < MAXSTUDIOVERTS );
  1666. mstudiomesh_t * RESTRICT pMesh = vertex.GetMesh( pStudioHdr );
  1667. Assert( pMesh );
  1668. if ( pLastMesh != pMesh )
  1669. {
  1670. // only if the mesh changes
  1671. pLastMesh = pMesh;
  1672. vertData = pMesh->GetVertexData( pStudioHdr );
  1673. if ( !vertData )
  1674. {
  1675. thinVertData = pMesh->GetThinVertexData( pStudioHdr );
  1676. }
  1677. }
  1678. vecTexcoord[nCount] = GetVecTexCoord( vertex.m_TexCoord );
  1679. if ( vertData )
  1680. {
  1681. mstudioboneweight_t* RESTRICT pBoneWeights = vertData->BoneWeights( n );
  1682. vecPosition[nCount].Init( *vertData->Position( n ) );
  1683. vecNormal[nCount].Init( *vertData->Normal( n ) );
  1684. ComputeSkinMatrixToMemorySSE( *pBoneWeights, pPoseToWorld, skinMatrix[nCount] );
  1685. }
  1686. else if ( thinVertData )
  1687. {
  1688. thinVertData->GetMeshBoneWeights( pMesh, n, &boneWeights );
  1689. thinVertData->GetMeshPosition( pMesh, n, &( vecPosition[nCount].AsVector3D() ) );
  1690. thinVertData->GetMeshNormal( pMesh, n, &( vecNormal[nCount].AsVector3D() ) );
  1691. ComputeSkinMatrixToMemorySSE( boneWeights, pPoseToWorld, skinMatrix[nCount] );
  1692. }
  1693. }
  1694. if ( nCount == 0 )
  1695. break;
  1696. // This loop transforms all the vertices
  1697. fltx4 col0, col1, col2, col3, p, n, x, y, z, r, offset;
  1698. for ( int vertIndex = 0; vertIndex < nCount; ++vertIndex )
  1699. {
  1700. const matrix3x4_t& mat = skinMatrix[vertIndex];
  1701. // Prepare the skin matrix
  1702. col0 = LoadAlignedSIMD( mat[0] );
  1703. col1 = LoadAlignedSIMD( mat[1] );
  1704. col2 = LoadAlignedSIMD( mat[2] );
  1705. col3 = Four_Origin;
  1706. TransposeSIMD( col0, col1, col2, col3 );
  1707. // Deal with position
  1708. p = LoadAlignedSIMD( vecPosition[vertIndex].Base() );
  1709. x = SplatXSIMD( p );
  1710. y = SplatYSIMD( p );
  1711. z = SplatZSIMD( p );
  1712. x = MulSIMD( x, col0 );
  1713. y = MulSIMD( y, col1 );
  1714. z = MulSIMD( z, col2 );
  1715. r = AddSIMD( x, col3 );
  1716. y = AddSIMD( y, z );
  1717. p = AddSIMD( r, y );
  1718. // Deal with normal
  1719. n = LoadAlignedSIMD( vecNormal[vertIndex].Base() );
  1720. x = SplatXSIMD( n );
  1721. y = SplatYSIMD( n );
  1722. z = SplatZSIMD( n );
  1723. r = MulSIMD( x, col0 );
  1724. y = MulSIMD( y, col1 );
  1725. z = MulSIMD( z, col2 );
  1726. offset = ReplicateX4( 0.1f );
  1727. r = AddSIMD( y, r );
  1728. r = AddSIMD( z, r );
  1729. StoreAlignedSIMD( vecWorldNormal[vertIndex].Base(), r );
  1730. // Add a little extra offset for hardware skinning; in that case
  1731. // we're doing software skinning for decals and it might not be quite right
  1732. p = MaddSIMD( r, offset, p );
  1733. StoreAlignedSIMD( vecWorldPosition[vertIndex].Base(), p );
  1734. }
  1735. // Add to meshbuilder
  1736. for ( int vertIndex = 0; vertIndex < nCount; ++vertIndex )
  1737. {
  1738. meshBuilder.Position3fv( vecWorldPosition[vertIndex].Base() );
  1739. meshBuilder.Normal3fv( vecWorldNormal[vertIndex].Base() );
  1740. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1741. meshBuilder.TexCoord2fv( 0, vecTexcoord[vertIndex].Base() );
  1742. meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR, 1>();
  1743. }
  1744. } while ( i != verts.InvalidIndex() );
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Draws decals
  1748. //-----------------------------------------------------------------------------
  1749. void CStudioRender::DrawModelArrayDecals( IMatRenderContext *pRenderContext,
  1750. studiohdr_t *pStudioHdr, int nCount, DecalRenderData_t *pRenderData, int nFlashlightMask )
  1751. {
  1752. VPROF( "CStudioRender::DrawModelArrayDecals" );
  1753. // FIXME: Is there a more optimal method? It's unclear since dynamic vbs are used
  1754. // NOTE: I *think* I can, assuming the same material is used. I can probably
  1755. // early-out needing to run the shader, and only run the per-instance state instead!
  1756. // NOTE: Flexing is not supported here yet
  1757. // FIXME: This is a problem. . . boomer has flexes and goes through this path, which doesn't apply flexes.
  1758. // Can you quickly tell if there are active flexes and make the model go through the slow path instead?
  1759. Assert ( pStudioHdr->numflexdesc == 0 );
  1760. pRenderContext->SetNumBoneWeights( 0 );
  1761. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1762. bool bSingleBone = ( pStudioHdr->numbones <= 1 );
  1763. if ( bSingleBone )
  1764. {
  1765. for ( int i = 0; i < nCount; ++i )
  1766. {
  1767. DecalRenderData_t &data = pRenderData[i];
  1768. StudioArrayInstanceData_t *pCurrInstance = data.m_pInstance;
  1769. const DecalMaterial_t &decalMaterial = *data.m_pDecalMaterial;
  1770. // Don't draw stuff that doesn't need to re-light or isn't affected by this flashlight
  1771. if ( nFlashlightMask )
  1772. {
  1773. if ( !data.m_bIsVertexLit || ( ( pCurrInstance->m_nFlashlightUsage & nFlashlightMask ) == 0 ) )
  1774. continue;
  1775. }
  1776. pRenderContext->Bind( data.m_pRenderMaterial, decalMaterial.m_pvProxyUserData );
  1777. pRenderContext->LoadMatrix( pCurrInstance->m_pPoseToWorld[0] );
  1778. if ( data.m_bIsVertexLit )
  1779. {
  1780. MaterialLightingState_t *pLightingState = pCurrInstance->m_pDecalLightingState ?
  1781. pCurrInstance->m_pDecalLightingState : pCurrInstance->m_pLightingState;
  1782. if ( pLightingState )
  1783. {
  1784. pRenderContext->SetLightingState( *pLightingState );
  1785. }
  1786. }
  1787. int nVertexCount = decalMaterial.m_Vertices.Count();
  1788. int nIndexCount = decalMaterial.m_Indices.Count();
  1789. CMeshBuilder meshBuilder;
  1790. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  1791. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, nIndexCount );
  1792. DrawSingleBoneDecals( meshBuilder, decalMaterial );
  1793. BuildDecalIndices( meshBuilder, decalMaterial );
  1794. meshBuilder.End();
  1795. pMesh->Draw();
  1796. }
  1797. }
  1798. else
  1799. {
  1800. pRenderContext->LoadIdentity( );
  1801. for ( int i = 0; i < nCount; ++i )
  1802. {
  1803. DecalRenderData_t &data = pRenderData[i];
  1804. StudioArrayInstanceData_t *pCurrInstance = data.m_pInstance;
  1805. const DecalMaterial_t &decalMaterial = *data.m_pDecalMaterial;
  1806. // Don't draw stuff that doesn't need to re-light or isn't affected by this flashlight
  1807. if ( nFlashlightMask )
  1808. {
  1809. if ( !data.m_bIsVertexLit || ( ( pCurrInstance->m_nFlashlightUsage & nFlashlightMask ) == 0 ) )
  1810. continue;
  1811. }
  1812. pRenderContext->Bind( data.m_pRenderMaterial, decalMaterial.m_pvProxyUserData );
  1813. if ( data.m_bIsVertexLit )
  1814. {
  1815. MaterialLightingState_t *pLightingState = pCurrInstance->m_pDecalLightingState ?
  1816. pCurrInstance->m_pDecalLightingState : pCurrInstance->m_pLightingState;
  1817. if ( pLightingState )
  1818. {
  1819. pRenderContext->SetLightingState( *pLightingState );
  1820. }
  1821. }
  1822. int nVertexCount = decalMaterial.m_Vertices.Count();
  1823. int nIndexCount = decalMaterial.m_Indices.Count();
  1824. CMeshBuilder meshBuilder;
  1825. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  1826. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, nIndexCount );
  1827. DrawInstancedMultiBoneDecals( meshBuilder, decalMaterial, pStudioHdr, pCurrInstance->m_pPoseToWorld );
  1828. BuildDecalIndices( meshBuilder, decalMaterial );
  1829. meshBuilder.End();
  1830. pMesh->Draw();
  1831. }
  1832. }
  1833. }
  1834. //-----------------------------------------------------------------------------
  1835. // Draws all the decals using a particular material
  1836. //-----------------------------------------------------------------------------
  1837. void CStudioRender::DrawDecalMaterial( IMatRenderContext *pRenderContext, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD )
  1838. {
  1839. // Performance analysis.
  1840. // VPROF_BUDGET( "Decals", "Decals" );
  1841. VPROF( "CStudioRender::DrawDecalMaterial" );
  1842. // It's possible for the index count to become zero due to decal retirement
  1843. int indexCount = decalMaterial.m_Indices.Count();
  1844. if ( indexCount == 0 )
  1845. return;
  1846. if ( !m_pRC->m_Config.m_bEnableHWMorph )
  1847. {
  1848. pStudioLOD = NULL;
  1849. }
  1850. bool bUseHWMorphing = ( pStudioLOD && ( pStudioLOD->m_pHWMorphDecalBoneRemap != NULL ) );
  1851. if ( bUseHWMorphing )
  1852. {
  1853. pRenderContext->BindMorph( MATERIAL_MORPH_DECAL );
  1854. }
  1855. // Bind the decal material
  1856. if ( !m_pRC->m_Config.bWireframeDecals )
  1857. {
  1858. pRenderContext->Bind( decalMaterial.m_pMaterial );
  1859. }
  1860. else
  1861. {
  1862. pRenderContext->Bind( m_pMaterialWireframe[0][0] ); // TODO: support displacement mapping
  1863. }
  1864. // Use a dynamic mesh...
  1865. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  1866. int vertexCount = decalMaterial.m_Vertices.Count();
  1867. CMeshBuilder meshBuilder;
  1868. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertexCount, indexCount );
  1869. // FIXME: Could make static meshes for these?
  1870. // But don't make no static meshes for decals that fade, at least
  1871. // Two possibilities: no/one bones, we let the hardware do all transformation
  1872. // or, more than one bone, we do software skinning.
  1873. bool bDraw = true;
  1874. if ( m_pStudioHdr->numbones <= 1 )
  1875. {
  1876. if ( m_pStudioHdr->numflexdesc != 0 )
  1877. {
  1878. DrawSingleBoneFlexedDecals( pRenderContext, meshBuilder, decalMaterial );
  1879. }
  1880. else
  1881. {
  1882. DrawSingleBoneDecals( meshBuilder, decalMaterial );
  1883. }
  1884. }
  1885. else
  1886. {
  1887. if ( m_pStudioHdr->numflexdesc != 0 )
  1888. {
  1889. if ( !DrawMultiBoneFlexedDecals( pRenderContext, meshBuilder, decalMaterial, pStudioHdr, pStudioLOD ) )
  1890. {
  1891. bDraw = false;
  1892. }
  1893. }
  1894. else
  1895. {
  1896. if ( !DrawMultiBoneDecals( meshBuilder, decalMaterial, pStudioHdr ) )
  1897. {
  1898. bDraw = false;
  1899. }
  1900. }
  1901. }
  1902. // Set the indices
  1903. BuildDecalIndices( meshBuilder, decalMaterial );
  1904. meshBuilder.End();
  1905. if ( bDraw )
  1906. {
  1907. pMesh->Draw();
  1908. }
  1909. else
  1910. {
  1911. pMesh->MarkAsDrawn();
  1912. }
  1913. if ( bUseHWMorphing )
  1914. {
  1915. pRenderContext->BindMorph( NULL );
  1916. }
  1917. }
  1918. //-----------------------------------------------------------------------------
  1919. // Purpose: Setup the render state for decals if object has lighting baked.
  1920. //-----------------------------------------------------------------------------
  1921. static Vector s_pWhite[6] =
  1922. {
  1923. Vector( 1.0, 1.0, 1.0 ),
  1924. Vector( 1.0, 1.0, 1.0 ),
  1925. Vector( 1.0, 1.0, 1.0 ),
  1926. Vector( 1.0, 1.0, 1.0 ),
  1927. Vector( 1.0, 1.0, 1.0 ),
  1928. Vector( 1.0, 1.0, 1.0 )
  1929. };
  1930. bool CStudioRender::PreDrawDecal( IMatRenderContext *pRenderContext, const DrawModelInfo_t &drawInfo )
  1931. {
  1932. if ( !drawInfo.m_bStaticLighting )
  1933. return false;
  1934. // FIXME: This is incredibly bogus,
  1935. // it's overwriting lighting state in the context without restoring it!
  1936. const Vector *pAmbient;
  1937. if ( m_pRC->m_Config.fullbright )
  1938. {
  1939. pAmbient = s_pWhite;
  1940. m_pRC->m_NumLocalLights = 0;
  1941. }
  1942. else
  1943. {
  1944. pAmbient = drawInfo.m_LightingState.m_vecAmbientCube;
  1945. m_pRC->m_NumLocalLights = CopyLocalLightingState( MAXLOCALLIGHTS, m_pRC->m_LocalLights,
  1946. drawInfo.m_LightingState.m_nLocalLightCount, drawInfo.m_LightingState.m_pLocalLightDesc );
  1947. }
  1948. for( int i = 0; i < 6; i++ )
  1949. {
  1950. VectorCopy( pAmbient[i], m_pRC->m_LightBoxColors[i].AsVector3D() );
  1951. m_pRC->m_LightBoxColors[i][3] = 1.0f;
  1952. }
  1953. SetLightingRenderState();
  1954. return true;
  1955. }
  1956. //-----------------------------------------------------------------------------
  1957. // Draws all the decals on a particular model
  1958. //-----------------------------------------------------------------------------
  1959. void CStudioRender::DrawDecal( const DrawModelInfo_t &drawInfo, int lod, int body )
  1960. {
  1961. StudioDecalHandle_t handle = drawInfo.m_Decals;
  1962. if ( handle == STUDIORENDER_DECAL_INVALID )
  1963. return;
  1964. VPROF("CStudioRender::DrawDecal");
  1965. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1966. PreDrawDecal( pRenderContext, drawInfo );
  1967. // All decal vertex data is are stored in pose space
  1968. // So as long as the pose-to-world transforms are set, we're all ready!
  1969. // FIXME: Body stuff isn't hooked in at all for decals
  1970. // Get the decal list for this lod
  1971. const DecalModelList_t& list = m_DecalList[(intp)handle];
  1972. m_pStudioHdr = drawInfo.m_pStudioHdr;
  1973. // Add this fix after I fix the other problem.
  1974. studioloddata_t *pStudioLOD = NULL;
  1975. Assert( m_pStudioHdr->numbones != 0 );
  1976. if ( m_pStudioHdr->numbones <= 1 )
  1977. {
  1978. // Use the model matrix to skin if we only have one bone.
  1979. pRenderContext->SetNumBoneWeights( 0 );
  1980. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1981. pRenderContext->LoadMatrix( m_PoseToWorld[0] );
  1982. }
  1983. else
  1984. {
  1985. pStudioLOD = &drawInfo.m_pHardwareData->m_pLODs[lod];
  1986. if ( !m_pRC->m_Config.m_bEnableHWMorph || !pStudioLOD->m_pHWMorphDecalBoneRemap )
  1987. {
  1988. // If we are multi-bone for this model and we aren't morphing, don't use
  1989. // hardware skinning at all and don't bother sending any bone weights down.
  1990. pRenderContext->SetNumBoneWeights( 0 );
  1991. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1992. pRenderContext->LoadIdentity( );
  1993. }
  1994. else
  1995. {
  1996. // If we are using hardware morphing, go ahead and use the hardware skinning
  1997. // instead of doing it on the CPU.
  1998. // Set up skinning for decal rendering with hw morphs
  1999. pRenderContext->SetNumBoneWeights( pStudioLOD->m_nDecalBoneCount );
  2000. // Bone 0 is always identity; necessary to multiple against non hw-morphed verts
  2001. matrix3x4_t identity;
  2002. SetIdentityMatrix( identity );
  2003. pRenderContext->LoadBoneMatrix( 0, identity );
  2004. // Set up the bone state from the mapping computed in ComputeHWMorphDecalBoneRemap
  2005. for ( int i = 0; i < m_pStudioHdr->numbones; ++i )
  2006. {
  2007. int nHWBone = pStudioLOD->m_pHWMorphDecalBoneRemap[i];
  2008. if ( nHWBone <= 0 )
  2009. continue;
  2010. pRenderContext->LoadBoneMatrix( nHWBone, m_PoseToWorld[i] );
  2011. }
  2012. }
  2013. }
  2014. // Gotta do this for all LODs
  2015. // Draw each set of decals using a particular material
  2016. unsigned short mat = list.m_pLod[lod].m_FirstMaterial;
  2017. for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
  2018. {
  2019. DecalMaterial_t& decalMaterial = m_DecalMaterial[mat];
  2020. DrawDecalMaterial( pRenderContext, decalMaterial, m_pStudioHdr, pStudioLOD );
  2021. }
  2022. }
  2023. void CStudioRender::DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, const matrix3x4_t &modelToWorld )
  2024. {
  2025. StudioDecalHandle_t handle = drawInfo.m_Decals;
  2026. if (handle == STUDIORENDER_DECAL_INVALID)
  2027. return;
  2028. m_pRC = const_cast< StudioRenderContext_t* >( &rc );
  2029. VPROF("CStudioRender::DrawStaticPropDecals");
  2030. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  2031. PreDrawDecal( pRenderContext, drawInfo );
  2032. // All decal vertex data is are stored in pose space
  2033. // So as long as the pose-to-world transforms are set, we're all ready!
  2034. // FIXME: Body stuff isn't hooked in at all for decals
  2035. pRenderContext->MatrixMode( MATERIAL_MODEL );
  2036. pRenderContext->LoadMatrix( modelToWorld );
  2037. const DecalModelList_t& list = m_DecalList[(intp)handle];
  2038. m_pStudioHdr = drawInfo.m_pStudioHdr;
  2039. // Gotta do this for all LODs
  2040. // Draw each set of decals using a particular material
  2041. unsigned short mat = list.m_pLod[drawInfo.m_Lod].m_FirstMaterial;
  2042. for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
  2043. {
  2044. DecalMaterial_t& decalMaterial = m_DecalMaterial[mat];
  2045. DrawDecalMaterial( pRenderContext, decalMaterial, drawInfo.m_pStudioHdr, NULL );
  2046. }
  2047. m_pRC = NULL;
  2048. }
  2049. void CStudioRender::CleanupDecals()
  2050. {
  2051. for( auto it = m_DecalLRU.Head(); it != m_DecalLRU.InvalidIndex(); )
  2052. {
  2053. auto itNext = m_DecalLRU.Next( it );
  2054. if ( ( m_DecalLRU[ it ].m_nFlags & DECAL_IMMEDIATECLEANUP ) != 0 )
  2055. {
  2056. StudioDecalHandle_t hRetire = m_DecalLRU[ it ].m_hDecalHandle;
  2057. DecalModelList_t &modelList = m_DecalList[ ( intp ) hRetire ];
  2058. RetireDecalAtAddress( modelList, it, modelList.m_pHardwareData->m_RootLOD, modelList.m_pHardwareData->m_NumLODs );
  2059. }
  2060. it = itNext;
  2061. }
  2062. }