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
469 lines
13 KiB
//========= 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() ); |
|
} |
|
}
|
|
|