From bee863ee9bb8d188031d0e421e94ae387867dfdf Mon Sep 17 00:00:00 2001 From: Garux Date: Mon, 10 Nov 2025 15:58:09 +0500 Subject: [PATCH] refactor model autoclip --- tools/quake3/q3map2/brush.cpp | 4 +- tools/quake3/q3map2/facebsp.cpp | 2 +- tools/quake3/q3map2/map.cpp | 64 ++-- tools/quake3/q3map2/model.cpp | 506 +++++++++++--------------------- tools/quake3/q3map2/q3map2.h | 7 +- tools/quake3/q3map2/surface.cpp | 6 +- 6 files changed, 209 insertions(+), 380 deletions(-) diff --git a/tools/quake3/q3map2/brush.cpp b/tools/quake3/q3map2/brush.cpp index fada6a8b..2f830395 100644 --- a/tools/quake3/q3map2/brush.cpp +++ b/tools/quake3/q3map2/brush.cpp @@ -333,10 +333,10 @@ static brush_t BrushFromBounds( const Vector3& mins, const Vector3& maxs ){ for ( int i = 0; i < 3; ++i ) { float dist = maxs[i]; - b.sides[i].planenum = FindFloatPlane( g_vector3_axes[i], dist, 1, &maxs ); + b.sides[i].planenum = FindFloatPlane( g_vector3_axes[i], dist, Span( &maxs, 1 ) ); dist = -mins[i]; - b.sides[3 + i].planenum = FindFloatPlane( -g_vector3_axes[i], dist, 1, &mins ); + b.sides[3 + i].planenum = FindFloatPlane( -g_vector3_axes[i], dist, Span( &mins, 1 ) ); } CreateBrushWindings( b ); diff --git a/tools/quake3/q3map2/facebsp.cpp b/tools/quake3/q3map2/facebsp.cpp index ddaffb0f..aa5a5d04 100644 --- a/tools/quake3/q3map2/facebsp.cpp +++ b/tools/quake3/q3map2/facebsp.cpp @@ -61,7 +61,7 @@ static void SelectSplitPlaneNum( const node_t *node, const facelist_t& list, int } const float dist = blockSize[ i ] * ( floor( node->minmax.mins[ i ] / blockSize[ i ] ) + 1 ); if ( node->minmax.maxs[ i ] > dist ) { - *splitPlaneNum = FindFloatPlane( g_vector3_axes[i], dist, 0, nullptr ); + *splitPlaneNum = FindFloatPlane( g_vector3_axes[i], dist, {} ); return; } } diff --git a/tools/quake3/q3map2/map.cpp b/tools/quake3/q3map2/map.cpp index aba6766e..8cdf8554 100644 --- a/tools/quake3/q3map2/map.cpp +++ b/tools/quake3/q3map2/map.cpp @@ -299,16 +299,16 @@ static void SnapPlane( Plane3f& plane ){ SnapPlaneImproved() snaps a plane to normal/distance epsilons, improved code */ -static void SnapPlaneImproved( Plane3f& plane, int numPoints, const Vector3 *points ){ +void SnapPlaneImproved( Plane3f& plane, const Span& points ){ if ( SnapNormal( plane.normal() ) ) { - if ( numPoints > 0 ) { + if ( !points.empty() ) { // Adjust the dist so that the provided points don't drift away. DoubleVector3 center( 0 ); - for ( const Vector3& point : Span( points, numPoints ) ) + for ( const Vector3& point : points ) { center += point; } - center /= numPoints; + center /= points.size(); plane.dist() = vector3_dot( plane.normal(), center ); } } @@ -331,14 +331,14 @@ static void SnapPlaneImproved( Plane3f& plane, int numPoints, const Vector3 *poi must be within an epsilon distance of the plane */ -int FindFloatPlane( const Plane3f& inplane, int numPoints, const Vector3 *points ) // NOTE: this has a side effect on the normal. Good or bad? +int FindFloatPlane( const Plane3f& inplane, const Span& points ) // NOTE: this has a side effect on the normal. Good or bad? #ifdef USE_HASHING { Plane3f plane( inplane ); #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX - SnapPlaneImproved( plane, numPoints, points ); + SnapPlaneImproved( plane, points ); #else SnapPlane( plane ); #endif @@ -362,24 +362,17 @@ int FindFloatPlane( const Plane3f& inplane, int numPoints, const Vector3 *points //% return p - mapplanes; /* ydnar: test supplied points against this plane */ - int j; - for ( j = 0; j < numPoints; ++j ) - { + if( std::ranges::all_of( points, [&]( const Vector3& point ){ // true for empty // NOTE: When dist approaches 2^16, the resolution of 32 bit floating // point number is greatly decreased. The distanceEpsilon cannot be // very small when world coordinates extend to 2^16. Making the // dot product here in 64 bit land will not really help the situation // because the error will already be carried in dist. - const double d = std::fabs( plane3_distance_to_point( p.plane, points[ j ] ) ); - if ( d != 0 && d >= distanceEpsilon ) { - break; // Point is too far from plane. - } - } - - /* found a matching plane */ - if ( j >= numPoints ) { - return pidx; - } + const double d = std::fabs( plane3_distance_to_point( p.plane, point ) ); + //% if( d > 0.2 ) Sys_Warning( "plane3_distance_to_point( p.plane, point ) %f\n", d ); + return d == 0 || d < distanceEpsilon; // Point is not too far from plane. + } ) ) + return pidx; /* found a matching plane */ } } @@ -390,16 +383,16 @@ int FindFloatPlane( const Plane3f& inplane, int numPoints, const Vector3 *points #else { - int i, j; + int i; plane_t *p; - Plane3f plane( innormal, dist ); + Plane3f plane( inplane ); #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX - SnapPlaneImproved( plane, numPoints, points ); + SnapPlaneImproved( plane, points ); #else SnapPlane( plane ); #endif - for ( i = 0, p = mapplanes; i < nummapplanes; ++i, ++p ) + for ( i = 0, p = mapplanes.data(); i < mapplanes.size(); ++i, ++p ) { if ( !PlaneEqual( *p, plane ) ) { continue; @@ -409,17 +402,11 @@ int FindFloatPlane( const Plane3f& inplane, int numPoints, const Vector3 *points //% return i; /* ydnar: test supplied points against this plane */ - for ( j = 0; j < numPoints; ++j ) - { - if ( std::fabs( plane3_distance_to_point( p->plane, points[ j ] ) ) > distanceEpsilon ) { - break; - } - } + if( std::ranges::all_of( points, [&]( const Vector3& point ){ // true for empty + return std::fabs( plane3_distance_to_point( p->plane, point ) ) <= distanceEpsilon; + } ) ) + return i; /* found a matching plane */ - /* found a matching plane */ - if ( j >= numPoints ) { - return i; - } // TODO: Note that the non-USE_HASHING code does not compute epsilons // for the provided points. It should do that. I think this code // is unmaintained because nobody sets USE_HASHING to off. @@ -445,11 +432,12 @@ inline int MapPlaneFromPoints( DoubleVector3 p[3] ){ // if the plane is 2^16 units away from the origin (the "epsilon" approaches // 0.01 in that case). const Vector3 points[3] = { p[0], p[1], p[2] }; - return FindFloatPlane( Plane3f( plane ), 3, points ); + return FindFloatPlane( Plane3f( plane ), points ); #else Plane3f plane; PlaneFromPoints( plane, p ); - return FindFloatPlane( plane, 3, p ); + const Vector3 points[3] = { p[0], p[1], p[2] }; + return FindFloatPlane( plane, points ); #endif } @@ -627,7 +615,7 @@ void AddBrushBevels(){ } } - s.planenum = FindFloatPlane( plane, 0, nullptr ); + s.planenum = FindFloatPlane( plane, {} ); s.contentFlags = sides[ 0 ].contentFlags; /* handle bevel surfaceflags */ for ( const side_t& side : sides ) { @@ -739,7 +727,7 @@ void AddBrushBevels(){ } side_t& s2 = sides.emplace_back(); - s2.planenum = FindFloatPlane( plane, 1, &sides[i].winding[ j ] ); + s2.planenum = FindFloatPlane( plane, Span( &sides[i].winding[ j ], 1 ) ); s2.contentFlags = sides[0].contentFlags; s2.surfaceFlags = ( sides[i].surfaceFlags & surfaceFlagsMask ); /* handle bevel surfaceflags */ s2.bevel = true; @@ -1229,7 +1217,7 @@ static void AdjustBrushesForOrigin( entity_t& ent ){ const float newdist = -plane3_distance_to_point( mapplanes[ side.planenum ].plane, ent.originbrush_origin ); /* find a new plane */ - side.planenum = FindFloatPlane( mapplanes[ side.planenum ].normal(), newdist, 0, nullptr ); + side.planenum = FindFloatPlane( mapplanes[ side.planenum ].normal(), newdist, {} ); side.plane.dist() = -plane3_distance_to_point( side.plane, ent.originbrush_origin ); } diff --git a/tools/quake3/q3map2/model.cpp b/tools/quake3/q3map2/model.cpp index d5253250..6fcd6de0 100644 --- a/tools/quake3/q3map2/model.cpp +++ b/tools/quake3/q3map2/model.cpp @@ -370,52 +370,81 @@ enum EModelFlags{ eClipFlags = eClipModel | eExtrudeFaceNormals | eExtrudeTerrain | eExtrudeVertexNormals | ePyramidaClip | eExtrudeDownwards | eExtrudeUpwards | eMaxExtrude | eAxialBackplane, }; -inline void nonax_clip_dbg( const Plane3f (&p)[3] ){ -#if 0 - for ( int j = 0; j < 3; ++j ){ - for ( int k = 0; k < 3; ++k ){ - const Vector3& n = p[j].normal(); - if ( std::fabs( n[k] ) < 0.00025f && n[k] != 0 ){ - Sys_Printf( "nonax nrm %6.17f %6.17f %6.17f\n", n[0], n[1], n[2] ); - } - } - } -#endif -} - inline size_t normal_make_axial( Vector3& normal ){ const size_t i = vector3_max_abs_component_index( normal ); normal = normal[i] >= 0? g_vector3_axes[i] : -g_vector3_axes[i]; return i; } -template // N = 4 or 5 -static void make_brush_sides( const Plane3f plane, const Plane3f (&p)[3], const Plane3f& reverse, Vector3 (&points)[4], shaderInfo_t& si ){ - /* set up brush sides */ - buildBrush.sides.clear(); // clear, so resize() will value-initialize elements - buildBrush.sides.resize( N ); +struct ClipSides +{ + Plane3f fplane; // front plane + winding_t fw; // front winding + Plane3f bplane{ 0, 0, 0, 0 }; // back plane, present if != 0 + winding_t bw; // back winding + std::vector splanes; // side planes, using fw[i], fw[i + 1] points, size = fw.size - if( debugClip ){ - buildBrush.sides[ 0 ].shaderInfo = &ShaderInfoForShader( "debugclip2" ); - for ( size_t i = 1; i < N; ++i ) - buildBrush.sides[i].shaderInfo = &ShaderInfoForShader( "debugclip" ); - } - else{ - buildBrush.sides[0].shaderInfo = &si; - buildBrush.sides[0].surfaceFlags = si.surfaceFlags; - for ( size_t i = 1; i < N; ++i ) - buildBrush.sides[i].shaderInfo = nullptr; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works + /* construct front and allocates sides, requires fw */ + bool construct(){ + if( !PlaneFromPoints( fplane, fw.data() ) ) + return false; + + // snap points before using them for further calculations + // precision suffers a lot, when two of normal values are under .00025 (often no collision, knocking up effect in ioq3) + // also broken drawsurfs in case of normal brushes + // ? worth to snap nearly axial edges (or on nearly axial plane) beforehand or SnapPlaneImproved is nuff good for sides + const Plane3f pln = fplane; + SnapPlaneImproved( fplane, fw ); + if( pln.normal() != fplane.normal() || pln.dist() != fplane.dist() ){ + for( Vector3& v : fw ){ + v = plane3_project_point( fplane, v ); + } + } + + splanes.resize( fw.size() ); + + /* sanity check */ + if ( triangle_min_angle_squared_sin( fw[0], fw[1], fw[2] ) < 1e-8 ) // degenerate triangle + return false; + + return true; } - points[3] = points[0]; // for cyclic usage + void add_back_plane( float clipDepth, const Vector3& bestNormal ){ + bplane = plane3_flipped( fplane ); + bplane.dist() += vector3_dot( bestNormal, fplane.normal() ) * clipDepth; + bw = fw; + for( Vector3& v : bw ) + v -= bestNormal * clipDepth; + } + + void make_brush_sides( shaderInfo_t& si ) const { + const bool doBack = bplane.normal() != g_vector3_identity; + /* set up brush sides */ + buildBrush.sides.clear(); // clear, so resize() will value-initialize elements + buildBrush.sides.resize( splanes.size() + 1 + doBack ); + + if( debugClip ){ + buildBrush.sides[0].shaderInfo = &ShaderInfoForShader( "debugclip2" ); + for ( size_t i = 1; i < buildBrush.sides.size(); ++i ) + buildBrush.sides[i].shaderInfo = &ShaderInfoForShader( "debugclip" ); + } + else{ + buildBrush.sides[0].shaderInfo = &si; + buildBrush.sides[0].surfaceFlags = si.surfaceFlags; + for ( size_t i = 1; i < buildBrush.sides.size(); ++i ) + buildBrush.sides[i].shaderInfo = nullptr; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works + } + + buildBrush.sides[0].planenum = FindFloatPlane( fplane, fw ); + for( size_t i = 0; i < splanes.size(); ++i ){ + buildBrush.sides[i + 1].planenum = FindFloatPlane( splanes[i], std::array{ fw[i], fw[winding_next( fw, i )] } ); + } + if( doBack ) + buildBrush.sides.back().planenum = FindFloatPlane( bplane, bw ); + } +}; - buildBrush.sides[0].planenum = FindFloatPlane( plane, 3, points ); - buildBrush.sides[1].planenum = FindFloatPlane( p[0], 2, &points[0] ); // p[0] contains points[0] and points[1] - buildBrush.sides[2].planenum = FindFloatPlane( p[1], 2, &points[1] ); // p[1] contains points[1] and points[2] - buildBrush.sides[3].planenum = FindFloatPlane( p[2], 2, &points[2] ); // p[2] contains points[2] and points[0] (copied to points[3]) - if constexpr( N == 5 ) - buildBrush.sides[4].planenum = FindFloatPlane( reverse, 0, nullptr ); -} static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const mapDrawSurface_t& ds, const char *modelName, entity_t& entity ){ const int spf = ( spawnFlags & ( eClipFlags & ~eClipModel ) ); @@ -442,18 +471,14 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const || spf == ( eExtrudeDownwards | eExtrudeUpwards | eAxialBackplane ) || spf == ( eExtrudeUpwards | eMaxExtrude ) || spf == ( eExtrudeUpwards | eAxialBackplane ) ){ - int j, k; - //int ok=0, notok=0; float limDepth = 0; if ( clipDepth < 0 ){ limDepth = -clipDepth; - clipDepth = 2.0; + clipDepth = 2.f; } - Vector3 points[ 4 ]; - Plane3f plane, reverse, p[3]; MinMax minmax; Vector3 avgDirection( 0 ); - int axis; + size_t axis; /* temp hack */ if ( !si.clipModel && !( si.compileFlags & C_SOLID ) ) { @@ -467,18 +492,17 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const if ( ( spf & eMaxExtrude ) || ( spf & eExtrudeTerrain ) ){ - for ( auto i = ds.indexes.cbegin(); i != ds.indexes.cend(); i += 3 ){ - for ( j = 0; j < 3; ++j ){ - points[j] = ds.verts[ *( i + j ) ].xyz; - } + for ( auto idx = ds.indexes.cbegin(); idx != ds.indexes.cend(); idx += 3 ){ + const Vector3 points[3]{ ds.verts[*( idx + 0 )].xyz, + ds.verts[*( idx + 1 )].xyz, + ds.verts[*( idx + 2 )].xyz }; + Plane3f plane; if ( PlaneFromPoints( plane, points ) ){ if ( spf & eExtrudeTerrain ) - avgDirection += plane.normal(); //calculate average mesh facing direction + avgDirection += plane.normal(); // calculate average mesh facing direction - //get min/max - for ( j = 0; j < 3; ++j ){ - minmax.extend( points[j] ); - } + for ( const auto& p : points ) // get min/max + minmax.extend( p ); } } //unify avg direction @@ -498,143 +522,63 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const buildBrush.detail = true; /* walk triangle list */ - for ( auto i = ds.indexes.cbegin(); i != ds.indexes.cend(); i += 3 ){ + for ( auto idx = ds.indexes.cbegin(); idx != ds.indexes.cend(); idx += 3 ){ + ClipSides cs; /* make points */ - for ( j = 0; j < 3; ++j ){ + for ( int i = 0; i < 3; ++i ){ /* copy xyz */ - points[j] = ds.verts[ *( i + j ) ].xyz; + cs.fw.push_back( ds.verts[*( idx + i )].xyz ); } /* make plane for triangle */ - if ( PlaneFromPoints( plane, points ) ) { - //snap points before using them for further calculations - //precision suffers a lot, when two of normal values are under .00025 (often no collision, knocking up effect in ioq3) - //also broken drawsurfs in case of normal brushes - bool snpd = false; - for ( j = 0; j < 3; ++j ) - { - if ( std::fabs( plane.normal()[j] ) < 0.00025f && std::fabs( plane.normal()[( j + 1) % 3] ) < 0.00025f - && ( plane.normal()[j] != 0 || plane.normal()[( j + 1 ) % 3] != 0 ) ){ - const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; - points[0][( j + 2 ) % 3] = points[1][( j + 2 ) % 3] = points[2][( j + 2 ) % 3] = cnt[( j + 2 ) % 3]; - snpd = true; - break; - } - } - - //snap pairs of points to prevent bad side planes - for ( j = 0; j < 3; ++j ) - { - const Vector3 nrm = VectorNormalized( points[j] - points[( j + 1 ) % 3] ); - for ( k = 0; k < 3; ++k ) - { - if ( nrm[k] != 0 && std::fabs( nrm[k] ) < 0.00025f ){ - //Sys_Printf( "b4(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); - points[j][k] = points[( j + 1 ) % 3][k] = ( points[j][k] + points[( j + 1 ) % 3][k] ) / 2.0; - //Sys_Printf( "sn(%6.6f %6.6f %6.6f)(%6.6f %6.6f %6.6f)\n", points[j][0], points[j][1], points[j][2], points[(j+1)%3][0], points[(j+1)%3][1], points[(j+1)%3][2] ); - snpd = true; - } - } - } - - if ( snpd ) { - PlaneFromPoints( plane, points ); - snpd = false; - } - - //vector-is-close-to-be-on-axis check again, happens after previous code sometimes - for ( j = 0; j < 3; ++j ) - { - if ( std::fabs( plane.normal()[j] ) < 0.00025f && std::fabs( plane.normal()[( j + 1 ) % 3] ) < 0.00025f - && ( plane.normal()[j] != 0 || plane.normal()[( j + 1 ) % 3] != 0 ) ){ - const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; - points[0][( j + 2 ) % 3] = points[1][( j + 2 ) % 3] = points[2][( j + 2 ) % 3] = cnt[( j + 2 ) % 3]; - PlaneFromPoints( plane, points ); - break; - } - } - - //snap single snappable normal components - for ( j = 0; j < 3; ++j ) - { - if ( plane.normal()[j] != 0 && std::fabs( plane.normal()[j] ) < 0.00005f ){ - plane.normal()[j] = 0; - snpd = true; - } - } - - //adjust plane dist - if ( snpd ) { - const Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; - VectorNormalize( plane.normal() ); - plane.dist() = vector3_dot( plane.normal(), cnt ); - - //project points to resulting plane to keep intersections precision - for ( j = 0; j < 3; ++j ) - { - //Sys_Printf( "b4 %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); - points[j] = plane3_project_point( plane, points[j] ); - //Sys_Printf( "sn %i (%6.7f %6.7f %6.7f)\n", j, points[j][0], points[j][1], points[j][2] ); - } - //Sys_Printf( "sn pln (%6.7f %6.7f %6.7f %6.7f)\n", plane.a, plane.b, plane.c, plane.d ); - //PlaneFromPoints( plane, points ); - //Sys_Printf( "pts pln (%6.7f %6.7f %6.7f %6.7f)\n", plane.a, plane.b, plane.c, plane.d ); - } - - /* sanity check */ - if ( triangle_min_angle_squared_sin( points[0], points[1], points[2] ) < 1e-8 ) { - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped: points on line\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], modelName ); - continue; - } - + if ( cs.construct() ) { if ( spf == ( ePyramidaClip | eAxialBackplane ) ){ // pyramid with 3 of 4 sides axial (->small bsp) - for ( j = 0; j < 3; ++j ) - if ( std::fabs( plane.normal()[j] ) < 0.05f && std::fabs( plane.normal()[( j + 1 ) % 3] ) < 0.05f ) //no way, close to lay on two axes + for ( int i = 0; i < 3; ++i ) + if ( std::fabs( cs.fplane.normal()[i] ) < 0.05f && std::fabs( cs.fplane.normal()[( i + 1 ) % 3] ) < 0.05f ) //no way, close to lay on two axes goto default_CLIPMODEL; // best axial normal - Vector3 bestNormal = plane.normal(); + Vector3 bestNormal = cs.fplane.normal(); axis = normal_make_axial( bestNormal ); float mindist = 999999; - for ( j = 0; j < 3; ++j ){ // planes + for ( size_t i = 0; i < cs.fw.size(); ++i ){ // planes float bestdist = 999999, bestangle = 1; - for ( k = 0; k < 3; ++k ){ // axes - Vector3 nrm = points[( j + 1 ) % 3] - points[j]; - if ( k == axis ){ - reverse.normal() = vector3_cross( bestNormal, nrm ); + for ( size_t j = 0; j < 3; ++j ){ // axes + Plane3f pln; + Vector3 nrm = cs.fw[winding_next( cs.fw, i )] - cs.fw[i]; + if ( j == axis ){ + pln.normal() = VectorNormalized( vector3_cross( bestNormal, nrm ) ); } else{ Vector3 vnrm( 0 ); - if ( ( k + 1 ) % 3 == axis ){ - if ( nrm[( k + 2 ) % 3] == 0 ) + if ( ( j + 1 ) % 3 == axis ){ + if ( nrm[( j + 2 ) % 3] == 0 ) continue; - vnrm[( k + 2 ) % 3] = nrm[( k + 2 ) % 3]; + vnrm[( j + 2 ) % 3] = nrm[( j + 2 ) % 3]; } else{ - if ( nrm[( k + 1 ) % 3] == 0 ) + if ( nrm[( j + 1 ) % 3] == 0 ) continue; - vnrm[( k + 1 ) % 3] = nrm[( k + 1 ) % 3]; + vnrm[( j + 1 ) % 3] = nrm[( j + 1 ) % 3]; } const Vector3 enrm = vector3_cross( bestNormal, vnrm ); - reverse.normal() = vector3_cross( enrm, nrm ); + pln.normal() = VectorNormalized( vector3_cross( enrm, nrm ) ); } - VectorNormalize( reverse.normal() ); - reverse.dist() = vector3_dot( points[ j ], reverse.normal() ); + pln.dist() = vector3_dot( cs.fw[i], pln.normal() ); //check facing, thickness - const float currdist = reverse.dist() - vector3_dot( reverse.normal(), points[( j + 2 ) % 3] ); - const float currangle = vector3_dot( reverse.normal(), plane.normal() ); + const float currdist = -plane3_distance_to_point( pln, cs.fw[( i + 2 ) % cs.fw.size()] ); + const float currangle = vector3_dot( pln.normal(), cs.fplane.normal() ); if ( ( ( currdist > 0.1 ) && ( currdist < bestdist ) && ( currangle < 0 ) ) || ( ( currangle >= 0 ) && ( currangle <= bestangle ) ) ){ bestangle = currangle; if ( currangle < 0 ) bestdist = currdist; - p[j] = reverse; + cs.splanes[i] = pln; } } if ( bestdist == 999999 && bestangle == 1 ){ @@ -646,12 +590,9 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const if ( ( limDepth != 0 ) && ( mindist > limDepth ) ) goto default_CLIPMODEL; - nonax_clip_dbg( p ); - - make_brush_sides<4>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } - else if ( spf == eExtrudeTerrain || spf == eExtrudeDownwards || spf == eExtrudeUpwards @@ -674,7 +615,7 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const else{ axis = 2; if ( ( spf & eExtrudeDownwards ) && ( spf & eExtrudeUpwards ) ){ - bestNormal = plane.normal()[2] >= 0? g_vector3_axis_z : -g_vector3_axis_z; + bestNormal = cs.fplane.normal().z() >= 0? g_vector3_axis_z : -g_vector3_axis_z; } else if ( spf & eExtrudeDownwards ){ bestNormal = g_vector3_axis_z; @@ -683,134 +624,105 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const bestNormal = -g_vector3_axis_z; } else{ // best axial normal - bestNormal = plane.normal(); + bestNormal = cs.fplane.normal(); axis = normal_make_axial( bestNormal ); } } - if ( vector3_dot( plane.normal(), bestNormal ) < 0.05 ){ + if ( vector3_dot( cs.fplane.normal(), bestNormal ) < 0.05 ){ goto default_CLIPMODEL; } /* make side planes */ - for ( j = 0; j < 3; ++j ) + for ( size_t i = 0; i < cs.fw.size(); ++i ) { - p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[( j + 1 ) % 3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); + cs.splanes[i].normal() = VectorNormalized( vector3_cross( bestNormal, cs.fw[winding_next( cs.fw, i )] - cs.fw[i] ) ); + cs.splanes[i].dist() = vector3_dot( cs.fw[i], cs.splanes[i].normal() ); } /* make back plane */ if ( spf & eMaxExtrude ){ //max extrude - reverse.normal() = -bestNormal; - if ( bestNormal[axis] > 0 ){ - reverse.dist() = -minmax.mins[axis] + clipDepth; - } - else{ - reverse.dist() = minmax.maxs[axis] + clipDepth; - } + cs.bplane.normal() = -bestNormal; + if ( bestNormal[axis] > 0 ) + cs.bplane.dist() = -minmax.mins[axis] + clipDepth; + else + cs.bplane.dist() = minmax.maxs[axis] + clipDepth; } else if ( spf & eAxialBackplane ){ //axial backplane - reverse.normal() = -bestNormal; - reverse.dist() = points[0][axis]; + cs.bplane.normal() = -bestNormal; + cs.bplane.dist() = cs.fw[0][axis]; if ( bestNormal[axis] > 0 ){ - for ( j = 1; j < 3; ++j ){ - value_minimize( reverse.dist(), points[j][axis] ); + for ( size_t i = 1; i < cs.fw.size(); ++i ){ + value_minimize( cs.bplane.dist(), cs.fw[i][axis] ); } - reverse.dist() = -reverse.dist() + clipDepth; + cs.bplane.dist() = -cs.bplane.dist() + clipDepth; } else{ - for ( j = 1; j < 3; ++j ){ - value_maximize( reverse.dist(), points[j][axis] ); + for ( size_t i = 1; i < cs.fw.size(); ++i ){ + value_maximize( cs.bplane.dist(), cs.fw[i][axis] ); } - reverse.dist() += clipDepth; + cs.bplane.dist() += clipDepth; } if ( limDepth != 0 ){ - Vector3 cnt = points[0]; + Vector3 cnt = cs.fw[0]; if ( bestNormal[axis] > 0 ){ - for ( j = 1; j < 3; ++j ){ - if ( points[j][axis] > cnt[axis] ){ - cnt = points[j]; - } - } + for ( size_t i = 1; i < cs.fw.size(); ++i ) + if ( cs.fw[i][axis] > cnt[axis] ) + cnt = cs.fw[i]; } else { - for ( j = 1; j < 3; ++j ){ - if ( points[j][axis] < cnt[axis] ){ - cnt = points[j]; - } - } + for ( size_t i = 1; i < cs.fw.size(); ++i ) + if ( cs.fw[i][axis] < cnt[axis] ) + cnt = cs.fw[i]; } - cnt = plane3_project_point( reverse, cnt ); - if ( -plane3_distance_to_point( plane, cnt ) > limDepth ){ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; + cnt = plane3_project_point( cs.bplane, cnt ); + if ( -plane3_distance_to_point( cs.fplane, cnt ) > limDepth ){ //normal backplane + cs.add_back_plane( clipDepth, cs.fplane.normal() ); } } } else{ //normal backplane - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; + cs.add_back_plane( clipDepth, cs.fplane.normal() ); } - nonax_clip_dbg( p ); - - make_brush_sides<5>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } - else if ( spf == ( eExtrudeFaceNormals | ePyramidaClip ) ){ // extrude 45 - //45 degrees normals for side planes - for ( j = 0; j < 3; ++j ) + /* 45 degrees normals for side planes */ + for ( size_t i = 0; i < cs.fw.size(); ++i ) { - const Vector3 nrm = points[( j + 1 ) % 3] - points[ j ]; - Vector3 enrm = VectorNormalized( vector3_cross( plane.normal(), nrm ) ); - enrm += plane.normal(); - VectorNormalize( enrm ); + const Vector3 enrm = VectorNormalized( vector3_cross( cs.fplane.normal(), cs.fw[winding_next( cs.fw, i )] - cs.fw[i] ) ); /* make side planes */ - p[j].normal() = VectorNormalized( vector3_cross( enrm, nrm ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; ++k ) - { - if ( std::fabs( p[j].normal()[k] ) < 0.00025f && p[j].normal()[k] != 0 ){ - p[j].normal()[k] = 0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - p[j].dist() = vector3_dot( ( points[j] + points[( j + 1 ) % 3] ) / 2.0, p[j].normal() ); - } + cs.splanes[i].normal() = VectorNormalized( enrm - cs.fplane.normal() ); + cs.splanes[i].dist() = vector3_dot( cs.fw[i], cs.splanes[i].normal() ); } /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; + cs.add_back_plane( clipDepth, cs.fplane.normal() ); - make_brush_sides<5>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } - else if ( spf == eExtrudeVertexNormals || spf == ( eExtrudeVertexNormals | ePyramidaClip ) ){ // vertex normals + don't check for sides, sticking outwards Vector3 Vnorm[3], Enorm[3]; /* get vertex normals */ - for ( j = 0; j < 3; ++j ){ + for ( int i = 0; i < 3; ++i ){ /* copy normal */ - Vnorm[j] = ds.verts[ *( i + j ) ].normal; + Vnorm[i] = ds.verts[*( idx + i )].normal; } //avg normals for side planes - for ( j = 0; j < 3; ++j ) + for ( int i = 0; i < 3; ++i ) { - Enorm[ j ] = VectorNormalized( Vnorm[ j ] + Vnorm[( j + 1 ) % 3] ); + Enorm[i] = VectorNormalized( Vnorm[i] + Vnorm[( i + 1 ) % 3] ); //check fuer bad ones - const Vector3 nrm = VectorNormalized( vector3_cross( plane.normal(), points[( j + 1 ) % 3] - points[ j ] ) ); + const Vector3 nrm = VectorNormalized( vector3_cross( cs.fplane.normal(), cs.fw[( i + 1 ) % 3] - cs.fw[i] ) ); //check for negative or outside direction - if ( vector3_dot( Enorm[j], plane.normal() ) > 0.1 ){ - if ( ( vector3_dot( Enorm[j], nrm ) > -0.2 ) || ( spf & ePyramidaClip ) ){ + if ( vector3_dot( Enorm[i], cs.fplane.normal() ) > 0.1 ){ + if ( ( vector3_dot( Enorm[i], nrm ) > -0.2 ) || ( spf & ePyramidaClip ) ){ //ok++; continue; } @@ -818,141 +730,68 @@ static void ClipModel( int spawnFlags, float clipDepth, shaderInfo_t& si, const //notok++; //Sys_Printf( "faulty Enormal %i/%i\n", notok, ok ); //use 45 normal - Enorm[ j ] = plane.normal() + nrm; - VectorNormalize( Enorm[ j ] ); + Enorm[i] = VectorNormalized( cs.fplane.normal() + nrm ); } /* make side planes */ - for ( j = 0; j < 3; ++j ) + for ( int i = 0; i < 3; ++i ) { - p[j].normal() = VectorNormalized( vector3_cross( Enorm[j], points[( j + 1 ) % 3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; ++k ) - { - if ( std::fabs( p[j].normal()[k] ) < 0.00025f && p[j].normal()[k] != 0 ){ - //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - p[j].normal()[k] = 0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - p[j].dist() = vector3_dot( ( points[j] + points[( j + 1 ) % 3] ) / 2.0, p[j].normal() ); - //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - } + cs.splanes[i].normal() = VectorNormalized( vector3_cross( Enorm[i], cs.fw[( i + 1 ) % 3] - cs.fw[i] ) ); + cs.splanes[i].dist() = vector3_dot( cs.fw[i], cs.splanes[i].normal() ); } /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; + cs.add_back_plane( clipDepth, cs.fplane.normal() ); - make_brush_sides<5>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } - else if ( spf == eExtrudeFaceNormals ){ - /* make side planes */ - for ( j = 0; j < 3; ++j ) + for ( size_t i = 0; i < cs.fw.size(); ++i ) { - p[j].normal() = VectorNormalized( vector3_cross( plane.normal(), points[( j + 1 ) % 3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); - //snap nearly axial side planes - snpd = false; - for ( k = 0; k < 3; ++k ) - { - if ( std::fabs( p[j].normal()[k] ) < 0.00025f && p[j].normal()[k] != 0 ){ - //Sys_Printf( "init plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - p[j].normal()[k] = 0; - snpd = true; - } - } - if ( snpd ){ - VectorNormalize( p[j].normal() ); - //Sys_Printf( "nrm plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - const Vector3 cnt = ( points[j] + points[( j + 1 ) % 3] ) / 2.0; - p[j].dist() = vector3_dot( cnt, p[j].normal() ); - //Sys_Printf( "dst plane %6.8f %6.8f %6.8f %6.8f\n", p[j].a, p[j].b, p[j].c, p[j].d ); - } + cs.splanes[i].normal() = VectorNormalized( vector3_cross( cs.fplane.normal(), cs.fw[winding_next( cs.fw, i )] - cs.fw[i] ) ); + cs.splanes[i].dist() = vector3_dot( cs.fw[i], cs.splanes[i].normal() ); } /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += clipDepth; + cs.add_back_plane( clipDepth, cs.fplane.normal() ); - nonax_clip_dbg( p ); - - make_brush_sides<5>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } - else if ( spf == ePyramidaClip ){ - /* calculate center */ - Vector3 cnt = ( points[0] + points[1] + points[2] ) / 3.0; - + Vector3 cnt = ( cs.fw[0] + cs.fw[1] + cs.fw[2] ) / 3.0; /* make back pyramid point */ - cnt -= plane.normal() * clipDepth; + cnt -= cs.fplane.normal() * clipDepth; // might want to shift this, if produces sus sides, since extreme angle with front /* make 3 more planes */ - if( PlaneFromPoints( p[0], points[1], points[0], cnt ) && - PlaneFromPoints( p[1], points[2], points[1], cnt ) && - PlaneFromPoints( p[2], points[0], points[2], cnt ) ) { - - //check for dangerous planes - while( ( ( p[0].a != 0 || p[0].b != 0 ) && std::fabs( p[0].a ) < 0.00025f && std::fabs( p[0].b ) < 0.00025f ) || - ( ( p[0].a != 0 || p[0].c != 0 ) && std::fabs( p[0].a ) < 0.00025f && std::fabs( p[0].c ) < 0.00025f ) || - ( ( p[0].c != 0 || p[0].b != 0 ) && std::fabs( p[0].c ) < 0.00025f && std::fabs( p[0].b ) < 0.00025f ) || - ( ( p[1].a != 0 || p[1].b != 0 ) && std::fabs( p[1].a ) < 0.00025f && std::fabs( p[1].b ) < 0.00025f ) || - ( ( p[1].a != 0 || p[1].c != 0 ) && std::fabs( p[1].a ) < 0.00025f && std::fabs( p[1].c ) < 0.00025f ) || - ( ( p[1].c != 0 || p[1].b != 0 ) && std::fabs( p[1].c ) < 0.00025f && std::fabs( p[1].b ) < 0.00025f ) || - ( ( p[2].a != 0 || p[2].b != 0 ) && std::fabs( p[2].a ) < 0.00025f && std::fabs( p[2].b ) < 0.00025f ) || - ( ( p[2].a != 0 || p[2].c != 0 ) && std::fabs( p[2].a ) < 0.00025f && std::fabs( p[2].c ) < 0.00025f ) || - ( ( p[2].c != 0 || p[2].b != 0 ) && std::fabs( p[2].c ) < 0.00025f && std::fabs( p[2].b ) < 0.00025f ) ) { - cnt -= plane.normal() * 0.1f; - // Sys_Printf( "shifting pyramid point\n" ); - PlaneFromPoints( p[0], points[1], points[0], cnt ); - PlaneFromPoints( p[1], points[2], points[1], cnt ); - PlaneFromPoints( p[2], points[0], points[2], cnt ); - } - - nonax_clip_dbg( p ); - - make_brush_sides<4>( plane, p, reverse, points, si ); - } - else + for ( size_t i = 0; i < cs.fw.size(); ++i ) { - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], modelName ); - continue; + PlaneFromPoints( cs.splanes[i], cs.fw[winding_next( cs.fw, i )], cs.fw[i], cnt ); } + + cs.make_brush_sides( si ); } - else if ( ( si.clipModel && spf == 0 ) || ( spawnFlags & eClipFlags ) == eClipModel ){ //default CLIPMODEL - default_CLIPMODEL: // axial normal - Vector3 bestNormal = plane.normal(); + Vector3 bestNormal = cs.fplane.normal(); normal_make_axial( bestNormal ); /* make side planes */ - for ( j = 0; j < 3; ++j ) + for ( size_t i = 0; i < cs.fw.size(); ++i ) { - p[j].normal() = VectorNormalized( vector3_cross( bestNormal, points[( j + 1 ) % 3] - points[j] ) ); - p[j].dist() = vector3_dot( points[j], p[j].normal() ); + cs.splanes[i].normal() = VectorNormalized( vector3_cross( bestNormal, cs.fw[winding_next( cs.fw, i )] - cs.fw[i] ) ); + cs.splanes[i].dist() = vector3_dot( cs.fw[i], cs.splanes[i].normal() ); } /* make back plane */ - reverse = plane3_flipped( plane ); - reverse.dist() += vector3_dot( bestNormal, plane.normal() ) * clipDepth; + cs.add_back_plane( clipDepth, bestNormal ); - nonax_clip_dbg( p ); - - make_brush_sides<5>( plane, p, reverse, points, si ); + cs.make_brush_sides( si ); } @@ -963,12 +802,13 @@ default_CLIPMODEL: brush_t& newBrush = entity.brushes.emplace_front( buildBrush ); newBrush.original = &newBrush; entity.numBrushes++; - } - else{ - Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", - points[0][0], points[0][1], points[0][2], points[1][0], points[1][1], points[1][2], points[2][0], points[2][1], points[2][2], modelName ); + continue; } } + Sys_Warning( "triangle (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) of %s was not autoclipped\n", + cs.fw[0][0], cs.fw[0][1], cs.fw[0][2], + cs.fw[1][0], cs.fw[1][1], cs.fw[1][2], + cs.fw[2][0], cs.fw[2][1], cs.fw[2][2], modelName ); } // normalEpsilon = normalEpsilon_save; } diff --git a/tools/quake3/q3map2/q3map2.h b/tools/quake3/q3map2/q3map2.h index e6190994..37b192c1 100644 --- a/tools/quake3/q3map2/q3map2.h +++ b/tools/quake3/q3map2/q3map2.h @@ -1569,9 +1569,10 @@ public: /* map.c */ void LoadMapFile( const char *filename, bool onlyLights, bool noCollapseGroups ); -int FindFloatPlane( const Plane3f& plane, int numPoints, const Vector3 *points ); -inline int FindFloatPlane( const Vector3& normal, float dist, int numPoints, const Vector3 *points ){ - return FindFloatPlane( Plane3f( normal, dist ), numPoints, points ); +void SnapPlaneImproved( Plane3f& plane, const Span& points ); +int FindFloatPlane( const Plane3f& plane, const Span& points ); +inline int FindFloatPlane( const Vector3& normal, float dist, const Span& points ){ + return FindFloatPlane( Plane3f( normal, dist ), points ); } bool PlaneEqual( const plane_t& p, const Plane3f& plane ); void AddBrushBevels(); diff --git a/tools/quake3/q3map2/surface.cpp b/tools/quake3/q3map2/surface.cpp index b8b32e5c..19aec529 100644 --- a/tools/quake3/q3map2/surface.cpp +++ b/tools/quake3/q3map2/surface.cpp @@ -398,7 +398,7 @@ void ClassifySurface( mapDrawSurface_t& ds ){ /* find map plane if necessary */ if ( ds.planar ) { if ( ds.planeNum < 0 ) { - ds.planeNum = FindFloatPlane( plane, 1, &ds.verts[ 0 ].xyz ); + ds.planeNum = FindFloatPlane( plane, Span( &ds.verts[ 0 ].xyz, 1 ) ); } ds.lightmapVecs[ 2 ] = plane.normal(); } @@ -873,7 +873,7 @@ mapDrawSurface_t *DrawSurfaceForMesh( const entity_t& e, parseMesh_t& p, mesh_t /* add a map plane */ if ( planar ) { /* make a map plane */ - ds.planeNum = FindFloatPlane( plane, 1, &mesh->verts[ 0 ].xyz ); + ds.planeNum = FindFloatPlane( plane, Span( &mesh->verts[ 0 ].xyz, 1 ) ); ds.lightmapVecs[ 2 ] = plane.normal(); /* push this normal to all verts (ydnar 2003-02-14: bad idea, small patches get screwed up) */ @@ -2524,7 +2524,7 @@ static void MakeDebugPortalSurfs_r( const node_t *node, shaderInfo_t& si ){ mapDrawSurface_t& ds = AllocDrawSurface( ESurfaceType::Face ); ds.shaderInfo = &si; ds.planar = true; - ds.planeNum = FindFloatPlane( p->plane.plane, 0, nullptr ); + ds.planeNum = FindFloatPlane( p->plane.plane, {} ); ds.lightmapVecs[ 2 ] = p->plane.normal(); ds.fogNum = FOG_INVALID; ds.verts.resize( w.size(), c_bspDrawVert_t0 );