Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

830 lines
25 KiB

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