@preference-sl/pref-viewer 2.13.0-beta.0 → 2.13.0-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.13.0-beta.0",
3
+ "version": "2.13.0-beta.1",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -35,11 +35,11 @@
35
35
  "index.d.ts"
36
36
  ],
37
37
  "dependencies": {
38
- "@babylonjs/core": "^8.46.2",
39
- "@babylonjs/loaders": "^8.46.2",
40
- "@babylonjs/serializers": "^8.46.2",
38
+ "@babylonjs/core": "^8.47.0",
39
+ "@babylonjs/loaders": "^8.47.0",
40
+ "@babylonjs/serializers": "^8.47.0",
41
41
  "@panzoom/panzoom": "^4.6.0",
42
- "babylonjs-gltf2interface": "^8.46.2",
42
+ "babylonjs-gltf2interface": "^8.47.0",
43
43
  "buffer": "^6.0.3",
44
44
  "idb": "^8.0.3",
45
45
  "is-svg": "^6.1.0",
@@ -1,5 +1,5 @@
1
1
  import { AssetContainer, Color3, HighlightLayer, InstancedMesh, Mesh, PickingInfo, Quaternion, Scene, UtilityLayerRenderer, Vector3 } from "@babylonjs/core";
2
- import { PrefViewerColors } from "./styles.js";
2
+ import { PrefViewerStyleVariables } from "./styles.js";
3
3
  import OpeningAnimation from "./babylonjs-animation-opening.js";
4
4
 
5
5
  /**
@@ -32,7 +32,7 @@ export default class BabylonJSAnimationController {
32
32
  #animatedNodes = [];
33
33
  #openingAnimations = [];
34
34
 
35
- #highlightColor = Color3.FromHexString(PrefViewerColors.primary);
35
+ #highlightColor = Color3.FromHexString(PrefViewerStyleVariables.colorPrimary);
36
36
  #highlightLayer = null;
37
37
  #overlayLayer = null;
38
38
  #useHighlightLayer = false; // Set to true to use HighlightLayer (better performance) and false to use overlay meshes (UtilityLayerRenderer - always on top)
@@ -8,101 +8,71 @@ import JSZip from "jszip";
8
8
  import GLTFResolver from "./gltf-resolver.js";
9
9
  import { MaterialData } from "./pref-viewer-3d-data.js";
10
10
  import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
11
+ import { translate } from "./localization/i18n.js";
11
12
 
12
13
  /**
13
- * BabylonJSController - Main controller for managing Babylon.js 3D scenes, assets, rendering, and user interactions in PrefViewer.
14
+ * BabylonJSController is the PrefViewer 3D engine coordinator: it bootstraps Babylon.js, manages asset containers,
15
+ * rebuilds post-process pipelines on demand, brokers XR/download interactions, and persists user render toggles so UI
16
+ * components can stay declarative. Higher layers hand over container + option state, while this class turns it into a
17
+ * fully interactive scene with deterministic reloads and exports.
14
18
  *
15
- * Summary:
16
- * Provides a high-level API for initializing, loading, displaying, and exporting 3D models and environments using Babylon.js.
17
- * Manages advanced rendering features, AR integration, asset containers, and user interaction logic.
19
+ * Overview
20
+ * - Spins up the Babylon.js engine/scene/camera stack, configures Draco loaders, and wires resize + render loops.
21
+ * - Resolves GLTF/GLB sources through GLTFResolver, loads them into `AssetContainer`s, and toggles their visibility.
22
+ * - Applies render-setting switches (AA, SSAO, IBL, shadows), persists them in localStorage, and rehydrates on startup.
23
+ * - Rebuilds DefaultRenderingPipeline, SSAO, IBL, and shadow pipelines every time the active camera or assets change.
24
+ * - Connects keyboard, pointer, wheel, hover, and XR events so menus, animation controllers, and PrefViewer attributes stay in sync.
25
+ * - Generates GLB, glTF+ZIP, or USDZ exports with timestamped names and localized dialog copy.
26
+ * - Translates metadata (inner floor offsets, cast/receive shadows, camera locks) into scene adjustments after reloads.
18
27
  *
19
- * Key Responsibilities:
20
- * - Initializes and manages the Babylon.js engine, scene, camera, lights, and asset containers.
21
- * - Handles loading, replacing, and disposing of 3D models, environments, and materials.
22
- * - Configures advanced rendering features such as Draco mesh compression, shadows, and image-based lighting (IBL).
23
- * - Integrates Babylon.js WebXR experience for augmented reality (AR) support.
24
- * - Provides methods for downloading models and scenes in GLB, glTF (ZIP), and USDZ formats.
25
- * - Manages camera and material options, container visibility, and user interactions.
26
- * - Observes canvas resize events and updates the engine accordingly.
27
- * - Applies metadata-driven scene adjustments (e.g., inner floor translation) after asset reloads.
28
- * - Designed for integration with PrefViewer and GLTFResolver.
29
- * - All resource management and rendering operations are performed asynchronously for performance.
28
+ * Runtime Flow
29
+ * 1. Instantiate with `new BabylonJSController(canvas, containers, options)`.
30
+ * 2. Call `enable()` to configure Draco, create the engine/scene, observers, and XR hooks.
31
+ * 3. Whenever PrefViewer marks containers/options pending, invoke `load()` to fetch sources and rebuild pipelines.
32
+ * 4. Use `applyRenderSettings` helpers (via menu events) to merge toggles, persist them, and trigger reloads when needed.
33
+ * 5. Respond to PrefViewer tasks by calling `showModel()/hideModel()` equivalents through container state changes.
34
+ * 6. Surface downloads through `downloadGLB/GLTF/USDZ` or the private `#openDownloadDialog()` triggered by keyboard shortcuts.
35
+ * 7. Invoke `disable()` when the element disconnects to teardown scenes, XR sessions, and listeners.
30
36
  *
31
- * Usage:
32
- * - Instantiate: const controller = new BabylonJSController(canvas, containers, options);
33
- * - Enable rendering: await controller.enable();
34
- * - Load assets: await controller.load();
35
- * - Set camera/material options: controller.setCameraOptions(), controller.setMaterialOptions();
36
- * - Control visibility: controller.setContainerVisibility(name, show);
37
- * - Download assets: controller.downloadGLB(), controller.downloadGLTF(), controller.downloadUSDZ();
38
- * - Disable rendering: controller.disable();
37
+ * Public API Highlights
38
+ * - constructor(canvas, containers, options)
39
+ * - enable() / disable()
40
+ * - load()
41
+ * - downloadGLB(content) / downloadGLTF(content) / downloadUSDZ(content)
42
+ * - getRenderSettings() / applyRenderSettings(partial)
43
+ * - setRenderSettings(settings) [via PrefViewer menu integration]
39
44
  *
40
- * Public Methods:
41
- * - constructor(canvas, containers, options): Creates the controller, wires container state, and stores runtime options.
42
- * - enable(): Boots the Babylon.js engine, scene, camera, baseline lights, XR support, and the render loop.
43
- * - disable(): Stops rendering and disposes the engine, lights, XR experience, and observers.
44
- * - load(): Reloads every pending asset container, re-applies options, and resolves with the loading summary { success, error }.
45
- * - setCameraOptions(): Applies the pending camera selection, reinstalls dependent pipelines, and restarts rendering safely.
46
- * - setMaterialOptions(): Re-applies all configured material overrides across visible containers and restarts rendering.
47
- * - setIBLOptions(): Pushes pending HDR/IBL updates, refreshes dependent effects, resumes the render loop, and reports whether anything changed.
48
- * - setContainerVisibility(name, show): Toggles model/environment containers, syncing wall/floor helpers and component attributes.
49
- * - downloadGLB(content): Exports the selected scope (scene/model/environment) into a time-stamped GLB and triggers the download.
50
- * - downloadGLTF(content): Generates a glTF + BIN + textures ZIP for the requested scope, adding metadata comments for traceability.
51
- * - downloadUSDZ(content): Builds an Apple USDZ archive for the requested scope and downloads it via blob streaming.
45
+ * Key Subsystems
46
+ * - Persistence: #applyRenderSettings, #loadStoredRenderSettings, #saveRenderSettings keep AA/SSAO/IBL/shadow flags synced.
47
+ * - Loading pipeline: #markContainersForReload, #markOptionsForReload, #loadAssetContainer, #loadContainers orchestrate deterministic reloads.
48
+ * - Visual setup: #configureDracoCompression, #createCamera, #createLights, #initializeVisualImprovements, #initializeAmbientOcclussion,
49
+ * #initializeIBLShadows, #initializeDefaultLightShadows, #initializeEnvironmentShadows, #setMaxSimultaneousLights.
50
+ * - Interaction + XR: #bindHandlers, #enableInteraction, #onPointerObservable, #onMouseWheel, #onKeyUp, #createXRExperience,
51
+ * #addStylesToARButton, #disposeXRExperience.
52
+ * - Container helpers: #addContainer, #removeContainer, #replaceContainer, #setOptions_Materials, #setOptions_Camera,
53
+ * #setOptions_IBL, #setVisibilityOfWallAndFloorInModel, #getPrefViewerComponent.
54
+ * - Metadata + download utilities: #checkModelMetadata, #checkInnerFloorTranslation, #translateNodeY, #addDateToName,
55
+ * #downloadZip, #openDownloadDialog.
52
56
  *
53
- * Private Methods (using ECMAScript private fields):
54
- * - #bindHandlers(): Pre-binds reusable event handlers to preserve stable references.
55
- * - #configureDracoCompression(): Sets up Draco mesh compression.
56
- * - #renderLoop(): Babylon.js render loop callback.
57
- * - #addStylesToARButton(): Styles AR button.
58
- * - #createXRExperience(): Initializes WebXR AR experience.
59
- * - #createCamera(): Creates and configures the main camera.
60
- * - #createLights(): Creates and configures scene lights and shadows.
61
- * - #initializeAmbientOcclussion(): Rebuilds the SSAO pipeline for the active camera.
62
- * - #initializeVisualImprovements(): Reinstalls the default rendering pipeline (MSAA/FXAA/grain).
63
- * - #initializeEnvironmentTexture(): Loads and sets the HDR environment texture.
64
- * - #initializeIBLShadows(): Sets up IBL shadow pipeline and assigns meshes/materials.
65
- * - #addMeshToShadowGenerator(shadowGenerator, mesh): Adds eligible meshes to the provided shadow generator while honoring GLTF metadata.
66
- * - #initializeShadows(): Sets up standard or IBL shadows for meshes.
67
- * - #initializeDefaultLightShadows(): Configures soft shadows when no HDR environment exists.
68
- * - #initializeEnvironmentShadows(): Rebuilds environment-provided shadow generators.
69
- * - #ensureMeshesReceiveShadows(): Ensures every non-root mesh receives shadows unless metadata opts out.
70
- * - #setMaxSimultaneousLights(): Updates max simultaneous lights for all materials.
71
- * - #onPointerObservable(info): Handles pointer events and dispatches to pointer/mouse handlers.
72
- * - #onPointerUp(event, pickInfo): Handles pointer up events (e.g., right-click for animation menu).
73
- * - #onPointerMove(event, pickInfo): Handles pointer move events (e.g., mesh highlighting).
74
- * - #onMouseWheel(event, pickInfo): Handles mouse wheel events for camera zoom.
75
- * - #onKeyUp(event): Handles keyup events for download dialog and shortcuts.
76
- * - #enableInteraction(): Adds canvas and scene interaction event listeners.
77
- * - #disableInteraction(): Removes canvas and scene interaction event listeners.
78
- * - #disposeAnimationController(): Disposes the animation controller if it exists.
79
- * - #disposeXRExperience(): Disposes the Babylon.js WebXR experience if it exists.
80
- * - #disposeEngine(): Disposes engine and releases all resources.
81
- * - #setOptionsMaterial(optionMaterial): Applies a material option to relevant meshes.
82
- * - #setOptions_Materials(): Applies all material options from configuration.
83
- * - #setOptions_Camera(): Applies camera options from configuration.
84
- * - #setOptions_IBL(): Applies pending HDR/IBL option updates and refreshes lights.
85
- * - #findContainerByName(name): Finds a container by its name.
86
- * - #addContainer(container, updateVisibility): Adds a container to the scene and updates visibility.
87
- * - #removeContainer(container, updateVisibility): Removes a container from the scene and updates visibility.
88
- * - #replaceContainer(container, newAssetContainer): Replaces a container in the scene.
89
- * - #getPrefViewer3DComponent(): Caches and retrieves the parent PREF-VIEWER-3D element.
90
- * - #getPrefViewerComponent(): Caches and retrieves the parent PREF-VIEWER element.
91
- * - #updateVisibilityAttributeInComponents(name, isVisible): Updates parent visibility attributes.
92
- * - #setVisibilityOfWallAndFloorInModel(show): Controls wall/floor mesh visibility.
93
- * - #stopRender(): Stops the Babylon.js render loop.
94
- * - #startRender(): Starts the Babylon.js render loop.
95
- * - #loadAssetContainer(container): Loads an asset container asynchronously.
96
- * - #loadContainers(): Loads all asset containers and adds them to the scene.
97
- * - #loadCameraDepentEffects(): Re-initializes camera-bound post-processes after reloads or option changes.
98
- * - #checkModelMetadata(oldMetadata, newMetadata): Processes metadata changes after loading.
99
- * - #checkInnerFloorTranslation(oldMetadata, newMetadata): Applies inner floor Y translations from metadata.
100
- * - #translateNodeY(name, deltaY): Adjusts the Y position of a scene node.
101
- * - #addDateToName(name): Appends the current date/time to a name string.
102
- * - #downloadZip(files, name, comment, addDateInName): Generates and downloads a ZIP file.
103
- * - #openDownloadDialog(): Opens the modal download dialog.
57
+ * Notes
58
+ * - Designed to be long-lived per PrefViewer instance; it caches parent components to reflect `show-model/show-scene` attributes.
59
+ * - All browser-only features guard against SSR/Node usage by checking `window` before touching localStorage or XR APIs.
60
+ * - Relies on PrefViewerMenu3D events to trigger render-setting updates, ensuring UI and persisted state never drift apart.
104
61
  */
105
62
  export default class BabylonJSController {
63
+
64
+ #RENDER_SETTINGS_STORAGE_KEY = "pref-viewer/render-settings";
65
+
66
+ // Default render settings
67
+ // Add here new render toggles that require persistence here, and remember to update the matching labels
68
+ // under translations.prefViewer.menu3d.switches in ./localization/translations.js.
69
+ static DEFAULT_RENDER_SETTINGS = {
70
+ antiAliasingEnabled: true,
71
+ ambientOcclusionEnabled: true,
72
+ iblEnabled: true,
73
+ shadowsEnabled: false,
74
+ };
75
+
106
76
  // Canvas HTML element
107
77
  #canvas = null;
108
78
 
@@ -133,12 +103,7 @@ export default class BabylonJSController {
133
103
  renderLoop: null,
134
104
  };
135
105
 
136
- #settings = {
137
- antiAliasingEnabled: true,
138
- ambientOcclusionEnabled: true,
139
- iblEnabled: true,
140
- shadowsEnabled: false,
141
- };
106
+ #settings = { ...BabylonJSController.DEFAULT_RENDER_SETTINGS };
142
107
 
143
108
  /**
144
109
  * Constructs a new BabylonJSController instance.
@@ -162,6 +127,7 @@ export default class BabylonJSController {
162
127
  };
163
128
  });
164
129
  this.#options = options;
130
+ this.#loadStoredRenderSettings();
165
131
  this.#bindHandlers();
166
132
  }
167
133
 
@@ -194,6 +160,114 @@ export default class BabylonJSController {
194
160
  };
195
161
  }
196
162
 
163
+ /**
164
+ * Merges boolean render toggles into the current configuration, clearing the environment texture when IBL turns off
165
+ * and persisting the updated state when at least one flag changed.
166
+ * @private
167
+ * @param {object} [settings={}] - Partial map of render settings (AA, SSAO, IBL, shadows, etc.).
168
+ * @returns {boolean} True when any setting changed and was saved.
169
+ */
170
+ #applyRenderSettings(settings = {}) {
171
+ if (!settings) {
172
+ return false;
173
+ }
174
+
175
+ let changed = false;
176
+ Object.keys(settings).forEach((key) => {
177
+ if (typeof settings[key] === "boolean" && this.#settings[key] !== settings[key]) {
178
+ this.#settings[key] = settings[key];
179
+ changed = true;
180
+ }
181
+ });
182
+
183
+ if (changed && settings.iblEnabled === false && this.#scene) {
184
+ this.#scene.environmentTexture = null;
185
+ }
186
+
187
+ if (changed) {
188
+ this.#saveRenderSettings();
189
+ }
190
+
191
+ return changed;
192
+ }
193
+
194
+ /**
195
+ * Restores previously persisted render toggles from localStorage, falling back to defaults when parsing fails or
196
+ * the browser environment is unavailable.
197
+ * @private
198
+ * @returns {void}
199
+ */
200
+ #loadStoredRenderSettings() {
201
+ if (typeof window === "undefined" || !window?.localStorage) {
202
+ return;
203
+ }
204
+ try {
205
+ const serialized = window.localStorage.getItem(this.#RENDER_SETTINGS_STORAGE_KEY);
206
+ if (!serialized) {
207
+ return;
208
+ }
209
+ const parsed = JSON.parse(serialized);
210
+ Object.keys(BabylonJSController.DEFAULT_RENDER_SETTINGS).forEach((key) => {
211
+ if (typeof parsed?.[key] === "boolean") {
212
+ this.#settings[key] = parsed[key];
213
+ }
214
+ });
215
+ } catch (error) {
216
+ console.warn("PrefViewer: unable to load render settings", error);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Serializes the current render toggle object to localStorage, ignoring non-browser contexts and logging warnings
222
+ * if persistence fails.
223
+ * @private
224
+ * @returns {void}
225
+ */
226
+ #saveRenderSettings() {
227
+ if (typeof window === "undefined" || !window?.localStorage) {
228
+ return;
229
+ }
230
+ try {
231
+ window.localStorage.setItem(this.#RENDER_SETTINGS_STORAGE_KEY, JSON.stringify(this.#settings));
232
+ } catch (error) {
233
+ console.warn("PrefViewer: unable to save render settings", error);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Walks every container state and marks it as pending using `setPendingWithCurrentStorage` so asset sources are
239
+ * re-requested on the next load cycle.
240
+ * @private
241
+ * @returns {boolean} True when at least one container flipped to pending.
242
+ */
243
+ #markContainersForReload() {
244
+ let marked = false;
245
+ Object.values(this.#containers).forEach((container) => {
246
+ if (container?.state?.setPendingWithCurrentStorage && container.state.setPendingWithCurrentStorage()) {
247
+ marked = true;
248
+ }
249
+ });
250
+ return marked;
251
+ }
252
+
253
+ /**
254
+ * Re-schedules option-driven resources (camera, materials, IBL) for application by calling their respective
255
+ * `setPendingWithCurrent`/`setPending` helpers when available.
256
+ * @private
257
+ * @returns {void}
258
+ */
259
+ #markOptionsForReload() {
260
+ if (this.#options?.camera?.setPendingWithCurrent) {
261
+ this.#options.camera.setPendingWithCurrent();
262
+ }
263
+ if (this.#options?.materials) {
264
+ Object.values(this.#options.materials).forEach((material) => material?.setPendingWithCurrent?.());
265
+ }
266
+ if (this.#options?.ibl?.setPending) {
267
+ this.#options.ibl.setPending();
268
+ }
269
+ }
270
+
197
271
  /**
198
272
  * Render loop callback for Babylon.js.
199
273
  * @private
@@ -1512,16 +1586,18 @@ export default class BabylonJSController {
1512
1586
  return;
1513
1587
  }
1514
1588
 
1515
- const header = "Download 3D Scene";
1589
+ const locale = this.#prefViewer.culture;
1590
+ const texts = translate("prefViewer.downloadDialog", locale ? { locale } : undefined) || {};
1591
+ const header = texts.title || "Download 3D Scene";
1516
1592
  const content = `
1517
1593
  <form slot="content" id="download-dialog-form" style="display:flex;flex-direction:column;gap:16px;">
1518
- <h4>Content</h4>
1594
+ <h4>${texts.sections?.content || "Content"}</h4>
1519
1595
  <div style="display:flex;flex-direction:row;gap:16px;margin:0 8px 0 8px;">
1520
- <label><input type="radio" name="content" value="1"> Model</label>
1521
- <label><input type="radio" name="content" value="2"> Scene</label>
1522
- <label><input type="radio" name="content" value="0" checked> Both</label>
1596
+ <label><input type="radio" name="content" value="1"> ${texts.options?.model || "Model"}</label>
1597
+ <label><input type="radio" name="content" value="2"> ${texts.options?.scene || "Scene"}</label>
1598
+ <label><input type="radio" name="content" value="0" checked> ${texts.options?.both || "Both"}</label>
1523
1599
  </div>
1524
- <h4>Format</h4>
1600
+ <h4>${texts.sections?.format || "Format"}</h4>
1525
1601
  <div style="display:flex;flex-direction:row;gap:16px;margin:0 8px 0 8px;">
1526
1602
  <label><input type="radio" name="format" value="gltf" checked> glTF (ZIP)</label>
1527
1603
  <label><input type="radio" name="format" value="glb"> GLB</label>
@@ -1529,8 +1605,8 @@ export default class BabylonJSController {
1529
1605
  </div>
1530
1606
  </form>`;
1531
1607
  const footer = `
1532
- <button type="button" class="primary" id="download-dialog-download">Download</button>
1533
- <button type="button" id="download-dialog-cancel">Cancel</button>`;
1608
+ <button type="button" class="primary" id="download-dialog-download">${texts.buttons?.download || "Download"}</button>
1609
+ <button type="button" id="download-dialog-cancel">${texts.buttons?.cancel || "Cancel"}</button>`;
1534
1610
 
1535
1611
  const dialog = this.#prefViewer.openDialog(header, content, footer);
1536
1612
 
@@ -1617,81 +1693,6 @@ export default class BabylonJSController {
1617
1693
  this.#disposeEngine();
1618
1694
  }
1619
1695
 
1620
- /**
1621
- * Loads all asset containers (model, environment, materials, etc.) and adds them to the scene.
1622
- * Applies material and camera options, sets visibility, and initializes lights and shadows.
1623
- * @public
1624
- * @returns {Promise<boolean>} Resolves to true if loading succeeds, false otherwise.
1625
- */
1626
- async load() {
1627
- return await this.#loadContainers();
1628
- }
1629
-
1630
- /**
1631
- * Applies camera options from the configuration to the active camera.
1632
- * Stops and restarts the render loop to apply changes.
1633
- * @public
1634
- * @returns {boolean} True if camera options were set successfully, false otherwise.
1635
- */
1636
- setCameraOptions() {
1637
- this.#stopRender();
1638
- const cameraOptionsSetted = this.#setOptions_Camera();
1639
- this.#loadCameraDepentEffects();
1640
- this.#startRender();
1641
- return cameraOptionsSetted;
1642
- }
1643
-
1644
- /**
1645
- * Applies material options from the configuration to the relevant meshes.
1646
- * Stops and restarts the render loop to apply changes.
1647
- * @public
1648
- * @returns {boolean} True if material options were set successfully, false otherwise.
1649
- */
1650
- setMaterialOptions() {
1651
- this.#stopRender();
1652
- const materialsOptionsSetted = this.#setOptions_Materials();
1653
- this.#startRender();
1654
- return materialsOptionsSetted;
1655
- }
1656
-
1657
- /**
1658
- * Reapplies image-based lighting configuration (HDR URL, intensity, shadow mode).
1659
- * Stops rendering, pushes pending IBL state into the scene, rebuilds camera-dependent effects, then resumes rendering.
1660
- * @public
1661
- * @returns {void}
1662
- */
1663
- setIBLOptions() {
1664
- this.#stopRender();
1665
- const IBLOptionsSetted = this.#setOptions_IBL();
1666
- this.#loadCameraDepentEffects();
1667
- this.#startRender();
1668
- return IBLOptionsSetted;
1669
- }
1670
-
1671
- /**
1672
- * Sets the visibility of a container (model, environment, etc.) by name.
1673
- * Adds or removes the container from the scene and updates wall/floor visibility.
1674
- * Restarts the render loop to apply changes.
1675
- * @public
1676
- * @param {string} name - The name of the container to show or hide.
1677
- * @param {boolean} show - True to show the container, false to hide it.
1678
- * @returns {void}
1679
- */
1680
- setContainerVisibility(name, show) {
1681
- const container = this.#findContainerByName(name);
1682
- if (!container) {
1683
- return;
1684
- }
1685
- if (container.state.show === show && container.state.visible === show) {
1686
- return;
1687
- }
1688
- container.state.show = show;
1689
- this.#stopRender();
1690
- show ? this.#addContainer(container) : this.#removeContainer(container);
1691
- this.#setVisibilityOfWallAndFloorInModel();
1692
- this.#startRender();
1693
- }
1694
-
1695
1696
  /**
1696
1697
  * Downloads the current scene, model, or environment as a GLB file.
1697
1698
  * @public
@@ -1794,4 +1795,118 @@ export default class BabylonJSController {
1794
1795
  }
1795
1796
  });
1796
1797
  }
1798
+
1799
+ /**
1800
+ * Returns a shallow copy of the current render settings so callers can read the active state without mutating the controller internals.
1801
+ * @public
1802
+ * @returns {{antiAliasingEnabled:boolean, ambientOcclusionEnabled:boolean, iblEnabled:boolean, shadowsEnabled:boolean}}
1803
+ */
1804
+ getRenderSettings() {
1805
+ return { ...this.#settings };
1806
+ }
1807
+
1808
+ /**
1809
+ * Loads all asset containers (model, environment, materials, etc.) and adds them to the scene.
1810
+ * Applies material and camera options, sets visibility, and initializes lights and shadows.
1811
+ * @public
1812
+ * @returns {Promise<boolean>} Resolves to true if loading succeeds, false otherwise.
1813
+ */
1814
+ async load() {
1815
+ return await this.#loadContainers();
1816
+ }
1817
+
1818
+ /**
1819
+ * Merges incoming render flags with the current configuration, persists them, and marks
1820
+ * all dependent loaders/options as pending when something actually changed.
1821
+ * @public
1822
+ * @param {{antiAliasingEnabled?:boolean, ambientOcclusionEnabled?:boolean, iblEnabled?:boolean, shadowsEnabled?:boolean}} settings Partial set of render toggles to apply.
1823
+ * @returns {{changed:boolean, settings:{antiAliasingEnabled:boolean, ambientOcclusionEnabled:boolean, iblEnabled:boolean, shadowsEnabled:boolean}}}
1824
+ * @description
1825
+ * Callers can inspect the `changed` flag to decide whether to trigger a reload with
1826
+ * `reloadWithCurrentSettings()` or simply reuse the returned snapshot.
1827
+ */
1828
+ scheduleRenderSettingsReload(settings = {}) {
1829
+ const changed = this.#applyRenderSettings(settings);
1830
+ if (!changed) {
1831
+ return { changed: false, settings: this.getRenderSettings() };
1832
+ }
1833
+ this.#markContainersForReload();
1834
+ this.#markOptionsForReload();
1835
+ return { changed: true, settings: this.getRenderSettings() };
1836
+ }
1837
+
1838
+ /**
1839
+ * Applies camera options from the configuration to the active camera.
1840
+ * Stops and restarts the render loop to apply changes.
1841
+ * @public
1842
+ * @returns {boolean} True if camera options were set successfully, false otherwise.
1843
+ */
1844
+ setCameraOptions() {
1845
+ this.#stopRender();
1846
+ const cameraOptionsSetted = this.#setOptions_Camera();
1847
+ this.#loadCameraDepentEffects();
1848
+ this.#startRender();
1849
+ return cameraOptionsSetted;
1850
+ }
1851
+
1852
+ /**
1853
+ * Applies material options from the configuration to the relevant meshes.
1854
+ * Stops and restarts the render loop to apply changes.
1855
+ * @public
1856
+ * @returns {boolean} True if material options were set successfully, false otherwise.
1857
+ */
1858
+ setMaterialOptions() {
1859
+ this.#stopRender();
1860
+ const materialsOptionsSetted = this.#setOptions_Materials();
1861
+ this.#startRender();
1862
+ return materialsOptionsSetted;
1863
+ }
1864
+
1865
+ /**
1866
+ * Reapplies image-based lighting configuration (HDR URL, intensity, shadow mode).
1867
+ * Stops rendering, pushes pending IBL state into the scene, rebuilds camera-dependent effects, then resumes rendering.
1868
+ * @public
1869
+ * @returns {void}
1870
+ */
1871
+ setIBLOptions() {
1872
+ this.#stopRender();
1873
+ const IBLOptionsSetted = this.#setOptions_IBL();
1874
+ this.#loadCameraDepentEffects();
1875
+ this.#startRender();
1876
+ return IBLOptionsSetted;
1877
+ }
1878
+
1879
+ /**
1880
+ * Sets the visibility of a container (model, environment, etc.) by name.
1881
+ * Adds or removes the container from the scene and updates wall/floor visibility.
1882
+ * Restarts the render loop to apply changes.
1883
+ * @public
1884
+ * @param {string} name - The name of the container to show or hide.
1885
+ * @param {boolean} show - True to show the container, false to hide it.
1886
+ * @returns {void}
1887
+ */
1888
+ setContainerVisibility(name, show) {
1889
+ const container = this.#findContainerByName(name);
1890
+ if (!container) {
1891
+ return;
1892
+ }
1893
+ if (container.state.show === show && container.state.visible === show) {
1894
+ return;
1895
+ }
1896
+ container.state.show = show;
1897
+ this.#stopRender();
1898
+ show ? this.#addContainer(container) : this.#removeContainer(container);
1899
+ this.#setVisibilityOfWallAndFloorInModel();
1900
+ this.#startRender();
1901
+ }
1902
+
1903
+ /**
1904
+ * Reloads every asset container using the latest staged render settings.
1905
+ * Intended to be called after `scheduleRenderSettingsReload()` marks data as pending.
1906
+ * @public
1907
+ * @returns {Promise<{success:boolean,error:any}>} Resolves with the same status object returned by `load()`.
1908
+ */
1909
+ async reloadWithCurrentSettings() {
1910
+ return await this.#loadContainers();
1911
+ }
1797
1912
  }
package/src/index.js CHANGED
@@ -3,11 +3,13 @@ import PrefViewer from "./pref-viewer.js";
3
3
  import PrefViewer2D from "./pref-viewer-2d.js";
4
4
  import PrefViewer3D from "./pref-viewer-3d.js";
5
5
  import PrefViewerDialog from "./pref-viewer-dialog.js";
6
+ import PrefViewerMenu3D from "./pref-viewer-menu-3d.js";
6
7
 
7
8
  // Defines custom elements for use as HTML tags in the application.
8
9
  customElements.define("pref-viewer-2d", PrefViewer2D);
9
10
  customElements.define("pref-viewer-3d", PrefViewer3D);
10
11
  customElements.define("pref-viewer-dialog", PrefViewerDialog);
12
+ customElements.define("pref-viewer-menu-3d", PrefViewerMenu3D);
11
13
  customElements.define("pref-viewer", PrefViewer);
12
14
 
13
15
  // Exposes selected PrefViewer classes globally for external JavaScript use.