934 lines
22 KiB
C
934 lines
22 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// visibility.c - map visibility
|
|
//=======================================================================
|
|
|
|
#include "bsplib.h"
|
|
|
|
#define WCONVEX_EPSILON 0.2
|
|
#define CONTINUOUS_EPSILON 0.005
|
|
|
|
int numportals;
|
|
int portalclusters;
|
|
vportal_t *portals;
|
|
leaf_t *leafs;
|
|
vportal_t *faces;
|
|
leaf_t *faceleafs;
|
|
int c_portaltest, c_portalpass, c_portalcheck;
|
|
int leafbytes; // (portalclusters+63)>>3
|
|
int leaflongs;
|
|
int portalbytes;
|
|
int portallongs;
|
|
int numvisfaces;
|
|
|
|
bool fastvis; // fast calc visibility
|
|
bool noPassageVis; // ignore passages
|
|
bool passageVisOnly; // visibility from passages only
|
|
bool mergevis; // merge visibility
|
|
bool nosort; // no sorting portals
|
|
bool saveprt; // don't remove prt file
|
|
|
|
byte *vismap, *vismap_p, *vismap_end; // past visfile
|
|
int originalvismapsize;
|
|
byte *uncompressedvis;
|
|
|
|
int totalpvs;
|
|
int totalphs;
|
|
|
|
vportal_t *sorted_portals[MAX_MAP_PORTALS*2];
|
|
void PassageMemory( void );
|
|
|
|
//=============================================================================
|
|
void PlaneFromWinding( winding_t *w, vplane_t *plane )
|
|
{
|
|
vec3_t v1, v2;
|
|
|
|
// calc plane
|
|
VectorSubtract( w->p[2], w->p[1], v1 );
|
|
VectorSubtract( w->p[0], w->p[1], v2 );
|
|
CrossProduct( v2, v1, plane->normal );
|
|
VectorNormalize( plane->normal );
|
|
plane->dist = DotProduct( w->p[0], plane->normal );
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
NewWinding
|
|
==================
|
|
*/
|
|
winding_t *NewWinding( int points )
|
|
{
|
|
int size;
|
|
|
|
if( points > MAX_POINTS_ON_WINDING )
|
|
Sys_Error( "NewWinding: %i points\n", points );
|
|
|
|
size = (int)((winding_t *)0)->p[points];
|
|
return BSP_Malloc( size );
|
|
}
|
|
|
|
static void prl( leaf_t *l )
|
|
{
|
|
int i;
|
|
vportal_t *p;
|
|
vplane_t pl;
|
|
|
|
for( i = 0; i < l->numportals; i++ )
|
|
{
|
|
p = l->portals[i];
|
|
pl = p->plane;
|
|
Msg ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
|
|
}
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=============
|
|
SortPortals
|
|
|
|
Sorts the portals from the least complex, so the later ones can reuse
|
|
the earlier information.
|
|
=============
|
|
*/
|
|
int PComp( const void *a, const void *b )
|
|
{
|
|
if((*(vportal_t **)a)->nummightsee == (*(vportal_t **)b)->nummightsee)
|
|
return 0;
|
|
if((*(vportal_t **)a)->nummightsee < (*(vportal_t **)b)->nummightsee)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
void SortPortals( void )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < numportals * 2; i++ )
|
|
sorted_portals[i] = &portals[i];
|
|
|
|
if( nosort ) return;
|
|
qsort( sorted_portals, numportals * 2, sizeof(sorted_portals[0]), PComp );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
LeafVectorFromPortalVector
|
|
==============
|
|
*/
|
|
int LeafVectorFromPortalVector( byte *portalbits, byte *leafbits )
|
|
{
|
|
int i, j, leafnum;
|
|
vportal_t *p;
|
|
|
|
for( i = 0; i < numportals * 2; i++ )
|
|
{
|
|
if( portalbits[i>>3] & (1<<( i & 7 )))
|
|
{
|
|
p = portals + i;
|
|
leafbits[p->leaf>>3] |= (1<<( p->leaf & 7 ));
|
|
}
|
|
}
|
|
|
|
for( j = 0; j < portalclusters; j++ )
|
|
{
|
|
leafnum = j;
|
|
while( leafs[leafnum].merged >= 0 )
|
|
leafnum = leafs[leafnum].merged;
|
|
// if the merged leaf is visible then the original leaf is visible
|
|
if( leafbits[leafnum>>3] & (1<<( leafnum & 7 )))
|
|
{
|
|
leafbits[j>>3] |= (1<<( j & 7 ));
|
|
}
|
|
}
|
|
return CountBits( leafbits, portalclusters );
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
ClusterPVS
|
|
|
|
Merges the portal visibility for a leaf
|
|
===============
|
|
*/
|
|
void ClusterPVS( int leafnum )
|
|
{
|
|
leaf_t *leaf;
|
|
byte portalvector[MAX_PORTALS/8];
|
|
byte uncompressed[MAX_MAP_LEAFS/8];
|
|
byte compressed[MAX_MAP_LEAFS/8];
|
|
int i, j;
|
|
int numvis, mergedleafnum;
|
|
byte *dest;
|
|
vportal_t *p;
|
|
int pnum;
|
|
|
|
// OR together all the portalvis bits
|
|
|
|
mergedleafnum = leafnum;
|
|
while( leafs[mergedleafnum].merged >= 0 )
|
|
mergedleafnum = leafs[mergedleafnum].merged;
|
|
|
|
memset( portalvector, 0, portalbytes );
|
|
leaf = &leafs[mergedleafnum];
|
|
for( i = 0; i < leaf->numportals; i++ )
|
|
{
|
|
p = leaf->portals[i];
|
|
if( p->removed ) continue;
|
|
|
|
if( p->status != stat_done ) Sys_Error( "portal not done\n" );
|
|
for( j = 0; j < portallongs; j++ )
|
|
((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
|
|
pnum = p - portals;
|
|
portalvector[pnum>>3] |= 1<<( pnum & 7 );
|
|
}
|
|
|
|
memset( uncompressed, 0, leafbytes );
|
|
uncompressed[mergedleafnum>>3] |= (1<<( mergedleafnum & 7 ));
|
|
|
|
// convert portal bits to leaf bits
|
|
numvis = LeafVectorFromPortalVector( portalvector, uncompressed );
|
|
numvis++; // count the leaf itself
|
|
|
|
// save uncompressed for PHS calculation
|
|
Mem_Copy( uncompressedvis + leafnum * leafbytes, uncompressed, leafbytes );
|
|
|
|
totalpvs += numvis;
|
|
i = CompressVis( uncompressed, compressed );
|
|
|
|
dest = vismap_p;
|
|
vismap_p += i;
|
|
|
|
if( vismap_p > vismap_end )
|
|
Sys_Error( "ClusterPVS: vismap expansion overflow at cluster %i\n", leafnum );
|
|
|
|
dvis->bitofs[leafnum][DVIS_PVS] = dest - vismap;
|
|
Mem_Copy( dest, compressed, i );
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CalcPortalVis
|
|
==================
|
|
*/
|
|
void CalcPortalVis( void )
|
|
{
|
|
RunThreadsOnIndividual( numportals * 2, true, PortalFlow );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CalcPassageVis
|
|
==================
|
|
*/
|
|
void CalcPassageVis( void )
|
|
{
|
|
PassageMemory();
|
|
|
|
RunThreadsOnIndividual( numportals * 2, true, CreatePassages );
|
|
RunThreadsOnIndividual( numportals * 2, true, PassageFlow );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CalcPassagePortalVis
|
|
==================
|
|
*/
|
|
void CalcPassagePortalVis( void )
|
|
{
|
|
PassageMemory();
|
|
|
|
RunThreadsOnIndividual( numportals * 2, true, CreatePassages );
|
|
RunThreadsOnIndividual( numportals * 2, true, PassagePortalFlow );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CalcFastVis
|
|
==================
|
|
*/
|
|
void CalcFastVis( void )
|
|
{
|
|
int i;
|
|
|
|
// fastvis just uses mightsee for a very loose bound
|
|
for( i = 0; i < numportals * 2; i++ )
|
|
{
|
|
portals[i].portalvis = portals[i].portalflood;
|
|
portals[i].status = stat_done;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CalcPVS
|
|
==================
|
|
*/
|
|
void CalcPVS( void )
|
|
{
|
|
int i;
|
|
|
|
Msg ("Building PVS...\n");
|
|
|
|
RunThreadsOnIndividual( numportals * 2, true, BasePortalVis );
|
|
SortPortals ();
|
|
|
|
if( fastvis ) CalcFastVis();
|
|
else if( noPassageVis )
|
|
CalcPortalVis();
|
|
else if( passageVisOnly )
|
|
CalcPassageVis();
|
|
else CalcPassagePortalVis();
|
|
|
|
// assemble the leaf vis lists by oring and compressing the portal lists
|
|
for( i = 0; i < portalclusters; i++ )
|
|
ClusterPVS( i );
|
|
|
|
Msg( "Total visible clusters: %i\n", totalpvs );
|
|
Msg( "Average clusters visible: %i\n", totalpvs / portalclusters );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SetPortalSphere
|
|
==================
|
|
*/
|
|
void SetPortalSphere( vportal_t *p )
|
|
{
|
|
int i;
|
|
vec3_t total, dist;
|
|
winding_t *w;
|
|
float r, bestr;
|
|
|
|
w = p->winding;
|
|
VectorCopy( vec3_origin, total );
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
VectorAdd( total, w->p[i], total );
|
|
}
|
|
|
|
for( i = 0; i < 3; i++ ) total[i] /= w->numpoints;
|
|
|
|
bestr = 0;
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
VectorSubtract( w->p[i], total, dist );
|
|
r = VectorLength( dist );
|
|
if( r > bestr ) bestr = r;
|
|
}
|
|
VectorCopy( total, p->origin );
|
|
p->radius = bestr;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Winding_PlanesConcave
|
|
=============
|
|
*/
|
|
int Winding_PlanesConcave( winding_t *w1, winding_t *w2, vec3_t normal1, vec3_t normal2, float dist1, float dist2 )
|
|
{
|
|
int i;
|
|
|
|
if( !w1 || !w2 ) return false;
|
|
|
|
// check if one of the points of winding 1 is at the front of the plane of winding 2
|
|
for( i = 0; i < w1->numpoints; i++ )
|
|
{
|
|
if( DotProduct( normal2, w1->p[i] ) - dist2 > WCONVEX_EPSILON ) return true;
|
|
}
|
|
// check if one of the points of winding 2 is at the front of the plane of winding 1
|
|
for( i = 0; i < w2->numpoints; i++ )
|
|
{
|
|
if( DotProduct( normal1, w2->p[i] ) - dist1 > WCONVEX_EPSILON ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
TryMergeLeaves
|
|
============
|
|
*/
|
|
int TryMergeLeaves( int l1num, int l2num )
|
|
{
|
|
int i, j, k, n, numportals;
|
|
vplane_t plane1, plane2;
|
|
leaf_t *l1, *l2;
|
|
vportal_t *p1, *p2;
|
|
vportal_t *portals[MAX_PORTALS_ON_LEAF];
|
|
|
|
for( k = 0; k < 2; k++ )
|
|
{
|
|
if( k ) l1 = &leafs[l1num];
|
|
else l1 = &faceleafs[l1num];
|
|
for( i = 0; i < l1->numportals; i++ )
|
|
{
|
|
p1 = l1->portals[i];
|
|
if( p1->leaf == l2num ) continue;
|
|
for( n = 0; n < 2; n++ )
|
|
{
|
|
if( n ) l2 = &leafs[l2num];
|
|
else l2 = &faceleafs[l2num];
|
|
for( j = 0; j < l2->numportals; j++ )
|
|
{
|
|
p2 = l2->portals[j];
|
|
if( p2->leaf == l1num )
|
|
continue;
|
|
plane1 = p1->plane;
|
|
plane2 = p2->plane;
|
|
if( Winding_PlanesConcave( p1->winding, p2->winding, plane1.normal, plane2.normal, plane1.dist, plane2.dist ))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for( k = 0; k < 2; k++ )
|
|
{
|
|
if( k )
|
|
{
|
|
l1 = &leafs[l1num];
|
|
l2 = &leafs[l2num];
|
|
}
|
|
else
|
|
{
|
|
l1 = &faceleafs[l1num];
|
|
l2 = &faceleafs[l2num];
|
|
}
|
|
numportals = 0;
|
|
// the leaves can be merged now
|
|
for( i = 0; i < l1->numportals; i++ )
|
|
{
|
|
p1 = l1->portals[i];
|
|
if( p1->leaf == l2num )
|
|
{
|
|
p1->removed = true;
|
|
continue;
|
|
}
|
|
portals[numportals++] = p1;
|
|
}
|
|
for( j = 0; j < l2->numportals; j++ )
|
|
{
|
|
p2 = l2->portals[j];
|
|
if( p2->leaf == l1num )
|
|
{
|
|
p2->removed = true;
|
|
continue;
|
|
}
|
|
portals[numportals++] = p2;
|
|
}
|
|
for( i = 0; i < numportals; i++ )
|
|
{
|
|
l2->portals[i] = portals[i];
|
|
}
|
|
l2->numportals = numportals;
|
|
l1->merged = l2num;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
============
|
|
UpdatePortals
|
|
============
|
|
*/
|
|
void UpdatePortals( void )
|
|
{
|
|
int i;
|
|
vportal_t *p;
|
|
|
|
for( i = 0; i < numportals * 2; i++ )
|
|
{
|
|
p = &portals[i];
|
|
if( p->removed ) continue;
|
|
while( leafs[p->leaf].merged >= 0 )
|
|
p->leaf = leafs[p->leaf].merged;
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
MergeLeaves
|
|
|
|
try to merge leaves but don't merge through hint splitters
|
|
============
|
|
*/
|
|
void MergeLeaves( void )
|
|
{
|
|
int i, j, nummerges, totalnummerges;
|
|
leaf_t *leaf;
|
|
vportal_t *p;
|
|
|
|
totalnummerges = 0;
|
|
do
|
|
{
|
|
nummerges = 0;
|
|
for( i = 0; i < portalclusters; i++ )
|
|
{
|
|
leaf = &leafs[i];
|
|
// if this leaf is merged already
|
|
if( leaf->merged >= 0 ) continue;
|
|
|
|
for( j = 0; j < leaf->numportals; j++ )
|
|
{
|
|
p = leaf->portals[j];
|
|
|
|
if( p->removed ) continue;
|
|
// never merge through hint portals
|
|
if( p->hint ) continue;
|
|
if( TryMergeLeaves( i, p->leaf ))
|
|
{
|
|
UpdatePortals();
|
|
nummerges++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
totalnummerges += nummerges;
|
|
} while( nummerges );
|
|
|
|
Msg( "%6d leaves merged\n", totalnummerges );
|
|
}
|
|
|
|
/*
|
|
============
|
|
TryMergeWinding
|
|
============
|
|
*/
|
|
winding_t *TryMergeWinding( winding_t *f1, winding_t *f2, vec3_t planenormal )
|
|
{
|
|
float *p1, *p2, *p3, *p4, *back;
|
|
winding_t *newf;
|
|
int i, j, k, l;
|
|
vec3_t normal, delta;
|
|
float dot;
|
|
bool keep1, keep2;
|
|
|
|
|
|
// find a common edge
|
|
p1 = p2 = NULL; // stop compiler warning
|
|
j = 0; //
|
|
|
|
for( i = 0; i < f1->numpoints; i++ )
|
|
{
|
|
p1 = f1->p[i];
|
|
p2 = f1->p[(i+1) % f1->numpoints];
|
|
for( j = 0; j < f2->numpoints; j++ )
|
|
{
|
|
p3 = f2->p[j];
|
|
p4 = f2->p[(j+1) % f2->numpoints];
|
|
for( k = 0; k < 3; k++ )
|
|
{
|
|
if(fabs( p1[k] - p4[k]) > 0.1 ) break;
|
|
if(fabs( p2[k] - p3[k]) > 0.1 ) break;
|
|
}
|
|
if( k == 3 ) break;
|
|
}
|
|
if( j < f2->numpoints ) break;
|
|
}
|
|
|
|
if( i == f1->numpoints ) return NULL; // no matching edges
|
|
|
|
// check slope of connected lines
|
|
// if the slopes are colinear, the point can be removed
|
|
back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
|
|
VectorSubtract( p1, back, delta );
|
|
CrossProduct( planenormal, delta, normal );
|
|
VectorNormalize( normal );
|
|
|
|
back = f2->p[(j+2)%f2->numpoints];
|
|
VectorSubtract( back, p1, delta );
|
|
dot = DotProduct( delta, normal );
|
|
if( dot > CONTINUOUS_EPSILON ) return NULL; // not a convex polygon
|
|
keep1 = (bool)(dot < -CONTINUOUS_EPSILON );
|
|
|
|
back = f1->p[(i+2)%f1->numpoints];
|
|
VectorSubtract( back, p2, delta );
|
|
CrossProduct( planenormal, delta, normal );
|
|
VectorNormalize( normal );
|
|
|
|
back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
|
|
VectorSubtract( back, p2, delta );
|
|
dot = DotProduct( delta, normal );
|
|
if( dot > CONTINUOUS_EPSILON ) return NULL; // not a convex polygon
|
|
keep2 = (bool)( dot < -CONTINUOUS_EPSILON );
|
|
|
|
// build the new polygon
|
|
newf = NewWinding( f1->numpoints + f2->numpoints );
|
|
|
|
// copy first polygon
|
|
for( k = (i+1)%f1->numpoints; k != i; k = (k+1)%f1->numpoints)
|
|
{
|
|
if( k == (i+1)%f1->numpoints && !keep2) continue;
|
|
VectorCopy( f1->p[k], newf->p[newf->numpoints] );
|
|
newf->numpoints++;
|
|
}
|
|
|
|
// copy second polygon
|
|
for( l = (j+1)%f2->numpoints; l != j; l = (l+1)%f2->numpoints )
|
|
{
|
|
if( l == (j+1)%f2->numpoints && !keep1 ) continue;
|
|
VectorCopy( f2->p[l], newf->p[newf->numpoints] );
|
|
newf->numpoints++;
|
|
}
|
|
return newf;
|
|
}
|
|
|
|
/*
|
|
============
|
|
MergeLeafPortals
|
|
============
|
|
*/
|
|
void MergeLeafPortals( void )
|
|
{
|
|
int i, j, k, nummerges, hintsmerged;
|
|
leaf_t *leaf;
|
|
vportal_t *p1, *p2;
|
|
winding_t *w;
|
|
|
|
nummerges = 0;
|
|
hintsmerged = 0;
|
|
for( i = 0; i < portalclusters; i++ )
|
|
{
|
|
leaf = &leafs[i];
|
|
if( leaf->merged >= 0 ) continue;
|
|
for( j = 0; j < leaf->numportals; j++ )
|
|
{
|
|
p1 = leaf->portals[j];
|
|
if( p1->removed ) continue;
|
|
for( k = j+1; k < leaf->numportals; k++ )
|
|
{
|
|
p2 = leaf->portals[k];
|
|
if( p2->removed ) continue;
|
|
if( p1->leaf == p2->leaf )
|
|
{
|
|
w = TryMergeWinding( p1->winding, p2->winding, p1->plane.normal );
|
|
if( w )
|
|
{
|
|
FreeWinding( p1->winding );
|
|
p1->winding = w;
|
|
if( p1->hint && p2->hint )
|
|
hintsmerged++;
|
|
p1->hint |= p2->hint;
|
|
SetPortalSphere( p1 );
|
|
p2->removed = true;
|
|
nummerges++;
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( k < leaf->numportals ) break;
|
|
}
|
|
}
|
|
|
|
Msg( "%6d portals merged\n", nummerges );
|
|
Msg( "%6d hint portals merged\n", hintsmerged );
|
|
}
|
|
|
|
/*
|
|
============
|
|
WritePortals
|
|
============
|
|
*/
|
|
int CountActivePortals( void )
|
|
{
|
|
int num, hints, j;
|
|
vportal_t *p;
|
|
|
|
num = 0;
|
|
hints = 0;
|
|
for( j = 0; j < numportals * 2; j++ )
|
|
{
|
|
p = portals + j;
|
|
if( p->removed ) continue;
|
|
if( p->hint ) hints++;
|
|
num++;
|
|
}
|
|
Msg( "%6d active portals\n", num );
|
|
Msg( "%6d hint portals\n", hints );
|
|
return num;
|
|
}
|
|
|
|
/*
|
|
============
|
|
LoadPortals
|
|
============
|
|
*/
|
|
void LoadPortals( void )
|
|
{
|
|
int i, j, hint;
|
|
bool load;
|
|
vportal_t *p;
|
|
leaf_t *l;
|
|
char magic[80];
|
|
char path[MAX_SYSPATH];
|
|
int numpoints;
|
|
winding_t *w;
|
|
int leafnums[2];
|
|
vplane_t plane;
|
|
|
|
sprintf( path, "maps/%s.prt", gs_filename );
|
|
load = Com_LoadScript( path, NULL, 0 );
|
|
if (!load) Sys_Error ("LoadPortals: couldn't read %s\n", path);
|
|
Msg ("reading %s\n", path);
|
|
|
|
com.strcpy( magic, Com_GetToken( true ));
|
|
portalclusters = com.atoi(Com_GetToken( true ));
|
|
numportals = com.atoi(Com_GetToken( true ));
|
|
numvisfaces = com.atoi(Com_GetToken( true ));
|
|
|
|
if( !portalclusters && !numportals ) Sys_Break( "LoadPortals: failed to read header\n" );
|
|
if( com.strcmp( magic, PORTALFILE )) Sys_Break( "LoadPortals: not a portal file\n" );
|
|
|
|
Msg( "%4i portalclusters\n", portalclusters );
|
|
Msg( "%4i numportals\n", numportals );
|
|
Msg( "%6i numfaces\n", numvisfaces );
|
|
|
|
// these counts should take advantage of 64 bit systems automatically
|
|
leafbytes = ((portalclusters+63)&~63)>>3;
|
|
leaflongs = leafbytes/sizeof(long);
|
|
|
|
portalbytes = ((numportals*2+63)&~63)>>3;
|
|
portallongs = portalbytes/sizeof(long);
|
|
|
|
// each file portal is split into two memory portals
|
|
portals = BSP_Malloc( 2 * numportals * sizeof( vportal_t ));
|
|
leafs = BSP_Malloc( portalclusters * sizeof(leaf_t));
|
|
|
|
originalvismapsize = portalclusters * leafbytes;
|
|
uncompressedvis = BSP_Malloc( originalvismapsize );
|
|
vismap = vismap_p = dvisdata;
|
|
dvis->numclusters = portalclusters;
|
|
vismap_p = (byte *)&dvis->bitofs[portalclusters];
|
|
vismap_end = vismap + MAX_MAP_VISIBILITY;
|
|
|
|
for( i = 0; i < portalclusters; i++ )
|
|
leafs[i].merged = -1;
|
|
|
|
for( i = 0, p = portals; i < numportals; i++ )
|
|
{
|
|
numpoints = com.atoi(Com_GetToken( true )); // newline
|
|
leafnums[0] = com.atoi(Com_GetToken( false ));
|
|
leafnums[1] = com.atoi(Com_GetToken( false ));
|
|
|
|
if( numpoints > MAX_POINTS_ON_WINDING )
|
|
Sys_Break( "LoadPortals: portal %i has too many points\n", i );
|
|
if(( uint )leafnums[0] > portalclusters || (uint)leafnums[1] > portalclusters )
|
|
Sys_Break( "LoadPortals: reading portal %i\n", i );
|
|
|
|
hint = com.atoi(Com_GetToken( false ));
|
|
w = p->winding = NewWinding( numpoints );
|
|
w->numpoints = numpoints;
|
|
|
|
for (j = 0; j < numpoints; j++)
|
|
{
|
|
double v[3];
|
|
int k;
|
|
|
|
// scanf into double, then assign to float
|
|
// so we don't care what size float is
|
|
Com_GetToken( false ); //get '(' symbol
|
|
if(!Com_MatchToken( "(" )) Sys_Break( "LoadPortals: not found ( reading portal %i\n", i );
|
|
v[0] = com.atof(Com_GetToken( false ));
|
|
v[1] = com.atof(Com_GetToken( false ));
|
|
v[2] = com.atof(Com_GetToken( false ));
|
|
|
|
Com_GetToken( false );
|
|
if(!Com_MatchToken( ")" )) Sys_Error ("LoadPortals: not found ) reading portal %i", i );
|
|
|
|
for( k = 0; k < 3; k++ ) w->p[j][k] = v[k];
|
|
}
|
|
|
|
// calc plane
|
|
PlaneFromWinding( w, &plane );
|
|
|
|
// create forward portal
|
|
l = &leafs[leafnums[0]];
|
|
if( l->numportals == MAX_PORTALS_ON_LEAF ) Sys_Break( "Leaf with too many portals\n" );
|
|
l->portals[l->numportals] = p;
|
|
l->numportals++;
|
|
|
|
p->num = i + 1;
|
|
p->hint = hint;
|
|
p->winding = w;
|
|
VectorNegate( plane.normal, p->plane.normal );
|
|
p->plane.dist = -plane.dist;
|
|
p->leaf = leafnums[1];
|
|
SetPortalSphere( p );
|
|
p++;
|
|
|
|
// create backwards portal
|
|
l = &leafs[leafnums[1]];
|
|
if (l->numportals == MAX_PORTALS_ON_LEAF) Sys_Error ("Leaf with too many portals");
|
|
l->portals[l->numportals] = p;
|
|
l->numportals++;
|
|
|
|
p->num = i + 1;
|
|
p->hint = hint;
|
|
p->winding = NewWinding( w->numpoints );
|
|
p->winding->numpoints = w->numpoints;
|
|
for( j = 0; j < w->numpoints; j++ )
|
|
{
|
|
VectorCopy( w->p[w->numpoints-1-j], p->winding->p[j] );
|
|
}
|
|
|
|
p->plane = plane;
|
|
p->leaf = leafnums[0];
|
|
SetPortalSphere( p );
|
|
p++;
|
|
}
|
|
|
|
faces = BSP_Malloc( 2 * numvisfaces * sizeof( vportal_t ));
|
|
faceleafs = BSP_Malloc( portalclusters * sizeof( leaf_t ));
|
|
|
|
for( i = 0, p = faces; i < numvisfaces; i++ )
|
|
{
|
|
numpoints = com.atoi(Com_GetToken( true )); // newline
|
|
leafnums[0] = com.atoi(Com_GetToken( false ));
|
|
|
|
w = p->winding = NewWinding( numpoints );
|
|
w->numpoints = numpoints;
|
|
|
|
for( j = 0; j < numpoints; j++ )
|
|
{
|
|
double v[3];
|
|
int k;
|
|
|
|
// scanf into double, then assign to vec_t
|
|
// so we don't care what size vec_t is
|
|
Com_GetToken( false ); //get '(' symbol
|
|
if(!Com_MatchToken( "(" )) Sys_Break( "LoadPortals: not found ( reading surface %i\n", i );
|
|
v[0] = com.atof(Com_GetToken( false ));
|
|
v[1] = com.atof(Com_GetToken( false ));
|
|
v[2] = com.atof(Com_GetToken( false ));
|
|
Com_GetToken( false );
|
|
if(!Com_MatchToken( ")" )) Sys_Break( "LoadPortals: not found ) reading surface %i", i );
|
|
for( k = 0; k < 3; k++ ) w->p[j][k] = v[k];
|
|
}
|
|
// calc plane
|
|
PlaneFromWinding( w, &plane );
|
|
|
|
l = &faceleafs[leafnums[0]];
|
|
l->merged = -1;
|
|
if( l->numportals == MAX_PORTALS_ON_LEAF )
|
|
Sys_Break( "Leaf with too many faces\n" );
|
|
l->portals[l->numportals] = p;
|
|
l->numportals++;
|
|
|
|
p->num = i + 1;
|
|
p->winding = w;
|
|
// normal pointing out of the leaf
|
|
VectorSubtract( vec3_origin, plane.normal, p->plane.normal );
|
|
p->plane.dist = -plane.dist;
|
|
p->leaf = -1;
|
|
SetPortalSphere( p );
|
|
p++;
|
|
}
|
|
}
|
|
|
|
void MergePortals( void )
|
|
{
|
|
if( !mergevis ) return;
|
|
MergeLeaves();
|
|
MergeLeafPortals();
|
|
}
|
|
|
|
void ClusterPHS( int clusternum )
|
|
{
|
|
int j, k, l, index;
|
|
int bitbyte;
|
|
long *dest, *src;
|
|
byte *scan;
|
|
byte uncompressed[MAX_MAP_LEAFS/8];
|
|
byte compressed[MAX_MAP_LEAFS/8];
|
|
|
|
scan = uncompressedvis + clusternum * leafbytes;
|
|
Mem_Copy( uncompressed, scan, leafbytes );
|
|
for( j = 0; j < leafbytes; j++ )
|
|
{
|
|
bitbyte = scan[j];
|
|
if( !bitbyte ) continue;
|
|
for( k = 0; k < 8; k++ )
|
|
{
|
|
if(!( bitbyte & (1<<k))) continue;
|
|
// OR this pvs row into the phs
|
|
index = ((j<<3)+k);
|
|
if( index >= portalclusters )
|
|
Sys_Error( "Bad bit in PVS\n" ); // pad bits should be 0
|
|
src = (long *)(uncompressedvis + index * leafbytes);
|
|
dest = (long *)uncompressed;
|
|
for (l = 0; l < leaflongs; l++) ((long *)uncompressed)[l] |= src[l];
|
|
}
|
|
}
|
|
for( j = 0; j < portalclusters; j++ )
|
|
if( uncompressed[j>>3] & (1<<( j & 7 )))
|
|
totalphs++;
|
|
|
|
// compress the bit string
|
|
j = CompressVis( uncompressed, compressed );
|
|
|
|
dest = (long *)vismap_p;
|
|
vismap_p += j;
|
|
|
|
if( vismap_p > vismap_end )
|
|
Sys_Error( "ClusterPHS: vismap expansion overflow at cluster %i", clusternum );
|
|
|
|
dvis->bitofs[clusternum][DVIS_PHS] = (byte *)dest - vismap;
|
|
Mem_Copy( dest, compressed, j );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CalcPHS
|
|
|
|
Calculate the PHS (Potentially Hearable Set)
|
|
by ORing together all the PVS visible from a leaf
|
|
================
|
|
*/
|
|
void CalcPHS( void )
|
|
{
|
|
Msg ("Building PHS...\n");
|
|
RunThreadsOnIndividual( portalclusters, true, ClusterPHS );
|
|
|
|
Msg( "Total hearable clusters: %i\n", totalphs );
|
|
Msg( "Average clusters hearable: %i\n", totalphs / portalclusters );
|
|
}
|
|
|
|
/*
|
|
===========
|
|
main
|
|
===========
|
|
*/
|
|
void WvisMain ( bool option )
|
|
{
|
|
fastvis = !option;
|
|
|
|
if(!LoadBSPFile())
|
|
{
|
|
// map not exist, create it
|
|
WbspMain( false );
|
|
LoadBSPFile();
|
|
}
|
|
|
|
if( numnodes == 0 || numsurfaces == 0 ) Sys_Error( "Empty map" );
|
|
Msg ("---- Visibility ---- [%s]\n", fastvis ? "fast" : "full" );
|
|
LoadPortals ();
|
|
MergePortals ();
|
|
CountActivePortals ();
|
|
|
|
CalcPVS ();
|
|
CalcPHS ();
|
|
|
|
visdatasize = vismap_p - dvisdata;
|
|
Msg( "visdatasize: %i compressed from %i\n", visdatasize, originalvismapsize * 2 );
|
|
|
|
WriteBSPFile();
|
|
}
|
|
|