@preference-sl/pref-viewer 2.10.0-beta.8 → 2.10.0-beta.9
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 +318 -18
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -47,7 +47,13 @@ import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompre
|
|
|
47
47
|
import { initDb, loadModel } from "./gltf-storage.js";
|
|
48
48
|
|
|
49
49
|
class PrefViewer extends HTMLElement {
|
|
50
|
+
static LOG_PREFIX = "[PrefViewer]";
|
|
51
|
+
static LOG_LEVELS = { none: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
52
|
+
// Por defecto NO loggear: control únicamente vía atributo `log-level`
|
|
53
|
+
static DEFAULT_LOG_LEVEL = "none";
|
|
54
|
+
|
|
50
55
|
#initialized = false;
|
|
56
|
+
#logLevel = PrefViewer.DEFAULT_LOG_LEVEL;
|
|
51
57
|
|
|
52
58
|
#data = {
|
|
53
59
|
containers: {
|
|
@@ -113,6 +119,67 @@ class PrefViewer extends HTMLElement {
|
|
|
113
119
|
},
|
|
114
120
|
};
|
|
115
121
|
|
|
122
|
+
#log(level, message, context) {
|
|
123
|
+
const levels = PrefViewer.LOG_LEVELS;
|
|
124
|
+
const current = levels[this.#logLevel] ?? levels[PrefViewer.DEFAULT_LOG_LEVEL];
|
|
125
|
+
const incoming = levels[level] ?? levels.info;
|
|
126
|
+
if (incoming > current || current === levels.none) return;
|
|
127
|
+
|
|
128
|
+
const logger = console[level] ?? console.log;
|
|
129
|
+
if (context !== undefined) {
|
|
130
|
+
logger(`${PrefViewer.LOG_PREFIX}: ${message}`, context);
|
|
131
|
+
} else {
|
|
132
|
+
logger(`${PrefViewer.LOG_PREFIX}: ${message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#setLogLevel(level) {
|
|
137
|
+
const value = String(level || "").toLowerCase();
|
|
138
|
+
this.#logLevel = (value in PrefViewer.LOG_LEVELS) ? value : PrefViewer.DEFAULT_LOG_LEVEL;
|
|
139
|
+
this.#logInfo("Log level set", { level: this.#logLevel });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#logDebug(message, context) {
|
|
143
|
+
this.#log("debug", message, context);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#logInfo(message, context) {
|
|
147
|
+
this.#log("info", message, context);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#logWarn(message, context) {
|
|
151
|
+
this.#log("warn", message, context);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#logError(message, context) {
|
|
155
|
+
this.#log("error", message, context);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#summarizeValue(value) {
|
|
159
|
+
if (typeof value === "string" && value.length > 150) {
|
|
160
|
+
return `${value.slice(0, 150)}… (${value.length} chars)`;
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#describeStorage(storage) {
|
|
166
|
+
if (!storage) {
|
|
167
|
+
return "none";
|
|
168
|
+
}
|
|
169
|
+
if (storage.db && storage.table && storage.id) {
|
|
170
|
+
return `IndexedDB(${storage.db}/${storage.table}#${storage.id})`;
|
|
171
|
+
}
|
|
172
|
+
if (typeof storage.url === "string") {
|
|
173
|
+
return storage.url.startsWith("data:") ? "data-url" : storage.url;
|
|
174
|
+
}
|
|
175
|
+
return "unknown";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static get observedAttributes() {
|
|
179
|
+
// Añadimos "log-level" para controlar logs fuera del objeto config
|
|
180
|
+
return ["config", "model", "scene", "show-model", "show-scene", "log-level"];
|
|
181
|
+
}
|
|
182
|
+
|
|
116
183
|
// DOM elements
|
|
117
184
|
#wrapper = null;
|
|
118
185
|
#canvas = null;
|
|
@@ -129,6 +196,7 @@ class PrefViewer extends HTMLElement {
|
|
|
129
196
|
|
|
130
197
|
constructor() {
|
|
131
198
|
super();
|
|
199
|
+
this.#logDebug("Constructing PrefViewer instance");
|
|
132
200
|
this.attachShadow({ mode: "open" });
|
|
133
201
|
this.#createCanvas();
|
|
134
202
|
this.#wrapCanvas();
|
|
@@ -144,11 +212,13 @@ class PrefViewer extends HTMLElement {
|
|
|
144
212
|
};
|
|
145
213
|
}
|
|
146
214
|
|
|
147
|
-
static get observedAttributes() {
|
|
148
|
-
return ["config", "model", "scene", "show-model", "show-scene"];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
215
|
attributeChangedCallback(name, _old, value) {
|
|
216
|
+
if (name === "log-level") {
|
|
217
|
+
this.#setLogLevel(value);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.#logDebug("Attribute change detected", { name, value: this.#summarizeValue(value) });
|
|
152
222
|
let data = null;
|
|
153
223
|
switch (name) {
|
|
154
224
|
case "config":
|
|
@@ -162,6 +232,7 @@ class PrefViewer extends HTMLElement {
|
|
|
162
232
|
break;
|
|
163
233
|
case "show-model":
|
|
164
234
|
data = value.toLowerCase?.() === "true";
|
|
235
|
+
this.#logDebug("Toggling model visibility from attribute", { visible: data, initialized: this.#initialized });
|
|
165
236
|
if (this.#initialized) {
|
|
166
237
|
data ? this.showModel() : this.hideModel();
|
|
167
238
|
} else {
|
|
@@ -170,6 +241,7 @@ class PrefViewer extends HTMLElement {
|
|
|
170
241
|
break;
|
|
171
242
|
case "show-scene":
|
|
172
243
|
data = value.toLowerCase?.() === "true";
|
|
244
|
+
this.#logDebug("Toggling scene visibility from attribute", { visible: data, initialized: this.#initialized });
|
|
173
245
|
if (this.#initialized) {
|
|
174
246
|
data ? this.showScene() : this.hideScene();
|
|
175
247
|
} else {
|
|
@@ -182,7 +254,7 @@ class PrefViewer extends HTMLElement {
|
|
|
182
254
|
connectedCallback() {
|
|
183
255
|
if (!this.hasAttribute("config")) {
|
|
184
256
|
const error = 'PrefViewer: provide "models" as array of model and environment';
|
|
185
|
-
|
|
257
|
+
this.#logError("Missing required config attribute", { error });
|
|
186
258
|
this.dispatchEvent(
|
|
187
259
|
new CustomEvent("model-error", {
|
|
188
260
|
detail: { error: new Error(error) },
|
|
@@ -193,18 +265,22 @@ class PrefViewer extends HTMLElement {
|
|
|
193
265
|
return false;
|
|
194
266
|
}
|
|
195
267
|
|
|
268
|
+
this.#logDebug("Connected to DOM, initializing Babylon");
|
|
196
269
|
this.#initializeBabylon();
|
|
197
270
|
this.#loadContainers(true, true, true);
|
|
198
271
|
this.#initialized = true;
|
|
272
|
+
this.#logInfo("Initialization completed", { initialized: this.#initialized });
|
|
199
273
|
}
|
|
200
274
|
|
|
201
275
|
disconnectedCallback() {
|
|
276
|
+
this.#logDebug("Disconnected from DOM, disposing resources");
|
|
202
277
|
this.#disposeEngine();
|
|
203
278
|
this.#canvasResizeObserver.disconnect();
|
|
204
279
|
}
|
|
205
280
|
|
|
206
281
|
// Web Component
|
|
207
282
|
#createCanvas() {
|
|
283
|
+
this.#logDebug("Creating rendering canvas");
|
|
208
284
|
this.#canvas = document.createElement("canvas");
|
|
209
285
|
Object.assign(this.#canvas.style, {
|
|
210
286
|
width: "100%",
|
|
@@ -212,9 +288,11 @@ class PrefViewer extends HTMLElement {
|
|
|
212
288
|
display: "block",
|
|
213
289
|
outline: "none",
|
|
214
290
|
});
|
|
291
|
+
this.#logDebug("Canvas element created and styled");
|
|
215
292
|
}
|
|
216
293
|
|
|
217
294
|
#wrapCanvas() {
|
|
295
|
+
this.#logDebug("Wrapping canvas inside container div");
|
|
218
296
|
this.#wrapper = document.createElement("div");
|
|
219
297
|
Object.assign(this.#wrapper.style, {
|
|
220
298
|
width: "100%",
|
|
@@ -223,20 +301,24 @@ class PrefViewer extends HTMLElement {
|
|
|
223
301
|
});
|
|
224
302
|
this.#wrapper.appendChild(this.#canvas);
|
|
225
303
|
this.shadowRoot.append(this.#wrapper);
|
|
304
|
+
this.#logDebug("Canvas wrapper appended to shadow DOM");
|
|
226
305
|
}
|
|
227
306
|
|
|
228
307
|
// Data
|
|
229
308
|
#checkCameraChanged(options) {
|
|
230
309
|
if (!options || !options.camera) {
|
|
310
|
+
this.#logDebug("Camera options not provided or unchanged");
|
|
231
311
|
return false;
|
|
232
312
|
}
|
|
233
313
|
this.#data.options.camera.changed = options.camera && options.camera !== this.#data.options.camera.value ? true : false;
|
|
234
314
|
this.#data.options.camera.value = this.#data.options.camera.changed ? options.camera : this.#data.options.camera.value;
|
|
315
|
+
this.#logDebug("Camera option processed", { changed: this.#data.options.camera.changed, value: this.#data.options.camera.value });
|
|
235
316
|
return this.#data.options.camera.changed;
|
|
236
317
|
}
|
|
237
318
|
|
|
238
319
|
#checkMaterialsChanged(options) {
|
|
239
320
|
if (!options) {
|
|
321
|
+
this.#logDebug("Material options not provided");
|
|
240
322
|
return false;
|
|
241
323
|
}
|
|
242
324
|
let someChanged = false;
|
|
@@ -246,22 +328,32 @@ class PrefViewer extends HTMLElement {
|
|
|
246
328
|
this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : this.#data.options.materials[material].value;
|
|
247
329
|
someChanged = someChanged || this.#data.options.materials[material].changed;
|
|
248
330
|
});
|
|
331
|
+
this.#logDebug("Material options processed", {
|
|
332
|
+
changed: someChanged,
|
|
333
|
+
values: Object.entries(this.#data.options.materials).reduce((acc, [key, entry]) => {
|
|
334
|
+
acc[key] = { value: entry.value, changed: entry.changed };
|
|
335
|
+
return acc;
|
|
336
|
+
}, {}),
|
|
337
|
+
});
|
|
249
338
|
return someChanged;
|
|
250
339
|
}
|
|
251
340
|
|
|
252
341
|
#storeChangedFlagsForContainer(container) {
|
|
253
342
|
container.timestamp = container.changed.timestamp;
|
|
254
343
|
container.size = container.changed.size;
|
|
344
|
+
this.#logDebug("Stored change flags for container", { name: container.name, timestamp: container.timestamp, size: container.size });
|
|
255
345
|
}
|
|
256
346
|
|
|
257
347
|
#resetChangedFlags() {
|
|
258
348
|
Object.values(this.#data.containers).forEach((container) => (container.changed = false));
|
|
259
349
|
Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
|
|
260
350
|
this.#data.options.camera.changed = false;
|
|
351
|
+
this.#logDebug("Reset change flags across containers and options");
|
|
261
352
|
}
|
|
262
353
|
|
|
263
354
|
// Babylon.js
|
|
264
355
|
async #initializeBabylon() {
|
|
356
|
+
this.#logInfo("Initializing Babylon engine and scene");
|
|
265
357
|
this.#engine = new Engine(this.#canvas, true, { alpha: true });
|
|
266
358
|
this.#scene = new Scene(this.#engine);
|
|
267
359
|
this.#scene.clearColor = new Color4(1, 1, 1, 1);
|
|
@@ -271,26 +363,32 @@ class PrefViewer extends HTMLElement {
|
|
|
271
363
|
|
|
272
364
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
273
365
|
this.#canvasResizeObserver.observe(this.#canvas);
|
|
366
|
+
this.#logDebug("Engine render loop started and resize observer attached");
|
|
274
367
|
|
|
275
368
|
await this.#createXRExperience();
|
|
369
|
+
this.#logInfo("Babylon initialization finished", { xrEnabled: !!this.#XRExperience });
|
|
276
370
|
}
|
|
277
371
|
|
|
278
372
|
addStylesToARButton() {
|
|
373
|
+
this.#logDebug("Adding styles to AR button");
|
|
279
374
|
const css = '.babylonVRicon { color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-image: url(data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%222048%22%20height%3D%221152%22%20viewBox%3D%220%200%202048%201152%22%20version%3D%221.1%22%3E%3Cpath%20transform%3D%22rotate%28180%201024%2C576.0000000000001%29%22%20d%3D%22m1109%2C896q17%2C0%2030%2C-12t13%2C-30t-12.5%2C-30.5t-30.5%2C-12.5l-170%2C0q-18%2C0%20-30.5%2C12.5t-12.5%2C30.5t13%2C30t30%2C12l170%2C0zm-85%2C256q59%2C0%20132.5%2C-1.5t154.5%2C-5.5t164.5%2C-11.5t163%2C-20t150%2C-30t124.5%2C-41.5q23%2C-11%2042%2C-24t38%2C-30q27%2C-25%2041%2C-61.5t14%2C-72.5l0%2C-257q0%2C-123%20-47%2C-232t-128%2C-190t-190%2C-128t-232%2C-47l-81%2C0q-37%2C0%20-68.5%2C14t-60.5%2C34.5t-55.5%2C45t-53%2C45t-53%2C34.5t-55.5%2C14t-55.5%2C-14t-53%2C-34.5t-53%2C-45t-55.5%2C-45t-60.5%2C-34.5t-68.5%2C-14l-81%2C0q-123%2C0%20-232%2C47t-190%2C128t-128%2C190t-47%2C232l0%2C257q0%2C68%2038%2C115t97%2C73q54%2C24%20124.5%2C41.5t150%2C30t163%2C20t164.5%2C11.5t154.5%2C5.5t132.5%2C1.5zm939%2C-298q0%2C39%20-24.5%2C67t-58.5%2C42q-54%2C23%20-122%2C39.5t-143.5%2C28t-155.5%2C19t-157%2C11t-148.5%2C5t-129.5%2C1.5q-59%2C0%20-130%2C-1.5t-148%2C-5t-157%2C-11t-155.5%2C-19t-143.5%2C-28t-122%2C-39.5q-34%2C-14%20-58.5%2C-42t-24.5%2C-67l0%2C-257q0%2C-106%2040.5%2C-199t110%2C-162.5t162.5%2C-109.5t199%2C-40l81%2C0q27%2C0%2052%2C14t50%2C34.5t51%2C44.5t55.5%2C44.5t63.5%2C34.5t74%2C14t74%2C-14t63.5%2C-34.5t55.5%2C-44.5t51%2C-44.5t50%2C-34.5t52%2C-14l14%2C0q37%2C0%2070%2C0.5t64.5%2C4.5t63.5%2C12t68%2C23q71%2C30%20128.5%2C78.5t98.5%2C110t63.5%2C133.5t22.5%2C149l0%2C257z%22%20fill%3D%22white%22%20/%3E%3C/svg%3E%0A); background-size: 80%; background-repeat:no-repeat; background-position: center; border: none; outline: none; transition: transform 0.125s ease-out } .babylonVRicon:hover { transform: scale(1.05) } .babylonVRicon:active {background-color: rgba(51,51,51,1) } .babylonVRicon:focus {background-color: rgba(51,51,51,1) }.babylonVRicon.vrdisplaypresenting { background-image: none;} .vrdisplaypresenting::after { content: "EXIT"} .xr-error::after { content: "ERROR"}';
|
|
280
375
|
const style = document.createElement("style");
|
|
281
376
|
style.appendChild(document.createTextNode(css));
|
|
282
377
|
this.#wrapper.appendChild(style);
|
|
378
|
+
this.#logDebug("AR button styles applied");
|
|
283
379
|
}
|
|
284
380
|
|
|
285
381
|
async #createXRExperience() {
|
|
286
382
|
if (this.#XRExperience) {
|
|
383
|
+
this.#logDebug("XR experience already created, skipping");
|
|
287
384
|
return true;
|
|
288
385
|
}
|
|
289
386
|
|
|
387
|
+
this.#logDebug("Attempting to create XR experience");
|
|
290
388
|
const sessionMode = "immersive-ar";
|
|
291
389
|
const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
|
|
292
390
|
if (!sessionSupported) {
|
|
293
|
-
|
|
391
|
+
this.#logInfo("WebXR session mode not supported", { sessionMode });
|
|
294
392
|
return false;
|
|
295
393
|
}
|
|
296
394
|
|
|
@@ -309,6 +407,7 @@ class PrefViewer extends HTMLElement {
|
|
|
309
407
|
};
|
|
310
408
|
|
|
311
409
|
this.#XRExperience = await WebXRDefaultExperience.CreateAsync(this.#scene, options);
|
|
410
|
+
this.#logInfo("XR experience created successfully", { sessionMode });
|
|
312
411
|
|
|
313
412
|
const featuresManager = this.#XRExperience.baseExperience.featuresManager;
|
|
314
413
|
featuresManager.enableFeature(WebXRFeatureName.TELEPORTATION, "stable", {
|
|
@@ -316,24 +415,32 @@ class PrefViewer extends HTMLElement {
|
|
|
316
415
|
floorMeshes: [ground],
|
|
317
416
|
timeToTeleport: 1500,
|
|
318
417
|
});
|
|
418
|
+
this.#logDebug("XR teleportation feature enabled");
|
|
319
419
|
|
|
320
420
|
this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
|
|
321
421
|
// Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
|
|
322
422
|
this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
|
|
323
423
|
this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
|
|
324
424
|
this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(this.#XRExperience.baseExperience.camera);
|
|
425
|
+
this.#logDebug("XR session ready and camera transformed");
|
|
325
426
|
});
|
|
326
427
|
|
|
327
428
|
this.addStylesToARButton();
|
|
328
429
|
} catch (error) {
|
|
329
|
-
|
|
430
|
+
this.#logWarn("Failed to create XR experience", { error });
|
|
330
431
|
this.#XRExperience = null;
|
|
331
432
|
}
|
|
332
433
|
}
|
|
333
434
|
|
|
334
|
-
#canvasResizeObserver = new ResizeObserver(() =>
|
|
435
|
+
#canvasResizeObserver = new ResizeObserver(() => {
|
|
436
|
+
if (this.#engine) {
|
|
437
|
+
this.#logDebug("Resize observer triggered");
|
|
438
|
+
this.#engine.resize();
|
|
439
|
+
}
|
|
440
|
+
});
|
|
335
441
|
|
|
336
442
|
#createCamera() {
|
|
443
|
+
this.#logDebug("Creating default camera");
|
|
337
444
|
this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
|
|
338
445
|
this.#camera.upperBetaLimit = Math.PI * 0.48;
|
|
339
446
|
this.#camera.lowerBetaLimit = Math.PI * 0.25;
|
|
@@ -342,9 +449,16 @@ class PrefViewer extends HTMLElement {
|
|
|
342
449
|
this.#camera.metadata = { locked: false }
|
|
343
450
|
this.#camera = this.#camera;
|
|
344
451
|
this.#camera.attachControl(this.#canvas, true);
|
|
452
|
+
this.#logDebug("Camera created", {
|
|
453
|
+
upperBetaLimit: this.#camera.upperBetaLimit,
|
|
454
|
+
lowerBetaLimit: this.#camera.lowerBetaLimit,
|
|
455
|
+
lowerRadiusLimit: this.#camera.lowerRadiusLimit,
|
|
456
|
+
upperRadiusLimit: this.#camera.upperRadiusLimit,
|
|
457
|
+
});
|
|
345
458
|
}
|
|
346
459
|
|
|
347
460
|
#createLights() {
|
|
461
|
+
this.#logDebug("Creating scene lights");
|
|
348
462
|
// 1) Stronger ambient fill
|
|
349
463
|
this.#hemiLight = new HemisphericLight("hemiLight", new Vector3(-10, 10, -10), this.#scene);
|
|
350
464
|
this.#hemiLight.intensity = 0.6;
|
|
@@ -364,32 +478,52 @@ class PrefViewer extends HTMLElement {
|
|
|
364
478
|
this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
|
|
365
479
|
this.#cameraLight.parent = this.#camera;
|
|
366
480
|
this.#cameraLight.intensity = 0.3;
|
|
481
|
+
this.#logDebug("Scene lights configured", {
|
|
482
|
+
hemiIntensity: this.#hemiLight.intensity,
|
|
483
|
+
dirIntensity: this.#dirLight.intensity,
|
|
484
|
+
pointIntensity: this.#cameraLight.intensity,
|
|
485
|
+
shadowKernel: this.#shadowGen.blurKernel,
|
|
486
|
+
});
|
|
367
487
|
}
|
|
368
488
|
|
|
369
489
|
#setupInteraction() {
|
|
490
|
+
this.#logDebug("Setting up canvas interaction listeners");
|
|
370
491
|
this.#canvas.addEventListener("wheel", (event) => {
|
|
371
492
|
if (!this.#scene || !this.#camera) {
|
|
493
|
+
this.#logWarn("Wheel interaction ignored; scene or camera missing");
|
|
372
494
|
return false;
|
|
373
495
|
}
|
|
374
496
|
//const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
375
497
|
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
376
498
|
if (!this.#scene.activeCamera.metadata?.locked) {
|
|
377
499
|
this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
|
|
500
|
+
this.#logDebug("Processed wheel interaction", {
|
|
501
|
+
deltaY: event.deltaY,
|
|
502
|
+
inertialRadiusOffset: this.#scene.activeCamera.inertialRadiusOffset,
|
|
503
|
+
});
|
|
504
|
+
} else {
|
|
505
|
+
this.#logDebug("Wheel interaction ignored because camera is locked");
|
|
378
506
|
}
|
|
379
507
|
event.preventDefault();
|
|
380
508
|
});
|
|
381
509
|
}
|
|
382
510
|
|
|
383
511
|
#disposeEngine() {
|
|
384
|
-
if (!this.#engine)
|
|
512
|
+
if (!this.#engine) {
|
|
513
|
+
this.#logDebug("Dispose engine called but engine already null");
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
this.#logDebug("Disposing Babylon resources");
|
|
385
517
|
this.#engine.dispose();
|
|
386
518
|
this.#engine = this.#scene = this.#camera = null;
|
|
387
519
|
this.#hemiLight = this.#dirLight = this.#cameraLight = null;
|
|
388
520
|
this.#shadowGen = null;
|
|
521
|
+
this.#logDebug("Babylon resources disposed");
|
|
389
522
|
}
|
|
390
523
|
|
|
391
524
|
// Utility methods for loading gltf/glb
|
|
392
525
|
async #getServerFileDataHeader(uri) {
|
|
526
|
+
this.#logDebug("Requesting server file header", { uri });
|
|
393
527
|
return new Promise((resolve) => {
|
|
394
528
|
const xhr = new XMLHttpRequest();
|
|
395
529
|
xhr.open("HEAD", uri, true);
|
|
@@ -398,12 +532,15 @@ class PrefViewer extends HTMLElement {
|
|
|
398
532
|
if (xhr.status === 200) {
|
|
399
533
|
const size = parseInt(xhr.getResponseHeader("Content-Length"));
|
|
400
534
|
const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
|
|
535
|
+
this.#logDebug("Received server file header", { uri, size, timestamp });
|
|
401
536
|
resolve(size, timestamp);
|
|
402
537
|
} else {
|
|
538
|
+
this.#logWarn("Failed to retrieve server file header", { uri, status: xhr.status });
|
|
403
539
|
resolve(0, null);
|
|
404
540
|
}
|
|
405
541
|
};
|
|
406
542
|
xhr.onerror = () => {
|
|
543
|
+
this.#logError("Error requesting server file header", { uri });
|
|
407
544
|
resolve(0, null);
|
|
408
545
|
};
|
|
409
546
|
xhr.send();
|
|
@@ -412,11 +549,14 @@ class PrefViewer extends HTMLElement {
|
|
|
412
549
|
|
|
413
550
|
#transformUrl(url) {
|
|
414
551
|
return new Promise((resolve) => {
|
|
415
|
-
|
|
552
|
+
const transformed = url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/");
|
|
553
|
+
this.#logDebug("Transformed URL", { original: url, transformed });
|
|
554
|
+
resolve(transformed);
|
|
416
555
|
});
|
|
417
556
|
}
|
|
418
557
|
|
|
419
558
|
#decodeBase64(base64) {
|
|
559
|
+
this.#logDebug("Decoding Base64 payload", { length: base64 ? base64.length : 0 });
|
|
420
560
|
const [, payload] = base64.split(",");
|
|
421
561
|
const raw = payload || base64;
|
|
422
562
|
let decoded = "";
|
|
@@ -426,44 +566,62 @@ class PrefViewer extends HTMLElement {
|
|
|
426
566
|
try {
|
|
427
567
|
decoded = atob(raw);
|
|
428
568
|
} catch {
|
|
569
|
+
this.#logWarn("Failed to decode Base64 string");
|
|
429
570
|
return { blob, extension, size };
|
|
430
571
|
}
|
|
431
572
|
let isJson = false;
|
|
432
573
|
try {
|
|
433
574
|
JSON.parse(decoded);
|
|
434
575
|
isJson = true;
|
|
435
|
-
} catch {}
|
|
576
|
+
} catch { }
|
|
436
577
|
extension = isJson ? ".gltf" : ".glb";
|
|
437
578
|
const type = isJson ? "model/gltf+json" : "model/gltf-binary";
|
|
438
579
|
const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
|
|
439
580
|
blob = new Blob([array], { type });
|
|
581
|
+
this.#logDebug("Decoded Base64 payload", { isJson, size, extension });
|
|
440
582
|
return { blob, extension, size };
|
|
441
583
|
}
|
|
442
584
|
|
|
443
585
|
async #initStorage(db, table) {
|
|
586
|
+
this.#logDebug("Initializing storage access", { db, table });
|
|
444
587
|
if (window.gltfDB && window.gltfDB.name === db && window.gltfDB.objectStoreNames.contains(table)) {
|
|
588
|
+
this.#logDebug("Reusing existing IndexedDB connection", { db, table });
|
|
445
589
|
return true;
|
|
446
590
|
}
|
|
447
591
|
await initDb(db, table);
|
|
592
|
+
this.#logDebug("IndexedDB initialized", { db, table });
|
|
448
593
|
}
|
|
449
594
|
|
|
450
595
|
// Methods for managing Asset Containers
|
|
451
596
|
#setVisibilityOfWallAndFloorInModel(show) {
|
|
452
597
|
if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
|
|
598
|
+
this.#logDebug("Skipping wall/floor visibility update", {
|
|
599
|
+
hasModel: !!this.#data.containers.model.assetContainer,
|
|
600
|
+
modelVisible: this.#data.containers.model.visible,
|
|
601
|
+
});
|
|
453
602
|
return false;
|
|
454
603
|
}
|
|
455
604
|
show = show !== undefined ? show : this.#data.containers.environment.visible;
|
|
456
605
|
const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
|
|
457
606
|
this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
|
|
607
|
+
this.#logDebug("Updated wall and floor visibility", { show });
|
|
458
608
|
}
|
|
459
609
|
|
|
460
610
|
#setOptionsMaterial(optionMaterial) {
|
|
461
611
|
if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
|
|
612
|
+
this.#logWarn("Material option invalid", { optionMaterial });
|
|
462
613
|
return false;
|
|
463
614
|
}
|
|
464
615
|
|
|
616
|
+
this.#logDebug("Applying material option", {
|
|
617
|
+
prefix: optionMaterial.prefix,
|
|
618
|
+
value: optionMaterial.value,
|
|
619
|
+
changed: optionMaterial.changed,
|
|
620
|
+
});
|
|
621
|
+
|
|
465
622
|
const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
|
|
466
623
|
if (!material) {
|
|
624
|
+
this.#logWarn("Requested material not found", { value: optionMaterial.value });
|
|
467
625
|
return false;
|
|
468
626
|
}
|
|
469
627
|
|
|
@@ -475,6 +633,7 @@ class PrefViewer extends HTMLElement {
|
|
|
475
633
|
containers.push(this.#data.containers.environment.assetContainer);
|
|
476
634
|
}
|
|
477
635
|
if (containers.length === 0) {
|
|
636
|
+
this.#logDebug("No containers required material update", { prefix: optionMaterial.prefix });
|
|
478
637
|
return false;
|
|
479
638
|
}
|
|
480
639
|
|
|
@@ -485,36 +644,61 @@ class PrefViewer extends HTMLElement {
|
|
|
485
644
|
.forEach((mesh) => {
|
|
486
645
|
mesh.material = material;
|
|
487
646
|
someSetted = true;
|
|
647
|
+
this.#logDebug("Assigned material to mesh", { mesh: mesh.name, material: material.name });
|
|
488
648
|
})
|
|
489
649
|
);
|
|
490
650
|
|
|
651
|
+
this.#logDebug("Material option applied", {
|
|
652
|
+
prefix: optionMaterial.prefix,
|
|
653
|
+
value: optionMaterial.value,
|
|
654
|
+
applied: someSetted,
|
|
655
|
+
containers: containers.map((container) => container.name),
|
|
656
|
+
});
|
|
657
|
+
|
|
491
658
|
return someSetted;
|
|
492
659
|
}
|
|
493
660
|
|
|
494
661
|
#setOptionsMaterials() {
|
|
662
|
+
if (!this.#data.containers.materials.assetContainer) {
|
|
663
|
+
this.#logDebug("Skipping materials update; materials container is missing");
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
this.#logDebug("Applying material options batch");
|
|
495
668
|
let someSetted = false;
|
|
496
669
|
Object.values(this.#data.options.materials).forEach((material) => {
|
|
497
670
|
let settedMaterial = this.#setOptionsMaterial(material);
|
|
498
671
|
someSetted = someSetted || settedMaterial;
|
|
499
672
|
});
|
|
673
|
+
this.#logDebug("Material batch processing finished", { appliedAny: someSetted });
|
|
500
674
|
return someSetted;
|
|
501
675
|
}
|
|
502
676
|
|
|
503
677
|
#setOptionsCamera() {
|
|
504
678
|
if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.assetContainer.changed)) {
|
|
679
|
+
this.#logDebug("No camera option update necessary", {
|
|
680
|
+
value: this.#data.options.camera.value,
|
|
681
|
+
changed: this.#data.options.camera.changed,
|
|
682
|
+
modelChanged: this.#data.containers.model.assetContainer?.changed,
|
|
683
|
+
});
|
|
505
684
|
return false;
|
|
506
685
|
}
|
|
507
686
|
|
|
508
687
|
let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
|
|
509
688
|
if (!camera) {
|
|
689
|
+
this.#logWarn("Requested camera not found", { name: this.#data.options.camera.value });
|
|
510
690
|
return false;
|
|
511
691
|
}
|
|
512
692
|
|
|
513
693
|
camera.metadata = { locked: this.#data.options.camera.locked };
|
|
514
694
|
if (!this.#data.options.camera.locked) {
|
|
515
695
|
camera.attachControl(this.#canvas, true);
|
|
696
|
+
this.#logDebug("Attached unlocked camera control", { camera: camera.name });
|
|
697
|
+
} else {
|
|
698
|
+
this.#logDebug("Using locked camera configuration", { camera: camera.name });
|
|
516
699
|
}
|
|
517
700
|
this.#scene.activeCamera = camera;
|
|
701
|
+
this.#logDebug("Active camera set", { camera: camera.name });
|
|
518
702
|
|
|
519
703
|
return true;
|
|
520
704
|
}
|
|
@@ -523,6 +707,14 @@ class PrefViewer extends HTMLElement {
|
|
|
523
707
|
if (container.assetContainer && !container.visible && container.show) {
|
|
524
708
|
container.assetContainer.addAllToScene();
|
|
525
709
|
container.visible = true;
|
|
710
|
+
this.#logDebug("Added container to scene", { name: container.name });
|
|
711
|
+
} else {
|
|
712
|
+
this.#logDebug("Skipped adding container", {
|
|
713
|
+
name: container?.name,
|
|
714
|
+
hasAssetContainer: !!container?.assetContainer,
|
|
715
|
+
visible: container?.visible,
|
|
716
|
+
show: container?.show,
|
|
717
|
+
});
|
|
526
718
|
}
|
|
527
719
|
}
|
|
528
720
|
|
|
@@ -530,40 +722,67 @@ class PrefViewer extends HTMLElement {
|
|
|
530
722
|
if (container.assetContainer && container.visible) {
|
|
531
723
|
container.assetContainer.removeAllFromScene();
|
|
532
724
|
container.visible = false;
|
|
725
|
+
this.#logDebug("Removed container from scene", { name: container.name });
|
|
726
|
+
} else {
|
|
727
|
+
this.#logDebug("Skipped removing container", {
|
|
728
|
+
name: container?.name,
|
|
729
|
+
hasAssetContainer: !!container?.assetContainer,
|
|
730
|
+
visible: container?.visible,
|
|
731
|
+
});
|
|
533
732
|
}
|
|
534
733
|
}
|
|
535
734
|
|
|
536
735
|
#replaceContainer(container, newAssetContainer) {
|
|
736
|
+
this.#logDebug("Replacing container asset", { name: container.name });
|
|
537
737
|
this.#removeContainer(container);
|
|
538
738
|
container.assetContainer = newAssetContainer;
|
|
539
739
|
container.assetContainer.meshes.forEach((mesh) => {
|
|
540
740
|
mesh.receiveShadows = true;
|
|
541
741
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
742
|
+
this.#logDebug("Configured mesh for shadows", { container: container.name, mesh: mesh.name });
|
|
542
743
|
});
|
|
543
744
|
this.#addContainer(container);
|
|
745
|
+
this.#logDebug("Container replacement complete", {
|
|
746
|
+
name: container.name,
|
|
747
|
+
meshCount: container.assetContainer.meshes.length,
|
|
748
|
+
});
|
|
544
749
|
}
|
|
545
750
|
|
|
546
751
|
async #loadAssetContainer(container) {
|
|
547
|
-
|
|
752
|
+
const storage = container?.storage;
|
|
753
|
+
this.#logDebug("Requested asset container load", {
|
|
754
|
+
container: container?.name,
|
|
755
|
+
storage: this.#describeStorage(storage),
|
|
756
|
+
});
|
|
548
757
|
|
|
549
758
|
if (!storage) {
|
|
759
|
+
this.#logWarn("No storage configuration provided for container", { container: container?.name });
|
|
550
760
|
return false;
|
|
551
761
|
}
|
|
552
762
|
|
|
553
763
|
let source = storage.url || null;
|
|
554
764
|
|
|
555
765
|
if (storage.db && storage.table && storage.id) {
|
|
766
|
+
this.#logDebug("Loading container from IndexedDB", {
|
|
767
|
+
container: container.name,
|
|
768
|
+
db: storage.db,
|
|
769
|
+
table: storage.table,
|
|
770
|
+
id: storage.id,
|
|
771
|
+
});
|
|
556
772
|
await this.#initStorage(storage.db, storage.table);
|
|
557
773
|
const object = await loadModel(storage.id, storage.table);
|
|
558
774
|
source = object.data;
|
|
559
775
|
if (object.timestamp === container.timestamp) {
|
|
776
|
+
this.#logDebug("IndexedDB model unchanged; skipping reload", { container: container.name });
|
|
560
777
|
return false;
|
|
561
778
|
} else {
|
|
562
779
|
container.changed = { timestamp: object.timestamp, size: object.size };
|
|
780
|
+
this.#logDebug("IndexedDB model marked as changed", { container: container.name, metadata: container.changed });
|
|
563
781
|
}
|
|
564
782
|
}
|
|
565
783
|
|
|
566
784
|
if (!source) {
|
|
785
|
+
this.#logWarn("No source resolved for container", { container: container.name });
|
|
567
786
|
return false;
|
|
568
787
|
}
|
|
569
788
|
|
|
@@ -571,14 +790,17 @@ class PrefViewer extends HTMLElement {
|
|
|
571
790
|
|
|
572
791
|
let { blob, extension, size } = this.#decodeBase64(source);
|
|
573
792
|
if (blob && extension) {
|
|
793
|
+
this.#logDebug("Source detected as Base64", { container: container.name, extension });
|
|
574
794
|
file = new File([blob], `${container.name}${extension}`, {
|
|
575
795
|
type: blob.type,
|
|
576
796
|
});
|
|
577
797
|
if (!container.changed) {
|
|
578
798
|
if (container.timestamp === null && container.size === size) {
|
|
799
|
+
this.#logDebug("Base64 model unchanged; skipping reload", { container: container.name, size });
|
|
579
800
|
return false;
|
|
580
801
|
} else {
|
|
581
802
|
container.changed = { timestamp: null, size: size };
|
|
803
|
+
this.#logDebug("Base64 model marked as changed", { container: container.name, size });
|
|
582
804
|
}
|
|
583
805
|
}
|
|
584
806
|
} else {
|
|
@@ -586,9 +808,15 @@ class PrefViewer extends HTMLElement {
|
|
|
586
808
|
extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
|
|
587
809
|
const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
|
|
588
810
|
if (container.timestamp === fileTimestamp && container.size === fileSize) {
|
|
811
|
+
this.#logDebug("Remote model unchanged; skipping reload", {
|
|
812
|
+
container: container.name,
|
|
813
|
+
fileTimestamp,
|
|
814
|
+
fileSize,
|
|
815
|
+
});
|
|
589
816
|
return false;
|
|
590
817
|
} else {
|
|
591
818
|
container.changed = { timestamp: fileTimestamp, size: fileSize };
|
|
819
|
+
this.#logDebug("Remote model marked as changed", { container: container.name, metadata: container.changed });
|
|
592
820
|
}
|
|
593
821
|
}
|
|
594
822
|
|
|
@@ -602,10 +830,12 @@ class PrefViewer extends HTMLElement {
|
|
|
602
830
|
},
|
|
603
831
|
};
|
|
604
832
|
|
|
833
|
+
this.#logInfo("Loading asset container", { container: container.name, extension });
|
|
605
834
|
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
606
835
|
}
|
|
607
836
|
|
|
608
837
|
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
838
|
+
this.#logInfo("Starting container load", { loadModel, loadEnvironment, loadMaterials });
|
|
609
839
|
const promiseArray = [];
|
|
610
840
|
promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
|
|
611
841
|
promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
|
|
@@ -620,20 +850,27 @@ class PrefViewer extends HTMLElement {
|
|
|
620
850
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
621
851
|
this.#replaceContainer(this.#data.containers.model, modelContainer.value);
|
|
622
852
|
this.#storeChangedFlagsForContainer(this.#data.containers.model);
|
|
853
|
+
this.#logInfo("Model container loaded successfully");
|
|
623
854
|
} else {
|
|
624
855
|
this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
|
|
856
|
+
this.#logDebug("Model container load skipped or failed", { status: modelContainer.status });
|
|
625
857
|
}
|
|
626
858
|
|
|
627
859
|
if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
|
|
628
860
|
this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
|
|
629
861
|
this.#storeChangedFlagsForContainer(this.#data.containers.environment);
|
|
862
|
+
this.#logInfo("Environment container loaded successfully");
|
|
630
863
|
} else {
|
|
631
864
|
this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
|
|
865
|
+
this.#logDebug("Environment container load skipped or failed", { status: environmentContainer.status });
|
|
632
866
|
}
|
|
633
867
|
|
|
634
868
|
if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
|
|
635
869
|
this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
|
|
636
870
|
this.#storeChangedFlagsForContainer(this.#data.containers.materials);
|
|
871
|
+
this.#logInfo("Materials container loaded successfully");
|
|
872
|
+
} else {
|
|
873
|
+
this.#logDebug("Materials container load skipped or failed", { status: materialsContainer.status });
|
|
637
874
|
}
|
|
638
875
|
|
|
639
876
|
this.#setOptionsMaterials();
|
|
@@ -642,6 +879,7 @@ class PrefViewer extends HTMLElement {
|
|
|
642
879
|
|
|
643
880
|
this.#resetChangedFlags();
|
|
644
881
|
|
|
882
|
+
this.#logInfo("Containers load routine completed");
|
|
645
883
|
this.dispatchEvent(
|
|
646
884
|
new CustomEvent("model-loaded", {
|
|
647
885
|
detail: { success: "" },
|
|
@@ -649,9 +887,10 @@ class PrefViewer extends HTMLElement {
|
|
|
649
887
|
composed: true,
|
|
650
888
|
})
|
|
651
889
|
);
|
|
890
|
+
this.#logDebug("Dispatched model-loaded event");
|
|
652
891
|
})
|
|
653
892
|
.catch((error) => {
|
|
654
|
-
|
|
893
|
+
this.#logError("Failed to load containers", { error });
|
|
655
894
|
this.dispatchEvent(
|
|
656
895
|
new CustomEvent("model-error", {
|
|
657
896
|
detail: { error: error },
|
|
@@ -664,8 +903,17 @@ class PrefViewer extends HTMLElement {
|
|
|
664
903
|
|
|
665
904
|
// Public Methods
|
|
666
905
|
loadConfig(config) {
|
|
667
|
-
|
|
906
|
+
this.#logInfo("loadConfig called", { initialized: this.#initialized, inputType: typeof config });
|
|
907
|
+
if (typeof config === "string") {
|
|
908
|
+
try {
|
|
909
|
+
config = JSON.parse(config);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
this.#logError("Failed to parse config JSON", { error });
|
|
912
|
+
throw error;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
668
915
|
if (!config) {
|
|
916
|
+
this.#logWarn("No config provided");
|
|
669
917
|
return false;
|
|
670
918
|
}
|
|
671
919
|
|
|
@@ -682,93 +930,145 @@ class PrefViewer extends HTMLElement {
|
|
|
682
930
|
this.#checkMaterialsChanged(config.options);
|
|
683
931
|
}
|
|
684
932
|
|
|
933
|
+
this.#logDebug("Config applied", {
|
|
934
|
+
modelStorage: this.#describeStorage(this.#data.containers.model.storage),
|
|
935
|
+
environmentStorage: this.#describeStorage(this.#data.containers.environment.storage),
|
|
936
|
+
});
|
|
937
|
+
|
|
685
938
|
this.#initialized && this.#loadContainers(true, true, true);
|
|
686
939
|
}
|
|
687
940
|
|
|
688
941
|
setOptions(options) {
|
|
942
|
+
this.#logInfo("setOptions called", { optionsProvided: !!options });
|
|
689
943
|
if (!options) {
|
|
944
|
+
this.#logWarn("setOptions called without options");
|
|
690
945
|
return false;
|
|
691
946
|
}
|
|
692
947
|
let someSetted = false;
|
|
693
948
|
if (this.#checkCameraChanged(options)) {
|
|
949
|
+
this.#logDebug("Camera options changed via setOptions");
|
|
694
950
|
someSetted = someSetted || this.#setOptionsCamera();
|
|
695
951
|
}
|
|
696
952
|
if (this.#checkMaterialsChanged(options)) {
|
|
953
|
+
this.#logDebug("Material options changed via setOptions");
|
|
697
954
|
someSetted = someSetted || this.#setOptionsMaterials();
|
|
698
955
|
}
|
|
699
956
|
this.#resetChangedFlags();
|
|
957
|
+
this.#logDebug("setOptions completed", { appliedAny: someSetted });
|
|
700
958
|
debugger;
|
|
701
959
|
return someSetted;
|
|
702
960
|
}
|
|
703
961
|
|
|
704
962
|
loadModel(model) {
|
|
705
|
-
|
|
963
|
+
this.#logInfo("loadModel called", { initialized: this.#initialized, inputType: typeof model });
|
|
964
|
+
if (typeof model === "string") {
|
|
965
|
+
try {
|
|
966
|
+
model = JSON.parse(model);
|
|
967
|
+
} catch (error) {
|
|
968
|
+
this.#logError("Failed to parse model JSON", { error });
|
|
969
|
+
throw error;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
706
972
|
if (!model) {
|
|
973
|
+
this.#logWarn("No model payload provided");
|
|
707
974
|
return false;
|
|
708
975
|
}
|
|
709
976
|
this.#data.containers.model.storage = model.storage || null;
|
|
710
977
|
this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
|
|
978
|
+
this.#logDebug("Model configuration updated", {
|
|
979
|
+
storage: this.#describeStorage(this.#data.containers.model.storage),
|
|
980
|
+
show: this.#data.containers.model.show,
|
|
981
|
+
});
|
|
711
982
|
this.#initialized && this.#loadContainers(true, false, false);
|
|
712
983
|
}
|
|
713
984
|
|
|
714
985
|
loadScene(scene) {
|
|
715
|
-
|
|
986
|
+
this.#logInfo("loadScene called", { initialized: this.#initialized, inputType: typeof scene });
|
|
987
|
+
if (typeof scene === "string") {
|
|
988
|
+
try {
|
|
989
|
+
scene = JSON.parse(scene);
|
|
990
|
+
} catch (error) {
|
|
991
|
+
this.#logError("Failed to parse scene JSON", { error });
|
|
992
|
+
throw error;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
716
995
|
if (!scene) {
|
|
996
|
+
this.#logWarn("No scene payload provided");
|
|
717
997
|
return false;
|
|
718
998
|
}
|
|
719
999
|
this.#data.containers.environment.storage = scene.storage || null;
|
|
720
1000
|
this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
|
|
1001
|
+
this.#logDebug("Scene configuration updated", {
|
|
1002
|
+
storage: this.#describeStorage(this.#data.containers.environment.storage),
|
|
1003
|
+
show: this.#data.containers.environment.show,
|
|
1004
|
+
});
|
|
721
1005
|
this.#initialized && this.#loadContainers(false, true, false);
|
|
722
1006
|
}
|
|
723
1007
|
|
|
724
1008
|
showModel() {
|
|
725
1009
|
this.#data.containers.model.show = true;
|
|
726
1010
|
this.#addContainer(this.#data.containers.model);
|
|
1011
|
+
this.#logInfo("Model visibility set to true");
|
|
727
1012
|
}
|
|
728
1013
|
|
|
729
1014
|
hideModel() {
|
|
730
1015
|
this.#data.containers.model.show = false;
|
|
731
1016
|
this.#removeContainer(this.#data.containers.model);
|
|
1017
|
+
this.#logInfo("Model visibility set to false");
|
|
732
1018
|
}
|
|
733
1019
|
|
|
734
1020
|
showScene() {
|
|
735
1021
|
this.#data.containers.environment.show = true;
|
|
736
1022
|
this.#addContainer(this.#data.containers.environment);
|
|
737
1023
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1024
|
+
this.#logInfo("Scene visibility set to true");
|
|
738
1025
|
}
|
|
739
1026
|
|
|
740
1027
|
hideScene() {
|
|
741
1028
|
this.#data.containers.environment.show = false;
|
|
742
1029
|
this.#removeContainer(this.#data.containers.environment);
|
|
743
1030
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1031
|
+
this.#logInfo("Scene visibility set to false");
|
|
744
1032
|
}
|
|
745
1033
|
|
|
746
1034
|
downloadModelGLB() {
|
|
747
1035
|
const fileName = "model";
|
|
748
|
-
|
|
1036
|
+
this.#logInfo("Initiating GLB download for model", { fileName });
|
|
1037
|
+
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
|
|
1038
|
+
this.#logDebug("Model GLB export ready", { fileName });
|
|
1039
|
+
glb.downloadFiles();
|
|
1040
|
+
});
|
|
749
1041
|
}
|
|
750
1042
|
|
|
751
1043
|
downloadModelUSDZ() {
|
|
752
1044
|
const fileName = "model";
|
|
1045
|
+
this.#logInfo("Initiating USDZ download for model", { fileName });
|
|
753
1046
|
USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
|
|
754
1047
|
if (response) {
|
|
755
1048
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
1049
|
+
this.#logDebug("Model USDZ export ready", { fileName });
|
|
756
1050
|
}
|
|
757
1051
|
});
|
|
758
1052
|
}
|
|
759
1053
|
|
|
760
1054
|
downloadModelAndSceneUSDZ() {
|
|
761
1055
|
const fileName = "scene";
|
|
1056
|
+
this.#logInfo("Initiating USDZ download for scene", { fileName });
|
|
762
1057
|
USDZExportAsync(this.#scene).then((response) => {
|
|
763
1058
|
if (response) {
|
|
764
1059
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
1060
|
+
this.#logDebug("Scene USDZ export ready", { fileName });
|
|
765
1061
|
}
|
|
766
1062
|
});
|
|
767
1063
|
}
|
|
768
1064
|
|
|
769
1065
|
downloadModelAndSceneGLB() {
|
|
770
1066
|
const fileName = "scene";
|
|
771
|
-
|
|
1067
|
+
this.#logInfo("Initiating GLB download for scene", { fileName });
|
|
1068
|
+
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
|
|
1069
|
+
this.#logDebug("Scene GLB export ready", { fileName });
|
|
1070
|
+
glb.downloadFiles();
|
|
1071
|
+
});
|
|
772
1072
|
}
|
|
773
1073
|
}
|
|
774
1074
|
|