@supersoniks/concorde 4.9.97-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 +14621 -13823
- 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 +3236 -2865
- package/concorde-webgpu.bundle.js +43 -32
- package/concorde-webgpu.es.js +2977 -2618
- package/dist/concorde-3d.bundle.js +201 -152
- package/dist/concorde-3d.es.js +14621 -13823
- 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 +3236 -2865
- package/dist/concorde-webgpu.bundle.js +43 -32
- package/dist/concorde-webgpu.es.js +2977 -2618
- package/dist/js/core/components/functional/3d/3d.js +100 -2
- package/dist/js/core/components/functional/3d/3d.js.map +1 -1
- package/dist/js/core/components/functional/3d/constants.js +3 -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/frame-source.js +22 -0
- package/dist/js/core/components/functional/3d/frame-source.js.map +1 -0
- 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 +5 -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 +280 -31
- package/dist/js/core/components/functional/shader/gl-runtime.js.map +1 -1
- package/dist/js/core/components/functional/shader/shader.js +207 -61
- 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 +43 -1
- package/dist/js/core/components/functional/webgpu/webgpu.js.map +1 -1
- package/dist/js/core/utils/mediaRef.js +86 -0
- package/dist/js/core/utils/mediaRef.js.map +1 -1
- package/dist/types/core/components/functional/3d/3d.d.ts +31 -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/frame-source.d.ts +23 -0
- package/dist/types/core/components/functional/3d/frame-source.d.ts.map +1 -0
- 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 +3 -0
- 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 +16 -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 +53 -2
- package/dist/types/core/components/functional/shader/gl-runtime.d.ts.map +1 -1
- package/dist/types/core/components/functional/shader/shader.d.ts +41 -9
- 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 +35 -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 +11 -1
- package/dist/types/core/components/functional/webgpu/webgpu.d.ts.map +1 -1
- package/dist/types/core/utils/mediaRef.d.ts +34 -0
- package/dist/types/core/utils/mediaRef.d.ts.map +1 -1
- package/package.json +49 -1
- package/src/core/components/functional/3d/3d.md +46 -3
- package/src/core/components/functional/3d/3d.spec.ts +42 -0
- package/src/core/components/functional/3d/3d.ts +112 -3
- package/src/core/components/functional/3d/constants.ts +3 -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/frame-source.ts +30 -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 +8 -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 +14 -0
- package/src/core/components/functional/3d/updated.ts +17 -1
- package/src/core/components/functional/shader/gl-runtime.ts +374 -36
- package/src/core/components/functional/shader/shader.md +36 -11
- package/src/core/components/functional/shader/shader.spec.ts +350 -3
- package/src/core/components/functional/shader/shader.ts +236 -59
- package/src/core/components/functional/shader/shadertoy-prelude.ts +49 -5
- package/src/core/components/functional/shader/types.ts +92 -13
- 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 +48 -1
- package/src/core/utils/mediaRef.spec.ts +61 -0
- package/src/core/utils/mediaRef.ts +108 -0
- package/src/docs/_misc/visual-stack.md +37 -3
- package/src/docs/components/docs-demo-sources.ts +42 -0
- package/src/docs/docs.ts +3 -0
- package/src/docs/example/docs-3d-post-depth-demo.ts +68 -0
- package/src/docs/example/docs-3d-shader-live-demo.ts +98 -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 +2583 -863
- package/src/tsconfig.json +36 -0
- package/src/tsconfig.tsbuildinfo +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* - `url` : http(s) | blob: | data: (préféré pour les canaux shader)
|
|
5
5
|
* - `blob` : binaire (HF, upload) — le producteur publie aussi `url` quand possible
|
|
6
6
|
* - `blobUrl` : alias historique (extract shader)
|
|
7
|
+
* - `element` : sélecteur `#id` ou Element (pont live → canaux shader, ignoré par toMediaUrl)
|
|
7
8
|
*/
|
|
8
9
|
export type SonicMediaRef = {
|
|
9
10
|
url?: string;
|
|
@@ -12,7 +13,40 @@ export type SonicMediaRef = {
|
|
|
12
13
|
width?: number;
|
|
13
14
|
height?: number;
|
|
14
15
|
mime?: string;
|
|
16
|
+
/** Sélecteur `#id` ou référence Element (upload live, pas d’URL). */
|
|
17
|
+
element?: string | Element;
|
|
15
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Contrat minimal pour qu’un composant Concorde alimente un canal shader
|
|
21
|
+
* sans JPEG / blob (`channel0="#plateau"` ou `{ element }`).
|
|
22
|
+
*/
|
|
23
|
+
export type SonicFrameSource = {
|
|
24
|
+
/** Canvas lisible (preserveDrawingBuffer ou 2D). `null` tant que pas prêt. */
|
|
25
|
+
getFrameCanvas(): HTMLCanvasElement | null;
|
|
26
|
+
/** Incrémenté après chaque rendu effectif. */
|
|
27
|
+
readonly frameSeq: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Producteur de frames live qui peut rester actif hors viewport
|
|
31
|
+
* tant qu’un consommateur est enregistré (`sonic-3d` Lot 9).
|
|
32
|
+
*/
|
|
33
|
+
export type SonicFrameConsumerHost = {
|
|
34
|
+
registerFrameConsumer(token?: object): void;
|
|
35
|
+
unregisterFrameConsumer(token?: object): void;
|
|
36
|
+
};
|
|
37
|
+
export declare function isFrameSource(el: unknown): el is SonicFrameSource;
|
|
38
|
+
export declare function isFrameConsumerHost(el: unknown): el is SonicFrameConsumerHost;
|
|
39
|
+
/** Suit les `#id` résolus → register/unregister auprès du producteur. */
|
|
40
|
+
export declare class FrameConsumerRegistry {
|
|
41
|
+
private tokens;
|
|
42
|
+
sync(elements: Iterable<Element | null | undefined>): void;
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
/** Enregistre un Element sans id → clé `#__sonic_frame_N` (réutilisée). */
|
|
46
|
+
export declare function registerLiveElement(el: Element): string;
|
|
47
|
+
export declare function lookupLiveElement(key: string): Element | null;
|
|
48
|
+
/** Normalise `element` DP → clé canal (`#id` ou live). */
|
|
49
|
+
export declare function mediaElementKey(value: unknown): string | null;
|
|
16
50
|
/** Objet image sérialisé (sortie HF / extract) : Blob + dimensions. */
|
|
17
51
|
export declare function isMediaBlobObject(value: unknown): value is SonicMediaRef & {
|
|
18
52
|
blob: Blob;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaRef.d.ts","sourceRoot":"","sources":["../../../../src/core/utils/mediaRef.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mediaRef.d.ts","sourceRoot":"","sources":["../../../../src/core/utils/mediaRef.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,cAAc,IAAI,iBAAiB,GAAG,IAAI,CAAC;IAC3C,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,gBAAgB,CAMjE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,sBAAsB,CAO7E;AAED,yEAAyE;AACzE,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA8B;IAE5C,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAkBnD,KAAK;CAGN;AAQD,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQ7D;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAY7D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAWzC;AAaD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAqBT;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAQ5D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA6B/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supersoniks/concorde",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.98-visual-stack.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "",
|
|
@@ -178,6 +178,18 @@
|
|
|
178
178
|
"types": "./dist/types/core/components/functional/3d/frame-capture.d.ts",
|
|
179
179
|
"default": "./dist/js/core/components/functional/3d/frame-capture.js"
|
|
180
180
|
},
|
|
181
|
+
"./frame-source": {
|
|
182
|
+
"types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
|
|
183
|
+
"default": "./dist/js/core/components/functional/3d/frame-source.js"
|
|
184
|
+
},
|
|
185
|
+
"./functional/frame-source": {
|
|
186
|
+
"types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
|
|
187
|
+
"default": "./dist/js/core/components/functional/3d/frame-source.js"
|
|
188
|
+
},
|
|
189
|
+
"./functional/3d/frame-source": {
|
|
190
|
+
"types": "./dist/types/core/components/functional/3d/frame-source.d.ts",
|
|
191
|
+
"default": "./dist/js/core/components/functional/3d/frame-source.js"
|
|
192
|
+
},
|
|
181
193
|
"./gl": {
|
|
182
194
|
"types": "./dist/types/core/components/functional/3d/gl.d.ts",
|
|
183
195
|
"default": "./dist/js/core/components/functional/3d/gl.js"
|
|
@@ -310,6 +322,42 @@
|
|
|
310
322
|
"types": "./dist/types/core/components/functional/3d/pointer.d.ts",
|
|
311
323
|
"default": "./dist/js/core/components/functional/3d/pointer.js"
|
|
312
324
|
},
|
|
325
|
+
"./post-bind": {
|
|
326
|
+
"types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
|
|
327
|
+
"default": "./dist/js/core/components/functional/3d/post-bind.js"
|
|
328
|
+
},
|
|
329
|
+
"./functional/post-bind": {
|
|
330
|
+
"types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
|
|
331
|
+
"default": "./dist/js/core/components/functional/3d/post-bind.js"
|
|
332
|
+
},
|
|
333
|
+
"./functional/3d/post-bind": {
|
|
334
|
+
"types": "./dist/types/core/components/functional/3d/post-bind.d.ts",
|
|
335
|
+
"default": "./dist/js/core/components/functional/3d/post-bind.js"
|
|
336
|
+
},
|
|
337
|
+
"./post-channels": {
|
|
338
|
+
"types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
|
|
339
|
+
"default": "./dist/js/core/components/functional/3d/post-channels.js"
|
|
340
|
+
},
|
|
341
|
+
"./functional/post-channels": {
|
|
342
|
+
"types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
|
|
343
|
+
"default": "./dist/js/core/components/functional/3d/post-channels.js"
|
|
344
|
+
},
|
|
345
|
+
"./functional/3d/post-channels": {
|
|
346
|
+
"types": "./dist/types/core/components/functional/3d/post-channels.d.ts",
|
|
347
|
+
"default": "./dist/js/core/components/functional/3d/post-channels.js"
|
|
348
|
+
},
|
|
349
|
+
"./post-fx": {
|
|
350
|
+
"types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
|
|
351
|
+
"default": "./dist/js/core/components/functional/3d/post-fx.js"
|
|
352
|
+
},
|
|
353
|
+
"./functional/post-fx": {
|
|
354
|
+
"types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
|
|
355
|
+
"default": "./dist/js/core/components/functional/3d/post-fx.js"
|
|
356
|
+
},
|
|
357
|
+
"./functional/3d/post-fx": {
|
|
358
|
+
"types": "./dist/types/core/components/functional/3d/post-fx.d.ts",
|
|
359
|
+
"default": "./dist/js/core/components/functional/3d/post-fx.js"
|
|
360
|
+
},
|
|
313
361
|
"./props": {
|
|
314
362
|
"types": "./dist/types/core/components/functional/3d/props.d.ts",
|
|
315
363
|
"default": "./dist/js/core/components/functional/3d/props.js"
|
|
@@ -85,7 +85,9 @@ Le viewer lit `asset` ou `src` depuis le DP — la source (fetch, upload, HF) n
|
|
|
85
85
|
| `fit` | Fit bbox après load (défaut true) |
|
|
86
86
|
| `pick` | Survol highlight + clic → event `pick` + `hit*` ; double-clic = fit |
|
|
87
87
|
| `play` / `play-on-hover` | Boucle RAF |
|
|
88
|
-
| `
|
|
88
|
+
| `play-offscreen` | Garde RAF / runtime hors viewport (pont live `#id`) |
|
|
89
|
+
| `release-offscreen` | Dispose renderer hors viewport (défaut true) sauf si `play-offscreen` / consommateur live |
|
|
90
|
+
| `damping` | Inertie OrbitControls (défaut `0.08`, `0` = off) |
|
|
89
91
|
| `dpr-max` | Cap devicePixelRatio (défaut 2) |
|
|
90
92
|
| `dataProvider` / `out-data-provider` | Entrée / sortie |
|
|
91
93
|
| `out-interval` | Throttle publication ms (défaut 80) |
|
|
@@ -109,10 +111,51 @@ Le viewer lit `asset` ou `src` depuis le DP — la source (fetch, upload, HF) n
|
|
|
109
111
|
|
|
110
112
|
## Shader bridging
|
|
111
113
|
|
|
112
|
-
No shared WebGL context between `sonic-3d` and `sonic-shader`.
|
|
114
|
+
No shared WebGL context between `sonic-3d` and `sonic-shader`.
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
| Need | Bridge |
|
|
117
|
+
|------|--------|
|
|
118
|
+
| **Live post** | `channel0="#plateau"` on `sonic-shader` (or DP `{ element }`) — canvas copy each frame via `SonicFrameSource` |
|
|
119
|
+
| **Export / HF / freeze** | `frame-out` → `frameUrl` + `snapshot` (`SonicMediaRef` JPEG). Clone with `cloneMediaRef` before holding a frame |
|
|
115
120
|
|
|
116
121
|
```html
|
|
122
|
+
<!-- Live -->
|
|
123
|
+
<sonic-3d id="plateau" play auto-rotate …></sonic-3d>
|
|
124
|
+
<sonic-shader channel0="#plateau" image="…" play></sonic-shader>
|
|
125
|
+
|
|
126
|
+
<!-- Export / freeze -->
|
|
117
127
|
<sonic-3d frame-out out-data-provider="view3d" snapshot-interval="250" …></sonic-3d>
|
|
118
128
|
```
|
|
129
|
+
|
|
130
|
+
Demo: [Visual stack — 3D → shader live](#docs/_misc/visual-stack.md/visual-stack).
|
|
131
|
+
|
|
132
|
+
Un `sonic-shader` / `sonic-webgpu` qui pointe `#id` s’enregistre via `registerFrameConsumer` : le 3D **continue hors viewport** (pas de dispose). Option explicite : `play-offscreen`.
|
|
133
|
+
|
|
134
|
+
### Arrêt net
|
|
135
|
+
|
|
136
|
+
`el.stopMotion()` coupe `autoRotate` et l’inertie OrbitControls. Quand la caméra est immobile depuis ~8 frames : event `rest` + champ out `moving: 0|1`. Attribut `damping` (défaut `0.08`, `0` = off).
|
|
137
|
+
|
|
138
|
+
## Post FX (in-viewer)
|
|
139
|
+
|
|
140
|
+
When `post-image` is set, the scene renders to an offscreen target with a **depth texture**, then ShaderToy-style passes run inside the same Three context (`post-buffer-a`…`post-image`). Same GLSL as `sonic-shader` (`mainImage`, `iChannel0–3`, `uParam0–3`, `sonicLinearDepth`).
|
|
141
|
+
|
|
142
|
+
| Channel token | Source |
|
|
143
|
+
|---------------|--------|
|
|
144
|
+
| `scene` (default ch0) | Color render target |
|
|
145
|
+
| `depth` | Depth texture from the scene pass |
|
|
146
|
+
| `buffer-a`…`d` | Ping-pong buffers |
|
|
147
|
+
| URL / `#id` | External texture (same as shader live bridge) |
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
<sonic-3d
|
|
151
|
+
post-image="…GLSL…"
|
|
152
|
+
post-image-ch0="scene"
|
|
153
|
+
post-image-ch1="depth"
|
|
154
|
+
param0="0.5"
|
|
155
|
+
post-scale="1"
|
|
156
|
+
></sonic-3d>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Demo: [Visual stack — post depth edges](#docs/_misc/visual-stack.md/visual-stack).
|
|
160
|
+
|
|
161
|
+
Pick / orbit still use scene geometry; `frame-out` captures the **final** canvas after post.
|
|
@@ -169,3 +169,45 @@ describe("orbit.wheelAffectsZoom", () => {
|
|
|
169
169
|
expect(orbit.wheelAffectsZoom(atMaxZoom, 1)).toBe(true);
|
|
170
170
|
});
|
|
171
171
|
});
|
|
172
|
+
|
|
173
|
+
describe("wantsOffscreenPlay / frame consumers", () => {
|
|
174
|
+
it("playOffscreen ou consumers → true", async () => {
|
|
175
|
+
const { wantsOffscreenPlay } = await import("./playback");
|
|
176
|
+
expect(wantsOffscreenPlay({ playOffscreen: false, frameConsumerCount: 0 })).toBe(
|
|
177
|
+
false,
|
|
178
|
+
);
|
|
179
|
+
expect(wantsOffscreenPlay({ playOffscreen: true, frameConsumerCount: 0 })).toBe(
|
|
180
|
+
true,
|
|
181
|
+
);
|
|
182
|
+
expect(wantsOffscreenPlay({ playOffscreen: false, frameConsumerCount: 1 })).toBe(
|
|
183
|
+
true,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("registerFrameConsumer incrémente le compteur", async () => {
|
|
188
|
+
const el = TestUtils.bootstrap(
|
|
189
|
+
`<sonic-3d style="width:64px;height:64px"></sonic-3d>`,
|
|
190
|
+
true,
|
|
191
|
+
)[0] as Sonic3d;
|
|
192
|
+
await el.updateComplete;
|
|
193
|
+
expect(el.frameConsumerCount).toBe(0);
|
|
194
|
+
const token = {};
|
|
195
|
+
el.registerFrameConsumer(token);
|
|
196
|
+
expect(el.frameConsumerCount).toBe(1);
|
|
197
|
+
expect(el.playOffscreen).toBe(false);
|
|
198
|
+
el.unregisterFrameConsumer(token);
|
|
199
|
+
expect(el.frameConsumerCount).toBe(0);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("stopMotion coupe autoRotate", async () => {
|
|
203
|
+
const el = TestUtils.bootstrap(
|
|
204
|
+
`<sonic-3d auto-rotate style="width:64px;height:64px"></sonic-3d>`,
|
|
205
|
+
true,
|
|
206
|
+
)[0] as Sonic3d;
|
|
207
|
+
await el.updateComplete;
|
|
208
|
+
expect(el.autoRotate).toBe(true);
|
|
209
|
+
el.stopMotion();
|
|
210
|
+
expect(el.autoRotate).toBe(false);
|
|
211
|
+
expect(el.moving).toBe(0);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { html } from "lit";
|
|
2
2
|
import { customElement, state } from "lit/decorators.js";
|
|
3
3
|
import { PublisherManager } from "@supersoniks/concorde/utils";
|
|
4
|
+
import { ensure } from "./ensure";
|
|
4
5
|
import { formBind, type FormOrbit } from "./form-bind";
|
|
5
6
|
import { life } from "./life";
|
|
6
7
|
import { outPublish } from "./out-publish";
|
|
8
|
+
import { postBind } from "./post-bind";
|
|
7
9
|
import type { SceneHit } from "./pick";
|
|
8
10
|
import { playback } from "./playback";
|
|
9
11
|
import { pointer } from "./pointer";
|
|
@@ -13,6 +15,8 @@ import { sonic3dStyles } from "./styles";
|
|
|
13
15
|
import { propUpdate } from "./updated";
|
|
14
16
|
import { wire } from "./wire";
|
|
15
17
|
|
|
18
|
+
/** Frames sans mouvement avant `rest` / `moving=0`. */
|
|
19
|
+
const REST_STILL_FRAMES = 8;
|
|
16
20
|
/**
|
|
17
21
|
* Viewer 3D Concorde (Three.js) — glTF/GLB, caméra orbit/perspective/ortho,
|
|
18
22
|
* outputs dataProvider paresseux. Opt-in : `@supersoniks/concorde/3d`.
|
|
@@ -21,7 +25,7 @@ import { wire } from "./wire";
|
|
|
21
25
|
export class Sonic3d extends Sonic3dProps {
|
|
22
26
|
static styles = sonic3dStyles;
|
|
23
27
|
|
|
24
|
-
@state()
|
|
28
|
+
@state() errorMessage = "";
|
|
25
29
|
|
|
26
30
|
/** État runtime partagé avec les helpers (lu ici pour noUnusedLocals). */
|
|
27
31
|
runtime: SceneRuntime | null = null;
|
|
@@ -46,7 +50,22 @@ export class Sonic3d extends Sonic3dProps {
|
|
|
46
50
|
snapshotBusy = false;
|
|
47
51
|
frameUrls: string[] = [];
|
|
48
52
|
snapshotSeq = 0;
|
|
53
|
+
/** Contrat SonicFrameSource — incrémenté après chaque render. */
|
|
54
|
+
frameSeq = 0;
|
|
55
|
+
lastDrawAt = 0;
|
|
49
56
|
lastFormOrbit: FormOrbit | null = null;
|
|
57
|
+
/** Consommateurs live (`registerFrameConsumer`). */
|
|
58
|
+
private frameConsumers = new Set<object>();
|
|
59
|
+
/** 1 si caméra en mouvement, 0 sinon (publié en out). */
|
|
60
|
+
moving = 0;
|
|
61
|
+
private stillFrames = 0;
|
|
62
|
+
private lastMotionYaw = NaN;
|
|
63
|
+
private lastMotionPitch = NaN;
|
|
64
|
+
private lastMotionDist = NaN;
|
|
65
|
+
|
|
66
|
+
get frameConsumerCount() {
|
|
67
|
+
return this.frameConsumers.size;
|
|
68
|
+
}
|
|
50
69
|
|
|
51
70
|
onFormMutation = () => {
|
|
52
71
|
const data = (this.formPublisher?.get() ?? {}) as Record<string, unknown>;
|
|
@@ -135,8 +154,93 @@ export class Sonic3d extends Sonic3dProps {
|
|
|
135
154
|
private drawFrame(now: number) {
|
|
136
155
|
if (!this.runtime || !this.ready) return;
|
|
137
156
|
this.frame++;
|
|
157
|
+
const time = (now - this.startTime) / 1000;
|
|
158
|
+
const timeDelta = Math.max(0, (now - this.lastDrawAt) / 1000);
|
|
159
|
+
this.lastDrawAt = now;
|
|
160
|
+
postBind.syncFrame(this as never, time, timeDelta, this.frame);
|
|
138
161
|
this.runtime.render();
|
|
139
|
-
|
|
162
|
+
this.frameSeq++;
|
|
163
|
+
this.trackMotion(time);
|
|
164
|
+
outPublish.maybePublish(this as never, now, time);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Enregistre un consommateur live (`sonic-shader` / `sonic-webgpu` via `#id`).
|
|
169
|
+
* Garde le RAF hors viewport tant qu’il reste au moins un token.
|
|
170
|
+
*/
|
|
171
|
+
registerFrameConsumer(token: object = {}) {
|
|
172
|
+
this.frameConsumers.add(token);
|
|
173
|
+
ensure.syncVisibility(this as never);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
unregisterFrameConsumer(token: object = {}) {
|
|
177
|
+
this.frameConsumers.delete(token);
|
|
178
|
+
ensure.syncVisibility(this as never);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Coupe `autoRotate` + inertie OrbitControls.
|
|
183
|
+
* Émet `rest` si la caméra était en mouvement.
|
|
184
|
+
*/
|
|
185
|
+
stopMotion() {
|
|
186
|
+
this.autoRotate = false;
|
|
187
|
+
this.runtime?.stopMotion();
|
|
188
|
+
this.stillFrames = REST_STILL_FRAMES;
|
|
189
|
+
if (this.moving !== 0) {
|
|
190
|
+
this.moving = 0;
|
|
191
|
+
this.dispatchEvent(
|
|
192
|
+
new CustomEvent("rest", {
|
|
193
|
+
detail: { moving: 0 },
|
|
194
|
+
bubbles: true,
|
|
195
|
+
composed: true,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
this.syncPlayback();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private trackMotion(time: number) {
|
|
203
|
+
if (!this.runtime) return;
|
|
204
|
+
const snap = this.runtime.snapshot({
|
|
205
|
+
time,
|
|
206
|
+
frame: this.frame,
|
|
207
|
+
hovering: this.hovering ? 1 : 0,
|
|
208
|
+
pointerX: this.pointerX,
|
|
209
|
+
pointerY: this.pointerY,
|
|
210
|
+
hit: this.lastHit,
|
|
211
|
+
});
|
|
212
|
+
const eps = 1e-5;
|
|
213
|
+
const still =
|
|
214
|
+
Number.isFinite(this.lastMotionYaw) &&
|
|
215
|
+
Math.abs(snap.yaw - this.lastMotionYaw) < eps &&
|
|
216
|
+
Math.abs(snap.pitch - this.lastMotionPitch) < eps &&
|
|
217
|
+
Math.abs(snap.distance - this.lastMotionDist) < eps &&
|
|
218
|
+
!this.autoRotate;
|
|
219
|
+
this.lastMotionYaw = snap.yaw;
|
|
220
|
+
this.lastMotionPitch = snap.pitch;
|
|
221
|
+
this.lastMotionDist = snap.distance;
|
|
222
|
+
if (still) {
|
|
223
|
+
this.stillFrames++;
|
|
224
|
+
if (this.stillFrames >= REST_STILL_FRAMES && this.moving !== 0) {
|
|
225
|
+
this.moving = 0;
|
|
226
|
+
this.dispatchEvent(
|
|
227
|
+
new CustomEvent("rest", {
|
|
228
|
+
detail: { moving: 0, yaw: snap.yaw, pitch: snap.pitch },
|
|
229
|
+
bubbles: true,
|
|
230
|
+
composed: true,
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
this.stillFrames = 0;
|
|
236
|
+
this.moving = 1;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Contrat SonicFrameSource — canvas Three (preserveDrawingBuffer). */
|
|
241
|
+
getFrameCanvas(): HTMLCanvasElement | null {
|
|
242
|
+
if (!this.runtime || !this.ready) return null;
|
|
243
|
+
return this.runtime.renderer.domElement;
|
|
140
244
|
}
|
|
141
245
|
|
|
142
246
|
onPointerMove = (e: PointerEvent) => pointer.onMove(this as never, e);
|
|
@@ -161,12 +265,17 @@ export type {
|
|
|
161
265
|
Sonic3dConfig,
|
|
162
266
|
Sonic3dOutput,
|
|
163
267
|
} from "./types";
|
|
164
|
-
export type { SonicMediaRef } from "@supersoniks/concorde/core/utils/mediaRef";
|
|
268
|
+
export type { SonicMediaRef, SonicFrameSource, SonicFrameConsumerHost } from "@supersoniks/concorde/core/utils/mediaRef";
|
|
165
269
|
export {
|
|
166
270
|
toMediaUrl,
|
|
167
271
|
cloneMediaRef,
|
|
272
|
+
isFrameSource,
|
|
273
|
+
isFrameConsumerHost,
|
|
274
|
+
FrameConsumerRegistry,
|
|
168
275
|
} from "@supersoniks/concorde/core/utils/mediaRef";
|
|
276
|
+
export { wantsOffscreenPlay } from "./playback";
|
|
169
277
|
export { frameCapture } from "./frame-capture";
|
|
278
|
+
export { frameSource } from "./frame-source";
|
|
170
279
|
|
|
171
280
|
declare global {
|
|
172
281
|
interface HTMLElementTagNameMap {
|
|
@@ -93,6 +93,9 @@ export const OUT_CONSUMER_KEYS = [
|
|
|
93
93
|
/** Canvas → ObjectURL (SonicMediaRef) pour chaînage shader. */
|
|
94
94
|
"frameUrl",
|
|
95
95
|
"snapshot",
|
|
96
|
+
/** Pont live → sonic-shader (`{ element }`), publié une fois au ready. */
|
|
97
|
+
"frameSource",
|
|
98
|
+
"moving",
|
|
96
99
|
] as const;
|
|
97
100
|
|
|
98
101
|
export type Sonic3dFormControlKey = (typeof FORM_CONTROL_KEYS)[number];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sonic3dCameraMode } from "./constants";
|
|
2
2
|
import { parse } from "./parse";
|
|
3
|
-
import { playback } from "./playback";
|
|
3
|
+
import { playback, wantsOffscreenPlay } from "./playback";
|
|
4
4
|
import { reload } from "./reload";
|
|
5
5
|
import { SceneRuntime } from "./scene-runtime";
|
|
6
6
|
|
|
@@ -16,10 +16,13 @@ export type EnsureHost = {
|
|
|
16
16
|
maxDistance: number;
|
|
17
17
|
minPolar: number;
|
|
18
18
|
maxPolar: number;
|
|
19
|
+
damping: number;
|
|
19
20
|
loadToken: number;
|
|
20
21
|
ready: boolean;
|
|
21
22
|
inView: boolean;
|
|
22
23
|
releaseOffscreen: boolean;
|
|
24
|
+
playOffscreen: boolean;
|
|
25
|
+
frameConsumerCount: number;
|
|
23
26
|
getBoundingClientRect: () => DOMRect;
|
|
24
27
|
clientWidth: number;
|
|
25
28
|
clientHeight: number;
|
|
@@ -52,6 +55,7 @@ async function ensureRuntime(host: EnsureHost) {
|
|
|
52
55
|
minPolar: reload.degToRad(host.minPolar),
|
|
53
56
|
maxPolar: reload.degToRad(host.maxPolar),
|
|
54
57
|
});
|
|
58
|
+
host.runtime.setDamping(host.damping);
|
|
55
59
|
reload.applyOrbitLimits(host as never);
|
|
56
60
|
syncSize(host);
|
|
57
61
|
await reload.run(host as never);
|
|
@@ -71,11 +75,15 @@ function releaseRuntime(host: EnsureHost) {
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
function syncVisibility(host: EnsureHost) {
|
|
74
|
-
|
|
78
|
+
const keepOffscreen = wantsOffscreenPlay(host);
|
|
79
|
+
if (!host.inView && host.releaseOffscreen && !keepOffscreen) {
|
|
75
80
|
releaseRuntime(host);
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
78
|
-
if (
|
|
83
|
+
if (
|
|
84
|
+
(!host.runtime || host.runtime.isLost) &&
|
|
85
|
+
(host.inView || keepOffscreen || !host.releaseOffscreen)
|
|
86
|
+
) {
|
|
79
87
|
void ensureRuntime(host);
|
|
80
88
|
return;
|
|
81
89
|
}
|
|
@@ -2,6 +2,7 @@ import { ORBIT_CHANGE_EPSILON } from "./constants";
|
|
|
2
2
|
import { formFields } from "./form-fields";
|
|
3
3
|
import { parse } from "./parse";
|
|
4
4
|
import { playback } from "./playback";
|
|
5
|
+
import { postBind } from "./post-bind";
|
|
5
6
|
import { reload } from "./reload";
|
|
6
7
|
import type { SceneRuntime } from "./runtime";
|
|
7
8
|
import type { Sonic3dAsset, Sonic3dCameraMode } from "./types";
|
|
@@ -91,6 +92,7 @@ function applyFormParams(host: FormBindHost, data: Record<string, unknown>) {
|
|
|
91
92
|
|
|
92
93
|
formFields.pushToRuntime(host, data);
|
|
93
94
|
applyFormOrbit(host, data);
|
|
95
|
+
postBind.applyFormPost(host as never, data);
|
|
94
96
|
if (needReload) void reload.run(host as never);
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { publisherFieldHasListener } from "./publisher-listen";
|
|
2
|
+
|
|
3
|
+
type OutPublisherLike = {
|
|
4
|
+
_proxies_?: Map<string, { hasListener?: () => boolean }>;
|
|
5
|
+
frameSource?: { set: (v: unknown) => void };
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type FrameSourceHost = {
|
|
9
|
+
outPublisher: OutPublisherLike | null;
|
|
10
|
+
bindOutPublisher: () => void;
|
|
11
|
+
ready: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Publie `{ element: host }` une fois au ready si un lecteur écoute `frameSource`.
|
|
16
|
+
* Aucun coût pendant l’animation.
|
|
17
|
+
*/
|
|
18
|
+
function maybePublish(host: FrameSourceHost & Element) {
|
|
19
|
+
if (!host.ready) return;
|
|
20
|
+
if (!host.outPublisher) host.bindOutPublisher();
|
|
21
|
+
const pub = host.outPublisher;
|
|
22
|
+
if (!pub || !publisherFieldHasListener(pub, "frameSource")) return;
|
|
23
|
+
try {
|
|
24
|
+
pub.frameSource?.set({ element: host });
|
|
25
|
+
} catch {
|
|
26
|
+
/* leaf manquant */
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const frameSource = { maybePublish } as const;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PublisherManager } from "@supersoniks/concorde/utils";
|
|
2
2
|
import { ensure } from "./ensure";
|
|
3
3
|
import { formBind } from "./form-bind";
|
|
4
|
+
import { wantsOffscreenPlay } from "./playback";
|
|
4
5
|
import type { SceneRuntime } from "./runtime";
|
|
5
6
|
|
|
6
7
|
/** Host Lit pour cycle de vie / observers / bind DP. */
|
|
@@ -9,6 +10,8 @@ export type LifeHost = {
|
|
|
9
10
|
dataProvider: string;
|
|
10
11
|
outDataProvider: string;
|
|
11
12
|
releaseOffscreen: boolean;
|
|
13
|
+
playOffscreen: boolean;
|
|
14
|
+
frameConsumerCount: number;
|
|
12
15
|
inView: boolean;
|
|
13
16
|
runtime: SceneRuntime | null;
|
|
14
17
|
camera: string;
|
|
@@ -86,7 +89,7 @@ function firstUpdated(host: LifeHost) {
|
|
|
86
89
|
bindFormProvider(host);
|
|
87
90
|
bindOutPublisher(host);
|
|
88
91
|
requestAnimationFrame(() => {
|
|
89
|
-
if (host.inView || !host.releaseOffscreen) {
|
|
92
|
+
if (host.inView || !host.releaseOffscreen || wantsOffscreenPlay(host)) {
|
|
90
93
|
void ensure.ensureRuntime(host as never);
|
|
91
94
|
}
|
|
92
95
|
});
|
|
@@ -33,6 +33,7 @@ export type OutPublishHost = {
|
|
|
33
33
|
pointerY: number;
|
|
34
34
|
lastHit: SceneHit | null;
|
|
35
35
|
fov: number;
|
|
36
|
+
moving: number;
|
|
36
37
|
formPublisher: OutPublisherLike | null;
|
|
37
38
|
bindOutPublisher: () => void;
|
|
38
39
|
dispatchEvent: (event: Event) => boolean;
|
|
@@ -64,7 +65,7 @@ function shouldSkipFormControlOutWrite(
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
function buildOutput(host: OutPublishHost, time: number): Sonic3dOutput {
|
|
67
|
-
|
|
68
|
+
const base =
|
|
68
69
|
host.runtime?.snapshot({
|
|
69
70
|
time,
|
|
70
71
|
frame: host.frame,
|
|
@@ -91,8 +92,8 @@ function buildOutput(host: OutPublishHost, time: number): Sonic3dOutput {
|
|
|
91
92
|
distance: 0,
|
|
92
93
|
yaw: 0,
|
|
93
94
|
pitch: 0,
|
|
94
|
-
}
|
|
95
|
-
|
|
95
|
+
};
|
|
96
|
+
return { ...base, moving: host.moving };
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function writePayload(host: OutPublishHost, payload: Sonic3dOutput) {
|
|
@@ -8,10 +8,21 @@ export type PlaybackHost = {
|
|
|
8
8
|
playOnHover: boolean;
|
|
9
9
|
hovering: boolean;
|
|
10
10
|
autoRotate: boolean;
|
|
11
|
+
playOffscreen: boolean;
|
|
12
|
+
/** Nombre de consommateurs live (`registerFrameConsumer`). */
|
|
13
|
+
frameConsumerCount: number;
|
|
11
14
|
raf: number;
|
|
12
15
|
drawFrame: (now: number) => void;
|
|
13
16
|
};
|
|
14
17
|
|
|
18
|
+
/** Hors écran : jouer si `play-offscreen` ou au moins un consommateur live. */
|
|
19
|
+
export function wantsOffscreenPlay(host: {
|
|
20
|
+
playOffscreen: boolean;
|
|
21
|
+
frameConsumerCount: number;
|
|
22
|
+
}): boolean {
|
|
23
|
+
return !!host.playOffscreen || host.frameConsumerCount > 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
function prefersReducedMotion(): boolean {
|
|
16
27
|
return (
|
|
17
28
|
typeof matchMedia === "function" &&
|
|
@@ -22,7 +33,7 @@ function prefersReducedMotion(): boolean {
|
|
|
22
33
|
function shouldAnimate(host: PlaybackHost): boolean {
|
|
23
34
|
if (!host.play) return false;
|
|
24
35
|
if (!host.ready) return false;
|
|
25
|
-
if (!host.inView) return false;
|
|
36
|
+
if (!host.inView && !wantsOffscreenPlay(host)) return false;
|
|
26
37
|
if (!host.runtime) return false;
|
|
27
38
|
if (prefersReducedMotion()) return false;
|
|
28
39
|
if (host.playOnHover) return host.hovering;
|
|
@@ -64,6 +75,7 @@ function syncPlayback(host: PlaybackHost, loop: () => void) {
|
|
|
64
75
|
export const playback = {
|
|
65
76
|
prefersReducedMotion,
|
|
66
77
|
shouldAnimate,
|
|
78
|
+
wantsOffscreenPlay,
|
|
67
79
|
syncAutoRotate,
|
|
68
80
|
syncPlayback,
|
|
69
81
|
stopLoop,
|