@preference-sl/pref-viewer 2.10.0-beta.12 → 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 -197
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.12",
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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * =============================================================================
3
- * PrefViewer Web Component (JavaScript) — con logging opcional por atributo
3
+ * PrefViewer Web Component (JavaScript)
4
4
  * =============================================================================
5
5
  *
6
6
  * Overview
@@ -49,76 +49,13 @@ 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
- };
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);
112
57
  }
113
58
 
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
-
122
59
  #data = {
123
60
  containers: {
124
61
  model: {
@@ -200,8 +137,6 @@ class PrefViewer extends HTMLElement {
200
137
  constructor() {
201
138
  super();
202
139
  this.attachShadow({ mode: "open" });
203
- this.#readDebugAttribute();
204
- this.#log.info("constructor()");
205
140
  this.#createCanvas();
206
141
  this.#wrapCanvas();
207
142
  // Point to whichever version you packaged or want to use:
@@ -214,24 +149,16 @@ class PrefViewer extends HTMLElement {
214
149
  // JS fallback if WASM isn’t available
215
150
  fallbackUrl: `${DRACO_BASE}/draco_decoder_gltf.js`,
216
151
  };
217
- this.#log.debug("Draco decoder configured", DracoCompression.Configuration.decoder);
152
+ this.#log("info", "constructed");
218
153
  }
219
154
 
220
155
  static get observedAttributes() {
221
- // Añadimos "debug" como atributo observable
222
- return ["config", "model", "scene", "show-model", "show-scene", "debug"];
156
+ return ["config", "model", "scene", "show-model", "show-scene"];
223
157
  }
224
158
 
225
159
  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
-
160
+ this.#log("groupCollapsed", `attributeChanged: ${name}`);
161
+ this.#log("info", "new value:", value);
235
162
  let data = null;
236
163
  switch (name) {
237
164
  case "config":
@@ -246,7 +173,6 @@ class PrefViewer extends HTMLElement {
246
173
  case "show-model":
247
174
  data = value.toLowerCase?.() === "true";
248
175
  if (this.#initialized) {
249
- this.#log.info(`attr show-model -> ${data}`);
250
176
  data ? this.showModel() : this.hideModel();
251
177
  } else {
252
178
  this.#data.containers.model.show = data;
@@ -255,17 +181,16 @@ class PrefViewer extends HTMLElement {
255
181
  case "show-scene":
256
182
  data = value.toLowerCase?.() === "true";
257
183
  if (this.#initialized) {
258
- this.#log.info(`attr show-scene -> ${data}`);
259
184
  data ? this.showScene() : this.hideScene();
260
185
  } else {
261
186
  this.#data.containers.environment.show = data;
262
187
  }
263
188
  break;
264
189
  }
190
+ console.groupEnd();
265
191
  }
266
192
 
267
193
  connectedCallback() {
268
- this.#log.info("connectedCallback()");
269
194
  if (!this.hasAttribute("config")) {
270
195
  const error = 'PrefViewer: provide "models" as array of model and environment';
271
196
  console.error(error);
@@ -279,21 +204,20 @@ class PrefViewer extends HTMLElement {
279
204
  return false;
280
205
  }
281
206
 
207
+ this.#log("info", "connectedCallback: initializing Babylon and starting initial load");
282
208
  this.#initializeBabylon();
283
209
  this.#loadContainers(true, true, true);
284
210
  this.#initialized = true;
285
- this.#log.info("initialized = true");
286
211
  }
287
212
 
288
213
  disconnectedCallback() {
289
- this.#log.info("disconnectedCallback()");
214
+ this.#log("info", "disconnectedCallback: disposing engine and observers");
290
215
  this.#disposeEngine();
291
216
  this.#canvasResizeObserver.disconnect();
292
217
  }
293
218
 
294
219
  // Web Component
295
220
  #createCanvas() {
296
- this.#log.debug("#createCanvas()");
297
221
  this.#canvas = document.createElement("canvas");
298
222
  Object.assign(this.#canvas.style, {
299
223
  width: "100%",
@@ -304,7 +228,6 @@ class PrefViewer extends HTMLElement {
304
228
  }
305
229
 
306
230
  #wrapCanvas() {
307
- this.#log.debug("#wrapCanvas()");
308
231
  this.#wrapper = document.createElement("div");
309
232
  Object.assign(this.#wrapper.style, {
310
233
  width: "100%",
@@ -318,48 +241,51 @@ class PrefViewer extends HTMLElement {
318
241
  // Data
319
242
  #checkCameraChanged(options) {
320
243
  if (!options || !options.camera) {
244
+ this.#log("debug", "checkCameraChanged: no camera in options");
321
245
  return false;
322
246
  }
323
247
  const prev = this.#data.options.camera.value;
248
+ const next = options.camera;
324
249
  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 });
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 });
327
252
  return this.#data.options.camera.changed;
328
253
  }
329
254
 
330
255
  #checkMaterialsChanged(options) {
331
256
  if (!options) {
257
+ this.#log("debug", "checkMaterialsChanged: no options object");
332
258
  return false;
333
259
  }
334
260
  let someChanged = false;
335
261
  Object.keys(this.#data.options.materials).forEach((material) => {
336
262
  const key = `${material}Material`;
337
263
  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;
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;
340
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 });
341
269
  });
342
- this.#log.debug("#checkMaterialsChanged()", { someChanged, values: this.#data.options.materials });
270
+ this.#log("info", "checkMaterialsChanged: someChanged =", someChanged);
343
271
  return someChanged;
344
272
  }
345
273
 
346
274
  #storeChangedFlagsForContainer(container) {
347
- this.#log.debug("#storeChangedFlagsForContainer()", { name: container.name, changed: container.changed });
348
275
  container.timestamp = container.changed.timestamp;
349
276
  container.size = container.changed.size;
277
+ this.#log("debug", "storeChangedFlagsForContainer:", { name: container.name, timestamp: container.timestamp, size: container.size });
350
278
  }
351
279
 
352
280
  #resetChangedFlags() {
353
- this.#log.debug("#resetChangedFlags()");
354
281
  Object.values(this.#data.containers).forEach((container) => (container.changed = false));
355
282
  Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
356
283
  this.#data.options.camera.changed = false;
284
+ this.#log("debug", "resetChangedFlags()");
357
285
  }
358
286
 
359
287
  // Babylon.js
360
288
  async #initializeBabylon() {
361
- this.#log.group("#initializeBabylon()");
362
- this.#log.time("babylon:init");
363
289
  this.#engine = new Engine(this.#canvas, true, { alpha: true });
364
290
  this.#scene = new Scene(this.#engine);
365
291
  this.#scene.clearColor = new Color4(1, 1, 1, 1);
@@ -370,13 +296,11 @@ class PrefViewer extends HTMLElement {
370
296
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
371
297
  this.#canvasResizeObserver.observe(this.#canvas);
372
298
 
299
+ this.#log("info", "Babylon initialized: creating XR experience (if supported)...");
373
300
  await this.#createXRExperience();
374
- this.#log.timeEnd("babylon:init");
375
- this.#log.groupEnd();
376
301
  }
377
302
 
378
303
  addStylesToARButton() {
379
- this.#log.debug("addStylesToARButton()");
380
304
  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"}';
381
305
  const style = document.createElement("style");
382
306
  style.appendChild(document.createTextNode(css));
@@ -389,15 +313,13 @@ class PrefViewer extends HTMLElement {
389
313
  }
390
314
 
391
315
  const sessionMode = "immersive-ar";
392
- this.#log.info("#createXRExperience()", { sessionMode });
393
316
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
394
317
  if (!sessionSupported) {
395
- console.info("PrefViewer: WebXR in mode AR is not supported");
318
+ this.#log("info", "WebXR in mode AR is not supported");
396
319
  return false;
397
320
  }
398
321
 
399
322
  try {
400
- this.#log.time("xr:create");
401
323
  const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
402
324
  ground.isVisible = false;
403
325
 
@@ -421,7 +343,6 @@ class PrefViewer extends HTMLElement {
421
343
  });
422
344
 
423
345
  this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
424
- this.#log.info("XR onXRReady");
425
346
  // Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
426
347
  this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
427
348
  this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
@@ -429,21 +350,16 @@ class PrefViewer extends HTMLElement {
429
350
  });
430
351
 
431
352
  this.addStylesToARButton();
432
- this.#log.timeEnd("xr:create");
353
+ this.#log("info", "WebXR experience created");
433
354
  } catch (error) {
434
355
  console.warn("PrefViewer: failed to create WebXR experience", error);
435
- this.#log.error("XR creation failed", error);
436
356
  this.#XRExperience = null;
437
357
  }
438
358
  }
439
359
 
440
- #canvasResizeObserver = new ResizeObserver(() => {
441
- this.#log.debug("ResizeObserver -> engine.resize()");
442
- this.#engine && this.#engine.resize();
443
- });
360
+ #canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
444
361
 
445
362
  #createCamera() {
446
- this.#log.debug("#createCamera()");
447
363
  this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
448
364
  this.#camera.upperBetaLimit = Math.PI * 0.48;
449
365
  this.#camera.lowerBetaLimit = Math.PI * 0.25;
@@ -452,10 +368,10 @@ class PrefViewer extends HTMLElement {
452
368
  this.#camera.metadata = { locked: false }
453
369
  this.#camera = this.#camera;
454
370
  this.#camera.attachControl(this.#canvas, true);
371
+ this.#log("info", "Default camera created");
455
372
  }
456
373
 
457
374
  #createLights() {
458
- this.#log.debug("#createLights()");
459
375
  // 1) Stronger ambient fill
460
376
  this.#hemiLight = new HemisphericLight("hemiLight", new Vector3(-10, 10, -10), this.#scene);
461
377
  this.#hemiLight.intensity = 0.6;
@@ -475,14 +391,17 @@ class PrefViewer extends HTMLElement {
475
391
  this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
476
392
  this.#cameraLight.parent = this.#camera;
477
393
  this.#cameraLight.intensity = 0.3;
394
+
395
+ this.#log("info", "Lights & shadows configured");
478
396
  }
479
397
 
480
398
  #setupInteraction() {
481
- this.#log.debug("#setupInteraction()");
482
399
  this.#canvas.addEventListener("wheel", (event) => {
483
400
  if (!this.#scene || !this.#camera) {
484
401
  return false;
485
402
  }
403
+ //const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
404
+ //this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
486
405
  if (!this.#scene.activeCamera.metadata?.locked) {
487
406
  this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
488
407
  }
@@ -492,16 +411,15 @@ class PrefViewer extends HTMLElement {
492
411
 
493
412
  #disposeEngine() {
494
413
  if (!this.#engine) return;
495
- this.#log.info("#disposeEngine()");
496
414
  this.#engine.dispose();
497
415
  this.#engine = this.#scene = this.#camera = null;
498
416
  this.#hemiLight = this.#dirLight = this.#cameraLight = null;
499
417
  this.#shadowGen = null;
418
+ this.#log("info", "Engine disposed");
500
419
  }
501
420
 
502
421
  // Utility methods for loading gltf/glb
503
422
  async #getServerFileDataHeader(uri) {
504
- this.#log.debug("#getServerFileDataHeader()", { uri });
505
423
  return new Promise((resolve) => {
506
424
  const xhr = new XMLHttpRequest();
507
425
  xhr.open("HEAD", uri, true);
@@ -510,15 +428,12 @@ class PrefViewer extends HTMLElement {
510
428
  if (xhr.status === 200) {
511
429
  const size = parseInt(xhr.getResponseHeader("Content-Length"));
512
430
  const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
513
- this.#log.debug("HEAD ok", { size, timestamp });
514
431
  resolve(size, timestamp);
515
432
  } else {
516
- this.#log.warn("HEAD failed", { status: xhr.status });
517
433
  resolve(0, null);
518
434
  }
519
435
  };
520
436
  xhr.onerror = () => {
521
- this.#log.error("HEAD network error");
522
437
  resolve(0, null);
523
438
  };
524
439
  xhr.send();
@@ -526,14 +441,12 @@ class PrefViewer extends HTMLElement {
526
441
  }
527
442
 
528
443
  #transformUrl(url) {
529
- this.#log.debug("#transformUrl()", { url });
530
444
  return new Promise((resolve) => {
531
445
  resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
532
446
  });
533
447
  }
534
448
 
535
449
  #decodeBase64(base64) {
536
- this.#log.debug("#decodeBase64()", { length: (base64 || "").length });
537
450
  const [, payload] = base64.split(",");
538
451
  const raw = payload || base64;
539
452
  let decoded = "";
@@ -543,11 +456,10 @@ class PrefViewer extends HTMLElement {
543
456
  try {
544
457
  decoded = atob(raw);
545
458
  } catch {
546
- this.#log.warn("atob failed (not base64?)");
547
459
  return { blob, extension, size };
548
460
  }
549
461
  let isJson = false;
550
- try {
462
+ try {
551
463
  JSON.parse(decoded);
552
464
  isJson = true;
553
465
  } catch {}
@@ -559,7 +471,6 @@ class PrefViewer extends HTMLElement {
559
471
  }
560
472
 
561
473
  async #initStorage(db, table) {
562
- this.#log.debug("#initStorage()", { db, table });
563
474
  if (window.gltfDB && window.gltfDB.name === db && window.gltfDB.objectStoreNames.contains(table)) {
564
475
  return true;
565
476
  }
@@ -568,24 +479,25 @@ class PrefViewer extends HTMLElement {
568
479
 
569
480
  // Methods for managing Asset Containers
570
481
  #setVisibilityOfWallAndFloorInModel(show) {
571
- this.#log.debug("#setVisibilityOfWallAndFloorInModel()", { show });
572
482
  if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
573
483
  return false;
574
484
  }
575
485
  show = show !== undefined ? show : this.#data.containers.environment.visible;
576
486
  const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
577
- 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));
578
490
  }
579
491
 
580
492
  #setOptionsMaterial(optionMaterial) {
581
493
  if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
494
+ this.#log("debug", "setOptionsMaterial: missing data", optionMaterial);
582
495
  return false;
583
496
  }
584
- this.#log.debug("#setOptionsMaterial()", optionMaterial);
585
497
 
586
498
  const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
587
499
  if (!material) {
588
- this.#log.warn("material not found", { wanted: optionMaterial.value });
500
+ this.#log("warn", `setOptionsMaterial: material "${optionMaterial.value}" not found in materials container`);
589
501
  return false;
590
502
  }
591
503
 
@@ -597,41 +509,43 @@ class PrefViewer extends HTMLElement {
597
509
  containers.push(this.#data.containers.environment.assetContainer);
598
510
  }
599
511
  if (containers.length === 0) {
512
+ this.#log("debug", "setOptionsMaterial: no target containers (unchanged)");
600
513
  return false;
601
514
  }
602
515
 
603
- let someSetted = false;
516
+ let count = 0;
604
517
  containers.forEach((container) =>
605
518
  container.meshes
606
519
  .filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix))
607
520
  .forEach((mesh) => {
608
521
  mesh.material = material;
609
- someSetted = true;
522
+ count++;
610
523
  })
611
524
  );
612
525
 
613
- return someSetted;
526
+ this.#log("info", "setOptionsMaterial applied", { prefix: optionMaterial.prefix, material: optionMaterial.value, meshesUpdated: count });
527
+ return count > 0;
614
528
  }
615
529
 
616
530
  #setOptionsMaterials() {
617
- this.#log.debug("#setOptionsMaterials()");
618
531
  let someSetted = false;
619
532
  Object.values(this.#data.options.materials).forEach((material) => {
620
533
  let settedMaterial = this.#setOptionsMaterial(material);
621
534
  someSetted = someSetted || settedMaterial;
622
535
  });
536
+ this.#log("info", "setOptionsMaterials: anyApplied =", someSetted);
623
537
  return someSetted;
624
538
  }
625
539
 
626
540
  #setOptionsCamera() {
627
- this.#log.debug("#setOptionsCamera()", this.#data.options.camera);
628
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)");
629
543
  return false;
630
544
  }
631
545
 
632
546
  let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
633
547
  if (!camera) {
634
- this.#log.warn("camera not found", { wanted: this.#data.options.camera.value });
548
+ this.#log("warn", `setOptionsCamera: camera "${this.#data.options.camera.value}" not found in model`);
635
549
  return false;
636
550
  }
637
551
 
@@ -640,28 +554,28 @@ class PrefViewer extends HTMLElement {
640
554
  camera.attachControl(this.#canvas, true);
641
555
  }
642
556
  this.#scene.activeCamera = camera;
557
+ this.#log("info", "setOptionsCamera: activated", { name: camera.name, locked: this.#data.options.camera.locked });
643
558
 
644
559
  return true;
645
560
  }
646
561
 
647
562
  #addContainer(container) {
648
563
  if (container.assetContainer && !container.visible && container.show) {
649
- this.#log.debug("#addContainer()", { name: container.name });
650
564
  container.assetContainer.addAllToScene();
651
565
  container.visible = true;
566
+ this.#log("debug", "addContainer -> visible", container.name);
652
567
  }
653
568
  }
654
569
 
655
570
  #removeContainer(container) {
656
571
  if (container.assetContainer && container.visible) {
657
- this.#log.debug("#removeContainer()", { name: container.name });
658
572
  container.assetContainer.removeAllFromScene();
659
573
  container.visible = false;
574
+ this.#log("debug", "removeContainer -> hidden", container.name);
660
575
  }
661
576
  }
662
577
 
663
578
  #replaceContainer(container, newAssetContainer) {
664
- this.#log.debug("#replaceContainer()", { name: container.name });
665
579
  this.#removeContainer(container);
666
580
  container.assetContainer = newAssetContainer;
667
581
  container.assetContainer.meshes.forEach((mesh) => {
@@ -669,18 +583,24 @@ class PrefViewer extends HTMLElement {
669
583
  this.#shadowGen.addShadowCaster(mesh, true);
670
584
  });
671
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
+ });
672
591
  }
673
592
 
674
593
  async #loadAssetContainer(container) {
675
- this.#log.group(`#loadAssetContainer(${container?.name})`);
676
594
  let storage = container?.storage;
677
595
 
678
596
  if (!storage) {
679
- this.#log.warn("no storage provided");
680
- this.#log.groupEnd();
597
+ this.#log("debug", `loadAssetContainer(${container?.name}): no storage provided`);
681
598
  return false;
682
599
  }
683
600
 
601
+ this.#log("groupCollapsed", `loadAssetContainer -> ${container.name}`);
602
+ this.#log("info", "storage:", storage);
603
+
684
604
  let source = storage.url || null;
685
605
 
686
606
  if (storage.db && storage.table && storage.id) {
@@ -688,17 +608,18 @@ class PrefViewer extends HTMLElement {
688
608
  const object = await loadModel(storage.id, storage.table);
689
609
  source = object.data;
690
610
  if (object.timestamp === container.timestamp) {
691
- this.#log.info("no change from IndexedDB (timestamp match)");
692
- this.#log.groupEnd();
611
+ this.#log("info", "IndexedDB source unchanged (timestamp match). Skipping reload.");
612
+ console.groupEnd();
693
613
  return false;
694
614
  } else {
695
615
  container.changed = { timestamp: object.timestamp, size: object.size };
616
+ this.#log("info", "IndexedDB source changed:", container.changed);
696
617
  }
697
618
  }
698
619
 
699
620
  if (!source) {
700
- this.#log.warn("no source URL or data");
701
- this.#log.groupEnd();
621
+ this.#log("warn", "no source (url/db) to load");
622
+ console.groupEnd();
702
623
  return false;
703
624
  }
704
625
 
@@ -706,17 +627,17 @@ class PrefViewer extends HTMLElement {
706
627
 
707
628
  let { blob, extension, size } = this.#decodeBase64(source);
708
629
  if (blob && extension) {
709
- this.#log.debug("source is base64", { extension, size });
710
630
  file = new File([blob], `${container.name}${extension}`, {
711
631
  type: blob.type,
712
632
  });
713
633
  if (!container.changed) {
714
634
  if (container.timestamp === null && container.size === size) {
715
- this.#log.info("no change (base64 size match)");
716
- this.#log.groupEnd();
635
+ this.#log("info", "Base64 source unchanged (size match). Skipping reload.");
636
+ console.groupEnd();
717
637
  return false;
718
638
  } else {
719
639
  container.changed = { timestamp: null, size: size };
640
+ this.#log("info", "Base64 source changed (size):", container.changed);
720
641
  }
721
642
  }
722
643
  } else {
@@ -724,11 +645,12 @@ class PrefViewer extends HTMLElement {
724
645
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
725
646
  const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
726
647
  if (container.timestamp === fileTimestamp && container.size === fileSize) {
727
- this.#log.info("no change (remote HEAD match)");
728
- this.#log.groupEnd();
648
+ this.#log("info", "URL source unchanged (HEAD size/timestamp). Skipping reload.");
649
+ console.groupEnd();
729
650
  return false;
730
651
  } else {
731
652
  container.changed = { timestamp: fileTimestamp, size: fileSize };
653
+ this.#log("info", "URL source changed:", container.changed);
732
654
  }
733
655
  }
734
656
 
@@ -742,15 +664,19 @@ class PrefViewer extends HTMLElement {
742
664
  },
743
665
  };
744
666
 
745
- this.#log.time(`babylon:LoadAssetContainerAsync:${container.name}`);
746
- const res = LoadAssetContainerAsync(file || source, this.#scene, options);
747
- this.#log.groupEnd();
748
- return res;
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;
749
676
  }
750
677
 
751
678
  async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
752
- this.#log.group("#loadContainers()");
753
- this.#log.debug("flags", { loadModel, loadEnvironment, loadMaterials });
679
+ this.#log("groupCollapsed", "loadContainers flags", { loadModel, loadEnvironment, loadMaterials });
754
680
  const promiseArray = [];
755
681
  promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
756
682
  promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
@@ -762,39 +688,39 @@ class PrefViewer extends HTMLElement {
762
688
  const environmentContainer = values[1];
763
689
  const materialsContainer = values[2];
764
690
 
691
+ this.#log("info", "loadContainers results:", {
692
+ model: modelContainer?.status,
693
+ environment: environmentContainer?.status,
694
+ materials: materialsContainer?.status,
695
+ });
696
+
765
697
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
766
- this.#log.timeEnd(`babylon:LoadAssetContainerAsync:model`);
767
698
  this.#replaceContainer(this.#data.containers.model, modelContainer.value);
768
699
  this.#storeChangedFlagsForContainer(this.#data.containers.model);
769
700
  } else {
770
- this.#log.debug("model container unchanged / not loaded");
771
701
  this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
772
702
  }
773
703
 
774
704
  if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
775
- this.#log.timeEnd(`babylon:LoadAssetContainerAsync:environment`);
776
705
  this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
777
706
  this.#storeChangedFlagsForContainer(this.#data.containers.environment);
778
707
  } else {
779
- this.#log.debug("environment container unchanged / not loaded");
780
708
  this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
781
709
  }
782
710
 
783
711
  if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
784
- this.#log.timeEnd(`babylon:LoadAssetContainerAsync:materials`);
785
712
  this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
786
713
  this.#storeChangedFlagsForContainer(this.#data.containers.materials);
787
- } else if (loadMaterials) {
788
- this.#log.debug("materials container unchanged / not loaded");
789
714
  }
790
715
 
791
- this.#setOptionsMaterials();
792
- this.#setOptionsCamera();
716
+ const materialsApplied = this.#setOptionsMaterials();
717
+ const cameraApplied = this.#setOptionsCamera();
793
718
  this.#setVisibilityOfWallAndFloorInModel();
794
719
 
720
+ this.#log("info", "post-load options applied", { materialsApplied, cameraApplied });
721
+
795
722
  this.#resetChangedFlags();
796
723
 
797
- this.#log.info("dispatch: model-loaded");
798
724
  this.dispatchEvent(
799
725
  new CustomEvent("model-loaded", {
800
726
  detail: { success: "" },
@@ -802,11 +728,11 @@ class PrefViewer extends HTMLElement {
802
728
  composed: true,
803
729
  })
804
730
  );
805
- this.#log.groupEnd();
731
+ this.#log("info", "model-loaded event dispatched");
732
+ console.groupEnd();
806
733
  })
807
734
  .catch((error) => {
808
735
  console.error("PrefViewer: failed to load model", error);
809
- this.#log.error("failed to load model", error);
810
736
  this.dispatchEvent(
811
737
  new CustomEvent("model-error", {
812
738
  detail: { error: error },
@@ -814,17 +740,18 @@ class PrefViewer extends HTMLElement {
814
740
  composed: true,
815
741
  })
816
742
  );
817
- this.#log.groupEnd();
743
+ this.#log("error", "model-error event dispatched");
744
+ console.groupEnd();
818
745
  });
819
746
  }
820
747
 
821
748
  // Public Methods
822
749
  loadConfig(config) {
823
- this.#log.group("loadConfig()");
750
+ this.#log("groupCollapsed", "loadConfig called with:", config);
824
751
  config = typeof config === "string" ? JSON.parse(config) : config;
825
752
  if (!config) {
826
- this.#log.warn("no config");
827
- this.#log.groupEnd();
753
+ this.#log("warn", "loadConfig: no config provided");
754
+ console.groupEnd();
828
755
  return false;
829
756
  }
830
757
 
@@ -835,22 +762,30 @@ class PrefViewer extends HTMLElement {
835
762
  this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
836
763
  this.#data.containers.materials.storage = config.materials?.storage || null;
837
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
+
838
771
  // Options
839
772
  if (config.options) {
840
- this.#checkCameraChanged(config.options);
841
- 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");
842
778
  }
843
779
 
844
- this.#log.debug("config applied", { containers: this.#data.containers, options: this.#data.options });
845
780
  this.#initialized && this.#loadContainers(true, true, true);
846
- this.#log.groupEnd();
781
+ console.groupEnd();
847
782
  }
848
783
 
849
784
  setOptions(options) {
850
- this.#log.group("setOptions()");
785
+ this.#log("groupCollapsed", "setOptions called with:", options);
851
786
  if (!options) {
852
- this.#log.warn("no options");
853
- this.#log.groupEnd();
787
+ this.#log("warn", "setOptions: no options");
788
+ console.groupEnd();
854
789
  return false;
855
790
  }
856
791
  let someSetted = false;
@@ -861,74 +796,74 @@ class PrefViewer extends HTMLElement {
861
796
  someSetted = someSetted || this.#setOptionsMaterials();
862
797
  }
863
798
  this.#resetChangedFlags();
799
+ this.#log("info", "setOptions result:", { applied: someSetted, currentOptions: this.#data.options });
864
800
  debugger;
865
- this.#log.debug("setOptions result", { someSetted });
866
- this.#log.groupEnd();
801
+ console.groupEnd();
867
802
  return someSetted;
868
803
  }
869
804
 
870
805
  loadModel(model) {
871
- this.#log.group("loadModel()");
806
+ this.#log("groupCollapsed", "loadModel called with:", model);
872
807
  model = typeof model === "string" ? JSON.parse(model) : model;
873
808
  if (!model) {
874
- this.#log.warn("no model");
875
- this.#log.groupEnd();
809
+ this.#log("warn", "loadModel: no model object");
810
+ console.groupEnd();
876
811
  return false;
877
812
  }
878
813
  this.#data.containers.model.storage = model.storage || null;
879
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);
880
816
  this.#initialized && this.#loadContainers(true, false, false);
881
- this.#log.groupEnd();
817
+ console.groupEnd();
882
818
  }
883
819
 
884
820
  loadScene(scene) {
885
- this.#log.group("loadScene()");
821
+ this.#log("groupCollapsed", "loadScene called with:", scene);
886
822
  scene = typeof scene === "string" ? JSON.parse(scene) : scene;
887
823
  if (!scene) {
888
- this.#log.warn("no scene");
889
- this.#log.groupEnd();
824
+ this.#log("warn", "loadScene: no scene object");
825
+ console.groupEnd();
890
826
  return false;
891
827
  }
892
828
  this.#data.containers.environment.storage = scene.storage || null;
893
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);
894
831
  this.#initialized && this.#loadContainers(false, true, false);
895
- this.#log.groupEnd();
832
+ console.groupEnd();
896
833
  }
897
834
 
898
835
  showModel() {
899
- this.#log.info("showModel()");
836
+ this.#log("info", "showModel()");
900
837
  this.#data.containers.model.show = true;
901
838
  this.#addContainer(this.#data.containers.model);
902
839
  }
903
840
 
904
841
  hideModel() {
905
- this.#log.info("hideModel()");
842
+ this.#log("info", "hideModel()");
906
843
  this.#data.containers.model.show = false;
907
844
  this.#removeContainer(this.#data.containers.model);
908
845
  }
909
846
 
910
847
  showScene() {
911
- this.#log.info("showScene()");
848
+ this.#log("info", "showScene()");
912
849
  this.#data.containers.environment.show = true;
913
850
  this.#addContainer(this.#data.containers.environment);
914
851
  this.#setVisibilityOfWallAndFloorInModel();
915
852
  }
916
853
 
917
854
  hideScene() {
918
- this.#log.info("hideScene()");
855
+ this.#log("info", "hideScene()");
919
856
  this.#data.containers.environment.show = false;
920
857
  this.#removeContainer(this.#data.containers.environment);
921
858
  this.#setVisibilityOfWallAndFloorInModel();
922
859
  }
923
860
 
924
861
  downloadModelGLB() {
925
- this.#log.info("downloadModelGLB()");
926
862
  const fileName = "model";
927
863
  GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
928
864
  }
929
865
 
930
866
  downloadModelUSDZ() {
931
- this.#log.info("downloadModelUSDZ()");
932
867
  const fileName = "model";
933
868
  USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
934
869
  if (response) {
@@ -938,7 +873,6 @@ class PrefViewer extends HTMLElement {
938
873
  }
939
874
 
940
875
  downloadModelAndSceneUSDZ() {
941
- this.#log.info("downloadModelAndSceneUSDZ()");
942
876
  const fileName = "scene";
943
877
  USDZExportAsync(this.#scene).then((response) => {
944
878
  if (response) {
@@ -948,7 +882,6 @@ class PrefViewer extends HTMLElement {
948
882
  }
949
883
 
950
884
  downloadModelAndSceneGLB() {
951
- this.#log.info("downloadModelAndSceneGLB()");
952
885
  const fileName = "scene";
953
886
  GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
954
887
  }