mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2024-11-13 00:24:29 +00:00
648 lines
12 KiB
C
648 lines
12 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
|
|
*/
|
|
// qrad.c
|
|
|
|
#include "qrad.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
NOTES
|
|
-----
|
|
|
|
every surface must be divided into at least two patches each axis
|
|
|
|
*/
|
|
|
|
patch_t *face_patches[MAX_MAP_FACES];
|
|
entity_t *face_entity[MAX_MAP_FACES];
|
|
patch_t patches[MAX_PATCHES];
|
|
unsigned num_patches;
|
|
|
|
vec3_t radiosity[MAX_PATCHES]; // light leaving a patch
|
|
vec3_t illumination[MAX_PATCHES]; // light arriving at a patch
|
|
|
|
vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels
|
|
dplane_t backplanes[MAX_MAP_PLANES];
|
|
|
|
char inbase[32], outbase[32];
|
|
|
|
int fakeplanes; // created planes for origin offset
|
|
|
|
int numbounce = 8;
|
|
qboolean extrasamples;
|
|
|
|
float subdiv = 64;
|
|
qboolean dumppatches;
|
|
|
|
void BuildLightmaps( void );
|
|
int TestLine( vec3_t start, vec3_t stop );
|
|
|
|
int junk;
|
|
|
|
float ambient = 0;
|
|
float maxlight = 196;
|
|
|
|
float lightscale = 1.0;
|
|
|
|
qboolean glview;
|
|
|
|
qboolean nopvs;
|
|
|
|
char source[1024];
|
|
|
|
float direct_scale = 0.4;
|
|
float entity_scale = 1.0;
|
|
|
|
/*
|
|
===================================================================
|
|
|
|
MISC
|
|
|
|
===================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
=============
|
|
MakeBackplanes
|
|
=============
|
|
*/
|
|
void MakeBackplanes( void ){
|
|
int i;
|
|
|
|
for ( i = 0 ; i < numplanes ; i++ )
|
|
{
|
|
backplanes[i].dist = -dplanes[i].dist;
|
|
VectorSubtract( vec3_origin, dplanes[i].normal, backplanes[i].normal );
|
|
}
|
|
}
|
|
|
|
int leafparents[MAX_MAP_LEAFS];
|
|
int nodeparents[MAX_MAP_NODES];
|
|
|
|
/*
|
|
=============
|
|
MakeParents
|
|
=============
|
|
*/
|
|
void MakeParents( int nodenum, int parent ){
|
|
int i, j;
|
|
dnode_t *node;
|
|
|
|
nodeparents[nodenum] = parent;
|
|
node = &dnodes[nodenum];
|
|
|
|
for ( i = 0 ; i < 2 ; i++ )
|
|
{
|
|
j = node->children[i];
|
|
if ( j < 0 ) {
|
|
leafparents[-j - 1] = nodenum;
|
|
}
|
|
else{
|
|
MakeParents( j, nodenum );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================================================================
|
|
|
|
TRANSFER SCALES
|
|
|
|
===================================================================
|
|
*/
|
|
|
|
int PointInLeafnum( vec3_t point ){
|
|
int nodenum;
|
|
vec_t dist;
|
|
dnode_t *node;
|
|
dplane_t *plane;
|
|
|
|
nodenum = 0;
|
|
while ( nodenum >= 0 )
|
|
{
|
|
node = &dnodes[nodenum];
|
|
plane = &dplanes[node->planenum];
|
|
dist = DotProduct( point, plane->normal ) - plane->dist;
|
|
if ( dist > 0 ) {
|
|
nodenum = node->children[0];
|
|
}
|
|
else{
|
|
nodenum = node->children[1];
|
|
}
|
|
}
|
|
|
|
return -nodenum - 1;
|
|
}
|
|
|
|
|
|
dleaf_t *Rad_PointInLeaf( vec3_t point ){
|
|
int num;
|
|
|
|
num = PointInLeafnum( point );
|
|
return &dleafs[num];
|
|
}
|
|
|
|
|
|
qboolean PvsForOrigin( vec3_t org, byte *pvs ){
|
|
dleaf_t *leaf;
|
|
|
|
if ( !visdatasize ) {
|
|
memset( pvs, 255, ( numleafs + 7 ) / 8 );
|
|
return true;
|
|
}
|
|
|
|
leaf = Rad_PointInLeaf( org );
|
|
if ( leaf->cluster == -1 ) {
|
|
return false; // in solid leaf
|
|
|
|
}
|
|
DecompressVis( dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs );
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
MakeTransfers
|
|
|
|
=============
|
|
*/
|
|
int total_transfer;
|
|
|
|
void MakeTransfers( int i ){
|
|
int j;
|
|
vec3_t delta;
|
|
vec_t dist, scale;
|
|
float trans;
|
|
int itrans;
|
|
patch_t *patch, *patch2;
|
|
float total;
|
|
dplane_t plane;
|
|
vec3_t origin;
|
|
float transfers[MAX_PATCHES], *all_transfers;
|
|
int s;
|
|
int itotal;
|
|
byte pvs[( MAX_MAP_LEAFS + 7 ) / 8];
|
|
int cluster;
|
|
|
|
patch = patches + i;
|
|
total = 0;
|
|
|
|
VectorCopy( patch->origin, origin );
|
|
plane = *patch->plane;
|
|
|
|
if ( !PvsForOrigin( patch->origin, pvs ) ) {
|
|
return;
|
|
}
|
|
|
|
// find out which patch2s will collect light
|
|
// from patch
|
|
|
|
all_transfers = transfers;
|
|
patch->numtransfers = 0;
|
|
for ( j = 0, patch2 = patches ; j < num_patches ; j++, patch2++ )
|
|
{
|
|
transfers[j] = 0;
|
|
|
|
if ( j == i ) {
|
|
continue;
|
|
}
|
|
|
|
// check pvs bit
|
|
if ( !nopvs ) {
|
|
cluster = patch2->cluster;
|
|
if ( cluster == -1 ) {
|
|
continue;
|
|
}
|
|
if ( !( pvs[cluster >> 3] & ( 1 << ( cluster & 7 ) ) ) ) {
|
|
continue; // not in pvs
|
|
}
|
|
}
|
|
|
|
// calculate vector
|
|
VectorSubtract( patch2->origin, origin, delta );
|
|
dist = VectorNormalize( delta, delta );
|
|
if ( !dist ) {
|
|
continue; // should never happen
|
|
|
|
}
|
|
// reletive angles
|
|
scale = DotProduct( delta, plane.normal );
|
|
scale *= -DotProduct( delta, patch2->plane->normal );
|
|
if ( scale <= 0 ) {
|
|
continue;
|
|
}
|
|
|
|
// check exact tramsfer
|
|
if ( TestLine_r( 0, patch->origin, patch2->origin ) ) {
|
|
continue;
|
|
}
|
|
|
|
trans = scale * patch2->area / ( dist * dist );
|
|
|
|
if ( trans < 0 ) {
|
|
trans = 0; // rounding errors...
|
|
|
|
}
|
|
transfers[j] = trans;
|
|
if ( trans > 0 ) {
|
|
total += trans;
|
|
patch->numtransfers++;
|
|
}
|
|
}
|
|
|
|
// copy the transfers out and normalize
|
|
// total should be somewhere near PI if everything went right
|
|
// because partial occlusion isn't accounted for, and nearby
|
|
// patches have underestimated form factors, it will usually
|
|
// be higher than PI
|
|
if ( patch->numtransfers ) {
|
|
transfer_t *t;
|
|
|
|
if ( patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES ) {
|
|
Error( "Weird numtransfers" );
|
|
}
|
|
s = patch->numtransfers * sizeof( transfer_t );
|
|
patch->transfers = malloc( s );
|
|
if ( !patch->transfers ) {
|
|
Error( "Memory allocation failure" );
|
|
}
|
|
|
|
//
|
|
// normalize all transfers so all of the light
|
|
// is transfered to the surroundings
|
|
//
|
|
t = patch->transfers;
|
|
itotal = 0;
|
|
for ( j = 0 ; j < num_patches ; j++ )
|
|
{
|
|
if ( transfers[j] <= 0 ) {
|
|
continue;
|
|
}
|
|
itrans = transfers[j] * 0x10000 / total;
|
|
itotal += itrans;
|
|
t->transfer = itrans;
|
|
t->patch = j;
|
|
t++;
|
|
}
|
|
}
|
|
|
|
// don't bother locking around this. not that important.
|
|
total_transfer += patch->numtransfers;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
FreeTransfers
|
|
=============
|
|
*/
|
|
void FreeTransfers( void ){
|
|
int i;
|
|
|
|
for ( i = 0 ; i < num_patches ; i++ )
|
|
{
|
|
free( patches[i].transfers );
|
|
patches[i].transfers = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
|
|
/*
|
|
=============
|
|
WriteWorld
|
|
=============
|
|
*/
|
|
void WriteWorld( char *name ){
|
|
int i, j;
|
|
FILE *out;
|
|
patch_t *patch;
|
|
winding_t *w;
|
|
|
|
out = fopen( name, "w" );
|
|
if ( !out ) {
|
|
Error( "Couldn't open %s", name );
|
|
}
|
|
|
|
for ( j = 0, patch = patches ; j < num_patches ; j++, patch++ )
|
|
{
|
|
w = patch->winding;
|
|
fprintf( out, "%i\n", w->numpoints );
|
|
for ( i = 0 ; i < w->numpoints ; i++ )
|
|
{
|
|
fprintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
|
|
w->p[i][0],
|
|
w->p[i][1],
|
|
w->p[i][2],
|
|
patch->totallight[0],
|
|
patch->totallight[1],
|
|
patch->totallight[2] );
|
|
}
|
|
fprintf( out, "\n" );
|
|
}
|
|
|
|
fclose( out );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
WriteGlView
|
|
=============
|
|
*/
|
|
void WriteGlView( void ){
|
|
char name[1024];
|
|
FILE *f;
|
|
int i, j;
|
|
patch_t *p;
|
|
winding_t *w;
|
|
|
|
strcpy( name, source );
|
|
StripExtension( name );
|
|
strcat( name, ".glr" );
|
|
|
|
f = fopen( name, "w" );
|
|
if ( !f ) {
|
|
Error( "Couldn't open %s", f );
|
|
}
|
|
|
|
for ( j = 0 ; j < num_patches ; j++ )
|
|
{
|
|
p = &patches[j];
|
|
w = p->winding;
|
|
fprintf( f, "%i\n", w->numpoints );
|
|
for ( i = 0 ; i < w->numpoints ; i++ )
|
|
{
|
|
fprintf( f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
|
|
w->p[i][0],
|
|
w->p[i][1],
|
|
w->p[i][2],
|
|
p->totallight[0] / 128,
|
|
p->totallight[1] / 128,
|
|
p->totallight[2] / 128 );
|
|
}
|
|
fprintf( f, "\n" );
|
|
}
|
|
|
|
fclose( f );
|
|
}
|
|
|
|
|
|
//==============================================================
|
|
|
|
/*
|
|
=============
|
|
CollectLight
|
|
=============
|
|
*/
|
|
float CollectLight( void ){
|
|
int i, j;
|
|
patch_t *patch;
|
|
vec_t total;
|
|
|
|
total = 0;
|
|
|
|
for ( i = 0, patch = patches ; i < num_patches ; i++, patch++ )
|
|
{
|
|
// skys never collect light, it is just dropped
|
|
if ( patch->sky ) {
|
|
VectorClear( radiosity[i] );
|
|
VectorClear( illumination[i] );
|
|
continue;
|
|
}
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
patch->totallight[j] += illumination[i][j] / patch->area;
|
|
radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];
|
|
}
|
|
|
|
total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];
|
|
VectorClear( illumination[i] );
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
ShootLight
|
|
|
|
Send light out to other patches
|
|
Run multi-threaded
|
|
=============
|
|
*/
|
|
void ShootLight( int patchnum ){
|
|
int k, l;
|
|
transfer_t *trans;
|
|
int num;
|
|
patch_t *patch;
|
|
vec3_t send;
|
|
|
|
// this is the amount of light we are distributing
|
|
// prescale it so that multiplying by the 16 bit
|
|
// transfer values gives a proper output value
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
send[k] = radiosity[patchnum][k] / 0x10000;
|
|
patch = &patches[patchnum];
|
|
|
|
trans = patch->transfers;
|
|
num = patch->numtransfers;
|
|
|
|
for ( k = 0 ; k < num ; k++, trans++ )
|
|
{
|
|
for ( l = 0 ; l < 3 ; l++ )
|
|
illumination[trans->patch][l] += send[l] * trans->transfer;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
BounceLight
|
|
=============
|
|
*/
|
|
void BounceLight( void ){
|
|
int i, j;
|
|
float added;
|
|
char name[64];
|
|
patch_t *p;
|
|
|
|
for ( i = 0 ; i < num_patches ; i++ )
|
|
{
|
|
p = &patches[i];
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
// p->totallight[j] = p->samplelight[j];
|
|
radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;
|
|
}
|
|
}
|
|
|
|
for ( i = 0 ; i < numbounce ; i++ )
|
|
{
|
|
RunThreadsOnIndividual( num_patches, false, ShootLight );
|
|
added = CollectLight();
|
|
|
|
Sys_FPrintf( SYS_VRB, "bounce:%i added:%f\n", i, added );
|
|
if ( dumppatches && ( i == 0 || i == numbounce - 1 ) ) {
|
|
sprintf( name, "bounce%i.txt", i );
|
|
WriteWorld( name );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//==============================================================
|
|
|
|
void CheckPatches( void ){
|
|
int i;
|
|
patch_t *patch;
|
|
|
|
for ( i = 0 ; i < num_patches ; i++ )
|
|
{
|
|
patch = &patches[i];
|
|
if ( patch->totallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0 ) {
|
|
Error( "negative patch totallight\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
RadWorld
|
|
=============
|
|
*/
|
|
void RadWorld( void ){
|
|
if ( numnodes == 0 || numfaces == 0 ) {
|
|
Error( "Empty map" );
|
|
}
|
|
MakeBackplanes();
|
|
MakeParents( 0, -1 );
|
|
MakeTnodes( &dmodels[0] );
|
|
|
|
// turn each face into a single patch
|
|
MakePatches();
|
|
|
|
// subdivide patches to a maximum dimension
|
|
SubdividePatches();
|
|
|
|
// create directlights out of patches and lights
|
|
CreateDirectLights();
|
|
|
|
// build initial facelights
|
|
RunThreadsOnIndividual( numfaces, true, BuildFacelights );
|
|
|
|
if ( numbounce > 0 ) {
|
|
// build transfer lists
|
|
RunThreadsOnIndividual( num_patches, true, MakeTransfers );
|
|
Sys_FPrintf( SYS_VRB, "transfer lists: %5.1f megs\n"
|
|
, (float)total_transfer * sizeof( transfer_t ) / ( 1024 * 1024 ) );
|
|
|
|
// spread light around
|
|
BounceLight();
|
|
|
|
FreeTransfers();
|
|
|
|
CheckPatches();
|
|
}
|
|
|
|
if ( glview ) {
|
|
WriteGlView();
|
|
}
|
|
|
|
// blend bounced light into direct light and save
|
|
PairEdges();
|
|
LinkPlaneFaces();
|
|
|
|
lightdatasize = 0;
|
|
RunThreadsOnIndividual( numfaces, true, FinalLightFace );
|
|
}
|
|
|
|
|
|
/*
|
|
========
|
|
main
|
|
|
|
light modelfile
|
|
========
|
|
*/
|
|
int RAD_Main(){
|
|
double start, end;
|
|
char name[1024];
|
|
int total_rad_time;
|
|
|
|
Sys_Printf( "\n----- RAD ----\n\n" );
|
|
|
|
if ( maxlight > 255 ) {
|
|
maxlight = 255;
|
|
}
|
|
|
|
start = I_FloatTime();
|
|
|
|
if ( !strcmp( game, "heretic2" ) ) {
|
|
CalcTextureReflectivity = &CalcTextureReflectivity_Heretic2;
|
|
}
|
|
else{
|
|
CalcTextureReflectivity = &CalcTextureReflectivity_Quake2;
|
|
}
|
|
|
|
SetQdirFromPath( mapname );
|
|
strcpy( source, ExpandArg( mapname ) );
|
|
StripExtension( source );
|
|
DefaultExtension( source, ".bsp" );
|
|
|
|
// ReadLightFile ();
|
|
|
|
sprintf( name, "%s%s", inbase, source );
|
|
Sys_Printf( "reading %s\n", name );
|
|
LoadBSPFile( name );
|
|
ParseEntities();
|
|
( *CalcTextureReflectivity )( );
|
|
|
|
if ( !visdatasize ) {
|
|
Sys_Printf( "No vis information, direct lighting only.\n" );
|
|
numbounce = 0;
|
|
ambient = 0.1;
|
|
}
|
|
|
|
RadWorld();
|
|
|
|
sprintf( name, "%s%s", outbase, source );
|
|
Sys_Printf( "writing %s\n", name );
|
|
WriteBSPFile( name );
|
|
|
|
end = I_FloatTime();
|
|
total_rad_time = (int) ( end - start );
|
|
Sys_Printf( "\nRAD Time: " );
|
|
if ( total_rad_time > 59 ) {
|
|
Sys_Printf( "%d Minutes ", total_rad_time / 60 );
|
|
}
|
|
Sys_Printf( "%d Seconds\n", total_rad_time % 60 );
|
|
|
|
|
|
return 0;
|
|
}
|