summaryrefslogtreecommitdiff
path: root/engine/gl_drawlights.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/gl_drawlights.cpp')
-rw-r--r--engine/gl_drawlights.cpp469
1 files changed, 469 insertions, 0 deletions
diff --git a/engine/gl_drawlights.cpp b/engine/gl_drawlights.cpp
new file mode 100644
index 0000000..4993d62
--- /dev/null
+++ b/engine/gl_drawlights.cpp
@@ -0,0 +1,469 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include "render_pch.h"
+#include "gl_matsysiface.h"
+#include "gl_cvars.h"
+#include "enginetrace.h"
+#include "r_local.h"
+#include "gl_model_private.h"
+#include "materialsystem/imesh.h"
+#include "cdll_engine_int.h"
+#include "cl_main.h"
+#include "debugoverlay.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static ConVar r_drawlights( "r_drawlights", "0", FCVAR_CHEAT );
+static ConVar r_drawlightinfo( "r_drawlightinfo", "0", FCVAR_CHEAT );
+
+static bool s_bActivateLightSprites = false;
+
+//-----------------------------------------------------------------------------
+// Should we draw light sprites over visible lights?
+//-----------------------------------------------------------------------------
+bool ActivateLightSprites( bool bActive )
+{
+ bool bOldValue = s_bActivateLightSprites;
+ s_bActivateLightSprites = bActive;
+ return bOldValue;
+}
+
+
+#define LIGHT_MIN_LIGHT_VALUE 0.03f
+
+float ComputeLightRadius( dworldlight_t *pLight, bool bIsHDR )
+{
+ float flLightRadius = pLight->radius;
+ if (flLightRadius == 0.0f)
+ {
+ // HACKHACK: Usually our designers scale the light intensity by 0.5 in HDR
+ // This keeps the behavior of the cutoff radius consistent between LDR and HDR
+ float minLightValue = bIsHDR ? (LIGHT_MIN_LIGHT_VALUE * 0.5f) : LIGHT_MIN_LIGHT_VALUE;
+
+ // Compute the light range based on attenuation factors
+ float flIntensity = sqrtf( DotProduct( pLight->intensity, pLight->intensity ) );
+ if (pLight->quadratic_attn == 0.0f)
+ {
+ if (pLight->linear_attn == 0.0f)
+ {
+ // Infinite, but we're not going to draw it as such
+ flLightRadius = 2000;
+ }
+ else
+ {
+ flLightRadius = (flIntensity / minLightValue - pLight->constant_attn) / pLight->linear_attn;
+ }
+ }
+ else
+ {
+ float a = pLight->quadratic_attn;
+ float b = pLight->linear_attn;
+ float c = pLight->constant_attn - flIntensity / minLightValue;
+ float discrim = b * b - 4 * a * c;
+ if (discrim < 0.0f)
+ {
+ // Infinite, but we're not going to draw it as such
+ flLightRadius = 2000;
+ }
+ else
+ {
+ flLightRadius = (-b + sqrtf(discrim)) / (2.0f * a);
+ if (flLightRadius < 0)
+ flLightRadius = 0;
+ }
+ }
+ }
+
+ return flLightRadius;
+}
+
+
+static void DrawLightSprite( dworldlight_t *pLight, float angleAttenFactor )
+{
+ Vector lightToEye;
+ lightToEye = CurrentViewOrigin() - pLight->origin;
+ VectorNormalize( lightToEye );
+ Vector up( 0.0f, 0.0f, 1.0f );
+ Vector right;
+ CrossProduct( up, lightToEye, right );
+ VectorNormalize( right );
+ CrossProduct( lightToEye, right, up );
+ VectorNormalize( up );
+
+/*
+ up *= dist;
+ right *= dist;
+
+ up *= ( 1.0f / 5.0f );
+ right *= ( 1.0f / 5.0f );
+
+ up *= 1.0f / sqrt( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
+ right *= 1.0f / sqrt( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
+*/
+
+ // float distFactor = 1.0f / ( pLight->constant_attn + dist * pLight->linear_attn + dist * dist * pLight->quadratic_attn );
+ //float distFactor = 1.0f;
+
+ Vector color = pLight->intensity;
+ VectorNormalize( color );
+ color *= angleAttenFactor;
+
+ color[0] = pow( color[0], 1.0f / 2.2f );
+ color[1] = pow( color[1], 1.0f / 2.2f );
+ color[2] = pow( color[2], 1.0f / 2.2f );
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ pRenderContext->Bind( g_pMaterialLightSprite );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ float radius = 16.0f;
+ Vector p;
+
+ ColorClamp( color );
+
+ p = pLight->origin + right * radius + up * radius;
+ meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
+ meshBuilder.Color3fv( color.Base() );
+ meshBuilder.Position3fv( p.Base() );
+ meshBuilder.AdvanceVertex();
+
+ p = pLight->origin + right * -radius + up * radius;
+ meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
+ meshBuilder.Color3fv( color.Base() );
+ meshBuilder.Position3fv( p.Base() );
+ meshBuilder.AdvanceVertex();
+
+ p = pLight->origin + right * -radius + up * -radius;
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ meshBuilder.Color3fv( color.Base() );
+ meshBuilder.Position3fv( p.Base() );
+ meshBuilder.AdvanceVertex();
+
+ p = pLight->origin + right * radius + up * -radius;
+ meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
+ meshBuilder.Color3fv( color.Base() );
+ meshBuilder.Position3fv( p.Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+#define POINT_THETA_GRID 8
+#define POINT_PHI_GRID 8
+
+static void DrawPointLight( const Vector &vecOrigin, float flLightRadius )
+{
+ int nVertCount = POINT_THETA_GRID * (POINT_PHI_GRID + 1);
+ int nIndexCount = 8 * POINT_THETA_GRID * POINT_PHI_GRID;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ pRenderContext->Bind( g_materialWorldWireframeZBuffer );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertCount, nIndexCount );
+
+ float dTheta = 360.0f / POINT_THETA_GRID;
+ float dPhi = 180.0f / POINT_PHI_GRID;
+
+ Vector pt;
+ int i;
+ float flPhi = 0;
+ for ( i = 0; i <= POINT_PHI_GRID; ++i )
+ {
+ float flSinPhi = sin(DEG2RAD(flPhi));
+ float flCosPhi = cos(DEG2RAD(flPhi));
+ float flTheta = 0;
+ for ( int j = 0; j < POINT_THETA_GRID; ++j )
+ {
+ pt = vecOrigin;
+ pt.x += flLightRadius * cos(DEG2RAD(flTheta)) * flSinPhi;
+ pt.y += flLightRadius * sin(DEG2RAD(flTheta)) * flSinPhi;
+ pt.z += flLightRadius * flCosPhi;
+
+ meshBuilder.Position3fv( pt.Base() );
+ meshBuilder.AdvanceVertex();
+
+ flTheta += dTheta;
+ }
+
+ flPhi += dPhi;
+ }
+
+ for ( i = 0; i < POINT_THETA_GRID; ++i )
+ {
+ for ( int j = 0; j < POINT_PHI_GRID; ++j )
+ {
+ int nNextIndex = (j != POINT_PHI_GRID - 1) ? j + 1 : 0;
+
+ meshBuilder.Index( i * POINT_PHI_GRID + j );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( (i + 1) * POINT_PHI_GRID + j );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( (i + 1) * POINT_PHI_GRID + j );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( (i + 1) * POINT_PHI_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( (i + 1) * POINT_PHI_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( i * POINT_PHI_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( i * POINT_PHI_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( i * POINT_PHI_GRID + j );
+ meshBuilder.AdvanceIndex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Draws the spot light
+//-----------------------------------------------------------------------------
+#define SPOT_GRID_LINE_COUNT 20
+#define SPOT_GRID_LINE_DISTANCE 50
+#define SPOT_RADIAL_GRID 8
+
+void DrawSpotLight( dworldlight_t *pLight )
+{
+ float flLightRadius = ComputeLightRadius( pLight, false );
+
+ int nGridLines = (int)(flLightRadius / SPOT_GRID_LINE_DISTANCE) + 1;
+ int nVertCount = SPOT_RADIAL_GRID * (nGridLines + 1);
+ int nIndexCount = 8 * SPOT_RADIAL_GRID * nGridLines;
+
+ // Compute a basis perpendicular to the normal
+ Vector xaxis, yaxis;
+ int nMinIndex = fabs(pLight->normal[0]) < fabs(pLight->normal[1]) ? 0 : 1;
+ nMinIndex = fabs(pLight->normal[nMinIndex]) < fabs(pLight->normal[2]) ? nMinIndex : 2;
+ Vector perp = vec3_origin;
+ perp[nMinIndex] = 1.0f;
+ CrossProduct( perp, pLight->normal, xaxis );
+ VectorNormalize( xaxis );
+ CrossProduct( pLight->normal, xaxis, yaxis );
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ pRenderContext->Bind( g_materialWorldWireframeZBuffer );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertCount, nIndexCount );
+
+ float flAngle = acos(pLight->stopdot2);
+ float flTanAngle = tan(flAngle);
+ float dTheta = 360.0f / SPOT_RADIAL_GRID;
+ float flDist = 0.0f;
+
+ int i;
+ for ( i = 0; i <= nGridLines; ++i )
+ {
+ Vector pt, vecCenter;
+ VectorMA( pLight->origin, flDist, pLight->normal, vecCenter );
+
+ float flRadius = flDist * flTanAngle;
+
+ float flTempAngle = 0;
+ for ( int j = 0; j < SPOT_RADIAL_GRID; ++j )
+ {
+ float flSin = sin( DEG2RAD( flTempAngle ) );
+ float flCos = cos( DEG2RAD( flTempAngle ) );
+ VectorMA( vecCenter, flRadius * flCos, xaxis, pt );
+ VectorMA( pt, flRadius * flSin, yaxis, pt );
+
+ meshBuilder.Position3fv( pt.Base() );
+ meshBuilder.AdvanceVertex();
+
+ flTempAngle += dTheta;
+ }
+
+ flDist += SPOT_GRID_LINE_DISTANCE;
+ }
+
+ for ( i = 0; i < nGridLines; ++i )
+ {
+ for ( int j = 0; j < SPOT_RADIAL_GRID; ++j )
+ {
+ int nNextIndex = (j != SPOT_RADIAL_GRID - 1) ? j + 1 : 0;
+
+ meshBuilder.Index( i * SPOT_RADIAL_GRID + j );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + j );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + j );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( (i + 1) * SPOT_RADIAL_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( i * SPOT_RADIAL_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( i * SPOT_RADIAL_GRID + nNextIndex );
+ meshBuilder.AdvanceIndex();
+ meshBuilder.Index( i * SPOT_RADIAL_GRID + j );
+ meshBuilder.AdvanceIndex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws sprites over all visible lights
+// NOTE: This is used to render env-cubemaps
+//-----------------------------------------------------------------------------
+void DrawLightSprites( void )
+{
+ if (!s_bActivateLightSprites)
+ return;
+
+ int i;
+ for (i = 0; i < host_state.worldbrush->numworldlights; i++)
+ {
+ dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
+ trace_t tr;
+ CTraceFilterWorldAndPropsOnly traceFilter;
+ Ray_t ray;
+ ray.Init( CurrentViewOrigin(), pLight->origin );
+ g_pEngineTraceClient->TraceRay( ray, MASK_OPAQUE, &traceFilter, &tr );
+ if( tr.fraction < 1.0f )
+ continue;
+
+ float angleAttenFactor = 0.0f;
+ Vector lightToEye;
+ lightToEye = CurrentViewOrigin() - pLight->origin;
+ VectorNormalize( lightToEye );
+ switch( pLight->type )
+ {
+ case emit_point:
+ angleAttenFactor = 1.0f;
+ break;
+ case emit_spotlight:
+ continue;
+ break;
+ case emit_surface:
+ // garymcthack - don't do surface lights
+ continue;
+ if( DotProduct( lightToEye, pLight->normal ) < 0.0f )
+ {
+ continue;
+ }
+ angleAttenFactor = 1.0f;
+ break;
+ case emit_skylight:
+ case emit_skyambient:
+ continue;
+ default:
+ assert( 0 );
+ continue;
+ }
+ DrawLightSprite( pLight, angleAttenFactor );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws debugging information for the lights
+//-----------------------------------------------------------------------------
+void DrawLightDebuggingInfo( void )
+{
+ int i;
+ char buf[256];
+ int lineOffset;
+
+ int nLight = r_drawlights.GetInt();
+
+ if ( r_drawlightinfo.GetBool() )
+ {
+ for (i = 0; i < host_state.worldbrush->numworldlights; i++)
+ {
+ dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
+
+ lineOffset = 0;
+ Q_snprintf( buf, sizeof( buf ), "light: %d\n", i+1 );
+ CDebugOverlay::AddTextOverlay( pLight->origin, lineOffset++, 0, buf );
+ Q_snprintf( buf, sizeof( buf ), "origin: <%d, %d, %d>\n", (int)pLight->origin[0], (int)pLight->origin[1], (int)pLight->origin[2] );
+ CDebugOverlay::AddTextOverlay( pLight->origin, lineOffset++, 0, buf );
+
+ if (!nLight)
+ {
+ // avoid a double debug draw
+ DrawLightSprite( pLight, 1.0f );
+ }
+ }
+ }
+
+ if (!nLight)
+ return;
+
+ for (i = 0; i < host_state.worldbrush->numworldlights; i++)
+ {
+ if ((nLight > 0) && (i != nLight-1))
+ continue;
+
+ dworldlight_t *pLight = &host_state.worldbrush->worldlights[i];
+ Vector lightToEye;
+ float angleAttenFactor = 0.0f;
+ switch( pLight->type )
+ {
+ case emit_point:
+ angleAttenFactor = 1.0f;
+ DrawPointLight( pLight->origin, ComputeLightRadius( pLight, false ) );
+ break;
+ case emit_spotlight:
+ angleAttenFactor = 1.0f;
+ DrawSpotLight( pLight );
+ break;
+ case emit_surface:
+ // garymcthack - don't do surface lights
+ continue;
+ lightToEye = CurrentViewOrigin() - pLight->origin;
+ VectorNormalize( lightToEye );
+ if( DotProduct( lightToEye, pLight->normal ) < 0.0f )
+ {
+ continue;
+ }
+ angleAttenFactor = 1.0f;
+ break;
+ case emit_skylight:
+ case emit_skyambient:
+ continue;
+ default:
+ assert( 0 );
+ continue;
+ }
+ DrawLightSprite( pLight, angleAttenFactor );
+ }
+
+ int lnum;
+ for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
+ {
+ // If the light's not active, then continue
+ if ( (r_dlightactive & (1 << lnum)) == 0 )
+ continue;
+
+ DrawPointLight( cl_dlights[lnum].origin, cl_dlights[lnum].GetRadius() );
+ }
+}