@preference-sl/pref-viewer 2.11.0-beta.4 → 2.11.0-beta.6

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.11.0-beta.4",
3
+ "version": "2.11.0-beta.6",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -12,6 +12,9 @@ import { OpeningAnimation } from "./babylonjs-animation-opening.js";
12
12
  * - Displays and disposes the animation control menu (GUI) for animated nodes.
13
13
  * - Provides API for interaction and animation control.
14
14
  *
15
+ * Public Methods:
16
+ * - dispose(): Disposes all resources managed by the animation controller.
17
+ *
15
18
  * @class
16
19
  */
17
20
  export default class BabylonJSAnimationController {
@@ -50,6 +53,7 @@ export default class BabylonJSAnimationController {
50
53
  */
51
54
  #getAnimatedNodes() {
52
55
  this.#scene.animationGroups.forEach((animationGroup) => {
56
+ animationGroup.stop();
53
57
  if (!animationGroup._targetedAnimations.length) {
54
58
  return;
55
59
  }
@@ -150,21 +154,34 @@ export default class BabylonJSAnimationController {
150
154
  * @private
151
155
  */
152
156
  #setupPointerObservers() {
153
- this.#scene.onPointerObservable.add((pointerInfo) => {
154
- if (pointerInfo.type === PointerEventTypes.POINTERMOVE) {
155
- const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
156
- this.#hightlightMeshesForAnimation(pickingInfo);
157
- }
158
- if (pointerInfo.type === PointerEventTypes.POINTERUP) {
159
- // Remove any previously created Babylon GUI
160
- if (this.#advancedDynamicTexture) {
161
- this.#advancedDynamicTexture.dispose();
162
- this.#advancedDynamicTexture = null;
163
- }
164
- const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
165
- this.#showMenu(pickingInfo);
157
+ if (this.#openingAnimations.length === 0) {
158
+ return;
159
+ }
160
+ this.#scene.onPointerObservable.add(this.#onAnimationPointerObservable.bind(this));
161
+ }
162
+
163
+ /**
164
+ * Handles pointer events in the Babylon.js scene for animation interaction.
165
+ * On pointer move, highlights meshes belonging to animated nodes under the cursor.
166
+ * On pointer up (click), disposes any existing GUI and displays the animation control menu for the selected node.
167
+ *
168
+ * @private
169
+ * @param {PointerInfo} pointerInfo - The pointer event information from Babylon.js.
170
+ */
171
+ #onAnimationPointerObservable(pointerInfo) {
172
+ if (pointerInfo.type === PointerEventTypes.POINTERMOVE) {
173
+ const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
174
+ this.#hightlightMeshesForAnimation(pickingInfo);
175
+ }
176
+ if (pointerInfo.type === PointerEventTypes.POINTERUP) {
177
+ // Remove any previously created Babylon GUI
178
+ if (this.#advancedDynamicTexture) {
179
+ this.#advancedDynamicTexture.dispose();
180
+ this.#advancedDynamicTexture = null;
166
181
  }
167
- });
182
+ const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
183
+ this.#showMenu(pickingInfo);
184
+ }
168
185
  }
169
186
 
170
187
  /**
@@ -192,4 +209,27 @@ export default class BabylonJSAnimationController {
192
209
  }
193
210
  openingAnimation.showControls(this.#advancedDynamicTexture);
194
211
  }
212
+
213
+ /**
214
+ * Disposes all resources managed by the animation controller.
215
+ * Cleans up the highlight layer, GUI texture, and internal animation/node lists.
216
+ * Should be called when the controller is no longer needed to prevent memory leaks.
217
+ * @public
218
+ */
219
+ dispose() {
220
+ if (this.#highlightLayer) {
221
+ this.#highlightLayer.dispose();
222
+ this.#highlightLayer = null;
223
+ }
224
+ if (this.#advancedDynamicTexture) {
225
+ this.#advancedDynamicTexture.dispose();
226
+ this.#advancedDynamicTexture = null;
227
+ }
228
+ this.#animatedNodes = [];
229
+ this.#openingAnimations = [];
230
+ const observer = this.#scene.onPointerObservable._observers.find((observer) => observer.callback.name.includes("#onAnimationPointerObservable"));
231
+ if (observer) {
232
+ this.#scene.onPointerObservable.remove(observer);
233
+ }
234
+ }
195
235
  }
@@ -294,29 +294,42 @@ export default class BabylonJSController {
294
294
  return false;
295
295
  }
296
296
 
297
- let createIBLShadowPipeline = function (scene) {
297
+ /**
298
+ * Creates and configures the IBL shadow render pipeline for the Babylon.js scene.
299
+ * Sets recommended options for resolution, sampling, opacity, and disables debug passes.
300
+ * Accepts an optional camera array for pipeline targeting.
301
+ * @private
302
+ * @param {Scene} scene - The Babylon.js scene instance.
303
+ * @param {Camera[]} [cameras] - Optional array of cameras to target with the pipeline.
304
+ * @returns {IblShadowsRenderPipeline} The configured IBL shadow pipeline.
305
+ */
306
+ let createIBLShadowPipeline = function (scene, cameras = [scene.activeCamera]) {
298
307
  const pipeline = new IblShadowsRenderPipeline(
299
308
  "iblShadowsPipeline",
300
309
  scene,
301
310
  {
302
- resolutionExp: 7,
303
- sampleDirections: 2,
311
+ resolutionExp: 8, // Higher resolution for better shadow quality
312
+ sampleDirections: 4, // More sample directions for smoother shadows
304
313
  ssShadowsEnabled: true,
305
- shadowRemanence: 0.8,
314
+ shadowRemanence: 0.85,
306
315
  triPlanarVoxelization: true,
307
- shadowOpacity: 0.8,
316
+ shadowOpacity: 0.85,
308
317
  },
309
- [scene.activeCamera]
318
+ cameras
310
319
  );
311
- pipeline.allowDebugPasses = false;
312
- pipeline.gbufferDebugEnabled = true;
313
- pipeline.importanceSamplingDebugEnabled = false;
314
- pipeline.voxelDebugEnabled = false;
315
- pipeline.voxelDebugDisplayMip = 1;
316
- pipeline.voxelDebugAxis = 2;
317
- pipeline.voxelTracingDebugEnabled = false;
318
- pipeline.spatialBlurPassDebugEnabled = false;
319
- pipeline.accumulationPassDebugEnabled = false;
320
+ // Disable all debug passes for performance
321
+ const pipelineProps = {
322
+ allowDebugPasses: false,
323
+ gbufferDebugEnabled: false,
324
+ importanceSamplingDebugEnabled: false,
325
+ voxelDebugEnabled: false,
326
+ voxelDebugDisplayMip: 0,
327
+ voxelDebugAxis: 0,
328
+ voxelTracingDebugEnabled: false,
329
+ spatialBlurPassDebugEnabled: false,
330
+ accumulationPassDebugEnabled: false,
331
+ };
332
+ Object.assign(pipeline, pipelineProps);
320
333
  return pipeline;
321
334
  };
322
335
 
@@ -583,7 +596,7 @@ export default class BabylonJSController {
583
596
  * @param {boolean} isVisible - True to show the container, false to hide it.
584
597
  * @returns {void}
585
598
  */
586
- #updateVisibilityAttributeInComponentes(name, isVisible) {
599
+ #updateVisibilityAttributeInComponents(name, isVisible) {
587
600
  // Cache references to parent custom elements
588
601
  this.#getPrefViewer3DComponent();
589
602
  this.#getPrefViewerComponent();
@@ -600,13 +613,16 @@ export default class BabylonJSController {
600
613
  * Adds the asset container to the Babylon.js scene if it should be shown and is not already visible.
601
614
  * @private
602
615
  * @param {ContainerData} container - The container object containing asset state and metadata.
616
+ * @param {boolean} [updateVisibility=true] - If true, updates the visibility attribute in parent components.
603
617
  * @returns {boolean} True if the container was added, false otherwise.
604
618
  */
605
- #addContainer(container) {
619
+ #addContainer(container, updateVisibility = true) {
606
620
  if (!container.assetContainer || container.state.isVisible || !container.state.mustBeShown) {
607
621
  return false;
608
622
  }
609
- this.#updateVisibilityAttributeInComponentes(container.state.name, true);
623
+ if (updateVisibility) {
624
+ this.#updateVisibilityAttributeInComponents(container.state.name, true);
625
+ }
610
626
  container.assetContainer.addAllToScene();
611
627
  container.state.visible = true;
612
628
  return true;
@@ -616,13 +632,16 @@ export default class BabylonJSController {
616
632
  * Removes the asset container from the Babylon.js scene if it is currently visible.
617
633
  * @private
618
634
  * @param {ContainerData} container - The container object containing asset state and metadata.
635
+ * @param {boolean} [updateVisibility=true] - If true, updates the visibility attribute in parent components.
619
636
  * @returns {boolean} True if the container was removed, false otherwise.
620
637
  */
621
- #removeContainer(container) {
638
+ #removeContainer(container, updateVisibility = true) {
622
639
  if (!container.assetContainer || !container.state.isVisible) {
623
640
  return false;
624
641
  }
625
- this.#updateVisibilityAttributeInComponentes(container.state.name, false);
642
+ if (updateVisibility) {
643
+ this.#updateVisibilityAttributeInComponents(container.state.name, false);
644
+ }
626
645
  container.assetContainer.removeAllFromScene();
627
646
  container.state.visible = false;
628
647
  return true;
@@ -637,14 +656,15 @@ export default class BabylonJSController {
637
656
  */
638
657
  #replaceContainer(container, newAssetContainer) {
639
658
  if (container.assetContainer) {
640
- this.#removeContainer(container);
659
+ this.#removeContainer(container, false);
641
660
  container.assetContainer.dispose();
642
661
  container.assetContainer = null;
643
662
  }
644
663
  this.#scene.getEngine().releaseEffects();
645
664
  container.assetContainer = newAssetContainer;
646
- return this.#addContainer(container);
665
+ return this.#addContainer(container, false);
647
666
  }
667
+
648
668
  /**
649
669
  * Sets the visibility of wall and floor meshes in the model container based on the provided value or environment visibility.
650
670
  * @private
@@ -748,6 +768,10 @@ export default class BabylonJSController {
748
768
 
749
769
  await Promise.allSettled(promiseArray)
750
770
  .then((values) => {
771
+ if (this.#babylonJSAnimationController) {
772
+ this.#babylonJSAnimationController.dispose();
773
+ this.#babylonJSAnimationController = null;
774
+ };
751
775
  values.forEach((result) => {
752
776
  const container = result.value ? result.value[0] : null;
753
777
  const assetContainer = result.value ? result.value[1] : null;
@@ -755,10 +779,7 @@ export default class BabylonJSController {
755
779
  if (container.state.name === "model") {
756
780
  assetContainer.lights = [];
757
781
  }
758
- const replacedAndAdded = this.#replaceContainer(container, assetContainer);
759
- if (replacedAndAdded && container.state.name === "model") {
760
- this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene);
761
- }
782
+ this.#replaceContainer(container, assetContainer);
762
783
  container.state.setSuccess(true);
763
784
  } else {
764
785
  if (container.assetContainer && container.state.mustBeShown !== container.state.isVisible && container.state.name === "materials") {
@@ -767,7 +788,7 @@ export default class BabylonJSController {
767
788
  container.state.setSuccess(false);
768
789
  }
769
790
  });
770
-
791
+
771
792
  this.#setOptions_Materials();
772
793
  this.#setOptions_Camera();
773
794
  this.#setVisibilityOfWallAndFloorInModel();
@@ -782,9 +803,9 @@ export default class BabylonJSController {
782
803
  .finally(async () => {
783
804
  this.#setMaxSimultaneousLights();
784
805
  this.#initializeShadows();
806
+ this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene);
785
807
  this.#startRender();
786
808
  });
787
-
788
809
  return detail;
789
810
  }
790
811