@preference-sl/pref-viewer 2.16.0-beta.1 → 2.16.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +13 -0
- package/README.md +486 -0
- package/package.json +7 -7
- package/src/babylonjs-animation-opening.js +11 -5
- package/src/babylonjs-controller.js +455 -903
- package/src/file-storage.js +5 -126
- package/src/gltf-resolver.js +24 -58
- package/src/localization/translations.js +5 -17
- package/src/material-utils.js +72 -0
- package/src/pref-viewer-2d.js +14 -21
- package/src/pref-viewer-3d.js +160 -193
- package/src/pref-viewer-menu-3d.js +185 -268
- package/src/pref-viewer-task.js +7 -2
- package/src/pref-viewer.js +549 -333
- package/src/render-math.js +127 -0
- package/src/styles.js +163 -179
- package/Readme.md +0 -8
package/src/pref-viewer-3d.js
CHANGED
|
@@ -10,14 +10,15 @@ import FileStorage from "./file-storage.js";
|
|
|
10
10
|
* - Encapsulates a Babylon.js-powered 3D viewer for displaying models, environments, and materials.
|
|
11
11
|
* - Manages internal state for containers (model, environment, materials) and options (camera, materials, IBL, render toggles).
|
|
12
12
|
* - Handles asset loading, configuration, and option updates (camera, materials, IBL) through attributes and public methods.
|
|
13
|
-
* - Exposes getters/setters for render settings so PrefViewerMenu3D or other UIs can
|
|
13
|
+
* - Exposes getters/setters for render settings so PrefViewerMenu3D or other UIs can toggle AA/SSAO/IBL/shadows,
|
|
14
|
+
* hover highlight, and time-of-day lighting.
|
|
14
15
|
* - Provides API for showing/hiding the viewer, model, and environment, and for downloading assets.
|
|
15
16
|
* - Emits custom events for loading, loaded, option-setting, and render-setting workflows.
|
|
16
17
|
*
|
|
17
18
|
* Usage:
|
|
18
19
|
* - Use as a custom HTML element: <pref-viewer-3d ...>
|
|
19
20
|
* - Configure via attributes (config, options, show-model, show-scene, visible).
|
|
20
|
-
* - Call getRenderSettings()/applyRenderSettings() to synchronize UI
|
|
21
|
+
* - Call getRenderSettings()/applyRenderSettings() to synchronize UI controls with BabylonJSController settings.
|
|
21
22
|
* - Control viewer state and assets via public methods.
|
|
22
23
|
*
|
|
23
24
|
* Observed Attributes:
|
|
@@ -30,12 +31,19 @@ import FileStorage from "./file-storage.js";
|
|
|
30
31
|
* - hide(): Hides the 3D viewer component.
|
|
31
32
|
* - load(config): Loads the provided configuration into the viewer.
|
|
32
33
|
* - setOptions(options): Sets viewer options such as camera, materials, and image-based lighting (IBL).
|
|
33
|
-
* - getRenderSettings(): Returns the current
|
|
34
|
-
* - applyRenderSettings(settings):
|
|
34
|
+
* - getRenderSettings(): Returns the current render settings snapshot, including highlight and lighting values.
|
|
35
|
+
* - applyRenderSettings(settings): Applies render settings, reloading only when a setting requires it.
|
|
35
36
|
* - showModel(): Shows the 3D model.
|
|
36
37
|
* - hideModel(): Hides the 3D model.
|
|
37
38
|
* - showEnvironment(): Shows the 3D environment/scene.
|
|
38
39
|
* - hideEnvironment(): Hides the 3D environment/scene.
|
|
40
|
+
* - isIBLAvailable(): Reports whether a valid IBL environment map is currently available.
|
|
41
|
+
* - isAnimationMenuEnabled(): Reports whether the animation control menu is currently enabled.
|
|
42
|
+
* - getDebugInfo(): Returns a diagnostic snapshot of the underlying Babylon.js runtime.
|
|
43
|
+
* - getAnimations(): Returns a snapshot of all available animations and their current state.
|
|
44
|
+
* - playAnimation(name, action): Controls a named animation by applying the specified action.
|
|
45
|
+
* - setAnimationProgress(name, progress): Seeks a named animation to a normalized progress position (0–1).
|
|
46
|
+
* - setAnimationLoop(name, loop): Enables or disables loop mode for a named animation.
|
|
39
47
|
* - downloadModelGLB(): Downloads the current 3D model as a GLB file.
|
|
40
48
|
* - downloadModelGLTF(): Downloads the current 3D model as a glTF ZIP file.
|
|
41
49
|
* - downloadModelUSDZ(): Downloads the current 3D model as a USDZ file.
|
|
@@ -56,15 +64,16 @@ import FileStorage from "./file-storage.js";
|
|
|
56
64
|
* - #checkNeedToUpdateCamera(options): Detects camera option changes and marks them pending.
|
|
57
65
|
* - #checkNeedToUpdateMaterials(options): Resolves material overrides that require updates.
|
|
58
66
|
* - #checkNeedToUpdateIBL(options): Fetches HDR URLs/timestamps and enqueues IBL updates when needed.
|
|
67
|
+
* - #checkNeedToUpdateLighting(options): Resolves time-of-day changes and marks lighting as pending.
|
|
59
68
|
* - #resetUpdateFlags(): Clears pending/success flags after loads or option-setting flows.
|
|
60
69
|
* - #onLoading/#onLoaded/#onSettingOptions/#onSetOptions(): Dispatch lifecycle events and synchronize attributes.
|
|
61
70
|
*
|
|
62
71
|
* Events:
|
|
63
|
-
* - "
|
|
64
|
-
* - "
|
|
65
|
-
* - "
|
|
66
|
-
* - "
|
|
67
|
-
* - "
|
|
72
|
+
* - "pref-viewer-3d-loading": Dispatched when a loading operation starts. Legacy alias: "scene-loading".
|
|
73
|
+
* - "pref-viewer-3d-loaded": Dispatched when a loading operation completes. Legacy alias: "scene-loaded".
|
|
74
|
+
* - "pref-viewer-3d-options-applying": Dispatched when viewer options are being set. Legacy alias: "scene-setting-options".
|
|
75
|
+
* - "pref-viewer-3d-options-applied": Dispatched when viewer options have been set. Legacy alias: "scene-set-options".
|
|
76
|
+
* - "pref-viewer-3d-animation-changed": Dispatched by OpeningAnimation instances via the pref-viewer-3d host when an animation state, progress, or loop value changes.
|
|
68
77
|
*
|
|
69
78
|
* Notes:
|
|
70
79
|
* - Internal state and heavy initialization are handled in connectedCallback.
|
|
@@ -77,7 +86,6 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
77
86
|
#isLoaded = false;
|
|
78
87
|
#isVisible = false;
|
|
79
88
|
#lastLoadTimestamp = undefined;
|
|
80
|
-
#suppressVisibilityAttributeCallback = false;
|
|
81
89
|
|
|
82
90
|
#wrapper = null;
|
|
83
91
|
#canvas = null;
|
|
@@ -99,7 +107,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
99
107
|
* @returns {string[]} Array of attribute names that trigger attributeChangedCallback.
|
|
100
108
|
*/
|
|
101
109
|
static get observedAttributes() {
|
|
102
|
-
return ["show-model", "show-scene", "visible"
|
|
110
|
+
return ["show-model", "show-scene", "visible"];
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
/**
|
|
@@ -114,9 +122,6 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
114
122
|
* - "visible": shows or hides the component according to the attribute value ("true"/"false").
|
|
115
123
|
*/
|
|
116
124
|
attributeChangedCallback(name, _old, value) {
|
|
117
|
-
if (this.#suppressVisibilityAttributeCallback && (name === "show-model" || name === "show-scene")) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
125
|
switch (name) {
|
|
121
126
|
case "show-model":
|
|
122
127
|
if (_old === value) {
|
|
@@ -142,18 +147,6 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
142
147
|
this.hide();
|
|
143
148
|
}
|
|
144
149
|
break;
|
|
145
|
-
case "show-animation-menu":
|
|
146
|
-
case "highlight-color":
|
|
147
|
-
case "highlight-enabled":
|
|
148
|
-
// Pass these to BabylonJSController if initialized
|
|
149
|
-
if (this.#babylonJSController && typeof this.#babylonJSController.setIlluminationConfig === "function") {
|
|
150
|
-
this.#babylonJSController.setIlluminationConfig({
|
|
151
|
-
showAnimationMenu: this.getAttribute("show-animation-menu"),
|
|
152
|
-
highlightColor: this.getAttribute("highlight-color"),
|
|
153
|
-
highlightEnabled: this.getAttribute("highlight-enabled"),
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
break;
|
|
157
150
|
}
|
|
158
151
|
}
|
|
159
152
|
|
|
@@ -234,8 +227,10 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
234
227
|
outerFloor: new MaterialData("outerFloor", undefined, undefined, ["outerFloor"]),
|
|
235
228
|
},
|
|
236
229
|
ibl: new IBLData(),
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
render: {
|
|
231
|
+
showAnimationMenu: false,
|
|
232
|
+
lightingTimeOfDay: 0.5,
|
|
233
|
+
showLightingTimeOfDay: false,
|
|
239
234
|
},
|
|
240
235
|
},
|
|
241
236
|
};
|
|
@@ -250,15 +245,6 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
250
245
|
this.#babylonJSController = new BabylonJSController(this.#canvas, this.#data.containers, this.#data.options);
|
|
251
246
|
this.#babylonJSController.enable();
|
|
252
247
|
|
|
253
|
-
// Apply highlight attributes that were set before the controller was ready
|
|
254
|
-
const highlightColor = this.getAttribute("highlight-color");
|
|
255
|
-
const highlightEnabled = this.getAttribute("highlight-enabled");
|
|
256
|
-
if (highlightColor !== null || highlightEnabled !== null) {
|
|
257
|
-
this.#babylonJSController.setIlluminationConfig({
|
|
258
|
-
highlightColor: highlightColor ?? undefined,
|
|
259
|
-
highlightEnabled: highlightEnabled ?? undefined,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
248
|
}
|
|
263
249
|
|
|
264
250
|
/**
|
|
@@ -430,29 +416,37 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
430
416
|
iblState.setValues(url, cachedUrl, intensity, shadows, timeStamp);
|
|
431
417
|
}
|
|
432
418
|
}
|
|
433
|
-
|
|
419
|
+
|
|
434
420
|
return needUpdate;
|
|
435
421
|
}
|
|
436
422
|
|
|
437
423
|
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
424
|
+
* Checks whether any lighting-related value in `options.render` differs from the cached state.
|
|
425
|
+
* `options.render.showLightingTimeOfDay` updates slider visibility, while
|
|
426
|
+
* `options.render.lightingTimeOfDay` updates the normalized 0..1 lighting value.
|
|
441
427
|
* @private
|
|
442
|
-
* @param {object} options - Options payload containing
|
|
443
|
-
* @
|
|
428
|
+
* @param {object} options - Options payload containing a `render` sub-object.
|
|
429
|
+
* @param {boolean|string} [options.render.showLightingTimeOfDay] - Whether the time-of-day slider should be visible.
|
|
430
|
+
* @param {number} [options.render.lightingTimeOfDay] - Desired time-of-day, normalized to 0..1.
|
|
431
|
+
* @returns {boolean} True when either the visibility flag or the time value differs from the cached state.
|
|
444
432
|
*/
|
|
445
433
|
#checkNeedToUpdateLighting(options) {
|
|
446
434
|
if (!options) {
|
|
447
435
|
return false;
|
|
448
436
|
}
|
|
449
437
|
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
438
|
+
const incomingTimeEnabled = options.render?.showLightingTimeOfDay;
|
|
439
|
+
if (incomingTimeEnabled !== undefined) {
|
|
440
|
+
const dataTimeEnabled = this.#data.options.render.showLightingTimeOfDay;
|
|
441
|
+
const needUpdate = dataTimeEnabled !== incomingTimeEnabled;
|
|
442
|
+
this.#data.options.render.showLightingTimeOfDay = incomingTimeEnabled;
|
|
443
|
+
if (needUpdate) {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const dataTime = this.#data.options.render.lightingTimeOfDay;
|
|
449
|
+
const incomingTime = typeof options.render?.lightingTimeOfDay === "number" ? options.render.lightingTimeOfDay : undefined;
|
|
456
450
|
|
|
457
451
|
if (!Number.isFinite(incomingTime)) {
|
|
458
452
|
return false;
|
|
@@ -460,9 +454,9 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
460
454
|
|
|
461
455
|
const normalizedTime = Math.min(1, Math.max(0, incomingTime));
|
|
462
456
|
const controllerTime = this.#babylonJSController?.getRenderSettings?.()?.lightingTimeOfDay;
|
|
463
|
-
const currentTime = Number.isFinite(controllerTime) ? controllerTime :
|
|
457
|
+
const currentTime = Number.isFinite(controllerTime) ? controllerTime : dataTime;
|
|
464
458
|
const needUpdate = normalizedTime !== currentTime;
|
|
465
|
-
|
|
459
|
+
this.#data.options.render.lightingTimeOfDay = normalizedTime;
|
|
466
460
|
return needUpdate;
|
|
467
461
|
}
|
|
468
462
|
|
|
@@ -477,8 +471,10 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
477
471
|
bubbles: true, // indicates whether the event bubbles up through the DOM tree or not
|
|
478
472
|
cancelable: true, // indicates whether the event can be canceled, and therefore prevented as if the event never happened
|
|
479
473
|
composed: false, // indicates whether or not the event will propagate across the shadow DOM boundary into the standard DOM
|
|
474
|
+
|
|
480
475
|
};
|
|
481
|
-
this.dispatchEvent(new CustomEvent("
|
|
476
|
+
this.dispatchEvent(new CustomEvent("pref-viewer-3d-loading", customEventOptions));
|
|
477
|
+
this.dispatchEvent(new CustomEvent("scene-loading", customEventOptions)); // Legacy alias for backward compatibility
|
|
482
478
|
|
|
483
479
|
this.removeAttribute("loaded");
|
|
484
480
|
this.setAttribute("loading", "");
|
|
@@ -526,7 +522,8 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
526
522
|
composed: false,
|
|
527
523
|
detail: detail,
|
|
528
524
|
};
|
|
529
|
-
this.dispatchEvent(new CustomEvent("
|
|
525
|
+
this.dispatchEvent(new CustomEvent("pref-viewer-3d-loaded", customEventOptions));
|
|
526
|
+
this.dispatchEvent(new CustomEvent("scene-loaded", customEventOptions)); // Legacy alias for backward compatibility
|
|
530
527
|
|
|
531
528
|
this.removeAttribute("loading");
|
|
532
529
|
this.setAttribute("loaded", "");
|
|
@@ -549,7 +546,8 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
549
546
|
cancelable: true,
|
|
550
547
|
composed: false,
|
|
551
548
|
};
|
|
552
|
-
this.dispatchEvent(new CustomEvent("
|
|
549
|
+
this.dispatchEvent(new CustomEvent("pref-viewer-3d-options-applying", customEventOptions));
|
|
550
|
+
this.dispatchEvent(new CustomEvent("scene-setting-options", customEventOptions)); // Legacy alias for backward compatibility
|
|
553
551
|
|
|
554
552
|
this.removeAttribute("loaded");
|
|
555
553
|
this.setAttribute("loading", "");
|
|
@@ -591,7 +589,8 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
591
589
|
composed: false,
|
|
592
590
|
detail: detail,
|
|
593
591
|
};
|
|
594
|
-
this.dispatchEvent(new CustomEvent("
|
|
592
|
+
this.dispatchEvent(new CustomEvent("pref-viewer-3d-options-applied", customEventOptions));
|
|
593
|
+
this.dispatchEvent(new CustomEvent("scene-set-options", customEventOptions)); // Legacy alias for backward compatibility
|
|
595
594
|
|
|
596
595
|
this.removeAttribute("loading");
|
|
597
596
|
this.setAttribute("loaded", "");
|
|
@@ -648,10 +647,15 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
648
647
|
this.#checkNeedToUpdateContainers(config);
|
|
649
648
|
|
|
650
649
|
// Options
|
|
651
|
-
let needUpdateLighting = false;
|
|
652
650
|
const iblOptions = config.scene?.ibl
|
|
653
|
-
|
|
654
|
-
|
|
651
|
+
? { ...(config.options ?? {}), ibl: { ...(config.options?.ibl ?? {}), ...config.scene.ibl } }
|
|
652
|
+
: config.options;
|
|
653
|
+
|
|
654
|
+
if (iblOptions) {
|
|
655
|
+
await this.#checkNeedToUpdateIBL(iblOptions);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
let needUpdateLighting = false;
|
|
655
659
|
|
|
656
660
|
if (config.options) {
|
|
657
661
|
this.#checkNeedToUpdateCamera(config.options);
|
|
@@ -659,16 +663,15 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
659
663
|
needUpdateLighting = this.#checkNeedToUpdateLighting(config.options);
|
|
660
664
|
}
|
|
661
665
|
|
|
662
|
-
if (iblOptions) {
|
|
663
|
-
await this.#checkNeedToUpdateIBL(iblOptions);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
const loadDetail = await this.#babylonJSController.load(forceReload);
|
|
667
|
-
|
|
668
666
|
if (needUpdateLighting && typeof this.#babylonJSController.setIlluminationConfig === "function") {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
667
|
+
const lightingConfig = {};
|
|
668
|
+
if (config.options?.render?.lightingTimeOfDay !== undefined) {
|
|
669
|
+
lightingConfig.lightingTimeOfDay = config.options.render.lightingTimeOfDay;
|
|
670
|
+
}
|
|
671
|
+
if (config.options?.render?.showLightingTimeOfDay !== undefined) {
|
|
672
|
+
lightingConfig.showLightingTimeOfDay = config.options.render.showLightingTimeOfDay;
|
|
673
|
+
}
|
|
674
|
+
this.#babylonJSController.setIlluminationConfig(lightingConfig);
|
|
672
675
|
}
|
|
673
676
|
|
|
674
677
|
// Apply initial render settings if provided in options
|
|
@@ -676,6 +679,8 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
676
679
|
await this.applyRenderSettings(config.options.render);
|
|
677
680
|
}
|
|
678
681
|
|
|
682
|
+
const loadDetail = await this.#babylonJSController.load(forceReload);
|
|
683
|
+
|
|
679
684
|
return { ...loadDetail, load: this.#onLoaded() };
|
|
680
685
|
}
|
|
681
686
|
|
|
@@ -706,77 +711,26 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
706
711
|
}
|
|
707
712
|
const needUpdateLighting = this.#checkNeedToUpdateLighting(options);
|
|
708
713
|
if (needUpdateLighting && typeof this.#babylonJSController.setIlluminationConfig === "function") {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
714
|
+
const lightingConfig = {};
|
|
715
|
+
if (options.render?.lightingTimeOfDay !== undefined) {
|
|
716
|
+
lightingConfig.lightingTimeOfDay = options.render.lightingTimeOfDay;
|
|
717
|
+
}
|
|
718
|
+
if (options.render?.showLightingTimeOfDay !== undefined) {
|
|
719
|
+
lightingConfig.showLightingTimeOfDay = options.render.showLightingTimeOfDay;
|
|
720
|
+
}
|
|
721
|
+
this.#babylonJSController.setIlluminationConfig(lightingConfig);
|
|
712
722
|
someSetted = true;
|
|
713
723
|
}
|
|
714
|
-
|
|
724
|
+
|
|
715
725
|
// Apply render settings if provided
|
|
716
726
|
if (options.render) {
|
|
717
727
|
await this.applyRenderSettings(options.render);
|
|
718
728
|
}
|
|
719
|
-
|
|
729
|
+
|
|
720
730
|
const detail = this.#onSetOptions();
|
|
721
731
|
return { success: someSetted, detail: detail };
|
|
722
732
|
}
|
|
723
733
|
|
|
724
|
-
/**
|
|
725
|
-
* Loads the model container independently in the background while the render loop keeps running.
|
|
726
|
-
* Merges the model into the scene atomically when ready, without stopping camera-dependent effects.
|
|
727
|
-
* @public
|
|
728
|
-
* @param {object} payload - Model payload with storage pointer and optional timestamp.
|
|
729
|
-
* @returns {Promise<{success: boolean, error: any, loadedContainers: string[]}>}
|
|
730
|
-
*/
|
|
731
|
-
async loadModelIndependent(payload) {
|
|
732
|
-
if (!this.#babylonJSController) {
|
|
733
|
-
return { success: false, error: "Controller not initialized", loadedContainers: [] };
|
|
734
|
-
}
|
|
735
|
-
// #checkNeedToUpdateContainers passed as callback — called AFTER any in-progress #loadContainers
|
|
736
|
-
// finishes so the scene load doesn't also pick up and consume the model container.
|
|
737
|
-
return await this.#babylonJSController.loadContainerIndependent("model", () => {
|
|
738
|
-
this.#checkNeedToUpdateContainers({ model: payload });
|
|
739
|
-
}, { loadSessionId: payload?.loadSessionId ?? null });
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* Loads the environment/scene container independently in the background, in parallel with any
|
|
744
|
-
* ongoing model load. Applies IBL (HDR) if the scene payload includes an ibl property.
|
|
745
|
-
* Dispatches "scene-loading" and "scene-loaded" (or "scene-error") events directly.
|
|
746
|
-
* @public
|
|
747
|
-
* @param {object|string} scene - Scene payload: { storage, ibl?, visible? }
|
|
748
|
-
* @returns {Promise<{success: boolean, error: any, loadedContainers: string[]}>}
|
|
749
|
-
*/
|
|
750
|
-
async loadSceneIndependent(scene) {
|
|
751
|
-
if (!this.#babylonJSController) {
|
|
752
|
-
return { success: false, error: "Controller not initialized", loadedContainers: [] };
|
|
753
|
-
}
|
|
754
|
-
// Pre-mark IBL as pending before entering loadContainerIndependent — the onReady callback
|
|
755
|
-
// is synchronous so async IBL resolution must happen here, not inside it.
|
|
756
|
-
if (scene?.ibl !== undefined) {
|
|
757
|
-
await this.#checkNeedToUpdateIBL({ ibl: scene.ibl });
|
|
758
|
-
}
|
|
759
|
-
return await this.#babylonJSController.loadContainerIndependent("environment", () => {
|
|
760
|
-
this.#checkNeedToUpdateContainers({ scene });
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* Loads the materials container independently in the background, in parallel with any
|
|
766
|
-
* ongoing model or environment load.
|
|
767
|
-
* @public
|
|
768
|
-
* @param {object|string} materials - Materials payload: { storage }
|
|
769
|
-
* @returns {Promise<{success: boolean, error: any, loadedContainers: string[]}>}
|
|
770
|
-
*/
|
|
771
|
-
async loadMaterialsIndependent(materials) {
|
|
772
|
-
if (!this.#babylonJSController) {
|
|
773
|
-
return { success: false, error: "Controller not initialized", loadedContainers: [] };
|
|
774
|
-
}
|
|
775
|
-
return await this.#babylonJSController.loadContainerIndependent("materials", () => {
|
|
776
|
-
this.#checkNeedToUpdateContainers({ materials });
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
|
|
780
734
|
/**
|
|
781
735
|
* Shows the 3D model within the viewer.
|
|
782
736
|
* @public
|
|
@@ -825,31 +779,6 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
825
779
|
this.#babylonJSController.setContainerVisibility("environment", false);
|
|
826
780
|
}
|
|
827
781
|
|
|
828
|
-
/**
|
|
829
|
-
* Reflects the current visibility of a container without re-triggering the command queue.
|
|
830
|
-
* Used by BabylonJSController when the visibility change originated from the renderer itself.
|
|
831
|
-
* @public
|
|
832
|
-
* @param {string} name - Container name ("model" or "environment").
|
|
833
|
-
* @param {boolean} isVisible - Whether the container should be reflected as visible.
|
|
834
|
-
* @returns {void}
|
|
835
|
-
*/
|
|
836
|
-
syncVisibility(name, isVisible) {
|
|
837
|
-
const attrName = name === "model" ? "show-model" : name === "environment" ? "show-scene" : null;
|
|
838
|
-
if (!attrName) {
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
const nextValue = isVisible ? "true" : "false";
|
|
842
|
-
if (this.getAttribute(attrName) === nextValue) {
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
this.#suppressVisibilityAttributeCallback = true;
|
|
846
|
-
try {
|
|
847
|
-
this.setAttribute(attrName, nextValue);
|
|
848
|
-
} finally {
|
|
849
|
-
this.#suppressVisibilityAttributeCallback = false;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
782
|
/**
|
|
854
783
|
* Downloads the current 3D model as a GLB file.
|
|
855
784
|
* @public
|
|
@@ -960,8 +889,9 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
960
889
|
|
|
961
890
|
/**
|
|
962
891
|
* Returns the current render settings used by the BabylonJSController.
|
|
892
|
+
* The snapshot is suitable for hydrating PrefViewerMenu3D controls.
|
|
963
893
|
* @public
|
|
964
|
-
* @returns {object|null} A shallow copy of the render settings or null if unavailable.
|
|
894
|
+
* @returns {object|null} A shallow copy of the render settings, including quality toggles, highlight state, and lighting time, or null if unavailable.
|
|
965
895
|
*/
|
|
966
896
|
getRenderSettings() {
|
|
967
897
|
if (!this.#babylonJSController) {
|
|
@@ -1030,40 +960,95 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
1030
960
|
}
|
|
1031
961
|
|
|
1032
962
|
/**
|
|
1033
|
-
*
|
|
963
|
+
* Reports whether the animation control menu is currently enabled.
|
|
964
|
+
* @public
|
|
965
|
+
* @returns {boolean} True when the animation menu option is active; otherwise false.
|
|
966
|
+
*/
|
|
967
|
+
isAnimationMenuEnabled() {
|
|
968
|
+
const renderSettings = this.getRenderSettings?.() ?? null;
|
|
969
|
+
if (renderSettings?.showAnimationMenu !== undefined) {
|
|
970
|
+
return renderSettings.showAnimationMenu === true;
|
|
971
|
+
}
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Reports whether the lighting time-of-day control should be visible and interactive.
|
|
977
|
+
* Requires both `showLightingTimeOfDay === true` and a finite `lightingTimeOfDay` value.
|
|
1034
978
|
* @public
|
|
1035
|
-
* @
|
|
1036
|
-
|
|
979
|
+
* @returns {boolean} True when the time-of-day slider is explicitly visible and has a valid value; otherwise false.
|
|
980
|
+
*/
|
|
981
|
+
isLightingTimeOfDayAvailable() {
|
|
982
|
+
const renderSettings = this.getRenderSettings?.() ?? null;
|
|
983
|
+
return renderSettings?.showLightingTimeOfDay === true && Number.isFinite(renderSettings.lightingTimeOfDay);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Returns a diagnostic snapshot of the underlying Babylon.js runtime.
|
|
988
|
+
* @public
|
|
989
|
+
* @returns {object|null} Debug information, or null when the controller is not initialized.
|
|
990
|
+
*/
|
|
991
|
+
getDebugInfo() {
|
|
992
|
+
return this.#babylonJSController?.getDebugInfo?.() ?? null;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Applies render settings received from PrefViewerMenu3D or another UI.
|
|
996
|
+
* Highlight and time-of-day lighting values are pushed directly to BabylonJSController; AA/SSAO/IBL/shadow changes
|
|
997
|
+
* are scheduled through the render-settings reload path when needed.
|
|
998
|
+
* @public
|
|
999
|
+
* @param {object} settings - Partial render settings payload.
|
|
1000
|
+
* @param {boolean} [settings.antiAliasingEnabled] - Enables or disables anti-aliasing.
|
|
1001
|
+
* @param {boolean} [settings.ambientOcclusionEnabled] - Enables or disables SSAO.
|
|
1002
|
+
* @param {boolean} [settings.iblEnabled] - Enables or disables image-based lighting.
|
|
1003
|
+
* @param {boolean} [settings.shadowsEnabled] - Enables or disables scene shadows.
|
|
1004
|
+
* @param {boolean|string} [settings.showAnimationMenu] - Enables/disables the animation controls when animations are available.
|
|
1005
|
+
* @param {boolean|string} [settings.highlightEnabled] - Enables or disables hover highlight; string "false" is normalized to false.
|
|
1006
|
+
* @param {string} [settings.highlightColor] - Hex color used for hover highlight.
|
|
1007
|
+
* @param {boolean|string} [settings.showLightingTimeOfDay] - Shows/hides the lighting time-of-day control.
|
|
1008
|
+
* @param {number} [settings.lightingTimeOfDay] - Normalized 0..1 time-of-day value.
|
|
1009
|
+
* @param {boolean} [settings.persist=true] - False for transient lighting updates such as playback frames.
|
|
1010
|
+
* @returns {Promise<{changed: boolean, success: boolean, detail?: object, controller?: object}>} Resolves with the apply/reload summary.
|
|
1037
1011
|
*/
|
|
1038
1012
|
async applyRenderSettings(settings) {
|
|
1039
1013
|
if (!this.#babylonJSController) {
|
|
1040
1014
|
return { changed: false, success: false };
|
|
1041
1015
|
}
|
|
1042
1016
|
|
|
1043
|
-
|
|
1044
|
-
if (settings.
|
|
1045
|
-
|
|
1046
|
-
// Convert string "false" to boolean false
|
|
1047
|
-
const highlightEnabled = settings.highlightEnabled === "false" ? false : settings.highlightEnabled;
|
|
1048
|
-
this.#babylonJSController.setIlluminationConfig({
|
|
1049
|
-
highlightEnabled: highlightEnabled,
|
|
1050
|
-
highlightColor: settings.highlightColor,
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1017
|
+
const illuminationSettings = {};
|
|
1018
|
+
if (settings.showAnimationMenu !== undefined) {
|
|
1019
|
+
illuminationSettings.showAnimationMenu = settings.showAnimationMenu;
|
|
1053
1020
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1021
|
+
if (settings.highlightEnabled !== undefined) {
|
|
1022
|
+
illuminationSettings.highlightEnabled = settings.highlightEnabled;
|
|
1023
|
+
}
|
|
1024
|
+
if (settings.highlightColor !== undefined) {
|
|
1025
|
+
illuminationSettings.highlightColor = settings.highlightColor;
|
|
1026
|
+
}
|
|
1027
|
+
if (settings.showLightingTimeOfDay !== undefined) {
|
|
1028
|
+
illuminationSettings.showLightingTimeOfDay = settings.showLightingTimeOfDay;
|
|
1029
|
+
}
|
|
1030
|
+
if (settings.lightingTimeOfDay !== undefined) {
|
|
1057
1031
|
const lightingTimeOfDay = Number(settings.lightingTimeOfDay);
|
|
1058
1032
|
if (Number.isFinite(lightingTimeOfDay)) {
|
|
1059
|
-
|
|
1060
|
-
lightingTimeOfDay,
|
|
1061
|
-
persist: settings.persist !== false,
|
|
1062
|
-
});
|
|
1033
|
+
illuminationSettings.lightingTimeOfDay = lightingTimeOfDay;
|
|
1063
1034
|
}
|
|
1064
1035
|
}
|
|
1036
|
+
if (settings.persist !== undefined) {
|
|
1037
|
+
illuminationSettings.persist = settings.persist;
|
|
1038
|
+
}
|
|
1039
|
+
if (Object.keys(illuminationSettings).length > 0 && typeof this.#babylonJSController.setIlluminationConfig === "function") {
|
|
1040
|
+
this.#babylonJSController.setIlluminationConfig(illuminationSettings);
|
|
1041
|
+
}
|
|
1065
1042
|
|
|
1066
|
-
const {
|
|
1043
|
+
const {
|
|
1044
|
+
showAnimationMenu: _sam,
|
|
1045
|
+
highlightEnabled: _he,
|
|
1046
|
+
highlightColor: _hc,
|
|
1047
|
+
showLightingTimeOfDay: _slt,
|
|
1048
|
+
lightingTimeOfDay: _lt,
|
|
1049
|
+
persist: _persist,
|
|
1050
|
+
...reloadSettings
|
|
1051
|
+
} = settings;
|
|
1067
1052
|
const scheduled = this.#babylonJSController.scheduleRenderSettingsReload(reloadSettings);
|
|
1068
1053
|
if (!scheduled.changed) {
|
|
1069
1054
|
return { changed: false, success: true };
|
|
@@ -1101,22 +1086,4 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
1101
1086
|
get isVisible() {
|
|
1102
1087
|
return this.#isVisible;
|
|
1103
1088
|
}
|
|
1104
|
-
|
|
1105
|
-
/**
|
|
1106
|
-
* Forces the active camera to frame the visible model.
|
|
1107
|
-
* @public
|
|
1108
|
-
* @returns {boolean}
|
|
1109
|
-
*/
|
|
1110
|
-
focusModel() {
|
|
1111
|
-
return this.#babylonJSController?.focusModel?.() ?? false;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
/**
|
|
1115
|
-
* Returns a debug snapshot of the internal 3D state, including camera data.
|
|
1116
|
-
* @public
|
|
1117
|
-
* @returns {object|null}
|
|
1118
|
-
*/
|
|
1119
|
-
getDebugState() {
|
|
1120
|
-
return this.#babylonJSController?.getDebugState?.() ?? null;
|
|
1121
|
-
}
|
|
1122
1089
|
}
|