@supersoniks/concorde 4.9.98-visual-stack.0 → 4.9.98-visual-stack.1
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/build-infos.json +1 -1
- package/concorde-3d.bundle.js +201 -152
- package/concorde-3d.es.js +14687 -13905
- package/concorde-core.bundle.js +1 -1
- package/concorde-core.es.js +1 -1
- package/concorde-hugging-face-infer.bundle.js +1 -1
- package/concorde-hugging-face-infer.es.js +1 -1
- package/concorde-shader.bundle.js +31 -20
- package/concorde-shader.es.js +2773 -2547
- package/concorde-webgpu.bundle.js +43 -32
- package/concorde-webgpu.es.js +1819 -1553
- package/dist/concorde-3d.bundle.js +201 -152
- package/dist/concorde-3d.es.js +14687 -13905
- package/dist/concorde-core.bundle.js +1 -1
- package/dist/concorde-core.es.js +1 -1
- package/dist/concorde-hugging-face-infer.bundle.js +1 -1
- package/dist/concorde-hugging-face-infer.es.js +1 -1
- package/dist/concorde-shader.bundle.js +31 -20
- package/dist/concorde-shader.es.js +2773 -2547
- package/dist/concorde-webgpu.bundle.js +43 -32
- package/dist/concorde-webgpu.es.js +1819 -1553
- package/dist/js/core/components/functional/3d/3d.js +90 -2
- package/dist/js/core/components/functional/3d/3d.js.map +1 -1
- package/dist/js/core/components/functional/3d/constants.js +1 -0
- package/dist/js/core/components/functional/3d/constants.js.map +1 -1
- package/dist/js/core/components/functional/3d/ensure.js +6 -3
- package/dist/js/core/components/functional/3d/ensure.js.map +1 -1
- package/dist/js/core/components/functional/3d/form-params.js +2 -0
- package/dist/js/core/components/functional/3d/form-params.js.map +1 -1
- package/dist/js/core/components/functional/3d/life.js +2 -1
- package/dist/js/core/components/functional/3d/life.js.map +1 -1
- package/dist/js/core/components/functional/3d/out-publish.js +3 -2
- package/dist/js/core/components/functional/3d/out-publish.js.map +1 -1
- package/dist/js/core/components/functional/3d/playback.js +6 -1
- package/dist/js/core/components/functional/3d/playback.js.map +1 -1
- package/dist/js/core/components/functional/3d/post-bind.js +207 -0
- package/dist/js/core/components/functional/3d/post-bind.js.map +1 -0
- package/dist/js/core/components/functional/3d/post-channels.js +39 -0
- package/dist/js/core/components/functional/3d/post-channels.js.map +1 -0
- package/dist/js/core/components/functional/3d/post-fx.js +495 -0
- package/dist/js/core/components/functional/3d/post-fx.js.map +1 -0
- package/dist/js/core/components/functional/3d/props.js +142 -0
- package/dist/js/core/components/functional/3d/props.js.map +1 -1
- package/dist/js/core/components/functional/3d/reload.js +2 -0
- package/dist/js/core/components/functional/3d/reload.js.map +1 -1
- package/dist/js/core/components/functional/3d/runtime.js +80 -3
- package/dist/js/core/components/functional/3d/runtime.js.map +1 -1
- package/dist/js/core/components/functional/3d/scene-runtime.js +1 -1
- package/dist/js/core/components/functional/3d/scene-runtime.js.map +1 -1
- package/dist/js/core/components/functional/3d/tune.js +21 -0
- package/dist/js/core/components/functional/3d/tune.js.map +1 -1
- package/dist/js/core/components/functional/3d/updated.js +13 -1
- package/dist/js/core/components/functional/3d/updated.js.map +1 -1
- package/dist/js/core/components/functional/shader/gl-runtime.js +128 -14
- package/dist/js/core/components/functional/shader/gl-runtime.js.map +1 -1
- package/dist/js/core/components/functional/shader/shader.js +154 -57
- package/dist/js/core/components/functional/shader/shader.js.map +1 -1
- package/dist/js/core/components/functional/shader/shadertoy-prelude.js +39 -5
- package/dist/js/core/components/functional/shader/shadertoy-prelude.js.map +1 -1
- package/dist/js/core/components/functional/shader/types.js +58 -7
- package/dist/js/core/components/functional/shader/types.js.map +1 -1
- package/dist/js/core/components/functional/webgpu/gl-fallback.js +27 -6
- package/dist/js/core/components/functional/webgpu/gl-fallback.js.map +1 -1
- package/dist/js/core/components/functional/webgpu/gpu-runtime.js +115 -13
- package/dist/js/core/components/functional/webgpu/gpu-runtime.js.map +1 -1
- package/dist/js/core/components/functional/webgpu/helpers.js +8 -2
- package/dist/js/core/components/functional/webgpu/helpers.js.map +1 -1
- package/dist/js/core/components/functional/webgpu/webgpu.js +34 -1
- package/dist/js/core/components/functional/webgpu/webgpu.js.map +1 -1
- package/dist/js/core/utils/mediaRef.js +37 -0
- package/dist/js/core/utils/mediaRef.js.map +1 -1
- package/dist/types/core/components/functional/3d/3d.d.ts +26 -3
- package/dist/types/core/components/functional/3d/3d.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/constants.d.ts +1 -1
- package/dist/types/core/components/functional/3d/constants.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/ensure.d.ts +3 -0
- package/dist/types/core/components/functional/3d/ensure.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/form-params.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/life.d.ts +2 -0
- package/dist/types/core/components/functional/3d/life.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/out-publish.d.ts +1 -0
- package/dist/types/core/components/functional/3d/out-publish.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/playback.d.ts +9 -0
- package/dist/types/core/components/functional/3d/playback.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/post-bind.d.ts +52 -0
- package/dist/types/core/components/functional/3d/post-bind.d.ts.map +1 -0
- package/dist/types/core/components/functional/3d/post-channels.d.ts +40 -0
- package/dist/types/core/components/functional/3d/post-channels.d.ts.map +1 -0
- package/dist/types/core/components/functional/3d/post-fx.d.ts +73 -0
- package/dist/types/core/components/functional/3d/post-fx.d.ts.map +1 -0
- package/dist/types/core/components/functional/3d/props.d.ts +42 -0
- package/dist/types/core/components/functional/3d/props.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/reload.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/runtime.d.ts +16 -1
- package/dist/types/core/components/functional/3d/runtime.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/scene-runtime.d.ts +1 -1
- package/dist/types/core/components/functional/3d/scene-runtime.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/tune.d.ts +5 -0
- package/dist/types/core/components/functional/3d/tune.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/types.d.ts +9 -0
- package/dist/types/core/components/functional/3d/types.d.ts.map +1 -1
- package/dist/types/core/components/functional/3d/updated.d.ts.map +1 -1
- package/dist/types/core/components/functional/shader/gl-runtime.d.ts +33 -1
- package/dist/types/core/components/functional/shader/gl-runtime.d.ts.map +1 -1
- package/dist/types/core/components/functional/shader/shader.d.ts +28 -7
- package/dist/types/core/components/functional/shader/shader.d.ts.map +1 -1
- package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts +16 -2
- package/dist/types/core/components/functional/shader/shadertoy-prelude.d.ts.map +1 -1
- package/dist/types/core/components/functional/shader/types.d.ts +29 -5
- package/dist/types/core/components/functional/shader/types.d.ts.map +1 -1
- package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts +4 -0
- package/dist/types/core/components/functional/webgpu/gl-fallback.d.ts.map +1 -1
- package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts +14 -0
- package/dist/types/core/components/functional/webgpu/gpu-runtime.d.ts.map +1 -1
- package/dist/types/core/components/functional/webgpu/helpers.d.ts +1 -0
- package/dist/types/core/components/functional/webgpu/helpers.d.ts.map +1 -1
- package/dist/types/core/components/functional/webgpu/webgpu.d.ts +7 -1
- package/dist/types/core/components/functional/webgpu/webgpu.d.ts.map +1 -1
- package/dist/types/core/utils/mediaRef.d.ts +15 -0
- package/dist/types/core/utils/mediaRef.d.ts.map +1 -1
- package/package.json +37 -1
- package/src/core/components/functional/3d/3d.md +34 -1
- package/src/core/components/functional/3d/3d.spec.ts +42 -0
- package/src/core/components/functional/3d/3d.ts +101 -3
- package/src/core/components/functional/3d/constants.ts +1 -0
- package/src/core/components/functional/3d/ensure.ts +11 -3
- package/src/core/components/functional/3d/form-params.ts +2 -0
- package/src/core/components/functional/3d/life.ts +4 -1
- package/src/core/components/functional/3d/out-publish.ts +4 -3
- package/src/core/components/functional/3d/playback.ts +13 -1
- package/src/core/components/functional/3d/post-bind.ts +262 -0
- package/src/core/components/functional/3d/post-channels.spec.ts +36 -0
- package/src/core/components/functional/3d/post-channels.ts +69 -0
- package/src/core/components/functional/3d/post-fx.ts +594 -0
- package/src/core/components/functional/3d/props.ts +45 -0
- package/src/core/components/functional/3d/reload.ts +2 -0
- package/src/core/components/functional/3d/runtime.ts +93 -4
- package/src/core/components/functional/3d/scene-runtime.ts +3 -0
- package/src/core/components/functional/3d/tune.ts +21 -0
- package/src/core/components/functional/3d/types.ts +9 -0
- package/src/core/components/functional/3d/updated.ts +17 -1
- package/src/core/components/functional/shader/gl-runtime.ts +169 -12
- package/src/core/components/functional/shader/shader.md +27 -4
- package/src/core/components/functional/shader/shader.spec.ts +267 -0
- package/src/core/components/functional/shader/shader.ts +177 -55
- package/src/core/components/functional/shader/shadertoy-prelude.ts +49 -5
- package/src/core/components/functional/shader/types.ts +89 -12
- package/src/core/components/functional/webgpu/gl-fallback.ts +28 -4
- package/src/core/components/functional/webgpu/gpu-runtime.ts +154 -18
- package/src/core/components/functional/webgpu/helpers.ts +8 -2
- package/src/core/components/functional/webgpu/webgpu.md +13 -2
- package/src/core/components/functional/webgpu/webgpu.spec.ts +37 -0
- package/src/core/components/functional/webgpu/webgpu.ts +39 -1
- package/src/core/utils/mediaRef.spec.ts +25 -0
- package/src/core/utils/mediaRef.ts +45 -0
- package/src/docs/_misc/visual-stack.md +19 -1
- package/src/docs/components/docs-demo-sources.ts +28 -0
- package/src/docs/docs.ts +2 -0
- package/src/docs/example/docs-3d-post-depth-demo.ts +68 -0
- package/src/docs/example/docs-3d-webgpu-live-demo.ts +83 -0
- package/src/docs/example/docs-harris-webgpu-demo.ts +53 -55
- package/src/docs/search/docs-search.json +252 -2
- package/src/tsconfig.json +27 -0
- package/src/tsconfig.tsbuildinfo +1 -1
|
@@ -6,14 +6,22 @@ import { bootScene } from "./boot";
|
|
|
6
6
|
import { ops, type OpsCtx } from "./ops";
|
|
7
7
|
import type { SceneRuntimeOptions } from "./opts";
|
|
8
8
|
import type { SceneHit } from "./pick";
|
|
9
|
+
import {
|
|
10
|
+
PostFxCompileError,
|
|
11
|
+
PostFxPipeline,
|
|
12
|
+
type PostFxConfig,
|
|
13
|
+
type PostFxUniforms,
|
|
14
|
+
} from "./post-fx";
|
|
9
15
|
import { tune, type TuneCtx } from "./tune";
|
|
10
16
|
import type { Sonic3dAsset, Sonic3dOutput } from "./types";
|
|
11
17
|
import { view, type ViewCtx } from "./view";
|
|
12
18
|
|
|
13
19
|
export type { SceneHit, SceneRuntimeOptions };
|
|
20
|
+
export { PostFxCompileError };
|
|
21
|
+
export type { PostFxConfig, PostFxUniforms };
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
|
-
* Runtime Three.js encapsulé : renderer, scène, caméra, load, fit, pick.
|
|
24
|
+
* Runtime Three.js encapsulé : renderer, scène, caméra, load, fit, pick, post FX.
|
|
17
25
|
* Le composant Lit gère RAF / viewport / DP.
|
|
18
26
|
*/
|
|
19
27
|
export class SceneRuntime {
|
|
@@ -46,6 +54,19 @@ export class SceneRuntime {
|
|
|
46
54
|
private highlighted: THREE.Mesh | null = null;
|
|
47
55
|
private prevEmissive = new THREE.Color();
|
|
48
56
|
private prevEmissiveIntensity = 0;
|
|
57
|
+
private postFx: PostFxPipeline;
|
|
58
|
+
private lastCssW = 1;
|
|
59
|
+
private lastCssH = 1;
|
|
60
|
+
private damping = 0.08;
|
|
61
|
+
private postFrame: PostFxUniforms = {
|
|
62
|
+
time: 0,
|
|
63
|
+
timeDelta: 1 / 60,
|
|
64
|
+
frame: 0,
|
|
65
|
+
mouse: [0, 0, 0, 0],
|
|
66
|
+
params: [0, 0, 0, 0],
|
|
67
|
+
near: 0.1,
|
|
68
|
+
far: 100,
|
|
69
|
+
};
|
|
49
70
|
|
|
50
71
|
constructor(opts: SceneRuntimeOptions) {
|
|
51
72
|
const b = bootScene(opts);
|
|
@@ -62,9 +83,32 @@ export class SceneRuntime {
|
|
|
62
83
|
this.fov = b.fov;
|
|
63
84
|
this.fitEnabled = b.fitEnabled;
|
|
64
85
|
this.limits = b.limits;
|
|
86
|
+
this.postFx = new PostFxPipeline(this.renderer);
|
|
65
87
|
this.applyCameraMode(this.mode);
|
|
66
88
|
if (b.autoRotate) this.setAutoRotate(true);
|
|
67
|
-
void [
|
|
89
|
+
void [
|
|
90
|
+
this.canvas,
|
|
91
|
+
this.perspCam,
|
|
92
|
+
this.orthoCam,
|
|
93
|
+
this.root,
|
|
94
|
+
this.content,
|
|
95
|
+
this.lights,
|
|
96
|
+
this.loader,
|
|
97
|
+
this.loadToken,
|
|
98
|
+
this.blobUrl,
|
|
99
|
+
this.dprMax,
|
|
100
|
+
this.fov,
|
|
101
|
+
this.fitEnabled,
|
|
102
|
+
this.limits,
|
|
103
|
+
this.bbox,
|
|
104
|
+
this.target,
|
|
105
|
+
this.raycaster,
|
|
106
|
+
this.pointerNdc,
|
|
107
|
+
this.highlighted,
|
|
108
|
+
this.prevEmissive,
|
|
109
|
+
this.prevEmissiveIntensity,
|
|
110
|
+
this.unbindWheelRelay,
|
|
111
|
+
];
|
|
68
112
|
}
|
|
69
113
|
|
|
70
114
|
get ready() {
|
|
@@ -79,6 +123,9 @@ export class SceneRuntime {
|
|
|
79
123
|
get isLost() {
|
|
80
124
|
return this.renderer.getContext()?.isContextLost?.() ?? false;
|
|
81
125
|
}
|
|
126
|
+
get hasPostFx() {
|
|
127
|
+
return this.postFx.isEnabled;
|
|
128
|
+
}
|
|
82
129
|
|
|
83
130
|
setDprMax(v: number) {
|
|
84
131
|
tune.setDprMax(this.asTuneCtx(), v);
|
|
@@ -95,6 +142,15 @@ export class SceneRuntime {
|
|
|
95
142
|
setAutoRotate(v: boolean) {
|
|
96
143
|
tune.setAutoRotate(this.asTuneCtx(), v);
|
|
97
144
|
}
|
|
145
|
+
setDamping(v: number) {
|
|
146
|
+
this.damping = Number.isFinite(v) ? Math.max(0, v) : 0.08;
|
|
147
|
+
tune.setDamping(this.asTuneCtx(), this.damping);
|
|
148
|
+
}
|
|
149
|
+
/** Coupe autoRotate + inertie OrbitControls. */
|
|
150
|
+
stopMotion() {
|
|
151
|
+
tune.setAutoRotate(this.asTuneCtx(), false);
|
|
152
|
+
tune.stopMotion(this.asTuneCtx());
|
|
153
|
+
}
|
|
98
154
|
setOrbitLimits(opts: Parameters<typeof tune.setOrbitLimits>[1]) {
|
|
99
155
|
tune.setOrbitLimits(this.asTuneCtx(), opts);
|
|
100
156
|
}
|
|
@@ -104,9 +160,17 @@ export class SceneRuntime {
|
|
|
104
160
|
|
|
105
161
|
applyCameraMode(mode: Sonic3dCameraMode) {
|
|
106
162
|
view.applyCameraMode(this.asViewCtx(), mode);
|
|
163
|
+
tune.setDamping(this.asTuneCtx(), this.damping);
|
|
107
164
|
}
|
|
108
165
|
resize(cssW: number, cssH: number) {
|
|
166
|
+
this.lastCssW = cssW;
|
|
167
|
+
this.lastCssH = cssH;
|
|
109
168
|
view.resize(this.asViewCtx(), cssW, cssH);
|
|
169
|
+
const dpr = Math.min(
|
|
170
|
+
typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1,
|
|
171
|
+
this.dprMax,
|
|
172
|
+
);
|
|
173
|
+
this.postFx.resize(cssW * dpr, cssH * dpr);
|
|
110
174
|
}
|
|
111
175
|
async load(asset: Sonic3dAsset | null) {
|
|
112
176
|
return ops.runLoad(this.asOpsCtx(), asset);
|
|
@@ -114,9 +178,34 @@ export class SceneRuntime {
|
|
|
114
178
|
fitToContent() {
|
|
115
179
|
ops.fitToContent(this.asOpsCtx());
|
|
116
180
|
}
|
|
181
|
+
|
|
182
|
+
setPostConfig(config: PostFxConfig | null) {
|
|
183
|
+
this.postFx.setConfig(config);
|
|
184
|
+
if (config && this.lastCssW > 0) {
|
|
185
|
+
this.resize(this.lastCssW, this.lastCssH);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
setPostElementResolver(fn: ((key: string) => Element | null) | null) {
|
|
190
|
+
this.postFx.setElementResolver(fn);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
setPostFrame(frame: Partial<PostFxUniforms>) {
|
|
194
|
+
Object.assign(this.postFrame, frame);
|
|
195
|
+
const cam = this.activeCam as THREE.PerspectiveCamera;
|
|
196
|
+
if ("near" in cam && "far" in cam) {
|
|
197
|
+
this.postFrame.near = cam.near;
|
|
198
|
+
this.postFrame.far = cam.far;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
117
202
|
render() {
|
|
118
203
|
this.controls?.update();
|
|
119
|
-
|
|
204
|
+
if (this.postFx.isEnabled) {
|
|
205
|
+
this.postFx.render(this.scene, this.activeCam, this.postFrame);
|
|
206
|
+
} else {
|
|
207
|
+
this.renderer.render(this.scene, this.activeCam);
|
|
208
|
+
}
|
|
120
209
|
}
|
|
121
210
|
pick(normX: number, normY: number): SceneHit | null {
|
|
122
211
|
return ops.pick(this.asOpsCtx(), normX, normY);
|
|
@@ -138,6 +227,7 @@ export class SceneRuntime {
|
|
|
138
227
|
return ops.buildSnapshot(this.asOpsCtx(), base);
|
|
139
228
|
}
|
|
140
229
|
dispose(loseContext = false) {
|
|
230
|
+
this.postFx.dispose();
|
|
141
231
|
ops.dispose(this.asOpsCtx(), loseContext);
|
|
142
232
|
}
|
|
143
233
|
|
|
@@ -145,4 +235,3 @@ export class SceneRuntime {
|
|
|
145
235
|
private asViewCtx = () => this as unknown as ViewCtx;
|
|
146
236
|
private asOpsCtx = () => this as unknown as OpsCtx;
|
|
147
237
|
}
|
|
148
|
-
|
|
@@ -37,6 +37,25 @@ function setAutoRotate(ctx: TuneCtx, v: boolean) {
|
|
|
37
37
|
if (ctx.controls) ctx.controls.autoRotate = v;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function setDamping(ctx: TuneCtx, v: number) {
|
|
41
|
+
if (!ctx.controls) return;
|
|
42
|
+
const d = Number.isFinite(v) ? Math.max(0, v) : 0.08;
|
|
43
|
+
ctx.controls.enableDamping = d > 0;
|
|
44
|
+
ctx.controls.dampingFactor = d > 0 ? d : 0.08;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Coupe autoRotate + vide l’inertie OrbitControls. */
|
|
48
|
+
function stopMotion(ctx: TuneCtx) {
|
|
49
|
+
if (!ctx.controls) return;
|
|
50
|
+
ctx.controls.autoRotate = false;
|
|
51
|
+
const wasDamping = ctx.controls.enableDamping;
|
|
52
|
+
const factor = ctx.controls.dampingFactor;
|
|
53
|
+
ctx.controls.enableDamping = false;
|
|
54
|
+
ctx.controls.update();
|
|
55
|
+
ctx.controls.enableDamping = wasDamping;
|
|
56
|
+
ctx.controls.dampingFactor = factor;
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
function setOrbitLimits(ctx: TuneCtx, opts: Partial<OrbitLimits>) {
|
|
41
60
|
ctx.limits = orbit.mergeLimits(ctx.limits, opts);
|
|
42
61
|
orbit.applyLimitsToControls(ctx.controls, ctx.limits);
|
|
@@ -67,6 +86,8 @@ export const tune = {
|
|
|
67
86
|
setFov,
|
|
68
87
|
setFit,
|
|
69
88
|
setAutoRotate,
|
|
89
|
+
setDamping,
|
|
90
|
+
stopMotion,
|
|
70
91
|
setOrbitLimits,
|
|
71
92
|
setOrbitSpherical,
|
|
72
93
|
} as const;
|
|
@@ -65,6 +65,13 @@ export type Sonic3dConfig = {
|
|
|
65
65
|
playOnHover?: boolean;
|
|
66
66
|
/** Libère le renderer hors viewport (défaut true). */
|
|
67
67
|
releaseOffscreen?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Garde RAF / runtime hors viewport (pont live).
|
|
70
|
+
* Aussi auto via `registerFrameConsumer`.
|
|
71
|
+
*/
|
|
72
|
+
playOffscreen?: boolean;
|
|
73
|
+
/** Inertie OrbitControls (défaut 0.08, 0 = off). */
|
|
74
|
+
damping?: number;
|
|
68
75
|
dprMax?: number;
|
|
69
76
|
aspectRatio?: string;
|
|
70
77
|
|
|
@@ -124,6 +131,8 @@ export type Sonic3dOutput = {
|
|
|
124
131
|
distance: number;
|
|
125
132
|
yaw: number;
|
|
126
133
|
pitch: number;
|
|
134
|
+
/** 1 si la caméra bouge (orbit / inertie / autoRotate), sinon 0. */
|
|
135
|
+
moving?: number;
|
|
127
136
|
/** Présents après un pick réussi. */
|
|
128
137
|
hitX?: number;
|
|
129
138
|
hitY?: number;
|
|
@@ -3,6 +3,7 @@ import type { LifeHost } from "./life";
|
|
|
3
3
|
import { life } from "./life";
|
|
4
4
|
import { parse } from "./parse";
|
|
5
5
|
import { playback } from "./playback";
|
|
6
|
+
import { postBind } from "./post-bind";
|
|
6
7
|
import { reload } from "./reload";
|
|
7
8
|
|
|
8
9
|
function hasAny(
|
|
@@ -61,10 +62,25 @@ function updated(
|
|
|
61
62
|
if (changed.has("autoRotate") || changed.has("play")) {
|
|
62
63
|
playback.syncAutoRotate(host as never);
|
|
63
64
|
}
|
|
64
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
changed.has("play") ||
|
|
67
|
+
changed.has("playOnHover") ||
|
|
68
|
+
changed.has("playOffscreen")
|
|
69
|
+
) {
|
|
65
70
|
host.syncPlayback();
|
|
66
71
|
}
|
|
72
|
+
if (changed.has("playOffscreen") || changed.has("releaseOffscreen")) {
|
|
73
|
+
ensure.syncVisibility(host as never);
|
|
74
|
+
}
|
|
75
|
+
if (changed.has("damping") && host.runtime) {
|
|
76
|
+
host.runtime.setDamping(
|
|
77
|
+
(host as unknown as { damping: number }).damping,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
67
80
|
if (changed.has("src") || changed.has("asset")) void reload.run(host as never);
|
|
81
|
+
if (postBind.propsChanged(changed)) {
|
|
82
|
+
postBind.applyConfig(host as never);
|
|
83
|
+
}
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
export const propUpdate = { updated } as const;
|
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
ChannelSource,
|
|
8
8
|
PassChannels,
|
|
9
9
|
ReadRectResult,
|
|
10
|
+
ShaderBufferFormat,
|
|
10
11
|
ShaderCompileError,
|
|
11
12
|
ShaderPassId,
|
|
12
13
|
ShaderSources,
|
|
14
|
+
parseBufferFormat,
|
|
13
15
|
} from "./types";
|
|
14
16
|
|
|
15
17
|
const PASS_ORDER: ShaderPassId[] = [
|
|
@@ -235,27 +237,110 @@ function getUniforms(
|
|
|
235
237
|
};
|
|
236
238
|
}
|
|
237
239
|
|
|
240
|
+
type BufferGlFormat = {
|
|
241
|
+
requested: ShaderBufferFormat;
|
|
242
|
+
active: ShaderBufferFormat;
|
|
243
|
+
internal: number;
|
|
244
|
+
format: number;
|
|
245
|
+
type: number;
|
|
246
|
+
filter: number;
|
|
247
|
+
float: boolean;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
let warnedFloatFallback = false;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Résout le format GPU des buffers A–D.
|
|
254
|
+
* Exporté pour tests (mock `getExtension`).
|
|
255
|
+
*/
|
|
256
|
+
export function resolveBufferGlFormat(
|
|
257
|
+
gl: WebGL2RenderingContext,
|
|
258
|
+
requested: ShaderBufferFormat,
|
|
259
|
+
): BufferGlFormat {
|
|
260
|
+
const want = parseBufferFormat(requested);
|
|
261
|
+
if (want === "rgba8") {
|
|
262
|
+
return {
|
|
263
|
+
requested: want,
|
|
264
|
+
active: "rgba8",
|
|
265
|
+
internal: gl.RGBA8,
|
|
266
|
+
format: gl.RGBA,
|
|
267
|
+
type: gl.UNSIGNED_BYTE,
|
|
268
|
+
filter: gl.LINEAR,
|
|
269
|
+
float: false,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const ext = gl.getExtension("EXT_color_buffer_float");
|
|
273
|
+
if (!ext) {
|
|
274
|
+
if (!warnedFloatFallback) {
|
|
275
|
+
warnedFloatFallback = true;
|
|
276
|
+
console.warn(
|
|
277
|
+
`[sonic-shader] buffer-format="${want}" indisponible (pas d'EXT_color_buffer_float) → rgba8`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return resolveBufferGlFormat(gl, "rgba8");
|
|
281
|
+
}
|
|
282
|
+
if (want === "rgba16f") {
|
|
283
|
+
return {
|
|
284
|
+
requested: want,
|
|
285
|
+
active: "rgba16f",
|
|
286
|
+
internal: gl.RGBA16F,
|
|
287
|
+
format: gl.RGBA,
|
|
288
|
+
type: gl.HALF_FLOAT,
|
|
289
|
+
filter: gl.LINEAR,
|
|
290
|
+
float: true,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// rgba32f
|
|
294
|
+
const linearOk = !!gl.getExtension("OES_texture_float_linear");
|
|
295
|
+
return {
|
|
296
|
+
requested: want,
|
|
297
|
+
active: "rgba32f",
|
|
298
|
+
internal: gl.RGBA32F,
|
|
299
|
+
format: gl.RGBA,
|
|
300
|
+
type: gl.FLOAT,
|
|
301
|
+
filter: linearOk ? gl.LINEAR : gl.NEAREST,
|
|
302
|
+
float: true,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Reset du warn unique (tests). */
|
|
307
|
+
export function resetBufferFormatWarnFlag() {
|
|
308
|
+
warnedFloatFallback = false;
|
|
309
|
+
}
|
|
310
|
+
|
|
238
311
|
function createEmptyTexture(
|
|
239
312
|
gl: WebGL2RenderingContext,
|
|
240
313
|
width: number,
|
|
241
314
|
height: number,
|
|
315
|
+
fmt?: BufferGlFormat,
|
|
242
316
|
): WebGLTexture {
|
|
317
|
+
const f =
|
|
318
|
+
fmt ??
|
|
319
|
+
({
|
|
320
|
+
requested: "rgba8",
|
|
321
|
+
active: "rgba8",
|
|
322
|
+
internal: gl.RGBA8,
|
|
323
|
+
format: gl.RGBA,
|
|
324
|
+
type: gl.UNSIGNED_BYTE,
|
|
325
|
+
filter: gl.LINEAR,
|
|
326
|
+
float: false,
|
|
327
|
+
} satisfies BufferGlFormat);
|
|
243
328
|
const tex = gl.createTexture();
|
|
244
329
|
if (!tex) throw new Error("Unable to create texture");
|
|
245
330
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
246
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
|
|
247
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,
|
|
331
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, f.filter);
|
|
332
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, f.filter);
|
|
248
333
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
249
334
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
250
335
|
gl.texImage2D(
|
|
251
336
|
gl.TEXTURE_2D,
|
|
252
337
|
0,
|
|
253
|
-
|
|
338
|
+
f.internal,
|
|
254
339
|
width,
|
|
255
340
|
height,
|
|
256
341
|
0,
|
|
257
|
-
|
|
258
|
-
|
|
342
|
+
f.format,
|
|
343
|
+
f.type,
|
|
259
344
|
null,
|
|
260
345
|
);
|
|
261
346
|
return tex;
|
|
@@ -287,10 +372,11 @@ function createPingPong(
|
|
|
287
372
|
gl: WebGL2RenderingContext,
|
|
288
373
|
width: number,
|
|
289
374
|
height: number,
|
|
375
|
+
fmt: BufferGlFormat,
|
|
290
376
|
): PingPong {
|
|
291
377
|
const textures: [WebGLTexture, WebGLTexture] = [
|
|
292
|
-
createEmptyTexture(gl, width, height),
|
|
293
|
-
createEmptyTexture(gl, width, height),
|
|
378
|
+
createEmptyTexture(gl, width, height, fmt),
|
|
379
|
+
createEmptyTexture(gl, width, height, fmt),
|
|
294
380
|
];
|
|
295
381
|
const framebuffers: [WebGLFramebuffer, WebGLFramebuffer] = [
|
|
296
382
|
createFramebuffer(gl, textures[0]),
|
|
@@ -349,6 +435,7 @@ export class ShaderToyRuntime {
|
|
|
349
435
|
private activePasses: ShaderPassId[] = [];
|
|
350
436
|
private blackTexture: WebGLTexture | null = null;
|
|
351
437
|
private elementResolver: ((key: string) => Element | null) | null = null;
|
|
438
|
+
private bufferFmt: BufferGlFormat;
|
|
352
439
|
|
|
353
440
|
constructor(canvas: HTMLCanvasElement) {
|
|
354
441
|
const gl = canvas.getContext("webgl2", {
|
|
@@ -364,6 +451,7 @@ export class ShaderToyRuntime {
|
|
|
364
451
|
}
|
|
365
452
|
this.canvas = canvas;
|
|
366
453
|
this.gl = gl;
|
|
454
|
+
this.bufferFmt = resolveBufferGlFormat(gl, "rgba8");
|
|
367
455
|
this.initGeometry();
|
|
368
456
|
this.blackTexture = createEmptyTexture(gl, 1, 1);
|
|
369
457
|
gl.bindTexture(gl.TEXTURE_2D, this.blackTexture);
|
|
@@ -380,6 +468,42 @@ export class ShaderToyRuntime {
|
|
|
380
468
|
);
|
|
381
469
|
}
|
|
382
470
|
|
|
471
|
+
/** Format réellement utilisé pour les buffers A–D (après repli éventuel). */
|
|
472
|
+
get activeBufferFormat(): ShaderBufferFormat {
|
|
473
|
+
return this.bufferFmt.active;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Change le format des buffers A–D. Recrée les FBO si la taille est déjà connue.
|
|
478
|
+
* Retourne le format actif (peut différer si extension manquante).
|
|
479
|
+
*/
|
|
480
|
+
setBufferFormat(requested: ShaderBufferFormat): ShaderBufferFormat {
|
|
481
|
+
const next = resolveBufferGlFormat(this.gl, requested);
|
|
482
|
+
if (
|
|
483
|
+
next.active === this.bufferFmt.active &&
|
|
484
|
+
next.filter === this.bufferFmt.filter
|
|
485
|
+
) {
|
|
486
|
+
this.bufferFmt = next;
|
|
487
|
+
return next.active;
|
|
488
|
+
}
|
|
489
|
+
this.bufferFmt = next;
|
|
490
|
+
if (this.width >= 1 && this.height >= 1) {
|
|
491
|
+
for (const id of BUFFER_PASSES) {
|
|
492
|
+
const existing = this.buffers.get(id);
|
|
493
|
+
if (existing) destroyPingPong(this.gl, existing);
|
|
494
|
+
if (this.programs.has(id)) {
|
|
495
|
+
this.buffers.set(
|
|
496
|
+
id,
|
|
497
|
+
createPingPong(this.gl, this.width, this.height, this.bufferFmt),
|
|
498
|
+
);
|
|
499
|
+
} else {
|
|
500
|
+
this.buffers.delete(id);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return next.active;
|
|
505
|
+
}
|
|
506
|
+
|
|
383
507
|
/** True si le contexte a été perdu / déjà disposé. */
|
|
384
508
|
get isLost(): boolean {
|
|
385
509
|
return this.gl.isContextLost();
|
|
@@ -416,7 +540,10 @@ export class ShaderToyRuntime {
|
|
|
416
540
|
const existing = this.buffers.get(id);
|
|
417
541
|
if (existing) destroyPingPong(this.gl, existing);
|
|
418
542
|
if (this.programs.has(id)) {
|
|
419
|
-
this.buffers.set(
|
|
543
|
+
this.buffers.set(
|
|
544
|
+
id,
|
|
545
|
+
createPingPong(this.gl, w, h, this.bufferFmt),
|
|
546
|
+
);
|
|
420
547
|
}
|
|
421
548
|
}
|
|
422
549
|
}
|
|
@@ -458,7 +585,10 @@ export class ShaderToyRuntime {
|
|
|
458
585
|
if (pass !== "image" && this.width > 0) {
|
|
459
586
|
const prev = this.buffers.get(pass);
|
|
460
587
|
if (prev) destroyPingPong(this.gl, prev);
|
|
461
|
-
this.buffers.set(
|
|
588
|
+
this.buffers.set(
|
|
589
|
+
pass,
|
|
590
|
+
createPingPong(this.gl, this.width, this.height, this.bufferFmt),
|
|
591
|
+
);
|
|
462
592
|
}
|
|
463
593
|
}
|
|
464
594
|
|
|
@@ -471,6 +601,18 @@ export class ShaderToyRuntime {
|
|
|
471
601
|
}
|
|
472
602
|
}
|
|
473
603
|
|
|
604
|
+
/**
|
|
605
|
+
* Met à jour les bindings de canaux sans recompiler les programmes.
|
|
606
|
+
* Les URLs / clés `#élément` absentes des textures sont allouées à la demande
|
|
607
|
+
* (via `ensureTextures` / `ensureElementTexture` côté composant).
|
|
608
|
+
*/
|
|
609
|
+
setPassChannels(channels: Map<ShaderPassId, PassChannels>) {
|
|
610
|
+
if (this.gl.isContextLost()) {
|
|
611
|
+
throw new Error("WebGL context lost");
|
|
612
|
+
}
|
|
613
|
+
this.passChannels = channels;
|
|
614
|
+
}
|
|
615
|
+
|
|
474
616
|
async ensureTextures(urls: string[]) {
|
|
475
617
|
const unique = [...new Set(urls.filter(Boolean))];
|
|
476
618
|
await Promise.all(unique.map((url) => this.loadTexture(url)));
|
|
@@ -846,6 +988,7 @@ export class ShaderToyRuntime {
|
|
|
846
988
|
|
|
847
989
|
/**
|
|
848
990
|
* Lit un rectangle d’un buffer ping-pong (dernier write) ou de l’Image (`pass === "image"`).
|
|
991
|
+
* Les buffers flottants renvoient un `Float32Array` ; Image / rgba8 → `Uint8Array`.
|
|
849
992
|
*/
|
|
850
993
|
readBufferRect(
|
|
851
994
|
pass: ShaderPassId,
|
|
@@ -857,7 +1000,14 @@ export class ShaderToyRuntime {
|
|
|
857
1000
|
if (pass === "image") return this.readRect(x, y, w, h);
|
|
858
1001
|
const pp = this.buffers.get(pass);
|
|
859
1002
|
if (!pp) return null;
|
|
860
|
-
return this.readFramebufferRect(
|
|
1003
|
+
return this.readFramebufferRect(
|
|
1004
|
+
pp.framebuffers[pp.index],
|
|
1005
|
+
x,
|
|
1006
|
+
y,
|
|
1007
|
+
w,
|
|
1008
|
+
h,
|
|
1009
|
+
this.bufferFmt.float,
|
|
1010
|
+
);
|
|
861
1011
|
}
|
|
862
1012
|
|
|
863
1013
|
/**
|
|
@@ -876,6 +1026,7 @@ export class ShaderToyRuntime {
|
|
|
876
1026
|
y: number,
|
|
877
1027
|
w: number,
|
|
878
1028
|
h: number,
|
|
1029
|
+
asFloat = false,
|
|
879
1030
|
): ReadRectResult | null {
|
|
880
1031
|
const gl = this.gl;
|
|
881
1032
|
if (gl.isContextLost() || this.width < 1 || this.height < 1) return null;
|
|
@@ -895,9 +1046,15 @@ export class ShaderToyRuntime {
|
|
|
895
1046
|
if (px + pw > this.width) pw = this.width - px;
|
|
896
1047
|
if (py + ph > this.height) ph = this.height - py;
|
|
897
1048
|
if (pw < 1 || ph < 1) return null;
|
|
898
|
-
const data = new Uint8Array(pw * ph * 4);
|
|
899
1049
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
900
|
-
|
|
1050
|
+
let data: Uint8Array | Float32Array;
|
|
1051
|
+
if (asFloat) {
|
|
1052
|
+
data = new Float32Array(pw * ph * 4);
|
|
1053
|
+
gl.readPixels(px, py, pw, ph, gl.RGBA, gl.FLOAT, data);
|
|
1054
|
+
} else {
|
|
1055
|
+
data = new Uint8Array(pw * ph * 4);
|
|
1056
|
+
gl.readPixels(px, py, pw, ph, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
1057
|
+
}
|
|
901
1058
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
902
1059
|
return { x: px, y: py, width: pw, height: ph, data };
|
|
903
1060
|
}
|
|
@@ -44,8 +44,8 @@ Live 3D → shader (no JPEG): [Visual stack](#docs/_misc/visual-stack.md/visual-
|
|
|
44
44
|
|--------|-----------|
|
|
45
45
|
| Sortie d’un effet → entrée du suivant | `image-ch0="buffer-a"` (ou `buffer-b`… / `self`) — **même contexte GPU**, FBO ping-pong |
|
|
46
46
|
| Scene 3D / autre canvas → post | `channel0="#plateau"` — copie `texImage2D` chaque `frameSeq` |
|
|
47
|
-
| Params / sync UI | `dataProvider` → `uParam0…3` **et** canaux (`channel0`, `bufferACh0`…) — string URL, `#id`, ou `SonicMediaRef` (`url` / `element`) |
|
|
48
|
-
| Hors viewport | `release-offscreen` (défaut)
|
|
47
|
+
| Params / sync UI | `dataProvider` → `uParam0…3` **et** canaux (`channel0`, `bufferACh0`…) — string URL, `#id`, ou `SonicMediaRef` (`url` / `element`). Changer un canal ne recompile pas (rebind). |
|
|
48
|
+
| Hors viewport | `release-offscreen` : `auto` (défaut) libère sauf si une passe lit `self` ; `true` / `false` forcent |
|
|
49
49
|
|
|
50
50
|
Les passes intermédiaires sont **déjà offscreen** (FBO). Pas besoin d’un second `sonic-shader` invisible pour du multipass interne.
|
|
51
51
|
|
|
@@ -138,6 +138,22 @@ onExtract() {
|
|
|
138
138
|
|
|
139
139
|
Live Harris → patches → WebGPU: [Visual stack](#docs/_misc/visual-stack.md/visual-stack).
|
|
140
140
|
|
|
141
|
+
## Précision des buffers (`buffer-format`)
|
|
142
|
+
|
|
143
|
+
Par défaut, les buffers A–D sont en **RGBA8** (unsigned). Un tenseur Harris naïf (`Ix*Ix`, `Iy*Iy`, `Ix*Iy`) **perd le signe** de `IxIy` et sature les carrés → bords au lieu de coins.
|
|
144
|
+
|
|
145
|
+
| Valeur | Usage |
|
|
146
|
+
|--------|--------|
|
|
147
|
+
| `rgba8` (défaut) | Couleur / masques 0–1 |
|
|
148
|
+
| `rgba16f` | Calculs signés (Harris, gradients) — recommandé |
|
|
149
|
+
| `rgba32f` | Haute précision ; filtrage `NEAREST` si `OES_texture_float_linear` manque |
|
|
150
|
+
|
|
151
|
+
Repli auto en `rgba8` si `EXT_color_buffer_float` est absent (`console.warn` une fois). L’attribut reflété `active-buffer-format` indique le format réellement utilisé. La passe Image reste le canvas RGBA8.
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<sonic-shader buffer-format="rgba16f" buffer-a="…" …></sonic-shader>
|
|
155
|
+
```
|
|
156
|
+
|
|
141
157
|
## API typée (`SonicShaderConfig`)
|
|
142
158
|
|
|
143
159
|
| Champ / attribut | Type | Rôle |
|
|
@@ -145,6 +161,7 @@ Live Harris → patches → WebGPU: [Visual stack](#docs/_misc/visual-stack.md/v
|
|
|
145
161
|
| `image` | `string` | GLSL pass Image (**requis**, propriété JS) |
|
|
146
162
|
| `bufferA`…`bufferD` / `buffer-a`… | `string` | Passes multipass |
|
|
147
163
|
| `common` | `string` | GLSL partagé |
|
|
164
|
+
| `bufferFormat` / `buffer-format` | `rgba8` \| `rgba16f` \| `rgba32f` | Format buffers A–D (défaut `rgba8`) ; `active-buffer-format` reflète le format réellement utilisé |
|
|
148
165
|
| `channel0`…`channel3` | `string` | URLs, `#id` élément, ou refs buffers (toutes passes) |
|
|
149
166
|
| `imageCh0`… / `image-ch0`… | `string` | Override canal : URL, `#id`, `self`, `buffer-a`…, `none` |
|
|
150
167
|
| `play` | `boolean` | Boucle RAF (défaut `true`) |
|
|
@@ -170,7 +187,13 @@ Live Harris → patches → WebGPU: [Visual stack](#docs/_misc/visual-stack.md/v
|
|
|
170
187
|
| `extractDataProvider` / `extract-data-provider` | `string` | Publisher extract (défaut = out) |
|
|
171
188
|
| `aspectRatio` / `aspect-ratio` | `string` | Ratio CSS forcé |
|
|
172
189
|
| `dprMax` / `dpr-max` | `number` | Plafond DPR (défaut `2`) |
|
|
173
|
-
| `releaseOffscreen` / `release-offscreen` | `boolean` | Hors viewport : libère textures/FBO (garde le contexte). `dispose` + loseContext au disconnect
|
|
190
|
+
| `releaseOffscreen` / `release-offscreen` | `boolean` \| `"auto"` | Hors viewport : libère textures/FBO (garde le contexte). `auto` (défaut) : ne libère pas si une passe lit `self`. `dispose` + loseContext au disconnect |
|
|
191
|
+
|
|
192
|
+
## État et visibilité
|
|
193
|
+
|
|
194
|
+
Un shader qui garde un état via `self` (feedback frame précédente) n’est plus réinitialisé quand il sort de l’écran : avec `release-offscreen="auto"` (défaut), seuls le RAF et les vidéos sont mis en pause ; les FBO restent. Forcer `release-offscreen` (attribut booléen / `true`) restaure l’ancien comportement (libération hors viewport même avec `self`).
|
|
195
|
+
|
|
196
|
+
Changer `channel0…3` / `*-ch*` re-lie les textures **sans** recompiler ni vider les buffers.
|
|
174
197
|
|
|
175
198
|
## Markup
|
|
176
199
|
|
|
@@ -184,7 +207,7 @@ Live Harris → patches → WebGPU: [Visual stack](#docs/_misc/visual-stack.md/v
|
|
|
184
207
|
hover-speed="12"
|
|
185
208
|
play
|
|
186
209
|
play-on-hover
|
|
187
|
-
release-offscreen
|
|
210
|
+
release-offscreen="auto"
|
|
188
211
|
channel0="/docs/shader-samples/color.jpg"
|
|
189
212
|
></sonic-shader>
|
|
190
213
|
</template>
|