@wave3d/core 0.1.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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/config/model.d.ts +240 -0
- package/dist/config/model.js +496 -0
- package/dist/config/model.js.map +1 -0
- package/dist/core-loader.d.ts +10 -0
- package/dist/core-loader.js +12 -0
- package/dist/core-loader.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/presets.d.ts +8 -0
- package/dist/presets.js +544 -0
- package/dist/presets.js.map +1 -0
- package/dist/renderer/WaveGeometry.d.ts +31 -0
- package/dist/renderer/WaveGeometry.js +92 -0
- package/dist/renderer/WaveGeometry.js.map +1 -0
- package/dist/renderer/WaveRenderer.d.ts +232 -0
- package/dist/renderer/WaveRenderer.js +1118 -0
- package/dist/renderer/WaveRenderer.js.map +1 -0
- package/dist/renderer/heroPalette.d.ts +10 -0
- package/dist/renderer/heroPalette.js +34 -0
- package/dist/renderer/heroPalette.js.map +1 -0
- package/dist/renderer/index.d.ts +4 -0
- package/dist/renderer/index.js +4 -0
- package/dist/renderer/palette.d.ts +67 -0
- package/dist/renderer/palette.js +535 -0
- package/dist/renderer/palette.js.map +1 -0
- package/dist/renderer/shaders.js +545 -0
- package/dist/renderer/shaders.js.map +1 -0
- package/dist/shell/createWave.d.ts +58 -0
- package/dist/shell/createWave.js +161 -0
- package/dist/shell/createWave.js.map +1 -0
- package/dist/shell/poster.js +64 -0
- package/dist/shell/poster.js.map +1 -0
- package/dist/shell/probe.js +34 -0
- package/dist/shell/probe.js.map +1 -0
- package/dist/standalone/wave3d.standalone.js +13507 -0
- package/dist/standalone.d.ts +13 -0
- package/dist/standalone.js +17 -0
- package/dist/standalone.js.map +1 -0
- package/dist/studio/StudioWaveRenderer.d.ts +187 -0
- package/dist/studio/StudioWaveRenderer.js +905 -0
- package/dist/studio/StudioWaveRenderer.js.map +1 -0
- package/dist/studio/index.d.ts +3 -0
- package/dist/studio/index.js +3 -0
- package/dist/studio/randomize.d.ts +28 -0
- package/dist/studio/randomize.js +264 -0
- package/dist/studio/randomize.js.map +1 -0
- package/dist/util/base64.js +14 -0
- package/dist/util/base64.js.map +1 -0
- package/dist/util/math.js +18 -0
- package/dist/util/math.js.map +1 -0
- package/package.json +76 -0
- package/skills/wave3d/SKILL.md +158 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WaveGeometry.js","names":[],"sources":["../../src/renderer/WaveGeometry.ts"],"sourcesContent":["import * as THREE from \"three\";\n\n/** Native plane size for folded() — keep this exact (400) so the vertex\n * shader's displace/twist frequencies (calibrated to this scale) stay faithful. */\nconst NATIVE = 400;\nconst FOLD_X = 16; // |x| < 16 is the semicircular hinge; outside it the two flat arms\nconst SHIFT = NATIVE / 4; // recentre the folded cross-section along x\n\nconst X_AXIS = new THREE.Vector3(1, 0, 0);\nconst Y_AXIS = new THREE.Vector3(0, 1, 0);\n\n/**\n * Base wave geometry — `folded()`: a flat PlaneGeometry folded into a hairpin\n * (sideways-U) cross-section, then stood up so the fold runs along the wave's length.\n *\n * - Each vertex gets a half-thickness `r` (per-vertex math below): tight along the\n * width centreline, flaring toward the long edges.\n * - The strip |x| < FOLD_X becomes a semicircular hinge; the plane's two halves bend\n * around it into parallel arms offset to +r and -r.\n * - Two −90° rotations (about X then Y) orient the U upright and down its length.\n *\n * folded() leaves the U open along one side and hollow at both ends, so at oblique\n * camera angles you could see straight through it. We weld the open side and cap both\n * ends with extra triangles so the mesh is a watertight solid — welding/capping adds\n * faces only, no vertex positions move.\n *\n * All further deformation (displacement, twist, transform) happens in the vertex shader\n * on top of this base. UVs: u along the fold/length, v across the width.\n */\nexport class WaveGeometry {\n readonly geometry: THREE.BufferGeometry;\n private segments = -1;\n\n constructor(segments: number) {\n this.geometry = new THREE.BufferGeometry();\n this.resize(segments);\n }\n\n resize(segments: number): void {\n if (segments === this.segments) return;\n this.segments = segments;\n\n // subX along the fold, subY across the width (twice as dense).\n const subX = THREE.MathUtils.clamp(Math.round(segments), 48, 200);\n const subY = subX * 2;\n\n const plane = new THREE.PlaneGeometry(NATIVE, NATIVE, subX, subY);\n const pos = plane.attributes.position as THREE.BufferAttribute;\n const uv = plane.attributes.uv as THREE.BufferAttribute;\n const v = new THREE.Vector3();\n\n for (let i = 0; i < pos.count; i++) {\n v.fromBufferAttribute(pos, i);\n const uy = uv.getY(i);\n // r: cross-section half-thickness — tight (2) along the width centreline, flaring (4)\n // toward the long edges. The pow() term is a sharp parabolic bump peaking at uv.y = 0.5.\n const r = 4 - 2 * Math.pow(4 * uy * (1 - uy), 9.5);\n\n if (v.x < -FOLD_X) {\n v.z += r; // long arm, at +r\n } else if (v.x < FOLD_X) {\n // semicircular hinge: z sweeps +r → -r, x collapses to the bend\n v.z = Math.cos(THREE.MathUtils.mapLinear(v.x, -FOLD_X, FOLD_X, 0, Math.PI)) * r;\n v.x =\n Math.cos(THREE.MathUtils.mapLinear(v.x, -FOLD_X, FOLD_X, -Math.PI / 2, Math.PI / 2)) * r -\n FOLD_X;\n } else {\n v.z -= r; // folded-over arm, mirrored back at -r\n v.x = -v.x;\n }\n\n v.x += SHIFT;\n v.applyAxisAngle(X_AXIS, -Math.PI / 2);\n v.applyAxisAngle(Y_AXIS, -Math.PI / 2);\n pos.setXYZ(i, v.x, v.y, v.z);\n }\n pos.needsUpdate = true;\n\n // Seal the hairpin's OPEN side. folded() leaves the two arm tips unconnected — the\n // plane's u=0 and u=subX edges, which fold to adjacent tips at +r and -r — so at oblique\n // camera angles you can see through the U to the background. Weld those two edges with a\n // strip of triangles, closing the tube. No vertex positions move; this only adds faces\n // over the previously-open seam.\n const cols = subX + 1;\n const srcIdx = plane.getIndex();\n const merged = srcIdx ? Array.from(srcIdx.array as ArrayLike<number>) : [];\n // (a) Weld the U's side opening: the u=0 and u=subX edges fold to adjacent tips at ±r.\n for (let iy = 0; iy < subY; iy++) {\n const a = iy * cols; // (row iy, col 0) — arm-A tip\n const b = (iy + 1) * cols; // (row iy+1, col 0)\n const c = a + subX; // (row iy, col subX) — arm-B tip\n const d = b + subX; // (row iy+1, col subX)\n merged.push(a, c, b, b, c, d);\n }\n // (b) Cap the two length-ends (v=0 and v=subX rows): the folded sheet is a hollow channel\n // open at both ends, so an edge-on camera sees straight through it. Fan-triangulate each\n // end's U cross-section (apex = the col-0 tip) to close it — making the wave a closed solid.\n for (const row of [0, subY]) {\n const apex = row * cols;\n for (let ix = 1; ix < subX; ix++) merged.push(apex, row * cols + ix, row * cols + ix + 1);\n }\n plane.setIndex(merged);\n\n plane.computeVertexNormals();\n\n // Move the baked attributes onto our reusable geometry, then drop the temp.\n this.geometry.setIndex(plane.getIndex());\n this.geometry.setAttribute(\"position\", plane.getAttribute(\"position\"));\n this.geometry.setAttribute(\"uv\", plane.getAttribute(\"uv\"));\n this.geometry.setAttribute(\"normal\", plane.getAttribute(\"normal\"));\n this.geometry.computeBoundingSphere();\n plane.dispose();\n }\n\n dispose(): void {\n this.geometry.dispose();\n }\n}\n"],"mappings":";;;;AAIA,MAAM,SAAS;AACf,MAAM,SAAS;AACf,MAAM,QAAQ,SAAS;AAEvB,MAAM,SAAS,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AACxC,MAAM,SAAS,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;;;;;;;;;;;;;;;;;;;AAoBxC,IAAa,eAAb,MAA0B;CACxB;CACA,WAAmB;CAEnB,YAAY,UAAkB;EAC5B,KAAK,WAAW,IAAI,MAAM,eAAe;EACzC,KAAK,OAAO,QAAQ;CACtB;CAEA,OAAO,UAAwB;EAC7B,IAAI,aAAa,KAAK,UAAU;EAChC,KAAK,WAAW;EAGhB,MAAM,OAAO,MAAM,UAAU,MAAM,KAAK,MAAM,QAAQ,GAAG,IAAI,GAAG;EAChE,MAAM,OAAO,OAAO;EAEpB,MAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,QAAQ,MAAM,IAAI;EAChE,MAAM,MAAM,MAAM,WAAW;EAC7B,MAAM,KAAK,MAAM,WAAW;EAC5B,MAAM,IAAI,IAAI,MAAM,QAAQ;EAE5B,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;GAClC,EAAE,oBAAoB,KAAK,CAAC;GAC5B,MAAM,KAAK,GAAG,KAAK,CAAC;GAGpB,MAAM,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,GAAG;GAEjD,IAAI,EAAE,IAAI,KACR,EAAE,KAAK;QACF,IAAI,EAAE,IAAI,QAAQ;IAEvB,EAAE,IAAI,KAAK,IAAI,MAAM,UAAU,UAAU,EAAE,GAAG,KAAS,QAAQ,GAAG,KAAK,EAAE,CAAC,IAAI;IAC9E,EAAE,IACA,KAAK,IAAI,MAAM,UAAU,UAAU,EAAE,GAAG,KAAS,QAAQ,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,IACvF;GACJ,OAAO;IACL,EAAE,KAAK;IACP,EAAE,IAAI,CAAC,EAAE;GACX;GAEA,EAAE,KAAK;GACP,EAAE,eAAe,QAAQ,CAAC,KAAK,KAAK,CAAC;GACrC,EAAE,eAAe,QAAQ,CAAC,KAAK,KAAK,CAAC;GACrC,IAAI,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;EAC7B;EACA,IAAI,cAAc;EAOlB,MAAM,OAAO,OAAO;EACpB,MAAM,SAAS,MAAM,SAAS;EAC9B,MAAM,SAAS,SAAS,MAAM,KAAK,OAAO,KAA0B,IAAI,CAAC;EAEzE,KAAK,IAAI,KAAK,GAAG,KAAK,MAAM,MAAM;GAChC,MAAM,IAAI,KAAK;GACf,MAAM,KAAK,KAAK,KAAK;GACrB,MAAM,IAAI,IAAI;GACd,MAAM,IAAI,IAAI;GACd,OAAO,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;EAC9B;EAIA,KAAK,MAAM,OAAO,CAAC,GAAG,IAAI,GAAG;GAC3B,MAAM,OAAO,MAAM;GACnB,KAAK,IAAI,KAAK,GAAG,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM,OAAO,IAAI,MAAM,OAAO,KAAK,CAAC;EAC1F;EACA,MAAM,SAAS,MAAM;EAErB,MAAM,qBAAqB;EAG3B,KAAK,SAAS,SAAS,MAAM,SAAS,CAAC;EACvC,KAAK,SAAS,aAAa,YAAY,MAAM,aAAa,UAAU,CAAC;EACrE,KAAK,SAAS,aAAa,MAAM,MAAM,aAAa,IAAI,CAAC;EACzD,KAAK,SAAS,aAAa,UAAU,MAAM,aAAa,QAAQ,CAAC;EACjE,KAAK,SAAS,sBAAsB;EACpC,MAAM,QAAQ;CAChB;CAEA,UAAgB;EACd,KAAK,SAAS,QAAQ;CACxB;AACF"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { StudioConfig, WaveConfig } from "../config/model.js";
|
|
2
|
+
import { WaveGeometry } from "./WaveGeometry.js";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
|
|
5
|
+
//#region src/renderer/WaveRenderer.d.ts
|
|
6
|
+
/** Reference frame (world units) the orthographic camera fills at cameraZoom 1. The wave is
|
|
7
|
+
* framed by COVERING this FRAME_W × FRAME_H rectangle (centred on cameraTarget) into the canvas
|
|
8
|
+
* — scaled to fill both dimensions, cropping the aspect overflow — so a given cameraZoom /
|
|
9
|
+
* cameraTarget frames the wave the SAME at any canvas size or aspect (only the cropped margin
|
|
10
|
+
* differs). FRAME_H = FRAME_W / (16/9) makes the reference a 16:9 rectangle; for canvases wider
|
|
11
|
+
* than that the width binds, narrower ones zoom in to fill instead of
|
|
12
|
+
* showing empty bands. This is what makes a saved preset reproduce on anyone's screen. */
|
|
13
|
+
declare const FRAME_W = 1333;
|
|
14
|
+
declare const FRAME_H = 750;
|
|
15
|
+
interface WaveRendererOptions {
|
|
16
|
+
/** Honor prefers-reduced-motion by freezing animation. Default true. */
|
|
17
|
+
respectReducedMotion?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Skip the intro time-ramp (the ~1s ease-in of animation on load), rendering at full speed
|
|
20
|
+
* immediately. The studio passes `import.meta.env.DEV` here so a fresh renderer on every HMR
|
|
21
|
+
* hot-swap doesn't replay the ease-in (which reads as a "speed up" when you tab back). Default
|
|
22
|
+
* false — production embeds keep the ease-in. Ignored while paused (a paused frame is always the
|
|
23
|
+
* full frame). Default false.
|
|
24
|
+
*/
|
|
25
|
+
skipIntroRamp?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Per-wave 2D palette texture (+ optional looping video). One instance per wave, so each
|
|
29
|
+
* wave carries its own palette. Guarded by a signature so it only rebuilds when that wave's
|
|
30
|
+
* palette actually changes (not every refresh).
|
|
31
|
+
*/
|
|
32
|
+
declare class WavePalette {
|
|
33
|
+
private readonly makeVideo;
|
|
34
|
+
private readonly onVideoReady;
|
|
35
|
+
texture?: THREE.Texture;
|
|
36
|
+
private sig;
|
|
37
|
+
private video?;
|
|
38
|
+
private videoUrl;
|
|
39
|
+
private failedUrl;
|
|
40
|
+
constructor(makeVideo: (url: string) => HTMLVideoElement, onVideoReady: () => void);
|
|
41
|
+
/** Rebuild this wave's palette texture from its config (video / custom image / stops /
|
|
42
|
+
* built-in map / hero LUT) and point the wave's palette uniforms at it. */
|
|
43
|
+
apply(cfg: WaveConfig, uniforms: Record<string, THREE.IUniform>): void;
|
|
44
|
+
/** Play/pause this wave's palette video with the render loop. */
|
|
45
|
+
syncPlayback(cfg: WaveConfig, running: boolean): void;
|
|
46
|
+
private ensureVideo;
|
|
47
|
+
private clearVideo;
|
|
48
|
+
dispose(): void;
|
|
49
|
+
}
|
|
50
|
+
type Wave = {
|
|
51
|
+
mesh: THREE.Mesh;
|
|
52
|
+
material: THREE.ShaderMaterial;
|
|
53
|
+
geometry: WaveGeometry; /** This wave's own 2D palette texture + optional video. */
|
|
54
|
+
palette: WavePalette;
|
|
55
|
+
};
|
|
56
|
+
/** Convert an sRGB hex string to a linear-space RGB vector (three's ColorManagement does the
|
|
57
|
+
* sRGB→linear conversion on parse). Exported for the studio subclass's live light-uniform push. */
|
|
58
|
+
declare function hexToLinearVec3(hex: string, target: THREE.Vector3): THREE.Vector3;
|
|
59
|
+
/**
|
|
60
|
+
* Renders a gradient "wave of light" from a {@link StudioConfig}. Framework-agnostic:
|
|
61
|
+
* it needs only a DOM container and a config. The studio mutates the config in
|
|
62
|
+
* place and calls `refresh()` / `rebuild()`.
|
|
63
|
+
*/
|
|
64
|
+
declare class WaveRenderer {
|
|
65
|
+
readonly renderer: THREE.WebGLRenderer;
|
|
66
|
+
protected readonly scene: THREE.Scene<THREE.Object3DEventMap>;
|
|
67
|
+
protected readonly camera: THREE.OrthographicCamera;
|
|
68
|
+
protected readonly group: THREE.Group<THREE.Object3DEventMap>;
|
|
69
|
+
private readonly composer;
|
|
70
|
+
private readonly postPass;
|
|
71
|
+
/** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */
|
|
72
|
+
private bloomPass?;
|
|
73
|
+
protected readonly container: HTMLElement;
|
|
74
|
+
private readonly respectReducedMotion;
|
|
75
|
+
private readonly skipIntroRamp;
|
|
76
|
+
protected config: StudioConfig;
|
|
77
|
+
protected waves: Wave[];
|
|
78
|
+
private backgroundTexture?;
|
|
79
|
+
private backgroundSig;
|
|
80
|
+
private backgroundImage?;
|
|
81
|
+
private backgroundImageUrl;
|
|
82
|
+
private failedBackgroundImageUrl;
|
|
83
|
+
private backgroundVideo?;
|
|
84
|
+
private backgroundVideoUrl;
|
|
85
|
+
private failedBackgroundVideoUrl;
|
|
86
|
+
private backgroundVideoCanvas?;
|
|
87
|
+
/** Authored default camera pose, for "Reset camera". */
|
|
88
|
+
protected readonly homeCamPos: THREE.Vector3;
|
|
89
|
+
protected readonly homeCamTarget: THREE.Vector3;
|
|
90
|
+
private readonly clipBox;
|
|
91
|
+
private readonly clipSphere;
|
|
92
|
+
private readonly clipTmpA;
|
|
93
|
+
private readonly clipTmpB;
|
|
94
|
+
private readonly clock;
|
|
95
|
+
private time;
|
|
96
|
+
private rafId;
|
|
97
|
+
protected running: boolean;
|
|
98
|
+
private started;
|
|
99
|
+
private visible;
|
|
100
|
+
private pageVisible;
|
|
101
|
+
private reducedMotion;
|
|
102
|
+
/** Intro ramp: eases animation time 0→1 over ~1s on load (when config.introRamp). */
|
|
103
|
+
private introTimeRamp;
|
|
104
|
+
private readonly resizeObserver;
|
|
105
|
+
private readonly intersectionObserver;
|
|
106
|
+
private readonly motionQuery;
|
|
107
|
+
protected capturing: boolean;
|
|
108
|
+
/** Fixed backing-buffer dimensions used by the studio's visible export frame. Embeds leave
|
|
109
|
+
* this unset and continue to resize responsively with their container and device DPR. */
|
|
110
|
+
private outputSize?;
|
|
111
|
+
constructor(container: HTMLElement, config: StudioConfig, options?: WaveRendererOptions);
|
|
112
|
+
private get segments();
|
|
113
|
+
private makeUniforms;
|
|
114
|
+
/** Vertex-shader #defines for a wave: TWIST_MOTION (per-wave animated twist wobble) and
|
|
115
|
+
* LOOP_MOTION (scene-level seamless loop). Both select #ifdef-gated code paths; an empty
|
|
116
|
+
* object compiles the default (linear-time) program. */
|
|
117
|
+
private waveDefines;
|
|
118
|
+
private addWave;
|
|
119
|
+
/**
|
|
120
|
+
* Apply config.blendMode to a material. "squared" (the default) is the hero blend:
|
|
121
|
+
* CustomBlending with AddEquation, src = SrcColorFactor, dst = ZeroFactor, so the
|
|
122
|
+
* framebuffer result is fragColor² — the squaring deepens the colours into the vivid
|
|
123
|
+
* hero look (without it the wave reads pastel). "additive"/"normal"/"multiply" are
|
|
124
|
+
* authoring overrides. Multiply uses Three's premultiplied-alpha path; the custom
|
|
125
|
+
* fragment shaders premultiply their output when Three injects PREMULTIPLIED_ALPHA.
|
|
126
|
+
* Returns true if material state changed (caller flags needsUpdate).
|
|
127
|
+
*/
|
|
128
|
+
private applyBlendMode;
|
|
129
|
+
private disposeWaves;
|
|
130
|
+
/**
|
|
131
|
+
* Reconcile the wave pool to `waveCount` WITHOUT tearing everything down:
|
|
132
|
+
* keep existing waves (so the compiled shader program is never deleted and
|
|
133
|
+
* re-compiled — that churn can crash some GPU drivers), add/remove only the
|
|
134
|
+
* delta, and resize each geometry to the current quality.
|
|
135
|
+
*/
|
|
136
|
+
private buildWaves;
|
|
137
|
+
/** Re-read per-frame-independent values from the (mutated) config. */
|
|
138
|
+
refresh(): void;
|
|
139
|
+
/** Point every wave's palette sampler at its own WavePalette texture (each rebuilt only
|
|
140
|
+
* when that wave's palette actually changes — see WavePalette.apply). */
|
|
141
|
+
private updatePaletteTextures;
|
|
142
|
+
/** Dev: 0 = normal, 1 = visualise the crease value, 2 = visualise derivative normal. */
|
|
143
|
+
setDebug(v: number): void;
|
|
144
|
+
/** Rebuild geometry + waves (call when waveCount or quality changes). */
|
|
145
|
+
rebuild(): void;
|
|
146
|
+
private applyBackground;
|
|
147
|
+
private applyColorBackground;
|
|
148
|
+
private applyGradientBackground;
|
|
149
|
+
/** Image mode: a live video, a user-loaded image, or a built-in map. `matte` shows while an
|
|
150
|
+
* async source is still loading. */
|
|
151
|
+
private applyImageBackground;
|
|
152
|
+
private backgroundCanvasSize;
|
|
153
|
+
private loadBackgroundImage;
|
|
154
|
+
private ensureBackgroundVideo;
|
|
155
|
+
private clearBackgroundVideo;
|
|
156
|
+
private createLoopingVideo;
|
|
157
|
+
private syncVideoPlayback;
|
|
158
|
+
private updateBackgroundVideoFrame;
|
|
159
|
+
private applyPost;
|
|
160
|
+
/** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so
|
|
161
|
+
* cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is
|
|
162
|
+
* created lazily the first time bloom is enabled and disposed when turned back off. It sits
|
|
163
|
+
* right after the scene RenderPass so it blooms the wave before the grain/blur pass. */
|
|
164
|
+
private applyBloom;
|
|
165
|
+
private onResize;
|
|
166
|
+
private onContextLost;
|
|
167
|
+
private onContextRestored;
|
|
168
|
+
resize(): void;
|
|
169
|
+
/** Set an exact output buffer while CSS scales the canvas into the on-screen export frame. */
|
|
170
|
+
setOutputSize(width: number, height: number): void;
|
|
171
|
+
start(): void;
|
|
172
|
+
stop(): void;
|
|
173
|
+
private onMotionChange;
|
|
174
|
+
private onVisibilityChange;
|
|
175
|
+
private updateRunning;
|
|
176
|
+
private loop;
|
|
177
|
+
/** Advance the per-frame clock uniforms (geometry itself is static). Time model:
|
|
178
|
+
* time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */
|
|
179
|
+
private updateTime;
|
|
180
|
+
/** Render exactly one frame at the current time. */
|
|
181
|
+
renderOnce(): void;
|
|
182
|
+
/** Re-evaluate play/pause after `config.paused` changes. */
|
|
183
|
+
refreshPlayback(): void;
|
|
184
|
+
/** Jump the camera to the config's authored framing (cameraPosition / cameraTarget /
|
|
185
|
+
* cameraZoom). Called on whole-config swaps (preset / reset / randomize / import). The base
|
|
186
|
+
* applies the pose directly; the studio subclass overrides it to also drive the orbit target
|
|
187
|
+
* and sync the panel. Hook ⑤. */
|
|
188
|
+
protected applyCameraFromConfig(): void;
|
|
189
|
+
/** Hook ①: true while orbit or an edit gizmo owns the camera, so refresh() won't reset it. */
|
|
190
|
+
protected isCameraExternallyDriven(): boolean;
|
|
191
|
+
/** Hook ②: called at the end of refresh(), before the trailing renderOnce(). */
|
|
192
|
+
protected onAfterRefresh(): void;
|
|
193
|
+
/** Hook ③: called at the end of renderOnce(), after the composed frame is drawn. */
|
|
194
|
+
protected onAfterRenderFrame(): void;
|
|
195
|
+
/** Hook ④: called at the end of resize(), before the trailing renderOnce(). */
|
|
196
|
+
protected onAfterResize(): void;
|
|
197
|
+
/** Responsive ortho zoom: COVER the FRAME_W × FRAME_H reference frame onto the canvas so the
|
|
198
|
+
* wave frames the same at any size/aspect/dpr (only the cropped margin differs), times the
|
|
199
|
+
* user's cameraZoom. `max(...)` = cover (fill both axes, crop overflow); `min(...)` would be
|
|
200
|
+
* contain (fit with letterbox bands). Cover keeps the wave filling the frame on every screen. */
|
|
201
|
+
protected applyZoom(): void;
|
|
202
|
+
/** Fit the orthographic near/far planes to the scene before every render, so no part of a wave
|
|
203
|
+
* is ever clipped as the camera orbits / dollies / pans (or when waves are added or scaled).
|
|
204
|
+
*
|
|
205
|
+
* The camera is *constructed* with fixed 1..10000 planes that only suit the authored hero
|
|
206
|
+
* framing; once the view moves, the wave's depth extent along the view axis easily crosses
|
|
207
|
+
* them and the GPU hard-slices the geometry along a flat plane — chunks of the wave vanish.
|
|
208
|
+
*
|
|
209
|
+
* The in-shader twist rotates each vertex about its LOCAL origin, so it can never move a vertex
|
|
210
|
+
* further from that origin than the base geometry already sits. A sphere at each mesh's origin
|
|
211
|
+
* (radius = base extent × the mesh's largest world scale, plus the Y displacement, ×1.2 slack)
|
|
212
|
+
* therefore safely contains the fully-deformed wave. We union those, then bracket the union
|
|
213
|
+
* along the view axis with a margin. Bracketing to the scene (rather than a fixed huge slab)
|
|
214
|
+
* keeps clip-space depth scene-normalised — so the wireframe theme's depth fade, which reads
|
|
215
|
+
* gl_Position.z, looks the same at any camera distance instead of washing out. Runs each frame;
|
|
216
|
+
* it's a handful of vector ops over 1–8 meshes with no allocation. */
|
|
217
|
+
private updateClipPlanes;
|
|
218
|
+
/** A world-space position delta that drops a duplicated wave into open frame space beside the
|
|
219
|
+
* one it was copied from — screen-left and a touch down, sized to the visible frame — instead
|
|
220
|
+
* of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed
|
|
221
|
+
* off-frame. Camera-relative: it's "left on screen" no matter how the view is rotated/zoomed,
|
|
222
|
+
* because it's built from the camera's right/up axes and the frame's visible world size. */
|
|
223
|
+
captureImage(mime: string, transparent?: boolean, quality?: number): Promise<Blob>;
|
|
224
|
+
captureStream(fps?: number): MediaStream;
|
|
225
|
+
get canvas(): HTMLCanvasElement;
|
|
226
|
+
getConfig(): StudioConfig;
|
|
227
|
+
setConfig(config: StudioConfig): void;
|
|
228
|
+
dispose(): void;
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
export { FRAME_H, FRAME_W, WaveRenderer, WaveRendererOptions, hexToLinearVec3 };
|
|
232
|
+
//# sourceMappingURL=WaveRenderer.d.ts.map
|