676 lines
14 KiB
C
676 lines
14 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// winding.c - bsp brush winding
|
|
//=======================================================================
|
|
|
|
#include "bsplib.h"
|
|
|
|
// counters are only bumped when running single threaded,
|
|
// because they are an awefull coherence problem
|
|
int c_active_windings;
|
|
int c_peak_windings;
|
|
int c_winding_allocs;
|
|
int c_winding_points;
|
|
|
|
/*
|
|
=============
|
|
AllocWinding
|
|
=============
|
|
*/
|
|
winding_t *AllocWinding( int points )
|
|
{
|
|
winding_t *w;
|
|
int s;
|
|
|
|
if( GetNumThreads() == 1 )
|
|
{
|
|
c_winding_allocs++;
|
|
c_winding_points += points;
|
|
c_active_windings++;
|
|
if( c_active_windings > c_peak_windings )
|
|
c_peak_windings = c_active_windings;
|
|
}
|
|
s = sizeof( vec_t ) * 3 * points + sizeof( int );
|
|
w = BSP_Malloc( s );
|
|
return w;
|
|
}
|
|
|
|
void FreeWinding( winding_t *w )
|
|
{
|
|
if( *(int *)w == 0xdeaddead )
|
|
Sys_Error( "FreeWinding: already freed\n" );
|
|
*(uint *)w = 0xdeaddead;
|
|
|
|
if( GetNumThreads() == 1 )
|
|
c_active_windings--;
|
|
Mem_Free( w );
|
|
}
|
|
|
|
/*
|
|
============
|
|
RemoveColinearPoints
|
|
============
|
|
*/
|
|
int c_removed;
|
|
|
|
void RemoveColinearPoints( winding_t *w )
|
|
{
|
|
int i, j, k;
|
|
vec3_t v1, v2;
|
|
int nump;
|
|
vec3_t p[MAX_POINTS_ON_WINDING];
|
|
|
|
nump = 0;
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
j = (i+1) % w->numpoints;
|
|
k = (i+w->numpoints-1) % w->numpoints;
|
|
VectorSubtract( w->p[j], w->p[i], v1 );
|
|
VectorSubtract( w->p[i], w->p[k], v2 );
|
|
VectorNormalize( v1 );
|
|
VectorNormalize( v2 );
|
|
if( DotProduct( v1, v2 ) < 0.999)
|
|
{
|
|
VectorCopy( w->p[i], p[nump] );
|
|
nump++;
|
|
}
|
|
}
|
|
|
|
if( nump == w->numpoints )
|
|
return;
|
|
|
|
if( GetNumThreads() == 1 )
|
|
c_removed += w->numpoints - nump;
|
|
w->numpoints = nump;
|
|
Mem_Copy( w->p, p, nump * sizeof( p[0] ));
|
|
}
|
|
|
|
/*
|
|
============
|
|
WindingPlane
|
|
============
|
|
*/
|
|
void WindingPlane( winding_t *w, vec3_t normal, vec_t *dist )
|
|
{
|
|
vec3_t v1, v2;
|
|
|
|
VectorSubtract( w->p[1], w->p[0], v1 );
|
|
VectorSubtract( w->p[2], w->p[0], v2 );
|
|
CrossProduct( v2, v1, normal );
|
|
VectorNormalize( normal );
|
|
*dist = DotProduct( w->p[0], normal );
|
|
|
|
}
|
|
|
|
/*
|
|
=============
|
|
WindingArea
|
|
=============
|
|
*/
|
|
vec_t WindingArea( winding_t *w )
|
|
{
|
|
int i;
|
|
vec3_t d1, d2, cross;
|
|
vec_t total;
|
|
|
|
total = 0;
|
|
for( i = 2; i < w->numpoints; i++ )
|
|
{
|
|
VectorSubtract( w->p[i-1], w->p[0], d1 );
|
|
VectorSubtract( w->p[i], w->p[0], d2 );
|
|
CrossProduct( d1, d2, cross );
|
|
total += 0.5 * VectorLength ( cross );
|
|
}
|
|
return total;
|
|
}
|
|
|
|
void WindingBounds( winding_t *w, vec3_t mins, vec3_t maxs )
|
|
{
|
|
vec_t v;
|
|
int i, j;
|
|
|
|
mins[0] = mins[1] = mins[2] = 99999;
|
|
maxs[0] = maxs[1] = maxs[2] = -99999;
|
|
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
v = w->p[i][j];
|
|
if( v < mins[j] ) mins[j] = v;
|
|
if( v > maxs[j] ) maxs[j] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
WindingCenter
|
|
=============
|
|
*/
|
|
void WindingCenter( winding_t *w, vec3_t center )
|
|
{
|
|
int i;
|
|
float scale;
|
|
|
|
VectorClear( center );
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
VectorAdd( w->p[i], center, center );
|
|
|
|
scale = 1.0 / w->numpoints;
|
|
VectorScale( center, scale, center );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
BaseWindingForPlane
|
|
=================
|
|
*/
|
|
winding_t *BaseWindingForPlane( vec3_t normal, vec_t dist )
|
|
{
|
|
int i, x;
|
|
vec_t max, v;
|
|
vec3_t org, vright, vup;
|
|
winding_t *w;
|
|
|
|
// find the major axis
|
|
max = -BOGUS_RANGE;
|
|
x = -1;
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
v = fabs( normal[i] );
|
|
if( v > max )
|
|
{
|
|
x = i;
|
|
max = v;
|
|
}
|
|
}
|
|
if( x == -1 ) Sys_Error( "BaseWindingForPlane: no axis found\n" );
|
|
|
|
VectorClear( vup );
|
|
switch( x )
|
|
{
|
|
case 0:
|
|
case 1:
|
|
vup[2] = 1;
|
|
break;
|
|
case 2:
|
|
vup[0] = 1;
|
|
break;
|
|
}
|
|
|
|
v = DotProduct( vup, normal );
|
|
VectorMA( vup, -v, normal, vup );
|
|
VectorNormalize( vup );
|
|
|
|
VectorScale( normal, dist, org );
|
|
CrossProduct( vup, normal, vright );
|
|
VectorScale( vup, MAX_WORLD_COORD, vup );
|
|
VectorScale( vright, MAX_WORLD_COORD, vright );
|
|
|
|
// project a really big axis aligned box onto the plane
|
|
w = AllocWinding( 4 );
|
|
|
|
VectorSubtract( org, vright, w->p[0] );
|
|
VectorAdd( w->p[0], vup, w->p[0] );
|
|
|
|
VectorAdd( org, vright, w->p[1] );
|
|
VectorAdd( w->p[1], vup, w->p[1] );
|
|
|
|
VectorAdd( org, vright, w->p[2] );
|
|
VectorSubtract( w->p[2], vup, w->p[2] );
|
|
|
|
VectorSubtract( org, vright, w->p[3] );
|
|
VectorSubtract( w->p[3], vup, w->p[3] );
|
|
|
|
w->numpoints = 4;
|
|
|
|
return w;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CopyWinding
|
|
==================
|
|
*/
|
|
winding_t *CopyWinding( winding_t *w )
|
|
{
|
|
int size;
|
|
winding_t *c;
|
|
|
|
c = AllocWinding( w->numpoints );
|
|
size = (int)((winding_t *)0)->p[w->numpoints];
|
|
Mem_Copy( c, w, size );
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
ReverseWinding
|
|
==================
|
|
*/
|
|
winding_t *ReverseWinding( winding_t *w )
|
|
{
|
|
int i;
|
|
winding_t *c;
|
|
|
|
c = AllocWinding( w->numpoints );
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
VectorCopy( w->p[w->numpoints-1-i], c->p[i] );
|
|
}
|
|
c->numpoints = w->numpoints;
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ClipWindingEpsilon
|
|
=============
|
|
*/
|
|
void ClipWindingEpsilon( winding_t *in, vec3_t normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back )
|
|
{
|
|
vec_t dists[MAX_POINTS_ON_WINDING+4];
|
|
int sides[MAX_POINTS_ON_WINDING+4];
|
|
int counts[3];
|
|
static vec_t dot;
|
|
int i, j;
|
|
vec_t *p1, *p2;
|
|
vec3_t mid;
|
|
winding_t *f, *b;
|
|
int maxpts;
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
// determine sides for each point
|
|
for( i = 0; i < in->numpoints; i++ )
|
|
{
|
|
dot = DotProduct( in->p[i], normal );
|
|
dot -= dist;
|
|
dists[i] = dot;
|
|
if( dot > epsilon )
|
|
sides[i] = SIDE_FRONT;
|
|
else if( dot < -epsilon )
|
|
sides[i] = SIDE_BACK;
|
|
else sides[i] = SIDE_ON;
|
|
counts[sides[i]]++;
|
|
}
|
|
sides[i] = sides[0];
|
|
dists[i] = dists[0];
|
|
|
|
*front = *back = NULL;
|
|
|
|
if( !counts[0] )
|
|
{
|
|
*back = CopyWinding( in );
|
|
return;
|
|
}
|
|
if( !counts[1] )
|
|
{
|
|
*front = CopyWinding( in );
|
|
return;
|
|
}
|
|
|
|
maxpts = in->numpoints + 4; // cant use counts[0]+2 because of fp grouping errors
|
|
|
|
*front = f = AllocWinding( maxpts );
|
|
*back = b = AllocWinding( maxpts );
|
|
|
|
for( i = 0; i < in->numpoints; i++ )
|
|
{
|
|
p1 = in->p[i];
|
|
|
|
if( sides[i] == SIDE_ON )
|
|
{
|
|
VectorCopy( p1, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
VectorCopy( p1, b->p[b->numpoints] );
|
|
b->numpoints++;
|
|
continue;
|
|
}
|
|
|
|
if( sides[i] == SIDE_FRONT )
|
|
{
|
|
VectorCopy( p1, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
}
|
|
if( sides[i] == SIDE_BACK )
|
|
{
|
|
VectorCopy( p1, b->p[b->numpoints] );
|
|
b->numpoints++;
|
|
}
|
|
|
|
if( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] )
|
|
continue;
|
|
|
|
// generate a split point
|
|
p2 = in->p[(i+1)%in->numpoints];
|
|
|
|
dot = dists[i] / (dists[i] - dists[i+1]);
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
// avoid round off error when possible
|
|
if( normal[j] == 1 )
|
|
mid[j] = dist;
|
|
else if( normal[j] == -1 )
|
|
mid[j] = -dist;
|
|
else mid[j] = p1[j] + dot * (p2[j]-p1[j]);
|
|
}
|
|
|
|
VectorCopy( mid, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
VectorCopy( mid, b->p[b->numpoints] );
|
|
b->numpoints++;
|
|
}
|
|
|
|
if( f->numpoints > maxpts || b->numpoints > maxpts )
|
|
Sys_Error( "ClipWinding: points exceeded estimate\n" );
|
|
if( f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING )
|
|
Sys_Error( "ClipWinding: MAX_POINTS_ON_WINDING\n" );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ChopWindingInPlace
|
|
=============
|
|
*/
|
|
void ChopWindingInPlace( winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon )
|
|
{
|
|
winding_t *in;
|
|
vec_t dists[MAX_POINTS_ON_WINDING+4];
|
|
int sides[MAX_POINTS_ON_WINDING+4];
|
|
int counts[3];
|
|
static vec_t dot;
|
|
int i, j;
|
|
vec_t *p1, *p2;
|
|
vec3_t mid;
|
|
winding_t *f;
|
|
int maxpts;
|
|
|
|
in = *inout;
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
// determine sides for each point
|
|
for( i = 0; i < in->numpoints; i++ )
|
|
{
|
|
dot = DotProduct( in->p[i], normal );
|
|
dot -= dist;
|
|
dists[i] = dot;
|
|
if( dot > epsilon )
|
|
sides[i] = SIDE_FRONT;
|
|
else if( dot < -epsilon )
|
|
sides[i] = SIDE_BACK;
|
|
else sides[i] = SIDE_ON;
|
|
counts[sides[i]]++;
|
|
}
|
|
|
|
sides[i] = sides[0];
|
|
dists[i] = dists[0];
|
|
|
|
if( !counts[0] )
|
|
{
|
|
FreeWinding( in );
|
|
*inout = NULL;
|
|
return;
|
|
}
|
|
|
|
if( !counts[1] ) return; // inout stays the same
|
|
maxpts = in->numpoints + 4; // cant use counts[0]+2 because of fp grouping errors
|
|
|
|
f = AllocWinding( maxpts );
|
|
|
|
for( i = 0; i < in->numpoints; i++ )
|
|
{
|
|
p1 = in->p[i];
|
|
|
|
if( sides[i] == SIDE_ON )
|
|
{
|
|
VectorCopy( p1, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
continue;
|
|
}
|
|
|
|
if( sides[i] == SIDE_FRONT )
|
|
{
|
|
VectorCopy( p1, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
}
|
|
|
|
if( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] )
|
|
continue;
|
|
|
|
// generate a split point
|
|
p2 = in->p[(i+1) % in->numpoints];
|
|
|
|
dot = dists[i] / (dists[i] - dists[i+1]);
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
// avoid round off error when possible
|
|
if( normal[j] == 1 )
|
|
mid[j] = dist;
|
|
else if( normal[j] == -1 )
|
|
mid[j] = -dist;
|
|
else mid[j] = p1[j] + dot * (p2[j]-p1[j]);
|
|
}
|
|
|
|
VectorCopy( mid, f->p[f->numpoints] );
|
|
f->numpoints++;
|
|
}
|
|
|
|
if( f->numpoints > maxpts )
|
|
Sys_Error( "ClipWinding: points exceeded estimate\n" );
|
|
if( f->numpoints > MAX_POINTS_ON_WINDING )
|
|
Sys_Error( "ClipWinding: MAX_POINTS_ON_WINDING\n" );
|
|
|
|
FreeWinding( in );
|
|
*inout = f;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CheckWinding
|
|
|
|
=================
|
|
*/
|
|
void CheckWinding( winding_t *w )
|
|
{
|
|
int i, j;
|
|
vec_t *p1, *p2;
|
|
vec_t d, edgedist;
|
|
vec3_t dir, edgenormal, facenormal;
|
|
vec_t area;
|
|
vec_t facedist;
|
|
|
|
if( w->numpoints < 3 )
|
|
Sys_Error( "CheckWinding: %i points\n", w->numpoints );
|
|
|
|
area = WindingArea( w );
|
|
if( area < 1 ) Sys_Error( "CheckWinding: %f area\n", area );
|
|
|
|
WindingPlane( w, facenormal, &facedist );
|
|
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
p1 = w->p[i];
|
|
|
|
for( j = 0; j < 3; j++ )
|
|
if( p1[j] > MAX_WORLD_COORD || p1[j] < -MIN_WORLD_COORD )
|
|
Sys_Error( "CheckWinding: BUGUS_RANGE: %f", p1[j] );
|
|
|
|
j = i + 1 == w->numpoints ? 0 : i + 1;
|
|
|
|
// check the point is on the face plane
|
|
d = DotProduct( p1, facenormal ) - facedist;
|
|
if( d < -ON_EPSILON || d > ON_EPSILON )
|
|
Sys_Error( "CheckWinding: point off plane\n" );
|
|
|
|
// check the edge isnt degenerate
|
|
p2 = w->p[j];
|
|
VectorSubtract( p2, p1, dir );
|
|
|
|
if( VectorLength( dir ) < ON_EPSILON )
|
|
Sys_Error( "CheckWinding: degenerate edge\n" );
|
|
|
|
CrossProduct( facenormal, dir, edgenormal );
|
|
VectorNormalize( edgenormal );
|
|
edgedist = DotProduct( p1, edgenormal );
|
|
edgedist += ON_EPSILON;
|
|
|
|
// all other points must be on front side
|
|
for( j = 0; j < w->numpoints; j++ )
|
|
{
|
|
if( j == i ) continue;
|
|
d = DotProduct( w->p[j], edgenormal );
|
|
if( d > edgedist ) Sys_Error( "CheckWinding: non-convex\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
WindingOnPlaneSide
|
|
============
|
|
*/
|
|
int WindingOnPlaneSide( winding_t *w, vec3_t normal, vec_t dist )
|
|
{
|
|
bool front, back;
|
|
int i;
|
|
vec_t d;
|
|
|
|
front = false;
|
|
back = false;
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
d = DotProduct( w->p[i], normal ) - dist;
|
|
if( d < -ON_EPSILON )
|
|
{
|
|
if( front ) return SIDE_CROSS;
|
|
back = true;
|
|
continue;
|
|
}
|
|
if( d > ON_EPSILON )
|
|
{
|
|
if( back ) return SIDE_CROSS;
|
|
front = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( back ) return SIDE_BACK;
|
|
if( front ) return SIDE_FRONT;
|
|
return SIDE_ON;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
AddWindingToConvexHull
|
|
|
|
Both w and *hull are on the same plane
|
|
=================
|
|
*/
|
|
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal )
|
|
{
|
|
int i, j, k;
|
|
float d, *p, *copy;
|
|
int numHullPoints, numNew;
|
|
vec3_t hullPoints[MAX_HULL_POINTS];
|
|
vec3_t newHullPoints[MAX_HULL_POINTS];
|
|
vec3_t hullDirs[MAX_HULL_POINTS];
|
|
bool hullSide[MAX_HULL_POINTS];
|
|
bool outside;
|
|
vec3_t dir;
|
|
|
|
if( !*hull )
|
|
{
|
|
*hull = CopyWinding( w );
|
|
return;
|
|
}
|
|
|
|
numHullPoints = (*hull)->numpoints;
|
|
Mem_Copy( hullPoints, (*hull)->p, numHullPoints * sizeof( vec3_t ));
|
|
|
|
for( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
p = w->p[i];
|
|
|
|
// calculate hull side vectors
|
|
for( j = 0; j < numHullPoints; j++ )
|
|
{
|
|
k = ( j + 1 ) % numHullPoints;
|
|
VectorSubtract( hullPoints[k], hullPoints[j], dir );
|
|
VectorNormalize( dir );
|
|
CrossProduct( normal, dir, hullDirs[j] );
|
|
}
|
|
|
|
outside = false;
|
|
for( j = 0; j < numHullPoints; j++ )
|
|
{
|
|
VectorSubtract( p, hullPoints[j], dir );
|
|
d = DotProduct( dir, hullDirs[j] );
|
|
if( d >= ON_EPSILON ) outside = true;
|
|
if( d >= -ON_EPSILON ) hullSide[j] = true;
|
|
else hullSide[j] = false;
|
|
}
|
|
|
|
// if the point is effectively inside, do nothing
|
|
if( !outside ) continue;
|
|
|
|
// find the back side to front side transition
|
|
for( j = 0; j < numHullPoints; j++ )
|
|
{
|
|
if( !hullSide[j % numHullPoints] && hullSide[(j + 1) % numHullPoints] )
|
|
break;
|
|
}
|
|
|
|
if( j == numHullPoints ) continue;
|
|
|
|
// insert the point here
|
|
VectorCopy( p, newHullPoints[0] );
|
|
numNew = 1;
|
|
|
|
// copy over all points that aren't double fronts
|
|
j = ( j + 1 ) % numHullPoints;
|
|
for( k = 0; k < numHullPoints; k++ )
|
|
{
|
|
if( hullSide[(j+k) % numHullPoints] && hullSide[(j+k+1) % numHullPoints] )
|
|
continue;
|
|
copy = hullPoints[(j+k+1) % numHullPoints];
|
|
VectorCopy( copy, newHullPoints[numNew] );
|
|
numNew++;
|
|
}
|
|
|
|
numHullPoints = numNew;
|
|
Mem_Copy( hullPoints, newHullPoints, numHullPoints * sizeof( vec3_t ));
|
|
}
|
|
|
|
FreeWinding( *hull );
|
|
w = AllocWinding( numHullPoints );
|
|
w->numpoints = numHullPoints;
|
|
*hull = w;
|
|
Mem_Copy( w->p, hullPoints, numHullPoints * sizeof( vec3_t ));
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
WindingFromDrawSurf
|
|
====================
|
|
*/
|
|
winding_t *WindingFromDrawSurf( drawsurf_t *ds )
|
|
{
|
|
winding_t *w;
|
|
int i;
|
|
|
|
w = AllocWinding( ds->numverts );
|
|
w->numpoints = ds->numverts;
|
|
for( i = 0; i < ds->numverts; i++ )
|
|
{
|
|
VectorCopy( ds->verts[i].point, w->p[i] );
|
|
}
|
|
return w;
|
|
}
|
|
|