@relu-interactives/spatial-ecs 0.3.11 → 0.3.13

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 (38) hide show
  1. package/dist/{XRHandPose-C-KJeYcJ.d.cts → XRHandPose-9mVZVU3K.d.cts} +32 -8
  2. package/dist/{XRHandPose-B6I4y1lj.d.ts → XRHandPose-To0260Lc.d.ts} +32 -8
  3. package/dist/{chunk-TXEGYR76.js → chunk-3LDQ4GVZ.js} +7 -5
  4. package/dist/chunk-3LDQ4GVZ.js.map +1 -0
  5. package/dist/{chunk-Y3AC3VZ2.js → chunk-CHZQ4NXP.js} +3 -3
  6. package/dist/{chunk-Y3AC3VZ2.js.map → chunk-CHZQ4NXP.js.map} +1 -1
  7. package/dist/{chunk-FSHRPY65.js → chunk-JHFBEMXM.js} +4 -4
  8. package/dist/{chunk-FSHRPY65.js.map → chunk-JHFBEMXM.js.map} +1 -1
  9. package/dist/{chunk-KLU2M43M.js → chunk-N2634D4W.js} +112 -29
  10. package/dist/chunk-N2634D4W.js.map +1 -0
  11. package/dist/components/index.cjs +5 -3
  12. package/dist/components/index.cjs.map +1 -1
  13. package/dist/components/index.d.cts +2 -2
  14. package/dist/components/index.d.ts +2 -2
  15. package/dist/components/index.js +1 -1
  16. package/dist/docs/guide/vr-and-hand-poses.md +5 -3
  17. package/dist/factories/index.cjs +5 -3
  18. package/dist/factories/index.cjs.map +1 -1
  19. package/dist/factories/index.d.cts +1 -1
  20. package/dist/factories/index.d.ts +1 -1
  21. package/dist/factories/index.js +3 -3
  22. package/dist/{index-DPRnnSI-.d.cts → index-BPKDsVF7.d.cts} +1 -1
  23. package/dist/{index-Bi_wIo1o.d.ts → index-BbXqWyEZ.d.ts} +34 -0
  24. package/dist/{index-CEZp7qLL.d.ts → index-Wzs-8FYa.d.ts} +1 -1
  25. package/dist/{index-jlgDou9W.d.cts → index-lqw8BM6E.d.cts} +34 -0
  26. package/dist/index.cjs +113 -28
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +5 -5
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +8 -8
  31. package/dist/systems/index.cjs +113 -28
  32. package/dist/systems/index.cjs.map +1 -1
  33. package/dist/systems/index.d.cts +1 -1
  34. package/dist/systems/index.d.ts +1 -1
  35. package/dist/systems/index.js +3 -3
  36. package/package.json +1 -1
  37. package/dist/chunk-KLU2M43M.js.map +0 -1
  38. package/dist/chunk-TXEGYR76.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-Y3AC3VZ2.js';
2
- import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ParentId, ScriptComponent, ScriptObjectComponent, XRRigComponent, VRTeleportAreaComponent, XRInteractableComponent, createAttachPoint, XRHandPoseComponent, createHandPoseEntry, XRInteractorComponent, XRInteractorSideComponent, InstanceSlotComponent, Name, ImageComponent, SpriteComponent, VideoComponent, AudioComponent, ImageTarget, Selectable, PrefabNode, PrefabInstance, handPoseDefinitionFor, getHandPosePreset } from './chunk-TXEGYR76.js';
1
+ import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-CHZQ4NXP.js';
2
+ import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ParentId, ScriptComponent, ScriptObjectComponent, XRRigComponent, VRTeleportAreaComponent, XRInteractableComponent, createAttachPoint, XRHandPoseComponent, createHandPoseEntry, XRInteractorComponent, XRInteractorSideComponent, InstanceSlotComponent, Name, ImageComponent, SpriteComponent, VideoComponent, AudioComponent, ImageTarget, Selectable, PrefabNode, PrefabInstance, handPoseDefinitionFor, getHandPosePreset } from './chunk-3LDQ4GVZ.js';
3
3
  import { __name } from './chunk-OY7ZODZP.js';
4
4
  import * as THREE19 from 'three';
5
5
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
@@ -3051,11 +3051,15 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
3051
3051
  )
3052
3052
  );
3053
3053
  }
3054
- if (typeof sourceInteractable.throwOnRelease === "boolean") {
3055
- interactable.throwOnRelease = sourceInteractable.throwOnRelease;
3054
+ if (typeof sourceInteractable.applyVelocityOnRelease === "boolean") {
3055
+ interactable.applyVelocityOnRelease = sourceInteractable.applyVelocityOnRelease;
3056
+ } else if (typeof sourceInteractable.throwOnRelease === "boolean") {
3057
+ interactable.applyVelocityOnRelease = sourceInteractable.throwOnRelease;
3056
3058
  }
3057
- if (typeof sourceInteractable.throwStrength === "number") {
3058
- interactable.throwStrength = sourceInteractable.throwStrength;
3059
+ if (typeof sourceInteractable.velocityScale === "number") {
3060
+ interactable.velocityScale = sourceInteractable.velocityScale;
3061
+ } else if (typeof sourceInteractable.throwStrength === "number") {
3062
+ interactable.velocityScale = sourceInteractable.throwStrength;
3059
3063
  }
3060
3064
  const grabPos = asRecord(sourceInteractable.grabOffsetPosition);
3061
3065
  if (grabPos) {
@@ -3131,8 +3135,16 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
3131
3135
  if (typeof sourceInteractor.handEmissiveIntensity === "number") {
3132
3136
  interactor.handEmissiveIntensity = sourceInteractor.handEmissiveIntensity;
3133
3137
  }
3134
- if (typeof sourceInteractor.showRay === "boolean") {
3135
- interactor.showRay = sourceInteractor.showRay;
3138
+ if (typeof sourceInteractor.rayVisibility === "string") {
3139
+ interactor.rayVisibility = sourceInteractor.rayVisibility;
3140
+ } else if (typeof sourceInteractor.showRay === "boolean") {
3141
+ interactor.rayVisibility = sourceInteractor.showRay ? "interacting" : "never";
3142
+ }
3143
+ if (typeof sourceInteractor.rayColor === "string") {
3144
+ interactor.rayColor = sourceInteractor.rayColor;
3145
+ }
3146
+ if (typeof sourceInteractor.rayThickness === "number") {
3147
+ interactor.rayThickness = sourceInteractor.rayThickness;
3136
3148
  }
3137
3149
  }
3138
3150
  }
@@ -7109,8 +7121,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7109
7121
  if (held.restoreBodyType !== null && physics) {
7110
7122
  physics.values.rigidbody.type = held.restoreBodyType;
7111
7123
  const dynamic = held.restoreBodyType === "dynamic";
7112
- if (dynamic && interactable?.throwOnRelease) {
7113
- const velocity = held.tracker.computeVelocity(this.elapsed).multiplyScalar(interactable.throwStrength);
7124
+ if (dynamic && interactable?.applyVelocityOnRelease) {
7125
+ const velocity = held.tracker.computeVelocity(this.elapsed).multiplyScalar(interactable.velocityScale);
7114
7126
  const spin = held.angularVelocity;
7115
7127
  setPhysicsLiveRef(physics, "pendingVelocity", {
7116
7128
  linear: { x: velocity.x, y: velocity.y, z: velocity.z },
@@ -7191,6 +7203,32 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7191
7203
  isGrabbing() {
7192
7204
  return this.grabs.size > 0;
7193
7205
  }
7206
+ /**
7207
+ * Whether this hand's primary action is committed to interaction.
7208
+ *
7209
+ * True while the hand holds something, and also while it merely HOVERS a
7210
+ * grabbable — because the pinch that is about to grab must not be read as a
7211
+ * teleport by the locomotion system. Without the hover half, the first pinch
7212
+ * of a grab teleports the player, and so does the pinch that takes an object
7213
+ * from the other hand.
7214
+ */
7215
+ isHandBusy(world, handedness) {
7216
+ if (this.isGrabbingWith(handedness)) return true;
7217
+ const hover = this.getHoverFor(handedness);
7218
+ if (!hover) return false;
7219
+ const interactable = world.getComponent(
7220
+ hover.entityId,
7221
+ XRInteractableComponent
7222
+ );
7223
+ return Boolean(interactable?.enabled && interactable.grabbable);
7224
+ }
7225
+ /** True when THIS hand is holding something. */
7226
+ isGrabbingWith(handedness) {
7227
+ for (const input of this.grabs.keys()) {
7228
+ if (input.handedness === handedness) return true;
7229
+ }
7230
+ return false;
7231
+ }
7194
7232
  /**
7195
7233
  * How an entity is currently held.
7196
7234
  *
@@ -7274,8 +7312,20 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
7274
7312
  this.stickAiming = /* @__PURE__ */ new WeakSet();
7275
7313
  /** True once we've tried to self-heal camera parenting for this session. */
7276
7314
  this.parentingChecked = false;
7315
+ /**
7316
+ * Optional; when wired, hands busy grabbing are excluded from teleport.
7317
+ *
7318
+ * On hand tracking the SAME pinch drives both grabbing and teleport aiming,
7319
+ * so without this a user who picks something up is teleported at the same
7320
+ * moment.
7321
+ */
7322
+ this.interactionSystem = null;
7277
7323
  this.inputSystem = inputSystem;
7278
7324
  }
7325
+ /** Wire the interaction system so grabs suppress teleport on that hand. */
7326
+ setInteractionSystem(interactionSystem) {
7327
+ this.interactionSystem = interactionSystem;
7328
+ }
7279
7329
  update(world, deltaTime) {
7280
7330
  this.aimCurve = [];
7281
7331
  this.landingPoint = null;
@@ -7393,6 +7443,9 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
7393
7443
  applyTeleport(world, transform, inputs) {
7394
7444
  const targets = this.collectTeleportTargets(world);
7395
7445
  for (const input of inputs) {
7446
+ if (input.handedness !== "none" && this.interactionSystem?.isHandBusy(world, input.handedness)) {
7447
+ continue;
7448
+ }
7396
7449
  const source = input;
7397
7450
  const stick = this.readStickAim(input, source);
7398
7451
  const aiming = input.select || stick.aiming;
@@ -7750,6 +7803,7 @@ __name(applyHandMaterial, "applyHandMaterial");
7750
7803
 
7751
7804
  // core/systems/XRControllerVisualSystem.ts
7752
7805
  var RAY_LENGTH = 5;
7806
+ var RAY_RADIUS = 3e-3;
7753
7807
  var SLOTS = [
7754
7808
  { index: 0, side: "left" },
7755
7809
  { index: 1, side: "right" }
@@ -7759,6 +7813,8 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
7759
7813
  this.controllerModelFactory = new XRControllerModelFactory();
7760
7814
  this.handModelFactory = new XRHandModelFactory();
7761
7815
  this.group = new THREE19.Group();
7816
+ /** Reused so a per-frame colour compare allocates nothing. */
7817
+ this.rayColorScratch = new THREE19.Color();
7762
7818
  this.slots = /* @__PURE__ */ new Map();
7763
7819
  this.attachedTo = null;
7764
7820
  /** Last applied preset signature, so materials rebuild only on real change. */
@@ -7787,8 +7843,9 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
7787
7843
  const hasHand = liveKind === null || liveKind === "hand";
7788
7844
  visuals.controllerModel.visible = showControllers && hasController;
7789
7845
  visuals.handModel.visible = showHands && hasHand;
7790
- visuals.ray.visible = this.shouldShowRay(interactor);
7846
+ visuals.ray.visible = this.shouldShowRay(interactor, side);
7791
7847
  this.fitRayToHover(visuals.ray, side);
7848
+ this.applyRayStyle(visuals.ray, interactor);
7792
7849
  if (showHands && this.handMaterial) {
7793
7850
  applyHandMaterial(visuals.handModel, this.handMaterial);
7794
7851
  }
@@ -7864,10 +7921,26 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
7864
7921
  * off entirely; otherwise it appears on hover/grab so it isn't permanent
7865
7922
  * clutter implying the whole scene is clickable.
7866
7923
  */
7867
- shouldShowRay(interactor) {
7868
- if (interactor && !interactor.showRay) return false;
7924
+ shouldShowRay(interactor, side) {
7925
+ const visibility = interactor?.rayVisibility ?? "interacting";
7926
+ if (visibility === "never") return false;
7927
+ if (visibility === "always") return true;
7869
7928
  if (!this.interactionSystem) return true;
7870
- return this.interactionSystem.getHoveredEntityId() !== null || this.interactionSystem.isGrabbing();
7929
+ if (this.interactionSystem.isGrabbingWith(side)) return false;
7930
+ return this.interactionSystem.getHoverFor(side) !== null;
7931
+ }
7932
+ /** Push the author's ray colour and thickness onto the mesh. */
7933
+ applyRayStyle(ray, interactor) {
7934
+ if (!interactor) return;
7935
+ const material = ray.material;
7936
+ if (material?.color && !material.color.equals(this.rayColorScratch.set(interactor.rayColor))) {
7937
+ material.color.copy(this.rayColorScratch);
7938
+ }
7939
+ const radius = Math.max(5e-4, interactor.rayThickness / 2) / RAY_RADIUS;
7940
+ if (ray.scale.x !== radius) {
7941
+ ray.scale.x = radius;
7942
+ ray.scale.y = radius;
7943
+ }
7871
7944
  }
7872
7945
  /**
7873
7946
  * Stop the ray at whatever it is pointing at.
@@ -7879,7 +7952,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
7879
7952
  fitRayToHover(ray, side) {
7880
7953
  const hover = this.interactionSystem?.getHoverFor(side) ?? null;
7881
7954
  const length = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
7882
- ray.scale.z = length / RAY_LENGTH;
7955
+ ray.scale.z = length;
7883
7956
  }
7884
7957
  /** Lazily build per-slot visuals; the device reports profiles late. */
7885
7958
  ensureSlots(world) {
@@ -7919,22 +7992,32 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
7919
7992
  target.add(this.group);
7920
7993
  this.attachedTo = target;
7921
7994
  }
7995
+ /**
7996
+ * The pointer ray.
7997
+ *
7998
+ * A cylinder rather than a `THREE.Line`, because WebGL ignores `linewidth` —
7999
+ * every line renders one pixel wide regardless, which is nearly invisible on a
8000
+ * headset. A thin mesh is the only way to get a ray with real thickness
8001
+ * without pulling in a line-rendering extension.
8002
+ */
7922
8003
  createRay() {
7923
- const geometry = new THREE19.BufferGeometry().setFromPoints([
7924
- new THREE19.Vector3(0, 0, 0),
7925
- new THREE19.Vector3(0, 0, -RAY_LENGTH)
7926
- ]);
7927
- const material = new THREE19.LineBasicMaterial({
8004
+ const geometry = new THREE19.CylinderGeometry(RAY_RADIUS, RAY_RADIUS, 1, 8, 1);
8005
+ geometry.rotateX(-Math.PI / 2);
8006
+ geometry.translate(0, 0, -0.5);
8007
+ const material = new THREE19.MeshBasicMaterial({
7928
8008
  color: "#4da3ff",
7929
8009
  transparent: true,
7930
- opacity: 0.6,
7931
- depthTest: false
8010
+ opacity: 0.75,
8011
+ depthTest: false,
8012
+ depthWrite: false
7932
8013
  });
7933
- const line = new THREE19.Line(geometry, material);
7934
- line.name = "XRPointerRay";
7935
- line.renderOrder = 999;
7936
- line.visible = false;
7937
- return line;
8014
+ const mesh = new THREE19.Mesh(geometry, material);
8015
+ mesh.name = "XRPointerRay";
8016
+ mesh.renderOrder = 999;
8017
+ mesh.visible = false;
8018
+ mesh.raycast = () => {
8019
+ };
8020
+ return mesh;
7938
8021
  }
7939
8022
  dispose() {
7940
8023
  this.group.removeFromParent();
@@ -8733,5 +8816,5 @@ __name(_XRHoverVisualSystem, "XRHoverVisualSystem");
8733
8816
  var XRHoverVisualSystem = _XRHoverVisualSystem;
8734
8817
 
8735
8818
  export { ADDED_NODE_MARKER_COMPONENT, ARC_GRAVITY, ARC_MAX_TIME, ARC_SPEED, ARC_STEPS, AnimationSystem, AudioSystem, Behaviour, CURLED_COS, CameraSyncSystem, DEFAULT_ENTER_CONFIDENCE, DEFAULT_EXIT_CONFIDENCE, DEFAULT_LOST_GRACE_SECONDS, DEFAULT_MIN_HOLD_SECONDS, EnvironmentSyncSystem, FALLBACK_HAND_SCALE, HYDRATABLE_ENTITY_KINDS, HandPoseTracker, InputSystem, InstancedMeshSystem, KeyboardKeys, LightSyncSystem, MATCH_FALLOFF, MAX_PLAUSIBLE_SPEED, MAX_SAMPLE_AGE_SECONDS, MaterialSyncSystem, MeshGeometrySystem, NODE_KEY_SEPARATOR, NO_XR_CAPABILITIES, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectVisibilitySystem, PINCH_FINGERS, PREFAB_FILE_VERSION, PREFAB_FILE_VERSION_NESTED, PhysicsSyncSystem, PostprocessingSyncSystem, RenderSystem, SCRIPT_OBJECT_ROOT_FLAG, STRAIGHT_COS, ScriptBehaviourSystem, ScriptObject, ScriptObjectSystem, ThrowVelocityTracker, TransformSyncSystem, VELOCITY_SAMPLES, VRTeleportAreaSystem, VideoSystem, XRControllerVisualSystem, XRHandPoseSystem, XRHoverVisualSystem, XRInputSystem, XRInteractionSystem, XRLocomotionSystem, XRSessionSystem, XRTeleportVisualSystem, XR_FINGERS, XR_HOVER_SHELL_FLAG, angularVelocityFrom, applyHandMaterial, arcGroundCrossing, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearPrefabCache, collectDiffPaths, collectEntitySubtree, collectSnapshotSubtreeIds, computeFingerCurl, computeHandShape, computeInstanceOverrides, computePalmPose, computePalmWidth, computePinch, computeSpread, createHandMaterial, decanonicalizeComponentValue, despawnPrefab, encodeOverride, estimateHandScale, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadPrefab, matchHandPose, mergeComponentValue, parseOverride, parsePrefabAsset, prefabFileVersionFor, readXrCapabilities, restoreComponentState, restorePostCreateComponents, sampleTeleportArc, setPrefabAssetUrlResolver, spawnPrefab };
8736
- //# sourceMappingURL=chunk-KLU2M43M.js.map
8737
- //# sourceMappingURL=chunk-KLU2M43M.js.map
8819
+ //# sourceMappingURL=chunk-N2634D4W.js.map
8820
+ //# sourceMappingURL=chunk-N2634D4W.js.map