SeabedFloraActor scatters seagrass tufts on flat sand in the lit band and
sea fans on rock between 15 and 60 m, deterministically from the site id,
one MultiMesh per tile and kind, with procedural alpha-cut meshes and
textures (SeabedFloraAssets), vertex sway and wrapped lighting, no
collision and no sonar presence. Placement reads the field arrays
directly and runs on the worker thread pool. The seabed shader grows
turf algae on sunlit rock, coralline crusts in patches and encrusting
sponges in the shade, fading out between 40 and 75 m. The water-column
optics move to a shared shader include.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tiles holding a slope over 35 degrees are meshed at two vertices per cell
on a clamped Catmull-Rom surface that passes through every grid node, so
cliffs and ledges lose the facets of the heightfield grid while the
collision stays on the grid. The domain field exposes the smooth
elevation and a separable, per-phase-weighted block sampler with linear
edge extrapolation and a one-cell margin for continuous normals; the actor
samples tiles on the worker thread pool and builds meshes afterwards.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A FogVolume covering the site carries a fog shader whose scattering albedo
follows the same spectral transmittance as the seabed and whose density
grows below the reference depth, so haze darkens and turns indigo with
depth around every object; the global volumetric density moves into the
volume and the far depth fog only covers what lies beyond 128 m. Sand
ripples are bent by a slow domain warp, blended with a larger ripple
scale and faded out between 40 and 60 m, which hides the 3.2 m tiling.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The seabed shader applies per-channel Beer-Lambert transmittance relative
to a 24 m reference depth: the scalar part dims the directional light in a
custom light() function and the sky ambient through AO, the chroma part
tints the albedo, and artificial lights are left untouched for the
submarine's projectors. Sky, fog and sun are cooled and the sun softened
so the plateau reads green-blue, the canyon floor cold blue and the
drop-off foot near-indigo.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The environment declared the sky as its ambient source without a sky, so
shadowed faces received no ambient light. A procedural underwater sky now
feeds ambient and reflections; the directional light is steeper and
stronger with 70 % soft shadows; SSAO, filmic tonemapping, aerial
perspective and tinted volumetric fog complete the pass.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rock is normal-mapped triplanar in world space at two scales, sand carries an
oriented ripple normal map on the xz plane, and a grain map drives detail
occlusion, albedo mottling and roughness. The three maps are seamless,
seeded, mipmapped textures built synchronously by SeabedDetailTextures, so
no asset is needed and builds are deterministic. The mesh has no tangents:
perturbed normals are computed in world space and written to NORMAL.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drop a dead initial value in PlayableBoundsRule, guard
DiveSite.configure() when called before the node is in the tree, and
align the zone 1 brief with what was built: generic dive_site.tscn,
the resolved collision check, and the graybox shader deviation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lighter fog and volumetric fog, brighter key and ambient light, terrain
tiles visible to 420 m (60 m fade), and a background colour equal to
the fog colour times its energy so culled tiles never read as a dark
band. The seabed shader culls back faces again now that the tiles are
wound front-face up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Building the 64 tiles of the Banc du Vigilant cost about 1.9 s through
per-vertex cell_world_position/normal_at_cell calls, leaving the 3 s
load budget with too little headroom (2.1-2.9 s measured, one 4.8 s
outlier). TerrainHeightField now returns positions and normals of a
cell block in one call, with normal_at_cell sharing the same formula;
the tile build drops to about 0.5 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DiveSite.configure() builds the height field once (TerrainHeightField
validates the data) instead of running the definition validation and
then the field construction again; the composition root relies on the
error returned by configure(). The collision shape takes the field's
row-major elevations directly instead of copying 263k cells one by one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The calibration site spawn sat 16 m from the south bounds edge, inside the
40 m warning margin. The vehicle_test recipe now extends the terrain and
playable bounds 42 m to the south so the spawn is inside the zone.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>