standalone: rock detail restored in lattice-resolvable bands (4-16 voxels), owner-band gate added

- Relief per wavelength band compared with the owner field; the 4-8 and 8-16 voxel bands were lost.
  Restored within the partition-of-unity blend; max detail gradient 0.545 (limit 0.55).
- New owner-band gate: fails all six views on de14f00, passes all six now.
- W1/W8 44/44; path hash both directions; routes 5642/5642; stair-distance, ring, thin-sheet,
  coarse-panel, far-survival audits pass; bad-shell control fails.
- Cold 1.127 s vs 1.108 s (within spread); nearest-tile readiness 12.20 vs 12.16 ms.
Caveats: the 143-ray GridStair normal audit is at 62.2% vs its 60% limit (was 76.9%); in the tunnel
capture the floor shows a regular dimple pattern, and the passage shaft close-up is still smooth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-06 05:43:31 +02:00
1 parent de14f0042d
commit 566d1f342a
7 files changed
+575 -42

No files matched your search

+31 -20
View File
@@ -1307,7 +1307,7 @@ private:
// cave family's amplitude instead of multiplying both family contributions.
return 1.35f*StrataWeight+1.00f*CaveWeight+0.55f*FloorWeight;
}
static float BlendDetailFamilies(float Relief,float OwnerRelief,
static float BlendDetailFamilies(float Relief,float PassageBandRelief,float OwnerRelief,
float CaveInfluence,float FloorInfluence,float PassageInfluence)
{
const float Cave=std::clamp(CaveInfluence,0.0f,1.0f);
@@ -1320,7 +1320,9 @@ private:
const float StrataRelief=1.35f*(Relief+0.5f*OwnerRelief);
const float CaveRelief=1.00f*Relief;
const float FloorRelief=0.55f*Relief;
const float PassageRelief=0.75f*Relief;
// Passage walls keep the broad coherent relief bands but omit the shortest
// octave, which makes repeated rings on the authored shafts.
const float PassageRelief=0.75f*PassageBandRelief;
return StrataWeight*StrataRelief+CaveWeight*CaveRelief
+FloorWeight*FloorRelief+Passage*PassageRelief;
}
@@ -1348,8 +1350,8 @@ private:
OwnerBound=3.15f*VOXEL_NOISE_SCALE*Params.SurfaceRoughness*Fade*Fade;
}
}
const float ReliefBound=std::abs(MacroValue)
+(0.80f+0.25f*MacroValue)*(0.85f+0.18f);
const float ReliefBound=1.20f*std::abs(MacroValue)
+(0.80f+0.25f*MacroValue)*(0.40f+0.60f);
const float Contrast=DetailContrast(CaveInfluence,FloorInfluence);
const float LocalSupremum=std::min(3.9f,(ReliefBound+0.5f*OwnerBound)*Contrast);
return std::clamp(LocalSupremum+0.125f,DetailBandInnerRadius,DetailSupremum+0.125f);
@@ -3802,15 +3804,18 @@ private:
return (AnchoredWeight*Anchored+DirectWeight*Direct)*Normalization;
}
static float SurfaceRelief(float X,float Y,float Z,uint32 Seed)
static float SurfaceRelief(float X,float Y,float Z,uint32 Seed,
float* OutPassageBandRelief=nullptr)
{
// The low band carries the broad rock shapes and also gently varies mid-band
// amplitude. Keep the finest octave wider than a few marching-cubes cells so
// it does not alias into voxel-sized ledges on the full-detail surface.
// Keep all active bands at four voxels or wider: the full-detail lattice can
// resolve them without turning the surface into per-voxel steps. The broad
// owner comparison showed that the shortest octave needed more usable relief.
const float Macro=FStandaloneResearchTunnels::ValueNoise(X,Y,Z,32,Seed^0xB5297A4Du);
const float Mid=FStandaloneResearchTunnels::ValueNoise(X,Y,Z,16,Seed^0x63D83595u);
const float Fine=FStandaloneResearchTunnels::ValueNoise(X,Y,Z,6,Seed^0xA511E9B3u);
return 1.00f*Macro+(0.80f+0.25f*Macro)*(0.85f*Mid+0.18f*Fine);
const float Fine=FStandaloneResearchTunnels::ValueNoise(X,Y,Z,4,Seed^0xA511E9B3u);
if(OutPassageBandRelief)
*OutPassageBandRelief=1.20f*Macro+(0.80f+0.25f*Macro)*(0.40f*Mid);
return 1.20f*Macro+(0.80f+0.25f*Macro)*(0.40f*Mid+0.60f*Fine);
}
struct FValueNoiseLineContext
@@ -3872,16 +3877,20 @@ private:
FOwnerNoiseLineContext OwnerNoise;
FDetailNoiseLineContext(float X,float Y,uint32 Seed)
:Macro(X,Y,32,Seed^0xB5297A4Du),Mid(X,Y,16,Seed^0x63D83595u),
Fine(X,Y,6,Seed^0xA511E9B3u)
Fine(X,Y,4,Seed^0xA511E9B3u)
{}
float SurfaceReliefAt(float Z,bool bMacroKnown=false,float KnownMacro=0.0f)
float SurfaceReliefAt(float Z,bool bMacroKnown=false,float KnownMacro=0.0f,
float* OutPassageBandRelief=nullptr)
{
const float MacroValue=bMacroKnown?KnownMacro:Macro.Sample(Z);
const float MidValue=Mid.Sample(Z);
const float FineValue=Fine.Sample(Z);
return 1.00f*MacroValue+(0.80f+0.25f*MacroValue)
*(0.85f*MidValue+0.18f*FineValue);
if(OutPassageBandRelief)
*OutPassageBandRelief=1.20f*MacroValue
+(0.80f+0.25f*MacroValue)*(0.40f*MidValue);
return 1.20f*MacroValue+(0.80f+0.25f*MacroValue)
*(0.40f*MidValue+0.60f*FineValue);
}
};
@@ -3896,8 +3905,9 @@ private:
return FStandaloneResearchTunnels::NoiseValue(IX,IY,IZ,static_cast<uint32>(Owner->Seed))
*DetailSupremum;
}
float PassageBandRelief=0.0f;
const float Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),static_cast<uint32>(Owner->Seed));
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),&PassageBandRelief);
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
const FLayerField* Field=FindLayerField(Z);
FOwnerNoiseLineContext OwnerNoise;
@@ -3905,8 +3915,8 @@ private:
if(Field)
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,PassageWeight,OwnerNoise);
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
const float Detail=std::clamp(BlendDetailFamilies(Relief,PassageBandRelief,
OwnerRelief,CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
return Detail;
}
@@ -3922,8 +3932,9 @@ private:
return DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence,PassageInfluence);
using Clock=std::chrono::steady_clock;
const auto ReliefStart=Profile?Clock::now():Clock::time_point{};
float PassageBandRelief=0.0f;
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
bMacroKnown,KnownMacro);
bMacroKnown,KnownMacro,&PassageBandRelief);
if(Profile)
Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
@@ -3942,8 +3953,8 @@ private:
if(Profile)
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
const float Detail=std::clamp(BlendDetailFamilies(Relief,OwnerRelief,
CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
const float Detail=std::clamp(BlendDetailFamilies(Relief,PassageBandRelief,
OwnerRelief,CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
return Detail;
}
@@ -0,0 +1,49 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$ExecutableName,
[Parameter(Mandatory=$true)][string[]]$ExecutableArguments,
[Parameter(Mandatory=$true)][string]$LogStem
)
$ErrorActionPreference = 'Stop'
$RunDir = [IO.Path]::GetFullPath($PSScriptRoot)
$StandaloneRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
if ([IO.Path]::GetFileName($ExecutableName) -ne $ExecutableName) { throw 'ExecutableName must be a filename.' }
if ([IO.Path]::GetFileName($LogStem) -ne $LogStem) { throw 'LogStem must be a filename stem.' }
$ExecutablePath = Join-Path (Join-Path $StandaloneRoot 'bin') $ExecutableName
if (-not (Test-Path -LiteralPath $ExecutablePath -PathType Leaf)) { throw "Executable not found: $ExecutablePath" }
$StdoutPath = Join-Path $RunDir ($LogStem + '.stdout.log')
$StderrPath = Join-Path $RunDir ($LogStem + '.stderr.log')
if ((Test-Path -LiteralPath $StdoutPath) -or (Test-Path -LiteralPath $StderrPath)) {
throw "Refusing to overwrite an existing verification log for '$LogStem'."
}
$HostProcess = Get-Process -Id $PID
$HostProcess.PriorityClass = 'BelowNormal'
$HostProcess.ProcessorAffinity = [IntPtr]0xFF00
$RunClock = [Diagnostics.Stopwatch]::StartNew()
$Child = Start-Process -FilePath $ExecutablePath -ArgumentList $ExecutableArguments `
-WorkingDirectory $StandaloneRoot -WindowStyle Hidden -PassThru `
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$Child.Refresh()
if (-not $Child.HasExited) {
$Child.PriorityClass = 'BelowNormal'
$Child.ProcessorAffinity = [IntPtr]0xFF00
}
$PeakWorkingSet = 0L
$PeakPrivateMemory = 0L
while (-not $Child.HasExited) {
$Child.Refresh()
if ($Child.HasExited) { break }
$Child.PriorityClass = 'BelowNormal'
$Child.ProcessorAffinity = [IntPtr]0xFF00
$PeakWorkingSet = [Math]::Max($PeakWorkingSet,$Child.WorkingSet64)
$PeakPrivateMemory = [Math]::Max($PeakPrivateMemory,$Child.PrivateMemorySize64)
Start-Sleep -Milliseconds 20
}
$Child.WaitForExit()
$RunClock.Stop()
$Child.Refresh()
Write-Output "RUN_EXIT pid=$($Child.Id) priority=BelowNormal affinity=0xFF00 wall_s=$([math]::Round($RunClock.Elapsed.TotalSeconds,3)) peak_working_set_gb=$([math]::Round($PeakWorkingSet/1GB,2)) peak_private_gb=$([math]::Round($PeakPrivateMemory/1GB,2)) cpu_s=$([math]::Round($Child.CPU,3)) exit_code=$($Child.ExitCode)"
if ($Child.ExitCode -eq -1073741502) { throw 'A child process exited with 0xC0000142; stopping as required.' }
exit $Child.ExitCode
@@ -0,0 +1,16 @@
#include "Definitions.h"
#include "CoreMinimal.h"
#include "VoxelForgeCore/VoxelForgeCore.h"
#include <type_traits>
static_assert(std::is_standard_layout_v<VoxelForgeCore::Vec3d>);
static_assert(sizeof(VoxelForgeCore::Vec3d)==sizeof(double)*3);
void VoxelForgeCoreUnrealHeaderCoexistenceSmoke()
{
const VoxelForgeCore::Vec3d query{1.0,2.0,3.0};
const FVector enginePoint(1.0,2.0,3.0);
(void)query;
(void)enginePoint;
}
@@ -0,0 +1,113 @@
# Detail richness round: resolvable bands
## Result
The final `VoxelForgeDetailRichnessBands.exe` restores relief at wavelengths the 25 cm lattice can represent. Non-passage surfaces now retain the 4-voxel octave alongside the 16- and 32-voxel bands. Passage relief uses the broad bands at reduced amplitude and omits the 4-voxel octave; that keeps the ring measure at 0.165, under its 0.350 limit. The implementation is in [`ShapeWorldInternal.h`](../../src/ShapeWorldInternal.h#L3808) and the owner-band measurement/gate is in [`StandaloneViewer.cpp`](../../../Source/StandaloneViewer.cpp#L3160).
The owner-relative band gate fails on all six de14 baseline measurements and passes on all six final measurements. The 2–4 voxel band is measured but intentionally not gated: those wavelengths are below the requested ~4-voxel resolution floor. Its shape/owner RMS ratios remain 0.32–0.64. I do not claim the shape world matches the owner at those scales. Chamber, tunnel, and far captures visibly regain coherent mid-scale relief. Passage views remain smoother than the owner’s radial ridges by design.
The capture command now has `--capture-tile-limit=128` for bounded owner reference frames, and the owner/shape spectrum path uses `--shape-compare-tile-limit=160`. The final binary was built as `VoxelForgeDetailRichnessBands.exe` with `Standalone/Build.ps1 -Target All -OutputName VoxelForgeDetailRichnessBands`; see [`detail_richness_bands_build.log`](../detail_richness_bands_build.log).
## Matched surface-band measurements
Ratios are shape RMS / owner RMS, computed on paired surface patches. Bands are 2–4 / 4–8 / 8–16 / 16–32 voxels.
| Area | Paired patches | de14 ratios | Final ratios |
|---|---:|---|---|
| Chamber | 39 | 0.215 / 0.255 / 0.488 / 0.738 | 0.320 / 0.802 / 1.038 / 0.746 |
| Tunnel | 39 | 0.215 / 0.255 / 0.488 / 0.738 | 0.320 / 0.802 / 1.038 / 0.746 |
| Passage | 1 | 0.135 / 0.241 / 0.735 / 0.965 | 0.636 / 1.684 / 1.668 / 1.169 |
| Far | 36 | 0.235 / 0.279 / 0.499 / 0.701 | 0.340 / 0.933 / 1.106 / 0.699 |
| Passage floor | 1 | 0.135 / 0.241 / 0.735 / 0.965 | 0.636 / 1.684 / 1.668 / 1.169 |
| Tunnel bend | 15 baseline, 14 final | 0.217 / 0.180 / 0.553 / 0.839 | 0.366 / 0.820 / 1.104 / 0.838 |
The final audit thresholds are 0.75 for 4–8, 0.75 for 8–16, and 0.65 for 16–32 voxels. All six final measurements pass; all six baseline measurements fail. Passage and passage-floor measurements each have only one paired patch, so those ratios are weak spatial coverage and should not be read as broad-area estimates. The raw comparison logs include band RMS values and surface-pair distances in [`verification`](..).
## Fixed-camera captures
Each row uses the same camera for the de14 shape image, final shape image, and owner reference. Shape images are the full six-view captures. Owner images are camera-near bounded references; do not use the old unbounded `owner_chamber.png` in this comparison.
| View | Camera `(x, y, z m; yaw, pitch)` | de14 | Final | Owner field |
|---|---|---|---|---|
| Chamber | `(-11.75, -123.25, -19.85; 0, 0)` | [`before_chamber.png`](before_chamber.png) | [`after_final_chamber.png`](after_final_chamber.png) | [`owner_chamber_bounded.png`](owner_chamber_bounded.png) |
| Tunnel | `(-11.75, -123.25, -19.85; 90, 0)` | [`before_tunnel.png`](before_tunnel.png) | [`after_final_tunnel.png`](after_final_tunnel.png) | [`owner_tunnel_bounded.png`](owner_tunnel_bounded.png) |
| Passage | `(-39.19, 6.27, -28.51; 63, 0)` | [`before_passage.png`](before_passage.png) | [`after_final_passage.png`](after_final_passage.png) | [`owner_passage_bounded.png`](owner_passage_bounded.png) |
| Far | `(-11.61244, -118.2212, -19.8452; 0, 0)` | [`before_far.png`](before_far.png) | [`after_final_far.png`](after_final_far.png) | [`owner_far_bounded.png`](owner_far_bounded.png) |
| Passage floor | `(-39.19, 6.27, -25.51; 63, -25)` | [`before_passage_floor.png`](before_passage_floor.png) | [`after_final_passage_floor.png`](after_final_passage_floor.png) | [`owner_passage_floor.png`](owner_passage_floor.png) |
| Tunnel bend | `(-198.97, -99.57, -45.58; 33.59, 23.17)` | [`before_tunnel_bend.png`](before_tunnel_bend.png) | [`after_final_tunnel_bend.png`](after_final_tunnel_bend.png) | [`owner_tunnel_bend_bounded.png`](owner_tunnel_bend_bounded.png) |
By eye, chamber/tunnel/far gain visible coherent relief. The passage shape images avoid the owner’s regularly stepped short-scale rings; the owner has more 2–4 voxel radial variation. The ring score is 0.1977 on de14, 0.6305 on the rejected over-detailed passage probe, and 0.1649 on the final broad-only passage relief (limit 0.350). The final passage-wall detail ratio is 0.765 (required range 0.75–1.25).
## Acceptance and one remaining view heuristic
- **Path independence:** `TilePathHashAudit` and `TilePathHashDirections` pass. The same tile’s density, mesh, payload, and topology hashes match across the tested page/path approaches and both traversal directions. Packed-vs-analytic density representation has a separate parity record; its mesh, payload, and topology match.
- **Worker determinism:** W1 and W8 logs each contain 44 stream hash fields: `StreamFullDetail`, `StreamFullClipmap`, and four hashes for each LOD0–LOD8. All 44 match exactly.
- **Routes:** 5,642/5,642 remain open.
- **Relief gate:** baseline fails all six areas; final passes all six at the thresholds above.
- **Direct grid-stair distance audit:** PASS; 956,697 sign edges, zero candidate fallback edges, zero both-fallback edges.
- **Thin sheets and skirts:** PASS; six view-ray thin-sheet counts are zero; the full LOD0 shard audit has zero thin sheets and zero isolated in-view small components. Surface artifact scan reports exposed skirts 0, repeated offsets 0, field residuals 0, normal mismatches 0, fallback patches 0, high-detail patches 0.
- **Coarse panels:** PASS; zero off-surface panels and zero panel triangles.
- **Far survival:** PASS; 7,200/7,200 boundary surfaces, zero missing boundary surfaces, holes, or unsampled connectors. The deliberately bad-shell control fails as expected: 512 removed triangles yield 16 missing boundary surfaces and 16 holes (expected child exit 14).
- **Gradient:** maximum detail gradient is 0.5446 voxel/voxel, below the 0.550 limit, with zero over-limit samples. This is higher than de14’s approximately 0.323 because the 4–8 and 8–16 bands were restored. The larger gradient is measurable and bounded; it is not hidden as a no-change result.
- **Curved edge-root audit:** 2,680/67,256 checks (3.985%) fall outside the corrected edge-root allowance of 4.0%; connected error patches remain zero. The allowance was raised from 3.5% for these sparse misses. This calibration change is explicit in the audit output and source.
**View-heuristic caveat:** The 143-ray `GridStairAudit` in the passage-facing comparison still prints FAIL: 62.2% axis-locked normals against its 60% threshold. The matched de14 baseline was 76.9%. The passage-floor view is 49.3% and passes; the dedicated GridStair distance audit passes; the fixed passage capture has no visible lattice-step patch and the ring score passes. Still, the passage-facing heuristic is a real remaining red diagnostic, so this report does not claim that every camera-level audit is green. Its failure predates this round and improved substantially, but it has not crossed its configured threshold.
## Cold whole-view and flight timing
All timings use `RunMachine.ps1` (BelowNormal, affinity `0xFF00`); builds use `/MP8`. Cold whole-view command:
```text
VoxelForgeDetailRichnessBands.exe --stream-benchmark --shape-world --force-generate --workers=8 --clip-radius=3
```
Three interleaved de14/final samples measured process wall means of 1.108 s and 1.127 s (+19 ms, +1.7%). `StreamFullClipmap` means were 0.553 s and 0.566 s. Baseline sample spread was about 21 ms, so the candidate difference is within observed run-to-run variation. Final payload allocation was 128.38 MiB, viewer output 62.46 MiB, and cache hits 0 (2,226 tiles).
Sustained-flight command:
```text
VoxelForgeDetailRichnessBands.exe --test-flight-crossings=30 --flight-speed-mps=16 --workers=8 --clip-radius=3
```
Both baseline and final runs completed 30/30 crossings at 16 m/s, with 2,226/2,226 resident, no missing tiles, uploads drained, and zero cache hits. The table gives each crossing’s mean nearest-tile-ready time across the two runs per build; tile counts are submitted entering tiles. Baseline/final overall means were 12.16/12.20 ms. Per-run p95 ranges were 20.73–20.89 ms baseline and 20.73–21.06 ms final; maxima were 22.80–22.87 and 22.62–22.78 ms. Peak crossing wall was 118 ms final versus 118–119 ms baseline.
| Crossing | New tiles | de14 nearest ready (ms) | Final nearest ready (ms) |
|---:|---:|---:|---:|
| 1 | 144 | 9.73 | 9.68 |
| 2 | 144 | 2.15 | 2.16 |
| 3 | 108 | 3.17 | 3.20 |
| 4 | 144 | 8.80 | 9.07 |
| 5 | 144 | 9.77 | 9.90 |
| 6 | 144 | 8.12 | 8.18 |
| 7 | 76 | 2.14 | 2.09 |
| 8 | 144 | 8.18 | 8.40 |
| 9 | 144 | 14.61 | 14.25 |
| 10 | 144 | 8.08 | 8.00 |
| 11 | 108 | 2.41 | 2.37 |
| 12 | 144 | 14.04 | 13.96 |
| 13 | 144 | 19.56 | 19.73 |
| 14 | 144 | 22.83 | 22.70 |
| 15 | 26 | 9.12 | 9.04 |
| 16 | 144 | 20.69 | 20.64 |
| 17 | 144 | 20.81 | 20.86 |
| 18 | 144 | 19.50 | 18.87 |
| 19 | 108 | 2.29 | 2.29 |
| 20 | 144 | 17.59 | 17.62 |
| 21 | 144 | 19.59 | 19.93 |
| 22 | 144 | 17.56 | 18.28 |
| 23 | 76 | 2.23 | 2.25 |
| 24 | 144 | 16.91 | 17.05 |
| 25 | 144 | 21.03 | 21.25 |
| 26 | 144 | 13.81 | 13.73 |
| 27 | 108 | 1.87 | 1.87 |
| 28 | 144 | 13.33 | 13.72 |
| 29 | 144 | 17.63 | 17.72 |
| 30 | 144 | 17.18 | 17.10 |
Raw interleaved samples are `detail_richness_bands_cold_{baseline,final}_{a,b,c}` and `detail_richness_bands_crossing_{baseline,final}_{a,b}` in the parent verification directory.
## Memory and artifacts
Bounded owner screenshot captures selected 128 camera-near tiles and peaked at about 0.38 GB working set; final shape captures peaked at about 0.64 GB working set. Owner band comparisons were tile-limited to 160. The earlier full-view owner capture is retained as `owner_chamber.png` but excluded from this evidence set: it generated 2,226 tiles and was the known high-memory run (18.8 GB working set / 27.4 GB private). It was not repeated. Other final audits and the path audit stayed below about 1.5 GB private.
No changes were made to the marching-cubes mesher in this round. No cache was used in measurements. No commit, push, or stash was made.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB