@volter/threejs-runtime 0.5.65 → 0.5.66

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/threejs-runtime",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.5.65",
5
+ "version": "0.5.66",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -43,7 +43,7 @@
43
43
  "@dimforge/rapier3d-compat": "^0.14.0",
44
44
  "@recast-navigation/three": "^0.43.0",
45
45
  "@sparkjsdev/spark": "^2.1.0",
46
- "@volter/editor-project": "0.5.65",
46
+ "@volter/editor-project": "0.5.66",
47
47
  "n8ao": "^1.10.1",
48
48
  "postprocessing": "^6.38.0",
49
49
  "quarks.core": "0.16.0",
@@ -1,8 +1,6 @@
1
1
  import type { SystemFn, SystemOptions, SystemPhaseName } from '@volter/editor-project/core/system-phase';
2
2
  import * as THREE from 'three';
3
3
  import type { AnyStateMachine } from 'xstate';
4
- import { deleteUserData, getUserData, setUserData } from '../ecs/user-data';
5
- import { attachAnimationRuntimeInspection } from './runtime-inspection';
6
4
  import {
7
5
  type AnimationBoneMask,
8
6
  type AnimationMetaStateNodeLike,
@@ -67,7 +65,7 @@ export interface XStateAnimationSnapshot {
67
65
  * compare `Actor<StateMachine<…>>` from one copy against `AnyActorRef` from
68
66
  * the other — structurally, through `system`, `src`, `_parent` and a generic
69
67
  * `select`, until it gives up with `TS2321: Excessive stack depth`. Measured
70
- * on a fresh scaffold + `vgai add humanoid`, where every in-repo gate
68
+ * on a fresh scaffold with a second xstate copy, where every in-repo gate
71
69
  * stayed green because the monorepo resolves exactly one copy. A shape this
72
70
  * shallow cannot blow that budget however many copies are in play.
73
71
  *
@@ -113,8 +111,6 @@ export interface XStateAnimationBindingOptions {
113
111
  selectParameters?: (context: unknown) => Record<string, number | boolean>;
114
112
  /** Root whose named Bone/Object3D hierarchy is used by `boneMask`. Defaults to mixer root. */
115
113
  root?: THREE.Object3D;
116
- /** Entity that exposes this binding to editor/runtime inspection. Defaults to `root`. */
117
- owner?: THREE.Object3D;
118
114
  /**
119
115
  * Engine runner used to register `tick` in the canonical animation phase.
120
116
  * Pass `ctx.systems`; disposal removes the callback automatically. When
@@ -144,29 +140,10 @@ export interface XStateAnimationBinding {
144
140
  getParameters: () => ReadonlyMap<string, number | boolean>;
145
141
  /** Current native composition, useful to the editor and game diagnostics. */
146
142
  getActiveLayers: () => readonly XStateAnimationLayerState[];
147
- /** Unsubscribe, stop/uncache actions, and remove owner inspection data. Idempotent. */
143
+ /** Unsubscribe and stop/uncache actions. Idempotent. */
148
144
  dispose: () => void;
149
145
  }
150
146
 
151
- let inspectionVersion = 0;
152
- const inspectionListeners = new Set<() => void>();
153
-
154
- /** React/useSyncExternalStore-compatible lifecycle signal for live editor inspection. */
155
- export function subscribeXStateAnimationBindings(listener: () => void): () => void {
156
- inspectionListeners.add(listener);
157
- return () => inspectionListeners.delete(listener);
158
- }
159
-
160
- /** Monotonic snapshot changed whenever a binding is attached or disposed. */
161
- export function getXStateAnimationBindingsVersion(): number {
162
- return inspectionVersion;
163
- }
164
-
165
- function notifyInspectionLifecycle(): void {
166
- inspectionVersion++;
167
- for (const listener of inspectionListeners) listener();
168
- }
169
-
170
147
  interface WeightedClip {
171
148
  clip: string;
172
149
  actionKey: string;
@@ -288,7 +265,6 @@ export function bindXStateAnimation(
288
265
  '[bindXStateAnimation] a THREE.Object3D `root` option is required when the mixer uses AnimationObjectGroup',
289
266
  );
290
267
  }
291
- const owner = options.owner ?? root;
292
268
  const machineRoot = machineOfXStateActor(actor).root as unknown as AnimationMetaStateNodeLike;
293
269
  const entries: MachineAnimationMetaEntry[] = collectMachineAnimationMeta(machineRoot);
294
270
  const metaByStateId = new Map(entries.map((entry) => [entry.stateId, entry.meta]));
@@ -379,7 +355,6 @@ export function bindXStateAnimation(
379
355
 
380
356
  const activeLayers = new Map<string, ActiveState>();
381
357
  let disposed = false;
382
- let detachRuntimeInspection = () => {};
383
358
  function silence(weights: WeightedClip[], except: string | undefined, duration: number): void {
384
359
  for (const weighted of weights) {
385
360
  if (weighted.actionKey === except) continue;
@@ -514,7 +489,6 @@ export function bindXStateAnimation(
514
489
  dispose(): void {
515
490
  if (disposed) return;
516
491
  disposed = true;
517
- detachRuntimeInspection();
518
492
  options.systems?.remove('animation', tick);
519
493
  subscription.unsubscribe();
520
494
  for (const { action, clip } of actionCache.values()) {
@@ -523,15 +497,8 @@ export function bindXStateAnimation(
523
497
  }
524
498
  actionCache.clear();
525
499
  activeLayers.clear();
526
- if (getUserData(owner, '_xstateAnimation') === binding) {
527
- deleteUserData(owner, '_xstateAnimation');
528
- notifyInspectionLifecycle();
529
- }
530
500
  },
531
501
  };
532
- detachRuntimeInspection = attachAnimationRuntimeInspection(owner, { mixer, clips });
533
- setUserData(owner, '_xstateAnimation', binding);
534
- notifyInspectionLifecycle();
535
502
  options.systems?.add('animation', tick);
536
503
  return binding;
537
504
  }
@@ -42,16 +42,8 @@
42
42
  *
43
43
  * Animation (load-bearing — ED5 disposal contract):
44
44
  * - `_animMixer` — THREE.AnimationMixer driving this subtree's clips.
45
- * - `_animClips` — Map<string, AnimationClip> discovered on the GLTF (E5 —
46
- * lets game code build its own XState-driven
47
- * binding via `bindXStateAnimation` over the SAME
48
- * mixer; see xstate-animation-binding.ts).
49
45
  * - `_availableClips` — string[] of clip names discovered on the GLTF
50
- * (inspector dropdown; same names as `_animClips`' keys).
51
- * - `_animationRuntime` — format-neutral live native mixer/action inspection.
52
- * - `_xstateBehavior` — optional live XState behavior actor inspection.
53
- * - `_xstateAnimation` — compatibility handle for the optional XState→Three
54
- * bridge; animation UI never discovers through it.
46
+ * (inspector dropdown).
55
47
  *
56
48
  * Disposal contract:
57
49
  * - `__sharedGeometry` — `true` when a mesh's geometry is shared/cached and MUST
@@ -132,9 +124,6 @@ import type { Object3DAuthoringSubjectMark } from '../adapter/object3d-authoring
132
124
  import type { RapierEditableBody } from '../adapter/rapier-physics-adapter';
133
125
  import type { ReflectionProbeMark } from '../adapter/reflection-probe';
134
126
  import type { TriggerVolumeMark } from '../adapter/trigger-volume';
135
- import type { AnimationRuntimeInspection } from '../animation/runtime-inspection';
136
- import type { XStateAnimationBinding } from '../animation/xstate-animation-binding';
137
- import type { XStateBehaviorInspection } from '../behavior/xstate-inspection';
138
127
  import { ObjectMarkKeys, type ObjectMarkSchema } from './object-marks';
139
128
 
140
129
  export type { EditorHelperType } from './object-marks';
@@ -156,11 +145,7 @@ export interface UserDataSchema extends ObjectMarkSchema {
156
145
  _particleSystem: ParticleSystem;
157
146
  gaussianSplat: { src: string; numSplats: number };
158
147
  _animMixer: THREE.AnimationMixer;
159
- _animClips: Map<string, THREE.AnimationClip>;
160
148
  _availableClips: string[];
161
- _animationRuntime: AnimationRuntimeInspection;
162
- _xstateBehavior: XStateBehaviorInspection;
163
- _xstateAnimation: XStateAnimationBinding;
164
149
  __sharedGeometry: boolean;
165
150
  __shadeOrig: THREE.Material | THREE.Material[];
166
151
  __shadeUnlit: THREE.Material[];
@@ -213,11 +198,7 @@ export const UserDataKeys = {
213
198
  _particleSystem: '_particleSystem',
214
199
  gaussianSplat: 'gaussianSplat',
215
200
  _animMixer: '_animMixer',
216
- _animClips: '_animClips',
217
201
  _availableClips: '_availableClips',
218
- _animationRuntime: '_animationRuntime',
219
- _xstateBehavior: '_xstateBehavior',
220
- _xstateAnimation: '_xstateAnimation',
221
202
  __sharedGeometry: '__sharedGeometry',
222
203
  __shadeOrig: '__shadeOrig',
223
204
  __shadeUnlit: '__shadeUnlit',
@@ -1,45 +0,0 @@
1
- import type * as THREE from 'three';
2
- import { deleteUserData, getUserData, setUserData } from '../ecs/user-data';
3
-
4
- /**
5
- * Live native animation playback for editor tools, READ-ONLY. A world states
6
- * what it is playing; nothing here moves it. Time on an editor stage belongs
7
- * to the stage transport (`@editor/animation/stage-transport`, WORK.md §The
8
- * stage transport and the animation door), and the transport writes time by
9
- * seeking a SUBJECT, never through this inspection door.
10
- * This is instrumentation, not an animation asset or graph format.
11
- */
12
- export interface AnimationRuntimeInspection {
13
- readonly mixer: THREE.AnimationMixer;
14
- readonly clips: ReadonlyMap<string, THREE.AnimationClip>;
15
- }
16
-
17
- let version = 0;
18
- const listeners = new Set<() => void>();
19
-
20
- function notify(): void {
21
- version++;
22
- for (const listener of listeners) listener();
23
- }
24
-
25
- export function subscribeAnimationRuntimeInspections(listener: () => void): () => void {
26
- listeners.add(listener);
27
- return () => listeners.delete(listener);
28
- }
29
-
30
- export function animationRuntimeInspectionsVersion(): number {
31
- return version;
32
- }
33
-
34
- export function attachAnimationRuntimeInspection(
35
- owner: THREE.Object3D,
36
- inspection: AnimationRuntimeInspection,
37
- ): () => void {
38
- setUserData(owner, '_animationRuntime', inspection);
39
- notify();
40
- return () => {
41
- if (getUserData(owner, '_animationRuntime') !== inspection) return;
42
- deleteUserData(owner, '_animationRuntime');
43
- notify();
44
- };
45
- }
@@ -1,51 +0,0 @@
1
- import type * as THREE from 'three';
2
- import type { AnyStateMachine } from 'xstate';
3
- import { deleteUserData, getUserData, setUserData } from '../ecs/user-data';
4
-
5
- /** Minimal structural actor surface needed by the live behavior debugger. */
6
- export interface InspectableXStateActor {
7
- readonly getSnapshot: () => {
8
- readonly value: unknown;
9
- readonly context: unknown;
10
- readonly machine?: AnyStateMachine;
11
- };
12
- readonly subscribe: (next: (snapshot: { readonly value: unknown }) => void) => {
13
- unsubscribe: () => void;
14
- };
15
- readonly logic?: AnyStateMachine;
16
- }
17
-
18
- export interface XStateBehaviorInspection {
19
- readonly actor: InspectableXStateActor;
20
- readonly machine: AnyStateMachine;
21
- }
22
-
23
- let version = 0;
24
- const listeners = new Set<() => void>();
25
-
26
- function notify(): void {
27
- version++;
28
- for (const listener of listeners) listener();
29
- }
30
-
31
- export function subscribeXStateBehaviorInspections(listener: () => void): () => void {
32
- listeners.add(listener);
33
- return () => listeners.delete(listener);
34
- }
35
-
36
- export function xstateBehaviorInspectionsVersion(): number {
37
- return version;
38
- }
39
-
40
- export function attachXStateBehaviorInspection(
41
- owner: THREE.Object3D,
42
- inspection: XStateBehaviorInspection,
43
- ): () => void {
44
- setUserData(owner, '_xstateBehavior', inspection);
45
- notify();
46
- return () => {
47
- if (getUserData(owner, '_xstateBehavior') !== inspection) return;
48
- deleteUserData(owner, '_xstateBehavior');
49
- notify();
50
- };
51
- }