2020-04-22 12:56:21 -04:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Revision: $
// $NoKeywords: $
//
// This file contains code to allow us to associate client data with bsp leaves.
//
//=============================================================================//
# include "vrad.h"
2023-05-09 19:57:22 +03:00
# include "bsplib.h"
# include "gamebspfile.h"
# include "tier1/utlbuffer.h"
# include "tier1/utlvector.h"
# include "cmodel.h"
2020-04-22 12:56:21 -04:00
# include "studio.h"
# include "pacifier.h"
# include "vraddetailprops.h"
# include "mathlib/halton.h"
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
# include "messbuf.h"
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
# include "byteswap.h"
2023-05-13 21:11:15 +03:00
extern float SoftenCosineTerm ( float flDot ) ;
extern float CalculateAmbientOcclusion ( Vector * pPosition , Vector * pNormal ) ;
2020-04-22 12:56:21 -04:00
bool LoadStudioModel ( char const * pModelName , CUtlBuffer & buf ) ;
//-----------------------------------------------------------------------------
// Purpose: Writes a glview text file containing the collision surface in question
// Input : *pCollide -
// *pFilename -
//-----------------------------------------------------------------------------
void DumpRayToGlView ( Ray_t const & ray , float dist , Vector * pColor , const char * pFilename )
{
Vector dir = ray . m_Delta ;
float len = VectorNormalize ( dir ) ;
if ( len < 1e-3 )
return ;
Vector up ( 0 , 0 , 1 ) ;
Vector crossDir ;
if ( fabs ( DotProduct ( up , dir ) ) - 1.0f < - 1e-3 )
{
CrossProduct ( dir , up , crossDir ) ;
VectorNormalize ( crossDir ) ;
}
else
{
up . Init ( 0 , 1 , 0 ) ;
CrossProduct ( dir , up , crossDir ) ;
VectorNormalize ( crossDir ) ;
}
Vector end ;
Vector start1 , start2 ;
VectorMA ( ray . m_Start , dist , ray . m_Delta , end ) ;
VectorMA ( ray . m_Start , - 2 , crossDir , start1 ) ;
VectorMA ( ray . m_Start , 2 , crossDir , start2 ) ;
FileHandle_t fp = g_pFileSystem - > Open ( pFilename , " a " ) ;
int vert = 0 ;
CmdLib_FPrintf ( fp , " 3 \n " ) ;
CmdLib_FPrintf ( fp , " %6.3f %6.3f %6.3f %.2f %.2f %.2f \n " , start1 . x , start1 . y , start1 . z ,
pColor - > x , pColor - > y , pColor - > z ) ;
vert + + ;
CmdLib_FPrintf ( fp , " %6.3f %6.3f %6.3f %.2f %.2f %.2f \n " , start2 . x , start2 . y , start2 . z ,
pColor - > x , pColor - > y , pColor - > z ) ;
vert + + ;
CmdLib_FPrintf ( fp , " %6.3f %6.3f %6.3f %.2f %.2f %.2f \n " , end . x , end . y , end . z ,
pColor - > x , pColor - > y , pColor - > z ) ;
vert + + ;
g_pFileSystem - > Close ( fp ) ;
}
//-----------------------------------------------------------------------------
// This puppy is used to construct the game lumps
//-----------------------------------------------------------------------------
static CUtlVector < DetailPropLightstylesLump_t > s_DetailPropLightStyleLumpLDR ;
static CUtlVector < DetailPropLightstylesLump_t > s_DetailPropLightStyleLumpHDR ;
static CUtlVector < DetailPropLightstylesLump_t > * s_pDetailPropLightStyleLump = & s_DetailPropLightStyleLumpLDR ;
//-----------------------------------------------------------------------------
// An amount to add to each model to get to the model center
//-----------------------------------------------------------------------------
CUtlVector < Vector > g_ModelCenterOffset ;
CUtlVector < Vector > g_SpriteCenterOffset ;
void VRadDetailProps_SetHDRMode ( bool bHDR )
{
if ( bHDR )
{
s_pDetailPropLightStyleLump = & s_DetailPropLightStyleLumpHDR ;
}
else
{
s_pDetailPropLightStyleLump = & s_DetailPropLightStyleLumpLDR ;
}
}
//-----------------------------------------------------------------------------
// Finds ambient sky lights
//-----------------------------------------------------------------------------
static directlight_t * FindAmbientSkyLight ( )
{
static directlight_t * s_pCachedSkylight = NULL ;
// Don't keep searching for the same light.
if ( ! s_pCachedSkylight )
{
// find any ambient lights
directlight_t * dl ;
for ( dl = activelights ; dl ! = 0 ; dl = dl - > next )
{
if ( dl - > light . type = = emit_skyambient )
{
s_pCachedSkylight = dl ;
break ;
}
}
}
return s_pCachedSkylight ;
}
//-----------------------------------------------------------------------------
// Compute world center of a prop
//-----------------------------------------------------------------------------
static void ComputeWorldCenter ( DetailObjectLump_t & prop , Vector & center , Vector & normal )
{
// Transform the offset into world space
Vector forward , right ;
AngleVectors ( prop . m_Angles , & forward , & right , & normal ) ;
VectorCopy ( prop . m_Origin , center ) ;
// FIXME: Take orientation into account?
switch ( prop . m_Type )
{
case DETAIL_PROP_TYPE_MODEL :
VectorMA ( center , g_ModelCenterOffset [ prop . m_DetailModel ] . x , forward , center ) ;
VectorMA ( center , - g_ModelCenterOffset [ prop . m_DetailModel ] . y , right , center ) ;
VectorMA ( center , g_ModelCenterOffset [ prop . m_DetailModel ] . z , normal , center ) ;
break ;
case DETAIL_PROP_TYPE_SPRITE :
Vector vecOffset ;
VectorMultiply ( g_SpriteCenterOffset [ prop . m_DetailModel ] , prop . m_flScale , vecOffset ) ;
VectorMA ( center , vecOffset . x , forward , center ) ;
VectorMA ( center , - vecOffset . y , right , center ) ;
VectorMA ( center , vecOffset . z , normal , center ) ;
break ;
}
}
//-----------------------------------------------------------------------------
// Computes max direct lighting for a single detal prop
//-----------------------------------------------------------------------------
static void ComputeMaxDirectLighting ( DetailObjectLump_t & prop , Vector * maxcolor , int iThread )
{
// The max direct lighting must be along the direction to one
// of the static lights....
Vector origin , normal ;
ComputeWorldCenter ( prop , origin , normal ) ;
if ( ! origin . IsValid ( ) | | ! normal . IsValid ( ) )
{
static bool s_Warned = false ;
if ( ! s_Warned )
{
Warning ( " WARNING: Bogus detail props encountered! \n " ) ;
s_Warned = true ;
}
// fill with debug color
for ( int i = 0 ; i < MAX_LIGHTSTYLES ; + + i )
{
maxcolor [ i ] . Init ( 1 , 0 , 0 ) ;
}
return ;
}
int cluster = ClusterFromPoint ( origin ) ;
Vector delta ;
CUtlVector < directlight_t * > lights ;
CUtlVector < Vector > directions ;
directlight_t * dl ;
for ( dl = activelights ; dl ! = 0 ; dl = dl - > next )
{
// skyambient doesn't affect dlights..
if ( dl - > light . type = = emit_skyambient )
continue ;
// is this lights cluster visible?
if ( PVSCheck ( dl - > pvs , cluster ) )
{
lights . AddToTail ( dl ) ;
VectorSubtract ( dl - > light . origin , origin , delta ) ;
VectorNormalize ( delta ) ;
directions . AddToTail ( delta ) ;
}
}
// Find the max illumination
int i ;
for ( i = 0 ; i < MAX_LIGHTSTYLES ; + + i )
{
maxcolor [ i ] . Init ( 0 , 0 , 0 ) ;
}
// NOTE: See version 10 for a method where we choose a normal based on whichever
// one produces the maximum possible illumination. This appeared to work better on
// e3_town, so I'm trying it now; hopefully it'll be good for all cases.
int j ;
for ( j = 0 ; j < lights . Count ( ) ; + + j )
{
dl = lights [ j ] ;
SSE_sampleLightOutput_t out ;
FourVectors origin4 ;
FourVectors normal4 ;
origin4 . DuplicateVector ( origin ) ;
normal4 . DuplicateVector ( normal ) ;
2023-05-13 21:11:15 +03:00
GatherSampleLightSSE ( out , dl , - 1 , origin4 , & normal4 , 1 , iThread , GATHERLFLAGS_STATICPROP ) ;
2023-05-09 19:57:22 +03:00
VectorMA ( maxcolor [ dl - > light . style ] , out . m_flFalloff [ 0 ] * out . m_flDot [ 0 ] [ 0 ] , dl - > light . intensity , maxcolor [ dl - > light . style ] ) ;
2020-04-22 12:56:21 -04:00
}
}
//-----------------------------------------------------------------------------
// Computes the ambient term from a particular surface
//-----------------------------------------------------------------------------
static void ComputeAmbientFromSurface ( dface_t * pFace , directlight_t * pSkylight ,
Vector & radcolor )
{
texinfo_t * pTex = & texinfo [ pFace - > texinfo ] ;
if ( pTex )
{
// If we hit the sky, use the sky ambient
if ( pTex - > flags & SURF_SKY )
{
if ( pSkylight )
{
// add in sky ambient
VectorDivide ( pSkylight - > light . intensity , 255.0f , radcolor ) ;
}
}
else
{
VectorMultiply ( radcolor , dtexdata [ pTex - > texdata ] . reflectivity , radcolor ) ;
}
}
}
//-----------------------------------------------------------------------------
// Computes the lightmap color at a particular point
//-----------------------------------------------------------------------------
static void ComputeLightmapColorFromAverage ( dface_t * pFace , directlight_t * pSkylight , float scale , Vector pColor [ MAX_LIGHTSTYLES ] )
{
texinfo_t * pTex = & texinfo [ pFace - > texinfo ] ;
if ( pTex - > flags & SURF_SKY )
{
if ( pSkylight )
{
// add in sky ambient
Vector amb = pSkylight - > light . intensity / 255.0f ;
pColor [ 0 ] + = amb * scale ;
}
return ;
}
for ( int maps = 0 ; maps < MAXLIGHTMAPS & & pFace - > styles [ maps ] ! = 255 ; + + maps )
{
ColorRGBExp32 * pAvgColor = dface_AvgLightColor ( pFace , maps ) ;
// this code expects values from [0..1] not [0..255]
Vector color ;
color [ 0 ] = TexLightToLinear ( pAvgColor - > r , pAvgColor - > exponent ) ;
color [ 1 ] = TexLightToLinear ( pAvgColor - > g , pAvgColor - > exponent ) ;
color [ 2 ] = TexLightToLinear ( pAvgColor - > b , pAvgColor - > exponent ) ;
ComputeAmbientFromSurface ( pFace , pSkylight , color ) ;
int style = pFace - > styles [ maps ] ;
pColor [ style ] + = color * scale ;
}
}
//-----------------------------------------------------------------------------
// Returns true if the surface has bumped lightmaps
//-----------------------------------------------------------------------------
static bool SurfHasBumpedLightmaps ( dface_t * pSurf )
{
bool hasBumpmap = false ;
if ( ( texinfo [ pSurf - > texinfo ] . flags & SURF_BUMPLIGHT ) & &
( ! ( texinfo [ pSurf - > texinfo ] . flags & SURF_NOLIGHT ) ) )
{
hasBumpmap = true ;
}
return hasBumpmap ;
}
//-----------------------------------------------------------------------------
// Computes the lightmap color at a particular point
//-----------------------------------------------------------------------------
static void ComputeLightmapColorPointSample ( dface_t * pFace , directlight_t * pSkylight , Vector2D const & luv , float scale , Vector pColor [ MAX_LIGHTSTYLES ] )
{
// face unaffected by light
if ( pFace - > lightofs = = - 1 )
return ;
int smax = ( pFace - > m_LightmapTextureSizeInLuxels [ 0 ] ) + 1 ;
int tmax = ( pFace - > m_LightmapTextureSizeInLuxels [ 1 ] ) + 1 ;
// luv is in the space of the accumulated lightmap page; we need to convert
// it to be in the space of the surface
int ds = clamp ( ( int ) luv . x , 0 , smax - 1 ) ;
int dt = clamp ( ( int ) luv . y , 0 , tmax - 1 ) ;
int offset = smax * tmax ;
if ( SurfHasBumpedLightmaps ( pFace ) )
offset * = ( NUM_BUMP_VECTS + 1 ) ;
ColorRGBExp32 * pLightmap = ( ColorRGBExp32 * ) & pdlightdata - > Base ( ) [ pFace - > lightofs ] ;
pLightmap + = dt * smax + ds ;
for ( int maps = 0 ; maps < MAXLIGHTMAPS & & pFace - > styles [ maps ] ! = 255 ; + + maps )
{
int style = pFace - > styles [ maps ] ;
Vector color ;
color [ 0 ] = TexLightToLinear ( pLightmap - > r , pLightmap - > exponent ) ;
color [ 1 ] = TexLightToLinear ( pLightmap - > g , pLightmap - > exponent ) ;
color [ 2 ] = TexLightToLinear ( pLightmap - > b , pLightmap - > exponent ) ;
ComputeAmbientFromSurface ( pFace , pSkylight , color ) ;
pColor [ style ] + = color * scale ;
pLightmap + = offset ;
}
}
//-----------------------------------------------------------------------------
// Tests a particular node
//-----------------------------------------------------------------------------
class CLightSurface : public IBSPNodeEnumerator
{
public :
CLightSurface ( int iThread ) : m_pSurface ( 0 ) , m_HitFrac ( 1.0f ) , m_bHasLuxel ( false ) , m_iThread ( iThread ) { }
// call back with a node and a context
2022-02-23 19:50:30 +08:00
bool EnumerateNode ( int node , Ray_t const & ray , float f , intp context )
2020-04-22 12:56:21 -04:00
{
dface_t * pSkySurface = 0 ;
// Compute the actual point
Vector pt ;
VectorMA ( ray . m_Start , f , ray . m_Delta , pt ) ;
dnode_t * pNode = & dnodes [ node ] ;
dface_t * pFace = & g_pFaces [ pNode - > firstface ] ;
for ( int i = 0 ; i < pNode - > numfaces ; + + i , + + pFace )
{
// Don't take into account faces that are int a leaf
if ( ! pFace - > onNode )
continue ;
// Don't test displacement faces
if ( pFace - > dispinfo ! = - 1 )
continue ;
texinfo_t * pTex = & texinfo [ pFace - > texinfo ] ;
// Don't immediately return when we hit sky;
// we may actually hit another surface
if ( pTex - > flags & SURF_SKY )
{
if ( TestPointAgainstSkySurface ( pt , pFace ) )
{
pSkySurface = pFace ;
}
continue ;
}
if ( TestPointAgainstSurface ( pt , pFace , pTex ) )
{
m_HitFrac = f ;
m_pSurface = pFace ;
m_bHasLuxel = true ;
return false ;
}
}
// if we hit a sky surface, return it
m_pSurface = pSkySurface ;
return ( m_pSurface = = 0 ) ;
}
// call back with a leaf and a context
2022-02-23 19:50:30 +08:00
virtual bool EnumerateLeaf ( int leaf , Ray_t const & ray , float start , float end , intp context )
2020-04-22 12:56:21 -04:00
{
bool hit = false ;
dleaf_t * pLeaf = & dleafs [ leaf ] ;
for ( int i = 0 ; i < pLeaf - > numleaffaces ; + + i )
{
Assert ( pLeaf - > firstleafface + i < numleaffaces ) ;
Assert ( dleaffaces [ pLeaf - > firstleafface + i ] < numfaces ) ;
dface_t * pFace = & g_pFaces [ dleaffaces [ pLeaf - > firstleafface + i ] ] ;
// Don't test displacement faces; we need to check another list
if ( pFace - > dispinfo ! = - 1 )
continue ;
// Don't take into account faces that are on a node
if ( pFace - > onNode )
continue ;
// Find intersection point against detail brushes
texinfo_t * pTex = & texinfo [ pFace - > texinfo ] ;
dplane_t * pPlane = & dplanes [ pFace - > planenum ] ;
// Backface cull...
if ( DotProduct ( pPlane - > normal , ray . m_Delta ) > 0 )
continue ;
float startDotN = DotProduct ( ray . m_Start , pPlane - > normal ) ;
float deltaDotN = DotProduct ( ray . m_Delta , pPlane - > normal ) ;
float front = startDotN + start * deltaDotN - pPlane - > dist ;
float back = startDotN + end * deltaDotN - pPlane - > dist ;
int side = front < 0 ;
// Blow it off if it doesn't split the plane...
if ( ( back < 0 ) = = side )
continue ;
// Don't test a surface that is farther away from the closest found intersection
float f = front / ( front - back ) ;
float mid = start * ( 1.0f - f ) + end * f ;
if ( mid > = m_HitFrac )
continue ;
Vector pt ;
VectorMA ( ray . m_Start , mid , ray . m_Delta , pt ) ;
if ( TestPointAgainstSurface ( pt , pFace , pTex ) )
{
m_HitFrac = mid ;
m_pSurface = pFace ;
hit = true ;
m_bHasLuxel = true ;
}
}
// Now try to clip against all displacements in the leaf
float dist ;
Vector2D luxelCoord ;
dface_t * pDispFace ;
StaticDispMgr ( ) - > ClipRayToDispInLeaf ( s_DispTested [ m_iThread ] , ray , leaf , dist , pDispFace , luxelCoord ) ;
if ( dist < m_HitFrac )
{
m_HitFrac = dist ;
m_pSurface = pDispFace ;
Vector2DCopy ( luxelCoord , m_LuxelCoord ) ;
hit = true ;
m_bHasLuxel = true ;
}
return ! hit ;
}
bool FindIntersection ( Ray_t const & ray )
{
StaticDispMgr ( ) - > StartRayTest ( s_DispTested [ m_iThread ] ) ;
return ! EnumerateNodesAlongRay ( ray , this , 0 ) ;
}
private :
bool TestPointAgainstSurface ( Vector const & pt , dface_t * pFace , texinfo_t * pTex )
{
// no lightmaps on this surface? punt...
// FIXME: should be water surface?
if ( pTex - > flags & SURF_NOLIGHT )
return false ;
// See where in lightmap space our intersection point is
float s , t ;
s = DotProduct ( pt . Base ( ) , pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] ) +
pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 3 ] ;
t = DotProduct ( pt . Base ( ) , pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] ) +
pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 3 ] ;
// Not in the bounds of our lightmap? punt...
if ( s < pFace - > m_LightmapTextureMinsInLuxels [ 0 ] | | t < pFace - > m_LightmapTextureMinsInLuxels [ 1 ] )
return false ;
// assuming a square lightmap (FIXME: which ain't always the case),
// lets see if it lies in that rectangle. If not, punt...
float ds = s - pFace - > m_LightmapTextureMinsInLuxels [ 0 ] ;
float dt = t - pFace - > m_LightmapTextureMinsInLuxels [ 1 ] ;
if ( ds > pFace - > m_LightmapTextureSizeInLuxels [ 0 ] | | dt > pFace - > m_LightmapTextureSizeInLuxels [ 1 ] )
return false ;
m_LuxelCoord . x = ds ;
m_LuxelCoord . y = dt ;
return true ;
}
bool TestPointAgainstSkySurface ( Vector const & pt , dface_t * pFace )
{
// Create sky face winding.
2023-05-09 19:57:22 +03:00
Vector vec ( 0.0f , 0.0f , 0.0f ) ;
winding_t * pWinding = WindingFromFace ( pFace , vec ) ;
2020-04-22 12:56:21 -04:00
// Test point in winding. (Since it is at the node, it is in the plane.)
bool bRet = PointInWinding ( pt , pWinding ) ;
FreeWinding ( pWinding ) ;
return bRet ;
}
public :
int m_iThread ;
dface_t * m_pSurface ;
float m_HitFrac ;
Vector2D m_LuxelCoord ;
bool m_bHasLuxel ;
} ;
bool CastRayInLeaf ( int iThread , const Vector & start , const Vector & end , int leafIndex , float * pFraction , Vector * pNormal )
{
pFraction [ 0 ] = 1.0f ;
Ray_t ray ;
ray . Init ( start , end , vec3_origin , vec3_origin ) ;
CBaseTrace trace ;
if ( TraceLeafBrushes ( leafIndex , start , end , trace ) ! = 1.0f )
{
pFraction [ 0 ] = trace . fraction ;
* pNormal = trace . plane . normal ;
}
else
{
Assert ( ! trace . startsolid & & ! trace . allsolid ) ;
}
StaticDispMgr ( ) - > StartRayTest ( s_DispTested [ iThread ] ) ;
// Now try to clip against all displacements in the leaf
float dist ;
Vector normal ;
StaticDispMgr ( ) - > ClipRayToDispInLeaf ( s_DispTested [ iThread ] , ray , leafIndex , dist , & normal ) ;
if ( dist < pFraction [ 0 ] )
{
pFraction [ 0 ] = dist ;
* pNormal = normal ;
}
return pFraction [ 0 ] ! = 1.0f ? true : false ;
}
//-----------------------------------------------------------------------------
// Computes ambient lighting along a specified ray.
// Ray represents a cone, tanTheta is the tan of the inner cone angle
//-----------------------------------------------------------------------------
void CalcRayAmbientLighting ( int iThread , const Vector & vStart , const Vector & vEnd , float tanTheta , Vector color [ MAX_LIGHTSTYLES ] )
{
Ray_t ray ;
ray . Init ( vStart , vEnd , vec3_origin , vec3_origin ) ;
directlight_t * pSkyLight = FindAmbientSkyLight ( ) ;
CLightSurface surfEnum ( iThread ) ;
if ( ! surfEnum . FindIntersection ( ray ) )
return ;
// compute the approximate radius of a circle centered around the intersection point
float dist = ray . m_Delta . Length ( ) * tanTheta * surfEnum . m_HitFrac ;
// until 20" we use the point sample, then blend in the average until we're covering 40"
// This is attempting to model the ray as a cone - in the ideal case we'd simply sample all
// luxels in the intersection of the cone with the surface. Since we don't have surface
// neighbor information computed we'll just approximate that sampling with a blend between
// a point sample and the face average.
// This yields results that are similar in that aliasing is reduced at distance while
// point samples provide accuracy for intersections with near geometry
float scaleAvg = RemapValClamped ( dist , 20 , 40 , 0.0f , 1.0f ) ;
if ( ! surfEnum . m_bHasLuxel )
{
// don't have luxel UV, so just use average sample
scaleAvg = 1.0 ;
}
float scaleSample = 1.0f - scaleAvg ;
if ( scaleAvg ! = 0 )
{
ComputeLightmapColorFromAverage ( surfEnum . m_pSurface , pSkyLight , scaleAvg , color ) ;
}
if ( scaleSample ! = 0 )
{
ComputeLightmapColorPointSample ( surfEnum . m_pSurface , pSkyLight , surfEnum . m_LuxelCoord , scaleSample , color ) ;
}
}
//-----------------------------------------------------------------------------
// Compute ambient lighting component at specified position.
//-----------------------------------------------------------------------------
static void ComputeAmbientLightingAtPoint ( int iThread , const Vector & origin , Vector radcolor [ NUMVERTEXNORMALS ] , Vector color [ MAX_LIGHTSTYLES ] )
{
// NOTE: I'm not dealing with shadow-casting static props here
// This is for speed, although we can add it if it turns out to
// be important
// sample world by casting N rays distributed across a sphere
Vector upend ;
int j ;
for ( j = 0 ; j < MAX_LIGHTSTYLES ; + + j )
{
color [ j ] . Init ( 0 , 0 , 0 ) ;
}
float tanTheta = tan ( VERTEXNORMAL_CONE_INNER_ANGLE ) ;
for ( int i = 0 ; i < NUMVERTEXNORMALS ; i + + )
{
VectorMA ( origin , COORD_EXTENT * 1.74 , g_anorms [ i ] , upend ) ;
// Now that we've got a ray, see what surface we've hit
CalcRayAmbientLighting ( iThread , origin , upend , tanTheta , color ) ;
// DumpRayToGlView( ray, surfEnum.m_HitFrac, &color[0], "test.out" );
}
for ( j = 0 ; j < MAX_LIGHTSTYLES ; + + j )
{
VectorMultiply ( color [ j ] , 255.0f / ( float ) NUMVERTEXNORMALS , color [ j ] ) ;
}
}
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
//
// Trace a ray from position. in the specified direction to determine a positive
// hit for indirect lighting.
//
// Fire ray out from start, with end as start + direction*MAX_TRACE_LENGTH
// If hit then fire ray back to start to see if it hits a back facing surface that would natually block the incoming light ray
// If still okay then test explicitly against light blockers, test only in the hit to start direction
// Update surfEnum and return true if a valid intersection for indirect light.
//
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
bool TraceIndirectLightingSample ( Vector & position , Vector & direction , CLightSurface & surfEnum , int iThread , bool force_fast )
2020-04-22 12:56:21 -04:00
{
Ray_t ray ;
2023-05-13 21:11:15 +03:00
// trace to determine surface
Vector vEnd , vStart ;
VectorScale ( direction , MAX_TRACE_LENGTH , vEnd ) ;
VectorAdd ( position , vEnd , vEnd ) ;
if ( force_fast )
{
vStart = position ;
}
else
{
// offset ray start position to compensate for ray leakage due to coincident surfaces (we are seeing some ray tests leak in some situations - e.g. prop vertex lies on ground plane)
VectorScale ( direction , - EQUAL_EPSILON , vStart ) ;
VectorAdd ( position , vStart , vStart ) ;
}
ray . Init ( vStart , vEnd , vec3_origin , vec3_origin ) ;
if ( ! surfEnum . FindIntersection ( ray ) )
return false ;
// Now test explicitly against light blockers (surfaces don't exist in the bsp nodes we're checking here, and this feels a safer change than updating indirect lighting for static props to use the slower rte path for all rays)
// test from hitfrac back to start only
VectorScale ( direction , MAX_TRACE_LENGTH * surfEnum . m_HitFrac , vEnd ) ;
VectorAdd ( position , vEnd , vEnd ) ;
FourVectors rayStart , rayEnd , rayDirection ;
fltx4 fractionVisible = Four_Ones ;
rayStart . DuplicateVector ( vStart ) ;
rayEnd . DuplicateVector ( vEnd ) ;
// rayDirection.DuplicateVector( direction );
// TestLine_LightBlockers( rayStart, rayEnd, &fractionVisible );
rayDirection . DuplicateVector ( - direction ) ;
TestLine_LightBlockers ( rayEnd , rayStart , & fractionVisible ) ;
if ( fractionVisible [ 0 ] < 1.0f )
{
// ray hit blocker
return false ;
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
return true ;
}
//-----------------------------------------------------------------------------
// Trace hemispherical rays from a vertex, accumulating indirect
// sources at each ray termination.
//
// force_fast = false currently implies 'new/improved' static prop lighting is to be used.
//-----------------------------------------------------------------------------
void ComputeIndirectLightingAtPoint ( Vector & position , Vector & normal , Vector & outColor ,
int iThread , bool force_fast , bool bIgnoreNormals , int nStaticPropToSkip )
{
outColor . Zero ( ) ;
2020-04-22 12:56:21 -04:00
int nSamples = NUMVERTEXNORMALS ;
2023-05-13 21:11:15 +03:00
if ( do_fast | | force_fast )
nSamples / = 4 ;
else
nSamples * = g_flStaticPropSampleScale ;
2020-04-22 12:56:21 -04:00
float totalDot = 0 ;
DirectionalSampler_t sampler ;
for ( int j = 0 ; j < nSamples ; j + + )
{
Vector samplingNormal = sampler . NextValue ( ) ;
float dot ;
if ( bIgnoreNormals )
dot = ( 0.7071 / 2 ) ;
else
dot = DotProduct ( normal , samplingNormal ) ;
if ( dot < = EQUAL_EPSILON )
{
// reject angles behind our plane
continue ;
}
totalDot + = dot ;
2023-05-13 21:11:15 +03:00
// trace static prop indirect
Vector staticPropIndirectColor ( 0.0f , 0.0f , 0.0f ) ;
float flStaticPropHitDist = FLT_MAX ;
if ( g_bStaticPropBounce )
{
FourRays myrays ;
myrays . origin . DuplicateVector ( position ) ;
myrays . direction . DuplicateVector ( samplingNormal ) ;
RayTracingResult rt_result ;
g_RtEnv_RadiosityPatches . Trace4Rays ( myrays , ReplicateX4 ( 10.0f ) , ReplicateX4 ( MAX_TRACE_LENGTH ) , & rt_result ) ;
if ( rt_result . HitIds [ 0 ] ! = - 1 )
{
const TriIntersectData_t & intersectData = g_RtEnv_RadiosityPatches . OptimizedTriangleList [ rt_result . HitIds [ 0 ] ] . m_Data . m_IntersectData ;
int nId = intersectData . m_nTriangleID ;
if ( nId & TRACE_ID_PATCH )
{
int nPatchId = nId & ~ TRACE_ID_PATCH ;
CPatch & patch = g_Patches [ nPatchId ] ;
if ( patch . staticPropIdx ! = nStaticPropToSkip )
{
staticPropIndirectColor = dot * ( patch . totallight . light [ 0 ] + patch . directlight ) * patch . reflectivity ;
flStaticPropHitDist = SubFloat ( rt_result . HitDistance , 0 ) ;
}
}
}
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// important to put the constructor here to init m_hitfrac, etc
CLightSurface surfEnum ( iThread ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// trace to determine surface
if ( ! TraceIndirectLightingSample ( position , samplingNormal , surfEnum , iThread , force_fast ) | |
flStaticPropHitDist < surfEnum . m_HitFrac * MAX_TRACE_LENGTH )
{
VectorAdd ( outColor , staticPropIndirectColor , outColor ) ; // we may have hit a static prop patch
2020-04-22 12:56:21 -04:00
continue ;
2023-05-13 21:11:15 +03:00
}
2020-04-22 12:56:21 -04:00
// get color from surface lightmap
texinfo_t * pTex = & texinfo [ surfEnum . m_pSurface - > texinfo ] ;
if ( ! pTex | | pTex - > flags & SURF_SKY )
{
// ignore contribution from sky
// sky ambient already accounted for during direct pass
continue ;
}
if ( surfEnum . m_pSurface - > styles [ 0 ] = = 255 | | surfEnum . m_pSurface - > lightofs < 0 )
{
// no light affects this face
continue ;
}
Vector lightmapColor ;
if ( ! surfEnum . m_bHasLuxel )
{
ColorRGBExp32 * pAvgLightmapColor = dface_AvgLightColor ( surfEnum . m_pSurface , 0 ) ;
ColorRGBExp32ToVector ( * pAvgLightmapColor , lightmapColor ) ;
}
else
{
// get color from displacement
int smax = ( surfEnum . m_pSurface - > m_LightmapTextureSizeInLuxels [ 0 ] ) + 1 ;
int tmax = ( surfEnum . m_pSurface - > m_LightmapTextureSizeInLuxels [ 1 ] ) + 1 ;
// luxelcoord is in the space of the accumulated lightmap page; we need to convert
// it to be in the space of the surface
int ds = clamp ( ( int ) surfEnum . m_LuxelCoord . x , 0 , smax - 1 ) ;
int dt = clamp ( ( int ) surfEnum . m_LuxelCoord . y , 0 , tmax - 1 ) ;
ColorRGBExp32 * pLightmap = ( ColorRGBExp32 * ) & ( * pdlightdata ) [ surfEnum . m_pSurface - > lightofs ] ;
pLightmap + = dt * smax + ds ;
ColorRGBExp32ToVector ( * pLightmap , lightmapColor ) ;
}
2023-05-13 21:11:15 +03:00
if ( force_fast )
{
VectorMultiply ( lightmapColor , dtexdata [ pTex - > texdata ] . reflectivity , lightmapColor ) ;
}
else
{
// Include dot falloff on accumulating irradiance here
// have tried using inv sqr falloff from TF2 changes to vrad (CL#2394791 & 2395471), but the result is very sensitive to the scale factor that is used (too dark or too bright otherwise)
// this seems to give the most natural looking result (static props matching brushes)
VectorMultiply ( lightmapColor , dot * dtexdata [ pTex - > texdata ] . reflectivity , lightmapColor ) ;
}
2020-04-22 12:56:21 -04:00
VectorAdd ( outColor , lightmapColor , outColor ) ;
}
if ( totalDot )
{
2023-05-13 21:11:15 +03:00
VectorScale ( outColor , 1.0f / totalDot , outColor ) ;
}
}
void ComputeIndirectLightingAtPoint ( Vector & position , Vector * normals , Vector * outColors , int numNormals ,
int iThread , bool force_fast , bool bIgnoreNormals , int nStaticPropToSkip )
{
const Vector vZero ( 0.0f , 0.0f , 0.0f ) ;
if ( numNormals ! = ( NUM_BUMP_VECTS + 1 ) )
{
for ( int k = 0 ; k < numNormals ; + + k )
{
ComputeIndirectLightingAtPoint ( position , normals [ k ] , outColors [ k ] , iThread , force_fast , bIgnoreNormals , nStaticPropToSkip ) ;
}
return ;
}
// optimize/unroll for num_bump_vects = 3
outColors [ 0 ] . Zero ( ) ;
outColors [ 1 ] . Zero ( ) ;
outColors [ 2 ] . Zero ( ) ;
outColors [ 3 ] . Zero ( ) ;
int nSamples = NUMVERTEXNORMALS ;
if ( do_fast | | force_fast )
nSamples / = 4 ;
else
nSamples * = g_flStaticPropSampleScale ;
float totalDot [ 4 ] = { 0.0f , 0.0f , 0.0f , 0.0f } ;
DirectionalSampler_t sampler ;
for ( int j = 0 ; j < nSamples ; j + + )
{
Vector samplingNormal = sampler . NextValue ( ) ;
float dot [ 4 ] ;
if ( bIgnoreNormals )
{
dot [ 0 ] = dot [ 1 ] = dot [ 2 ] = dot [ 3 ] = ( 0.7071 / 2 ) ;
}
else
{
samplingNormal . NormalizeInPlace ( ) ;
dot [ 0 ] = DotProduct ( normals [ 0 ] , samplingNormal ) ;
dot [ 1 ] = DotProduct ( normals [ 1 ] , samplingNormal ) ;
dot [ 2 ] = DotProduct ( normals [ 2 ] , samplingNormal ) ;
dot [ 3 ] = DotProduct ( normals [ 3 ] , samplingNormal ) ;
}
bool bDoRayTrace = false ;
bool bIncLighting [ 4 ] = { false , false , false , false } ;
if ( dot [ 0 ] > EQUAL_EPSILON )
{
dot [ 0 ] = SoftenCosineTerm ( dot [ 0 ] ) ;
totalDot [ 0 ] + = dot [ 0 ] ;
bDoRayTrace = true ;
bIncLighting [ 0 ] = true ;
}
else
{
dot [ 0 ] = 0.0f ;
}
if ( dot [ 1 ] > EQUAL_EPSILON )
{
dot [ 1 ] = SoftenCosineTerm ( dot [ 1 ] ) ;
totalDot [ 1 ] + = dot [ 1 ] ;
bDoRayTrace = true ;
bIncLighting [ 1 ] = true ;
}
else
{
dot [ 1 ] = 0.0f ;
}
if ( dot [ 2 ] > EQUAL_EPSILON )
{
dot [ 2 ] = SoftenCosineTerm ( dot [ 2 ] ) ;
totalDot [ 2 ] + = dot [ 2 ] ;
bDoRayTrace = true ;
bIncLighting [ 2 ] = true ;
}
else
{
dot [ 2 ] = 0.0f ;
}
if ( dot [ 3 ] > EQUAL_EPSILON )
{
dot [ 3 ] = SoftenCosineTerm ( dot [ 3 ] ) ;
totalDot [ 3 ] + = dot [ 3 ] ;
bDoRayTrace = true ;
bIncLighting [ 3 ] = true ;
}
else
{
dot [ 3 ] = 0.0f ;
}
// important to skip
if ( dot [ 0 ] < = EQUAL_EPSILON )
{
continue ;
}
if ( bDoRayTrace )
{
Vector staticPropIndirectColor ( 0.0f , 0.0f , 0.0f ) ;
float flStaticPropHitDist = FLT_MAX ;
if ( g_bStaticPropBounce )
{
FourRays myrays ;
myrays . origin . DuplicateVector ( position ) ;
myrays . direction . DuplicateVector ( samplingNormal ) ;
RayTracingResult rt_result ;
g_RtEnv_RadiosityPatches . Trace4Rays ( myrays , ReplicateX4 ( 10.0f ) , ReplicateX4 ( MAX_TRACE_LENGTH ) , & rt_result ) ;
if ( rt_result . HitIds [ 0 ] ! = - 1 )
{
const TriIntersectData_t & intersectData = g_RtEnv_RadiosityPatches . OptimizedTriangleList [ rt_result . HitIds [ 0 ] ] . m_Data . m_IntersectData ;
int nId = intersectData . m_nTriangleID ;
if ( nId & TRACE_ID_PATCH )
{
int nPatchId = nId & ~ TRACE_ID_PATCH ;
CPatch & patch = g_Patches [ nPatchId ] ;
if ( patch . staticPropIdx ! = nStaticPropToSkip )
{
staticPropIndirectColor = ( patch . totallight . light [ 0 ] + patch . directlight ) * patch . reflectivity ;
flStaticPropHitDist = SubFloat ( rt_result . HitDistance , 0 ) ;
}
}
}
}
// important to put the constructor here to init m_hitfrac, etc
CLightSurface surfEnum ( iThread ) ;
// trace to determine surface
if ( ! TraceIndirectLightingSample ( position , samplingNormal , surfEnum , iThread , force_fast ) | |
flStaticPropHitDist < surfEnum . m_HitFrac * MAX_TRACE_LENGTH )
{
// The dot values are 0 if bIncLighting is false so we don't actually need to branch here.
VectorAdd ( outColors [ 0 ] , dot [ 0 ] * staticPropIndirectColor , outColors [ 0 ] ) ; // we may have hit a static prop patch
VectorAdd ( outColors [ 1 ] , dot [ 1 ] * staticPropIndirectColor , outColors [ 1 ] ) ;
VectorAdd ( outColors [ 2 ] , dot [ 2 ] * staticPropIndirectColor , outColors [ 2 ] ) ;
VectorAdd ( outColors [ 3 ] , dot [ 3 ] * staticPropIndirectColor , outColors [ 3 ] ) ;
continue ;
}
// get color from surface lightmap
texinfo_t * pTex = & texinfo [ surfEnum . m_pSurface - > texinfo ] ;
if ( ! pTex | | pTex - > flags & SURF_SKY )
{
// ignore contribution from sky
// sky ambient already accounted for during direct pass
continue ;
}
if ( surfEnum . m_pSurface - > styles [ 0 ] = = 255 | | surfEnum . m_pSurface - > lightofs < 0 )
{
// no light affects this face
continue ;
}
Vector lightmapColor ;
Vector lightmapColors [ 4 ] ;
if ( ! surfEnum . m_bHasLuxel )
{
ColorRGBExp32 * pAvgLightmapColor = dface_AvgLightColor ( surfEnum . m_pSurface , 0 ) ;
ColorRGBExp32ToVector ( * pAvgLightmapColor , lightmapColor ) ;
}
else
{
// get color from displacement
int smax = ( surfEnum . m_pSurface - > m_LightmapTextureSizeInLuxels [ 0 ] ) + 1 ;
int tmax = ( surfEnum . m_pSurface - > m_LightmapTextureSizeInLuxels [ 1 ] ) + 1 ;
// luxelcoord is in the space of the accumulated lightmap page; we need to convert
// it to be in the space of the surface
int ds = clamp ( ( int ) surfEnum . m_LuxelCoord . x , 0 , smax - 1 ) ;
int dt = clamp ( ( int ) surfEnum . m_LuxelCoord . y , 0 , tmax - 1 ) ;
ColorRGBExp32 * pLightmap = ( ColorRGBExp32 * ) & ( * pdlightdata ) [ surfEnum . m_pSurface - > lightofs ] ;
pLightmap + = dt * smax + ds ;
ColorRGBExp32ToVector ( * pLightmap , lightmapColor ) ;
}
lightmapColor . Max ( vZero ) ;
if ( force_fast )
{
VectorMultiply ( lightmapColor , dtexdata [ pTex - > texdata ] . reflectivity , lightmapColors [ 0 ] ) ;
if ( bIncLighting [ 0 ] )
{
VectorAdd ( outColors [ 0 ] , lightmapColors [ 0 ] , outColors [ 0 ] ) ;
}
if ( bIncLighting [ 1 ] )
{
VectorAdd ( outColors [ 1 ] , lightmapColors [ 0 ] , outColors [ 1 ] ) ;
}
if ( bIncLighting [ 2 ] )
{
VectorAdd ( outColors [ 2 ] , lightmapColors [ 0 ] , outColors [ 2 ] ) ;
}
if ( bIncLighting [ 3 ] )
{
VectorAdd ( outColors [ 3 ] , lightmapColors [ 0 ] , outColors [ 3 ] ) ;
}
}
else
{
// Include dot falloff on accumulating irradiance here
// have tried using inv sqr falloff from TF2 changes to vrad (CL#2394791 & 2395471), but the result is very sensitive to the scale factor that is used (too dark or too bright otherwise)
// this seems to give the most natural looking result (static props matching brushes)
if ( bIncLighting [ 0 ] )
{
VectorMultiply ( lightmapColor , dot [ 0 ] * dtexdata [ pTex - > texdata ] . reflectivity , lightmapColors [ 0 ] ) ;
VectorAdd ( outColors [ 0 ] , lightmapColors [ 0 ] , outColors [ 0 ] ) ;
}
if ( bIncLighting [ 1 ] )
{
VectorMultiply ( lightmapColor , dot [ 1 ] * dtexdata [ pTex - > texdata ] . reflectivity , lightmapColors [ 1 ] ) ;
VectorAdd ( outColors [ 1 ] , lightmapColors [ 1 ] , outColors [ 1 ] ) ;
}
if ( bIncLighting [ 2 ] )
{
VectorMultiply ( lightmapColor , dot [ 2 ] * dtexdata [ pTex - > texdata ] . reflectivity , lightmapColors [ 2 ] ) ;
VectorAdd ( outColors [ 2 ] , lightmapColors [ 2 ] , outColors [ 2 ] ) ;
}
if ( bIncLighting [ 3 ] )
{
VectorMultiply ( lightmapColor , dot [ 3 ] * dtexdata [ pTex - > texdata ] . reflectivity , lightmapColors [ 3 ] ) ;
VectorAdd ( outColors [ 3 ] , lightmapColors [ 3 ] , outColors [ 3 ] ) ;
}
}
}
}
if ( totalDot [ 0 ] )
{
VectorScale ( outColors [ 0 ] , 1.0f / totalDot [ 0 ] , outColors [ 0 ] ) ;
}
if ( totalDot [ 1 ] )
{
VectorScale ( outColors [ 1 ] , 1.0f / totalDot [ 1 ] , outColors [ 1 ] ) ;
}
if ( totalDot [ 2 ] )
{
VectorScale ( outColors [ 2 ] , 1.0f / totalDot [ 2 ] , outColors [ 2 ] ) ;
}
if ( totalDot [ 3 ] )
{
VectorScale ( outColors [ 3 ] , 1.0f / totalDot [ 3 ] , outColors [ 3 ] ) ;
2020-04-22 12:56:21 -04:00
}
}
2023-05-13 21:11:15 +03:00
2020-04-22 12:56:21 -04:00
static void ComputeAmbientLighting ( int iThread , DetailObjectLump_t & prop , Vector color [ MAX_LIGHTSTYLES ] )
{
Vector origin , normal ;
ComputeWorldCenter ( prop , origin , normal ) ;
if ( ! origin . IsValid ( ) | | ! normal . IsValid ( ) )
{
static bool s_Warned = false ;
if ( ! s_Warned )
{
Warning ( " WARNING: Bogus detail props encountered! \n " ) ;
s_Warned = true ;
}
// fill with debug color
for ( int i = 0 ; i < MAX_LIGHTSTYLES ; + + i )
{
color [ i ] . Init ( 1 , 0 , 0 ) ;
}
return ;
}
Vector radcolor [ NUMVERTEXNORMALS ] ;
ComputeAmbientLightingAtPoint ( iThread , origin , radcolor , color ) ;
}
//-----------------------------------------------------------------------------
// Computes lighting for a single detal prop
//-----------------------------------------------------------------------------
static void ComputeLighting ( DetailObjectLump_t & prop , int iThread )
{
// We're going to take the maximum of the ambient lighting and
// the strongest directional light. This works because we're assuming
// the props will have built-in faked lighting.
Vector directColor [ MAX_LIGHTSTYLES ] ;
Vector ambColor [ MAX_LIGHTSTYLES ] ;
// Get the max influence of all direct lights
ComputeMaxDirectLighting ( prop , directColor , iThread ) ;
// Get the ambient lighting + lightstyles
ComputeAmbientLighting ( iThread , prop , ambColor ) ;
// Base lighting
Vector totalColor ;
VectorAdd ( directColor [ 0 ] , ambColor [ 0 ] , totalColor ) ;
VectorToColorRGBExp32 ( totalColor , prop . m_Lighting ) ;
bool hasLightstyles = false ;
prop . m_LightStyleCount = 0 ;
// lightstyles
for ( int i = 1 ; i < MAX_LIGHTSTYLES ; + + i )
{
VectorAdd ( directColor [ i ] , ambColor [ i ] , totalColor ) ;
totalColor * = 0.5f ;
if ( ( totalColor [ 0 ] ! = 0.0f ) | | ( totalColor [ 1 ] ! = 0.0f ) | |
( totalColor [ 2 ] ! = 0.0f ) )
{
if ( ! hasLightstyles )
{
2023-05-13 21:11:15 +03:00
prop . m_LightStyles = s_pDetailPropLightStyleLump - > Count ( ) ;
2020-04-22 12:56:21 -04:00
hasLightstyles = true ;
}
int j = s_pDetailPropLightStyleLump - > AddToTail ( ) ;
VectorToColorRGBExp32 ( totalColor , ( * s_pDetailPropLightStyleLump ) [ j ] . m_Lighting ) ;
( * s_pDetailPropLightStyleLump ) [ j ] . m_Style = i ;
+ + prop . m_LightStyleCount ;
}
}
}
//-----------------------------------------------------------------------------
// Unserialization
//-----------------------------------------------------------------------------
static void UnserializeModelDict ( CUtlBuffer & buf )
{
// Get origin offset for each model...
int count = buf . GetInt ( ) ;
while ( - - count > = 0 )
{
DetailObjectDictLump_t lump ;
buf . Get ( & lump , sizeof ( DetailObjectDictLump_t ) ) ;
int i = g_ModelCenterOffset . AddToTail ( ) ;
CUtlBuffer mdlbuf ;
if ( LoadStudioModel ( lump . m_Name , mdlbuf ) )
{
studiohdr_t * pHdr = ( studiohdr_t * ) mdlbuf . Base ( ) ;
VectorAdd ( pHdr - > hull_min , pHdr - > hull_max , g_ModelCenterOffset [ i ] ) ;
g_ModelCenterOffset [ i ] * = 0.5f ;
}
else
{
g_ModelCenterOffset [ i ] . Init ( 0 , 0 , 0 ) ;
}
}
}
static void UnserializeSpriteDict ( CUtlBuffer & buf )
{
// Get origin offset for each model...
int count = buf . GetInt ( ) ;
while ( - - count > = 0 )
{
DetailSpriteDictLump_t lump ;
buf . Get ( & lump , sizeof ( DetailSpriteDictLump_t ) ) ;
// For these sprites, x goes out the front, y right, z up
int i = g_SpriteCenterOffset . AddToTail ( ) ;
g_SpriteCenterOffset [ i ] . x = 0.0f ;
g_SpriteCenterOffset [ i ] . y = lump . m_LR . x + lump . m_UL . x ;
g_SpriteCenterOffset [ i ] . z = lump . m_LR . y + lump . m_UL . y ;
g_SpriteCenterOffset [ i ] * = 0.5f ;
}
}
//-----------------------------------------------------------------------------
// Unserializes the detail props
//-----------------------------------------------------------------------------
static int UnserializeDetailProps ( DetailObjectLump_t * & pProps )
{
GameLumpHandle_t handle = g_GameLumps . GetGameLumpHandle ( GAMELUMP_DETAIL_PROPS ) ;
if ( g_GameLumps . GetGameLumpVersion ( handle ) ! = GAMELUMP_DETAIL_PROPS_VERSION )
return 0 ;
// Unserialize
CUtlBuffer buf ( g_GameLumps . GetGameLump ( handle ) , g_GameLumps . GameLumpSize ( handle ) , CUtlBuffer : : READ_ONLY ) ;
UnserializeModelDict ( buf ) ;
UnserializeSpriteDict ( buf ) ;
// Now we're pointing to the detail prop data
// This actually works because the scope of the game lump data
// is global and the buf was just pointing to it.
int count = buf . GetInt ( ) ;
if ( count )
{
pProps = ( DetailObjectLump_t * ) buf . PeekGet ( ) ;
}
else
{
pProps = 0 ;
}
return count ;
}
//-----------------------------------------------------------------------------
// Writes the detail lighting lump
//-----------------------------------------------------------------------------
static void WriteDetailLightingLump ( int lumpID , int lumpVersion , CUtlVector < DetailPropLightstylesLump_t > & lumpData )
{
GameLumpHandle_t handle = g_GameLumps . GetGameLumpHandle ( lumpID ) ;
if ( handle ! = g_GameLumps . InvalidGameLump ( ) )
g_GameLumps . DestroyGameLump ( handle ) ;
2023-05-13 21:11:15 +03:00
int lightsize = lumpData . Count ( ) * sizeof ( DetailPropLightstylesLump_t ) ;
2020-04-22 12:56:21 -04:00
int lumpsize = lightsize + sizeof ( int ) ;
handle = g_GameLumps . CreateGameLump ( lumpID , lumpsize , 0 , lumpVersion ) ;
// Serialize the data
CUtlBuffer buf ( g_GameLumps . GetGameLump ( handle ) , lumpsize ) ;
2023-05-13 21:11:15 +03:00
buf . PutInt ( lumpData . Count ( ) ) ;
2020-04-22 12:56:21 -04:00
if ( lightsize )
buf . Put ( lumpData . Base ( ) , lightsize ) ;
}
static void WriteDetailLightingLumps ( void )
{
WriteDetailLightingLump ( GAMELUMP_DETAIL_PROP_LIGHTING , GAMELUMP_DETAIL_PROP_LIGHTING_VERSION , s_DetailPropLightStyleLumpLDR ) ;
WriteDetailLightingLump ( GAMELUMP_DETAIL_PROP_LIGHTING_HDR , GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION , s_DetailPropLightStyleLumpHDR ) ;
}
// need to do this so that if we are building HDR data, the LDR data is intact, and vice versa.s
void UnserializeDetailPropLighting ( int lumpID , int lumpVersion , CUtlVector < DetailPropLightstylesLump_t > & lumpData )
{
GameLumpHandle_t handle = g_GameLumps . GetGameLumpHandle ( lumpID ) ;
if ( handle = = g_GameLumps . InvalidGameLump ( ) )
{
return ;
}
if ( g_GameLumps . GetGameLumpVersion ( handle ) ! = lumpVersion )
return ;
// Unserialize
CUtlBuffer buf ( g_GameLumps . GetGameLump ( handle ) , g_GameLumps . GameLumpSize ( handle ) , CUtlBuffer : : READ_ONLY ) ;
int count = buf . GetInt ( ) ;
if ( ! count )
{
return ;
}
lumpData . SetCount ( count ) ;
int lightsize = lumpData . Size ( ) * sizeof ( DetailPropLightstylesLump_t ) ;
buf . Get ( lumpData . Base ( ) , lightsize ) ;
}
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
DetailObjectLump_t * g_pMPIDetailProps = NULL ;
void VMPI_ProcessDetailPropWU ( int iThread , int iWorkUnit , MessageBuffer * pBuf )
{
CUtlVector < DetailPropLightstylesLump_t > * pDetailPropLump = s_pDetailPropLightStyleLump ;
DetailObjectLump_t & prop = g_pMPIDetailProps [ iWorkUnit ] ;
ComputeLighting ( prop , iThread ) ;
// Send the results back...
pBuf - > write ( & prop . m_Lighting , sizeof ( prop . m_Lighting ) ) ;
pBuf - > write ( & prop . m_LightStyleCount , sizeof ( prop . m_LightStyleCount ) ) ;
pBuf - > write ( & prop . m_LightStyles , sizeof ( prop . m_LightStyles ) ) ;
for ( int i = 0 ; i < prop . m_LightStyleCount ; i + + )
{
DetailPropLightstylesLump_t * l = & pDetailPropLump - > Element ( i + prop . m_LightStyles ) ;
pBuf - > write ( & l - > m_Lighting , sizeof ( l - > m_Lighting ) ) ;
pBuf - > write ( & l - > m_Style , sizeof ( l - > m_Style ) ) ;
}
}
void VMPI_ReceiveDetailPropWU ( int iWorkUnit , MessageBuffer * pBuf , int iWorker )
{
CUtlVector < DetailPropLightstylesLump_t > * pDetailPropLump = s_pDetailPropLightStyleLump ;
DetailObjectLump_t & prop = g_pMPIDetailProps [ iWorkUnit ] ;
pBuf - > read ( & prop . m_Lighting , sizeof ( prop . m_Lighting ) ) ;
pBuf - > read ( & prop . m_LightStyleCount , sizeof ( prop . m_LightStyleCount ) ) ;
pBuf - > read ( & prop . m_LightStyles , sizeof ( prop . m_LightStyles ) ) ;
pDetailPropLump - > EnsureCount ( prop . m_LightStyles + prop . m_LightStyleCount ) ;
for ( int i = 0 ; i < prop . m_LightStyleCount ; i + + )
{
DetailPropLightstylesLump_t * l = & pDetailPropLump - > Element ( i + prop . m_LightStyles ) ;
pBuf - > read ( & l - > m_Lighting , sizeof ( l - > m_Lighting ) ) ;
pBuf - > read ( & l - > m_Style , sizeof ( l - > m_Style ) ) ;
}
}
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Computes lighting for the detail props
//-----------------------------------------------------------------------------
void ComputeDetailPropLighting ( int iThread )
{
// illuminate them all
DetailObjectLump_t * pProps ;
int count = UnserializeDetailProps ( pProps ) ;
if ( ! count )
return ;
// unserialize the lump that we aren't computing.
if ( g_bHDR )
{
UnserializeDetailPropLighting ( GAMELUMP_DETAIL_PROP_LIGHTING , GAMELUMP_DETAIL_PROP_LIGHTING_VERSION , s_DetailPropLightStyleLumpLDR ) ;
}
else
{
UnserializeDetailPropLighting ( GAMELUMP_DETAIL_PROP_LIGHTING_HDR , GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION , s_DetailPropLightStyleLumpHDR ) ;
}
StartPacifier ( " Computing detail prop lighting : " ) ;
for ( int i = 0 ; i < count ; + + i )
{
UpdatePacifier ( ( float ) i / ( float ) count ) ;
ComputeLighting ( pProps [ i ] , iThread ) ;
}
// Write detail prop lightstyle lump...
WriteDetailLightingLumps ( ) ;
EndPacifier ( true ) ;
}