standalone: landing plate removed; passage shaft rings reduced with the rock detail kept

- AddLanding no longer adds a shallow positive ellipsoid (floor plate check 71.3% -> 0%).
- Round 10 had removed all relief on passage walls to pass the ring check (smooth tube; rejected).
  Cause found instead: the owner roughness's 8-voxel Z anchors with linear interpolation. Passage-
  controlled walls now use 2-voxel anchors, blended smoothly into the 8-voxel ones.
- Ring gate (lag-20 correlation) 0.376 -> 0.038 AND shaft relief RMS 1.294 (smooth-tube control
  0.276 fails); passage/cave relief ratio 1.152. Faint rings remain deep in the shaft.
- Whole view 0.984 s cold @8w (interleaved, full payload); W1/W8 44/44 hash fields equal; routes
  5642/5642; look and far-survival audits pass; bad-shell control fails.

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-05 09:56:11 +02:00
1 parent 2656d81ac9
commit 631dd57eb4
14 files changed
+552 -48

No files matched your search

@@ -1,10 +1,22 @@
# Shape world speed round — final half-load verification # Shape world — speed and look verification
## Round 11 — passage shaft anchor spacing (2026-10-05)
Round 11 restores full shaft relief and keeps the landing plate fix. The broad shaft rings track the 8-voxel owner-roughness anchors: the 20-pixel radial-profile correlation falls from **0.376** to **0.038** when passage-controlled walls use 2-voxel anchors. Finer distance quantization and cubic anchor interpolation leave the repeat in place. The focused image gate fails the full-detail pre-fix capture for rings, fails the smooth-tube control for lost relief, and passes the final capture on both measures; the shaft wall relief RMS is **1.152×** nearby cave walls.
The required cold W8 full-payload interleave averages **0.984 s** for Round 11 and **0.986 s** for Round 10. Both new runs are below one second. All runs generate 2,226 tiles with the full **110.90 MiB** payload and zero cache hits. W1/W8 match all 44 hash fields both at the default benchmark position and around the passage camera; the default region also matches the restored full-detail pre-fix executable. Routes remain **5,642/5,642**; the shape/look and far-survival audits pass, and the damaged-shell control fails as intended. The six fixed captures and four remote yaw views are recaptured and compared in the [Round 11 report](round11/REPORT.md).
## Round 10 — passage look defects (2026-10-05)
Round 10 removes the false landing-floor rock overlay and suppresses coherent relief where passage capsules control the shaft wall. The fixed passage and close-up image gates fail on the Round 9 captures and pass on Round 10. The two small components reported at a tile boundary were traced to per-tile grouping: both share every tested edge vertex with an adjacent tile, so they were not deleted as shards. Full captures, pixel comparisons, diagnostics, audits, failed attempts, and logs are in the [Round 10 report](round10/REPORT.md).
The required cold W8 full-payload interleave averages **0.993 s** for Round 10 versus **1.022 s** for Round 9; one of two new samples is 1.014 s and the other is 0.972 s. A separate new-only repeat took **1.044 s**, making the three-new-run mean **1.010 s**; the local under-one-second margin is not robust. W1/W8 match all 44 output hashes, routes remain 5,642/5,642, far survival passes, and the bad-shell control still fails as intended. The six fixed captures and all four remote yaw views are included in the report.
## Round 9 — packed X batches and interval reuse (2026-10-05) ## Round 9 — packed X batches and interval reuse (2026-10-05)
The final executable averages **0.992 s** for the required cold W8 full-payload direct interleave, below the 1-second target. It measured **1.001 / 0.983 s** (mean 0.992) versus **1.217 / 1.150 s** (mean 1.184) for the local `16612ac` executable. A separate new-only cold repeat was 1.005 s; all three new runs average **0.996 s**. Every run generated 2,226/2,226 tiles with the full 110.90 MiB payload and zero cache hits. The fixed six captures are pixel-identical; the four X=500 remote views are being recaptured after confirming their original Y/Z camera position. The final executable averages **0.992 s** for the required cold W8 full-payload direct interleave, below the 1-second target. It measured **1.001 / 0.983 s** (mean 0.992) versus **1.217 / 1.150 s** (mean 1.184) for the local `16612ac` executable. A separate new-only cold repeat was 1.005 s; all three new runs average **0.996 s**. Every run generated 2,226/2,226 tiles with the full 110.90 MiB payload and zero cache hits. The six fixed captures are pixel-identical, and the four remote views were recaptured against Round 8 with the recorded camera positions; pixel deltas are in the [Round 9 report](round9/REPORT.md).
The largest gain comes from batching eight fine packed-density samples along X: the LOD0/1 density-grid profiles fall 62%/61%. Reusing Z-axis intervals in the candidate mask cuts interval/sign work by about 20 ms and removes 18.35 MiB of retained candidate-axis allocation. The final W1/W8 matrix matches all 44 fields and W8 matches Round 8. Shape/look and far-survival audits pass; the injected bad-shell control still fails as intended. Full measurements, rejected probes, logs, and the pending remote-camera note are in [Round 9 report](round9/REPORT.md). The largest gain comes from batching eight fine packed-density samples along X: the LOD0/1 density-grid profiles fall 62%/61%. Reusing Z-axis intervals in the candidate mask cuts interval/sign work by about 20 ms and removes 18.35 MiB of retained candidate-axis allocation. The final W1/W8 matrix matches all 44 fields and W8 matches Round 8. Shape/look and far-survival audits pass; the injected bad-shell control still fails as intended. Full measurements, rejected probes, and logs are in the [Round 9 report](round9/REPORT.md).
## Round 8 — canonical AVX2 shape arithmetic (2026-10-05) ## Round 8 — canonical AVX2 shape arithmetic (2026-10-05)
@@ -0,0 +1,77 @@
# Round 10 — passage look defects
## Result
Two defects are fixed in the final `VoxelForgeRound10Final.exe`: the broad landing plate and the repeated high-frequency bands on passage shafts. The changes are local to landing overlays and passage-controlled relief. The chamber, tunnel, far, and tunnel-bend captures are pixel-identical to Round 9.
The required cold, full-payload W8 interleave averaged **0.993 s** for Round 10 and **1.022 s** for `VoxelForgeRound9Final.exe`. The Round 10 samples were **1.014 / 0.972 s**; one sample is 14 ms over the target, while the mean is 7 ms under it. A separate new-only cold repeat measured **1.044 s**, so the local run-to-run margin is not robust yet. All runs generated 2,226 tiles with zero cache hits and a 110.90 MiB payload.
| Order | Executable | Process wall | Shape stage | Detail band | Far-parent build | Full clipmap | Cache hits |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| old A | `VoxelForgeRound9Final.exe` | 1.071 s | 402.974 ms | 210.013 ms | 46.082 ms | 0.473 s | 0 / 2,226 |
| new A | `VoxelForgeRound10Final.exe` | 1.014 s | 399.349 ms | 206.952 ms | 43.985 ms | 0.462 s | 0 / 2,226 |
| old B | `VoxelForgeRound9Final.exe` | 0.973 s | 402.974 ms | 211.865 ms | 44.336 ms | 0.463 s | 0 / 2,226 |
| new B | `VoxelForgeRound10Final.exe` | 0.972 s | 402.045 ms | 210.640 ms | 43.764 ms | 0.464 s | 0 / 2,226 |
| new C (separate repeat) | `VoxelForgeRound10Final.exe` | 1.044 s | 399.033 ms | 206.829 ms | 44.094 ms | 0.465 s | 0 / 2,226 |
Primary interleaved means: process **1.022 → 0.993 s**, shape stage **403.0 → 400.7 ms**, detail band **210.9 → 208.8 ms**, far-parent build **45.2 → 43.9 ms**, full clipmap **468 → 463 ms**. Startup/setup/finalization, derived by subtracting shape and full-clipmap walls from process wall, is approximately **151 → 129 ms** on the paired runs; the separate new C remainder is **180 ms**, so startup variation dominates the small margin. It is not a separately timed stage. Head-to-head stdout logs: [old A](../round10_ab_oldA_w8.stdout.log), [new A](../round10_ab_newA_w8.stdout.log), [old B](../round10_ab_oldB_w8.stdout.log), [new B](../round10_ab_newB_w8.stdout.log). Separate repeat: [stdout](../round10_final_repeat_c_w8.stdout.log), [runner](../round10_final_repeat_c_w8.runner.log). The final `/MP8` build is [here](build_round10_final_full_suppression.log).
## Causes and fixes
### Passage shaft bands
The bands came from relief displacement on passage capsule surfaces, not from the passage radius profile or segment joins. Radius/slope joins are still zero. The earlier radius and filter probes made no visible change; owner roughness alone and surface value noise alone each retained the bands. Tagging the passage source and suppressing its relief removes them while leaving detail on other cave walls unchanged.
I rejected partial suppression: a 50%-relief probe had shaft high-pass RMS **0.822**, and a 20%-relief probe measured **0.386** and still showed residual rings. The final fully passage-controlled wall measures **0.276**. The fixed-camera [ring check](check_passage_rings.py) uses a 0.350 limit: it fails Round 9 at **1.331** and passes Round 10 at **0.276** ([before](ring_check_before_machine.stdout.log), [after](ring_check_after_machine.stdout.log)). The [final close-up](after_fixed_passage_floor_closeup.png) shows the result.
### Landing and floor plate
`AddLanding` added a shallow positive-rock ellipsoid directly under the floor. It formed the broad brown shelf where the shaft meets the passage floor. The landing carve and native floor remain; the solid ellipsoid was removed. In the fixed passage camera, the warm plate mask drops from **71.2661%** to **0.0000%** of its ROI. The [plate check](check_landing_plate.py) fails the Round 9 capture and passes Round 10 ([before](landing_check_before_machine.stdout.log), [after](landing_check_after_machine.stdout.log)). The chamber image is unchanged.
### Boundary components / slivers
The final shard audit reports two small components at tile `(-3,-14,-3)`: 56 triangles at 3.079 vox thickness and 20 triangles at 1.347 vox. Neither is a thin sheet. An instrumented cross-tile check found **10/10** and **4/4** exact boundary vertices on the adjacent tile, respectively. They are connected surface pieces split by the per-tile component audit, rather than isolated shards. The final audit still reports zero isolated in-view components and zero thin sheets; its two boundary candidates remain connected across the seam. I made no geometry deletion here because it would cut the continuing surface. If the viewer shows a separate visible seam artifact beyond these audit hits, this check does not claim to resolve it.
Evidence: [final shard audit](../round10_final_shard_audit_w8.stdout.log), [cross-tile probe](../round10_cross_tile_final_probe_shard.stdout.log). The latter used a temporary audit-only instrumentation build; `StandaloneViewer.cpp` was restored afterward.
## Determinism, audits, and payload
- W1 and W8 match all **44** density, mesh, payload, and topology hash fields: [comparison](hash_compare_machine.stdout.log), [W1](../round10_final_hash_w1.stdout.log), [W8](../round10_final_hash_w8.stdout.log).
- Passage walkability is **5,642/5,642**. Shape/look checks pass: GridStair relative join step **0.090 / 0.400**, zero thin sheets, detail audit pass, and flat-patch share **6.5%**.
- Far survival passes **7,200/7,200** boundary surfaces and all **10,976** connectors across **178,351** sampled sections. Missing surfaces, holes, and unsampled connectors are zero: [audit](../round10_final_far_survival_w8.stdout.log).
- The damaged-shell control still fails as required: **512** triangles removed, **16** missing surfaces and **16** holes, exit code **14**: [control](../round10_final_bad_shell_control_w8.stdout.log).
- The direct benchmark and both hash runs generated all **2,226/2,226** tiles, used the complete **110.90 MiB** payload, and recorded zero mesh-cache hits.
## Captures
The six Round 9 before captures and six Round 10 after captures use the established fixed cameras. Pixel comparison: [full results](capture_pixel_diffs_machine.stdout.log).
| View | Camera `(x,y,z,yaw,pitch)` | Round 9 before | Round 10 after | Changed pixels |
| --- | --- | --- | --- | ---: |
| Chamber | `(-11.75,-123.25,-19.85,0,0)` | [before](before_fixed_chamber.png) | [after](after_fixed_chamber.png) | 0 |
| Tunnel | `(-11.75,-123.25,-19.85,90,0)` | [before](before_fixed_tunnel.png) | [after](after_fixed_tunnel.png) | 0 |
| Passage | `(-39.19,6.27,-28.51,63,0)` | [before](before_fixed_passage.png) | [after](after_fixed_passage.png) | 932,273 / 1,024,000 |
| Far | `(-11.61244,-118.2212,-19.8452,0,0)` | [before](before_fixed_far.png) | [after](after_fixed_far.png) | 0 |
| Passage-floor close-up | `(-39.19,6.27,-25.51,63,-25)` | [before](before_fixed_passage_floor_closeup.png) | [after](after_fixed_passage_floor_closeup.png) | 972,890 / 1,024,000 |
| Tunnel bend | `(-198.97,-99.57,-45.58,33.59,23.17)` | [before](before_fixed_tunnel_bend.png) | [after](after_fixed_tunnel_bend.png) | 0 |
The remote captures use `(500,0,0,yaw,0)` at yaw 0/90/180/270. The first comparison is Round 8 vs Round 9; the second is Round 9 vs Round 10.
| Yaw | Round 8 | Round 9 | Changed R8→R9 | Round 10 | Changed R9→R10 |
| ---: | --- | --- | ---: | --- | ---: |
| 0 | [image](r8_remote_yaw0.png) | [image](r9_remote_yaw0.png) | 0 / 1,024,000 | [image](round10_remote_yaw0.png) | 136 / 1,024,000 |
| 90 | [image](r8_remote_yaw90.png) | [image](r9_remote_yaw90.png) | 111 / 1,024,000 | [image](round10_remote_yaw90.png) | 2 / 1,024,000 |
| 180 | [image](r8_remote_yaw180.png) | [image](r9_remote_yaw180.png) | 32 / 1,024,000 | [image](round10_remote_yaw180.png) | 0 / 1,024,000 |
| 270 | [image](r8_remote_yaw270.png) | [image](r9_remote_yaw270.png) | 386 / 1,024,000 | [image](round10_remote_yaw270.png) | 386 / 1,024,000 |
Per-channel means/maxima are in [Round 8 vs Round 9 results](remote_compare.log) and [Round 9 vs Round 10 results](capture_pixel_diffs_machine.stdout.log).
## Failed attempts and machine rules
- One capture-wrapper invocation used a nonexistent `...VoxelForge\speed_round` working directory and failed before launching a child (Windows error 267). It was rerun from `Standalone\Research\OwnerShapeWorld\speed_round`; no workload process started on the failed attempt.
- The first six image/hash helper launches through `RunMachinePython.ps1` passed the script path to `Start-Process` without quoting its `E:\Projet Unreal` path; Python exited 2 with “can't open file `E:\Projet`.” I quoted the path and reran all six hash, pixel, and baseline/final gate checks successfully (the two baseline gates exit 1 by design). Records: [failed launches](python_wrapper_initial_failure.log), [constrained reruns](machine_helper_runs.log).
- The first landing-image check expected 1024×768, while captures are 1280×800. That check failed on both images; the ROI script was corrected and rerun. The recorded final before/after logs above use 1280×800.
- The 20%-relief probe was rejected for visible residual bands, despite passing the earlier looser 0.55 exploratory threshold. The final threshold is 0.35; the final image passes.
- The bad-shell exit 14 and the two baseline image-gate exit 1 results are expected controls. All builds succeeded with `/MP8`; final benchmark, hash, audit, capture, and image-check processes ran BelowNormal on affinity `0xFF00`. No process returned `0xC0000142`, and no viewer owned by anyone else was closed.
No commit, push, or stash was made.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

@@ -0,0 +1,37 @@
from pathlib import Path
import numpy as np
from PIL import Image
ROOT = Path(__file__).parent
FILES = (
"before_fixed_passage_floor_closeup.png",
"owner_relief_only_passage_floor_closeup.png",
"surface_relief_only_passage_floor_closeup.png",
"smooth_detail_probe_passage_floor_closeup.png",
)
CX, CY = 638.0, 253.0
THETA = np.deg2rad(np.linspace(195, 345, 61))
RADII = np.arange(28, 226)
KERNEL = np.ones(41, dtype=np.float64) / 41.0
for filename in FILES:
image = np.asarray(Image.open(ROOT / filename).convert("RGB"), dtype=np.float64)
luminance = 0.2126 * image[:, :, 0] + 0.7152 * image[:, :, 1] + 0.0722 * image[:, :, 2]
xs = np.rint(CX + RADII[:, None] * np.cos(THETA)[None, :]).astype(np.int32)
ys = np.rint(CY + RADII[:, None] * np.sin(THETA)[None, :]).astype(np.int32)
profile = luminance[ys, xs].mean(axis=1)
detrended = profile - np.convolve(profile, KERNEL, mode="same")
signal = detrended[20:-20]
autocorrelation = [
float(np.dot(signal[:-lag], signal[lag:])
/ (np.linalg.norm(signal[:-lag]) * np.linalg.norm(signal[lag:]) + 1.0e-9))
for lag in range(5, 41)
]
print(
f"{filename}: band_rms={float(np.sqrt(np.mean(signal * signal))):.5f} "
f"max_periodic_corr={max(autocorrelation):.5f} "
f"best_lag_px={5 + int(np.argmax(autocorrelation))} "
f"corr20={autocorrelation[15]:.5f} corr24={autocorrelation[19]:.5f}"
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

@@ -0,0 +1,33 @@
"""Image gate for the broad landing shelf in the fixed passage camera."""
import argparse
from pathlib import Path
import numpy as np
from PIL import Image
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("capture", type=Path)
args = parser.parse_args()
image = np.asarray(Image.open(args.capture).convert("RGB"), dtype=np.int16)
if image.shape[:2] != (800, 1280):
raise SystemExit(f"FAIL: expected 1280x800 capture, got {image.shape[1]}x{image.shape[0]}")
# At camera (-39.19, 6.27, -28.51, 63, 0), the unwanted disk fills this
# central passage ROI with brown rock. The natural cave/floor colors are cool.
roi = image[450:580, 0:850]
warm_plate = ((roi[:, :, 0] - roi[:, :, 1]) > 5) & ((roi[:, :, 1] - roi[:, :, 2]) > 5)
coverage = float(warm_plate.mean())
print(f"[LandingPlateImageAudit] warm_roi_share={coverage:.4%} limit=2.0000%")
if coverage > 0.02:
print("[LandingPlateImageAudit] status=FAIL")
return 1
print("[LandingPlateImageAudit] status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,35 @@
"""Guard against high-frequency relief bands in the fixed passage shaft view."""
import argparse
from pathlib import Path
import numpy as np
from PIL import Image, ImageFilter
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("capture", type=Path)
args = parser.parse_args()
image = Image.open(args.capture).convert("L")
if image.size != (1280, 800):
raise SystemExit(f"FAIL: expected 1280x800 capture, got {image.width}x{image.height}")
# The upper shaft in the fixed camera occupies this ROI. A 2 px high-pass
# isolates the sharp repeated ridges; broad cave shading is removed by blur.
pixels = np.asarray(image, dtype=np.float32)
blurred = np.asarray(image.filter(ImageFilter.GaussianBlur(2.0)), dtype=np.float32)
roi = (slice(0, 180), slice(500, 770))
high_pass = pixels[roi] - blurred[roi]
energy = float(np.sqrt(np.mean(high_pass * high_pass)))
print(f"[PassageRingImageAudit] shaft_highpass_rms={energy:.5f} limit=0.35000")
if energy > 0.35:
print("[PassageRingImageAudit] status=FAIL")
return 1
print("[PassageRingImageAudit] status=PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,49 @@
"""Compare all full-payload stream hashes across 1 and 8 workers."""
from pathlib import Path
import re
ROOT = Path(__file__).parent
LINE = re.compile(r"\[(StreamFullDetail|StreamFullClipmap|StreamLevel)\](.*)")
VALUE = re.compile(r"(density_hash|mesh_hash|payload_hash|topology_hash)=(0x[0-9a-fA-F]+)")
GROUPS = ["full_detail", "whole_view", *(f"lod{i}" for i in range(9))]
FIELDS = ("density", "mesh", "payload", "topology")
def read(path: Path) -> dict[str, dict[str, str]]:
result = {}
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
match = LINE.search(line)
if not match:
continue
kind, tail = match.groups()
lod = re.search(r"\blod=(\d+)", tail)
group = {
"StreamFullDetail": "full_detail",
"StreamFullClipmap": "whole_view",
"StreamLevel": f"lod{lod.group(1)}" if lod else None,
}[kind]
if group is None:
raise SystemExit(f"FAIL: missing LOD in {line}")
values = {key.removesuffix("_hash"): value.lower() for key, value in VALUE.findall(tail)}
if values:
result[group] = values
return result
left = read(ROOT.parent / "round10_final_hash_w1.stdout.log")
right = read(ROOT.parent / "round10_final_hash_w8.stdout.log")
for name, values in (("W1", left), ("W8", right)):
if len(values) != len(GROUPS):
raise SystemExit(f"FAIL {name}: found {len(values)} hash groups, expected {len(GROUPS)}")
checks = 0
for group in GROUPS:
for field in FIELDS:
a = left.get(group, {}).get(field)
b = right.get(group, {}).get(field)
if a is None or b is None or a != b:
raise SystemExit(f"FAIL W1/W8 {group}.{field}: {a} != {b}")
checks += 1
print(f"PASS W1 matches W8: {checks} density/mesh/payload/topology fields")
@@ -0,0 +1,20 @@
from pathlib import Path
import numpy as np
from PIL import Image
ROOT = Path(__file__).parent
for yaw in (0, 90, 180, 270):
before = np.asarray(Image.open(ROOT / f"r8_remote_yaw{yaw}.png").convert("RGB"), dtype=np.int16)
after = np.asarray(Image.open(ROOT / f"r9_remote_yaw{yaw}.png").convert("RGB"), dtype=np.int16)
if before.shape != after.shape:
raise SystemExit(f"yaw {yaw}: dimensions differ {before.shape} vs {after.shape}")
delta = np.abs(after - before)
changed = np.any(delta != 0, axis=2)
print(
f"remote_yaw{yaw}: changed_pixels={int(changed.sum())}/{changed.size} "
f"changed_channels={int(np.count_nonzero(delta))}/{delta.size} "
f"mean_abs_channel_delta={float(delta.mean()):.6f} "
f"max_channel_delta={int(delta.max())}"
)
@@ -0,0 +1,44 @@
"""Compare Round 9 before captures with Round 10 fixed and remote captures."""
from pathlib import Path
import numpy as np
from PIL import Image
ROOT = Path(__file__).parent
FIXED = (
"chamber",
"tunnel",
"passage",
"far",
"passage_floor_closeup",
"tunnel_bend",
)
def compare(label: str, before: Path, after: Path) -> None:
a = np.asarray(Image.open(before).convert("RGB"), dtype=np.int16)
b = np.asarray(Image.open(after).convert("RGB"), dtype=np.int16)
if a.shape != b.shape:
raise SystemExit(f"FAIL {label}: dimensions differ {a.shape} vs {b.shape}")
delta = np.abs(a - b)
changed = np.any(delta != 0, axis=2)
print(
f"{label}: changed_pixels={int(changed.sum())}/{changed.size} "
f"changed_channels={int(np.count_nonzero(delta))}/{delta.size} "
f"mean_abs_channel_delta={float(delta.mean()):.6f} "
f"max_channel_delta={int(delta.max())}"
)
for view in FIXED:
compare(view, ROOT / f"before_fixed_{view}.png", ROOT / f"after_fixed_{view}.png")
print("remote_round9_to_round10:")
for yaw in (0, 90, 180, 270):
compare(
f"remote_yaw{yaw}",
ROOT / f"r9_remote_yaw{yaw}.png",
ROOT / f"round10_remote_yaw{yaw}.png",
)
@@ -0,0 +1,97 @@
# Round 11 — passage shaft anchor spacing
## Result
Round 11 restores the passage-wall relief removed in Round 10 and keeps the `AddLanding` plate fix. The broad, regularly spaced shaft bands track the owner roughness interpolation anchors: changing only passage-controlled owner roughness from 8-voxel to 2-voxel Z anchors drops the 20-pixel radial-profile correlation from **0.37625** to **0.03793**. Fine rock relief remains: final shaft high-pass RMS is **1.29366** versus **1.33143** on the full-detail baseline, and the field-level passage relief RMS is **1.152×** the nearby cave-wall RMS.
The final W8 cold full-payload runs are **0.983 s** and **0.985 s**. Their mean is **0.984 s**, versus **0.9855 s** for Round 10's **0.985 s** and **0.986 s**. Both final samples stay below one second. The full **110.90 MiB** payload is enabled, all **2,226/2,226** tiles are generated, and every direct run reports zero cache hits.
Final executable: `Standalone/bin/VoxelForgeRound11Final.exe`. The final `/MP8`, `/arch:AVX2`, `/fp:precise` build is [build_round11_final.log](build_round11_final.log).
## Cause and tests
The fixed close-up is 1280×800. The ring analyzer samples a radial profile around the shaft and reports autocorrelation at fixed pixel lags. The restored full-detail image has its broad peak at **20 px**, with lag-20 correlation **0.37625**. In the same view, lag 8 is **-0.17880** and lag 32 is **-0.11875**. Changing only the owner-roughness anchor interval removes this repeated period while keeping relief. Phase and domain-warp probes could lower lag-20 correlation, but they moved the repeat elsewhere—for example, the independent-phase probe has lag-20 correlation **0.10099** and lag-32 correlation **0.41219**—or made the relief visibly stronger and less even.
| Probe | Owner Z anchors on passage walls | Lag 8 | Lag 20 | Lag 32 | Shaft high-pass RMS | Result |
| --- | ---: | ---: | ---: | ---: | ---: | --- |
| Restored full-detail baseline | 8 voxels | -0.17880 | 0.37625 | -0.11875 | 1.33143 | Repeated broad bands |
| `SurfaceDistanceQ` scale refined from 1/16 to 1/256 | 8 voxels | -0.16419 | 0.36106 | -0.10573 | 1.26432 | Quantization is not the cause |
| Cubic Hermite owner-anchor interpolation | 8 voxels | -0.22643 | 0.37990 | -0.08620 | 1.41653 | Does not remove the repeat |
| Per-voxel owner roughness | 1 voxel | 0.03742 | 0.02828 | 0.06502 | 1.36528 | Repeat removed; relief retained |
| **Final passage interpolation** | **2 voxels** | **0.00533** | **0.03793** | **0.07640** | **1.29366** | **Repeat removed; relief retained** |
The screen-space period is camera-dependent; the causal test is the change in the field's Z anchor interval while keeping the noise function and relief amplitude. Reducing the anchor interval to one voxel moves the strongest measured 5–40 px profile correlation to the 5 px lower limit of that band scan. Two-voxel anchors keep lag-20 correlation low and cost less than evaluating the owner roughness independently at every voxel.
`OwnerCaveRoughnessForDetail` uses the two-voxel anchors when passage influence is at least 0.60. A smooth blend from the existing eight-voxel anchors covers passage influence 0.40–0.60. Other cave walls keep their existing eight-voxel interpolation. The landing ellipsoid remains removed from `AddLanding`.
The ring gate is [check_passage_rings.py](check_passage_rings.py). It checks lag-20 correlation below **0.15** and a shaft 2 px high-pass relief floor above **0.70**. This makes the full-detail baseline fail for rings, makes the smooth Round 10 control fail for missing relief, and makes the final capture pass both:
| Capture | Lag-20 correlation | Shaft relief RMS | Gate |
| --- | ---: | ---: | --- |
| [Full-detail before](before_passage_floor_closeup.png) | 0.37625 | 1.33143 | Fail: rings |
| [Round 10 smooth-tube control](../round10/after_fixed_passage_floor_closeup.png) | -0.09955 | 0.27577 | Fail: detail |
| [Round 11 final](after_passage_floor_closeup.png) | 0.03793 | 1.29366 | Pass |
The independent [passage wall detail audit](../round11_passage_detail_audit_w8.stdout.log) samples the actual density-field relief: **13,271** passage-wall samples, RMS **1.0020**; **465,563** nearby ordinary cave-wall samples, RMS **0.8701**; ratio **1.152**, within the **0.75–1.25** range. The explicit `--shape-detail=smooth` control reports ratio **0.000** and exits 8 as intended: [control log](../round11_passage_detail_smooth_control_w8.stdout.log).
Distance quantization and Hermite interpolation therefore do not explain the broad repeat. The owner Z-anchor interval does. The 5 px peak and pixel-scale correlations remain in the final image as fine texture; the two-voxel result is not obtained by suppressing relief.
## Profile and cold interleave
The interleave order was Round 10 A, Round 11 A, Round 11 B, Round 10 B. Each executable ran directly with `--stream-benchmark --shape-world --force-generate --workers=8 --clip-radius=3`, in a fresh process through `RunMachine.ps1`.
| Measure | Round 10 A | Round 11 A | Round 11 B | Round 10 B | Round 10 mean | Round 11 mean |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| Process wall | 0.985 s | 0.983 s | 0.985 s | 0.986 s | 0.9855 s | **0.984 s** |
| Shape stage | 399.310 ms | 411.659 ms | 415.237 ms | 404.777 ms | 402.044 ms | **413.448 ms** |
| Detail band | 206.815 ms | 219.364 ms | 217.363 ms | 212.613 ms | 209.714 ms | **218.364 ms** |
| Far-shape stage | 43.770 ms | 43.573 ms | 49.788 ms | 43.824 ms | 43.797 ms | **46.681 ms** |
| Full clipmap | 460 ms | 464 ms | 462 ms | 460 ms | 460 ms | **463 ms** |
| Payload / cache | 110.90 MiB / 0 hits | 110.90 MiB / 0 hits | 110.90 MiB / 0 hits | 110.90 MiB / 0 hits | | |
Raw logs: [Round 10 A](../round11_round10_oldA_w8.stdout.log), [Round 11 A](../round11_round11_newA_w8.stdout.log), [Round 11 B](../round11_round11_newB_w8.stdout.log), [Round 10 B](../round11_round10_oldB_w8.stdout.log). The process-wall remainder after subtracting shape stage and full-clipmap time is approximately **123 ms** for Round 10 and **108 ms** for Round 11; this is a derived startup/setup/finalization remainder, not a separately timed stage.
The final attributed profile is [here](../round11_final_profile_attribution_w8.stdout.log): shape stage **406.865 ms**, surface-detail band **217.383 ms**, far-shape build **43.548 ms**, full clipmap **459 ms**. It processes **9,389,017** candidate distance samples and **3,527,283** detail evaluations. Sampled `EffectiveRockDistance` work averages **66.3 ns** per distance evaluation; detail work averages **334.0 ns**, including **111.6 ns** owner roughness and **83.0 ns** surface relief. The shape allocation profile reports **36.71 MiB** candidate-axis words, **131.79 MiB** proxy address capacity, and **11.25 MiB** primitive storage; the full stream payload remains **110.90 MiB**. Profile sampling uses 1/256 of distance evaluations and 1/64 of detail evaluations, so the estimated worker sums are not wall-clock durations.
## Determinism, audits, and payload
- The final 1-worker and 8-worker full-payload hash runs match all **44/44** density, mesh, payload, and topology fields both at the default benchmark position and with the stream centered on the passage camera. The default-position W8 hashes also match all **44/44** fields from the restored full-detail pre-fix executable. Against the retained `VoxelForgeShapeRound8Final.exe` executable for `16612ac`, **35/44** default-position fields match; the nine differing fields are the whole-view hashes, LOD2 density, and all four LOD3 hashes. Comparison output: [hash comparison](round11_hash_compare.stdout.log), [default W1](../round11_final_hash_w1.stdout.log), [default W8](../round11_final_hash_w8.stdout.log), [passage W1](../round11_passage_hash_w1.stdout.log), [passage W8](../round11_passage_hash_w8.stdout.log), [restored baseline W8](../round11_full_detail_hash_w8.stdout.log), [16612ac W8](../round11_16612ac_hash_w8.stdout.log).
- The standard shape/look audit passes: [log](../round11_final_shape_audit_w8.stdout.log). Routes remain **5,642/5,642**. GridStair relative join step is **0.090 / 0.400**; surface-band family checks, flat-patch share (**6.5%**), detail, floor joins, and tunnel radius pass. The default audit footprint contains no tagged passage wall, so its passage-only ratio is marked `SKIP`; the focused passage-wall audit above runs in the passage region and passes.
- Far survival passes **7,200/7,200** boundary surfaces and all **10,976** connectors across **178,351** sampled sections. Missing surfaces, holes, unsampled connectors, and boundary holes are zero: [audit](../round11_far_survival_w8.stdout.log).
- The damaged-shell control remains red as intended: **512** triangles removed, **16** boundary surfaces missing, **16** holes, exit **14**: [control](../round11_bad_shell_control_w8.stdout.log).
- Direct benchmarks and hash runs generated **2,226/2,226** tiles with the full **110.90 MiB** payload and zero mesh-cache hits.
## Captures
All “before” captures use the local `VoxelForgeRound11FullDetail.exe` baseline before the anchor change; “after” captures use `VoxelForgeRound11Final.exe`. Each image is 1280×800. Cameras are in metres followed by yaw/pitch degrees. Pixel deltas are from [round11_capture_pixel_compare.stdout.log](round11_capture_pixel_compare.stdout.log), generated by [compare_round11_captures.py](compare_round11_captures.py).
| Fixed view | Camera `(x,y,z,yaw,pitch)` | Before | After | Changed pixels |
| --- | --- | --- | --- | ---: |
| Chamber | `(-11.75,-123.25,-19.85,0,0)` | [before](before_chamber.png) | [after](after_chamber.png) | 0 |
| Tunnel | `(-11.75,-123.25,-19.85,90,0)` | [before](before_tunnel.png) | [after](after_tunnel.png) | 0 |
| Passage | `(-39.19,6.27,-28.51,63,0)` | [before](before_passage.png) | [after](after_passage.png) | 265,844 |
| Far | `(-11.61244,-118.2212,-19.8452,0,0)` | [before](before_far.png) | [after](after_far.png) | 0 |
| Passage-floor close-up | `(-39.19,6.27,-25.51,63,-25)` | [before](before_passage_floor_closeup.png) | [after](after_passage_floor_closeup.png) | 528,282 |
| Tunnel bend | `(-198.97,-99.57,-45.58,33.59,23.17)` | [before](before_tunnel_bend.png) | [after](after_tunnel_bend.png) | 0 |
The four remote views use `(500,0,0,yaw,0)`:
| Yaw | Before | After | Changed pixels | Mean channel delta | Max channel delta |
| ---: | --- | --- | ---: | ---: | ---: |
| 0 | [before](before_remote_yaw0.png) | [after](after_remote_yaw0.png) | 53,752 | 2.919636 | 96 |
| 90 | [before](before_remote_yaw90.png) | [after](after_remote_yaw90.png) | 1 | 0.000000 | 1 |
| 180 | [before](before_remote_yaw180.png) | [after](after_remote_yaw180.png) | 113,721 | 4.021039 | 117 |
| 270 | [before](before_remote_yaw270.png) | [after](after_remote_yaw270.png) | 219,245 | 6.364519 | 126 |
The chamber, tunnel, far, and bend captures are pixel-identical. Passage and passage-floor views change as the relief interpolation changes; the remote comparisons also record the pixels affected in those four views for owner review.
## Failed attempts and machine limits
- All final builds succeeded with `/MP8`, `/arch:AVX2`, and `/fp:precise`; they emit the existing `vswhere.exe` lookup diagnostic and `C4838` narrowing warnings in `VoxelStrateManager.cpp`. No process returned `0xC0000142`.
- The first exploratory build log wrapper used a relative `Tee-Object` path after `Build.ps1` changed to `Standalone/Build`; the wrapper returned 1 even though the executable had been produced. It was rerun with an absolute log path. The first analytic-normal probe build had a `C2065` undeclared `ActiveShapeWorld`; the source was corrected and the probe rebuilt successfully.
- Two early Python helper launches omitted required wrapper arguments and exited 1. The first ring-gate launches also resolved image paths from the wrong working directory and raised `FileNotFoundError`; each was rerun with Standalone-relative paths. No viewer started in those failed helper launches.
- The first standard shape audit treated an empty passage sample set in its default region as a failure. The audit now marks that passage-only subcheck `SKIP` when no passage samples are present; the standard audit then passes, and the dedicated passage-region detail audit provides the non-skipped measurement.
- A forced axis-aligned passage-camera `--shape-audit` returned 7 because the view measured **74.8%** axis-locked normals against a **60%** threshold; the pre-fix full-detail executable also failed that down-shaft camera. A separate earlier close-up camera audit measured **65.0%** and also failed. These were orientation diagnostics, not the standard owner-start acceptance audit; the dedicated detail audit avoids that unrelated ray-normal criterion.
- The baseline ring image and smooth-tube image intentionally exit 1 in the ring/relief gate; the `--shape-detail=smooth` field control intentionally exits 8; the bad-shell control intentionally exits 14.
All workload, capture, audit, and helper processes ran BelowNormal on affinity `0xFF00`; no viewer that this work did not start was closed. All edits and outputs are confined to `Standalone\`. No commit, push, or stash was made.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

+140 -45
View File
@@ -138,6 +138,7 @@ public:
double FloorBlendRadius = 0.0; double FloorBlendRadius = 0.0;
bool bCarve = true; bool bCarve = true;
bool bCritical = false; bool bCritical = false;
bool bPassageSurface = false;
bool bCapsule = false; bool bCapsule = false;
bool bSmoothCapsuleRadius = false; bool bSmoothCapsuleRadius = false;
bool bRoundedBox = false; bool bRoundedBox = false;
@@ -612,14 +613,17 @@ private:
} }
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f; ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f; bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float PassageInfluence=0.0f;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF, float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir); &bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
&PassageInfluence);
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f) &&std::abs(RockDistance)<=DetailSupremum+0.125f)
{ {
const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF, const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF,
CaveInfluence,FloorInfluence); CaveInfluence,FloorInfluence);
if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade; if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,PassageInfluence)*Fade;
} }
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f); if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
const bool bRock=RockDistance>=0.0f; const bool bRock=RockDistance>=0.0f;
@@ -683,7 +687,7 @@ public:
return bFlips&&bCoherent&&bFamilies; return bFlips&&bCoherent&&bFamilies;
} }
bool AuditSurfaceBandFamilies() const bool AuditSurfaceBandFamilies(bool bPassageOnly=false) const
{ {
struct FPatchKey struct FPatchKey
{ {
@@ -706,6 +710,7 @@ public:
struct FFamilyStats {uint64 Count=0,NearZero=0;double Sum=0.0,SumSq=0.0;}; struct FFamilyStats {uint64 Count=0,NearZero=0;double Sum=0.0,SumSq=0.0;};
std::unordered_map<FPatchKey,FPatchStats,FPatchHash> Patches; std::unordered_map<FPatchKey,FPatchStats,FPatchHash> Patches;
std::array<FFamilyStats,3> Families{}; std::array<FFamilyStats,3> Families{};
FFamilyStats PassageWalls,OrdinaryCaveWalls;
for(size_t Line=0;Line<static_cast<size_t>(Size[0])*Size[1];++Line) for(size_t Line=0;Line<static_cast<size_t>(Size[0])*Size[1];++Line)
{ {
const int32 X=Min[0]+static_cast<int32>(Line%Size[0]); const int32 X=Min[0]+static_cast<int32>(Line%Size[0]);
@@ -721,16 +726,23 @@ public:
const int32 Z=Min[2]+static_cast<int32>(Zi); const int32 Z=Min[2]+static_cast<int32>(Zi);
ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f; ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f; bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float PassageInfluence=0.0f;
const float Raw=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF, const float Raw=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence); &bFloorSurface,&CaveInfluence,&FloorInfluence,nullptr,nullptr,false,
&PassageInfluence);
if(std::abs(Raw)>=0.5f)continue; if(std::abs(Raw)>=0.5f)continue;
const float Fade=DetailBandFadeAt(X,Y,Z,Raw,SurfaceSDF, const float Fade=DetailBandFadeAt(X,Y,Z,Raw,SurfaceSDF,
CaveInfluence,FloorInfluence); CaveInfluence,FloorInfluence);
const float Detail=Fade>0.0f const float Detail=Fade>0.0f
?DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade:0.0f; ?DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence,
PassageInfluence)*Fade:0.0f;
const size_t FamilyIndex=static_cast<size_t>(Family); const size_t FamilyIndex=static_cast<size_t>(Family);
FFamilyStats& F=Families[FamilyIndex];++F.Count;F.Sum+=Detail;F.SumSq+=Detail*Detail; FFamilyStats& F=Families[FamilyIndex];++F.Count;F.Sum+=Detail;F.SumSq+=Detail*Detail;
if(std::abs(Detail)<0.05f)++F.NearZero; if(std::abs(Detail)<0.05f)++F.NearZero;
if(PassageInfluence>=0.5f)
{++PassageWalls.Count;PassageWalls.Sum+=Detail;PassageWalls.SumSq+=Detail*Detail;}
else if(Family==ESurfaceFamily::Cave&&PassageInfluence<0.1f)
{++OrdinaryCaveWalls.Count;OrdinaryCaveWalls.Sum+=Detail;OrdinaryCaveWalls.SumSq+=Detail*Detail;}
const FPatchKey Key{FloorDiv(X,16),FloorDiv(Y,16),FloorDiv(Z,16), const FPatchKey Key{FloorDiv(X,16),FloorDiv(Y,16),FloorDiv(Z,16),
static_cast<uint8>(FamilyIndex)}; static_cast<uint8>(FamilyIndex)};
FPatchStats& P=Patches[Key];++P.Count;P.Sum+=Detail;P.SumSq+=Detail*Detail; FPatchStats& P=Patches[Key];++P.Count;P.Sum+=Detail;P.SumSq+=Detail*Detail;
@@ -769,7 +781,23 @@ public:
std::printf("[FlatPatchAudit] patch_width=16 vox min_patch_samples=16 flat_stddev_lt=0.10 flat_share=%.1f%% largest_flat_patch=%llu status=%s\n", std::printf("[FlatPatchAudit] patch_width=16 vox min_patch_samples=16 flat_stddev_lt=0.10 flat_share=%.1f%% largest_flat_patch=%llu status=%s\n",
FlatShare,static_cast<unsigned long long>(LargestFlatPatch), FlatShare,static_cast<unsigned long long>(LargestFlatPatch),
Tested>0&&FlatShare<20.0?"PASS":"FAIL"); Tested>0&&FlatShare<20.0?"PASS":"FAIL");
return Tested>0&&FlatShare<20.0&&bPass; const double PassageRms=PassageWalls.Count
?std::sqrt(PassageWalls.SumSq/PassageWalls.Count):0.0;
const double OrdinaryCaveRms=OrdinaryCaveWalls.Count
?std::sqrt(OrdinaryCaveWalls.SumSq/OrdinaryCaveWalls.Count):0.0;
const double PassageRmsRatio=OrdinaryCaveRms>1.0e-9
?PassageRms/OrdinaryCaveRms:0.0;
const bool bHasPassageSamples=PassageWalls.Count>0;
const bool bRatioPass=PassageWalls.Count>=32&&OrdinaryCaveWalls.Count>=32
&&PassageRmsRatio>=0.75&&PassageRmsRatio<=1.25;
const bool bPassageDetail=bPassageOnly?bRatioPass:(!bHasPassageSamples||bRatioPass);
const char* PassageStatus=!bHasPassageSamples
?(bPassageOnly?"FAIL":"SKIP"):(bPassageDetail?"PASS":"FAIL");
std::printf("[PassageWallDetailAudit] passage_samples=%llu passage_rms=%.4f cave_samples=%llu cave_rms=%.4f ratio=%.3f range=(0.750,1.250) status=%s\n",
static_cast<unsigned long long>(PassageWalls.Count),PassageRms,
static_cast<unsigned long long>(OrdinaryCaveWalls.Count),OrdinaryCaveRms,
PassageRmsRatio,PassageStatus);
return bPassageOnly?bPassageDetail:(Tested>0&&FlatShare<20.0&&bPass&&bPassageDetail);
} }
bool AuditViewSurface(double CameraX,double CameraY,double CameraZ, bool AuditViewSurface(double CameraX,double CameraY,double CameraZ,
@@ -788,12 +816,14 @@ public:
const double OX=CameraX*4.0,OY=CameraY*4.0,OZ=CameraZ*4.0; const double OX=CameraX*4.0,OY=CameraY*4.0,OZ=CameraZ*4.0;
auto Distance=[&](double X,double Y,double Z,ESurfaceFamily* OutFamily=nullptr, auto Distance=[&](double X,double Y,double Z,ESurfaceFamily* OutFamily=nullptr,
float* OutRaw=nullptr,float* OutDetail=nullptr, float* OutRaw=nullptr,float* OutDetail=nullptr,
bool* OutFloorSurface=nullptr) bool* OutFloorSurface=nullptr,float* OutPassageInfluence=nullptr)
{ {
ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f; ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f; bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float PassageInfluence=0.0f;
float D=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF, float D=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence); &bFloorSurface,&CaveInfluence,&FloorInfluence,nullptr,nullptr,false,
&PassageInfluence);
float Detail=0.0f; float Detail=0.0f;
const bool bInside=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1] const bool bInside=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2]; &&Z>=Min[2]&&Z<=Max[2];
@@ -802,13 +832,15 @@ public:
{ {
const float Fade=DetailBandFadeAt(X,Y,Z,D,SurfaceSDF,CaveInfluence,FloorInfluence); const float Fade=DetailBandFadeAt(X,Y,Z,D,SurfaceSDF,CaveInfluence,FloorInfluence);
if(Fade>0.0f) if(Fade>0.0f)
{Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade;D+=Detail;} {Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence,
PassageInfluence)*Fade;D+=Detail;}
} }
if(bCriticalAir)D=std::min(D,-1.0f/16.0f); if(bCriticalAir)D=std::min(D,-1.0f/16.0f);
if(OutFamily)*OutFamily=Family; if(OutFamily)*OutFamily=Family;
if(OutRaw)*OutRaw=D-Detail; if(OutRaw)*OutRaw=D-Detail;
if(OutDetail)*OutDetail=Detail; if(OutDetail)*OutDetail=Detail;
if(OutFloorSurface)*OutFloorSurface=bFloorSurface; if(OutFloorSurface)*OutFloorSurface=bFloorSurface;
if(OutPassageInfluence)*OutPassageInfluence=PassageInfluence;
return D; return D;
}; };
uint64 TotalHits=0,TotalAxisLocked=0,TotalNearZero=0,FloorTaggedHits=0,FloorAxisLocked=0; uint64 TotalHits=0,TotalAxisLocked=0,TotalNearZero=0,FloorTaggedHits=0,FloorAxisLocked=0;
@@ -842,8 +874,8 @@ public:
PrimaryHitT=HitT; PrimaryHitT=HitT;
const double XH=OX+DX*HitT,YH=OY+DY*HitT,ZH=OZ+DZ*HitT; const double XH=OX+DX*HitT,YH=OY+DY*HitT,ZH=OZ+DZ*HitT;
ESurfaceFamily Family=ESurfaceFamily::Strata;float Raw=0.0f,Detail=0.0f; ESurfaceFamily Family=ESurfaceFamily::Strata;float Raw=0.0f,Detail=0.0f;
bool bFloorSurface=false; bool bFloorSurface=false;float PassageInfluence=0.0f;
Distance(XH,YH,ZH,&Family,&Raw,&Detail,&bFloorSurface); Distance(XH,YH,ZH,&Family,&Raw,&Detail,&bFloorSurface,&PassageInfluence);
const double GX=Distance(XH+0.5,YH,ZH)-Distance(XH-0.5,YH,ZH); const double GX=Distance(XH+0.5,YH,ZH)-Distance(XH-0.5,YH,ZH);
const double GY=Distance(XH,YH+0.5,ZH)-Distance(XH,YH-0.5,ZH); const double GY=Distance(XH,YH+0.5,ZH)-Distance(XH,YH-0.5,ZH);
const double GZ=Distance(XH,YH,ZH+0.5)-Distance(XH,YH,ZH-0.5); const double GZ=Distance(XH,YH,ZH+0.5)-Distance(XH,YH,ZH-0.5);
@@ -897,8 +929,8 @@ public:
{ {
const char* Name=Family==ESurfaceFamily::Strata?"strata": const char* Name=Family==ESurfaceFamily::Strata?"strata":
Family==ESurfaceFamily::Cave?"cave":"solid_overlay"; Family==ESurfaceFamily::Cave?"cave":"solid_overlay";
std::printf("[SurfaceRay] t=%.2f vox hit=(%.2f,%.2f,%.2f) family=%s floor_surface=%s raw_sdf=%.4f detail=%.4f axis_dot=%.4f\n", std::printf("[SurfaceRay] t=%.2f vox hit=(%.2f,%.2f,%.2f) family=%s floor_surface=%s passage_influence=%.3f raw_sdf=%.4f detail=%.4f axis_dot=%.4f\n",
HitT,XH,YH,ZH,Name,bFloorSurface?"yes":"no",Raw,Detail,AxisDot); HitT,XH,YH,ZH,Name,bFloorSurface?"yes":"no",PassageInfluence,Raw,Detail,AxisDot);
} }
bFound=true;break; bFound=true;break;
} }
@@ -1872,6 +1904,7 @@ private:
const double CoreExtendStart=I>0?CoreExtension:0.0,CoreExtendEnd=I+2<Points.Num()?CoreExtension:0.0; const double CoreExtendStart=I>0?CoreExtension:0.0,CoreExtendEnd=I+2<Points.Num()?CoreExtension:0.0;
AddTaperedCapsule(Points[I],Points[I+1],RadiusA,RadiusB,true,false,true,ZA,ZB, AddTaperedCapsule(Points[I],Points[I+1],RadiusA,RadiusB,true,false,true,ZA,ZB,
ExtendStart,ExtendEnd); ExtendStart,ExtendEnd);
Primitives.back().bPassageSurface=true;
SetCurvedFloorClip(Primitives.back(),Points[I],Points[I+1],ZA,ZB, SetCurvedFloorClip(Primitives.back(),Points[I],Points[I+1],ZA,ZB,
FloorTangents[static_cast<size_t>(I)],FloorTangents[static_cast<size_t>(I+1)]); FloorTangents[static_cast<size_t>(I)],FloorTangents[static_cast<size_t>(I+1)]);
++Stats.CurvedFloorSegments; ++Stats.CurvedFloorSegments;
@@ -1929,7 +1962,11 @@ private:
} }
} }
} }
else AddCapsule(P.UpperPoint,P.LowerPoint,std::max(6.0f,P.Radius),true,true); else
{
AddCapsule(P.UpperPoint,P.LowerPoint,std::max(6.0f,P.Radius),true,true);
Primitives.back().bPassageSurface=true;
}
AddLanding(P.UpperLanding,true); AddLanding(P.UpperLanding,true);
AddLanding(P.LowerLanding,true); AddLanding(P.LowerLanding,true);
} }
@@ -1955,8 +1992,8 @@ private:
AddEllipsoid(Center,{2.5,2.5,HalfZ},{1,0,0},{0,1,0},{0,0,1},true); AddEllipsoid(Center,{2.5,2.5,HalfZ},{1,0,0},{0,1,0},{0,0,1},true);
for(size_t I=Before;I<Primitives.size();++I)Primitives[I].bCritical=true; for(size_t I=Before;I<Primitives.size();++I)Primitives[I].bCritical=true;
} }
const FVector Floor(Center.X,Center.Y,L.FloorZ-1.25); // The rounded landing carve and native floor already provide support.
AddEllipsoid(Floor,{Width,Width,1.25},{1,0,0},{0,1,0},{0,0,1},false); // A shallow positive ellipsoid under this floor creates a broad shaft/floor plate.
} }
void BuildRoundTunnelPrimitives() void BuildRoundTunnelPrimitives()
@@ -2637,6 +2674,7 @@ private:
const float SurfaceSDF=Sample.SurfaceSDF; const float SurfaceSDF=Sample.SurfaceSDF;
const float CaveInfluence=Sample.CaveInfluence; const float CaveInfluence=Sample.CaveInfluence;
const float FloorInfluence=Sample.FloorInfluence; const float FloorInfluence=Sample.FloorInfluence;
const float PassageInfluence=Sample.PassageInfluence;
const bool bCriticalAir=Sample.bCriticalAir; const bool bCriticalAir=Sample.bCriticalAir;
float DetailFade=DetailBandFade(SDF); float DetailFade=DetailBandFade(SDF);
float MacroValue=0.0f; float MacroValue=0.0f;
@@ -2662,7 +2700,7 @@ private:
{ {
if(bProfile)++Profile.DetailEvaluations; if(bProfile)++Profile.DetailEvaluations;
Displaced+=DetailNoiseOnLine(X,Y,Z,Sample.SurfaceSDF, Displaced+=DetailNoiseOnLine(X,Y,Z,Sample.SurfaceSDF,
CaveInfluence,FloorInfluence,NoiseLine,bTimeDetail?&Profile:nullptr, CaveInfluence,FloorInfluence,PassageInfluence,NoiseLine,bTimeDetail?&Profile:nullptr,
DetailField,bDetailFieldKnown,bMacroKnown,MacroValue)*DetailFade; DetailField,bDetailFieldKnown,bMacroKnown,MacroValue)*DetailFade;
} }
if(bTimeDetail) if(bTimeDetail)
@@ -2906,12 +2944,14 @@ private:
float* OutFloorInfluence=nullptr, float* OutFloorInfluence=nullptr,
bool* OutCriticalAir=nullptr, bool* OutCriticalAir=nullptr,
const FBinItems* KnownBinItems=nullptr, const FBinItems* KnownBinItems=nullptr,
bool bKnownBin=false) const bool bKnownBin=false,
float* OutPassageInfluence=nullptr) const
{ {
const float Base=StrataDistance(Z); const float Base=StrataDistance(Z);
float Cave=100000.0f; float Cave=100000.0f;
float Overlay=-100000.0f; float Overlay=-100000.0f;
float CaveFloorInfluence=0.0f; float CaveFloorInfluence=0.0f;
float CavePassageInfluence=0.0f;
bool bCriticalAir=false; bool bCriticalAir=false;
const FBinItems* BinItems=KnownBinItems; const FBinItems* BinItems=KnownBinItems;
if(!bKnownBin) if(!bKnownBin)
@@ -2949,6 +2989,9 @@ private:
const float PrimitiveWeight=SmoothUnit(0.5f+(Cave-Distance)/(2.0f*MetadataWidth)); const float PrimitiveWeight=SmoothUnit(0.5f+(Cave-Distance)/(2.0f*MetadataWidth));
const float Combined=SmoothMinDistance(Cave,Distance,static_cast<float>(P.BlendRadius)); const float Combined=SmoothMinDistance(Cave,Distance,static_cast<float>(P.BlendRadius));
CaveFloorInfluence=std::lerp(CaveFloorInfluence,PrimitiveFloorInfluence,PrimitiveWeight); CaveFloorInfluence=std::lerp(CaveFloorInfluence,PrimitiveFloorInfluence,PrimitiveWeight);
if(OutPassageInfluence&&(CavePassageInfluence>0.0f||P.bPassageSurface))
CavePassageInfluence=std::lerp(CavePassageInfluence,
P.bPassageSurface?1.0f:0.0f,PrimitiveWeight);
Cave=Combined; Cave=Combined;
} }
else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1] else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1]
@@ -2965,6 +3008,7 @@ private:
const float CarveInfluence=SmoothUnit(0.5f+(Base-CarveOrOverlay)/(2.0f*FamilyBlendWidth)); const float CarveInfluence=SmoothUnit(0.5f+(Base-CarveOrOverlay)/(2.0f*FamilyBlendWidth));
const float CaveInfluence=CarveInfluence*CaveVsOverlay; const float CaveInfluence=CarveInfluence*CaveVsOverlay;
const float FloorInfluence=CaveInfluence*CaveFloorInfluence; const float FloorInfluence=CaveInfluence*CaveFloorInfluence;
const float PassageInfluence=CaveInfluence*CavePassageInfluence;
const float RockDistance=SmoothMinDistance(Base,CarveOrOverlay,StrataCaveBlendRadius); const float RockDistance=SmoothMinDistance(Base,CarveOrOverlay,StrataCaveBlendRadius);
if(OutFamily) if(OutFamily)
*OutFamily=Family; *OutFamily=Family;
@@ -2973,6 +3017,7 @@ private:
(Family==ESurfaceFamily::Cave?Cave:Overlay); (Family==ESurfaceFamily::Cave?Cave:Overlay);
if(OutCaveInfluence)*OutCaveInfluence=CaveInfluence; if(OutCaveInfluence)*OutCaveInfluence=CaveInfluence;
if(OutFloorInfluence)*OutFloorInfluence=FloorInfluence; if(OutFloorInfluence)*OutFloorInfluence=FloorInfluence;
if(OutPassageInfluence)*OutPassageInfluence=PassageInfluence;
if(OutFloorSurface)*OutFloorSurface=FloorInfluence>=0.5f; if(OutFloorSurface)*OutFloorSurface=FloorInfluence>=0.5f;
return RockDistance; return RockDistance;
} }
@@ -2983,6 +3028,7 @@ private:
float SurfaceSDF=0.0f; float SurfaceSDF=0.0f;
float CaveInfluence=0.0f; float CaveInfluence=0.0f;
float FloorInfluence=0.0f; float FloorInfluence=0.0f;
float PassageInfluence=0.0f;
ESurfaceFamily Family=ESurfaceFamily::Strata; ESurfaceFamily Family=ESurfaceFamily::Strata;
bool bFloorSurface=false; bool bFloorSurface=false;
bool bCriticalAir=false; bool bCriticalAir=false;
@@ -3085,7 +3131,7 @@ private:
void EffectiveRockDistanceZ8(int32 X,int32 Y,const int32 Z[8],int32 Count, void EffectiveRockDistanceZ8(int32 X,int32 Y,const int32 Z[8],int32 Count,
const FBinItems* BinItems,FEffectiveRockSample Out[8]) const const FBinItems* BinItems,FEffectiveRockSample Out[8]) const
{ {
float Base[8],Cave[8],Overlay[8],CaveFloorInfluence[8]; float Base[8],Cave[8],Overlay[8],CaveFloorInfluence[8],CavePassageInfluence[8];
bool CriticalAir[8]{}; bool CriticalAir[8]{};
for(int32 Lane=0;Lane<Count;++Lane) for(int32 Lane=0;Lane<Count;++Lane)
{ {
@@ -3093,6 +3139,7 @@ private:
Cave[Lane]=100000.0f; Cave[Lane]=100000.0f;
Overlay[Lane]=-100000.0f; Overlay[Lane]=-100000.0f;
CaveFloorInfluence[Lane]=0.0f; CaveFloorInfluence[Lane]=0.0f;
CavePassageInfluence[Lane]=0.0f;
} }
if(BinItems) if(BinItems)
{ {
@@ -3158,6 +3205,9 @@ private:
static_cast<float>(P.BlendRadius)); static_cast<float>(P.BlendRadius));
CaveFloorInfluence[Lane]=std::lerp(CaveFloorInfluence[Lane], CaveFloorInfluence[Lane]=std::lerp(CaveFloorInfluence[Lane],
PrimitiveFloorInfluence,PrimitiveWeight); PrimitiveFloorInfluence,PrimitiveWeight);
if(CavePassageInfluence[Lane]>0.0f||P.bPassageSurface)
CavePassageInfluence[Lane]=std::lerp(CavePassageInfluence[Lane],
P.bPassageSurface?1.0f:0.0f,PrimitiveWeight);
Cave[Lane]=Combined; Cave[Lane]=Combined;
} }
} }
@@ -3180,12 +3230,14 @@ private:
+(Base[Lane]-CarveOrOverlay)/(2.0f*FamilyBlendWidth)); +(Base[Lane]-CarveOrOverlay)/(2.0f*FamilyBlendWidth));
const float CaveInfluence=CarveInfluence*CaveVsOverlay; const float CaveInfluence=CarveInfluence*CaveVsOverlay;
const float FloorInfluence=CaveInfluence*CaveFloorInfluence[Lane]; const float FloorInfluence=CaveInfluence*CaveFloorInfluence[Lane];
const float PassageInfluence=CaveInfluence*CavePassageInfluence[Lane];
Out[Lane].Distance=SmoothMinDistance(Base[Lane],CarveOrOverlay,StrataCaveBlendRadius); Out[Lane].Distance=SmoothMinDistance(Base[Lane],CarveOrOverlay,StrataCaveBlendRadius);
Out[Lane].Family=Family; Out[Lane].Family=Family;
Out[Lane].SurfaceSDF=Family==ESurfaceFamily::Strata?Base[Lane]: Out[Lane].SurfaceSDF=Family==ESurfaceFamily::Strata?Base[Lane]:
(Family==ESurfaceFamily::Cave?Cave[Lane]:Overlay[Lane]); (Family==ESurfaceFamily::Cave?Cave[Lane]:Overlay[Lane]);
Out[Lane].CaveInfluence=CaveInfluence; Out[Lane].CaveInfluence=CaveInfluence;
Out[Lane].FloorInfluence=FloorInfluence; Out[Lane].FloorInfluence=FloorInfluence;
Out[Lane].PassageInfluence=PassageInfluence;
Out[Lane].bFloorSurface=FloorInfluence>=0.5f; Out[Lane].bFloorSurface=FloorInfluence>=0.5f;
Out[Lane].bCriticalAir=CriticalAir[Lane]; Out[Lane].bCriticalAir=CriticalAir[Lane];
} }
@@ -3257,27 +3309,29 @@ private:
{ {
const uint32 Seed=static_cast<uint32>(Owner->Seed); const uint32 Seed=static_cast<uint32>(Owner->Seed);
const float RF=Params.RoughnessFrequency; const float RF=Params.RoughnessFrequency;
const float NoiseX=static_cast<float>(X);
const float NoiseY=static_cast<float>(Y);
const float EffectiveZ=(Params.VerticalScale>0.0f&&Params.VerticalScale!=1.0f) const float EffectiveZ=(Params.VerticalScale>0.0f&&Params.VerticalScale!=1.0f)
?static_cast<float>(Z)/Params.VerticalScale:static_cast<float>(Z); ?static_cast<float>(Z)/Params.VerticalScale:static_cast<float>(Z);
FVector3f Main(static_cast<float>(X)*RF+VoxelHash::SeedOffset(Seed,11.3f), FVector3f Main(NoiseX*RF+VoxelHash::SeedOffset(Seed,11.3f),
static_cast<float>(Y)*RF+VoxelHash::SeedOffset(Seed,13.7f), NoiseY*RF+VoxelHash::SeedOffset(Seed,13.7f),
EffectiveZ*RF+VoxelHash::SeedOffset(Seed,17.1f)); EffectiveZ*RF+VoxelHash::SeedOffset(Seed,17.1f));
FVector3f Fine(static_cast<float>(X)*RF*3.0f+VoxelHash::SeedOffset(Seed,19.1f)+2000.0f, FVector3f Fine(NoiseX*RF*3.0f+VoxelHash::SeedOffset(Seed,19.1f)+2000.0f,
static_cast<float>(Y)*RF*3.0f+VoxelHash::SeedOffset(Seed,23.7f)+2500.0f, NoiseY*RF*3.0f+VoxelHash::SeedOffset(Seed,23.7f)+2500.0f,
EffectiveZ*RF*3.0f+VoxelHash::SeedOffset(Seed,29.3f)+3000.0f); EffectiveZ*RF*3.0f+VoxelHash::SeedOffset(Seed,29.3f)+3000.0f);
if(Params.DomainWarpStrength>0.0f) if(Params.DomainWarpStrength>0.0f)
{ {
const float WF=Params.DomainWarpFrequency,WS=Params.DomainWarpStrength; const float WF=Params.DomainWarpFrequency,WS=Params.DomainWarpStrength;
float WX=0.0f,WY=0.0f,WZ=0.0f; float WX=0.0f,WY=0.0f,WZ=0.0f;
VoxelNoise::Perlin3D_x3( VoxelNoise::Perlin3D_x3(
static_cast<float>(X)*WF+VoxelHash::SeedOffset(Seed,5.2f), NoiseX*WF+VoxelHash::SeedOffset(Seed,5.2f),
static_cast<float>(Y)*WF+VoxelHash::SeedOffset(Seed,1.3f), NoiseY*WF+VoxelHash::SeedOffset(Seed,1.3f),
EffectiveZ*WF+VoxelHash::SeedOffset(Seed,9.7f), EffectiveZ*WF+VoxelHash::SeedOffset(Seed,9.7f),
static_cast<float>(X)*WF+100.0f+VoxelHash::SeedOffset(Seed,7.7f), NoiseX*WF+100.0f+VoxelHash::SeedOffset(Seed,7.7f),
static_cast<float>(Y)*WF+200.0f+VoxelHash::SeedOffset(Seed,3.1f), NoiseY*WF+200.0f+VoxelHash::SeedOffset(Seed,3.1f),
EffectiveZ*WF+300.0f, EffectiveZ*WF+300.0f,
static_cast<float>(X)*WF+400.0f, NoiseX*WF+400.0f,
static_cast<float>(Y)*WF+500.0f+VoxelHash::SeedOffset(Seed,11.9f), NoiseY*WF+500.0f+VoxelHash::SeedOffset(Seed,11.9f),
EffectiveZ*WF+600.0f+VoxelHash::SeedOffset(Seed,13.3f),WX,WY,WZ); EffectiveZ*WF+600.0f+VoxelHash::SeedOffset(Seed,13.3f),WX,WY,WZ);
WX*=VOXEL_NOISE_SCALE*WS;WY*=VOXEL_NOISE_SCALE*WS;WZ*=VOXEL_NOISE_SCALE*WS; WX*=VOXEL_NOISE_SCALE*WS;WY*=VOXEL_NOISE_SCALE*WS;WZ*=VOXEL_NOISE_SCALE*WS;
Main+=FVector3f(WX,WY,WZ);Fine+=FVector3f(WX,WY,WZ); Main+=FVector3f(WX,WY,WZ);Fine+=FVector3f(WX,WY,WZ);
@@ -3320,18 +3374,21 @@ private:
float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF, float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF,
const FLayerField& Field,float CaveInfluence, const FLayerField& Field,float CaveInfluence,
FOwnerNoiseLineContext& LineContext, FOwnerNoiseLineContext& LineContext,
FDetailWorkerProfile* Profile=nullptr) const FDetailWorkerProfile* Profile=nullptr,
int32 AnchorStep=8) const
{ {
const FStrateGenerationParams& Params=Field.Params; const FStrateGenerationParams& Params=Field.Params;
const float Depth=Params.SurfaceRoughness*2.0f; const float Depth=Params.SurfaceRoughness*2.0f;
if(!(Depth>0.0f)||std::abs(SurfaceSDF)>=Depth||!(Params.RoughnessFrequency>0.0f))return 0.0f; if(!(Depth>0.0f)||std::abs(SurfaceSDF)>=Depth||!(Params.RoughnessFrequency>0.0f))return 0.0f;
if(Profile)++Profile->OwnerRoughnessActiveCalls; if(Profile)++Profile->OwnerRoughnessActiveCalls;
constexpr int32 ZStep=8; const int32 ZStep=std::max(1,AnchorStep);
const int32 GridZ0=static_cast<int32>(std::floor(Z/static_cast<double>(ZStep)))*ZStep; const int32 GridZ0=static_cast<int32>(std::floor(Z/static_cast<double>(ZStep)))*ZStep;
const int32 SampleZ0=std::max(GridZ0,Field.Lo); const int32 SampleZ0=std::max(GridZ0,Field.Lo);
const int32 SampleZ1=std::min(GridZ0+ZStep,Field.Hi); const int32 SampleZ1=std::min(GridZ0+ZStep,Field.Hi);
float Total=0.0f; float Total=0.0f;
if(SampleZ1>SampleZ0) if(ZStep==1)
Total=OwnerCaveRoughnessCore(X,Y,Z,Params);
else if(SampleZ1>SampleZ0)
{ {
const bool bCacheHit=LineContext.Field==&Field&&LineContext.GridZ0==GridZ0; const bool bCacheHit=LineContext.Field==&Field&&LineContext.GridZ0==GridZ0;
if(Profile) if(Profile)
@@ -3357,6 +3414,33 @@ private:
return Total*Fade*Fade; return Total*Fade*Fade;
} }
float OwnerCaveRoughnessForDetail(double X,double Y,double Z,float SurfaceSDF,
const FLayerField& Field,float CaveInfluence,
float PassageInfluence,
FOwnerNoiseLineContext& LineContext,
FDetailWorkerProfile* Profile=nullptr) const
{
constexpr float DirectStart=0.40f,DirectEnd=0.60f;
constexpr int32 PassageAnchorStep=2;
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
if(PassageWeight>=DirectEnd)
return OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field,CaveInfluence,
LineContext,Profile,PassageAnchorStep);
const float Anchored=OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field,
CaveInfluence,LineContext,Profile);
if(PassageWeight<=DirectStart)return Anchored;
const float Direct=OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field,
CaveInfluence,LineContext,Profile,PassageAnchorStep);
const float DirectWeight=SmoothUnit((PassageWeight-DirectStart)
/(DirectEnd-DirectStart));
const float AnchoredWeight=1.0f-DirectWeight;
const float Normalization=1.0f/std::sqrt(
AnchoredWeight*AnchoredWeight+DirectWeight*DirectWeight);
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)
{ {
// The low band carries the broad rock shapes and also gently varies mid-band // The low band carries the broad rock shapes and also gently varies mid-band
@@ -3442,27 +3526,31 @@ private:
}; };
float DetailNoise(double X,double Y,double Z,float SurfaceSDF, float DetailNoise(double X,double Y,double Z,float SurfaceSDF,
float CaveInfluence,float FloorInfluence) const float CaveInfluence,float FloorInfluence,float PassageInfluence) const
{ {
if(DetailVariant==EDetailVariant::Smooth)return 0.0f; if(DetailVariant==EDetailVariant::Smooth)return 0.0f;
if(DetailVariant==EDetailVariant::PerVoxel) if(DetailVariant==EDetailVariant::PerVoxel)
{ {
const int32 IX=static_cast<int32>(std::floor(X)),IY=static_cast<int32>(std::floor(Y)),IZ=static_cast<int32>(std::floor(Z)); const int32 IX=static_cast<int32>(std::floor(X)),IY=static_cast<int32>(std::floor(Y)),IZ=static_cast<int32>(std::floor(Z));
return FStandaloneResearchTunnels::NoiseValue(IX,IY,IZ,static_cast<uint32>(Owner->Seed))*DetailSupremum; return FStandaloneResearchTunnels::NoiseValue(IX,IY,IZ,static_cast<uint32>(Owner->Seed))
*DetailSupremum;
} }
const float Relief=SurfaceRelief(static_cast<float>(X), const float Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Y),static_cast<float>(Z),static_cast<uint32>(Owner->Seed)); static_cast<float>(Z),static_cast<uint32>(Owner->Seed));
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
const FLayerField* Field=FindLayerField(Z); const FLayerField* Field=FindLayerField(Z);
FOwnerNoiseLineContext OwnerNoise; FOwnerNoiseLineContext OwnerNoise;
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,*Field, float OwnerRelief=0.0f;
CaveInfluence,OwnerNoise):0.0f; if(Field)
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,PassageWeight,OwnerNoise);
const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f) const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f)
+1.85f*std::clamp(FloorInfluence,0.0f,1.0f); +1.85f*std::clamp(FloorInfluence,0.0f,1.0f);
return std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f); return std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
} }
float DetailNoiseOnLine(double X,double Y,double Z,float SurfaceSDF, float DetailNoiseOnLine(double X,double Y,double Z,float SurfaceSDF,
float CaveInfluence,float FloorInfluence, float CaveInfluence,float FloorInfluence,float PassageInfluence,
FDetailNoiseLineContext& NoiseLine, FDetailNoiseLineContext& NoiseLine,
FDetailWorkerProfile* Profile=nullptr, FDetailWorkerProfile* Profile=nullptr,
const FLayerField* KnownField=nullptr, const FLayerField* KnownField=nullptr,
@@ -3470,10 +3558,11 @@ private:
float KnownMacro=0.0f) const float KnownMacro=0.0f) const
{ {
if(DetailVariant!=EDetailVariant::Coherent) if(DetailVariant!=EDetailVariant::Coherent)
return DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence); return DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence,PassageInfluence);
using Clock=std::chrono::steady_clock; using Clock=std::chrono::steady_clock;
const auto ReliefStart=Profile?Clock::now():Clock::time_point{}; const auto ReliefStart=Profile?Clock::now():Clock::time_point{};
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),bMacroKnown,KnownMacro); const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
bMacroKnown,KnownMacro);
if(Profile) if(Profile)
Profile->ReliefSampleNanoseconds+=static_cast<uint64>( Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count()); std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
@@ -3484,8 +3573,11 @@ private:
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-LookupStart).count()); std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-LookupStart).count());
const auto OwnerStart=Profile?Clock::now():Clock::time_point{}; const auto OwnerStart=Profile?Clock::now():Clock::time_point{};
if(Profile&&Field)++Profile->OwnerRoughnessFieldCalls; if(Profile&&Field)++Profile->OwnerRoughnessFieldCalls;
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,*Field, const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
CaveInfluence,NoiseLine.OwnerNoise,Profile):0.0f; float OwnerRelief=0.0f;
if(Field)
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,PassageWeight,NoiseLine.OwnerNoise,Profile);
if(Profile) if(Profile)
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>( Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count()); std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
@@ -3498,15 +3590,18 @@ private:
{ {
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f; ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f; bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float PassageInfluence=0.0f;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF, float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir); &bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
&PassageInfluence);
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]&&Z>=Min[2]&&Z<=Max[2]; const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]&&Z>=Min[2]&&Z<=Max[2];
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f) &&std::abs(RockDistance)<=DetailSupremum+0.125f)
{ {
const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF, const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF,
CaveInfluence,FloorInfluence); CaveInfluence,FloorInfluence);
if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade; if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,PassageInfluence)*Fade;
} }
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f); if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
return RockDistance>=0.0f; return RockDistance>=0.0f;
+5
View File
@@ -2366,6 +2366,7 @@ int main(int Argc,char** Argv)
bool bStreamBenchmark=false; bool bStreamBenchmark=false;
bool bStreamPipeline=false; bool bStreamPipeline=false;
bool bProfileDetailBand=false; bool bProfileDetailBand=false;
bool bPassageDetailAuditOnly=false;
bool bShardAudit=false; bool bShardAudit=false;
bool bFarSurvivalAudit=false; bool bFarSurvivalAudit=false;
bool bFarAuditBadShell=false; bool bFarAuditBadShell=false;
@@ -2389,6 +2390,8 @@ int main(int Argc,char** Argv)
else if(Arg=="--density-hash")App.bDensityHash=true; else if(Arg=="--density-hash")App.bDensityHash=true;
else if(Arg=="--shape-world")App.bShapeWorld=true; else if(Arg=="--shape-world")App.bShapeWorld=true;
else if(Arg=="--shape-audit")App.bShapeWorld=true,App.bShapeAuditOnly=true; else if(Arg=="--shape-audit")App.bShapeWorld=true,App.bShapeAuditOnly=true;
else if(Arg=="--passage-wall-detail-audit")
{App.bShapeWorld=true;App.bShapeAuditOnly=true;bPassageDetailAuditOnly=true;}
else if(Arg=="--shape-compare")App.bShapeWorld=true,App.bShapeCompare=true; else if(Arg=="--shape-compare")App.bShapeWorld=true,App.bShapeCompare=true;
else if(Arg.starts_with("--shape-compare-level=")) else if(Arg.starts_with("--shape-compare-level="))
ShapeCompareLevel=std::atoi(std::string(Arg.substr(std::string_view("--shape-compare-level=").size())).c_str()); ShapeCompareLevel=std::atoi(std::string(Arg.substr(std::string_view("--shape-compare-level=").size())).c_str());
@@ -2496,6 +2499,8 @@ int main(int Argc,char** Argv)
{std::fprintf(stderr,"Shape interval build failed: %s\n",Error.c_str());return 2;} {std::fprintf(stderr,"Shape interval build failed: %s\n",Error.c_str());return 2;}
if(App.bShapeAuditOnly) if(App.bShapeAuditOnly)
{ {
if(bPassageDetailAuditOnly)
return App.ShapeWorld->AuditSurfaceBandFamilies(true)?0:8;
const bool bSurfacePassing=App.ShapeWorld->AuditViewSurface(App.Position.X,App.Position.Y, const bool bSurfacePassing=App.ShapeWorld->AuditViewSurface(App.Position.X,App.Position.Y,
App.Position.Z,App.Yaw,App.Pitch); App.Position.Z,App.Yaw,App.Pitch);
return App.ShapeWorld->IsDetailAuditPassing()&&bSurfacePassing?0:7; return App.ShapeWorld->IsDetailAuditPassing()&&bSurfacePassing?0:7;