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

1203 lines
36 KiB

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