|
|
//========= 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() ); } }
|