From 39fa34c427be11aa55afb3446db1b6b048a78a8a Mon Sep 17 00:00:00 2001 From: morb Date: Wed, 10 Jun 2026 09:33:22 -0400 Subject: [PATCH 1/3] dmap: match HumanHead's proc output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the 205→186 shadowModel and 40.7→21.0 MB gap that was producing specular rainbow and dancing shadow artifacts in feedingtowerd. Verified via Ghidra + gdb reference captures, A/B BSP vertex bit comparison, and shadowModel set diffs against commercial 1.2 and 1.4. Final results: 186 shadowModels, 2,373 surfaces, 21.04 MB — geometrically identical to what's in commercial pakfiles. usurface.cpp inline-merge skip filters: - IsDefaultModel() results (unregistered inline func_static_N entities were merging bogus 24-vert _default boxes). - Non-drawn, non-shadow surfaces (matches TriListForSide; was leaking collision_* into worldspawn). - Materials with ConstantRegisters()==NULL: these reference per-entity shaderParm0..6 (ft_lightpod, buglight2) and lose their parm values when folded into worldspawn. Winding.cpp x87 parity for BSP topology: - Plane distance + dot in double (Split/Clip/ClipInPlace/PlaneSide) so SelectSplitPlaneNum scoring matches. - Split-point lerp in long double (x86_64 native 80-bit) to match HumanHead's builds with x87 intermediate precision. --- neo/idlib/geometry/Winding.cpp | 157 ++++++++++++++++---------- neo/tools/compilers/dmap/usurface.cpp | 18 +++ 2 files changed, 113 insertions(+), 62 deletions(-) diff --git a/neo/idlib/geometry/Winding.cpp b/neo/idlib/geometry/Winding.cpp index 619f9e8d..48e3986f 100644 --- a/neo/idlib/geometry/Winding.cpp +++ b/neo/idlib/geometry/Winding.cpp @@ -90,10 +90,10 @@ idWinding::Split ============= */ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **front, idWinding **back ) const { - float * dists; + double * dists; byte * sides; int counts[3]; - float dot; + double dot; int i, j; const idVec5 * p1, *p2; idVec5 mid; @@ -107,14 +107,18 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro return 0; // it's not like the callers check the return value anyway.. } - dists = (float *) _alloca( (numPoints+4) * sizeof( float ) ); + dists = (double *) _alloca( (numPoints+4) * sizeof( double ) ); sides = (byte *) _alloca( (numPoints+4) * sizeof( byte ) ); counts[0] = counts[1] = counts[2] = 0; + // morb: per-point distance in double to match x87 80-bit + const idVec3 &pn = plane.Normal(); + double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; + // determine sides for each point for ( i = 0; i < numPoints; i++ ) { - dists[i] = dot = plane.Distance( p[i].ToVec3() ); + dists[i] = dot = pa * (double)p[i].x + pb * (double)p[i].y + pc * (double)p[i].z + pd; if ( dot > epsilon ) { sides[i] = SIDE_FRONT; } else if ( dot < -epsilon ) { @@ -188,8 +192,9 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro // always calculate the split going from the same side // or minor epsilon issues can happen + // morb: long double (x86_64 = 80-bit) lerp to match x87 intermediates. if ( sides[i] == SIDE_FRONT ) { - dot = dists[i] / ( dists[i] - dists[i+1] ); + long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -197,13 +202,13 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (*p1)[j] + dot * ( (*p2)[j] - (*p1)[j] ); + mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); } } - mid.s = p1->s + dot * ( p2->s - p1->s ); - mid.t = p1->t + dot * ( p2->t - p1->t ); + mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); + mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); } else { - dot = dists[i+1] / ( dists[i+1] - dists[i] ); + long double dotd = (long double)dists[i+1] / ( (long double)dists[i+1] - (long double)dists[i] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -211,13 +216,14 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (*p2)[j] + dot * ( (*p1)[j] - (*p2)[j] ); + mid[j] = (float)( (long double)(*p2)[j] + dotd * ( (long double)(*p1)[j] - (long double)(*p2)[j] ) ); } } - mid.s = p2->s + dot * ( p1->s - p2->s ); - mid.t = p2->t + dot * ( p1->t - p2->t ); + mid.s = (float)( (long double)p2->s + dotd * ( (long double)p1->s - (long double)p2->s ) ); + mid.t = (float)( (long double)p2->t + dotd * ( (long double)p1->t - (long double)p2->t ) ); } + f->p[f->numPoints] = mid; f->numPoints++; b->p[b->numPoints] = mid; @@ -237,12 +243,12 @@ idWinding::Clip ============= */ idWinding *idWinding::Clip( const idPlane &plane, const float epsilon, const bool keepOn ) { - float * dists; + double * dists; byte * sides; idVec5 * newPoints; int newNumPoints; int counts[3]; - float dot; + double dot; int i, j; idVec5 * p1, *p2; idVec5 mid; @@ -256,14 +262,18 @@ idWinding *idWinding::Clip( const idPlane &plane, const float epsilon, const boo return NULL; } - dists = (float *) _alloca( (numPoints+4) * sizeof( float ) ); + dists = (double *) _alloca( (numPoints+4) * sizeof( double ) ); sides = (byte *) _alloca( (numPoints+4) * sizeof( byte ) ); counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + // morb: distance in double precision (see idWinding::Split) + const idVec3 &pn = plane.Normal(); + double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; + // determine sides for each point for ( i = 0; i < numPoints; i++ ) { - dists[i] = dot = plane.Distance( p[i].ToVec3() ); + dists[i] = dot = pa * (double)p[i].x + pb * (double)p[i].y + pc * (double)p[i].z + pd; if ( dot > epsilon ) { sides[i] = SIDE_FRONT; } else if ( dot < -epsilon ) { @@ -324,19 +334,22 @@ idWinding *idWinding::Clip( const idPlane &plane, const float epsilon, const boo // generate a split point p2 = &p[(i+1)%numPoints]; - dot = dists[i] / (dists[i] - dists[i+1]); - for ( j = 0; j < 3; j++ ) { - // avoid round off error when possible - if ( plane.Normal()[j] == 1.0f ) { - mid[j] = plane.Dist(); - } else if ( plane.Normal()[j] == -1.0f ) { - mid[j] = -plane.Dist(); - } else { - mid[j] = (*p1)[j] + dot * ( (*p2)[j] - (*p1)[j] ); + // morb: lerp in long double (80-bit) to match x87 intermediates. + { + long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + for ( j = 0; j < 3; j++ ) { + // avoid round off error when possible + if ( plane.Normal()[j] == 1.0f ) { + mid[j] = plane.Dist(); + } else if ( plane.Normal()[j] == -1.0f ) { + mid[j] = -plane.Dist(); + } else { + mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + } } + mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); + mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); } - mid.s = p1->s + dot * ( p2->s - p1->s ); - mid.t = p1->t + dot * ( p2->t - p1->t ); newPoints[newNumPoints] = mid; newNumPoints++; @@ -358,12 +371,12 @@ idWinding::ClipInPlace ============= */ bool idWinding::ClipInPlace( const idPlane &plane, const float epsilon, const bool keepOn ) { - float* dists; + double* dists; byte * sides; idVec5 * newPoints; int newNumPoints; int counts[3]; - float dot; + double dot; int i, j; idVec5 * p1, *p2; idVec5 mid; @@ -371,14 +384,18 @@ bool idWinding::ClipInPlace( const idPlane &plane, const float epsilon, const bo assert( this ); - dists = (float *) _alloca( (numPoints+4) * sizeof( float ) ); + dists = (double *) _alloca( (numPoints+4) * sizeof( double ) ); sides = (byte *) _alloca( (numPoints+4) * sizeof( byte ) ); counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + // morb: distance in double precision (see idWinding::Split) + const idVec3 &pn = plane.Normal(); + double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; + // determine sides for each point for ( i = 0; i < numPoints; i++ ) { - dists[i] = dot = plane.Distance( p[i].ToVec3() ); + dists[i] = dot = pa * (double)p[i].x + pb * (double)p[i].y + pc * (double)p[i].z + pd; if ( dot > epsilon ) { sides[i] = SIDE_FRONT; } else if ( dot < -epsilon ) { @@ -439,19 +456,22 @@ bool idWinding::ClipInPlace( const idPlane &plane, const float epsilon, const bo // generate a split point p2 = &p[(i+1)%numPoints]; - dot = dists[i] / (dists[i] - dists[i+1]); - for ( j = 0; j < 3; j++ ) { - // avoid round off error when possible - if ( plane.Normal()[j] == 1.0f ) { - mid[j] = plane.Dist(); - } else if ( plane.Normal()[j] == -1.0f ) { - mid[j] = -plane.Dist(); - } else { - mid[j] = (*p1)[j] + dot * ( (*p2)[j] - (*p1)[j] ); + // morb: lerp in long double (80-bit) to match x87 intermediates. + { + long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + for ( j = 0; j < 3; j++ ) { + // avoid round off error when possible + if ( plane.Normal()[j] == 1.0f ) { + mid[j] = plane.Dist(); + } else if ( plane.Normal()[j] == -1.0f ) { + mid[j] = -plane.Dist(); + } else { + mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + } } + mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); + mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); } - mid.s = p1->s + dot * ( p2->s - p1->s ); - mid.t = p1->t + dot * ( p2->t - p1->t ); newPoints[newNumPoints] = mid; newNumPoints++; @@ -766,12 +786,14 @@ void idWinding::RemoveColinearPoints( const idVec3 &normal, const float epsilon idVec3 edgeNormal; float dist; - if ( numPoints <= 3 ) { - return; - } - for ( i = 0; i < numPoints; i++ ) { + // c/o TDM - don't remove any points when there are only 3 or less left to avoid + // degenerating the winding, otherwise this may result in windings with numPoints = 0 + if ( numPoints <= 3 ) { + return; + } + // create plane through edge orthogonal to winding plane edgeNormal = (p[i].ToVec3() - p[(i+numPoints-1)%numPoints].ToVec3()).Cross( normal ); edgeNormal.Normalize(); @@ -1303,12 +1325,16 @@ idWinding::PlaneSide int idWinding::PlaneSide( const idPlane &plane, const float epsilon ) const { bool front, back; int i; - float d; + double d; + + // morb: double-precision distance to match x87 + const idVec3 &pn = plane.Normal(); + double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; front = false; back = false; for ( i = 0; i < numPoints; i++ ) { - d = plane.Distance( p[i].ToVec3() ); + d = pa * (double)p[i].x + pb * (double)p[i].y + pc * (double)p[i].z + pd; if ( d < -epsilon ) { if ( front ) { return SIDE_CROSS; @@ -1494,9 +1520,9 @@ idFixedWinding::Split */ int idFixedWinding::Split( idFixedWinding *back, const idPlane &plane, const float epsilon ) { int counts[3]; - float dists[MAX_POINTS_ON_WINDING+4]; + double dists[MAX_POINTS_ON_WINDING+4]; byte sides[MAX_POINTS_ON_WINDING+4]; - float dot; + double dot; int i, j; idVec5 *p1, *p2; idVec5 mid; @@ -1504,9 +1530,13 @@ int idFixedWinding::Split( idFixedWinding *back, const idPlane &plane, const flo counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + // morb: distance in double precision (see idWinding::Split) + const idVec3 &pn = plane.Normal(); + double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; + // determine sides for each point for ( i = 0; i < numPoints; i++ ) { - dists[i] = dot = plane.Distance( p[i].ToVec3() ); + dists[i] = dot = pa * (double)p[i].x + pb * (double)p[i].y + pc * (double)p[i].z + pd; if ( dot > epsilon ) { sides[i] = SIDE_FRONT; } else if ( dot < -epsilon ) { @@ -1584,19 +1614,22 @@ int idFixedWinding::Split( idFixedWinding *back, const idPlane &plane, const flo p2 = &p[j]; } - dot = dists[i] / (dists[i] - dists[i+1]); - for ( j = 0; j < 3; j++ ) { - // avoid round off error when possible - if ( plane.Normal()[j] == 1.0f ) { - mid[j] = plane.Dist(); - } else if ( plane.Normal()[j] == -1.0f ) { - mid[j] = -plane.Dist(); - } else { - mid[j] = (*p1)[j] + dot * ( (*p2)[j] - (*p1)[j] ); + // morb: lerp in long double (80-bit) to match x87 intermediates. + { + long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + for ( j = 0; j < 3; j++ ) { + // avoid round off error when possible + if ( plane.Normal()[j] == 1.0f ) { + mid[j] = plane.Dist(); + } else if ( plane.Normal()[j] == -1.0f ) { + mid[j] = -plane.Dist(); + } else { + mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + } } + mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); + mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); } - mid.s = p1->s + dot * ( p2->s - p1->s ); - mid.t = p1->t + dot * ( p2->t - p1->t ); out.p[out.numPoints] = mid; out.numPoints++; diff --git a/neo/tools/compilers/dmap/usurface.cpp b/neo/tools/compilers/dmap/usurface.cpp index 92696936..cda8a5d7 100644 --- a/neo/tools/compilers/dmap/usurface.cpp +++ b/neo/tools/compilers/dmap/usurface.cpp @@ -725,6 +725,13 @@ void PutPrimitivesInAreas( uEntity_t* e ) } idRenderModel* model = renderModelManager->FindModel( modelName ); + // morb: skip when FindModel returns a default model — inline func_statics aren't registered + // with renderModelManager, so this would inject a bogus 24-vert "_default" box. + if ( !model || model->IsDefaultModel() ) + { + continue; + } + common->Printf( "inlining %s.\n", entity->mapEntity->epairs.GetString( "name" ) ); idMat3 axis; @@ -749,6 +756,17 @@ void PutPrimitivesInAreas( uEntity_t* e ) const modelSurface_t* surface = model->Surface( i ); const srfTriangles_t* tri = surface->geometry; + // morb: skip non-visible model surfaces (collision-only, etc.); matches TriListForSide. + if ( !surface->shader || ( !surface->shader->SurfaceCastsShadow() && !surface->shader->IsDrawn() ) ) { + continue; + } + + // morb: skip surfaces with material depends on per-entity shaderParm. Otherwise, inlining merges them + // into worldspawn and loses the entity-specific parm values. + if ( !surface->shader->ConstantRegisters() ) { + continue; + } + mapTri_t mapTri; memset( &mapTri, 0, sizeof( mapTri ) ); mapTri.material = surface->shader; From 987f1060449441dbdd5dc69c57485ef364ae8eaf Mon Sep 17 00:00:00 2001 From: morb Date: Fri, 19 Jun 2026 09:53:06 -0400 Subject: [PATCH 2/3] dmap-fixes: dmap was horking on complex maps such as shuttlea. x87 precision was overkill. Discovered and Worked around some segfaults, hangs, and stack-smashing situ. --- neo/idlib/geometry/Winding.cpp | 51 +++++++++++----------- neo/tools/compilers/aas/AASBuild.cpp | 32 ++++++++------ neo/tools/compilers/aas/AASBuild_file.cpp | 14 +++++- neo/tools/compilers/aas/AASBuild_merge.cpp | 12 +++-- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/neo/idlib/geometry/Winding.cpp b/neo/idlib/geometry/Winding.cpp index 48e3986f..ba4aca2c 100644 --- a/neo/idlib/geometry/Winding.cpp +++ b/neo/idlib/geometry/Winding.cpp @@ -112,7 +112,6 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro counts[0] = counts[1] = counts[2] = 0; - // morb: per-point distance in double to match x87 80-bit const idVec3 &pn = plane.Normal(); double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; @@ -192,9 +191,9 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro // always calculate the split going from the same side // or minor epsilon issues can happen - // morb: long double (x86_64 = 80-bit) lerp to match x87 intermediates. + // morb: lerp in double is enough. trying to nail down x87 equivalence resulted in complexity. if ( sides[i] == SIDE_FRONT ) { - long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + double dotd = (double)dists[i] / ( (double)dists[i] - (double)dists[i+1] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -202,13 +201,13 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + mid[j] = (float)( (double)(*p1)[j] + dotd * ( (double)(*p2)[j] - (double)(*p1)[j] ) ); } } - mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); - mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); + mid.s = (float)( (double)p1->s + dotd * ( (double)p2->s - (double)p1->s ) ); + mid.t = (float)( (double)p1->t + dotd * ( (double)p2->t - (double)p1->t ) ); } else { - long double dotd = (long double)dists[i+1] / ( (long double)dists[i+1] - (long double)dists[i] ); + double dotd = (double)dists[i+1] / ( (double)dists[i+1] - (double)dists[i] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -216,11 +215,11 @@ int idWinding::Split( const idPlane &plane, const float epsilon, idWinding **fro } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (float)( (long double)(*p2)[j] + dotd * ( (long double)(*p1)[j] - (long double)(*p2)[j] ) ); + mid[j] = (float)( (double)(*p2)[j] + dotd * ( (double)(*p1)[j] - (double)(*p2)[j] ) ); } } - mid.s = (float)( (long double)p2->s + dotd * ( (long double)p1->s - (long double)p2->s ) ); - mid.t = (float)( (long double)p2->t + dotd * ( (long double)p1->t - (long double)p2->t ) ); + mid.s = (float)( (double)p2->s + dotd * ( (double)p1->s - (double)p2->s ) ); + mid.t = (float)( (double)p2->t + dotd * ( (double)p1->t - (double)p2->t ) ); } @@ -334,9 +333,9 @@ idWinding *idWinding::Clip( const idPlane &plane, const float epsilon, const boo // generate a split point p2 = &p[(i+1)%numPoints]; - // morb: lerp in long double (80-bit) to match x87 intermediates. + // morb: lerp in double. { - long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + double dotd = (double)dists[i] / ( (double)dists[i] - (double)dists[i+1] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -344,11 +343,11 @@ idWinding *idWinding::Clip( const idPlane &plane, const float epsilon, const boo } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + mid[j] = (float)( (double)(*p1)[j] + dotd * ( (double)(*p2)[j] - (double)(*p1)[j] ) ); } } - mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); - mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); + mid.s = (float)( (double)p1->s + dotd * ( (double)p2->s - (double)p1->s ) ); + mid.t = (float)( (double)p1->t + dotd * ( (double)p2->t - (double)p1->t ) ); } newPoints[newNumPoints] = mid; @@ -456,9 +455,9 @@ bool idWinding::ClipInPlace( const idPlane &plane, const float epsilon, const bo // generate a split point p2 = &p[(i+1)%numPoints]; - // morb: lerp in long double (80-bit) to match x87 intermediates. + // morb: lerp in double. { - long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + double dotd = (double)dists[i] / ( (double)dists[i] - (double)dists[i+1] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -466,11 +465,11 @@ bool idWinding::ClipInPlace( const idPlane &plane, const float epsilon, const bo } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + mid[j] = (float)( (double)(*p1)[j] + dotd * ( (double)(*p2)[j] - (double)(*p1)[j] ) ); } } - mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); - mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); + mid.s = (float)( (double)p1->s + dotd * ( (double)p2->s - (double)p1->s ) ); + mid.t = (float)( (double)p1->t + dotd * ( (double)p2->t - (double)p1->t ) ); } newPoints[newNumPoints] = mid; @@ -1327,7 +1326,7 @@ int idWinding::PlaneSide( const idPlane &plane, const float epsilon ) const { int i; double d; - // morb: double-precision distance to match x87 + // morb: double-precision distance for SelectSplitPlaneNum scoring. const idVec3 &pn = plane.Normal(); double pa = pn[0], pb = pn[1], pc = pn[2], pd = plane[3]; @@ -1614,9 +1613,9 @@ int idFixedWinding::Split( idFixedWinding *back, const idPlane &plane, const flo p2 = &p[j]; } - // morb: lerp in long double (80-bit) to match x87 intermediates. + // morb: lerp in double. { - long double dotd = (long double)dists[i] / ( (long double)dists[i] - (long double)dists[i+1] ); + double dotd = (double)dists[i] / ( (double)dists[i] - (double)dists[i+1] ); for ( j = 0; j < 3; j++ ) { // avoid round off error when possible if ( plane.Normal()[j] == 1.0f ) { @@ -1624,11 +1623,11 @@ int idFixedWinding::Split( idFixedWinding *back, const idPlane &plane, const flo } else if ( plane.Normal()[j] == -1.0f ) { mid[j] = -plane.Dist(); } else { - mid[j] = (float)( (long double)(*p1)[j] + dotd * ( (long double)(*p2)[j] - (long double)(*p1)[j] ) ); + mid[j] = (float)( (double)(*p1)[j] + dotd * ( (double)(*p2)[j] - (double)(*p1)[j] ) ); } } - mid.s = (float)( (long double)p1->s + dotd * ( (long double)p2->s - (long double)p1->s ) ); - mid.t = (float)( (long double)p1->t + dotd * ( (long double)p2->t - (long double)p1->t ) ); + mid.s = (float)( (double)p1->s + dotd * ( (double)p2->s - (double)p1->s ) ); + mid.t = (float)( (double)p1->t + dotd * ( (double)p2->t - (double)p1->t ) ); } out.p[out.numPoints] = mid; diff --git a/neo/tools/compilers/aas/AASBuild.cpp b/neo/tools/compilers/aas/AASBuild.cpp index 5e2f111b..b6e2ef44 100644 --- a/neo/tools/compilers/aas/AASBuild.cpp +++ b/neo/tools/compilers/aas/AASBuild.cpp @@ -863,24 +863,30 @@ bool idAASBuild::Build( const idStr& fileName, const idAASSettings* settings ) bsp.MeltPortals( AREACONTENTS_SOLID ); // store the file from the bsp tree - StoreFile( bsp ); + bool storeOk = StoreFile( bsp ); file->settings = *aasSettings; - // calculate reachability - reach.Build( mapFile, file ); + if ( storeOk ) { + // calculate reachability + reach.Build( mapFile, file ); - // build clusters - cluster.Build( file ); + // build clusters + cluster.Build( file ); - // optimize the file - if( !aasSettings->noOptimize ) - { - file->Optimize(); - } + // optimize the file + if( !aasSettings->noOptimize ) + { + file->Optimize(); + } - // write the file - name.SetFileExtension( aasSettings->fileExtension ); - file->Write( name, mapFile->GetGeometryCRC() ); + // write the file + name.SetFileExtension( aasSettings->fileExtension ); + file->Write( name, mapFile->GetGeometryCRC() ); + } else { + // morb: downstream phases would dereference truncated area indices + common->Warning( "Skipping AAS write for %s", + aasSettings->fileExtension.c_str() ); + } // delete the map file delete mapFile; diff --git a/neo/tools/compilers/aas/AASBuild_file.cpp b/neo/tools/compilers/aas/AASBuild_file.cpp index dcd081b0..1ce4dfa2 100644 --- a/neo/tools/compilers/aas/AASBuild_file.cpp +++ b/neo/tools/compilers/aas/AASBuild_file.cpp @@ -237,7 +237,7 @@ idAASBuild::GetFaceForPortal bool idAASBuild::GetFaceForPortal( idBrushBSPPortal* portal, int side, int* faceNum ) { int i, j, v1num; - int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING]; + int numFaceEdges; idWinding* w; aasFace_t face; @@ -255,6 +255,10 @@ bool idAASBuild::GetFaceForPortal( idBrushBSPPortal* portal, int side, int* face } w = portal->GetWinding(); + // morb: buffer should be sized to the winding. + // modern systems otherwise detect stack-smashing here (!) with >64 points. + int* faceEdges = (int*)_alloca( w->GetNumPoints() * sizeof( int ) ); + // turn the winding into a sequence of edges numFaceEdges = 0; v1num = -1; // first vertex unknown @@ -554,5 +558,13 @@ bool idAASBuild::StoreFile( const idBrushBSP& bsp ) common->Printf( "\r%6d areas\n", file->areas.Num() ); + // morb: aasFace_t::areas[2] is `short`; >32767 areas silently truncates, + // dereferences indices, then segfaults. + if ( file->areas.Num() > 32767 ) { + common->Warning( "Skipping write: AAS area count %d is greater than 32767", + file->areas.Num() ); + return false; + } + return true; } diff --git a/neo/tools/compilers/aas/AASBuild_merge.cpp b/neo/tools/compilers/aas/AASBuild_merge.cpp index ff6cddb1..98d1f61a 100644 --- a/neo/tools/compilers/aas/AASBuild_merge.cpp +++ b/neo/tools/compilers/aas/AASBuild_merge.cpp @@ -177,11 +177,9 @@ void idAASBuild::MergeLeafNodes( idBrushBSP& bsp ) { numMergedLeafNodes = 0; - common->Printf( "[Merge Leaf Nodes]\n" ); - - MergeLeafNodes_r( bsp, bsp.GetRootNode() ); - bsp.GetRootNode()->RemoveFlagRecurse( NODE_DONE ); - bsp.PruneMergedTree_r( bsp.GetRootNode() ); - - common->Printf( "\r%6d leaf nodes merged\n", numMergedLeafNodes ); + // morb: MergeLeafNodes is slow on dense Prey maps (i.e. shuttlea) + // partial aborts leave area/face refs that crash + // skipping keeps the BSP geometry consistent, AAS files are larger but valid. + common->Printf( "[Merge Leaf Nodes] (skipped)\n" ); + (void)bsp; } From 605cd266511b64c90d637b19c286eb6f18d001c7 Mon Sep 17 00:00:00 2001 From: morb Date: Fri, 19 Jun 2026 10:06:45 -0400 Subject: [PATCH 3/3] attempting to force runs-on: windows-2022 --- .github/workflows/Windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 471ca6d9..4b251a71 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -18,7 +18,7 @@ on: jobs: build: name: Windows (${{ matrix.arch }}) - runs-on: windows-latest + runs-on: windows-2022 strategy: fail-fast: false