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.

837 lines
26 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "render_pch.h"
  9. #include "client.h"
  10. #include "bitmap/imageformat.h"
  11. #include "bitmap/tgawriter.h"
  12. #include <float.h>
  13. #include "collisionutils.h"
  14. #include "cl_main.h"
  15. #include "tier0/vprof.h"
  16. #include "debugoverlay.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. //-----------------------------------------------------------------------------
  20. // Forward declarations
  21. //-----------------------------------------------------------------------------
  22. extern ConVar r_avglight;
  23. extern int r_surfacevisframe;
  24. extern ConVar r_unloadlightmaps;
  25. extern ConVar r_keepstyledlightmapsonly;
  26. extern bool g_bHunkAllocLightmaps;
  27. static model_t* s_pLightVecModel = 0;
  28. ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT );
  29. ConVar r_visualizelighttraces( "r_visualizelighttraces", "0", FCVAR_CHEAT );
  30. ConVar r_visualizelighttracesshowfulltrace( "r_visualizelighttracesshowfulltrace", "0", FCVAR_CHEAT );
  31. //-----------------------------------------------------------------------------
  32. // State associated with R_LightVec
  33. //-----------------------------------------------------------------------------
  34. struct LightVecState_t
  35. {
  36. LightVecState_t()
  37. {
  38. }
  39. Ray_t m_Ray;
  40. float m_HitFrac;
  41. float* m_pTextureS;
  42. float* m_pTextureT;
  43. float* m_pLightmapS;
  44. float* m_pLightmapT;
  45. SurfaceHandle_t m_nSkySurfID;
  46. bool m_bUseLightStyles;
  47. CUtlVector<IDispInfo *> m_LightTestDisps;
  48. };
  49. //-----------------------------------------------------------------------------
  50. // Globals associated with dynamic lighting
  51. //-----------------------------------------------------------------------------
  52. int r_dlightchanged;
  53. int r_dlightactive;
  54. //-----------------------------------------------------------------------------
  55. // Displacements to test against for R_LightVec
  56. //-----------------------------------------------------------------------------
  57. /*
  58. ==================
  59. R_AnimateLight
  60. ==================
  61. */
  62. void R_AnimateLight (void)
  63. {
  64. INetworkStringTable *table = GetBaseLocalClient().m_pLightStyleTable;
  65. if ( !table )
  66. return;
  67. // light animations
  68. // 'm' is normal light, 'a' is no light, 'z' is double bright
  69. int i = (int)(GetBaseLocalClient().GetTime()*10);
  70. for (int j=0 ; j<MAX_LIGHTSTYLES ; j++)
  71. {
  72. int length;
  73. const char * lightstyle = (const char*) table->GetStringUserData( j, &length );
  74. length--;
  75. if (!lightstyle || !lightstyle[0])
  76. {
  77. d_lightstylevalue[j] = 256;
  78. d_lightstylenumframes[j] = 0;
  79. continue;
  80. }
  81. d_lightstylenumframes[j] = length;
  82. int k = i % length;
  83. k = lightstyle[k] - 'a';
  84. k = k*22;
  85. if (d_lightstylevalue[j] != k)
  86. {
  87. d_lightstylevalue[j] = k;
  88. d_lightstyleframe[j] = r_framecount;
  89. }
  90. }
  91. }
  92. /*
  93. =============================================================================
  94. DYNAMIC LIGHTS
  95. =============================================================================
  96. */
  97. // Returns true if the surface has the specified dlight already set on it for this frame.
  98. inline bool R_IsDLightAlreadyMarked( msurfacelighting_t *pLighting, int bit )
  99. {
  100. return (pLighting->m_nDLightFrame == r_framecount) && (pLighting->m_fDLightBits & bit);
  101. }
  102. // Mark the surface as changed by the specified dlight (so its texture gets updated when
  103. // it comes time to render).
  104. inline void R_MarkSurfaceDLight( SurfaceHandle_t surfID, msurfacelighting_t *pLighting, int bit)
  105. {
  106. pLighting->m_nDLightFrame = r_framecount;
  107. pLighting->m_fDLightBits |= bit;
  108. MSurf_Flags( surfID ) |= SURFDRAW_HASDLIGHT;
  109. }
  110. int R_TryLightMarkSurface( dlight_t *light, msurfacelighting_t *pLighting, SurfaceHandle_t surfID, int bit )
  111. {
  112. // Make sure this light actually intersects the surface cache of the surfaces it hits
  113. mtexinfo_t *tex;
  114. // FIXME: No worky for brush models
  115. // Find the perpendicular distance to the surface we're lighting
  116. // NOTE: Allow some stuff that's slightly behind it because view models can get behind walls
  117. // FIXME: We should figure out a better way to deal with view models
  118. float perpDistSq = DotProduct (light->origin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist;
  119. if (perpDistSq < DLIGHT_BEHIND_PLANE_DIST)
  120. return 0;
  121. perpDistSq *= perpDistSq;
  122. float flInPlaneRadiusSq = light->GetRadiusSquared() - perpDistSq;
  123. if (flInPlaneRadiusSq <= 0)
  124. return 0;
  125. tex = MSurf_TexInfo( surfID );
  126. Vector2D mins, maxs;
  127. mins.Init( pLighting->m_LightmapMins[0], pLighting->m_LightmapMins[1] );
  128. maxs.Init( mins.x + pLighting->m_LightmapExtents[0], mins.y + pLighting->m_LightmapExtents[1] );
  129. // Project light center into texture coordinates
  130. Vector2D vecCircleCenter;
  131. vecCircleCenter.x = DotProduct (light->origin, tex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  132. tex->lightmapVecsLuxelsPerWorldUnits[0][3];
  133. vecCircleCenter.y = DotProduct (light->origin, tex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  134. tex->lightmapVecsLuxelsPerWorldUnits[1][3];
  135. // convert from world space to luxel space and convert to int
  136. float flInPlaneLuxelRadius = sqrtf( flInPlaneRadiusSq * tex->luxelsPerWorldUnit * tex->luxelsPerWorldUnit );
  137. // Does the circle intersect the square?
  138. if ( !IsCircleIntersectingRectangle( mins, maxs, vecCircleCenter, flInPlaneLuxelRadius ) )
  139. return 0;
  140. // Ok, mark the surface as using this light.
  141. R_MarkSurfaceDLight( surfID, pLighting, bit);
  142. return 1;
  143. }
  144. int R_MarkLightsLeaf( dlight_t *light, int bit, mleaf_t *pLeaf )
  145. {
  146. int countMarked = 0;
  147. for ( int i = 0; i < pLeaf->dispCount; i++ )
  148. {
  149. IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  150. SurfaceHandle_t parentSurfID = pDispInfo->GetParent();
  151. if ( parentSurfID )
  152. {
  153. // Don't redo all this work if we already hit this surface and decided it's lit by this light.
  154. msurfacelighting_t *pLighting = SurfaceLighting( parentSurfID );
  155. if( !R_IsDLightAlreadyMarked( pLighting, bit) )
  156. {
  157. // Do a different test for displacement surfaces.
  158. Vector bmin, bmax;
  159. MSurf_DispInfo( parentSurfID )->GetBoundingBox( bmin, bmax );
  160. if ( IsBoxIntersectingSphere(bmin, bmax, light->origin, light->GetRadius()) )
  161. {
  162. R_MarkSurfaceDLight( parentSurfID, pLighting, bit );
  163. countMarked++;
  164. }
  165. }
  166. }
  167. }
  168. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  169. for ( int i = 0; i < pLeaf->nummarksurfaces; i++ )
  170. {
  171. SurfaceHandle_t surfID = pHandle[i];
  172. ASSERT_SURF_VALID( surfID );
  173. // only process leaf surfaces
  174. if ( MSurf_Flags( surfID ) & SURFDRAW_NODE )
  175. continue;
  176. // Don't redo all this work if we already hit this surface and decided it's lit by this light.
  177. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  178. if(R_IsDLightAlreadyMarked(pLighting, bit))
  179. continue;
  180. float dist = DotProduct( light->origin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist;
  181. if ( dist > light->GetRadius() || dist < -light->GetRadius() )
  182. continue;
  183. countMarked += R_TryLightMarkSurface( light, pLighting, surfID, bit );
  184. }
  185. return countMarked;
  186. }
  187. /*
  188. =============
  189. R_MarkLights
  190. =============
  191. */
  192. int R_MarkLights (dlight_t *light, int bit, mnode_t *node)
  193. {
  194. cplane_t *splitplane;
  195. float dist;
  196. int i;
  197. if (node->contents >= 0)
  198. {
  199. // This is a leaf, so check displacement surfaces and leaf faces
  200. return R_MarkLightsLeaf( light, bit, (mleaf_t*)node );
  201. }
  202. splitplane = node->plane;
  203. dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
  204. if (dist > light->GetRadius())
  205. {
  206. return R_MarkLights (light, bit, node->children[0]);
  207. }
  208. if (dist < -light->GetRadius())
  209. {
  210. return R_MarkLights (light, bit, node->children[1]);
  211. }
  212. // mark the polygons
  213. int countMarked = 0;
  214. SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface );
  215. for (i=0 ; i<node->numsurfaces ; i++, surfID++)
  216. {
  217. // Don't redo all this work if we already hit this surface and decided it's lit by this light.
  218. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  219. if(R_IsDLightAlreadyMarked( pLighting, bit))
  220. continue;
  221. countMarked += R_TryLightMarkSurface( light, pLighting, surfID, bit );
  222. }
  223. countMarked += R_MarkLights( light, bit, node->children[0] );
  224. return countMarked + R_MarkLights( light, bit, node->children[1] );
  225. }
  226. void R_MarkDLightsOnSurface( mnode_t* pNode )
  227. {
  228. if (!pNode || !g_bActiveDlights)
  229. return;
  230. dlight_t *l = cl_dlights;
  231. for (int i=0 ; i<MAX_DLIGHTS ; i++, l++)
  232. {
  233. if (l->die < GetBaseLocalClient().GetTime() || !l->IsRadiusGreaterThanZero() )
  234. continue;
  235. if (l->flags & DLIGHT_NO_WORLD_ILLUMINATION)
  236. continue;
  237. R_MarkLights ( l, 1<<i, pNode );
  238. }
  239. }
  240. /*
  241. =============
  242. R_PushDlights
  243. =============
  244. */
  245. void R_PushDlights (void)
  246. {
  247. R_MarkDLightsOnSurface( host_state.worldbrush->nodes );
  248. MarkDLightsOnStaticProps();
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Computes s and t coords of texture at intersection pt
  252. //-----------------------------------------------------------------------------
  253. static void ComputeTextureCoordsAtIntersection( mtexinfo_t* pTex, Vector const& pt, float *textureS, float *textureT )
  254. {
  255. if( pTex->material && textureS && textureT )
  256. {
  257. *textureS = DotProduct( pt, pTex->textureVecsTexelsPerWorldUnits[0].AsVector3D() ) +
  258. pTex->textureVecsTexelsPerWorldUnits[0][3];
  259. *textureT = DotProduct( pt, pTex->textureVecsTexelsPerWorldUnits[1].AsVector3D() ) +
  260. pTex->textureVecsTexelsPerWorldUnits[1][3];
  261. *textureS /= pTex->material->GetMappingWidth();
  262. *textureT /= pTex->material->GetMappingHeight();
  263. }
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Computes s and t coords of texture at intersection pt
  267. //-----------------------------------------------------------------------------
  268. static void ComputeLightmapCoordsAtIntersection( msurfacelighting_t *pLighting, float ds,
  269. float dt, float *lightmapS, float *lightmapT )
  270. {
  271. if( lightmapS && lightmapT )
  272. {
  273. if( pLighting->m_LightmapExtents[0] != 0 )
  274. *lightmapS = (ds + 0.5f) / ( float )pLighting->m_LightmapExtents[0];
  275. else
  276. *lightmapS = 0.5f;
  277. if( pLighting->m_LightmapExtents[1] != 0 )
  278. *lightmapT = (dt + 0.5f) / ( float )pLighting->m_LightmapExtents[1];
  279. else
  280. *lightmapT = 0.5f;
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Computes the lightmap color at a particular point
  285. //-----------------------------------------------------------------------------
  286. static void ComputeLightmapColorFromAverage( msurfacelighting_t *pLighting, bool bUseLightStyles, Vector& c )
  287. {
  288. int nMaxMaps = bUseLightStyles ? MAXLIGHTMAPS : 1;
  289. for (int maps = 0 ; maps < nMaxMaps && pLighting->m_nStyles[maps] != 255 ; ++maps)
  290. {
  291. float scale = LightStyleValue( pLighting->m_nStyles[maps] );
  292. ColorRGBExp32* pAvgColor = pLighting->AvgLightColor(maps);
  293. c[0] += TexLightToLinear( pAvgColor->r, pAvgColor->exponent ) * scale;
  294. c[1] += TexLightToLinear( pAvgColor->g, pAvgColor->exponent ) * scale;
  295. c[2] += TexLightToLinear( pAvgColor->b, pAvgColor->exponent ) * scale;
  296. }
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Computes the lightmap color at a particular point
  300. //-----------------------------------------------------------------------------
  301. static void ComputeLightmapColor( SurfaceHandle_t surfID, int ds, int dt, bool bUseLightStyles, Vector& c )
  302. {
  303. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  304. ColorRGBExp32* pLightmap = pLighting->m_pSamples;
  305. if( !pLightmap )
  306. {
  307. static int messagecount = 0;
  308. if ( ++messagecount < 10 )
  309. {
  310. // Stop spamming. I heard you already!!!
  311. ConMsg( "hit surface has no samples\n" );
  312. }
  313. return;
  314. }
  315. if ( !g_bHunkAllocLightmaps && r_keepstyledlightmapsonly.GetBool() )
  316. {
  317. // lightmap data is gone, can only fallback to an apporximate
  318. ComputeLightmapColorFromAverage( pLighting, bUseLightStyles, c );
  319. return;
  320. }
  321. int smax = ( pLighting->m_LightmapExtents[0] ) + 1;
  322. int tmax = ( pLighting->m_LightmapExtents[1] ) + 1;
  323. int offset = smax * tmax;
  324. if ( SurfHasBumpedLightmaps( surfID ) )
  325. {
  326. offset *= ( NUM_BUMP_VECTS + 1 );
  327. }
  328. pLightmap += dt * smax + ds;
  329. int nMaxMaps = bUseLightStyles ? MAXLIGHTMAPS : 1;
  330. for (int maps = 0 ; maps < nMaxMaps && pLighting->m_nStyles[maps] != 255 ; ++maps)
  331. {
  332. float scale = LightStyleValue( pLighting->m_nStyles[maps] );
  333. c[0] += TexLightToLinear( pLightmap->r, pLightmap->exponent ) * scale;
  334. c[1] += TexLightToLinear( pLightmap->g, pLightmap->exponent ) * scale;
  335. c[2] += TexLightToLinear( pLightmap->b, pLightmap->exponent ) * scale;
  336. // Check version 32 in source safe for some debugging crap
  337. pLightmap += offset;
  338. }
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Tests a particular surface
  342. //-----------------------------------------------------------------------------
  343. static bool FASTCALL FindIntersectionAtSurface( SurfaceHandle_t surfID, float f,
  344. Vector& c, LightVecState_t& state )
  345. {
  346. // no lightmaps on this surface? punt...
  347. // FIXME: should be water surface?
  348. if (MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT)
  349. return false;
  350. // Compute the actual point
  351. Vector pt;
  352. VectorMA( state.m_Ray.m_Start, f, state.m_Ray.m_Delta, pt );
  353. mtexinfo_t* pTex = MSurf_TexInfo( surfID );
  354. // See where in lightmap space our intersection point is
  355. float s, t;
  356. s = DotProduct (pt, pTex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  357. pTex->lightmapVecsLuxelsPerWorldUnits[0][3];
  358. t = DotProduct (pt, pTex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  359. pTex->lightmapVecsLuxelsPerWorldUnits[1][3];
  360. // Not in the bounds of our lightmap? punt...
  361. msurfacelighting_t *pLighting = SurfaceLighting( surfID );
  362. if( s < pLighting->m_LightmapMins[0] ||
  363. t < pLighting->m_LightmapMins[1] )
  364. return false;
  365. // assuming a square lightmap (FIXME: which ain't always the case),
  366. // lets see if it lies in that rectangle. If not, punt...
  367. float ds = s - pLighting->m_LightmapMins[0];
  368. float dt = t - pLighting->m_LightmapMins[1];
  369. if ( !pLighting->m_LightmapExtents[0] && !pLighting->m_LightmapExtents[1] )
  370. {
  371. worldbrushdata_t *pBrushData = host_state.worldbrush;
  372. //
  373. float lightMaxs[2];
  374. lightMaxs[ 0 ] = pLighting->m_LightmapMins[0];
  375. lightMaxs[ 1 ] = pLighting->m_LightmapMins[1];
  376. int i;
  377. for (i=0 ; i<MSurf_VertCount( surfID ); i++)
  378. {
  379. int e = pBrushData->vertindices[MSurf_FirstVertIndex( surfID )+i];
  380. mvertex_t *v = &pBrushData->vertexes[e];
  381. int j;
  382. for ( j=0 ; j<2 ; j++)
  383. {
  384. float sextent, textent;
  385. sextent = DotProduct (v->position, pTex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  386. pTex->lightmapVecsLuxelsPerWorldUnits[0][3] - pLighting->m_LightmapMins[0];
  387. textent = DotProduct (v->position, pTex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  388. pTex->lightmapVecsLuxelsPerWorldUnits[1][3] - pLighting->m_LightmapMins[1];
  389. if ( sextent > lightMaxs[ 0 ] )
  390. {
  391. lightMaxs[ 0 ] = sextent;
  392. }
  393. if ( textent > lightMaxs[ 1 ] )
  394. {
  395. lightMaxs[ 1 ] = textent;
  396. }
  397. }
  398. }
  399. if( ds > lightMaxs[0] || dt > lightMaxs[1] )
  400. return false;
  401. }
  402. else
  403. {
  404. if( ds > pLighting->m_LightmapExtents[0] || dt > pLighting->m_LightmapExtents[1] )
  405. return false;
  406. }
  407. // Store off the hit distance...
  408. state.m_HitFrac = f;
  409. // You heard the man!
  410. ComputeTextureCoordsAtIntersection( pTex, pt, state.m_pTextureS, state.m_pTextureT );
  411. #ifdef USE_CONVARS
  412. if ( r_avglight.GetInt() )
  413. #else
  414. if ( 1 )
  415. #endif
  416. {
  417. // This is the faster path; it looks slightly different though
  418. ComputeLightmapColorFromAverage( pLighting, state.m_bUseLightStyles, c );
  419. }
  420. else
  421. {
  422. // Compute lightmap coords
  423. ComputeLightmapCoordsAtIntersection( pLighting, ds, dt, state.m_pLightmapS, state.m_pLightmapT );
  424. // Check out the value of the lightmap at the intersection point
  425. ComputeLightmapColor( surfID, (int)ds, (int)dt, state.m_bUseLightStyles, c );
  426. }
  427. return true;
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Tests a particular node
  431. //-----------------------------------------------------------------------------
  432. // returns a surfID
  433. static SurfaceHandle_t FindIntersectionSurfaceAtNode( mnode_t *node, float t,
  434. Vector& c, LightVecState_t& state )
  435. {
  436. SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface );
  437. for (int i=0 ; i<node->numsurfaces ; ++i, ++surfID)
  438. {
  439. // Don't immediately return when we hit sky;
  440. // we may actually hit another surface
  441. if (MSurf_Flags( surfID ) & SURFDRAW_SKY)
  442. {
  443. state.m_nSkySurfID = surfID;
  444. continue;
  445. }
  446. // Don't let water surfaces affect us
  447. if (MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE)
  448. continue;
  449. // Check this surface to see if there's an intersection
  450. if (FindIntersectionAtSurface( surfID, t, c, state ))
  451. {
  452. return surfID;
  453. }
  454. }
  455. return SURFACE_HANDLE_INVALID;
  456. }
  457. //-----------------------------------------------------------------------------
  458. // Tests a ray against displacements
  459. //-----------------------------------------------------------------------------
  460. // returns surfID
  461. static SurfaceHandle_t R_LightVecDisplacementChain( LightVecState_t& state, bool bUseLightStyles, Vector& c )
  462. {
  463. // test the ray against displacements
  464. SurfaceHandle_t surfID = SURFACE_HANDLE_INVALID;
  465. for ( int i = 0; i < state.m_LightTestDisps.Count(); i++ )
  466. {
  467. float dist;
  468. Vector2D luv, tuv;
  469. IDispInfo *pDispInfo = state.m_LightTestDisps[i];
  470. if (pDispInfo->TestRay( state.m_Ray, 0.0f, state.m_HitFrac, dist, &luv, &tuv ))
  471. {
  472. // It hit it, and at a point closer than the previously computed
  473. // nearest intersection point
  474. state.m_HitFrac = dist;
  475. surfID = pDispInfo->GetParent();
  476. ComputeLightmapColor( surfID, (int)luv.x, (int)luv.y, bUseLightStyles, c );
  477. if (state.m_pLightmapS && state.m_pLightmapT)
  478. {
  479. ComputeLightmapCoordsAtIntersection( SurfaceLighting(surfID), (int)luv.x, (int)luv.y, state.m_pLightmapS, state.m_pLightmapT );
  480. }
  481. if (state.m_pTextureS && state.m_pTextureT)
  482. {
  483. *state.m_pTextureS = tuv.x;
  484. *state.m_pTextureT = tuv.y;
  485. }
  486. }
  487. }
  488. return surfID;
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Adds displacements in a leaf to a list to be tested against
  492. //-----------------------------------------------------------------------------
  493. static void AddDisplacementsInLeafToTestList( mleaf_t* pLeaf, LightVecState_t& state )
  494. {
  495. // add displacement surfaces
  496. for ( int i = 0; i < pLeaf->dispCount; i++ )
  497. {
  498. // NOTE: We're not using the displacement's touched method here
  499. // because we're just using the parent surface's visframe in the
  500. // surface add methods below
  501. IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  502. SurfaceHandle_t parentSurfID = pDispInfo->GetParent();
  503. // already processed this frame? Then don't do it again!
  504. if (MSurf_VisFrame( parentSurfID ) != r_surfacevisframe)
  505. {
  506. MSurf_VisFrame( parentSurfID ) = r_surfacevisframe;
  507. state.m_LightTestDisps.AddToTail( pDispInfo );
  508. }
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Tests a particular leaf
  513. //-----------------------------------------------------------------------------
  514. // returns surfID
  515. static SurfaceHandle_t FASTCALL FindIntersectionSurfaceAtLeaf( mleaf_t *pLeaf,
  516. float start, float end, Vector& c, LightVecState_t& state )
  517. {
  518. Vector pt;
  519. SurfaceHandle_t closestSurfID = SURFACE_HANDLE_INVALID;
  520. // Adds displacements in the leaf to a list of displacements to test at the end
  521. AddDisplacementsInLeafToTestList( pLeaf, state );
  522. // Add non-displacement surfaces
  523. // Since there's no BSP tree here, we gotta test *all* surfaces! (blech)
  524. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  525. // NOTE: Skip all marknodesurfaces, only check detail/leaf faces
  526. for ( int i = pLeaf->nummarknodesurfaces; i < pLeaf->nummarksurfaces; i++ )
  527. {
  528. SurfaceHandle_t surfID = pHandle[i];
  529. ASSERT_SURF_VALID( surfID );
  530. // Don't add surfaces that have displacement; they are handled above
  531. // In fact, don't even set the vis frame; we need it unset for translucent
  532. // displacement code
  533. if ( SurfaceHasDispInfo(surfID) )
  534. continue;
  535. Assert(!(MSurf_Flags( surfID ) & SURFDRAW_NODE));
  536. if ( MSurf_Flags( surfID ) & (SURFDRAW_NODE|SURFDRAW_NODRAW | SURFDRAW_WATERSURFACE) )
  537. continue;
  538. cplane_t* pPlane = &MSurf_Plane( surfID );
  539. // Backface cull...
  540. if (DotProduct( pPlane->normal, state.m_Ray.m_Delta ) > 0.f)
  541. continue;
  542. float startDotN = DotProduct( state.m_Ray.m_Start, pPlane->normal );
  543. float deltaDotN = DotProduct( state.m_Ray.m_Delta, pPlane->normal );
  544. float front = startDotN + start * deltaDotN - pPlane->dist;
  545. float back = startDotN + end * deltaDotN - pPlane->dist;
  546. int side = front < 0.f;
  547. // Blow it off if it doesn't split the plane...
  548. if ( (back < 0.f) == side )
  549. continue;
  550. // Don't test a surface that is farther away from the closest found intersection
  551. float frac = front / (front-back);
  552. if (frac >= state.m_HitFrac)
  553. continue;
  554. float mid = start * (1.0f - frac) + end * frac;
  555. // Check this surface to see if there's an intersection
  556. if (FindIntersectionAtSurface( surfID, mid, c, state ))
  557. {
  558. closestSurfID = surfID;
  559. }
  560. }
  561. // Return the closest surface hit
  562. return closestSurfID;
  563. }
  564. //-----------------------------------------------------------------------------
  565. // LIGHT SAMPLING
  566. //-----------------------------------------------------------------------------
  567. // returns surfID
  568. SurfaceHandle_t RecursiveLightPoint (mnode_t *node, float start, float end,
  569. Vector& c, LightVecState_t& state )
  570. {
  571. // didn't hit anything
  572. if (node->contents >= 0)
  573. {
  574. // FIXME: Should we always do this? It could get expensive...
  575. // Check all the faces at the leaves
  576. return FindIntersectionSurfaceAtLeaf( (mleaf_t*)node, start, end, c, state );
  577. }
  578. // Determine which side of the node plane our points are on
  579. // FIXME: optimize for axial
  580. cplane_t* plane = node->plane;
  581. float startDotN = DotProduct( state.m_Ray.m_Start, plane->normal );
  582. float deltaDotN = DotProduct( state.m_Ray.m_Delta, plane->normal );
  583. float front = startDotN + start * deltaDotN - plane->dist;
  584. float back = startDotN + end * deltaDotN - plane->dist;
  585. int side = front < 0;
  586. // If they're both on the same side of the plane, don't bother to split
  587. // just check the appropriate child
  588. SurfaceHandle_t surfID;
  589. if ( (back < 0) == side )
  590. {
  591. surfID = RecursiveLightPoint (node->children[side], start, end, c, state);
  592. return surfID;
  593. }
  594. // calculate mid point
  595. float frac = front / (front-back);
  596. float mid = start * (1.0f - frac) + end * frac;
  597. // go down front side
  598. surfID = RecursiveLightPoint (node->children[side], start, mid, c, state );
  599. if ( IS_SURF_VALID( surfID ) )
  600. return surfID; // hit something
  601. // check for impact on this node
  602. surfID = FindIntersectionSurfaceAtNode( node, mid, c, state );
  603. if ( IS_SURF_VALID( surfID ) )
  604. return surfID;
  605. // go down back side
  606. surfID = RecursiveLightPoint (node->children[!side], mid, end, c, state );
  607. return surfID;
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Allows us to use a different model for R_LightVec
  611. //-----------------------------------------------------------------------------
  612. void R_LightVecUseModel( model_t* pModel )
  613. {
  614. s_pLightVecModel = pModel;
  615. }
  616. //-----------------------------------------------------------------------------
  617. // returns light in range from 0 to 1.
  618. // lightmapS/T is in [0,1] within the space of the surface.
  619. // returns surfID
  620. //-----------------------------------------------------------------------------
  621. SurfaceHandle_t R_LightVec (const Vector& start, const Vector& end, bool bUseLightStyles, Vector& c,
  622. float *textureS, float *textureT, float *lightmapS, float *lightmapT )
  623. {
  624. VPROF_INCREMENT_COUNTER( "R_LightVec", 1 );
  625. SurfaceHandle_t retSurfID;
  626. SurfaceHandle_t dispSurfID;
  627. // We're using the vis frame here for lightvec tests
  628. // to make sure we test each displacement only once
  629. ++r_surfacevisframe;
  630. LightVecState_t state;
  631. state.m_HitFrac = 1.0f;
  632. state.m_Ray.Init( start, end );
  633. state.m_pTextureS = textureS;
  634. state.m_pTextureT = textureT;
  635. state.m_pLightmapS = lightmapS;
  636. state.m_pLightmapT = lightmapT;
  637. state.m_nSkySurfID = SURFACE_HANDLE_INVALID;
  638. state.m_bUseLightStyles = bUseLightStyles;
  639. c[0] = c[1] = c[2] = 0.0f;
  640. model_t* model = s_pLightVecModel ? s_pLightVecModel : host_state.worldmodel;
  641. retSurfID = RecursiveLightPoint(&model->brush.pShared->nodes[model->brush.firstnode],
  642. 0.0f, 1.0f, c, state );
  643. // While doing recursive light point, we built a list of all
  644. // displacement surfaces which we need to test, so let's test them
  645. dispSurfID = R_LightVecDisplacementChain( state, bUseLightStyles, c );
  646. if( r_visualizelighttraces.GetBool() )
  647. {
  648. if( r_visualizelighttracesshowfulltrace.GetBool() )
  649. {
  650. CDebugOverlay::AddLineOverlay( start, end, 0, 255, 0, 255, true, -1.0f );
  651. }
  652. else
  653. {
  654. CDebugOverlay::AddLineOverlay( start, start + ( end - start ) * state.m_HitFrac, 0, 255, 0, 255, true, -1.0f );
  655. }
  656. }
  657. if ( IS_SURF_VALID( dispSurfID ) )
  658. retSurfID = dispSurfID;
  659. // ConMsg( "R_LightVec: %f %f %f\n", c[0], c[1], c[2] );
  660. // If we didn't hit anything else, but we hit a sky surface at
  661. // some point along the ray cast, return the sky id.
  662. if ( ( retSurfID == SURFACE_HANDLE_INVALID ) && ( state.m_nSkySurfID != SURFACE_HANDLE_INVALID ) )
  663. return state.m_nSkySurfID;
  664. return retSurfID;
  665. }
  666. // returns light in range from 0 to 1.
  667. colorVec R_LightPoint (Vector& p)
  668. {
  669. SurfaceHandle_t surfID;
  670. Vector end;
  671. colorVec c;
  672. Vector color;
  673. end[0] = p[0];
  674. end[1] = p[1];
  675. end[2] = p[2] - 2048;
  676. surfID = R_LightVec( p, end, true, color );
  677. if( IS_SURF_VALID( surfID ) )
  678. {
  679. c.r = LinearToScreenGamma( color[0] ) * 255;
  680. c.g = LinearToScreenGamma( color[1] ) * 255;
  681. c.b = LinearToScreenGamma( color[2] ) * 255;
  682. c.a = 1;
  683. }
  684. else
  685. {
  686. c.r = c.g = c.b = c.a = 0;
  687. }
  688. return c;
  689. }