@preference-sl/pref-viewer 2.11.0-beta.0 → 2.11.0-beta.10
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 +14 -8
- package/src/babylonjs-animation-controller.js +235 -0
- package/src/babylonjs-animation-opening-menu.js +360 -0
- package/src/babylonjs-animation-opening.js +496 -0
- package/src/babylonjs-controller.js +1186 -0
- package/src/css/pref-viewer-2d.css +39 -0
- package/src/css/pref-viewer-3d.css +28 -0
- package/src/css/pref-viewer-dialog.css +105 -0
- package/src/css/pref-viewer.css +11 -0
- package/src/file-storage.js +166 -39
- package/src/gltf-resolver.js +288 -0
- package/src/index.js +721 -1057
- package/src/panzoom-controller.js +494 -0
- package/src/pref-viewer-2d.js +460 -0
- package/src/pref-viewer-3d-data.js +178 -0
- package/src/pref-viewer-3d.js +700 -0
- package/src/pref-viewer-dialog.js +139 -0
- package/src/pref-viewer-task.js +54 -0
- package/src/svg-resolver.js +281 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
import { CameraData, ContainerData, MaterialData } from "./pref-viewer-3d-data.js";
|
|
2
|
+
import BabylonJSController from "./babylonjs-controller.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PrefViewer3D - Custom Web Component for interactive 3D visualization and configuration.
|
|
6
|
+
*
|
|
7
|
+
* Overview:
|
|
8
|
+
* - Encapsulates a Babylon.js-powered 3D viewer for displaying models, environments, and materials.
|
|
9
|
+
* - Manages internal state for containers (model, environment, materials) and options (camera, materials).
|
|
10
|
+
* - Handles asset loading, configuration, and option updates through attributes and public methods.
|
|
11
|
+
* - Provides API for showing/hiding the viewer, model, and environment, and for downloading assets.
|
|
12
|
+
* - Emits custom events for loading, loaded, and option-setting states.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* - Use as a custom HTML element: <pref-viewer-3d ...>
|
|
16
|
+
* - Configure via attributes (config, options, show-model, show-scene, visible).
|
|
17
|
+
* - Control viewer state and assets via public methods.
|
|
18
|
+
*
|
|
19
|
+
* Observed Attributes:
|
|
20
|
+
* - show-model: Show or hide the 3D model ("true"/"false").
|
|
21
|
+
* - show-scene: Show or hide the 3D environment ("true"/"false").
|
|
22
|
+
* - visible: Show or hide the entire viewer ("true"/"false").
|
|
23
|
+
*
|
|
24
|
+
* Public Methods:
|
|
25
|
+
* - show(): Shows the 3D viewer component.
|
|
26
|
+
* - hide(): Hides the 3D viewer component.
|
|
27
|
+
* - load(config): Loads the provided configuration into the viewer.
|
|
28
|
+
* - setOptions(options): Sets viewer options such as camera and materials.
|
|
29
|
+
* - showModel(): Shows the 3D model.
|
|
30
|
+
* - hideModel(): Hides the 3D model.
|
|
31
|
+
* - showEnvironment(): Shows the 3D environment/scene.
|
|
32
|
+
* - hideEnvironment(): Hides the 3D environment/scene.
|
|
33
|
+
* - downloadModelGLB(): Downloads the current 3D model as a GLB file.
|
|
34
|
+
* - downloadModelGLTF(): Downloads the current 3D model as a glTF ZIP file.
|
|
35
|
+
* - downloadModelUSDZ(): Downloads the current 3D model as a USDZ file.
|
|
36
|
+
* - downloadModelAndSceneGLB(): Downloads both the model and scene as a GLB file.
|
|
37
|
+
* - downloadModelAndSceneGLTF(): Downloads both the model and scene as a glTF ZIP file.
|
|
38
|
+
* - downloadModelAndSceneUSDZ(): Downloads both the model and scene as a USDZ file.
|
|
39
|
+
* - downloadSceneGLB(): Downloads the environment as a GLB file.
|
|
40
|
+
* - downloadSceneGLTF(): Downloads the environment as a glTF ZIP file.
|
|
41
|
+
* - downloadSceneUSDZ(): Downloads the environment as a USDZ file.
|
|
42
|
+
*
|
|
43
|
+
* Public Properties:
|
|
44
|
+
* - isInitialized: Indicates whether the component has completed initialization.
|
|
45
|
+
* - isLoaded: Indicates whether the GLTF/GLB content is loaded and ready.
|
|
46
|
+
* - isVisible: Indicates whether the component is currently visible.
|
|
47
|
+
*
|
|
48
|
+
* Events:
|
|
49
|
+
* - "scene-loading": Dispatched when a loading operation starts.
|
|
50
|
+
* - "scene-loaded": Dispatched when a loading operation completes.
|
|
51
|
+
* - "scene-setting-options": Dispatched when viewer options are being set.
|
|
52
|
+
* - "scene-set-options": Dispatched when viewer options have been set.
|
|
53
|
+
*
|
|
54
|
+
* Notes:
|
|
55
|
+
* - Internal state and heavy initialization are handled in connectedCallback.
|
|
56
|
+
* - Designed for extensibility and integration in product configurators and visualization tools.
|
|
57
|
+
* - All resource management and rendering operations are performed asynchronously for performance.
|
|
58
|
+
*/
|
|
59
|
+
export class PrefViewer3D extends HTMLElement {
|
|
60
|
+
// State flags
|
|
61
|
+
#isInitialized = false;
|
|
62
|
+
#isLoaded = false;
|
|
63
|
+
#isVisible = false;
|
|
64
|
+
|
|
65
|
+
#wrapper = null;
|
|
66
|
+
#canvas = null;
|
|
67
|
+
|
|
68
|
+
#data = null; // Component data
|
|
69
|
+
|
|
70
|
+
#babylonJSController = null; // BabylonJSController instance
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a new instance of the 3D viewer component.
|
|
74
|
+
* The constructor only calls super(); heavy initialization happens in connectedCallback.
|
|
75
|
+
*/
|
|
76
|
+
constructor() {
|
|
77
|
+
super();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Attributes observed by the custom element.
|
|
82
|
+
* @returns {string[]} Array of attribute names that trigger attributeChangedCallback.
|
|
83
|
+
*/
|
|
84
|
+
static get observedAttributes() {
|
|
85
|
+
return ["show-model", "show-scene", "visible"];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Handles changes to observed attributes and updates the component state accordingly.
|
|
90
|
+
* @param {string} name - The name of the changed attribute.
|
|
91
|
+
* @param {string|null} _old - The previous value of the attribute.
|
|
92
|
+
* @param {string|null} value - The new value of the attribute.
|
|
93
|
+
* @returns {void}
|
|
94
|
+
* @description
|
|
95
|
+
* - "show-model": shows or hides the model according to the attribute value ("true"/"false").
|
|
96
|
+
* - "show-scene": shows or hides the scene according to the attribute value ("true"/"false").
|
|
97
|
+
* - "visible": shows or hides the component according to the attribute value ("true"/"false").
|
|
98
|
+
*/
|
|
99
|
+
attributeChangedCallback(name, _old, value) {
|
|
100
|
+
switch (name) {
|
|
101
|
+
case "show-model":
|
|
102
|
+
if (_old === value) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const showModel = value.toLowerCase?.() === "true";
|
|
106
|
+
showModel ? this.showModel() : this.hideModel();
|
|
107
|
+
break;
|
|
108
|
+
case "show-scene":
|
|
109
|
+
if (_old === value) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const showScene = value.toLowerCase?.() === "true";
|
|
113
|
+
showScene ? this.showEnvironment() : this.hideEnvironment();
|
|
114
|
+
break;
|
|
115
|
+
case "visible":
|
|
116
|
+
if (_old === value) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (value === "true" && this.#isVisible === false) {
|
|
120
|
+
this.show();
|
|
121
|
+
} else if (value === "false" && this.#isVisible === true) {
|
|
122
|
+
this.hide();
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Called when the element is added to the DOM.
|
|
130
|
+
* Creates the DOM structure, sets initial visibility, initializes component data, and sets up the BabylonJSController.
|
|
131
|
+
* Performs heavy initialization tasks required for the 3D viewer.
|
|
132
|
+
* @returns {void}
|
|
133
|
+
*/
|
|
134
|
+
connectedCallback() {
|
|
135
|
+
this.#createDOMElements();
|
|
136
|
+
this.#setInitialVisibility();
|
|
137
|
+
this.#initializeData();
|
|
138
|
+
this.#initializeBabylonJS();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Called when the element is removed from the DOM.
|
|
143
|
+
* Disables the BabylonJSController to clean up resources and event listeners associated with the 3D viewer.
|
|
144
|
+
* @returns {void}
|
|
145
|
+
*/
|
|
146
|
+
disconnectedCallback() {
|
|
147
|
+
if (this.#babylonJSController) {
|
|
148
|
+
this.#babylonJSController.disable();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create DOM structure and basic styles used by the component.
|
|
154
|
+
* @private
|
|
155
|
+
* @returns {void}
|
|
156
|
+
*/
|
|
157
|
+
#createDOMElements() {
|
|
158
|
+
this.#wrapper = document.createElement("div");
|
|
159
|
+
this.append(this.#wrapper);
|
|
160
|
+
this.#canvas = document.createElement("canvas");
|
|
161
|
+
this.#wrapper.appendChild(this.#canvas);
|
|
162
|
+
const style = document.createElement("style");
|
|
163
|
+
style.textContent = `@import "/dist/css/pref-viewer-3d.css";`;
|
|
164
|
+
this.append(style);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Set initial visibility based on inline style and the visible attribute.
|
|
169
|
+
* @private
|
|
170
|
+
* @returns {void}
|
|
171
|
+
* @description If the "visible" attribute is not present, defaults to visible.
|
|
172
|
+
*/
|
|
173
|
+
#setInitialVisibility() {
|
|
174
|
+
const visible = !this.hasAttribute("visible") || this.getAttribute("visible") === "true";
|
|
175
|
+
visible ? this.show() : this.hide();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Initializes the internal data structure for containers and options used by the 3D viewer.
|
|
180
|
+
* Sets up default states for model, environment, materials, camera, and material options.
|
|
181
|
+
* @private
|
|
182
|
+
* @returns {void}
|
|
183
|
+
*/
|
|
184
|
+
#initializeData() {
|
|
185
|
+
this.#data = {
|
|
186
|
+
containers: {
|
|
187
|
+
model: new ContainerData("model"),
|
|
188
|
+
environment: new ContainerData("environment"),
|
|
189
|
+
materials: new ContainerData("materials"),
|
|
190
|
+
},
|
|
191
|
+
options: {
|
|
192
|
+
camera: new CameraData("activeCamera", null, true),
|
|
193
|
+
materials: {
|
|
194
|
+
innerWall: new MaterialData("innerWall", undefined, undefined, ["innerWall"]),
|
|
195
|
+
outerWall: new MaterialData("outerWall", undefined, undefined, ["outerWall"]),
|
|
196
|
+
innerFloor: new MaterialData("innerFloor", undefined, undefined, ["innerFloor"]),
|
|
197
|
+
outerFloor: new MaterialData("outerFloor", undefined, undefined, ["outerFloor"]),
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Initializes the BabylonJSController and enables the Babylon.js engine for rendering.
|
|
205
|
+
* @private
|
|
206
|
+
* @returns {void}
|
|
207
|
+
*/
|
|
208
|
+
#initializeBabylonJS() {
|
|
209
|
+
this.#babylonJSController = new BabylonJSController(this.#canvas, this.#data.containers, this.#data.options);
|
|
210
|
+
this.#babylonJSController.enable();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Resets update flags for all containers and material/camera options after loading or setting options.
|
|
215
|
+
* @private
|
|
216
|
+
* @returns {void}
|
|
217
|
+
*/
|
|
218
|
+
#resetUpdateFlags() {
|
|
219
|
+
Object.values(this.#data.containers).forEach((container) => container.reset());
|
|
220
|
+
Object.values(this.#data.options.materials).forEach((material) => material.reset());
|
|
221
|
+
this.#data.options.camera.reset();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Checks if any container (model, environment, materials) needs to be updated based on the provided config.
|
|
226
|
+
* Sets pending state for containers if storage or visibility changes are detected.
|
|
227
|
+
* @private
|
|
228
|
+
* @param {object} config - Configuration object containing model, scene, and materials info.
|
|
229
|
+
* @returns {void}
|
|
230
|
+
*/
|
|
231
|
+
#checkNeedToUpdateContainers(config) {
|
|
232
|
+
const modelState = this.#data.containers.model;
|
|
233
|
+
const modelHasStorage = !!config.model?.storage;
|
|
234
|
+
const modelNeedToChangeVisibility = config.model?.visible !== undefined && config.model?.visible !== modelState.show;
|
|
235
|
+
if (modelHasStorage) {
|
|
236
|
+
modelState.setPending(modelHasStorage ? config.model.storage : null, modelNeedToChangeVisibility ? config.model.visible : undefined);
|
|
237
|
+
} else if (modelNeedToChangeVisibility) {
|
|
238
|
+
modelState.show = config.model.visible;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const environmentState = this.#data.containers.environment;
|
|
242
|
+
const environmentHasStorage = !!config.scene?.storage;
|
|
243
|
+
const environmentNeedToChangeVisibility = config.scene?.visible !== undefined && config.scene?.visible !== environmentState.show;
|
|
244
|
+
if (environmentHasStorage) {
|
|
245
|
+
environmentState.setPending(environmentHasStorage ? config.scene.storage : null, environmentNeedToChangeVisibility ? config.scene.visible : undefined);
|
|
246
|
+
} else if (environmentNeedToChangeVisibility) {
|
|
247
|
+
environmentState.show = config.scene.visible;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Materials container. Visibility is not established for this container because it does not contain geometry.
|
|
251
|
+
const materialsState = this.#data.containers.materials;
|
|
252
|
+
const materialsHasStorage = !!config.materials?.storage;
|
|
253
|
+
if (materialsHasStorage) {
|
|
254
|
+
materialsState.setPending(materialsHasStorage ? config.materials.storage : null, null);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Checks if the camera option needs to be updated based on the provided options.
|
|
260
|
+
* Sets pending state for the camera if a change is detected.
|
|
261
|
+
* @private
|
|
262
|
+
* @param {object} options - Options object containing camera info.
|
|
263
|
+
* @returns {boolean} True if camera needs update, false otherwise.
|
|
264
|
+
*/
|
|
265
|
+
#checkNeedToUpdateCamera(options) {
|
|
266
|
+
if (!options || options.camera === undefined || options.camera === "") {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
const cameraState = this.#data.options.camera;
|
|
270
|
+
const needUpdate = options.camera !== cameraState.value;
|
|
271
|
+
if (needUpdate) {
|
|
272
|
+
cameraState.setPending(options.camera);
|
|
273
|
+
}
|
|
274
|
+
return needUpdate;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Checks if any material option needs to be updated based on the provided options.
|
|
279
|
+
* Sets pending state for materials if changes are detected.
|
|
280
|
+
* @private
|
|
281
|
+
* @param {object} options - Options object containing material info.
|
|
282
|
+
* @returns {boolean} True if any material needs update, false otherwise.
|
|
283
|
+
*/
|
|
284
|
+
#checkNeedToUpdateMaterials(options) {
|
|
285
|
+
if (!options) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
let someNeedUpdate = false;
|
|
289
|
+
Object.keys(this.#data.options.materials).forEach((material) => {
|
|
290
|
+
const key = `${material}Material`;
|
|
291
|
+
const materialState = this.#data.options.materials[material];
|
|
292
|
+
const previousValue = materialState.value;
|
|
293
|
+
const incomingValue = options[key];
|
|
294
|
+
const needUpdate = !!incomingValue && incomingValue !== previousValue;
|
|
295
|
+
if (needUpdate) {
|
|
296
|
+
materialState.setPending(incomingValue);
|
|
297
|
+
}
|
|
298
|
+
someNeedUpdate = someNeedUpdate || needUpdate;
|
|
299
|
+
});
|
|
300
|
+
return someNeedUpdate;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Dispatches a "prefviewer3d-loading" event and updates loading state attributes.
|
|
305
|
+
* Used internally when a loading operation starts.
|
|
306
|
+
* @private
|
|
307
|
+
* @returns {void}
|
|
308
|
+
*/
|
|
309
|
+
#onLoading() {
|
|
310
|
+
const customEventOptions = {
|
|
311
|
+
bubbles: true, // indicates whether the event bubbles up through the DOM tree or not
|
|
312
|
+
cancelable: true, // indicates whether the event can be canceled, and therefore prevented as if the event never happened
|
|
313
|
+
composed: false, // indicates whether or not the event will propagate across the shadow DOM boundary into the standard DOM
|
|
314
|
+
};
|
|
315
|
+
this.dispatchEvent(new CustomEvent("scene-loading", customEventOptions));
|
|
316
|
+
|
|
317
|
+
this.removeAttribute("loaded");
|
|
318
|
+
this.setAttribute("loading", "");
|
|
319
|
+
|
|
320
|
+
if (this.#isLoaded === true) {
|
|
321
|
+
this.#isLoaded = false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Dispatches a "prefviewer3d-loaded" event with details about what was loaded and updates state attributes.
|
|
327
|
+
* Used internally when a loading operation completes.
|
|
328
|
+
* @private
|
|
329
|
+
* @returns {object} Detail object describing what was tried and what succeeded.
|
|
330
|
+
*/
|
|
331
|
+
#onLoaded() {
|
|
332
|
+
const toLoadDetail = {
|
|
333
|
+
container_model: this.#data.containers.model.isPending,
|
|
334
|
+
container_scene: this.#data.containers.environment.isPending,
|
|
335
|
+
container_materials: this.#data.containers.materials.isPending,
|
|
336
|
+
options_camera: this.#data.options.camera.isPending,
|
|
337
|
+
options_material_innerWall: this.#data.options.materials.innerWall.isPending,
|
|
338
|
+
options_material_outerWall: this.#data.options.materials.outerWall.isPending,
|
|
339
|
+
options_material_innerFloor: this.#data.options.materials.innerFloor.isPending,
|
|
340
|
+
options_material_outerFloor: this.#data.options.materials.outerFloor.isPending,
|
|
341
|
+
};
|
|
342
|
+
const loadedDetail = {
|
|
343
|
+
container_model: this.#data.containers.model.isSuccess,
|
|
344
|
+
container_scene: this.#data.containers.environment.isSuccess,
|
|
345
|
+
container_materials: this.#data.containers.materials.isSuccess,
|
|
346
|
+
options_camera: this.#data.options.camera.isSuccess,
|
|
347
|
+
options_material_innerWall: this.#data.options.materials.innerWall.isSuccess,
|
|
348
|
+
options_material_outerWall: this.#data.options.materials.outerWall.isSuccess,
|
|
349
|
+
options_material_innerFloor: this.#data.options.materials.innerFloor.isSuccess,
|
|
350
|
+
options_material_outerFloor: this.#data.options.materials.outerFloor.isSuccess,
|
|
351
|
+
};
|
|
352
|
+
const detail = {
|
|
353
|
+
tried: toLoadDetail,
|
|
354
|
+
success: loadedDetail,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const customEventOptions = {
|
|
358
|
+
bubbles: true,
|
|
359
|
+
cancelable: true,
|
|
360
|
+
composed: false,
|
|
361
|
+
detail: detail,
|
|
362
|
+
};
|
|
363
|
+
this.dispatchEvent(new CustomEvent("scene-loaded", customEventOptions));
|
|
364
|
+
|
|
365
|
+
this.removeAttribute("loading");
|
|
366
|
+
this.setAttribute("loaded", "");
|
|
367
|
+
|
|
368
|
+
this.#resetUpdateFlags();
|
|
369
|
+
this.#isLoaded = true;
|
|
370
|
+
|
|
371
|
+
return detail;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Dispatches a "prefviewer3d-setting-options" event and updates loading state attributes.
|
|
376
|
+
* Used internally when options are being set.
|
|
377
|
+
* @private
|
|
378
|
+
* @returns {void}
|
|
379
|
+
*/
|
|
380
|
+
#onSettingOptions() {
|
|
381
|
+
const customEventOptions = {
|
|
382
|
+
bubbles: true,
|
|
383
|
+
cancelable: true,
|
|
384
|
+
composed: false,
|
|
385
|
+
};
|
|
386
|
+
this.dispatchEvent(new CustomEvent("scene-setting-options", customEventOptions));
|
|
387
|
+
|
|
388
|
+
this.removeAttribute("loaded");
|
|
389
|
+
this.setAttribute("loading", "");
|
|
390
|
+
|
|
391
|
+
if (this.#isLoaded === true) {
|
|
392
|
+
this.#isLoaded = false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Dispatches a "prefviewer3d-loaded" event with details about what options were set and updates state attributes.
|
|
398
|
+
* Used internally when options have been set.
|
|
399
|
+
* @private
|
|
400
|
+
* @returns {object} Detail object describing what was tried and what succeeded.
|
|
401
|
+
*/
|
|
402
|
+
#onSetOptions() {
|
|
403
|
+
const toSetDetail = {
|
|
404
|
+
camera: this.#data.options.camera.isPending,
|
|
405
|
+
innerWallMaterial: this.#data.options.materials.innerWall.isPending,
|
|
406
|
+
outerWallMaterial: this.#data.options.materials.outerWall.isPending,
|
|
407
|
+
innerFloorMaterial: this.#data.options.materials.innerFloor.isPending,
|
|
408
|
+
outerFloorMaterial: this.#data.options.materials.outerFloor.isPending,
|
|
409
|
+
};
|
|
410
|
+
const settedDetail = {
|
|
411
|
+
camera: this.#data.options.camera.isSuccess,
|
|
412
|
+
innerWallMaterial: this.#data.options.materials.innerWall.isSuccess,
|
|
413
|
+
outerWallMaterial: this.#data.options.materials.outerWall.isSuccess,
|
|
414
|
+
innerFloorMaterial: this.#data.options.materials.innerFloor.isSuccess,
|
|
415
|
+
outerFloorMaterial: this.#data.options.materials.outerFloor.isSuccess,
|
|
416
|
+
};
|
|
417
|
+
const detail = {
|
|
418
|
+
tried: toSetDetail,
|
|
419
|
+
success: settedDetail,
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const customEventOptions = {
|
|
423
|
+
bubbles: true,
|
|
424
|
+
cancelable: true,
|
|
425
|
+
composed: false,
|
|
426
|
+
detail: detail,
|
|
427
|
+
};
|
|
428
|
+
this.dispatchEvent(new CustomEvent("scene-set-options", customEventOptions));
|
|
429
|
+
|
|
430
|
+
this.removeAttribute("loading");
|
|
431
|
+
this.setAttribute("loaded", "");
|
|
432
|
+
|
|
433
|
+
this.#resetUpdateFlags();
|
|
434
|
+
this.#isLoaded = true;
|
|
435
|
+
|
|
436
|
+
return detail;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* ---------------------------
|
|
441
|
+
* Public methods
|
|
442
|
+
* ---------------------------
|
|
443
|
+
*/
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Hides the 3D viewer component by updating its visibility state and DOM attribute.
|
|
447
|
+
* @public
|
|
448
|
+
* @returns {void}
|
|
449
|
+
*/
|
|
450
|
+
hide() {
|
|
451
|
+
this.#isVisible = false;
|
|
452
|
+
this.setAttribute("visible", "false");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Shows the 3D viewer component by updating its visibility state and DOM attribute.
|
|
457
|
+
* @public
|
|
458
|
+
* @returns {void}
|
|
459
|
+
*/
|
|
460
|
+
show() {
|
|
461
|
+
this.#isVisible = true;
|
|
462
|
+
this.setAttribute("visible", "true");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Loads the provided configuration into the 3D viewer.
|
|
467
|
+
* Updates containers and options, triggers loading events, and returns the loading result.
|
|
468
|
+
* @public
|
|
469
|
+
* @param {object} config - Configuration object containing containers and options.
|
|
470
|
+
* @returns {Promise<object>} Resolves with an object containing success and error status and loading details.
|
|
471
|
+
*/
|
|
472
|
+
async load(config) {
|
|
473
|
+
if (!this.#babylonJSController) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
this.#onLoading();
|
|
478
|
+
|
|
479
|
+
// Containers
|
|
480
|
+
this.#checkNeedToUpdateContainers(config);
|
|
481
|
+
|
|
482
|
+
// Options
|
|
483
|
+
if (config.options) {
|
|
484
|
+
this.#checkNeedToUpdateCamera(config.options);
|
|
485
|
+
this.#checkNeedToUpdateMaterials(config.options);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const loadDetail = await this.#babylonJSController.load();
|
|
489
|
+
|
|
490
|
+
return { ...loadDetail, load: this.#onLoaded() };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Sets viewer options such as camera and materials.
|
|
495
|
+
* Updates internal states, triggers option setting events, and returns the result.
|
|
496
|
+
* @public
|
|
497
|
+
* @param {object} options - Options object containing camera and material settings.
|
|
498
|
+
* @returns {object} Object containing success status and details of set options.
|
|
499
|
+
*/
|
|
500
|
+
setOptions(options) {
|
|
501
|
+
if (!this.#babylonJSController) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
this.#onSettingOptions();
|
|
506
|
+
|
|
507
|
+
let someSetted = false;
|
|
508
|
+
if (this.#checkNeedToUpdateCamera(options)) {
|
|
509
|
+
someSetted = someSetted || this.#babylonJSController.setCameraOptions();
|
|
510
|
+
}
|
|
511
|
+
if (this.#checkNeedToUpdateMaterials(options)) {
|
|
512
|
+
someSetted = someSetted || this.#babylonJSController.setMaterialOptions();
|
|
513
|
+
}
|
|
514
|
+
const detail = this.#onSetOptions();
|
|
515
|
+
return { success: someSetted, detail: detail };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Shows the 3D model within the viewer.
|
|
520
|
+
* @public
|
|
521
|
+
* @returns {void}
|
|
522
|
+
*/
|
|
523
|
+
showModel() {
|
|
524
|
+
if (!this.#babylonJSController) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
this.#babylonJSController.setContainerVisibility("model", true);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Hides the 3D model within the viewer.
|
|
532
|
+
* @public
|
|
533
|
+
* @returns {void}
|
|
534
|
+
*/
|
|
535
|
+
hideModel() {
|
|
536
|
+
if (!this.#babylonJSController) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
this.#babylonJSController.setContainerVisibility("model", false);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Shows the 3D environment/scene within the viewer.
|
|
544
|
+
* @public
|
|
545
|
+
* @returns {void}
|
|
546
|
+
*/
|
|
547
|
+
showEnvironment() {
|
|
548
|
+
if (!this.#babylonJSController) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
this.#babylonJSController.setContainerVisibility("environment", true);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Hides the 3D environment/scene within the viewer.
|
|
556
|
+
* @public
|
|
557
|
+
* @returns {void}
|
|
558
|
+
*/
|
|
559
|
+
hideEnvironment() {
|
|
560
|
+
if (!this.#babylonJSController) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
this.#babylonJSController.setContainerVisibility("environment", false);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Downloads the current 3D model as a GLB file.
|
|
568
|
+
* @public
|
|
569
|
+
* @returns {void}
|
|
570
|
+
*/
|
|
571
|
+
downloadModelGLB() {
|
|
572
|
+
if (!this.#babylonJSController) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
this.#babylonJSController.downloadGLB(1);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Downloads the current 3D model as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
580
|
+
* @public
|
|
581
|
+
* @returns {void}
|
|
582
|
+
*/
|
|
583
|
+
downloadModelGLTF() {
|
|
584
|
+
if (!this.#babylonJSController) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
this.#babylonJSController.downloadGLTF(1);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Downloads the current 3D model as a USDZ file.
|
|
592
|
+
* @public
|
|
593
|
+
* @returns {void}
|
|
594
|
+
*/
|
|
595
|
+
downloadModelUSDZ() {
|
|
596
|
+
if (!this.#babylonJSController) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
this.#babylonJSController.downloadUSDZ(1);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Downloads the current 3D complete scene (model and environment) as a GLB file.
|
|
604
|
+
* @public
|
|
605
|
+
* @returns {void}
|
|
606
|
+
*/
|
|
607
|
+
downloadModelAndSceneGLB() {
|
|
608
|
+
if (!this.#babylonJSController) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
this.#babylonJSController.downloadModelGLB(0);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Downloads the current 3D complete scene (model and environment) as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
616
|
+
* @public
|
|
617
|
+
* @returns {void}
|
|
618
|
+
*/
|
|
619
|
+
downloadModelAndSceneGLTF() {
|
|
620
|
+
if (!this.#babylonJSController) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
this.#babylonJSController.downloadGLTF(0);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Downloads the current 3D complete scene (model and environment) as a USDZ file.
|
|
628
|
+
* @public
|
|
629
|
+
* @returns {void}
|
|
630
|
+
*/
|
|
631
|
+
downloadModelAndSceneUSDZ() {
|
|
632
|
+
if (!this.#babylonJSController) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
this.#babylonJSController.downloadUSDZ(0);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Downloads the current 3D environment as a GLB file.
|
|
640
|
+
* @public
|
|
641
|
+
* @returns {void}
|
|
642
|
+
*/
|
|
643
|
+
downloadSceneGLB() {
|
|
644
|
+
if (!this.#babylonJSController) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
this.#babylonJSController.downloadGLB(2);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Downloads the current 3D environment as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
652
|
+
* @public
|
|
653
|
+
* @returns {void}
|
|
654
|
+
*/
|
|
655
|
+
downloadSceneGLTF() {
|
|
656
|
+
if (!this.#babylonJSController) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.#babylonJSController.downloadGLTF(2);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Downloads the current 3D environment as a USDZ file.
|
|
664
|
+
* @public
|
|
665
|
+
* @returns {void}
|
|
666
|
+
*/
|
|
667
|
+
downloadSceneUSDZ() {
|
|
668
|
+
if (!this.#babylonJSController) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
this.#babylonJSController.downloadUSDZ(2);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Indicates whether the component has completed its initialization.
|
|
676
|
+
* @public
|
|
677
|
+
* @returns {boolean} True if the component is initialized; otherwise, false.
|
|
678
|
+
*/
|
|
679
|
+
get isInitialized() {
|
|
680
|
+
return this.#isInitialized;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Indicates whether the GLTF/GLB content is loaded and ready.
|
|
685
|
+
* @public
|
|
686
|
+
* @returns {boolean} True if the GLTF/GLB is loaded; otherwise, false.
|
|
687
|
+
*/
|
|
688
|
+
get isLoaded() {
|
|
689
|
+
return this.#isLoaded;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Indicates whether the component is currently visible.
|
|
694
|
+
* @public
|
|
695
|
+
* @returns {boolean} True if the component is visible; otherwise, false.
|
|
696
|
+
*/
|
|
697
|
+
get isVisible() {
|
|
698
|
+
return this.#isVisible;
|
|
699
|
+
}
|
|
700
|
+
}
|