halflife-sdk/utils/qrad/vismat.c
1999-04-07 00:00:00 +00:00

569 lines
13 KiB
C

/***
*
* Copyright (c) 1998, 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.
*
****/
#include "qrad.h"
#define HALFBIT
extern char source[MAX_PATH];
extern char vismatfile[_MAX_PATH];
extern char incrementfile[_MAX_PATH];
extern qboolean incremental;
/*
===================================================================
VISIBILITY MATRIX
Determine which patches can see each other
Use the PVS to accelerate if available
===================================================================
*/
byte *vismatrix;
dleaf_t *PointInLeaf (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 &dleafs[-nodenum - 1];
}
void PvsForOrigin (vec3_t org, byte *pvs)
{
dleaf_t *leaf;
if (!visdatasize)
{
memset (pvs, 255, (numleafs+7)/8 );
return;
}
leaf = PointInLeaf (org);
if (leaf->visofs == -1)
Error ("leaf->visofs == -1");
DecompressVis (&dvisdata[leaf->visofs], pvs);
}
/*
==============
PatchPlaneDist
Fixes up patch planes for brush models with an origin brush
==============
*/
vec_t PatchPlaneDist( patch_t *patch )
{
return patch->plane->dist + DotProduct( face_offset[ patch->faceNumber ], patch->normal );
}
/*
==============
TestPatchToFace
Sets vis bits for all patches in the face
==============
*/
void TestPatchToFace (unsigned patchnum, int facenum, int head, unsigned bitpos)
{
patch_t *patch = &patches[patchnum];
patch_t *patch2 = face_patches[facenum];
// if emitter is behind that face plane, skip all patches
if ( patch2 && DotProduct(patch->origin, patch2->normal) > PatchPlaneDist(patch2)+1.01 )
{
// we need to do a real test
for ( ; patch2 ; patch2 = patch2->next)
{
unsigned m = patch2 - patches;
// check vis between patch and patch2
// if bit has not already been set
// && v2 is not behind light plane
// && v2 is visible from v1
if ( m > patchnum
&& DotProduct (patch2->origin, patch->normal) > PatchPlaneDist(patch)+1.01
&& TestLine_r (head, patch->origin, patch2->origin) == CONTENTS_EMPTY )
{
// patchnum can see patch m
int bitset = bitpos+m;
vismatrix[ bitset>>3 ] |= 1 << (bitset&7);
}
}
}
}
/*
==============
BuildVisRow
Calc vis bits from a single patch
==============
*/
void BuildVisRow (int patchnum, byte *pvs, int head, unsigned bitpos)
{
int j, k, l;
patch_t *patch;
byte face_tested[MAX_MAP_FACES];
dleaf_t *leaf;
patch = &patches[patchnum];
memset (face_tested, 0, numfaces);
// leaf 0 is the solid leaf (skipped)
for (j=1, leaf=dleafs+1 ; j<numleafs ; j++, leaf++)
{
if ( ! ( pvs[(j-1)>>3] & (1<<((j-1)&7)) ) )
continue; // not in pvs
for (k=0 ; k<leaf->nummarksurfaces ; k++)
{
l = dmarksurfaces[leaf->firstmarksurface + k];
// faces can be marksurfed by multiple leaves, but
// don't bother testing again
if (face_tested[l])
continue;
face_tested[l] = 1;
TestPatchToFace (patchnum, l, head, bitpos);
}
}
}
/*
===========
BuildVisLeafs
This is run by multiple threads
===========
*/
void BuildVisLeafs (int threadnum)
{
int i;
int lface, facenum, facenum2;
byte pvs[(MAX_MAP_LEAFS+7)/8];
dleaf_t *srcleaf, *leaf;
patch_t *patch;
int head;
unsigned bitpos;
unsigned patchnum;
while (1)
{
//
// build a minimal BSP tree that only
// covers areas relevent to the PVS
//
i = GetThreadWork ();
if (i == -1)
break;
i++; // skip leaf 0
srcleaf = &dleafs[i];
DecompressVis (&dvisdata[srcleaf->visofs], pvs);
#if 0
// is this valid multithreaded???
memset (nodehit, 0, numnodes);
for (j=1, leaf=dleafs+1 ; j<numleafs ; j++, leaf++)
{
if ( !( pvs[(j-1)>>3] & (1<<((j-1)&7)) ) )
continue;
n = leafparents[j];
while (n != -1)
{
nodehit[n] = 1;
n = nodeparents[n];
}
}
head = PartialHead ();
#else
head = 0;
#endif
//
// go through all the faces inside the
// leaf, and process the patches that
// actually have origins inside
//
for (lface = 0 ; lface < srcleaf->nummarksurfaces ; lface++)
{
facenum = dmarksurfaces[srcleaf->firstmarksurface + lface];
for (patch = face_patches[facenum] ; patch ; patch=patch->next)
{
leaf = PointInLeaf (patch->origin);
if (leaf != srcleaf)
continue;
patchnum = patch - patches;
#ifdef HALFBIT
bitpos = patchnum * num_patches - (patchnum*(patchnum+1))/2;
#else
bitpos = patchnum * num_patches;
#endif
// build to all other world leafs
BuildVisRow (patchnum, pvs, head, bitpos);
// build to bmodel faces
if (nummodels < 2)
continue;
for (facenum2 = dmodels[1].firstface ; facenum2 < numfaces ; facenum2++)
TestPatchToFace (patchnum, facenum2, head, bitpos);
}
}
}
}
/*
==============
getfiletime
==============
*/
time_t
getfiletime(char *filename)
{
time_t filetime = 0;
struct _stat filestat;
if ( _stat(filename, &filestat) == 0 )
filetime = max( filestat.st_mtime, filestat.st_ctime );
return filetime;
}
/*
==============
getfilesize
==============
*/
long
getfilesize(char *filename)
{
long size = 0;
struct _stat filestat;
if ( _stat(filename, &filestat) == 0 )
size = filestat.st_size;
return size;
}
/*
==============
getfiledata
==============
*/
long
getfiledata(char *filename, char *buffer, int buffersize)
{
long size = 0;
int handle;
long start,end;
time(&start);
if ( (handle = _open( filename, _O_RDONLY | _O_BINARY )) != -1 )
{
int bytesread;
printf("%-20s Restoring [%-13s - ", "BuildVisMatrix:", filename );
while( ( bytesread = _read( handle, buffer, min( 32*1024, buffersize - size ) ) ) > 0 )
{
size += bytesread;
buffer += bytesread;
}
_close( handle );
time(&end);
printf("%10.3fMB] (%d)\n",size/(1024.0*1024.0), end-start);
}
if (buffersize != size)
{
printf( "Invalid file [%s] found. File will be rebuilt!\n", filename );
unlink(filename);
}
return size;
}
/*
==============
getfreespace
==============
*/
_int64
getfreespace(char *filename)
{
_int64 freespace = 0;
int drive = 0;
struct _diskfree_t df;
if ( filename[0] && filename[1] == ':' )
drive = toupper(filename[0]) - 'A' + 1;
else
drive = _getdrive();
if ( _getdiskfree(drive, &df) == 0 )
{
freespace = df.avail_clusters;
freespace *= df.sectors_per_cluster;
freespace *= df.bytes_per_sector;
}
return freespace;
}
/*
==============
putfiledata
==============
*/
long
putfiledata(char *filename, char *buffer, int buffersize)
{
long size = 0;
int handle;
if ( getfreespace(filename) >= (_int64)(buffersize - getfilesize(filename)) )
{
if ( (handle = _open( filename, _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE )) != -1 )
{
int byteswritten;
qprintf("Writing [%s] with new saved qrad data", filename );
while( ( byteswritten = _write( handle, buffer, min( 32*1024, buffersize - size ) ) ) > 0 )
{
size += byteswritten;
buffer += byteswritten;
if ( size >= buffersize )
break;
}
qprintf("(%d)\n", size );
_close( handle );
}
}
else
printf("Insufficient disk space(%ld) for 'incremental QRAD save file'!\n",
buffersize - getfilesize(filename) );
return size;
}
/*
==============
IsIncremental
==============
*/
qboolean
IsIncremental(char *filename)
{
qboolean status = false;
int sum;
int handle;
if ( (handle = _open( filename, _O_RDONLY | _O_BINARY )) != -1 )
{
if ( _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dmodels_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dvertexes_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dplanes_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dleafs_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dnodes_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == texinfo_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dclipnodes_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dfaces_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dmarksurfaces_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dsurfedges_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dedges_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dtexdata_checksum
&& _read( handle, &sum, sizeof(sum) ) == sizeof(sum) && sum == dvisdata_checksum )
status = true;
_close( handle );
}
return status;
}
/*
==============
SaveIncremental
==============
*/
int
SaveIncremental(char *filename)
{
long size = 0;
int handle;
int expected_size = 13*sizeof(int);
if ( getfreespace(filename) >= expected_size )
{
if ( (handle = _open( filename, _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC, _S_IREAD | _S_IWRITE )) != -1 )
{
qprintf("Writing [%s] with new saved qrad data", filename );
if ( _write( handle, &dmodels_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dvertexes_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dplanes_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dleafs_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dnodes_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &texinfo_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dclipnodes_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dfaces_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dmarksurfaces_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dsurfedges_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dedges_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dtexdata_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int))
&& _write( handle, &dvisdata_checksum, sizeof(int) ) == sizeof(int) && (size += sizeof(int)) )
{
qprintf("(%d)\n", size );
}
else
{
qprintf("...failed!");
}
_close( handle );
}
}
else
printf("Insufficient disk space(%ld) for incremental file[%s]'!\n",
expected_size, filename );
return size;
}
/*
==============
BuildVisMatrix
==============
*/
void BuildVisMatrix (void)
{
int c;
HANDLE h;
#ifdef HALFBIT
c = ((num_patches+1)*(((num_patches+1)+15)/16));
#else
c = num_patches*((num_patches+7)/8);
#endif
qprintf ("visibility matrix: %5.1f megs\n", c/(1024*1024.0));
if ( h = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, c ) )
vismatrix = GlobalLock( h );
else
Error ("vismatrix too big");
strcpy(vismatfile, source);
StripExtension (vismatfile);
DefaultExtension(vismatfile, ".r1");
if ( !incremental
|| !IsIncremental(incrementfile)
|| getfilesize(vismatfile) != c
|| getfiledata(vismatfile,vismatrix, c) != c )
{
// memset (vismatrix, 0, c);
RunThreadsOn (numleafs-1, true, BuildVisLeafs);
}
// Get rid of any old _bogus_ r1 files; we never read them!
unlink(vismatfile);
}
void FreeVisMatrix (void)
{
if ( vismatrix )
{
HANDLE h = GlobalHandle(vismatrix);
GlobalUnlock(h);
GlobalFree(h);
vismatrix = NULL;
}
}
/*
==============
touchfile
=============
*/
void
TouchFile(char *filename)
{
int handle;
if ( (handle = _open( filename, _O_RDWR | _O_BINARY )) != -1 )
{
char bytebuffer;
qprintf("Updating saved qrad data <%s> with current time.\n", filename);
_read( handle, &bytebuffer, sizeof(bytebuffer));
_lseek(handle,0,SEEK_SET);
_write( handle, &bytebuffer, sizeof(bytebuffer));
_close( handle );
}
}
/*
==============
CheckVisBit
==============
*/
qboolean CheckVisBit (int p1, int p2)
{
int t;
int bitpos;
if (p1 > p2)
{
t = p1;
p1 = p2;
p2 = t;
}
#ifdef HALFBIT
bitpos = p1 * num_patches - (p1*(p1+1))/2 + p2;
#else
bitpos = p1 * num_patches + p2;
#endif
if (vismatrix[bitpos>>3] & (1<<(bitpos&7)))
return true;
return false;
}