@relu-interactives/spatial-ecs 0.3.15 → 0.3.18

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-LR4EhboN.d.cts → XRHandPose-B8xxFb9A.d.cts} +12 -0
  2. package/dist/{XRHandPose-SZ7ou63b.d.ts → XRHandPose-ubMe5Igc.d.ts} +12 -0
  3. package/dist/{chunk-GULLCPO6.js → chunk-7SSOZWXM.js} +123 -36
  4. package/dist/chunk-7SSOZWXM.js.map +1 -0
  5. package/dist/{chunk-3LDQ4GVZ.js → chunk-J3EUNW57.js} +5 -2
  6. package/dist/chunk-J3EUNW57.js.map +1 -0
  7. package/dist/{chunk-UHJ4MDIM.js → chunk-KRWBNC6Y.js} +4 -4
  8. package/dist/{chunk-UHJ4MDIM.js.map → chunk-KRWBNC6Y.js.map} +1 -1
  9. package/dist/{chunk-PNQ7HDQC.js → chunk-PFE2SDXR.js} +3 -3
  10. package/dist/{chunk-PNQ7HDQC.js.map → chunk-PFE2SDXR.js.map} +1 -1
  11. package/dist/components/index.cjs +3 -0
  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 +1 -1
  17. package/dist/factories/index.cjs +3 -0
  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-BKGV3VvQ.d.cts → index-7CFf45fo.d.cts} +24 -1
  23. package/dist/{index-BL5EWkwY.d.cts → index-CA9fqh4Z.d.cts} +1 -1
  24. package/dist/{index-Bi7L3QHF.d.ts → index-D7aSZ-e9.d.ts} +24 -1
  25. package/dist/{index-ByiUoIoh.d.ts → index-DhWfxUgL.d.ts} +1 -1
  26. package/dist/index.cjs +122 -32
  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 +122 -32
  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-3LDQ4GVZ.js.map +0 -1
  38. package/dist/chunk-GULLCPO6.js.map +0 -1
@@ -1450,6 +1450,12 @@ type XRRigOptions = {
1450
1450
  snapTurnDegrees?: number;
1451
1451
  /** Height in metres used when the device reports no floor-relative space. */
1452
1452
  fallbackEyeHeight?: number;
1453
+ /** Teleport arc colour when it lands somewhere valid. */
1454
+ teleportArcColor?: string;
1455
+ /** Teleport arc colour when it lands nowhere teleportable. */
1456
+ teleportArcInvalidColor?: string;
1457
+ /** Teleport arc thickness (diameter), metres. */
1458
+ teleportArcThickness?: number;
1453
1459
  };
1454
1460
  /**
1455
1461
  * Marks an entity as the XR rig — the player's movable origin.
@@ -1478,6 +1484,12 @@ declare class XRRigComponent extends Component {
1478
1484
  snapTurnDegrees: number;
1479
1485
  /** Eye height fallback for devices without a floor-relative reference space. */
1480
1486
  fallbackEyeHeight: number;
1487
+ /** Teleport arc colour when it lands somewhere valid. */
1488
+ teleportArcColor: string;
1489
+ /** Teleport arc colour when it lands nowhere teleportable. */
1490
+ teleportArcInvalidColor: string;
1491
+ /** Teleport arc thickness (diameter), metres. */
1492
+ teleportArcThickness: number;
1481
1493
  constructor(options?: XRRigOptions);
1482
1494
  }
1483
1495
 
@@ -1450,6 +1450,12 @@ type XRRigOptions = {
1450
1450
  snapTurnDegrees?: number;
1451
1451
  /** Height in metres used when the device reports no floor-relative space. */
1452
1452
  fallbackEyeHeight?: number;
1453
+ /** Teleport arc colour when it lands somewhere valid. */
1454
+ teleportArcColor?: string;
1455
+ /** Teleport arc colour when it lands nowhere teleportable. */
1456
+ teleportArcInvalidColor?: string;
1457
+ /** Teleport arc thickness (diameter), metres. */
1458
+ teleportArcThickness?: number;
1453
1459
  };
1454
1460
  /**
1455
1461
  * Marks an entity as the XR rig — the player's movable origin.
@@ -1478,6 +1484,12 @@ declare class XRRigComponent extends Component {
1478
1484
  snapTurnDegrees: number;
1479
1485
  /** Eye height fallback for devices without a floor-relative reference space. */
1480
1486
  fallbackEyeHeight: number;
1487
+ /** Teleport arc colour when it lands somewhere valid. */
1488
+ teleportArcColor: string;
1489
+ /** Teleport arc colour when it lands nowhere teleportable. */
1490
+ teleportArcInvalidColor: string;
1491
+ /** Teleport arc thickness (diameter), metres. */
1492
+ teleportArcThickness: number;
1481
1493
  constructor(options?: XRRigOptions);
1482
1494
  }
1483
1495
 
@@ -1,5 +1,5 @@
1
- import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-PNQ7HDQC.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';
1
+ import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-PFE2SDXR.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-J3EUNW57.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';
@@ -2974,6 +2974,15 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
2974
2974
  if (typeof sourceXrRig.snapTurnDegrees === "number") {
2975
2975
  rig.snapTurnDegrees = sourceXrRig.snapTurnDegrees;
2976
2976
  }
2977
+ if (typeof sourceXrRig.teleportArcColor === "string") {
2978
+ rig.teleportArcColor = sourceXrRig.teleportArcColor;
2979
+ }
2980
+ if (typeof sourceXrRig.teleportArcInvalidColor === "string") {
2981
+ rig.teleportArcInvalidColor = sourceXrRig.teleportArcInvalidColor;
2982
+ }
2983
+ if (typeof sourceXrRig.teleportArcThickness === "number") {
2984
+ rig.teleportArcThickness = sourceXrRig.teleportArcThickness;
2985
+ }
2977
2986
  if (typeof sourceXrRig.fallbackEyeHeight === "number") {
2978
2987
  rig.fallbackEyeHeight = sourceXrRig.fallbackEyeHeight;
2979
2988
  }
@@ -7352,6 +7361,8 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
7352
7361
  this.aimValid = false;
7353
7362
  /** Sources currently aiming via thumbstick (hysteresis, so it doesn't chatter). */
7354
7363
  this.stickAiming = /* @__PURE__ */ new WeakSet();
7364
+ /** One-time warning when a committed teleport found nowhere to land. */
7365
+ this.warnedNoLanding = false;
7355
7366
  /** Gates teleport commits on a prior aim. See {@link TeleportAimGate}. */
7356
7367
  this.teleportAim = new TeleportAimGate();
7357
7368
  /** True once we've tried to self-heal camera parenting for this session. */
@@ -7498,7 +7509,8 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
7498
7509
  }
7499
7510
  const stick = this.readStickAim(input, source);
7500
7511
  const aiming = input.select || input.selectStart || stick.aiming;
7501
- const releasing = input.selectEnd || stick.released;
7512
+ const vanished = !input.connected && this.teleportAim.isAiming(source);
7513
+ const releasing = input.selectEnd || stick.released || vanished;
7502
7514
  const commit = this.teleportAim.update(source, aiming, releasing);
7503
7515
  if (!aiming && !commit) continue;
7504
7516
  const points = sampleTeleportArc(
@@ -7518,6 +7530,12 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
7518
7530
  this.landingPoint = null;
7519
7531
  this.aimValid = false;
7520
7532
  } else if (commit) {
7533
+ if (!this.warnedNoLanding) {
7534
+ this.warnedNoLanding = true;
7535
+ console.warn(
7536
+ "[XRLocomotionSystem] Teleport released but the arc did not land on a Teleport Area. Add a teleportArea entity where the player should be able to go."
7537
+ );
7538
+ }
7521
7539
  this.aimCurve = [];
7522
7540
  this.aimValid = false;
7523
7541
  }
@@ -8093,24 +8111,33 @@ __name(_XRControllerVisualSystem, "XRControllerVisualSystem");
8093
8111
  var XRControllerVisualSystem = _XRControllerVisualSystem;
8094
8112
  var MARKER_RADIUS = 0.28;
8095
8113
  var ACCENT = new THREE19.Color("#4da3ff");
8096
- var BLOCKED = new THREE19.Color("#ff5a5a");
8114
+ var SEGMENT_AXIS = new THREE19.Vector3(0, 1, 0);
8115
+ var HEAD_CLEARANCE = 0.25;
8097
8116
  var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
8098
8117
  constructor(locomotionSystem) {
8099
8118
  this.group = new THREE19.Group();
8100
- this.arcGeometry = new THREE19.BufferGeometry();
8119
+ /** Holds the segment chain that draws the arc. */
8120
+ this.arc = new THREE19.Group();
8121
+ /**
8122
+ * Unit cylinder along +Y, reused by every segment.
8123
+ *
8124
+ * The arc was a `THREE.Line`, which WebGL always renders one pixel wide —
8125
+ * effectively invisible on a headset, and not thickenable at all. A chain of
8126
+ * scaled cylinders gives real thickness while rebuilding no geometry: each
8127
+ * frame only moves, rotates and scales meshes that already exist.
8128
+ */
8129
+ this.segmentGeometry = new THREE19.CylinderGeometry(1, 1, 1, 6, 1);
8130
+ this.segments = [];
8101
8131
  this.marker = new THREE19.Group();
8102
8132
  this.attached = false;
8133
+ this.scratchDirection = new THREE19.Vector3();
8134
+ this.scratchHead = new THREE19.Vector3();
8135
+ this.validColor = new THREE19.Color();
8136
+ this.invalidColor = new THREE19.Color();
8103
8137
  this.locomotionSystem = locomotionSystem;
8104
8138
  this.group.name = "XRTeleportVisuals";
8105
8139
  this.group.userData.ecsPreviewHidden = true;
8106
- const arcMaterial = new THREE19.LineBasicMaterial({
8107
- vertexColors: true,
8108
- transparent: true,
8109
- depthTest: false
8110
- });
8111
- this.arc = new THREE19.Line(this.arcGeometry, arcMaterial);
8112
- this.arc.frustumCulled = false;
8113
- this.arc.renderOrder = 998;
8140
+ this.arc.name = "XRTeleportArc";
8114
8141
  this.group.add(this.arc);
8115
8142
  this.marker.add(this.createRing(), this.createDot());
8116
8143
  this.marker.renderOrder = 999;
@@ -8122,15 +8149,27 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
8122
8149
  world.getScene().add(this.group);
8123
8150
  this.attached = true;
8124
8151
  }
8125
- const curve = this.locomotionSystem.getAimCurve();
8152
+ const raw = this.locomotionSystem.getAimCurve();
8126
8153
  const landing = this.locomotionSystem.getLandingPoint();
8127
8154
  const valid = this.locomotionSystem.isAimValid();
8155
+ if (raw.length < 2) {
8156
+ this.setVisible(false);
8157
+ return;
8158
+ }
8159
+ const curve = this.trimNearHead(world, raw);
8128
8160
  if (curve.length < 2) {
8129
8161
  this.setVisible(false);
8130
8162
  return;
8131
8163
  }
8132
- this.updateArcGeometry(curve, valid ? ACCENT : BLOCKED);
8164
+ const rig = this.resolveRig(world);
8165
+ const thickness = Math.min(
8166
+ 0.1,
8167
+ Math.max(1e-3, rig?.teleportArcThickness ?? 0.012)
8168
+ );
8169
+ const tint = valid ? this.validColor.set(rig?.teleportArcColor ?? "#4da3ff") : this.invalidColor.set(rig?.teleportArcInvalidColor ?? "#ff5a5a");
8170
+ this.updateArcGeometry(curve, tint, thickness);
8133
8171
  if (valid && landing) {
8172
+ this.tintMarker(tint);
8134
8173
  this.marker.position.copy(landing);
8135
8174
  this.marker.position.y += 0.01;
8136
8175
  this.marker.visible = true;
@@ -8139,27 +8178,66 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
8139
8178
  }
8140
8179
  this.setVisible(true);
8141
8180
  }
8142
- updateArcGeometry(curve, tint) {
8143
- const count = curve.length;
8144
- const positions = new Float32Array(count * 3);
8145
- const colors = new Float32Array(count * 3);
8146
- for (let i = 0; i < count; i += 1) {
8147
- const point = curve[i];
8148
- positions[i * 3] = point.x;
8149
- positions[i * 3 + 1] = point.y;
8150
- positions[i * 3 + 2] = point.z;
8151
- const t = count > 1 ? i / (count - 1) : 0;
8181
+ updateArcGeometry(curve, tint, thickness) {
8182
+ const needed = curve.length - 1;
8183
+ this.ensureSegments(needed);
8184
+ const radius = thickness / 2;
8185
+ for (let i = 0; i < this.segments.length; i += 1) {
8186
+ const segment = this.segments[i];
8187
+ if (i >= needed) {
8188
+ segment.visible = false;
8189
+ continue;
8190
+ }
8191
+ const from = curve[i];
8192
+ const to = curve[i + 1];
8193
+ this.scratchDirection.subVectors(to, from);
8194
+ const length = this.scratchDirection.length();
8195
+ if (length < 1e-6) {
8196
+ segment.visible = false;
8197
+ continue;
8198
+ }
8199
+ segment.visible = true;
8200
+ segment.position.copy(from).addScaledVector(this.scratchDirection, 0.5);
8201
+ segment.quaternion.setFromUnitVectors(
8202
+ SEGMENT_AXIS,
8203
+ this.scratchDirection.divideScalar(length)
8204
+ );
8205
+ segment.scale.set(radius, length, radius);
8206
+ const t = needed > 1 ? i / (needed - 1) : 0;
8152
8207
  const fade = 1 - t * 0.75;
8153
- colors[i * 3] = tint.r * fade;
8154
- colors[i * 3 + 1] = tint.g * fade;
8155
- colors[i * 3 + 2] = tint.b * fade;
8208
+ segment.material.color.copy(tint).multiplyScalar(fade);
8156
8209
  }
8157
- this.arcGeometry.setAttribute(
8158
- "position",
8159
- new THREE19.BufferAttribute(positions, 3)
8160
- );
8161
- this.arcGeometry.setAttribute("color", new THREE19.BufferAttribute(colors, 3));
8162
- this.arcGeometry.computeBoundingSphere();
8210
+ }
8211
+ /** Grow the segment pool to cover the current arc. */
8212
+ ensureSegments(count) {
8213
+ while (this.segments.length < count) {
8214
+ const material = new THREE19.MeshBasicMaterial({
8215
+ transparent: true,
8216
+ depthTest: false,
8217
+ depthWrite: false
8218
+ });
8219
+ const mesh = new THREE19.Mesh(this.segmentGeometry, material);
8220
+ mesh.frustumCulled = false;
8221
+ mesh.renderOrder = 998;
8222
+ mesh.raycast = () => {
8223
+ };
8224
+ this.segments.push(mesh);
8225
+ this.arc.add(mesh);
8226
+ }
8227
+ }
8228
+ /** Drop leading points that sit too close to the viewer's head. */
8229
+ trimNearHead(world, curve) {
8230
+ world.getCamera().getWorldPosition(this.scratchHead);
8231
+ let start = 0;
8232
+ while (start < curve.length - 2 && curve[start].distanceTo(this.scratchHead) < HEAD_CLEARANCE) {
8233
+ start += 1;
8234
+ }
8235
+ return start === 0 ? curve : curve.slice(start);
8236
+ }
8237
+ resolveRig(world) {
8238
+ const entityId = world.query(XRRigComponent)[0];
8239
+ if (typeof entityId !== "number") return null;
8240
+ return world.getComponent(entityId, XRRigComponent) ?? null;
8163
8241
  }
8164
8242
  createRing() {
8165
8243
  const geometry = new THREE19.RingGeometry(
@@ -8193,6 +8271,15 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
8193
8271
  })
8194
8272
  );
8195
8273
  }
8274
+ /** Recolour the landing marker to match the arc. */
8275
+ tintMarker(color) {
8276
+ this.marker.traverse((child) => {
8277
+ const material = child.material;
8278
+ if (material && !Array.isArray(material)) {
8279
+ material.color?.copy(color);
8280
+ }
8281
+ });
8282
+ }
8196
8283
  setVisible(visible) {
8197
8284
  this.group.visible = visible;
8198
8285
  }
@@ -8866,5 +8953,5 @@ __name(_XRHoverVisualSystem, "XRHoverVisualSystem");
8866
8953
  var XRHoverVisualSystem = _XRHoverVisualSystem;
8867
8954
 
8868
8955
  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, TeleportAimGate, 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 };
8869
- //# sourceMappingURL=chunk-GULLCPO6.js.map
8870
- //# sourceMappingURL=chunk-GULLCPO6.js.map
8956
+ //# sourceMappingURL=chunk-7SSOZWXM.js.map
8957
+ //# sourceMappingURL=chunk-7SSOZWXM.js.map