@preference-sl/pref-viewer 2.10.0-beta.13 → 2.10.0-beta.15
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 +182 -172
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -47,14 +47,9 @@ 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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
#logNS = "PrefViewer";
|
|
54
|
-
#log(method, ...args) {
|
|
55
|
-
const fn = console[method] || console.log;
|
|
56
|
-
fn(`[${this.#logNS}]`, ...args);
|
|
57
|
-
}
|
|
50
|
+
initialized = false;
|
|
51
|
+
loaded = false;
|
|
52
|
+
loading = false;
|
|
58
53
|
|
|
59
54
|
#data = {
|
|
60
55
|
containers: {
|
|
@@ -149,7 +144,6 @@ class PrefViewer extends HTMLElement {
|
|
|
149
144
|
// JS fallback if WASM isn’t available
|
|
150
145
|
fallbackUrl: `${DRACO_BASE}/draco_decoder_gltf.js`,
|
|
151
146
|
};
|
|
152
|
-
this.#log("info", "constructed");
|
|
153
147
|
}
|
|
154
148
|
|
|
155
149
|
static get observedAttributes() {
|
|
@@ -157,8 +151,6 @@ class PrefViewer extends HTMLElement {
|
|
|
157
151
|
}
|
|
158
152
|
|
|
159
153
|
attributeChangedCallback(name, _old, value) {
|
|
160
|
-
this.#log("groupCollapsed", `attributeChanged: ${name}`);
|
|
161
|
-
this.#log("info", "new value:", value);
|
|
162
154
|
let data = null;
|
|
163
155
|
switch (name) {
|
|
164
156
|
case "config":
|
|
@@ -172,7 +164,7 @@ class PrefViewer extends HTMLElement {
|
|
|
172
164
|
break;
|
|
173
165
|
case "show-model":
|
|
174
166
|
data = value.toLowerCase?.() === "true";
|
|
175
|
-
if (this
|
|
167
|
+
if (this.initialized) {
|
|
176
168
|
data ? this.showModel() : this.hideModel();
|
|
177
169
|
} else {
|
|
178
170
|
this.#data.containers.model.show = data;
|
|
@@ -180,14 +172,13 @@ class PrefViewer extends HTMLElement {
|
|
|
180
172
|
break;
|
|
181
173
|
case "show-scene":
|
|
182
174
|
data = value.toLowerCase?.() === "true";
|
|
183
|
-
if (this
|
|
175
|
+
if (this.initialized) {
|
|
184
176
|
data ? this.showScene() : this.hideScene();
|
|
185
177
|
} else {
|
|
186
178
|
this.#data.containers.environment.show = data;
|
|
187
179
|
}
|
|
188
180
|
break;
|
|
189
181
|
}
|
|
190
|
-
console.groupEnd();
|
|
191
182
|
}
|
|
192
183
|
|
|
193
184
|
connectedCallback() {
|
|
@@ -195,23 +186,22 @@ class PrefViewer extends HTMLElement {
|
|
|
195
186
|
const error = 'PrefViewer: provide "models" as array of model and environment';
|
|
196
187
|
console.error(error);
|
|
197
188
|
this.dispatchEvent(
|
|
198
|
-
new CustomEvent("
|
|
199
|
-
detail: { error: new Error(error) },
|
|
189
|
+
new CustomEvent("scene-error", {
|
|
200
190
|
bubbles: true,
|
|
191
|
+
cancelable: false,
|
|
201
192
|
composed: true,
|
|
193
|
+
detail: { error: new Error(error) },
|
|
202
194
|
})
|
|
203
195
|
);
|
|
204
196
|
return false;
|
|
205
197
|
}
|
|
206
198
|
|
|
207
|
-
this.#log("info", "connectedCallback: initializing Babylon and starting initial load");
|
|
208
199
|
this.#initializeBabylon();
|
|
200
|
+
this.initialized = true;
|
|
209
201
|
this.#loadContainers(true, true, true);
|
|
210
|
-
this.#initialized = true;
|
|
211
202
|
}
|
|
212
203
|
|
|
213
204
|
disconnectedCallback() {
|
|
214
|
-
this.#log("info", "disconnectedCallback: disposing engine and observers");
|
|
215
205
|
this.#disposeEngine();
|
|
216
206
|
this.#canvasResizeObserver.disconnect();
|
|
217
207
|
}
|
|
@@ -238,50 +228,98 @@ class PrefViewer extends HTMLElement {
|
|
|
238
228
|
this.shadowRoot.append(this.#wrapper);
|
|
239
229
|
}
|
|
240
230
|
|
|
231
|
+
#setStatusSceneLoading(detail) {
|
|
232
|
+
this.loaded = false;
|
|
233
|
+
this.loading = true;
|
|
234
|
+
if (this.hasAttribute("loaded")) {
|
|
235
|
+
this.removeAttribute("loaded");
|
|
236
|
+
}
|
|
237
|
+
this.setAttribute("loading", "");
|
|
238
|
+
this.dispatchEvent(
|
|
239
|
+
new CustomEvent("scene-loading", {
|
|
240
|
+
bubbles: true,
|
|
241
|
+
cancelable: false,
|
|
242
|
+
composed: true,
|
|
243
|
+
detail: detail,
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#setStatusSceneLoaded(detail) {
|
|
249
|
+
this.loaded = true;
|
|
250
|
+
this.loading = false;
|
|
251
|
+
if (this.hasAttribute("loading")) {
|
|
252
|
+
this.removeAttribute("loading");
|
|
253
|
+
}
|
|
254
|
+
this.setAttribute("loaded", "");
|
|
255
|
+
this.dispatchEvent(
|
|
256
|
+
new CustomEvent("scene-loaded", {
|
|
257
|
+
bubbles: true,
|
|
258
|
+
cancelable: false,
|
|
259
|
+
composed: true,
|
|
260
|
+
detail: detail,
|
|
261
|
+
})
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#setStatusOptionsLoading(detail) {
|
|
266
|
+
this.dispatchEvent(
|
|
267
|
+
new CustomEvent("options-loading", {
|
|
268
|
+
bubbles: true,
|
|
269
|
+
cancelable: false,
|
|
270
|
+
composed: true,
|
|
271
|
+
detail: detail,
|
|
272
|
+
})
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#setStatusOptionsLoaded(detail) {
|
|
277
|
+
this.dispatchEvent(
|
|
278
|
+
new CustomEvent("options-loaded", {
|
|
279
|
+
bubbles: true,
|
|
280
|
+
cancelable: false,
|
|
281
|
+
composed: true,
|
|
282
|
+
detail: detail,
|
|
283
|
+
})
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
241
287
|
// Data
|
|
242
288
|
#checkCameraChanged(options) {
|
|
243
289
|
if (!options || !options.camera) {
|
|
244
|
-
this.#log("debug", "checkCameraChanged: no camera in options");
|
|
245
290
|
return false;
|
|
246
291
|
}
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
this.#data.options.camera.
|
|
250
|
-
|
|
251
|
-
this.#log("info", "checkCameraChanged:", { prev, next, changed: this.#data.options.camera.changed });
|
|
252
|
-
return this.#data.options.camera.changed;
|
|
292
|
+
const cameraChanged = options.camera && options.camera !== this.#data.options.camera.value ? true : false
|
|
293
|
+
this.#data.options.camera.changed = cameraChanged ? { oldValue: this.#data.options.camera.value, success: false } : false;
|
|
294
|
+
this.#data.options.camera.value = cameraChanged ? options.camera : this.#data.options.camera.value;
|
|
295
|
+
return cameraChanged;
|
|
253
296
|
}
|
|
254
297
|
|
|
255
298
|
#checkMaterialsChanged(options) {
|
|
256
299
|
if (!options) {
|
|
257
|
-
this.#log("debug", "checkMaterialsChanged: no options object");
|
|
258
300
|
return false;
|
|
259
301
|
}
|
|
260
302
|
let someChanged = false;
|
|
261
303
|
Object.keys(this.#data.options.materials).forEach((material) => {
|
|
262
304
|
const key = `${material}Material`;
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
this.#data.options.materials[material].
|
|
266
|
-
this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? next : prev;
|
|
305
|
+
const materialChanged = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
|
|
306
|
+
this.#data.options.materials[material].changed = materialChanged ? { oldValue: this.#data.options.materials[material].value, success: false } : false;
|
|
307
|
+
this.#data.options.materials[material].value = materialChanged ? options[key] : this.#data.options.materials[material].value;
|
|
267
308
|
someChanged = someChanged || this.#data.options.materials[material].changed;
|
|
268
|
-
this.#log("info", "checkMaterialsChanged item:", { material, key, prev, next, changed: this.#data.options.materials[material].changed });
|
|
269
309
|
});
|
|
270
|
-
this.#log("info", "checkMaterialsChanged: someChanged =", someChanged);
|
|
271
310
|
return someChanged;
|
|
272
311
|
}
|
|
273
312
|
|
|
274
313
|
#storeChangedFlagsForContainer(container) {
|
|
275
314
|
container.timestamp = container.changed.timestamp;
|
|
276
315
|
container.size = container.changed.size;
|
|
277
|
-
|
|
316
|
+
container.changed.success = true;
|
|
278
317
|
}
|
|
279
318
|
|
|
280
319
|
#resetChangedFlags() {
|
|
281
320
|
Object.values(this.#data.containers).forEach((container) => (container.changed = false));
|
|
282
321
|
Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
|
|
283
322
|
this.#data.options.camera.changed = false;
|
|
284
|
-
this.#log("debug", "resetChangedFlags()");
|
|
285
323
|
}
|
|
286
324
|
|
|
287
325
|
// Babylon.js
|
|
@@ -296,7 +334,6 @@ class PrefViewer extends HTMLElement {
|
|
|
296
334
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
297
335
|
this.#canvasResizeObserver.observe(this.#canvas);
|
|
298
336
|
|
|
299
|
-
this.#log("info", "Babylon initialized: creating XR experience (if supported)...");
|
|
300
337
|
await this.#createXRExperience();
|
|
301
338
|
}
|
|
302
339
|
|
|
@@ -315,7 +352,7 @@ class PrefViewer extends HTMLElement {
|
|
|
315
352
|
const sessionMode = "immersive-ar";
|
|
316
353
|
const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
|
|
317
354
|
if (!sessionSupported) {
|
|
318
|
-
|
|
355
|
+
console.info("PrefViewer: WebXR in mode AR is not supported");
|
|
319
356
|
return false;
|
|
320
357
|
}
|
|
321
358
|
|
|
@@ -350,7 +387,6 @@ class PrefViewer extends HTMLElement {
|
|
|
350
387
|
});
|
|
351
388
|
|
|
352
389
|
this.addStylesToARButton();
|
|
353
|
-
this.#log("info", "WebXR experience created");
|
|
354
390
|
} catch (error) {
|
|
355
391
|
console.warn("PrefViewer: failed to create WebXR experience", error);
|
|
356
392
|
this.#XRExperience = null;
|
|
@@ -366,9 +402,8 @@ class PrefViewer extends HTMLElement {
|
|
|
366
402
|
this.#camera.lowerRadiusLimit = 5;
|
|
367
403
|
this.#camera.upperRadiusLimit = 20;
|
|
368
404
|
this.#camera.metadata = { locked: false }
|
|
369
|
-
this.#camera = this.#camera;
|
|
370
405
|
this.#camera.attachControl(this.#canvas, true);
|
|
371
|
-
this.#
|
|
406
|
+
this.#scene.activeCamera = this.#camera;
|
|
372
407
|
}
|
|
373
408
|
|
|
374
409
|
#createLights() {
|
|
@@ -391,8 +426,6 @@ class PrefViewer extends HTMLElement {
|
|
|
391
426
|
this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
|
|
392
427
|
this.#cameraLight.parent = this.#camera;
|
|
393
428
|
this.#cameraLight.intensity = 0.3;
|
|
394
|
-
|
|
395
|
-
this.#log("info", "Lights & shadows configured");
|
|
396
429
|
}
|
|
397
430
|
|
|
398
431
|
#setupInteraction() {
|
|
@@ -415,7 +448,6 @@ class PrefViewer extends HTMLElement {
|
|
|
415
448
|
this.#engine = this.#scene = this.#camera = null;
|
|
416
449
|
this.#hemiLight = this.#dirLight = this.#cameraLight = null;
|
|
417
450
|
this.#shadowGen = null;
|
|
418
|
-
this.#log("info", "Engine disposed");
|
|
419
451
|
}
|
|
420
452
|
|
|
421
453
|
// Utility methods for loading gltf/glb
|
|
@@ -428,13 +460,13 @@ class PrefViewer extends HTMLElement {
|
|
|
428
460
|
if (xhr.status === 200) {
|
|
429
461
|
const size = parseInt(xhr.getResponseHeader("Content-Length"));
|
|
430
462
|
const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
|
|
431
|
-
resolve(size, timestamp);
|
|
463
|
+
resolve([size, timestamp]);
|
|
432
464
|
} else {
|
|
433
|
-
resolve(0, null);
|
|
465
|
+
resolve([0, null]);
|
|
434
466
|
}
|
|
435
467
|
};
|
|
436
468
|
xhr.onerror = () => {
|
|
437
|
-
resolve(0, null);
|
|
469
|
+
resolve([0, null]);
|
|
438
470
|
};
|
|
439
471
|
xhr.send();
|
|
440
472
|
});
|
|
@@ -459,7 +491,7 @@ class PrefViewer extends HTMLElement {
|
|
|
459
491
|
return { blob, extension, size };
|
|
460
492
|
}
|
|
461
493
|
let isJson = false;
|
|
462
|
-
|
|
494
|
+
try {
|
|
463
495
|
JSON.parse(decoded);
|
|
464
496
|
isJson = true;
|
|
465
497
|
} catch {}
|
|
@@ -484,47 +516,47 @@ class PrefViewer extends HTMLElement {
|
|
|
484
516
|
}
|
|
485
517
|
show = show !== undefined ? show : this.#data.containers.environment.visible;
|
|
486
518
|
const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
|
|
487
|
-
|
|
488
|
-
this.#log("debug", "setVisibilityOfWallAndFloorInModel:", { show, affectedMeshes: meshes.map(m => m.name) });
|
|
489
|
-
meshes.forEach((mesh) => mesh.setEnabled(show));
|
|
519
|
+
this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
|
|
490
520
|
}
|
|
491
521
|
|
|
492
522
|
#setOptionsMaterial(optionMaterial) {
|
|
493
523
|
if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
|
|
494
|
-
this.#log("debug", "setOptionsMaterial: missing data", optionMaterial);
|
|
495
524
|
return false;
|
|
496
525
|
}
|
|
497
526
|
|
|
498
527
|
const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
|
|
499
528
|
if (!material) {
|
|
500
|
-
this.#log("warn", `setOptionsMaterial: material "${optionMaterial.value}" not found in materials container`);
|
|
501
529
|
return false;
|
|
502
530
|
}
|
|
503
531
|
|
|
504
532
|
const containers = [];
|
|
505
|
-
if (this.#data.containers.model.assetContainer && (this.#data.containers.model.
|
|
533
|
+
if (this.#data.containers.model.assetContainer && (this.#data.containers.model.changed || optionMaterial.changed)) {
|
|
506
534
|
containers.push(this.#data.containers.model.assetContainer);
|
|
507
535
|
}
|
|
508
|
-
if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.
|
|
536
|
+
if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.changed || optionMaterial.changed)) {
|
|
509
537
|
containers.push(this.#data.containers.environment.assetContainer);
|
|
510
538
|
}
|
|
511
539
|
if (containers.length === 0) {
|
|
512
|
-
this.#log("debug", "setOptionsMaterial: no target containers (unchanged)");
|
|
513
540
|
return false;
|
|
514
541
|
}
|
|
515
542
|
|
|
516
|
-
let
|
|
543
|
+
let someSetted = false;
|
|
517
544
|
containers.forEach((container) =>
|
|
518
545
|
container.meshes
|
|
519
546
|
.filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix))
|
|
520
547
|
.forEach((mesh) => {
|
|
521
548
|
mesh.material = material;
|
|
522
|
-
|
|
549
|
+
someSetted = true;
|
|
523
550
|
})
|
|
524
551
|
);
|
|
525
552
|
|
|
526
|
-
|
|
527
|
-
|
|
553
|
+
if (someSetted) {
|
|
554
|
+
optionMaterial.changed.success = true;
|
|
555
|
+
} else {
|
|
556
|
+
optionMaterial.value = optionMaterial.changed.oldValue;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return someSetted;
|
|
528
560
|
}
|
|
529
561
|
|
|
530
562
|
#setOptionsMaterials() {
|
|
@@ -533,29 +565,36 @@ class PrefViewer extends HTMLElement {
|
|
|
533
565
|
let settedMaterial = this.#setOptionsMaterial(material);
|
|
534
566
|
someSetted = someSetted || settedMaterial;
|
|
535
567
|
});
|
|
536
|
-
this.#log("info", "setOptionsMaterials: anyApplied =", someSetted);
|
|
537
568
|
return someSetted;
|
|
538
569
|
}
|
|
539
570
|
|
|
540
571
|
#setOptionsCamera() {
|
|
541
|
-
if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.
|
|
542
|
-
this.#log("debug", "setOptionsCamera: skipped (no change or no camera specified)");
|
|
572
|
+
if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.changed && !this.#data.containers.environment.changed)) {
|
|
543
573
|
return false;
|
|
544
574
|
}
|
|
545
575
|
|
|
546
|
-
let camera = this.#data.containers.model.assetContainer?.cameras.find((
|
|
576
|
+
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;
|
|
547
577
|
if (!camera) {
|
|
548
|
-
this.#
|
|
549
|
-
|
|
578
|
+
if (this.#data.options.camera.changed?.oldValue && this.#data.options.camera.changed?.oldValue !== this.#data.options.camera.value) {
|
|
579
|
+
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 === his.#data.options.camera.changed.oldValue) || null;
|
|
580
|
+
}
|
|
581
|
+
if (camera){
|
|
582
|
+
camera.metadata = { locked: this.#data.options.camera.changed.oldLocked };
|
|
583
|
+
this.#data.options.camera.value = this.#data.options.camera.changed.oldValue;
|
|
584
|
+
this.#data.options.camera.locked = this.#data.options.camera.changed.oldLocked;
|
|
585
|
+
} else {
|
|
586
|
+
camera = this.#camera;
|
|
587
|
+
this.#data.options.camera.value = null;
|
|
588
|
+
this.#data.options.camera.locked = this.#camera.metadata.locked;
|
|
589
|
+
}
|
|
590
|
+
this.#data.options.camera.changed.success = false;
|
|
591
|
+
} else {
|
|
592
|
+
camera.metadata = { locked: this.#data.options.camera.locked };
|
|
550
593
|
}
|
|
551
|
-
|
|
552
|
-
camera.metadata = { locked: this.#data.options.camera.locked };
|
|
553
|
-
if (!this.#data.options.camera.locked) {
|
|
594
|
+
if (!this.#data.options.camera.locked && this.#data.options.camera.value !== null) {
|
|
554
595
|
camera.attachControl(this.#canvas, true);
|
|
555
596
|
}
|
|
556
597
|
this.#scene.activeCamera = camera;
|
|
557
|
-
this.#log("info", "setOptionsCamera: activated", { name: camera.name, locked: this.#data.options.camera.locked });
|
|
558
|
-
|
|
559
598
|
return true;
|
|
560
599
|
}
|
|
561
600
|
|
|
@@ -563,7 +602,6 @@ class PrefViewer extends HTMLElement {
|
|
|
563
602
|
if (container.assetContainer && !container.visible && container.show) {
|
|
564
603
|
container.assetContainer.addAllToScene();
|
|
565
604
|
container.visible = true;
|
|
566
|
-
this.#log("debug", "addContainer -> visible", container.name);
|
|
567
605
|
}
|
|
568
606
|
}
|
|
569
607
|
|
|
@@ -571,7 +609,6 @@ class PrefViewer extends HTMLElement {
|
|
|
571
609
|
if (container.assetContainer && container.visible) {
|
|
572
610
|
container.assetContainer.removeAllFromScene();
|
|
573
611
|
container.visible = false;
|
|
574
|
-
this.#log("debug", "removeContainer -> hidden", container.name);
|
|
575
612
|
}
|
|
576
613
|
}
|
|
577
614
|
|
|
@@ -583,24 +620,15 @@ class PrefViewer extends HTMLElement {
|
|
|
583
620
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
584
621
|
});
|
|
585
622
|
this.#addContainer(container);
|
|
586
|
-
this.#log("debug", "replaceContainer:", container.name, {
|
|
587
|
-
meshes: container.assetContainer.meshes?.length,
|
|
588
|
-
materials: container.assetContainer.materials?.length,
|
|
589
|
-
cameras: container.assetContainer.cameras?.length,
|
|
590
|
-
});
|
|
591
623
|
}
|
|
592
624
|
|
|
593
625
|
async #loadAssetContainer(container) {
|
|
594
626
|
let storage = container?.storage;
|
|
595
627
|
|
|
596
628
|
if (!storage) {
|
|
597
|
-
this.#log("debug", `loadAssetContainer(${container?.name}): no storage provided`);
|
|
598
629
|
return false;
|
|
599
630
|
}
|
|
600
631
|
|
|
601
|
-
this.#log("groupCollapsed", `loadAssetContainer -> ${container.name}`);
|
|
602
|
-
this.#log("info", "storage:", storage);
|
|
603
|
-
|
|
604
632
|
let source = storage.url || null;
|
|
605
633
|
|
|
606
634
|
if (storage.db && storage.table && storage.id) {
|
|
@@ -608,18 +636,13 @@ class PrefViewer extends HTMLElement {
|
|
|
608
636
|
const object = await loadModel(storage.id, storage.table);
|
|
609
637
|
source = object.data;
|
|
610
638
|
if (object.timestamp === container.timestamp) {
|
|
611
|
-
this.#log("info", "IndexedDB source unchanged (timestamp match). Skipping reload.");
|
|
612
|
-
console.groupEnd();
|
|
613
639
|
return false;
|
|
614
640
|
} else {
|
|
615
|
-
container.changed = { timestamp: object.timestamp, size: object.size };
|
|
616
|
-
this.#log("info", "IndexedDB source changed:", container.changed);
|
|
641
|
+
container.changed = { timestamp: object.timestamp, size: object.size, success: false };
|
|
617
642
|
}
|
|
618
643
|
}
|
|
619
644
|
|
|
620
645
|
if (!source) {
|
|
621
|
-
this.#log("warn", "no source (url/db) to load");
|
|
622
|
-
console.groupEnd();
|
|
623
646
|
return false;
|
|
624
647
|
}
|
|
625
648
|
|
|
@@ -632,25 +655,19 @@ class PrefViewer extends HTMLElement {
|
|
|
632
655
|
});
|
|
633
656
|
if (!container.changed) {
|
|
634
657
|
if (container.timestamp === null && container.size === size) {
|
|
635
|
-
this.#log("info", "Base64 source unchanged (size match). Skipping reload.");
|
|
636
|
-
console.groupEnd();
|
|
637
658
|
return false;
|
|
638
659
|
} else {
|
|
639
|
-
container.changed = { timestamp: null, size: size };
|
|
640
|
-
this.#log("info", "Base64 source changed (size):", container.changed);
|
|
660
|
+
container.changed = { timestamp: null, size: size, success: false };
|
|
641
661
|
}
|
|
642
662
|
}
|
|
643
663
|
} else {
|
|
644
664
|
const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
|
|
645
665
|
extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
|
|
646
|
-
const
|
|
647
|
-
if (container.
|
|
648
|
-
this.#log("info", "URL source unchanged (HEAD size/timestamp). Skipping reload.");
|
|
649
|
-
console.groupEnd();
|
|
666
|
+
const [fileSize, fileTimestamp ] = await this.#getServerFileDataHeader(source);
|
|
667
|
+
if (container.size === fileSize && container.timestamp === fileTimestamp) {
|
|
650
668
|
return false;
|
|
651
669
|
} else {
|
|
652
|
-
container.changed = { timestamp: fileTimestamp, size: fileSize };
|
|
653
|
-
this.#log("info", "URL source changed:", container.changed);
|
|
670
|
+
container.changed = { timestamp: fileTimestamp, size: fileSize, success: false };
|
|
654
671
|
}
|
|
655
672
|
}
|
|
656
673
|
|
|
@@ -664,36 +681,35 @@ class PrefViewer extends HTMLElement {
|
|
|
664
681
|
},
|
|
665
682
|
};
|
|
666
683
|
|
|
667
|
-
|
|
668
|
-
const result = await LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
669
|
-
this.#log("info", "Asset container loaded:", {
|
|
670
|
-
meshes: result.meshes?.length,
|
|
671
|
-
materials: result.materials?.length,
|
|
672
|
-
cameras: result.cameras?.length,
|
|
673
|
-
});
|
|
674
|
-
console.groupEnd();
|
|
675
|
-
return result;
|
|
684
|
+
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
676
685
|
}
|
|
677
686
|
|
|
678
|
-
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
679
|
-
this.#log("groupCollapsed", "loadContainers flags", { loadModel, loadEnvironment, loadMaterials });
|
|
687
|
+
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
680
688
|
const promiseArray = [];
|
|
681
689
|
promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
|
|
682
690
|
promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
|
|
683
691
|
promiseArray.push(loadMaterials ? this.#loadAssetContainer(this.#data.containers.materials) : false);
|
|
684
692
|
|
|
693
|
+
const loadingDetail = {
|
|
694
|
+
model: !!this.#data.containers.model.changed,
|
|
695
|
+
environment: !!this.#data.containers.environment.changed,
|
|
696
|
+
materials: !!this.#data.containers.materials.changed,
|
|
697
|
+
options: {
|
|
698
|
+
camera: !!this.#data.options.camera.changed,
|
|
699
|
+
inneWallMaterial: !!this.#data.options.materials.innerWall.changed,
|
|
700
|
+
outerWallMaterial: !!this.#data.options.materials.outerWall.changed,
|
|
701
|
+
innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed,
|
|
702
|
+
outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed,
|
|
703
|
+
},
|
|
704
|
+
};
|
|
705
|
+
this.#setStatusSceneLoading(loadingDetail);
|
|
706
|
+
|
|
685
707
|
Promise.allSettled(promiseArray)
|
|
686
708
|
.then(async (values) => {
|
|
687
709
|
const modelContainer = values[0];
|
|
688
710
|
const environmentContainer = values[1];
|
|
689
711
|
const materialsContainer = values[2];
|
|
690
712
|
|
|
691
|
-
this.#log("info", "loadContainers results:", {
|
|
692
|
-
model: modelContainer?.status,
|
|
693
|
-
environment: environmentContainer?.status,
|
|
694
|
-
materials: materialsContainer?.status,
|
|
695
|
-
});
|
|
696
|
-
|
|
697
713
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
698
714
|
this.#replaceContainer(this.#data.containers.model, modelContainer.value);
|
|
699
715
|
this.#storeChangedFlagsForContainer(this.#data.containers.model);
|
|
@@ -713,45 +729,45 @@ class PrefViewer extends HTMLElement {
|
|
|
713
729
|
this.#storeChangedFlagsForContainer(this.#data.containers.materials);
|
|
714
730
|
}
|
|
715
731
|
|
|
716
|
-
|
|
717
|
-
|
|
732
|
+
this.#setOptionsMaterials();
|
|
733
|
+
this.#setOptionsCamera();
|
|
718
734
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
719
|
-
|
|
720
|
-
|
|
735
|
+
|
|
736
|
+
const loadedDetail = {
|
|
737
|
+
model: !!this.#data.containers.model.changed?.success,
|
|
738
|
+
environment: !!this.#data.containers.environment.changed?.success,
|
|
739
|
+
materials: !!this.#data.containers.materials.changed?.success,
|
|
740
|
+
options: {
|
|
741
|
+
camera: !!this.#data.options.camera.changed?.success,
|
|
742
|
+
inneWallMaterial: !!this.#data.options.materials.innerWall.changed?.success,
|
|
743
|
+
outerWallMaterial: !!this.#data.options.materials.outerWall.changed?.success,
|
|
744
|
+
innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed?.success,
|
|
745
|
+
outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed?.success,
|
|
746
|
+
},
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
this.#setStatusSceneLoaded(loadedDetail);
|
|
721
750
|
|
|
722
751
|
this.#resetChangedFlags();
|
|
723
|
-
|
|
724
|
-
this.dispatchEvent(
|
|
725
|
-
new CustomEvent("model-loaded", {
|
|
726
|
-
detail: { success: "" },
|
|
727
|
-
bubbles: true,
|
|
728
|
-
composed: true,
|
|
729
|
-
})
|
|
730
|
-
);
|
|
731
|
-
this.#log("info", "model-loaded event dispatched");
|
|
732
|
-
console.groupEnd();
|
|
733
752
|
})
|
|
734
753
|
.catch((error) => {
|
|
754
|
+
this.loaded = true;
|
|
735
755
|
console.error("PrefViewer: failed to load model", error);
|
|
736
756
|
this.dispatchEvent(
|
|
737
|
-
new CustomEvent("
|
|
738
|
-
detail: { error: error },
|
|
757
|
+
new CustomEvent("scene-error", {
|
|
739
758
|
bubbles: true,
|
|
759
|
+
cancelable: false,
|
|
740
760
|
composed: true,
|
|
761
|
+
detail: { error: error },
|
|
741
762
|
})
|
|
742
763
|
);
|
|
743
|
-
this.#log("error", "model-error event dispatched");
|
|
744
|
-
console.groupEnd();
|
|
745
764
|
});
|
|
746
765
|
}
|
|
747
766
|
|
|
748
767
|
// Public Methods
|
|
749
768
|
loadConfig(config) {
|
|
750
|
-
this.#log("groupCollapsed", "loadConfig called with:", config);
|
|
751
769
|
config = typeof config === "string" ? JSON.parse(config) : config;
|
|
752
770
|
if (!config) {
|
|
753
|
-
this.#log("warn", "loadConfig: no config provided");
|
|
754
|
-
console.groupEnd();
|
|
755
771
|
return false;
|
|
756
772
|
}
|
|
757
773
|
|
|
@@ -762,97 +778,91 @@ class PrefViewer extends HTMLElement {
|
|
|
762
778
|
this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
|
|
763
779
|
this.#data.containers.materials.storage = config.materials?.storage || null;
|
|
764
780
|
|
|
765
|
-
this.#log("info", "containers set from config", {
|
|
766
|
-
model: this.#data.containers.model,
|
|
767
|
-
environment: this.#data.containers.environment,
|
|
768
|
-
materials: this.#data.containers.materials,
|
|
769
|
-
});
|
|
770
|
-
|
|
771
781
|
// Options
|
|
772
782
|
if (config.options) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
this.#log("info", "options parsed", { camChanged, matsChanged, options: this.#data.options });
|
|
776
|
-
} else {
|
|
777
|
-
this.#log("debug", "no options in config");
|
|
783
|
+
this.#checkCameraChanged(config.options);
|
|
784
|
+
this.#checkMaterialsChanged(config.options);
|
|
778
785
|
}
|
|
779
786
|
|
|
780
|
-
this
|
|
781
|
-
console.groupEnd();
|
|
787
|
+
this.initialized && this.#loadContainers(true, true, true);
|
|
782
788
|
}
|
|
783
789
|
|
|
784
790
|
setOptions(options) {
|
|
785
|
-
this.#log("groupCollapsed", "setOptions called with:", options);
|
|
786
791
|
if (!options) {
|
|
787
|
-
this.#log("warn", "setOptions: no options");
|
|
788
|
-
console.groupEnd();
|
|
789
792
|
return false;
|
|
790
793
|
}
|
|
794
|
+
|
|
795
|
+
const cameraChanged = this.#checkCameraChanged(options);
|
|
796
|
+
const materialsChanged = this.#checkMaterialsChanged(options);
|
|
797
|
+
|
|
798
|
+
const loadingDetail = {
|
|
799
|
+
camera: !!this.#data.options.camera.changed,
|
|
800
|
+
inneWallMaterial: !!this.#data.options.materials.innerWall.changed,
|
|
801
|
+
outerWallMaterial: !!this.#data.options.materials.outerWall.changed,
|
|
802
|
+
innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed,
|
|
803
|
+
outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed,
|
|
804
|
+
};
|
|
805
|
+
this.#setStatusOptionsLoading(loadingDetail);
|
|
806
|
+
|
|
791
807
|
let someSetted = false;
|
|
792
|
-
if (
|
|
808
|
+
if (cameraChanged) {
|
|
793
809
|
someSetted = someSetted || this.#setOptionsCamera();
|
|
794
810
|
}
|
|
795
|
-
if (
|
|
811
|
+
if (materialsChanged) {
|
|
796
812
|
someSetted = someSetted || this.#setOptionsMaterials();
|
|
797
813
|
}
|
|
814
|
+
|
|
815
|
+
const loadedDetail = {
|
|
816
|
+
camera: !!this.#data.options.camera.changed?.success,
|
|
817
|
+
inneWallMaterial: !!this.#data.options.materials.innerWall.changed?.success,
|
|
818
|
+
outerWallMaterial: !!this.#data.options.materials.outerWall.changed?.success,
|
|
819
|
+
innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed?.success,
|
|
820
|
+
outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed?.success,
|
|
821
|
+
};
|
|
822
|
+
this.#setStatusOptionsLoaded(loadedDetail);
|
|
823
|
+
|
|
798
824
|
this.#resetChangedFlags();
|
|
799
|
-
|
|
800
|
-
debugger;
|
|
801
|
-
console.groupEnd();
|
|
825
|
+
|
|
802
826
|
return someSetted;
|
|
803
827
|
}
|
|
804
828
|
|
|
805
829
|
loadModel(model) {
|
|
806
|
-
this.#log("groupCollapsed", "loadModel called with:", model);
|
|
807
830
|
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
808
831
|
if (!model) {
|
|
809
|
-
this.#log("warn", "loadModel: no model object");
|
|
810
|
-
console.groupEnd();
|
|
811
832
|
return false;
|
|
812
833
|
}
|
|
813
834
|
this.#data.containers.model.storage = model.storage || null;
|
|
814
835
|
this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
|
|
815
|
-
this.#
|
|
816
|
-
this.#initialized && this.#loadContainers(true, false, false);
|
|
817
|
-
console.groupEnd();
|
|
836
|
+
this.initialized && this.#loadContainers(true, false, false);
|
|
818
837
|
}
|
|
819
838
|
|
|
820
839
|
loadScene(scene) {
|
|
821
|
-
this.#log("groupCollapsed", "loadScene called with:", scene);
|
|
822
840
|
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
823
841
|
if (!scene) {
|
|
824
|
-
this.#log("warn", "loadScene: no scene object");
|
|
825
|
-
console.groupEnd();
|
|
826
842
|
return false;
|
|
827
843
|
}
|
|
828
844
|
this.#data.containers.environment.storage = scene.storage || null;
|
|
829
845
|
this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
|
|
830
|
-
this.#
|
|
831
|
-
this.#initialized && this.#loadContainers(false, true, false);
|
|
832
|
-
console.groupEnd();
|
|
846
|
+
this.initialized && this.#loadContainers(false, true, false);
|
|
833
847
|
}
|
|
834
848
|
|
|
835
849
|
showModel() {
|
|
836
|
-
this.#log("info", "showModel()");
|
|
837
850
|
this.#data.containers.model.show = true;
|
|
838
851
|
this.#addContainer(this.#data.containers.model);
|
|
839
852
|
}
|
|
840
853
|
|
|
841
854
|
hideModel() {
|
|
842
|
-
this.#log("info", "hideModel()");
|
|
843
855
|
this.#data.containers.model.show = false;
|
|
844
856
|
this.#removeContainer(this.#data.containers.model);
|
|
845
857
|
}
|
|
846
858
|
|
|
847
859
|
showScene() {
|
|
848
|
-
this.#log("info", "showScene()");
|
|
849
860
|
this.#data.containers.environment.show = true;
|
|
850
861
|
this.#addContainer(this.#data.containers.environment);
|
|
851
862
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
852
863
|
}
|
|
853
864
|
|
|
854
865
|
hideScene() {
|
|
855
|
-
this.#log("info", "hideScene()");
|
|
856
866
|
this.#data.containers.environment.show = false;
|
|
857
867
|
this.#removeContainer(this.#data.containers.environment);
|
|
858
868
|
this.#setVisibilityOfWallAndFloorInModel();
|