@preference-sl/pref-viewer 2.12.0-beta.1 → 2.12.0-beta.3
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
CHANGED
|
@@ -24,6 +24,7 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
24
24
|
* - Provides methods for downloading models and scenes in GLB, glTF (ZIP), and USDZ formats.
|
|
25
25
|
* - Manages camera and material options, container visibility, and user interactions.
|
|
26
26
|
* - Observes canvas resize events and updates the engine accordingly.
|
|
27
|
+
* - Applies metadata-driven scene adjustments (e.g., inner floor translation) after asset reloads.
|
|
27
28
|
* - Designed for integration with PrefViewer and GLTFResolver.
|
|
28
29
|
* - All resource management and rendering operations are performed asynchronously for performance.
|
|
29
30
|
*
|
|
@@ -84,6 +85,9 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
84
85
|
* - #startRender(): Starts the Babylon.js render loop.
|
|
85
86
|
* - #loadAssetContainer(container): Loads an asset container asynchronously.
|
|
86
87
|
* - #loadContainers(): Loads all asset containers and adds them to the scene.
|
|
88
|
+
* - #checkModelMetadata(oldMetadata, newMetadata): Processes metadata changes after loading.
|
|
89
|
+
* - #checkInnerFloorTranslation(oldMetadata, newMetadata): Applies inner floor Y translations from metadata.
|
|
90
|
+
* - #translateNodeY(name, deltaY): Adjusts the Y position of a scene node.
|
|
87
91
|
* - #addDateToName(name): Appends the current date/time to a name string.
|
|
88
92
|
* - #downloadZip(files, name, comment, addDateInName): Generates and downloads a ZIP file.
|
|
89
93
|
* - #openDownloadDialog(): Opens the modal download dialog.
|
|
@@ -319,9 +323,9 @@ export default class BabylonJSController {
|
|
|
319
323
|
* @returns {boolean}
|
|
320
324
|
*/
|
|
321
325
|
#initializeEnvironmentTexture() {
|
|
322
|
-
return false;
|
|
326
|
+
return false; // Environment texture disabled by the moment
|
|
323
327
|
if (this.#scene.environmentTexture) {
|
|
324
|
-
return;
|
|
328
|
+
return false;
|
|
325
329
|
}
|
|
326
330
|
const hdrTextureURI = "../src/environments/noon_grass.hdr";
|
|
327
331
|
const hdrTexture = new HDRCubeTexture(hdrTextureURI, this.#scene, 128);
|
|
@@ -329,6 +333,7 @@ export default class BabylonJSController {
|
|
|
329
333
|
hdrTexture._noMipmap = false;
|
|
330
334
|
hdrTexture.level = 2.0;
|
|
331
335
|
this.#scene.environmentTexture = hdrTexture;
|
|
336
|
+
return true;
|
|
332
337
|
}
|
|
333
338
|
|
|
334
339
|
/**
|
|
@@ -340,7 +345,7 @@ export default class BabylonJSController {
|
|
|
340
345
|
* @returns {void|false} Returns false if no environment texture is set; otherwise void.
|
|
341
346
|
*/
|
|
342
347
|
#initializeIBLShadows() {
|
|
343
|
-
if (!this.#scene.environmentTexture) {
|
|
348
|
+
if (!this.#scene.environmentTexture || !this.#scene.environmentTexture.isReady()) {
|
|
344
349
|
return false;
|
|
345
350
|
}
|
|
346
351
|
|
|
@@ -407,7 +412,7 @@ export default class BabylonJSController {
|
|
|
407
412
|
* Otherwise, sets up shadow casting and receiving for all relevant meshes using the shadow generator.
|
|
408
413
|
*/
|
|
409
414
|
#initializeShadows() {
|
|
410
|
-
if (
|
|
415
|
+
if (this.#scene.environmentTexture) {
|
|
411
416
|
this.#initializeIBLShadows();
|
|
412
417
|
return true;
|
|
413
418
|
}
|
|
@@ -417,7 +422,7 @@ export default class BabylonJSController {
|
|
|
417
422
|
return false;
|
|
418
423
|
}
|
|
419
424
|
mesh.receiveShadows = true;
|
|
420
|
-
if (
|
|
425
|
+
if (mesh.name !== "hdri") {
|
|
421
426
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
422
427
|
}
|
|
423
428
|
});
|
|
@@ -684,7 +689,7 @@ export default class BabylonJSController {
|
|
|
684
689
|
const modelContainer = this.#containers.model;
|
|
685
690
|
const environmentContainer = this.#containers.environment;
|
|
686
691
|
|
|
687
|
-
if (!cameraState.isPending && !modelContainer.state.
|
|
692
|
+
if (!cameraState.isPending && !modelContainer.state.isSuccess && !environmentContainer.state.isSuccess) {
|
|
688
693
|
return false;
|
|
689
694
|
}
|
|
690
695
|
|
|
@@ -728,6 +733,7 @@ export default class BabylonJSController {
|
|
|
728
733
|
cameraState.setSuccess(true);
|
|
729
734
|
}
|
|
730
735
|
}
|
|
736
|
+
this.#scene.activeCamera?.detachControl();
|
|
731
737
|
if (!cameraState.locked && cameraState.value !== null) {
|
|
732
738
|
camera.attachControl(this.#canvas, true);
|
|
733
739
|
}
|
|
@@ -848,6 +854,8 @@ export default class BabylonJSController {
|
|
|
848
854
|
container.assetContainer = null;
|
|
849
855
|
}
|
|
850
856
|
this.#scene.getEngine().releaseEffects();
|
|
857
|
+
this.#scene.getEngine().releaseComputeEffects();
|
|
858
|
+
|
|
851
859
|
container.assetContainer = newAssetContainer;
|
|
852
860
|
return this.#addContainer(container, false);
|
|
853
861
|
}
|
|
@@ -908,13 +916,14 @@ export default class BabylonJSController {
|
|
|
908
916
|
if (!this.#gltfResolver) {
|
|
909
917
|
this.#gltfResolver = new GLTFResolver();
|
|
910
918
|
}
|
|
911
|
-
|
|
912
|
-
|
|
919
|
+
//let sourceData = await this.#gltfResolver.getSource(container.state.update.storage, container.state.size, container.state.timeStamp);
|
|
920
|
+
// TEMPORARY: We never pass 'size' or 'timeStamp' to always force a reload and avoid issues with the active camera in reloading assetContainers
|
|
921
|
+
let sourceData = await this.#gltfResolver.getSource(container.state.update.storage, 0, null);
|
|
913
922
|
if (!sourceData) {
|
|
914
923
|
return [container, false];
|
|
915
924
|
}
|
|
916
925
|
|
|
917
|
-
container.state.setPendingCacheData(sourceData.size, sourceData.timeStamp);
|
|
926
|
+
container.state.setPendingCacheData(sourceData.size, sourceData.timeStamp, sourceData.metadata);
|
|
918
927
|
|
|
919
928
|
// https://doc.babylonjs.com/typedoc/interfaces/BABYLON.LoadAssetContainerOptions
|
|
920
929
|
let options = {
|
|
@@ -943,6 +952,9 @@ export default class BabylonJSController {
|
|
|
943
952
|
async #loadContainers() {
|
|
944
953
|
this.#stopRender();
|
|
945
954
|
|
|
955
|
+
let oldModelMetadata = { ...(this.#containers.model?.state?.metadata ?? {}) };
|
|
956
|
+
let newModelMetadata = {};
|
|
957
|
+
|
|
946
958
|
const promiseArray = [];
|
|
947
959
|
Object.values(this.#containers).forEach((container) => {
|
|
948
960
|
promiseArray.push(this.#loadAssetContainer(container));
|
|
@@ -962,6 +974,7 @@ export default class BabylonJSController {
|
|
|
962
974
|
if (result.status === "fulfilled" && assetContainer) {
|
|
963
975
|
if (container.state.name === "model") {
|
|
964
976
|
assetContainer.lights = [];
|
|
977
|
+
newModelMetadata = { ...(container.state.update.metadata ?? {}) };
|
|
965
978
|
}
|
|
966
979
|
this.#replaceContainer(container, assetContainer);
|
|
967
980
|
container.state.setSuccess(true);
|
|
@@ -985,6 +998,7 @@ export default class BabylonJSController {
|
|
|
985
998
|
detail.error = error;
|
|
986
999
|
})
|
|
987
1000
|
.finally(async () => {
|
|
1001
|
+
this.#checkModelMetadata(oldModelMetadata, newModelMetadata);
|
|
988
1002
|
this.#setMaxSimultaneousLights();
|
|
989
1003
|
this.#initializeShadows();
|
|
990
1004
|
this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene);
|
|
@@ -993,6 +1007,101 @@ export default class BabylonJSController {
|
|
|
993
1007
|
return detail;
|
|
994
1008
|
}
|
|
995
1009
|
|
|
1010
|
+
/**
|
|
1011
|
+
* Checks and applies model metadata changes after asset loading.
|
|
1012
|
+
* @private
|
|
1013
|
+
* @param {object} oldMetadata - The previous metadata object for the model.
|
|
1014
|
+
* @param {object} newMetadata - The new metadata object for the model.
|
|
1015
|
+
* @returns {void}
|
|
1016
|
+
* @description
|
|
1017
|
+
* Currently, it only checks for changes related to the inner floor translation by delegating to #checkInnerFloorTranslation.
|
|
1018
|
+
*/
|
|
1019
|
+
#checkModelMetadata(oldMetadata, newMetadata) {
|
|
1020
|
+
// For the moment only check inner floor translation
|
|
1021
|
+
this.#checkInnerFloorTranslation(oldMetadata, newMetadata);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Checks and applies Y-axis translation for the inner floor node based on metadata changes.
|
|
1026
|
+
* @private
|
|
1027
|
+
* @param {object} oldMetadata - The previous metadata object for the model.
|
|
1028
|
+
* @param {object} newMetadata - The new metadata object for the model.
|
|
1029
|
+
* @returns {void}
|
|
1030
|
+
*/
|
|
1031
|
+
#checkInnerFloorTranslation(oldMetadata, newMetadata) {
|
|
1032
|
+
let newDeltaY = newMetadata?.customProperties?.innerFloorValue ? parseFloat(newMetadata.customProperties.innerFloorValue) : 0;
|
|
1033
|
+
let oldDeltaY = oldMetadata?.customProperties?.innerFloorValue ? parseFloat(oldMetadata.customProperties.innerFloorValue) : 0;
|
|
1034
|
+
|
|
1035
|
+
if (newDeltaY === 0 && oldDeltaY === 0) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
if (!this.#containers.environment.assetContainer) {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const getNodeName = (data) => {
|
|
1044
|
+
if (data && typeof data === "string" && data.includes("/")) {
|
|
1045
|
+
data = data.slice(data.lastIndexOf("/") + 1);
|
|
1046
|
+
return data;
|
|
1047
|
+
}
|
|
1048
|
+
return false;
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
let applyNewTranslation = false;
|
|
1052
|
+
let applyOldTranslation = false;
|
|
1053
|
+
let undoOldTranslation = false;
|
|
1054
|
+
const newTranslationNodeName = getNodeName(newMetadata?.customProperties?.innerFloorPath);
|
|
1055
|
+
const oldTranslationNodeName = getNodeName(oldMetadata?.customProperties?.innerFloorPath);
|
|
1056
|
+
|
|
1057
|
+
if (this.#containers.environment.state.isSuccess) {
|
|
1058
|
+
// If environment is being reloaded:
|
|
1059
|
+
if (this.#containers.model.state.isSuccess) {
|
|
1060
|
+
// If model is also being reloaded, apply only new translation
|
|
1061
|
+
applyNewTranslation = newDeltaY !== 0 && newTranslationNodeName;
|
|
1062
|
+
} else {
|
|
1063
|
+
// If model is not being reloaded, apply old translation
|
|
1064
|
+
applyOldTranslation = oldDeltaY !== 0 && oldTranslationNodeName;
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
// If environment is not being reloaded:
|
|
1068
|
+
if (this.#containers.model.state.isSuccess) {
|
|
1069
|
+
// If environment is not being reload but model is being reloaded, undo old trasnslation and apply new translation
|
|
1070
|
+
undoOldTranslation = oldDeltaY !== 0 && oldTranslationNodeName;
|
|
1071
|
+
applyNewTranslation = newDeltaY !== 0 && newTranslationNodeName;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (undoOldTranslation) {
|
|
1076
|
+
this.#translateNodeY(oldTranslationNodeName, -oldDeltaY);
|
|
1077
|
+
}
|
|
1078
|
+
if (applyOldTranslation) {
|
|
1079
|
+
this.#translateNodeY(oldTranslationNodeName, oldDeltaY);
|
|
1080
|
+
}
|
|
1081
|
+
if (applyNewTranslation) {
|
|
1082
|
+
this.#translateNodeY(newTranslationNodeName, newDeltaY);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Translates a node along the scene's vertical (Y) axis by the provided value.
|
|
1088
|
+
* @private
|
|
1089
|
+
* @param {string} name - The node name to find in the scene.
|
|
1090
|
+
* @param {number} deltaY - The Y-axis translation amount (positive or negative).
|
|
1091
|
+
* @returns {boolean} True if the node was found and translated, false otherwise.
|
|
1092
|
+
*/
|
|
1093
|
+
#translateNodeY(name, deltaY) {
|
|
1094
|
+
if (!this.#scene) {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
const node = this.#scene.getNodeByName(name);
|
|
1098
|
+
if (!node || !node.position) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
node.position.y += deltaY;
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
996
1105
|
/**
|
|
997
1106
|
* Appends the current date and time to the provided name string in the format: name_YYYY-MM-DD_HH.mm.ss
|
|
998
1107
|
* @private
|
package/src/gltf-resolver.js
CHANGED
|
@@ -220,6 +220,7 @@ export default class GLTFResolver {
|
|
|
220
220
|
let source = storage.url || null;
|
|
221
221
|
let newSize, newTimeStamp;
|
|
222
222
|
let pending = false;
|
|
223
|
+
let metadata = {};
|
|
223
224
|
|
|
224
225
|
if (storage.db && storage.table && storage.id) {
|
|
225
226
|
await this.#initializeStorage(storage.db, storage.table);
|
|
@@ -228,6 +229,7 @@ export default class GLTFResolver {
|
|
|
228
229
|
return false;
|
|
229
230
|
}
|
|
230
231
|
source = object.data;
|
|
232
|
+
metadata = { ...(object.metadata ?? {}) };
|
|
231
233
|
if (object.timeStamp === currentTimeStamp) {
|
|
232
234
|
return false;
|
|
233
235
|
} else {
|
|
@@ -283,6 +285,6 @@ export default class GLTFResolver {
|
|
|
283
285
|
}
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
|
-
return { source: file || source, size: newSize, timeStamp: newTimeStamp, extension: extension };
|
|
288
|
+
return { source: file || source, size: newSize, timeStamp: newTimeStamp, extension: extension, metadata: metadata, };
|
|
287
289
|
}
|
|
288
290
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export class ContainerData {
|
|
15
15
|
constructor(name = "") {
|
|
16
|
+
this.metadata = {};
|
|
16
17
|
this.name = name;
|
|
17
18
|
this.show = true;
|
|
18
19
|
this.size = 0;
|
|
@@ -23,6 +24,7 @@ export class ContainerData {
|
|
|
23
24
|
}
|
|
24
25
|
reset() {
|
|
25
26
|
this.update = {
|
|
27
|
+
metadata: {},
|
|
26
28
|
pending: false,
|
|
27
29
|
storage: null,
|
|
28
30
|
success: false,
|
|
@@ -37,6 +39,7 @@ export class ContainerData {
|
|
|
37
39
|
this.show = this.update.show !== null ? this.update.show : this.show;
|
|
38
40
|
this.size = this.update.size;
|
|
39
41
|
this.timeStamp = this.update.timeStamp;
|
|
42
|
+
this.metadata = { ...(this.update.metadata ?? {}) };
|
|
40
43
|
} else {
|
|
41
44
|
this.update.success = false;
|
|
42
45
|
}
|
|
@@ -48,9 +51,10 @@ export class ContainerData {
|
|
|
48
51
|
this.update.success = false;
|
|
49
52
|
this.update.show = show !== undefined ? show : this.update.show !== null ? this.update.show : this.show;
|
|
50
53
|
}
|
|
51
|
-
setPendingCacheData(size = 0, timeStamp = null) {
|
|
54
|
+
setPendingCacheData(size = 0, timeStamp = null, metadata = {}) {
|
|
52
55
|
this.update.size = size;
|
|
53
56
|
this.update.timeStamp = timeStamp;
|
|
57
|
+
this.update.metadata = { ...(metadata ?? {}) };
|
|
54
58
|
}
|
|
55
59
|
get isPending() {
|
|
56
60
|
return this.update.pending === true;
|