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.

1652 lines
47 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include <stdio.h>
  8. #include "mathlib/vector.h"
  9. #include "bspfile.h"
  10. #include "bsplib.h"
  11. #include "cmdlib.h"
  12. #include "physdll.h"
  13. #include "utlvector.h"
  14. #include "vbsp.h"
  15. #include "phyfile.h"
  16. #include <float.h>
  17. #include "KeyValues.h"
  18. #include "UtlBuffer.h"
  19. #include "UtlSymbol.h"
  20. #include "UtlRBTree.h"
  21. #include "ivp.h"
  22. #include "disp_ivp.h"
  23. #include "materialpatch.h"
  24. #include "bitvec.h"
  25. #include "tier3/tier3.h"
  26. // bit per leaf
  27. typedef CBitVec<MAX_MAP_LEAFS> leafbitarray_t;
  28. // parameters for conversion to vphysics
  29. #define NO_SHRINK 0.0f
  30. // NOTE: vphysics maintains a minimum separation radius between objects
  31. // This radius is set to 0.25, but it's symmetric. So shrinking potentially moveable
  32. // brushes by 0.5 in every direction ensures that these brushes can be constructed
  33. // touching the world, and constrained in place without collisions or friction
  34. // UNDONE: Add a key to disable this shrinking if necessary
  35. #define VPHYSICS_SHRINK (0.5f) // shrink BSP brushes by this much for collision
  36. #define VPHYSICS_MERGE 0.01f // merge verts closer than this
  37. void EmitPhysCollision();
  38. IPhysicsCollision *physcollision = NULL;
  39. extern IPhysicsSurfaceProps *physprops;
  40. // a list of all of the materials in the world model
  41. static CUtlVector<int> s_WorldPropList;
  42. //-----------------------------------------------------------------------------
  43. // Purpose: Write key/value pairs out to a memory buffer
  44. //-----------------------------------------------------------------------------
  45. CTextBuffer::CTextBuffer( void )
  46. {
  47. }
  48. CTextBuffer::~CTextBuffer( void )
  49. {
  50. }
  51. void CTextBuffer::WriteText( const char *pText )
  52. {
  53. int len = strlen( pText );
  54. CopyData( pText, len );
  55. }
  56. void CTextBuffer::WriteIntKey( const char *pKeyName, int outputData )
  57. {
  58. char tmp[1024];
  59. // FAIL!
  60. if ( strlen(pKeyName) > 1000 )
  61. {
  62. Msg("Error writing collision data %s\n", pKeyName );
  63. return;
  64. }
  65. sprintf( tmp, "\"%s\" \"%d\"\n", pKeyName, outputData );
  66. CopyData( tmp, strlen(tmp) );
  67. }
  68. void CTextBuffer::WriteStringKey( const char *pKeyName, const char *outputData )
  69. {
  70. CopyStringQuotes( pKeyName );
  71. CopyData( " ", 1 );
  72. CopyStringQuotes( outputData );
  73. CopyData( "\n", 1 );
  74. }
  75. void CTextBuffer::WriteFloatKey( const char *pKeyName, float outputData )
  76. {
  77. char tmp[1024];
  78. // FAIL!
  79. if ( strlen(pKeyName) > 1000 )
  80. {
  81. Msg("Error writing collision data %s\n", pKeyName );
  82. return;
  83. }
  84. sprintf( tmp, "\"%s\" \"%f\"\n", pKeyName, outputData );
  85. CopyData( tmp, strlen(tmp) );
  86. }
  87. void CTextBuffer::WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count )
  88. {
  89. char tmp[1024];
  90. // FAIL!
  91. if ( strlen(pKeyName) > 1000 )
  92. {
  93. Msg("Error writing collision data %s\n", pKeyName );
  94. return;
  95. }
  96. sprintf( tmp, "\"%s\" \"", pKeyName );
  97. for ( int i = 0; i < count; i++ )
  98. {
  99. char buf[80];
  100. sprintf( buf, "%f ", outputData[i] );
  101. strcat( tmp, buf );
  102. }
  103. strcat( tmp, "\"\n" );
  104. CopyData( tmp, strlen(tmp) );
  105. }
  106. void CTextBuffer::CopyStringQuotes( const char *pString )
  107. {
  108. CopyData( "\"", 1 );
  109. CopyData( pString, strlen(pString) );
  110. CopyData( "\"", 1 );
  111. }
  112. void CTextBuffer::Terminate( void )
  113. {
  114. CopyData( "\0", 1 );
  115. }
  116. void CTextBuffer::CopyData( const char *pData, int len )
  117. {
  118. int offset = m_buffer.AddMultipleToTail( len );
  119. memcpy( m_buffer.Base() + offset, pData, len );
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Writes a glview text file containing the collision surface in question
  123. // Input : *pCollide -
  124. // *pFilename -
  125. //-----------------------------------------------------------------------------
  126. void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename )
  127. {
  128. if ( !pCollide )
  129. return;
  130. Msg("Writing %s...\n", pFilename );
  131. Vector *outVerts;
  132. int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
  133. FILE *fp = fopen( pFilename, "w" );
  134. int triCount = vertCount / 3;
  135. int vert = 0;
  136. for ( int i = 0; i < triCount; i++ )
  137. {
  138. fprintf( fp, "3\n" );
  139. fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  140. vert++;
  141. fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  142. vert++;
  143. fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
  144. vert++;
  145. }
  146. fclose( fp );
  147. physcollision->DestroyDebugMesh( vertCount, outVerts );
  148. }
  149. void DumpCollideToPHY( CPhysCollide *pCollide, CTextBuffer *text, const char *pFilename )
  150. {
  151. Msg("Writing %s...\n", pFilename );
  152. FILE *fp = fopen( pFilename, "wb" );
  153. phyheader_t header;
  154. header.size = sizeof(header);
  155. header.id = 0;
  156. header.checkSum = 0;
  157. header.solidCount = 1;
  158. fwrite( &header, sizeof(header), 1, fp );
  159. int size = physcollision->CollideSize( pCollide );
  160. fwrite( &size, sizeof(int), 1, fp );
  161. char *buf = (char *)malloc( size );
  162. physcollision->CollideWrite( buf, pCollide );
  163. fwrite( buf, size, 1, fp );
  164. fwrite( text->GetData(), text->GetSize(), 1, fp );
  165. fclose( fp );
  166. free( buf );
  167. }
  168. CPhysCollisionEntry::CPhysCollisionEntry( CPhysCollide *pCollide )
  169. {
  170. m_pCollide = pCollide;
  171. }
  172. unsigned int CPhysCollisionEntry::GetCollisionBinarySize()
  173. {
  174. return physcollision->CollideSize( m_pCollide );
  175. }
  176. unsigned int CPhysCollisionEntry::WriteCollisionBinary( char *pDest )
  177. {
  178. return physcollision->CollideWrite( pDest, m_pCollide );
  179. }
  180. void CPhysCollisionEntry::DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer )
  181. {
  182. char tmp[128];
  183. sprintf( tmp, "%s%03d.phy", pName, modelIndex );
  184. DumpCollideToPHY( m_pCollide, pTextBuffer, tmp );
  185. sprintf( tmp, "%s%03d.txt", pName, modelIndex );
  186. DumpCollideToGlView( m_pCollide, tmp );
  187. }
  188. class CPhysCollisionEntrySolid : public CPhysCollisionEntry
  189. {
  190. public:
  191. CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass );
  192. virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  193. virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  194. private:
  195. float m_volume;
  196. float m_mass;
  197. const char *m_pMaterial;
  198. };
  199. CPhysCollisionEntrySolid::CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass )
  200. : CPhysCollisionEntry( pCollide )
  201. {
  202. m_volume = physcollision->CollideVolume( m_pCollide );
  203. m_mass = mass;
  204. m_pMaterial = pMaterialName;
  205. }
  206. void CPhysCollisionEntrySolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  207. {
  208. DumpCollideFileName( "collide", modelIndex, pTextBuffer );
  209. }
  210. void CPhysCollisionEntrySolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  211. {
  212. pTextBuffer->WriteText( "solid {\n" );
  213. pTextBuffer->WriteIntKey( "index", collideIndex );
  214. pTextBuffer->WriteFloatKey( "mass", m_mass );
  215. if ( m_pMaterial )
  216. {
  217. pTextBuffer->WriteStringKey( "surfaceprop", m_pMaterial );
  218. }
  219. if ( m_volume != 0.f )
  220. {
  221. pTextBuffer->WriteFloatKey( "volume", m_volume );
  222. }
  223. pTextBuffer->WriteText( "}\n" );
  224. }
  225. class CPhysCollisionEntryStaticSolid : public CPhysCollisionEntry
  226. {
  227. public:
  228. CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask );
  229. virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  230. virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  231. private:
  232. int m_contentsMask;
  233. };
  234. CPhysCollisionEntryStaticSolid ::CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask )
  235. : CPhysCollisionEntry( pCollide ), m_contentsMask(contentsMask)
  236. {
  237. }
  238. void CPhysCollisionEntryStaticSolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  239. {
  240. char tmp[128];
  241. sprintf( tmp, "static%02d", modelIndex );
  242. DumpCollideFileName( tmp, collideIndex, pTextBuffer );
  243. }
  244. void CPhysCollisionEntryStaticSolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  245. {
  246. pTextBuffer->WriteText( "staticsolid {\n" );
  247. pTextBuffer->WriteIntKey( "index", collideIndex );
  248. pTextBuffer->WriteIntKey( "contents", m_contentsMask );
  249. pTextBuffer->WriteText( "}\n" );
  250. }
  251. CPhysCollisionEntryStaticMesh::CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName )
  252. : CPhysCollisionEntry( pCollide )
  253. {
  254. m_pMaterial = pMaterialName;
  255. }
  256. void CPhysCollisionEntryStaticMesh::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  257. {
  258. char tmp[128];
  259. sprintf( tmp, "mesh%02d", modelIndex );
  260. DumpCollideFileName( tmp, collideIndex, pTextBuffer );
  261. }
  262. void CPhysCollisionEntryStaticMesh::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  263. {
  264. pTextBuffer->WriteText( "staticsolid {\n" );
  265. pTextBuffer->WriteIntKey( "index", collideIndex );
  266. pTextBuffer->WriteText( "}\n" );
  267. }
  268. class CPhysCollisionEntryFluid : public CPhysCollisionEntry
  269. {
  270. public:
  271. ~CPhysCollisionEntryFluid();
  272. CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents );
  273. virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  274. virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
  275. private:
  276. char *m_pSurfaceProp;
  277. float m_damping;
  278. Vector m_surfaceNormal;
  279. float m_surfaceDist;
  280. int m_contentsMask;
  281. };
  282. CPhysCollisionEntryFluid::CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents )
  283. : CPhysCollisionEntry( pCollide )
  284. {
  285. m_surfaceNormal = normal;
  286. m_surfaceDist = dist;
  287. m_pSurfaceProp = new char[strlen(pSurfaceProp)+1];
  288. strcpy( m_pSurfaceProp, pSurfaceProp );
  289. m_damping = damping;
  290. m_contentsMask = nContents;
  291. }
  292. CPhysCollisionEntryFluid::~CPhysCollisionEntryFluid()
  293. {
  294. delete[] m_pSurfaceProp;
  295. }
  296. void CPhysCollisionEntryFluid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  297. {
  298. char tmp[128];
  299. sprintf( tmp, "water%02d", modelIndex );
  300. DumpCollideFileName( tmp, collideIndex, pTextBuffer );
  301. }
  302. void CPhysCollisionEntryFluid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
  303. {
  304. pTextBuffer->WriteText( "fluid {\n" );
  305. pTextBuffer->WriteIntKey( "index", collideIndex );
  306. pTextBuffer->WriteStringKey( "surfaceprop", m_pSurfaceProp ); // write out water material
  307. pTextBuffer->WriteFloatKey( "damping", m_damping ); // write out water damping
  308. pTextBuffer->WriteIntKey( "contents", m_contentsMask ); // write out water contents
  309. float array[4];
  310. m_surfaceNormal.CopyToArray( array );
  311. array[3] = m_surfaceDist;
  312. pTextBuffer->WriteFloatArrayKey( "surfaceplane", array, 4 ); // write out water surface plane
  313. pTextBuffer->WriteFloatArrayKey( "currentvelocity", vec3_origin.Base(), 3 ); // write out water velocity
  314. pTextBuffer->WriteText( "}\n" );
  315. }
  316. // Get an index into the prop list of this prop (add it if necessary)
  317. static int PropIndex( CUtlVector<int> &propList, int propIndex )
  318. {
  319. for ( int i = 0; i < propList.Count(); i++ )
  320. {
  321. if ( propList[i] == propIndex )
  322. return i+1;
  323. }
  324. if ( propList.Count() < 126 )
  325. {
  326. return propList.AddToTail( propIndex )+1;
  327. }
  328. return 0;
  329. }
  330. int RemapWorldMaterial( int materialIndexIn )
  331. {
  332. return PropIndex( s_WorldPropList, materialIndexIn );
  333. }
  334. typedef struct
  335. {
  336. float normal[3];
  337. float dist;
  338. } listplane_t;
  339. static void AddListPlane( CUtlVector<listplane_t> *list, float x, float y, float z, float d )
  340. {
  341. listplane_t plane;
  342. plane.normal[0] = x;
  343. plane.normal[1] = y;
  344. plane.normal[2] = z;
  345. plane.dist = d;
  346. list->AddToTail( plane );
  347. }
  348. class CPlaneList
  349. {
  350. public:
  351. CPlaneList( float shrink, float merge );
  352. ~CPlaneList( void );
  353. void AddConvex( CPhysConvex *pConvex );
  354. // add the brushes to the model
  355. int AddBrushes( void );
  356. // Adds a single brush as a convex object
  357. void ReferenceBrush( int brushnumber );
  358. bool IsBrushReferenced( int brushnumber );
  359. void ReferenceLeaf( int leafIndex );
  360. bool IsLeafReferenced( int leafIndex );
  361. int GetFirstBrushSide();
  362. private:
  363. CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum );
  364. public:
  365. CUtlVector<CPhysConvex *> m_convex;
  366. CUtlVector<int> m_leafList;
  367. int m_contentsMask;
  368. float m_shrink;
  369. float m_merge;
  370. bool *m_brushAdded;
  371. float m_totalVolume;
  372. };
  373. CPlaneList::CPlaneList( float shrink, float merge )
  374. {
  375. m_shrink = shrink;
  376. m_merge = merge;
  377. m_contentsMask = MASK_SOLID;
  378. m_brushAdded = new bool[numbrushes];
  379. memset( m_brushAdded, 0, sizeof(bool) * numbrushes );
  380. m_totalVolume = 0;
  381. m_leafList.Purge();
  382. }
  383. CPlaneList::~CPlaneList( void )
  384. {
  385. delete[] m_brushAdded;
  386. }
  387. void CPlaneList::AddConvex( CPhysConvex *pConvex )
  388. {
  389. if ( pConvex )
  390. {
  391. m_totalVolume += physcollision->ConvexVolume( pConvex );
  392. m_convex.AddToTail( pConvex );
  393. }
  394. }
  395. // Adds a single brush as a convex object
  396. void CPlaneList::ReferenceBrush( int brushnumber )
  397. {
  398. if ( !(dbrushes[brushnumber].contents & m_contentsMask) )
  399. return;
  400. m_brushAdded[brushnumber] = true;
  401. }
  402. bool CPlaneList::IsBrushReferenced( int brushnumber )
  403. {
  404. return m_brushAdded[brushnumber];
  405. }
  406. CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum )
  407. {
  408. CUtlVector<listplane_t> temp( 0, 32 );
  409. for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
  410. {
  411. dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
  412. if ( pside->bevel )
  413. continue;
  414. dplane_t *pplane = dplanes + pside->planenum;
  415. float shrinkThisPlane = shrink;
  416. if ( i < g_MainMap->mapbrushes[brushnumber].numsides )
  417. {
  418. if ( !g_MainMap->mapbrushes[brushnumber].original_sides[i].visible )
  419. {
  420. // don't shrink brush sides with no visible components.
  421. // this produces something closer to the ideal shrink than simply shrinking all planes
  422. shrinkThisPlane = 0;
  423. }
  424. }
  425. // Make sure shrinking won't swallow geometry along this axis.
  426. if ( pCollideTest && shrinkThisPlane != 0 )
  427. {
  428. Vector start = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, pplane->normal );
  429. Vector end = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, -pplane->normal );
  430. float thick = DotProduct( (end-start), pplane->normal );
  431. // NOTE: The object must be at least "shrinkMinimum" inches wide on each axis
  432. if ( fabs(thick) < shrinkMinimum )
  433. {
  434. #if _DEBUG
  435. Warning("Can't shrink brush %d, plane %d (%.2f, %.2f, %.2f)\n", brushnumber, pside->planenum, pplane->normal[0], pplane->normal[1], pplane->normal[2] );
  436. #endif
  437. shrinkThisPlane = 0;
  438. }
  439. }
  440. AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist - shrinkThisPlane );
  441. }
  442. return physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), m_merge );
  443. }
  444. int CPlaneList::AddBrushes( void )
  445. {
  446. int count = 0;
  447. for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
  448. {
  449. if ( IsBrushReferenced(brushnumber) )
  450. {
  451. CPhysConvex *pBrushConvex = NULL;
  452. if ( m_shrink != 0 )
  453. {
  454. // Make sure shrinking won't swallow this brush.
  455. CPhysConvex *pConvex = BuildConvexForBrush( brushnumber, 0, NULL, 0 );
  456. CPhysCollide *pUnshrunkCollide = physcollision->ConvertConvexToCollide( &pConvex, 1 );
  457. pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, pUnshrunkCollide, m_shrink * 3 );
  458. physcollision->DestroyCollide( pUnshrunkCollide );
  459. }
  460. else
  461. {
  462. pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, NULL, 1.0 );
  463. }
  464. if ( pBrushConvex )
  465. {
  466. count++;
  467. physcollision->SetConvexGameData( pBrushConvex, brushnumber );
  468. AddConvex( pBrushConvex );
  469. }
  470. }
  471. }
  472. return count;
  473. }
  474. int CPlaneList::GetFirstBrushSide()
  475. {
  476. for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
  477. {
  478. if ( IsBrushReferenced(brushnumber) )
  479. {
  480. for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
  481. {
  482. int sideIndex = i + dbrushes[brushnumber].firstside;
  483. dbrushside_t *pside = dbrushsides + sideIndex;
  484. if ( pside->bevel )
  485. continue;
  486. return sideIndex;
  487. }
  488. }
  489. }
  490. return 0;
  491. }
  492. // UNDONE: Try using this kind of algorithm if we run into precision problems.
  493. // NOTE: ConvexFromPlanes will be doing a bunch of matrix inversions that can suffer
  494. // if plane normals are too close to each other...
  495. #if 0
  496. void CPlaneList::AddBrushes( void )
  497. {
  498. CUtlVector<listplane_t> temp;
  499. for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
  500. {
  501. if ( IsBrushReferenced(brushnumber) )
  502. {
  503. CUtlVector<winding_t *> windings;
  504. for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
  505. {
  506. dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
  507. if (pside->bevel)
  508. continue;
  509. dplane_t *pplane = dplanes + pside->planenum;
  510. winding_t *w = BaseWindingForPlane( pplane->normal, pplane->dist - m_shrink );
  511. for ( int j = 0; j < dbrushes[brushnumber].numsides && w; j++ )
  512. {
  513. if (i == j)
  514. continue;
  515. dbrushside_t *pClipSide = dbrushsides + j + dbrushes[brushnumber].firstside;
  516. if (pClipSide->bevel)
  517. continue;
  518. dplane_t *pClipPlane = dplanes + pClipSide->planenum;
  519. ChopWindingInPlace (&w, -pClipPlane->normal, -pClipPlane->dist+m_shrink, 0); //CLIP_EPSILON);
  520. }
  521. if ( w )
  522. {
  523. windings.AddToTail( w );
  524. }
  525. }
  526. CUtlVector<Vector *> vertList;
  527. for ( int p = 0; p < windings.Count(); p++ )
  528. {
  529. for ( int v = 0; v < windings[p]->numpoints; v++ )
  530. {
  531. vertList.AddToTail( windings[p]->p + v );
  532. }
  533. }
  534. CPhysConvex *pConvex = physcollision->ConvexFromVerts( vertList.Base(), vertList.Count() );
  535. if ( pConvex )
  536. {
  537. physcollision->SetConvexGameData( pConvex, brushnumber );
  538. AddConvex( pConvex );
  539. }
  540. temp.RemoveAll();
  541. }
  542. }
  543. }
  544. #endif
  545. // If I have a list of leaves, make sure this leaf is in it.
  546. // Otherwise, process all leaves
  547. bool CPlaneList::IsLeafReferenced( int leafIndex )
  548. {
  549. if ( !m_leafList.Count() )
  550. return true;
  551. for ( int i = 0; i < m_leafList.Count(); i++ )
  552. {
  553. if ( m_leafList[i] == leafIndex )
  554. return true;
  555. }
  556. return false;
  557. }
  558. // Add a leaf to my list of interesting leaves
  559. void CPlaneList::ReferenceLeaf( int leafIndex )
  560. {
  561. m_leafList.AddToTail( leafIndex );
  562. }
  563. static void VisitLeaves_r( CPlaneList &planes, int node )
  564. {
  565. if ( node < 0 )
  566. {
  567. int leafIndex = -1 - node;
  568. if ( planes.IsLeafReferenced(leafIndex) )
  569. {
  570. int i;
  571. // Add the solids in the "empty" leaf
  572. for ( i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
  573. {
  574. int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
  575. planes.ReferenceBrush( brushIndex );
  576. }
  577. }
  578. }
  579. else
  580. {
  581. dnode_t *pnode = dnodes + node;
  582. VisitLeaves_r( planes, pnode->children[0] );
  583. VisitLeaves_r( planes, pnode->children[1] );
  584. }
  585. }
  586. //-----------------------------------------------------------------------------
  587. //-----------------------------------------------------------------------------
  588. struct waterleaf_t
  589. {
  590. Vector surfaceNormal;
  591. float surfaceDist;
  592. float minZ;
  593. bool hasSurface;
  594. int waterLeafIndex;// this is the submerged leaf
  595. int planenum; //UNDONE: REMOVE
  596. int surfaceTexInfo; // if hasSurface == true, this is the texinfo index for the water material
  597. int outsideLeafIndex;// this is the leaf on the other side of the water surface
  598. node_t *pNode;
  599. };
  600. // returns true if newleaf should appear before currentleaf in the list
  601. static bool IsLowerLeaf( const waterleaf_t &newleaf, const waterleaf_t &currentleaf )
  602. {
  603. if ( newleaf.hasSurface && currentleaf.hasSurface )
  604. {
  605. // the one with the upmost pointing z goes first
  606. if ( currentleaf.surfaceNormal.z > newleaf.surfaceNormal.z )
  607. return false;
  608. if ( fabs(currentleaf.surfaceNormal.z - newleaf.surfaceNormal.z) < 0.01 )
  609. {
  610. if ( newleaf.surfaceDist < currentleaf.surfaceDist )
  611. return true;
  612. }
  613. return true;
  614. }
  615. else if ( newleaf.hasSurface ) // the leaf with a surface always goes first
  616. return true;
  617. return false;
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Purpose: Water surfaces are stored in an RB tree and the tree is used to
  621. // create one-off .vmt files embedded in the .bsp for each surface so that the
  622. // water depth effect occurs on a per-water surface level.
  623. //-----------------------------------------------------------------------------
  624. struct WaterTexInfo
  625. {
  626. // The mangled new .vmt name ( materials/levelename/oldmaterial_depth_xxx ) where xxx is
  627. // the water depth (as an integer )
  628. CUtlSymbol m_FullName;
  629. // The original .vmt name
  630. CUtlSymbol m_MaterialName;
  631. // The depth of the water this texinfo refers to
  632. int m_nWaterDepth;
  633. // The texinfo id
  634. int m_nTexInfo;
  635. // The subdivision size for the water surface
  636. // float m_SubdivSize;
  637. };
  638. //-----------------------------------------------------------------------------
  639. // Purpose: Helper for RB tree operations ( we compare full mangled names )
  640. // Input : src1 -
  641. // src2 -
  642. // Output : Returns true on success, false on failure.
  643. //-----------------------------------------------------------------------------
  644. bool WaterLessFunc( WaterTexInfo const& src1, WaterTexInfo const& src2 )
  645. {
  646. return src1.m_FullName < src2.m_FullName;
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Purpose: A growable RB tree of water surfaces
  650. //-----------------------------------------------------------------------------
  651. static CUtlRBTree< WaterTexInfo, int > g_WaterTexInfos( 0, 32, WaterLessFunc );
  652. #if 0
  653. float GetSubdivSizeForFogVolume( int fogVolumeID )
  654. {
  655. Assert( fogVolumeID >= 0 && fogVolumeID < g_WaterTexInfos.Count() );
  656. return g_WaterTexInfos[fogVolumeID].m_SubdivSize;
  657. }
  658. #endif
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. // Input : *mapname -
  662. // *materialname -
  663. // waterdepth -
  664. // *fullname -
  665. //-----------------------------------------------------------------------------
  666. void GetWaterTextureName( char const *mapname, char const *materialname, int waterdepth, char *fullname )
  667. {
  668. char temp[ 512 ];
  669. // Construct the full name (prepend mapname to reduce name collisions)
  670. sprintf( temp, "maps/%s/%s_depth_%i", mapname, materialname, (int)waterdepth );
  671. // Make sure it's lower case
  672. strlwr( temp );
  673. strcpy( fullname, temp );
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: Called to write procedural materials in the rb tree to the embedded
  677. // pak file for this .bsp
  678. //-----------------------------------------------------------------------------
  679. void EmitWaterMaterialFile( WaterTexInfo *wti )
  680. {
  681. char waterTextureName[512];
  682. if ( !wti )
  683. {
  684. return;
  685. }
  686. GetWaterTextureName( mapbase, wti->m_MaterialName.String(), ( int )wti->m_nWaterDepth, waterTextureName );
  687. // Convert to string
  688. char szDepth[ 32 ];
  689. sprintf( szDepth, "%i", wti->m_nWaterDepth );
  690. CreateMaterialPatch( wti->m_MaterialName.String(), waterTextureName, "$waterdepth", szDepth, PATCH_INSERT );
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose: Takes the texinfo_t referenced by the .vmt and the computed depth for the
  694. // surface and looks up or creates a texdata/texinfo for the mangled one-off water .vmt file
  695. // Input : *pBaseInfo -
  696. // depth -
  697. // Output : int
  698. //-----------------------------------------------------------------------------
  699. int FindOrCreateWaterTexInfo( texinfo_t *pBaseInfo, float depth )
  700. {
  701. char fullname[ 512 ];
  702. char materialname[ 512 ];
  703. // Get the base texture/material name
  704. char const *name = TexDataStringTable_GetString( GetTexData( pBaseInfo->texdata )->nameStringTableID );
  705. GetWaterTextureName( mapbase, name, (int)depth, fullname );
  706. // See if we already have an entry for this depth
  707. WaterTexInfo lookup;
  708. lookup.m_FullName = fullname;
  709. int idx = g_WaterTexInfos.Find( lookup );
  710. // If so, return the existing entry texinfo index
  711. if ( idx != g_WaterTexInfos.InvalidIndex() )
  712. {
  713. return g_WaterTexInfos[ idx ].m_nTexInfo;
  714. }
  715. // Otherwise, fill in the rest of the data
  716. lookup.m_nWaterDepth = (int)depth;
  717. // Remember the current material name
  718. sprintf( materialname, "%s", name );
  719. strlwr( materialname );
  720. lookup.m_MaterialName = materialname;
  721. texinfo_t ti;
  722. // Make a copy
  723. ti = *pBaseInfo;
  724. // Create a texdata that is based on the underlying existing entry
  725. ti.texdata = FindAliasedTexData( fullname, GetTexData( pBaseInfo->texdata ) );
  726. // Find or create a new index
  727. lookup.m_nTexInfo = FindOrCreateTexInfo( ti );
  728. // Add the new texinfo to the RB tree
  729. idx = g_WaterTexInfos.Insert( lookup );
  730. // Msg( "created texinfo for %s\n", lookup.m_FullName.String() );
  731. // Go ahead and create the new vmt file.
  732. EmitWaterMaterialFile( &g_WaterTexInfos[idx] );
  733. // Return the new texinfo
  734. return g_WaterTexInfos[ idx ].m_nTexInfo;
  735. }
  736. extern node_t *dfacenodes[MAX_MAP_FACES];
  737. static void WriteFogVolumeIDs( dmodel_t *pModel )
  738. {
  739. int i;
  740. // write fog volume ID to each face in this model
  741. for( i = pModel->firstface; i < pModel->firstface + pModel->numfaces; i++ )
  742. {
  743. dface_t *pFace = &dfaces[i];
  744. node_t *pFaceNode = dfacenodes[i];
  745. texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
  746. pFace->surfaceFogVolumeID = -1;
  747. if ( pFaceNode )
  748. {
  749. if ( (pTexInfo->flags & SURF_WARP ) && pFaceNode->planenum == PLANENUM_LEAF && pFaceNode->diskId >= 0 )
  750. {
  751. pFace->surfaceFogVolumeID = dleafs[pFaceNode->diskId].leafWaterDataID;
  752. dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[pFace->surfaceFogVolumeID];
  753. // HACKHACK: Should probably mark these faces as water bottom or "bottommaterial" faces.
  754. // HACKHACK: Use a heuristic, if it points up, it's the water top.
  755. if ( dplanes[pFace->planenum].normal.z > 0 )
  756. {
  757. pFace->texinfo = pLeafWaterData->surfaceTexInfoID;
  758. }
  759. }
  760. else
  761. {
  762. // missed this face somehow?
  763. Assert( !(pTexInfo->flags & SURF_WARP ) );
  764. }
  765. }
  766. }
  767. }
  768. static bool PortalCrossesWater( waterleaf_t &baseleaf, portal_t *portal )
  769. {
  770. if ( baseleaf.hasSurface )
  771. {
  772. int side = WindingOnPlaneSide( portal->winding, baseleaf.surfaceNormal, baseleaf.surfaceDist );
  773. if ( side == SIDE_CROSS || side == SIDE_FRONT )
  774. return true;
  775. }
  776. return false;
  777. }
  778. static int FindOrCreateLeafWaterData( float surfaceZ, float minZ, int surfaceTexInfoID )
  779. {
  780. int i;
  781. for( i = 0; i < numleafwaterdata; i++ )
  782. {
  783. dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[i];
  784. if( pLeafWaterData->surfaceZ == surfaceZ &&
  785. pLeafWaterData->minZ == minZ &&
  786. pLeafWaterData->surfaceTexInfoID == surfaceTexInfoID )
  787. {
  788. return i;
  789. }
  790. }
  791. dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[numleafwaterdata];
  792. pLeafWaterData->surfaceZ = surfaceZ;
  793. pLeafWaterData->minZ = minZ;
  794. pLeafWaterData->surfaceTexInfoID = surfaceTexInfoID;
  795. numleafwaterdata++;
  796. return numleafwaterdata - 1;
  797. }
  798. // Enumerate all leaves under node with contents in contentsMask and add them to list
  799. void EnumLeaves_r( CUtlVector<node_t *> &list, node_t *node, int contentsMask )
  800. {
  801. if ( node->planenum != PLANENUM_LEAF )
  802. {
  803. EnumLeaves_r( list, node->children[0], contentsMask );
  804. EnumLeaves_r( list, node->children[1], contentsMask );
  805. return;
  806. }
  807. if ( !(node->contents & contentsMask) )
  808. return;
  809. // has the contents, put it in the list
  810. list.AddToTail( node );
  811. }
  812. // Builds a waterleaf_t for the given leaf
  813. static void BuildWaterLeaf( node_t *pLeafIn, waterleaf_t &waterLeafOut )
  814. {
  815. waterLeafOut.pNode = pLeafIn;
  816. waterLeafOut.waterLeafIndex = pLeafIn->diskId;
  817. waterLeafOut.outsideLeafIndex = -1;
  818. waterLeafOut.hasSurface = false;
  819. waterLeafOut.surfaceDist = MAX_COORD_INTEGER;
  820. waterLeafOut.surfaceNormal.Init( 0.f, 0.f, 1.f );
  821. waterLeafOut.planenum = -1;
  822. waterLeafOut.surfaceTexInfo = -1;
  823. waterLeafOut.minZ = MAX_COORD_INTEGER;
  824. // search the list of portals out of this leaf for one that leaves water
  825. // If you find one, this leaf has a surface, so fill out the surface data
  826. int oppositeNodeIndex = 0;
  827. for (portal_t *p = pLeafIn->portals ; p ; p = p->next[!oppositeNodeIndex])
  828. {
  829. oppositeNodeIndex = (p->nodes[0] == pLeafIn) ? 1 : 0;
  830. // not visible, can't be the portals we're looking for...
  831. if ( !p->side )
  832. continue;
  833. // See if this portal crosses into air
  834. node_t *pOpposite = p->nodes[oppositeNodeIndex];
  835. if ( !(pOpposite->contents & MASK_WATER) && !(pOpposite->contents & MASK_SOLID) )
  836. {
  837. // it does, there must be a surface here
  838. plane_t *plane = &g_MainMap->mapplanes[p->side->planenum];
  839. if ( waterLeafOut.hasSurface )
  840. {
  841. // Sort to find the most upward facing normal (skips sides)
  842. if ( waterLeafOut.surfaceNormal.z > plane->normal.z )
  843. continue;
  844. if ( (waterLeafOut.surfaceNormal.z == plane->normal.z) && waterLeafOut.surfaceDist >= plane->dist )
  845. continue;
  846. }
  847. // water surface needs to point at least somewhat up, this is
  848. // probably a map error
  849. if ( plane->normal.z <= 0 )
  850. continue;
  851. waterLeafOut.surfaceDist = plane->dist;
  852. waterLeafOut.surfaceNormal = plane->normal;
  853. waterLeafOut.hasSurface = true;
  854. waterLeafOut.outsideLeafIndex = p->nodes[oppositeNodeIndex]->diskId;
  855. waterLeafOut.surfaceTexInfo = p->side->texinfo;
  856. }
  857. }
  858. }
  859. static void InsertSortWaterLeaf( CUtlVector<waterleaf_t> &list, const waterleaf_t &leafInsert )
  860. {
  861. // insertion sort the leaf (lowest leaves go first)
  862. // leaves that aren't actually on the surface of the water will have leaf.hasSurface == false.
  863. for ( int i = 0; i < list.Count(); i++ )
  864. {
  865. if ( IsLowerLeaf( leafInsert, list[i] ) )
  866. {
  867. list.InsertBefore( i, leafInsert );
  868. return;
  869. }
  870. }
  871. // must the highest one, so stick it at the end.
  872. list.AddToTail( leafInsert );
  873. }
  874. // Flood fill the tree, finding neighboring water volumes and connecting them to this list
  875. // Cut groups that try to cross the surface.
  876. // Mark leaves that are in a group as "visited" so they won't be chosen by subsequent fills
  877. static void Flood_FindConnectedWaterVolumes_r( CUtlVector<node_t *> &list, node_t *pLeaf, waterleaf_t &baseleaf, leafbitarray_t &visited )
  878. {
  879. // already visited, or not the same water contents
  880. if ( pLeaf->diskId < 0 || visited.Get(pLeaf->diskId) || !(pLeaf->contents & (baseleaf.pNode->contents & MASK_WATER) ) )
  881. return;
  882. int oppositeNodeIndex = 0;
  883. for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
  884. {
  885. oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
  886. // If any portal crosses the water surface, don't flow through this leaf
  887. if ( PortalCrossesWater( baseleaf, p ) )
  888. return;
  889. }
  890. visited.Set( pLeaf->diskId );
  891. list.AddToTail( pLeaf );
  892. baseleaf.minZ = MIN( pLeaf->mins.z, baseleaf.minZ );
  893. for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
  894. {
  895. oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
  896. Flood_FindConnectedWaterVolumes_r( list, p->nodes[oppositeNodeIndex], baseleaf, visited );
  897. }
  898. }
  899. // UNDONE: This is a bit of a hack to avoid crashing when we can't find an
  900. // appropriate texinfo for a water model (to get physics properties)
  901. int FirstWaterTexinfo( bspbrush_t *brushlist, int contents )
  902. {
  903. while (brushlist)
  904. {
  905. if ( brushlist->original->contents & contents )
  906. {
  907. for ( int i = 0; i < brushlist->original->numsides; i++ )
  908. {
  909. if ( brushlist->original->original_sides[i].contents & contents )
  910. {
  911. return brushlist->original->original_sides[i].texinfo;
  912. }
  913. }
  914. }
  915. brushlist = brushlist->next;
  916. }
  917. Assert(0);
  918. return 0;
  919. }
  920. // This is a list of water data that will be turned into physics models
  921. struct watermodel_t
  922. {
  923. int modelIndex;
  924. int contents;
  925. waterleaf_t waterLeafData;
  926. int depthTexinfo;
  927. int firstWaterLeafIndex;
  928. int waterLeafCount;
  929. int fogVolumeIndex;
  930. };
  931. static CUtlVector<watermodel_t> g_WaterModels;
  932. static CUtlVector<int> g_WaterLeafList;
  933. // Creates a list of watermodel_t for later processing by EmitPhysCollision
  934. void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *node )
  935. {
  936. CUtlVector<node_t *> leafListAnyWater;
  937. // build the list of all leaves containing water
  938. EnumLeaves_r( leafListAnyWater, node, MASK_WATER );
  939. // make a sorted list to flood fill
  940. CUtlVector<waterleaf_t> list;
  941. int i;
  942. for ( i = 0; i < leafListAnyWater.Count(); i++ )
  943. {
  944. waterleaf_t waterLeaf;
  945. BuildWaterLeaf( leafListAnyWater[i], waterLeaf );
  946. InsertSortWaterLeaf( list, waterLeaf );
  947. }
  948. leafbitarray_t visited;
  949. CUtlVector<node_t *> waterAreaList;
  950. for ( i = 0; i < list.Count(); i++ )
  951. {
  952. Flood_FindConnectedWaterVolumes_r( waterAreaList, list[i].pNode, list[i], visited );
  953. // did we find a list of leaves connected to this one?
  954. // remember the list is sorted, so this one may have been attached to a previous
  955. // leaf. So it could have nothing hanging off of it.
  956. if ( waterAreaList.Count() )
  957. {
  958. // yes, emit a watermodel
  959. watermodel_t tmp;
  960. tmp.modelIndex = nummodels;
  961. tmp.contents = list[i].pNode->contents;
  962. tmp.waterLeafData = list[i];
  963. tmp.firstWaterLeafIndex = g_WaterLeafList.Count();
  964. tmp.waterLeafCount = waterAreaList.Count();
  965. float waterDepth = tmp.waterLeafData.surfaceDist - tmp.waterLeafData.minZ;
  966. if ( tmp.waterLeafData.surfaceTexInfo < 0 )
  967. {
  968. // the map has probably leaked in this case, but output something anyway.
  969. Assert(list[i].pNode->planenum == PLANENUM_LEAF);
  970. tmp.waterLeafData.surfaceTexInfo = FirstWaterTexinfo( list[i].pNode->brushlist, tmp.contents );
  971. }
  972. tmp.depthTexinfo = FindOrCreateWaterTexInfo( &texinfo[ tmp.waterLeafData.surfaceTexInfo ], waterDepth );
  973. tmp.fogVolumeIndex = FindOrCreateLeafWaterData( tmp.waterLeafData.surfaceDist, tmp.waterLeafData.minZ, tmp.waterLeafData.surfaceTexInfo );
  974. for ( int j = 0; j < waterAreaList.Count(); j++ )
  975. {
  976. g_WaterLeafList.AddToTail( waterAreaList[j]->diskId );
  977. }
  978. waterAreaList.RemoveAll();
  979. g_WaterModels.AddToTail( tmp );
  980. }
  981. }
  982. WriteFogVolumeIDs( pModel );
  983. }
  984. static void ConvertWaterModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex,
  985. float shrinkSize, float mergeTolerance )
  986. {
  987. dmodel_t *pModel = dmodels + modelIndex;
  988. for ( int i = 0; i < g_WaterModels.Count(); i++ )
  989. {
  990. watermodel_t &waterModel = g_WaterModels[i];
  991. if ( waterModel.modelIndex != modelIndex )
  992. continue;
  993. CPlaneList planes( shrinkSize, mergeTolerance );
  994. int firstLeaf = waterModel.firstWaterLeafIndex;
  995. planes.m_contentsMask = waterModel.contents;
  996. // push all of the leaves into the collision list
  997. for ( int j = 0; j < waterModel.waterLeafCount; j++ )
  998. {
  999. int leafIndex = g_WaterLeafList[firstLeaf + j];
  1000. dleaf_t *pLeaf = dleafs + leafIndex;
  1001. // fixup waterdata
  1002. pLeaf->leafWaterDataID = waterModel.fogVolumeIndex;
  1003. planes.ReferenceLeaf( leafIndex );
  1004. }
  1005. // visit the referenced leaves that belong to this model
  1006. VisitLeaves_r( planes, pModel->headnode );
  1007. // Now add the brushes from those leaves as convex
  1008. // BUGBUG: NOTE: If your map has a brush that crosses the surface, it will be added to two water
  1009. // volumes. This only happens with connected water volumes with multiple surface heights
  1010. // UNDONE: Right now map makers must cut such brushes. It could be automatically cut by adding the
  1011. // surface plane to the list for each brush before calling ConvexFromPlanes()
  1012. planes.AddBrushes();
  1013. int count = planes.m_convex.Count();
  1014. if ( !count )
  1015. continue;
  1016. // Save off the plane of the surface for this group as well as the collision model
  1017. // for all convex objects in the group.
  1018. CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
  1019. if ( pCollide )
  1020. {
  1021. int waterSurfaceTexInfoID = -1;
  1022. // use defaults
  1023. const char *pSurfaceProp = "water";
  1024. float damping = 0.01;
  1025. if ( waterSurfaceTexInfoID >= 0 )
  1026. {
  1027. // material override
  1028. int texdata = texinfo[waterSurfaceTexInfoID].texdata;
  1029. int prop = g_SurfaceProperties[texdata];
  1030. pSurfaceProp = physprops->GetPropName( prop );
  1031. }
  1032. if ( !waterModel.waterLeafData.hasSurface )
  1033. {
  1034. waterModel.waterLeafData.surfaceNormal.Init( 0,0,1 );
  1035. Vector top = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, waterModel.waterLeafData.surfaceNormal );
  1036. waterModel.waterLeafData.surfaceDist = top.z;
  1037. }
  1038. CPhysCollisionEntryFluid *pCollisionEntryFuild = new CPhysCollisionEntryFluid( pCollide,
  1039. pSurfaceProp, damping, waterModel.waterLeafData.surfaceNormal, waterModel.waterLeafData.surfaceDist, waterModel.contents );
  1040. collisionList.AddToTail( pCollisionEntryFuild );
  1041. }
  1042. }
  1043. }
  1044. // compute a normal for a triangle of the given three points (points are clockwise, normal points out)
  1045. static Vector TriangleNormal( const Vector &p0, const Vector &p1, const Vector &p2 )
  1046. {
  1047. Vector e0 = p1 - p0;
  1048. Vector e1 = p2 - p0;
  1049. Vector normal = CrossProduct( e1, e0 );
  1050. VectorNormalize( normal );
  1051. return normal;
  1052. }
  1053. // find the side of the brush with the normal closest to the given normal
  1054. static dbrushside_t *FindBrushSide( int brushIndex, const Vector &normal )
  1055. {
  1056. dbrush_t *pbrush = &dbrushes[brushIndex];
  1057. dbrushside_t *out = NULL;
  1058. float best = -1.f;
  1059. for ( int i = 0; i < pbrush->numsides; i++ )
  1060. {
  1061. dbrushside_t *pside = dbrushsides + i + pbrush->firstside;
  1062. dplane_t *pplane = dplanes + pside->planenum;
  1063. float dot = DotProduct( normal, pplane->normal );
  1064. if ( dot > best )
  1065. {
  1066. best = dot;
  1067. out = pside;
  1068. }
  1069. }
  1070. return out;
  1071. }
  1072. static void ConvertWorldBrushesToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance, int contentsMask )
  1073. {
  1074. CPlaneList planes( shrinkSize, mergeTolerance );
  1075. planes.m_contentsMask = contentsMask;
  1076. VisitLeaves_r( planes, dmodels[0].headnode );
  1077. planes.AddBrushes();
  1078. int count = planes.m_convex.Count();
  1079. if ( count )
  1080. {
  1081. CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
  1082. ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
  1083. int convex = pQuery->ConvexCount();
  1084. for ( int i = 0; i < convex; i++ )
  1085. {
  1086. int triCount = pQuery->TriangleCount( i );
  1087. int brushIndex = pQuery->GetGameData( i );
  1088. Vector points[3];
  1089. for ( int j = 0; j < triCount; j++ )
  1090. {
  1091. pQuery->GetTriangleVerts( i, j, points );
  1092. Vector normal = TriangleNormal( points[0], points[1], points[2] );
  1093. dbrushside_t *pside = FindBrushSide( brushIndex, normal );
  1094. if ( pside->texinfo != TEXINFO_NODE )
  1095. {
  1096. int prop = g_SurfaceProperties[texinfo[pside->texinfo].texdata];
  1097. pQuery->SetTriangleMaterialIndex( i, j, RemapWorldMaterial( prop ) );
  1098. }
  1099. }
  1100. }
  1101. physcollision->DestroyQueryModel( pQuery );
  1102. pQuery = NULL;
  1103. collisionList.AddToTail( new CPhysCollisionEntryStaticSolid( pCollide, contentsMask ) );
  1104. }
  1105. }
  1106. // adds any world, terrain, and water collision models to the collision list
  1107. static void BuildWorldPhysModel( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance )
  1108. {
  1109. ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, MASK_SOLID & (~CONTENTS_GRATE) );
  1110. ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_GRATE );
  1111. ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_PLAYERCLIP );
  1112. ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_MONSTERCLIP );
  1113. // if there's terrain, save it off as a static mesh/polysoup
  1114. if ( g_bNoVirtualMesh || !physcollision->SupportsVirtualMesh() )
  1115. {
  1116. Disp_AddCollisionModels( collisionList, &dmodels[0], MASK_SOLID );
  1117. }
  1118. else
  1119. {
  1120. Disp_BuildVirtualMesh( MASK_SOLID );
  1121. }
  1122. ConvertWaterModelToPhysCollide( collisionList, 0, shrinkSize, mergeTolerance );
  1123. }
  1124. // adds a collision entry for this brush model
  1125. static void ConvertModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex, int contents, float shrinkSize, float mergeTolerance )
  1126. {
  1127. int i;
  1128. CPlaneList planes( shrinkSize, mergeTolerance );
  1129. planes.m_contentsMask = contents;
  1130. dmodel_t *pModel = dmodels + modelIndex;
  1131. VisitLeaves_r( planes, pModel->headnode );
  1132. planes.AddBrushes();
  1133. int count = planes.m_convex.Count();
  1134. convertconvexparams_t params;
  1135. params.Defaults();
  1136. params.buildOuterConvexHull = count > 1 ? true : false;
  1137. params.buildDragAxisAreas = true;
  1138. Vector size = pModel->maxs - pModel->mins;
  1139. float minSurfaceArea = -1.0f;
  1140. for ( i = 0; i < 3; i++ )
  1141. {
  1142. int other = (i+1)%3;
  1143. int cross = (i+2)%3;
  1144. float surfaceArea = size[other] * size[cross];
  1145. if ( minSurfaceArea < 0 || surfaceArea < minSurfaceArea )
  1146. {
  1147. minSurfaceArea = surfaceArea;
  1148. }
  1149. }
  1150. // this can be really slow with super-large models and a low error tolerance
  1151. // Basically you get a ray cast through each square of epsilon surface area on each OBB side
  1152. // So compute it for 1% error (on the smallest side, less on larger sides)
  1153. params.dragAreaEpsilon = clamp( minSurfaceArea * 1e-2f, 1.0f, 1024.0f );
  1154. CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( planes.m_convex.Base(), count, params );
  1155. if ( !pCollide )
  1156. return;
  1157. struct
  1158. {
  1159. int prop;
  1160. float area;
  1161. } proplist[256];
  1162. int numprops = 1;
  1163. proplist[0].prop = -1;
  1164. proplist[0].area = 1;
  1165. // compute the array of props on the surface of this model
  1166. // NODRAW brushes no longer have any faces
  1167. if ( !dmodels[modelIndex].numfaces )
  1168. {
  1169. int sideIndex = planes.GetFirstBrushSide();
  1170. int texdata = texinfo[dbrushsides[sideIndex].texinfo].texdata;
  1171. int prop = g_SurfaceProperties[texdata];
  1172. proplist[numprops].prop = prop;
  1173. proplist[numprops].area = 2;
  1174. numprops++;
  1175. }
  1176. for ( i = 0; i < dmodels[modelIndex].numfaces; i++ )
  1177. {
  1178. dface_t *face = dfaces + i + dmodels[modelIndex].firstface;
  1179. int texdata = texinfo[face->texinfo].texdata;
  1180. int prop = g_SurfaceProperties[texdata];
  1181. int j;
  1182. for ( j = 0; j < numprops; j++ )
  1183. {
  1184. if ( proplist[j].prop == prop )
  1185. {
  1186. proplist[j].area += face->area;
  1187. break;
  1188. }
  1189. }
  1190. if ( (!numprops || j >= numprops) && numprops < ARRAYSIZE(proplist) )
  1191. {
  1192. proplist[numprops].prop = prop;
  1193. proplist[numprops].area = face->area;
  1194. numprops++;
  1195. }
  1196. }
  1197. // choose the prop with the most surface area
  1198. int maxIndex = -1;
  1199. float maxArea = 0;
  1200. float totalArea = 0;
  1201. for ( i = 0; i < numprops; i++ )
  1202. {
  1203. if ( proplist[i].area > maxArea )
  1204. {
  1205. maxIndex = i;
  1206. maxArea = proplist[i].area;
  1207. }
  1208. // add up the total surface area
  1209. totalArea += proplist[i].area;
  1210. }
  1211. float mass = 1.0f;
  1212. const char *pMaterial = "default";
  1213. if ( maxIndex >= 0 )
  1214. {
  1215. int prop = proplist[maxIndex].prop;
  1216. // use default if this material has no prop
  1217. if ( prop < 0 )
  1218. prop = 0;
  1219. pMaterial = physprops->GetPropName( prop );
  1220. float density, thickness;
  1221. physprops->GetPhysicsProperties( prop, &density, &thickness, NULL, NULL );
  1222. // if this is a "shell" material (it is hollow and encloses some empty space)
  1223. // compute the mass with a constant surface thickness
  1224. if ( thickness != 0 )
  1225. {
  1226. mass = totalArea * thickness * density * CUBIC_METERS_PER_CUBIC_INCH;
  1227. }
  1228. else
  1229. {
  1230. // material is completely solid, compute total mass as if constant density throughout.
  1231. mass = planes.m_totalVolume * density * CUBIC_METERS_PER_CUBIC_INCH;
  1232. }
  1233. }
  1234. // Clamp mass to 100,000 kg
  1235. if ( mass > VPHYSICS_MAX_MASS )
  1236. {
  1237. mass = VPHYSICS_MAX_MASS;
  1238. }
  1239. collisionList.AddToTail( new CPhysCollisionEntrySolid( pCollide, pMaterial, mass ) );
  1240. }
  1241. static void ClearLeafWaterData( void )
  1242. {
  1243. int i;
  1244. for( i = 0; i < numleafs; i++ )
  1245. {
  1246. dleafs[i].leafWaterDataID = -1;
  1247. dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
  1248. }
  1249. }
  1250. // This is the only public entry to this file.
  1251. // The global data touched in the file is:
  1252. // from bsplib.h:
  1253. // g_pPhysCollide : This is an output from this file.
  1254. // g_PhysCollideSize : This is set in this file.
  1255. // g_numdispinfo : This is an input to this file.
  1256. // g_dispinfo : This is an input to this file.
  1257. // numnodewaterdata : This is an output from this file.
  1258. // dleafwaterdata : This is an output from this file.
  1259. // from vbsp.h:
  1260. // g_SurfaceProperties : This is an input to this file.
  1261. void EmitPhysCollision()
  1262. {
  1263. ClearLeafWaterData();
  1264. CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  1265. if ( physicsFactory )
  1266. {
  1267. physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  1268. }
  1269. if ( !physcollision )
  1270. {
  1271. Warning("!!! WARNING: Can't build collision data!\n" );
  1272. return;
  1273. }
  1274. CUtlVector<CPhysCollisionEntry *> collisionList[MAX_MAP_MODELS];
  1275. CTextBuffer *pTextBuffer[MAX_MAP_MODELS];
  1276. int physModelCount = 0, totalSize = 0;
  1277. int start = Plat_FloatTime();
  1278. Msg("Building Physics collision data...\n" );
  1279. int i, j;
  1280. for ( i = 0; i < nummodels; i++ )
  1281. {
  1282. // Build a list of collision models for this brush model section
  1283. if ( i == 0 )
  1284. {
  1285. // world is the only model that processes water separately.
  1286. // other brushes are assumed to be completely solid or completely liquid
  1287. BuildWorldPhysModel( collisionList[i], NO_SHRINK, VPHYSICS_MERGE);
  1288. }
  1289. else
  1290. {
  1291. ConvertModelToPhysCollide( collisionList[i], i, MASK_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|MASK_WATER, VPHYSICS_SHRINK, VPHYSICS_MERGE );
  1292. }
  1293. pTextBuffer[i] = NULL;
  1294. if ( !collisionList[i].Count() )
  1295. continue;
  1296. // if we've got collision models, write their script for processing in the game
  1297. pTextBuffer[i] = new CTextBuffer;
  1298. for ( j = 0; j < collisionList[i].Count(); j++ )
  1299. {
  1300. // dump a text file for visualization
  1301. if ( dumpcollide )
  1302. {
  1303. collisionList[i][j]->DumpCollide( pTextBuffer[i], i, j );
  1304. }
  1305. // each model knows how to write its script
  1306. collisionList[i][j]->WriteToTextBuffer( pTextBuffer[i], i, j );
  1307. // total up the binary section's size
  1308. totalSize += collisionList[i][j]->GetCollisionBinarySize() + sizeof(int);
  1309. }
  1310. // These sections only appear in the world's collision text
  1311. if ( i == 0 )
  1312. {
  1313. if ( !g_bNoVirtualMesh && physcollision->SupportsVirtualMesh() )
  1314. {
  1315. pTextBuffer[i]->WriteText("virtualterrain {}\n");
  1316. }
  1317. if ( s_WorldPropList.Count() )
  1318. {
  1319. pTextBuffer[i]->WriteText( "materialtable {\n" );
  1320. for ( j = 0; j < s_WorldPropList.Count(); j++ )
  1321. {
  1322. int propIndex = s_WorldPropList[j];
  1323. if ( propIndex < 0 )
  1324. {
  1325. pTextBuffer[i]->WriteIntKey( "default", j+1 );
  1326. }
  1327. else
  1328. {
  1329. pTextBuffer[i]->WriteIntKey( physprops->GetPropName( propIndex ), j+1 );
  1330. }
  1331. }
  1332. pTextBuffer[i]->WriteText( "}\n" );
  1333. }
  1334. }
  1335. pTextBuffer[i]->Terminate();
  1336. // total lump size includes the text buffers (scripts)
  1337. totalSize += pTextBuffer[i]->GetSize();
  1338. physModelCount++;
  1339. }
  1340. // add one for tail of list marker
  1341. physModelCount++;
  1342. // DWORD align the lump because AddLump assumes that it is DWORD aligned.
  1343. byte *ptr ;
  1344. g_PhysCollideSize = totalSize + (physModelCount * sizeof(dphysmodel_t));
  1345. g_pPhysCollide = (byte *)malloc(( g_PhysCollideSize + 3 ) & ~3 );
  1346. memset( g_pPhysCollide, 0, g_PhysCollideSize );
  1347. ptr = g_pPhysCollide;
  1348. for ( i = 0; i < nummodels; i++ )
  1349. {
  1350. if ( pTextBuffer[i] )
  1351. {
  1352. int j;
  1353. dphysmodel_t model;
  1354. model.modelIndex = i;
  1355. model.solidCount = collisionList[i].Count();
  1356. model.dataSize = sizeof(int) * model.solidCount;
  1357. for ( j = 0; j < model.solidCount; j++ )
  1358. {
  1359. model.dataSize += collisionList[i][j]->GetCollisionBinarySize();
  1360. }
  1361. model.keydataSize = pTextBuffer[i]->GetSize();
  1362. // store the header
  1363. memcpy( ptr, &model, sizeof(model) );
  1364. ptr += sizeof(model);
  1365. for ( j = 0; j < model.solidCount; j++ )
  1366. {
  1367. int collideSize = collisionList[i][j]->GetCollisionBinarySize();
  1368. // write size
  1369. memcpy( ptr, &collideSize, sizeof(int) );
  1370. ptr += sizeof(int);
  1371. // now write the collision model
  1372. collisionList[i][j]->WriteCollisionBinary( reinterpret_cast<char *>(ptr) );
  1373. ptr += collideSize;
  1374. }
  1375. memcpy( ptr, pTextBuffer[i]->GetData(), pTextBuffer[i]->GetSize() );
  1376. ptr += pTextBuffer[i]->GetSize();
  1377. }
  1378. delete pTextBuffer[i];
  1379. }
  1380. dphysmodel_t model;
  1381. // Mark end of list
  1382. model.modelIndex = -1;
  1383. model.dataSize = -1;
  1384. model.keydataSize = 0;
  1385. model.solidCount = 0;
  1386. memcpy( ptr, &model, sizeof(model) );
  1387. ptr += sizeof(model);
  1388. Assert( (ptr-g_pPhysCollide) == g_PhysCollideSize);
  1389. Msg("done (%d) (%d bytes)\n", (int)(Plat_FloatTime() - start), g_PhysCollideSize );
  1390. // UNDONE: Collision models (collisionList) memory leak!
  1391. }