Counter Strike : Global Offensive Source Code
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.

3128 lines
97 KiB

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