@needle-tools/engine 5.1.0 → 5.1.2
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/CHANGELOG.md +7 -1
- package/dist/{needle-engine.bundle-5oIL49YP.js → needle-engine.bundle-BAUfRyBA.js} +7692 -7506
- package/dist/{needle-engine.bundle-DvOrfXws.umd.cjs → needle-engine.bundle-BL2wG5Te.umd.cjs} +159 -159
- package/dist/{needle-engine.bundle-DAAfj8Cs.min.js → needle-engine.bundle-BpNRkY3s.min.js} +184 -184
- package/dist/needle-engine.d.ts +25 -7
- package/dist/needle-engine.js +533 -533
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +1 -1
- package/lib/engine/api.js +1 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +45 -7
- package/lib/engine/engine_license.js +276 -77
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking.js +6 -0
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_networking_peer.js +19 -13
- package/lib/engine/engine_networking_peer.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_qrcode.js.map +1 -1
- package/lib/engine/webcomponents/WebXRButtons.js +5 -6
- package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js +5 -5
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +31 -2
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +7 -1
- package/lib/engine/xr/NeedleXRSession.js +70 -11
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/NeedleXRSync.js +23 -4
- package/lib/engine/xr/NeedleXRSync.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine-components/SyncedTransform.d.ts +5 -1
- package/lib/engine-components/SyncedTransform.js +45 -8
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/webxr/Avatar.d.ts +8 -0
- package/lib/engine-components/webxr/Avatar.js +60 -32
- package/lib/engine-components/webxr/Avatar.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.js +25 -14
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/lib/engine-schemes/synced-transform-model.d.ts +2 -0
- package/lib/engine-schemes/synced-transform-model.js +8 -1
- package/lib/engine-schemes/synced-transform-model.js.map +1 -1
- package/package.json +2 -2
- package/plugins/common/license.js +4 -4
- package/plugins/types/userconfig.d.ts +4 -3
- package/plugins/vite/build-pipeline.d.ts +3 -1
- package/plugins/vite/build-pipeline.js +19 -9
- package/plugins/vite/license.js +4 -4
- package/src/engine/api.ts +1 -1
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_license.ts +263 -73
- package/src/engine/engine_networking.ts +5 -0
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_networking_peer.ts +12 -13
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/webcomponents/WebXRButtons.ts +5 -6
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +5 -5
- package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
- package/src/engine/webcomponents/needle-engine.ts +31 -2
- package/src/engine/xr/NeedleXRSession.ts +72 -14
- package/src/engine/xr/NeedleXRSync.ts +24 -4
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/SyncedTransform.ts +49 -10
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/src/engine-components/webxr/Avatar.ts +60 -33
- package/src/engine-components/webxr/WebXR.ts +23 -15
- package/src/engine-schemes/synced-transform-model.ts +10 -1
- package/src/engine-schemes/transforms.fbs +1 -0
|
@@ -42,6 +42,10 @@ export class Avatar extends Behaviour {
|
|
|
42
42
|
private _leftHandMeshes?: Mesh[];
|
|
43
43
|
private _rightHandMeshes?: Mesh[];
|
|
44
44
|
|
|
45
|
+
// meshes of remote avatar hands - hidden render-only so the hand root stays active and keeps syncing
|
|
46
|
+
private _remoteLeftHandMeshes?: Mesh[];
|
|
47
|
+
private _remoteRightHandMeshes?: Mesh[];
|
|
48
|
+
|
|
45
49
|
private _syncTransforms?: SyncedTransform[];
|
|
46
50
|
|
|
47
51
|
async onEnterXR(_args: NeedleXREventArgs) {
|
|
@@ -120,46 +124,54 @@ export class Avatar extends Behaviour {
|
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
// synchronize hands
|
|
127
|
+
// IMPORTANT: never toggle `.visible` on the hand object itself - it carries the SyncedTransform,
|
|
128
|
+
// and setting it invisible deactivates the component (onDisable -> freeOwnership), which means the
|
|
129
|
+
// owner loses ownership and stops sending (the remote hand then stays frozen). The head syncs fine
|
|
130
|
+
// precisely because its synced root is never deactivated (only a child mesh is hidden). So we keep
|
|
131
|
+
// the hand object active and hide only the rendered meshes (render-only, via setCustomVisibility).
|
|
123
132
|
const leftCtrl = args.xr.leftController;
|
|
124
133
|
const leftObj = this.leftHand?.asset as Object3D;
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
if (leftObj) {
|
|
135
|
+
if (leftCtrl) {
|
|
136
|
+
leftObj.position.copy(leftCtrl.gripPosition);
|
|
137
|
+
leftObj.quaternion.copy(leftCtrl.gripQuaternion);
|
|
138
|
+
leftObj.quaternion.multiply(flipForwardQuaternion);
|
|
139
|
+
}
|
|
140
|
+
leftObj.visible = true;
|
|
130
141
|
this.updateHandVisibility(leftCtrl, leftObj, this._leftHandMeshes);
|
|
131
142
|
}
|
|
132
|
-
else if (leftObj && leftObj.visible) {
|
|
133
|
-
leftObj.visible = false;
|
|
134
|
-
}
|
|
135
143
|
|
|
136
144
|
const right = args.xr.rightController;
|
|
137
145
|
const rightObj = this.rightHand?.asset as Object3D;
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
if (rightObj) {
|
|
147
|
+
if (right) {
|
|
148
|
+
rightObj.position.copy(right.gripPosition);
|
|
149
|
+
rightObj.quaternion.copy(right.gripQuaternion);
|
|
150
|
+
rightObj.quaternion.multiply(flipForwardQuaternion);
|
|
151
|
+
}
|
|
152
|
+
rightObj.visible = true;
|
|
143
153
|
this.updateHandVisibility(right, rightObj, this._rightHandMeshes);
|
|
144
154
|
}
|
|
145
|
-
else if (rightObj && rightObj.visible) {
|
|
146
|
-
rightObj.visible = false;
|
|
147
|
-
}
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
onBeforeRender(): void {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
// Remote avatar visibility depends on the *owner's* tracking state, not on whether THIS client is
|
|
159
|
+
// in XR: other users' avatars are syncInstantiated onto non-XR clients too (spectators / lobby),
|
|
160
|
+
// and must hide/show their hands correctly there as well. So this runs regardless of the local XR
|
|
161
|
+
// state. updateRemoteAvatarVisibility() early-outs for the local player and when not connected.
|
|
162
|
+
if (this.context.time.frame % 10 === 0)
|
|
163
|
+
this.updateRemoteAvatarVisibility();
|
|
155
164
|
}
|
|
156
165
|
|
|
157
|
-
private updateHandVisibility(controller: NeedleXRController, avatarHand: Object3D, meshes: Mesh[] | undefined) {
|
|
166
|
+
private updateHandVisibility(controller: NeedleXRController | null | undefined, avatarHand: Object3D, meshes: Mesh[] | undefined) {
|
|
158
167
|
if (meshes) {
|
|
159
|
-
// Hide the hand meshes
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
// Hide the hand meshes (render-only) when the hand is not tracked, or when another model
|
|
169
|
+
// (e.g. the controller/hand model) is being rendered in its place. We hide the meshes rather
|
|
170
|
+
// than the object's `visible` flag because that would disable SyncedTransform networking.
|
|
171
|
+
const isTracking = controller?.isTracking === true;
|
|
172
|
+
const hasOtherRenderingModel = !!controller?.model && controller.model.visible && controller.model !== avatarHand;
|
|
173
|
+
const render = isTracking && !hasOtherRenderingModel;
|
|
174
|
+
meshes.forEach(mesh => { setCustomVisibility(mesh, render); });
|
|
163
175
|
}
|
|
164
176
|
}
|
|
165
177
|
|
|
@@ -173,14 +185,13 @@ export class Avatar extends Behaviour {
|
|
|
173
185
|
if (sync.hasState(state.owner)) {
|
|
174
186
|
this.tryFindAvatarObjectsIfMissing();
|
|
175
187
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
188
|
+
// Keep the hand object active (don't toggle `.visible`) so its SyncedTransform keeps
|
|
189
|
+
// applying updates and a non-owner freeOwnership can't strip the owner's ownership.
|
|
190
|
+
// Hide/show the rendered meshes based on the owner's tracking state instead.
|
|
191
|
+
const leftRendered = sync.isTracking(state.owner, "left") ?? false;
|
|
192
|
+
this._remoteLeftHandMeshes = this.setRemoteHandRendered(this.leftHand?.asset as Object3D, this._remoteLeftHandMeshes, leftRendered);
|
|
193
|
+
const rightRendered = sync.isTracking(state.owner, "right") ?? false;
|
|
194
|
+
this._remoteRightHandMeshes = this.setRemoteHandRendered(this.rightHand?.asset as Object3D, this._remoteRightHandMeshes, rightRendered);
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
197
|
|
|
@@ -198,6 +209,22 @@ export class Avatar extends Behaviour {
|
|
|
198
209
|
|
|
199
210
|
|
|
200
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Show/hide a remote avatar hand render-only. The hand object stays active so its SyncedTransform keeps
|
|
214
|
+
* applying networked updates (toggling `.visible` would deactivate it and could free the owner's ownership).
|
|
215
|
+
* The mesh list is gathered lazily and cached, and returned so the caller can keep it.
|
|
216
|
+
*/
|
|
217
|
+
private setRemoteHandRendered(obj: Object3D | undefined, meshes: Mesh[] | undefined, rendered: boolean): Mesh[] | undefined {
|
|
218
|
+
if (!obj) return meshes;
|
|
219
|
+
obj.visible = true;
|
|
220
|
+
if (!meshes || meshes.length === 0) {
|
|
221
|
+
meshes = [];
|
|
222
|
+
obj.traverse(o => { if ((o as Mesh).isMesh) meshes!.push(o as Mesh); });
|
|
223
|
+
}
|
|
224
|
+
for (const mesh of meshes) setCustomVisibility(mesh, rendered);
|
|
225
|
+
return meshes;
|
|
226
|
+
}
|
|
227
|
+
|
|
201
228
|
private tryFindAvatarObjectsIfMissing() {
|
|
202
229
|
// if no avatar objects are set, try to find them
|
|
203
230
|
if (!this.head || !this.leftHand || !this.rightHand) {
|
|
@@ -237,22 +237,30 @@ export class WebXR extends Behaviour {
|
|
|
237
237
|
// Showing the QuickLook button depends on whether we're on iOS or visionOS –
|
|
238
238
|
// on iOS we have AppClip support, so we don't need QuickLook button there,
|
|
239
239
|
// while on visionOS we use QuickLook for AR experiences for now (unless it happens to have WebXR support by then).
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
this._usdzExporter = GameObject.addComponent(this.gameObject, USDZExporter);
|
|
250
|
-
this._usdzExporter.objectToExport = this.context.scene;
|
|
251
|
-
this._usdzExporter.autoExportAnimations = true;
|
|
252
|
-
this._usdzExporter.autoExportAudioSources = true;
|
|
253
|
-
}
|
|
240
|
+
const addUsdzExporterIfMissing = () => {
|
|
241
|
+
const existingUSDZExporter = GameObject.findObjectOfType(USDZExporter);
|
|
242
|
+
if (!existingUSDZExporter) {
|
|
243
|
+
// if no USDZ Exporter is found we add one and assign the scene to be exported
|
|
244
|
+
if (debug) console.log("WebXR: Adding USDZExporter");
|
|
245
|
+
this._usdzExporter = GameObject.addComponent(this.gameObject, USDZExporter);
|
|
246
|
+
this._usdzExporter.objectToExport = this.context.scene;
|
|
247
|
+
this._usdzExporter.autoExportAnimations = true;
|
|
248
|
+
this._usdzExporter.autoExportAudioSources = true;
|
|
254
249
|
}
|
|
255
|
-
}
|
|
250
|
+
};
|
|
251
|
+
// QuickLook is added when explicitly requested, or as the visionOS fallback when
|
|
252
|
+
// WebXR AR isn't available. Only probe XR support on visionOS (the only case that
|
|
253
|
+
// consumes the result) and go through the cached NeedleXRSession.isARSupported() so
|
|
254
|
+
// we don't eagerly hit navigator.xr on every enable — that probe trips the
|
|
255
|
+
// `xr-spatial-tracking` Permissions-Policy in embeds that weren't granted it.
|
|
256
|
+
if (this.useQuicklookExport) {
|
|
257
|
+
addUsdzExporterIfMissing();
|
|
258
|
+
}
|
|
259
|
+
else if (DeviceUtilities.isVisionOS()) {
|
|
260
|
+
NeedleXRSession.isARSupported().then((arSupported) => {
|
|
261
|
+
if (!arSupported) addUsdzExporterIfMissing();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
256
264
|
|
|
257
265
|
this.handleCreatingHTML();
|
|
258
266
|
this.handleOfferSession();
|
|
@@ -54,8 +54,13 @@ dontSave():boolean {
|
|
|
54
54
|
return offset ? !!this.bb!.readInt8(this.bb_pos + offset) : false;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
version():number {
|
|
58
|
+
const offset = this.bb!.__offset(this.bb_pos, 12);
|
|
59
|
+
return offset ? this.bb!.readInt32(this.bb_pos + offset) : 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
static startSyncedTransformModel(builder:flatbuffers.Builder) {
|
|
58
|
-
builder.startObject(
|
|
63
|
+
builder.startObject(5);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
static addGuid(builder:flatbuffers.Builder, guidOffset:flatbuffers.Offset) {
|
|
@@ -74,6 +79,10 @@ static addDontSave(builder:flatbuffers.Builder, dontSave:boolean) {
|
|
|
74
79
|
builder.addFieldInt8(3, +dontSave, +false);
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
static addVersion(builder:flatbuffers.Builder, version:number) {
|
|
83
|
+
builder.addFieldInt32(4, version, 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
77
86
|
static endSyncedTransformModel(builder:flatbuffers.Builder):flatbuffers.Offset {
|
|
78
87
|
const offset = builder.endObject();
|
|
79
88
|
return offset;
|