Water Playground
App: apps/water_playground/
The interactive water capstone (engine Block WTR2 — Water Fidelity). Where the Water Demo shows the two surface variants side by side on a fixed path, the playground hands you the camera: free-fly across the water and dive beneath it, flip the Realistic ↔ Toon variant and the weather at runtime, and spawn buoyant crates and barrels that bob and rock on the very waves the surface is rendering. It composes every piece of the water program in one scene:
- the two stylized water variants (the Water Demo surface pack);
- camera-submersion detection via
Graphics.setWaterPlane+Graphics.cameraWaterState(ADR 0094); - an underwater post effect cross-faded by the submersion
amount; - a camera-facing waterline meniscus quad (the half-in/half-out line at the surface);
- Jolt buoyancy —
Physics.applyBuoyancyagainst aWaterso the floaters ride the same Gerstner waves the shader draws; - a small pure-Wren weather driver (clear / overcast / storm) over the atmosphere knobs.
Run from root
Section titled “Run from root”% ./plume3d water_playgroundOn launch it runs a short scripted tour — spawns floaters, shows both variants and a storm, dives under the surface, and captures screenshots — so a headless run self-verifies. Pressing any control key hands control to the player.
Controls
Section titled “Controls”| Key | Action |
|---|---|
W A S D | move (free-fly) |
Q / E | descend / ascend (dive below, rise above) |
| Arrow keys | look (yaw / pitch) |
Space | spawn a buoyant crate in front of the camera |
B | spawn a buoyant barrel |
1 / 2 | Realistic / Toon water variant |
Tab | cycle weather (clear → overcast → storm) |
R | reset the floaters |
Esc | quit |
What it does
Section titled “What it does”-
One water surface, both variants. A single large subdivided
MeshGen.planeis marked transparent and tuned through the set-5 custom params (the same param layout the Water Demo documents, including the real Perlin foam textures in custom slots0/1). Pressing1/2swaps which shader it is drawn with —shaders/water_realisticorshaders/water_toon— over the same mesh and the same waves. -
Registers the water surface for submersion. At init it registers the infinite water plane and its underwater fog, then enables planar reflection and opaque capture (the gate the water shaders need):
Graphics.setPlanarReflection(true, 0.0)Graphics.setWaterPlane(0.0, 0.05, 0.24, 0.32, 0.5) // surfaceY, fog rgb, fog densityGraphics.opaqueCaptureEnabled(true) -
One
Waterdrives both the render and buoyancy. TheWaterCPU wave model is set to the same wave params as the realistic variant’sp3, so a floater bobs on the exact crests you see. Spawned crates/barrels are dynamic Jolt bodies; every framePhysics.applyBuoyancysamples probe points across each body against thatWater, so they float, bob, and rock:_water = Water.new()_water.setWaves(0.6, 13.0, 0.55, 1.0) // matches the realistic p3_water.setWind(1.0, 0.25)_water.setBaseY(0.0)// …per frame, per floater:Physics.applyBuoyancy(node, _water, _t, probeOffsets, strength, density) -
Dive below and the frame goes underwater. Each frame
draw()reads the submersion state and cross-fades the underwater post on theamountvalue — so crossing the surface is a smooth fade, not a hard cut — feeding it the fog colour/density the plane was registered with:var ws = Graphics.cameraWaterState() // [submerged, amount, level, fogR, fogG, fogB, fogDensity]// …draw seabed + rocks + floaters (opaque), then the water surface (transparent) while shallow…if (ws[1] > 0.15) {var k = /* smoothstep on ws[1] */Graphics.addPostEffect("shaders/underwater", [ws[3], ws[4], ws[5], ws[6], 0.006, ws[1], 0.5, 120.0, 0.6 * k, 0.4 * k, 0.0, 0.0])} else {Graphics.addPostEffect("shaders/present", [])}The underwater post tints the frame toward the deep-water colour and adds fog, a caustic shimmer and god-rays, all faded in by the submersion amount. The water surface mesh is skipped once the camera is deeply submerged (its underside would band), so from below you see the fog and the meniscus rather than the plane’s back face.
-
The waterline meniscus. A small camera-facing quad (
shaders/water_line) is drawn pinned in front of the camera; the shader raises a deep-water fill from the bottom as the camera’s worldYdrops through the surface, giving the half-in / half-out waterline you see when your eye is right at the surface. It is a plain set-5 custom-material quad — no engine change — the cameraYis written into a param each frame. -
Runtime weather.
Tabcycles a pure-Wren weather driver (clear / overcast / storm) that drives only the engine’s neutral atmosphere knobs —setSky,setFog,setAmbientand globalsetWind(which also re-steers the water waves) — plus a storm lightning flash. The same policy-over-neutral-knobs pattern as the Sky / Weather and Weather packs.
Graphics.setWaterPlane+Graphics.cameraWaterState— the WTR2 camera-submersion API (ADR 0094): register the surface + fog, read[submerged, amount, …]each frame.Water+Physics.applyBuoyancy— the CPU wave mirror and multi-point Jolt buoyancy (ADR 0091) that make the floaters ride the rendered waves.- Mesh — custom material (set 5) — the water surface, the meniscus quad, and the underwater post are all tuned through set-5 params (no engine change).
Graphics.opaqueCaptureEnabled— the single gate feeding both the opaque colour copy and the scene depth copy the water shaders read.Graphics.setPlanarReflection— the reflection the water samples.Graphics.addPostEffect— the underwater ↔ present swap.- MeshGen — the water plane, seabed, rocks, and floater meshes.
Notes and limitations
Section titled “Notes and limitations”- The surface look is pure content; the submersion state is the engine bit. The two water variants,
the underwater post, and the meniscus are composed from already-shipped capabilities (set-5 custom
params, scene depth, opaque capture,
planar reflection). The only new engine surface this app relies on
is the WTR2 camera-submersion API —
Graphics.setWaterPlaneandGraphics.cameraWaterState(additive, frozen; ADR 0094) — plus theWater+ buoyancy feature (ADR 0091). - Underwater is a screen-space approximation. The fog / caustics / god-rays live in a post effect, so they are screen-locked; a dedicated world-space underwater pass is a documented follow-up (ADR 0094). It reads convincingly for a dive, not as a full submerged-scene render.
- Submersion uses an infinite plane. This demo registers a
setWaterPlane; bounded water volumes (a box-tagged pool that also bounds “underwater” to the box footprint) are not yet wired intocameraWaterState(see the Graphics water API note). Camis not used here. Like the other immediate-mode water demos it drives view/projection explicitly each frame (Graphics.setViewMatrix/setProjectionMatrix) rather thanScene.draw()with aCamrig, because it composes the opaque/transparent phases and the post swap by hand.