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.

882 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "vrad.h"
  9. #include "lightmap.h"
  10. #include "radial.h"
  11. #include "mathlib/bumpvects.h"
  12. #include "utlrbtree.h"
  13. #include "mathlib/VMatrix.h"
  14. #include "macro_texture.h"
  15. void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
  16. {
  17. Vector pos;
  18. VectorSubtract( world, l->luxelOrigin, pos );
  19. coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
  20. coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
  21. }
  22. void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
  23. {
  24. Vector pos;
  25. s += l->face->m_LightmapTextureMinsInLuxels[0];
  26. t += l->face->m_LightmapTextureMinsInLuxels[1];
  27. VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
  28. VectorMA( pos, t, l->luxelToWorldSpace[1], world );
  29. }
  30. void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
  31. {
  32. FourVectors luxelOrigin;
  33. luxelOrigin.DuplicateVector ( l->luxelOrigin );
  34. FourVectors pos = world;
  35. pos -= luxelOrigin;
  36. coord.x = pos * l->worldToLuxelSpace[0];
  37. coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
  38. coord.y = pos * l->worldToLuxelSpace[1];
  39. coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
  40. coord.z = Four_Zeros;
  41. }
  42. void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
  43. {
  44. world.DuplicateVector ( l->luxelOrigin );
  45. FourVectors st;
  46. s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
  47. st.DuplicateVector ( l->luxelToWorldSpace[0] );
  48. st *= s;
  49. world += st;
  50. t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
  51. st.DuplicateVector ( l->luxelToWorldSpace[1] );
  52. st *= t;
  53. world += st;
  54. }
  55. void AddDirectToRadial( radial_t *rad,
  56. Vector const &pnt,
  57. Vector2D const &coordmins, Vector2D const &coordmaxs,
  58. LightingValue_t const light[NUM_BUMP_VECTS+1],
  59. bool hasBumpmap, bool neighborHasBumpmap )
  60. {
  61. int s_min, s_max, t_min, t_max;
  62. Vector2D coord;
  63. int s, t;
  64. float ds, dt;
  65. float r;
  66. float area;
  67. int bumpSample;
  68. // convert world pos into local lightmap texture coord
  69. WorldToLuxelSpace( &rad->l, pnt, coord );
  70. s_min = ( int )( coordmins[0] );
  71. t_min = ( int )( coordmins[1] );
  72. s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
  73. t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
  74. s_min = max( s_min, 0 );
  75. t_min = max( t_min, 0 );
  76. s_max = min( s_max, rad->w );
  77. t_max = min( t_max, rad->h );
  78. for( s = s_min; s < s_max; s++ )
  79. {
  80. for( t = t_min; t < t_max; t++ )
  81. {
  82. float s0 = max( coordmins[0] - s, -1.0 );
  83. float t0 = max( coordmins[1] - t, -1.0 );
  84. float s1 = min( coordmaxs[0] - s, 1.0 );
  85. float t1 = min( coordmaxs[1] - t, 1.0 );
  86. area = (s1 - s0) * (t1 - t0);
  87. if (area > EQUAL_EPSILON)
  88. {
  89. ds = fabs( coord[0] - s );
  90. dt = fabs( coord[1] - t );
  91. r = max( ds, dt );
  92. if (r < 0.1)
  93. {
  94. r = area / 0.1;
  95. }
  96. else
  97. {
  98. r = area / r;
  99. }
  100. int i = s+t*rad->w;
  101. if( hasBumpmap )
  102. {
  103. if( neighborHasBumpmap )
  104. {
  105. for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  106. {
  107. rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
  108. }
  109. }
  110. else
  111. {
  112. rad->light[0][i].AddWeighted(light[0],r );
  113. for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  114. {
  115. rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
  116. }
  117. }
  118. }
  119. else
  120. {
  121. rad->light[0][i].AddWeighted( light[0], r );
  122. }
  123. rad->weight[i] += r;
  124. }
  125. }
  126. }
  127. }
  128. void AddBouncedToRadial( radial_t *rad,
  129. Vector const &pnt,
  130. Vector2D const &coordmins, Vector2D const &coordmaxs,
  131. Vector const light[NUM_BUMP_VECTS+1],
  132. bool hasBumpmap, bool neighborHasBumpmap )
  133. {
  134. int s_min, s_max, t_min, t_max;
  135. Vector2D coord;
  136. int s, t;
  137. float ds, dt;
  138. float r;
  139. int bumpSample;
  140. // convert world pos into local lightmap texture coord
  141. WorldToLuxelSpace( &rad->l, pnt, coord );
  142. float dists, distt;
  143. dists = (coordmaxs[0] - coordmins[0]);
  144. distt = (coordmaxs[1] - coordmins[1]);
  145. // patches less than a luxel in size could be mistakeningly filtered, so clamp.
  146. dists = max( 1.0, dists );
  147. distt = max( 1.0, distt );
  148. // find possible domain of patch influence
  149. s_min = ( int )( coord[0] - dists * RADIALDIST );
  150. t_min = ( int )( coord[1] - distt * RADIALDIST );
  151. s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
  152. t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
  153. // clamp to valid luxel
  154. s_min = max( s_min, 0 );
  155. t_min = max( t_min, 0 );
  156. s_max = min( s_max, rad->w );
  157. t_max = min( t_max, rad->h );
  158. for( s = s_min; s < s_max; s++ )
  159. {
  160. for( t = t_min; t < t_max; t++ )
  161. {
  162. // patch influence is based on patch size
  163. ds = ( coord[0] - s ) / dists;
  164. dt = ( coord[1] - t ) / distt;
  165. r = RADIALDIST2 - (ds * ds + dt * dt);
  166. int i = s+t*rad->w;
  167. if (r > 0)
  168. {
  169. if( hasBumpmap )
  170. {
  171. if( neighborHasBumpmap )
  172. {
  173. for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  174. {
  175. rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
  176. }
  177. }
  178. else
  179. {
  180. rad->light[0][i].AddWeighted( light[0], r );
  181. for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  182. {
  183. rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
  184. }
  185. }
  186. }
  187. else
  188. {
  189. rad->light[0][i].AddWeighted( light[0], r );
  190. }
  191. rad->weight[i] += r;
  192. }
  193. }
  194. }
  195. }
  196. void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
  197. {
  198. winding_t *w;
  199. int i;
  200. Vector2D coord;
  201. mins.Init( 1E30, 1E30 );
  202. maxs.Init( -1E30, -1E30 );
  203. CPatch *patch = &g_Patches.Element( ndxPatch );
  204. w = patch->winding;
  205. for (i = 0; i < w->numpoints; i++)
  206. {
  207. WorldToLuxelSpace( &rad->l, w->p[i], coord );
  208. mins[0] = min( mins[0], coord[0] );
  209. maxs[0] = max( maxs[0], coord[0] );
  210. mins[1] = min( mins[1], coord[1] );
  211. maxs[1] = max( maxs[1], coord[1] );
  212. }
  213. }
  214. radial_t *AllocateRadial( int facenum )
  215. {
  216. radial_t *rad;
  217. rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
  218. rad->facenum = facenum;
  219. InitLightinfo( &rad->l, facenum );
  220. rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
  221. rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
  222. return rad;
  223. }
  224. void FreeRadial( radial_t *rad )
  225. {
  226. if (rad)
  227. free( rad );
  228. }
  229. radial_t *BuildPatchRadial( int facenum )
  230. {
  231. int j;
  232. radial_t *rad;
  233. CPatch *patch;
  234. faceneighbor_t *fn;
  235. Vector2D mins, maxs;
  236. bool needsBumpmap, neighborNeedsBumpmap;
  237. needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
  238. rad = AllocateRadial( facenum );
  239. fn = &faceneighbor[ rad->facenum ];
  240. CPatch *pNextPatch;
  241. if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
  242. {
  243. for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
  244. {
  245. // next patch
  246. pNextPatch = NULL;
  247. if( patch->ndxNext != g_Patches.InvalidIndex() )
  248. {
  249. pNextPatch = &g_Patches.Element( patch->ndxNext );
  250. }
  251. // skip patches with children
  252. if (patch->child1 != g_Patches.InvalidIndex() )
  253. continue;
  254. // get the range of patch lightmap texture coords
  255. int ndxPatch = patch - g_Patches.Base();
  256. PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
  257. if (patch->numtransfers == 0)
  258. {
  259. // Error, using patch that was never evaluated or has no samples
  260. // patch->totallight[1] = 255;
  261. }
  262. //
  263. // displacement surface patch origin position and normal vectors have been changed to
  264. // represent the displacement surface position and normal -- for radial "blending"
  265. // we need to get the base surface patch origin!
  266. //
  267. if( ValidDispFace( &g_pFaces[facenum] ) )
  268. {
  269. Vector patchOrigin;
  270. WindingCenter (patch->winding, patchOrigin );
  271. AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
  272. needsBumpmap, needsBumpmap );
  273. }
  274. else
  275. {
  276. AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
  277. needsBumpmap, needsBumpmap );
  278. }
  279. }
  280. }
  281. for (j=0 ; j<fn->numneighbors; j++)
  282. {
  283. if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
  284. {
  285. for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
  286. {
  287. // next patch
  288. pNextPatch = NULL;
  289. if( patch->ndxNext != g_Patches.InvalidIndex() )
  290. {
  291. pNextPatch = &g_Patches.Element( patch->ndxNext );
  292. }
  293. // skip patches with children
  294. if (patch->child1 != g_Patches.InvalidIndex() )
  295. continue;
  296. // get the range of patch lightmap texture coords
  297. int ndxPatch = patch - g_Patches.Base();
  298. PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
  299. neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
  300. //
  301. // displacement surface patch origin position and normal vectors have been changed to
  302. // represent the displacement surface position and normal -- for radial "blending"
  303. // we need to get the base surface patch origin!
  304. //
  305. if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
  306. {
  307. Vector patchOrigin;
  308. WindingCenter (patch->winding, patchOrigin );
  309. AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
  310. needsBumpmap, needsBumpmap );
  311. }
  312. else
  313. {
  314. AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
  315. needsBumpmap, needsBumpmap );
  316. }
  317. }
  318. }
  319. }
  320. return rad;
  321. }
  322. radial_t *BuildLuxelRadial( int facenum, int style )
  323. {
  324. LightingValue_t light[NUM_BUMP_VECTS + 1];
  325. facelight_t *fl = &facelight[facenum];
  326. faceneighbor_t *fn = &faceneighbor[facenum];
  327. radial_t *rad = AllocateRadial( facenum );
  328. bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
  329. for (int k=0 ; k<fl->numsamples ; k++)
  330. {
  331. if( needsBumpmap )
  332. {
  333. for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  334. {
  335. light[bumpSample] = fl->light[style][bumpSample][k];
  336. }
  337. }
  338. else
  339. {
  340. light[0] = fl->light[style][0][k];
  341. }
  342. AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
  343. }
  344. for (int j = 0; j < fn->numneighbors; j++)
  345. {
  346. fl = &facelight[fn->neighbor[j]];
  347. bool neighborHasBumpmap = false;
  348. if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
  349. {
  350. neighborHasBumpmap = true;
  351. }
  352. int nstyle = 0;
  353. // look for style that matches
  354. if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
  355. {
  356. for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
  357. if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
  358. break;
  359. // if not found, skip this neighbor
  360. if (nstyle >= MAXLIGHTMAPS)
  361. continue;
  362. }
  363. lightinfo_t l;
  364. InitLightinfo( &l, fn->neighbor[j] );
  365. for (int k=0 ; k<fl->numsamples ; k++)
  366. {
  367. if( neighborHasBumpmap )
  368. {
  369. for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  370. {
  371. light[bumpSample] = fl->light[nstyle][bumpSample][k];
  372. }
  373. }
  374. else
  375. {
  376. light[0]=fl->light[nstyle][0][k];
  377. }
  378. Vector tmp;
  379. Vector2D mins, maxs;
  380. LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
  381. WorldToLuxelSpace( &rad->l, tmp, mins );
  382. LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
  383. WorldToLuxelSpace( &rad->l, tmp, maxs );
  384. AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
  385. needsBumpmap, neighborHasBumpmap );
  386. }
  387. }
  388. return rad;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose: returns the closest light value for a given point on the surface
  392. // this is normally a 1:1 mapping
  393. //-----------------------------------------------------------------------------
  394. bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
  395. {
  396. int bumpSample;
  397. Vector2D coord;
  398. WorldToLuxelSpace( &rad->l, pnt, coord );
  399. int u = ( int )( coord[0] + 0.5f );
  400. int v = ( int )( coord[1] + 0.5f );
  401. int i = u + v * rad->w;
  402. if (u < 0 || u > rad->w || v < 0 || v > rad->h)
  403. {
  404. static bool warning = false;
  405. if ( !warning )
  406. {
  407. // punting over to KenB
  408. // 2d coord indexes off of lightmap, generation of pnt seems suspect
  409. Warning( "SampleRadial: Punting, Waiting for fix\n" );
  410. warning = true;
  411. }
  412. for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
  413. {
  414. light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
  415. }
  416. return false;
  417. }
  418. bool baseSampleOk = true;
  419. for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
  420. {
  421. light[bumpSample].Zero();
  422. if (rad->weight[i] > WEIGHT_EPS)
  423. {
  424. light[bumpSample]= rad->light[bumpSample][i];
  425. light[bumpSample].Scale( 1.0 / rad->weight[i] );
  426. }
  427. else
  428. {
  429. if ( bRed2Black )
  430. {
  431. // Error, luxel has no samples
  432. light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
  433. }
  434. else
  435. {
  436. // Error, luxel has no samples
  437. // Yes, it actually should be 2550
  438. light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
  439. }
  440. if (bumpSample == 0)
  441. baseSampleOk = false;
  442. }
  443. }
  444. return baseSampleOk;
  445. }
  446. bool FloatLess( float const& src1, float const& src2 )
  447. {
  448. return src1 < src2;
  449. }
  450. //-----------------------------------------------------------------------------
  451. // Debugging!
  452. //-----------------------------------------------------------------------------
  453. void GetRandomColor( unsigned char *color )
  454. {
  455. static bool firstTime = true;
  456. if( firstTime )
  457. {
  458. firstTime = false;
  459. srand( 0 );
  460. }
  461. color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
  462. color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
  463. color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
  464. }
  465. #if 0
  466. // debugging! -- not accurate!
  467. void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
  468. {
  469. static FileHandle_t pFpLuxels = NULL;
  470. ThreadLock();
  471. if( !pFpLuxels )
  472. {
  473. pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
  474. }
  475. dface_t *pFace = &g_pFaces[ndxFace];
  476. bool bDisp = ( pFace->dispinfo != -1 );
  477. for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
  478. {
  479. WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
  480. if( bDumpNormals && bDisp )
  481. {
  482. WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
  483. }
  484. }
  485. ThreadUnlock();
  486. }
  487. #endif
  488. static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
  489. void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
  490. {
  491. // Lock the thread and dump the luxel data.
  492. ThreadLock();
  493. // Get the face and facelight data.
  494. facelight_t *pFaceLight = &facelight[iFace];
  495. // Open the luxel files.
  496. char szFileName[512];
  497. for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
  498. {
  499. if ( pFileLuxels[iBump] == NULL )
  500. {
  501. sprintf( szFileName, "luxels_bump%d.txt", iBump );
  502. pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
  503. }
  504. }
  505. WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
  506. ThreadUnlock();
  507. }
  508. void CloseDispLuxels()
  509. {
  510. for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
  511. {
  512. if ( pFileLuxels[iBump] )
  513. {
  514. g_pFileSystem->Close( pFileLuxels[iBump] );
  515. }
  516. }
  517. }
  518. /*
  519. =============
  520. FinalLightFace
  521. Add the indirect lighting on top of the direct
  522. lighting and save into final map format
  523. =============
  524. */
  525. void FinalLightFace( int iThread, int facenum )
  526. {
  527. dface_t *f;
  528. int i, j, k;
  529. facelight_t *fl;
  530. float minlight;
  531. int lightstyles;
  532. LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
  533. unsigned char *pdata[NUM_BUMP_VECTS + 1];
  534. int bumpSample;
  535. radial_t *rad = NULL;
  536. radial_t *prad = NULL;
  537. f = &g_pFaces[facenum];
  538. // test for non-lit texture
  539. if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
  540. return;
  541. fl = &facelight[facenum];
  542. for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
  543. {
  544. if ( f->styles[lightstyles] == 255 )
  545. break;
  546. }
  547. if ( !lightstyles )
  548. return;
  549. //
  550. // sample the triangulation
  551. //
  552. minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
  553. bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
  554. int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
  555. bool bDisp = ( f->dispinfo != -1 );
  556. //#define RANDOM_COLOR
  557. #ifdef RANDOM_COLOR
  558. unsigned char randomColor[3];
  559. GetRandomColor( randomColor );
  560. #endif
  561. // NOTE: I'm using these RB trees to sort all the illumination values
  562. // to compute median colors. Turns out that this is a somewhat better
  563. // method that using the average; usually if there are surfaces
  564. // with a large light intensity variation, the extremely bright regions
  565. // have a very small area and tend to influence the average too much.
  566. CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
  567. CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
  568. CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
  569. for (k=0 ; k < lightstyles; k++ )
  570. {
  571. m_Red.RemoveAll();
  572. m_Green.RemoveAll();
  573. m_Blue.RemoveAll();
  574. if (!do_fast)
  575. {
  576. if( !bDisp )
  577. {
  578. rad = BuildLuxelRadial( facenum, k );
  579. }
  580. else
  581. {
  582. rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
  583. }
  584. }
  585. if (numbounce > 0 && k == 0)
  586. {
  587. // currently only radiosity light non-displacement surfaces!
  588. if( !bDisp )
  589. {
  590. prad = BuildPatchRadial( facenum );
  591. }
  592. else
  593. {
  594. prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
  595. }
  596. }
  597. // pack the nonbump texture and the three bump texture for the given
  598. // lightstyle right next to each other.
  599. // NOTE: Even though it's building positions for all bump-mapped data,
  600. // it isn't going to use those positions (see loop over bumpSample below)
  601. // The file offset is correctly computed to only store space for 1 set
  602. // of light data if we don't have bumped lighting.
  603. for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
  604. {
  605. pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
  606. }
  607. // Compute the average luxel color, but not for the bump samples
  608. Vector avg( 0.0f, 0.0f, 0.0f );
  609. int avgCount = 0;
  610. for (j=0 ; j<fl->numluxels; j++)
  611. {
  612. // garymct - direct lighting
  613. bool baseSampleOk = true;
  614. if (!do_fast)
  615. {
  616. if( !bDisp )
  617. {
  618. baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
  619. }
  620. else
  621. {
  622. baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
  623. }
  624. }
  625. else
  626. {
  627. for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
  628. {
  629. lb[iBump] = fl->light[0][iBump][j];
  630. }
  631. }
  632. if (prad)
  633. {
  634. // garymct - bounced light
  635. // v is indirect light that is received on the luxel.
  636. if( !bDisp )
  637. {
  638. SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
  639. }
  640. else
  641. {
  642. StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
  643. }
  644. for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
  645. {
  646. lb[bumpSample].AddLight( v[bumpSample] );
  647. }
  648. }
  649. if ( bDisp && g_bDumpPatches )
  650. {
  651. for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
  652. {
  653. DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
  654. }
  655. }
  656. if (fl->numsamples == 0)
  657. {
  658. for( i = 0; i < bumpSampleCount; i++ )
  659. {
  660. lb[i].Init( 255, 0, 0 );
  661. }
  662. baseSampleOk = false;
  663. }
  664. int bumpSample;
  665. for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
  666. {
  667. // clip from the bottom first
  668. // garymct: minlight is a per entity minimum light value?
  669. for( i=0; i<3; i++ )
  670. {
  671. lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
  672. }
  673. // Do the average light computation, I'm assuming (perhaps incorrectly?)
  674. // that all luxels in a particular lightmap have the same area here.
  675. // Also, don't bother doing averages for the bump samples. Doing it here
  676. // because of the minlight clamp above + the random color testy thingy.
  677. // Also have to do it before Vec3toColorRGBExp32 because it
  678. // destructively modifies lb[bumpSample] (Feh!)
  679. if ((bumpSample == 0) && baseSampleOk)
  680. {
  681. ++avgCount;
  682. ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
  683. // For median computation
  684. m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
  685. m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
  686. m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
  687. }
  688. #ifdef RANDOM_COLOR
  689. pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
  690. pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
  691. pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
  692. pdata[bumpSample][3] = 0;
  693. #else
  694. // convert to a 4 byte r,g,b,signed exponent format
  695. VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
  696. lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
  697. #endif
  698. pdata[bumpSample] += 4;
  699. }
  700. }
  701. FreeRadial( rad );
  702. if (prad)
  703. {
  704. FreeRadial( prad );
  705. prad = NULL;
  706. }
  707. // Compute the median color for this lightstyle
  708. // Remember, the data goes *before* the specified light_ofs, in *reverse order*
  709. ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
  710. if (avgCount == 0)
  711. {
  712. Vector median( 0, 0, 0 );
  713. VectorToColorRGBExp32( median, *pAvgColor );
  714. }
  715. else
  716. {
  717. unsigned int r, g, b;
  718. r = m_Red.FirstInorder();
  719. g = m_Green.FirstInorder();
  720. b = m_Blue.FirstInorder();
  721. avgCount >>= 1;
  722. while (avgCount > 0)
  723. {
  724. r = m_Red.NextInorder(r);
  725. g = m_Green.NextInorder(g);
  726. b = m_Blue.NextInorder(b);
  727. --avgCount;
  728. }
  729. Vector median( m_Red[r], m_Green[g], m_Blue[b] );
  730. VectorToColorRGBExp32( median, *pAvgColor );
  731. }
  732. }
  733. }