ericw-tools/light/ltface.c

1073 lines
25 KiB
C

/* Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#include <light/light.h>
static const float scalecos = 0.5;
static const vec3_t bsp_origin = { 0, 0, 0 };
/* ======================================================================== */
// Solve three simultaneous equations
// mtx is modified by the function...
#define ZERO_EPSILON (0.001)
static qboolean
LU_Decompose(vec3_t mtx[3], int r[3], int c[2])
{
int i, j, k; // loop variables
vec_t max;
int max_r, max_c;
// Do gauss elimination
for (i = 0; i < 3; ++i) {
max = 0;
max_r = max_c = i;
for (j = i; j < 3; ++j) {
for (k = i; k < 3; ++k) {
if (fabs(mtx[j][k]) > max) {
max = fabs(mtx[j][k]);
max_r = j;
max_c = k;
}
}
}
// Check for parallel planes
if (max < ZERO_EPSILON)
return false;
// Swap rows/columns if necessary
if (max_r != i) {
for (j = 0; j < 3; ++j) {
max = mtx[i][j];
mtx[i][j] = mtx[max_r][j];
mtx[max_r][j] = max;
}
k = r[i];
r[i] = r[max_r];
r[max_r] = k;
}
if (max_c != i) {
for (j = 0; j < 3; ++j) {
max = mtx[j][i];
mtx[j][i] = mtx[j][max_c];
mtx[j][max_c] = max;
}
k = c[i];
c[i] = c[max_c];
c[max_c] = k;
}
// Do pivot
for (j = i + 1; j < 3; ++j) {
mtx[j][i] /= mtx[i][i];
for (k = i + 1; k < 3; ++k)
mtx[j][k] -= mtx[j][i] * mtx[i][k];
}
}
return true;
}
static void
solve3(const vec3_t mtx[3], const int r[3], const int c[3],
const vec3_t rhs, vec3_t soln)
{
vec3_t y;
// forward-substitution
y[0] = rhs[r[0]];
y[1] = rhs[r[1]] - mtx[1][0] * y[0];
y[2] = rhs[r[2]] - mtx[2][0] * y[0] - mtx[2][1] * y[1];
// back-substitution
soln[c[2]] = y[2] / mtx[2][2];
soln[c[1]] = (y[1] - mtx[1][2] * soln[c[2]]) / mtx[1][1];
soln[c[0]] = (y[0] - mtx[0][1] * soln[c[1]] - mtx[0][2] * soln[c[2]])
/ mtx[0][0];
}
/* ======================================================================== */
/*
* ============
* CastRay
* Returns the distance between the points, or -1 if blocked
* =============
*/
static vec_t
CastRay(const vec3_t p1, const vec3_t p2)
{
int i;
vec_t t;
qboolean trace;
trace = TestLine(p1, p2);
if (!trace)
return -1; /* ray was blocked */
t = 0;
for (i = 0; i < 3; i++)
t += (p2[i] - p1[i]) * (p2[i] - p1[i]);
if (t == 0)
t = 1; /* don't blow up... */
return sqrt(t);
}
/*
* ============================================================================
* SAMPLE POINT DETERMINATION
* void SetupBlock (dface_t *f) Returns with surfpt[] set
*
* This is a little tricky because the lightmap covers more area than the face.
* If done in the straightforward fashion, some of the sample points will be
* inside walls or on the other side of walls, causing false shadows and light
* bleeds.
*
* To solve this, I only consider a sample point valid if a line can be drawn
* between it and the exact midpoint of the face. If invalid, it is adjusted
* towards the center until it is valid.
*
* FIXME: This doesn't completely work; I think what we really want is to move
* the light point to the nearst sample point that is on the polygon;
* ============================================================================
*/
#define SINGLEMAP (18*18*4)
typedef struct {
vec_t lightmaps[MAXLIGHTMAPS][SINGLEMAP];
int numlightstyles;
vec_t *light;
vec_t facedist;
vec3_t facenormal;
int numsurfpt;
vec3_t surfpt[SINGLEMAP];
/* FIXME - Comment this properly */
vec_t worldtotex[2][4]; // Copy of face->texinfo->vecs
vec3_t LU[3];
int row_p[3];
int col_p[3];
vec_t exactmid[2];
int texmins[2], texsize[2];
int lightstyles[256];
int surfnum;
dface_t *face;
/* Colored lighting */
vec3_t lightmapcolors[MAXLIGHTMAPS][SINGLEMAP];
} lightinfo_t;
/*
* ================
* CalcFaceVectors
* Fills in texorg, worldtotex. and textoworld
* ================
*/
static void
CalcFaceVectors(lightinfo_t * l)
{
texinfo_t *tex;
int i, j;
/* convert from float to vec_t */
tex = &texinfo[l->face->texinfo];
for (i = 0; i < 2; i++)
for (j = 0; j < 4; j++)
l->worldtotex[i][j] = tex->vecs[i][j];
/* Prepare LU and row, column permutations */
for (i = 0; i < 3; ++i)
l->row_p[i] = l->col_p[i] = i;
VectorCopy(l->worldtotex[0], l->LU[0]);
VectorCopy(l->worldtotex[1], l->LU[1]);
VectorCopy(l->facenormal, l->LU[2]);
/* Decompose the matrix. If we can't, texture axes are invalid. */
if (!LU_Decompose(l->LU, l->row_p, l->col_p)) {
const vec_t *p = dvertexes[dedges[l->face->firstedge].v[0]].point;
Error("Bad texture axes on face:\n"
" face point at (%5.3f, %5.3f, %5.3f)\n", p[0], p[1], p[2]);
}
}
static void
tex_to_world(vec_t s, vec_t t, const lightinfo_t * l, vec3_t world)
{
vec3_t rhs;
rhs[0] = s - l->worldtotex[0][3];
rhs[1] = t - l->worldtotex[1][3];
rhs[2] = l->facedist + 1; // one "unit" in front of surface
solve3(l->LU, l->row_p, l->col_p, rhs, world);
}
/*
* Functions to aid in calculation of polygon centroid
*/
static void
tri_centroid(const dvertex_t *v0, const dvertex_t *v1, const dvertex_t *v2,
vec3_t out)
{
int i;
for (i = 0; i < 3; i++)
out[i] = (v0->point[i] + v1->point[i] + v2->point[i]) / 3.0;
}
static vec_t
tri_area(const dvertex_t *v0, const dvertex_t *v1, const dvertex_t *v2)
{
int i;
vec3_t edge0, edge1, cross;
for (i =0; i < 3; i++) {
edge0[i] = v1->point[i] - v0->point[i];
edge1[i] = v2->point[i] - v0->point[i];
}
CrossProduct(edge0, edge1, cross);
return VectorLength(cross) * 0.5;
}
static void
face_centroid(const dface_t *f, vec3_t out)
{
int i, e;
dvertex_t *v0, *v1, *v2;
vec3_t centroid, poly_centroid;
vec_t area, poly_area;
VectorCopy(vec3_origin, poly_centroid);
poly_area = 0;
e = dsurfedges[f->firstedge];
if (e >= 0)
v0 = dvertexes + dedges[e].v[0];
else
v0 = dvertexes + dedges[-e].v[1];
for (i = 1; i < f->numedges - 1; i++) {
e = dsurfedges[f->firstedge + i];
if (e >= 0) {
v1 = dvertexes + dedges[e].v[0];
v2 = dvertexes + dedges[e].v[1];
} else {
v1 = dvertexes + dedges[-e].v[1];
v2 = dvertexes + dedges[-e].v[0];
}
area = tri_area(v0, v1, v2);
poly_area += area;
tri_centroid(v0, v1, v2, centroid);
VectorMA(poly_centroid, area, centroid, poly_centroid);
}
VectorScale(poly_centroid, 1.0 / poly_area, out);
}
/*
* ================
* CalcFaceExtents
* Fills in s->texmins[], s->texsize[] and sets exactmid[]
* ================
*/
static void
CalcFaceExtents(lightinfo_t * l, const vec3_t faceoffset)
{
dface_t *s;
vec_t mins[2], maxs[2], val;
vec3_t centroid;
int i, j, e;
dvertex_t *v;
texinfo_t *tex;
s = l->face;
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
tex = &texinfo[s->texinfo];
for (i = 0; i < s->numedges; i++) {
e = dsurfedges[s->firstedge + i];
if (e >= 0)
v = dvertexes + dedges[e].v[0];
else
v = dvertexes + dedges[-e].v[1];
for (j = 0; j < 2; j++) {
// This is world->tex with world offset...
val = (v->point[0] + faceoffset[0]) * tex->vecs[j][0]
+ (v->point[1] + faceoffset[1]) * tex->vecs[j][1]
+ (v->point[2] + faceoffset[2]) * tex->vecs[j][2]
+ tex->vecs[j][3];
if (val < mins[j])
mins[j] = val;
if (val > maxs[j])
maxs[j] = val;
}
}
face_centroid(s, centroid);
for (i = 0; i < 2; i++) {
l->exactmid[i] =
(centroid[0] + faceoffset[0]) * tex->vecs[i][0]
+ (centroid[1] + faceoffset[1]) * tex->vecs[i][1]
+ (centroid[2] + faceoffset[2]) * tex->vecs[i][2]
+ tex->vecs[i][3];
mins[i] = floor(mins[i] / 16);
maxs[i] = ceil(maxs[i] / 16);
l->texmins[i] = mins[i];
l->texsize[i] = maxs[i] - mins[i];
if (l->texsize[i] > 17)
Error("Bad surface extents");
}
}
/*
* =================
* CalcPoints
* For each texture aligned grid point, back project onto the plane
* to get the world xyz value of the sample point
* =================
*/
static void
CalcPoints(lightinfo_t * l)
{
int i;
int s, t;
int w, h, step;
vec_t starts, startt, us, ut;
vec_t *surf;
vec_t mids, midt;
vec3_t facemid, move;
/* fill in surforg */
/* the points are biased towards the center of the surface */
/* to help avoid edge cases just inside walls */
surf = l->surfpt[0];
mids = l->exactmid[0];
midt = l->exactmid[1];
tex_to_world(mids, midt, l, facemid);
if (extrasamples) { /* extra filtering */
h = (l->texsize[1] + 1) * 2;
w = (l->texsize[0] + 1) * 2;
starts = (l->texmins[0] - 0.5) * 16;
startt = (l->texmins[1] - 0.5) * 16;
step = 8;
} else {
h = l->texsize[1] + 1;
w = l->texsize[0] + 1;
starts = l->texmins[0] * 16;
startt = l->texmins[1] * 16;
step = 16;
}
l->numsurfpt = w * h;
for (t = 0; t < h; t++) {
for (s = 0; s < w; s++, surf += 3) {
us = starts + s * step;
ut = startt + t * step;
/* if a line can be traced from surf to facemid, point is good */
for (i = 0; i < 6; i++) {
tex_to_world(us, ut, l, surf);
if (CastRay(facemid, surf) != -1)
break; /* got it */
if (i & 1) { // i is odd
if (us > mids) {
us -= 8;
if (us < mids)
us = mids;
} else {
us += 8;
if (us > mids)
us = mids;
}
} else {
if (ut > midt) {
ut -= 8;
if (ut < midt)
ut = midt;
} else {
ut += 8;
if (ut > midt)
ut = midt;
}
}
/* move surf 8 pixels towards the center */
VectorSubtract(facemid, surf, move);
VectorNormalize(move);
VectorMA(surf, 8, move, surf);
}
}
}
}
/*
* ============================================================================
* FACE LIGHTING
* ============================================================================
*/
static int c_culldistplane;
static int c_proper;
/*
* ==============================================
* LIGHT: Attenuation formulae setup functions
* ==============================================
*/
static vec_t
scaledDistance(vec_t distance, const entity_t *light)
{
switch (light->formula) {
case LF_LINEAR:
return scaledist * light->atten * distance;
case LF_INVERSE:
case LF_INVERSE2:
case LF_INFINITE:
/* Return a small distance to prevent culling these lights, since we */
/* know these formulae won't fade to nothing. */
return (distance <= 0.0) ? -0.25 : 0.25;
default:
Error("Internal error: unknown light formula");
}
}
static vec_t
scaledLight(vec_t distance, const entity_t *light)
{
const vec_t tmp = scaledist * light->atten * distance;
switch (light->formula) {
case LF_INFINITE:
return light->light;
case LF_INVERSE:
return light->light / (tmp / LF_SCALE);
case LF_INVERSE2:
return light->light / ((tmp * tmp) / (LF_SCALE * LF_SCALE));
case LF_LINEAR:
if (light->light > 0)
return (light->light - tmp > 0) ? light->light - tmp : 0;
else
return (light->light + tmp < 0) ? light->light + tmp : 0;
default:
Error("Internal error: unknown light formula");
}
}
/*
* ================
* SingleLightFace
* ================
*/
static void
SingleLightFace(const entity_t *light, lightinfo_t * l,
const vec3_t faceoffset, const vec3_t colors)
{
vec_t dist;
vec3_t incoming;
vec_t angle;
vec_t add;
vec_t *surf;
qboolean hit;
int mapnum;
int size;
int c, i;
vec3_t rel;
vec3_t spotvec;
vec_t falloff;
vec_t *lightsamp;
/* Colored lighting */
vec3_t *lightcolorsamp;
VectorSubtract(light->origin, bsp_origin, rel);
dist = scaledDistance((DotProduct(rel, l->facenormal) - l->facedist),
light);
/* don't bother with lights behind the surface */
if (dist < 0)
return;
/* don't bother with light too far away */
if (dist > abs(light->light)) {
c_culldistplane++;
return;
}
if (light->targetent) {
VectorSubtract(light->targetent->origin, light->origin, spotvec);
VectorNormalize(spotvec);
if (!light->angle)
falloff = -cos(20 * Q_PI / 180);
else
falloff = -cos(light->angle / 2 * Q_PI / 180);
} else if (light->use_mangle) {
VectorCopy(light->mangle, spotvec);
if (!light->angle)
falloff = -cos(20 * Q_PI / 180);
else
falloff = -cos(light->angle / 2 * Q_PI / 180);
} else {
falloff = 0; /* shut up compiler warnings */
}
for (mapnum = 0; mapnum < l->numlightstyles; mapnum++)
if (l->lightstyles[mapnum] == light->style)
break;
lightsamp = l->lightmaps[mapnum];
lightcolorsamp = l->lightmapcolors[mapnum];
if (mapnum == l->numlightstyles) {
/* init a new light map */
size = (l->texsize[1] + 1) * (l->texsize[0] + 1);
for (i = 0; i < size; i++) {
if (colored) {
lightcolorsamp[i][0] = 0;
lightcolorsamp[i][1] = 0;
lightcolorsamp[i][2] = 0;
}
lightsamp[i] = 0;
}
}
/* check it for real */
hit = false;
c_proper++;
surf = l->surfpt[0];
for (c = 0; c < l->numsurfpt; c++, surf += 3) {
dist = scaledDistance(CastRay(light->origin, surf), light);
if (dist < 0)
continue; /* light doesn't reach */
VectorSubtract(light->origin, surf, incoming);
VectorNormalize(incoming);
angle = DotProduct(incoming, l->facenormal);
if (light->targetent || light->use_mangle) { /* spotlight cutoff */
if (DotProduct(spotvec, incoming) > falloff)
continue;
}
angle = (1.0 - scalecos) + scalecos * angle;
add = scaledLight(CastRay(light->origin, surf), light);
add *= angle;
lightsamp[c] += add;
if (colored) {
add /= (vec_t)255.0;
lightcolorsamp[c][0] += add * colors[0];
lightcolorsamp[c][1] += add * colors[1];
lightcolorsamp[c][2] += add * colors[2];
}
if (abs(lightsamp[c]) > 1) /* ignore really tiny lights */
hit = true;
}
if (mapnum == l->numlightstyles && hit) {
if (mapnum == MAXLIGHTMAPS - 1) {
logprint("WARNING: Too many light styles on a face\n");
logprint(" lightmap point near (%0.0f, %0.0f, %0.0f)\n",
l->surfpt[0][0], l->surfpt[0][1], l->surfpt[0][2]);
logprint(" light->origin (%0.0f, %0.0f, %0.0f)\n",
light->origin[0], light->origin[1], light->origin[2]);
return;
}
l->lightstyles[mapnum] = light->style;
l->numlightstyles++; /* the style has some real data now */
}
}
/*
* =============
* SkyLightFace
* =============
*/
static void
SkyLightFace(lightinfo_t *l, const vec3_t faceoffset, const vec3_t colors)
{
int i, j;
vec_t *surf;
vec3_t incoming;
vec_t angle;
/* Don't bother if surface facing away from sun */
if (DotProduct(sunmangle, l->facenormal) < -ANGLE_EPSILON)
return;
/* if sunlight is set, use a style 0 light map */
for (i = 0; i < l->numlightstyles; i++)
if (l->lightstyles[i] == 0)
break;
if (i == l->numlightstyles) {
if (l->numlightstyles == MAXLIGHTMAPS)
return; /* oh well, too many lightmaps... */
l->lightstyles[i] = 0;
l->numlightstyles++;
}
/* Check each point... */
VectorCopy(sunmangle, incoming);
VectorNormalize(incoming);
angle = DotProduct(incoming, l->facenormal);
angle = (1.0 - scalecos) + scalecos * angle;
#if 0
/* Experimental - lighting of faces parallel to sunlight*/
{
int a, b, c, k;
vec_t oldangle, offset;
vec3_t sun_vectors[5];
// Try to hit parallel surfaces?
oldangle = DotProduct(incoming, l->facenormal);
if (oldangle < ANGLE_EPSILON) {
printf("real small angle! (%f)\n", oldangle);
angle = (1.0 - scalecos) + scalecos * ANGLE_EPSILON;
}
a = fabs(sunmangle[0]) > fabs(sunmangle[1]) ?
(fabs(sunmangle[0]) > fabs(sunmangle[2]) ? 0 : 2) :
(fabs(sunmangle[1]) > fabs(sunmangle[2]) ? 1 : 2);
b = (a + 1) % 3;
c = (a + 2) % 3;
offset = sunmangle[a] * ANGLE_EPSILON * 2.0; // approx...
for (j = 0; j < 5; ++j)
VectorCopy(sunmangle, sun_vectors[j]);
sun_vectors[1][b] += offset;
sun_vectors[2][b] -= offset;
sun_vectors[3][c] += offset;
sun_vectors[4][c] -= offset;
surf = l->surfpt[0];
for (j = 0; j < l->numsurfpt; j++, surf += 3) {
for (k = 0; k < 1 || (oldangle < ANGLE_EPSILON && k < 5); ++k) {
if (TestSky(surf, sun_vectors[k])) {
l->lightmaps[i][j] += (angle * sunlight);
if (colored)
VectorMA(l->lightmapcolors[i][j],
angle * sunlight / 255,
colors, l->lightmapcolors[i][j]);
break;
}
}
}
}
#else
surf = l->surfpt[0];
for (j = 0; j < l->numsurfpt; j++, surf += 3) {
if (TestSky(surf, sunmangle)) {
l->lightmaps[i][j] += (angle * sunlight);
if (colored)
VectorMA(l->lightmapcolors[i][j], angle * sunlight / 255,
colors, l->lightmapcolors[i][j]);
}
}
#endif
}
/*
* ============
* FixMinlight
* ============
*/
static void
FixMinlight(lightinfo_t * l)
{
int i, j, k;
vec_t tmp;
/* if minlight is set, there must be a style 0 light map */
for (i = 0; i < l->numlightstyles; i++) {
if (l->lightstyles[i] == 0) {
// Set the minimum light level...
break;
} else {
// Set zero minimum...
}
}
if (i == l->numlightstyles) {
if (l->numlightstyles == MAXLIGHTMAPS)
return; /* oh well.. */
for (j = 0; j < l->numsurfpt; j++)
l->lightmaps[i][j] = worldminlight;
if (colored)
for (j = 0; j < l->numsurfpt; j++)
VectorScale(minlight_color, worldminlight / (vec_t)255,
l->lightmapcolors[i][j]);
l->lightstyles[i] = 0;
l->numlightstyles++;
} else {
for (j = 0; j < l->numsurfpt; j++) {
if (l->lightmaps[i][j] < worldminlight)
l->lightmaps[i][j] = worldminlight;
if (colored) {
for (k = 0; k < 3; k++) {
tmp = (vec_t)worldminlight *minlight_color[k];
tmp /= (vec_t)255.0;
if (l->lightmapcolors[i][j][k] < tmp)
l->lightmapcolors[i][j][k] = tmp;
}
}
}
}
}
/*
* light is the light intensity, needed to check if +ve or -ve.
* src and dest are the source and destination color vectors (vec3_t).
* dest becomes a copy of src where
* PositiveColors zeros negative light components.
* NegativeColors zeros positive light components.
*/
static void
PositiveColors(int light, vec3_t dest, const vec3_t src)
{
int i;
if (light >= 0) {
for (i = 0; i < 3; i++)
if (src[i] < 0)
dest[i] = 0;
else
dest[i] = src[i];
} else {
for (i = 0; i < 3; i++)
if (src[i] > 0)
dest[i] = 0;
else
dest[i] = src[i];
}
}
static void
NegativeColors(int light, vec3_t dest, const vec3_t src)
{
int i;
if (light >= 0) {
for (i = 0; i < 3; i++)
if (src[i] > 0)
dest[i] = 0;
else
dest[i] = src[i];
} else {
for (i = 0; i < 3; i++)
if (src[i] < 0)
dest[i] = 0;
else
dest[i] = src[i];
}
}
/*
* ============
* LightFace
* ============
*/
void
LightFace(int surfnum, qboolean nolight, const vec3_t faceoffset)
{
const entity_t *entity;
dface_t *face;
lightinfo_t l;
int s, t;
int i, j, k, c;
vec_t max;
int x1, x2, x3, x4;
vec_t total;
int size;
int lightmapwidth;
int lightmapsize;
byte *out;
byte *lit_out;
vec_t *light;
vec3_t *lightcolor;
vec3_t colors = { 0, 0, 0 };
int width;
vec3_t point;
face = dfaces + surfnum;
/* some surfaces don't need lightmaps */
face->lightofs = -1;
for (j = 0; j < MAXLIGHTMAPS; j++)
face->styles[j] = 255;
if (texinfo[face->texinfo].flags & TEX_SPECIAL)
return; /* non-lit texture */
memset(&l, 0, sizeof(l));
l.surfnum = surfnum;
l.face = face;
/* rotate plane */
VectorCopy(dplanes[face->planenum].normal, l.facenormal);
l.facedist = dplanes[face->planenum].dist;
VectorScale(l.facenormal, l.facedist, point);
VectorAdd(point, faceoffset, point);
l.facedist = DotProduct(point, l.facenormal);
if (face->side) {
VectorSubtract(vec3_origin, l.facenormal, l.facenormal);
l.facedist = -l.facedist;
}
CalcFaceVectors(&l);
CalcFaceExtents(&l, faceoffset);
CalcPoints(&l);
lightmapwidth = l.texsize[0] + 1;
size = lightmapwidth * (l.texsize[1] + 1);
if (size > SINGLEMAP)
Error("Bad lightmap size");
for (i = 0; i < MAXLIGHTMAPS; i++)
l.lightstyles[i] = 255;
/* Under normal circumstances, the lighting procedure is:
* - cast all light entities
* - cast sky lighting
* - do minlighting.
*
* However, if nominlimit is enabled then we need to do the following:
* - cast _positive_ lights
* - cast _positive_ skylight (if any)
* - do minlighting
* - cast _negative_ lights
* - cast _negative_ sky light (if any)
*/
l.numlightstyles = 0;
if (nominlimit) {
/* cast only positive lights */
for (i = 0, entity = entities; i < num_entities; i++, entity++) {
if (colored) {
if (entity->light) {
PositiveColors(entity->light, colors, entity->lightcolor);
SingleLightFace(entity, &l, faceoffset, colors);
}
} else if (entity->light > 0) {
SingleLightFace(entity, &l, faceoffset, colors);
}
}
/* cast positive sky light */
if (sunlight) {
if (colored) {
PositiveColors(sunlight, sunlight_color, colors);
SkyLightFace(&l, faceoffset, colors);
} else if (sunlight > 0) {
SkyLightFace(&l, faceoffset, colors);
}
}
} else {
/* (!nominlimit) => cast all lights */
for (i = 0, entity = entities; i < num_entities; i++, entity++)
if (entity->light)
SingleLightFace(entity, &l, faceoffset, colors);
/* cast sky light */
if (sunlight)
SkyLightFace(&l, faceoffset, colors);
}
/* Minimum lighting */
FixMinlight(&l);
if (nominlimit) {
/* cast only negative lights */
for (i = 0, entity = entities; i < num_entities; i++, entity++) {
if (colored) {
if (entity->light) {
NegativeColors(entity->light, colors, entity->lightcolor);
SingleLightFace(entity, &l, faceoffset, colors);
}
} else if (entity->light < 0) {
SingleLightFace(entity, &l, faceoffset, colors);
}
}
/* cast negative sky light */
if (sunlight) {
if (colored) {
NegativeColors(sunlight, colors, sunlight_color);
SkyLightFace(&l, faceoffset, colors);
} else if (sunlight < 0) {
SkyLightFace(&l, faceoffset, colors);
}
}
/* Fix any negative values */
for (i = 0; i < l.numlightstyles; i++) {
for (j = 0; j < l.numsurfpt; j++) {
if (l.lightmaps[i][j] < 0) {
l.lightmaps[i][j] = 0;
}
if (colored) {
for (k = 0; k < 3; k++) {
if (l.lightmapcolors[i][j][k] < 0) {
l.lightmapcolors[i][j][k] = 0;
}
}
}
}
}
}
if (!l.numlightstyles)
return; /* no light hitting it */
/* save out the values */
for (i = 0; i < MAXLIGHTMAPS; i++)
face->styles[i] = l.lightstyles[i];
/* Extra room for BSP30 lightmaps */
if (colored && bsp30)
lightmapsize = size * l.numlightstyles * 4;
else
lightmapsize = size * l.numlightstyles;
out = GetFileSpace(lightmapsize);
if (litfile)
lit_out = GetLitFileSpace(lightmapsize * 3);
else
lit_out = NULL; /* Fix compiler warning... */
face->lightofs = out - filebase;
/* extra filtering */
width = (l.texsize[0] + 1) * 2;
for (i = 0; i < l.numlightstyles; i++) {
if (l.lightstyles[i] == 0xff)
Error("Wrote empty lightmap");
light = l.lightmaps[i];
lightcolor = l.lightmapcolors[i];
c = 0;
for (t = 0; t <= l.texsize[1]; t++) {
for (s = 0; s <= l.texsize[0]; s++, c++) {
if (extrasamples) {
x1 = t * 2 * width + s * 2;
x2 = x1 + 1;
x3 = (t * 2 + 1) * width + s * 2;
x4 = x3 + 1;
/* filtered sample */
total = light[x1] + light[x2] + light[x3] + light[x4];
total *= 0.25;
/* Calculate the color */
if (colored) {
colors[0] =
lightcolor[x1][0] + lightcolor[x2][0]
+ lightcolor[x3][0] + lightcolor[x4][0];
colors[0] *= 0.25;
colors[1] =
lightcolor[x1][1] + lightcolor[x2][1]
+ lightcolor[x3][1] + lightcolor[x4][1];
colors[1] *= 0.25;
colors[2] =
lightcolor[x1][2] + lightcolor[x2][2]
+ lightcolor[x3][2] + lightcolor[x4][2];
colors[2] *= 0.25;
}
} else {
total = light[c];
if (colored)
VectorCopy(lightcolor[c], colors);
}
total *= rangescale; /* scale before clamping */
if (colored) {
/* Scale back intensity, instead of capping individual
* colors
*/
VectorScale(colors, rangescale, colors);
max = 0.0;
for (j = 0; j < 3; j++)
if (colors[j] > max) {
max = colors[j];
} else if (colors[j] < 0.0f) {
Error("color %i < 0", j);
}
if (max > 255.0f)
VectorScale(colors, 255.0f / max, colors);
}
if (total > 255.0f)
total = 255.0f;
else if (total < 0) {
//Error ("light < 0");
total = 0;
}
/* Write out the lightmap in the appropriate format */
if (colored && bsp30) {
*out++ = colors[0];
*out++ = colors[1];
*out++ = colors[2];
}
if (colored && litfile) {
*lit_out++ = colors[0];
*lit_out++ = colors[1];
*lit_out++ = colors[2];
}
*out++ = total;
}
}
}
}