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.

469 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "render_pch.h"
  10. #include "gl_matsysiface.h"
  11. #include "gl_cvars.h"
  12. #include "enginetrace.h"
  13. #include "r_local.h"
  14. #include "gl_model_private.h"
  15. #include "materialsystem/imesh.h"
  16. #include "cdll_engine_int.h"
  17. #include "cl_main.h"
  18. #include "debugoverlay.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. static ConVar r_drawlights( "r_drawlights", "0", FCVAR_CHEAT );
  22. static ConVar r_drawlightinfo( "r_drawlightinfo", "0", FCVAR_CHEAT );
  23. static bool s_bActivateLightSprites = false;
  24. //-----------------------------------------------------------------------------
  25. // Should we draw light sprites over visible lights?
  26. //-----------------------------------------------------------------------------
  27. bool ActivateLightSprites( bool bActive )
  28. {
  29. bool bOldValue = s_bActivateLightSprites;
  30. s_bActivateLightSprites = bActive;
  31. return bOldValue;
  32. }
  33. #define LIGHT_MIN_LIGHT_VALUE 0.03f
  34. float ComputeLightRadius( dworldlight_t *pLight, bool bIsHDR )
  35. {
  36. float flLightRadius = pLight->radius;
  37. if (flLightRadius == 0.0f)
  38. {
  39. // HACKHACK: Usually our designers scale the light intensity by 0.5 in HDR
  40. // This keeps the behavior of the cutoff radius consistent between LDR and HDR
  41. float minLightValue = bIsHDR ? (LIGHT_MIN_LIGHT_VALUE * 0.5f) : LIGHT_MIN_LIGHT_VALUE;
  42. // Compute the light range based on attenuation factors
  43. float flIntensity = sqrtf( DotProduct( pLight->intensity, pLight->intensity ) );
  44. if (pLight->quadratic_attn == 0.0f)
  45. {
  46. if (pLight->linear_attn == 0.0f)
  47. {
  48. // Infinite, but we're not going to draw it as such
  49. flLightRadius = 2000;
  50. }
  51. else
  52. {
  53. flLightRadius = (flIntensity / minLightValue - pLight->constant_attn) / pLight->linear_attn;
  54. }
  55. }
  56. else
  57. {
  58. float a = pLight->quadratic_attn;
  59. float b = pLight->linear_attn;
  60. float c = pLight->constant_attn - flIntensity / minLightValue;
  61. float discrim = b * b - 4 * a * c;
  62. if (discrim < 0.0f)
  63. {
  64. // Infinite, but we're not going to draw it as such
  65. flLightRadius = 2000;
  66. }
  67. else
  68. {
  69. flLightRadius = (-b + sqrtf(discrim)) / (2.0f * a);
  70. if (flLightRadius < 0)
  71. flLightRadius = 0;
  72. }
  73. }
  74. }
  75. return flLightRadius;
  76. }
  77. static void DrawLightSprite( dworldlight_t *pLight, float angleAttenFactor )
  78. {
  79. Vector lightToEye;
  80. lightToEye = CurrentViewOrigin() - pLight->origin;
  81. VectorNormalize( lightToEye );
  82. Vector up( 0.0f, 0.0f, 1.0f );
  83. Vector right;
  84. CrossProduct( up, lightToEye, right );
  85. VectorNormalize( right );
  86. CrossProduct( lightToEye, right, up );
  87. VectorNormalize( up );
  88. /*
  89. up *= dist;
  90. right *= dist;
  91. up *= ( 1.0f / 5.0f );
  92. right *= ( 1.0f / 5.0f );
  93. up *= 1.0f / sqrt( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
  94. right *= 1.0f / sqrt( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
  95. */
  96. // float distFactor = 1.0f / ( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
  97. //float distFactor = 1.0f;
  98. Vector color = pLight->intensity;
  99. VectorNormalize( color );
  100. color *= angleAttenFactor;
  101. color[0] = pow( color[0], 1.0f / 2.2f );
  102. color[1] = pow( color[1], 1.0f / 2.2f );
  103. color[2] = pow( color[2], 1.0f / 2.2f );
  104. CMatRenderContextPtr pRenderContext( materials );
  105. pRenderContext->Bind( g_pMaterialLightSprite );
  106. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  107. CMeshBuilder meshBuilder;
  108. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  109. float radius = 16.0f;
  110. Vector p;
  111. ColorClamp( color );
  112. p = pLight->origin + right * radius + up * radius;
  113. meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
  114. meshBuilder.Color3fv( color.Base() );
  115. meshBuilder.Position3fv( p.Base() );
  116. meshBuilder.AdvanceVertex();
  117. p = pLight->origin + right * -radius + up * radius;
  118. meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
  119. meshBuilder.Color3fv( color.Base() );
  120. meshBuilder.Position3fv( p.Base() );
  121. meshBuilder.AdvanceVertex();
  122. p = pLight->origin + right * -radius + up * -radius;
  123. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  124. meshBuilder.Color3fv( color.Base() );
  125. meshBuilder.Position3fv( p.Base() );
  126. meshBuilder.AdvanceVertex();
  127. p = pLight->origin + right * radius + up * -radius;
  128. meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
  129. meshBuilder.Color3fv( color.Base() );
  130. meshBuilder.Position3fv( p.Base() );
  131. meshBuilder.AdvanceVertex();
  132. meshBuilder.End();
  133. pMesh->Draw();
  134. }
  135. #define POINT_THETA_GRID 8
  136. #define POINT_PHI_GRID 8
  137. static void DrawPointLight( const Vector &vecOrigin, float flLightRadius )
  138. {
  139. int nVertCount = POINT_THETA_GRID * (POINT_PHI_GRID + 1);
  140. int nIndexCount = 8 * POINT_THETA_GRID * POINT_PHI_GRID;
  141. CMatRenderContextPtr pRenderContext( materials );
  142. pRenderContext->Bind( g_materialWorldWireframeZBuffer );
  143. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  144. CMeshBuilder meshBuilder;
  145. meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertCount, nIndexCount );
  146. float dTheta = 360.0f / POINT_THETA_GRID;
  147. float dPhi = 180.0f / POINT_PHI_GRID;
  148. Vector pt;
  149. int i;
  150. float flPhi = 0;
  151. for ( i = 0; i <= POINT_PHI_GRID; ++i )
  152. {
  153. float flSinPhi = sin(DEG2RAD(flPhi));
  154. float flCosPhi = cos(DEG2RAD(flPhi));
  155. float flTheta = 0;
  156. for ( int j = 0; j < POINT_THETA_GRID; ++j )
  157. {
  158. pt = vecOrigin;
  159. pt.x += flLightRadius * cos(DEG2RAD(flTheta)) * flSinPhi;
  160. pt.y += flLightRadius * sin(DEG2RAD(flTheta)) * flSinPhi;
  161. pt.z += flLightRadius * flCosPhi;
  162. meshBuilder.Position3fv( pt.Base() );
  163. meshBuilder.AdvanceVertex();
  164. flTheta += dTheta;
  165. }
  166. flPhi += dPhi;
  167. }
  168. for ( i = 0; i < POINT_THETA_GRID; ++i )
  169. {
  170. for ( int j = 0; j < POINT_PHI_GRID; ++j )
  171. {
  172. int nNextIndex = (j != POINT_PHI_GRID - 1) ? j + 1 : 0;
  173. meshBuilder.Index( i * POINT_PHI_GRID + j );
  174. meshBuilder.AdvanceIndex();
  175. meshBuilder.Index( (i + 1) * POINT_PHI_GRID + j );
  176. meshBuilder.AdvanceIndex();
  177. meshBuilder.Index( (i + 1) * POINT_PHI_GRID + j );
  178. meshBuilder.AdvanceIndex();
  179. meshBuilder.Index( (i + 1) * POINT_PHI_GRID + nNextIndex );
  180. meshBuilder.AdvanceIndex();
  181. meshBuilder.Index( (i + 1) * POINT_PHI_GRID + nNextIndex );
  182. meshBuilder.AdvanceIndex();
  183. meshBuilder.Index( i * POINT_PHI_GRID + nNextIndex );
  184. meshBuilder.AdvanceIndex();
  185. meshBuilder.Index( i * POINT_PHI_GRID + nNextIndex );
  186. meshBuilder.AdvanceIndex();
  187. meshBuilder.Index( i * POINT_PHI_GRID + j );
  188. meshBuilder.AdvanceIndex();
  189. }
  190. }
  191. meshBuilder.End();
  192. pMesh->Draw();
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Draws the spot light
  196. //-----------------------------------------------------------------------------
  197. #define SPOT_GRID_LINE_COUNT 20
  198. #define SPOT_GRID_LINE_DISTANCE 50
  199. #define SPOT_RADIAL_GRID 8
  200. void DrawSpotLight( dworldlight_t *pLight )
  201. {
  202. float flLightRadius = ComputeLightRadius( pLight, false );
  203. int nGridLines = (int)(flLightRadius / SPOT_GRID_LINE_DISTANCE) + 1;
  204. int nVertCount = SPOT_RADIAL_GRID * (nGridLines + 1);
  205. int nIndexCount = 8 * SPOT_RADIAL_GRID * nGridLines;
  206. // Compute a basis perpendicular to the normal
  207. Vector xaxis, yaxis;
  208. int nMinIndex = fabs(pLight->normal[0]) < fabs(pLight->normal[1]) ? 0 : 1;
  209. nMinIndex = fabs(pLight->normal[nMinIndex]) < fabs(pLight->normal[2]) ? nMinIndex : 2;
  210. Vector perp = vec3_origin;
  211. perp[nMinIndex] = 1.0f;
  212. CrossProduct( perp, pLight->normal, xaxis );
  213. VectorNormalize( xaxis );
  214. CrossProduct( pLight->normal, xaxis, yaxis );
  215. CMatRenderContextPtr pRenderContext( materials );
  216. pRenderContext->Bind( g_materialWorldWireframeZBuffer );
  217. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  218. CMeshBuilder meshBuilder;
  219. meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertCount, nIndexCount );
  220. float flAngle = acos(pLight->stopdot2);
  221. float flTanAngle = tan(flAngle);
  222. float dTheta = 360.0f / SPOT_RADIAL_GRID;
  223. float flDist = 0.0f;
  224. int i;
  225. for ( i = 0; i <= nGridLines; ++i )
  226. {
  227. Vector pt, vecCenter;
  228. VectorMA( pLight->origin, flDist, pLight->normal, vecCenter );
  229. float flRadius = flDist * flTanAngle;
  230. float flTempAngle = 0;
  231. for ( int j = 0; j < SPOT_RADIAL_GRID; ++j )
  232. {
  233. float flSin = sin( DEG2RAD( flTempAngle ) );
  234. float flCos = cos( DEG2RAD( flTempAngle ) );
  235. VectorMA( vecCenter, flRadius * flCos, xaxis, pt );
  236. VectorMA( pt, flRadius * flSin, yaxis, pt );
  237. meshBuilder.Position3fv( pt.Base() );
  238. meshBuilder.AdvanceVertex();
  239. flTempAngle += dTheta;
  240. }
  241. flDist += SPOT_GRID_LINE_DISTANCE;
  242. }
  243. for ( i = 0; i < nGridLines; ++i )
  244. {
  245. for ( int j = 0; j < SPOT_RADIAL_GRID; ++j )
  246. {
  247. int nNextIndex = (j != SPOT_RADIAL_GRID - 1) ? j + 1 : 0;
  248. meshBuilder.Index( i * SPOT_RADIAL_GRID + j );
  249. meshBuilder.AdvanceIndex();
  250. meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + j );
  251. meshBuilder.AdvanceIndex();
  252. meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + j );
  253. meshBuilder.AdvanceIndex();
  254. meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + nNextIndex );
  255. meshBuilder.AdvanceIndex();
  256. meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + nNextIndex );
  257. meshBuilder.AdvanceIndex();
  258. meshBuilder.Index( i * SPOT_RADIAL_GRID + nNextIndex );
  259. meshBuilder.AdvanceIndex();
  260. meshBuilder.Index( i * SPOT_RADIAL_GRID + nNextIndex );
  261. meshBuilder.AdvanceIndex();
  262. meshBuilder.Index( i * SPOT_RADIAL_GRID + j );
  263. meshBuilder.AdvanceIndex();
  264. }
  265. }
  266. meshBuilder.End();
  267. pMesh->Draw();
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Draws sprites over all visible lights
  271. // NOTE: This is used to render env-cubemaps
  272. //-----------------------------------------------------------------------------
  273. void DrawLightSprites( void )
  274. {
  275. if (!s_bActivateLightSprites)
  276. return;
  277. int i;
  278. for (i = 0; i < host_state.worldbrush->numworldlights; i++)
  279. {
  280. dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
  281. trace_t tr;
  282. CTraceFilterWorldAndPropsOnly traceFilter;
  283. Ray_t ray;
  284. ray.Init( CurrentViewOrigin(), pLight->origin );
  285. g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, &traceFilter, &tr );
  286. if( tr.fraction < 1.0f )
  287. continue;
  288. float angleAttenFactor = 0.0f;
  289. Vector lightToEye;
  290. lightToEye = CurrentViewOrigin() - pLight->origin;
  291. VectorNormalize( lightToEye );
  292. switch( pLight->type )
  293. {
  294. case emit_point:
  295. angleAttenFactor = 1.0f;
  296. break;
  297. case emit_spotlight:
  298. continue;
  299. break;
  300. case emit_surface:
  301. // garymcthack - don't do surface lights
  302. continue;
  303. if( DotProduct( lightToEye, pLight->normal ) < 0.0f )
  304. {
  305. continue;
  306. }
  307. angleAttenFactor = 1.0f;
  308. break;
  309. case emit_skylight:
  310. case emit_skyambient:
  311. continue;
  312. default:
  313. assert( 0 );
  314. continue;
  315. }
  316. DrawLightSprite( pLight, angleAttenFactor );
  317. }
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Draws debugging information for the lights
  321. //-----------------------------------------------------------------------------
  322. void DrawLightDebuggingInfo( void )
  323. {
  324. int i;
  325. char buf[256];
  326. int lineOffset;
  327. int nLight = r_drawlights.GetInt();
  328. if ( r_drawlightinfo.GetBool() )
  329. {
  330. for (i = 0; i < host_state.worldbrush->numworldlights; i++)
  331. {
  332. dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
  333. lineOffset = 0;
  334. Q_snprintf( buf, sizeof( buf ), "light: %d\n", i+1 );
  335. CDebugOverlay::AddTextOverlay( pLight->origin, lineOffset++, 0, buf );
  336. Q_snprintf( buf, sizeof( buf ), "origin: <%d, %d, %d>\n", (int)pLight->origin[0], (int)pLight->origin[1], (int)pLight->origin[2] );
  337. CDebugOverlay::AddTextOverlay( pLight->origin, lineOffset++, 0, buf );
  338. if (!nLight)
  339. {
  340. // avoid a double debug draw
  341. DrawLightSprite( pLight, 1.0f );
  342. }
  343. }
  344. }
  345. if (!nLight)
  346. return;
  347. for (i = 0; i < host_state.worldbrush->numworldlights; i++)
  348. {
  349. if ((nLight > 0) && (i != nLight-1))
  350. continue;
  351. dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
  352. Vector lightToEye;
  353. float angleAttenFactor = 0.0f;
  354. switch( pLight->type )
  355. {
  356. case emit_point:
  357. angleAttenFactor = 1.0f;
  358. DrawPointLight( pLight->origin, ComputeLightRadius( pLight, false ) );
  359. break;
  360. case emit_spotlight:
  361. angleAttenFactor = 1.0f;
  362. DrawSpotLight( pLight );
  363. break;
  364. case emit_surface:
  365. // garymcthack - don't do surface lights
  366. continue;
  367. lightToEye = CurrentViewOrigin() - pLight->origin;
  368. VectorNormalize( lightToEye );
  369. if( DotProduct( lightToEye, pLight->normal ) < 0.0f )
  370. {
  371. continue;
  372. }
  373. angleAttenFactor = 1.0f;
  374. break;
  375. case emit_skylight:
  376. case emit_skyambient:
  377. continue;
  378. default:
  379. assert( 0 );
  380. continue;
  381. }
  382. DrawLightSprite( pLight, angleAttenFactor );
  383. }
  384. int lnum;
  385. for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
  386. {
  387. // If the light's not active, then continue
  388. if ( (r_dlightactive & (1 << lnum)) == 0 )
  389. continue;
  390. DrawPointLight( cl_dlights[lnum].origin, cl_dlights[lnum].GetRadius() );
  391. }
  392. }