research: chamber support bodies (L153, 3 designs fail; kept as patch)
Support under chambers and 9 m approaches either refilled the bore or left the same 5 Surface World route + 2 upper chamber probes unsupported in all three variants (identical counts with or without support), so those probes are likely not under the support footprint. Also exposed an FI chamber content-band validation failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
11b59a9a9e
commit
bea40502af
11 files changed
+703
No files matched your search
@@ -0,0 +1,44 @@
|
||||
| Landing | Before: L151 HEAD | After: L153 candidate 3 |
|
||||
|---|---|---|
|
||||
| passage/0-1/0 upper |  |  |
|
||||
| passage/0-1/0 lower |  |  |
|
||||
| passage/1-2/0 upper |  |  |
|
||||
| passage/1-2/0 lower |  |  |
|
||||
| passage/2-3/0 upper |  |  |
|
||||
| passage/2-3/0 lower |  |  |
|
||||
|
||||
# L153 — landing support did not pass
|
||||
|
||||
The third support shape still fails seed 2: five Surface World route probes and two upper chamber probes remain unsupported. The work stopped after three failed implementations, as required by the round instructions. L153 is not ready to promote.
|
||||
|
||||
Captures are at eye height for all six chambers. L151 and L153 camera positions match; the upper `passage/0-1/0` pitch differs by 3.10° because its planned floor tangent changed. L153 capture quality passed 6/6. The manifest and reproducible capture command are in [captures](captures/INDEX.md) and [RunL153Captures.py](RunL153Captures.py).
|
||||
|
||||
## Change
|
||||
|
||||
`WorldAssemblyEvaluatorVersion` is now 240. The candidate adds a tapered support SDF under chamber floors and the first/last 9 m of route approaches. Surface landings anchor to native ground within a 2 m depth budget; cave and shaft landings use a 0.75 m budget. The support footprint tapers over 1 m and embeds 0.20 m. Bounds were expanded for the support depth.
|
||||
|
||||
The three seed 2 attempts used the same W8 Surface World command and differed only in support composition:
|
||||
|
||||
| Attempt | Source stamp | Change | Seed 2 result |
|
||||
|---|---|---|---|
|
||||
| 1 | `4c83a0a143d44b7b1f44758408fafa8d220390825a9a15cd48b5484e22c35a0c` | Add the support body after passage composition | Chamber support passed, but 99 route capsule samples became solid; `interior_air=FAIL`. Five Surface World route probes remained unsupported. |
|
||||
| 2 | `663ab4fff5e4605a19d2a6242b1ba23fff230c143f5ae5e9d7ebc00a2b3cf5df` | Clip support by the full passage/chamber air union | Interior air passed with zero solid capsule samples, but the upper chamber had 2 unsupported probes and Surface World route support still had 5. |
|
||||
| 3 | `4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc` | Clip support by the bore field only, leaving chamber air out of the clip | Same 2 upper chamber and 5 route probes remained unsupported. This is the current working tree. |
|
||||
|
||||
Attempt 3’s final seed 2 summary is `interfaces=3 passed=2`, `strate_routes=3`, `route_walkable=2`, `route_bindings=3`, `route_binding_failures=0`, `status=FAIL`. The upper `passage/0-1/0` chamber reports `unsupported=2`; the Surface World route reports `unsupported=5`. Tunnel Network and Vertical Shafts route support pass. The passage interior capsule audit passes with 0 solid samples out of 320,355 and all 3 interior negative controls detected. Shell, naturalness (3/3), mouth profile (3/3), ripple (3/3), and blocked-route controls pass.
|
||||
|
||||
The seam audit did not emit a result: the headless audit exits with code 10 after the failed passage summary. Seam status is unverified.
|
||||
|
||||
## Checks
|
||||
|
||||
- Clang 19 build passed with `-ffp-contract=off`, 4 jobs. Final source stamp: `4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc`.
|
||||
- `git diff --check` passed.
|
||||
- Focused seed 2 audit: failed the support gate as described above. Log: `Saved/Linux/l153/seed2-focused.log`.
|
||||
- Fast tier: failed after 0.5 s on the first Floating Islands owner fixture. Its resolved-layout snapshot rejects `passage/0-1/0` because the lower landing chamber does not fit inside its host content band. Log: `Saved/Linux/l153/fast-tier.log`.
|
||||
- Capture quality: 6/6 passed. Existing L151 before images were reused; the after images were rendered from the L153 candidate.
|
||||
|
||||
The 16-seed Tunnel Network and Vertical Shafts sweeps were not run after the seed 2 gate failed and the three support attempts were exhausted. The tile seam result is absent for the same early audit exit. GCC W1/W8/W12, Clang W1/W8/W12 determinism, L80 timing across four layers, and the Linux UE start plus 30 m/s run were not run. No v240 canonical baselines were promoted because the candidate fails seed 2 and the fast tier. No Unreal `Source/` files changed.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The support body can either preserve the passage interior or provide the required support at the Surface World upper landing in this seed, but the three tested compositions did not do both. The v240 evaluator change also exposes a Floating Islands chamber content-band validation failure in the fast tier. The implementation and baselines need a new premise before this round can pass.
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture eye-height views of the six owner landing chambers for L153."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
OUT = ROOT / "Saved/Linux/l153/captures"
|
||||
LAYOUT = OUT / "seed0-resolved-layout.json"
|
||||
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux153/captures"
|
||||
HEADLESS = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l153-clang"
|
||||
RENDERER = ROOT / "Standalone/bin/linux/VoxelForgeOffscreenCapture-l153-clang"
|
||||
STAMP = "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc"
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
|
||||
|
||||
|
||||
def fmt(values) -> str:
|
||||
return ",".join(f"{value:.6f}" for value in values)
|
||||
|
||||
|
||||
def angles(camera, target) -> tuple[float, float]:
|
||||
dx, dy, dz = (target[index] - camera[index] for index in range(3))
|
||||
return math.degrees(math.atan2(dx, dy)), math.degrees(
|
||||
math.atan2(dz, math.hypot(dx, dy)))
|
||||
|
||||
|
||||
def point_q(layout: dict, values) -> tuple[float, float, float]:
|
||||
scale = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0)
|
||||
return tuple(value * scale for value in values)
|
||||
|
||||
|
||||
def chamber_views(layout: dict) -> list[dict]:
|
||||
result = []
|
||||
for passage in layout["passages"]:
|
||||
controls = passage["control_points"]
|
||||
for side, endpoint, neighbor in (
|
||||
("upper", controls[0], controls[1]),
|
||||
("lower", controls[-1], controls[-2])):
|
||||
chamber = passage[f"{side}_landing_chamber"]
|
||||
center = point_q(layout, chamber["floor_center_q"])
|
||||
a = point_q(layout, neighbor["position_q"])
|
||||
b = point_q(layout, endpoint["position_q"])
|
||||
dx, dy = a[0] - b[0], a[1] - b[1]
|
||||
length = math.hypot(dx, dy)
|
||||
if length <= 1.0e-9:
|
||||
raise ValueError(f"degenerate {side} tunnel tangent: {passage['stable_id']}")
|
||||
dx, dy = dx / length, dy / length
|
||||
eye_z = center[2] + 1.45
|
||||
distance = 9.0
|
||||
target = (center[0] + dx * distance, center[1] + dy * distance,
|
||||
eye_z + (chamber["floor_gradient_q16"][0] / 65536.0 * dx
|
||||
+ chamber["floor_gradient_q16"][1] / 65536.0 * dy)
|
||||
* distance)
|
||||
camera = (center[0], center[1], eye_z)
|
||||
slug = passage["stable_id"].replace("passage/", "").replace("/", "-")
|
||||
result.append({"name": f"{slug}-{side}-landing", "passage": passage["stable_id"],
|
||||
"side": side, "camera": camera, "target": target,
|
||||
"focus": (center[0], center[1], eye_z, 18.0)})
|
||||
return result
|
||||
|
||||
|
||||
def stamped_run(name: str, command: list[object], log: Path, env: dict[str, str]) -> str:
|
||||
probe = subprocess.run([*LIMIT, "timeout", "15s", str(command[0]), "--print-source-stamp"],
|
||||
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=20)
|
||||
match = STAMP_RE.search(probe.stdout or "")
|
||||
if probe.returncode or not match or match.group(1) != STAMP:
|
||||
raise RuntimeError(f"{name}: source stamp mismatch for {command[0]}")
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
|
||||
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1745, check=False)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
emitted = STAMP_RE.search(output)
|
||||
if result.returncode or not emitted or emitted.group(1) != STAMP:
|
||||
raise RuntimeError(f"{name}: export or render failed; see {log}")
|
||||
return output
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not DATA.is_file() or not HEADLESS.is_file() or not RENDERER.is_file():
|
||||
raise SystemExit("missing owner fixture or L153 Clang capture binaries")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
ARTIFACTS.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
|
||||
resolve = [HEADLESS, DATA, "--world-assembler-prototype", "--stream-benchmark",
|
||||
"--stream-pipeline", "--workers=12", "--clip-radius=6", "--max-clip-level=0",
|
||||
"--max-level=0", "--force-generate", "--seed=0", "--recipe=surface-world",
|
||||
"--resolved-layout-only", f"--resolved-layout-output={LAYOUT}"]
|
||||
stamped_run("l153-resolve-layout", resolve, OUT / "resolve-layout.log", env)
|
||||
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
|
||||
if len(layout.get("passages", [])) != 3:
|
||||
raise RuntimeError("owner layout did not resolve the expected three tunnels")
|
||||
|
||||
quality_tool = ROOT / "Standalone/Tools/verify/capture_quality.py"
|
||||
manifest = []
|
||||
failures = []
|
||||
for view in chamber_views(layout):
|
||||
camera, target = view["camera"], view["target"]
|
||||
yaw, pitch = angles(camera, target)
|
||||
mesh_dir = OUT / "meshes"
|
||||
mesh_dir.mkdir(parents=True, exist_ok=True)
|
||||
mesh = mesh_dir / f"{view['name']}.vfmesh"
|
||||
capture_layout = mesh_dir / f"{view['name']}-layout.json"
|
||||
export = [HEADLESS, DATA, "--world-assembler-prototype", "--stream-benchmark",
|
||||
"--stream-pipeline", "--workers=12", "--clip-radius=6", "--max-clip-level=0",
|
||||
"--max-level=0", "--force-generate", "--seed=0", "--recipe=surface-world",
|
||||
"--render-distance-chunks=0", f"--camera={fmt((*camera, yaw, pitch))}",
|
||||
f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"]
|
||||
stamped_run(f"l153-{view['name']}-export", export,
|
||||
OUT / f"{view['name']}-export.log", env)
|
||||
if not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError(f"{view['name']}: empty mesh export")
|
||||
png = OUT / f"{view['name']}.png"
|
||||
jpg = ARTIFACTS / f"after-l153-{view['name']}.jpg"
|
||||
render = [RENDERER, mesh, png, "--size=1280x720", "--fov-deg=78.0",
|
||||
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", "--height-center-m=-950",
|
||||
"--height-range-m=60", "--tile-edges=off",
|
||||
f"--focus-region={fmt(view['focus'])}"]
|
||||
stamped_run(f"l153-{view['name']}-render", render,
|
||||
OUT / f"{view['name']}-render.log", env)
|
||||
subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin",
|
||||
"-hide_banner", "-loglevel", "error", "-y", "-i", png,
|
||||
"-frames:v", "1", "-q:v", "4", jpg], cwd=ROOT, env=env,
|
||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, timeout=125)
|
||||
check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
|
||||
quality_tool, jpg], cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=125)
|
||||
quality_ok = check.returncode == 0 and "status=PASS" in check.stdout
|
||||
if not quality_ok:
|
||||
failures.append(view["name"])
|
||||
quality_line = (check.stdout.strip().splitlines()[-1]
|
||||
if check.stdout.strip() else f"quality exit={check.returncode}")
|
||||
manifest.append({"version": "after-l153-candidate3", "view": view["name"],
|
||||
"passage": view["passage"], "side": view["side"],
|
||||
"file": str(jpg.relative_to(ROOT)), "camera_m": camera,
|
||||
"target_m": target, "yaw_deg": yaw, "pitch_deg": pitch,
|
||||
"source_stamp": STAMP, "pose_layout_identity": layout.get("stable_identity"),
|
||||
"quality_status": "PASS" if quality_ok else "FAIL", "quality": quality_line})
|
||||
print(f"[L153Capture] view={view['name']} quality={'PASS' if quality_ok else 'FAIL'} "
|
||||
f"file={jpg.relative_to(ROOT)}", flush=True)
|
||||
|
||||
(ARTIFACTS / "manifest.json").write_text(
|
||||
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
with (ARTIFACTS / "INDEX.md").open("w", encoding="utf-8") as index:
|
||||
index.write("# L153 owner landing chamber captures\n\n"
|
||||
"Seed 0 eye-height poses resolved from the L153 candidate layout. The before "
|
||||
"images use the L151 HEAD state; after images show L153 candidate 3, which "
|
||||
"did not pass the seed 2 support gate.\n\n")
|
||||
for view in chamber_views(layout):
|
||||
image = f"after-l153-{view['name']}.jpg"
|
||||
before = ROOT / "Standalone/Research/WorldAssemblerM3/Linux151/captures" \
|
||||
/ f"after-l151-{view['name']}.jpg"
|
||||
index.write(f"## {view['passage']} / {view['side']} landing\n\n"
|
||||
f"- Before: [{before.name}](../../Linux151/captures/{before.name})\n"
|
||||
f"- After: [image]({image})\n\n")
|
||||
index.write(f"Capture quality failures: {len(failures)}/{len(manifest)}.\n")
|
||||
print(f"[L153CaptureSummary] chamber_views={len(manifest)} "
|
||||
f"quality_failures={len(failures)}", flush=True)
|
||||
return 0 if not failures else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[L153CaptureFailure] {error}", file=sys.stderr, flush=True)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,35 @@
|
||||
# L153 owner landing chamber captures
|
||||
|
||||
Seed 0 eye-height poses resolved from the L153 candidate layout. The before images use the L151 HEAD state; after images show L153 candidate 3, which did not pass the seed 2 support gate.
|
||||
|
||||
## passage/0-1/0 / upper landing
|
||||
|
||||
- Before: [after-l151-0-1-0-upper-landing.jpg](../../Linux151/captures/after-l151-0-1-0-upper-landing.jpg)
|
||||
- After: [image](after-l153-0-1-0-upper-landing.jpg)
|
||||
|
||||
## passage/0-1/0 / lower landing
|
||||
|
||||
- Before: [after-l151-0-1-0-lower-landing.jpg](../../Linux151/captures/after-l151-0-1-0-lower-landing.jpg)
|
||||
- After: [image](after-l153-0-1-0-lower-landing.jpg)
|
||||
|
||||
## passage/1-2/0 / upper landing
|
||||
|
||||
- Before: [after-l151-1-2-0-upper-landing.jpg](../../Linux151/captures/after-l151-1-2-0-upper-landing.jpg)
|
||||
- After: [image](after-l153-1-2-0-upper-landing.jpg)
|
||||
|
||||
## passage/1-2/0 / lower landing
|
||||
|
||||
- Before: [after-l151-1-2-0-lower-landing.jpg](../../Linux151/captures/after-l151-1-2-0-lower-landing.jpg)
|
||||
- After: [image](after-l153-1-2-0-lower-landing.jpg)
|
||||
|
||||
## passage/2-3/0 / upper landing
|
||||
|
||||
- Before: [after-l151-2-3-0-upper-landing.jpg](../../Linux151/captures/after-l151-2-3-0-upper-landing.jpg)
|
||||
- After: [image](after-l153-2-3-0-upper-landing.jpg)
|
||||
|
||||
## passage/2-3/0 / lower landing
|
||||
|
||||
- Before: [after-l151-2-3-0-lower-landing.jpg](../../Linux151/captures/after-l151-2-3-0-lower-landing.jpg)
|
||||
- After: [image](after-l153-2-3-0-lower-landing.jpg)
|
||||
|
||||
Capture quality failures: 0/6.
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,140 @@
|
||||
[
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "0-1-0-upper-landing",
|
||||
"passage": "passage/0-1/0",
|
||||
"side": "upper",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-0-1-0-upper-landing.jpg",
|
||||
"camera_m": [
|
||||
37.0,
|
||||
-5.25,
|
||||
-372.3810546875
|
||||
],
|
||||
"target_m": [
|
||||
28.40474992442715,
|
||||
-7.918646874047773,
|
||||
-371.83751160914875
|
||||
],
|
||||
"yaw_deg": -107.24848481126237,
|
||||
"pitch_deg": 3.4561048595792685,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-0-1-0-upper-landing.jpg pixels=921600 mean_luma=117.986 stddev_luma=14.908 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "0-1-0-lower-landing",
|
||||
"passage": "passage/0-1/0",
|
||||
"side": "lower",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-0-1-0-lower-landing.jpg",
|
||||
"camera_m": [
|
||||
25.8369140625,
|
||||
3.19921875,
|
||||
-736.6798828125
|
||||
],
|
||||
"target_m": [
|
||||
17.97380590951016,
|
||||
-1.1793118453464206,
|
||||
-734.970169262755
|
||||
],
|
||||
"yaw_deg": -119.11101808688004,
|
||||
"pitch_deg": 10.756207017349817,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-0-1-0-lower-landing.jpg pixels=921600 mean_luma=111.545 stddev_luma=12.291 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "1-2-0-upper-landing",
|
||||
"passage": "passage/1-2/0",
|
||||
"side": "upper",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-1-2-0-upper-landing.jpg",
|
||||
"camera_m": [
|
||||
19.166015625,
|
||||
-14.2001953125,
|
||||
-731.8810546875
|
||||
],
|
||||
"target_m": [
|
||||
10.35825176774404,
|
||||
-16.050410390488395,
|
||||
-733.216132282697
|
||||
],
|
||||
"yaw_deg": -101.86342177874246,
|
||||
"pitch_deg": -8.437834550031965,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-1-2-0-upper-landing.jpg pixels=921600 mean_luma=117.059 stddev_luma=15.604 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "1-2-0-lower-landing",
|
||||
"passage": "passage/1-2/0",
|
||||
"side": "lower",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-1-2-0-lower-landing.jpg",
|
||||
"camera_m": [
|
||||
19.166015625,
|
||||
-14.2001953125,
|
||||
-753.05390625
|
||||
],
|
||||
"target_m": [
|
||||
10.48848361510052,
|
||||
-16.587754362485967,
|
||||
-751.7193175939389
|
||||
],
|
||||
"yaw_deg": -105.38384894032598,
|
||||
"pitch_deg": 8.434788863627224,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-1-2-0-lower-landing.jpg pixels=921600 mean_luma=119.900 stddev_luma=14.906 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "2-3-0-upper-landing",
|
||||
"passage": "passage/2-3/0",
|
||||
"side": "upper",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-2-3-0-upper-landing.jpg",
|
||||
"camera_m": [
|
||||
-20.0625,
|
||||
-5.068359375,
|
||||
-812.075390625
|
||||
],
|
||||
"target_m": [
|
||||
-13.604954164712868,
|
||||
-11.337338700629157,
|
||||
-813.6662645746064
|
||||
],
|
||||
"yaw_deg": 134.15112141809087,
|
||||
"pitch_deg": -10.024269857937275,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-2-3-0-upper-landing.jpg pixels=921600 mean_luma=118.188 stddev_luma=15.874 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"version": "after-l153-candidate3",
|
||||
"view": "2-3-0-lower-landing",
|
||||
"passage": "passage/2-3/0",
|
||||
"side": "lower",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-2-3-0-lower-landing.jpg",
|
||||
"camera_m": [
|
||||
-20.0625,
|
||||
-5.068359375,
|
||||
-900.0333984375
|
||||
],
|
||||
"target_m": [
|
||||
-14.865342978484083,
|
||||
-12.416118141706198,
|
||||
-898.4427583280197
|
||||
],
|
||||
"yaw_deg": 144.72777711381306,
|
||||
"pitch_deg": 10.022826283920647,
|
||||
"source_stamp": "4599aacdabb2940f223570a9a537723727989dd5236dfc084524e6dff55bc1dc",
|
||||
"pose_layout_identity": "resolved-layout-v3-fnv64:46a5ebaca9fef170",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux153/captures/after-l153-2-3-0-lower-landing.jpg pixels=921600 mean_luma=121.648 stddev_luma=16.295 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,302 @@
|
||||
diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
index 4799b9e..e3ecf5e 100644
|
||||
--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
+++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
|
||||
@@ -512,7 +512,8 @@ namespace VoxelForgeCore
|
||||
// boundary-blended host rock from widening the authored passage wall.
|
||||
// v238 plans one resolved passage spline with explicit landing chambers.
|
||||
// v239 plans their floor planes as the passage's continuous floor profile.
|
||||
- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 239;
|
||||
+ // v240 gives landing chambers and their route approaches tapered floor support.
|
||||
+ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 240;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h
|
||||
index 6b1a6af..f0e0da4 100644
|
||||
--- a/Standalone/Core/src/ShapeWorldInternal.h
|
||||
+++ b/Standalone/Core/src/ShapeWorldInternal.h
|
||||
@@ -57,6 +57,7 @@ static constexpr double VF_PassageShellThicknessMetres=0.75;
|
||||
static constexpr double VF_PassageWallReliefMetres=0.21;
|
||||
static constexpr double VF_PassageCornerBlendMetres=0.50;
|
||||
static constexpr double VF_PassageReachMarginMetres=0.01;
|
||||
+static constexpr double VF_LandingSupportBoundsDepthMetres=2.20;
|
||||
// Radial support in metres: shell + maximum relief + the smooth-max support
|
||||
// (corner-blend width / 6) + a 1 cm guard for float and bound rounding.
|
||||
static constexpr double VF_PassageInfluenceReachMetres=
|
||||
@@ -1260,11 +1261,92 @@ public:
|
||||
constexpr double WallReliefMetres=VF_PassageWallReliefMetres;
|
||||
constexpr double OpenAirSideWallHeightMetres=1.0;
|
||||
constexpr double MouthShellBlendMetres=5.0;
|
||||
+ // Landing floors carry a tapered footing below their walk plane. Surface
|
||||
+ // landings use local native ground within a 2 m budget; cave and shaft
|
||||
+ // landings reuse the cave planner's 0.75 m floor-support depth.
|
||||
+ constexpr double LandingSupportMaximumDepthMetres=2.0;
|
||||
+ constexpr double LandingSupportFallbackDepthMetres=0.75;
|
||||
+ constexpr double LandingSupportTaperMetres=1.0;
|
||||
+ constexpr double LandingSupportGroundEmbedMetres=0.20;
|
||||
+ constexpr double LandingSupportTopBiasSubvoxels=2.0;
|
||||
const double qPerMetre=Q*1000000.0/
|
||||
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const double qx=static_cast<double>(X)*Q,qy=static_cast<double>(Y)*Q,
|
||||
qz=static_cast<double>(Z)*Q;
|
||||
const double shellThickness=FloorShellThicknessMetres*qPerMetre;
|
||||
+ const auto* queryStratum=FindBoundStratumForManagerZ(Z);
|
||||
+ double landingSupportDistanceVoxels=std::numeric_limits<double>::infinity();
|
||||
+ double landingSupportAirClipVoxels=-std::numeric_limits<double>::infinity();
|
||||
+ double routeUnionDistance=std::numeric_limits<double>::infinity();
|
||||
+ bool surfaceGroundQueried=false,surfaceGroundValid=false;
|
||||
+ double surfaceGroundVoxels=0.0;
|
||||
+ const bool surfaceLandingStratum=queryStratum&&queryStratum->Resolved
|
||||
+ &&queryStratum->Resolved->generatorKind=="SurfaceWorld";
|
||||
+ const auto surfaceGroundAtQuery=[&]()
|
||||
+ {
|
||||
+ if(!surfaceGroundQueried)
|
||||
+ {
|
||||
+ surfaceGroundQueried=true;
|
||||
+ const double layoutWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
+ if(layoutWidth>0.0)
|
||||
+ {
|
||||
+ const int32 chunkZ=FMath::FloorToInt(
|
||||
+ static_cast<float>(static_cast<double>(Z)/layoutWidth));
|
||||
+ float ground=0.0f,ceiling=0.0f;
|
||||
+ surfaceGroundValid=GetNativeSurfaceHeightsAt(
|
||||
+ X,Y,chunkZ,ground,ceiling);
|
||||
+ if(surfaceGroundValid)surfaceGroundVoxels=ground;
|
||||
+ }
|
||||
+ }
|
||||
+ return surfaceGroundValid?surfaceGroundVoxels
|
||||
+ :std::numeric_limits<double>::quiet_NaN();
|
||||
+ };
|
||||
+ const auto includeLandingSupport=[&](double floorSubvoxels,
|
||||
+ double centerDistanceSubvoxels,double footprintRadiusSubvoxels)
|
||||
+ {
|
||||
+ if(!(footprintRadiusSubvoxels>0.0))return;
|
||||
+ const double floorVoxels=floorSubvoxels/Q;
|
||||
+ const double centerDistanceVoxels=centerDistanceSubvoxels/Q;
|
||||
+ const double radiusVoxels=footprintRadiusSubvoxels/Q;
|
||||
+ const double supportDepthVoxels=(surfaceLandingStratum
|
||||
+ ?LandingSupportMaximumDepthMetres:LandingSupportFallbackDepthMetres)
|
||||
+ *qPerMetre/Q;
|
||||
+ const double taperVoxels=LandingSupportTaperMetres*qPerMetre/Q;
|
||||
+ const double embedVoxels=LandingSupportGroundEmbedMetres*qPerMetre/Q;
|
||||
+ const double topBiasVoxels=LandingSupportTopBiasSubvoxels/Q;
|
||||
+ if(centerDistanceVoxels>=radiusVoxels+taperVoxels
|
||||
+ ||static_cast<double>(Z)<floorVoxels-supportDepthVoxels-embedVoxels
|
||||
+ ||static_cast<double>(Z)>floorVoxels+topBiasVoxels)return;
|
||||
+
|
||||
+ double groundAnchorVoxels=floorVoxels-supportDepthVoxels;
|
||||
+ if(surfaceLandingStratum)
|
||||
+ {
|
||||
+ const double ground=surfaceGroundAtQuery();
|
||||
+ if(std::isfinite(ground))
|
||||
+ groundAnchorVoxels=std::max(groundAnchorVoxels,
|
||||
+ std::min(ground,floorVoxels));
|
||||
+ }
|
||||
+ const double radialFade=std::clamp(
|
||||
+ (radiusVoxels+taperVoxels-centerDistanceVoxels)/taperVoxels,
|
||||
+ 0.0,1.0);
|
||||
+ const double smoothFade=radialFade*radialFade*(3.0-2.0*radialFade);
|
||||
+ const double supportTop=groundAnchorVoxels
|
||||
+ +(floorVoxels+topBiasVoxels-groundAnchorVoxels)*smoothFade;
|
||||
+ const double supportBottom=groundAnchorVoxels-embedVoxels*smoothFade;
|
||||
+ const double supportDistance=std::max(
|
||||
+ centerDistanceVoxels-(radiusVoxels+taperVoxels),
|
||||
+ std::max(static_cast<double>(Z)-supportTop,
|
||||
+ supportBottom-static_cast<double>(Z)));
|
||||
+ landingSupportDistanceVoxels=std::min(landingSupportDistanceVoxels,
|
||||
+ supportDistance);
|
||||
+ };
|
||||
+ const auto applyLandingSupport=[&](float composed)
|
||||
+ {
|
||||
+ if(!std::isfinite(landingSupportDistanceVoxels))return composed;
|
||||
+ const double clippedSupportDistance=std::max(landingSupportDistanceVoxels,
|
||||
+ landingSupportAirClipVoxels);
|
||||
+ return std::min(composed,static_cast<float>(clippedSupportDistance));
|
||||
+ };
|
||||
struct FNearestTubeSample
|
||||
{
|
||||
double signedDistance=std::numeric_limits<double>::infinity();
|
||||
@@ -1284,7 +1366,6 @@ public:
|
||||
std::numeric_limits<double>::infinity();
|
||||
double plannedTunnelPassageShellDistance=std::numeric_limits<double>::infinity();
|
||||
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
|
||||
- const auto* queryStratum=FindBoundStratumForManagerZ(Z);
|
||||
const bool plannedTunnelStratum=queryStratum&&queryStratum->Resolved
|
||||
&&(queryStratum->Resolved->generatorKind=="TunnelNetwork"
|
||||
||queryStratum->Resolved->generatorKind=="VerticalShafts")
|
||||
@@ -1346,6 +1427,9 @@ public:
|
||||
const double radialDistance=DeterministicSqrt(
|
||||
(qx-centerX)*(qx-centerX)+(qy-centerY)*(qy-centerY))
|
||||
-landingChamber->radiusSubvoxels;
|
||||
+ includeLandingSupport(floorCenterZ,
|
||||
+ radialDistance+landingChamber->radiusSubvoxels,
|
||||
+ landingChamber->radiusSubvoxels);
|
||||
const double floorDistance=floorCenterZ-qz;
|
||||
const double ceilingDistance=qz-(floorCenterZ
|
||||
+landingChamber->clearHeightSubvoxels);
|
||||
@@ -1578,6 +1662,42 @@ public:
|
||||
visitShellSegment);
|
||||
}
|
||||
if(!nearest.valid)continue;
|
||||
+ if(queryStratum&&queryStratum->Resolved
|
||||
+ &&(queryStratum->StrateIndex==passage.upperStrateIndex
|
||||
+ ||queryStratum->StrateIndex==passage.lowerStrateIndex)
|
||||
+ &&nearest.floorSegmentIndex<PassageSegmentData[passageIndex].size())
|
||||
+ {
|
||||
+ constexpr double LandingApproachLengthMetres=9.0;
|
||||
+ const auto& floorSegment=
|
||||
+ PassageSegmentData[passageIndex][nearest.floorSegmentIndex];
|
||||
+ const double floorAlongMetres=floorSegment.authoredAlongMetres
|
||||
+ +nearest.floorT*floorSegment.authoredLengthMetres;
|
||||
+ const bool upperApproach=queryStratum->StrateIndex
|
||||
+ ==passage.upperStrateIndex
|
||||
+ &&floorAlongMetres>=-LandingSupportTaperMetres
|
||||
+ &&floorAlongMetres<=LandingApproachLengthMetres;
|
||||
+ const bool lowerApproach=queryStratum->StrateIndex
|
||||
+ ==passage.lowerStrateIndex
|
||||
+ &&constants.totalPathLengthMetres-floorAlongMetres
|
||||
+ <=LandingApproachLengthMetres
|
||||
+ &&constants.totalPathLengthMetres-floorAlongMetres
|
||||
+ >=-LandingSupportTaperMetres;
|
||||
+ if(floorSegment.routeSegment&&(upperApproach||lowerApproach))
|
||||
+ {
|
||||
+ const double routeX=floorSegment.a[0]
|
||||
+ +nearest.floorT*floorSegment.delta[0];
|
||||
+ const double routeY=floorSegment.a[1]
|
||||
+ +nearest.floorT*floorSegment.delta[1];
|
||||
+ const double routeDistance=DeterministicSqrt(
|
||||
+ (qx-routeX)*(qx-routeX)+(qy-routeY)*(qy-routeY));
|
||||
+ const double floorRadius=floorSegment.radiusA
|
||||
+ +std::clamp(nearest.floorT,0.0,1.0)*floorSegment.radiusDelta;
|
||||
+ const double supportRadius=std::max(0.0,
|
||||
+ floorRadius-0.23*qPerMetre);
|
||||
+ includeLandingSupport(nearest.floorCenterZ-nearest.floorDepth,
|
||||
+ routeDistance,supportRadius);
|
||||
+ }
|
||||
+ }
|
||||
const double wallElevationMetres=(qz-(nearest.floorCenterZ
|
||||
-nearest.floorDepth))/qPerMetre;
|
||||
double distanceToCenter=std::max(1.0e-9,
|
||||
@@ -1648,6 +1768,8 @@ public:
|
||||
static_cast<double>(shapedDistance)-floorShellThickness);
|
||||
nearest.floorShellSignedDistance=std::min(
|
||||
nearest.floorShellSignedDistance,floorShellDistance);
|
||||
+ routeUnionDistance=std::min(routeUnionDistance,
|
||||
+ static_cast<double>(shapedDistance));
|
||||
unionDistance=std::min(unionDistance,static_cast<double>(shapedDistance));
|
||||
unionFloorShellDistance=std::min(unionFloorShellDistance,
|
||||
nearest.floorShellSignedDistance);
|
||||
@@ -1657,13 +1779,18 @@ public:
|
||||
std::min(localShellDistance,nearest.shellSignedDistance));
|
||||
}
|
||||
|
||||
- if(!std::isfinite(unionDistance))return density;
|
||||
+ if(!std::isfinite(unionDistance))return applyLandingSupport(density);
|
||||
const float passageAirDistance=static_cast<float>(-unionDistance/Q);
|
||||
+ // Keep support out of the passage bore while allowing it under the
|
||||
+ // chamber's own floor, where the chamber volume is naturally open.
|
||||
+ if(std::isfinite(routeUnionDistance))
|
||||
+ landingSupportAirClipVoxels=-routeUnionDistance/Q;
|
||||
// Keep the landing floor shell where it is exposed to the chamber. At
|
||||
// the tunnel joint, the passage void must be able to open through that
|
||||
// floor shell so its own continuous floor and shell define the throat.
|
||||
if(unionLandingChamberFloorShellDistance<0.0&&unionDistance>0.0)
|
||||
- return static_cast<float>(unionLandingChamberFloorShellDistance/Q);
|
||||
+ return applyLandingSupport(
|
||||
+ static_cast<float>(unionLandingChamberFloorShellDistance/Q));
|
||||
const auto plannedPortalWalkCorridorContains=[&]()
|
||||
{
|
||||
if(!queryStratum||!queryStratum->Resolved
|
||||
@@ -1855,7 +1982,7 @@ public:
|
||||
// otherwise the neighboring field can widen its wall by a whole
|
||||
// shell thickness. Native graph air still wins above via the
|
||||
// positive nativeTunnelFieldDensity path.
|
||||
- return passageAirDistance;
|
||||
+ return applyLandingSupport(passageAirDistance);
|
||||
}
|
||||
// Air is the union of the native graph and passage voids. Keeping the
|
||||
// signed passage distance outside the void as well gives marching cubes
|
||||
@@ -1865,9 +1992,10 @@ public:
|
||||
&&plannedTunnelPassageShellDistance<0.0&&unionDistance>0.0)
|
||||
{
|
||||
if(nativeTunnelFieldDensity>=0.0f&&plannedPortalWalkCorridorContains())
|
||||
- return std::max(nativeTunnelFieldDensity,passageAirDistance);
|
||||
- return std::min(density,
|
||||
- static_cast<float>(plannedTunnelPassageShellDistance/Q));
|
||||
+ return applyLandingSupport(
|
||||
+ std::max(nativeTunnelFieldDensity,passageAirDistance));
|
||||
+ return applyLandingSupport(std::min(density,
|
||||
+ static_cast<float>(plannedTunnelPassageShellDistance/Q)));
|
||||
}
|
||||
if(nativeTunnelFieldDensity>=0.0f)
|
||||
{
|
||||
@@ -1875,29 +2003,30 @@ public:
|
||||
// cave voids after the explicit mouth fade has ended. Keep this scoped
|
||||
// to the planner-crossing passage shell so nearby portal joints stay open.
|
||||
if(plannedTunnelPassageShellDistance<0.0&&unionDistance>0.0)
|
||||
- return std::min(nativeTunnelFieldDensity,
|
||||
- static_cast<float>(plannedTunnelPassageShellDistance/Q));
|
||||
+ return applyLandingSupport(std::min(nativeTunnelFieldDensity,
|
||||
+ static_cast<float>(plannedTunnelPassageShellDistance/Q)));
|
||||
constexpr float NativeCaveAirMarginVoxels=0.75f;
|
||||
if(unionDistance>0.0
|
||||
&&nativeTunnelFieldDensity<NativeCaveAirMarginVoxels
|
||||
&&unionFloorShellDistance<0.0)
|
||||
- return std::min(nativeTunnelFieldDensity,
|
||||
- static_cast<float>(unionFloorShellDistance/Q));
|
||||
- return std::max(nativeTunnelFieldDensity,passageAirDistance);
|
||||
+ return applyLandingSupport(std::min(nativeTunnelFieldDensity,
|
||||
+ static_cast<float>(unionFloorShellDistance/Q)));
|
||||
+ return applyLandingSupport(
|
||||
+ std::max(nativeTunnelFieldDensity,passageAirDistance));
|
||||
}
|
||||
if(nativeFloorSupport!=std::numeric_limits<float>::lowest()
|
||||
&&nativeFloorSupport<0.0f)
|
||||
- return std::min(nativeFloorSupport,unionDistance<=0.0
|
||||
- ?passageAirDistance:density);
|
||||
+ return applyLandingSupport(std::min(nativeFloorSupport,unionDistance<=0.0
|
||||
+ ?passageAirDistance:density));
|
||||
if(nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest())
|
||||
- return std::max(density,passageAirDistance);
|
||||
+ return applyLandingSupport(std::max(density,passageAirDistance));
|
||||
if(unionDistance>0.0&&unionShellDistance<0.0&&density>=0.0f)
|
||||
{
|
||||
if(connectedSurfaceLandingShellContains())
|
||||
- return std::max(density,passageAirDistance);
|
||||
- return static_cast<float>(unionShellDistance/Q);
|
||||
+ return applyLandingSupport(std::max(density,passageAirDistance));
|
||||
+ return applyLandingSupport(static_cast<float>(unionShellDistance/Q));
|
||||
}
|
||||
- return std::max(density,passageAirDistance);
|
||||
+ return applyLandingSupport(std::max(density,passageAirDistance));
|
||||
}
|
||||
|
||||
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
|
||||
@@ -2044,8 +2173,11 @@ public:
|
||||
static_cast<double>(chamber.floorGradientQ16[0])/65536.0,
|
||||
static_cast<double>(chamber.floorGradientQ16[1])/65536.0);
|
||||
const double extent=reachCoreVoxels+0.01;
|
||||
+ const double supportDepth=(VF_LandingSupportBoundsDepthMetres*1000000.0/
|
||||
+ std::max<std::uint32_t>(1,
|
||||
+ ResolvedLayout->coreVoxelSizeMicrometres));
|
||||
const double low[3]={cx-radius-extent,cy-radius-extent,
|
||||
- cz-floorExtent-extent};
|
||||
+ cz-floorExtent-supportDepth-extent};
|
||||
const double high[3]={cx+radius+extent,cy+radius+extent,
|
||||
cz+floorExtent+static_cast<double>(
|
||||
chamber.clearHeightSubvoxels)/Q+extent};
|
||||
@@ -5484,8 +5616,12 @@ private:
|
||||
const double height=static_cast<double>(
|
||||
chamber.clearHeightSubvoxels)/Q;
|
||||
const double extent=requiredReachCoreVoxels+0.01;
|
||||
+ const double supportDepth=(VF_LandingSupportBoundsDepthMetres*1000000.0/
|
||||
+ std::max<std::uint32_t>(1,
|
||||
+ ResolvedLayout->coreVoxelSizeMicrometres));
|
||||
const std::array<double,3> minimum{
|
||||
- cx-radius-extent,cy-radius-extent,cz-floorExtent-extent};
|
||||
+ cx-radius-extent,cy-radius-extent,
|
||||
+ cz-floorExtent-supportDepth-extent};
|
||||
const std::array<double,3> maximum{
|
||||
cx+radius+extent,cy+radius+extent,
|
||||
cz+floorExtent+height+extent};
|
||||
Reference in new issue
Block a user