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

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 +315 -85
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.5",
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,43 @@ 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
+ return this.#data.options.camera.changed;
236
+ }
237
+
238
+ #checkMaterialsChanged(options) {
239
+ if (!options) {
240
+ return false;
241
+ }
242
+ let someChanged = false;
243
+ Object.keys(this.#data.options.materials).forEach((material) => {
244
+ const key = `${material}Material`;
245
+ this.#data.options.materials[material].changed = options[key] && options[key] !== this.#data.options.materials[material].value ? true : false;
246
+ this.#data.options.materials[material].value = this.#data.options.materials[material].changed ? options[key] : this.#data.options.materials[material].value;
247
+ someChanged = someChanged || this.#data.options.materials[material].changed;
248
+ });
249
+ return someChanged;
250
+ }
251
+
252
+ #storeChangedFlagsForContainer(container) {
253
+ container.timestamp = container.changed.timestamp;
254
+ container.size = container.changed.size;
255
+ }
256
+
257
+ #resetChangedFlags() {
258
+ Object.values(this.#data.containers).forEach((container) => (container.changed = false));
259
+ Object.values(this.#data.options.materials).forEach((material) => (material.changed = false));
260
+ this.#data.options.camera.changed = false;
261
+ }
262
+
263
+ // Babylon.js
179
264
  async #initializeBabylon() {
180
265
  this.#engine = new Engine(this.#canvas, true, { alpha: true });
181
266
  this.#scene = new Scene(this.#engine);
@@ -183,15 +268,14 @@ class PrefViewer extends HTMLElement {
183
268
  this.#createCamera();
184
269
  this.#createLights();
185
270
  this.#setupInteraction();
186
-
271
+
187
272
  this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
188
273
  this.#canvasResizeObserver.observe(this.#canvas);
189
274
 
190
275
  await this.#createXRExperience();
191
-
192
276
  }
193
277
 
194
- addStylesToARButton(){
278
+ addStylesToARButton() {
195
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"}';
196
280
  const style = document.createElement("style");
197
281
  style.appendChild(document.createTextNode(css));
@@ -202,14 +286,14 @@ class PrefViewer extends HTMLElement {
202
286
  if (this.#XRExperience) {
203
287
  return true;
204
288
  }
205
-
289
+
206
290
  const sessionMode = "immersive-ar";
207
291
  const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
208
292
  if (!sessionSupported) {
209
293
  console.info("PrefViewer: WebXR in mode AR is not supported");
210
294
  return false;
211
295
  }
212
-
296
+
213
297
  try {
214
298
  const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
215
299
  ground.isVisible = false;
@@ -231,7 +315,6 @@ class PrefViewer extends HTMLElement {
231
315
  xrInput: this.#XRExperience.input,
232
316
  floorMeshes: [ground],
233
317
  timeToTeleport: 1500,
234
- useMainComponentOnly: true,
235
318
  });
236
319
 
237
320
  this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
@@ -251,11 +334,13 @@ class PrefViewer extends HTMLElement {
251
334
  #canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
252
335
 
253
336
  #createCamera() {
254
- this.#camera = new ArcRotateCamera("camera", 3 * Math.PI / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
337
+ this.#camera = new ArcRotateCamera("camera", (3 * Math.PI) / 2, Math.PI * 0.47, 10, Vector3.Zero(), this.#scene);
255
338
  this.#camera.upperBetaLimit = Math.PI * 0.48;
256
339
  this.#camera.lowerBetaLimit = Math.PI * 0.25;
257
340
  this.#camera.lowerRadiusLimit = 5;
258
341
  this.#camera.upperRadiusLimit = 20;
342
+ this.#camera.metadata = { locked: false }
343
+ this.#camera = this.#camera;
259
344
  this.#camera.attachControl(this.#canvas, true);
260
345
  }
261
346
 
@@ -283,10 +368,14 @@ class PrefViewer extends HTMLElement {
283
368
 
284
369
  #setupInteraction() {
285
370
  this.#canvas.addEventListener("wheel", (event) => {
286
- if (!this.#scene || !this.#camera) return;
287
- const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
371
+ if (!this.#scene || !this.#camera) {
372
+ return false;
373
+ }
374
+ //const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
288
375
  //this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
289
- this.#camera.inertialRadiusOffset -= event.deltaY * this.#camera.wheelPrecision * 0.001;
376
+ if (!this.#scene.activeCamera.metadata?.locked) {
377
+ this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
378
+ }
290
379
  event.preventDefault();
291
380
  });
292
381
  }
@@ -300,6 +389,27 @@ class PrefViewer extends HTMLElement {
300
389
  }
301
390
 
302
391
  // Utility methods for loading gltf/glb
392
+ async #getServerFileDataHeader(uri) {
393
+ return new Promise((resolve) => {
394
+ const xhr = new XMLHttpRequest();
395
+ xhr.open("HEAD", uri, true);
396
+ xhr.responseType = "blob";
397
+ xhr.onload = () => {
398
+ if (xhr.status === 200) {
399
+ const size = parseInt(xhr.getResponseHeader("Content-Length"));
400
+ const timestamp = new Date(xhr.getResponseHeader("Last-Modified")).toISOString();
401
+ resolve(size, timestamp);
402
+ } else {
403
+ resolve(0, null);
404
+ }
405
+ };
406
+ xhr.onerror = () => {
407
+ resolve(0, null);
408
+ };
409
+ xhr.send();
410
+ });
411
+ }
412
+
303
413
  #transformUrl(url) {
304
414
  return new Promise((resolve) => {
305
415
  resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
@@ -312,10 +422,11 @@ class PrefViewer extends HTMLElement {
312
422
  let decoded = "";
313
423
  let blob = null;
314
424
  let extension = null;
425
+ let size = raw.length;
315
426
  try {
316
427
  decoded = atob(raw);
317
428
  } catch {
318
- return { blob, extension };
429
+ return { blob, extension, size };
319
430
  }
320
431
  let isJson = false;
321
432
  try {
@@ -326,7 +437,7 @@ class PrefViewer extends HTMLElement {
326
437
  const type = isJson ? "model/gltf+json" : "model/gltf-binary";
327
438
  const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
328
439
  blob = new Blob([array], { type });
329
- return { blob, extension };
440
+ return { blob, extension, size };
330
441
  }
331
442
 
332
443
  async #initStorage(db, table) {
@@ -338,41 +449,103 @@ class PrefViewer extends HTMLElement {
338
449
 
339
450
  // Methods for managing Asset Containers
340
451
  #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));
452
+ if (!this.#data.containers.model.assetContainer || !this.#data.containers.model.visible) {
453
+ return false;
454
+ }
455
+ show = show !== undefined ? show : this.#data.containers.environment.visible;
456
+ const prefixes = Object.values(this.#data.options.materials).map((material) => material.prefix);
457
+ this.#data.containers.model.assetContainer.meshes.filter((meshToFilter) => prefixes.some((prefix) => meshToFilter.name.startsWith(prefix))).forEach((mesh) => mesh.setEnabled(show));
458
+ }
459
+
460
+ #setOptionsMaterial(optionMaterial) {
461
+ if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
462
+ return false;
463
+ }
464
+
465
+ const material = this.#data.containers.materials.assetContainer?.materials.find((mat) => mat.name === optionMaterial.value) || null;
466
+ if (!material) {
467
+ return false;
468
+ }
469
+
470
+ const containers = [];
471
+ if (this.#data.containers.model.assetContainer && (this.#data.containers.model.assetContainer.changed || optionMaterial.changed)) {
472
+ containers.push(this.#data.containers.model.assetContainer);
473
+ }
474
+ if (this.#data.containers.environment.assetContainer && (this.#data.containers.environment.assetContainer.changed || optionMaterial.changed)) {
475
+ containers.push(this.#data.containers.environment.assetContainer);
476
+ }
477
+ if (containers.length === 0) {
478
+ return false;
348
479
  }
480
+
481
+ let someSetted = false;
482
+ containers.forEach((container) =>
483
+ container.meshes
484
+ .filter((meshToFilter) => meshToFilter.name.startsWith(optionMaterial.prefix))
485
+ .forEach((mesh) => {
486
+ mesh.material = material;
487
+ someSetted = true;
488
+ })
489
+ );
490
+
491
+ return someSetted;
492
+ }
493
+
494
+ #setOptionsMaterials() {
495
+ let someSetted = false;
496
+ Object.values(this.#data.options.materials).forEach((material) => {
497
+ let settedMaterial = this.#setOptionsMaterial(material);
498
+ someSetted = someSetted || settedMaterial;
499
+ });
500
+ return someSetted;
349
501
  }
350
502
 
351
- #addContainer(group) {
352
- if (group.container && !group.visible && group.show) {
353
- group.container.addAllToScene();
354
- group.visible = true;
503
+ #setOptionsCamera() {
504
+ if (!this.#data.options.camera.value || (!this.#data.options.camera.changed && !this.#data.containers.model.assetContainer.changed)) {
505
+ return false;
506
+ }
507
+
508
+ let camera = this.#data.containers.model.assetContainer?.cameras.find((cam) => cam.name === this.#data.options.camera.value) || null;
509
+ if (!camera) {
510
+ return false;
511
+ }
512
+
513
+ camera.metadata = { locked: this.#data.options.camera.locked };
514
+ if (!this.#data.options.camera.locked) {
515
+ camera.attachControl(this.#canvas, true);
355
516
  }
517
+ this.#scene.activeCamera = camera;
518
+
519
+ return true;
356
520
  }
357
521
 
358
- #removeContainer(group) {
359
- if (group.container && group.visible) {
360
- group.container.removeAllFromScene();
361
- group.visible = false;
522
+ #addContainer(container) {
523
+ if (container.assetContainer && !container.visible && container.show) {
524
+ container.assetContainer.addAllToScene();
525
+ container.visible = true;
362
526
  }
363
527
  }
364
528
 
365
- #replaceContainer(group, newContainer) {
366
- this.#removeContainer(group);
367
- group.container = newContainer;
368
- group.container.meshes.forEach((mesh) => {
529
+ #removeContainer(container) {
530
+ if (container.assetContainer && container.visible) {
531
+ container.assetContainer.removeAllFromScene();
532
+ container.visible = false;
533
+ }
534
+ }
535
+
536
+ #replaceContainer(container, newAssetContainer) {
537
+ this.#removeContainer(container);
538
+ container.assetContainer = newAssetContainer;
539
+ container.assetContainer.meshes.forEach((mesh) => {
369
540
  mesh.receiveShadows = true;
370
541
  this.#shadowGen.addShadowCaster(mesh, true);
371
542
  });
372
- this.#addContainer(group);
543
+ this.#addContainer(container);
373
544
  }
374
545
 
375
- async #loadAssetContainer(storage) {
546
+ async #loadAssetContainer(container) {
547
+ let storage = container?.storage;
548
+
376
549
  if (!storage) {
377
550
  return false;
378
551
  }
@@ -383,6 +556,11 @@ class PrefViewer extends HTMLElement {
383
556
  await this.#initStorage(storage.db, storage.table);
384
557
  const object = await loadModel(storage.id, storage.table);
385
558
  source = object.data;
559
+ if (object.timestamp === container.timestamp) {
560
+ return false;
561
+ } else {
562
+ container.changed = { timestamp: object.timestamp, size: object.size };
563
+ }
386
564
  }
387
565
 
388
566
  if (!source) {
@@ -391,20 +569,34 @@ class PrefViewer extends HTMLElement {
391
569
 
392
570
  let file = null;
393
571
 
394
- let { blob, extension } = this.#decodeBase64(source);
572
+ let { blob, extension, size } = this.#decodeBase64(source);
395
573
  if (blob && extension) {
396
- file = new File([blob], `model${extension}`, {
574
+ file = new File([blob], `${container.name}${extension}`, {
397
575
  type: blob.type,
398
576
  });
577
+ if (!container.changed) {
578
+ if (container.timestamp === null && container.size === size) {
579
+ return false;
580
+ } else {
581
+ container.changed = { timestamp: null, size: size };
582
+ }
583
+ }
399
584
  } else {
400
585
  const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
401
586
  extension = extMatch ? `.${extMatch[1].toLowerCase()}` : ".gltf";
587
+ const { fileSize, fileTimestamp } = await this.#getServerFileDataHeader(source);
588
+ if (container.timestamp === fileTimestamp && container.size === fileSize) {
589
+ return false;
590
+ } else {
591
+ container.changed = { timestamp: fileTimestamp, size: fileSize };
592
+ }
402
593
  }
403
594
 
404
595
  let options = {
405
596
  pluginExtension: extension,
406
597
  pluginOptions: {
407
598
  gltf: {
599
+ loadAllMaterials: true,
408
600
  preprocessUrlAsync: this.#transformUrl,
409
601
  },
410
602
  },
@@ -413,27 +605,43 @@ class PrefViewer extends HTMLElement {
413
605
  return LoadAssetContainerAsync(file || source, this.#scene, options);
414
606
  }
415
607
 
416
- async #loadContainers(loadModel = true, loadEnvironment = true) {
608
+ async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
417
609
  const promiseArray = [];
418
-
419
- promiseArray.push(loadModel ? this.#loadAssetContainer(this.#model.storage) : false);
420
- promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#environment.storage) : false);
610
+ promiseArray.push(loadModel ? this.#loadAssetContainer(this.#data.containers.model) : false);
611
+ promiseArray.push(loadEnvironment ? this.#loadAssetContainer(this.#data.containers.environment) : false);
612
+ promiseArray.push(loadMaterials ? this.#loadAssetContainer(this.#data.containers.materials) : false);
421
613
 
422
614
  Promise.allSettled(promiseArray)
423
615
  .then(async (values) => {
424
616
  const modelContainer = values[0];
425
617
  const environmentContainer = values[1];
618
+ const materialsContainer = values[2];
426
619
 
427
620
  if (modelContainer.status === "fulfilled" && modelContainer.value) {
428
- this.#replaceContainer(this.#model, modelContainer.value);
621
+ this.#replaceContainer(this.#data.containers.model, modelContainer.value);
622
+ this.#storeChangedFlagsForContainer(this.#data.containers.model);
623
+ } else {
624
+ this.#data.containers.model.show ? this.#addContainer(this.#data.containers.model) : this.#removeContainer(this.#data.containers.model);
429
625
  }
430
626
 
431
627
  if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
432
- this.#replaceContainer(this.#environment, environmentContainer.value);
628
+ this.#replaceContainer(this.#data.containers.environment, environmentContainer.value);
629
+ this.#storeChangedFlagsForContainer(this.#data.containers.environment);
630
+ } else {
631
+ this.#data.containers.environment.show ? this.#addContainer(this.#data.containers.environment) : this.#removeContainer(this.#data.containers.environment);
632
+ }
633
+
634
+ if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
635
+ this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
636
+ this.#storeChangedFlagsForContainer(this.#data.containers.materials);
433
637
  }
434
638
 
639
+ this.#setOptionsMaterials();
640
+ this.#setOptionsCamera();
435
641
  this.#setVisibilityOfWallAndFloorInModel();
436
-
642
+
643
+ this.#resetChangedFlags();
644
+
437
645
  this.dispatchEvent(
438
646
  new CustomEvent("model-loaded", {
439
647
  detail: { success: "" },
@@ -460,11 +668,37 @@ class PrefViewer extends HTMLElement {
460
668
  if (!config) {
461
669
  return false;
462
670
  }
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);
671
+
672
+ // Containers
673
+ this.#data.containers.model.storage = config.model?.storage || null;
674
+ this.#data.containers.model.show = config.model?.visible !== undefined ? config.model.visible : this.#data.containers.model.show;
675
+ this.#data.containers.environment.storage = config.scene?.storage || null;
676
+ this.#data.containers.environment.show = config.scene?.visible !== undefined ? config.scene.visible : this.#data.containers.environment.show;
677
+ this.#data.containers.materials.storage = config.materials?.storage || null;
678
+
679
+ // Options
680
+ if (config.options) {
681
+ this.#checkCameraChanged(config.options);
682
+ this.#checkMaterialsChanged(config.options);
683
+ }
684
+
685
+ this.#initialized && this.#loadContainers(true, true, true);
686
+ }
687
+
688
+ setOptions(options) {
689
+ if (!options) {
690
+ return false;
691
+ }
692
+ let someSetted = false;
693
+ if (this.#checkCameraChanged(options)) {
694
+ someSetted = someSetted || this.#setOptionsCamera();
695
+ }
696
+ if (this.#checkMaterialsChanged(options)) {
697
+ someSetted = someSetted || this.#setOptionsMaterials();
698
+ }
699
+ this.#resetChangedFlags();
700
+ debugger;
701
+ return someSetted;
468
702
  }
469
703
 
470
704
  loadModel(model) {
@@ -472,9 +706,9 @@ class PrefViewer extends HTMLElement {
472
706
  if (!model) {
473
707
  return false;
474
708
  }
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);
709
+ this.#data.containers.model.storage = model.storage || null;
710
+ this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
711
+ this.#initialized && this.#loadContainers(true, false, false);
478
712
  }
479
713
 
480
714
  loadScene(scene) {
@@ -482,43 +716,41 @@ class PrefViewer extends HTMLElement {
482
716
  if (!scene) {
483
717
  return false;
484
718
  }
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);
719
+ this.#data.containers.environment.storage = scene.storage || null;
720
+ this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
721
+ this.#initialized && this.#loadContainers(false, true, false);
488
722
  }
489
723
 
490
724
  showModel() {
491
- this.#model.show = true;
492
- this.#addContainer(this.#model);
725
+ this.#data.containers.model.show = true;
726
+ this.#addContainer(this.#data.containers.model);
493
727
  }
494
728
 
495
729
  hideModel() {
496
- this.#model.show = false;
497
- this.#removeContainer(this.#model);
730
+ this.#data.containers.model.show = false;
731
+ this.#removeContainer(this.#data.containers.model);
498
732
  }
499
733
 
500
734
  showScene() {
501
- this.#environment.show = true;
502
- this.#addContainer(this.#environment);
735
+ this.#data.containers.environment.show = true;
736
+ this.#addContainer(this.#data.containers.environment);
503
737
  this.#setVisibilityOfWallAndFloorInModel();
504
738
  }
505
739
 
506
740
  hideScene() {
507
- this.#environment.show = false;
508
- this.#removeContainer(this.#environment);
741
+ this.#data.containers.environment.show = false;
742
+ this.#removeContainer(this.#data.containers.environment);
509
743
  this.#setVisibilityOfWallAndFloorInModel();
510
744
  }
511
745
 
512
746
  downloadModelGLB() {
513
747
  const fileName = "model";
514
- GLTF2Export.GLBAsync(this.#model.container, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
515
- glb.downloadFiles();
516
- });
748
+ GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
517
749
  }
518
750
 
519
751
  downloadModelUSDZ() {
520
752
  const fileName = "model";
521
- USDZExportAsync(this.#model.container).then((response) => {
753
+ USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
522
754
  if (response) {
523
755
  Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
524
756
  }
@@ -536,9 +768,7 @@ class PrefViewer extends HTMLElement {
536
768
 
537
769
  downloadModelAndSceneGLB() {
538
770
  const fileName = "scene";
539
- GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
540
- glb.downloadFiles();
541
- });
771
+ GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
542
772
  }
543
773
  }
544
774