feat(client): cutaway modes — corridor / bottom-walls-low / both, C-key live toggle (T-1088)
Live-session two-minds resolution: the trippy sightline corridor stays as a mode (candidate signature look); mode 1 drops any wall with a KNOWN non-wall tile on its camera-far side to the stub (X2 bottom-walls read — the player sees the room without walking to it); mode 2 combines. Bottom-wall flag rides MultiMesh INSTANCE_CUSTOM.r, updated on paint and when newly-learned floors promote camera-ward walls; camera-far directions derived from the live WorldRoot basis, never hand-derived. Default stays corridor pending the feel verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -781,3 +781,18 @@ Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the creat
|
||||
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
|
||||
|
||||
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.', NULL, '2026-07-06 12:59:45', '2026-07-06 12:59:45.213', '2026-07-06 12:59:45.213', NULL, 'd416d57aec826414cd29d739238749cf', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FKCER95EK266H8RWPW8XT1F0', 'description', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
|
||||
|
||||
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
|
||||
|
||||
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
|
||||
|
||||
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
|
||||
|
||||
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
|
||||
|
||||
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
|
||||
|
||||
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
|
||||
|
||||
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).', NULL, '2026-07-06 17:12:09', '2026-07-06 17:12:09.008', '2026-07-06 17:12:09.008', NULL, 'c973d0c7b4460d39520d95a4693a87d0', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -674,3 +674,12 @@ Scope guard (user concern, 2026-07-06): Synty intake must NOT displace the creat
|
||||
ROUTE DECISION (user, 2026-07-06, final): Synty intake REJECTED despite the spike''s technical PASS — (1) USD199.99/pack is too expensive; (2) Sidekick modularity is baked body-segment swap (clothes fused to body chunks), incompatible with our layered clothing-over-body system (11 bodies, skin tones, slot swapping); (3) user dislikes the Synty style. The spike still paid: transplant/batch scripts are source-agnostic and become the G1-family production pipeline; T-1090 (5 bodies broken bare) discovered; the Synty question is closed with evidence, not parked. OPERATIVE ROUTE: (b) derive from owned Fantasy parts + (c) hand-author — with a new preferred authoring technique: OFFSET-SHELL FROM OUR OWN BODY MESHES (extract body segment surface, offset outward into garment shell, cut neckline/hem/sleeves; weights inherited by construction, style fully ours, USD0). Suit + uniform remain the full hand-author proof. Purchases still open (separate concerns): UAL1/UAL2 Source ~USD30 for animation clips; Fantasy Outfits Source USD20 low-priority convenience.
|
||||
|
||||
Chromakey garment-QA harness DELIVERED (2026-07-06, commits eaca6c8c4/74fa16260/b09a50efb): two-pass depth-proximity clip detection — pass A keys covered body segments magenta, pass B re-renders garment-only with a depth-only 3cm epsilon bias toward camera; analyzer splits key pixels into exposed_skin (informational — sleeveless arms, collars) vs clip_through (gating — skin within 3cm in front of cloth). Run: tooling/garment-qa/run-garment-qa <config>. Verified on the peasant set (avg_m/f x Walk/Sprint/Crouch_Fwd x 3 frames x 4 yaws): 0/18 front-view false positives; genuine findings = sleeveless armhole seam (avg_f, sides), deep-crouch waist gap (worst 150px, back), collar nape. First real fit observations already on record for the peasant set. Sensitivity: clip_epsilon_m + --min-pixels, to calibrate against the first modern garments. This is the acceptance gate for every garment the wardrobe pipeline produces; pair with the T-1088 sandbox for in-world walk QA.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:34:18.656', '2026-07-06 12:59:45.213', NULL, 'ffdb04fb1abf542f041161529a4a2f89', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FKCER95EK266H8RWPW8XT1F0', 'story', '06FB0TNSRXX29H5X0EB0F0GBW4', '3D character locomotion sidequest — composited character walks the Gauntlet on the live server', 'Fable-5-window sidequest (sanctioned cascade exception, 2026-07-05): the presentation layer that turns discrete server tile steps into a believable walking 3D character. Scope: 3D sandbox dev scene (SR_LIVE=1, greybox tiles derived from server snapshots, no hand-authored map), CharacterVisual instantiated in-world, locomotion rig (tile-step + facing-octant interpolation, stance-aware speeds), UAL animation state machine (idle/walk/sprint/careful/crouch), D-148 camera rig (30deg low-angle follow, 45deg rotation). Server-authoritative from day one — no client-local movement model. Builds on D-148/D-149/D-244/D-066 and the T-417 stance system. If the Fable 5 window closes mid-flight, work parks here.
|
||||
|
||||
Visual reference (user, 2026-07-06): Xenonauts 2 nails the target tiles/occlusion/character perspective — readable tile grid, low-angle tactical camera with angle options (matches D-148 30deg low-angle + 45deg rotation steps and the spikes/3dpipeline angle presets), and crucially its wall/roof occlusion treatment: camera-facing walls cut down/ghosted so interiors stay readable, floors peel per z-level. One deliberate difference: our camera locks to the player character (D-015/T-116) — no free map panning. Implication for the locomotion sandbox: greybox walls should prototype camera-facing cutaway occlusion early, since wall readability interacts with the locked low-angle camera.
|
||||
|
||||
Scoping call (user + lead agreement, 2026-07-06): wall cutaway is CLIENT-SIDE presentation, decoupled from fog-of-perception. Rationale: two occlusion systems with different subjects — perception/fog governs what the CHARACTER knows (server-enforced info boundary; client only receives observer-filtered geometry), cutaway governs what the PLAYER''S CAMERA sees (pure render mode on wall data the client already holds). They compose: cutaway cuts render height; fog overlay still gates what is visible behind. Walls remain first-class client data objects (future destruction/explosion interactions), cutaway is a render mode on them like tint/LOD. Nuance parked for Phase 5 proper: destroyed-while-unobserved walls should render in remembered state (fog-memory applied to geometry). If this holds through implementation, mint a D-record (perception domain) when the sidequest lands.
|
||||
|
||||
Animation supply resolved (2026-07-06): user purchased UAL1[Source+Pro] (120 clips, identical lists) + UAL2[Source] (134 clips), unzipped in docs/assets/downloads/. Gains: Turn90_L/R + Turn180_L/R (turn-in-place), Sprint_Enter/Exit + Crouch_Enter/Exit (stance transitions), full 8-direction Walk/Jog/Crouch sets incl. diagonals, Counter_Idle + talking/phone/sitting idles (life-sim verbs), plus _RM root-motion variants (unused — in-place is correct for server-authoritative interpolation). Still absent everywhere: walk start/stop + stairs — the only remaining custom clips. Integration is NOT a drop-in swap: upstream renamed clips (Walk_Loop -> Walk_Fwd_Loop etc.), so wire via the design''s recorded plan — explicit library names + lib/Clip addressing + gait-table update. NOTE for facing design: the 8-dir walk sets unlock Q-084''s parked walk-vs-aim split (direction-matched clips relative to facing) — revisit during the S9 tuning session.
|
||||
|
||||
Live-session feedback (user, 2026-07-06): (1) Corridor cutaway verdict — two minds: ''extremely trippy, could be the game''s own look'' vs ''player cannot see what is in the room unless close''. Resolution: cutaway MODES, toggled live in the sandbox (corridor / bottom-walls-low / both) — bottom-wall rule is data-driven (wall drops to stub when a KNOWN floor tile sits on its camera-far side; camera-facing directions derived from WorldRoot basis at runtime, not hand-derived). Default picked by feel during the session; the trippy corridor stays available as a signature-look option. (2) NEW: mouse-over move-here marker on the hovered tile + optimal path line — client-side A* over the never-evict KNOWN tile store (info-boundary-honest: the character plans only through known tiles; fog is unpathable), 8-dir with diagonal step cost 1 (time-optimal per the no-sqrt2 wire), terrain-cost provider seam (uniform today, Phase-4 terrain later), click executes by streaming ordinary Move* step commands through the existing throttle (zero protocol change, server validates every step), cancelled by WASD override / path invalidation / arrival. Settles more of Q-084 (click-to-move half).', 'backlog', 'high', NULL, 'client', NULL, '2026-07-06 07:01:06.475', '2026-07-06 17:12:09.007', NULL, '4286ff0868e7ab0aaafbaf2d183a6975', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
|
||||
@@ -155,6 +155,11 @@ var _free_slots: Array = [[], []]
|
||||
var _state_colors: Array[Color] = []
|
||||
var _last_tick: int = -1
|
||||
var _capacity_warned := false
|
||||
## 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
|
||||
## _compute_behind_dirs — derived from the live WorldRoot basis).
|
||||
var _behind_dirs: Array[Vector3i] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -190,7 +195,12 @@ func _ready() -> void:
|
||||
_wall_material.set_shader_parameter("u_cut_height", SandboxConstants.CUT_HEIGHT)
|
||||
_wall_material.set_shader_parameter("u_corridor_half_w", SandboxConstants.CUT_CORRIDOR_HALF_W)
|
||||
_wall_material.set_shader_parameter("u_cut_band", SandboxConstants.CUT_BAND)
|
||||
_wall_mm = _make_layer("WallMM", wall_mesh, _wall_material)
|
||||
_wall_mm = _make_layer("WallMM", wall_mesh, _wall_material, true)
|
||||
# Camera-far neighbor directions for the bottom-wall rule — derived from the
|
||||
# actual WorldRoot basis at runtime (never hand-derived; the E/W-mirror
|
||||
# lesson). A wall is a "bottom wall" when a KNOWN non-wall tile sits on its
|
||||
# camera-far side. Deferred: global_transform is not final until tree-ready.
|
||||
_compute_behind_dirs.call_deferred()
|
||||
|
||||
|
||||
## Push the world->sim transform into the floor grid shader (deferred from
|
||||
@@ -227,13 +237,17 @@ func set_cutaway_char_pos(world_pos: Vector3, reach_m: float) -> void:
|
||||
_wall_material.set_shader_parameter("u_cut_reach", reach_m)
|
||||
|
||||
|
||||
func _make_layer(layer_name: String, mesh: Mesh, material: ShaderMaterial) -> MultiMeshInstance3D:
|
||||
func _make_layer(
|
||||
layer_name: String, mesh: Mesh, material: ShaderMaterial, with_custom := false
|
||||
) -> MultiMeshInstance3D:
|
||||
var mm := MultiMesh.new()
|
||||
# Format flags must be set before instance_count (buffer layout is fixed at
|
||||
# allocation). use_colors routes set_instance_color() to the shaders' COLOR
|
||||
# built-in, which multiplies albedo (§3.2).
|
||||
# built-in, which multiplies albedo (§3.2). use_custom_data (walls only)
|
||||
# carries the bottom-wall flag to INSTANCE_CUSTOM.r for the cutaway modes.
|
||||
mm.transform_format = MultiMesh.TRANSFORM_3D
|
||||
mm.use_colors = true
|
||||
mm.use_custom_data = with_custom
|
||||
mm.mesh = mesh
|
||||
mm.instance_count = INSTANCE_CAPACITY
|
||||
for i in INSTANCE_CAPACITY:
|
||||
@@ -267,6 +281,63 @@ func _paint_new(key: Vector3i) -> void:
|
||||
mm.set_instance_transform(slot, _tile_xform(key, kind))
|
||||
mm.set_instance_color(slot, _state_colors[store.state_of(key)])
|
||||
_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)
|
||||
|
||||
|
||||
## Cutaway mode (design amendment, live session 2026-07-06): 0 = sightline
|
||||
## corridor, 1 = bottom walls low, 2 = both. Pushed to the wall shader.
|
||||
func set_cutaway_mode(mode: int) -> void:
|
||||
cutaway_mode = mode % 3
|
||||
_wall_material.set_shader_parameter("u_cutaway_mode", cutaway_mode)
|
||||
print("GreyboxWorld: cutaway mode %d (%s)" % [
|
||||
cutaway_mode, ["sightline corridor", "bottom walls low", "both"][cutaway_mode]
|
||||
])
|
||||
|
||||
|
||||
# Dev affordance (same pattern as the camera's T tilt cycle): C cycles the
|
||||
# cutaway mode live so the corridor-vs-bottom-walls call is made by feel.
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
var key := event as InputEventKey
|
||||
if key.pressed and not key.echo and key.physical_keycode == KEY_C:
|
||||
set_cutaway_mode(cutaway_mode + 1)
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
## World-space camera-far sim directions (as tile-key deltas): the wall face
|
||||
## pointing toward the camera hides the tile at key + behind_dir. Computed from
|
||||
## this node's global basis so the WorldRoot rotation can never desync it.
|
||||
func _compute_behind_dirs() -> void:
|
||||
_behind_dirs.clear()
|
||||
for d: Vector3i in [Vector3i(1, 0, 0), Vector3i(-1, 0, 0), Vector3i(0, 1, 0), Vector3i(0, -1, 0)]:
|
||||
# Sim x -> local X, sim y -> local Z (SandboxSpace convention).
|
||||
var world_dir := global_transform.basis * Vector3(float(d.x), 0.0, float(d.y))
|
||||
# Camera looks world -Z, so a camera-ward face points +Z; the tile it
|
||||
# hides lies OPPOSITE that face -> behind_dir = -d for camera-ward d.
|
||||
if world_dir.z > 0.01:
|
||||
_behind_dirs.append(-d)
|
||||
# Repaint flags for walls painted before the deferred computation ran.
|
||||
for key: Vector3i in _slots:
|
||||
if (_slots[key] as Vector2i).x == Store.Kind.WALL:
|
||||
_refresh_bottom_flag(key)
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
break
|
||||
_wall_mm.multimesh.set_instance_custom_data(slot.y, Color(flag, 0.0, 0.0, 0.0))
|
||||
|
||||
|
||||
func _repaint_color(key: Vector3i) -> void:
|
||||
|
||||
@@ -33,13 +33,20 @@ uniform float u_cut_height = 0.75; // SandboxConstants.CUT_HEIGHT — the
|
||||
uniform float u_corridor_half_w = 1.4; // SandboxConstants.CUT_CORRIDOR_HALF_W
|
||||
uniform float u_cut_band = 0.75; // SandboxConstants.CUT_BAND — falloff band
|
||||
uniform float u_cut_reach = 4.4; // per-frame: WALL_H / tan(camera pitch)
|
||||
// 0 = sightline corridor, 1 = bottom walls low, 2 = both (live-toggled, C key).
|
||||
uniform int u_cutaway_mode = 0;
|
||||
uniform vec3 u_base_albedo : source_color = vec3(0.66, 0.64, 0.60);
|
||||
|
||||
varying vec3 v_world_pos;
|
||||
// Bottom-wall flag (INSTANCE_CUSTOM.r, set by GreyboxWorld): 1.0 when a KNOWN
|
||||
// non-wall tile sits on this wall's camera-far side — the wall would occlude
|
||||
// interior the player should see, X2-style.
|
||||
varying flat float v_bottom;
|
||||
|
||||
void vertex() {
|
||||
// MODEL_MATRIX folds in the per-instance MultiMesh transform.
|
||||
v_world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
v_bottom = INSTANCE_CUSTOM.r;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
@@ -50,7 +57,14 @@ void fragment() {
|
||||
float near_f = smoothstep(-0.25, 0.75, to_wall.y);
|
||||
// Far gate: corridor ends where a WALL_H wall can no longer occlude.
|
||||
float far_f = 1.0 - smoothstep(u_cut_reach, u_cut_reach + u_cut_band, to_wall.y);
|
||||
float allowed = mix(u_wall_h, u_cut_height, lateral * near_f * far_f);
|
||||
float corridor_f = lateral * near_f * far_f;
|
||||
float cut_f = corridor_f;
|
||||
if (u_cutaway_mode == 1) {
|
||||
cut_f = v_bottom;
|
||||
} else if (u_cutaway_mode == 2) {
|
||||
cut_f = max(corridor_f, v_bottom);
|
||||
}
|
||||
float allowed = mix(u_wall_h, u_cut_height, cut_f);
|
||||
if (v_world_pos.y > allowed) {
|
||||
discard;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user