diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/utils/vrad/lightmap.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/utils/vrad/lightmap.cpp')
| -rw-r--r-- | sp/src/utils/vrad/lightmap.cpp | 7154 |
1 files changed, 3577 insertions, 3577 deletions
diff --git a/sp/src/utils/vrad/lightmap.cpp b/sp/src/utils/vrad/lightmap.cpp index bc3d254e..bfa4bd03 100644 --- a/sp/src/utils/vrad/lightmap.cpp +++ b/sp/src/utils/vrad/lightmap.cpp @@ -1,3577 +1,3577 @@ -//========= 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\tmaterial=%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\tmaterial=%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 ¢roid = 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!
- CUtlVector<sample_t> sampleData;
- sampleData.SetCount( SINGLE_BRUSH_MAP * 2 );
- sample_t *samples = sampleData.Base();
- 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 ("\nWARNING: 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.
-//-----------------------------------------------------------------------------
-void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst )
-{
- Vector linearColor;
- Vector vertexColor;
-
- // 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 );
-
- // 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
- vertexColor[0] = LinearToVertexLight( linearColor[0] );
- vertexColor[1] = LinearToVertexLight( linearColor[1] );
- vertexColor[2] = LinearToVertexLight( linearColor[2] );
-
- // this is really a color normalization with a floor
- ColorClamp( vertexColor );
-
- // final [0..255] scale
- pDst[0] = RoundFloatToByte( vertexColor[0] * 255.0f );
- pDst[1] = RoundFloatToByte( vertexColor[1] * 255.0f );
- pDst[2] = RoundFloatToByte( vertexColor[2] * 255.0f );
- pDst[3] = 255;
-}
-
+//========= 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\tmaterial=%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\tmaterial=%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 ¢roid = 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! + CUtlVector<sample_t> sampleData; + sampleData.SetCount( SINGLE_BRUSH_MAP * 2 ); + sample_t *samples = sampleData.Base(); + 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 ("\nWARNING: 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. +//----------------------------------------------------------------------------- +void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst ) +{ + Vector linearColor; + Vector vertexColor; + + // 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 ); + + // 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 + vertexColor[0] = LinearToVertexLight( linearColor[0] ); + vertexColor[1] = LinearToVertexLight( linearColor[1] ); + vertexColor[2] = LinearToVertexLight( linearColor[2] ); + + // this is really a color normalization with a floor + ColorClamp( vertexColor ); + + // final [0..255] scale + pDst[0] = RoundFloatToByte( vertexColor[0] * 255.0f ); + pDst[1] = RoundFloatToByte( vertexColor[1] * 255.0f ); + pDst[2] = RoundFloatToByte( vertexColor[2] * 255.0f ); + pDst[3] = 255; +} + |