@preference-sl/pref-viewer 2.10.0-beta.10 → 2.10.0-beta.11
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 +18 -318
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -47,13 +47,7 @@ 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
|
-
|
|
55
50
|
#initialized = false;
|
|
56
|
-
#logLevel = PrefViewer.DEFAULT_LOG_LEVEL;
|
|
57
51
|
|
|
58
52
|
#data = {
|
|
59
53
|
containers: {
|
|
@@ -119,67 +113,6 @@ class PrefViewer extends HTMLElement {
|
|
|
119
113
|
},
|
|
120
114
|
};
|
|
121
115
|
|
|
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
|
-
|
|
183
116
|
// DOM elements
|
|
184
117
|
#wrapper = null;
|
|
185
118
|
#canvas = null;
|
|
@@ -196,7 +129,6 @@ class PrefViewer extends HTMLElement {
|
|
|
196
129
|
|
|
197
130
|
constructor() {
|
|
198
131
|
super();
|
|
199
|
-
this.#logDebug("Constructing PrefViewer instance");
|
|
200
132
|
this.attachShadow({ mode: "open" });
|
|
201
133
|
this.#createCanvas();
|
|
202
134
|
this.#wrapCanvas();
|
|
@@ -212,13 +144,11 @@ class PrefViewer extends HTMLElement {
|
|
|
212
144
|
};
|
|
213
145
|
}
|
|
214
146
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
147
|
+
static get observedAttributes() {
|
|
148
|
+
return ["config", "model", "scene", "show-model", "show-scene"];
|
|
149
|
+
}
|
|
220
150
|
|
|
221
|
-
|
|
151
|
+
attributeChangedCallback(name, _old, value) {
|
|
222
152
|
let data = null;
|
|
223
153
|
switch (name) {
|
|
224
154
|
case "config":
|
|
@@ -232,7 +162,6 @@ class PrefViewer extends HTMLElement {
|
|
|
232
162
|
break;
|
|
233
163
|
case "show-model":
|
|
234
164
|
data = value.toLowerCase?.() === "true";
|
|
235
|
-
this.#logDebug("Toggling model visibility from attribute", { visible: data, initialized: this.#initialized });
|
|
236
165
|
if (this.#initialized) {
|
|
237
166
|
data ? this.showModel() : this.hideModel();
|
|
238
167
|
} else {
|
|
@@ -241,7 +170,6 @@ class PrefViewer extends HTMLElement {
|
|
|
241
170
|
break;
|
|
242
171
|
case "show-scene":
|
|
243
172
|
data = value.toLowerCase?.() === "true";
|
|
244
|
-
this.#logDebug("Toggling scene visibility from attribute", { visible: data, initialized: this.#initialized });
|
|
245
173
|
if (this.#initialized) {
|
|
246
174
|
data ? this.showScene() : this.hideScene();
|
|
247
175
|
} else {
|
|
@@ -254,7 +182,7 @@ class PrefViewer extends HTMLElement {
|
|
|
254
182
|
connectedCallback() {
|
|
255
183
|
if (!this.hasAttribute("config")) {
|
|
256
184
|
const error = 'PrefViewer: provide "models" as array of model and environment';
|
|
257
|
-
|
|
185
|
+
console.error(error);
|
|
258
186
|
this.dispatchEvent(
|
|
259
187
|
new CustomEvent("model-error", {
|
|
260
188
|
detail: { error: new Error(error) },
|
|
@@ -265,22 +193,18 @@ class PrefViewer extends HTMLElement {
|
|
|
265
193
|
return false;
|
|
266
194
|
}
|
|
267
195
|
|
|
268
|
-
this.#logDebug("Connected to DOM, initializing Babylon");
|
|
269
196
|
this.#initializeBabylon();
|
|
270
197
|
this.#loadContainers(true, true, true);
|
|
271
198
|
this.#initialized = true;
|
|
272
|
-
this.#logInfo("Initialization completed", { initialized: this.#initialized });
|
|
273
199
|
}
|
|
274
200
|
|
|
275
201
|
disconnectedCallback() {
|
|
276
|
-
this.#logDebug("Disconnected from DOM, disposing resources");
|
|
277
202
|
this.#disposeEngine();
|
|
278
203
|
this.#canvasResizeObserver.disconnect();
|
|
279
204
|
}
|
|
280
205
|
|
|
281
206
|
// Web Component
|
|
282
207
|
#createCanvas() {
|
|
283
|
-
this.#logDebug("Creating rendering canvas");
|
|
284
208
|
this.#canvas = document.createElement("canvas");
|
|
285
209
|
Object.assign(this.#canvas.style, {
|
|
286
210
|
width: "100%",
|
|
@@ -288,11 +212,9 @@ class PrefViewer extends HTMLElement {
|
|
|
288
212
|
display: "block",
|
|
289
213
|
outline: "none",
|
|
290
214
|
});
|
|
291
|
-
this.#logDebug("Canvas element created and styled");
|
|
292
215
|
}
|
|
293
216
|
|
|
294
217
|
#wrapCanvas() {
|
|
295
|
-
this.#logDebug("Wrapping canvas inside container div");
|
|
296
218
|
this.#wrapper = document.createElement("div");
|
|
297
219
|
Object.assign(this.#wrapper.style, {
|
|
298
220
|
width: "100%",
|
|
@@ -301,24 +223,20 @@ class PrefViewer extends HTMLElement {
|
|
|
301
223
|
});
|
|
302
224
|
this.#wrapper.appendChild(this.#canvas);
|
|
303
225
|
this.shadowRoot.append(this.#wrapper);
|
|
304
|
-
this.#logDebug("Canvas wrapper appended to shadow DOM");
|
|
305
226
|
}
|
|
306
227
|
|
|
307
228
|
// Data
|
|
308
229
|
#checkCameraChanged(options) {
|
|
309
230
|
if (!options || !options.camera) {
|
|
310
|
-
this.#logDebug("Camera options not provided or unchanged");
|
|
311
231
|
return false;
|
|
312
232
|
}
|
|
313
233
|
this.#data.options.camera.changed = options.camera && options.camera !== this.#data.options.camera.value ? true : false;
|
|
314
234
|
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 });
|
|
316
235
|
return this.#data.options.camera.changed;
|
|
317
236
|
}
|
|
318
237
|
|
|
319
238
|
#checkMaterialsChanged(options) {
|
|
320
239
|
if (!options) {
|
|
321
|
-
this.#logDebug("Material options not provided");
|
|
322
240
|
return false;
|
|
323
241
|
}
|
|
324
242
|
let someChanged = false;
|
|
@@ -328,32 +246,22 @@ class PrefViewer extends HTMLElement {
|
|
|
328
246
|
this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : this.#data.options.materials[material].value;
|
|
329
247
|
someChanged = someChanged || this.#data.options.materials[material].changed;
|
|
330
248
|
});
|
|
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
|
-
});
|
|
338
249
|
return someChanged;
|
|
339
250
|
}
|
|
340
251
|
|
|
341
252
|
#storeChangedFlagsForContainer(container) {
|
|
342
253
|
container.timestamp = container.changed.timestamp;
|
|
343
254
|
container.size = container.changed.size;
|
|
344
|
-
this.#logDebug("Stored change flags for container", { name: container.name, timestamp: container.timestamp, size: container.size });
|
|
345
255
|
}
|
|
346
256
|
|
|
347
257
|
#resetChangedFlags() {
|
|
348
258
|
Object.values(this.#data.containers).forEach((container) => (container.changed = false));
|
|
349
259
|
Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
|
|
350
260
|
this.#data.options.camera.changed = false;
|
|
351
|
-
this.#logDebug("Reset change flags across containers and options");
|
|
352
261
|
}
|
|
353
262
|
|
|
354
263
|
// Babylon.js
|
|
355
264
|
async #initializeBabylon() {
|
|
356
|
-
this.#logInfo("Initializing Babylon engine and scene");
|
|
357
265
|
this.#engine = new Engine(this.#canvas, true, { alpha: true });
|
|
358
266
|
this.#scene = new Scene(this.#engine);
|
|
359
267
|
this.#scene.clearColor = new Color4(1, 1, 1, 1);
|
|
@@ -363,32 +271,26 @@ class PrefViewer extends HTMLElement {
|
|
|
363
271
|
|
|
364
272
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
365
273
|
this.#canvasResizeObserver.observe(this.#canvas);
|
|
366
|
-
this.#logDebug("Engine render loop started and resize observer attached");
|
|
367
274
|
|
|
368
275
|
await this.#createXRExperience();
|
|
369
|
-
this.#logInfo("Babylon initialization finished", { xrEnabled: !!this.#XRExperience });
|
|
370
276
|
}
|
|
371
277
|
|
|
372
278
|
addStylesToARButton() {
|
|
373
|
-
this.#logDebug("Adding styles to AR button");
|
|
374
279
|
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"}';
|
|
375
280
|
const style = document.createElement("style");
|
|
376
281
|
style.appendChild(document.createTextNode(css));
|
|
377
282
|
this.#wrapper.appendChild(style);
|
|
378
|
-
this.#logDebug("AR button styles applied");
|
|
379
283
|
}
|
|
380
284
|
|
|
381
285
|
async #createXRExperience() {
|
|
382
286
|
if (this.#XRExperience) {
|
|
383
|
-
this.#logDebug("XR experience already created, skipping");
|
|
384
287
|
return true;
|
|
385
288
|
}
|
|
386
289
|
|
|
387
|
-
this.#logDebug("Attempting to create XR experience");
|
|
388
290
|
const sessionMode = "immersive-ar";
|
|
389
291
|
const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
|
|
390
292
|
if (!sessionSupported) {
|
|
391
|
-
|
|
293
|
+
console.info("PrefViewer: WebXR in mode AR is not supported");
|
|
392
294
|
return false;
|
|
393
295
|
}
|
|
394
296
|
|
|
@@ -407,7 +309,6 @@ class PrefViewer extends HTMLElement {
|
|
|
407
309
|
};
|
|
408
310
|
|
|
409
311
|
this.#XRExperience = await WebXRDefaultExperience.CreateAsync(this.#scene, options);
|
|
410
|
-
this.#logInfo("XR experience created successfully", { sessionMode });
|
|
411
312
|
|
|
412
313
|
const featuresManager = this.#XRExperience.baseExperience.featuresManager;
|
|
413
314
|
featuresManager.enableFeature(WebXRFeatureName.TELEPORTATION, "stable", {
|
|
@@ -415,32 +316,24 @@ class PrefViewer extends HTMLElement {
|
|
|
415
316
|
floorMeshes: [ground],
|
|
416
317
|
timeToTeleport: 1500,
|
|
417
318
|
});
|
|
418
|
-
this.#logDebug("XR teleportation feature enabled");
|
|
419
319
|
|
|
420
320
|
this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
|
|
421
321
|
// Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
|
|
422
322
|
this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
|
|
423
323
|
this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
|
|
424
324
|
this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(this.#XRExperience.baseExperience.camera);
|
|
425
|
-
this.#logDebug("XR session ready and camera transformed");
|
|
426
325
|
});
|
|
427
326
|
|
|
428
327
|
this.addStylesToARButton();
|
|
429
328
|
} catch (error) {
|
|
430
|
-
|
|
329
|
+
console.warn("PrefViewer: failed to create WebXR experience", error);
|
|
431
330
|
this.#XRExperience = null;
|
|
432
331
|
}
|
|
433
332
|
}
|
|
434
333
|
|
|
435
|
-
#canvasResizeObserver = new ResizeObserver(() =>
|
|
436
|
-
if (this.#engine) {
|
|
437
|
-
this.#logDebug("Resize observer triggered");
|
|
438
|
-
this.#engine.resize();
|
|
439
|
-
}
|
|
440
|
-
});
|
|
334
|
+
#canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
|
|
441
335
|
|
|
442
336
|
#createCamera() {
|
|
443
|
-
this.#logDebug("Creating default camera");
|
|
444
337
|
this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
|
|
445
338
|
this.#camera.upperBetaLimit = Math.PI * 0.48;
|
|
446
339
|
this.#camera.lowerBetaLimit = Math.PI * 0.25;
|
|
@@ -449,16 +342,9 @@ class PrefViewer extends HTMLElement {
|
|
|
449
342
|
this.#camera.metadata = { locked: false }
|
|
450
343
|
this.#camera = this.#camera;
|
|
451
344
|
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
|
-
});
|
|
458
345
|
}
|
|
459
346
|
|
|
460
347
|
#createLights() {
|
|
461
|
-
this.#logDebug("Creating scene lights");
|
|
462
348
|
// 1) Stronger ambient fill
|
|
463
349
|
this.#hemiLight = new HemisphericLight("hemiLight", new Vector3(-10, 10, -10), this.#scene);
|
|
464
350
|
this.#hemiLight.intensity = 0.6;
|
|
@@ -478,52 +364,32 @@ class PrefViewer extends HTMLElement {
|
|
|
478
364
|
this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
|
|
479
365
|
this.#cameraLight.parent = this.#camera;
|
|
480
366
|
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
|
-
});
|
|
487
367
|
}
|
|
488
368
|
|
|
489
369
|
#setupInteraction() {
|
|
490
|
-
this.#logDebug("Setting up canvas interaction listeners");
|
|
491
370
|
this.#canvas.addEventListener("wheel", (event) => {
|
|
492
371
|
if (!this.#scene || !this.#camera) {
|
|
493
|
-
this.#logWarn("Wheel interaction ignored; scene or camera missing");
|
|
494
372
|
return false;
|
|
495
373
|
}
|
|
496
374
|
//const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
497
375
|
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
498
376
|
if (!this.#scene.activeCamera.metadata?.locked) {
|
|
499
377
|
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");
|
|
506
378
|
}
|
|
507
379
|
event.preventDefault();
|
|
508
380
|
});
|
|
509
381
|
}
|
|
510
382
|
|
|
511
383
|
#disposeEngine() {
|
|
512
|
-
if (!this.#engine)
|
|
513
|
-
this.#logDebug("Dispose engine called but engine already null");
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
this.#logDebug("Disposing Babylon resources");
|
|
384
|
+
if (!this.#engine) return;
|
|
517
385
|
this.#engine.dispose();
|
|
518
386
|
this.#engine = this.#scene = this.#camera = null;
|
|
519
387
|
this.#hemiLight = this.#dirLight = this.#cameraLight = null;
|
|
520
388
|
this.#shadowGen = null;
|
|
521
|
-
this.#logDebug("Babylon resources disposed");
|
|
522
389
|
}
|
|
523
390
|
|
|
524
391
|
// Utility methods for loading gltf/glb
|
|
525
392
|
async #getServerFileDataHeader(uri) {
|
|
526
|
-
this.#logDebug("Requesting server file header", { uri });
|
|
527
393
|
return new Promise((resolve) => {
|
|
528
394
|
const xhr = new XMLHttpRequest();
|
|
529
395
|
xhr.open("HEAD", uri, true);
|
|
@@ -532,15 +398,12 @@ class PrefViewer extends HTMLElement {
|
|
|
532
398
|
if (xhr.status === 200) {
|
|
533
399
|
const size = parseInt(xhr.getResponseHeader("Content-Length"));
|
|
534
400
|
const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
|
|
535
|
-
this.#logDebug("Received server file header", { uri, size, timestamp });
|
|
536
401
|
resolve(size, timestamp);
|
|
537
402
|
} else {
|
|
538
|
-
this.#logWarn("Failed to retrieve server file header", { uri, status: xhr.status });
|
|
539
403
|
resolve(0, null);
|
|
540
404
|
}
|
|
541
405
|
};
|
|
542
406
|
xhr.onerror = () => {
|
|
543
|
-
this.#logError("Error requesting server file header", { uri });
|
|
544
407
|
resolve(0, null);
|
|
545
408
|
};
|
|
546
409
|
xhr.send();
|
|
@@ -549,14 +412,11 @@ class PrefViewer extends HTMLElement {
|
|
|
549
412
|
|
|
550
413
|
#transformUrl(url) {
|
|
551
414
|
return new Promise((resolve) => {
|
|
552
|
-
|
|
553
|
-
this.#logDebug("Transformed URL", { original: url, transformed });
|
|
554
|
-
resolve(transformed);
|
|
415
|
+
resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
|
|
555
416
|
});
|
|
556
417
|
}
|
|
557
418
|
|
|
558
419
|
#decodeBase64(base64) {
|
|
559
|
-
this.#logDebug("Decoding Base64 payload", { length: base64 ? base64.length : 0 });
|
|
560
420
|
const [, payload] = base64.split(",");
|
|
561
421
|
const raw = payload || base64;
|
|
562
422
|
let decoded = "";
|
|
@@ -566,62 +426,44 @@ class PrefViewer extends HTMLElement {
|
|
|
566
426
|
try {
|
|
567
427
|
decoded = atob(raw);
|
|
568
428
|
} catch {
|
|
569
|
-
this.#logWarn("Failed to decode Base64 string");
|
|
570
429
|
return { blob, extension, size };
|
|
571
430
|
}
|
|
572
431
|
let isJson = false;
|
|
573
432
|
try {
|
|
574
433
|
JSON.parse(decoded);
|
|
575
434
|
isJson = true;
|
|
576
|
-
} catch {
|
|
435
|
+
} catch {}
|
|
577
436
|
extension = isJson ? ".gltf" : ".glb";
|
|
578
437
|
const type = isJson ? "model/gltf+json" : "model/gltf-binary";
|
|
579
438
|
const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
|
|
580
439
|
blob = new Blob([array], { type });
|
|
581
|
-
this.#logDebug("Decoded Base64 payload", { isJson, size, extension });
|
|
582
440
|
return { blob, extension, size };
|
|
583
441
|
}
|
|
584
442
|
|
|
585
443
|
async #initStorage(db, table) {
|
|
586
|
-
this.#logDebug("Initializing storage access", { db, table });
|
|
587
444
|
if (window.gltfDB && window.gltfDB.name === db && window.gltfDB.objectStoreNames.contains(table)) {
|
|
588
|
-
this.#logDebug("Reusing existing IndexedDB connection", { db, table });
|
|
589
445
|
return true;
|
|
590
446
|
}
|
|
591
447
|
await initDb(db, table);
|
|
592
|
-
this.#logDebug("IndexedDB initialized", { db, table });
|
|
593
448
|
}
|
|
594
449
|
|
|
595
450
|
// Methods for managing Asset Containers
|
|
596
451
|
#setVisibilityOfWallAndFloorInModel(show) {
|
|
597
452
|
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
|
-
});
|
|
602
453
|
return false;
|
|
603
454
|
}
|
|
604
455
|
show = show !== undefined ? show : this.#data.containers.environment.visible;
|
|
605
456
|
const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
|
|
606
457
|
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 });
|
|
608
458
|
}
|
|
609
459
|
|
|
610
460
|
#setOptionsMaterial(optionMaterial) {
|
|
611
461
|
if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
|
|
612
|
-
this.#logWarn("Material option invalid", { optionMaterial });
|
|
613
462
|
return false;
|
|
614
463
|
}
|
|
615
464
|
|
|
616
|
-
this.#logDebug("Applying material option", {
|
|
617
|
-
prefix: optionMaterial.prefix,
|
|
618
|
-
value: optionMaterial.value,
|
|
619
|
-
changed: optionMaterial.changed,
|
|
620
|
-
});
|
|
621
|
-
|
|
622
465
|
const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
|
|
623
466
|
if (!material) {
|
|
624
|
-
this.#logWarn("Requested material not found", { value: optionMaterial.value });
|
|
625
467
|
return false;
|
|
626
468
|
}
|
|
627
469
|
|
|
@@ -633,7 +475,6 @@ class PrefViewer extends HTMLElement {
|
|
|
633
475
|
containers.push(this.#data.containers.environment.assetContainer);
|
|
634
476
|
}
|
|
635
477
|
if (containers.length === 0) {
|
|
636
|
-
this.#logDebug("No containers required material update", { prefix: optionMaterial.prefix });
|
|
637
478
|
return false;
|
|
638
479
|
}
|
|
639
480
|
|
|
@@ -644,61 +485,36 @@ class PrefViewer extends HTMLElement {
|
|
|
644
485
|
.forEach((mesh) => {
|
|
645
486
|
mesh.material = material;
|
|
646
487
|
someSetted = true;
|
|
647
|
-
this.#logDebug("Assigned material to mesh", { mesh: mesh.name, material: material.name });
|
|
648
488
|
})
|
|
649
489
|
);
|
|
650
490
|
|
|
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
|
-
|
|
658
491
|
return someSetted;
|
|
659
492
|
}
|
|
660
493
|
|
|
661
494
|
#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");
|
|
668
495
|
let someSetted = false;
|
|
669
496
|
Object.values(this.#data.options.materials).forEach((material) => {
|
|
670
497
|
let settedMaterial = this.#setOptionsMaterial(material);
|
|
671
498
|
someSetted = someSetted || settedMaterial;
|
|
672
499
|
});
|
|
673
|
-
this.#logDebug("Material batch processing finished", { appliedAny: someSetted });
|
|
674
500
|
return someSetted;
|
|
675
501
|
}
|
|
676
502
|
|
|
677
503
|
#setOptionsCamera() {
|
|
678
504
|
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
|
-
});
|
|
684
505
|
return false;
|
|
685
506
|
}
|
|
686
507
|
|
|
687
508
|
let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
|
|
688
509
|
if (!camera) {
|
|
689
|
-
this.#logWarn("Requested camera not found", { name: this.#data.options.camera.value });
|
|
690
510
|
return false;
|
|
691
511
|
}
|
|
692
512
|
|
|
693
513
|
camera.metadata = { locked: this.#data.options.camera.locked };
|
|
694
514
|
if (!this.#data.options.camera.locked) {
|
|
695
515
|
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 });
|
|
699
516
|
}
|
|
700
517
|
this.#scene.activeCamera = camera;
|
|
701
|
-
this.#logDebug("Active camera set", { camera: camera.name });
|
|
702
518
|
|
|
703
519
|
return true;
|
|
704
520
|
}
|
|
@@ -707,14 +523,6 @@ class PrefViewer extends HTMLElement {
|
|
|
707
523
|
if (container.assetContainer && !container.visible && container.show) {
|
|
708
524
|
container.assetContainer.addAllToScene();
|
|
709
525
|
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
|
-
});
|
|
718
526
|
}
|
|
719
527
|
}
|
|
720
528
|
|
|
@@ -722,67 +530,40 @@ class PrefViewer extends HTMLElement {
|
|
|
722
530
|
if (container.assetContainer && container.visible) {
|
|
723
531
|
container.assetContainer.removeAllFromScene();
|
|
724
532
|
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
|
-
});
|
|
732
533
|
}
|
|
733
534
|
}
|
|
734
535
|
|
|
735
536
|
#replaceContainer(container, newAssetContainer) {
|
|
736
|
-
this.#logDebug("Replacing container asset", { name: container.name });
|
|
737
537
|
this.#removeContainer(container);
|
|
738
538
|
container.assetContainer = newAssetContainer;
|
|
739
539
|
container.assetContainer.meshes.forEach((mesh) => {
|
|
740
540
|
mesh.receiveShadows = true;
|
|
741
541
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
742
|
-
this.#logDebug("Configured mesh for shadows", { container: container.name, mesh: mesh.name });
|
|
743
542
|
});
|
|
744
543
|
this.#addContainer(container);
|
|
745
|
-
this.#logDebug("Container replacement complete", {
|
|
746
|
-
name: container.name,
|
|
747
|
-
meshCount: container.assetContainer.meshes.length,
|
|
748
|
-
});
|
|
749
544
|
}
|
|
750
545
|
|
|
751
546
|
async #loadAssetContainer(container) {
|
|
752
|
-
|
|
753
|
-
this.#logDebug("Requested asset container load", {
|
|
754
|
-
container: container?.name,
|
|
755
|
-
storage: this.#describeStorage(storage),
|
|
756
|
-
});
|
|
547
|
+
let storage = container?.storage;
|
|
757
548
|
|
|
758
549
|
if (!storage) {
|
|
759
|
-
this.#logWarn("No storage configuration provided for container", { container: container?.name });
|
|
760
550
|
return false;
|
|
761
551
|
}
|
|
762
552
|
|
|
763
553
|
let source = storage.url || null;
|
|
764
554
|
|
|
765
555
|
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
|
-
});
|
|
772
556
|
await this.#initStorage(storage.db, storage.table);
|
|
773
557
|
const object = await loadModel(storage.id, storage.table);
|
|
774
558
|
source = object.data;
|
|
775
559
|
if (object.timestamp === container.timestamp) {
|
|
776
|
-
this.#logDebug("IndexedDB model unchanged; skipping reload", { container: container.name });
|
|
777
560
|
return false;
|
|
778
561
|
} else {
|
|
779
562
|
container.changed = { timestamp: object.timestamp, size: object.size };
|
|
780
|
-
this.#logDebug("IndexedDB model marked as changed", { container: container.name, metadata: container.changed });
|
|
781
563
|
}
|
|
782
564
|
}
|
|
783
565
|
|
|
784
566
|
if (!source) {
|
|
785
|
-
this.#logWarn("No source resolved for container", { container: container.name });
|
|
786
567
|
return false;
|
|
787
568
|
}
|
|
788
569
|
|
|
@@ -790,17 +571,14 @@ class PrefViewer extends HTMLElement {
|
|
|
790
571
|
|
|
791
572
|
let { blob, extension, size } = this.#decodeBase64(source);
|
|
792
573
|
if (blob && extension) {
|
|
793
|
-
this.#logDebug("Source detected as Base64", { container: container.name, extension });
|
|
794
574
|
file = new File([blob], `${container.name}${extension}`, {
|
|
795
575
|
type: blob.type,
|
|
796
576
|
});
|
|
797
577
|
if (!container.changed) {
|
|
798
578
|
if (container.timestamp === null && container.size === size) {
|
|
799
|
-
this.#logDebug("Base64 model unchanged; skipping reload", { container: container.name, size });
|
|
800
579
|
return false;
|
|
801
580
|
} else {
|
|
802
581
|
container.changed = { timestamp: null, size: size };
|
|
803
|
-
this.#logDebug("Base64 model marked as changed", { container: container.name, size });
|
|
804
582
|
}
|
|
805
583
|
}
|
|
806
584
|
} else {
|
|
@@ -808,15 +586,9 @@ class PrefViewer extends HTMLElement {
|
|
|
808
586
|
extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
|
|
809
587
|
const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
|
|
810
588
|
if (container.timestamp === fileTimestamp && container.size === fileSize) {
|
|
811
|
-
this.#logDebug("Remote model unchanged; skipping reload", {
|
|
812
|
-
container: container.name,
|
|
813
|
-
fileTimestamp,
|
|
814
|
-
fileSize,
|
|
815
|
-
});
|
|
816
589
|
return false;
|
|
817
590
|
} else {
|
|
818
591
|
container.changed = { timestamp: fileTimestamp, size: fileSize };
|
|
819
|
-
this.#logDebug("Remote model marked as changed", { container: container.name, metadata: container.changed });
|
|
820
592
|
}
|
|
821
593
|
}
|
|
822
594
|
|
|
@@ -830,12 +602,10 @@ class PrefViewer extends HTMLElement {
|
|
|
830
602
|
},
|
|
831
603
|
};
|
|
832
604
|
|
|
833
|
-
this.#logInfo("Loading asset container", { container: container.name, extension });
|
|
834
605
|
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
835
606
|
}
|
|
836
607
|
|
|
837
608
|
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
838
|
-
this.#logInfo("Starting container load", { loadModel, loadEnvironment, loadMaterials });
|
|
839
609
|
const promiseArray = [];
|
|
840
610
|
promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
|
|
841
611
|
promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
|
|
@@ -850,27 +620,20 @@ class PrefViewer extends HTMLElement {
|
|
|
850
620
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
851
621
|
this.#replaceContainer(this.#data.containers.model, modelContainer.value);
|
|
852
622
|
this.#storeChangedFlagsForContainer(this.#data.containers.model);
|
|
853
|
-
this.#logInfo("Model container loaded successfully");
|
|
854
623
|
} else {
|
|
855
624
|
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 });
|
|
857
625
|
}
|
|
858
626
|
|
|
859
627
|
if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
|
|
860
628
|
this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
|
|
861
629
|
this.#storeChangedFlagsForContainer(this.#data.containers.environment);
|
|
862
|
-
this.#logInfo("Environment container loaded successfully");
|
|
863
630
|
} else {
|
|
864
631
|
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 });
|
|
866
632
|
}
|
|
867
633
|
|
|
868
634
|
if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
|
|
869
635
|
this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
|
|
870
636
|
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 });
|
|
874
637
|
}
|
|
875
638
|
|
|
876
639
|
this.#setOptionsMaterials();
|
|
@@ -879,7 +642,6 @@ class PrefViewer extends HTMLElement {
|
|
|
879
642
|
|
|
880
643
|
this.#resetChangedFlags();
|
|
881
644
|
|
|
882
|
-
this.#logInfo("Containers load routine completed");
|
|
883
645
|
this.dispatchEvent(
|
|
884
646
|
new CustomEvent("model-loaded", {
|
|
885
647
|
detail: { success: "" },
|
|
@@ -887,10 +649,9 @@ class PrefViewer extends HTMLElement {
|
|
|
887
649
|
composed: true,
|
|
888
650
|
})
|
|
889
651
|
);
|
|
890
|
-
this.#logDebug("Dispatched model-loaded event");
|
|
891
652
|
})
|
|
892
653
|
.catch((error) => {
|
|
893
|
-
|
|
654
|
+
console.error("PrefViewer: failed to load model", error);
|
|
894
655
|
this.dispatchEvent(
|
|
895
656
|
new CustomEvent("model-error", {
|
|
896
657
|
detail: { error: error },
|
|
@@ -903,17 +664,8 @@ class PrefViewer extends HTMLElement {
|
|
|
903
664
|
|
|
904
665
|
// Public Methods
|
|
905
666
|
loadConfig(config) {
|
|
906
|
-
|
|
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
|
-
}
|
|
667
|
+
config = typeof config === "string" ? JSON.parse(config) : config;
|
|
915
668
|
if (!config) {
|
|
916
|
-
this.#logWarn("No config provided");
|
|
917
669
|
return false;
|
|
918
670
|
}
|
|
919
671
|
|
|
@@ -930,145 +682,93 @@ class PrefViewer extends HTMLElement {
|
|
|
930
682
|
this.#checkMaterialsChanged(config.options);
|
|
931
683
|
}
|
|
932
684
|
|
|
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
|
-
|
|
938
685
|
this.#initialized && this.#loadContainers(true, true, true);
|
|
939
686
|
}
|
|
940
687
|
|
|
941
688
|
setOptions(options) {
|
|
942
|
-
this.#logInfo("setOptions called", { optionsProvided: !!options });
|
|
943
689
|
if (!options) {
|
|
944
|
-
this.#logWarn("setOptions called without options");
|
|
945
690
|
return false;
|
|
946
691
|
}
|
|
947
692
|
let someSetted = false;
|
|
948
693
|
if (this.#checkCameraChanged(options)) {
|
|
949
|
-
this.#logDebug("Camera options changed via setOptions");
|
|
950
694
|
someSetted = someSetted || this.#setOptionsCamera();
|
|
951
695
|
}
|
|
952
696
|
if (this.#checkMaterialsChanged(options)) {
|
|
953
|
-
this.#logDebug("Material options changed via setOptions");
|
|
954
697
|
someSetted = someSetted || this.#setOptionsMaterials();
|
|
955
698
|
}
|
|
956
699
|
this.#resetChangedFlags();
|
|
957
|
-
this.#logDebug("setOptions completed", { appliedAny: someSetted });
|
|
958
700
|
debugger;
|
|
959
701
|
return someSetted;
|
|
960
702
|
}
|
|
961
703
|
|
|
962
704
|
loadModel(model) {
|
|
963
|
-
|
|
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
|
-
}
|
|
705
|
+
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
972
706
|
if (!model) {
|
|
973
|
-
this.#logWarn("No model payload provided");
|
|
974
707
|
return false;
|
|
975
708
|
}
|
|
976
709
|
this.#data.containers.model.storage = model.storage || null;
|
|
977
710
|
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
|
-
});
|
|
982
711
|
this.#initialized && this.#loadContainers(true, false, false);
|
|
983
712
|
}
|
|
984
713
|
|
|
985
714
|
loadScene(scene) {
|
|
986
|
-
|
|
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
|
-
}
|
|
715
|
+
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
995
716
|
if (!scene) {
|
|
996
|
-
this.#logWarn("No scene payload provided");
|
|
997
717
|
return false;
|
|
998
718
|
}
|
|
999
719
|
this.#data.containers.environment.storage = scene.storage || null;
|
|
1000
720
|
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
|
-
});
|
|
1005
721
|
this.#initialized && this.#loadContainers(false, true, false);
|
|
1006
722
|
}
|
|
1007
723
|
|
|
1008
724
|
showModel() {
|
|
1009
725
|
this.#data.containers.model.show = true;
|
|
1010
726
|
this.#addContainer(this.#data.containers.model);
|
|
1011
|
-
this.#logInfo("Model visibility set to true");
|
|
1012
727
|
}
|
|
1013
728
|
|
|
1014
729
|
hideModel() {
|
|
1015
730
|
this.#data.containers.model.show = false;
|
|
1016
731
|
this.#removeContainer(this.#data.containers.model);
|
|
1017
|
-
this.#logInfo("Model visibility set to false");
|
|
1018
732
|
}
|
|
1019
733
|
|
|
1020
734
|
showScene() {
|
|
1021
735
|
this.#data.containers.environment.show = true;
|
|
1022
736
|
this.#addContainer(this.#data.containers.environment);
|
|
1023
737
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1024
|
-
this.#logInfo("Scene visibility set to true");
|
|
1025
738
|
}
|
|
1026
739
|
|
|
1027
740
|
hideScene() {
|
|
1028
741
|
this.#data.containers.environment.show = false;
|
|
1029
742
|
this.#removeContainer(this.#data.containers.environment);
|
|
1030
743
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
1031
|
-
this.#logInfo("Scene visibility set to false");
|
|
1032
744
|
}
|
|
1033
745
|
|
|
1034
746
|
downloadModelGLB() {
|
|
1035
747
|
const fileName = "model";
|
|
1036
|
-
this.#
|
|
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
|
-
});
|
|
748
|
+
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
1041
749
|
}
|
|
1042
750
|
|
|
1043
751
|
downloadModelUSDZ() {
|
|
1044
752
|
const fileName = "model";
|
|
1045
|
-
this.#logInfo("Initiating USDZ download for model", { fileName });
|
|
1046
753
|
USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
|
|
1047
754
|
if (response) {
|
|
1048
755
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
1049
|
-
this.#logDebug("Model USDZ export ready", { fileName });
|
|
1050
756
|
}
|
|
1051
757
|
});
|
|
1052
758
|
}
|
|
1053
759
|
|
|
1054
760
|
downloadModelAndSceneUSDZ() {
|
|
1055
761
|
const fileName = "scene";
|
|
1056
|
-
this.#logInfo("Initiating USDZ download for scene", { fileName });
|
|
1057
762
|
USDZExportAsync(this.#scene).then((response) => {
|
|
1058
763
|
if (response) {
|
|
1059
764
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
1060
|
-
this.#logDebug("Scene USDZ export ready", { fileName });
|
|
1061
765
|
}
|
|
1062
766
|
});
|
|
1063
767
|
}
|
|
1064
768
|
|
|
1065
769
|
downloadModelAndSceneGLB() {
|
|
1066
770
|
const fileName = "scene";
|
|
1067
|
-
this.#
|
|
1068
|
-
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
|
|
1069
|
-
this.#logDebug("Scene GLB export ready", { fileName });
|
|
1070
|
-
glb.downloadFiles();
|
|
1071
|
-
});
|
|
771
|
+
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
1072
772
|
}
|
|
1073
773
|
}
|
|
1074
774
|
|