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.

1517 lines
56 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "render_pch.h"
  9. #include "gl_lightmap.h"
  10. #include "view.h"
  11. #include "gl_cvars.h"
  12. #include "zone.h"
  13. #include "gl_water.h"
  14. #include "r_local.h"
  15. #include "gl_model_private.h"
  16. #include "mathlib/bumpvects.h"
  17. #include "gl_matsysiface.h"
  18. #include <float.h>
  19. #include "materialsystem/imaterialsystemhardwareconfig.h"
  20. #include "materialsystem/imesh.h"
  21. #include "tier0/dbg.h"
  22. #include "tier0/vprof.h"
  23. #include "tier1/callqueue.h"
  24. #include "lightcache.h"
  25. #include "cl_main.h"
  26. #include "materialsystem/imaterial.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. //-----------------------------------------------------------------------------
  30. // globals
  31. //-----------------------------------------------------------------------------
  32. // Only enable this if you are testing lightstyle performance.
  33. //#define UPDATE_LIGHTSTYLES_EVERY_FRAME
  34. ALIGN128 Vector4D blocklights[NUM_BUMP_VECTS+1][ MAX_LIGHTMAP_DIM_INCLUDING_BORDER * MAX_LIGHTMAP_DIM_INCLUDING_BORDER ];
  35. ConVar r_avglightmap( "r_avglightmap", "0", FCVAR_CHEAT | FCVAR_MATERIAL_SYSTEM_THREAD );
  36. ConVar r_maxdlights( "r_maxdlights", "32" );
  37. extern ConVar r_unloadlightmaps;
  38. extern bool g_bHunkAllocLightmaps;
  39. static int r_dlightvisible;
  40. static int r_dlightvisiblethisframe;
  41. static int s_nVisibleDLightCount;
  42. static int s_nMaxVisibleDLightCount;
  43. //-----------------------------------------------------------------------------
  44. // Visible, not visible DLights
  45. //-----------------------------------------------------------------------------
  46. void R_MarkDLightVisible( int dlight )
  47. {
  48. if ( (r_dlightvisible & ( 1 << dlight )) == 0 )
  49. {
  50. ++s_nVisibleDLightCount;
  51. r_dlightvisible |= 1 << dlight;
  52. }
  53. }
  54. void R_MarkDLightNotVisible( int dlight )
  55. {
  56. if ( r_dlightvisible & ( 1 << dlight ))
  57. {
  58. --s_nVisibleDLightCount;
  59. r_dlightvisible &= ~( 1 << dlight );
  60. }
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Must call these at the start + end of rendering each view
  64. //-----------------------------------------------------------------------------
  65. void R_DLightStartView()
  66. {
  67. r_dlightvisiblethisframe = 0;
  68. s_nMaxVisibleDLightCount = r_maxdlights.GetInt();
  69. }
  70. void R_DLightEndView()
  71. {
  72. if ( !g_bActiveDlights )
  73. return;
  74. for( int lnum=0 ; lnum<MAX_DLIGHTS; lnum++ )
  75. {
  76. if ( r_dlightvisiblethisframe & ( 1 << lnum ))
  77. continue;
  78. R_MarkDLightNotVisible( lnum );
  79. }
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Can we use another dynamic light, or is it just too expensive?
  83. //-----------------------------------------------------------------------------
  84. bool R_CanUseVisibleDLight( int dlight )
  85. {
  86. r_dlightvisiblethisframe |= (1 << dlight);
  87. if ( r_dlightvisible & ( 1 << dlight ) )
  88. return true;
  89. if ( s_nVisibleDLightCount >= s_nMaxVisibleDLightCount )
  90. return false;
  91. R_MarkDLightVisible( dlight );
  92. return true;
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Adds a single dynamic light
  96. //-----------------------------------------------------------------------------
  97. static bool AddSingleDynamicLight( dlight_t& dl, SurfaceHandle_t surfID, const Vector &lightOrigin, float perpDistSq, float lightRadiusSq )
  98. {
  99. // transform the light into brush local space
  100. Vector local;
  101. // Spotlight early outs...
  102. if (dl.m_OuterAngle)
  103. {
  104. if (dl.m_OuterAngle < 180.0f)
  105. {
  106. // Can't light anything from the rear...
  107. if (DotProduct(dl.m_Direction, MSurf_Plane( surfID ).normal) >= 0.0f)
  108. return false;
  109. }
  110. }
  111. // Transform the light center point into (u,v) space of the lightmap
  112. mtexinfo_t* tex = MSurf_TexInfo( surfID );
  113. local[0] = DotProduct (lightOrigin, tex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  114. tex->lightmapVecsLuxelsPerWorldUnits[0][3];
  115. local[1] = DotProduct (lightOrigin, tex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  116. tex->lightmapVecsLuxelsPerWorldUnits[1][3];
  117. // Now put the center points into the space of the lightmap rectangle
  118. // defined by the lightmapMins + lightmapExtents
  119. local[0] -= MSurf_LightmapMins( surfID )[0];
  120. local[1] -= MSurf_LightmapMins( surfID )[1];
  121. // Figure out the quadratic attenuation factor...
  122. Vector intensity;
  123. float lightStyleValue = LightStyleValue( dl.style );
  124. intensity[0] = TexLightToLinear( dl.color.r, dl.color.exponent ) * lightStyleValue;
  125. intensity[1] = TexLightToLinear( dl.color.g, dl.color.exponent ) * lightStyleValue;
  126. intensity[2] = TexLightToLinear( dl.color.b, dl.color.exponent ) * lightStyleValue;
  127. float minlight = fpmax( g_flMinLightingValue, dl.minlight );
  128. float ooQuadraticAttn = lightRadiusSq * minlight;
  129. float ooRadiusSq = 1.0f / lightRadiusSq;
  130. // Compute a color at each luxel
  131. // We want to know the square distance from luxel center to light
  132. // so we can compute an 1/r^2 falloff in light color
  133. int smax = MSurf_LightmapExtents( surfID )[0] + 1;
  134. int tmax = MSurf_LightmapExtents( surfID )[1] + 1;
  135. for (int t=0; t<tmax; ++t)
  136. {
  137. float td = (local[1] - t) * tex->worldUnitsPerLuxel;
  138. for (int s=0; s<smax; ++s)
  139. {
  140. float sd = (local[0] - s) * tex->worldUnitsPerLuxel;
  141. float inPlaneDistSq = sd * sd + td * td;
  142. float totalDistSq = inPlaneDistSq + perpDistSq;
  143. if (totalDistSq < lightRadiusSq)
  144. {
  145. // at least all floating point only happens when a luxel is lit.
  146. float scale = (totalDistSq != 0.0f) ? ooQuadraticAttn / totalDistSq : 1.0f;
  147. // Apply a little extra attenuation
  148. scale *= (1.0f - totalDistSq * ooRadiusSq);
  149. if (scale > 2.0f)
  150. scale = 2.0f;
  151. int idx = t*smax + s;
  152. // Compute the base lighting just as is done in the non-bump case...
  153. blocklights[0][idx][0] += scale * intensity[0];
  154. blocklights[0][idx][1] += scale * intensity[1];
  155. blocklights[0][idx][2] += scale * intensity[2];
  156. }
  157. }
  158. }
  159. return true;
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Adds a dynamic light to the bumped lighting
  163. //-----------------------------------------------------------------------------
  164. static void AddSingleDynamicLightToBumpLighting( dlight_t& dl, SurfaceHandle_t surfID,
  165. const Vector &lightOrigin, float perpDistSq, float lightRadiusSq, Vector* pBumpBasis, const Vector& luxelBasePosition )
  166. {
  167. Vector local;
  168. // FIXME: For now, only elights can be spotlights
  169. // the lightmap computation could get expensive for spotlights...
  170. Assert( dl.m_OuterAngle == 0.0f );
  171. // Transform the light center point into (u,v) space of the lightmap
  172. mtexinfo_t *pTexInfo = MSurf_TexInfo( surfID );
  173. local[0] = DotProduct (lightOrigin, pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  174. pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3];
  175. local[1] = DotProduct (lightOrigin, pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  176. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3];
  177. // Now put the center points into the space of the lightmap rectangle
  178. // defined by the lightmapMins + lightmapExtents
  179. local[0] -= MSurf_LightmapMins( surfID )[0];
  180. local[1] -= MSurf_LightmapMins( surfID )[1];
  181. // Figure out the quadratic attenuation factor...
  182. Vector intensity;
  183. float lightStyleValue = LightStyleValue( dl.style );
  184. intensity[0] = TexLightToLinear( dl.color.r, dl.color.exponent ) * lightStyleValue;
  185. intensity[1] = TexLightToLinear( dl.color.g, dl.color.exponent ) * lightStyleValue;
  186. intensity[2] = TexLightToLinear( dl.color.b, dl.color.exponent ) * lightStyleValue;
  187. float minlight = fpmax( g_flMinLightingValue, dl.minlight );
  188. float ooQuadraticAttn = lightRadiusSq * minlight;
  189. float ooRadiusSq = 1.0f / lightRadiusSq;
  190. // The algorithm here is necessary to make dynamic lights live in the
  191. // same world as the non-bumped dynamic lights. Therefore, we compute
  192. // the intensity of the flat lightmap the exact same way here as when
  193. // we've got a non-bumped surface.
  194. // Then, I compute an actual light direction vector per luxel (FIXME: !!expensive!!)
  195. // and compute what light would have to come in along that direction
  196. // in order to produce the same illumination on the flat lightmap. That's
  197. // computed by dividing the flat lightmap color by n dot l.
  198. Vector lightDirection(0, 0, 0), texelWorldPosition;
  199. #if 1
  200. bool useLightDirection = (dl.m_OuterAngle != 0.0f) &&
  201. (fabs(dl.m_Direction.LengthSqr() - 1.0f) < 1e-3);
  202. if (useLightDirection)
  203. VectorMultiply( dl.m_Direction, -1.0f, lightDirection );
  204. #endif
  205. // Since there's a scale factor used when going from world to luxel,
  206. // we gotta undo that scale factor when going from luxel to world
  207. float fixupFactor = pTexInfo->worldUnitsPerLuxel * pTexInfo->worldUnitsPerLuxel;
  208. // Compute a color at each luxel
  209. // We want to know the square distance from luxel center to light
  210. // so we can compute an 1/r^2 falloff in light color
  211. int smax = MSurf_LightmapExtents( surfID )[0] + 1;
  212. int tmax = MSurf_LightmapExtents( surfID )[1] + 1;
  213. for (int t=0; t<tmax; ++t)
  214. {
  215. float td = (local[1] - t) * pTexInfo->worldUnitsPerLuxel;
  216. // Move along the v direction
  217. VectorMA( luxelBasePosition, t * fixupFactor, pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D(),
  218. texelWorldPosition );
  219. for (int s=0; s<smax; ++s)
  220. {
  221. float sd = (local[0] - s) * pTexInfo->worldUnitsPerLuxel;
  222. float inPlaneDistSq = sd * sd + td * td;
  223. float totalDistSq = inPlaneDistSq + perpDistSq;
  224. if (totalDistSq < lightRadiusSq)
  225. {
  226. // at least all floating point only happens when a luxel is lit.
  227. float scale = (totalDistSq != 0.0f) ? ooQuadraticAttn / totalDistSq : 1.0f;
  228. // Apply a little extra attenuation
  229. scale *= (1.0f - totalDistSq * ooRadiusSq);
  230. if (scale > 2.0f)
  231. scale = 2.0f;
  232. int idx = t*smax + s;
  233. // Compute the base lighting just as is done in the non-bump case...
  234. VectorMA( blocklights[0][idx].AsVector3D(), scale, intensity, blocklights[0][idx].AsVector3D() );
  235. #if 1
  236. if (!useLightDirection)
  237. {
  238. VectorSubtract( lightOrigin, texelWorldPosition, lightDirection );
  239. VectorNormalize( lightDirection );
  240. }
  241. float lDotN = DotProduct( lightDirection, MSurf_Plane( surfID ).normal );
  242. if (lDotN < 1e-3)
  243. lDotN = 1e-3;
  244. scale /= lDotN;
  245. int i;
  246. for( i = 1; i < NUM_BUMP_VECTS + 1; i++ )
  247. {
  248. float dot = DotProduct( lightDirection, pBumpBasis[i-1] );
  249. if( dot <= 0.0f )
  250. continue;
  251. VectorMA( blocklights[i][idx].AsVector3D(), dot * scale, intensity,
  252. blocklights[i][idx].AsVector3D() );
  253. }
  254. #else
  255. VectorMA( blocklights[1][idx].AsVector3D(), scale, intensity, blocklights[1][idx].AsVector3D() );
  256. VectorMA( blocklights[2][idx].AsVector3D(), scale, intensity, blocklights[2][idx].AsVector3D() );
  257. VectorMA( blocklights[3][idx].AsVector3D(), scale, intensity, blocklights[3][idx].AsVector3D() );
  258. #endif
  259. }
  260. }
  261. // Move along u
  262. VectorMA( texelWorldPosition, fixupFactor,
  263. pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D(), texelWorldPosition );
  264. }
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Compute the bumpmap basis for this surface
  268. //-----------------------------------------------------------------------------
  269. static void R_ComputeSurfaceBasis( SurfaceHandle_t surfID, Vector *pBumpNormals, Vector &luxelBasePosition )
  270. {
  271. // Get the bump basis vects in the space of the surface.
  272. Vector sVect, tVect;
  273. VectorCopy( MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D(), sVect );
  274. VectorNormalize( sVect );
  275. VectorCopy( MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D(), tVect );
  276. VectorNormalize( tVect );
  277. GetBumpNormals( sVect, tVect, MSurf_Plane( surfID ).normal, MSurf_Plane( surfID ).normal, pBumpNormals );
  278. // Compute the location of the first luxel in worldspace
  279. // Since there's a scale factor used when going from world to luxel,
  280. // we gotta undo that scale factor when going from luxel to world
  281. float fixupFactor =
  282. MSurf_TexInfo( surfID )->worldUnitsPerLuxel *
  283. MSurf_TexInfo( surfID )->worldUnitsPerLuxel;
  284. // The starting u of the surface is surf->lightmapMins[0];
  285. // since N * P + D = u, N * P = u - D, therefore we gotta move (u-D) along uvec
  286. VectorMultiply( MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D(),
  287. (MSurf_LightmapMins( surfID )[0] - MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[0][3]) * fixupFactor,
  288. luxelBasePosition );
  289. // Do the same thing for the v direction.
  290. VectorMA( luxelBasePosition,
  291. (MSurf_LightmapMins( surfID )[1] -
  292. MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[1][3]) * fixupFactor,
  293. MSurf_TexInfo( surfID )->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D(),
  294. luxelBasePosition );
  295. // Move out in the direction of the plane normal...
  296. VectorMA( luxelBasePosition, MSurf_Plane( surfID ).dist, MSurf_Plane( surfID ).normal, luxelBasePosition );
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Compute the mask of which dlights affect a surface
  300. // NOTE: Also has the side effect of updating the surface lighting dlight flags!
  301. //-----------------------------------------------------------------------------
  302. unsigned int R_ComputeDynamicLightMask( dlight_t *pLights, SurfaceHandle_t surfID, msurfacelighting_t *pLighting, const matrix3x4_t& entityToWorld )
  303. {
  304. ASSERT_SURF_VALID( surfID );
  305. Vector bumpNormals[3];
  306. Vector luxelBasePosition;
  307. // Displacements do dynamic lights different
  308. if( SurfaceHasDispInfo( surfID ) )
  309. {
  310. return MSurf_DispInfo( surfID )->ComputeDynamicLightMask(pLights);
  311. }
  312. if ( !g_bActiveDlights )
  313. return 0;
  314. int lightMask = 0;
  315. for ( int lnum = 0, testBit = 1, mask = r_dlightactive; lnum < MAX_DLIGHTS; lnum++, mask >>= 1, testBit <<= 1 )
  316. {
  317. if ( mask & 1 )
  318. {
  319. // not lit by this light
  320. if ( !(pLighting->m_fDLightBits & testBit ) )
  321. continue;
  322. // This light doesn't affect the world
  323. if ( pLights[lnum].flags & (DLIGHT_NO_WORLD_ILLUMINATION|DLIGHT_DISPLACEMENT_MASK))
  324. continue;
  325. // This is used to ensure a maximum number of dlights in a frame
  326. if ( !R_CanUseVisibleDLight( lnum ) )
  327. continue;
  328. // Cull surface to light radius
  329. Vector lightOrigin;
  330. VectorITransform( pLights[lnum].origin, entityToWorld, lightOrigin );
  331. // NOTE: Dist can be negative because muzzle flashes can actually get behind walls
  332. // since the gun isn't checked for collision tests.
  333. float perpDistSq = DotProduct (lightOrigin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist;
  334. if (perpDistSq < DLIGHT_BEHIND_PLANE_DIST)
  335. {
  336. // update the surfacelighting and remove this light's bit
  337. pLighting->m_fDLightBits &= ~testBit;
  338. continue;
  339. }
  340. perpDistSq *= perpDistSq;
  341. // If the perp distance > radius of light, blow it off
  342. float lightRadiusSq = pLights[lnum].GetRadiusSquared();
  343. if (lightRadiusSq <= perpDistSq)
  344. {
  345. // update the surfacelighting and remove this light's bit
  346. pLighting->m_fDLightBits &= ~testBit;
  347. continue;
  348. }
  349. lightMask |= testBit;
  350. }
  351. }
  352. return lightMask;
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose: Modifies blocklights[][][] to include the state of the dlights
  356. // affecting this surface.
  357. // NOTE: Can be threaded, should not reference or modify any global state
  358. // other than blocklights.
  359. //-----------------------------------------------------------------------------
  360. void R_AddDynamicLights( dlight_t *pLights, SurfaceHandle_t surfID, const matrix3x4_t& entityToWorld, bool needsBumpmap, unsigned int lightMask )
  361. {
  362. ASSERT_SURF_VALID( surfID );
  363. VPROF( "R_AddDynamicLights" );
  364. Vector bumpNormals[3];
  365. bool computedBumpBasis = false;
  366. Vector luxelBasePosition;
  367. // Displacements do dynamic lights different
  368. if( SurfaceHasDispInfo( surfID ) )
  369. {
  370. MSurf_DispInfo( surfID )->AddDynamicLights(pLights, lightMask);
  371. return;
  372. }
  373. // iterate all of the active dynamic lights. Uses several iterators to keep
  374. // the light mask (bit), light index, and active mask current
  375. for ( int lnum = 0, testBit = 1, mask = lightMask; lnum < MAX_DLIGHTS && mask != 0; lnum++, mask >>= 1, testBit <<= 1 )
  376. {
  377. // shift over the mask of active lights each iteration, if this one is active, apply it
  378. if ( mask & 1 )
  379. {
  380. // Cull surface to light radius
  381. Vector lightOrigin;
  382. VectorITransform( pLights[lnum].origin, entityToWorld, lightOrigin );
  383. // NOTE: Dist can be negative because muzzle flashes can actually get behind walls
  384. // since the gun isn't checked for collision tests.
  385. float perpDistSq = DotProduct (lightOrigin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist;
  386. if (perpDistSq < DLIGHT_BEHIND_PLANE_DIST)
  387. continue;
  388. perpDistSq *= perpDistSq;
  389. // If the perp distance > radius of light, blow it off
  390. float lightRadiusSq = pLights[lnum].GetRadiusSquared();
  391. if (lightRadiusSq <= perpDistSq)
  392. continue;
  393. if (!needsBumpmap)
  394. {
  395. AddSingleDynamicLight( pLights[lnum], surfID, lightOrigin, perpDistSq, lightRadiusSq );
  396. continue;
  397. }
  398. // Here, I'm precomputing things needed by bumped lighting that
  399. // are the same for a surface...
  400. if (!computedBumpBasis)
  401. {
  402. R_ComputeSurfaceBasis( surfID, bumpNormals, luxelBasePosition );
  403. computedBumpBasis = true;
  404. }
  405. AddSingleDynamicLightToBumpLighting( pLights[lnum], surfID, lightOrigin, perpDistSq, lightRadiusSq, bumpNormals, luxelBasePosition );
  406. }
  407. }
  408. }
  409. // Fixed point (8.8) color/intensity ratios
  410. #define I_RED ((int)(0.299*255))
  411. #define I_GREEN ((int)(0.587*255))
  412. #define I_BLUE ((int)(0.114*255))
  413. //-----------------------------------------------------------------------------
  414. // Sets all elements in a lightmap to a particular opaque greyscale value
  415. //-----------------------------------------------------------------------------
  416. static void InitLMSamples( Vector4D *pSamples, int nSamples, float value )
  417. {
  418. for( int i=0; i < nSamples; i++ )
  419. {
  420. pSamples[i][0] = pSamples[i][1] = pSamples[i][2] = value;
  421. pSamples[i][3] = 1.0f;
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Computes the lightmap size
  426. //-----------------------------------------------------------------------------
  427. static int ComputeLightmapSize( SurfaceHandle_t surfID )
  428. {
  429. int smax = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  430. int tmax = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
  431. int size = smax * tmax;
  432. int nMaxSize = MSurf_MaxLightmapSizeWithBorder( surfID );
  433. if (size > nMaxSize * nMaxSize)
  434. {
  435. ConMsg("Bad lightmap extents on material \"%s\"\n",
  436. materialSortInfoArray[MSurf_MaterialSortID( surfID )].material->GetName());
  437. return 0;
  438. }
  439. return size;
  440. }
  441. #ifndef _X360
  442. //-----------------------------------------------------------------------------
  443. // Compute the portion of the lightmap generated from lightstyles
  444. //-----------------------------------------------------------------------------
  445. static void AccumulateLightstyles( ColorRGBExp32* pLightmap, int lightmapSize, float scalar )
  446. {
  447. Assert( pLightmap );
  448. for (int i=0; i<lightmapSize ; ++i)
  449. {
  450. blocklights[0][i][0] += scalar * TexLightToLinear( pLightmap[i].r, pLightmap[i].exponent );
  451. blocklights[0][i][1] += scalar * TexLightToLinear( pLightmap[i].g, pLightmap[i].exponent );
  452. blocklights[0][i][2] += scalar * TexLightToLinear( pLightmap[i].b, pLightmap[i].exponent );
  453. }
  454. }
  455. static void AccumulateLightstylesFlat( ColorRGBExp32* pLightmap, int lightmapSize, float scalar )
  456. {
  457. Assert( pLightmap );
  458. for (int i=0; i<lightmapSize ; ++i)
  459. {
  460. blocklights[0][i][0] += scalar * TexLightToLinear( pLightmap->r, pLightmap->exponent );
  461. blocklights[0][i][1] += scalar * TexLightToLinear( pLightmap->g, pLightmap->exponent );
  462. blocklights[0][i][2] += scalar * TexLightToLinear( pLightmap->b, pLightmap->exponent );
  463. }
  464. }
  465. static void AccumulateBumpedLightstyles( ColorRGBExp32* pLightmap, int lightmapSize, float scalar )
  466. {
  467. ColorRGBExp32 *pBumpedLightmaps[3];
  468. pBumpedLightmaps[0] = pLightmap + lightmapSize;
  469. pBumpedLightmaps[1] = pLightmap + 2 * lightmapSize;
  470. pBumpedLightmaps[2] = pLightmap + 3 * lightmapSize;
  471. // I chose to split up the loops this way because it was the best tradeoff
  472. // based on profiles between cache miss + loop overhead
  473. for (int i=0 ; i<lightmapSize ; ++i)
  474. {
  475. blocklights[0][i][0] += scalar * TexLightToLinear( pLightmap[i].r, pLightmap[i].exponent );
  476. blocklights[0][i][1] += scalar * TexLightToLinear( pLightmap[i].g, pLightmap[i].exponent );
  477. blocklights[0][i][2] += scalar * TexLightToLinear( pLightmap[i].b, pLightmap[i].exponent );
  478. Assert( blocklights[0][i][0] >= 0.0f );
  479. Assert( blocklights[0][i][1] >= 0.0f );
  480. Assert( blocklights[0][i][2] >= 0.0f );
  481. blocklights[1][i][0] += scalar * TexLightToLinear( pBumpedLightmaps[0][i].r, pBumpedLightmaps[0][i].exponent );
  482. blocklights[1][i][1] += scalar * TexLightToLinear( pBumpedLightmaps[0][i].g, pBumpedLightmaps[0][i].exponent );
  483. blocklights[1][i][2] += scalar * TexLightToLinear( pBumpedLightmaps[0][i].b, pBumpedLightmaps[0][i].exponent );
  484. Assert( blocklights[1][i][0] >= 0.0f );
  485. Assert( blocklights[1][i][1] >= 0.0f );
  486. Assert( blocklights[1][i][2] >= 0.0f );
  487. }
  488. for ( int i=0 ; i<lightmapSize ; ++i)
  489. {
  490. blocklights[2][i][0] += scalar * TexLightToLinear( pBumpedLightmaps[1][i].r, pBumpedLightmaps[1][i].exponent );
  491. blocklights[2][i][1] += scalar * TexLightToLinear( pBumpedLightmaps[1][i].g, pBumpedLightmaps[1][i].exponent );
  492. blocklights[2][i][2] += scalar * TexLightToLinear( pBumpedLightmaps[1][i].b, pBumpedLightmaps[1][i].exponent );
  493. Assert( blocklights[2][i][0] >= 0.0f );
  494. Assert( blocklights[2][i][1] >= 0.0f );
  495. Assert( blocklights[2][i][2] >= 0.0f );
  496. blocklights[3][i][0] += scalar * TexLightToLinear( pBumpedLightmaps[2][i].r, pBumpedLightmaps[2][i].exponent );
  497. blocklights[3][i][1] += scalar * TexLightToLinear( pBumpedLightmaps[2][i].g, pBumpedLightmaps[2][i].exponent );
  498. blocklights[3][i][2] += scalar * TexLightToLinear( pBumpedLightmaps[2][i].b, pBumpedLightmaps[2][i].exponent );
  499. Assert( blocklights[3][i][0] >= 0.0f );
  500. Assert( blocklights[3][i][1] >= 0.0f );
  501. Assert( blocklights[3][i][2] >= 0.0f );
  502. }
  503. }
  504. #else
  505. /*
  506. // unpack four ColorRGBExp32's loaded into a single vector register
  507. // into four. Can't do this as a function coz you can't return four
  508. // values and even the inliner falls down on pass-by-ref.
  509. #define UNPACK_COLORRGBEXP(fromVec, toVec0, toVec1, toVec2, toVec3) {\
  510. }
  511. */
  512. // because the e component of the colors is signed, we need to mask
  513. // off the corresponding channel in the intermediate halfword expansion
  514. // when we combine it with the unsigned unpack for the other channels
  515. static const int32 ALIGN16 g_SIMD_HalfWordMask[4]= { 0x0000000, 0x0000FFFF, 0x0000000, 0x0000FFFF };
  516. static const fltx4 vOneOverTwoFiftyFive = { 1.0f / 255.0f , 1.0f / 255.0f , 1.0f / 255.0f , 1.0f / 255.0f };
  517. // grind through accumlating onto the blocklights,
  518. // one cache line at a time. Input pointers are assumed
  519. // to be cache aligned.
  520. // For a simpler reference implementation, see the PC version in the ifdef above.
  521. // This function makes heavy use of the special XBOX360 opcodes for
  522. // packing and unpacking integer d3d data. (Not available in SSE, sadly.)
  523. static void AccumulateLightstyles_EightAtAtime( ColorRGBExp32* RESTRICT pLightmap, // the input lightmap (not necessarily aligned)
  524. int lightmapSize,
  525. fltx4 vScalar,
  526. Vector4D * RESTRICT bLights // pointer to the blocklights row we'll be writing into -- should be cache aligned, but only hurts perf if it's not
  527. )
  528. {
  529. // We process blockLights in groups of four at a time, because we load the pLightmap four
  530. // at a time (four words fit into a vector register).
  531. // On top of that, we do two groups at once, because that's the length
  532. // of a cache line, and it helps us better hide latency.
  533. AssertMsg((lightmapSize & 7) == 0, "Input to Accumulate...EightAtATime not divisible by eight. Data corruption is the likely result." );
  534. const fltx4 vHalfWordMask = XMLoadVector4A(g_SIMD_HalfWordMask);
  535. fltx4 zero = Four_Zeros;
  536. for (int i = 0 ; i < lightmapSize ; i += 8 )
  537. {
  538. // cache prefetch two lines ahead on bLights, and one on pLightmap
  539. __dcbt(256, bLights);
  540. __dcbt(128, pLightmap);
  541. // the naming convention on these psuedoarrays (they are actually
  542. // registers) is that the number before the index is the group id,
  543. // and the index itself is which word in the group. If this seems
  544. // unclear to you, feel free to just use array indices 0..7
  545. // The compiler doesn't seem to deal properly with multidim arrays
  546. // (at least in the sense of aliasing them to registers)
  547. // However, if you always access through the arrays by using
  548. // compile-time immediate constants (eg, foo[2] rather than
  549. // int x = 2; foo[x]
  550. // it will at least treat them as register variables.
  551. // load four blockLights entries, and four colors
  552. fltx4 vLight0[4], vLight1[4];
  553. fltx4 colorLightMap0[4], colorLightMap1[4];
  554. fltx4 bytePackedLightMap0 = XMLoadVector4(pLightmap+i); // because each colorrgbexp is actually a 32-bit struct,
  555. // this loads four of them into one vector -- they are ubytes for rgb and sbyte for e
  556. fltx4 bytePackedLightMap1 = XMLoadVector4(pLightmap+i+4);
  557. // load group 0
  558. vLight0[0] = LoadAlignedSIMD( &(bLights + i + 0)->x );
  559. vLight0[1] = LoadAlignedSIMD( &(bLights + i + 1)->x );
  560. vLight0[2] = LoadAlignedSIMD( &(bLights + i + 2)->x );
  561. vLight0[3] = LoadAlignedSIMD( &(bLights + i + 3)->x );
  562. // load group 1
  563. vLight1[0] = LoadAlignedSIMD( &(bLights + i + 4)->x );
  564. vLight1[1] = LoadAlignedSIMD( &(bLights + i + 5)->x );
  565. vLight1[2] = LoadAlignedSIMD( &(bLights + i + 6)->x );
  566. vLight1[3] = LoadAlignedSIMD( &(bLights + i + 7)->x );
  567. // unpack the color light maps now that they have loaded
  568. // interleaving (four-vector) group 0 and 1
  569. // unpack rgbe 0 and 1:
  570. // like an unsigned unpack: { 0x00, colorLightMap[0].r, 0x00, colorLightMap[0].g, 0x00, colorLightMap[0].b, 0x00, colorLightMap[0].e,
  571. // 0x00, colorLightMap[1].r, 0x00, colorLightMap[1].g, 0x00, colorLightMap[1].b, 0x00, colorLightMap[1].e}
  572. fltx4 unsignedUnpackHi0 = __vmrghb(zero, bytePackedLightMap0); // GROUP 0
  573. fltx4 unsignedUnpackLo0 = __vmrglb(zero, bytePackedLightMap0); // rgbe words 2 and 3
  574. fltx4 unsignedUnpackHi1 = __vmrghb(zero, bytePackedLightMap1); // GROUP 1
  575. fltx4 unsignedUnpackLo1 = __vmrglb(zero, bytePackedLightMap1); // rgbe words 2 and 3
  576. fltx4 signedUnpackHi0 = __vupkhsb(bytePackedLightMap0); // signed unpack of words 0 and 1, like the unsigned unpack but replaces 0x00 w/ sign extension
  577. fltx4 signedUnpackLo0 = __vupklsb(bytePackedLightMap0); // GROUP 0
  578. fltx4 signedUnpackHi1 = __vupkhsb(bytePackedLightMap1); // signed unpack of words 0 and 1, like the unsigned unpack but replaces 0x00 w/ sign extension
  579. fltx4 signedUnpackLo1 = __vupklsb(bytePackedLightMap1); // GROUP 1
  580. // merge the signed and unsigned unpacks together to make the full halfwords
  581. unsignedUnpackHi0 = MaskedAssign(vHalfWordMask, signedUnpackHi0, unsignedUnpackHi0 );
  582. unsignedUnpackLo0 = MaskedAssign(vHalfWordMask, signedUnpackLo0, unsignedUnpackLo0 );
  583. unsignedUnpackHi1 = MaskedAssign(vHalfWordMask, signedUnpackHi1, unsignedUnpackHi1 );
  584. unsignedUnpackLo1 = MaskedAssign(vHalfWordMask, signedUnpackLo1, unsignedUnpackLo1 );
  585. // now complete the unpack from halfwords to words (we can just use signed because there are 0x00's above the rgb channels)
  586. colorLightMap0[0] = __vupkhsh(unsignedUnpackHi0); // vector unpack high signed halfword
  587. colorLightMap0[1] = __vupklsh(unsignedUnpackHi0); // vector unpack low signed halfword
  588. colorLightMap0[2] = __vupkhsh(unsignedUnpackLo0);
  589. colorLightMap0[3] = __vupklsh(unsignedUnpackLo0);
  590. colorLightMap0[0] = __vcfsx( colorLightMap0[0], 0); // convert to floats
  591. colorLightMap1[0] = __vupkhsh(unsignedUnpackHi1); // interleave group 1 unpacks
  592. colorLightMap0[1] = __vcfsx( colorLightMap0[1], 0); // convert to floats
  593. colorLightMap1[1] = __vupklsh(unsignedUnpackHi1); // should dual issue
  594. colorLightMap0[2] = __vcfsx( colorLightMap0[2], 0); // convert to floats
  595. colorLightMap1[2] = __vupkhsh(unsignedUnpackLo1);
  596. colorLightMap0[3] = __vcfsx( colorLightMap0[3], 0); // convert to floats
  597. colorLightMap1[3] = __vupklsh(unsignedUnpackLo1);
  598. // finish unpacking group 1 (giving group 0 time to finish converting)
  599. colorLightMap1[0] = __vcfsx( colorLightMap1[0], 0);
  600. colorLightMap1[1] = __vcfsx( colorLightMap1[1], 0);
  601. colorLightMap1[2] = __vcfsx( colorLightMap1[2], 0);
  602. colorLightMap1[3] = __vcfsx( colorLightMap1[3], 0);
  603. // manufacture exponent splats and start normalizing the rgb channels (eg *= 1/255)
  604. fltx4 expW0[4], expW1[4];
  605. expW0[0] = XMVectorSplatW(colorLightMap0[0]);
  606. colorLightMap0[0] = MulSIMD(colorLightMap0[0], vOneOverTwoFiftyFive); // normalize the rgb channels
  607. expW0[1] = XMVectorSplatW(colorLightMap0[1]);
  608. colorLightMap0[1] = MulSIMD(colorLightMap0[1], vOneOverTwoFiftyFive); // normalize the rgb channels
  609. expW0[2] = XMVectorSplatW(colorLightMap0[2]);
  610. colorLightMap0[2] = MulSIMD(colorLightMap0[2], vOneOverTwoFiftyFive); // normalize the rgb channels
  611. expW0[3] = XMVectorSplatW(colorLightMap0[3]);
  612. colorLightMap0[3] = MulSIMD(colorLightMap0[3], vOneOverTwoFiftyFive); // normalize the rgb channels
  613. // scale each of the color channels by the exponent channel
  614. // (the estimate operation is exact for integral inputs, as here)
  615. expW0[0] = XMVectorExpEst( expW0[0] ); // x = 2^x
  616. expW1[0] = XMVectorSplatW(colorLightMap1[0]); // interleave splats on exp group 1 (dual issue)
  617. colorLightMap1[0] = MulSIMD(colorLightMap1[0], vOneOverTwoFiftyFive); // normalize the rgb channels
  618. expW0[1] = XMVectorExpEst( expW0[1] );
  619. expW1[1] = XMVectorSplatW(colorLightMap1[1]);
  620. colorLightMap1[1] = MulSIMD(colorLightMap1[1], vOneOverTwoFiftyFive); // normalize the rgb channels
  621. expW0[2] = XMVectorExpEst( expW0[2] );
  622. expW1[2] = XMVectorSplatW(colorLightMap1[2]);
  623. colorLightMap1[2] = MulSIMD(colorLightMap1[2], vOneOverTwoFiftyFive); // normalize the rgb channels
  624. expW0[3] = XMVectorExpEst( expW0[3] );
  625. expW1[3] = XMVectorSplatW(colorLightMap1[3]);
  626. colorLightMap1[3] = MulSIMD(colorLightMap1[3], vOneOverTwoFiftyFive); // normalize the rgb channels
  627. // finish scale-by-exponent on group 1
  628. expW1[0] = XMVectorExpEst( expW1[0] );
  629. expW1[1] = XMVectorExpEst( expW1[1] );
  630. expW1[2] = XMVectorExpEst( expW1[2] );
  631. expW1[3] = XMVectorExpEst( expW1[3] );
  632. colorLightMap0[0] = MulSIMD(expW0[0], colorLightMap0[0]);
  633. colorLightMap0[1] = MulSIMD(expW0[1], colorLightMap0[1]);
  634. colorLightMap0[2] = MulSIMD(expW0[2], colorLightMap0[2]);
  635. colorLightMap0[3] = MulSIMD(expW0[3], colorLightMap0[3]);
  636. colorLightMap1[0] = MulSIMD(expW1[0], colorLightMap1[0]);
  637. colorLightMap1[1] = MulSIMD(expW1[1], colorLightMap1[1]);
  638. colorLightMap1[2] = MulSIMD(expW1[2], colorLightMap1[2]);
  639. colorLightMap1[3] = MulSIMD(expW1[3], colorLightMap1[3]);
  640. #ifdef X360_DOUBLECHECK_LIGHTMAPS
  641. for (int group = 0 ; group < 4 ; ++group)
  642. {
  643. Assert( colorLightMap0[group].v[0] == TexLightToLinear( pLightmap[i + group].r, pLightmap[i + group].exponent ) &&
  644. colorLightMap0[group].v[1] == TexLightToLinear( pLightmap[i + group].g, pLightmap[i + group].exponent ) &&
  645. colorLightMap0[group].v[2] == TexLightToLinear( pLightmap[i + group].b, pLightmap[i + group].exponent ) );
  646. }
  647. #endif
  648. // accumulate into blocklights
  649. vLight0[0] = XMVectorMultiplyAdd(vScalar, colorLightMap0[0], vLight0[0]);
  650. vLight0[1] = XMVectorMultiplyAdd(vScalar, colorLightMap0[1], vLight0[1]);
  651. vLight0[2] = XMVectorMultiplyAdd(vScalar, colorLightMap0[2], vLight0[2]);
  652. vLight0[3] = XMVectorMultiplyAdd(vScalar, colorLightMap0[3], vLight0[3]);
  653. vLight1[0] = XMVectorMultiplyAdd(vScalar, colorLightMap1[0], vLight1[0]);
  654. vLight1[1] = XMVectorMultiplyAdd(vScalar, colorLightMap1[1], vLight1[1]);
  655. vLight1[2] = XMVectorMultiplyAdd(vScalar, colorLightMap1[2], vLight1[2]);
  656. vLight1[3] = XMVectorMultiplyAdd(vScalar, colorLightMap1[3], vLight1[3]);
  657. // save
  658. XMStoreVector4A( bLights + i + 0, vLight0[0]);
  659. XMStoreVector4A( bLights + i + 1, vLight0[1]);
  660. XMStoreVector4A( bLights + i + 2, vLight0[2]);
  661. XMStoreVector4A( bLights + i + 3, vLight0[3]);
  662. XMStoreVector4A( bLights + i + 4, vLight1[0]);
  663. XMStoreVector4A( bLights + i + 5, vLight1[1]);
  664. XMStoreVector4A( bLights + i + 6, vLight1[2]);
  665. XMStoreVector4A( bLights + i + 7, vLight1[3]);
  666. }
  667. }
  668. // just like XMLoadByte4 only no asserts
  669. FORCEINLINE XMVECTOR LoadSignedByte4NoAssert ( CONST XMBYTE4* pSource )
  670. {
  671. XMVECTOR V;
  672. V = __lvlx(pSource, 0);
  673. V = __vupkhsb(V);
  674. V = __vupkhsh(V);
  675. V = __vcfsx(V, 0);
  676. return V;
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Compute the portion of the lightmap generated from lightstyles
  680. //-----------------------------------------------------------------------------
  681. static void AccumulateLightstyles( ColorRGBExp32* pLightmap, int lightmapSize, fltx4 vScalar )
  682. {
  683. Assert( pLightmap );
  684. // crush w of the scalar to zero (so we don't overwrite blocklight[x][y][3] in the madds)
  685. vScalar = __vrlimi(vScalar, Four_Zeros, 1, 0);
  686. int lightmapSizeEightAligned = lightmapSize & (~0x07);
  687. // crunch as many groups of eight as possible, then deal with the remainder
  688. AccumulateLightstyles_EightAtAtime(pLightmap, lightmapSizeEightAligned, vScalar, blocklights[0]);
  689. // handle remainders
  690. for (int i = lightmapSizeEightAligned; i < lightmapSize ; ++i )
  691. {
  692. // load four blockLights entries, and four colors
  693. fltx4 vLight;
  694. fltx4 colorLightMap;
  695. vLight = LoadAlignedSIMD(blocklights[0][i].Base());
  696. // unpack the color light maps
  697. // load the unsigned bytes
  698. colorLightMap = XMLoadUByte4(reinterpret_cast<XMUBYTE4 *>(pLightmap + i));
  699. // fish out the exponent component from a signed load
  700. fltx4 exponentiator = XMVectorExpEst(XMVectorSplatW(LoadSignedByte4NoAssert(reinterpret_cast<XMBYTE4 *>(pLightmap + i))));
  701. // scale each of the color light channels by the exponent
  702. colorLightMap = MulSIMD( MulSIMD(colorLightMap, vOneOverTwoFiftyFive ), exponentiator );
  703. Assert( colorLightMap.v[0] == TexLightToLinear( pLightmap[i].r, pLightmap[i].exponent ) &&
  704. colorLightMap.v[1] == TexLightToLinear( pLightmap[i].g, pLightmap[i].exponent ) &&
  705. colorLightMap.v[2] == TexLightToLinear( pLightmap[i].b, pLightmap[i].exponent ) );
  706. // accumulate onto blocklights
  707. vLight = MaddSIMD(vScalar, colorLightMap, vLight);
  708. StoreAlignedSIMD(blocklights[0][i].Base(), vLight);
  709. }
  710. }
  711. static void AccumulateLightstylesFlat( ColorRGBExp32* pLightmap, int lightmapSize, fltx4 vScalar )
  712. {
  713. Assert( pLightmap );
  714. // this isn't a terribly fast way of doing things, but
  715. // this function doesn't seem to be called much (so
  716. // it's not worth the trouble of custom loop scheduling)
  717. fltx4 colorLightMap;
  718. // unpack the color light maps
  719. // load the unsigned bytes
  720. colorLightMap = XMLoadUByte4(reinterpret_cast<XMUBYTE4 *>(pLightmap));
  721. // fish out the exponent component from a signed load
  722. fltx4 exponentiator = XMVectorExpEst(XMVectorSplatW(LoadSignedByte4NoAssert(reinterpret_cast<XMBYTE4 *>(pLightmap))));
  723. // scale each of the color light channels by the exponent
  724. colorLightMap = MulSIMD( MulSIMD(colorLightMap, vOneOverTwoFiftyFive ), exponentiator );
  725. for (int i = 0; i < lightmapSize ; ++i )
  726. {
  727. // load four blockLights entries, and four colors
  728. fltx4 vLight;
  729. vLight = LoadAlignedSIMD(blocklights[0][i].Base());
  730. // accumulate onto blocklights
  731. vLight = MaddSIMD(vScalar, colorLightMap, vLight);
  732. StoreAlignedSIMD(blocklights[0][i].Base(), vLight);
  733. }
  734. }
  735. static void AccumulateBumpedLightstyles( ColorRGBExp32* RESTRICT pLightmap, int lightmapSize, fltx4 vScalar )
  736. {
  737. COMPILE_TIME_ASSERT(sizeof(ColorRGBExp32) == 4); // This function is carefully scheduled around four-byte colors
  738. // crush w of the scalar to zero (so we don't overwrite blocklight[x][y][3] in the madds)
  739. vScalar = __vrlimi(vScalar, Four_Zeros, 1, 0);
  740. /*
  741. ColorRGBExp32 * RESTRICT pBumpedLightmaps[3];
  742. pBumpedLightmaps[1] = pLightmap + lightmapSize;
  743. pBumpedLightmaps[2] = pLightmap + 2 * lightmapSize;
  744. pBumpedLightmaps[3] = pLightmap + 3 * lightmapSize;
  745. */
  746. // assert word (not vector) alignment
  747. AssertMsg( ((reinterpret_cast<unsigned int>(pLightmap) & 0x03 ) == 0), "Lightmap was not word-aligned: AccumulateBumpedLightstyles must fail." );
  748. // assert vector alignment
  749. AssertMsg( (reinterpret_cast<unsigned int>(blocklights) & 0x0F ) == 0, "Blocklights is not vector-aligned. You're doomed." );
  750. AssertMsg( (reinterpret_cast<unsigned int>(blocklights) & 127 ) == 0, "Blocklights is not cache-aligned. Performance will suffer." );
  751. #if 0 // reference: This is the simple version -- four-way accumulate (no interleaving)
  752. for (int i = 0 ; i < lightmapSize ; i+= 4)
  753. {
  754. // load four blockLights entries, and four colors
  755. fltx4 vLight[4];
  756. fltx4 colorLightMap[4];
  757. vLight[0] = LoadUnalignedSIMD(&blocklights[0][i]);
  758. vLight[1] = LoadUnalignedSIMD(&blocklights[0][i+1]);
  759. vLight[2] = LoadUnalignedSIMD(&blocklights[0][i+2]);
  760. vLight[3] = LoadUnalignedSIMD(&blocklights[0][i+3]);
  761. // unpack the color light maps
  762. {
  763. fltx4 zero = Four_Zeros;
  764. fltx4 colorLightmap = LoadUnalignedSIMD(pLightmap+i); // because each colorrgbexp is actually a 32-bit struct,
  765. // this loads four of them into one vector -- they are ubytes for rgb and sbyte for e
  766. // unpack rgbe 0 and 1:
  767. // like an unsigned unpack: { 0x00, colorLightMap[0].r, 0x00, colorLightMap[0].g, 0x00, colorLightMap[0].b, 0x00, colorLightMap[0].e,
  768. // 0x00, colorLightMap[1].r, 0x00, colorLightMap[1].g, 0x00, colorLightMap[1].b, 0x00, colorLightMap[1].e}
  769. fltx4 unsignedUnpackHi = __vmrghb(zero, colorLightMap);
  770. fltx4 unsignedUnpackLo = __vmrghb(zero, colorLightMap); // rgbe words 2 and 3
  771. fltx4 signedUnpackHi = __vupkhsb(colorLightMap); // signed unpack of words 0 and 1, like the unsigned unpack but repl 0x00 w/ sign extension
  772. fltx4 signedUnpackLo = __vupklsb(colorLightMap);
  773. // merge the signed and unsigned unpacks together to make the full halfwords
  774. unsignedUnpackHi = MaskedAssign(vHalfWordMask, signedUnpackHi, unsignedUnpackHi );
  775. unsignedUnpackLo = MaskedAssign(vHalfWordMask, signedUnpackLo, unsignedUnpackLo );
  776. // now complete the unpack from halfwords to words (we can just use signed because there are 0x00's above the rgb channels)
  777. // and convert to float
  778. colorLightMap[0] = __vcfsx( __vupkhsh(unsignedUnpackHi), 0);
  779. colorLightMap[1] = __vcfsx( __vupklsh(unsignedUnpackHi), 0);
  780. colorLightMap[2] = __vcfsx( __vupkhsh(unsignedUnpackLo), 0);
  781. colorLightMap[3] = __vcfsx( __vupklsh(unsignedUnpackLo), 0);
  782. }
  783. // scale each of the color channels by the exponent channel
  784. colorLightMap[0] = XMVectorExpEst( XMVectorSplatW(colorLightMap[0]) );
  785. colorLightMap[1] = XMVectorExpEst( XMVectorSplatW(colorLightMap[1]) );
  786. colorLightMap[2] = XMVectorExpEst( XMVectorSplatW(colorLightMap[2]) );
  787. colorLightMap[3] = XMVectorExpEst( XMVectorSplatW(colorLightMap[3]) );
  788. // accumulate into blocklights
  789. vLight[0] = XMVectorMultiplyAdd(vScalar, colorLightMap[0], vLight[0]);
  790. vLight[1] = XMVectorMultiplyAdd(vScalar, colorLightMap[1], vLight[1]);
  791. vLight[2] = XMVectorMultiplyAdd(vScalar, colorLightMap[2], vLight[2]);
  792. vLight[3] = XMVectorMultiplyAdd(vScalar, colorLightMap[3], vLight[3]);
  793. // save
  794. XMStoreVector4(&blocklights[0][i], vLight[0]);
  795. XMStoreVector4(&blocklights[1][i], vLight[1]);
  796. XMStoreVector4(&blocklights[2][i], vLight[2]);
  797. XMStoreVector4(&blocklights[3][i], vLight[3]);
  798. }
  799. #endif
  800. int lightmapSizeEightAligned = lightmapSize & (~0x07);
  801. // crunch each of the lightmap groups.
  802. for (int mapGroup = 0 ; mapGroup <= 3 ; ++mapGroup, pLightmap += lightmapSize )
  803. {
  804. // process the base lightmap
  805. AccumulateLightstyles_EightAtAtime(pLightmap, lightmapSizeEightAligned, vScalar, blocklights[mapGroup]);
  806. // handle remainders
  807. for (int i = lightmapSizeEightAligned; i < lightmapSize ; ++i )
  808. {
  809. // load four blockLights entries, and four colors
  810. fltx4 vLight;
  811. fltx4 colorLightMap;
  812. vLight = LoadAlignedSIMD(blocklights[mapGroup][i].Base());
  813. // unpack the color light maps
  814. // load the unsigned bytes
  815. colorLightMap = XMLoadUByte4(reinterpret_cast<XMUBYTE4 *>(pLightmap + i));
  816. // fish out the exponent component from a signed load
  817. fltx4 exponentiator = XMVectorExpEst(XMVectorSplatW(LoadSignedByte4NoAssert(reinterpret_cast<XMBYTE4 *>(pLightmap + i))));
  818. // scale each of the color light channels by the exponent
  819. colorLightMap = MulSIMD( MulSIMD(colorLightMap, vOneOverTwoFiftyFive ), exponentiator );
  820. Assert( colorLightMap.v[0] == TexLightToLinear( pLightmap[i].r, pLightmap[i].exponent ) &&
  821. colorLightMap.v[1] == TexLightToLinear( pLightmap[i].g, pLightmap[i].exponent ) &&
  822. colorLightMap.v[2] == TexLightToLinear( pLightmap[i].b, pLightmap[i].exponent ) );
  823. // accumulate onto blocklights
  824. vLight = MaddSIMD(vScalar, colorLightMap, vLight);
  825. StoreAlignedSIMD(blocklights[mapGroup][i].Base(), vLight);
  826. }
  827. // note: pLightmap is incremented as well.
  828. }
  829. }
  830. #endif
  831. //-----------------------------------------------------------------------------
  832. // Compute the portion of the lightmap generated from lightstyles
  833. //-----------------------------------------------------------------------------
  834. static void ComputeLightmapFromLightstyle( msurfacelighting_t *pLighting, bool computeLightmap,
  835. bool computeBumpmap, int lightmapSize, bool hasBumpmapLightmapData )
  836. {
  837. VPROF( "ComputeLightmapFromLightstyle" );
  838. ColorRGBExp32 *pLightmap = pLighting->m_pSamples;
  839. // Compute iteration range
  840. int minmap, maxmap;
  841. #ifdef USE_CONVARS
  842. if( r_lightmap.GetInt() != -1 )
  843. {
  844. minmap = r_lightmap.GetInt();
  845. maxmap = minmap + 1;
  846. }
  847. else
  848. #endif
  849. {
  850. minmap = 0; maxmap = MAXLIGHTMAPS;
  851. }
  852. for (int maps = minmap; maps < maxmap && pLighting->m_nStyles[maps] != 255; ++maps)
  853. {
  854. if( r_lightstyle.GetInt() != -1 && pLighting->m_nStyles[maps] != r_lightstyle.GetInt())
  855. {
  856. continue;
  857. }
  858. float fscalar = LightStyleValue( pLighting->m_nStyles[maps] );
  859. // hack - don't know why we are getting negative values here.
  860. // if (scalar > 0.0f && maps > 0 )
  861. if (fscalar > 0.0f)
  862. {
  863. #ifdef _X360
  864. fltx4 scalar = ReplicateX4(fscalar); // we use SIMD versions of these functions on 360
  865. #else
  866. const float &scalar = fscalar;
  867. #endif
  868. if( computeBumpmap )
  869. {
  870. AccumulateBumpedLightstyles( pLightmap, lightmapSize, scalar );
  871. }
  872. else if( computeLightmap )
  873. {
  874. if (r_avglightmap.GetInt())
  875. {
  876. pLightmap = pLighting->AvgLightColor(maps);
  877. AccumulateLightstylesFlat( pLightmap, lightmapSize, scalar );
  878. }
  879. else
  880. {
  881. AccumulateLightstyles( pLightmap, lightmapSize, scalar );
  882. }
  883. }
  884. }
  885. // skip to next lightmap. If we store lightmap data, we need to jump forward 4
  886. pLightmap += hasBumpmapLightmapData ? lightmapSize * ( NUM_BUMP_VECTS + 1 ) : lightmapSize;
  887. }
  888. }
  889. // instrumentation to measure locks
  890. /*
  891. static CUtlVector<int> g_LightmapLocks;
  892. static int g_Lastdlightframe = -1;
  893. static int g_lastlock = -1;
  894. static int g_unsorted = 0;
  895. void MarkPage( int pageID )
  896. {
  897. if ( g_Lastdlightframe != r_framecount )
  898. {
  899. int total = 0;
  900. int locks = 0;
  901. for ( int i = 0; i < g_LightmapLocks.Count(); i++ )
  902. {
  903. int count = g_LightmapLocks[i];
  904. if ( count )
  905. {
  906. total++;
  907. locks += count;
  908. }
  909. g_LightmapLocks[i] = 0;
  910. }
  911. g_Lastdlightframe = r_framecount;
  912. g_lastlock = -1;
  913. if ( locks )
  914. Msg("Total pages %d, locks %d, unsorted locks %d\n", total, locks, g_unsorted );
  915. g_unsorted = 0;
  916. }
  917. if ( pageID != g_lastlock )
  918. {
  919. g_lastlock = pageID;
  920. g_unsorted++;
  921. }
  922. g_LightmapLocks.EnsureCount(pageID+1);
  923. g_LightmapLocks[pageID]++;
  924. }
  925. */
  926. //-----------------------------------------------------------------------------
  927. // Update the lightmaps...
  928. //-----------------------------------------------------------------------------
  929. static void UpdateLightmapTextures( SurfaceHandle_t surfID, bool needsBumpmap )
  930. {
  931. ASSERT_SURF_VALID( surfID );
  932. if( materialSortInfoArray )
  933. {
  934. int lightmapSize[2];
  935. int offsetIntoLightmapPage[2];
  936. lightmapSize[0] = ( MSurf_LightmapExtents( surfID )[0] ) + 1;
  937. lightmapSize[1] = ( MSurf_LightmapExtents( surfID )[1] ) + 1;
  938. offsetIntoLightmapPage[0] = MSurf_OffsetIntoLightmapPage( surfID )[0];
  939. offsetIntoLightmapPage[1] = MSurf_OffsetIntoLightmapPage( surfID )[1];
  940. Assert( MSurf_MaterialSortID( surfID ) >= 0 &&
  941. MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  942. // FIXME: Should differentiate between bumped and unbumped since the perf characteristics
  943. // are completely different?
  944. // MarkPage( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID );
  945. if( needsBumpmap )
  946. {
  947. materials->UpdateLightmap( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID,
  948. lightmapSize, offsetIntoLightmapPage,
  949. &blocklights[0][0][0], &blocklights[1][0][0], &blocklights[2][0][0], &blocklights[3][0][0] );
  950. }
  951. else
  952. {
  953. materials->UpdateLightmap( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID,
  954. lightmapSize, offsetIntoLightmapPage,
  955. &blocklights[0][0][0], NULL, NULL, NULL );
  956. }
  957. }
  958. }
  959. unsigned int R_UpdateDlightState( dlight_t *pLights, SurfaceHandle_t surfID, const matrix3x4_t& entityToWorld, bool bOnlyUseLightStyles, bool bLightmap )
  960. {
  961. unsigned int dlightMask = 0;
  962. // Mark the surface with the particular cached light values...
  963. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  964. // Retire dlights that are no longer active
  965. pLighting->m_fDLightBits &= r_dlightactive;
  966. pLighting->m_nLastComputedFrame = r_framecount;
  967. // Here, it's got the data it needs. So use it!
  968. if ( !bOnlyUseLightStyles )
  969. {
  970. // add all the dynamic lights
  971. if( bLightmap && ( pLighting->m_nDLightFrame == r_framecount ) )
  972. {
  973. dlightMask = R_ComputeDynamicLightMask( pLights, surfID, pLighting, entityToWorld );
  974. }
  975. if ( !dlightMask || !pLighting->m_fDLightBits )
  976. {
  977. pLighting->m_fDLightBits = 0;
  978. MSurf_Flags(surfID) &= ~SURFDRAW_HASDLIGHT;
  979. }
  980. }
  981. return dlightMask;
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose: Build the blocklights array for a given surface and copy to dest
  985. // Combine and scale multiple lightmaps into the 8.8 format in blocklights
  986. // Input : *psurf - surface to rebuild
  987. // *dest - texture pointer to receive copy in lightmap texture format
  988. // stride - stride of *dest memory
  989. //-----------------------------------------------------------------------------
  990. void R_BuildLightMapGuts( dlight_t *pLights, SurfaceHandle_t surfID, const matrix3x4_t& entityToWorld, unsigned int dlightMask, bool needsBumpmap, bool needsLightmap )
  991. {
  992. VPROF_("R_BuildLightMapGuts", 1, VPROF_BUDGETGROUP_DLIGHT_RENDERING, false, 0);
  993. int bumpID;
  994. // Lightmap data can be dumped to save memory - this precludes any dynamic lighting on the world
  995. Assert( !host_state.worldbrush->unloadedlightmaps );
  996. // Mark the surface with the particular cached light values...
  997. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  998. int size = ComputeLightmapSize( surfID );
  999. if (size == 0)
  1000. return;
  1001. bool hasBumpmap = SurfHasBumpedLightmaps( surfID );
  1002. bool hasLightmap = SurfHasLightmap( surfID );
  1003. // clear to no light
  1004. if( needsLightmap )
  1005. {
  1006. // set to full bright if no light data
  1007. InitLMSamples( blocklights[0], size, hasLightmap ? 0.0f : 1.0f );
  1008. }
  1009. if( needsBumpmap )
  1010. {
  1011. // set to full bright if no light data
  1012. for( bumpID = 1; bumpID < NUM_BUMP_VECTS + 1; bumpID++ )
  1013. {
  1014. InitLMSamples( blocklights[bumpID], size, hasBumpmap ? 0.0f : 1.0f );
  1015. }
  1016. }
  1017. // add all the lightmaps
  1018. // Here, it's got the data it needs. So use it!
  1019. if( ( hasLightmap && needsLightmap ) || ( hasBumpmap && needsBumpmap ) )
  1020. {
  1021. ComputeLightmapFromLightstyle( pLighting, ( hasLightmap && needsLightmap ),
  1022. ( hasBumpmap && needsBumpmap ), size, hasBumpmap );
  1023. }
  1024. else if( !hasBumpmap && needsBumpmap && hasLightmap )
  1025. {
  1026. // make something up for the bumped lights if you need them but don't have the data
  1027. // if you have a lightmap, use that, otherwise fullbright
  1028. ComputeLightmapFromLightstyle( pLighting, true, false, size, hasBumpmap );
  1029. for( bumpID = 0; bumpID < ( hasBumpmap ? ( NUM_BUMP_VECTS + 1 ) : 1 ); bumpID++ )
  1030. {
  1031. for (int i=0 ; i<size ; i++)
  1032. {
  1033. blocklights[bumpID][i].AsVector3D() = blocklights[0][i].AsVector3D();
  1034. }
  1035. }
  1036. }
  1037. else if( needsBumpmap && !hasLightmap )
  1038. {
  1039. // set to full bright if no light data
  1040. InitLMSamples( blocklights[1], size, 0.0f );
  1041. InitLMSamples( blocklights[2], size, 0.0f );
  1042. InitLMSamples( blocklights[3], size, 0.0f );
  1043. }
  1044. else if( !needsBumpmap && !needsLightmap )
  1045. {
  1046. }
  1047. else if( needsLightmap && !hasLightmap )
  1048. {
  1049. }
  1050. else
  1051. {
  1052. Assert( 0 );
  1053. }
  1054. // add all the dynamic lights
  1055. if ( dlightMask && (needsLightmap || needsBumpmap) )
  1056. {
  1057. R_AddDynamicLights( pLights, surfID, entityToWorld, needsBumpmap, dlightMask );
  1058. }
  1059. // Update the texture state
  1060. UpdateLightmapTextures( surfID, needsBumpmap );
  1061. }
  1062. void R_BuildLightMap( dlight_t *pLights, ICallQueue *pCallQueue, SurfaceHandle_t surfID, const matrix3x4_t &entityToWorld, bool bOnlyUseLightStyles )
  1063. {
  1064. bool needsBumpmap = SurfNeedsBumpedLightmaps( surfID );
  1065. bool needsLightmap = SurfNeedsLightmap( surfID );
  1066. if( !needsBumpmap && !needsLightmap )
  1067. return;
  1068. if( materialSortInfoArray )
  1069. {
  1070. Assert( MSurf_MaterialSortID( surfID ) >= 0 &&
  1071. MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  1072. if (( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID == MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE ) ||
  1073. ( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID == MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP ) )
  1074. {
  1075. return;
  1076. }
  1077. }
  1078. bool bDlightsInLightmap = needsLightmap || needsBumpmap;
  1079. unsigned int dlightMask = R_UpdateDlightState( pLights, surfID, entityToWorld, bOnlyUseLightStyles, bDlightsInLightmap );
  1080. // update the state, but don't render any dlights if only lightstyles requested
  1081. if ( bOnlyUseLightStyles )
  1082. dlightMask = 0;
  1083. if ( !pCallQueue )
  1084. {
  1085. R_BuildLightMapGuts( pLights, surfID, entityToWorld, dlightMask, needsBumpmap, needsLightmap );
  1086. }
  1087. else
  1088. {
  1089. pCallQueue->QueueCall( R_BuildLightMapGuts, pLights, surfID, RefToVal( entityToWorld ), dlightMask, needsBumpmap, needsLightmap );
  1090. }
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // Purpose: Save off the average light values, and dump the rest of the lightmap data.
  1094. // Can be used to save memory, at the expense of dynamic lights and lightstyles.
  1095. //-----------------------------------------------------------------------------
  1096. void CacheAndUnloadLightmapData()
  1097. {
  1098. Assert( !g_bHunkAllocLightmaps );
  1099. if ( g_bHunkAllocLightmaps )
  1100. {
  1101. return;
  1102. }
  1103. worldbrushdata_t *pBrushData = host_state.worldbrush;
  1104. msurfacelighting_t *pLighting = pBrushData->surfacelighting;
  1105. int numSurfaces = pBrushData->numsurfaces;
  1106. // This will allocate more data than necessary, but only 1-2K max
  1107. byte *pDestBase = (byte*)malloc( numSurfaces * MAXLIGHTMAPS * sizeof( ColorRGBExp32 ) );
  1108. byte *pDest = pDestBase;
  1109. for ( int i = 0; i < numSurfaces; ++i, ++pLighting )
  1110. {
  1111. int nStyleCt = 0;
  1112. for ( int map = 0 ; map < MAXLIGHTMAPS; ++map )
  1113. {
  1114. if ( pLighting->m_nStyles[map] != 255 )
  1115. ++nStyleCt;
  1116. }
  1117. const int nHdrBytes = nStyleCt * sizeof( ColorRGBExp32 );
  1118. byte *pHdr = (byte*)pLighting->m_pSamples - nHdrBytes;
  1119. // Copy just the 0-4 average color entries
  1120. Q_memcpy( pDest, pHdr, nHdrBytes );
  1121. // m_pSamples needs to point AFTER the average color data
  1122. pDest += nHdrBytes;
  1123. pLighting->m_pSamples = (ColorRGBExp32*)pDest;
  1124. }
  1125. // Update the lightdata pointer
  1126. free( host_state.worldbrush->lightdata );
  1127. host_state.worldbrush->lightdata = (ColorRGBExp32*)pDestBase;
  1128. host_state.worldbrush->unloadedlightmaps = true;
  1129. }
  1130. //sorts the surfaces in place
  1131. static void SortSurfacesByLightmapID( SurfaceHandle_t *pToSort, int iSurfaceCount )
  1132. {
  1133. SurfaceHandle_t *pSortTemp = (SurfaceHandle_t *)stackalloc( sizeof( SurfaceHandle_t ) * iSurfaceCount );
  1134. //radix sort
  1135. for( int radix = 0; radix != 4; ++radix )
  1136. {
  1137. //swap the inputs for the next pass
  1138. {
  1139. SurfaceHandle_t *pTemp = pToSort;
  1140. pToSort = pSortTemp;
  1141. pSortTemp = pTemp;
  1142. }
  1143. int iCounts[256] = { 0 };
  1144. int iBitOffset = radix * 8;
  1145. for( int i = 0; i != iSurfaceCount; ++i )
  1146. {
  1147. uint8 val = (materialSortInfoArray[MSurf_MaterialSortID( pSortTemp[i] )].lightmapPageID >> iBitOffset) & 0xFF;
  1148. ++iCounts[val];
  1149. }
  1150. int iOffsetTable[256];
  1151. iOffsetTable[0] = 0;
  1152. for( int i = 0; i != 255; ++i )
  1153. {
  1154. iOffsetTable[i + 1] = iOffsetTable[i] + iCounts[i];
  1155. }
  1156. for( int i = 0; i != iSurfaceCount; ++i )
  1157. {
  1158. uint8 val = (materialSortInfoArray[MSurf_MaterialSortID( pSortTemp[i] )].lightmapPageID >> iBitOffset) & 0xFF;
  1159. int iWriteIndex = iOffsetTable[val];
  1160. pToSort[iWriteIndex] = pSortTemp[i];
  1161. ++iOffsetTable[val];
  1162. }
  1163. }
  1164. }
  1165. void R_RedownloadAllLightmaps()
  1166. {
  1167. #ifdef _DEBUG
  1168. static bool initializedBlockLights = false;
  1169. if (!initializedBlockLights)
  1170. {
  1171. memset( &blocklights[0][0][0], 0, MAX_LIGHTMAP_DIM_INCLUDING_BORDER * MAX_LIGHTMAP_DIM_INCLUDING_BORDER * (NUM_BUMP_VECTS + 1) * sizeof( Vector ) );
  1172. initializedBlockLights = true;
  1173. }
  1174. #endif
  1175. double st = Sys_FloatTime();
  1176. bool bOnlyUseLightStyles = false;
  1177. if( r_dynamic.GetInt() == 0 )
  1178. {
  1179. bOnlyUseLightStyles = true;
  1180. }
  1181. // Can't build lightmaps if the source data has been dumped
  1182. CMatRenderContextPtr pRenderContext( materials );
  1183. ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
  1184. if ( !host_state.worldbrush->unloadedlightmaps )
  1185. {
  1186. int iSurfaceCount = host_state.worldbrush->numsurfaces;
  1187. SurfaceHandle_t *pSortedSurfaces = (SurfaceHandle_t *)stackalloc( sizeof( SurfaceHandle_t ) * iSurfaceCount );
  1188. for( int surfaceIndex = 0; surfaceIndex < iSurfaceCount; surfaceIndex++ )
  1189. {
  1190. SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfaceIndex );
  1191. pSortedSurfaces[surfaceIndex] = surfID;
  1192. }
  1193. SortSurfacesByLightmapID( pSortedSurfaces, iSurfaceCount ); //sorts in place, so now the array really is sorted
  1194. if( pCallQueue )
  1195. pCallQueue->QueueCall( materials, &IMaterialSystem::BeginUpdateLightmaps );
  1196. else
  1197. materials->BeginUpdateLightmaps();
  1198. matrix3x4_t xform;
  1199. SetIdentityMatrix(xform);
  1200. for( int surfaceIndex = 0; surfaceIndex < iSurfaceCount; surfaceIndex++ )
  1201. {
  1202. SurfaceHandle_t surfID = pSortedSurfaces[surfaceIndex];
  1203. ASSERT_SURF_VALID( surfID );
  1204. R_BuildLightMap( &cl_dlights[0], pCallQueue, surfID, xform, bOnlyUseLightStyles );
  1205. }
  1206. if( pCallQueue )
  1207. pCallQueue->QueueCall( materials, &IMaterialSystem::EndUpdateLightmaps );
  1208. else
  1209. materials->EndUpdateLightmaps();
  1210. if ( !g_bHunkAllocLightmaps && r_unloadlightmaps.GetInt() == 1 )
  1211. {
  1212. // Delete the lightmap data from memory
  1213. if ( !pCallQueue )
  1214. {
  1215. CacheAndUnloadLightmapData();
  1216. }
  1217. else
  1218. {
  1219. pCallQueue->QueueCall( CacheAndUnloadLightmapData );
  1220. }
  1221. }
  1222. }
  1223. float elapsed = ( float )( Sys_FloatTime() - st ) * 1000.0;
  1224. DevMsg( "R_RedownloadAllLightmaps took %.3f msec!\n", elapsed );
  1225. g_RebuildLightmaps = false;
  1226. }
  1227. //-----------------------------------------------------------------------------
  1228. // Purpose: flag the lightmaps as needing to be rebuilt (gamma change)
  1229. //-----------------------------------------------------------------------------
  1230. bool g_RebuildLightmaps = false;
  1231. void GL_RebuildLightmaps( void )
  1232. {
  1233. g_RebuildLightmaps = true;
  1234. }
  1235. //-----------------------------------------------------------------------------
  1236. // Purpose: Update the in-RAM texture for the given surface's lightmap
  1237. // Input : *fa - surface pointer
  1238. //-----------------------------------------------------------------------------
  1239. #ifdef UPDATE_LIGHTSTYLES_EVERY_FRAME
  1240. ConVar mat_updatelightstyleseveryframe( "mat_updatelightstyleseveryframe", "0" );
  1241. #endif
  1242. void FASTCALL R_RenderDynamicLightmaps ( dlight_t *pLights, ICallQueue *pCallQueue, SurfaceHandle_t surfID, const matrix3x4_t &xform )
  1243. {
  1244. VPROF_BUDGET( "R_RenderDynamicLightmaps", VPROF_BUDGETGROUP_DLIGHT_RENDERING );
  1245. ASSERT_SURF_VALID( surfID );
  1246. int fSurfFlags = MSurf_Flags( surfID );
  1247. if( fSurfFlags & SURFDRAW_NOLIGHT )
  1248. return;
  1249. // check for lightmap modification
  1250. bool bChanged = false;
  1251. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  1252. if( fSurfFlags & SURFDRAW_HASLIGHTSYTLES )
  1253. {
  1254. #ifdef UPDATE_LIGHTSTYLES_EVERY_FRAME
  1255. if( mat_updatelightstyleseveryframe.GetBool() && ( pLighting->m_nStyles[0] != 0 || pLighting->m_nStyles[1] != 255 ) )
  1256. {
  1257. bChanged = true;
  1258. }
  1259. #endif
  1260. for( int maps = 0; maps < MAXLIGHTMAPS && pLighting->m_nStyles[maps] != 255; maps++ )
  1261. {
  1262. if( d_lightstyleframe[pLighting->m_nStyles[maps]] > pLighting->m_nLastComputedFrame )
  1263. {
  1264. bChanged = true;
  1265. break;
  1266. }
  1267. }
  1268. }
  1269. // was it dynamic this frame (pLighting->m_nDLightFrame == r_framecount)
  1270. // or dynamic previously (pLighting->m_fDLightBits)
  1271. bool bDLightChanged = ( pLighting->m_nDLightFrame == r_framecount ) || pLighting->m_fDLightBits;
  1272. bool bOnlyUseLightStyles = false;
  1273. if( r_dynamic.GetInt() == 0 )
  1274. {
  1275. bOnlyUseLightStyles = true;
  1276. bDLightChanged = false;
  1277. }
  1278. if ( bChanged || bDLightChanged )
  1279. {
  1280. R_BuildLightMap( pLights, pCallQueue, surfID, xform, bOnlyUseLightStyles );
  1281. }
  1282. }