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.

1036 lines
31 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 "Bsplib.h"
  13. #include "GameBSPFile.h"
  14. #include "UtlBuffer.h"
  15. #include "utlvector.h"
  16. #include "CModel.h"
  17. #include "studio.h"
  18. #include "pacifier.h"
  19. #include "vraddetailprops.h"
  20. #include "mathlib/halton.h"
  21. #include "messbuf.h"
  22. #include "byteswap.h"
  23. bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf );
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Writes a glview text file containing the collision surface in question
  26. // Input : *pCollide -
  27. // *pFilename -
  28. //-----------------------------------------------------------------------------
  29. void DumpRayToGlView( Ray_t const& ray, float dist, Vector* pColor, const char *pFilename )
  30. {
  31. Vector dir = ray.m_Delta;
  32. float len = VectorNormalize(dir);
  33. if (len < 1e-3)
  34. return;
  35. Vector up( 0, 0, 1 );
  36. Vector crossDir;
  37. if (fabs(DotProduct(up, dir)) - 1.0f < -1e-3 )
  38. {
  39. CrossProduct( dir, up, crossDir );
  40. VectorNormalize(crossDir);
  41. }
  42. else
  43. {
  44. up.Init( 0, 1, 0 );
  45. CrossProduct( dir, up, crossDir );
  46. VectorNormalize(crossDir);
  47. }
  48. Vector end;
  49. Vector start1, start2;
  50. VectorMA( ray.m_Start, dist, ray.m_Delta, end );
  51. VectorMA( ray.m_Start, -2, crossDir, start1 );
  52. VectorMA( ray.m_Start, 2, crossDir, start2 );
  53. FileHandle_t fp = g_pFileSystem->Open( pFilename, "a" );
  54. int vert = 0;
  55. CmdLib_FPrintf( fp, "3\n" );
  56. CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start1.x, start1.y, start1.z,
  57. pColor->x, pColor->y, pColor->z );
  58. vert++;
  59. CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start2.x, start2.y, start2.z,
  60. pColor->x, pColor->y, pColor->z );
  61. vert++;
  62. CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", end.x, end.y, end.z,
  63. pColor->x, pColor->y, pColor->z );
  64. vert++;
  65. g_pFileSystem->Close( fp );
  66. }
  67. //-----------------------------------------------------------------------------
  68. // This puppy is used to construct the game lumps
  69. //-----------------------------------------------------------------------------
  70. static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpLDR;
  71. static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpHDR;
  72. static CUtlVector<DetailPropLightstylesLump_t> *s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
  73. //-----------------------------------------------------------------------------
  74. // An amount to add to each model to get to the model center
  75. //-----------------------------------------------------------------------------
  76. CUtlVector<Vector> g_ModelCenterOffset;
  77. CUtlVector<Vector> g_SpriteCenterOffset;
  78. void VRadDetailProps_SetHDRMode( bool bHDR )
  79. {
  80. if( bHDR )
  81. {
  82. s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpHDR;
  83. }
  84. else
  85. {
  86. s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
  87. }
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Finds ambient sky lights
  91. //-----------------------------------------------------------------------------
  92. static directlight_t* FindAmbientSkyLight()
  93. {
  94. static directlight_t *s_pCachedSkylight = NULL;
  95. // Don't keep searching for the same light.
  96. if ( !s_pCachedSkylight )
  97. {
  98. // find any ambient lights
  99. directlight_t* dl;
  100. for (dl = activelights; dl != 0; dl = dl->next)
  101. {
  102. if (dl->light.type == emit_skyambient)
  103. {
  104. s_pCachedSkylight = dl;
  105. break;
  106. }
  107. }
  108. }
  109. return s_pCachedSkylight;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Compute world center of a prop
  113. //-----------------------------------------------------------------------------
  114. static void ComputeWorldCenter( DetailObjectLump_t& prop, Vector& center, Vector& normal )
  115. {
  116. // Transform the offset into world space
  117. Vector forward, right;
  118. AngleVectors( prop.m_Angles, &forward, &right, &normal );
  119. VectorCopy( prop.m_Origin, center );
  120. // FIXME: Take orientation into account?
  121. switch (prop.m_Type )
  122. {
  123. case DETAIL_PROP_TYPE_MODEL:
  124. VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].x, forward, center );
  125. VectorMA( center, -g_ModelCenterOffset[prop.m_DetailModel].y, right, center );
  126. VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].z, normal, center );
  127. break;
  128. case DETAIL_PROP_TYPE_SPRITE:
  129. Vector vecOffset;
  130. VectorMultiply( g_SpriteCenterOffset[prop.m_DetailModel], prop.m_flScale, vecOffset );
  131. VectorMA( center, vecOffset.x, forward, center );
  132. VectorMA( center, -vecOffset.y, right, center );
  133. VectorMA( center, vecOffset.z, normal, center );
  134. break;
  135. }
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Computes max direct lighting for a single detal prop
  139. //-----------------------------------------------------------------------------
  140. static void ComputeMaxDirectLighting( DetailObjectLump_t& prop, Vector* maxcolor, int iThread )
  141. {
  142. // The max direct lighting must be along the direction to one
  143. // of the static lights....
  144. Vector origin, normal;
  145. ComputeWorldCenter( prop, origin, normal );
  146. if ( !origin.IsValid() || !normal.IsValid() )
  147. {
  148. static bool s_Warned = false;
  149. if ( !s_Warned )
  150. {
  151. Warning("WARNING: Bogus detail props encountered!\n" );
  152. s_Warned = true;
  153. }
  154. // fill with debug color
  155. for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
  156. {
  157. maxcolor[i].Init(1,0,0);
  158. }
  159. return;
  160. }
  161. int cluster = ClusterFromPoint(origin);
  162. Vector delta;
  163. CUtlVector< directlight_t* > lights;
  164. CUtlVector< Vector > directions;
  165. directlight_t* dl;
  166. for (dl = activelights; dl != 0; dl = dl->next)
  167. {
  168. // skyambient doesn't affect dlights..
  169. if (dl->light.type == emit_skyambient)
  170. continue;
  171. // is this lights cluster visible?
  172. if ( PVSCheck( dl->pvs, cluster ) )
  173. {
  174. lights.AddToTail(dl);
  175. VectorSubtract( dl->light.origin, origin, delta );
  176. VectorNormalize( delta );
  177. directions.AddToTail( delta );
  178. }
  179. }
  180. // Find the max illumination
  181. int i;
  182. for ( i = 0; i < MAX_LIGHTSTYLES; ++i)
  183. {
  184. maxcolor[i].Init(0,0,0);
  185. }
  186. // NOTE: See version 10 for a method where we choose a normal based on whichever
  187. // one produces the maximum possible illumination. This appeared to work better on
  188. // e3_town, so I'm trying it now; hopefully it'll be good for all cases.
  189. int j;
  190. for ( j = 0; j < lights.Count(); ++j)
  191. {
  192. dl = lights[j];
  193. SSE_sampleLightOutput_t out;
  194. FourVectors origin4;
  195. FourVectors normal4;
  196. origin4.DuplicateVector( origin );
  197. normal4.DuplicateVector( normal );
  198. GatherSampleLightSSE ( out, dl, -1, origin4, &normal4, 1, iThread );
  199. VectorMA( maxcolor[dl->light.style], out.m_flFalloff.m128_f32[0] * out.m_flDot[0].m128_f32[0], dl->light.intensity, maxcolor[dl->light.style] );
  200. }
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Computes the ambient term from a particular surface
  204. //-----------------------------------------------------------------------------
  205. static void ComputeAmbientFromSurface( dface_t* pFace, directlight_t* pSkylight,
  206. Vector& radcolor )
  207. {
  208. texinfo_t* pTex = &texinfo[pFace->texinfo];
  209. if (pTex)
  210. {
  211. // If we hit the sky, use the sky ambient
  212. if (pTex->flags & SURF_SKY)
  213. {
  214. if (pSkylight)
  215. {
  216. // add in sky ambient
  217. VectorDivide( pSkylight->light.intensity, 255.0f, radcolor );
  218. }
  219. }
  220. else
  221. {
  222. VectorMultiply( radcolor, dtexdata[pTex->texdata].reflectivity, radcolor );
  223. }
  224. }
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Computes the lightmap color at a particular point
  228. //-----------------------------------------------------------------------------
  229. static void ComputeLightmapColorFromAverage( dface_t* pFace, directlight_t* pSkylight, float scale, Vector pColor[MAX_LIGHTSTYLES] )
  230. {
  231. texinfo_t* pTex = &texinfo[pFace->texinfo];
  232. if (pTex->flags & SURF_SKY)
  233. {
  234. if (pSkylight)
  235. {
  236. // add in sky ambient
  237. Vector amb = pSkylight->light.intensity / 255.0f;
  238. pColor[0] += amb * scale;
  239. }
  240. return;
  241. }
  242. for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
  243. {
  244. ColorRGBExp32* pAvgColor = dface_AvgLightColor( pFace, maps );
  245. // this code expects values from [0..1] not [0..255]
  246. Vector color;
  247. color[0] = TexLightToLinear( pAvgColor->r, pAvgColor->exponent );
  248. color[1] = TexLightToLinear( pAvgColor->g, pAvgColor->exponent );
  249. color[2] = TexLightToLinear( pAvgColor->b, pAvgColor->exponent );
  250. ComputeAmbientFromSurface( pFace, pSkylight, color );
  251. int style = pFace->styles[maps];
  252. pColor[style] += color * scale;
  253. }
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Returns true if the surface has bumped lightmaps
  257. //-----------------------------------------------------------------------------
  258. static bool SurfHasBumpedLightmaps( dface_t *pSurf )
  259. {
  260. bool hasBumpmap = false;
  261. if( ( texinfo[pSurf->texinfo].flags & SURF_BUMPLIGHT ) &&
  262. ( !( texinfo[pSurf->texinfo].flags & SURF_NOLIGHT ) ) )
  263. {
  264. hasBumpmap = true;
  265. }
  266. return hasBumpmap;
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Computes the lightmap color at a particular point
  270. //-----------------------------------------------------------------------------
  271. static void ComputeLightmapColorPointSample( dface_t* pFace, directlight_t* pSkylight, Vector2D const& luv, float scale, Vector pColor[MAX_LIGHTSTYLES] )
  272. {
  273. // face unaffected by light
  274. if (pFace->lightofs == -1 )
  275. return;
  276. int smax = ( pFace->m_LightmapTextureSizeInLuxels[0] ) + 1;
  277. int tmax = ( pFace->m_LightmapTextureSizeInLuxels[1] ) + 1;
  278. // luv is in the space of the accumulated lightmap page; we need to convert
  279. // it to be in the space of the surface
  280. int ds = clamp( (int)luv.x, 0, smax-1 );
  281. int dt = clamp( (int)luv.y, 0, tmax-1 );
  282. int offset = smax * tmax;
  283. if ( SurfHasBumpedLightmaps( pFace ) )
  284. offset *= ( NUM_BUMP_VECTS + 1 );
  285. ColorRGBExp32* pLightmap = (ColorRGBExp32*)&pdlightdata->Base()[pFace->lightofs];
  286. pLightmap += dt * smax + ds;
  287. for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
  288. {
  289. int style = pFace->styles[maps];
  290. Vector color;
  291. color[0] = TexLightToLinear( pLightmap->r, pLightmap->exponent );
  292. color[1] = TexLightToLinear( pLightmap->g, pLightmap->exponent );
  293. color[2] = TexLightToLinear( pLightmap->b, pLightmap->exponent );
  294. ComputeAmbientFromSurface( pFace, pSkylight, color );
  295. pColor[style] += color * scale;
  296. pLightmap += offset;
  297. }
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Tests a particular node
  301. //-----------------------------------------------------------------------------
  302. class CLightSurface : public IBSPNodeEnumerator
  303. {
  304. public:
  305. CLightSurface(int iThread) : m_pSurface(0), m_HitFrac(1.0f), m_bHasLuxel(false), m_iThread(iThread) {}
  306. // call back with a node and a context
  307. bool EnumerateNode( int node, Ray_t const& ray, float f, int context )
  308. {
  309. dface_t* pSkySurface = 0;
  310. // Compute the actual point
  311. Vector pt;
  312. VectorMA( ray.m_Start, f, ray.m_Delta, pt );
  313. dnode_t* pNode = &dnodes[node];
  314. dface_t* pFace = &g_pFaces[pNode->firstface];
  315. for (int i=0 ; i < pNode->numfaces ; ++i, ++pFace)
  316. {
  317. // Don't take into account faces that are int a leaf
  318. if ( !pFace->onNode )
  319. continue;
  320. // Don't test displacement faces
  321. if ( pFace->dispinfo != -1 )
  322. continue;
  323. texinfo_t* pTex = &texinfo[pFace->texinfo];
  324. // Don't immediately return when we hit sky;
  325. // we may actually hit another surface
  326. if (pTex->flags & SURF_SKY)
  327. {
  328. if (TestPointAgainstSkySurface( pt, pFace ))
  329. {
  330. pSkySurface = pFace;
  331. }
  332. continue;
  333. }
  334. if (TestPointAgainstSurface( pt, pFace, pTex ))
  335. {
  336. m_HitFrac = f;
  337. m_pSurface = pFace;
  338. m_bHasLuxel = true;
  339. return false;
  340. }
  341. }
  342. // if we hit a sky surface, return it
  343. m_pSurface = pSkySurface;
  344. return (m_pSurface == 0);
  345. }
  346. // call back with a leaf and a context
  347. virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context )
  348. {
  349. bool hit = false;
  350. dleaf_t* pLeaf = &dleafs[leaf];
  351. for (int i=0 ; i < pLeaf->numleaffaces ; ++i)
  352. {
  353. Assert( pLeaf->firstleafface + i < numleaffaces );
  354. Assert( dleaffaces[pLeaf->firstleafface + i] < numfaces );
  355. dface_t* pFace = &g_pFaces[dleaffaces[pLeaf->firstleafface + i]];
  356. // Don't test displacement faces; we need to check another list
  357. if ( pFace->dispinfo != -1 )
  358. continue;
  359. // Don't take into account faces that are on a node
  360. if ( pFace->onNode )
  361. continue;
  362. // Find intersection point against detail brushes
  363. texinfo_t* pTex = &texinfo[pFace->texinfo];
  364. dplane_t* pPlane = &dplanes[pFace->planenum];
  365. // Backface cull...
  366. if (DotProduct( pPlane->normal, ray.m_Delta ) > 0)
  367. continue;
  368. float startDotN = DotProduct( ray.m_Start, pPlane->normal );
  369. float deltaDotN = DotProduct( ray.m_Delta, pPlane->normal );
  370. float front = startDotN + start * deltaDotN - pPlane->dist;
  371. float back = startDotN + end * deltaDotN - pPlane->dist;
  372. int side = front < 0;
  373. // Blow it off if it doesn't split the plane...
  374. if ( (back < 0) == side )
  375. continue;
  376. // Don't test a surface that is farther away from the closest found intersection
  377. float f = front / (front-back);
  378. float mid = start * (1.0f - f) + end * f;
  379. if (mid >= m_HitFrac)
  380. continue;
  381. Vector pt;
  382. VectorMA( ray.m_Start, mid, ray.m_Delta, pt );
  383. if (TestPointAgainstSurface( pt, pFace, pTex ))
  384. {
  385. m_HitFrac = mid;
  386. m_pSurface = pFace;
  387. hit = true;
  388. m_bHasLuxel = true;
  389. }
  390. }
  391. // Now try to clip against all displacements in the leaf
  392. float dist;
  393. Vector2D luxelCoord;
  394. dface_t *pDispFace;
  395. StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[m_iThread], ray, leaf, dist, pDispFace, luxelCoord );
  396. if (dist < m_HitFrac)
  397. {
  398. m_HitFrac = dist;
  399. m_pSurface = pDispFace;
  400. Vector2DCopy( luxelCoord, m_LuxelCoord );
  401. hit = true;
  402. m_bHasLuxel = true;
  403. }
  404. return !hit;
  405. }
  406. bool FindIntersection( Ray_t const& ray )
  407. {
  408. StaticDispMgr()->StartRayTest( s_DispTested[m_iThread] );
  409. return !EnumerateNodesAlongRay( ray, this, 0 );
  410. }
  411. private:
  412. bool TestPointAgainstSurface( Vector const& pt, dface_t* pFace, texinfo_t* pTex )
  413. {
  414. // no lightmaps on this surface? punt...
  415. // FIXME: should be water surface?
  416. if (pTex->flags & SURF_NOLIGHT)
  417. return false;
  418. // See where in lightmap space our intersection point is
  419. float s, t;
  420. s = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[0]) +
  421. pTex->lightmapVecsLuxelsPerWorldUnits[0][3];
  422. t = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[1]) +
  423. pTex->lightmapVecsLuxelsPerWorldUnits[1][3];
  424. // Not in the bounds of our lightmap? punt...
  425. if( s < pFace->m_LightmapTextureMinsInLuxels[0] || t < pFace->m_LightmapTextureMinsInLuxels[1] )
  426. return false;
  427. // assuming a square lightmap (FIXME: which ain't always the case),
  428. // lets see if it lies in that rectangle. If not, punt...
  429. float ds = s - pFace->m_LightmapTextureMinsInLuxels[0];
  430. float dt = t - pFace->m_LightmapTextureMinsInLuxels[1];
  431. if( ds > pFace->m_LightmapTextureSizeInLuxels[0] || dt > pFace->m_LightmapTextureSizeInLuxels[1] )
  432. return false;
  433. m_LuxelCoord.x = ds;
  434. m_LuxelCoord.y = dt;
  435. return true;
  436. }
  437. bool TestPointAgainstSkySurface( Vector const &pt, dface_t *pFace )
  438. {
  439. // Create sky face winding.
  440. winding_t *pWinding = WindingFromFace( pFace, Vector( 0.0f, 0.0f, 0.0f ) );
  441. // Test point in winding. (Since it is at the node, it is in the plane.)
  442. bool bRet = PointInWinding( pt, pWinding );
  443. FreeWinding( pWinding );
  444. return bRet;
  445. }
  446. public:
  447. int m_iThread;
  448. dface_t* m_pSurface;
  449. float m_HitFrac;
  450. Vector2D m_LuxelCoord;
  451. bool m_bHasLuxel;
  452. };
  453. bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal )
  454. {
  455. pFraction[0] = 1.0f;
  456. Ray_t ray;
  457. ray.Init( start, end, vec3_origin, vec3_origin );
  458. CBaseTrace trace;
  459. if ( TraceLeafBrushes( leafIndex, start, end, trace ) != 1.0f )
  460. {
  461. pFraction[0] = trace.fraction;
  462. *pNormal = trace.plane.normal;
  463. }
  464. else
  465. {
  466. Assert(!trace.startsolid && !trace.allsolid);
  467. }
  468. StaticDispMgr()->StartRayTest( s_DispTested[iThread] );
  469. // Now try to clip against all displacements in the leaf
  470. float dist;
  471. Vector normal;
  472. StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[iThread], ray, leafIndex, dist, &normal );
  473. if ( dist < pFraction[0] )
  474. {
  475. pFraction[0] = dist;
  476. *pNormal = normal;
  477. }
  478. return pFraction[0] != 1.0f ? true : false;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Computes ambient lighting along a specified ray.
  482. // Ray represents a cone, tanTheta is the tan of the inner cone angle
  483. //-----------------------------------------------------------------------------
  484. void CalcRayAmbientLighting( int iThread, const Vector &vStart, const Vector &vEnd, float tanTheta, Vector color[MAX_LIGHTSTYLES] )
  485. {
  486. Ray_t ray;
  487. ray.Init( vStart, vEnd, vec3_origin, vec3_origin );
  488. directlight_t *pSkyLight = FindAmbientSkyLight();
  489. CLightSurface surfEnum(iThread);
  490. if (!surfEnum.FindIntersection( ray ))
  491. return;
  492. // compute the approximate radius of a circle centered around the intersection point
  493. float dist = ray.m_Delta.Length() * tanTheta * surfEnum.m_HitFrac;
  494. // until 20" we use the point sample, then blend in the average until we're covering 40"
  495. // This is attempting to model the ray as a cone - in the ideal case we'd simply sample all
  496. // luxels in the intersection of the cone with the surface. Since we don't have surface
  497. // neighbor information computed we'll just approximate that sampling with a blend between
  498. // a point sample and the face average.
  499. // This yields results that are similar in that aliasing is reduced at distance while
  500. // point samples provide accuracy for intersections with near geometry
  501. float scaleAvg = RemapValClamped( dist, 20, 40, 0.0f, 1.0f );
  502. if ( !surfEnum.m_bHasLuxel )
  503. {
  504. // don't have luxel UV, so just use average sample
  505. scaleAvg = 1.0;
  506. }
  507. float scaleSample = 1.0f - scaleAvg;
  508. if (scaleAvg != 0)
  509. {
  510. ComputeLightmapColorFromAverage( surfEnum.m_pSurface, pSkyLight, scaleAvg, color );
  511. }
  512. if (scaleSample != 0)
  513. {
  514. ComputeLightmapColorPointSample( surfEnum.m_pSurface, pSkyLight, surfEnum.m_LuxelCoord, scaleSample, color );
  515. }
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Compute ambient lighting component at specified position.
  519. //-----------------------------------------------------------------------------
  520. static void ComputeAmbientLightingAtPoint( int iThread, const Vector &origin, Vector radcolor[NUMVERTEXNORMALS], Vector color[MAX_LIGHTSTYLES] )
  521. {
  522. // NOTE: I'm not dealing with shadow-casting static props here
  523. // This is for speed, although we can add it if it turns out to
  524. // be important
  525. // sample world by casting N rays distributed across a sphere
  526. Vector upend;
  527. int j;
  528. for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
  529. {
  530. color[j].Init( 0,0,0 );
  531. }
  532. float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
  533. for (int i = 0; i < NUMVERTEXNORMALS; i++)
  534. {
  535. VectorMA( origin, COORD_EXTENT * 1.74, g_anorms[i], upend );
  536. // Now that we've got a ray, see what surface we've hit
  537. CalcRayAmbientLighting( iThread, origin, upend, tanTheta, color );
  538. // DumpRayToGlView( ray, surfEnum.m_HitFrac, &color[0], "test.out" );
  539. }
  540. for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
  541. {
  542. VectorMultiply( color[j], 255.0f / (float)NUMVERTEXNORMALS, color[j] );
  543. }
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Trace hemispherical rays from a vertex, accumulating indirect
  547. // sources at each ray termination.
  548. //-----------------------------------------------------------------------------
  549. void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
  550. int iThread, bool force_fast, bool bIgnoreNormals )
  551. {
  552. Ray_t ray;
  553. CLightSurface surfEnum(iThread);
  554. outColor.Init();
  555. int nSamples = NUMVERTEXNORMALS;
  556. if ( do_fast || force_fast )
  557. nSamples /= 4;
  558. else
  559. nSamples *= g_flSkySampleScale;
  560. float totalDot = 0;
  561. DirectionalSampler_t sampler;
  562. for (int j = 0; j < nSamples; j++)
  563. {
  564. Vector samplingNormal = sampler.NextValue();
  565. float dot;
  566. if ( bIgnoreNormals )
  567. dot = (0.7071/2);
  568. else
  569. dot = DotProduct( normal, samplingNormal );
  570. if ( dot <= EQUAL_EPSILON )
  571. {
  572. // reject angles behind our plane
  573. continue;
  574. }
  575. totalDot += dot;
  576. // trace to determine surface
  577. Vector vEnd;
  578. VectorScale( samplingNormal, MAX_TRACE_LENGTH, vEnd );
  579. VectorAdd( position, vEnd, vEnd );
  580. ray.Init( position, vEnd, vec3_origin, vec3_origin );
  581. if ( !surfEnum.FindIntersection( ray ) )
  582. continue;
  583. // get color from surface lightmap
  584. texinfo_t* pTex = &texinfo[surfEnum.m_pSurface->texinfo];
  585. if ( !pTex || pTex->flags & SURF_SKY )
  586. {
  587. // ignore contribution from sky
  588. // sky ambient already accounted for during direct pass
  589. continue;
  590. }
  591. if ( surfEnum.m_pSurface->styles[0] == 255 || surfEnum.m_pSurface->lightofs < 0 )
  592. {
  593. // no light affects this face
  594. continue;
  595. }
  596. Vector lightmapColor;
  597. if ( !surfEnum.m_bHasLuxel )
  598. {
  599. ColorRGBExp32* pAvgLightmapColor = dface_AvgLightColor( surfEnum.m_pSurface, 0 );
  600. ColorRGBExp32ToVector( *pAvgLightmapColor, lightmapColor );
  601. }
  602. else
  603. {
  604. // get color from displacement
  605. int smax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[0] ) + 1;
  606. int tmax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[1] ) + 1;
  607. // luxelcoord is in the space of the accumulated lightmap page; we need to convert
  608. // it to be in the space of the surface
  609. int ds = clamp( (int)surfEnum.m_LuxelCoord.x, 0, smax-1 );
  610. int dt = clamp( (int)surfEnum.m_LuxelCoord.y, 0, tmax-1 );
  611. ColorRGBExp32* pLightmap = (ColorRGBExp32*)&(*pdlightdata)[surfEnum.m_pSurface->lightofs];
  612. pLightmap += dt * smax + ds;
  613. ColorRGBExp32ToVector( *pLightmap, lightmapColor );
  614. }
  615. float invLengthSqr = 1.0f / (1.0f + ((vEnd - position) * surfEnum.m_HitFrac / 128.0).LengthSqr());
  616. // Include falloff using invsqrlaw.
  617. VectorMultiply( lightmapColor, invLengthSqr * dtexdata[pTex->texdata].reflectivity, lightmapColor );
  618. VectorAdd( outColor, lightmapColor, outColor );
  619. }
  620. if ( totalDot )
  621. {
  622. VectorScale( outColor, 1.0f/totalDot, outColor );
  623. }
  624. }
  625. static void ComputeAmbientLighting( int iThread, DetailObjectLump_t& prop, Vector color[MAX_LIGHTSTYLES] )
  626. {
  627. Vector origin, normal;
  628. ComputeWorldCenter( prop, origin, normal );
  629. if ( !origin.IsValid() || !normal.IsValid() )
  630. {
  631. static bool s_Warned = false;
  632. if ( !s_Warned )
  633. {
  634. Warning("WARNING: Bogus detail props encountered!\n" );
  635. s_Warned = true;
  636. }
  637. // fill with debug color
  638. for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
  639. {
  640. color[i].Init(1,0,0);
  641. }
  642. return;
  643. }
  644. Vector radcolor[NUMVERTEXNORMALS];
  645. ComputeAmbientLightingAtPoint( iThread, origin, radcolor, color );
  646. }
  647. //-----------------------------------------------------------------------------
  648. // Computes lighting for a single detal prop
  649. //-----------------------------------------------------------------------------
  650. static void ComputeLighting( DetailObjectLump_t& prop, int iThread )
  651. {
  652. // We're going to take the maximum of the ambient lighting and
  653. // the strongest directional light. This works because we're assuming
  654. // the props will have built-in faked lighting.
  655. Vector directColor[MAX_LIGHTSTYLES];
  656. Vector ambColor[MAX_LIGHTSTYLES];
  657. // Get the max influence of all direct lights
  658. ComputeMaxDirectLighting( prop, directColor, iThread );
  659. // Get the ambient lighting + lightstyles
  660. ComputeAmbientLighting( iThread, prop, ambColor );
  661. // Base lighting
  662. Vector totalColor;
  663. VectorAdd( directColor[0], ambColor[0], totalColor );
  664. VectorToColorRGBExp32( totalColor, prop.m_Lighting );
  665. bool hasLightstyles = false;
  666. prop.m_LightStyleCount = 0;
  667. // lightstyles
  668. for (int i = 1; i < MAX_LIGHTSTYLES; ++i )
  669. {
  670. VectorAdd( directColor[i], ambColor[i], totalColor );
  671. totalColor *= 0.5f;
  672. if ((totalColor[0] != 0.0f) || (totalColor[1] != 0.0f) ||
  673. (totalColor[2] != 0.0f) )
  674. {
  675. if (!hasLightstyles)
  676. {
  677. prop.m_LightStyles = s_pDetailPropLightStyleLump->Size();
  678. hasLightstyles = true;
  679. }
  680. int j = s_pDetailPropLightStyleLump->AddToTail();
  681. VectorToColorRGBExp32( totalColor, (*s_pDetailPropLightStyleLump)[j].m_Lighting );
  682. (*s_pDetailPropLightStyleLump)[j].m_Style = i;
  683. ++prop.m_LightStyleCount;
  684. }
  685. }
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Unserialization
  689. //-----------------------------------------------------------------------------
  690. static void UnserializeModelDict( CUtlBuffer& buf )
  691. {
  692. // Get origin offset for each model...
  693. int count = buf.GetInt();
  694. while ( --count >= 0 )
  695. {
  696. DetailObjectDictLump_t lump;
  697. buf.Get( &lump, sizeof(DetailObjectDictLump_t) );
  698. int i = g_ModelCenterOffset.AddToTail();
  699. CUtlBuffer mdlbuf;
  700. if (LoadStudioModel( lump.m_Name, mdlbuf ))
  701. {
  702. studiohdr_t* pHdr = (studiohdr_t*)mdlbuf.Base();
  703. VectorAdd( pHdr->hull_min, pHdr->hull_max, g_ModelCenterOffset[i] );
  704. g_ModelCenterOffset[i] *= 0.5f;
  705. }
  706. else
  707. {
  708. g_ModelCenterOffset[i].Init(0,0,0);
  709. }
  710. }
  711. }
  712. static void UnserializeSpriteDict( CUtlBuffer& buf )
  713. {
  714. // Get origin offset for each model...
  715. int count = buf.GetInt();
  716. while ( --count >= 0 )
  717. {
  718. DetailSpriteDictLump_t lump;
  719. buf.Get( &lump, sizeof(DetailSpriteDictLump_t) );
  720. // For these sprites, x goes out the front, y right, z up
  721. int i = g_SpriteCenterOffset.AddToTail();
  722. g_SpriteCenterOffset[i].x = 0.0f;
  723. g_SpriteCenterOffset[i].y = lump.m_LR.x + lump.m_UL.x;
  724. g_SpriteCenterOffset[i].z = lump.m_LR.y + lump.m_UL.y;
  725. g_SpriteCenterOffset[i] *= 0.5f;
  726. }
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Unserializes the detail props
  730. //-----------------------------------------------------------------------------
  731. static int UnserializeDetailProps( DetailObjectLump_t*& pProps )
  732. {
  733. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
  734. if (g_GameLumps.GetGameLumpVersion(handle) != GAMELUMP_DETAIL_PROPS_VERSION)
  735. return 0;
  736. // Unserialize
  737. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
  738. UnserializeModelDict( buf );
  739. UnserializeSpriteDict( buf );
  740. // Now we're pointing to the detail prop data
  741. // This actually works because the scope of the game lump data
  742. // is global and the buf was just pointing to it.
  743. int count = buf.GetInt();
  744. if (count)
  745. {
  746. pProps = (DetailObjectLump_t*)buf.PeekGet();
  747. }
  748. else
  749. {
  750. pProps = 0;
  751. }
  752. return count;
  753. }
  754. //-----------------------------------------------------------------------------
  755. // Writes the detail lighting lump
  756. //-----------------------------------------------------------------------------
  757. static void WriteDetailLightingLump( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
  758. {
  759. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(lumpID);
  760. if (handle != g_GameLumps.InvalidGameLump())
  761. g_GameLumps.DestroyGameLump(handle);
  762. int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
  763. int lumpsize = lightsize + sizeof(int);
  764. handle = g_GameLumps.CreateGameLump( lumpID, lumpsize, 0, lumpVersion );
  765. // Serialize the data
  766. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), lumpsize );
  767. buf.PutInt( lumpData.Size() );
  768. if (lightsize)
  769. buf.Put( lumpData.Base(), lightsize );
  770. }
  771. static void WriteDetailLightingLumps( void )
  772. {
  773. WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
  774. WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
  775. }
  776. // need to do this so that if we are building HDR data, the LDR data is intact, and vice versa.s
  777. void UnserializeDetailPropLighting( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
  778. {
  779. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( lumpID );
  780. if( handle == g_GameLumps.InvalidGameLump() )
  781. {
  782. return;
  783. }
  784. if (g_GameLumps.GetGameLumpVersion(handle) != lumpVersion)
  785. return;
  786. // Unserialize
  787. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
  788. int count = buf.GetInt();
  789. if( !count )
  790. {
  791. return;
  792. }
  793. lumpData.SetCount( count );
  794. int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
  795. buf.Get( lumpData.Base(), lightsize );
  796. }
  797. DetailObjectLump_t *g_pMPIDetailProps = NULL;
  798. void VMPI_ProcessDetailPropWU( int iThread, int iWorkUnit, MessageBuffer *pBuf )
  799. {
  800. CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
  801. DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
  802. ComputeLighting( prop, iThread );
  803. // Send the results back...
  804. pBuf->write( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
  805. pBuf->write( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
  806. pBuf->write( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
  807. for ( int i=0; i < prop.m_LightStyleCount; i++ )
  808. {
  809. DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
  810. pBuf->write( &l->m_Lighting, sizeof( l->m_Lighting ) );
  811. pBuf->write( &l->m_Style, sizeof( l->m_Style ) );
  812. }
  813. }
  814. void VMPI_ReceiveDetailPropWU( int iWorkUnit, MessageBuffer *pBuf, int iWorker )
  815. {
  816. CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
  817. DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
  818. pBuf->read( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
  819. pBuf->read( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
  820. pBuf->read( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
  821. pDetailPropLump->EnsureCount( prop.m_LightStyles + prop.m_LightStyleCount );
  822. for ( int i=0; i < prop.m_LightStyleCount; i++ )
  823. {
  824. DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
  825. pBuf->read( &l->m_Lighting, sizeof( l->m_Lighting ) );
  826. pBuf->read( &l->m_Style, sizeof( l->m_Style ) );
  827. }
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Computes lighting for the detail props
  831. //-----------------------------------------------------------------------------
  832. void ComputeDetailPropLighting( int iThread )
  833. {
  834. // illuminate them all
  835. DetailObjectLump_t* pProps;
  836. int count = UnserializeDetailProps( pProps );
  837. if (!count)
  838. return;
  839. // unserialize the lump that we aren't computing.
  840. if( g_bHDR )
  841. {
  842. UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
  843. }
  844. else
  845. {
  846. UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
  847. }
  848. StartPacifier("Computing detail prop lighting : ");
  849. for (int i = 0; i < count; ++i)
  850. {
  851. UpdatePacifier( (float)i / (float)count );
  852. ComputeLighting( pProps[i], iThread );
  853. }
  854. // Write detail prop lightstyle lump...
  855. WriteDetailLightingLumps();
  856. EndPacifier( true );
  857. }