2013-06-26 15:22:04 -07:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
# include "vrad.h"
# include "lightmap.h"
# include "radial.h"
# include "mathlib/bumpvects.h"
# include "tier1/utlvector.h"
# include "vmpi.h"
# include "mathlib/anorms.h"
# include "map_utils.h"
# include "mathlib/halton.h"
# include "imagepacker.h"
# include "tier1/utlrbtree.h"
# include "tier1/utlbuffer.h"
# include "bitmap/tgawriter.h"
# include "mathlib/quantize.h"
# include "bitmap/imageformat.h"
# include "coordsize.h"
enum
{
AMBIENT_ONLY = 0x1 ,
NON_AMBIENT_ONLY = 0x2 ,
} ;
# define SMOOTHING_GROUP_HARD_EDGE 0xff000000
//==========================================================================//
// CNormalList.
//==========================================================================//
// This class keeps a list of unique normals and provides a fast
class CNormalList
{
public :
CNormalList ( ) ;
// Adds the normal if unique. Otherwise, returns the normal's index into m_Normals.
int FindOrAddNormal ( Vector const & vNormal ) ;
public :
CUtlVector < Vector > m_Normals ;
private :
// This represents a grid from (-1,-1,-1) to (1,1,1).
enum { NUM_SUBDIVS = 8 } ;
CUtlVector < int > m_NormalGrid [ NUM_SUBDIVS ] [ NUM_SUBDIVS ] [ NUM_SUBDIVS ] ;
} ;
int g_iCurFace ;
edgeshare_t edgeshare [ MAX_MAP_EDGES ] ;
Vector face_centroids [ MAX_MAP_EDGES ] ;
int vertexref [ MAX_MAP_VERTS ] ;
int * vertexface [ MAX_MAP_VERTS ] ;
faceneighbor_t faceneighbor [ MAX_MAP_FACES ] ;
static directlight_t * gSkyLight = NULL ;
static directlight_t * gAmbient = NULL ;
//==========================================================================//
// CNormalList implementation.
//==========================================================================//
CNormalList : : CNormalList ( ) : m_Normals ( 128 )
{
for ( int i = 0 ; i < sizeof ( m_NormalGrid ) / sizeof ( m_NormalGrid [ 0 ] [ 0 ] [ 0 ] ) ; i + + )
{
( & m_NormalGrid [ 0 ] [ 0 ] [ 0 ] + i ) - > SetGrowSize ( 16 ) ;
}
}
int CNormalList : : FindOrAddNormal ( Vector const & vNormal )
{
int gi [ 3 ] ;
// See which grid element it's in.
for ( int iDim = 0 ; iDim < 3 ; iDim + + )
{
gi [ iDim ] = ( int ) ( ( ( vNormal [ iDim ] + 1.0f ) * 0.5f ) * NUM_SUBDIVS - 0.000001f ) ;
gi [ iDim ] = min ( gi [ iDim ] , NUM_SUBDIVS ) ;
gi [ iDim ] = max ( gi [ iDim ] , 0 ) ;
}
// Look for a matching vector in there.
CUtlVector < int > * pGridElement = & m_NormalGrid [ gi [ 0 ] ] [ gi [ 1 ] ] [ gi [ 2 ] ] ;
for ( int i = 0 ; i < pGridElement - > Size ( ) ; i + + )
{
int iNormal = pGridElement - > Element ( i ) ;
Vector * pVec = & m_Normals [ iNormal ] ;
//if( pVec->DistToSqr(vNormal) < 0.00001f )
if ( * pVec = = vNormal )
return iNormal ;
}
// Ok, add a new one.
pGridElement - > AddToTail ( m_Normals . Size ( ) ) ;
return m_Normals . AddToTail ( vNormal ) ;
}
// FIXME: HACK until the plane normals are made more happy
void GetBumpNormals ( const float * sVect , const float * tVect , const Vector & flatNormal ,
const Vector & phongNormal , Vector bumpNormals [ NUM_BUMP_VECTS ] )
{
Vector stmp ( sVect [ 0 ] , sVect [ 1 ] , sVect [ 2 ] ) ;
Vector ttmp ( tVect [ 0 ] , tVect [ 1 ] , tVect [ 2 ] ) ;
GetBumpNormals ( stmp , ttmp , flatNormal , phongNormal , bumpNormals ) ;
}
int EdgeVertex ( dface_t * f , int edge )
{
int k ;
if ( edge < 0 )
edge + = f - > numedges ;
else if ( edge > = f - > numedges )
edge = edge % f - > numedges ;
k = dsurfedges [ f - > firstedge + edge ] ;
if ( k < 0 )
{
// Msg("(%d %d) ", dedges[-k].v[1], dedges[-k].v[0] );
return dedges [ - k ] . v [ 1 ] ;
}
else
{
// Msg("(%d %d) ", dedges[k].v[0], dedges[k].v[1] );
return dedges [ k ] . v [ 0 ] ;
}
}
/*
= = = = = = = = = = = =
PairEdges
= = = = = = = = = = = =
*/
void PairEdges ( void )
{
int i , j , k , n , m ;
dface_t * f ;
int numneighbors ;
int tmpneighbor [ 64 ] ;
faceneighbor_t * fn ;
// count number of faces that reference each vertex
for ( i = 0 , f = g_pFaces ; i < numfaces ; i + + , f + + )
{
for ( j = 0 ; j < f - > numedges ; j + + )
{
// Store the count in vertexref
vertexref [ EdgeVertex ( f , j ) ] + + ;
}
}
// allocate room
for ( i = 0 ; i < numvertexes ; i + + )
{
// use the count from above to allocate a big enough array
vertexface [ i ] = ( int * ) calloc ( vertexref [ i ] , sizeof ( vertexface [ 0 ] ) ) ;
// clear the temporary data
vertexref [ i ] = 0 ;
}
// store a list of every face that uses a particular vertex
for ( i = 0 , f = g_pFaces ; i < numfaces ; i + + , f + + )
{
for ( j = 0 ; j < f - > numedges ; j + + )
{
n = EdgeVertex ( f , j ) ;
for ( k = 0 ; k < vertexref [ n ] ; k + + )
{
if ( vertexface [ n ] [ k ] = = i )
break ;
}
if ( k > = vertexref [ n ] )
{
// add the face to the list
vertexface [ n ] [ k ] = i ;
vertexref [ n ] + + ;
}
}
}
// calc normals and set displacement surface flag
for ( i = 0 , f = g_pFaces ; i < numfaces ; i + + , f + + )
{
fn = & faceneighbor [ i ] ;
// get face normal
VectorCopy ( dplanes [ f - > planenum ] . normal , fn - > facenormal ) ;
// set displacement surface flag
fn - > bHasDisp = false ;
if ( ValidDispFace ( f ) )
{
fn - > bHasDisp = true ;
}
}
// find neighbors
for ( i = 0 , f = g_pFaces ; i < numfaces ; i + + , f + + )
{
numneighbors = 0 ;
fn = & faceneighbor [ i ] ;
// allocate room for vertex normals
fn - > normal = ( Vector * ) calloc ( f - > numedges , sizeof ( fn - > normal [ 0 ] ) ) ;
// look up all faces sharing vertices and add them to the list
for ( j = 0 ; j < f - > numedges ; j + + )
{
n = EdgeVertex ( f , j ) ;
for ( k = 0 ; k < vertexref [ n ] ; k + + )
{
double cos_normals_angle ;
Vector * pNeighbornormal ;
// skip self
if ( vertexface [ n ] [ k ] = = i )
continue ;
// if this face doens't have a displacement -- don't consider displacement neighbors
if ( ( ! fn - > bHasDisp ) & & ( faceneighbor [ vertexface [ n ] [ k ] ] . bHasDisp ) )
continue ;
pNeighbornormal = & faceneighbor [ vertexface [ n ] [ k ] ] . facenormal ;
cos_normals_angle = DotProduct ( * pNeighbornormal , fn - > facenormal ) ;
// add normal if >= threshold or its a displacement surface (this is only if the original
// face is a displacement)
if ( fn - > bHasDisp )
{
// Always smooth with and against a displacement surface.
VectorAdd ( fn - > normal [ j ] , * pNeighbornormal , fn - > normal [ j ] ) ;
}
else
{
// No smoothing - use of method (backwards compatibility).
if ( ( f - > smoothingGroups = = 0 ) & & ( g_pFaces [ vertexface [ n ] [ k ] ] . smoothingGroups = = 0 ) )
{
if ( cos_normals_angle > = smoothing_threshold )
{
VectorAdd ( fn - > normal [ j ] , * pNeighbornormal , fn - > normal [ j ] ) ;
}
else
{
// not considered a neighbor
continue ;
}
}
else
{
unsigned int smoothingGroup = ( f - > smoothingGroups & g_pFaces [ vertexface [ n ] [ k ] ] . smoothingGroups ) ;
// Hard edge.
if ( ( smoothingGroup & SMOOTHING_GROUP_HARD_EDGE ) ! = 0 )
continue ;
if ( smoothingGroup ! = 0 )
{
VectorAdd ( fn - > normal [ j ] , * pNeighbornormal , fn - > normal [ j ] ) ;
}
else
{
// not considered a neighbor
continue ;
}
}
}
// look to see if we've already added this one
for ( m = 0 ; m < numneighbors ; m + + )
{
if ( tmpneighbor [ m ] = = vertexface [ n ] [ k ] )
break ;
}
if ( m > = numneighbors )
{
// add to neighbor list
tmpneighbor [ m ] = vertexface [ n ] [ k ] ;
numneighbors + + ;
if ( numneighbors > ARRAYSIZE ( tmpneighbor ) )
{
Error ( " Stack overflow in neighbors \n " ) ;
}
}
}
}
if ( numneighbors )
{
// copy over neighbor list
fn - > numneighbors = numneighbors ;
fn - > neighbor = ( int * ) calloc ( numneighbors , sizeof ( fn - > neighbor [ 0 ] ) ) ;
for ( m = 0 ; m < numneighbors ; m + + )
{
fn - > neighbor [ m ] = tmpneighbor [ m ] ;
}
}
// fixup normals
for ( j = 0 ; j < f - > numedges ; j + + )
{
VectorAdd ( fn - > normal [ j ] , fn - > facenormal , fn - > normal [ j ] ) ;
VectorNormalize ( fn - > normal [ j ] ) ;
}
}
}
void SaveVertexNormals ( void )
{
faceneighbor_t * fn ;
int i , j ;
dface_t * f ;
CNormalList normalList ;
g_numvertnormalindices = 0 ;
for ( i = 0 ; i < numfaces ; i + + )
{
fn = & faceneighbor [ i ] ;
f = & g_pFaces [ i ] ;
for ( j = 0 ; j < f - > numedges ; j + + )
{
Vector vNormal ;
if ( fn - > normal )
{
vNormal = fn - > normal [ j ] ;
}
else
{
// original faces don't have normals
vNormal . Init ( 0 , 0 , 0 ) ;
}
if ( g_numvertnormalindices = = MAX_MAP_VERTNORMALINDICES )
{
Error ( " g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES " ) ;
}
g_vertnormalindices [ g_numvertnormalindices ] = ( unsigned short ) normalList . FindOrAddNormal ( vNormal ) ;
g_numvertnormalindices + + ;
}
}
if ( normalList . m_Normals . Size ( ) > MAX_MAP_VERTNORMALS )
{
Error ( " g_numvertnormals > MAX_MAP_VERTNORMALS " ) ;
}
// Copy the list of unique vert normals into g_vertnormals.
g_numvertnormals = normalList . m_Normals . Size ( ) ;
memcpy ( g_vertnormals , normalList . m_Normals . Base ( ) , sizeof ( g_vertnormals [ 0 ] ) * normalList . m_Normals . Size ( ) ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
LIGHTMAP SAMPLE GENERATION
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
//-----------------------------------------------------------------------------
// Purpose: Spits out an error message with information about a lightinfo_t.
// Input : s - Error message string.
// l - lightmap info struct.
//-----------------------------------------------------------------------------
void ErrorLightInfo ( const char * s , lightinfo_t * l )
{
texinfo_t * tex = & texinfo [ l - > face - > texinfo ] ;
winding_t * w = WindingFromFace ( & g_pFaces [ l - > facenum ] , l - > modelorg ) ;
//
// Show the face center and material name if possible.
//
if ( w ! = NULL )
{
// Don't exit, we'll try to recover...
Vector vecCenter ;
WindingCenter ( w , vecCenter ) ;
// FreeWinding(w);
Warning ( " %s at (%g, %g, %g) \n \t material=%s \n " , s , ( double ) vecCenter . x , ( double ) vecCenter . y , ( double ) vecCenter . z , TexDataStringTable_GetString ( dtexdata [ tex - > texdata ] . nameStringTableID ) ) ;
}
//
// If not, just show the material name.
//
else
{
Warning ( " %s at (degenerate face) \n \t material=%s \n " , s , TexDataStringTable_GetString ( dtexdata [ tex - > texdata ] . nameStringTableID ) ) ;
}
}
void CalcFaceVectors ( lightinfo_t * l )
{
texinfo_t * tex ;
int i , j ;
tex = & texinfo [ l - > face - > texinfo ] ;
// move into lightinfo_t
for ( i = 0 ; i < 2 ; i + + )
{
for ( j = 0 ; j < 3 ; j + + )
{
l - > worldToLuxelSpace [ i ] [ j ] = tex - > lightmapVecsLuxelsPerWorldUnits [ i ] [ j ] ;
}
}
//Solve[ { x * w00 + y * w01 + z * w02 - s == 0, x * w10 + y * w11 + z * w12 - t == 0, A * x + B * y + C * z + D == 0 }, { x, y, z } ]
//Rule(x,( C*s*w11 - B*s*w12 + B*t*w02 - C*t*w01 + D*w02*w11 - D*w01*w12) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
//Rule(y,( A*s*w12 - C*s*w10 + C*t*w00 - A*t*w02 + D*w00*w12 - D*w02*w10) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
//Rule(z,( B*s*w10 - A*s*w11 + A*t*w01 - B*t*w00 + D*w01*w10 - D*w00*w11) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 ))))
Vector luxelSpaceCross ;
luxelSpaceCross [ 0 ] =
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 1 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 2 ] -
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 2 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 1 ] ;
luxelSpaceCross [ 1 ] =
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 2 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 0 ] -
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 0 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 2 ] ;
luxelSpaceCross [ 2 ] =
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 0 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 1 ] -
tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 1 ] * tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 0 ] ;
float det = - DotProduct ( l - > facenormal , luxelSpaceCross ) ;
if ( fabs ( det ) < 1.0e-20 )
{
Warning ( " warning - face vectors parallel to face normal. bad lighting will be produced \n " ) ;
l - > luxelOrigin = vec3_origin ;
}
else
{
// invert the matrix
l - > luxelToWorldSpace [ 0 ] [ 0 ] = ( l - > facenormal [ 2 ] * l - > worldToLuxelSpace [ 1 ] [ 1 ] - l - > facenormal [ 1 ] * l - > worldToLuxelSpace [ 1 ] [ 2 ] ) / det ;
l - > luxelToWorldSpace [ 1 ] [ 0 ] = ( l - > facenormal [ 1 ] * l - > worldToLuxelSpace [ 0 ] [ 2 ] - l - > facenormal [ 2 ] * l - > worldToLuxelSpace [ 0 ] [ 1 ] ) / det ;
l - > luxelOrigin [ 0 ] = - ( l - > facedist * luxelSpaceCross [ 0 ] ) / det ;
l - > luxelToWorldSpace [ 0 ] [ 1 ] = ( l - > facenormal [ 0 ] * l - > worldToLuxelSpace [ 1 ] [ 2 ] - l - > facenormal [ 2 ] * l - > worldToLuxelSpace [ 1 ] [ 0 ] ) / det ;
l - > luxelToWorldSpace [ 1 ] [ 1 ] = ( l - > facenormal [ 2 ] * l - > worldToLuxelSpace [ 0 ] [ 0 ] - l - > facenormal [ 0 ] * l - > worldToLuxelSpace [ 0 ] [ 2 ] ) / det ;
l - > luxelOrigin [ 1 ] = - ( l - > facedist * luxelSpaceCross [ 1 ] ) / det ;
l - > luxelToWorldSpace [ 0 ] [ 2 ] = ( l - > facenormal [ 1 ] * l - > worldToLuxelSpace [ 1 ] [ 0 ] - l - > facenormal [ 0 ] * l - > worldToLuxelSpace [ 1 ] [ 1 ] ) / det ;
l - > luxelToWorldSpace [ 1 ] [ 2 ] = ( l - > facenormal [ 0 ] * l - > worldToLuxelSpace [ 0 ] [ 1 ] - l - > facenormal [ 1 ] * l - > worldToLuxelSpace [ 0 ] [ 0 ] ) / det ;
l - > luxelOrigin [ 2 ] = - ( l - > facedist * luxelSpaceCross [ 2 ] ) / det ;
// adjust for luxel offset
VectorMA ( l - > luxelOrigin , - tex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] [ 3 ] , l - > luxelToWorldSpace [ 0 ] , l - > luxelOrigin ) ;
VectorMA ( l - > luxelOrigin , - tex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] [ 3 ] , l - > luxelToWorldSpace [ 1 ] , l - > luxelOrigin ) ;
}
// compensate for org'd bmodels
VectorAdd ( l - > luxelOrigin , l - > modelorg , l - > luxelOrigin ) ;
}
winding_t * LightmapCoordWindingForFace ( lightinfo_t * l )
{
int i ;
winding_t * w ;
w = WindingFromFace ( l - > face , l - > modelorg ) ;
for ( i = 0 ; i < w - > numpoints ; i + + )
{
Vector2D coord ;
WorldToLuxelSpace ( l , w - > p [ i ] , coord ) ;
w - > p [ i ] . x = coord . x ;
w - > p [ i ] . y = coord . y ;
w - > p [ i ] . z = 0 ;
}
return w ;
}
void WriteCoordWinding ( FILE * out , lightinfo_t * l , winding_t * w , Vector & color )
{
int i ;
Vector pos ;
fprintf ( out , " %i \n " , w - > numpoints ) ;
for ( i = 0 ; i < w - > numpoints ; i + + )
{
LuxelSpaceToWorld ( l , w - > p [ i ] [ 0 ] , w - > p [ i ] [ 1 ] , pos ) ;
fprintf ( out , " %5.2f %5.2f %5.2f %5.3f %5.3f %5.3f \n " ,
pos [ 0 ] ,
pos [ 1 ] ,
pos [ 2 ] ,
color [ 0 ] / 256 ,
color [ 1 ] / 256 ,
color [ 2 ] / 256 ) ;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void DumpFaces ( lightinfo_t * pLightInfo , int ndxFace )
{
static FileHandle_t out ;
// get face data
faceneighbor_t * fn = & faceneighbor [ ndxFace ] ;
Vector & centroid = face_centroids [ ndxFace ] ;
// disable threading (not a multi-threadable function!)
ThreadLock ( ) ;
if ( ! out )
{
// open the file
out = g_pFileSystem - > Open ( " face.txt " , " w " ) ;
if ( ! out )
return ;
}
//
// write out face
//
for ( int ndxEdge = 0 ; ndxEdge < pLightInfo - > face - > numedges ; ndxEdge + + )
{
// int edge = dsurfedges[pLightInfo->face->firstedge+ndxEdge];
Vector p1 , p2 ;
VectorAdd ( dvertexes [ EdgeVertex ( pLightInfo - > face , ndxEdge ) ] . point , pLightInfo - > modelorg , p1 ) ;
VectorAdd ( dvertexes [ EdgeVertex ( pLightInfo - > face , ndxEdge + 1 ) ] . point , pLightInfo - > modelorg , p2 ) ;
Vector & n1 = fn - > normal [ ndxEdge ] ;
Vector & n2 = fn - > normal [ ( ndxEdge + 1 ) % pLightInfo - > face - > numedges ] ;
CmdLib_FPrintf ( out , " 3 \n " ) ;
CmdLib_FPrintf ( out , " %f %f %f %f %f %f \n " , p1 [ 0 ] , p1 [ 1 ] , p1 [ 2 ] , n1 [ 0 ] * 0.5 + 0.5 , n1 [ 1 ] * 0.5 + 0.5 , n1 [ 2 ] * 0.5 + 0.5 ) ;
CmdLib_FPrintf ( out , " %f %f %f %f %f %f \n " , p2 [ 0 ] , p2 [ 1 ] , p2 [ 2 ] , n2 [ 0 ] * 0.5 + 0.5 , n2 [ 1 ] * 0.5 + 0.5 , n2 [ 2 ] * 0.5 + 0.5 ) ;
CmdLib_FPrintf ( out , " %f %f %f %f %f %f \n " , centroid [ 0 ] + pLightInfo - > modelorg [ 0 ] ,
centroid [ 1 ] + pLightInfo - > modelorg [ 1 ] ,
centroid [ 2 ] + pLightInfo - > modelorg [ 2 ] ,
fn - > facenormal [ 0 ] * 0.5 + 0.5 ,
fn - > facenormal [ 1 ] * 0.5 + 0.5 ,
fn - > facenormal [ 2 ] * 0.5 + 0.5 ) ;
}
// enable threading
ThreadUnlock ( ) ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BuildFacesamplesAndLuxels_DoFast ( lightinfo_t * pLightInfo , facelight_t * pFaceLight )
{
// lightmap size
int width = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 0 ] + 1 ;
int height = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 1 ] + 1 ;
// ratio of world area / lightmap area
texinfo_t * pTex = & texinfo [ pLightInfo - > face - > texinfo ] ;
pFaceLight - > worldAreaPerLuxel = 1.0 / ( sqrt ( DotProduct ( pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] ,
pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] ) ) *
sqrt ( DotProduct ( pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] ,
pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] ) ) ) ;
//
// quickly create samples and luxels (copy over samples)
//
pFaceLight - > numsamples = width * height ;
pFaceLight - > sample = ( sample_t * ) calloc ( pFaceLight - > numsamples , sizeof ( * pFaceLight - > sample ) ) ;
if ( ! pFaceLight - > sample )
return false ;
pFaceLight - > numluxels = width * height ;
pFaceLight - > luxel = ( Vector * ) calloc ( pFaceLight - > numluxels , sizeof ( * pFaceLight - > luxel ) ) ;
if ( ! pFaceLight - > luxel )
return false ;
sample_t * pSamples = pFaceLight - > sample ;
Vector * pLuxels = pFaceLight - > luxel ;
for ( int t = 0 ; t < height ; t + + )
{
for ( int s = 0 ; s < width ; s + + )
{
pSamples - > s = s ;
pSamples - > t = t ;
pSamples - > coord [ 0 ] = s ;
pSamples - > coord [ 1 ] = t ;
// unused but initialized anyway
pSamples - > mins [ 0 ] = s - 0.5 ;
pSamples - > mins [ 1 ] = t - 0.5 ;
pSamples - > maxs [ 0 ] = s + 0.5 ;
pSamples - > maxs [ 1 ] = t + 0.5 ;
pSamples - > area = pFaceLight - > worldAreaPerLuxel ;
LuxelSpaceToWorld ( pLightInfo , pSamples - > coord [ 0 ] , pSamples - > coord [ 1 ] , pSamples - > pos ) ;
VectorCopy ( pSamples - > pos , * pLuxels ) ;
pSamples + + ;
pLuxels + + ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BuildSamplesAndLuxels_DoFast ( lightinfo_t * pLightInfo , facelight_t * pFaceLight , int ndxFace )
{
// build samples for a "face"
if ( pLightInfo - > face - > dispinfo = = - 1 )
{
return BuildFacesamplesAndLuxels_DoFast ( pLightInfo , pFaceLight ) ;
}
// build samples for a "displacement"
else
{
return StaticDispMgr ( ) - > BuildDispSamplesAndLuxels_DoFast ( pLightInfo , pFaceLight , ndxFace ) ;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BuildFacesamples ( lightinfo_t * pLightInfo , facelight_t * pFaceLight )
{
// lightmap size
int width = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 0 ] + 1 ;
int height = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 1 ] + 1 ;
// ratio of world area / lightmap area
texinfo_t * pTex = & texinfo [ pLightInfo - > face - > texinfo ] ;
pFaceLight - > worldAreaPerLuxel = 1.0 / ( sqrt ( DotProduct ( pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] ,
pTex - > lightmapVecsLuxelsPerWorldUnits [ 0 ] ) ) *
sqrt ( DotProduct ( pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] ,
pTex - > lightmapVecsLuxelsPerWorldUnits [ 1 ] ) ) ) ;
// allocate a large number of samples for creation -- get copied later!
2013-07-17 18:26:59 -07:00
CUtlVector < sample_t > sampleData ;
sampleData . SetCount ( SINGLE_BRUSH_MAP * 2 ) ;
sample_t * samples = sampleData . Base ( ) ;
2013-06-26 15:22:04 -07:00
sample_t * pSamples = samples ;
// lightmap space winding
winding_t * pLightmapWinding = LightmapCoordWindingForFace ( pLightInfo ) ;
//
// build vector pointing along the lightmap cutting planes
//
Vector sNorm ( 1.0f , 0.0f , 0.0f ) ;
Vector tNorm ( 0.0f , 1.0f , 0.0f ) ;
// sample center offset
float sampleOffset = ( do_centersamples ) ? 0.5 : 1.0 ;
//
// clip the lightmap "spaced" winding by the lightmap cutting planes
//
winding_t * pWindingT1 , * pWindingT2 ;
winding_t * pWindingS1 , * pWindingS2 ;
float dist ;
for ( int t = 0 ; t < height & & pLightmapWinding ; t + + )
{
dist = t + sampleOffset ;
// lop off a sample in the t dimension
// hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
ClipWindingEpsilon ( pLightmapWinding , tNorm , dist , ON_EPSILON / 16.0f , & pWindingT1 , & pWindingT2 ) ;
for ( int s = 0 ; s < width & & pWindingT2 ; s + + )
{
dist = s + sampleOffset ;
// lop off a sample in the s dimension, and put it in ws2
// hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
ClipWindingEpsilon ( pWindingT2 , sNorm , dist , ON_EPSILON / 16.0f , & pWindingS1 , & pWindingS2 ) ;
//
// s2 winding is a single sample worth of winding
//
if ( pWindingS2 )
{
// save the s, t positions
pSamples - > s = s ;
pSamples - > t = t ;
// get the lightmap space area of ws2 and convert to world area
// and find the center (then convert it to 2D)
Vector center ;
pSamples - > area = WindingAreaAndBalancePoint ( pWindingS2 , center ) * pFaceLight - > worldAreaPerLuxel ;
pSamples - > coord [ 0 ] = center . x ;
pSamples - > coord [ 1 ] = center . y ;
// find winding bounds (then convert it to 2D)
Vector minbounds , maxbounds ;
WindingBounds ( pWindingS2 , minbounds , maxbounds ) ;
pSamples - > mins [ 0 ] = minbounds . x ;
pSamples - > mins [ 1 ] = minbounds . y ;
pSamples - > maxs [ 0 ] = maxbounds . x ;
pSamples - > maxs [ 1 ] = maxbounds . y ;
// convert from lightmap space to world space
LuxelSpaceToWorld ( pLightInfo , pSamples - > coord [ 0 ] , pSamples - > coord [ 1 ] , pSamples - > pos ) ;
if ( g_bDumpPatches | | ( do_extra & & pSamples - > area < pFaceLight - > worldAreaPerLuxel - EQUAL_EPSILON ) )
{
//
// convert the winding from lightmaps space to world for debug rendering and sub-sampling
//
Vector worldPos ;
for ( int ndxPt = 0 ; ndxPt < pWindingS2 - > numpoints ; ndxPt + + )
{
LuxelSpaceToWorld ( pLightInfo , pWindingS2 - > p [ ndxPt ] . x , pWindingS2 - > p [ ndxPt ] . y , worldPos ) ;
VectorCopy ( worldPos , pWindingS2 - > p [ ndxPt ] ) ;
}
pSamples - > w = pWindingS2 ;
}
else
{
// winding isn't needed, free it.
pSamples - > w = NULL ;
FreeWinding ( pWindingS2 ) ;
}
pSamples + + ;
}
//
// if winding T2 still exists free it and set it equal S1 (the rest of the row minus the sample just created)
//
if ( pWindingT2 )
{
FreeWinding ( pWindingT2 ) ;
}
// clip the rest of "s"
pWindingT2 = pWindingS1 ;
}
//
// if the original lightmap winding exists free it and set it equal to T1 (the rest of the winding not cut into samples)
//
if ( pLightmapWinding )
{
FreeWinding ( pLightmapWinding ) ;
}
if ( pWindingT2 )
{
FreeWinding ( pWindingT2 ) ;
}
pLightmapWinding = pWindingT1 ;
}
//
// copy over samples
//
pFaceLight - > numsamples = pSamples - samples ;
pFaceLight - > sample = ( sample_t * ) calloc ( pFaceLight - > numsamples , sizeof ( * pFaceLight - > sample ) ) ;
if ( ! pFaceLight - > sample )
return false ;
memcpy ( pFaceLight - > sample , samples , pFaceLight - > numsamples * sizeof ( * pFaceLight - > sample ) ) ;
// supply a default sample normal (face normal - assumed flat)
for ( int ndxSample = 0 ; ndxSample < pFaceLight - > numsamples ; ndxSample + + )
{
Assert ( VectorLength ( pLightInfo - > facenormal ) > 1.0e-20 ) ;
pFaceLight - > sample [ ndxSample ] . normal = pLightInfo - > facenormal ;
}
// statistics - warning?!
if ( pFaceLight - > numsamples = = 0 )
{
Msg ( " no samples %d \n " , pLightInfo - > face - g_pFaces ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Free any windings used by this facelight. It's currently assumed they're not needed again
//-----------------------------------------------------------------------------
void FreeSampleWindings ( facelight_t * fl )
{
int i ;
for ( i = 0 ; i < fl - > numsamples ; i + + )
{
if ( fl - > sample [ i ] . w )
{
FreeWinding ( fl - > sample [ i ] . w ) ;
fl - > sample [ i ] . w = NULL ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: build the sample data for each lightmapped primitive type
//-----------------------------------------------------------------------------
bool BuildSamples ( lightinfo_t * pLightInfo , facelight_t * pFaceLight , int ndxFace )
{
// build samples for a "face"
if ( pLightInfo - > face - > dispinfo = = - 1 )
{
return BuildFacesamples ( pLightInfo , pFaceLight ) ;
}
// build samples for a "displacement"
else
{
return StaticDispMgr ( ) - > BuildDispSamples ( pLightInfo , pFaceLight , ndxFace ) ;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool BuildFaceLuxels ( lightinfo_t * pLightInfo , facelight_t * pFaceLight )
{
// lightmap size
int width = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 0 ] + 1 ;
int height = pLightInfo - > face - > m_LightmapTextureSizeInLuxels [ 1 ] + 1 ;
// calcuate actual luxel points
pFaceLight - > numluxels = width * height ;
pFaceLight - > luxel = ( Vector * ) calloc ( pFaceLight - > numluxels , sizeof ( * pFaceLight - > luxel ) ) ;
if ( ! pFaceLight - > luxel )
return false ;
for ( int t = 0 ; t < height ; t + + )
{
for ( int s = 0 ; s < width ; s + + )
{
LuxelSpaceToWorld ( pLightInfo , s , t , pFaceLight - > luxel [ s + t * width ] ) ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: build the luxels (find the luxel centers) for each lightmapped
// primitive type
//-----------------------------------------------------------------------------
bool BuildLuxels ( lightinfo_t * pLightInfo , facelight_t * pFaceLight , int ndxFace )
{
// build luxels for a "face"
if ( pLightInfo - > face - > dispinfo = = - 1 )
{
return BuildFaceLuxels ( pLightInfo , pFaceLight ) ;
}
// build luxels for a "displacement"
else
{
return StaticDispMgr ( ) - > BuildDispLuxels ( pLightInfo , pFaceLight , ndxFace ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: for each face, find the center of each luxel; for each texture
// aligned grid point, back project onto the plane and get the world
// xyz value of the sample point
// NOTE: ndxFace = facenum
//-----------------------------------------------------------------------------
void CalcPoints ( lightinfo_t * pLightInfo , facelight_t * pFaceLight , int ndxFace )
{
// debugging!
if ( g_bDumpPatches )
{
DumpFaces ( pLightInfo , ndxFace ) ;
}
// quick and dirty!
if ( do_fast )
{
if ( ! BuildSamplesAndLuxels_DoFast ( pLightInfo , pFaceLight , ndxFace ) )
{
Msg ( " Face %d: (Fast)Error Building Samples and Luxels \n " , ndxFace ) ;
}
return ;
}
// build the samples
if ( ! BuildSamples ( pLightInfo , pFaceLight , ndxFace ) )
{
Msg ( " Face %d: Error Building Samples \n " , ndxFace ) ;
}
// build the luxels
if ( ! BuildLuxels ( pLightInfo , pFaceLight , ndxFace ) )
{
Msg ( " Face %d: Error Building Luxels \n " , ndxFace ) ;
}
}
//==============================================================
directlight_t * activelights ;
directlight_t * freelights ;
facelight_t facelight [ MAX_MAP_FACES ] ;
int numdlights ;
/*
= = = = = = = = = = = = = = = = = =
FindTargetEntity
= = = = = = = = = = = = = = = = = =
*/
entity_t * FindTargetEntity ( char * target )
{
int i ;
char * n ;
for ( i = 0 ; i < num_entities ; i + + )
{
n = ValueForKey ( & entities [ i ] , " targetname " ) ;
if ( ! strcmp ( n , target ) )
return & entities [ i ] ;
}
return NULL ;
}
/*
= = = = = = = = = = = = =
AllocDLight
= = = = = = = = = = = = =
*/
int GetVisCache ( int lastoffset , int cluster , byte * pvs ) ;
void SetDLightVis ( directlight_t * dl , int cluster ) ;
void MergeDLightVis ( directlight_t * dl , int cluster ) ;
directlight_t * AllocDLight ( Vector & origin , bool bAddToList )
{
directlight_t * dl ;
dl = ( directlight_t * ) calloc ( 1 , sizeof ( directlight_t ) ) ;
dl - > index = numdlights + + ;
VectorCopy ( origin , dl - > light . origin ) ;
dl - > light . cluster = ClusterFromPoint ( dl - > light . origin ) ;
SetDLightVis ( dl , dl - > light . cluster ) ;
dl - > facenum = - 1 ;
if ( bAddToList )
{
dl - > next = activelights ;
activelights = dl ;
}
return dl ;
}
void AddDLightToActiveList ( directlight_t * dl )
{
dl - > next = activelights ;
activelights = dl ;
}
void FreeDLights ( )
{
gSkyLight = NULL ;
gAmbient = NULL ;
directlight_t * pNext ;
for ( directlight_t * pCur = activelights ; pCur ; pCur = pNext )
{
pNext = pCur - > next ;
free ( pCur ) ;
}
activelights = 0 ;
}
void SetDLightVis ( directlight_t * dl , int cluster )
{
if ( dl - > pvs = = NULL )
{
dl - > pvs = ( byte * ) calloc ( 1 , ( dvis - > numclusters / 8 ) + 1 ) ;
}
GetVisCache ( - 1 , cluster , dl - > pvs ) ;
}
void MergeDLightVis ( directlight_t * dl , int cluster )
{
if ( dl - > pvs = = NULL )
{
SetDLightVis ( dl , cluster ) ;
}
else
{
byte pvs [ MAX_MAP_CLUSTERS / 8 ] ;
GetVisCache ( - 1 , cluster , pvs ) ;
// merge both vis graphs
for ( int i = 0 ; i < ( dvis - > numclusters / 8 ) + 1 ; i + + )
{
dl - > pvs [ i ] | = pvs [ i ] ;
}
}
}
/*
= = = = = = = = = = = = =
LightForKey
= = = = = = = = = = = = =
*/
int LightForKey ( entity_t * ent , char * key , Vector & intensity )
{
char * pLight ;
pLight = ValueForKey ( ent , key ) ;
return LightForString ( pLight , intensity ) ;
}
int LightForString ( char * pLight , Vector & intensity )
{
double r , g , b , scaler ;
int argCnt ;
VectorFill ( intensity , 0 ) ;
// scanf into doubles, then assign, so it is vec_t size independent
r = g = b = scaler = 0 ;
double r_hdr , g_hdr , b_hdr , scaler_hdr ;
argCnt = sscanf ( pLight , " %lf %lf %lf %lf %lf %lf %lf %lf " ,
& r , & g , & b , & scaler , & r_hdr , & g_hdr , & b_hdr , & scaler_hdr ) ;
if ( argCnt = = 8 ) // 2 4-tuples
{
if ( g_bHDR )
{
r = r_hdr ;
g = g_hdr ;
b = b_hdr ;
scaler = scaler_hdr ;
}
argCnt = 4 ;
}
// make sure light is legal
if ( r < 0.0f | | g < 0.0f | | b < 0.0f | | scaler < 0.0f )
{
intensity . Init ( 0.0f , 0.0f , 0.0f ) ;
return false ;
}
intensity [ 0 ] = pow ( r / 255.0 , 2.2 ) * 255 ; // convert to linear
switch ( argCnt )
{
case 1 :
// The R,G,B values are all equal.
intensity [ 1 ] = intensity [ 2 ] = intensity [ 0 ] ;
break ;
case 3 :
case 4 :
// Save the other two G,B values.
intensity [ 1 ] = pow ( g / 255.0 , 2.2 ) * 255 ;
intensity [ 2 ] = pow ( b / 255.0 , 2.2 ) * 255 ;
// Did we also get an "intensity" scaler value too?
if ( argCnt = = 4 )
{
// Scale the normalized 0-255 R,G,B values by the intensity scaler
VectorScale ( intensity , scaler / 255.0 , intensity ) ;
}
break ;
default :
printf ( " unknown light specifier type - %s \n " , pLight ) ;
return false ;
}
// scale up source lights by scaling factor
VectorScale ( intensity , lightscale , intensity ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Various parsing methods
//-----------------------------------------------------------------------------
static void ParseLightGeneric ( entity_t * e , directlight_t * dl )
{
entity_t * e2 ;
char * target ;
Vector dest ;
dl - > light . style = ( int ) FloatForKey ( e , " style " ) ;
// get intenfsity
if ( g_bHDR & & LightForKey ( e , " _lightHDR " , dl - > light . intensity ) )
{
}
else
{
LightForKey ( e , " _light " , dl - > light . intensity ) ;
}
// check angle, targets
target = ValueForKey ( e , " target " ) ;
if ( target [ 0 ] )
{ // point towards target
e2 = FindTargetEntity ( target ) ;
if ( ! e2 )
Warning ( " WARNING: light at (%i %i %i) has missing target \n " ,
( int ) dl - > light . origin [ 0 ] , ( int ) dl - > light . origin [ 1 ] , ( int ) dl - > light . origin [ 2 ] ) ;
else
{
GetVectorForKey ( e2 , " origin " , dest ) ;
VectorSubtract ( dest , dl - > light . origin , dl - > light . normal ) ;
VectorNormalize ( dl - > light . normal ) ;
}
}
else
{
// point down angle
Vector angles ;
GetVectorForKey ( e , " angles " , angles ) ;
float pitch = FloatForKey ( e , " pitch " ) ;
float angle = FloatForKey ( e , " angle " ) ;
SetupLightNormalFromProps ( QAngle ( angles . x , angles . y , angles . z ) , angle , pitch , dl - > light . normal ) ;
}
if ( g_bHDR )
VectorScale ( dl - > light . intensity ,
FloatForKeyWithDefault ( e , " _lightscaleHDR " , 1.0 ) ,
dl - > light . intensity ) ;
}
static void SetLightFalloffParams ( entity_t * e , directlight_t * dl )
{
float d50 = FloatForKey ( e , " _fifty_percent_distance " ) ;
dl - > m_flStartFadeDistance = 0 ;
dl - > m_flEndFadeDistance = - 1 ;
dl - > m_flCapDist = 1.0e22 ;
if ( d50 )
{
float d0 = FloatForKey ( e , " _zero_percent_distance " ) ;
if ( d0 < d50 )
{
Warning ( " light has _fifty_percent_distance of %f but _zero_percent_distance of %f \n " , d50 , d0 ) ;
d0 = 2.0 * d50 ;
}
float a = 0 , b = 1 , c = 0 ;
if ( ! SolveInverseQuadraticMonotonic ( 0 , 1.0 , d50 , 2.0 , d0 , 256.0 , a , b , c ) )
{
Warning ( " can't solve quadratic for light %f %f \n " , d50 , d0 ) ;
}
// it it possible that the parameters couldn't be used because of enforing monoticity. If so, rescale so at
// least the 50 percent value is right
// printf("50 percent=%f 0 percent=%f\n",d50,d0);
// printf("a=%f b=%f c=%f\n",a,b,c);
float v50 = c + d50 * ( b + d50 * a ) ;
float scale = 2.0 / v50 ;
a * = scale ;
b * = scale ;
c * = scale ;
// printf("scaled=%f a=%f b=%f c=%f\n",scale,a,b,c);
// for(float d=0;d<1000;d+=20)
// printf("at %f, %f\n",d,1.0/(c+d*(b+d*a)));
dl - > light . quadratic_attn = a ;
dl - > light . linear_attn = b ;
dl - > light . constant_attn = c ;
if ( IntForKey ( e , " _hardfalloff " ) )
{
dl - > m_flEndFadeDistance = d0 ;
dl - > m_flStartFadeDistance = 0.75 * d0 + 0.25 * d50 ; // start fading 3/4 way between 50 and 0. could allow adjust
}
else
{
// now, we will find the point at which the 1/x term reaches its maximum value, and
// prevent the light from going past there. If a user specifes an extreme falloff, the
// quadratic will start making the light brighter at some distance. We handle this by
// fading it from the minimum brightess point down to zero at 10x the minimum distance
if ( fabs ( a ) > 0. )
{
float flMax = b / ( - 2.0 * a ) ; // where f' = 0
if ( flMax > 0.0 )
{
dl - > m_flCapDist = flMax ;
dl - > m_flStartFadeDistance = flMax ;
dl - > m_flEndFadeDistance = 10.0 * flMax ;
}
}
}
}
else
{
dl - > light . constant_attn = FloatForKey ( e , " _constant_attn " ) ;
dl - > light . linear_attn = FloatForKey ( e , " _linear_attn " ) ;
dl - > light . quadratic_attn = FloatForKey ( e , " _quadratic_attn " ) ;
dl - > light . radius = FloatForKey ( e , " _distance " ) ;
// clamp values to >= 0
if ( dl - > light . constant_attn < EQUAL_EPSILON )
dl - > light . constant_attn = 0 ;
if ( dl - > light . linear_attn < EQUAL_EPSILON )
dl - > light . linear_attn = 0 ;
if ( dl - > light . quadratic_attn < EQUAL_EPSILON )
dl - > light . quadratic_attn = 0 ;
if ( dl - > light . constant_attn < EQUAL_EPSILON & & dl - > light . linear_attn < EQUAL_EPSILON & & dl - > light . quadratic_attn < EQUAL_EPSILON )
dl - > light . constant_attn = 1 ;
// scale intensity for unit 100 distance
float ratio = ( dl - > light . constant_attn + 100 * dl - > light . linear_attn + 100 * 100 * dl - > light . quadratic_attn ) ;
if ( ratio > 0 )
{
VectorScale ( dl - > light . intensity , ratio , dl - > light . intensity ) ;
}
}
}
static void ParseLightSpot ( entity_t * e , directlight_t * dl )
{
Vector dest ;
GetVectorForKey ( e , " origin " , dest ) ;
dl = AllocDLight ( dest , true ) ;
ParseLightGeneric ( e , dl ) ;
dl - > light . type = emit_spotlight ;
dl - > light . stopdot = FloatForKey ( e , " _inner_cone " ) ;
if ( ! dl - > light . stopdot )
dl - > light . stopdot = 10 ;
dl - > light . stopdot2 = FloatForKey ( e , " _cone " ) ;
if ( ! dl - > light . stopdot2 )
dl - > light . stopdot2 = dl - > light . stopdot ;
if ( dl - > light . stopdot2 < dl - > light . stopdot )
dl - > light . stopdot2 = dl - > light . stopdot ;
// This is a point light if stop dots are 180...
if ( ( dl - > light . stopdot = = 180 ) & & ( dl - > light . stopdot2 = = 180 ) )
{
dl - > light . stopdot = dl - > light . stopdot2 = 0 ;
dl - > light . type = emit_point ;
dl - > light . exponent = 0 ;
}
else
{
// Clamp to 90, that's all DX8 can handle!
if ( dl - > light . stopdot > 90 )
{
Warning ( " WARNING: light_spot at (%i %i %i) has inner angle larger than 90 degrees! Clamping to 90... \n " ,
( int ) dl - > light . origin [ 0 ] , ( int ) dl - > light . origin [ 1 ] , ( int ) dl - > light . origin [ 2 ] ) ;
dl - > light . stopdot = 90 ;
}
if ( dl - > light . stopdot2 > 90 )
{
Warning ( " WARNING: light_spot at (%i %i %i) has outer angle larger than 90 degrees! Clamping to 90... \n " ,
( int ) dl - > light . origin [ 0 ] , ( int ) dl - > light . origin [ 1 ] , ( int ) dl - > light . origin [ 2 ] ) ;
dl - > light . stopdot2 = 90 ;
}
dl - > light . stopdot2 = ( float ) cos ( dl - > light . stopdot2 / 180 * M_PI ) ;
dl - > light . stopdot = ( float ) cos ( dl - > light . stopdot / 180 * M_PI ) ;
dl - > light . exponent = FloatForKey ( e , " _exponent " ) ;
}
SetLightFalloffParams ( e , dl ) ;
}
// NOTE: This is just a heuristic. It traces a finite number of rays to find sky
// NOTE: Full vis is necessary to make this 100% correct.
bool CanLeafTraceToSky ( int iLeaf )
{
// UNDONE: Really want a point inside the leaf here. Center is a guess, may not be in the leaf
// UNDONE: Clip this to each plane bounding the leaf to guarantee
Vector center = vec3_origin ;
for ( int i = 0 ; i < 3 ; i + + )
{
center [ i ] = ( ( float ) ( dleafs [ iLeaf ] . mins [ i ] + dleafs [ iLeaf ] . maxs [ i ] ) ) * 0.5f ;
}
FourVectors center4 , delta ;
fltx4 fractionVisible ;
for ( int j = 0 ; j < NUMVERTEXNORMALS ; j + = 4 )
{
// search back to see if we can hit a sky brush
delta . LoadAndSwizzle ( g_anorms [ j ] , g_anorms [ min ( j + 1 , NUMVERTEXNORMALS - 1 ) ] ,
g_anorms [ min ( j + 2 , NUMVERTEXNORMALS - 1 ) ] , g_anorms [ min ( j + 3 , NUMVERTEXNORMALS - 1 ) ] ) ;
delta * = - MAX_TRACE_LENGTH ;
delta + = center4 ;
// return true if any hits sky
TestLine_DoesHitSky ( center4 , delta , & fractionVisible ) ;
if ( TestSignSIMD ( CmpGtSIMD ( fractionVisible , Four_Zeros ) ) )
return true ;
}
return false ;
}
void BuildVisForLightEnvironment ( void )
{
// Create the vis.
for ( int iLeaf = 0 ; iLeaf < numleafs ; + + iLeaf )
{
dleafs [ iLeaf ] . flags & = ~ ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ;
unsigned int iFirstFace = dleafs [ iLeaf ] . firstleafface ;
for ( int iLeafFace = 0 ; iLeafFace < dleafs [ iLeaf ] . numleaffaces ; + + iLeafFace )
{
unsigned int iFace = dleaffaces [ iFirstFace + iLeafFace ] ;
texinfo_t & tex = texinfo [ g_pFaces [ iFace ] . texinfo ] ;
if ( tex . flags & SURF_SKY )
{
if ( tex . flags & SURF_SKY2D )
{
dleafs [ iLeaf ] . flags | = LEAF_FLAGS_SKY2D ;
}
else
{
dleafs [ iLeaf ] . flags | = LEAF_FLAGS_SKY ;
}
MergeDLightVis ( gSkyLight , dleafs [ iLeaf ] . cluster ) ;
MergeDLightVis ( gAmbient , dleafs [ iLeaf ] . cluster ) ;
break ;
}
}
}
// Second pass to set flags on leaves that don't contain sky, but touch leaves that
// contain sky.
byte pvs [ MAX_MAP_CLUSTERS / 8 ] ;
int nLeafBytes = ( numleafs > > 3 ) + 1 ;
unsigned char * pLeafBits = ( unsigned char * ) stackalloc ( nLeafBytes * sizeof ( unsigned char ) ) ;
unsigned char * pLeaf2DBits = ( unsigned char * ) stackalloc ( nLeafBytes * sizeof ( unsigned char ) ) ;
memset ( pLeafBits , 0 , nLeafBytes ) ;
memset ( pLeaf2DBits , 0 , nLeafBytes ) ;
for ( int iLeaf = 0 ; iLeaf < numleafs ; + + iLeaf )
{
// If this leaf has light (3d skybox) in it, then don't bother
if ( dleafs [ iLeaf ] . flags & LEAF_FLAGS_SKY )
continue ;
// Don't bother with this leaf if it's solid
if ( dleafs [ iLeaf ] . contents & CONTENTS_SOLID )
continue ;
// See what other leaves are visible from this leaf
GetVisCache ( - 1 , dleafs [ iLeaf ] . cluster , pvs ) ;
// Now check out all other leaves
int nByte = iLeaf > > 3 ;
int nBit = 1 < < ( iLeaf & 0x7 ) ;
for ( int iLeaf2 = 0 ; iLeaf2 < numleafs ; + + iLeaf2 )
{
if ( iLeaf2 = = iLeaf )
continue ;
if ( ! ( dleafs [ iLeaf2 ] . flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
continue ;
// Can this leaf see into the leaf with the sky in it?
if ( ! PVSCheck ( pvs , dleafs [ iLeaf2 ] . cluster ) )
continue ;
if ( dleafs [ iLeaf2 ] . flags & LEAF_FLAGS_SKY2D )
{
pLeaf2DBits [ nByte ] | = nBit ;
}
if ( dleafs [ iLeaf2 ] . flags & LEAF_FLAGS_SKY )
{
pLeafBits [ nByte ] | = nBit ;
// As soon as we know this leaf needs to draw the 3d skybox, we're done
break ;
}
}
}
// Must set the bits in a separate pass so as to not flood-fill LEAF_FLAGS_SKY everywhere
// pLeafbits is a bit array of all leaves that need to be marked as seeing sky
for ( int iLeaf = 0 ; iLeaf < numleafs ; + + iLeaf )
{
// If this leaf has light (3d skybox) in it, then don't bother
if ( dleafs [ iLeaf ] . flags & LEAF_FLAGS_SKY )
continue ;
// Don't bother with this leaf if it's solid
if ( dleafs [ iLeaf ] . contents & CONTENTS_SOLID )
continue ;
// Check to see if this is a 2D skybox leaf
if ( pLeaf2DBits [ iLeaf > > 3 ] & ( 1 < < ( iLeaf & 0x7 ) ) )
{
dleafs [ iLeaf ] . flags | = LEAF_FLAGS_SKY2D ;
}
// If this is a 3D skybox leaf, then we don't care if it was previously a 2D skybox leaf
if ( pLeafBits [ iLeaf > > 3 ] & ( 1 < < ( iLeaf & 0x7 ) ) )
{
dleafs [ iLeaf ] . flags | = LEAF_FLAGS_SKY ;
dleafs [ iLeaf ] . flags & = ~ LEAF_FLAGS_SKY2D ;
}
else
{
// if radial vis was used on this leaf some of the portals leading
// to sky may have been culled. Try tracing to find sky.
if ( dleafs [ iLeaf ] . flags & LEAF_FLAGS_RADIAL )
{
if ( CanLeafTraceToSky ( iLeaf ) )
{
// FIXME: Should make a version that checks if we hit 2D skyboxes.. oh well.
dleafs [ iLeaf ] . flags | = LEAF_FLAGS_SKY ;
}
}
}
}
}
static char * ValueForKeyWithDefault ( entity_t * ent , char * key , char * default_value = NULL )
{
epair_t * ep ;
for ( ep = ent - > epairs ; ep ; ep = ep - > next )
if ( ! strcmp ( ep - > key , key ) )
return ep - > value ;
return default_value ;
}
static void ParseLightEnvironment ( entity_t * e , directlight_t * dl )
{
Vector dest ;
GetVectorForKey ( e , " origin " , dest ) ;
dl = AllocDLight ( dest , false ) ;
ParseLightGeneric ( e , dl ) ;
char * angle_str = ValueForKeyWithDefault ( e , " SunSpreadAngle " ) ;
if ( angle_str )
{
g_SunAngularExtent = atof ( angle_str ) ;
g_SunAngularExtent = sin ( ( M_PI / 180.0 ) * g_SunAngularExtent ) ;
printf ( " sun extent from map=%f \n " , g_SunAngularExtent ) ;
}
if ( ! gSkyLight )
{
// Sky light.
gSkyLight = dl ;
dl - > light . type = emit_skylight ;
// Sky ambient light.
gAmbient = AllocDLight ( dl - > light . origin , false ) ;
gAmbient - > light . type = emit_skyambient ;
if ( g_bHDR & & LightForKey ( e , " _ambientHDR " , gAmbient - > light . intensity ) )
{
// we have a valid HDR ambient light value
}
else if ( ! LightForKey ( e , " _ambient " , gAmbient - > light . intensity ) )
{
VectorScale ( dl - > light . intensity , 0.5 , gAmbient - > light . intensity ) ;
}
if ( g_bHDR )
{
VectorScale ( gAmbient - > light . intensity ,
FloatForKeyWithDefault ( e , " _AmbientScaleHDR " , 1.0 ) ,
gAmbient - > light . intensity ) ;
}
BuildVisForLightEnvironment ( ) ;
// Add sky and sky ambient lights to the list.
AddDLightToActiveList ( gSkyLight ) ;
AddDLightToActiveList ( gAmbient ) ;
}
}
static void ParseLightPoint ( entity_t * e , directlight_t * dl )
{
Vector dest ;
GetVectorForKey ( e , " origin " , dest ) ;
dl = AllocDLight ( dest , true ) ;
ParseLightGeneric ( e , dl ) ;
dl - > light . type = emit_point ;
SetLightFalloffParams ( e , dl ) ;
}
/*
= = = = = = = = = = = = =
CreateDirectLights
= = = = = = = = = = = = =
*/
# define DIRECT_SCALE (100.0*100.0)
void CreateDirectLights ( void )
{
unsigned i ;
CPatch * p = NULL ;
directlight_t * dl = NULL ;
entity_t * e = NULL ;
char * name ;
Vector dest ;
numdlights = 0 ;
FreeDLights ( ) ;
//
// surfaces
//
unsigned int uiPatchCount = g_Patches . Count ( ) ;
for ( i = 0 ; i < uiPatchCount ; i + + )
{
p = & g_Patches . Element ( i ) ;
// skip parent patches
if ( p - > child1 ! = g_Patches . InvalidIndex ( ) )
continue ;
if ( p - > basearea < 1e-6 )
continue ;
if ( VectorAvg ( p - > baselight ) > = dlight_threshold )
{
dl = AllocDLight ( p - > origin , true ) ;
dl - > light . type = emit_surface ;
VectorCopy ( p - > normal , dl - > light . normal ) ;
Assert ( VectorLength ( p - > normal ) > 1.0e-20 ) ;
// scale intensity by number of texture instances
VectorScale ( p - > baselight , lightscale * p - > area * p - > scale [ 0 ] * p - > scale [ 1 ] / p - > basearea , dl - > light . intensity ) ;
// scale to a range that results in actual light
VectorScale ( dl - > light . intensity , DIRECT_SCALE , dl - > light . intensity ) ;
}
}
//
// entities
//
for ( i = 0 ; i < ( unsigned ) num_entities ; i + + )
{
e = & entities [ i ] ;
name = ValueForKey ( e , " classname " ) ;
if ( strncmp ( name , " light " , 5 ) )
continue ;
// Light_dynamic is actually a real entity; not to be included here...
if ( ! strcmp ( name , " light_dynamic " ) )
continue ;
if ( ! strcmp ( name , " light_spot " ) )
{
ParseLightSpot ( e , dl ) ;
}
else if ( ! strcmp ( name , " light_environment " ) )
{
ParseLightEnvironment ( e , dl ) ;
}
else if ( ! strcmp ( name , " light " ) )
{
ParseLightPoint ( e , dl ) ;
}
else
{
qprintf ( " unsupported light entity: \" %s \" \n " , name ) ;
}
}
qprintf ( " %i direct lights \n " , numdlights ) ;
// exit(1);
}
/*
= = = = = = = = = = = = =
ExportDirectLightsToWorldLights
= = = = = = = = = = = = =
*/
void ExportDirectLightsToWorldLights ( )
{
directlight_t * dl ;
// In case the level has already been VRADed.
* pNumworldlights = 0 ;
for ( dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
dworldlight_t * wl = & dworldlights [ ( * pNumworldlights ) + + ] ;
if ( * pNumworldlights > MAX_MAP_WORLDLIGHTS )
{
Error ( " too many lights %d / %d \n " , * pNumworldlights , MAX_MAP_WORLDLIGHTS ) ;
}
wl - > cluster = dl - > light . cluster ;
wl - > type = dl - > light . type ;
wl - > style = dl - > light . style ;
VectorCopy ( dl - > light . origin , wl - > origin ) ;
// FIXME: why does vrad want 0 to 255 and not 0 to 1??
VectorScale ( dl - > light . intensity , ( 1.0 / 255.0 ) , wl - > intensity ) ;
VectorCopy ( dl - > light . normal , wl - > normal ) ;
wl - > stopdot = dl - > light . stopdot ;
wl - > stopdot2 = dl - > light . stopdot2 ;
wl - > exponent = dl - > light . exponent ;
wl - > radius = dl - > light . radius ;
wl - > constant_attn = dl - > light . constant_attn ;
wl - > linear_attn = dl - > light . linear_attn ;
wl - > quadratic_attn = dl - > light . quadratic_attn ;
wl - > flags = 0 ;
}
}
/*
= = = = = = = = = = = = =
GatherSampleLight
= = = = = = = = = = = = =
*/
# define NORMALFORMFACTOR 40.156979 // accumuated dot products for hemisphere
# define CONSTANT_DOT (.7 / 2)
# define NSAMPLES_SUN_AREA_LIGHT 30 // number of samples to take for an
// non-point sun light
// Helper function - gathers light from sun (emit_skylight)
void GatherSampleSkyLightSSE ( SSE_sampleLightOutput_t & out , directlight_t * dl , int facenum ,
FourVectors const & pos , FourVectors * pNormals , int normalCount , int iThread ,
int nLFlags , int static_prop_index_to_ignore ,
float flEpsilon )
{
bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) ! = 0 ;
bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) ! = 0 ;
fltx4 dot ;
if ( bIgnoreNormals )
dot = ReplicateX4 ( CONSTANT_DOT ) ;
else
dot = NegSIMD ( pNormals [ 0 ] * dl - > light . normal ) ;
dot = MaxSIMD ( dot , Four_Zeros ) ;
int zeroMask = TestSignSIMD ( CmpEqSIMD ( dot , Four_Zeros ) ) ;
if ( zeroMask = = 0xF )
return ;
int nsamples = 1 ;
if ( g_SunAngularExtent > 0.0f )
{
nsamples = NSAMPLES_SUN_AREA_LIGHT ;
if ( do_fast | | force_fast )
nsamples / = 4 ;
}
fltx4 totalFractionVisible = Four_Zeros ;
fltx4 fractionVisible = Four_Zeros ;
DirectionalSampler_t sampler ;
for ( int d = 0 ; d < nsamples ; d + + )
{
// determine visibility of skylight
// serach back to see if we can hit a sky brush
Vector delta ;
VectorScale ( dl - > light . normal , - MAX_TRACE_LENGTH , delta ) ;
if ( d )
{
// jitter light source location
Vector ofs = sampler . NextValue ( ) ;
ofs * = MAX_TRACE_LENGTH * g_SunAngularExtent ;
delta + = ofs ;
}
FourVectors delta4 ;
delta4 . DuplicateVector ( delta ) ;
delta4 + = pos ;
TestLine_DoesHitSky ( pos , delta4 , & fractionVisible , true , static_prop_index_to_ignore ) ;
totalFractionVisible = AddSIMD ( totalFractionVisible , fractionVisible ) ;
}
fltx4 seeAmount = MulSIMD ( totalFractionVisible , ReplicateX4 ( 1.0f / nsamples ) ) ;
out . m_flDot [ 0 ] = MulSIMD ( dot , seeAmount ) ;
out . m_flFalloff = Four_Ones ;
out . m_flSunAmount = MulSIMD ( seeAmount , ReplicateX4 ( 10000.0f ) ) ;
for ( int i = 1 ; i < normalCount ; i + + )
{
if ( bIgnoreNormals )
out . m_flDot [ i ] = ReplicateX4 ( CONSTANT_DOT ) ;
else
{
out . m_flDot [ i ] = NegSIMD ( pNormals [ i ] * dl - > light . normal ) ;
out . m_flDot [ i ] = MulSIMD ( out . m_flDot [ i ] , seeAmount ) ;
}
}
}
// Helper function - gathers light from ambient sky light
void GatherSampleAmbientSkySSE ( SSE_sampleLightOutput_t & out , directlight_t * dl , int facenum ,
FourVectors const & pos , FourVectors * pNormals , int normalCount , int iThread ,
int nLFlags , int static_prop_index_to_ignore ,
float flEpsilon )
{
bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) ! = 0 ;
bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) ! = 0 ;
fltx4 sumdot = Four_Zeros ;
fltx4 ambient_intensity [ NUM_BUMP_VECTS + 1 ] ;
fltx4 possibleHitCount [ NUM_BUMP_VECTS + 1 ] ;
fltx4 dots [ NUM_BUMP_VECTS + 1 ] ;
for ( int i = 0 ; i < normalCount ; i + + )
{
ambient_intensity [ i ] = Four_Zeros ;
possibleHitCount [ i ] = Four_Zeros ;
}
DirectionalSampler_t sampler ;
int nsky_samples = NUMVERTEXNORMALS ;
if ( do_fast | | force_fast )
nsky_samples / = 4 ;
else
nsky_samples * = g_flSkySampleScale ;
for ( int j = 0 ; j < nsky_samples ; j + + )
{
FourVectors anorm ;
anorm . DuplicateVector ( sampler . NextValue ( ) ) ;
if ( bIgnoreNormals )
dots [ 0 ] = ReplicateX4 ( CONSTANT_DOT ) ;
else
dots [ 0 ] = NegSIMD ( pNormals [ 0 ] * anorm ) ;
fltx4 validity = CmpGtSIMD ( dots [ 0 ] , ReplicateX4 ( EQUAL_EPSILON ) ) ;
// No possibility of anybody getting lit
if ( ! TestSignSIMD ( validity ) )
continue ;
dots [ 0 ] = AndSIMD ( validity , dots [ 0 ] ) ;
sumdot = AddSIMD ( dots [ 0 ] , sumdot ) ;
possibleHitCount [ 0 ] = AddSIMD ( AndSIMD ( validity , Four_Ones ) , possibleHitCount [ 0 ] ) ;
for ( int i = 1 ; i < normalCount ; i + + )
{
if ( bIgnoreNormals )
dots [ i ] = ReplicateX4 ( CONSTANT_DOT ) ;
else
dots [ i ] = NegSIMD ( pNormals [ i ] * anorm ) ;
fltx4 validity2 = CmpGtSIMD ( dots [ i ] , ReplicateX4 ( EQUAL_EPSILON ) ) ;
dots [ i ] = AndSIMD ( validity2 , dots [ i ] ) ;
possibleHitCount [ i ] = AddSIMD ( AndSIMD ( AndSIMD ( validity , validity2 ) , Four_Ones ) , possibleHitCount [ i ] ) ;
}
// search back to see if we can hit a sky brush
FourVectors delta = anorm ;
delta * = - MAX_TRACE_LENGTH ;
delta + = pos ;
FourVectors surfacePos = pos ;
FourVectors offset = anorm ;
offset * = - flEpsilon ;
surfacePos - = offset ;
fltx4 fractionVisible = Four_Ones ;
TestLine_DoesHitSky ( surfacePos , delta , & fractionVisible , true , static_prop_index_to_ignore ) ;
for ( int i = 0 ; i < normalCount ; i + + )
{
fltx4 addedAmount = MulSIMD ( fractionVisible , dots [ i ] ) ;
ambient_intensity [ i ] = AddSIMD ( ambient_intensity [ i ] , addedAmount ) ;
}
}
out . m_flFalloff = Four_Ones ;
for ( int i = 0 ; i < normalCount ; i + + )
{
// now scale out the missing parts of the hemisphere of this bump basis vector
fltx4 factor = ReciprocalSIMD ( possibleHitCount [ 0 ] ) ;
factor = MulSIMD ( factor , possibleHitCount [ i ] ) ;
out . m_flDot [ i ] = MulSIMD ( factor , sumdot ) ;
out . m_flDot [ i ] = ReciprocalSIMD ( out . m_flDot [ i ] ) ;
out . m_flDot [ i ] = MulSIMD ( ambient_intensity [ i ] , out . m_flDot [ i ] ) ;
}
}
// Helper function - gathers light from area lights, spot lights, and point lights
void GatherSampleStandardLightSSE ( SSE_sampleLightOutput_t & out , directlight_t * dl , int facenum ,
FourVectors const & pos , FourVectors * pNormals , int normalCount , int iThread ,
int nLFlags , int static_prop_index_to_ignore ,
float flEpsilon )
{
bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) ! = 0 ;
FourVectors src ;
src . DuplicateVector ( vec3_origin ) ;
if ( dl - > facenum = = - 1 )
{
src . DuplicateVector ( dl - > light . origin ) ;
}
// Find light vector
FourVectors delta ;
delta = src ;
delta - = pos ;
fltx4 dist2 = delta . length2 ( ) ;
fltx4 rpcDist = ReciprocalSqrtSIMD ( dist2 ) ;
delta * = rpcDist ;
fltx4 dist = SqrtEstSIMD ( dist2 ) ; //delta.VectorNormalize();
// Compute dot
fltx4 dot = ReplicateX4 ( ( float ) CONSTANT_DOT ) ;
if ( ! bIgnoreNormals )
dot = delta * pNormals [ 0 ] ;
dot = MaxSIMD ( Four_Zeros , dot ) ;
// Affix dot to zero if past fade distz
bool bHasHardFalloff = ( dl - > m_flEndFadeDistance > dl - > m_flStartFadeDistance ) ;
if ( bHasHardFalloff )
{
fltx4 notPastFadeDist = CmpLeSIMD ( dist , ReplicateX4 ( dl - > m_flEndFadeDistance ) ) ;
dot = AndSIMD ( dot , notPastFadeDist ) ; // dot = 0 if past fade distance
if ( ! TestSignSIMD ( notPastFadeDist ) )
return ;
}
dist = MaxSIMD ( dist , Four_Ones ) ;
fltx4 falloffEvalDist = MinSIMD ( dist , ReplicateX4 ( dl - > m_flCapDist ) ) ;
fltx4 constant , linear , quadratic ;
fltx4 dot2 , inCone , inFringe , mult ;
FourVectors offset ;
switch ( dl - > light . type )
{
case emit_point :
constant = ReplicateX4 ( dl - > light . constant_attn ) ;
linear = ReplicateX4 ( dl - > light . linear_attn ) ;
quadratic = ReplicateX4 ( dl - > light . quadratic_attn ) ;
out . m_flFalloff = MulSIMD ( falloffEvalDist , falloffEvalDist ) ;
out . m_flFalloff = MulSIMD ( out . m_flFalloff , quadratic ) ;
out . m_flFalloff = AddSIMD ( out . m_flFalloff , MulSIMD ( linear , falloffEvalDist ) ) ;
out . m_flFalloff = AddSIMD ( out . m_flFalloff , constant ) ;
out . m_flFalloff = ReciprocalSIMD ( out . m_flFalloff ) ;
break ;
case emit_surface :
dot2 = delta * dl - > light . normal ;
dot2 = NegSIMD ( dot2 ) ;
// Light behind surface yields zero dot
dot2 = MaxSIMD ( Four_Zeros , dot2 ) ;
if ( TestSignSIMD ( CmpEqSIMD ( Four_Zeros , dot ) ) = = 0xF )
return ;
out . m_flFalloff = ReciprocalSIMD ( dist2 ) ;
out . m_flFalloff = MulSIMD ( out . m_flFalloff , dot2 ) ;
// move the endpoint away from the surface by epsilon to prevent hitting the surface with the trace
offset . DuplicateVector ( dl - > light . normal ) ;
offset * = DIST_EPSILON ;
src + = offset ;
break ;
case emit_spotlight :
dot2 = delta * dl - > light . normal ;
dot2 = NegSIMD ( dot2 ) ;
// Affix dot2 to zero if outside light cone
inCone = CmpGtSIMD ( dot2 , ReplicateX4 ( dl - > light . stopdot2 ) ) ;
if ( ! TestSignSIMD ( inCone ) )
return ;
dot = AndSIMD ( inCone , dot ) ;
constant = ReplicateX4 ( dl - > light . constant_attn ) ;
linear = ReplicateX4 ( dl - > light . linear_attn ) ;
quadratic = ReplicateX4 ( dl - > light . quadratic_attn ) ;
out . m_flFalloff = MulSIMD ( falloffEvalDist , falloffEvalDist ) ;
out . m_flFalloff = MulSIMD ( out . m_flFalloff , quadratic ) ;
out . m_flFalloff = AddSIMD ( out . m_flFalloff , MulSIMD ( linear , falloffEvalDist ) ) ;
out . m_flFalloff = AddSIMD ( out . m_flFalloff , constant ) ;
out . m_flFalloff = ReciprocalSIMD ( out . m_flFalloff ) ;
out . m_flFalloff = MulSIMD ( out . m_flFalloff , dot2 ) ;
// outside the inner cone
inFringe = CmpLeSIMD ( dot2 , ReplicateX4 ( dl - > light . stopdot ) ) ;
mult = ReplicateX4 ( dl - > light . stopdot - dl - > light . stopdot2 ) ;
mult = ReciprocalSIMD ( mult ) ;
mult = MulSIMD ( mult , SubSIMD ( dot2 , ReplicateX4 ( dl - > light . stopdot2 ) ) ) ;
mult = MinSIMD ( mult , Four_Ones ) ;
mult = MaxSIMD ( mult , Four_Zeros ) ;
// pow is fixed point, so this isn't the most accurate, but it doesn't need to be
if ( ( dl - > light . exponent ! = 0.0f ) & & ( dl - > light . exponent ! = 1.0f ) )
mult = PowSIMD ( mult , dl - > light . exponent ) ;
// if not in between inner and outer cones, mult by 1
mult = AndSIMD ( inFringe , mult ) ;
mult = AddSIMD ( mult , AndNotSIMD ( inFringe , Four_Ones ) ) ;
out . m_flFalloff = MulSIMD ( mult , out . m_flFalloff ) ;
break ;
}
// we may be in the fade region - modulate lighting by the fade curve
//float t = ( dist - dl->m_flStartFadeDistance ) /
// ( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
if ( bHasHardFalloff )
{
fltx4 t = ReplicateX4 ( dl - > m_flEndFadeDistance - dl - > m_flStartFadeDistance ) ;
t = ReciprocalSIMD ( t ) ;
t = MulSIMD ( t , SubSIMD ( dist , ReplicateX4 ( dl - > m_flStartFadeDistance ) ) ) ;
// clamp t to [0...1]
t = MinSIMD ( t , Four_Ones ) ;
t = MaxSIMD ( t , Four_Zeros ) ;
t = SubSIMD ( Four_Ones , t ) ;
// Using QuinticInterpolatingPolynomial, SSE-ified
// t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 )
mult = SubSIMD ( MulSIMD ( ReplicateX4 ( 6.0f ) , t ) , ReplicateX4 ( 15.0f ) ) ;
mult = AddSIMD ( MulSIMD ( mult , t ) , ReplicateX4 ( 10.0f ) ) ;
mult = MulSIMD ( MulSIMD ( t , t ) , mult ) ;
mult = MulSIMD ( t , mult ) ;
out . m_flFalloff = MulSIMD ( mult , out . m_flFalloff ) ;
}
// Raytrace for visibility function
fltx4 fractionVisible = Four_Ones ;
TestLine ( pos , src , & fractionVisible , static_prop_index_to_ignore ) ;
dot = MulSIMD ( fractionVisible , dot ) ;
out . m_flDot [ 0 ] = dot ;
for ( int i = 1 ; i < normalCount ; i + + )
{
if ( bIgnoreNormals )
out . m_flDot [ i ] = ReplicateX4 ( ( float ) CONSTANT_DOT ) ;
else
{
out . m_flDot [ i ] = pNormals [ i ] * delta ;
out . m_flDot [ i ] = MaxSIMD ( Four_Zeros , out . m_flDot [ i ] ) ;
}
}
}
// returns dot product with normal and delta
// dl - light
// pos - position of sample
// normal - surface normal of sample
// out.m_flDot[] - returned dot products with light vector and each normal
// out.m_flFalloff - amount of light falloff
void GatherSampleLightSSE ( SSE_sampleLightOutput_t & out , directlight_t * dl , int facenum ,
FourVectors const & pos , FourVectors * pNormals , int normalCount , int iThread ,
int nLFlags ,
int static_prop_index_to_ignore ,
float flEpsilon )
{
for ( int b = 0 ; b < normalCount ; b + + )
out . m_flDot [ b ] = Four_Zeros ;
out . m_flFalloff = Four_Zeros ;
out . m_flSunAmount = Four_Zeros ;
Assert ( normalCount < = ( NUM_BUMP_VECTS + 1 ) ) ;
// skylights work fundamentally differently than normal lights
switch ( dl - > light . type )
{
case emit_skylight :
GatherSampleSkyLightSSE ( out , dl , facenum , pos , pNormals , normalCount ,
iThread , nLFlags , static_prop_index_to_ignore , flEpsilon ) ;
break ;
case emit_skyambient :
GatherSampleAmbientSkySSE ( out , dl , facenum , pos , pNormals , normalCount ,
iThread , nLFlags , static_prop_index_to_ignore , flEpsilon ) ;
break ;
case emit_point :
case emit_surface :
case emit_spotlight :
GatherSampleStandardLightSSE ( out , dl , facenum , pos , pNormals , normalCount ,
iThread , nLFlags , static_prop_index_to_ignore , flEpsilon ) ;
break ;
default :
Error ( " Bad dl->light.type " ) ;
return ;
}
// NOTE: Notice here that if the light is on the back side of the face
// (tested by checking the dot product of the face normal and the light position)
// we don't want it to contribute to *any* of the bumped lightmaps. It glows
// in disturbing ways if we don't do this.
out . m_flDot [ 0 ] = MaxSIMD ( out . m_flDot [ 0 ] , Four_Zeros ) ;
fltx4 notZero = CmpGtSIMD ( out . m_flDot [ 0 ] , Four_Zeros ) ;
for ( int n = 1 ; n < normalCount ; n + + )
{
out . m_flDot [ n ] = MaxSIMD ( out . m_flDot [ n ] , Four_Zeros ) ;
out . m_flDot [ n ] = AndSIMD ( out . m_flDot [ n ] , notZero ) ;
}
}
/*
= = = = = = = = = = = = =
AddSampleToPatch
Take the sample ' s collected light and
add it back into the apropriate patch
for the radiosity pass .
= = = = = = = = = = = = =
*/
void AddSampleToPatch ( sample_t * s , LightingValue_t & light , int facenum )
{
CPatch * patch ;
Vector mins , maxs ;
int i ;
if ( numbounce = = 0 )
return ;
if ( VectorAvg ( light . m_vecLighting ) < 1 )
return ;
//
// fixed the sample position and normal -- need to find the equiv pos, etc to set up
// patches
//
if ( g_FacePatches . Element ( facenum ) = = g_FacePatches . InvalidIndex ( ) )
return ;
float radius = sqrt ( s - > area ) / 2.0 ;
CPatch * pNextPatch = NULL ;
for ( patch = & g_Patches . Element ( g_FacePatches . Element ( facenum ) ) ; patch ; patch = pNextPatch )
{
// next patch
pNextPatch = NULL ;
if ( patch - > ndxNext ! = g_Patches . InvalidIndex ( ) )
{
pNextPatch = & g_Patches . Element ( patch - > ndxNext ) ;
}
if ( patch - > sky )
continue ;
// skip patches with children
if ( patch - > child1 ! = g_Patches . InvalidIndex ( ) )
continue ;
// see if the point is in this patch (roughly)
WindingBounds ( patch - > winding , mins , maxs ) ;
for ( i = 0 ; i < 3 ; i + + )
{
if ( mins [ i ] > s - > pos [ i ] + radius )
goto nextpatch ;
if ( maxs [ i ] < s - > pos [ i ] - radius )
goto nextpatch ;
}
// add the sample to the patch
patch - > samplearea + = s - > area ;
VectorMA ( patch - > samplelight , s - > area , light . m_vecLighting , patch - > samplelight ) ;
nextpatch : ;
}
// don't worry if some samples don't find a patch
}
void GetPhongNormal ( int facenum , Vector const & spot , Vector & phongnormal )
{
int j ;
dface_t * f = & g_pFaces [ facenum ] ;
// dplane_t *p = &dplanes[f->planenum];
Vector facenormal , vspot ;
VectorCopy ( dplanes [ f - > planenum ] . normal , facenormal ) ;
VectorCopy ( facenormal , phongnormal ) ;
if ( smoothing_threshold ! = 1 )
{
faceneighbor_t * fn = & faceneighbor [ facenum ] ;
// Calculate modified point normal for surface
// Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
// Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
// Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
// Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
for ( j = 0 ; j < f - > numedges ; j + + )
{
Vector v1 , v2 ;
//int e = dsurfedges[f->firstedge + j];
//int e1 = dsurfedges[f->firstedge + ((j+f->numedges-1)%f->numedges)];
//int e2 = dsurfedges[f->firstedge + ((j+1)%f->numedges)];
//edgeshare_t *es = &edgeshare[abs(e)];
//edgeshare_t *es1 = &edgeshare[abs(e1)];
//edgeshare_t *es2 = &edgeshare[abs(e2)];
// dface_t *f2;
float a1 , a2 , aa , bb , ab ;
int vert1 , vert2 ;
Vector & n1 = fn - > normal [ j ] ;
Vector & n2 = fn - > normal [ ( j + 1 ) % f - > numedges ] ;
/*
if ( VectorCompare ( n1 , fn - > facenormal )
& & VectorCompare ( n2 , fn - > facenormal ) )
continue ;
*/
vert1 = EdgeVertex ( f , j ) ;
vert2 = EdgeVertex ( f , j + 1 ) ;
Vector & p1 = dvertexes [ vert1 ] . point ;
Vector & p2 = dvertexes [ vert2 ] . point ;
// Build vectors from the middle of the face to the edge vertexes and the sample pos.
VectorSubtract ( p1 , face_centroids [ facenum ] , v1 ) ;
VectorSubtract ( p2 , face_centroids [ facenum ] , v2 ) ;
VectorSubtract ( spot , face_centroids [ facenum ] , vspot ) ;
aa = DotProduct ( v1 , v1 ) ;
bb = DotProduct ( v2 , v2 ) ;
ab = DotProduct ( v1 , v2 ) ;
a1 = ( bb * DotProduct ( v1 , vspot ) - ab * DotProduct ( vspot , v2 ) ) / ( aa * bb - ab * ab ) ;
a2 = ( DotProduct ( vspot , v2 ) - a1 * ab ) / bb ;
// Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)
if ( a1 > = 0.0 & & a2 > = 0.0 )
{
// calculate distance from edge to pos
Vector temp ;
float scale ;
// Interpolate between the center and edge normals based on sample position
scale = 1.0 - a1 - a2 ;
VectorScale ( fn - > facenormal , scale , phongnormal ) ;
VectorScale ( n1 , a1 , temp ) ;
VectorAdd ( phongnormal , temp , phongnormal ) ;
VectorScale ( n2 , a2 , temp ) ;
VectorAdd ( phongnormal , temp , phongnormal ) ;
Assert ( VectorLength ( phongnormal ) > 1.0e-20 ) ;
VectorNormalize ( phongnormal ) ;
/*
if ( a1 > 1 | | a2 > 1 | | a1 + a2 > 1 )
{
Msg ( " \n %.2f %.2f \n " , a1 , a2 ) ;
Msg ( " %.2f %.2f %.2f \n " , v1 [ 0 ] , v1 [ 1 ] , v1 [ 2 ] ) ;
Msg ( " %.2f %.2f %.2f \n " , v2 [ 0 ] , v2 [ 1 ] , v2 [ 2 ] ) ;
Msg ( " %.2f %.2f %.2f \n " , vspot [ 0 ] , vspot [ 1 ] , vspot [ 2 ] ) ;
exit ( 1 ) ;
a1 = 0 ;
}
*/
/*
phongnormal [ 0 ] = ( ( ( j + 1 ) & 4 ) ! = 0 ) * 255 ;
phongnormal [ 1 ] = ( ( ( j + 1 ) & 2 ) ! = 0 ) * 255 ;
phongnormal [ 2 ] = ( ( ( j + 1 ) & 1 ) ! = 0 ) * 255 ;
*/
return ;
}
}
}
}
void GetPhongNormal ( int facenum , FourVectors const & spot , FourVectors & phongnormal )
{
int j ;
dface_t * f = & g_pFaces [ facenum ] ;
// dplane_t *p = &dplanes[f->planenum];
Vector facenormal ;
FourVectors vspot ;
VectorCopy ( dplanes [ f - > planenum ] . normal , facenormal ) ;
phongnormal . DuplicateVector ( facenormal ) ;
FourVectors faceCentroid ;
faceCentroid . DuplicateVector ( face_centroids [ facenum ] ) ;
if ( smoothing_threshold ! = 1 )
{
faceneighbor_t * fn = & faceneighbor [ facenum ] ;
// Calculate modified point normal for surface
// Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
// Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
// Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
// Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
for ( j = 0 ; j < f - > numedges ; + + j )
{
Vector v1 , v2 ;
fltx4 a1 , a2 ;
float aa , bb , ab ;
int vert1 , vert2 ;
Vector & n1 = fn - > normal [ j ] ;
Vector & n2 = fn - > normal [ ( j + 1 ) % f - > numedges ] ;
vert1 = EdgeVertex ( f , j ) ;
vert2 = EdgeVertex ( f , j + 1 ) ;
Vector & p1 = dvertexes [ vert1 ] . point ;
Vector & p2 = dvertexes [ vert2 ] . point ;
// Build vectors from the middle of the face to the edge vertexes and the sample pos.
VectorSubtract ( p1 , face_centroids [ facenum ] , v1 ) ;
VectorSubtract ( p2 , face_centroids [ facenum ] , v2 ) ;
//VectorSubtract( spot, face_centroids[facenum], vspot );
vspot = spot ;
vspot - = faceCentroid ;
aa = DotProduct ( v1 , v1 ) ;
bb = DotProduct ( v2 , v2 ) ;
ab = DotProduct ( v1 , v2 ) ;
//a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
a1 = ReciprocalSIMD ( ReplicateX4 ( aa * bb - ab * ab ) ) ;
a1 = MulSIMD ( a1 , SubSIMD ( MulSIMD ( ReplicateX4 ( bb ) , vspot * v1 ) , MulSIMD ( ReplicateX4 ( ab ) , vspot * v2 ) ) ) ;
//a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
a2 = ReciprocalSIMD ( ReplicateX4 ( bb ) ) ;
a2 = MulSIMD ( a2 , SubSIMD ( vspot * v2 , MulSIMD ( a1 , ReplicateX4 ( ab ) ) ) ) ;
fltx4 resultMask = AndSIMD ( CmpGeSIMD ( a1 , Four_Zeros ) , CmpGeSIMD ( a2 , Four_Zeros ) ) ;
if ( ! TestSignSIMD ( resultMask ) )
continue ;
// Store the old phong normal to avoid overwriting already computed phong normals
FourVectors oldPhongNormal = phongnormal ;
// calculate distance from edge to pos
FourVectors temp ;
fltx4 scale ;
// Interpolate between the center and edge normals based on sample position
scale = SubSIMD ( SubSIMD ( Four_Ones , a1 ) , a2 ) ;
phongnormal . DuplicateVector ( fn - > facenormal ) ;
phongnormal * = scale ;
temp . DuplicateVector ( n1 ) ;
temp * = a1 ;
phongnormal + = temp ;
temp . DuplicateVector ( n2 ) ;
temp * = a2 ;
phongnormal + = temp ;
// restore the old phong normals
phongnormal . x = AddSIMD ( AndSIMD ( resultMask , phongnormal . x ) , AndNotSIMD ( resultMask , oldPhongNormal . x ) ) ;
phongnormal . y = AddSIMD ( AndSIMD ( resultMask , phongnormal . y ) , AndNotSIMD ( resultMask , oldPhongNormal . y ) ) ;
phongnormal . z = AddSIMD ( AndSIMD ( resultMask , phongnormal . z ) , AndNotSIMD ( resultMask , oldPhongNormal . z ) ) ;
}
phongnormal . VectorNormalize ( ) ;
}
}
int GetVisCache ( int lastoffset , int cluster , byte * pvs )
{
// get the PVS for the pos to limit the number of checks
if ( ! visdatasize )
{
memset ( pvs , 255 , ( dvis - > numclusters + 7 ) / 8 ) ;
lastoffset = - 1 ;
}
else
{
if ( cluster < 0 )
{
// Error, point embedded in wall
// sampled[0][1] = 255;
memset ( pvs , 255 , ( dvis - > numclusters + 7 ) / 8 ) ;
lastoffset = - 1 ;
}
else
{
int thisoffset = dvis - > bitofs [ cluster ] [ DVIS_PVS ] ;
if ( thisoffset ! = lastoffset )
{
if ( thisoffset = = - 1 )
{
Error ( " visofs == -1 " ) ;
}
DecompressVis ( & dvisdata [ thisoffset ] , pvs ) ;
}
lastoffset = thisoffset ;
}
}
return lastoffset ;
}
void BuildPatchLights ( int facenum ) ;
void DumpSamples ( int ndxFace , facelight_t * pFaceLight )
{
ThreadLock ( ) ;
dface_t * pFace = & g_pFaces [ ndxFace ] ;
if ( pFace )
{
bool bBumpped = ( ( texinfo [ pFace - > texinfo ] . flags & SURF_BUMPLIGHT ) ! = 0 ) ;
for ( int iStyle = 0 ; iStyle < 4 ; + + iStyle )
{
if ( pFace - > styles [ iStyle ] ! = 255 )
{
for ( int iBump = 0 ; iBump < 4 ; + + iBump )
{
if ( iBump = = 0 | | ( iBump > 0 & & bBumpped ) )
{
for ( int iSample = 0 ; iSample < pFaceLight - > numsamples ; + + iSample )
{
sample_t * pSample = & pFaceLight - > sample [ iSample ] ;
WriteWinding ( pFileSamples [ iStyle ] [ iBump ] , pSample - > w , pFaceLight - > light [ iStyle ] [ iBump ] [ iSample ] . m_vecLighting ) ;
if ( bDumpNormals )
{
WriteNormal ( pFileSamples [ iStyle ] [ iBump ] , pSample - > pos , pSample - > normal , 15.0f , pSample - > normal * 255.0f ) ;
}
}
}
}
}
}
}
ThreadUnlock ( ) ;
}
//-----------------------------------------------------------------------------
// Allocates light sample data
//-----------------------------------------------------------------------------
static inline void AllocateLightstyleSamples ( facelight_t * fl , int styleIndex , int numnormals )
{
for ( int n = 0 ; n < numnormals ; + + n )
{
fl - > light [ styleIndex ] [ n ] = ( LightingValue_t * ) calloc ( fl - > numsamples , sizeof ( LightingValue_t ) ) ;
}
}
//-----------------------------------------------------------------------------
// Used to find an existing lightstyle on a face
//-----------------------------------------------------------------------------
static inline int FindLightstyle ( dface_t * f , int lightstyle )
{
for ( int k = 0 ; k < MAXLIGHTMAPS ; k + + )
{
if ( f - > styles [ k ] = = lightstyle )
return k ;
}
return - 1 ;
}
static int FindOrAllocateLightstyleSamples ( dface_t * f , facelight_t * fl , int lightstyle , int numnormals )
{
// Search the lightstyles associated with the face for a match
int k ;
for ( k = 0 ; k < MAXLIGHTMAPS ; k + + )
{
if ( f - > styles [ k ] = = lightstyle )
break ;
// Found an empty entry, we can use it for a new lightstyle
if ( f - > styles [ k ] = = 255 )
{
AllocateLightstyleSamples ( fl , k , numnormals ) ;
f - > styles [ k ] = lightstyle ;
break ;
}
}
// Check for overflow
if ( k > = MAXLIGHTMAPS )
return - 1 ;
return k ;
}
//-----------------------------------------------------------------------------
// Compute the illumination point + normal for the sample
//-----------------------------------------------------------------------------
static void ComputeIlluminationPointAndNormalsSSE ( lightinfo_t const & l , FourVectors const & pos , FourVectors const & norm , SSE_SampleInfo_t * pInfo , int numSamples )
{
Vector v [ 4 ] ;
pInfo - > m_Points = pos ;
bool computeNormals = ( pInfo - > m_NormalCount > 1 & & ( pInfo - > m_IsDispFace | | ! l . isflat ) ) ;
// FIXME: move sample point off the surface a bit, this is done so that
// light sampling will not be affected by a bug where raycasts will
// intersect with the face being lit. We really should just have that
// logic in GatherSampleLight
FourVectors faceNormal ;
faceNormal . DuplicateVector ( l . facenormal ) ;
pInfo - > m_Points + = faceNormal ;
if ( pInfo - > m_IsDispFace )
{
pInfo - > m_PointNormals [ 0 ] = norm ;
}
else if ( ! l . isflat )
{
// If the face isn't flat, use a phong-based normal instead
FourVectors modelorg ;
modelorg . DuplicateVector ( l . modelorg ) ;
FourVectors vecSample = pos ;
vecSample - = modelorg ;
GetPhongNormal ( pInfo - > m_FaceNum , vecSample , pInfo - > m_PointNormals [ 0 ] ) ;
}
if ( computeNormals )
{
Vector bv [ 4 ] [ NUM_BUMP_VECTS ] ;
for ( int i = 0 ; i < 4 ; + + i )
{
// TODO: using Vec may slow things down a bit
GetBumpNormals ( pInfo - > m_pTexInfo - > textureVecsTexelsPerWorldUnits [ 0 ] ,
pInfo - > m_pTexInfo - > textureVecsTexelsPerWorldUnits [ 1 ] ,
l . facenormal , pInfo - > m_PointNormals [ 0 ] . Vec ( i ) , bv [ i ] ) ;
}
for ( int b = 0 ; b < NUM_BUMP_VECTS ; + + b )
{
pInfo - > m_PointNormals [ b + 1 ] . LoadAndSwizzle ( bv [ 0 ] [ b ] , bv [ 1 ] [ b ] , bv [ 2 ] [ b ] , bv [ 3 ] [ b ] ) ;
}
}
// TODO: this may slow things down a bit ( using Vec )
for ( int i = 0 ; i < 4 ; + + i )
pInfo - > m_Clusters [ i ] = ClusterFromPoint ( pos . Vec ( i ) ) ;
}
//-----------------------------------------------------------------------------
// Iterates over all lights and computes lighting at up to 4 sample points
//-----------------------------------------------------------------------------
static void GatherSampleLightAt4Points ( SSE_SampleInfo_t & info , int sampleIdx , int numSamples )
{
SSE_sampleLightOutput_t out ;
// Iterate over all direct lights and add them to the particular sample
for ( directlight_t * dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
// is this lights cluster visible?
fltx4 dotMask = Four_Zeros ;
bool skipLight = true ;
for ( int s = 0 ; s < numSamples ; s + + )
{
if ( PVSCheck ( dl - > pvs , info . m_Clusters [ s ] ) )
{
dotMask = SetComponentSIMD ( dotMask , s , 1.0f ) ;
skipLight = false ;
}
}
if ( skipLight )
continue ;
GatherSampleLightSSE ( out , dl , info . m_FaceNum , info . m_Points , info . m_PointNormals , info . m_NormalCount , info . m_iThread ) ;
// Apply the PVS check filter and compute falloff x dot
fltx4 fxdot [ NUM_BUMP_VECTS + 1 ] ;
skipLight = true ;
for ( int b = 0 ; b < info . m_NormalCount ; b + + )
{
fxdot [ b ] = MulSIMD ( out . m_flDot [ b ] , dotMask ) ;
fxdot [ b ] = MulSIMD ( fxdot [ b ] , out . m_flFalloff ) ;
if ( ! IsAllZeros ( fxdot [ b ] ) )
{
skipLight = false ;
}
}
if ( skipLight )
continue ;
// Figure out the lightstyle for this particular sample
int lightStyleIndex = FindOrAllocateLightstyleSamples ( info . m_pFace , info . m_pFaceLight ,
dl - > light . style , info . m_NormalCount ) ;
if ( lightStyleIndex < 0 )
{
if ( info . m_WarnFace ! = info . m_FaceNum )
{
Warning ( " \n WARNING: Too many light styles on a face at (%f, %f, %f) \n " ,
info . m_Points . x . m128_f32 [ 0 ] , info . m_Points . y . m128_f32 [ 0 ] , info . m_Points . z . m128_f32 [ 0 ] ) ;
info . m_WarnFace = info . m_FaceNum ;
}
continue ;
}
// pLightmaps is an array of the lightmaps for each normal direction,
// here's where the result of the sample gathering goes
LightingValue_t * * pLightmaps = info . m_pFaceLight - > light [ lightStyleIndex ] ;
// Incremental lighting only cares about lightstyle zero
if ( g_pIncremental & & ( dl - > light . style = = 0 ) )
{
for ( int i = 0 ; i < numSamples ; i + + )
{
g_pIncremental - > AddLightToFace ( dl - > m_IncrementalID , info . m_FaceNum , sampleIdx + i ,
info . m_LightmapSize , SubFloat ( fxdot [ 0 ] , i ) , info . m_iThread ) ;
}
}
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
for ( int i = 0 ; i < numSamples ; i + + )
{
pLightmaps [ n ] [ sampleIdx + i ] . AddLight ( SubFloat ( fxdot [ n ] , i ) , dl - > light . intensity , SubFloat ( out . m_flSunAmount , i ) ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Iterates over all lights and computes lighting at a sample point
//-----------------------------------------------------------------------------
static void ResampleLightAt4Points ( SSE_SampleInfo_t & info , int lightStyleIndex , int flags , LightingValue_t pLightmap [ 4 ] [ NUM_BUMP_VECTS + 1 ] )
{
SSE_sampleLightOutput_t out ;
// Clear result
for ( int i = 0 ; i < 4 ; + + i )
{
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
pLightmap [ i ] [ n ] . Zero ( ) ;
}
}
// Iterate over all direct lights and add them to the particular sample
for ( directlight_t * dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
if ( ( flags & AMBIENT_ONLY ) & & ( dl - > light . type ! = emit_skyambient ) )
continue ;
if ( ( flags & NON_AMBIENT_ONLY ) & & ( dl - > light . type = = emit_skyambient ) )
continue ;
// Only add contributions that match the lightstyle
Assert ( lightStyleIndex < = MAXLIGHTMAPS ) ;
Assert ( info . m_pFace - > styles [ lightStyleIndex ] ! = 255 ) ;
if ( dl - > light . style ! = info . m_pFace - > styles [ lightStyleIndex ] )
continue ;
// is this lights cluster visible?
fltx4 dotMask = Four_Zeros ;
bool skipLight = true ;
for ( int s = 0 ; s < 4 ; s + + )
{
if ( PVSCheck ( dl - > pvs , info . m_Clusters [ s ] ) )
{
dotMask = SetComponentSIMD ( dotMask , s , 1.0f ) ;
skipLight = false ;
}
}
if ( skipLight )
continue ;
// NOTE: Notice here that if the light is on the back side of the face
// (tested by checking the dot product of the face normal and the light position)
// we don't want it to contribute to *any* of the bumped lightmaps. It glows
// in disturbing ways if we don't do this.
GatherSampleLightSSE ( out , dl , info . m_FaceNum , info . m_Points , info . m_PointNormals , info . m_NormalCount , info . m_iThread ) ;
// Apply the PVS check filter and compute falloff x dot
fltx4 fxdot [ NUM_BUMP_VECTS + 1 ] ;
for ( int b = 0 ; b < info . m_NormalCount ; b + + )
{
fxdot [ b ] = MulSIMD ( out . m_flFalloff , out . m_flDot [ b ] ) ;
fxdot [ b ] = MulSIMD ( fxdot [ b ] , dotMask ) ;
}
// Compute the contributions to each of the bumped lightmaps
// The first sample is for non-bumped lighting.
// The other sample are for bumpmapping.
for ( int i = 0 ; i < 4 ; + + i )
{
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
pLightmap [ i ] [ n ] . AddLight ( SubFloat ( fxdot [ n ] , i ) , dl - > light . intensity , SubFloat ( out . m_flSunAmount , i ) ) ;
}
}
}
}
bool PointsInWinding ( FourVectors const & point , winding_t * w , int & invalidBits )
{
FourVectors edge , toPt , cross , testCross , p0 , p1 ;
fltx4 invalidMask ;
//
// get the first normal to test
//
p0 . DuplicateVector ( w - > p [ 0 ] ) ;
p1 . DuplicateVector ( w - > p [ 1 ] ) ;
toPt = point ;
toPt - = p0 ;
edge = p1 ;
edge - = p0 ;
testCross = edge ^ toPt ;
testCross . VectorNormalizeFast ( ) ;
for ( int ndxPt = 1 ; ndxPt < w - > numpoints ; ndxPt + + )
{
p0 . DuplicateVector ( w - > p [ ndxPt ] ) ;
p1 . DuplicateVector ( w - > p [ ( ndxPt + 1 ) % w - > numpoints ] ) ;
toPt = point ;
toPt - = p0 ;
edge = p1 ;
edge - = p0 ;
cross = edge ^ toPt ;
cross . VectorNormalizeFast ( ) ;
fltx4 dot = cross * testCross ;
invalidMask = OrSIMD ( invalidMask , CmpLtSIMD ( dot , Four_Zeros ) ) ;
invalidBits = TestSignSIMD ( invalidMask ) ;
if ( invalidBits = = 0xF )
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Perform supersampling at a particular point
//-----------------------------------------------------------------------------
static int SupersampleLightAtPoint ( lightinfo_t & l , SSE_SampleInfo_t & info ,
int sampleIndex , int lightStyleIndex , LightingValue_t * pLight , int flags )
{
sample_t & sample = info . m_pFaceLight - > sample [ sampleIndex ] ;
// Get the position of the original sample in lightmapspace
Vector2D temp ;
WorldToLuxelSpace ( & l , sample . pos , temp ) ;
Vector sampleLightOrigin ( temp [ 0 ] , temp [ 1 ] , 0.0f ) ;
// Some parameters related to supersampling
float sampleWidth = ( flags & NON_AMBIENT_ONLY ) ? 4 : 2 ;
float cscale = 1.0f / sampleWidth ;
float csshift = - ( ( sampleWidth - 1 ) * cscale ) / 2.0 ;
// Clear out the light values
for ( int i = 0 ; i < info . m_NormalCount ; + + i )
pLight [ i ] . Zero ( ) ;
int subsampleCount = 0 ;
FourVectors superSampleNormal ;
superSampleNormal . DuplicateVector ( sample . normal ) ;
FourVectors superSampleLightCoord ;
FourVectors superSamplePosition ;
if ( flags & NON_AMBIENT_ONLY )
{
float aRow [ 4 ] ;
for ( int coord = 0 ; coord < 4 ; + + coord )
aRow [ coord ] = csshift + coord * cscale ;
fltx4 sseRow = LoadUnalignedSIMD ( aRow ) ;
for ( int s = 0 ; s < 4 ; + + s )
{
// make sure the coordinate is inside of the sample's winding and when normalizing
// below use the number of samples used, not just numsamples and some of them
// will be skipped if they are not inside of the winding
superSampleLightCoord . DuplicateVector ( sampleLightOrigin ) ;
superSampleLightCoord . x = AddSIMD ( superSampleLightCoord . x , ReplicateX4 ( aRow [ s ] ) ) ;
superSampleLightCoord . y = AddSIMD ( superSampleLightCoord . y , sseRow ) ;
// Figure out where the supersample exists in the world, and make sure
// it lies within the sample winding
LuxelSpaceToWorld ( & l , superSampleLightCoord [ 0 ] , superSampleLightCoord [ 1 ] , superSamplePosition ) ;
// A winding should exist only if the sample wasn't a uniform luxel, or if g_bDumpPatches is true.
int invalidBits = 0 ;
if ( sample . w & & ! PointsInWinding ( superSamplePosition , sample . w , invalidBits ) )
continue ;
// Compute the super-sample illumination point and normal
// We're assuming the flat normal is the same for all supersamples
ComputeIlluminationPointAndNormalsSSE ( l , superSamplePosition , superSampleNormal , & info , 4 ) ;
// Resample the non-ambient light at this point...
LightingValue_t result [ 4 ] [ NUM_BUMP_VECTS + 1 ] ;
ResampleLightAt4Points ( info , lightStyleIndex , NON_AMBIENT_ONLY , result ) ;
// Got more subsamples
for ( int i = 0 ; i < 4 ; i + + )
{
if ( ! ( ( invalidBits > > i ) & 0x1 ) )
{
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
pLight [ n ] . AddLight ( result [ i ] [ n ] ) ;
}
+ + subsampleCount ;
}
}
}
}
else
{
FourVectors superSampleOffsets ;
superSampleOffsets . LoadAndSwizzle ( Vector ( csshift , csshift , 0 ) , Vector ( csshift , csshift + cscale , 0 ) ,
Vector ( csshift + cscale , csshift , 0 ) , Vector ( csshift + cscale , csshift + cscale , 0 ) ) ;
superSampleLightCoord . DuplicateVector ( sampleLightOrigin ) ;
superSampleLightCoord + = superSampleOffsets ;
LuxelSpaceToWorld ( & l , superSampleLightCoord [ 0 ] , superSampleLightCoord [ 1 ] , superSamplePosition ) ;
int invalidBits = 0 ;
if ( sample . w & & ! PointsInWinding ( superSamplePosition , sample . w , invalidBits ) )
return 0 ;
ComputeIlluminationPointAndNormalsSSE ( l , superSamplePosition , superSampleNormal , & info , 4 ) ;
LightingValue_t result [ 4 ] [ NUM_BUMP_VECTS + 1 ] ;
ResampleLightAt4Points ( info , lightStyleIndex , AMBIENT_ONLY , result ) ;
// Got more subsamples
for ( int i = 0 ; i < 4 ; i + + )
{
if ( ! ( ( invalidBits > > i ) & 0x1 ) )
{
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
pLight [ n ] . AddLight ( result [ i ] [ n ] ) ;
}
+ + subsampleCount ;
}
}
}
return subsampleCount ;
}
//-----------------------------------------------------------------------------
// Compute gradients of a lightmap
//-----------------------------------------------------------------------------
static void ComputeLightmapGradients ( SSE_SampleInfo_t & info , bool const * pHasProcessedSample ,
float * pIntensity , float * gradient )
{
int w = info . m_LightmapWidth ;
int h = info . m_LightmapHeight ;
facelight_t * fl = info . m_pFaceLight ;
for ( int i = 0 ; i < fl - > numsamples ; i + + )
{
// Don't supersample the same sample twice
if ( pHasProcessedSample [ i ] )
continue ;
gradient [ i ] = 0.0f ;
sample_t & sample = fl - > sample [ i ] ;
// Choose the maximum gradient of all bumped lightmap intensities
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
int j = n * info . m_LightmapSize + sample . s + sample . t * w ;
if ( sample . t > 0 )
{
if ( sample . s > 0 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j - 1 - w ] ) ) ;
gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j - w ] ) ) ;
if ( sample . s < w - 1 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j + 1 - w ] ) ) ;
}
if ( sample . t < h - 1 )
{
if ( sample . s > 0 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j - 1 + w ] ) ) ;
gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j + w ] ) ) ;
if ( sample . s < w - 1 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j + 1 + w ] ) ) ;
}
if ( sample . s > 0 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j - 1 ] ) ) ;
if ( sample . s < w - 1 ) gradient [ i ] = max ( gradient [ i ] , fabs ( pIntensity [ j ] - pIntensity [ j + 1 ] ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// ComputeLuxelIntensity...
//-----------------------------------------------------------------------------
static inline void ComputeLuxelIntensity ( SSE_SampleInfo_t & info , int sampleIdx ,
LightingValue_t * * ppLightSamples , float * pSampleIntensity )
{
// Compute a separate intensity for each
sample_t & sample = info . m_pFaceLight - > sample [ sampleIdx ] ;
int destIdx = sample . s + sample . t * info . m_LightmapWidth ;
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
float intensity = ppLightSamples [ n ] [ sampleIdx ] . Intensity ( ) ;
// convert to a linear perception space
pSampleIntensity [ n * info . m_LightmapSize + destIdx ] = pow ( intensity / 256.0 , 1.0 / 2.2 ) ;
}
}
//-----------------------------------------------------------------------------
// Compute the maximum intensity based on all bumped lighting
//-----------------------------------------------------------------------------
static void ComputeSampleIntensities ( SSE_SampleInfo_t & info , LightingValue_t * * ppLightSamples , float * pSampleIntensity )
{
for ( int i = 0 ; i < info . m_pFaceLight - > numsamples ; i + + )
{
ComputeLuxelIntensity ( info , i , ppLightSamples , pSampleIntensity ) ;
}
}
//-----------------------------------------------------------------------------
// Perform supersampling on a particular lightstyle
//-----------------------------------------------------------------------------
static void BuildSupersampleFaceLights ( lightinfo_t & l , SSE_SampleInfo_t & info , int lightstyleIndex )
{
LightingValue_t pAmbientLight [ NUM_BUMP_VECTS + 1 ] ;
LightingValue_t pDirectLight [ NUM_BUMP_VECTS + 1 ] ;
// This is used to make sure we don't supersample a light sample more than once
int processedSampleSize = info . m_LightmapSize * sizeof ( bool ) ;
bool * pHasProcessedSample = ( bool * ) stackalloc ( processedSampleSize ) ;
memset ( pHasProcessedSample , 0 , processedSampleSize ) ;
// This is used to compute a simple gradient computation of the light samples
// We're going to store the maximum intensity of all bumped samples at each sample location
float * pGradient = ( float * ) stackalloc ( info . m_pFaceLight - > numsamples * sizeof ( float ) ) ;
float * pSampleIntensity = ( float * ) stackalloc ( info . m_NormalCount * info . m_LightmapSize * sizeof ( float ) ) ;
// Compute the maximum intensity of all lighting associated with this lightstyle
// for all bumped lighting
LightingValue_t * * ppLightSamples = info . m_pFaceLight - > light [ lightstyleIndex ] ;
ComputeSampleIntensities ( info , ppLightSamples , pSampleIntensity ) ;
Vector * pVisualizePass = NULL ;
if ( debug_extra )
{
int visualizationSize = info . m_pFaceLight - > numsamples * sizeof ( Vector ) ;
pVisualizePass = ( Vector * ) stackalloc ( visualizationSize ) ;
memset ( pVisualizePass , 0 , visualizationSize ) ;
}
// What's going on here is that we're looking for large lighting discontinuities
// (large light intensity gradients) as a clue that we should probably be supersampling
// in that area. Because the supersampling operation will cause lighting changes,
// we've found that it's good to re-check the gradients again and see if any other
// areas should be supersampled as a result of the previous pass. Keep going
// until all the gradients are reasonable or until we hit a max number of passes
bool do_anotherpass = true ;
int pass = 1 ;
while ( do_anotherpass & & pass < = extrapasses )
{
// Look for lighting discontinuities to see what we should be supersampling
ComputeLightmapGradients ( info , pHasProcessedSample , pSampleIntensity , pGradient ) ;
do_anotherpass = false ;
// Now check all of the samples and supersample those which we have
// marked as having high gradients
for ( int i = 0 ; i < info . m_pFaceLight - > numsamples ; + + i )
{
// Don't supersample the same sample twice
if ( pHasProcessedSample [ i ] )
continue ;
// Don't supersample if the lighting is pretty uniform near the sample
if ( pGradient [ i ] < 0.0625 )
continue ;
// Joy! We're supersampling now, and we therefore must do another pass
// Also, we need never bother with this sample again
pHasProcessedSample [ i ] = true ;
do_anotherpass = true ;
if ( debug_extra )
{
// Mark the little visualization bitmap with a color indicating
// which pass it was updated on.
pVisualizePass [ i ] [ 0 ] = ( pass & 1 ) * 255 ;
pVisualizePass [ i ] [ 1 ] = ( pass & 2 ) * 128 ;
pVisualizePass [ i ] [ 2 ] = ( pass & 4 ) * 64 ;
}
// Supersample the ambient light for each bump direction vector
int ambientSupersampleCount = SupersampleLightAtPoint ( l , info , i , lightstyleIndex , pAmbientLight , AMBIENT_ONLY ) ;
// Supersample the non-ambient light for each bump direction vector
int directSupersampleCount = SupersampleLightAtPoint ( l , info , i , lightstyleIndex , pDirectLight , NON_AMBIENT_ONLY ) ;
// Because of sampling problems, small area triangles may have no samples.
// In this case, just use what we already have
if ( ambientSupersampleCount > 0 & & directSupersampleCount > 0 )
{
// Add the ambient + directional terms together, stick it back into the lightmap
for ( int n = 0 ; n < info . m_NormalCount ; + + n )
{
ppLightSamples [ n ] [ i ] . Zero ( ) ;
ppLightSamples [ n ] [ i ] . AddWeighted ( pDirectLight [ n ] , 1.0f / directSupersampleCount ) ;
ppLightSamples [ n ] [ i ] . AddWeighted ( pAmbientLight [ n ] , 1.0f / ambientSupersampleCount ) ;
}
// Recompute the luxel intensity based on the supersampling
ComputeLuxelIntensity ( info , i , ppLightSamples , pSampleIntensity ) ;
}
}
// We've finished another pass
pass + + ;
}
if ( debug_extra )
{
// Copy colors representing which supersample pass the sample was messed with
// into the actual lighting values so we can visualize it
for ( int i = 0 ; i < info . m_pFaceLight - > numsamples ; + + i )
{
for ( int j = 0 ; j < info . m_NormalCount ; + + j )
{
VectorCopy ( pVisualizePass [ i ] , ppLightSamples [ j ] [ i ] . m_vecLighting ) ;
}
}
}
}
void InitLightinfo ( lightinfo_t * pl , int facenum )
{
dface_t * f ;
f = & g_pFaces [ facenum ] ;
memset ( pl , 0 , sizeof ( * pl ) ) ;
pl - > facenum = facenum ;
pl - > face = f ;
//
// rotate plane
//
VectorCopy ( dplanes [ f - > planenum ] . normal , pl - > facenormal ) ;
pl - > facedist = dplanes [ f - > planenum ] . dist ;
// get the origin offset for rotating bmodels
VectorCopy ( face_offset [ facenum ] , pl - > modelorg ) ;
CalcFaceVectors ( pl ) ;
// figure out if the surface is flat
pl - > isflat = true ;
if ( smoothing_threshold ! = 1 )
{
faceneighbor_t * fn = & faceneighbor [ facenum ] ;
for ( int j = 0 ; j < f - > numedges ; j + + )
{
float dot = DotProduct ( pl - > facenormal , fn - > normal [ j ] ) ;
if ( dot < 1.0 - EQUAL_EPSILON )
{
pl - > isflat = false ;
break ;
}
}
}
}
static void InitSampleInfo ( lightinfo_t const & l , int iThread , SSE_SampleInfo_t & info )
{
info . m_LightmapWidth = l . face - > m_LightmapTextureSizeInLuxels [ 0 ] + 1 ;
info . m_LightmapHeight = l . face - > m_LightmapTextureSizeInLuxels [ 1 ] + 1 ;
info . m_LightmapSize = info . m_LightmapWidth * info . m_LightmapHeight ;
// How many lightmaps are we going to need?
info . m_pTexInfo = & texinfo [ l . face - > texinfo ] ;
info . m_NormalCount = ( info . m_pTexInfo - > flags & SURF_BUMPLIGHT ) ? NUM_BUMP_VECTS + 1 : 1 ;
info . m_FaceNum = l . facenum ;
info . m_pFace = l . face ;
info . m_pFaceLight = & facelight [ info . m_FaceNum ] ;
info . m_IsDispFace = ValidDispFace ( info . m_pFace ) ;
info . m_iThread = iThread ;
info . m_WarnFace = - 1 ;
info . m_NumSamples = info . m_pFaceLight - > numsamples ;
info . m_NumSampleGroups = ( info . m_NumSamples & 0x3 ) ? ( info . m_NumSamples / 4 ) + 1 : ( info . m_NumSamples / 4 ) ;
// initialize normals if the surface is flat
if ( l . isflat )
{
info . m_PointNormals [ 0 ] . DuplicateVector ( l . facenormal ) ;
// use facenormal along with the smooth normal to build the three bump map vectors
if ( info . m_NormalCount > 1 )
{
Vector bumpVects [ NUM_BUMP_VECTS ] ;
GetBumpNormals ( info . m_pTexInfo - > textureVecsTexelsPerWorldUnits [ 0 ] ,
info . m_pTexInfo - > textureVecsTexelsPerWorldUnits [ 1 ] , l . facenormal ,
l . facenormal , bumpVects ) ; //&info.m_PointNormal[1] );
for ( int b = 0 ; b < NUM_BUMP_VECTS ; + + b )
{
info . m_PointNormals [ b + 1 ] . DuplicateVector ( bumpVects [ b ] ) ;
}
}
}
}
void BuildFacelights ( int iThread , int facenum )
{
int i , j ;
lightinfo_t l ;
dface_t * f ;
facelight_t * fl ;
SSE_SampleInfo_t sampleInfo ;
directlight_t * dl ;
Vector spot ;
Vector v [ 4 ] , n [ 4 ] ;
if ( g_bInterrupt )
return ;
// FIXME: Is there a better way to do this? Like, in RunThreadsOn, for instance?
// Don't pay this cost unless we have to; this is super perf-critical code.
if ( g_pIncremental )
{
// Both threads will be accessing this so it needs to be protected or else thread A
// will load it in and thread B will increment it but its increment will be
// overwritten by thread A when thread A writes it back.
ThreadLock ( ) ;
+ + g_iCurFace ;
ThreadUnlock ( ) ;
}
// some surfaces don't need lightmaps
f = & g_pFaces [ facenum ] ;
f - > lightofs = - 1 ;
for ( j = 0 ; j < MAXLIGHTMAPS ; j + + )
f - > styles [ j ] = 255 ;
// Trivial-reject the whole face?
if ( ! ( g_FacesVisibleToLights [ facenum > > 3 ] & ( 1 < < ( facenum & 7 ) ) ) )
return ;
if ( texinfo [ f - > texinfo ] . flags & TEX_SPECIAL )
return ; // non-lit texture
// check for patches for this face. If none it must be degenerate. Ignore.
if ( g_FacePatches . Element ( facenum ) = = g_FacePatches . InvalidIndex ( ) )
return ;
fl = & facelight [ facenum ] ;
InitLightinfo ( & l , facenum ) ;
CalcPoints ( & l , fl , facenum ) ;
InitSampleInfo ( l , iThread , sampleInfo ) ;
// Allocate sample positions/normals to SSE
int numGroups = ( fl - > numsamples & 0x3 ) ? ( fl - > numsamples / 4 ) + 1 : ( fl - > numsamples / 4 ) ;
// always allocate style 0 lightmap
f - > styles [ 0 ] = 0 ;
AllocateLightstyleSamples ( fl , 0 , sampleInfo . m_NormalCount ) ;
// sample the lights at each sample location
for ( int grp = 0 ; grp < numGroups ; + + grp )
{
int nSample = 4 * grp ;
sample_t * sample = sampleInfo . m_pFaceLight - > sample + nSample ;
int numSamples = min ( 4 , sampleInfo . m_pFaceLight - > numsamples - nSample ) ;
FourVectors positions ;
FourVectors normals ;
for ( int i = 0 ; i < 4 ; i + + )
{
v [ i ] = ( i < numSamples ) ? sample [ i ] . pos : sample [ numSamples - 1 ] . pos ;
n [ i ] = ( i < numSamples ) ? sample [ i ] . normal : sample [ numSamples - 1 ] . normal ;
}
positions . LoadAndSwizzle ( v [ 0 ] , v [ 1 ] , v [ 2 ] , v [ 3 ] ) ;
normals . LoadAndSwizzle ( n [ 0 ] , n [ 1 ] , n [ 2 ] , n [ 3 ] ) ;
ComputeIlluminationPointAndNormalsSSE ( l , positions , normals , & sampleInfo , numSamples ) ;
// Fixup sample normals in case of smooth faces
if ( ! l . isflat )
{
for ( int i = 0 ; i < numSamples ; i + + )
sample [ i ] . normal = sampleInfo . m_PointNormals [ 0 ] . Vec ( i ) ;
}
// Iterate over all the lights and add their contribution to this group of spots
GatherSampleLightAt4Points ( sampleInfo , nSample , numSamples ) ;
}
// Tell the incremental light manager that we're done with this face.
if ( g_pIncremental )
{
for ( dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
// Only deal with lightstyle 0 for incremental lighting
if ( dl - > light . style = = 0 )
g_pIncremental - > FinishFace ( dl - > m_IncrementalID , facenum , iThread ) ;
}
// Don't have to deal with patch lights (only direct lighting is used)
// or supersampling
return ;
}
// get rid of the -extra functionality on displacement surfaces
if ( do_extra & & ! sampleInfo . m_IsDispFace )
{
// For each lightstyle, perform a supersampling pass
for ( i = 0 ; i < MAXLIGHTMAPS ; + + i )
{
// Stop when we run out of lightstyles
if ( f - > styles [ i ] = = 255 )
break ;
BuildSupersampleFaceLights ( l , sampleInfo , i ) ;
}
}
if ( ! g_bUseMPI )
{
//
// This is done on the master node when MPI is used
//
BuildPatchLights ( facenum ) ;
}
if ( g_bDumpPatches )
{
DumpSamples ( facenum , fl ) ;
}
else
{
FreeSampleWindings ( fl ) ;
}
}
void BuildPatchLights ( int facenum )
{
int i , k ;
CPatch * patch ;
dface_t * f = & g_pFaces [ facenum ] ;
facelight_t * fl = & facelight [ facenum ] ;
for ( k = 0 ; k < MAXLIGHTMAPS ; k + + )
{
if ( f - > styles [ k ] = = 0 )
break ;
}
if ( k > = MAXLIGHTMAPS )
return ;
for ( i = 0 ; i < fl - > numsamples ; i + + )
{
AddSampleToPatch ( & fl - > sample [ i ] , fl - > light [ k ] [ 0 ] [ i ] , facenum ) ;
}
// check for a valid face
if ( g_FacePatches . Element ( facenum ) = = g_FacePatches . InvalidIndex ( ) )
return ;
// push up sampled light to parents (children always exist first in the list)
CPatch * pNextPatch ;
for ( patch = & g_Patches . Element ( g_FacePatches . Element ( facenum ) ) ; patch ; patch = pNextPatch )
{
// next patch
pNextPatch = NULL ;
if ( patch - > ndxNext ! = g_Patches . InvalidIndex ( ) )
{
pNextPatch = & g_Patches . Element ( patch - > ndxNext ) ;
}
// skip patches without parents
if ( patch - > parent = = g_Patches . InvalidIndex ( ) )
// if (patch->parent == -1)
continue ;
CPatch * parent = & g_Patches . Element ( patch - > parent ) ;
parent - > samplearea + = patch - > samplearea ;
VectorAdd ( parent - > samplelight , patch - > samplelight , parent - > samplelight ) ;
}
// average up the direct light on each patch for radiosity
if ( numbounce > 0 )
{
for ( patch = & g_Patches . Element ( g_FacePatches . Element ( facenum ) ) ; patch ; patch = pNextPatch )
{
// next patch
pNextPatch = NULL ;
if ( patch - > ndxNext ! = g_Patches . InvalidIndex ( ) )
{
pNextPatch = & g_Patches . Element ( patch - > ndxNext ) ;
}
if ( patch - > samplearea )
{
float scale ;
Vector v ;
scale = 1.0 / patch - > samplearea ;
VectorScale ( patch - > samplelight , scale , v ) ;
VectorAdd ( patch - > totallight . light [ 0 ] , v , patch - > totallight . light [ 0 ] ) ;
VectorAdd ( patch - > directlight , v , patch - > directlight ) ;
}
}
}
// pull totallight from children (children always exist first in the list)
for ( patch = & g_Patches . Element ( g_FacePatches . Element ( facenum ) ) ; patch ; patch = pNextPatch )
{
// next patch
pNextPatch = NULL ;
if ( patch - > ndxNext ! = g_Patches . InvalidIndex ( ) )
{
pNextPatch = & g_Patches . Element ( patch - > ndxNext ) ;
}
if ( patch - > child1 ! = g_Patches . InvalidIndex ( ) )
{
float s1 , s2 ;
CPatch * child1 ;
CPatch * child2 ;
child1 = & g_Patches . Element ( patch - > child1 ) ;
child2 = & g_Patches . Element ( patch - > child2 ) ;
s1 = child1 - > area / ( child1 - > area + child2 - > area ) ;
s2 = child2 - > area / ( child1 - > area + child2 - > area ) ;
VectorScale ( child1 - > totallight . light [ 0 ] , s1 , patch - > totallight . light [ 0 ] ) ;
VectorMA ( patch - > totallight . light [ 0 ] , s2 , child2 - > totallight . light [ 0 ] , patch - > totallight . light [ 0 ] ) ;
VectorCopy ( patch - > totallight . light [ 0 ] , patch - > directlight ) ;
}
}
bool needsBumpmap = false ;
if ( texinfo [ f - > texinfo ] . flags & SURF_BUMPLIGHT )
{
needsBumpmap = true ;
}
// add an ambient term if desired
if ( ambient [ 0 ] | | ambient [ 1 ] | | ambient [ 2 ] )
{
for ( int j = 0 ; j < MAXLIGHTMAPS & & f - > styles [ j ] ! = 255 ; j + + )
{
if ( f - > styles [ j ] = = 0 )
{
for ( i = 0 ; i < fl - > numsamples ; i + + )
{
fl - > light [ j ] [ 0 ] [ i ] . m_vecLighting + = ambient ;
if ( needsBumpmap )
{
fl - > light [ j ] [ 1 ] [ i ] . m_vecLighting + = ambient ;
fl - > light [ j ] [ 2 ] [ i ] . m_vecLighting + = ambient ;
fl - > light [ j ] [ 3 ] [ i ] . m_vecLighting + = ambient ;
}
}
break ;
}
}
}
// light from dlight_threshold and above is sent out, but the
// texture itself should still be full bright
#if 0
// if( VectorAvg( g_FacePatches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow
{
for ( j = 0 ; j < MAXLIGHTMAPS & & f - > styles [ j ] ! = 255 ; j + + )
{
if ( f - > styles [ j ] = = 0 )
{
// BUG: shouldn't this be done for all patches on the face?
for ( i = 0 ; i < fl - > numsamples ; i + + )
{
// garymctchange
VectorAdd ( fl - > light [ j ] [ 0 ] [ i ] , g_FacePatches [ facenum ] - > baselight , fl - > light [ j ] [ 0 ] [ i ] ) ;
if ( needsBumpmap )
{
for ( bumpSample = 1 ; bumpSample < NUM_BUMP_VECTS + 1 ; bumpSample + + )
{
VectorAdd ( fl - > light [ j ] [ bumpSample ] [ i ] , g_FacePatches [ facenum ] - > baselight , fl - > light [ j ] [ bumpSample ] [ i ] ) ;
}
}
}
break ;
}
}
}
# endif
}
/*
= = = = = = = = = = = = =
PrecompLightmapOffsets
= = = = = = = = = = = = =
*/
void PrecompLightmapOffsets ( )
{
int facenum ;
dface_t * f ;
int lightstyles ;
int lightdatasize = 0 ;
// NOTE: We store avg face light data in this lump *before* the lightmap data itself
// in *reverse order* of the way the lightstyles appear in the styles array.
for ( facenum = 0 ; facenum < numfaces ; facenum + + )
{
f = & g_pFaces [ facenum ] ;
if ( texinfo [ f - > texinfo ] . flags & TEX_SPECIAL )
continue ; // non-lit texture
if ( dlight_map ! = 0 )
f - > styles [ 1 ] = 0 ;
for ( lightstyles = 0 ; lightstyles < MAXLIGHTMAPS ; lightstyles + + )
{
if ( f - > styles [ lightstyles ] = = 255 )
break ;
}
if ( ! lightstyles )
continue ;
// Reserve room for the avg light color data
lightdatasize + = lightstyles * 4 ;
f - > lightofs = lightdatasize ;
bool needsBumpmap = false ;
if ( texinfo [ f - > texinfo ] . flags & SURF_BUMPLIGHT )
{
needsBumpmap = true ;
}
int nLuxels = ( f - > m_LightmapTextureSizeInLuxels [ 0 ] + 1 ) * ( f - > m_LightmapTextureSizeInLuxels [ 1 ] + 1 ) ;
if ( needsBumpmap )
{
lightdatasize + = nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 ) ;
}
else
{
lightdatasize + = nLuxels * 4 * lightstyles ;
}
}
// The incremental lighting code needs us to preserve the contents of dlightdata
// since it only recomposites lighting for faces that have lights that touch them.
if ( g_pIncremental & & pdlightdata - > Count ( ) )
return ;
pdlightdata - > SetSize ( lightdatasize ) ;
}
// Clamp the three values for bumped lighting such that we trade off directionality for brightness.
static void ColorClampBumped ( Vector & color1 , Vector & color2 , Vector & color3 )
{
Vector maxs ;
Vector * colors [ 3 ] = { & color1 , & color2 , & color3 } ;
maxs [ 0 ] = VectorMaximum ( color1 ) ;
maxs [ 1 ] = VectorMaximum ( color2 ) ;
maxs [ 2 ] = VectorMaximum ( color3 ) ;
// HACK! Clean this up, and add some else statements
# define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
int order [ 3 ] ;
CONDITION ( 0 , 1 , 2 ) ;
CONDITION ( 0 , 2 , 1 ) ;
CONDITION ( 1 , 0 , 2 ) ;
CONDITION ( 1 , 2 , 0 ) ;
CONDITION ( 2 , 0 , 1 ) ;
CONDITION ( 2 , 1 , 0 ) ;
int i ;
for ( i = 0 ; i < 3 ; i + + )
{
float max = VectorMaximum ( * colors [ order [ i ] ] ) ;
if ( max < = 1.0f )
{
continue ;
}
// This channel is too bright. . take half of the amount that we are over and
// add it to the other two channel.
float factorToRedist = ( max - 1.0f ) / max ;
Vector colorToRedist = factorToRedist * * colors [ order [ i ] ] ;
* colors [ order [ i ] ] - = colorToRedist ;
colorToRedist * = 0.5f ;
* colors [ order [ ( i + 1 ) % 3 ] ] + = colorToRedist ;
* colors [ order [ ( i + 2 ) % 3 ] ] + = colorToRedist ;
}
ColorClamp ( color1 ) ;
ColorClamp ( color2 ) ;
ColorClamp ( color3 ) ;
if ( color1 [ 0 ] < 0.f ) color1 [ 0 ] = 0.f ;
if ( color1 [ 1 ] < 0.f ) color1 [ 1 ] = 0.f ;
if ( color1 [ 2 ] < 0.f ) color1 [ 2 ] = 0.f ;
if ( color2 [ 0 ] < 0.f ) color2 [ 0 ] = 0.f ;
if ( color2 [ 1 ] < 0.f ) color2 [ 1 ] = 0.f ;
if ( color2 [ 2 ] < 0.f ) color2 [ 2 ] = 0.f ;
if ( color3 [ 0 ] < 0.f ) color3 [ 0 ] = 0.f ;
if ( color3 [ 1 ] < 0.f ) color3 [ 1 ] = 0.f ;
if ( color3 [ 2 ] < 0.f ) color3 [ 2 ] = 0.f ;
}
static void LinearToBumpedLightmap (
const float * linearColor ,
const float * linearBumpColor1 ,
const float * linearBumpColor2 ,
const float * linearBumpColor3 ,
unsigned char * ret ,
unsigned char * retBump1 ,
unsigned char * retBump2 ,
unsigned char * retBump3 )
{
const Vector & linearBump1 = * ( ( const Vector * ) linearBumpColor1 ) ;
const Vector & linearBump2 = * ( ( const Vector * ) linearBumpColor2 ) ;
const Vector & linearBump3 = * ( ( const Vector * ) linearBumpColor3 ) ;
Vector gammaGoal ;
// gammaGoal is premultiplied by 1/overbright, which we want
gammaGoal [ 0 ] = LinearToVertexLight ( linearColor [ 0 ] ) ;
gammaGoal [ 1 ] = LinearToVertexLight ( linearColor [ 1 ] ) ;
gammaGoal [ 2 ] = LinearToVertexLight ( linearColor [ 2 ] ) ;
Vector bumpAverage = linearBump1 ;
bumpAverage + = linearBump2 ;
bumpAverage + = linearBump3 ;
bumpAverage * = ( 1.0f / 3.0f ) ;
Vector correctionScale ;
if ( * ( int * ) & bumpAverage [ 0 ] ! = 0 & & * ( int * ) & bumpAverage [ 1 ] ! = 0 & & * ( int * ) & bumpAverage [ 2 ] ! = 0 )
{
// fast path when we know that we don't have to worry about divide by zero.
VectorDivide ( gammaGoal , bumpAverage , correctionScale ) ;
// correctionScale = gammaGoal / bumpSum;
}
else
{
correctionScale . Init ( 0.0f , 0.0f , 0.0f ) ;
if ( bumpAverage [ 0 ] ! = 0.0f )
{
correctionScale [ 0 ] = gammaGoal [ 0 ] / bumpAverage [ 0 ] ;
}
if ( bumpAverage [ 1 ] ! = 0.0f )
{
correctionScale [ 1 ] = gammaGoal [ 1 ] / bumpAverage [ 1 ] ;
}
if ( bumpAverage [ 2 ] ! = 0.0f )
{
correctionScale [ 2 ] = gammaGoal [ 2 ] / bumpAverage [ 2 ] ;
}
}
Vector correctedBumpColor1 ;
Vector correctedBumpColor2 ;
Vector correctedBumpColor3 ;
VectorMultiply ( linearBump1 , correctionScale , correctedBumpColor1 ) ;
VectorMultiply ( linearBump2 , correctionScale , correctedBumpColor2 ) ;
VectorMultiply ( linearBump3 , correctionScale , correctedBumpColor3 ) ;
Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f ;
ColorClampBumped ( correctedBumpColor1 , correctedBumpColor2 , correctedBumpColor3 ) ;
ret [ 0 ] = RoundFloatToByte ( gammaGoal [ 0 ] * 255.0f ) ;
ret [ 1 ] = RoundFloatToByte ( gammaGoal [ 1 ] * 255.0f ) ;
ret [ 2 ] = RoundFloatToByte ( gammaGoal [ 2 ] * 255.0f ) ;
retBump1 [ 0 ] = RoundFloatToByte ( correctedBumpColor1 [ 0 ] * 255.0f ) ;
retBump1 [ 1 ] = RoundFloatToByte ( correctedBumpColor1 [ 1 ] * 255.0f ) ;
retBump1 [ 2 ] = RoundFloatToByte ( correctedBumpColor1 [ 2 ] * 255.0f ) ;
retBump2 [ 0 ] = RoundFloatToByte ( correctedBumpColor2 [ 0 ] * 255.0f ) ;
retBump2 [ 1 ] = RoundFloatToByte ( correctedBumpColor2 [ 1 ] * 255.0f ) ;
retBump2 [ 2 ] = RoundFloatToByte ( correctedBumpColor2 [ 2 ] * 255.0f ) ;
retBump3 [ 0 ] = RoundFloatToByte ( correctedBumpColor3 [ 0 ] * 255.0f ) ;
retBump3 [ 1 ] = RoundFloatToByte ( correctedBumpColor3 [ 1 ] * 255.0f ) ;
retBump3 [ 2 ] = RoundFloatToByte ( correctedBumpColor3 [ 2 ] * 255.0f ) ;
}
//-----------------------------------------------------------------------------
// Convert a RGBExp32 to a RGBA8888
// This matches the engine's conversion, so the lighting result is consistent.
//-----------------------------------------------------------------------------
2014-10-30 12:30:57 -04:00
void ConvertRGBExp32ToRGBA8888 ( const ColorRGBExp32 * pSrc , unsigned char * pDst , Vector * _optOutLinear )
2013-06-26 15:22:04 -07:00
{
Vector linearColor ;
// convert from ColorRGBExp32 to linear space
linearColor [ 0 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > r , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
linearColor [ 1 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > g , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
linearColor [ 2 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > b , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
2014-10-30 12:30:57 -04:00
ConvertLinearToRGBA8888 ( & linearColor , pDst ) ;
if ( _optOutLinear )
* _optOutLinear = linearColor ;
}
//-----------------------------------------------------------------------------
// Converts a RGBExp32 to a linear color value.
//-----------------------------------------------------------------------------
void ConvertRGBExp32ToLinear ( const ColorRGBExp32 * pSrc , Vector * pDst )
{
( * pDst ) [ 0 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > r , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
( * pDst ) [ 1 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > g , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
( * pDst ) [ 2 ] = TexLightToLinear ( ( ( ColorRGBExp32 * ) pSrc ) - > b , ( ( ColorRGBExp32 * ) pSrc ) - > exponent ) ;
}
//-----------------------------------------------------------------------------
// Converts a linear color value (suitable for combining linearly) to an RBGA8888 value expected by the engine.
//-----------------------------------------------------------------------------
void ConvertLinearToRGBA8888 ( const Vector * pSrcLinear , unsigned char * pDst )
{
Vector vertexColor ;
2013-06-26 15:22:04 -07:00
// convert from linear space to lightmap space
// cannot use mathlib routine directly because it doesn't match
// the colorspace version found in the engine, which *is* the same sequence here
2014-10-30 12:30:57 -04:00
vertexColor [ 0 ] = LinearToVertexLight ( ( * pSrcLinear ) [ 0 ] ) ;
vertexColor [ 1 ] = LinearToVertexLight ( ( * pSrcLinear ) [ 1 ] ) ;
vertexColor [ 2 ] = LinearToVertexLight ( ( * pSrcLinear ) [ 2 ] ) ;
2013-06-26 15:22:04 -07:00
// this is really a color normalization with a floor
2014-10-30 12:30:57 -04:00
ColorClamp ( vertexColor ) ;
2013-06-26 15:22:04 -07:00
// final [0..255] scale
2014-10-30 12:30:57 -04:00
pDst [ 0 ] = RoundFloatToByte ( vertexColor [ 0 ] * 255.0f ) ;
pDst [ 1 ] = RoundFloatToByte ( vertexColor [ 1 ] * 255.0f ) ;
pDst [ 2 ] = RoundFloatToByte ( vertexColor [ 2 ] * 255.0f ) ;
2013-06-26 15:22:04 -07:00
pDst [ 3 ] = 255 ;
}