@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.
- package/package.json +1 -1
- package/src/index.js +201 -252
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,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
|
|
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() {
|
|
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
|
-
|
|
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,67 @@ 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
|
-
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
791
|
-
container.assetContainer.materials?.forEach(
|
|
759
|
+
// Opcional: limitar luces por material para ganar margen
|
|
760
|
+
container.assetContainer.materials?.forEach(m => {
|
|
792
761
|
if ("maxSimultaneousLights" in m) {
|
|
793
|
-
m.maxSimultaneousLights =
|
|
762
|
+
m.maxSimultaneousLights = 2; // 2–3 suele ir bien
|
|
794
763
|
}
|
|
795
764
|
});
|
|
796
765
|
|
|
797
|
-
// 3) sombras
|
|
798
|
-
container.assetContainer.meshes.forEach(
|
|
799
|
-
mesh.receiveShadows =
|
|
800
|
-
|
|
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
|
-
|
|
781
|
+
console.log("PrefViewer: #loadAssetContainer() START", { name: container?.name, storage: container?.storage });
|
|
782
|
+
let storage = container?.storage;
|
|
817
783
|
|
|
818
784
|
if (!storage) {
|
|
819
|
-
|
|
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
|
-
|
|
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
|
-
|
|
800
|
+
console.log("PrefViewer: DB entry changed", container.changed);
|
|
842
801
|
}
|
|
843
802
|
}
|
|
844
803
|
|
|
845
804
|
if (!source) {
|
|
846
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
929
|
-
|
|
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
|
-
|
|
939
|
-
|
|
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
|
-
|
|
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
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
|
|
927
|
+
console.log("PrefViewer: loadConfig()", config);
|
|
979
928
|
config = typeof config === "string" ? JSON.parse(config) : config;
|
|
980
929
|
if (!config) {
|
|
981
|
-
|
|
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
|
-
|
|
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
|
-
|
|
973
|
+
console.log("PrefViewer: loadModel()", model);
|
|
1027
974
|
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
1028
975
|
if (!model) {
|
|
1029
|
-
|
|
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
|
-
|
|
985
|
+
console.log("PrefViewer: loadScene()", scene);
|
|
1040
986
|
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
1041
987
|
if (!scene) {
|
|
1042
|
-
|
|
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
|
|