@relu-interactives/spatial-ecs 0.3.10 → 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.
- package/dist/{XRHandPose-CjacJ_16.d.ts → XRHandPose-B6I4y1lj.d.ts} +48 -14
- package/dist/{XRHandPose-hAtj9wm-.d.cts → XRHandPose-C-KJeYcJ.d.cts} +48 -14
- package/dist/{chunk-5UVBEAGW.js → chunk-FSHRPY65.js} +4 -4
- package/dist/{chunk-5UVBEAGW.js.map → chunk-FSHRPY65.js.map} +1 -1
- package/dist/{chunk-O2HSIZFC.js → chunk-KLU2M43M.js} +270 -33
- package/dist/chunk-KLU2M43M.js.map +1 -0
- package/dist/{chunk-ELSZ5U3F.js → chunk-TXEGYR76.js} +5 -4
- package/dist/chunk-TXEGYR76.js.map +1 -0
- package/dist/{chunk-NJOO7NO6.js → chunk-Y3AC3VZ2.js} +3 -3
- package/dist/{chunk-NJOO7NO6.js.map → chunk-Y3AC3VZ2.js.map} +1 -1
- package/dist/components/index.cjs +3 -2
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +1 -1
- package/dist/docs/guide/vr-and-hand-poses.md +7 -3
- package/dist/factories/index.cjs +3 -2
- package/dist/factories/index.cjs.map +1 -1
- package/dist/factories/index.d.cts +1 -1
- package/dist/factories/index.d.ts +1 -1
- package/dist/factories/index.js +3 -3
- package/dist/{index-B-KhN8Ox.d.ts → index-Bi_wIo1o.d.ts} +56 -1
- package/dist/{index-B7JDsx4a.d.ts → index-CEZp7qLL.d.ts} +2 -3
- package/dist/{index-B9JqYr3Q.d.cts → index-DPRnnSI-.d.cts} +2 -3
- package/dist/{index-DU-CF-3m.d.cts → index-jlgDou9W.d.cts} +56 -1
- package/dist/index.cjs +270 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +8 -8
- package/dist/systems/index.cjs +270 -30
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.d.cts +1 -1
- package/dist/systems/index.d.ts +1 -1
- package/dist/systems/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-ELSZ5U3F.js.map +0 -1
- package/dist/chunk-O2HSIZFC.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -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";
|
|
@@ -2855,7 +2854,9 @@ var _XRInteractableComponent = class _XRInteractableComponent extends Component
|
|
|
2855
2854
|
);
|
|
2856
2855
|
this.throwOnRelease = options?.throwOnRelease ?? true;
|
|
2857
2856
|
this.throwStrength = options?.throwStrength ?? 1;
|
|
2858
|
-
this.
|
|
2857
|
+
this.hoverBehavior = options?.hoverBehavior ?? "color";
|
|
2858
|
+
this.hoverColor = options?.hoverColor ?? "#4da3ff";
|
|
2859
|
+
this.hoverScale = options?.hoverScale ?? 1.06;
|
|
2859
2860
|
}
|
|
2860
2861
|
};
|
|
2861
2862
|
__name(_XRInteractableComponent, "XRInteractableComponent");
|
|
@@ -4866,9 +4867,6 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
|
|
|
4866
4867
|
if (typeof sourceInteractable.pointable === "boolean") {
|
|
4867
4868
|
interactable.pointable = sourceInteractable.pointable;
|
|
4868
4869
|
}
|
|
4869
|
-
if (typeof sourceInteractable.grabMode === "string") {
|
|
4870
|
-
interactable.grabMode = sourceInteractable.grabMode;
|
|
4871
|
-
}
|
|
4872
4870
|
if (typeof sourceInteractable.grabReach === "string") {
|
|
4873
4871
|
interactable.grabReach = sourceInteractable.grabReach;
|
|
4874
4872
|
}
|
|
@@ -4927,8 +4925,16 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
|
|
|
4927
4925
|
z: typeof grabRot.z === "number" ? grabRot.z : 0
|
|
4928
4926
|
};
|
|
4929
4927
|
}
|
|
4930
|
-
if (typeof sourceInteractable.
|
|
4931
|
-
interactable.
|
|
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;
|
|
4932
4938
|
}
|
|
4933
4939
|
}
|
|
4934
4940
|
}
|
|
@@ -7445,6 +7451,20 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
|
|
|
7445
7451
|
__name(_PostprocessingSyncSystem, "PostprocessingSyncSystem");
|
|
7446
7452
|
var PostprocessingSyncSystem = _PostprocessingSyncSystem;
|
|
7447
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");
|
|
7448
7468
|
function createRigidBodyDesc(RAPIER, bodyType) {
|
|
7449
7469
|
if (bodyType === "fixed") {
|
|
7450
7470
|
return RAPIER.RigidBodyDesc.fixed();
|
|
@@ -7743,7 +7763,8 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
|
|
|
7743
7763
|
setPhysicsLiveRef(physics, "collider", null);
|
|
7744
7764
|
continue;
|
|
7745
7765
|
}
|
|
7746
|
-
const
|
|
7766
|
+
const { type: _desiredBodyType, ...rigidBodyRebuildValues } = physics.values.rigidbody;
|
|
7767
|
+
const rigidBodySignature = JSON.stringify(rigidBodyRebuildValues);
|
|
7747
7768
|
const previousRigidBodySignature = this.rigidBodySignatures.get(entityId);
|
|
7748
7769
|
const hasRigidBody = this.rigidBodyHandles.has(entityId);
|
|
7749
7770
|
const shouldRecreateBody = !hasRigidBody || previousRigidBodySignature !== rigidBodySignature;
|
|
@@ -7798,6 +7819,25 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
|
|
|
7798
7819
|
continue;
|
|
7799
7820
|
}
|
|
7800
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
|
+
}
|
|
7801
7841
|
if (this.isEditorMode || physics.values.rigidbody.type !== "dynamic") {
|
|
7802
7842
|
syncBodyFromTransform(body, transform);
|
|
7803
7843
|
}
|
|
@@ -11054,6 +11094,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11054
11094
|
this.raycaster = new THREE51__namespace.Raycaster();
|
|
11055
11095
|
this.grabs = /* @__PURE__ */ new Map();
|
|
11056
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();
|
|
11057
11099
|
/** Monotonic seconds, accumulated from deltaTime for velocity sampling. */
|
|
11058
11100
|
this.elapsed = 0;
|
|
11059
11101
|
this.inputSystem = inputSystem;
|
|
@@ -11067,6 +11109,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11067
11109
|
const inputs = this.inputSystem.getInputs();
|
|
11068
11110
|
const interactables = this.collectInteractables(world);
|
|
11069
11111
|
this.hoveredEntityId = null;
|
|
11112
|
+
this.hovers.clear();
|
|
11070
11113
|
for (const input of inputs) {
|
|
11071
11114
|
if (!input.connected) {
|
|
11072
11115
|
this.release(world, input);
|
|
@@ -11084,6 +11127,10 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11084
11127
|
if (this.hoveredEntityId === null) {
|
|
11085
11128
|
this.hoveredEntityId = candidate.entityId;
|
|
11086
11129
|
}
|
|
11130
|
+
this.hovers.set(input, {
|
|
11131
|
+
entityId: candidate.entityId,
|
|
11132
|
+
distance: candidate.distance
|
|
11133
|
+
});
|
|
11087
11134
|
const interactable = world.getComponent(
|
|
11088
11135
|
candidate.entityId,
|
|
11089
11136
|
XRInteractableComponent
|
|
@@ -11156,7 +11203,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11156
11203
|
transform.position.x - hand.x,
|
|
11157
11204
|
transform.position.y - hand.y,
|
|
11158
11205
|
transform.position.z - hand.z
|
|
11159
|
-
);
|
|
11206
|
+
).applyQuaternion(handQuaternion.clone().invert());
|
|
11160
11207
|
record.handRotationOffset = handQuaternion.clone().invert().multiply(objectQuaternion);
|
|
11161
11208
|
record.remote = false;
|
|
11162
11209
|
record.tracker.clear();
|
|
@@ -11267,13 +11314,22 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11267
11314
|
}
|
|
11268
11315
|
}
|
|
11269
11316
|
if (nearestId !== null) {
|
|
11270
|
-
return {
|
|
11317
|
+
return {
|
|
11318
|
+
entityId: nearestId,
|
|
11319
|
+
viaDistance: false,
|
|
11320
|
+
distance: nearestDistance
|
|
11321
|
+
};
|
|
11271
11322
|
}
|
|
11272
|
-
const
|
|
11273
|
-
if (
|
|
11323
|
+
const hit = this.raycast(input, interactables);
|
|
11324
|
+
if (hit === null) return null;
|
|
11325
|
+
const hitId = hit.entityId;
|
|
11274
11326
|
const interactable = world.getComponent(hitId, XRInteractableComponent);
|
|
11275
11327
|
const viaDistanceAllowed = interactable?.grabReach === "distance" || interactable?.grabReach === "both";
|
|
11276
|
-
return {
|
|
11328
|
+
return {
|
|
11329
|
+
entityId: hitId,
|
|
11330
|
+
viaDistance: viaDistanceAllowed,
|
|
11331
|
+
distance: hit.distance
|
|
11332
|
+
};
|
|
11277
11333
|
}
|
|
11278
11334
|
collectInteractables(world) {
|
|
11279
11335
|
const out = [];
|
|
@@ -11304,7 +11360,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11304
11360
|
nearestId = entry.entityId;
|
|
11305
11361
|
}
|
|
11306
11362
|
}
|
|
11307
|
-
return nearestId;
|
|
11363
|
+
return nearestId === null ? null : { entityId: nearestId, distance: nearestDistance };
|
|
11308
11364
|
}
|
|
11309
11365
|
/**
|
|
11310
11366
|
* Pick the authored hold this hand should take.
|
|
@@ -11379,7 +11435,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11379
11435
|
transform.position.x - hand.x,
|
|
11380
11436
|
transform.position.y - hand.y,
|
|
11381
11437
|
transform.position.z - hand.z
|
|
11382
|
-
);
|
|
11438
|
+
).applyQuaternion(handQuaternion.clone().invert());
|
|
11383
11439
|
}
|
|
11384
11440
|
const physics = world.getComponent(entityId, PhysicsComponent);
|
|
11385
11441
|
let restoreBodyType = null;
|
|
@@ -11514,9 +11570,10 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11514
11570
|
transform.position.z = hand.z - anchor.z;
|
|
11515
11571
|
writeEuler(transform.rotation, objectQuaternion);
|
|
11516
11572
|
} else {
|
|
11517
|
-
|
|
11518
|
-
transform.position.
|
|
11519
|
-
transform.position.
|
|
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;
|
|
11520
11577
|
writeEuler(transform.rotation, objectQuaternion);
|
|
11521
11578
|
}
|
|
11522
11579
|
held.tracker.record(
|
|
@@ -11569,19 +11626,14 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11569
11626
|
const physics = world.getComponent(held.entityId, PhysicsComponent);
|
|
11570
11627
|
if (held.restoreBodyType !== null && physics) {
|
|
11571
11628
|
physics.values.rigidbody.type = held.restoreBodyType;
|
|
11572
|
-
const body = physics.rigidBody;
|
|
11573
11629
|
const dynamic = held.restoreBodyType === "dynamic";
|
|
11574
|
-
if (
|
|
11630
|
+
if (dynamic && interactable?.throwOnRelease) {
|
|
11575
11631
|
const velocity = held.tracker.computeVelocity(this.elapsed).multiplyScalar(interactable.throwStrength);
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
const spin = held.angularVelocity;
|
|
11582
|
-
body.setAngvel({ x: spin.x, y: spin.y, z: spin.z }, true);
|
|
11583
|
-
} catch {
|
|
11584
|
-
}
|
|
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
|
+
});
|
|
11585
11637
|
}
|
|
11586
11638
|
}
|
|
11587
11639
|
held.tracker.clear();
|
|
@@ -11629,6 +11681,30 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
11629
11681
|
getHoveredEntityId() {
|
|
11630
11682
|
return this.hoveredEntityId;
|
|
11631
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
|
+
}
|
|
11632
11708
|
/** True when any source is holding something. */
|
|
11633
11709
|
isGrabbing() {
|
|
11634
11710
|
return this.grabs.size > 0;
|
|
@@ -12189,6 +12265,9 @@ function applyHandMaterial(root, material) {
|
|
|
12189
12265
|
});
|
|
12190
12266
|
}
|
|
12191
12267
|
__name(applyHandMaterial, "applyHandMaterial");
|
|
12268
|
+
|
|
12269
|
+
// core/systems/XRControllerVisualSystem.ts
|
|
12270
|
+
var RAY_LENGTH = 5;
|
|
12192
12271
|
var SLOTS = [
|
|
12193
12272
|
{ index: 0, side: "left" },
|
|
12194
12273
|
{ index: 1, side: "right" }
|
|
@@ -12227,6 +12306,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
12227
12306
|
visuals.controllerModel.visible = showControllers && hasController;
|
|
12228
12307
|
visuals.handModel.visible = showHands && hasHand;
|
|
12229
12308
|
visuals.ray.visible = this.shouldShowRay(interactor);
|
|
12309
|
+
this.fitRayToHover(visuals.ray, side);
|
|
12230
12310
|
if (showHands && this.handMaterial) {
|
|
12231
12311
|
applyHandMaterial(visuals.handModel, this.handMaterial);
|
|
12232
12312
|
}
|
|
@@ -12307,6 +12387,18 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
12307
12387
|
if (!this.interactionSystem) return true;
|
|
12308
12388
|
return this.interactionSystem.getHoveredEntityId() !== null || this.interactionSystem.isGrabbing();
|
|
12309
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
|
+
}
|
|
12310
12402
|
/** Lazily build per-slot visuals; the device reports profiles late. */
|
|
12311
12403
|
ensureSlots(world) {
|
|
12312
12404
|
const renderer = world.getRenderer();
|
|
@@ -12348,7 +12440,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
12348
12440
|
createRay() {
|
|
12349
12441
|
const geometry = new THREE51__namespace.BufferGeometry().setFromPoints([
|
|
12350
12442
|
new THREE51__namespace.Vector3(0, 0, 0),
|
|
12351
|
-
new THREE51__namespace.Vector3(0, 0, -
|
|
12443
|
+
new THREE51__namespace.Vector3(0, 0, -RAY_LENGTH)
|
|
12352
12444
|
]);
|
|
12353
12445
|
const material = new THREE51__namespace.LineBasicMaterial({
|
|
12354
12446
|
color: "#4da3ff",
|
|
@@ -13011,6 +13103,152 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
|
|
|
13011
13103
|
};
|
|
13012
13104
|
__name(_XRHandPoseSystem, "XRHandPoseSystem");
|
|
13013
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;
|
|
13014
13252
|
|
|
13015
13253
|
// core/World.ts
|
|
13016
13254
|
var _World = class _World {
|
|
@@ -14443,12 +14681,14 @@ exports.VideoSystem = VideoSystem;
|
|
|
14443
14681
|
exports.World = World;
|
|
14444
14682
|
exports.XRControllerVisualSystem = XRControllerVisualSystem;
|
|
14445
14683
|
exports.XRHandPoseSystem = XRHandPoseSystem;
|
|
14684
|
+
exports.XRHoverVisualSystem = XRHoverVisualSystem;
|
|
14446
14685
|
exports.XRInputSystem = XRInputSystem;
|
|
14447
14686
|
exports.XRInteractionSystem = XRInteractionSystem;
|
|
14448
14687
|
exports.XRLocomotionSystem = XRLocomotionSystem;
|
|
14449
14688
|
exports.XRSessionSystem = XRSessionSystem;
|
|
14450
14689
|
exports.XRTeleportVisualSystem = XRTeleportVisualSystem;
|
|
14451
14690
|
exports.XR_FINGERS = XR_FINGERS;
|
|
14691
|
+
exports.XR_HOVER_SHELL_FLAG = XR_HOVER_SHELL_FLAG;
|
|
14452
14692
|
exports.angularVelocityFrom = angularVelocityFrom;
|
|
14453
14693
|
exports.applyHandMaterial = applyHandMaterial;
|
|
14454
14694
|
exports.applyPrefabToSnapshotInstances = applyPrefabToSnapshotInstances;
|