From 8be1c4d9efa3bdcf1f19971980401af0bfb3b5ce Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Thu, 22 Oct 2026 13:01:54 +0200 Subject: [PATCH] wip(assembler): round L36 -- tunnel floor without washboard; FI mouth without shards (evaluator v23) The walk tread was built per segment as a one-sided density clamp, so its crossing with the native field moved at segment joins (L35: 2.25-2.48 cm washboard). Treads now overlap up to 2 m as bounded signed-distance slabs (0.06 cm residual). The FI mouth's crater detail and tread met without a smooth join; v23 filters the local field, drops thin isolated solids and smooth-unions the tread with the terrain. New floor ripple and mouth fragment/crease audits with L35 as failing controls. 24/24 passage rows, 30/30 determinism, 16/16 invariants, LOD0-8 rings; cost within noise. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX --- ARCHITECTURE.md | 24 ++ CODEMAP.md | 15 +- .../include/VoxelForgeCore/VoxelForgeCore.h | 6 +- Standalone/Core/src/ShapeWorldInternal.h | 336 ++++++++++++--- .../Linux36/CapturePassageStandardSetL36.py | 404 ++++++++++++++++++ .../Linux36/ProbeMouthComponentsL36.py | 89 ++++ .../Linux36/ProbeWalkFloorProfileL36.py | 158 +++++++ .../WorldAssemblerM3/Linux36/REPORT.md | 54 +++ .../WorldAssemblerM3/Linux36/RunCostL36.py | 88 ++++ .../Linux36/RunDeterminismL36.py | 119 ++++++ .../Linux36/RunInvariantAuditsL36.py | 82 ++++ .../Linux36/RunLegacyAuditsL36.py | 69 +++ .../Linux36/RunMouthFragmentAuditL36.py | 166 +++++++ .../Linux36/RunPassageAuditsL36.py | 93 ++++ .../Linux36/captures/owner-fi-arrival.jpg | Bin 0 -> 23432 bytes .../captures/owner-fi-ground-mouth.jpg | Bin 0 -> 33605 bytes .../Linux36/captures/owner-fi-mid-tunnel.jpg | Bin 0 -> 23182 bytes .../Linux36/captures/owner-fi-rock-entry.jpg | Bin 0 -> 26512 bytes .../Linux36/captures/owner-sw-arrival.jpg | Bin 0 -> 21976 bytes .../captures/owner-sw-ground-mouth.jpg | Bin 0 -> 29442 bytes .../Linux36/captures/owner-sw-mid-tunnel.jpg | Bin 0 -> 23540 bytes .../Linux36/captures/owner-sw-rock-entry.jpg | Bin 0 -> 43435 bytes .../captures/upper-sw-fixture-arrival.jpg | Bin 0 -> 23906 bytes .../upper-sw-fixture-ground-mouth.jpg | Bin 0 -> 25541 bytes .../captures/upper-sw-fixture-mid-tunnel.jpg | Bin 0 -> 24063 bytes .../captures/upper-sw-fixture-rock-entry.jpg | Bin 0 -> 25763 bytes Standalone/Source/StreamBenchmarkPipeline.h | 139 +++++- 27 files changed, 1787 insertions(+), 55 deletions(-) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/CapturePassageStandardSetL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/ProbeMouthComponentsL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/ProbeWalkFloorProfileL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunCostL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunDeterminismL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunInvariantAuditsL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunLegacyAuditsL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunMouthFragmentAuditL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/RunPassageAuditsL36.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-arrival.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-ground-mouth.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-mid-tunnel.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-rock-entry.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-arrival.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-ground-mouth.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-mid-tunnel.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-rock-entry.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-arrival.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-ground-mouth.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-mid-tunnel.jpg create mode 100644 Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-rock-entry.jpg diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1136ca3..1b7d232 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2138,6 +2138,30 @@ turn. Passage, capsule, support, open-air, bridge, binding, strict layout, captu and cost evidence are recorded in [`Standalone/Research/WorldAssemblerM3/Linux35/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux35/REPORT.md). +### 8.49 Walk-floor washboard and FI mouth naturalness — World Assembler M3 L36 + +L35's exported owner-SW mid-tunnel floor measured a 7.250 m repeat at 0.02252 m amplitude; its +upper-SW fixture arrival measured a 1.438 m repeat at 0.02482 m. Both came from segment-built, +one-sided support treads meeting the native terrain zero crossing at per-segment joins. The floor +probe samples the exported walk mesh along the resolved route, separately from the wall-radius scan. +Eligible tread sections overlap by up to 2 m and all sparse route segments now use a bounded +signed-distance slab, including SurfaceWorld-to-TunnelNetwork routes. This keeps the tread top stable +across segment joins while capsule-clearance and native-solid gates retain walkability and open air. +Regular tread tops remain seated 5 cm lower and ease back to 0.20 m within 20 m of a lower landing; +seeded shoulder grain is removed, and gravity-facing relief is reduced while side-wall and ceiling +relief remain irregular. + +On v23, the same mesh probes measure 7.188 m / 0.00067 m (0.067 cm) on owner-SW and 6.500 m / +0.00057 m (0.057 cm) on the upper fixture. The density audit checks five 32 m walk-floor windows at +0.125 m spacing, rejects amplitudes of 0.015 m or more, and detects its beaded control. The FI mouth +uses a local 2 m symmetric signed-field filter with the route protected and joins route-mouth support +to native terrain with a 1.5 m smooth signed-distance union. Its mesh audit reduces the L35 +42.27° fade-ring jump to 27.13° and finds no detached component above the 0.0001 m² area floor; one +8-triangle subthreshold component at 0.0000266 m² is recorded. The evaluator is version 23. Full +recipe/style audits, route support, open-air, strict layout, legacy validators, determinism, capture and +paired-cost results are in +[`Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md). + ## 9. Multiplayer model (listen-server first, dedicated-friendly) > **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a diff --git a/CODEMAP.md b/CODEMAP.md index 1d00b64..4a8241f 100644 --- a/CODEMAP.md +++ b/CODEMAP.md @@ -1250,11 +1250,24 @@ ARCHITECTURE §8.47 and | `VF_FieldPassageWallRelief` | `Standalone/Core/src/VoxelForgeFieldShared.h` | Supplies passage-only irregular wall relief at rotated 43/29/19 m scales. Other `VF_FieldSurfaceRelief` users stay unchanged. | | `ApplyResolvedStratePassageCarves` | `Standalone/Core/src/ShapeWorldInternal.h` | Catmull–Rom smooths eligible interior sweep joins, applies passage-only wall relief, and tapers/grains the entry support shoulder below the capsule/support probe footprint. | | `PlanResolvedLayoutPassagesV1` | `Standalone/Core/src/ResolvedLayoutInternal.h` | Gives each Cascade turn a seeded shallow radial profile instead of identical repeated full-depth cosine lobes. | -| `auditPassageRipples` / `PassageRippleAudit` | `Standalone/Source/StreamBenchmarkPipeline.h` | Samples field wall radius at 0.125 m spacing, scans periods 0.5–8 m in 32 m windows, and rejects amplitudes ≥0.035 m. The negative control uses 2 m radius spheres at 1.5 m spacing. | +| `auditPassageRipples` / `PassageRippleAudit` | `Standalone/Source/StreamBenchmarkPipeline.h` | Samples wall radii and walk-floor zero crossings at 0.125 m spacing, scans periods 0.5–8 m in 32 m windows, and rejects wall/floor amplitudes ≥0.035/0.015 m. Sphere and beaded-floor controls must be detected. | `WorldAssemblyEvaluatorVersion=21`. See ARCHITECTURE §8.48 and [`Standalone/Research/WorldAssemblerM3/Linux35/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux35/REPORT.md). +### 3.32 Walk-floor washboard and FI mouth blend — World Assembler M3 L36 + +| Symbol | Canonical file | Role | +|---|---|---| +| `ApplyResolvedStratePassageCarves` | `Standalone/Core/src/ShapeWorldInternal.h` | Extends eligible tread sections by up to 2 m and uses bounded signed-distance support on every sparse route, including SurfaceWorld-to-TunnelNetwork, removes seeded shoulder grain, and seats regular tread tops 5 cm lower with a smooth return at the lower landing. Capsule/open-air gates remain. Gravity-facing wall relief is reduced; side and ceiling relief remain. | +| `ApplyResolvedStratePassageCarves` mouth support | `Standalone/Core/src/ShapeWorldInternal.h` | Builds exact signed-distance route-mouth tread boxes and smooth-unions them with native terrain over 1.5 m. | +| `NativeFloatingIslandDensityAt` | `Standalone/Core/src/ShapeWorldInternal.h` | Applies a local 2 m symmetric signed-field filter around FI passage mouths, exempts resolved route support, and removes isolated negative samples thinner than 2 m on all axes. | +| `auditPassageRipples` / `PassageRippleAudit` | `Standalone/Source/StreamBenchmarkPipeline.h` | Keeps the wall-radius audit and adds walk-floor zero-crossing profiles at 0.125 m, with five 32 m windows and a 0.015 m limit plus a beaded-floor negative control. | +| `RunMouthFragmentAuditL36.py`, `ProbeWalkFloorProfileL36.py` | `Standalone/Research/WorldAssemblerM3/Linux36/` | Compare L35 floor and FI-mouth controls with L36; the mouth check fails on detached components at or above 0.0001 m² and checks normal jumps across the fade ring. | + +`WorldAssemblyEvaluatorVersion=23`. See ARCHITECTURE §8.49 and +[`Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md). + ## 4. The density pipeline (most-edited hot path) ### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749 diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 8216b8b..88c00d1 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -250,8 +250,10 @@ namespace VoxelForgeCore inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256; inline constexpr std::uint32_t WorldAssemblySchemaVersion = 2; // Version 14 adds shared passage columns; version 16 smooths owner Gradient faces; - // version 20 restores full Gradient amplitude and uses a continuous signed-distance band union. - inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 21; + // version 20 restores full Gradient amplitude and uses a continuous signed-distance band union; + // version 22 overlaps tread sections and smooths FloatingIslands passage-mouth features; + // version 23 uses signed-distance support for sparse SurfaceWorld-to-TunnelNetwork treads. + inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 23; enum class FieldChannel : std::uint32_t { diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index d30f942..7510e76 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -727,8 +727,9 @@ public: } if(routeCount<2)return; const std::size_t lastSegment=routeCount-1; - const double floorThickness=1000000.0/std::max(1, - ResolvedLayout->coreVoxelSizeMicrometres); + constexpr double MouthSupportBlendMetres=1.5; + const double mouthBlendWidth=MouthSupportBlendMetres*qPerMetre; + double mouthSupportDistance=std::numeric_limits::infinity(); for(std::size_t index=0;index(b.values[3])-a.values[3]):0.0; const double halfWidth=std::max(0.75*qPerMetre,routeRadius); const double horizontalX=qx-centerX,horizontalY=qy-centerY; - if(horizontalX*horizontalX+horizontalY*horizontalY>halfWidth*halfWidth)continue; - const double floorVoxels=(centerZ-1.20*qPerMetre)/Q; - if(ZfloorVoxels+1.0/Q)continue; - composed=std::min(composed,static_cast( - std::clamp(Z-floorVoxels,-floorThickness,0.0))); + const double horizontalDistance=DeterministicSqrt( + horizontalX*horizontalX+horizontalY*horizontalY); + const double floorQ=centerZ-1.20*qPerMetre; + const double sideDistance=horizontalDistance-halfWidth; + const double topDistance=qz-floorQ; + const double bottomDistance=floorQ-qPerMetre-qz; + const double verticalDistance=std::max(topDistance,bottomDistance); + if(sideDistance>mouthBlendWidth||verticalDistance>mouthBlendWidth)continue; + const double outside=DeterministicSqrt( + std::max(sideDistance,0.0)*std::max(sideDistance,0.0) + +std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0)); + const double inside=std::min(std::max(sideDistance,verticalDistance),0.0); + const double signedDistance=outside+inside; + mouthSupportDistance=std::min(mouthSupportDistance,signedDistance); } + if(std::isfinite(mouthSupportDistance)) + composed=SmoothMinDistance(composed, + static_cast(mouthSupportDistance/Q), + static_cast(mouthBlendWidth/Q)); }; // Keep the native surface route connected to the entrance. Passage carving // then removes only the parts that overlap the tunnel, whose own tread owns @@ -765,6 +779,19 @@ public: { const auto& passage=ResolvedLayout->passages[passageIndex]; if(passage.controlPoints.size()<2)continue; + const auto supportStratum=[&](std::int32_t strateIndex) + ->const VoxelForgeCore::ResolvedLayoutStratumV2* + { + const auto it=std::find_if(ResolvedLayout->strates.begin(), + ResolvedLayout->strates.end(),[strateIndex](const auto& stratum) + {return stratum.strateIndex==strateIndex;}); + return it==ResolvedLayout->strates.end()?nullptr:&*it; + }; + const auto* supportUpper=supportStratum(passage.upperStrateIndex); + const auto* supportLower=supportStratum(passage.lowerStrateIndex); + const bool surfaceToTunnelSupport=supportUpper&&supportLower + &&supportUpper->generatorKind=="SurfaceWorld" + &&supportLower->generatorKind=="TunnelNetwork"; const double firstFloorQ=passage.controlPoints.front().positionSubvoxels[2] -passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ; const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2] @@ -880,7 +907,20 @@ public: std::ptrdiff_t bestScanIndex=std::numeric_limits::max(); std::ptrdiff_t bestSupportSegmentIndex=0; bool hasSupportFloor=false; - double bestSupportDensity=0.0; + double bestSupportDistanceSquared=std::numeric_limits::infinity(); + double bestSupportDensity=std::numeric_limits::infinity(); + double bestSupportFloorQ=0.0; + bool bestSupportUsesSignedDistance=false; + std::ptrdiff_t selectedSupportSegmentIndex=-1; + const double lowerMouthDx=qx-passage.lowerMouthSubvoxels[0]; + const double lowerMouthDy=qy-passage.lowerMouthSubvoxels[1]; + const double lowerMouthDz=qz-passage.lowerMouthSubvoxels[2]; + const double lowerMouthDistance=DeterministicSqrt( + lowerMouthDx*lowerMouthDx+lowerMouthDy*lowerMouthDy + +lowerMouthDz*lowerMouthDz)/qPerMetre; + const double lowerMouthT=std::clamp((20.0-lowerMouthDistance)/12.0,0.0,1.0); + const double lowerMouthBlend=lowerMouthT*lowerMouthT + *(3.0-2.0*lowerMouthT); const auto considerCarveSegment=[&](std::size_t segmentIndex,int scanPhase) { const std::ptrdiff_t index=static_cast(segmentIndex); @@ -992,7 +1032,7 @@ public: /distanceToCenter,0.0,1.0):0.0; const float relief=VF_FieldPassageWallRelief(static_cast(X),static_cast(Y), static_cast(Z),static_cast(Owner?Owner->Seed:0)); - const double wallReliefWeight=1.0-0.80*verticalWallAlignment; + const double wallReliefWeight=0.05+0.95*(1.0-verticalWallAlignment); const double roughSignedDistance=bestSignedDistance +static_cast(std::clamp(relief,-1.0f,1.0f))*0.21*qPerMetre*wallReliefWeight; if(IsPassageShortSpanSegment(passageIndex,static_cast(bestIndex))) @@ -1004,38 +1044,48 @@ public: // Positive density is air; rough rock is cut with full side/ceiling detail and // gentler relief on the gravity-facing wall so the supported tread stays open. composed=std::max(composed,static_cast(-roughSignedDistance/Q)); - double bestSupportDistanceSquared=std::numeric_limits::infinity(); const auto considerSupportSegment=[&](std::ptrdiff_t index) { const auto& segmentA=passage.controlPoints[static_cast(index)]; const auto& segmentB=passage.controlPoints[static_cast(index+1)]; const double dx=static_cast(segmentB.positionSubvoxels[0])-segmentA.positionSubvoxels[0]; const double dy=static_cast(segmentB.positionSubvoxels[1])-segmentA.positionSubvoxels[1]; + const double dz=static_cast(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2]; const double radiusA=segmentA.radiusSubvoxels; const double radiusB=segmentB.radiusSubvoxels; const bool entryMouthShoulder=index<=2; // Keep the tread within the probe band and below the capsule's // lowest sampled point. - const double floorOffset=IsPassageShortSpanSegment(passageIndex, + const bool bridgeDeck=IsPassageShortSpanSegment(passageIndex, static_cast(index)) - ||IsPassageColumnSegment(passageIndex,static_cast(index)) + ||IsPassageColumnSegment(passageIndex,static_cast(index)); + // Keep a small capsule margin at regular treads and restore most of + // the nominal height smoothly as a tread approaches its landing. + double floorOffset=bridgeDeck ?PassageBridgeDeckOffsetMetres(passageIndex,static_cast(index)) - :0.20; + :std::lerp(0.15,0.20,lowerMouthBlend); const double horizontalLengthSq=dx*dx+dy*dy; + const bool overlapSupportJoin=index+12(index)) + &&(index==0||(!IsPassageShortSpanSegment(passageIndex, + static_cast(index-1)))); + const bool smoothSupportJoin=index>0&&overlapSupportJoin + &&!IsPassageShortSpanSegment(passageIndex,static_cast(index+1)); + const double projection=horizontalLengthSq>0.0 + ?((qx-segmentA.positionSubvoxels[0])*dx + +(qy-segmentA.positionSubvoxels[1])*dy)/horizontalLengthSq:bestT; + // Extend tread segments by a fixed distance through route knots. A + // fractional extension made the support width change with segment length. + const double overlapFraction=overlapSupportJoin&&horizontalLengthSq>0.0 + ?std::min(0.5,2.0*qPerMetre/DeterministicSqrt(horizontalLengthSq)):0.0; const double supportT=horizontalLengthSq>0.0 - ?std::clamp(((qx-segmentA.positionSubvoxels[0])*dx - +(qy-segmentA.positionSubvoxels[1])*dy)/horizontalLengthSq,0.0,1.0) - :bestT; + ?std::clamp(projection,-overlapFraction,1.0+overlapFraction):bestT; const double centerX=segmentA.positionSubvoxels[0]+supportT*dx; const double centerY=segmentA.positionSubvoxels[1]+supportT*dy; double centerZ=segmentA.positionSubvoxels[2]+supportT*( static_cast(segmentB.positionSubvoxels[2])-segmentA.positionSubvoxels[2]); double radius=segmentA.radiusSubvoxels+supportT*( static_cast(segmentB.radiusSubvoxels)-segmentA.radiusSubvoxels); - const bool smoothSupportJoin=index>0&&index+12(index-1)) - &&!IsPassageShortSpanSegment(passageIndex,static_cast(index)) - &&!IsPassageShortSpanSegment(passageIndex,static_cast(index+1)); if(smoothSupportJoin) { const auto& p0=passage.controlPoints[static_cast(index-1)]; @@ -1063,7 +1113,15 @@ public: // Support belongs just below the avatar's foot plane. Queries within // the capsule body, or a neighbouring coil's overlapping bore, must // not turn that tread into a ceiling or mid-air shelf. - if(supportQueryDepth<0.93*qPerMetre||supportQueryDepth>1.30*qPerMetre)return; + // Sparse authored routes use the full signed distance around the top; + // dense routes retain the legacy one-sided tread density. + const double supportSegmentLength=DeterministicSqrt( + horizontalLengthSq+dz*dz)/qPerMetre; + const bool sparseRouteSupport=supportSegmentLength>1.5; + const double minimumSupportQueryDepth=index==selectedSupportSegmentIndex + &&sparseRouteSupport?0.65:0.93; + if(supportQueryDepth1.30*qPerMetre)return; const bool fallbackSupport=qz>=supportPlayerCenterZ-1.50*qPerMetre; const double supportThicknessVoxels=fallbackSupport ?qPerMetre/Q:0.5*qPerMetre/Q; @@ -1072,12 +1130,8 @@ public: // capsule on the neighbouring turn. const bool lowerMouthBodyOverlap=index>=lowerMouthIndex-2 &&supportQueryDepth<0.98*qPerMetre; - const double supportEdgeNoise=entryMouthShoulder?std::clamp( - static_cast(VF_FieldPassageGroundGrain(static_cast(X), - static_cast(Y), - static_cast(Owner?Owner->Seed:0))),-1.0,1.0):0.0; const double supportHalfWidthMetres=lowerMouthBodyOverlap?0.90 - :entryMouthShoulder?1.20+0.20*supportEdgeNoise:1.25; + :entryMouthShoulder?1.20:1.25; const double supportHalfWidth=std::min(radius, supportHalfWidthMetres*qPerMetre); // Keep the walk core within a 0.50 m span with shallow relief, then @@ -1095,30 +1149,50 @@ public: const double localSupportThicknessVoxels=entryMouthShoulder ?supportThicknessVoxels*(1.0-0.72*supportEdgeSmooth) :supportThicknessVoxels; - const double supportEdgeDepth=entryMouthShoulder - ?0.35+0.15*supportEdgeNoise:0.35; + constexpr double supportEdgeDepth=0.35; const double floorQ=centerZ-radius+floorOffset*qPerMetre - +supportEdgeNoise*(horizontalDistance>0.60*qPerMetre - ?0.35:0.14)*qPerMetre -supportEdgeSmooth*supportEdgeDepth*qPerMetre; const double floorVoxels=floorQ/Q; - if(ZfloorVoxels+1.0/Q)return; + const double supportPositiveRangeVoxels=sparseRouteSupport + ?0.35*qPerMetre/Q:1.0/Q; + if(ZfloorVoxels+supportPositiveRangeVoxels)return; if(horizontalDistance>supportHalfWidth)return; - const double supportDistanceSq=horizontalDistance*horizontalDistance + const double supportDistanceSquared=horizontalDistance*horizontalDistance +(Z-floorVoxels)*(Z-floorVoxels); - if(supportDistanceSq>=bestSupportDistanceSquared)return; - bestSupportDistanceSquared=supportDistanceSq; + if(supportDistanceSquared>=bestSupportDistanceSquared)return; + bestSupportDistanceSquared=supportDistanceSquared; hasSupportFloor=true; bestSupportSegmentIndex=index; - bestSupportDensity=std::clamp(Z-floorVoxels, - -localSupportThicknessVoxels,0.0); + if(sparseRouteSupport) + { + // Sparse support profiles need both sides of a signed distance + // so the sampled zero crossing stays on the tread top. + const double topDistance=qz-floorQ; + const double bottomDistance=floorQ-localSupportThicknessVoxels*Q-qz; + const double verticalDistance=std::max(topDistance,bottomDistance); + const double sideDistance=horizontalDistance-supportHalfWidth; + const double outside=DeterministicSqrt( + std::max(sideDistance,0.0)*std::max(sideDistance,0.0) + +std::max(verticalDistance,0.0)*std::max(verticalDistance,0.0)); + const double inside=std::min(std::max(sideDistance,verticalDistance),0.0); + bestSupportDensity=(outside+inside)/Q; + } + else + { + bestSupportDensity=std::clamp(Z-floorVoxels, + -localSupportThicknessVoxels,0.0); + } + bestSupportFloorQ=floorQ; + bestSupportUsesSignedDistance=sparseRouteSupport; }; std::ptrdiff_t supportCenterIndex=bestIndex; if(bestIndexvalue;}); @@ -1132,6 +1206,8 @@ public: const std::ptrdiff_t supportBegin=std::max(0,supportCenterIndex-1); const std::ptrdiff_t supportEnd=std::min(lastIndex-1, supportCenterIndex+1); + selectedSupportSegmentIndex=supportCenterIndex>0 + ?supportCenterIndex-1:supportCenterIndex; for(std::ptrdiff_t index=supportBegin;index<=supportEnd;++index) considerSupportSegment(index); // Keep route treads inside the native rock envelope. Carving may reopen the @@ -1142,13 +1218,24 @@ public: const bool shortSpanFloorSupport=hasSupportFloor &&IsPassageShortSpanSegment(passageIndex, static_cast(bestSupportSegmentIndex)); - const bool capsuleAllowsSupport=passageTileScopeMatches + const double capsuleClearanceMetres=bestSupportUsesSignedDistance?0.045:0.05; + const double capsuleQueryZ=bestSupportUsesSignedDistance?bestSupportFloorQ:qz; + const bool supportCapsuleProfileAvailable=passageIndex=0.05*qPerMetre; + lowerMouthIndex,mouthSearchBegin,mouthSearchEnd,lastIndex,qx,qy, + capsuleQueryZ,qPerMetre,capsuleClearanceMetres) + :bestPlayerCapsuleSignedDistance>=capsuleClearanceMetres*qPerMetre; + // Outside bridge and short-span segments, add the tread only where the + // native field already provides a solid envelope; the capsule check keeps + // that support below the player's walk space. if(hasSupportFloor&&(density<0.0f||passageColumnFloorSupport||shortSpanFloorSupport) - &&capsuleAllowsSupport) + &&(capsuleAllowsSupport||bestSupportDensity>=0.0)) + { composed=std::min(composed,static_cast(bestSupportDensity)); + } } return composed; } @@ -2990,7 +3077,8 @@ private: } bool PassagePlayerCapsuleBlocksSupportNode(std::size_t passageIndex,int nodeIndex, - std::size_t first,std::size_t end,double qx,double qy,double qz,double qPerMetre) const + std::size_t first,std::size_t end,double qx,double qy,double qz,double qPerMetre, + double minimumClearanceMetres) const { if(passageIndex>=PassageNearestNodes.size()||nodeIndex<0)return false; const auto& nodes=PassageNearestNodes[passageIndex]; @@ -3026,26 +3114,26 @@ private: const double vertical=std::max(0.0,std::abs(qz-centerZ)-0.54*qPerMetre); const double distance=DeterministicSqrt(horizontalX*horizontalX+horizontalY*horizontalY +vertical*vertical)-0.34*qPerMetre; - if(distance<0.05*qPerMetre)return true; + if(distance(first),static_cast(last+1), - qx,qy,qz,qPerMetre); + qx,qy,qz,qPerMetre,minimumClearanceMetres); }; return blocked(carveBegin,carveEnd) ||blocked(0,std::min(8,lowerMouthIndex)-1) @@ -4429,7 +4517,159 @@ private: const VoxelForgeCore::FloatingIslandFieldV1 Field( Plan->manifest.recipe,Plan->manifest.plan); if(!Field.isValid())return false; - std::int64_t Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z)); + const std::int64_t qx=ToFixed(X),qy=ToFixed(Y),qz=ToFixed(Z); + std::int64_t Density=Field.densitySubvoxels(qx,qy,qz); + // The anchor-root field keeps broad lobes, but its shortest authored noise + // wavelength is below one metre. Filter that detail only around a FloatingIslands + // passage mouth so Marching Cubes does not expose voxel-sized chips on the crater + // wall. The broad root profile is unchanged outside this local mouth envelope. + double mouthFilterWeight=0.0; + const double qToMetres=static_cast(ResolvedLayout->coreVoxelSizeMicrometres) + /(static_cast(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)*1000000.0); + const double densityMetres=std::abs(static_cast(Density))*qToMetres; + if(densityMetres<4.0) + { + bool foundPassageMouth=false; + for(const auto& passage:ResolvedLayout->passages) + { + if(passage.upperStrateIndex!=StrateIndex)continue; + foundPassageMouth=true; + const double dx=(static_cast(qx)-passage.upperMouthSubvoxels[0])*qToMetres; + const double dy=(static_cast(qy)-passage.upperMouthSubvoxels[1])*qToMetres; + const double dz=(static_cast(qz)-passage.upperMouthSubvoxels[2])*qToMetres; + const double distance=std::sqrt(dx*dx+dy*dy+dz*dz); + constexpr double FilterCoreRadiusMetres=8.5; + constexpr double FilterOuterRadiusMetres=12.5; + const double fade=std::clamp((FilterOuterRadiusMetres-distance) + /(FilterOuterRadiusMetres-FilterCoreRadiusMetres),0.0,1.0); + const double weight=fade*fade*(3.0-2.0*fade); + mouthFilterWeight=std::max(mouthFilterWeight,weight); + } + if(!foundPassageMouth) + { + // The passage-free native control retains the stratum route anchors. + // Use its terminal route point as the same local mouth filter origin so + // both native and composed fields receive the same root smoothing. + const auto stratum=std::find_if(ResolvedLayout->strates.begin(), + ResolvedLayout->strates.end(),[StrateIndex](const auto& value) + {return value.strateIndex==StrateIndex;}); + if(stratum!=ResolvedLayout->strates.end()) + { + const VoxelForgeCore::ResolvedLayoutAnchorV2* terminal=nullptr; + for(const auto& anchor:stratum->anchors) + if(anchor.kind=="route_point"&&anchor.values.size()>=3) + terminal=&anchor; + if(terminal) + { + const double dx=static_cast(qx-terminal->values[0])*qToMetres; + const double dy=static_cast(qy-terminal->values[1])*qToMetres; + const double dz=static_cast(qz-terminal->values[2])*qToMetres; + const double distance=std::sqrt(dx*dx+dy*dy+dz*dz); + constexpr double FilterCoreRadiusMetres=8.5; + constexpr double FilterOuterRadiusMetres=12.5; + const double fade=std::clamp((FilterOuterRadiusMetres-distance) + /(FilterOuterRadiusMetres-FilterCoreRadiusMetres),0.0,1.0); + mouthFilterWeight=fade*fade*(3.0-2.0*fade); + } + } + } + } + if(mouthFilterWeight>0.0) + { + // Preserve the resolved walk route while filtering the passage and crater field. + const double queryX=static_cast(qx)*qToMetres; + const double queryY=static_cast(qy)*qToMetres; + const double queryZ=static_cast(qz)*qToMetres; + const auto stratum=std::find_if(ResolvedLayout->strates.begin(), + ResolvedLayout->strates.end(),[StrateIndex](const auto& value) + {return value.strateIndex==StrateIndex;}); + if(stratum!=ResolvedLayout->strates.end()) + { + const VoxelForgeCore::ResolvedLayoutAnchorV2* previous=nullptr; + for(const auto& anchor:stratum->anchors) + { + if(anchor.kind!="route_point"||anchor.values.size()<3)continue; + if(previous) + { + const double ax=static_cast(previous->values[0])*qToMetres; + const double ay=static_cast(previous->values[1])*qToMetres; + const double az=static_cast(previous->values[2])*qToMetres; + const double dx=(static_cast(anchor.values[0]) + -previous->values[0])*qToMetres; + const double dy=(static_cast(anchor.values[1]) + -previous->values[1])*qToMetres; + const double lengthSquared=dx*dx+dy*dy; + const double t=lengthSquared>0.0?std::clamp( + ((queryX-ax)*dx+(queryY-ay)*dy)/lengthSquared,0.0,1.0):0.0; + const double centerX=ax+t*dx,centerY=ay+t*dy; + const double centerZ=az+t*(static_cast(anchor.values[2]) + -previous->values[2])*qToMetres; + const double supportRadius=previous->values.size()>=4 + ?static_cast(previous->values[3])*qToMetres:0.75; + const double floorZ=centerZ-1.20; + const double protectedRadius=supportRadius+0.75; + if((queryX-centerX)*(queryX-centerX) + +(queryY-centerY)*(queryY-centerY) + 0.0) + { + constexpr std::int64_t FilterStep=8*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel; + std::int64_t weightedSum=0; + for(int dz=-1;dz<=1;++dz) + for(int dy=-1;dy<=1;++dy) + for(int dx=-1;dx<=1;++dx) + { + const std::int64_t weight=(dx==0?2:1)*(dy==0?2:1)*(dz==0?2:1); + const std::int64_t sample=(dx==0&&dy==0&&dz==0)?Density: + Field.densitySubvoxels(qx+dx*FilterStep,qy+dy*FilterStep, + qz+dz*FilterStep); + weightedSum+=sample*weight; + } + // A separable [1,2,1]^3 kernel over a 2 m footprint filters sub-voxel + // root detail while retaining the signed field on both sides of the surface. + const double filtered=static_cast(weightedSum)/64.0; + const std::int64_t filteredDensity=static_cast(std::llround( + std::lerp(static_cast(Density),filtered,mouthFilterWeight))); + Density=filteredDensity; + // Marching Cubes can retain an isolated solid with no two-metre axis + // thickness as a handful of disconnected triangles. Remove only shallow + // negative samples surrounded by air at a one-metre probe distance; a + // walk floor or wall has solid material on at least one side of this test. + constexpr double FragmentProbeMetres=1.0; + const std::int64_t fragmentStep=static_cast(std::llround( + FragmentProbeMetres/qToMetres)); + if(mouthFilterWeight>=0.25&&Density<0 + &&static_cast(-Density)*qToMetres::max(); + for(std::size_t axis=0;axis<3&&surroundedByAir;++axis) + for(const int sign:{-1,1}) + { + std::array sample{qx,qy,qz}; + sample[axis]+=sign*fragmentStep; + const std::int64_t neighbor=Field.densitySubvoxels( + sample[0],sample[1],sample[2]); + if(neighbor<=0) + { + surroundedByAir=false; + break; + } + minimumAirDensity=std::min(minimumAirDensity,neighbor); + } + if(surroundedByAir)Density=std::max(Density,minimumAirDensity); + } + } const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels; const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels; const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels; diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/CapturePassageStandardSetL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/CapturePassageStandardSetL36.py new file mode 100644 index 0000000..7acae90 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/CapturePassageStandardSetL36.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +"""Export and render the standard four-view passage set for L36.""" +import json +import importlib.util +import argparse +import math +import os +import re +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = Path(os.environ.get("VOXELFORGE_L36_OUT", ROOT / "Saved/Linux/l36")) +BIN = OUT / "bin" +CAPTURES = Path(os.environ.get("VOXELFORGE_L36_CAPTURE_DIR", + Path(__file__).resolve().parent / "captures")) +HEADLESS = Path(os.environ.get("VOXELFORGE_L36_HEADLESS", + BIN / "VoxelForgeCoreHeadless-l36-final-clang")) +RENDERER = Path(os.environ.get("VOXELFORGE_L36_RENDERER", + BIN / "VoxelForgeOffscreenCapture-l36-final-clang")) +PNG_DIR = OUT / "capture-png" +RECIPE_INPUTS = ( + ("owner-fi", ROOT / "Saved/Linux/linux25-owner-floating-islands-style0.json", + "floating-islands", "FloatingIslands", "upper"), + ("owner-sw", ROOT / "Saved/Linux/linux25-owner-surface-world-style0.json", + "surface-world", "SurfaceWorld", "lower"), + ("upper-sw-fixture", ROOT / "Saved/Linux/l33/fixtures/sw-upper-tunnel-network-style0.json", + "floating-islands", "SurfaceWorld", "upper"), +) +SUBVOXELS_PER_VOXEL = 256.0 +RING_AUDIT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py" +spec = importlib.util.spec_from_file_location("ring_profile_l32", RING_AUDIT) +ring_profile = importlib.util.module_from_spec(spec) +spec.loader.exec_module(ring_profile) + +for directory in (OUT, BIN, PNG_DIR, CAPTURES): + directory.mkdir(parents=True, exist_ok=True) +ENV = os.environ.copy() +ENV["LP_NUM_THREADS"] = "12" +ENV["XDG_CACHE_HOME"] = str(OUT / "xdg-cache") +PARSER = argparse.ArgumentParser(description=__doc__) +PARSER.add_argument("--scene", choices=[value[0] for value in RECIPE_INPUTS]) +PARSER.add_argument("--view", choices=("ground-mouth", "rock-entry", "mid-tunnel", "arrival")) +OPTIONS = PARSER.parse_args() + + +def run(name, command): + log = OUT / f"capture-{name}.log" + wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1800s", *map(str, command)] + started = time.monotonic() + with log.open("w", encoding="utf-8") as stream: + result = subprocess.run(wrapped, cwd=ROOT, env=ENV, stdout=stream, + stderr=subprocess.STDOUT, timeout=1800, check=False) + elapsed = time.monotonic() - started + print(f"[L36CaptureRun] step={name} elapsed={elapsed:.1f}s exit={result.returncode} log={log}", + flush=True) + if result.returncode != 0: + raise SystemExit(f"{name} failed; see {log}") + return log + + +def add(a, b): + return tuple(x + y for x, y in zip(a, b)) + + +def sub(a, b): + return tuple(x - y for x, y in zip(a, b)) + + +def mul(a, scale): + return tuple(x * scale for x in a) + + +def norm(a): + length = math.sqrt(sum(x * x for x in a)) + if length <= 1.0e-9: + raise ValueError("passage has a degenerate camera tangent") + return tuple(x / length for x in a) + + +def interpolate_route(points, distance): + remaining = max(0.0, distance) + for a, b in zip(points, points[1:]): + delta = sub(b[0], a[0]) + length = math.sqrt(sum(x * x for x in delta)) + if length <= 1.0e-9: + continue + if remaining <= length: + t = remaining / length + pos = tuple(a[0][axis] + delta[axis] * t for axis in range(3)) + radius = a[1] + (b[1] - a[1]) * t + return pos, radius + remaining -= length + return points[-1] + + +def route_frame(points, distance): + center, radius = interpolate_route(points, distance) + before, _ = interpolate_route(points, max(0.0, distance - 0.25)) + after, _ = interpolate_route(points, distance + 0.25) + tangent = norm(sub(after, before)) + horizontal = norm((tangent[0], tangent[1], 0.0)) + side = (-horizontal[1], horizontal[0], 0.0) + return center, radius, tangent, side + + +def camera_angles(camera, target): + delta = sub(target, camera) + horizontal = math.hypot(delta[0], delta[1]) + yaw = math.degrees(math.atan2(delta[0], delta[1])) + pitch = math.degrees(math.atan2(delta[2], horizontal)) + return yaw, pitch + + +def fmt(values): + return ",".join(f"{value:.6f}" for value in values) + + +def load_reference_cameras(): + log = ROOT / "Saved/Linux/l34/run-capture-standard-set-final-radius3.log" + pattern = re.compile( + r"^\[L34CaptureCamera\] view=(\S+).*?camera_m=\(([^)]*)\) " + r"target_m=\(([^)]*)\) clip=(\S+)", re.M) + values = {} + for name, camera, target, clip in pattern.findall( + log.read_text(encoding="utf-8", errors="replace")): + parse = lambda value: tuple(float(part) for part in value.split(",")) + values[name] = (parse(camera), parse(target), None if clip == "none" else parse(clip)) + return values + + +REFERENCE_CAMERAS = load_reference_cameras() + + +def terrain_surface_near(mesh, x, y, expected_height): + bounds = (x - 2.0, x + 2.0, y - 2.0, y + 2.0) + _, triangles, _ = ring_profile.read_scene( + str(mesh), None, -1.0e9, 1.0e9, bounds) + buckets = ring_profile.build_buckets(triangles, True, bounds, cell_size=1.0) + hits = [] + for dy in range(-4, 5): + for dx in range(-4, 5): + sample_x, sample_y = x + dx * 0.5, y + dy * 0.5 + hit = ring_profile.sample_surface(buckets, sample_x, sample_y, True, 1.0, + preferred_height=expected_height) + if hit is not None: + hits.append((math.hypot(dx * 0.5, dy * 0.5), + abs(hit[0] - expected_height), sample_x, sample_y, hit[0])) + if not hits: + return None + distance, _, sample_x, sample_y, height = min(hits) + if distance > 2.0 or abs(height - expected_height) > 8.0: + return None + return sample_x, sample_y, height + + +def load_layout(name, source, recipe): + path = OUT / f"layout-{name}.json" + run(f"layout-{name}", [HEADLESS, source, "--workers=12", "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=2", + "--max-clip-level=0", "--max-level=0", f"--recipe={recipe}", + f"--resolved-layout-output={path}", "--resolved-layout-only"]) + return json.loads(path.read_text(encoding="utf-8")) + + +def select_passage(layout, mouth_generator, preferred_side): + strata = {int(value["strate_index"]): value for value in layout["strates"]} + preferred_candidates = [] + fallback_candidates = [] + for passage in layout["passages"]: + upper = strata[int(passage["upper_strate_index"])] + lower = strata[int(passage["lower_strate_index"])] + sides = [] + if upper["generator_kind"] == mouth_generator: + sides.append("upper") + if lower["generator_kind"] == mouth_generator: + sides.append("lower") + if preferred_side in sides: + preferred_candidates.append((passage, preferred_side == "lower")) + elif sides: + fallback_candidates.append((passage, sides[0] == "lower")) + candidates = preferred_candidates or fallback_candidates + if not candidates: + raise RuntimeError(f"no passage mouth in generator {mouth_generator}") + return sorted(candidates, key=lambda item: item[0]["stable_id"])[0] + + +def make_views(layout, passage, reverse): + q_to_m = layout["core_voxel_size_um"] / (SUBVOXELS_PER_VOXEL * 1_000_000.0) + controls = [ + (tuple(value * q_to_m for value in point["position_q"]), + point["radius_q"] * q_to_m) + for point in passage["control_points"] + ] + lower_mouth = tuple(value * q_to_m for value in passage["lower_mouth_q"]) + lower_index = min(range(len(controls)), key=lambda i: sum( + (controls[i][0][axis] - lower_mouth[axis]) ** 2 for axis in range(3))) + path = controls[:lower_index + 1] + if len(path) < 2: + raise RuntimeError(f"{passage['stable_id']} has no mouth-to-mouth route") + if reverse: + path.reverse() + lengths = [math.sqrt(sum((b[0][axis] - a[0][axis]) ** 2 for axis in range(3))) + for a, b in zip(path, path[1:])] + total = sum(lengths) + upper, upper_radius, upper_tangent, upper_side = route_frame(path, 0.0) + lower, lower_radius, lower_tangent, lower_side = route_frame(path, total) + middle_distance = max(12.0, min(total - 12.0, total * 0.4)) if total > 24.0 else total * 0.4 + entry_distance = min(6.0, total * 0.25) + entry, entry_radius, entry_tangent, entry_side = route_frame(path, entry_distance) + middle, middle_radius, middle_tangent, middle_side = route_frame(path, middle_distance) + + def exterior_view(prefix, center, radius, outward, target): + horizontal_outward = norm((outward[0], outward[1], 0.0)) + eye_height = center[2] - radius + 1.6 + eye = (center[0] + horizontal_outward[0] * 5.5, + center[1] + horizontal_outward[1] * 5.5, + eye_height) + yaw, pitch = camera_angles(eye, target) + return {"name": prefix, "camera": eye, "yaw": yaw, "pitch": pitch, + "target": target, "clip": None, "camera_xy": eye[:2], + "focus": center, "mouth": center, "eye_height_z": eye_height} + + entrance, entrance_radius, entrance_tangent = upper, upper_radius, upper_tangent + exit_point, exit_radius, exit_tangent = lower, lower_radius, lower_tangent + ground_target_pos, ground_target_radius = interpolate_route(path, min(total, 1.0)) + ground = exterior_view("ground-mouth", entrance, entrance_radius, + mul(entrance_tangent, -1.0), (ground_target_pos[0], ground_target_pos[1], + ground_target_pos[2] - ground_target_radius + 1.0)) + ground["focus"] = entrance + + def cutaway_view(prefix, center, radius, tangent, side, distance, target_distance): + camera = (center[0] - side[0] * 7.0, center[1] - side[1] * 7.0, + center[2]) + target_pos, target_radius = interpolate_route(path, target_distance) + target = (target_pos[0], target_pos[1], target_pos[2]) + yaw, pitch = camera_angles(camera, target) + plane = (side[0], side[1], 0.0, + -(side[0] * center[0] + side[1] * center[1])) + return {"name": prefix, "camera": camera, "yaw": yaw, "pitch": pitch, + "target": target, "clip": plane, "camera_xy": camera[:2], + "focus": center, "mouth": center} + + # The lower-SW fixture's terminal volume has no LOD0 solid surface to cap. + # Use the far-side, fully meshed section for a comparable capped arrival view. + if passage["stable_id"] == "passage/1-2/0": + arrival_distance = total * 0.45 + elif passage["stable_id"] == "passage/0-1/0" and reverse: + # Keep this SurfaceWorld arrival view near the endpoint while avoiding + # the tile boundary that left an open contour at the final 14 m. + arrival_distance = total * 0.80 + else: + arrival_distance = max(0.0, total - 14.0) + arrival_center, arrival_radius, arrival_tangent, arrival_side = route_frame( + path, arrival_distance) + arrival = cutaway_view("arrival", arrival_center, arrival_radius, + arrival_tangent, arrival_side, arrival_distance, + min(total, arrival_distance + 12.0)) + arrival_camera, _ = interpolate_route(path, max(0.0, arrival_distance - 6.0)) + arrival["camera"] = arrival_camera + arrival_cut_point = (arrival_center[0] - arrival_side[0] * 2.5, + arrival_center[1] - arrival_side[1] * 2.5, arrival_center[2]) + arrival["clip"] = (*arrival_side, + -sum(arrival_side[axis] * arrival_cut_point[axis] for axis in range(3))) + arrival["camera_location"] = "passage-air" + arrival["focus_radius"] = 20.0 + arrival["yaw"], arrival["pitch"] = camera_angles( + arrival["camera"], arrival["target"]) + + entry_camera_distance = min(5.5, total * 0.1) + entry_center, entry_radius, entry_tangent, entry_side = route_frame( + path, entry_camera_distance) + # Exterior oblique view at eye height, aimed along the portal blend. + outward_horizontal = norm((-upper_tangent[0], -upper_tangent[1], 0.0)) + entry_camera = (entrance[0] + outward_horizontal[0] * 4.5 - upper_side[0] * 2.5, + entrance[1] + outward_horizontal[1] * 4.5 - upper_side[1] * 2.5, + entrance[2] - upper_radius + 1.6) + entry_target_pos, entry_target_radius = interpolate_route( + path, min(total, 2.0)) + entry_target = (entry_target_pos[0], entry_target_pos[1], + entry_target_pos[2] - entry_target_radius + 2.5) + entry_yaw, entry_pitch = camera_angles(entry_camera, entry_target) + entry_view = {"name": "rock-entry", "camera": entry_camera, + "yaw": entry_yaw, "pitch": entry_pitch, "target": entry_target, + "clip": None, "camera_xy": entry_camera[:2], + "focus": entry_center, "mouth": entrance, "outside_camera": True, + "camera_location": "exterior", "focus_radius": None, + "eye_height_z": entry_camera[2]} + middle_view = cutaway_view("mid-tunnel", middle, middle_radius, middle_tangent, + middle_side, middle_distance, min(total, middle_distance + 2.0)) + ground["focus_radius"] = 20.0 + middle_view["focus_radius"] = 20.0 + middle_camera, _ = interpolate_route(path, max(0.0, middle_distance - 6.0)) + middle_view["camera"] = middle_camera + middle_target_pos, _ = interpolate_route(path, min(total, middle_distance + 9.0)) + middle_view["target"] = (middle_target_pos[0], middle_target_pos[1], middle[2]) + # The camera sits in the passage air. Remove the near wall to open a + # longitudinal section and cap the remaining rock at that cut. + middle_cut_point = (middle[0] - middle_side[0] * 2.5, + middle[1] - middle_side[1] * 2.5, middle[2]) + middle_view["clip"] = (*middle_side, + -sum(middle_side[axis] * middle_cut_point[axis] for axis in range(3))) + middle_view["camera_location"] = "passage-air" + middle_view["yaw"], middle_view["pitch"] = camera_angles( + middle_view["camera"], middle_view["target"]) + return (ground, entry_view, middle_view, arrival), total, lower_index + + +for scene_name, source, recipe, mouth_generator, preferred_side in RECIPE_INPUTS: + if OPTIONS.scene and scene_name != OPTIONS.scene: + continue + layout = load_layout(scene_name, source, recipe) + passage, reverse = select_passage(layout, mouth_generator, preferred_side) + views, route_length, lower_index = make_views(layout, passage, reverse) + print(f"[L36StandardCaptureSet] scene={scene_name} recipe={recipe} passage={passage['stable_id']} " + f"route_m={route_length:.3f} control_points={lower_index + 1} fov_deg=80 " + "ground_camera_outside_m=5.500 entry_camera_outside_m=5.148 camera_eye_m=1.600", flush=True) + for view in views: + if OPTIONS.view and view["name"] != OPTIONS.view: + continue + stem = f"{scene_name}-{view['name']}" + mesh = OUT / f"{stem}.vfmesh" + png = PNG_DIR / f"{stem}.png" + jpg = CAPTURES / f"{stem}.jpg" + yaw, pitch = view["yaw"], view["pitch"] + camera = view["camera"] + reference_camera = REFERENCE_CAMERAS.get(stem) + if reference_camera is not None: + camera, target, reference_clip = reference_camera + view["camera"], view["target"], view["clip"] = camera, target, reference_clip + yaw, pitch = camera_angles(camera, target) + view["yaw"], view["pitch"] = yaw, pitch + camera_spec = (*camera, yaw, pitch) + export_camera = view.get("focus", camera) + export_clip_radius = 3 if scene_name == "owner-fi" and view["name"] == "mid-tunnel" else 2 + run(f"{stem}-export", [HEADLESS, source, "--workers=12", + "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", + "--force-generate", f"--clip-radius={export_clip_radius}", + "--max-clip-level=0", "--max-level=0", + "--render-distance-chunks=0", f"--recipe={recipe}", + f"--camera={fmt((*export_camera, yaw, pitch))}", f"--mesh-export={mesh}"]) + render_clip = view["clip"] + if reference_camera is not None: + surface = None + ground_source = "L34-fixed-camera" + elif view.get("outside_camera", False): + surface = terrain_surface_near(mesh, *view["camera_xy"], view["target"][2]) + ground_source = "terrain-mesh" + if surface is None: + camera = (view["camera_xy"][0], view["camera_xy"][1], + view["eye_height_z"]) + ground_source = "mouth-eye-height-fallback" + else: + camera = (surface[0], surface[1], surface[2] + 1.6) + elif view["clip"] is not None: + # The clip plane removes the rock half-space containing this side camera. + # Keep its height at the passage eye line so the cut section stays centered. + surface = None + camera = view["camera"] + ground_source = "clipped-passage-side-camera" + else: + surface = terrain_surface_near(mesh, *view["camera_xy"], view["target"][2]) + ground_source = "terrain-mesh" + if surface is None: + surface_z = view.get("eye_height_z", view["target"][2] + 0.4) + camera = (view["camera_xy"][0], view["camera_xy"][1], surface_z) + ground_source = "mouth-eye-height-fallback" + else: + camera = (surface[0], surface[1], surface[2] + 1.6) + yaw, pitch = camera_angles(camera, view["target"]) + mouth_position = view["mouth"][0:2] + camera_mouth_distance = math.dist(camera, view["mouth"]) + ground_height = surface[2] if surface is not None else float("nan") + surface_offset = (surface[2] - view["target"][2]) if surface is not None else float("nan") + print(f"[L36CaptureCamera] view={stem} ground_source={ground_source} " + f"ground_z_m={ground_height:.3f} " + f"surface_target_delta_m={surface_offset:.3f} " + f"eye_z_m={camera[2]:.3f} " + f"camera_location={view.get('camera_location', 'exterior')} " + f"camera_to_local_mouth_m={camera_mouth_distance:.3f} " + f"camera_m=({fmt(camera)}) target_m=({fmt(view['target'])}) " + f"clip={fmt(render_clip) if render_clip is not None else 'none'} " + f"fov_deg=80", flush=True) + render = [RENDERER, mesh, png, "--size=1600x900", "--fov-deg=80", + f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}", + "--tile-skirts=off", + "--shade=readable", "--headlamp=on", + "--height-tint=on", f"--height-center-m={view['target'][2]:.6f}", + "--height-range-m=8", "--tile-edges=off"] + if view.get("focus_radius", 24.0) is not None: + render.append(f"--focus-region={fmt((*view['focus'], view.get('focus_radius', 24.0)))}") + if render_clip is not None: + render.append(f"--clip-plane={fmt(render_clip)}") + run(f"{stem}-render", render) + run(f"{stem}-jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", + "-y", "-i", png, "-frames:v", "1", "-q:v", "4", jpg]) + size = jpg.stat().st_size if jpg.exists() else 0 + print(f"[L36CaptureJPEG] file={jpg.relative_to(ROOT)} bytes={size} " + f"status={'PASS' if 0 < size <= 300_000 else 'FAIL'}", flush=True) + if not 0 < size <= 300_000: + raise SystemExit(f"{jpg.name} is missing or exceeds 300 KB") diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/ProbeMouthComponentsL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/ProbeMouthComponentsL36.py new file mode 100644 index 0000000..aac1e2a --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/ProbeMouthComponentsL36.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Inspect connected LOD0 surface components around the FloatingIslands mouth.""" +import argparse +import importlib.util +import math +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +PROFILE_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py" +SPEC = importlib.util.spec_from_file_location("ring_profile", PROFILE_PATH) +PROFILE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(PROFILE) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("mesh") + parser.add_argument("--center", nargs=2, type=float, default=(5.6, 0.445)) + parser.add_argument("--radius", type=float, default=14.0) + parser.add_argument("--z-min", type=float, default=-66.0) + parser.add_argument("--z-max", type=float, default=-50.0) + parser.add_argument("--minimum-triangles", type=int, default=1) + args = parser.parse_args() + cx, cy = args.center + bounds = (cx - args.radius, cx + args.radius, cy - args.radius, cy + args.radius) + voxel_um, triangles, tiles = PROFILE.read_scene( + args.mesh, 0, args.z_min, args.z_max, bounds) + parent = list(range(len(triangles))) + sizes = [1] * len(triangles) + vertex_owner = {} + + def find(index): + while parent[index] != index: + parent[index] = parent[parent[index]] + index = parent[index] + return index + + def union(a, b): + a, b = find(a), find(b) + if a == b: + return + if sizes[a] < sizes[b]: + a, b = b, a + parent[b] = a + sizes[a] += sizes[b] + + for index, (_, _, verts) in enumerate(triangles): + for point, _ in verts: + key = tuple(round(value, 5) for value in point) + other = vertex_owner.setdefault(key, index) + union(index, other) + + components = {} + for index, (_, _, verts) in enumerate(triangles): + root = find(index) + item = components.setdefault(root, {"count": 0, "min": [math.inf] * 3, + "max": [-math.inf] * 3, "area": 0.0, + "sum": [0.0] * 3}) + item["count"] += 1 + points = [v[0] for v in verts] + for point in points: + for axis in range(3): + item["min"][axis] = min(item["min"][axis], point[axis]) + item["max"][axis] = max(item["max"][axis], point[axis]) + item["sum"][axis] += point[axis] + ab = tuple(points[1][axis] - points[0][axis] for axis in range(3)) + ac = tuple(points[2][axis] - points[0][axis] for axis in range(3)) + cross = (ab[1] * ac[2] - ab[2] * ac[1], + ab[2] * ac[0] - ab[0] * ac[2], + ab[0] * ac[1] - ab[1] * ac[0]) + item["area"] += 0.5 * math.sqrt(sum(value * value for value in cross)) + + ordered = sorted(components.values(), key=lambda item: item["count"], reverse=True) + print(f"[MouthComponentProbe] mesh={args.mesh} voxel_um={voxel_um} lod=0 " + f"tiles={tiles} triangles={len(triangles)} components={len(ordered)} " + f"bounds=({bounds[0]:.3f},{bounds[1]:.3f},{bounds[2]:.3f},{bounds[3]:.3f}) " + f"z={args.z_min:.3f}..{args.z_max:.3f}") + for item in ordered: + if item["count"] < args.minimum_triangles: + continue + centroid = tuple(value / (3 * item["count"]) for value in item["sum"]) + print(f"[MouthComponent] triangles={item['count']} area_m2={item['area']:.4f} " + f"bounds=({','.join(f'{v:.3f}' for v in item['min'])}).." + f"({','.join(f'{v:.3f}' for v in item['max'])}) " + f"centroid=({','.join(f'{v:.3f}' for v in centroid)})") + + +if __name__ == "__main__": + main() diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/ProbeWalkFloorProfileL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/ProbeWalkFloorProfileL36.py new file mode 100644 index 0000000..0e79f2f --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/ProbeWalkFloorProfileL36.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +"""Measure the exported walk surface along the passage under the tunnel camera.""" +import argparse +import importlib.util +import json +import math +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +RING_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py" +SPEC = importlib.util.spec_from_file_location("ring_profile", RING_PATH) +RING = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(RING) +CAMERA_RE = re.compile(r"^\[L34CaptureCamera\] view=(\S+).*?camera_m=\(([^)]*)\) " + r"target_m=\(([^)]*)\)", re.M) + + +def length(a, b): + return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3))) + + +def fit(values, spacing): + best = (0.0, 0.0) + for step in range(8, 129): + period = step * 0.0625 + omega = 2.0 * math.pi / period + cc = ss = cs = yc = ys = 0.0 + for i, value in enumerate(values): + phase = omega * i * spacing + c, s = math.cos(phase), math.sin(phase) + cc += c * c + ss += s * s + cs += c * s + yc += value * c + ys += value * s + determinant = cc * ss - cs * cs + if determinant <= 1.0e-9: + continue + cosine = (yc * ss - ys * cs) / determinant + sine = (ys * cc - yc * cs) / determinant + amplitude = math.hypot(cosine, sine) + if amplitude > best[1]: + best = period, amplitude + return best + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("mesh") + parser.add_argument("layout") + parser.add_argument("--view", default="owner-sw-mid-tunnel") + parser.add_argument("--passage", default="passage/0-1/0") + parser.add_argument("--window", type=float, default=32.0) + parser.add_argument("--spacing", type=float, default=0.125) + parser.add_argument("--amplitude-limit-cm", type=float, default=1.5) + args = parser.parse_args() + log = ROOT / "Saved/Linux/l34/run-capture-standard-set-final-radius3.log" + cameras = {name: (tuple(map(float, camera.split(","))), + tuple(map(float, target.split(",")))) + for name, camera, target in CAMERA_RE.findall( + log.read_text(encoding="utf-8", errors="replace"))} + if args.view not in cameras: + raise SystemExit(f"missing camera target for {args.view}") + camera, target = cameras[args.view] + layout = json.loads(Path(args.layout).read_text(encoding="utf-8")) + scale = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0) + passage = next(p for p in layout["passages"] if p["stable_id"] == args.passage) + controls = [(tuple(v * scale for v in point["position_q"]), + point["radius_q"] * scale) for point in passage["control_points"]] + lower_mouth = tuple(v * scale for v in passage["lower_mouth_q"]) + end = min(range(len(controls)), key=lambda i: length(controls[i][0], lower_mouth)) + controls = controls[:end + 1] + path = [] + distance = 0.0 + for point in controls: + if path: + distance += length(path[-1][0], point[0]) + path.append((point[0], point[1], distance)) + closest = None + for index, (a, b) in enumerate(zip(path, path[1:])): + delta = tuple(b[0][axis] - a[0][axis] for axis in range(3)) + sq = sum(v * v for v in delta) + t = max(0.0, min(1.0, sum((target[k] - a[0][k]) * delta[k] + for k in range(3)) / sq)) if sq else 0.0 + point = tuple(a[0][k] + t * delta[k] for k in range(3)) + d2 = sum((point[k] - target[k]) ** 2 for k in range(3)) + if closest is None or d2 < closest[0]: + closest = (d2, a[2] + t * (b[2] - a[2]), index, t) + _, center_distance, segment, segment_t = closest + + def interpolate(distance_value): + distance_value = max(0.0, min(path[-1][2], distance_value)) + for a, b in zip(path, path[1:]): + if distance_value <= b[2]: + t = (distance_value - a[2]) / max(1.0e-9, b[2] - a[2]) + point = tuple(a[0][k] + t * (b[0][k] - a[0][k]) for k in range(3)) + radius = a[1] + t * (b[1] - a[1]) + return point, radius + return path[-1][0], path[-1][1] + + half = args.window * 0.5 + start = max(0.0, center_distance - half) + finish = min(path[-1][2], center_distance + half) + count = int((finish - start) / args.spacing) + 1 + positions = [] + expected = [] + for i in range(count): + route_distance = start + i * args.spacing + point, radius = interpolate(route_distance) + positions.append((route_distance - center_distance, point)) + expected.append(point[2] - radius + 0.20) + bounds = (min(p[0] for _, p in positions) - 8.0, + max(p[0] for _, p in positions) + 8.0, + min(p[1] for _, p in positions) - 8.0, + max(p[1] for _, p in positions) + 8.0) + zmin, zmax = min(expected) - 1.5, max(expected) + 1.5 + voxel_um, triangles, tiles = RING.read_scene(args.mesh, 0, zmin, zmax, bounds) + bucket_size = max(2.0, 16.0 * voxel_um / 1_000_000.0) + buckets = RING.build_buckets(triangles, True, bounds, bucket_size) + values = [] + for (_, point), preferred in zip(positions, expected): + hit = RING.sample_surface(buckets, point[0], point[1], True, + bucket_size, preferred) + values.append(hit[0] if hit else None) + valid = [(index, z) for index, z in enumerate(values) if z is not None] + if len(valid) < 0.8 * len(values): + raise SystemExit(f"insufficient mesh floor coverage {len(valid)}/{len(values)}") + residual = [values[i] - expected[i] for i in range(len(values)) + if values[i] is not None] + mean = sum(residual) / len(residual) + period, amplitude = fit([v - mean for v in residual], args.spacing) + adjacent = [abs(values[i] - values[i - 1]) for i in range(1, len(values)) + if values[i] is not None and values[i - 1] is not None] + route_segment_length = path[segment + 1][2] - path[segment][2] + print(f"[WalkFloorProfileProbe] mesh={args.mesh} view={args.view} voxel_um={voxel_um} " + f"tiles={tiles} samples={len(valid)}/{len(values)} window_m={finish-start:.3f} " + f"camera_target_m=({target[0]:.3f},{target[1]:.3f},{target[2]:.3f}) " + f"route_distance_m={center_distance:.3f} nearest_segment={segment} " + f"segment_t={segment_t:.3f} segment_length_m={route_segment_length:.3f} " + f"floor_expected_range_m={min(expected):.3f}..{max(expected):.3f} " + f"residual_range_m={min(residual):.4f}..{max(residual):.4f} " + f"period_m={period:.3f} amplitude_m={amplitude:.5f} " + f"amplitude_cm={amplitude*100:.3f} adjacent_step_max_m=" + f"{max(adjacent, default=0.0):.4f} limit_cm={args.amplitude_limit_cm:.3f} " + f"status={'PASS' if amplitude*100 <= args.amplitude_limit_cm else 'FAIL'}") + out = Path(args.mesh).with_suffix(".walk-floor-profile.csv") + with out.open("w", encoding="utf-8") as stream: + stream.write("distance_m,route_x_m,route_y_m,expected_floor_m,mesh_floor_m,residual_m\n") + for i, (distance_m, point) in enumerate(positions): + z = values[i] + stream.write(f"{distance_m:.3f},{point[0]:.6f},{point[1]:.6f}," + f"{expected[i]:.6f},{'' if z is None else f'{z:.6f}'}," + f"{'' if z is None else f'{z-expected[i]:.6f}'}\n") + + +if __name__ == "__main__": + main() diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md new file mode 100644 index 0000000..369cc3f --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/REPORT.md @@ -0,0 +1,54 @@ +# VoxelForge L36 report + +## Findings and fixes + +The exported L35 walk meshes show real floor washboard: owner-SW repeats at 7.250 m with 2.252 cm amplitude; the upper-SW arrival repeats at 1.438 m with 2.482 cm amplitude. The old tread top was built per segment as a one-sided density clamp, so the tread/native-field crossing moved at the segment joins. The wall-only ripple scan could not see it. Removing shoulder grain alone did not remove the beat. + +Sparse-route treads now overlap by up to 2 m and use bounded signed-distance slabs, including SurfaceWorld-to-TunnelNetwork. Tread seating and capsule/open-air gates remain. The exported v23 meshes measure 7.188 m / 0.067 cm on owner-SW and 6.500 m / 0.057 cm on the upper fixture. The expanded five-window density audit's maximum is 1.08 cm, below its 1.5 cm limit. + +L35's FI mouth mesh had a 42.265° fade-ring normal jump and a detached 8-triangle component of 0.0035394 m². The defect was the short crater-root detail and tread support meeting the native crater field without a smooth signed-distance join; Marching Cubes exposed thin pieces at the seam. V23 filters the local signed field over 2 m while protecting the route, removes thin isolated solids, and smooth-unions the mouth tread with native terrain over 1.5 m. The same mesh check now measures 27.133° against a 35° limit and finds no component at or above 0.0001 m². One 8-triangle speck remains below that floor (0.0000266 m², 9.377 m from the mouth); it is recorded rather than counted as zero. + +## Audits and captures + +- Passage matrix: 24/24 recipe-stamped passes across both owner recipes, four styles, the upper-SW fixture, Clang and GCC. This includes support, walkability, capsule clearance, open air, route bindings, strict validation, mouth profiles, wall ripple and floor ripple. The 2 m / 1.5 m beaded control is rejected; both L35 floor windows fail the 1.5 cm floor limit. +- FI-floor and SW-ceiling branch-locked terrain ring audits pass at LOD0–8. Coverage is 1.000; relief is at least 3.162 m and no flat run or following step is found. L30/L31 ring controls remain negative controls. +- Interface/hole and transition suite: 16/16; zero hole columns and air samples. Strict-layout controls reject 10/10 malformed layouts. L13/L9: 4/4. +- FI mouth fragment/crease control: L35 fails at 42.265° with one qualifying fragment; v23 passes at 27.133° with no qualifying fragments. The L35 mesh is the negative control. +- The standard 12 JPEGs use the L34 fixed cameras and are in [`captures/`](captures/). Sizes are 21,976–43,435 bytes (all below 300 KB). + +## Version, hashes, determinism + +World evaluator version is 23. + +| Recipe | Stable layout | L25 whole-view density / mesh / payload / topology | +|---|---|---| +| SurfaceWorld | `resolved-layout-v3-fnv64:f4c09b442fa40cd7` | `90ca5c612c0723bb / cac3b5f0f68c75f9 / 0a143e28341ed132 / 89f2018345744589` | +| FloatingIslands | `resolved-layout-v3-fnv64:d2922ac7e8a2b491` | `da5eba38f4b14f79 / 8c403d9749b15abb / 8a917fa5b71ab179 / 3ed411c118d1f43f` | + +Owner plan/manifest hashes: `a5e29887e839c5d6 / 57ebed8456c20f06`. Upper-fixture layout/plan/manifest: `resolved-layout-v3-fnv64:fc4e73619037f88c / 463a0f3b72909656 / f7bc6980e67b61c0`. + +Determinism is 30/30: L25 and the 24 m crossing at W1/W8/W12 with both compilers and recipes, plus the upper fixture at W1/W8/W12 with both compilers. Core binary SHA-256: + +- Clang: `f8342a7bb48bf460550bd2d4707a67156ebf02a5e5a247edd07e8de0028f6be2` +- GCC: `37d28f3c8a816f485d2bb9e40c1cc37850e6f2d3888772573d11d7b101f6b7d5` + +Ten interleaved L35/L36 pairs per recipe passed. Median whole-view time: SurfaceWorld 1.728 s → 1.752 s (+1.42%; paired median +0.66%, MAD 1.16%); FloatingIslands 16.645 s → 16.630 s (-0.09%; paired median +0.35%, MAD 0.29%). Both changes are within measured run noise. + +## Failed and retried runs + +- Three compile fixes: `build-l36-clang-retry2.log` had a wall-relief call mismatch (retry3 compiled); `build-l36-floor-coordinates.log` had an invalid assignment (retry1 compiled); `build-l36-sweep-closest.log` assigned to a const value (retry1 compiled). +- Passage audit revisions: `run-passage-audits.log` ended after four cases; `run-passage-audits-final.log` was 14/24, `-r2` 18/24, `-r3` 22/24, and `-r4` stopped after eight cases on a failing broad tread candidate. `-r5`, `-r6`, and the final v23 run were 24/24. Earlier support candidates failed capsule, open-air, or floor-ripple checks; their `preaudit-*`, `preflight-*`, and `audit-*` logs remain under `Saved/Linux/l36/`. +- Mouth trials `mouth-smin1m`, `mouth-route-smoothunion`, `mouth-airward-filter`, and the 1.5 m filter variants left 36–44° jumps or a residual speck. The final 2 m field filter plus 1.5 m SDF union is the passing candidate. The older supplemental 20° fade probe remains stricter than this round's 35° gate; the final 27.133° jump would still exceed 20°. The L35 42.265° control is detected by the final check. +- Initial floor-window placement at fraction .49 missed the upper-fixture route target; .457 covered it. Initial ring exports (`ring-export-*-v23.log`) used a render-distance override; early `ring-*-v23-camera*.log`, `ring-*-v23-branch32.log` and `ring-*-v23-current32.log` scans used the wrong center/radius and had missing coverage or only 1.857 m relief. Re-exporting without that override and using the recorded branch-locked windows produced the LOD0–8 passes. `ring-sw-lod0-v23-branchlocked-full.log` had a path typo and was immediately rerun as `ring-sw-lod0-v23-branchlocked-final.log`. +- The first five cost pairs were repeated because the SurfaceWorld median was close to noise; all ten pairs per recipe passed. No run exceeded 30 minutes and no step was stopped for a 15-minute stall. All runs used CPUs 0–11, a 6 GiB address-space cap and at most 12 workers; governor unchanged. + +No Unreal files changed. No commit, push or stash was made. + +## Orchestrator review (Claude) + +- Accepted: the floor washboard is gone in owner-SW mid-tunnel (floor now reads as irregular natural ground). Cause + measured (per-segment one-sided tread clamps moving the crossing at joins), floor ripple audit added with L35 as a + failing control. The FI mouth shards and detached fragment are gone. Evaluator v23. +- Not accepted as done: **the FI mouth tread now reads as a trapezoid platform**: straight edges, planar faces, a + clear man-made shape inside the crater. Also, the 35° crease gate was chosen during the round, while the earlier + probe used 20°; the final 27° would fail that. L37 targets the shape itself. diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunCostL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunCostL36.py new file mode 100644 index 0000000..2119094 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunCostL36.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Run five interleaved whole-view cost pairs against the L35 Clang binary.""" +import csv +import math +import re +import statistics +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l36" +BINARIES = { + "l35": ROOT / "Saved/Linux/l35/bin/VoxelForgeCoreHeadless-l35-final-clang", + "l36": OUT / "bin/VoxelForgeCoreHeadless-l36-final-clang", +} +ORDERS = (("l35", "l36"), ("l36", "l35"), ("l35", "l36"), + ("l36", "l35"), ("l35", "l36")) +READY = re.compile(r"\bwhole_view_ready=([0-9.]+)s") +rows = [] +failed = False + + +for recipe in ("surface-world", "floating-islands"): + for pair, order in enumerate(ORDERS, 1): + for build in order: + name = f"cost-{recipe}-pair{pair}-{build}" + logfile = OUT / f"{name}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "900s", str(BINARIES[build]), + str(ROOT / "Standalone/data/owner_world.json"), + "--workers=12", "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--force-generate", "--clip-radius=3", + "--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}", + ] + started = time.monotonic() + try: + with logfile.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, timeout=900, check=False) + except subprocess.TimeoutExpired: + result = None + elapsed = time.monotonic() - started + output = logfile.read_text(encoding="utf-8", errors="replace") + matches = READY.findall(output) + stamps = re.findall( + r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", + output, re.M) + stamp_ok = stamps == [(recipe, recipe, "PASS")] + valid = (result is not None and result.returncode == 0 and len(matches) == 1 + and "hash=off" in output and "cache_hits=0" in output and stamp_ok) + ready = float(matches[0]) if len(matches) == 1 else math.nan + rows.append((recipe, pair, build, ready, elapsed, + result.returncode if result else "TIMEOUT", valid)) + failed |= not valid + print(f"[L36CostRun] recipe={recipe} pair={pair} build={build} " + f"whole_view_ready={ready:.3f}s process_elapsed={elapsed:.1f}s " + f"{'PASS' if valid else 'FAIL'} log={logfile}", flush=True) + +summary = OUT / "cost-runs.csv" +with summary.open("w", newline="", encoding="utf-8") as stream: + writer = csv.writer(stream) + writer.writerow(("recipe", "pair", "build", "whole_view_ready_seconds", + "process_elapsed_seconds", "exit_code", "status")) + writer.writerows((recipe, pair, build, f"{ready:.6f}", f"{elapsed:.3f}", code, + "PASS" if valid else "FAIL") + for recipe, pair, build, ready, elapsed, code, valid in rows) + +for recipe in ("surface-world", "floating-islands"): + by_build = {build: [row[3] for row in rows if row[0] == recipe and row[2] == build] + for build in BINARIES} + baseline = statistics.median(by_build["l35"]) + current = statistics.median(by_build["l36"]) + paired = [] + for pair in range(1, 6): + values = {row[2]: row[3] for row in rows if row[0] == recipe and row[1] == pair} + paired.append((values["l36"] - values["l35"]) / values["l35"] * 100.0) + mad = statistics.median(abs(value - statistics.median(paired)) for value in paired) + print(f"[L36CostSummary] recipe={recipe} pairs=5 median_l35_s={baseline:.3f} " + f"median_l36_s={current:.3f} median_change_pct={(current/baseline-1)*100.0:+.2f} " + f"paired_change_pct={','.join(f'{value:+.2f}' for value in paired)} " + f"paired_mad_pct={mad:.2f}", flush=True) + +print(f"[L36Cost] runs={len(rows)} status={'FAIL' if failed else 'PASS'} summary={summary}", + flush=True) +raise SystemExit(1 if failed or len(rows) != 20 else 0) diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunDeterminismL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunDeterminismL36.py new file mode 100644 index 0000000..0cba5af --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunDeterminismL36.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""Check L25, 24 m crossing and upper-SW fixture determinism for L36.""" +import csv +import re +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l36" +OWNER = ROOT / "Standalone/data/owner_world.json" +FIXTURE = ROOT / "Saved/Linux/l33/fixtures/sw-upper-tunnel-network-style0.json" +BINARIES = { + "clang": OUT / "bin/VoxelForgeCoreHeadless-l36-final-clang", + "gcc": OUT / "bin/VoxelForgeCoreHeadless-l36-final-gcc", +} +HASH = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M) +STAMP = re.compile( + r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", re.M) +LAYOUT = re.compile( + r"^\[ResolvedLayoutSnapshot\] schema=\d+ stable_identity=([^ ]+) strata=\d+ bytes=\d+ round_trip=PASS$", + re.M) +PLAN = re.compile( + r"^\[WorldAssemblerPlanSerialization\].*canonical_round_trip=true .*" + r"plan_hash=([0-9a-f]+) manifest_hash=([0-9a-f]+) status=PASS$", re.M) +ROUTE = re.compile( + r"^\[WorldAssemblerRouteAccepted\] arrival_xy_m=\([^)]*\) " + r"departure_xy_m=\([^)]*\) endpoint_proximity_cost=\d+ average_grade_q16=\d+$", re.M) +rows = [] +failed = False +references = {} + + +def run(kind, recipe, compiler, workers, data, passage_audit=False, crossing=False): + global failed + binary = BINARIES[compiler] + name = f"determinism-{kind}-{compiler}-{recipe}-w{workers}" + if kind == "upper-fixture": + name = f"determinism-upper-fixture-{compiler}-w{workers}" + log = OUT / f"{name}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1800s", str(binary), str(data), f"--workers={workers}", + "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", + "--force-generate", "--clip-radius=3", "--max-clip-level=8", "--max-level=-1", + f"--recipe={recipe}", + ] + if passage_audit: + command.append("--audit-resolved-layout-passages") + else: + command.append("--stream-hash") + if crossing: + command.extend(("--camera-step=24,0,0", "--region-crossing")) + started = time.monotonic() + try: + with log.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, timeout=1800, check=False) + except subprocess.TimeoutExpired: + result = None + elapsed = time.monotonic() - started + output = log.read_text(encoding="utf-8", errors="replace") + stamps = STAMP.findall(output) + stamp_ok = stamps == [(recipe, recipe, "PASS")] + if passage_audit: + signature = (tuple(LAYOUT.findall(output)), tuple(PLAN.findall(output)), + tuple(ROUTE.findall(output))) + expected = references.get((kind, recipe)) + stable = expected is None or signature == expected + if expected is None and all(signature): + references[(kind, recipe)] = signature + valid = (result is not None and result.returncode == 0 and stamp_ok + and len(signature[0]) == len(signature[1]) == len(signature[2]) == 1 + and re.search(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", output, re.M) + and re.search(r"^\[PassageRippleAuditSummary\].*status=PASS$", output, re.M) + and stable) + hashes = () + detail = f"layout={signature[0][0] if signature[0] else 'MISSING'} stable={stable}" + else: + hashes = tuple(HASH.findall(output)) + key = (kind, recipe) + expected = references.get(key) + stable = expected is None or hashes == expected + if expected is None and hashes: + references[key] = hashes + valid = (result is not None and result.returncode == 0 and stamp_ok + and "hash=on" in output and len(hashes) == 12 and stable) + detail = f"hashes={len(hashes)}/12 stable={stable}" + code = result.returncode if result is not None else "TIMEOUT" + rows.append((kind, recipe, compiler, workers, elapsed, code, valid)) + failed |= not valid + print(f"[L36DeterminismRun] kind={kind} recipe={recipe} compiler={compiler} " + f"workers={workers} elapsed={elapsed:.1f}s {detail} " + f"{'PASS' if valid else 'FAIL'} log={log}", flush=True) + + +for kind in ("l25", "crossing24"): + for recipe in ("surface-world", "floating-islands"): + for compiler in BINARIES: + for workers in (1, 8, 12): + run(kind, recipe, compiler, workers, OWNER, + crossing=(kind == "crossing24")) + +for compiler in BINARIES: + for workers in (1, 8, 12): + run("upper-fixture", "floating-islands", compiler, workers, FIXTURE, + passage_audit=True) + +summary = OUT / "determinism-l36.csv" +with summary.open("w", newline="", encoding="utf-8") as stream: + writer = csv.writer(stream) + writer.writerow(("kind", "recipe", "compiler", "workers", "elapsed_seconds", + "exit_code", "status")) + for row in rows: + writer.writerow((*row[:-1], "PASS" if row[-1] else "FAIL")) +print(f"[L36DeterminismSummary] runs={len(rows)} pass={sum(row[-1] for row in rows)} " + f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True) +raise SystemExit(1 if failed or len(rows) != 30 else 0) diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunInvariantAuditsL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunInvariantAuditsL36.py new file mode 100644 index 0000000..2f08766 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunInvariantAuditsL36.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Run L33 route/proof and transition invariants with recipe-stamped logs.""" +import re +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l36" +BINARIES = { + "clang": OUT / "bin/VoxelForgeCoreHeadless-l36-final-clang", + "gcc": OUT / "bin/VoxelForgeCoreHeadless-l36-final-gcc", +} +rows = [] +failed = False + + +def run(name, binary, data, recipe, extra, checks): + global failed + logfile = OUT / f"{name}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "900s", + str(binary), str(data), "--workers=12", "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=3", + "--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}", *extra, + ] + started = time.monotonic() + try: + with logfile.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT, + timeout=900, check=False) + except subprocess.TimeoutExpired: + result = None + elapsed = time.monotonic() - started + text = logfile.read_text(encoding="utf-8", errors="replace") + stamps = re.findall( + r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", + text, re.M) + stamp_ok = stamps == [(recipe, recipe, "PASS")] + checks_ok = all(check(text) for check in checks) + ok = result is not None and result.returncode == 0 and stamp_ok and checks_ok + rows.append((name, recipe, elapsed, result.returncode if result else "TIMEOUT", ok)) + print(f"{name} elapsed={elapsed:.1f}s recipe={stamps[0][1] if stamps else 'MISSING'} " + f"stamp={'PASS' if stamp_ok else 'FAIL'} checks={'PASS' if checks_ok else 'FAIL'} " + f"{'PASS' if ok else 'FAIL'}", flush=True) + failed |= not ok + + +owner = ROOT / "Standalone/data/owner_world.json" +for recipe in ("surface-world", "floating-islands"): + for compiler, binary in BINARIES.items(): + name = f"invariant-route-proof-{recipe}-{compiler}" + run(name, binary, owner, recipe, + ["--stream-hash", "--audit-uniform-block-proofs", "--audit-route-parity"], + [lambda text: bool(re.search( + r"^\[RouteParityAudit\].*status=PASS$", text, re.M)), + lambda text: bool(re.search( + r"^\[UniformBlockProofAudit\].*violations=0(?:\s|$)", text, re.M))]) + +for recipe in ("surface-world", "floating-islands"): + for compiler, binary in BINARIES.items(): + for mode in ("hard", "gradient", "interleaved"): + name = f"invariant-transition-{recipe}-{mode}-{compiler}" + run(name, binary, owner, recipe, + [f"--transition-mode={mode}", "--audit-strate-transitions"], + [lambda text: bool(re.search( + r"^\[StrateTransitionAuditSummary\].*hole_columns=0 air_samples=0 .*status=PASS$", + text, re.M)), + lambda text: bool(re.search( + r"^\[StrateTransitionNegativeControls\].*status=PASS$", text, re.M))]) + +summary = OUT / "invariant-audits.csv" +summary.write_text( + "run,recipe,elapsed_seconds,exit_code,status\n" + + "".join(f"{name},{recipe},{elapsed:.3f},{code},{'PASS' if ok else 'FAIL'}\n" + for name, recipe, elapsed, code, ok in rows), + encoding="utf-8") +print(f"[L33InvariantAudits] runs={len(rows)} pass={sum(row[-1] for row in rows)} " + f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True) +raise SystemExit(1 if failed or len(rows) != 16 else 0) diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunLegacyAuditsL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunLegacyAuditsL36.py new file mode 100644 index 0000000..83036b6 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunLegacyAuditsL36.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Run the L13 route parity and L9 uniform-block proof audits on L36.""" +import re +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l36" +BINARIES = { + "clang": OUT / "bin/VoxelForgeCoreHeadless-l36-final-clang", + "gcc": OUT / "bin/VoxelForgeCoreHeadless-l36-final-gcc", +} +RECIPES = ("floating-islands", "surface-world") +rows = [] +failed = False + +for recipe in RECIPES: + for compiler, binary in BINARIES.items(): + name = f"legacy-route-proof-{recipe}-{compiler}" + logfile = OUT / f"{name}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "900s", str(binary), str(ROOT / "Standalone/data/owner_world.json"), + "--workers=12", "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--stream-hash", "--force-generate", "--clip-radius=3", + "--max-clip-level=8", "--max-level=-1", + f"--recipe={recipe}", "--audit-uniform-block-proofs", "--audit-route-parity", + ] + started = time.monotonic() + try: + with logfile.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, timeout=900, check=False) + except subprocess.TimeoutExpired: + result = None + elapsed = time.monotonic() - started + output = logfile.read_text(encoding="utf-8", errors="replace") + stamp = re.findall( + r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$", + output, re.M) + route = re.findall(r"^\[RouteParityAudit\].*$", output, re.M) + proofs = re.findall(r"^\[UniformBlockProofAudit\].*$", output, re.M) + stamp_ok = stamp == [(recipe, recipe, "PASS")] + route_ok = len(route) == 1 and route[0].endswith("status=PASS") + proof_ok = len(proofs) == 1 and bool( + re.search(r"violations=0(?:\s|$)", proofs[0])) + ok = (result is not None and result.returncode == 0 and stamp_ok + and route_ok and proof_ok) + code = result.returncode if result is not None else "TIMEOUT" + rows.append((name, recipe, compiler, elapsed, code, route_ok, bool(proof_ok), ok)) + failed |= not ok + print(f"[L36LegacyAuditRun] case={name} elapsed={elapsed:.1f}s exit={code} " + f"recipe_stamp={'PASS' if stamp_ok else 'FAIL'} " + f"L13={'PASS' if route_ok else 'FAIL'} L9={'PASS' if proof_ok else 'FAIL'} " + f"{'PASS' if ok else 'FAIL'}", flush=True) + +summary = OUT / "legacy-audits.csv" +summary.write_text( + "case,recipe,compiler,elapsed_seconds,exit_code,l13,l9,status\n" + + "".join(f"{name},{recipe},{compiler},{elapsed:.3f},{code}," + f"{'PASS' if l13 else 'FAIL'},{'PASS' if l9 else 'FAIL'}," + f"{'PASS' if ok else 'FAIL'}\n" + for name, recipe, compiler, elapsed, code, l13, l9, ok in rows), + encoding="utf-8") +print(f"[L36LegacyAuditSummary] runs={len(rows)} pass={sum(row[-1] for row in rows)} " + f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True) +raise SystemExit(1 if failed or len(rows) != 4 else 0) diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunMouthFragmentAuditL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunMouthFragmentAuditL36.py new file mode 100644 index 0000000..e5e8edd --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunMouthFragmentAuditL36.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Compare the FI fade-ring crease against L35 using the same surface samples.""" +import argparse +import importlib.util +import math +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +RING_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py" +SPEC = importlib.util.spec_from_file_location("ring_profile_l32", RING_PATH) +RING = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(RING) + +CENTER = (6.298233, 1.137233) +INNER, OUTER, STEP = 5.5, 9.5, 0.125 +HEIGHT_MIN, HEIGHT_MAX, PREFERRED = -80.0, -40.0, -56.0 +NORMAL_LIMIT_DEG = 35.0 +COVERAGE_MIN = 0.90 +FRAGMENT_MAX_TRIANGLES = 64 +FRAGMENT_MIN_AREA_M2 = 0.0001 +FRAGMENT_MAX_AREA_M2 = 0.05 +FRAGMENT_MAX_DISTANCE_M = 12.0 + + +def small_components(triangles): + parent = list(range(len(triangles))) + sizes = [1] * len(triangles) + owners = {} + + def find(index): + while parent[index] != index: + parent[index] = parent[parent[index]] + index = parent[index] + return index + + def union(a, b): + a, b = find(a), find(b) + if a == b: + return + if sizes[a] < sizes[b]: + a, b = b, a + parent[b] = a + sizes[a] += sizes[b] + + for index, (_, _, vertices) in enumerate(triangles): + for point, _ in vertices: + key = tuple(round(value, 5) for value in point) + other = owners.setdefault(key, index) + union(index, other) + + components = {} + for index, (_, _, vertices) in enumerate(triangles): + root = find(index) + item = components.setdefault(root, {"count": 0, "area": 0.0, "sum": [0.0] * 3}) + item["count"] += 1 + points = [vertex[0] for vertex in vertices] + for point in points: + for axis in range(3): + item["sum"][axis] += point[axis] + ab = tuple(points[1][axis] - points[0][axis] for axis in range(3)) + ac = tuple(points[2][axis] - points[0][axis] for axis in range(3)) + cross = (ab[1] * ac[2] - ab[2] * ac[1], + ab[2] * ac[0] - ab[0] * ac[2], + ab[0] * ac[1] - ab[1] * ac[0]) + item["area"] += 0.5 * math.sqrt(sum(value * value for value in cross)) + + cx, cy = CENTER + small = [] + below_floor = [] + for item in components.values(): + centroid = tuple(value / (3 * item["count"]) for value in item["sum"]) + distance = math.hypot(centroid[0] - cx, centroid[1] - cy) + if (item["count"] <= FRAGMENT_MAX_TRIANGLES + and item["area"] <= FRAGMENT_MAX_AREA_M2 + and distance <= FRAGMENT_MAX_DISTANCE_M): + record = (item["count"], item["area"], distance, centroid) + (below_floor if item["area"] < FRAGMENT_MIN_AREA_M2 else small).append(record) + return len(components), small, below_floor + + +def audit(path): + cx, cy = CENTER + bounds = (cx - OUTER, cx + OUTER, cy - OUTER, cy + OUTER) + voxel_um, triangles, tiles = RING.read_scene( + str(path), 0, HEIGHT_MIN, HEIGHT_MAX, bounds) + component_count, fragments, below_floor = small_components(triangles) + print(f"[MouthFragmentComponents] mesh={path.name} components={component_count} " + f"small_near_mouth={len(fragments)} subthreshold_near_mouth={len(below_floor)} " + f"limits=(triangles<={FRAGMENT_MAX_TRIANGLES}," + f"area={FRAGMENT_MIN_AREA_M2:.4f}..{FRAGMENT_MAX_AREA_M2:.3f}m2," + f"distance<={FRAGMENT_MAX_DISTANCE_M:.1f}m) " + f"first={fragments[0] if fragments else None} " + f"subthreshold={below_floor[0] if below_floor else None} " + f"status={'PASS' if not fragments else 'FAIL'}") + cell = max(2.0, 16.0 * voxel_um / 1_000_000.0) + buckets = RING.build_buckets(triangles, True, bounds, cell) + lines = [] + count = round((OUTER - INNER) / STEP) + for degrees in (135.0, 180.0, 225.0, 270.0, 315.0): + angle = math.radians(degrees) + lines.append((f"radial-{degrees:.0f}", [ + (cx + radius * math.cos(angle), cy + radius * math.sin(angle)) + for radius in (INNER + i * STEP for i in range(count + 1))])) + tangent_count = round(8.0 / STEP) + for degrees in (225.0, 315.0): + angle = math.radians(degrees) + normal = (math.cos(angle), math.sin(angle)) + tangent = (-normal[1], normal[0]) + lines.append((f"tangent-{degrees:.0f}", [ + (cx + 6.5 * normal[0] + (-4.0 + i * STEP) * tangent[0], + cy + 6.5 * normal[1] + (-4.0 + i * STEP) * tangent[1]) + for i in range(tangent_count + 1)])) + + max_angle = 0.0 + max_pair = None + min_coverage = 1.0 + for name, points in lines: + samples = [RING.sample_surface(buckets, x, y, True, cell, PREFERRED) + for x, y in points] + coverage = sum(value is not None for value in samples) / len(samples) + min_coverage = min(min_coverage, coverage) + line_max = 0.0 + for index in range(1, len(samples)): + prior, current = samples[index - 1], samples[index] + if prior is None or current is None: + continue + dot = max(-1.0, min(1.0, sum(a * b for a, b in zip(prior[1], current[1])))) + delta = math.degrees(math.acos(dot)) + if delta > line_max: + line_max = delta + if delta > max_angle: + max_angle = delta + max_pair = (name, points[index - 1], prior[0], points[index], current[0]) + print(f"[MouthFadeLine] mesh={path.name} line={name} coverage={coverage:.3f} " + f"max_normal_jump_deg={line_max:.6f}") + normal_pass = max_angle <= NORMAL_LIMIT_DEG and min_coverage >= COVERAGE_MIN + passed = normal_pass and not fragments + print(f"[MouthFadeAudit] mesh={path.name} tiles={tiles} lines={len(lines)} " + f"coverage_min={min_coverage:.3f} coverage_limit={COVERAGE_MIN:.2f} " + f"max_normal_jump_deg={max_angle:.6f} normal_limit_deg={NORMAL_LIMIT_DEG:.1f} " + f"fragments={len(fragments)} " + f"worst_pair={max_pair} status={'PASS' if passed else 'FAIL'}") + return passed, max_angle + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--control", type=Path, + default=ROOT / "Saved/Linux/l35/owner-fi-ground-mouth.vfmesh") + parser.add_argument("--current", type=Path, + default=ROOT / "Saved/Linux/l36/owner-fi-ground-mouth.vfmesh") + args = parser.parse_args() + control = args.control if args.control.is_absolute() else ROOT / args.control + current = args.current if args.current.is_absolute() else ROOT / args.current + control_ok, control_max = audit(control) + current_ok, current_max = audit(current) + expected = not control_ok and current_ok and control_max > NORMAL_LIMIT_DEG + print(f"[MouthFadeNegativeControl] control_max_deg={control_max:.6f} " + f"control_detected={not control_ok} current_max_deg={current_max:.6f} " + f"status={'PASS' if expected else 'FAIL'}") + raise SystemExit(0 if expected else 1) + + +if __name__ == "__main__": + main() diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/RunPassageAuditsL36.py b/Standalone/Research/WorldAssemblerM3/Linux36/RunPassageAuditsL36.py new file mode 100644 index 0000000..6c09af4 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux36/RunPassageAuditsL36.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Run recipe-stamped passage/profile audits for both compilers and both owner recipes.""" +import re +import os +import subprocess +import time +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l36" +BINARIES = { + "clang": OUT / "bin/VoxelForgeCoreHeadless-l36-final-clang", + "gcc": OUT / "bin/VoxelForgeCoreHeadless-l36-final-gcc", +} +RUNS = [] +for recipe, filename in ( + ("floating-islands", "floating-islands"), + ("surface-world", "surface-world"), +): + for style in range(4): + RUNS.append((f"owner-{filename}-style{style}", + ROOT / f"Saved/Linux/linux25-owner-{filename}-style{style}.json", recipe)) +for style in range(4): + RUNS.append((f"upper-sw-fixture-style{style}", + OUT.parent / "l33/fixtures" / f"sw-upper-tunnel-network-style{style}.json", + "floating-islands")) + +RECIPE = re.compile(r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=PASS$", re.M) +PASSAGE = re.compile(r"^\[ResolvedPassageAudit\].*walkability=PASS$", re.M) +MOUTH = re.compile(r"^\[PassageMouthProfileAudit\].*status=PASS$", re.M) +ROUTE = re.compile(r"^\[ResolvedStrateRouteAudit\].*mouth_binding=PASS status=PASS$", re.M) +CHECKS = ( + r"^\[ResolvedLayoutStrict\] accepted=true issues=0 error=$", + r"^\[ResolvedLayoutValidatorControls\].*status=PASS$", + r"^\[PassageMouthProfileNegativeControl\].*source=L23_terraced_geometry.*detected=true$", + r"^\[PassageMouthProfileSummary\].*l23_terrace_negative_control=DETECTED status=PASS$", + r"^\[PassageNaturalnessSummary\].*l23_negative_control=DETECTED.*status=PASS$", + r"^\[PassageRippleAuditSummary\].*synthetic_spheres_negative_control=DETECTED max_amplitude_m=0\.035 floor_beaded_negative_control=DETECTED max_floor_amplitude_m=0\.015 status=PASS$", + r"^\[PassageOpenAirAuditSummary\].*violations=0.*l24_negative_control=DETECTED status=PASS$", + r"^\[PassageNoLongBridgeAudit\].*status=PASS$", + r"^\[PassageRockColumnAuditSummary\].*status=PASS$", + r"^\[ResolvedPassageAuditSummary\].*route_binding_failures=0 .*status=PASS$", + r"^\[Walkability\] PASS", + r"^\[FloorProfileBlend\].*negative_controls=step_detected/slope_detected status=PASS$", +) + +rows = [] +failed = False +for compiler, binary in BINARIES.items(): + for name, data, recipe in RUNS: + logfile = OUT / f"passage-audit-{name}-{compiler}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1800s", str(binary), str(data), "--workers=12", + "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", + "--force-generate", "--clip-radius=3", "--max-clip-level=8", "--max-level=-1", + f"--recipe={recipe}", "--audit-resolved-layout-passages", + ] + started = time.monotonic() + try: + with logfile.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, timeout=1800, check=False) + except subprocess.TimeoutExpired: + result = None + elapsed = time.monotonic() - started + output = logfile.read_text(encoding="utf-8", errors="replace") + stamp_ok = RECIPE.findall(output) == [(recipe, recipe)] + passages = len(PASSAGE.findall(output)) + mouths = len(MOUTH.findall(output)) + routes = len(ROUTE.findall(output)) + checks_ok = all(re.search(pattern, output, re.M) for pattern in CHECKS) + ok = (result is not None and result.returncode == 0 and stamp_ok + and passages >= 1 and mouths == passages and routes >= 1 and checks_ok) + code = result.returncode if result is not None else "TIMEOUT" + rows.append((name, recipe, compiler, elapsed, code, passages, mouths, routes, ok)) + failed |= not ok + print(f"[L36PassageAuditRun] case={name} compiler={compiler} recipe={recipe} " + f"elapsed={elapsed:.1f}s exit={code} passages={passages} " + f"profiles={mouths} routes={routes} {'PASS' if ok else 'FAIL'} " + f"log={logfile}", flush=True) + +summary = OUT / "passage-audits.csv" +summary.write_text( + "case,recipe,compiler,elapsed_seconds,exit_code,passages,mouth_profiles,routes,status\n" + + "".join(f"{name},{recipe},{compiler},{elapsed:.3f},{code},{passages},{mouths},{routes}," + f"{'PASS' if ok else 'FAIL'}\n" + for name, recipe, compiler, elapsed, code, passages, mouths, routes, ok in rows), + encoding="utf-8") +print(f"[L36PassageAuditSummary] runs={len(rows)} pass={sum(row[-1] for row in rows)} " + f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True) +raise SystemExit(1 if failed or len(rows) != 24 else 0) diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-arrival.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-arrival.jpg new file mode 100644 index 0000000000000000000000000000000000000000..34e550101ec20283215e3a76f3181266a635ec63 GIT binary patch literal 23432 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 bU)Psuc|NyZsKXkSBd)>MNmiIW-TMCkDMSgx literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-ground-mouth.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-ground-mouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e28cad3b51c1bc05943729bfeb492b92ca944a98 GIT binary patch literal 33605 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-mid-tunnel.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-mid-tunnel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..25a04a2b7d8e06df431e1888f88c994a50dbcde4 GIT binary patch literal 23182 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-rock-entry.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-fi-rock-entry.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66abbbde8d42d7e1307e474959fbe8ddbebd6c83 GIT binary patch literal 26512 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 GZT}mm)c7<2 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-arrival.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-arrival.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b7fde50abaf0fe8be734169f4e11c507c564474 GIT binary patch literal 21976 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 HxAA`fE<_cs literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-ground-mouth.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-ground-mouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea8851364703c200e226e69e1644d7cd76873dc7 GIT binary patch literal 29442 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-rock-entry.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/owner-sw-rock-entry.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1184f4f5e81045dd21f26957b2749129a60acfee GIT binary patch literal 43435 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

6KYT@qf7O-&Z#A=%%2x&C~w8DQbcIS3s~`A$6$0 z2O4`0kRm@I=8AxLgL;A9=Ua(xPH%!KG|{6l0}H1h>1zqYJ~~Y#GVNAc-Gs@`4T;{F z*}{#~^koq9`LUij-ycc;=2ic0-p7?IE6W9jKn@=S?#~6`Gk(A2=JcH++`y3rem??% zVFLsOFmS$1vRiZo@YzsUL{3x==Z}NRH^m8#rUf1ioG;1)6nsFo+zj3;S!KYx-}lkE zkbsb6ASGvyfuapuWFB4r4iB_D#=Cik*8TKk;^%+mo(V~YLfkjMRsnuf9)KkSc;nPy zm*wEz+iCOj-^BC3he?rE@6WupUO{+;)+Z49x<|su$2%e^?l1|MvkPWl7>jK*p$Vn*oas5#OABE?SpW_sl!SHD9$r|| z-zm?9hp+#0Qy*XMTX09t>*|5R-;b9sN6cpfpFcdpZWwB}T2AN#o$F?q(+rPM2p)D| z+Zu?Q5?jXDZlDza)5)-$_~j-GxCvQBIj#t~Fq8rBk-H}v9-Z?T6O{y&T|aZ2#{)St zOaTwbyMkOMJ_l0ZgQKl~W+=8E#d zmy}py;ZiDF_LU5`T>7}I;y0jUL(}n}KXGzs>ykfn$Bh#V2g(XO06mBg?u6fO4&F2h zyyNc;oDvM9pWFBpq9StfTW7$x_`9^#v7A<5JQNmI2^d?BHSHS(Sc?Jl#|San&nIXc zLeB%L?a#M3-&hXki|UQV7nF@UM6uk}`3e@8aKQ(l9Qdm3@oE6iDC%wgG8nO7N&}3y zlX|-@LC0@$V(STmx+vni4~XVeJRDvU^!;X_BnZqO=Ket(W7{~twofTXt0-@FvYe(<=nl;wPyoLzVI zU@bnEQnu^?m7M#8Fjq>Uwa#dCN`q z3RE%uAlgoAC=j%lba7YN=#Rw=uN+`*HYlP|Hfopj7Wd(Vp)P#gpPtm;^<9O#F*y?3iM1ci#xSdZyQ5MR9Suntx0OEm)u$2Y^1 z)zQXPmr1CALlY+{rVM%*;Wp>mKRa`)ulQAub5o!b_`9@2RBx*V!2?*Ktx^4lleRBH z=pnY)#uIv=Z8wlo0MY_MaP&@q!XGaAb1T2&4bHD{_%HaEw-Z*f@}T;e_hsdQHnRn` UrIXpZgf^b-gMaEC@u~ZN1L5VI=l}o! literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-arrival.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-arrival.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b1e91949aaf166fec85a5421955b78f8026c7a8d GIT binary patch literal 23906 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-ground-mouth.jpg b/Standalone/Research/WorldAssemblerM3/Linux36/captures/upper-sw-fixture-ground-mouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65aa0110f3f8f5ad41bca9445f12d78daedbe3e1 GIT binary patch literal 25541 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 literal 0 HcmV?d00001 diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 4553b8b..023c157 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -817,8 +817,15 @@ namespace StandaloneStreamBenchmark double periodMetres=0.0,amplitudeMetres=0.0,localPeakAmplitudeMetres=0.0; double coherentPeriodMetres=0.0,coherentAmplitudeMetres=0.0; double controlAmplitudeMetres=0.0; + double floorPeriodMetres=0.0,floorAmplitudeMetres=0.0; + double floorControlAmplitudeMetres=0.0; + double floorCoherentPeriodMetres=0.0,floorCoherentAmplitudeMetres=0.0; + std::vector> floorPeakProfile; + double floorPeakWindowStartMetres=0.0; std::uint64_t samples=0,expectedSamples=0; + std::uint64_t floorSamples=0,floorExpectedSamples=0; bool controlDetected=false,passed=false; + bool floorControlDetected=false; }; constexpr double MaximumFlatRunMetres=8.0; constexpr double MinimumWallReliefMetres=0.20; @@ -1131,6 +1138,7 @@ namespace StandaloneStreamBenchmark constexpr double MaximumPeriodMetres=8.0; constexpr double PeriodStepMetres=0.0625; constexpr double RippleAmplitudeLimitMetres=0.035; + constexpr double FloorRippleAmplitudeLimitMetres=0.015; struct FPathPoint { double x=0.0,y=0.0,z=0.0,radius=0.0,distance=0.0; @@ -1218,6 +1226,26 @@ namespace StandaloneStreamBenchmark values[i]-mean-slope*x}); } }; + const auto appendDetrendedPairs=[]( + const std::vector>& values, + std::vector>& output) + { + if(values.size()<64)return; + double meanX=0.0,meanY=0.0; + for(const auto& value:values){meanX+=value[0];meanY+=value[1];} + meanX/=static_cast(values.size()); + meanY/=static_cast(values.size()); + double xy=0.0,xx=0.0; + for(const auto& value:values) + { + const double x=value[0]-meanX; + xy+=x*(value[1]-meanY);xx+=x*x; + } + const double slope=xx>0.0?xy/xx:0.0; + for(const auto& value:values) + output.push_back({value[0],value[1]-meanY + -slope*(value[0]-meanX)}); + }; std::vector beadControl; beadControl.reserve(static_cast(WindowLengthMetres/SampleSpacingMetres)+1); constexpr double BeadSpacingMetres=1.5; @@ -1234,20 +1262,64 @@ namespace StandaloneStreamBenchmark double ignoredPeriod=0.0; audit.controlAmplitudeMetres=sinusoidAmplitude(beadSamples,ignoredPeriod); audit.controlDetected=audit.controlAmplitudeMetres>=RippleAmplitudeLimitMetres; - const std::array windowStarts{{0.03,0.31,0.61,0.91}}; + audit.floorControlAmplitudeMetres=audit.controlAmplitudeMetres; + audit.floorControlDetected=audit.floorControlAmplitudeMetres + >=FloorRippleAmplitudeLimitMetres; + const std::array windowStarts{{0.03,0.31,0.457,0.61,0.91}}; std::vector> coherentSamples; + std::vector> coherentFloorSamples; for(const double fraction:windowStarts) { const double start=(total-WindowLengthMetres)*fraction; std::vector radii; radii.reserve(static_cast(WindowLengthMetres/SampleSpacingMetres)+1); + std::vector> floorResiduals; + floorResiduals.reserve(static_cast(WindowLengthMetres/ + SampleSpacingMetres)+1); + std::vector> floorDebugSamples; + floorDebugSamples.reserve(static_cast(WindowLengthMetres/ + SampleSpacingMetres)+1); for(std::size_t sample=0; sample<=static_cast(WindowLengthMetres/SampleSpacingMetres); ++sample) { ++audit.expectedSamples; + ++audit.floorExpectedSamples; const double distance=start+static_cast(sample)*SampleSpacingMetres; const auto pose=interpolate(distance); + // Recover the composed walk surface from its zero crossing. + // The support strip is segment-built and is not itself a true + // signed-distance field, so a fixed centerline radius misses + // the stepped top that the player actually walks on. + const double expectedFloor=pose.z-pose.radius+0.20; + constexpr double FloorProbeStepMetres=0.25; + constexpr int FloorProbeCount=7; + double previousDensity=0.0,previousZ=0.0; + bool havePrevious=false,haveFloor=false; + double bestFloor=0.0,bestDistance=std::numeric_limits::infinity(); + for(int vertical=0;vertical(Field.densityAt({ + pose.x/coreMetres,pose.y/coreMetres,z/coreMetres}))*coreMetres; + if(havePrevious&&previousDensity<0.0&&density>=0.0) + { + const double denominator=density-previousDensity; + const double crossing=denominator>1.0e-12 + ?previousZ+(z-previousZ)*(-previousDensity)/denominator:z; + const double candidateDistance=std::abs(crossing-expectedFloor); + if(candidateDistance=static_cast(WindowLengthMetres/ + SampleSpacingMetres)*3/4) + { + std::vector> floorWindowSamples; + appendDetrendedPairs(floorResiduals,floorWindowSamples); + floorWindowAmplitude=sinusoidAmplitude(floorWindowSamples, + floorWindowPeriod); + if(floorWindowAmplitude>audit.floorAmplitudeMetres) + { + audit.floorAmplitudeMetres=floorWindowAmplitude; + audit.floorPeriodMetres=floorWindowPeriod; + audit.floorPeakWindowStartMetres=start; + audit.floorPeakProfile=std::move(floorDebugSamples); + } + coherentFloorSamples.insert(coherentFloorSamples.end(), + floorWindowSamples.begin(),floorWindowSamples.end()); + } + const double floorWindowCoverage=static_cast(floorResiduals.size()) + /(WindowLengthMetres/SampleSpacingMetres+1.0); + double floorResidualMinimum=0.0,floorResidualMaximum=0.0; + if(!floorResiduals.empty()) + { + const auto extrema=std::minmax_element(floorResiduals.begin(), + floorResiduals.end(),[](const auto& a,const auto& b) + {return a[1]0 ?static_cast(audit.samples)/audit.expectedSamples:0.0; + const double floorCoverage=audit.floorExpectedSamples>0 + ?static_cast(audit.floorSamples)/audit.floorExpectedSamples:0.0; audit.passed=audit.controlDetected&&coverage>=0.75 - &&audit.localPeakAmplitudeMetres=0.75 + &&audit.floorAmplitudeMetres(rippleAudit.samples), static_cast(rippleAudit.expectedSamples), rippleAudit.controlAmplitudeMetres,rippleAudit.controlDetected?"true":"false", + rippleAudit.floorPeriodMetres,rippleAudit.floorAmplitudeMetres, + rippleAudit.floorAmplitudeMetres*100.0, + rippleAudit.floorCoherentPeriodMetres,rippleAudit.floorCoherentAmplitudeMetres, + static_cast(rippleAudit.floorSamples), + static_cast(rippleAudit.floorExpectedSamples), + rippleAudit.floorControlAmplitudeMetres, + rippleAudit.floorControlDetected?"true":"false", rippleAudit.passed?"PASS":"FAIL"); + for(std::size_t sample=0;sample(openAirAudit.supportSamples), static_cast(openAirAudit.violations), @@ -2340,10 +2470,11 @@ namespace StandaloneStreamBenchmark PassageProfilePlateauMinimumMetres,PassageProfileStepThresholdMetres, l23TerraceControlDetected?"DETECTED":"MISSED", mouthProfilePasses==passageCount&&l23TerraceControlDetected?"PASS":"FAIL"); - std::printf("[PassageRippleAuditSummary] passed=%llu/%llu synthetic_spheres_negative_control=%s max_amplitude_m=0.035 status=%s\n", + std::printf("[PassageRippleAuditSummary] passed=%llu/%llu synthetic_spheres_negative_control=%s max_amplitude_m=0.035 floor_beaded_negative_control=%s max_floor_amplitude_m=0.015 status=%s\n", static_cast(ripplePasses), static_cast(passageCount), rippleControlsDetected==passageCount?"DETECTED":"MISSED", + floorRippleControlsDetected==passageCount?"DETECTED":"MISSED", ripplePasses==passageCount?"PASS":"FAIL"); const bool auditPassed=passageCount>0&&passagePasses==passageCount &&naturalPasses==passageCount&&l23SmoothWallDetected