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.

3009 lines
91 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "quakedef.h"
  8. #include "lightcache.h"
  9. #include "cmodel_engine.h"
  10. #include "istudiorender.h"
  11. #include "studio_internal.h"
  12. #include "bspfile.h"
  13. #include "cdll_engine_int.h"
  14. #include "tier1/mempool.h"
  15. #include "gl_model_private.h"
  16. #include "r_local.h"
  17. #include "materialsystem/imaterialsystemhardwareconfig.h"
  18. #include "materialsystem/imaterialsystem.h"
  19. #include "materialsystem/imaterial.h"
  20. #include "materialsystem/imaterialvar.h"
  21. #include "l_studio.h"
  22. #include "debugoverlay.h"
  23. #include "worldsize.h"
  24. #include "ispatialpartitioninternal.h"
  25. #include "staticpropmgr.h"
  26. #include "cmodel_engine.h"
  27. #include "icliententitylist.h"
  28. #include "icliententity.h"
  29. #include "enginetrace.h"
  30. #include "client.h"
  31. #include "cl_main.h"
  32. #include "collisionutils.h"
  33. #include "tier0/vprof.h"
  34. #include "filesystem_engine.h"
  35. #include "mathlib/anorms.h"
  36. #include "gl_matsysiface.h"
  37. #include "materialsystem/materialsystem_config.h"
  38. #include "tier2/tier2.h"
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. // EMIT_SURFACE LIGHTS:
  42. //
  43. // Dim emit_surface lights go in the ambient cube because there are a ton of them and they are often so dim that
  44. // they get filtered out by r_worldlightmin.
  45. //
  46. // (Dim) emit_surface lights only get calculated at runtime for static props because static props
  47. // do the full calculation of ambient lighting at runtime instead of using vrad's per-leaf
  48. // calculation. Vrad's calculation includes the emit_surfaces, so if we're NOT using it, then
  49. // we want to include emit_surface lights here.
  50. // this should be prime to make the hash better
  51. #define MAX_CACHE_ENTRY 200
  52. #define MAX_CACHE_BUCKETS MAX_CACHE_ENTRY
  53. // number of bits per grid in x, y, z
  54. #define HASH_GRID_SIZEX 5
  55. #define HASH_GRID_SIZEY 5
  56. #define HASH_GRID_SIZEZ 7
  57. #define LIGHTCACHE_SNAP_EPSILON 0.5f
  58. float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta, bool bNoRadiusCheck = false );
  59. float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta );
  60. #define MAX_LIGHTSTYLE_BITS MAX_LIGHTSTYLES
  61. #define MAX_LIGHTSTYLE_BYTES ( (MAX_LIGHTSTYLE_BITS + 7) / 8 )
  62. static byte g_FrameMissCount = 0;
  63. static int g_FrameIndex = 0;
  64. ConVar lightcache_maxmiss("lightcache_maxmiss","2", FCVAR_CHEAT);
  65. #define NUMRANDOMNORMALS 162
  66. static Vector s_raddir[NUMRANDOMNORMALS] = {
  67. #include "randomnormals.h"
  68. };
  69. static ConVar r_lightcache_numambientsamples( "r_lightcache_numambientsamples", "162", FCVAR_CHEAT,
  70. "number of random directions to fire rays when computing ambient lighting",
  71. true, 1.0f, true, ( float )NUMRANDOMNORMALS );
  72. ConVar r_ambientlightingonly(
  73. "r_ambientlightingonly",
  74. "0",
  75. FCVAR_CHEAT,
  76. "Set this to 1 to light models with only ambient lighting (and no static lighting)." );
  77. ConVar r_oldlightselection("r_oldlightselection", "0", FCVAR_CHEAT, "Set this to revert to HL2's method of selecting lights");
  78. static void ComputeAmbientFromSphericalSamples( const Vector& start,
  79. Vector* lightBoxColor );
  80. //-----------------------------------------------------------------------------
  81. // Cache used to compute which lightcache entries computed this frame
  82. // may be able to be used temporarily for lighting other objects in the
  83. // case where we've got too many new lightcache samples in a single frame
  84. //-----------------------------------------------------------------------------
  85. struct CacheInfo_t
  86. {
  87. int x;
  88. int y;
  89. int z;
  90. int leaf;
  91. };
  92. //-----------------------------------------------------------------------------
  93. // Flags to pass into LightIntensityAndDirectionAtPoint + LightIntensityAndDirectionInBox
  94. //-----------------------------------------------------------------------------
  95. enum LightIntensityFlags_t
  96. {
  97. LIGHT_NO_OCCLUSION_CHECK = 0x1,
  98. LIGHT_NO_RADIUS_CHECK = 0x2,
  99. LIGHT_OCCLUDE_VS_PROPS = 0x4,
  100. LIGHT_IGNORE_LIGHTSTYLE_VALUE = 0x8,
  101. };
  102. //-----------------------------------------------------------------------------
  103. // Lightcache entry
  104. //-----------------------------------------------------------------------------
  105. enum
  106. {
  107. HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE = 0x1,
  108. HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE = 0x2, // flickering lights
  109. HACKLIGHTCACHEFLAGS_HASDONESTATICLIGHTING = 0x4, // for static props
  110. };
  111. struct LightingStateInfo_t
  112. {
  113. float m_pIllum[MAXLOCALLIGHTS];
  114. bool m_LightingStateHasSkylight;
  115. LightingStateInfo_t()
  116. {
  117. memset( this, 0, sizeof( *this ) );
  118. }
  119. void Clear()
  120. {
  121. memset( this, 0, sizeof( *this ) );
  122. }
  123. };
  124. // This holds the shared data between lightcache_t and PropLightcache_t.
  125. // This way, PropLightcache_t can be about half the size, since it doesn't need a bunch of data in lightcache_t.
  126. class CBaseLightCache : public LightingStateInfo_t
  127. {
  128. public:
  129. CBaseLightCache()
  130. {
  131. m_pEnvCubemapTexture = NULL;
  132. memset( m_pLightstyles, 0, sizeof( m_pLightstyles ) );
  133. m_LightingFlags = 0;
  134. m_LastFrameUpdated_LightStyles = -1;
  135. }
  136. bool HasLightStyle()
  137. {
  138. return ( m_LightingFlags & ( HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE | HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE ) ) ? true : false;
  139. }
  140. bool HasSwitchableLightStyle()
  141. {
  142. return ( m_LightingFlags & HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE ) ? true : false;
  143. }
  144. bool HasNonSwitchableLightStyle()
  145. {
  146. return ( m_LightingFlags & HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE ) ? true : false;
  147. }
  148. public:
  149. // cache for static lighting . . never changes after cache creation
  150. // preserved because static prop's color meshes are under cache control
  151. LightingState_t m_StaticLightingState;
  152. // cache for light styles
  153. LightingState_t m_LightStyleLightingState; // This includes m_StaticLightingState
  154. int m_LastFrameUpdated_LightStyles;
  155. LightingState_t m_DynamicLightingState; // This includes m_LightStyleLightingState
  156. int m_LastFrameUpdated_DynamicLighting;
  157. // FIXME: could just use m_LightStyleWorldLights.Count() if we are a static prop
  158. int m_LightingFlags; /* LightCacheFlags_t */
  159. int leaf;
  160. unsigned char m_pLightstyles[MAX_LIGHTSTYLE_BYTES];
  161. // for a dynamic prop, ideally the cache center if valid space, otherwise initial origin
  162. // for a static prop, the provided origin
  163. Vector m_LightingOrigin;
  164. // env_cubemap texture associated with this entry.
  165. ITexture * m_pEnvCubemapTexture;
  166. };
  167. class lightcache_t : public CBaseLightCache
  168. {
  169. public:
  170. lightcache_t()
  171. {
  172. m_LastFrameUpdated_DynamicLighting = -1;
  173. }
  174. public:
  175. // Precalculated for the static lighting from AddWorldLightToLightingState.
  176. dworldlight_t *m_StaticPrecalc_LocalLight[MAXLOCALLIGHTS];
  177. unsigned short m_StaticPrecalc_NumLocalLights;
  178. LightingStateInfo_t m_StaticPrecalc_LightingStateInfo;
  179. // the boxcolor is stored in m_StaticLightingState.
  180. // bucket singly linked list.
  181. unsigned short next; // index into lightcache
  182. unsigned short bucket; // index into lightbuckets
  183. // lru links
  184. unsigned short lru_prev;
  185. unsigned short lru_next;
  186. int x,y,z;
  187. };
  188. struct PropLightcache_t : public CBaseLightCache
  189. {
  190. public:
  191. // Linked into s_pAllStaticProps.
  192. PropLightcache_t *m_pNextPropLightcache;
  193. unsigned int m_Flags; // corresponds to LIGHTCACHEFLAGS_*
  194. // stuff for pushing lights onto static props
  195. int m_DLightActive; // bit field for which dlights currently affect us.
  196. // recomputed by AddDlightsForStaticProps
  197. int m_DLightMarkFrame; // last frame in which a dlight was marked on this prop (helps detect lights that are marked but have moved away from this prop)
  198. CUtlVector<short> m_LightStyleWorldLights; // This is a list of lights that affect this static prop cache entry.
  199. int m_SwitchableLightFrame; // This is the last frame that switchable lights were calculated.
  200. Vector mins; // fixme: make these smaller
  201. Vector maxs; // fixme: make these smaller
  202. bool HasDlights() { return m_DLightActive ? true : false; }
  203. PropLightcache_t()
  204. {
  205. m_Flags = 0;
  206. m_SwitchableLightFrame = -1;
  207. m_DLightActive = 0;
  208. m_DLightMarkFrame = 0;
  209. }
  210. };
  211. ConVar r_worldlights ("r_worldlights", "4", 0, "number of world lights to use per vertex" );
  212. ConVar r_radiosity ("r_radiosity", "4", FCVAR_CHEAT, "0: no radiosity\n1: radiosity with ambient cube (6 samples)\n2: radiosity with 162 samples\n3: 162 samples for static props, 6 samples for everything else" );
  213. ConVar r_worldlightmin ("r_worldlightmin", "0.0002" );
  214. ConVar r_avglight ("r_avglight", "1", FCVAR_CHEAT);
  215. static ConVar r_drawlightcache ("r_drawlightcache", "0", FCVAR_CHEAT, "0: off\n1: draw light cache entries\n2: draw rays\n");
  216. static ConVar r_minnewsamples ("r_minnewsamples", "3");
  217. static ConVar r_maxnewsamples ("r_maxnewsamples", "6");
  218. static ConVar r_maxsampledist ("r_maxsampledist", "128");
  219. static ConVar r_lightcachecenter ("r_lightcachecenter", "1", FCVAR_CHEAT );
  220. // head and tail sentinels of the LRU
  221. #define LIGHT_LRU_HEAD_INDEX MAX_CACHE_ENTRY
  222. #define LIGHT_LRU_TAIL_INDEX (MAX_CACHE_ENTRY+1)
  223. static lightcache_t lightcache[MAX_CACHE_ENTRY + 2]; // the extra 2 are the head and tail
  224. static unsigned short lightbuckets[MAX_CACHE_BUCKETS];
  225. static CClassMemoryPool<PropLightcache_t> s_PropCache( 256, CClassMemoryPool<lightcache_t>::GROW_SLOW );
  226. // A memory pool of lightcache entries that is
  227. static int cached_r_worldlights = -1;
  228. static int cached_r_radiosity = -1;
  229. static int cached_r_avglight = -1;
  230. static int cached_mat_fullbright = -1;
  231. static int cached_r_lightcache_numambientsamples = -1;
  232. static PropLightcache_t* s_pAllStaticProps = NULL;
  233. // Used to convert RGB colors to greyscale intensity
  234. static Vector s_Grayscale( 0.299f, 0.587f, 0.114f );
  235. #define BIT_SET( a, b ) ((a)[(b)>>3] & (1<<((b)&7)))
  236. inline unsigned short GetLightCacheIndex( const lightcache_t *pCache )
  237. {
  238. return pCache - lightcache;
  239. }
  240. inline lightcache_t& GetLightLRUHead()
  241. {
  242. return lightcache[LIGHT_LRU_HEAD_INDEX];
  243. }
  244. inline lightcache_t& GetLightLRUTail()
  245. {
  246. return lightcache[LIGHT_LRU_TAIL_INDEX];
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Set up the LRU
  250. //-----------------------------------------------------------------------------
  251. void R_StudioInitLightingCache( void )
  252. {
  253. unsigned short i;
  254. memset( lightcache, 0, sizeof(lightcache) );
  255. for ( i=0; i < ARRAYSIZE( lightcache ); i++ )
  256. lightcache[i].bucket = 0xFFFF;
  257. for ( i=0; i < ARRAYSIZE( lightbuckets ); i++ )
  258. lightbuckets[i] = 0xFFFF;
  259. unsigned short last = LIGHT_LRU_HEAD_INDEX;
  260. // Link every node into the LRU
  261. for ( i = 0; i < MAX_CACHE_ENTRY-1; i++)
  262. {
  263. lightcache[i].lru_prev = last;
  264. lightcache[i].lru_next = i + 1;
  265. last = i;
  266. }
  267. // terminate the lru list
  268. lightcache[i].lru_prev = last;
  269. lightcache[i].lru_next = LIGHT_LRU_TAIL_INDEX;
  270. // link the sentinels
  271. lightcache[LIGHT_LRU_HEAD_INDEX].lru_next = 0;
  272. lightcache[LIGHT_LRU_TAIL_INDEX].lru_prev = i;
  273. // Lower number of lights on older hardware
  274. if ( g_pMaterialSystemHardwareConfig->MaxNumLights() < r_worldlights.GetInt() )
  275. {
  276. r_worldlights.SetValue( g_pMaterialSystemHardwareConfig->MaxNumLights() );
  277. }
  278. cached_r_worldlights = r_worldlights.GetInt();
  279. cached_r_radiosity = r_radiosity.GetInt();
  280. cached_r_avglight = r_avglight.GetInt();
  281. cached_mat_fullbright = g_pMaterialSystemConfig->nFullbright;
  282. cached_r_lightcache_numambientsamples = r_lightcache_numambientsamples.GetInt();
  283. // Recompute all static lighting
  284. InvalidateStaticLightingCache();
  285. }
  286. void R_StudioCheckReinitLightingCache()
  287. {
  288. // Make sure this stays clamped to match hardware capabilities
  289. if ( g_pMaterialSystemHardwareConfig->MaxNumLights() < r_worldlights.GetInt() )
  290. {
  291. r_worldlights.SetValue( g_pMaterialSystemHardwareConfig->MaxNumLights() );
  292. }
  293. // Flush the lighting cache, if necessary
  294. if (cached_r_worldlights != r_worldlights.GetInt() ||
  295. cached_r_radiosity != r_radiosity.GetInt() ||
  296. cached_r_avglight != r_avglight.GetInt() ||
  297. cached_mat_fullbright != g_pMaterialSystemConfig->nFullbright ||
  298. cached_r_lightcache_numambientsamples != r_lightcache_numambientsamples.GetInt() )
  299. {
  300. R_StudioInitLightingCache();
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: Moves this cache entry to the end of the lru, i.e. marks it recently used
  305. // Input : *pcache -
  306. //-----------------------------------------------------------------------------
  307. static void LightcacheMark( lightcache_t *pcache )
  308. {
  309. // don't link in static lighting
  310. if ( !pcache->lru_next && !pcache->lru_prev )
  311. return;
  312. // already at tail
  313. if ( GetLightCacheIndex( pcache ) == lightcache[LIGHT_LRU_TAIL_INDEX].lru_prev )
  314. return;
  315. // unlink pcache
  316. lightcache[pcache->lru_prev].lru_next = pcache->lru_next;
  317. lightcache[pcache->lru_next].lru_prev = pcache->lru_prev;
  318. // link to tail
  319. // patch backward link
  320. lightcache[GetLightLRUTail().lru_prev].lru_next = GetLightCacheIndex( pcache );
  321. pcache->lru_prev = GetLightLRUTail().lru_prev;
  322. // patch forward link
  323. pcache->lru_next = LIGHT_LRU_TAIL_INDEX;
  324. GetLightLRUTail().lru_prev = GetLightCacheIndex( pcache );
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose: Unlink a cache entry from its current bucket
  328. // Input : *pcache -
  329. //-----------------------------------------------------------------------------
  330. static void LightcacheUnlink( lightcache_t *pcache )
  331. {
  332. unsigned short iBucket = pcache->bucket;
  333. // not used yet?
  334. if ( iBucket == 0xFFFF )
  335. return;
  336. unsigned short iCache = GetLightCacheIndex( pcache );
  337. // unlink it
  338. unsigned short plist = lightbuckets[iBucket];
  339. if ( plist == iCache )
  340. {
  341. // head of bucket? move bucket down
  342. lightbuckets[iBucket] = pcache->next;
  343. }
  344. else
  345. {
  346. bool found = false;
  347. // walk the bucket
  348. while ( plist != 0xFFFF )
  349. {
  350. // if next is pcache, unlink pcache
  351. if ( lightcache[plist].next == iCache )
  352. {
  353. lightcache[plist].next = pcache->next;
  354. found = true;
  355. break;
  356. }
  357. plist = lightcache[plist].next;
  358. }
  359. assert(found);
  360. }
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: Get the least recently used cache entry
  364. //-----------------------------------------------------------------------------
  365. static lightcache_t *LightcacheGetLRU( void )
  366. {
  367. // grab head
  368. lightcache_t *pcache = &lightcache[GetLightLRUHead().lru_next];
  369. // move to tail
  370. LightcacheMark( pcache );
  371. // unlink from the bucket
  372. LightcacheUnlink( pcache );
  373. pcache->leaf = -1;
  374. return pcache;
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Quick & Dirty hashing function to bucket the cube in 4d parameter space
  378. //-----------------------------------------------------------------------------
  379. static int LightcacheHashKey( int x, int y, int z, int leaf )
  380. {
  381. unsigned int key = (((x<<20) + (y<<8) + z) ^ (leaf));
  382. key = key % MAX_CACHE_BUCKETS;
  383. return (int)key;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Compute the lightcache bucket given a position
  387. //-----------------------------------------------------------------------------
  388. static lightcache_t* FindInCache( int bucket, int x, int y, int z, int leaf )
  389. {
  390. // loop over the entries in this bucket
  391. unsigned short iCache;
  392. for ( iCache = lightbuckets[bucket]; iCache != 0xFFFF; iCache = lightcache[iCache].next )
  393. {
  394. lightcache_t *pCache = &lightcache[iCache];
  395. // hit?
  396. if (pCache->x == x && pCache->y == y && pCache->z == z && pCache->leaf == leaf )
  397. {
  398. return pCache;
  399. }
  400. }
  401. return 0;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Links to a bucket
  405. //-----------------------------------------------------------------------------
  406. static inline void LinkToBucket( int bucket, lightcache_t* pcache )
  407. {
  408. pcache->next = lightbuckets[bucket];
  409. lightbuckets[bucket] = GetLightCacheIndex( pcache );
  410. // point back to the bucket
  411. pcache->bucket = (unsigned short)bucket;
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Links in a new lightcache entry
  415. //-----------------------------------------------------------------------------
  416. static lightcache_t* NewLightcacheEntry( int bucket )
  417. {
  418. // re-use the LRU cache entry
  419. lightcache_t* pcache = LightcacheGetLRU();
  420. LinkToBucket( bucket, pcache );
  421. return pcache;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Compute the lightcache origin
  425. //-----------------------------------------------------------------------------
  426. #if 0
  427. static inline void ComputeLightcacheOrigin( int x, int y, int z, Vector& org )
  428. {
  429. // this is suspicious and *maybe* wrong
  430. // the bucket origin can't re-establish the correct negative numbers
  431. // because of the non-arithmetic shift down?
  432. int ix = x << HASH_GRID_SIZEX;
  433. int iy = y << HASH_GRID_SIZEY;
  434. int iz = z << HASH_GRID_SIZEZ;
  435. org.Init( ix, iy, iz );
  436. }
  437. #endif
  438. //-----------------------------------------------------------------------------
  439. // Compute the lightcache bounds given a point
  440. //-----------------------------------------------------------------------------
  441. void ComputeLightcacheBounds( const Vector &vecOrigin, Vector *pMins, Vector *pMaxs )
  442. {
  443. bool bXPos = (vecOrigin[0] >= 0);
  444. bool bYPos = (vecOrigin[1] >= 0);
  445. bool bZPos = (vecOrigin[2] >= 0);
  446. // can't snap and shift negative values
  447. // truncate positive number and shift
  448. int ix = ((int)(fabs(vecOrigin[0]))) >> HASH_GRID_SIZEX;
  449. int iy = ((int)(fabs(vecOrigin[1]))) >> HASH_GRID_SIZEY;
  450. int iz = ((int)(fabs(vecOrigin[2]))) >> HASH_GRID_SIZEZ;
  451. // mins is floored as fixup depending on <0 or >0
  452. pMins->x = (bXPos ? ix : -(ix + 1)) << HASH_GRID_SIZEX;
  453. pMins->y = (bYPos ? iy : -(iy + 1)) << HASH_GRID_SIZEY;
  454. pMins->z = (bZPos ? iz : -(iz + 1)) << HASH_GRID_SIZEZ;
  455. // maxs is exactly one grid increasing from mins
  456. pMaxs->x = pMins->x + (1 << HASH_GRID_SIZEX );
  457. pMaxs->y = pMins->y + (1 << HASH_GRID_SIZEY );
  458. pMaxs->z = pMins->z + (1 << HASH_GRID_SIZEZ );
  459. Assert( (pMins->x <= vecOrigin.x) && (pMins->y <= vecOrigin.y) && (pMins->z <= vecOrigin.z) );
  460. Assert( (pMaxs->x >= vecOrigin.x) && (pMaxs->y >= vecOrigin.y) && (pMaxs->z >= vecOrigin.z) );
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Compute the cache origin suitable for key
  464. //-----------------------------------------------------------------------------
  465. static inline void OriginToCacheOrigin( const Vector &origin, int &x, int &y, int &z )
  466. {
  467. x = ((int)origin[0] + 32768) >> HASH_GRID_SIZEX;
  468. y = ((int)origin[1] + 32768) >> HASH_GRID_SIZEY;
  469. z = ((int)origin[2] + 32768) >> HASH_GRID_SIZEZ;
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Finds ambient lights
  473. //-----------------------------------------------------------------------------
  474. dworldlight_t* FindAmbientLight()
  475. {
  476. // find any ambient lights
  477. for (int i = 0; i < host_state.worldbrush->numworldlights; i++)
  478. {
  479. if (host_state.worldbrush->worldlights[i].type == emit_skyambient)
  480. {
  481. return &host_state.worldbrush->worldlights[i];
  482. }
  483. }
  484. return 0;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Computes the ambient term from a particular surface
  488. //-----------------------------------------------------------------------------
  489. static void ComputeAmbientFromSurface( SurfaceHandle_t surfID, dworldlight_t* pSkylight,
  490. Vector& radcolor )
  491. {
  492. if (IS_SURF_VALID( surfID ) )
  493. {
  494. // If we hit the sky, use the sky ambient
  495. if (MSurf_Flags( surfID ) & SURFDRAW_SKY)
  496. {
  497. if (pSkylight)
  498. {
  499. // add in sky ambient
  500. VectorCopy( pSkylight->intensity, radcolor );
  501. }
  502. }
  503. else
  504. {
  505. Vector reflectivity;
  506. MSurf_TexInfo( surfID )->material->GetReflectivity( reflectivity );
  507. VectorMultiply( radcolor, reflectivity, radcolor );
  508. }
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Computes the ambient term from a large number of spherical samples
  513. //-----------------------------------------------------------------------------
  514. static void ComputeAmbientFromSphericalSamples( const Vector& start,
  515. Vector* lightBoxColor )
  516. {
  517. VPROF( "ComputeAmbientFromSphericalSamples" );
  518. // find any ambient lights
  519. dworldlight_t *pSkylight = FindAmbientLight();
  520. Vector radcolor[NUMRANDOMNORMALS];
  521. Assert( cached_r_lightcache_numambientsamples <= ARRAYSIZE( radcolor ) );
  522. // sample world by casting N rays distributed across a sphere
  523. Vector upend;
  524. int i;
  525. for ( i = 0; i < cached_r_lightcache_numambientsamples; i++)
  526. {
  527. // FIXME: a good optimization would be to scale this per leaf
  528. VectorMA( start, COORD_EXTENT * 1.74, g_anorms[i], upend );
  529. // Now that we've got a ray, see what surface we've hit
  530. SurfaceHandle_t surfID = R_LightVec (start, upend, false, radcolor[i] );
  531. if (!IS_SURF_VALID(surfID) )
  532. continue;
  533. ComputeAmbientFromSurface( surfID, pSkylight, radcolor[i] );
  534. }
  535. // accumulate samples into radiant box
  536. const Vector* pBoxDirs = g_pStudioRender->GetAmbientLightDirections();
  537. for (int j = g_pStudioRender->GetNumAmbientLightSamples(); --j >= 0; )
  538. {
  539. float c, t;
  540. t = 0;
  541. lightBoxColor[j][0] = 0;
  542. lightBoxColor[j][1] = 0;
  543. lightBoxColor[j][2] = 0;
  544. for (i = 0; i < cached_r_lightcache_numambientsamples; i++)
  545. {
  546. c = DotProduct( g_anorms[i], pBoxDirs[j] );
  547. if (c > 0)
  548. {
  549. t += c;
  550. VectorMA( lightBoxColor[j], c, radcolor[i], lightBoxColor[j] );
  551. }
  552. }
  553. VectorMultiply( lightBoxColor[j], 1/t, lightBoxColor[j] );
  554. }
  555. }
  556. static void ComputeAmbientFromLeaf( const Vector &start, int leafID, Vector *lightBoxColor, bool *bAddedLeafAmbientCube )
  557. {
  558. if( leafID >= 0 )
  559. {
  560. Mod_LeafAmbientColorAtPos( lightBoxColor, start, leafID );
  561. // The ambient lighting in the leaves has the emit_surface lights factored in.
  562. *bAddedLeafAmbientCube = true;
  563. }
  564. else
  565. {
  566. int i;
  567. for( i = 0; i < 6; i++ )
  568. {
  569. lightBoxColor[i].Init( 0.0f, 0.0f, 0.0f );
  570. }
  571. }
  572. }
  573. //-----------------------------------------------------------------------------
  574. // Computes the ambient term from 6 cardinal directions
  575. //-----------------------------------------------------------------------------
  576. static void ComputeAmbientFromAxisAlignedSamples( const Vector& start,
  577. Vector* lightBoxColor )
  578. {
  579. Vector upend;
  580. // find any ambient lights
  581. dworldlight_t *pSkylight = FindAmbientLight();
  582. // sample world only along cardinal axes
  583. const Vector* pBoxDirs = g_pStudioRender->GetAmbientLightDirections();
  584. for (int i = 0; i < 6; i++)
  585. {
  586. VectorMA( start, COORD_EXTENT * 1.74, pBoxDirs[i], upend );
  587. // Now that we've got a ray, see what surface we've hit
  588. SurfaceHandle_t surfID = R_LightVec (start, upend, false, lightBoxColor[i] );
  589. if (!IS_SURF_VALID( surfID ) )
  590. continue;
  591. ComputeAmbientFromSurface( surfID, pSkylight, lightBoxColor[i] );
  592. }
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Computes the ambient lighting at a point, and sets the lightstyles bitfield
  596. //-----------------------------------------------------------------------------
  597. static void R_StudioGetAmbientLightForPoint(
  598. int leafID,
  599. const Vector& start,
  600. Vector* pLightBoxColor,
  601. bool bIsStaticProp,
  602. bool *bAddedLeafAmbientCube )
  603. {
  604. *bAddedLeafAmbientCube = false;
  605. VPROF( "R_StudioGetAmbientLightForPoint" );
  606. int i;
  607. if ( g_pMaterialSystemConfig->nFullbright == 1 )
  608. {
  609. for (i = g_pStudioRender->GetNumAmbientLightSamples(); --i >= 0; )
  610. {
  611. VectorFill( pLightBoxColor[i], 1.0 );
  612. }
  613. return;
  614. }
  615. switch( r_radiosity.GetInt() )
  616. {
  617. case 1:
  618. ComputeAmbientFromAxisAlignedSamples( start, pLightBoxColor );
  619. break;
  620. case 2:
  621. ComputeAmbientFromSphericalSamples( start, pLightBoxColor );
  622. break;
  623. case 3:
  624. if (bIsStaticProp)
  625. ComputeAmbientFromSphericalSamples( start, pLightBoxColor );
  626. else
  627. ComputeAmbientFromAxisAlignedSamples( start, pLightBoxColor );
  628. break;
  629. case 4:
  630. if (bIsStaticProp)
  631. ComputeAmbientFromSphericalSamples( start, pLightBoxColor );
  632. else
  633. ComputeAmbientFromLeaf( start, leafID, pLightBoxColor, bAddedLeafAmbientCube );
  634. break;
  635. default:
  636. // assume no bounced light from the world
  637. for (i = g_pStudioRender->GetNumAmbientLightSamples(); --i >= 0; )
  638. {
  639. VectorFill( pLightBoxColor[i], 0 );
  640. }
  641. }
  642. }
  643. //-----------------------------------------------------------------------------
  644. // This filter bumps against the world + all but one prop
  645. //-----------------------------------------------------------------------------
  646. class CTraceFilterWorldAndProps : public ITraceFilter
  647. {
  648. public:
  649. CTraceFilterWorldAndProps( IHandleEntity *pHandleEntity ) : m_pIgnoreProp( pHandleEntity ) {}
  650. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  651. {
  652. // We only bump against props + we ignore one particular prop
  653. if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  654. return false;
  655. return ( pHandleEntity != m_pIgnoreProp );
  656. }
  657. virtual TraceType_t GetTraceType() const
  658. {
  659. return TRACE_EVERYTHING_FILTER_PROPS;
  660. }
  661. private:
  662. IHandleEntity *m_pIgnoreProp;
  663. };
  664. static float LightIntensityAndDirectionAtPointOld( dworldlight_t* pLight,
  665. const Vector& mid, int fFlags, IHandleEntity *pIgnoreEnt, Vector *pDirection )
  666. {
  667. CTraceFilterWorldOnly worldTraceFilter;
  668. CTraceFilterWorldAndProps propTraceFilter( pIgnoreEnt );
  669. ITraceFilter *pTraceFilter = &worldTraceFilter;
  670. if (fFlags & LIGHT_OCCLUDE_VS_PROPS)
  671. {
  672. pTraceFilter = &propTraceFilter;
  673. }
  674. // Special case lights
  675. switch (pLight->type)
  676. {
  677. case emit_skylight:
  678. {
  679. // There can be more than one skylight, but we should only
  680. // ever be affected by one of them (multiple ones are created from
  681. // a single light in vrad)
  682. VectorFill( *pDirection, 0 );
  683. // check to see if you can hit the sky texture
  684. Vector end;
  685. VectorMA( mid, -COORD_EXTENT * 1.74f, pLight->normal, end ); // max_range * sqrt(3)
  686. trace_t tr;
  687. Ray_t ray;
  688. ray.Init( mid, end );
  689. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &tr );
  690. // Here, we didn't hit the sky, so we must be in shadow
  691. if ( !(tr.surface.flags & SURF_SKY) )
  692. return 0.0f;
  693. // fudge delta and dist for skylights
  694. *pDirection = -pLight->normal;
  695. return 1.0f;
  696. }
  697. case emit_skyambient:
  698. // always ignore these
  699. return 0.0f;
  700. }
  701. // all other lights
  702. // check distance
  703. VectorSubtract( pLight->origin, mid, *pDirection );
  704. float ratio = Engine_WorldLightDistanceFalloff( pLight, *pDirection, (fFlags & LIGHT_NO_RADIUS_CHECK) != 0 );
  705. // Add in light style component
  706. if( !( fFlags & LIGHT_IGNORE_LIGHTSTYLE_VALUE ) )
  707. {
  708. ratio *= LightStyleValue( pLight->style );
  709. }
  710. // Early out for really low-intensity lights
  711. // That way we don't need to ray-cast or normalize
  712. float intensity = max( pLight->intensity[0], pLight->intensity[1] );
  713. intensity = max(intensity, pLight->intensity[2] );
  714. // This is about 1/256
  715. // See the comment titled "EMIT_SURFACE LIGHTS" at the top for info about why we don't
  716. // test emit_surface lights here.
  717. if ( pLight->type != emit_surface )
  718. {
  719. if (intensity * ratio < r_worldlightmin.GetFloat() )
  720. return 0.0f;
  721. }
  722. float dist = VectorNormalize( *pDirection );
  723. if ( fFlags & LIGHT_NO_OCCLUSION_CHECK )
  724. return ratio;
  725. trace_t pm;
  726. Ray_t ray;
  727. ray.Init( mid, pLight->origin );
  728. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &pm );
  729. // hack
  730. if ( (1.f-pm.fraction) * dist > 8 )
  731. {
  732. #ifndef SWDS
  733. if (r_drawlightcache.GetInt() == 2)
  734. {
  735. CDebugOverlay::AddLineOverlay( mid, pm.endpos, 255, 0, 0, 255, true, 3 );
  736. }
  737. #endif
  738. return 0.f;
  739. }
  740. return ratio;
  741. }
  742. //-----------------------------------------------------------------------------
  743. // This method returns the effective intensity of a light as seen from
  744. // a particular point. PVS is used to speed up the task.
  745. //-----------------------------------------------------------------------------
  746. static float LightIntensityAndDirectionAtPointNew( dworldlight_t* pLight, lightzbuffer_t *pZBuf,
  747. const Vector& mid, int fFlags, IHandleEntity *pIgnoreEnt, Vector *pDirection )
  748. {
  749. CTraceFilterWorldOnly worldTraceFilter;
  750. CTraceFilterWorldAndProps propTraceFilter( pIgnoreEnt );
  751. ITraceFilter *pTraceFilter = &worldTraceFilter;
  752. if (fFlags & LIGHT_OCCLUDE_VS_PROPS)
  753. {
  754. pTraceFilter = &propTraceFilter;
  755. }
  756. // Special case lights
  757. switch (pLight->type)
  758. {
  759. case emit_skylight:
  760. {
  761. // There can be more than one skylight, but we should only
  762. // ever be affected by one of them (multiple ones are created from
  763. // a single light in vrad)
  764. VectorFill( *pDirection, 0 );
  765. // check to see if you can hit the sky texture
  766. Vector end;
  767. VectorMA( mid, -COORD_EXTENT * 1.74f, pLight->normal, end ); // max_range * sqrt(3)
  768. trace_t tr;
  769. Ray_t ray;
  770. ray.Init( mid, end );
  771. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &tr );
  772. // Here, we didn't hit the sky, so we must be in shadow
  773. if ( !(tr.surface.flags & SURF_SKY) )
  774. return 0.0f;
  775. // fudge delta and dist for skylights
  776. *pDirection = -pLight->normal;
  777. return 1.0f;
  778. }
  779. case emit_skyambient:
  780. // always ignore these
  781. return 0.0f;
  782. }
  783. // all other lights
  784. // check distance
  785. VectorSubtract( pLight->origin, mid, *pDirection );
  786. float ratio = Engine_WorldLightDistanceFalloff( pLight, *pDirection, (fFlags & LIGHT_NO_RADIUS_CHECK) != 0 );
  787. // Add in light style component
  788. if( !( fFlags & LIGHT_IGNORE_LIGHTSTYLE_VALUE ) )
  789. {
  790. ratio *= LightStyleValue( pLight->style );
  791. }
  792. // Early out for really low-intensity lights
  793. // That way we don't need to ray-cast or normalize
  794. float intensity = fpmax( pLight->intensity[0], pLight->intensity[1] );
  795. intensity = fpmax(intensity, pLight->intensity[2] );
  796. // This is about 1/256
  797. // See the comment titled "EMIT_SURFACE LIGHTS" at the top for info about why we don't
  798. // test emit_surface lights here.
  799. if ( pLight->type != emit_surface )
  800. {
  801. if (intensity * ratio < r_worldlightmin.GetFloat() )
  802. return 0.0f;
  803. }
  804. float dist = VectorNormalize( *pDirection );
  805. if ( fFlags & LIGHT_NO_OCCLUSION_CHECK )
  806. return ratio;
  807. float flTraceDistance = dist;
  808. // check if we are so close to the light that we shouldn't use our coarse z buf
  809. if ( dist - ( 1 << HASH_GRID_SIZEZ ) < 8 * SHADOW_ZBUF_RES )
  810. pZBuf = NULL;
  811. Vector epnt = mid;
  812. LightShadowZBufferSample_t *pSample = NULL;
  813. if ( pZBuf )
  814. {
  815. pSample = &( pZBuf->GetSample( *pDirection ) );
  816. if ( ( pSample->m_flHitDistance < pSample->m_flTraceDistance ) || ( pSample->m_flTraceDistance >= dist ) )
  817. {
  818. // hit!
  819. if ( dist > pSample->m_flHitDistance + 8 ) // shadow hit
  820. {
  821. #ifndef SWDS
  822. if (r_drawlightcache.GetInt() == 2 )
  823. {
  824. CDebugOverlay::AddLineOverlay( mid, pLight->origin, 0, 0, 0, 255, true, 3 );
  825. }
  826. #endif
  827. return 0;
  828. }
  829. else
  830. {
  831. #ifndef SWDS
  832. if (r_drawlightcache.GetInt() == 2 )
  833. {
  834. CDebugOverlay::AddLineOverlay( mid, pLight->origin, 0, 255, 0, 255, true, 3 );
  835. }
  836. #endif
  837. return ratio;
  838. }
  839. }
  840. // cache miss
  841. flTraceDistance = max( 100.0, 2.0 * dist ); // trace a little further for better caching
  842. epnt += ( dist - flTraceDistance ) * ( *pDirection );
  843. }
  844. trace_t pm;
  845. Ray_t ray;
  846. ray.Init( pLight->origin, epnt ); // trace from light to object
  847. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &pm );
  848. float flHitDistance = ( pm.startsolid ) ? FLT_EPSILON : ( pm.fraction ) * flTraceDistance;
  849. if ( pSample )
  850. {
  851. pSample->m_flTraceDistance = flTraceDistance;
  852. pSample->m_flHitDistance = ( pm.fraction >= 1.0 ) ? 1.0e23 : flHitDistance;
  853. }
  854. if ( dist > flHitDistance + 8)
  855. {
  856. #ifndef SWDS
  857. if (r_drawlightcache.GetInt() == 2 )
  858. {
  859. CDebugOverlay::AddLineOverlay( mid, pLight->origin, 255, 0, 0, 255, true, 3 );
  860. }
  861. #endif
  862. return 0.f;
  863. }
  864. return ratio;
  865. }
  866. static float LightIntensityAndDirectionAtPoint( dworldlight_t* pLight, lightzbuffer_t *pZBuf,
  867. const Vector& mid, int fFlags, IHandleEntity *pIgnoreEnt, Vector *pDirection )
  868. {
  869. if ( pZBuf )
  870. return LightIntensityAndDirectionAtPointNew( pLight, pZBuf, mid, fFlags, pIgnoreEnt, pDirection );
  871. else
  872. return LightIntensityAndDirectionAtPointOld( pLight, mid, fFlags, pIgnoreEnt, pDirection );
  873. #if 0
  874. float old = LightIntensityAndDirectionAtPointOld( pLight, mid, fFlags, pIgnoreEnt, pDirection );
  875. float newf = LightIntensityAndDirectionAtPointNew( pLight, pZBuf, mid, fFlags, pIgnoreEnt, pDirection );
  876. if ( old != newf )
  877. {
  878. float old2 = LightIntensityAndDirectionAtPointOld( pLight, mid, fFlags, pIgnoreEnt, pDirection );
  879. float newf2 = LightIntensityAndDirectionAtPointNew( pLight, pZBuf, mid, fFlags, pIgnoreEnt, pDirection );
  880. }
  881. return newf;
  882. #endif
  883. }
  884. //-----------------------------------------------------------------------------
  885. // This method returns the effective intensity of a light as seen within
  886. // a particular box...
  887. // a particular point. PVS is used to speed up the task.
  888. //-----------------------------------------------------------------------------
  889. static float LightIntensityAndDirectionInBox( dworldlight_t* pLight,
  890. lightzbuffer_t *pZBuf,
  891. const Vector &mid, const Vector &mins, const Vector &maxs, int fFlags,
  892. Vector *pDirection )
  893. {
  894. // Choose the point closest on the box to the light to get max intensity
  895. // within the box....
  896. if ( !r_oldlightselection.GetBool() )
  897. {
  898. switch (pLight->type)
  899. {
  900. case emit_spotlight: // directional & positional
  901. {
  902. float sphereRadius = (maxs-mid).Length();
  903. // first do a sphere/sphere check
  904. float dist = (pLight->origin - mid).Length();
  905. if ( dist > (sphereRadius + pLight->radius) )
  906. return 0;
  907. // PERFORMANCE: precalc this and store in the light?
  908. float angle = acos(pLight->stopdot2);
  909. float sinAngle = sin(angle);
  910. if ( !IsSphereIntersectingCone( mid, sphereRadius, pLight->origin, pLight->normal, sinAngle, pLight->stopdot2 ) )
  911. return 0;
  912. }
  913. // NOTE: fall through to radius check in point case
  914. case emit_point:
  915. {
  916. float distSqr = CalcSqrDistanceToAABB( mins, maxs, pLight->origin );
  917. if ( distSqr > pLight->radius * pLight->radius )
  918. return 0;
  919. }
  920. break;
  921. case emit_surface: // directional & positional, fixed cone size
  922. {
  923. float sphereRadius = (maxs-mid).Length();
  924. // first do a sphere/sphere check
  925. float dist = (pLight->origin - mid).Length();
  926. if ( dist > (sphereRadius + pLight->radius) )
  927. return 0;
  928. // PERFORMANCE: precalc this and store in the light?
  929. if ( !IsSphereIntersectingCone( mid, sphereRadius, pLight->origin, pLight->normal, 1.0f, 0.0f ) )
  930. return 0;
  931. }
  932. break;
  933. }
  934. }
  935. else
  936. {
  937. // NOTE: Here, we do radius check to check to see if we should even care about the light
  938. // because we want to check the closest point in the box
  939. switch (pLight->type)
  940. {
  941. case emit_point:
  942. case emit_spotlight: // directional & positional
  943. {
  944. Vector vecClosestPoint;
  945. vecClosestPoint.Init();
  946. for ( int i = 0; i < 3; ++i )
  947. {
  948. vecClosestPoint[i] = clamp( pLight->origin[i], mins[i], maxs[i] );
  949. }
  950. vecClosestPoint -= pLight->origin;
  951. if ( vecClosestPoint.LengthSqr() > pLight->radius * pLight->radius )
  952. return 0;
  953. }
  954. break;
  955. }
  956. }
  957. return LightIntensityAndDirectionAtPoint( pLight, pZBuf, mid, fFlags | LIGHT_NO_RADIUS_CHECK, NULL, pDirection );
  958. }
  959. //-----------------------------------------------------------------------------
  960. // Computes the static vertex lighting term from a large number of spherical samples
  961. //-----------------------------------------------------------------------------
  962. bool ComputeVertexLightingFromSphericalSamples( const Vector& vecVertex,
  963. const Vector &vecNormal, IHandleEntity *pIgnoreEnt, Vector *pLinearColor )
  964. {
  965. if ( IsX360() )
  966. return false;
  967. // Check to see if this vertex is in solid
  968. trace_t tr;
  969. CTraceFilterWorldAndProps filter( pIgnoreEnt );
  970. Ray_t ray;
  971. ray.Init( vecVertex, vecVertex );
  972. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, &filter, &tr );
  973. if ( tr.startsolid || tr.allsolid )
  974. return false;
  975. pLinearColor->Init( 0, 0, 0 );
  976. // find any ambient lights
  977. dworldlight_t *pSkylight = FindAmbientLight();
  978. // sample world by casting N rays distributed across a sphere
  979. float t = 0.0f;
  980. Vector upend, color;
  981. int i;
  982. for ( i = 0; i < cached_r_lightcache_numambientsamples; i++)
  983. {
  984. float flDot = DotProduct( vecNormal, s_raddir[i] );
  985. if ( flDot < 0.0f )
  986. continue;
  987. // FIXME: a good optimization would be to scale this per leaf
  988. VectorMA( vecVertex, COORD_EXTENT * 1.74, s_raddir[i], upend );
  989. // Now that we've got a ray, see what surface we've hit
  990. SurfaceHandle_t surfID = R_LightVec( vecVertex, upend, false, color );
  991. if ( !IS_SURF_VALID(surfID) )
  992. continue;
  993. // FIXME: Maybe make sure we aren't obstructed by static props?
  994. // To do this, R_LightVec would need to return distance of hit...
  995. // Or, we need another arg to R_LightVec to return black when hitting a static prop
  996. ComputeAmbientFromSurface( surfID, pSkylight, color );
  997. t += flDot;
  998. VectorMA( *pLinearColor, flDot, color, *pLinearColor );
  999. }
  1000. if (t != 0.0f)
  1001. {
  1002. *pLinearColor /= t;
  1003. }
  1004. // Now deal with direct lighting
  1005. bool bHasSkylight = false;
  1006. // Figure out the PVS info for this location
  1007. int leaf = CM_PointLeafnum( vecVertex );
  1008. const byte* pVis = CM_ClusterPVS( CM_LeafCluster( leaf ) );
  1009. // Now add in the direct lighting
  1010. Vector vecDirection;
  1011. for (i = 0; i < host_state.worldbrush->numworldlights; ++i)
  1012. {
  1013. dworldlight_t *wl = &host_state.worldbrush->worldlights[i];
  1014. // FIXME: This is sort of a hack; only one skylight is allowed in the
  1015. // lighting...
  1016. if ((wl->type == emit_skylight) && bHasSkylight)
  1017. continue;
  1018. // only do it if the entity can see into the lights leaf
  1019. if ((wl->cluster < 0) || (!BIT_SET( pVis, wl->cluster )) )
  1020. continue;
  1021. float flRatio = LightIntensityAndDirectionAtPoint( wl, NULL, vecVertex, LIGHT_OCCLUDE_VS_PROPS, pIgnoreEnt, &vecDirection );
  1022. // No light contribution? Get outta here!
  1023. if ( flRatio <= 0.0f )
  1024. continue;
  1025. // Check if we've got a skylight
  1026. if ( wl->type == emit_skylight )
  1027. bHasSkylight = true;
  1028. // Figure out spotlight attenuation
  1029. float flAngularRatio = Engine_WorldLightAngle( wl, wl->normal, vecNormal, vecDirection );
  1030. // Add in the direct lighting
  1031. VectorMAInline( *pLinearColor, flAngularRatio * flRatio, wl->intensity, *pLinearColor );
  1032. }
  1033. return true;
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Finds the minimum light
  1037. //-----------------------------------------------------------------------------
  1038. static int FindDarkestWorldLight( int numLights, float* pLightIllum, float newIllum )
  1039. {
  1040. // FIXME: make the list sorted?
  1041. int minLightIndex = -1;
  1042. float minillum = newIllum;
  1043. for (int j = 0; j < numLights; ++j)
  1044. {
  1045. // only check ones dimmer than have already been checked
  1046. if (pLightIllum[j] < minillum)
  1047. {
  1048. minillum = pLightIllum[j];
  1049. minLightIndex = j;
  1050. }
  1051. }
  1052. return minLightIndex;
  1053. }
  1054. //-----------------------------------------------------------------------------
  1055. // Adds a world light to the ambient cube
  1056. //-----------------------------------------------------------------------------
  1057. static void AddWorldLightToLightCube( dworldlight_t* pWorldLight,
  1058. Vector* pBoxColor,
  1059. const Vector& direction,
  1060. float ratio )
  1061. {
  1062. if (ratio == 0.0f)
  1063. return;
  1064. // add whatever didn't stay in the list to lightBoxColor
  1065. // FIXME: This method is a guess, I don't know how it should be done
  1066. const Vector* pBoxDir = g_pStudioRender->GetAmbientLightDirections();
  1067. for (int j = g_pStudioRender->GetNumAmbientLightSamples(); --j >= 0; )
  1068. {
  1069. float t = DotProduct( pBoxDir[j], direction );
  1070. if (t > 0)
  1071. {
  1072. VectorMAInline( pBoxColor[j], ratio * t, pWorldLight->intensity, pBoxColor[j] );
  1073. }
  1074. }
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Adds a world light to the ambient cube
  1078. //-----------------------------------------------------------------------------
  1079. void AddWorldLightToAmbientCube( dworldlight_t* pWorldLight, const Vector &vecLightingOrigin, AmbientCube_t &ambientCube )
  1080. {
  1081. Vector vecDirection;
  1082. float ratio = LightIntensityAndDirectionAtPoint( pWorldLight, NULL, vecLightingOrigin, 0, NULL, &vecDirection );
  1083. float angularRatio = Engine_WorldLightAngle( pWorldLight, pWorldLight->normal, vecDirection, vecDirection );
  1084. AddWorldLightToLightCube( pWorldLight, ambientCube, vecDirection, ratio * angularRatio );
  1085. }
  1086. static inline const byte* FastRejectLightSource(
  1087. bool bIgnoreVis,
  1088. const byte *pVis,
  1089. const Vector &bucketOrigin,
  1090. int lightType,
  1091. int lightCluster,
  1092. bool &bReject )
  1093. {
  1094. bReject = false;
  1095. if( !bIgnoreVis )
  1096. {
  1097. // This is an optimization to avoid decompressing Vis twice
  1098. if (!pVis)
  1099. {
  1100. // Figure out the PVS info for this location
  1101. int bucketOriginLeaf = CM_PointLeafnum( bucketOrigin );
  1102. pVis = CM_ClusterPVS( CM_LeafCluster( bucketOriginLeaf ) );
  1103. }
  1104. if ( lightType == emit_skylight )
  1105. {
  1106. int bucketOriginLeaf = CM_PointLeafnum( bucketOrigin );
  1107. mleaf_t *pLeaf = &host_state.worldbrush->leafs[bucketOriginLeaf];
  1108. if ( pLeaf && !( pLeaf->flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
  1109. {
  1110. bReject = true;
  1111. }
  1112. }
  1113. else
  1114. {
  1115. if ((lightCluster < 0) || (!BIT_SET( pVis, lightCluster )) )
  1116. bReject = true;
  1117. }
  1118. }
  1119. return pVis;
  1120. }
  1121. //-----------------------------------------------------------------------------
  1122. // Adds a world light to the list of lights
  1123. //-----------------------------------------------------------------------------
  1124. static const byte *AddWorldLightToLightingState( dworldlight_t* pWorldLight,
  1125. lightzbuffer_t* pZBuf,
  1126. LightingState_t& lightingState, LightingStateInfo_t& info,
  1127. const Vector& bucketOrigin, const byte* pVis, bool dynamic = false,
  1128. bool bIgnoreVis = false,
  1129. bool bIgnoreVisTest = false )
  1130. {
  1131. Assert( lightingState.numlights >= 0 && lightingState.numlights <= MAXLOCALLIGHTS );
  1132. // FIXME: This is sort of a hack; only one skylight is allowed in the
  1133. // lighting...
  1134. if ((pWorldLight->type == emit_skylight) && info.m_LightingStateHasSkylight)
  1135. return pVis;
  1136. // only do it if the entity can see into the lights leaf
  1137. if ( !bIgnoreVisTest )
  1138. {
  1139. bool bReject;
  1140. pVis = FastRejectLightSource( bIgnoreVis, pVis, bucketOrigin, pWorldLight->type, pWorldLight->cluster, bReject );
  1141. if ( bReject )
  1142. return pVis;
  1143. }
  1144. // Get the lighting ratio
  1145. Vector direction;
  1146. float ratio;
  1147. if (!dynamic && r_oldlightselection.GetBool())
  1148. {
  1149. ratio = LightIntensityAndDirectionAtPoint( pWorldLight, pZBuf, bucketOrigin, 0, NULL, &direction );
  1150. }
  1151. else
  1152. {
  1153. Vector mins, maxs;
  1154. ComputeLightcacheBounds( bucketOrigin, &mins, &maxs );
  1155. ratio = LightIntensityAndDirectionInBox( pWorldLight, pZBuf, bucketOrigin, mins, maxs, dynamic ? LIGHT_NO_OCCLUSION_CHECK : 0, &direction );
  1156. }
  1157. // No light contribution? Get outta here!
  1158. if ( ratio <= 0.0f )
  1159. return pVis;
  1160. // Check if we've got a skylight
  1161. if (pWorldLight->type == emit_skylight)
  1162. info.m_LightingStateHasSkylight = true;
  1163. // Figure out spotlight attenuation
  1164. float angularRatio = Engine_WorldLightAngle( pWorldLight, pWorldLight->normal, direction, direction );
  1165. // Use standard RGB to gray conversion
  1166. float illum = ratio * DotProduct( pWorldLight->intensity, s_Grayscale ); // Don't multiply by cone angle?
  1167. // It can't be a local light if it's too dark
  1168. // See the comment titled "EMIT_SURFACE LIGHTS" at the top for info.
  1169. if (pWorldLight->type == emit_surface || illum >= r_worldlightmin.GetFloat()) // FIXME: tune this value
  1170. {
  1171. int nWorldLights = min( g_pMaterialSystemHardwareConfig->MaxNumLights(), r_worldlights.GetInt() );
  1172. // if remaining slots, add to list
  1173. if ( lightingState.numlights < nWorldLights )
  1174. {
  1175. // save pointer to world light
  1176. lightingState.locallight[lightingState.numlights] = pWorldLight;
  1177. info.m_pIllum[lightingState.numlights] = illum;
  1178. ++lightingState.numlights;
  1179. return pVis;
  1180. }
  1181. // no remaining slots
  1182. // find the dimmest existing light and replace
  1183. // If dynamic, make sure that it stays as a local light if possible.
  1184. int minLightIndex = FindDarkestWorldLight( lightingState.numlights, info.m_pIllum, dynamic ? 100000 : illum );
  1185. if (minLightIndex != -1)
  1186. {
  1187. // FIXME: We're sorting by ratio here instead of illum cause we either
  1188. // have to store more memory or do more computations; I'm not
  1189. // convinced it's any better of a metric though, so ratios for now...
  1190. // found a light was was dimmer, swap it with the current light
  1191. V_swap( pWorldLight, lightingState.locallight[minLightIndex] );
  1192. V_swap( illum, info.m_pIllum[minLightIndex] );
  1193. // FIXME: Avoid these recomputations
  1194. // But I don't know how to do it without storing a ton of data
  1195. // per cache entry... yuck!
  1196. // NOTE: We know the dot product can't be zero or illum would have been 0 to start with!
  1197. ratio = illum / DotProduct( pWorldLight->intensity, s_Grayscale );
  1198. if (pWorldLight->type == emit_skylight)
  1199. {
  1200. VectorFill( direction, 0 );
  1201. angularRatio = 1.0f;
  1202. }
  1203. else
  1204. {
  1205. VectorSubtract( pWorldLight->origin, bucketOrigin, direction );
  1206. VectorNormalize( direction );
  1207. // Recompute the ratios
  1208. angularRatio = Engine_WorldLightAngle( pWorldLight, pWorldLight->normal, direction, direction );
  1209. }
  1210. }
  1211. }
  1212. // Add the low light to the ambient box color
  1213. AddWorldLightToLightCube( pWorldLight, lightingState.r_boxcolor, direction, ratio * angularRatio );
  1214. return pVis;
  1215. }
  1216. //-----------------------------------------------------------------------------
  1217. // Construct a world light from a dynamic light
  1218. //-----------------------------------------------------------------------------
  1219. static void WorldLightFromDynamicLight( dlight_t const& dynamicLight,
  1220. dworldlight_t& worldLight )
  1221. {
  1222. VectorCopy( dynamicLight.origin, worldLight.origin );
  1223. worldLight.type = emit_point;
  1224. worldLight.intensity[0] = TexLightToLinear( dynamicLight.color.r, dynamicLight.color.exponent );
  1225. worldLight.intensity[1] = TexLightToLinear( dynamicLight.color.g, dynamicLight.color.exponent );
  1226. worldLight.intensity[2] = TexLightToLinear( dynamicLight.color.b, dynamicLight.color.exponent );
  1227. worldLight.style = dynamicLight.style;
  1228. // Compute cluster associated with the dynamic light
  1229. worldLight.cluster = CM_LeafCluster( CM_PointLeafnum(worldLight.origin) );
  1230. // Assume a quadratic attenuation factor; atten so we hit minlight
  1231. // at radius away...
  1232. float minlight = fpmax( dynamicLight.minlight, g_flMinLightingValue );
  1233. // NOTE: Previous implementation turned off attenuation at radius zero.
  1234. // clamping is more continuous
  1235. float radius = dynamicLight.GetRadius();
  1236. if ( radius < 0.1f )
  1237. radius = 0.1f;
  1238. worldLight.constant_attn = 0;
  1239. worldLight.linear_attn = 0;
  1240. worldLight.quadratic_attn = 1.0f / (minlight * radius * radius);
  1241. // Set the max radius
  1242. worldLight.radius = radius;
  1243. // Spotlights...
  1244. if (dynamicLight.m_OuterAngle > 0.0f)
  1245. {
  1246. worldLight.type = emit_spotlight;
  1247. VectorCopy( dynamicLight.m_Direction, worldLight.normal );
  1248. worldLight.stopdot = cos( dynamicLight.m_InnerAngle * M_PI / 180.0f );
  1249. worldLight.stopdot2 = cos( dynamicLight.m_OuterAngle * M_PI / 180.0f );
  1250. }
  1251. }
  1252. //-----------------------------------------------------------------------------
  1253. // Add in dynamic worldlights (lightstyles)
  1254. //-----------------------------------------------------------------------------
  1255. static const byte *ComputeLightStyles( lightcache_t* pCache, LightingState_t& lightingState,
  1256. const Vector& origin, int leaf, const byte* pVis )
  1257. {
  1258. VPROF_INCREMENT_COUNTER( "ComputeLightStyles", 1 );
  1259. LightingStateInfo_t info;
  1260. lightingState.ZeroLightingState();
  1261. // Next, add each world light with a lightstyle into the lighting state,
  1262. // ejecting less relevant local lights + folding them into the ambient cube
  1263. for ( int i = 0; i < host_state.worldbrush->numworldlights; ++i)
  1264. {
  1265. dworldlight_t *wl = &host_state.worldbrush->worldlights[i];
  1266. if (wl->style == 0)
  1267. continue;
  1268. int byte = wl->style >> 3;
  1269. int bit = wl->style & 0x7;
  1270. if( !( pCache->m_pLightstyles[byte] & ( 1 << bit ) ) )
  1271. {
  1272. continue;
  1273. }
  1274. // This is an optimization to avoid decompressing Vis twice
  1275. if (!pVis)
  1276. {
  1277. // Figure out the PVS info for this location
  1278. pVis = CM_ClusterPVS( CM_LeafCluster( leaf ) );
  1279. }
  1280. // Now add that world light into our list of worldlights
  1281. AddWorldLightToLightingState( wl, NULL, lightingState, info, origin, pVis );
  1282. }
  1283. pCache->m_LastFrameUpdated_LightStyles = r_framecount;
  1284. return pVis;
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. // Add in dynamic worldlights (lightstyles)
  1288. //-----------------------------------------------------------------------------
  1289. static void AddLightStylesForStaticProp( PropLightcache_t *pcache, LightingState_t& lightingState )
  1290. {
  1291. // Next, add each world light with a lightstyle into the lighting state,
  1292. // ejecting less relevant local lights + folding them into the ambient cube
  1293. for( int i = 0; i < pcache->m_LightStyleWorldLights.Count(); ++i )
  1294. {
  1295. Assert( pcache->m_LightStyleWorldLights[i] >= 0 );
  1296. Assert( pcache->m_LightStyleWorldLights[i] < host_state.worldbrush->numworldlights );
  1297. dworldlight_t *wl = &host_state.worldbrush->worldlights[pcache->m_LightStyleWorldLights[i]];
  1298. Assert( wl->style != 0 );
  1299. // Now add that world light into our list of worldlights
  1300. AddWorldLightToLightingState( wl, NULL, lightingState, *pcache, pcache->m_LightingOrigin, NULL,
  1301. false /*dynamic*/, true /*ignorevis*/ );
  1302. }
  1303. }
  1304. //-----------------------------------------------------------------------------
  1305. // Add DLights + ELights to the dynamic lighting
  1306. //-----------------------------------------------------------------------------
  1307. static dworldlight_t s_pDynamicLight[MAX_DLIGHTS + MAX_ELIGHTS];
  1308. static const byte* AddDLights( LightingStateInfo_t& info, LightingState_t& lightingState,
  1309. const Vector& origin, int leaf, const byte* pVis )
  1310. {
  1311. if ( !g_bActiveDlights )
  1312. return pVis;
  1313. const bool bIgnoreVis = false;
  1314. const bool bIgnoreVisTest = true;
  1315. // Next, add each world light with a lightstyle into the lighting state,
  1316. // ejecting less relevant local lights + folding them into the ambient cube
  1317. dlight_t* dl = cl_dlights;
  1318. for ( int i=0; i<MAX_DLIGHTS; ++i, ++dl )
  1319. {
  1320. // If the light's not active, then continue
  1321. if ( (r_dlightactive & (1 << i)) == 0 )
  1322. continue;
  1323. // If the light doesn't affect models, then continue
  1324. if (dl->flags & (DLIGHT_NO_MODEL_ILLUMINATION | DLIGHT_DISPLACEMENT_MASK))
  1325. continue;
  1326. // Fast reject. If we can reject it here, then we don't have to call WorldLightFromDynamicLight..
  1327. bool bReject;
  1328. int lightCluster = CM_LeafCluster( g_DLightLeafAccessors[i].GetLeaf( dl->origin ) );
  1329. pVis = FastRejectLightSource( bIgnoreVis, pVis, origin, emit_point, lightCluster, bReject );
  1330. if ( bReject )
  1331. continue;
  1332. // Construct a world light representing the dynamic light
  1333. // we're making a static list here because the lighting state
  1334. // contains a set of pointers to dynamic lights
  1335. WorldLightFromDynamicLight( *dl, s_pDynamicLight[i] );
  1336. // Now add that world light into our list of worldlights
  1337. pVis = AddWorldLightToLightingState( &s_pDynamicLight[i], NULL, lightingState,
  1338. info, origin, pVis, true, bIgnoreVis, bIgnoreVisTest );
  1339. }
  1340. return pVis;
  1341. }
  1342. static const byte* AddELights( LightingStateInfo_t& info, LightingState_t& lightingState,
  1343. const Vector& origin, int leaf, const byte* pVis )
  1344. {
  1345. if ( !g_bActiveElights )
  1346. return pVis;
  1347. const bool bIgnoreVis = false;
  1348. const bool bIgnoreVisTest = true;
  1349. // Next, add each world light with a lightstyle into the lighting state,
  1350. // ejecting less relevant local lights + folding them into the ambient cube
  1351. dlight_t* dl = cl_elights;
  1352. for ( int i=0; i<MAX_ELIGHTS; ++i, ++dl )
  1353. {
  1354. // If the light's not active, then continue
  1355. if ( !dl->IsRadiusGreaterThanZero() )
  1356. continue;
  1357. // If the light doesn't affect models, then continue
  1358. if (dl->flags & (DLIGHT_NO_MODEL_ILLUMINATION | DLIGHT_DISPLACEMENT_MASK))
  1359. continue;
  1360. // Fast reject. If we can reject it here, then we don't have to call WorldLightFromDynamicLight..
  1361. bool bReject;
  1362. int lightCluster = CM_LeafCluster( g_ELightLeafAccessors[i].GetLeaf( dl->origin ) );
  1363. pVis = FastRejectLightSource( bIgnoreVis, pVis, origin, emit_point, lightCluster, bReject );
  1364. if ( bReject )
  1365. continue;
  1366. // Construct a world light representing the dynamic light
  1367. // we're making a static list here because the lighting state
  1368. // contains a set of pointers to dynamic lights
  1369. WorldLightFromDynamicLight( *dl, s_pDynamicLight[i+MAX_DLIGHTS] );
  1370. // Now add that world light into our list of worldlights
  1371. pVis = AddWorldLightToLightingState( &s_pDynamicLight[i+MAX_DLIGHTS], NULL, lightingState,
  1372. info, origin, pVis, true, bIgnoreVis, bIgnoreVisTest );
  1373. }
  1374. return pVis;
  1375. }
  1376. //-----------------------------------------------------------------------------
  1377. // Given static + dynamic lighting, figure out the total light
  1378. //-----------------------------------------------------------------------------
  1379. static const byte *ComputeDynamicLighting( lightcache_t* pCache, LightingState_t& lightingState,
  1380. const Vector& origin, int leaf, const byte* pVis = 0 )
  1381. {
  1382. if (pCache->m_LastFrameUpdated_DynamicLighting != r_framecount)
  1383. {
  1384. VPROF_INCREMENT_COUNTER( "ComputeDynamicLighting", 1 );
  1385. // First factor in the cache into the current lighting state..
  1386. LightingStateInfo_t info;
  1387. pCache->m_DynamicLightingState.ZeroLightingState();
  1388. // Next, add each dlight one at a time
  1389. pVis = AddDLights( info, pCache->m_DynamicLightingState, origin, leaf, pVis );
  1390. // Finally, add in elights
  1391. // FIXME: Do we actually use these?
  1392. pVis = AddELights( info, pCache->m_DynamicLightingState, origin, leaf, pVis );
  1393. pCache->m_LastFrameUpdated_DynamicLighting = r_framecount;
  1394. }
  1395. Assert( pCache->m_DynamicLightingState.numlights >= 0 && pCache->m_DynamicLightingState.numlights <= MAXLOCALLIGHTS );
  1396. memcpy( &lightingState, &pCache->m_DynamicLightingState, sizeof(LightingState_t) );
  1397. return pVis;
  1398. }
  1399. //-----------------------------------------------------------------------------
  1400. // Adds a world light to the list of lights
  1401. //-----------------------------------------------------------------------------
  1402. static void AddWorldLightToLightingStateForStaticProps( dworldlight_t* pWorldLight,
  1403. LightingState_t& lightingState, LightingStateInfo_t& info, PropLightcache_t *pCache,
  1404. bool dynamic = false )
  1405. {
  1406. // FIXME: This is sort of a hack; only one skylight is allowed in the
  1407. // lighting...
  1408. if ((pWorldLight->type == emit_skylight) && info.m_LightingStateHasSkylight)
  1409. return;
  1410. // Get the lighting ratio
  1411. float ratio;
  1412. Vector direction;
  1413. if (!dynamic)
  1414. {
  1415. ratio = LightIntensityAndDirectionAtPoint( pWorldLight, NULL, pCache->m_LightingOrigin, 0, NULL, &direction );
  1416. }
  1417. else
  1418. {
  1419. Vector mins, maxs;
  1420. ComputeLightcacheBounds( pCache->m_LightingOrigin, &mins, &maxs );
  1421. ratio = LightIntensityAndDirectionInBox( pWorldLight, NULL, pCache->m_LightingOrigin,
  1422. pCache->mins, pCache->maxs, LIGHT_NO_OCCLUSION_CHECK, &direction );
  1423. }
  1424. // No light contribution? Get outta here!
  1425. if ( ratio <= 0.0f )
  1426. return;
  1427. // Check if we've got a skylight
  1428. if (pWorldLight->type == emit_skylight)
  1429. info.m_LightingStateHasSkylight = true;
  1430. // Figure out spotlight attenuation
  1431. float angularRatio = Engine_WorldLightAngle( pWorldLight, pWorldLight->normal, direction, direction );
  1432. // Use standard RGB to gray conversion
  1433. float illum = ratio * DotProduct( pWorldLight->intensity, s_Grayscale ); // Don't multiply by cone angle?
  1434. // It can't be a local light if it's too dark
  1435. // See the comment titled "EMIT_SURFACE LIGHTS" at the top for info.
  1436. if (pWorldLight->type == emit_surface || illum >= r_worldlightmin.GetFloat()) // FIXME: tune this value
  1437. {
  1438. int nWorldLights = min( g_pMaterialSystemHardwareConfig->MaxNumLights(), r_worldlights.GetInt() );
  1439. // if remaining slots, add to list
  1440. if ( lightingState.numlights < nWorldLights )
  1441. {
  1442. // save pointer to world light
  1443. lightingState.locallight[lightingState.numlights] = pWorldLight;
  1444. info.m_pIllum[lightingState.numlights] = illum;
  1445. ++lightingState.numlights;
  1446. return;
  1447. }
  1448. // no remaining slots
  1449. // find the dimmest existing light and replace
  1450. int minLightIndex = FindDarkestWorldLight( lightingState.numlights, info.m_pIllum, dynamic ? 100000 : illum );
  1451. if (minLightIndex != -1)
  1452. {
  1453. // FIXME: We're sorting by ratio here instead of illum cause we either
  1454. // have to store more memory or do more computations; I'm not
  1455. // convinced it's any better of a metric though, so ratios for now...
  1456. // found a light was was dimmer, swap it with the current light
  1457. V_swap( pWorldLight, lightingState.locallight[minLightIndex] );
  1458. V_swap( illum, info.m_pIllum[minLightIndex] );
  1459. // FIXME: Avoid these recomputations
  1460. // But I don't know how to do it without storing a ton of data
  1461. // per cache entry... yuck!
  1462. // NOTE: We know the dot product can't be zero or illum would have been 0 to start with!
  1463. ratio = illum / DotProduct( pWorldLight->intensity, s_Grayscale );
  1464. if (pWorldLight->type == emit_skylight)
  1465. {
  1466. VectorFill( direction, 0 );
  1467. angularRatio = 1.0f;
  1468. }
  1469. else
  1470. {
  1471. VectorSubtract( pWorldLight->origin, pCache->m_LightingOrigin, direction );
  1472. VectorNormalize( direction );
  1473. // Recompute the ratios
  1474. angularRatio = Engine_WorldLightAngle( pWorldLight, pWorldLight->normal, direction, direction );
  1475. }
  1476. }
  1477. }
  1478. // Add the low light to the ambient box color
  1479. AddWorldLightToLightCube( pWorldLight, lightingState.r_boxcolor, direction, ratio * angularRatio );
  1480. }
  1481. static void AddDLightsForStaticProps( LightingStateInfo_t& info, LightingState_t& lightingState,
  1482. PropLightcache_t *pCache )
  1483. {
  1484. // mask off any dlights that have gone inactive
  1485. pCache->m_DLightActive &= r_dlightactive;
  1486. if ( pCache->m_DLightMarkFrame != r_framecount )
  1487. {
  1488. pCache->m_DLightActive = 0;
  1489. }
  1490. if ( !pCache->m_DLightActive )
  1491. return;
  1492. // Iterate the relevant dlights and add them to the lighting state
  1493. dlight_t *dl = cl_dlights;
  1494. for ( int i=0; i<MAX_DLIGHTS; ++i, ++dl )
  1495. {
  1496. // If the light doesn't affect this model, then continue.
  1497. if( !( pCache->m_DLightActive & ( 1 << i ) ) )
  1498. continue;
  1499. // Construct a world light representing the dynamic light
  1500. // we're making a static list here because the lighting state
  1501. // contains a set of pointers to dynamic lights
  1502. WorldLightFromDynamicLight( *dl, s_pDynamicLight[i] );
  1503. // Now add that world light into our list of worldlights
  1504. AddWorldLightToLightingStateForStaticProps( &s_pDynamicLight[i], lightingState,
  1505. info, pCache, true );
  1506. }
  1507. }
  1508. //-----------------------------------------------------------------------------
  1509. // Add static lighting to the lighting state
  1510. //-----------------------------------------------------------------------------
  1511. ConVar r_lightcache_zbuffercache( "r_lightcache_zbuffercache", "0", FCVAR_ALLOWED_IN_COMPETITIVE );
  1512. static void AddStaticLighting(
  1513. CBaseLightCache* pCache,
  1514. const Vector& origin,
  1515. const byte* pVis,
  1516. bool bStaticProp,
  1517. bool bAddedLeafAmbientCube )
  1518. {
  1519. VPROF( "AddStaticLighting" );
  1520. // First, blat out the lighting state
  1521. int i;
  1522. pCache->m_StaticLightingState.numlights = 0;
  1523. pCache->m_LightingStateHasSkylight = false;
  1524. // NOTE: for static props, we mark lightstyles elsewhere (BuildStaticLightingCacheLightStyleInfo)
  1525. if( !bStaticProp )
  1526. {
  1527. pCache->m_LightingFlags &= ~( HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE |
  1528. HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE );
  1529. memset( pCache->m_pLightstyles, 0, sizeof( pCache->m_pLightstyles ) );
  1530. }
  1531. // Next, add each static light one at a time into the lighting state,
  1532. // ejecting less relevant local lights + folding them into the ambient cube
  1533. // Also, we need to add *all* new lights into the total box color
  1534. for (i = 0; i < host_state.worldbrush->numworldlights; ++i)
  1535. {
  1536. dworldlight_t *wl = &host_state.worldbrush->worldlights[i];
  1537. lightzbuffer_t *pZBuf;
  1538. if ( r_lightcache_zbuffercache.GetInt() )
  1539. pZBuf = &host_state.worldbrush->shadowzbuffers[i];
  1540. else
  1541. pZBuf = NULL;
  1542. // See the comment titled "EMIT_SURFACE LIGHTS" at the top for info.
  1543. if ( bAddedLeafAmbientCube && (wl->flags & DWL_FLAGS_INAMBIENTCUBE) )
  1544. {
  1545. Assert( wl->type == emit_surface );
  1546. continue;
  1547. }
  1548. // Don't add lights without lightstyles... we cache static lighting + lightstyles separately from static lighting
  1549. if (wl->style == 0)
  1550. {
  1551. // Now add that world light into our list of worldlights
  1552. AddWorldLightToLightingState( wl, pZBuf, pCache->m_StaticLightingState, *pCache, origin, pVis,
  1553. false, false );
  1554. }
  1555. else
  1556. {
  1557. // This is a lighstyle (flickering or switchable light)
  1558. // NOTE: for static props, we mark lightstyles elsewhere. (BuildStaticLightingCacheLightStyleInfo)
  1559. if( !bStaticProp )
  1560. {
  1561. int byte = wl->style >> 3;
  1562. int bit = wl->style & 0x7;
  1563. if( !( pCache->m_pLightstyles[byte] & ( 1 << bit ) ) )
  1564. {
  1565. Vector mins, maxs;
  1566. Vector dummyDirection;
  1567. ComputeLightcacheBounds( origin, &mins, &maxs );
  1568. float ratio = LightIntensityAndDirectionInBox( wl, NULL, origin, mins, maxs,
  1569. LIGHT_NO_OCCLUSION_CHECK | LIGHT_IGNORE_LIGHTSTYLE_VALUE, &dummyDirection );
  1570. // See if this light has any contribution on this cache entry.
  1571. if( ratio > 0.0f )
  1572. {
  1573. if( d_lightstylenumframes[wl->style] <= 1 )
  1574. {
  1575. pCache->m_LightingFlags |= HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE;
  1576. }
  1577. else
  1578. {
  1579. pCache->m_LightingFlags |= HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE;
  1580. }
  1581. pCache->m_pLightstyles[byte] |= (1 << bit);
  1582. }
  1583. }
  1584. }
  1585. }
  1586. }
  1587. }
  1588. //-----------------------------------------------------------------------------
  1589. // Checks to see if the lightstyles are valid for this cache entry
  1590. //-----------------------------------------------------------------------------
  1591. static bool IsCachedLightStylesValid( CBaseLightCache* pCache )
  1592. {
  1593. if (!pCache->HasLightStyle())
  1594. return true;
  1595. // FIXME: Can this start at 1, or is 0 required?
  1596. for (int i = 1; i < MAX_LIGHTSTYLES; ++i)
  1597. {
  1598. int byte = i >> 3;
  1599. int bit = i & 0x7;
  1600. if (pCache->m_pLightstyles[byte] & ( 1 << bit ))
  1601. {
  1602. if (d_lightstyleframe[i] > pCache->m_LastFrameUpdated_LightStyles)
  1603. return false;
  1604. }
  1605. }
  1606. return true;
  1607. }
  1608. //-----------------------------------------------------------------------------
  1609. // Find a lightcache entry within the requested radius from a point
  1610. //-----------------------------------------------------------------------------
  1611. #if 0
  1612. static int FindRecentCacheEntryWithinRadius( int count, CacheInfo_t* pCache, const Vector& origin, float radius )
  1613. {
  1614. radius *= radius;
  1615. // Try to find something within the radius of an existing new sample
  1616. int minIndex = -1;
  1617. for (int i = 0; i < count; ++i)
  1618. {
  1619. Vector delta;
  1620. ComputeLightcacheOrigin( pCache[i].x, pCache[i].y, pCache[i].z, delta );
  1621. delta -= origin;
  1622. float distSq = delta.LengthSqr();
  1623. if (distSq < radius )
  1624. {
  1625. minIndex = i;
  1626. radius = distSq;
  1627. }
  1628. }
  1629. return minIndex;
  1630. }
  1631. #endif
  1632. //-----------------------------------------------------------------------------
  1633. // Draw the lightcache box for debugging
  1634. //-----------------------------------------------------------------------------
  1635. static void DebugRenderLightcache( Vector &sampleOrigin, LightingState_t& lightingState, bool bDebugModel )
  1636. {
  1637. #ifndef SWDS
  1638. // draw the cache entry defined by the sampling origin
  1639. Vector cacheOrigin, cacheMins, cacheMaxs, lightMins, lightMaxs;
  1640. ComputeLightcacheBounds( sampleOrigin, &cacheMins, &cacheMaxs );
  1641. cacheOrigin = ( cacheMins + cacheMaxs ) * 0.5f;
  1642. cacheMins -= cacheOrigin;
  1643. cacheMaxs -= cacheOrigin;
  1644. // For drawing irradiance light probes as shown in [Greger98]
  1645. if( r_drawlightcache.GetInt() == 5 )
  1646. {
  1647. if ( bDebugModel )
  1648. {
  1649. CDebugOverlay::AddSphereOverlay( sampleOrigin, 2.5f, 32, 32, 255, 255, 255, 255, 0.0f ); // 8 inch solid white sphere
  1650. for ( int j = 0; j < lightingState.numlights; ++j )
  1651. {
  1652. Vector vLightPosition;
  1653. int r, g, b;
  1654. if ( lightingState.locallight[j]->type == emit_skylight )
  1655. {
  1656. vLightPosition = sampleOrigin - lightingState.locallight[j]->normal * 10000.0f;
  1657. r = 255;
  1658. g = 50;
  1659. b = 50;
  1660. }
  1661. else
  1662. {
  1663. vLightPosition = lightingState.locallight[j]->origin;
  1664. r = 255;
  1665. g = 255;
  1666. b = 255;
  1667. }
  1668. CDebugOverlay::AddLineOverlay( sampleOrigin, vLightPosition, r, g, b, 255, true, 0.0f );
  1669. }
  1670. }
  1671. }
  1672. else
  1673. {
  1674. // draw cache entry
  1675. CDebugOverlay::AddBoxOverlay( cacheOrigin, cacheMins, cacheMaxs, vec3_angle, 255, 255, 255, 0, 0.0f );
  1676. // draw boxes at light ray terminals to visualize endpoints
  1677. if ( lightingState.numlights > 0 )
  1678. {
  1679. lightMins.Init( -2, -2, -2 );
  1680. lightMaxs.Init( 2, 2, 2);
  1681. CDebugOverlay::AddBoxOverlay( sampleOrigin, lightMins, lightMaxs, vec3_angle, 100, 255, 100, 0, 0.0f );
  1682. }
  1683. int nLineColor[4] = {255, 170, 85, 0};
  1684. for (int j = 0; j < lightingState.numlights; ++j)
  1685. {
  1686. Vector vLightPosition;
  1687. int r, g, b;
  1688. if ( lightingState.locallight[j]->type == emit_skylight )
  1689. {
  1690. vLightPosition = sampleOrigin - lightingState.locallight[j]->normal * 10000.0f;
  1691. r = 255;
  1692. g = 50;
  1693. b = 50;
  1694. }
  1695. else
  1696. {
  1697. vLightPosition = lightingState.locallight[j]->origin;
  1698. r = g = b = nLineColor[j];
  1699. }
  1700. // draw lines from sampling point to light
  1701. CDebugOverlay::AddLineOverlay( sampleOrigin, vLightPosition, r, g, b, 255, true, 0.0f );
  1702. CDebugOverlay::AddBoxOverlay( lightingState.locallight[j]->origin, lightMins, lightMaxs, vec3_angle, 255, 255, 100, 0, 0.0f );
  1703. }
  1704. }
  1705. #endif
  1706. }
  1707. //-----------------------------------------------------------------------------
  1708. // Identify lighting errors
  1709. //-----------------------------------------------------------------------------
  1710. bool IdentifyLightingErrors( int leaf, LightingState_t& lightingState )
  1711. {
  1712. if (r_drawlightcache.GetInt() == 3)
  1713. {
  1714. if (CM_LeafContents(leaf) == CONTENTS_SOLID)
  1715. {
  1716. // Try another choice...
  1717. lightingState.r_boxcolor[0].Init( 1, 0, 0 );
  1718. lightingState.r_boxcolor[1].Init( 1, 0, 0 );
  1719. lightingState.r_boxcolor[2].Init( 1, 0, 0 );
  1720. lightingState.r_boxcolor[3].Init( 1, 0, 0 );
  1721. lightingState.r_boxcolor[4].Init( 1, 0, 0 );
  1722. lightingState.r_boxcolor[5].Init( 1, 0, 0 );
  1723. lightingState.numlights = 0;
  1724. return true;
  1725. }
  1726. }
  1727. return false;
  1728. }
  1729. //-----------------------------------------------------------------------------
  1730. // Compute the cache...
  1731. //-----------------------------------------------------------------------------
  1732. static const byte* ComputeStaticLightingForCacheEntry( CBaseLightCache *pcache, const Vector& origin, int leaf, bool bStaticProp = false )
  1733. {
  1734. VPROF_INCREMENT_COUNTER( "ComputeStaticLightingForCacheEntry", 1 );
  1735. VPROF( "ComputeStaticLightingForCacheEntry" );
  1736. // Figure out the PVS info for this location
  1737. const byte* pVis = CM_ClusterPVS( CM_LeafCluster( leaf ) );
  1738. bool bAddedLeafAmbientCube;
  1739. R_StudioGetAmbientLightForPoint(
  1740. leaf,
  1741. origin,
  1742. pcache->m_StaticLightingState.r_boxcolor,
  1743. bStaticProp,
  1744. &bAddedLeafAmbientCube );
  1745. // get direct lighting from world light sources (point lights, etc.)
  1746. if ( !r_ambientlightingonly.GetInt() )
  1747. {
  1748. AddStaticLighting( pcache, origin, pVis, bStaticProp, bAddedLeafAmbientCube );
  1749. }
  1750. return pVis;
  1751. }
  1752. static void BuildStaticLightingCacheLightStyleInfo( PropLightcache_t* pcache, const Vector& mins, const Vector& maxs )
  1753. {
  1754. const byte *pVis = NULL;
  1755. Assert( pcache->m_LightStyleWorldLights.Count() == 0 );
  1756. pcache->m_LightingFlags &= ~( HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE | HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE );
  1757. // clear lightstyles
  1758. memset( pcache->m_pLightstyles, 0, MAX_LIGHTSTYLE_BYTES );
  1759. for ( short i = 0; i < host_state.worldbrush->numworldlights; ++i)
  1760. {
  1761. dworldlight_t *wl = &host_state.worldbrush->worldlights[i];
  1762. if (wl->style == 0)
  1763. continue;
  1764. // This is an optimization to avoid decompressing Vis twice
  1765. if (!pVis)
  1766. {
  1767. // Figure out the PVS info for this static prop
  1768. pVis = CM_ClusterPVS( CM_LeafCluster( pcache->leaf ) );
  1769. }
  1770. // FIXME: Could do better here if we had access to the list of leaves that this
  1771. // static prop is in. For now, we use the lighting origin.
  1772. if( pVis[ wl->cluster >> 3 ] & ( 1 << ( wl->cluster & 7 ) ) )
  1773. {
  1774. // Use the maximum illumination to cull out lights that are far away.
  1775. dworldlight_t tmpLight = *wl;
  1776. tmpLight.style = 0;
  1777. Vector dummyDirection;
  1778. float ratio = LightIntensityAndDirectionInBox( &tmpLight, NULL, pcache->m_LightingOrigin, mins, maxs,
  1779. LIGHT_NO_OCCLUSION_CHECK | LIGHT_IGNORE_LIGHTSTYLE_VALUE, &dummyDirection );
  1780. // See if this light has any contribution on this cache entry.
  1781. if( ratio <= 0.0f )
  1782. {
  1783. continue;
  1784. }
  1785. {
  1786. MEM_ALLOC_CREDIT();
  1787. pcache->m_LightStyleWorldLights.AddToTail( i );
  1788. }
  1789. int byte = wl->style >> 3;
  1790. int bit = wl->style & 0x7;
  1791. pcache->m_pLightstyles[byte] |= ( 1 << bit );
  1792. if( d_lightstylenumframes[wl->style] <= 1 )
  1793. {
  1794. pcache->m_LightingFlags |= HACKLIGHTCACHEFLAGS_HASSWITCHABLELIGHTSTYLE;
  1795. }
  1796. else
  1797. {
  1798. pcache->m_LightingFlags |= HACKLIGHTCACHEFLAGS_HASNONSWITCHABLELIGHTSTYLE;
  1799. }
  1800. }
  1801. }
  1802. }
  1803. static ITexture *FindEnvCubemapForPoint( const Vector& origin )
  1804. {
  1805. worldbrushdata_t *pBrushData = host_state.worldbrush;
  1806. if( pBrushData && pBrushData->m_nCubemapSamples > 0 )
  1807. {
  1808. int smallestIndex = 0;
  1809. Vector blah = origin - pBrushData->m_pCubemapSamples[0].origin;
  1810. float smallestDist = DotProduct( blah, blah );
  1811. for( int i = 1; i < pBrushData->m_nCubemapSamples; i++ )
  1812. {
  1813. Vector ign = origin - pBrushData->m_pCubemapSamples[i].origin;
  1814. float dist = DotProduct( ign, ign );
  1815. if( dist < smallestDist )
  1816. {
  1817. smallestDist = dist;
  1818. smallestIndex = i;
  1819. }
  1820. }
  1821. return pBrushData->m_pCubemapSamples[smallestIndex].pTexture;
  1822. }
  1823. else
  1824. {
  1825. return NULL;
  1826. }
  1827. }
  1828. //-----------------------------------------------------------------------------
  1829. // Create static light cache entry
  1830. //-----------------------------------------------------------------------------
  1831. LightCacheHandle_t CreateStaticLightingCache( const Vector& origin, const Vector& mins, const Vector& maxs )
  1832. {
  1833. PropLightcache_t* pcache = s_PropCache.Alloc();
  1834. pcache->m_LightingOrigin = origin;
  1835. pcache->m_Flags = 0;
  1836. pcache->mins = mins;
  1837. pcache->maxs = maxs;
  1838. // initialize this to point to our current origin
  1839. pcache->leaf = CM_PointLeafnum(origin);
  1840. // Add the prop to the list of props
  1841. pcache->m_pNextPropLightcache = s_pAllStaticProps;
  1842. s_pAllStaticProps = pcache;
  1843. pcache->m_Flags = 0; // must set this to zero so that this cache entry will be invalid.
  1844. pcache->m_pEnvCubemapTexture = FindEnvCubemapForPoint( origin );
  1845. BuildStaticLightingCacheLightStyleInfo( pcache, mins, maxs );
  1846. return (LightCacheHandle_t)pcache;
  1847. }
  1848. bool StaticLightCacheAffectedByDynamicLight( LightCacheHandle_t handle )
  1849. {
  1850. PropLightcache_t *pcache = ( PropLightcache_t *)handle;
  1851. return pcache->HasDlights();
  1852. }
  1853. bool StaticLightCacheAffectedByAnimatedLightStyle( LightCacheHandle_t handle )
  1854. {
  1855. PropLightcache_t *pcache = ( PropLightcache_t *)handle;
  1856. if( !pcache->HasLightStyle() )
  1857. {
  1858. return false;
  1859. }
  1860. else
  1861. {
  1862. for( int i = 0; i < pcache->m_LightStyleWorldLights.Count(); ++i )
  1863. {
  1864. Assert( pcache->m_LightStyleWorldLights[i] >= 0 );
  1865. Assert( pcache->m_LightStyleWorldLights[i] < host_state.worldbrush->numworldlights );
  1866. dworldlight_t *wl = &host_state.worldbrush->worldlights[pcache->m_LightStyleWorldLights[i]];
  1867. Assert( wl->style != 0 );
  1868. if( d_lightstylenumframes[wl->style] > 1 )
  1869. {
  1870. return true;
  1871. }
  1872. }
  1873. return false;
  1874. }
  1875. }
  1876. bool StaticLightCacheNeedsSwitchableLightUpdate( LightCacheHandle_t handle )
  1877. {
  1878. PropLightcache_t *pcache = ( PropLightcache_t *)handle;
  1879. if( !pcache->HasSwitchableLightStyle() )
  1880. {
  1881. return false;
  1882. }
  1883. else
  1884. {
  1885. for( int i = 0; i < pcache->m_LightStyleWorldLights.Count(); ++i )
  1886. {
  1887. Assert( pcache->m_LightStyleWorldLights[i] >= 0 );
  1888. Assert( pcache->m_LightStyleWorldLights[i] < host_state.worldbrush->numworldlights );
  1889. dworldlight_t *wl = &host_state.worldbrush->worldlights[pcache->m_LightStyleWorldLights[i]];
  1890. Assert( wl->style != 0 );
  1891. // Is it a switchable light?
  1892. if( d_lightstylenumframes[wl->style] <= 1 )
  1893. {
  1894. // Has it changed since the last time we updated our cached static VB version?
  1895. if( pcache->m_SwitchableLightFrame < d_lightstyleframe[wl->style] )
  1896. {
  1897. pcache->m_SwitchableLightFrame = r_framecount;
  1898. // return true since our static vb is dirty
  1899. return true;
  1900. }
  1901. }
  1902. }
  1903. return false;
  1904. }
  1905. }
  1906. //-----------------------------------------------------------------------------
  1907. // Clears the prop lighting cache
  1908. //-----------------------------------------------------------------------------
  1909. void ClearStaticLightingCache()
  1910. {
  1911. s_PropCache.Clear();
  1912. s_pAllStaticProps = NULL;
  1913. }
  1914. //-----------------------------------------------------------------------------
  1915. // Recomputes all static prop lighting
  1916. //-----------------------------------------------------------------------------
  1917. void InvalidateStaticLightingCache(void)
  1918. {
  1919. for ( PropLightcache_t *pCur=s_pAllStaticProps; pCur; pCur=pCur->m_pNextPropLightcache )
  1920. {
  1921. // Compute the static lighting
  1922. pCur->m_Flags = 0;
  1923. pCur->m_LightingFlags &=~HACKLIGHTCACHEFLAGS_HASDONESTATICLIGHTING;
  1924. LightcacheGetStatic( ( LightCacheHandle_t )pCur, NULL, LIGHTCACHEFLAGS_STATIC );
  1925. }
  1926. }
  1927. //-----------------------------------------------------------------------------
  1928. // Gets the lightcache entry for a static prop
  1929. //-----------------------------------------------------------------------------
  1930. LightingState_t *LightcacheGetStatic( LightCacheHandle_t cache, ITexture **pEnvCubemapTexture, unsigned int flags )
  1931. {
  1932. PropLightcache_t *pcache = ( PropLightcache_t * )cache;
  1933. Assert( pcache );
  1934. // get the cubemap texture
  1935. if ( pEnvCubemapTexture )
  1936. {
  1937. *pEnvCubemapTexture = pcache->m_pEnvCubemapTexture;
  1938. }
  1939. bool bRecalcStaticLighting = false;
  1940. bool bRecalcLightStyles = (pcache->HasLightStyle() && pcache->m_LastFrameUpdated_LightStyles != r_framecount) && !IsCachedLightStylesValid(pcache);
  1941. bool bRecalcDLights = pcache->HasDlights() && pcache->m_LastFrameUpdated_DynamicLighting != r_framecount;
  1942. if ( flags != pcache->m_Flags )
  1943. {
  1944. // This should not happen often, but if the flags change, blow away all of the lighting state.
  1945. // This cache entry's state must be regenerated.
  1946. bRecalcStaticLighting = true;
  1947. bRecalcLightStyles = true;
  1948. bRecalcDLights = true;
  1949. pcache->m_Flags = flags;
  1950. }
  1951. else if ( !bRecalcDLights && !bRecalcLightStyles )
  1952. {
  1953. // already have expected lighting state
  1954. // get out of here since we already did this this frame.
  1955. return &pcache->m_DynamicLightingState;
  1956. }
  1957. else
  1958. {
  1959. // the dlight cache includes lightstyles
  1960. // we have to recalc the dlight cache if lightstyles change.
  1961. if ( bRecalcLightStyles )
  1962. {
  1963. bRecalcDLights = true;
  1964. }
  1965. }
  1966. // must need to recalc, do so
  1967. LightingState_t accumulatedState;
  1968. // static lighting state gets preserved because its expensive to generate
  1969. // it gets re-requested for static props that rebake
  1970. if ( flags & LIGHTCACHEFLAGS_STATIC )
  1971. {
  1972. // want static lighting data
  1973. if ( bRecalcStaticLighting && !(pcache->m_LightingFlags & HACKLIGHTCACHEFLAGS_HASDONESTATICLIGHTING) )
  1974. {
  1975. ComputeStaticLightingForCacheEntry( pcache, pcache->m_LightingOrigin, pcache->leaf, true );
  1976. pcache->m_LightingFlags |= HACKLIGHTCACHEFLAGS_HASDONESTATICLIGHTING;
  1977. }
  1978. // set as start values for accumulation
  1979. accumulatedState = pcache->m_StaticLightingState;
  1980. }
  1981. else
  1982. {
  1983. // set as zero for accumulation
  1984. accumulatedState.ZeroLightingState();
  1985. }
  1986. // lightstyle lighting state gets preserved when there is no lightstyle change
  1987. if ( flags & LIGHTCACHEFLAGS_LIGHTSTYLE )
  1988. {
  1989. if ( bRecalcLightStyles )
  1990. {
  1991. // accumulate lightstyles
  1992. AddLightStylesForStaticProp( pcache, accumulatedState );
  1993. pcache->m_LightStyleLightingState = accumulatedState;
  1994. pcache->m_LastFrameUpdated_LightStyles = r_framecount;
  1995. }
  1996. else
  1997. {
  1998. accumulatedState = pcache->m_LightStyleLightingState;
  1999. }
  2000. }
  2001. if ( flags & LIGHTCACHEFLAGS_DYNAMIC )
  2002. {
  2003. if ( bRecalcDLights )
  2004. {
  2005. // accumulate dynamic lights
  2006. AddDLightsForStaticProps( *( LightingStateInfo_t *)pcache, accumulatedState, pcache );
  2007. pcache->m_DynamicLightingState = accumulatedState;
  2008. pcache->m_LastFrameUpdated_DynamicLighting = r_framecount;
  2009. }
  2010. else
  2011. {
  2012. accumulatedState = pcache->m_DynamicLightingState;
  2013. }
  2014. }
  2015. else
  2016. {
  2017. // hold the current state
  2018. pcache->m_DynamicLightingState = accumulatedState;
  2019. }
  2020. // caller gets requested data
  2021. return &pcache->m_DynamicLightingState;
  2022. }
  2023. inline const byte *AddLightingState(
  2024. LightingState_t &dst,
  2025. const LightingState_t &src,
  2026. LightingStateInfo_t &info,
  2027. const Vector& bucketOrigin,
  2028. const byte *pVis,
  2029. bool bDynamic,
  2030. bool bIgnoreVis )
  2031. {
  2032. int i;
  2033. for( i = 0; i < src.numlights; i++ )
  2034. {
  2035. pVis = AddWorldLightToLightingState( src.locallight[i], NULL, dst, info, bucketOrigin, pVis,
  2036. bDynamic, bIgnoreVis );
  2037. }
  2038. for( i = 0; i < 6; i++ )
  2039. {
  2040. dst.r_boxcolor[i] += src.r_boxcolor[i];
  2041. }
  2042. return pVis;
  2043. }
  2044. //-----------------------------------------------------------------------------
  2045. // Get or create the lighting information for this point
  2046. // This is the version for dynamic objects.
  2047. //-----------------------------------------------------------------------------
  2048. const byte* PrecalcLightingState( lightcache_t *pCache, const byte *pVis )
  2049. {
  2050. LightingState_t lightingState;
  2051. lightingState.ZeroLightingState();
  2052. pCache->m_StaticPrecalc_LightingStateInfo.Clear();
  2053. int i;
  2054. for( i = 0; i < pCache->m_StaticLightingState.numlights; i++ )
  2055. {
  2056. pVis = AddWorldLightToLightingState(
  2057. pCache->m_StaticLightingState.locallight[i],
  2058. NULL,
  2059. lightingState,
  2060. pCache->m_StaticPrecalc_LightingStateInfo,
  2061. pCache->m_LightingOrigin,
  2062. pVis,
  2063. true, // bDynamic
  2064. false // bIgnoreVis
  2065. );
  2066. }
  2067. for ( i=0; i < 6; i++ )
  2068. pCache->m_StaticLightingState.r_boxcolor[i] += lightingState.r_boxcolor[i];
  2069. pCache->m_StaticPrecalc_NumLocalLights = lightingState.numlights;
  2070. for ( i=0; i < pCache->m_StaticPrecalc_NumLocalLights; i++ )
  2071. pCache->m_StaticPrecalc_LocalLight[i] = lightingState.locallight[i];
  2072. return pVis;
  2073. }
  2074. void CopyPrecalcedLightingState( lightcache_t *pCache, LightingState_t &lightingState, LightingStateInfo_t &info )
  2075. {
  2076. info = pCache->m_StaticPrecalc_LightingStateInfo;
  2077. int i;
  2078. for ( i=0; i < 6; i++ )
  2079. lightingState.r_boxcolor[i] = pCache->m_StaticLightingState.r_boxcolor[i];
  2080. lightingState.numlights = pCache->m_StaticPrecalc_NumLocalLights;
  2081. for ( i=0; i < lightingState.numlights; i++ )
  2082. lightingState.locallight[i] = pCache->m_StaticPrecalc_LocalLight[i];
  2083. }
  2084. void AdjustLightCacheOrigin( lightcache_t *pCache, const Vector &origin, int originLeaf )
  2085. {
  2086. Vector cacheMins;
  2087. Vector cacheMaxs;
  2088. Vector center;
  2089. trace_t tr;
  2090. Ray_t ray;
  2091. CTraceFilterWorldOnly worldTraceFilter;
  2092. ITraceFilter *pTraceFilter = &worldTraceFilter;
  2093. // quiet compiler
  2094. tr.startsolid = false;
  2095. tr.fraction = 0;
  2096. // prefer to use the center of the light cache for all sampling
  2097. // which helps consistent stable cache entries
  2098. ComputeLightcacheBounds( origin, &cacheMins, &cacheMaxs );
  2099. center = cacheMins + cacheMaxs;
  2100. center *= 0.5f;
  2101. bool bTraceToCenter = true;
  2102. int centerLeaf = CM_PointLeafnum(center);
  2103. if (centerLeaf != originLeaf)
  2104. {
  2105. // preferred center resides in a different leaf
  2106. if (CM_LeafContents(centerLeaf) & MASK_OPAQUE)
  2107. {
  2108. // preferred center is invalid
  2109. bTraceToCenter = false;
  2110. }
  2111. else
  2112. {
  2113. // ensure the desired center resides in the leaf that the provided origin is in
  2114. CM_SnapPointToReferenceLeaf(origin, LIGHTCACHE_SNAP_EPSILON, &center);
  2115. }
  2116. }
  2117. if (bTraceToCenter)
  2118. {
  2119. // if the center is unavailable, fallback to provided origin
  2120. ray.Init( origin, center );
  2121. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &tr );
  2122. }
  2123. if (bTraceToCenter && tr.startsolid)
  2124. {
  2125. // origin is in solid, can't trace anywhere, use bad origin as provided
  2126. VectorCopy(origin, pCache->m_LightingOrigin);
  2127. }
  2128. else if (!bTraceToCenter || tr.fraction < 1)
  2129. {
  2130. // center is occluded
  2131. // trace again, recompute alternate x-y center, substitute z
  2132. // ensure the desired center resides in the leaf that the provided origin is in
  2133. center.x = (cacheMins.x + cacheMaxs.x) * 0.5f;
  2134. center.y = (cacheMins.y + cacheMaxs.y) * 0.5f;
  2135. center.z = origin.z;
  2136. CM_SnapPointToReferenceLeaf(origin, LIGHTCACHE_SNAP_EPSILON, &center);
  2137. ray.Init( origin, center );
  2138. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, pTraceFilter, &tr );
  2139. if (tr.fraction < 1)
  2140. {
  2141. // no further fallback, use origin as provided
  2142. VectorCopy(origin, pCache->m_LightingOrigin);
  2143. }
  2144. else
  2145. {
  2146. // trace succeeded
  2147. VectorCopy(center, pCache->m_LightingOrigin);
  2148. }
  2149. }
  2150. else
  2151. {
  2152. // trace succeeded
  2153. VectorCopy(center, pCache->m_LightingOrigin);
  2154. }
  2155. }
  2156. bool AllowFullCacheMiss(int flags)
  2157. {
  2158. if ( r_framecount < 60 || r_framecount != g_FrameIndex )
  2159. {
  2160. g_FrameMissCount = 0;
  2161. g_FrameIndex = r_framecount;
  2162. }
  2163. if ( g_FrameMissCount < lightcache_maxmiss.GetInt() )
  2164. {
  2165. g_FrameMissCount++;
  2166. return true;
  2167. }
  2168. if ( flags & LIGHTCACHEFLAGS_ALLOWFAST )
  2169. return false;
  2170. return true;
  2171. }
  2172. lightcache_t *FindNearestCache( int x, int y, int z, int leafIndex )
  2173. {
  2174. int bestDist = INT_MAX;
  2175. lightcache_t *pBest = NULL;
  2176. short current = GetLightLRUTail().lru_prev;
  2177. int dx, dy, dz;
  2178. while ( current != LIGHT_LRU_HEAD_INDEX )
  2179. {
  2180. lightcache_t *pCache = &lightcache[current];
  2181. int dist = 0;
  2182. dx = pCache->x - x;
  2183. dx = abs(dx);
  2184. dy = pCache->y - y;
  2185. dy = abs(dy);
  2186. dz = pCache->z - z;
  2187. dz = abs(dz);
  2188. if ( leafIndex != pCache->leaf )
  2189. {
  2190. dist += 2;
  2191. }
  2192. dist = max(dist, dx);
  2193. dist = max(dist, dy);
  2194. dist = max(dist, dz);
  2195. if ( dist < bestDist )
  2196. {
  2197. pBest = pCache;
  2198. bestDist = dist;
  2199. if ( dist <= 1 )
  2200. break;
  2201. }
  2202. current = pCache->lru_prev;
  2203. }
  2204. return pBest;
  2205. }
  2206. ITexture *LightcacheGetDynamic( const Vector& origin, LightingState_t& lightingState,
  2207. LightcacheGetDynamic_Stats &stats, unsigned int flags, bool bDebugModel )
  2208. {
  2209. VPROF_BUDGET( "LightcacheGet", VPROF_BUDGETGROUP_LIGHTCACHE );
  2210. LightingStateInfo_t info;
  2211. // generate the hashing vars
  2212. int originLeaf = CM_PointLeafnum(origin);
  2213. /*
  2214. if (IdentifyLightingErrors(leaf, lightingState))
  2215. return false;
  2216. */
  2217. int x, y, z;
  2218. OriginToCacheOrigin( origin, x, y, z );
  2219. // convert vars to hash key / bucket id
  2220. int bucket = LightcacheHashKey( x, y, z, originLeaf );
  2221. const byte* pVis = NULL;
  2222. bool bComputeLightStyles = ( flags & LIGHTCACHEFLAGS_LIGHTSTYLE ) != 0;
  2223. // See if we've already computed the light in this location
  2224. lightcache_t *pCache = FindInCache(bucket, x, y, z, originLeaf);
  2225. if ( pCache )
  2226. {
  2227. // cache hit, move to tail of LRU
  2228. LightcacheMark( pCache );
  2229. if ( bComputeLightStyles && IsCachedLightStylesValid( pCache ) )
  2230. {
  2231. bComputeLightStyles = false;
  2232. }
  2233. }
  2234. else if ( !AllowFullCacheMiss(flags) )
  2235. {
  2236. pCache = FindNearestCache( x, y, z, originLeaf );
  2237. originLeaf = pCache->leaf;
  2238. x = pCache->x;
  2239. y = pCache->y;
  2240. z = pCache->z;
  2241. }
  2242. if ( !pCache )
  2243. {
  2244. VPROF_INCREMENT_COUNTER( "lightcache miss", 1 );
  2245. // cache miss, nothing appropriate from the frame cache, make a new entry
  2246. pCache = NewLightcacheEntry(bucket);
  2247. // initialize the cache entry based on provided origin
  2248. pCache->x = x;
  2249. pCache->y = y;
  2250. pCache->z = z;
  2251. pCache->leaf = originLeaf;
  2252. if ( r_lightcachecenter.GetBool() )
  2253. {
  2254. AdjustLightCacheOrigin( pCache, origin, originLeaf );
  2255. }
  2256. else
  2257. {
  2258. // old behavior, use provided origin
  2259. VectorCopy(origin, pCache->m_LightingOrigin);
  2260. }
  2261. // Figure out which env_cubemap is used for this cache entry.
  2262. pCache->m_pEnvCubemapTexture = FindEnvCubemapForPoint( pCache->m_LightingOrigin );
  2263. // Compute the static portion of the cache
  2264. pVis = ComputeStaticLightingForCacheEntry( pCache, pCache->m_LightingOrigin, originLeaf );
  2265. pVis = PrecalcLightingState( pCache, pVis );
  2266. }
  2267. // NOTE: On a cache miss, this has to be after ComputeStaticLightingForCacheEntry since these flags are computed there.
  2268. stats.m_bHasNonSwitchableLightStyles = pCache->HasNonSwitchableLightStyle();
  2269. stats.m_bHasSwitchableLightStyles = pCache->HasSwitchableLightStyle();
  2270. if ( bComputeLightStyles )
  2271. {
  2272. pVis = ComputeLightStyles( pCache, pCache->m_LightStyleLightingState, pCache->m_LightingOrigin, originLeaf, pVis );
  2273. stats.m_bNeedsSwitchableLightStyleUpdate = true;
  2274. }
  2275. else
  2276. {
  2277. stats.m_bNeedsSwitchableLightStyleUpdate = false;
  2278. }
  2279. stats.m_bHasDLights = false;
  2280. if ( flags & LIGHTCACHEFLAGS_DYNAMIC )
  2281. {
  2282. pVis = ComputeDynamicLighting( pCache, pCache->m_DynamicLightingState, pCache->m_LightingOrigin, originLeaf, pVis );
  2283. if( pCache->m_DynamicLightingState.numlights > 0 )
  2284. {
  2285. stats.m_bHasDLights = true;
  2286. }
  2287. }
  2288. if ( flags & LIGHTCACHEFLAGS_STATIC )
  2289. {
  2290. CopyPrecalcedLightingState( pCache, lightingState, info );
  2291. }
  2292. else
  2293. {
  2294. lightingState.ZeroLightingState();
  2295. }
  2296. if ( flags & LIGHTCACHEFLAGS_LIGHTSTYLE )
  2297. {
  2298. pVis = AddLightingState( lightingState, pCache->m_LightStyleLightingState, info, pCache->m_LightingOrigin, pVis,
  2299. true /*bDynamic*/, false /*bIgnoreVis*/ );
  2300. }
  2301. if ( flags & LIGHTCACHEFLAGS_DYNAMIC )
  2302. {
  2303. pVis = AddLightingState( lightingState, pCache->m_DynamicLightingState, info, pCache->m_LightingOrigin, pVis,
  2304. true /*bDynamic*/, false /*bIgnoreVis*/ );
  2305. }
  2306. if ( r_drawlightcache.GetBool() )
  2307. {
  2308. DebugRenderLightcache( pCache->m_LightingOrigin, lightingState, bDebugModel );
  2309. }
  2310. return pCache->m_pEnvCubemapTexture;
  2311. }
  2312. //-----------------------------------------------------------------------------
  2313. // Compute the contribution of D- and E- lights at a point + normal
  2314. //-----------------------------------------------------------------------------
  2315. void ComputeDynamicLighting( const Vector& pt, const Vector* pNormal, Vector& color )
  2316. {
  2317. if ( !g_bActiveDlights && !g_bActiveElights )
  2318. {
  2319. VectorFill( color, 0 );
  2320. return;
  2321. }
  2322. // Next, add each world light with a lightstyle into the lighting state,
  2323. // ejecting less relevant local lights + folding them into the ambient cube
  2324. static Vector ambientTerm[6] =
  2325. {
  2326. Vector(0,0,0),
  2327. Vector(0,0,0),
  2328. Vector(0,0,0),
  2329. Vector(0,0,0),
  2330. Vector(0,0,0),
  2331. Vector(0,0,0)
  2332. };
  2333. int lightCount = 0;
  2334. LightDesc_t pLightDesc[MAX_DLIGHTS + MAX_ELIGHTS];
  2335. int i;
  2336. dlight_t* dl = cl_dlights;
  2337. if ( g_bActiveDlights )
  2338. {
  2339. for ( i=0; i<MAX_DLIGHTS; ++i, ++dl )
  2340. {
  2341. // If the light's not active, then continue
  2342. if ( (r_dlightactive & (1 << i)) == 0 )
  2343. continue;
  2344. // If the light doesn't affect models, then continue
  2345. if (dl->flags & (DLIGHT_NO_MODEL_ILLUMINATION | DLIGHT_DISPLACEMENT_MASK))
  2346. continue;
  2347. // Construct a world light representing the dynamic light
  2348. // we're making a static list here because the lighting state
  2349. // contains a set of pointers to dynamic lights
  2350. dworldlight_t worldLight;
  2351. WorldLightFromDynamicLight( *dl, worldLight );
  2352. WorldLightToMaterialLight( &worldLight, pLightDesc[lightCount] );
  2353. ++lightCount;
  2354. }
  2355. }
  2356. if ( g_bActiveElights )
  2357. {
  2358. // Next, add each world light with a lightstyle into the lighting state,
  2359. // ejecting less relevant local lights + folding them into the ambient cube
  2360. dl = cl_elights;
  2361. for ( i=0; i<MAX_ELIGHTS; ++i, ++dl )
  2362. {
  2363. // If the light's not active, then continue
  2364. if ( !dl->IsRadiusGreaterThanZero() )
  2365. continue;
  2366. // If the light doesn't affect models, then continue
  2367. if (dl->flags & (DLIGHT_NO_MODEL_ILLUMINATION | DLIGHT_DISPLACEMENT_MASK))
  2368. continue;
  2369. // Construct a world light representing the dynamic light
  2370. // we're making a static list here because the lighting state
  2371. // contains a set of pointers to dynamic lights
  2372. dworldlight_t worldLight;
  2373. WorldLightFromDynamicLight( *dl, worldLight );
  2374. WorldLightToMaterialLight( &worldLight, pLightDesc[lightCount] );
  2375. ++lightCount;
  2376. }
  2377. }
  2378. if ( lightCount )
  2379. {
  2380. g_pStudioRender->ComputeLighting( ambientTerm, lightCount,
  2381. pLightDesc, pt, *pNormal, color );
  2382. }
  2383. else
  2384. {
  2385. VectorFill( color, 0 );
  2386. }
  2387. }
  2388. //-----------------------------------------------------------------------------
  2389. // Is Dynamic Light?
  2390. //-----------------------------------------------------------------------------
  2391. static bool IsDynamicLight( dworldlight_t *pWorldLight )
  2392. {
  2393. // NOTE: This only works because we're using some implementation details
  2394. // that the dynamic lights are stored in a little static array
  2395. return ( pWorldLight >= s_pDynamicLight && pWorldLight < &s_pDynamicLight[ARRAYSIZE(s_pDynamicLight)] );
  2396. }
  2397. //-----------------------------------------------------------------------------
  2398. // Computes an average color (of sorts) at a particular point + optional normal
  2399. //-----------------------------------------------------------------------------
  2400. void ComputeLighting( const Vector& pt, const Vector* pNormal, bool bClamp, Vector& color, Vector *pBoxColors )
  2401. {
  2402. LightingState_t lightingState;
  2403. LightcacheGetDynamic_Stats stats;
  2404. LightcacheGetDynamic( pt, lightingState, stats, LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST );
  2405. int i;
  2406. if ( pNormal )
  2407. {
  2408. LightDesc_t* pLightDesc = (LightDesc_t*)stackalloc( lightingState.numlights * sizeof(LightDesc_t) );
  2409. for ( i=0; i < lightingState.numlights; ++i )
  2410. {
  2411. // Construct a world light representing the dynamic light
  2412. // we're making a static list here because the lighting state
  2413. // contains a set of pointers to dynamic lights
  2414. WorldLightToMaterialLight( lightingState.locallight[i], pLightDesc[i] );
  2415. }
  2416. g_pStudioRender->ComputeLighting( lightingState.r_boxcolor, lightingState.numlights, pLightDesc, pt, *pNormal, color );
  2417. }
  2418. else
  2419. {
  2420. Vector direction;
  2421. for ( i = 0; i < lightingState.numlights; ++i )
  2422. {
  2423. if ( IsDynamicLight( lightingState.locallight[i] ) )
  2424. continue;
  2425. float ratio = LightIntensityAndDirectionAtPoint( lightingState.locallight[i], NULL, pt, LIGHT_NO_OCCLUSION_CHECK, NULL, &direction );
  2426. float angularRatio = Engine_WorldLightAngle( lightingState.locallight[i], lightingState.locallight[i]->normal, direction, direction );
  2427. AddWorldLightToLightCube( lightingState.locallight[i], lightingState.r_boxcolor, direction, ratio * angularRatio );
  2428. }
  2429. color.Init( 0, 0, 0 );
  2430. for ( i = 0; i < 6; ++i )
  2431. {
  2432. color += lightingState.r_boxcolor[i];
  2433. }
  2434. color /= 6.0f;
  2435. }
  2436. // If they want the colors for each box side, give it to them.
  2437. if ( pBoxColors )
  2438. {
  2439. memcpy( pBoxColors, lightingState.r_boxcolor, sizeof( lightingState.r_boxcolor ) );
  2440. }
  2441. if (bClamp)
  2442. {
  2443. if (color.x > 1.0f)
  2444. color.x = 1.0f;
  2445. if (color.y > 1.0f)
  2446. color.y = 1.0f;
  2447. if (color.z > 1.0f)
  2448. color.z = 1.0f;
  2449. }
  2450. }
  2451. static const byte *s_pDLightVis = NULL;
  2452. // All dlights that affect a static prop must mark that static prop every frame.
  2453. class MarkStaticPropLightsEmumerator : public IPartitionEnumerator
  2454. {
  2455. public:
  2456. void SetLightID( int nLightID )
  2457. {
  2458. m_nLightID = nLightID;
  2459. }
  2460. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  2461. {
  2462. Assert( StaticPropMgr()->IsStaticProp( pHandleEntity ) );
  2463. PropLightcache_t *pCache =
  2464. ( PropLightcache_t * )StaticPropMgr()->GetLightCacheHandleForStaticProp( pHandleEntity );
  2465. if ( !pCache )
  2466. {
  2467. return ITERATION_CONTINUE;
  2468. }
  2469. if( !s_pDLightVis )
  2470. {
  2471. s_pDLightVis = CM_ClusterPVS( CM_LeafCluster( CM_PointLeafnum( cl_dlights[m_nLightID].origin ) ) );
  2472. }
  2473. if( !StaticPropMgr()->IsPropInPVS( pHandleEntity, s_pDLightVis ) )
  2474. {
  2475. return ITERATION_CONTINUE;
  2476. }
  2477. if ( !pCache )
  2478. {
  2479. return ITERATION_CONTINUE;
  2480. }
  2481. #ifdef _DEBUG
  2482. if( r_drawlightcache.GetInt() == 4 )
  2483. {
  2484. Vector mins( -5, -5, -5 );
  2485. Vector maxs( 5, 5, 5 );
  2486. CDebugOverlay::AddLineOverlay( cl_dlights[m_nLightID].origin, pCache->m_LightingOrigin, 0, 0, 255, 255, true, 0.001f );
  2487. CDebugOverlay::AddBoxOverlay( pCache->m_LightingOrigin, mins, maxs, vec3_angle, 255, 0, 0, 0, 0.001f );
  2488. }
  2489. #endif
  2490. pCache->m_DLightActive |= ( 1 << m_nLightID );
  2491. pCache->m_DLightMarkFrame = r_framecount;
  2492. return ITERATION_CONTINUE;
  2493. }
  2494. private:
  2495. int m_nLightID;
  2496. };
  2497. static MarkStaticPropLightsEmumerator s_MarkStaticPropLightsEnumerator;
  2498. void MarkDLightsOnStaticProps( void )
  2499. {
  2500. if ( !g_bActiveDlights )
  2501. return;
  2502. dlight_t *l = cl_dlights;
  2503. for (int i=0 ; i<MAX_DLIGHTS ; i++, l++)
  2504. {
  2505. if (l->flags & (DLIGHT_NO_MODEL_ILLUMINATION | DLIGHT_DISPLACEMENT_MASK))
  2506. continue;
  2507. if (l->die < cl.GetTime() || !l->IsRadiusGreaterThanZero() )
  2508. continue;
  2509. // If the light's not active, then continue
  2510. if ( (r_dlightactive & (1 << i)) == 0 )
  2511. continue;
  2512. #ifdef _DEBUG
  2513. if( r_drawlightcache.GetInt() == 4 )
  2514. {
  2515. Vector mins( -5, -5, -5 );
  2516. Vector maxs( 5, 5, 5 );
  2517. CDebugOverlay::AddBoxOverlay( l->origin, mins, maxs, vec3_angle, 255, 255, 255, 0, 0.001f );
  2518. }
  2519. #endif
  2520. s_pDLightVis = NULL;
  2521. s_MarkStaticPropLightsEnumerator.SetLightID( i );
  2522. SpatialPartition()->EnumerateElementsInSphere( PARTITION_ENGINE_STATIC_PROPS,
  2523. l->origin, l->GetRadius(), true, &s_MarkStaticPropLightsEnumerator );
  2524. }
  2525. }
  2526. float g_flMinLightingValue = 1.0f;
  2527. void InitDLightGlobals( int nMapVersion )
  2528. {
  2529. if( nMapVersion >= 20 )
  2530. {
  2531. // The light level at which we are close enough to black to treat as black for
  2532. // culling purposes.
  2533. g_flMinLightingValue = 1.0f / 256.0f;
  2534. }
  2535. else
  2536. {
  2537. // This is the broken value from HL2. It is supposed to be
  2538. // the light level at which we are close enough to black to treat as black for
  2539. // culling purposes. We leave it at the broken value here for old bsp files
  2540. // Since HL2 maps were compiled with this bsp version.
  2541. g_flMinLightingValue = 20.0f / 256.0f;
  2542. }
  2543. }