HLua/utils/qrad/qrad.c

1570 lines
33 KiB
C

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// 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 emitlight[MAX_PATCHES];
vec3_t addlight[MAX_PATCHES];
vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels
dplane_t backplanes[MAX_MAP_PLANES];
unsigned numbounce = 1; // 3; /* Originally this was 8 */
float maxchop = 64;
float minchop = 64;
qboolean dumppatches;
int TestLine (vec3_t start, vec3_t stop);
int junk;
vec3_t ambient = { 0, 0, 0 };
float maxlight = 256; // 196 /* Originally this was 196 */
float lightscale = 1.0;
float dlight_threshold = 25.0; // was DIRECT_LIGHT constant
char source[MAX_PATH] = "";
char global_lights[MAX_PATH] = "";
char designer_lights[MAX_PATH] = "";
char level_lights[MAX_PATH] = "";
char transferfile[MAX_PATH] = "";
char vismatfile[_MAX_PATH] = "";
char incrementfile[_MAX_PATH] = "";
qboolean incremental = 0;
float gamma = 0.5;
float indirect_sun = 1.0;
qboolean extra = false;
float smoothing_threshold = 0; // default: cos(45.0*(Q_PI/180));
// Cosine of smoothing angle(in radians)
float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps)
qboolean texscale = true;
/*
===================================================================
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);
}
}
/*
===================================================================
TEXTURE LIGHT VALUES
===================================================================
*/
typedef struct
{
char name[256];
vec3_t value;
char *filename;
} texlight_t;
#define MAX_TEXLIGHTS 128
texlight_t texlights[MAX_TEXLIGHTS];
int num_texlights;
/*
============
ReadLightFile
============
*/
void ReadLightFile (char *filename)
{
FILE *f;
char scan[128];
short argCnt;
vec_t intensity;
int i = 1.0, j, file_texlights = 0;
f = fopen (filename, "r");
if (!f)
Error ("ERROR: Couldn't open texlight file %s", filename);
else
printf("[Reading texlights from '%s']\n", filename);
while ( fgets(scan, sizeof(scan), f) )
{
char szTexlight[256];
vec_t r, g, b, i = 1;
if (num_texlights == MAX_TEXLIGHTS)
Error ("MAX_TEXLIGHTS");
argCnt = sscanf (scan, "%s %f %f %f %f",szTexlight, &r, &g, &b, &i );
if( argCnt == 2 )
{
// With 1+1 args, the R,G,B values are all equal to the first value
g = b = r;
}
else if ( argCnt == 5 )
{
// With 1+4 args, the R,G,B values are "scaled" by the fourth numeric value i;
r *= i / 255.0;
g *= i / 255.0;
b *= i / 255.0;
}
else if( argCnt != 4 )
{
if (strlen( scan ) > 4)
printf("ignoring bad texlight '%s' in %s", scan, filename );
continue;
}
for( j=0; j<num_texlights; j++ )
{
if ( strcmp( texlights[j].name, szTexlight ) == 0 )
{
if ( strcmp(texlights[j].filename, filename ) == 0 )
{
printf( "ERROR\a: Duplication of '%s' in file '%s'!\n",
texlights[j].name, texlights[j].filename );
}
else if ( texlights[j].value[0] != r
|| texlights[j].value[1] != g
|| texlights[j].value[2] != b )
{
printf( "Warning: Overriding '%s' from '%s' with '%s'!\n",
texlights[j].name, texlights[j].filename, filename );
}
else
{
printf( "Warning: Redundant '%s' def in '%s' AND '%s'!\n",
texlights[j].name, texlights[j].filename, filename );
}
break;
}
}
strcpy( texlights[j].name, szTexlight );
texlights[j].value[0] = r;
texlights[j].value[1] = g;
texlights[j].value[2] = b;
texlights[j].filename = filename;
file_texlights++;
num_texlights = max( num_texlights, j+1 );
}
qprintf ("[%i texlights parsed from '%s']\n\n", file_texlights, filename);
}
/*
============
LightForTexture
============
*/
void LightForTexture( char *name, vec3_t result )
{
int i;
result[ 0 ] = result[ 1 ] = result[ 2 ] = 0;
for (i=0 ; i<num_texlights ; i++)
{
if (!Q_strcasecmp (name, texlights[i].name))
{
VectorCopy( texlights[i].value, result );
return;
}
}
}
/*
=======================================================================
MAKE FACES
=======================================================================
*/
/*
=============
WindingFromFace
=============
*/
winding_t *WindingFromFace (dface_t *f)
{
int i;
int se;
dvertex_t *dv;
int v;
winding_t *w;
w = AllocWinding (f->numedges);
w->numpoints = f->numedges;
for (i=0 ; i<f->numedges ; i++)
{
se = dsurfedges[f->firstedge + i];
if (se < 0)
v = dedges[-se].v[1];
else
v = dedges[se].v[0];
dv = &dvertexes[v];
VectorCopy (dv->point, w->p[i]);
}
RemoveColinearPoints (w);
return w;
}
/*
=============
BaseLightForFace
=============
*/
void BaseLightForFace( dface_t *f, vec3_t light, vec3_t reflectivity )
{
texinfo_t *tx;
miptex_t *mt;
int ofs;
long sum[3];
long samples = 0;
int x, y, i;
//
// check for light emited by texture
//
tx = &texinfo[f->texinfo];
ofs = ((dmiptexlump_t *)dtexdata)->dataofs[tx->miptex];
mt = (miptex_t *) ( (byte *)dtexdata + ofs);
LightForTexture (mt->name, light);
#ifdef TEXTURE_REFLECTIVITY
// Average up the texture pixels' color for an average reflectivity
for ( x = 0; x < ; x++ )
for ( y = 0; y < ; y++ )
{
samples++;
for(i=0; i < 3; i++)
sum[i] += mt[][x][y][i] // FIXME later
}
for(i=0; i < 3; i++)
reflectivity[i] = samples ? (BYTE)(sum[i] / samples) : 0;
#endif
}
/*
=============
IsSky
=============
*/
qboolean IsSky (dface_t *f)
{
texinfo_t *tx;
miptex_t *mt;
int ofs;
tx = &texinfo[f->texinfo];
ofs = ((dmiptexlump_t *)dtexdata)->dataofs[tx->miptex];
mt = (miptex_t *) ( (byte *)dtexdata + ofs);
if (!strncmp (mt->name, "sky", 3) )
return true;
if (!strncmp (mt->name, "SKY", 3) )
return true;
return false;
}
/*
=============
MakePatchForFace
=============
*/
float totalarea;
void MakePatchForFace (int fn, winding_t *w)
{
dface_t *f = dfaces + fn;
// No patches at all for the sky!
if ( !IsSky(f) )
{
float area;
patch_t *patch;
vec3_t light;
vec3_t centroid = {0,0,0};
int i, j;
texinfo_t *tx = &texinfo[f->texinfo];
area = WindingArea (w);
totalarea += area;
patch = &patches[num_patches];
if (num_patches == MAX_PATCHES)
Error ("num_patches == MAX_PATCHES");
patch->next = face_patches[fn];
face_patches[fn] = patch;
if ( texscale )
{
// Compute the texture "scale" in s,t
for( i=0; i<2; i++ )
{
patch->scale[i] = 0.0;
for( j=0; j<3; j++ )
patch->scale[i] += tx->vecs[i][j] * tx->vecs[i][j];
patch->scale[i] = sqrt( patch->scale[i] );
}
}
else
patch->scale[0] = patch->scale[1] = 1.0;
patch->area = area;
patch->chop = maxchop / (int)((patch->scale[0]+patch->scale[1])/2);
patch->sky = FALSE;
patch->winding = w;
if (f->side)
patch->plane = &backplanes[f->planenum];
else
patch->plane = &dplanes[f->planenum];
for (j=0 ; j<f->numedges ; j++)
{
int edge = dsurfedges[ f->firstedge + j ];
int edge2 = dsurfedges[ j==f->numedges-1 ? f->firstedge : f->firstedge + j + 1 ];
if (edge > 0)
{
VectorAdd( dvertexes[dedges[edge].v[0]].point, centroid, centroid );
VectorAdd( dvertexes[dedges[edge].v[1]].point, centroid, centroid );
}
else
{
VectorAdd( dvertexes[dedges[-edge].v[1]].point, centroid, centroid );
VectorAdd( dvertexes[dedges[-edge].v[0]].point, centroid, centroid );
}
}
VectorScale( centroid, 1.0 / (f->numedges * 2), centroid );
VectorCopy( centroid, face_centroids[fn] ); // Save them for generating the patch normals later.
patch->faceNumber = fn;
WindingCenter (w, patch->origin);
#ifdef PHONG_NORMAL_PATCHES
// This seems to be a bad idea for some reason. Leave it turned off for now.
VectorAdd (patch->origin, patch->plane->normal, patch->origin);
GetPhongNormal( fn, patch->origin, patch->normal );
VectorSubtract (patch->origin, patch->plane->normal, patch->origin);
if ( !VectorCompare( patch->plane->normal, patch->normal ) )
patch->chop = 16; // Chop it fine!
#else
VectorCopy( patch->plane->normal, patch->normal );
#endif
VectorAdd (patch->origin, patch->normal, patch->origin);
WindingBounds (w, patch->face_mins, patch->face_maxs);
VectorCopy( patch->face_mins, patch->mins );
VectorCopy( patch->face_maxs, patch->maxs );
BaseLightForFace( f, light, patch->reflectivity );
VectorCopy( light, patch->totallight );
VectorCopy( light, patch->baselight );
// Chop all texlights very fine.
if ( !VectorCompare( light, vec3_origin ) )
patch->chop = extra ? minchop / 2 : minchop;
num_patches++;
}
}
entity_t *EntityForModel (int modnum)
{
int i;
char *s;
char name[16];
sprintf (name, "*%i", modnum);
// search the entities for one using modnum
for (i=0 ; i<num_entities ; i++)
{
s = ValueForKey (&entities[i], "model");
if (!strcmp (s, name))
return &entities[i];
}
return &entities[0];
}
/*
=============
MakePatches
=============
*/
void MakePatches (void)
{
int i, j, k;
dface_t *f;
int fn;
winding_t *w;
dmodel_t *mod;
vec3_t origin;
entity_t *ent;
char *s;
ParseEntities ();
qprintf ("%i faces\n", numfaces);
for (i=0 ; i<nummodels ; i++)
{
mod = dmodels+i;
ent = EntityForModel (i);
VectorCopy (vec3_origin, origin);
// bmodels with origin brushes need to be offset into their
// in-use position
if ( *(s = ValueForKey(ent,"origin")) )
{
double v1, v2, v3;
if ( sscanf (s, "%lf %lf %lf", &v1, &v2, &v3) == 3 )
{
origin[0] = v1;
origin[1] = v2;
origin[2] = v3;
}
}
for (j=0 ; j<mod->numfaces ; j++)
{
fn = mod->firstface + j;
face_entity[fn] = ent;
VectorCopy (origin, face_offset[fn]);
f = dfaces+fn;
w = WindingFromFace (f);
for (k=0 ; k<w->numpoints ; k++)
{
VectorAdd (w->p[k], origin, w->p[k]);
}
MakePatchForFace (fn, w);
}
}
qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea );
}
/*
=======================================================================
SUBDIVIDE
=======================================================================
*/
/*
=============
SubdividePatch
=============
*/
void SubdividePatch (patch_t *patch)
{
winding_t *w, *o1, *o2;
vec3_t total;
vec3_t split;
vec_t dist;
vec_t widest = -1;
int i, j, widest_axis = -1;
int subdivide_it = 0;
vec_t v;
patch_t *newp;
w = patch->winding;
VectorSubtract (patch->maxs, patch->mins, total);
for (i=0 ; i<3 ; i++)
{
if ( total[i] > widest )
{
widest_axis = i;
widest = total[i];
}
if ( total[i] > patch->chop
|| (patch->face_maxs[i] == patch->maxs[i] || patch->face_mins[i] == patch->mins[i] )
&& total[i] > minchop )
{
subdivide_it = 1;
}
}
if ( subdivide_it )
{
//
// split the winding
//
VectorCopy (vec3_origin, split);
split[widest_axis] = 1;
dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f;
ClipWinding (w, split, dist, &o1, &o2);
//
// create a new patch
//
if (num_patches == MAX_PATCHES)
Error ("MAX_PATCHES");
newp = &patches[num_patches];
newp->next = patch->next;
patch->next = newp;
patch->winding = o1;
newp->winding = o2;
VectorCopy( patch->face_mins, newp->face_mins );
VectorCopy( patch->face_maxs, newp->face_maxs );
VectorCopy( patch->baselight, newp->baselight );
VectorCopy( patch->directlight, newp->directlight );
VectorCopy( patch->totallight, newp->totallight );
VectorCopy( patch->reflectivity, newp->reflectivity );
newp->plane = patch->plane;
newp->sky = patch->sky;
newp->chop = patch->chop;
newp->faceNumber = patch->faceNumber;
num_patches++;
patch->area = WindingArea (patch->winding);
newp->area = WindingArea (newp->winding);
WindingCenter (patch->winding, patch->origin);
WindingCenter (newp->winding, newp->origin);
#ifdef PHONG_NORMAL_PATCHES
// This seems to be a bad idea for some reason. Leave it turned off for now.
// Set (Copy or Calculate) the synthetic normal for these new patches
VectorAdd (patch->origin, patch->plane->normal, patch->origin);
VectorAdd (newp->origin, newp->plane->normal, newp->origin);
GetPhongNormal( patch->faceNumber, patch->origin, patch->normal );
GetPhongNormal( newp->faceNumber, newp->origin, newp->normal );
VectorSubtract( patch->origin, patch->plane->normal, patch->origin);
VectorSubtract( newp->origin, newp->plane->normal, newp->origin);
#else
VectorCopy( patch->plane->normal, patch->normal );
VectorCopy( newp->plane->normal, newp->normal );
#endif
VectorAdd( patch->origin, patch->normal, patch->origin );
VectorAdd( newp->origin, newp->normal, newp->origin );
WindingBounds(patch->winding, patch->mins, patch->maxs);
WindingBounds(newp->winding, newp->mins, newp->maxs);
// Subdivide patch even more if on the edge of the face; this is a hack!
VectorSubtract (patch->maxs, patch->mins, total);
if ( total[0] < patch->chop && total[1] < patch->chop && total[2] < patch->chop )
for ( i=0; i<3; i++ )
if ( (patch->face_maxs[i] == patch->maxs[i] || patch->face_mins[i] == patch->mins[i] )
&& total[i] > minchop )
{
patch->chop = max( minchop, patch->chop / 2 );
break;
}
SubdividePatch (patch);
// Subdivide patch even more if on the edge of the face; this is a hack!
VectorSubtract (newp->maxs, newp->mins, total);
if ( total[0] < newp->chop && total[1] < newp->chop && total[2] < newp->chop )
for ( i=0; i<3; i++ )
if ( (newp->face_maxs[i] == newp->maxs[i] || newp->face_mins[i] == newp->mins[i] )
&& total[i] > minchop )
{
newp->chop = max( minchop, newp->chop / 2 );
break;
}
SubdividePatch (newp);
}
}
/*
=============
SubdividePatches
=============
*/
void SubdividePatches (void)
{
int i, num;
num = num_patches; // because the list will grow
for (i=0 ; i<num ; i++)
{
patch_t *patch = patches + i;
SubdividePatch( patch );
}
qprintf ("%i patches after subdivision\n", num_patches);
}
//=====================================================================
/*
=============
MakeScales
This is the primary time sink.
It can be run multi threaded.
=============
*/
int total_transfer;
void MakeScales (int threadnum)
{
int i;
unsigned j;
vec3_t delta;
vec_t dist, scale;
int count;
float trans;
patch_t *patch, *patch2;
float total, send;
dplane_t plane;
vec3_t origin;
vec_t area;
transfer_t transfers[MAX_PATCHES], *all_transfers;
count = 0;
while (1)
{
i = GetThreadWork ();
if (i == -1)
break;
patch = patches + i;
total = 0;
patch->numtransfers = 0;
VectorCopy (patch->origin, origin);
plane = *patch->plane;
plane.dist = PatchPlaneDist( patch );
area = patch->area;
// find out which patch2's will collect light
// from patch
all_transfers = transfers;
for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)
{
if (!CheckVisBit (i, j))
continue;
// calculate transferemnce
VectorSubtract (patch2->origin, origin, delta);
dist = VectorNormalize (delta);
// skys don't care about the interface angle, but everything
// else does
if (!patch->sky)
scale = DotProduct (delta, patch->normal);
else
scale = 1;
scale *= -DotProduct (delta, patch2->normal);
trans = scale / (dist*dist);
if (trans < -ON_EPSILON)
Error ("transfer < 0");
send = trans*patch2->area;
if (send > 0.4f)
{
trans = 0.4f / patch2->area;
send = 0.4f;
}
total += send;
// scale to 16 bit
trans = trans * area * INVERSE_TRANSFER_SCALE;
if (trans >= 0x10000)
trans = 0xffff;
if (!trans)
continue;
all_transfers->transfer = (unsigned short)trans;
all_transfers->patch = j;
all_transfers++;
patch->numtransfers++;
count++;
}
// copy the transfers out
if (patch->numtransfers)
{
transfer_t *t, *t2;
patch->transfers = calloc (patch->numtransfers, sizeof(transfer_t));
if (!patch->transfers)
Error ("Memory allocation failure");
//
// normalize all transfers so exactly 50% of the light
// is transfered to the surroundings
//
total = 0.5f/total;
t = patch->transfers;
t2 = transfers;
for (j=0 ; j<(unsigned)patch->numtransfers ; j++, t++, t2++)
{
t->transfer = (unsigned short)(t2->transfer*total);
t->patch = t2->patch;
}
}
}
ThreadLock ();
total_transfer += count;
ThreadUnlock ();
}
/*
=============
WriteWorld
=============
*/
void WriteWorld (char *name)
{
int i;
unsigned 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 ] / 256,
patch->totallight[ 1 ] / 256,
patch->totallight[ 2 ] / 256 );
}
fprintf (out, "\n");
}
fclose (out);
}
/*
=============
SwapTransfersTask
Change transfers from light sent out to light collected in.
In an ideal world, they would be exactly symetrical, but
because the form factors are only aproximated, then normalized,
they will actually be rather different.
=============
*/
void SwapTransfersTask (int patchnum)
{
int j, k, l, m, n, h;
patch_t *patch, *patch2;
transfer_t *t, *t2;
int transfer;
patch = patches + patchnum;
t = patch->transfers;
for (j=0 ; j<patch->numtransfers ; j++, t++)
{
k = t->patch;
if (k > patchnum)
break; // done with this list
patch2 = &patches[k];
t2 = patch2->transfers;
if (!patch2->numtransfers)
{
printf ("WARNING: SwapTransfers: unmatched\n");
continue;
}
//
// binary search for match
//
l = 0;
h = patch2->numtransfers-1;
while (1)
{
m = (l+h)>>1;
n = t2[m].patch;
if (n < patchnum)
{
l = m+1;
continue;
}
if (n > patchnum)
{
h = m-1;
continue;
}
t2 += m;
transfer = t2->transfer;
t2->transfer = t->transfer;
t->transfer = transfer;
break;
}
#if 0
for (l=0 ; l<patch2->numtransfers ; l++, t2++)
{
if (t2->patch == i)
{
transfer = t2->transfer;
t2->transfer = t->transfer;
t->transfer = transfer;
break;
}
}
#endif
if (l == patch2->numtransfers)
Error ("Didn't match transfer");
}
}
/*
=============
CollectLight
=============
*/
void CollectLight( vec3_t total )
{
unsigned i;
patch_t *patch;
VectorFill( total, 0 );
for (i=0, patch=patches ; i<num_patches ; i++, patch++)
{
// sky's never collect light, it is just dropped
if (patch->sky)
{
VectorFill( emitlight[ i ], 0 );
VectorFill( addlight[ i ], 0 );
continue;
}
VectorAdd( patch->totallight, addlight[i], patch->totallight );
VectorScale( addlight[i], TRANSFER_SCALE, emitlight[i] );
VectorAdd( total, emitlight[i], total );
VectorFill( addlight[ i ], 0 );
}
VectorScale( total, INVERSE_TRANSFER_SCALE, total );
}
/*
=============
GatherLight
Get light from other patches
Run multi-threaded
=============
*/
void GatherLight (int threadnum)
{
int j, k;
transfer_t *trans;
int num;
patch_t *patch;
vec3_t sum, v;
while (1)
{
j = GetThreadWork ();
if (j == -1)
break;
patch = &patches[j];
trans = patch->transfers;
num = patch->numtransfers;
VectorFill( sum, 0 )
for (k=0 ; k<num ; k++, trans++)
{
VectorScale( emitlight[trans->patch], trans->transfer, v );
VectorAdd( sum, v, sum );
}
VectorCopy( sum, addlight[j] );
}
}
/*
=============
BounceLight
=============
*/
void BounceLight (void)
{
unsigned i;
vec3_t added;
char name[64];
for (i=0 ; i<num_patches ; i++)
VectorScale( patches[i].totallight, TRANSFER_SCALE, emitlight[i] );
for (i=0 ; i<numbounce ; i++)
{
RunThreadsOn (num_patches, true, GatherLight);
CollectLight( added );
qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] );
if ( dumppatches && (i==0 || i == (unsigned)numbounce-1) )
{
sprintf (name, "bounce%i.txt", i);
WriteWorld (name);
}
}
}
/*
=============
writetransfers
=============
*/
long
writetransfers(char *transferfile, long total_patches)
{
int handle;
long writtenpatches = 0, writtentransfers = 0, totalbytes = 0;
int spacerequired = sizeof(long) + total_patches * sizeof(long) + total_transfer * sizeof(transfer_t);
if ( spacerequired - getfilesize(transferfile) < getfreespace(transferfile) )
{
if ( (handle = _open( transferfile, _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE )) != -1 )
{
unsigned byteswritten;
qprintf("Writing [%s] with new saved qrad data", transferfile );
if ( (byteswritten = _write(handle, &total_patches, sizeof(total_patches))) == sizeof(total_patches) )
{
patch_t *patch;
totalbytes += byteswritten;
for( patch = patches; total_patches-- > 0; patch++ )
{
if ( (byteswritten = _write(handle, &patch->numtransfers, sizeof(patch->numtransfers)))
== sizeof(patch->numtransfers) )
{
totalbytes += byteswritten;
if ( patch->numtransfers &&
(byteswritten = _write(handle, patch->transfers, patch->numtransfers*sizeof(transfer_t)))
== patch->numtransfers*sizeof(transfer_t) )
{
totalbytes += byteswritten;
writtentransfers += patch->numtransfers;
}
writtenpatches++;
}
else
{
break;
}
}
}
qprintf("(%d)\n", totalbytes );
_close( handle );
}
}
else
printf("Insufficient disk space(%ld) for 'QRAD save file'[%s]!\n",
spacerequired - getfilesize(transferfile), transferfile );
return writtenpatches;
}
/*
=============
readtransfers
=============
*/
long
readtransfers(char *transferfile, long numpatches)
{
int handle;
long readpatches = 0, readtransfers = 0, totalbytes = 0;
long start, end;
time(&start);
if ( (handle = _open( transferfile, _O_RDONLY | _O_BINARY )) != -1 )
{
long filepatches;
unsigned long bytesread;
printf("%-20s Restoring [%-13s - ", "MakeAllScales:", transferfile );
if ( (bytesread = _read(handle, &filepatches, sizeof(filepatches))) == sizeof(filepatches) )
{
if ( filepatches == numpatches )
{
patch_t *patch;
totalbytes += bytesread;
for( patch = patches; readpatches < numpatches; patch++ )
{
if ( (bytesread = _read(handle, &patch->numtransfers, sizeof(patch->numtransfers)))
== sizeof(patch->numtransfers) )
{
if ( patch->transfers = calloc(patch->numtransfers, sizeof(patch->transfers[0])) )
{
totalbytes += bytesread;
if ( patch->numtransfers )
{
if ( (bytesread = _read(handle, patch->transfers, patch->numtransfers*sizeof(transfer_t)))
== patch->numtransfers*sizeof(transfer_t) )
{
totalbytes += bytesread;
readtransfers += patch->numtransfers;
}
else
{
printf("\nMissing transfer count! Save file will now be rebuilt." );
break;
}
}
readpatches++;
}
else
{
printf("\nMemory allocation failure creating transfer lists(%d*%d)!\n",
patch->numtransfers, sizeof(transfer_t) );
break;
}
}
else
{
printf("\nMissing patch count! Save file will now be rebuilt." );
break;
}
}
}
else
printf("\nIncorrect transfer patch count found! Save file will now be rebuilt." );
}
_close( handle );
time(&end);
printf("%10.3fMB] (%d)\n",totalbytes/(1024.0*1024.0), end-start);
}
if (readpatches != numpatches )
unlink(transferfile);
else
total_transfer = readtransfers;
return readpatches;
}
//==============================================================
void MakeAllScales (void)
{
strcpy(transferfile, source);
StripExtension( transferfile );
DefaultExtension( transferfile, ".r2" );
if ( !incremental
|| !IsIncremental(incrementfile)
|| (unsigned)readtransfers(transferfile, num_patches) != num_patches )
{
// determine visibility between patches
BuildVisMatrix ();
RunThreadsOn (num_patches, true, MakeScales);
if ( incremental )
writetransfers(transferfile, num_patches);
else
unlink(transferfile);
// release visibility matrix
FreeVisMatrix ();
}
qprintf ("transfer lists: %5.1f megs\n"
, (float)total_transfer * sizeof(transfer_t) / (1024*1024));
}
/*
=============
RadWorld
=============
*/
void RadWorld (void)
{
int i;
MakeBackplanes ();
MakeParents (0, -1);
MakeTnodes (&dmodels[0]);
// turn each face into a single patch
MakePatches ();
PairEdges ();
// subdivide patches to a maximum dimension
SubdividePatches ();
do
{
// create directlights out of patches and lights
CreateDirectLights ();
// build initial facelights
RunThreadsOnIndividual (numfaces, true, BuildFacelights);
// free up the direct lights now that we have facelights
DeleteDirectLights ();
}
while( numbounce != 0 && ProgressiveRefinement() );
if (numbounce > 0)
{
// build transfer lists
MakeAllScales ();
// invert the transfers for gather vs scatter
RunThreadsOnIndividual (num_patches, true, SwapTransfersTask);
// spread light around
BounceLight ();
for( i=0; i < num_patches; i++ )
if ( !VectorCompare( patches[i].directlight, vec3_origin ) )
VectorSubtract( patches[i].totallight, patches[i].directlight, patches[i].totallight );
}
// blend bounced light into direct light and save
PrecompLightmapOffsets();
RunThreadsOnIndividual (numfaces, true, FinalLightFace);
}
/*
========
main
light modelfile
========
*/
extern char qproject[];
int main (int argc, char **argv)
{
int i;
double start, end;
printf( "qrad.exe v 1.5 (%s)\n", __DATE__ );
printf ("----- Radiosity ----\n");
verbose = true; // Originally FALSE
smoothing_threshold = cos(45.0*(Q_PI/180)); // Originally zero.
for (i=1 ; i<argc ; i++)
{
if (!strcmp(argv[i],"-dump"))
dumppatches = true;
else if (!strcmp(argv[i],"-bounce"))
{
if ( ++i < argc )
{
numbounce = atoi (argv[i]);
if ( numbounce < 0 )
{
fprintf(stderr, "Error: expected non-negative value after '-bounce'\n" );
return 1;
}
}
else
{
fprintf( stderr, "Error: expected a value after '-bounce'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-verbose"))
{
verbose = true;
}
else if (!strcmp(argv[i],"-terse"))
{
verbose = false;
}
else if (!strcmp(argv[i],"-threads"))
{
if ( ++i < argc )
{
numthreads = atoi (argv[i]);
if ( numthreads <= 0 )
{
fprintf(stderr, "Error: expected positive value after '-threads'\n" );
return 1;
}
}
else
{
fprintf( stderr, "Error: expected a value after '-threads'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-maxchop"))
{
if ( ++i < argc )
{
maxchop = (float)atof (argv[i]);
if ( maxchop < 2 )
{
fprintf(stderr, "Error: expected positive value after '-maxchop'\n" );
return 1;
}
}
else
{
fprintf( stderr, "Error: expected a value after '-maxchop'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-chop"))
{
if ( ++i < argc )
{
minchop = (float)atof (argv[i]);
if ( minchop < 1 )
{
fprintf(stderr, "Error: expected positive value after '-chop'\n" );
return 1;
}
if ( minchop < 32 )
{
fprintf(stderr, "WARNING: Chop values below 32 are not recommended. Use -extra instead.\n");
}
}
else
{
fprintf( stderr, "Error: expected a value after '-chop'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-scale"))
{
if ( ++i < argc )
{
lightscale = (float)atof (argv[i]);
}
else
{
fprintf( stderr, "Error: expected a value after '-scale'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-ambient"))
{
if ( i+3 < argc )
{
ambient[0] = (float)atof (argv[++i]) * 128;
ambient[1] = (float)atof (argv[++i]) * 128;
ambient[2] = (float)atof (argv[++i]) * 128;
}
else
{
fprintf( stderr, "Error: expected three color values after '-ambient'\n" );
return 1;
}
}
else if( !strcmp(argv[i], "-proj") )
{
if ( ++i < argc && *argv[i] )
strcpy( qproject, argv[i] );
else
{
fprintf(stderr, "Error: expected path name after '-proj'\n" );
return 1;
}
}
else if ( !strcmp(argv[i], "-maxlight") )
{
if ( ++i < argc && *argv[i] )
{
maxlight = (float)atof (argv[i]) * 128;
if ( maxlight <= 0 )
{
fprintf(stderr, "Error: expected positive value after '-maxlight'\n" );
return 1;
}
}
else
{
fprintf( stderr, "Error: expected a value after '-maxlight'\n" );
return 1;
}
}
else if ( !strcmp(argv[i], "-lights" ) )
{
if ( ++i < argc && *argv[i] )
{
strcpy( designer_lights, argv[i] );
}
else
{
fprintf( stderr, "Error: expected a filepath after '-lights'\n" );
return 1;
}
}
else if ( !strcmp(argv[i], "-inc" ) )
{
incremental = true;
}
else if (!strcmp(argv[i],"-gamma"))
{
if ( ++i < argc )
{
gamma = (float)atof (argv[i]);
}
else
{
fprintf( stderr, "Error: expected a value after '-gamma'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-dlight"))
{
if ( ++i < argc )
{
dlight_threshold = (float)atof (argv[i]);
}
else
{
fprintf( stderr, "Error: expected a value after '-dlight'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-extra"))
{
extra = true;
}
else if (!strcmp(argv[i],"-sky"))
{
if ( ++i < argc )
{
indirect_sun = (float)atof (argv[i]);
}
else
{
fprintf( stderr, "Error: expected a value after '-gamma'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-smooth"))
{
if ( ++i < argc )
{
smoothing_threshold = (float)cos(atof(argv[i])*(Q_PI/180.0));
}
else
{
fprintf( stderr, "Error: expected an angle after '-smooth'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-coring"))
{
if ( ++i < argc )
{
coring = (float)atof( argv[i] );
}
else
{
fprintf( stderr, "Error: expected a light threshold after '-coring'\n" );
return 1;
}
}
else if (!strcmp(argv[i],"-notexscale"))
{
texscale = false;
}
else
{
break;
}
}
ThreadSetDefault ();
if (maxlight > 255)
maxlight = 255;
if (i != argc - 1)
Error ("usage: qrad [-dump] [-inc] [-bounce n] [-threads n] [-verbose] [-terse] [-chop n] [-maxchop n] [-scale n] [-ambient red green blue] [-proj file] [-maxlight n] [-threads n] [-lights file] [-gamma n] [-dlight n] [-extra] [-smooth n] [-coring n] [-notexscale] bspfile");
start = I_FloatTime ();
strcpy (source, argv[i]);
StripExtension (source);
SetQdirFromPath (source);
// Set the required global lights filename
strcat( strcpy( global_lights, gamedir ), "lights.rad" );
if ( _access( global_lights, 0x04) == -1 )
{
// try looking in qproject
strcat( strcpy( global_lights, qproject ), "lights.rad" );
if ( _access( global_lights, 0x04) == -1 )
{
// try looking in the directory we were run from
GetModuleFileName( NULL, global_lights, sizeof( global_lights ) );
ExtractFilePath( global_lights, global_lights );
strcat( global_lights, "lights.rad" );
}
}
// Set the optional level specific lights filename
DefaultExtension( strcpy( level_lights, source ), ".rad" );
if ( _access( level_lights, 0x04) == -1 ) *level_lights = 0;
ReadLightFile(global_lights); // Required
if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line
if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied
strcpy(incrementfile, source);
DefaultExtension(incrementfile, ".r0");
DefaultExtension(source, ".bsp");
LoadBSPFile (source);
ParseEntities ();
if (!visdatasize)
{
printf ("No vis information, direct lighting only.\n");
numbounce = 0;
ambient[0] = ambient[1] = ambient[2] = 0.1f;
}
RadWorld ();
if (verbose)
PrintBSPFileSizes ();
WriteBSPFile (source);
if ( incremental )
{
if ( !IsIncremental(incrementfile) )
{
SaveIncremental(incrementfile);
}
}
else
{
unlink(incrementfile);
}
end = I_FloatTime ();
printf ("%5.0f seconds elapsed\n", end-start);
return 0;
}