Skip to content

[feat] 40 F14-F16: the Aquarium reef, motion nodes, fish-scale look - #350

Closed
AlexZ005 wants to merge 6 commits into
feat/40from
feat/40-aquarium
Closed

AlexZ005 wants to merge 6 commits into
feat/40from
feat/40-aquarium

Conversation

@AlexZ005

@AlexZ005 AlexZ005 commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Roadmap 40 rows F14-F16 (lane 40-aquarium).

F15 — general-purpose motion nodes

  • Follow Path, Wander, Orient to Velocity, Body Wave: engine nodes, not fish code. Each has a docs page (docs PR) and unit tests (23).

F16 — fish-scale look

  • The Fish scales starter look (thin-film iridescence, scale relief, a light transmission pass) plus a per-device look tier: a phone drops only the transmission pass. 10 unit tests.

F14 — the Aquarium becomes a reef

  • Fallback LOD groups: an object's own simplified meshes are LOD0 and draw only until the real model's levels are available. The scene still works if the pack CDN is unreachable.
  • The Aquarium template: Meshy clownfish, blue tangs, angelfish, coral and plants (packs aquarium-kit), swimming with the F15 nodes.
  • Fix for a pre-existing bug: a nested render (the water reflection pass) froze every LOD group after its first frames. Both scene after-hooks (lod.js, waterRuntime.js) returned early before calling the hook they wrap, which leaked one depth per nested render. vitest lodHookChain reads depth 5 and 0 passes with the old hook.

Proof

  • e2e aquarium-reef 32/32 (packs served locally), incl. 2.0: the LOD pass keeps running with water in the scene.
  • vitest 166 files / 1923 tests green. svelte-check 300/41 (baseline held). npm run build green.
  • Before/after shots, phone 390×844 and desktop 1440×900, dark and light: ~/.code/lanes-30/after-40/40-aquarium/shots/.

Release order

Merge the packs PR and retag format-1 BEFORE the scenes Aquarium goes live (its models load from PACKS_BASE).

🤖 Generated with Claude Code

AlexZ005 and others added 6 commits October 8, 2026 13:55
…elocity, body wave (40 F15)

- motion/motionCore.js (pure, unit-tested): centripetal Catmull-Rom paths with arc
  length, loop/pingpong/once + offset, signed curvature, bank roll, seeded smooth
  bounded noise with an analytic rate, the body-wave envelope/drive/offset
- motion/motionNodes.js (three leaf): Follow Path rides a drawn Spline, a Flow path
  or its own waypoints in world space, faces along it and banks into turns; Wander
  drifts inside an area object's box (or a box around its base), seeded per object;
  Orient to Velocity faces the frame's motion with smoothed turning + bank and holds
  while still; Body Wave bends any mesh subtree with a vertex-shader travelling wave
  whose amplitude/frequency follow speed and whose bend follows turn rate (per-mesh
  material clones keeping the original uuid, put back when the wave stops)
- flowRuntime applies the followers after the movers (stable sort, graphs without
  them unchanged) and prunes waves that stopped
- catalog (Animation group), sockets (path/area take an object), cards, doc lines
- tests/unit/motionCore.test.js: 23 tests incl. the bank-direction counterfactual

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(40 F16)

- material presets carry the thin film (iridescence, its IOR and thickness range) and
  the transmission volume; three's [min, max] range maps both ways
- a new starter "Fish scales": an iridescent physical look over a procedural, tileable
  scale relief (scales hang like shingles, lower row in front), a light clear coat and
  a hint of transmission; the swatch shows the film as a hue sweep
- materialTiersCore (pure) + materialTiers (runtime): every physical material is drawn
  at this device's tier, the water rule (headset or post off = low, a phone's start =
  mid): mid drops transmission (the extra full-scene pass), low also drops the thin
  film and sheen. LOCAL only; the authored numbers ride as userData.lookTierAuthored,
  written only when a device draws less, so a desktop save stays byte-identical and a
  headset's save restores the full look elsewhere; an edit made since is the new look
- debug hooks: materialTiers, motionNodes
- tests: materialTiers.test.js (10, incl. a save on a low device reloading at full
  look), the starter list grew by one

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…swimming with the motion nodes (40 F14)

- lodGroupCore: an additive `fallback: true` group — LOD0 (the object itself) is a STAND-IN,
  drawn only while the real model's levels are loading or unreachable; the pick runs over
  levels 1..n (pickFallbackLevel, hysteresis kept). Unit tests incl. the counterfactual.
- lodGroup: a pack level that does not pair with the object (or any level of a fallback
  group) is a TREE level, cloned per object, casting shadows; lodTrees.js (a leaf) lists
  the substitutes so Body Wave bends the real model and the look tier reaches its film
- Body Wave: each mesh set (the object's own, each substitute) bends in its own frame,
  from the local matrix chain
- author-templates: a `lod` object field; the card builds every fallback level first and
  draws each object's finest real model
- the Aquarium: six reef fish (clownfish Wander + Orient to Velocity, tangs and angelfish
  Follow Path loops with banking, every fish a Body Wave), a reef rock arch (was the torus),
  staghorn coral, eelgrass and Amazon swords — all aquarium-kit models over the old
  primitives as stand-ins wearing the thin film
- tests: e2e aquarium-reef (motion, fallback LOD, saves, tier, offline counterfactual);
  select-inside-36 / fb-water-probe find the fish the loaded Aquarium has

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… reef suite test fixes (40 F14)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- lod.js afterRender and the water runtime's afterSceneRender returned early on a
  nested render BEFORE calling the hook they wrap, while their before-hooks always
  call theirs: every water reflection pass leaked one depth into the wrapped hook,
  so the LOD group pass was skipped forever after the first frames (pre-existing,
  seen on the 1.31 Aquarium too). Both now always hand on to prevAfter.
- vitest lodHookChain (counterfactual: depth 5 / 0 passes with the old hook);
  aquarium-reef 2.0 asserts the pass keeps running with water in the scene.
- GroupEntry typedef carries the lastWant debug field (svelte-check back to 300/41).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…erty (40 F14)

- the tang loop is an ellipse (1.45 x 0.5 m), so two fish half a lap apart
  are never closer than its minor diameter, 1.0 m: the old > 1.2 m threshold
  could not pass. The check now asserts their midpoint stays on the loop centre
  (measured 0.00 m); with a broken offset it would be 0.5-1.45 m off.
- suite 32/32 green with the packs reachable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AlexZ005

Copy link
Copy Markdown
Collaborator Author

Included in 1.32.0: merged into feat/40-int → feat/41 → release PR #365 (v1.32.0, 862ed79). Closing as included.

@AlexZ005 AlexZ005 closed this Oct 13, 2026
@AlexZ005
AlexZ005 deleted the feat/40-aquarium branch October 23, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant