diff --git a/public/tide-pool-world.js b/public/tide-pool-world.js index a47a9a6..227d206 100644 --- a/public/tide-pool-world.js +++ b/public/tide-pool-world.js @@ -382,7 +382,10 @@ function sediment(world, dt) { for (let j = 0; j < ROWS; j++) for (let i = 0; i < COLS; i++) { const k = j * COLS + i, d = w[k] - h[k] - wall[k]; // Water too thin to hold anything drops what it carried. - if (d < WET) { if (silt[k] > 0) { sand[k] += silt[k]; h[k] += silt[k]; changed += silt[k]; silt[k] = 0; } continue; } + if (d < WET) { + if (silt[k] > 0) { sand[k] += silt[k]; h[k] += silt[k]; changed += silt[k]; silt[k] = 0; if (w[k] < h[k] + wall[k]) w[k] = h[k] + wall[k]; } + continue; + } const u = ((i > 0 ? qx[k - 1] : 0) + qx[k]) * .5 / d, v = ((j > 0 ? qz[k - COLS] : 0) + qz[k]) * .5 / d; const over = Math.hypot(u, v) - LIFT, room = over > 0 ? CARRY * d * over * over : 0; if (silt[k] < room) { @@ -392,6 +395,7 @@ function sediment(world, dt) { } else if (silt[k] > room) { const fall = Math.min((silt[k] - room) * SETTLE * dt, silt[k]); sand[k] += fall; h[k] += fall; silt[k] -= fall; changed += fall; + if (w[k] < h[k] + wall[k]) w[k] = h[k] + wall[k]; } } // Across each face the flow carries its share of the sand held in the cell it leaves; out past the edge, the sea keeps it. diff --git a/public/tide-pool.js b/public/tide-pool.js index dcd43e8..57d9500 100644 --- a/public/tide-pool.js +++ b/public/tide-pool.js @@ -1,7 +1,7 @@ import { createWorld, advanceWorld, offerObject, SPECIES, SPECIES_ORDER, CELL, COLS, ROWS, X0, Z0, X1, Z1, START_POOL, waterLevel, daylight, - heightAt, surfaceAt, depthAt, onShelf, bodyAt, tideOf, tideRising, label, goalText, describeWorld, creatureById, MACHINES, BLOCK, BLOCK_Y0, blockKey, footAt, debrisAt, footing, stormAt, shineLantern, douseLantern, moonlight, -} from './tide-pool-world.js?v=38'; + heightAt, surfaceAt, depthAt, onShelf, bodyAt, tideOf, tideRising, label, goalText, describeWorld, creatureById, MACHINES, BLOCK, BLOCK_Y0, blockKey, footAt, debrisAt, footing, stormAt, shineLantern, douseLantern, moonlight, SWELL, swellHeight, +} from './tide-pool-world.js?v=39'; const root = document.querySelector('[data-tide-pool]'); const status = document.querySelector('#tide-status'); @@ -156,6 +156,10 @@ async function initialize() { planktonTexture.magFilter = planktonTexture.minFilter = T.LinearFilter; planktonTexture.needsUpdate = true; shared.uPlankton = { value: planktonTexture }; shared.uHaze = { value: new T.Vector4(0, 1, 0, 0) }; shared.uHazeZoom = { value: 7 }; + // The swell the simulation brings in at its seaward edge, so the open sea beyond carries the same waves on. + shared.uSwell = { value: SWELL.map(s => new T.Vector4(s.a, s.kx, s.kz, s.w)) }; + shared.uSwellPhase = { value: new T.Vector3(...SWELL.map(s => s.phase)) }; + shared.uSwellLift = { value: 1 }; shared.uHazeColor = { value: new T.Color(0x1a1524) }; // The simulation grid as a texture: r = water surface, g = ground, b = set stone, a = how damp the ground still is. // Sampled with a cubic B-spline (four bilinear taps) so shorelines and hollows come out round, not grid-shaped. @@ -354,6 +358,7 @@ async function initialize() { uGrid: shared.uGrid, uGridBox: shared.uGridBox, uFlow: shared.uFlow, uOpen: { value: 0 }, uTide: shared.uWater, uLamps: shared.uLamps, uLampColor: shared.uLampColor, uNight: shared.uNight, uDusk: shared.uDusk, uStorm: shared.uStorm, uWakes: shared.uWakes, uPlankton: shared.uPlankton, uMoon: shared.uMoon, + uSwell: shared.uSwell, uSwellPhase: shared.uSwellPhase, uSwellLift: shared.uSwellLift, uHaze: shared.uHaze, uHazeZoom: shared.uHazeZoom, uHazeColor: shared.uHazeColor }, vertexShader: `${GRID} uniform float uOpen, uTide, uTime, uStorm; varying vec3 vW; varying float vDepth; varying vec2 vSlope; @@ -375,6 +380,7 @@ async function initialize() { uniform vec4 uLamps[${LAMPS}]; uniform vec3 uLampColor, uHazeColor; uniform float uNight, uDusk, uHazeZoom, uStorm; uniform vec4 uHaze; uniform vec4 uWakes[${WAKES}]; uniform sampler2D uPlankton; uniform float uMoon; uniform sampler2D uFlow; uniform vec4 uGridBox; uniform float uOpen; + uniform vec4 uSwell[3]; uniform vec3 uSwellPhase; uniform float uSwellLift; ${WAVES} float foamHash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } // Foam lace: the walls between cells scattered over the surface, as the bubbles of spent foam draw them. @@ -401,6 +407,11 @@ async function initialize() { vec2 g1 = waveSlope(vW.xz - flow * t1 * 2.2, uTime, 1.4), g2 = waveSlope(vW.xz - flow * t2 * 2.2, uTime, 1.4); // A storm roughs the water up. vec2 g = mix(g1, g2, abs(2. * t1 - 1.)) * smoothstep(.01, .25, d) * 1.3 * detail * (1. + uStorm * 1.6) + vSlope * 1.6; + // Out on the open sea the swell's slope comes straight from the waves themselves. + if (uOpen > .5) for (int i = 0; i < 3; i++) { + vec4 s = uSwell[i]; + g += s.yz * s.x * uSwellLift * cos(s.y * vW.x + s.z * (vW.z - ${Z1.toFixed(2)}) + s.w * uTime + uSwellPhase[i]) * 1.6; + } vec3 n = normalize(vec3(-g.x, 1., -g.y)); // Depth: a clear lavender-grey in the shallows, deep violet where the water is deep. float absorb = 1. - exp(-d * 1.3); @@ -1600,6 +1611,7 @@ async function initialize() { shared.uHaze.value.set(-Math.sin(YAW), -Math.cos(YAW), view.x, view.z); shared.uHazeZoom.value = view.zoom; shared.uWater.value = waterLevel(world); + shared.uSwellLift.value = swellHeight(world); uploadGrid(); uploadFlow(); if (night > .02) { uploadPlankton(1 / 30); gatherWakes(); } diff --git a/src/components/tide-pool.tsx b/src/components/tide-pool.tsx index 3f0ea2e..d4f823c 100644 --- a/src/components/tide-pool.tsx +++ b/src/components/tide-pool.tsx @@ -142,7 +142,7 @@ export function TidePoolContent() { {/* Lets the three.js add-ons resolve the same pinned module the page already uses. */} + ); }