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.

2855 lines
89 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //
  8. // This file contains code to allow us to associate client data with bsp leaves.
  9. //
  10. //=============================================================================//
  11. #include "vrad.h"
  12. #include "mathlib/vector.h"
  13. #include "UtlBuffer.h"
  14. #include "UtlVector.h"
  15. #include "GameBSPFile.h"
  16. #include "BSPTreeData.h"
  17. #include "VPhysics_Interface.h"
  18. #include "Studio.h"
  19. #include "Optimize.h"
  20. #include "Bsplib.h"
  21. #include "CModel.h"
  22. #include "PhysDll.h"
  23. #include "phyfile.h"
  24. #include "collisionutils.h"
  25. #include "tier1/KeyValues.h"
  26. #include "pacifier.h"
  27. #include "materialsystem/imaterial.h"
  28. #include "materialsystem/hardwareverts.h"
  29. #include "byteswap.h"
  30. #include "mpivrad.h"
  31. #include "vtf/vtf.h"
  32. #include "tier1/utldict.h"
  33. #include "tier1/utlsymbol.h"
  34. #include "tier3/tier3.h"
  35. #include "messbuf.h"
  36. #include "vmpi.h"
  37. #include "vmpi_distribute_work.h"
  38. #include "iscratchpad3d.h"
  39. //#include "glview_buffer.h"
  40. #define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
  41. int g_numVradStaticPropsLightingStreams = 3;
  42. static const TableVector g_localUpBumpBasis[NUM_BUMP_VECTS] =
  43. {
  44. // consistent basis wrt lightmaps
  45. { OO_SQRT_2_OVER_3, 0.0f, OO_SQRT_3 },
  46. { -OO_SQRT_6, OO_SQRT_2, OO_SQRT_3 },
  47. { -OO_SQRT_6, -OO_SQRT_2, OO_SQRT_3 }
  48. };
  49. void GetStaticPropBumpNormals( const Vector& sVect, const Vector& tVect, const Vector& flatNormal,
  50. const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] )
  51. {
  52. Vector tmpNormal;
  53. bool leftHanded;
  54. int i;
  55. assert( NUM_BUMP_VECTS == 3 );
  56. // Are we left or right handed?
  57. CrossProduct( sVect, tVect, tmpNormal );
  58. if( DotProduct( flatNormal, tmpNormal ) < 0.0f )
  59. {
  60. leftHanded = true;
  61. }
  62. else
  63. {
  64. leftHanded = false;
  65. }
  66. // Build a basis for the face around the phong normal
  67. matrix3x4_t smoothBasis;
  68. CrossProduct( phongNormal.Base(), sVect.Base(), smoothBasis[1] );
  69. VectorNormalize( smoothBasis[1] );
  70. CrossProduct( smoothBasis[1], phongNormal.Base(), smoothBasis[0] );
  71. VectorNormalize( smoothBasis[0] );
  72. VectorCopy( phongNormal.Base(), smoothBasis[2] );
  73. if( leftHanded )
  74. {
  75. VectorNegate( smoothBasis[1] );
  76. }
  77. // move the g_localUpBumpBasis into world space to create bumpNormals
  78. for( i = 0; i < 3; i++ )
  79. {
  80. VectorIRotate( g_localUpBumpBasis[i], smoothBasis, bumpNormals[i] );
  81. }
  82. }
  83. // identifies a vertex embedded in solid
  84. // lighting will be copied from nearest valid neighbor
  85. struct badVertex_t
  86. {
  87. int m_ColorVertex;
  88. Vector m_Position;
  89. Vector m_Normals[ NUM_BUMP_VECTS + 1 ];
  90. };
  91. // a final colored vertex
  92. struct colorVertex_t
  93. {
  94. Vector m_Colors[ NUM_BUMP_VECTS + 1 ];
  95. float m_SunAmount[ NUM_BUMP_VECTS + 1 ];
  96. Vector m_Position;
  97. bool m_bValid;
  98. };
  99. class CComputeStaticPropLightingResults
  100. {
  101. public:
  102. ~CComputeStaticPropLightingResults()
  103. {
  104. m_ColorVertsArrays.PurgeAndDeleteElements();
  105. }
  106. CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
  107. };
  108. Vector NormalizeVertexBumpedLighting( Vector const *pColorNormal, Vector *pColorBumps )
  109. {
  110. const Vector &linearUnbumped = *( ( const Vector * )pColorNormal );
  111. Vector linearBump1 = *( ( const Vector * )(pColorBumps + 0) );
  112. Vector linearBump2 = *( ( const Vector * )(pColorBumps + 1) );
  113. Vector linearBump3 = *( ( const Vector * )(pColorBumps + 2) );
  114. const float flNormalizationFactor = 1.0f / 3.0f;
  115. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  116. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  117. // areas.
  118. Vector bumpAverage = linearBump1;
  119. bumpAverage += linearBump2;
  120. bumpAverage += linearBump3;
  121. bumpAverage *= flNormalizationFactor;
  122. Vector correctionScale;
  123. if( *( int * )&bumpAverage[0] != 0 &&
  124. *( int * )&bumpAverage[1] != 0 &&
  125. *( int * )&bumpAverage[2] != 0 )
  126. {
  127. // fast path when we know that we don't have to worry about divide by zero.
  128. VectorDivide( linearUnbumped, bumpAverage, correctionScale );
  129. }
  130. else
  131. {
  132. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  133. if( bumpAverage[0] != 0.0f )
  134. {
  135. correctionScale[0] = linearUnbumped[0] / bumpAverage[0];
  136. }
  137. if( bumpAverage[1] != 0.0f )
  138. {
  139. correctionScale[1] = linearUnbumped[1] / bumpAverage[1];
  140. }
  141. if( bumpAverage[2] != 0.0f )
  142. {
  143. correctionScale[2] = linearUnbumped[2] / bumpAverage[2];
  144. }
  145. }
  146. linearBump1 *= correctionScale;
  147. linearBump2 *= correctionScale;
  148. linearBump3 *= correctionScale;
  149. *((Vector *) (pColorBumps + 0)) = linearBump1;
  150. *((Vector *) (pColorBumps + 1)) = linearBump2;
  151. *((Vector *) (pColorBumps + 2)) = linearBump3;
  152. return correctionScale;
  153. }
  154. void NormalizeVertexBumpedSunAmount( float const *pSunAmount0, float *pSunAmount1, float *pSunAmount2, float *pSunAmount3 )
  155. {
  156. const float &linearSunAmountUnbumped = *((const float *)pSunAmount0);
  157. float linearSunAmount1 = *((const float *)(pSunAmount1));
  158. float linearSunAmount2 = *((const float *)(pSunAmount2));
  159. float linearSunAmount3 = *((const float *)(pSunAmount3));
  160. const float flNormalizationFactor = 1.0f;// / 3.0f; - store in 0..1 space (for 0..255 alpha channel), multiply by 3.0 in the shader
  161. // find a scale factor which makes the average of the 3 bumped mapped vectors match the
  162. // straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
  163. // areas.
  164. float bumpAverage = linearSunAmount1;
  165. bumpAverage += linearSunAmount2;
  166. bumpAverage += linearSunAmount3;
  167. bumpAverage *= flNormalizationFactor;
  168. float correctionScale;
  169. if ( *(int *)&bumpAverage != 0 )
  170. {
  171. // fast path when we know that we don't have to worry about divide by zero.
  172. correctionScale = linearSunAmountUnbumped / bumpAverage;
  173. }
  174. else
  175. {
  176. correctionScale = 1.0f;
  177. if ( bumpAverage != 0.0f )
  178. {
  179. correctionScale = linearSunAmountUnbumped / bumpAverage;
  180. }
  181. }
  182. linearSunAmount1 *= correctionScale;
  183. linearSunAmount2 *= correctionScale;
  184. linearSunAmount3 *= correctionScale;
  185. *((float *)(pSunAmount1)) = linearSunAmount1;
  186. *((float *)(pSunAmount2)) = linearSunAmount2;
  187. *((float *)(pSunAmount3)) = linearSunAmount3;
  188. }
  189. void DumpElapsedTime( int timeTaken )
  190. {
  191. if ( g_bDumpBumpStaticProps && (g_numVradStaticPropsLightingStreams == 3) )
  192. {
  193. char mapName[MAX_PATH];
  194. Q_FileBase( source, mapName, sizeof( mapName ) );
  195. char bumpPropFilename[MAX_PATH];
  196. sprintf( bumpPropFilename, "vrad_bumpstaticprops_%s.txt", mapName );
  197. Msg( "Writing %s...\n", bumpPropFilename );
  198. FILE *fp = fopen( bumpPropFilename, "a" );
  199. if ( !fp )
  200. {
  201. Msg( "Writing %s...failed\n", bumpPropFilename );
  202. return;
  203. }
  204. char str[512];
  205. GetHourMinuteSecondsString( timeTaken, str, sizeof( str ) );
  206. fprintf( fp, "\n\nUsing -staticpropsamplescale %f (-final defaults to 16)\n", g_flStaticPropSampleScale );
  207. fprintf( fp, "\nTotal time taken to bake static prop lighting: %s\n", str );
  208. fclose( fp );
  209. }
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Globals
  213. //-----------------------------------------------------------------------------
  214. CUtlSymbolTable g_ForcedTextureShadowsModels;
  215. // DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
  216. // INSIDE PropTested_t. USE THAT INSTEAD.
  217. IPhysicsCollision *s_pPhysCollision = NULL;
  218. //-----------------------------------------------------------------------------
  219. // Vrad's static prop manager
  220. //-----------------------------------------------------------------------------
  221. class CVradStaticPropMgr : public IVradStaticPropMgr
  222. {
  223. public:
  224. // constructor, destructor
  225. CVradStaticPropMgr();
  226. virtual ~CVradStaticPropMgr();
  227. // methods of IStaticPropMgr
  228. void Init();
  229. void Shutdown();
  230. // iterate all the instanced static props and compute their vertex lighting
  231. void ComputeLighting( int iThread );
  232. virtual void MakePatches() override;
  233. private:
  234. // VMPI stuff.
  235. static void VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf );
  236. static void VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker );
  237. void VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf );
  238. void VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker );
  239. // local thread version
  240. static void ThreadComputeStaticPropLighting( int iThread, void *pUserData );
  241. void ComputeLightingForProp( int iThread, int iStaticProp );
  242. // Methods associated with unserializing static props
  243. void UnserializeModelDict( CUtlBuffer& buf );
  244. void UnserializeModels( CUtlBuffer& buf );
  245. void UnserializeStaticProps();
  246. // Creates a collision model
  247. void CreateCollisionModel( char const* pModelName );
  248. private:
  249. // Unique static prop models
  250. struct StaticPropDict_t
  251. {
  252. vcollide_t m_loadedModel;
  253. CPhysCollide* m_pModel;
  254. Vector m_Mins; // Bounding box is in local coordinates
  255. Vector m_Maxs;
  256. studiohdr_t* m_pStudioHdr;
  257. CUtlBuffer m_VtxBuf;
  258. CUtlVector<int> m_textureShadowIndex; // each texture has an index if this model casts texture shadows
  259. CUtlVector<int> m_triangleMaterialIndex;// each triangle has an index if this model casts texture shadows
  260. Vector m_vReflectivity;
  261. bool m_bHasBumpmap;
  262. bool m_bHasPhong;
  263. };
  264. struct MeshData_t
  265. {
  266. CUtlVector<Vector4D> m_VertColorData; // w has the additional lightmap alpha data
  267. int m_numVerts;
  268. int m_nLod;
  269. };
  270. // A static prop instance
  271. struct CStaticProp
  272. {
  273. Vector m_Origin;
  274. QAngle m_Angles;
  275. Vector m_mins;
  276. Vector m_maxs;
  277. Vector m_LightingOrigin;
  278. int m_ModelIdx;
  279. BSPTreeDataHandle_t m_Handle;
  280. CUtlVector<MeshData_t> m_MeshData;
  281. int m_Flags;
  282. int m_FlagsEx;
  283. bool m_bLightingOriginValid;
  284. Vector m_vReflectivity;
  285. };
  286. // Enumeration context
  287. struct EnumContext_t
  288. {
  289. PropTested_t* m_pPropTested;
  290. Ray_t const* m_pRay;
  291. };
  292. // The list of all static props
  293. CUtlVector <StaticPropDict_t> m_StaticPropDict;
  294. CUtlVector <CStaticProp> m_StaticProps;
  295. bool m_bIgnoreStaticPropTrace;
  296. void ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults );
  297. void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
  298. void SerializeLighting();
  299. void AddPolysForRayTrace();
  300. void BuildTriList( CStaticProp &prop );
  301. };
  302. //-----------------------------------------------------------------------------
  303. // Expose IVradStaticPropMgr to vrad
  304. //-----------------------------------------------------------------------------
  305. static CVradStaticPropMgr g_StaticPropMgr;
  306. IVradStaticPropMgr* StaticPropMgr()
  307. {
  308. return &g_StaticPropMgr;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // constructor, destructor
  312. //-----------------------------------------------------------------------------
  313. CVradStaticPropMgr::CVradStaticPropMgr()
  314. {
  315. // set to ignore static prop traces
  316. m_bIgnoreStaticPropTrace = false;
  317. }
  318. CVradStaticPropMgr::~CVradStaticPropMgr()
  319. {
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Makes sure the studio model is a static prop
  323. //-----------------------------------------------------------------------------
  324. bool IsStaticProp( studiohdr_t* pHdr )
  325. {
  326. if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
  327. return false;
  328. return true;
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Load a file into a Utlbuf
  332. //-----------------------------------------------------------------------------
  333. static bool LoadFile( char const* pFileName, CUtlBuffer& buf )
  334. {
  335. if ( ReadFileFromPak( GetPakFile(), pFileName, false, buf ) )
  336. return true;
  337. if ( !g_pFullFileSystem )
  338. return false;
  339. return g_pFullFileSystem->ReadFile( pFileName, NULL, buf );
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Constructs the file name from the model name
  343. //-----------------------------------------------------------------------------
  344. static char const* ConstructFileName( char const* pModelName )
  345. {
  346. static char buf[1024];
  347. sprintf( buf, "%s%s", gamedir, pModelName );
  348. return buf;
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Computes a convex hull from a studio mesh
  352. //-----------------------------------------------------------------------------
  353. static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh, studiohdr_t *pStudioHdr )
  354. {
  355. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  356. Assert( vertData ); // This can only return NULL on X360 for now
  357. // Generate a list of all verts in the mesh
  358. Vector** ppVerts = (Vector**)_alloca(pMesh->numvertices * sizeof(Vector*) );
  359. for (int i = 0; i < pMesh->numvertices; ++i)
  360. {
  361. ppVerts[i] = vertData->Position(i);
  362. }
  363. // Generate a convex hull from the verts
  364. return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Computes a convex hull from the studio model
  368. //-----------------------------------------------------------------------------
  369. CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
  370. {
  371. CUtlVector<CPhysConvex*> convexHulls;
  372. for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
  373. {
  374. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
  375. for( int model = 0; model < pBodyPart->nummodels; ++model )
  376. {
  377. mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
  378. for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
  379. {
  380. // Make a convex hull for each mesh
  381. // NOTE: This won't work unless the model has been compiled
  382. // with $staticprop
  383. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
  384. convexHulls.AddToTail( ComputeConvexHull( pStudioMesh, pStudioHdr ) );
  385. }
  386. }
  387. }
  388. // Convert an array of convex elements to a compiled collision model
  389. // (this deletes the convex elements)
  390. return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Count() );
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Load studio model vertex data from a file...
  394. //-----------------------------------------------------------------------------
  395. bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf )
  396. {
  397. // No luck, gotta build it
  398. // Construct the file name...
  399. if (!LoadFile( pModelName, buf ))
  400. {
  401. Warning("Error! Unable to load model \"%s\"\n", pModelName );
  402. return false;
  403. }
  404. // Check that it's valid
  405. if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
  406. strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
  407. {
  408. Warning("Error! Invalid model file \"%s\"\n", pModelName );
  409. return false;
  410. }
  411. studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
  412. Studio_ConvertStudioHdrToNewVersion( pHdr );
  413. if (pHdr->version != STUDIO_VERSION)
  414. {
  415. Warning("Error! Invalid model version \"%s\"\n", pModelName );
  416. return false;
  417. }
  418. if (!IsStaticProp(pHdr))
  419. {
  420. Warning("Error! To use model \"%s\"\n"
  421. " as a static prop, it must be compiled with $staticprop!\n", pModelName );
  422. return false;
  423. }
  424. // ensure reset
  425. pHdr->SetVertexBase( NULL );
  426. pHdr->SetIndexBase( NULL );
  427. return true;
  428. }
  429. bool LoadStudioCollisionModel( char const* pModelName, CUtlBuffer& buf )
  430. {
  431. char tmp[1024];
  432. Q_strncpy( tmp, pModelName, sizeof( tmp ) );
  433. Q_SetExtension( tmp, ".phy", sizeof( tmp ) );
  434. // No luck, gotta build it
  435. if (!LoadFile( tmp, buf ))
  436. {
  437. // this is not an error, the model simply has no PHY file
  438. return false;
  439. }
  440. phyheader_t *header = (phyheader_t *)buf.PeekGet();
  441. if ( header->size != sizeof(*header) || header->solidCount <= 0 )
  442. return false;
  443. return true;
  444. }
  445. bool LoadVTXFile( char const* pModelName, const studiohdr_t *pStudioHdr, CUtlBuffer& buf )
  446. {
  447. char filename[MAX_PATH];
  448. // construct filename
  449. Q_StripExtension( pModelName, filename, sizeof( filename ) );
  450. strcat( filename, ".dx90.vtx" );
  451. if ( !LoadFile( filename, buf ) )
  452. {
  453. Warning( "Error! Unable to load file \"%s\"\n", filename );
  454. return false;
  455. }
  456. OptimizedModel::FileHeader_t* pVtxHdr = (OptimizedModel::FileHeader_t *)buf.Base();
  457. // Check that it's valid
  458. if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
  459. {
  460. Warning( "Error! Invalid VTX file version: %d, expected %d \"%s\"\n", pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION, filename );
  461. return false;
  462. }
  463. if ( pVtxHdr->checkSum != pStudioHdr->checksum )
  464. {
  465. Warning( "Error! Invalid VTX file checksum: %d, expected %d \"%s\"\n", pVtxHdr->checkSum, pStudioHdr->checksum, filename );
  466. return false;
  467. }
  468. return true;
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Gets a vertex position from a strip index
  472. //-----------------------------------------------------------------------------
  473. inline static Vector* PositionFromIndex( const mstudio_meshvertexdata_t *vertData, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup, int i )
  474. {
  475. OptimizedModel::Vertex_t* pVert = pStripGroup->pVertex( i );
  476. return vertData->Position( pVert->origMeshVertID );
  477. }
  478. //-----------------------------------------------------------------------------
  479. // Purpose: Writes a glview text file containing the collision surface in question
  480. // Input : *pCollide -
  481. // *pFilename -
  482. //-----------------------------------------------------------------------------
  483. void DumpCollideToGlView( vcollide_t *pCollide, const char *pFilename )
  484. {
  485. if ( !pCollide )
  486. return;
  487. Msg("Writing %s...\n", pFilename );
  488. FILE *fp = fopen( pFilename, "w" );
  489. for (int i = 0; i < pCollide->solidCount; ++i)
  490. {
  491. Vector *outVerts;
  492. int vertCount = s_pPhysCollision->CreateDebugMesh( pCollide->solids[i], &outVerts );
  493. int triCount = vertCount / 3;
  494. int vert = 0;
  495. unsigned char r = (i & 1) * 64 + 64;
  496. unsigned char g = (i & 2) * 64 + 64;
  497. unsigned char b = (i & 4) * 64 + 64;
  498. float fr = r / 255.0f;
  499. float fg = g / 255.0f;
  500. float fb = b / 255.0f;
  501. for ( int i = 0; i < triCount; i++ )
  502. {
  503. fprintf( fp, "3\n" );
  504. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  505. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  506. vert++;
  507. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  508. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  509. vert++;
  510. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  511. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  512. vert++;
  513. }
  514. s_pPhysCollision->DestroyDebugMesh( vertCount, outVerts );
  515. }
  516. fclose( fp );
  517. }
  518. static bool PointInTriangle( const Vector2D &p, const Vector2D &v0, const Vector2D &v1, const Vector2D &v2 )
  519. {
  520. float coords[3];
  521. GetBarycentricCoords2D( v0, v1, v2, p, coords );
  522. for ( int i = 0; i < 3; i++ )
  523. {
  524. if ( coords[i] < 0.0f || coords[i] > 1.0f )
  525. return false;
  526. }
  527. float sum = coords[0] + coords[1] + coords[2];
  528. if ( sum > 1.0f )
  529. return false;
  530. return true;
  531. }
  532. bool LoadFileIntoBuffer( CUtlBuffer &buf, const char *pFilename )
  533. {
  534. FileHandle_t fileHandle = g_pFileSystem->Open( pFilename, "rb" );
  535. if ( !fileHandle )
  536. return false;
  537. // Get the file size
  538. int texSize = g_pFileSystem->Size( fileHandle );
  539. buf.EnsureCapacity( texSize );
  540. int nBytesRead = g_pFileSystem->Read( buf.Base(), texSize, fileHandle );
  541. g_pFileSystem->Close( fileHandle );
  542. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  543. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  544. return true;
  545. }
  546. // keeps a list of all textures that cast shadows via alpha channel
  547. class CShadowTextureList
  548. {
  549. public:
  550. // This loads a vtf and converts it to RGB8888 format
  551. unsigned char *LoadVTFRGB8888( const char *pName, int *pWidth, int *pHeight, bool *pClampU, bool *pClampV )
  552. {
  553. char szPath[MAX_PATH];
  554. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  555. Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  556. Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
  557. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  558. CUtlBuffer buf;
  559. if ( !LoadFileIntoBuffer( buf, szPath ) )
  560. return NULL;
  561. IVTFTexture *pTex = CreateVTFTexture();
  562. if (!pTex->Unserialize( buf ))
  563. return NULL;
  564. Msg("Loaded alpha texture %s\n", szPath );
  565. unsigned char *pSrcImage = pTex->ImageData( 0, 0, 0, 0, 0, 0 );
  566. int iWidth = pTex->Width();
  567. int iHeight = pTex->Height();
  568. ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888;
  569. ImageFormat srcFormat = pTex->Format();
  570. *pClampU = (pTex->Flags() & TEXTUREFLAGS_CLAMPS) ? true : false;
  571. *pClampV = (pTex->Flags() & TEXTUREFLAGS_CLAMPT) ? true : false;
  572. unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
  573. if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
  574. pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
  575. {
  576. delete[] pDstImage;
  577. return NULL;
  578. }
  579. *pWidth = iWidth;
  580. *pHeight = iHeight;
  581. return pDstImage;
  582. }
  583. // Checks the database for the material and loads if necessary
  584. // returns true if found and pIndex will be the index, -1 if no alpha shadows
  585. bool FindOrLoadIfValid( const char *pMaterialName, int *pIndex )
  586. {
  587. *pIndex = -1;
  588. int index = m_Textures.Find(pMaterialName);
  589. bool bFound = false;
  590. if ( index != m_Textures.InvalidIndex() )
  591. {
  592. bFound = true;
  593. *pIndex = index;
  594. }
  595. else
  596. {
  597. KeyValues *pVMT = new KeyValues("vmt");
  598. CUtlBuffer buf(0,0,CUtlBuffer::TEXT_BUFFER);
  599. LoadFileIntoBuffer( buf, pMaterialName );
  600. if ( pVMT->LoadFromBuffer( pMaterialName, buf ) )
  601. {
  602. bFound = true;
  603. if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") )
  604. {
  605. KeyValues *pBaseTexture = pVMT->FindKey("$basetexture");
  606. if ( pBaseTexture )
  607. {
  608. const char *pBaseTextureName = pBaseTexture->GetString();
  609. if ( pBaseTextureName )
  610. {
  611. int w, h;
  612. bool bClampU = false;
  613. bool bClampV = false;
  614. unsigned char *pImageBits = LoadVTFRGB8888( pBaseTextureName, &w, &h, &bClampU, &bClampV );
  615. if ( pImageBits )
  616. {
  617. int index = m_Textures.Insert( pMaterialName );
  618. m_Textures[index].InitFromRGB8888( w, h, pImageBits );
  619. *pIndex = index;
  620. if ( pVMT->FindKey("$nocull") )
  621. {
  622. // UNDONE: Support this? Do we need to emit two triangles?
  623. m_Textures[index].allowBackface = true;
  624. }
  625. m_Textures[index].clampU = bClampU;
  626. m_Textures[index].clampV = bClampV;
  627. delete[] pImageBits;
  628. }
  629. }
  630. }
  631. }
  632. }
  633. pVMT->deleteThis();
  634. }
  635. return bFound;
  636. }
  637. // iterate the textures for the model and load each one into the database
  638. // this is used on models marked to cast texture shadows
  639. void LoadAllTexturesForModel( studiohdr_t *pHdr, int *pTextureList )
  640. {
  641. for ( int i = 0; i < pHdr->numtextures; i++ )
  642. {
  643. int textureIndex = -1;
  644. // try to add each texture to the transparent shadow manager
  645. char szPath[MAX_PATH];
  646. // iterate quietly through all specified directories until a valid material is found
  647. for ( int j = 0; j < pHdr->numcdtextures; j++ )
  648. {
  649. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  650. Q_strncat( szPath, pHdr->pCdtexture( j ), sizeof( szPath ) );
  651. const char *textureName = pHdr->pTexture( i )->pszName();
  652. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  653. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  654. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  655. if ( FindOrLoadIfValid( szPath, &textureIndex ) )
  656. break;
  657. }
  658. pTextureList[i] = textureIndex;
  659. }
  660. }
  661. int AddMaterialEntry( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  662. {
  663. int index = m_MaterialEntries.AddToTail();
  664. m_MaterialEntries[index].textureIndex = shadowTextureIndex;
  665. m_MaterialEntries[index].uv[0] = t0;
  666. m_MaterialEntries[index].uv[1] = t1;
  667. m_MaterialEntries[index].uv[2] = t2;
  668. return index;
  669. }
  670. // HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
  671. float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  672. {
  673. float umin = min(t0.x, t1.x);
  674. umin = min(umin, t2.x);
  675. float umax = max(t0.x, t1.x);
  676. umax = max(umax, t2.x);
  677. float vmin = min(t0.y, t1.y);
  678. vmin = min(vmin, t2.y);
  679. float vmax = max(t0.y, t1.y);
  680. vmax = max(vmax, t2.y);
  681. // UNDONE: Do something about tiling
  682. umin = clamp(umin, 0, 1);
  683. umax = clamp(umax, 0, 1);
  684. vmin = clamp(vmin, 0, 1);
  685. vmax = clamp(vmax, 0, 1);
  686. Assert(umin>=0.0f && umax <= 1.0f);
  687. Assert(vmin>=0.0f && vmax <= 1.0f);
  688. const alphatexture_t &tex = m_Textures.Element(shadowTextureIndex);
  689. int u0 = umin * (tex.width-1);
  690. int u1 = umax * (tex.width-1);
  691. int v0 = vmin * (tex.height-1);
  692. int v1 = vmax * (tex.height-1);
  693. int total = 0;
  694. int count = 0;
  695. for ( int v = v0; v <= v1; v++ )
  696. {
  697. int row = (v * tex.width);
  698. for ( int u = u0; u <= u1; u++ )
  699. {
  700. total += tex.pAlphaTexels[row + u];
  701. count++;
  702. }
  703. }
  704. if ( count )
  705. {
  706. float coverage = float(total) / (count * 255.0f);
  707. return coverage;
  708. }
  709. return 1.0f;
  710. }
  711. int SampleMaterial( int materialIndex, const Vector &coords, bool bBackface )
  712. {
  713. const materialentry_t &mat = m_MaterialEntries[materialIndex];
  714. const alphatexture_t &tex = m_Textures.Element(m_MaterialEntries[materialIndex].textureIndex);
  715. if ( bBackface && !tex.allowBackface )
  716. return 0;
  717. Vector2D uv = coords.x * mat.uv[0] + coords.y * mat.uv[1] + coords.z * mat.uv[2];
  718. // bilinear filtered sample
  719. float ou = uv[0] * tex.width;
  720. float ov = uv[1] * tex.height;
  721. int u = floor( ou );
  722. int v = floor( ov );
  723. int u1 = u+1;
  724. int v1 = v+1;
  725. u &= (tex.width-1);
  726. u1 &= (tex.width-1);
  727. v &= (tex.height-1);
  728. v1 &= (tex.height-1);
  729. float lerpU = ou - u;
  730. float lerpV = ov - v;
  731. int x = (tex.pAlphaTexels[v * tex.width + u] * (1-lerpU)) + (lerpU*tex.pAlphaTexels[v * tex.width + u1]);
  732. int y = (tex.pAlphaTexels[v1 * tex.width + u] * (1-lerpU)) + (lerpU*tex.pAlphaTexels[v1 * tex.width + u1]);
  733. return int( x * (1-lerpV) + (y*lerpV) );
  734. }
  735. void GetMapping( int shadowTextureIndex, int *pWidth, int *pHeight )
  736. {
  737. *pWidth = m_Textures[shadowTextureIndex].width;
  738. *pHeight = m_Textures[shadowTextureIndex].height;
  739. }
  740. struct alphatexture_t
  741. {
  742. short width;
  743. short height;
  744. bool allowBackface;
  745. bool clampU;
  746. bool clampV;
  747. unsigned char *pAlphaTexels;
  748. void InitFromRGB8888( int w, int h, unsigned char *pTexels )
  749. {
  750. width = w;
  751. height = h;
  752. pAlphaTexels = new unsigned char[w*h];
  753. for ( int i = 0; i < h; i++ )
  754. {
  755. for ( int j = 0; j < w; j++ )
  756. {
  757. int index = (i*w) + j;
  758. pAlphaTexels[index] = pTexels[index*4 + 3];
  759. }
  760. }
  761. }
  762. };
  763. struct materialentry_t
  764. {
  765. int textureIndex;
  766. Vector2D uv[3];
  767. };
  768. // this is the list of textures we've loaded
  769. // only load each one once
  770. CUtlDict< alphatexture_t, unsigned short > m_Textures;
  771. CUtlVector<materialentry_t> m_MaterialEntries;
  772. };
  773. // global to keep the shadow-casting texture list and their alpha bits
  774. CShadowTextureList g_ShadowTextureList;
  775. float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID )
  776. {
  777. const float alphaScale = 1.0f / 255.0f;
  778. // UNDONE: Pass ray down to determine backfacing?
  779. //Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
  780. //bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
  781. Vector coords(b0,b1,b2);
  782. return alphaScale * g_ShadowTextureList.SampleMaterial( g_RtEnv.GetTriangleMaterial(hitID), coords, false );
  783. }
  784. // this is here to strip models/ or .mdl or whatnot
  785. void CleanModelName( const char *pModelName, char *pOutput, int outLen )
  786. {
  787. // strip off leading models/ if it exists
  788. const char *pModelDir = "models/";
  789. int modelLen = Q_strlen(pModelDir);
  790. if ( !Q_strnicmp(pModelName, pModelDir, modelLen ) )
  791. {
  792. pModelName += modelLen;
  793. }
  794. Q_strncpy( pOutput, pModelName, outLen );
  795. // truncate any .mdl extension
  796. char *dot = strchr(pOutput,'.');
  797. if ( dot )
  798. {
  799. *dot = 0;
  800. }
  801. }
  802. int LoadShadowTexture( const char *pMaterialName )
  803. {
  804. int textureIndex = -1;
  805. // try to add each texture to the transparent shadow manager
  806. char szPath[MAX_PATH];
  807. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  808. Q_strncat( szPath, pMaterialName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  809. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  810. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  811. g_ShadowTextureList.FindOrLoadIfValid( szPath, &textureIndex );
  812. return textureIndex;
  813. }
  814. int AddShadowTextureTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  815. {
  816. return g_ShadowTextureList.AddMaterialEntry(shadowTextureIndex, t0, t1, t2 );
  817. }
  818. float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  819. {
  820. return g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, t0, t1, t2 );
  821. }
  822. void GetShadowTextureMapping( int shadowTextureIndex, int *pWidth, int *pHeight )
  823. {
  824. g_ShadowTextureList.GetMapping( shadowTextureIndex, pWidth, pHeight );
  825. }
  826. void ForceTextureShadowsOnModel( const char *pModelName )
  827. {
  828. char buf[1024];
  829. CleanModelName( pModelName, buf, sizeof(buf) );
  830. if ( !g_ForcedTextureShadowsModels.Find(buf).IsValid())
  831. {
  832. g_ForcedTextureShadowsModels.AddString(buf);
  833. }
  834. }
  835. bool IsModelTextureShadowsForced( const char *pModelName )
  836. {
  837. char buf[1024];
  838. CleanModelName( pModelName, buf, sizeof(buf) );
  839. return g_ForcedTextureShadowsModels.Find(buf).IsValid();
  840. }
  841. bool IsStaticPropBumpmapped( studiohdr_t *pStudioHdr )
  842. {
  843. if ( g_numVradStaticPropsLightingStreams == 1 )
  844. {
  845. return false;
  846. }
  847. // check if prop uses "$bumpmap" in any materials, use this as an indication of valid tangent data (availability of tangentdata does not imply it's valid/used)
  848. for ( int textureIndex = 0; textureIndex < pStudioHdr->numtextures; textureIndex++ )
  849. {
  850. char szPath[MAX_PATH];
  851. // iterate quietly through all specified directories until a valid material is found
  852. for ( int i = 0; i < pStudioHdr->numcdtextures; i++ )
  853. {
  854. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  855. Q_strncat( szPath, pStudioHdr->pCdtexture( i ), sizeof( szPath ) );
  856. const char *textureName = pStudioHdr->pTexture( textureIndex )->pszName();
  857. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  858. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  859. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  860. KeyValues *pVMT = new KeyValues( "vmt" );
  861. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  862. LoadFileIntoBuffer( buf, szPath );
  863. if ( pVMT->LoadFromBuffer( szPath, buf ) )
  864. {
  865. if ( pVMT->FindKey( "$bumpmap" ) )
  866. {
  867. pVMT->deleteThis();
  868. return true;
  869. }
  870. }
  871. pVMT->deleteThis();
  872. }
  873. }
  874. return false;
  875. }
  876. void StaticPropHasPhongBump( studiohdr_t *pStudioHdr, bool *pHasBumpmap, bool *pHasPhong )
  877. {
  878. if ( g_numVradStaticPropsLightingStreams == 1 )
  879. {
  880. return;
  881. }
  882. *pHasBumpmap = false;
  883. *pHasPhong = false;
  884. // check if prop uses "$bumpmap" in any materials, use this as an indication of valid tangent data (availability of tangentdata does not imply it's valid/used)
  885. for ( int textureIndex = 0; textureIndex < pStudioHdr->numtextures; textureIndex++ )
  886. {
  887. char szPath[MAX_PATH];
  888. // iterate quietly through all specified directories until a valid material is found
  889. for ( int i = 0; i < pStudioHdr->numcdtextures; i++ )
  890. {
  891. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  892. Q_strncat( szPath, pStudioHdr->pCdtexture( i ), sizeof( szPath ) );
  893. const char *textureName = pStudioHdr->pTexture( textureIndex )->pszName();
  894. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  895. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  896. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  897. KeyValues *pVMT = new KeyValues( "vmt" );
  898. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  899. LoadFileIntoBuffer( buf, szPath );
  900. if ( pVMT->LoadFromBuffer( szPath, buf ) )
  901. {
  902. if ( pVMT->FindKey( "$bumpmap" ) )
  903. {
  904. *pHasBumpmap = true;
  905. // is it also phong
  906. if ( pVMT->FindKey( "$phong" ) )
  907. {
  908. *pHasPhong = true;
  909. pVMT->deleteThis();
  910. return;
  911. }
  912. }
  913. }
  914. pVMT->deleteThis();
  915. }
  916. }
  917. return;
  918. }
  919. Vector ReadReflectivityFromVTF( const char *pName )
  920. {
  921. Vector vRefl( 0.18f, 0.18f, 0.18f );
  922. char szPath[ MAX_PATH ];
  923. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  924. Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  925. Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
  926. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  927. int nHeaderSize = VTFFileHeaderSize();
  928. unsigned char *pMem = (unsigned char *)stackalloc( nHeaderSize );
  929. CUtlBuffer buf( pMem, nHeaderSize );
  930. if ( g_pFullFileSystem->ReadFile( szPath, NULL, buf, nHeaderSize ) )
  931. {
  932. IVTFTexture *pTex = CreateVTFTexture();
  933. if ( pTex->Unserialize( buf, true ) )
  934. {
  935. vRefl = pTex->Reflectivity();
  936. }
  937. DestroyVTFTexture( pTex );
  938. }
  939. return vRefl;
  940. }
  941. Vector ComputeStaticPropReflectivity( studiohdr_t *pStudioHdr )
  942. {
  943. Vector vReflectivity( 0.18f, 0.18f, 0.18f );
  944. for ( int textureIndex = 0; textureIndex < pStudioHdr->numtextures; textureIndex++ )
  945. {
  946. char szPath[ MAX_PATH ];
  947. // iterate quietly through all specified directories until a valid material is found
  948. for ( int i = 0; i < pStudioHdr->numcdtextures; i++ )
  949. {
  950. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  951. Q_strncat( szPath, pStudioHdr->pCdtexture( i ), sizeof( szPath ) );
  952. const char *textureName = pStudioHdr->pTexture( textureIndex )->pszName();
  953. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  954. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  955. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  956. Vector vVtfRefl( 1.0f, 1.0f, 1.0f );
  957. Vector vTint( 1.0f, 1.0f, 1.0f );
  958. KeyValues *pVMT = new KeyValues( "vmt" );
  959. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  960. LoadFileIntoBuffer( buf, szPath );
  961. if ( pVMT->LoadFromBuffer( szPath, buf ) )
  962. {
  963. KeyValues *pBaseTexture = pVMT->FindKey( "$basetexture" );
  964. if ( pBaseTexture )
  965. {
  966. const char *pBaseTextureName = pBaseTexture->GetString();
  967. if ( pBaseTextureName )
  968. {
  969. vVtfRefl = ReadReflectivityFromVTF( pBaseTextureName );
  970. }
  971. }
  972. vReflectivity = vVtfRefl;
  973. KeyValues *pColorTint = pVMT->FindKey( "color" );
  974. if ( pColorTint )
  975. {
  976. const char *pColorString = pColorTint->GetString();
  977. if ( pColorString[ 0 ] == '{' )
  978. {
  979. int r = 0;
  980. int g = 0;
  981. int b = 0;
  982. sscanf( pColorString, "{%d %d %d}", &r, &g, &b );
  983. vTint.x = SrgbGammaToLinear( clamp( float( r ) / 255.0f, 0.0f, 1.0f ) );
  984. vTint.y = SrgbGammaToLinear( clamp( float( r ) / 255.0f, 0.0f, 1.0f ) );
  985. vTint.z = SrgbGammaToLinear( clamp( float( r ) / 255.0f, 0.0f, 1.0f ) );
  986. }
  987. else if ( pColorString[ 0 ] == '[' )
  988. {
  989. sscanf( pColorString, "[%f %f %f]", &vTint.x, &vTint.y, &vTint.z );
  990. vTint.x = clamp( vTint.x, 0.0f, 1.0f );
  991. vTint.y = clamp( vTint.y, 0.0f, 1.0f );
  992. vTint.z = clamp( vTint.z, 0.0f, 1.0f );
  993. }
  994. }
  995. }
  996. pVMT->deleteThis();
  997. vReflectivity = vVtfRefl * vTint;
  998. if ( vReflectivity.x == 1.0f && vReflectivity.y == 1.0f && vReflectivity.z == 1.0f )
  999. {
  1000. vReflectivity.Init( 0.18f, 0.18f, 0.18f );
  1001. }
  1002. return vReflectivity;
  1003. }
  1004. }
  1005. return vReflectivity;
  1006. }
  1007. //-----------------------------------------------------------------------------
  1008. // Creates a collision model (based on the render geometry!)
  1009. //-----------------------------------------------------------------------------
  1010. void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName )
  1011. {
  1012. CUtlBuffer buf;
  1013. CUtlBuffer bufvtx;
  1014. CUtlBuffer bufphy;
  1015. int i = m_StaticPropDict.AddToTail();
  1016. m_StaticPropDict[i].m_pModel = NULL;
  1017. m_StaticPropDict[i].m_pStudioHdr = NULL;
  1018. if ( !LoadStudioModel( pModelName, buf ) )
  1019. {
  1020. VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins );
  1021. VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs );
  1022. return;
  1023. }
  1024. studiohdr_t* pHdr = (studiohdr_t*)buf.Base();
  1025. VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins );
  1026. VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs );
  1027. if ( LoadStudioCollisionModel( pModelName, bufphy ) )
  1028. {
  1029. phyheader_t header;
  1030. bufphy.Get( &header, sizeof(header) );
  1031. vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel;
  1032. s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() );
  1033. m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0];
  1034. /*
  1035. static int propNum = 0;
  1036. char tmp[128];
  1037. sprintf( tmp, "staticprop%03d.txt", propNum );
  1038. DumpCollideToGlView( pCollide, tmp );
  1039. ++propNum;
  1040. */
  1041. }
  1042. else
  1043. {
  1044. // mark this as unused
  1045. m_StaticPropDict[i].m_loadedModel.solidCount = 0;
  1046. // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
  1047. m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr );
  1048. }
  1049. // clone it
  1050. m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() );
  1051. memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() );
  1052. if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) )
  1053. {
  1054. // failed, leave state identified as disabled
  1055. m_StaticPropDict[i].m_VtxBuf.Purge();
  1056. }
  1057. if ( g_bTextureShadows )
  1058. {
  1059. if ( (pHdr->flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS) || IsModelTextureShadowsForced(pModelName) )
  1060. {
  1061. m_StaticPropDict[i].m_textureShadowIndex.RemoveAll();
  1062. m_StaticPropDict[i].m_triangleMaterialIndex.RemoveAll();
  1063. m_StaticPropDict[i].m_textureShadowIndex.AddMultipleToTail( pHdr->numtextures );
  1064. g_ShadowTextureList.LoadAllTexturesForModel( pHdr, m_StaticPropDict[i].m_textureShadowIndex.Base() );
  1065. }
  1066. }
  1067. // mark static props that use $bumpmap, $phong materials
  1068. StaticPropHasPhongBump( pHdr, &m_StaticPropDict[ i ].m_bHasBumpmap, &m_StaticPropDict[ i ].m_bHasPhong );
  1069. m_StaticPropDict[ i ].m_vReflectivity = ComputeStaticPropReflectivity( pHdr );
  1070. }
  1071. //-----------------------------------------------------------------------------
  1072. // Unserialize static prop model dictionary
  1073. //-----------------------------------------------------------------------------
  1074. void CVradStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
  1075. {
  1076. int count = buf.GetInt();
  1077. while ( --count >= 0 )
  1078. {
  1079. StaticPropDictLump_t lump;
  1080. buf.Get( &lump, sizeof(StaticPropDictLump_t) );
  1081. CreateCollisionModel( lump.m_Name );
  1082. }
  1083. // spew bump static prop info
  1084. if ( g_bDumpBumpStaticProps && (g_numVradStaticPropsLightingStreams == 3) )
  1085. {
  1086. char mapName[MAX_PATH];
  1087. Q_FileBase( source, mapName, sizeof( mapName ) );
  1088. char bumpPropFilename[MAX_PATH];
  1089. sprintf( bumpPropFilename, "vrad_bumpstaticprops_%s.txt", mapName);
  1090. Msg( "Writing %s...\n", bumpPropFilename );
  1091. FILE *fp = fopen( bumpPropFilename, "w" );
  1092. if ( !fp )
  1093. {
  1094. Msg( "Writing %s...failed\n", bumpPropFilename );
  1095. return;
  1096. }
  1097. fprintf( fp, "Bumpmap static prop list for %s\n", mapName );
  1098. int numBumpmapStaticProps = 0;
  1099. int numPhongStaticProps = 0;
  1100. for ( int i = m_StaticPropDict.Count(); --i >= 0; )
  1101. {
  1102. studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
  1103. if ( m_StaticPropDict[i].m_bHasBumpmap )
  1104. {
  1105. numBumpmapStaticProps++;
  1106. }
  1107. if ( m_StaticPropDict[i].m_bHasPhong )
  1108. {
  1109. numPhongStaticProps++;
  1110. }
  1111. if ( m_StaticPropDict[i].m_bHasBumpmap || m_StaticPropDict[i].m_bHasPhong )
  1112. {
  1113. fprintf( fp, "\nprop: %s\nvmt's containing $bumpmap, $phong:\n", pStudioHdr->pszName() );
  1114. for ( int textureIndex = 0; textureIndex < pStudioHdr->numtextures; textureIndex++ )
  1115. {
  1116. char szPath[MAX_PATH];
  1117. // iterate quietly through all specified directories until a valid material is found
  1118. for ( int i = 0; i < pStudioHdr->numcdtextures; i++ )
  1119. {
  1120. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  1121. Q_strncat( szPath, pStudioHdr->pCdtexture( i ), sizeof( szPath ) );
  1122. const char *textureName = pStudioHdr->pTexture( textureIndex )->pszName();
  1123. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  1124. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  1125. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  1126. KeyValues *pVMT = new KeyValues( "vmt" );
  1127. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1128. LoadFileIntoBuffer( buf, szPath );
  1129. if ( pVMT->LoadFromBuffer( szPath, buf ) )
  1130. {
  1131. if ( pVMT->FindKey( "$bumpmap" ) )
  1132. {
  1133. if ( pVMT->FindKey( "$phong" ) )
  1134. {
  1135. fprintf( fp, "$bump, $phong: %s\n", szPath );
  1136. }
  1137. else
  1138. {
  1139. fprintf( fp, "$bump: %s\n", szPath );
  1140. }
  1141. }
  1142. else if ( pVMT->FindKey( "$phong" ) )
  1143. {
  1144. // not possible/error?
  1145. fprintf( fp, "$phong: %s\n", szPath );
  1146. }
  1147. }
  1148. pVMT->deleteThis();
  1149. }
  1150. }
  1151. }
  1152. }
  1153. fprintf( fp, "\n%d static props, %d bumped static props (%d phong static props)\n", m_StaticPropDict.Count(), numBumpmapStaticProps, numPhongStaticProps );
  1154. fclose( fp );
  1155. }
  1156. }
  1157. void CVradStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
  1158. {
  1159. int count = buf.GetInt();
  1160. m_StaticProps.AddMultipleToTail(count);
  1161. for ( int i = 0; i < count; ++i )
  1162. {
  1163. StaticPropLump_t lump;
  1164. buf.Get( &lump, sizeof(StaticPropLump_t) );
  1165. VectorCopy( lump.m_Origin, m_StaticProps[i].m_Origin );
  1166. VectorCopy( lump.m_Angles, m_StaticProps[i].m_Angles );
  1167. VectorCopy( lump.m_LightingOrigin, m_StaticProps[i].m_LightingOrigin );
  1168. m_StaticProps[i].m_bLightingOriginValid = ( lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0;
  1169. m_StaticProps[i].m_ModelIdx = lump.m_PropType;
  1170. m_StaticProps[i].m_Handle = TREEDATA_INVALID_HANDLE;
  1171. m_StaticProps[i].m_Flags = lump.m_Flags;
  1172. m_StaticProps[ i ].m_FlagsEx = lump.m_FlagsEx;
  1173. m_StaticProps[ i ].m_vReflectivity.Init( SrgbGammaToLinear( float( lump.m_DiffuseModulation.r ) / 255.0f ),
  1174. SrgbGammaToLinear( float( lump.m_DiffuseModulation.g ) / 255.0f ),
  1175. SrgbGammaToLinear( float( lump.m_DiffuseModulation.b ) / 255.0f ) );
  1176. m_StaticProps[ i ].m_vReflectivity *= m_StaticPropDict[ m_StaticProps[ i ].m_ModelIdx ].m_vReflectivity;
  1177. }
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Unserialize static props
  1181. //-----------------------------------------------------------------------------
  1182. void CVradStaticPropMgr::UnserializeStaticProps()
  1183. {
  1184. // Unserialize static props, insert them into the appropriate leaves
  1185. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
  1186. int size = g_GameLumps.GameLumpSize( handle );
  1187. if (!size)
  1188. return;
  1189. if ( g_GameLumps.GetGameLumpVersion( handle ) != GAMELUMP_STATIC_PROPS_VERSION )
  1190. {
  1191. Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
  1192. }
  1193. if ( g_GameLumps.GetGameLump( handle ) )
  1194. {
  1195. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size, CUtlBuffer::READ_ONLY );
  1196. UnserializeModelDict( buf );
  1197. // Skip the leaf list data
  1198. int count = buf.GetInt();
  1199. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, count * sizeof(StaticPropLeafLump_t) );
  1200. UnserializeModels( buf );
  1201. }
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // Level init, shutdown
  1205. //-----------------------------------------------------------------------------
  1206. void CVradStaticPropMgr::Init()
  1207. {
  1208. CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  1209. if ( !physicsFactory )
  1210. Error( "Unable to load vphysics DLL." );
  1211. s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  1212. if( !s_pPhysCollision )
  1213. {
  1214. Error( "Unable to get '%s' for physics interface.", VPHYSICS_COLLISION_INTERFACE_VERSION );
  1215. return;
  1216. }
  1217. // Read in static props that have been compiled into the bsp file
  1218. UnserializeStaticProps();
  1219. }
  1220. void CVradStaticPropMgr::Shutdown()
  1221. {
  1222. // Remove all static prop model data
  1223. for (int i = m_StaticPropDict.Count(); --i >= 0; )
  1224. {
  1225. studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
  1226. if ( pStudioHdr )
  1227. {
  1228. if ( pStudioHdr->VertexBase() )
  1229. {
  1230. free( pStudioHdr->VertexBase() );
  1231. pStudioHdr->SetVertexBase( nullptr );
  1232. }
  1233. free( pStudioHdr );
  1234. }
  1235. }
  1236. m_StaticProps.Purge();
  1237. m_StaticPropDict.Purge();
  1238. }
  1239. void ComputeLightmapColor( dface_t* pFace, Vector &color )
  1240. {
  1241. texinfo_t* pTex = &texinfo[pFace->texinfo];
  1242. if ( pTex->flags & SURF_SKY )
  1243. {
  1244. // sky ambient already accounted for in direct component
  1245. return;
  1246. }
  1247. }
  1248. bool PositionInSolid( Vector &position )
  1249. {
  1250. /* Testing enabling/disabling since it erroneously reports verts inside light blockers
  1251. and there are a number of position offsets applied elsewhere to avoid surface acne
  1252. that might well be enough */
  1253. if ( g_bDisableStaticPropVertexInSolidTest )
  1254. {
  1255. return false;
  1256. }
  1257. int ndxLeaf = PointLeafnum( position );
  1258. if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
  1259. {
  1260. // position embedded in solid
  1261. return true;
  1262. }
  1263. return false;
  1264. }
  1265. bool PositionIn3DSkybox( Vector &position )
  1266. {
  1267. int iLeaf = PointLeafnum( position );
  1268. int area = dleafs[ iLeaf ].area;
  1269. return area_sky_cameras[ area ] >= 0;
  1270. }
  1271. //-----------------------------------------------------------------------------
  1272. // Trace from a vertex to each direct light source, accumulating its contribution.
  1273. //-----------------------------------------------------------------------------
  1274. void ComputeDirectLightingAtPoint( Vector &position, Vector *normals, Vector *outColors, float *outSunAmount, int numNormals, bool bSkipSkyLight, int iThread,
  1275. int static_prop_id_to_skip, int nLFlags )
  1276. {
  1277. SSE_sampleLightOutput_t sampleOutput;
  1278. for ( int k = 0; k < numNormals; ++ k )
  1279. {
  1280. outColors[k].Init();
  1281. outSunAmount[k] = 0.0f;
  1282. }
  1283. // Iterate over all direct lights and accumulate their contribution
  1284. int cluster = ClusterFromPoint( position );
  1285. for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
  1286. {
  1287. if ( dl->light.style )
  1288. {
  1289. // skip lights with style
  1290. continue;
  1291. }
  1292. // is this lights cluster visible?
  1293. if ( !PVSCheck( dl->pvs, cluster ) )
  1294. continue;
  1295. // push the vertex towards the light to avoid surface acne
  1296. Vector adjusted_pos = position;
  1297. float flEpsilon = 0.0;
  1298. const float flFudgeFactor = 4.0;
  1299. if (dl->light.type != emit_skyambient)
  1300. {
  1301. // push towards the light
  1302. Vector fudge;
  1303. if ( dl->light.type == emit_skylight )
  1304. fudge = -( dl->light.normal);
  1305. else
  1306. {
  1307. fudge = dl->light.origin-position;
  1308. VectorNormalize( fudge );
  1309. }
  1310. fudge *= flFudgeFactor;
  1311. adjusted_pos += fudge;
  1312. }
  1313. else
  1314. {
  1315. // push out along normal
  1316. adjusted_pos += flFudgeFactor * normals[0];
  1317. // flEpsilon = 1.0;
  1318. }
  1319. FourVectors adjusted_pos4;
  1320. adjusted_pos4.DuplicateVector( adjusted_pos );
  1321. FourVectors normal4;
  1322. switch( numNormals )
  1323. {
  1324. case 4:
  1325. normal4.LoadAndSwizzle( normals[0], normals[1], normals[2], normals[3] );
  1326. break;
  1327. case 3:
  1328. normal4.LoadAndSwizzle( normals[0], normals[1], normals[2], normals[0] );
  1329. break;
  1330. default:
  1331. normal4.DuplicateVector( normals[0] );
  1332. break;
  1333. }
  1334. GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4,
  1335. 1, // really it's number of FourVectors passed
  1336. iThread, g_bFastStaticProps ? ( nLFlags | GATHERLFLAGS_FORCE_FAST ) : nLFlags,
  1337. static_prop_id_to_skip, flEpsilon );
  1338. for ( int k = 0; k < numNormals; ++k )
  1339. {
  1340. if ( !((dl->light.type == emit_skylight) && bSkipSkyLight) )
  1341. {
  1342. VectorMA( outColors[k],
  1343. sampleOutput.m_flFalloff.m128_f32[k] * sampleOutput.m_flDot[0].m128_f32[k],
  1344. dl->light.intensity,
  1345. outColors[k] );
  1346. }
  1347. outSunAmount[k] += SubFloat( sampleOutput.m_flSunAmount[0], k ) * (sampleOutput.m_flDot[0].m128_f32[0] > 0.0f ? 1.0f : 0.0f);
  1348. }
  1349. }
  1350. }
  1351. //-----------------------------------------------------------------------------
  1352. // version of above that just computes/returns the sun amount
  1353. //-----------------------------------------------------------------------------
  1354. void ComputeSunAmountAtPoint( Vector &position, Vector *normals, float *outSunAmount, int numNormals, int iThread,
  1355. int static_prop_id_to_skip = -1, int nLFlags = 0 )
  1356. {
  1357. SSE_sampleLightOutput_t sampleOutput;
  1358. for ( int k = 0; k < numNormals; ++k )
  1359. {
  1360. outSunAmount[k] = 0.0f;
  1361. }
  1362. // Iterate over all direct lights and accumulate their contribution
  1363. int cluster = ClusterFromPoint( position );
  1364. for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
  1365. {
  1366. if ( dl->light.style )
  1367. {
  1368. // skip lights with style
  1369. continue;
  1370. }
  1371. if ( dl->light.type != emit_skylight )
  1372. {
  1373. // skip lights that don't contribue to sunamount
  1374. continue;
  1375. }
  1376. // is this lights cluster visible?
  1377. if ( !PVSCheck( dl->pvs, cluster ) )
  1378. continue;
  1379. // push the vertex towards the light to avoid surface acne
  1380. Vector adjusted_pos = position;
  1381. float flEpsilon = 0.0;
  1382. const float flFudgeFactor = 4.0;
  1383. // push towards the light
  1384. Vector fudge;
  1385. fudge = -(dl->light.normal);
  1386. fudge *= flFudgeFactor;
  1387. adjusted_pos += fudge;
  1388. FourVectors adjusted_pos4;
  1389. adjusted_pos4.DuplicateVector( adjusted_pos );
  1390. FourVectors normal4;
  1391. switch ( numNormals )
  1392. {
  1393. case 4:
  1394. normal4.LoadAndSwizzle( normals[0], normals[1], normals[2], normals[3] );
  1395. break;
  1396. case 3:
  1397. normal4.LoadAndSwizzle( normals[0], normals[1], normals[2], normals[0] );
  1398. break;
  1399. default:
  1400. normal4.DuplicateVector( normals[0] );
  1401. break;
  1402. }
  1403. GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4,
  1404. 1, // really it's number of FourVectors passed
  1405. iThread, g_bFastStaticProps ? (nLFlags | GATHERLFLAGS_FORCE_FAST) : nLFlags,
  1406. static_prop_id_to_skip, flEpsilon );
  1407. for ( int k = 0; k < numNormals; ++k )
  1408. {
  1409. outSunAmount[k] += SubFloat( sampleOutput.m_flSunAmount[0], k ) * (sampleOutput.m_flDot[0].m128_f32[0] > 0.0f ? 1.0f : 0.0f);
  1410. }
  1411. }
  1412. }
  1413. //-----------------------------------------------------------------------------
  1414. // Takes the results from a ComputeLighting call and applies it to the static prop in question.
  1415. //-----------------------------------------------------------------------------
  1416. void CVradStaticPropMgr::ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults )
  1417. {
  1418. if ( pResults->m_ColorVertsArrays.Count() == 0 )
  1419. return;
  1420. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1421. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1422. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1423. Assert( pStudioHdr && pVtxHdr );
  1424. int const numVertexLightComponents = g_numVradStaticPropsLightingStreams;
  1425. int iCurColorVertsArray = 0;
  1426. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1427. {
  1428. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  1429. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1430. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1431. {
  1432. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  1433. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1434. const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++];
  1435. for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ )
  1436. {
  1437. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  1438. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  1439. {
  1440. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  1441. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  1442. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  1443. {
  1444. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  1445. int nMeshIdx = prop.m_MeshData.AddToTail();
  1446. prop.m_MeshData[nMeshIdx].m_VertColorData.AddMultipleToTail( pStripGroup->numVerts * numVertexLightComponents );
  1447. prop.m_MeshData[nMeshIdx].m_numVerts = pStripGroup->numVerts;
  1448. prop.m_MeshData[nMeshIdx].m_nLod = nLod;
  1449. for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
  1450. {
  1451. int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
  1452. Assert( nIndex < pStudioModel->numvertices );
  1453. if ( numVertexLightComponents <= 1 )
  1454. {
  1455. prop.m_MeshData[nMeshIdx].m_VertColorData[nVertex].AsVector3D() = colorVerts[nIndex].m_Colors[0];
  1456. prop.m_MeshData[nMeshIdx].m_VertColorData[nVertex].w = colorVerts[nIndex].m_SunAmount[0];
  1457. }
  1458. else for ( int k = 0 ; k < numVertexLightComponents; ++ k )
  1459. {
  1460. prop.m_MeshData[nMeshIdx].m_VertColorData[nVertex * numVertexLightComponents + k].AsVector3D() = colorVerts[nIndex].m_Colors[k + 1];
  1461. prop.m_MeshData[nMeshIdx].m_VertColorData[nVertex * numVertexLightComponents + k].w = colorVerts[nIndex].m_SunAmount[k + 1];
  1462. }
  1463. }
  1464. }
  1465. }
  1466. }
  1467. }
  1468. }
  1469. }
  1470. //-----------------------------------------------------------------------------
  1471. // Trace rays from each unique vertex, accumulating direct and indirect
  1472. // sources at each ray termination. Use the winding data to distribute the unique vertexes
  1473. // into the rendering layout.
  1474. //-----------------------------------------------------------------------------
  1475. void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults )
  1476. {
  1477. CUtlVector<badVertex_t> badVerts;
  1478. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1479. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1480. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1481. if ( !pStudioHdr || !pVtxHdr )
  1482. {
  1483. // must have model and its verts for lighting computation
  1484. // game will fallback to fullbright
  1485. return;
  1486. }
  1487. int nGatherFlags = (prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
  1488. nGatherFlags |= (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING) ? GATHERLFLAGS_NO_OCCLUSION : 0;
  1489. if ( dict.m_bHasPhong )
  1490. {
  1491. nGatherFlags &= ~GATHERLFLAGS_IGNORE_NORMALS;
  1492. }
  1493. nGatherFlags |= GATHERLFLAGS_STATICPROP;
  1494. VMPI_SetCurrentStage( "ComputeLighting" );
  1495. int numSampleNormals = (g_numVradStaticPropsLightingStreams > 1) ? (NUM_BUMP_VECTS + 1) : 1;
  1496. bool bCanUseTangents = dict.m_bHasBumpmap;
  1497. bool bSkipDirectSkylight = true; // Only computing indirect GI for all static props now. Direct sunlight applied in shader.
  1498. if ( PositionIn3DSkybox( prop.m_Origin ) )
  1499. {
  1500. bSkipDirectSkylight = false;
  1501. }
  1502. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1503. {
  1504. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1505. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1506. {
  1507. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1508. // light all unique vertexes
  1509. CUtlVector<colorVertex_t> *pColorVertsArray = new CUtlVector<colorVertex_t>;
  1510. pResults->m_ColorVertsArrays.AddToTail( pColorVertsArray );
  1511. CUtlVector<colorVertex_t> &colorVerts = *pColorVertsArray;
  1512. colorVerts.EnsureCount( pStudioModel->numvertices );
  1513. memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) );
  1514. int numVertexes = 0;
  1515. for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID )
  1516. {
  1517. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  1518. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData((void *)pStudioHdr);
  1519. Assert( vertData ); // This can only return NULL on X360 for now
  1520. for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
  1521. {
  1522. Vector sampleNormals[ NUM_BUMP_VECTS + 1 ];
  1523. Vector samplePosition;
  1524. // transform position and normal into world coordinate system
  1525. matrix3x4_t matrix;
  1526. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  1527. VectorTransform( *vertData->Position( vertexID ), matrix, samplePosition );
  1528. AngleMatrix( prop.m_Angles, matrix );
  1529. VectorTransform( *vertData->Normal( vertexID ), matrix, sampleNormals[0] );
  1530. if( numSampleNormals > 1 )
  1531. {
  1532. Vector *bumpVects = &sampleNormals[1];
  1533. Vector4D *vecTangentS = vertData->HasTangentData() ? vertData->TangentS( vertexID ) : NULL;
  1534. if ( vecTangentS && bCanUseTangents )
  1535. {
  1536. Vector vecTexS;
  1537. VectorTransform( vecTangentS->AsVector3D(), matrix, vecTexS );
  1538. Vector vecTexT;
  1539. CrossProduct( sampleNormals[0], vecTexS, vecTexT );
  1540. vecTexT.NormalizeInPlace();
  1541. // recompute S-vector to have S, T, N as an orthonormal basis for hl2 vectors
  1542. CrossProduct( vecTexT, sampleNormals[0], vecTexS );
  1543. // respect the flip-factor for T-vector
  1544. vecTexT *= vecTangentS->w;
  1545. GetStaticPropBumpNormals(
  1546. vecTexS, vecTexT,
  1547. sampleNormals[0],
  1548. sampleNormals[0],
  1549. bumpVects );
  1550. sampleNormals[0].NormalizeInPlace();
  1551. sampleNormals[1].NormalizeInPlace();
  1552. sampleNormals[2].NormalizeInPlace();
  1553. sampleNormals[3].NormalizeInPlace();
  1554. }
  1555. else
  1556. {
  1557. sampleNormals[1] = sampleNormals[0];
  1558. sampleNormals[2] = sampleNormals[0];
  1559. sampleNormals[3] = sampleNormals[0];
  1560. }
  1561. }
  1562. if ( PositionInSolid( samplePosition ) )
  1563. {
  1564. // vertex is in solid, add to the bad list, and recover later
  1565. badVertex_t badVertex;
  1566. badVertex.m_ColorVertex = numVertexes;
  1567. badVertex.m_Position = samplePosition;
  1568. memcpy( badVertex.m_Normals, sampleNormals, sizeof( badVertex.m_Normals ) );
  1569. badVerts.AddToTail( badVertex );
  1570. }
  1571. else
  1572. {
  1573. Vector direct_pos=samplePosition;
  1574. int skip_prop = -1;
  1575. if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
  1576. {
  1577. skip_prop = prop_index;
  1578. }
  1579. Vector directColors[ NUM_BUMP_VECTS + 1 ];
  1580. float sunAmount[ NUM_BUMP_VECTS + 1 ];
  1581. memset( directColors, 0, sizeof( directColors ) );
  1582. memset( sunAmount, 0, sizeof( sunAmount ) );
  1583. if ( bCanUseTangents )
  1584. {
  1585. ComputeDirectLightingAtPoint( direct_pos,
  1586. sampleNormals, directColors, sunAmount, numSampleNormals, bSkipDirectSkylight,
  1587. iThread,
  1588. skip_prop, nGatherFlags );
  1589. }
  1590. else
  1591. {
  1592. ComputeDirectLightingAtPoint( direct_pos,
  1593. sampleNormals, directColors, sunAmount, 1, bSkipDirectSkylight,
  1594. iThread,
  1595. skip_prop, nGatherFlags );
  1596. directColors[1] = directColors[0];
  1597. directColors[2] = directColors[0];
  1598. directColors[3] = directColors[0];
  1599. sunAmount[1] = sunAmount[0];
  1600. sunAmount[2] = sunAmount[0];
  1601. sunAmount[3] = sunAmount[0];
  1602. }
  1603. if ( numSampleNormals > 1 )
  1604. {
  1605. // doing this for direct and indirect separately helps eliminate errors with CSM blending
  1606. NormalizeVertexBumpedLighting( directColors, directColors + 1 );
  1607. }
  1608. Vector indirectColors[ NUM_BUMP_VECTS + 1 ];
  1609. memset( indirectColors, 0, sizeof( indirectColors ) );
  1610. if (g_bShowStaticPropNormals)
  1611. {
  1612. directColors[0] = sampleNormals[0];
  1613. directColors[0] += Vector(1.0,1.0,1.0);
  1614. directColors[0] *= 50.0;
  1615. directColors[1] = directColors[0];
  1616. directColors[2] = directColors[0];
  1617. directColors[3] = directColors[0];
  1618. }
  1619. else
  1620. {
  1621. if (numbounce >= 1)
  1622. {
  1623. if ( bCanUseTangents )
  1624. {
  1625. ComputeIndirectLightingAtPoint(
  1626. samplePosition, sampleNormals,
  1627. indirectColors, numSampleNormals, iThread, g_bFastStaticProps,
  1628. ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) != 0, prop_index );
  1629. }
  1630. else
  1631. {
  1632. ComputeIndirectLightingAtPoint(
  1633. samplePosition, sampleNormals,
  1634. indirectColors, 1, iThread, g_bFastStaticProps,
  1635. ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) != 0, prop_index );
  1636. indirectColors[1] = indirectColors[0];
  1637. indirectColors[2] = indirectColors[0];
  1638. indirectColors[3] = indirectColors[0];
  1639. }
  1640. if ( numSampleNormals > 1 )
  1641. {
  1642. // doing this for direct and indirect separately helps eliminate errors with CSM blending
  1643. NormalizeVertexBumpedLighting( indirectColors, indirectColors + 1 );
  1644. }
  1645. }
  1646. }
  1647. colorVerts[numVertexes].m_bValid = true;
  1648. colorVerts[numVertexes].m_Position = samplePosition;
  1649. for ( int k = 0; k < numSampleNormals; ++ k )
  1650. {
  1651. VectorAdd( directColors[k], indirectColors[k], colorVerts[numVertexes].m_Colors[k] );
  1652. colorVerts[numVertexes].m_SunAmount[k] = sunAmount[k];
  1653. }
  1654. if ( numSampleNormals > 1 )
  1655. {
  1656. float *pSunAmountUnbumped = &colorVerts[numVertexes].m_SunAmount[0];
  1657. NormalizeVertexBumpedSunAmount( pSunAmountUnbumped, pSunAmountUnbumped+1, pSunAmountUnbumped+2, pSunAmountUnbumped+3 );
  1658. }
  1659. }
  1660. numVertexes++;
  1661. }
  1662. }
  1663. // color in the bad vertexes
  1664. // when entire model has no lighting origin and no valid neighbors
  1665. // must punt, leave black coloring
  1666. if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) )
  1667. {
  1668. for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ )
  1669. {
  1670. Vector bestPosition;
  1671. if ( prop.m_bLightingOriginValid )
  1672. {
  1673. // use the specified lighting origin
  1674. VectorCopy( prop.m_LightingOrigin, bestPosition );
  1675. }
  1676. else
  1677. {
  1678. // find the closest valid neighbor
  1679. int best = 0;
  1680. float closest = FLT_MAX;
  1681. for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ )
  1682. {
  1683. if ( !colorVerts[nColorVertex].m_bValid )
  1684. {
  1685. // skip invalid neighbors
  1686. continue;
  1687. }
  1688. Vector delta;
  1689. VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta );
  1690. float distance = VectorLength( delta );
  1691. if ( distance < closest )
  1692. {
  1693. closest = distance;
  1694. best = nColorVertex;
  1695. }
  1696. }
  1697. // use the best neighbor as the direction to crawl
  1698. VectorCopy( colorVerts[best].m_Position, bestPosition );
  1699. }
  1700. // crawl toward best position
  1701. // subdivide to determine a closer valid point to the bad vertex, and re-light
  1702. Vector midPosition;
  1703. int numIterations = 20;
  1704. while ( --numIterations > 0 )
  1705. {
  1706. VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition );
  1707. VectorScale( midPosition, 0.5f, midPosition );
  1708. if ( PositionInSolid( midPosition ) )
  1709. break;
  1710. bestPosition = midPosition;
  1711. }
  1712. Vector directColors[ NUM_BUMP_VECTS + 1 ];
  1713. memset( directColors, 0, sizeof( directColors ) );
  1714. Vector indirectColors[ NUM_BUMP_VECTS + 1 ];
  1715. memset( indirectColors, 0, sizeof( indirectColors ) );
  1716. float sunAmount[NUM_BUMP_VECTS + 1];
  1717. memset( sunAmount, 0, sizeof( sunAmount ) );
  1718. // re-light from better position
  1719. if ( bCanUseTangents )
  1720. {
  1721. ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normals,
  1722. directColors, sunAmount, numSampleNormals, bSkipDirectSkylight, iThread );
  1723. ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normals,
  1724. indirectColors, numSampleNormals, iThread, true, false, prop_index );
  1725. }
  1726. else
  1727. {
  1728. ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normals,
  1729. directColors, sunAmount, 1, bSkipDirectSkylight, iThread );
  1730. // doing this for direct and indirect separately helps eliminate errors with CSM blending
  1731. ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normals,
  1732. indirectColors, 1, iThread, true, false, prop_index );
  1733. for ( int k = 1; k < numSampleNormals; ++k )
  1734. {
  1735. directColors[k] = directColors[0];
  1736. indirectColors[k] = indirectColors[0];
  1737. sunAmount[k] = sunAmount[0];
  1738. }
  1739. }
  1740. if ( numSampleNormals > 1 )
  1741. {
  1742. // doing this for direct and indirect separately helps eliminate errors with CSM blending
  1743. NormalizeVertexBumpedLighting( directColors, directColors + 1 );
  1744. NormalizeVertexBumpedLighting( indirectColors, indirectColors + 1 );
  1745. }
  1746. // save results, not changing valid status
  1747. // to ensure this offset position is not considered as a viable candidate
  1748. const int idxColorVertex = badVerts[nBadVertex].m_ColorVertex;
  1749. colorVerts[idxColorVertex].m_Position = bestPosition;
  1750. for ( int k = 0; k < numSampleNormals; ++ k )
  1751. {
  1752. VectorAdd( directColors[k], indirectColors[k], colorVerts[idxColorVertex].m_Colors[k] );
  1753. colorVerts[idxColorVertex].m_SunAmount[k] = sunAmount[k];
  1754. }
  1755. if ( numSampleNormals > 1 )
  1756. {
  1757. float *pSunAmountUnbumped = &colorVerts[idxColorVertex].m_SunAmount[0];
  1758. NormalizeVertexBumpedSunAmount( pSunAmountUnbumped, pSunAmountUnbumped + 1, pSunAmountUnbumped + 2, pSunAmountUnbumped + 3 );
  1759. }
  1760. }
  1761. }
  1762. // discard bad verts
  1763. badVerts.Purge();
  1764. }
  1765. }
  1766. }
  1767. //-----------------------------------------------------------------------------
  1768. // Write the lighting to bsp pak lump
  1769. //-----------------------------------------------------------------------------
  1770. void CVradStaticPropMgr::SerializeLighting()
  1771. {
  1772. char filename[MAX_PATH];
  1773. CUtlBuffer utlBuf;
  1774. // illuminate them all
  1775. int count = m_StaticProps.Count();
  1776. if ( !count )
  1777. {
  1778. // nothing to do
  1779. return;
  1780. }
  1781. char mapName[MAX_PATH];
  1782. Q_FileBase( source, mapName, sizeof( mapName ) );
  1783. int size;
  1784. for (int i = 0; i < count; ++i)
  1785. {
  1786. if (g_bHDR)
  1787. {
  1788. sprintf( filename, "sp_hdr_%d.vhv", i );
  1789. }
  1790. else
  1791. {
  1792. sprintf( filename, "sp_%d.vhv", i );
  1793. }
  1794. int totalVertexes = 0;
  1795. for ( int j=0; j<m_StaticProps[i].m_MeshData.Count(); j++ )
  1796. {
  1797. totalVertexes += m_StaticProps[i].m_MeshData[j].m_numVerts;
  1798. }
  1799. int numLightingComponents = g_numVradStaticPropsLightingStreams;
  1800. // allocate a buffer with enough padding for alignment
  1801. size = sizeof( HardwareVerts::FileHeader_t ) +
  1802. m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
  1803. totalVertexes*4*numLightingComponents + 2*512;
  1804. utlBuf.EnsureCapacity( size );
  1805. Q_memset( utlBuf.Base(), 0, size );
  1806. HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
  1807. // align to start of vertex data
  1808. unsigned char *pVertexData = (unsigned char *)(sizeof( HardwareVerts::FileHeader_t ) + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t));
  1809. pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
  1810. // construct header
  1811. pVhvHdr->m_nVersion = VHV_VERSION;
  1812. pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
  1813. pVhvHdr->m_nVertexFlags = ( numLightingComponents > 1 ) ? VERTEX_NORMAL : VERTEX_COLOR;
  1814. pVhvHdr->m_nVertexSize = 4 * numLightingComponents;
  1815. pVhvHdr->m_nVertexes = totalVertexes;
  1816. pVhvHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
  1817. for (int n=0; n<pVhvHdr->m_nMeshes; n++)
  1818. {
  1819. // construct mesh dictionary
  1820. HardwareVerts::MeshHeader_t *pMesh = pVhvHdr->pMesh( n );
  1821. pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
  1822. pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_numVerts;
  1823. pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
  1824. // construct vertexes
  1825. for (int k=0; k<m_StaticProps[i].m_MeshData[n].m_VertColorData.Count(); k++)
  1826. {
  1827. Vector &vector = m_StaticProps[i].m_MeshData[n].m_VertColorData[k].AsVector3D();
  1828. //if ( (vector.x > 1024.0f) || (vector.y > 1024.0f) || (vector.z > 1024.0f) )s
  1829. // Msg(" *** out of range prop lighting *** \n");
  1830. ColorRGBExp32 rgbColor;
  1831. VectorToColorRGBExp32( vector, rgbColor );
  1832. unsigned char dstColor[4];
  1833. ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
  1834. // b,g,r,a order
  1835. pVertexData[0] = dstColor[2];
  1836. pVertexData[1] = dstColor[1];
  1837. pVertexData[2] = dstColor[0];
  1838. // Use the unmodified lighting data to generate the sun percentage, not the output of the RGBE conversions above!
  1839. float flSunAmount = m_StaticProps[i].m_MeshData[n].m_VertColorData[k].w;
  1840. pVertexData[3] = uint8( clamp( flSunAmount, 0.0f, 1.0f ) * 255.0f + 0.5f );
  1841. pVertexData += 4;
  1842. }
  1843. }
  1844. // align to end of file
  1845. pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
  1846. pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
  1847. AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
  1848. }
  1849. }
  1850. void CVradStaticPropMgr::VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf )
  1851. {
  1852. g_StaticPropMgr.VMPI_ProcessStaticProp( iThread, iStaticProp, pBuf );
  1853. }
  1854. void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker )
  1855. {
  1856. g_StaticPropMgr.VMPI_ReceiveStaticPropResults( iStaticProp, pBuf, iWorker );
  1857. }
  1858. //-----------------------------------------------------------------------------
  1859. // Called on workers to do the computation for a static prop and send
  1860. // it to the master.
  1861. //-----------------------------------------------------------------------------
  1862. void CVradStaticPropMgr::VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf )
  1863. {
  1864. // Compute the lighting.
  1865. CComputeStaticPropLightingResults results;
  1866. ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
  1867. VMPI_SetCurrentStage( "EncodeLightingResults" );
  1868. // Encode the results.
  1869. int nLists = results.m_ColorVertsArrays.Count();
  1870. pBuf->write( &nLists, sizeof( nLists ) );
  1871. for ( int i=0; i < nLists; i++ )
  1872. {
  1873. CUtlVector<colorVertex_t> &curList = *results.m_ColorVertsArrays[i];
  1874. int count = curList.Count();
  1875. pBuf->write( &count, sizeof( count ) );
  1876. pBuf->write( curList.Base(), curList.Count() * sizeof( colorVertex_t ) );
  1877. }
  1878. }
  1879. //-----------------------------------------------------------------------------
  1880. // Called on the master when a worker finishes processing a static prop.
  1881. //-----------------------------------------------------------------------------
  1882. void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker )
  1883. {
  1884. // Read in the results.
  1885. CComputeStaticPropLightingResults results;
  1886. int nLists;
  1887. pBuf->read( &nLists, sizeof( nLists ) );
  1888. for ( int i=0; i < nLists; i++ )
  1889. {
  1890. CUtlVector<colorVertex_t> *pList = new CUtlVector<colorVertex_t>;
  1891. results.m_ColorVertsArrays.AddToTail( pList );
  1892. int count;
  1893. pBuf->read( &count, sizeof( count ) );
  1894. pList->SetSize( count );
  1895. pBuf->read( pList->Base(), count * sizeof( colorVertex_t ) );
  1896. }
  1897. // Apply the results.
  1898. ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
  1899. }
  1900. void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
  1901. {
  1902. // Compute the lighting.
  1903. CComputeStaticPropLightingResults results;
  1904. ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
  1905. ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
  1906. }
  1907. void CVradStaticPropMgr::ThreadComputeStaticPropLighting( int iThread, void *pUserData )
  1908. {
  1909. while (1)
  1910. {
  1911. int j = GetThreadWork ();
  1912. if (j == -1)
  1913. break;
  1914. CComputeStaticPropLightingResults results;
  1915. g_StaticPropMgr.ComputeLightingForProp( iThread, j );
  1916. }
  1917. }
  1918. //-----------------------------------------------------------------------------
  1919. // Computes lighting for the static props.
  1920. // Must be after all other surface lighting has been computed for the indirect sampling.
  1921. //-----------------------------------------------------------------------------
  1922. void CVradStaticPropMgr::ComputeLighting( int iThread )
  1923. {
  1924. // illuminate them all
  1925. int count = m_StaticProps.Count();
  1926. if ( !count )
  1927. {
  1928. // nothing to do
  1929. return;
  1930. }
  1931. double start = Plat_FloatTime();
  1932. StartPacifier( "Computing static prop lighting : " );
  1933. #if 0
  1934. CGlViewBuffer glViewBuf;
  1935. glViewBuf.WriteKDTree( &g_RtEnv );
  1936. g_pFullFileSystem->WriteFile( "maps/rtenv.gl", "GAME", glViewBuf );
  1937. #endif
  1938. // ensure any traces against us are ignored because we have no inherit lighting contribution
  1939. m_bIgnoreStaticPropTrace = true;
  1940. if ( g_bUseMPI )
  1941. {
  1942. // Distribute the work among the workers.
  1943. VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
  1944. DistributeWork(
  1945. count,
  1946. &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
  1947. &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
  1948. }
  1949. else
  1950. {
  1951. RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
  1952. }
  1953. // restore default
  1954. m_bIgnoreStaticPropTrace = false;
  1955. // save data to bsp
  1956. SerializeLighting();
  1957. EndPacifier( true );
  1958. double end = Plat_FloatTime();
  1959. DumpElapsedTime( (int)(end - start) );
  1960. }
  1961. //-----------------------------------------------------------------------------
  1962. // Adds all static prop polys to the ray trace store.
  1963. //-----------------------------------------------------------------------------
  1964. void CVradStaticPropMgr::AddPolysForRayTrace( void )
  1965. {
  1966. int count = m_StaticProps.Count();
  1967. if ( !count )
  1968. {
  1969. // nothing to do
  1970. return;
  1971. }
  1972. // Triangle coverage of 1 (full coverage)
  1973. Vector fullCoverage;
  1974. fullCoverage.x = 1.0f;
  1975. for ( int nProp = 0; nProp < count; ++nProp )
  1976. {
  1977. CStaticProp &prop = m_StaticProps[nProp];
  1978. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1979. if ( prop.m_Flags & STATIC_PROP_NO_SHADOW )
  1980. continue;
  1981. // If not using static prop polys, use AABB
  1982. if ( !g_bStaticPropPolys )
  1983. {
  1984. if ( dict.m_pModel )
  1985. {
  1986. VMatrix xform;
  1987. xform.SetupMatrixOrgAngles ( prop.m_Origin, prop.m_Angles );
  1988. ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
  1989. for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
  1990. {
  1991. for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
  1992. {
  1993. Vector verts[3];
  1994. queryModel->GetTriangleVerts( nConvex, nTri, verts );
  1995. for ( int nVert = 0; nVert < 3; ++nVert )
  1996. verts[nVert] = xform.VMul4x3(verts[nVert]);
  1997. g_RtEnv.AddTriangle ( TRACE_ID_STATICPROP | nProp, verts[0], verts[1], verts[2], fullCoverage );
  1998. }
  1999. }
  2000. s_pPhysCollision->DestroyQueryModel( queryModel );
  2001. }
  2002. else
  2003. {
  2004. VectorAdd ( dict.m_Mins, prop.m_Origin, prop.m_mins );
  2005. VectorAdd ( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
  2006. g_RtEnv.AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
  2007. }
  2008. continue;
  2009. }
  2010. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  2011. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  2012. if ( !pStudioHdr || !pVtxHdr )
  2013. {
  2014. // must have model and its verts for decoding triangles
  2015. // must have model and its verts for decoding triangles
  2016. printf( "Can't get studio header (%p) and vertex data (%p) for %s\n", pStudioHdr, pVtxHdr,
  2017. pStudioHdr ? pStudioHdr->name : "***unknown***" );
  2018. continue;
  2019. }
  2020. // only init the triangle table the first time
  2021. bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
  2022. int triangleIndex = 0;
  2023. // transform position into world coordinate system
  2024. matrix3x4_t matrix;
  2025. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  2026. // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
  2027. // body parts -> models -> lod meshes -> strip groups -> strips
  2028. // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
  2029. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  2030. {
  2031. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  2032. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  2033. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  2034. {
  2035. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  2036. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  2037. // assuming lod 0, could iterate if required
  2038. int nLod = 0;
  2039. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  2040. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  2041. {
  2042. // check if this mesh's material is in the no shadow material name list
  2043. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  2044. mstudiotexture_t *pTxtr=pStudioHdr->pTexture(pMesh->material);
  2045. //printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
  2046. bool bSkipThisMesh = false;
  2047. for(int check=0; check<g_NonShadowCastingMaterialStrings.Count(); check++)
  2048. {
  2049. if ( Q_stristr( pTxtr->pszName(),
  2050. g_NonShadowCastingMaterialStrings[check] ) )
  2051. {
  2052. //printf("skip mat name=%s\n",pTxtr->pszName());
  2053. bSkipThisMesh = true;
  2054. break;
  2055. }
  2056. }
  2057. if ( bSkipThisMesh)
  2058. continue;
  2059. int shadowTextureIndex = -1;
  2060. if ( dict.m_textureShadowIndex.Count() )
  2061. {
  2062. shadowTextureIndex = dict.m_textureShadowIndex[pMesh->material];
  2063. }
  2064. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  2065. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  2066. Assert( vertData ); // This can only return NULL on X360 for now
  2067. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  2068. {
  2069. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  2070. int nStrip;
  2071. for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
  2072. {
  2073. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
  2074. for ( int i = 0; i < pStrip->numIndices; i += 3 )
  2075. {
  2076. int idx = pStrip->indexOffset + i;
  2077. unsigned short i1 = *pStripGroup->pIndex( idx );
  2078. unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
  2079. unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
  2080. int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
  2081. int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
  2082. int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
  2083. // transform position into world coordinate system
  2084. matrix3x4_t matrix;
  2085. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  2086. Vector position1;
  2087. Vector position2;
  2088. Vector position3;
  2089. VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
  2090. VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
  2091. VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
  2092. unsigned short flags = 0;
  2093. int materialIndex = -1;
  2094. Vector color = vec3_origin;
  2095. if ( shadowTextureIndex >= 0 )
  2096. {
  2097. if ( bInitTriangles )
  2098. {
  2099. // add texture space and texture index to material database
  2100. // now
  2101. float coverage = g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
  2102. if ( coverage < 1.0f )
  2103. {
  2104. materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
  2105. color.x = coverage;
  2106. }
  2107. else
  2108. {
  2109. materialIndex = -1;
  2110. }
  2111. dict.m_triangleMaterialIndex.AddToTail(materialIndex);
  2112. }
  2113. else
  2114. {
  2115. materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
  2116. triangleIndex++;
  2117. }
  2118. if ( materialIndex >= 0 )
  2119. {
  2120. flags = FCACHETRI_TRANSPARENT;
  2121. }
  2122. }
  2123. // printf( "\ngl 3\n" );
  2124. // printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
  2125. // printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
  2126. // printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
  2127. g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
  2128. position1, position2, position3,
  2129. color, flags, materialIndex);
  2130. }
  2131. }
  2132. }
  2133. }
  2134. }
  2135. }
  2136. }
  2137. }
  2138. struct tl_tri_t
  2139. {
  2140. Vector p0;
  2141. Vector p1;
  2142. Vector p2;
  2143. Vector n0;
  2144. Vector n1;
  2145. Vector n2;
  2146. bool operator == (const tl_tri_t &t) const
  2147. {
  2148. return ( p0 == t.p0 &&
  2149. p1 == t.p1 &&
  2150. p2 == t.p2 &&
  2151. n0 == t.n0 &&
  2152. n1 == t.n1 &&
  2153. n2 == t.n2 );
  2154. }
  2155. };
  2156. struct tl_vert_t
  2157. {
  2158. Vector m_position;
  2159. CUtlLinkedList< tl_tri_t, int > m_triList;
  2160. };
  2161. void AddTriVertsToList( CUtlVector< tl_vert_t > &triListVerts, int vertIndex, Vector vertPosition, Vector p0, Vector p1, Vector p2, Vector n0, Vector n1, Vector n2 )
  2162. {
  2163. tl_tri_t tlTri;
  2164. tlTri.p0 = p0;
  2165. tlTri.p1 = p1;
  2166. tlTri.p2 = p2;
  2167. tlTri.n0 = n0;
  2168. tlTri.n1 = n1;
  2169. tlTri.n2 = n2;
  2170. triListVerts.EnsureCapacity( vertIndex+1 );
  2171. triListVerts[vertIndex].m_position = vertPosition;
  2172. int index = triListVerts[vertIndex].m_triList.Find( tlTri );
  2173. if ( !triListVerts[vertIndex].m_triList.IsValidIndex( index ) )
  2174. {
  2175. // not in list, add to list of triangles
  2176. triListVerts[vertIndex].m_triList.AddToTail( tlTri );
  2177. }
  2178. }
  2179. //-----------------------------------------------------------------------------
  2180. // Builds a list of tris for every vertex
  2181. //-----------------------------------------------------------------------------
  2182. void CVradStaticPropMgr::BuildTriList( CStaticProp &prop )
  2183. {
  2184. // the generated list will consist of a list of verts
  2185. // each vert will have a linked list of triangles that it belongs to
  2186. CUtlVector< tl_vert_t > triListVerts;
  2187. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  2188. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  2189. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  2190. if ( !pStudioHdr || !pVtxHdr )
  2191. {
  2192. // must have model and its verts for decoding triangles
  2193. return;
  2194. }
  2195. // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
  2196. // body parts -> models -> lod meshes -> strip groups -> strips
  2197. // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
  2198. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  2199. {
  2200. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  2201. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  2202. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  2203. {
  2204. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  2205. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  2206. // get the specified lod, assuming lod 0
  2207. int nLod = 0;
  2208. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  2209. // must reset because each model has their own vertexes [0..n]
  2210. // in order for this to be monolithic for the entire prop the list must be segmented
  2211. triListVerts.Purge();
  2212. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  2213. {
  2214. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  2215. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  2216. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  2217. Assert( vertData ); // This can only return NULL on X360 for now
  2218. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  2219. {
  2220. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  2221. int nStrip;
  2222. for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
  2223. {
  2224. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
  2225. for ( int i = 0; i < pStrip->numIndices; i += 3 )
  2226. {
  2227. int idx = pStrip->indexOffset + i;
  2228. unsigned short i1 = *pStripGroup->pIndex( idx );
  2229. unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
  2230. unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
  2231. int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
  2232. int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
  2233. int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
  2234. // transform position into world coordinate system
  2235. matrix3x4_t matrix;
  2236. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  2237. Vector position1;
  2238. Vector position2;
  2239. Vector position3;
  2240. VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
  2241. VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
  2242. VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
  2243. Vector normal1;
  2244. Vector normal2;
  2245. Vector normal3;
  2246. VectorTransform( *vertData->Normal( vertex1 ), matrix, normal1 );
  2247. VectorTransform( *vertData->Normal( vertex2 ), matrix, normal2 );
  2248. VectorTransform( *vertData->Normal( vertex3 ), matrix, normal3 );
  2249. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex1, position1, position1, position2, position3, normal1, normal2, normal3 );
  2250. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex2, position2, position1, position2, position3, normal1, normal2, normal3 );
  2251. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex3, position3, position1, position2, position3, normal1, normal2, normal3 );
  2252. }
  2253. }
  2254. }
  2255. }
  2256. }
  2257. }
  2258. }
  2259. const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
  2260. {
  2261. studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
  2262. Assert( pActiveStudioHdr );
  2263. if ( pActiveStudioHdr->VertexBase() )
  2264. {
  2265. return (vertexFileHeader_t *)pActiveStudioHdr->VertexBase();
  2266. }
  2267. // mandatory callback to make requested data resident
  2268. // load and persist the vertex file
  2269. char fileName[MAX_PATH];
  2270. strcpy( fileName, "models/" );
  2271. strcat( fileName, pActiveStudioHdr->pszName() );
  2272. Q_StripExtension( fileName, fileName, sizeof( fileName ) );
  2273. strcat( fileName, ".vvd" );
  2274. // load the model
  2275. CUtlBuffer bufData;
  2276. if ( !LoadFile( fileName, bufData ) )
  2277. {
  2278. Error( "Unable to load vertex data \"%s\"\n", fileName );
  2279. }
  2280. // Get the file size
  2281. int vvdSize = bufData.TellPut();
  2282. if ( vvdSize == 0 )
  2283. {
  2284. Error( "Bad size for vertex data \"%s\"\n", fileName );
  2285. }
  2286. vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *) bufData.Base();
  2287. // check header
  2288. if ( pVvdHdr->id != MODEL_VERTEX_FILE_ID )
  2289. {
  2290. Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
  2291. }
  2292. if ( pVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
  2293. {
  2294. Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
  2295. }
  2296. if ( pVvdHdr->checksum != pActiveStudioHdr->checksum )
  2297. {
  2298. Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, pActiveStudioHdr->checksum);
  2299. }
  2300. // need to perform mesh relocation fixups
  2301. // allocate a new copy
  2302. vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
  2303. if ( !pNewVvdHdr )
  2304. {
  2305. Error( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
  2306. }
  2307. // load vertexes and run fixups
  2308. bool bExtraData = (pActiveStudioHdr->flags & STUDIOHDR_FLAGS_EXTRA_VERTEX_DATA) != 0;
  2309. Studio_LoadVertexes(pVvdHdr, pNewVvdHdr, 0, true, bExtraData);
  2310. // discard original
  2311. pVvdHdr = pNewVvdHdr;
  2312. pActiveStudioHdr->SetVertexBase( (void*)pVvdHdr );
  2313. return pVvdHdr;
  2314. }
  2315. extern float totalarea;
  2316. extern unsigned num_degenerate_faces;
  2317. extern int fakeplanes;
  2318. extern int PlaneTypeForNormal( Vector& normal );
  2319. void MakePatchForTriangle( winding_t *w, Vector vRefl, int nStaticPropIdx )
  2320. {
  2321. float area;
  2322. CPatch *patch;
  2323. Vector centroid( 0, 0, 0 );
  2324. area = WindingArea( w );
  2325. if ( area <= 0 )
  2326. {
  2327. num_degenerate_faces++;
  2328. return;
  2329. }
  2330. totalarea += area;
  2331. // get a patch
  2332. int ndxPatch = g_Patches.AddToTail();
  2333. patch = &g_Patches[ ndxPatch ];
  2334. memset( patch, 0, sizeof( CPatch ) );
  2335. patch->ndxNext = g_Patches.InvalidIndex();
  2336. patch->ndxNextParent = g_Patches.InvalidIndex();
  2337. patch->ndxNextClusterChild = g_Patches.InvalidIndex();
  2338. patch->child1 = g_Patches.InvalidIndex();
  2339. patch->child2 = g_Patches.InvalidIndex();
  2340. patch->parent = g_Patches.InvalidIndex();
  2341. patch->needsBumpmap = false;
  2342. patch->staticPropIdx = nStaticPropIdx;
  2343. patch->scale[ 0 ] = patch->scale[ 1 ] = 1.0f;
  2344. patch->area = area;
  2345. patch->sky = false;
  2346. // chop scaled up lightmaps coarser
  2347. patch->luxscale = 16.0f;
  2348. patch->chop = maxchop;
  2349. patch->winding = w;
  2350. patch->plane = new dplane_t;
  2351. Vector vecNormal;
  2352. CrossProduct( w->p[ 2 ] - w->p[ 0 ], w->p[ 1 ] - w->p[ 0 ], vecNormal );
  2353. VectorNormalize( vecNormal );
  2354. VectorCopy( vecNormal, patch->plane->normal );
  2355. patch->plane->dist = vecNormal.Dot( w->p[ 0 ] );
  2356. patch->plane->type = PlaneTypeForNormal( patch->plane->normal );
  2357. patch->planeDist = patch->plane->dist;
  2358. patch->faceNumber = -1; // This is a bit hacky and is used to identify static prop patches in other parts of the code
  2359. WindingCenter( w, patch->origin );
  2360. VectorCopy( patch->plane->normal, patch->normal );
  2361. WindingBounds( w, patch->face_mins, patch->face_maxs );
  2362. VectorCopy( patch->face_mins, patch->mins );
  2363. VectorCopy( patch->face_maxs, patch->maxs );
  2364. patch->baselight.Init( 0.0f, 0.0f, 0.0f );
  2365. patch->basearea = 1;
  2366. patch->reflectivity = vRefl;
  2367. }
  2368. void CVradStaticPropMgr::MakePatches()
  2369. {
  2370. int count = m_StaticProps.Count();
  2371. if ( !count )
  2372. {
  2373. // nothing to do
  2374. return;
  2375. }
  2376. // Triangle coverage of 1 (full coverage)
  2377. Vector fullCoverage;
  2378. fullCoverage.x = 1.0f;
  2379. int nPatchCount = 0;
  2380. //IScratchPad3D *pPad = ScratchPad3D_Create();
  2381. //pPad->SetAutoFlush( false );
  2382. for ( int nProp = 0; nProp < count; ++nProp )
  2383. {
  2384. CStaticProp &prop = m_StaticProps[ nProp ];
  2385. if ( ( prop.m_FlagsEx & STATIC_PROP_FLAGS_EX_ENABLE_LIGHT_BOUNCE ) == 0 )
  2386. {
  2387. continue;
  2388. }
  2389. StaticPropDict_t &dict = m_StaticPropDict[ prop.m_ModelIdx ];
  2390. if ( dict.m_pModel )
  2391. {
  2392. // Get material, get reflectivity
  2393. VMatrix xform;
  2394. xform.SetupMatrixOrgAngles( prop.m_Origin, prop.m_Angles );
  2395. ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
  2396. for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
  2397. {
  2398. for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
  2399. {
  2400. Vector verts[ 3 ];
  2401. queryModel->GetTriangleVerts( nConvex, nTri, verts );
  2402. for ( int nVert = 0; nVert < 3; ++nVert )
  2403. verts[ nVert ] = xform.VMul4x3( verts[ nVert ] );
  2404. //pPad->DrawPolygon( CSPVertList( verts, 3, CSPColor( prop.m_vReflectivity ) ) );
  2405. //pPad->DrawLine( CSPVert( g_Patches.Tail().origin ), CSPVert( g_Patches.Tail().origin + 5.0f * g_Patches.Tail().normal) );
  2406. winding_t *w = AllocWinding( 3 );
  2407. for ( int i = 0; i < 3; i++ )
  2408. {
  2409. w->p[ i ] = verts[ i ];
  2410. }
  2411. w->numpoints = 3;
  2412. MakePatchForTriangle( w, prop.m_vReflectivity, nProp );
  2413. //pPad->DrawPolygon( CSPVertList( verts, 3 ) );
  2414. //pPad->DrawLine( CSPVert( g_Patches.Tail().origin ), CSPVert( g_Patches.Tail().origin + 5.0f * g_Patches.Tail().normal) );
  2415. g_RtEnv_RadiosityPatches.AddTriangle( TRACE_ID_PATCH | (g_Patches.Count() - 1), verts[ 0 ], verts[ 1 ], verts[ 2 ], Vector( 1.0f, 1.0f, 1.0f ) );
  2416. nPatchCount++;
  2417. }
  2418. }
  2419. s_pPhysCollision->DestroyQueryModel( queryModel );
  2420. }
  2421. else
  2422. {
  2423. // FIXME
  2424. #if 0
  2425. VectorAdd( dict.m_Mins, prop.m_Origin, prop.m_mins );
  2426. VectorAdd( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
  2427. g_RtEnv.AddAxisAlignedRectangularSolid( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
  2428. #endif
  2429. }
  2430. }
  2431. //pPad->Release();
  2432. g_RtEnv_RadiosityPatches.SetupAccelerationStructure();
  2433. qprintf( "%i static prop patches\n", nPatchCount );
  2434. }