Files
HL2Overcharged/utils/vrad/radial.cpp
2025-05-21 21:20:08 +03:00

883 lines
21 KiB
C++

//========= 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);
}
}
}