mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-28 00:00:03 +02:00
* Q3MAP2_EXPERIMENTAL_OFFSET_WINDING_CREATION for higher windings precision
This commit is contained in:
@@ -124,6 +124,34 @@ Vector3 WindingCenter( const winding_t& w ){
|
||||
BaseWindingForPlaneAccu
|
||||
=================
|
||||
*/
|
||||
winding_accu_t BaseWindingForPlaneAccu( const Plane3& plane, const DoubleMinMax& minmax ){
|
||||
const DoubleVector3& n = plane.normal();
|
||||
const int z = vector3_max_abs_component_index( n );
|
||||
const int x = ( z == 2 )? 0 : z + 1;
|
||||
const int y = ( x == 2 )? 0 : x + 1;
|
||||
|
||||
if ( std::fabs( n[z] ) < 0.56 ) {
|
||||
Error( "BaseWindingForPlaneAccu: no dominant axis found because normal is too short" );
|
||||
}
|
||||
|
||||
// four points which are intersection of the plane with minmax edges
|
||||
DoubleVector3 a, b, c, d;
|
||||
a[x] = minmax.maxs[x];
|
||||
a[y] = minmax.maxs[y];
|
||||
a[z] = ( plane.dist() - a[x] * n[x] - a[y] * n[y] ) / n[z];
|
||||
b[x] = minmax.maxs[x];
|
||||
b[y] = minmax.mins[y];
|
||||
b[z] = ( plane.dist() - b[x] * n[x] - b[y] * n[y] ) / n[z];
|
||||
c[x] = minmax.mins[x];
|
||||
c[y] = minmax.mins[y];
|
||||
c[z] = ( plane.dist() - c[x] * n[x] - c[y] * n[y] ) / n[z];
|
||||
d[x] = minmax.mins[x];
|
||||
d[y] = minmax.maxs[y];
|
||||
d[z] = ( plane.dist() - d[x] * n[x] - d[y] * n[y] ) / n[z];
|
||||
|
||||
return ( plane.normal()[z] > 0 )? winding_accu_t{ a, b, c, d } : winding_accu_t{ d, c, b, a };
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -107,6 +107,7 @@ bool windings_intersect_coplanar( const winding_t& w1, const winding_t& w2, cons
|
||||
|
||||
using winding_accu_t = std::vector<DoubleVector3>;
|
||||
|
||||
winding_accu_t BaseWindingForPlaneAccu( const Plane3& plane, const DoubleMinMax& minmax );
|
||||
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 );
|
||||
|
||||
@@ -91,6 +91,7 @@ struct MinMax___
|
||||
};
|
||||
|
||||
using MinMax = MinMax___<float>;
|
||||
using DoubleMinMax = MinMax___<double>;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -217,11 +217,8 @@ static bool FixWinding( winding_t& w ){
|
||||
static bool FixWindingAccu( winding_accu_t& w ){
|
||||
bool altered = false;
|
||||
|
||||
while ( true )
|
||||
while ( w.size() > 1 ) // Don't remove the only remaining point.
|
||||
{
|
||||
if ( w.size() < 2 ) {
|
||||
break; // Don't remove the only remaining point.
|
||||
}
|
||||
bool done = true;
|
||||
for ( winding_accu_t::iterator i = w.end() - 1, j = w.begin(); j != w.end(); i = j, ++j )
|
||||
{
|
||||
@@ -252,7 +249,66 @@ static bool FixWindingAccu( winding_accu_t& w ){
|
||||
return altered;
|
||||
}
|
||||
|
||||
// Solve: n1·x = d1, n2·x = d2, n3·x = d3
|
||||
// Returns true if unique solution
|
||||
static bool solve3Planes( const Plane3 &p1, const Plane3 &p2, const Plane3 &p3, DoubleVector3 &out ){
|
||||
// Build matrix: rows = normals
|
||||
const double m[3][3] = { { p1.normal().x(), p1.normal().y(), p1.normal().z() },
|
||||
{ p2.normal().x(), p2.normal().y(), p2.normal().z() },
|
||||
{ p3.normal().x(), p3.normal().y(), p3.normal().z() } };
|
||||
const double b[3] = { p1.dist(), p2.dist(), p3.dist() };
|
||||
|
||||
// Cramer's rule
|
||||
const double det = m[0][0] * ( m[1][1] * m[2][2] - m[2][1] * m[1][2] ) -
|
||||
m[0][1] * ( m[1][0] * m[2][2] - m[2][0] * m[1][2] ) +
|
||||
m[0][2] * ( m[1][0] * m[2][1] - m[1][1] * m[2][0] );
|
||||
|
||||
if( std::fabs( det ) < 1e-9 )
|
||||
return false; // parallel or degenerate
|
||||
|
||||
// x = det(mx) / det
|
||||
const double mx[3][3] = { { b[0], m[0][1], m[0][2] },
|
||||
{ b[1], m[1][1], m[1][2] },
|
||||
{ b[2], m[2][1], m[2][2] } };
|
||||
const double det_x = mx[0][0] * ( mx[1][1] * mx[2][2] - mx[2][1] * mx[1][2] ) -
|
||||
mx[0][1] * ( mx[1][0] * mx[2][2] - mx[2][0] * mx[1][2] ) +
|
||||
mx[0][2] * ( mx[1][0] * mx[2][1] - mx[1][1] * mx[2][0] );
|
||||
// y
|
||||
const double my[3][3] = { { m[0][0], b[0], m[0][2] },
|
||||
{ m[1][0], b[1], m[1][2] },
|
||||
{ m[2][0], b[2], m[2][2] } };
|
||||
const double det_y = my[0][0] * ( my[1][1] * my[2][2] - my[2][1] * my[1][2] ) -
|
||||
my[0][1] * ( my[1][0] * my[2][2] - my[2][0] * my[1][2] ) +
|
||||
my[0][2] * ( my[1][0] * my[2][1] - my[1][1] * my[2][0] );
|
||||
// z
|
||||
const double mz[3][3] = { { m[0][0], m[0][1], b[0] },
|
||||
{ m[1][0], m[1][1], b[1] },
|
||||
{ m[2][0], m[2][1], b[2] } };
|
||||
const double det_z = mz[0][0] * ( mz[1][1] * mz[2][2] - mz[2][1] * mz[1][2] ) -
|
||||
mz[0][1] * ( mz[1][0] * mz[2][2] - mz[2][0] * mz[1][2] ) +
|
||||
mz[0][2] * ( mz[1][0] * mz[2][1] - mz[1][1] * mz[2][0] );
|
||||
out.x() = det_x / det;
|
||||
out.y() = det_y / det;
|
||||
out.z() = det_z / det;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DoubleMinMax brushMinMaxFromPlanes( const std::vector<Plane3>& planes ){
|
||||
DoubleMinMax minmax;
|
||||
|
||||
for ( auto i = planes.cbegin(), end = planes.cend(); i != end; ++i )
|
||||
for ( auto j = i + 1; j != end; ++j )
|
||||
for ( auto k = j + 1; k != end; ++k )
|
||||
if( DoubleVector3 v; solve3Planes( *i, *j, *k, v ) )
|
||||
// Validate against ALL planes
|
||||
if( std::ranges::all_of( planes, [&]( const Plane3& plane ){ return plane3_distance_to_point( plane, v ) < ON_EPSILON; } ) )
|
||||
minmax.extend( v );
|
||||
|
||||
return minmax;
|
||||
}
|
||||
|
||||
#define Q3MAP2_EXPERIMENTAL_OFFSET_WINDING_CREATION 1
|
||||
/*
|
||||
CreateBrushWindings()
|
||||
makes basewindigs for sides and mins/maxs for the brush
|
||||
@@ -260,17 +316,43 @@ static bool FixWindingAccu( winding_accu_t& w ){
|
||||
*/
|
||||
|
||||
bool CreateBrushWindings( brush_t& brush ){
|
||||
std::vector<Plane3> planes;
|
||||
planes.reserve( brush.sides.size() );
|
||||
|
||||
for( const side_t& side : brush.sides ){
|
||||
ENSURE( !side.bevel );
|
||||
planes.push_back( ( side.plane.normal() != g_vector3_identity )? side.plane : Plane3( mapplanes[ side.planenum ].plane ) );
|
||||
}
|
||||
#if Q3MAP2_EXPERIMENTAL_OFFSET_WINDING_CREATION
|
||||
DoubleMinMax minmax = brushMinMaxFromPlanes( planes );
|
||||
|
||||
if( !minmax.valid() )
|
||||
return false;
|
||||
|
||||
const DoubleVector3 offset = minmax.origin();
|
||||
minmax.maxs -= offset;
|
||||
minmax.mins -= offset;
|
||||
for( Plane3& p : planes )
|
||||
p = plane3_translated( plane3_flipped( p ), -offset ); // flip for clipping
|
||||
#else
|
||||
for( Plane3& p : planes )
|
||||
p = plane3_flipped( p );
|
||||
#endif
|
||||
/* walk the list of brush sides */
|
||||
for ( size_t i = 0; i < brush.sides.size(); ++i )
|
||||
{
|
||||
/* get side and plane */
|
||||
side_t& side = brush.sides[ i ];
|
||||
const plane_t& plane = mapplanes[ side.planenum ];
|
||||
|
||||
/* make huge winding */
|
||||
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
|
||||
winding_accu_t w = BaseWindingForPlaneAccu( ( side.plane.normal() != g_vector3_identity )? side.plane : Plane3( plane.plane ) );
|
||||
#if Q3MAP2_EXPERIMENTAL_OFFSET_WINDING_CREATION
|
||||
winding_accu_t w = BaseWindingForPlaneAccu( plane3_flipped( planes[ i ] ), minmax );
|
||||
#else
|
||||
winding_accu_t w = BaseWindingForPlaneAccu( plane3_flipped( planes[ i ] ) );
|
||||
#endif
|
||||
#else
|
||||
const plane_t& plane = mapplanes[ side.planenum ];
|
||||
winding_t w = BaseWindingForPlane( plane.plane );
|
||||
#endif
|
||||
|
||||
@@ -278,15 +360,14 @@ bool CreateBrushWindings( brush_t& brush ){
|
||||
for ( size_t j = 0; j < brush.sides.size() && !w.empty(); ++j )
|
||||
{
|
||||
const side_t& cside = brush.sides[ j ];
|
||||
const plane_t& cplane = mapplanes[ cside.planenum ^ 1 ];
|
||||
if ( i == j
|
||||
|| cside.planenum == ( side.planenum ^ 1 ) /* back side clipaway */
|
||||
|| cside.bevel ) {
|
||||
|| cside.planenum == ( side.planenum ^ 1 ) ) { /* back side clipaway */
|
||||
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, planes[ j ], 0 );
|
||||
#else
|
||||
const plane_t& cplane = mapplanes[ cside.planenum ^ 1 ];
|
||||
ChopWindingInPlace( w, cplane.plane, 0 ); // CLIP_EPSILON );
|
||||
#endif
|
||||
|
||||
@@ -304,10 +385,16 @@ bool CreateBrushWindings( brush_t& brush ){
|
||||
/* set side winding */
|
||||
#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
|
||||
FixWindingAccu( w );
|
||||
if( w.size() >= 3 )
|
||||
if( w.size() >= 3 ){
|
||||
#if Q3MAP2_EXPERIMENTAL_OFFSET_WINDING_CREATION
|
||||
for( DoubleVector3& v : w )
|
||||
v += offset;
|
||||
#endif
|
||||
side.winding = CopyWindingAccuToRegular( w );
|
||||
else
|
||||
}
|
||||
else{
|
||||
side.winding.clear();
|
||||
}
|
||||
#else
|
||||
side.winding.swap( w );
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user