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.

580 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "render_pch.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. // Leaf visualization routines
  12. struct leafvis_t
  13. {
  14. leafvis_t()
  15. {
  16. leafIndex = 0;
  17. CCollisionBSPData *pBSP = GetCollisionBSPData();
  18. if ( pBSP )
  19. {
  20. numbrushes = pBSP->numbrushes;
  21. numentitychars = pBSP->numentitychars;
  22. }
  23. }
  24. bool IsValid()
  25. {
  26. CCollisionBSPData *pBSP = GetCollisionBSPData();
  27. if ( !pBSP || numbrushes != pBSP->numbrushes || numentitychars != pBSP->numentitychars )
  28. return false;
  29. return true;
  30. }
  31. CUtlVector<Vector> verts;
  32. CUtlVector<int> polyVertCount;
  33. Vector color;
  34. int numbrushes;
  35. int numentitychars;
  36. int leafIndex;
  37. };
  38. const int MAX_LEAF_PVERTS = 128;
  39. // Only allocate this after it is turned on
  40. leafvis_t *g_LeafVis = NULL;
  41. static void AddPlaneToList( CUtlVector<cplane_t> &list, const Vector& normal, float dist, int invert )
  42. {
  43. cplane_t plane;
  44. plane.dist = invert ? -dist : dist;
  45. plane.normal = invert ? -normal : normal;
  46. Vector point = plane.dist * plane.normal;
  47. for ( int i = 0; i < list.Count(); i++ )
  48. {
  49. // same plane, remove or replace
  50. if ( list[i].normal == plane.normal )
  51. {
  52. float d = DotProduct(point, list[i].normal) - list[i].dist;
  53. if ( d > 0 )
  54. {
  55. // new plane is in front of the old one
  56. list[i].dist = plane.dist;
  57. }
  58. // new plane is behind the old one
  59. return;
  60. }
  61. }
  62. list.AddToTail( plane );
  63. }
  64. static void PlaneList( int leafIndex, model_t *model, CUtlVector<cplane_t> &planeList )
  65. {
  66. if (!model || !model->brush.pShared || !model->brush.pShared->nodes)
  67. Sys_Error ("PlaneList: bad model");
  68. mleaf_t *pLeaf = &model->brush.pShared->leafs[leafIndex];
  69. mnode_t *pNode = pLeaf->parent;
  70. mnode_t *pChild = (mnode_t *)pLeaf;
  71. while (pNode)
  72. {
  73. // was the child on the front or back of the plane of this node?
  74. bool front = (pNode->children[0] == pChild) ? true : false;
  75. AddPlaneToList( planeList, pNode->plane->normal, pNode->plane->dist, !front );
  76. pChild = pNode;
  77. pNode = pNode->parent;
  78. }
  79. }
  80. Vector CSGInsidePoint( cplane_t *pPlanes, int planeCount )
  81. {
  82. Vector point = vec3_origin;
  83. for ( int i = 0; i < planeCount; i++ )
  84. {
  85. float d = DotProduct( pPlanes[i].normal, point ) - pPlanes[i].dist;
  86. if ( d < 0 )
  87. {
  88. point -= d * pPlanes[i].normal;
  89. }
  90. }
  91. return point;
  92. }
  93. void TranslatePlaneList( cplane_t *pPlanes, int planeCount, const Vector &offset )
  94. {
  95. for ( int i = 0; i < planeCount; i++ )
  96. {
  97. pPlanes[i].dist += DotProduct( offset, pPlanes[i].normal );
  98. }
  99. }
  100. void CSGPlaneList( leafvis_t *pVis, CUtlVector<cplane_t> &planeList)
  101. {
  102. int planeCount = planeList.Count();
  103. Vector vertsIn[MAX_LEAF_PVERTS], vertsOut[MAX_LEAF_PVERTS];
  104. // compute a point inside the volume defined by these planes
  105. Vector insidePoint = CSGInsidePoint( planeList.Base(), planeList.Count() );
  106. // move the planes so that the inside point is at the origin
  107. // NOTE: This is to maximize precision for the CSG operations
  108. TranslatePlaneList( planeList.Base(), planeList.Count(), -insidePoint );
  109. // Build the CSG solid of this leaf given that the planes in the list define a convex solid
  110. for ( int i = 0; i < planeCount; i++ )
  111. {
  112. // Build a big-ass poly in this plane
  113. int vertCount = PolyFromPlane( vertsIn, planeList[i].normal, planeList[i].dist ); // BaseWindingForPlane()
  114. // Now chop it by every other plane
  115. int j;
  116. for ( j = 0; j < planeCount; j++ )
  117. {
  118. // don't clip planes with themselves
  119. if ( i == j )
  120. continue;
  121. // Less than a poly left, something's wrong, don't bother with this polygon
  122. if ( vertCount < 3 )
  123. continue;
  124. // Chop the polygon against this plane
  125. vertCount = ClipPolyToPlane( vertsIn, vertCount, vertsOut, planeList[j].normal, planeList[j].dist );
  126. // Just copy the verts each time, don't bother swapping pointers (efficiency is not a goal here)
  127. for ( int k = 0; k < vertCount; k++ )
  128. {
  129. VectorCopy( vertsOut[k], vertsIn[k] );
  130. }
  131. }
  132. // We've got a polygon here
  133. if ( vertCount >= 3 )
  134. {
  135. // Copy polygon out
  136. pVis->polyVertCount.AddToTail( vertCount );
  137. for ( j = 0; j < vertCount; j++ )
  138. {
  139. // move the verts back by the initial translation
  140. Vector vert = vertsIn[j] + insidePoint;
  141. pVis->verts.AddToTail( vert );
  142. }
  143. }
  144. }
  145. }
  146. void LeafvisChanged( IConVar *pLeafvisVar, const char *pOld, float flOldValue )
  147. {
  148. if ( g_LeafVis )
  149. {
  150. delete g_LeafVis;
  151. g_LeafVis = NULL;
  152. }
  153. }
  154. void AddLeafPortals( leafvis_t *pLeafvis, int leafIndex )
  155. {
  156. CUtlVector<cplane_t> planeList;
  157. Vector normal;
  158. // Build a list of inward pointing planes of the tree descending to this
  159. PlaneList( leafIndex, host_state.worldmodel, planeList );
  160. VectorCopy( vec3_origin, normal );
  161. // Add world bounding box planes in case the world isn't closed
  162. // x-axis
  163. normal[0] = 1;
  164. AddPlaneToList( planeList, normal, MAX_COORD_INTEGER, true );
  165. AddPlaneToList( planeList, normal, -MAX_COORD_INTEGER, false );
  166. normal[0] = 0;
  167. // y-axis
  168. normal[1] = 1;
  169. AddPlaneToList( planeList, normal, MAX_COORD_INTEGER, true );
  170. AddPlaneToList( planeList, normal, -MAX_COORD_INTEGER, false );
  171. normal[1] = 0;
  172. // z-axis
  173. normal[2] = 1;
  174. AddPlaneToList( planeList, normal, MAX_COORD_INTEGER, true );
  175. AddPlaneToList( planeList, normal, -MAX_COORD_INTEGER, false );
  176. CSGPlaneList( pLeafvis, planeList );
  177. }
  178. ConVar mat_leafvis("mat_leafvis","0", FCVAR_CHEAT, "Draw wireframe of current leaf", LeafvisChanged );
  179. ConVar r_visambient("r_visambient","0", 0, "Draw leaf ambient lighting samples. Needs mat_leafvis 1 to work" );
  180. //-----------------------------------------------------------------------------
  181. // Purpose: Builds a convex polyhedron of the leaf boundary around p
  182. // Input : p - point to classify determining the leaf
  183. //-----------------------------------------------------------------------------
  184. void LeafVisBuild( const Vector &p )
  185. {
  186. if ( !mat_leafvis.GetInt() )
  187. {
  188. Assert( !g_LeafVis );
  189. return;
  190. }
  191. else
  192. {
  193. static int last_leaf = -1;
  194. int leafIndex = CM_PointLeafnum( p );
  195. if ( g_LeafVis && last_leaf == leafIndex )
  196. return;
  197. DevMsg( 1, "Leaf %d, Area %d, Cluster %d\n", leafIndex, CM_LeafArea( leafIndex ), CM_LeafCluster( leafIndex ) );
  198. last_leaf = leafIndex;
  199. delete g_LeafVis;
  200. g_LeafVis = new leafvis_t;
  201. g_LeafVis->color.Init( 1.0f, 0.0f, 0.0f );
  202. g_LeafVis->leafIndex = leafIndex;
  203. switch( mat_leafvis.GetInt() )
  204. {
  205. case 2:
  206. {
  207. const mleaf_t *pLeaf = host_state.worldmodel->brush.pShared->leafs;
  208. int leafCount = host_state.worldmodel->brush.pShared->numleafs;
  209. int visCluster = pLeaf[leafIndex].cluster;
  210. // do entire viscluster
  211. for ( int i = 0; i < leafCount; i++ )
  212. {
  213. if ( pLeaf[i].cluster == visCluster )
  214. {
  215. AddLeafPortals( g_LeafVis, i );
  216. }
  217. }
  218. }
  219. break;
  220. case 3:
  221. {
  222. // do entire pvs
  223. byte pvs[ MAX_MAP_LEAFS/8 ];
  224. const mleaf_t *pLeaf = host_state.worldmodel->brush.pShared->leafs;
  225. int leafCount = host_state.worldmodel->brush.pShared->numleafs;
  226. int visCluster = pLeaf[leafIndex].cluster;
  227. CM_Vis( pvs, sizeof( pvs ), visCluster, DVIS_PVS );
  228. for ( int i = 0; i < leafCount; i++ )
  229. {
  230. int cluster = pLeaf[i].cluster;
  231. if ( cluster >= 0 && (pvs[cluster>>3] & (1<<(cluster&7))) )
  232. {
  233. AddLeafPortals( g_LeafVis, i );
  234. }
  235. }
  236. }
  237. break;
  238. case 0:
  239. default:
  240. AddLeafPortals( g_LeafVis, leafIndex );
  241. break;
  242. }
  243. }
  244. }
  245. #ifndef SWDS
  246. void DrawLeafvis( leafvis_t *pVis )
  247. {
  248. CMatRenderContextPtr pRenderContext( materials );
  249. int vert = 0;
  250. g_materialLeafVisWireframe->ColorModulate( pVis->color[0], pVis->color[1], pVis->color[2] );
  251. pRenderContext->Bind( g_materialLeafVisWireframe );
  252. for ( int i = 0; i < pVis->polyVertCount.Count(); i++ )
  253. {
  254. if ( pVis->polyVertCount[i] >= 3 )
  255. {
  256. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  257. CMeshBuilder meshBuilder;
  258. meshBuilder.Begin( pMesh, MATERIAL_LINES, pVis->polyVertCount[i] );
  259. for ( int j = 0; j < pVis->polyVertCount[i]; j++ )
  260. {
  261. meshBuilder.Position3fv( pVis->verts[ vert + j ].Base() );
  262. meshBuilder.AdvanceVertex();
  263. meshBuilder.Position3fv( pVis->verts[ vert + ( ( j + 1 ) % pVis->polyVertCount[i] ) ].Base() );
  264. meshBuilder.AdvanceVertex();
  265. }
  266. meshBuilder.End();
  267. pMesh->Draw();
  268. }
  269. vert += pVis->polyVertCount[i];
  270. }
  271. }
  272. void DrawLeafvis_Solid( leafvis_t *pVis )
  273. {
  274. CMatRenderContextPtr pRenderContext( materials );
  275. int vert = 0;
  276. Vector lightNormal(1,1,1);
  277. VectorNormalize(lightNormal);
  278. pRenderContext->Bind( g_pMaterialDebugFlat );
  279. for ( int i = 0; i < pVis->polyVertCount.Count(); i++ )
  280. {
  281. int vertCount = pVis->polyVertCount[i];
  282. if ( vertCount >= 3 )
  283. {
  284. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  285. CMeshBuilder meshBuilder;
  286. int triangleCount = vertCount-2;
  287. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, triangleCount );
  288. Vector e0 = pVis->verts[vert+1] - pVis->verts[vert];
  289. Vector e1 = pVis->verts[vert+2] - pVis->verts[vert];
  290. Vector normal = CrossProduct(e1,e0);
  291. VectorNormalize( normal );
  292. float light = 0.5f + (DotProduct(normal, lightNormal)*0.5f);
  293. Vector color = pVis->color * light;
  294. for ( int j = 0; j < vertCount; j++ )
  295. {
  296. meshBuilder.Position3fv( pVis->verts[ vert + j ].Base() );
  297. meshBuilder.Color3fv( color.Base() );
  298. meshBuilder.AdvanceVertex();
  299. }
  300. for ( int j = 0; j < triangleCount; j++ )
  301. {
  302. meshBuilder.FastIndex(0);
  303. meshBuilder.FastIndex(j+2);
  304. meshBuilder.FastIndex(j+1);
  305. }
  306. meshBuilder.End();
  307. pMesh->Draw();
  308. }
  309. vert += vertCount;
  310. }
  311. }
  312. leafvis_t *g_FrustumVis = NULL, *g_ClipVis[3] = {NULL,NULL,NULL};
  313. int FindMinBrush( CCollisionBSPData *pBSPData, int nodenum, int brushIndex )
  314. {
  315. while (1)
  316. {
  317. if (nodenum < 0)
  318. {
  319. int leafIndex = -1 - nodenum;
  320. cleaf_t &leaf = pBSPData->map_leafs[leafIndex];
  321. int firstbrush = pBSPData->map_leafbrushes[ leaf.firstleafbrush ];
  322. if ( firstbrush < brushIndex )
  323. {
  324. brushIndex = firstbrush;
  325. }
  326. return brushIndex;
  327. }
  328. cnode_t &node = pBSPData->map_rootnode[nodenum];
  329. brushIndex = FindMinBrush( pBSPData, node.children[0], brushIndex );
  330. nodenum = node.children[1];
  331. }
  332. return brushIndex;
  333. }
  334. void RecomputeClipbrushes( bool bEnabled )
  335. {
  336. for ( int v = 0; v < 3; v++ )
  337. {
  338. delete g_ClipVis[v];
  339. g_ClipVis[v] = NULL;
  340. }
  341. if ( !bEnabled )
  342. return;
  343. for ( int v = 0; v < 3; v++ )
  344. {
  345. int contents[3] = {CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP, CONTENTS_MONSTERCLIP, CONTENTS_PLAYERCLIP};
  346. g_ClipVis[v] = new leafvis_t;
  347. g_ClipVis[v]->color.Init( v != 1 ? 1.0f : 0.5, 0.0f, v != 0 ? 1.0f : 0.0f );
  348. CCollisionBSPData *pBSP = GetCollisionBSPData();
  349. int lastBrush = pBSP->numbrushes;
  350. if ( pBSP->numcmodels > 1 )
  351. {
  352. lastBrush = FindMinBrush( pBSP, pBSP->map_cmodels[1].headnode, lastBrush );
  353. }
  354. for ( int i = 0; i < lastBrush; i++ )
  355. {
  356. cbrush_t *pBrush = &pBSP->map_brushes[i];
  357. if ( (pBrush->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP)) == contents[v] )
  358. {
  359. CUtlVector<cplane_t> planeList;
  360. if ( pBrush->IsBox() )
  361. {
  362. cboxbrush_t *pBox = &pBSP->map_boxbrushes[pBrush->GetBox()];
  363. for ( int idxSide = 0; idxSide < 3; idxSide++ )
  364. {
  365. Vector normal = vec3_origin;
  366. normal[idxSide] = 1.0f;
  367. AddPlaneToList( planeList, normal, pBox->maxs[idxSide], true );
  368. AddPlaneToList( planeList, -normal, -pBox->mins[idxSide], true );
  369. }
  370. }
  371. else
  372. {
  373. for ( int j = 0; j < pBrush->numsides; j++ )
  374. {
  375. cbrushside_t *pSide = &pBSP->map_brushsides[pBrush->firstbrushside + j];
  376. if ( pSide->bBevel )
  377. continue;
  378. AddPlaneToList( planeList, pSide->plane->normal, pSide->plane->dist, true );
  379. }
  380. }
  381. CSGPlaneList( g_ClipVis[v], planeList );
  382. }
  383. }
  384. }
  385. }
  386. // NOTE: UNDONE: This doesn't work on brush models - only the world.
  387. void ClipChanged( IConVar *pConVar, const char *pOld, float flOldValue )
  388. {
  389. ConVarRef clipVar( pConVar );
  390. RecomputeClipbrushes( clipVar.GetBool() );
  391. }
  392. static ConVar r_drawclipbrushes( "r_drawclipbrushes", "0", FCVAR_CHEAT, "Draw clip brushes (red=NPC+player, pink=player, purple=NPC)", ClipChanged );
  393. static Vector LeafAmbientSamplePos( int leafIndex, const mleafambientlighting_t &sample )
  394. {
  395. mleaf_t *pLeaf = &host_state.worldbrush->leafs[leafIndex];
  396. Vector out = pLeaf->m_vecCenter - pLeaf->m_vecHalfDiagonal;
  397. out.x += float(sample.x) * pLeaf->m_vecHalfDiagonal.x * (2.0f / 255.0f);
  398. out.y += float(sample.y) * pLeaf->m_vecHalfDiagonal.y * (2.0f / 255.0f);
  399. out.z += float(sample.z) * pLeaf->m_vecHalfDiagonal.z * (2.0f / 255.0f);
  400. return out;
  401. }
  402. // convert color formats
  403. static void ColorRGBExp32ToColor32( const ColorRGBExp32 &color, color32 &out )
  404. {
  405. Vector tmp;
  406. ColorRGBExp32ToVector( color, tmp );
  407. out.r = LinearToScreenGamma(tmp.x);
  408. out.g = LinearToScreenGamma(tmp.y);
  409. out.b = LinearToScreenGamma(tmp.z);
  410. }
  411. // some simple helpers to draw a cube in the special way the ambient visualization wants
  412. static Vector CubeSide( const Vector &pos, float size, int vert )
  413. {
  414. Vector side = pos;
  415. side.x += (vert & 1) ? -size : size;
  416. side.y += (vert & 2) ? -size : size;
  417. side.z += (vert & 4) ? -size : size;
  418. return side;
  419. }
  420. static void CubeFace( CMeshBuilder &meshBuilder, const Vector org, int v0, int v1, int v2, int v3, float size, const color32 &color )
  421. {
  422. meshBuilder.Position3fv( CubeSide(org,size,v0).Base() );
  423. meshBuilder.Color4ubv( (byte *)&color );
  424. meshBuilder.AdvanceVertex();
  425. meshBuilder.Position3fv( CubeSide(org,size,v1).Base() );
  426. meshBuilder.Color4ubv( (byte *)&color );
  427. meshBuilder.AdvanceVertex();
  428. meshBuilder.Position3fv( CubeSide(org,size,v2).Base() );
  429. meshBuilder.Color4ubv( (byte *)&color );
  430. meshBuilder.AdvanceVertex();
  431. meshBuilder.Position3fv( CubeSide(org,size,v3).Base() );
  432. meshBuilder.Color4ubv( (byte *)&color );
  433. meshBuilder.AdvanceVertex();
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose: Draw the leaf geometry that was computed by LeafVisBuild()
  437. //-----------------------------------------------------------------------------
  438. void LeafVisDraw( void )
  439. {
  440. if ( g_FrustumVis )
  441. {
  442. DrawLeafvis( g_FrustumVis );
  443. }
  444. if ( g_LeafVis )
  445. {
  446. DrawLeafvis( g_LeafVis );
  447. }
  448. if ( g_ClipVis[0] )
  449. {
  450. if ( !g_ClipVis[0]->IsValid() )
  451. {
  452. RecomputeClipbrushes(true);
  453. }
  454. if ( r_drawclipbrushes.GetInt() == 2 )
  455. {
  456. DrawLeafvis_Solid( g_ClipVis[0] );
  457. DrawLeafvis_Solid( g_ClipVis[1] );
  458. DrawLeafvis_Solid( g_ClipVis[2] );
  459. }
  460. else
  461. {
  462. DrawLeafvis( g_ClipVis[0] );
  463. DrawLeafvis( g_ClipVis[1] );
  464. DrawLeafvis( g_ClipVis[2] );
  465. }
  466. }
  467. if ( g_LeafVis && r_visambient.GetBool() )
  468. {
  469. CMatRenderContextPtr pRenderContext( materials );
  470. pRenderContext->Bind( g_pMaterialDebugFlat );
  471. float cubesize = 12.0f;
  472. int leafIndex = g_LeafVis->leafIndex;
  473. mleafambientindex_t *pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  474. if ( !pAmbient->ambientSampleCount && pAmbient->firstAmbientSample )
  475. {
  476. // this leaf references another leaf, move there (this leaf is a solid leaf so it borrows samples from a neighbor)
  477. leafIndex = pAmbient->firstAmbientSample;
  478. pAmbient = &host_state.worldbrush->m_pLeafAmbient[leafIndex];
  479. }
  480. for ( int i = 0; i < pAmbient->ambientSampleCount; i++ )
  481. {
  482. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  483. CMeshBuilder meshBuilder;
  484. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 6 );
  485. const mleafambientlighting_t &sample = host_state.worldbrush->m_pAmbientSamples[pAmbient->firstAmbientSample+i];
  486. Vector pos = LeafAmbientSamplePos( leafIndex, sample );
  487. // x axis
  488. color32 color;
  489. ColorRGBExp32ToColor32( sample.cube.m_Color[0], color ); // x
  490. CubeFace( meshBuilder, pos, 4, 6, 2, 0, cubesize, color );
  491. ColorRGBExp32ToColor32( sample.cube.m_Color[1], color ); // -x
  492. CubeFace( meshBuilder, pos, 7, 5, 1, 3, cubesize, color );
  493. ColorRGBExp32ToColor32( sample.cube.m_Color[2], color ); // y
  494. CubeFace( meshBuilder, pos, 0, 1, 5, 4, cubesize, color );
  495. ColorRGBExp32ToColor32( sample.cube.m_Color[3], color ); // -y
  496. CubeFace( meshBuilder, pos, 3, 2, 6, 7, cubesize, color );
  497. ColorRGBExp32ToColor32( sample.cube.m_Color[4], color ); // z
  498. CubeFace( meshBuilder, pos, 2, 3, 1, 0, cubesize, color );
  499. ColorRGBExp32ToColor32( sample.cube.m_Color[5], color ); // -z
  500. CubeFace( meshBuilder, pos, 4, 5, 7, 6, cubesize, color );
  501. meshBuilder.End();
  502. pMesh->Draw();
  503. }
  504. }
  505. }
  506. void CSGFrustum( Frustum_t &frustum )
  507. {
  508. delete g_FrustumVis;
  509. g_FrustumVis = new leafvis_t;
  510. g_FrustumVis->color.Init(1.0f, 1.0f, 1.0f);
  511. CUtlVector<cplane_t> planeList;
  512. for ( int i = 0; i < 6; i++ )
  513. {
  514. planeList.AddToTail( *frustum.GetPlane( i ) );
  515. }
  516. CSGPlaneList( g_FrustumVis, planeList );
  517. }
  518. #endif