@relu-interactives/spatial-ecs 0.3.7 → 0.3.8

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 (47) hide show
  1. package/dist/{System-TmC2EijF.d.cts → System-31wk-A0g.d.cts} +1 -1
  2. package/dist/{System-TmC2EijF.d.ts → System-31wk-A0g.d.ts} +1 -1
  3. package/dist/{XRInteractor-CCUzSqg9.d.ts → XRHandPose-CxPz8VBp.d.ts} +139 -20
  4. package/dist/{XRInteractor-I2q6TKtF.d.cts → XRHandPose-VuSbe4iz.d.cts} +139 -20
  5. package/dist/{chunk-FQTOC7HK.js → chunk-KIKIWCEB.js} +14 -5
  6. package/dist/chunk-KIKIWCEB.js.map +1 -0
  7. package/dist/{chunk-N4GKZ7IV.js → chunk-KVGGAL5V.js} +227 -3
  8. package/dist/chunk-KVGGAL5V.js.map +1 -0
  9. package/dist/{chunk-JRJ3CL5H.js → chunk-L5BP727H.js} +3 -3
  10. package/dist/{chunk-JRJ3CL5H.js.map → chunk-L5BP727H.js.map} +1 -1
  11. package/dist/{chunk-O6FJA4XW.js → chunk-LMTB4KQ4.js} +929 -42
  12. package/dist/chunk-LMTB4KQ4.js.map +1 -0
  13. package/dist/components/index.cjs +98 -0
  14. package/dist/components/index.cjs.map +1 -1
  15. package/dist/components/index.d.cts +4 -3
  16. package/dist/components/index.d.ts +4 -3
  17. package/dist/components/index.js +1 -1
  18. package/dist/docs/guide/vr-and-hand-poses.md +111 -0
  19. package/dist/factories/index.cjs +58 -0
  20. package/dist/factories/index.cjs.map +1 -1
  21. package/dist/factories/index.d.cts +6 -3
  22. package/dist/factories/index.d.ts +6 -3
  23. package/dist/factories/index.js +3 -3
  24. package/dist/handPose-dFxqbFFY.d.cts +240 -0
  25. package/dist/handPose-dFxqbFFY.d.ts +240 -0
  26. package/dist/{index-BltjPEhg.d.cts → index-BIQoaKAA.d.cts} +9 -3
  27. package/dist/{index-W8pUajCW.d.ts → index-BxdXgqiI.d.ts} +9 -3
  28. package/dist/{index-_q9TVDI5.d.ts → index-C7XQyrgE.d.ts} +79 -7
  29. package/dist/{index-B9nwRDf9.d.cts → index-DyN50JZo.d.cts} +79 -7
  30. package/dist/index.cjs +1187 -37
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +104 -9
  33. package/dist/index.d.ts +104 -9
  34. package/dist/index.js +8 -8
  35. package/dist/systems/index.cjs +1144 -37
  36. package/dist/systems/index.cjs.map +1 -1
  37. package/dist/systems/index.d.cts +3 -2
  38. package/dist/systems/index.d.ts +3 -2
  39. package/dist/systems/index.js +3 -3
  40. package/dist/types.d.cts +1 -1
  41. package/dist/types.d.ts +1 -1
  42. package/dist/worldSnapshotUtils.d.cts +1 -1
  43. package/dist/worldSnapshotUtils.d.ts +1 -1
  44. package/package.json +1 -1
  45. package/dist/chunk-FQTOC7HK.js.map +0 -1
  46. package/dist/chunk-N4GKZ7IV.js.map +0 -1
  47. package/dist/chunk-O6FJA4XW.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -2611,6 +2611,218 @@ function createVRTeleportAreaComponent(world, entityId, ...args) {
2611
2611
  }
2612
2612
  __name(createVRTeleportAreaComponent, "createVRTeleportAreaComponent");
2613
2613
 
2614
+ // core/xr/handPosePresets.ts
2615
+ var WIDE = 0.35;
2616
+ var NARROW = 0.25;
2617
+ var EXTENDED = { curl: 0.05, tolerance: WIDE };
2618
+ var CURLED = { curl: 0.95, tolerance: WIDE };
2619
+ var THUMB_TUCKED = { curl: 0.7, tolerance: NARROW + 0.1 };
2620
+ var THUMB_RELAXED = { curl: 0.6, tolerance: 0.4 };
2621
+ function fingers(thumb, index, middle, ring, pinky) {
2622
+ return { thumb, index, middle, ring, pinky };
2623
+ }
2624
+ __name(fingers, "fingers");
2625
+ var THUMB_UP = {
2626
+ axis: "thumb",
2627
+ direction: "up",
2628
+ toleranceDegrees: 45
2629
+ };
2630
+ var THUMB_DOWN = {
2631
+ axis: "thumb",
2632
+ direction: "down",
2633
+ toleranceDegrees: 45
2634
+ };
2635
+ var HAND_POSE_PRESETS = {
2636
+ fist: {
2637
+ id: "fist",
2638
+ name: "Fist",
2639
+ fingers: fingers(THUMB_TUCKED, CURLED, CURLED, CURLED, CURLED)
2640
+ },
2641
+ "open-palm": {
2642
+ id: "open-palm",
2643
+ name: "Open Palm",
2644
+ fingers: fingers(EXTENDED, EXTENDED, EXTENDED, EXTENDED, EXTENDED)
2645
+ },
2646
+ point: {
2647
+ id: "point",
2648
+ name: "Point",
2649
+ fingers: fingers(THUMB_RELAXED, EXTENDED, CURLED, CURLED, CURLED)
2650
+ },
2651
+ "thumbs-up": {
2652
+ id: "thumbs-up",
2653
+ name: "Thumbs Up",
2654
+ fingers: fingers(EXTENDED, CURLED, CURLED, CURLED, CURLED),
2655
+ orientation: THUMB_UP
2656
+ },
2657
+ "thumbs-down": {
2658
+ id: "thumbs-down",
2659
+ name: "Thumbs Down",
2660
+ fingers: fingers(EXTENDED, CURLED, CURLED, CURLED, CURLED),
2661
+ orientation: THUMB_DOWN
2662
+ },
2663
+ peace: {
2664
+ id: "peace",
2665
+ name: "Peace",
2666
+ fingers: fingers(THUMB_RELAXED, EXTENDED, EXTENDED, CURLED, CURLED)
2667
+ },
2668
+ pinch: {
2669
+ id: "pinch",
2670
+ name: "Pinch",
2671
+ // Curl is left loose: what defines a pinch is the tips MEETING, and people
2672
+ // pinch with wildly different amounts of finger bend.
2673
+ fingers: fingers(
2674
+ { curl: 0.5, tolerance: 0.5 },
2675
+ { curl: 0.5, tolerance: 0.5 },
2676
+ { curl: 0.5, tolerance: 0.5 },
2677
+ { curl: 0.5, tolerance: 0.5 },
2678
+ { curl: 0.5, tolerance: 0.5 }
2679
+ ),
2680
+ pinch: { finger: "index", target: 1, tolerance: 0.25 }
2681
+ },
2682
+ ok: {
2683
+ id: "ok",
2684
+ name: "OK Sign",
2685
+ // Thumb and index meet in a ring while the rest stay up — that contrast with
2686
+ // a plain pinch is the whole gesture, so the other three are constrained.
2687
+ fingers: fingers(
2688
+ { curl: 0.5, tolerance: 0.4 },
2689
+ { curl: 0.55, tolerance: 0.35 },
2690
+ EXTENDED,
2691
+ EXTENDED,
2692
+ EXTENDED
2693
+ ),
2694
+ pinch: { finger: "index", target: 1, tolerance: 0.3 }
2695
+ },
2696
+ gun: {
2697
+ id: "gun",
2698
+ name: "Gun",
2699
+ fingers: fingers(EXTENDED, EXTENDED, CURLED, CURLED, CURLED)
2700
+ },
2701
+ rock: {
2702
+ id: "rock",
2703
+ name: "Rock Horns",
2704
+ fingers: fingers(THUMB_TUCKED, EXTENDED, CURLED, CURLED, EXTENDED)
2705
+ },
2706
+ shaka: {
2707
+ id: "shaka",
2708
+ name: "Shaka",
2709
+ fingers: fingers(EXTENDED, CURLED, CURLED, CURLED, EXTENDED)
2710
+ },
2711
+ "palm-up": {
2712
+ id: "palm-up",
2713
+ name: "Palm Up",
2714
+ fingers: fingers(EXTENDED, EXTENDED, EXTENDED, EXTENDED, EXTENDED),
2715
+ orientation: {
2716
+ axis: "palm-normal",
2717
+ direction: "up",
2718
+ toleranceDegrees: 40
2719
+ }
2720
+ }
2721
+ };
2722
+ var HAND_POSE_PRESET_IDS = Object.keys(HAND_POSE_PRESETS);
2723
+ function getHandPosePreset(id) {
2724
+ const preset = HAND_POSE_PRESETS[id];
2725
+ if (!preset) return null;
2726
+ return cloneHandPose(preset);
2727
+ }
2728
+ __name(getHandPosePreset, "getHandPosePreset");
2729
+ function cloneHandPose(definition) {
2730
+ return {
2731
+ ...definition,
2732
+ fingers: {
2733
+ thumb: { ...definition.fingers.thumb },
2734
+ index: { ...definition.fingers.index },
2735
+ middle: { ...definition.fingers.middle },
2736
+ ring: { ...definition.fingers.ring },
2737
+ pinky: { ...definition.fingers.pinky }
2738
+ },
2739
+ pinch: definition.pinch ? { ...definition.pinch } : void 0,
2740
+ orientation: definition.orientation ? { ...definition.orientation } : void 0
2741
+ };
2742
+ }
2743
+ __name(cloneHandPose, "cloneHandPose");
2744
+ function handPoseFromCurls(id, name, curls, options) {
2745
+ const tolerance = options?.tolerance ?? NARROW;
2746
+ const target = /* @__PURE__ */ __name((curl) => ({
2747
+ curl: Math.min(1, Math.max(0, curl)),
2748
+ tolerance
2749
+ }), "target");
2750
+ return {
2751
+ id,
2752
+ name,
2753
+ fingers: {
2754
+ thumb: target(curls.thumb),
2755
+ index: target(curls.index),
2756
+ middle: target(curls.middle),
2757
+ ring: target(curls.ring),
2758
+ pinky: target(curls.pinky)
2759
+ },
2760
+ pinch: options?.pinch,
2761
+ orientation: options?.orientation,
2762
+ hand: options?.hand
2763
+ };
2764
+ }
2765
+ __name(handPoseFromCurls, "handPoseFromCurls");
2766
+
2767
+ // core/components/XRHandPose.ts
2768
+ var DEFAULT_POSE_CURLS = {
2769
+ thumb: 0,
2770
+ index: 0,
2771
+ middle: 0,
2772
+ ring: 0,
2773
+ pinky: 0
2774
+ };
2775
+ function createHandPoseEntry(partial) {
2776
+ return {
2777
+ id: partial.id,
2778
+ name: partial.name ?? partial.id,
2779
+ preset: partial.preset ?? null,
2780
+ curls: { ...DEFAULT_POSE_CURLS, ...partial.curls },
2781
+ tolerance: partial.tolerance ?? 0.25,
2782
+ hand: partial.hand ?? null,
2783
+ orientation: partial.orientation ?? null,
2784
+ minHoldSeconds: partial.minHoldSeconds ?? 0.15,
2785
+ enabled: partial.enabled ?? true
2786
+ };
2787
+ }
2788
+ __name(createHandPoseEntry, "createHandPoseEntry");
2789
+ function handPoseDefinitionFor(entry, resolvePreset) {
2790
+ if (entry.preset) {
2791
+ const preset = resolvePreset(entry.preset);
2792
+ if (!preset) return null;
2793
+ return {
2794
+ ...preset,
2795
+ id: entry.id,
2796
+ name: entry.name,
2797
+ hand: entry.hand ?? preset.hand,
2798
+ orientation: entry.orientation ?? preset.orientation
2799
+ };
2800
+ }
2801
+ return handPoseFromCurls(entry.id, entry.name, entry.curls, {
2802
+ tolerance: entry.tolerance,
2803
+ orientation: entry.orientation ?? void 0,
2804
+ hand: entry.hand ?? void 0
2805
+ });
2806
+ }
2807
+ __name(handPoseDefinitionFor, "handPoseDefinitionFor");
2808
+ var _XRHandPoseComponent = class _XRHandPoseComponent extends Component {
2809
+ constructor(options) {
2810
+ super();
2811
+ this.enabled = options?.enabled ?? true;
2812
+ this.poses = (options?.poses ?? []).map((pose) => createHandPoseEntry(pose));
2813
+ }
2814
+ };
2815
+ __name(_XRHandPoseComponent, "XRHandPoseComponent");
2816
+ var XRHandPoseComponent = _XRHandPoseComponent;
2817
+
2818
+ // core/factories/components/XRHandPoseFactory.ts
2819
+ function createXRHandPoseComponent(world, entityId, ...args) {
2820
+ const component = new XRHandPoseComponent(...args);
2821
+ world.addComponent(entityId, component);
2822
+ return component;
2823
+ }
2824
+ __name(createXRHandPoseComponent, "createXRHandPoseComponent");
2825
+
2614
2826
  // core/components/XRInteractable.ts
2615
2827
  var _XRInteractableComponent = class _XRInteractableComponent extends Component {
2616
2828
  constructor(options) {
@@ -2619,6 +2831,22 @@ var _XRInteractableComponent = class _XRInteractableComponent extends Component
2619
2831
  this.grabbable = options?.grabbable ?? true;
2620
2832
  this.pointable = options?.pointable ?? true;
2621
2833
  this.grabMode = options?.grabMode ?? "kinematic";
2834
+ this.grabReach = options?.grabReach ?? "both";
2835
+ this.distanceBehavior = options?.distanceBehavior ?? "snap";
2836
+ this.grabGesture = options?.grabGesture ?? "any";
2837
+ this.grabRadius = options?.grabRadius ?? 0.15;
2838
+ this.grabOffsetPosition = options?.grabOffsetPosition ?? {
2839
+ x: 0,
2840
+ y: 0,
2841
+ z: 0
2842
+ };
2843
+ this.grabOffsetRotation = options?.grabOffsetRotation ?? {
2844
+ x: 0,
2845
+ y: 0,
2846
+ z: 0
2847
+ };
2848
+ this.throwOnRelease = options?.throwOnRelease ?? true;
2849
+ this.throwStrength = options?.throwStrength ?? 1;
2622
2850
  this.highlightOnHover = options?.highlightOnHover ?? true;
2623
2851
  }
2624
2852
  };
@@ -2683,6 +2911,7 @@ function registerDefaultComponentFactories(objectManagementSystem) {
2683
2911
  objectManagementSystem.registerComponentFactory("image", createImageComponent);
2684
2912
  objectManagementSystem.registerComponentFactory("xrRig", createXRRigComponent);
2685
2913
  objectManagementSystem.registerComponentFactory("vrTeleportArea", createVRTeleportAreaComponent);
2914
+ objectManagementSystem.registerComponentFactory("xrHandPose", createXRHandPoseComponent);
2686
2915
  objectManagementSystem.registerComponentFactory("xrInteractable", createXRInteractableComponent);
2687
2916
  objectManagementSystem.registerComponentFactory("xrInteractor", createXRInteractorComponent);
2688
2917
  objectManagementSystem.registerComponentFactory("xrInteractorSide", createXRInteractorSideComponent);
@@ -3705,6 +3934,7 @@ __export(components_exports, {
3705
3934
  DEFAULT_INSTANCE_TRANSFORM: () => DEFAULT_INSTANCE_TRANSFORM,
3706
3935
  DEFAULT_MESH_GEOMETRY_PARAMETERS: () => DEFAULT_MESH_GEOMETRY_PARAMETERS,
3707
3936
  DEFAULT_PHYSICS_STATE: () => DEFAULT_PHYSICS_STATE,
3937
+ DEFAULT_POSE_CURLS: () => DEFAULT_POSE_CURLS,
3708
3938
  DEFAULT_POSTPROCESSING_STATE: () => DEFAULT_POSTPROCESSING_STATE,
3709
3939
  DEFAULT_RIGIDBODY_STATE: () => DEFAULT_RIGIDBODY_STATE,
3710
3940
  EntityType: () => EntityType,
@@ -3734,11 +3964,14 @@ __export(components_exports, {
3734
3964
  TransformComponent: () => TransformComponent,
3735
3965
  VRTeleportAreaComponent: () => VRTeleportAreaComponent,
3736
3966
  VideoComponent: () => VideoComponent,
3967
+ XRHandPoseComponent: () => XRHandPoseComponent,
3737
3968
  XRInteractableComponent: () => XRInteractableComponent,
3738
3969
  XRInteractorComponent: () => XRInteractorComponent,
3739
3970
  XRInteractorSideComponent: () => XRInteractorSideComponent,
3740
3971
  XRRigComponent: () => XRRigComponent,
3972
+ createHandPoseEntry: () => createHandPoseEntry,
3741
3973
  defaultMaterialValue: () => defaultMaterialValue,
3974
+ handPoseDefinitionFor: () => handPoseDefinitionFor,
3742
3975
  normalizePhysicsState: () => normalizePhysicsState,
3743
3976
  setPhysicsLiveRef: () => setPhysicsLiveRef
3744
3977
  });
@@ -4427,6 +4660,10 @@ function buildComponentValueMaps(snapshot) {
4427
4660
  snapshot.components,
4428
4661
  "XRInteractorComponent"
4429
4662
  ),
4663
+ xrHandPoseValues: readByComponent(
4664
+ snapshot.components,
4665
+ "XRHandPoseComponent"
4666
+ ),
4430
4667
  xrInteractorSideValues: readByComponent(
4431
4668
  snapshot.components,
4432
4669
  "XRInteractorSideComponent"
@@ -4612,11 +4849,65 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
4612
4849
  if (typeof sourceInteractable.grabMode === "string") {
4613
4850
  interactable.grabMode = sourceInteractable.grabMode;
4614
4851
  }
4852
+ if (typeof sourceInteractable.grabReach === "string") {
4853
+ interactable.grabReach = sourceInteractable.grabReach;
4854
+ }
4855
+ if (typeof sourceInteractable.distanceBehavior === "string") {
4856
+ interactable.distanceBehavior = sourceInteractable.distanceBehavior;
4857
+ }
4858
+ if (typeof sourceInteractable.grabGesture === "string") {
4859
+ interactable.grabGesture = sourceInteractable.grabGesture;
4860
+ }
4861
+ if (typeof sourceInteractable.grabRadius === "number") {
4862
+ interactable.grabRadius = sourceInteractable.grabRadius;
4863
+ }
4864
+ if (typeof sourceInteractable.throwOnRelease === "boolean") {
4865
+ interactable.throwOnRelease = sourceInteractable.throwOnRelease;
4866
+ }
4867
+ if (typeof sourceInteractable.throwStrength === "number") {
4868
+ interactable.throwStrength = sourceInteractable.throwStrength;
4869
+ }
4870
+ const grabPos = asRecord3(sourceInteractable.grabOffsetPosition);
4871
+ if (grabPos) {
4872
+ interactable.grabOffsetPosition = {
4873
+ x: typeof grabPos.x === "number" ? grabPos.x : 0,
4874
+ y: typeof grabPos.y === "number" ? grabPos.y : 0,
4875
+ z: typeof grabPos.z === "number" ? grabPos.z : 0
4876
+ };
4877
+ }
4878
+ const grabRot = asRecord3(sourceInteractable.grabOffsetRotation);
4879
+ if (grabRot) {
4880
+ interactable.grabOffsetRotation = {
4881
+ x: typeof grabRot.x === "number" ? grabRot.x : 0,
4882
+ y: typeof grabRot.y === "number" ? grabRot.y : 0,
4883
+ z: typeof grabRot.z === "number" ? grabRot.z : 0
4884
+ };
4885
+ }
4615
4886
  if (typeof sourceInteractable.highlightOnHover === "boolean") {
4616
4887
  interactable.highlightOnHover = sourceInteractable.highlightOnHover;
4617
4888
  }
4618
4889
  }
4619
4890
  }
4891
+ {
4892
+ const sourceHandPose = asRecord3(
4893
+ values.xrHandPoseValues.get(sourceEntityId)
4894
+ );
4895
+ const handPose = world.getComponent(targetEntityId, XRHandPoseComponent);
4896
+ if (sourceHandPose && handPose) {
4897
+ if (typeof sourceHandPose.enabled === "boolean") {
4898
+ handPose.enabled = sourceHandPose.enabled;
4899
+ }
4900
+ if (Array.isArray(sourceHandPose.poses)) {
4901
+ handPose.poses = sourceHandPose.poses.map((entry) => asRecord3(entry)).filter(
4902
+ (entry) => entry !== null && typeof entry.id === "string"
4903
+ ).map(
4904
+ (entry) => createHandPoseEntry(
4905
+ entry
4906
+ )
4907
+ );
4908
+ }
4909
+ }
4910
+ }
4620
4911
  {
4621
4912
  const sourceInteractor = asRecord3(
4622
4913
  values.xrInteractorValues.get(sourceEntityId)
@@ -9178,6 +9469,17 @@ var _Behaviour = class _Behaviour {
9178
9469
  *
9179
9470
  * @param input - The source doing the grabbing.
9180
9471
  */
9472
+ /**
9473
+ * A hand pose declared on this entity started being held.
9474
+ *
9475
+ * Fires once the pose has been sustained for its hold time, not the instant
9476
+ * the fingers pass through it.
9477
+ */
9478
+ onHandPoseStart(_event) {
9479
+ }
9480
+ /** A held hand pose ended, or its hand stopped being tracked. */
9481
+ onHandPoseEnd(_event) {
9482
+ }
9181
9483
  onGrab(input) {
9182
9484
  }
9183
9485
  /** Called when this entity is released. Optional XR hook. */
@@ -10320,13 +10622,65 @@ function blankState(source) {
10320
10622
  squeezeEnd: false,
10321
10623
  axes: [],
10322
10624
  hasAxes: false,
10323
- buttons: []
10625
+ buttons: [],
10626
+ joints: null
10324
10627
  };
10325
10628
  }
10326
10629
  __name(blankState, "blankState");
10630
+ var JOINT_MATRIX_BUFFER = new Float32Array(25 * 16);
10631
+ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10632
+ const names = [];
10633
+ const spaces = [];
10634
+ for (const [name, space] of hand) {
10635
+ names.push(name);
10636
+ spaces.push(space);
10637
+ }
10638
+ if (spaces.length === 0) return null;
10639
+ const target = existing ?? {};
10640
+ const matrix = new THREE39__namespace.Matrix4();
10641
+ const buffer = spaces.length * 16 <= JOINT_MATRIX_BUFFER.length ? JOINT_MATRIX_BUFFER : new Float32Array(spaces.length * 16);
10642
+ const fillPoses = frame.fillPoses;
10643
+ if (typeof fillPoses === "function") {
10644
+ let filled = false;
10645
+ try {
10646
+ filled = fillPoses.call(frame, spaces, referenceSpace, buffer);
10647
+ } catch {
10648
+ filled = false;
10649
+ }
10650
+ if (filled) {
10651
+ for (let i = 0; i < names.length; i += 1) {
10652
+ matrix.fromArray(buffer, i * 16).premultiply(rigMatrix);
10653
+ const vector = target[names[i]] ?? new THREE39__namespace.Vector3();
10654
+ vector.setFromMatrixPosition(matrix);
10655
+ target[names[i]] = vector;
10656
+ }
10657
+ return target;
10658
+ }
10659
+ }
10660
+ let anyTracked = false;
10661
+ for (let i = 0; i < names.length; i += 1) {
10662
+ const pose = frame.getJointPose?.(
10663
+ spaces[i],
10664
+ referenceSpace
10665
+ );
10666
+ if (!pose) {
10667
+ target[names[i]] = void 0;
10668
+ continue;
10669
+ }
10670
+ matrix.fromArray(pose.transform.matrix).premultiply(rigMatrix);
10671
+ const vector = target[names[i]] ?? new THREE39__namespace.Vector3();
10672
+ vector.setFromMatrixPosition(matrix);
10673
+ target[names[i]] = vector;
10674
+ anyTracked = true;
10675
+ }
10676
+ return anyTracked ? target : null;
10677
+ }
10678
+ __name(readHandJoints, "readHandJoints");
10327
10679
  var _XRInputSystem = class _XRInputSystem {
10328
10680
  constructor() {
10329
10681
  this.records = /* @__PURE__ */ new Map();
10682
+ /** Reference-space → world matrix, rebuilt each frame from the XR rig. */
10683
+ this.referenceToWorld = new THREE39__namespace.Matrix4();
10330
10684
  this.session = null;
10331
10685
  this.onSelectStart = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectStart"), "onSelectStart");
10332
10686
  this.onSelectEnd = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectEnd"), "onSelectEnd");
@@ -10406,6 +10760,14 @@ var _XRInputSystem = class _XRInputSystem {
10406
10760
  const referenceSpace = renderer.xr.getReferenceSpace();
10407
10761
  const frame = renderer.xr.getFrame?.();
10408
10762
  const live = /* @__PURE__ */ new Set();
10763
+ const rigMatrix = this.referenceToWorld;
10764
+ const rig = world.getCamera().parent;
10765
+ if (rig) {
10766
+ rig.updateMatrixWorld();
10767
+ rigMatrix.copy(rig.matrixWorld);
10768
+ } else {
10769
+ rigMatrix.identity();
10770
+ }
10409
10771
  for (const source of session.inputSources) {
10410
10772
  live.add(source);
10411
10773
  const record = this.ensureRecord(source);
@@ -10416,7 +10778,7 @@ var _XRInputSystem = class _XRInputSystem {
10416
10778
  if (frame && referenceSpace && source.targetRaySpace) {
10417
10779
  const pose = frame.getPose(source.targetRaySpace, referenceSpace);
10418
10780
  if (pose) {
10419
- const m = new THREE39__namespace.Matrix4().fromArray(pose.transform.matrix);
10781
+ const m = new THREE39__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
10420
10782
  state.rayOrigin.setFromMatrixPosition(m);
10421
10783
  state.rayDirection.set(0, 0, -1).applyQuaternion(
10422
10784
  new THREE39__namespace.Quaternion().setFromRotationMatrix(m)
@@ -10426,7 +10788,7 @@ var _XRInputSystem = class _XRInputSystem {
10426
10788
  if (frame && referenceSpace && source.gripSpace) {
10427
10789
  const gripPose = frame.getPose(source.gripSpace, referenceSpace);
10428
10790
  if (gripPose) {
10429
- const m = new THREE39__namespace.Matrix4().fromArray(gripPose.transform.matrix);
10791
+ const m = new THREE39__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
10430
10792
  state.gripPosition ?? (state.gripPosition = new THREE39__namespace.Vector3());
10431
10793
  state.gripQuaternion ?? (state.gripQuaternion = new THREE39__namespace.Quaternion());
10432
10794
  state.gripPosition.setFromMatrixPosition(m);
@@ -10436,6 +10798,17 @@ var _XRInputSystem = class _XRInputSystem {
10436
10798
  state.gripPosition = null;
10437
10799
  state.gripQuaternion = null;
10438
10800
  }
10801
+ if (frame && referenceSpace && source.hand) {
10802
+ state.joints = readHandJoints(
10803
+ frame,
10804
+ source.hand,
10805
+ referenceSpace,
10806
+ rigMatrix,
10807
+ state.joints
10808
+ );
10809
+ } else {
10810
+ state.joints = null;
10811
+ }
10439
10812
  const gamepad = source.gamepad;
10440
10813
  if (gamepad) {
10441
10814
  state.axes = Array.from(gamepad.axes);
@@ -10500,20 +10873,84 @@ var _XRInputSystem = class _XRInputSystem {
10500
10873
  };
10501
10874
  __name(_XRInputSystem, "XRInputSystem");
10502
10875
  var XRInputSystem = _XRInputSystem;
10876
+ var VELOCITY_SAMPLES = 5;
10877
+ var MAX_SAMPLE_AGE_SECONDS = 0.2;
10878
+ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
10879
+ constructor() {
10880
+ this.samples = [];
10881
+ }
10882
+ /** Record where the hand is now. Call once per frame while held. */
10883
+ record(position, time) {
10884
+ this.samples.push({ position: position.clone(), time });
10885
+ if (this.samples.length > VELOCITY_SAMPLES) {
10886
+ this.samples.shift();
10887
+ }
10888
+ }
10889
+ /**
10890
+ * Velocity to release with, in metres/second.
10891
+ *
10892
+ * Uses the FASTEST segment inside the window rather than the most recent or
10893
+ * the window average. Users decelerate as they open their hand, so the last
10894
+ * segment understates the throw and the average washes it out; peak speed is
10895
+ * what matches the motion the user actually felt they made.
10896
+ */
10897
+ computeVelocity(now) {
10898
+ const fresh = this.samples.filter(
10899
+ (sample) => now - sample.time <= MAX_SAMPLE_AGE_SECONDS
10900
+ );
10901
+ if (fresh.length < 2) return new THREE39__namespace.Vector3();
10902
+ let best = new THREE39__namespace.Vector3();
10903
+ let bestSpeed = 0;
10904
+ for (let i = 1; i < fresh.length; i += 1) {
10905
+ const previous = fresh[i - 1];
10906
+ const current = fresh[i];
10907
+ const dt = current.time - previous.time;
10908
+ if (dt <= 1e-5) continue;
10909
+ const velocity = new THREE39__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
10910
+ const speed = velocity.length();
10911
+ if (speed > bestSpeed) {
10912
+ bestSpeed = speed;
10913
+ best = velocity;
10914
+ }
10915
+ }
10916
+ return best;
10917
+ }
10918
+ clear() {
10919
+ this.samples.length = 0;
10920
+ }
10921
+ };
10922
+ __name(_ThrowVelocityTracker, "ThrowVelocityTracker");
10923
+ var ThrowVelocityTracker = _ThrowVelocityTracker;
10924
+ function angularVelocityFrom(previous, current, deltaTime) {
10925
+ if (deltaTime <= 1e-5) return new THREE39__namespace.Vector3();
10926
+ const delta = current.clone().multiply(previous.clone().invert());
10927
+ if (delta.w < 0) {
10928
+ delta.set(-delta.x, -delta.y, -delta.z, -delta.w);
10929
+ }
10930
+ const angle = 2 * Math.acos(THREE39__namespace.MathUtils.clamp(delta.w, -1, 1));
10931
+ const sinHalf = Math.sqrt(Math.max(0, 1 - delta.w * delta.w));
10932
+ if (sinHalf < 1e-6 || angle < 1e-6) return new THREE39__namespace.Vector3();
10933
+ return new THREE39__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
10934
+ }
10935
+ __name(angularVelocityFrom, "angularVelocityFrom");
10936
+
10937
+ // core/systems/XRInteractionSystem.ts
10503
10938
  var RAYLESS_HOLD_DISTANCE = 0.5;
10504
10939
  var _XRInteractionSystem = class _XRInteractionSystem {
10505
10940
  constructor(inputSystem) {
10506
10941
  this.raycaster = new THREE39__namespace.Raycaster();
10507
- /** Which entity each source is holding. Keyed by source state identity. */
10508
10942
  this.grabs = /* @__PURE__ */ new Map();
10509
10943
  this.hoveredEntityId = null;
10944
+ /** Monotonic seconds, accumulated from deltaTime for velocity sampling. */
10945
+ this.elapsed = 0;
10510
10946
  this.inputSystem = inputSystem;
10511
10947
  }
10512
- update(world) {
10948
+ update(world, deltaTime) {
10513
10949
  if (!world.isXrPresenting()) {
10514
10950
  this.releaseAll(world);
10515
10951
  return;
10516
10952
  }
10953
+ this.elapsed += Math.max(0, deltaTime);
10517
10954
  const inputs = this.inputSystem.getInputs();
10518
10955
  const interactables = this.collectInteractables(world);
10519
10956
  this.hoveredEntityId = null;
@@ -10524,29 +10961,100 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10524
10961
  }
10525
10962
  const held = this.grabs.get(input);
10526
10963
  if (held) {
10527
- this.moveHeld(world, input, held);
10528
- if (input.selectEnd) this.release(world, input);
10964
+ this.moveHeld(world, input, held, deltaTime);
10965
+ if (this.wantsRelease(world, input, held)) {
10966
+ this.release(world, input);
10967
+ }
10529
10968
  continue;
10530
10969
  }
10531
- const hitEntityId = this.raycast(input, interactables);
10532
- if (hitEntityId === null) continue;
10970
+ const candidate = this.findGrabCandidate(world, input, interactables);
10971
+ if (candidate === null) continue;
10533
10972
  if (this.hoveredEntityId === null) {
10534
- this.hoveredEntityId = hitEntityId;
10973
+ this.hoveredEntityId = candidate.entityId;
10535
10974
  }
10536
10975
  const interactable = world.getComponent(
10537
- hitEntityId,
10976
+ candidate.entityId,
10538
10977
  XRInteractableComponent
10539
10978
  );
10540
- if (input.selectStart && interactable?.grabbable) {
10541
- this.grab(world, input, hitEntityId);
10979
+ if (interactable?.grabbable && this.gestureMatches(
10980
+ input,
10981
+ interactable,
10982
+ /* starting */
10983
+ true
10984
+ )) {
10985
+ this.grab(world, input, candidate.entityId, candidate.viaDistance);
10986
+ }
10987
+ }
10988
+ }
10989
+ /** Whether the grab gesture for this object is currently starting. */
10990
+ gestureMatches(input, interactable, starting) {
10991
+ const pinch = starting ? input.selectStart : input.select;
10992
+ const full = starting ? input.squeezeStart : input.squeeze;
10993
+ switch (interactable.grabGesture) {
10994
+ case "pinch":
10995
+ return pinch;
10996
+ case "full":
10997
+ return full;
10998
+ default:
10999
+ return pinch || full;
11000
+ }
11001
+ }
11002
+ /** True once the gesture that started the grab has ended. */
11003
+ wantsRelease(world, input, held) {
11004
+ const interactable = world.getComponent(
11005
+ held.entityId,
11006
+ XRInteractableComponent
11007
+ );
11008
+ if (!interactable) return true;
11009
+ switch (interactable.grabGesture) {
11010
+ case "pinch":
11011
+ return input.selectEnd || !input.select;
11012
+ case "full":
11013
+ return input.squeezeEnd || !input.squeeze;
11014
+ default:
11015
+ return !input.select && !input.squeeze;
11016
+ }
11017
+ }
11018
+ /**
11019
+ * Find what this hand would grab: a physically-reachable object first, then
11020
+ * one along the pointer ray.
11021
+ *
11022
+ * Proximity wins over the ray so that reaching for something you are touching
11023
+ * never picks up a distant object behind it, which feels broken.
11024
+ */
11025
+ findGrabCandidate(world, input, interactables) {
11026
+ const hand = this.handPosition(input);
11027
+ let nearestId = null;
11028
+ let nearestDistance = Number.POSITIVE_INFINITY;
11029
+ for (const entry of interactables) {
11030
+ const interactable2 = world.getComponent(
11031
+ entry.entityId,
11032
+ XRInteractableComponent
11033
+ );
11034
+ if (!interactable2) continue;
11035
+ if (interactable2.grabReach === "distance") continue;
11036
+ const position = new THREE39__namespace.Vector3();
11037
+ entry.object.getWorldPosition(position);
11038
+ const distance = position.distanceTo(hand);
11039
+ if (distance <= interactable2.grabRadius && distance < nearestDistance) {
11040
+ nearestDistance = distance;
11041
+ nearestId = entry.entityId;
10542
11042
  }
10543
11043
  }
11044
+ if (nearestId !== null) {
11045
+ return { entityId: nearestId, viaDistance: false };
11046
+ }
11047
+ const hitId = this.raycast(input, interactables);
11048
+ if (hitId === null) return null;
11049
+ const interactable = world.getComponent(hitId, XRInteractableComponent);
11050
+ const viaDistanceAllowed = interactable?.grabReach === "distance" || interactable?.grabReach === "both";
11051
+ return { entityId: hitId, viaDistance: viaDistanceAllowed };
10544
11052
  }
10545
11053
  collectInteractables(world) {
10546
11054
  const out = [];
10547
11055
  for (const entityId of world.query(XRInteractableComponent, Object3DRef)) {
10548
11056
  const interactable = world.getComponent(entityId, XRInteractableComponent);
10549
- if (!interactable?.enabled || !interactable.pointable) continue;
11057
+ if (!interactable?.enabled) continue;
10550
11058
  if (!world.isEntityVisible(entityId)) continue;
10551
11059
  const object = world.getComponent(entityId, Object3DRef)?.object;
10552
11060
  if (object) out.push({ entityId, object });
@@ -10557,6 +11065,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10557
11065
  raycast(input, interactables) {
10558
11066
  if (interactables.length === 0) return null;
10559
11067
  this.raycaster.set(input.rayOrigin, input.rayDirection);
11068
+ this.raycaster.far = Number.POSITIVE_INFINITY;
10560
11069
  let nearestId = null;
10561
11070
  let nearestDistance = Number.POSITIVE_INFINITY;
10562
11071
  for (const entry of interactables) {
@@ -10569,43 +11078,120 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10569
11078
  }
10570
11079
  return nearestId;
10571
11080
  }
10572
- grab(world, input, entityId) {
11081
+ grab(world, input, entityId, viaDistance) {
10573
11082
  const transform = world.getComponent(entityId, TransformComponent);
10574
- if (!transform) return;
11083
+ const interactable = world.getComponent(entityId, XRInteractableComponent);
11084
+ if (!transform || !interactable) return;
10575
11085
  const hand = this.handPosition(input);
10576
- const offset = new THREE39__namespace.Vector3(
10577
- transform.position.x - hand.x,
10578
- transform.position.y - hand.y,
10579
- transform.position.z - hand.z
10580
- );
11086
+ const handQuaternion = this.handQuaternion(input);
11087
+ const remote = viaDistance && interactable.distanceBehavior === "remote";
11088
+ let offset;
11089
+ let remoteDistance = 0;
11090
+ if (remote) {
11091
+ const position = new THREE39__namespace.Vector3(
11092
+ transform.position.x,
11093
+ transform.position.y,
11094
+ transform.position.z
11095
+ );
11096
+ remoteDistance = position.distanceTo(input.rayOrigin);
11097
+ offset = new THREE39__namespace.Vector3();
11098
+ } else if (viaDistance) {
11099
+ offset = new THREE39__namespace.Vector3(
11100
+ interactable.grabOffsetPosition.x,
11101
+ interactable.grabOffsetPosition.y,
11102
+ interactable.grabOffsetPosition.z
11103
+ );
11104
+ } else {
11105
+ offset = new THREE39__namespace.Vector3(
11106
+ transform.position.x - hand.x,
11107
+ transform.position.y - hand.y,
11108
+ transform.position.z - hand.z
11109
+ );
11110
+ }
10581
11111
  const physics = world.getComponent(entityId, PhysicsComponent);
10582
11112
  let restoreBodyType = null;
10583
- const interactable = world.getComponent(entityId, XRInteractableComponent);
10584
- if (physics && interactable?.grabMode === "kinematic") {
11113
+ if (physics) {
10585
11114
  restoreBodyType = physics.values.rigidbody.type;
10586
11115
  physics.values.rigidbody.type = "kinematicPosition";
10587
11116
  }
10588
- this.grabs.set(input, { entityId, offset, restoreBodyType });
11117
+ const tracker = new ThrowVelocityTracker();
11118
+ tracker.record(hand, this.elapsed);
11119
+ this.grabs.set(input, {
11120
+ entityId,
11121
+ offset,
11122
+ rotationOffset: new THREE39__namespace.Quaternion().setFromEuler(
11123
+ new THREE39__namespace.Euler(
11124
+ interactable.grabOffsetRotation.x,
11125
+ interactable.grabOffsetRotation.y,
11126
+ interactable.grabOffsetRotation.z
11127
+ )
11128
+ ),
11129
+ remoteDistance,
11130
+ remote,
11131
+ restoreBodyType,
11132
+ tracker,
11133
+ lastHandQuaternion: handQuaternion.clone(),
11134
+ angularVelocity: new THREE39__namespace.Vector3()
11135
+ });
10589
11136
  this.dispatch(world, entityId, (b) => b.onGrab(input));
10590
11137
  }
10591
- moveHeld(world, input, held) {
11138
+ moveHeld(world, input, held, deltaTime) {
10592
11139
  const transform = world.getComponent(held.entityId, TransformComponent);
10593
11140
  if (!transform) return;
10594
11141
  const hand = this.handPosition(input);
10595
- transform.position.x = hand.x + held.offset.x;
10596
- transform.position.y = hand.y + held.offset.y;
10597
- transform.position.z = hand.z + held.offset.z;
11142
+ if (held.remote) {
11143
+ const target = new THREE39__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
11144
+ transform.position.x = target.x;
11145
+ transform.position.y = target.y;
11146
+ transform.position.z = target.z;
11147
+ } else {
11148
+ transform.position.x = hand.x + held.offset.x;
11149
+ transform.position.y = hand.y + held.offset.y;
11150
+ transform.position.z = hand.z + held.offset.z;
11151
+ }
11152
+ held.tracker.record(
11153
+ held.remote ? new THREE39__namespace.Vector3(
11154
+ transform.position.x,
11155
+ transform.position.y,
11156
+ transform.position.z
11157
+ ) : hand,
11158
+ this.elapsed
11159
+ );
11160
+ const handQuaternion = this.handQuaternion(input);
11161
+ held.angularVelocity = angularVelocityFrom(
11162
+ held.lastHandQuaternion,
11163
+ handQuaternion,
11164
+ deltaTime
11165
+ );
11166
+ held.lastHandQuaternion = handQuaternion;
10598
11167
  }
10599
11168
  release(world, input) {
10600
11169
  const held = this.grabs.get(input);
10601
11170
  if (!held) return;
10602
11171
  this.grabs.delete(input);
10603
- if (held.restoreBodyType !== null) {
10604
- const physics = world.getComponent(held.entityId, PhysicsComponent);
10605
- if (physics) {
10606
- physics.values.rigidbody.type = held.restoreBodyType;
11172
+ const interactable = world.getComponent(
11173
+ held.entityId,
11174
+ XRInteractableComponent
11175
+ );
11176
+ const physics = world.getComponent(held.entityId, PhysicsComponent);
11177
+ if (held.restoreBodyType !== null && physics) {
11178
+ physics.values.rigidbody.type = held.restoreBodyType;
11179
+ const body = physics.rigidBody;
11180
+ const dynamic = held.restoreBodyType === "dynamic";
11181
+ if (body && dynamic && interactable?.throwOnRelease) {
11182
+ const velocity = held.tracker.computeVelocity(this.elapsed).multiplyScalar(interactable.throwStrength);
11183
+ try {
11184
+ body.setLinvel(
11185
+ { x: velocity.x, y: velocity.y, z: velocity.z },
11186
+ true
11187
+ );
11188
+ const spin = held.angularVelocity;
11189
+ body.setAngvel({ x: spin.x, y: spin.y, z: spin.z }, true);
11190
+ } catch {
11191
+ }
10607
11192
  }
10608
11193
  }
11194
+ held.tracker.clear();
10609
11195
  this.dispatch(world, held.entityId, (b) => b.onRelease(input));
10610
11196
  }
10611
11197
  releaseAll(world) {
@@ -10614,14 +11200,25 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10614
11200
  }
10615
11201
  }
10616
11202
  /**
10617
- * Where the "hand" is for this source. Controllers and tracked hands report a
10618
- * grip pose; gaze and transient-pointer sources have none, so we hold the
10619
- * object a fixed distance along the ray instead.
11203
+ * Where the "hand" is. Controllers and tracked hands report a grip pose;
11204
+ * gaze and transient-pointer sources have none, so we hold the object a fixed
11205
+ * distance along the ray instead — which is what makes Vision Pro work.
10620
11206
  */
10621
11207
  handPosition(input) {
10622
11208
  if (input.gripPosition) return input.gripPosition;
10623
11209
  return new THREE39__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
10624
11210
  }
11211
+ /**
11212
+ * Hand orientation. Grip-less sources (gaze / transient-pointer) have none,
11213
+ * so we derive one from the aim direction — enough for spin on release.
11214
+ */
11215
+ handQuaternion(input) {
11216
+ if (input.gripQuaternion) return input.gripQuaternion.clone();
11217
+ return new THREE39__namespace.Quaternion().setFromUnitVectors(
11218
+ new THREE39__namespace.Vector3(0, 0, -1),
11219
+ input.rayDirection.clone().normalize()
11220
+ );
11221
+ }
10625
11222
  dispatch(world, entityId, visit) {
10626
11223
  const script = world.getComponent(entityId, ScriptComponent);
10627
11224
  const instances = script?.scriptInstances;
@@ -11191,6 +11788,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11191
11788
  this.handStyleSignature = "";
11192
11789
  this.handMaterial = null;
11193
11790
  this.interactionSystem = options?.interactionSystem ?? null;
11791
+ this.inputSystem = options?.inputSystem ?? null;
11194
11792
  this.group.name = "XRControllerVisuals";
11195
11793
  this.group.userData.ecsPreviewHidden = true;
11196
11794
  }
@@ -11207,8 +11805,11 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11207
11805
  for (const { index, side } of SLOTS) {
11208
11806
  const visuals = this.slots.get(index);
11209
11807
  if (!visuals) continue;
11210
- visuals.controllerModel.visible = showControllers;
11211
- visuals.handModel.visible = showHands;
11808
+ const liveKind = this.liveKindFor(side);
11809
+ const hasController = liveKind === null || liveKind === "controller";
11810
+ const hasHand = liveKind === null || liveKind === "hand";
11811
+ visuals.controllerModel.visible = showControllers && hasController;
11812
+ visuals.handModel.visible = showHands && hasHand;
11212
11813
  visuals.ray.visible = this.shouldShowRay(interactor);
11213
11814
  if (showHands && this.handMaterial) {
11214
11815
  applyHandMaterial(visuals.handModel, this.handMaterial);
@@ -11216,6 +11817,20 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11216
11817
  this.syncCustomContent(world, visuals, side);
11217
11818
  }
11218
11819
  }
11820
+ /**
11821
+ * What kind of source is currently driving this hand, or null when we cannot
11822
+ * tell (no input system wired) — in which case callers fall back to showing
11823
+ * everything rather than hiding the user's hands entirely.
11824
+ */
11825
+ liveKindFor(side) {
11826
+ if (!this.inputSystem) return null;
11827
+ const inputs = this.inputSystem.getInputs();
11828
+ if (inputs.length === 0) return null;
11829
+ for (const input of inputs) {
11830
+ if (input.handedness === side && input.connected) return input.kind;
11831
+ }
11832
+ return "none";
11833
+ }
11219
11834
  /** The scene's interactor config, if one was authored. */
11220
11835
  resolveInteractor(world) {
11221
11836
  const entityId = world.query(XRInteractorComponent)[0];
@@ -11475,6 +12090,511 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
11475
12090
  };
11476
12091
  __name(_XRTeleportVisualSystem, "XRTeleportVisualSystem");
11477
12092
  var XRTeleportVisualSystem = _XRTeleportVisualSystem;
12093
+ var XR_FINGERS = [
12094
+ "thumb",
12095
+ "index",
12096
+ "middle",
12097
+ "ring",
12098
+ "pinky"
12099
+ ];
12100
+ var PINCH_FINGERS = [
12101
+ "index",
12102
+ "middle",
12103
+ "ring",
12104
+ "pinky"
12105
+ ];
12106
+ var FINGER_CHAINS = {
12107
+ thumb: [
12108
+ "thumb-metacarpal",
12109
+ "thumb-phalanx-proximal",
12110
+ "thumb-phalanx-distal",
12111
+ "thumb-tip"
12112
+ ],
12113
+ index: [
12114
+ "index-finger-metacarpal",
12115
+ "index-finger-phalanx-proximal",
12116
+ "index-finger-phalanx-intermediate",
12117
+ "index-finger-phalanx-distal",
12118
+ "index-finger-tip"
12119
+ ],
12120
+ middle: [
12121
+ "middle-finger-metacarpal",
12122
+ "middle-finger-phalanx-proximal",
12123
+ "middle-finger-phalanx-intermediate",
12124
+ "middle-finger-phalanx-distal",
12125
+ "middle-finger-tip"
12126
+ ],
12127
+ ring: [
12128
+ "ring-finger-metacarpal",
12129
+ "ring-finger-phalanx-proximal",
12130
+ "ring-finger-phalanx-intermediate",
12131
+ "ring-finger-phalanx-distal",
12132
+ "ring-finger-tip"
12133
+ ],
12134
+ pinky: [
12135
+ "pinky-finger-metacarpal",
12136
+ "pinky-finger-phalanx-proximal",
12137
+ "pinky-finger-phalanx-intermediate",
12138
+ "pinky-finger-phalanx-distal",
12139
+ "pinky-finger-tip"
12140
+ ]
12141
+ };
12142
+ var TIP_JOINTS = {
12143
+ thumb: "thumb-tip",
12144
+ index: "index-finger-tip",
12145
+ middle: "middle-finger-tip",
12146
+ ring: "ring-finger-tip",
12147
+ pinky: "pinky-finger-tip"
12148
+ };
12149
+ var METACARPAL_JOINTS = {
12150
+ thumb: "thumb-metacarpal",
12151
+ index: "index-finger-metacarpal",
12152
+ middle: "middle-finger-metacarpal",
12153
+ ring: "ring-finger-metacarpal",
12154
+ pinky: "pinky-finger-metacarpal"
12155
+ };
12156
+ var FALLBACK_HAND_SCALE = 0.08;
12157
+ var STRAIGHT_COS = 0.95;
12158
+ var CURLED_COS = 0.55;
12159
+ function clamp01(value) {
12160
+ return value < 0 ? 0 : value > 1 ? 1 : value;
12161
+ }
12162
+ __name(clamp01, "clamp01");
12163
+ function boneDirections(joints, finger) {
12164
+ const chain = FINGER_CHAINS[finger];
12165
+ const directions = [];
12166
+ for (let i = 1; i < chain.length; i += 1) {
12167
+ const from = joints[chain[i - 1]];
12168
+ const to = joints[chain[i]];
12169
+ if (!from || !to) return null;
12170
+ const direction = new THREE39__namespace.Vector3().subVectors(to, from);
12171
+ if (direction.lengthSq() < 1e-12) return null;
12172
+ directions.push(direction.normalize());
12173
+ }
12174
+ return directions.length >= 2 ? directions : null;
12175
+ }
12176
+ __name(boneDirections, "boneDirections");
12177
+ function bendAmounts(directions) {
12178
+ const bends = [];
12179
+ for (let i = 1; i < directions.length; i += 1) {
12180
+ const cos = directions[i - 1].dot(directions[i]);
12181
+ const straightness = clamp01((cos - CURLED_COS) / (STRAIGHT_COS - CURLED_COS));
12182
+ bends.push(1 - straightness);
12183
+ }
12184
+ return bends;
12185
+ }
12186
+ __name(bendAmounts, "bendAmounts");
12187
+ function average(values) {
12188
+ if (values.length === 0) return 0;
12189
+ let sum = 0;
12190
+ for (const value of values) sum += value;
12191
+ return sum / values.length;
12192
+ }
12193
+ __name(average, "average");
12194
+ function computeFingerCurl(joints, finger) {
12195
+ const directions = boneDirections(joints, finger);
12196
+ if (!directions) return null;
12197
+ const bends = bendAmounts(directions);
12198
+ if (bends.length === 0) return null;
12199
+ return {
12200
+ baseCurl: bends[0],
12201
+ tipCurl: average(bends.slice(1)) || bends[bends.length - 1],
12202
+ curl: average(bends)
12203
+ };
12204
+ }
12205
+ __name(computeFingerCurl, "computeFingerCurl");
12206
+ function computePalmWidth(joints) {
12207
+ const index = joints["index-finger-metacarpal"];
12208
+ const pinky = joints["pinky-finger-metacarpal"];
12209
+ if (!index || !pinky) return null;
12210
+ const width = index.distanceTo(pinky);
12211
+ return width > 1e-6 ? width : null;
12212
+ }
12213
+ __name(computePalmWidth, "computePalmWidth");
12214
+ function estimateHandScale(joints) {
12215
+ const measurements = [];
12216
+ const width = computePalmWidth(joints);
12217
+ if (width !== null) measurements.push(width);
12218
+ const wrist = joints.wrist;
12219
+ const middleTip = joints["middle-finger-tip"];
12220
+ if (wrist && middleTip) {
12221
+ const span = wrist.distanceTo(middleTip);
12222
+ if (span > 1e-6) measurements.push(span * 0.5);
12223
+ }
12224
+ if (measurements.length === 0) return FALLBACK_HAND_SCALE;
12225
+ return average(measurements);
12226
+ }
12227
+ __name(estimateHandScale, "estimateHandScale");
12228
+ function computePinch(joints, finger) {
12229
+ const thumbTip = joints["thumb-tip"];
12230
+ const fingerTip = joints[TIP_JOINTS[finger]];
12231
+ if (!thumbTip || !fingerTip) return null;
12232
+ const scale = estimateHandScale(joints);
12233
+ const distance = thumbTip.distanceTo(fingerTip);
12234
+ return clamp01(1 - distance / (scale * 0.7));
12235
+ }
12236
+ __name(computePinch, "computePinch");
12237
+ function computeSpread(joints, a, b) {
12238
+ const aBase = joints[METACARPAL_JOINTS[a]];
12239
+ const aTip = joints[TIP_JOINTS[a]];
12240
+ const bBase = joints[METACARPAL_JOINTS[b]];
12241
+ const bTip = joints[TIP_JOINTS[b]];
12242
+ if (!aBase || !aTip || !bBase || !bTip) return null;
12243
+ const dirA = new THREE39__namespace.Vector3().subVectors(aTip, aBase);
12244
+ const dirB = new THREE39__namespace.Vector3().subVectors(bTip, bBase);
12245
+ if (dirA.lengthSq() < 1e-12 || dirB.lengthSq() < 1e-12) return null;
12246
+ return clamp01((1 - dirA.normalize().dot(dirB.normalize())) / 0.45);
12247
+ }
12248
+ __name(computeSpread, "computeSpread");
12249
+ function computePalmPose(joints, handedness) {
12250
+ const wrist = joints.wrist;
12251
+ const indexBase = joints["index-finger-metacarpal"];
12252
+ const pinkyBase = joints["pinky-finger-metacarpal"];
12253
+ if (!wrist || !indexBase || !pinkyBase) return null;
12254
+ const u = new THREE39__namespace.Vector3().subVectors(indexBase, wrist);
12255
+ const v = new THREE39__namespace.Vector3().subVectors(pinkyBase, wrist);
12256
+ const normal = new THREE39__namespace.Vector3().crossVectors(u, v);
12257
+ if (normal.lengthSq() < 1e-12) return null;
12258
+ normal.normalize();
12259
+ if (handedness === "left") normal.multiplyScalar(-1);
12260
+ const right = new THREE39__namespace.Vector3().subVectors(indexBase, pinkyBase);
12261
+ if (right.lengthSq() < 1e-12) return null;
12262
+ right.normalize();
12263
+ if (handedness === "left") right.multiplyScalar(-1);
12264
+ const up = new THREE39__namespace.Vector3().crossVectors(normal, right).normalize();
12265
+ const center = new THREE39__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
12266
+ return {
12267
+ center,
12268
+ normal,
12269
+ right,
12270
+ up,
12271
+ width: computePalmWidth(joints) ?? FALLBACK_HAND_SCALE
12272
+ };
12273
+ }
12274
+ __name(computePalmPose, "computePalmPose");
12275
+ function computeHandShape(joints, handedness) {
12276
+ const fingers2 = {};
12277
+ let tracked = 0;
12278
+ for (const finger of XR_FINGERS) {
12279
+ const curl = computeFingerCurl(joints, finger);
12280
+ if (curl) {
12281
+ fingers2[finger] = curl;
12282
+ tracked += 1;
12283
+ } else {
12284
+ fingers2[finger] = { baseCurl: 0, tipCurl: 0, curl: 0 };
12285
+ }
12286
+ }
12287
+ if (tracked === 0) return null;
12288
+ const pinch = {};
12289
+ for (const finger of PINCH_FINGERS) {
12290
+ pinch[finger] = computePinch(joints, finger) ?? 0;
12291
+ }
12292
+ const thumbBase = joints["thumb-metacarpal"];
12293
+ const thumbTip = joints["thumb-tip"];
12294
+ let thumbDirection = null;
12295
+ if (thumbBase && thumbTip) {
12296
+ const direction = new THREE39__namespace.Vector3().subVectors(thumbTip, thumbBase);
12297
+ if (direction.lengthSq() > 1e-12) thumbDirection = direction.normalize();
12298
+ }
12299
+ return {
12300
+ handedness,
12301
+ fingers: fingers2,
12302
+ pinch,
12303
+ spread: {
12304
+ thumbIndex: computeSpread(joints, "thumb", "index") ?? 0,
12305
+ indexMiddle: computeSpread(joints, "index", "middle") ?? 0,
12306
+ middleRing: computeSpread(joints, "middle", "ring") ?? 0,
12307
+ ringPinky: computeSpread(joints, "ring", "pinky") ?? 0
12308
+ },
12309
+ palm: computePalmPose(joints, handedness),
12310
+ scale: estimateHandScale(joints),
12311
+ thumbDirection
12312
+ };
12313
+ }
12314
+ __name(computeHandShape, "computeHandShape");
12315
+ var MATCH_FALLOFF = 0.25;
12316
+ var NO_MATCH = {
12317
+ matched: false,
12318
+ confidence: 0,
12319
+ worstConstraint: null
12320
+ };
12321
+ function scoreAgainst(actual, target, tolerance) {
12322
+ const error = Math.abs(actual - target);
12323
+ if (error <= tolerance) return 1;
12324
+ return clamp01(1 - (error - tolerance) / MATCH_FALLOFF);
12325
+ }
12326
+ __name(scoreAgainst, "scoreAgainst");
12327
+ var WORLD_DIRECTIONS = {
12328
+ up: new THREE39__namespace.Vector3(0, 1, 0),
12329
+ down: new THREE39__namespace.Vector3(0, -1, 0),
12330
+ forward: new THREE39__namespace.Vector3(0, 0, -1),
12331
+ back: new THREE39__namespace.Vector3(0, 0, 1),
12332
+ left: new THREE39__namespace.Vector3(-1, 0, 0),
12333
+ right: new THREE39__namespace.Vector3(1, 0, 0)
12334
+ };
12335
+ function axisVector(shape, axis) {
12336
+ if (axis === "thumb") return shape.thumbDirection;
12337
+ if (!shape.palm) return null;
12338
+ return axis === "palm-normal" ? shape.palm.normal : shape.palm.up;
12339
+ }
12340
+ __name(axisVector, "axisVector");
12341
+ function resolveDirection(direction, shape, context) {
12342
+ if (direction !== "toward-head" && direction !== "away-from-head") {
12343
+ return WORLD_DIRECTIONS[direction];
12344
+ }
12345
+ const head = context?.headPosition;
12346
+ const origin = shape.palm?.center;
12347
+ if (!head || !origin) return null;
12348
+ const toHead = new THREE39__namespace.Vector3().subVectors(head, origin);
12349
+ if (toHead.lengthSq() < 1e-12) return null;
12350
+ toHead.normalize();
12351
+ return direction === "toward-head" ? toHead : toHead.multiplyScalar(-1);
12352
+ }
12353
+ __name(resolveDirection, "resolveDirection");
12354
+ function matchHandPose(shape, definition, context) {
12355
+ if (definition.hand && definition.hand !== shape.handedness) return NO_MATCH;
12356
+ let confidence = 1;
12357
+ let worstScore = 1;
12358
+ let worstConstraint = null;
12359
+ const consider = /* @__PURE__ */ __name((score, label) => {
12360
+ if (score < confidence) confidence = score;
12361
+ if (score < worstScore) {
12362
+ worstScore = score;
12363
+ worstConstraint = label;
12364
+ }
12365
+ }, "consider");
12366
+ for (const finger of XR_FINGERS) {
12367
+ const target = definition.fingers[finger];
12368
+ if (!target) continue;
12369
+ const actual = shape.fingers[finger];
12370
+ consider(scoreAgainst(actual.curl, target.curl, target.tolerance), finger);
12371
+ if (target.baseCurl !== void 0) {
12372
+ consider(
12373
+ scoreAgainst(actual.baseCurl, target.baseCurl, target.tolerance),
12374
+ `${finger} knuckle`
12375
+ );
12376
+ }
12377
+ if (target.tipCurl !== void 0) {
12378
+ consider(
12379
+ scoreAgainst(actual.tipCurl, target.tipCurl, target.tolerance),
12380
+ `${finger} tip`
12381
+ );
12382
+ }
12383
+ }
12384
+ if (definition.pinch) {
12385
+ const { finger, target, tolerance } = definition.pinch;
12386
+ consider(
12387
+ scoreAgainst(shape.pinch[finger], target, tolerance),
12388
+ `${finger} pinch`
12389
+ );
12390
+ }
12391
+ if (definition.orientation) {
12392
+ const { axis, direction, toleranceDegrees } = definition.orientation;
12393
+ const handAxis = axisVector(shape, axis);
12394
+ const wanted = resolveDirection(direction, shape, context);
12395
+ if (!handAxis || !wanted) {
12396
+ consider(0, "orientation");
12397
+ } else {
12398
+ const angle = THREE39__namespace.MathUtils.radToDeg(
12399
+ Math.acos(THREE39__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
12400
+ );
12401
+ const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
12402
+ consider(clamp01(1 - normalizedError / MATCH_FALLOFF), "orientation");
12403
+ }
12404
+ }
12405
+ return {
12406
+ matched: confidence >= 1,
12407
+ confidence,
12408
+ worstConstraint: confidence >= 1 ? null : worstConstraint
12409
+ };
12410
+ }
12411
+ __name(matchHandPose, "matchHandPose");
12412
+ var DEFAULT_ENTER_CONFIDENCE = 0.8;
12413
+ var DEFAULT_EXIT_CONFIDENCE = 0.55;
12414
+ var DEFAULT_MIN_HOLD_SECONDS = 0.15;
12415
+ var DEFAULT_LOST_GRACE_SECONDS = 0.25;
12416
+ var _HandPoseTracker = class _HandPoseTracker {
12417
+ constructor(options) {
12418
+ this.active = false;
12419
+ this.holdingSince = null;
12420
+ this.lostSince = null;
12421
+ this.enterConfidence = options?.enterConfidence ?? DEFAULT_ENTER_CONFIDENCE;
12422
+ const exit = options?.exitConfidence ?? DEFAULT_EXIT_CONFIDENCE;
12423
+ this.exitConfidence = Math.min(exit, this.enterConfidence - 1e-3);
12424
+ this.minHoldSeconds = options?.minHoldSeconds ?? DEFAULT_MIN_HOLD_SECONDS;
12425
+ this.lostGraceSeconds = options?.lostGraceSeconds ?? DEFAULT_LOST_GRACE_SECONDS;
12426
+ }
12427
+ isActive() {
12428
+ return this.active;
12429
+ }
12430
+ /** Feed this frame's confidence. Returns an event on a transition. */
12431
+ update(confidence, now) {
12432
+ this.lostSince = null;
12433
+ if (this.active) {
12434
+ if (confidence < this.exitConfidence) {
12435
+ this.active = false;
12436
+ this.holdingSince = null;
12437
+ return "end";
12438
+ }
12439
+ return null;
12440
+ }
12441
+ if (confidence >= this.enterConfidence) {
12442
+ if (this.holdingSince === null) this.holdingSince = now;
12443
+ if (now - this.holdingSince >= this.minHoldSeconds) {
12444
+ this.active = true;
12445
+ return "start";
12446
+ }
12447
+ return null;
12448
+ }
12449
+ this.holdingSince = null;
12450
+ return null;
12451
+ }
12452
+ /** Feed a frame where the hand isn't tracked at all. */
12453
+ miss(now) {
12454
+ this.holdingSince = null;
12455
+ if (!this.active) return null;
12456
+ if (this.lostSince === null) this.lostSince = now;
12457
+ if (now - this.lostSince < this.lostGraceSeconds) return null;
12458
+ this.active = false;
12459
+ this.lostSince = null;
12460
+ return "end";
12461
+ }
12462
+ reset() {
12463
+ this.active = false;
12464
+ this.holdingSince = null;
12465
+ this.lostSince = null;
12466
+ }
12467
+ };
12468
+ __name(_HandPoseTracker, "HandPoseTracker");
12469
+ var HandPoseTracker = _HandPoseTracker;
12470
+
12471
+ // core/systems/XRHandPoseSystem.ts
12472
+ var _XRHandPoseSystem = class _XRHandPoseSystem {
12473
+ constructor(inputSystem) {
12474
+ this.trackers = /* @__PURE__ */ new Map();
12475
+ this.shapes = /* @__PURE__ */ new Map();
12476
+ /** Reused so head-relative orientation checks allocate nothing per frame. */
12477
+ this.headPosition = new THREE39__namespace.Vector3();
12478
+ this.elapsed = 0;
12479
+ this.inputSystem = inputSystem;
12480
+ }
12481
+ update(world, deltaTime) {
12482
+ if (!world.isXrPresenting()) {
12483
+ this.resetAll();
12484
+ return;
12485
+ }
12486
+ this.elapsed += deltaTime;
12487
+ this.refreshShapes();
12488
+ const entries = this.collectEntries(world);
12489
+ if (entries.length === 0) return;
12490
+ world.getCamera().getWorldPosition(this.headPosition);
12491
+ const context = { headPosition: this.headPosition };
12492
+ for (const { entityId, entry } of entries) {
12493
+ const definition = handPoseDefinitionFor(entry, getHandPosePreset);
12494
+ if (!definition) continue;
12495
+ for (const handedness of ["left", "right"]) {
12496
+ if (entry.hand && entry.hand !== handedness) continue;
12497
+ const key = `${entityId}:${entry.id}`;
12498
+ const trackers = this.trackersFor(handedness);
12499
+ let tracker = trackers.get(key);
12500
+ if (!tracker) {
12501
+ tracker = new HandPoseTracker({
12502
+ minHoldSeconds: entry.minHoldSeconds
12503
+ });
12504
+ trackers.set(key, tracker);
12505
+ }
12506
+ const shape = this.shapes.get(handedness) ?? null;
12507
+ if (!shape) {
12508
+ if (tracker.miss(this.elapsed) === "end") {
12509
+ this.dispatch(world, entityId, "end", {
12510
+ poseId: entry.id,
12511
+ poseName: entry.name,
12512
+ handedness,
12513
+ confidence: 0
12514
+ });
12515
+ }
12516
+ continue;
12517
+ }
12518
+ const match = matchHandPose(shape, definition, context);
12519
+ const event = tracker.update(match.confidence, this.elapsed);
12520
+ if (event) {
12521
+ this.dispatch(world, entityId, event, {
12522
+ poseId: entry.id,
12523
+ poseName: entry.name,
12524
+ handedness,
12525
+ confidence: match.confidence
12526
+ });
12527
+ }
12528
+ }
12529
+ }
12530
+ }
12531
+ /** Derive each hand's shape once for the whole frame. */
12532
+ refreshShapes() {
12533
+ for (const handedness of ["left", "right"]) {
12534
+ const input = this.inputSystem.getInput(handedness);
12535
+ const joints = input?.joints ?? null;
12536
+ this.shapes.set(
12537
+ handedness,
12538
+ joints ? computeHandShape(joints, handedness) : null
12539
+ );
12540
+ }
12541
+ }
12542
+ collectEntries(world) {
12543
+ const collected = [];
12544
+ for (const entityId of world.query(XRHandPoseComponent)) {
12545
+ const component = world.getComponent(entityId, XRHandPoseComponent);
12546
+ if (!component?.enabled) continue;
12547
+ for (const entry of component.poses) {
12548
+ if (entry.enabled) collected.push({ entityId, entry });
12549
+ }
12550
+ }
12551
+ return collected;
12552
+ }
12553
+ trackersFor(handedness) {
12554
+ let trackers = this.trackers.get(handedness);
12555
+ if (!trackers) {
12556
+ trackers = /* @__PURE__ */ new Map();
12557
+ this.trackers.set(handedness, trackers);
12558
+ }
12559
+ return trackers;
12560
+ }
12561
+ /**
12562
+ * Deliver to the entity's own Behaviours.
12563
+ *
12564
+ * Scoped to the entity that declared the pose rather than broadcast, so two
12565
+ * objects listening for the same gesture stay independent.
12566
+ */
12567
+ dispatch(world, entityId, event, detail) {
12568
+ const script = world.getComponent(entityId, ScriptComponent);
12569
+ const instances = script?.scriptInstances;
12570
+ if (!instances) return;
12571
+ for (const instance of instances.values()) {
12572
+ if (!(instance instanceof Behaviour)) continue;
12573
+ try {
12574
+ if (event === "start") instance.onHandPoseStart(detail);
12575
+ else instance.onHandPoseEnd(detail);
12576
+ } catch (error) {
12577
+ console.error("[XRHandPoseSystem] Behaviour hook failed:", error);
12578
+ }
12579
+ }
12580
+ }
12581
+ resetAll() {
12582
+ for (const trackers of this.trackers.values()) {
12583
+ for (const tracker of trackers.values()) tracker.reset();
12584
+ }
12585
+ this.shapes.clear();
12586
+ }
12587
+ /** Live shape for a hand — powers the editor's pose readout. */
12588
+ getHandShape(handedness) {
12589
+ return this.shapes.get(handedness) ?? null;
12590
+ }
12591
+ /** Whether a given entity's pose is currently held. */
12592
+ isPoseActive(entityId, poseId, handedness) {
12593
+ return this.trackers.get(handedness)?.get(`${entityId}:${poseId}`)?.isActive() ?? false;
12594
+ }
12595
+ };
12596
+ __name(_XRHandPoseSystem, "XRHandPoseSystem");
12597
+ var XRHandPoseSystem = _XRHandPoseSystem;
11478
12598
 
11479
12599
  // core/World.ts
11480
12600
  var _World = class _World {
@@ -12858,15 +13978,26 @@ exports.AnimationStateManager = AnimationStateManager;
12858
13978
  exports.AnimationSystem = AnimationSystem;
12859
13979
  exports.AudioSystem = AudioSystem;
12860
13980
  exports.Behaviour = Behaviour;
13981
+ exports.CURLED_COS = CURLED_COS;
12861
13982
  exports.CameraSyncSystem = CameraSyncSystem;
12862
13983
  exports.Component = Component;
12863
13984
  exports.CoreComponents = components_exports;
13985
+ exports.DEFAULT_ENTER_CONFIDENCE = DEFAULT_ENTER_CONFIDENCE;
13986
+ exports.DEFAULT_EXIT_CONFIDENCE = DEFAULT_EXIT_CONFIDENCE;
13987
+ exports.DEFAULT_LOST_GRACE_SECONDS = DEFAULT_LOST_GRACE_SECONDS;
13988
+ exports.DEFAULT_MIN_HOLD_SECONDS = DEFAULT_MIN_HOLD_SECONDS;
12864
13989
  exports.EnvironmentSyncSystem = EnvironmentSyncSystem;
13990
+ exports.FALLBACK_HAND_SCALE = FALLBACK_HAND_SCALE;
13991
+ exports.HAND_POSE_PRESETS = HAND_POSE_PRESETS;
13992
+ exports.HAND_POSE_PRESET_IDS = HAND_POSE_PRESET_IDS;
12865
13993
  exports.HYDRATABLE_ENTITY_KINDS = HYDRATABLE_ENTITY_KINDS;
13994
+ exports.HandPoseTracker = HandPoseTracker;
12866
13995
  exports.InputSystem = InputSystem;
12867
13996
  exports.InstancedMeshSystem = InstancedMeshSystem;
12868
13997
  exports.KeyboardKeys = KeyboardKeys;
12869
13998
  exports.LightSyncSystem = LightSyncSystem;
13999
+ exports.MATCH_FALLOFF = MATCH_FALLOFF;
14000
+ exports.MAX_SAMPLE_AGE_SECONDS = MAX_SAMPLE_AGE_SECONDS;
12870
14001
  exports.MaterialSyncSystem = MaterialSyncSystem;
12871
14002
  exports.MeshGeometrySystem = MeshGeometrySystem;
12872
14003
  exports.NODE_KEY_SEPARATOR = NODE_KEY_SEPARATOR;
@@ -12875,6 +14006,7 @@ exports.OVERRIDE_DIFF_EXCLUDED_COMPONENTS = OVERRIDE_DIFF_EXCLUDED_COMPONENTS;
12875
14006
  exports.OVERRIDE_ROOT_EXCLUDED_COMPONENTS = OVERRIDE_ROOT_EXCLUDED_COMPONENTS;
12876
14007
  exports.ObjectManagementSystem = ObjectManagementSystem;
12877
14008
  exports.ObjectVisibilitySystem = ObjectVisibilitySystem;
14009
+ exports.PINCH_FINGERS = PINCH_FINGERS;
12878
14010
  exports.PREFAB_FILE_VERSION = PREFAB_FILE_VERSION;
12879
14011
  exports.PREFAB_FILE_VERSION_NESTED = PREFAB_FILE_VERSION_NESTED;
12880
14012
  exports.PhysicsSyncSystem = PhysicsSyncSystem;
@@ -12882,19 +14014,25 @@ exports.PostprocessingSyncSystem = PostprocessingSyncSystem;
12882
14014
  exports.RenderSystem = RenderSystem;
12883
14015
  exports.SCRIPT_OBJECT_ROOT_FLAG = SCRIPT_OBJECT_ROOT_FLAG;
12884
14016
  exports.SHARED_MODEL_RESOURCE_FLAG = SHARED_MODEL_RESOURCE_FLAG;
14017
+ exports.STRAIGHT_COS = STRAIGHT_COS;
12885
14018
  exports.ScriptBehaviourSystem = ScriptBehaviourSystem;
12886
14019
  exports.ScriptObject = ScriptObject;
12887
14020
  exports.ScriptObjectSystem = ScriptObjectSystem;
14021
+ exports.ThrowVelocityTracker = ThrowVelocityTracker;
12888
14022
  exports.TransformSyncSystem = TransformSyncSystem;
14023
+ exports.VELOCITY_SAMPLES = VELOCITY_SAMPLES;
12889
14024
  exports.VRTeleportAreaSystem = VRTeleportAreaSystem;
12890
14025
  exports.VideoSystem = VideoSystem;
12891
14026
  exports.World = World;
12892
14027
  exports.XRControllerVisualSystem = XRControllerVisualSystem;
14028
+ exports.XRHandPoseSystem = XRHandPoseSystem;
12893
14029
  exports.XRInputSystem = XRInputSystem;
12894
14030
  exports.XRInteractionSystem = XRInteractionSystem;
12895
14031
  exports.XRLocomotionSystem = XRLocomotionSystem;
12896
14032
  exports.XRSessionSystem = XRSessionSystem;
12897
14033
  exports.XRTeleportVisualSystem = XRTeleportVisualSystem;
14034
+ exports.XR_FINGERS = XR_FINGERS;
14035
+ exports.angularVelocityFrom = angularVelocityFrom;
12898
14036
  exports.applyHandMaterial = applyHandMaterial;
12899
14037
  exports.applyPrefabToSnapshotInstances = applyPrefabToSnapshotInstances;
12900
14038
  exports.arcGroundCrossing = arcGroundCrossing;
@@ -12904,11 +14042,18 @@ exports.buildNodeMapsFromSnapshot = buildNodeMapsFromSnapshot;
12904
14042
  exports.canonicalizeComponentValue = canonicalizeComponentValue;
12905
14043
  exports.clearModelCache = clearModelCache;
12906
14044
  exports.clearPrefabCache = clearPrefabCache;
14045
+ exports.cloneHandPose = cloneHandPose;
12907
14046
  exports.collectDiffPaths = collectDiffPaths;
12908
14047
  exports.collectDirectPrefabDependencies = collectDirectPrefabDependencies;
12909
14048
  exports.collectEntitySubtree = collectEntitySubtree;
12910
14049
  exports.collectSnapshotSubtreeIds = collectSnapshotSubtreeIds;
14050
+ exports.computeFingerCurl = computeFingerCurl;
14051
+ exports.computeHandShape = computeHandShape;
12911
14052
  exports.computeInstanceOverrides = computeInstanceOverrides;
14053
+ exports.computePalmPose = computePalmPose;
14054
+ exports.computePalmWidth = computePalmWidth;
14055
+ exports.computePinch = computePinch;
14056
+ exports.computeSpread = computeSpread;
12912
14057
  exports.createAnimationComponent = createAnimationComponent;
12913
14058
  exports.createAudioComponent = createAudioComponent;
12914
14059
  exports.createCameraComponent = createCameraComponent;
@@ -12938,6 +14083,7 @@ exports.createSpriteComponent = createSpriteComponent;
12938
14083
  exports.createTransformComponent = createTransformComponent;
12939
14084
  exports.createVRTeleportAreaComponent = createVRTeleportAreaComponent;
12940
14085
  exports.createVideoComponent = createVideoComponent;
14086
+ exports.createXRHandPoseComponent = createXRHandPoseComponent;
12941
14087
  exports.createXRInteractableComponent = createXRInteractableComponent;
12942
14088
  exports.createXRInteractorComponent = createXRInteractorComponent;
12943
14089
  exports.createXRInteractorSideComponent = createXRInteractorSideComponent;
@@ -12945,15 +14091,19 @@ exports.createXRRigComponent = createXRRigComponent;
12945
14091
  exports.decanonicalizeComponentValue = decanonicalizeComponentValue;
12946
14092
  exports.despawnPrefab = despawnPrefab;
12947
14093
  exports.encodeOverride = encodeOverride;
14094
+ exports.estimateHandScale = estimateHandScale;
12948
14095
  exports.findSnapshotRootEntityIds = findSnapshotRootEntityIds;
12949
14096
  exports.findTopLevelNestedRegions = findTopLevelNestedRegions;
14097
+ exports.getHandPosePreset = getHandPosePreset;
12950
14098
  exports.groupOverridePaths = groupOverridePaths;
14099
+ exports.handPoseFromCurls = handPoseFromCurls;
12951
14100
  exports.hydrateWorldFromSnapshot = hydrateWorldFromSnapshot;
12952
14101
  exports.hydrateWorldFromSnapshotDetailed = hydrateWorldFromSnapshotDetailed;
12953
14102
  exports.indexSnapshotByEntity = indexSnapshotByEntity;
12954
14103
  exports.instantiateSubtree = instantiateSubtree;
12955
14104
  exports.loadPrefab = loadPrefab;
12956
14105
  exports.loadSharedGltf = loadSharedGltf;
14106
+ exports.matchHandPose = matchHandPose;
12957
14107
  exports.mergeComponentValue = mergeComponentValue;
12958
14108
  exports.parseOverride = parseOverride;
12959
14109
  exports.parsePrefabAsset = parsePrefabAsset;