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>
79 lines
4.1 KiB
Plaintext
79 lines
4.1 KiB
Plaintext
shader_type spatial;
|
|
render_mode diffuse_lambert, specular_disabled;
|
|
|
|
// T-1088 greybox wall + camera-side cutaway (design §8) — technique
|
|
// proto-production.
|
|
//
|
|
// COLOR-multiplied albedo (same four-state tint contract as the floor shader)
|
|
// plus a smoothstep height cut along the CAMERA->CHARACTER SIGHTLINE CORRIDOR:
|
|
// walls inside the corridor drop to a CUT_HEIGHT stub — the Xenonauts read,
|
|
// aligned to what actually occludes the character (the earlier proximity dome
|
|
// bit side walls while leaving the occluding wall standing). The cutaway cuts
|
|
// RENDER HEIGHT; the tint gates KNOWLEDGE — independent factors, so remembered
|
|
// walls can be cut too. Open box tops are acceptable greybox fidelity; ghost
|
|
// band / dither / capping are named polish on this same shader.
|
|
//
|
|
// Corridor geometry (world space — the camera has yaw 0 and looks world -Z;
|
|
// the diamond view is WorldRoot's 45 deg rotation, never the camera's, D-148):
|
|
// the ground projection of the camera->character sightline is the +Z half-line
|
|
// at the character's x. A wall occludes the character only within
|
|
// |to_wall.x| < corridor half-width AND 0 < to_wall.y < reach, where reach is
|
|
// pitch-derived (a WALL_H wall can occlude someone at most WALL_H / tan(pitch)
|
|
// away in ortho): ~4.3 m at the -30 deg gameplay preset, long at -5 frontal,
|
|
// near-zero at -80 overhead. u_char_pos_xz + u_cut_reach are pushed per frame
|
|
// (GreyboxWorld.set_cutaway_char_pos) so the corridor glides with the character
|
|
// and breathes with the tilt. Parked far away by default: no cut until pushed.
|
|
//
|
|
// Static uniforms default to the SandboxConstants values; GreyboxWorld pushes
|
|
// them at material build so SandboxConstants stays the single source (§12).
|
|
|
|
uniform vec2 u_char_pos_xz = vec2(1000000.0, 1000000.0);
|
|
uniform float u_wall_h = 2.5; // SandboxConstants.WALL_H
|
|
uniform float u_cut_height = 0.75; // SandboxConstants.CUT_HEIGHT — the stub
|
|
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;
|
|
// Whole-box cut decision, computed per-INSTANCE in vertex() from the box's tile
|
|
// center — every wall box is either full height or the stub, no per-fragment
|
|
// falloff curves carving organic notches (the "sphere" read). X2-crisp tile
|
|
// cuts; the smoothstep bands only shape where the 0.5 threshold falls.
|
|
varying flat float v_cut;
|
|
|
|
void vertex() {
|
|
// MODEL_MATRIX folds in the per-instance MultiMesh transform; its origin
|
|
// is the box center — .xz is the wall tile's center in world space.
|
|
vec2 inst_xz = (MODEL_MATRIX * vec4(0.0, 0.0, 0.0, 1.0)).xz;
|
|
vec2 to_wall = inst_xz - u_char_pos_xz;
|
|
// Lateral gate: full cut inside (half_w - band), fading to none at half_w.
|
|
float lateral = 1.0 - smoothstep(u_corridor_half_w - u_cut_band, u_corridor_half_w, abs(to_wall.x));
|
|
// Near gate: soft start just behind the character (no hard seam at their feet).
|
|
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 corridor_f = lateral * near_f * far_f;
|
|
// Bottom-wall flag (INSTANCE_CUSTOM.r, GreyboxWorld): KNOWN non-wall tile
|
|
// behind this wall run — it would occlude interior the player should see.
|
|
float cut_f = corridor_f;
|
|
if (u_cutaway_mode == 1) {
|
|
cut_f = INSTANCE_CUSTOM.r;
|
|
} else if (u_cutaway_mode == 2) {
|
|
cut_f = max(corridor_f, INSTANCE_CUSTOM.r);
|
|
}
|
|
v_cut = cut_f > 0.5 ? 1.0 : 0.0;
|
|
v_world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
}
|
|
|
|
void fragment() {
|
|
float allowed = mix(u_wall_h, u_cut_height, v_cut);
|
|
if (v_world_pos.y > allowed) {
|
|
discard;
|
|
}
|
|
ALBEDO = u_base_albedo * COLOR.rgb;
|
|
ROUGHNESS = 1.0;
|
|
}
|