mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-29 23:22:23 +00:00
1332 lines
28 KiB
C
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 );
|
|
}
|
|
}
|