@preference-sl/pref-viewer 2.10.0-beta.3 → 2.10.0-beta.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +286 -80
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.3",
3
+ "version": "2.10.0-beta.4",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -49,18 +49,68 @@ import { initDb, loadModel } from "./gltf-storage.js";
49
49
  class PrefViewer extends HTMLElement {
50
50
  #initialized = false;
51
51
 
52
- #model = {
53
- container: null,
54
- show: true, // Show model by default
55
- storage: null,
56
- visible: false,
57
- };
58
-
59
- #environment = {
60
- container: null,
61
- show: true, // Show environment by default
62
- storage: null,
63
- visible: false,
52
+ #data = {
53
+ containers: {
54
+ model: {
55
+ name: "model",
56
+ container: null,
57
+ show: true,
58
+ storage: null,
59
+ visible: false,
60
+ size: null,
61
+ timestamp: null,
62
+ changed: false,
63
+ },
64
+ environment: {
65
+ name: "environment",
66
+ container: null,
67
+ show: true,
68
+ storage: null,
69
+ visible: false,
70
+ size: null,
71
+ timestamp: null,
72
+ changed: false,
73
+ },
74
+ materials: {
75
+ name: "materials",
76
+ container: null,
77
+ storage: null,
78
+ show: true,
79
+ visible: false,
80
+ size: null,
81
+ timestamp: null,
82
+ changed: false,
83
+ },
84
+ },
85
+ options: {
86
+ camera: {
87
+ value: null,
88
+ locked: true,
89
+ changed: false,
90
+ },
91
+ materials: {
92
+ innerWall: {
93
+ value: null,
94
+ prefix: "innerWall",
95
+ changed: false,
96
+ },
97
+ outerWall: {
98
+ value: null,
99
+ prefix: "outerWall",
100
+ changed: false,
101
+ },
102
+ innerFloor: {
103
+ value: null,
104
+ prefix: "innerFloor",
105
+ changed: false,
106
+ },
107
+ outerFloor: {
108
+ value: null,
109
+ prefix: "outerFloor",
110
+ changed: false,
111
+ },
112
+ },
113
+ },
64
114
  };
65
115
 
66
116
  // DOM elements
@@ -115,7 +165,7 @@ class PrefViewer extends HTMLElement {
115
165
  if (this.#initialized) {
116
166
  data ? this.showModel() : this.hideModel();
117
167
  } else {
118
- this.#model.show = data;
168
+ this.#data.containers.model.show = data;
119
169
  }
120
170
  break;
121
171
  case "show-scene":
@@ -123,7 +173,7 @@ class PrefViewer extends HTMLElement {
123
173
  if (this.#initialized) {
124
174
  data ? this.showScene() : this.hideScene();
125
175
  } else {
126
- this.#environment.show = data;
176
+ this.#data.containers.environment.show = data;
127
177
  }
128
178
  break;
129
179
  }
@@ -144,7 +194,7 @@ class PrefViewer extends HTMLElement {
144
194
  }
145
195
 
146
196
  this.#initializeBabylon();
147
- this.#loadContainers(true, true);
197
+ this.#loadContainers(true, true, true);
148
198
  this.#initialized = true;
149
199
  }
150
200
 
@@ -174,8 +224,39 @@ class PrefViewer extends HTMLElement {
174
224
  this.#wrapper.appendChild(this.#canvas);
175
225
  this.shadowRoot.append(this.#wrapper);
176
226
  }
177
-
178
- // Bbylon.js
227
+
228
+ // Data
229
+ #checkCameraChanged(options) {
230
+ if (!options || !options.camera) {
231
+ return false;
232
+ }
233
+ this.#data.options.camera.changed = options.camera && options.camera !== this.#data.options.camera.value ? true : false;
234
+ this.#data.options.camera.value = this.#data.options.camera.changed ? options.camera : this.#data.options.camera.value;
235
+ }
236
+
237
+ #checkMaterialsChanged(options) {
238
+ if (!options) {
239
+ return false;
240
+ }
241
+ Object.keys(this.#data.options.materials).forEach((material) => {
242
+ const key = `${material}Material`;
243
+ this.#data.options.materials[material].changed = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
244
+ this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : this.#data.options.materials[material].value;
245
+ });
246
+ }
247
+
248
+ #storeChangedFlagsForContainer(container) {
249
+ container.timestamp = container.changed.timestamp;
250
+ container.size = container.changed.size;
251
+ }
252
+
253
+ #resetChangedFlags() {
254
+ Object.values(this.#data.containers).forEach((container) => (container.changed = false));
255
+ Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
256
+ this.#data.options.camera.changed = false;
257
+ }
258
+
259
+ // Babylon.js
179
260
  async #initializeBabylon() {
180
261
  this.#engine = new Engine(this.#canvas, true, { alpha: true });
181
262
  this.#scene = new Scene(this.#engine);
@@ -183,15 +264,14 @@ class PrefViewer extends HTMLElement {
183
264
  this.#createCamera();
184
265
  this.#createLights();
185
266
  this.#setupInteraction();
186
-
267
+
187
268
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
188
269
  this.#canvasResizeObserver.observe(this.#canvas);
189
270
 
190
271
  await this.#createXRExperience();
191
-
192
272
  }
193
273
 
194
- addStylesToARButton(){
274
+ addStylesToARButton() {
195
275
  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"}';
196
276
  const style = document.createElement("style");
197
277
  style.appendChild(document.createTextNode(css));
@@ -202,14 +282,14 @@ class PrefViewer extends HTMLElement {
202
282
  if (this.#XRExperience) {
203
283
  return true;
204
284
  }
205
-
285
+
206
286
  const sessionMode = "immersive-ar";
207
287
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
208
288
  if (!sessionSupported) {
209
289
  console.info("PrefViewer: WebXR in mode AR is not supported");
210
290
  return false;
211
291
  }
212
-
292
+
213
293
  try {
214
294
  const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
215
295
  ground.isVisible = false;
@@ -231,7 +311,6 @@ class PrefViewer extends HTMLElement {
231
311
  xrInput: this.#XRExperience.input,
232
312
  floorMeshes: [ground],
233
313
  timeToTeleport: 1500,
234
- useMainComponentOnly: true,
235
314
  });
236
315
 
237
316
  this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
@@ -251,11 +330,13 @@ class PrefViewer extends HTMLElement {
251
330
  #canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
252
331
 
253
332
  #createCamera() {
254
- this.#camera = new ArcRotateCamera("camera", 3 * Math.PI / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
333
+ this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
255
334
  this.#camera.upperBetaLimit = Math.PI * 0.48;
256
335
  this.#camera.lowerBetaLimit = Math.PI * 0.25;
257
336
  this.#camera.lowerRadiusLimit = 5;
258
337
  this.#camera.upperRadiusLimit = 20;
338
+ this.#camera.metadata = { locked: false }
339
+ this.#camera = this.#camera;
259
340
  this.#camera.attachControl(this.#canvas, true);
260
341
  }
261
342
 
@@ -283,10 +364,14 @@ class PrefViewer extends HTMLElement {
283
364
 
284
365
  #setupInteraction() {
285
366
  this.#canvas.addEventListener("wheel", (event) => {
286
- if (!this.#scene || !this.#camera) return;
287
- const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
367
+ if (!this.#scene || !this.#camera) {
368
+ return false;
369
+ }
370
+ //const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
288
371
  //this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
289
- this.#camera.inertialRadiusOffset -= event.deltaY * this.#camera.wheelPrecision * 0.001;
372
+ if (!this.#scene.activeCamera.metadata?.locked) {
373
+ this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
374
+ }
290
375
  event.preventDefault();
291
376
  });
292
377
  }
@@ -300,6 +385,27 @@ class PrefViewer extends HTMLElement {
300
385
  }
301
386
 
302
387
  // Utility methods for loading gltf/glb
388
+ async #getServerFileDataHeader(uri) {
389
+ return new Promise((resolve) => {
390
+ const xhr = new XMLHttpRequest();
391
+ xhr.open("HEAD", uri, true);
392
+ xhr.responseType = "blob";
393
+ xhr.onload = () => {
394
+ if (xhr.status === 200) {
395
+ const size = parseInt(xhr.getResponseHeader("Content-Length"));
396
+ const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
397
+ resolve(size, timestamp);
398
+ } else {
399
+ resolve(0, null);
400
+ }
401
+ };
402
+ xhr.onerror = () => {
403
+ resolve(0, null);
404
+ };
405
+ xhr.send();
406
+ });
407
+ }
408
+
303
409
  #transformUrl(url) {
304
410
  return new Promise((resolve) => {
305
411
  resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
@@ -312,10 +418,11 @@ class PrefViewer extends HTMLElement {
312
418
  let decoded = "";
313
419
  let blob = null;
314
420
  let extension = null;
421
+ let size = raw.length;
315
422
  try {
316
423
  decoded = atob(raw);
317
424
  } catch {
318
- return { blob, extension };
425
+ return { blob, extension, size };
319
426
  }
320
427
  let isJson = false;
321
428
  try {
@@ -326,7 +433,7 @@ class PrefViewer extends HTMLElement {
326
433
  const type = isJson ? "model/gltf+json" : "model/gltf-binary";
327
434
  const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
328
435
  blob = new Blob([array], { type });
329
- return { blob, extension };
436
+ return { blob, extension, size };
330
437
  }
331
438
 
332
439
  async #initStorage(db, table) {
@@ -338,41 +445,95 @@ class PrefViewer extends HTMLElement {
338
445
 
339
446
  // Methods for managing Asset Containers
340
447
  #setVisibilityOfWallAndFloorInModel(show) {
341
- if (this.#model.container && this.#model.visible) {
342
- const names = ["outer_0", "inner_1", "outerFloor", "innerFloor"];
343
- const nodes = this.#model.container.getNodes();
344
- this.#model.container
345
- .getNodes()
346
- .filter((filter) => names.includes(filter.name))
347
- .forEach((node) => node.setEnabled(show !== undefined ? show : this.#environment.show));
448
+ if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
449
+ return false;
348
450
  }
451
+ show = show !== undefined ? show : this.#data.containers.environment.visible;
452
+ const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
453
+ this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
349
454
  }
350
455
 
351
- #addContainer(group) {
352
- if (group.container && !group.visible && group.show) {
353
- group.container.addAllToScene();
354
- group.visible = true;
456
+ #setOptionsMaterial(optionMaterial) {
457
+ if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
458
+ return false;
355
459
  }
460
+
461
+ const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
462
+ if (!material) {
463
+ return false;
464
+ }
465
+
466
+ const containers = [];
467
+ if (this.#data.containers.model.assetContainer && (this.#data.containers.model.assetContainer.changed || optionMaterial.changed)) {
468
+ containers.push(this.#data.containers.model.assetContainer);
469
+ }
470
+ if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.assetContainer.changed || optionMaterial.changed)) {
471
+ containers.push(this.#data.containers.environment.assetContainer);
472
+ }
473
+ if (containers.length === 0) {
474
+ return false;
475
+ }
476
+
477
+ containers.forEach((container) => {
478
+ container.meshes.filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix)).forEach((mesh) => {
479
+ if (mesh.material) {
480
+ mesh.material.dispose();
481
+ }
482
+ mesh.material = material;
483
+ });
484
+ });
485
+
486
+ return true;
487
+ }
488
+
489
+ #setOptionsMaterials() {
490
+ Object.values(this.#data.options.materials).forEach((material) => this.#setOptionsMaterial(material));
356
491
  }
357
492
 
358
- #removeContainer(group) {
359
- if (group.container && group.visible) {
360
- group.container.removeAllFromScene();
361
- group.visible = false;
493
+ #setOptionsCamera() {
494
+ if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.assetContainer.changed)) {
495
+ return false;
496
+ }
497
+ let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
498
+ if (!camera) {
499
+ camera = this.#camera;
500
+ } else {
501
+ camera.metadata = { locked: this.#data.options.camera.locked };
502
+ if (!this.#data.options.camera.locked) {
503
+ camera.attachControl(this.#canvas, true);
504
+ }
362
505
  }
506
+
507
+ this.#scene.activeCamera = camera;
363
508
  }
364
509
 
365
- #replaceContainer(group, newContainer) {
366
- this.#removeContainer(group);
367
- group.container = newContainer;
368
- group.container.meshes.forEach((mesh) => {
510
+ #addContainer(container) {
511
+ if (container.assetContainer && !container.visible && container.show) {
512
+ container.assetContainer.addAllToScene();
513
+ container.visible = true;
514
+ }
515
+ }
516
+
517
+ #removeContainer(container) {
518
+ if (container.assetContainer && container.visible) {
519
+ container.assetContainer.removeAllFromScene();
520
+ container.visible = false;
521
+ }
522
+ }
523
+
524
+ #replaceContainer(container, newAssetContainer) {
525
+ this.#removeContainer(container);
526
+ container.assetContainer = newAssetContainer;
527
+ container.assetContainer.meshes.forEach((mesh) => {
369
528
  mesh.receiveShadows = true;
370
529
  this.#shadowGen.addShadowCaster(mesh, true);
371
530
  });
372
- this.#addContainer(group);
531
+ this.#addContainer(container);
373
532
  }
374
533
 
375
- async #loadAssetContainer(storage) {
534
+ async #loadAssetContainer(container) {
535
+ let storage = container?.storage;
536
+
376
537
  if (!storage) {
377
538
  return false;
378
539
  }
@@ -383,6 +544,11 @@ class PrefViewer extends HTMLElement {
383
544
  await this.#initStorage(storage.db, storage.table);
384
545
  const object = await loadModel(storage.id, storage.table);
385
546
  source = object.data;
547
+ if (object.timestamp === container.timestamp) {
548
+ return false;
549
+ } else {
550
+ container.changed = { timestamp: object.timestamp, size: object.size };
551
+ }
386
552
  }
387
553
 
388
554
  if (!source) {
@@ -391,20 +557,34 @@ class PrefViewer extends HTMLElement {
391
557
 
392
558
  let file = null;
393
559
 
394
- let { blob, extension } = this.#decodeBase64(source);
560
+ let { blob, extension, size } = this.#decodeBase64(source);
395
561
  if (blob && extension) {
396
- file = new File([blob], `model${extension}`, {
562
+ file = new File([blob], `${container.name}${extension}`, {
397
563
  type: blob.type,
398
564
  });
565
+ if (!container.changed) {
566
+ if (container.timestamp === null && container.size === size) {
567
+ return false;
568
+ } else {
569
+ container.changed = { timestamp: null, size: size };
570
+ }
571
+ }
399
572
  } else {
400
573
  const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
401
574
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
575
+ const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
576
+ if (container.timestamp === fileTimestamp && container.size === fileSize) {
577
+ return false;
578
+ } else {
579
+ container.changed = { timestamp: fileTimestamp, size: fileSize };
580
+ }
402
581
  }
403
582
 
404
583
  let options = {
405
584
  pluginExtension: extension,
406
585
  pluginOptions: {
407
586
  gltf: {
587
+ loadAllMaterials: true,
408
588
  preprocessUrlAsync: this.#transformUrl,
409
589
  },
410
590
  },
@@ -413,27 +593,43 @@ class PrefViewer extends HTMLElement {
413
593
  return LoadAssetContainerAsync(file || source, this.#scene, options);
414
594
  }
415
595
 
416
- async #loadContainers(loadModel = true, loadEnvironment = true) {
596
+ async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
417
597
  const promiseArray = [];
418
-
419
- promiseArray.push(loadModel ? this.#loadAssetContainer(this.#model.storage) : false);
420
- promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#environment.storage) : false);
598
+ promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
599
+ promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
600
+ promiseArray.push(loadMaterials ? this.#loadAssetContainer(this.#data.containers.materials) : false);
421
601
 
422
602
  Promise.allSettled(promiseArray)
423
603
  .then(async (values) => {
424
604
  const modelContainer = values[0];
425
605
  const environmentContainer = values[1];
606
+ const materialsContainer = values[2];
426
607
 
427
608
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
428
- this.#replaceContainer(this.#model, modelContainer.value);
609
+ this.#replaceContainer(this.#data.containers.model, modelContainer.value);
610
+ this.#storeChangedFlagsForContainer(this.#data.containers.model);
611
+ } else {
612
+ this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
429
613
  }
430
614
 
431
615
  if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
432
- this.#replaceContainer(this.#environment, environmentContainer.value);
616
+ this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
617
+ this.#storeChangedFlagsForContainer(this.#data.containers.environment);
618
+ } else {
619
+ this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
620
+ }
621
+
622
+ if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
623
+ this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
624
+ this.#storeChangedFlagsForContainer(this.#data.containers.materials);
433
625
  }
434
626
 
627
+ this.#setOptionsMaterials();
628
+ this.#setOptionsCamera();
435
629
  this.#setVisibilityOfWallAndFloorInModel();
436
-
630
+
631
+ this.#resetChangedFlags();
632
+
437
633
  this.dispatchEvent(
438
634
  new CustomEvent("model-loaded", {
439
635
  detail: { success: "" },
@@ -460,11 +656,21 @@ class PrefViewer extends HTMLElement {
460
656
  if (!config) {
461
657
  return false;
462
658
  }
463
- this.#model.storage = config.model?.storage || null;
464
- this.#model.show = config.model?.visible !== undefined ? config.model.visible : this.#model.show;
465
- this.#environment.storage = config.scene?.storage || null;
466
- this.#environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#environment.show;
467
- this.#initialized && this.#loadContainers(true, true);
659
+
660
+ // AssetContainers
661
+ this.#data.containers.model.storage = config.model?.storage || null;
662
+ this.#data.containers.model.show = config.model?.visible !== undefined ? config.model.visible : this.#data.containers.model.show;
663
+ this.#data.containers.environment.storage = config.scene?.storage || null;
664
+ this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
665
+ this.#data.containers.materials.storage = config.materials?.storage || null;
666
+
667
+ // Options
668
+ if (config.options) {
669
+ this.#checkCameraChanged(config.options);
670
+ this.#checkMaterialsChanged(config.options);
671
+ }
672
+
673
+ this.#initialized && this.#loadContainers(true, true, true);
468
674
  }
469
675
 
470
676
  loadModel(model) {
@@ -472,9 +678,9 @@ class PrefViewer extends HTMLElement {
472
678
  if (!model) {
473
679
  return false;
474
680
  }
475
- this.#model.storage = model.storage || null;
476
- this.#model.show = model.visible !== undefined ? model.visible : this.#model.show;
477
- this.#initialized && this.#loadContainers(true, false);
681
+ this.#data.containers.model.storage = model.storage || null;
682
+ this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
683
+ this.#initialized && this.#loadContainers(true, false, false);
478
684
  }
479
685
 
480
686
  loadScene(scene) {
@@ -482,43 +688,43 @@ class PrefViewer extends HTMLElement {
482
688
  if (!scene) {
483
689
  return false;
484
690
  }
485
- this.#environment.storage = scene.storage || null;
486
- this.#environment.show = scene.visible !== undefined ? scene.visible : this.#environment.show;
487
- this.#initialized && this.#loadContainers(false, true);
691
+ this.#data.containers.environment.storage = scene.storage || null;
692
+ this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
693
+ this.#initialized && this.#loadContainers(false, true, false);
488
694
  }
489
695
 
490
696
  showModel() {
491
- this.#model.show = true;
492
- this.#addContainer(this.#model);
697
+ this.#data.containers.model.show = true;
698
+ this.#addContainer(this.#data.containers.model);
493
699
  }
494
700
 
495
701
  hideModel() {
496
- this.#model.show = false;
497
- this.#removeContainer(this.#model);
702
+ this.#data.containers.model.show = false;
703
+ this.#removeContainer(this.#data.containers.model);
498
704
  }
499
705
 
500
706
  showScene() {
501
- this.#environment.show = true;
502
- this.#addContainer(this.#environment);
707
+ this.#data.containers.environment.show = true;
708
+ this.#addContainer(this.#data.containers.environment);
503
709
  this.#setVisibilityOfWallAndFloorInModel();
504
710
  }
505
711
 
506
712
  hideScene() {
507
- this.#environment.show = false;
508
- this.#removeContainer(this.#environment);
713
+ this.#data.containers.environment.show = false;
714
+ this.#removeContainer(this.#data.containers.environment);
509
715
  this.#setVisibilityOfWallAndFloorInModel();
510
716
  }
511
717
 
512
718
  downloadModelGLB() {
513
719
  const fileName = "model";
514
- GLTF2Export.GLBAsync(this.#model.container, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
720
+ GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
515
721
  glb.downloadFiles();
516
722
  });
517
723
  }
518
724
 
519
725
  downloadModelUSDZ() {
520
726
  const fileName = "model";
521
- USDZExportAsync(this.#model.container).then((response) => {
727
+ USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
522
728
  if (response) {
523
729
  Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
524
730
  }