@relu-interactives/spatial-ecs 0.4.5 → 0.5.1
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-ln4Q7Eot.d.cts → System-V_mv07le.d.cts} +2 -2
- package/dist/{System-ln4Q7Eot.d.ts → System-V_mv07le.d.ts} +2 -2
- package/dist/{XRHandPose-CxPv-LBo.d.cts → XRHandPose-CQTLGxFC.d.cts} +218 -2
- package/dist/{XRHandPose-BImIa4ZS.d.ts → XRHandPose-CU3vNFuS.d.ts} +218 -2
- package/dist/{chunk-4AK4I3QF.js → chunk-BUEKOSR5.js} +102 -3
- package/dist/chunk-BUEKOSR5.js.map +1 -0
- package/dist/{chunk-QIIWFP3V.js → chunk-Q52ADLF7.js} +1255 -18
- package/dist/chunk-Q52ADLF7.js.map +1 -0
- package/dist/{chunk-SGADE55G.js → chunk-RXBLOO5B.js} +249 -90
- package/dist/chunk-RXBLOO5B.js.map +1 -0
- package/dist/{chunk-4DWZK4X4.js → chunk-WJD2VKEH.js} +3 -3
- package/dist/chunk-WJD2VKEH.js.map +1 -0
- package/dist/components/index.cjs +99 -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/docs/guide/spatial-ui.md +165 -0
- package/dist/factories/index.cjs +372 -114
- package/dist/factories/index.cjs.map +1 -1
- package/dist/factories/index.d.cts +13 -3
- package/dist/factories/index.d.ts +13 -3
- package/dist/factories/index.js +3 -3
- package/dist/{index-CuGqxuk-.d.ts → index-C3ik3sA1.d.ts} +16 -3
- package/dist/{index-BphKHaV-.d.ts → index-D2Jt_JzH.d.ts} +156 -3
- package/dist/{index-CH7qMsQT.d.cts → index-DaG03vyN.d.cts} +156 -3
- package/dist/{index-CQsHUez3.d.cts → index-Dlr-7-4m.d.cts} +16 -3
- package/dist/index.cjs +1864 -360
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -9
- package/dist/index.d.ts +42 -9
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/dist/systems/index.cjs +1346 -13
- 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-4AK4I3QF.js.map +0 -1
- package/dist/chunk-4DWZK4X4.js.map +0 -1
- package/dist/chunk-QIIWFP3V.js.map +0 -1
- package/dist/chunk-SGADE55G.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-
|
|
2
|
-
import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ParentId, ScriptComponent, ScriptObjectComponent, XRRigComponent, VRTeleportAreaComponent, XRInteractableComponent, createAttachPoint, XRHandPoseComponent, createHandPoseEntry, XRAnchorComponent, XRInteractorComponent, XRInteractorSideComponent, InstanceSlotComponent, Name, ImageComponent, SpriteComponent, VideoComponent, AudioComponent, ImageTarget, Selectable, PrefabNode, PrefabInstance, XREnvironmentComponent, handPoseDefinitionFor, getHandPosePreset } from './chunk-
|
|
1
|
+
import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-WJD2VKEH.js';
|
|
2
|
+
import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ParentId, ScriptComponent, ScriptObjectComponent, XRRigComponent, VRTeleportAreaComponent, XRInteractableComponent, createAttachPoint, XRHandPoseComponent, createHandPoseEntry, XRAnchorComponent, UIPanelComponent, UITextComponent, UIButtonComponent, UIToggleComponent, UISliderComponent, XRInteractorComponent, XRInteractorSideComponent, InstanceSlotComponent, Name, ImageComponent, SpriteComponent, VideoComponent, AudioComponent, ImageTarget, Selectable, PrefabNode, PrefabInstance, XREnvironmentComponent, handPoseDefinitionFor, getHandPosePreset } from './chunk-BUEKOSR5.js';
|
|
3
3
|
import { __name } from './chunk-OY7ZODZP.js';
|
|
4
4
|
import * as THREE19 from 'three';
|
|
5
5
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
@@ -2848,6 +2848,11 @@ function buildComponentValueMaps(snapshot) {
|
|
|
2848
2848
|
"XRHandPoseComponent"
|
|
2849
2849
|
),
|
|
2850
2850
|
xrAnchorValues: readByComponent(snapshot.components, "XRAnchorComponent"),
|
|
2851
|
+
uiPanelValues: readByComponent(snapshot.components, "UIPanelComponent"),
|
|
2852
|
+
uiTextValues: readByComponent(snapshot.components, "UITextComponent"),
|
|
2853
|
+
uiButtonValues: readByComponent(snapshot.components, "UIButtonComponent"),
|
|
2854
|
+
uiToggleValues: readByComponent(snapshot.components, "UIToggleComponent"),
|
|
2855
|
+
uiSliderValues: readByComponent(snapshot.components, "UISliderComponent"),
|
|
2851
2856
|
xrInteractorSideValues: readByComponent(
|
|
2852
2857
|
snapshot.components,
|
|
2853
2858
|
"XRInteractorSideComponent"
|
|
@@ -3169,6 +3174,49 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
|
|
|
3169
3174
|
}
|
|
3170
3175
|
}
|
|
3171
3176
|
}
|
|
3177
|
+
{
|
|
3178
|
+
const restoreUi = /* @__PURE__ */ __name((map, Ctor, existing) => {
|
|
3179
|
+
const source = asRecord(map.get(sourceEntityId));
|
|
3180
|
+
if (!source) return;
|
|
3181
|
+
let component = existing;
|
|
3182
|
+
if (!component) {
|
|
3183
|
+
component = new Ctor();
|
|
3184
|
+
world.addComponent(targetEntityId, component);
|
|
3185
|
+
}
|
|
3186
|
+
const target = component;
|
|
3187
|
+
for (const [key, value] of Object.entries(source)) {
|
|
3188
|
+
const current = target[key];
|
|
3189
|
+
if ((typeof value === "string" || typeof value === "number" || typeof value === "boolean") && typeof current === typeof value) {
|
|
3190
|
+
target[key] = value;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}, "restoreUi");
|
|
3194
|
+
restoreUi(
|
|
3195
|
+
values.uiPanelValues,
|
|
3196
|
+
UIPanelComponent,
|
|
3197
|
+
world.getComponent(targetEntityId, UIPanelComponent)
|
|
3198
|
+
);
|
|
3199
|
+
restoreUi(
|
|
3200
|
+
values.uiTextValues,
|
|
3201
|
+
UITextComponent,
|
|
3202
|
+
world.getComponent(targetEntityId, UITextComponent)
|
|
3203
|
+
);
|
|
3204
|
+
restoreUi(
|
|
3205
|
+
values.uiButtonValues,
|
|
3206
|
+
UIButtonComponent,
|
|
3207
|
+
world.getComponent(targetEntityId, UIButtonComponent)
|
|
3208
|
+
);
|
|
3209
|
+
restoreUi(
|
|
3210
|
+
values.uiToggleValues,
|
|
3211
|
+
UIToggleComponent,
|
|
3212
|
+
world.getComponent(targetEntityId, UIToggleComponent)
|
|
3213
|
+
);
|
|
3214
|
+
restoreUi(
|
|
3215
|
+
values.uiSliderValues,
|
|
3216
|
+
UISliderComponent,
|
|
3217
|
+
world.getComponent(targetEntityId, UISliderComponent)
|
|
3218
|
+
);
|
|
3219
|
+
}
|
|
3172
3220
|
{
|
|
3173
3221
|
const sourceInteractor = asRecord(
|
|
3174
3222
|
values.xrInteractorValues.get(sourceEntityId)
|
|
@@ -5134,6 +5182,23 @@ var _Behaviour = class _Behaviour {
|
|
|
5134
5182
|
/** Called when this entity is released. Optional XR hook. */
|
|
5135
5183
|
onRelease(input) {
|
|
5136
5184
|
}
|
|
5185
|
+
/** A UI button on this entity (or a descendant) was clicked. Clicks fire on
|
|
5186
|
+
* RELEASE while still pointing at the widget — sliding off cancels. */
|
|
5187
|
+
onUiPress(_event) {
|
|
5188
|
+
}
|
|
5189
|
+
/** A pointer began hovering a UI widget on this entity (or a descendant). */
|
|
5190
|
+
onUiHoverStart(_event) {
|
|
5191
|
+
}
|
|
5192
|
+
/** The pointer left the widget. */
|
|
5193
|
+
onUiHoverEnd(_event) {
|
|
5194
|
+
}
|
|
5195
|
+
/** A UI toggle flipped; `event.value` is the new boolean. */
|
|
5196
|
+
onUiToggle(_event) {
|
|
5197
|
+
}
|
|
5198
|
+
/** A UI slider moved; `event.value` is the new number. Fires on real value
|
|
5199
|
+
* changes during the drag and once more on release. */
|
|
5200
|
+
onUiChange(_event) {
|
|
5201
|
+
}
|
|
5137
5202
|
};
|
|
5138
5203
|
__name(_Behaviour, "Behaviour");
|
|
5139
5204
|
/**
|
|
@@ -6664,6 +6729,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
6664
6729
|
this.grabs = /* @__PURE__ */ new Map();
|
|
6665
6730
|
this.hoveredEntityId = null;
|
|
6666
6731
|
/** Per-hand hover, with the hit distance so the ray can stop at the surface. */
|
|
6732
|
+
this.busyContributors = [];
|
|
6667
6733
|
this.hovers = /* @__PURE__ */ new Map();
|
|
6668
6734
|
/** Monotonic seconds, accumulated from deltaTime for velocity sampling. */
|
|
6669
6735
|
this.elapsed = 0;
|
|
@@ -6672,6 +6738,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
6672
6738
|
update(world, deltaTime) {
|
|
6673
6739
|
if (!world.isXrPresenting()) {
|
|
6674
6740
|
this.releaseAll(world);
|
|
6741
|
+
this.hovers.clear();
|
|
6742
|
+
this.hoveredEntityId = null;
|
|
6675
6743
|
return;
|
|
6676
6744
|
}
|
|
6677
6745
|
this.elapsed += Math.max(0, deltaTime);
|
|
@@ -7294,13 +7362,30 @@ var _XRInteractionSystem = class _XRInteractionSystem {
|
|
|
7294
7362
|
*/
|
|
7295
7363
|
isSourceBusy(world, input) {
|
|
7296
7364
|
if (this.grabs.has(input)) return true;
|
|
7365
|
+
for (const contributor of this.busyContributors) {
|
|
7366
|
+
try {
|
|
7367
|
+
if (contributor(world, input)) return true;
|
|
7368
|
+
} catch {
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7297
7371
|
const hover = this.hovers.get(input);
|
|
7298
7372
|
if (!hover) return false;
|
|
7299
7373
|
const interactable = world.getComponent(
|
|
7300
7374
|
hover.entityId,
|
|
7301
7375
|
XRInteractableComponent
|
|
7302
7376
|
);
|
|
7303
|
-
return Boolean(
|
|
7377
|
+
return Boolean(
|
|
7378
|
+
interactable?.enabled && (interactable.grabbable || interactable.pointable)
|
|
7379
|
+
);
|
|
7380
|
+
}
|
|
7381
|
+
/** OR-ed into isSourceBusy; see the comment there. */
|
|
7382
|
+
registerBusyContributor(contributor) {
|
|
7383
|
+
this.busyContributors.push(contributor);
|
|
7384
|
+
}
|
|
7385
|
+
/** Hover for a specific input source (handedness-free — Vision Pro's
|
|
7386
|
+
* transient pointers all report handedness "none"). */
|
|
7387
|
+
getHoverForInput(input) {
|
|
7388
|
+
return this.hovers.get(input) ?? null;
|
|
7304
7389
|
}
|
|
7305
7390
|
/** True when THIS hand is holding something. */
|
|
7306
7391
|
isGrabbingWith(handedness) {
|
|
@@ -7935,6 +8020,88 @@ function decomposeXrPose(matrixElements, target) {
|
|
|
7935
8020
|
}
|
|
7936
8021
|
__name(decomposeXrPose, "decomposeXrPose");
|
|
7937
8022
|
|
|
8023
|
+
// core/xr/poseStability.ts
|
|
8024
|
+
var GLUED_REF_SPEED = 0.12;
|
|
8025
|
+
var STILL_SPEED = 0.05;
|
|
8026
|
+
var GLUED_FRAMES_TO_FREEZE = 3;
|
|
8027
|
+
var STATIC_FRAMES_TO_RECOVER = 10;
|
|
8028
|
+
var SNAP_SPEED = 3;
|
|
8029
|
+
var dist = /* @__PURE__ */ __name((a, b) => Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z), "dist");
|
|
8030
|
+
var _SurfacePoseGate = class _SurfacePoseGate {
|
|
8031
|
+
constructor() {
|
|
8032
|
+
this.prevRef = null;
|
|
8033
|
+
this.prevView = null;
|
|
8034
|
+
this.gluedStreak = 0;
|
|
8035
|
+
this.staticStreak = 0;
|
|
8036
|
+
this.suspect = false;
|
|
8037
|
+
this.deltas = 0;
|
|
8038
|
+
}
|
|
8039
|
+
/** True while poses are being rejected as head-glued. */
|
|
8040
|
+
get frozen() {
|
|
8041
|
+
return this.suspect;
|
|
8042
|
+
}
|
|
8043
|
+
/**
|
|
8044
|
+
* True once enough motion has been observed to classify the pose at all.
|
|
8045
|
+
* Surface CREATION waits for this: an entity spawned from a glued pose
|
|
8046
|
+
* materializes wherever the head happens to be — a rotated ghost copy of
|
|
8047
|
+
* the room — and freezing it there is barely better than letting it follow.
|
|
8048
|
+
*/
|
|
8049
|
+
get warmedUp() {
|
|
8050
|
+
return this.deltas >= GLUED_FRAMES_TO_FREEZE;
|
|
8051
|
+
}
|
|
8052
|
+
/**
|
|
8053
|
+
* Feed this frame's surface position in reference space and in view
|
|
8054
|
+
* (viewer-relative) space. Returns true when the pose should be APPLIED,
|
|
8055
|
+
* false when it should be frozen out.
|
|
8056
|
+
*
|
|
8057
|
+
* `viewPos` may be null when the viewer pose is unavailable (older
|
|
8058
|
+
* runtimes) — the gate then passes everything through, degrading to the
|
|
8059
|
+
* flag-only behavior.
|
|
8060
|
+
*/
|
|
8061
|
+
update(refPos, viewPos, deltaTime) {
|
|
8062
|
+
if (!viewPos || deltaTime <= 0) {
|
|
8063
|
+
this.prevRef = { ...refPos };
|
|
8064
|
+
this.prevView = viewPos ? { ...viewPos } : null;
|
|
8065
|
+
return !this.suspect;
|
|
8066
|
+
}
|
|
8067
|
+
const prevRef = this.prevRef;
|
|
8068
|
+
const prevView = this.prevView;
|
|
8069
|
+
this.prevRef = { ...refPos };
|
|
8070
|
+
this.prevView = { ...viewPos };
|
|
8071
|
+
if (!prevRef || !prevView) return !this.suspect;
|
|
8072
|
+
this.deltas += 1;
|
|
8073
|
+
const refSpeed = dist(refPos, prevRef) / deltaTime;
|
|
8074
|
+
const viewSpeed = dist(viewPos, prevView) / deltaTime;
|
|
8075
|
+
const glued = refSpeed > GLUED_REF_SPEED && viewSpeed < STILL_SPEED;
|
|
8076
|
+
const worldStatic = refSpeed < STILL_SPEED && viewSpeed > GLUED_REF_SPEED;
|
|
8077
|
+
if (this.suspect) {
|
|
8078
|
+
if (refSpeed > SNAP_SPEED && viewSpeed > SNAP_SPEED) {
|
|
8079
|
+
this.suspect = false;
|
|
8080
|
+
this.gluedStreak = 0;
|
|
8081
|
+
this.staticStreak = 0;
|
|
8082
|
+
return true;
|
|
8083
|
+
}
|
|
8084
|
+
this.staticStreak = worldStatic ? this.staticStreak + 1 : 0;
|
|
8085
|
+
if (this.staticStreak >= STATIC_FRAMES_TO_RECOVER) {
|
|
8086
|
+
this.suspect = false;
|
|
8087
|
+
this.gluedStreak = 0;
|
|
8088
|
+
this.staticStreak = 0;
|
|
8089
|
+
return true;
|
|
8090
|
+
}
|
|
8091
|
+
return false;
|
|
8092
|
+
}
|
|
8093
|
+
this.gluedStreak = glued ? this.gluedStreak + 1 : 0;
|
|
8094
|
+
if (this.gluedStreak >= GLUED_FRAMES_TO_FREEZE) {
|
|
8095
|
+
this.suspect = true;
|
|
8096
|
+
this.staticStreak = 0;
|
|
8097
|
+
return false;
|
|
8098
|
+
}
|
|
8099
|
+
return true;
|
|
8100
|
+
}
|
|
8101
|
+
};
|
|
8102
|
+
__name(_SurfacePoseGate, "SurfacePoseGate");
|
|
8103
|
+
var SurfacePoseGate = _SurfacePoseGate;
|
|
8104
|
+
|
|
7938
8105
|
// core/systems/XREnvironmentSystem.ts
|
|
7939
8106
|
var DEBUG_COLOR = 5088255;
|
|
7940
8107
|
function debugColorFor(kind, semanticLabel) {
|
|
@@ -7958,6 +8125,11 @@ function debugColorFor(kind, semanticLabel) {
|
|
|
7958
8125
|
}
|
|
7959
8126
|
__name(debugColorFor, "debugColorFor");
|
|
7960
8127
|
var GLOBAL_MESH_DEBUG_OPACITY = 0.35;
|
|
8128
|
+
var GLOBAL_MESH_SWITCH_MARGIN = 1.5;
|
|
8129
|
+
function isGlobalMeshSurface(mesh) {
|
|
8130
|
+
return (mesh.semanticLabel ?? null) === "global mesh";
|
|
8131
|
+
}
|
|
8132
|
+
__name(isGlobalMeshSurface, "isGlobalMeshSurface");
|
|
7961
8133
|
var DEBUG_RENDER_ORDER = 900;
|
|
7962
8134
|
var ROOM_CAPTURE_DELAY_SECONDS = 3;
|
|
7963
8135
|
var SYNTHETIC_FLOOR_EXTENT = 50;
|
|
@@ -7978,12 +8150,30 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
7978
8150
|
/** Keyed by the XRPlane/XRMesh object itself — the runtime's own identity. */
|
|
7979
8151
|
this.planes = /* @__PURE__ */ new Map();
|
|
7980
8152
|
this.meshes = /* @__PURE__ */ new Map();
|
|
8153
|
+
/**
|
|
8154
|
+
* Surfaces observed but not yet SPAWNED — their gate is still proving the
|
|
8155
|
+
* pose is world-anchored. Creating from an unflagged head-glued pose (seen
|
|
8156
|
+
* on-device after a suspend/resume regenerated the scan identities) plants
|
|
8157
|
+
* a rotated ghost copy of the room wherever the head happens to be, which
|
|
8158
|
+
* the apply-gate then freezes there — wrong forever. So admission itself
|
|
8159
|
+
* is gated: a few frames of world-anchored behavior first.
|
|
8160
|
+
*/
|
|
8161
|
+
this.pendingPlanes = /* @__PURE__ */ new Map();
|
|
8162
|
+
this.pendingMeshes = /* @__PURE__ */ new Map();
|
|
7981
8163
|
this.presenting = false;
|
|
7982
8164
|
this.debugVisible = false;
|
|
7983
8165
|
/** Seconds presenting, used to decide the room has genuinely nothing in it. */
|
|
7984
8166
|
this.elapsed = 0;
|
|
7985
8167
|
/** `initiateRoomCapture` may be called only ONCE per session. */
|
|
7986
8168
|
this.roomCaptureRequested = false;
|
|
8169
|
+
/** This frame's inverse viewer matrix, or null when unavailable. */
|
|
8170
|
+
this.viewerInverse = null;
|
|
8171
|
+
/** This frame's viewer position in reference space, for room election. */
|
|
8172
|
+
this.viewerPos = null;
|
|
8173
|
+
/** The ONE global room mesh currently allowed to spawn. */
|
|
8174
|
+
this.electedGlobalMesh = null;
|
|
8175
|
+
this.tmpViewerMatrix = new THREE19.Matrix4();
|
|
8176
|
+
this.tmpViewPos = new THREE19.Vector3();
|
|
7987
8177
|
}
|
|
7988
8178
|
update(world, deltaTime = 0) {
|
|
7989
8179
|
const presenting = world.isXrPresenting();
|
|
@@ -8021,13 +8211,22 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8021
8211
|
const meshesAvailable = Boolean(frame.detectedMeshes);
|
|
8022
8212
|
const useMesh = (mode === "mesh" || mode === "auto") && meshesAvailable;
|
|
8023
8213
|
const usePlanes = mode === "planes" || mode === "auto" && !useMesh;
|
|
8214
|
+
const viewer = frame.getViewerPose?.(referenceSpace);
|
|
8215
|
+
if (viewer) {
|
|
8216
|
+
const vm = viewer.transform.matrix;
|
|
8217
|
+
this.viewerPos = { x: vm[12], y: vm[13], z: vm[14] };
|
|
8218
|
+
this.viewerInverse = this.tmpViewerMatrix.fromArray(vm).invert();
|
|
8219
|
+
} else {
|
|
8220
|
+
this.viewerPos = null;
|
|
8221
|
+
this.viewerInverse = null;
|
|
8222
|
+
}
|
|
8024
8223
|
if (usePlanes) {
|
|
8025
|
-
this.syncPlanes(world, frame, referenceSpace);
|
|
8224
|
+
this.syncPlanes(world, frame, referenceSpace, deltaTime);
|
|
8026
8225
|
} else if (this.planes.size > 0) {
|
|
8027
8226
|
this.clearMap(world, this.planes);
|
|
8028
8227
|
}
|
|
8029
8228
|
if (useMesh) {
|
|
8030
|
-
this.syncMeshes(world, frame, referenceSpace);
|
|
8229
|
+
this.syncMeshes(world, frame, referenceSpace, deltaTime);
|
|
8031
8230
|
} else if (this.meshes.size > 0) {
|
|
8032
8231
|
this.clearMap(world, this.meshes);
|
|
8033
8232
|
}
|
|
@@ -8114,7 +8313,7 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8114
8313
|
if (ids.length === 0) return null;
|
|
8115
8314
|
return world.getComponent(ids[0], XRRigComponent) ?? null;
|
|
8116
8315
|
}
|
|
8117
|
-
syncPlanes(world, frame, referenceSpace) {
|
|
8316
|
+
syncPlanes(world, frame, referenceSpace, deltaTime) {
|
|
8118
8317
|
const detected = frame.detectedPlanes;
|
|
8119
8318
|
if (!detected) return;
|
|
8120
8319
|
for (const [plane, entry] of this.planes) {
|
|
@@ -8123,6 +8322,9 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8123
8322
|
this.planes.delete(plane);
|
|
8124
8323
|
}
|
|
8125
8324
|
}
|
|
8325
|
+
for (const plane of this.pendingPlanes.keys()) {
|
|
8326
|
+
if (!detected.has(plane)) this.pendingPlanes.delete(plane);
|
|
8327
|
+
}
|
|
8126
8328
|
for (const plane of detected) {
|
|
8127
8329
|
const pose = frame.getPose(plane.planeSpace, referenceSpace);
|
|
8128
8330
|
if (!pose) continue;
|
|
@@ -8131,8 +8333,11 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8131
8333
|
const changed = plane.lastChangedTime ?? 0;
|
|
8132
8334
|
if (!entry) {
|
|
8133
8335
|
if (emulated) continue;
|
|
8336
|
+
const gate = this.admit(this.pendingPlanes, plane, pose, deltaTime);
|
|
8337
|
+
if (!gate) continue;
|
|
8134
8338
|
const created = this.createPlaneEntity(world, plane);
|
|
8135
8339
|
if (!created) continue;
|
|
8340
|
+
created.gate = gate;
|
|
8136
8341
|
entry = created;
|
|
8137
8342
|
this.planes.set(plane, entry);
|
|
8138
8343
|
} else if (changed !== entry.lastChangedTime) {
|
|
@@ -8152,11 +8357,12 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8152
8357
|
}
|
|
8153
8358
|
}
|
|
8154
8359
|
}
|
|
8155
|
-
entry.
|
|
8156
|
-
|
|
8360
|
+
const allowed = !emulated && this.gateAllows(entry.gate, pose, deltaTime);
|
|
8361
|
+
entry.emulated = emulated || entry.gate.frozen;
|
|
8362
|
+
if (allowed) this.applyPose(world, entry.entityId, pose);
|
|
8157
8363
|
}
|
|
8158
8364
|
}
|
|
8159
|
-
syncMeshes(world, frame, referenceSpace) {
|
|
8365
|
+
syncMeshes(world, frame, referenceSpace, deltaTime) {
|
|
8160
8366
|
const detected = frame.detectedMeshes;
|
|
8161
8367
|
if (!detected) return;
|
|
8162
8368
|
for (const [mesh, entry] of this.meshes) {
|
|
@@ -8165,7 +8371,20 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8165
8371
|
this.meshes.delete(mesh);
|
|
8166
8372
|
}
|
|
8167
8373
|
}
|
|
8374
|
+
for (const mesh of this.pendingMeshes.keys()) {
|
|
8375
|
+
if (!detected.has(mesh)) this.pendingMeshes.delete(mesh);
|
|
8376
|
+
}
|
|
8377
|
+
const electedGlobal = this.electGlobalMesh(frame, detected, referenceSpace);
|
|
8168
8378
|
for (const mesh of detected) {
|
|
8379
|
+
if (isGlobalMeshSurface(mesh) && mesh !== electedGlobal) {
|
|
8380
|
+
const stale = this.meshes.get(mesh);
|
|
8381
|
+
if (stale) {
|
|
8382
|
+
this.destroyEntry(world, stale);
|
|
8383
|
+
this.meshes.delete(mesh);
|
|
8384
|
+
}
|
|
8385
|
+
this.pendingMeshes.delete(mesh);
|
|
8386
|
+
continue;
|
|
8387
|
+
}
|
|
8169
8388
|
const pose = frame.getPose(mesh.meshSpace, referenceSpace);
|
|
8170
8389
|
if (!pose) continue;
|
|
8171
8390
|
const emulated = pose.emulatedPosition === true;
|
|
@@ -8173,17 +8392,95 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8173
8392
|
const changed = mesh.lastChangedTime ?? 0;
|
|
8174
8393
|
if (!entry) {
|
|
8175
8394
|
if (emulated) continue;
|
|
8395
|
+
const gate = this.admit(this.pendingMeshes, mesh, pose, deltaTime);
|
|
8396
|
+
if (!gate) continue;
|
|
8176
8397
|
const created = this.createMeshEntity(world, mesh);
|
|
8177
8398
|
if (!created) continue;
|
|
8399
|
+
created.gate = gate;
|
|
8178
8400
|
entry = created;
|
|
8179
8401
|
this.meshes.set(mesh, entry);
|
|
8180
8402
|
} else if (changed !== entry.lastChangedTime) {
|
|
8181
8403
|
entry.lastChangedTime = changed;
|
|
8182
8404
|
writeXrMeshGeometry(mesh.vertices, mesh.indices, entry.geometry);
|
|
8183
8405
|
}
|
|
8184
|
-
entry.
|
|
8185
|
-
|
|
8406
|
+
const allowed = !emulated && this.gateAllows(entry.gate, pose, deltaTime);
|
|
8407
|
+
entry.emulated = emulated || entry.gate.frozen;
|
|
8408
|
+
if (allowed) this.applyPose(world, entry.entityId, pose);
|
|
8409
|
+
}
|
|
8410
|
+
}
|
|
8411
|
+
/**
|
|
8412
|
+
* Pick THE room's global mesh: the one whose scan origin is nearest the
|
|
8413
|
+
* viewer, sticky to the current choice within a margin so origin jitter
|
|
8414
|
+
* can't flap the room. Without a viewer pose (or with a single candidate)
|
|
8415
|
+
* the first candidate stands; ties are impossible to break anyway.
|
|
8416
|
+
*/
|
|
8417
|
+
electGlobalMesh(frame, detected, referenceSpace) {
|
|
8418
|
+
let nearest = null;
|
|
8419
|
+
let nearestDist = Number.POSITIVE_INFINITY;
|
|
8420
|
+
let currentDist = Number.POSITIVE_INFINITY;
|
|
8421
|
+
let currentStillDetected = false;
|
|
8422
|
+
for (const mesh of detected) {
|
|
8423
|
+
if (!isGlobalMeshSurface(mesh)) continue;
|
|
8424
|
+
if (!this.viewerPos) {
|
|
8425
|
+
return this.electedGlobalMesh && detected.has(this.electedGlobalMesh) ? this.electedGlobalMesh : mesh;
|
|
8426
|
+
}
|
|
8427
|
+
const pose = frame.getPose(mesh.meshSpace, referenceSpace);
|
|
8428
|
+
if (!pose) continue;
|
|
8429
|
+
const m = pose.transform.matrix;
|
|
8430
|
+
const d = Math.hypot(
|
|
8431
|
+
m[12] - this.viewerPos.x,
|
|
8432
|
+
m[13] - this.viewerPos.y,
|
|
8433
|
+
m[14] - this.viewerPos.z
|
|
8434
|
+
);
|
|
8435
|
+
if (mesh === this.electedGlobalMesh) {
|
|
8436
|
+
currentDist = d;
|
|
8437
|
+
currentStillDetected = true;
|
|
8438
|
+
}
|
|
8439
|
+
if (d < nearestDist) {
|
|
8440
|
+
nearestDist = d;
|
|
8441
|
+
nearest = mesh;
|
|
8442
|
+
}
|
|
8443
|
+
}
|
|
8444
|
+
if (currentStillDetected && currentDist < nearestDist + GLOBAL_MESH_SWITCH_MARGIN) {
|
|
8445
|
+
return this.electedGlobalMesh;
|
|
8446
|
+
}
|
|
8447
|
+
this.electedGlobalMesh = nearest;
|
|
8448
|
+
return nearest;
|
|
8449
|
+
}
|
|
8450
|
+
/**
|
|
8451
|
+
* Admission control for a NOT-yet-spawned surface. With a viewer pose
|
|
8452
|
+
* available, the surface must behave world-anchored for a few frames before
|
|
8453
|
+
* an entity is created for it; without one (older runtimes, tests) it is
|
|
8454
|
+
* admitted immediately — the gate can't classify anyway. Returns the
|
|
8455
|
+
* warmed-up gate (carried into the entry so its motion history survives),
|
|
8456
|
+
* or null while the surface must keep waiting.
|
|
8457
|
+
*/
|
|
8458
|
+
admit(pending, key, pose, deltaTime) {
|
|
8459
|
+
if (!this.viewerInverse) return new SurfacePoseGate();
|
|
8460
|
+
let gate = pending.get(key);
|
|
8461
|
+
if (!gate) {
|
|
8462
|
+
gate = new SurfacePoseGate();
|
|
8463
|
+
pending.set(key, gate);
|
|
8464
|
+
}
|
|
8465
|
+
const allowed = this.gateAllows(gate, pose, deltaTime);
|
|
8466
|
+
if (!allowed || !gate.warmedUp) return null;
|
|
8467
|
+
pending.delete(key);
|
|
8468
|
+
return gate;
|
|
8469
|
+
}
|
|
8470
|
+
/**
|
|
8471
|
+
* Run a surface's pose through its head-glue gate: position in reference
|
|
8472
|
+
* space vs position relative to the viewer. Without a viewer pose the gate
|
|
8473
|
+
* passes everything, degrading to the flag-only behavior.
|
|
8474
|
+
*/
|
|
8475
|
+
gateAllows(gate, pose, deltaTime) {
|
|
8476
|
+
const m = pose.transform.matrix;
|
|
8477
|
+
const refPos = { x: m[12], y: m[13], z: m[14] };
|
|
8478
|
+
let viewPos = null;
|
|
8479
|
+
if (this.viewerInverse) {
|
|
8480
|
+
const v = this.tmpViewPos.set(refPos.x, refPos.y, refPos.z).applyMatrix4(this.viewerInverse);
|
|
8481
|
+
viewPos = { x: v.x, y: v.y, z: v.z };
|
|
8186
8482
|
}
|
|
8483
|
+
return gate.update(refPos, viewPos, deltaTime);
|
|
8187
8484
|
}
|
|
8188
8485
|
createPlaneEntity(world, plane) {
|
|
8189
8486
|
const collider = planeColliderFromPolygon(plane.polygon);
|
|
@@ -8203,7 +8500,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8203
8500
|
lastChangedTime: plane.lastChangedTime ?? 0,
|
|
8204
8501
|
geometry: null,
|
|
8205
8502
|
material: null,
|
|
8206
|
-
emulated: false
|
|
8503
|
+
emulated: false,
|
|
8504
|
+
gate: new SurfacePoseGate()
|
|
8207
8505
|
};
|
|
8208
8506
|
if (this.debugVisible) {
|
|
8209
8507
|
this.attachPlaneDebug(world, entry, collider, plane.semanticLabel ?? null);
|
|
@@ -8276,7 +8574,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8276
8574
|
lastChangedTime: mesh.lastChangedTime ?? 0,
|
|
8277
8575
|
geometry,
|
|
8278
8576
|
material,
|
|
8279
|
-
emulated: false
|
|
8577
|
+
emulated: false,
|
|
8578
|
+
gate: new SurfacePoseGate()
|
|
8280
8579
|
};
|
|
8281
8580
|
}
|
|
8282
8581
|
/** A thin box so a detected plane can be SEEN, not just collided with. */
|
|
@@ -8357,6 +8656,9 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
|
|
|
8357
8656
|
clearAll(world) {
|
|
8358
8657
|
this.clearMap(world, this.planes);
|
|
8359
8658
|
this.clearMap(world, this.meshes);
|
|
8659
|
+
this.pendingPlanes.clear();
|
|
8660
|
+
this.pendingMeshes.clear();
|
|
8661
|
+
this.electedGlobalMesh = null;
|
|
8360
8662
|
}
|
|
8361
8663
|
dispose(world) {
|
|
8362
8664
|
this.clearAll(world);
|
|
@@ -8368,6 +8670,918 @@ function normalizeOrientation(value) {
|
|
|
8368
8670
|
return value === "horizontal" || value === "vertical" ? value : null;
|
|
8369
8671
|
}
|
|
8370
8672
|
__name(normalizeOrientation, "normalizeOrientation");
|
|
8673
|
+
var MOUSE_CLICK_TRAVEL_PX = 6;
|
|
8674
|
+
var PRESS_TINT_MIN_SECONDS = 0.1;
|
|
8675
|
+
var PRESS_TINT = 0.72;
|
|
8676
|
+
var HOVER_TINT = 0.9;
|
|
8677
|
+
var _UIInteractionSystem = class _UIInteractionSystem {
|
|
8678
|
+
/**
|
|
8679
|
+
* @param interaction The XR interaction system, or null in a world with no
|
|
8680
|
+
* XR (flat 3D experiences) — the mouse path still works via
|
|
8681
|
+
* `options.inputSystem`.
|
|
8682
|
+
*/
|
|
8683
|
+
constructor(interaction, options) {
|
|
8684
|
+
this.xrPresses = /* @__PURE__ */ new Map();
|
|
8685
|
+
this.mousePress = null;
|
|
8686
|
+
this.mouseHover = null;
|
|
8687
|
+
/** entityId → seconds of pressed-tint left to display. */
|
|
8688
|
+
this.tintFloors = /* @__PURE__ */ new Map();
|
|
8689
|
+
this.tinted = /* @__PURE__ */ new Set();
|
|
8690
|
+
this.raycaster = new THREE19.Raycaster();
|
|
8691
|
+
this.interaction = interaction;
|
|
8692
|
+
this.inputSystem = options?.inputSystem ?? null;
|
|
8693
|
+
interaction?.registerBusyContributor(
|
|
8694
|
+
(_world, input) => this.xrPresses.has(input)
|
|
8695
|
+
);
|
|
8696
|
+
}
|
|
8697
|
+
update(world, deltaTime = 0) {
|
|
8698
|
+
if (world.isEditorMode()) return;
|
|
8699
|
+
if (world.isXrPresenting()) {
|
|
8700
|
+
this.updateXr(world);
|
|
8701
|
+
} else {
|
|
8702
|
+
this.updateMouse(world);
|
|
8703
|
+
}
|
|
8704
|
+
this.updateTints(world, deltaTime);
|
|
8705
|
+
}
|
|
8706
|
+
// ── XR path ────────────────────────────────────────────────────────────────
|
|
8707
|
+
updateXr(world) {
|
|
8708
|
+
const interaction = this.interaction;
|
|
8709
|
+
if (!interaction) return;
|
|
8710
|
+
const xrInput = interaction.inputSystem;
|
|
8711
|
+
const inputs = xrInput.getInputs();
|
|
8712
|
+
for (const input of inputs) {
|
|
8713
|
+
const hover = interaction.getHoverForInput(input);
|
|
8714
|
+
const hovered = hover ? this.widgetKind(world, hover.entityId) : null;
|
|
8715
|
+
if (input.selectStart && hover && hovered) {
|
|
8716
|
+
this.xrPresses.set(input, {
|
|
8717
|
+
entityId: hover.entityId,
|
|
8718
|
+
kind: hovered,
|
|
8719
|
+
travelPx: 0
|
|
8720
|
+
});
|
|
8721
|
+
this.beginPressTint(world, hover.entityId);
|
|
8722
|
+
}
|
|
8723
|
+
const press = this.xrPresses.get(input);
|
|
8724
|
+
if (press && press.kind === "slider") {
|
|
8725
|
+
this.dragSlider(world, press.entityId, input);
|
|
8726
|
+
}
|
|
8727
|
+
if (input.selectEnd || !input.connected) {
|
|
8728
|
+
if (press) {
|
|
8729
|
+
this.xrPresses.delete(input);
|
|
8730
|
+
const stillOn = hover?.entityId === press.entityId || // Vision Pro: the transient source is gone from the hover map on
|
|
8731
|
+
// its release frame — confirm against the last latched ray.
|
|
8732
|
+
this.rayHitsWidget(world, press.entityId, input);
|
|
8733
|
+
if (stillOn || press.kind === "slider") {
|
|
8734
|
+
this.fire(world, press, input);
|
|
8735
|
+
}
|
|
8736
|
+
this.endPressTint(press.entityId);
|
|
8737
|
+
}
|
|
8738
|
+
}
|
|
8739
|
+
}
|
|
8740
|
+
for (const [input] of this.xrPresses) {
|
|
8741
|
+
if (!inputs.includes(input)) this.xrPresses.delete(input);
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
// ── Mouse path (flat preview) ──────────────────────────────────────────────
|
|
8745
|
+
updateMouse(world) {
|
|
8746
|
+
const input = this.inputSystem;
|
|
8747
|
+
if (!input) return;
|
|
8748
|
+
const hit = this.raycastMouse(world);
|
|
8749
|
+
const hovered = hit && this.widgetKind(world, hit) ? hit : null;
|
|
8750
|
+
if (hovered !== this.mouseHover) {
|
|
8751
|
+
if (this.mouseHover !== null) {
|
|
8752
|
+
this.setTint(world, this.mouseHover, 1);
|
|
8753
|
+
this.dispatchBubbling(
|
|
8754
|
+
world,
|
|
8755
|
+
this.mouseHover,
|
|
8756
|
+
(b, e) => b.onUiHoverEnd(e)
|
|
8757
|
+
);
|
|
8758
|
+
}
|
|
8759
|
+
if (hovered !== null) {
|
|
8760
|
+
this.setTint(world, hovered, HOVER_TINT);
|
|
8761
|
+
this.dispatchBubbling(world, hovered, (b, e) => b.onUiHoverStart(e));
|
|
8762
|
+
}
|
|
8763
|
+
this.mouseHover = hovered;
|
|
8764
|
+
}
|
|
8765
|
+
if (input.getMouseDown("left") && hovered) {
|
|
8766
|
+
const kind = this.widgetKind(world, hovered);
|
|
8767
|
+
if (kind) {
|
|
8768
|
+
this.mousePress = { entityId: hovered, kind, travelPx: 0 };
|
|
8769
|
+
this.beginPressTint(world, hovered);
|
|
8770
|
+
}
|
|
8771
|
+
}
|
|
8772
|
+
if (this.mousePress) {
|
|
8773
|
+
const delta = input.getMouseDelta();
|
|
8774
|
+
this.mousePress.travelPx += Math.hypot(delta.x, delta.y);
|
|
8775
|
+
if (this.mousePress.kind === "slider") {
|
|
8776
|
+
this.dragSliderMouse(world, this.mousePress.entityId);
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
if (input.getMouseUp("left") && this.mousePress) {
|
|
8780
|
+
const press = this.mousePress;
|
|
8781
|
+
this.mousePress = null;
|
|
8782
|
+
const stillOn = hovered === press.entityId;
|
|
8783
|
+
const withinTravel = press.travelPx <= MOUSE_CLICK_TRAVEL_PX;
|
|
8784
|
+
if (press.kind === "slider" || stillOn && withinTravel) {
|
|
8785
|
+
this.fire(world, press, null);
|
|
8786
|
+
}
|
|
8787
|
+
this.endPressTint(press.entityId);
|
|
8788
|
+
}
|
|
8789
|
+
}
|
|
8790
|
+
raycastMouse(world) {
|
|
8791
|
+
const input = this.inputSystem;
|
|
8792
|
+
if (!input) return null;
|
|
8793
|
+
const dom = world.getRenderer().domElement;
|
|
8794
|
+
const rect = dom.getBoundingClientRect();
|
|
8795
|
+
let ndcX = 0;
|
|
8796
|
+
let ndcY = 0;
|
|
8797
|
+
if (input.isMousePointerLocked()) {
|
|
8798
|
+
ndcX = 0;
|
|
8799
|
+
ndcY = 0;
|
|
8800
|
+
} else {
|
|
8801
|
+
const pos = input.getMousePosition();
|
|
8802
|
+
ndcX = (pos.x - rect.left) / Math.max(1, rect.width) * 2 - 1;
|
|
8803
|
+
ndcY = -((pos.y - rect.top) / Math.max(1, rect.height) * 2 - 1);
|
|
8804
|
+
if (ndcX < -1 || ndcX > 1 || ndcY < -1 || ndcY > 1) return null;
|
|
8805
|
+
}
|
|
8806
|
+
this.raycaster.setFromCamera(new THREE19.Vector2(ndcX, ndcY), world.getCamera());
|
|
8807
|
+
const hits = world.raycast(
|
|
8808
|
+
this.raycaster.ray.origin,
|
|
8809
|
+
this.raycaster.ray.direction
|
|
8810
|
+
);
|
|
8811
|
+
for (const hit of hits) {
|
|
8812
|
+
if (typeof hit.entityId !== "number") continue;
|
|
8813
|
+
if (this.widgetKind(world, hit.entityId)) return hit.entityId;
|
|
8814
|
+
return null;
|
|
8815
|
+
}
|
|
8816
|
+
return null;
|
|
8817
|
+
}
|
|
8818
|
+
// ── Shared machinery ───────────────────────────────────────────────────────
|
|
8819
|
+
widgetKind(world, entityId) {
|
|
8820
|
+
if (world.getComponent(entityId, UIButtonComponent)) return "button";
|
|
8821
|
+
if (world.getComponent(entityId, UIToggleComponent)) return "toggle";
|
|
8822
|
+
if (world.getComponent(entityId, UISliderComponent)) return "slider";
|
|
8823
|
+
return null;
|
|
8824
|
+
}
|
|
8825
|
+
fire(world, press, input) {
|
|
8826
|
+
if (press.kind === "button") {
|
|
8827
|
+
this.dispatchBubbling(world, press.entityId, (b, e) => b.onUiPress(e), input);
|
|
8828
|
+
return;
|
|
8829
|
+
}
|
|
8830
|
+
if (press.kind === "toggle") {
|
|
8831
|
+
const toggle = world.getComponent(press.entityId, UIToggleComponent);
|
|
8832
|
+
if (!toggle) return;
|
|
8833
|
+
toggle.value = !toggle.value;
|
|
8834
|
+
this.dispatchBubbling(
|
|
8835
|
+
world,
|
|
8836
|
+
press.entityId,
|
|
8837
|
+
(b, e) => b.onUiToggle(e),
|
|
8838
|
+
input,
|
|
8839
|
+
toggle.value
|
|
8840
|
+
);
|
|
8841
|
+
return;
|
|
8842
|
+
}
|
|
8843
|
+
const slider = world.getComponent(press.entityId, UISliderComponent);
|
|
8844
|
+
if (!slider) return;
|
|
8845
|
+
this.dispatchBubbling(
|
|
8846
|
+
world,
|
|
8847
|
+
press.entityId,
|
|
8848
|
+
(b, e) => b.onUiChange(e),
|
|
8849
|
+
input,
|
|
8850
|
+
slider.value
|
|
8851
|
+
);
|
|
8852
|
+
}
|
|
8853
|
+
dragSlider(world, entityId, input) {
|
|
8854
|
+
const point = this.rayPointOnWidget(world, entityId, input);
|
|
8855
|
+
if (point) this.applySliderPoint(world, entityId, point, input);
|
|
8856
|
+
}
|
|
8857
|
+
dragSliderMouse(world, entityId) {
|
|
8858
|
+
const object = this.widgetObject(world, entityId);
|
|
8859
|
+
if (!object) return;
|
|
8860
|
+
const hits = this.raycaster.intersectObject(object, true);
|
|
8861
|
+
if (hits.length > 0) this.applySliderPoint(world, entityId, hits[0].point, null);
|
|
8862
|
+
}
|
|
8863
|
+
applySliderPoint(world, entityId, worldPoint, input) {
|
|
8864
|
+
const slider = world.getComponent(entityId, UISliderComponent);
|
|
8865
|
+
const object = this.widgetObject(world, entityId);
|
|
8866
|
+
if (!slider || !object) return;
|
|
8867
|
+
const local = object.worldToLocal(worldPoint.clone());
|
|
8868
|
+
const inset = slider.height / 2;
|
|
8869
|
+
const usable = Math.max(1e-3, slider.width - inset * 2);
|
|
8870
|
+
const t = Math.min(1, Math.max(0, local.x / usable + 0.5));
|
|
8871
|
+
const next = slider.min + t * (slider.max - slider.min);
|
|
8872
|
+
if (Math.abs(next - slider.value) < 1e-4) return;
|
|
8873
|
+
slider.value = next;
|
|
8874
|
+
this.dispatchBubbling(
|
|
8875
|
+
world,
|
|
8876
|
+
entityId,
|
|
8877
|
+
(b, e) => b.onUiChange(e),
|
|
8878
|
+
input,
|
|
8879
|
+
next
|
|
8880
|
+
);
|
|
8881
|
+
}
|
|
8882
|
+
widgetObject(world, entityId) {
|
|
8883
|
+
return world.getComponent(entityId, Object3DRef)?.object;
|
|
8884
|
+
}
|
|
8885
|
+
rayHitsWidget(world, entityId, input) {
|
|
8886
|
+
const object = this.widgetObject(world, entityId);
|
|
8887
|
+
if (!object) return false;
|
|
8888
|
+
this.raycaster.set(
|
|
8889
|
+
input.rayOrigin.clone(),
|
|
8890
|
+
input.rayDirection.clone().normalize()
|
|
8891
|
+
);
|
|
8892
|
+
return this.raycaster.intersectObject(object, true).length > 0;
|
|
8893
|
+
}
|
|
8894
|
+
rayPointOnWidget(world, entityId, input) {
|
|
8895
|
+
const object = this.widgetObject(world, entityId);
|
|
8896
|
+
if (!object) return null;
|
|
8897
|
+
this.raycaster.set(
|
|
8898
|
+
input.rayOrigin.clone(),
|
|
8899
|
+
input.rayDirection.clone().normalize()
|
|
8900
|
+
);
|
|
8901
|
+
const hits = this.raycaster.intersectObject(object, true);
|
|
8902
|
+
return hits.length > 0 ? hits[0].point : null;
|
|
8903
|
+
}
|
|
8904
|
+
/**
|
|
8905
|
+
* Dispatch to the widget's Behaviours, then BUBBLE up the parent chain so a
|
|
8906
|
+
* panel script hears its children. Bubbling (not a world broadcast) keeps
|
|
8907
|
+
* two spawned copies of the same menu prefab independent.
|
|
8908
|
+
*/
|
|
8909
|
+
dispatchBubbling(world, entityId, visit, input = null, value) {
|
|
8910
|
+
const event = {
|
|
8911
|
+
targetEntityId: entityId,
|
|
8912
|
+
actionId: this.actionIdOf(world, entityId),
|
|
8913
|
+
input,
|
|
8914
|
+
...value === void 0 ? {} : { value }
|
|
8915
|
+
};
|
|
8916
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8917
|
+
let current = entityId;
|
|
8918
|
+
for (; ; ) {
|
|
8919
|
+
if (visited.has(current)) break;
|
|
8920
|
+
visited.add(current);
|
|
8921
|
+
const script = world.getComponent(current, ScriptComponent);
|
|
8922
|
+
const instances = script?.scriptInstances;
|
|
8923
|
+
if (instances) {
|
|
8924
|
+
for (const instance of instances.values()) {
|
|
8925
|
+
if (!(instance instanceof Behaviour)) continue;
|
|
8926
|
+
try {
|
|
8927
|
+
visit(instance, event);
|
|
8928
|
+
} catch (error) {
|
|
8929
|
+
console.error("[UIInteractionSystem] hook failed:", error);
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
}
|
|
8933
|
+
const parentId = world.getComponent(current, ParentId)?.value;
|
|
8934
|
+
if (typeof parentId !== "number") break;
|
|
8935
|
+
current = parentId;
|
|
8936
|
+
}
|
|
8937
|
+
}
|
|
8938
|
+
actionIdOf(world, entityId) {
|
|
8939
|
+
const button = world.getComponent(entityId, UIButtonComponent);
|
|
8940
|
+
if (button?.actionId) return button.actionId;
|
|
8941
|
+
const toggle = world.getComponent(entityId, UIToggleComponent);
|
|
8942
|
+
if (toggle?.actionId) return toggle.actionId;
|
|
8943
|
+
const slider = world.getComponent(entityId, UISliderComponent);
|
|
8944
|
+
if (slider?.actionId) return slider.actionId;
|
|
8945
|
+
const name = world.getComponent(entityId, Name)?.value;
|
|
8946
|
+
return typeof name === "string" ? name : String(entityId);
|
|
8947
|
+
}
|
|
8948
|
+
// ── Pressed/hover tint ─────────────────────────────────────────────────────
|
|
8949
|
+
beginPressTint(world, entityId) {
|
|
8950
|
+
this.setTint(world, entityId, PRESS_TINT);
|
|
8951
|
+
this.tintFloors.set(entityId, PRESS_TINT_MIN_SECONDS);
|
|
8952
|
+
}
|
|
8953
|
+
endPressTint(entityId) {
|
|
8954
|
+
if (!this.tintFloors.has(entityId)) {
|
|
8955
|
+
this.tintFloors.set(entityId, 0);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
updateTints(world, deltaTime) {
|
|
8959
|
+
for (const [entityId, remaining] of this.tintFloors) {
|
|
8960
|
+
const next = remaining - deltaTime;
|
|
8961
|
+
const stillHeld = [...this.xrPresses.values()].some((p) => p.entityId === entityId) || this.mousePress?.entityId === entityId;
|
|
8962
|
+
if (stillHeld) continue;
|
|
8963
|
+
if (next <= 0) {
|
|
8964
|
+
this.tintFloors.delete(entityId);
|
|
8965
|
+
this.setTint(
|
|
8966
|
+
world,
|
|
8967
|
+
entityId,
|
|
8968
|
+
this.mouseHover === entityId ? HOVER_TINT : 1
|
|
8969
|
+
);
|
|
8970
|
+
} else {
|
|
8971
|
+
this.tintFloors.set(entityId, next);
|
|
8972
|
+
}
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
setTint(world, entityId, scalar) {
|
|
8976
|
+
const object = this.widgetObject(world, entityId);
|
|
8977
|
+
if (!object) return;
|
|
8978
|
+
if (scalar === 1) this.tinted.delete(entityId);
|
|
8979
|
+
else this.tinted.add(entityId);
|
|
8980
|
+
object.traverse((child) => {
|
|
8981
|
+
const material = child.material;
|
|
8982
|
+
if (material?.color) material.color.setScalar(scalar);
|
|
8983
|
+
});
|
|
8984
|
+
}
|
|
8985
|
+
};
|
|
8986
|
+
__name(_UIInteractionSystem, "UIInteractionSystem");
|
|
8987
|
+
var UIInteractionSystem = _UIInteractionSystem;
|
|
8988
|
+
|
|
8989
|
+
// core/ui/uiCanvas.ts
|
|
8990
|
+
var DEFAULT_PIXELS_PER_METER = 1024;
|
|
8991
|
+
var MIN_PIXELS_PER_METER = 256;
|
|
8992
|
+
var MAX_PIXELS_PER_METER = 2048;
|
|
8993
|
+
var MAX_CANVAS_SIDE = 2048;
|
|
8994
|
+
function resolveCanvasSize(widthMeters, heightMeters, pixelDensity = DEFAULT_PIXELS_PER_METER) {
|
|
8995
|
+
const w = Math.max(0.01, widthMeters);
|
|
8996
|
+
const h = Math.max(0.01, heightMeters);
|
|
8997
|
+
const authored = Math.min(
|
|
8998
|
+
MAX_PIXELS_PER_METER,
|
|
8999
|
+
Math.max(MIN_PIXELS_PER_METER, pixelDensity)
|
|
9000
|
+
);
|
|
9001
|
+
const density = Math.min(authored, MAX_CANVAS_SIDE / Math.max(w, h));
|
|
9002
|
+
const even = /* @__PURE__ */ __name((v) => Math.max(2, 2 * Math.round(v * density / 2)), "even");
|
|
9003
|
+
return { pxWidth: even(w), pxHeight: even(h), density };
|
|
9004
|
+
}
|
|
9005
|
+
__name(resolveCanvasSize, "resolveCanvasSize");
|
|
9006
|
+
function wrapText(measure, text, maxWidthPx) {
|
|
9007
|
+
const lines = [];
|
|
9008
|
+
for (const paragraph of text.split("\n")) {
|
|
9009
|
+
const words = paragraph.split(/\s+/).filter((w) => w.length > 0);
|
|
9010
|
+
if (words.length === 0) {
|
|
9011
|
+
lines.push("");
|
|
9012
|
+
continue;
|
|
9013
|
+
}
|
|
9014
|
+
let line = "";
|
|
9015
|
+
for (const word of words) {
|
|
9016
|
+
const candidate = line ? `${line} ${word}` : word;
|
|
9017
|
+
if (measure(candidate) <= maxWidthPx) {
|
|
9018
|
+
line = candidate;
|
|
9019
|
+
continue;
|
|
9020
|
+
}
|
|
9021
|
+
if (line) {
|
|
9022
|
+
lines.push(line);
|
|
9023
|
+
line = "";
|
|
9024
|
+
}
|
|
9025
|
+
if (measure(word) <= maxWidthPx) {
|
|
9026
|
+
line = word;
|
|
9027
|
+
continue;
|
|
9028
|
+
}
|
|
9029
|
+
let chunk = "";
|
|
9030
|
+
for (const char of word) {
|
|
9031
|
+
const next = chunk + char;
|
|
9032
|
+
if (measure(next) <= maxWidthPx || chunk.length === 0) {
|
|
9033
|
+
chunk = next;
|
|
9034
|
+
} else {
|
|
9035
|
+
lines.push(chunk);
|
|
9036
|
+
chunk = char;
|
|
9037
|
+
}
|
|
9038
|
+
}
|
|
9039
|
+
line = chunk;
|
|
9040
|
+
}
|
|
9041
|
+
lines.push(line);
|
|
9042
|
+
}
|
|
9043
|
+
return lines;
|
|
9044
|
+
}
|
|
9045
|
+
__name(wrapText, "wrapText");
|
|
9046
|
+
var ELLIPSIS = "\u2026";
|
|
9047
|
+
function clampLines(measure, lines, maxLines, maxWidthPx) {
|
|
9048
|
+
if (maxLines <= 0) return [];
|
|
9049
|
+
if (lines.length <= maxLines) return lines;
|
|
9050
|
+
const kept = lines.slice(0, maxLines);
|
|
9051
|
+
let last = kept[maxLines - 1];
|
|
9052
|
+
while (last.length > 0 && measure(last + ELLIPSIS) > maxWidthPx) {
|
|
9053
|
+
last = last.slice(0, -1);
|
|
9054
|
+
}
|
|
9055
|
+
kept[maxLines - 1] = last + ELLIPSIS;
|
|
9056
|
+
return kept;
|
|
9057
|
+
}
|
|
9058
|
+
__name(clampLines, "clampLines");
|
|
9059
|
+
var UI_FONT_STACK = '-apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
|
|
9060
|
+
function tracePath(ctx, x, y, w, h, r) {
|
|
9061
|
+
const radius = Math.max(0, Math.min(r, w / 2, h / 2));
|
|
9062
|
+
ctx.beginPath();
|
|
9063
|
+
if (typeof ctx.roundRect === "function") {
|
|
9064
|
+
ctx.roundRect(x, y, w, h, radius);
|
|
9065
|
+
return;
|
|
9066
|
+
}
|
|
9067
|
+
ctx.moveTo(x + radius, y);
|
|
9068
|
+
ctx.arcTo(x + w, y, x + w, y + h, radius);
|
|
9069
|
+
ctx.arcTo(x + w, y + h, x, y + h, radius);
|
|
9070
|
+
ctx.arcTo(x, y + h, x, y, radius);
|
|
9071
|
+
ctx.arcTo(x, y, x + w, y, radius);
|
|
9072
|
+
ctx.closePath();
|
|
9073
|
+
}
|
|
9074
|
+
__name(tracePath, "tracePath");
|
|
9075
|
+
function withOpacity(ctx, color, opacity) {
|
|
9076
|
+
if (opacity >= 1) return color;
|
|
9077
|
+
ctx.save();
|
|
9078
|
+
ctx.fillStyle = color;
|
|
9079
|
+
const parsed = ctx.fillStyle;
|
|
9080
|
+
ctx.restore();
|
|
9081
|
+
if (parsed.startsWith("#") && (parsed.length === 7 || parsed.length === 4)) {
|
|
9082
|
+
const hex = parsed.length === 4 ? `#${parsed[1]}${parsed[1]}${parsed[2]}${parsed[2]}${parsed[3]}${parsed[3]}` : parsed;
|
|
9083
|
+
const r = Number.parseInt(hex.slice(1, 3), 16);
|
|
9084
|
+
const g = Number.parseInt(hex.slice(3, 5), 16);
|
|
9085
|
+
const b = Number.parseInt(hex.slice(5, 7), 16);
|
|
9086
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
9087
|
+
}
|
|
9088
|
+
return parsed;
|
|
9089
|
+
}
|
|
9090
|
+
__name(withOpacity, "withOpacity");
|
|
9091
|
+
function drawChrome(ctx, pxWidth, pxHeight, density, style) {
|
|
9092
|
+
ctx.clearRect(0, 0, pxWidth, pxHeight);
|
|
9093
|
+
const border = Math.max(0, style.borderWidthMeters * density);
|
|
9094
|
+
const radius = style.cornerRadiusMeters * density;
|
|
9095
|
+
tracePath(ctx, 0, 0, pxWidth, pxHeight, radius);
|
|
9096
|
+
ctx.fillStyle = withOpacity(
|
|
9097
|
+
ctx,
|
|
9098
|
+
style.backgroundColor,
|
|
9099
|
+
Math.max(0, Math.min(1, style.backgroundOpacity))
|
|
9100
|
+
);
|
|
9101
|
+
ctx.fill();
|
|
9102
|
+
if (border > 0) {
|
|
9103
|
+
tracePath(
|
|
9104
|
+
ctx,
|
|
9105
|
+
border / 2,
|
|
9106
|
+
border / 2,
|
|
9107
|
+
pxWidth - border,
|
|
9108
|
+
pxHeight - border,
|
|
9109
|
+
Math.max(0, radius - border / 2)
|
|
9110
|
+
);
|
|
9111
|
+
ctx.strokeStyle = style.borderColor;
|
|
9112
|
+
ctx.lineWidth = border;
|
|
9113
|
+
ctx.stroke();
|
|
9114
|
+
}
|
|
9115
|
+
}
|
|
9116
|
+
__name(drawChrome, "drawChrome");
|
|
9117
|
+
function drawTextBlock(ctx, pxWidth, pxHeight, density, text, style) {
|
|
9118
|
+
const fontPx = Math.max(4, style.fontSizeMeters * density);
|
|
9119
|
+
const paddingPx = style.paddingMeters * density;
|
|
9120
|
+
const contentW = Math.max(1, pxWidth - paddingPx * 2);
|
|
9121
|
+
const contentH = Math.max(1, pxHeight - paddingPx * 2);
|
|
9122
|
+
const lineHeightPx = fontPx * style.lineHeight;
|
|
9123
|
+
ctx.font = `${style.fontWeight} ${fontPx}px ${UI_FONT_STACK}`;
|
|
9124
|
+
const measure = /* @__PURE__ */ __name((s) => ctx.measureText(s).width, "measure");
|
|
9125
|
+
const maxLines = Math.max(1, Math.floor(contentH / lineHeightPx));
|
|
9126
|
+
const lines = clampLines(
|
|
9127
|
+
measure,
|
|
9128
|
+
wrapText(measure, text, contentW),
|
|
9129
|
+
maxLines,
|
|
9130
|
+
contentW
|
|
9131
|
+
);
|
|
9132
|
+
const blockH = lines.length * lineHeightPx;
|
|
9133
|
+
const top = style.valign === "top" ? paddingPx : style.valign === "bottom" ? pxHeight - paddingPx - blockH : (pxHeight - blockH) / 2;
|
|
9134
|
+
const anchorX = style.align === "left" ? paddingPx : style.align === "right" ? pxWidth - paddingPx : pxWidth / 2;
|
|
9135
|
+
ctx.save();
|
|
9136
|
+
tracePath(ctx, 0, 0, pxWidth, pxHeight, 0);
|
|
9137
|
+
ctx.clip();
|
|
9138
|
+
ctx.fillStyle = style.color;
|
|
9139
|
+
ctx.textAlign = style.align;
|
|
9140
|
+
ctx.textBaseline = "middle";
|
|
9141
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
9142
|
+
ctx.fillText(lines[i], anchorX, top + (i + 0.5) * lineHeightPx);
|
|
9143
|
+
}
|
|
9144
|
+
ctx.restore();
|
|
9145
|
+
}
|
|
9146
|
+
__name(drawTextBlock, "drawTextBlock");
|
|
9147
|
+
function panelSignature(widthM, heightM, size, style) {
|
|
9148
|
+
return JSON.stringify([
|
|
9149
|
+
widthM,
|
|
9150
|
+
heightM,
|
|
9151
|
+
size.pxWidth,
|
|
9152
|
+
size.pxHeight,
|
|
9153
|
+
style.backgroundColor,
|
|
9154
|
+
style.backgroundOpacity,
|
|
9155
|
+
style.cornerRadiusMeters,
|
|
9156
|
+
style.borderWidthMeters,
|
|
9157
|
+
style.borderColor
|
|
9158
|
+
]);
|
|
9159
|
+
}
|
|
9160
|
+
__name(panelSignature, "panelSignature");
|
|
9161
|
+
function textSignature(widthM, heightM, size, text, style) {
|
|
9162
|
+
return JSON.stringify([
|
|
9163
|
+
widthM,
|
|
9164
|
+
heightM,
|
|
9165
|
+
size.pxWidth,
|
|
9166
|
+
size.pxHeight,
|
|
9167
|
+
text,
|
|
9168
|
+
style.fontSizeMeters,
|
|
9169
|
+
style.fontWeight,
|
|
9170
|
+
style.color,
|
|
9171
|
+
style.align,
|
|
9172
|
+
style.valign,
|
|
9173
|
+
style.lineHeight,
|
|
9174
|
+
style.paddingMeters
|
|
9175
|
+
]);
|
|
9176
|
+
}
|
|
9177
|
+
__name(textSignature, "textSignature");
|
|
9178
|
+
function buttonSignature(widthM, heightM, size, panel, label, text) {
|
|
9179
|
+
return JSON.stringify([
|
|
9180
|
+
panelSignature(widthM, heightM, size, panel),
|
|
9181
|
+
label,
|
|
9182
|
+
text.fontSizeMeters,
|
|
9183
|
+
text.fontWeight,
|
|
9184
|
+
text.color
|
|
9185
|
+
]);
|
|
9186
|
+
}
|
|
9187
|
+
__name(buttonSignature, "buttonSignature");
|
|
9188
|
+
function trackSignature(widthM, heightM, size, panel, accentColor, knobColor) {
|
|
9189
|
+
return JSON.stringify([
|
|
9190
|
+
panelSignature(widthM, heightM, size, panel),
|
|
9191
|
+
accentColor,
|
|
9192
|
+
knobColor
|
|
9193
|
+
]);
|
|
9194
|
+
}
|
|
9195
|
+
__name(trackSignature, "trackSignature");
|
|
9196
|
+
function drawRoundedBar(ctx, pxX, pxY, pxW, pxH, color) {
|
|
9197
|
+
tracePath(ctx, pxX, pxY, pxW, pxH, pxH / 2);
|
|
9198
|
+
ctx.fillStyle = color;
|
|
9199
|
+
ctx.fill();
|
|
9200
|
+
}
|
|
9201
|
+
__name(drawRoundedBar, "drawRoundedBar");
|
|
9202
|
+
|
|
9203
|
+
// core/systems/UIPanelSystem.ts
|
|
9204
|
+
var FACE_RENDER_ORDER = 10;
|
|
9205
|
+
var CHILD_Z = 2e-3;
|
|
9206
|
+
var _UIPanelSystem = class _UIPanelSystem {
|
|
9207
|
+
constructor(options) {
|
|
9208
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
9209
|
+
this.createCanvas = options?.createCanvas ?? (() => document.createElement("canvas"));
|
|
9210
|
+
}
|
|
9211
|
+
update(world, deltaTime = 0) {
|
|
9212
|
+
const live = /* @__PURE__ */ new Set();
|
|
9213
|
+
for (const entityId of world.query(UIPanelComponent, Object3DRef)) {
|
|
9214
|
+
const panel = world.getComponent(entityId, UIPanelComponent);
|
|
9215
|
+
if (!panel) continue;
|
|
9216
|
+
live.add(entityId);
|
|
9217
|
+
this.syncPanel(world, entityId, panel);
|
|
9218
|
+
this.applyFollow(world, entityId, panel);
|
|
9219
|
+
}
|
|
9220
|
+
for (const entityId of world.query(UITextComponent, Object3DRef)) {
|
|
9221
|
+
const text = world.getComponent(entityId, UITextComponent);
|
|
9222
|
+
if (!text) continue;
|
|
9223
|
+
live.add(entityId);
|
|
9224
|
+
this.syncText(world, entityId, text);
|
|
9225
|
+
}
|
|
9226
|
+
for (const entityId of world.query(UIButtonComponent, Object3DRef)) {
|
|
9227
|
+
const button = world.getComponent(entityId, UIButtonComponent);
|
|
9228
|
+
if (!button) continue;
|
|
9229
|
+
live.add(entityId);
|
|
9230
|
+
this.syncButton(world, entityId, button);
|
|
9231
|
+
}
|
|
9232
|
+
for (const entityId of world.query(UIToggleComponent, Object3DRef)) {
|
|
9233
|
+
const toggle = world.getComponent(entityId, UIToggleComponent);
|
|
9234
|
+
if (!toggle) continue;
|
|
9235
|
+
live.add(entityId);
|
|
9236
|
+
this.syncToggle(world, entityId, toggle, deltaTime);
|
|
9237
|
+
}
|
|
9238
|
+
for (const entityId of world.query(UISliderComponent, Object3DRef)) {
|
|
9239
|
+
const slider = world.getComponent(entityId, UISliderComponent);
|
|
9240
|
+
if (!slider) continue;
|
|
9241
|
+
live.add(entityId);
|
|
9242
|
+
this.syncSlider(world, entityId, slider);
|
|
9243
|
+
}
|
|
9244
|
+
for (const [entityId, entry] of this.entries) {
|
|
9245
|
+
if (!live.has(entityId)) {
|
|
9246
|
+
this.destroyEntry(entry);
|
|
9247
|
+
this.entries.delete(entityId);
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
/** All GPU/canvas resources; call on world teardown. */
|
|
9252
|
+
dispose() {
|
|
9253
|
+
for (const entry of this.entries.values()) this.destroyEntry(entry);
|
|
9254
|
+
this.entries.clear();
|
|
9255
|
+
}
|
|
9256
|
+
// ── Widgets ────────────────────────────────────────────────────────────────
|
|
9257
|
+
syncPanel(world, entityId, panel) {
|
|
9258
|
+
const size = resolveCanvasSize(panel.width, panel.height, panel.pixelDensity);
|
|
9259
|
+
const style = {
|
|
9260
|
+
backgroundColor: panel.backgroundColor,
|
|
9261
|
+
backgroundOpacity: panel.backgroundOpacity,
|
|
9262
|
+
cornerRadiusMeters: panel.cornerRadius,
|
|
9263
|
+
borderWidthMeters: panel.borderWidth,
|
|
9264
|
+
borderColor: panel.borderColor
|
|
9265
|
+
};
|
|
9266
|
+
const signature = panelSignature(panel.width, panel.height, size, style);
|
|
9267
|
+
this.rebuildIfChanged(world, entityId, signature, panel.width, panel.height, size, (ctx) => {
|
|
9268
|
+
drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, style);
|
|
9269
|
+
});
|
|
9270
|
+
}
|
|
9271
|
+
syncText(world, entityId, text) {
|
|
9272
|
+
const size = resolveCanvasSize(text.width, text.height);
|
|
9273
|
+
const style = {
|
|
9274
|
+
fontSizeMeters: text.fontSize,
|
|
9275
|
+
fontWeight: text.fontWeight,
|
|
9276
|
+
color: text.color,
|
|
9277
|
+
align: text.align,
|
|
9278
|
+
valign: text.valign,
|
|
9279
|
+
lineHeight: text.lineHeight,
|
|
9280
|
+
paddingMeters: text.padding
|
|
9281
|
+
};
|
|
9282
|
+
const signature = textSignature(text.width, text.height, size, text.text, style);
|
|
9283
|
+
this.rebuildIfChanged(world, entityId, signature, text.width, text.height, size, (ctx) => {
|
|
9284
|
+
ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
|
|
9285
|
+
drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, text.text, style);
|
|
9286
|
+
});
|
|
9287
|
+
}
|
|
9288
|
+
syncButton(world, entityId, button) {
|
|
9289
|
+
const size = resolveCanvasSize(button.width, button.height);
|
|
9290
|
+
const chrome = {
|
|
9291
|
+
backgroundColor: button.backgroundColor,
|
|
9292
|
+
backgroundOpacity: button.backgroundOpacity,
|
|
9293
|
+
cornerRadiusMeters: button.cornerRadius,
|
|
9294
|
+
borderWidthMeters: button.borderWidth,
|
|
9295
|
+
borderColor: button.borderColor
|
|
9296
|
+
};
|
|
9297
|
+
const label = {
|
|
9298
|
+
fontSizeMeters: button.fontSize,
|
|
9299
|
+
fontWeight: button.fontWeight,
|
|
9300
|
+
color: button.textColor,
|
|
9301
|
+
align: "center",
|
|
9302
|
+
valign: "middle",
|
|
9303
|
+
lineHeight: 1.2,
|
|
9304
|
+
paddingMeters: 0.01
|
|
9305
|
+
};
|
|
9306
|
+
const signature = buttonSignature(
|
|
9307
|
+
button.width,
|
|
9308
|
+
button.height,
|
|
9309
|
+
size,
|
|
9310
|
+
chrome,
|
|
9311
|
+
button.label,
|
|
9312
|
+
label
|
|
9313
|
+
);
|
|
9314
|
+
this.rebuildIfChanged(world, entityId, signature, button.width, button.height, size, (ctx) => {
|
|
9315
|
+
drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
|
|
9316
|
+
drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, button.label, label);
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9319
|
+
syncToggle(world, entityId, toggle, deltaTime) {
|
|
9320
|
+
const size = resolveCanvasSize(toggle.width, toggle.height);
|
|
9321
|
+
const chrome = {
|
|
9322
|
+
backgroundColor: toggle.value ? toggle.accentColor : toggle.trackColor,
|
|
9323
|
+
backgroundOpacity: 1,
|
|
9324
|
+
cornerRadiusMeters: toggle.height / 2,
|
|
9325
|
+
borderWidthMeters: 0,
|
|
9326
|
+
borderColor: "#000000"
|
|
9327
|
+
};
|
|
9328
|
+
const signature = JSON.stringify([
|
|
9329
|
+
trackSignature(
|
|
9330
|
+
toggle.width,
|
|
9331
|
+
toggle.height,
|
|
9332
|
+
size,
|
|
9333
|
+
chrome,
|
|
9334
|
+
toggle.accentColor,
|
|
9335
|
+
toggle.knobColor
|
|
9336
|
+
),
|
|
9337
|
+
toggle.value
|
|
9338
|
+
]);
|
|
9339
|
+
const entry = this.rebuildIfChanged(
|
|
9340
|
+
world,
|
|
9341
|
+
entityId,
|
|
9342
|
+
signature,
|
|
9343
|
+
toggle.width,
|
|
9344
|
+
toggle.height,
|
|
9345
|
+
size,
|
|
9346
|
+
(ctx) => {
|
|
9347
|
+
drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
|
|
9348
|
+
}
|
|
9349
|
+
);
|
|
9350
|
+
if (!entry) return;
|
|
9351
|
+
if (!entry.knob) {
|
|
9352
|
+
entry.knob = this.makeDisc(
|
|
9353
|
+
toggle.height * 0.4,
|
|
9354
|
+
toggle.knobColor,
|
|
9355
|
+
entry.face.parent,
|
|
9356
|
+
CHILD_Z
|
|
9357
|
+
);
|
|
9358
|
+
}
|
|
9359
|
+
const inset = toggle.height / 2;
|
|
9360
|
+
const target = toggle.value ? toggle.width / 2 - inset : -(toggle.width / 2 - inset);
|
|
9361
|
+
const knob = entry.knob;
|
|
9362
|
+
const ease = Math.min(1, deltaTime * 14);
|
|
9363
|
+
knob.position.x += (target - knob.position.x) * (deltaTime > 0 ? ease : 1);
|
|
9364
|
+
knob.position.z = CHILD_Z;
|
|
9365
|
+
}
|
|
9366
|
+
syncSlider(world, entityId, slider) {
|
|
9367
|
+
const size = resolveCanvasSize(slider.width, slider.height);
|
|
9368
|
+
const trackThickness = slider.height * 0.32;
|
|
9369
|
+
const knobRadius = slider.height * 0.45;
|
|
9370
|
+
const usable = Math.max(1e-3, slider.width - slider.height);
|
|
9371
|
+
const chrome = {
|
|
9372
|
+
backgroundColor: slider.trackColor,
|
|
9373
|
+
backgroundOpacity: 1,
|
|
9374
|
+
cornerRadiusMeters: trackThickness / 2,
|
|
9375
|
+
borderWidthMeters: 0,
|
|
9376
|
+
borderColor: "#000000"
|
|
9377
|
+
};
|
|
9378
|
+
const signature = trackSignature(
|
|
9379
|
+
slider.width,
|
|
9380
|
+
slider.height,
|
|
9381
|
+
size,
|
|
9382
|
+
chrome,
|
|
9383
|
+
slider.accentColor,
|
|
9384
|
+
slider.knobColor
|
|
9385
|
+
);
|
|
9386
|
+
const entry = this.rebuildIfChanged(
|
|
9387
|
+
world,
|
|
9388
|
+
entityId,
|
|
9389
|
+
signature,
|
|
9390
|
+
slider.width,
|
|
9391
|
+
slider.height,
|
|
9392
|
+
size,
|
|
9393
|
+
(ctx) => {
|
|
9394
|
+
ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
|
|
9395
|
+
const trackWidthM = usable + trackThickness;
|
|
9396
|
+
drawRoundedBar(
|
|
9397
|
+
ctx,
|
|
9398
|
+
(slider.width - trackWidthM) / 2 * size.density,
|
|
9399
|
+
(slider.height - trackThickness) / 2 * size.density,
|
|
9400
|
+
trackWidthM * size.density,
|
|
9401
|
+
trackThickness * size.density,
|
|
9402
|
+
slider.trackColor
|
|
9403
|
+
);
|
|
9404
|
+
}
|
|
9405
|
+
);
|
|
9406
|
+
if (!entry) return;
|
|
9407
|
+
if (!entry.fill) {
|
|
9408
|
+
entry.fill = this.makeQuad(
|
|
9409
|
+
1,
|
|
9410
|
+
trackThickness,
|
|
9411
|
+
slider.accentColor,
|
|
9412
|
+
entry.face.parent,
|
|
9413
|
+
CHILD_Z / 2
|
|
9414
|
+
);
|
|
9415
|
+
entry.cap = this.makeDisc(
|
|
9416
|
+
trackThickness / 2,
|
|
9417
|
+
slider.accentColor,
|
|
9418
|
+
entry.face.parent,
|
|
9419
|
+
CHILD_Z / 2,
|
|
9420
|
+
Math.PI / 2,
|
|
9421
|
+
Math.PI
|
|
9422
|
+
);
|
|
9423
|
+
entry.cap.position.x = -usable / 2;
|
|
9424
|
+
}
|
|
9425
|
+
if (!entry.knob) {
|
|
9426
|
+
entry.knob = this.makeDisc(
|
|
9427
|
+
knobRadius,
|
|
9428
|
+
slider.knobColor,
|
|
9429
|
+
entry.face.parent,
|
|
9430
|
+
CHILD_Z
|
|
9431
|
+
);
|
|
9432
|
+
}
|
|
9433
|
+
const range = Math.max(1e-6, slider.max - slider.min);
|
|
9434
|
+
const t = Math.min(1, Math.max(0, (slider.value - slider.min) / range));
|
|
9435
|
+
entry.knob.position.x = -usable / 2 + t * usable;
|
|
9436
|
+
entry.knob.position.z = CHILD_Z;
|
|
9437
|
+
entry.fill.scale.x = Math.max(1e-4, t * usable);
|
|
9438
|
+
entry.fill.position.x = -usable / 2 + t * usable / 2;
|
|
9439
|
+
entry.fill.position.z = CHILD_Z / 2;
|
|
9440
|
+
}
|
|
9441
|
+
// ── Machinery ──────────────────────────────────────────────────────────────
|
|
9442
|
+
rebuildIfChanged(world, entityId, signature, widthM, heightM, size, draw) {
|
|
9443
|
+
const root = world.getComponent(entityId, Object3DRef)?.object;
|
|
9444
|
+
if (!root) return null;
|
|
9445
|
+
let entry = this.entries.get(entityId);
|
|
9446
|
+
if (entry && entry.signature === signature) return entry;
|
|
9447
|
+
if (entry) this.disposeMovingParts(entry);
|
|
9448
|
+
if (!entry) {
|
|
9449
|
+
const canvas2 = this.createCanvas();
|
|
9450
|
+
const texture = new THREE19.CanvasTexture(
|
|
9451
|
+
canvas2
|
|
9452
|
+
);
|
|
9453
|
+
this.configureTexture(world, texture);
|
|
9454
|
+
const material = new THREE19.MeshBasicMaterial({
|
|
9455
|
+
map: texture,
|
|
9456
|
+
transparent: true,
|
|
9457
|
+
depthWrite: false,
|
|
9458
|
+
// UI colours are authored absolutes; scene tone mapping would shift
|
|
9459
|
+
// them (the classic "why is my white UI beige" bug).
|
|
9460
|
+
toneMapped: false,
|
|
9461
|
+
side: THREE19.DoubleSide,
|
|
9462
|
+
premultipliedAlpha: true
|
|
9463
|
+
});
|
|
9464
|
+
const face = new THREE19.Mesh(
|
|
9465
|
+
new THREE19.PlaneGeometry(widthM, heightM),
|
|
9466
|
+
material
|
|
9467
|
+
);
|
|
9468
|
+
face.renderOrder = FACE_RENDER_ORDER;
|
|
9469
|
+
root.add(face);
|
|
9470
|
+
entry = {
|
|
9471
|
+
signature: "",
|
|
9472
|
+
face,
|
|
9473
|
+
texture,
|
|
9474
|
+
material,
|
|
9475
|
+
canvas: canvas2,
|
|
9476
|
+
knob: null,
|
|
9477
|
+
fill: null,
|
|
9478
|
+
cap: null
|
|
9479
|
+
};
|
|
9480
|
+
this.entries.set(entityId, entry);
|
|
9481
|
+
}
|
|
9482
|
+
const { canvas } = entry;
|
|
9483
|
+
if (canvas.width !== size.pxWidth || canvas.height !== size.pxHeight) {
|
|
9484
|
+
canvas.width = size.pxWidth;
|
|
9485
|
+
canvas.height = size.pxHeight;
|
|
9486
|
+
}
|
|
9487
|
+
const params = entry.face.geometry.parameters;
|
|
9488
|
+
if (params.width !== widthM || params.height !== heightM) {
|
|
9489
|
+
entry.face.geometry.dispose();
|
|
9490
|
+
entry.face.geometry = new THREE19.PlaneGeometry(widthM, heightM);
|
|
9491
|
+
}
|
|
9492
|
+
const ctx = canvas.getContext("2d");
|
|
9493
|
+
if (ctx) draw(ctx);
|
|
9494
|
+
entry.texture.needsUpdate = true;
|
|
9495
|
+
entry.signature = signature;
|
|
9496
|
+
return entry;
|
|
9497
|
+
}
|
|
9498
|
+
configureTexture(world, texture) {
|
|
9499
|
+
texture.colorSpace = THREE19.SRGBColorSpace;
|
|
9500
|
+
texture.generateMipmaps = true;
|
|
9501
|
+
texture.minFilter = THREE19.LinearMipmapLinearFilter;
|
|
9502
|
+
texture.magFilter = THREE19.LinearFilter;
|
|
9503
|
+
texture.premultiplyAlpha = true;
|
|
9504
|
+
try {
|
|
9505
|
+
const max = world.getRenderer().capabilities?.getMaxAnisotropy?.();
|
|
9506
|
+
if (typeof max === "number") texture.anisotropy = Math.min(8, max);
|
|
9507
|
+
} catch {
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
makeQuad(widthM, heightM, color, parent, z) {
|
|
9511
|
+
return this.addPart(
|
|
9512
|
+
new THREE19.PlaneGeometry(widthM, heightM),
|
|
9513
|
+
color,
|
|
9514
|
+
parent,
|
|
9515
|
+
z
|
|
9516
|
+
);
|
|
9517
|
+
}
|
|
9518
|
+
/** A filled disc (or arc segment, for the fill's rounded cap). */
|
|
9519
|
+
makeDisc(radiusM, color, parent, z, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
9520
|
+
return this.addPart(
|
|
9521
|
+
new THREE19.CircleGeometry(radiusM, 32, thetaStart, thetaLength),
|
|
9522
|
+
color,
|
|
9523
|
+
parent,
|
|
9524
|
+
z
|
|
9525
|
+
);
|
|
9526
|
+
}
|
|
9527
|
+
addPart(geometry, color, parent, z) {
|
|
9528
|
+
const mesh = new THREE19.Mesh(
|
|
9529
|
+
geometry,
|
|
9530
|
+
new THREE19.MeshBasicMaterial({
|
|
9531
|
+
color,
|
|
9532
|
+
transparent: true,
|
|
9533
|
+
depthWrite: false,
|
|
9534
|
+
toneMapped: false,
|
|
9535
|
+
side: THREE19.DoubleSide
|
|
9536
|
+
})
|
|
9537
|
+
);
|
|
9538
|
+
mesh.renderOrder = FACE_RENDER_ORDER + 1;
|
|
9539
|
+
mesh.position.z = z;
|
|
9540
|
+
mesh.raycast = () => {
|
|
9541
|
+
};
|
|
9542
|
+
parent?.add(mesh);
|
|
9543
|
+
return mesh;
|
|
9544
|
+
}
|
|
9545
|
+
/** Remove + dispose knob/fill/cap so a sync pass rebuilds them fresh. */
|
|
9546
|
+
disposeMovingParts(entry) {
|
|
9547
|
+
for (const key of ["knob", "fill", "cap"]) {
|
|
9548
|
+
const mesh = entry[key];
|
|
9549
|
+
if (!mesh) continue;
|
|
9550
|
+
mesh.removeFromParent();
|
|
9551
|
+
mesh.geometry.dispose();
|
|
9552
|
+
mesh.material.dispose();
|
|
9553
|
+
entry[key] = null;
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
/**
|
|
9557
|
+
* Billboard: yaw the ENTITY TRANSFORM toward the camera. Writing the
|
|
9558
|
+
* Object3D directly would be overwritten by `TransformSyncSystem` (which
|
|
9559
|
+
* runs after this system, before render) — the same reason locomotion
|
|
9560
|
+
* writes the rig transform. Skipped in the editor so the gizmo and the
|
|
9561
|
+
* authored rotation stay in charge while authoring.
|
|
9562
|
+
*/
|
|
9563
|
+
applyFollow(world, entityId, panel) {
|
|
9564
|
+
if (panel.follow !== "billboard" || world.isEditorMode()) return;
|
|
9565
|
+
const transform = world.getComponent(entityId, TransformComponent);
|
|
9566
|
+
const object = world.getComponent(entityId, Object3DRef)?.object;
|
|
9567
|
+
if (!transform || !object) return;
|
|
9568
|
+
const camera = world.getCamera();
|
|
9569
|
+
const cam = new THREE19.Vector3();
|
|
9570
|
+
const self = new THREE19.Vector3();
|
|
9571
|
+
camera.getWorldPosition(cam);
|
|
9572
|
+
object.getWorldPosition(self);
|
|
9573
|
+
transform.rotation.y = Math.atan2(cam.x - self.x, cam.z - self.z);
|
|
9574
|
+
}
|
|
9575
|
+
destroyEntry(entry) {
|
|
9576
|
+
this.disposeMovingParts(entry);
|
|
9577
|
+
entry.face.removeFromParent();
|
|
9578
|
+
entry.face.geometry.dispose();
|
|
9579
|
+
entry.face.material.dispose();
|
|
9580
|
+
entry.texture.dispose();
|
|
9581
|
+
}
|
|
9582
|
+
};
|
|
9583
|
+
__name(_UIPanelSystem, "UIPanelSystem");
|
|
9584
|
+
var UIPanelSystem = _UIPanelSystem;
|
|
8371
9585
|
var _XRHitTestSystem = class _XRHitTestSystem {
|
|
8372
9586
|
constructor() {
|
|
8373
9587
|
this.sources = /* @__PURE__ */ new Map();
|
|
@@ -8480,9 +9694,13 @@ var _XRAnchorSystem = class _XRAnchorSystem {
|
|
|
8480
9694
|
this.anchors = /* @__PURE__ */ new Map();
|
|
8481
9695
|
/** In-flight creations, so one entity is not anchored twice. */
|
|
8482
9696
|
this.pending = /* @__PURE__ */ new Set();
|
|
9697
|
+
/** Per-anchor head-glue gates — see XREnvironmentSystem's TrackedEntry. */
|
|
9698
|
+
this.gates = /* @__PURE__ */ new Map();
|
|
8483
9699
|
this.presenting = false;
|
|
9700
|
+
this.tmpViewerMatrix = new THREE19.Matrix4();
|
|
9701
|
+
this.tmpViewPos = new THREE19.Vector3();
|
|
8484
9702
|
}
|
|
8485
|
-
update(world) {
|
|
9703
|
+
update(world, deltaTime = 0) {
|
|
8486
9704
|
const presenting = world.isXrPresenting();
|
|
8487
9705
|
if (!presenting) {
|
|
8488
9706
|
if (this.presenting) {
|
|
@@ -8502,8 +9720,11 @@ var _XRAnchorSystem = class _XRAnchorSystem {
|
|
|
8502
9720
|
if (!live.has(entityId)) {
|
|
8503
9721
|
anchor.delete?.();
|
|
8504
9722
|
this.anchors.delete(entityId);
|
|
9723
|
+
this.gates.delete(entityId);
|
|
8505
9724
|
}
|
|
8506
9725
|
}
|
|
9726
|
+
const viewer = frame.getViewerPose?.(referenceSpace);
|
|
9727
|
+
const viewerInverse = viewer ? this.tmpViewerMatrix.fromArray(viewer.transform.matrix).invert() : null;
|
|
8507
9728
|
for (const entityId of entityIds) {
|
|
8508
9729
|
const component = world.getComponent(entityId, XRAnchorComponent);
|
|
8509
9730
|
const transform = world.getComponent(entityId, TransformComponent);
|
|
@@ -8513,6 +9734,7 @@ var _XRAnchorSystem = class _XRAnchorSystem {
|
|
|
8513
9734
|
if (existing) {
|
|
8514
9735
|
existing.delete?.();
|
|
8515
9736
|
this.anchors.delete(entityId);
|
|
9737
|
+
this.gates.delete(entityId);
|
|
8516
9738
|
}
|
|
8517
9739
|
continue;
|
|
8518
9740
|
}
|
|
@@ -8523,7 +9745,21 @@ var _XRAnchorSystem = class _XRAnchorSystem {
|
|
|
8523
9745
|
}
|
|
8524
9746
|
const pose = frame.getPose(anchor.anchorSpace, referenceSpace);
|
|
8525
9747
|
if (pose && pose.emulatedPosition !== true) {
|
|
8526
|
-
|
|
9748
|
+
let gate = this.gates.get(entityId);
|
|
9749
|
+
if (!gate) {
|
|
9750
|
+
gate = new SurfacePoseGate();
|
|
9751
|
+
this.gates.set(entityId, gate);
|
|
9752
|
+
}
|
|
9753
|
+
const m = pose.transform.matrix;
|
|
9754
|
+
const refPos = { x: m[12], y: m[13], z: m[14] };
|
|
9755
|
+
let viewPos = null;
|
|
9756
|
+
if (viewerInverse) {
|
|
9757
|
+
const v = this.tmpViewPos.set(refPos.x, refPos.y, refPos.z).applyMatrix4(viewerInverse);
|
|
9758
|
+
viewPos = { x: v.x, y: v.y, z: v.z };
|
|
9759
|
+
}
|
|
9760
|
+
if (gate.update(refPos, viewPos, deltaTime)) {
|
|
9761
|
+
decomposeXrPose(pose.transform.matrix, transform);
|
|
9762
|
+
}
|
|
8527
9763
|
}
|
|
8528
9764
|
}
|
|
8529
9765
|
}
|
|
@@ -8568,6 +9804,7 @@ var _XRAnchorSystem = class _XRAnchorSystem {
|
|
|
8568
9804
|
}).finally(() => this.pending.delete(entityId));
|
|
8569
9805
|
}
|
|
8570
9806
|
releaseAll() {
|
|
9807
|
+
this.gates.clear();
|
|
8571
9808
|
for (const anchor of this.anchors.values()) anchor.delete?.();
|
|
8572
9809
|
this.anchors.clear();
|
|
8573
9810
|
this.pending.clear();
|
|
@@ -9813,6 +11050,6 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
|
|
|
9813
11050
|
__name(_XRHoverVisualSystem, "XRHoverVisualSystem");
|
|
9814
11051
|
var XRHoverVisualSystem = _XRHoverVisualSystem;
|
|
9815
11052
|
|
|
9816
|
-
export { ADDED_NODE_MARKER_COMPONENT, ARC_GRAVITY, ARC_MAX_TIME, ARC_SPEED, ARC_STEPS, AnimationSystem, AudioSystem, Behaviour, CURLED_COS, CameraSyncSystem, DEFAULT_DIRECTION_TOLERANCE, DEFAULT_ENTER_CONFIDENCE, DEFAULT_EXIT_CONFIDENCE, DEFAULT_LOST_GRACE_SECONDS, DEFAULT_MIN_HOLD_SECONDS, EnvironmentSyncSystem, FALLBACK_HAND_SCALE, HYDRATABLE_ENTITY_KINDS, HandPoseTracker, InputSystem, InstancedMeshSystem, KeyboardKeys, LightSyncSystem, MATCH_FALLOFF, MAX_PLAUSIBLE_SPEED, MAX_SAMPLE_AGE_SECONDS, MaterialSyncSystem, MeshGeometrySystem, NODE_KEY_SEPARATOR, NO_XR_CAPABILITIES, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectVisibilitySystem, PINCH_FINGERS, PREFAB_FILE_VERSION, PREFAB_FILE_VERSION_NESTED, PhysicsSyncSystem, PostprocessingSyncSystem, RenderSystem, SCRIPT_OBJECT_ROOT_FLAG, STRAIGHT_COS, ScriptBehaviourSystem, ScriptObject, ScriptObjectSystem, TeleportAimGate, ThrowVelocityTracker, TransformSyncSystem, VELOCITY_SAMPLES, VRTeleportAreaSystem, VideoSystem, XRAnchorSystem, XRControllerVisualSystem, XREnvironmentSystem, XRHandPoseSystem, XRHitTestSystem, XRHoverVisualSystem, XRInputSystem, XRInteractionSystem, XRLocomotionSystem, XRSessionSystem, XRTeleportVisualSystem, XR_FINGERS, XR_HOVER_SHELL_FLAG, angularVelocityFrom, applyHandMaterial, arcGroundCrossing, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearPrefabCache, collectDiffPaths, collectEntitySubtree, collectSnapshotSubtreeIds, computeFingerCurl, computeHandShape, computeInstanceOverrides, computePalmPose, computePalmWidth, computePinch, computeSpread, createHandMaterial, decanonicalizeComponentValue, despawnPrefab, encodeOverride, estimateHandScale, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadPrefab, matchHandPose, mergeComponentValue, parseOverride, parsePrefabAsset, prefabFileVersionFor, readXrCapabilities, restoreComponentState, restorePostCreateComponents, sampleTeleportArc, setPrefabAssetUrlResolver, spawnPrefab };
|
|
9817
|
-
//# sourceMappingURL=chunk-
|
|
9818
|
-
//# sourceMappingURL=chunk-
|
|
11053
|
+
export { ADDED_NODE_MARKER_COMPONENT, ARC_GRAVITY, ARC_MAX_TIME, ARC_SPEED, ARC_STEPS, AnimationSystem, AudioSystem, Behaviour, CURLED_COS, CameraSyncSystem, DEFAULT_DIRECTION_TOLERANCE, DEFAULT_ENTER_CONFIDENCE, DEFAULT_EXIT_CONFIDENCE, DEFAULT_LOST_GRACE_SECONDS, DEFAULT_MIN_HOLD_SECONDS, EnvironmentSyncSystem, FALLBACK_HAND_SCALE, HYDRATABLE_ENTITY_KINDS, HandPoseTracker, InputSystem, InstancedMeshSystem, KeyboardKeys, LightSyncSystem, MATCH_FALLOFF, MAX_PLAUSIBLE_SPEED, MAX_SAMPLE_AGE_SECONDS, MaterialSyncSystem, MeshGeometrySystem, NODE_KEY_SEPARATOR, NO_XR_CAPABILITIES, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectVisibilitySystem, PINCH_FINGERS, PREFAB_FILE_VERSION, PREFAB_FILE_VERSION_NESTED, PhysicsSyncSystem, PostprocessingSyncSystem, RenderSystem, SCRIPT_OBJECT_ROOT_FLAG, STRAIGHT_COS, ScriptBehaviourSystem, ScriptObject, ScriptObjectSystem, TeleportAimGate, ThrowVelocityTracker, TransformSyncSystem, UIInteractionSystem, UIPanelSystem, VELOCITY_SAMPLES, VRTeleportAreaSystem, VideoSystem, XRAnchorSystem, XRControllerVisualSystem, XREnvironmentSystem, XRHandPoseSystem, XRHitTestSystem, XRHoverVisualSystem, XRInputSystem, XRInteractionSystem, XRLocomotionSystem, XRSessionSystem, XRTeleportVisualSystem, XR_FINGERS, XR_HOVER_SHELL_FLAG, angularVelocityFrom, applyHandMaterial, arcGroundCrossing, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearPrefabCache, collectDiffPaths, collectEntitySubtree, collectSnapshotSubtreeIds, computeFingerCurl, computeHandShape, computeInstanceOverrides, computePalmPose, computePalmWidth, computePinch, computeSpread, createHandMaterial, decanonicalizeComponentValue, despawnPrefab, encodeOverride, estimateHandScale, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadPrefab, matchHandPose, mergeComponentValue, parseOverride, parsePrefabAsset, prefabFileVersionFor, readXrCapabilities, restoreComponentState, restorePostCreateComponents, sampleTeleportArc, setPrefabAssetUrlResolver, spawnPrefab };
|
|
11054
|
+
//# sourceMappingURL=chunk-Q52ADLF7.js.map
|
|
11055
|
+
//# sourceMappingURL=chunk-Q52ADLF7.js.map
|