mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-12-12 08:38:24 +00:00
261 lines
7.3 KiB
C++
261 lines
7.3 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
#include "vrad.h"
|
|
#include "leaf_ambient_lighting.h"
|
|
#include "bsplib.h"
|
|
#include "vraddetailprops.h"
|
|
#include "anorms.h"
|
|
#include "pacifier.h"
|
|
|
|
|
|
static TableVector g_BoxDirections[6] =
|
|
{
|
|
{ 1, 0, 0 },
|
|
{ -1, 0, 0 },
|
|
{ 0, 1, 0 },
|
|
{ 0, -1, 0 },
|
|
{ 0, 0, 1 },
|
|
{ 0, 0, -1 },
|
|
};
|
|
|
|
|
|
|
|
static void ComputeAmbientFromSurface( dface_t *surfID, dworldlight_t* pSkylight,
|
|
Vector& radcolor )
|
|
{
|
|
if ( !surfID )
|
|
return;
|
|
|
|
texinfo_t *pTexInfo = &texinfo[surfID->texinfo];
|
|
|
|
// If we hit the sky, use the sky ambient
|
|
if ( pTexInfo->flags & SURF_SKY )
|
|
{
|
|
if ( pSkylight )
|
|
{
|
|
// add in sky ambient
|
|
VectorCopy( pSkylight->intensity, radcolor );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vector reflectivity = dtexdata[pTexInfo->texdata].reflectivity;
|
|
VectorMultiply( radcolor, reflectivity, radcolor );
|
|
}
|
|
}
|
|
|
|
|
|
// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
|
|
float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
|
|
{
|
|
float dot, dot2;
|
|
|
|
Assert( wl->type == emit_surface );
|
|
|
|
dot = DotProduct( snormal, delta );
|
|
if (dot < 0)
|
|
return 0;
|
|
|
|
dot2 = -DotProduct (delta, lnormal);
|
|
if (dot2 <= ON_EPSILON/10)
|
|
return 0; // behind light surface
|
|
|
|
return dot * dot2;
|
|
}
|
|
|
|
|
|
// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
|
|
float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta )
|
|
{
|
|
Assert( wl->type == emit_surface );
|
|
|
|
// Cull out stuff that's too far
|
|
if (wl->radius != 0)
|
|
{
|
|
if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
|
|
return 0.0f;
|
|
}
|
|
|
|
return InvRSquared(delta);
|
|
}
|
|
|
|
|
|
void AddEmitSurfaceLights( const Vector &vStart, Vector lightBoxColor[6] )
|
|
{
|
|
int iThread = 0;
|
|
|
|
for ( int iLight=0; iLight < *pNumworldlights; iLight++ )
|
|
{
|
|
dworldlight_t *wl = &dworldlights[iLight];
|
|
|
|
// Should this light even go in the ambient cubes?
|
|
if ( !( wl->flags & DWL_FLAGS_INAMBIENTCUBE ) )
|
|
continue;
|
|
|
|
Assert( wl->type == emit_surface );
|
|
|
|
// Can this light see the point?
|
|
if ( TestLine( vStart, wl->origin, 0, iThread ) != CONTENTS_EMPTY )
|
|
continue;
|
|
|
|
// Add this light's contribution.
|
|
Vector vDelta = wl->origin - vStart;
|
|
float flDistanceScale = Engine_WorldLightDistanceFalloff( wl, vDelta );
|
|
|
|
Vector vDeltaNorm = vDelta;
|
|
VectorNormalize( vDeltaNorm );
|
|
float flAngleScale = Engine_WorldLightAngle( wl, wl->normal, vDeltaNorm, vDeltaNorm );
|
|
|
|
float ratio = flDistanceScale * flAngleScale;
|
|
if ( ratio == 0 )
|
|
continue;
|
|
|
|
for ( int i=0; i < 6; i++ )
|
|
{
|
|
float t = DotProduct( g_BoxDirections[i], vDeltaNorm );
|
|
if ( t > 0 )
|
|
{
|
|
lightBoxColor[i] += wl->intensity * (t * ratio);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ComputeAmbientFromSphericalSamples( const Vector &vStart, Vector lightBoxColor[6] )
|
|
{
|
|
// Figure out the color that rays hit when shot out from this position.
|
|
Vector radcolor[NUMVERTEXNORMALS];
|
|
for ( int i = 0; i < NUMVERTEXNORMALS; i++ )
|
|
{
|
|
Vector vEnd = vStart + g_anorms[i] * (COORD_EXTENT * 1.74);
|
|
|
|
// Now that we've got a ray, see what surface we've hit
|
|
Vector lightStyleColors[MAX_LIGHTSTYLES];
|
|
lightStyleColors[0].Init(); // We only care about light style 0 here.
|
|
Vector colorSum;
|
|
CalcRayAmbientLighting( vStart, vEnd, lightStyleColors, colorSum );
|
|
|
|
radcolor[i] = lightStyleColors[0];
|
|
}
|
|
|
|
// accumulate samples into radiant box
|
|
for ( int j = 6; --j >= 0; )
|
|
{
|
|
float t = 0;
|
|
|
|
lightBoxColor[j].Init();
|
|
|
|
for (int i = 0; i < NUMVERTEXNORMALS; i++)
|
|
{
|
|
float c = DotProduct( g_anorms[i], g_BoxDirections[j] );
|
|
if (c > 0)
|
|
{
|
|
t += c;
|
|
lightBoxColor[j] += radcolor[i] * c;
|
|
}
|
|
}
|
|
|
|
lightBoxColor[j] *= 1/t;
|
|
}
|
|
|
|
// Now add direct light from the emit_surface lights. These go in the ambient cube because
|
|
// there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
|
|
AddEmitSurfaceLights( vStart, lightBoxColor );
|
|
}
|
|
|
|
|
|
bool IsLeafAmbientSurfaceLight( dworldlight_t *wl )
|
|
{
|
|
static const float g_flWorldLightMinEmitSurface = 0.005f;
|
|
static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared( Vector( 0, 0, 512 ) ) );
|
|
|
|
if ( wl->type != emit_surface )
|
|
return false;
|
|
|
|
if ( wl->style != 0 )
|
|
return false;
|
|
|
|
float intensity = MAX( wl->intensity[0], wl->intensity[1] );
|
|
intensity = MAX( intensity, wl->intensity[2] );
|
|
|
|
return (intensity * g_flWorldLightMinEmitSurfaceDistanceRatio) < g_flWorldLightMinEmitSurface;
|
|
}
|
|
|
|
|
|
// There are problems with water lighting that make bobbing objects look bad. the same problems
|
|
// show up for other poorly sampled areas of the world, but floating in water makes it look the
|
|
// worst because of oscillating back and forth and because water always acts as a bsp splitting
|
|
// plane. The hack turned on if this #define is set makes all per leaf ambient for water exactly
|
|
// match the per leaf above the water, and does solve the specific problems seen with d1_canals_11.
|
|
// It was decided to punt it for hl2 because of effecting the underwater lighting of every object
|
|
// in all levels at the last minute. This solution isn't good anyway - what is needed is either
|
|
// some sort of adaptive sampling strategy, or distance-based interpolation or some other sort of
|
|
// smoothly spacially varying lighting.
|
|
#define MAKE_LIGHT_BELOW_WATER_MATCH_LIGHT_ABOVE_WATER 0
|
|
|
|
void ComputePerLeafAmbientLighting()
|
|
{
|
|
// Figure out which lights should go in the per-leaf ambient cubes.
|
|
int nInAmbientCube = 0;
|
|
int nSurfaceLights = 0;
|
|
for ( int i=0; i < *pNumworldlights; i++ )
|
|
{
|
|
dworldlight_t *wl = &dworldlights[i];
|
|
|
|
if ( IsLeafAmbientSurfaceLight( wl ) )
|
|
wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
|
|
else
|
|
wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
|
|
|
|
if ( wl->type == emit_surface )
|
|
++nSurfaceLights;
|
|
|
|
if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
|
|
++nInAmbientCube;
|
|
}
|
|
|
|
Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );
|
|
|
|
g_pLeafAmbientLighting->SetCount( numleafs );
|
|
|
|
StartPacifier( "ComputePerLeafAmbientLighting: " );
|
|
|
|
for ( int leafID = 0; leafID < numleafs; leafID++ )
|
|
{
|
|
dleaf_t *pLeaf = &dleafs[leafID];
|
|
|
|
Vector cube[6];
|
|
Vector center = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
|
|
#if MAKE_LIGHT_BELOW_WATER_MATCH_LIGHT_ABOVE_WATER
|
|
if (pLeaf->contents & CONTENTS_WATER)
|
|
{
|
|
center.z=pLeaf->maxs[2]+1;
|
|
int above_leaf=PointLeafnum( center);
|
|
dleaf_t *pLeaf = &dleafs[above_leaf];
|
|
|
|
center = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
|
|
}
|
|
#endif
|
|
|
|
ComputeAmbientFromSphericalSamples( center, cube );
|
|
for ( int i = 0; i < 6; i++ )
|
|
{
|
|
VectorToColorRGBExp32( cube[i], (*g_pLeafAmbientLighting)[leafID].m_Color[i] );
|
|
}
|
|
|
|
UpdatePacifier( (float)leafID / numleafs );
|
|
}
|
|
|
|
EndPacifier( true );
|
|
}
|
|
|
|
|
|
|