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.

1129 lines
34 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "render_pch.h"
  10. #include "gl_cvars.h"
  11. #include "gl_model_private.h"
  12. #include "gl_lightmap.h"
  13. #include "disp.h"
  14. #include "mathlib/mathlib.h"
  15. #include "gl_rsurf.h"
  16. #include "gl_matsysiface.h"
  17. #include "zone.h"
  18. #include "materialsystem/imesh.h"
  19. #include "iscratchpad3d.h"
  20. #include "decal_private.h"
  21. #include "con_nprint.h"
  22. #include "dispcoll_common.h"
  23. #include "cmodel_private.h"
  24. #include "collisionutils.h"
  25. #include "tier0/dbg.h"
  26. #include "gl_rmain.h"
  27. #include "lightcache.h"
  28. #include "disp_tesselate.h"
  29. #include "shadowmgr.h"
  30. #include "debugoverlay.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. //-----------------------------------------------------------------------------
  34. // Globals.
  35. //-----------------------------------------------------------------------------
  36. Vector modelorg;
  37. ConVar r_DispDrawAxes( "r_DispDrawAxes", "0" );
  38. //-----------------------------------------------------------------------------
  39. // CEngineTesselateHelper implements the abstract parts of the tesselation code.
  40. // We're only interested in the final triangles anyway, right??
  41. //-----------------------------------------------------------------------------
  42. class CEngineTesselateHelper : public CBaseTesselateHelper
  43. {
  44. public:
  45. void EndTriangle()
  46. {
  47. // Put all triangles in here.
  48. int iVertOffset = m_pDisp->m_iVertOffset;
  49. // Add this tri to our mesh.
  50. m_IndexMesh.Index( m_TempIndices[0] + iVertOffset );
  51. m_IndexMesh.AdvanceIndex();
  52. m_IndexMesh.Index( m_TempIndices[1] + iVertOffset );
  53. m_IndexMesh.AdvanceIndex();
  54. m_IndexMesh.Index( m_TempIndices[2] + iVertOffset );
  55. m_IndexMesh.AdvanceIndex();
  56. // Store off the indices...
  57. m_pDisp->m_Indices[m_nIndices] = m_TempIndices[0] + iVertOffset;
  58. m_pDisp->m_Indices[m_nIndices+1] = m_TempIndices[1] + iVertOffset;
  59. m_pDisp->m_Indices[m_nIndices+2] = m_TempIndices[2] + iVertOffset;
  60. m_nIndices += 3;
  61. }
  62. DispNodeInfo_t& GetNodeInfo( int iNodeBit )
  63. {
  64. return m_pDisp->m_pNodeInfo[iNodeBit];
  65. }
  66. public:
  67. // The mesh that we specify indices into while tesselating.
  68. CMeshBuilder m_IndexMesh;
  69. CDispInfo *m_pDisp;
  70. };
  71. //-----------------------------------------------------------------------------
  72. // CDispInfo implementation.
  73. //-----------------------------------------------------------------------------
  74. inline CVertIndex CDispInfo::IndexToVert( int index ) const
  75. {
  76. if( index == -1 )
  77. return CVertIndex( -1, -1 );
  78. else
  79. return CVertIndex( index % GetSideLength(), index / GetSideLength() );
  80. }
  81. void CDispInfo::UpdateBoundingBox()
  82. {
  83. m_BBoxMin.Init( 1e24, 1e24, 1e24 );
  84. m_BBoxMax.Init( -1e24, -1e24, -1e24 );
  85. for( int i=0; i < NumVerts(); i++ )
  86. {
  87. const Vector &pos = m_MeshReader.Position( i );
  88. VectorMin( pos, m_BBoxMin, m_BBoxMin );
  89. VectorMax( pos, m_BBoxMax, m_BBoxMax );
  90. }
  91. }
  92. inline void CDispInfo::DecalProjectVert( Vector const &vPos, CDispDecalBase *pDecalBase, ShadowInfo_t const* pInfo, Vector &out )
  93. {
  94. if (!pInfo)
  95. {
  96. CDispDecal* pDispDecal = static_cast<CDispDecal*>(pDecalBase);
  97. out.x = vPos.Dot( pDispDecal->m_TextureSpaceBasis[0] ) - pDispDecal->m_pDecal->dx + .5f;
  98. out.y = vPos.Dot( pDispDecal->m_TextureSpaceBasis[1] ) - pDispDecal->m_pDecal->dy + .5f;
  99. out.z = 0;
  100. }
  101. else
  102. {
  103. Vector3DMultiplyPosition( pInfo->m_WorldToShadow, vPos, out );
  104. }
  105. }
  106. // ----------------------------------------------------------------------------- //
  107. // This version works for normal decals
  108. // ----------------------------------------------------------------------------- //
  109. void CDispInfo::TestAddDecalTri( int iIndexStart, unsigned short decalHandle, CDispDecal *pDispDecal )
  110. {
  111. decal_t *pDecal = pDispDecal->m_pDecal;
  112. // If the decal is too far away from the plane of this triangle, reject it.
  113. unsigned short tempIndices[3] =
  114. {
  115. (unsigned short)(m_MeshReader.Index( iIndexStart+0 ) - m_iVertOffset),
  116. (unsigned short)(m_MeshReader.Index( iIndexStart+1 ) - m_iVertOffset),
  117. (unsigned short)(m_MeshReader.Index( iIndexStart+2 ) - m_iVertOffset)
  118. };
  119. const Vector &v0 = m_MeshReader.Position( tempIndices[0] );
  120. const Vector &v1 = m_MeshReader.Position( tempIndices[1] );
  121. const Vector &v2 = m_MeshReader.Position( tempIndices[2] );
  122. Vector vNormal = (v2 - v0).Cross( v1 - v0 );
  123. VectorNormalize( vNormal );
  124. if ( vNormal.Dot( pDecal->position - v0 ) >= pDispDecal->m_flSize )
  125. return;
  126. // Setup verts.
  127. CDecalVert verts[3];
  128. int iVert;
  129. for( iVert=0; iVert < 3; iVert++ )
  130. {
  131. CDecalVert *pOutVert = &verts[iVert];
  132. pOutVert->m_vPos = m_MeshReader.Position( tempIndices[iVert] );
  133. {
  134. float x = pOutVert->m_cLMCoords.x;
  135. float y = pOutVert->m_cLMCoords.y;
  136. m_MeshReader.TexCoord2f( tempIndices[iVert], 1, x, y );
  137. pOutVert->m_cLMCoords.x = x;
  138. pOutVert->m_cLMCoords.y = y;
  139. }
  140. // garymcthack - what about m_ParentTexCoords?
  141. Vector tmp;
  142. DecalProjectVert( pOutVert->m_vPos, pDispDecal, 0, tmp );
  143. pOutVert->m_ctCoords.x = tmp.x;
  144. pOutVert->m_ctCoords.y = tmp.y;
  145. }
  146. // Clip them.
  147. CDecalVert *pClipped;
  148. CDecalVert *pOutVerts = NULL;
  149. pClipped = R_DoDecalSHClip( &verts[0], pOutVerts, pDecal, 3, vec3_origin );
  150. int outCount = pDecal->clippedVertCount;
  151. if ( outCount > 2 )
  152. {
  153. outCount = min( outCount, (int)CDispDecalFragment::MAX_VERTS );
  154. // Allocate a new fragment...
  155. CDispDecalFragment* pFragment = AllocateDispDecalFragment( decalHandle, outCount );
  156. // Alrighty, store the triangles!
  157. for( iVert=0; iVert < outCount; iVert++ )
  158. {
  159. pFragment->m_pVerts[iVert].m_vPos = pClipped[iVert].m_vPos;
  160. // garymcthack - need to make this work for displacements
  161. // pFragment->m_tCoords[iVert] = pClipped[iVert].m_tCoords;
  162. // garymcthack - need to change m_TCoords to m_ParentTexCoords
  163. pFragment->m_pVerts[iVert].m_ctCoords = pClipped[iVert].m_ctCoords;
  164. pFragment->m_pVerts[iVert].m_cLMCoords = pClipped[iVert].m_cLMCoords;
  165. }
  166. /*
  167. static int three = 0;
  168. static int total = 0;
  169. total++;
  170. if( outCount == 3 )
  171. {
  172. three++;
  173. }
  174. //if( )
  175. {
  176. char buffer[256];
  177. sprintf(buffer, "Verts: 3:%i 4+:%i (%i)\n",three, total, sizeof(CDecalVert));
  178. Msg(buffer);
  179. }
  180. */
  181. pFragment->m_pDecal = pDecal;
  182. pFragment->m_nVerts = outCount;
  183. pDispDecal->m_nVerts += pFragment->m_nVerts;
  184. pDispDecal->m_nTris += pFragment->m_nVerts - 2;
  185. }
  186. }
  187. // ----------------------------------------------------------------------------- //
  188. // This version works for shadow decals
  189. // ----------------------------------------------------------------------------- //
  190. void CDispInfo::TestAddDecalTri( int iIndexStart, unsigned short decalHandle, CDispShadowDecal *pDecal )
  191. {
  192. unsigned short tempIndices[3] =
  193. {
  194. (unsigned short)(m_MeshReader.Index( iIndexStart+0 ) - m_iVertOffset),
  195. (unsigned short)(m_MeshReader.Index( iIndexStart+1 ) - m_iVertOffset),
  196. (unsigned short)(m_MeshReader.Index( iIndexStart+2 ) - m_iVertOffset)
  197. };
  198. #ifndef SWDS
  199. // Setup verts.
  200. Vector vPositions[3] ={
  201. GetOverlayPos( &m_MeshReader, tempIndices[0] ),
  202. GetOverlayPos( &m_MeshReader, tempIndices[1] ),
  203. GetOverlayPos( &m_MeshReader, tempIndices[2] )
  204. };
  205. Vector* ppPosition[3] = { &vPositions[0], &vPositions[1], &vPositions[2] };
  206. ShadowVertex_t** ppClipVertex;
  207. int count = g_pShadowMgr->ProjectAndClipVertices( pDecal->m_Shadow, 3, ppPosition, &ppClipVertex );
  208. if (count < 3)
  209. return;
  210. // Ok, clipping happened; lets create a decal fragment.
  211. Assert( count <= CDispShadowFragment::MAX_VERTS );
  212. // Allocate a new fragment...
  213. CDispShadowFragment* pFragment = AllocateShadowDecalFragment( decalHandle, count );
  214. // Copy the fragment data in place
  215. pFragment->m_nVerts = count;
  216. for (int i = 0; i < count; ++i )
  217. {
  218. VectorCopy( ppClipVertex[i]->m_Position, pFragment->m_ShadowVerts[i].m_Position );
  219. VectorCopy( ppClipVertex[i]->m_ShadowSpaceTexCoord, pFragment->m_ShadowVerts[i].m_ShadowSpaceTexCoord );
  220. // Make sure it's been clipped
  221. Assert( pFragment->m_ShadowVerts[i].m_ShadowSpaceTexCoord[0] >= -1e-3f );
  222. Assert( pFragment->m_ShadowVerts[i].m_ShadowSpaceTexCoord[0] - 1.0f <= 1e-3f );
  223. Assert( pFragment->m_ShadowVerts[i].m_ShadowSpaceTexCoord[1] >= -1e-3f );
  224. Assert( pFragment->m_ShadowVerts[i].m_ShadowSpaceTexCoord[1] - 1.0f <= 1e-3f );
  225. }
  226. // Update the number of triangles in the decal
  227. pDecal->m_nVerts += pFragment->m_nVerts;
  228. pDecal->m_nTris += pFragment->m_nVerts - 2;
  229. Assert( pDecal->m_nTris != 0 );
  230. #endif
  231. }
  232. void CDispInfo::CullDecals(
  233. int iNodeBit,
  234. CDispDecal **decals,
  235. int nDecals,
  236. CDispDecal **childDecals,
  237. int &nChildDecals )
  238. {
  239. // Only let the decals through that can affect this node or its children.
  240. nChildDecals = 0;
  241. for( int iDecal=0; iDecal < nDecals; iDecal++ )
  242. {
  243. if( decals[iDecal]->m_NodeIntersect.Get( iNodeBit ) )
  244. {
  245. childDecals[nChildDecals] = decals[iDecal];
  246. ++nChildDecals;
  247. }
  248. }
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Retesselates a displacement
  252. //-----------------------------------------------------------------------------
  253. void CDispInfo::TesselateDisplacement()
  254. {
  255. // Clear decals. They get regenerated in TesselateDisplacement_R.
  256. ClearAllDecalFragments();
  257. // Blow away cached shadow decals
  258. ClearAllShadowDecalFragments();
  259. int nMaxIndices = Square( GetSideLength() - 1 ) * 6;
  260. CEngineTesselateHelper helper;
  261. helper.m_pDisp = this;
  262. helper.m_IndexMesh.BeginModify( m_pMesh->m_pMesh, 0, 0, m_iIndexOffset, nMaxIndices );
  263. helper.m_pActiveVerts = m_ActiveVerts.Base();
  264. helper.m_pPowerInfo = GetPowerInfo();
  265. // Generate the indices.
  266. ::TesselateDisplacement<CEngineTesselateHelper>( &helper ); // (implemented in disp_tesselate.h)
  267. helper.m_IndexMesh.EndModify();
  268. m_nIndices = helper.m_nIndices;
  269. }
  270. void CDispInfo::SpecifyDynamicMesh()
  271. {
  272. CMatRenderContextPtr pRenderContext( materials );
  273. // Specify the vertices and indices.
  274. IMesh *pMesh = pRenderContext->GetDynamicMesh( true );
  275. CMeshBuilder builder;
  276. builder.Begin( pMesh, MATERIAL_TRIANGLES, NumVerts(), m_nIndices );
  277. // This should mirror how FillStaticBuffer works.
  278. int nVerts = NumVerts();
  279. for( int iVert=0; iVert < nVerts; iVert++ )
  280. {
  281. CDispRenderVert *pVert = &m_Verts[iVert];
  282. builder.Position3fv( pVert->m_vPos.Base() );
  283. builder.TexCoord2fv( 0, pVert->m_vTexCoord.Base() );
  284. builder.TexCoord2fv( 1, pVert->m_LMCoords.Base() );
  285. builder.TexCoord2f( 2, m_BumpSTexCoordOffset, 0 );
  286. builder.Normal3fv( pVert->m_vNormal.Base() );
  287. builder.TangentS3fv( pVert->m_vSVector.Base() );
  288. builder.TangentT3fv( pVert->m_vTVector.Base() );
  289. builder.AdvanceVertex();
  290. }
  291. for( int iIndex=0; iIndex < m_nIndices; iIndex++ )
  292. {
  293. builder.Index( m_Indices[iIndex] - m_iVertOffset );
  294. builder.AdvanceIndex();
  295. }
  296. builder.End( false, true );
  297. }
  298. //-----------------------------------------------------------------------------
  299. //-----------------------------------------------------------------------------
  300. void CDispInfo::SpecifyWalkableDynamicMesh( void )
  301. {
  302. // Specify the vertices and indices.
  303. CMatRenderContextPtr pRenderContext( materials );
  304. #ifdef SWDS
  305. IMesh *pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, NULL );
  306. #else
  307. IMesh *pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, g_materialTranslucentSingleColor );
  308. g_materialTranslucentSingleColor->ColorModulate( 1.0f, 1.0f, 0.0f );
  309. g_materialTranslucentSingleColor->AlphaModulate( 0.33f );
  310. #endif
  311. CMeshBuilder builder;
  312. builder.Begin( pMesh, MATERIAL_TRIANGLES, NumVerts(), m_nWalkIndexCount );
  313. int nVerts = NumVerts();
  314. for( int iVert=0; iVert < nVerts; iVert++ )
  315. {
  316. builder.Position3fv( m_Verts[iVert].m_vPos.Base() );
  317. builder.AdvanceVertex();
  318. }
  319. for( int iIndex=0; iIndex < m_nWalkIndexCount; iIndex++ )
  320. {
  321. builder.Index( m_pWalkIndices[iIndex] );
  322. builder.AdvanceIndex();
  323. }
  324. builder.End( false, true );
  325. }
  326. //-----------------------------------------------------------------------------
  327. //-----------------------------------------------------------------------------
  328. void CDispInfo::SpecifyBuildableDynamicMesh( void )
  329. {
  330. // Specify the vertices and indices.
  331. CMatRenderContextPtr pRenderContext( materials );
  332. #ifdef SWDS
  333. IMesh *pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, NULL );
  334. #else
  335. g_materialTranslucentSingleColor->ColorModulate( 0.0f, 1.0f, 1.0f );
  336. g_materialTranslucentSingleColor->AlphaModulate( 0.33f );
  337. IMesh *pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, g_materialTranslucentSingleColor );
  338. #endif
  339. CMeshBuilder builder;
  340. builder.Begin( pMesh, MATERIAL_TRIANGLES, NumVerts(), m_nBuildIndexCount );
  341. int nVerts = NumVerts();
  342. for( int iVert=0; iVert < nVerts; iVert++ )
  343. {
  344. builder.Position3fv( m_Verts[iVert].m_vPos.Base() );
  345. builder.AdvanceVertex();
  346. }
  347. for( int iIndex=0; iIndex < m_nBuildIndexCount; iIndex++ )
  348. {
  349. builder.Index( m_pBuildIndices[iIndex] );
  350. builder.AdvanceIndex();
  351. }
  352. builder.End( false, true );
  353. }
  354. void CDispInfo::InitializeActiveVerts()
  355. {
  356. // Mark the corners vertices and root node by default..
  357. m_ActiveVerts.ClearAll();
  358. m_ActiveVerts.Set( VertIndex( 0, 0 ) );
  359. m_ActiveVerts.Set( VertIndex( GetSideLength()-1, 0 ) );
  360. m_ActiveVerts.Set( VertIndex( GetSideLength()-1, GetSideLength()-1 ) );
  361. m_ActiveVerts.Set( VertIndex( 0, GetSideLength()-1 ) );
  362. m_ActiveVerts.Set( VertIndex( m_pPowerInfo->m_RootNode ) );
  363. // Force the midpoint active on any edges where there are sub displacements.
  364. for( int iSide=0; iSide < 4; iSide++ )
  365. {
  366. CDispNeighbor *pSide = &m_EdgeNeighbors[iSide];
  367. if( (pSide->m_SubNeighbors[0].IsValid() && pSide->m_SubNeighbors[0].m_Span != CORNER_TO_CORNER) ||
  368. (pSide->m_SubNeighbors[1].IsValid() && pSide->m_SubNeighbors[1].m_Span != CORNER_TO_CORNER) )
  369. {
  370. int iEdgeDim = g_EdgeDims[iSide];
  371. CVertIndex nodeIndex;
  372. nodeIndex[iEdgeDim] = g_EdgeSideLenMul[iSide] * m_pPowerInfo->m_SideLengthM1;
  373. nodeIndex[!iEdgeDim] = m_pPowerInfo->m_MidPoint;
  374. m_ActiveVerts.Set( VertIndex( nodeIndex ) );
  375. }
  376. }
  377. }
  378. void CDispInfo::ClearLOD()
  379. {
  380. // First, everything as inactive.
  381. m_ActiveVerts.ClearAll();
  382. }
  383. extern ConVar mat_surfaceid;
  384. extern ConVar mat_surfacemat;
  385. bool DispInfoRenderDebugModes()
  386. {
  387. if( ShouldDrawInWireFrameMode() || mat_luxels.GetInt() || r_DispWalkable.GetInt() ||
  388. r_DispBuildable.GetInt()
  389. #if !defined( SWDS )
  390. || mat_surfaceid.GetInt() || mat_surfacemat.GetInt()
  391. #endif // SWDS
  392. )
  393. return true;
  394. return false;
  395. }
  396. bool CDispInfo::Render( CGroupMesh *pGroup, bool bAllowDebugModes )
  397. {
  398. #ifndef SWDS
  399. if( !m_pMesh )
  400. {
  401. Assert( !"CDispInfo::Render: m_pMesh == NULL" );
  402. return false;
  403. }
  404. // Trivial reject?
  405. if( R_CullBox(m_BBoxMin, m_BBoxMax, g_Frustum) )
  406. return false;
  407. bool bNormalRender = true;
  408. if ( bAllowDebugModes )
  409. {
  410. CMatRenderContextPtr pRenderContext( materials );
  411. // Wireframe?
  412. if( ShouldDrawInWireFrameMode() )
  413. {
  414. pRenderContext->Bind( g_materialWireframe );
  415. SpecifyDynamicMesh();
  416. bNormalRender = false;
  417. }
  418. if( mat_luxels.GetInt() )
  419. {
  420. pRenderContext->Bind( MSurf_TexInfo( m_ParentSurfID )->material );
  421. //SpecifyDynamicMesh();
  422. pGroup->m_pMesh->Draw( m_iIndexOffset, m_nIndices );
  423. pRenderContext->Bind( g_materialDebugLuxels );
  424. SpecifyDynamicMesh();
  425. bNormalRender = false;
  426. }
  427. if ( r_DispWalkable.GetInt() || r_DispBuildable.GetInt() )
  428. {
  429. pRenderContext->Bind( MSurf_TexInfo( m_ParentSurfID )->material );
  430. pGroup->m_pMesh->Draw( m_iIndexOffset, m_nIndices );
  431. if ( r_DispWalkable.GetInt() )
  432. SpecifyWalkableDynamicMesh();
  433. if ( r_DispBuildable.GetInt() )
  434. SpecifyBuildableDynamicMesh();
  435. bNormalRender = false;
  436. }
  437. #if !defined( SWDS )
  438. if ( mat_surfaceid.GetInt() )
  439. {
  440. Vector bbMin, bbMax, vecCenter;
  441. GetBoundingBox( bbMin, bbMax );
  442. VectorAdd( bbMin, bbMax, vecCenter );
  443. vecCenter *= 0.5f;
  444. int nInt = ( mat_surfaceid.GetInt() != 2 ) ? (int)m_ParentSurfID : (msurface2_t*)m_ParentSurfID - host_state.worldbrush->surfaces2;
  445. char buf[32];
  446. Q_snprintf( buf, sizeof( buf ), "%d", nInt );
  447. CDebugOverlay::AddTextOverlay( vecCenter, 0, buf );
  448. }
  449. if ( mat_surfacemat.GetInt() )
  450. {
  451. Vector bbMin, bbMax, vecCenter;
  452. GetBoundingBox( bbMin, bbMax );
  453. VectorAdd( bbMin, bbMax, vecCenter );
  454. vecCenter *= 0.5f;
  455. mtexinfo_t * pTexInfo = MSurf_TexInfo(m_ParentSurfID);
  456. const char *pFullMaterialName = pTexInfo->material ? pTexInfo->material->GetName() : "no material";
  457. const char *pSlash = strrchr( pFullMaterialName, '/' );
  458. const char *pMaterialName = strrchr( pFullMaterialName, '\\' );
  459. if (pSlash > pMaterialName)
  460. pMaterialName = pSlash;
  461. if (pMaterialName)
  462. ++pMaterialName;
  463. else
  464. pMaterialName = pFullMaterialName;
  465. CDebugOverlay::AddTextOverlay( vecCenter, 0, pMaterialName );
  466. }
  467. #endif // SWDS
  468. }
  469. // Mark it visible.
  470. if( bNormalRender )
  471. {
  472. if( pGroup->m_nVisible < pGroup->m_Visible.Size() )
  473. {
  474. // Don't bother if all faces are backfacing, or somesuch...
  475. if (m_nIndices)
  476. {
  477. pGroup->m_Visible[pGroup->m_nVisible].m_FirstIndex = m_iIndexOffset;
  478. pGroup->m_Visible[pGroup->m_nVisible].m_NumIndices = m_nIndices;
  479. pGroup->m_VisibleDisps[pGroup->m_nVisible] = this;
  480. pGroup->m_nVisible++;
  481. pGroup->m_pGroup->m_nVisible++;
  482. }
  483. }
  484. else
  485. {
  486. Assert( !"Overflowed visible mesh list" );
  487. }
  488. }
  489. #endif
  490. return true;
  491. }
  492. struct ProcessLightmapSampleData_t;
  493. typedef void ProcessLightmapSampleFunc_t( const ProcessLightmapSampleData_t &data, const Vector &vPos, const Vector &vNormal, const Vector &vTangentS, const Vector &vTangentT, int t, int s, int tmax, int smax );
  494. struct ProcessLightmapSampleData_t
  495. {
  496. float m_ooQuadraticAttn;
  497. float m_ooRadiusSq;
  498. Vector m_Intensity;
  499. float m_LightDistSqr;
  500. Vector m_vLightOrigin;
  501. ProcessLightmapSampleFunc_t *pProcessLightmapSampleDataFunc;
  502. };
  503. #ifndef DEDICATED
  504. static void ProcessLightmapSample( const ProcessLightmapSampleData_t &data, const Vector &vPos, const Vector &vNormal, const Vector &vTangentS, const Vector &vTangentT, int t, int s, int tmax, int smax )
  505. {
  506. float distSqr = data.m_vLightOrigin.DistToSqr( vPos );
  507. if( distSqr < data.m_LightDistSqr )
  508. {
  509. float scale = (distSqr != 0.0f) ? data.m_ooQuadraticAttn / distSqr : 1.0f;
  510. // Apply a little extra attenuation
  511. scale *= (1.0f - distSqr * data.m_ooRadiusSq);
  512. if (scale > 2.0f)
  513. scale = 2.0f;
  514. int index = t*smax + s;
  515. VectorMA( blocklights[0][index].AsVector3D(),
  516. scale, data.m_Intensity,
  517. blocklights[0][index].AsVector3D() );
  518. }
  519. }
  520. static void ProcessLightmapSampleBumped( const ProcessLightmapSampleData_t &data, const Vector &vPos, const Vector &vNormal, const Vector &vTangentS, const Vector &vTangentT, int t, int s, int tmax, int smax )
  521. {
  522. float distSqr = data.m_vLightOrigin.DistToSqr( vPos );
  523. if( distSqr < data.m_LightDistSqr )
  524. {
  525. float scale = (distSqr != 0.0f) ? data.m_ooQuadraticAttn / distSqr : 1.0f;
  526. // Get the vector from the surface to the light in world space
  527. Vector vLightVecWorld;
  528. VectorSubtract( data.m_vLightOrigin, vPos, vLightVecWorld );
  529. VectorNormalize( vLightVecWorld );
  530. // Transform the vector from the surface to the light into tangent space
  531. Vector vLightVecTangent;
  532. vLightVecTangent.x = DotProduct( vTangentS, vLightVecWorld );
  533. vLightVecTangent.y = DotProduct( vTangentT, vLightVecWorld );
  534. vLightVecTangent.z = DotProduct( vNormal, vLightVecWorld );
  535. // Apply a little extra attenuation
  536. scale *= (1.0f - distSqr * data.m_ooRadiusSq);
  537. if (scale > 2.0f)
  538. scale = 2.0f;
  539. int index = t*smax + s;
  540. float directionalAtten;
  541. directionalAtten = fpmax( 0.0f, vLightVecTangent.z );
  542. VectorMA( blocklights[0][index].AsVector3D(), scale * directionalAtten,
  543. data.m_Intensity,
  544. blocklights[0][index].AsVector3D() );
  545. directionalAtten = fpmax( 0.0f, DotProduct( vLightVecTangent, g_localBumpBasis[0] ) );
  546. VectorMA( blocklights[1][index].AsVector3D(), scale * directionalAtten,
  547. data.m_Intensity,
  548. blocklights[1][index].AsVector3D() );
  549. directionalAtten = fpmax( 0.0f, DotProduct( vLightVecTangent, g_localBumpBasis[1] ) );
  550. VectorMA( blocklights[2][index].AsVector3D(), scale * directionalAtten,
  551. data.m_Intensity,
  552. blocklights[2][index].AsVector3D() );
  553. directionalAtten = fpmax( 0.0f, DotProduct( vLightVecTangent, g_localBumpBasis[2] ) );
  554. VectorMA( blocklights[3][index].AsVector3D(), scale * directionalAtten,
  555. data.m_Intensity,
  556. blocklights[3][index].AsVector3D() );
  557. }
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Alpha channel modulation
  561. //-----------------------------------------------------------------------------
  562. static void ProcessLightmapSampleAlpha( const ProcessLightmapSampleData_t &data, const Vector &vPos, const Vector &vNormal, const Vector &vTangentS, const Vector &vTangentT, int t, int s, int tmax, int smax )
  563. {
  564. float distSqr = data.m_vLightOrigin.DistToSqr( vPos );
  565. if( distSqr < data.m_LightDistSqr )
  566. {
  567. float scale = (distSqr != 0.0f) ? data.m_ooQuadraticAttn / distSqr : 1.0f;
  568. // Apply a little extra attenuation
  569. scale *= (1.0f - distSqr * data.m_ooRadiusSq);
  570. if (scale > 1.0f)
  571. scale = 1.0f;
  572. int index = t*smax + s;
  573. blocklights[0][index][3] += scale * data.m_Intensity[0];
  574. }
  575. }
  576. #endif
  577. // This iterates over all the lightmap samples and for each one, calls:
  578. // T::ProcessLightmapSample( Vector const &vPos, int t, int s, int tmax, int smax );
  579. void IterateLightmapSamples( CDispInfo *pDisp, const ProcessLightmapSampleData_t &data )
  580. {
  581. ASSERT_SURF_VALID( pDisp->m_ParentSurfID );
  582. int smax = MSurf_LightmapExtents( pDisp->m_ParentSurfID )[0] + 1;
  583. int tmax = MSurf_LightmapExtents( pDisp->m_ParentSurfID )[1] + 1;
  584. unsigned char *pCurSample = &g_DispLightmapSamplePositions[pDisp->m_iLightmapSamplePositionStart];
  585. for( int t = 0 ; t<tmax ; t++ )
  586. {
  587. for( int s=0 ; s<smax ; s++ )
  588. {
  589. // Figure out what triangle this sample is on.
  590. // NOTE: this usually stores 4 bytes per lightmap sample.
  591. // It's a lot simpler and faster to just store the position but then it's
  592. // 16 bytes instead of 4.
  593. int iTri;
  594. if( *pCurSample == 255 )
  595. {
  596. ++pCurSample;
  597. iTri = *pCurSample + 255;
  598. }
  599. else
  600. {
  601. iTri = *pCurSample;
  602. }
  603. ++pCurSample;
  604. float a = (float)*(pCurSample++) / 255.0f;
  605. float b = (float)*(pCurSample++) / 255.0f;
  606. float c = (float)*(pCurSample++) / 255.0f;
  607. CTriInfo *pTri = &pDisp->m_pPowerInfo->m_pTriInfos[iTri];
  608. Vector vPos =
  609. pDisp->m_MeshReader.Position( pTri->m_Indices[0] ) * a +
  610. pDisp->m_MeshReader.Position( pTri->m_Indices[1] ) * b +
  611. pDisp->m_MeshReader.Position( pTri->m_Indices[2] ) * c;
  612. Vector vNormal, vTangentS, vTangentT;
  613. if( pDisp->NumLightMaps() > 1 )
  614. {
  615. vNormal =
  616. pDisp->m_MeshReader.Normal( pTri->m_Indices[0] ) * a +
  617. pDisp->m_MeshReader.Normal( pTri->m_Indices[1] ) * b +
  618. pDisp->m_MeshReader.Normal( pTri->m_Indices[2] ) * c;
  619. vTangentS =
  620. pDisp->m_MeshReader.TangentS( pTri->m_Indices[0] ) * a +
  621. pDisp->m_MeshReader.TangentS( pTri->m_Indices[1] ) * b +
  622. pDisp->m_MeshReader.TangentS( pTri->m_Indices[2] ) * c;
  623. vTangentT =
  624. pDisp->m_MeshReader.TangentT( pTri->m_Indices[0] ) * a +
  625. pDisp->m_MeshReader.TangentT( pTri->m_Indices[1] ) * b +
  626. pDisp->m_MeshReader.TangentT( pTri->m_Indices[2] ) * c;
  627. }
  628. (*data.pProcessLightmapSampleDataFunc)( data, vPos, vNormal, vTangentS, vTangentT, t, s, tmax, smax );
  629. }
  630. }
  631. }
  632. void CDispInfo::AddSingleDynamicLight( dlight_t& dl )
  633. {
  634. #ifndef SWDS
  635. ProcessLightmapSampleData_t data;
  636. data.m_LightDistSqr = dl.GetRadiusSquared();
  637. float lightStyleValue = LightStyleValue( dl.style );
  638. data.m_Intensity[0] = TexLightToLinear( dl.color.r, dl.color.exponent ) * lightStyleValue;
  639. data.m_Intensity[1] = TexLightToLinear( dl.color.g, dl.color.exponent ) * lightStyleValue;
  640. data.m_Intensity[2] = TexLightToLinear( dl.color.b, dl.color.exponent ) * lightStyleValue;
  641. float minlight = fpmax( g_flMinLightingValue, dl.minlight );
  642. float ooQuadraticAttn = data.m_LightDistSqr * minlight; // / maxIntensity;
  643. data.m_ooQuadraticAttn = ooQuadraticAttn;
  644. data.m_vLightOrigin = dl.origin;
  645. data.m_ooRadiusSq = 1.0f / dl.GetRadiusSquared();;
  646. data.pProcessLightmapSampleDataFunc = &ProcessLightmapSample;
  647. // Touch all the lightmap samples.
  648. IterateLightmapSamples( this, data );
  649. #endif
  650. }
  651. void CDispInfo::AddSingleDynamicLightBumped( dlight_t& dl )
  652. {
  653. #ifndef SWDS
  654. ProcessLightmapSampleData_t data;
  655. data.m_LightDistSqr = dl.GetRadiusSquared();
  656. float lightStyleValue = LightStyleValue( dl.style );
  657. data.m_Intensity[0] = TexLightToLinear( dl.color.r, dl.color.exponent ) * lightStyleValue;
  658. data.m_Intensity[1] = TexLightToLinear( dl.color.g, dl.color.exponent ) * lightStyleValue;
  659. data.m_Intensity[2] = TexLightToLinear( dl.color.b, dl.color.exponent ) * lightStyleValue;
  660. float minlight = fpmax( g_flMinLightingValue, dl.minlight );
  661. float ooQuadraticAttn = data.m_LightDistSqr * minlight; // / maxIntensity;
  662. data.m_ooQuadraticAttn = ooQuadraticAttn;
  663. data.m_vLightOrigin = dl.origin;
  664. data.m_ooRadiusSq = 1.0f / dl.GetRadiusSquared();
  665. data.pProcessLightmapSampleDataFunc = &ProcessLightmapSampleBumped;
  666. // Touch all the lightmap samples.
  667. IterateLightmapSamples( this, data );
  668. #endif
  669. }
  670. void CDispInfo::AddSingleDynamicAlphaLight( dlight_t& dl )
  671. {
  672. #ifndef SWDS
  673. ProcessLightmapSampleData_t data;
  674. data.m_LightDistSqr = dl.GetRadiusSquared();
  675. float lightStyleValue = LightStyleValue( dl.style );
  676. data.m_Intensity[0] = TexLightToLinear( dl.color.r, dl.color.exponent ) * lightStyleValue;
  677. if ( dl.flags & DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA )
  678. data.m_Intensity *= -1.0f;
  679. float minlight = max( g_flMinLightingValue, dl.minlight );
  680. float ooQuadraticAttn = data.m_LightDistSqr * minlight; // / maxIntensity;
  681. data.m_ooQuadraticAttn = ooQuadraticAttn;
  682. data.m_vLightOrigin = dl.origin;
  683. data.m_ooRadiusSq = 1.0f / dl.GetRadiusSquared();
  684. data.pProcessLightmapSampleDataFunc = &ProcessLightmapSampleAlpha;
  685. // Touch all the lightmap samples.
  686. IterateLightmapSamples( this, data );
  687. #endif
  688. }
  689. //-----------------------------------------------------------------------------
  690. // A little cache to help us not project vertices multiple times
  691. //-----------------------------------------------------------------------------
  692. class CDecalNodeSetupCache
  693. {
  694. public:
  695. CDecalNodeSetupCache() : m_CurrentCacheIndex(0) {}
  696. Vector m_ProjectedVert[MAX_DISPVERTS];
  697. int m_CacheIndex[MAX_DISPVERTS];
  698. bool IsCached( int v ) { return m_CacheIndex[v] == m_CurrentCacheIndex; }
  699. void MarkCached( int v ) { m_CacheIndex[v] = m_CurrentCacheIndex; }
  700. void ResetCache() { ++m_CurrentCacheIndex; }
  701. private:
  702. int m_CurrentCacheIndex;
  703. };
  704. //-----------------------------------------------------------------------------
  705. // Check to see which nodes are hit by a decal
  706. //-----------------------------------------------------------------------------
  707. bool CDispInfo::SetupDecalNodeIntersect_R( CVertIndex const &nodeIndex,
  708. int iNodeBitIndex, CDispDecalBase *pDispDecal, ShadowInfo_t const* pInfo,
  709. int iLevel, CDecalNodeSetupCache* pCache )
  710. {
  711. int iNodeIndex = VertIndex( nodeIndex );
  712. if( iLevel+1 < m_Power )
  713. {
  714. // Recurse into child nodes.
  715. bool anyChildIntersected = false;
  716. int iChildNodeBit = iNodeBitIndex + 1;
  717. for( int iChild=0; iChild < 4; iChild++ )
  718. {
  719. CVertIndex const &childNode = m_pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild];
  720. // If any of our children intersect, then we do too...
  721. if (SetupDecalNodeIntersect_R( childNode, iChildNodeBit, pDispDecal, pInfo, iLevel + 1, pCache ) )
  722. anyChildIntersected = true;
  723. iChildNodeBit += m_pPowerInfo->m_NodeIndexIncrements[iLevel];
  724. }
  725. if (anyChildIntersected)
  726. {
  727. pDispDecal->m_NodeIntersect.Set( iNodeBitIndex );
  728. return true;
  729. }
  730. // None of our children intersect this decal, so neither does the node
  731. return false;
  732. }
  733. // Expand our box by the node and by its side verts.
  734. Vector vMin, vMax;
  735. if (!pCache->IsCached(iNodeIndex))
  736. {
  737. DecalProjectVert( m_MeshReader.Position( iNodeIndex ), pDispDecal, pInfo, pCache->m_ProjectedVert[iNodeIndex] );
  738. pCache->MarkCached(iNodeIndex);
  739. }
  740. vMin = pCache->m_ProjectedVert[iNodeIndex];
  741. vMax = pCache->m_ProjectedVert[iNodeIndex];
  742. // Now test each neighbor + child vert to see if it should exist.
  743. for( int i=0; i < 4; i++ )
  744. {
  745. CVertIndex const &sideVert = m_pPowerInfo->m_pSideVerts[iNodeIndex].m_Verts[i];
  746. CVertIndex const &cornerVert = m_pPowerInfo->m_pSideVertCorners[iNodeIndex].m_Verts[i];
  747. int iSideIndex = VertIndex(sideVert);
  748. if (!pCache->IsCached(iSideIndex))
  749. {
  750. DecalProjectVert( m_MeshReader.Position( iSideIndex ), pDispDecal, pInfo, pCache->m_ProjectedVert[iSideIndex] );
  751. pCache->MarkCached(iSideIndex);
  752. }
  753. VectorMin( pCache->m_ProjectedVert[iSideIndex], vMin, vMin );
  754. VectorMax( pCache->m_ProjectedVert[iSideIndex], vMax, vMax );
  755. int iCornerIndex = VertIndex(cornerVert);
  756. if (!pCache->IsCached(iCornerIndex))
  757. {
  758. DecalProjectVert( m_MeshReader.Position( iCornerIndex ), pDispDecal, pInfo, pCache->m_ProjectedVert[iCornerIndex] );
  759. pCache->MarkCached(iCornerIndex);
  760. }
  761. VectorMin( pCache->m_ProjectedVert[iCornerIndex], vMin, vMin );
  762. VectorMax( pCache->m_ProjectedVert[iCornerIndex], vMax, vMax );
  763. }
  764. // Now just see if our bbox intersects the [0,0] - [1,1] bbox, which is where this
  765. // decal sits.
  766. if( vMin.x <= 1 && vMax.x >= 0 && vMin.y <= 1 && vMax.y >= 0 )
  767. {
  768. // Z cull for shadows...
  769. if( pInfo )
  770. {
  771. if ((vMax.z < 0) || (vMin.z > pInfo->m_MaxDist))
  772. return false;
  773. }
  774. // Ok, this node is needed and its children may be needed as well.
  775. pDispDecal->m_NodeIntersect.Set( iNodeBitIndex );
  776. return true;
  777. }
  778. return false;
  779. }
  780. void CDispInfo::SetupDecalNodeIntersect( CVertIndex const &nodeIndex, int iNodeBitIndex,
  781. CDispDecalBase *pDispDecal, ShadowInfo_t const* pInfo )
  782. {
  783. pDispDecal->m_NodeIntersect.ClearAll();
  784. // Generate a vertex cache, so we're not continually reprojecting vertices...
  785. static CDecalNodeSetupCache cache;
  786. cache.ResetCache();
  787. bool anyIntersection = SetupDecalNodeIntersect_R(
  788. nodeIndex, iNodeBitIndex, pDispDecal, pInfo, 0, &cache );
  789. pDispDecal->m_Flags |= CDispDecalBase::NODE_BITFIELD_COMPUTED;
  790. if (anyIntersection)
  791. pDispDecal->m_Flags &= ~CDispDecalBase::NO_INTERSECTION;
  792. else
  793. pDispDecal->m_Flags |= CDispDecalBase::NO_INTERSECTION;
  794. }
  795. Vector CDispInfo::GetFlatVert( int iVertex )
  796. {
  797. int sideLength = m_pPowerInfo->GetSideLength();
  798. int x = iVertex % sideLength;
  799. int y = iVertex / sideLength;
  800. float ooInt = 1.0f / ( float )( sideLength - 1 );
  801. // Lerp between the left and right edges to get a line along 'x'.
  802. Vector endPts[2];
  803. VectorLerp( m_BaseSurfacePositions[0], m_BaseSurfacePositions[1], y*ooInt, endPts[0] );
  804. VectorLerp( m_BaseSurfacePositions[3], m_BaseSurfacePositions[2], y*ooInt, endPts[1] );
  805. // Lerp along the X line.
  806. Vector vOutputPos;
  807. VectorLerp( endPts[0], endPts[1], x*ooInt, vOutputPos );
  808. // This can be used to verify that the position generated here is correct.
  809. // It should be the same as CCoreDispInfo::GetFlatVert.
  810. // Assert( vOutputPos.DistTo( m_Verts[iVertex].m_vFlatPos ) < 0.1f );
  811. // Voila!
  812. return vOutputPos;
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Computes the texture + lightmap coordinate given a displacement uv
  816. //-----------------------------------------------------------------------------
  817. void CDispInfo::ComputeLightmapAndTextureCoordinate( RayDispOutput_t const& output,
  818. Vector2D* luv, Vector2D* tuv )
  819. {
  820. #ifndef SWDS
  821. // lightmap coordinate
  822. if( luv )
  823. {
  824. ComputePointFromBarycentric(
  825. m_MeshReader.TexCoordVector2D( output.ndxVerts[0], DISP_LMCOORDS_STAGE ),
  826. m_MeshReader.TexCoordVector2D( output.ndxVerts[1], DISP_LMCOORDS_STAGE ),
  827. m_MeshReader.TexCoordVector2D( output.ndxVerts[2], DISP_LMCOORDS_STAGE ),
  828. output.u, output.v, *luv );
  829. // luv is in the space of the accumulated lightmap page; we need to convert
  830. // it to be in the space of the surface
  831. int lightmapPageWidth, lightmapPageHeight;
  832. materials->GetLightmapPageSize(
  833. SortInfoToLightmapPage(MSurf_MaterialSortID( m_ParentSurfID ) ),
  834. &lightmapPageWidth, &lightmapPageHeight );
  835. luv->x *= lightmapPageWidth;
  836. luv->y *= lightmapPageHeight;
  837. luv->x -= 0.5f + MSurf_OffsetIntoLightmapPage( m_ParentSurfID )[0];
  838. luv->y -= 0.5f + MSurf_OffsetIntoLightmapPage( m_ParentSurfID )[1];
  839. }
  840. // texture coordinate
  841. if( tuv )
  842. {
  843. // Compute base face (u,v) at each of the three vertices
  844. int size = (1 << m_Power) + 1;
  845. Vector2D baseUV[3];
  846. for (int i = 0; i < 3; ++i )
  847. {
  848. baseUV[i].y = (int)(output.ndxVerts[i] / size);
  849. baseUV[i].x = output.ndxVerts[i] - size * baseUV[i].y;
  850. baseUV[i] /= size - 1;
  851. }
  852. Vector2D basefaceUV;
  853. ComputePointFromBarycentric( baseUV[0], baseUV[1], baseUV[2],
  854. output.u, output.v, basefaceUV );
  855. // Convert the base face uv to a texture uv based on the base face texture coords
  856. TexCoordInQuadFromBarycentric( m_BaseSurfaceTexCoords[0],
  857. m_BaseSurfaceTexCoords[3], m_BaseSurfaceTexCoords[2], m_BaseSurfaceTexCoords[1],
  858. basefaceUV, *tuv );
  859. }
  860. #endif
  861. }
  862. //-----------------------------------------------------------------------------
  863. // Cast a ray against this surface
  864. //-----------------------------------------------------------------------------
  865. bool CDispInfo::TestRay( Ray_t const& ray, float start, float end, float& dist,
  866. Vector2D* luv, Vector2D* tuv )
  867. {
  868. // Get the index associated with this disp info....
  869. int idx = DispInfo_ComputeIndex( host_state.worldbrush->hDispInfos, this );
  870. CDispCollTree* pTree = CollisionBSPData_GetCollisionTree( idx );
  871. if (!pTree)
  872. return false;
  873. CBaseTrace tr;
  874. tr.fraction = 1.0f;
  875. // Only test the portion of the ray between start and end
  876. Vector startpt, endpt,endpt2;
  877. VectorMA( ray.m_Start, start, ray.m_Delta, startpt );
  878. VectorMA( ray.m_Start, end, ray.m_Delta, endpt );
  879. Ray_t shortenedRay;
  880. shortenedRay.Init( startpt, endpt );
  881. RayDispOutput_t output;
  882. output.dist = 1.0f;
  883. if (pTree->AABBTree_Ray( shortenedRay, output ))
  884. {
  885. Assert( (output.u <= 1.0f) && (output.v <= 1.0f ));
  886. Assert( (output.u >= 0.0f) && (output.v >= 0.0f ));
  887. // Compute the actual distance along the ray
  888. dist = start * (1.0f - output.dist) + end * output.dist;
  889. // Compute lightmap + texture coordinates
  890. ComputeLightmapAndTextureCoordinate( output, luv, tuv );
  891. return true;
  892. }
  893. return false;
  894. }
  895. const CPowerInfo* CDispInfo::GetPowerInfo() const
  896. {
  897. return m_pPowerInfo;
  898. }
  899. CDispNeighbor* CDispInfo::GetEdgeNeighbor( int index )
  900. {
  901. Assert( index >= 0 && index < ARRAYSIZE( m_EdgeNeighbors ) );
  902. return &m_EdgeNeighbors[index];
  903. }
  904. CDispCornerNeighbors* CDispInfo::GetCornerNeighbors( int index )
  905. {
  906. Assert( index >= 0 && index < ARRAYSIZE( m_CornerNeighbors ) );
  907. return &m_CornerNeighbors[index];
  908. }
  909. CDispUtilsHelper* CDispInfo::GetDispUtilsByIndex( int index )
  910. {
  911. return GetDispByIndex( index );
  912. }