@volter/editor-threejs 0.5.212 → 0.5.214
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/dist-node/serving.mjs +4 -20
- package/package.json +3 -5
- package/serving/animation-live-module.ts +4 -20
- package/src/animation/live-mixers.ts +1 -9
- package/src/kit/animation/three-clips-subject.ts +10 -50
- package/src/kit/authoring/component-instance-root.ts +2 -3
- package/src/kit/components/Object3DDocumentToolbar.tsx +1 -2
- package/src/kit/components/Object3DDocumentViewport.tsx +2 -4
- package/src/kit/components/StageHost.tsx +78 -208
- package/src/kit/components/ViewportViewMenu.tsx +1 -1
- package/src/kit/components/asset-viewers/EntityModelDocument.tsx +2 -4
- package/src/kit/components/asset-viewers/EnvironmentAssetDocument.tsx +4 -6
- package/src/kit/components/asset-viewers/LiveModuleDocument.tsx +7 -31
- package/src/kit/components/asset-viewers/LutAssetDocument.tsx +4 -6
- package/src/kit/components/asset-viewers/ModelAssetDocument.tsx +4 -8
- package/src/kit/components/asset-viewers/Object3DPreview.tsx +3 -19
- package/src/kit/components/asset-viewers/QuarksAssetDocument.tsx +4 -8
- package/src/kit/components/asset-viewers/ShaderAssetDocument.tsx +37 -55
- package/src/kit/components/object3d-contribution-surfaces.tsx +4 -2
- package/src/kit/components/stage-transform-tools.tsx +1 -2
- package/src/kit/editor-shell-store.ts +2 -26
- package/src/kit/editor-viewport.ts +11 -1
- package/src/kit/three-integration.ts +0 -2
- package/src/viewport-api.ts +4 -16
- package/src/viewport-door.ts +2 -38
- package/src/kit/components/world-root-binding.ts +0 -59
- package/src/kit/play-camera-flight.ts +0 -291
- package/src/kit/scene-document-viewport.ts +0 -51
- package/src/kit/viewport-surface-status.tsx +0 -44
package/dist-node/serving.mjs
CHANGED
|
@@ -5,17 +5,7 @@ import ts from "typescript";
|
|
|
5
5
|
// serving/animation-live-module.ts
|
|
6
6
|
var ANIMATION_LIVE_MODULE_ID = "virtual:volter-three-animation-live";
|
|
7
7
|
var animationLiveModuleSource = `
|
|
8
|
-
const live = (globalThis[Symbol.for('volter.three.animation.live')] ??= {
|
|
9
|
-
mixers: new Map(),
|
|
10
|
-
listeners: new Set(),
|
|
11
|
-
version: 0,
|
|
12
|
-
});
|
|
13
|
-
function notify() {
|
|
14
|
-
live.version++;
|
|
15
|
-
for (const listener of live.listeners) {
|
|
16
|
-
try { listener(); } catch (error) { console.error(error); }
|
|
17
|
-
}
|
|
18
|
-
}
|
|
8
|
+
const live = (globalThis[Symbol.for('volter.three.animation.live')] ??= { mixers: new Map() });
|
|
19
9
|
export function __volterMixer(mixer, key) {
|
|
20
10
|
if (!mixer || typeof mixer.clipAction !== 'function') return mixer;
|
|
21
11
|
if (mixer.__volterMixerKey) return mixer;
|
|
@@ -28,10 +18,8 @@ export function __volterMixer(mixer, key) {
|
|
|
28
18
|
if (action) {
|
|
29
19
|
const played = action.getClip();
|
|
30
20
|
const target = action.getRoot();
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
if (target && !entry.roots.has(target)) { entry.roots.add(target); changed = true; }
|
|
34
|
-
if (changed) notify();
|
|
21
|
+
if (played && !entry.clips.has(played.name)) entry.clips.set(played.name, played);
|
|
22
|
+
if (target) entry.roots.add(target);
|
|
35
23
|
}
|
|
36
24
|
return action;
|
|
37
25
|
};
|
|
@@ -40,10 +28,8 @@ export function __volterMixer(mixer, key) {
|
|
|
40
28
|
const result = uncacheRoot.call(this, root);
|
|
41
29
|
entry.roots.delete(root);
|
|
42
30
|
if (root === this.getRoot()) live.mixers.delete(this);
|
|
43
|
-
notify();
|
|
44
31
|
return result;
|
|
45
32
|
};
|
|
46
|
-
notify();
|
|
47
33
|
return mixer;
|
|
48
34
|
}
|
|
49
35
|
/** drei's \`useAnimations\` result: its mixer, and the clips it was handed. */
|
|
@@ -52,11 +38,9 @@ export function __volterAnimations(result, key) {
|
|
|
52
38
|
__volterMixer(result.mixer, key);
|
|
53
39
|
const entry = live.mixers.get(result.mixer);
|
|
54
40
|
if (entry && Array.isArray(result.clips)) {
|
|
55
|
-
let changed = false;
|
|
56
41
|
for (const clip of result.clips) {
|
|
57
|
-
if (clip && clip.name && !entry.clips.has(clip.name))
|
|
42
|
+
if (clip && clip.name && !entry.clips.has(clip.name)) entry.clips.set(clip.name, clip);
|
|
58
43
|
}
|
|
59
|
-
if (changed) notify();
|
|
60
44
|
}
|
|
61
45
|
return result;
|
|
62
46
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@volter/editor-threejs",
|
|
3
3
|
"publisher": "volter",
|
|
4
4
|
"author": "Volter AI, Inc.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.214",
|
|
6
6
|
"engines": {
|
|
7
7
|
"vscode": "*"
|
|
8
8
|
},
|
|
@@ -83,7 +83,6 @@
|
|
|
83
83
|
"./kit/components/stage-transform-chrome": "./src/kit/components/stage-transform-chrome.ts",
|
|
84
84
|
"./kit/components/stage-transform-tools": "./src/kit/components/stage-transform-tools.tsx",
|
|
85
85
|
"./kit/components/standard-viewport-dressing": "./src/kit/components/standard-viewport-dressing.ts",
|
|
86
|
-
"./kit/components/world-root-binding": "./src/kit/components/world-root-binding.ts",
|
|
87
86
|
"./kit/constraint-helper": "./src/kit/constraint-helper.ts",
|
|
88
87
|
"./kit/editor-shell-store": "./src/kit/editor-shell-store.ts",
|
|
89
88
|
"./kit/editor-viewport": "./src/kit/editor-viewport.ts",
|
|
@@ -95,7 +94,6 @@
|
|
|
95
94
|
"./kit/model-thumbnail": "./src/kit/model-thumbnail.ts",
|
|
96
95
|
"./kit/projection/three": "./src/kit/projection/three.ts",
|
|
97
96
|
"./kit/reflection-probe-helper": "./src/kit/reflection-probe-helper.ts",
|
|
98
|
-
"./kit/scene-document-viewport": "./src/kit/scene-document-viewport.ts",
|
|
99
97
|
"./kit/scene-framing": "./src/kit/scene-framing.ts",
|
|
100
98
|
"./kit/scene-view-fog": "./src/kit/scene-view-fog.ts",
|
|
101
99
|
"./kit/spatial-handle-visuals": "./src/kit/spatial-handle-visuals.ts",
|
|
@@ -416,11 +414,11 @@
|
|
|
416
414
|
}
|
|
417
415
|
},
|
|
418
416
|
"dependencies": {
|
|
419
|
-
"@volter/project": "0.5.
|
|
417
|
+
"@volter/project": "0.5.214",
|
|
420
418
|
"@sparkjsdev/spark": "^2.1.0",
|
|
421
419
|
"three.quarks": "^0.16.0",
|
|
422
420
|
"typescript": "5.9.3",
|
|
423
|
-
"@volter/sdk": "0.5.
|
|
421
|
+
"@volter/sdk": "0.5.214",
|
|
424
422
|
"postprocessing": "6.39.5",
|
|
425
423
|
"quarks.core": "0.16.0",
|
|
426
424
|
"@fortawesome/free-solid-svg-icons": "^7.2.0"
|
|
@@ -12,17 +12,7 @@
|
|
|
12
12
|
export const ANIMATION_LIVE_MODULE_ID = 'virtual:volter-three-animation-live';
|
|
13
13
|
|
|
14
14
|
export const animationLiveModuleSource = `
|
|
15
|
-
const live = (globalThis[Symbol.for('volter.three.animation.live')] ??= {
|
|
16
|
-
mixers: new Map(),
|
|
17
|
-
listeners: new Set(),
|
|
18
|
-
version: 0,
|
|
19
|
-
});
|
|
20
|
-
function notify() {
|
|
21
|
-
live.version++;
|
|
22
|
-
for (const listener of live.listeners) {
|
|
23
|
-
try { listener(); } catch (error) { console.error(error); }
|
|
24
|
-
}
|
|
25
|
-
}
|
|
15
|
+
const live = (globalThis[Symbol.for('volter.three.animation.live')] ??= { mixers: new Map() });
|
|
26
16
|
export function __volterMixer(mixer, key) {
|
|
27
17
|
if (!mixer || typeof mixer.clipAction !== 'function') return mixer;
|
|
28
18
|
if (mixer.__volterMixerKey) return mixer;
|
|
@@ -35,10 +25,8 @@ export function __volterMixer(mixer, key) {
|
|
|
35
25
|
if (action) {
|
|
36
26
|
const played = action.getClip();
|
|
37
27
|
const target = action.getRoot();
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
if (target && !entry.roots.has(target)) { entry.roots.add(target); changed = true; }
|
|
41
|
-
if (changed) notify();
|
|
28
|
+
if (played && !entry.clips.has(played.name)) entry.clips.set(played.name, played);
|
|
29
|
+
if (target) entry.roots.add(target);
|
|
42
30
|
}
|
|
43
31
|
return action;
|
|
44
32
|
};
|
|
@@ -47,10 +35,8 @@ export function __volterMixer(mixer, key) {
|
|
|
47
35
|
const result = uncacheRoot.call(this, root);
|
|
48
36
|
entry.roots.delete(root);
|
|
49
37
|
if (root === this.getRoot()) live.mixers.delete(this);
|
|
50
|
-
notify();
|
|
51
38
|
return result;
|
|
52
39
|
};
|
|
53
|
-
notify();
|
|
54
40
|
return mixer;
|
|
55
41
|
}
|
|
56
42
|
/** drei's \`useAnimations\` result: its mixer, and the clips it was handed. */
|
|
@@ -59,11 +45,9 @@ export function __volterAnimations(result, key) {
|
|
|
59
45
|
__volterMixer(result.mixer, key);
|
|
60
46
|
const entry = live.mixers.get(result.mixer);
|
|
61
47
|
if (entry && Array.isArray(result.clips)) {
|
|
62
|
-
let changed = false;
|
|
63
48
|
for (const clip of result.clips) {
|
|
64
|
-
if (clip && clip.name && !entry.clips.has(clip.name))
|
|
49
|
+
if (clip && clip.name && !entry.clips.has(clip.name)) entry.clips.set(clip.name, clip);
|
|
65
50
|
}
|
|
66
|
-
if (changed) notify();
|
|
67
51
|
}
|
|
68
52
|
return result;
|
|
69
53
|
}
|
|
@@ -19,15 +19,13 @@ export interface LiveMixer {
|
|
|
19
19
|
|
|
20
20
|
interface Registry {
|
|
21
21
|
readonly mixers: Map<unknown, LiveMixer>;
|
|
22
|
-
readonly listeners: Set<() => void>;
|
|
23
|
-
version: number;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
const REGISTRY = Symbol.for('volter.three.animation.live');
|
|
27
25
|
|
|
28
26
|
function registry(): Registry {
|
|
29
27
|
const holder = globalThis as unknown as Record<symbol, Registry | undefined>;
|
|
30
|
-
return (holder[REGISTRY] ??= { mixers: new Map()
|
|
28
|
+
return (holder[REGISTRY] ??= { mixers: new Map() });
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
/** The live mixer that animates `object`, or null when the game animates it with none. */
|
|
@@ -43,9 +41,3 @@ export function liveMixerFor(object: THREE.Object3D): LiveMixer | null {
|
|
|
43
41
|
export function liveMixers(): LiveMixer[] {
|
|
44
42
|
return [...registry().mixers.values()];
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
export function subscribeLiveMixers(listener: () => void): () => void {
|
|
48
|
-
const listeners = registry().listeners;
|
|
49
|
-
listeners.add(listener);
|
|
50
|
-
return () => listeners.delete(listener);
|
|
51
|
-
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* reaches clips loaded through useGLTF and useAnimations. Subjects seek through
|
|
6
6
|
* AnimationMixer.setTime; the transport owns playback position.
|
|
7
7
|
*/
|
|
8
|
-
import { liveMixerFor
|
|
8
|
+
import { liveMixerFor } from '@volter/editor-threejs/animation/live-mixers';
|
|
9
9
|
import * as THREE from 'three';
|
|
10
10
|
import type { StageTransport } from '@volter/sdk/animation/stage-transport';
|
|
11
11
|
|
|
@@ -104,62 +104,22 @@ function attachInspected(object: THREE.Object3D, transport: StageTransport): (()
|
|
|
104
104
|
return detach;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/**
|
|
108
|
-
* One scan of a mounted root, and the handle that keeps it current.
|
|
109
|
-
*
|
|
110
|
-
* `refresh()` DIFFS by object uuid rather than re-attaching: a rescan of an
|
|
111
|
-
* unchanged tree must not detach and re-attach every subject, because that
|
|
112
|
-
* would reset which clip each one is showing under a person's hands. The
|
|
113
|
-
* caller decides when to rescan (the stage host throttles it and skips it
|
|
114
|
-
* while the world is driving time).
|
|
115
|
-
*/
|
|
107
|
+
/** One scan of a mounted root, and the handle that detaches what it attached. */
|
|
116
108
|
export interface ClipSubjectScan {
|
|
117
|
-
refresh(): void;
|
|
118
109
|
dispose(): void;
|
|
119
110
|
}
|
|
120
111
|
|
|
121
|
-
export function scanClipSubjects(
|
|
122
|
-
root: THREE.Object3D | (() => THREE.Object3D | null),
|
|
123
|
-
transport: StageTransport,
|
|
124
|
-
options: {
|
|
125
|
-
/** Rescan when the game registers or drops a mixer. */
|
|
126
|
-
readonly rescanOnLiveMixers?: boolean;
|
|
127
|
-
} = {},
|
|
128
|
-
): ClipSubjectScan {
|
|
112
|
+
export function scanClipSubjects(root: THREE.Object3D, transport: StageTransport): ClipSubjectScan {
|
|
129
113
|
const attached = new Map<string, () => void>();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (attached.has(object.uuid)) {
|
|
138
|
-
seen.add(object.uuid);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
// The inspection wins when an object has both: it means a world binding
|
|
142
|
-
// is already driving that skeleton, and a second mixer over it would
|
|
143
|
-
// give one object two writers.
|
|
144
|
-
const detach = attachInspected(object, transport) ?? attachOwnClips(object, transport);
|
|
145
|
-
if (!detach) return;
|
|
146
|
-
attached.set(object.uuid, detach);
|
|
147
|
-
seen.add(object.uuid);
|
|
148
|
-
});
|
|
149
|
-
for (const [uuid, detach] of [...attached]) {
|
|
150
|
-
if (seen.has(uuid)) continue;
|
|
151
|
-
detach();
|
|
152
|
-
attached.delete(uuid);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
refresh();
|
|
157
|
-
const stopMixers = options.rescanOnLiveMixers ? subscribeLiveMixers(refresh) : () => {};
|
|
158
|
-
|
|
114
|
+
root.traverse((object) => {
|
|
115
|
+
// The project's own mixer wins when an object has both: the project's
|
|
116
|
+
// code is already driving that skeleton, and a second mixer over it would
|
|
117
|
+
// give one object two writers.
|
|
118
|
+
const detach = attachInspected(object, transport) ?? attachOwnClips(object, transport);
|
|
119
|
+
if (detach) attached.set(object.uuid, detach);
|
|
120
|
+
});
|
|
159
121
|
return {
|
|
160
|
-
refresh,
|
|
161
122
|
dispose: () => {
|
|
162
|
-
stopMixers();
|
|
163
123
|
for (const detach of attached.values()) detach();
|
|
164
124
|
attached.clear();
|
|
165
125
|
},
|
|
@@ -105,9 +105,8 @@ function forwardedCallsiteOid(object: THREE.Object3D | null | undefined): string
|
|
|
105
105
|
* same oid twice, and both mint the same shape of id: the first one seen in
|
|
106
106
|
* depth-first order keeps the bare address, later ones get `#1`, `#2`, ….
|
|
107
107
|
*
|
|
108
|
-
* The suffix is spelled HERE and nowhere else
|
|
109
|
-
* (`projection/three.ts
|
|
110
|
-
* check and the presence report all use this one.
|
|
108
|
+
* The suffix is spelled HERE and nowhere else; the minting walk
|
|
109
|
+
* (`projection/three.ts`) uses this one.
|
|
111
110
|
*/
|
|
112
111
|
export function occurrenceId(baseId: string, occurrence: number): string {
|
|
113
112
|
return occurrence === 0 ? baseId : `${baseId}#${occurrence}`;
|
|
@@ -112,8 +112,7 @@ export function Object3DDocumentToolbar({
|
|
|
112
112
|
session?.getSnapshot ?? noSessionVersion,
|
|
113
113
|
);
|
|
114
114
|
// THIS STAGE's own store and its own helpers: the shared panels read
|
|
115
|
-
// whichever store the FOCUSED document's stage runs on
|
|
116
|
-
// world root's.
|
|
115
|
+
// whichever store the FOCUSED document's stage runs on, and a toolbar is a control over ITS document.
|
|
117
116
|
useSyncExternalStore(subscribeStageStores, stageStoresVersion, stageStoresVersion);
|
|
118
117
|
const stage = stageStore(documentId);
|
|
119
118
|
useSyncExternalStore(
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { lazy, Suspense, useEffect } from 'react';
|
|
10
10
|
import { announceDocumentStage } from '@volter/sdk/document-viewports';
|
|
11
11
|
import type { Object3DDocumentViewportProps } from './StageHost';
|
|
12
|
-
import {
|
|
12
|
+
import { Progress } from '@volter/sdk/progress';
|
|
13
13
|
|
|
14
14
|
const LazyObject3DDocumentViewport = lazy(async () => {
|
|
15
15
|
const module = await import('./StageHost');
|
|
@@ -26,9 +26,7 @@ export function Object3DDocumentViewport(props: Object3DDocumentViewportProps) {
|
|
|
26
26
|
return (
|
|
27
27
|
<Suspense
|
|
28
28
|
fallback={
|
|
29
|
-
<
|
|
30
|
-
{OBJECT3D_SURFACE_BUILDING}
|
|
31
|
-
</ViewportSurfaceStatus>
|
|
29
|
+
props.chromeless ? null : <Progress documentId={props.documentId} title="Preparing the viewport" />
|
|
32
30
|
}
|
|
33
31
|
>
|
|
34
32
|
<LazyObject3DDocumentViewport {...props} />
|