@preference-sl/pref-viewer 2.10.0-beta.28 → 2.10.0-beta.29

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 +163 -96
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.10.0-beta.28",
3
+ "version": "2.10.0-beta.29",
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
@@ -46,10 +46,40 @@ import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
46
46
  import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression";
47
47
  import { initDb, loadModel } from "./gltf-storage.js";
48
48
 
49
+ class PrefViewerTask {
50
+ static Types = Object.freeze({
51
+ Config: "config",
52
+ Environment: "environment",
53
+ Materials: "materials",
54
+ Model: "model",
55
+ Options: "options",
56
+ });
57
+
58
+ /**
59
+ * value: any payload for the task
60
+ * type: must match one of PrefViewerTask.Types values (case-insensitive)
61
+ */
62
+ constructor(value, type) {
63
+ this.value = value;
64
+
65
+ const t = typeof type === "string" ? type.toLowerCase() : String(type).toLowerCase();
66
+ const allowed = Object.values(PrefViewerTask.Types);
67
+ if (!allowed.includes(t)) {
68
+ throw new TypeError(
69
+ `PrefViewerTask: invalid type "${type}". Allowed types: ${allowed.join(", ")}`
70
+ );
71
+ }
72
+ this.type = t;
73
+
74
+ Object.freeze(this);
75
+ }
76
+ }
77
+
49
78
  class PrefViewer extends HTMLElement {
50
79
  initialized = false;
51
80
  loaded = false;
52
81
  loading = false;
82
+ #taskQueue = [];
53
83
 
54
84
  #data = {
55
85
  containers: {
@@ -162,6 +192,12 @@ class PrefViewer extends HTMLElement {
162
192
  case "scene":
163
193
  this.loadScene(value);
164
194
  break;
195
+ case "materials":
196
+ this.loadMaterials(value);
197
+ break;
198
+ case "options":
199
+ this.setOptions(value);
200
+ break;
165
201
  case "show-model":
166
202
  data = value.toLowerCase?.() === "true";
167
203
  if (this.initialized) {
@@ -198,7 +234,7 @@ class PrefViewer extends HTMLElement {
198
234
 
199
235
  this.#initializeBabylon();
200
236
  this.initialized = true;
201
- this.#loadContainers(true, true, true);
237
+ this.#processNextTask();
202
238
  }
203
239
 
204
240
  disconnectedCallback() {
@@ -228,7 +264,7 @@ class PrefViewer extends HTMLElement {
228
264
  this.shadowRoot.append(this.#wrapper);
229
265
  }
230
266
 
231
- #setStatusSceneLoading() {
267
+ #setStatusLoading() {
232
268
  this.loaded = false;
233
269
  this.loading = true;
234
270
  if (this.hasAttribute("loaded")) {
@@ -242,12 +278,10 @@ class PrefViewer extends HTMLElement {
242
278
  composed: true,
243
279
  })
244
280
  );
281
+ this.#engine.stopRenderLoop(this.#renderLoop);
245
282
  }
246
283
 
247
- #setStatusSceneLoaded() {
248
- this.loaded = true;
249
- this.loading = false;
250
-
284
+ async #setStatusLoaded() {
251
285
  const toLoadDetail = {
252
286
  container_model: !!this.#data.containers.model.changed.pending,
253
287
  container_environment: !!this.#data.containers.environment.changed.pending,
@@ -268,16 +302,11 @@ class PrefViewer extends HTMLElement {
268
302
  options_innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed.success,
269
303
  options_outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed.success,
270
304
  };
271
-
272
305
  const detail = {
273
306
  tried: toLoadDetail,
274
307
  success: loadedDetail,
275
308
  };
276
-
277
- if (this.hasAttribute("loading")) {
278
- this.removeAttribute("loading");
279
- }
280
- this.setAttribute("loaded", "");
309
+
281
310
  this.dispatchEvent(
282
311
  new CustomEvent("scene-loaded", {
283
312
  bubbles: true,
@@ -286,49 +315,21 @@ class PrefViewer extends HTMLElement {
286
315
  detail: detail,
287
316
  })
288
317
  );
289
- this.#resetChangedFlags();
290
- }
291
318
 
292
- #setStatusOptionsLoading() {
293
- this.dispatchEvent(
294
- new CustomEvent("options-loading", {
295
- bubbles: true,
296
- cancelable: false,
297
- composed: true,
298
- })
299
- );
300
- }
319
+ await this.#scene.whenReadyAsync();
320
+ this.#engine.runRenderLoop(this.#renderLoop);
321
+
322
+ this.#resetChangedFlags();
301
323
 
302
- #setStatusOptionsLoaded() {
303
- const toLoadDetail = {
304
- camera: !!this.#data.options.camera.changed.pending,
305
- innerWallMaterial: !!this.#data.options.materials.innerWall.changed.pending,
306
- outerWallMaterial: !!this.#data.options.materials.outerWall.changed.pending,
307
- innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed.pending,
308
- outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed.pending,
309
- };
310
- const loadedDetail = {
311
- camera: !!this.#data.options.camera.changed.success,
312
- innerWallMaterial: !!this.#data.options.materials.innerWall.changed.success,
313
- outerWallMaterial: !!this.#data.options.materials.outerWall.changed.success,
314
- innerFloorMaterial: !!this.#data.options.materials.innerFloor.changed.success,
315
- outerFloorMaterial: !!this.#data.options.materials.outerFloor.changed.success,
316
- };
324
+ if (this.hasAttribute("loading")) {
325
+ this.removeAttribute("loading");
326
+ }
327
+ this.setAttribute("loaded", "");
317
328
 
318
- const detail = {
319
- tried: toLoadDetail,
320
- success: loadedDetail,
321
- };
329
+ this.loaded = true;
330
+ this.loading = false;
322
331
 
323
- this.dispatchEvent(
324
- new CustomEvent("options-loaded", {
325
- bubbles: true,
326
- cancelable: false,
327
- composed: true,
328
- detail: detail,
329
- })
330
- );
331
- this.#resetChangedFlags();
332
+ this.#processNextTask();
332
333
  }
333
334
 
334
335
  // Data
@@ -771,17 +772,23 @@ class PrefViewer extends HTMLElement {
771
772
  }
772
773
 
773
774
  #addContainer(container) {
774
- if (container.assetContainer && !container.visible && container.show) {
775
- container.assetContainer.addAllToScene();
776
- container.visible = true;
775
+ if (!container.assetContainer || container.visible || !container.show) {
776
+ return false;
777
777
  }
778
+
779
+ container.assetContainer.addAllToScene();
780
+ container.visible = true;
781
+ return true;
778
782
  }
779
783
 
780
784
  #removeContainer(container) {
781
- if (container.assetContainer && container.visible) {
782
- container.assetContainer.removeAllFromScene();
783
- container.visible = false;
785
+ if (!container.assetContainer || !container.visible) {
786
+ return false;
784
787
  }
788
+
789
+ container.assetContainer.removeAllFromScene();
790
+ container.visible = false;
791
+ return true;
785
792
  }
786
793
 
787
794
  #replaceContainer(container, newAssetContainer) {
@@ -793,6 +800,7 @@ class PrefViewer extends HTMLElement {
793
800
  this.#scene.getEngine().releaseEffects();
794
801
  container.assetContainer = newAssetContainer;
795
802
  this.#addContainer(container);
803
+ return true;
796
804
  }
797
805
 
798
806
  async #loadAssetContainer(container) {
@@ -864,7 +872,6 @@ class PrefViewer extends HTMLElement {
864
872
  }
865
873
 
866
874
  async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
867
- this.#setStatusSceneLoading();
868
875
  this.#engine.stopRenderLoop(this.#renderLoop);
869
876
 
870
877
  const promiseArray = [];
@@ -873,7 +880,7 @@ class PrefViewer extends HTMLElement {
873
880
  promiseArray.push(loadMaterials ? this.#loadAssetContainer(this.#data.containers.materials) : false);
874
881
 
875
882
  Promise.allSettled(promiseArray)
876
- .then(async (values) => {
883
+ .then((values) => {
877
884
  const modelContainer = values[0];
878
885
  const environmentContainer = values[1];
879
886
  const materialsContainer = values[2];
@@ -906,7 +913,6 @@ class PrefViewer extends HTMLElement {
906
913
  this.#storeChangedFlagsForContainer(this.#data.containers.materials, false);
907
914
  }
908
915
 
909
- await this.#scene.whenReadyAsync();
910
916
  this.#setOptionsMaterials();
911
917
  this.#setOptionsCamera();
912
918
  this.#setVisibilityOfWallAndFloorInModel();
@@ -923,24 +929,51 @@ class PrefViewer extends HTMLElement {
923
929
  })
924
930
  );
925
931
  })
926
- .finally(() => {
932
+ .finally(async () => {
927
933
  this.#setMaxSimultaneousLights();
928
934
  this.#initShadows();
929
- this.#setStatusSceneLoaded();
930
- this.#engine.runRenderLoop(this.#renderLoop);
935
+ await this.#setStatusLoaded();
931
936
  });
932
937
  }
933
938
 
934
- // Public Methods
935
- loadConfig(config) {
936
- config = typeof config === "string" ? JSON.parse(config) : config;
937
- if (!config) {
939
+ // Tasks
940
+ #addTaskToQueue(value, type) {
941
+ this.#taskQueue.push(new PrefViewerTask(value, type));
942
+ if (this.initialized && !this.loading) {
943
+ this.#processNextTask();
944
+ }
945
+ }
946
+
947
+ #processNextTask() {
948
+ if (!this.#taskQueue.length) {
938
949
  return false;
939
950
  }
951
+ const task = this.#taskQueue[0];
952
+ this.#taskQueue.shift();
953
+ switch (task.type) {
954
+ case PrefViewerTask.Types.Config:
955
+ this.#processConfig(task.value);
956
+ break;
957
+ case PrefViewerTask.Types.Model:
958
+ this.#processModel(task.value);
959
+ break;
960
+ case PrefViewerTask.Types.Environment:
961
+ this.#processEnvironment(task.value);
962
+ break;
963
+ case PrefViewerTask.Types.Materials:
964
+ this.#processMaterials(task.value);
965
+ break;
966
+ case PrefViewerTask.Types.Options:
967
+ this.#processOptions(task.value);
968
+ break;
969
+ }
970
+ }
971
+
972
+ #processConfig(config) {
973
+ this.#setStatusLoading();
940
974
 
941
975
  // Containers
942
976
  const loadModel = !!config.model?.storage;
943
- // CHANGED: marcar pending, no boolean
944
977
  this.#data.containers.model.changed.pending = loadModel;
945
978
  this.#data.containers.model.changed.success = false;
946
979
  this.#data.containers.model.changed.storage = this.#data.containers.model.storage;
@@ -966,15 +999,49 @@ class PrefViewer extends HTMLElement {
966
999
  this.#checkMaterialsChanged(config.options);
967
1000
  }
968
1001
 
969
- this.initialized && this.#loadContainers(loadModel, loadEnvironment, loadMaterials);
1002
+ this.#loadContainers(loadModel, loadEnvironment, loadMaterials);
970
1003
  }
971
1004
 
972
- setOptions(options) {
973
- if (!options) {
974
- return false;
975
- }
1005
+ #processModel(model) {
1006
+ this.#setStatusLoading();
1007
+
1008
+ const loadModel = !!model.storage;
1009
+ this.#data.containers.model.changed.pending = loadModel;
1010
+ this.#data.containers.model.changed.success = false;
1011
+ this.#data.containers.model.changed.storage = this.#data.containers.model.storage;
1012
+ this.#data.containers.model.storage = loadModel ? model.storage : this.#data.containers.model.storage;
1013
+ this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
1014
+
1015
+ this.initialized && this.#loadContainers(loadModel, false, false);
1016
+ }
1017
+
1018
+ #processEnvironment(environment) {
1019
+ this.#setStatusLoading();
1020
+
1021
+ const loadEnvironment = !!environment.storage;
1022
+ this.#data.containers.environment.changed.pending = loadEnvironment;
1023
+ this.#data.containers.environment.changed.success = false;
1024
+ this.#data.containers.environment.changed.storage = this.#data.containers.environment.storage;
1025
+ this.#data.containers.environment.storage = loadEnvironment ? environment.storage : this.#data.containers.environment.storage;
1026
+ this.#data.containers.environment.show = environment.visible !== undefined ? environment.visible : this.#data.containers.environment.show;
1027
+
1028
+ this.#loadContainers(false, loadEnvironment, false);
1029
+ }
1030
+
1031
+ #processMaterials(materials) {
1032
+ this.#setStatusLoading();
1033
+
1034
+ const loadMaterials = !!materials.storage;
1035
+ this.#data.containers.materials.changed.pending = loadMaterials;
1036
+ this.#data.containers.materials.changed.success = false;
1037
+ this.#data.containers.materials.changed.storage = this.#data.containers.materials.storage;
1038
+ this.#data.containers.materials.storage = loadMaterials ? materials.storage : this.#data.containers.materials.storage;
1039
+
1040
+ this.#loadContainers(false, false, loadMaterials);
1041
+ }
976
1042
 
977
- this.#setStatusOptionsLoading();
1043
+ async #processOptions(options) {
1044
+ this.#setStatusLoading();
978
1045
 
979
1046
  let someSetted = false;
980
1047
  if (this.#checkCameraChanged(options)) {
@@ -983,24 +1050,27 @@ class PrefViewer extends HTMLElement {
983
1050
  if (this.#checkMaterialsChanged(options)) {
984
1051
  someSetted = someSetted || this.#setOptionsMaterials();
985
1052
  }
986
-
987
- this.#setStatusOptionsLoaded();
1053
+
1054
+ await this.#setStatusLoaded();
988
1055
 
989
1056
  return someSetted;
990
1057
  }
991
1058
 
1059
+ // Public Methods
1060
+ loadConfig(config) {
1061
+ config = typeof config === "string" ? JSON.parse(config) : config;
1062
+ if (!config) {
1063
+ return false;
1064
+ }
1065
+ this.#addTaskToQueue(config, "config");
1066
+ }
1067
+
992
1068
  loadModel(model) {
993
1069
  model = typeof model === "string" ? JSON.parse(model) : model;
994
1070
  if (!model) {
995
1071
  return false;
996
1072
  }
997
- const loadModel = !!model.storage;
998
- this.#data.containers.model.changed.pending = loadModel;
999
- this.#data.containers.model.changed.success = false;
1000
- this.#data.containers.model.changed.storage = this.#data.containers.model.storage;
1001
- this.#data.containers.model.storage = loadModel ? model.storage : this.#data.containers.model.storage;
1002
- this.#data.containers.model.show = model.visible !== undefined ? model.visible : this.#data.containers.model.show;
1003
- this.initialized && this.#loadContainers(loadModel, false, false);
1073
+ this.#addTaskToQueue(model, "model");
1004
1074
  }
1005
1075
 
1006
1076
  loadScene(scene) {
@@ -1008,13 +1078,7 @@ class PrefViewer extends HTMLElement {
1008
1078
  if (!scene) {
1009
1079
  return false;
1010
1080
  }
1011
- const loadEnvironment = !!scene.storage;
1012
- this.#data.containers.environment.changed.pending = loadEnvironment;
1013
- this.#data.containers.environment.changed.success = false;
1014
- this.#data.containers.environment.changed.storage = this.#data.containers.environment.storage;
1015
- this.#data.containers.environment.storage = loadEnvironment ? scene.storage : this.#data.containers.environment.storage;
1016
- this.#data.containers.environment.show = scene.visible !== undefined ? scene.visible : this.#data.containers.environment.show;
1017
- this.initialized && this.#loadContainers(false, loadEnvironment, false);
1081
+ this.#addTaskToQueue(scene, "environment");
1018
1082
  }
1019
1083
 
1020
1084
  loadMaterials(materials) {
@@ -1022,12 +1086,15 @@ class PrefViewer extends HTMLElement {
1022
1086
  if (!materials) {
1023
1087
  return false;
1024
1088
  }
1025
- const loadMaterials = !!materials.storage;
1026
- this.#data.containers.materials.changed.pending = loadMaterials;
1027
- this.#data.containers.materials.changed.success = false;
1028
- this.#data.containers.materials.changed.storage = this.#data.containers.materials.storage;
1029
- this.#data.containers.materials.storage = loadMaterials ? materials.storage : this.#data.containers.materials.storage;
1030
- this.initialized && this.#loadContainers(false, false, loadMaterials);
1089
+ this.#addTaskToQueue(materials, "materials");
1090
+ }
1091
+
1092
+ setOptions(options) {
1093
+ options = typeof options === "string" ? JSON.parse(options) : options;
1094
+ if (!options) {
1095
+ return false;
1096
+ }
1097
+ this.#addTaskToQueue(options, "options");
1031
1098
  }
1032
1099
 
1033
1100
  showModel() {