diff options
Diffstat (limited to 'engine/gl_drawlights.cpp')
| -rw-r--r-- | engine/gl_drawlights.cpp | 469 |
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() ); + } +} |