@relu-interactives/spatial-ecs 0.3.9 → 0.3.11

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-CxPz8VBp.d.ts → XRHandPose-B6I4y1lj.d.ts} +117 -14
  2. package/dist/{XRHandPose-VuSbe4iz.d.cts → XRHandPose-C-KJeYcJ.d.cts} +117 -14
  3. package/dist/{chunk-KIKIWCEB.js → chunk-FSHRPY65.js} +4 -4
  4. package/dist/{chunk-KIKIWCEB.js.map → chunk-FSHRPY65.js.map} +1 -1
  5. package/dist/{chunk-W5TSYV6Q.js → chunk-KLU2M43M.js} +836 -212
  6. package/dist/chunk-KLU2M43M.js.map +1 -0
  7. package/dist/{chunk-KVGGAL5V.js → chunk-TXEGYR76.js} +26 -5
  8. package/dist/chunk-TXEGYR76.js.map +1 -0
  9. package/dist/{chunk-L5BP727H.js → chunk-Y3AC3VZ2.js} +3 -3
  10. package/dist/{chunk-L5BP727H.js.map → chunk-Y3AC3VZ2.js.map} +1 -1
  11. package/dist/components/index.cjs +23 -2
  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 +55 -3
  17. package/dist/factories/index.cjs +22 -2
  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-C7XQyrgE.d.ts → index-Bi_wIo1o.d.ts} +107 -1
  23. package/dist/{index-BxdXgqiI.d.ts → index-CEZp7qLL.d.ts} +9 -3
  24. package/dist/{index-BIQoaKAA.d.cts → index-DPRnnSI-.d.cts} +9 -3
  25. package/dist/{index-DyN50JZo.d.cts → index-jlgDou9W.d.cts} +107 -1
  26. package/dist/index.cjs +978 -331
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +14 -5
  29. package/dist/index.d.ts +14 -5
  30. package/dist/index.js +8 -8
  31. package/dist/systems/index.cjs +885 -239
  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-KVGGAL5V.js.map +0 -1
  38. package/dist/chunk-W5TSYV6Q.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var THREE39 = require('three');
3
+ var THREE51 = require('three');
4
4
  var SkeletonUtils_js = require('three/examples/jsm/utils/SkeletonUtils.js');
5
5
  var RectAreaLightHelper_js = require('three/examples/jsm/helpers/RectAreaLightHelper.js');
6
6
  var DRACOLoader_js = require('three/examples/jsm/loaders/DRACOLoader.js');
@@ -41,7 +41,7 @@ function _interopNamespace(e) {
41
41
  return Object.freeze(n);
42
42
  }
43
43
 
44
- var THREE39__namespace = /*#__PURE__*/_interopNamespace(THREE39);
44
+ var THREE51__namespace = /*#__PURE__*/_interopNamespace(THREE51);
45
45
 
46
46
  var __defProp = Object.defineProperty;
47
47
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -605,14 +605,14 @@ var _AnimationComponent = class _AnimationComponent extends Component {
605
605
  return;
606
606
  }
607
607
  this.importedClips = clips.map((entry) => {
608
- if (entry instanceof THREE39__namespace.AnimationClip) {
608
+ if (entry instanceof THREE51__namespace.AnimationClip) {
609
609
  return { clip: entry, sourceUrl: null };
610
610
  }
611
611
  if (!entry || typeof entry !== "object") {
612
612
  return null;
613
613
  }
614
614
  const candidate = entry;
615
- if (!(candidate.clip instanceof THREE39__namespace.AnimationClip)) {
615
+ if (!(candidate.clip instanceof THREE51__namespace.AnimationClip)) {
616
616
  return null;
617
617
  }
618
618
  return {
@@ -820,11 +820,11 @@ function normalizeDistanceModel(distanceModel) {
820
820
  }
821
821
  __name(normalizeDistanceModel, "normalizeDistanceModel");
822
822
  function createConfiguredAudio(listener, options) {
823
- const sound = options.positional ? new THREE39__namespace.PositionalAudio(listener) : new THREE39__namespace.Audio(listener);
823
+ const sound = options.positional ? new THREE51__namespace.PositionalAudio(listener) : new THREE51__namespace.Audio(listener);
824
824
  sound.setLoop(options.loop);
825
825
  sound.setVolume(options.volume);
826
826
  sound.setPlaybackRate(options.pitch);
827
- if (sound instanceof THREE39__namespace.PositionalAudio) {
827
+ if (sound instanceof THREE51__namespace.PositionalAudio) {
828
828
  sound.setRefDistance(options.refDistance);
829
829
  sound.setDistanceModel(normalizeDistanceModel(options.distanceModel));
830
830
  sound.setRolloffFactor(options.rolloffFactor);
@@ -834,16 +834,16 @@ function createConfiguredAudio(listener, options) {
834
834
  __name(createConfiguredAudio, "createConfiguredAudio");
835
835
  function createAudioEditorHelper() {
836
836
  if (typeof document === "undefined") {
837
- const geometry = new THREE39__namespace.EdgesGeometry(
838
- new THREE39__namespace.OctahedronGeometry(0.22, 0)
837
+ const geometry = new THREE51__namespace.EdgesGeometry(
838
+ new THREE51__namespace.OctahedronGeometry(0.22, 0)
839
839
  );
840
- const material2 = new THREE39__namespace.LineBasicMaterial({
840
+ const material2 = new THREE51__namespace.LineBasicMaterial({
841
841
  color: "#8bc8ff",
842
842
  transparent: true,
843
843
  opacity: 0.95,
844
844
  depthTest: false
845
845
  });
846
- const fallback = new THREE39__namespace.LineSegments(geometry, material2);
846
+ const fallback = new THREE51__namespace.LineSegments(geometry, material2);
847
847
  fallback.renderOrder = 10;
848
848
  fallback.userData.ecsHelper = true;
849
849
  fallback.visible = false;
@@ -855,7 +855,7 @@ function createAudioEditorHelper() {
855
855
  canvas.height = size;
856
856
  const ctx = canvas.getContext("2d");
857
857
  if (!ctx) {
858
- return new THREE39__namespace.Object3D();
858
+ return new THREE51__namespace.Object3D();
859
859
  }
860
860
  ctx.clearRect(0, 0, size, size);
861
861
  ctx.fillStyle = "rgba(27, 35, 52, 0.9)";
@@ -886,14 +886,14 @@ function createAudioEditorHelper() {
886
886
  ctx.beginPath();
887
887
  ctx.arc(56, 48, 16, -0.8, 0.8);
888
888
  ctx.stroke();
889
- const texture = new THREE39__namespace.CanvasTexture(canvas);
889
+ const texture = new THREE51__namespace.CanvasTexture(canvas);
890
890
  texture.needsUpdate = true;
891
- const material = new THREE39__namespace.SpriteMaterial({
891
+ const material = new THREE51__namespace.SpriteMaterial({
892
892
  map: texture,
893
893
  transparent: true,
894
894
  depthTest: false
895
895
  });
896
- const helper = new THREE39__namespace.Sprite(material);
896
+ const helper = new THREE51__namespace.Sprite(material);
897
897
  helper.scale.set(0.68, 0.68, 0.68);
898
898
  helper.renderOrder = 10;
899
899
  helper.userData.ecsHelper = true;
@@ -927,7 +927,7 @@ var DEFAULT_ORTHO_TOP = 5;
927
927
  var DEFAULT_ORTHO_BOTTOM = -5;
928
928
  var DEFAULT_ZOOM = 1;
929
929
  function inferProjection(camera) {
930
- if (camera instanceof THREE39__namespace.OrthographicCamera) {
930
+ if (camera instanceof THREE51__namespace.OrthographicCamera) {
931
931
  return "orthographic";
932
932
  }
933
933
  return "perspective";
@@ -948,7 +948,7 @@ function readCameraFar(camera) {
948
948
  }
949
949
  __name(readCameraFar, "readCameraFar");
950
950
  function inferFov(camera) {
951
- if (camera instanceof THREE39__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
951
+ if (camera instanceof THREE51__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
952
952
  return camera.fov;
953
953
  }
954
954
  return DEFAULT_FOV;
@@ -971,28 +971,28 @@ function inferFar(camera) {
971
971
  }
972
972
  __name(inferFar, "inferFar");
973
973
  function inferOrthoLeft(camera) {
974
- if (camera instanceof THREE39__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
974
+ if (camera instanceof THREE51__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
975
975
  return camera.left;
976
976
  }
977
977
  return DEFAULT_ORTHO_LEFT;
978
978
  }
979
979
  __name(inferOrthoLeft, "inferOrthoLeft");
980
980
  function inferOrthoRight(camera) {
981
- if (camera instanceof THREE39__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
981
+ if (camera instanceof THREE51__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
982
982
  return camera.right;
983
983
  }
984
984
  return DEFAULT_ORTHO_RIGHT;
985
985
  }
986
986
  __name(inferOrthoRight, "inferOrthoRight");
987
987
  function inferOrthoTop(camera) {
988
- if (camera instanceof THREE39__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
988
+ if (camera instanceof THREE51__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
989
989
  return camera.top;
990
990
  }
991
991
  return DEFAULT_ORTHO_TOP;
992
992
  }
993
993
  __name(inferOrthoTop, "inferOrthoTop");
994
994
  function inferOrthoBottom(camera) {
995
- if (camera instanceof THREE39__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
995
+ if (camera instanceof THREE51__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
996
996
  return camera.bottom;
997
997
  }
998
998
  return DEFAULT_ORTHO_BOTTOM;
@@ -1008,7 +1008,7 @@ __name(inferZoom, "inferZoom");
1008
1008
  var _CameraComponent = class _CameraComponent extends Component {
1009
1009
  constructor(payload = {}) {
1010
1010
  super();
1011
- this.camera = payload.camera ?? new THREE39__namespace.PerspectiveCamera();
1011
+ this.camera = payload.camera ?? new THREE51__namespace.PerspectiveCamera();
1012
1012
  this.type = payload.type ?? "3d";
1013
1013
  this.isDefault = typeof payload.isDefault === "boolean" ? payload.isDefault : false;
1014
1014
  this.projection = payload.projection ?? inferProjection(payload.camera);
@@ -1039,7 +1039,7 @@ var Object3DRef = _Object3DRef;
1039
1039
  // core/factories/components/CameraFactory.ts
1040
1040
  function createCameraComponent(world, entityId, payload = {}) {
1041
1041
  const object = world.getComponent(entityId, Object3DRef)?.object;
1042
- const objectCamera = object instanceof THREE39__namespace.Camera ? object : void 0;
1042
+ const objectCamera = object instanceof THREE51__namespace.Camera ? object : void 0;
1043
1043
  const component = new CameraComponent({
1044
1044
  ...payload,
1045
1045
  camera: payload.camera ?? objectCamera
@@ -1609,15 +1609,15 @@ function createValueState(light) {
1609
1609
  castShadow: light.castShadow ?? false,
1610
1610
  frustumCulling: light.frustumCulled
1611
1611
  };
1612
- if (light instanceof THREE39__namespace.PointLight) {
1612
+ if (light instanceof THREE51__namespace.PointLight) {
1613
1613
  base.distance = light.distance;
1614
1614
  base.decay = light.decay;
1615
- } else if (light instanceof THREE39__namespace.SpotLight) {
1615
+ } else if (light instanceof THREE51__namespace.SpotLight) {
1616
1616
  base.distance = light.distance;
1617
1617
  base.decay = light.decay;
1618
1618
  base.angle = light.angle;
1619
1619
  base.penumbra = light.penumbra;
1620
- } else if (light instanceof THREE39__namespace.RectAreaLight) {
1620
+ } else if (light instanceof THREE51__namespace.RectAreaLight) {
1621
1621
  base.width = light.width;
1622
1622
  base.height = light.height;
1623
1623
  }
@@ -1660,13 +1660,13 @@ function toHexColor2(value, fallback) {
1660
1660
  }
1661
1661
  __name(toHexColor2, "toHexColor");
1662
1662
  function getMaterialType(material) {
1663
- if (material instanceof THREE39__namespace.MeshPhysicalMaterial) {
1663
+ if (material instanceof THREE51__namespace.MeshPhysicalMaterial) {
1664
1664
  return "physical";
1665
1665
  }
1666
- if (material instanceof THREE39__namespace.MeshPhongMaterial) {
1666
+ if (material instanceof THREE51__namespace.MeshPhongMaterial) {
1667
1667
  return "phong";
1668
1668
  }
1669
- if (material instanceof THREE39__namespace.MeshBasicMaterial) {
1669
+ if (material instanceof THREE51__namespace.MeshBasicMaterial) {
1670
1670
  return "basic";
1671
1671
  }
1672
1672
  return "standard";
@@ -1701,8 +1701,8 @@ function createMaterialValue(material) {
1701
1701
  opacity: typeof materialRecord.opacity === "number" ? materialRecord.opacity : 1,
1702
1702
  emissive: toHexColor2(materialRecord.emissive, "#000000"),
1703
1703
  emissiveIntensity: typeof materialRecord.emissiveIntensity === "number" ? materialRecord.emissiveIntensity : 0,
1704
- side: typeof materialRecord.side === "number" ? materialRecord.side : THREE39__namespace.FrontSide,
1705
- blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE39__namespace.NormalBlending,
1704
+ side: typeof materialRecord.side === "number" ? materialRecord.side : THREE51__namespace.FrontSide,
1705
+ blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE51__namespace.NormalBlending,
1706
1706
  depthTest: materialRecord.depthTest !== false,
1707
1707
  depthWrite: materialRecord.depthWrite !== false,
1708
1708
  wireframe: Boolean(materialRecord.wireframe),
@@ -1734,7 +1734,7 @@ function createMaterialValue(material) {
1734
1734
  materialRecord.lightMap
1735
1735
  ),
1736
1736
  envMap: textureImageSrc(materialRecord.envMap),
1737
- textureFiltering: map?.magFilter === THREE39__namespace.NearestFilter ? "sharp" : "smooth",
1737
+ textureFiltering: map?.magFilter === THREE51__namespace.NearestFilter ? "sharp" : "smooth",
1738
1738
  useMipmaps: map?.generateMipmaps !== false
1739
1739
  };
1740
1740
  }
@@ -1749,8 +1749,8 @@ function defaultMaterialValue() {
1749
1749
  opacity: 1,
1750
1750
  emissive: "#000000",
1751
1751
  emissiveIntensity: 0,
1752
- side: THREE39__namespace.FrontSide,
1753
- blending: THREE39__namespace.NormalBlending,
1752
+ side: THREE51__namespace.FrontSide,
1753
+ blending: THREE51__namespace.NormalBlending,
1754
1754
  depthTest: true,
1755
1755
  depthWrite: true,
1756
1756
  wireframe: false,
@@ -2327,14 +2327,14 @@ var _TransformComponent = class _TransformComponent extends Component {
2327
2327
  z: scale.z ?? 1
2328
2328
  };
2329
2329
  }
2330
- toPositionVector3(target = new THREE39.Vector3()) {
2330
+ toPositionVector3(target = new THREE51.Vector3()) {
2331
2331
  return target.set(this.position.x, this.position.y, this.position.z);
2332
2332
  }
2333
- toRotationEuler(target = new THREE39.Euler(), order = "XYZ") {
2333
+ toRotationEuler(target = new THREE51.Euler(), order = "XYZ") {
2334
2334
  return target.set(this.rotation.x, this.rotation.y, this.rotation.z, order);
2335
2335
  }
2336
- toRotationQuaternion(target = new THREE39.Quaternion(), order = "XYZ") {
2337
- const euler = new THREE39.Euler(
2336
+ toRotationQuaternion(target = new THREE51.Quaternion(), order = "XYZ") {
2337
+ const euler = new THREE51.Euler(
2338
2338
  this.rotation.x,
2339
2339
  this.rotation.y,
2340
2340
  this.rotation.z,
@@ -2342,7 +2342,7 @@ var _TransformComponent = class _TransformComponent extends Component {
2342
2342
  );
2343
2343
  return target.setFromEuler(euler);
2344
2344
  }
2345
- toScaleVector3(target = new THREE39.Vector3()) {
2345
+ toScaleVector3(target = new THREE51.Vector3()) {
2346
2346
  return target.set(this.scale.x, this.scale.y, this.scale.z);
2347
2347
  }
2348
2348
  setPosition(input) {
@@ -2360,8 +2360,8 @@ var _TransformComponent = class _TransformComponent extends Component {
2360
2360
  return this;
2361
2361
  }
2362
2362
  setRotation(input, order = "XYZ") {
2363
- if (input instanceof THREE39.Quaternion) {
2364
- const euler = new THREE39.Euler().setFromQuaternion(input, order);
2363
+ if (input instanceof THREE51.Quaternion) {
2364
+ const euler = new THREE51.Euler().setFromQuaternion(input, order);
2365
2365
  this.rotation.x = euler.x;
2366
2366
  this.rotation.y = euler.y;
2367
2367
  this.rotation.z = euler.z;
@@ -2830,7 +2830,6 @@ var _XRInteractableComponent = class _XRInteractableComponent extends Component
2830
2830
  this.enabled = options?.enabled ?? true;
2831
2831
  this.grabbable = options?.grabbable ?? true;
2832
2832
  this.pointable = options?.pointable ?? true;
2833
- this.grabMode = options?.grabMode ?? "kinematic";
2834
2833
  this.grabReach = options?.grabReach ?? "both";
2835
2834
  this.distanceBehavior = options?.distanceBehavior ?? "snap";
2836
2835
  this.grabGesture = options?.grabGesture ?? "any";
@@ -2845,13 +2844,34 @@ var _XRInteractableComponent = class _XRInteractableComponent extends Component
2845
2844
  y: 0,
2846
2845
  z: 0
2847
2846
  };
2847
+ this.grabHands = options?.grabHands ?? "handOff";
2848
+ this.twoHandRotation = options?.twoHandRotation ?? "align";
2849
+ this.allowTwoHandScale = options?.allowTwoHandScale ?? false;
2850
+ this.minScale = options?.minScale ?? 0.25;
2851
+ this.maxScale = options?.maxScale ?? 4;
2852
+ this.attachPoints = (options?.attachPoints ?? []).map(
2853
+ (point) => createAttachPoint(point)
2854
+ );
2848
2855
  this.throwOnRelease = options?.throwOnRelease ?? true;
2849
2856
  this.throwStrength = options?.throwStrength ?? 1;
2850
- this.highlightOnHover = options?.highlightOnHover ?? true;
2857
+ this.hoverBehavior = options?.hoverBehavior ?? "color";
2858
+ this.hoverColor = options?.hoverColor ?? "#4da3ff";
2859
+ this.hoverScale = options?.hoverScale ?? 1.06;
2851
2860
  }
2852
2861
  };
2853
2862
  __name(_XRInteractableComponent, "XRInteractableComponent");
2854
2863
  var XRInteractableComponent = _XRInteractableComponent;
2864
+ function createAttachPoint(partial) {
2865
+ return {
2866
+ id: partial.id,
2867
+ name: partial.name ?? partial.id,
2868
+ position: { x: 0, y: 0, z: 0, ...partial.position },
2869
+ rotation: { x: 0, y: 0, z: 0, ...partial.rotation },
2870
+ hand: partial.hand ?? "any",
2871
+ role: partial.role ?? "any"
2872
+ };
2873
+ }
2874
+ __name(createAttachPoint, "createAttachPoint");
2855
2875
 
2856
2876
  // core/factories/components/XRInteractableFactory.ts
2857
2877
  function createXRInteractableComponent(world, entityId, ...args) {
@@ -2958,7 +2978,7 @@ var SHARED_MODEL_RESOURCE_FLAG = "spatialEcsSharedModelResource";
2958
2978
  var modelCache = /* @__PURE__ */ new Map();
2959
2979
  function markSharedResources(root) {
2960
2980
  root.traverse((child) => {
2961
- if (!(child instanceof THREE39__namespace.Mesh)) {
2981
+ if (!(child instanceof THREE51__namespace.Mesh)) {
2962
2982
  return;
2963
2983
  }
2964
2984
  if (child.geometry) {
@@ -3037,10 +3057,10 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
3037
3057
  }
3038
3058
  getOrCreateAudioListener() {
3039
3059
  const existing = this.camera.userData.ecsAudioListener;
3040
- if (existing instanceof THREE39__namespace.AudioListener) {
3060
+ if (existing instanceof THREE51__namespace.AudioListener) {
3041
3061
  return existing;
3042
3062
  }
3043
- const listener = new THREE39__namespace.AudioListener();
3063
+ const listener = new THREE51__namespace.AudioListener();
3044
3064
  this.camera.add(listener);
3045
3065
  this.camera.userData.ecsAudioListener = listener;
3046
3066
  return listener;
@@ -3114,7 +3134,7 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
3114
3134
  }
3115
3135
  if (dispose && object3d) {
3116
3136
  object3d.traverse((child) => {
3117
- if (child instanceof THREE39__namespace.Mesh) {
3137
+ if (child instanceof THREE51__namespace.Mesh) {
3118
3138
  if (!child.geometry?.userData?.[SHARED_MODEL_RESOURCE_FLAG]) {
3119
3139
  child.geometry?.dispose();
3120
3140
  }
@@ -3251,10 +3271,10 @@ __name(createBaseEntity, "createBaseEntity");
3251
3271
  // core/factories/objects/AudioFactory.ts
3252
3272
  function getOrCreateListener(camera) {
3253
3273
  const existing = camera.userData.ecsAudioListener;
3254
- if (existing instanceof THREE39__namespace.AudioListener) {
3274
+ if (existing instanceof THREE51__namespace.AudioListener) {
3255
3275
  return existing;
3256
3276
  }
3257
- const listener = new THREE39__namespace.AudioListener();
3277
+ const listener = new THREE51__namespace.AudioListener();
3258
3278
  camera.add(listener);
3259
3279
  camera.userData.ecsAudioListener = listener;
3260
3280
  return listener;
@@ -3267,7 +3287,7 @@ function createAudio({
3267
3287
  payload,
3268
3288
  isEditor
3269
3289
  }) {
3270
- const anchor = new THREE39__namespace.Object3D();
3290
+ const anchor = new THREE51__namespace.Object3D();
3271
3291
  const entityId = createBaseEntity(world, anchor, {
3272
3292
  label: payload.name ?? "Audio",
3273
3293
  entityType: "audio",
@@ -3289,7 +3309,7 @@ function createAudio({
3289
3309
  return entityId;
3290
3310
  }
3291
3311
  if (!isEditor) {
3292
- const loader = new THREE39__namespace.AudioLoader();
3312
+ const loader = new THREE51__namespace.AudioLoader();
3293
3313
  loader.load(
3294
3314
  audio.path,
3295
3315
  (buffer) => {
@@ -3364,7 +3384,7 @@ function applyCameraSettings(camera, options) {
3364
3384
  const near = typeof options.near === "number" ? options.near : DEFAULT_NEAR2;
3365
3385
  const far = typeof options.far === "number" ? options.far : DEFAULT_FAR2;
3366
3386
  const projection = options.projection;
3367
- if (camera instanceof THREE39__namespace.PerspectiveCamera) {
3387
+ if (camera instanceof THREE51__namespace.PerspectiveCamera) {
3368
3388
  if (!projection || projection === "perspective") {
3369
3389
  camera.fov = typeof options.fov === "number" ? options.fov : DEFAULT_FOV2;
3370
3390
  }
@@ -3374,7 +3394,7 @@ function applyCameraSettings(camera, options) {
3374
3394
  camera.updateProjectionMatrix();
3375
3395
  return;
3376
3396
  }
3377
- if (camera instanceof THREE39__namespace.OrthographicCamera) {
3397
+ if (camera instanceof THREE51__namespace.OrthographicCamera) {
3378
3398
  camera.left = typeof options.left === "number" ? options.left : -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
3379
3399
  camera.right = typeof options.right === "number" ? options.right : DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
3380
3400
  camera.top = typeof options.top === "number" ? options.top : DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
@@ -3395,7 +3415,7 @@ function createCameraObject(options) {
3395
3415
  const bottom = options.bottom ?? -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
3396
3416
  const left = options.left ?? -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
3397
3417
  const right = options.right ?? DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
3398
- const camera = new THREE39__namespace.OrthographicCamera(
3418
+ const camera = new THREE51__namespace.OrthographicCamera(
3399
3419
  left,
3400
3420
  right,
3401
3421
  top,
@@ -3407,7 +3427,7 @@ function createCameraObject(options) {
3407
3427
  camera.updateProjectionMatrix();
3408
3428
  return camera;
3409
3429
  }
3410
- return new THREE39__namespace.PerspectiveCamera(
3430
+ return new THREE51__namespace.PerspectiveCamera(
3411
3431
  options.fov ?? DEFAULT_FOV2,
3412
3432
  readViewportAspect(),
3413
3433
  near,
@@ -3425,7 +3445,7 @@ function createCamera({ world, scene, camera, payload, isEditor }) {
3425
3445
  transformInput: payload.transform
3426
3446
  });
3427
3447
  const objectRef = world.getComponent(entityId, Object3DRef);
3428
- if (objectRef?.object instanceof THREE39__namespace.Camera) {
3448
+ if (objectRef?.object instanceof THREE51__namespace.Camera) {
3429
3449
  objectRef.object.userData.entityId = entityId;
3430
3450
  }
3431
3451
  const componentOptions = {
@@ -3457,9 +3477,9 @@ function createCapsule({
3457
3477
  scene,
3458
3478
  payload
3459
3479
  }) {
3460
- const geometry = new THREE39__namespace.CapsuleGeometry(0.5, 1, 4, 8);
3461
- const material = new THREE39__namespace.MeshStandardMaterial({ color: "#ffffff" });
3462
- const mesh = new THREE39__namespace.Mesh(geometry, material);
3480
+ const geometry = new THREE51__namespace.CapsuleGeometry(0.5, 1, 4, 8);
3481
+ const material = new THREE51__namespace.MeshStandardMaterial({ color: "#ffffff" });
3482
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
3463
3483
  const entityId = createBaseEntity(world, mesh, {
3464
3484
  label: payload.name ?? "Capsule",
3465
3485
  entityType: "capsule",
@@ -3479,9 +3499,9 @@ function createCapsule({
3479
3499
  }
3480
3500
  __name(createCapsule, "createCapsule");
3481
3501
  function createCube({ world, scene, payload }) {
3482
- const geometry = new THREE39__namespace.BoxGeometry(1, 1, 1);
3483
- const material = new THREE39__namespace.MeshStandardMaterial({ color: "#ffffff" });
3484
- const mesh = new THREE39__namespace.Mesh(geometry, material);
3502
+ const geometry = new THREE51__namespace.BoxGeometry(1, 1, 1);
3503
+ const material = new THREE51__namespace.MeshStandardMaterial({ color: "#ffffff" });
3504
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
3485
3505
  const entityId = createBaseEntity(world, mesh, {
3486
3506
  label: payload.name ?? "Cube",
3487
3507
  entityType: "cube",
@@ -3506,9 +3526,9 @@ function createCylinder({
3506
3526
  scene,
3507
3527
  payload
3508
3528
  }) {
3509
- const geometry = new THREE39__namespace.CylinderGeometry(0.5, 0.5, 1, 20);
3510
- const material = new THREE39__namespace.MeshStandardMaterial({ color: "#ffffff" });
3511
- const mesh = new THREE39__namespace.Mesh(geometry, material);
3529
+ const geometry = new THREE51__namespace.CylinderGeometry(0.5, 0.5, 1, 20);
3530
+ const material = new THREE51__namespace.MeshStandardMaterial({ color: "#ffffff" });
3531
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
3512
3532
  const entityId = createBaseEntity(world, mesh, {
3513
3533
  label: payload.name ?? "Cylinder",
3514
3534
  entityType: "cylinder",
@@ -3528,7 +3548,7 @@ function createCylinder({
3528
3548
  }
3529
3549
  __name(createCylinder, "createCylinder");
3530
3550
  function createEmpty({ world, scene, payload, isEditor }) {
3531
- const object = new THREE39__namespace.Group();
3551
+ const object = new THREE51__namespace.Group();
3532
3552
  const entityId = createBaseEntity(world, object, {
3533
3553
  label: payload.name ?? "Empty",
3534
3554
  entityType: "empty",
@@ -3539,7 +3559,7 @@ function createEmpty({ world, scene, payload, isEditor }) {
3539
3559
  }
3540
3560
  __name(createEmpty, "createEmpty");
3541
3561
  function createImage({ world, scene, payload }) {
3542
- const anchor = new THREE39__namespace.Object3D();
3562
+ const anchor = new THREE51__namespace.Object3D();
3543
3563
  const imagePath = payload.path ?? "";
3544
3564
  const entityId = createBaseEntity(world, anchor, {
3545
3565
  label: payload.name ?? "Image",
@@ -3552,7 +3572,7 @@ function createImage({ world, scene, payload }) {
3552
3572
  if (!imagePath) {
3553
3573
  return entityId;
3554
3574
  }
3555
- const loader = new THREE39__namespace.TextureLoader();
3575
+ const loader = new THREE51__namespace.TextureLoader();
3556
3576
  loader.load(
3557
3577
  imagePath,
3558
3578
  (texture) => {
@@ -3560,13 +3580,13 @@ function createImage({ world, scene, payload }) {
3560
3580
  texture.dispose();
3561
3581
  return;
3562
3582
  }
3563
- const material = new THREE39__namespace.MeshBasicMaterial({
3583
+ const material = new THREE51__namespace.MeshBasicMaterial({
3564
3584
  map: texture,
3565
3585
  transparent: true,
3566
- side: THREE39__namespace.DoubleSide
3586
+ side: THREE51__namespace.DoubleSide
3567
3587
  });
3568
- const geometry = new THREE39__namespace.PlaneGeometry(1, 1);
3569
- const plane = new THREE39__namespace.Mesh(geometry, material);
3588
+ const geometry = new THREE51__namespace.PlaneGeometry(1, 1);
3589
+ const plane = new THREE51__namespace.Mesh(geometry, material);
3570
3590
  plane.userData.entityId = entityId;
3571
3591
  const textureImage = texture.image;
3572
3592
  const width = textureImage?.width ?? 1;
@@ -3612,7 +3632,7 @@ function createImageTarget({
3612
3632
  payload,
3613
3633
  isEditor
3614
3634
  }) {
3615
- const anchor = new THREE39__namespace.Object3D();
3635
+ const anchor = new THREE51__namespace.Object3D();
3616
3636
  const imagePath = payload.path ?? "";
3617
3637
  const entityId = createBaseEntity(world, anchor, {
3618
3638
  label: payload.name ?? "Image Target",
@@ -3629,7 +3649,7 @@ function createImageTarget({
3629
3649
  if (!isEditor || !imagePath) {
3630
3650
  return entityId;
3631
3651
  }
3632
- const loader = new THREE39__namespace.TextureLoader();
3652
+ const loader = new THREE51__namespace.TextureLoader();
3633
3653
  loader.load(
3634
3654
  imagePath,
3635
3655
  (texture) => {
@@ -3637,13 +3657,13 @@ function createImageTarget({
3637
3657
  texture.dispose();
3638
3658
  return;
3639
3659
  }
3640
- const material = new THREE39__namespace.MeshBasicMaterial({
3660
+ const material = new THREE51__namespace.MeshBasicMaterial({
3641
3661
  map: texture,
3642
3662
  transparent: true,
3643
- side: THREE39__namespace.DoubleSide
3663
+ side: THREE51__namespace.DoubleSide
3644
3664
  });
3645
- const geometry = new THREE39__namespace.PlaneGeometry(1, 1);
3646
- const plane = new THREE39__namespace.Mesh(geometry, material);
3665
+ const geometry = new THREE51__namespace.PlaneGeometry(1, 1);
3666
+ const plane = new THREE51__namespace.Mesh(geometry, material);
3647
3667
  plane.userData.entityId = entityId;
3648
3668
  const textureImage = texture.image;
3649
3669
  const width = textureImage?.width ?? 1;
@@ -3694,7 +3714,7 @@ function createInstancedMesh({
3694
3714
  scene,
3695
3715
  payload
3696
3716
  }) {
3697
- const anchor = new THREE39__namespace.Object3D();
3717
+ const anchor = new THREE51__namespace.Object3D();
3698
3718
  const opts = payload.options ?? {};
3699
3719
  const meshKind = isInstancedMeshKind(opts.meshKind) ? opts.meshKind : "cube";
3700
3720
  const meshParameters = opts.meshParameters && typeof opts.meshParameters === "object" ? opts.meshParameters : void 0;
@@ -3765,7 +3785,7 @@ function createAmbientLight({
3765
3785
  scene,
3766
3786
  payload
3767
3787
  }) {
3768
- const light = new THREE39__namespace.AmbientLight("#ffffff", 0.6);
3788
+ const light = new THREE51__namespace.AmbientLight("#ffffff", 0.6);
3769
3789
  const entityId = createBaseEntity(world, light, {
3770
3790
  label: payload.name ?? "Ambient Light",
3771
3791
  entityType: "ambientLight",
@@ -3782,7 +3802,7 @@ function createAreaLight({
3782
3802
  payload,
3783
3803
  isEditor
3784
3804
  }) {
3785
- const light = new THREE39__namespace.RectAreaLight("#ffffff", 5, 4, 4);
3805
+ const light = new THREE51__namespace.RectAreaLight("#ffffff", 5, 4, 4);
3786
3806
  light.lookAt(0, 0, 0);
3787
3807
  const entityId = createBaseEntity(world, light, {
3788
3808
  label: payload.name ?? "Area Light",
@@ -3811,7 +3831,7 @@ function createDirectionalLight({
3811
3831
  payload,
3812
3832
  isEditor
3813
3833
  }) {
3814
- const light = new THREE39__namespace.DirectionalLight("#ffffff", 1.5);
3834
+ const light = new THREE51__namespace.DirectionalLight("#ffffff", 1.5);
3815
3835
  light.castShadow = true;
3816
3836
  const entityId = createBaseEntity(world, light, {
3817
3837
  label: payload.name ?? "Directional Light",
@@ -3822,7 +3842,7 @@ function createDirectionalLight({
3822
3842
  });
3823
3843
  createLightComponent(world, entityId, light);
3824
3844
  if (isEditor) {
3825
- const helper = new THREE39__namespace.DirectionalLightHelper(light, 1.8, "#38bdf8");
3845
+ const helper = new THREE51__namespace.DirectionalLightHelper(light, 1.8, "#38bdf8");
3826
3846
  helper.onBeforeRender = () => {
3827
3847
  helper.update();
3828
3848
  };
@@ -3840,7 +3860,7 @@ function createPointLight({
3840
3860
  payload,
3841
3861
  isEditor
3842
3862
  }) {
3843
- const light = new THREE39__namespace.PointLight("#ffffff", 1, 10);
3863
+ const light = new THREE51__namespace.PointLight("#ffffff", 1, 10);
3844
3864
  const entityId = createBaseEntity(world, light, {
3845
3865
  label: payload.name ?? "Point Light",
3846
3866
  entityType: "pointLight",
@@ -3850,7 +3870,7 @@ function createPointLight({
3850
3870
  });
3851
3871
  createLightComponent(world, entityId, light);
3852
3872
  if (isEditor) {
3853
- const helper = new THREE39__namespace.PointLightHelper(light, 0.35, "#fbbf24");
3873
+ const helper = new THREE51__namespace.PointLightHelper(light, 0.35, "#fbbf24");
3854
3874
  helper.onBeforeRender = () => {
3855
3875
  helper.update();
3856
3876
  };
@@ -3868,7 +3888,7 @@ function createSpotLight({
3868
3888
  payload,
3869
3889
  isEditor
3870
3890
  }) {
3871
- const light = new THREE39__namespace.SpotLight("#ffffff", 1, 20, Math.PI / 6, 0.1, 1);
3891
+ const light = new THREE51__namespace.SpotLight("#ffffff", 1, 20, Math.PI / 6, 0.1, 1);
3872
3892
  const entityId = createBaseEntity(world, light, {
3873
3893
  label: payload.name ?? "Spot Light",
3874
3894
  entityType: "spotLight",
@@ -3878,7 +3898,7 @@ function createSpotLight({
3878
3898
  });
3879
3899
  createLightComponent(world, entityId, light);
3880
3900
  if (isEditor) {
3881
- const helper = new THREE39__namespace.SpotLightHelper(light, "#f59e0b");
3901
+ const helper = new THREE51__namespace.SpotLightHelper(light, "#f59e0b");
3882
3902
  helper.onBeforeRender = () => {
3883
3903
  helper.update();
3884
3904
  };
@@ -3969,6 +3989,7 @@ __export(components_exports, {
3969
3989
  XRInteractorComponent: () => XRInteractorComponent,
3970
3990
  XRInteractorSideComponent: () => XRInteractorSideComponent,
3971
3991
  XRRigComponent: () => XRRigComponent,
3992
+ createAttachPoint: () => createAttachPoint,
3972
3993
  createHandPoseEntry: () => createHandPoseEntry,
3973
3994
  defaultMaterialValue: () => defaultMaterialValue,
3974
3995
  handPoseDefinitionFor: () => handPoseDefinitionFor,
@@ -4029,7 +4050,7 @@ function createModel({
4029
4050
  payload,
4030
4051
  isEditor
4031
4052
  }) {
4032
- const anchor = new THREE39__namespace.Object3D();
4053
+ const anchor = new THREE51__namespace.Object3D();
4033
4054
  const modelPath = payload.path ?? "";
4034
4055
  const entityId = createBaseEntity(world, anchor, {
4035
4056
  label: payload.name ?? "Model",
@@ -4095,12 +4116,12 @@ function createModel({
4095
4116
  }
4096
4117
  __name(createModel, "createModel");
4097
4118
  function createPlane({ world, scene, payload }) {
4098
- const geometry = new THREE39__namespace.PlaneGeometry(100, 100);
4099
- const material = new THREE39__namespace.MeshStandardMaterial({
4119
+ const geometry = new THREE51__namespace.PlaneGeometry(100, 100);
4120
+ const material = new THREE51__namespace.MeshStandardMaterial({
4100
4121
  color: "#ffffff",
4101
- side: THREE39__namespace.DoubleSide
4122
+ side: THREE51__namespace.DoubleSide
4102
4123
  });
4103
- const mesh = new THREE39__namespace.Mesh(geometry, material);
4124
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
4104
4125
  const entityId = createBaseEntity(
4105
4126
  world,
4106
4127
  mesh,
@@ -4136,7 +4157,7 @@ function createScriptObject({
4136
4157
  scene,
4137
4158
  payload
4138
4159
  }) {
4139
- const object = new THREE39__namespace.Group();
4160
+ const object = new THREE51__namespace.Group();
4140
4161
  const entityId = createBaseEntity(world, object, {
4141
4162
  label: payload.name ?? "Script Object",
4142
4163
  entityType: "scriptObject",
@@ -4161,15 +4182,15 @@ function createTeleportArea({
4161
4182
  const options = payload.options ?? {};
4162
4183
  const width = typeof options.width === "number" ? options.width : 10;
4163
4184
  const depth = typeof options.depth === "number" ? options.depth : 10;
4164
- const geometry = new THREE39__namespace.PlaneGeometry(width, depth);
4165
- const material = new THREE39__namespace.MeshBasicMaterial({
4185
+ const geometry = new THREE51__namespace.PlaneGeometry(width, depth);
4186
+ const material = new THREE51__namespace.MeshBasicMaterial({
4166
4187
  color: "#4da3ff",
4167
4188
  transparent: true,
4168
4189
  opacity: 0.18,
4169
- side: THREE39__namespace.DoubleSide,
4190
+ side: THREE51__namespace.DoubleSide,
4170
4191
  depthWrite: false
4171
4192
  });
4172
- const object = new THREE39__namespace.Mesh(geometry, material);
4193
+ const object = new THREE51__namespace.Mesh(geometry, material);
4173
4194
  const entityId = createBaseEntity(world, object, {
4174
4195
  label: payload.name ?? "Teleport Area",
4175
4196
  entityType: "teleportArea",
@@ -4197,7 +4218,7 @@ function createXRInteractor({
4197
4218
  scene,
4198
4219
  payload
4199
4220
  }) {
4200
- const root = new THREE39__namespace.Group();
4221
+ const root = new THREE51__namespace.Group();
4201
4222
  const options = payload.options ?? {};
4202
4223
  const entityId = createBaseEntity(world, root, {
4203
4224
  label: payload.name ?? "XR Interactor",
@@ -4207,7 +4228,7 @@ function createXRInteractor({
4207
4228
  world.addComponent(entityId, new XRInteractorComponent(options));
4208
4229
  scene.add(root);
4209
4230
  for (const side of ["left", "right"]) {
4210
- const slot = new THREE39__namespace.Group();
4231
+ const slot = new THREE51__namespace.Group();
4211
4232
  const label = side === "left" ? "Left Interactor" : "Right Interactor";
4212
4233
  const slotEntityId = createBaseEntity(world, slot, {
4213
4234
  label,
@@ -4225,7 +4246,7 @@ function createXRRig({
4225
4246
  scene,
4226
4247
  payload
4227
4248
  }) {
4228
- const object = new THREE39__namespace.Group();
4249
+ const object = new THREE51__namespace.Group();
4229
4250
  const entityId = createBaseEntity(world, object, {
4230
4251
  label: payload.name ?? "XR Rig",
4231
4252
  entityType: "xrRig",
@@ -4238,7 +4259,7 @@ function createXRRig({
4238
4259
  }
4239
4260
  __name(createXRRig, "createXRRig");
4240
4261
  function createSprite({ world, scene, payload }) {
4241
- const anchor = new THREE39__namespace.Object3D();
4262
+ const anchor = new THREE51__namespace.Object3D();
4242
4263
  const imagePath = payload.path ?? "";
4243
4264
  const entityId = createBaseEntity(world, anchor, {
4244
4265
  label: payload.name ?? "Sprite",
@@ -4250,7 +4271,7 @@ function createSprite({ world, scene, payload }) {
4250
4271
  if (!imagePath) {
4251
4272
  return entityId;
4252
4273
  }
4253
- const loader = new THREE39__namespace.TextureLoader();
4274
+ const loader = new THREE51__namespace.TextureLoader();
4254
4275
  loader.load(
4255
4276
  imagePath,
4256
4277
  (texture) => {
@@ -4258,11 +4279,11 @@ function createSprite({ world, scene, payload }) {
4258
4279
  texture.dispose();
4259
4280
  return;
4260
4281
  }
4261
- const material = new THREE39__namespace.SpriteMaterial({
4282
+ const material = new THREE51__namespace.SpriteMaterial({
4262
4283
  map: texture,
4263
4284
  transparent: true
4264
4285
  });
4265
- const sprite = new THREE39__namespace.Sprite(material);
4286
+ const sprite = new THREE51__namespace.Sprite(material);
4266
4287
  sprite.userData.entityId = entityId;
4267
4288
  const textureImage = texture.image;
4268
4289
  spriteComponent.width = textureImage?.width ?? 1;
@@ -4280,9 +4301,9 @@ function createSprite({ world, scene, payload }) {
4280
4301
  }
4281
4302
  __name(createSprite, "createSprite");
4282
4303
  function createSphere({ world, scene, payload }) {
4283
- const geometry = new THREE39__namespace.SphereGeometry(0.5, 24, 24);
4284
- const material = new THREE39__namespace.MeshStandardMaterial({ color: "#ffffff" });
4285
- const mesh = new THREE39__namespace.Mesh(geometry, material);
4304
+ const geometry = new THREE51__namespace.SphereGeometry(0.5, 24, 24);
4305
+ const material = new THREE51__namespace.MeshStandardMaterial({ color: "#ffffff" });
4306
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
4286
4307
  const entityId = createBaseEntity(world, mesh, {
4287
4308
  label: payload.name ?? "Sphere",
4288
4309
  entityType: "sphere",
@@ -4302,10 +4323,10 @@ function createSphere({ world, scene, payload }) {
4302
4323
  __name(createSphere, "createSphere");
4303
4324
  function getOrCreateListener2(camera) {
4304
4325
  const existing = camera.userData.ecsAudioListener;
4305
- if (existing instanceof THREE39__namespace.AudioListener) {
4326
+ if (existing instanceof THREE51__namespace.AudioListener) {
4306
4327
  return existing;
4307
4328
  }
4308
- const listener = new THREE39__namespace.AudioListener();
4329
+ const listener = new THREE51__namespace.AudioListener();
4309
4330
  camera.add(listener);
4310
4331
  camera.userData.ecsAudioListener = listener;
4311
4332
  return listener;
@@ -4355,10 +4376,10 @@ function normalizeOptions(input) {
4355
4376
  }
4356
4377
  __name(normalizeOptions, "normalizeOptions");
4357
4378
  function createChromaMaterial(texture, options) {
4358
- return new THREE39__namespace.ShaderMaterial({
4379
+ return new THREE51__namespace.ShaderMaterial({
4359
4380
  uniforms: {
4360
4381
  tex: { value: texture },
4361
- keyColor: { value: new THREE39__namespace.Color(options.backgroundColor) },
4382
+ keyColor: { value: new THREE51__namespace.Color(options.backgroundColor) },
4362
4383
  similarity: { value: options.similarity },
4363
4384
  smoothness: { value: options.smoothness },
4364
4385
  spill: { value: options.spill }
@@ -4390,22 +4411,22 @@ function createChromaMaterial(texture, options) {
4390
4411
  }
4391
4412
  `,
4392
4413
  transparent: true,
4393
- side: THREE39__namespace.DoubleSide
4414
+ side: THREE51__namespace.DoubleSide
4394
4415
  });
4395
4416
  }
4396
4417
  __name(createChromaMaterial, "createChromaMaterial");
4397
4418
  function createStandardMaterial(texture) {
4398
- return new THREE39__namespace.MeshBasicMaterial({
4419
+ return new THREE51__namespace.MeshBasicMaterial({
4399
4420
  map: texture,
4400
4421
  transparent: true,
4401
- side: THREE39__namespace.DoubleSide
4422
+ side: THREE51__namespace.DoubleSide
4402
4423
  });
4403
4424
  }
4404
4425
  __name(createStandardMaterial, "createStandardMaterial");
4405
4426
  function createVideoMesh(entityId, texture, options) {
4406
- const geometry = new THREE39__namespace.PlaneGeometry(1, 1);
4427
+ const geometry = new THREE51__namespace.PlaneGeometry(1, 1);
4407
4428
  const material = options.isGreenScreen ? createChromaMaterial(texture, options) : createStandardMaterial(texture);
4408
- const mesh = new THREE39__namespace.Mesh(geometry, material);
4429
+ const mesh = new THREE51__namespace.Mesh(geometry, material);
4409
4430
  mesh.userData.entityId = entityId;
4410
4431
  mesh.scale.set(options.width, options.height, 1);
4411
4432
  return { mesh, material };
@@ -4440,7 +4461,7 @@ function createVideo({
4440
4461
  payload,
4441
4462
  isEditor
4442
4463
  }) {
4443
- const anchor = new THREE39__namespace.Object3D();
4464
+ const anchor = new THREE51__namespace.Object3D();
4444
4465
  const path = payload.path ?? "";
4445
4466
  const options = normalizeOptions(payload.options);
4446
4467
  const entityId = createBaseEntity(world, anchor, {
@@ -4455,8 +4476,8 @@ function createVideo({
4455
4476
  return entityId;
4456
4477
  }
4457
4478
  const video = createVideoElement(path, options);
4458
- const texture = new THREE39__namespace.VideoTexture(video);
4459
- texture.colorSpace = THREE39__namespace.SRGBColorSpace;
4479
+ const texture = new THREE51__namespace.VideoTexture(video);
4480
+ texture.colorSpace = THREE51__namespace.SRGBColorSpace;
4460
4481
  texture.needsUpdate = true;
4461
4482
  const { mesh, material } = createVideoMesh(entityId, texture, options);
4462
4483
  anchor.add(mesh);
@@ -4472,7 +4493,7 @@ function createVideo({
4472
4493
  });
4473
4494
  if (!isEditor && options.positional) {
4474
4495
  const listener = getOrCreateListener2(camera);
4475
- const audioNode = new THREE39__namespace.PositionalAudio(listener);
4496
+ const audioNode = new THREE51__namespace.PositionalAudio(listener);
4476
4497
  audioNode.setVolume(options.volume);
4477
4498
  audioNode.setRefDistance(options.refDistance);
4478
4499
  audioNode.setDistanceModel(normalizeDistanceModel2(options.distanceModel));
@@ -4846,9 +4867,6 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
4846
4867
  if (typeof sourceInteractable.pointable === "boolean") {
4847
4868
  interactable.pointable = sourceInteractable.pointable;
4848
4869
  }
4849
- if (typeof sourceInteractable.grabMode === "string") {
4850
- interactable.grabMode = sourceInteractable.grabMode;
4851
- }
4852
4870
  if (typeof sourceInteractable.grabReach === "string") {
4853
4871
  interactable.grabReach = sourceInteractable.grabReach;
4854
4872
  }
@@ -4861,6 +4879,30 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
4861
4879
  if (typeof sourceInteractable.grabRadius === "number") {
4862
4880
  interactable.grabRadius = sourceInteractable.grabRadius;
4863
4881
  }
4882
+ if (typeof sourceInteractable.grabHands === "string") {
4883
+ interactable.grabHands = sourceInteractable.grabHands;
4884
+ }
4885
+ if (typeof sourceInteractable.twoHandRotation === "string") {
4886
+ interactable.twoHandRotation = sourceInteractable.twoHandRotation;
4887
+ }
4888
+ if (typeof sourceInteractable.allowTwoHandScale === "boolean") {
4889
+ interactable.allowTwoHandScale = sourceInteractable.allowTwoHandScale;
4890
+ }
4891
+ if (typeof sourceInteractable.minScale === "number") {
4892
+ interactable.minScale = sourceInteractable.minScale;
4893
+ }
4894
+ if (typeof sourceInteractable.maxScale === "number") {
4895
+ interactable.maxScale = sourceInteractable.maxScale;
4896
+ }
4897
+ if (Array.isArray(sourceInteractable.attachPoints)) {
4898
+ interactable.attachPoints = sourceInteractable.attachPoints.map((point) => asRecord3(point)).filter(
4899
+ (point) => point !== null && typeof point.id === "string"
4900
+ ).map(
4901
+ (point) => createAttachPoint(
4902
+ point
4903
+ )
4904
+ );
4905
+ }
4864
4906
  if (typeof sourceInteractable.throwOnRelease === "boolean") {
4865
4907
  interactable.throwOnRelease = sourceInteractable.throwOnRelease;
4866
4908
  }
@@ -4883,8 +4925,16 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
4883
4925
  z: typeof grabRot.z === "number" ? grabRot.z : 0
4884
4926
  };
4885
4927
  }
4886
- if (typeof sourceInteractable.highlightOnHover === "boolean") {
4887
- interactable.highlightOnHover = sourceInteractable.highlightOnHover;
4928
+ if (typeof sourceInteractable.hoverBehavior === "string") {
4929
+ interactable.hoverBehavior = sourceInteractable.hoverBehavior;
4930
+ } else if (sourceInteractable.highlightOnHover === false) {
4931
+ interactable.hoverBehavior = "none";
4932
+ }
4933
+ if (typeof sourceInteractable.hoverColor === "string") {
4934
+ interactable.hoverColor = sourceInteractable.hoverColor;
4935
+ }
4936
+ if (typeof sourceInteractable.hoverScale === "number") {
4937
+ interactable.hoverScale = sourceInteractable.hoverScale;
4888
4938
  }
4889
4939
  }
4890
4940
  }
@@ -6660,7 +6710,7 @@ function transitionToClip(runtime, nextClipId, crossFadeDuration) {
6660
6710
  }
6661
6711
  __name(transitionToClip, "transitionToClip");
6662
6712
  function applyActionState(action, component, paused, shouldLoop) {
6663
- const loopMode = shouldLoop ? THREE39__namespace.LoopRepeat : THREE39__namespace.LoopOnce;
6713
+ const loopMode = shouldLoop ? THREE51__namespace.LoopRepeat : THREE51__namespace.LoopOnce;
6664
6714
  const repetitions = shouldLoop ? Infinity : 1;
6665
6715
  action.setLoop(loopMode, repetitions);
6666
6716
  action.clampWhenFinished = !shouldLoop;
@@ -6833,7 +6883,7 @@ var _AnimationSystem = class _AnimationSystem {
6833
6883
  if (runtime) {
6834
6884
  stopAllActions(runtime);
6835
6885
  }
6836
- const mixer = new THREE39__namespace.AnimationMixer(root);
6886
+ const mixer = new THREE51__namespace.AnimationMixer(root);
6837
6887
  const entries2 = readRuntimeClipEntries(animation);
6838
6888
  const indexedActions = createActions(mixer, entries2);
6839
6889
  runtime = {
@@ -6959,11 +7009,11 @@ function normalizeOrthoBounds(component) {
6959
7009
  }
6960
7010
  __name(normalizeOrthoBounds, "normalizeOrthoBounds");
6961
7011
  function isPerspectiveCamera(camera) {
6962
- return camera instanceof THREE39__namespace.PerspectiveCamera;
7012
+ return camera instanceof THREE51__namespace.PerspectiveCamera;
6963
7013
  }
6964
7014
  __name(isPerspectiveCamera, "isPerspectiveCamera");
6965
7015
  function isOrthographicCamera(camera) {
6966
- return camera instanceof THREE39__namespace.OrthographicCamera;
7016
+ return camera instanceof THREE51__namespace.OrthographicCamera;
6967
7017
  }
6968
7018
  __name(isOrthographicCamera, "isOrthographicCamera");
6969
7019
  function readWindowAspect() {
@@ -7017,7 +7067,7 @@ var _CameraSyncSystem = class _CameraSyncSystem {
7017
7067
  continue;
7018
7068
  }
7019
7069
  const camera = objectRef.object;
7020
- if (!(camera instanceof THREE39__namespace.Camera)) {
7070
+ if (!(camera instanceof THREE51__namespace.Camera)) {
7021
7071
  continue;
7022
7072
  }
7023
7073
  if (xrCamera !== null && camera === xrCamera) {
@@ -7084,7 +7134,7 @@ var MIN_DENSITY = 0;
7084
7134
  function createGradientTexture(effect, colors) {
7085
7135
  if (typeof document === "undefined") {
7086
7136
  const pixel = new Uint8Array([30, 30, 30, 255]);
7087
- const fallback = new THREE39__namespace.DataTexture(pixel, 1, 1, THREE39__namespace.RGBAFormat);
7137
+ const fallback = new THREE51__namespace.DataTexture(pixel, 1, 1, THREE51__namespace.RGBAFormat);
7088
7138
  fallback.needsUpdate = true;
7089
7139
  return fallback;
7090
7140
  }
@@ -7093,7 +7143,7 @@ function createGradientTexture(effect, colors) {
7093
7143
  canvas.height = 512;
7094
7144
  const context = canvas.getContext("2d");
7095
7145
  if (!context) {
7096
- const fallback = new THREE39__namespace.CanvasTexture(canvas);
7146
+ const fallback = new THREE51__namespace.CanvasTexture(canvas);
7097
7147
  fallback.needsUpdate = true;
7098
7148
  return fallback;
7099
7149
  }
@@ -7121,15 +7171,15 @@ function createGradientTexture(effect, colors) {
7121
7171
  context.fillRect(0, 0, 512, 512);
7122
7172
  context.globalCompositeOperation = "source-over";
7123
7173
  }
7124
- const texture = new THREE39__namespace.CanvasTexture(canvas);
7125
- texture.colorSpace = THREE39__namespace.SRGBColorSpace;
7174
+ const texture = new THREE51__namespace.CanvasTexture(canvas);
7175
+ texture.colorSpace = THREE51__namespace.SRGBColorSpace;
7126
7176
  texture.needsUpdate = true;
7127
7177
  return texture;
7128
7178
  }
7129
7179
  __name(createGradientTexture, "createGradientTexture");
7130
7180
  var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
7131
7181
  constructor(scene) {
7132
- this.textureLoader = new THREE39__namespace.TextureLoader();
7182
+ this.textureLoader = new THREE51__namespace.TextureLoader();
7133
7183
  this.signature = null;
7134
7184
  this.activeTexture = null;
7135
7185
  this.pendingImageUrl = null;
@@ -7152,7 +7202,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
7152
7202
  if (skybox.type === "color") {
7153
7203
  this.disposeActiveTexture();
7154
7204
  this.pendingImageUrl = null;
7155
- this.scene.background = new THREE39__namespace.Color(skybox.color || "#1e1e1e");
7205
+ this.scene.background = new THREE51__namespace.Color(skybox.color || "#1e1e1e");
7156
7206
  return;
7157
7207
  }
7158
7208
  if (skybox.type === "gradient") {
@@ -7186,7 +7236,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
7186
7236
  texture.dispose();
7187
7237
  return;
7188
7238
  }
7189
- texture.colorSpace = THREE39__namespace.SRGBColorSpace;
7239
+ texture.colorSpace = THREE51__namespace.SRGBColorSpace;
7190
7240
  this.activeTexture = texture;
7191
7241
  this.scene.background = texture;
7192
7242
  },
@@ -7205,16 +7255,16 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
7205
7255
  this.scene.fog = null;
7206
7256
  return;
7207
7257
  }
7208
- const fogColor = new THREE39__namespace.Color(fog.color || "#1e1e1e");
7258
+ const fogColor = new THREE51__namespace.Color(fog.color || "#1e1e1e");
7209
7259
  if (fog.type === "linear") {
7210
7260
  const near = Number.isFinite(fog.linear.near) ? fog.linear.near : 1;
7211
7261
  const farCandidate = Number.isFinite(fog.linear.far) ? fog.linear.far : near + 10;
7212
7262
  const far = Math.max(near + MIN_FOG_DELTA, farCandidate);
7213
- this.scene.fog = new THREE39__namespace.Fog(fogColor, near, far);
7263
+ this.scene.fog = new THREE51__namespace.Fog(fogColor, near, far);
7214
7264
  return;
7215
7265
  }
7216
7266
  const density = Number.isFinite(fog.exponential.density) ? Math.max(MIN_DENSITY, fog.exponential.density) : 2e-3;
7217
- this.scene.fog = new THREE39__namespace.FogExp2(fogColor, density);
7267
+ this.scene.fog = new THREE51__namespace.FogExp2(fogColor, density);
7218
7268
  }
7219
7269
  update(world) {
7220
7270
  const entityIds = world.query(Environment);
@@ -7296,10 +7346,10 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
7296
7346
  );
7297
7347
  }
7298
7348
  if (values.bloom.enabled) {
7299
- const size = this.renderer.getSize(new THREE39__namespace.Vector2());
7349
+ const size = this.renderer.getSize(new THREE51__namespace.Vector2());
7300
7350
  composer.addPass(
7301
7351
  new UnrealBloomPass_js.UnrealBloomPass(
7302
- new THREE39__namespace.Vector2(size.width, size.height),
7352
+ new THREE51__namespace.Vector2(size.width, size.height),
7303
7353
  values.bloom.intensity,
7304
7354
  values.bloom.radius,
7305
7355
  values.bloom.threshold
@@ -7344,7 +7394,7 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
7344
7394
  if (values.dotScreen.enabled) {
7345
7395
  composer.addPass(
7346
7396
  new DotScreenPass_js.DotScreenPass(
7347
- new THREE39__namespace.Vector2(0, 0),
7397
+ new THREE51__namespace.Vector2(0, 0),
7348
7398
  values.dotScreen.angle ?? values.dotScreen.dotScreenAngle,
7349
7399
  values.dotScreen.scale ?? values.dotScreen.dotScreenScale
7350
7400
  )
@@ -7401,6 +7451,20 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
7401
7451
  __name(_PostprocessingSyncSystem, "PostprocessingSyncSystem");
7402
7452
  var PostprocessingSyncSystem = _PostprocessingSyncSystem;
7403
7453
  var MIN_SIZE = 1e-4;
7454
+ function toBodyType(RAPIER, bodyType) {
7455
+ switch (bodyType) {
7456
+ case "fixed":
7457
+ return RAPIER.RigidBodyType.Fixed;
7458
+ case "kinematicPosition":
7459
+ return RAPIER.RigidBodyType.KinematicPositionBased;
7460
+ case "kinematicVelocity":
7461
+ return RAPIER.RigidBodyType.KinematicVelocityBased;
7462
+ case "dynamic":
7463
+ default:
7464
+ return RAPIER.RigidBodyType.Dynamic;
7465
+ }
7466
+ }
7467
+ __name(toBodyType, "toBodyType");
7404
7468
  function createRigidBodyDesc(RAPIER, bodyType) {
7405
7469
  if (bodyType === "fixed") {
7406
7470
  return RAPIER.RigidBodyDesc.fixed();
@@ -7418,7 +7482,7 @@ function computeColliderState(colliderValues, object) {
7418
7482
  if (!object) {
7419
7483
  return colliderValues;
7420
7484
  }
7421
- const scale = new THREE39__namespace.Vector3();
7485
+ const scale = new THREE51__namespace.Vector3();
7422
7486
  object.getWorldScale(scale);
7423
7487
  return {
7424
7488
  ...colliderValues,
@@ -7469,8 +7533,8 @@ function createColliderDesc(RAPIER, values, triMeshData) {
7469
7533
  values.offsetPosition.y,
7470
7534
  values.offsetPosition.z
7471
7535
  );
7472
- const offsetQuaternion = new THREE39__namespace.Quaternion().setFromEuler(
7473
- new THREE39__namespace.Euler(
7536
+ const offsetQuaternion = new THREE51__namespace.Quaternion().setFromEuler(
7537
+ new THREE51__namespace.Euler(
7474
7538
  values.offsetRotation.x,
7475
7539
  values.offsetRotation.y,
7476
7540
  values.offsetRotation.z
@@ -7485,17 +7549,17 @@ function collectTriMeshParts(object) {
7485
7549
  return null;
7486
7550
  }
7487
7551
  object.updateWorldMatrix(true, true);
7488
- const worldScale = new THREE39__namespace.Vector3();
7552
+ const worldScale = new THREE51__namespace.Vector3();
7489
7553
  object.getWorldScale(worldScale);
7490
7554
  const scale = {
7491
7555
  x: Math.abs(worldScale.x) || 1,
7492
7556
  y: Math.abs(worldScale.y) || 1,
7493
7557
  z: Math.abs(worldScale.z) || 1
7494
7558
  };
7495
- const worldToObject = new THREE39__namespace.Matrix4().copy(object.matrixWorld).invert();
7559
+ const worldToObject = new THREE51__namespace.Matrix4().copy(object.matrixWorld).invert();
7496
7560
  const parts = [];
7497
7561
  object.traverse((child) => {
7498
- if (!(child instanceof THREE39__namespace.Mesh) || !child.geometry) {
7562
+ if (!(child instanceof THREE51__namespace.Mesh) || !child.geometry) {
7499
7563
  return;
7500
7564
  }
7501
7565
  const geometry = child.geometry;
@@ -7512,7 +7576,7 @@ function collectTriMeshParts(object) {
7512
7576
  if (indexCount < 3) {
7513
7577
  return;
7514
7578
  }
7515
- const relativeMatrix = new THREE39__namespace.Matrix4().multiplyMatrices(
7579
+ const relativeMatrix = new THREE51__namespace.Matrix4().multiplyMatrices(
7516
7580
  worldToObject,
7517
7581
  child.matrixWorld
7518
7582
  );
@@ -7553,7 +7617,7 @@ function createTriMeshData(collected) {
7553
7617
  }
7554
7618
  const vertices = new Float32Array(vertexTotal * 3);
7555
7619
  const indices = new Uint32Array(indexTotal);
7556
- const point = new THREE39__namespace.Vector3();
7620
+ const point = new THREE51__namespace.Vector3();
7557
7621
  let vertexOffset = 0;
7558
7622
  let indexOffset = 0;
7559
7623
  for (const part of parts) {
@@ -7585,8 +7649,8 @@ function createTriMeshData(collected) {
7585
7649
  }
7586
7650
  __name(createTriMeshData, "createTriMeshData");
7587
7651
  function syncBodyFromTransform(body, transform) {
7588
- const quaternion = new THREE39__namespace.Quaternion().setFromEuler(
7589
- new THREE39__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
7652
+ const quaternion = new THREE51__namespace.Quaternion().setFromEuler(
7653
+ new THREE51__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
7590
7654
  );
7591
7655
  body.setTranslation(
7592
7656
  {
@@ -7613,8 +7677,8 @@ function syncTransformFromBody(transform, body) {
7613
7677
  transform.position.x = position.x;
7614
7678
  transform.position.y = position.y;
7615
7679
  transform.position.z = position.z;
7616
- const euler = new THREE39__namespace.Euler().setFromQuaternion(
7617
- new THREE39__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
7680
+ const euler = new THREE51__namespace.Euler().setFromQuaternion(
7681
+ new THREE51__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
7618
7682
  );
7619
7683
  transform.rotation.x = euler.x;
7620
7684
  transform.rotation.y = euler.y;
@@ -7699,7 +7763,8 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
7699
7763
  setPhysicsLiveRef(physics, "collider", null);
7700
7764
  continue;
7701
7765
  }
7702
- const rigidBodySignature = JSON.stringify(physics.values.rigidbody);
7766
+ const { type: _desiredBodyType, ...rigidBodyRebuildValues } = physics.values.rigidbody;
7767
+ const rigidBodySignature = JSON.stringify(rigidBodyRebuildValues);
7703
7768
  const previousRigidBodySignature = this.rigidBodySignatures.get(entityId);
7704
7769
  const hasRigidBody = this.rigidBodyHandles.has(entityId);
7705
7770
  const shouldRecreateBody = !hasRigidBody || previousRigidBodySignature !== rigidBodySignature;
@@ -7726,8 +7791,8 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
7726
7791
  transform.position.y,
7727
7792
  transform.position.z
7728
7793
  );
7729
- const quaternion = new THREE39__namespace.Quaternion().setFromEuler(
7730
- new THREE39__namespace.Euler(
7794
+ const quaternion = new THREE51__namespace.Quaternion().setFromEuler(
7795
+ new THREE51__namespace.Euler(
7731
7796
  transform.rotation.x,
7732
7797
  transform.rotation.y,
7733
7798
  transform.rotation.z
@@ -7754,6 +7819,25 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
7754
7819
  continue;
7755
7820
  }
7756
7821
  setPhysicsLiveRef(physics, "rigidBody", body);
7822
+ const desiredBodyType = this.isEditorMode ? "fixed" : physics.values.rigidbody.type;
7823
+ const desiredRapierType = toBodyType(this.rapier, desiredBodyType);
7824
+ if (body.bodyType() !== desiredRapierType) {
7825
+ body.setBodyType(desiredRapierType, true);
7826
+ if (!this.isEditorMode && desiredBodyType === "dynamic") {
7827
+ body.setAdditionalMass(physics.values.rigidbody.mass, true);
7828
+ }
7829
+ }
7830
+ const pending = physics.pendingVelocity;
7831
+ if (pending) {
7832
+ setPhysicsLiveRef(physics, "pendingVelocity", null);
7833
+ if (!this.isEditorMode && desiredBodyType === "dynamic") {
7834
+ try {
7835
+ body.setLinvel(pending.linear, true);
7836
+ body.setAngvel(pending.angular, true);
7837
+ } catch {
7838
+ }
7839
+ }
7840
+ }
7757
7841
  if (this.isEditorMode || physics.values.rigidbody.type !== "dynamic") {
7758
7842
  syncBodyFromTransform(body, transform);
7759
7843
  }
@@ -8306,7 +8390,7 @@ function applyLightValues(lightComponent) {
8306
8390
  values.castShadow
8307
8391
  );
8308
8392
  }
8309
- if (light instanceof THREE39__namespace.PointLight) {
8393
+ if (light instanceof THREE51__namespace.PointLight) {
8310
8394
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
8311
8395
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
8312
8396
  }
@@ -8314,7 +8398,7 @@ function applyLightValues(lightComponent) {
8314
8398
  light.decay = Math.max(0, values.decay);
8315
8399
  }
8316
8400
  }
8317
- if (light instanceof THREE39__namespace.SpotLight) {
8401
+ if (light instanceof THREE51__namespace.SpotLight) {
8318
8402
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
8319
8403
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
8320
8404
  }
@@ -8328,7 +8412,7 @@ function applyLightValues(lightComponent) {
8328
8412
  light.penumbra = Math.max(0, Math.min(1, values.penumbra));
8329
8413
  }
8330
8414
  }
8331
- if (light instanceof THREE39__namespace.RectAreaLight) {
8415
+ if (light instanceof THREE51__namespace.RectAreaLight) {
8332
8416
  if (typeof values.width === "number" && Number.isFinite(values.width)) {
8333
8417
  light.width = Math.max(MIN_AREA_SIZE, values.width);
8334
8418
  }
@@ -8389,15 +8473,15 @@ function resolveMaterialPropertyKey(key) {
8389
8473
  __name(resolveMaterialPropertyKey, "resolveMaterialPropertyKey");
8390
8474
  function createMaterialByType(type) {
8391
8475
  if (type === "basic") {
8392
- return new THREE39__namespace.MeshBasicMaterial();
8476
+ return new THREE51__namespace.MeshBasicMaterial();
8393
8477
  }
8394
8478
  if (type === "phong") {
8395
- return new THREE39__namespace.MeshPhongMaterial();
8479
+ return new THREE51__namespace.MeshPhongMaterial();
8396
8480
  }
8397
8481
  if (type === "physical") {
8398
- return new THREE39__namespace.MeshPhysicalMaterial();
8482
+ return new THREE51__namespace.MeshPhysicalMaterial();
8399
8483
  }
8400
- return new THREE39__namespace.MeshStandardMaterial();
8484
+ return new THREE51__namespace.MeshStandardMaterial();
8401
8485
  }
8402
8486
  __name(createMaterialByType, "createMaterialByType");
8403
8487
  function replaceMaterialAcrossObject(object, previous, next) {
@@ -8456,12 +8540,12 @@ function applyTextureFiltering(material, filtering) {
8456
8540
  const target = material;
8457
8541
  for (const slot of materialTextureSlots) {
8458
8542
  const texture = target[slot];
8459
- if (!texture || !(texture instanceof THREE39__namespace.Texture)) {
8543
+ if (!texture || !(texture instanceof THREE51__namespace.Texture)) {
8460
8544
  continue;
8461
8545
  }
8462
8546
  const useMipmaps = texture.generateMipmaps !== false;
8463
- texture.magFilter = sharp ? THREE39__namespace.NearestFilter : THREE39__namespace.LinearFilter;
8464
- texture.minFilter = useMipmaps ? sharp ? THREE39__namespace.NearestMipmapNearestFilter : THREE39__namespace.LinearMipmapLinearFilter : sharp ? THREE39__namespace.NearestFilter : THREE39__namespace.LinearFilter;
8547
+ texture.magFilter = sharp ? THREE51__namespace.NearestFilter : THREE51__namespace.LinearFilter;
8548
+ texture.minFilter = useMipmaps ? sharp ? THREE51__namespace.NearestMipmapNearestFilter : THREE51__namespace.LinearMipmapLinearFilter : sharp ? THREE51__namespace.NearestFilter : THREE51__namespace.LinearFilter;
8465
8549
  texture.needsUpdate = true;
8466
8550
  }
8467
8551
  material.needsUpdate = true;
@@ -8471,12 +8555,12 @@ function setTextureMipmaps(material, enabled) {
8471
8555
  const target = material;
8472
8556
  for (const slot of materialTextureSlots) {
8473
8557
  const texture = target[slot];
8474
- if (!texture || !(texture instanceof THREE39__namespace.Texture)) {
8558
+ if (!texture || !(texture instanceof THREE51__namespace.Texture)) {
8475
8559
  continue;
8476
8560
  }
8477
8561
  texture.generateMipmaps = enabled;
8478
- const sharp = texture.magFilter === THREE39__namespace.NearestFilter;
8479
- texture.minFilter = enabled ? sharp ? THREE39__namespace.NearestMipmapNearestFilter : THREE39__namespace.LinearMipmapLinearFilter : sharp ? THREE39__namespace.NearestFilter : THREE39__namespace.LinearFilter;
8562
+ const sharp = texture.magFilter === THREE51__namespace.NearestFilter;
8563
+ texture.minFilter = enabled ? sharp ? THREE51__namespace.NearestMipmapNearestFilter : THREE51__namespace.LinearMipmapLinearFilter : sharp ? THREE51__namespace.NearestFilter : THREE51__namespace.LinearFilter;
8480
8564
  texture.needsUpdate = true;
8481
8565
  }
8482
8566
  material.needsUpdate = true;
@@ -8487,7 +8571,7 @@ function assignMaterialTexture(material, rawKey, url, textureCache) {
8487
8571
  const key = resolveMaterialPropertyKey(rawKey);
8488
8572
  let texture = textureCache.get(url);
8489
8573
  if (!texture) {
8490
- texture = new THREE39__namespace.TextureLoader().load(url);
8574
+ texture = new THREE51__namespace.TextureLoader().load(url);
8491
8575
  textureCache.set(url, texture);
8492
8576
  }
8493
8577
  target[key] = texture;
@@ -8525,7 +8609,7 @@ function applyMaterialState(material, value, textureCache) {
8525
8609
  }
8526
8610
  target.side = value.side;
8527
8611
  target.blending = value.blending;
8528
- target.premultipliedAlpha = value.blending === THREE39__namespace.MultiplyBlending || value.blending === THREE39__namespace.SubtractiveBlending;
8612
+ target.premultipliedAlpha = value.blending === THREE51__namespace.MultiplyBlending || value.blending === THREE51__namespace.SubtractiveBlending;
8529
8613
  target.depthTest = value.depthTest;
8530
8614
  target.depthWrite = value.depthWrite;
8531
8615
  target.wireframe = value.wireframe;
@@ -8686,12 +8770,12 @@ function resolvePrimaryMesh(object) {
8686
8770
  if (!object) {
8687
8771
  return null;
8688
8772
  }
8689
- if (object instanceof THREE39__namespace.Mesh) {
8773
+ if (object instanceof THREE51__namespace.Mesh) {
8690
8774
  return object;
8691
8775
  }
8692
8776
  let found = null;
8693
8777
  object.traverse((child) => {
8694
- if (!found && child instanceof THREE39__namespace.Mesh) {
8778
+ if (!found && child instanceof THREE51__namespace.Mesh) {
8695
8779
  found = child;
8696
8780
  }
8697
8781
  });
@@ -8712,22 +8796,22 @@ function applyGeometry(component) {
8712
8796
  switch (component.kind) {
8713
8797
  case "cube": {
8714
8798
  const params = sanitized;
8715
- nextGeometry = new THREE39__namespace.BoxGeometry(params.width, params.height, params.depth);
8799
+ nextGeometry = new THREE51__namespace.BoxGeometry(params.width, params.height, params.depth);
8716
8800
  break;
8717
8801
  }
8718
8802
  case "plane": {
8719
8803
  const params = sanitized;
8720
- nextGeometry = new THREE39__namespace.PlaneGeometry(params.width, params.height);
8804
+ nextGeometry = new THREE51__namespace.PlaneGeometry(params.width, params.height);
8721
8805
  break;
8722
8806
  }
8723
8807
  case "sphere": {
8724
8808
  const params = sanitized;
8725
- nextGeometry = new THREE39__namespace.SphereGeometry(params.radius, 24, 24);
8809
+ nextGeometry = new THREE51__namespace.SphereGeometry(params.radius, 24, 24);
8726
8810
  break;
8727
8811
  }
8728
8812
  case "cylinder": {
8729
8813
  const params = sanitized;
8730
- nextGeometry = new THREE39__namespace.CylinderGeometry(
8814
+ nextGeometry = new THREE51__namespace.CylinderGeometry(
8731
8815
  params.radius,
8732
8816
  params.radius,
8733
8817
  params.height,
@@ -8737,7 +8821,7 @@ function applyGeometry(component) {
8737
8821
  }
8738
8822
  case "capsule": {
8739
8823
  const params = sanitized;
8740
- nextGeometry = new THREE39__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
8824
+ nextGeometry = new THREE51__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
8741
8825
  break;
8742
8826
  }
8743
8827
  default:
@@ -8753,7 +8837,7 @@ function applyShadowFlags(component) {
8753
8837
  return;
8754
8838
  }
8755
8839
  component.object.traverse((child) => {
8756
- if (child instanceof THREE39__namespace.Mesh) {
8840
+ if (child instanceof THREE51__namespace.Mesh) {
8757
8841
  child.castShadow = component.castShadow;
8758
8842
  child.receiveShadow = component.receiveShadow;
8759
8843
  }
@@ -8836,35 +8920,35 @@ function buildGeometry(component) {
8836
8920
  switch (component.meshKind) {
8837
8921
  case "cube": {
8838
8922
  const params = p;
8839
- return new THREE39__namespace.BoxGeometry(params.width, params.height, params.depth);
8923
+ return new THREE51__namespace.BoxGeometry(params.width, params.height, params.depth);
8840
8924
  }
8841
8925
  case "plane": {
8842
8926
  const params = p;
8843
- return new THREE39__namespace.PlaneGeometry(params.width, params.height);
8927
+ return new THREE51__namespace.PlaneGeometry(params.width, params.height);
8844
8928
  }
8845
8929
  case "sphere": {
8846
8930
  const params = p;
8847
- return new THREE39__namespace.SphereGeometry(params.radius, 24, 24);
8931
+ return new THREE51__namespace.SphereGeometry(params.radius, 24, 24);
8848
8932
  }
8849
8933
  case "cylinder": {
8850
8934
  const params = p;
8851
- return new THREE39__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
8935
+ return new THREE51__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
8852
8936
  }
8853
8937
  case "capsule": {
8854
8938
  const params = p;
8855
- return new THREE39__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
8939
+ return new THREE51__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
8856
8940
  }
8857
8941
  default:
8858
- return new THREE39__namespace.BoxGeometry(1, 1, 1);
8942
+ return new THREE51__namespace.BoxGeometry(1, 1, 1);
8859
8943
  }
8860
8944
  }
8861
8945
  __name(buildGeometry, "buildGeometry");
8862
- var _position = new THREE39__namespace.Vector3();
8863
- var _quaternion = new THREE39__namespace.Quaternion();
8864
- var _euler = new THREE39__namespace.Euler();
8865
- var _scale = new THREE39__namespace.Vector3();
8866
- var _matrix = new THREE39__namespace.Matrix4();
8867
- var _modelMatrix = new THREE39__namespace.Matrix4();
8946
+ var _position = new THREE51__namespace.Vector3();
8947
+ var _quaternion = new THREE51__namespace.Quaternion();
8948
+ var _euler = new THREE51__namespace.Euler();
8949
+ var _scale = new THREE51__namespace.Vector3();
8950
+ var _matrix = new THREE51__namespace.Matrix4();
8951
+ var _modelMatrix = new THREE51__namespace.Matrix4();
8868
8952
  function instanceTransformFromComponent(transform, out) {
8869
8953
  _position.set(transform.position.x, transform.position.y, transform.position.z);
8870
8954
  _euler.set(transform.rotation.x, transform.rotation.y, transform.rotation.z, "XYZ");
@@ -9017,7 +9101,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
9017
9101
  const sourceMaterial = Array.isArray(meshChild.material) ? meshChild.material.map((m) => m.clone()) : meshChild.material.clone();
9018
9102
  meshChild.updateWorldMatrix(true, false);
9019
9103
  const localMatrix = meshChild.matrixWorld.clone();
9020
- const instancedMesh2 = new THREE39__namespace.InstancedMesh(
9104
+ const instancedMesh2 = new THREE51__namespace.InstancedMesh(
9021
9105
  geometry2,
9022
9106
  Array.isArray(sourceMaterial) ? sourceMaterial : sourceMaterial,
9023
9107
  count2
@@ -9042,7 +9126,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
9042
9126
  this.loadingSignatures.delete(targetEntityId);
9043
9127
  return;
9044
9128
  }
9045
- const group = new THREE39__namespace.Group();
9129
+ const group = new THREE51__namespace.Group();
9046
9130
  for (const entry of entries) {
9047
9131
  group.add(entry.instancedMesh);
9048
9132
  }
@@ -9060,8 +9144,8 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
9060
9144
  }
9061
9145
  const count = instanceCount;
9062
9146
  const geometry = buildGeometry(comp);
9063
- const material = new THREE39__namespace.MeshStandardMaterial({ color: "#ffffff" });
9064
- const instancedMesh = new THREE39__namespace.InstancedMesh(geometry, material, count);
9147
+ const material = new THREE51__namespace.MeshStandardMaterial({ color: "#ffffff" });
9148
+ const instancedMesh = new THREE51__namespace.InstancedMesh(geometry, material, count);
9065
9149
  instancedMesh.castShadow = comp.castShadow;
9066
9150
  instancedMesh.receiveShadow = comp.receiveShadow;
9067
9151
  for (let i = 0; i < count; i++) {
@@ -9244,7 +9328,7 @@ var _Behaviour = class _Behaviour {
9244
9328
  if (!this.threeRef) {
9245
9329
  return null;
9246
9330
  }
9247
- const position = new THREE39__namespace.Vector3();
9331
+ const position = new THREE51__namespace.Vector3();
9248
9332
  this.threeRef.object.getWorldPosition(position);
9249
9333
  return position;
9250
9334
  }
@@ -9256,7 +9340,7 @@ var _Behaviour = class _Behaviour {
9256
9340
  if (!this.threeRef) {
9257
9341
  return null;
9258
9342
  }
9259
- const target = new THREE39__namespace.Vector3();
9343
+ const target = new THREE51__namespace.Vector3();
9260
9344
  this.threeRef.object.getWorldDirection(target);
9261
9345
  return target;
9262
9346
  }
@@ -9318,7 +9402,7 @@ var _Behaviour = class _Behaviour {
9318
9402
  if (!this.threeRef) {
9319
9403
  return;
9320
9404
  }
9321
- const rotation = new THREE39__namespace.Quaternion();
9405
+ const rotation = new THREE51__namespace.Quaternion();
9322
9406
  this.threeRef.object.getWorldQuaternion(rotation);
9323
9407
  rotation.slerp(targetRotation, alpha);
9324
9408
  this.transform.setRotation(rotation);
@@ -9335,7 +9419,7 @@ var _Behaviour = class _Behaviour {
9335
9419
  return;
9336
9420
  }
9337
9421
  const clone3 = this.threeRef.object.clone();
9338
- const rotation = new THREE39__namespace.Quaternion();
9422
+ const rotation = new THREE51__namespace.Quaternion();
9339
9423
  clone3.lookAt(targetPosition);
9340
9424
  clone3.getWorldQuaternion(rotation);
9341
9425
  this.transform.setRotation(rotation);
@@ -9473,6 +9557,17 @@ var _Behaviour = class _Behaviour {
9473
9557
  *
9474
9558
  * @param input - The source doing the grabbing.
9475
9559
  */
9560
+ /**
9561
+ * A SECOND hand joined a two-hand grab on this entity.
9562
+ *
9563
+ * The first hand anchors the object and keeps holding it; this fires for the
9564
+ * hand that came second — the foregrip on a rifle, say.
9565
+ */
9566
+ onSecondaryGrab(_input) {
9567
+ }
9568
+ /** The second hand let go, leaving the object in the first hand alone. */
9569
+ onSecondaryRelease(_input) {
9570
+ }
9476
9571
  /**
9477
9572
  * A hand pose declared on this entity started being held.
9478
9573
  *
@@ -9690,8 +9785,8 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
9690
9785
  }, "onPointerUp");
9691
9786
  this.moduleLoader = moduleLoader ?? defaultModuleLoader;
9692
9787
  this.selectionCtx = selectionContext ?? null;
9693
- this.raycaster = new THREE39__namespace.Raycaster();
9694
- this.ndcPointer = new THREE39__namespace.Vector2();
9788
+ this.raycaster = new THREE51__namespace.Raycaster();
9789
+ this.ndcPointer = new THREE51__namespace.Vector2();
9695
9790
  if (this.selectionCtx) {
9696
9791
  this.selectionCtx.domElement.addEventListener("pointerdown", this.onPointerDown);
9697
9792
  this.selectionCtx.domElement.addEventListener("pointerup", this.onPointerUp);
@@ -10070,7 +10165,7 @@ function disposeBuiltObject(root) {
10070
10165
  for (const mat of materials) {
10071
10166
  if (mat.userData?.[SHARED_MODEL_RESOURCE_FLAG]) continue;
10072
10167
  for (const value of Object.values(mat)) {
10073
- if (value instanceof THREE39__namespace.Texture) value.dispose();
10168
+ if (value instanceof THREE51__namespace.Texture) value.dispose();
10074
10169
  }
10075
10170
  mat.dispose?.();
10076
10171
  }
@@ -10434,7 +10529,7 @@ var _AudioSystem = class _AudioSystem {
10434
10529
  if (!prev || prev.volume !== opts.volume) {
10435
10530
  sound.setVolume(opts.volume);
10436
10531
  }
10437
- if (opts.positional && sound instanceof THREE39__namespace.PositionalAudio) {
10532
+ if (opts.positional && sound instanceof THREE51__namespace.PositionalAudio) {
10438
10533
  if (!prev || prev.refDistance !== opts.refDistance) {
10439
10534
  sound.setRefDistance(opts.refDistance);
10440
10535
  }
@@ -10560,7 +10655,7 @@ var _VideoSystem = class _VideoSystem {
10560
10655
  component.object.scale.set(opts.width, opts.height, 1);
10561
10656
  }
10562
10657
  const material = component.material;
10563
- if (material instanceof THREE39__namespace.ShaderMaterial) {
10658
+ if (material instanceof THREE51__namespace.ShaderMaterial) {
10564
10659
  if (!prev || prev.backgroundColor !== opts.backgroundColor) {
10565
10660
  material.uniforms.keyColor.value.set(opts.backgroundColor);
10566
10661
  }
@@ -10574,7 +10669,7 @@ var _VideoSystem = class _VideoSystem {
10574
10669
  material.uniforms.spill.value = opts.spill;
10575
10670
  }
10576
10671
  }
10577
- if (opts.positional && component.audioNode instanceof THREE39__namespace.PositionalAudio) {
10672
+ if (opts.positional && component.audioNode instanceof THREE51__namespace.PositionalAudio) {
10578
10673
  const posAudio = component.audioNode;
10579
10674
  if (!prev || prev.refDistance !== opts.refDistance) {
10580
10675
  posAudio.setRefDistance(opts.refDistance);
@@ -10614,8 +10709,8 @@ function blankState(source) {
10614
10709
  handedness: handednessOf(source),
10615
10710
  kind: kindOf(source),
10616
10711
  connected: true,
10617
- rayOrigin: new THREE39__namespace.Vector3(),
10618
- rayDirection: new THREE39__namespace.Vector3(0, 0, -1),
10712
+ rayOrigin: new THREE51__namespace.Vector3(),
10713
+ rayDirection: new THREE51__namespace.Vector3(0, 0, -1),
10619
10714
  gripPosition: null,
10620
10715
  gripQuaternion: null,
10621
10716
  select: false,
@@ -10641,7 +10736,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10641
10736
  }
10642
10737
  if (spaces.length === 0) return null;
10643
10738
  const target = existing ?? {};
10644
- const matrix = new THREE39__namespace.Matrix4();
10739
+ const matrix = new THREE51__namespace.Matrix4();
10645
10740
  const buffer = spaces.length * 16 <= JOINT_MATRIX_BUFFER.length ? JOINT_MATRIX_BUFFER : new Float32Array(spaces.length * 16);
10646
10741
  const fillPoses = frame.fillPoses;
10647
10742
  if (typeof fillPoses === "function") {
@@ -10654,7 +10749,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10654
10749
  if (filled) {
10655
10750
  for (let i = 0; i < names.length; i += 1) {
10656
10751
  matrix.fromArray(buffer, i * 16).premultiply(rigMatrix);
10657
- const vector = target[names[i]] ?? new THREE39__namespace.Vector3();
10752
+ const vector = target[names[i]] ?? new THREE51__namespace.Vector3();
10658
10753
  vector.setFromMatrixPosition(matrix);
10659
10754
  target[names[i]] = vector;
10660
10755
  }
@@ -10672,7 +10767,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10672
10767
  continue;
10673
10768
  }
10674
10769
  matrix.fromArray(pose.transform.matrix).premultiply(rigMatrix);
10675
- const vector = target[names[i]] ?? new THREE39__namespace.Vector3();
10770
+ const vector = target[names[i]] ?? new THREE51__namespace.Vector3();
10676
10771
  vector.setFromMatrixPosition(matrix);
10677
10772
  target[names[i]] = vector;
10678
10773
  anyTracked = true;
@@ -10684,7 +10779,7 @@ var _XRInputSystem = class _XRInputSystem {
10684
10779
  constructor() {
10685
10780
  this.records = /* @__PURE__ */ new Map();
10686
10781
  /** Reference-space → world matrix, rebuilt each frame from the XR rig. */
10687
- this.referenceToWorld = new THREE39__namespace.Matrix4();
10782
+ this.referenceToWorld = new THREE51__namespace.Matrix4();
10688
10783
  this.session = null;
10689
10784
  this.onSelectStart = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectStart"), "onSelectStart");
10690
10785
  this.onSelectEnd = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectEnd"), "onSelectEnd");
@@ -10782,19 +10877,19 @@ var _XRInputSystem = class _XRInputSystem {
10782
10877
  if (frame && referenceSpace && source.targetRaySpace) {
10783
10878
  const pose = frame.getPose(source.targetRaySpace, referenceSpace);
10784
10879
  if (pose) {
10785
- const m = new THREE39__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
10880
+ const m = new THREE51__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
10786
10881
  state.rayOrigin.setFromMatrixPosition(m);
10787
10882
  state.rayDirection.set(0, 0, -1).applyQuaternion(
10788
- new THREE39__namespace.Quaternion().setFromRotationMatrix(m)
10883
+ new THREE51__namespace.Quaternion().setFromRotationMatrix(m)
10789
10884
  ).normalize();
10790
10885
  }
10791
10886
  }
10792
10887
  if (frame && referenceSpace && source.gripSpace) {
10793
10888
  const gripPose = frame.getPose(source.gripSpace, referenceSpace);
10794
10889
  if (gripPose) {
10795
- const m = new THREE39__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
10796
- state.gripPosition ?? (state.gripPosition = new THREE39__namespace.Vector3());
10797
- state.gripQuaternion ?? (state.gripQuaternion = new THREE39__namespace.Quaternion());
10890
+ const m = new THREE51__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
10891
+ state.gripPosition ?? (state.gripPosition = new THREE51__namespace.Vector3());
10892
+ state.gripQuaternion ?? (state.gripQuaternion = new THREE51__namespace.Quaternion());
10798
10893
  state.gripPosition.setFromMatrixPosition(m);
10799
10894
  state.gripQuaternion.setFromRotationMatrix(m);
10800
10895
  }
@@ -10903,15 +10998,15 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
10903
10998
  const fresh = this.samples.filter(
10904
10999
  (sample) => now - sample.time <= MAX_SAMPLE_AGE_SECONDS
10905
11000
  );
10906
- if (fresh.length < 2) return new THREE39__namespace.Vector3();
10907
- let best = new THREE39__namespace.Vector3();
11001
+ if (fresh.length < 2) return new THREE51__namespace.Vector3();
11002
+ let best = new THREE51__namespace.Vector3();
10908
11003
  let bestSpeed = 0;
10909
11004
  for (let i = 1; i < fresh.length; i += 1) {
10910
11005
  const previous = fresh[i - 1];
10911
11006
  const current = fresh[i];
10912
11007
  const dt = current.time - previous.time;
10913
11008
  if (dt <= 1e-5) continue;
10914
- const velocity = new THREE39__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
11009
+ const velocity = new THREE51__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
10915
11010
  const speed = velocity.length();
10916
11011
  if (speed > MAX_PLAUSIBLE_SPEED) continue;
10917
11012
  if (speed > bestSpeed) {
@@ -10928,25 +11023,79 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
10928
11023
  __name(_ThrowVelocityTracker, "ThrowVelocityTracker");
10929
11024
  var ThrowVelocityTracker = _ThrowVelocityTracker;
10930
11025
  function angularVelocityFrom(previous, current, deltaTime) {
10931
- if (deltaTime <= 1e-5) return new THREE39__namespace.Vector3();
11026
+ if (deltaTime <= 1e-5) return new THREE51__namespace.Vector3();
10932
11027
  const delta = current.clone().multiply(previous.clone().invert());
10933
11028
  if (delta.w < 0) {
10934
11029
  delta.set(-delta.x, -delta.y, -delta.z, -delta.w);
10935
11030
  }
10936
- const angle = 2 * Math.acos(THREE39__namespace.MathUtils.clamp(delta.w, -1, 1));
11031
+ const angle = 2 * Math.acos(THREE51__namespace.MathUtils.clamp(delta.w, -1, 1));
10937
11032
  const sinHalf = Math.sqrt(Math.max(0, 1 - delta.w * delta.w));
10938
- if (sinHalf < 1e-6 || angle < 1e-6) return new THREE39__namespace.Vector3();
10939
- return new THREE39__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
11033
+ if (sinHalf < 1e-6 || angle < 1e-6) return new THREE51__namespace.Vector3();
11034
+ return new THREE51__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
10940
11035
  }
10941
11036
  __name(angularVelocityFrom, "angularVelocityFrom");
11037
+ var MIN_HAND_SEPARATION = 0.06;
11038
+ var MIN_ANCHOR_SEPARATION = 1e-3;
11039
+ var SCALE_DEADZONE = 0.05;
11040
+ function solveTwoHandTransform(primary, secondary, basis, options) {
11041
+ const separation = primary.position.distanceTo(secondary.position);
11042
+ const anchorDelta = new THREE51__namespace.Vector3().subVectors(
11043
+ basis.secondaryLocal,
11044
+ basis.primaryLocal
11045
+ );
11046
+ const anchorLength = anchorDelta.length();
11047
+ let factor = 1;
11048
+ if (options.allowScale && basis.initialSeparation > MIN_ANCHOR_SEPARATION && separation > MIN_ANCHOR_SEPARATION) {
11049
+ factor = separation / basis.initialSeparation;
11050
+ if (Math.abs(factor - 1) <= SCALE_DEADZONE) {
11051
+ factor = 1;
11052
+ } else {
11053
+ factor += factor > 1 ? -SCALE_DEADZONE : SCALE_DEADZONE;
11054
+ }
11055
+ factor = Math.min(options.maxScale, Math.max(options.minScale, factor));
11056
+ }
11057
+ const scale = basis.initialScale.clone().multiplyScalar(factor);
11058
+ const degenerate = separation < MIN_HAND_SEPARATION || anchorLength < MIN_ANCHOR_SEPARATION;
11059
+ const base = primary.quaternion.clone().multiply(basis.primaryHandOffset);
11060
+ let quaternion;
11061
+ if (options.rotationMode === "primary" || degenerate) {
11062
+ quaternion = base;
11063
+ } else {
11064
+ const axisLocal = anchorDelta.clone().normalize();
11065
+ const currentAxis = axisLocal.clone().applyQuaternion(base).normalize();
11066
+ const desiredAxis = new THREE51__namespace.Vector3().subVectors(secondary.position, primary.position).normalize();
11067
+ const correction = new THREE51__namespace.Quaternion().setFromUnitVectors(
11068
+ currentAxis,
11069
+ desiredAxis
11070
+ );
11071
+ quaternion = correction.multiply(base);
11072
+ }
11073
+ const anchorOffset = basis.primaryLocal.clone().multiply(scale).applyQuaternion(quaternion);
11074
+ const position = primary.position.clone().sub(anchorOffset);
11075
+ return { position, quaternion, scale, degenerate };
11076
+ }
11077
+ __name(solveTwoHandTransform, "solveTwoHandTransform");
10942
11078
 
10943
11079
  // core/systems/XRInteractionSystem.ts
10944
11080
  var RAYLESS_HOLD_DISTANCE = 0.5;
11081
+ function quatFromEulerVec(r) {
11082
+ return new THREE51__namespace.Quaternion().setFromEuler(new THREE51__namespace.Euler(r.x, r.y, r.z));
11083
+ }
11084
+ __name(quatFromEulerVec, "quatFromEulerVec");
11085
+ function writeEuler(target, q) {
11086
+ const euler = new THREE51__namespace.Euler().setFromQuaternion(q);
11087
+ target.x = euler.x;
11088
+ target.y = euler.y;
11089
+ target.z = euler.z;
11090
+ }
11091
+ __name(writeEuler, "writeEuler");
10945
11092
  var _XRInteractionSystem = class _XRInteractionSystem {
10946
11093
  constructor(inputSystem) {
10947
- this.raycaster = new THREE39__namespace.Raycaster();
11094
+ this.raycaster = new THREE51__namespace.Raycaster();
10948
11095
  this.grabs = /* @__PURE__ */ new Map();
10949
11096
  this.hoveredEntityId = null;
11097
+ /** Per-hand hover, with the hit distance so the ray can stop at the surface. */
11098
+ this.hovers = /* @__PURE__ */ new Map();
10950
11099
  /** Monotonic seconds, accumulated from deltaTime for velocity sampling. */
10951
11100
  this.elapsed = 0;
10952
11101
  this.inputSystem = inputSystem;
@@ -10960,6 +11109,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10960
11109
  const inputs = this.inputSystem.getInputs();
10961
11110
  const interactables = this.collectInteractables(world);
10962
11111
  this.hoveredEntityId = null;
11112
+ this.hovers.clear();
10963
11113
  for (const input of inputs) {
10964
11114
  if (!input.connected) {
10965
11115
  this.release(world, input);
@@ -10967,7 +11117,6 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10967
11117
  }
10968
11118
  const held = this.grabs.get(input);
10969
11119
  if (held) {
10970
- this.moveHeld(world, input, held, deltaTime);
10971
11120
  if (this.wantsRelease(world, input, held)) {
10972
11121
  this.release(world, input);
10973
11122
  }
@@ -10978,6 +11127,10 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10978
11127
  if (this.hoveredEntityId === null) {
10979
11128
  this.hoveredEntityId = candidate.entityId;
10980
11129
  }
11130
+ this.hovers.set(input, {
11131
+ entityId: candidate.entityId,
11132
+ distance: candidate.distance
11133
+ });
10981
11134
  const interactable = world.getComponent(
10982
11135
  candidate.entityId,
10983
11136
  XRInteractableComponent
@@ -10988,9 +11141,122 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10988
11141
  /* starting */
10989
11142
  true
10990
11143
  )) {
10991
- this.grab(world, input, candidate.entityId, candidate.viaDistance);
11144
+ this.tryGrab(world, input, candidate.entityId, candidate.viaDistance);
10992
11145
  }
10993
11146
  }
11147
+ this.moveAllHeld(world, deltaTime);
11148
+ }
11149
+ /** Hands currently holding an entity. */
11150
+ holdersOf(entityId) {
11151
+ const holders = [];
11152
+ for (const [input, record] of this.grabs) {
11153
+ if (record.entityId === entityId) holders.push({ input, record });
11154
+ }
11155
+ return holders;
11156
+ }
11157
+ /**
11158
+ * Route a new grab: first hand, hand-off, or second hand.
11159
+ *
11160
+ * Which of those happens is the object's choice (`grabHands`), not the
11161
+ * hand's — a ball should pass between hands, a rifle should be held by both,
11162
+ * and a lever should stay with whoever grabbed it first.
11163
+ */
11164
+ tryGrab(world, input, entityId, viaDistance) {
11165
+ const interactable = world.getComponent(entityId, XRInteractableComponent);
11166
+ if (!interactable) return;
11167
+ const holders = this.holdersOf(entityId);
11168
+ if (holders.length === 0) {
11169
+ this.grab(world, input, entityId, viaDistance, "primary");
11170
+ return;
11171
+ }
11172
+ if (holders.some((holder) => holder.input === input)) return;
11173
+ if (interactable.grabHands === "single") return;
11174
+ if (interactable.grabHands === "handOff") {
11175
+ const previous = holders[0];
11176
+ this.grabs.delete(previous.input);
11177
+ const record = previous.record;
11178
+ record.role = "primary";
11179
+ record.twoHand = null;
11180
+ this.reanchor(world, input, record, interactable);
11181
+ this.grabs.set(input, record);
11182
+ this.dispatch(world, entityId, (b) => b.onGrab(input));
11183
+ return;
11184
+ }
11185
+ if (holders.length >= 2) return;
11186
+ this.grab(world, input, entityId, viaDistance, "secondary");
11187
+ this.beginTwoHand(world, entityId);
11188
+ }
11189
+ /**
11190
+ * Re-derive a record's hold from where the object is NOW.
11191
+ *
11192
+ * Used on hand-off and on promotion after the primary lets go. Without it the
11193
+ * object snaps to whatever offset the previous hand had, which reads as the
11194
+ * object jumping out of your grip at the moment you take it.
11195
+ */
11196
+ reanchor(world, input, record, interactable) {
11197
+ const transform = world.getComponent(record.entityId, TransformComponent);
11198
+ if (!transform) return;
11199
+ const hand = this.handPosition(input);
11200
+ const handQuaternion = this.handQuaternion(input);
11201
+ const objectQuaternion = quatFromEulerVec(transform.rotation);
11202
+ record.offset = new THREE51__namespace.Vector3(
11203
+ transform.position.x - hand.x,
11204
+ transform.position.y - hand.y,
11205
+ transform.position.z - hand.z
11206
+ ).applyQuaternion(handQuaternion.clone().invert());
11207
+ record.handRotationOffset = handQuaternion.clone().invert().multiply(objectQuaternion);
11208
+ record.remote = false;
11209
+ record.tracker.clear();
11210
+ record.tracker.record(hand, this.elapsed);
11211
+ record.lastHandQuaternion = handQuaternion.clone();
11212
+ if (record.attachLocal && interactable.attachPoints.length === 0) {
11213
+ record.attachLocal = null;
11214
+ record.attachQuaternion = null;
11215
+ record.attachPointId = null;
11216
+ }
11217
+ }
11218
+ /** Capture the two-hand basis once both hands are on the object. */
11219
+ beginTwoHand(world, entityId) {
11220
+ const holders = this.holdersOf(entityId);
11221
+ if (holders.length !== 2) return;
11222
+ const primary = holders.find((h) => h.record.role === "primary");
11223
+ const secondary = holders.find((h) => h.record.role === "secondary");
11224
+ if (!primary || !secondary) return;
11225
+ const transform = world.getComponent(entityId, TransformComponent);
11226
+ if (!transform) return;
11227
+ const primaryHand = this.handPosition(primary.input);
11228
+ const secondaryHand = this.handPosition(secondary.input);
11229
+ const primaryLocal = primary.record.attachLocal?.clone() ?? this.objectLocalPoint(transform, primaryHand);
11230
+ const secondaryLocal = secondary.record.attachLocal?.clone() ?? this.objectLocalPoint(transform, secondaryHand);
11231
+ primary.record.twoHand = {
11232
+ primaryLocal,
11233
+ secondaryLocal,
11234
+ initialScale: new THREE51__namespace.Vector3(
11235
+ transform.scale.x,
11236
+ transform.scale.y,
11237
+ transform.scale.z
11238
+ ),
11239
+ initialSeparation: primaryHand.distanceTo(secondaryHand),
11240
+ // How the object sits relative to the anchoring hand right now, so the
11241
+ // two-hand solve picks up exactly where the one-hand hold left off.
11242
+ primaryHandOffset: this.handQuaternion(primary.input).invert().multiply(quatFromEulerVec(transform.rotation)),
11243
+ initialQuaternion: quatFromEulerVec(transform.rotation)
11244
+ };
11245
+ this.dispatch(world, entityId, (b) => b.onSecondaryGrab(secondary.input));
11246
+ }
11247
+ /** World point → the object's UNSCALED local space. */
11248
+ objectLocalPoint(transform, worldPoint) {
11249
+ const local = worldPoint.clone().sub(
11250
+ new THREE51__namespace.Vector3(
11251
+ transform.position.x,
11252
+ transform.position.y,
11253
+ transform.position.z
11254
+ )
11255
+ ).applyQuaternion(quatFromEulerVec(transform.rotation).invert());
11256
+ local.x /= transform.scale.x || 1;
11257
+ local.y /= transform.scale.y || 1;
11258
+ local.z /= transform.scale.z || 1;
11259
+ return local;
10994
11260
  }
10995
11261
  /** Whether the grab gesture for this object is currently starting. */
10996
11262
  gestureMatches(input, interactable, starting) {
@@ -11039,7 +11305,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11039
11305
  );
11040
11306
  if (!interactable2) continue;
11041
11307
  if (interactable2.grabReach === "distance") continue;
11042
- const position = new THREE39__namespace.Vector3();
11308
+ const position = new THREE51__namespace.Vector3();
11043
11309
  entry.object.getWorldPosition(position);
11044
11310
  const distance = position.distanceTo(hand);
11045
11311
  if (distance <= interactable2.grabRadius && distance < nearestDistance) {
@@ -11048,13 +11314,22 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11048
11314
  }
11049
11315
  }
11050
11316
  if (nearestId !== null) {
11051
- return { entityId: nearestId, viaDistance: false };
11317
+ return {
11318
+ entityId: nearestId,
11319
+ viaDistance: false,
11320
+ distance: nearestDistance
11321
+ };
11052
11322
  }
11053
- const hitId = this.raycast(input, interactables);
11054
- if (hitId === null) return null;
11323
+ const hit = this.raycast(input, interactables);
11324
+ if (hit === null) return null;
11325
+ const hitId = hit.entityId;
11055
11326
  const interactable = world.getComponent(hitId, XRInteractableComponent);
11056
11327
  const viaDistanceAllowed = interactable?.grabReach === "distance" || interactable?.grabReach === "both";
11057
- return { entityId: hitId, viaDistance: viaDistanceAllowed };
11328
+ return {
11329
+ entityId: hitId,
11330
+ viaDistance: viaDistanceAllowed,
11331
+ distance: hit.distance
11332
+ };
11058
11333
  }
11059
11334
  collectInteractables(world) {
11060
11335
  const out = [];
@@ -11085,9 +11360,54 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11085
11360
  nearestId = entry.entityId;
11086
11361
  }
11087
11362
  }
11088
- return nearestId;
11363
+ return nearestId === null ? null : { entityId: nearestId, distance: nearestDistance };
11089
11364
  }
11090
- grab(world, input, entityId, viaDistance) {
11365
+ /**
11366
+ * Pick the authored hold this hand should take.
11367
+ *
11368
+ * Filters by hand and by role — that is what makes "the trigger hand goes on
11369
+ * the grip" hold regardless of which hand reaches first — then takes the
11370
+ * nearest remaining one so grabbing near the foregrip picks the foregrip.
11371
+ */
11372
+ chooseAttachPoint(world, input, entityId, interactable, role) {
11373
+ if (interactable.attachPoints.length === 0) return null;
11374
+ const transform = world.getComponent(entityId, TransformComponent);
11375
+ if (!transform) return null;
11376
+ const taken = new Set(
11377
+ this.holdersOf(entityId).map((holder) => holder.record.attachPointId).filter((id) => id !== null)
11378
+ );
11379
+ const hand = this.handPosition(input);
11380
+ const objectPosition = new THREE51__namespace.Vector3(
11381
+ transform.position.x,
11382
+ transform.position.y,
11383
+ transform.position.z
11384
+ );
11385
+ const objectQuaternion = quatFromEulerVec(transform.rotation);
11386
+ const scale = new THREE51__namespace.Vector3(
11387
+ transform.scale.x,
11388
+ transform.scale.y,
11389
+ transform.scale.z
11390
+ );
11391
+ let best = null;
11392
+ let bestDistance = Number.POSITIVE_INFINITY;
11393
+ for (const point of interactable.attachPoints) {
11394
+ if (taken.has(point.id)) continue;
11395
+ if (point.hand !== "any" && point.hand !== input.handedness) continue;
11396
+ if (point.role !== "any" && point.role !== role) continue;
11397
+ const world3 = new THREE51__namespace.Vector3(
11398
+ point.position.x,
11399
+ point.position.y,
11400
+ point.position.z
11401
+ ).multiply(scale).applyQuaternion(objectQuaternion).add(objectPosition);
11402
+ const distance = world3.distanceTo(hand);
11403
+ if (distance < bestDistance) {
11404
+ bestDistance = distance;
11405
+ best = point;
11406
+ }
11407
+ }
11408
+ return best;
11409
+ }
11410
+ grab(world, input, entityId, viaDistance, role) {
11091
11411
  const transform = world.getComponent(entityId, TransformComponent);
11092
11412
  const interactable = world.getComponent(entityId, XRInteractableComponent);
11093
11413
  if (!transform || !interactable) return;
@@ -11097,25 +11417,25 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11097
11417
  let offset;
11098
11418
  let remoteDistance = 0;
11099
11419
  if (remote) {
11100
- const position = new THREE39__namespace.Vector3(
11420
+ const position = new THREE51__namespace.Vector3(
11101
11421
  transform.position.x,
11102
11422
  transform.position.y,
11103
11423
  transform.position.z
11104
11424
  );
11105
11425
  remoteDistance = position.distanceTo(input.rayOrigin);
11106
- offset = new THREE39__namespace.Vector3();
11426
+ offset = new THREE51__namespace.Vector3();
11107
11427
  } else if (viaDistance) {
11108
- offset = new THREE39__namespace.Vector3(
11428
+ offset = new THREE51__namespace.Vector3(
11109
11429
  interactable.grabOffsetPosition.x,
11110
11430
  interactable.grabOffsetPosition.y,
11111
11431
  interactable.grabOffsetPosition.z
11112
11432
  );
11113
11433
  } else {
11114
- offset = new THREE39__namespace.Vector3(
11434
+ offset = new THREE51__namespace.Vector3(
11115
11435
  transform.position.x - hand.x,
11116
11436
  transform.position.y - hand.y,
11117
11437
  transform.position.z - hand.z
11118
- );
11438
+ ).applyQuaternion(handQuaternion.clone().invert());
11119
11439
  }
11120
11440
  const physics = world.getComponent(entityId, PhysicsComponent);
11121
11441
  let restoreBodyType = null;
@@ -11125,11 +11445,30 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11125
11445
  }
11126
11446
  const tracker = new ThrowVelocityTracker();
11127
11447
  tracker.record(hand, this.elapsed);
11448
+ const attachPoint = this.chooseAttachPoint(
11449
+ world,
11450
+ input,
11451
+ entityId,
11452
+ interactable,
11453
+ role
11454
+ );
11455
+ const objectQuaternion = quatFromEulerVec(transform.rotation);
11456
+ const handRotationOffset = handQuaternion.clone().invert().multiply(objectQuaternion);
11128
11457
  this.grabs.set(input, {
11129
11458
  entityId,
11459
+ role,
11460
+ attachPointId: attachPoint?.id ?? null,
11461
+ attachLocal: attachPoint ? new THREE51__namespace.Vector3(
11462
+ attachPoint.position.x,
11463
+ attachPoint.position.y,
11464
+ attachPoint.position.z
11465
+ ) : null,
11466
+ attachQuaternion: attachPoint ? quatFromEulerVec(attachPoint.rotation) : null,
11467
+ handRotationOffset,
11468
+ twoHand: null,
11130
11469
  offset,
11131
- rotationOffset: new THREE39__namespace.Quaternion().setFromEuler(
11132
- new THREE39__namespace.Euler(
11470
+ rotationOffset: new THREE51__namespace.Quaternion().setFromEuler(
11471
+ new THREE51__namespace.Euler(
11133
11472
  interactable.grabOffsetRotation.x,
11134
11473
  interactable.grabOffsetRotation.y,
11135
11474
  interactable.grabOffsetRotation.z
@@ -11140,33 +11479,111 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11140
11479
  restoreBodyType,
11141
11480
  tracker,
11142
11481
  lastHandQuaternion: handQuaternion.clone(),
11143
- angularVelocity: new THREE39__namespace.Vector3()
11482
+ angularVelocity: new THREE51__namespace.Vector3()
11144
11483
  });
11145
- this.dispatch(world, entityId, (b) => b.onGrab(input));
11484
+ if (role === "primary") {
11485
+ this.dispatch(world, entityId, (b) => b.onGrab(input));
11486
+ }
11487
+ }
11488
+ /** Drive every held object, once each, after grabs and releases settle. */
11489
+ moveAllHeld(world, deltaTime) {
11490
+ const done = /* @__PURE__ */ new Set();
11491
+ for (const [input, record] of this.grabs) {
11492
+ if (done.has(record.entityId)) continue;
11493
+ done.add(record.entityId);
11494
+ const holders = this.holdersOf(record.entityId);
11495
+ if (holders.length >= 2) {
11496
+ this.moveTwoHand(world, record.entityId, holders, deltaTime);
11497
+ } else {
11498
+ this.moveHeld(world, input, record, deltaTime);
11499
+ }
11500
+ }
11501
+ }
11502
+ /** Solve and apply a two-hand hold. */
11503
+ moveTwoHand(world, entityId, holders, deltaTime) {
11504
+ const primary = holders.find((h) => h.record.role === "primary");
11505
+ const secondary = holders.find((h) => h.record.role === "secondary");
11506
+ const transform = world.getComponent(entityId, TransformComponent);
11507
+ const interactable = world.getComponent(entityId, XRInteractableComponent);
11508
+ const basis = primary?.record.twoHand ?? null;
11509
+ if (!primary || !secondary || !transform || !interactable || !basis) {
11510
+ if (primary) {
11511
+ this.moveHeld(world, primary.input, primary.record, deltaTime);
11512
+ }
11513
+ return;
11514
+ }
11515
+ const primaryHand = this.handPosition(primary.input);
11516
+ const primaryQuaternion = this.handQuaternion(primary.input);
11517
+ const solved = solveTwoHandTransform(
11518
+ { position: primaryHand, quaternion: primaryQuaternion },
11519
+ {
11520
+ position: this.handPosition(secondary.input),
11521
+ quaternion: this.handQuaternion(secondary.input)
11522
+ },
11523
+ basis,
11524
+ {
11525
+ rotationMode: interactable.twoHandRotation,
11526
+ allowScale: interactable.allowTwoHandScale,
11527
+ minScale: interactable.minScale,
11528
+ maxScale: interactable.maxScale
11529
+ }
11530
+ );
11531
+ transform.position.x = solved.position.x;
11532
+ transform.position.y = solved.position.y;
11533
+ transform.position.z = solved.position.z;
11534
+ writeEuler(transform.rotation, solved.quaternion);
11535
+ if (interactable.allowTwoHandScale) {
11536
+ transform.scale.x = solved.scale.x;
11537
+ transform.scale.y = solved.scale.y;
11538
+ transform.scale.z = solved.scale.z;
11539
+ }
11540
+ primary.record.tracker.record(primaryHand, this.elapsed);
11541
+ primary.record.angularVelocity = angularVelocityFrom(
11542
+ primary.record.lastHandQuaternion,
11543
+ primaryQuaternion,
11544
+ deltaTime
11545
+ );
11546
+ primary.record.lastHandQuaternion = primaryQuaternion.clone();
11146
11547
  }
11147
11548
  moveHeld(world, input, held, deltaTime) {
11148
11549
  const transform = world.getComponent(held.entityId, TransformComponent);
11149
11550
  if (!transform) return;
11150
11551
  const hand = this.handPosition(input);
11552
+ const handQuaternion = this.handQuaternion(input);
11553
+ const objectQuaternion = held.attachQuaternion ? handQuaternion.clone().multiply(held.rotationOffset).multiply(held.attachQuaternion.clone().invert()) : handQuaternion.clone().multiply(held.handRotationOffset);
11151
11554
  if (held.remote) {
11152
- const target = new THREE39__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
11555
+ const target = new THREE51__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
11153
11556
  transform.position.x = target.x;
11154
11557
  transform.position.y = target.y;
11155
11558
  transform.position.z = target.z;
11559
+ writeEuler(transform.rotation, objectQuaternion);
11560
+ } else if (held.attachLocal) {
11561
+ const anchor = held.attachLocal.clone().multiply(
11562
+ new THREE51__namespace.Vector3(
11563
+ transform.scale.x,
11564
+ transform.scale.y,
11565
+ transform.scale.z
11566
+ )
11567
+ ).applyQuaternion(objectQuaternion);
11568
+ transform.position.x = hand.x - anchor.x;
11569
+ transform.position.y = hand.y - anchor.y;
11570
+ transform.position.z = hand.z - anchor.z;
11571
+ writeEuler(transform.rotation, objectQuaternion);
11156
11572
  } else {
11157
- transform.position.x = hand.x + held.offset.x;
11158
- transform.position.y = hand.y + held.offset.y;
11159
- transform.position.z = hand.z + held.offset.z;
11573
+ const carried = held.offset.clone().applyQuaternion(handQuaternion);
11574
+ transform.position.x = hand.x + carried.x;
11575
+ transform.position.y = hand.y + carried.y;
11576
+ transform.position.z = hand.z + carried.z;
11577
+ writeEuler(transform.rotation, objectQuaternion);
11160
11578
  }
11161
11579
  held.tracker.record(
11162
- held.remote ? new THREE39__namespace.Vector3(
11580
+ held.remote ? new THREE51__namespace.Vector3(
11163
11581
  transform.position.x,
11164
11582
  transform.position.y,
11165
11583
  transform.position.z
11166
11584
  ) : hand,
11167
11585
  this.elapsed
11168
11586
  );
11169
- const handQuaternion = this.handQuaternion(input);
11170
11587
  held.angularVelocity = angularVelocityFrom(
11171
11588
  held.lastHandQuaternion,
11172
11589
  handQuaternion,
@@ -11182,22 +11599,41 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11182
11599
  held.entityId,
11183
11600
  XRInteractableComponent
11184
11601
  );
11602
+ const remaining = this.holdersOf(held.entityId);
11603
+ if (remaining.length > 0 && interactable) {
11604
+ if (held.role === "secondary") {
11605
+ const primary = remaining.find((h) => h.record.role === "primary");
11606
+ if (primary) {
11607
+ primary.record.twoHand = null;
11608
+ this.reanchor(world, primary.input, primary.record, interactable);
11609
+ }
11610
+ this.dispatch(
11611
+ world,
11612
+ held.entityId,
11613
+ (b) => b.onSecondaryRelease(input)
11614
+ );
11615
+ } else {
11616
+ const next = remaining[0];
11617
+ next.record.role = "primary";
11618
+ next.record.twoHand = null;
11619
+ next.record.restoreBodyType = held.restoreBodyType;
11620
+ this.reanchor(world, next.input, next.record, interactable);
11621
+ this.dispatch(world, held.entityId, (b) => b.onRelease(input));
11622
+ }
11623
+ held.tracker.clear();
11624
+ return;
11625
+ }
11185
11626
  const physics = world.getComponent(held.entityId, PhysicsComponent);
11186
11627
  if (held.restoreBodyType !== null && physics) {
11187
11628
  physics.values.rigidbody.type = held.restoreBodyType;
11188
- const body = physics.rigidBody;
11189
11629
  const dynamic = held.restoreBodyType === "dynamic";
11190
- if (body && dynamic && interactable?.throwOnRelease) {
11630
+ if (dynamic && interactable?.throwOnRelease) {
11191
11631
  const velocity = held.tracker.computeVelocity(this.elapsed).multiplyScalar(interactable.throwStrength);
11192
- try {
11193
- body.setLinvel(
11194
- { x: velocity.x, y: velocity.y, z: velocity.z },
11195
- true
11196
- );
11197
- const spin = held.angularVelocity;
11198
- body.setAngvel({ x: spin.x, y: spin.y, z: spin.z }, true);
11199
- } catch {
11200
- }
11632
+ const spin = held.angularVelocity;
11633
+ setPhysicsLiveRef(physics, "pendingVelocity", {
11634
+ linear: { x: velocity.x, y: velocity.y, z: velocity.z },
11635
+ angular: { x: spin.x, y: spin.y, z: spin.z }
11636
+ });
11201
11637
  }
11202
11638
  }
11203
11639
  held.tracker.clear();
@@ -11215,7 +11651,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11215
11651
  */
11216
11652
  handPosition(input) {
11217
11653
  if (input.gripPosition) return input.gripPosition;
11218
- return new THREE39__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
11654
+ return new THREE51__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
11219
11655
  }
11220
11656
  /**
11221
11657
  * Hand orientation. Grip-less sources (gaze / transient-pointer) have none,
@@ -11223,8 +11659,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11223
11659
  */
11224
11660
  handQuaternion(input) {
11225
11661
  if (input.gripQuaternion) return input.gripQuaternion.clone();
11226
- return new THREE39__namespace.Quaternion().setFromUnitVectors(
11227
- new THREE39__namespace.Vector3(0, 0, -1),
11662
+ return new THREE51__namespace.Quaternion().setFromUnitVectors(
11663
+ new THREE51__namespace.Vector3(0, 0, -1),
11228
11664
  input.rayDirection.clone().normalize()
11229
11665
  );
11230
11666
  }
@@ -11245,10 +11681,57 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11245
11681
  getHoveredEntityId() {
11246
11682
  return this.hoveredEntityId;
11247
11683
  }
11684
+ /**
11685
+ * What this hand is pointing at, with the distance to it.
11686
+ *
11687
+ * The distance is what lets the pointer ray stop at the surface instead of
11688
+ * passing through the object.
11689
+ */
11690
+ getHoverFor(handedness) {
11691
+ for (const [input, hover] of this.hovers) {
11692
+ if (input.handedness === handedness) return hover;
11693
+ }
11694
+ return null;
11695
+ }
11696
+ /** Every entity under a pointer this frame — drives hover feedback. */
11697
+ getHoveredEntityIds() {
11698
+ const ids = /* @__PURE__ */ new Set();
11699
+ for (const hover of this.hovers.values()) ids.add(hover.entityId);
11700
+ return ids;
11701
+ }
11702
+ /** Entities currently held, so hover feedback can skip them. */
11703
+ getHeldEntityIds() {
11704
+ const ids = /* @__PURE__ */ new Set();
11705
+ for (const record of this.grabs.values()) ids.add(record.entityId);
11706
+ return ids;
11707
+ }
11248
11708
  /** True when any source is holding something. */
11249
11709
  isGrabbing() {
11250
11710
  return this.grabs.size > 0;
11251
11711
  }
11712
+ /**
11713
+ * How an entity is currently held.
11714
+ *
11715
+ * Exposed for scripts: a Behaviour can poll this in `onUpdate` to drive
11716
+ * two-hand-specific behaviour — showing a bipod when both hands are on a
11717
+ * weapon, reading the live hand separation to gate a gesture, or reacting to
11718
+ * the scale a pull-apart produced. Returns null when nothing holds it.
11719
+ */
11720
+ getGrabInfo(entityId) {
11721
+ const holders = this.holdersOf(entityId);
11722
+ if (holders.length === 0) return null;
11723
+ const primary = holders.find((h) => h.record.role === "primary");
11724
+ const secondary = holders.find((h) => h.record.role === "secondary");
11725
+ return {
11726
+ hands: holders.length,
11727
+ primaryHandedness: primary?.input.handedness ?? null,
11728
+ secondaryHandedness: secondary?.input.handedness ?? null,
11729
+ handSeparation: primary && secondary ? this.handPosition(primary.input).distanceTo(
11730
+ this.handPosition(secondary.input)
11731
+ ) : null,
11732
+ attachPointIds: holders.map((h) => h.record.attachPointId)
11733
+ };
11734
+ }
11252
11735
  };
11253
11736
  __name(_XRInteractionSystem, "XRInteractionSystem");
11254
11737
  var XRInteractionSystem = _XRInteractionSystem;
@@ -11266,7 +11749,7 @@ function sampleTeleportArc(origin, direction, out = [], options) {
11266
11749
  for (let i = 0; i < steps; i += 1) {
11267
11750
  const t = i / (steps - 1) * maxTime;
11268
11751
  out.push(
11269
- new THREE39__namespace.Vector3(
11752
+ new THREE51__namespace.Vector3(
11270
11753
  origin.x + velocity.x * t,
11271
11754
  origin.y + velocity.y * t - 0.5 * gravity * t * t,
11272
11755
  origin.z + velocity.z * t
@@ -11283,7 +11766,7 @@ function arcGroundCrossing(points, groundY) {
11283
11766
  if (previous.y >= groundY && current.y <= groundY) {
11284
11767
  const span = previous.y - current.y;
11285
11768
  const alpha = span > 1e-6 ? (previous.y - groundY) / span : 0;
11286
- return new THREE39__namespace.Vector3().lerpVectors(previous, current, alpha);
11769
+ return new THREE51__namespace.Vector3().lerpVectors(previous, current, alpha);
11287
11770
  }
11288
11771
  }
11289
11772
  return null;
@@ -11291,11 +11774,11 @@ function arcGroundCrossing(points, groundY) {
11291
11774
  __name(arcGroundCrossing, "arcGroundCrossing");
11292
11775
 
11293
11776
  // core/systems/XRLocomotionSystem.ts
11294
- var UP = new THREE39__namespace.Vector3(0, 1, 0);
11777
+ var UP = new THREE51__namespace.Vector3(0, 1, 0);
11295
11778
  var AXIS_DEADZONE = 0.5;
11296
11779
  var _XRLocomotionSystem = class _XRLocomotionSystem {
11297
11780
  constructor(inputSystem) {
11298
- this.raycaster = new THREE39__namespace.Raycaster();
11781
+ this.raycaster = new THREE51__namespace.Raycaster();
11299
11782
  /** Per-source latch so one stick push yields exactly one snap turn. */
11300
11783
  this.turnLatched = /* @__PURE__ */ new WeakSet();
11301
11784
  /** Reused arc buffer — aiming runs every frame. */
@@ -11404,7 +11887,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11404
11887
  }
11405
11888
  if (this.turnLatched.has(source)) continue;
11406
11889
  this.turnLatched.add(source);
11407
- transform.rotation.y -= Math.sign(x) * THREE39__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
11890
+ transform.rotation.y -= Math.sign(x) * THREE51__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
11408
11891
  }
11409
11892
  }
11410
11893
  applySmoothMove(world, rig, transform, inputs, deltaTime) {
@@ -11414,12 +11897,12 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11414
11897
  const y = input.axes[1] ?? 0;
11415
11898
  if (Math.abs(x) < 0.15 && Math.abs(y) < 0.15) continue;
11416
11899
  const camera = world.getCamera();
11417
- const forward = new THREE39__namespace.Vector3();
11900
+ const forward = new THREE51__namespace.Vector3();
11418
11901
  camera.getWorldDirection(forward);
11419
11902
  forward.y = 0;
11420
11903
  if (forward.lengthSq() < 1e-6) continue;
11421
11904
  forward.normalize();
11422
- const right = new THREE39__namespace.Vector3().crossVectors(forward, UP).normalize();
11905
+ const right = new THREE51__namespace.Vector3().crossVectors(forward, UP).normalize();
11423
11906
  const step = rig.moveSpeed * Math.max(0, deltaTime);
11424
11907
  transform.position.x += (forward.x * -y + right.x * x) * step;
11425
11908
  transform.position.z += (forward.z * -y + right.z * x) * step;
@@ -11484,7 +11967,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11484
11967
  for (let i = 1; i < points.length; i += 1) {
11485
11968
  const from = points[i - 1];
11486
11969
  const to = points[i];
11487
- const delta = new THREE39__namespace.Vector3().subVectors(to, from);
11970
+ const delta = new THREE51__namespace.Vector3().subVectors(to, from);
11488
11971
  const distance = delta.length();
11489
11972
  if (distance < 1e-6) continue;
11490
11973
  this.raycaster.set(from, delta.divideScalar(distance));
@@ -11692,9 +12175,9 @@ var XRSessionSystem = _XRSessionSystem;
11692
12175
  function buildGeometry2(area) {
11693
12176
  if (area.shape === "circle") {
11694
12177
  const radius = Math.max(5e-3, area.width / 2);
11695
- return new THREE39__namespace.CircleGeometry(radius, 48);
12178
+ return new THREE51__namespace.CircleGeometry(radius, 48);
11696
12179
  }
11697
- return new THREE39__namespace.PlaneGeometry(
12180
+ return new THREE51__namespace.PlaneGeometry(
11698
12181
  Math.max(0.01, area.width),
11699
12182
  Math.max(0.01, area.depth)
11700
12183
  );
@@ -11717,7 +12200,7 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
11717
12200
  for (const entityId of entityIds) {
11718
12201
  const area = world.getComponent(entityId, VRTeleportAreaComponent);
11719
12202
  const object = world.getComponent(entityId, Object3DRef)?.object;
11720
- if (!area || !(object instanceof THREE39__namespace.Mesh)) continue;
12203
+ if (!area || !(object instanceof THREE51__namespace.Mesh)) continue;
11721
12204
  const signature = signatureOf(area);
11722
12205
  if (this.signatures.get(entityId) === signature) continue;
11723
12206
  this.signatures.set(entityId, signature);
@@ -11729,18 +12212,18 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
11729
12212
  __name(_VRTeleportAreaSystem, "VRTeleportAreaSystem");
11730
12213
  var VRTeleportAreaSystem = _VRTeleportAreaSystem;
11731
12214
  function createHandMaterial(preset, options) {
11732
- const color = new THREE39__namespace.Color(options.color);
12215
+ const color = new THREE51__namespace.Color(options.color);
11733
12216
  switch (preset) {
11734
12217
  case "ghost":
11735
- return new THREE39__namespace.MeshBasicMaterial({
12218
+ return new THREE51__namespace.MeshBasicMaterial({
11736
12219
  color,
11737
12220
  transparent: true,
11738
12221
  opacity: Math.min(1, Math.max(0.05, options.opacity * 0.45)),
11739
12222
  depthWrite: false,
11740
- side: THREE39__namespace.DoubleSide
12223
+ side: THREE51__namespace.DoubleSide
11741
12224
  });
11742
12225
  case "neon":
11743
- return new THREE39__namespace.MeshStandardMaterial({
12226
+ return new THREE51__namespace.MeshStandardMaterial({
11744
12227
  color,
11745
12228
  emissive: color,
11746
12229
  emissiveIntensity: Math.max(0.5, options.emissiveIntensity || 2),
@@ -11750,7 +12233,7 @@ function createHandMaterial(preset, options) {
11750
12233
  opacity: options.opacity
11751
12234
  });
11752
12235
  case "skeleton":
11753
- return new THREE39__namespace.MeshBasicMaterial({
12236
+ return new THREE51__namespace.MeshBasicMaterial({
11754
12237
  color,
11755
12238
  wireframe: true,
11756
12239
  transparent: true,
@@ -11758,7 +12241,7 @@ function createHandMaterial(preset, options) {
11758
12241
  });
11759
12242
  case "custom":
11760
12243
  case "default":
11761
- return new THREE39__namespace.MeshStandardMaterial({
12244
+ return new THREE51__namespace.MeshStandardMaterial({
11762
12245
  color,
11763
12246
  roughness: 0.85,
11764
12247
  metalness: 0,
@@ -11766,10 +12249,10 @@ function createHandMaterial(preset, options) {
11766
12249
  emissiveIntensity: Math.max(0, options.emissiveIntensity),
11767
12250
  transparent: options.opacity < 1,
11768
12251
  opacity: options.opacity,
11769
- side: THREE39__namespace.DoubleSide
12252
+ side: THREE51__namespace.DoubleSide
11770
12253
  });
11771
12254
  default: {
11772
- return new THREE39__namespace.MeshStandardMaterial({ color });
12255
+ return new THREE51__namespace.MeshStandardMaterial({ color });
11773
12256
  }
11774
12257
  }
11775
12258
  }
@@ -11782,6 +12265,9 @@ function applyHandMaterial(root, material) {
11782
12265
  });
11783
12266
  }
11784
12267
  __name(applyHandMaterial, "applyHandMaterial");
12268
+
12269
+ // core/systems/XRControllerVisualSystem.ts
12270
+ var RAY_LENGTH = 5;
11785
12271
  var SLOTS = [
11786
12272
  { index: 0, side: "left" },
11787
12273
  { index: 1, side: "right" }
@@ -11790,7 +12276,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11790
12276
  constructor(options) {
11791
12277
  this.controllerModelFactory = new XRControllerModelFactory_js.XRControllerModelFactory();
11792
12278
  this.handModelFactory = new XRHandModelFactory_js.XRHandModelFactory();
11793
- this.group = new THREE39__namespace.Group();
12279
+ this.group = new THREE51__namespace.Group();
11794
12280
  this.slots = /* @__PURE__ */ new Map();
11795
12281
  this.attachedTo = null;
11796
12282
  /** Last applied preset signature, so materials rebuild only on real change. */
@@ -11820,6 +12306,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11820
12306
  visuals.controllerModel.visible = showControllers && hasController;
11821
12307
  visuals.handModel.visible = showHands && hasHand;
11822
12308
  visuals.ray.visible = this.shouldShowRay(interactor);
12309
+ this.fitRayToHover(visuals.ray, side);
11823
12310
  if (showHands && this.handMaterial) {
11824
12311
  applyHandMaterial(visuals.handModel, this.handMaterial);
11825
12312
  }
@@ -11900,6 +12387,18 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11900
12387
  if (!this.interactionSystem) return true;
11901
12388
  return this.interactionSystem.getHoveredEntityId() !== null || this.interactionSystem.isGrabbing();
11902
12389
  }
12390
+ /**
12391
+ * Stop the ray at whatever it is pointing at.
12392
+ *
12393
+ * A fixed-length ray drawn with depth testing off punches straight through the
12394
+ * object, so the user cannot tell where the pointer actually lands. Scaling it
12395
+ * to the hit distance turns it into a pointer that meets the surface.
12396
+ */
12397
+ fitRayToHover(ray, side) {
12398
+ const hover = this.interactionSystem?.getHoverFor(side) ?? null;
12399
+ const length = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
12400
+ ray.scale.z = length / RAY_LENGTH;
12401
+ }
11903
12402
  /** Lazily build per-slot visuals; the device reports profiles late. */
11904
12403
  ensureSlots(world) {
11905
12404
  const renderer = world.getRenderer();
@@ -11939,17 +12438,17 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11939
12438
  this.attachedTo = target;
11940
12439
  }
11941
12440
  createRay() {
11942
- const geometry = new THREE39__namespace.BufferGeometry().setFromPoints([
11943
- new THREE39__namespace.Vector3(0, 0, 0),
11944
- new THREE39__namespace.Vector3(0, 0, -5)
12441
+ const geometry = new THREE51__namespace.BufferGeometry().setFromPoints([
12442
+ new THREE51__namespace.Vector3(0, 0, 0),
12443
+ new THREE51__namespace.Vector3(0, 0, -RAY_LENGTH)
11945
12444
  ]);
11946
- const material = new THREE39__namespace.LineBasicMaterial({
12445
+ const material = new THREE51__namespace.LineBasicMaterial({
11947
12446
  color: "#4da3ff",
11948
12447
  transparent: true,
11949
12448
  opacity: 0.6,
11950
12449
  depthTest: false
11951
12450
  });
11952
- const line = new THREE39__namespace.Line(geometry, material);
12451
+ const line = new THREE51__namespace.Line(geometry, material);
11953
12452
  line.name = "XRPointerRay";
11954
12453
  line.renderOrder = 999;
11955
12454
  line.visible = false;
@@ -11978,23 +12477,23 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
11978
12477
  __name(_XRControllerVisualSystem, "XRControllerVisualSystem");
11979
12478
  var XRControllerVisualSystem = _XRControllerVisualSystem;
11980
12479
  var MARKER_RADIUS = 0.28;
11981
- var ACCENT = new THREE39__namespace.Color("#4da3ff");
11982
- var BLOCKED = new THREE39__namespace.Color("#ff5a5a");
12480
+ var ACCENT = new THREE51__namespace.Color("#4da3ff");
12481
+ var BLOCKED = new THREE51__namespace.Color("#ff5a5a");
11983
12482
  var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
11984
12483
  constructor(locomotionSystem) {
11985
- this.group = new THREE39__namespace.Group();
11986
- this.arcGeometry = new THREE39__namespace.BufferGeometry();
11987
- this.marker = new THREE39__namespace.Group();
12484
+ this.group = new THREE51__namespace.Group();
12485
+ this.arcGeometry = new THREE51__namespace.BufferGeometry();
12486
+ this.marker = new THREE51__namespace.Group();
11988
12487
  this.attached = false;
11989
12488
  this.locomotionSystem = locomotionSystem;
11990
12489
  this.group.name = "XRTeleportVisuals";
11991
12490
  this.group.userData.ecsPreviewHidden = true;
11992
- const arcMaterial = new THREE39__namespace.LineBasicMaterial({
12491
+ const arcMaterial = new THREE51__namespace.LineBasicMaterial({
11993
12492
  vertexColors: true,
11994
12493
  transparent: true,
11995
12494
  depthTest: false
11996
12495
  });
11997
- this.arc = new THREE39__namespace.Line(this.arcGeometry, arcMaterial);
12496
+ this.arc = new THREE51__namespace.Line(this.arcGeometry, arcMaterial);
11998
12497
  this.arc.frustumCulled = false;
11999
12498
  this.arc.renderOrder = 998;
12000
12499
  this.group.add(this.arc);
@@ -12042,39 +12541,39 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
12042
12541
  }
12043
12542
  this.arcGeometry.setAttribute(
12044
12543
  "position",
12045
- new THREE39__namespace.BufferAttribute(positions, 3)
12544
+ new THREE51__namespace.BufferAttribute(positions, 3)
12046
12545
  );
12047
- this.arcGeometry.setAttribute("color", new THREE39__namespace.BufferAttribute(colors, 3));
12546
+ this.arcGeometry.setAttribute("color", new THREE51__namespace.BufferAttribute(colors, 3));
12048
12547
  this.arcGeometry.computeBoundingSphere();
12049
12548
  }
12050
12549
  createRing() {
12051
- const geometry = new THREE39__namespace.RingGeometry(
12550
+ const geometry = new THREE51__namespace.RingGeometry(
12052
12551
  MARKER_RADIUS * 0.72,
12053
12552
  MARKER_RADIUS,
12054
12553
  48
12055
12554
  );
12056
12555
  geometry.rotateX(-Math.PI / 2);
12057
- return new THREE39__namespace.Mesh(
12556
+ return new THREE51__namespace.Mesh(
12058
12557
  geometry,
12059
- new THREE39__namespace.MeshBasicMaterial({
12558
+ new THREE51__namespace.MeshBasicMaterial({
12060
12559
  color: ACCENT,
12061
12560
  transparent: true,
12062
12561
  opacity: 0.9,
12063
- side: THREE39__namespace.DoubleSide,
12562
+ side: THREE51__namespace.DoubleSide,
12064
12563
  depthTest: false
12065
12564
  })
12066
12565
  );
12067
12566
  }
12068
12567
  createDot() {
12069
- const geometry = new THREE39__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
12568
+ const geometry = new THREE51__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
12070
12569
  geometry.rotateX(-Math.PI / 2);
12071
- return new THREE39__namespace.Mesh(
12570
+ return new THREE51__namespace.Mesh(
12072
12571
  geometry,
12073
- new THREE39__namespace.MeshBasicMaterial({
12572
+ new THREE51__namespace.MeshBasicMaterial({
12074
12573
  color: ACCENT,
12075
12574
  transparent: true,
12076
12575
  opacity: 0.55,
12077
- side: THREE39__namespace.DoubleSide,
12576
+ side: THREE51__namespace.DoubleSide,
12078
12577
  depthTest: false
12079
12578
  })
12080
12579
  );
@@ -12176,7 +12675,7 @@ function boneDirections(joints, finger) {
12176
12675
  const from = joints[chain[i - 1]];
12177
12676
  const to = joints[chain[i]];
12178
12677
  if (!from || !to) return null;
12179
- const direction = new THREE39__namespace.Vector3().subVectors(to, from);
12678
+ const direction = new THREE51__namespace.Vector3().subVectors(to, from);
12180
12679
  if (direction.lengthSq() < 1e-12) return null;
12181
12680
  directions.push(direction.normalize());
12182
12681
  }
@@ -12249,8 +12748,8 @@ function computeSpread(joints, a, b) {
12249
12748
  const bBase = joints[METACARPAL_JOINTS[b]];
12250
12749
  const bTip = joints[TIP_JOINTS[b]];
12251
12750
  if (!aBase || !aTip || !bBase || !bTip) return null;
12252
- const dirA = new THREE39__namespace.Vector3().subVectors(aTip, aBase);
12253
- const dirB = new THREE39__namespace.Vector3().subVectors(bTip, bBase);
12751
+ const dirA = new THREE51__namespace.Vector3().subVectors(aTip, aBase);
12752
+ const dirB = new THREE51__namespace.Vector3().subVectors(bTip, bBase);
12254
12753
  if (dirA.lengthSq() < 1e-12 || dirB.lengthSq() < 1e-12) return null;
12255
12754
  return clamp01((1 - dirA.normalize().dot(dirB.normalize())) / 0.45);
12256
12755
  }
@@ -12260,18 +12759,18 @@ function computePalmPose(joints, handedness) {
12260
12759
  const indexBase = joints["index-finger-metacarpal"];
12261
12760
  const pinkyBase = joints["pinky-finger-metacarpal"];
12262
12761
  if (!wrist || !indexBase || !pinkyBase) return null;
12263
- const u = new THREE39__namespace.Vector3().subVectors(indexBase, wrist);
12264
- const v = new THREE39__namespace.Vector3().subVectors(pinkyBase, wrist);
12265
- const normal = new THREE39__namespace.Vector3().crossVectors(u, v);
12762
+ const u = new THREE51__namespace.Vector3().subVectors(indexBase, wrist);
12763
+ const v = new THREE51__namespace.Vector3().subVectors(pinkyBase, wrist);
12764
+ const normal = new THREE51__namespace.Vector3().crossVectors(u, v);
12266
12765
  if (normal.lengthSq() < 1e-12) return null;
12267
12766
  normal.normalize();
12268
12767
  if (handedness === "left") normal.multiplyScalar(-1);
12269
- const right = new THREE39__namespace.Vector3().subVectors(indexBase, pinkyBase);
12768
+ const right = new THREE51__namespace.Vector3().subVectors(indexBase, pinkyBase);
12270
12769
  if (right.lengthSq() < 1e-12) return null;
12271
12770
  right.normalize();
12272
12771
  if (handedness === "left") right.multiplyScalar(-1);
12273
- const up = new THREE39__namespace.Vector3().crossVectors(normal, right).normalize();
12274
- const center = new THREE39__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
12772
+ const up = new THREE51__namespace.Vector3().crossVectors(normal, right).normalize();
12773
+ const center = new THREE51__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
12275
12774
  return {
12276
12775
  center,
12277
12776
  normal,
@@ -12302,7 +12801,7 @@ function computeHandShape(joints, handedness) {
12302
12801
  const thumbTip = joints["thumb-tip"];
12303
12802
  let thumbDirection = null;
12304
12803
  if (thumbBase && thumbTip) {
12305
- const direction = new THREE39__namespace.Vector3().subVectors(thumbTip, thumbBase);
12804
+ const direction = new THREE51__namespace.Vector3().subVectors(thumbTip, thumbBase);
12306
12805
  if (direction.lengthSq() > 1e-12) thumbDirection = direction.normalize();
12307
12806
  }
12308
12807
  return {
@@ -12334,12 +12833,12 @@ function scoreAgainst(actual, target, tolerance) {
12334
12833
  }
12335
12834
  __name(scoreAgainst, "scoreAgainst");
12336
12835
  var WORLD_DIRECTIONS = {
12337
- up: new THREE39__namespace.Vector3(0, 1, 0),
12338
- down: new THREE39__namespace.Vector3(0, -1, 0),
12339
- forward: new THREE39__namespace.Vector3(0, 0, -1),
12340
- back: new THREE39__namespace.Vector3(0, 0, 1),
12341
- left: new THREE39__namespace.Vector3(-1, 0, 0),
12342
- right: new THREE39__namespace.Vector3(1, 0, 0)
12836
+ up: new THREE51__namespace.Vector3(0, 1, 0),
12837
+ down: new THREE51__namespace.Vector3(0, -1, 0),
12838
+ forward: new THREE51__namespace.Vector3(0, 0, -1),
12839
+ back: new THREE51__namespace.Vector3(0, 0, 1),
12840
+ left: new THREE51__namespace.Vector3(-1, 0, 0),
12841
+ right: new THREE51__namespace.Vector3(1, 0, 0)
12343
12842
  };
12344
12843
  function axisVector(shape, axis) {
12345
12844
  if (axis === "thumb") return shape.thumbDirection;
@@ -12354,7 +12853,7 @@ function resolveDirection(direction, shape, context) {
12354
12853
  const head = context?.headPosition;
12355
12854
  const origin = shape.palm?.center;
12356
12855
  if (!head || !origin) return null;
12357
- const toHead = new THREE39__namespace.Vector3().subVectors(head, origin);
12856
+ const toHead = new THREE51__namespace.Vector3().subVectors(head, origin);
12358
12857
  if (toHead.lengthSq() < 1e-12) return null;
12359
12858
  toHead.normalize();
12360
12859
  return direction === "toward-head" ? toHead : toHead.multiplyScalar(-1);
@@ -12404,8 +12903,8 @@ function matchHandPose(shape, definition, context) {
12404
12903
  if (!handAxis || !wanted) {
12405
12904
  consider(0, "orientation");
12406
12905
  } else {
12407
- const angle = THREE39__namespace.MathUtils.radToDeg(
12408
- Math.acos(THREE39__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
12906
+ const angle = THREE51__namespace.MathUtils.radToDeg(
12907
+ Math.acos(THREE51__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
12409
12908
  );
12410
12909
  const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
12411
12910
  consider(clamp01(1 - normalizedError / MATCH_FALLOFF), "orientation");
@@ -12483,7 +12982,7 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
12483
12982
  this.trackers = /* @__PURE__ */ new Map();
12484
12983
  this.shapes = /* @__PURE__ */ new Map();
12485
12984
  /** Reused so head-relative orientation checks allocate nothing per frame. */
12486
- this.headPosition = new THREE39__namespace.Vector3();
12985
+ this.headPosition = new THREE51__namespace.Vector3();
12487
12986
  this.elapsed = 0;
12488
12987
  this.inputSystem = inputSystem;
12489
12988
  }
@@ -12604,6 +13103,152 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
12604
13103
  };
12605
13104
  __name(_XRHandPoseSystem, "XRHandPoseSystem");
12606
13105
  var XRHandPoseSystem = _XRHandPoseSystem;
13106
+ var XR_HOVER_SHELL_FLAG = "__xrHoverShell";
13107
+ var SHELL_INFLATE = 1.04;
13108
+ var SCALE_SMOOTH_SECONDS = 0.09;
13109
+ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
13110
+ constructor(interactionSystem) {
13111
+ this.records = /* @__PURE__ */ new Map();
13112
+ this.interactionSystem = interactionSystem;
13113
+ }
13114
+ update(world, deltaTime) {
13115
+ if (!world.isXrPresenting()) {
13116
+ this.clearAll(world);
13117
+ return;
13118
+ }
13119
+ const hovered = this.interactionSystem.getHoveredEntityIds();
13120
+ const held = this.interactionSystem.getHeldEntityIds();
13121
+ for (const entityId of hovered) {
13122
+ if (held.has(entityId)) continue;
13123
+ const interactable = world.getComponent(entityId, XRInteractableComponent);
13124
+ if (!interactable?.enabled) continue;
13125
+ if (interactable.hoverBehavior === "none") continue;
13126
+ this.begin(world, entityId, interactable);
13127
+ }
13128
+ for (const [entityId, record] of this.records) {
13129
+ if (!hovered.has(entityId) || held.has(entityId)) {
13130
+ record.active = false;
13131
+ }
13132
+ }
13133
+ this.animate(world, deltaTime);
13134
+ }
13135
+ begin(world, entityId, interactable) {
13136
+ const existing = this.records.get(entityId);
13137
+ if (existing) {
13138
+ existing.active = true;
13139
+ return;
13140
+ }
13141
+ const transform = world.getComponent(entityId, TransformComponent);
13142
+ if (!transform) return;
13143
+ const wantsColor = interactable.hoverBehavior === "color" || interactable.hoverBehavior === "both";
13144
+ this.records.set(entityId, {
13145
+ shell: wantsColor ? this.addShell(world, entityId, interactable) : null,
13146
+ baseScale: new THREE51__namespace.Vector3(
13147
+ transform.scale.x,
13148
+ transform.scale.y,
13149
+ transform.scale.z
13150
+ ),
13151
+ progress: 0,
13152
+ active: true
13153
+ });
13154
+ }
13155
+ /** Build an inflated back-face copy of the object's meshes. */
13156
+ addShell(world, entityId, interactable) {
13157
+ const object = world.getComponent(entityId, Object3DRef)?.object;
13158
+ if (!object) return null;
13159
+ const material = new THREE51__namespace.MeshBasicMaterial({
13160
+ color: new THREE51__namespace.Color(interactable.hoverColor),
13161
+ // Back faces only, so the shell shows just outside the silhouette instead
13162
+ // of covering the object.
13163
+ side: THREE51__namespace.BackSide,
13164
+ transparent: true,
13165
+ opacity: 0.85,
13166
+ depthWrite: false
13167
+ });
13168
+ const group = new THREE51__namespace.Group();
13169
+ group.name = "XRHoverShell";
13170
+ group.userData[XR_HOVER_SHELL_FLAG] = true;
13171
+ group.renderOrder = 998;
13172
+ object.traverse((child) => {
13173
+ if (!(child instanceof THREE51__namespace.Mesh) || !child.geometry) return;
13174
+ if (child.userData[XR_HOVER_SHELL_FLAG]) return;
13175
+ const clone3 = new THREE51__namespace.Mesh(child.geometry, material);
13176
+ child.updateWorldMatrix(true, false);
13177
+ object.updateWorldMatrix(true, false);
13178
+ clone3.matrixAutoUpdate = false;
13179
+ clone3.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE51__namespace.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
13180
+ clone3.userData[XR_HOVER_SHELL_FLAG] = true;
13181
+ clone3.raycast = () => {
13182
+ };
13183
+ group.add(clone3);
13184
+ });
13185
+ if (group.children.length === 0) {
13186
+ material.dispose();
13187
+ return null;
13188
+ }
13189
+ object.add(group);
13190
+ return group;
13191
+ }
13192
+ animate(world, deltaTime) {
13193
+ const step = 1 - Math.exp(-Math.max(0, deltaTime) / SCALE_SMOOTH_SECONDS);
13194
+ for (const [entityId, record] of Array.from(this.records)) {
13195
+ const interactable = world.getComponent(entityId, XRInteractableComponent);
13196
+ const target = record.active ? 1 : 0;
13197
+ record.progress += (target - record.progress) * step;
13198
+ const wantsScale = interactable?.hoverBehavior === "scale" || interactable?.hoverBehavior === "both";
13199
+ if (wantsScale) {
13200
+ const transform = world.getComponent(entityId, TransformComponent);
13201
+ if (transform) {
13202
+ const factor = 1 + (interactable.hoverScale - 1) * Math.min(1, record.progress);
13203
+ transform.scale.x = record.baseScale.x * factor;
13204
+ transform.scale.y = record.baseScale.y * factor;
13205
+ transform.scale.z = record.baseScale.z * factor;
13206
+ }
13207
+ }
13208
+ if (!record.active && record.progress < 0.01) {
13209
+ this.finish(world, entityId, record);
13210
+ }
13211
+ }
13212
+ }
13213
+ /** Remove the effect and restore the authored scale exactly. */
13214
+ finish(world, entityId, record) {
13215
+ const transform = world.getComponent(entityId, TransformComponent);
13216
+ if (transform) {
13217
+ transform.scale.x = record.baseScale.x;
13218
+ transform.scale.y = record.baseScale.y;
13219
+ transform.scale.z = record.baseScale.z;
13220
+ }
13221
+ this.disposeShell(record.shell);
13222
+ this.records.delete(entityId);
13223
+ }
13224
+ disposeShell(shell) {
13225
+ if (!shell) return;
13226
+ shell.parent?.remove(shell);
13227
+ shell.traverse((child) => {
13228
+ if (child instanceof THREE51__namespace.Mesh) {
13229
+ const material = child.material;
13230
+ if (Array.isArray(material)) {
13231
+ for (const entry of material) entry.dispose();
13232
+ } else {
13233
+ material?.dispose();
13234
+ }
13235
+ }
13236
+ });
13237
+ }
13238
+ clearAll(world) {
13239
+ for (const [entityId, record] of Array.from(this.records)) {
13240
+ this.finish(world, entityId, record);
13241
+ }
13242
+ }
13243
+ dispose() {
13244
+ for (const record of this.records.values()) {
13245
+ this.disposeShell(record.shell);
13246
+ }
13247
+ this.records.clear();
13248
+ }
13249
+ };
13250
+ __name(_XRHoverVisualSystem, "XRHoverVisualSystem");
13251
+ var XRHoverVisualSystem = _XRHoverVisualSystem;
12607
13252
 
12608
13253
  // core/World.ts
12609
13254
  var _World = class _World {
@@ -13274,8 +13919,8 @@ var _World = class _World {
13274
13919
  * ```
13275
13920
  */
13276
13921
  raycast(origin, direction, near = 0, far = Infinity) {
13277
- const rayOrigin = new THREE39__namespace.Vector3(origin.x, origin.y, origin.z);
13278
- const rayDirection = new THREE39__namespace.Vector3(
13922
+ const rayOrigin = new THREE51__namespace.Vector3(origin.x, origin.y, origin.z);
13923
+ const rayDirection = new THREE51__namespace.Vector3(
13279
13924
  direction.x,
13280
13925
  direction.y,
13281
13926
  direction.z
@@ -13284,7 +13929,7 @@ var _World = class _World {
13284
13929
  return [];
13285
13930
  }
13286
13931
  rayDirection.normalize();
13287
- const raycaster = new THREE39__namespace.Raycaster(rayOrigin, rayDirection, near, far);
13932
+ const raycaster = new THREE51__namespace.Raycaster(rayOrigin, rayDirection, near, far);
13288
13933
  const intersections = raycaster.intersectObjects(
13289
13934
  this.scene.children,
13290
13935
  true
@@ -13318,14 +13963,14 @@ var _World = class _World {
13318
13963
  if (!sourceObject) {
13319
13964
  return [];
13320
13965
  }
13321
- const rayOrigin = new THREE39__namespace.Vector3();
13322
- const rayDirection = new THREE39__namespace.Vector3();
13966
+ const rayOrigin = new THREE51__namespace.Vector3();
13967
+ const rayDirection = new THREE51__namespace.Vector3();
13323
13968
  sourceObject.getWorldPosition(rayOrigin);
13324
13969
  sourceObject.getWorldDirection(rayDirection);
13325
13970
  if (rayDirection.lengthSq() === 0) {
13326
13971
  return [];
13327
13972
  }
13328
- const raycaster = new THREE39__namespace.Raycaster(rayOrigin, rayDirection, near, far);
13973
+ const raycaster = new THREE51__namespace.Raycaster(rayOrigin, rayDirection, near, far);
13329
13974
  const intersections = raycaster.intersectObjects(
13330
13975
  this.scene.children,
13331
13976
  true
@@ -14036,12 +14681,14 @@ exports.VideoSystem = VideoSystem;
14036
14681
  exports.World = World;
14037
14682
  exports.XRControllerVisualSystem = XRControllerVisualSystem;
14038
14683
  exports.XRHandPoseSystem = XRHandPoseSystem;
14684
+ exports.XRHoverVisualSystem = XRHoverVisualSystem;
14039
14685
  exports.XRInputSystem = XRInputSystem;
14040
14686
  exports.XRInteractionSystem = XRInteractionSystem;
14041
14687
  exports.XRLocomotionSystem = XRLocomotionSystem;
14042
14688
  exports.XRSessionSystem = XRSessionSystem;
14043
14689
  exports.XRTeleportVisualSystem = XRTeleportVisualSystem;
14044
14690
  exports.XR_FINGERS = XR_FINGERS;
14691
+ exports.XR_HOVER_SHELL_FLAG = XR_HOVER_SHELL_FLAG;
14045
14692
  exports.angularVelocityFrom = angularVelocityFrom;
14046
14693
  exports.applyHandMaterial = applyHandMaterial;
14047
14694
  exports.applyPrefabToSnapshotInstances = applyPrefabToSnapshotInstances;