River Demo
App: apps/river_demo/
Demonstrates a spline water mesh (engine PLM-273, Block WATER) — the owner ask
“spline water meshes for rivers and water flow.” A Spline (Catmull-Rom)
is swept into a flat ribbon with MeshGen.ribbon (Block
SPL) using the per-bend flow option (flow = true), and the shared stylized Toon water
(the same water_common.slangh core as the Water Demo) is applied to
it. The river flows downstream along each bend: ribbon bakes every station’s downstream
tangent into the mesh, and the water shader scrolls its foam along that local direction — so
the current follows the curve of the channel rather than one fixed global direction (ADR 0113).
This is content built on a small additive engine seam — the per-bend flow adds an optional
flow parameter to MeshGen.ribbon (ADR 0113); everything
else is already-shipped capability (the Block SPL Spline, the
set-5 custom material,
PLM-263 scene depth, and A#3 opaque capture).
Run from root
Section titled “Run from root”% ./plume3d river_demoGreen land the river runs across writes depth for the water’s shoreline; rocks poke through
the channel as foam witnesses. The demo screenshots river_demo.png at frame 70 and exits at
frame 90.
What it does
Section titled “What it does”-
Builds the river as a Catmull-Rom
Splineswept into a flat ribbon aty = 0withMeshGen.ribbon— passingflow = true(the 5-arg form) so each station’s downstream tangent is baked into the ribbon — and marks it transparent so it renders in the post-copy phase:_water = Graphics.loadShader("shaders/water")var path = Spline.new()path.addPoint(-9.0, 0.0, -34.0)path.addPoint(-3.0, 0.0, -20.0)path.addPoint( 4.0, 0.0, -6.0)path.addPoint(-2.0, 0.0, 8.0)path.addPoint( 2.0, 0.0, 24.0)_river = MeshGen.ribbon(path, 13.0, 96, null, true) // flow = true → bake per-bend downstream flow_river.blendMode = "alpha" // transparent → samples the opaque copy -
The Toon water shader reads that baked flow and scrolls the whitewater foam along each local bend — so the foam follows the river’s curve, not one global wind vector. (Where no flow is baked, the shader falls back to the global wind direction, as open water does.)
-
Tunes the Toon water through the set-5 custom params (
setCustomColor’s first argument is a byte offset, not a slot index) — a bright river teal, small fast waves, faster flow, and wide whitewater bank foam:_river.setCustomColor(0, 0.40, 0.82, 0.78, 0.0) // p0 shallow_river.setCustomColor(48, 0.10, 10.0, 0.22, 1.1) // p3 waveGain, waveLen, waveSteepness, waveSpeed (small, fast)_river.setCustomColor(64, 0.0, 0.0, 0.05, 0.16) // p4 …, flowSpeed (faster), normalStrength_river.setCustomColor(96, 0.62, 0.02, 3.0, 0.7) // p6 (legacy toon stepped-look knobs — no longer read by the faithful shader)_river.setCustomColor(112, 0.777, 0.02, 0.12, 0.27) // p7 surfaceNoiseCutoff (Roystan .777), foamScrollX, foamScrollY, surfaceDistortAmount// … p1 deep, p2 foam, p5 refract/fresnel/spec, p8 camera planes, p9 cam pos, p10 horizon -
Each frame in
draw(): drives view/projection explicitly, enables opaque capture (the shared gate for the A#3 opaque colour and the PLM-263 scene depth), draws the opaque riverbed + rocks, then the transparent river, and adds a passthrough post effect:Graphics.opaqueCaptureEnabled(true)Graphics.drawMesh(_land, _lit, /* riverbed, below y=0 */) // opaque: depth for the shorelinefor (r in _rocks) Graphics.drawMesh(_rock, _lit, /* … */) // foam witnessesGraphics.drawMesh(_river, _water, /* … */) // transparent flowing waterGraphics.addPostEffect("shaders/present", [])
Spline— the Catmull-Rom curve the river follows.MeshGen.ribbon— sweeps the spline into the flat water ribbon; the 5-argflow = trueform bakes each bend’s downstream direction so the foam flows along the curve (pass aSceneasconformSceneto hug terrain instead; herenull= flat).- Mesh — custom material (set 5) — the Toon water params the whole look is tuned through.
Graphics.opaqueCaptureEnabled— the gate feeding the opaque-colour and scene-depth copies the water shader reads.
Notes and limitations
Section titled “Notes and limitations”- Per-bend flow is a small additive engine seam. The river composes the stylized water pack
with the depth/opaque-capture feeds; the one engine addition is the optional
flowparameter onMeshGen.ribbon(ADR 0113) — the ribbon bakes the downstream tangent into the mesh’s vertex colour and the water shader scrolls foam along it. - This is the flowing surface. It does not float anything — for physics on the water see
the Buoyancy Demo (
Water+Physics.applyBuoyancy) and the masked buoyant open-hull boat in the Water Mask Demo. Custom water textures for full tutorial parity remain a documented follow-up.