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

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 +192 -10
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.12",
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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * =============================================================================
3
- * PrefViewer Web Component (JavaScript)
3
+ * PrefViewer Web Component (JavaScript) — con logging opcional por atributo
4
4
  * =============================================================================
5
5
  *
6
6
  * Overview
@@ -49,6 +49,76 @@ import { initDb, loadModel } from "./gltf-storage.js";
49
49
  class PrefViewer extends HTMLElement {
50
50
  #initialized = false;
51
51
 
52
+ // --- Logging -------------------------------------------------------------
53
+ #debugEnabled = false;
54
+ #logPrefix = "PrefViewer";
55
+ #timers = new Map();
56
+ #log = {
57
+ debug: () => {},
58
+ info: () => {},
59
+ warn: () => {},
60
+ error: () => {},
61
+ time: () => {},
62
+ timeEnd: () => {},
63
+ group: () => {},
64
+ groupEnd: () => {},
65
+ };
66
+
67
+ #enableDebug(enable) {
68
+ const on = !!enable;
69
+ this.#debugEnabled = on;
70
+ if (!on) {
71
+ this.#log = {
72
+ debug: () => {},
73
+ info: () => {},
74
+ warn: () => {},
75
+ error: () => {},
76
+ time: () => {},
77
+ timeEnd: () => {},
78
+ group: () => {},
79
+ groupEnd: () => {},
80
+ };
81
+ return;
82
+ }
83
+ const tag = (lvl, args) => {
84
+ if (!args || !args.length) return [`${this.#logPrefix}:`];
85
+ const [first, ...rest] = args;
86
+ if (typeof first === "string") return [`${this.#logPrefix} [${lvl}] ${first}`, ...rest];
87
+ return [`${this.#logPrefix} [${lvl}]`, first, ...rest];
88
+ };
89
+ this.#log = {
90
+ debug: (...a) => console.debug(...tag("debug", a)),
91
+ info: (...a) => console.info(...tag("info", a)),
92
+ warn: (...a) => console.warn(...tag("warn", a)),
93
+ error: (...a) => console.error(...tag("error", a)),
94
+ time: (label) => {
95
+ const key = `${label}`;
96
+ this.#timers.set(key, performance.now());
97
+ console.debug(`${this.#logPrefix} [time] ${key} start`);
98
+ },
99
+ timeEnd: (label) => {
100
+ const key = `${label}`;
101
+ if (this.#timers.has(key)) {
102
+ const delta = (performance.now() - this.#timers.get(key)).toFixed(2);
103
+ console.debug(`${this.#logPrefix} [time] ${key} ${delta}ms`);
104
+ this.#timers.delete(key);
105
+ } else {
106
+ console.debug(`${this.#logPrefix} [time] ${key} (no start)`);
107
+ }
108
+ },
109
+ group: (title) => console.group?.(`${this.#logPrefix} ${title}`),
110
+ groupEnd: () => console.groupEnd?.(),
111
+ };
112
+ }
113
+
114
+ #readDebugAttribute() {
115
+ const v = (this.getAttribute("debug") || "").trim().toLowerCase();
116
+ // true/1/yes/on
117
+ const enabled = v === "true" || v === "1" || v === "yes" || v === "on" || v === "debug";
118
+ this.#enableDebug(enabled);
119
+ }
120
+ // ------------------------------------------------------------------------
121
+
52
122
  #data = {
53
123
  containers: {
54
124
  model: {
@@ -130,6 +200,8 @@ class PrefViewer extends HTMLElement {
130
200
  constructor() {
131
201
  super();
132
202
  this.attachShadow({ mode: "open" });
203
+ this.#readDebugAttribute();
204
+ this.#log.info("constructor()");
133
205
  this.#createCanvas();
134
206
  this.#wrapCanvas();
135
207
  // Point to whichever version you packaged or want to use:
@@ -142,13 +214,24 @@ class PrefViewer extends HTMLElement {
142
214
  // JS fallback if WASM isn’t available
143
215
  fallbackUrl: `${DRACO_BASE}/draco_decoder_gltf.js`,
144
216
  };
217
+ this.#log.debug("Draco decoder configured", DracoCompression.Configuration.decoder);
145
218
  }
146
219
 
147
220
  static get observedAttributes() {
148
- return ["config", "model", "scene", "show-model", "show-scene"];
221
+ // Añadimos "debug" como atributo observable
222
+ return ["config", "model", "scene", "show-model", "show-scene", "debug"];
149
223
  }
150
224
 
151
225
  attributeChangedCallback(name, _old, value) {
226
+ // Nota: el cambio de "debug" debe ocurrir antes de loguear otras cosas.
227
+ if (name === "debug") {
228
+ this.#enableDebug((value || "").toLowerCase() === "true" || value === "1" || value === "yes" || value === "on" || value === "debug");
229
+ this.#log.info(`attributeChanged: debug -> ${this.#debugEnabled}`);
230
+ return;
231
+ }
232
+
233
+ this.#log.debug("attributeChangedCallback()", { name, oldValue: _old, value });
234
+
152
235
  let data = null;
153
236
  switch (name) {
154
237
  case "config":
@@ -163,6 +246,7 @@ class PrefViewer extends HTMLElement {
163
246
  case "show-model":
164
247
  data = value.toLowerCase?.() === "true";
165
248
  if (this.#initialized) {
249
+ this.#log.info(`attr show-model -> ${data}`);
166
250
  data ? this.showModel() : this.hideModel();
167
251
  } else {
168
252
  this.#data.containers.model.show = data;
@@ -171,6 +255,7 @@ class PrefViewer extends HTMLElement {
171
255
  case "show-scene":
172
256
  data = value.toLowerCase?.() === "true";
173
257
  if (this.#initialized) {
258
+ this.#log.info(`attr show-scene -> ${data}`);
174
259
  data ? this.showScene() : this.hideScene();
175
260
  } else {
176
261
  this.#data.containers.environment.show = data;
@@ -180,6 +265,7 @@ class PrefViewer extends HTMLElement {
180
265
  }
181
266
 
182
267
  connectedCallback() {
268
+ this.#log.info("connectedCallback()");
183
269
  if (!this.hasAttribute("config")) {
184
270
  const error = 'PrefViewer: provide "models" as array of model and environment';
185
271
  console.error(error);
@@ -196,15 +282,18 @@ class PrefViewer extends HTMLElement {
196
282
  this.#initializeBabylon();
197
283
  this.#loadContainers(true, true, true);
198
284
  this.#initialized = true;
285
+ this.#log.info("initialized = true");
199
286
  }
200
287
 
201
288
  disconnectedCallback() {
289
+ this.#log.info("disconnectedCallback()");
202
290
  this.#disposeEngine();
203
291
  this.#canvasResizeObserver.disconnect();
204
292
  }
205
293
 
206
294
  // Web Component
207
295
  #createCanvas() {
296
+ this.#log.debug("#createCanvas()");
208
297
  this.#canvas = document.createElement("canvas");
209
298
  Object.assign(this.#canvas.style, {
210
299
  width: "100%",
@@ -215,6 +304,7 @@ class PrefViewer extends HTMLElement {
215
304
  }
216
305
 
217
306
  #wrapCanvas() {
307
+ this.#log.debug("#wrapCanvas()");
218
308
  this.#wrapper = document.createElement("div");
219
309
  Object.assign(this.#wrapper.style, {
220
310
  width: "100%",
@@ -230,8 +320,10 @@ class PrefViewer extends HTMLElement {
230
320
  if (!options || !options.camera) {
231
321
  return false;
232
322
  }
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;
323
+ const prev = this.#data.options.camera.value;
324
+ this.#data.options.camera.changed = options.camera && options.camera !== prev ? true : false;
325
+ this.#data.options.camera.value = this.#data.options.camera.changed ? options.camera : prev;
326
+ this.#log.debug("#checkCameraChanged()", { prev, next: this.#data.options.camera.value, changed: this.#data.options.camera.changed });
235
327
  return this.#data.options.camera.changed;
236
328
  }
237
329
 
@@ -242,19 +334,23 @@ class PrefViewer extends HTMLElement {
242
334
  let someChanged = false;
243
335
  Object.keys(this.#data.options.materials).forEach((material) => {
244
336
  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;
337
+ const prev = this.#data.options.materials[material].value;
338
+ this.#data.options.materials[material].changed = options[key] && options[key] !== prev ? true : false;
339
+ this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : prev;
247
340
  someChanged = someChanged || this.#data.options.materials[material].changed;
248
341
  });
342
+ this.#log.debug("#checkMaterialsChanged()", { someChanged, values: this.#data.options.materials });
249
343
  return someChanged;
250
344
  }
251
345
 
252
346
  #storeChangedFlagsForContainer(container) {
347
+ this.#log.debug("#storeChangedFlagsForContainer()", { name: container.name, changed: container.changed });
253
348
  container.timestamp = container.changed.timestamp;
254
349
  container.size = container.changed.size;
255
350
  }
256
351
 
257
352
  #resetChangedFlags() {
353
+ this.#log.debug("#resetChangedFlags()");
258
354
  Object.values(this.#data.containers).forEach((container) => (container.changed = false));
259
355
  Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
260
356
  this.#data.options.camera.changed = false;
@@ -262,6 +358,8 @@ class PrefViewer extends HTMLElement {
262
358
 
263
359
  // Babylon.js
264
360
  async #initializeBabylon() {
361
+ this.#log.group("#initializeBabylon()");
362
+ this.#log.time("babylon:init");
265
363
  this.#engine = new Engine(this.#canvas, true, { alpha: true });
266
364
  this.#scene = new Scene(this.#engine);
267
365
  this.#scene.clearColor = new Color4(1, 1, 1, 1);
@@ -273,9 +371,12 @@ class PrefViewer extends HTMLElement {
273
371
  this.#canvasResizeObserver.observe(this.#canvas);
274
372
 
275
373
  await this.#createXRExperience();
374
+ this.#log.timeEnd("babylon:init");
375
+ this.#log.groupEnd();
276
376
  }
277
377
 
278
378
  addStylesToARButton() {
379
+ this.#log.debug("addStylesToARButton()");
279
380
  const css = '.babylonVRicon { color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-image: url(data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%222048%22%20height%3D%221152%22%20viewBox%3D%220%200%202048%201152%22%20version%3D%221.1%22%3E%3Cpath%20transform%3D%22rotate%28180%201024%2C576.0000000000001%29%22%20d%3D%22m1109%2C896q17%2C0%2030%2C-12t13%2C-30t-12.5%2C-30.5t-30.5%2C-12.5l-170%2C0q-18%2C0%20-30.5%2C12.5t-12.5%2C30.5t13%2C30t30%2C12l170%2C0zm-85%2C256q59%2C0%20132.5%2C-1.5t154.5%2C-5.5t164.5%2C-11.5t163%2C-20t150%2C-30t124.5%2C-41.5q23%2C-11%2042%2C-24t38%2C-30q27%2C-25%2041%2C-61.5t14%2C-72.5l0%2C-257q0%2C-123%20-47%2C-232t-128%2C-190t-190%2C-128t-232%2C-47l-81%2C0q-37%2C0%20-68.5%2C14t-60.5%2C34.5t-55.5%2C45t-53%2C45t-53%2C34.5t-55.5%2C14t-55.5%2C-14t-53%2C-34.5t-53%2C-45t-55.5%2C-45t-60.5%2C-34.5t-68.5%2C-14l-81%2C0q-123%2C0%20-232%2C47t-190%2C128t-128%2C190t-47%2C232l0%2C257q0%2C68%2038%2C115t97%2C73q54%2C24%20124.5%2C41.5t150%2C30t163%2C20t164.5%2C11.5t154.5%2C5.5t132.5%2C1.5zm939%2C-298q0%2C39%20-24.5%2C67t-58.5%2C42q-54%2C23%20-122%2C39.5t-143.5%2C28t-155.5%2C19t-157%2C11t-148.5%2C5t-129.5%2C1.5q-59%2C0%20-130%2C-1.5t-148%2C-5t-157%2C-11t-155.5%2C-19t-143.5%2C-28t-122%2C-39.5q-34%2C-14%20-58.5%2C-42t-24.5%2C-67l0%2C-257q0%2C-106%2040.5%2C-199t110%2C-162.5t162.5%2C-109.5t199%2C-40l81%2C0q27%2C0%2052%2C14t50%2C34.5t51%2C44.5t55.5%2C44.5t63.5%2C34.5t74%2C14t74%2C-14t63.5%2C-34.5t55.5%2C-44.5t51%2C-44.5t50%2C-34.5t52%2C-14l14%2C0q37%2C0%2070%2C0.5t64.5%2C4.5t63.5%2C12t68%2C23q71%2C30%20128.5%2C78.5t98.5%2C110t63.5%2C133.5t22.5%2C149l0%2C257z%22%20fill%3D%22white%22%20/%3E%3C/svg%3E%0A); background-size: 80%; background-repeat:no-repeat; background-position: center; border: none; outline: none; transition: transform 0.125s ease-out } .babylonVRicon:hover { transform: scale(1.05) } .babylonVRicon:active {background-color: rgba(51,51,51,1) } .babylonVRicon:focus {background-color: rgba(51,51,51,1) }.babylonVRicon.vrdisplaypresenting { background-image: none;} .vrdisplaypresenting::after { content: "EXIT"} .xr-error::after { content: "ERROR"}';
280
381
  const style = document.createElement("style");
281
382
  style.appendChild(document.createTextNode(css));
@@ -288,6 +389,7 @@ class PrefViewer extends HTMLElement {
288
389
  }
289
390
 
290
391
  const sessionMode = "immersive-ar";
392
+ this.#log.info("#createXRExperience()", { sessionMode });
291
393
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
292
394
  if (!sessionSupported) {
293
395
  console.info("PrefViewer: WebXR in mode AR is not supported");
@@ -295,6 +397,7 @@ class PrefViewer extends HTMLElement {
295
397
  }
296
398
 
297
399
  try {
400
+ this.#log.time("xr:create");
298
401
  const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
299
402
  ground.isVisible = false;
300
403
 
@@ -318,6 +421,7 @@ class PrefViewer extends HTMLElement {
318
421
  });
319
422
 
320
423
  this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
424
+ this.#log.info("XR onXRReady");
321
425
  // Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
322
426
  this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
323
427
  this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
@@ -325,15 +429,21 @@ class PrefViewer extends HTMLElement {
325
429
  });
326
430
 
327
431
  this.addStylesToARButton();
432
+ this.#log.timeEnd("xr:create");
328
433
  } catch (error) {
329
434
  console.warn("PrefViewer: failed to create WebXR experience", error);
435
+ this.#log.error("XR creation failed", error);
330
436
  this.#XRExperience = null;
331
437
  }
332
438
  }
333
439
 
334
- #canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
440
+ #canvasResizeObserver = new ResizeObserver(() => {
441
+ this.#log.debug("ResizeObserver -> engine.resize()");
442
+ this.#engine && this.#engine.resize();
443
+ });
335
444
 
336
445
  #createCamera() {
446
+ this.#log.debug("#createCamera()");
337
447
  this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
338
448
  this.#camera.upperBetaLimit = Math.PI * 0.48;
339
449
  this.#camera.lowerBetaLimit = Math.PI * 0.25;
@@ -345,6 +455,7 @@ class PrefViewer extends HTMLElement {
345
455
  }
346
456
 
347
457
  #createLights() {
458
+ this.#log.debug("#createLights()");
348
459
  // 1) Stronger ambient fill
349
460
  this.#hemiLight = new HemisphericLight("hemiLight", new Vector3(-10, 10, -10), this.#scene);
350
461
  this.#hemiLight.intensity = 0.6;
@@ -367,12 +478,11 @@ class PrefViewer extends HTMLElement {
367
478
  }
368
479
 
369
480
  #setupInteraction() {
481
+ this.#log.debug("#setupInteraction()");
370
482
  this.#canvas.addEventListener("wheel", (event) => {
371
483
  if (!this.#scene || !this.#camera) {
372
484
  return false;
373
485
  }
374
- //const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
375
- //this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
376
486
  if (!this.#scene.activeCamera.metadata?.locked) {
377
487
  this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
378
488
  }
@@ -382,6 +492,7 @@ class PrefViewer extends HTMLElement {
382
492
 
383
493
  #disposeEngine() {
384
494
  if (!this.#engine) return;
495
+ this.#log.info("#disposeEngine()");
385
496
  this.#engine.dispose();
386
497
  this.#engine = this.#scene = this.#camera = null;
387
498
  this.#hemiLight = this.#dirLight = this.#cameraLight = null;
@@ -390,6 +501,7 @@ class PrefViewer extends HTMLElement {
390
501
 
391
502
  // Utility methods for loading gltf/glb
392
503
  async #getServerFileDataHeader(uri) {
504
+ this.#log.debug("#getServerFileDataHeader()", { uri });
393
505
  return new Promise((resolve) => {
394
506
  const xhr = new XMLHttpRequest();
395
507
  xhr.open("HEAD", uri, true);
@@ -398,12 +510,15 @@ class PrefViewer extends HTMLElement {
398
510
  if (xhr.status === 200) {
399
511
  const size = parseInt(xhr.getResponseHeader("Content-Length"));
400
512
  const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
513
+ this.#log.debug("HEAD ok", { size, timestamp });
401
514
  resolve(size, timestamp);
402
515
  } else {
516
+ this.#log.warn("HEAD failed", { status: xhr.status });
403
517
  resolve(0, null);
404
518
  }
405
519
  };
406
520
  xhr.onerror = () => {
521
+ this.#log.error("HEAD network error");
407
522
  resolve(0, null);
408
523
  };
409
524
  xhr.send();
@@ -411,12 +526,14 @@ class PrefViewer extends HTMLElement {
411
526
  }
412
527
 
413
528
  #transformUrl(url) {
529
+ this.#log.debug("#transformUrl()", { url });
414
530
  return new Promise((resolve) => {
415
531
  resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
416
532
  });
417
533
  }
418
534
 
419
535
  #decodeBase64(base64) {
536
+ this.#log.debug("#decodeBase64()", { length: (base64 || "").length });
420
537
  const [, payload] = base64.split(",");
421
538
  const raw = payload || base64;
422
539
  let decoded = "";
@@ -426,6 +543,7 @@ class PrefViewer extends HTMLElement {
426
543
  try {
427
544
  decoded = atob(raw);
428
545
  } catch {
546
+ this.#log.warn("atob failed (not base64?)");
429
547
  return { blob, extension, size };
430
548
  }
431
549
  let isJson = false;
@@ -441,6 +559,7 @@ class PrefViewer extends HTMLElement {
441
559
  }
442
560
 
443
561
  async #initStorage(db, table) {
562
+ this.#log.debug("#initStorage()", { db, table });
444
563
  if (window.gltfDB && window.gltfDB.name === db && window.gltfDB.objectStoreNames.contains(table)) {
445
564
  return true;
446
565
  }
@@ -449,6 +568,7 @@ class PrefViewer extends HTMLElement {
449
568
 
450
569
  // Methods for managing Asset Containers
451
570
  #setVisibilityOfWallAndFloorInModel(show) {
571
+ this.#log.debug("#setVisibilityOfWallAndFloorInModel()", { show });
452
572
  if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
453
573
  return false;
454
574
  }
@@ -461,9 +581,11 @@ class PrefViewer extends HTMLElement {
461
581
  if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
462
582
  return false;
463
583
  }
584
+ this.#log.debug("#setOptionsMaterial()", optionMaterial);
464
585
 
465
586
  const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
466
587
  if (!material) {
588
+ this.#log.warn("material not found", { wanted: optionMaterial.value });
467
589
  return false;
468
590
  }
469
591
 
@@ -492,6 +614,7 @@ class PrefViewer extends HTMLElement {
492
614
  }
493
615
 
494
616
  #setOptionsMaterials() {
617
+ this.#log.debug("#setOptionsMaterials()");
495
618
  let someSetted = false;
496
619
  Object.values(this.#data.options.materials).forEach((material) => {
497
620
  let settedMaterial = this.#setOptionsMaterial(material);
@@ -501,12 +624,14 @@ class PrefViewer extends HTMLElement {
501
624
  }
502
625
 
503
626
  #setOptionsCamera() {
627
+ this.#log.debug("#setOptionsCamera()", this.#data.options.camera);
504
628
  if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.assetContainer.changed)) {
505
629
  return false;
506
630
  }
507
631
 
508
632
  let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
509
633
  if (!camera) {
634
+ this.#log.warn("camera not found", { wanted: this.#data.options.camera.value });
510
635
  return false;
511
636
  }
512
637
 
@@ -521,6 +646,7 @@ class PrefViewer extends HTMLElement {
521
646
 
522
647
  #addContainer(container) {
523
648
  if (container.assetContainer && !container.visible && container.show) {
649
+ this.#log.debug("#addContainer()", { name: container.name });
524
650
  container.assetContainer.addAllToScene();
525
651
  container.visible = true;
526
652
  }
@@ -528,12 +654,14 @@ class PrefViewer extends HTMLElement {
528
654
 
529
655
  #removeContainer(container) {
530
656
  if (container.assetContainer && container.visible) {
657
+ this.#log.debug("#removeContainer()", { name: container.name });
531
658
  container.assetContainer.removeAllFromScene();
532
659
  container.visible = false;
533
660
  }
534
661
  }
535
662
 
536
663
  #replaceContainer(container, newAssetContainer) {
664
+ this.#log.debug("#replaceContainer()", { name: container.name });
537
665
  this.#removeContainer(container);
538
666
  container.assetContainer = newAssetContainer;
539
667
  container.assetContainer.meshes.forEach((mesh) => {
@@ -544,9 +672,12 @@ class PrefViewer extends HTMLElement {
544
672
  }
545
673
 
546
674
  async #loadAssetContainer(container) {
675
+ this.#log.group(`#loadAssetContainer(${container?.name})`);
547
676
  let storage = container?.storage;
548
677
 
549
678
  if (!storage) {
679
+ this.#log.warn("no storage provided");
680
+ this.#log.groupEnd();
550
681
  return false;
551
682
  }
552
683
 
@@ -557,6 +688,8 @@ class PrefViewer extends HTMLElement {
557
688
  const object = await loadModel(storage.id, storage.table);
558
689
  source = object.data;
559
690
  if (object.timestamp === container.timestamp) {
691
+ this.#log.info("no change from IndexedDB (timestamp match)");
692
+ this.#log.groupEnd();
560
693
  return false;
561
694
  } else {
562
695
  container.changed = { timestamp: object.timestamp, size: object.size };
@@ -564,6 +697,8 @@ class PrefViewer extends HTMLElement {
564
697
  }
565
698
 
566
699
  if (!source) {
700
+ this.#log.warn("no source URL or data");
701
+ this.#log.groupEnd();
567
702
  return false;
568
703
  }
569
704
 
@@ -571,11 +706,14 @@ class PrefViewer extends HTMLElement {
571
706
 
572
707
  let { blob, extension, size } = this.#decodeBase64(source);
573
708
  if (blob && extension) {
709
+ this.#log.debug("source is base64", { extension, size });
574
710
  file = new File([blob], `${container.name}${extension}`, {
575
711
  type: blob.type,
576
712
  });
577
713
  if (!container.changed) {
578
714
  if (container.timestamp === null && container.size === size) {
715
+ this.#log.info("no change (base64 size match)");
716
+ this.#log.groupEnd();
579
717
  return false;
580
718
  } else {
581
719
  container.changed = { timestamp: null, size: size };
@@ -586,6 +724,8 @@ class PrefViewer extends HTMLElement {
586
724
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
587
725
  const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
588
726
  if (container.timestamp === fileTimestamp && container.size === fileSize) {
727
+ this.#log.info("no change (remote HEAD match)");
728
+ this.#log.groupEnd();
589
729
  return false;
590
730
  } else {
591
731
  container.changed = { timestamp: fileTimestamp, size: fileSize };
@@ -602,10 +742,15 @@ class PrefViewer extends HTMLElement {
602
742
  },
603
743
  };
604
744
 
605
- return LoadAssetContainerAsync(file || source, this.#scene, options);
745
+ this.#log.time(`babylon:LoadAssetContainerAsync:${container.name}`);
746
+ const res = LoadAssetContainerAsync(file || source, this.#scene, options);
747
+ this.#log.groupEnd();
748
+ return res;
606
749
  }
607
750
 
608
751
  async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
752
+ this.#log.group("#loadContainers()");
753
+ this.#log.debug("flags", { loadModel, loadEnvironment, loadMaterials });
609
754
  const promiseArray = [];
610
755
  promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
611
756
  promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
@@ -618,22 +763,29 @@ class PrefViewer extends HTMLElement {
618
763
  const materialsContainer = values[2];
619
764
 
620
765
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
766
+ this.#log.timeEnd(`babylon:LoadAssetContainerAsync:model`);
621
767
  this.#replaceContainer(this.#data.containers.model, modelContainer.value);
622
768
  this.#storeChangedFlagsForContainer(this.#data.containers.model);
623
769
  } else {
770
+ this.#log.debug("model container unchanged / not loaded");
624
771
  this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
625
772
  }
626
773
 
627
774
  if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
775
+ this.#log.timeEnd(`babylon:LoadAssetContainerAsync:environment`);
628
776
  this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
629
777
  this.#storeChangedFlagsForContainer(this.#data.containers.environment);
630
778
  } else {
779
+ this.#log.debug("environment container unchanged / not loaded");
631
780
  this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
632
781
  }
633
782
 
634
783
  if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
784
+ this.#log.timeEnd(`babylon:LoadAssetContainerAsync:materials`);
635
785
  this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
636
786
  this.#storeChangedFlagsForContainer(this.#data.containers.materials);
787
+ } else if (loadMaterials) {
788
+ this.#log.debug("materials container unchanged / not loaded");
637
789
  }
638
790
 
639
791
  this.#setOptionsMaterials();
@@ -642,6 +794,7 @@ class PrefViewer extends HTMLElement {
642
794
 
643
795
  this.#resetChangedFlags();
644
796
 
797
+ this.#log.info("dispatch: model-loaded");
645
798
  this.dispatchEvent(
646
799
  new CustomEvent("model-loaded", {
647
800
  detail: { success: "" },
@@ -649,9 +802,11 @@ class PrefViewer extends HTMLElement {
649
802
  composed: true,
650
803
  })
651
804
  );
805
+ this.#log.groupEnd();
652
806
  })
653
807
  .catch((error) => {
654
808
  console.error("PrefViewer: failed to load model", error);
809
+ this.#log.error("failed to load model", error);
655
810
  this.dispatchEvent(
656
811
  new CustomEvent("model-error", {
657
812
  detail: { error: error },
@@ -659,13 +814,17 @@ class PrefViewer extends HTMLElement {
659
814
  composed: true,
660
815
  })
661
816
  );
817
+ this.#log.groupEnd();
662
818
  });
663
819
  }
664
820
 
665
821
  // Public Methods
666
822
  loadConfig(config) {
823
+ this.#log.group("loadConfig()");
667
824
  config = typeof config === "string" ? JSON.parse(config) : config;
668
825
  if (!config) {
826
+ this.#log.warn("no config");
827
+ this.#log.groupEnd();
669
828
  return false;
670
829
  }
671
830
 
@@ -682,11 +841,16 @@ class PrefViewer extends HTMLElement {
682
841
  this.#checkMaterialsChanged(config.options);
683
842
  }
684
843
 
844
+ this.#log.debug("config applied", { containers: this.#data.containers, options: this.#data.options });
685
845
  this.#initialized && this.#loadContainers(true, true, true);
846
+ this.#log.groupEnd();
686
847
  }
687
848
 
688
849
  setOptions(options) {
850
+ this.#log.group("setOptions()");
689
851
  if (!options) {
852
+ this.#log.warn("no options");
853
+ this.#log.groupEnd();
690
854
  return false;
691
855
  }
692
856
  let someSetted = false;
@@ -698,57 +862,73 @@ class PrefViewer extends HTMLElement {
698
862
  }
699
863
  this.#resetChangedFlags();
700
864
  debugger;
865
+ this.#log.debug("setOptions result", { someSetted });
866
+ this.#log.groupEnd();
701
867
  return someSetted;
702
868
  }
703
869
 
704
870
  loadModel(model) {
871
+ this.#log.group("loadModel()");
705
872
  model = typeof model === "string" ? JSON.parse(model) : model;
706
873
  if (!model) {
874
+ this.#log.warn("no model");
875
+ this.#log.groupEnd();
707
876
  return false;
708
877
  }
709
878
  this.#data.containers.model.storage = model.storage || null;
710
879
  this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
711
880
  this.#initialized && this.#loadContainers(true, false, false);
881
+ this.#log.groupEnd();
712
882
  }
713
883
 
714
884
  loadScene(scene) {
885
+ this.#log.group("loadScene()");
715
886
  scene = typeof scene === "string" ? JSON.parse(scene) : scene;
716
887
  if (!scene) {
888
+ this.#log.warn("no scene");
889
+ this.#log.groupEnd();
717
890
  return false;
718
891
  }
719
892
  this.#data.containers.environment.storage = scene.storage || null;
720
893
  this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
721
894
  this.#initialized && this.#loadContainers(false, true, false);
895
+ this.#log.groupEnd();
722
896
  }
723
897
 
724
898
  showModel() {
899
+ this.#log.info("showModel()");
725
900
  this.#data.containers.model.show = true;
726
901
  this.#addContainer(this.#data.containers.model);
727
902
  }
728
903
 
729
904
  hideModel() {
905
+ this.#log.info("hideModel()");
730
906
  this.#data.containers.model.show = false;
731
907
  this.#removeContainer(this.#data.containers.model);
732
908
  }
733
909
 
734
910
  showScene() {
911
+ this.#log.info("showScene()");
735
912
  this.#data.containers.environment.show = true;
736
913
  this.#addContainer(this.#data.containers.environment);
737
914
  this.#setVisibilityOfWallAndFloorInModel();
738
915
  }
739
916
 
740
917
  hideScene() {
918
+ this.#log.info("hideScene()");
741
919
  this.#data.containers.environment.show = false;
742
920
  this.#removeContainer(this.#data.containers.environment);
743
921
  this.#setVisibilityOfWallAndFloorInModel();
744
922
  }
745
923
 
746
924
  downloadModelGLB() {
925
+ this.#log.info("downloadModelGLB()");
747
926
  const fileName = "model";
748
927
  GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
749
928
  }
750
929
 
751
930
  downloadModelUSDZ() {
931
+ this.#log.info("downloadModelUSDZ()");
752
932
  const fileName = "model";
753
933
  USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
754
934
  if (response) {
@@ -758,6 +938,7 @@ class PrefViewer extends HTMLElement {
758
938
  }
759
939
 
760
940
  downloadModelAndSceneUSDZ() {
941
+ this.#log.info("downloadModelAndSceneUSDZ()");
761
942
  const fileName = "scene";
762
943
  USDZExportAsync(this.#scene).then((response) => {
763
944
  if (response) {
@@ -767,6 +948,7 @@ class PrefViewer extends HTMLElement {
767
948
  }
768
949
 
769
950
  downloadModelAndSceneGLB() {
951
+ this.#log.info("downloadModelAndSceneGLB()");
770
952
  const fileName = "scene";
771
953
  GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
772
954
  }