@preference-sl/pref-viewer 2.13.0-beta.11 → 2.13.0-beta.12
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/package.json +1 -1
- package/src/babylonjs-controller.js +74 -29
- package/src/localization/i18n.js +1 -1
- package/src/pref-viewer-3d.js +1 -1
package/package.json
CHANGED
|
@@ -39,7 +39,8 @@ import { translate } from "./localization/i18n.js";
|
|
|
39
39
|
* 5. Use `setContainerVisibility`, `setMaterialOptions`, `setCameraOptions`, or `setIBLOptions` for targeted updates; these
|
|
40
40
|
* helpers stop/restart the render loop while they rebuild camera-dependent resources.
|
|
41
41
|
* 6. Invoke `disable()` when the element disconnects to tear down scenes, XR sessions, observers, and handlers.
|
|
42
|
-
*
|
|
42
|
+
* `disable()` is asynchronous; it waits for XR/session shutdown before engine disposal and also disposes the
|
|
43
|
+
* shared GLTF resolver, which closes its internal IndexedDB handle.
|
|
43
44
|
*
|
|
44
45
|
* Public API Highlights
|
|
45
46
|
* - constructor(canvas, containers, options)
|
|
@@ -65,6 +66,8 @@ import { translate } from "./localization/i18n.js";
|
|
|
65
66
|
* in SSR/Node contexts (though functionality activates only in browsers).
|
|
66
67
|
* - Pointer-pick lifecycle: hover/highlight raycasts are sampled on POINTERMOVE (time + distance thresholds), wheel
|
|
67
68
|
* input avoids picks entirely, and right-click POINTERUP performs an on-demand pick for context-menu targeting.
|
|
69
|
+
* - Teardown lifecycle: concurrent `disable()` calls are coalesced into a single in-flight promise to avoid races
|
|
70
|
+
* during XR exit and engine disposal.
|
|
68
71
|
*/
|
|
69
72
|
export default class BabylonJSController {
|
|
70
73
|
#RENDER_SETTINGS_STORAGE_KEY = "pref-viewer/render-settings";
|
|
@@ -96,7 +99,7 @@ export default class BabylonJSController {
|
|
|
96
99
|
#shadowGen = [];
|
|
97
100
|
#XRExperience = null;
|
|
98
101
|
#canvasResizeObserver = null;
|
|
99
|
-
|
|
102
|
+
|
|
100
103
|
#hdrTexture = null; // reusable in-memory HDR source cloned into scene.environmentTexture across reloads
|
|
101
104
|
#lastPickedMeshId = null;
|
|
102
105
|
|
|
@@ -148,6 +151,12 @@ export default class BabylonJSController {
|
|
|
148
151
|
movePickMinDistancePx: 2, // skip picks for sub-pixel jitter
|
|
149
152
|
};
|
|
150
153
|
|
|
154
|
+
// Promises to track async disable() lifecycle when XR and general teardown may run concurrently; ensures idempotent disable calls are safe and callers can await full teardown completion.
|
|
155
|
+
#disablingPromises = {
|
|
156
|
+
xr: null,
|
|
157
|
+
general: null,
|
|
158
|
+
};
|
|
159
|
+
|
|
151
160
|
/**
|
|
152
161
|
* Constructs a new BabylonJSController instance.
|
|
153
162
|
* Initializes the canvas, asset containers, and options for the Babylon.js scene.
|
|
@@ -1308,30 +1317,43 @@ export default class BabylonJSController {
|
|
|
1308
1317
|
|
|
1309
1318
|
/**
|
|
1310
1319
|
* Disposes the Babylon.js WebXR experience if it exists.
|
|
1320
|
+
* If XR is currently active, waits for `exitXRAsync()` before disposing to avoid
|
|
1321
|
+
* tearing down the engine while the XR session is still shutting down.
|
|
1322
|
+
* Concurrent calls share the same in-flight promise so disposal runs only once.
|
|
1311
1323
|
* @private
|
|
1312
|
-
* @returns {void}
|
|
1324
|
+
* @returns {Promise<void>}
|
|
1313
1325
|
*/
|
|
1314
|
-
#disposeXRExperience() {
|
|
1315
|
-
if (
|
|
1326
|
+
async #disposeXRExperience() {
|
|
1327
|
+
if (this.#disablingPromises.xr) {
|
|
1328
|
+
return await this.#disablingPromises.xr;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
const xrExperience = this.#XRExperience;
|
|
1332
|
+
if (!xrExperience) {
|
|
1316
1333
|
return;
|
|
1317
1334
|
}
|
|
1318
1335
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
.
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1336
|
+
this.#disablingPromises.xr = (async () => {
|
|
1337
|
+
try {
|
|
1338
|
+
if (xrExperience.baseExperience?.state === WebXRState.IN_XR) {
|
|
1339
|
+
await xrExperience.baseExperience.exitXRAsync();
|
|
1340
|
+
}
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
console.warn("PrefViewer: Error exiting XR experience:", error);
|
|
1343
|
+
} finally {
|
|
1344
|
+
try {
|
|
1345
|
+
xrExperience.dispose();
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
console.warn("PrefViewer: Error disposing XR experience:", error);
|
|
1348
|
+
}
|
|
1349
|
+
if (this.#XRExperience === xrExperience) {
|
|
1329
1350
|
this.#XRExperience = null;
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1351
|
+
}
|
|
1352
|
+
this.#disablingPromises.xr = null;
|
|
1353
|
+
}
|
|
1354
|
+
})();
|
|
1355
|
+
|
|
1356
|
+
await this.#disablingPromises.xr;
|
|
1335
1357
|
}
|
|
1336
1358
|
|
|
1337
1359
|
/**
|
|
@@ -2345,17 +2367,40 @@ export default class BabylonJSController {
|
|
|
2345
2367
|
/**
|
|
2346
2368
|
* Disposes the Babylon.js engine and disconnects the canvas resize observer.
|
|
2347
2369
|
* Cleans up all scene, camera, light, XR, and GLTF resolver resources.
|
|
2370
|
+
* The teardown is asynchronous: it waits for XR/session-dependent shutdown work
|
|
2371
|
+
* before disposing the engine, and coalesces concurrent calls into one in-flight promise.
|
|
2348
2372
|
* @public
|
|
2349
|
-
* @returns {void}
|
|
2373
|
+
* @returns {Promise<void>}
|
|
2350
2374
|
*/
|
|
2351
|
-
disable() {
|
|
2352
|
-
this.#
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
this.#
|
|
2357
|
-
|
|
2358
|
-
|
|
2375
|
+
async disable() {
|
|
2376
|
+
if (this.#disablingPromises.general) {
|
|
2377
|
+
return await this.#disablingPromises.general;
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
this.#disablingPromises.general = (async () => {
|
|
2381
|
+
this.#disableInteraction();
|
|
2382
|
+
this.#disposeAnimationController();
|
|
2383
|
+
this.#disposeGLTFResolver();
|
|
2384
|
+
try {
|
|
2385
|
+
await this.#disposeXRExperience();
|
|
2386
|
+
} catch (error) {
|
|
2387
|
+
console.warn("PrefViewer: Error while disposing XR experience:", error);
|
|
2388
|
+
}
|
|
2389
|
+
try {
|
|
2390
|
+
await this.#unloadCameraDependentEffects();
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
console.warn("PrefViewer: Error while unloading camera-dependent effects:", error);
|
|
2393
|
+
} finally {
|
|
2394
|
+
this.#stopEngineRenderLoop();
|
|
2395
|
+
this.#disposeEngine();
|
|
2396
|
+
}
|
|
2397
|
+
})();
|
|
2398
|
+
|
|
2399
|
+
try {
|
|
2400
|
+
await this.#disablingPromises.general;
|
|
2401
|
+
} finally {
|
|
2402
|
+
this.#disablingPromises.general = null;
|
|
2403
|
+
}
|
|
2359
2404
|
}
|
|
2360
2405
|
|
|
2361
2406
|
/**
|
package/src/localization/i18n.js
CHANGED
package/src/pref-viewer-3d.js
CHANGED