@preference-sl/pref-viewer 2.10.0-beta.2 → 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.
- package/package.json +1 -1
- package/src/index.js +327 -89
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, WebXRSessionManager, WebXRDefaultExperience } 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
|
|
@@ -113,7 +165,7 @@ class PrefViewer extends HTMLElement {
|
|
|
113
165
|
if (this.#initialized) {
|
|
114
166
|
data ? this.showModel() : this.hideModel();
|
|
115
167
|
} else {
|
|
116
|
-
this.#model.show = data;
|
|
168
|
+
this.#data.containers.model.show = data;
|
|
117
169
|
}
|
|
118
170
|
break;
|
|
119
171
|
case "show-scene":
|
|
@@ -121,7 +173,7 @@ class PrefViewer extends HTMLElement {
|
|
|
121
173
|
if (this.#initialized) {
|
|
122
174
|
data ? this.showScene() : this.hideScene();
|
|
123
175
|
} else {
|
|
124
|
-
this.#environment.show = data;
|
|
176
|
+
this.#data.containers.environment.show = data;
|
|
125
177
|
}
|
|
126
178
|
break;
|
|
127
179
|
}
|
|
@@ -142,7 +194,7 @@ class PrefViewer extends HTMLElement {
|
|
|
142
194
|
}
|
|
143
195
|
|
|
144
196
|
this.#initializeBabylon();
|
|
145
|
-
this.#loadContainers(true, true);
|
|
197
|
+
this.#loadContainers(true, true, true);
|
|
146
198
|
this.#initialized = true;
|
|
147
199
|
}
|
|
148
200
|
|
|
@@ -163,17 +215,48 @@ class PrefViewer extends HTMLElement {
|
|
|
163
215
|
}
|
|
164
216
|
|
|
165
217
|
#wrapCanvas() {
|
|
166
|
-
|
|
167
|
-
Object.assign(wrapper.style, {
|
|
218
|
+
this.#wrapper = document.createElement("div");
|
|
219
|
+
Object.assign(this.#wrapper.style, {
|
|
168
220
|
width: "100%",
|
|
169
221
|
height: "100%",
|
|
170
222
|
position: "relative",
|
|
171
223
|
});
|
|
172
|
-
wrapper.appendChild(this.#canvas);
|
|
173
|
-
this.shadowRoot.append(wrapper);
|
|
224
|
+
this.#wrapper.appendChild(this.#canvas);
|
|
225
|
+
this.shadowRoot.append(this.#wrapper);
|
|
174
226
|
}
|
|
175
|
-
|
|
176
|
-
//
|
|
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
|
|
177
260
|
async #initializeBabylon() {
|
|
178
261
|
this.#engine = new Engine(this.#canvas, true, { alpha: true });
|
|
179
262
|
this.#scene = new Scene(this.#engine);
|
|
@@ -181,18 +264,25 @@ class PrefViewer extends HTMLElement {
|
|
|
181
264
|
this.#createCamera();
|
|
182
265
|
this.#createLights();
|
|
183
266
|
this.#setupInteraction();
|
|
184
|
-
|
|
267
|
+
|
|
185
268
|
this.#engine.runRenderLoop(() => this.#scene && this.#scene.render());
|
|
186
269
|
this.#canvasResizeObserver.observe(this.#canvas);
|
|
187
270
|
|
|
188
271
|
await this.#createXRExperience();
|
|
189
272
|
}
|
|
190
273
|
|
|
274
|
+
addStylesToARButton() {
|
|
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"}';
|
|
276
|
+
const style = document.createElement("style");
|
|
277
|
+
style.appendChild(document.createTextNode(css));
|
|
278
|
+
this.#wrapper.appendChild(style);
|
|
279
|
+
}
|
|
280
|
+
|
|
191
281
|
async #createXRExperience() {
|
|
192
282
|
if (this.#XRExperience) {
|
|
193
283
|
return true;
|
|
194
284
|
}
|
|
195
|
-
|
|
285
|
+
|
|
196
286
|
const sessionMode = "immersive-ar";
|
|
197
287
|
const sessionSupported = await WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
|
|
198
288
|
if (!sessionSupported) {
|
|
@@ -200,16 +290,37 @@ class PrefViewer extends HTMLElement {
|
|
|
200
290
|
return false;
|
|
201
291
|
}
|
|
202
292
|
|
|
203
|
-
const options = {
|
|
204
|
-
uiOptions: {
|
|
205
|
-
sessionMode: sessionMode,
|
|
206
|
-
renderTarget: "xrLayer",
|
|
207
|
-
referenceSpaceType: "local",
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
|
|
211
293
|
try {
|
|
212
|
-
|
|
294
|
+
const ground = MeshBuilder.CreateGround("ground", { width: 1000, height: 1000 }, this.#scene);
|
|
295
|
+
ground.isVisible = false;
|
|
296
|
+
|
|
297
|
+
const options = {
|
|
298
|
+
floorMeshes: [ground],
|
|
299
|
+
uiOptions: {
|
|
300
|
+
sessionMode: sessionMode,
|
|
301
|
+
renderTarget: "xrLayer",
|
|
302
|
+
referenceSpaceType: "local",
|
|
303
|
+
},
|
|
304
|
+
optionalFeatures: true,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
this.#XRExperience = await WebXRDefaultExperience.CreateAsync(this.#scene, options);
|
|
308
|
+
|
|
309
|
+
const featuresManager = this.#XRExperience.baseExperience.featuresManager;
|
|
310
|
+
featuresManager.enableFeature(WebXRFeatureName.TELEPORTATION, "stable", {
|
|
311
|
+
xrInput: this.#XRExperience.input,
|
|
312
|
+
floorMeshes: [ground],
|
|
313
|
+
timeToTeleport: 1500,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
this.#XRExperience.baseExperience.sessionManager.onXRReady.add(() => {
|
|
317
|
+
// Set the initial position of xrCamera: use nonVRCamera, which contains a copy of the original this.#scene.activeCamera before entering XR
|
|
318
|
+
this.#XRExperience.baseExperience.camera.setTransformationFromNonVRCamera(this.#XRExperience.baseExperience._nonVRCamera);
|
|
319
|
+
this.#XRExperience.baseExperience.camera.setTarget(Vector3.Zero());
|
|
320
|
+
this.#XRExperience.baseExperience.onInitialXRPoseSetObservable.notifyObservers(this.#XRExperience.baseExperience.camera);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
this.addStylesToARButton();
|
|
213
324
|
} catch (error) {
|
|
214
325
|
console.warn("PrefViewer: failed to create WebXR experience", error);
|
|
215
326
|
this.#XRExperience = null;
|
|
@@ -219,11 +330,13 @@ class PrefViewer extends HTMLElement {
|
|
|
219
330
|
#canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
|
|
220
331
|
|
|
221
332
|
#createCamera() {
|
|
222
|
-
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);
|
|
223
334
|
this.#camera.upperBetaLimit = Math.PI * 0.48;
|
|
224
335
|
this.#camera.lowerBetaLimit = Math.PI * 0.25;
|
|
225
336
|
this.#camera.lowerRadiusLimit = 5;
|
|
226
337
|
this.#camera.upperRadiusLimit = 20;
|
|
338
|
+
this.#camera.metadata = { locked: false }
|
|
339
|
+
this.#camera = this.#camera;
|
|
227
340
|
this.#camera.attachControl(this.#canvas, true);
|
|
228
341
|
}
|
|
229
342
|
|
|
@@ -251,10 +364,14 @@ class PrefViewer extends HTMLElement {
|
|
|
251
364
|
|
|
252
365
|
#setupInteraction() {
|
|
253
366
|
this.#canvas.addEventListener("wheel", (event) => {
|
|
254
|
-
if (!this.#scene || !this.#camera)
|
|
255
|
-
|
|
367
|
+
if (!this.#scene || !this.#camera) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
//const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
256
371
|
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
257
|
-
this.#
|
|
372
|
+
if (!this.#scene.activeCamera.metadata?.locked) {
|
|
373
|
+
this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
|
|
374
|
+
}
|
|
258
375
|
event.preventDefault();
|
|
259
376
|
});
|
|
260
377
|
}
|
|
@@ -268,6 +385,27 @@ class PrefViewer extends HTMLElement {
|
|
|
268
385
|
}
|
|
269
386
|
|
|
270
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
|
+
|
|
271
409
|
#transformUrl(url) {
|
|
272
410
|
return new Promise((resolve) => {
|
|
273
411
|
resolve(url.replace(/^blob:[^/]+\//i, "").replace(/\\/g, "/"));
|
|
@@ -280,10 +418,11 @@ class PrefViewer extends HTMLElement {
|
|
|
280
418
|
let decoded = "";
|
|
281
419
|
let blob = null;
|
|
282
420
|
let extension = null;
|
|
421
|
+
let size = raw.length;
|
|
283
422
|
try {
|
|
284
423
|
decoded = atob(raw);
|
|
285
424
|
} catch {
|
|
286
|
-
return { blob, extension };
|
|
425
|
+
return { blob, extension, size };
|
|
287
426
|
}
|
|
288
427
|
let isJson = false;
|
|
289
428
|
try {
|
|
@@ -294,7 +433,7 @@ class PrefViewer extends HTMLElement {
|
|
|
294
433
|
const type = isJson ? "model/gltf+json" : "model/gltf-binary";
|
|
295
434
|
const array = Uint8Array.from(decoded, (c) => c.charCodeAt(0));
|
|
296
435
|
blob = new Blob([array], { type });
|
|
297
|
-
return { blob, extension };
|
|
436
|
+
return { blob, extension, size };
|
|
298
437
|
}
|
|
299
438
|
|
|
300
439
|
async #initStorage(db, table) {
|
|
@@ -306,41 +445,95 @@ class PrefViewer extends HTMLElement {
|
|
|
306
445
|
|
|
307
446
|
// Methods for managing Asset Containers
|
|
308
447
|
#setVisibilityOfWallAndFloorInModel(show) {
|
|
309
|
-
if (this.#model.
|
|
310
|
-
|
|
311
|
-
const nodes = this.#model.container.getNodes();
|
|
312
|
-
this.#model.container
|
|
313
|
-
.getNodes()
|
|
314
|
-
.filter((filter) => names.includes(filter.name))
|
|
315
|
-
.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;
|
|
316
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));
|
|
317
454
|
}
|
|
318
455
|
|
|
319
|
-
#
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
456
|
+
#setOptionsMaterial(optionMaterial) {
|
|
457
|
+
if (!optionMaterial || !optionMaterial.prefix || !optionMaterial.value) {
|
|
458
|
+
return false;
|
|
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;
|
|
323
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;
|
|
324
487
|
}
|
|
325
488
|
|
|
326
|
-
#
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
489
|
+
#setOptionsMaterials() {
|
|
490
|
+
Object.values(this.#data.options.materials).forEach((material) => this.#setOptionsMaterial(material));
|
|
491
|
+
}
|
|
492
|
+
|
|
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
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
this.#scene.activeCamera = camera;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
#addContainer(container) {
|
|
511
|
+
if (container.assetContainer && !container.visible && container.show) {
|
|
512
|
+
container.assetContainer.addAllToScene();
|
|
513
|
+
container.visible = true;
|
|
330
514
|
}
|
|
331
515
|
}
|
|
332
516
|
|
|
333
|
-
#
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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) => {
|
|
337
528
|
mesh.receiveShadows = true;
|
|
338
529
|
this.#shadowGen.addShadowCaster(mesh, true);
|
|
339
530
|
});
|
|
340
|
-
this.#addContainer(
|
|
531
|
+
this.#addContainer(container);
|
|
341
532
|
}
|
|
342
533
|
|
|
343
|
-
async #loadAssetContainer(
|
|
534
|
+
async #loadAssetContainer(container) {
|
|
535
|
+
let storage = container?.storage;
|
|
536
|
+
|
|
344
537
|
if (!storage) {
|
|
345
538
|
return false;
|
|
346
539
|
}
|
|
@@ -351,6 +544,11 @@ class PrefViewer extends HTMLElement {
|
|
|
351
544
|
await this.#initStorage(storage.db, storage.table);
|
|
352
545
|
const object = await loadModel(storage.id, storage.table);
|
|
353
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
|
+
}
|
|
354
552
|
}
|
|
355
553
|
|
|
356
554
|
if (!source) {
|
|
@@ -359,20 +557,34 @@ class PrefViewer extends HTMLElement {
|
|
|
359
557
|
|
|
360
558
|
let file = null;
|
|
361
559
|
|
|
362
|
-
let { blob, extension } = this.#decodeBase64(source);
|
|
560
|
+
let { blob, extension, size } = this.#decodeBase64(source);
|
|
363
561
|
if (blob && extension) {
|
|
364
|
-
file = new File([blob],
|
|
562
|
+
file = new File([blob], `${container.name}${extension}`, {
|
|
365
563
|
type: blob.type,
|
|
366
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
|
+
}
|
|
367
572
|
} else {
|
|
368
573
|
const extMatch = source.match(/\.(gltf|glb)(\?|#|$)/i);
|
|
369
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
|
+
}
|
|
370
581
|
}
|
|
371
582
|
|
|
372
583
|
let options = {
|
|
373
584
|
pluginExtension: extension,
|
|
374
585
|
pluginOptions: {
|
|
375
586
|
gltf: {
|
|
587
|
+
loadAllMaterials: true,
|
|
376
588
|
preprocessUrlAsync: this.#transformUrl,
|
|
377
589
|
},
|
|
378
590
|
},
|
|
@@ -381,27 +593,43 @@ class PrefViewer extends HTMLElement {
|
|
|
381
593
|
return LoadAssetContainerAsync(file || source, this.#scene, options);
|
|
382
594
|
}
|
|
383
595
|
|
|
384
|
-
async #loadContainers(loadModel = true, loadEnvironment = true) {
|
|
596
|
+
async #loadContainers(loadModel = true, loadEnvironment = true, loadMaterials = true) {
|
|
385
597
|
const promiseArray = [];
|
|
386
|
-
|
|
387
|
-
promiseArray.push(
|
|
388
|
-
promiseArray.push(
|
|
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);
|
|
389
601
|
|
|
390
602
|
Promise.allSettled(promiseArray)
|
|
391
603
|
.then(async (values) => {
|
|
392
604
|
const modelContainer = values[0];
|
|
393
605
|
const environmentContainer = values[1];
|
|
606
|
+
const materialsContainer = values[2];
|
|
394
607
|
|
|
395
608
|
if (modelContainer.status === "fulfilled" && modelContainer.value) {
|
|
396
|
-
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);
|
|
397
613
|
}
|
|
398
614
|
|
|
399
615
|
if (environmentContainer.status === "fulfilled" && environmentContainer.value) {
|
|
400
|
-
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);
|
|
401
620
|
}
|
|
402
621
|
|
|
622
|
+
if (materialsContainer.status === "fulfilled" && materialsContainer.value) {
|
|
623
|
+
this.#replaceContainer(this.#data.containers.materials, materialsContainer.value);
|
|
624
|
+
this.#storeChangedFlagsForContainer(this.#data.containers.materials);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
this.#setOptionsMaterials();
|
|
628
|
+
this.#setOptionsCamera();
|
|
403
629
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
404
630
|
|
|
631
|
+
this.#resetChangedFlags();
|
|
632
|
+
|
|
405
633
|
this.dispatchEvent(
|
|
406
634
|
new CustomEvent("model-loaded", {
|
|
407
635
|
detail: { success: "" },
|
|
@@ -428,11 +656,21 @@ class PrefViewer extends HTMLElement {
|
|
|
428
656
|
if (!config) {
|
|
429
657
|
return false;
|
|
430
658
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
this.#
|
|
434
|
-
this.#
|
|
435
|
-
this.#
|
|
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);
|
|
436
674
|
}
|
|
437
675
|
|
|
438
676
|
loadModel(model) {
|
|
@@ -440,9 +678,9 @@ class PrefViewer extends HTMLElement {
|
|
|
440
678
|
if (!model) {
|
|
441
679
|
return false;
|
|
442
680
|
}
|
|
443
|
-
this.#model.storage = model.storage || null;
|
|
444
|
-
this.#model.show = model.visible !== undefined ? model.visible : this.#model.show;
|
|
445
|
-
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);
|
|
446
684
|
}
|
|
447
685
|
|
|
448
686
|
loadScene(scene) {
|
|
@@ -450,43 +688,43 @@ class PrefViewer extends HTMLElement {
|
|
|
450
688
|
if (!scene) {
|
|
451
689
|
return false;
|
|
452
690
|
}
|
|
453
|
-
this.#environment.storage = scene.storage || null;
|
|
454
|
-
this.#environment.show = scene.visible !== undefined ? scene.visible : this.#environment.show;
|
|
455
|
-
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);
|
|
456
694
|
}
|
|
457
695
|
|
|
458
696
|
showModel() {
|
|
459
|
-
this.#model.show = true;
|
|
460
|
-
this.#addContainer(this.#model);
|
|
697
|
+
this.#data.containers.model.show = true;
|
|
698
|
+
this.#addContainer(this.#data.containers.model);
|
|
461
699
|
}
|
|
462
700
|
|
|
463
701
|
hideModel() {
|
|
464
|
-
this.#model.show = false;
|
|
465
|
-
this.#removeContainer(this.#model);
|
|
702
|
+
this.#data.containers.model.show = false;
|
|
703
|
+
this.#removeContainer(this.#data.containers.model);
|
|
466
704
|
}
|
|
467
705
|
|
|
468
706
|
showScene() {
|
|
469
|
-
this.#environment.show = true;
|
|
470
|
-
this.#addContainer(this.#environment);
|
|
707
|
+
this.#data.containers.environment.show = true;
|
|
708
|
+
this.#addContainer(this.#data.containers.environment);
|
|
471
709
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
472
710
|
}
|
|
473
711
|
|
|
474
712
|
hideScene() {
|
|
475
|
-
this.#environment.show = false;
|
|
476
|
-
this.#removeContainer(this.#environment);
|
|
713
|
+
this.#data.containers.environment.show = false;
|
|
714
|
+
this.#removeContainer(this.#data.containers.environment);
|
|
477
715
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
478
716
|
}
|
|
479
717
|
|
|
480
718
|
downloadModelGLB() {
|
|
481
719
|
const fileName = "model";
|
|
482
|
-
GLTF2Export.GLBAsync(this.#model.
|
|
720
|
+
GLTF2Export.GLBAsync(this.#data.containers.model.assetContainer, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
|
|
483
721
|
glb.downloadFiles();
|
|
484
722
|
});
|
|
485
723
|
}
|
|
486
724
|
|
|
487
725
|
downloadModelUSDZ() {
|
|
488
726
|
const fileName = "model";
|
|
489
|
-
USDZExportAsync(this.#model.
|
|
727
|
+
USDZExportAsync(this.#data.containers.model.assetContainer).then((response) => {
|
|
490
728
|
if (response) {
|
|
491
729
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
492
730
|
}
|