@preference-sl/pref-viewer 2.10.0-beta.21 → 2.10.0-beta.22

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 +201 -252
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.21",
3
+ "version": "2.10.0-beta.22",
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
@@ -39,23 +39,7 @@
39
39
  * </pref-viewer>
40
40
  * ```
41
41
  */
42
- import {
43
- Engine,
44
- Scene,
45
- ArcRotateCamera,
46
- Vector3,
47
- Color4,
48
- HemisphericLight,
49
- DirectionalLight,
50
- PointLight,
51
- ShadowGenerator,
52
- LoadAssetContainerAsync,
53
- Tools,
54
- WebXRSessionManager,
55
- WebXRDefaultExperience,
56
- MeshBuilder,
57
- WebXRFeatureName,
58
- } from "@babylonjs/core";
42
+ import { Engine, Scene, ArcRotateCamera, Vector3, Color4, HemisphericLight, DirectionalLight, PointLight, ShadowGenerator, LoadAssetContainerAsync, Tools, WebXRSessionManager, WebXRDefaultExperience, MeshBuilder, WebXRFeatureName } from "@babylonjs/core";
59
43
  import "@babylonjs/loaders";
60
44
  import { USDZExportAsync, GLTF2Export } from "@babylonjs/serializers";
61
45
  import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
@@ -145,45 +129,15 @@ class PrefViewer extends HTMLElement {
145
129
  #shadowGen = null;
146
130
  #XRExperience = null;
147
131
 
148
- // -------------------------
149
- // Logger util + timers
150
- // -------------------------
151
- #LOG_LEVEL = (window?.PREFV_LOG_LEVEL ?? "debug"); // 'debug' | 'info' | 'warn' | 'error'
152
- #timeMarks = new Map();
153
-
154
- #log(level, msg, extra) {
155
- const order = { debug: 0, info: 1, warn: 2, error: 3 };
156
- const cur = order[this.#LOG_LEVEL] ?? 0;
157
- const now = new Date().toISOString();
158
- if ((order[level] ?? 0) < cur) return;
159
- const line = `[PrefViewer][${level.toUpperCase()}][${now}] ${msg}`;
160
- try {
161
- if (extra !== undefined) console[level](line, extra);
162
- else console[level](line);
163
- } catch {
164
- console.log(line, extra);
165
- }
166
- }
167
- #timeStart(label) {
168
- this.#timeMarks.set(label, performance.now());
169
- this.#log("debug", `⏱️ start ${label}`);
170
- }
171
- #timeEnd(label) {
172
- const t0 = this.#timeMarks.get(label);
173
- if (t0 !== undefined) {
174
- const dt = (performance.now() - t0).toFixed(1);
175
- this.#log("debug", `⏱️ end ${label} +${dt}ms`);
176
- this.#timeMarks.delete(label);
177
- }
178
- }
179
-
180
132
  constructor() {
181
133
  super();
134
+ console.log("PrefViewer: constructor()");
182
135
  this.attachShadow({ mode: "open" });
183
136
  this.#createCanvas();
184
137
  this.#wrapCanvas();
185
138
  // Point to whichever version you packaged or want to use:
186
139
  const DRACO_BASE = "https://www.gstatic.com/draco/versioned/decoders/1.5.7";
140
+ console.log("PrefViewer: DRACO config base =", DRACO_BASE);
187
141
  DracoCompression.Configuration.decoder = {
188
142
  // loader for the “wrapper” that pulls in the real WASM
189
143
  wasmUrl: `${DRACO_BASE}/draco_wasm_wrapper_gltf.js`,
@@ -199,7 +153,7 @@ class PrefViewer extends HTMLElement {
199
153
  }
200
154
 
201
155
  attributeChangedCallback(name, _old, value) {
202
- this.#log("debug", `attributeChangedCallback: ${name}`, value);
156
+ console.log("PrefViewer: attributeChangedCallback()", { name, old: _old, value });
203
157
  let data = null;
204
158
  switch (name) {
205
159
  case "config":
@@ -214,6 +168,7 @@ class PrefViewer extends HTMLElement {
214
168
  case "show-model":
215
169
  data = value.toLowerCase?.() === "true";
216
170
  if (this.initialized) {
171
+ console.log("PrefViewer: toggling model visibility (attr)", data);
217
172
  data ? this.showModel() : this.hideModel();
218
173
  } else {
219
174
  this.#data.containers.model.show = data;
@@ -222,6 +177,7 @@ class PrefViewer extends HTMLElement {
222
177
  case "show-scene":
223
178
  data = value.toLowerCase?.() === "true";
224
179
  if (this.initialized) {
180
+ console.log("PrefViewer: toggling scene visibility (attr)", data);
225
181
  data ? this.showScene() : this.hideScene();
226
182
  } else {
227
183
  this.#data.containers.environment.show = data;
@@ -231,10 +187,9 @@ class PrefViewer extends HTMLElement {
231
187
  }
232
188
 
233
189
  connectedCallback() {
234
- this.#log("info", "connectedCallback");
190
+ console.log("PrefViewer: connectedCallback()");
235
191
  if (!this.hasAttribute("config")) {
236
192
  const error = 'PrefViewer: provide "models" as array of model and environment';
237
- this.#log("error", error);
238
193
  console.error(error);
239
194
  this.dispatchEvent(
240
195
  new CustomEvent("scene-error", {
@@ -249,17 +204,19 @@ class PrefViewer extends HTMLElement {
249
204
 
250
205
  this.#initializeBabylon();
251
206
  this.initialized = true;
207
+ console.log("PrefViewer: initialized = true, loading containers…");
252
208
  this.#loadContainers(true, true, true);
253
209
  }
254
210
 
255
211
  disconnectedCallback() {
256
- this.#log("info", "disconnectedCallback → dispose engine");
212
+ console.log("PrefViewer: disconnectedCallback()");
257
213
  this.#disposeEngine();
258
214
  this.#canvasResizeObserver.disconnect();
259
215
  }
260
216
 
261
217
  // Web Component
262
218
  #createCanvas() {
219
+ console.log("PrefViewer: #createCanvas()");
263
220
  this.#canvas = document.createElement("canvas");
264
221
  Object.assign(this.#canvas.style, {
265
222
  width: "100%",
@@ -270,6 +227,7 @@ class PrefViewer extends HTMLElement {
270
227
  }
271
228
 
272
229
  #wrapCanvas() {
230
+ console.log("PrefViewer: #wrapCanvas()");
273
231
  this.#wrapper = document.createElement("div");
274
232
  Object.assign(this.#wrapper.style, {
275
233
  width: "100%",
@@ -281,13 +239,13 @@ class PrefViewer extends HTMLElement {
281
239
  }
282
240
 
283
241
  #setStatusSceneLoading() {
242
+ console.log("PrefViewer: #setStatusSceneLoading()");
284
243
  this.loaded = false;
285
244
  this.loading = true;
286
245
  if (this.hasAttribute("loaded")) {
287
246
  this.removeAttribute("loaded");
288
247
  }
289
248
  this.setAttribute("loading", "");
290
- this.#log("info", "Escena → loading");
291
249
  this.dispatchEvent(
292
250
  new CustomEvent("scene-loading", {
293
251
  bubbles: true,
@@ -298,6 +256,7 @@ class PrefViewer extends HTMLElement {
298
256
  }
299
257
 
300
258
  #setStatusSceneLoaded() {
259
+ console.log("PrefViewer: #setStatusSceneLoaded()");
301
260
  this.loaded = true;
302
261
  this.loading = false;
303
262
 
@@ -331,7 +290,6 @@ class PrefViewer extends HTMLElement {
331
290
  this.removeAttribute("loading");
332
291
  }
333
292
  this.setAttribute("loaded", "");
334
- this.#log("info", "Escena → loaded", detail);
335
293
  this.dispatchEvent(
336
294
  new CustomEvent("scene-loaded", {
337
295
  bubbles: true,
@@ -340,10 +298,11 @@ class PrefViewer extends HTMLElement {
340
298
  detail: detail,
341
299
  })
342
300
  );
301
+ console.log("PrefViewer: scene-loaded detail =", detail);
343
302
  }
344
303
 
345
304
  #setStatusOptionsLoading() {
346
- this.#log("info", "Opciones → loading");
305
+ console.log("PrefViewer: #setStatusOptionsLoading()");
347
306
  this.dispatchEvent(
348
307
  new CustomEvent("options-loading", {
349
308
  bubbles: true,
@@ -354,6 +313,7 @@ class PrefViewer extends HTMLElement {
354
313
  }
355
314
 
356
315
  #setStatusOptionsLoaded() {
316
+ console.log("PrefViewer: #setStatusOptionsLoaded()");
357
317
  const toLoadDetail = {
358
318
  innerWallMaterial: !!this.#data.options.materials.innerWall.changed,
359
319
  outerWallMaterial: !!this.#data.options.materials.outerWall.changed,
@@ -372,7 +332,6 @@ class PrefViewer extends HTMLElement {
372
332
  success: loadedDetail,
373
333
  };
374
334
 
375
- this.#log("info", "Opciones → loaded", detail);
376
335
  this.dispatchEvent(
377
336
  new CustomEvent("options-loaded", {
378
337
  bubbles: true,
@@ -381,6 +340,7 @@ class PrefViewer extends HTMLElement {
381
340
  detail: detail,
382
341
  })
383
342
  );
343
+ console.log("PrefViewer: options-loaded detail =", detail);
384
344
  }
385
345
 
386
346
  // Data
@@ -389,40 +349,38 @@ class PrefViewer extends HTMLElement {
389
349
  return false;
390
350
  }
391
351
  const changed = options.camera !== this.#data.options.camera.value;
392
- this.#data.options.camera.changed = changed
393
- ? { oldValue: this.#data.options.camera.value, oldLocked: this.#data.options.camera.locked, success: false }
394
- : false;
352
+ console.log("PrefViewer: #checkCameraChanged()", { incoming: options.camera, previous: this.#data.options.camera.value, changed });
353
+ this.#data.options.camera.changed = changed ? { oldValue: this.#data.options.camera.value, oldLocked: this.#data.options.camera.locked, success: false } : false;
395
354
  if (changed) this.#data.options.camera.value = options.camera;
396
- this.#log("debug", `#checkCameraChanged → ${changed}`, {
397
- new: this.#data.options.camera.value,
398
- old: this.#data.options.camera.changed?.oldValue ?? null,
399
- });
355
+
400
356
  return changed;
401
357
  }
402
358
 
403
359
  #checkMaterialsChanged(options) {
404
- if (!options) return false;
360
+ if (!options) {
361
+ return false;
362
+ }
405
363
  let someChanged = false;
406
364
  Object.keys(this.#data.options.materials).forEach((material) => {
407
365
  const key = `${material}Material`;
408
366
  const materialChanged = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
409
- this.#data.options.materials[material].changed = materialChanged
410
- ? { oldValue: this.#data.options.materials[material].value, success: false }
411
- : false;
367
+ console.log("PrefViewer: #checkMaterialsChanged()", { key, incoming: options[key], previous: this.#data.options.materials[material].value, materialChanged });
368
+ this.#data.options.materials[material].changed = materialChanged ? { oldValue: this.#data.options.materials[material].value, success: false } : false;
412
369
  this.#data.options.materials[material].value = materialChanged ? options[key] : this.#data.options.materials[material].value;
413
370
  someChanged = someChanged || this.#data.options.materials[material].changed;
414
371
  });
415
- this.#log("debug", `#checkMaterialsChanged → ${!!someChanged}`);
416
372
  return someChanged;
417
373
  }
418
374
 
419
375
  #storeChangedFlagsForContainer(container) {
376
+ console.log("PrefViewer: #storeChangedFlagsForContainer()", { name: container.name, changed: container.changed });
420
377
  container.timeStamp = container.changed.timeStamp;
421
378
  container.size = container.changed.size;
422
379
  container.changed.success = true;
423
380
  }
424
381
 
425
382
  #resetChangedFlags() {
383
+ console.log("PrefViewer: #resetChangedFlags()");
426
384
  Object.values(this.#data.containers).forEach((container) => (container.changed = false));
427
385
  Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
428
386
  this.#data.options.camera.changed = false;
@@ -430,47 +388,52 @@ class PrefViewer extends HTMLElement {
430
388
 
431
389
  // Babylon.js
432
390
  async #initializeBabylon() {
433
- this.#timeStart("initializeBabylon");
391
+ console.log("PrefViewer: #initializeBabylon() START");
434
392
  this.#engine = new Engine(this.#canvas, true, { alpha: true });
435
- this.#engine.disableUniformBuffers = true; // evita GL_MAX_*_UNIFORM_BUFFERS (workaround)
393
+ this.#engine.disableUniformBuffers = true; // <- evita el límite de GL_MAX_*_UNIFORM_BUFFERS // PROVISIONAL, YA QUE ESTO ES UN POCO OVERKILL
394
+ console.log("PrefViewer: Engine created", { disableUBO: this.#engine.disableUniformBuffers });
395
+
436
396
  this.#scene = new Scene(this.#engine);
437
397
  this.#scene.clearColor = new Color4(1, 1, 1, 1);
398
+ console.log("PrefViewer: Scene created, clearColor set to white");
399
+
438
400
  this.#createCamera();
439
- this.#createLights(); // luces desactivadas
401
+ this.#createLights();
440
402
  this.#setupInteraction();
441
403
 
442
- this.#log("info", "Engine y Scene creados", {
443
- webgl: this.#engine.webGLVersion,
444
- disableUBO: this.#engine.disableUniformBuffers,
445
- });
446
-
447
404
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
405
+ console.log("PrefViewer: runRenderLoop started");
448
406
  this.#canvasResizeObserver.observe(this.#canvas);
407
+ console.log("PrefViewer: ResizeObserver attached");
449
408
 
450
409
  await this.#createXRExperience();
451
- this.#timeEnd("initializeBabylon");
410
+ console.log("PrefViewer: #initializeBabylon() END");
452
411
  }
453
412
 
454
413
  addStylesToARButton() {
455
- const css =
456
- '.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"}';
414
+ console.log("PrefViewer: addStylesToARButton()");
415
+ 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"}';
457
416
  const style = document.createElement("style");
458
417
  style.appendChild(document.createTextNode(css));
459
418
  this.#wrapper.appendChild(style);
460
419
  }
461
420
 
462
421
  async #createXRExperience() {
463
- if (this.#XRExperience) return true;
422
+ console.log("PrefViewer: #createXRExperience() START");
423
+ if (this.#XRExperience) {
424
+ console.log("PrefViewer: XR already exists, skipping.");
425
+ return true;
426
+ }
464
427
 
465
428
  const sessionMode = "immersive-ar";
466
429
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
430
+ console.log("PrefViewer: WebXR session supported =", sessionSupported);
467
431
  if (!sessionSupported) {
468
- this.#log("info", `WebXR no soportado para ${sessionMode}`);
432
+ console.info("PrefViewer: WebXR in mode AR is not supported");
469
433
  return false;
470
434
  }
471
435
 
472
436
  try {
473
- this.#log("debug", "Creando XR DefaultExperience…");
474
437
  const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
475
438
  ground.isVisible = false;
476
439
 
@@ -485,7 +448,7 @@ class PrefViewer extends HTMLElement {
485
448
  };
486
449
 
487
450
  this.#XRExperience = await WebXRDefaultExperience.CreateAsync(this.#scene, options);
488
- this.#log("info", "XR DefaultExperience creado");
451
+ console.log("PrefViewer: XR experience created");
489
452
 
490
453
  const featuresManager = this.#XRExperience.baseExperience.featuresManager;
491
454
  featuresManager.enableFeature(WebXRFeatureName.TELEPORTATION, "stable", {
@@ -495,75 +458,98 @@ class PrefViewer extends HTMLElement {
495
458
  });
496
459
 
497
460
  this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
461
+ console.log("PrefViewer: onXRReady - syncing camera pose");
498
462
  // Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
499
- this.#log("debug", "XR Ready → copiando pose inicial a xrCamera");
500
- this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(
501
- this.#XRExperience.baseExperience._nonVRCamera
502
- );
463
+ this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
503
464
  this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
504
- this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(
505
- this.#XRExperience.baseExperience.camera
506
- );
465
+ this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(this.#XRExperience.baseExperience.camera);
507
466
  });
508
467
 
509
468
  this.addStylesToARButton();
510
469
  } catch (error) {
511
- this.#log("warn", "Falló la creación de la experiencia WebXR", error);
470
+ console.warn("PrefViewer: failed to create WebXR experience", error);
512
471
  this.#XRExperience = null;
513
472
  }
473
+ console.log("PrefViewer: #createXRExperience() END");
514
474
  }
515
475
 
516
476
  #canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
517
477
 
518
478
  #createCamera() {
479
+ console.log("PrefViewer: #createCamera()");
519
480
  this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
520
481
  this.#camera.upperBetaLimit = Math.PI * 0.48;
521
482
  this.#camera.lowerBetaLimit = Math.PI * 0.25;
522
483
  this.#camera.lowerRadiusLimit = 5;
523
484
  this.#camera.upperRadiusLimit = 20;
524
- this.#camera.metadata = { locked: false };
485
+ this.#camera.metadata = { locked: false }
525
486
  this.#camera.attachControl(this.#canvas, true);
526
487
  this.#scene.activeCamera = this.#camera;
527
- this.#log("debug", "Cámara creada y asignada como activa", {
528
- name: this.#camera.name,
529
- locked: this.#camera.metadata.locked,
488
+ console.log("PrefViewer: camera configured", {
489
+ upperBetaLimit: this.#camera.upperBetaLimit,
490
+ lowerBetaLimit: this.#camera.lowerBetaLimit,
491
+ lowerRadiusLimit: this.#camera.lowerRadiusLimit,
492
+ upperRadiusLimit: this.#camera.upperRadiusLimit
530
493
  });
531
494
  }
532
495
 
533
- // [LIGHTS OFF] — sin luces del componente
534
496
  #createLights() {
535
- this.#hemiLight = null;
536
- this.#dirLight = null;
537
- this.#cameraLight = null;
538
- this.#shadowGen = null;
539
- this.#log("info", "Luces internas desactivadas (#createLights). No se crean sombras.");
497
+ console.log("PrefViewer: #createLights()");
498
+ // 1) Stronger ambient fill
499
+ this.#hemiLight = new HemisphericLight("hemiLight", new Vector3(-10, 10, -10), this.#scene);
500
+ this.#hemiLight.intensity = 0.6;
501
+
502
+ // 2) Directional light from the front-right, angled slightly down
503
+ this.#dirLight = new DirectionalLight("dirLight", new Vector3(-10, 10, -10), this.#scene);
504
+ this.#dirLight.position = new Vector3(5, 4, 5); // light is IN FRONT + ABOVE + to the RIGHT
505
+ this.#dirLight.intensity = 0.6;
506
+
507
+ // 3) Soft shadows
508
+ this.#shadowGen = new ShadowGenerator(1024, this.#dirLight);
509
+ this.#shadowGen.useBlurExponentialShadowMap = true;
510
+ this.#shadowGen.blurKernel = 16;
511
+ this.#shadowGen.darkness = 0.5;
512
+
513
+ // 4) Camera‐attached headlight
514
+ this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
515
+ this.#cameraLight.parent = this.#camera;
516
+ this.#cameraLight.intensity = 0.3;
517
+
518
+ console.log("PrefViewer: lights created", {
519
+ hemiIntensity: this.#hemiLight.intensity,
520
+ dirIntensity: this.#dirLight.intensity,
521
+ shadowMapSize: 1024,
522
+ pointIntensity: this.#cameraLight.intensity
523
+ });
540
524
  }
541
525
 
542
526
  #setupInteraction() {
527
+ console.log("PrefViewer: #setupInteraction()");
543
528
  this.#canvas.addEventListener("wheel", (event) => {
544
- if (!this.#scene || !this.#camera) return false;
529
+ if (!this.#scene || !this.#camera) {
530
+ return false;
531
+ }
545
532
  if (!this.#scene.activeCamera.metadata?.locked) {
546
- this.#scene.activeCamera.inertialRadiusOffset -=
547
- event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
533
+ this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
548
534
  }
549
535
  event.preventDefault();
550
536
  });
551
537
  }
552
538
 
553
539
  #disposeEngine() {
540
+ console.log("PrefViewer: #disposeEngine()");
554
541
  if (!this.#engine) return;
555
542
  this.#shadowGen?.dispose();
556
- this.#scene?.lights?.slice().forEach((l) => l.dispose());
543
+ this.#scene?.lights?.slice().forEach(l => l.dispose());
557
544
  this.#engine.dispose();
558
545
  this.#engine = this.#scene = this.#camera = null;
559
546
  this.#hemiLight = this.#dirLight = this.#cameraLight = null;
560
547
  this.#shadowGen = null;
561
- this.#log("info", "Engine y recursos Babylon eliminados");
562
548
  }
563
549
 
564
550
  // Utility methods for loading gltf/glb
565
551
  async #getServerFileDataHeader(uri) {
566
- this.#log("debug", `HEAD ${uri}`);
552
+ console.log("PrefViewer: #getServerFileDataHeader()", uri);
567
553
  return new Promise((resolve) => {
568
554
  const xhr = new XMLHttpRequest();
569
555
  xhr.open("HEAD", uri, true);
@@ -572,15 +558,15 @@ class PrefViewer extends HTMLElement {
572
558
  if (xhr.status === 200) {
573
559
  const size = parseInt(xhr.getResponseHeader("Content-Length"));
574
560
  const timeStamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
575
- this.#log("debug", "HEAD ok", { size, timeStamp });
561
+ console.log("PrefViewer: HEAD ok", { size, timeStamp });
576
562
  resolve([size, timeStamp]);
577
563
  } else {
578
- this.#log("warn", "HEAD non-200", { status: xhr.status });
564
+ console.warn("PrefViewer: HEAD failed", xhr.status);
579
565
  resolve([0, null]);
580
566
  }
581
567
  };
582
568
  xhr.onerror = () => {
583
- this.#log("warn", "HEAD error");
569
+ console.warn("PrefViewer: HEAD network error");
584
570
  resolve([0, null]);
585
571
  };
586
572
  xhr.send();
@@ -589,12 +575,14 @@ class PrefViewer extends HTMLElement {
589
575
 
590
576
  #transformUrl(url) {
591
577
  return new Promise((resolve) => {
592
- resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
578
+ const transformed = url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/");
579
+ console.log("PrefViewer: #transformUrl()", { in: url, out: transformed });
580
+ resolve(transformed);
593
581
  });
594
582
  }
595
583
 
596
584
  #decodeBase64(base64) {
597
- this.#log("debug", "Decodificando Base64…");
585
+ console.log("PrefViewer: #decodeBase64() START");
598
586
  const [, payload] = base64.split(",");
599
587
  const raw = payload || base64;
600
588
  let decoded = "";
@@ -604,73 +592,67 @@ class PrefViewer extends HTMLElement {
604
592
  try {
605
593
  decoded = atob(raw);
606
594
  } catch {
607
- this.#log("warn", "Base64 inválido (atob falló)");
595
+ console.warn("PrefViewer: base64 decode failed (not base64?)");
608
596
  return { blob, extension, size };
609
597
  }
610
598
  let isJson = false;
611
599
  try {
612
600
  JSON.parse(decoded);
613
601
  isJson = true;
614
- } catch {}
602
+ } catch { }
615
603
  extension = isJson ? ".gltf" : ".glb";
616
604
  const type = isJson ? "model/gltf+json" : "model/gltf-binary";
617
605
  const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
618
606
  blob = new Blob([array], { type });
619
- this.#log("debug", "Base64 decodificado", { extension, size });
607
+ console.log("PrefViewer: #decodeBase64() END", { extension, size, type });
620
608
  return { blob, extension, size };
621
609
  }
622
610
 
623
611
  async #initStorage(db, table) {
612
+ console.log("PrefViewer: #initStorage()", { db, table });
624
613
  if (window.gltfDB && window.gltfDB.name === db && window.gltfDB.objectStoreNames.contains(table)) {
614
+ console.log("PrefViewer: existing DB connection reused");
625
615
  return true;
626
616
  }
627
617
  await initDb(db, table);
628
- this.#log("debug", "IndexedDB inicializado/abierto", { db, table });
618
+ console.log("PrefViewer: DB initialized");
629
619
  }
630
620
 
631
621
  // Methods for managing Asset Containers
632
622
  #setVisibilityOfWallAndFloorInModel(show) {
623
+ console.log("PrefViewer: #setVisibilityOfWallAndFloorInModel()", { incomingShow: show });
633
624
  if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
625
+ console.log("PrefViewer: no model assetContainer or not visible, skip visibility set");
634
626
  return false;
635
627
  }
636
628
  show = show !== undefined ? show : this.#data.containers.environment.visible;
637
629
  const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
638
- const meshes = this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) =>
639
- prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))
640
- );
641
- meshes.forEach((mesh) => mesh.setEnabled(show));
642
- this.#log("debug", "setVisibilityOfWallAndFloorInModel", { show, count: meshes.length });
630
+ console.log("PrefViewer: toggling meshes with prefixes", prefixes, "->", show);
631
+ this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
643
632
  }
644
633
 
645
634
  #setOptionsMaterial(optionMaterial) {
635
+ console.log("PrefViewer: #setOptionsMaterial()", { optionMaterial });
646
636
  if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
637
+ console.log("PrefViewer: optionMaterial incomplete, skipping");
647
638
  return false;
648
639
  }
649
640
 
650
- const material =
651
- this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) ||
652
- null;
641
+ const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
653
642
  if (!material) {
654
- this.#log("warn", "Material no encontrado en contenedor 'materials'", { name: optionMaterial.value });
643
+ console.warn("PrefViewer: material not found", optionMaterial.value);
655
644
  return false;
656
645
  }
657
646
 
658
647
  const containers = [];
659
- if (
660
- this.#data.containers.model.assetContainer &&
661
- (this.#data.containers.model.changed || this.#data.containers.materials.changed || optionMaterial.changed)
662
- ) {
648
+ if (this.#data.containers.model.assetContainer && (this.#data.containers.model.changed || this.#data.containers.materials.changed || optionMaterial.changed)) {
663
649
  containers.push(this.#data.containers.model.assetContainer);
664
650
  }
665
- if (
666
- this.#data.containers.environment.assetContainer &&
667
- (this.#data.containers.environment.changed ||
668
- this.#data.containers.materials.changed ||
669
- optionMaterial.changed)
670
- ) {
651
+ if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.changed || this.#data.containers.materials.changed || optionMaterial.changed)) {
671
652
  containers.push(this.#data.containers.environment.assetContainer);
672
653
  }
673
654
  if (containers.length === 0) {
655
+ console.log("PrefViewer: no containers require material update");
674
656
  return false;
675
657
  }
676
658
 
@@ -684,54 +666,43 @@ class PrefViewer extends HTMLElement {
684
666
  })
685
667
  );
686
668
 
669
+ console.log("PrefViewer: material assignment result", { prefix: optionMaterial.prefix, material: optionMaterial.value, someSetted });
670
+
687
671
  if (someSetted) {
688
- optionMaterial.changed && (optionMaterial.changed.success = true);
689
- this.#log("debug", "Material aplicado", { prefix: optionMaterial.prefix, name: optionMaterial.value });
672
+ optionMaterial.changed.success = true;
690
673
  } else {
691
- optionMaterial.value = optionMaterial.changed?.oldValue ?? optionMaterial.value;
674
+ optionMaterial.value = optionMaterial.changed.oldValue;
692
675
  }
693
676
 
694
677
  return someSetted;
695
678
  }
696
679
 
697
680
  #setOptionsMaterials() {
681
+ console.log("PrefViewer: #setOptionsMaterials() START");
698
682
  let someSetted = false;
699
683
  Object.values(this.#data.options.materials).forEach((material) => {
700
684
  let settedMaterial = this.#setOptionsMaterial(material);
701
685
  someSetted = someSetted || settedMaterial;
702
686
  });
687
+ console.log("PrefViewer: #setOptionsMaterials() END", { someSetted });
703
688
  return someSetted;
704
689
  }
705
690
 
706
691
  #setOptionsCamera() {
707
- if (
708
- !this.#data.options.camera.value &&
709
- !this.#data.options.camera.changed &&
710
- !this.#data.containers.model.changed &&
711
- !this.#data.containers.environment.changed
712
- ) {
692
+ console.log("PrefViewer: #setOptionsCamera()", {
693
+ requested: this.#data.options.camera.value,
694
+ changed: this.#data.options.camera.changed,
695
+ modelChanged: this.#data.containers.model.changed,
696
+ envChanged: this.#data.containers.environment.changed
697
+ });
698
+ if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.changed && !this.#data.containers.environment.changed)) {
713
699
  return false;
714
700
  }
715
701
 
716
- let camera =
717
- this.#data.containers.model.assetContainer?.cameras.find(
718
- (thisCamera) => thisCamera.name === this.#data.options.camera.value
719
- ) ||
720
- this.#data.containers.environment.assetContainer?.cameras.find(
721
- (thisCamera) => thisCamera.name === this.#data.options.camera.value
722
- ) ||
723
- null;
724
-
702
+ 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;
725
703
  if (!camera) {
726
704
  if (this.#data.options.camera.changed?.oldValue && this.#data.options.camera.changed?.oldValue !== this.#data.options.camera.value) {
727
- camera =
728
- this.#data.containers.model.assetContainer?.cameras.find(
729
- (thisCamera) => thisCamera.name === this.#data.options.camera.changed.oldValue
730
- ) ||
731
- this.#data.containers.environment.assetContainer?.cameras.find(
732
- (thisCamera) => thisCamera.name === this.#data.options.camera.changed.oldValue
733
- ) ||
734
- null;
705
+ 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 === this.#data.options.camera.changed.oldValue) || null;
735
706
  }
736
707
  if (camera) {
737
708
  camera.metadata = { locked: this.#data.options.camera.changed.oldLocked };
@@ -742,62 +713,60 @@ class PrefViewer extends HTMLElement {
742
713
  this.#data.options.camera.value = null;
743
714
  this.#data.options.camera.locked = this.#camera.metadata.locked;
744
715
  }
745
- this.#data.options.camera.changed && (this.#data.options.camera.changed.success = false);
716
+ this.#data.options.camera.changed.success = false;
746
717
  } else {
747
718
  camera.metadata = { locked: this.#data.options.camera.locked };
748
719
  }
749
-
750
720
  if (!this.#data.options.camera.locked && this.#data.options.camera.value !== null) {
751
721
  camera.attachControl(this.#canvas, true);
752
722
  }
753
723
  this.#scene.activeCamera = camera;
754
- this.#log("debug", "Cámara actualizada", {
755
- active: this.#scene?.activeCamera?.name ?? null,
756
- locked: this.#scene?.activeCamera?.metadata?.locked ?? null,
757
- });
724
+ console.log("PrefViewer: active camera set", { name: camera.name, locked: camera.metadata?.locked });
758
725
  return true;
759
726
  }
760
727
 
761
728
  #addContainer(container) {
729
+ console.log("PrefViewer: #addContainer()", { name: container.name, hasContainer: !!container.assetContainer, visible: container.visible, show: container.show });
762
730
  if (container.assetContainer && !container.visible && container.show) {
763
731
  container.assetContainer.addAllToScene();
764
732
  container.visible = true;
765
- this.#log("debug", `Añadido a escena: ${container.name}`);
733
+ console.log("PrefViewer: container added to scene", container.name);
766
734
  }
767
735
  }
768
736
 
769
737
  #removeContainer(container) {
738
+ console.log("PrefViewer: #removeContainer()", { name: container.name, hasContainer: !!container.assetContainer, visible: container.visible });
770
739
  if (container.assetContainer && container.visible) {
771
740
  container.assetContainer.removeAllFromScene();
772
741
  container.visible = false;
773
- this.#log("debug", `Eliminado de escena: ${container.name}`);
742
+ console.log("PrefViewer: container removed from scene", container.name);
774
743
  }
775
744
  }
776
745
 
777
746
  #replaceContainer(container, newAssetContainer) {
747
+ console.log("PrefViewer: #replaceContainer()", { name: container.name, hadContainer: !!container.assetContainer });
778
748
  // 1) quita y destruye el anterior si existía
779
749
  const old = container.assetContainer;
780
750
  if (old) {
781
- if (container.visible) {
782
- old.removeAllFromScene();
783
- }
784
- old.dispose();
751
+ if (container.visible) { old.removeAllFromScene(); }
752
+ old.dispose(); // <- importante
753
+ console.log("PrefViewer: old container disposed", container.name);
785
754
  }
786
755
 
787
756
  // 2) asigna el nuevo y prepara
788
757
  container.assetContainer = newAssetContainer;
789
758
 
790
- // Limitar luces por material (no usamos luces, poner 0 asegura shaders más simples)
791
- container.assetContainer.materials?.forEach((m) => {
759
+ // Opcional: limitar luces por material para ganar margen
760
+ container.assetContainer.materials?.forEach(m => {
792
761
  if ("maxSimultaneousLights" in m) {
793
- m.maxSimultaneousLights = 0; // [LIGHTS OFF]
762
+ m.maxSimultaneousLights = 2; // 2–3 suele ir bien
794
763
  }
795
764
  });
796
765
 
797
- // 3) sombras sólo si existe generador (con luces OFF es null)
798
- container.assetContainer.meshes.forEach((mesh) => {
799
- mesh.receiveShadows = !!this.#shadowGen;
800
- if (this.#shadowGen) this.#shadowGen.addShadowCaster(mesh, true);
766
+ // 3) sombras solo para los meshes que te interesen (mejor que todos)
767
+ container.assetContainer.meshes.forEach(mesh => {
768
+ mesh.receiveShadows = true;
769
+ this.#shadowGen.addShadowCaster(mesh, true);
801
770
  });
802
771
 
803
772
  // 4) añade a escena
@@ -805,46 +774,35 @@ class PrefViewer extends HTMLElement {
805
774
 
806
775
  // 5) fuerza recompilación con defines correctos del nuevo estado
807
776
  this.#scene.getEngine().releaseEffects();
808
-
809
- this.#log("debug", `Container reemplazado: ${container.name}`, {
810
- meshes: container.assetContainer.meshes?.length ?? 0,
811
- materials: container.assetContainer.materials?.length ?? 0,
812
- });
777
+ console.log("PrefViewer: container replaced and effects released", container.name);
813
778
  }
814
779
 
815
780
  async #loadAssetContainer(container) {
816
- const storage = container?.storage;
781
+ console.log("PrefViewer: #loadAssetContainer() START", { name: container?.name, storage: container?.storage });
782
+ let storage = container?.storage;
817
783
 
818
784
  if (!storage) {
819
- this.#log("debug", `Sin storage para "${container?.name}"`);
785
+ console.log("PrefViewer: no storage, skipping", container?.name);
820
786
  return false;
821
787
  }
822
788
 
823
- this.#timeStart(`load:${container.name}`);
824
789
  let source = storage.url || null;
825
790
 
826
791
  if (storage.db && storage.table && storage.id) {
827
- this.#log("info", `Cargando ${container.name} desde IndexedDB`, {
828
- db: storage.db,
829
- table: storage.table,
830
- id: storage.id,
831
- });
832
792
  await this.#initStorage(storage.db, storage.table);
833
793
  const object = await loadModel(storage.id, storage.table);
834
794
  source = object.data;
835
795
  if (object.timeStamp === container.timeStamp) {
836
- this.#log("debug", `${container.name}: sin cambios en IndexedDB`);
837
- this.#timeEnd(`load:${container.name}`);
796
+ console.log("PrefViewer: DB entry unchanged, skipping", container.name);
838
797
  return false;
839
798
  } else {
840
799
  container.changed = { timeStamp: object.timeStamp, size: object.size, success: false };
841
- this.#log("debug", `${container.name}: cambios detectados`, container.changed);
800
+ console.log("PrefViewer: DB entry changed", container.changed);
842
801
  }
843
802
  }
844
803
 
845
804
  if (!source) {
846
- this.#log("warn", `${container.name}: no hay source`);
847
- this.#timeEnd(`load:${container.name}`);
805
+ console.log("PrefViewer: no source after storage resolution", container.name);
848
806
  return false;
849
807
  }
850
808
 
@@ -857,23 +815,23 @@ class PrefViewer extends HTMLElement {
857
815
  });
858
816
  if (!container.changed) {
859
817
  if (container.timeStamp === null && container.size === size) {
860
- this.#log("debug", `${container.name}: Base64 sin cambios`);
861
- this.#timeEnd(`load:${container.name}`);
818
+ console.log("PrefViewer: same base64 size and null timestamp, skipping", container.name);
862
819
  return false;
863
820
  } else {
864
821
  container.changed = { timeStamp: null, size: size, success: false };
865
822
  }
866
823
  }
824
+ console.log("PrefViewer: prepared File from base64", { name: file.name, size: file.size, type: file.type });
867
825
  } else {
868
826
  const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
869
827
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
870
828
  const [fileSize, fileTimeStamp] = await this.#getServerFileDataHeader(source);
871
829
  if (container.size === fileSize && container.timeStamp === fileTimeStamp) {
872
- this.#log("debug", `${container.name}: URL sin cambios`);
873
- this.#timeEnd(`load:${container.name}`);
830
+ console.log("PrefViewer: remote file unchanged, skipping", container.name);
874
831
  return false;
875
832
  } else {
876
833
  container.changed = { timeStamp: fileTimeStamp, size: fileSize, success: false };
834
+ console.log("PrefViewer: remote file changed", container.changed);
877
835
  }
878
836
  }
879
837
 
@@ -886,21 +844,13 @@ class PrefViewer extends HTMLElement {
886
844
  },
887
845
  },
888
846
  };
889
- this.#log("info", `LoadAssetContainerAsync ${container.name}`, { extension, changed: container.changed });
890
847
 
891
- try {
892
- const result = await LoadAssetContainerAsync(file || source, this.#scene, options);
893
- this.#timeEnd(`load:${container.name}`);
894
- return result;
895
- } catch (e) {
896
- this.#timeEnd(`load:${container.name}`);
897
- this.#log("error", `LoadAssetContainerAsync falló para ${container.name}`, e);
898
- throw e;
899
- }
848
+ console.log("PrefViewer: calling LoadAssetContainerAsync()", { extension });
849
+ return LoadAssetContainerAsync(file || source, this.#scene, options);
900
850
  }
901
851
 
902
852
  async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
903
- this.#log("info", "loadContainers()", { loadModel, loadEnvironment, loadMaterials });
853
+ console.log("PrefViewer: #loadContainers()", { loadModel, loadEnvironment, loadMaterials });
904
854
  const promiseArray = [];
905
855
  promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
906
856
  promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
@@ -910,37 +860,33 @@ class PrefViewer extends HTMLElement {
910
860
 
911
861
  Promise.allSettled(promiseArray)
912
862
  .then(async (values) => {
863
+ console.log("PrefViewer: Promise.allSettled results", values);
913
864
  const modelContainer = values[0];
914
865
  const environmentContainer = values[1];
915
866
  const materialsContainer = values[2];
916
867
 
917
- this.#log(
918
- "debug",
919
- "Resultados Promise.allSettled",
920
- values.map((v) => ({ status: v.status, hasValue: !!v.value }))
921
- );
922
-
923
868
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
869
+ console.log("PrefViewer: model container fulfilled, applying");
924
870
  this.#stripImportedLights(modelContainer.value);
925
871
  this.#replaceContainer(this.#data.containers.model, modelContainer.value);
926
872
  this.#storeChangedFlagsForContainer(this.#data.containers.model);
927
873
  } else {
928
- this.#data.containers.model.show
929
- ? this.#addContainer(this.#data.containers.model)
930
- : this.#removeContainer(this.#data.containers.model);
874
+ console.log("PrefViewer: model container not fulfilled or unchanged; ensuring visibility state");
875
+ this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
931
876
  }
932
877
 
933
878
  if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
879
+ console.log("PrefViewer: environment container fulfilled, applying");
934
880
  this.#stripImportedLights(environmentContainer.value);
935
881
  this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
936
882
  this.#storeChangedFlagsForContainer(this.#data.containers.environment);
937
883
  } else {
938
- this.#data.containers.environment.show
939
- ? this.#addContainer(this.#data.containers.environment)
940
- : this.#removeContainer(this.#data.containers.environment);
884
+ console.log("PrefViewer: environment container not fulfilled or unchanged; ensuring visibility state");
885
+ this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
941
886
  }
942
887
 
943
888
  if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
889
+ console.log("PrefViewer: materials container fulfilled, applying");
944
890
  this.#stripImportedLights(materialsContainer.value);
945
891
  this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
946
892
  this.#storeChangedFlagsForContainer(this.#data.containers.materials);
@@ -951,11 +897,10 @@ class PrefViewer extends HTMLElement {
951
897
  this.#setVisibilityOfWallAndFloorInModel();
952
898
  this.#setStatusSceneLoaded();
953
899
  this.#resetChangedFlags();
954
- this.#log("info", "Escena cargada");
955
900
  })
956
901
  .catch((error) => {
957
902
  this.loaded = true;
958
- this.#log("error", "Failed to load containers", error);
903
+ console.error("PrefViewer: failed to load model", error);
959
904
  this.dispatchEvent(
960
905
  new CustomEvent("scene-error", {
961
906
  bubbles: true,
@@ -968,27 +913,29 @@ class PrefViewer extends HTMLElement {
968
913
  }
969
914
 
970
915
  #stripImportedLights(container) {
971
- const n = container?.lights?.length ?? 0;
972
- if (n) container.lights.slice().forEach((l) => l.dispose());
973
- this.#log("debug", `stripImportedLights(): ${n} → 0`);
916
+ console.log("PrefViewer: #stripImportedLights()", { lights: container?.lights?.length || 0 });
917
+ // El glTF puede traer KHR_lights_punctual: bórralas antes de añadir a la escena
918
+ if (container?.lights?.length) {
919
+ // Clonar para no mutar mientras iteras
920
+ container.lights.slice().forEach(l => l.dispose());
921
+ console.log("PrefViewer: stripped punctual lights from imported asset");
922
+ }
974
923
  }
975
924
 
976
925
  // Public Methods
977
926
  loadConfig(config) {
978
- this.#log("info", "loadConfig()", typeof config === "string" ? "[string]" : config);
927
+ console.log("PrefViewer: loadConfig()", config);
979
928
  config = typeof config === "string" ? JSON.parse(config) : config;
980
929
  if (!config) {
981
- this.#log("warn", "loadConfig() config vacío/nulo");
930
+ console.warn("PrefViewer: loadConfig() no config provided");
982
931
  return false;
983
932
  }
984
933
 
985
934
  // Containers
986
935
  this.#data.containers.model.storage = config.model?.storage || null;
987
- this.#data.containers.model.show =
988
- config.model?.visible !== undefined ? config.model.visible : this.#data.containers.model.show;
936
+ this.#data.containers.model.show = config.model?.visible !== undefined ? config.model.visible : this.#data.containers.model.show;
989
937
  this.#data.containers.environment.storage = config.scene?.storage || null;
990
- this.#data.containers.environment.show =
991
- config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
938
+ this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
992
939
  this.#data.containers.materials.storage = config.materials?.storage || null;
993
940
 
994
941
  // Options
@@ -1001,7 +948,7 @@ class PrefViewer extends HTMLElement {
1001
948
  }
1002
949
 
1003
950
  setOptions(options) {
1004
- this.#log("info", "setOptions()", options);
951
+ console.log("PrefViewer: setOptions()", options);
1005
952
  if (!options) {
1006
953
  return false;
1007
954
  }
@@ -1023,61 +970,63 @@ class PrefViewer extends HTMLElement {
1023
970
  }
1024
971
 
1025
972
  loadModel(model) {
1026
- this.#log("info", "loadModel()", typeof model === "string" ? "[string]" : model);
973
+ console.log("PrefViewer: loadModel()", model);
1027
974
  model = typeof model === "string" ? JSON.parse(model) : model;
1028
975
  if (!model) {
1029
- this.#log("warn", "loadModel() model vacío/nulo");
976
+ console.warn("PrefViewer: loadModel() no model provided");
1030
977
  return false;
1031
978
  }
1032
979
  this.#data.containers.model.storage = model.storage || null;
1033
- this.#data.containers.model.show =
1034
- model.visible !== undefined ? model.visible : this.#data.containers.model.show;
980
+ this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
1035
981
  this.initialized && this.#loadContainers(true, false, false);
1036
982
  }
1037
983
 
1038
984
  loadScene(scene) {
1039
- this.#log("info", "loadScene()", typeof scene === "string" ? "[string]" : scene);
985
+ console.log("PrefViewer: loadScene()", scene);
1040
986
  scene = typeof scene === "string" ? JSON.parse(scene) : scene;
1041
987
  if (!scene) {
1042
- this.#log("warn", "loadScene() scene vacío/nulo");
988
+ console.warn("PrefViewer: loadScene() no scene provided");
1043
989
  return false;
1044
990
  }
1045
991
  this.#data.containers.environment.storage = scene.storage || null;
1046
- this.#data.containers.environment.show =
1047
- scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
992
+ this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
1048
993
  this.initialized && this.#loadContainers(false, true, false);
1049
994
  }
1050
995
 
1051
996
  showModel() {
997
+ console.log("PrefViewer: showModel()");
1052
998
  this.#data.containers.model.show = true;
1053
999
  this.#addContainer(this.#data.containers.model);
1054
1000
  }
1055
1001
 
1056
1002
  hideModel() {
1003
+ console.log("PrefViewer: hideModel()");
1057
1004
  this.#data.containers.model.show = false;
1058
1005
  this.#removeContainer(this.#data.containers.model);
1059
1006
  }
1060
1007
 
1061
1008
  showScene() {
1009
+ console.log("PrefViewer: showScene()");
1062
1010
  this.#data.containers.environment.show = true;
1063
1011
  this.#addContainer(this.#data.containers.environment);
1064
1012
  this.#setVisibilityOfWallAndFloorInModel();
1065
1013
  }
1066
1014
 
1067
1015
  hideScene() {
1016
+ console.log("PrefViewer: hideScene()");
1068
1017
  this.#data.containers.environment.show = false;
1069
1018
  this.#removeContainer(this.#data.containers.environment);
1070
1019
  this.#setVisibilityOfWallAndFloorInModel();
1071
1020
  }
1072
1021
 
1073
1022
  downloadModelGLB() {
1023
+ console.log("PrefViewer: downloadModelGLB()");
1074
1024
  const fileName = "model";
1075
- GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, {
1076
- exportWithoutWaitingForScene: true,
1077
- }).then((glb) => glb.downloadFiles());
1025
+ GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
1078
1026
  }
1079
1027
 
1080
1028
  downloadModelUSDZ() {
1029
+ console.log("PrefViewer: downloadModelUSDZ()");
1081
1030
  const fileName = "model";
1082
1031
  USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
1083
1032
  if (response) {
@@ -1087,6 +1036,7 @@ class PrefViewer extends HTMLElement {
1087
1036
  }
1088
1037
 
1089
1038
  downloadModelAndSceneUSDZ() {
1039
+ console.log("PrefViewer: downloadModelAndSceneUSDZ()");
1090
1040
  const fileName = "scene";
1091
1041
  USDZExportAsync(this.#scene).then((response) => {
1092
1042
  if (response) {
@@ -1096,10 +1046,9 @@ class PrefViewer extends HTMLElement {
1096
1046
  }
1097
1047
 
1098
1048
  downloadModelAndSceneGLB() {
1049
+ console.log("PrefViewer: downloadModelAndSceneGLB()");
1099
1050
  const fileName = "scene";
1100
- GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) =>
1101
- glb.downloadFiles()
1102
- );
1051
+ GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
1103
1052
  }
1104
1053
  }
1105
1054