@relu-interactives/spatial-ecs 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/{System-ln4Q7Eot.d.cts → System-V_mv07le.d.cts} +2 -2
  2. package/dist/{System-ln4Q7Eot.d.ts → System-V_mv07le.d.ts} +2 -2
  3. package/dist/{XRHandPose-CxPv-LBo.d.cts → XRHandPose-CQTLGxFC.d.cts} +218 -2
  4. package/dist/{XRHandPose-BImIa4ZS.d.ts → XRHandPose-CU3vNFuS.d.ts} +218 -2
  5. package/dist/{chunk-4AK4I3QF.js → chunk-BUEKOSR5.js} +102 -3
  6. package/dist/chunk-BUEKOSR5.js.map +1 -0
  7. package/dist/{chunk-SGADE55G.js → chunk-RXBLOO5B.js} +249 -90
  8. package/dist/chunk-RXBLOO5B.js.map +1 -0
  9. package/dist/{chunk-4DWZK4X4.js → chunk-WJD2VKEH.js} +3 -3
  10. package/dist/chunk-WJD2VKEH.js.map +1 -0
  11. package/dist/{chunk-OY2OONRR.js → chunk-Z6ZMR3CS.js} +1374 -30
  12. package/dist/chunk-Z6ZMR3CS.js.map +1 -0
  13. package/dist/components/index.cjs +99 -0
  14. package/dist/components/index.cjs.map +1 -1
  15. package/dist/components/index.d.cts +3 -3
  16. package/dist/components/index.d.ts +3 -3
  17. package/dist/components/index.js +1 -1
  18. package/dist/docs/guide/mixed-reality.md +137 -3
  19. package/dist/docs/guide/spatial-ui.md +165 -0
  20. package/dist/docs/guide/vr-and-hand-poses.md +29 -0
  21. package/dist/docs/guide/world-api.md +1 -1
  22. package/dist/factories/index.cjs +372 -114
  23. package/dist/factories/index.cjs.map +1 -1
  24. package/dist/factories/index.d.cts +13 -3
  25. package/dist/factories/index.d.ts +13 -3
  26. package/dist/factories/index.js +3 -3
  27. package/dist/{index-CuGqxuk-.d.ts → index-C3ik3sA1.d.ts} +16 -3
  28. package/dist/{index-B2oHfqkj.d.ts → index-CppKDxbu.d.ts} +187 -3
  29. package/dist/{index-CQsHUez3.d.cts → index-Dlr-7-4m.d.cts} +16 -3
  30. package/dist/{index-7YCk4cmQ.d.cts → index-I2IsocYW.d.cts} +187 -3
  31. package/dist/index.cjs +1977 -366
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +42 -9
  34. package/dist/index.d.ts +42 -9
  35. package/dist/index.js +10 -8
  36. package/dist/index.js.map +1 -1
  37. package/dist/systems/index.cjs +1465 -25
  38. package/dist/systems/index.cjs.map +1 -1
  39. package/dist/systems/index.d.cts +2 -2
  40. package/dist/systems/index.d.ts +2 -2
  41. package/dist/systems/index.js +3 -3
  42. package/dist/types.d.cts +1 -1
  43. package/dist/types.d.ts +1 -1
  44. package/dist/worldSnapshotUtils.d.cts +1 -1
  45. package/dist/worldSnapshotUtils.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/dist/chunk-4AK4I3QF.js.map +0 -1
  48. package/dist/chunk-4DWZK4X4.js.map +0 -1
  49. package/dist/chunk-OY2OONRR.js.map +0 -1
  50. package/dist/chunk-SGADE55G.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { k as EcsSystem, l as World, r as EntityId } from './System-ln4Q7Eot.cjs';
1
+ import { k as EcsSystem, l as World, I as InputSystem, r as EntityId } from './System-V_mv07le.cjs';
2
2
  import * as THREE from 'three';
3
3
  import { m as XrHandJoints, r as XrHandShape } from './handPose-DlcTtqgx.cjs';
4
4
 
@@ -426,6 +426,7 @@ declare class XRInteractionSystem implements EcsSystem {
426
426
  private readonly grabs;
427
427
  private hoveredEntityId;
428
428
  /** Per-hand hover, with the hit distance so the ray can stop at the surface. */
429
+ private readonly busyContributors;
429
430
  private readonly hovers;
430
431
  /** Monotonic seconds, accumulated from deltaTime for velocity sampling. */
431
432
  private elapsed;
@@ -529,6 +530,14 @@ declare class XRInteractionSystem implements EcsSystem {
529
530
  * grabbed and teleported.
530
531
  */
531
532
  isSourceBusy(world: World, input: XrInputState): boolean;
533
+ /** OR-ed into isSourceBusy; see the comment there. */
534
+ registerBusyContributor(contributor: (world: World, input: XrInputState) => boolean): void;
535
+ /** Hover for a specific input source (handedness-free — Vision Pro's
536
+ * transient pointers all report handedness "none"). */
537
+ getHoverForInput(input: XrInputState): {
538
+ entityId: number;
539
+ distance: number;
540
+ } | null;
532
541
  /** True when THIS hand is holding something. */
533
542
  isGrabbingWith(handedness: XrInputState["handedness"]): boolean;
534
543
  /**
@@ -651,6 +660,25 @@ declare class XRSessionSystem implements EcsSystem {
651
660
  private session;
652
661
  constructor(inputSystem?: XRInputSystem);
653
662
  update(world: World): void;
663
+ /**
664
+ * Stop three's depth-occlusion pass from writing COLOR.
665
+ *
666
+ * With depth sensing granted, three draws a full-screen, head-locked mesh
667
+ * every frame whose fragment shader assigns ONLY `gl_FragDepth` — that is
668
+ * the whole occlusion mechanism — but its material never sets
669
+ * `colorWrite: false`, so the colour output is UNDEFINED per GLSL. Desktop
670
+ * drivers happen to zero it, which is why this ships unnoticed; Quest's
671
+ * mobile GPU frequently leaves the last-computed temporary in the register —
672
+ * the sampled DEPTH value — so the pass paints the depth camera's view over
673
+ * the screen as faint head-locked contour bands. On device that reads as "a
674
+ * large wireframe dome that follows my head".
675
+ *
676
+ * The mesh is created lazily inside three's render, so this is re-checked
677
+ * every frame rather than hooked once; the write is idempotent and the
678
+ * check is two property reads. Depth-only is the mesh's entire purpose —
679
+ * occlusion is unaffected.
680
+ */
681
+ private muteDepthOcclusionColor;
654
682
  private handleSessionStart;
655
683
  /**
656
684
  * Apply the rig's authored tracking origin, and compensate when the device
@@ -690,12 +718,26 @@ declare class XREnvironmentSystem implements EcsSystem {
690
718
  /** Keyed by the XRPlane/XRMesh object itself — the runtime's own identity. */
691
719
  private readonly planes;
692
720
  private readonly meshes;
721
+ /**
722
+ * Surfaces observed but not yet SPAWNED — their gate is still proving the
723
+ * pose is world-anchored. Creating from an unflagged head-glued pose (seen
724
+ * on-device after a suspend/resume regenerated the scan identities) plants
725
+ * a rotated ghost copy of the room wherever the head happens to be, which
726
+ * the apply-gate then freezes there — wrong forever. So admission itself
727
+ * is gated: a few frames of world-anchored behavior first.
728
+ */
729
+ private readonly pendingPlanes;
730
+ private readonly pendingMeshes;
693
731
  private presenting;
694
732
  private debugVisible;
695
733
  /** Seconds presenting, used to decide the room has genuinely nothing in it. */
696
734
  private elapsed;
697
735
  /** `initiateRoomCapture` may be called only ONCE per session. */
698
736
  private roomCaptureRequested;
737
+ /** This frame's inverse viewer matrix, or null when unavailable. */
738
+ private viewerInverse;
739
+ private readonly tmpViewerMatrix;
740
+ private readonly tmpViewPos;
699
741
  update(world: World, deltaTime?: number): void;
700
742
  /**
701
743
  * Ask the runtime to scan the room when it turns out we have no room.
@@ -718,9 +760,47 @@ declare class XREnvironmentSystem implements EcsSystem {
718
760
  planes: number;
719
761
  meshes: number;
720
762
  };
763
+ /**
764
+ * A census of every surface currently spawned: what it is, where it sits and
765
+ * roughly how big it is. Built for the template's post-session readout, so a
766
+ * mystery shape on the headset can be looked up by its label instead of
767
+ * guessed at — a small-radius entry at head height IS the mystery shape, and
768
+ * a clean list means whatever was seen did not come from this system.
769
+ */
770
+ getDebugSnapshot(world: World): {
771
+ kind: "plane" | "mesh";
772
+ label: string | null;
773
+ position: {
774
+ x: number;
775
+ y: number;
776
+ z: number;
777
+ };
778
+ radius: number;
779
+ /** Tracking currently lost — pose frozen at the last known place. */
780
+ emulated: boolean;
781
+ }[];
782
+ /** Create the no-scan floor once; it lives in the ordinary plane map. */
783
+ private ensureSyntheticFloor;
784
+ /** Real scene data exists (or arrived) — the stand-in must go. */
785
+ private removeSyntheticFloor;
721
786
  private findRig;
722
787
  private syncPlanes;
723
788
  private syncMeshes;
789
+ /**
790
+ * Admission control for a NOT-yet-spawned surface. With a viewer pose
791
+ * available, the surface must behave world-anchored for a few frames before
792
+ * an entity is created for it; without one (older runtimes, tests) it is
793
+ * admitted immediately — the gate can't classify anyway. Returns the
794
+ * warmed-up gate (carried into the entry so its motion history survives),
795
+ * or null while the surface must keep waiting.
796
+ */
797
+ private admit;
798
+ /**
799
+ * Run a surface's pose through its head-glue gate: position in reference
800
+ * space vs position relative to the viewer. Without a viewer pose the gate
801
+ * passes everything, degrading to the flag-only behavior.
802
+ */
803
+ private gateAllows;
724
804
  private createPlaneEntity;
725
805
  private applyPlaneCollider;
726
806
  private createMeshEntity;
@@ -734,6 +814,106 @@ declare class XREnvironmentSystem implements EcsSystem {
734
814
  dispose(world: World): void;
735
815
  }
736
816
 
817
+ declare class UIInteractionSystem implements EcsSystem {
818
+ private readonly interaction;
819
+ private readonly inputSystem;
820
+ private readonly xrPresses;
821
+ private mousePress;
822
+ private mouseHover;
823
+ /** entityId → seconds of pressed-tint left to display. */
824
+ private readonly tintFloors;
825
+ private readonly tinted;
826
+ private readonly raycaster;
827
+ /**
828
+ * @param interaction The XR interaction system, or null in a world with no
829
+ * XR (flat 3D experiences) — the mouse path still works via
830
+ * `options.inputSystem`.
831
+ */
832
+ constructor(interaction: XRInteractionSystem | null, options?: {
833
+ inputSystem?: InputSystem;
834
+ });
835
+ update(world: World, deltaTime?: number): void;
836
+ private updateXr;
837
+ private updateMouse;
838
+ private raycastMouse;
839
+ private widgetKind;
840
+ private fire;
841
+ private dragSlider;
842
+ private dragSliderMouse;
843
+ private applySliderPoint;
844
+ private widgetObject;
845
+ private rayHitsWidget;
846
+ private rayPointOnWidget;
847
+ /**
848
+ * Dispatch to the widget's Behaviours, then BUBBLE up the parent chain so a
849
+ * panel script hears its children. Bubbling (not a world broadcast) keeps
850
+ * two spawned copies of the same menu prefab independent.
851
+ */
852
+ private dispatchBubbling;
853
+ private actionIdOf;
854
+ private beginPressTint;
855
+ private endPressTint;
856
+ private updateTints;
857
+ private setTint;
858
+ }
859
+
860
+ /**
861
+ * Renders spatial-UI widgets: panels, text, buttons, toggles and sliders.
862
+ *
863
+ * Lives in `World.setup`'s DEFAULT system list (like `VRTeleportAreaSystem`)
864
+ * so the editor and every runtime render UI with no consumer wiring. Each
865
+ * widget's face is ONE canvas-textured quad, rebuilt only when its style
866
+ * SIGNATURE changes — a canvas redraw plus GPU upload costs 1–3 ms, fine per
867
+ * edit and catastrophic per frame. Interaction state (hover/pressed) never
868
+ * touches the canvas: `UIInteractionSystem` tints the material instead.
869
+ *
870
+ * Toggle knobs and slider knob/fill are separate GEOMETRY positioned from the
871
+ * value each frame, so dragging a slider redraws nothing.
872
+ *
873
+ * Widget SIZE is authored on the component in metres, never via transform
874
+ * scale — the texture is rasterised for the authored size, so scale would
875
+ * stretch texels into blur (and folding live scale into the signature would
876
+ * rebuild every frame during a two-hand scale gesture).
877
+ */
878
+ /** Structural canvas type so tests can inject a stub with no DOM. */
879
+ type UiCanvasLike = {
880
+ width: number;
881
+ height: number;
882
+ getContext(kind: "2d"): CanvasRenderingContext2D | null;
883
+ };
884
+ declare class UIPanelSystem implements EcsSystem {
885
+ private readonly entries;
886
+ private readonly createCanvas;
887
+ constructor(options?: {
888
+ createCanvas?: () => UiCanvasLike;
889
+ });
890
+ update(world: World, deltaTime?: number): void;
891
+ /** All GPU/canvas resources; call on world teardown. */
892
+ dispose(): void;
893
+ private syncPanel;
894
+ private syncText;
895
+ private syncButton;
896
+ private syncToggle;
897
+ private syncSlider;
898
+ private rebuildIfChanged;
899
+ private configureTexture;
900
+ private makeQuad;
901
+ /** A filled disc (or arc segment, for the fill's rounded cap). */
902
+ private makeDisc;
903
+ private addPart;
904
+ /** Remove + dispose knob/fill/cap so a sync pass rebuilds them fresh. */
905
+ private disposeMovingParts;
906
+ /**
907
+ * Billboard: yaw the ENTITY TRANSFORM toward the camera. Writing the
908
+ * Object3D directly would be overwritten by `TransformSyncSystem` (which
909
+ * runs after this system, before render) — the same reason locomotion
910
+ * writes the rig transform. Skipped in the editor so the gizmo and the
911
+ * authored rotation stay in charge while authoring.
912
+ */
913
+ private applyFollow;
914
+ private destroyEntry;
915
+ }
916
+
737
917
  /**
738
918
  * Answers "where does this ray meet the real world?".
739
919
  *
@@ -787,8 +967,12 @@ declare class XRAnchorSystem implements EcsSystem {
787
967
  private readonly anchors;
788
968
  /** In-flight creations, so one entity is not anchored twice. */
789
969
  private readonly pending;
970
+ /** Per-anchor head-glue gates — see XREnvironmentSystem's TrackedEntry. */
971
+ private readonly gates;
790
972
  private presenting;
791
- update(world: World): void;
973
+ private readonly tmpViewerMatrix;
974
+ private readonly tmpViewPos;
975
+ update(world: World, deltaTime?: number): void;
792
976
  /** Whether this entity's pose is currently being driven by an anchor. */
793
977
  isAnchored(entityId: EntityId): boolean;
794
978
  private requestAnchor;
@@ -979,4 +1163,4 @@ declare class XRHoverVisualSystem implements EcsSystem {
979
1163
  dispose(): void;
980
1164
  }
981
1165
 
982
- export { AnimationSystem as A, CameraSyncSystem as C, EnvironmentSyncSystem as E, InstancedMeshSystem as I, LightSyncSystem as L, MaterialSyncSystem as M, NO_XR_CAPABILITIES as N, ObjectVisibilitySystem as O, PhysicsSyncSystem as P, SCRIPT_OBJECT_ROOT_FLAG as S, VRTeleportAreaSystem as V, type XrCapabilities as X, type XrInputState as a, type XrHandPoseEventDetail as b, AudioSystem as c, MeshGeometrySystem as d, type ModuleLoader as e, PostprocessingSyncSystem as f, ScriptBehaviourSystem as g, ScriptObjectSystem as h, type SelectionContext as i, VideoSystem as j, XRAnchorSystem as k, XRControllerVisualSystem as l, XREnvironmentSystem as m, XRHandPoseSystem as n, XRHitTestSystem as o, XRHoverVisualSystem as p, XRInputSystem as q, XRInteractionSystem as r, XRLocomotionSystem as s, XRSessionSystem as t, XRTeleportVisualSystem as u, XR_HOVER_SHELL_FLAG as v, type XrHandedness as w, type XrHitResult as x, type XrInputKind as y, readXrCapabilities as z };
1166
+ export { AnimationSystem as A, type XrInputKind as B, CameraSyncSystem as C, readXrCapabilities as D, EnvironmentSyncSystem as E, InstancedMeshSystem as I, LightSyncSystem as L, MaterialSyncSystem as M, NO_XR_CAPABILITIES as N, ObjectVisibilitySystem as O, PhysicsSyncSystem as P, SCRIPT_OBJECT_ROOT_FLAG as S, UIInteractionSystem as U, VRTeleportAreaSystem as V, type XrCapabilities as X, type XrInputState as a, type XrHandPoseEventDetail as b, AudioSystem as c, MeshGeometrySystem as d, type ModuleLoader as e, PostprocessingSyncSystem as f, ScriptBehaviourSystem as g, ScriptObjectSystem as h, type SelectionContext as i, UIPanelSystem as j, type UiCanvasLike as k, VideoSystem as l, XRAnchorSystem as m, XRControllerVisualSystem as n, XREnvironmentSystem as o, XRHandPoseSystem as p, XRHitTestSystem as q, XRHoverVisualSystem as r, XRInputSystem as s, XRInteractionSystem as t, XRLocomotionSystem as u, XRSessionSystem as v, XRTeleportVisualSystem as w, XR_HOVER_SHELL_FLAG as x, type XrHandedness as y, type XrHitResult as z };