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.

708 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "vrad.h"
  7. #include "leaf_ambient_lighting.h"
  8. #include "bsplib.h"
  9. #include "vraddetailprops.h"
  10. #include "mathlib/anorms.h"
  11. #include "pacifier.h"
  12. #include "coordsize.h"
  13. #include "vstdlib/random.h"
  14. #include "bsptreedata.h"
  15. #include "messbuf.h"
  16. #include "vmpi.h"
  17. #include "vmpi_distribute_work.h"
  18. static TableVector g_BoxDirections[6] =
  19. {
  20. { 1, 0, 0 },
  21. { -1, 0, 0 },
  22. { 0, 1, 0 },
  23. { 0, -1, 0 },
  24. { 0, 0, 1 },
  25. { 0, 0, -1 },
  26. };
  27. static void ComputeAmbientFromSurface( dface_t *surfID, dworldlight_t* pSkylight,
  28. Vector& radcolor )
  29. {
  30. if ( !surfID )
  31. return;
  32. texinfo_t *pTexInfo = &texinfo[surfID->texinfo];
  33. // If we hit the sky, use the sky ambient
  34. if ( pTexInfo->flags & SURF_SKY )
  35. {
  36. if ( pSkylight )
  37. {
  38. // add in sky ambient
  39. VectorCopy( pSkylight->intensity, radcolor );
  40. }
  41. }
  42. else
  43. {
  44. Vector reflectivity = dtexdata[pTexInfo->texdata].reflectivity;
  45. VectorMultiply( radcolor, reflectivity, radcolor );
  46. }
  47. }
  48. // TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
  49. float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
  50. {
  51. float dot, dot2;
  52. Assert( wl->type == emit_surface );
  53. dot = DotProduct( snormal, delta );
  54. if (dot < 0)
  55. return 0;
  56. dot2 = -DotProduct (delta, lnormal);
  57. if (dot2 <= ON_EPSILON/10)
  58. return 0; // behind light surface
  59. return dot * dot2;
  60. }
  61. // TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
  62. float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta )
  63. {
  64. Assert( wl->type == emit_surface );
  65. // Cull out stuff that's too far
  66. if (wl->radius != 0)
  67. {
  68. if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
  69. return 0.0f;
  70. }
  71. return InvRSquared(delta);
  72. }
  73. void AddEmitSurfaceLights( const Vector &vStart, Vector lightBoxColor[6] )
  74. {
  75. fltx4 fractionVisible;
  76. FourVectors vStart4, wlOrigin4;
  77. vStart4.DuplicateVector ( vStart );
  78. for ( int iLight=0; iLight < *pNumworldlights; iLight++ )
  79. {
  80. dworldlight_t *wl = &dworldlights[iLight];
  81. // Should this light even go in the ambient cubes?
  82. if ( !( wl->flags & DWL_FLAGS_INAMBIENTCUBE ) )
  83. continue;
  84. Assert( wl->type == emit_surface );
  85. // Can this light see the point?
  86. wlOrigin4.DuplicateVector ( wl->origin );
  87. TestLine ( vStart4, wlOrigin4, &fractionVisible );
  88. if ( !TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
  89. continue;
  90. // Add this light's contribution.
  91. Vector vDelta = wl->origin - vStart;
  92. float flDistanceScale = Engine_WorldLightDistanceFalloff( wl, vDelta );
  93. Vector vDeltaNorm = vDelta;
  94. VectorNormalize( vDeltaNorm );
  95. float flAngleScale = Engine_WorldLightAngle( wl, wl->normal, vDeltaNorm, vDeltaNorm );
  96. float ratio = flDistanceScale * flAngleScale * SubFloat ( fractionVisible, 0 );
  97. if ( ratio == 0 )
  98. continue;
  99. for ( int i=0; i < 6; i++ )
  100. {
  101. float t = DotProduct( g_BoxDirections[i], vDeltaNorm );
  102. if ( t > 0 )
  103. {
  104. lightBoxColor[i] += wl->intensity * (t * ratio);
  105. }
  106. }
  107. }
  108. }
  109. void ComputeAmbientFromSphericalSamples( int iThread, const Vector &vStart, Vector lightBoxColor[6] )
  110. {
  111. // Figure out the color that rays hit when shot out from this position.
  112. Vector radcolor[NUMVERTEXNORMALS];
  113. float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
  114. for ( int i = 0; i < NUMVERTEXNORMALS; i++ )
  115. {
  116. Vector vEnd = vStart + g_anorms[i] * (COORD_EXTENT * 1.74);
  117. // Now that we've got a ray, see what surface we've hit
  118. Vector lightStyleColors[MAX_LIGHTSTYLES];
  119. lightStyleColors[0].Init(); // We only care about light style 0 here.
  120. CalcRayAmbientLighting( iThread, vStart, vEnd, tanTheta, lightStyleColors );
  121. radcolor[i] = lightStyleColors[0];
  122. }
  123. // accumulate samples into radiant box
  124. for ( int j = 6; --j >= 0; )
  125. {
  126. float t = 0;
  127. lightBoxColor[j].Init();
  128. for (int i = 0; i < NUMVERTEXNORMALS; i++)
  129. {
  130. float c = DotProduct( g_anorms[i], g_BoxDirections[j] );
  131. if (c > 0)
  132. {
  133. t += c;
  134. lightBoxColor[j] += radcolor[i] * c;
  135. }
  136. }
  137. lightBoxColor[j] *= 1/t;
  138. }
  139. // Now add direct light from the emit_surface lights. These go in the ambient cube because
  140. // there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
  141. AddEmitSurfaceLights( vStart, lightBoxColor );
  142. }
  143. bool IsLeafAmbientSurfaceLight( dworldlight_t *wl )
  144. {
  145. static const float g_flWorldLightMinEmitSurface = 0.005f;
  146. static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared( Vector( 0, 0, 512 ) ) );
  147. if ( wl->type != emit_surface )
  148. return false;
  149. if ( wl->style != 0 )
  150. return false;
  151. float intensity = max( wl->intensity[0], wl->intensity[1] );
  152. intensity = max( intensity, wl->intensity[2] );
  153. return (intensity * g_flWorldLightMinEmitSurfaceDistanceRatio) < g_flWorldLightMinEmitSurface;
  154. }
  155. class CLeafSampler
  156. {
  157. public:
  158. CLeafSampler( int iThread ) : m_iThread(iThread) {}
  159. // Generate a random point in the leaf's bounding volume
  160. // reject any points that aren't actually in the leaf
  161. // do a couple of tracing heuristics to eliminate points that are inside detail brushes
  162. // or underneath displacement surfaces in the leaf
  163. // return once we have a valid point, use the center if one can't be computed quickly
  164. void GenerateLeafSamplePosition( int leafIndex, const CUtlVector<dplane_t> &leafPlanes, Vector &samplePosition )
  165. {
  166. dleaf_t *pLeaf = dleafs + leafIndex;
  167. float dx = pLeaf->maxs[0] - pLeaf->mins[0];
  168. float dy = pLeaf->maxs[1] - pLeaf->mins[1];
  169. float dz = pLeaf->maxs[2] - pLeaf->mins[2];
  170. bool bValid = false;
  171. for ( int i = 0; i < 1000 && !bValid; i++ )
  172. {
  173. samplePosition.x = pLeaf->mins[0] + m_random.RandomFloat(0, dx);
  174. samplePosition.y = pLeaf->mins[1] + m_random.RandomFloat(0, dy);
  175. samplePosition.z = pLeaf->mins[2] + m_random.RandomFloat(0, dz);
  176. bValid = true;
  177. for ( int j = leafPlanes.Count(); --j >= 0 && bValid; )
  178. {
  179. float d = DotProduct(leafPlanes[j].normal, samplePosition) - leafPlanes[j].dist;
  180. if ( d < DIST_EPSILON )
  181. {
  182. // not inside the leaf, try again
  183. bValid = false;
  184. break;
  185. }
  186. }
  187. if ( !bValid )
  188. continue;
  189. for ( int j = 0; j < 6; j++ )
  190. {
  191. Vector start = samplePosition;
  192. int axis = j%3;
  193. start[axis] = (j<3) ? pLeaf->mins[axis] : pLeaf->maxs[axis];
  194. float t;
  195. Vector normal;
  196. CastRayInLeaf( m_iThread, samplePosition, start, leafIndex, &t, &normal );
  197. if ( t == 0.0f )
  198. {
  199. // inside a func_detail, try again.
  200. bValid = false;
  201. break;
  202. }
  203. if ( t != 1.0f )
  204. {
  205. Vector delta = start - samplePosition;
  206. if ( DotProduct(delta, normal) > 0 )
  207. {
  208. // hit backside of displacement, try again.
  209. bValid = false;
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. if ( !bValid )
  216. {
  217. // didn't generate a valid sample point, just use the center of the leaf bbox
  218. samplePosition = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
  219. }
  220. }
  221. private:
  222. int m_iThread;
  223. CUniformRandomStream m_random;
  224. };
  225. // gets a list of the planes pointing into a leaf
  226. void GetLeafBoundaryPlanes( CUtlVector<dplane_t> &list, int leafIndex )
  227. {
  228. list.RemoveAll();
  229. int nodeIndex = leafparents[leafIndex];
  230. int child = -(leafIndex + 1);
  231. while ( nodeIndex >= 0 )
  232. {
  233. dnode_t *pNode = dnodes + nodeIndex;
  234. dplane_t *pNodePlane = dplanes + pNode->planenum;
  235. if ( pNode->children[0] == child )
  236. {
  237. // front side
  238. list.AddToTail( *pNodePlane );
  239. }
  240. else
  241. {
  242. // back side
  243. int plane = list.AddToTail();
  244. list[plane].dist = -pNodePlane->dist;
  245. list[plane].normal = -pNodePlane->normal;
  246. list[plane].type = pNodePlane->type;
  247. }
  248. child = nodeIndex;
  249. nodeIndex = nodeparents[child];
  250. }
  251. }
  252. // this stores each sample of the ambient lighting
  253. struct ambientsample_t
  254. {
  255. Vector pos;
  256. Vector cube[6];
  257. };
  258. // add the sample to the list. If we exceed the maximum number of samples, the worst sample will
  259. // be discarded. This has the effect of converging on the best samples when enough are added.
  260. void AddSampleToList( CUtlVector<ambientsample_t> &list, const Vector &samplePosition, Vector *pCube )
  261. {
  262. const int MAX_SAMPLES = 16;
  263. int index = list.AddToTail();
  264. list[index].pos = samplePosition;
  265. for ( int i = 0; i < 6; i++ )
  266. {
  267. list[index].cube[i] = pCube[i];
  268. }
  269. if ( list.Count() <= MAX_SAMPLES )
  270. return;
  271. int nearestNeighborIndex = 0;
  272. float nearestNeighborDist = FLT_MAX;
  273. float nearestNeighborTotal = 0;
  274. for ( int i = 0; i < list.Count(); i++ )
  275. {
  276. int closestIndex = 0;
  277. float closestDist = FLT_MAX;
  278. float totalDC = 0;
  279. for ( int j = 0; j < list.Count(); j++ )
  280. {
  281. if ( j == i )
  282. continue;
  283. float dist = (list[i].pos - list[j].pos).Length();
  284. float maxDC = 0;
  285. for ( int k = 0; k < 6; k++ )
  286. {
  287. // color delta is computed per-component, per cube side
  288. for (int s = 0; s < 3; s++ )
  289. {
  290. float dc = fabs(list[i].cube[k][s] - list[j].cube[k][s]);
  291. maxDC = max(maxDC,dc);
  292. }
  293. totalDC += maxDC;
  294. }
  295. // need a measurable difference in color or we'll just rely on position
  296. if ( maxDC < 1e-4f )
  297. {
  298. maxDC = 0;
  299. }
  300. else if ( maxDC > 1.0f )
  301. {
  302. maxDC = 1.0f;
  303. }
  304. // selection criteria is 10% distance, 90% color difference
  305. // choose samples that fill the space (large distance from each other)
  306. // and have largest color variation
  307. float distanceFactor = 0.1f + (maxDC * 0.9f);
  308. dist *= distanceFactor;
  309. // find the "closest" sample to this one
  310. if ( dist < closestDist )
  311. {
  312. closestDist = dist;
  313. closestIndex = j;
  314. }
  315. }
  316. // the sample with the "closest" neighbor is rejected
  317. if ( closestDist < nearestNeighborDist || (closestDist == nearestNeighborDist && totalDC < nearestNeighborTotal) )
  318. {
  319. nearestNeighborDist = closestDist;
  320. nearestNeighborIndex = i;
  321. }
  322. }
  323. list.FastRemove( nearestNeighborIndex );
  324. }
  325. // max number of units in gamma space of per-side delta
  326. int CubeDeltaGammaSpace( Vector *pCube0, Vector *pCube1 )
  327. {
  328. int maxDelta = 0;
  329. // do this comparison in gamma space to try and get a perceptual basis for the compare
  330. for ( int i = 0; i < 6; i++ )
  331. {
  332. for ( int j = 0; j < 3; j++ )
  333. {
  334. int val0 = LinearToScreenGamma( pCube0[i][j] );
  335. int val1 = LinearToScreenGamma( pCube1[i][j] );
  336. int delta = abs(val0-val1);
  337. if ( delta > maxDelta )
  338. maxDelta = delta;
  339. }
  340. }
  341. return maxDelta;
  342. }
  343. // reconstruct the ambient lighting for a leaf at the given position in worldspace
  344. // optionally skip one of the entries in the list
  345. void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, const CUtlVector<ambientsample_t> &list, int skipIndex )
  346. {
  347. for ( int i = 0; i < 6; i++ )
  348. {
  349. pOut[i].Init();
  350. }
  351. float totalFactor = 0;
  352. for ( int i = 0; i < list.Count(); i++ )
  353. {
  354. if ( i == skipIndex )
  355. continue;
  356. // do an inverse squared distance weighted average of the samples to reconstruct
  357. // the original function
  358. float dist = (list[i].pos - pos).LengthSqr();
  359. float factor = 1.0f / (dist + 1.0f);
  360. totalFactor += factor;
  361. for ( int j = 0; j < 6; j++ )
  362. {
  363. pOut[j] += list[i].cube[j] * factor;
  364. }
  365. }
  366. for ( int i = 0; i < 6; i++ )
  367. {
  368. pOut[i] *= (1.0f / totalFactor);
  369. }
  370. }
  371. // this samples the lighting at each sample and removes any unnecessary samples
  372. void CompressAmbientSampleList( CUtlVector<ambientsample_t> &list )
  373. {
  374. Vector testCube[6];
  375. for ( int i = 0; i < list.Count(); i++ )
  376. {
  377. if ( list.Count() > 1 )
  378. {
  379. Mod_LeafAmbientColorAtPos( testCube, list[i].pos, list, i );
  380. if ( CubeDeltaGammaSpace(testCube, list[i].cube) < 3 )
  381. {
  382. list.FastRemove(i);
  383. i--;
  384. }
  385. }
  386. }
  387. }
  388. // basically this is an intersection routine that returns a distance between the boxes
  389. float AABBDistance( const Vector &mins0, const Vector &maxs0, const Vector &mins1, const Vector &maxs1 )
  390. {
  391. Vector delta;
  392. for ( int i = 0; i < 3; i++ )
  393. {
  394. float greatestMin = max(mins0[i], mins1[i]);
  395. float leastMax = min(maxs0[i], maxs1[i]);
  396. delta[i] = (greatestMin < leastMax) ? 0 : (leastMax - greatestMin);
  397. }
  398. return delta.Length();
  399. }
  400. // build a list of leaves from a query
  401. class CLeafList : public ISpatialLeafEnumerator
  402. {
  403. public:
  404. virtual bool EnumerateLeaf( int leaf, int context )
  405. {
  406. m_list.AddToTail(leaf);
  407. return true;
  408. }
  409. CUtlVector<int> m_list;
  410. };
  411. // conver short[3] to vector
  412. static void LeafBounds( int leafIndex, Vector &mins, Vector &maxs )
  413. {
  414. for ( int i = 0; i < 3; i++ )
  415. {
  416. mins[i] = dleafs[leafIndex].mins[i];
  417. maxs[i] = dleafs[leafIndex].maxs[i];
  418. }
  419. }
  420. // returns the index of the nearest leaf with ambient samples
  421. int NearestNeighborWithLight(int leafID)
  422. {
  423. Vector mins, maxs;
  424. LeafBounds( leafID, mins, maxs );
  425. Vector size = maxs - mins;
  426. CLeafList leafList;
  427. ToolBSPTree()->EnumerateLeavesInBox( mins-size, maxs+size, &leafList, 0 );
  428. float bestDist = FLT_MAX;
  429. int bestIndex = leafID;
  430. for ( int i = 0; i < leafList.m_list.Count(); i++ )
  431. {
  432. int testIndex = leafList.m_list[i];
  433. if ( !g_pLeafAmbientIndex->Element(testIndex).ambientSampleCount )
  434. continue;
  435. Vector testMins, testMaxs;
  436. LeafBounds( testIndex, testMins, testMaxs );
  437. float dist = AABBDistance( mins, maxs, testMins, testMaxs );
  438. if ( dist < bestDist )
  439. {
  440. bestDist = dist;
  441. bestIndex = testIndex;
  442. }
  443. }
  444. return bestIndex;
  445. }
  446. // maps a float to a byte fraction between min & max
  447. static byte Fixed8Fraction( float t, float tMin, float tMax )
  448. {
  449. if ( tMax <= tMin )
  450. return 0;
  451. float frac = RemapValClamped( t, tMin, tMax, 0.0f, 255.0f );
  452. return byte(frac+0.5f);
  453. }
  454. CUtlVector< CUtlVector<ambientsample_t> > g_LeafAmbientSamples;
  455. void ComputeAmbientForLeaf( int iThread, int leafID, CUtlVector<ambientsample_t> &list )
  456. {
  457. CUtlVector<dplane_t> leafPlanes;
  458. CLeafSampler sampler( iThread );
  459. GetLeafBoundaryPlanes( leafPlanes, leafID );
  460. list.RemoveAll();
  461. // this heuristic tries to generate at least one sample per volume (chosen to be similar to the size of a player) in the space
  462. int xSize = (dleafs[leafID].maxs[0] - dleafs[leafID].mins[0]) / 32;
  463. int ySize = (dleafs[leafID].maxs[1] - dleafs[leafID].mins[1]) / 32;
  464. int zSize = (dleafs[leafID].maxs[2] - dleafs[leafID].mins[2]) / 64;
  465. xSize = max(xSize,1);
  466. ySize = max(xSize,1);
  467. zSize = max(xSize,1);
  468. // generate update 128 candidate samples, always at least one sample
  469. int volumeCount = xSize * ySize * zSize;
  470. if ( g_bFastAmbient )
  471. {
  472. // save compute time, only do one sample
  473. volumeCount = 1;
  474. }
  475. int sampleCount = clamp( volumeCount, 1, 128 );
  476. if ( dleafs[leafID].contents & CONTENTS_SOLID )
  477. {
  478. // don't generate any samples in solid leaves
  479. // NOTE: We copy the nearest non-solid leaf sample pointers into this leaf at the end
  480. return;
  481. }
  482. Vector cube[6];
  483. for ( int i = 0; i < sampleCount; i++ )
  484. {
  485. // compute each candidate sample and add to the list
  486. Vector samplePosition;
  487. sampler.GenerateLeafSamplePosition( leafID, leafPlanes, samplePosition );
  488. ComputeAmbientFromSphericalSamples( iThread, samplePosition, cube );
  489. // note this will remove the least valuable sample once the limit is reached
  490. AddSampleToList( list, samplePosition, cube );
  491. }
  492. // remove any samples that can be reconstructed with the remaining data
  493. CompressAmbientSampleList( list );
  494. }
  495. static void ThreadComputeLeafAmbient( int iThread, void *pUserData )
  496. {
  497. CUtlVector<ambientsample_t> list;
  498. while (1)
  499. {
  500. int leafID = GetThreadWork ();
  501. if (leafID == -1)
  502. break;
  503. list.RemoveAll();
  504. ComputeAmbientForLeaf(iThread, leafID, list);
  505. // copy to the output array
  506. g_LeafAmbientSamples[leafID].SetCount( list.Count() );
  507. for ( int i = 0; i < list.Count(); i++ )
  508. {
  509. g_LeafAmbientSamples[leafID].Element(i) = list.Element(i);
  510. }
  511. }
  512. }
  513. void VMPI_ProcessLeafAmbient( int iThread, uint64 iLeaf, MessageBuffer *pBuf )
  514. {
  515. CUtlVector<ambientsample_t> list;
  516. ComputeAmbientForLeaf(iThread, (int)iLeaf, list);
  517. VMPI_SetCurrentStage( "EncodeLeafAmbientResults" );
  518. // Encode the results.
  519. int nSamples = list.Count();
  520. pBuf->write( &nSamples, sizeof( nSamples ) );
  521. if ( nSamples )
  522. {
  523. pBuf->write( list.Base(), list.Count() * sizeof( ambientsample_t ) );
  524. }
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Called on the master when a worker finishes processing a static prop.
  528. //-----------------------------------------------------------------------------
  529. void VMPI_ReceiveLeafAmbientResults( uint64 leafID, MessageBuffer *pBuf, int iWorker )
  530. {
  531. // Decode the results.
  532. int nSamples;
  533. pBuf->read( &nSamples, sizeof( nSamples ) );
  534. g_LeafAmbientSamples[leafID].SetCount( nSamples );
  535. if ( nSamples )
  536. {
  537. pBuf->read(g_LeafAmbientSamples[leafID].Base(), nSamples * sizeof(ambientsample_t) );
  538. }
  539. }
  540. void ComputePerLeafAmbientLighting()
  541. {
  542. // Figure out which lights should go in the per-leaf ambient cubes.
  543. int nInAmbientCube = 0;
  544. int nSurfaceLights = 0;
  545. for ( int i=0; i < *pNumworldlights; i++ )
  546. {
  547. dworldlight_t *wl = &dworldlights[i];
  548. if ( IsLeafAmbientSurfaceLight( wl ) )
  549. wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
  550. else
  551. wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
  552. if ( wl->type == emit_surface )
  553. ++nSurfaceLights;
  554. if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
  555. ++nInAmbientCube;
  556. }
  557. Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );
  558. g_LeafAmbientSamples.SetCount(numleafs);
  559. if ( g_bUseMPI )
  560. {
  561. // Distribute the work among the workers.
  562. VMPI_SetCurrentStage( "ComputeLeafAmbientLighting" );
  563. DistributeWork( numleafs, VMPI_DISTRIBUTEWORK_PACKETID, VMPI_ProcessLeafAmbient, VMPI_ReceiveLeafAmbientResults );
  564. }
  565. else
  566. {
  567. RunThreadsOn(numleafs, true, ThreadComputeLeafAmbient);
  568. }
  569. // now write out the data
  570. Msg("Writing leaf ambient...");
  571. g_pLeafAmbientIndex->RemoveAll();
  572. g_pLeafAmbientLighting->RemoveAll();
  573. g_pLeafAmbientIndex->SetCount( numleafs );
  574. g_pLeafAmbientLighting->EnsureCapacity( numleafs*4 );
  575. for ( int leafID = 0; leafID < numleafs; leafID++ )
  576. {
  577. const CUtlVector<ambientsample_t> &list = g_LeafAmbientSamples[leafID];
  578. g_pLeafAmbientIndex->Element(leafID).ambientSampleCount = list.Count();
  579. if ( !list.Count() )
  580. {
  581. g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = 0;
  582. }
  583. else
  584. {
  585. g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = g_pLeafAmbientLighting->Count();
  586. // compute the samples in disk format. Encode the positions in 8-bits using leaf bounds fractions
  587. for ( int i = 0; i < list.Count(); i++ )
  588. {
  589. int outIndex = g_pLeafAmbientLighting->AddToTail();
  590. dleafambientlighting_t &light = g_pLeafAmbientLighting->Element(outIndex);
  591. light.x = Fixed8Fraction( list[i].pos.x, dleafs[leafID].mins[0], dleafs[leafID].maxs[0] );
  592. light.y = Fixed8Fraction( list[i].pos.y, dleafs[leafID].mins[1], dleafs[leafID].maxs[1] );
  593. light.z = Fixed8Fraction( list[i].pos.z, dleafs[leafID].mins[2], dleafs[leafID].maxs[2] );
  594. light.pad = 0;
  595. for ( int side = 0; side < 6; side++ )
  596. {
  597. VectorToColorRGBExp32( list[i].cube[side], light.cube.m_Color[side] );
  598. }
  599. }
  600. }
  601. }
  602. for ( int i = 0; i < numleafs; i++ )
  603. {
  604. // UNDONE: Do this dynamically in the engine instead. This will allow us to sample across leaf
  605. // boundaries always which should improve the quality of lighting in general
  606. if ( g_pLeafAmbientIndex->Element(i).ambientSampleCount == 0 )
  607. {
  608. if ( !(dleafs[i].contents & CONTENTS_SOLID) )
  609. {
  610. Msg("Bad leaf ambient for leaf %d\n", i );
  611. }
  612. int refLeaf = NearestNeighborWithLight(i);
  613. g_pLeafAmbientIndex->Element(i).ambientSampleCount = 0;
  614. g_pLeafAmbientIndex->Element(i).firstAmbientSample = refLeaf;
  615. }
  616. }
  617. Msg("done\n");
  618. }