@preference-sl/pref-viewer 2.10.0-beta.21 → 2.10.0-beta.23
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.
- package/package.json +1 -1
- package/src/index.js +252 -260
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,88 +349,91 @@ 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
|
|
393
|
-
|
|
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
|
-
|
|
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)
|
|
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].
|
|
410
|
-
|
|
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() {
|
|
426
|
-
|
|
427
|
-
Object.values(this.#data.
|
|
428
|
-
this.#data.options.
|
|
383
|
+
console.log("PrefViewer: #resetChangedFlags()");
|
|
384
|
+
Object.values(this.#data.containers).forEach((container) => (container.changed.success = false));
|
|
385
|
+
Object.values(this.#data.options.materials).forEach((material) => (material.changed.success = false));
|
|
386
|
+
this.#data.options.camera.changed.success = false;
|
|
429
387
|
}
|
|
430
388
|
|
|
431
389
|
// Babylon.js
|
|
432
390
|
async #initializeBabylon() {
|
|
433
|
-
|
|
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
|
|
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();
|
|
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
|
-
|
|
410
|
+
console.log("PrefViewer: #initializeBabylon() END");
|
|
452
411
|
}
|
|
453
412
|
|
|
454
413
|
addStylesToARButton() {
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
this.#
|
|
538
|
-
this.#
|
|
539
|
-
|
|
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)
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
561
|
+
console.log("PrefViewer: HEAD ok", { size, timeStamp });
|
|
576
562
|
resolve([size, timeStamp]);
|
|
577
563
|
} else {
|
|
578
|
-
|
|
564
|
+
console.warn("PrefViewer: HEAD failed", xhr.status);
|
|
579
565
|
resolve([0, null]);
|
|
580
566
|
}
|
|
581
567
|
};
|
|
582
568
|
xhr.onerror = () => {
|
|
583
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,73 @@ class PrefViewer extends HTMLElement {
|
|
|
604
592
|
try {
|
|
605
593
|
decoded = atob(raw);
|
|
606
594
|
} catch {
|
|
607
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
639
|
-
|
|
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
|
-
|
|
652
|
-
|
|
641
|
+
const material = this.#data.containers.materials.assetContainer?.materials
|
|
642
|
+
.find((mat) => mat.name === optionMaterial.value) || null;
|
|
643
|
+
|
|
653
644
|
if (!material) {
|
|
654
|
-
|
|
645
|
+
console.warn("PrefViewer: material not found", optionMaterial.value);
|
|
655
646
|
return false;
|
|
656
647
|
}
|
|
657
648
|
|
|
649
|
+
const hadExplicitChange = !!optionMaterial.changed;
|
|
650
|
+
|
|
658
651
|
const containers = [];
|
|
659
|
-
if (
|
|
660
|
-
this.#data.containers.model.
|
|
661
|
-
(this.#data.containers.model.changed || this.#data.containers.materials.changed || optionMaterial.changed)
|
|
662
|
-
) {
|
|
652
|
+
if (this.#data.containers.model.assetContainer &&
|
|
653
|
+
(this.#data.containers.model.changed || this.#data.containers.materials.changed || optionMaterial.changed)) {
|
|
663
654
|
containers.push(this.#data.containers.model.assetContainer);
|
|
664
655
|
}
|
|
665
|
-
if (
|
|
666
|
-
this.#data.containers.environment.
|
|
667
|
-
(this.#data.containers.environment.changed ||
|
|
668
|
-
this.#data.containers.materials.changed ||
|
|
669
|
-
optionMaterial.changed)
|
|
670
|
-
) {
|
|
656
|
+
if (this.#data.containers.environment.assetContainer &&
|
|
657
|
+
(this.#data.containers.environment.changed || this.#data.containers.materials.changed || optionMaterial.changed)) {
|
|
671
658
|
containers.push(this.#data.containers.environment.assetContainer);
|
|
672
659
|
}
|
|
673
660
|
if (containers.length === 0) {
|
|
661
|
+
console.log("PrefViewer: no containers require material update");
|
|
674
662
|
return false;
|
|
675
663
|
}
|
|
676
664
|
|
|
@@ -684,120 +672,144 @@ class PrefViewer extends HTMLElement {
|
|
|
684
672
|
})
|
|
685
673
|
);
|
|
686
674
|
|
|
675
|
+
console.log("PrefViewer: material assignment result", {
|
|
676
|
+
prefix: optionMaterial.prefix, material: optionMaterial.value, someSetted
|
|
677
|
+
});
|
|
678
|
+
|
|
687
679
|
if (someSetted) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
680
|
+
// Asegura estructura si antes era `false`
|
|
681
|
+
if (!optionMaterial.changed) {
|
|
682
|
+
optionMaterial.changed = { oldValue: optionMaterial.value, success: false };
|
|
683
|
+
}
|
|
684
|
+
optionMaterial.changed.success = true;
|
|
685
|
+
} else if (hadExplicitChange) {
|
|
686
|
+
// Solo revertimos si el usuario pidió el cambio y falló
|
|
687
|
+
optionMaterial.value = optionMaterial.changed.oldValue;
|
|
692
688
|
}
|
|
693
689
|
|
|
694
690
|
return someSetted;
|
|
695
691
|
}
|
|
696
692
|
|
|
697
693
|
#setOptionsMaterials() {
|
|
694
|
+
console.log("PrefViewer: #setOptionsMaterials() START");
|
|
698
695
|
let someSetted = false;
|
|
699
696
|
Object.values(this.#data.options.materials).forEach((material) => {
|
|
700
697
|
let settedMaterial = this.#setOptionsMaterial(material);
|
|
701
698
|
someSetted = someSetted || settedMaterial;
|
|
702
699
|
});
|
|
700
|
+
console.log("PrefViewer: #setOptionsMaterials() END", { someSetted });
|
|
703
701
|
return someSetted;
|
|
704
702
|
}
|
|
705
703
|
|
|
706
704
|
#setOptionsCamera() {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
)
|
|
705
|
+
console.log("PrefViewer: #setOptionsCamera()", {
|
|
706
|
+
requested: this.#data.options.camera.value,
|
|
707
|
+
changed: this.#data.options.camera.changed,
|
|
708
|
+
modelChanged: this.#data.containers.model.changed,
|
|
709
|
+
envChanged: this.#data.containers.environment.changed
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
const camState = this.#data.options.camera;
|
|
713
|
+
|
|
714
|
+
if (!camState.value && !camState.changed && !this.#data.containers.model.changed && !this.#data.containers.environment.changed) {
|
|
713
715
|
return false;
|
|
714
716
|
}
|
|
715
717
|
|
|
718
|
+
const hadExplicitChange = !!camState.changed;
|
|
719
|
+
|
|
716
720
|
let camera =
|
|
717
|
-
this.#data.containers.model.assetContainer?.cameras.find(
|
|
718
|
-
|
|
719
|
-
) ||
|
|
720
|
-
this.#data.containers.environment.assetContainer?.cameras.find(
|
|
721
|
-
(thisCamera) => thisCamera.name === this.#data.options.camera.value
|
|
722
|
-
) ||
|
|
721
|
+
this.#data.containers.model.assetContainer?.cameras.find(c => c.name === camState.value) ||
|
|
722
|
+
this.#data.containers.environment.assetContainer?.cameras.find(c => c.name === camState.value) ||
|
|
723
723
|
null;
|
|
724
724
|
|
|
725
725
|
if (!camera) {
|
|
726
|
-
|
|
726
|
+
// Si falló la cámara solicitada y hubo cambio explícito, intentamos volver a la anterior
|
|
727
|
+
if (hadExplicitChange && camState.changed.oldValue && camState.changed.oldValue !== camState.value) {
|
|
727
728
|
camera =
|
|
728
|
-
this.#data.containers.model.assetContainer?.cameras.find(
|
|
729
|
-
|
|
730
|
-
) ||
|
|
731
|
-
this.#data.containers.environment.assetContainer?.cameras.find(
|
|
732
|
-
(thisCamera) => thisCamera.name === this.#data.options.camera.changed.oldValue
|
|
733
|
-
) ||
|
|
729
|
+
this.#data.containers.model.assetContainer?.cameras.find(c => c.name === camState.changed.oldValue) ||
|
|
730
|
+
this.#data.containers.environment.assetContainer?.cameras.find(c => c.name === camState.changed.oldValue) ||
|
|
734
731
|
null;
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
732
|
+
|
|
733
|
+
// Asegura estructura para escribir success
|
|
734
|
+
if (!camState.changed) camState.changed = { oldValue: camState.value, oldLocked: camState.locked, success: false };
|
|
735
|
+
|
|
736
|
+
if (camera) {
|
|
737
|
+
camera.metadata = { locked: camState.changed.oldLocked };
|
|
738
|
+
camState.value = camState.changed.oldValue;
|
|
739
|
+
camState.locked = camState.changed.oldLocked;
|
|
740
|
+
camState.changed.success = false;
|
|
741
|
+
} else {
|
|
742
|
+
// Fallback a la cámara por defecto del componente
|
|
743
|
+
camera = this.#camera;
|
|
744
|
+
camState.value = null;
|
|
745
|
+
camState.locked = this.#camera.metadata.locked;
|
|
746
|
+
camState.changed.success = false;
|
|
747
|
+
}
|
|
740
748
|
} else {
|
|
749
|
+
// No hubo cambio explícito: usa fallback sin tocar changed
|
|
741
750
|
camera = this.#camera;
|
|
742
|
-
|
|
743
|
-
|
|
751
|
+
camState.value = null;
|
|
752
|
+
camState.locked = this.#camera.metadata.locked;
|
|
744
753
|
}
|
|
745
|
-
this.#data.options.camera.changed && (this.#data.options.camera.changed.success = false);
|
|
746
754
|
} else {
|
|
747
|
-
camera.metadata = { locked:
|
|
755
|
+
camera.metadata = { locked: camState.locked };
|
|
756
|
+
// Marca success si hubo cambio explícito
|
|
757
|
+
if (hadExplicitChange) {
|
|
758
|
+
if (!camState.changed) camState.changed = { oldValue: camState.value, oldLocked: camState.locked, success: false };
|
|
759
|
+
camState.changed.success = true;
|
|
760
|
+
}
|
|
748
761
|
}
|
|
749
762
|
|
|
750
|
-
if (!
|
|
763
|
+
if (!camState.locked && camState.value !== null) {
|
|
751
764
|
camera.attachControl(this.#canvas, true);
|
|
752
765
|
}
|
|
753
766
|
this.#scene.activeCamera = camera;
|
|
754
|
-
|
|
755
|
-
active: this.#scene?.activeCamera?.name ?? null,
|
|
756
|
-
locked: this.#scene?.activeCamera?.metadata?.locked ?? null,
|
|
757
|
-
});
|
|
767
|
+
console.log("PrefViewer: active camera set", { name: camera.name, locked: camera.metadata?.locked });
|
|
758
768
|
return true;
|
|
759
769
|
}
|
|
760
770
|
|
|
761
771
|
#addContainer(container) {
|
|
772
|
+
console.log("PrefViewer: #addContainer()", { name: container.name, hasContainer: !!container.assetContainer, visible: container.visible, show: container.show });
|
|
762
773
|
if (container.assetContainer && !container.visible && container.show) {
|
|
763
774
|
container.assetContainer.addAllToScene();
|
|
764
775
|
container.visible = true;
|
|
765
|
-
|
|
776
|
+
console.log("PrefViewer: container added to scene", container.name);
|
|
766
777
|
}
|
|
767
778
|
}
|
|
768
779
|
|
|
769
780
|
#removeContainer(container) {
|
|
781
|
+
console.log("PrefViewer: #removeContainer()", { name: container.name, hasContainer: !!container.assetContainer, visible: container.visible });
|
|
770
782
|
if (container.assetContainer && container.visible) {
|
|
771
783
|
container.assetContainer.removeAllFromScene();
|
|
772
784
|
container.visible = false;
|
|
773
|
-
|
|
785
|
+
console.log("PrefViewer: container removed from scene", container.name);
|
|
774
786
|
}
|
|
775
787
|
}
|
|
776
788
|
|
|
777
789
|
#replaceContainer(container, newAssetContainer) {
|
|
790
|
+
console.log("PrefViewer: #replaceContainer()", { name: container.name, hadContainer: !!container.assetContainer });
|
|
778
791
|
// 1) quita y destruye el anterior si existía
|
|
779
792
|
const old = container.assetContainer;
|
|
780
793
|
if (old) {
|
|
781
|
-
if (container.visible) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
old.dispose();
|
|
794
|
+
if (container.visible) { old.removeAllFromScene(); }
|
|
795
|
+
old.dispose(); // <- importante
|
|
796
|
+
console.log("PrefViewer: old container disposed", container.name);
|
|
785
797
|
}
|
|
786
798
|
|
|
787
799
|
// 2) asigna el nuevo y prepara
|
|
788
800
|
container.assetContainer = newAssetContainer;
|
|
789
801
|
|
|
790
|
-
//
|
|
791
|
-
container.assetContainer.materials?.forEach(
|
|
802
|
+
// Opcional: limitar luces por material para ganar margen
|
|
803
|
+
container.assetContainer.materials?.forEach(m => {
|
|
792
804
|
if ("maxSimultaneousLights" in m) {
|
|
793
|
-
m.maxSimultaneousLights =
|
|
805
|
+
m.maxSimultaneousLights = 2; // 2–3 suele ir bien
|
|
794
806
|
}
|
|
795
807
|
});
|
|
796
808
|
|
|
797
|
-
// 3) sombras
|
|
798
|
-
container.assetContainer.meshes.forEach(
|
|
799
|
-
mesh.receiveShadows =
|
|
800
|
-
|
|
809
|
+
// 3) sombras solo para los meshes que te interesen (mejor que todos)
|
|
810
|
+
container.assetContainer.meshes.forEach(mesh => {
|
|
811
|
+
mesh.receiveShadows = true;
|
|
812
|
+
this.#shadowGen.addShadowCaster(mesh, true);
|
|
801
813
|
});
|
|
802
814
|
|
|
803
815
|
// 4) añade a escena
|
|
@@ -805,46 +817,35 @@ class PrefViewer extends HTMLElement {
|
|
|
805
817
|
|
|
806
818
|
// 5) fuerza recompilación con defines correctos del nuevo estado
|
|
807
819
|
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
|
-
});
|
|
820
|
+
console.log("PrefViewer: container replaced and effects released", container.name);
|
|
813
821
|
}
|
|
814
822
|
|
|
815
823
|
async #loadAssetContainer(container) {
|
|
816
|
-
|
|
824
|
+
console.log("PrefViewer: #loadAssetContainer() START", { name: container?.name, storage: container?.storage });
|
|
825
|
+
let storage = container?.storage;
|
|
817
826
|
|
|
818
827
|
if (!storage) {
|
|
819
|
-
|
|
828
|
+
console.log("PrefViewer: no storage, skipping", container?.name);
|
|
820
829
|
return false;
|
|
821
830
|
}
|
|
822
831
|
|
|
823
|
-
this.#timeStart(`load:${container.name}`);
|
|
824
832
|
let source = storage.url || null;
|
|
825
833
|
|
|
826
834
|
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
835
|
await this.#initStorage(storage.db, storage.table);
|
|
833
836
|
const object = await loadModel(storage.id, storage.table);
|
|
834
837
|
source = object.data;
|
|
835
838
|
if (object.timeStamp === container.timeStamp) {
|
|
836
|
-
|
|
837
|
-
this.#timeEnd(`load:${container.name}`);
|
|
839
|
+
console.log("PrefViewer: DB entry unchanged, skipping", container.name);
|
|
838
840
|
return false;
|
|
839
841
|
} else {
|
|
840
842
|
container.changed = { timeStamp: object.timeStamp, size: object.size, success: false };
|
|
841
|
-
|
|
843
|
+
console.log("PrefViewer: DB entry changed", container.changed);
|
|
842
844
|
}
|
|
843
845
|
}
|
|
844
846
|
|
|
845
847
|
if (!source) {
|
|
846
|
-
|
|
847
|
-
this.#timeEnd(`load:${container.name}`);
|
|
848
|
+
console.log("PrefViewer: no source after storage resolution", container.name);
|
|
848
849
|
return false;
|
|
849
850
|
}
|
|
850
851
|
|
|
@@ -857,23 +858,23 @@ class PrefViewer extends HTMLElement {
|
|
|
857
858
|
});
|
|
858
859
|
if (!container.changed) {
|
|
859
860
|
if (container.timeStamp === null && container.size === size) {
|
|
860
|
-
|
|
861
|
-
this.#timeEnd(`load:${container.name}`);
|
|
861
|
+
console.log("PrefViewer: same base64 size and null timestamp, skipping", container.name);
|
|
862
862
|
return false;
|
|
863
863
|
} else {
|
|
864
864
|
container.changed = { timeStamp: null, size: size, success: false };
|
|
865
865
|
}
|
|
866
866
|
}
|
|
867
|
+
console.log("PrefViewer: prepared File from base64", { name: file.name, size: file.size, type: file.type });
|
|
867
868
|
} else {
|
|
868
869
|
const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
|
|
869
870
|
extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
|
|
870
871
|
const [fileSize, fileTimeStamp] = await this.#getServerFileDataHeader(source);
|
|
871
872
|
if (container.size === fileSize && container.timeStamp === fileTimeStamp) {
|
|
872
|
-
|
|
873
|
-
this.#timeEnd(`load:${container.name}`);
|
|
873
|
+
console.log("PrefViewer: remote file unchanged, skipping", container.name);
|
|
874
874
|
return false;
|
|
875
875
|
} else {
|
|
876
876
|
container.changed = { timeStamp: fileTimeStamp, size: fileSize, success: false };
|
|
877
|
+
console.log("PrefViewer: remote file changed", container.changed);
|
|
877
878
|
}
|
|
878
879
|
}
|
|
879
880
|
|
|
@@ -886,21 +887,13 @@ class PrefViewer extends HTMLElement {
|
|
|
886
887
|
},
|
|
887
888
|
},
|
|
888
889
|
};
|
|
889
|
-
this.#log("info", `LoadAssetContainerAsync ${container.name}`, { extension, changed: container.changed });
|
|
890
890
|
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
}
|
|
891
|
+
console.log("PrefViewer: calling LoadAssetContainerAsync()", { extension });
|
|
892
|
+
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
900
893
|
}
|
|
901
894
|
|
|
902
895
|
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
903
|
-
|
|
896
|
+
console.log("PrefViewer: #loadContainers()", { loadModel, loadEnvironment, loadMaterials });
|
|
904
897
|
const promiseArray = [];
|
|
905
898
|
promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
|
|
906
899
|
promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
|
|
@@ -910,37 +903,33 @@ class PrefViewer extends HTMLElement {
|
|
|
910
903
|
|
|
911
904
|
Promise.allSettled(promiseArray)
|
|
912
905
|
.then(async (values) => {
|
|
906
|
+
console.log("PrefViewer: Promise.allSettled results", values);
|
|
913
907
|
const modelContainer = values[0];
|
|
914
908
|
const environmentContainer = values[1];
|
|
915
909
|
const materialsContainer = values[2];
|
|
916
910
|
|
|
917
|
-
this.#log(
|
|
918
|
-
"debug",
|
|
919
|
-
"Resultados Promise.allSettled",
|
|
920
|
-
values.map((v) => ({ status: v.status, hasValue: !!v.value }))
|
|
921
|
-
);
|
|
922
|
-
|
|
923
911
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
912
|
+
console.log("PrefViewer: model container fulfilled, applying");
|
|
924
913
|
this.#stripImportedLights(modelContainer.value);
|
|
925
914
|
this.#replaceContainer(this.#data.containers.model, modelContainer.value);
|
|
926
915
|
this.#storeChangedFlagsForContainer(this.#data.containers.model);
|
|
927
916
|
} else {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
: this.#removeContainer(this.#data.containers.model);
|
|
917
|
+
console.log("PrefViewer: model container not fulfilled or unchanged; ensuring visibility state");
|
|
918
|
+
this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
|
|
931
919
|
}
|
|
932
920
|
|
|
933
921
|
if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
|
|
922
|
+
console.log("PrefViewer: environment container fulfilled, applying");
|
|
934
923
|
this.#stripImportedLights(environmentContainer.value);
|
|
935
924
|
this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
|
|
936
925
|
this.#storeChangedFlagsForContainer(this.#data.containers.environment);
|
|
937
926
|
} else {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
: this.#removeContainer(this.#data.containers.environment);
|
|
927
|
+
console.log("PrefViewer: environment container not fulfilled or unchanged; ensuring visibility state");
|
|
928
|
+
this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
|
|
941
929
|
}
|
|
942
930
|
|
|
943
931
|
if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
|
|
932
|
+
console.log("PrefViewer: materials container fulfilled, applying");
|
|
944
933
|
this.#stripImportedLights(materialsContainer.value);
|
|
945
934
|
this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
|
|
946
935
|
this.#storeChangedFlagsForContainer(this.#data.containers.materials);
|
|
@@ -951,11 +940,10 @@ class PrefViewer extends HTMLElement {
|
|
|
951
940
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
952
941
|
this.#setStatusSceneLoaded();
|
|
953
942
|
this.#resetChangedFlags();
|
|
954
|
-
this.#log("info", "Escena cargada");
|
|
955
943
|
})
|
|
956
944
|
.catch((error) => {
|
|
957
945
|
this.loaded = true;
|
|
958
|
-
|
|
946
|
+
console.error("PrefViewer: failed to load model", error);
|
|
959
947
|
this.dispatchEvent(
|
|
960
948
|
new CustomEvent("scene-error", {
|
|
961
949
|
bubbles: true,
|
|
@@ -968,27 +956,29 @@ class PrefViewer extends HTMLElement {
|
|
|
968
956
|
}
|
|
969
957
|
|
|
970
958
|
#stripImportedLights(container) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
959
|
+
console.log("PrefViewer: #stripImportedLights()", { lights: container?.lights?.length || 0 });
|
|
960
|
+
// El glTF puede traer KHR_lights_punctual: bórralas antes de añadir a la escena
|
|
961
|
+
if (container?.lights?.length) {
|
|
962
|
+
// Clonar para no mutar mientras iteras
|
|
963
|
+
container.lights.slice().forEach(l => l.dispose());
|
|
964
|
+
console.log("PrefViewer: stripped punctual lights from imported asset");
|
|
965
|
+
}
|
|
974
966
|
}
|
|
975
967
|
|
|
976
968
|
// Public Methods
|
|
977
969
|
loadConfig(config) {
|
|
978
|
-
|
|
970
|
+
console.log("PrefViewer: loadConfig()", config);
|
|
979
971
|
config = typeof config === "string" ? JSON.parse(config) : config;
|
|
980
972
|
if (!config) {
|
|
981
|
-
|
|
973
|
+
console.warn("PrefViewer: loadConfig() no config provided");
|
|
982
974
|
return false;
|
|
983
975
|
}
|
|
984
976
|
|
|
985
977
|
// Containers
|
|
986
978
|
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;
|
|
979
|
+
this.#data.containers.model.show = config.model?.visible !== undefined ? config.model.visible : this.#data.containers.model.show;
|
|
989
980
|
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;
|
|
981
|
+
this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
|
|
992
982
|
this.#data.containers.materials.storage = config.materials?.storage || null;
|
|
993
983
|
|
|
994
984
|
// Options
|
|
@@ -1001,7 +991,7 @@ class PrefViewer extends HTMLElement {
|
|
|
1001
991
|
}
|
|
1002
992
|
|
|
1003
993
|
setOptions(options) {
|
|
1004
|
-
|
|
994
|
+
console.log("PrefViewer: setOptions()", options);
|
|
1005
995
|
if (!options) {
|
|
1006
996
|
return false;
|
|
1007
997
|
}
|
|
@@ -1023,61 +1013,63 @@ class PrefViewer extends HTMLElement {
|
|
|
1023
1013
|
}
|
|
1024
1014
|
|
|
1025
1015
|
loadModel(model) {
|
|
1026
|
-
|
|
1016
|
+
console.log("PrefViewer: loadModel()", model);
|
|
1027
1017
|
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
1028
1018
|
if (!model) {
|
|
1029
|
-
|
|
1019
|
+
console.warn("PrefViewer: loadModel() no model provided");
|
|
1030
1020
|
return false;
|
|
1031
1021
|
}
|
|
1032
1022
|
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;
|
|
1023
|
+
this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
|
|
1035
1024
|
this.initialized && this.#loadContainers(true, false, false);
|
|
1036
1025
|
}
|
|
1037
1026
|
|
|
1038
1027
|
loadScene(scene) {
|
|
1039
|
-
|
|
1028
|
+
console.log("PrefViewer: loadScene()", scene);
|
|
1040
1029
|
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
1041
1030
|
if (!scene) {
|
|
1042
|
-
|
|
1031
|
+
console.warn("PrefViewer: loadScene() no scene provided");
|
|
1043
1032
|
return false;
|
|
1044
1033
|
}
|
|
1045
1034
|
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;
|
|
1035
|
+
this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
|
|
1048
1036
|
this.initialized && this.#loadContainers(false, true, false);
|
|
1049
1037
|
}
|
|
1050
1038
|
|
|
1051
1039
|
showModel() {
|
|
1040
|
+
console.log("PrefViewer: showModel()");
|
|
1052
1041
|
this.#data.containers.model.show = true;
|
|
1053
1042
|
this.#addContainer(this.#data.containers.model);
|
|
1054
1043
|
}
|
|
1055
1044
|
|
|
1056
1045
|
hideModel() {
|
|
1046
|
+
console.log("PrefViewer: hideModel()");
|
|
1057
1047
|
this.#data.containers.model.show = false;
|
|
1058
1048
|
this.#removeContainer(this.#data.containers.model);
|
|
1059
1049
|
}
|
|
1060
1050
|
|
|
1061
1051
|
showScene() {
|
|
1052
|
+
console.log("PrefViewer: showScene()");
|
|
1062
1053
|
this.#data.containers.environment.show = true;
|
|
1063
1054
|
this.#addContainer(this.#data.containers.environment);
|
|
1064
1055
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1065
1056
|
}
|
|
1066
1057
|
|
|
1067
1058
|
hideScene() {
|
|
1059
|
+
console.log("PrefViewer: hideScene()");
|
|
1068
1060
|
this.#data.containers.environment.show = false;
|
|
1069
1061
|
this.#removeContainer(this.#data.containers.environment);
|
|
1070
1062
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1071
1063
|
}
|
|
1072
1064
|
|
|
1073
1065
|
downloadModelGLB() {
|
|
1066
|
+
console.log("PrefViewer: downloadModelGLB()");
|
|
1074
1067
|
const fileName = "model";
|
|
1075
|
-
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, {
|
|
1076
|
-
exportWithoutWaitingForScene: true,
|
|
1077
|
-
}).then((glb) => glb.downloadFiles());
|
|
1068
|
+
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
1078
1069
|
}
|
|
1079
1070
|
|
|
1080
1071
|
downloadModelUSDZ() {
|
|
1072
|
+
console.log("PrefViewer: downloadModelUSDZ()");
|
|
1081
1073
|
const fileName = "model";
|
|
1082
1074
|
USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
|
|
1083
1075
|
if (response) {
|
|
@@ -1087,6 +1079,7 @@ class PrefViewer extends HTMLElement {
|
|
|
1087
1079
|
}
|
|
1088
1080
|
|
|
1089
1081
|
downloadModelAndSceneUSDZ() {
|
|
1082
|
+
console.log("PrefViewer: downloadModelAndSceneUSDZ()");
|
|
1090
1083
|
const fileName = "scene";
|
|
1091
1084
|
USDZExportAsync(this.#scene).then((response) => {
|
|
1092
1085
|
if (response) {
|
|
@@ -1096,10 +1089,9 @@ class PrefViewer extends HTMLElement {
|
|
|
1096
1089
|
}
|
|
1097
1090
|
|
|
1098
1091
|
downloadModelAndSceneGLB() {
|
|
1092
|
+
console.log("PrefViewer: downloadModelAndSceneGLB()");
|
|
1099
1093
|
const fileName = "scene";
|
|
1100
|
-
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) =>
|
|
1101
|
-
glb.downloadFiles()
|
|
1102
|
-
);
|
|
1094
|
+
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
1103
1095
|
}
|
|
1104
1096
|
}
|
|
1105
1097
|
|