Files
settled-reach/client/shaders/sandbox/greybox_tile.gdshader
T
jpmschweitzerandClaude Fable 5 a7801a942a feat(client): 3D locomotion sandbox — character walks the live Gauntlet (T-1088)
New SR_LIVE sandbox scene: CharacterVisual composited in a 3D greybox world
derived from server snapshots. Per-leg constant-velocity interpolation keyed
to the stance throttle, 'server feet / client eyes' facing (wire octant while
moving, client aim octant idle), cadence-synced gait state machine on
AnimationPlayer custom blends, D-148 orthographic follow camera (-30deg
default, T-cycle presets), sim-space grid shader, camera-side wall cutaway,
accumulating never-evict tile store with four-state visibility tint.

Additive seams only: InputMapper.facing_angle_provider (2D path unchanged),
CharacterVisual.play_animation blend_time param + get_animation_player().
Visual harness gains per-scenario scene field + SR_AUTOPILOT input scripting.
210 new gdUnit assertions across five suites; verified live (230/230 total,
clean smoke, screenshot at .cache/screenshots/locomotion_idle_live.png).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:23:16 +02:00

56 lines
2.6 KiB
Plaintext

shader_type spatial;
render_mode diffuse_lambert, specular_disabled;
// T-1088 greybox floor (design §3.3) — sandbox-only.
//
// Per-instance visibility tint x world-space grid lines. The MultiMesh instance
// color (use_colors, GreyboxWorld) arrives as the COLOR built-in and multiplies
// albedo: white = Forward, 85% = Peripheral, 70% = BoundaryWall, dim cool grey =
// Remembered (SandboxConstants.TINTS — §3.2).
//
// Grid: data is per-0.5 m subtile (D-066/D-222) but the floor must READ as 1 m
// visual tiles (2x2 subtiles) — minor lines every 0.5 m (alpha 0.15), major
// lines every 1.0 m (alpha 0.45). Lines are sampled in SIM space (u_sim_from_world
// undoes the 45 deg WorldRoot rotation) so they land on tile edges; sampling raw
// world space would draw the grid rotated 45 deg against the tiles. Continuous
// across instances because sim space is shared. D-243's "voxel = 1 m" is
// generation-cascade vocabulary — the 1 m visual tile exists ONLY as the major
// lines here (§2).
uniform vec3 u_base_albedo : source_color = vec3(0.52, 0.54, 0.58);
uniform vec3 u_line_albedo : source_color = vec3(0.10, 0.11, 0.14);
uniform float u_minor_spacing_m = 0.5;
uniform float u_major_spacing_m = 1.0;
uniform float u_minor_alpha = 0.15;
uniform float u_major_alpha = 0.45;
uniform float u_line_half_width_m = 0.02;
// World -> sim-space transform; set by GreyboxWorld from its own inverse global
// transform so the grid rotates WITH the tiles under the WorldRoot 45 deg.
uniform mat4 u_sim_from_world = mat4(1.0);
varying vec2 v_world_xz;
void vertex() {
// MODEL_MATRIX folds in the per-instance MultiMesh transform (incl. WorldRoot
// rotation); u_sim_from_world undoes the scene rotation back to sim axes.
v_world_xz = (u_sim_from_world * MODEL_MATRIX * vec4(VERTEX, 1.0)).xz;
}
// 1.0 on a grid line of the given spacing, 0.0 off it, one-fwidth AA edge.
float line_mask(vec2 p, float spacing, float half_width) {
vec2 dxy = abs(fract(p / spacing + 0.5) - 0.5) * spacing; // metres to nearest line
float d = min(dxy.x, dxy.y);
float aa = fwidth(d);
return 1.0 - smoothstep(half_width - aa, half_width + aa, d);
}
void fragment() {
float minor = line_mask(v_world_xz, u_minor_spacing_m, u_line_half_width_m) * u_minor_alpha;
float major = line_mask(v_world_xz, u_major_spacing_m, u_line_half_width_m) * u_major_alpha;
float line_a = max(minor, major); // major wins where the grids coincide
vec3 tinted = u_base_albedo * COLOR.rgb;
// Line color is tinted too, so remembered/peripheral tiles keep dim lines.
ALBEDO = mix(tinted, u_line_albedo * COLOR.rgb, line_a);
ROUGHNESS = 1.0;
}