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

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 +182 -172
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.13",
3
+ "version": "2.10.0-beta.15",
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
@@ -47,14 +47,9 @@ import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompre
47
47
  import { initDb, loadModel } from "./gltf-storage.js";
48
48
 
49
49
  class PrefViewer extends HTMLElement {
50
- #initialized = false;
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
- }
50
+ initialized = false;
51
+ loaded = false;
52
+ loading = false;
58
53
 
59
54
  #data = {
60
55
  containers: {
@@ -149,7 +144,6 @@ class PrefViewer extends HTMLElement {
149
144
  // JS fallback if WASM isn’t available
150
145
  fallbackUrl: `${DRACO_BASE}/draco_decoder_gltf.js`,
151
146
  };
152
- this.#log("info", "constructed");
153
147
  }
154
148
 
155
149
  static get observedAttributes() {
@@ -157,8 +151,6 @@ class PrefViewer extends HTMLElement {
157
151
  }
158
152
 
159
153
  attributeChangedCallback(name, _old, value) {
160
- this.#log("groupCollapsed", `attributeChanged: ${name}`);
161
- this.#log("info", "new value:", value);
162
154
  let data = null;
163
155
  switch (name) {
164
156
  case "config":
@@ -172,7 +164,7 @@ class PrefViewer extends HTMLElement {
172
164
  break;
173
165
  case "show-model":
174
166
  data = value.toLowerCase?.() === "true";
175
- if (this.#initialized) {
167
+ if (this.initialized) {
176
168
  data ? this.showModel() : this.hideModel();
177
169
  } else {
178
170
  this.#data.containers.model.show = data;
@@ -180,14 +172,13 @@ class PrefViewer extends HTMLElement {
180
172
  break;
181
173
  case "show-scene":
182
174
  data = value.toLowerCase?.() === "true";
183
- if (this.#initialized) {
175
+ if (this.initialized) {
184
176
  data ? this.showScene() : this.hideScene();
185
177
  } else {
186
178
  this.#data.containers.environment.show = data;
187
179
  }
188
180
  break;
189
181
  }
190
- console.groupEnd();
191
182
  }
192
183
 
193
184
  connectedCallback() {
@@ -195,23 +186,22 @@ class PrefViewer extends HTMLElement {
195
186
  const error = 'PrefViewer: provide "models" as array of model and environment';
196
187
  console.error(error);
197
188
  this.dispatchEvent(
198
- new CustomEvent("model-error", {
199
- detail: { error: new Error(error) },
189
+ new CustomEvent("scene-error", {
200
190
  bubbles: true,
191
+ cancelable: false,
201
192
  composed: true,
193
+ detail: { error: new Error(error) },
202
194
  })
203
195
  );
204
196
  return false;
205
197
  }
206
198
 
207
- this.#log("info", "connectedCallback: initializing Babylon and starting initial load");
208
199
  this.#initializeBabylon();
200
+ this.initialized = true;
209
201
  this.#loadContainers(true, true, true);
210
- this.#initialized = true;
211
202
  }
212
203
 
213
204
  disconnectedCallback() {
214
- this.#log("info", "disconnectedCallback: disposing engine and observers");
215
205
  this.#disposeEngine();
216
206
  this.#canvasResizeObserver.disconnect();
217
207
  }
@@ -238,50 +228,98 @@ class PrefViewer extends HTMLElement {
238
228
  this.shadowRoot.append(this.#wrapper);
239
229
  }
240
230
 
231
+ #setStatusSceneLoading(detail) {
232
+ this.loaded = false;
233
+ this.loading = true;
234
+ if (this.hasAttribute("loaded")) {
235
+ this.removeAttribute("loaded");
236
+ }
237
+ this.setAttribute("loading", "");
238
+ this.dispatchEvent(
239
+ new CustomEvent("scene-loading", {
240
+ bubbles: true,
241
+ cancelable: false,
242
+ composed: true,
243
+ detail: detail,
244
+ })
245
+ );
246
+ }
247
+
248
+ #setStatusSceneLoaded(detail) {
249
+ this.loaded = true;
250
+ this.loading = false;
251
+ if (this.hasAttribute("loading")) {
252
+ this.removeAttribute("loading");
253
+ }
254
+ this.setAttribute("loaded", "");
255
+ this.dispatchEvent(
256
+ new CustomEvent("scene-loaded", {
257
+ bubbles: true,
258
+ cancelable: false,
259
+ composed: true,
260
+ detail: detail,
261
+ })
262
+ );
263
+ }
264
+
265
+ #setStatusOptionsLoading(detail) {
266
+ this.dispatchEvent(
267
+ new CustomEvent("options-loading", {
268
+ bubbles: true,
269
+ cancelable: false,
270
+ composed: true,
271
+ detail: detail,
272
+ })
273
+ );
274
+ }
275
+
276
+ #setStatusOptionsLoaded(detail) {
277
+ this.dispatchEvent(
278
+ new CustomEvent("options-loaded", {
279
+ bubbles: true,
280
+ cancelable: false,
281
+ composed: true,
282
+ detail: detail,
283
+ })
284
+ );
285
+ }
286
+
241
287
  // Data
242
288
  #checkCameraChanged(options) {
243
289
  if (!options || !options.camera) {
244
- this.#log("debug", "checkCameraChanged: no camera in options");
245
290
  return false;
246
291
  }
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 });
252
- return this.#data.options.camera.changed;
292
+ const cameraChanged = options.camera && options.camera !== this.#data.options.camera.value ? true : false
293
+ this.#data.options.camera.changed = cameraChanged ? { oldValue: this.#data.options.camera.value, success: false } : false;
294
+ this.#data.options.camera.value = cameraChanged ? options.camera : this.#data.options.camera.value;
295
+ return cameraChanged;
253
296
  }
254
297
 
255
298
  #checkMaterialsChanged(options) {
256
299
  if (!options) {
257
- this.#log("debug", "checkMaterialsChanged: no options object");
258
300
  return false;
259
301
  }
260
302
  let someChanged = false;
261
303
  Object.keys(this.#data.options.materials).forEach((material) => {
262
304
  const key = `${material}Material`;
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;
305
+ const materialChanged = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
306
+ this.#data.options.materials[material].changed = materialChanged ? { oldValue: this.#data.options.materials[material].value, success: false } : false;
307
+ this.#data.options.materials[material].value = materialChanged ? options[key] : this.#data.options.materials[material].value;
267
308
  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 });
269
309
  });
270
- this.#log("info", "checkMaterialsChanged: someChanged =", someChanged);
271
310
  return someChanged;
272
311
  }
273
312
 
274
313
  #storeChangedFlagsForContainer(container) {
275
314
  container.timestamp = container.changed.timestamp;
276
315
  container.size = container.changed.size;
277
- this.#log("debug", "storeChangedFlagsForContainer:", { name: container.name, timestamp: container.timestamp, size: container.size });
316
+ container.changed.success = true;
278
317
  }
279
318
 
280
319
  #resetChangedFlags() {
281
320
  Object.values(this.#data.containers).forEach((container) => (container.changed = false));
282
321
  Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
283
322
  this.#data.options.camera.changed = false;
284
- this.#log("debug", "resetChangedFlags()");
285
323
  }
286
324
 
287
325
  // Babylon.js
@@ -296,7 +334,6 @@ class PrefViewer extends HTMLElement {
296
334
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
297
335
  this.#canvasResizeObserver.observe(this.#canvas);
298
336
 
299
- this.#log("info", "Babylon initialized: creating XR experience (if supported)...");
300
337
  await this.#createXRExperience();
301
338
  }
302
339
 
@@ -315,7 +352,7 @@ class PrefViewer extends HTMLElement {
315
352
  const sessionMode = "immersive-ar";
316
353
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
317
354
  if (!sessionSupported) {
318
- this.#log("info", "WebXR in mode AR is not supported");
355
+ console.info("PrefViewer: WebXR in mode AR is not supported");
319
356
  return false;
320
357
  }
321
358
 
@@ -350,7 +387,6 @@ class PrefViewer extends HTMLElement {
350
387
  });
351
388
 
352
389
  this.addStylesToARButton();
353
- this.#log("info", "WebXR experience created");
354
390
  } catch (error) {
355
391
  console.warn("PrefViewer: failed to create WebXR experience", error);
356
392
  this.#XRExperience = null;
@@ -366,9 +402,8 @@ class PrefViewer extends HTMLElement {
366
402
  this.#camera.lowerRadiusLimit = 5;
367
403
  this.#camera.upperRadiusLimit = 20;
368
404
  this.#camera.metadata = { locked: false }
369
- this.#camera = this.#camera;
370
405
  this.#camera.attachControl(this.#canvas, true);
371
- this.#log("info", "Default camera created");
406
+ this.#scene.activeCamera = this.#camera;
372
407
  }
373
408
 
374
409
  #createLights() {
@@ -391,8 +426,6 @@ class PrefViewer extends HTMLElement {
391
426
  this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
392
427
  this.#cameraLight.parent = this.#camera;
393
428
  this.#cameraLight.intensity = 0.3;
394
-
395
- this.#log("info", "Lights & shadows configured");
396
429
  }
397
430
 
398
431
  #setupInteraction() {
@@ -415,7 +448,6 @@ class PrefViewer extends HTMLElement {
415
448
  this.#engine = this.#scene = this.#camera = null;
416
449
  this.#hemiLight = this.#dirLight = this.#cameraLight = null;
417
450
  this.#shadowGen = null;
418
- this.#log("info", "Engine disposed");
419
451
  }
420
452
 
421
453
  // Utility methods for loading gltf/glb
@@ -428,13 +460,13 @@ class PrefViewer extends HTMLElement {
428
460
  if (xhr.status === 200) {
429
461
  const size = parseInt(xhr.getResponseHeader("Content-Length"));
430
462
  const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
431
- resolve(size, timestamp);
463
+ resolve([size, timestamp]);
432
464
  } else {
433
- resolve(0, null);
465
+ resolve([0, null]);
434
466
  }
435
467
  };
436
468
  xhr.onerror = () => {
437
- resolve(0, null);
469
+ resolve([0, null]);
438
470
  };
439
471
  xhr.send();
440
472
  });
@@ -459,7 +491,7 @@ class PrefViewer extends HTMLElement {
459
491
  return { blob, extension, size };
460
492
  }
461
493
  let isJson = false;
462
- try {
494
+ try {
463
495
  JSON.parse(decoded);
464
496
  isJson = true;
465
497
  } catch {}
@@ -484,47 +516,47 @@ class PrefViewer extends HTMLElement {
484
516
  }
485
517
  show = show !== undefined ? show : this.#data.containers.environment.visible;
486
518
  const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
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));
519
+ this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
490
520
  }
491
521
 
492
522
  #setOptionsMaterial(optionMaterial) {
493
523
  if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
494
- this.#log("debug", "setOptionsMaterial: missing data", optionMaterial);
495
524
  return false;
496
525
  }
497
526
 
498
527
  const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
499
528
  if (!material) {
500
- this.#log("warn", `setOptionsMaterial: material "${optionMaterial.value}" not found in materials container`);
501
529
  return false;
502
530
  }
503
531
 
504
532
  const containers = [];
505
- if (this.#data.containers.model.assetContainer && (this.#data.containers.model.assetContainer.changed || optionMaterial.changed)) {
533
+ if (this.#data.containers.model.assetContainer && (this.#data.containers.model.changed || optionMaterial.changed)) {
506
534
  containers.push(this.#data.containers.model.assetContainer);
507
535
  }
508
- if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.assetContainer.changed || optionMaterial.changed)) {
536
+ if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.changed || optionMaterial.changed)) {
509
537
  containers.push(this.#data.containers.environment.assetContainer);
510
538
  }
511
539
  if (containers.length === 0) {
512
- this.#log("debug", "setOptionsMaterial: no target containers (unchanged)");
513
540
  return false;
514
541
  }
515
542
 
516
- let count = 0;
543
+ let someSetted = false;
517
544
  containers.forEach((container) =>
518
545
  container.meshes
519
546
  .filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix))
520
547
  .forEach((mesh) => {
521
548
  mesh.material = material;
522
- count++;
549
+ someSetted = true;
523
550
  })
524
551
  );
525
552
 
526
- this.#log("info", "setOptionsMaterial applied", { prefix: optionMaterial.prefix, material: optionMaterial.value, meshesUpdated: count });
527
- return count > 0;
553
+ if (someSetted) {
554
+ optionMaterial.changed.success = true;
555
+ } else {
556
+ optionMaterial.value = optionMaterial.changed.oldValue;
557
+ }
558
+
559
+ return someSetted;
528
560
  }
529
561
 
530
562
  #setOptionsMaterials() {
@@ -533,29 +565,36 @@ class PrefViewer extends HTMLElement {
533
565
  let settedMaterial = this.#setOptionsMaterial(material);
534
566
  someSetted = someSetted || settedMaterial;
535
567
  });
536
- this.#log("info", "setOptionsMaterials: anyApplied =", someSetted);
537
568
  return someSetted;
538
569
  }
539
570
 
540
571
  #setOptionsCamera() {
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)");
572
+ if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.changed && !this.#data.containers.environment.changed)) {
543
573
  return false;
544
574
  }
545
575
 
546
- let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
576
+ let camera = this.#data.containers.model.assetContainer?.cameras.find((thisCamera) => thisCamera.name === this.#data.options.camera.value) || this.#data.containers.environment.assetContainer?.cameras.find((thisCamera) => thisCamera.name === this.#data.options.camera.value) || null;
547
577
  if (!camera) {
548
- this.#log("warn", `setOptionsCamera: camera "${this.#data.options.camera.value}" not found in model`);
549
- return false;
578
+ if (this.#data.options.camera.changed?.oldValue && this.#data.options.camera.changed?.oldValue !== this.#data.options.camera.value) {
579
+ camera = this.#data.containers.model.assetContainer?.cameras.find((thisCamera) => thisCamera.name === this.#data.options.camera.changed.oldValue) || this.#data.containers.environment.assetContainer?.cameras.find((thisCamera) => thisCamera.name === his.#data.options.camera.changed.oldValue) || null;
580
+ }
581
+ if (camera){
582
+ camera.metadata = { locked: this.#data.options.camera.changed.oldLocked };
583
+ this.#data.options.camera.value = this.#data.options.camera.changed.oldValue;
584
+ this.#data.options.camera.locked = this.#data.options.camera.changed.oldLocked;
585
+ } else {
586
+ camera = this.#camera;
587
+ this.#data.options.camera.value = null;
588
+ this.#data.options.camera.locked = this.#camera.metadata.locked;
589
+ }
590
+ this.#data.options.camera.changed.success = false;
591
+ } else {
592
+ camera.metadata = { locked: this.#data.options.camera.locked };
550
593
  }
551
-
552
- camera.metadata = { locked: this.#data.options.camera.locked };
553
- if (!this.#data.options.camera.locked) {
594
+ if (!this.#data.options.camera.locked && this.#data.options.camera.value !== null) {
554
595
  camera.attachControl(this.#canvas, true);
555
596
  }
556
597
  this.#scene.activeCamera = camera;
557
- this.#log("info", "setOptionsCamera: activated", { name: camera.name, locked: this.#data.options.camera.locked });
558
-
559
598
  return true;
560
599
  }
561
600
 
@@ -563,7 +602,6 @@ class PrefViewer extends HTMLElement {
563
602
  if (container.assetContainer && !container.visible && container.show) {
564
603
  container.assetContainer.addAllToScene();
565
604
  container.visible = true;
566
- this.#log("debug", "addContainer -> visible", container.name);
567
605
  }
568
606
  }
569
607
 
@@ -571,7 +609,6 @@ class PrefViewer extends HTMLElement {
571
609
  if (container.assetContainer && container.visible) {
572
610
  container.assetContainer.removeAllFromScene();
573
611
  container.visible = false;
574
- this.#log("debug", "removeContainer -> hidden", container.name);
575
612
  }
576
613
  }
577
614
 
@@ -583,24 +620,15 @@ class PrefViewer extends HTMLElement {
583
620
  this.#shadowGen.addShadowCaster(mesh, true);
584
621
  });
585
622
  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
- });
591
623
  }
592
624
 
593
625
  async #loadAssetContainer(container) {
594
626
  let storage = container?.storage;
595
627
 
596
628
  if (!storage) {
597
- this.#log("debug", `loadAssetContainer(${container?.name}): no storage provided`);
598
629
  return false;
599
630
  }
600
631
 
601
- this.#log("groupCollapsed", `loadAssetContainer -> ${container.name}`);
602
- this.#log("info", "storage:", storage);
603
-
604
632
  let source = storage.url || null;
605
633
 
606
634
  if (storage.db && storage.table && storage.id) {
@@ -608,18 +636,13 @@ class PrefViewer extends HTMLElement {
608
636
  const object = await loadModel(storage.id, storage.table);
609
637
  source = object.data;
610
638
  if (object.timestamp === container.timestamp) {
611
- this.#log("info", "IndexedDB source unchanged (timestamp match). Skipping reload.");
612
- console.groupEnd();
613
639
  return false;
614
640
  } else {
615
- container.changed = { timestamp: object.timestamp, size: object.size };
616
- this.#log("info", "IndexedDB source changed:", container.changed);
641
+ container.changed = { timestamp: object.timestamp, size: object.size, success: false };
617
642
  }
618
643
  }
619
644
 
620
645
  if (!source) {
621
- this.#log("warn", "no source (url/db) to load");
622
- console.groupEnd();
623
646
  return false;
624
647
  }
625
648
 
@@ -632,25 +655,19 @@ class PrefViewer extends HTMLElement {
632
655
  });
633
656
  if (!container.changed) {
634
657
  if (container.timestamp === null && container.size === size) {
635
- this.#log("info", "Base64 source unchanged (size match). Skipping reload.");
636
- console.groupEnd();
637
658
  return false;
638
659
  } else {
639
- container.changed = { timestamp: null, size: size };
640
- this.#log("info", "Base64 source changed (size):", container.changed);
660
+ container.changed = { timestamp: null, size: size, success: false };
641
661
  }
642
662
  }
643
663
  } else {
644
664
  const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
645
665
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
646
- const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
647
- if (container.timestamp === fileTimestamp && container.size === fileSize) {
648
- this.#log("info", "URL source unchanged (HEAD size/timestamp). Skipping reload.");
649
- console.groupEnd();
666
+ const [fileSize, fileTimestamp ] = await this.#getServerFileDataHeader(source);
667
+ if (container.size === fileSize && container.timestamp === fileTimestamp) {
650
668
  return false;
651
669
  } else {
652
- container.changed = { timestamp: fileTimestamp, size: fileSize };
653
- this.#log("info", "URL source changed:", container.changed);
670
+ container.changed = { timestamp: fileTimestamp, size: fileSize, success: false };
654
671
  }
655
672
  }
656
673
 
@@ -664,36 +681,35 @@ class PrefViewer extends HTMLElement {
664
681
  },
665
682
  };
666
683
 
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;
684
+ return LoadAssetContainerAsync(file || source, this.#scene, options);
676
685
  }
677
686
 
678
- async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
679
- this.#log("groupCollapsed", "loadContainers flags", { loadModel, loadEnvironment, loadMaterials });
687
+ async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
680
688
  const promiseArray = [];
681
689
  promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
682
690
  promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
683
691
  promiseArray.push(loadMaterials ? this.#loadAssetContainer(this.#data.containers.materials) : false);
684
692
 
693
+ const loadingDetail = {
694
+ model: !!this.#data.containers.model.changed,
695
+ environment: !!this.#data.containers.environment.changed,
696
+ materials: !!this.#data.containers.materials.changed,
697
+ options: {
698
+ camera: !!this.#data.options.camera.changed,
699
+ inneWallMaterial: !!this.#data.options.materials.innerWall.changed,
700
+ outerWallMaterial: !!this.#data.options.materials.outerWall.changed,
701
+ innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed,
702
+ outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed,
703
+ },
704
+ };
705
+ this.#setStatusSceneLoading(loadingDetail);
706
+
685
707
  Promise.allSettled(promiseArray)
686
708
  .then(async (values) => {
687
709
  const modelContainer = values[0];
688
710
  const environmentContainer = values[1];
689
711
  const materialsContainer = values[2];
690
712
 
691
- this.#log("info", "loadContainers results:", {
692
- model: modelContainer?.status,
693
- environment: environmentContainer?.status,
694
- materials: materialsContainer?.status,
695
- });
696
-
697
713
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
698
714
  this.#replaceContainer(this.#data.containers.model, modelContainer.value);
699
715
  this.#storeChangedFlagsForContainer(this.#data.containers.model);
@@ -713,45 +729,45 @@ class PrefViewer extends HTMLElement {
713
729
  this.#storeChangedFlagsForContainer(this.#data.containers.materials);
714
730
  }
715
731
 
716
- const materialsApplied = this.#setOptionsMaterials();
717
- const cameraApplied = this.#setOptionsCamera();
732
+ this.#setOptionsMaterials();
733
+ this.#setOptionsCamera();
718
734
  this.#setVisibilityOfWallAndFloorInModel();
719
-
720
- this.#log("info", "post-load options applied", { materialsApplied, cameraApplied });
735
+
736
+ const loadedDetail = {
737
+ model: !!this.#data.containers.model.changed?.success,
738
+ environment: !!this.#data.containers.environment.changed?.success,
739
+ materials: !!this.#data.containers.materials.changed?.success,
740
+ options: {
741
+ camera: !!this.#data.options.camera.changed?.success,
742
+ inneWallMaterial: !!this.#data.options.materials.innerWall.changed?.success,
743
+ outerWallMaterial: !!this.#data.options.materials.outerWall.changed?.success,
744
+ innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed?.success,
745
+ outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed?.success,
746
+ },
747
+ };
748
+
749
+ this.#setStatusSceneLoaded(loadedDetail);
721
750
 
722
751
  this.#resetChangedFlags();
723
-
724
- this.dispatchEvent(
725
- new CustomEvent("model-loaded", {
726
- detail: { success: "" },
727
- bubbles: true,
728
- composed: true,
729
- })
730
- );
731
- this.#log("info", "model-loaded event dispatched");
732
- console.groupEnd();
733
752
  })
734
753
  .catch((error) => {
754
+ this.loaded = true;
735
755
  console.error("PrefViewer: failed to load model", error);
736
756
  this.dispatchEvent(
737
- new CustomEvent("model-error", {
738
- detail: { error: error },
757
+ new CustomEvent("scene-error", {
739
758
  bubbles: true,
759
+ cancelable: false,
740
760
  composed: true,
761
+ detail: { error: error },
741
762
  })
742
763
  );
743
- this.#log("error", "model-error event dispatched");
744
- console.groupEnd();
745
764
  });
746
765
  }
747
766
 
748
767
  // Public Methods
749
768
  loadConfig(config) {
750
- this.#log("groupCollapsed", "loadConfig called with:", config);
751
769
  config = typeof config === "string" ? JSON.parse(config) : config;
752
770
  if (!config) {
753
- this.#log("warn", "loadConfig: no config provided");
754
- console.groupEnd();
755
771
  return false;
756
772
  }
757
773
 
@@ -762,97 +778,91 @@ class PrefViewer extends HTMLElement {
762
778
  this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
763
779
  this.#data.containers.materials.storage = config.materials?.storage || null;
764
780
 
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
-
771
781
  // Options
772
782
  if (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");
783
+ this.#checkCameraChanged(config.options);
784
+ this.#checkMaterialsChanged(config.options);
778
785
  }
779
786
 
780
- this.#initialized && this.#loadContainers(true, true, true);
781
- console.groupEnd();
787
+ this.initialized && this.#loadContainers(true, true, true);
782
788
  }
783
789
 
784
790
  setOptions(options) {
785
- this.#log("groupCollapsed", "setOptions called with:", options);
786
791
  if (!options) {
787
- this.#log("warn", "setOptions: no options");
788
- console.groupEnd();
789
792
  return false;
790
793
  }
794
+
795
+ const cameraChanged = this.#checkCameraChanged(options);
796
+ const materialsChanged = this.#checkMaterialsChanged(options);
797
+
798
+ const loadingDetail = {
799
+ camera: !!this.#data.options.camera.changed,
800
+ inneWallMaterial: !!this.#data.options.materials.innerWall.changed,
801
+ outerWallMaterial: !!this.#data.options.materials.outerWall.changed,
802
+ innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed,
803
+ outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed,
804
+ };
805
+ this.#setStatusOptionsLoading(loadingDetail);
806
+
791
807
  let someSetted = false;
792
- if (this.#checkCameraChanged(options)) {
808
+ if (cameraChanged) {
793
809
  someSetted = someSetted || this.#setOptionsCamera();
794
810
  }
795
- if (this.#checkMaterialsChanged(options)) {
811
+ if (materialsChanged) {
796
812
  someSetted = someSetted || this.#setOptionsMaterials();
797
813
  }
814
+
815
+ const loadedDetail = {
816
+ camera: !!this.#data.options.camera.changed?.success,
817
+ inneWallMaterial: !!this.#data.options.materials.innerWall.changed?.success,
818
+ outerWallMaterial: !!this.#data.options.materials.outerWall.changed?.success,
819
+ innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed?.success,
820
+ outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed?.success,
821
+ };
822
+ this.#setStatusOptionsLoaded(loadedDetail);
823
+
798
824
  this.#resetChangedFlags();
799
- this.#log("info", "setOptions result:", { applied: someSetted, currentOptions: this.#data.options });
800
- debugger;
801
- console.groupEnd();
825
+
802
826
  return someSetted;
803
827
  }
804
828
 
805
829
  loadModel(model) {
806
- this.#log("groupCollapsed", "loadModel called with:", model);
807
830
  model = typeof model === "string" ? JSON.parse(model) : model;
808
831
  if (!model) {
809
- this.#log("warn", "loadModel: no model object");
810
- console.groupEnd();
811
832
  return false;
812
833
  }
813
834
  this.#data.containers.model.storage = model.storage || null;
814
835
  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);
816
- this.#initialized && this.#loadContainers(true, false, false);
817
- console.groupEnd();
836
+ this.initialized && this.#loadContainers(true, false, false);
818
837
  }
819
838
 
820
839
  loadScene(scene) {
821
- this.#log("groupCollapsed", "loadScene called with:", scene);
822
840
  scene = typeof scene === "string" ? JSON.parse(scene) : scene;
823
841
  if (!scene) {
824
- this.#log("warn", "loadScene: no scene object");
825
- console.groupEnd();
826
842
  return false;
827
843
  }
828
844
  this.#data.containers.environment.storage = scene.storage || null;
829
845
  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);
831
- this.#initialized && this.#loadContainers(false, true, false);
832
- console.groupEnd();
846
+ this.initialized && this.#loadContainers(false, true, false);
833
847
  }
834
848
 
835
849
  showModel() {
836
- this.#log("info", "showModel()");
837
850
  this.#data.containers.model.show = true;
838
851
  this.#addContainer(this.#data.containers.model);
839
852
  }
840
853
 
841
854
  hideModel() {
842
- this.#log("info", "hideModel()");
843
855
  this.#data.containers.model.show = false;
844
856
  this.#removeContainer(this.#data.containers.model);
845
857
  }
846
858
 
847
859
  showScene() {
848
- this.#log("info", "showScene()");
849
860
  this.#data.containers.environment.show = true;
850
861
  this.#addContainer(this.#data.containers.environment);
851
862
  this.#setVisibilityOfWallAndFloorInModel();
852
863
  }
853
864
 
854
865
  hideScene() {
855
- this.#log("info", "hideScene()");
856
866
  this.#data.containers.environment.show = false;
857
867
  this.#removeContainer(this.#data.containers.environment);
858
868
  this.#setVisibilityOfWallAndFloorInModel();