aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vrad/radial.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/vrad/radial.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/vrad/radial.cpp')
-rw-r--r--mp/src/utils/vrad/radial.cpp1764
1 files changed, 882 insertions, 882 deletions
diff --git a/mp/src/utils/vrad/radial.cpp b/mp/src/utils/vrad/radial.cpp
index 46e3b925..5dc99b50 100644
--- a/mp/src/utils/vrad/radial.cpp
+++ b/mp/src/utils/vrad/radial.cpp
@@ -1,882 +1,882 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "lightmap.h"
-#include "radial.h"
-#include "mathlib/bumpvects.h"
-#include "utlrbtree.h"
-#include "mathlib/VMatrix.h"
-#include "macro_texture.h"
-
-
-void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
-{
- Vector pos;
-
- VectorSubtract( world, l->luxelOrigin, pos );
- coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
- coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
-}
-
-void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
-{
- Vector pos;
-
- s += l->face->m_LightmapTextureMinsInLuxels[0];
- t += l->face->m_LightmapTextureMinsInLuxels[1];
- VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
- VectorMA( pos, t, l->luxelToWorldSpace[1], world );
-}
-
-void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
-{
- FourVectors luxelOrigin;
- luxelOrigin.DuplicateVector ( l->luxelOrigin );
-
- FourVectors pos = world;
- pos -= luxelOrigin;
-
- coord.x = pos * l->worldToLuxelSpace[0];
- coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
- coord.y = pos * l->worldToLuxelSpace[1];
- coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
- coord.z = Four_Zeros;
-}
-
-void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
-{
- world.DuplicateVector ( l->luxelOrigin );
- FourVectors st;
-
- s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
- st.DuplicateVector ( l->luxelToWorldSpace[0] );
- st *= s;
- world += st;
-
- t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
- st.DuplicateVector ( l->luxelToWorldSpace[1] );
- st *= t;
- world += st;
-}
-
-
-
-void AddDirectToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- LightingValue_t const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap )
-{
- int s_min, s_max, t_min, t_max;
- Vector2D coord;
- int s, t;
- float ds, dt;
- float r;
- float area;
- int bumpSample;
-
- // convert world pos into local lightmap texture coord
- WorldToLuxelSpace( &rad->l, pnt, coord );
-
- s_min = ( int )( coordmins[0] );
- t_min = ( int )( coordmins[1] );
- s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
- t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
-
- s_min = max( s_min, 0 );
- t_min = max( t_min, 0 );
- s_max = min( s_max, rad->w );
- t_max = min( t_max, rad->h );
-
- for( s = s_min; s < s_max; s++ )
- {
- for( t = t_min; t < t_max; t++ )
- {
- float s0 = max( coordmins[0] - s, -1.0 );
- float t0 = max( coordmins[1] - t, -1.0 );
- float s1 = min( coordmaxs[0] - s, 1.0 );
- float t1 = min( coordmaxs[1] - t, 1.0 );
-
- area = (s1 - s0) * (t1 - t0);
-
- if (area > EQUAL_EPSILON)
- {
- ds = fabs( coord[0] - s );
- dt = fabs( coord[1] - t );
-
- r = max( ds, dt );
-
- if (r < 0.1)
- {
- r = area / 0.1;
- }
- else
- {
- r = area / r;
- }
-
- int i = s+t*rad->w;
-
- if( hasBumpmap )
- {
- if( neighborHasBumpmap )
- {
- for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
- }
- }
- else
- {
- rad->light[0][i].AddWeighted(light[0],r );
- for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
- }
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- }
-
- rad->weight[i] += r;
- }
- }
- }
-}
-
-
-
-void AddBouncedToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- Vector const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap )
-{
- int s_min, s_max, t_min, t_max;
- Vector2D coord;
- int s, t;
- float ds, dt;
- float r;
- int bumpSample;
-
- // convert world pos into local lightmap texture coord
- WorldToLuxelSpace( &rad->l, pnt, coord );
-
- float dists, distt;
-
- dists = (coordmaxs[0] - coordmins[0]);
- distt = (coordmaxs[1] - coordmins[1]);
-
- // patches less than a luxel in size could be mistakeningly filtered, so clamp.
- dists = max( 1.0, dists );
- distt = max( 1.0, distt );
-
- // find possible domain of patch influence
- s_min = ( int )( coord[0] - dists * RADIALDIST );
- t_min = ( int )( coord[1] - distt * RADIALDIST );
- s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
- t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
-
- // clamp to valid luxel
- s_min = max( s_min, 0 );
- t_min = max( t_min, 0 );
- s_max = min( s_max, rad->w );
- t_max = min( t_max, rad->h );
-
- for( s = s_min; s < s_max; s++ )
- {
- for( t = t_min; t < t_max; t++ )
- {
- // patch influence is based on patch size
- ds = ( coord[0] - s ) / dists;
- dt = ( coord[1] - t ) / distt;
-
- r = RADIALDIST2 - (ds * ds + dt * dt);
-
- int i = s+t*rad->w;
-
- if (r > 0)
- {
- if( hasBumpmap )
- {
- if( neighborHasBumpmap )
- {
- for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
- }
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- }
-
- rad->weight[i] += r;
- }
- }
- }
-}
-
-void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
-{
- winding_t *w;
- int i;
- Vector2D coord;
-
- mins.Init( 1E30, 1E30 );
- maxs.Init( -1E30, -1E30 );
-
- CPatch *patch = &g_Patches.Element( ndxPatch );
- w = patch->winding;
-
- for (i = 0; i < w->numpoints; i++)
- {
- WorldToLuxelSpace( &rad->l, w->p[i], coord );
- mins[0] = min( mins[0], coord[0] );
- maxs[0] = max( maxs[0], coord[0] );
- mins[1] = min( mins[1], coord[1] );
- maxs[1] = max( maxs[1], coord[1] );
- }
-}
-
-radial_t *AllocateRadial( int facenum )
-{
- radial_t *rad;
-
- rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
-
- rad->facenum = facenum;
- InitLightinfo( &rad->l, facenum );
-
- rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
- rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
-
- return rad;
-}
-
-void FreeRadial( radial_t *rad )
-{
- if (rad)
- free( rad );
-}
-
-
-radial_t *BuildPatchRadial( int facenum )
-{
- int j;
- radial_t *rad;
- CPatch *patch;
- faceneighbor_t *fn;
- Vector2D mins, maxs;
- bool needsBumpmap, neighborNeedsBumpmap;
-
- needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- rad = AllocateRadial( facenum );
-
- fn = &faceneighbor[ rad->facenum ];
-
- CPatch *pNextPatch;
-
- if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
- {
- for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- // skip patches with children
- if (patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- // get the range of patch lightmap texture coords
- int ndxPatch = patch - g_Patches.Base();
- PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
-
- if (patch->numtransfers == 0)
- {
- // Error, using patch that was never evaluated or has no samples
- // patch->totallight[1] = 255;
- }
-
- //
- // displacement surface patch origin position and normal vectors have been changed to
- // represent the displacement surface position and normal -- for radial "blending"
- // we need to get the base surface patch origin!
- //
- if( ValidDispFace( &g_pFaces[facenum] ) )
- {
- Vector patchOrigin;
- WindingCenter (patch->winding, patchOrigin );
- AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- else
- {
- AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- }
- }
-
- for (j=0 ; j<fn->numneighbors; j++)
- {
- if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
- {
- for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- // skip patches with children
- if (patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- // get the range of patch lightmap texture coords
- int ndxPatch = patch - g_Patches.Base();
- PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
-
- neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- //
- // displacement surface patch origin position and normal vectors have been changed to
- // represent the displacement surface position and normal -- for radial "blending"
- // we need to get the base surface patch origin!
- //
- if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
- {
- Vector patchOrigin;
- WindingCenter (patch->winding, patchOrigin );
- AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- else
- {
- AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- }
- }
- }
-
- return rad;
-}
-
-
-radial_t *BuildLuxelRadial( int facenum, int style )
-{
- LightingValue_t light[NUM_BUMP_VECTS + 1];
-
- facelight_t *fl = &facelight[facenum];
- faceneighbor_t *fn = &faceneighbor[facenum];
-
- radial_t *rad = AllocateRadial( facenum );
-
- bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- for (int k=0 ; k<fl->numsamples ; k++)
- {
- if( needsBumpmap )
- {
- for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- light[bumpSample] = fl->light[style][bumpSample][k];
- }
- }
- else
- {
- light[0] = fl->light[style][0][k];
- }
-
- AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
- }
-
- for (int j = 0; j < fn->numneighbors; j++)
- {
- fl = &facelight[fn->neighbor[j]];
-
- bool neighborHasBumpmap = false;
-
- if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
- {
- neighborHasBumpmap = true;
- }
-
- int nstyle = 0;
-
- // look for style that matches
- if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
- {
- for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
- if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
- break;
-
- // if not found, skip this neighbor
- if (nstyle >= MAXLIGHTMAPS)
- continue;
- }
-
- lightinfo_t l;
-
- InitLightinfo( &l, fn->neighbor[j] );
-
- for (int k=0 ; k<fl->numsamples ; k++)
- {
- if( neighborHasBumpmap )
- {
- for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- light[bumpSample] = fl->light[nstyle][bumpSample][k];
- }
- }
- else
- {
- light[0]=fl->light[nstyle][0][k];
- }
-
- Vector tmp;
- Vector2D mins, maxs;
-
- LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
- WorldToLuxelSpace( &rad->l, tmp, mins );
- LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
- WorldToLuxelSpace( &rad->l, tmp, maxs );
-
- AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
- needsBumpmap, neighborHasBumpmap );
- }
- }
-
- return rad;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: returns the closest light value for a given point on the surface
-// this is normally a 1:1 mapping
-//-----------------------------------------------------------------------------
-bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
-{
- int bumpSample;
- Vector2D coord;
-
- WorldToLuxelSpace( &rad->l, pnt, coord );
- int u = ( int )( coord[0] + 0.5f );
- int v = ( int )( coord[1] + 0.5f );
- int i = u + v * rad->w;
-
- if (u < 0 || u > rad->w || v < 0 || v > rad->h)
- {
- static bool warning = false;
- if ( !warning )
- {
- // punting over to KenB
- // 2d coord indexes off of lightmap, generation of pnt seems suspect
- Warning( "SampleRadial: Punting, Waiting for fix\n" );
- warning = true;
- }
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
- }
- return false;
- }
-
- bool baseSampleOk = true;
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- light[bumpSample].Zero();
-
- if (rad->weight[i] > WEIGHT_EPS)
- {
- light[bumpSample]= rad->light[bumpSample][i];
- light[bumpSample].Scale( 1.0 / rad->weight[i] );
- }
- else
- {
- if ( bRed2Black )
- {
- // Error, luxel has no samples
- light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
- }
- else
- {
- // Error, luxel has no samples
- // Yes, it actually should be 2550
- light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
- }
-
- if (bumpSample == 0)
- baseSampleOk = false;
- }
- }
-
- return baseSampleOk;
-}
-
-bool FloatLess( float const& src1, float const& src2 )
-{
- return src1 < src2;
-}
-
-
-//-----------------------------------------------------------------------------
-// Debugging!
-//-----------------------------------------------------------------------------
-void GetRandomColor( unsigned char *color )
-{
- static bool firstTime = true;
-
- if( firstTime )
- {
- firstTime = false;
- srand( 0 );
- }
-
- color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
- color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
- color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
-}
-
-
-#if 0
-// debugging! -- not accurate!
-void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
-{
- static FileHandle_t pFpLuxels = NULL;
-
- ThreadLock();
-
- if( !pFpLuxels )
- {
- pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
- }
-
- dface_t *pFace = &g_pFaces[ndxFace];
- bool bDisp = ( pFace->dispinfo != -1 );
-
- for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
- {
- WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
- if( bDumpNormals && bDisp )
- {
- WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
- }
- }
-
- ThreadUnlock();
-}
-#endif
-
-
-static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
-
-void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
-{
- // Lock the thread and dump the luxel data.
- ThreadLock();
-
- // Get the face and facelight data.
- facelight_t *pFaceLight = &facelight[iFace];
-
- // Open the luxel files.
- char szFileName[512];
- for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
- {
- if ( pFileLuxels[iBump] == NULL )
- {
- sprintf( szFileName, "luxels_bump%d.txt", iBump );
- pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
- }
- }
-
- WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
-
- ThreadUnlock();
-}
-
-void CloseDispLuxels()
-{
- for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
- {
- if ( pFileLuxels[iBump] )
- {
- g_pFileSystem->Close( pFileLuxels[iBump] );
- }
- }
-}
-
-/*
-=============
-FinalLightFace
-
-Add the indirect lighting on top of the direct
-lighting and save into final map format
-=============
-*/
-void FinalLightFace( int iThread, int facenum )
-{
- dface_t *f;
- int i, j, k;
- facelight_t *fl;
- float minlight;
- int lightstyles;
- LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
- unsigned char *pdata[NUM_BUMP_VECTS + 1];
- int bumpSample;
- radial_t *rad = NULL;
- radial_t *prad = NULL;
-
- f = &g_pFaces[facenum];
-
- // test for non-lit texture
- if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
- return;
-
- fl = &facelight[facenum];
-
-
- for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
- {
- if ( f->styles[lightstyles] == 255 )
- break;
- }
- if ( !lightstyles )
- return;
-
-
- //
- // sample the triangulation
- //
- minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
-
- bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
- int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
-
- bool bDisp = ( f->dispinfo != -1 );
-
-//#define RANDOM_COLOR
-
-#ifdef RANDOM_COLOR
- unsigned char randomColor[3];
- GetRandomColor( randomColor );
-#endif
-
-
- // NOTE: I'm using these RB trees to sort all the illumination values
- // to compute median colors. Turns out that this is a somewhat better
- // method that using the average; usually if there are surfaces
- // with a large light intensity variation, the extremely bright regions
- // have a very small area and tend to influence the average too much.
- CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
- CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
- CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
-
- for (k=0 ; k < lightstyles; k++ )
- {
- m_Red.RemoveAll();
- m_Green.RemoveAll();
- m_Blue.RemoveAll();
-
- if (!do_fast)
- {
- if( !bDisp )
- {
- rad = BuildLuxelRadial( facenum, k );
- }
- else
- {
- rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
- }
- }
-
- if (numbounce > 0 && k == 0)
- {
- // currently only radiosity light non-displacement surfaces!
- if( !bDisp )
- {
- prad = BuildPatchRadial( facenum );
- }
- else
- {
- prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
- }
- }
-
- // pack the nonbump texture and the three bump texture for the given
- // lightstyle right next to each other.
- // NOTE: Even though it's building positions for all bump-mapped data,
- // it isn't going to use those positions (see loop over bumpSample below)
- // The file offset is correctly computed to only store space for 1 set
- // of light data if we don't have bumped lighting.
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
- }
-
- // Compute the average luxel color, but not for the bump samples
- Vector avg( 0.0f, 0.0f, 0.0f );
- int avgCount = 0;
-
- for (j=0 ; j<fl->numluxels; j++)
- {
- // garymct - direct lighting
- bool baseSampleOk = true;
-
- if (!do_fast)
- {
- if( !bDisp )
- {
- baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
- }
- else
- {
- baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
- }
- }
- else
- {
- for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
- {
- lb[iBump] = fl->light[0][iBump][j];
- }
- }
-
- if (prad)
- {
- // garymct - bounced light
- // v is indirect light that is received on the luxel.
- if( !bDisp )
- {
- SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
- }
- else
- {
- StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
- }
-
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- lb[bumpSample].AddLight( v[bumpSample] );
- }
- }
-
- if ( bDisp && g_bDumpPatches )
- {
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
- }
- }
-
- if (fl->numsamples == 0)
- {
- for( i = 0; i < bumpSampleCount; i++ )
- {
- lb[i].Init( 255, 0, 0 );
- }
- baseSampleOk = false;
- }
-
- int bumpSample;
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- // clip from the bottom first
- // garymct: minlight is a per entity minimum light value?
- for( i=0; i<3; i++ )
- {
- lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
- }
-
- // Do the average light computation, I'm assuming (perhaps incorrectly?)
- // that all luxels in a particular lightmap have the same area here.
- // Also, don't bother doing averages for the bump samples. Doing it here
- // because of the minlight clamp above + the random color testy thingy.
- // Also have to do it before Vec3toColorRGBExp32 because it
- // destructively modifies lb[bumpSample] (Feh!)
- if ((bumpSample == 0) && baseSampleOk)
- {
- ++avgCount;
-
- ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
-
- // For median computation
- m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
- m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
- m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
- }
-
-#ifdef RANDOM_COLOR
- pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
- pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
- pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
- pdata[bumpSample][3] = 0;
-#else
- // convert to a 4 byte r,g,b,signed exponent format
- VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
- lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
-#endif
-
- pdata[bumpSample] += 4;
- }
- }
- FreeRadial( rad );
- if (prad)
- {
- FreeRadial( prad );
- prad = NULL;
- }
-
- // Compute the median color for this lightstyle
- // Remember, the data goes *before* the specified light_ofs, in *reverse order*
- ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
- if (avgCount == 0)
- {
- Vector median( 0, 0, 0 );
- VectorToColorRGBExp32( median, *pAvgColor );
- }
- else
- {
- unsigned int r, g, b;
- r = m_Red.FirstInorder();
- g = m_Green.FirstInorder();
- b = m_Blue.FirstInorder();
- avgCount >>= 1;
- while (avgCount > 0)
- {
- r = m_Red.NextInorder(r);
- g = m_Green.NextInorder(g);
- b = m_Blue.NextInorder(b);
- --avgCount;
- }
-
- Vector median( m_Red[r], m_Green[g], m_Blue[b] );
- VectorToColorRGBExp32( median, *pAvgColor );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+#include "radial.h"
+#include "mathlib/bumpvects.h"
+#include "utlrbtree.h"
+#include "mathlib/VMatrix.h"
+#include "macro_texture.h"
+
+
+void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
+{
+ Vector pos;
+
+ VectorSubtract( world, l->luxelOrigin, pos );
+ coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
+ coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
+{
+ Vector pos;
+
+ s += l->face->m_LightmapTextureMinsInLuxels[0];
+ t += l->face->m_LightmapTextureMinsInLuxels[1];
+ VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
+ VectorMA( pos, t, l->luxelToWorldSpace[1], world );
+}
+
+void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
+{
+ FourVectors luxelOrigin;
+ luxelOrigin.DuplicateVector ( l->luxelOrigin );
+
+ FourVectors pos = world;
+ pos -= luxelOrigin;
+
+ coord.x = pos * l->worldToLuxelSpace[0];
+ coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ coord.y = pos * l->worldToLuxelSpace[1];
+ coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ coord.z = Four_Zeros;
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
+{
+ world.DuplicateVector ( l->luxelOrigin );
+ FourVectors st;
+
+ s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[0] );
+ st *= s;
+ world += st;
+
+ t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[1] );
+ st *= t;
+ world += st;
+}
+
+
+
+void AddDirectToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ LightingValue_t const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ float area;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ s_min = ( int )( coordmins[0] );
+ t_min = ( int )( coordmins[1] );
+ s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
+ t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
+
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ float s0 = max( coordmins[0] - s, -1.0 );
+ float t0 = max( coordmins[1] - t, -1.0 );
+ float s1 = min( coordmaxs[0] - s, 1.0 );
+ float t1 = min( coordmaxs[1] - t, 1.0 );
+
+ area = (s1 - s0) * (t1 - t0);
+
+ if (area > EQUAL_EPSILON)
+ {
+ ds = fabs( coord[0] - s );
+ dt = fabs( coord[1] - t );
+
+ r = max( ds, dt );
+
+ if (r < 0.1)
+ {
+ r = area / 0.1;
+ }
+ else
+ {
+ r = area / r;
+ }
+
+ int i = s+t*rad->w;
+
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted(light[0],r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+
+
+void AddBouncedToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ float dists, distt;
+
+ dists = (coordmaxs[0] - coordmins[0]);
+ distt = (coordmaxs[1] - coordmins[1]);
+
+ // patches less than a luxel in size could be mistakeningly filtered, so clamp.
+ dists = max( 1.0, dists );
+ distt = max( 1.0, distt );
+
+ // find possible domain of patch influence
+ s_min = ( int )( coord[0] - dists * RADIALDIST );
+ t_min = ( int )( coord[1] - distt * RADIALDIST );
+ s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
+ t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
+
+ // clamp to valid luxel
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ // patch influence is based on patch size
+ ds = ( coord[0] - s ) / dists;
+ dt = ( coord[1] - t ) / distt;
+
+ r = RADIALDIST2 - (ds * ds + dt * dt);
+
+ int i = s+t*rad->w;
+
+ if (r > 0)
+ {
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
+{
+ winding_t *w;
+ int i;
+ Vector2D coord;
+
+ mins.Init( 1E30, 1E30 );
+ maxs.Init( -1E30, -1E30 );
+
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+ w = patch->winding;
+
+ for (i = 0; i < w->numpoints; i++)
+ {
+ WorldToLuxelSpace( &rad->l, w->p[i], coord );
+ mins[0] = min( mins[0], coord[0] );
+ maxs[0] = max( maxs[0], coord[0] );
+ mins[1] = min( mins[1], coord[1] );
+ maxs[1] = max( maxs[1], coord[1] );
+ }
+}
+
+radial_t *AllocateRadial( int facenum )
+{
+ radial_t *rad;
+
+ rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
+
+ rad->facenum = facenum;
+ InitLightinfo( &rad->l, facenum );
+
+ rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
+ rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ return rad;
+}
+
+void FreeRadial( radial_t *rad )
+{
+ if (rad)
+ free( rad );
+}
+
+
+radial_t *BuildPatchRadial( int facenum )
+{
+ int j;
+ radial_t *rad;
+ CPatch *patch;
+ faceneighbor_t *fn;
+ Vector2D mins, maxs;
+ bool needsBumpmap, neighborNeedsBumpmap;
+
+ needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ rad = AllocateRadial( facenum );
+
+ fn = &faceneighbor[ rad->facenum ];
+
+ CPatch *pNextPatch;
+
+ if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ if (patch->numtransfers == 0)
+ {
+ // Error, using patch that was never evaluated or has no samples
+ // patch->totallight[1] = 255;
+ }
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[facenum] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+
+ for (j=0 ; j<fn->numneighbors; j++)
+ {
+ if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+ }
+
+ return rad;
+}
+
+
+radial_t *BuildLuxelRadial( int facenum, int style )
+{
+ LightingValue_t light[NUM_BUMP_VECTS + 1];
+
+ facelight_t *fl = &facelight[facenum];
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ radial_t *rad = AllocateRadial( facenum );
+
+ bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( needsBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[style][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0] = fl->light[style][0][k];
+ }
+
+ AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
+ }
+
+ for (int j = 0; j < fn->numneighbors; j++)
+ {
+ fl = &facelight[fn->neighbor[j]];
+
+ bool neighborHasBumpmap = false;
+
+ if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
+ {
+ neighborHasBumpmap = true;
+ }
+
+ int nstyle = 0;
+
+ // look for style that matches
+ if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
+ {
+ for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
+ if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
+ break;
+
+ // if not found, skip this neighbor
+ if (nstyle >= MAXLIGHTMAPS)
+ continue;
+ }
+
+ lightinfo_t l;
+
+ InitLightinfo( &l, fn->neighbor[j] );
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( neighborHasBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[nstyle][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0]=fl->light[nstyle][0][k];
+ }
+
+ Vector tmp;
+ Vector2D mins, maxs;
+
+ LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, mins );
+ LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, maxs );
+
+ AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
+ needsBumpmap, neighborHasBumpmap );
+ }
+ }
+
+ return rad;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the closest light value for a given point on the surface
+// this is normally a 1:1 mapping
+//-----------------------------------------------------------------------------
+bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
+{
+ int bumpSample;
+ Vector2D coord;
+
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+ int u = ( int )( coord[0] + 0.5f );
+ int v = ( int )( coord[1] + 0.5f );
+ int i = u + v * rad->w;
+
+ if (u < 0 || u > rad->w || v < 0 || v > rad->h)
+ {
+ static bool warning = false;
+ if ( !warning )
+ {
+ // punting over to KenB
+ // 2d coord indexes off of lightmap, generation of pnt seems suspect
+ Warning( "SampleRadial: Punting, Waiting for fix\n" );
+ warning = true;
+ }
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+ return false;
+ }
+
+ bool baseSampleOk = true;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].Zero();
+
+ if (rad->weight[i] > WEIGHT_EPS)
+ {
+ light[bumpSample]= rad->light[bumpSample][i];
+ light[bumpSample].Scale( 1.0 / rad->weight[i] );
+ }
+ else
+ {
+ if ( bRed2Black )
+ {
+ // Error, luxel has no samples
+ light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
+ }
+ else
+ {
+ // Error, luxel has no samples
+ // Yes, it actually should be 2550
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+
+ if (bumpSample == 0)
+ baseSampleOk = false;
+ }
+ }
+
+ return baseSampleOk;
+}
+
+bool FloatLess( float const& src1, float const& src2 )
+{
+ return src1 < src2;
+}
+
+
+//-----------------------------------------------------------------------------
+// Debugging!
+//-----------------------------------------------------------------------------
+void GetRandomColor( unsigned char *color )
+{
+ static bool firstTime = true;
+
+ if( firstTime )
+ {
+ firstTime = false;
+ srand( 0 );
+ }
+
+ color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+}
+
+
+#if 0
+// debugging! -- not accurate!
+void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
+{
+ static FileHandle_t pFpLuxels = NULL;
+
+ ThreadLock();
+
+ if( !pFpLuxels )
+ {
+ pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
+ }
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ bool bDisp = ( pFace->dispinfo != -1 );
+
+ for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
+ {
+ WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
+ if( bDumpNormals && bDisp )
+ {
+ WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
+ }
+ }
+
+ ThreadUnlock();
+}
+#endif
+
+
+static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
+
+void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
+{
+ // Lock the thread and dump the luxel data.
+ ThreadLock();
+
+ // Get the face and facelight data.
+ facelight_t *pFaceLight = &facelight[iFace];
+
+ // Open the luxel files.
+ char szFileName[512];
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] == NULL )
+ {
+ sprintf( szFileName, "luxels_bump%d.txt", iBump );
+ pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
+ }
+ }
+
+ WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
+
+ ThreadUnlock();
+}
+
+void CloseDispLuxels()
+{
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] )
+ {
+ g_pFileSystem->Close( pFileLuxels[iBump] );
+ }
+ }
+}
+
+/*
+=============
+FinalLightFace
+
+Add the indirect lighting on top of the direct
+lighting and save into final map format
+=============
+*/
+void FinalLightFace( int iThread, int facenum )
+{
+ dface_t *f;
+ int i, j, k;
+ facelight_t *fl;
+ float minlight;
+ int lightstyles;
+ LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
+ unsigned char *pdata[NUM_BUMP_VECTS + 1];
+ int bumpSample;
+ radial_t *rad = NULL;
+ radial_t *prad = NULL;
+
+ f = &g_pFaces[facenum];
+
+ // test for non-lit texture
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ return;
+
+ fl = &facelight[facenum];
+
+
+ for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
+ {
+ if ( f->styles[lightstyles] == 255 )
+ break;
+ }
+ if ( !lightstyles )
+ return;
+
+
+ //
+ // sample the triangulation
+ //
+ minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
+
+ bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
+ int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
+
+ bool bDisp = ( f->dispinfo != -1 );
+
+//#define RANDOM_COLOR
+
+#ifdef RANDOM_COLOR
+ unsigned char randomColor[3];
+ GetRandomColor( randomColor );
+#endif
+
+
+ // NOTE: I'm using these RB trees to sort all the illumination values
+ // to compute median colors. Turns out that this is a somewhat better
+ // method that using the average; usually if there are surfaces
+ // with a large light intensity variation, the extremely bright regions
+ // have a very small area and tend to influence the average too much.
+ CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
+
+ for (k=0 ; k < lightstyles; k++ )
+ {
+ m_Red.RemoveAll();
+ m_Green.RemoveAll();
+ m_Blue.RemoveAll();
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ rad = BuildLuxelRadial( facenum, k );
+ }
+ else
+ {
+ rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
+ }
+ }
+
+ if (numbounce > 0 && k == 0)
+ {
+ // currently only radiosity light non-displacement surfaces!
+ if( !bDisp )
+ {
+ prad = BuildPatchRadial( facenum );
+ }
+ else
+ {
+ prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
+ }
+ }
+
+ // pack the nonbump texture and the three bump texture for the given
+ // lightstyle right next to each other.
+ // NOTE: Even though it's building positions for all bump-mapped data,
+ // it isn't going to use those positions (see loop over bumpSample below)
+ // The file offset is correctly computed to only store space for 1 set
+ // of light data if we don't have bumped lighting.
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
+ }
+
+ // Compute the average luxel color, but not for the bump samples
+ Vector avg( 0.0f, 0.0f, 0.0f );
+ int avgCount = 0;
+
+ for (j=0 ; j<fl->numluxels; j++)
+ {
+ // garymct - direct lighting
+ bool baseSampleOk = true;
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
+ }
+ else
+ {
+ baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
+ }
+ }
+ else
+ {
+ for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
+ {
+ lb[iBump] = fl->light[0][iBump][j];
+ }
+ }
+
+ if (prad)
+ {
+ // garymct - bounced light
+ // v is indirect light that is received on the luxel.
+ if( !bDisp )
+ {
+ SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
+ }
+ else
+ {
+ StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
+ }
+
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ lb[bumpSample].AddLight( v[bumpSample] );
+ }
+ }
+
+ if ( bDisp && g_bDumpPatches )
+ {
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
+ }
+ }
+
+ if (fl->numsamples == 0)
+ {
+ for( i = 0; i < bumpSampleCount; i++ )
+ {
+ lb[i].Init( 255, 0, 0 );
+ }
+ baseSampleOk = false;
+ }
+
+ int bumpSample;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ // clip from the bottom first
+ // garymct: minlight is a per entity minimum light value?
+ for( i=0; i<3; i++ )
+ {
+ lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
+ }
+
+ // Do the average light computation, I'm assuming (perhaps incorrectly?)
+ // that all luxels in a particular lightmap have the same area here.
+ // Also, don't bother doing averages for the bump samples. Doing it here
+ // because of the minlight clamp above + the random color testy thingy.
+ // Also have to do it before Vec3toColorRGBExp32 because it
+ // destructively modifies lb[bumpSample] (Feh!)
+ if ((bumpSample == 0) && baseSampleOk)
+ {
+ ++avgCount;
+
+ ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
+
+ // For median computation
+ m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
+ m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
+ m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
+ }
+
+#ifdef RANDOM_COLOR
+ pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
+ pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
+ pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
+ pdata[bumpSample][3] = 0;
+#else
+ // convert to a 4 byte r,g,b,signed exponent format
+ VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
+ lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
+#endif
+
+ pdata[bumpSample] += 4;
+ }
+ }
+ FreeRadial( rad );
+ if (prad)
+ {
+ FreeRadial( prad );
+ prad = NULL;
+ }
+
+ // Compute the median color for this lightstyle
+ // Remember, the data goes *before* the specified light_ofs, in *reverse order*
+ ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
+ if (avgCount == 0)
+ {
+ Vector median( 0, 0, 0 );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ else
+ {
+ unsigned int r, g, b;
+ r = m_Red.FirstInorder();
+ g = m_Green.FirstInorder();
+ b = m_Blue.FirstInorder();
+ avgCount >>= 1;
+ while (avgCount > 0)
+ {
+ r = m_Red.NextInorder(r);
+ g = m_Green.NextInorder(g);
+ b = m_Blue.NextInorder(b);
+ --avgCount;
+ }
+
+ Vector median( m_Red[r], m_Green[g], m_Blue[b] );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ }
+}