using winding_accu_t = std::vector<DoubleVector3>;

This commit is contained in:
Garux 2021-07-30 12:20:56 +03:00
parent ffa1a4340c
commit dbfb22e273
3 changed files with 45 additions and 150 deletions

View File

@ -48,18 +48,6 @@ winding_t *AllocWinding( int points ){
return safe_calloc( offsetof_array( winding_t, p, points ) );
}
/*
=============
AllocWindingAccu
=============
*/
winding_accu_t *AllocWindingAccu( int points ){
if ( points >= MAX_POINTS_ON_WINDING ) {
Error( "AllocWindingAccu failed: MAX_POINTS_ON_WINDING exceeded" );
}
return safe_calloc( offsetof_array( winding_accu_t, p, points ) );
}
/*
=============
FreeWinding
@ -78,24 +66,6 @@ void FreeWinding( winding_t *w ){
free( w );
}
/*
=============
FreeWindingAccu
=============
*/
void FreeWindingAccu( winding_accu_t *w ){
if ( !w ) {
Error( "FreeWindingAccu: winding is NULL" );
}
if ( *( (unsigned *) w ) == 0xdeaddead ) {
Error( "FreeWindingAccu: freed a freed winding" );
}
*( (unsigned *) w ) = 0xdeaddead;
free( w );
}
/*
============
RemoveColinearPoints
@ -177,7 +147,7 @@ Vector3 WindingCenter( const winding_t *w ){
BaseWindingForPlaneAccu
=================
*/
winding_accu_t *BaseWindingForPlaneAccu( const Plane3& plane ){
winding_accu_t BaseWindingForPlaneAccu( const Plane3& plane ){
// The goal in this function is to replicate the behavior of the original BaseWindingForPlane()
// function (see below) but at the same time increasing accuracy substantially.
@ -194,7 +164,6 @@ winding_accu_t *BaseWindingForPlaneAccu( const Plane3& plane ){
int x, i;
float max, v;
DoubleVector3 vright, vup, org;
winding_accu_t *w;
// One of the components of normal must have a magnitiude greater than this value,
// otherwise normal is not a unit vector. This is a little bit of inexpensive
@ -265,16 +234,12 @@ winding_accu_t *BaseWindingForPlaneAccu( const Plane3& plane ){
// outside the world. sqrt(262144^2 + 262144^2)/2 = 185363, which is greater than
// 113512.
w = AllocWindingAccu( 4 );
w->p[0] = org - vright + vup;
w->p[1] = org + vright + vup;
w->p[2] = org + vright - vup;
w->p[3] = org - vright - vup;
w->numpoints = 4;
return w;
return winding_accu_t{
org - vright + vup,
org + vright + vup,
org + vright - vup,
org - vright - vup
};
}
/*
@ -364,39 +329,17 @@ winding_t *CopyWinding( const winding_t *w ){
return void_ptr( memcpy( AllocWinding( w->numpoints ), w, offsetof_array( winding_t, p, w->numpoints ) ) );
}
/*
==================
CopyWindingAccuIncreaseSizeAndFreeOld
==================
*/
winding_accu_t *CopyWindingAccuIncreaseSizeAndFreeOld( winding_accu_t *w ){
if ( !w ) {
Error( "CopyWindingAccuIncreaseSizeAndFreeOld: winding is NULL" );
}
winding_accu_t *c = void_ptr( memcpy( AllocWindingAccu( w->numpoints + 1 ), w, offsetof_array( winding_accu_t, p, w->numpoints ) ) );
FreeWindingAccu( w );
return c;
}
/*
==================
CopyWindingAccuToRegular
==================
*/
winding_t *CopyWindingAccuToRegular( const winding_accu_t *w ){
int i;
winding_t *c;
if ( !w ) {
Error( "CopyWindingAccuToRegular: winding is NULL" );
}
c = AllocWinding( w->numpoints );
c->numpoints = w->numpoints;
for ( i = 0; i < c->numpoints; i++ )
winding_t *CopyWindingAccuToRegular( const winding_accu_t& w ){
winding_t *c = AllocWinding( w.size() );
c->numpoints = w.size();
for ( int i = 0; i < c->numpoints; i++ )
{
c->p[i] = w->p[i];
c->p[i] = w[i];
}
return c;
}
@ -553,15 +496,11 @@ void ClipWindingEpsilon( winding_t *in, const Plane3f& plane,
ChopWindingInPlaceAccu
=============
*/
void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float crudeEpsilon ){
winding_accu_t *in;
int counts[3];
int i, j;
void ChopWindingInPlaceAccu( winding_accu_t& inout, const Plane3& plane, float crudeEpsilon ){
size_t counts[3] = { 0 };
size_t i, j;
double dists[MAX_POINTS_ON_WINDING + 1];
EPlaneSide sides[MAX_POINTS_ON_WINDING + 1];
int maxpts;
winding_accu_t *f;
double w;
// We require at least a very small epsilon. It's a good idea for several reasons.
// First, we will be dividing by a potentially very small distance below. We don't
@ -598,12 +537,9 @@ void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float
static const double smallestEpsilonAllowed = ( (double) VEC_SMALLEST_EPSILON_AROUND_ONE ) * 0.5;
const double fineEpsilon = std::max( smallestEpsilonAllowed, (double) crudeEpsilon );
in = *inout;
counts[0] = counts[1] = counts[2] = 0;
for ( i = 0; i < in->numpoints; i++ )
for ( i = 0; i < inout.size(); i++ )
{
dists[i] = plane3_distance_to_point( plane, in->p[i] );
dists[i] = plane3_distance_to_point( plane, inout[i] );
if ( dists[i] > fineEpsilon ) {
sides[i] = eSideFront;
}
@ -622,8 +558,7 @@ void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float
// that we never get a case where two nearly equal planes result in 2 NULL windings
// due to the 'if' statement below. TODO: Investigate this.
if ( !counts[eSideFront] ) {
FreeWindingAccu( in );
*inout = NULL;
inout.clear();
return;
}
if ( !counts[eSideBack] ) {
@ -633,24 +568,18 @@ void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float
// NOTE: The least number of points that a winding can have at this point is 2.
// In that case, one point is SIDE_FRONT and the other is SIDE_BACK.
maxpts = counts[eSideFront] + 2; // We dynamically expand if this is too small.
f = AllocWindingAccu( maxpts );
winding_accu_t f;
f.reserve( counts[eSideFront] + 2 );
for ( i = 0; i < in->numpoints; i++ )
for ( i = 0; i < inout.size(); i++ )
{
const DoubleVector3& p1 = in->p[i];
const DoubleVector3& p1 = inout[i];
if ( sides[i] == eSideOn || sides[i] == eSideFront ) {
if ( f->numpoints >= MAX_POINTS_ON_WINDING ) {
if ( f.size() >= MAX_POINTS_ON_WINDING ) {
Error( "ChopWindingInPlaceAccu: MAX_POINTS_ON_WINDING" );
}
if ( f->numpoints >= maxpts ) { // This will probably never happen.
Sys_FPrintf( SYS_WRN | SYS_VRBflag, "WARNING: estimate on chopped winding size incorrect (no problem)\n" );
f = CopyWindingAccuIncreaseSizeAndFreeOld( f );
maxpts++;
}
f->p[f->numpoints] = p1;
f->numpoints++;
f.push_back( p1 );
if ( sides[i] == eSideOn ) {
continue;
}
@ -660,11 +589,11 @@ void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float
}
// Generate a split point.
const DoubleVector3& p2 = in->p[( ( i + 1 ) == in->numpoints ) ? 0 : ( i + 1 )];
const DoubleVector3& p2 = inout[( ( i + 1 ) == inout.size() ) ? 0 : ( i + 1 )];
// The divisor's absolute value is greater than the dividend's absolute value.
// w is in the range (0,1).
w = dists[i] / ( dists[i] - dists[i + 1] );
const double w = dists[i] / ( dists[i] - dists[i + 1] );
DoubleVector3 mid;
for ( j = 0; j < 3; j++ )
{
@ -679,20 +608,13 @@ void ChopWindingInPlaceAccu( winding_accu_t **inout, const Plane3& plane, float
mid[j] = p1[j] + ( w * ( p2[j] - p1[j] ) );
}
}
if ( f->numpoints >= MAX_POINTS_ON_WINDING ) {
if ( f.size() >= MAX_POINTS_ON_WINDING ) {
Error( "ChopWindingInPlaceAccu: MAX_POINTS_ON_WINDING" );
}
if ( f->numpoints >= maxpts ) { // This will probably never happen.
Sys_FPrintf( SYS_WRN | SYS_VRBflag, "WARNING: estimate on chopped winding size incorrect (no problem)\n" );
f = CopyWindingAccuIncreaseSizeAndFreeOld( f );
maxpts++;
}
f->p[f->numpoints] = mid;
f->numpoints++;
f.push_back( mid );
}
FreeWindingAccu( in );
*inout = f;
inout.swap( f );
}
/*

View File

@ -75,13 +75,8 @@ void pw( winding_t *w );
// in q3map2 brush processing.
///////////////////////////////////////////////////////////////////////////////////////
struct winding_accu_t
{
int numpoints;
DoubleVector3 p[];
};
using winding_accu_t = std::vector<DoubleVector3>;
winding_accu_t *BaseWindingForPlaneAccu( const Plane3& plane );
void ChopWindingInPlaceAccu( winding_accu_t **w, const Plane3& plane, float epsilon );
winding_t *CopyWindingAccuToRegular( const winding_accu_t *w );
void FreeWindingAccu( winding_accu_t *w );
winding_accu_t BaseWindingForPlaneAccu( const Plane3& plane );
void ChopWindingInPlaceAccu( winding_accu_t& w, const Plane3& plane, float epsilon );
winding_t *CopyWindingAccuToRegular( const winding_accu_t& w );

View File

@ -353,33 +353,25 @@ bool FixWinding( winding_t *w ){
if the some of the winding's points are close together.
==================
*/
bool FixWindingAccu( winding_accu_t *w ){
if ( w == NULL ) {
Error( "FixWindingAccu: NULL argument" );
}
bool FixWindingAccu( winding_accu_t& w ){
bool altered = false;
while ( true )
{
if ( w->numpoints < 2 ) {
if ( w.size() < 2 ) {
break; // Don't remove the only remaining point.
}
bool done = true;
for ( int i = w->numpoints - 1, j = 0; j < w->numpoints; i = j, ++j )
for ( winding_accu_t::iterator i = w.end() - 1, j = w.begin(); j != w.end(); i = j, ++j )
{
if ( vector3_length( w->p[i] - w->p[j] ) < DEGENERATE_EPSILON ) {
if ( vector3_length( *i - *j ) < DEGENERATE_EPSILON ) {
// TODO: I think the "snap weld vector" was written before
// some of the math precision fixes, and its purpose was
// probably to address math accuracy issues. We can think
// about changing the logic here. Maybe once plane distance
// gets 64 bits, we can look at it then.
w->p[i] = SnapWeldVectorAccu( w->p[i], w->p[j] );
for ( int k = j + 1; k < w->numpoints; k++ )
{
w->p[k - 1] = w->p[k];
}
w->numpoints--;
*i = SnapWeldVectorAccu( *i, *j );
w.erase( j );
altered = true;
// The only way to finish off fixing the winding consistently and
// accurately is by fixing the winding all over again. For example,
@ -407,11 +399,6 @@ bool FixWindingAccu( winding_accu_t *w ){
*/
bool CreateBrushWindings( brush_t *brush ){
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
winding_accu_t *w;
#else
winding_t *w;
#endif
/* walk the list of brush sides */
for ( int i = 0; i < brush->numsides; i++ )
{
@ -421,13 +408,13 @@ bool CreateBrushWindings( brush_t *brush ){
/* make huge winding */
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
w = BaseWindingForPlaneAccu( ( side.plane.normal() != g_vector3_identity )? side.plane : Plane3( plane.plane ) );
winding_accu_t w = BaseWindingForPlaneAccu( ( side.plane.normal() != g_vector3_identity )? side.plane : Plane3( plane.plane ) );
#else
w = BaseWindingForPlane( plane.plane );
winding_t *w = BaseWindingForPlane( plane.plane );
#endif
/* walk the list of brush sides */
for ( int j = 0; j < brush->numsides && w != NULL; j++ )
for ( int j = 0; j < brush->numsides && !w.empty(); j++ )
{
const side_t& cside = brush->sides[ j ];
const plane_t& cplane = mapplanes[ cside.planenum ^ 1 ];
@ -437,7 +424,7 @@ bool CreateBrushWindings( brush_t *brush ){
continue;
}
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
ChopWindingInPlaceAccu( &w, ( cside.plane.normal() != g_vector3_identity )? plane3_flipped( cside.plane ) : Plane3( cplane.plane ), 0 );
ChopWindingInPlaceAccu( w, ( cside.plane.normal() != g_vector3_identity )? plane3_flipped( cside.plane ) : Plane3( cplane.plane ), 0 );
#else
ChopWindingInPlace( &w, cplane.plane, 0 ); // CLIP_EPSILON );
#endif
@ -455,17 +442,8 @@ bool CreateBrushWindings( brush_t *brush ){
/* set side winding */
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
if ( w != NULL ) {
FixWindingAccu( w );
if ( w->numpoints < 3 ) {
FreeWindingAccu( w );
w = NULL;
}
}
side.winding = ( w ? CopyWindingAccuToRegular( w ) : NULL );
if ( w ) {
FreeWindingAccu( w );
}
FixWindingAccu( w );
side.winding = ( w.size() >= 3 ? CopyWindingAccuToRegular( w ) : NULL );
#else
side.winding = w;
#endif