@relu-interactives/spatial-ecs 0.3.4 → 0.3.6
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/{System-vrU46Vuw.d.cts → System-TmC2EijF.d.cts} +2 -2
- package/dist/{System-vrU46Vuw.d.ts → System-TmC2EijF.d.ts} +2 -2
- package/dist/{XRRig-2686IqLQ.d.cts → XRInteractor-CCUzSqg9.d.ts} +96 -2
- package/dist/{XRRig-D2QURXl1.d.ts → XRInteractor-I2q6TKtF.d.cts} +96 -2
- package/dist/{chunk-4BZYK3JP.js → chunk-FQTOC7HK.js} +135 -88
- package/dist/chunk-FQTOC7HK.js.map +1 -0
- package/dist/{chunk-2LVFGVWI.js → chunk-JRJ3CL5H.js} +3 -3
- package/dist/{chunk-2LVFGVWI.js.map → chunk-JRJ3CL5H.js.map} +1 -1
- package/dist/{chunk-6QC2PJLD.js → chunk-N4GKZ7IV.js} +29 -3
- package/dist/chunk-N4GKZ7IV.js.map +1 -0
- package/dist/{chunk-ID266ZUA.js → chunk-WRJ2K3LZ.js} +525 -41
- package/dist/chunk-WRJ2K3LZ.js.map +1 -0
- package/dist/components/index.cjs +26 -0
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +3 -3
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +1 -1
- package/dist/factories/index.cjs +185 -112
- package/dist/factories/index.cjs.map +1 -1
- package/dist/factories/index.d.cts +7 -3
- package/dist/factories/index.d.ts +7 -3
- package/dist/factories/index.js +3 -3
- package/dist/{index-BtKOaHfY.d.ts → index-BltjPEhg.d.cts} +10 -3
- package/dist/{index-CRmfSYdj.d.ts → index-BpbmZuOz.d.ts} +92 -16
- package/dist/{index-Bc0YCz1A.d.cts → index-C5FrDt3z.d.cts} +92 -16
- package/dist/{index-Xu238jLn.d.cts → index-W8pUajCW.d.ts} +10 -3
- package/dist/index.cjs +829 -261
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -9
- package/dist/index.d.ts +76 -9
- package/dist/index.js +8 -8
- package/dist/systems/index.cjs +532 -36
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.d.cts +2 -2
- package/dist/systems/index.d.ts +2 -2
- package/dist/systems/index.js +3 -3
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/worldSnapshotUtils.d.cts +1 -1
- package/dist/worldSnapshotUtils.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-4BZYK3JP.js.map +0 -1
- package/dist/chunk-6QC2PJLD.js.map +0 -1
- package/dist/chunk-ID266ZUA.js.map +0 -1
package/dist/systems/index.cjs
CHANGED
|
@@ -2109,6 +2109,7 @@ var XRInteractableComponent = _XRInteractableComponent;
|
|
|
2109
2109
|
var _XRRigComponent = class _XRRigComponent extends Component {
|
|
2110
2110
|
constructor(options) {
|
|
2111
2111
|
super();
|
|
2112
|
+
this.trackingOrigin = options?.trackingOrigin ?? "floor";
|
|
2112
2113
|
this.locomotion = options?.locomotion ?? "teleport";
|
|
2113
2114
|
this.moveSpeed = options?.moveSpeed ?? 2;
|
|
2114
2115
|
this.snapTurn = options?.snapTurn ?? true;
|
|
@@ -2119,6 +2120,29 @@ var _XRRigComponent = class _XRRigComponent extends Component {
|
|
|
2119
2120
|
__name(_XRRigComponent, "XRRigComponent");
|
|
2120
2121
|
var XRRigComponent = _XRRigComponent;
|
|
2121
2122
|
|
|
2123
|
+
// core/components/XRInteractor.ts
|
|
2124
|
+
var _XRInteractorComponent = class _XRInteractorComponent extends Component {
|
|
2125
|
+
constructor(options) {
|
|
2126
|
+
super();
|
|
2127
|
+
this.interactorType = options?.interactorType ?? "both";
|
|
2128
|
+
this.handPreset = options?.handPreset ?? "default";
|
|
2129
|
+
this.handColor = options?.handColor ?? "#e8b48a";
|
|
2130
|
+
this.handOpacity = options?.handOpacity ?? 1;
|
|
2131
|
+
this.handEmissiveIntensity = options?.handEmissiveIntensity ?? 0;
|
|
2132
|
+
this.showRay = options?.showRay ?? true;
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
__name(_XRInteractorComponent, "XRInteractorComponent");
|
|
2136
|
+
var XRInteractorComponent = _XRInteractorComponent;
|
|
2137
|
+
var _XRInteractorSideComponent = class _XRInteractorSideComponent extends Component {
|
|
2138
|
+
constructor(options) {
|
|
2139
|
+
super();
|
|
2140
|
+
this.side = options?.side ?? "left";
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
__name(_XRInteractorSideComponent, "XRInteractorSideComponent");
|
|
2144
|
+
var XRInteractorSideComponent = _XRInteractorSideComponent;
|
|
2145
|
+
|
|
2122
2146
|
// core/utils/dracoDecoderPath.ts
|
|
2123
2147
|
var LOCAL_DRACO_DECODER_RELATIVE_PATH = "./assets/draco/";
|
|
2124
2148
|
var DRACO_DECODER_CDN_PATH = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/";
|
|
@@ -4913,6 +4937,14 @@ function buildComponentValueMaps(snapshot) {
|
|
|
4913
4937
|
snapshot.components,
|
|
4914
4938
|
"XRInteractableComponent"
|
|
4915
4939
|
),
|
|
4940
|
+
xrInteractorValues: readByComponent(
|
|
4941
|
+
snapshot.components,
|
|
4942
|
+
"XRInteractorComponent"
|
|
4943
|
+
),
|
|
4944
|
+
xrInteractorSideValues: readByComponent(
|
|
4945
|
+
snapshot.components,
|
|
4946
|
+
"XRInteractorSideComponent"
|
|
4947
|
+
),
|
|
4916
4948
|
instancedMeshValues: readByComponent(snapshot.components, "InstancedMeshComponent"),
|
|
4917
4949
|
instanceSlotValues: readByComponent(snapshot.components, "InstanceSlotComponent"),
|
|
4918
4950
|
prefabNodeValues: readByComponent(snapshot.components, "PrefabNode"),
|
|
@@ -5099,6 +5131,60 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
|
|
|
5099
5131
|
}
|
|
5100
5132
|
}
|
|
5101
5133
|
}
|
|
5134
|
+
{
|
|
5135
|
+
const sourceInteractor = asRecord3(
|
|
5136
|
+
values.xrInteractorValues.get(sourceEntityId)
|
|
5137
|
+
);
|
|
5138
|
+
const interactor = world.getComponent(targetEntityId, XRInteractorComponent);
|
|
5139
|
+
if (sourceInteractor && interactor) {
|
|
5140
|
+
if (typeof sourceInteractor.interactorType === "string") {
|
|
5141
|
+
interactor.interactorType = sourceInteractor.interactorType;
|
|
5142
|
+
}
|
|
5143
|
+
if (typeof sourceInteractor.handPreset === "string") {
|
|
5144
|
+
interactor.handPreset = sourceInteractor.handPreset;
|
|
5145
|
+
}
|
|
5146
|
+
if (typeof sourceInteractor.handColor === "string") {
|
|
5147
|
+
interactor.handColor = sourceInteractor.handColor;
|
|
5148
|
+
}
|
|
5149
|
+
if (typeof sourceInteractor.handOpacity === "number") {
|
|
5150
|
+
interactor.handOpacity = sourceInteractor.handOpacity;
|
|
5151
|
+
}
|
|
5152
|
+
if (typeof sourceInteractor.handEmissiveIntensity === "number") {
|
|
5153
|
+
interactor.handEmissiveIntensity = sourceInteractor.handEmissiveIntensity;
|
|
5154
|
+
}
|
|
5155
|
+
if (typeof sourceInteractor.showRay === "boolean") {
|
|
5156
|
+
interactor.showRay = sourceInteractor.showRay;
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
{
|
|
5161
|
+
const sourceSide = asRecord3(
|
|
5162
|
+
values.xrInteractorSideValues.get(sourceEntityId)
|
|
5163
|
+
);
|
|
5164
|
+
if (sourceSide && typeof sourceSide.side === "string") {
|
|
5165
|
+
const existing = world.getComponent(
|
|
5166
|
+
targetEntityId,
|
|
5167
|
+
XRInteractorSideComponent
|
|
5168
|
+
);
|
|
5169
|
+
if (existing) {
|
|
5170
|
+
existing.side = sourceSide.side;
|
|
5171
|
+
} else {
|
|
5172
|
+
world.addComponent(
|
|
5173
|
+
targetEntityId,
|
|
5174
|
+
new XRInteractorSideComponent({
|
|
5175
|
+
side: sourceSide.side
|
|
5176
|
+
})
|
|
5177
|
+
);
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
{
|
|
5182
|
+
const sourceRigOrigin = asRecord3(values.xrRigValues.get(sourceEntityId));
|
|
5183
|
+
const rigForOrigin = world.getComponent(targetEntityId, XRRigComponent);
|
|
5184
|
+
if (sourceRigOrigin && rigForOrigin && typeof sourceRigOrigin.trackingOrigin === "string") {
|
|
5185
|
+
rigForOrigin.trackingOrigin = sourceRigOrigin.trackingOrigin;
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5102
5188
|
{
|
|
5103
5189
|
const imComponent = world.getComponent(targetEntityId, InstancedMeshComponent);
|
|
5104
5190
|
if (imComponent && imComponent.instanceEntityIds.length > 0) {
|
|
@@ -5688,6 +5774,7 @@ var HYDRATABLE_ENTITY_KINDS = /* @__PURE__ */ new Set([
|
|
|
5688
5774
|
"instanceSlot",
|
|
5689
5775
|
"scriptObject",
|
|
5690
5776
|
"xrRig",
|
|
5777
|
+
"xrInteractor",
|
|
5691
5778
|
"teleportArea"
|
|
5692
5779
|
]);
|
|
5693
5780
|
|
|
@@ -7698,6 +7785,9 @@ var _XRInputSystem = class _XRInputSystem {
|
|
|
7698
7785
|
state.hasAxes = false;
|
|
7699
7786
|
state.buttons = [];
|
|
7700
7787
|
}
|
|
7788
|
+
}
|
|
7789
|
+
for (const record of this.records.values()) {
|
|
7790
|
+
const state = record.state;
|
|
7701
7791
|
if (record.pendingSelectStart) {
|
|
7702
7792
|
state.select = true;
|
|
7703
7793
|
state.selectStart = true;
|
|
@@ -7895,6 +7985,32 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
7895
7985
|
};
|
|
7896
7986
|
__name(_XRInteractionSystem, "XRInteractionSystem");
|
|
7897
7987
|
var XRInteractionSystem = _XRInteractionSystem;
|
|
7988
|
+
var ARC_GRAVITY = 9.8;
|
|
7989
|
+
var ARC_SPEED = 7;
|
|
7990
|
+
var ARC_STEPS = 24;
|
|
7991
|
+
var ARC_MAX_TIME = 2;
|
|
7992
|
+
function sampleTeleportArc(origin, direction, out = [], options) {
|
|
7993
|
+
const speed = options?.speed ?? ARC_SPEED;
|
|
7994
|
+
const gravity = options?.gravity ?? ARC_GRAVITY;
|
|
7995
|
+
const steps = Math.max(2, options?.steps ?? ARC_STEPS);
|
|
7996
|
+
const maxTime = options?.maxTime ?? ARC_MAX_TIME;
|
|
7997
|
+
out.length = 0;
|
|
7998
|
+
const velocity = direction.clone().normalize().multiplyScalar(speed);
|
|
7999
|
+
for (let i = 0; i < steps; i += 1) {
|
|
8000
|
+
const t = i / (steps - 1) * maxTime;
|
|
8001
|
+
out.push(
|
|
8002
|
+
new THREE14__namespace.Vector3(
|
|
8003
|
+
origin.x + velocity.x * t,
|
|
8004
|
+
origin.y + velocity.y * t - 0.5 * gravity * t * t,
|
|
8005
|
+
origin.z + velocity.z * t
|
|
8006
|
+
)
|
|
8007
|
+
);
|
|
8008
|
+
}
|
|
8009
|
+
return out;
|
|
8010
|
+
}
|
|
8011
|
+
__name(sampleTeleportArc, "sampleTeleportArc");
|
|
8012
|
+
|
|
8013
|
+
// core/systems/XRLocomotionSystem.ts
|
|
7898
8014
|
var UP = new THREE14__namespace.Vector3(0, 1, 0);
|
|
7899
8015
|
var AXIS_DEADZONE = 0.5;
|
|
7900
8016
|
var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
@@ -7902,13 +8018,26 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
7902
8018
|
this.raycaster = new THREE14__namespace.Raycaster();
|
|
7903
8019
|
/** Per-source latch so one stick push yields exactly one snap turn. */
|
|
7904
8020
|
this.turnLatched = /* @__PURE__ */ new WeakSet();
|
|
7905
|
-
/**
|
|
7906
|
-
this.
|
|
8021
|
+
/** Reused arc buffer — aiming runs every frame. */
|
|
8022
|
+
this.arcPoints = [];
|
|
8023
|
+
/** Curve up to the landing point, for the visual system. Empty when not aiming. */
|
|
8024
|
+
this.aimCurve = [];
|
|
8025
|
+
this.landingPoint = null;
|
|
8026
|
+
/** True once we've tried to self-heal camera parenting for this session. */
|
|
8027
|
+
this.parentingChecked = false;
|
|
7907
8028
|
this.inputSystem = inputSystem;
|
|
7908
8029
|
}
|
|
7909
8030
|
update(world, deltaTime) {
|
|
7910
|
-
this.
|
|
7911
|
-
|
|
8031
|
+
this.aimCurve = [];
|
|
8032
|
+
this.landingPoint = null;
|
|
8033
|
+
if (!world.isXrPresenting()) {
|
|
8034
|
+
this.parentingChecked = false;
|
|
8035
|
+
return;
|
|
8036
|
+
}
|
|
8037
|
+
if (!this.parentingChecked) {
|
|
8038
|
+
this.parentingChecked = true;
|
|
8039
|
+
this.ensureCameraUnderRig(world);
|
|
8040
|
+
}
|
|
7912
8041
|
const rigEntityId = world.query(XRRigComponent, TransformComponent)[0];
|
|
7913
8042
|
if (typeof rigEntityId !== "number") return;
|
|
7914
8043
|
const rig = world.getComponent(rigEntityId, XRRigComponent);
|
|
@@ -7925,9 +8054,37 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
7925
8054
|
this.applySmoothMove(world, rig, rigTransform, inputs, deltaTime);
|
|
7926
8055
|
}
|
|
7927
8056
|
}
|
|
8057
|
+
/**
|
|
8058
|
+
* Guarantee the render camera is a CHILD of the rig.
|
|
8059
|
+
*
|
|
8060
|
+
* Locomotion moves the rig, and three composes the head pose against
|
|
8061
|
+
* `camera.parent` — so if the camera sits at the scene root instead, moving
|
|
8062
|
+
* the rig does nothing at all and the player is frozen in place. Scenes
|
|
8063
|
+
* authored before the rig existed (or with the camera re-parented by hand)
|
|
8064
|
+
* would otherwise silently fail to move, which is exactly the symptom that is
|
|
8065
|
+
* hardest to diagnose from inside a headset. Repairing it via `ParentId` lets
|
|
8066
|
+
* `ObjectManagementSystem` reconcile the scene graph normally.
|
|
8067
|
+
*/
|
|
8068
|
+
ensureCameraUnderRig(world) {
|
|
8069
|
+
const rigEntityId = world.query(XRRigComponent)[0];
|
|
8070
|
+
if (typeof rigEntityId !== "number") return;
|
|
8071
|
+
const activeCamera = world.getCamera();
|
|
8072
|
+
for (const entityId of world.query(CameraComponent, Object3DRef)) {
|
|
8073
|
+
if (world.getComponent(entityId, Object3DRef)?.object !== activeCamera) {
|
|
8074
|
+
continue;
|
|
8075
|
+
}
|
|
8076
|
+
const parent = world.getComponent(entityId, ParentId);
|
|
8077
|
+
if (parent) {
|
|
8078
|
+
if (parent.value !== rigEntityId) parent.value = rigEntityId;
|
|
8079
|
+
} else {
|
|
8080
|
+
world.addComponent(entityId, new ParentId(rigEntityId));
|
|
8081
|
+
}
|
|
8082
|
+
return;
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
7928
8085
|
/**
|
|
7929
8086
|
* Snap turning, not smooth turning — rotating the view continuously is the
|
|
7930
|
-
* single biggest cause of VR nausea, so the comfort-safe option is
|
|
8087
|
+
* single biggest cause of VR nausea, so the comfort-safe option is what's
|
|
7931
8088
|
* wired to the stick.
|
|
7932
8089
|
*/
|
|
7933
8090
|
applySnapTurn(rig, transform, inputs) {
|
|
@@ -7941,8 +8098,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
7941
8098
|
}
|
|
7942
8099
|
if (this.turnLatched.has(source)) continue;
|
|
7943
8100
|
this.turnLatched.add(source);
|
|
7944
|
-
|
|
7945
|
-
transform.rotation.y -= Math.sign(x) * step;
|
|
8101
|
+
transform.rotation.y -= Math.sign(x) * THREE14__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
|
|
7946
8102
|
}
|
|
7947
8103
|
}
|
|
7948
8104
|
applySmoothMove(world, rig, transform, inputs, deltaTime) {
|
|
@@ -7967,16 +8123,41 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
7967
8123
|
const targets = this.collectTeleportTargets(world);
|
|
7968
8124
|
if (targets.length === 0) return;
|
|
7969
8125
|
for (const input of inputs) {
|
|
7970
|
-
|
|
7971
|
-
const
|
|
8126
|
+
if (!input.select && !input.selectEnd) continue;
|
|
8127
|
+
const points = sampleTeleportArc(
|
|
8128
|
+
input.rayOrigin,
|
|
8129
|
+
input.rayDirection,
|
|
8130
|
+
this.arcPoints
|
|
8131
|
+
);
|
|
8132
|
+
const hit = this.raycastArc(points, targets);
|
|
7972
8133
|
if (!hit) continue;
|
|
7973
|
-
this.
|
|
8134
|
+
this.landingPoint = hit.point;
|
|
8135
|
+
this.aimCurve = points.slice(0, hit.segmentIndex + 1).concat(hit.point);
|
|
7974
8136
|
if (input.selectEnd) {
|
|
7975
8137
|
transform.position.x = hit.point.x;
|
|
7976
8138
|
transform.position.y = hit.point.y;
|
|
7977
8139
|
transform.position.z = hit.point.z;
|
|
8140
|
+
this.aimCurve = [];
|
|
8141
|
+
this.landingPoint = null;
|
|
8142
|
+
}
|
|
8143
|
+
}
|
|
8144
|
+
}
|
|
8145
|
+
/** Walk the arc segment-by-segment, returning the first surface hit. */
|
|
8146
|
+
raycastArc(points, targets) {
|
|
8147
|
+
for (let i = 1; i < points.length; i += 1) {
|
|
8148
|
+
const from = points[i - 1];
|
|
8149
|
+
const to = points[i];
|
|
8150
|
+
const delta = new THREE14__namespace.Vector3().subVectors(to, from);
|
|
8151
|
+
const distance = delta.length();
|
|
8152
|
+
if (distance < 1e-6) continue;
|
|
8153
|
+
this.raycaster.set(from, delta.divideScalar(distance));
|
|
8154
|
+
this.raycaster.far = distance;
|
|
8155
|
+
const hit = this.raycaster.intersectObjects(targets, true)[0];
|
|
8156
|
+
if (hit) {
|
|
8157
|
+
return { point: hit.point.clone(), segmentIndex: i - 1 };
|
|
7978
8158
|
}
|
|
7979
8159
|
}
|
|
8160
|
+
return null;
|
|
7980
8161
|
}
|
|
7981
8162
|
collectTeleportTargets(world) {
|
|
7982
8163
|
const objects = [];
|
|
@@ -7989,12 +8170,13 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
7989
8170
|
}
|
|
7990
8171
|
return objects;
|
|
7991
8172
|
}
|
|
7992
|
-
/**
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
8173
|
+
/** Arc points up to the landing spot this frame; empty when not aiming. */
|
|
8174
|
+
getAimCurve() {
|
|
8175
|
+
return this.aimCurve;
|
|
8176
|
+
}
|
|
8177
|
+
/** Where the arc currently lands, or null when not aiming at a valid area. */
|
|
8178
|
+
getLandingPoint() {
|
|
8179
|
+
return this.landingPoint;
|
|
7998
8180
|
}
|
|
7999
8181
|
};
|
|
8000
8182
|
__name(_XRLocomotionSystem, "XRLocomotionSystem");
|
|
@@ -8034,6 +8216,8 @@ var _XRSessionSystem = class _XRSessionSystem {
|
|
|
8034
8216
|
constructor(inputSystem) {
|
|
8035
8217
|
this.presenting = false;
|
|
8036
8218
|
this.capabilities = NO_XR_CAPABILITIES;
|
|
8219
|
+
/** Y compensation applied when a floor-relative space was requested but not granted. */
|
|
8220
|
+
this.appliedOriginOffset = 0;
|
|
8037
8221
|
this.inputSystem = inputSystem ?? null;
|
|
8038
8222
|
}
|
|
8039
8223
|
update(world) {
|
|
@@ -8057,11 +8241,59 @@ var _XRSessionSystem = class _XRSessionSystem {
|
|
|
8057
8241
|
if (session && this.inputSystem) {
|
|
8058
8242
|
this.inputSystem.attachSession(session);
|
|
8059
8243
|
}
|
|
8244
|
+
this.applyTrackingOrigin(world, session ?? null);
|
|
8060
8245
|
this.forEachBehaviour(world, (behaviour) => {
|
|
8061
8246
|
behaviour.onXRSessionStart(this.capabilities);
|
|
8062
8247
|
});
|
|
8063
8248
|
}
|
|
8249
|
+
/**
|
|
8250
|
+
* Apply the rig's authored tracking origin, and compensate when the device
|
|
8251
|
+
* cannot honour it.
|
|
8252
|
+
*
|
|
8253
|
+
* `floor` (`local-floor`) puts Y=0 on the user's real floor, which is what a
|
|
8254
|
+
* scene authored standing on the ground expects. `eye` (`local`) puts Y=0 at
|
|
8255
|
+
* the headset, for seated experiences.
|
|
8256
|
+
*
|
|
8257
|
+
* The compensation matters: `local-floor` is only ever REQUESTED as an
|
|
8258
|
+
* optional feature (requiring it would make `requestSession` reject outright
|
|
8259
|
+
* on devices that lack it). When it isn't granted, the runtime silently falls
|
|
8260
|
+
* back to `local` — the origin jumps to head height and a floor-authored
|
|
8261
|
+
* scene swallows the user, which is exactly the "I'm standing under the
|
|
8262
|
+
* building" symptom. Offsetting the rig down by the eye height restores the
|
|
8263
|
+
* intended floor relationship instead of leaving it broken.
|
|
8264
|
+
*/
|
|
8265
|
+
applyTrackingOrigin(world, session) {
|
|
8266
|
+
const rigEntityId = world.query(XRRigComponent, TransformComponent)[0];
|
|
8267
|
+
if (typeof rigEntityId !== "number") return;
|
|
8268
|
+
const rig = world.getComponent(rigEntityId, XRRigComponent);
|
|
8269
|
+
const transform = world.getComponent(rigEntityId, TransformComponent);
|
|
8270
|
+
if (!rig || !transform) return;
|
|
8271
|
+
const renderer = world.getRenderer();
|
|
8272
|
+
const wantsFloor = rig.trackingOrigin === "floor";
|
|
8273
|
+
try {
|
|
8274
|
+
renderer.xr.setReferenceSpaceType(wantsFloor ? "local-floor" : "local");
|
|
8275
|
+
} catch {
|
|
8276
|
+
}
|
|
8277
|
+
const granted = session?.enabledFeatures;
|
|
8278
|
+
const floorGranted = Array.isArray(granted) ? granted.includes("local-floor") : (
|
|
8279
|
+
// No report at all: trust the request, since a wrong offset is worse
|
|
8280
|
+
// than none on devices that did honour it.
|
|
8281
|
+
true
|
|
8282
|
+
);
|
|
8283
|
+
this.appliedOriginOffset = wantsFloor && !floorGranted ? rig.fallbackEyeHeight : 0;
|
|
8284
|
+
if (this.appliedOriginOffset !== 0) {
|
|
8285
|
+
transform.position.y -= this.appliedOriginOffset;
|
|
8286
|
+
}
|
|
8287
|
+
}
|
|
8064
8288
|
handleSessionEnd(world) {
|
|
8289
|
+
if (this.appliedOriginOffset !== 0) {
|
|
8290
|
+
const rigEntityId = world.query(XRRigComponent, TransformComponent)[0];
|
|
8291
|
+
if (typeof rigEntityId === "number") {
|
|
8292
|
+
const transform = world.getComponent(rigEntityId, TransformComponent);
|
|
8293
|
+
if (transform) transform.position.y += this.appliedOriginOffset;
|
|
8294
|
+
}
|
|
8295
|
+
this.appliedOriginOffset = 0;
|
|
8296
|
+
}
|
|
8065
8297
|
this.capabilities = NO_XR_CAPABILITIES;
|
|
8066
8298
|
this.inputSystem?.detachSession();
|
|
8067
8299
|
this.forEachBehaviour(world, (behaviour) => {
|
|
@@ -8155,16 +8387,75 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
|
|
|
8155
8387
|
};
|
|
8156
8388
|
__name(_VRTeleportAreaSystem, "VRTeleportAreaSystem");
|
|
8157
8389
|
var VRTeleportAreaSystem = _VRTeleportAreaSystem;
|
|
8390
|
+
function createHandMaterial(preset, options) {
|
|
8391
|
+
const color = new THREE14__namespace.Color(options.color);
|
|
8392
|
+
switch (preset) {
|
|
8393
|
+
case "ghost":
|
|
8394
|
+
return new THREE14__namespace.MeshBasicMaterial({
|
|
8395
|
+
color,
|
|
8396
|
+
transparent: true,
|
|
8397
|
+
opacity: Math.min(1, Math.max(0.05, options.opacity * 0.45)),
|
|
8398
|
+
depthWrite: false,
|
|
8399
|
+
side: THREE14__namespace.DoubleSide
|
|
8400
|
+
});
|
|
8401
|
+
case "neon":
|
|
8402
|
+
return new THREE14__namespace.MeshStandardMaterial({
|
|
8403
|
+
color,
|
|
8404
|
+
emissive: color,
|
|
8405
|
+
emissiveIntensity: Math.max(0.5, options.emissiveIntensity || 2),
|
|
8406
|
+
roughness: 0.35,
|
|
8407
|
+
metalness: 0,
|
|
8408
|
+
transparent: options.opacity < 1,
|
|
8409
|
+
opacity: options.opacity
|
|
8410
|
+
});
|
|
8411
|
+
case "skeleton":
|
|
8412
|
+
return new THREE14__namespace.MeshBasicMaterial({
|
|
8413
|
+
color,
|
|
8414
|
+
wireframe: true,
|
|
8415
|
+
transparent: true,
|
|
8416
|
+
opacity: options.opacity
|
|
8417
|
+
});
|
|
8418
|
+
case "custom":
|
|
8419
|
+
case "default":
|
|
8420
|
+
return new THREE14__namespace.MeshStandardMaterial({
|
|
8421
|
+
color,
|
|
8422
|
+
roughness: 0.85,
|
|
8423
|
+
metalness: 0,
|
|
8424
|
+
emissive: color,
|
|
8425
|
+
emissiveIntensity: Math.max(0, options.emissiveIntensity),
|
|
8426
|
+
transparent: options.opacity < 1,
|
|
8427
|
+
opacity: options.opacity,
|
|
8428
|
+
side: THREE14__namespace.DoubleSide
|
|
8429
|
+
});
|
|
8430
|
+
default: {
|
|
8431
|
+
return new THREE14__namespace.MeshStandardMaterial({ color });
|
|
8432
|
+
}
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8435
|
+
__name(createHandMaterial, "createHandMaterial");
|
|
8436
|
+
function applyHandMaterial(root, material) {
|
|
8437
|
+
root.traverse((child) => {
|
|
8438
|
+
const mesh = child;
|
|
8439
|
+
if (!mesh.isMesh) return;
|
|
8440
|
+
mesh.material = material;
|
|
8441
|
+
});
|
|
8442
|
+
}
|
|
8443
|
+
__name(applyHandMaterial, "applyHandMaterial");
|
|
8444
|
+
var SLOTS = [
|
|
8445
|
+
{ index: 0, side: "left" },
|
|
8446
|
+
{ index: 1, side: "right" }
|
|
8447
|
+
];
|
|
8158
8448
|
var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
8159
|
-
constructor(
|
|
8449
|
+
constructor(options) {
|
|
8160
8450
|
this.controllerModelFactory = new XRControllerModelFactory_js.XRControllerModelFactory();
|
|
8161
8451
|
this.handModelFactory = new XRHandModelFactory_js.XRHandModelFactory();
|
|
8162
8452
|
this.group = new THREE14__namespace.Group();
|
|
8163
|
-
this.
|
|
8453
|
+
this.slots = /* @__PURE__ */ new Map();
|
|
8164
8454
|
this.attachedTo = null;
|
|
8165
|
-
/**
|
|
8166
|
-
this.
|
|
8167
|
-
this.
|
|
8455
|
+
/** Last applied preset signature, so materials rebuild only on real change. */
|
|
8456
|
+
this.handStyleSignature = "";
|
|
8457
|
+
this.handMaterial = null;
|
|
8458
|
+
this.interactionSystem = options?.interactionSystem ?? null;
|
|
8168
8459
|
this.group.name = "XRControllerVisuals";
|
|
8169
8460
|
this.group.userData.ecsPreviewHidden = true;
|
|
8170
8461
|
}
|
|
@@ -8172,23 +8463,109 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
8172
8463
|
const renderer = world.getRenderer();
|
|
8173
8464
|
if (!renderer.xr?.enabled) return;
|
|
8174
8465
|
this.ensureAttached(world);
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8466
|
+
this.ensureSlots(world);
|
|
8467
|
+
const interactor = this.resolveInteractor(world);
|
|
8468
|
+
const type = interactor?.interactorType ?? "both";
|
|
8469
|
+
const showControllers = type === "controllers" || type === "both";
|
|
8470
|
+
const showHands = type === "hands" || type === "both";
|
|
8471
|
+
this.syncHandMaterial(interactor);
|
|
8472
|
+
for (const { index, side } of SLOTS) {
|
|
8473
|
+
const visuals = this.slots.get(index);
|
|
8474
|
+
if (!visuals) continue;
|
|
8475
|
+
visuals.controllerModel.visible = showControllers;
|
|
8476
|
+
visuals.handModel.visible = showHands;
|
|
8477
|
+
visuals.ray.visible = this.shouldShowRay(interactor);
|
|
8478
|
+
if (showHands && this.handMaterial) {
|
|
8479
|
+
applyHandMaterial(visuals.handModel, this.handMaterial);
|
|
8480
|
+
}
|
|
8481
|
+
this.syncCustomContent(world, visuals, side);
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
/** The scene's interactor config, if one was authored. */
|
|
8485
|
+
resolveInteractor(world) {
|
|
8486
|
+
const entityId = world.query(XRInteractorComponent)[0];
|
|
8487
|
+
if (typeof entityId !== "number") return null;
|
|
8488
|
+
return world.getComponent(entityId, XRInteractorComponent) ?? null;
|
|
8489
|
+
}
|
|
8490
|
+
/**
|
|
8491
|
+
* Re-parent the author's left/right interactor content onto the tracked grip
|
|
8492
|
+
* space so it follows the hand.
|
|
8493
|
+
*
|
|
8494
|
+
* `ObjectManagementSystem` reconciles ECS objects back under their ParentId
|
|
8495
|
+
* every frame, so this re-attach has to be re-asserted — the same coexistence
|
|
8496
|
+
* dance the AR anchor system performs.
|
|
8497
|
+
*/
|
|
8498
|
+
syncCustomContent(world, visuals, side) {
|
|
8499
|
+
let content = null;
|
|
8500
|
+
for (const entityId of world.query(
|
|
8501
|
+
XRInteractorSideComponent,
|
|
8502
|
+
Object3DRef
|
|
8503
|
+
)) {
|
|
8504
|
+
const slot = world.getComponent(entityId, XRInteractorSideComponent);
|
|
8505
|
+
if (slot?.side !== side) continue;
|
|
8506
|
+
content = world.getComponent(entityId, Object3DRef)?.object ?? null;
|
|
8507
|
+
break;
|
|
8508
|
+
}
|
|
8509
|
+
if (!content) {
|
|
8510
|
+
visuals.attachedCustom = null;
|
|
8511
|
+
return;
|
|
8512
|
+
}
|
|
8513
|
+
if (content.parent !== visuals.grip) {
|
|
8514
|
+
visuals.grip.add(content);
|
|
8515
|
+
}
|
|
8516
|
+
visuals.attachedCustom = content;
|
|
8517
|
+
}
|
|
8518
|
+
/** Rebuild the hand material only when the authored style actually changes. */
|
|
8519
|
+
syncHandMaterial(interactor) {
|
|
8520
|
+
const preset = interactor?.handPreset ?? "default";
|
|
8521
|
+
const color = interactor?.handColor ?? "#e8b48a";
|
|
8522
|
+
const opacity = interactor?.handOpacity ?? 1;
|
|
8523
|
+
const emissive = interactor?.handEmissiveIntensity ?? 0;
|
|
8524
|
+
const signature = `${preset}:${color}:${opacity}:${emissive}`;
|
|
8525
|
+
if (signature === this.handStyleSignature && this.handMaterial) return;
|
|
8526
|
+
this.handStyleSignature = signature;
|
|
8527
|
+
this.handMaterial?.dispose();
|
|
8528
|
+
this.handMaterial = createHandMaterial(preset, {
|
|
8529
|
+
color,
|
|
8530
|
+
opacity,
|
|
8531
|
+
emissiveIntensity: emissive
|
|
8532
|
+
});
|
|
8533
|
+
}
|
|
8534
|
+
/**
|
|
8535
|
+
* The ray is meaningful only when it points at something. Authors can turn it
|
|
8536
|
+
* off entirely; otherwise it appears on hover/grab so it isn't permanent
|
|
8537
|
+
* clutter implying the whole scene is clickable.
|
|
8538
|
+
*/
|
|
8539
|
+
shouldShowRay(interactor) {
|
|
8540
|
+
if (interactor && !interactor.showRay) return false;
|
|
8541
|
+
if (!this.interactionSystem) return true;
|
|
8542
|
+
return this.interactionSystem.getHoveredEntityId() !== null || this.interactionSystem.isGrabbing();
|
|
8543
|
+
}
|
|
8544
|
+
/** Lazily build per-slot visuals; the device reports profiles late. */
|
|
8545
|
+
ensureSlots(world) {
|
|
8546
|
+
const renderer = world.getRenderer();
|
|
8547
|
+
for (const { index } of SLOTS) {
|
|
8548
|
+
if (this.slots.has(index)) continue;
|
|
8178
8549
|
const controller = renderer.xr.getController(index);
|
|
8179
8550
|
const grip = renderer.xr.getControllerGrip(index);
|
|
8180
8551
|
const hand = renderer.xr.getHand(index);
|
|
8181
8552
|
if (!controller || !grip || !hand) continue;
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
this.
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8553
|
+
const ray = this.createRay();
|
|
8554
|
+
controller.add(ray);
|
|
8555
|
+
const controllerModel = this.controllerModelFactory.createControllerModel(grip);
|
|
8556
|
+
grip.add(controllerModel);
|
|
8557
|
+
const handModel = this.handModelFactory.createHandModel(hand, "mesh");
|
|
8558
|
+
hand.add(handModel);
|
|
8559
|
+
this.group.add(controller, grip, hand);
|
|
8560
|
+
this.slots.set(index, {
|
|
8561
|
+
controller,
|
|
8562
|
+
grip,
|
|
8563
|
+
hand,
|
|
8564
|
+
ray,
|
|
8565
|
+
controllerModel,
|
|
8566
|
+
handModel,
|
|
8567
|
+
attachedCustom: null
|
|
8568
|
+
});
|
|
8192
8569
|
}
|
|
8193
8570
|
}
|
|
8194
8571
|
/**
|
|
@@ -8216,6 +8593,7 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
8216
8593
|
const line = new THREE14__namespace.Line(geometry, material);
|
|
8217
8594
|
line.name = "XRPointerRay";
|
|
8218
8595
|
line.renderOrder = 999;
|
|
8596
|
+
line.visible = false;
|
|
8219
8597
|
return line;
|
|
8220
8598
|
}
|
|
8221
8599
|
dispose() {
|
|
@@ -8231,13 +8609,130 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
|
|
|
8231
8609
|
}
|
|
8232
8610
|
});
|
|
8233
8611
|
this.group.clear();
|
|
8234
|
-
this.
|
|
8235
|
-
this.
|
|
8612
|
+
this.slots.clear();
|
|
8613
|
+
this.handMaterial?.dispose();
|
|
8614
|
+
this.handMaterial = null;
|
|
8615
|
+
this.handStyleSignature = "";
|
|
8236
8616
|
this.attachedTo = null;
|
|
8237
8617
|
}
|
|
8238
8618
|
};
|
|
8239
8619
|
__name(_XRControllerVisualSystem, "XRControllerVisualSystem");
|
|
8240
8620
|
var XRControllerVisualSystem = _XRControllerVisualSystem;
|
|
8621
|
+
var MARKER_RADIUS = 0.28;
|
|
8622
|
+
var ACCENT = new THREE14__namespace.Color("#4da3ff");
|
|
8623
|
+
var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
|
|
8624
|
+
constructor(locomotionSystem) {
|
|
8625
|
+
this.group = new THREE14__namespace.Group();
|
|
8626
|
+
this.arcGeometry = new THREE14__namespace.BufferGeometry();
|
|
8627
|
+
this.marker = new THREE14__namespace.Group();
|
|
8628
|
+
this.attached = false;
|
|
8629
|
+
this.locomotionSystem = locomotionSystem;
|
|
8630
|
+
this.group.name = "XRTeleportVisuals";
|
|
8631
|
+
this.group.userData.ecsPreviewHidden = true;
|
|
8632
|
+
const arcMaterial = new THREE14__namespace.LineBasicMaterial({
|
|
8633
|
+
vertexColors: true,
|
|
8634
|
+
transparent: true,
|
|
8635
|
+
depthTest: false
|
|
8636
|
+
});
|
|
8637
|
+
this.arc = new THREE14__namespace.Line(this.arcGeometry, arcMaterial);
|
|
8638
|
+
this.arc.frustumCulled = false;
|
|
8639
|
+
this.arc.renderOrder = 998;
|
|
8640
|
+
this.group.add(this.arc);
|
|
8641
|
+
this.marker.add(this.createRing(), this.createDot());
|
|
8642
|
+
this.marker.renderOrder = 999;
|
|
8643
|
+
this.group.add(this.marker);
|
|
8644
|
+
this.setVisible(false);
|
|
8645
|
+
}
|
|
8646
|
+
update(world) {
|
|
8647
|
+
if (!this.attached) {
|
|
8648
|
+
world.getScene().add(this.group);
|
|
8649
|
+
this.attached = true;
|
|
8650
|
+
}
|
|
8651
|
+
const curve = this.locomotionSystem.getAimCurve();
|
|
8652
|
+
const landing = this.locomotionSystem.getLandingPoint();
|
|
8653
|
+
if (!landing || curve.length < 2) {
|
|
8654
|
+
this.setVisible(false);
|
|
8655
|
+
return;
|
|
8656
|
+
}
|
|
8657
|
+
this.updateArcGeometry(curve);
|
|
8658
|
+
this.marker.position.copy(landing);
|
|
8659
|
+
this.marker.position.y += 0.01;
|
|
8660
|
+
this.setVisible(true);
|
|
8661
|
+
}
|
|
8662
|
+
updateArcGeometry(curve) {
|
|
8663
|
+
const count = curve.length;
|
|
8664
|
+
const positions = new Float32Array(count * 3);
|
|
8665
|
+
const colors = new Float32Array(count * 3);
|
|
8666
|
+
for (let i = 0; i < count; i += 1) {
|
|
8667
|
+
const point = curve[i];
|
|
8668
|
+
positions[i * 3] = point.x;
|
|
8669
|
+
positions[i * 3 + 1] = point.y;
|
|
8670
|
+
positions[i * 3 + 2] = point.z;
|
|
8671
|
+
const t = count > 1 ? i / (count - 1) : 0;
|
|
8672
|
+
const fade = 1 - t * 0.75;
|
|
8673
|
+
colors[i * 3] = ACCENT.r * fade;
|
|
8674
|
+
colors[i * 3 + 1] = ACCENT.g * fade;
|
|
8675
|
+
colors[i * 3 + 2] = ACCENT.b * fade;
|
|
8676
|
+
}
|
|
8677
|
+
this.arcGeometry.setAttribute(
|
|
8678
|
+
"position",
|
|
8679
|
+
new THREE14__namespace.BufferAttribute(positions, 3)
|
|
8680
|
+
);
|
|
8681
|
+
this.arcGeometry.setAttribute("color", new THREE14__namespace.BufferAttribute(colors, 3));
|
|
8682
|
+
this.arcGeometry.computeBoundingSphere();
|
|
8683
|
+
}
|
|
8684
|
+
createRing() {
|
|
8685
|
+
const geometry = new THREE14__namespace.RingGeometry(
|
|
8686
|
+
MARKER_RADIUS * 0.72,
|
|
8687
|
+
MARKER_RADIUS,
|
|
8688
|
+
48
|
|
8689
|
+
);
|
|
8690
|
+
geometry.rotateX(-Math.PI / 2);
|
|
8691
|
+
return new THREE14__namespace.Mesh(
|
|
8692
|
+
geometry,
|
|
8693
|
+
new THREE14__namespace.MeshBasicMaterial({
|
|
8694
|
+
color: ACCENT,
|
|
8695
|
+
transparent: true,
|
|
8696
|
+
opacity: 0.9,
|
|
8697
|
+
side: THREE14__namespace.DoubleSide,
|
|
8698
|
+
depthTest: false
|
|
8699
|
+
})
|
|
8700
|
+
);
|
|
8701
|
+
}
|
|
8702
|
+
createDot() {
|
|
8703
|
+
const geometry = new THREE14__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
|
|
8704
|
+
geometry.rotateX(-Math.PI / 2);
|
|
8705
|
+
return new THREE14__namespace.Mesh(
|
|
8706
|
+
geometry,
|
|
8707
|
+
new THREE14__namespace.MeshBasicMaterial({
|
|
8708
|
+
color: ACCENT,
|
|
8709
|
+
transparent: true,
|
|
8710
|
+
opacity: 0.55,
|
|
8711
|
+
side: THREE14__namespace.DoubleSide,
|
|
8712
|
+
depthTest: false
|
|
8713
|
+
})
|
|
8714
|
+
);
|
|
8715
|
+
}
|
|
8716
|
+
setVisible(visible) {
|
|
8717
|
+
this.group.visible = visible;
|
|
8718
|
+
}
|
|
8719
|
+
dispose() {
|
|
8720
|
+
this.group.removeFromParent();
|
|
8721
|
+
this.group.traverse((child) => {
|
|
8722
|
+
const mesh = child;
|
|
8723
|
+
mesh.geometry?.dispose?.();
|
|
8724
|
+
const material = mesh.material;
|
|
8725
|
+
if (Array.isArray(material)) {
|
|
8726
|
+
for (const entry of material) entry.dispose?.();
|
|
8727
|
+
} else {
|
|
8728
|
+
material?.dispose?.();
|
|
8729
|
+
}
|
|
8730
|
+
});
|
|
8731
|
+
this.attached = false;
|
|
8732
|
+
}
|
|
8733
|
+
};
|
|
8734
|
+
__name(_XRTeleportVisualSystem, "XRTeleportVisualSystem");
|
|
8735
|
+
var XRTeleportVisualSystem = _XRTeleportVisualSystem;
|
|
8241
8736
|
|
|
8242
8737
|
exports.AnimationSystem = AnimationSystem;
|
|
8243
8738
|
exports.AudioSystem = AudioSystem;
|
|
@@ -8265,5 +8760,6 @@ exports.XRInputSystem = XRInputSystem;
|
|
|
8265
8760
|
exports.XRInteractionSystem = XRInteractionSystem;
|
|
8266
8761
|
exports.XRLocomotionSystem = XRLocomotionSystem;
|
|
8267
8762
|
exports.XRSessionSystem = XRSessionSystem;
|
|
8763
|
+
exports.XRTeleportVisualSystem = XRTeleportVisualSystem;
|
|
8268
8764
|
//# sourceMappingURL=index.cjs.map
|
|
8269
8765
|
//# sourceMappingURL=index.cjs.map
|