@preference-sl/pref-viewer 2.10.0-beta.1 → 2.10.0-beta.11
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 +377 -85
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* </pref-viewer>
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
import { Engine, Scene, ArcRotateCamera, Vector3, Color4, HemisphericLight, DirectionalLight, PointLight, ShadowGenerator, LoadAssetContainerAsync, Tools } from "@babylonjs/core";
|
|
42
|
+
import { Engine, Scene, ArcRotateCamera, Vector3, Color4, HemisphericLight, DirectionalLight, PointLight, ShadowGenerator, LoadAssetContainerAsync, Tools, WebXRSessionManager, WebXRDefaultExperience, MeshBuilder, WebXRFeatureName } from "@babylonjs/core";
|
|
43
43
|
import "@babylonjs/loaders";
|
|
44
44
|
import { USDZExportAsync, GLTF2Export } from "@babylonjs/serializers";
|
|
45
45
|
import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
|
|
@@ -49,20 +49,72 @@ import { initDb, loadModel } from "./gltf-storage.js";
|
|
|
49
49
|
class PrefViewer extends HTMLElement {
|
|
50
50
|
#initialized = false;
|
|
51
51
|
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
|
116
|
+
// DOM elements
|
|
117
|
+
#wrapper = null;
|
|
66
118
|
#canvas = null;
|
|
67
119
|
|
|
68
120
|
// Babylon.js core objects
|
|
@@ -73,6 +125,7 @@ class PrefViewer extends HTMLElement {
|
|
|
73
125
|
#dirLight = null;
|
|
74
126
|
#cameraLight = null;
|
|
75
127
|
#shadowGen = null;
|
|
128
|
+
#XRExperience = null;
|
|
76
129
|
|
|
77
130
|
constructor() {
|
|
78
131
|
super();
|
|
@@ -112,7 +165,7 @@ class PrefViewer extends HTMLElement {
|
|
|
112
165
|
if (this.#initialized) {
|
|
113
166
|
data ? this.showModel() : this.hideModel();
|
|
114
167
|
} else {
|
|
115
|
-
this.#model.show = data;
|
|
168
|
+
this.#data.containers.model.show = data;
|
|
116
169
|
}
|
|
117
170
|
break;
|
|
118
171
|
case "show-scene":
|
|
@@ -120,7 +173,7 @@ class PrefViewer extends HTMLElement {
|
|
|
120
173
|
if (this.#initialized) {
|
|
121
174
|
data ? this.showScene() : this.hideScene();
|
|
122
175
|
} else {
|
|
123
|
-
this.#environment.show = data;
|
|
176
|
+
this.#data.containers.environment.show = data;
|
|
124
177
|
}
|
|
125
178
|
break;
|
|
126
179
|
}
|
|
@@ -141,7 +194,7 @@ class PrefViewer extends HTMLElement {
|
|
|
141
194
|
}
|
|
142
195
|
|
|
143
196
|
this.#initializeBabylon();
|
|
144
|
-
this.#loadContainers(true, true);
|
|
197
|
+
this.#loadContainers(true, true, true);
|
|
145
198
|
this.#initialized = true;
|
|
146
199
|
}
|
|
147
200
|
|
|
@@ -162,38 +215,132 @@ class PrefViewer extends HTMLElement {
|
|
|
162
215
|
}
|
|
163
216
|
|
|
164
217
|
#wrapCanvas() {
|
|
165
|
-
|
|
166
|
-
Object.assign(wrapper.style, {
|
|
218
|
+
this.#wrapper = document.createElement("div");
|
|
219
|
+
Object.assign(this.#wrapper.style, {
|
|
167
220
|
width: "100%",
|
|
168
221
|
height: "100%",
|
|
169
222
|
position: "relative",
|
|
170
223
|
});
|
|
171
|
-
wrapper.appendChild(this.#canvas);
|
|
172
|
-
this.shadowRoot.append(wrapper);
|
|
224
|
+
this.#wrapper.appendChild(this.#canvas);
|
|
225
|
+
this.shadowRoot.append(this.#wrapper);
|
|
173
226
|
}
|
|
174
227
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
264
|
+
async #initializeBabylon() {
|
|
178
265
|
this.#engine = new Engine(this.#canvas, true, { alpha: true });
|
|
179
266
|
this.#scene = new Scene(this.#engine);
|
|
180
267
|
this.#scene.clearColor = new Color4(1, 1, 1, 1);
|
|
181
268
|
this.#createCamera();
|
|
182
269
|
this.#createLights();
|
|
183
270
|
this.#setupInteraction();
|
|
184
|
-
|
|
271
|
+
|
|
185
272
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
186
273
|
this.#canvasResizeObserver.observe(this.#canvas);
|
|
274
|
+
|
|
275
|
+
await this.#createXRExperience();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
addStylesToARButton() {
|
|
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"}';
|
|
280
|
+
const style = document.createElement("style");
|
|
281
|
+
style.appendChild(document.createTextNode(css));
|
|
282
|
+
this.#wrapper.appendChild(style);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async #createXRExperience() {
|
|
286
|
+
if (this.#XRExperience) {
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const sessionMode = "immersive-ar";
|
|
291
|
+
const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
|
|
292
|
+
if (!sessionSupported) {
|
|
293
|
+
console.info("PrefViewer: WebXR in mode AR is not supported");
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
|
|
299
|
+
ground.isVisible = false;
|
|
300
|
+
|
|
301
|
+
const options = {
|
|
302
|
+
floorMeshes: [ground],
|
|
303
|
+
uiOptions: {
|
|
304
|
+
sessionMode: sessionMode,
|
|
305
|
+
renderTarget: "xrLayer",
|
|
306
|
+
referenceSpaceType: "local",
|
|
307
|
+
},
|
|
308
|
+
optionalFeatures: true,
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
this.#XRExperience = await WebXRDefaultExperience.CreateAsync(this.#scene, options);
|
|
312
|
+
|
|
313
|
+
const featuresManager = this.#XRExperience.baseExperience.featuresManager;
|
|
314
|
+
featuresManager.enableFeature(WebXRFeatureName.TELEPORTATION, "stable", {
|
|
315
|
+
xrInput: this.#XRExperience.input,
|
|
316
|
+
floorMeshes: [ground],
|
|
317
|
+
timeToTeleport: 1500,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
|
|
321
|
+
// Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
|
|
322
|
+
this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
|
|
323
|
+
this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
|
|
324
|
+
this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(this.#XRExperience.baseExperience.camera);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
this.addStylesToARButton();
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.warn("PrefViewer: failed to create WebXR experience", error);
|
|
330
|
+
this.#XRExperience = null;
|
|
331
|
+
}
|
|
187
332
|
}
|
|
188
333
|
|
|
189
334
|
#canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
|
|
190
335
|
|
|
191
336
|
#createCamera() {
|
|
192
|
-
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);
|
|
193
338
|
this.#camera.upperBetaLimit = Math.PI * 0.48;
|
|
194
339
|
this.#camera.lowerBetaLimit = Math.PI * 0.25;
|
|
195
340
|
this.#camera.lowerRadiusLimit = 5;
|
|
196
341
|
this.#camera.upperRadiusLimit = 20;
|
|
342
|
+
this.#camera.metadata = { locked: false }
|
|
343
|
+
this.#camera = this.#camera;
|
|
197
344
|
this.#camera.attachControl(this.#canvas, true);
|
|
198
345
|
}
|
|
199
346
|
|
|
@@ -221,10 +368,14 @@ class PrefViewer extends HTMLElement {
|
|
|
221
368
|
|
|
222
369
|
#setupInteraction() {
|
|
223
370
|
this.#canvas.addEventListener("wheel", (event) => {
|
|
224
|
-
if (!this.#scene || !this.#camera)
|
|
225
|
-
|
|
371
|
+
if (!this.#scene || !this.#camera) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
//const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
226
375
|
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
227
|
-
this.#
|
|
376
|
+
if (!this.#scene.activeCamera.metadata?.locked) {
|
|
377
|
+
this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
|
|
378
|
+
}
|
|
228
379
|
event.preventDefault();
|
|
229
380
|
});
|
|
230
381
|
}
|
|
@@ -238,6 +389,27 @@ class PrefViewer extends HTMLElement {
|
|
|
238
389
|
}
|
|
239
390
|
|
|
240
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
|
+
|
|
241
413
|
#transformUrl(url) {
|
|
242
414
|
return new Promise((resolve) => {
|
|
243
415
|
resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
|
|
@@ -250,10 +422,11 @@ class PrefViewer extends HTMLElement {
|
|
|
250
422
|
let decoded = "";
|
|
251
423
|
let blob = null;
|
|
252
424
|
let extension = null;
|
|
425
|
+
let size = raw.length;
|
|
253
426
|
try {
|
|
254
427
|
decoded = atob(raw);
|
|
255
428
|
} catch {
|
|
256
|
-
return { blob, extension };
|
|
429
|
+
return { blob, extension, size };
|
|
257
430
|
}
|
|
258
431
|
let isJson = false;
|
|
259
432
|
try {
|
|
@@ -264,7 +437,7 @@ class PrefViewer extends HTMLElement {
|
|
|
264
437
|
const type = isJson ? "model/gltf+json" : "model/gltf-binary";
|
|
265
438
|
const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
|
|
266
439
|
blob = new Blob([array], { type });
|
|
267
|
-
return { blob, extension };
|
|
440
|
+
return { blob, extension, size };
|
|
268
441
|
}
|
|
269
442
|
|
|
270
443
|
async #initStorage(db, table) {
|
|
@@ -276,41 +449,103 @@ class PrefViewer extends HTMLElement {
|
|
|
276
449
|
|
|
277
450
|
// Methods for managing Asset Containers
|
|
278
451
|
#setVisibilityOfWallAndFloorInModel(show) {
|
|
279
|
-
if (this.#model.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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;
|
|
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;
|
|
501
|
+
}
|
|
502
|
+
|
|
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;
|
|
286
511
|
}
|
|
512
|
+
|
|
513
|
+
camera.metadata = { locked: this.#data.options.camera.locked };
|
|
514
|
+
if (!this.#data.options.camera.locked) {
|
|
515
|
+
camera.attachControl(this.#canvas, true);
|
|
516
|
+
}
|
|
517
|
+
this.#scene.activeCamera = camera;
|
|
518
|
+
|
|
519
|
+
return true;
|
|
287
520
|
}
|
|
288
521
|
|
|
289
|
-
#addContainer(
|
|
290
|
-
if (
|
|
291
|
-
|
|
292
|
-
|
|
522
|
+
#addContainer(container) {
|
|
523
|
+
if (container.assetContainer && !container.visible && container.show) {
|
|
524
|
+
container.assetContainer.addAllToScene();
|
|
525
|
+
container.visible = true;
|
|
293
526
|
}
|
|
294
527
|
}
|
|
295
528
|
|
|
296
|
-
#removeContainer(
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
529
|
+
#removeContainer(container) {
|
|
530
|
+
if (container.assetContainer && container.visible) {
|
|
531
|
+
container.assetContainer.removeAllFromScene();
|
|
532
|
+
container.visible = false;
|
|
300
533
|
}
|
|
301
534
|
}
|
|
302
535
|
|
|
303
|
-
#replaceContainer(
|
|
304
|
-
this.#removeContainer(
|
|
305
|
-
|
|
306
|
-
|
|
536
|
+
#replaceContainer(container, newAssetContainer) {
|
|
537
|
+
this.#removeContainer(container);
|
|
538
|
+
container.assetContainer = newAssetContainer;
|
|
539
|
+
container.assetContainer.meshes.forEach((mesh) => {
|
|
307
540
|
mesh.receiveShadows = true;
|
|
308
541
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
309
542
|
});
|
|
310
|
-
this.#addContainer(
|
|
543
|
+
this.#addContainer(container);
|
|
311
544
|
}
|
|
312
545
|
|
|
313
|
-
async #loadAssetContainer(
|
|
546
|
+
async #loadAssetContainer(container) {
|
|
547
|
+
let storage = container?.storage;
|
|
548
|
+
|
|
314
549
|
if (!storage) {
|
|
315
550
|
return false;
|
|
316
551
|
}
|
|
@@ -321,6 +556,11 @@ class PrefViewer extends HTMLElement {
|
|
|
321
556
|
await this.#initStorage(storage.db, storage.table);
|
|
322
557
|
const object = await loadModel(storage.id, storage.table);
|
|
323
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
|
+
}
|
|
324
564
|
}
|
|
325
565
|
|
|
326
566
|
if (!source) {
|
|
@@ -329,20 +569,34 @@ class PrefViewer extends HTMLElement {
|
|
|
329
569
|
|
|
330
570
|
let file = null;
|
|
331
571
|
|
|
332
|
-
let { blob, extension } = this.#decodeBase64(source);
|
|
572
|
+
let { blob, extension, size } = this.#decodeBase64(source);
|
|
333
573
|
if (blob && extension) {
|
|
334
|
-
file = new File([blob],
|
|
574
|
+
file = new File([blob], `${container.name}${extension}`, {
|
|
335
575
|
type: blob.type,
|
|
336
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
|
+
}
|
|
337
584
|
} else {
|
|
338
585
|
const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
|
|
339
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
|
+
}
|
|
340
593
|
}
|
|
341
594
|
|
|
342
595
|
let options = {
|
|
343
596
|
pluginExtension: extension,
|
|
344
597
|
pluginOptions: {
|
|
345
598
|
gltf: {
|
|
599
|
+
loadAllMaterials: true,
|
|
346
600
|
preprocessUrlAsync: this.#transformUrl,
|
|
347
601
|
},
|
|
348
602
|
},
|
|
@@ -351,27 +605,43 @@ class PrefViewer extends HTMLElement {
|
|
|
351
605
|
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
352
606
|
}
|
|
353
607
|
|
|
354
|
-
async #loadContainers(loadModel = true, loadEnvironment = true) {
|
|
608
|
+
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
355
609
|
const promiseArray = [];
|
|
356
|
-
|
|
357
|
-
promiseArray.push(
|
|
358
|
-
promiseArray.push(
|
|
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);
|
|
359
613
|
|
|
360
614
|
Promise.allSettled(promiseArray)
|
|
361
615
|
.then(async (values) => {
|
|
362
616
|
const modelContainer = values[0];
|
|
363
617
|
const environmentContainer = values[1];
|
|
618
|
+
const materialsContainer = values[2];
|
|
364
619
|
|
|
365
620
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
366
|
-
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);
|
|
367
625
|
}
|
|
368
626
|
|
|
369
627
|
if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
|
|
370
|
-
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);
|
|
371
637
|
}
|
|
372
638
|
|
|
639
|
+
this.#setOptionsMaterials();
|
|
640
|
+
this.#setOptionsCamera();
|
|
373
641
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
374
642
|
|
|
643
|
+
this.#resetChangedFlags();
|
|
644
|
+
|
|
375
645
|
this.dispatchEvent(
|
|
376
646
|
new CustomEvent("model-loaded", {
|
|
377
647
|
detail: { success: "" },
|
|
@@ -398,11 +668,37 @@ class PrefViewer extends HTMLElement {
|
|
|
398
668
|
if (!config) {
|
|
399
669
|
return false;
|
|
400
670
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.#
|
|
404
|
-
this.#
|
|
405
|
-
this.#
|
|
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;
|
|
406
702
|
}
|
|
407
703
|
|
|
408
704
|
loadModel(model) {
|
|
@@ -410,9 +706,9 @@ class PrefViewer extends HTMLElement {
|
|
|
410
706
|
if (!model) {
|
|
411
707
|
return false;
|
|
412
708
|
}
|
|
413
|
-
this.#model.storage = model.storage || null;
|
|
414
|
-
this.#model.show = model.visible !== undefined ? model.visible : this.#model.show;
|
|
415
|
-
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);
|
|
416
712
|
}
|
|
417
713
|
|
|
418
714
|
loadScene(scene) {
|
|
@@ -420,43 +716,41 @@ class PrefViewer extends HTMLElement {
|
|
|
420
716
|
if (!scene) {
|
|
421
717
|
return false;
|
|
422
718
|
}
|
|
423
|
-
this.#environment.storage = scene.storage || null;
|
|
424
|
-
this.#environment.show = scene.visible !== undefined ? scene.visible : this.#environment.show;
|
|
425
|
-
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);
|
|
426
722
|
}
|
|
427
723
|
|
|
428
724
|
showModel() {
|
|
429
|
-
this.#model.show = true;
|
|
430
|
-
this.#addContainer(this.#model);
|
|
725
|
+
this.#data.containers.model.show = true;
|
|
726
|
+
this.#addContainer(this.#data.containers.model);
|
|
431
727
|
}
|
|
432
728
|
|
|
433
729
|
hideModel() {
|
|
434
|
-
this.#model.show = false;
|
|
435
|
-
this.#removeContainer(this.#model);
|
|
730
|
+
this.#data.containers.model.show = false;
|
|
731
|
+
this.#removeContainer(this.#data.containers.model);
|
|
436
732
|
}
|
|
437
733
|
|
|
438
734
|
showScene() {
|
|
439
|
-
this.#environment.show = true;
|
|
440
|
-
this.#addContainer(this.#environment);
|
|
735
|
+
this.#data.containers.environment.show = true;
|
|
736
|
+
this.#addContainer(this.#data.containers.environment);
|
|
441
737
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
442
738
|
}
|
|
443
739
|
|
|
444
740
|
hideScene() {
|
|
445
|
-
this.#environment.show = false;
|
|
446
|
-
this.#removeContainer(this.#environment);
|
|
741
|
+
this.#data.containers.environment.show = false;
|
|
742
|
+
this.#removeContainer(this.#data.containers.environment);
|
|
447
743
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
448
744
|
}
|
|
449
745
|
|
|
450
746
|
downloadModelGLB() {
|
|
451
747
|
const fileName = "model";
|
|
452
|
-
GLTF2Export.GLBAsync(this.#model.
|
|
453
|
-
glb.downloadFiles();
|
|
454
|
-
});
|
|
748
|
+
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
455
749
|
}
|
|
456
750
|
|
|
457
751
|
downloadModelUSDZ() {
|
|
458
752
|
const fileName = "model";
|
|
459
|
-
USDZExportAsync(this.#model.
|
|
753
|
+
USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
|
|
460
754
|
if (response) {
|
|
461
755
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
462
756
|
}
|
|
@@ -474,9 +768,7 @@ class PrefViewer extends HTMLElement {
|
|
|
474
768
|
|
|
475
769
|
downloadModelAndSceneGLB() {
|
|
476
770
|
const fileName = "scene";
|
|
477
|
-
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) =>
|
|
478
|
-
glb.downloadFiles();
|
|
479
|
-
});
|
|
771
|
+
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
480
772
|
}
|
|
481
773
|
}
|
|
482
774
|
|