@wave3d/core 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/config/model.d.ts +104 -1
- package/dist/config/model.js +111 -1
- package/dist/config/model.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/presets.js +15 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +34 -0
- package/dist/renderer/WaveRenderer.js +232 -7
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/interaction.d.ts +119 -0
- package/dist/renderer/interaction.js +474 -0
- package/dist/renderer/interaction.js.map +1 -0
- package/dist/renderer/palette.js +14 -0
- package/dist/renderer/palette.js.map +1 -1
- package/dist/renderer/shaders.js +105 -0
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/shell/createWave.d.ts +9 -1
- package/dist/shell/createWave.js +7 -1
- package/dist/shell/createWave.js.map +1 -1
- package/dist/shell/poster.d.ts +12 -0
- package/dist/shell/poster.js +4 -3
- package/dist/shell/poster.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +864 -219
- package/dist/standalone.d.ts +2 -2
- package/dist/standalone.js +2 -2
- package/dist/studio/StudioWaveRenderer.d.ts +15 -0
- package/dist/studio/StudioWaveRenderer.js +32 -1
- package/dist/studio/StudioWaveRenderer.js.map +1 -1
- package/dist/studio/randomize.d.ts +3 -3
- package/dist/studio/randomize.js +4 -4
- package/dist/studio/randomize.js.map +1 -1
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +20 -2
package/dist/presets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["/**\n * Built-in presets: each a complete studio config (scene + one or more waves) in the wave model.\n * IP-clean — no copyrighted assets. The studio layers its own extra presets (and its historical\n * \"Stripe *\" display names) on top; see apps/studio/src/presets.ts.\n */\nimport { createDefaultConfig, makeStops, makeWaveSpread } from \"./config/model\";\nimport type { StudioConfig, NoiseBand } from \"./config/model\";\n\nconst RAD = 180 / Math.PI;\n\n/** Build a preset from a set of wave parameters. rotation/hue are given in RADIANS and\n * converted to degrees. All presets are solid-theme, so they reuse the hero palette +\n * surfaceColor fibers (600/0.2) and sheen 0, like the hero. camTarget/zoom frame the\n * wave (we pan the look-at to centre each one). */\nfunction buildPreset(p: {\n speed: number;\n contrast: number;\n sat: number;\n hueRad: number;\n dispX: number;\n dispZ: number;\n dispAmt: number;\n pos: [number, number, number];\n rotRad: [number, number, number];\n scale: [number, number, number];\n twF: [number, number, number];\n twP: [number, number, number];\n glow: [number, number, number];\n grain: number;\n blur: number;\n zoom: number;\n camTarget: [number, number];\n noiseBands?: NoiseBand[];\n twistMotion?: boolean;\n}): StudioConfig {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.speed = p.speed;\n w.colorContrast = p.contrast;\n w.colorSaturation = p.sat;\n w.hueShift = p.hueRad * RAD;\n w.displaceFrequency = { x: p.dispX, y: p.dispZ };\n w.displaceAmount = p.dispAmt;\n w.position = { x: p.pos[0], y: p.pos[1], z: p.pos[2] };\n w.rotation = { x: p.rotRad[0] * RAD, y: p.rotRad[1] * RAD, z: p.rotRad[2] * RAD };\n w.scale = { x: p.scale[0], y: p.scale[1], z: p.scale[2] };\n w.twistFrequency = { x: p.twF[0], y: p.twF[1], z: p.twF[2] };\n w.twistPower = { x: p.twP[0], y: p.twP[1], z: p.twP[2] };\n w.creaseLight = p.glow[0];\n w.creaseSharpness = p.glow[1];\n w.creaseSoftness = p.glow[2];\n if (p.noiseBands) w.noiseBands = p.noiseBands;\n if (p.twistMotion) w.twistMotion = true;\n c.grain = p.grain;\n c.blur = p.blur;\n c.cameraPosition = { x: 100, y: 0, z: 5000 };\n c.cameraTarget = { x: p.camTarget[0], y: p.camTarget[1], z: 0 };\n c.cameraZoom = p.zoom;\n return c;\n}\n\n/** Presets: each a complete studio config (scene + one or more waves) in the wave model. */\nexport const PRESETS: Record<string, () => StudioConfig> = {\n // The app's default wave: a centred, full-frame ribbon (window-independent framing).\n // Shown first and named \"Hero\"; several presets below derive from it.\n Hero: () =>\n buildPreset({\n speed: 0.04,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [380, -301.7, -11.1],\n rotRad: [-0.44959, -0.11759, 1.874407],\n scale: [9, 8, 5],\n twF: [-0.65, 0.41, -0.58],\n twP: [3.63, 0.7, 3.95],\n glow: [1.98, 0.806, 0.834],\n grain: 1.1,\n blur: 0.02,\n zoom: 0.55,\n camTarget: [-420, -200], // user-tuned default framing\n }),\n // Stripe's real hero, recreated faithfully: an orthographic ×10 scene that overflows the\n // frame, so only the twisted crop shows. This is the model's plain default config.\n \"Wave 2\": () => createDefaultConfig(),\n // camTarget on the waves below is a first-pass centring; tune per-wave. NOTE: Wave 4 also\n // uses a variant vertex shader (animated twist-X wobble) we don't fully replicate — its\n // STATIC frame is close, the motion differs.\n \"Wave 3\": () =>\n buildPreset({\n speed: 0.08,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [-200.7, -65.4, -11.1],\n rotRad: [-2.875593, 3.095927, -2.925927],\n scale: [3, 3, 3],\n twF: [0.059, 0.32, -0.397],\n twP: [3.63, 0.44, 5.99],\n glow: [3.86, 0.923, 1],\n grain: 1.2,\n blur: 0.02,\n zoom: 1.3,\n camTarget: [-104, 13], // centred; zoomed in (wide/flat wave)\n }),\n \"Wave 4\": () =>\n buildPreset({\n speed: 0.0525,\n contrast: 0.969,\n sat: 1.383,\n hueRad: 0.0376991,\n dispX: 0.005,\n dispZ: 0.0212,\n dispAmt: 6.68,\n pos: [206.1, -438, -11.1],\n rotRad: [-0.666018, -0.031416, 0.779115],\n scale: [6.0501, 8.3983, 6.9854],\n twF: [-0.424, 0.024, -1.312],\n twP: [1.81, 0.94, 4.76],\n glow: [1.55, 1.174, 0.972],\n grain: 0.576,\n blur: 0,\n zoom: 0.9316,\n camTarget: [194, -402], // centred on the wave\n twistMotion: true, // variant vertex shader — animated twist-X wobble\n noiseBands: [\n {\n startX: 0.856,\n endX: 1,\n startY: 0,\n endY: 0.913,\n feather: 0.5,\n strength: 0.346,\n frequency: 1018,\n colorAttenuation: 1,\n parabolaPower: 0,\n },\n {\n startX: 0.038,\n endX: 0.538,\n startY: 0.105,\n endY: 1,\n feather: 0.3315,\n strength: 1,\n frequency: 190,\n colorAttenuation: 0,\n parabolaPower: 2.11,\n },\n ],\n }),\n // The dark-background hero: identical geometry/camera to the default hero, but theme\n // \"wireframe\" → the line shader on a dark page background, with grain 1.2. Same palette.\n Wireframe: () => {\n const c = createDefaultConfig();\n c.waves[0].theme = \"wireframe\";\n c.grain = 1.2;\n c.background = \"#0a2540\"; // dark navy page background\n c.transparentBackground = false;\n return c;\n },\n \"Neon Dark Multistrand\": () => {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.theme = \"wireframe\"; // line shader on the near-black background — neon wireframe look\n w.blendMode = \"additive\";\n w.palette = makeStops([\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"]);\n w.creaseLight = 1.0;\n c.background = \"#05060c\";\n c.transparentBackground = false; // fill the dark bg so the neon lines read on black (not the page)\n c.waves = makeWaveSpread(w, 3); // three overlapping neon waves\n c.waveCount = 3;\n return c;\n },\n \"Mesh Gradient\": () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a84ff\", x: 0.06, y: 0.9, influence: 0.68 },\n { color: \"#64d2ff\", x: 0.88, y: 0.92, influence: 0.72 },\n { color: \"#bf5af2\", x: 0.5, y: 0.64, influence: 0.58 },\n { color: \"#ff375f\", x: 0.1, y: 0.14, influence: 0.7 },\n { color: \"#ff9f0a\", x: 0.84, y: 0.12, influence: 0.74 },\n { color: \"#30d158\", x: 0.94, y: 0.5, influence: 0.54 },\n ];\n w.meshGradientSoftness = 0.68;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.06;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.14;\n c.grain = 0.3;\n c.blur = 0.008;\n c.background = \"#070914\";\n c.backgroundMode = \"color\";\n c.transparentBackground = false;\n return c;\n },\n \"Solar Bloom\": () => {\n // Radial gradient: a warm core blooming out to a deep-indigo edge. usePaletteTexture off so\n // our own stops map along the radial gradCoord instead of sampling the baked hero LUT.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"radial\";\n w.gradientShift = 0.14;\n w.palette = [\n { color: \"#fff3c4\", pos: 0 }, // warm-white core\n { color: \"#ffd166\", pos: 0.22 }, // gold\n { color: \"#ff8c42\", pos: 0.42 }, // orange\n { color: \"#ff5d8f\", pos: 0.62 }, // coral-pink\n { color: \"#a64dff\", pos: 0.82 }, // violet\n { color: \"#241246\", pos: 1 }, // deep indigo edge\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.01;\n // Deep warm radial vignette behind the bloom.\n c.background = \"#0a0714\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"radial\";\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#2a1330\", \"#08040f\"]);\n c.transparentBackground = false;\n return c;\n },\n Holographic: () => {\n // Conic gradient: an iridescent oil-slick sweep. The palette wraps (first ≈ last stop) so\n // the conic seam is invisible.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"conic\";\n w.gradientShift = 0.08;\n w.palette = [\n { color: \"#8ef6e4\", pos: 0 }, // mint (seam)\n { color: \"#6ec3ff\", pos: 0.18 }, // sky\n { color: \"#9b8cff\", pos: 0.36 }, // periwinkle\n { color: \"#ff8ad8\", pos: 0.54 }, // pink\n { color: \"#ffd98e\", pos: 0.72 }, // peach\n { color: \"#a0f0c8\", pos: 0.88 }, // seafoam\n { color: \"#8ef6e4\", pos: 1 }, // mint again (seamless wrap)\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.04;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.12;\n c.grain = 0.28;\n c.blur = 0.01;\n // Subtle deep teal → violet wash behind the iridescence.\n c.background = \"#05060c\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"linear\";\n c.backgroundGradientAngle = 135;\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#04121a\", \"#0a0518\"]);\n c.transparentBackground = false;\n return c;\n },\n Aurora: () => {\n // Mesh gradient: a moody aurora — teals/greens drifting into violet over a night-sky base\n // (distinct from the brighter iOS-style \"Mesh Gradient\").\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a1f3c\", x: 0.08, y: 0.12, influence: 0.62 },\n { color: \"#1fddb0\", x: 0.3, y: 0.7, influence: 0.78 },\n { color: \"#57f5a3\", x: 0.58, y: 0.86, influence: 0.7 },\n { color: \"#3a86ff\", x: 0.82, y: 0.55, influence: 0.62 },\n { color: \"#a15cff\", x: 0.5, y: 0.32, influence: 0.7 },\n { color: \"#071433\", x: 0.92, y: 0.08, influence: 0.6 },\n ];\n w.meshGradientSoftness = 0.72;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.008;\n // Dark night-sky MESH backdrop (also shows off the mesh background type).\n c.background = \"#03060f\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"mesh\";\n c.backgroundMeshPoints = [\n { color: \"#02040c\", x: 0.15, y: 0.85, influence: 0.7 },\n { color: \"#08243a\", x: 0.5, y: 0.5, influence: 0.75 },\n { color: \"#0a0f2e\", x: 0.85, y: 0.7, influence: 0.7 },\n { color: \"#04121a\", x: 0.7, y: 0.2, influence: 0.6 },\n { color: \"#000208\", x: 0.12, y: 0.12, influence: 0.6 },\n ];\n c.backgroundMeshSoftness = 0.75;\n c.transparentBackground = false;\n return c;\n },\n Palestine: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"palestine\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1;\n w.colorSaturation = 1;\n c.grain = 0.35;\n c.background = \"#f2efe8\";\n c.transparentBackground = true;\n return c;\n },\n \"Vaporwave Sunset\": () => {\n // The Hero wave re-posed/re-framed, plus the vaporwave palette.\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.position.x = 525;\n w.rotation.x = -0.64 * RAD;\n w.rotation.z = 1.68 * RAD;\n w.paletteSource = \"vaporwave\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.08;\n w.colorSaturation = 1.15;\n w.creaseLight = 1.25;\n c.cameraZoom = 1.1;\n c.cameraTarget = { x: 150, y: 360, z: 0 };\n c.background = \"#09051f\";\n c.transparentBackground = false;\n return c;\n },\n Kaleidoscope: () => {\n const c = PRESETS[\"Wave 3\"]();\n const w = c.waves[0];\n w.paletteSource = \"kaleidoscope\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.12;\n c.grain = 0.5;\n return c;\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["/**\n * Built-in presets: each a complete studio config (scene + one or more waves) in the wave model.\n * IP-clean — no copyrighted assets. The studio layers its own extra presets (and its historical\n * \"Stripe *\" display names) on top; see apps/studio/src/presets.ts.\n */\nimport { createDefaultConfig, makeStops, makeWaveSpread } from \"./config/model\";\nimport type { StudioConfig, NoiseBand } from \"./config/model\";\n\nconst RAD = 180 / Math.PI;\n\n/** Build a preset from a set of wave parameters. rotation/hue are given in RADIANS and\n * converted to degrees. All presets are solid-theme, so they reuse the hero palette +\n * surfaceColor fibers (600/0.2) and sheen 0, like the hero. camTarget/zoom frame the\n * wave (we pan the look-at to centre each one). */\nfunction buildPreset(p: {\n speed: number;\n contrast: number;\n sat: number;\n hueRad: number;\n dispX: number;\n dispZ: number;\n dispAmt: number;\n pos: [number, number, number];\n rotRad: [number, number, number];\n scale: [number, number, number];\n twF: [number, number, number];\n twP: [number, number, number];\n glow: [number, number, number];\n grain: number;\n blur: number;\n zoom: number;\n camTarget: [number, number];\n noiseBands?: NoiseBand[];\n twistMotion?: boolean;\n}): StudioConfig {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.speed = p.speed;\n w.colorContrast = p.contrast;\n w.colorSaturation = p.sat;\n w.hueShift = p.hueRad * RAD;\n w.displaceFrequency = { x: p.dispX, y: p.dispZ };\n w.displaceAmount = p.dispAmt;\n w.position = { x: p.pos[0], y: p.pos[1], z: p.pos[2] };\n w.rotation = { x: p.rotRad[0] * RAD, y: p.rotRad[1] * RAD, z: p.rotRad[2] * RAD };\n w.scale = { x: p.scale[0], y: p.scale[1], z: p.scale[2] };\n w.twistFrequency = { x: p.twF[0], y: p.twF[1], z: p.twF[2] };\n w.twistPower = { x: p.twP[0], y: p.twP[1], z: p.twP[2] };\n w.creaseLight = p.glow[0];\n w.creaseSharpness = p.glow[1];\n w.creaseSoftness = p.glow[2];\n if (p.noiseBands) w.noiseBands = p.noiseBands;\n if (p.twistMotion) w.twistMotion = true;\n c.grain = p.grain;\n c.blur = p.blur;\n c.cameraPosition = { x: 100, y: 0, z: 5000 };\n c.cameraTarget = { x: p.camTarget[0], y: p.camTarget[1], z: 0 };\n c.cameraZoom = p.zoom;\n return c;\n}\n\n/** Presets: each a complete studio config (scene + one or more waves) in the wave model. */\nexport const PRESETS: Record<string, () => StudioConfig> = {\n // The app's default wave: a centred, full-frame ribbon (window-independent framing).\n // Shown first and named \"Hero\"; several presets below derive from it.\n Hero: () =>\n buildPreset({\n speed: 0.04,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [380, -301.7, -11.1],\n rotRad: [-0.44959, -0.11759, 1.874407],\n scale: [9, 8, 5],\n twF: [-0.65, 0.41, -0.58],\n twP: [3.63, 0.7, 3.95],\n glow: [1.98, 0.806, 0.834],\n grain: 1.1,\n blur: 0.02,\n zoom: 0.55,\n camTarget: [-420, -200], // user-tuned default framing\n }),\n // Stripe's real hero, recreated faithfully: an orthographic ×10 scene that overflows the\n // frame, so only the twisted crop shows. This is the model's plain default config.\n \"Wave 2\": () => createDefaultConfig(),\n // camTarget on the waves below is a first-pass centring; tune per-wave. NOTE: Wave 4 also\n // uses a variant vertex shader (animated twist-X wobble) we don't fully replicate — its\n // STATIC frame is close, the motion differs.\n \"Wave 3\": () =>\n buildPreset({\n speed: 0.08,\n contrast: 1,\n sat: 1,\n hueRad: -0.00159265,\n dispX: 0.005831,\n dispZ: 0.016001,\n dispAmt: -7.821,\n pos: [-200.7, -65.4, -11.1],\n rotRad: [-2.875593, 3.095927, -2.925927],\n scale: [3, 3, 3],\n twF: [0.059, 0.32, -0.397],\n twP: [3.63, 0.44, 5.99],\n glow: [3.86, 0.923, 1],\n grain: 1.2,\n blur: 0.02,\n zoom: 1.3,\n camTarget: [-104, 13], // centred; zoomed in (wide/flat wave)\n }),\n \"Wave 4\": () =>\n buildPreset({\n speed: 0.0525,\n contrast: 0.969,\n sat: 1.383,\n hueRad: 0.0376991,\n dispX: 0.005,\n dispZ: 0.0212,\n dispAmt: 6.68,\n pos: [206.1, -438, -11.1],\n rotRad: [-0.666018, -0.031416, 0.779115],\n scale: [6.0501, 8.3983, 6.9854],\n twF: [-0.424, 0.024, -1.312],\n twP: [1.81, 0.94, 4.76],\n glow: [1.55, 1.174, 0.972],\n grain: 0.576,\n blur: 0,\n zoom: 0.9316,\n camTarget: [194, -402], // centred on the wave\n twistMotion: true, // variant vertex shader — animated twist-X wobble\n noiseBands: [\n {\n startX: 0.856,\n endX: 1,\n startY: 0,\n endY: 0.913,\n feather: 0.5,\n strength: 0.346,\n frequency: 1018,\n colorAttenuation: 1,\n parabolaPower: 0,\n },\n {\n startX: 0.038,\n endX: 0.538,\n startY: 0.105,\n endY: 1,\n feather: 0.3315,\n strength: 1,\n frequency: 190,\n colorAttenuation: 0,\n parabolaPower: 2.11,\n },\n ],\n }),\n // The dark-background hero: identical geometry/camera to the default hero, but theme\n // \"wireframe\" → the line shader on a dark page background, with grain 1.2. Same palette.\n Wireframe: () => {\n const c = createDefaultConfig();\n c.waves[0].theme = \"wireframe\";\n c.grain = 1.2;\n c.background = \"#0a2540\"; // dark navy page background\n c.transparentBackground = false;\n return c;\n },\n \"Neon Dark Multistrand\": () => {\n const c = createDefaultConfig();\n const w = c.waves[0];\n w.theme = \"wireframe\"; // line shader on the near-black background — neon wireframe look\n w.blendMode = \"additive\";\n w.palette = makeStops([\"#00f5d4\", \"#00bbf9\", \"#9b5de5\", \"#f15bb5\", \"#fee440\"]);\n w.creaseLight = 1.0;\n c.background = \"#05060c\";\n c.transparentBackground = false; // fill the dark bg so the neon lines read on black (not the page)\n c.waves = makeWaveSpread(w, 3); // three overlapping neon waves\n c.waveCount = 3;\n return c;\n },\n \"Mesh Gradient\": () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a84ff\", x: 0.06, y: 0.9, influence: 0.68 },\n { color: \"#64d2ff\", x: 0.88, y: 0.92, influence: 0.72 },\n { color: \"#bf5af2\", x: 0.5, y: 0.64, influence: 0.58 },\n { color: \"#ff375f\", x: 0.1, y: 0.14, influence: 0.7 },\n { color: \"#ff9f0a\", x: 0.84, y: 0.12, influence: 0.74 },\n { color: \"#30d158\", x: 0.94, y: 0.5, influence: 0.54 },\n ];\n w.meshGradientSoftness = 0.68;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.06;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.14;\n c.grain = 0.3;\n c.blur = 0.008;\n c.background = \"#070914\";\n c.backgroundMode = \"color\";\n c.transparentBackground = false;\n return c;\n },\n \"Solar Bloom\": () => {\n // Radial gradient: a warm core blooming out to a deep-indigo edge. usePaletteTexture off so\n // our own stops map along the radial gradCoord instead of sampling the baked hero LUT.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"radial\";\n w.gradientShift = 0.14;\n w.palette = [\n { color: \"#fff3c4\", pos: 0 }, // warm-white core\n { color: \"#ffd166\", pos: 0.22 }, // gold\n { color: \"#ff8c42\", pos: 0.42 }, // orange\n { color: \"#ff5d8f\", pos: 0.62 }, // coral-pink\n { color: \"#a64dff\", pos: 0.82 }, // violet\n { color: \"#241246\", pos: 1 }, // deep indigo edge\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.01;\n // Deep warm radial vignette behind the bloom.\n c.background = \"#0a0714\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"radial\";\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#2a1330\", \"#08040f\"]);\n c.transparentBackground = false;\n return c;\n },\n Holographic: () => {\n // Conic gradient: an iridescent oil-slick sweep. The palette wraps (first ≈ last stop) so\n // the conic seam is invisible.\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.usePaletteTexture = false;\n w.gradientType = \"conic\";\n w.gradientShift = 0.08;\n w.palette = [\n { color: \"#8ef6e4\", pos: 0 }, // mint (seam)\n { color: \"#6ec3ff\", pos: 0.18 }, // sky\n { color: \"#9b8cff\", pos: 0.36 }, // periwinkle\n { color: \"#ff8ad8\", pos: 0.54 }, // pink\n { color: \"#ffd98e\", pos: 0.72 }, // peach\n { color: \"#a0f0c8\", pos: 0.88 }, // seafoam\n { color: \"#8ef6e4\", pos: 1 }, // mint again (seamless wrap)\n ];\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.04;\n w.colorSaturation = 1.12;\n w.fiberStrength = 0.12;\n c.grain = 0.28;\n c.blur = 0.01;\n // Subtle deep teal → violet wash behind the iridescence.\n c.background = \"#05060c\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"linear\";\n c.backgroundGradientAngle = 135;\n c.backgroundGradientSource = \"stops\";\n c.backgroundPalette = makeStops([\"#04121a\", \"#0a0518\"]);\n c.transparentBackground = false;\n return c;\n },\n Aurora: () => {\n // Mesh gradient: a moody aurora — teals/greens drifting into violet over a night-sky base\n // (distinct from the brighter iOS-style \"Mesh Gradient\").\n const c = PRESETS[\"Hero\"]();\n const w = c.waves[0];\n w.gradientType = \"mesh\";\n w.meshGradientPoints = [\n { color: \"#0a1f3c\", x: 0.08, y: 0.12, influence: 0.62 },\n { color: \"#1fddb0\", x: 0.3, y: 0.7, influence: 0.78 },\n { color: \"#57f5a3\", x: 0.58, y: 0.86, influence: 0.7 },\n { color: \"#3a86ff\", x: 0.82, y: 0.55, influence: 0.62 },\n { color: \"#a15cff\", x: 0.5, y: 0.32, influence: 0.7 },\n { color: \"#071433\", x: 0.92, y: 0.08, influence: 0.6 },\n ];\n w.meshGradientSoftness = 0.72;\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.18;\n w.fiberStrength = 0.12;\n c.grain = 0.3;\n c.blur = 0.008;\n // Dark night-sky MESH backdrop (also shows off the mesh background type).\n c.background = \"#03060f\";\n c.backgroundMode = \"gradient\";\n c.backgroundGradientType = \"mesh\";\n c.backgroundMeshPoints = [\n { color: \"#02040c\", x: 0.15, y: 0.85, influence: 0.7 },\n { color: \"#08243a\", x: 0.5, y: 0.5, influence: 0.75 },\n { color: \"#0a0f2e\", x: 0.85, y: 0.7, influence: 0.7 },\n { color: \"#04121a\", x: 0.7, y: 0.2, influence: 0.6 },\n { color: \"#000208\", x: 0.12, y: 0.12, influence: 0.6 },\n ];\n c.backgroundMeshSoftness = 0.75;\n c.transparentBackground = false;\n return c;\n },\n Palestine: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"palestine\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1;\n w.colorSaturation = 1;\n c.grain = 0.35;\n c.background = \"#f2efe8\";\n c.transparentBackground = true;\n return c;\n },\n Spain: () => {\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.paletteSource = \"spain\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.18;\n w.colorSaturation = 1.25;\n w.creaseLight = 1.6; // moderate crease-light: rich crimson without washing to salmon (Hero's is 1.98)\n c.grain = 0.3;\n c.background = \"#1a0608\"; // deep oxblood stage\n c.backgroundMode = \"color\";\n c.transparentBackground = false; // opaque, so the dark stage makes the flag pop\n return c;\n },\n \"Vaporwave Sunset\": () => {\n // The Hero wave re-posed/re-framed, plus the vaporwave palette.\n const c = PRESETS[\"Hero\"](); // the centred default \"Hero\" wave\n const w = c.waves[0];\n w.position.x = 525;\n w.rotation.x = -0.64 * RAD;\n w.rotation.z = 1.68 * RAD;\n w.paletteSource = \"vaporwave\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.08;\n w.colorSaturation = 1.15;\n w.creaseLight = 1.25;\n c.cameraZoom = 1.1;\n c.cameraTarget = { x: 150, y: 360, z: 0 };\n c.background = \"#09051f\";\n c.transparentBackground = false;\n return c;\n },\n Kaleidoscope: () => {\n const c = PRESETS[\"Wave 3\"]();\n const w = c.waves[0];\n w.paletteSource = \"kaleidoscope\";\n w.blendMode = \"normal\";\n w.hueShift = 0;\n w.colorContrast = 1.05;\n w.colorSaturation = 1.12;\n c.grain = 0.5;\n return c;\n },\n};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StudioConfig, WaveConfig } from "../config/model.js";
|
|
2
2
|
import { WaveGeometry } from "./WaveGeometry.js";
|
|
3
|
+
import { InteractionController } from "./interaction.js";
|
|
3
4
|
import * as THREE from "three";
|
|
4
5
|
|
|
5
6
|
//#region src/renderer/WaveRenderer.d.ts
|
|
@@ -91,6 +92,15 @@ declare class WaveRenderer {
|
|
|
91
92
|
private readonly clipSphere;
|
|
92
93
|
private readonly clipTmpA;
|
|
93
94
|
private readonly clipTmpB;
|
|
95
|
+
/** Created by syncInteraction() when interaction turns on, disposed when it turns off. */
|
|
96
|
+
protected interaction?: InteractionController;
|
|
97
|
+
/** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().
|
|
98
|
+
* Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */
|
|
99
|
+
protected interactionZoom: number;
|
|
100
|
+
/** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */
|
|
101
|
+
private interactionTimeOffset;
|
|
102
|
+
/** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */
|
|
103
|
+
private readonly interactionSceneOut;
|
|
94
104
|
private readonly timer;
|
|
95
105
|
private time;
|
|
96
106
|
private rafId;
|
|
@@ -179,6 +189,30 @@ declare class WaveRenderer {
|
|
|
179
189
|
private updateTime;
|
|
180
190
|
/** Render exactly one frame at the current time. */
|
|
181
191
|
renderOnce(): void;
|
|
192
|
+
/** Create/dispose the interaction controller as config toggles interaction on/off. Called from
|
|
193
|
+
* refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */
|
|
194
|
+
private syncInteraction;
|
|
195
|
+
/** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a
|
|
196
|
+
* controller. While capturing it writes the REST state instead (pointer field zeroed, every bound
|
|
197
|
+
* param at its authored base) — merely skipping the write would freeze whatever live hover/scroll
|
|
198
|
+
* state the previous frame left in the uniforms, so exports wouldn't be deterministic. */
|
|
199
|
+
private applyInteraction;
|
|
200
|
+
/** Write the capture-frame interaction state: exactly what this config renders with no input —
|
|
201
|
+
* pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each
|
|
202
|
+
* bound param at its authored base. Live controller state is left untouched, so the frame after
|
|
203
|
+
* the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.
|
|
204
|
+
* interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and
|
|
205
|
+
* the post-capture restore depends on it being unchanged. */
|
|
206
|
+
private applyInteractionRest;
|
|
207
|
+
/** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /
|
|
208
|
+
* presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages
|
|
209
|
+
* are shared. Per-wave amplitudes were already pushed statically in refresh(). */
|
|
210
|
+
private applyPointerField;
|
|
211
|
+
/** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene
|
|
212
|
+
* bindings drive scene params; each wave's bindings drive that wave's uniforms. */
|
|
213
|
+
private applyBindings;
|
|
214
|
+
/** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off. */
|
|
215
|
+
setInteractionInput(name: string, value: number): void;
|
|
182
216
|
/** Re-evaluate play/pause after `config.paused` changes. */
|
|
183
217
|
refreshPlayback(): void;
|
|
184
218
|
/** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureStudioConfig } from "../config/model.js";
|
|
2
2
|
import { fragmentShader, lineFragmentShader, postFragmentShader, postVertexShader, vertexShader } from "./shaders.js";
|
|
3
3
|
import { WaveGeometry } from "./WaveGeometry.js";
|
|
4
|
+
import { InteractionController, SCENE_APPLIERS, WAVE_APPLIERS, anyPointerFxActive, interactionActive, wavePointerFxActive, waveRipplesActive } from "./interaction.js";
|
|
4
5
|
import { PALETTE_MAPS, buildBackgroundGradientCanvas, buildBackgroundImageCanvas, buildBackgroundMeshCanvas, buildPaletteTexture, canvasToTexture, configurePaletteTexture, drawBackgroundMediaFrame, loadPaletteImage, paletteMapCanvas, paletteSignature } from "./palette.js";
|
|
5
6
|
import { buildHeroPaletteCanvas, buildHeroPaletteTexture } from "./heroPalette.js";
|
|
6
7
|
import * as THREE from "three";
|
|
@@ -169,6 +170,18 @@ var WaveRenderer = class {
|
|
|
169
170
|
clipSphere = new THREE.Sphere();
|
|
170
171
|
clipTmpA = new THREE.Vector3();
|
|
171
172
|
clipTmpB = new THREE.Vector3();
|
|
173
|
+
/** Created by syncInteraction() when interaction turns on, disposed when it turns off. */
|
|
174
|
+
interaction;
|
|
175
|
+
/** Extra ortho-zoom MULTIPLIER from a cameraZoom binding (1 = none); applied in applyZoom().
|
|
176
|
+
* Protected so the studio's writeCameraToConfig() can divide it back out (keep it out of config). */
|
|
177
|
+
interactionZoom = 1;
|
|
178
|
+
/** Extra time-offset DELTA from a timeOffset binding (0 = none); added in updateTime(). */
|
|
179
|
+
interactionTimeOffset = 0;
|
|
180
|
+
/** Scene-binding out-params: appliers write into this, applyBindings() reads it back. */
|
|
181
|
+
interactionSceneOut = {
|
|
182
|
+
timeOffset: 0,
|
|
183
|
+
zoom: 1
|
|
184
|
+
};
|
|
172
185
|
timer = new THREE.Timer();
|
|
173
186
|
time = 0;
|
|
174
187
|
rafId = 0;
|
|
@@ -276,6 +289,14 @@ var WaveRenderer = class {
|
|
|
276
289
|
bandParams.push(new THREE.Vector4());
|
|
277
290
|
bandParaPow.push(0);
|
|
278
291
|
}
|
|
292
|
+
const rippleOrigin = [];
|
|
293
|
+
const rippleAge = [];
|
|
294
|
+
const rippleAmp = [];
|
|
295
|
+
for (let i = 0; i < 4; i++) {
|
|
296
|
+
rippleOrigin.push(new THREE.Vector2());
|
|
297
|
+
rippleAge.push(0);
|
|
298
|
+
rippleAmp.push(0);
|
|
299
|
+
}
|
|
279
300
|
return {
|
|
280
301
|
uTime: { value: 0 },
|
|
281
302
|
uSpeed: { value: .05 },
|
|
@@ -342,7 +363,22 @@ var WaveRenderer = class {
|
|
|
342
363
|
uLineThickness: { value: 1 },
|
|
343
364
|
uLineDerivativePower: { value: .95 },
|
|
344
365
|
uMaxWidth: { value: 1232 },
|
|
345
|
-
uClearColor: { value: new THREE.Vector3(1, 1, 1) }
|
|
366
|
+
uClearColor: { value: new THREE.Vector3(1, 1, 1) },
|
|
367
|
+
uPointer: { value: new THREE.Vector2(0, 0) },
|
|
368
|
+
uPointerActive: { value: 0 },
|
|
369
|
+
uPointerRadius: { value: .6 },
|
|
370
|
+
uPointerAspect: { value: 1 },
|
|
371
|
+
uPointerAgitate: { value: 0 },
|
|
372
|
+
uPointerPush: { value: 0 },
|
|
373
|
+
uPointerWake: { value: 0 },
|
|
374
|
+
uPointerVel: { value: new THREE.Vector2(0, 0) },
|
|
375
|
+
uPointerThin: { value: 0 },
|
|
376
|
+
uPointerHue: { value: 0 },
|
|
377
|
+
uPointerLighten: { value: 0 },
|
|
378
|
+
uPointerRipple: { value: 0 },
|
|
379
|
+
uRippleOrigin: { value: rippleOrigin },
|
|
380
|
+
uRippleAge: { value: rippleAge },
|
|
381
|
+
uRippleAmp: { value: rippleAmp }
|
|
346
382
|
};
|
|
347
383
|
}
|
|
348
384
|
/** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and
|
|
@@ -352,9 +388,14 @@ var WaveRenderer = class {
|
|
|
352
388
|
const defines = {};
|
|
353
389
|
if (sc?.twistMotion) defines.TWIST_MOTION = "";
|
|
354
390
|
if ((this.config.loopSeconds ?? 0) > 0) defines.LOOP_MOTION = "";
|
|
355
|
-
|
|
391
|
+
const bindsDetail = sc?.interaction?.bindings?.some((b) => b.target === "detailAmount") ?? false;
|
|
392
|
+
if ((sc?.detailAmount ?? 0) !== 0 || bindsDetail) defines.DETAIL_OCTAVE = "";
|
|
356
393
|
if ((sc?.depthTint ?? 0) > 0) defines.DEPTH_TINT = "";
|
|
357
394
|
if ((sc?.edgeFeather ?? .1) !== .1) defines.EDGE_FEATHER = "";
|
|
395
|
+
if (sc && wavePointerFxActive(this.config, sc)) {
|
|
396
|
+
defines.POINTER_FX = "";
|
|
397
|
+
if (waveRipplesActive(this.config, sc)) defines.POINTER_RIPPLES = "";
|
|
398
|
+
}
|
|
358
399
|
return defines;
|
|
359
400
|
}
|
|
360
401
|
addWave() {
|
|
@@ -442,6 +483,7 @@ var WaveRenderer = class {
|
|
|
442
483
|
refresh() {
|
|
443
484
|
this.applyBackground();
|
|
444
485
|
this.applyPost();
|
|
486
|
+
this.syncInteraction();
|
|
445
487
|
if (!this.isCameraExternallyDriven()) {
|
|
446
488
|
const p = this.config.cameraPosition;
|
|
447
489
|
const tg = this.config.cameraTarget;
|
|
@@ -562,6 +604,21 @@ var WaveRenderer = class {
|
|
|
562
604
|
wave.mesh.position.set(sc.position.x, sc.position.y, sc.position.z);
|
|
563
605
|
u.uOpacity.value = sc.opacity;
|
|
564
606
|
});
|
|
607
|
+
const sharedRadius = (this.config.interaction?.radius ?? .3) * 2;
|
|
608
|
+
this.waves.forEach((wave, i) => {
|
|
609
|
+
const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];
|
|
610
|
+
if (!wavePointerFxActive(this.config, sc)) return;
|
|
611
|
+
const u = wave.material.uniforms;
|
|
612
|
+
const h = sc.interaction?.hover;
|
|
613
|
+
u.uPointerRadius.value = sharedRadius;
|
|
614
|
+
u.uPointerAgitate.value = h?.agitate ?? 0;
|
|
615
|
+
u.uPointerPush.value = h?.push ?? 0;
|
|
616
|
+
u.uPointerWake.value = h?.wake ?? 0;
|
|
617
|
+
u.uPointerThin.value = h?.thin ?? 0;
|
|
618
|
+
u.uPointerHue.value = h?.hueShift ?? 0;
|
|
619
|
+
u.uPointerLighten.value = h?.lighten ?? 0;
|
|
620
|
+
u.uPointerRipple.value = sc.interaction?.press?.ripple ?? 0;
|
|
621
|
+
});
|
|
565
622
|
this.updatePaletteTextures();
|
|
566
623
|
this.syncVideoPlayback();
|
|
567
624
|
this.group.scale.set(this.config.mirrorH ? -1 : 1, this.config.mirrorV ? -1 : 1, 1);
|
|
@@ -933,6 +990,7 @@ var WaveRenderer = class {
|
|
|
933
990
|
}
|
|
934
991
|
if (!this.running) {
|
|
935
992
|
this.introTimeRamp = 1;
|
|
993
|
+
this.interaction?.settle();
|
|
936
994
|
this.renderOnce();
|
|
937
995
|
}
|
|
938
996
|
this.syncVideoPlayback();
|
|
@@ -940,7 +998,9 @@ var WaveRenderer = class {
|
|
|
940
998
|
loop = () => {
|
|
941
999
|
if (!this.running) return;
|
|
942
1000
|
this.timer.update();
|
|
943
|
-
|
|
1001
|
+
const dt = this.timer.getDelta();
|
|
1002
|
+
this.time += dt;
|
|
1003
|
+
this.interaction?.update(dt);
|
|
944
1004
|
if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + .016);
|
|
945
1005
|
this.renderOnce();
|
|
946
1006
|
this.rafId = requestAnimationFrame(this.loop);
|
|
@@ -949,7 +1009,7 @@ var WaveRenderer = class {
|
|
|
949
1009
|
* time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */
|
|
950
1010
|
updateTime() {
|
|
951
1011
|
const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;
|
|
952
|
-
const t = this.time * ramp + (this.config.timeOffset ?? 0);
|
|
1012
|
+
const t = this.time * ramp + (this.config.timeOffset ?? 0) + this.interactionTimeOffset;
|
|
953
1013
|
for (let i = 0; i < this.waves.length; i++) {
|
|
954
1014
|
const u = this.waves[i].material.uniforms;
|
|
955
1015
|
u.uTime.value = t;
|
|
@@ -967,10 +1027,156 @@ var WaveRenderer = class {
|
|
|
967
1027
|
renderOnce() {
|
|
968
1028
|
this.updateBackgroundVideoFrame();
|
|
969
1029
|
this.updateTime();
|
|
1030
|
+
this.applyInteraction();
|
|
970
1031
|
this.updateClipPlanes();
|
|
971
1032
|
this.composer.render();
|
|
972
1033
|
this.onAfterRenderFrame();
|
|
973
1034
|
}
|
|
1035
|
+
/** Create/dispose the interaction controller as config toggles interaction on/off. Called from
|
|
1036
|
+
* refresh(); the compiled define set (POINTER_FX etc.) is handled separately by waveDefines(). */
|
|
1037
|
+
syncInteraction() {
|
|
1038
|
+
const active = interactionActive(this.config);
|
|
1039
|
+
if (active && !this.interaction) this.interaction = new InteractionController(this.container, () => this.config);
|
|
1040
|
+
else if (!active && this.interaction) {
|
|
1041
|
+
this.interaction.dispose();
|
|
1042
|
+
this.interaction = void 0;
|
|
1043
|
+
this.interactionTimeOffset = 0;
|
|
1044
|
+
if (this.interactionZoom !== 1) {
|
|
1045
|
+
this.interactionZoom = 1;
|
|
1046
|
+
this.applyZoom();
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
/** Per-frame interaction write: dynamic pointer-field uniforms + bindings. No-op without a
|
|
1051
|
+
* controller. While capturing it writes the REST state instead (pointer field zeroed, every bound
|
|
1052
|
+
* param at its authored base) — merely skipping the write would freeze whatever live hover/scroll
|
|
1053
|
+
* state the previous frame left in the uniforms, so exports wouldn't be deterministic. */
|
|
1054
|
+
applyInteraction() {
|
|
1055
|
+
if (!this.interaction) return;
|
|
1056
|
+
if (this.capturing) {
|
|
1057
|
+
this.applyInteractionRest();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (anyPointerFxActive(this.config)) this.applyPointerField(this.interaction);
|
|
1061
|
+
this.applyBindings(this.interaction);
|
|
1062
|
+
}
|
|
1063
|
+
/** Write the capture-frame interaction state: exactly what this config renders with no input —
|
|
1064
|
+
* pointer presence + ripple envelopes zeroed (vPointerFall gates every hover effect to 0) and each
|
|
1065
|
+
* bound param at its authored base. Live controller state is left untouched, so the frame after
|
|
1066
|
+
* the capture resumes mid-gesture; the trailing renderOnce() in captureImage restores the preview.
|
|
1067
|
+
* interactionZoom is deliberately NOT reset — captureImage strips it from camera.zoom itself, and
|
|
1068
|
+
* the post-capture restore depends on it being unchanged. */
|
|
1069
|
+
applyInteractionRest() {
|
|
1070
|
+
for (let i = 0; i < this.waves.length; i++) {
|
|
1071
|
+
const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];
|
|
1072
|
+
if (!wavePointerFxActive(this.config, sc)) continue;
|
|
1073
|
+
const u = this.waves[i].material.uniforms;
|
|
1074
|
+
u.uPointerActive.value = 0;
|
|
1075
|
+
const rAmp = u.uRippleAmp.value;
|
|
1076
|
+
for (let r = 0; r < 4; r++) rAmp[r] = 0;
|
|
1077
|
+
}
|
|
1078
|
+
const sceneBindings = this.config.interaction?.bindings;
|
|
1079
|
+
if (sceneBindings) {
|
|
1080
|
+
this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;
|
|
1081
|
+
this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;
|
|
1082
|
+
const sceneArgs = {
|
|
1083
|
+
post: this.postPass.uniforms,
|
|
1084
|
+
out: this.interactionSceneOut
|
|
1085
|
+
};
|
|
1086
|
+
for (const b of sceneBindings) {
|
|
1087
|
+
const applier = SCENE_APPLIERS[b.target];
|
|
1088
|
+
applier.apply(applier.base(this.config), sceneArgs);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (this.interactionTimeOffset !== 0) {
|
|
1092
|
+
this.interactionTimeOffset = 0;
|
|
1093
|
+
this.updateTime();
|
|
1094
|
+
}
|
|
1095
|
+
for (let i = 0; i < this.waves.length; i++) {
|
|
1096
|
+
const sc = this.config.waves[i];
|
|
1097
|
+
const bindings = sc?.interaction?.bindings;
|
|
1098
|
+
if (!sc || !bindings || bindings.length === 0) continue;
|
|
1099
|
+
const wave = this.waves[i];
|
|
1100
|
+
for (const b of bindings) {
|
|
1101
|
+
const applier = WAVE_APPLIERS[b.target];
|
|
1102
|
+
applier.apply(applier.base(sc), {
|
|
1103
|
+
u: wave.material.uniforms,
|
|
1104
|
+
mesh: wave.mesh
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/** Write the dynamic pointer-field uniforms to every wave that HAS a pointer field. Position /
|
|
1110
|
+
* presence are PER WAVE (each trails the cursor at its own hover smoothing); ripple origins/ages
|
|
1111
|
+
* are shared. Per-wave amplitudes were already pushed statically in refresh(). */
|
|
1112
|
+
applyPointerField(ic) {
|
|
1113
|
+
const dw = this.camera.right - this.camera.left;
|
|
1114
|
+
const dh = this.camera.top - this.camera.bottom;
|
|
1115
|
+
const aspect = dh !== 0 ? dw / dh : 1;
|
|
1116
|
+
const ripples = ic.sample().ripples;
|
|
1117
|
+
const agitateDrive = Math.min(1, .2 + .85 * ic.pointerFlux());
|
|
1118
|
+
const vel = ic.pointerVelocity();
|
|
1119
|
+
for (let i = 0; i < this.waves.length; i++) {
|
|
1120
|
+
const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];
|
|
1121
|
+
if (!wavePointerFxActive(this.config, sc)) continue;
|
|
1122
|
+
const u = this.waves[i].material.uniforms;
|
|
1123
|
+
u.uPointerAgitate.value = (sc.interaction?.hover?.agitate ?? 0) * agitateDrive;
|
|
1124
|
+
u.uPointerVel.value.copy(vel);
|
|
1125
|
+
const f = ic.fieldFor(i);
|
|
1126
|
+
if (f) {
|
|
1127
|
+
u.uPointer.value.copy(f.ndc);
|
|
1128
|
+
u.uPointerActive.value = f.presence;
|
|
1129
|
+
} else u.uPointerActive.value = 0;
|
|
1130
|
+
u.uPointerAspect.value = aspect;
|
|
1131
|
+
const rOrigin = u.uRippleOrigin.value;
|
|
1132
|
+
const rAge = u.uRippleAge.value;
|
|
1133
|
+
const rAmp = u.uRippleAmp.value;
|
|
1134
|
+
for (let r = 0; r < 4; r++) {
|
|
1135
|
+
rOrigin[r].copy(ripples[r].origin);
|
|
1136
|
+
rAge[r] = ripples[r].age;
|
|
1137
|
+
rAmp[r] = ripples[r].amp;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
/** Evaluate bindings via the applier tables: value = mix(from ?? base, to, smoothedSource). Scene
|
|
1142
|
+
* bindings drive scene params; each wave's bindings drive that wave's uniforms. */
|
|
1143
|
+
applyBindings(ic) {
|
|
1144
|
+
this.interactionSceneOut.timeOffset = this.config.timeOffset ?? 0;
|
|
1145
|
+
this.interactionSceneOut.zoom = this.config.cameraZoom ?? 1;
|
|
1146
|
+
const sceneArgs = {
|
|
1147
|
+
post: this.postPass.uniforms,
|
|
1148
|
+
out: this.interactionSceneOut
|
|
1149
|
+
};
|
|
1150
|
+
for (const b of this.config.interaction?.bindings ?? []) {
|
|
1151
|
+
const applier = SCENE_APPLIERS[b.target];
|
|
1152
|
+
const value = THREE.MathUtils.lerp(b.from ?? applier.base(this.config), b.to, ic.bindingValue(b));
|
|
1153
|
+
applier.apply(value, sceneArgs);
|
|
1154
|
+
}
|
|
1155
|
+
for (let i = 0; i < this.waves.length; i++) {
|
|
1156
|
+
const sc = this.config.waves[i];
|
|
1157
|
+
const bindings = sc?.interaction?.bindings;
|
|
1158
|
+
if (!sc || !bindings || bindings.length === 0) continue;
|
|
1159
|
+
const wave = this.waves[i];
|
|
1160
|
+
for (const b of bindings) {
|
|
1161
|
+
const applier = WAVE_APPLIERS[b.target];
|
|
1162
|
+
const value = THREE.MathUtils.lerp(b.from ?? applier.base(sc), b.to, ic.bindingValue(b));
|
|
1163
|
+
applier.apply(value, {
|
|
1164
|
+
u: wave.material.uniforms,
|
|
1165
|
+
mesh: wave.mesh
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
this.interactionTimeOffset = this.interactionSceneOut.timeOffset - (this.config.timeOffset ?? 0);
|
|
1170
|
+
const nextZoom = this.interactionSceneOut.zoom / (this.config.cameraZoom || 1);
|
|
1171
|
+
if (nextZoom !== this.interactionZoom) {
|
|
1172
|
+
this.interactionZoom = nextZoom;
|
|
1173
|
+
this.applyZoom();
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
/** Feed a `custom:<name>` interaction input (developer API). No-op when interaction is off. */
|
|
1177
|
+
setInteractionInput(name, value) {
|
|
1178
|
+
this.interaction?.setInput(name, value);
|
|
1179
|
+
}
|
|
974
1180
|
/** Re-evaluate play/pause after `config.paused` changes. */
|
|
975
1181
|
refreshPlayback() {
|
|
976
1182
|
this.updateRunning();
|
|
@@ -1005,7 +1211,7 @@ var WaveRenderer = class {
|
|
|
1005
1211
|
applyZoom() {
|
|
1006
1212
|
const dw = this.camera.right - this.camera.left;
|
|
1007
1213
|
const dh = this.camera.top - this.camera.bottom;
|
|
1008
|
-
this.camera.zoom = Math.max(dw / FRAME_W, dh / 750) * (this.config.cameraZoom ?? 1);
|
|
1214
|
+
this.camera.zoom = Math.max(dw / FRAME_W, dh / 750) * (this.config.cameraZoom ?? 1) * this.interactionZoom;
|
|
1009
1215
|
this.camera.updateProjectionMatrix();
|
|
1010
1216
|
}
|
|
1011
1217
|
/** Fit the orthographic near/far planes to the scene before every render, so no part of a wave
|
|
@@ -1025,14 +1231,21 @@ var WaveRenderer = class {
|
|
|
1025
1231
|
* it's a handful of vector ops over 1–8 meshes with no allocation. */
|
|
1026
1232
|
updateClipPlanes() {
|
|
1027
1233
|
this.clipBox.makeEmpty();
|
|
1028
|
-
for (
|
|
1234
|
+
for (let i = 0; i < this.waves.length; i++) {
|
|
1235
|
+
const wave = this.waves[i];
|
|
1029
1236
|
const mesh = wave.mesh;
|
|
1030
1237
|
mesh.updateWorldMatrix(true, false);
|
|
1031
1238
|
const bs = mesh.geometry.boundingSphere;
|
|
1032
1239
|
if (!bs) continue;
|
|
1033
1240
|
const center = this.clipTmpA.setFromMatrixPosition(mesh.matrixWorld);
|
|
1034
1241
|
const disp = Math.abs(Number(wave.material.uniforms.uDispAmount.value) || 0);
|
|
1035
|
-
const
|
|
1242
|
+
const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];
|
|
1243
|
+
let pointerDisp = 0;
|
|
1244
|
+
if (wavePointerFxActive(this.config, sc)) {
|
|
1245
|
+
const h = sc.interaction?.hover;
|
|
1246
|
+
pointerDisp = (h?.agitate ?? 0) + Math.abs(h?.push ?? 0) + (h?.wake ?? 0) + (sc.interaction?.press?.ripple ?? 0);
|
|
1247
|
+
}
|
|
1248
|
+
const radius = (bs.center.length() + bs.radius + disp + pointerDisp) * mesh.matrixWorld.getMaxScaleOnAxis() * 1.2;
|
|
1036
1249
|
this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(radius));
|
|
1037
1250
|
this.clipBox.expandByPoint(this.clipTmpB.copy(center).addScalar(-radius));
|
|
1038
1251
|
}
|
|
@@ -1065,12 +1278,22 @@ var WaveRenderer = class {
|
|
|
1065
1278
|
this.introTimeRamp = 1;
|
|
1066
1279
|
}
|
|
1067
1280
|
let blob = null;
|
|
1281
|
+
const prevZoom = this.camera.zoom;
|
|
1282
|
+
const strippingZoom = this.interactionZoom !== 1;
|
|
1068
1283
|
try {
|
|
1069
1284
|
this.capturing = true;
|
|
1285
|
+
if (strippingZoom) {
|
|
1286
|
+
this.camera.zoom = prevZoom / this.interactionZoom;
|
|
1287
|
+
this.camera.updateProjectionMatrix();
|
|
1288
|
+
}
|
|
1070
1289
|
this.renderOnce();
|
|
1071
1290
|
blob = await new Promise((resolve) => this.canvas.toBlob(resolve, mime, quality));
|
|
1072
1291
|
} finally {
|
|
1073
1292
|
this.capturing = false;
|
|
1293
|
+
if (strippingZoom) {
|
|
1294
|
+
this.camera.zoom = prevZoom;
|
|
1295
|
+
this.camera.updateProjectionMatrix();
|
|
1296
|
+
}
|
|
1074
1297
|
if (transparent !== prevBg) {
|
|
1075
1298
|
this.config.transparentBackground = prevBg;
|
|
1076
1299
|
this.applyBackground();
|
|
@@ -1104,6 +1327,8 @@ var WaveRenderer = class {
|
|
|
1104
1327
|
dispose() {
|
|
1105
1328
|
cancelAnimationFrame(this.rafId);
|
|
1106
1329
|
this.running = false;
|
|
1330
|
+
this.interaction?.dispose();
|
|
1331
|
+
this.interaction = void 0;
|
|
1107
1332
|
this.resizeObserver.disconnect();
|
|
1108
1333
|
this.intersectionObserver.disconnect();
|
|
1109
1334
|
this.motionQuery.removeEventListener("change", this.onMotionChange);
|