@preference-sl/pref-viewer 2.12.0-beta.5 → 2.12.0-beta.7

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.12.0-beta.5",
3
+ "version": "2.12.0-beta.7",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -313,18 +313,27 @@ export default class BabylonJSController {
313
313
  }
314
314
 
315
315
  // 1) Stronger ambient fill
316
- this.#hemiLight = new HemisphericLight("PrefViewerHemiLight", new Vector3(-10, 10, -10), this.#scene);
317
- this.#hemiLight.intensity = 0.6;
316
+ this.#hemiLight = this.#scene.getLightByName("PrefViewerHemiLight");
317
+ if (!this.#hemiLight) {
318
+ this.#hemiLight = new HemisphericLight("PrefViewerHemiLight", new Vector3(-10, 10, -10), this.#scene);
319
+ this.#hemiLight.intensity = 0.6;
320
+ }
318
321
 
319
322
  // 2) Directional light from the front-right, angled slightly down
320
- this.#dirLight = new DirectionalLight("PrefViewerDirLight", new Vector3(-10, 10, -10), this.#scene);
321
- this.#dirLight.position = new Vector3(5, 4, 5); // light is IN FRONT + ABOVE + to the RIGHT
322
- this.#dirLight.intensity = 0.6;
323
+ this.#dirLight = this.#scene.getLightByName("PrefViewerDirLight");
324
+ if (!this.#dirLight) {
325
+ this.#dirLight = new DirectionalLight("PrefViewerDirLight", new Vector3(-10, 10, -10), this.#scene);
326
+ this.#dirLight.position = new Vector3(5, 4, 5); // light is IN FRONT + ABOVE + to the RIGHT
327
+ this.#dirLight.intensity = 0.6;
328
+ }
323
329
 
324
330
  // 3) Camera‐attached headlight
325
- this.#cameraLight = new PointLight("PrefViewerCameraLight", this.#camera.position, this.#scene);
326
- this.#cameraLight.parent = this.#camera;
327
- this.#cameraLight.intensity = 0.3;
331
+ this.#cameraLight = this.#scene.getLightByName("PrefViewerCameraLight");
332
+ if (!this.#cameraLight) {
333
+ this.#cameraLight = new PointLight("PrefViewerCameraLight", this.#camera.position, this.#scene);
334
+ this.#cameraLight.parent = this.#camera;
335
+ this.#cameraLight.intensity = 0.3;
336
+ }
328
337
  }
329
338
 
330
339
  /**
@@ -375,6 +384,13 @@ export default class BabylonJSController {
375
384
  ssaoPipeline.radius = 0.0001;
376
385
  ssaoPipeline.totalStrength = 1;
377
386
  ssaoPipeline.base = 0.6;
387
+
388
+ // Configure SSAO to calculate only once instead of every frame for better performance
389
+ if (ssaoPipeline._ssaoPostProcess) {
390
+ ssaoPipeline._ssaoPostProcess.autoClear = false;
391
+ ssaoPipeline._ssaoPostProcess.samples = 1;
392
+ }
393
+
378
394
  this.#scene.postProcessRenderPipelineManager.update();
379
395
  return true;
380
396
  } else {
@@ -447,6 +463,14 @@ export default class BabylonJSController {
447
463
  defaultPipeline.grain.animated = false;
448
464
  defaultPipeline.grain.intensity = 3;
449
465
 
466
+ // Configure post-processes to calculate only once instead of every frame for better performance
467
+ if (defaultPipeline.fxaa?._postProcess) {
468
+ defaultPipeline.fxaa._postProcess.autoClear = false;
469
+ }
470
+ if (defaultPipeline.grain?._postProcess) {
471
+ defaultPipeline.grain._postProcess.autoClear = false;
472
+ }
473
+
450
474
  this.#scene.postProcessRenderPipelineManager.update();
451
475
  return true;
452
476
  } else {
@@ -503,8 +527,8 @@ export default class BabylonJSController {
503
527
  }
504
528
 
505
529
  const pipelineOptions = {
506
- resolutionExp: 8, // Higher resolution for better shadow quality
507
- sampleDirections: 4, // More sample directions for smoother shadows
530
+ resolutionExp: 1, // Higher resolution for better shadow quality (recomended 8)
531
+ sampleDirections: 4, // More sample directions for smoother shadows (recomended 4)
508
532
  ssShadowsEnabled: true,
509
533
  shadowRemanence: 0.85,
510
534
  triPlanarVoxelization: true,
@@ -533,8 +557,15 @@ export default class BabylonJSController {
533
557
 
534
558
  Object.assign(iblShadowsRenderPipeline, pipelineProps);
535
559
 
560
+ if (iblShadowsRenderPipeline._ssaoPostProcess) {
561
+ iblShadowsRenderPipeline._ssaoPostProcess.autoClear = false;
562
+ iblShadowsRenderPipeline._ssaoPostProcess.samples = 1;
563
+ }
564
+
536
565
  this.#scene.meshes.forEach((mesh) => {
537
- if (mesh.id.startsWith("__root__") || mesh.name === "hdri") {
566
+ const isRootMesh = mesh.id.startsWith("__root__");
567
+ const isHDRIMesh = mesh.name?.toLowerCase() === "hdri";
568
+ if (isRootMesh || isHDRIMesh) {
538
569
  return false;
539
570
  }
540
571
  iblShadowsRenderPipeline.addShadowCastingMesh(mesh);
@@ -566,6 +597,9 @@ export default class BabylonJSController {
566
597
  */
567
598
  #initializeDefaultLightShadows() {
568
599
  this.#shadowGen = [];
600
+ if (!this.#dirLight) {
601
+ return;
602
+ }
569
603
  this.#dirLight.autoUpdateExtends = false;
570
604
  const shadowGenerator = new ShadowGenerator(1024, this.#dirLight);
571
605
  shadowGenerator.useBlurExponentialShadowMap = true;
@@ -578,7 +612,7 @@ export default class BabylonJSController {
578
612
  if (mesh.id.startsWith("__root__")) {
579
613
  return false;
580
614
  }
581
- if (mesh.name !== "hdri") {
615
+ if (mesh.name?.toLowerCase() !== "hdri") {
582
616
  shadowGenerator.addShadowCaster(mesh, true);
583
617
  }
584
618
  mesh.receiveShadows = true;
@@ -618,7 +652,7 @@ export default class BabylonJSController {
618
652
  if (mesh.id.startsWith("__root__")) {
619
653
  return false;
620
654
  }
621
- if (mesh.name !== "hdri") {
655
+ if (mesh.name?.toLowerCase() !== "hdri") {
622
656
  shadowGenerator.addShadowCaster(mesh, true);
623
657
  }
624
658
  });
@@ -1180,7 +1214,14 @@ export default class BabylonJSController {
1180
1214
  },
1181
1215
  };
1182
1216
 
1183
- return [container, await LoadAssetContainerAsync(sourceData.source, this.#scene, options)];
1217
+ let assetContainer = null;
1218
+
1219
+ try {
1220
+ assetContainer = await LoadAssetContainerAsync(sourceData.source, this.#scene, options);
1221
+ return [container, assetContainer];
1222
+ } catch (error) {
1223
+ return [container, assetContainer];
1224
+ }
1184
1225
  }
1185
1226
 
1186
1227
  /**
@@ -1341,6 +1382,13 @@ export default class BabylonJSController {
1341
1382
  }
1342
1383
  }
1343
1384
 
1385
+ /**
1386
+ * Configures realistic glass material properties for all materials whose names include "Glass".
1387
+ * @private
1388
+ * @param {AssetContainer} [assetContainer] - The asset container with materials to process. If undefined, uses the model container.
1389
+ * @returns {void}
1390
+ * @note This method assumes that glass materials are named with the substring "Glass".
1391
+ */
1344
1392
  #forceReflectionsInModelGlasses(assetContainer) {
1345
1393
  if (assetContainer === undefined) {
1346
1394
  assetContainer = this.#containers.model.assetContainer;
@@ -1350,7 +1398,7 @@ export default class BabylonJSController {
1350
1398
  }
1351
1399
  assetContainer.materials?.forEach(material => {
1352
1400
  if (material && material.name && material.name.includes("Glass")) {
1353
- material.metallic = 1.0;
1401
+ material.metallic = 0.25;
1354
1402
  material.environmentIntensity = 1.0;
1355
1403
  }
1356
1404
  });
@@ -169,7 +169,7 @@ export class FileStorage {
169
169
  let file = undefined;
170
170
  return new Promise((resolve) => {
171
171
  const xhr = new XMLHttpRequest();
172
- xhr.open("GET", uri, true);
172
+ xhr.open("GET", encodeURI(uri), true);
173
173
  xhr.responseType = "blob";
174
174
  xhr.onload = () => {
175
175
  if (xhr.status === 200) {
@@ -205,7 +205,7 @@ export class FileStorage {
205
205
  let timeStamp = null;
206
206
  return new Promise((resolve) => {
207
207
  const xhr = new XMLHttpRequest();
208
- xhr.open("HEAD", uri, true);
208
+ xhr.open("HEAD", encodeURI(uri), true);
209
209
  xhr.responseType = "blob";
210
210
  xhr.onload = () => {
211
211
  if (xhr.status === 200) {
@@ -238,7 +238,7 @@ export class FileStorage {
238
238
  let size = 0;
239
239
  return new Promise((resolve) => {
240
240
  const xhr = new XMLHttpRequest();
241
- xhr.open("HEAD", uri, true);
241
+ xhr.open("HEAD", encodeURI(uri), true);
242
242
  xhr.responseType = "blob";
243
243
  xhr.onload = () => {
244
244
  if (xhr.status === 200) {