gtkradiant/tools/quake2/q2map/lightmap.c
2012-03-17 15:01:54 -05:00

1332 lines
28 KiB
C

/*
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "qrad.h"
#define MAX_LSTYLES 256
typedef struct
{
dface_t *faces[2];
qboolean coplanar;
} edgeshare_t;
edgeshare_t edgeshare[MAX_MAP_EDGES];
int facelinks[MAX_MAP_FACES];
int planelinks[2][MAX_MAP_PLANES];
/*
============
LinkPlaneFaces
============
*/
void LinkPlaneFaces( void ){
int i;
dface_t *f;
f = dfaces;
for ( i = 0 ; i < numfaces ; i++, f++ )
{
facelinks[i] = planelinks[f->side][f->planenum];
planelinks[f->side][f->planenum] = i;
}
}
/*
============
PairEdges
============
*/
void PairEdges( void ){
int i, j, k;
dface_t *f;
edgeshare_t *e;
f = dfaces;
for ( i = 0 ; i < numfaces ; i++, f++ )
{
for ( j = 0 ; j < f->numedges ; j++ )
{
k = dsurfedges[f->firstedge + j];
if ( k < 0 ) {
e = &edgeshare[-k];
e->faces[1] = f;
}
else
{
e = &edgeshare[k];
e->faces[0] = f;
}
if ( e->faces[0] && e->faces[1] ) {
// determine if coplanar
if ( e->faces[0]->planenum == e->faces[1]->planenum ) {
e->coplanar = true;
}
}
}
}
}
/*
=================================================================
POINT TRIANGULATION
=================================================================
*/
typedef struct triedge_s
{
int p0, p1;
vec3_t normal;
vec_t dist;
struct triangle_s *tri;
} triedge_t;
typedef struct triangle_s
{
triedge_t *edges[3];
} triangle_t;
#define MAX_TRI_POINTS 1024
#define MAX_TRI_EDGES ( MAX_TRI_POINTS * 6 )
#define MAX_TRI_TRIS ( MAX_TRI_POINTS * 2 )
typedef struct
{
int numpoints;
int numedges;
int numtris;
dplane_t *plane;
triedge_t *edgematrix[MAX_TRI_POINTS][MAX_TRI_POINTS];
patch_t *points[MAX_TRI_POINTS];
triedge_t edges[MAX_TRI_EDGES];
triangle_t tris[MAX_TRI_TRIS];
} triangulation_t;
/*
===============
AllocTriangulation
===============
*/
triangulation_t *AllocTriangulation( dplane_t *plane ){
triangulation_t *t;
t = malloc( sizeof( triangulation_t ) );
t->numpoints = 0;
t->numedges = 0;
t->numtris = 0;
t->plane = plane;
// memset (t->edgematrix, 0, sizeof(t->edgematrix));
return t;
}
/*
===============
FreeTriangulation
===============
*/
void FreeTriangulation( triangulation_t *tr ){
free( tr );
}
triedge_t *FindEdge( triangulation_t *trian, int p0, int p1 ){
triedge_t *e, *be;
vec3_t v1;
vec3_t normal;
vec_t dist;
if ( trian->edgematrix[p0][p1] ) {
return trian->edgematrix[p0][p1];
}
if ( trian->numedges > MAX_TRI_EDGES - 2 ) {
Error( "trian->numedges > MAX_TRI_EDGES-2" );
}
VectorSubtract( trian->points[p1]->origin, trian->points[p0]->origin, v1 );
VectorNormalize( v1, v1 );
CrossProduct( v1, trian->plane->normal, normal );
dist = DotProduct( trian->points[p0]->origin, normal );
e = &trian->edges[trian->numedges];
e->p0 = p0;
e->p1 = p1;
e->tri = NULL;
VectorCopy( normal, e->normal );
e->dist = dist;
trian->numedges++;
trian->edgematrix[p0][p1] = e;
be = &trian->edges[trian->numedges];
be->p0 = p1;
be->p1 = p0;
be->tri = NULL;
VectorSubtract( vec3_origin, normal, be->normal );
be->dist = -dist;
trian->numedges++;
trian->edgematrix[p1][p0] = be;
return e;
}
triangle_t *AllocTriangle( triangulation_t *trian ){
triangle_t *t;
if ( trian->numtris >= MAX_TRI_TRIS ) {
Error( "trian->numtris >= MAX_TRI_TRIS" );
}
t = &trian->tris[trian->numtris];
trian->numtris++;
return t;
}
/*
============
TriEdge_r
============
*/
void TriEdge_r( triangulation_t *trian, triedge_t *e ){
int i, bestp;
vec3_t v1, v2;
vec_t *p0, *p1, *p;
vec_t best, ang;
triangle_t *nt;
if ( e->tri ) {
return; // allready connected by someone
}
// find the point with the best angle
p0 = trian->points[e->p0]->origin;
p1 = trian->points[e->p1]->origin;
best = 1.1;
for ( i = 0 ; i < trian->numpoints ; i++ )
{
p = trian->points[i]->origin;
// a 0 dist will form a degenerate triangle
if ( DotProduct( p, e->normal ) - e->dist < 0 ) {
continue; // behind edge
}
VectorSubtract( p0, p, v1 );
VectorSubtract( p1, p, v2 );
if ( !VectorNormalize( v1,v1 ) ) {
continue;
}
if ( !VectorNormalize( v2,v2 ) ) {
continue;
}
ang = DotProduct( v1, v2 );
if ( ang < best ) {
best = ang;
bestp = i;
}
}
if ( best >= 1 ) {
return; // edge doesn't match anything
}
// make a new triangle
nt = AllocTriangle( trian );
nt->edges[0] = e;
nt->edges[1] = FindEdge( trian, e->p1, bestp );
nt->edges[2] = FindEdge( trian, bestp, e->p0 );
for ( i = 0 ; i < 3 ; i++ )
nt->edges[i]->tri = nt;
TriEdge_r( trian, FindEdge( trian, bestp, e->p1 ) );
TriEdge_r( trian, FindEdge( trian, e->p0, bestp ) );
}
/*
============
TriangulatePoints
============
*/
void TriangulatePoints( triangulation_t *trian ){
vec_t d, bestd;
vec3_t v1;
int bp1, bp2, i, j;
vec_t *p1, *p2;
triedge_t *e, *e2;
if ( trian->numpoints < 2 ) {
return;
}
// find the two closest points
bestd = 9999;
for ( i = 0 ; i < trian->numpoints ; i++ )
{
p1 = trian->points[i]->origin;
for ( j = i + 1 ; j < trian->numpoints ; j++ )
{
p2 = trian->points[j]->origin;
VectorSubtract( p2, p1, v1 );
d = VectorLength( v1 );
if ( d < bestd ) {
bestd = d;
bp1 = i;
bp2 = j;
}
}
}
e = FindEdge( trian, bp1, bp2 );
e2 = FindEdge( trian, bp2, bp1 );
TriEdge_r( trian, e );
TriEdge_r( trian, e2 );
}
/*
===============
AddPointToTriangulation
===============
*/
void AddPointToTriangulation( patch_t *patch, triangulation_t *trian ){
int pnum;
pnum = trian->numpoints;
if ( pnum == MAX_TRI_POINTS ) {
Error( "trian->numpoints == MAX_TRI_POINTS" );
}
trian->points[pnum] = patch;
trian->numpoints++;
}
/*
===============
LerpTriangle
===============
*/
void LerpTriangle( triangulation_t *trian, triangle_t *t, vec3_t point, vec3_t color ){
patch_t *p1, *p2, *p3;
vec3_t base, d1, d2;
float x, y, x1, y1, x2, y2;
p1 = trian->points[t->edges[0]->p0];
p2 = trian->points[t->edges[1]->p0];
p3 = trian->points[t->edges[2]->p0];
VectorCopy( p1->totallight, base );
VectorSubtract( p2->totallight, base, d1 );
VectorSubtract( p3->totallight, base, d2 );
x = DotProduct( point, t->edges[0]->normal ) - t->edges[0]->dist;
y = DotProduct( point, t->edges[2]->normal ) - t->edges[2]->dist;
x1 = 0;
y1 = DotProduct( p2->origin, t->edges[2]->normal ) - t->edges[2]->dist;
x2 = DotProduct( p3->origin, t->edges[0]->normal ) - t->edges[0]->dist;
y2 = 0;
if ( fabs( y1 ) < ON_EPSILON || fabs( x2 ) < ON_EPSILON ) {
VectorCopy( base, color );
return;
}
VectorMA( base, x / x2, d2, color );
VectorMA( color, y / y1, d1, color );
}
qboolean PointInTriangle( vec3_t point, triangle_t *t ){
int i;
triedge_t *e;
vec_t d;
for ( i = 0 ; i < 3 ; i++ )
{
e = t->edges[i];
d = DotProduct( e->normal, point ) - e->dist;
if ( d < 0 ) {
return false; // not inside
}
}
return true;
}
/*
===============
SampleTriangulation
===============
*/
void SampleTriangulation( vec3_t point, triangulation_t *trian, vec3_t color ){
triangle_t *t;
triedge_t *e;
vec_t d, best;
patch_t *p0, *p1;
vec3_t v1, v2;
int i, j;
if ( trian->numpoints == 0 ) {
VectorClear( color );
return;
}
if ( trian->numpoints == 1 ) {
VectorCopy( trian->points[0]->totallight, color );
return;
}
// search for triangles
for ( t = trian->tris, j = 0 ; j < trian->numtris ; t++, j++ )
{
if ( !PointInTriangle( point, t ) ) {
continue;
}
// this is it
LerpTriangle( trian, t, point, color );
return;
}
// search for exterior edge
for ( e = trian->edges, j = 0 ; j < trian->numedges ; e++, j++ )
{
if ( e->tri ) {
continue; // not an exterior edge
}
d = DotProduct( point, e->normal ) - e->dist;
if ( d < 0 ) {
continue; // not in front of edge
}
p0 = trian->points[e->p0];
p1 = trian->points[e->p1];
VectorSubtract( p1->origin, p0->origin, v1 );
VectorNormalize( v1, v1 );
VectorSubtract( point, p0->origin, v2 );
d = DotProduct( v2, v1 );
if ( d < 0 ) {
continue;
}
if ( d > 1 ) {
continue;
}
for ( i = 0 ; i < 3 ; i++ )
color[i] = p0->totallight[i] + d * ( p1->totallight[i] - p0->totallight[i] );
return;
}
// search for nearest point
best = 99999;
p1 = NULL;
for ( j = 0 ; j < trian->numpoints ; j++ )
{
p0 = trian->points[j];
VectorSubtract( point, p0->origin, v1 );
d = VectorLength( v1 );
if ( d < best ) {
best = d;
p1 = p0;
}
}
if ( !p1 ) {
Error( "SampleTriangulation: no points" );
}
VectorCopy( p1->totallight, color );
}
/*
=================================================================
LIGHTMAP SAMPLE GENERATION
=================================================================
*/
#define SINGLEMAP ( 64 * 64 * 4 )
typedef struct
{
vec_t facedist;
vec3_t facenormal;
int numsurfpt;
vec3_t surfpt[SINGLEMAP];
vec3_t modelorg; // for origined bmodels
vec3_t texorg;
vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]
vec3_t textoworld[2]; // world = texorg + s * textoworld[0]
vec_t exactmins[2], exactmaxs[2];
int texmins[2], texsize[2];
int surfnum;
dface_t *face;
} lightinfo_t;
/*
================
CalcFaceExtents
Fills in s->texmins[] and s->texsize[]
also sets exactmins[] and exactmaxs[]
================
*/
void CalcFaceExtents( lightinfo_t *l ){
dface_t *s;
vec_t mins[2], maxs[2], val;
int i,j, e;
dvertex_t *v;
texinfo_t *tex;
vec3_t vt;
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];
}
// VectorAdd (v->point, l->modelorg, vt);
VectorCopy( v->point, vt );
for ( j = 0 ; j < 2 ; j++ )
{
val = DotProduct( vt, tex->vecs[j] ) + tex->vecs[j][3];
if ( val < mins[j] ) {
mins[j] = val;
}
if ( val > maxs[j] ) {
maxs[j] = val;
}
}
}
for ( i = 0 ; i < 2 ; i++ )
{
l->exactmins[i] = mins[i];
l->exactmaxs[i] = maxs[i];
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[0] * l->texsize[1] > SINGLEMAP / 4 ) { // div 4 for extrasamples
Error( "Surface to large to map" );
}
}
}
/*
================
CalcFaceVectors
Fills in texorg, worldtotex. and textoworld
================
*/
void CalcFaceVectors( lightinfo_t *l ){
texinfo_t *tex;
int i, j;
vec3_t texnormal;
vec_t distscale;
vec_t dist, len;
int w, h;
tex = &texinfo[l->face->texinfo];
// convert from float to double
for ( i = 0 ; i < 2 ; i++ )
for ( j = 0 ; j < 3 ; j++ )
l->worldtotex[i][j] = tex->vecs[i][j];
// calculate a normal to the texture axis. points can be moved along this
// without changing their S/T
texnormal[0] = tex->vecs[1][1] * tex->vecs[0][2]
- tex->vecs[1][2] * tex->vecs[0][1];
texnormal[1] = tex->vecs[1][2] * tex->vecs[0][0]
- tex->vecs[1][0] * tex->vecs[0][2];
texnormal[2] = tex->vecs[1][0] * tex->vecs[0][1]
- tex->vecs[1][1] * tex->vecs[0][0];
VectorNormalize( texnormal, texnormal );
// flip it towards plane normal
distscale = DotProduct( texnormal, l->facenormal );
if ( !distscale ) {
Sys_FPrintf( SYS_VRB, "WARNING: Texture axis perpendicular to face\n" );
distscale = 1;
}
if ( distscale < 0 ) {
distscale = -distscale;
VectorSubtract( vec3_origin, texnormal, texnormal );
}
// distscale is the ratio of the distance along the texture normal to
// the distance along the plane normal
distscale = 1 / distscale;
for ( i = 0 ; i < 2 ; i++ )
{
len = VectorLength( l->worldtotex[i] );
dist = DotProduct( l->worldtotex[i], l->facenormal );
dist *= distscale;
VectorMA( l->worldtotex[i], -dist, texnormal, l->textoworld[i] );
VectorScale( l->textoworld[i], ( 1 / len ) * ( 1 / len ), l->textoworld[i] );
}
// calculate texorg on the texture plane
for ( i = 0 ; i < 3 ; i++ )
l->texorg[i] = -tex->vecs[0][3] * l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];
// project back to the face plane
dist = DotProduct( l->texorg, l->facenormal ) - l->facedist - 1;
dist *= distscale;
VectorMA( l->texorg, -dist, texnormal, l->texorg );
// compensate for org'd bmodels
VectorAdd( l->texorg, l->modelorg, l->texorg );
// total sample count
h = l->texsize[1] + 1;
w = l->texsize[0] + 1;
l->numsurfpt = w * h;
}
/*
=================
CalcPoints
For each texture aligned grid point, back project onto the plane
to get the world xyz value of the sample point
=================
*/
void CalcPoints( lightinfo_t *l, float sofs, float tofs ){
int i;
int s, t, j;
int w, h, step;
vec_t starts, startt, us, ut;
vec_t *surf;
vec_t mids, midt;
vec3_t facemid;
dleaf_t *leaf;
surf = l->surfpt[0];
mids = ( l->exactmaxs[0] + l->exactmins[0] ) / 2;
midt = ( l->exactmaxs[1] + l->exactmins[1] ) / 2;
for ( j = 0 ; j < 3 ; j++ )
facemid[j] = l->texorg[j] + l->textoworld[0][j] * mids + l->textoworld[1][j] * midt;
h = l->texsize[1] + 1;
w = l->texsize[0] + 1;
l->numsurfpt = w * h;
starts = l->texmins[0] * 16;
startt = l->texmins[1] * 16;
step = 16;
for ( t = 0 ; t < h ; t++ )
{
for ( s = 0 ; s < w ; s++, surf += 3 )
{
us = starts + ( s + sofs ) * step;
ut = startt + ( t + tofs ) * step;
// if a line can be traced from surf to facemid, the point is good
for ( i = 0 ; i < 6 ; i++ )
{
// calculate texture point
for ( j = 0 ; j < 3 ; j++ )
surf[j] = l->texorg[j] + l->textoworld[0][j] * us
+ l->textoworld[1][j] * ut;
leaf = Rad_PointInLeaf( surf );
if ( leaf->contents != CONTENTS_SOLID ) {
if ( !TestLine_r( 0, facemid, surf ) ) {
break; // got it
}
}
// nudge it
if ( i & 1 ) {
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;
}
}
}
}
}
}
}
//==============================================================
#define MAX_STYLES 32
typedef struct
{
int numsamples;
float *origins;
int numstyles;
int stylenums[MAX_STYLES];
float *samples[MAX_STYLES];
} facelight_t;
directlight_t *directlights[MAX_MAP_LEAFS];
facelight_t facelight[MAX_MAP_FACES];
int numdlights;
/*
==================
FindTargetEntity
==================
*/
entity_t *FindTargetEntity( char *target ){
int i;
char *n;
for ( i = 0 ; i < num_entities ; i++ )
{
n = ValueForKey( &entities[i], "targetname" );
if ( !strcmp( n, target ) ) {
return &entities[i];
}
}
return NULL;
}
//#define DIRECT_LIGHT 3000
#define DIRECT_LIGHT 3
/*
=============
CreateDirectLights
=============
*/
void CreateDirectLights( void ){
int i;
patch_t *p;
directlight_t *dl;
dleaf_t *leaf;
int cluster;
entity_t *e, *e2;
char *name;
char *target;
float angle;
vec3_t dest;
char *_color;
float intensity;
//
// surfaces
//
for ( i = 0, p = patches ; i < (int) num_patches ; i++, p++ )
{
if ( p->totallight[0] < DIRECT_LIGHT
&& p->totallight[1] < DIRECT_LIGHT
&& p->totallight[2] < DIRECT_LIGHT ) {
continue;
}
numdlights++;
dl = malloc( sizeof( directlight_t ) );
memset( dl, 0, sizeof( *dl ) );
VectorCopy( p->origin, dl->origin );
leaf = Rad_PointInLeaf( dl->origin );
cluster = leaf->cluster;
dl->next = directlights[cluster];
directlights[cluster] = dl;
dl->type = emit_surface;
VectorCopy( p->plane->normal, dl->normal );
dl->intensity = ColorNormalize( p->totallight, dl->color );
dl->intensity *= p->area * direct_scale;
VectorClear( p->totallight ); // all sent now
}
//
// entities
//
for ( i = 0 ; i < num_entities ; i++ )
{
e = &entities[i];
name = ValueForKey( e, "classname" );
if ( strncmp( name, "light", 5 ) ) {
continue;
}
numdlights++;
dl = malloc( sizeof( directlight_t ) );
memset( dl, 0, sizeof( *dl ) );
GetVectorForKey( e, "origin", dl->origin );
dl->style = FloatForKey( e, "_style" );
if ( !dl->style ) {
dl->style = FloatForKey( e, "style" );
}
if ( dl->style < 0 || dl->style >= MAX_LSTYLES ) {
dl->style = 0;
}
leaf = Rad_PointInLeaf( dl->origin );
cluster = leaf->cluster;
dl->next = directlights[cluster];
directlights[cluster] = dl;
intensity = FloatForKey( e, "light" );
if ( !intensity ) {
intensity = FloatForKey( e, "_light" );
}
if ( !intensity ) {
intensity = 300;
}
_color = ValueForKey( e, "_color" );
if ( _color && _color[0] ) {
sscanf( _color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2] );
ColorNormalize( dl->color, dl->color );
}
else{
dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
}
dl->intensity = intensity * entity_scale;
dl->type = emit_point;
target = ValueForKey( e, "target" );
if ( !strcmp( name, "light_spot" ) || target[0] ) {
dl->type = emit_spotlight;
dl->stopdot = FloatForKey( e, "_cone" );
if ( !dl->stopdot ) {
dl->stopdot = 10;
}
dl->stopdot = cos( dl->stopdot / 180 * 3.14159 );
if ( target[0] ) { // point towards target
e2 = FindTargetEntity( target );
if ( !e2 ) {
Sys_Printf( "WARNING: light at (%i %i %i) has missing target\n",
(int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2] );
}
else
{
GetVectorForKey( e2, "origin", dest );
VectorSubtract( dest, dl->origin, dl->normal );
VectorNormalize( dl->normal, dl->normal );
}
}
else
{ // point down angle
angle = FloatForKey( e, "angle" );
if ( angle == ANGLE_UP ) {
dl->normal[0] = dl->normal[1] = 0;
dl->normal[2] = 1;
}
else if ( angle == ANGLE_DOWN ) {
dl->normal[0] = dl->normal[1] = 0;
dl->normal[2] = -1;
}
else
{
dl->normal[2] = 0;
dl->normal[0] = cos( angle / 180 * 3.14159 );
dl->normal[1] = sin( angle / 180 * 3.14159 );
}
}
}
}
Sys_FPrintf( SYS_VRB, "%i direct lights\n", numdlights );
}
/*
=============
GatherSampleLight
Lightscale is the normalizer for multisampling
=============
*/
void GatherSampleLight( vec3_t pos, vec3_t normal,
float **styletable, int offset, int mapsize, float lightscale ){
int i;
directlight_t *l;
byte pvs[( MAX_MAP_LEAFS + 7 ) / 8];
vec3_t delta;
float dot, dot2;
float dist;
float scale;
float *dest;
// get the PVS for the pos to limit the number of checks
if ( !PvsForOrigin( pos, pvs ) ) {
return;
}
for ( i = 0 ; i < dvis->numclusters ; i++ )
{
if ( !( pvs[ i >> 3] & ( 1 << ( i & 7 ) ) ) ) {
continue;
}
for ( l = directlights[i] ; l ; l = l->next )
{
VectorSubtract( l->origin, pos, delta );
dist = VectorNormalize( delta, delta );
dot = DotProduct( delta, normal );
if ( dot <= 0.001 ) {
continue; // behind sample surface
}
switch ( l->type )
{
case emit_point:
// linear falloff
scale = ( l->intensity - dist ) * dot;
break;
case emit_surface:
dot2 = -DotProduct( delta, l->normal );
if ( dot2 <= 0.001 ) {
goto skipadd; // behind light surface
}
scale = ( l->intensity / ( dist * dist ) ) * dot * dot2;
break;
case emit_spotlight:
// linear falloff
dot2 = -DotProduct( delta, l->normal );
if ( dot2 <= l->stopdot ) {
goto skipadd; // outside light cone
}
scale = ( l->intensity - dist ) * dot;
break;
default:
Error( "Bad l->type" );
}
if ( TestLine_r( 0, pos, l->origin ) ) {
continue; // occluded
}
if ( scale <= 0 ) {
continue;
}
// if this style doesn't have a table yet, allocate one
if ( !styletable[l->style] ) {
styletable[l->style] = malloc( mapsize );
memset( styletable[l->style], 0, mapsize );
}
dest = styletable[l->style] + offset;
// add some light to it
VectorMA( dest, scale * lightscale, l->color, dest );
skipadd: ;
}
}
}
/*
=============
AddSampleToPatch
Take the sample's collected light and
add it back into the apropriate patch
for the radiosity pass.
The sample is added to all patches that might include
any part of it. They are counted and averaged, so it
doesn't generate extra light.
=============
*/
void AddSampleToPatch( vec3_t pos, vec3_t color, int facenum ){
patch_t *patch;
vec3_t mins, maxs;
int i;
if ( numbounce == 0 ) {
return;
}
if ( color[0] + color[1] + color[2] < 3 ) {
return;
}
for ( patch = face_patches[facenum] ; patch ; patch = patch->next )
{
// see if the point is in this patch (roughly)
WindingBounds( patch->winding, mins, maxs );
for ( i = 0 ; i < 3 ; i++ )
{
if ( mins[i] > pos[i] + 16 ) {
goto nextpatch;
}
if ( maxs[i] < pos[i] - 16 ) {
goto nextpatch;
}
}
// add the sample to the patch
patch->samples++;
VectorAdd( patch->samplelight, color, patch->samplelight );
nextpatch:;
}
}
/*
=============
BuildFacelights
=============
*/
float sampleofs[5][2] =
{ {0,0}, {-0.25, -0.25}, {0.25, -0.25}, {0.25, 0.25}, {-0.25, 0.25} };
void BuildFacelights( int facenum ){
dface_t *f;
lightinfo_t l[5];
float *styletable[MAX_LSTYLES];
int i, j;
float *spot;
patch_t *patch;
int numsamples;
int tablesize;
facelight_t *fl;
f = &dfaces[facenum];
if ( texinfo[f->texinfo].flags & ( SURF_WARP | SURF_SKY ) ) {
return; // non-lit texture
}
memset( styletable,0, sizeof( styletable ) );
if ( extrasamples ) {
numsamples = 5;
}
else{
numsamples = 1;
}
for ( i = 0 ; i < numsamples ; i++ )
{
memset( &l[i], 0, sizeof( l[i] ) );
l[i].surfnum = facenum;
l[i].face = f;
VectorCopy( dplanes[f->planenum].normal, l[i].facenormal );
l[i].facedist = dplanes[f->planenum].dist;
if ( f->side ) {
VectorSubtract( vec3_origin, l[i].facenormal, l[i].facenormal );
l[i].facedist = -l[i].facedist;
}
// get the origin offset for rotating bmodels
VectorCopy( face_offset[facenum], l[i].modelorg );
CalcFaceVectors( &l[i] );
CalcFaceExtents( &l[i] );
CalcPoints( &l[i], sampleofs[i][0], sampleofs[i][1] );
}
tablesize = l[0].numsurfpt * sizeof( vec3_t );
styletable[0] = malloc( tablesize );
memset( styletable[0], 0, tablesize );
fl = &facelight[facenum];
fl->numsamples = l[0].numsurfpt;
fl->origins = malloc( tablesize );
memcpy( fl->origins, l[0].surfpt, tablesize );
for ( i = 0 ; i < l[0].numsurfpt ; i++ )
{
for ( j = 0 ; j < numsamples ; j++ )
{
GatherSampleLight( l[j].surfpt[i], l[0].facenormal, styletable,
i * 3, tablesize, 1.0 / numsamples );
}
// contribute the sample to one or more patches
AddSampleToPatch( l[0].surfpt[i], styletable[0] + i * 3, facenum );
}
// average up the direct light on each patch for radiosity
for ( patch = face_patches[facenum] ; patch ; patch = patch->next )
{
if ( patch->samples ) {
VectorScale( patch->samplelight, 1.0 / patch->samples, patch->samplelight );
}
else
{
// printf ("patch with no samples\n");
}
}
for ( i = 0 ; i < MAX_LSTYLES ; i++ )
{
if ( !styletable[i] ) {
continue;
}
if ( fl->numstyles == MAX_STYLES ) {
break;
}
fl->samples[fl->numstyles] = styletable[i];
fl->stylenums[fl->numstyles] = i;
fl->numstyles++;
}
// the light from DIRECT_LIGHTS is sent out, but the
// texture itself should still be full bright
if ( face_patches[facenum]->baselight[0] >= DIRECT_LIGHT ||
face_patches[facenum]->baselight[1] >= DIRECT_LIGHT ||
face_patches[facenum]->baselight[2] >= DIRECT_LIGHT
) {
spot = fl->samples[0];
for ( i = 0 ; i < l[0].numsurfpt ; i++, spot += 3 )
{
VectorAdd( spot, face_patches[facenum]->baselight, spot );
}
}
}
/*
=============
FinalLightFace
Add the indirect lighting on top of the direct
lighting and save into final map format
=============
*/
void FinalLightFace( int facenum ){
dface_t *f;
int i, j, k, st;
vec3_t lb;
patch_t *patch;
triangulation_t *trian;
facelight_t *fl;
float minlight;
float max, newmax;
byte *dest;
int pfacenum;
vec3_t facemins, facemaxs;
f = &dfaces[facenum];
fl = &facelight[facenum];
if ( texinfo[f->texinfo].flags & ( SURF_WARP | SURF_SKY ) ) {
return; // non-lit texture
}
ThreadLock();
f->lightofs = lightdatasize;
lightdatasize += fl->numstyles * ( fl->numsamples * 3 );
// add green sentinals between lightmaps
#if 0
lightdatasize += 64 * 3;
for ( i = 0 ; i < 64 ; i++ )
dlightdata[lightdatasize - ( i + 1 ) * 3 + 1] = 255;
#endif
if ( lightdatasize > MAX_MAP_LIGHTING ) {
Error( "MAX_MAP_LIGHTING" );
}
ThreadUnlock();
f->styles[0] = 0;
f->styles[1] = f->styles[2] = f->styles[3] = 0xff;
//
// set up the triangulation
//
if ( numbounce > 0 ) {
ClearBounds( facemins, facemaxs );
for ( i = 0 ; i < f->numedges ; i++ )
{
int ednum;
ednum = dsurfedges[f->firstedge + i];
if ( ednum >= 0 ) {
AddPointToBounds( dvertexes[dedges[ednum].v[0]].point,
facemins, facemaxs );
}
else{
AddPointToBounds( dvertexes[dedges[-ednum].v[1]].point,
facemins, facemaxs );
}
}
trian = AllocTriangulation( &dplanes[f->planenum] );
// for all faces on the plane, add the nearby patches
// to the triangulation
for ( pfacenum = planelinks[f->side][f->planenum]
; pfacenum ; pfacenum = facelinks[pfacenum] )
{
for ( patch = face_patches[pfacenum] ; patch ; patch = patch->next )
{
for ( i = 0 ; i < 3 ; i++ )
{
if ( facemins[i] - patch->origin[i] > subdiv * 2 ) {
break;
}
if ( patch->origin[i] - facemaxs[i] > subdiv * 2 ) {
break;
}
}
if ( i != 3 ) {
continue; // not needed for this face
}
AddPointToTriangulation( patch, trian );
}
}
for ( i = 0 ; i < trian->numpoints ; i++ )
memset( trian->edgematrix[i], 0, trian->numpoints * sizeof( trian->edgematrix[0][0] ) );
TriangulatePoints( trian );
}
//
// sample the triangulation
//
// _minlight allows models that have faces that would not be
// illuminated to receive a mottled light pattern instead of
// black
minlight = FloatForKey( face_entity[facenum], "_minlight" ) * 128;
dest = &dlightdata[f->lightofs];
if ( fl->numstyles > MAXLIGHTMAPS ) {
fl->numstyles = MAXLIGHTMAPS;
Sys_Printf( "face with too many lightstyles: (%f %f %f)\n",
face_patches[facenum]->origin[0],
face_patches[facenum]->origin[1],
face_patches[facenum]->origin[2]
);
}
for ( st = 0 ; st < fl->numstyles ; st++ )
{
f->styles[st] = fl->stylenums[st];
for ( j = 0 ; j < fl->numsamples ; j++ )
{
VectorCopy( ( fl->samples[st] + j * 3 ), lb );
if ( numbounce > 0 && st == 0 ) {
vec3_t add;
SampleTriangulation( fl->origins + j * 3, trian, add );
VectorAdd( lb, add, lb );
}
// add an ambient term if desired
lb[0] += ambient;
lb[1] += ambient;
lb[2] += ambient;
VectorScale( lb, lightscale, lb );
// we need to clamp without allowing hue to change
for ( k = 0 ; k < 3 ; k++ )
if ( lb[k] < 1 ) {
lb[k] = 1;
}
max = lb[0];
if ( lb[1] > max ) {
max = lb[1];
}
if ( lb[2] > max ) {
max = lb[2];
}
newmax = max;
if ( newmax < 0 ) {
newmax = 0; // roundoff problems
}
if ( newmax < minlight ) {
newmax = minlight + ( rand() % 48 );
}
if ( newmax > maxlight ) {
newmax = maxlight;
}
for ( k = 0 ; k < 3 ; k++ )
{
*dest++ = lb[k] * newmax / max;
}
}
}
if ( numbounce > 0 ) {
FreeTriangulation( trian );
}
}