@preference-sl/pref-viewer 2.10.0-beta.11 → 2.10.0-beta.13

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +130 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.11",
3
+ "version": "2.10.0-beta.13",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -49,6 +49,13 @@ import { initDb, loadModel } from "./gltf-storage.js";
49
49
  class PrefViewer extends HTMLElement {
50
50
  #initialized = false;
51
51
 
52
+ // --- logging helper (no functional change) ---
53
+ #logNS = "PrefViewer";
54
+ #log(method, ...args) {
55
+ const fn = console[method] || console.log;
56
+ fn(`[${this.#logNS}]`, ...args);
57
+ }
58
+
52
59
  #data = {
53
60
  containers: {
54
61
  model: {
@@ -142,6 +149,7 @@ class PrefViewer extends HTMLElement {
142
149
  // JS fallback if WASM isn’t available
143
150
  fallbackUrl: `${DRACO_BASE}/draco_decoder_gltf.js`,
144
151
  };
152
+ this.#log("info", "constructed");
145
153
  }
146
154
 
147
155
  static get observedAttributes() {
@@ -149,6 +157,8 @@ class PrefViewer extends HTMLElement {
149
157
  }
150
158
 
151
159
  attributeChangedCallback(name, _old, value) {
160
+ this.#log("groupCollapsed", `attributeChanged: ${name}`);
161
+ this.#log("info", "new value:", value);
152
162
  let data = null;
153
163
  switch (name) {
154
164
  case "config":
@@ -177,6 +187,7 @@ class PrefViewer extends HTMLElement {
177
187
  }
178
188
  break;
179
189
  }
190
+ console.groupEnd();
180
191
  }
181
192
 
182
193
  connectedCallback() {
@@ -193,12 +204,14 @@ class PrefViewer extends HTMLElement {
193
204
  return false;
194
205
  }
195
206
 
207
+ this.#log("info", "connectedCallback: initializing Babylon and starting initial load");
196
208
  this.#initializeBabylon();
197
209
  this.#loadContainers(true, true, true);
198
210
  this.#initialized = true;
199
211
  }
200
212
 
201
213
  disconnectedCallback() {
214
+ this.#log("info", "disconnectedCallback: disposing engine and observers");
202
215
  this.#disposeEngine();
203
216
  this.#canvasResizeObserver.disconnect();
204
217
  }
@@ -228,36 +241,47 @@ class PrefViewer extends HTMLElement {
228
241
  // Data
229
242
  #checkCameraChanged(options) {
230
243
  if (!options || !options.camera) {
244
+ this.#log("debug", "checkCameraChanged: no camera in options");
231
245
  return false;
232
246
  }
233
- this.#data.options.camera.changed = options.camera && options.camera !== this.#data.options.camera.value ? true : false;
234
- this.#data.options.camera.value = this.#data.options.camera.changed ? options.camera : this.#data.options.camera.value;
247
+ const prev = this.#data.options.camera.value;
248
+ const next = options.camera;
249
+ this.#data.options.camera.changed = options.camera && options.camera !== prev ? true : false;
250
+ this.#data.options.camera.value = this.#data.options.camera.changed ? next : prev;
251
+ this.#log("info", "checkCameraChanged:", { prev, next, changed: this.#data.options.camera.changed });
235
252
  return this.#data.options.camera.changed;
236
253
  }
237
254
 
238
255
  #checkMaterialsChanged(options) {
239
256
  if (!options) {
257
+ this.#log("debug", "checkMaterialsChanged: no options object");
240
258
  return false;
241
259
  }
242
260
  let someChanged = false;
243
261
  Object.keys(this.#data.options.materials).forEach((material) => {
244
262
  const key = `${material}Material`;
245
- this.#data.options.materials[material].changed = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
246
- this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : this.#data.options.materials[material].value;
263
+ const prev = this.#data.options.materials[material].value;
264
+ const next = options[key];
265
+ this.#data.options.materials[material].changed = next && next !== prev ? true : false;
266
+ this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? next : prev;
247
267
  someChanged = someChanged || this.#data.options.materials[material].changed;
268
+ this.#log("info", "checkMaterialsChanged item:", { material, key, prev, next, changed: this.#data.options.materials[material].changed });
248
269
  });
270
+ this.#log("info", "checkMaterialsChanged: someChanged =", someChanged);
249
271
  return someChanged;
250
272
  }
251
273
 
252
274
  #storeChangedFlagsForContainer(container) {
253
275
  container.timestamp = container.changed.timestamp;
254
276
  container.size = container.changed.size;
277
+ this.#log("debug", "storeChangedFlagsForContainer:", { name: container.name, timestamp: container.timestamp, size: container.size });
255
278
  }
256
279
 
257
280
  #resetChangedFlags() {
258
281
  Object.values(this.#data.containers).forEach((container) => (container.changed = false));
259
282
  Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
260
283
  this.#data.options.camera.changed = false;
284
+ this.#log("debug", "resetChangedFlags()");
261
285
  }
262
286
 
263
287
  // Babylon.js
@@ -272,6 +296,7 @@ class PrefViewer extends HTMLElement {
272
296
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
273
297
  this.#canvasResizeObserver.observe(this.#canvas);
274
298
 
299
+ this.#log("info", "Babylon initialized: creating XR experience (if supported)...");
275
300
  await this.#createXRExperience();
276
301
  }
277
302
 
@@ -290,7 +315,7 @@ class PrefViewer extends HTMLElement {
290
315
  const sessionMode = "immersive-ar";
291
316
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
292
317
  if (!sessionSupported) {
293
- console.info("PrefViewer: WebXR in mode AR is not supported");
318
+ this.#log("info", "WebXR in mode AR is not supported");
294
319
  return false;
295
320
  }
296
321
 
@@ -325,6 +350,7 @@ class PrefViewer extends HTMLElement {
325
350
  });
326
351
 
327
352
  this.addStylesToARButton();
353
+ this.#log("info", "WebXR experience created");
328
354
  } catch (error) {
329
355
  console.warn("PrefViewer: failed to create WebXR experience", error);
330
356
  this.#XRExperience = null;
@@ -342,6 +368,7 @@ class PrefViewer extends HTMLElement {
342
368
  this.#camera.metadata = { locked: false }
343
369
  this.#camera = this.#camera;
344
370
  this.#camera.attachControl(this.#canvas, true);
371
+ this.#log("info", "Default camera created");
345
372
  }
346
373
 
347
374
  #createLights() {
@@ -364,6 +391,8 @@ class PrefViewer extends HTMLElement {
364
391
  this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
365
392
  this.#cameraLight.parent = this.#camera;
366
393
  this.#cameraLight.intensity = 0.3;
394
+
395
+ this.#log("info", "Lights & shadows configured");
367
396
  }
368
397
 
369
398
  #setupInteraction() {
@@ -386,6 +415,7 @@ class PrefViewer extends HTMLElement {
386
415
  this.#engine = this.#scene = this.#camera = null;
387
416
  this.#hemiLight = this.#dirLight = this.#cameraLight = null;
388
417
  this.#shadowGen = null;
418
+ this.#log("info", "Engine disposed");
389
419
  }
390
420
 
391
421
  // Utility methods for loading gltf/glb
@@ -429,7 +459,7 @@ class PrefViewer extends HTMLElement {
429
459
  return { blob, extension, size };
430
460
  }
431
461
  let isJson = false;
432
- try {
462
+ try {
433
463
  JSON.parse(decoded);
434
464
  isJson = true;
435
465
  } catch {}
@@ -454,16 +484,20 @@ class PrefViewer extends HTMLElement {
454
484
  }
455
485
  show = show !== undefined ? show : this.#data.containers.environment.visible;
456
486
  const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
457
- this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
487
+ const meshes = this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix)));
488
+ this.#log("debug", "setVisibilityOfWallAndFloorInModel:", { show, affectedMeshes: meshes.map(m => m.name) });
489
+ meshes.forEach((mesh) => mesh.setEnabled(show));
458
490
  }
459
491
 
460
492
  #setOptionsMaterial(optionMaterial) {
461
493
  if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
494
+ this.#log("debug", "setOptionsMaterial: missing data", optionMaterial);
462
495
  return false;
463
496
  }
464
497
 
465
498
  const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
466
499
  if (!material) {
500
+ this.#log("warn", `setOptionsMaterial: material "${optionMaterial.value}" not found in materials container`);
467
501
  return false;
468
502
  }
469
503
 
@@ -475,20 +509,22 @@ class PrefViewer extends HTMLElement {
475
509
  containers.push(this.#data.containers.environment.assetContainer);
476
510
  }
477
511
  if (containers.length === 0) {
512
+ this.#log("debug", "setOptionsMaterial: no target containers (unchanged)");
478
513
  return false;
479
514
  }
480
515
 
481
- let someSetted = false;
516
+ let count = 0;
482
517
  containers.forEach((container) =>
483
518
  container.meshes
484
519
  .filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix))
485
520
  .forEach((mesh) => {
486
521
  mesh.material = material;
487
- someSetted = true;
522
+ count++;
488
523
  })
489
524
  );
490
525
 
491
- return someSetted;
526
+ this.#log("info", "setOptionsMaterial applied", { prefix: optionMaterial.prefix, material: optionMaterial.value, meshesUpdated: count });
527
+ return count > 0;
492
528
  }
493
529
 
494
530
  #setOptionsMaterials() {
@@ -497,16 +533,19 @@ class PrefViewer extends HTMLElement {
497
533
  let settedMaterial = this.#setOptionsMaterial(material);
498
534
  someSetted = someSetted || settedMaterial;
499
535
  });
536
+ this.#log("info", "setOptionsMaterials: anyApplied =", someSetted);
500
537
  return someSetted;
501
538
  }
502
539
 
503
540
  #setOptionsCamera() {
504
541
  if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.assetContainer.changed)) {
542
+ this.#log("debug", "setOptionsCamera: skipped (no change or no camera specified)");
505
543
  return false;
506
544
  }
507
545
 
508
546
  let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
509
547
  if (!camera) {
548
+ this.#log("warn", `setOptionsCamera: camera "${this.#data.options.camera.value}" not found in model`);
510
549
  return false;
511
550
  }
512
551
 
@@ -515,6 +554,7 @@ class PrefViewer extends HTMLElement {
515
554
  camera.attachControl(this.#canvas, true);
516
555
  }
517
556
  this.#scene.activeCamera = camera;
557
+ this.#log("info", "setOptionsCamera: activated", { name: camera.name, locked: this.#data.options.camera.locked });
518
558
 
519
559
  return true;
520
560
  }
@@ -523,6 +563,7 @@ class PrefViewer extends HTMLElement {
523
563
  if (container.assetContainer && !container.visible && container.show) {
524
564
  container.assetContainer.addAllToScene();
525
565
  container.visible = true;
566
+ this.#log("debug", "addContainer -> visible", container.name);
526
567
  }
527
568
  }
528
569
 
@@ -530,6 +571,7 @@ class PrefViewer extends HTMLElement {
530
571
  if (container.assetContainer && container.visible) {
531
572
  container.assetContainer.removeAllFromScene();
532
573
  container.visible = false;
574
+ this.#log("debug", "removeContainer -> hidden", container.name);
533
575
  }
534
576
  }
535
577
 
@@ -541,15 +583,24 @@ class PrefViewer extends HTMLElement {
541
583
  this.#shadowGen.addShadowCaster(mesh, true);
542
584
  });
543
585
  this.#addContainer(container);
586
+ this.#log("debug", "replaceContainer:", container.name, {
587
+ meshes: container.assetContainer.meshes?.length,
588
+ materials: container.assetContainer.materials?.length,
589
+ cameras: container.assetContainer.cameras?.length,
590
+ });
544
591
  }
545
592
 
546
593
  async #loadAssetContainer(container) {
547
594
  let storage = container?.storage;
548
595
 
549
596
  if (!storage) {
597
+ this.#log("debug", `loadAssetContainer(${container?.name}): no storage provided`);
550
598
  return false;
551
599
  }
552
600
 
601
+ this.#log("groupCollapsed", `loadAssetContainer -> ${container.name}`);
602
+ this.#log("info", "storage:", storage);
603
+
553
604
  let source = storage.url || null;
554
605
 
555
606
  if (storage.db && storage.table && storage.id) {
@@ -557,13 +608,18 @@ class PrefViewer extends HTMLElement {
557
608
  const object = await loadModel(storage.id, storage.table);
558
609
  source = object.data;
559
610
  if (object.timestamp === container.timestamp) {
611
+ this.#log("info", "IndexedDB source unchanged (timestamp match). Skipping reload.");
612
+ console.groupEnd();
560
613
  return false;
561
614
  } else {
562
615
  container.changed = { timestamp: object.timestamp, size: object.size };
616
+ this.#log("info", "IndexedDB source changed:", container.changed);
563
617
  }
564
618
  }
565
619
 
566
620
  if (!source) {
621
+ this.#log("warn", "no source (url/db) to load");
622
+ console.groupEnd();
567
623
  return false;
568
624
  }
569
625
 
@@ -576,9 +632,12 @@ class PrefViewer extends HTMLElement {
576
632
  });
577
633
  if (!container.changed) {
578
634
  if (container.timestamp === null && container.size === size) {
635
+ this.#log("info", "Base64 source unchanged (size match). Skipping reload.");
636
+ console.groupEnd();
579
637
  return false;
580
638
  } else {
581
639
  container.changed = { timestamp: null, size: size };
640
+ this.#log("info", "Base64 source changed (size):", container.changed);
582
641
  }
583
642
  }
584
643
  } else {
@@ -586,9 +645,12 @@ class PrefViewer extends HTMLElement {
586
645
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
587
646
  const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
588
647
  if (container.timestamp === fileTimestamp && container.size === fileSize) {
648
+ this.#log("info", "URL source unchanged (HEAD size/timestamp). Skipping reload.");
649
+ console.groupEnd();
589
650
  return false;
590
651
  } else {
591
652
  container.changed = { timestamp: fileTimestamp, size: fileSize };
653
+ this.#log("info", "URL source changed:", container.changed);
592
654
  }
593
655
  }
594
656
 
@@ -602,10 +664,19 @@ class PrefViewer extends HTMLElement {
602
664
  },
603
665
  };
604
666
 
605
- return LoadAssetContainerAsync(file || source, this.#scene, options);
667
+ this.#log("info", "Loading asset container with options:", options);
668
+ const result = await LoadAssetContainerAsync(file || source, this.#scene, options);
669
+ this.#log("info", "Asset container loaded:", {
670
+ meshes: result.meshes?.length,
671
+ materials: result.materials?.length,
672
+ cameras: result.cameras?.length,
673
+ });
674
+ console.groupEnd();
675
+ return result;
606
676
  }
607
677
 
608
678
  async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
679
+ this.#log("groupCollapsed", "loadContainers flags", { loadModel, loadEnvironment, loadMaterials });
609
680
  const promiseArray = [];
610
681
  promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
611
682
  promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
@@ -617,6 +688,12 @@ class PrefViewer extends HTMLElement {
617
688
  const environmentContainer = values[1];
618
689
  const materialsContainer = values[2];
619
690
 
691
+ this.#log("info", "loadContainers results:", {
692
+ model: modelContainer?.status,
693
+ environment: environmentContainer?.status,
694
+ materials: materialsContainer?.status,
695
+ });
696
+
620
697
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
621
698
  this.#replaceContainer(this.#data.containers.model, modelContainer.value);
622
699
  this.#storeChangedFlagsForContainer(this.#data.containers.model);
@@ -636,10 +713,12 @@ class PrefViewer extends HTMLElement {
636
713
  this.#storeChangedFlagsForContainer(this.#data.containers.materials);
637
714
  }
638
715
 
639
- this.#setOptionsMaterials();
640
- this.#setOptionsCamera();
716
+ const materialsApplied = this.#setOptionsMaterials();
717
+ const cameraApplied = this.#setOptionsCamera();
641
718
  this.#setVisibilityOfWallAndFloorInModel();
642
719
 
720
+ this.#log("info", "post-load options applied", { materialsApplied, cameraApplied });
721
+
643
722
  this.#resetChangedFlags();
644
723
 
645
724
  this.dispatchEvent(
@@ -649,6 +728,8 @@ class PrefViewer extends HTMLElement {
649
728
  composed: true,
650
729
  })
651
730
  );
731
+ this.#log("info", "model-loaded event dispatched");
732
+ console.groupEnd();
652
733
  })
653
734
  .catch((error) => {
654
735
  console.error("PrefViewer: failed to load model", error);
@@ -659,13 +740,18 @@ class PrefViewer extends HTMLElement {
659
740
  composed: true,
660
741
  })
661
742
  );
743
+ this.#log("error", "model-error event dispatched");
744
+ console.groupEnd();
662
745
  });
663
746
  }
664
747
 
665
748
  // Public Methods
666
749
  loadConfig(config) {
750
+ this.#log("groupCollapsed", "loadConfig called with:", config);
667
751
  config = typeof config === "string" ? JSON.parse(config) : config;
668
752
  if (!config) {
753
+ this.#log("warn", "loadConfig: no config provided");
754
+ console.groupEnd();
669
755
  return false;
670
756
  }
671
757
 
@@ -676,17 +762,30 @@ class PrefViewer extends HTMLElement {
676
762
  this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
677
763
  this.#data.containers.materials.storage = config.materials?.storage || null;
678
764
 
765
+ this.#log("info", "containers set from config", {
766
+ model: this.#data.containers.model,
767
+ environment: this.#data.containers.environment,
768
+ materials: this.#data.containers.materials,
769
+ });
770
+
679
771
  // Options
680
772
  if (config.options) {
681
- this.#checkCameraChanged(config.options);
682
- this.#checkMaterialsChanged(config.options);
773
+ const camChanged = this.#checkCameraChanged(config.options);
774
+ const matsChanged = this.#checkMaterialsChanged(config.options);
775
+ this.#log("info", "options parsed", { camChanged, matsChanged, options: this.#data.options });
776
+ } else {
777
+ this.#log("debug", "no options in config");
683
778
  }
684
779
 
685
780
  this.#initialized && this.#loadContainers(true, true, true);
781
+ console.groupEnd();
686
782
  }
687
783
 
688
784
  setOptions(options) {
785
+ this.#log("groupCollapsed", "setOptions called with:", options);
689
786
  if (!options) {
787
+ this.#log("warn", "setOptions: no options");
788
+ console.groupEnd();
690
789
  return false;
691
790
  }
692
791
  let someSetted = false;
@@ -697,47 +796,63 @@ class PrefViewer extends HTMLElement {
697
796
  someSetted = someSetted || this.#setOptionsMaterials();
698
797
  }
699
798
  this.#resetChangedFlags();
799
+ this.#log("info", "setOptions result:", { applied: someSetted, currentOptions: this.#data.options });
700
800
  debugger;
801
+ console.groupEnd();
701
802
  return someSetted;
702
803
  }
703
804
 
704
805
  loadModel(model) {
806
+ this.#log("groupCollapsed", "loadModel called with:", model);
705
807
  model = typeof model === "string" ? JSON.parse(model) : model;
706
808
  if (!model) {
809
+ this.#log("warn", "loadModel: no model object");
810
+ console.groupEnd();
707
811
  return false;
708
812
  }
709
813
  this.#data.containers.model.storage = model.storage || null;
710
814
  this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
815
+ this.#log("info", "model container updated", this.#data.containers.model);
711
816
  this.#initialized && this.#loadContainers(true, false, false);
817
+ console.groupEnd();
712
818
  }
713
819
 
714
820
  loadScene(scene) {
821
+ this.#log("groupCollapsed", "loadScene called with:", scene);
715
822
  scene = typeof scene === "string" ? JSON.parse(scene) : scene;
716
823
  if (!scene) {
824
+ this.#log("warn", "loadScene: no scene object");
825
+ console.groupEnd();
717
826
  return false;
718
827
  }
719
828
  this.#data.containers.environment.storage = scene.storage || null;
720
829
  this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
830
+ this.#log("info", "environment container updated", this.#data.containers.environment);
721
831
  this.#initialized && this.#loadContainers(false, true, false);
832
+ console.groupEnd();
722
833
  }
723
834
 
724
835
  showModel() {
836
+ this.#log("info", "showModel()");
725
837
  this.#data.containers.model.show = true;
726
838
  this.#addContainer(this.#data.containers.model);
727
839
  }
728
840
 
729
841
  hideModel() {
842
+ this.#log("info", "hideModel()");
730
843
  this.#data.containers.model.show = false;
731
844
  this.#removeContainer(this.#data.containers.model);
732
845
  }
733
846
 
734
847
  showScene() {
848
+ this.#log("info", "showScene()");
735
849
  this.#data.containers.environment.show = true;
736
850
  this.#addContainer(this.#data.containers.environment);
737
851
  this.#setVisibilityOfWallAndFloorInModel();
738
852
  }
739
853
 
740
854
  hideScene() {
855
+ this.#log("info", "hideScene()");
741
856
  this.#data.containers.environment.show = false;
742
857
  this.#removeContainer(this.#data.containers.environment);
743
858
  this.#setVisibilityOfWallAndFloorInModel();