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

1990 lines
65 KiB

  1. //========= Copyright 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 "tier0/vprof.h"
  18. #include "tier0/minidump.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. #define MAX_DECAL_INDICES_PER_MODEL 2048
  33. //-----------------------------------------------------------------------------
  34. // Triangle clipping state
  35. //-----------------------------------------------------------------------------
  36. struct DecalClipState_t
  37. {
  38. // Number of used vertices
  39. int m_VertCount;
  40. // Indices into the clip verts array of the used vertices
  41. int m_Indices[2][7];
  42. // Helps us avoid copying the m_Indices array by using double-buffering
  43. bool m_Pass;
  44. // Add vertices we've started with and had to generate due to clipping
  45. int m_ClipVertCount;
  46. DecalVertex_t m_ClipVerts[16];
  47. // Union of the decal triangle clip flags above for each vert
  48. int m_ClipFlags[16];
  49. DecalClipState_t() {}
  50. private:
  51. // Copy constructors are not allowed
  52. DecalClipState_t( const DecalClipState_t& src );
  53. };
  54. //-----------------------------------------------------------------------------
  55. //
  56. // Lovely decal code begins here... ABANDON ALL HOPE YE WHO ENTER!!!
  57. //
  58. //-----------------------------------------------------------------------------
  59. //-----------------------------------------------------------------------------
  60. // Functions to make vertex opaque
  61. //-----------------------------------------------------------------------------
  62. #ifdef COMPACT_DECAL_VERT
  63. #define GetVecTexCoord( v ) (v.operator Vector2D())
  64. #define GetVecNormal( v ) (v.operator Vector())
  65. #else
  66. #define GetVecTexCoord( v ) v
  67. #define GetVecNormal( v ) v
  68. #endif
  69. //-----------------------------------------------------------------------------
  70. // Remove decal from LRU
  71. //-----------------------------------------------------------------------------
  72. void CStudioRender::RemoveDecalListFromLRU( StudioDecalHandle_t h )
  73. {
  74. DecalLRUListIndex_t i, next;
  75. for ( i = m_DecalLRU.Head(); i != m_DecalLRU.InvalidIndex(); i = next )
  76. {
  77. next = m_DecalLRU.Next(i);
  78. if ( m_DecalLRU[i].m_hDecalHandle == h )
  79. {
  80. m_DecalLRU.Remove( i );
  81. }
  82. }
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Create, destroy list of decals for a particular model
  86. //-----------------------------------------------------------------------------
  87. StudioDecalHandle_t CStudioRender::CreateDecalList( studiohwdata_t *pHardwareData )
  88. {
  89. if ( !pHardwareData || pHardwareData->m_NumLODs <= 0 )
  90. return STUDIORENDER_DECAL_INVALID;
  91. // NOTE: This function is called directly without queueing
  92. m_DecalMutex.Lock();
  93. int handle = m_DecalList.AddToTail();
  94. m_DecalMutex.Unlock();
  95. m_DecalList[handle].m_pHardwareData = pHardwareData;
  96. m_DecalList[handle].m_pLod = new DecalLod_t[pHardwareData->m_NumLODs];
  97. m_DecalList[handle].m_nLods = pHardwareData->m_NumLODs;
  98. for (int i = 0; i < pHardwareData->m_NumLODs; i++)
  99. {
  100. m_DecalList[handle].m_pLod[i].m_FirstMaterial = m_DecalMaterial.InvalidIndex();
  101. }
  102. return (StudioDecalHandle_t)handle;
  103. }
  104. void CStudioRender::DestroyDecalList( StudioDecalHandle_t hDecal )
  105. {
  106. if ( hDecal == STUDIORENDER_DECAL_INVALID )
  107. return;
  108. RemoveDecalListFromLRU( hDecal );
  109. int h = (int)hDecal;
  110. // Clean up
  111. for (int i = 0; i < m_DecalList[h].m_nLods; i++ )
  112. {
  113. // Blat out all geometry associated with all materials
  114. unsigned short mat = m_DecalList[h].m_pLod[i].m_FirstMaterial;
  115. unsigned short next;
  116. while (mat != m_DecalMaterial.InvalidIndex())
  117. {
  118. next = m_DecalMaterial.Next(mat);
  119. g_nTotalDecalVerts -= m_DecalMaterial[mat].m_Vertices.Count();
  120. m_DecalMaterial.Free(mat);
  121. mat = next;
  122. }
  123. }
  124. delete[] m_DecalList[h].m_pLod;
  125. m_DecalList[h].m_pLod = NULL;
  126. m_DecalMutex.Lock();
  127. m_DecalList.Remove( h );
  128. m_DecalMutex.Unlock();
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Transformation/Rotation for decals
  132. //-----------------------------------------------------------------------------
  133. #define FRONTFACING_EPS 0.1f
  134. inline bool CStudioRender::IsFrontFacing( const Vector * pnorm, const mstudioboneweight_t * pboneweight )
  135. {
  136. // NOTE: This only works to rotate normals if there's no scale in the
  137. // pose to world transforms. If we ever add scale, we'll need to
  138. // multiply by the inverse transpose of the pose to decal
  139. float z;
  140. if (pboneweight->numbones == 1)
  141. {
  142. z = DotProduct( pnorm->Base(), m_PoseToDecal[(unsigned)pboneweight->bone[0]][2] );
  143. }
  144. else
  145. {
  146. float zbone;
  147. z = 0;
  148. for (int i = 0; i < pboneweight->numbones; i++)
  149. {
  150. zbone = DotProduct( pnorm->Base(), m_PoseToDecal[(unsigned)pboneweight->bone[i]][2] );
  151. z += zbone * pboneweight->weight[i];
  152. }
  153. }
  154. return ( z >= FRONTFACING_EPS );
  155. }
  156. inline bool CStudioRender::TransformToDecalSpace( DecalBuildInfo_t& build, const Vector& pos,
  157. mstudioboneweight_t *pboneweight, Vector2D& uv )
  158. {
  159. // NOTE: This only works to rotate normals if there's no scale in the
  160. // pose to world transforms. If we ever add scale, we'll need to
  161. // multiply by the inverse transpose of the pose to world
  162. if (pboneweight->numbones == 1)
  163. {
  164. uv.x = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[0]][0] ) +
  165. m_PoseToDecal[(unsigned)pboneweight->bone[0]][0][3];
  166. uv.y = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[0]][1] ) +
  167. m_PoseToDecal[(unsigned)pboneweight->bone[0]][1][3];
  168. }
  169. else
  170. {
  171. uv.x = uv.y = 0;
  172. float ubone, vbone;
  173. for (int i = 0; i < pboneweight->numbones; i++)
  174. {
  175. ubone = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[i]][0] ) +
  176. m_PoseToDecal[(unsigned)pboneweight->bone[i]][0][3];
  177. vbone = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[i]][1] ) +
  178. m_PoseToDecal[(unsigned)pboneweight->bone[i]][1][3];
  179. uv.x += ubone * pboneweight->weight[i];
  180. uv.y += vbone * pboneweight->weight[i];
  181. }
  182. }
  183. if (!build.m_NoPokeThru)
  184. return true;
  185. // No poke thru? do culling....
  186. float z;
  187. if (pboneweight->numbones == 1)
  188. {
  189. z = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[0]][2] ) +
  190. m_PoseToDecal[(unsigned)pboneweight->bone[0]][2][3];
  191. }
  192. else
  193. {
  194. z = 0;
  195. float zbone;
  196. for (int i = 0; i < pboneweight->numbones; i++)
  197. {
  198. zbone = DotProduct( pos.Base(), m_PoseToDecal[(unsigned)pboneweight->bone[i]][2] ) +
  199. m_PoseToDecal[(unsigned)pboneweight->bone[i]][2][3];
  200. z += zbone * pboneweight->weight[i];
  201. }
  202. }
  203. return (fabs(z) < build.m_Radius );
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Projects a decal onto a mesh
  207. //-----------------------------------------------------------------------------
  208. bool CStudioRender::ProjectDecalOntoMesh( DecalBuildInfo_t& build, DecalBuildVertexInfo_t* pVertexInfo, mstudiomesh_t *pMesh )
  209. {
  210. float invRadius = (build.m_Radius != 0.0f) ? 1.0f / build.m_Radius : 1.0f;
  211. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( build.m_pStudioHdr );
  212. const thinModelVertices_t *thinVertData = NULL;
  213. if ( !vertData )
  214. {
  215. // For most models (everything that's not got flex data), the vertex data is 'thinned' on load to save memory
  216. thinVertData = pMesh->GetThinVertexData( build.m_pStudioHdr );
  217. if ( !thinVertData )
  218. return false;
  219. }
  220. // For this to work, the plane and intercept must have been transformed
  221. // into pose space. Also, we'll not be bothering with flexes.
  222. for ( int j=0; j < pMesh->numvertices; ++j )
  223. {
  224. mstudioboneweight_t localBoneWeights;
  225. Vector localPosition;
  226. Vector localNormal;
  227. Vector * vecPosition;
  228. Vector * vecNormal;
  229. mstudioboneweight_t * boneWeights;
  230. if ( vertData )
  231. {
  232. mstudiovertex_t &vert = *vertData->Vertex( j );
  233. vecPosition = &vert.m_vecPosition;
  234. vecNormal = &vert.m_vecNormal;
  235. boneWeights = &vert.m_BoneWeights;
  236. }
  237. else
  238. {
  239. thinVertData->GetMeshPosition( pMesh, j, &localPosition );
  240. vecPosition = &localPosition;
  241. thinVertData->GetMeshNormal( pMesh, j, &localNormal );
  242. vecNormal = &localNormal;
  243. thinVertData->GetMeshBoneWeights( pMesh, j, &localBoneWeights );
  244. boneWeights = &localBoneWeights;
  245. }
  246. // No decal vertex yet...
  247. pVertexInfo[j].m_VertexIndex = 0xFFFF;
  248. pVertexInfo[j].m_UniqueID = 0xFF;
  249. pVertexInfo[j].m_Flags = 0;
  250. // We need to know if the normal is pointing in the negative direction
  251. // if so, blow off all triangles connected to that vertex.
  252. if ( !IsFrontFacing( vecNormal, boneWeights ) )
  253. continue;
  254. pVertexInfo[j].m_Flags |= DecalBuildVertexInfo_t::FRONT_FACING;
  255. bool inValidArea = TransformToDecalSpace( build, *vecPosition, boneWeights, pVertexInfo[j].m_UV );
  256. pVertexInfo[j].m_Flags |= ( inValidArea << 1 );
  257. pVertexInfo[j].m_UV *= invRadius * 0.5f;
  258. pVertexInfo[j].m_UV[0] += 0.5f;
  259. pVertexInfo[j].m_UV[1] += 0.5f;
  260. }
  261. return true;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Computes clip flags
  265. //-----------------------------------------------------------------------------
  266. inline int ComputeClipFlags( Vector2D const& uv )
  267. {
  268. // Otherwise we gotta do the test
  269. int flags = 0;
  270. if (uv.x < 0.0f)
  271. flags |= DECAL_CLIP_MINUSU;
  272. else if (uv.x > 1.0f)
  273. flags |= DECAL_CLIP_PLUSU;
  274. if (uv.y < 0.0f)
  275. flags |= DECAL_CLIP_MINUSV;
  276. else if (uv.y > 1.0f )
  277. flags |= DECAL_CLIP_PLUSV;
  278. return flags;
  279. }
  280. inline int CStudioRender::ComputeClipFlags( DecalBuildVertexInfo_t* pVertexInfo, int i )
  281. {
  282. return ::ComputeClipFlags( pVertexInfo[i].m_UV );
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Creates a new vertex where the edge intersects the plane
  286. //-----------------------------------------------------------------------------
  287. static int IntersectPlane( DecalClipState_t& state, int start, int end,
  288. int normalInd, float val )
  289. {
  290. DecalVertex_t& startVert = state.m_ClipVerts[start];
  291. DecalVertex_t& endVert = state.m_ClipVerts[end];
  292. Vector2D dir;
  293. Vector2DSubtract( endVert.m_TexCoord, startVert.m_TexCoord, dir );
  294. Assert( dir[normalInd] != 0.0f );
  295. float t = (val - GetVecTexCoord( startVert.m_TexCoord )[normalInd]) / dir[normalInd];
  296. // Allocate a clipped vertex
  297. DecalVertex_t& out = state.m_ClipVerts[state.m_ClipVertCount];
  298. int newVert = state.m_ClipVertCount++;
  299. // The clipped vertex has no analogue in the original mesh
  300. out.m_MeshVertexIndex = 0xFFFF;
  301. out.m_Mesh = 0xFFFF;
  302. out.m_Model = ( sizeof(out.m_Model) == 1 ) ? 0xFF : 0xFFFF;
  303. out.m_Body = ( sizeof(out.m_Body) == 1 ) ? 0xFF : 0xFFFF;
  304. // Interpolate position
  305. out.m_Position[0] = startVert.m_Position[0] * (1.0 - t) + endVert.m_Position[0] * t;
  306. out.m_Position[1] = startVert.m_Position[1] * (1.0 - t) + endVert.m_Position[1] * t;
  307. out.m_Position[2] = startVert.m_Position[2] * (1.0 - t) + endVert.m_Position[2] * t;
  308. // Interpolate normal
  309. Vector vNormal;
  310. // FIXME: this is a bug (it's using position data to compute interpolated normals!)... not seeing any obvious artifacts, though
  311. vNormal[0] = startVert.m_Position[0] * (1.0 - t) + endVert.m_Position[0] * t;
  312. vNormal[1] = startVert.m_Position[1] * (1.0 - t) + endVert.m_Position[1] * t;
  313. vNormal[2] = startVert.m_Position[2] * (1.0 - t) + endVert.m_Position[2] * t;
  314. VectorNormalize( vNormal );
  315. out.m_Normal = vNormal;
  316. // Interpolate texture coord
  317. Vector2D vTexCoord;
  318. Vector2DLerp( GetVecTexCoord( startVert.m_TexCoord ), GetVecTexCoord( endVert.m_TexCoord ), t, vTexCoord );
  319. out.m_TexCoord = vTexCoord;
  320. // Compute the clip flags baby...
  321. state.m_ClipFlags[newVert] = ComputeClipFlags( out.m_TexCoord );
  322. return newVert;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Clips a triangle against a plane, use clip flags to speed it up
  326. //-----------------------------------------------------------------------------
  327. static void ClipTriangleAgainstPlane( DecalClipState_t& state, int normalInd, int flag, float val )
  328. {
  329. // FIXME: Could compute the & of all the clip flags of all the verts
  330. // as we go through the loop to do another early out
  331. // Ye Olde Sutherland-Hodgman clipping algorithm
  332. int outVertCount = 0;
  333. int start = state.m_Indices[state.m_Pass][state.m_VertCount - 1];
  334. bool startInside = (state.m_ClipFlags[start] & flag) == 0;
  335. for (int i = 0; i < state.m_VertCount; ++i)
  336. {
  337. int end = state.m_Indices[state.m_Pass][i];
  338. bool endInside = (state.m_ClipFlags[end] & flag) == 0;
  339. if (endInside)
  340. {
  341. if (!startInside)
  342. {
  343. int clipVert = IntersectPlane( state, start, end, normalInd, val );
  344. state.m_Indices[!state.m_Pass][outVertCount++] = clipVert;
  345. }
  346. state.m_Indices[!state.m_Pass][outVertCount++] = end;
  347. }
  348. else
  349. {
  350. if (startInside)
  351. {
  352. int clipVert = IntersectPlane( state, start, end, normalInd, val );
  353. state.m_Indices[!state.m_Pass][outVertCount++] = clipVert;
  354. }
  355. }
  356. start = end;
  357. startInside = endInside;
  358. }
  359. state.m_Pass = !state.m_Pass;
  360. state.m_VertCount = outVertCount;
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Converts a mesh index to a DecalVertex_t
  364. //-----------------------------------------------------------------------------
  365. void CStudioRender::ConvertMeshVertexToDecalVertex( DecalBuildInfo_t& build,
  366. int meshIndex, DecalVertex_t& decalVertex, int nGroupIndex )
  367. {
  368. // Copy over the data;
  369. // get the texture coords from the decal planar projection
  370. Assert( meshIndex < MAXSTUDIOVERTS );
  371. if ( build.m_pMeshVertexData )
  372. {
  373. VectorCopy( *build.m_pMeshVertexData->Position( meshIndex ), decalVertex.m_Position );
  374. VectorCopy( *build.m_pMeshVertexData->Normal( meshIndex ), GetVecNormal( decalVertex.m_Normal ) );
  375. }
  376. else
  377. {
  378. // At this point in the code, we should definitely have either compressed or uncompressed vertex data
  379. Assert( build.m_pMeshThinVertexData );
  380. Vector position;
  381. Vector normal;
  382. build.m_pMeshThinVertexData->GetMeshPosition( build.m_pMesh, meshIndex, &position );
  383. build.m_pMeshThinVertexData->GetMeshNormal( build.m_pMesh, meshIndex, &normal );
  384. VectorCopy( position, decalVertex.m_Position );
  385. VectorCopy( normal, GetVecNormal( decalVertex.m_Normal ) );
  386. }
  387. Vector2DCopy( build.m_pVertexInfo[meshIndex].m_UV, GetVecTexCoord( decalVertex.m_TexCoord ) );
  388. decalVertex.m_MeshVertexIndex = meshIndex;
  389. decalVertex.m_Mesh = build.m_Mesh;
  390. Assert( decalVertex.m_Mesh < 100 );
  391. decalVertex.m_Model = build.m_Model;
  392. decalVertex.m_Body = build.m_Body;
  393. decalVertex.m_Group = build.m_Group;
  394. decalVertex.m_GroupIndex = nGroupIndex;
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Adds a vertex to the list of vertices for this material
  398. //-----------------------------------------------------------------------------
  399. inline unsigned short CStudioRender::AddVertexToDecal( DecalBuildInfo_t& build, int nMeshIndex, int nGroupIndex )
  400. {
  401. DecalBuildVertexInfo_t* pVertexInfo = build.m_pVertexInfo;
  402. // If we've never seen this vertex before, we need to add a new decal vert
  403. if ( pVertexInfo[nMeshIndex].m_UniqueID != build.m_nGlobalMeshIndex )
  404. {
  405. pVertexInfo[nMeshIndex].m_UniqueID = build.m_nGlobalMeshIndex;
  406. DecalVertexList_t& decalVertexList = build.m_pDecalMaterial->m_Vertices;
  407. DecalVertexList_t::IndexType_t v;
  408. v = decalVertexList.AddToTail();
  409. g_nTotalDecalVerts++;
  410. // Copy over the data;
  411. ConvertMeshVertexToDecalVertex( build, nMeshIndex, build.m_pDecalMaterial->m_Vertices[v], nGroupIndex );
  412. #ifdef _DEBUG
  413. // Make sure clipped vertices are in the right range...
  414. if (build.m_UseClipVert)
  415. {
  416. Assert( (decalVertexList[v].m_TexCoord[0] >= -1e-3) && (decalVertexList[v].m_TexCoord[0] - 1.0f < 1e-3) );
  417. Assert( (decalVertexList[v].m_TexCoord[1] >= -1e-3) && (decalVertexList[v].m_TexCoord[1] - 1.0f < 1e-3) );
  418. }
  419. #endif
  420. // Store off the index of this vertex so we can reference it again
  421. pVertexInfo[nMeshIndex].m_VertexIndex = build.m_VertexCount;
  422. ++build.m_VertexCount;
  423. if (build.m_FirstVertex == decalVertexList.InvalidIndex())
  424. {
  425. build.m_FirstVertex = v;
  426. }
  427. }
  428. return pVertexInfo[nMeshIndex].m_VertexIndex;
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Adds a vertex to the list of vertices for this material
  432. //-----------------------------------------------------------------------------
  433. inline unsigned short CStudioRender::AddVertexToDecal( DecalBuildInfo_t& build, DecalVertex_t& vert )
  434. {
  435. // This creates a unique vertex
  436. DecalVertexList_t& decalVertexList = build.m_pDecalMaterial->m_Vertices;
  437. // Try to see if the clipped vertex already exists in our decal list...
  438. // Only search for matches with verts appearing in the current decal
  439. DecalVertexList_t::IndexType_t i;
  440. unsigned short vertexCount = 0;
  441. for ( i = build.m_FirstVertex; i != decalVertexList.InvalidIndex();
  442. i = decalVertexList.Next(i), ++vertexCount )
  443. {
  444. // Only bother to check against clipped vertices
  445. if ( decalVertexList[i].GetMesh( build.m_pStudioHdr ) )
  446. continue;
  447. // They must have the same position, and normal
  448. // texcoord will fall right out if the positions match
  449. Vector temp;
  450. VectorSubtract( decalVertexList[i].m_Position, vert.m_Position, temp );
  451. if ( (fabs(temp[0]) > 1e-3) || (fabs(temp[1]) > 1e-3) || (fabs(temp[2]) > 1e-3) )
  452. continue;
  453. VectorSubtract( decalVertexList[i].m_Normal, vert.m_Normal, temp );
  454. if ( (fabs(temp[0]) > 1e-3) || (fabs(temp[1]) > 1e-3) || (fabs(temp[2]) > 1e-3) )
  455. continue;
  456. return vertexCount;
  457. }
  458. // This path is the path taken by clipped vertices
  459. Assert( (vert.m_TexCoord[0] >= -1e-3) && (vert.m_TexCoord[0] - 1.0f < 1e-3) );
  460. Assert( (vert.m_TexCoord[1] >= -1e-3) && (vert.m_TexCoord[1] - 1.0f < 1e-3) );
  461. // Must create a new vertex...
  462. DecalVertexList_t::IndexType_t idx = decalVertexList.AddToTail(vert);
  463. g_nTotalDecalVerts++;
  464. if (build.m_FirstVertex == decalVertexList.InvalidIndex())
  465. build.m_FirstVertex = idx;
  466. Assert( vertexCount == build.m_VertexCount );
  467. return build.m_VertexCount++;
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Adds the clipped triangle to the decal
  471. //-----------------------------------------------------------------------------
  472. void CStudioRender::AddClippedDecalToTriangle( DecalBuildInfo_t& build, DecalClipState_t& clipState )
  473. {
  474. // FIXME: Clipped vertices will almost always be shared. We
  475. // need a way of associating clipped vertices with edges so we can share
  476. // the clipped vertices quickly
  477. Assert( clipState.m_VertCount <= 7 );
  478. // Yeah baby yeah!! Add this sucka
  479. int i;
  480. unsigned short indices[7];
  481. for ( i = 0; i < clipState.m_VertCount; ++i)
  482. {
  483. // First add the vertices
  484. int vertIdx = clipState.m_Indices[clipState.m_Pass][i];
  485. if (vertIdx < 3)
  486. {
  487. indices[i] = AddVertexToDecal( build, clipState.m_ClipVerts[vertIdx].m_MeshVertexIndex );
  488. }
  489. else
  490. {
  491. indices[i] = AddVertexToDecal( build, clipState.m_ClipVerts[vertIdx] );
  492. }
  493. }
  494. // Add a trifan worth of triangles
  495. for ( i = 1; i < clipState.m_VertCount - 1; ++i)
  496. {
  497. MEM_ALLOC_CREDIT();
  498. build.m_pDecalMaterial->m_Indices.AddToTail( indices[0] );
  499. build.m_pDecalMaterial->m_Indices.AddToTail( indices[i] );
  500. build.m_pDecalMaterial->m_Indices.AddToTail( indices[i+1] );
  501. }
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Clips the triangle to +/- radius
  505. //-----------------------------------------------------------------------------
  506. bool CStudioRender::ClipDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int *pClipFlags )
  507. {
  508. int i;
  509. DecalClipState_t clipState;
  510. clipState.m_VertCount = 3;
  511. ConvertMeshVertexToDecalVertex( build, i1, clipState.m_ClipVerts[0] );
  512. ConvertMeshVertexToDecalVertex( build, i2, clipState.m_ClipVerts[1] );
  513. ConvertMeshVertexToDecalVertex( build, i3, clipState.m_ClipVerts[2] );
  514. clipState.m_ClipVertCount = 3;
  515. for ( i = 0; i < 3; ++i)
  516. {
  517. clipState.m_ClipFlags[i] = pClipFlags[i];
  518. clipState.m_Indices[0][i] = i;
  519. }
  520. clipState.m_Pass = 0;
  521. // Clip against each plane
  522. ClipTriangleAgainstPlane( clipState, 0, DECAL_CLIP_MINUSU, 0.0f );
  523. if (clipState.m_VertCount < 3)
  524. return false;
  525. ClipTriangleAgainstPlane( clipState, 0, DECAL_CLIP_PLUSU, 1.0f );
  526. if (clipState.m_VertCount < 3)
  527. return false;
  528. ClipTriangleAgainstPlane( clipState, 1, DECAL_CLIP_MINUSV, 0.0f );
  529. if (clipState.m_VertCount < 3)
  530. return false;
  531. ClipTriangleAgainstPlane( clipState, 1, DECAL_CLIP_PLUSV, 1.0f );
  532. if (clipState.m_VertCount < 3)
  533. return false;
  534. // Only add the clipped decal to the triangle if it's one bone
  535. // otherwise just return if it was clipped
  536. if ( build.m_UseClipVert )
  537. {
  538. AddClippedDecalToTriangle( build, clipState );
  539. }
  540. return true;
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Adds a decal to a triangle, but only if it should
  544. //-----------------------------------------------------------------------------
  545. void CStudioRender::AddTriangleToDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int gi1, int gi2, int gi3 )
  546. {
  547. DecalBuildVertexInfo_t* pVertexInfo = build.m_pVertexInfo;
  548. // All must be front-facing for a decal to be added
  549. // FIXME: Could make it work if not all are front-facing, need clipping for that
  550. int nAllFrontFacing = pVertexInfo[i1].m_Flags & pVertexInfo[i2].m_Flags & pVertexInfo[i3].m_Flags;
  551. if ( ( nAllFrontFacing & DecalBuildVertexInfo_t::FRONT_FACING ) == 0 )
  552. return;
  553. // This is used to prevent poke through; if the points are too far away
  554. // from the contact point, then don't add the decal
  555. int nAllNotInValidArea = pVertexInfo[i1].m_Flags | pVertexInfo[i2].m_Flags | pVertexInfo[i3].m_Flags;
  556. if ( ( nAllNotInValidArea & DecalBuildVertexInfo_t::VALID_AREA ) == 0 )
  557. return;
  558. // Clip to +/- radius
  559. int clipFlags[3];
  560. clipFlags[0] = ComputeClipFlags( pVertexInfo, i1 );
  561. clipFlags[1] = ComputeClipFlags( pVertexInfo, i2 );
  562. clipFlags[2] = ComputeClipFlags( pVertexInfo, i3 );
  563. // Cull... The result is non-zero if they're all outside the same plane
  564. if ( (clipFlags[0] & (clipFlags[1] & clipFlags[2]) ) != 0)
  565. return;
  566. bool doClip = true;
  567. // Trivial accept for skinned polys... if even one vert is inside
  568. // the draw region, accept
  569. if ((!build.m_UseClipVert) && ( !clipFlags[0] || !clipFlags[1] || !clipFlags[2] ))
  570. {
  571. doClip = false;
  572. }
  573. // Trivial accept... no clip flags set means all in
  574. // Don't clip if we have more than one bone... we'll need to do skinning
  575. // and we can't clip the bone indices
  576. // We *do* want to clip in the one bone case though; useful for large
  577. // static props.
  578. if ( doClip && ( clipFlags[0] || clipFlags[1] || clipFlags[2] ))
  579. {
  580. bool validTri = ClipDecal( build, i1, i2, i3, clipFlags );
  581. // Don't add the triangle if we culled the triangle or if
  582. // we had one or less bones
  583. if (build.m_UseClipVert || (!validTri))
  584. return;
  585. }
  586. // Add the vertices to the decal since there was no clipping
  587. i1 = AddVertexToDecal( build, i1, gi1 );
  588. i2 = AddVertexToDecal( build, i2, gi2 );
  589. i3 = AddVertexToDecal( build, i3, gi3 );
  590. MEM_ALLOC_CREDIT();
  591. build.m_pDecalMaterial->m_Indices.AddToTail(i1);
  592. build.m_pDecalMaterial->m_Indices.AddToTail(i2);
  593. build.m_pDecalMaterial->m_Indices.AddToTail(i3);
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Adds a decal to a mesh
  597. //-----------------------------------------------------------------------------
  598. void CStudioRender::AddDecalToMesh( DecalBuildInfo_t& build )
  599. {
  600. MeshVertexInfo_t &vertexInfo = build.m_pMeshVertices[ build.m_nGlobalMeshIndex ];
  601. if ( vertexInfo.m_nIndex < 0 )
  602. return;
  603. build.m_pVertexInfo = &build.m_pVertexBuffer[ vertexInfo.m_nIndex ];
  604. // Draw all the various mesh groups...
  605. for ( int j = 0; j < build.m_pMeshData->m_NumGroup; ++j )
  606. {
  607. build.m_Group = j;
  608. studiomeshgroup_t* pGroup = &build.m_pMeshData->m_pMeshGroup[j];
  609. // Must add decal to each strip in the strip group
  610. // We do this so we can re-use all of the bone state change
  611. // info associated with the strips
  612. for (int k = 0; k < pGroup->m_NumStrips; ++k)
  613. {
  614. OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[k];
  615. if (pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP)
  616. {
  617. for (int i = 0; i < pStrip->numIndices - 2; ++i)
  618. {
  619. bool ccw = (i & 0x1) == 0;
  620. int ti1 = pStrip->indexOffset + i;
  621. int ti2 = ti1+1+ccw;
  622. int ti3 = ti1+2-ccw;
  623. int i1 = pGroup->MeshIndex(ti1);
  624. int i2 = pGroup->MeshIndex(ti2);
  625. int i3 = pGroup->MeshIndex(ti3);
  626. AddTriangleToDecal( build, i1, i2, i3, pGroup->m_pIndices[ti1], pGroup->m_pIndices[ti2], pGroup->m_pIndices[ti3] );
  627. }
  628. }
  629. else
  630. {
  631. Assert( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST );
  632. for (int i = 0; i < pStrip->numIndices; i += 3)
  633. {
  634. int idx = pStrip->indexOffset + i;
  635. int i1 = pGroup->MeshIndex(idx);
  636. int i2 = pGroup->MeshIndex(idx+1);
  637. int i3 = pGroup->MeshIndex(idx+2);
  638. AddTriangleToDecal( build, i1, i2, i3, pGroup->m_pIndices[idx], pGroup->m_pIndices[idx+1], pGroup->m_pIndices[idx+2] );
  639. }
  640. }
  641. }
  642. }
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Adds a decal to a mesh
  646. //-----------------------------------------------------------------------------
  647. bool CStudioRender::AddDecalToModel( DecalBuildInfo_t& buildInfo )
  648. {
  649. // FIXME: We need to do some high-level culling to figure out exactly
  650. // which meshes we need to add the decals to
  651. // Turns out this solution may also be good for mesh sorting
  652. // we need to know the center of each mesh, could also store a
  653. // bounding radius for each mesh and test the ray against each sphere.
  654. for ( int i = 0; i < m_pSubModel->nummeshes; ++i)
  655. {
  656. buildInfo.m_Mesh = i;
  657. buildInfo.m_pMesh = m_pSubModel->pMesh(i);
  658. buildInfo.m_pMeshData = &m_pStudioMeshes[buildInfo.m_pMesh->meshid];
  659. Assert(buildInfo.m_pMeshData);
  660. // Grab either fat or thin vertex data
  661. buildInfo.m_pMeshVertexData = buildInfo.m_pMesh->GetVertexData( buildInfo.m_pStudioHdr );
  662. if ( buildInfo.m_pMeshVertexData == NULL )
  663. {
  664. buildInfo.m_pMeshThinVertexData = buildInfo.m_pMesh->GetThinVertexData( buildInfo.m_pStudioHdr );
  665. if ( !buildInfo.m_pMeshThinVertexData )
  666. return false;
  667. }
  668. AddDecalToMesh( buildInfo );
  669. ++buildInfo.m_nGlobalMeshIndex;
  670. }
  671. return true;
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Computes the pose to decal plane transform
  675. //-----------------------------------------------------------------------------
  676. bool CStudioRender::ComputePoseToDecal( const Ray_t& ray, const Vector& up )
  677. {
  678. // Create a transform that projects world coordinates into a
  679. // basis for the decal
  680. matrix3x4_t worldToDecal;
  681. Vector decalU, decalV, decalN;
  682. // Get the z axis
  683. VectorMultiply( ray.m_Delta, -1.0f, decalN );
  684. if (VectorNormalize( decalN ) == 0.0f)
  685. return false;
  686. // Deal with the u axis
  687. CrossProduct( up, decalN, decalU );
  688. if ( VectorNormalize( decalU ) < 1e-3 )
  689. {
  690. // if up parallel or antiparallel to ray, deal...
  691. Vector fixup( up.y, up.z, up.x );
  692. CrossProduct( fixup, decalN, decalU );
  693. if ( VectorNormalize( decalU ) < 1e-3 )
  694. return false;
  695. }
  696. CrossProduct( decalN, decalU, decalV );
  697. // Since I want world-to-decal, I gotta take the inverse of the decal
  698. // to world. Assuming post-multiplying column vectors, the decal to world =
  699. // [ Ux Vx Nx | ray.m_Start[0] ]
  700. // [ Uy Vy Ny | ray.m_Start[1] ]
  701. // [ Uz Vz Nz | ray.m_Start[2] ]
  702. VectorCopy( decalU.Base(), worldToDecal[0] );
  703. VectorCopy( decalV.Base(), worldToDecal[1] );
  704. VectorCopy( decalN.Base(), worldToDecal[2] );
  705. worldToDecal[0][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[0] );
  706. worldToDecal[1][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[1] );
  707. worldToDecal[2][3] = -DotProduct( ray.m_Start.Base(), worldToDecal[2] );
  708. // Compute transforms from pose space to decal plane space
  709. for ( int i = 0; i < m_pStudioHdr->numbones; i++)
  710. {
  711. ConcatTransforms( worldToDecal, m_PoseToWorld[i], m_PoseToDecal[i] );
  712. }
  713. return true;
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Gets the list of triangles for a particular material and lod
  717. //-----------------------------------------------------------------------------
  718. int CStudioRender::GetDecalMaterial( DecalLod_t& decalLod, IMaterial* pDecalMaterial )
  719. {
  720. // Grab the material for this lod...
  721. unsigned short j;
  722. for ( j = decalLod.m_FirstMaterial; j != m_DecalMaterial.InvalidIndex(); j = m_DecalMaterial.Next(j) )
  723. {
  724. if (m_DecalMaterial[j].m_pMaterial == pDecalMaterial)
  725. {
  726. return j;
  727. }
  728. }
  729. // If we got here, this must be the first time we saw this material
  730. j = m_DecalMaterial.Alloc( true );
  731. // Link it into the list of data for this lod
  732. if (decalLod.m_FirstMaterial != m_DecalMaterial.InvalidIndex() )
  733. m_DecalMaterial.LinkBefore( decalLod.m_FirstMaterial, j );
  734. decalLod.m_FirstMaterial = j;
  735. m_DecalMaterial[j].m_pMaterial = pDecalMaterial;
  736. return j;
  737. }
  738. //-----------------------------------------------------------------------------
  739. // Purpose:
  740. //-----------------------------------------------------------------------------
  741. void CStudioRender::RetireDecal( DecalModelList_t &list, DecalId_t nRetireID, int iLOD, int iMaxLOD )
  742. {
  743. // Remove it from the global LRU...
  744. DecalLRUListIndex_t i;
  745. for ( i = m_DecalLRU.Head(); i != m_DecalLRU.InvalidIndex(); i = m_DecalLRU.Next( i ) )
  746. {
  747. if ( nRetireID == m_DecalLRU[i].m_nDecalId )
  748. {
  749. m_DecalLRU.Remove( i );
  750. break;
  751. }
  752. }
  753. Assert( i != m_DecalLRU.InvalidIndex() );
  754. // Find the id to retire and retire all the decals with this id across all LODs.
  755. DecalHistoryList_t *pHistoryList = &list.m_pLod[iLOD].m_DecalHistory;
  756. Assert( pHistoryList->Count() );
  757. if ( !pHistoryList->Count() )
  758. return;
  759. DecalHistory_t *pDecalHistory = &pHistoryList->Element( pHistoryList->Head() );
  760. // Retire this decal in all lods.
  761. for ( int iLod = ( iMaxLOD - 1 ); iLod >= list.m_pHardwareData->m_RootLOD; --iLod )
  762. {
  763. pHistoryList = &list.m_pLod[iLod].m_DecalHistory;
  764. if ( !pHistoryList )
  765. continue;
  766. unsigned short iList = pHistoryList->Head();
  767. unsigned short iNext = pHistoryList->InvalidIndex();
  768. while ( iList != pHistoryList->InvalidIndex() )
  769. {
  770. iNext = pHistoryList->Next( iList );
  771. pDecalHistory = &pHistoryList->Element( iList );
  772. if ( !pDecalHistory || pDecalHistory->m_nId != nRetireID )
  773. {
  774. iList = iNext;
  775. continue;
  776. }
  777. // Find the decal material for the decal to remove
  778. DecalMaterial_t *pMaterial = &m_DecalMaterial[pDecalHistory->m_Material];
  779. if ( pMaterial )
  780. {
  781. // @Note!! Decals must be removed in the reverse order they are added. This code
  782. // assumes that the decal to remove is the oldest one on the model, and therefore
  783. // its vertices start at the head of the list
  784. DecalVertexList_t &vertices = pMaterial->m_Vertices;
  785. Decal_t &decalToRemove = pMaterial->m_Decals[pDecalHistory->m_Decal];
  786. // Now clear out the vertices referenced by the indices....
  787. DecalVertexList_t::IndexType_t next;
  788. DecalVertexList_t::IndexType_t vert = vertices.Head();
  789. Assert( vertices.Count() >= decalToRemove.m_VertexCount );
  790. int vertsToRemove = decalToRemove.m_VertexCount;
  791. while ( vertsToRemove > 0 )
  792. {
  793. // blat out the vertices
  794. next = vertices.Next( vert );
  795. vertices.Remove( vert );
  796. vert = next;
  797. g_nTotalDecalVerts--;
  798. --vertsToRemove;
  799. }
  800. if ( vertices.Count() == 0 )
  801. {
  802. vertices.Purge();
  803. }
  804. // FIXME: This does a memmove. How expensive is it?
  805. pMaterial->m_Indices.RemoveMultiple( 0, decalToRemove.m_IndexCount );
  806. if ( pMaterial->m_Indices.Count() == 0)
  807. {
  808. pMaterial->m_Indices.Purge();
  809. }
  810. // Remove the decal
  811. pMaterial->m_Decals.Remove( pDecalHistory->m_Decal );
  812. if ( pMaterial->m_Decals.Count() == 0)
  813. {
  814. #if 1
  815. pMaterial->m_Decals.Purge();
  816. #else
  817. if ( list.m_pLod[iLOD].m_FirstMaterial == pDecalHistory->m_Material )
  818. {
  819. list.m_pLod[iLOD].m_FirstMaterial = m_DecalMaterial.Next( pDecalHistory->m_Material );
  820. }
  821. m_DecalMaterial.Free( pDecalHistory->m_Material );
  822. #endif
  823. }
  824. }
  825. // Clear the decal out of the history
  826. pHistoryList->Remove( iList );
  827. // Next element.
  828. iList = iNext;
  829. }
  830. }
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Adds a decal to the history list
  834. //-----------------------------------------------------------------------------
  835. int CStudioRender::AddDecalToMaterialList( DecalMaterial_t* pMaterial )
  836. {
  837. DecalList_t& decalList = pMaterial->m_Decals;
  838. return decalList.AddToTail();
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Total number of meshes we have to deal with
  842. //-----------------------------------------------------------------------------
  843. int CStudioRender::ComputeTotalMeshCount( int iRootLOD, int iMaxLOD, int body ) const
  844. {
  845. int nMeshCount = 0;
  846. for ( int k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  847. {
  848. mstudiomodel_t *pSubModel;
  849. R_StudioSetupModel( k, body, &pSubModel, m_pStudioHdr );
  850. nMeshCount += pSubModel->nummeshes;
  851. }
  852. nMeshCount *= iMaxLOD-iRootLOD+1;
  853. return nMeshCount;
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Set up the locations for vertices to use
  857. //-----------------------------------------------------------------------------
  858. int CStudioRender::ComputeVertexAllocation( int iMaxLOD, int body, studiohwdata_t *pHardwareData, MeshVertexInfo_t *pMeshVertices )
  859. {
  860. bool bSuppressTlucDecal = (m_pStudioHdr->flags & STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS) != 0;
  861. int nCurrMesh = 0;
  862. int nVertexCount = 0;
  863. for ( int i = iMaxLOD-1; i >= pHardwareData->m_RootLOD; i--)
  864. {
  865. IMaterial **ppMaterials = pHardwareData->m_pLODs[i].ppMaterials;
  866. for ( int k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  867. {
  868. mstudiomodel_t *pSubModel;
  869. R_StudioSetupModel( k, body, &pSubModel, m_pStudioHdr );
  870. for ( int meshID = 0; meshID < pSubModel->nummeshes; ++meshID, ++nCurrMesh)
  871. {
  872. mstudiomesh_t *pMesh = pSubModel->pMesh(meshID);
  873. pMeshVertices[nCurrMesh].m_pMesh = pMesh;
  874. int n;
  875. for ( n = nCurrMesh; --n >= 0; )
  876. {
  877. if ( pMeshVertices[n].m_pMesh == pMesh )
  878. {
  879. pMeshVertices[nCurrMesh].m_nIndex = pMeshVertices[n].m_nIndex;
  880. break;
  881. }
  882. }
  883. if ( n >= 0 )
  884. continue;
  885. // Don't add to the mesh if the mesh has a translucent material
  886. short *pSkinRef = m_pStudioHdr->pSkinref( 0 );
  887. IMaterial *pMaterial = ppMaterials[pSkinRef[pMesh->material]];
  888. if (bSuppressTlucDecal)
  889. {
  890. if (pMaterial->IsTranslucent())
  891. {
  892. pMeshVertices[nCurrMesh].m_nIndex = -1;
  893. continue;
  894. }
  895. }
  896. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SUPPRESS_DECALS ) )
  897. {
  898. pMeshVertices[nCurrMesh].m_nIndex = -1;
  899. continue;
  900. }
  901. pMeshVertices[nCurrMesh].m_nIndex = nVertexCount;
  902. nVertexCount += pMesh->numvertices;
  903. }
  904. }
  905. }
  906. return nVertexCount;
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Project decals onto all meshes
  910. //-----------------------------------------------------------------------------
  911. void CStudioRender::ProjectDecalsOntoMeshes( DecalBuildInfo_t& build, int nMeshCount )
  912. {
  913. int nMaxVertexIndex = -1;
  914. for ( int i = 0; i < nMeshCount; ++i )
  915. {
  916. int nIndex = build.m_pMeshVertices[i].m_nIndex;
  917. // No mesh, or have we already projected this?
  918. if (( nIndex < 0 ) || ( nIndex <= nMaxVertexIndex ))
  919. continue;
  920. nMaxVertexIndex = nIndex;
  921. // Project all vertices for this group into decal space
  922. ProjectDecalOntoMesh( build, &build.m_pVertexBuffer[ nIndex ], build.m_pMeshVertices[i].m_pMesh );
  923. }
  924. }
  925. //-----------------------------------------------------------------------------
  926. // Add decals to a decal list by doing a planar projection along the ray
  927. //-----------------------------------------------------------------------------
  928. void CStudioRender::AddDecal( StudioDecalHandle_t hDecal, const StudioRenderContext_t& rc, matrix3x4_t *pBoneToWorld,
  929. studiohdr_t *pStudioHdr, const Ray_t& ray, const Vector& decalUp, IMaterial* pDecalMaterial,
  930. float radius, int body, bool noPokethru, int maxLODToDecal )
  931. {
  932. VPROF( "CStudioRender::AddDecal" );
  933. if ( hDecal == STUDIORENDER_DECAL_INVALID )
  934. return;
  935. // For each lod, build the decal list
  936. int h = (int)hDecal;
  937. DecalModelList_t& list = m_DecalList[h];
  938. if ( list.m_pHardwareData->m_NumStudioMeshes == 0 )
  939. return;
  940. m_pRC = const_cast< StudioRenderContext_t* >( &rc );
  941. m_pStudioHdr = pStudioHdr;
  942. m_pBoneToWorld = pBoneToWorld;
  943. // Bone to world must be set before calling AddDecal; it uses that here
  944. // UNDONE: Use current LOD to cull matrices here?
  945. ComputePoseToWorld( m_PoseToWorld, pStudioHdr, BONE_USED_BY_ANYTHING, m_pRC->m_ViewOrigin, m_pBoneToWorld );
  946. // Compute transforms from pose space to decal plane space
  947. if (!ComputePoseToDecal( ray, decalUp ))
  948. {
  949. m_pStudioHdr = NULL;
  950. m_pRC = NULL;
  951. m_pBoneToWorld = NULL;
  952. return;
  953. }
  954. // Get dynamic information from the material (fade start, fade time)
  955. float fadeStartTime = 0.0f;
  956. float fadeDuration = 0.0f;
  957. int flags = 0;
  958. // This sucker is state needed only when building decals
  959. DecalBuildInfo_t buildInfo;
  960. buildInfo.m_Radius = radius;
  961. buildInfo.m_NoPokeThru = noPokethru;
  962. buildInfo.m_pStudioHdr = pStudioHdr;
  963. buildInfo.m_UseClipVert = ( m_pStudioHdr->numbones <= 1 ) && ( m_pStudioHdr->numflexdesc == 0 );
  964. buildInfo.m_nGlobalMeshIndex = 0;
  965. buildInfo.m_pMeshVertexData = NULL;
  966. // Find out which LODs we're defacing
  967. int iMaxLOD;
  968. if ( maxLODToDecal == ADDDECAL_TO_ALL_LODS )
  969. {
  970. iMaxLOD = list.m_pHardwareData->m_NumLODs;
  971. }
  972. else
  973. {
  974. iMaxLOD = min( list.m_pHardwareData->m_NumLODs, maxLODToDecal );
  975. }
  976. // Allocate space for all projected mesh vertices. We do this to prevent
  977. // re-projection of the same meshes when they appear in multiple LODs
  978. int nMeshCount = ComputeTotalMeshCount( list.m_pHardwareData->m_RootLOD, iMaxLOD-1, body );
  979. // NOTE: This is a consequence of the sizeof (m_UniqueID)
  980. if ( nMeshCount >= 255 )
  981. {
  982. Warning("Unable to apply decals to model (%s), it has more than 255 unique meshes!\n", m_pStudioHdr->pszName() );
  983. m_pStudioHdr = NULL;
  984. m_pRC = NULL;
  985. m_pBoneToWorld = NULL;
  986. return;
  987. }
  988. if ( !IsX360() )
  989. {
  990. buildInfo.m_pMeshVertices = (MeshVertexInfo_t*)stackalloc( nMeshCount * sizeof(MeshVertexInfo_t) );
  991. int nVertexCount = ComputeVertexAllocation( iMaxLOD, body, list.m_pHardwareData, buildInfo.m_pMeshVertices );
  992. buildInfo.m_pVertexBuffer = (DecalBuildVertexInfo_t*)stackalloc( nVertexCount * sizeof(DecalBuildVertexInfo_t) );
  993. }
  994. else
  995. {
  996. // Don't allocate on the stack
  997. buildInfo.m_pMeshVertices = (MeshVertexInfo_t*)malloc( nMeshCount * sizeof(MeshVertexInfo_t) );
  998. int nVertexCount = ComputeVertexAllocation( iMaxLOD, body, list.m_pHardwareData, buildInfo.m_pMeshVertices );
  999. buildInfo.m_pVertexBuffer = (DecalBuildVertexInfo_t*)malloc( nVertexCount * sizeof(DecalBuildVertexInfo_t) );
  1000. }
  1001. // Project all mesh vertices
  1002. ProjectDecalsOntoMeshes( buildInfo, nMeshCount );
  1003. if ( IsX360() )
  1004. {
  1005. while ( g_nTotalDecalVerts * sizeof(DecalVertex_t) > 256*1024 && m_DecalLRU.Head() != m_DecalLRU.InvalidIndex() )
  1006. {
  1007. DecalId_t nRetireID = m_DecalLRU[ m_DecalLRU.Head() ].m_nDecalId;
  1008. StudioDecalHandle_t hRetire = m_DecalLRU[ m_DecalLRU.Head() ].m_hDecalHandle;
  1009. DecalModelList_t &modelList = m_DecalList[(int)hRetire];
  1010. RetireDecal( modelList, nRetireID, modelList.m_pHardwareData->m_RootLOD, modelList.m_pHardwareData->m_NumLODs );
  1011. }
  1012. }
  1013. // Check to see if we have too many decals on this model
  1014. // This assumes that every decal is applied to the root lod at least
  1015. int nRootLOD = list.m_pHardwareData->m_RootLOD;
  1016. int nFinalLOD = list.m_pHardwareData->m_NumLODs;
  1017. DecalHistoryList_t *pHistoryList = &list.m_pLod[list.m_pHardwareData->m_RootLOD].m_DecalHistory;
  1018. if ( m_DecalLRU.Count() >= m_pRC->m_Config.maxDecalsPerModel * 1.5 )
  1019. {
  1020. DecalId_t nRetireID = m_DecalLRU[ m_DecalLRU.Head() ].m_nDecalId;
  1021. StudioDecalHandle_t hRetire = m_DecalLRU[ m_DecalLRU.Head() ].m_hDecalHandle;
  1022. DecalModelList_t &modelList = m_DecalList[(int)hRetire];
  1023. RetireDecal( modelList, nRetireID, modelList.m_pHardwareData->m_RootLOD, modelList.m_pHardwareData->m_NumLODs );
  1024. }
  1025. if ( pHistoryList->Count() >= m_pRC->m_Config.maxDecalsPerModel )
  1026. {
  1027. DecalHistory_t *pDecalHistory = &pHistoryList->Element( pHistoryList->Head() );
  1028. DecalId_t nRetireID = pDecalHistory->m_nId;
  1029. StudioDecalHandle_t hRetire = hDecal;
  1030. RetireDecal( m_DecalList[(int)hRetire], nRetireID, nRootLOD, nFinalLOD );
  1031. }
  1032. // Search all LODs for an overflow condition and retire those also
  1033. for ( int i = iMaxLOD-1; i >= list.m_pHardwareData->m_RootLOD; i-- )
  1034. {
  1035. // Grab the list of all decals using the same material for this lod...
  1036. int materialIdx = GetDecalMaterial( list.m_pLod[i], pDecalMaterial );
  1037. // Check to see if we should retire the decal
  1038. DecalMaterial_t *pDecalMaterial = &m_DecalMaterial[materialIdx];
  1039. while ( pDecalMaterial->m_Indices.Count() > MAX_DECAL_INDICES_PER_MODEL )
  1040. {
  1041. DecalHistoryList_t *pHistoryList = &list.m_pLod[i].m_DecalHistory;
  1042. DecalHistory_t *pDecalHistory = &pHistoryList->Element( pHistoryList->Head() );
  1043. RetireDecal( list, pDecalHistory->m_nId, nRootLOD, nFinalLOD );
  1044. }
  1045. }
  1046. // Gotta do this for all LODs
  1047. bool bAddedDecals = false;
  1048. for ( int i = iMaxLOD-1; i >= list.m_pHardwareData->m_RootLOD; i-- )
  1049. {
  1050. // Grab the list of all decals using the same material for this lod...
  1051. int materialIdx = GetDecalMaterial( list.m_pLod[i], pDecalMaterial );
  1052. buildInfo.m_pDecalMaterial = &m_DecalMaterial[materialIdx];
  1053. // Grab the meshes for this lod
  1054. m_pStudioMeshes = list.m_pHardwareData->m_pLODs[i].m_pMeshData;
  1055. // Don't decal on meshes that are translucent if it's twopass
  1056. buildInfo.m_ppMaterials = list.m_pHardwareData->m_pLODs[i].ppMaterials;
  1057. // Set up info needed for vertex sharing
  1058. buildInfo.m_FirstVertex = buildInfo.m_pDecalMaterial->m_Vertices.InvalidIndex();
  1059. buildInfo.m_VertexCount = 0;
  1060. int prevIndexCount = buildInfo.m_pDecalMaterial->m_Indices.Count();
  1061. // Step over all body parts + add decals to em all!
  1062. int k;
  1063. for ( k=0 ; k < m_pStudioHdr->numbodyparts ; k++)
  1064. {
  1065. // Grab the model for this body part
  1066. int model = R_StudioSetupModel( k, body, &m_pSubModel, m_pStudioHdr );
  1067. buildInfo.m_Body = k;
  1068. buildInfo.m_Model = model;
  1069. if ( !AddDecalToModel( buildInfo ) )
  1070. break;
  1071. }
  1072. if ( k != m_pStudioHdr->numbodyparts )
  1073. continue;
  1074. // Add this to the list of decals in this material
  1075. if ( buildInfo.m_VertexCount )
  1076. {
  1077. bAddedDecals = true;
  1078. int decalIndexCount = buildInfo.m_pDecalMaterial->m_Indices.Count() - prevIndexCount;
  1079. Assert(decalIndexCount > 0);
  1080. int decalIndex = AddDecalToMaterialList( buildInfo.m_pDecalMaterial );
  1081. Decal_t& decal = buildInfo.m_pDecalMaterial->m_Decals[decalIndex];
  1082. decal.m_VertexCount = buildInfo.m_VertexCount;
  1083. decal.m_IndexCount = decalIndexCount;
  1084. decal.m_FadeStartTime = fadeStartTime;
  1085. decal.m_FadeDuration = fadeDuration;
  1086. decal.m_Flags = flags;
  1087. // Add this decal to the history...
  1088. int h = list.m_pLod[i].m_DecalHistory.AddToTail();
  1089. list.m_pLod[i].m_DecalHistory[h].m_Material = materialIdx;
  1090. list.m_pLod[i].m_DecalHistory[h].m_Decal = decalIndex;
  1091. list.m_pLod[i].m_DecalHistory[h].m_nId = m_nDecalId;
  1092. list.m_pLod[i].m_DecalHistory[h].m_nPad = 0;
  1093. }
  1094. }
  1095. // Add to LRU
  1096. if ( bAddedDecals )
  1097. {
  1098. DecalLRUListIndex_t h = m_DecalLRU.AddToTail();
  1099. m_DecalLRU[h].m_nDecalId = m_nDecalId;
  1100. m_DecalLRU[h].m_hDecalHandle = hDecal;
  1101. // Increment count.
  1102. ++m_nDecalId;
  1103. }
  1104. if ( IsX360() )
  1105. {
  1106. free( buildInfo.m_pMeshVertices );
  1107. free( buildInfo.m_pVertexBuffer );
  1108. }
  1109. m_pStudioHdr = NULL;
  1110. m_pRC = NULL;
  1111. m_pBoneToWorld = NULL;
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. //
  1115. // This code here is all about rendering the decals
  1116. //
  1117. //-----------------------------------------------------------------------------
  1118. //-----------------------------------------------------------------------------
  1119. // Inner loop for rendering decals that have a single bone
  1120. //-----------------------------------------------------------------------------
  1121. void CStudioRender::DrawSingleBoneDecals( CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial )
  1122. {
  1123. // We don't got no bones, so yummy yummy yum, just copy the data out
  1124. // Static props should go though this code path
  1125. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1126. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1127. {
  1128. DecalVertex_t& vertex = verts[i];
  1129. meshBuilder.Position3fv( vertex.m_Position.Base() );
  1130. meshBuilder.Normal3fv( GetVecNormal( vertex.m_Normal ).Base() );
  1131. #if 0
  1132. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1133. {
  1134. float offset[2], scale[2];
  1135. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1136. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1137. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1138. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1139. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1140. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1141. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1142. }
  1143. else
  1144. #endif
  1145. {
  1146. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1147. }
  1148. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1149. if ( meshBuilder.NumBoneWeights() > 0 ) // bone weight of 0 will not write anything, so these calls would be wasted
  1150. {
  1151. meshBuilder.BoneWeight( 0, 1.0f );
  1152. meshBuilder.BoneWeight( 1, 0.0f );
  1153. meshBuilder.BoneWeight( 2, 0.0f );
  1154. meshBuilder.BoneWeight( 3, 0.0f );
  1155. }
  1156. meshBuilder.BoneMatrix( 0, 0 );
  1157. meshBuilder.BoneMatrix( 1, 0 );
  1158. meshBuilder.BoneMatrix( 2, 0 );
  1159. meshBuilder.BoneMatrix( 3, 0 );
  1160. meshBuilder.AdvanceVertex();
  1161. }
  1162. }
  1163. void CStudioRender::DrawSingleBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial )
  1164. {
  1165. // We don't got no bones, so yummy yummy yum, just copy the data out
  1166. // Static props should go though this code path
  1167. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1168. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1169. {
  1170. DecalVertex_t& vertex = verts[i];
  1171. // Clipped verts shouldn't come through here, only static props should use clipped
  1172. Assert ( vertex.m_MeshVertexIndex >= 0 );
  1173. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1174. if (m_VertexCache.IsVertexFlexed( vertex.m_MeshVertexIndex ))
  1175. {
  1176. CachedPosNormTan_t* pFlexedVertex = m_VertexCache.GetFlexVertex( vertex.m_MeshVertexIndex );
  1177. meshBuilder.Position3fv( pFlexedVertex->m_Position.Base() );
  1178. meshBuilder.Normal3fv( pFlexedVertex->m_Normal.Base() );
  1179. }
  1180. else
  1181. {
  1182. meshBuilder.Position3fv( vertex.m_Position.Base() );
  1183. meshBuilder.Normal3fv( GetVecNormal( vertex.m_Normal ).Base() );
  1184. }
  1185. #if 0
  1186. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1187. {
  1188. float offset[2], scale[2];
  1189. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1190. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1191. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1192. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1193. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1194. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1195. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1196. }
  1197. else
  1198. #endif
  1199. {
  1200. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1201. }
  1202. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1203. if ( meshBuilder.NumBoneWeights() > 0 ) // bone weight of 0 will not write anything, so these calls would be wasted
  1204. {
  1205. meshBuilder.BoneWeight( 0, 1.0f );
  1206. meshBuilder.BoneWeight( 1, 0.0f );
  1207. meshBuilder.BoneWeight( 2, 0.0f );
  1208. meshBuilder.BoneWeight( 3, 0.0f );
  1209. }
  1210. meshBuilder.BoneMatrix( 0, 0 );
  1211. meshBuilder.BoneMatrix( 1, 0 );
  1212. meshBuilder.BoneMatrix( 2, 0 );
  1213. meshBuilder.BoneMatrix( 3, 0 );
  1214. meshBuilder.AdvanceVertex();
  1215. }
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Inner loop for rendering decals that have multiple bones
  1219. //-----------------------------------------------------------------------------
  1220. bool CStudioRender::DrawMultiBoneDecals( CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr )
  1221. {
  1222. const thinModelVertices_t *thinVertData = NULL;
  1223. const mstudio_meshvertexdata_t *vertData = NULL;
  1224. mstudiomesh_t *pLastMesh = NULL;
  1225. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1226. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1227. {
  1228. DecalVertex_t& vertex = verts[i];
  1229. int n = vertex.m_MeshVertexIndex;
  1230. Assert( n < MAXSTUDIOVERTS );
  1231. mstudiomesh_t *pMesh = vertex.GetMesh( pStudioHdr );
  1232. Assert( pMesh );
  1233. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1234. if (m_VertexCache.IsVertexPositionCached( n ))
  1235. {
  1236. CachedPosNorm_t* pCachedVert = m_VertexCache.GetWorldVertex( n );
  1237. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1238. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1239. }
  1240. else
  1241. {
  1242. // Prevent the computation of this again....
  1243. m_VertexCache.SetupComputation(pMesh);
  1244. CachedPosNorm_t* pCachedVert = m_VertexCache.CreateWorldVertex( n );
  1245. if ( pLastMesh != pMesh )
  1246. {
  1247. // only if the mesh changes
  1248. pLastMesh = pMesh;
  1249. vertData = pMesh->GetVertexData( pStudioHdr );
  1250. if ( vertData )
  1251. thinVertData = NULL;
  1252. else
  1253. thinVertData = pMesh->GetThinVertexData( pStudioHdr );
  1254. }
  1255. if ( vertData )
  1256. {
  1257. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1258. // FIXME: could be faster to blend the matrices and then transform the pos+norm by the same matrix
  1259. R_StudioTransform( *vertData->Position( n ), pBoneWeights, pCachedVert->m_Position.AsVector3D() );
  1260. R_StudioRotate( *vertData->Normal( n ), pBoneWeights, pCachedVert->m_Normal.AsVector3D() );
  1261. }
  1262. else if ( thinVertData )
  1263. {
  1264. // Using compressed vertex data
  1265. mstudioboneweight_t boneWeights;
  1266. Vector position;
  1267. Vector normal;
  1268. thinVertData->GetMeshBoneWeights( pMesh, n, &boneWeights );
  1269. thinVertData->GetMeshPosition( pMesh, n, &position );
  1270. thinVertData->GetMeshNormal( pMesh, n, &normal );
  1271. R_StudioTransform( position, &boneWeights, pCachedVert->m_Position.AsVector3D() );
  1272. R_StudioRotate( normal, &boneWeights, pCachedVert->m_Normal.AsVector3D() );
  1273. }
  1274. else
  1275. {
  1276. return false;
  1277. }
  1278. // Add a little extra offset for hardware skinning; in that case
  1279. // we're doing software skinning for decals and it might not be quite right
  1280. VectorMA( pCachedVert->m_Position.AsVector3D(), 0.1, pCachedVert->m_Normal.AsVector3D(), pCachedVert->m_Position.AsVector3D() );
  1281. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1282. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1283. }
  1284. #if 0
  1285. if ( decalMaterial.m_pMaterial->InMaterialPage() )
  1286. {
  1287. float offset[2], scale[2];
  1288. decalMaterial.m_pMaterial->GetMaterialOffset( offset );
  1289. decalMaterial.m_pMaterial->GetMaterialScale( scale );
  1290. Vector2D vecTexCoord( vertex.m_TexCoord.x, vertex.m_TexCoord.y );
  1291. vecTexCoord.x = clamp( vecTexCoord.x, 0.0f, 1.0f );
  1292. vecTexCoord.y = clamp( vecTexCoord.y, 0.0f, 1.0f );
  1293. meshBuilder.TexCoordSubRect2f( 0, vecTexCoord.x, vecTexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1294. // meshBuilder.TexCoordSubRect2f( 0, vertex.m_TexCoord.x, vertex.m_TexCoord.y, offset[0], offset[1], scale[0], scale[1] );
  1295. }
  1296. else
  1297. #endif
  1298. {
  1299. meshBuilder.TexCoord2fv( 0, GetVecTexCoord(vertex.m_TexCoord).Base() );
  1300. }
  1301. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1302. if ( meshBuilder.NumBoneWeights() > 0 ) // bone weight of 0 will not write anything, so these calls would be wasted
  1303. {
  1304. meshBuilder.BoneWeight( 0, 1.0f );
  1305. meshBuilder.BoneWeight( 1, 0.0f );
  1306. meshBuilder.BoneWeight( 2, 0.0f );
  1307. meshBuilder.BoneWeight( 3, 0.0f );
  1308. }
  1309. meshBuilder.BoneMatrix( 0, 0 );
  1310. meshBuilder.BoneMatrix( 1, 0 );
  1311. meshBuilder.BoneMatrix( 2, 0 );
  1312. meshBuilder.BoneMatrix( 3, 0 );
  1313. meshBuilder.AdvanceVertex();
  1314. }
  1315. return true;
  1316. }
  1317. bool CStudioRender::DrawMultiBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder,
  1318. DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD )
  1319. {
  1320. int *pBoneRemap = pStudioLOD ? pStudioLOD->m_pHWMorphDecalBoneRemap : NULL;
  1321. mstudiomesh_t *pLastMesh = NULL;
  1322. const mstudio_meshvertexdata_t *vertData = NULL;
  1323. DecalVertexList_t& verts = decalMaterial.m_Vertices;
  1324. for ( DecalVertexList_t::IndexLocalType_t i = verts.Head(); i != verts.InvalidIndex(); i = verts.Next(i) )
  1325. {
  1326. DecalVertex_t& vertex = verts[i];
  1327. int n = vertex.m_MeshVertexIndex;
  1328. mstudiomesh_t *pMesh = vertex.GetMesh( pStudioHdr );
  1329. Assert( pMesh );
  1330. if ( pLastMesh != pMesh )
  1331. {
  1332. // only if the mesh changes
  1333. pLastMesh = pMesh;
  1334. vertData = pMesh->GetVertexData( pStudioHdr );
  1335. }
  1336. if ( !vertData )
  1337. return false;
  1338. IMorph *pMorph = pBoneRemap ? vertex.GetMorph( m_pStudioHdr, m_pStudioMeshes ) : NULL;
  1339. Vector2D morphUV;
  1340. if ( pMorph )
  1341. {
  1342. Assert( pBoneRemap );
  1343. Assert( vertex.m_GroupIndex != 0xFFFF );
  1344. if ( !pRenderContext->GetMorphAccumulatorTexCoord( &morphUV, pMorph, vertex.m_GroupIndex ) )
  1345. {
  1346. pMorph = NULL;
  1347. }
  1348. }
  1349. if ( !pMorph )
  1350. {
  1351. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1352. m_VertexCache.SetBodyModelMesh( vertex.m_Body, vertex.m_Model, vertex.m_Mesh );
  1353. if ( m_VertexCache.IsVertexPositionCached( n ) )
  1354. {
  1355. CachedPosNorm_t* pCachedVert = m_VertexCache.GetWorldVertex( n );
  1356. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1357. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1358. }
  1359. else
  1360. {
  1361. // Prevent the computation of this again....
  1362. m_VertexCache.SetupComputation(pMesh);
  1363. CachedPosNorm_t* pCachedVert = m_VertexCache.CreateWorldVertex( n );
  1364. if (m_VertexCache.IsThinVertexFlexed( n ))
  1365. {
  1366. CachedPosNorm_t* pFlexedVertex = m_VertexCache.GetThinFlexVertex( n );
  1367. Vector vecPosition, vecNormal;
  1368. VectorAdd( *vertData->Position( n ), pFlexedVertex->m_Position.AsVector3D(), vecPosition );
  1369. VectorAdd( *vertData->Normal( n ), pFlexedVertex->m_Normal.AsVector3D(), vecNormal );
  1370. R_StudioTransform( vecPosition, pBoneWeights, pCachedVert->m_Position.AsVector3D() );
  1371. R_StudioRotate( vecNormal, pBoneWeights, pCachedVert->m_Normal.AsVector3D() );
  1372. VectorNormalize( pCachedVert->m_Normal.AsVector3D() );
  1373. }
  1374. else if (m_VertexCache.IsVertexFlexed( n ))
  1375. {
  1376. CachedPosNormTan_t* pFlexedVertex = m_VertexCache.GetFlexVertex( n );
  1377. R_StudioTransform( pFlexedVertex->m_Position, pBoneWeights, pCachedVert->m_Position.AsVector3D() );
  1378. R_StudioRotate( pFlexedVertex->m_Normal, pBoneWeights, pCachedVert->m_Normal.AsVector3D() );
  1379. }
  1380. else
  1381. {
  1382. Assert( pMesh );
  1383. R_StudioTransform( *vertData->Position( n ), pBoneWeights, pCachedVert->m_Position.AsVector3D() );
  1384. R_StudioRotate( *vertData->Normal( n ), pBoneWeights, pCachedVert->m_Normal.AsVector3D() );
  1385. }
  1386. // Add a little extra offset for hardware skinning; in that case
  1387. // we're doing software skinning for decals and it might not be quite right
  1388. VectorMA( pCachedVert->m_Position.AsVector3D(), 0.1, pCachedVert->m_Normal.AsVector3D(), pCachedVert->m_Position.AsVector3D() );
  1389. meshBuilder.Position3fv( pCachedVert->m_Position.Base() );
  1390. meshBuilder.Normal3fv( pCachedVert->m_Normal.Base() );
  1391. }
  1392. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1393. meshBuilder.TexCoord2fv( 0, GetVecTexCoord( vertex.m_TexCoord ).Base() );
  1394. meshBuilder.TexCoord3f( 2, 0.0f, 0.0f, 0.0f );
  1395. // NOTE: Even if HW morphing is active, since we're using bone 0, it will multiply by identity in the shader
  1396. if ( meshBuilder.NumBoneWeights() > 0 ) // bone weight of 0 will not write anything, so these calls would be wasted
  1397. {
  1398. meshBuilder.BoneWeight( 0, 1.0f );
  1399. meshBuilder.BoneWeight( 1, 0.0f );
  1400. meshBuilder.BoneWeight( 2, 0.0f );
  1401. meshBuilder.BoneWeight( 3, 0.0f );
  1402. }
  1403. meshBuilder.BoneMatrix( 0, 0 );
  1404. meshBuilder.BoneMatrix( 1, 0 );
  1405. meshBuilder.BoneMatrix( 2, 0 );
  1406. meshBuilder.BoneMatrix( 3, 0 );
  1407. }
  1408. else
  1409. {
  1410. meshBuilder.Position3fv( vertData->Position( n )->Base() );
  1411. meshBuilder.Normal3fv( vertData->Normal( n )->Base() );
  1412. meshBuilder.Color4ub( 255, 255, 255, 255 );
  1413. meshBuilder.TexCoord2fv( 0, GetVecTexCoord( vertex.m_TexCoord ).Base() );
  1414. meshBuilder.TexCoord3f( 2, morphUV.x, morphUV.y, 1.0f );
  1415. // NOTE: We should be renormalizing bone weights here like R_AddVertexToMesh does..
  1416. // It's too expensive. Tough noogies.
  1417. mstudioboneweight_t* pBoneWeights = vertData->BoneWeights( n );
  1418. Assert( pBoneWeights->numbones <= 3 );
  1419. meshBuilder.BoneWeight( 0, pBoneWeights->weight[ 0 ] );
  1420. meshBuilder.BoneWeight( 1, pBoneWeights->weight[ 1 ] );
  1421. meshBuilder.BoneWeight( 2, 1.0f - pBoneWeights->weight[ 1 ] - pBoneWeights->weight[ 0 ] );
  1422. meshBuilder.BoneWeight( 3, 0.0f );
  1423. meshBuilder.BoneMatrix( 0, pBoneRemap[ (unsigned)pBoneWeights->bone[0] ] );
  1424. meshBuilder.BoneMatrix( 1, pBoneRemap[ (unsigned)pBoneWeights->bone[1] ] );
  1425. meshBuilder.BoneMatrix( 2, pBoneRemap[ (unsigned)pBoneWeights->bone[2] ] );
  1426. meshBuilder.BoneMatrix( 3, BONE_MATRIX_INDEX_INVALID );
  1427. }
  1428. meshBuilder.AdvanceVertex();
  1429. }
  1430. return true;
  1431. }
  1432. //-----------------------------------------------------------------------------
  1433. // Draws all the decals using a particular material
  1434. //-----------------------------------------------------------------------------
  1435. void CStudioRender::DrawDecalMaterial( IMatRenderContext *pRenderContext, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD )
  1436. {
  1437. // Performance analysis.
  1438. // VPROF_BUDGET( "Decals", "Decals" );
  1439. VPROF( "DecalsDrawStudio" );
  1440. // It's possible for the index count to become zero due to decal retirement
  1441. int indexCount = decalMaterial.m_Indices.Count();
  1442. if ( indexCount == 0 )
  1443. return;
  1444. if ( !m_pRC->m_Config.m_bEnableHWMorph )
  1445. {
  1446. pStudioLOD = NULL;
  1447. }
  1448. bool bUseHWMorphing = ( pStudioLOD && ( pStudioLOD->m_pHWMorphDecalBoneRemap != NULL ) );
  1449. if ( bUseHWMorphing )
  1450. {
  1451. pRenderContext->BindMorph( MATERIAL_MORPH_DECAL );
  1452. }
  1453. // Bind the decal material
  1454. if ( !m_pRC->m_Config.bWireframeDecals )
  1455. {
  1456. pRenderContext->Bind( decalMaterial.m_pMaterial );
  1457. }
  1458. else
  1459. {
  1460. pRenderContext->Bind( m_pMaterialMRMWireframe );
  1461. }
  1462. // Use a dynamic mesh...
  1463. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  1464. int vertexCount = decalMaterial.m_Vertices.Count();
  1465. CMeshBuilder meshBuilder;
  1466. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertexCount, indexCount );
  1467. // FIXME: Could make static meshes for these?
  1468. // But don't make no static meshes for decals that fade, at least
  1469. // Two possibilities: no/one bones, we let the hardware do all transformation
  1470. // or, more than one bone, we do software skinning.
  1471. bool bDraw = true;
  1472. if ( m_pStudioHdr->numbones <= 1 )
  1473. {
  1474. if ( m_pStudioHdr->numflexdesc != 0 )
  1475. {
  1476. DrawSingleBoneFlexedDecals( pRenderContext, meshBuilder, decalMaterial );
  1477. }
  1478. else
  1479. {
  1480. DrawSingleBoneDecals( meshBuilder, decalMaterial );
  1481. }
  1482. }
  1483. else
  1484. {
  1485. if ( m_pStudioHdr->numflexdesc != 0 )
  1486. {
  1487. if ( !DrawMultiBoneFlexedDecals( pRenderContext, meshBuilder, decalMaterial, pStudioHdr, pStudioLOD ) )
  1488. {
  1489. bDraw = false;
  1490. }
  1491. }
  1492. else
  1493. {
  1494. if ( !DrawMultiBoneDecals( meshBuilder, decalMaterial, pStudioHdr ) )
  1495. {
  1496. bDraw = false;
  1497. }
  1498. }
  1499. }
  1500. // Set the indices
  1501. // This is a little tricky. Because we can retire decals, the indices
  1502. // for each decal start at 0. We output all the vertices in order of
  1503. // each decal, and then fix up the indices based on how many vertices
  1504. // we wrote out for the decals
  1505. unsigned short decal = decalMaterial.m_Decals.Head();
  1506. int indicesRemaining = decalMaterial.m_Decals[decal].m_IndexCount;
  1507. int vertexOffset = 0;
  1508. for ( int i = 0; i < indexCount; ++i)
  1509. {
  1510. meshBuilder.Index( decalMaterial.m_Indices[i] + vertexOffset );
  1511. meshBuilder.AdvanceIndex();
  1512. if (--indicesRemaining <= 0)
  1513. {
  1514. vertexOffset += decalMaterial.m_Decals[decal].m_VertexCount;
  1515. decal = decalMaterial.m_Decals.Next(decal);
  1516. if (decal != decalMaterial.m_Decals.InvalidIndex())
  1517. {
  1518. indicesRemaining = decalMaterial.m_Decals[decal].m_IndexCount;
  1519. }
  1520. #ifdef _DEBUG
  1521. else
  1522. {
  1523. Assert( i + 1 == indexCount );
  1524. }
  1525. #endif
  1526. }
  1527. }
  1528. meshBuilder.End();
  1529. if ( bDraw )
  1530. {
  1531. pMesh->Draw();
  1532. }
  1533. else
  1534. {
  1535. pMesh->MarkAsDrawn();
  1536. }
  1537. if ( bUseHWMorphing )
  1538. {
  1539. pRenderContext->BindMorph( NULL );
  1540. }
  1541. }
  1542. //-----------------------------------------------------------------------------
  1543. // Purpose: Setup the render state for decals if object has lighting baked.
  1544. //-----------------------------------------------------------------------------
  1545. static Vector s_pWhite[6] =
  1546. {
  1547. Vector( 1.0, 1.0, 1.0 ),
  1548. Vector( 1.0, 1.0, 1.0 ),
  1549. Vector( 1.0, 1.0, 1.0 ),
  1550. Vector( 1.0, 1.0, 1.0 ),
  1551. Vector( 1.0, 1.0, 1.0 ),
  1552. Vector( 1.0, 1.0, 1.0 )
  1553. };
  1554. bool CStudioRender::PreDrawDecal( IMatRenderContext *pRenderContext, const DrawModelInfo_t &drawInfo )
  1555. {
  1556. if ( !drawInfo.m_bStaticLighting )
  1557. return false;
  1558. // FIXME: This is incredibly bogus,
  1559. // it's overwriting lighting state in the context without restoring it!
  1560. const Vector *pAmbient;
  1561. if ( m_pRC->m_Config.fullbright )
  1562. {
  1563. pAmbient = s_pWhite;
  1564. m_pRC->m_NumLocalLights = 0;
  1565. }
  1566. else
  1567. {
  1568. pAmbient = drawInfo.m_vecAmbientCube;
  1569. m_pRC->m_NumLocalLights = CopyLocalLightingState( MAXLOCALLIGHTS, m_pRC->m_LocalLights,
  1570. drawInfo.m_nLocalLightCount, drawInfo.m_LocalLightDescs );
  1571. }
  1572. for( int i = 0; i < 6; i++ )
  1573. {
  1574. VectorCopy( pAmbient[i], m_pRC->m_LightBoxColors[i].AsVector3D() );
  1575. m_pRC->m_LightBoxColors[i][3] = 1.0f;
  1576. }
  1577. SetLightingRenderState();
  1578. return true;
  1579. }
  1580. //-----------------------------------------------------------------------------
  1581. // Draws all the decals on a particular model
  1582. //-----------------------------------------------------------------------------
  1583. void CStudioRender::DrawDecal( const DrawModelInfo_t &drawInfo, int lod, int body )
  1584. {
  1585. StudioDecalHandle_t handle = drawInfo.m_Decals;
  1586. if ( handle == STUDIORENDER_DECAL_INVALID )
  1587. return;
  1588. VPROF("CStudioRender::DrawDecal");
  1589. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1590. PreDrawDecal( pRenderContext, drawInfo );
  1591. // All decal vertex data is are stored in pose space
  1592. // So as long as the pose-to-world transforms are set, we're all ready!
  1593. // FIXME: Body stuff isn't hooked in at all for decals
  1594. // Get the decal list for this lod
  1595. const DecalModelList_t& list = m_DecalList[(int)handle];
  1596. m_pStudioHdr = drawInfo.m_pStudioHdr;
  1597. // Add this fix after I fix the other problem.
  1598. studioloddata_t *pStudioLOD = NULL;
  1599. if ( m_pStudioHdr->numbones <= 1 )
  1600. {
  1601. pRenderContext->SetNumBoneWeights( m_pStudioHdr->numbones );
  1602. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1603. pRenderContext->LoadMatrix( m_PoseToWorld[0] );
  1604. }
  1605. else
  1606. {
  1607. pStudioLOD = &drawInfo.m_pHardwareData->m_pLODs[lod];
  1608. if ( !m_pRC->m_Config.m_bEnableHWMorph || !pStudioLOD->m_pHWMorphDecalBoneRemap )
  1609. {
  1610. pRenderContext->SetNumBoneWeights( 0 );
  1611. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1612. pRenderContext->LoadIdentity( );
  1613. }
  1614. else
  1615. {
  1616. // Set up skinning for decal rendering with hw morphs
  1617. pRenderContext->SetNumBoneWeights( pStudioLOD->m_nDecalBoneCount );
  1618. // Bone 0 is always identity; necessary to multiple against non hw-morphed verts
  1619. matrix3x4_t identity;
  1620. SetIdentityMatrix( identity );
  1621. pRenderContext->LoadBoneMatrix( 0, identity );
  1622. // Set up the bone state from the mapping computed in ComputeHWMorphDecalBoneRemap
  1623. for ( int i = 0; i < m_pStudioHdr->numbones; ++i )
  1624. {
  1625. int nHWBone = pStudioLOD->m_pHWMorphDecalBoneRemap[i];
  1626. if ( nHWBone <= 0 )
  1627. continue;
  1628. pRenderContext->LoadBoneMatrix( nHWBone, m_PoseToWorld[i] );
  1629. }
  1630. }
  1631. }
  1632. // Gotta do this for all LODs
  1633. // Draw each set of decals using a particular material
  1634. unsigned short mat = list.m_pLod[lod].m_FirstMaterial;
  1635. for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
  1636. {
  1637. DecalMaterial_t& decalMaterial = m_DecalMaterial[mat];
  1638. DrawDecalMaterial( pRenderContext, decalMaterial, m_pStudioHdr, pStudioLOD );
  1639. }
  1640. }
  1641. void CStudioRender::DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, const matrix3x4_t &modelToWorld )
  1642. {
  1643. StudioDecalHandle_t handle = drawInfo.m_Decals;
  1644. if (handle == STUDIORENDER_DECAL_INVALID)
  1645. return;
  1646. m_pRC = const_cast< StudioRenderContext_t* >( &rc );
  1647. VPROF("CStudioRender::DrawStaticPropDecals");
  1648. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1649. PreDrawDecal( pRenderContext, drawInfo );
  1650. // All decal vertex data is are stored in pose space
  1651. // So as long as the pose-to-world transforms are set, we're all ready!
  1652. // FIXME: Body stuff isn't hooked in at all for decals
  1653. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1654. pRenderContext->LoadMatrix( modelToWorld );
  1655. const DecalModelList_t& list = m_DecalList[(int)handle];
  1656. // Gotta do this for all LODs
  1657. // Draw each set of decals using a particular material
  1658. unsigned short mat = list.m_pLod[drawInfo.m_Lod].m_FirstMaterial;
  1659. for ( ; mat != m_DecalMaterial.InvalidIndex(); mat = m_DecalMaterial.Next(mat))
  1660. {
  1661. DecalMaterial_t& decalMaterial = m_DecalMaterial[mat];
  1662. DrawDecalMaterial( pRenderContext, decalMaterial, drawInfo.m_pStudioHdr, NULL );
  1663. }
  1664. m_pRC = NULL;
  1665. }