fix(client): bottom-wall scan pierces 2-thick walls + corner diagonal; whole-box quantized cuts (T-1088)

Live-session findings, three in one: (1) Gauntlet walls are 2 tiles thick —
the outer row's camera-far neighbor is the inner WALL, so its flag stayed 0
and it stood full-height behind the collapsed inner stub; the flag now scans
up to 3 tiles through consecutive KNOWN walls (unknown stops the scan — no
assumptions past the info boundary), and any newly-learned tile refreshes the
wall chain behind it. (2) Corner pillars at the screen-bottom junction never
collapsed — their interior floor sits DIAGONALLY behind, which no straight
scan line reaches; the combined diagonal joins the scan set. (3) Per-fragment
smoothstep falloff carved organic notches (the 'sphere' read) — the cut now
computes per-instance in the vertex shader from the box's tile center and
quantizes at 0.5: every wall box is either full or stub, X2-crisp.

Verified via locomotion_cutaway autopilot capture: south runs read as clean
stub rows, corners collapse, camera-far walls stand.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 20:10:28 +02:00
co-authored by Claude Fable 5
parent ae04091f93
commit 6372537f2f
2 changed files with 47 additions and 21 deletions
+28 -7
View File
@@ -155,6 +155,10 @@ var _free_slots: Array = [[], []]
var _state_colors: Array[Color] = []
var _last_tick: int = -1
var _capacity_warned := false
## Bottom-wall scan depth (tiles) along each camera-far direction — covers the
## Gauntlet's 2-thick walls plus door trim.
const BOTTOM_SCAN_DEPTH := 3
## Cutaway mode: 0 = sightline corridor, 1 = bottom walls low, 2 = both.
var cutaway_mode: int = 0
## Camera-far sim-neighbor deltas for the bottom-wall rule (see
@@ -284,10 +288,13 @@ func _paint_new(key: Vector3i) -> void:
_slots[key] = Vector2i(kind, slot)
if kind == Store.Kind.WALL:
_refresh_bottom_flag(key)
else:
# A newly-learned floor can promote camera-ward walls to bottom walls.
for d in _behind_dirs:
_refresh_bottom_flag(key - d)
# Any newly-learned tile can promote camera-ward walls up the scan chain:
# a floor is the promotion itself; a wall can EXTEND a chain that previously
# stopped at unknown (Gauntlet walls are 2 tiles thick — the outer row sees
# floor only THROUGH the inner row).
for d in _behind_dirs:
for step in range(1, BOTTOM_SCAN_DEPTH + 1):
_refresh_bottom_flag(key - d * step)
## Cutaway mode (design amendment, live session 2026-07-06): 0 = sightline
@@ -322,6 +329,11 @@ func _compute_behind_dirs() -> void:
# hides lies OPPOSITE that face -> behind_dir = -d for camera-ward d.
if world_dir.z > 0.01:
_behind_dirs.append(-d)
# Corner pillars: at a wall-run junction the interior floor sits DIAGONALLY
# behind (neither straight scan line reaches it) — include the combined
# diagonal in the scan set.
if _behind_dirs.size() == 2:
_behind_dirs.append(_behind_dirs[0] + _behind_dirs[1])
# Repaint flags for walls painted before the deferred computation ran.
for key: Vector3i in _slots:
if (_slots[key] as Vector2i).x == Store.Kind.WALL:
@@ -332,11 +344,20 @@ func _refresh_bottom_flag(key: Vector3i) -> void:
var slot: Vector2i = _slots.get(key, Vector2i(-1, -1))
if slot.y < 0 or slot.x != Store.Kind.WALL:
return
# Scan up to BOTTOM_SCAN_DEPTH tiles along each camera-far direction,
# walking THROUGH consecutive known walls (Gauntlet walls are 2 thick):
# a known non-wall tile behind the wall run marks the whole run "bottom".
# Unknown tiles stop the scan — no assumptions past the info boundary.
var flag := 0.0
for d in _behind_dirs:
var n: Vector3i = key + d
if store.has_tile(n) and store.kind_of(n) != Store.Kind.WALL:
flag = 1.0
for step in range(1, BOTTOM_SCAN_DEPTH + 1):
var n: Vector3i = key + d * step
if not store.has_tile(n):
break
if store.kind_of(n) != Store.Kind.WALL:
flag = 1.0
break
if flag > 0.0:
break
_wall_mm.multimesh.set_instance_custom_data(slot.y, Color(flag, 0.0, 0.0, 0.0))