source-engine/utils/vrad/radial.cpp

883 lines
22 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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 );
}
}
}