Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2694 lines
88 KiB

  1. //========= Copyright 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 "materialsystem/hardwaretexels.h"
  30. #include "byteswap.h"
  31. #include "mpivrad.h"
  32. #include "vtf/vtf.h"
  33. #include "tier1/utldict.h"
  34. #include "tier1/utlsymbol.h"
  35. #include "bitmap/tgawriter.h"
  36. #include "messbuf.h"
  37. #include "vmpi.h"
  38. #include "vmpi_distribute_work.h"
  39. #define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
  40. // identifies a vertex embedded in solid
  41. // lighting will be copied from nearest valid neighbor
  42. struct badVertex_t
  43. {
  44. int m_ColorVertex;
  45. Vector m_Position;
  46. Vector m_Normal;
  47. };
  48. // a final colored vertex
  49. struct colorVertex_t
  50. {
  51. Vector m_Color;
  52. Vector m_Position;
  53. bool m_bValid;
  54. };
  55. // a texel suitable for a model
  56. struct colorTexel_t
  57. {
  58. Vector m_Color;
  59. Vector m_WorldPosition;
  60. Vector m_WorldNormal;
  61. float m_fDistanceToTri; // If we are outside of the triangle, how far away is it?
  62. bool m_bValid;
  63. bool m_bPossiblyInteresting;
  64. };
  65. class CComputeStaticPropLightingResults
  66. {
  67. public:
  68. ~CComputeStaticPropLightingResults()
  69. {
  70. m_ColorVertsArrays.PurgeAndDeleteElements();
  71. m_ColorTexelsArrays.PurgeAndDeleteElements();
  72. }
  73. CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
  74. CUtlVector< CUtlVector<colorTexel_t>* > m_ColorTexelsArrays;
  75. };
  76. //-----------------------------------------------------------------------------
  77. struct Rasterizer
  78. {
  79. struct Location
  80. {
  81. Vector barycentric;
  82. Vector2D uv;
  83. bool insideTriangle;
  84. };
  85. Rasterizer(Vector2D t0, Vector2D t1, Vector2D t2, size_t resX, size_t resY)
  86. : mT0(t0)
  87. , mT1(t1)
  88. , mT2(t2)
  89. , mResX(resX)
  90. , mResY(resY)
  91. , mUvStepX(1.0f / resX)
  92. , mUvStepY(1.0f / resY)
  93. {
  94. Build();
  95. }
  96. CUtlVector< Location >::iterator begin() { return mRasterizedLocations.begin(); }
  97. CUtlVector< Location >::iterator end() { return mRasterizedLocations.end(); }
  98. void Build();
  99. inline size_t GetRow(float y) const { return size_t(y * mResY); }
  100. inline size_t GetCol(float x) const { return size_t(x * mResX); }
  101. inline size_t GetLinearPos( const CUtlVector< Location >::iterator& it ) const
  102. {
  103. // Given an iterator, return what the linear position in the buffer would be for the data.
  104. return (size_t)(GetRow(it->uv.y) * mResX)
  105. + (size_t)(GetCol(it->uv.x));
  106. }
  107. private:
  108. const Vector2D mT0, mT1, mT2;
  109. const size_t mResX, mResY;
  110. const float mUvStepX, mUvStepY;
  111. // Right now, we just fill this out and directly iterate over it.
  112. // It could be large. This is a memory/speed tradeoff. We could instead generate them
  113. // on demand.
  114. CUtlVector< Location > mRasterizedLocations;
  115. };
  116. //-----------------------------------------------------------------------------
  117. inline Vector ComputeBarycentric( Vector2D _edgeC, Vector2D _edgeA, Vector2D _edgeB, float _dAA, float _dAB, float _dBB, float _invDenom )
  118. {
  119. float dCA = _edgeC.Dot(_edgeA);
  120. float dCB = _edgeC.Dot(_edgeB);
  121. Vector retVal;
  122. retVal.y = (_dBB * dCA - _dAB * dCB) * _invDenom;
  123. retVal.z = (_dAA * dCB - _dAB * dCA) * _invDenom;
  124. retVal.x = 1.0f - retVal.y - retVal.z;
  125. return retVal;
  126. }
  127. //-----------------------------------------------------------------------------
  128. void Rasterizer::Build()
  129. {
  130. // For now, use the barycentric method. It's easy, I'm lazy.
  131. // We can optimize later if it's a performance issue.
  132. const float baseX = mUvStepX / 2.0f;
  133. const float baseY = mUvStepY / 2.0f;
  134. float fMinX = min(min(mT0.x, mT1.x), mT2.x);
  135. float fMinY = min(min(mT0.y, mT1.y), mT2.y);
  136. float fMaxX = max(max(mT0.x, mT1.x), mT2.x);
  137. float fMaxY = max(max(mT0.y, mT1.y), mT2.y);
  138. // Degenerate. Consider warning about these, but otherwise no problem.
  139. if (fMinX == fMaxX || fMinY == fMaxY)
  140. return;
  141. // Clamp to 0..1
  142. fMinX = max(0, fMinX);
  143. fMinY = max(0, fMinY);
  144. fMaxX = min(1.0f, fMaxX);
  145. fMaxY = min(1.0f, fMaxY);
  146. // We puff the interesting area up by 1 so we can hit an inflated region for the necessary bilerp data.
  147. // If we wanted to support better texturing (almost definitely unnecessary), we'd change this to a larger size.
  148. const int kFilterSampleRadius = 1;
  149. int iMinX = GetCol(fMinX) - kFilterSampleRadius;
  150. int iMinY = GetRow(fMinY) - kFilterSampleRadius;
  151. int iMaxX = GetCol(fMaxX) + 1 + kFilterSampleRadius;
  152. int iMaxY = GetRow(fMaxY) + 1 + kFilterSampleRadius;
  153. // Clamp to valid texture (integer) locations
  154. iMinX = max(0, iMinX);
  155. iMinY = max(0, iMinY);
  156. iMaxX = min(iMaxX, mResX - 1);
  157. iMaxY = min(iMaxY, mResY - 1);
  158. // Set the size to be as expected.
  159. // TODO: Pass this in from outside to minimize allocations
  160. int count = (iMaxY - iMinY + 1)
  161. * (iMaxX - iMinX + 1);
  162. mRasterizedLocations.EnsureCount(count);
  163. memset( mRasterizedLocations.Base(), 0, mRasterizedLocations.Count() * sizeof( Location ) );
  164. // Computing Barycentrics adapted from here http://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates
  165. Vector2D edgeA = mT1 - mT0;
  166. Vector2D edgeB = mT2 - mT0;
  167. float dAA = edgeA.Dot(edgeA);
  168. float dAB = edgeA.Dot(edgeB);
  169. float dBB = edgeB.Dot(edgeB);
  170. float invDenom = 1.0f / (dAA * dBB - dAB * dAB);
  171. int linearPos = 0;
  172. for (int j = iMinY; j <= iMaxY; ++j) {
  173. for (int i = iMinX; i <= iMaxX; ++i) {
  174. Vector2D testPt( i * mUvStepX + baseX, j * mUvStepY + baseY );
  175. Vector barycentric = ComputeBarycentric( testPt - mT0, edgeA, edgeB, dAA, dAB, dBB, invDenom );
  176. // Test whether the point is inside the triangle.
  177. // MCJOHNTODO: Edge rules and whatnot--right now we re-rasterize points on the edge.
  178. Location& newLoc = mRasterizedLocations[linearPos++];
  179. newLoc.barycentric = barycentric;
  180. newLoc.uv = testPt;
  181. newLoc.insideTriangle = (barycentric.x >= 0.0f && barycentric.x <= 1.0f && barycentric.y >= 0.0f && barycentric.y <= 1.0f && barycentric.z >= 0.0f && barycentric.z <= 1.0f);
  182. }
  183. }
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Globals
  187. //-----------------------------------------------------------------------------
  188. CUtlSymbolTable g_ForcedTextureShadowsModels;
  189. // DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
  190. // INSIDE PropTested_t. USE THAT INSTEAD.
  191. IPhysicsCollision *s_pPhysCollision = NULL;
  192. static void ConvertTexelDataToTexture(unsigned int _resX, unsigned int _resY, ImageFormat _destFmt, const CUtlVector<colorTexel_t>& _srcTexels, CUtlMemory<byte>* _outTexture);
  193. // Such a monstrosity. :(
  194. static void GenerateLightmapSamplesForMesh( const matrix3x4_t& _matPos, const matrix3x4_t& _matNormal, int _iThread, int _skipProp, int _nFlags, int _lightmapResX, int _lightmapResY,
  195. studiohdr_t* _pStudioHdr, mstudiomodel_t* _pStudioModel, OptimizedModel::ModelHeader_t* _pVtxModel, int _meshID,
  196. CComputeStaticPropLightingResults *_pResults );
  197. // Debug function, converts lightmaps to linear space then dumps them out.
  198. // TODO: Write out the file in a .dds instead of a .tga, in whatever format we're supposed to use.
  199. static void DumpLightmapLinear( const char* _dstFilename, const CUtlVector<colorTexel_t>& _srcTexels, int _width, int _height );
  200. //-----------------------------------------------------------------------------
  201. // Vrad's static prop manager
  202. //-----------------------------------------------------------------------------
  203. class CVradStaticPropMgr : public IVradStaticPropMgr
  204. {
  205. public:
  206. // constructor, destructor
  207. CVradStaticPropMgr();
  208. virtual ~CVradStaticPropMgr();
  209. // methods of IStaticPropMgr
  210. void Init();
  211. void Shutdown();
  212. // iterate all the instanced static props and compute their vertex lighting
  213. void ComputeLighting( int iThread );
  214. private:
  215. // VMPI stuff.
  216. static void VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf );
  217. static void VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker );
  218. void VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf );
  219. void VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker );
  220. // local thread version
  221. static void ThreadComputeStaticPropLighting( int iThread, void *pUserData );
  222. void ComputeLightingForProp( int iThread, int iStaticProp );
  223. // Methods associated with unserializing static props
  224. void UnserializeModelDict( CUtlBuffer& buf );
  225. void UnserializeModels( CUtlBuffer& buf );
  226. void UnserializeStaticProps();
  227. // Creates a collision model
  228. void CreateCollisionModel( char const* pModelName );
  229. private:
  230. // Unique static prop models
  231. struct StaticPropDict_t
  232. {
  233. vcollide_t m_loadedModel;
  234. CPhysCollide* m_pModel;
  235. Vector m_Mins; // Bounding box is in local coordinates
  236. Vector m_Maxs;
  237. studiohdr_t* m_pStudioHdr;
  238. CUtlBuffer m_VtxBuf;
  239. CUtlVector<int> m_textureShadowIndex; // each texture has an index if this model casts texture shadows
  240. CUtlVector<int> m_triangleMaterialIndex;// each triangle has an index if this model casts texture shadows
  241. };
  242. struct MeshData_t
  243. {
  244. CUtlVector<Vector> m_VertexColors;
  245. CUtlMemory<byte> m_TexelsEncoded;
  246. int m_nLod;
  247. };
  248. // A static prop instance
  249. struct CStaticProp
  250. {
  251. Vector m_Origin;
  252. QAngle m_Angles;
  253. Vector m_mins;
  254. Vector m_maxs;
  255. Vector m_LightingOrigin;
  256. int m_ModelIdx;
  257. BSPTreeDataHandle_t m_Handle;
  258. CUtlVector<MeshData_t> m_MeshData;
  259. int m_Flags;
  260. bool m_bLightingOriginValid;
  261. // Note that all lightmaps for a given prop share the same resolution (and format)--and there can be multiple lightmaps
  262. // per prop (if there are multiple pieces--the watercooler is an example).
  263. // This is effectively because there's not a good way in hammer for a prop to say "this should be the resolution
  264. // of each of my sub-pieces."
  265. ImageFormat m_LightmapImageFormat;
  266. unsigned int m_LightmapImageWidth;
  267. unsigned int m_LightmapImageHeight;
  268. };
  269. // Enumeration context
  270. struct EnumContext_t
  271. {
  272. PropTested_t* m_pPropTested;
  273. Ray_t const* m_pRay;
  274. };
  275. // The list of all static props
  276. CUtlVector <StaticPropDict_t> m_StaticPropDict;
  277. CUtlVector <CStaticProp> m_StaticProps;
  278. bool m_bIgnoreStaticPropTrace;
  279. void ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults );
  280. void ApplyLightingToStaticProp( int iStaticProp, CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
  281. void SerializeLighting();
  282. void AddPolysForRayTrace();
  283. void BuildTriList( CStaticProp &prop );
  284. };
  285. //-----------------------------------------------------------------------------
  286. // Expose IVradStaticPropMgr to vrad
  287. //-----------------------------------------------------------------------------
  288. static CVradStaticPropMgr g_StaticPropMgr;
  289. IVradStaticPropMgr* StaticPropMgr()
  290. {
  291. return &g_StaticPropMgr;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // constructor, destructor
  295. //-----------------------------------------------------------------------------
  296. CVradStaticPropMgr::CVradStaticPropMgr()
  297. {
  298. // set to ignore static prop traces
  299. m_bIgnoreStaticPropTrace = false;
  300. }
  301. CVradStaticPropMgr::~CVradStaticPropMgr()
  302. {
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Makes sure the studio model is a static prop
  306. //-----------------------------------------------------------------------------
  307. bool IsStaticProp( studiohdr_t* pHdr )
  308. {
  309. if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
  310. return false;
  311. return true;
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Load a file into a Utlbuf
  315. //-----------------------------------------------------------------------------
  316. static bool LoadFile( char const* pFileName, CUtlBuffer& buf )
  317. {
  318. if ( !g_pFullFileSystem )
  319. return false;
  320. return g_pFullFileSystem->ReadFile( pFileName, NULL, buf );
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Constructs the file name from the model name
  324. //-----------------------------------------------------------------------------
  325. static char const* ConstructFileName( char const* pModelName )
  326. {
  327. static char buf[1024];
  328. sprintf( buf, "%s%s", gamedir, pModelName );
  329. return buf;
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Computes a convex hull from a studio mesh
  333. //-----------------------------------------------------------------------------
  334. static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh, studiohdr_t *pStudioHdr )
  335. {
  336. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  337. Assert( vertData ); // This can only return NULL on X360 for now
  338. // Generate a list of all verts in the mesh
  339. Vector** ppVerts = (Vector**)_alloca(pMesh->numvertices * sizeof(Vector*) );
  340. for (int i = 0; i < pMesh->numvertices; ++i)
  341. {
  342. ppVerts[i] = vertData->Position(i);
  343. }
  344. // Generate a convex hull from the verts
  345. return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Computes a convex hull from the studio model
  349. //-----------------------------------------------------------------------------
  350. CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
  351. {
  352. CUtlVector<CPhysConvex*> convexHulls;
  353. for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
  354. {
  355. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
  356. for( int model = 0; model < pBodyPart->nummodels; ++model )
  357. {
  358. mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
  359. for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
  360. {
  361. // Make a convex hull for each mesh
  362. // NOTE: This won't work unless the model has been compiled
  363. // with $staticprop
  364. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
  365. convexHulls.AddToTail( ComputeConvexHull( pStudioMesh, pStudioHdr ) );
  366. }
  367. }
  368. }
  369. // Convert an array of convex elements to a compiled collision model
  370. // (this deletes the convex elements)
  371. return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Load studio model vertex data from a file...
  375. //-----------------------------------------------------------------------------
  376. bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf )
  377. {
  378. // No luck, gotta build it
  379. // Construct the file name...
  380. if (!LoadFile( pModelName, buf ))
  381. {
  382. Warning("Error! Unable to load model \"%s\"\n", pModelName );
  383. return false;
  384. }
  385. // Check that it's valid
  386. if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
  387. strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
  388. {
  389. Warning("Error! Invalid model file \"%s\"\n", pModelName );
  390. return false;
  391. }
  392. studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
  393. Studio_ConvertStudioHdrToNewVersion( pHdr );
  394. if (pHdr->version != STUDIO_VERSION)
  395. {
  396. Warning("Error! Invalid model version \"%s\"\n", pModelName );
  397. return false;
  398. }
  399. if (!IsStaticProp(pHdr))
  400. {
  401. Warning("Error! To use model \"%s\"\n"
  402. " as a static prop, it must be compiled with $staticprop!\n", pModelName );
  403. return false;
  404. }
  405. // ensure reset
  406. pHdr->pVertexBase = NULL;
  407. pHdr->pIndexBase = NULL;
  408. return true;
  409. }
  410. bool LoadStudioCollisionModel( char const* pModelName, CUtlBuffer& buf )
  411. {
  412. char tmp[1024];
  413. Q_strncpy( tmp, pModelName, sizeof( tmp ) );
  414. Q_SetExtension( tmp, ".phy", sizeof( tmp ) );
  415. // No luck, gotta build it
  416. if (!LoadFile( tmp, buf ))
  417. {
  418. // this is not an error, the model simply has no PHY file
  419. return false;
  420. }
  421. phyheader_t *header = (phyheader_t *)buf.PeekGet();
  422. if ( header->size != sizeof(*header) || header->solidCount <= 0 )
  423. return false;
  424. return true;
  425. }
  426. bool LoadVTXFile( char const* pModelName, const studiohdr_t *pStudioHdr, CUtlBuffer& buf )
  427. {
  428. char filename[MAX_PATH];
  429. // construct filename
  430. Q_StripExtension( pModelName, filename, sizeof( filename ) );
  431. strcat( filename, ".dx80.vtx" );
  432. if ( !LoadFile( filename, buf ) )
  433. {
  434. Warning( "Error! Unable to load file \"%s\"\n", filename );
  435. return false;
  436. }
  437. OptimizedModel::FileHeader_t* pVtxHdr = (OptimizedModel::FileHeader_t *)buf.Base();
  438. // Check that it's valid
  439. if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
  440. {
  441. Warning( "Error! Invalid VTX file version: %d, expected %d \"%s\"\n", pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION, filename );
  442. return false;
  443. }
  444. if ( pVtxHdr->checkSum != pStudioHdr->checksum )
  445. {
  446. Warning( "Error! Invalid VTX file checksum: %d, expected %d \"%s\"\n", pVtxHdr->checkSum, pStudioHdr->checksum, filename );
  447. return false;
  448. }
  449. return true;
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Gets a vertex position from a strip index
  453. //-----------------------------------------------------------------------------
  454. inline static Vector* PositionFromIndex( const mstudio_meshvertexdata_t *vertData, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup, int i )
  455. {
  456. OptimizedModel::Vertex_t* pVert = pStripGroup->pVertex( i );
  457. return vertData->Position( pVert->origMeshVertID );
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: Writes a glview text file containing the collision surface in question
  461. // Input : *pCollide -
  462. // *pFilename -
  463. //-----------------------------------------------------------------------------
  464. void DumpCollideToGlView( vcollide_t *pCollide, const char *pFilename )
  465. {
  466. if ( !pCollide )
  467. return;
  468. Msg("Writing %s...\n", pFilename );
  469. FILE *fp = fopen( pFilename, "w" );
  470. for (int i = 0; i < pCollide->solidCount; ++i)
  471. {
  472. Vector *outVerts;
  473. int vertCount = s_pPhysCollision->CreateDebugMesh( pCollide->solids[i], &outVerts );
  474. int triCount = vertCount / 3;
  475. int vert = 0;
  476. unsigned char r = (i & 1) * 64 + 64;
  477. unsigned char g = (i & 2) * 64 + 64;
  478. unsigned char b = (i & 4) * 64 + 64;
  479. float fr = r / 255.0f;
  480. float fg = g / 255.0f;
  481. float fb = b / 255.0f;
  482. for ( int i = 0; i < triCount; i++ )
  483. {
  484. fprintf( fp, "3\n" );
  485. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  486. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  487. vert++;
  488. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  489. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  490. vert++;
  491. fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
  492. outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
  493. vert++;
  494. }
  495. s_pPhysCollision->DestroyDebugMesh( vertCount, outVerts );
  496. }
  497. fclose( fp );
  498. }
  499. static bool PointInTriangle( const Vector2D &p, const Vector2D &v0, const Vector2D &v1, const Vector2D &v2 )
  500. {
  501. float coords[3];
  502. GetBarycentricCoords2D( v0, v1, v2, p, coords );
  503. for ( int i = 0; i < 3; i++ )
  504. {
  505. if ( coords[i] < 0.0f || coords[i] > 1.0f )
  506. return false;
  507. }
  508. float sum = coords[0] + coords[1] + coords[2];
  509. if ( sum > 1.0f )
  510. return false;
  511. return true;
  512. }
  513. bool LoadFileIntoBuffer( CUtlBuffer &buf, const char *pFilename )
  514. {
  515. FileHandle_t fileHandle = g_pFileSystem->Open( pFilename, "rb" );
  516. if ( !fileHandle )
  517. return false;
  518. // Get the file size
  519. int texSize = g_pFileSystem->Size( fileHandle );
  520. buf.EnsureCapacity( texSize );
  521. int nBytesRead = g_pFileSystem->Read( buf.Base(), texSize, fileHandle );
  522. g_pFileSystem->Close( fileHandle );
  523. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  524. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  525. return true;
  526. }
  527. // keeps a list of all textures that cast shadows via alpha channel
  528. class CShadowTextureList
  529. {
  530. public:
  531. // This loads a vtf and converts it to RGB8888 format
  532. unsigned char *LoadVTFRGB8888( const char *pName, int *pWidth, int *pHeight, bool *pClampU, bool *pClampV )
  533. {
  534. char szPath[MAX_PATH];
  535. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  536. Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  537. Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
  538. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  539. CUtlBuffer buf;
  540. if ( !LoadFileIntoBuffer( buf, szPath ) )
  541. return NULL;
  542. IVTFTexture *pTex = CreateVTFTexture();
  543. if (!pTex->Unserialize( buf ))
  544. return NULL;
  545. Msg("Loaded alpha texture %s\n", szPath );
  546. unsigned char *pSrcImage = pTex->ImageData( 0, 0, 0, 0, 0, 0 );
  547. int iWidth = pTex->Width();
  548. int iHeight = pTex->Height();
  549. ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888;
  550. ImageFormat srcFormat = pTex->Format();
  551. *pClampU = (pTex->Flags() & TEXTUREFLAGS_CLAMPS) ? true : false;
  552. *pClampV = (pTex->Flags() & TEXTUREFLAGS_CLAMPT) ? true : false;
  553. unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
  554. if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
  555. pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
  556. {
  557. delete[] pDstImage;
  558. return NULL;
  559. }
  560. *pWidth = iWidth;
  561. *pHeight = iHeight;
  562. return pDstImage;
  563. }
  564. // Checks the database for the material and loads if necessary
  565. // returns true if found and pIndex will be the index, -1 if no alpha shadows
  566. bool FindOrLoadIfValid( const char *pMaterialName, int *pIndex )
  567. {
  568. *pIndex = -1;
  569. int index = m_Textures.Find(pMaterialName);
  570. bool bFound = false;
  571. if ( index != m_Textures.InvalidIndex() )
  572. {
  573. bFound = true;
  574. *pIndex = index;
  575. }
  576. else
  577. {
  578. KeyValues *pVMT = new KeyValues("vmt");
  579. CUtlBuffer buf(0,0,CUtlBuffer::TEXT_BUFFER);
  580. LoadFileIntoBuffer( buf, pMaterialName );
  581. if ( pVMT->LoadFromBuffer( pMaterialName, buf ) )
  582. {
  583. bFound = true;
  584. if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") )
  585. {
  586. KeyValues *pBaseTexture = pVMT->FindKey("$basetexture");
  587. if ( pBaseTexture )
  588. {
  589. const char *pBaseTextureName = pBaseTexture->GetString();
  590. if ( pBaseTextureName )
  591. {
  592. int w, h;
  593. bool bClampU = false;
  594. bool bClampV = false;
  595. unsigned char *pImageBits = LoadVTFRGB8888( pBaseTextureName, &w, &h, &bClampU, &bClampV );
  596. if ( pImageBits )
  597. {
  598. int index = m_Textures.Insert( pMaterialName );
  599. m_Textures[index].InitFromRGB8888( w, h, pImageBits );
  600. *pIndex = index;
  601. if ( pVMT->FindKey("$nocull") )
  602. {
  603. // UNDONE: Support this? Do we need to emit two triangles?
  604. m_Textures[index].allowBackface = true;
  605. }
  606. m_Textures[index].clampU = bClampU;
  607. m_Textures[index].clampV = bClampV;
  608. delete[] pImageBits;
  609. }
  610. }
  611. }
  612. }
  613. }
  614. pVMT->deleteThis();
  615. }
  616. return bFound;
  617. }
  618. // iterate the textures for the model and load each one into the database
  619. // this is used on models marked to cast texture shadows
  620. void LoadAllTexturesForModel( studiohdr_t *pHdr, int *pTextureList )
  621. {
  622. for ( int i = 0; i < pHdr->numtextures; i++ )
  623. {
  624. int textureIndex = -1;
  625. // try to add each texture to the transparent shadow manager
  626. char szPath[MAX_PATH];
  627. // iterate quietly through all specified directories until a valid material is found
  628. for ( int j = 0; j < pHdr->numcdtextures; j++ )
  629. {
  630. Q_strncpy( szPath, "materials/", sizeof( szPath ) );
  631. Q_strncat( szPath, pHdr->pCdtexture( j ), sizeof( szPath ) );
  632. const char *textureName = pHdr->pTexture( i )->pszName();
  633. Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
  634. Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
  635. Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
  636. if ( FindOrLoadIfValid( szPath, &textureIndex ) )
  637. break;
  638. }
  639. pTextureList[i] = textureIndex;
  640. }
  641. }
  642. int AddMaterialEntry( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  643. {
  644. int index = m_MaterialEntries.AddToTail();
  645. m_MaterialEntries[index].textureIndex = shadowTextureIndex;
  646. m_MaterialEntries[index].uv[0] = t0;
  647. m_MaterialEntries[index].uv[1] = t1;
  648. m_MaterialEntries[index].uv[2] = t2;
  649. return index;
  650. }
  651. // HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
  652. float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
  653. {
  654. float umin = min(t0.x, t1.x);
  655. umin = min(umin, t2.x);
  656. float umax = max(t0.x, t1.x);
  657. umax = max(umax, t2.x);
  658. float vmin = min(t0.y, t1.y);
  659. vmin = min(vmin, t2.y);
  660. float vmax = max(t0.y, t1.y);
  661. vmax = max(vmax, t2.y);
  662. // UNDONE: Do something about tiling
  663. umin = clamp(umin, 0, 1);
  664. umax = clamp(umax, 0, 1);
  665. vmin = clamp(vmin, 0, 1);
  666. vmax = clamp(vmax, 0, 1);
  667. Assert(umin>=0.0f && umax <= 1.0f);
  668. Assert(vmin>=0.0f && vmax <= 1.0f);
  669. const alphatexture_t &tex = m_Textures.Element(shadowTextureIndex);
  670. int u0 = umin * (tex.width-1);
  671. int u1 = umax * (tex.width-1);
  672. int v0 = vmin * (tex.height-1);
  673. int v1 = vmax * (tex.height-1);
  674. int total = 0;
  675. int count = 0;
  676. for ( int v = v0; v <= v1; v++ )
  677. {
  678. int row = (v * tex.width);
  679. for ( int u = u0; u <= u1; u++ )
  680. {
  681. total += tex.pAlphaTexels[row + u];
  682. count++;
  683. }
  684. }
  685. if ( count )
  686. {
  687. float coverage = float(total) / (count * 255.0f);
  688. return coverage;
  689. }
  690. return 1.0f;
  691. }
  692. int SampleMaterial( int materialIndex, const Vector &coords, bool bBackface )
  693. {
  694. const materialentry_t &mat = m_MaterialEntries[materialIndex];
  695. const alphatexture_t &tex = m_Textures.Element(m_MaterialEntries[materialIndex].textureIndex);
  696. if ( bBackface && !tex.allowBackface )
  697. return 0;
  698. Vector2D uv = coords.x * mat.uv[0] + coords.y * mat.uv[1] + coords.z * mat.uv[2];
  699. int u = RoundFloatToInt( uv[0] * tex.width );
  700. int v = RoundFloatToInt( uv[1] * tex.height );
  701. // asume power of 2, clamp or wrap
  702. // UNDONE: Support clamp? This code should work
  703. #if 0
  704. u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1));
  705. v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1));
  706. #else
  707. // for now always wrap
  708. u &= (tex.width-1);
  709. v &= (tex.height-1);
  710. #endif
  711. return tex.pAlphaTexels[v * tex.width + u];
  712. }
  713. struct alphatexture_t
  714. {
  715. short width;
  716. short height;
  717. bool allowBackface;
  718. bool clampU;
  719. bool clampV;
  720. unsigned char *pAlphaTexels;
  721. void InitFromRGB8888( int w, int h, unsigned char *pTexels )
  722. {
  723. width = w;
  724. height = h;
  725. pAlphaTexels = new unsigned char[w*h];
  726. for ( int i = 0; i < h; i++ )
  727. {
  728. for ( int j = 0; j < w; j++ )
  729. {
  730. int index = (i*w) + j;
  731. pAlphaTexels[index] = pTexels[index*4 + 3];
  732. }
  733. }
  734. }
  735. };
  736. struct materialentry_t
  737. {
  738. int textureIndex;
  739. Vector2D uv[3];
  740. };
  741. // this is the list of textures we've loaded
  742. // only load each one once
  743. CUtlDict< alphatexture_t, unsigned short > m_Textures;
  744. CUtlVector<materialentry_t> m_MaterialEntries;
  745. };
  746. // global to keep the shadow-casting texture list and their alpha bits
  747. CShadowTextureList g_ShadowTextureList;
  748. float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID )
  749. {
  750. const float alphaScale = 1.0f / 255.0f;
  751. // UNDONE: Pass ray down to determine backfacing?
  752. //Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
  753. //bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
  754. Vector coords(b0,b1,b2);
  755. return alphaScale * g_ShadowTextureList.SampleMaterial( g_RtEnv.GetTriangleMaterial(hitID), coords, false );
  756. }
  757. // this is here to strip models/ or .mdl or whatnot
  758. void CleanModelName( const char *pModelName, char *pOutput, int outLen )
  759. {
  760. // strip off leading models/ if it exists
  761. const char *pModelDir = "models/";
  762. int modelLen = Q_strlen(pModelDir);
  763. if ( !Q_strnicmp(pModelName, pModelDir, modelLen ) )
  764. {
  765. pModelName += modelLen;
  766. }
  767. Q_strncpy( pOutput, pModelName, outLen );
  768. // truncate any .mdl extension
  769. char *dot = strchr(pOutput,'.');
  770. if ( dot )
  771. {
  772. *dot = 0;
  773. }
  774. }
  775. void ForceTextureShadowsOnModel( const char *pModelName )
  776. {
  777. char buf[1024];
  778. CleanModelName( pModelName, buf, sizeof(buf) );
  779. if ( !g_ForcedTextureShadowsModels.Find(buf).IsValid())
  780. {
  781. g_ForcedTextureShadowsModels.AddString(buf);
  782. }
  783. }
  784. bool IsModelTextureShadowsForced( const char *pModelName )
  785. {
  786. char buf[1024];
  787. CleanModelName( pModelName, buf, sizeof(buf) );
  788. return g_ForcedTextureShadowsModels.Find(buf).IsValid();
  789. }
  790. //-----------------------------------------------------------------------------
  791. // Creates a collision model (based on the render geometry!)
  792. //-----------------------------------------------------------------------------
  793. void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName )
  794. {
  795. CUtlBuffer buf;
  796. CUtlBuffer bufvtx;
  797. CUtlBuffer bufphy;
  798. int i = m_StaticPropDict.AddToTail();
  799. m_StaticPropDict[i].m_pModel = NULL;
  800. m_StaticPropDict[i].m_pStudioHdr = NULL;
  801. if ( !LoadStudioModel( pModelName, buf ) )
  802. {
  803. VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins );
  804. VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs );
  805. return;
  806. }
  807. studiohdr_t* pHdr = (studiohdr_t*)buf.Base();
  808. VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins );
  809. VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs );
  810. if ( LoadStudioCollisionModel( pModelName, bufphy ) )
  811. {
  812. phyheader_t header;
  813. bufphy.Get( &header, sizeof(header) );
  814. vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel;
  815. s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() );
  816. m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0];
  817. /*
  818. static int propNum = 0;
  819. char tmp[128];
  820. sprintf( tmp, "staticprop%03d.txt", propNum );
  821. DumpCollideToGlView( pCollide, tmp );
  822. ++propNum;
  823. */
  824. }
  825. else
  826. {
  827. // mark this as unused
  828. m_StaticPropDict[i].m_loadedModel.solidCount = 0;
  829. // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
  830. m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr );
  831. }
  832. // clone it
  833. m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() );
  834. memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() );
  835. if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) )
  836. {
  837. // failed, leave state identified as disabled
  838. m_StaticPropDict[i].m_VtxBuf.Purge();
  839. }
  840. if ( g_bTextureShadows )
  841. {
  842. if ( (pHdr->flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS) || IsModelTextureShadowsForced(pModelName) )
  843. {
  844. m_StaticPropDict[i].m_textureShadowIndex.RemoveAll();
  845. m_StaticPropDict[i].m_triangleMaterialIndex.RemoveAll();
  846. m_StaticPropDict[i].m_textureShadowIndex.AddMultipleToTail( pHdr->numtextures );
  847. g_ShadowTextureList.LoadAllTexturesForModel( pHdr, m_StaticPropDict[i].m_textureShadowIndex.Base() );
  848. }
  849. }
  850. }
  851. //-----------------------------------------------------------------------------
  852. // Unserialize static prop model dictionary
  853. //-----------------------------------------------------------------------------
  854. void CVradStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
  855. {
  856. int count = buf.GetInt();
  857. while ( --count >= 0 )
  858. {
  859. StaticPropDictLump_t lump;
  860. buf.Get( &lump, sizeof(StaticPropDictLump_t) );
  861. CreateCollisionModel( lump.m_Name );
  862. }
  863. }
  864. void CVradStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
  865. {
  866. int count = buf.GetInt();
  867. m_StaticProps.AddMultipleToTail(count);
  868. for ( int i = 0; i < count; ++i )
  869. {
  870. StaticPropLump_t lump;
  871. buf.Get( &lump, sizeof(StaticPropLump_t) );
  872. VectorCopy( lump.m_Origin, m_StaticProps[i].m_Origin );
  873. VectorCopy( lump.m_Angles, m_StaticProps[i].m_Angles );
  874. VectorCopy( lump.m_LightingOrigin, m_StaticProps[i].m_LightingOrigin );
  875. m_StaticProps[i].m_bLightingOriginValid = ( lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0;
  876. m_StaticProps[i].m_ModelIdx = lump.m_PropType;
  877. m_StaticProps[i].m_Handle = TREEDATA_INVALID_HANDLE;
  878. m_StaticProps[i].m_Flags = lump.m_Flags;
  879. // Changed this from using DXT1 to RGB888 because the compression artifacts were pretty nasty.
  880. // TODO: Consider changing back or basing this on user selection in hammer.
  881. m_StaticProps[i].m_LightmapImageFormat = IMAGE_FORMAT_RGB888;
  882. m_StaticProps[i].m_LightmapImageWidth = lump.m_nLightmapResolutionX;
  883. m_StaticProps[i].m_LightmapImageHeight = lump.m_nLightmapResolutionY;
  884. }
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Unserialize static props
  888. //-----------------------------------------------------------------------------
  889. void CVradStaticPropMgr::UnserializeStaticProps()
  890. {
  891. // Unserialize static props, insert them into the appropriate leaves
  892. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
  893. int size = g_GameLumps.GameLumpSize( handle );
  894. if (!size)
  895. return;
  896. if ( g_GameLumps.GetGameLumpVersion( handle ) != GAMELUMP_STATIC_PROPS_VERSION )
  897. {
  898. Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
  899. }
  900. if ( g_GameLumps.GetGameLump( handle ) )
  901. {
  902. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size, CUtlBuffer::READ_ONLY );
  903. UnserializeModelDict( buf );
  904. // Skip the leaf list data
  905. int count = buf.GetInt();
  906. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, count * sizeof(StaticPropLeafLump_t) );
  907. UnserializeModels( buf );
  908. }
  909. }
  910. //-----------------------------------------------------------------------------
  911. // Level init, shutdown
  912. //-----------------------------------------------------------------------------
  913. void CVradStaticPropMgr::Init()
  914. {
  915. CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  916. if ( !physicsFactory )
  917. Error( "Unable to load vphysics DLL." );
  918. s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  919. if( !s_pPhysCollision )
  920. {
  921. Error( "Unable to get '%s' for physics interface.", VPHYSICS_COLLISION_INTERFACE_VERSION );
  922. return;
  923. }
  924. // Read in static props that have been compiled into the bsp file
  925. UnserializeStaticProps();
  926. }
  927. void CVradStaticPropMgr::Shutdown()
  928. {
  929. // Remove all static prop model data
  930. for (int i = m_StaticPropDict.Size(); --i >= 0; )
  931. {
  932. studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
  933. if ( pStudioHdr )
  934. {
  935. if ( pStudioHdr->pVertexBase )
  936. {
  937. free( pStudioHdr->pVertexBase );
  938. }
  939. free( pStudioHdr );
  940. }
  941. }
  942. m_StaticProps.Purge();
  943. m_StaticPropDict.Purge();
  944. }
  945. void ComputeLightmapColor( dface_t* pFace, Vector &color )
  946. {
  947. texinfo_t* pTex = &texinfo[pFace->texinfo];
  948. if ( pTex->flags & SURF_SKY )
  949. {
  950. // sky ambient already accounted for in direct component
  951. return;
  952. }
  953. }
  954. bool PositionInSolid( Vector &position )
  955. {
  956. int ndxLeaf = PointLeafnum( position );
  957. if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
  958. {
  959. // position embedded in solid
  960. return true;
  961. }
  962. return false;
  963. }
  964. //-----------------------------------------------------------------------------
  965. // Trace from a vertex to each direct light source, accumulating its contribution.
  966. //-----------------------------------------------------------------------------
  967. void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread,
  968. int static_prop_id_to_skip=-1, int nLFlags = 0)
  969. {
  970. SSE_sampleLightOutput_t sampleOutput;
  971. outColor.Init();
  972. // Iterate over all direct lights and accumulate their contribution
  973. int cluster = ClusterFromPoint( position );
  974. for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
  975. {
  976. if ( dl->light.style )
  977. {
  978. // skip lights with style
  979. continue;
  980. }
  981. // is this lights cluster visible?
  982. if ( !PVSCheck( dl->pvs, cluster ) )
  983. continue;
  984. // push the vertex towards the light to avoid surface acne
  985. Vector adjusted_pos = position;
  986. float flEpsilon = 0.0;
  987. if (dl->light.type != emit_skyambient)
  988. {
  989. // push towards the light
  990. Vector fudge;
  991. if ( dl->light.type == emit_skylight )
  992. fudge = -( dl->light.normal);
  993. else
  994. {
  995. fudge = dl->light.origin-position;
  996. VectorNormalize( fudge );
  997. }
  998. fudge *= 4.0;
  999. adjusted_pos += fudge;
  1000. }
  1001. else
  1002. {
  1003. // push out along normal
  1004. adjusted_pos += 4.0 * normal;
  1005. // flEpsilon = 1.0;
  1006. }
  1007. FourVectors adjusted_pos4;
  1008. FourVectors normal4;
  1009. adjusted_pos4.DuplicateVector( adjusted_pos );
  1010. normal4.DuplicateVector( normal );
  1011. GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST,
  1012. static_prop_id_to_skip, flEpsilon );
  1013. VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor );
  1014. }
  1015. }
  1016. //-----------------------------------------------------------------------------
  1017. // Takes the results from a ComputeLighting call and applies it to the static prop in question.
  1018. //-----------------------------------------------------------------------------
  1019. void CVradStaticPropMgr::ApplyLightingToStaticProp( int iStaticProp, CStaticProp &prop, const CComputeStaticPropLightingResults *pResults )
  1020. {
  1021. if ( pResults->m_ColorVertsArrays.Count() == 0 && pResults->m_ColorTexelsArrays.Count() == 0 )
  1022. return;
  1023. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1024. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1025. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1026. Assert( pStudioHdr && pVtxHdr );
  1027. int iCurColorVertsArray = 0;
  1028. int iCurColorTexelsArray = 0;
  1029. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1030. {
  1031. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  1032. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1033. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1034. {
  1035. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  1036. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1037. const CUtlVector<colorVertex_t> *colorVerts = pResults->m_ColorVertsArrays.Count() ? pResults->m_ColorVertsArrays[iCurColorVertsArray++] : nullptr;
  1038. const CUtlVector<colorTexel_t> *colorTexels = pResults->m_ColorTexelsArrays.Count() ? pResults->m_ColorTexelsArrays[iCurColorTexelsArray++] : nullptr;
  1039. for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ )
  1040. {
  1041. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  1042. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  1043. {
  1044. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  1045. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  1046. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  1047. {
  1048. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  1049. int nMeshIdx = prop.m_MeshData.AddToTail();
  1050. if (colorVerts)
  1051. {
  1052. prop.m_MeshData[nMeshIdx].m_VertexColors.AddMultipleToTail( pStripGroup->numVerts );
  1053. prop.m_MeshData[nMeshIdx].m_nLod = nLod;
  1054. for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
  1055. {
  1056. int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
  1057. Assert( nIndex < pStudioModel->numvertices );
  1058. prop.m_MeshData[nMeshIdx].m_VertexColors[nVertex] = (*colorVerts)[nIndex].m_Color;
  1059. }
  1060. }
  1061. if (colorTexels)
  1062. {
  1063. // TODO: Consider doing this work in the worker threads, because then we distribute it.
  1064. ConvertTexelDataToTexture(prop.m_LightmapImageWidth, prop.m_LightmapImageHeight, prop.m_LightmapImageFormat, (*colorTexels), &prop.m_MeshData[nMeshIdx].m_TexelsEncoded);
  1065. if (g_bDumpPropLightmaps)
  1066. {
  1067. char buffer[_MAX_PATH];
  1068. V_snprintf(
  1069. buffer,
  1070. _MAX_PATH - 1,
  1071. "staticprop_lightmap_%d_%.0f_%.0f_%.0f_%s_%d_%d_%d_%d_%d.tga",
  1072. iStaticProp,
  1073. prop.m_Origin.x,
  1074. prop.m_Origin.y,
  1075. prop.m_Origin.z,
  1076. dict.m_pStudioHdr->pszName(),
  1077. bodyID,
  1078. modelID,
  1079. nLod,
  1080. nMesh,
  1081. nGroup
  1082. );
  1083. for ( int i = 0; buffer[i]; ++i )
  1084. {
  1085. if (buffer[i] == '/' || buffer[i] == '\\')
  1086. buffer[i] = '-';
  1087. }
  1088. DumpLightmapLinear( buffer, (*colorTexels), prop.m_LightmapImageWidth, prop.m_LightmapImageHeight );
  1089. }
  1090. }
  1091. }
  1092. }
  1093. }
  1094. }
  1095. }
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Trace rays from each unique vertex, accumulating direct and indirect
  1099. // sources at each ray termination. Use the winding data to distribute the unique vertexes
  1100. // into the rendering layout.
  1101. //-----------------------------------------------------------------------------
  1102. void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults )
  1103. {
  1104. CUtlVector<badVertex_t> badVerts;
  1105. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1106. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1107. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1108. if ( !pStudioHdr || !pVtxHdr )
  1109. {
  1110. // must have model and its verts for lighting computation
  1111. // game will fallback to fullbright
  1112. return;
  1113. }
  1114. const bool withVertexLighting = (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING) == 0;
  1115. const bool withTexelLighting = (prop.m_Flags & STATIC_PROP_NO_PER_TEXEL_LIGHTING) == 0;
  1116. if (!withVertexLighting && !withTexelLighting)
  1117. return;
  1118. const int skip_prop = (g_bDisablePropSelfShadowing || (prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING)) ? prop_index : -1;
  1119. const int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
  1120. VMPI_SetCurrentStage( "ComputeLighting" );
  1121. matrix3x4_t matPos, matNormal;
  1122. AngleMatrix(prop.m_Angles, prop.m_Origin, matPos);
  1123. AngleMatrix(prop.m_Angles, matNormal);
  1124. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1125. {
  1126. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  1127. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1128. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1129. {
  1130. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel(modelID);
  1131. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1132. if (withTexelLighting)
  1133. {
  1134. CUtlVector<colorTexel_t> *pColorTexelArray = new CUtlVector<colorTexel_t>;
  1135. pResults->m_ColorTexelsArrays.AddToTail(pColorTexelArray);
  1136. }
  1137. // light all unique vertexes
  1138. CUtlVector<colorVertex_t> *pColorVertsArray = new CUtlVector<colorVertex_t>;
  1139. pResults->m_ColorVertsArrays.AddToTail( pColorVertsArray );
  1140. CUtlVector<colorVertex_t> &colorVerts = *pColorVertsArray;
  1141. colorVerts.EnsureCount( pStudioModel->numvertices );
  1142. memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) );
  1143. int numVertexes = 0;
  1144. for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID )
  1145. {
  1146. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
  1147. const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData((void *)pStudioHdr);
  1148. Assert(vertData); // This can only return NULL on X360 for now
  1149. // TODO: Move this into its own function. In fact, refactor this whole function.
  1150. if (withTexelLighting)
  1151. {
  1152. GenerateLightmapSamplesForMesh( matPos, matNormal, iThread, skip_prop, nFlags, prop.m_LightmapImageWidth, prop.m_LightmapImageHeight, pStudioHdr, pStudioModel, pVtxModel, meshID, pResults );
  1153. }
  1154. // If we do lightmapping, we also do vertex lighting as a potential fallback. This may change.
  1155. for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
  1156. {
  1157. Vector sampleNormal;
  1158. Vector samplePosition;
  1159. // transform position and normal into world coordinate system
  1160. VectorTransform(*vertData->Position(vertexID), matPos, samplePosition);
  1161. VectorTransform(*vertData->Normal(vertexID), matNormal, sampleNormal);
  1162. if ( PositionInSolid( samplePosition ) )
  1163. {
  1164. // vertex is in solid, add to the bad list, and recover later
  1165. badVertex_t badVertex;
  1166. badVertex.m_ColorVertex = numVertexes;
  1167. badVertex.m_Position = samplePosition;
  1168. badVertex.m_Normal = sampleNormal;
  1169. badVerts.AddToTail( badVertex );
  1170. }
  1171. else
  1172. {
  1173. Vector direct_pos=samplePosition;
  1174. Vector directColor(0,0,0);
  1175. ComputeDirectLightingAtPoint( direct_pos,
  1176. sampleNormal, directColor, iThread,
  1177. skip_prop, nFlags );
  1178. Vector indirectColor(0,0,0);
  1179. if (g_bShowStaticPropNormals)
  1180. {
  1181. directColor= sampleNormal;
  1182. directColor += Vector(1.0,1.0,1.0);
  1183. directColor *= 50.0;
  1184. }
  1185. else
  1186. {
  1187. if (numbounce >= 1)
  1188. ComputeIndirectLightingAtPoint(
  1189. samplePosition, sampleNormal,
  1190. indirectColor, iThread, true,
  1191. ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 );
  1192. }
  1193. colorVerts[numVertexes].m_bValid = true;
  1194. colorVerts[numVertexes].m_Position = samplePosition;
  1195. VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color );
  1196. }
  1197. numVertexes++;
  1198. }
  1199. }
  1200. // color in the bad vertexes
  1201. // when entire model has no lighting origin and no valid neighbors
  1202. // must punt, leave black coloring
  1203. if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) )
  1204. {
  1205. for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ )
  1206. {
  1207. Vector bestPosition;
  1208. if ( prop.m_bLightingOriginValid )
  1209. {
  1210. // use the specified lighting origin
  1211. VectorCopy( prop.m_LightingOrigin, bestPosition );
  1212. }
  1213. else
  1214. {
  1215. // find the closest valid neighbor
  1216. int best = 0;
  1217. float closest = FLT_MAX;
  1218. for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ )
  1219. {
  1220. if ( !colorVerts[nColorVertex].m_bValid )
  1221. {
  1222. // skip invalid neighbors
  1223. continue;
  1224. }
  1225. Vector delta;
  1226. VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta );
  1227. float distance = VectorLength( delta );
  1228. if ( distance < closest )
  1229. {
  1230. closest = distance;
  1231. best = nColorVertex;
  1232. }
  1233. }
  1234. // use the best neighbor as the direction to crawl
  1235. VectorCopy( colorVerts[best].m_Position, bestPosition );
  1236. }
  1237. // crawl toward best position
  1238. // sudivide to determine a closer valid point to the bad vertex, and re-light
  1239. Vector midPosition;
  1240. int numIterations = 20;
  1241. while ( --numIterations > 0 )
  1242. {
  1243. VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition );
  1244. VectorScale( midPosition, 0.5f, midPosition );
  1245. if ( PositionInSolid( midPosition ) )
  1246. break;
  1247. bestPosition = midPosition;
  1248. }
  1249. // re-light from better position
  1250. Vector directColor;
  1251. ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread );
  1252. Vector indirectColor;
  1253. ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal,
  1254. indirectColor, iThread, true );
  1255. // save results, not changing valid status
  1256. // to ensure this offset position is not considered as a viable candidate
  1257. colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition;
  1258. VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color );
  1259. }
  1260. }
  1261. // discard bad verts
  1262. badVerts.Purge();
  1263. }
  1264. }
  1265. }
  1266. //-----------------------------------------------------------------------------
  1267. // Write the lighitng to bsp pak lump
  1268. //-----------------------------------------------------------------------------
  1269. void CVradStaticPropMgr::SerializeLighting()
  1270. {
  1271. char filename[MAX_PATH];
  1272. CUtlBuffer utlBuf;
  1273. // illuminate them all
  1274. int count = m_StaticProps.Count();
  1275. if ( !count )
  1276. {
  1277. // nothing to do
  1278. return;
  1279. }
  1280. char mapName[MAX_PATH];
  1281. Q_FileBase( source, mapName, sizeof( mapName ) );
  1282. int size;
  1283. for (int i = 0; i < count; ++i)
  1284. {
  1285. // no need to write this file if we didn't compute the data
  1286. // props marked this way will not load the info anyway
  1287. if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
  1288. continue;
  1289. if (g_bHDR)
  1290. {
  1291. sprintf( filename, "sp_hdr_%d.vhv", i );
  1292. }
  1293. else
  1294. {
  1295. sprintf( filename, "sp_%d.vhv", i );
  1296. }
  1297. int totalVertexes = 0;
  1298. for ( int j=0; j<m_StaticProps[i].m_MeshData.Count(); j++ )
  1299. {
  1300. totalVertexes += m_StaticProps[i].m_MeshData[j].m_VertexColors.Count();
  1301. }
  1302. // allocate a buffer with enough padding for alignment
  1303. size = sizeof( HardwareVerts::FileHeader_t ) +
  1304. m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
  1305. totalVertexes*4 + 2*512;
  1306. utlBuf.EnsureCapacity( size );
  1307. Q_memset( utlBuf.Base(), 0, size );
  1308. HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
  1309. // align to start of vertex data
  1310. unsigned char *pVertexData = (unsigned char *)(sizeof( HardwareVerts::FileHeader_t ) + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t));
  1311. pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
  1312. // construct header
  1313. pVhvHdr->m_nVersion = VHV_VERSION;
  1314. pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
  1315. pVhvHdr->m_nVertexFlags = VERTEX_COLOR;
  1316. pVhvHdr->m_nVertexSize = 4;
  1317. pVhvHdr->m_nVertexes = totalVertexes;
  1318. pVhvHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
  1319. for (int n=0; n<pVhvHdr->m_nMeshes; n++)
  1320. {
  1321. // construct mesh dictionary
  1322. HardwareVerts::MeshHeader_t *pMesh = pVhvHdr->pMesh( n );
  1323. pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
  1324. pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_VertexColors.Count();
  1325. pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
  1326. // construct vertexes
  1327. for (int k=0; k<pMesh->m_nVertexes; k++)
  1328. {
  1329. Vector &vertexColor = m_StaticProps[i].m_MeshData[n].m_VertexColors[k];
  1330. ColorRGBExp32 rgbColor;
  1331. VectorToColorRGBExp32( vertexColor, rgbColor );
  1332. unsigned char dstColor[4];
  1333. ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
  1334. // b,g,r,a order
  1335. pVertexData[0] = dstColor[2];
  1336. pVertexData[1] = dstColor[1];
  1337. pVertexData[2] = dstColor[0];
  1338. pVertexData[3] = dstColor[3];
  1339. pVertexData += 4;
  1340. }
  1341. }
  1342. // align to end of file
  1343. pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
  1344. pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
  1345. AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
  1346. }
  1347. for (int i = 0; i < count; ++i)
  1348. {
  1349. const int kAlignment = 512;
  1350. // no need to write this file if we didn't compute the data
  1351. // props marked this way will not load the info anyway
  1352. if (m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_TEXEL_LIGHTING)
  1353. continue;
  1354. sprintf(filename, "texelslighting_%d.ppl", i);
  1355. ImageFormat fmt = m_StaticProps[i].m_LightmapImageFormat;
  1356. unsigned int totalTexelSizeBytes = 0;
  1357. for (int j = 0; j < m_StaticProps[i].m_MeshData.Count(); j++)
  1358. {
  1359. totalTexelSizeBytes += m_StaticProps[i].m_MeshData[j].m_TexelsEncoded.Count();
  1360. }
  1361. // allocate a buffer with enough padding for alignment
  1362. size = sizeof(HardwareTexels::FileHeader_t)
  1363. + m_StaticProps[i].m_MeshData.Count() * sizeof(HardwareTexels::MeshHeader_t)
  1364. + totalTexelSizeBytes
  1365. + 2 * kAlignment;
  1366. utlBuf.EnsureCapacity(size);
  1367. Q_memset(utlBuf.Base(), 0, size);
  1368. HardwareTexels::FileHeader_t *pVhtHdr = (HardwareTexels::FileHeader_t *)utlBuf.Base();
  1369. // align start of texel data
  1370. unsigned char *pTexelData = (unsigned char *)(sizeof(HardwareTexels::FileHeader_t) + m_StaticProps[i].m_MeshData.Count() * sizeof(HardwareTexels::MeshHeader_t));
  1371. pTexelData = (unsigned char*)pVhtHdr + ALIGN_TO_POW2((unsigned int)pTexelData, kAlignment);
  1372. pVhtHdr->m_nVersion = VHT_VERSION;
  1373. pVhtHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
  1374. pVhtHdr->m_nTexelFormat = fmt;
  1375. pVhtHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
  1376. for (int n = 0; n < pVhtHdr->m_nMeshes; n++)
  1377. {
  1378. HardwareTexels::MeshHeader_t *pMesh = pVhtHdr->pMesh(n);
  1379. pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
  1380. pMesh->m_nOffset = (unsigned int)pTexelData - (unsigned int)pVhtHdr;
  1381. pMesh->m_nBytes = m_StaticProps[i].m_MeshData[n].m_TexelsEncoded.Count();
  1382. pMesh->m_nWidth = m_StaticProps[i].m_LightmapImageWidth;
  1383. pMesh->m_nHeight = m_StaticProps[i].m_LightmapImageHeight;
  1384. Q_memcpy(pTexelData, m_StaticProps[i].m_MeshData[n].m_TexelsEncoded.Base(), m_StaticProps[i].m_MeshData[n].m_TexelsEncoded.Count());
  1385. pTexelData += m_StaticProps[i].m_MeshData[n].m_TexelsEncoded.Count();
  1386. }
  1387. pTexelData = (unsigned char *)((unsigned int)pTexelData - (unsigned int)pVhtHdr);
  1388. pTexelData = (unsigned char*)pVhtHdr + ALIGN_TO_POW2((unsigned int)pTexelData, kAlignment);
  1389. AddBufferToPak(GetPakFile(), filename, (void*)pVhtHdr, pTexelData - (unsigned char*)pVhtHdr, false);
  1390. }
  1391. }
  1392. void CVradStaticPropMgr::VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf )
  1393. {
  1394. g_StaticPropMgr.VMPI_ProcessStaticProp( iThread, iStaticProp, pBuf );
  1395. }
  1396. void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker )
  1397. {
  1398. g_StaticPropMgr.VMPI_ReceiveStaticPropResults( iStaticProp, pBuf, iWorker );
  1399. }
  1400. //-----------------------------------------------------------------------------
  1401. // Called on workers to do the computation for a static prop and send
  1402. // it to the master.
  1403. //-----------------------------------------------------------------------------
  1404. void CVradStaticPropMgr::VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf )
  1405. {
  1406. // Compute the lighting.
  1407. CComputeStaticPropLightingResults results;
  1408. ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
  1409. VMPI_SetCurrentStage( "EncodeLightingResults" );
  1410. // Encode the results.
  1411. int nLists = results.m_ColorVertsArrays.Count();
  1412. pBuf->write( &nLists, sizeof( nLists ) );
  1413. for ( int i=0; i < nLists; i++ )
  1414. {
  1415. CUtlVector<colorVertex_t> &curList = *results.m_ColorVertsArrays[i];
  1416. int count = curList.Count();
  1417. pBuf->write( &count, sizeof( count ) );
  1418. pBuf->write( curList.Base(), curList.Count() * sizeof( colorVertex_t ) );
  1419. }
  1420. nLists = results.m_ColorTexelsArrays.Count();
  1421. pBuf->write(&nLists, sizeof(nLists));
  1422. for (int i = 0; i < nLists; i++)
  1423. {
  1424. CUtlVector<colorTexel_t> &curList = *results.m_ColorTexelsArrays[i];
  1425. int count = curList.Count();
  1426. pBuf->write(&count, sizeof(count));
  1427. pBuf->write(curList.Base(), curList.Count() * sizeof(colorTexel_t));
  1428. }
  1429. }
  1430. //-----------------------------------------------------------------------------
  1431. // Called on the master when a worker finishes processing a static prop.
  1432. //-----------------------------------------------------------------------------
  1433. void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker )
  1434. {
  1435. // Read in the results.
  1436. CComputeStaticPropLightingResults results;
  1437. int nLists;
  1438. pBuf->read( &nLists, sizeof( nLists ) );
  1439. for ( int i=0; i < nLists; i++ )
  1440. {
  1441. CUtlVector<colorVertex_t> *pList = new CUtlVector<colorVertex_t>;
  1442. results.m_ColorVertsArrays.AddToTail( pList );
  1443. int count;
  1444. pBuf->read( &count, sizeof( count ) );
  1445. pList->SetSize( count );
  1446. pBuf->read( pList->Base(), count * sizeof( colorVertex_t ) );
  1447. }
  1448. pBuf->read(&nLists, sizeof(nLists));
  1449. for (int i = 0; i < nLists; i++)
  1450. {
  1451. CUtlVector<colorTexel_t> *pList = new CUtlVector<colorTexel_t>;
  1452. results.m_ColorTexelsArrays.AddToTail(pList);
  1453. int count;
  1454. pBuf->read(&count, sizeof(count));
  1455. pList->SetSize(count);
  1456. pBuf->read(pList->Base(), count * sizeof(colorTexel_t));
  1457. }
  1458. // Apply the results.
  1459. ApplyLightingToStaticProp( iStaticProp, m_StaticProps[iStaticProp], &results );
  1460. }
  1461. void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
  1462. {
  1463. // Compute the lighting.
  1464. CComputeStaticPropLightingResults results;
  1465. ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
  1466. ApplyLightingToStaticProp( iStaticProp, m_StaticProps[iStaticProp], &results );
  1467. }
  1468. void CVradStaticPropMgr::ThreadComputeStaticPropLighting( int iThread, void *pUserData )
  1469. {
  1470. while (1)
  1471. {
  1472. int j = GetThreadWork ();
  1473. if (j == -1)
  1474. break;
  1475. CComputeStaticPropLightingResults results;
  1476. g_StaticPropMgr.ComputeLightingForProp( iThread, j );
  1477. }
  1478. }
  1479. //-----------------------------------------------------------------------------
  1480. // Computes lighting for the static props.
  1481. // Must be after all other surface lighting has been computed for the indirect sampling.
  1482. //-----------------------------------------------------------------------------
  1483. void CVradStaticPropMgr::ComputeLighting( int iThread )
  1484. {
  1485. // illuminate them all
  1486. int count = m_StaticProps.Count();
  1487. if ( !count )
  1488. {
  1489. // nothing to do
  1490. return;
  1491. }
  1492. StartPacifier( "Computing static prop lighting : " );
  1493. // ensure any traces against us are ignored because we have no inherit lighting contribution
  1494. m_bIgnoreStaticPropTrace = true;
  1495. if ( g_bUseMPI )
  1496. {
  1497. // Distribute the work among the workers.
  1498. VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
  1499. DistributeWork(
  1500. count,
  1501. VMPI_DISTRIBUTEWORK_PACKETID,
  1502. &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
  1503. &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
  1504. }
  1505. else
  1506. {
  1507. RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
  1508. }
  1509. // restore default
  1510. m_bIgnoreStaticPropTrace = false;
  1511. // save data to bsp
  1512. SerializeLighting();
  1513. EndPacifier( true );
  1514. }
  1515. //-----------------------------------------------------------------------------
  1516. // Adds all static prop polys to the ray trace store.
  1517. //-----------------------------------------------------------------------------
  1518. void CVradStaticPropMgr::AddPolysForRayTrace( void )
  1519. {
  1520. int count = m_StaticProps.Count();
  1521. if ( !count )
  1522. {
  1523. // nothing to do
  1524. return;
  1525. }
  1526. // Triangle coverage of 1 (full coverage)
  1527. Vector fullCoverage;
  1528. fullCoverage.x = 1.0f;
  1529. for ( int nProp = 0; nProp < count; ++nProp )
  1530. {
  1531. CStaticProp &prop = m_StaticProps[nProp];
  1532. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1533. if ( prop.m_Flags & STATIC_PROP_NO_SHADOW )
  1534. continue;
  1535. // If not using static prop polys, use AABB
  1536. if ( !g_bStaticPropPolys )
  1537. {
  1538. if ( dict.m_pModel )
  1539. {
  1540. VMatrix xform;
  1541. xform.SetupMatrixOrgAngles ( prop.m_Origin, prop.m_Angles );
  1542. ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
  1543. for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
  1544. {
  1545. for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
  1546. {
  1547. Vector verts[3];
  1548. queryModel->GetTriangleVerts( nConvex, nTri, verts );
  1549. for ( int nVert = 0; nVert < 3; ++nVert )
  1550. verts[nVert] = xform.VMul4x3(verts[nVert]);
  1551. g_RtEnv.AddTriangle ( TRACE_ID_STATICPROP | nProp, verts[0], verts[1], verts[2], fullCoverage );
  1552. }
  1553. }
  1554. s_pPhysCollision->DestroyQueryModel( queryModel );
  1555. }
  1556. else
  1557. {
  1558. VectorAdd ( dict.m_Mins, prop.m_Origin, prop.m_mins );
  1559. VectorAdd ( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
  1560. g_RtEnv.AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
  1561. }
  1562. continue;
  1563. }
  1564. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1565. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1566. if ( !pStudioHdr || !pVtxHdr )
  1567. {
  1568. // must have model and its verts for decoding triangles
  1569. return;
  1570. }
  1571. // only init the triangle table the first time
  1572. bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
  1573. int triangleIndex = 0;
  1574. // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
  1575. // body parts -> models -> lod meshes -> strip groups -> strips
  1576. // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
  1577. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1578. {
  1579. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  1580. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1581. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1582. {
  1583. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  1584. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1585. // assuming lod 0, could iterate if required
  1586. int nLod = 0;
  1587. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  1588. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  1589. {
  1590. // check if this mesh's material is in the no shadow material name list
  1591. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  1592. mstudiotexture_t *pTxtr=pStudioHdr->pTexture(pMesh->material);
  1593. //printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
  1594. bool bSkipThisMesh = false;
  1595. for(int check=0; check<g_NonShadowCastingMaterialStrings.Count(); check++)
  1596. {
  1597. if ( Q_stristr( pTxtr->pszName(),
  1598. g_NonShadowCastingMaterialStrings[check] ) )
  1599. {
  1600. //printf("skip mat name=%s\n",pTxtr->pszName());
  1601. bSkipThisMesh = true;
  1602. break;
  1603. }
  1604. }
  1605. if ( bSkipThisMesh)
  1606. continue;
  1607. int shadowTextureIndex = -1;
  1608. if ( dict.m_textureShadowIndex.Count() )
  1609. {
  1610. shadowTextureIndex = dict.m_textureShadowIndex[pMesh->material];
  1611. }
  1612. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  1613. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  1614. Assert( vertData ); // This can only return NULL on X360 for now
  1615. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  1616. {
  1617. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  1618. int nStrip;
  1619. for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
  1620. {
  1621. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
  1622. if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
  1623. {
  1624. for ( int i = 0; i < pStrip->numIndices; i += 3 )
  1625. {
  1626. int idx = pStrip->indexOffset + i;
  1627. unsigned short i1 = *pStripGroup->pIndex( idx );
  1628. unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
  1629. unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
  1630. int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
  1631. int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
  1632. int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
  1633. // transform position into world coordinate system
  1634. matrix3x4_t matrix;
  1635. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  1636. Vector position1;
  1637. Vector position2;
  1638. Vector position3;
  1639. VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
  1640. VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
  1641. VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
  1642. unsigned short flags = 0;
  1643. int materialIndex = -1;
  1644. Vector color = vec3_origin;
  1645. if ( shadowTextureIndex >= 0 )
  1646. {
  1647. if ( bInitTriangles )
  1648. {
  1649. // add texture space and texture index to material database
  1650. // now
  1651. float coverage = g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
  1652. if ( coverage < 1.0f )
  1653. {
  1654. materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
  1655. color.x = coverage;
  1656. }
  1657. else
  1658. {
  1659. materialIndex = -1;
  1660. }
  1661. dict.m_triangleMaterialIndex.AddToTail(materialIndex);
  1662. }
  1663. else
  1664. {
  1665. materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
  1666. triangleIndex++;
  1667. }
  1668. if ( materialIndex >= 0 )
  1669. {
  1670. flags = FCACHETRI_TRANSPARENT;
  1671. }
  1672. }
  1673. // printf( "\ngl 3\n" );
  1674. // printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
  1675. // printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
  1676. // printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
  1677. g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
  1678. position1, position2, position3,
  1679. color, flags, materialIndex);
  1680. }
  1681. }
  1682. else
  1683. {
  1684. // all tris expected to be discrete tri lists
  1685. // must fixme if stripping ever occurs
  1686. printf( "unexpected strips found\n" );
  1687. Assert( 0 );
  1688. return;
  1689. }
  1690. }
  1691. }
  1692. }
  1693. }
  1694. }
  1695. }
  1696. }
  1697. struct tl_tri_t
  1698. {
  1699. Vector p0;
  1700. Vector p1;
  1701. Vector p2;
  1702. Vector n0;
  1703. Vector n1;
  1704. Vector n2;
  1705. bool operator == (const tl_tri_t &t) const
  1706. {
  1707. return ( p0 == t.p0 &&
  1708. p1 == t.p1 &&
  1709. p2 == t.p2 &&
  1710. n0 == t.n0 &&
  1711. n1 == t.n1 &&
  1712. n2 == t.n2 );
  1713. }
  1714. };
  1715. struct tl_vert_t
  1716. {
  1717. Vector m_position;
  1718. CUtlLinkedList< tl_tri_t, int > m_triList;
  1719. };
  1720. void AddTriVertsToList( CUtlVector< tl_vert_t > &triListVerts, int vertIndex, Vector vertPosition, Vector p0, Vector p1, Vector p2, Vector n0, Vector n1, Vector n2 )
  1721. {
  1722. tl_tri_t tlTri;
  1723. tlTri.p0 = p0;
  1724. tlTri.p1 = p1;
  1725. tlTri.p2 = p2;
  1726. tlTri.n0 = n0;
  1727. tlTri.n1 = n1;
  1728. tlTri.n2 = n2;
  1729. triListVerts.EnsureCapacity( vertIndex+1 );
  1730. triListVerts[vertIndex].m_position = vertPosition;
  1731. int index = triListVerts[vertIndex].m_triList.Find( tlTri );
  1732. if ( !triListVerts[vertIndex].m_triList.IsValidIndex( index ) )
  1733. {
  1734. // not in list, add to list of triangles
  1735. triListVerts[vertIndex].m_triList.AddToTail( tlTri );
  1736. }
  1737. }
  1738. //-----------------------------------------------------------------------------
  1739. // Builds a list of tris for every vertex
  1740. //-----------------------------------------------------------------------------
  1741. void CVradStaticPropMgr::BuildTriList( CStaticProp &prop )
  1742. {
  1743. // the generated list will consist of a list of verts
  1744. // each vert will have a linked list of triangles that it belongs to
  1745. CUtlVector< tl_vert_t > triListVerts;
  1746. StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
  1747. studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
  1748. OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
  1749. if ( !pStudioHdr || !pVtxHdr )
  1750. {
  1751. // must have model and its verts for decoding triangles
  1752. return;
  1753. }
  1754. // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
  1755. // body parts -> models -> lod meshes -> strip groups -> strips
  1756. // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
  1757. for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
  1758. {
  1759. OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
  1760. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
  1761. for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
  1762. {
  1763. OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
  1764. mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
  1765. // get the specified lod, assuming lod 0
  1766. int nLod = 0;
  1767. OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
  1768. // must reset because each model has their own vertexes [0..n]
  1769. // in order for this to be monolithic for the entire prop the list must be segmented
  1770. triListVerts.Purge();
  1771. for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
  1772. {
  1773. mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
  1774. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
  1775. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
  1776. Assert( vertData ); // This can only return NULL on X360 for now
  1777. for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
  1778. {
  1779. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
  1780. int nStrip;
  1781. for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
  1782. {
  1783. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
  1784. if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
  1785. {
  1786. for ( int i = 0; i < pStrip->numIndices; i += 3 )
  1787. {
  1788. int idx = pStrip->indexOffset + i;
  1789. unsigned short i1 = *pStripGroup->pIndex( idx );
  1790. unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
  1791. unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
  1792. int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
  1793. int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
  1794. int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
  1795. // transform position into world coordinate system
  1796. matrix3x4_t matrix;
  1797. AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
  1798. Vector position1;
  1799. Vector position2;
  1800. Vector position3;
  1801. VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
  1802. VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
  1803. VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
  1804. Vector normal1;
  1805. Vector normal2;
  1806. Vector normal3;
  1807. VectorTransform( *vertData->Normal( vertex1 ), matrix, normal1 );
  1808. VectorTransform( *vertData->Normal( vertex2 ), matrix, normal2 );
  1809. VectorTransform( *vertData->Normal( vertex3 ), matrix, normal3 );
  1810. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex1, position1, position1, position2, position3, normal1, normal2, normal3 );
  1811. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex2, position2, position1, position2, position3, normal1, normal2, normal3 );
  1812. AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex3, position3, position1, position2, position3, normal1, normal2, normal3 );
  1813. }
  1814. }
  1815. else
  1816. {
  1817. // all tris expected to be discrete tri lists
  1818. // must fixme if stripping ever occurs
  1819. printf( "unexpected strips found\n" );
  1820. Assert( 0 );
  1821. return;
  1822. }
  1823. }
  1824. }
  1825. }
  1826. }
  1827. }
  1828. }
  1829. const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
  1830. {
  1831. studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
  1832. Assert( pActiveStudioHdr );
  1833. if ( pActiveStudioHdr->pVertexBase )
  1834. {
  1835. return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase;
  1836. }
  1837. // mandatory callback to make requested data resident
  1838. // load and persist the vertex file
  1839. char fileName[MAX_PATH];
  1840. strcpy( fileName, "models/" );
  1841. strcat( fileName, pActiveStudioHdr->pszName() );
  1842. Q_StripExtension( fileName, fileName, sizeof( fileName ) );
  1843. strcat( fileName, ".vvd" );
  1844. // load the model
  1845. FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" );
  1846. if ( !fileHandle )
  1847. {
  1848. Error( "Unable to load vertex data \"%s\"\n", fileName );
  1849. }
  1850. // Get the file size
  1851. int vvdSize = g_pFileSystem->Size( fileHandle );
  1852. if ( vvdSize == 0 )
  1853. {
  1854. g_pFileSystem->Close( fileHandle );
  1855. Error( "Bad size for vertex data \"%s\"\n", fileName );
  1856. }
  1857. vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
  1858. g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle );
  1859. g_pFileSystem->Close( fileHandle );
  1860. // check header
  1861. if ( pVvdHdr->id != MODEL_VERTEX_FILE_ID )
  1862. {
  1863. Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
  1864. }
  1865. if ( pVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
  1866. {
  1867. Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
  1868. }
  1869. if ( pVvdHdr->checksum != pActiveStudioHdr->checksum )
  1870. {
  1871. Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, pActiveStudioHdr->checksum);
  1872. }
  1873. // need to perform mesh relocation fixups
  1874. // allocate a new copy
  1875. vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
  1876. if ( !pNewVvdHdr )
  1877. {
  1878. Error( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
  1879. }
  1880. // load vertexes and run fixups
  1881. Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true );
  1882. // discard original
  1883. free( pVvdHdr );
  1884. pVvdHdr = pNewVvdHdr;
  1885. pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
  1886. return pVvdHdr;
  1887. }
  1888. // ------------------------------------------------------------------------------------------------
  1889. // ------------------------------------------------------------------------------------------------
  1890. // ------------------------------------------------------------------------------------------------
  1891. struct ColorTexelValue
  1892. {
  1893. Vector mLinearColor; // Linear color value for this texel
  1894. bool mValidData; // Whether there is valid data in this texel.
  1895. size_t mTriangleIndex; // Which triangle we used to generate the texel.
  1896. };
  1897. // ------------------------------------------------------------------------------------------------
  1898. inline int ComputeLinearPos( int _x, int _y, int _resX, int _resY )
  1899. {
  1900. return Min( Max( 0, _y ), _resY - 1 ) * _resX
  1901. + Min( Max( 0, _x ), _resX - 1 );
  1902. }
  1903. // ------------------------------------------------------------------------------------------------
  1904. inline float ComputeBarycentricDistanceToTri( Vector _barycentricCoord, Vector2D _v[3] )
  1905. {
  1906. Vector2D realPos = _barycentricCoord.x * _v[0]
  1907. + _barycentricCoord.y * _v[1]
  1908. + _barycentricCoord.z * _v[2];
  1909. int minIndex = 0;
  1910. float minVal = _barycentricCoord[0];
  1911. for (int i = 1; i < 3; ++i) {
  1912. if (_barycentricCoord[i] < minVal) {
  1913. minVal = _barycentricCoord[i];
  1914. minIndex = i;
  1915. }
  1916. }
  1917. Vector2D& first = _v[ (minIndex + 1) % 3];
  1918. Vector2D& second = _v[ (minIndex + 2) % 3];
  1919. return CalcDistanceToLineSegment2D( realPos, first, second );
  1920. }
  1921. // ------------------------------------------------------------------------------------------------
  1922. static void GenerateLightmapSamplesForMesh( const matrix3x4_t& _matPos, const matrix3x4_t& _matNormal, int _iThread, int _skipProp, int _flags, int _lightmapResX, int _lightmapResY, studiohdr_t* _pStudioHdr, mstudiomodel_t* _pStudioModel, OptimizedModel::ModelHeader_t* _pVtxModel, int _meshID, CComputeStaticPropLightingResults *_outResults )
  1923. {
  1924. // Could iterate and gen this if needed.
  1925. int nLod = 0;
  1926. OptimizedModel::ModelLODHeader_t *pVtxLOD = _pVtxModel->pLOD(nLod);
  1927. CUtlVector<colorTexel_t> &colorTexels = (*_outResults->m_ColorTexelsArrays.Tail());
  1928. const int cTotalPixelCount = _lightmapResX * _lightmapResY;
  1929. colorTexels.EnsureCount(cTotalPixelCount);
  1930. memset(colorTexels.Base(), 0, colorTexels.Count() * sizeof(colorTexel_t));
  1931. for (int i = 0; i < colorTexels.Count(); ++i) {
  1932. colorTexels[i].m_fDistanceToTri = FLT_MAX;
  1933. }
  1934. mstudiomesh_t* pMesh = _pStudioModel->pMesh(_meshID);
  1935. OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh(_meshID);
  1936. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData((void *)_pStudioHdr);
  1937. Assert(vertData); // This can only return NULL on X360 for now
  1938. for (int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup)
  1939. {
  1940. OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup(nGroup);
  1941. int nStrip;
  1942. for (nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++)
  1943. {
  1944. OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip(nStrip);
  1945. // If this hits, re-factor the code to iterate over triangles, and build the triangles
  1946. // from the underlying structures.
  1947. Assert((pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP) == 0);
  1948. if (pStrip->flags & OptimizedModel::STRIP_IS_TRILIST)
  1949. {
  1950. for (int i = 0; i < pStrip->numIndices; i += 3)
  1951. {
  1952. int idx = pStrip->indexOffset + i;
  1953. unsigned short i1 = *pStripGroup->pIndex(idx);
  1954. unsigned short i2 = *pStripGroup->pIndex(idx + 1);
  1955. unsigned short i3 = *pStripGroup->pIndex(idx + 2);
  1956. int vertex1 = pStripGroup->pVertex(i1)->origMeshVertID;
  1957. int vertex2 = pStripGroup->pVertex(i2)->origMeshVertID;
  1958. int vertex3 = pStripGroup->pVertex(i3)->origMeshVertID;
  1959. Vector modelPos[3] = {
  1960. *vertData->Position(vertex1),
  1961. *vertData->Position(vertex2),
  1962. *vertData->Position(vertex3)
  1963. };
  1964. Vector modelNormal[3] = {
  1965. *vertData->Normal(vertex1),
  1966. *vertData->Normal(vertex2),
  1967. *vertData->Normal(vertex3)
  1968. };
  1969. Vector worldPos[3];
  1970. Vector worldNormal[3];
  1971. VectorTransform(modelPos[0], _matPos, worldPos[0]);
  1972. VectorTransform(modelPos[1], _matPos, worldPos[1]);
  1973. VectorTransform(modelPos[2], _matPos, worldPos[2]);
  1974. VectorTransform(modelNormal[0], _matNormal, worldNormal[0]);
  1975. VectorTransform(modelNormal[1], _matNormal, worldNormal[1]);
  1976. VectorTransform(modelNormal[2], _matNormal, worldNormal[2]);
  1977. Vector2D texcoord[3] = {
  1978. *vertData->Texcoord(vertex1),
  1979. *vertData->Texcoord(vertex2),
  1980. *vertData->Texcoord(vertex3)
  1981. };
  1982. Rasterizer rasterizer(texcoord[0], texcoord[1], texcoord[2],
  1983. _lightmapResX, _lightmapResY);
  1984. for (auto it = rasterizer.begin(); it != rasterizer.end(); ++it)
  1985. {
  1986. size_t linearPos = rasterizer.GetLinearPos(it);
  1987. Assert(linearPos < cTotalPixelCount);
  1988. if ( colorTexels[linearPos].m_bValid )
  1989. {
  1990. continue;
  1991. }
  1992. float ourDistancetoTri = ComputeBarycentricDistanceToTri( it->barycentric, texcoord );
  1993. bool doWrite = it->insideTriangle
  1994. || !colorTexels[linearPos].m_bPossiblyInteresting
  1995. || colorTexels[linearPos].m_fDistanceToTri > ourDistancetoTri;
  1996. if (doWrite)
  1997. {
  1998. Vector itWorldPos = worldPos[0] * it->barycentric.x
  1999. + worldPos[1] * it->barycentric.y
  2000. + worldPos[2] * it->barycentric.z;
  2001. Vector itWorldNormal = worldNormal[0] * it->barycentric.x
  2002. + worldNormal[1] * it->barycentric.y
  2003. + worldNormal[2] * it->barycentric.z;
  2004. itWorldNormal.NormalizeInPlace();
  2005. colorTexels[linearPos].m_WorldPosition = itWorldPos;
  2006. colorTexels[linearPos].m_WorldNormal = itWorldNormal;
  2007. colorTexels[linearPos].m_bValid = it->insideTriangle;
  2008. colorTexels[linearPos].m_bPossiblyInteresting = true;
  2009. colorTexels[linearPos].m_fDistanceToTri = ourDistancetoTri;
  2010. }
  2011. }
  2012. }
  2013. }
  2014. }
  2015. }
  2016. // Process neighbors to the valid region. Walk through the existing array, look for samples that
  2017. // are not valid but are adjacent to valid samples. Works if we are only bilinearly sampling
  2018. // on the other side.
  2019. // First attempt: Just pretend the triangle was larger and cast a ray from this new world pos
  2020. // as above.
  2021. int linearPos = 0;
  2022. for ( int j = 0; j < _lightmapResY; ++j )
  2023. {
  2024. for (int i = 0; i < _lightmapResX; ++i )
  2025. {
  2026. bool shouldProcess = colorTexels[linearPos].m_bValid;
  2027. // Are any of the eight neighbors valid??
  2028. if ( colorTexels[linearPos].m_bPossiblyInteresting )
  2029. {
  2030. // Look at our neighborhood (3x3 centerd on us).
  2031. shouldProcess = shouldProcess
  2032. || colorTexels[ComputeLinearPos( i - 1, j - 1, _lightmapResX, _lightmapResY )].m_bValid // TL
  2033. || colorTexels[ComputeLinearPos( i , j - 1, _lightmapResX, _lightmapResY )].m_bValid // T
  2034. || colorTexels[ComputeLinearPos( i + 1, j - 1, _lightmapResX, _lightmapResY )].m_bValid // TR
  2035. || colorTexels[ComputeLinearPos( i - 1, j , _lightmapResX, _lightmapResY )].m_bValid // L
  2036. || colorTexels[ComputeLinearPos( i + 1, j , _lightmapResX, _lightmapResY )].m_bValid // R
  2037. || colorTexels[ComputeLinearPos( i - 1, j + 1, _lightmapResX, _lightmapResY )].m_bValid // BL
  2038. || colorTexels[ComputeLinearPos( i , j + 1, _lightmapResX, _lightmapResY )].m_bValid // B
  2039. || colorTexels[ComputeLinearPos( i + 1, j + 1, _lightmapResX, _lightmapResY )].m_bValid; // BR
  2040. }
  2041. if (shouldProcess)
  2042. {
  2043. Vector directColor(0, 0, 0),
  2044. indirectColor(0, 0, 0);
  2045. ComputeDirectLightingAtPoint( colorTexels[linearPos].m_WorldPosition, colorTexels[linearPos].m_WorldNormal, directColor, _iThread, _skipProp, _flags);
  2046. if (numbounce >= 1) {
  2047. ComputeIndirectLightingAtPoint( colorTexels[linearPos].m_WorldPosition, colorTexels[linearPos].m_WorldNormal, indirectColor, _iThread, true, (_flags & GATHERLFLAGS_IGNORE_NORMALS) != 0 );
  2048. }
  2049. VectorAdd(directColor, indirectColor, colorTexels[linearPos].m_Color);
  2050. }
  2051. ++linearPos;
  2052. }
  2053. }
  2054. }
  2055. // ------------------------------------------------------------------------------------------------
  2056. static int GetTexelCount(unsigned int _resX, unsigned int _resY, bool _mipmaps)
  2057. {
  2058. // Because they are unsigned, this is a != check--but if we were to change to ints, this would be
  2059. // the right assert (and it's no worse than != now).
  2060. Assert(_resX > 0 && _resY > 0);
  2061. if (_mipmaps == false)
  2062. return _resX * _resY;
  2063. int retVal = 0;
  2064. while (_resX > 1 || _resY > 1)
  2065. {
  2066. retVal += _resX * _resY;
  2067. _resX = max(1, _resX >> 1);
  2068. _resY = max(1, _resY >> 1);
  2069. }
  2070. // Add in the 1x1 mipmap level, which wasn't hit above. This could be done in the initializer of
  2071. // retVal, but it's more obvious here.
  2072. retVal += 1;
  2073. return retVal;
  2074. }
  2075. // ------------------------------------------------------------------------------------------------
  2076. static void FilterFineMipmap(unsigned int _resX, unsigned int _resY, const CUtlVector<colorTexel_t>& _srcTexels, CUtlVector<Vector>* _outLinear)
  2077. {
  2078. Assert(_outLinear);
  2079. // We can't filter in place, so go ahead and create a linear buffer here.
  2080. CUtlVector<Vector> filterSrc;
  2081. filterSrc.EnsureCount(_srcTexels.Count());
  2082. for (int i = 0; i < _srcTexels.Count(); ++i)
  2083. {
  2084. ColorRGBExp32 rgbColor;
  2085. VectorToColorRGBExp32(_srcTexels[i].m_Color, rgbColor);
  2086. ConvertRGBExp32ToLinear( &rgbColor, &(filterSrc[i]) );
  2087. }
  2088. const int cRadius = 1;
  2089. const float cOneOverDiameter = 1.0f / pow(2.0f * cRadius + 1.0f, 2.0f) ;
  2090. // Filter here.
  2091. for (int j = 0; j < _resY; ++j)
  2092. {
  2093. for (int i = 0; i < _resX; ++i)
  2094. {
  2095. Vector value(0, 0, 0);
  2096. int thisIndex = ComputeLinearPos(i, j, _resX, _resY);
  2097. if (!_srcTexels[thisIndex].m_bValid)
  2098. {
  2099. (*_outLinear)[thisIndex] = filterSrc[thisIndex];
  2100. continue;
  2101. }
  2102. // TODO: Check ASM for this, unroll by hand if needed.
  2103. for ( int offsetJ = -cRadius; offsetJ <= cRadius; ++offsetJ )
  2104. {
  2105. for ( int offsetI = -cRadius; offsetI <= cRadius; ++offsetI )
  2106. {
  2107. int finalIndex = ComputeLinearPos( i + offsetI, j + offsetJ, _resX, _resY );
  2108. if ( !_srcTexels[finalIndex].m_bValid )
  2109. {
  2110. finalIndex = thisIndex;
  2111. }
  2112. value += filterSrc[finalIndex];
  2113. }
  2114. }
  2115. (*_outLinear)[thisIndex] = value * cOneOverDiameter;
  2116. }
  2117. }
  2118. }
  2119. // ------------------------------------------------------------------------------------------------
  2120. static void BuildFineMipmap(unsigned int _resX, unsigned int _resY, bool _applyFilter, const CUtlVector<colorTexel_t>& _srcTexels, CUtlVector<RGB888_t>* _outTexelsRGB888, CUtlVector<Vector>* _outLinear)
  2121. {
  2122. // At least one of these needs to be non-null, otherwise what are we doing here?
  2123. Assert(_outTexelsRGB888 || _outLinear);
  2124. Assert(!_applyFilter || _outLinear);
  2125. Assert(_srcTexels.Count() == GetTexelCount(_resX, _resY, false));
  2126. int texelCount = GetTexelCount(_resX, _resY, true);
  2127. if (_outTexelsRGB888)
  2128. (*_outTexelsRGB888).EnsureCount(texelCount);
  2129. if (_outLinear)
  2130. (*_outLinear).EnsureCount(GetTexelCount(_resX, _resY, false));
  2131. // This code can take awhile, so minimize the branchiness of the inner-loop.
  2132. if (_applyFilter)
  2133. {
  2134. FilterFineMipmap(_resX, _resY, _srcTexels, _outLinear);
  2135. if ( _outTexelsRGB888 )
  2136. {
  2137. for (int i = 0; i < _srcTexels.Count(); ++i)
  2138. {
  2139. RGBA8888_t encodedColor;
  2140. Vector linearColor = (*_outLinear)[i];
  2141. ConvertLinearToRGBA8888( &linearColor, (unsigned char*)&encodedColor );
  2142. (*_outTexelsRGB888)[i].r = encodedColor.r;
  2143. (*_outTexelsRGB888)[i].g = encodedColor.g;
  2144. (*_outTexelsRGB888)[i].b = encodedColor.b;
  2145. }
  2146. }
  2147. }
  2148. else
  2149. {
  2150. for (int i = 0; i < _srcTexels.Count(); ++i)
  2151. {
  2152. ColorRGBExp32 rgbColor;
  2153. RGBA8888_t encodedColor;
  2154. VectorToColorRGBExp32(_srcTexels[i].m_Color, rgbColor);
  2155. ConvertRGBExp32ToRGBA8888(&rgbColor, (unsigned char*)&encodedColor, (_outLinear ? (&(*_outLinear)[i]) : NULL) );
  2156. // We drop alpha on the floor here, if this were to fire we'd need to consider using a different compressed format.
  2157. Assert(encodedColor.a == 0xFF);
  2158. if (_outTexelsRGB888)
  2159. {
  2160. (*_outTexelsRGB888)[i].r = encodedColor.r;
  2161. (*_outTexelsRGB888)[i].g = encodedColor.g;
  2162. (*_outTexelsRGB888)[i].b = encodedColor.b;
  2163. }
  2164. }
  2165. }
  2166. }
  2167. // ------------------------------------------------------------------------------------------------
  2168. static void FilterCoarserMipmaps(unsigned int _resX, unsigned int _resY, CUtlVector<Vector>* _scratchLinear, CUtlVector<RGB888_t> *_outTexelsRGB888)
  2169. {
  2170. Assert(_outTexelsRGB888);
  2171. int srcResX = _resX;
  2172. int srcResY = _resY;
  2173. int dstResX = max(1, (srcResX >> 1));
  2174. int dstResY = max(1, (srcResY >> 1));
  2175. int dstOffset = GetTexelCount(srcResX, srcResY, false);
  2176. // Build mipmaps here, after being converted to linear space.
  2177. // TODO: Should do better filtering for downsampling. But this will work for now.
  2178. while (srcResX > 1 || srcResY > 1)
  2179. {
  2180. for (int j = 0; j < srcResY; j += 2) {
  2181. for (int i = 0; i < srcResX; i += 2) {
  2182. int srcCol0 = i;
  2183. int srcCol1 = i + 1 > srcResX - 1 ? srcResX - 1 : i + 1;
  2184. int srcRow0 = j;
  2185. int srcRow1 = j + 1 > srcResY - 1 ? srcResY - 1 : j + 1;;
  2186. int dstCol = i >> 1;
  2187. int dstRow = j >> 1;
  2188. const Vector& tl = (*_scratchLinear)[srcCol0 + (srcRow0 * srcResX)];
  2189. const Vector& tr = (*_scratchLinear)[srcCol1 + (srcRow0 * srcResX)];
  2190. const Vector& bl = (*_scratchLinear)[srcCol0 + (srcRow1 * srcResX)];
  2191. const Vector& br = (*_scratchLinear)[srcCol1 + (srcRow1 * srcResX)];
  2192. Vector sample = (tl + tr + bl + br) / 4.0f;
  2193. ConvertLinearToRGBA8888(&sample, (unsigned char*)&(*_outTexelsRGB888)[dstOffset + dstCol + dstRow * dstResX]);
  2194. // Also overwrite the srcBuffer to filter the next loop. This is safe because we won't be reading this source value
  2195. // again during this mipmap level.
  2196. (*_scratchLinear)[dstCol + dstRow * dstResX] = sample;
  2197. }
  2198. }
  2199. srcResX = dstResX;
  2200. srcResY = dstResY;
  2201. dstResX = max(1, (srcResX >> 1));
  2202. dstResY = max(1, (srcResY >> 1));
  2203. dstOffset += GetTexelCount(srcResX, srcResY, false);
  2204. }
  2205. }
  2206. // ------------------------------------------------------------------------------------------------
  2207. static void ConvertToDestinationFormat(unsigned int _resX, unsigned int _resY, ImageFormat _destFmt, const CUtlVector<RGB888_t>& _scratchRBG888, CUtlMemory<byte>* _outTexture)
  2208. {
  2209. const ImageFormat cSrcImageFormat = IMAGE_FORMAT_RGB888;
  2210. // Converts from the scratch RGB888 buffer, which should be fully filled out to the output texture.
  2211. int destMemoryUsage = ImageLoader::GetMemRequired(_resX, _resY, 1, _destFmt, true);
  2212. (*_outTexture).EnsureCapacity(destMemoryUsage);
  2213. int srcResX = _resX;
  2214. int srcResY = _resY;
  2215. int srcOffset = 0;
  2216. int dstOffset = 0;
  2217. // The usual case--that they'll be different.
  2218. if (cSrcImageFormat != _destFmt)
  2219. {
  2220. while (srcResX > 1 || srcResY > 1)
  2221. {
  2222. // Convert this mipmap level.
  2223. ImageLoader::ConvertImageFormat((unsigned char*)(&_scratchRBG888[srcOffset]), cSrcImageFormat, (*_outTexture).Base() + dstOffset, _destFmt, srcResX, srcResY);
  2224. // Then update offsets for the next mipmap level.
  2225. srcOffset += GetTexelCount(srcResX, srcResY, false);
  2226. dstOffset += ImageLoader::GetMemRequired(srcResX, srcResY, 1, _destFmt, false);
  2227. srcResX = max(1, (srcResX >> 1));
  2228. srcResY = max(1, (srcResY >> 1));
  2229. }
  2230. // Do the 1x1 level also.
  2231. ImageLoader::ConvertImageFormat((unsigned char*)_scratchRBG888.Base() + srcOffset, cSrcImageFormat, (*_outTexture).Base() + dstOffset, _destFmt, srcResX, srcResY);
  2232. } else {
  2233. // But sometimes (particularly for debugging) they will be the same.
  2234. Q_memcpy( (*_outTexture).Base(), _scratchRBG888.Base(), destMemoryUsage );
  2235. }
  2236. }
  2237. // ------------------------------------------------------------------------------------------------
  2238. static void ConvertTexelDataToTexture(unsigned int _resX, unsigned int _resY, ImageFormat _destFmt, const CUtlVector<colorTexel_t>& _srcTexels, CUtlMemory<byte>* _outTexture)
  2239. {
  2240. Assert(_outTexture);
  2241. Assert(_srcTexels.Count() == _resX * _resY);
  2242. CUtlVector<RGB888_t> scratchRGB888;
  2243. CUtlVector<Vector> scratchLinear;
  2244. BuildFineMipmap(_resX, _resY, true, _srcTexels, &scratchRGB888, &scratchLinear);
  2245. FilterCoarserMipmaps(_resX, _resY, &scratchLinear, &scratchRGB888 );
  2246. ConvertToDestinationFormat(_resX, _resY, _destFmt, scratchRGB888, _outTexture);
  2247. }
  2248. // ------------------------------------------------------------------------------------------------
  2249. static void DumpLightmapLinear( const char* _dstFilename, const CUtlVector<colorTexel_t>& _srcTexels, int _width, int _height )
  2250. {
  2251. CUtlVector< Vector > linearFloats;
  2252. CUtlVector< BGR888_t > linearBuffer;
  2253. BuildFineMipmap( _width, _height, true, _srcTexels, NULL, &linearFloats );
  2254. linearBuffer.SetCount( linearFloats.Count() );
  2255. for ( int i = 0; i < linearFloats.Count(); ++i ) {
  2256. linearBuffer[i].b = RoundFloatToByte(linearFloats[i].z * 255.0f);
  2257. linearBuffer[i].g = RoundFloatToByte(linearFloats[i].y * 255.0f);
  2258. linearBuffer[i].r = RoundFloatToByte(linearFloats[i].x * 255.0f);
  2259. }
  2260. TGAWriter::WriteTGAFile( _dstFilename, _width, _height, IMAGE_FORMAT_BGR888, (uint8*)(linearBuffer.Base()), _width * ImageLoader::SizeInBytes(IMAGE_FORMAT_BGR888) );
  2261. }