@preference-sl/pref-viewer 2.12.0-beta.2 → 2.12.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/babylonjs-controller.js +346 -83
- package/src/pref-viewer-3d-data.js +50 -0
- package/src/pref-viewer-3d.js +63 -3
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArcRotateCamera, AssetContainer, Camera, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointerEventTypes, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager, WebXRState } from "@babylonjs/core";
|
|
1
|
+
import { ArcRotateCamera, AssetContainer, Camera, Color4, DefaultRenderingPipeline, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PBRMaterial, PointerEventTypes, PointLight, Scene, ShadowGenerator, SpotLight, SSAORenderingPipeline, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager, WebXRState } from "@babylonjs/core";
|
|
2
2
|
import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression.js";
|
|
3
3
|
import "@babylonjs/loaders";
|
|
4
4
|
import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression.js";
|
|
@@ -38,15 +38,17 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
38
38
|
* - Disable rendering: controller.disable();
|
|
39
39
|
*
|
|
40
40
|
* Public Methods:
|
|
41
|
-
* -
|
|
42
|
-
* -
|
|
43
|
-
* -
|
|
44
|
-
* -
|
|
45
|
-
* -
|
|
46
|
-
* -
|
|
47
|
-
* -
|
|
48
|
-
* -
|
|
49
|
-
* -
|
|
41
|
+
* - constructor(canvas, containers, options): Creates the controller, wires container state, and stores runtime options.
|
|
42
|
+
* - enable(): Boots the Babylon.js engine, scene, camera, baseline lights, XR support, and the render loop.
|
|
43
|
+
* - disable(): Stops rendering and disposes the engine, lights, XR experience, and observers.
|
|
44
|
+
* - load(): Reloads every pending asset container, re-applies options, and resolves with the loading summary { success, error }.
|
|
45
|
+
* - setCameraOptions(): Applies the pending camera selection, reinstalls dependent pipelines, and restarts rendering safely.
|
|
46
|
+
* - setMaterialOptions(): Re-applies all configured material overrides across visible containers and restarts rendering.
|
|
47
|
+
* - setIBLOptions(): Pushes pending HDR/IBL updates, refreshes dependent effects, and resumes the render loop.
|
|
48
|
+
* - setContainerVisibility(name, show): Toggles model/environment containers, syncing wall/floor helpers and component attributes.
|
|
49
|
+
* - downloadGLB(content): Exports the selected scope (scene/model/environment) into a time-stamped GLB and triggers the download.
|
|
50
|
+
* - downloadGLTF(content): Generates a glTF + BIN + textures ZIP for the requested scope, adding metadata comments for traceability.
|
|
51
|
+
* - downloadUSDZ(content): Builds an Apple USDZ archive for the requested scope and downloads it via blob streaming.
|
|
50
52
|
*
|
|
51
53
|
* Private Methods (using ECMAScript private fields):
|
|
52
54
|
* - #bindHandlers(): Pre-binds reusable event handlers to preserve stable references.
|
|
@@ -56,9 +58,13 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
56
58
|
* - #createXRExperience(): Initializes WebXR AR experience.
|
|
57
59
|
* - #createCamera(): Creates and configures the main camera.
|
|
58
60
|
* - #createLights(): Creates and configures scene lights and shadows.
|
|
61
|
+
* - #initializeAmbientOcclussion(): Rebuilds the SSAO pipeline for the active camera.
|
|
62
|
+
* - #initializeVisualImprovements(): Reinstalls the default rendering pipeline (MSAA/FXAA/grain).
|
|
59
63
|
* - #initializeEnvironmentTexture(): Loads and sets the HDR environment texture.
|
|
60
64
|
* - #initializeIBLShadows(): Sets up IBL shadow pipeline and assigns meshes/materials.
|
|
61
65
|
* - #initializeShadows(): Sets up standard or IBL shadows for meshes.
|
|
66
|
+
* - #initializeDefaultLightShadows(): Configures soft shadows when no HDR environment exists.
|
|
67
|
+
* - #initializeEnvironmentShadows(): Rebuilds environment-provided shadow generators.
|
|
62
68
|
* - #setMaxSimultaneousLights(): Updates max simultaneous lights for all materials.
|
|
63
69
|
* - #onPointerObservable(info): Handles pointer events and dispatches to pointer/mouse handlers.
|
|
64
70
|
* - #onPointerUp(event, pickInfo): Handles pointer up events (e.g., right-click for animation menu).
|
|
@@ -73,6 +79,7 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
73
79
|
* - #setOptionsMaterial(optionMaterial): Applies a material option to relevant meshes.
|
|
74
80
|
* - #setOptions_Materials(): Applies all material options from configuration.
|
|
75
81
|
* - #setOptions_Camera(): Applies camera options from configuration.
|
|
82
|
+
* - #setOptions_IBL(): Applies pending HDR/IBL option updates and refreshes lights.
|
|
76
83
|
* - #findContainerByName(name): Finds a container by its name.
|
|
77
84
|
* - #addContainer(container, updateVisibility): Adds a container to the scene and updates visibility.
|
|
78
85
|
* - #removeContainer(container, updateVisibility): Removes a container from the scene and updates visibility.
|
|
@@ -85,6 +92,7 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
85
92
|
* - #startRender(): Starts the Babylon.js render loop.
|
|
86
93
|
* - #loadAssetContainer(container): Loads an asset container asynchronously.
|
|
87
94
|
* - #loadContainers(): Loads all asset containers and adds them to the scene.
|
|
95
|
+
* - #loadCameraDepentEffects(): Re-initializes camera-bound post-processes after reloads or option changes.
|
|
88
96
|
* - #checkModelMetadata(oldMetadata, newMetadata): Processes metadata changes after loading.
|
|
89
97
|
* - #checkInnerFloorTranslation(oldMetadata, newMetadata): Applies inner floor Y translations from metadata.
|
|
90
98
|
* - #translateNodeY(name, deltaY): Adjusts the Y position of a scene node.
|
|
@@ -107,7 +115,7 @@ export default class BabylonJSController {
|
|
|
107
115
|
#hemiLight = null;
|
|
108
116
|
#dirLight = null;
|
|
109
117
|
#cameraLight = null;
|
|
110
|
-
#shadowGen =
|
|
118
|
+
#shadowGen = [];
|
|
111
119
|
#XRExperience = null;
|
|
112
120
|
#canvasResizeObserver = null;
|
|
113
121
|
|
|
@@ -123,6 +131,8 @@ export default class BabylonJSController {
|
|
|
123
131
|
renderLoop: null,
|
|
124
132
|
};
|
|
125
133
|
|
|
134
|
+
#shadowsEnabled = false;
|
|
135
|
+
|
|
126
136
|
/**
|
|
127
137
|
* Constructs a new BabylonJSController instance.
|
|
128
138
|
* Initializes the canvas, asset containers, and options for the Babylon.js scene.
|
|
@@ -288,33 +298,151 @@ export default class BabylonJSController {
|
|
|
288
298
|
* @returns {void}
|
|
289
299
|
*/
|
|
290
300
|
#createLights() {
|
|
291
|
-
this.#
|
|
301
|
+
if (this.#options.ibl && this.#options.ibl.url) {
|
|
302
|
+
this.#hemiLight = this.#dirLight = this.#cameraLight = null;
|
|
303
|
+
this.#initializeEnvironmentTexture();
|
|
304
|
+
}
|
|
292
305
|
|
|
293
306
|
if (this.#scene.environmentTexture) {
|
|
294
307
|
return true;
|
|
295
308
|
}
|
|
296
309
|
|
|
297
310
|
// 1) Stronger ambient fill
|
|
298
|
-
this.#hemiLight = new HemisphericLight("
|
|
311
|
+
this.#hemiLight = new HemisphericLight("PrefViewerHemiLight", new Vector3(-10, 10, -10), this.#scene);
|
|
299
312
|
this.#hemiLight.intensity = 0.6;
|
|
300
313
|
|
|
301
314
|
// 2) Directional light from the front-right, angled slightly down
|
|
302
|
-
this.#dirLight = new DirectionalLight("
|
|
315
|
+
this.#dirLight = new DirectionalLight("PrefViewerDirLight", new Vector3(-10, 10, -10), this.#scene);
|
|
303
316
|
this.#dirLight.position = new Vector3(5, 4, 5); // light is IN FRONT + ABOVE + to the RIGHT
|
|
304
317
|
this.#dirLight.intensity = 0.6;
|
|
305
318
|
|
|
306
|
-
//
|
|
307
|
-
this.#
|
|
308
|
-
this.#shadowGen.useBlurExponentialShadowMap = true;
|
|
309
|
-
this.#shadowGen.blurKernel = 16;
|
|
310
|
-
this.#shadowGen.darkness = 0.5;
|
|
311
|
-
|
|
312
|
-
// 4) Camera‐attached headlight
|
|
313
|
-
this.#cameraLight = new PointLight("pl", this.#camera.position, this.#scene);
|
|
319
|
+
// 3) Camera‐attached headlight
|
|
320
|
+
this.#cameraLight = new PointLight("PrefViewerCameraLight", this.#camera.position, this.#scene);
|
|
314
321
|
this.#cameraLight.parent = this.#camera;
|
|
315
322
|
this.#cameraLight.intensity = 0.3;
|
|
316
323
|
}
|
|
317
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Rebuilds the SSAO post-process pipeline to inject screenspace ambient occlusion on the active camera.
|
|
327
|
+
* Disposes previous SSAO pipelines, instantiates a tuned `SSAORenderingPipeline`, and attaches it to the
|
|
328
|
+
* current camera so contact shadows enhance depth perception once assets reload or the camera changes.
|
|
329
|
+
* @private
|
|
330
|
+
* @returns {boolean} True if the SSAO pipeline is supported and enabled, otherwise false.
|
|
331
|
+
*/
|
|
332
|
+
#initializeAmbientOcclussion() {
|
|
333
|
+
if (!this.#scene || !this.#scene.postProcessRenderPipelineManager || this.#scene.activeCamera === null) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const pipelineName = "PrefViewerSSAORenderingPipeline";
|
|
338
|
+
|
|
339
|
+
const supportedPipelines = this.#scene.postProcessRenderPipelineManager.supportedPipelines;
|
|
340
|
+
|
|
341
|
+
if (!supportedPipelines) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const oldSsaoPipeline = supportedPipelines ? supportedPipelines.find((pipeline) => pipeline.name === pipelineName) : false;
|
|
346
|
+
|
|
347
|
+
if (oldSsaoPipeline) {
|
|
348
|
+
oldSsaoPipeline.dispose();
|
|
349
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const ssaoRatio = {
|
|
353
|
+
ssaoRatio: 0.5,
|
|
354
|
+
combineRatio: 1.0
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const ssaoPipeline = new SSAORenderingPipeline(pipelineName, this.#scene, ssaoRatio, [this.#scene.activeCamera]);
|
|
358
|
+
|
|
359
|
+
if (!ssaoPipeline){
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (ssaoPipeline.isSupported) {
|
|
364
|
+
ssaoPipeline.fallOff = 0.000001;
|
|
365
|
+
ssaoPipeline.area = 1;
|
|
366
|
+
ssaoPipeline.radius = 0.0001;
|
|
367
|
+
ssaoPipeline.totalStrength = 1;
|
|
368
|
+
ssaoPipeline.base = 0.6;
|
|
369
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
370
|
+
return true;
|
|
371
|
+
} else {
|
|
372
|
+
ssaoPipeline.dispose();
|
|
373
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Rebuilds the custom default rendering pipeline (MSAA, FXAA, film grain) for the active camera.
|
|
380
|
+
* Disposes any previous pipeline instance to avoid duplicates, then attaches a fresh
|
|
381
|
+
* `DefaultRenderingPipeline` with tuned settings for sharper anti-aliasing and subtle grain.
|
|
382
|
+
* @private
|
|
383
|
+
* @returns {boolean} True when the pipeline is supported and active, otherwise false.
|
|
384
|
+
* @see {@link https://doc.babylonjs.com/features/featuresDeepDive/postProcesses/defaultRenderingPipeline|Using the Default Rendering Pipeline | Babylon.js Documentation}
|
|
385
|
+
*/
|
|
386
|
+
#initializeVisualImprovements() {
|
|
387
|
+
if (!this.#scene || !this.#scene.postProcessRenderPipelineManager || this.#scene.activeCamera === null) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const pipelineName = "PrefViewerDefaultRenderingPipeline";
|
|
392
|
+
const supportedPipelines = this.#scene.postProcessRenderPipelineManager.supportedPipelines;
|
|
393
|
+
|
|
394
|
+
if (!supportedPipelines) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const oldDefaultPipeline = supportedPipelines ? supportedPipelines.find((pipeline) => pipeline.name === pipelineName) : false;
|
|
399
|
+
|
|
400
|
+
if (oldDefaultPipeline) {
|
|
401
|
+
oldDefaultPipeline.dispose();
|
|
402
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const defaultPipeline = new DefaultRenderingPipeline(pipelineName, true, this.#scene, [this.#scene.activeCamera], true);
|
|
406
|
+
|
|
407
|
+
if (!defaultPipeline){
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (defaultPipeline.isSupported) {
|
|
412
|
+
// MSAA - Multisample Anti-Aliasing
|
|
413
|
+
const caps = this.#scene.getEngine()?.getCaps?.() || {};
|
|
414
|
+
const maxSamples = typeof caps.maxMSAASamples === "number" ? caps.maxMSAASamples : 4;
|
|
415
|
+
defaultPipeline.samples = Math.max(1, Math.min(8, maxSamples));
|
|
416
|
+
|
|
417
|
+
// FXAA - Fast Approximate Anti-Aliasing
|
|
418
|
+
defaultPipeline.fxaaEnabled = true;
|
|
419
|
+
defaultPipeline.fxaa.samples = 8;
|
|
420
|
+
defaultPipeline.fxaa.adaptScaleToCurrentViewport = true;
|
|
421
|
+
if (defaultPipeline.fxaa.edgeThreshold !== undefined) {
|
|
422
|
+
defaultPipeline.fxaa.edgeThreshold = 0.125;
|
|
423
|
+
}
|
|
424
|
+
if (defaultPipeline.fxaa.edgeThresholdMin !== undefined) {
|
|
425
|
+
defaultPipeline.fxaa.edgeThresholdMin = 0.0625;
|
|
426
|
+
}
|
|
427
|
+
if (defaultPipeline.fxaa.subPixelQuality !== undefined) {
|
|
428
|
+
defaultPipeline.fxaa.subPixelQuality = 0.75;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Grain
|
|
432
|
+
defaultPipeline.grainEnabled = true;
|
|
433
|
+
defaultPipeline.grain.adaptScaleToCurrentViewport = true;
|
|
434
|
+
defaultPipeline.grain.animated = false;
|
|
435
|
+
defaultPipeline.grain.intensity = 3;
|
|
436
|
+
|
|
437
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
438
|
+
return true;
|
|
439
|
+
} else {
|
|
440
|
+
defaultPipeline.dispose();
|
|
441
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
318
446
|
/**
|
|
319
447
|
* Initializes the environment texture for the Babylon.js scene.
|
|
320
448
|
* Loads an HDR texture from a predefined URI and assigns it to the scene's environmentTexture property.
|
|
@@ -323,15 +451,9 @@ export default class BabylonJSController {
|
|
|
323
451
|
* @returns {boolean}
|
|
324
452
|
*/
|
|
325
453
|
#initializeEnvironmentTexture() {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
const hdrTextureURI = "../src/environments/noon_grass.hdr";
|
|
331
|
-
const hdrTexture = new HDRCubeTexture(hdrTextureURI, this.#scene, 128);
|
|
332
|
-
hdrTexture.gammaSpace = true;
|
|
333
|
-
hdrTexture._noMipmap = false;
|
|
334
|
-
hdrTexture.level = 2.0;
|
|
454
|
+
const hdrTextureURI = this.#options.ibl.url;
|
|
455
|
+
const hdrTexture = new HDRCubeTexture(hdrTextureURI, this.#scene, 128, false, false, false, true);
|
|
456
|
+
hdrTexture.level = this.#options.ibl.intensity;
|
|
335
457
|
this.#scene.environmentTexture = hdrTexture;
|
|
336
458
|
return true;
|
|
337
459
|
}
|
|
@@ -345,61 +467,141 @@ export default class BabylonJSController {
|
|
|
345
467
|
* @returns {void|false} Returns false if no environment texture is set; otherwise void.
|
|
346
468
|
*/
|
|
347
469
|
#initializeIBLShadows() {
|
|
348
|
-
if (!this.#scene
|
|
470
|
+
if (!this.#scene || !this.#scene.postProcessRenderPipelineManager || this.#scene.activeCamera === null) {
|
|
349
471
|
return false;
|
|
350
472
|
}
|
|
351
473
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
474
|
+
// if (!this.#scene.environmentTexture || !this.#scene.environmentTexture.isReady()) {
|
|
475
|
+
if (!this.#scene.environmentTexture) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
const pipelineName = "PrefViewerIblShadowsRenderPipeline";
|
|
479
|
+
const supportedPipelines = this.#scene.postProcessRenderPipelineManager.supportedPipelines;
|
|
480
|
+
|
|
481
|
+
if (!supportedPipelines) {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const oldIblShadowsRenderPipeline = supportedPipelines ? supportedPipelines.find((pipeline) => pipeline.name === pipelineName) : false;
|
|
486
|
+
|
|
487
|
+
if (oldIblShadowsRenderPipeline) {
|
|
488
|
+
oldIblShadowsRenderPipeline.dispose();
|
|
489
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const pipelineOptions = {
|
|
493
|
+
resolutionExp: 8, // Higher resolution for better shadow quality
|
|
494
|
+
sampleDirections: 4, // More sample directions for smoother shadows
|
|
495
|
+
ssShadowsEnabled: true,
|
|
496
|
+
shadowRemanence: 0.85,
|
|
497
|
+
triPlanarVoxelization: true,
|
|
498
|
+
shadowOpacity: 0.85,
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const iblShadowsRenderPipeline = new IblShadowsRenderPipeline(pipelineName, this.#scene, pipelineOptions, [this.#scene.activeCamera]);
|
|
502
|
+
|
|
503
|
+
if (!iblShadowsRenderPipeline) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (iblShadowsRenderPipeline.isSupported) {
|
|
375
508
|
// Disable all debug passes for performance
|
|
376
509
|
const pipelineProps = {
|
|
377
510
|
allowDebugPasses: false,
|
|
378
511
|
gbufferDebugEnabled: false,
|
|
379
512
|
importanceSamplingDebugEnabled: false,
|
|
380
513
|
voxelDebugEnabled: false,
|
|
381
|
-
voxelDebugDisplayMip:
|
|
514
|
+
voxelDebugDisplayMip: 1,
|
|
382
515
|
voxelDebugAxis: 0,
|
|
383
516
|
voxelTracingDebugEnabled: false,
|
|
384
517
|
spatialBlurPassDebugEnabled: false,
|
|
385
518
|
accumulationPassDebugEnabled: false,
|
|
386
519
|
};
|
|
387
|
-
Object.assign(pipeline, pipelineProps);
|
|
388
|
-
return pipeline;
|
|
389
|
-
};
|
|
390
520
|
|
|
391
|
-
|
|
521
|
+
Object.assign(iblShadowsRenderPipeline, pipelineProps);
|
|
522
|
+
|
|
523
|
+
this.#scene.meshes.forEach((mesh) => {
|
|
524
|
+
if (mesh.id.startsWith("__root__") || mesh.name === "hdri") {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
iblShadowsRenderPipeline.addShadowCastingMesh(mesh);
|
|
528
|
+
iblShadowsRenderPipeline.updateSceneBounds();
|
|
529
|
+
mesh.receiveShadows = true; // Not necessary for IBL shadows, but yes for standard shadows
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
this.#scene.materials.forEach((material) => {
|
|
533
|
+
if (material instanceof PBRMaterial) {
|
|
534
|
+
material.enableSpecularAntiAliasing = false;
|
|
535
|
+
}
|
|
536
|
+
iblShadowsRenderPipeline.addShadowReceivingMaterial(material);
|
|
537
|
+
});
|
|
392
538
|
|
|
539
|
+
iblShadowsRenderPipeline.updateVoxelization();
|
|
540
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
541
|
+
return true;
|
|
542
|
+
} else {
|
|
543
|
+
iblShadowsRenderPipeline.dispose();
|
|
544
|
+
this.#scene.postProcessRenderPipelineManager.update();
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Configures soft shadows for the built-in directional light used when no HDR environment is present.
|
|
551
|
+
* @private
|
|
552
|
+
* @returns {void}
|
|
553
|
+
*/
|
|
554
|
+
#initializeDefaultLightShadows() {
|
|
555
|
+
this.#shadowGen = [];
|
|
556
|
+
const shadowGenerator = new ShadowGenerator(1024, this.#dirLight);
|
|
557
|
+
shadowGenerator.useBlurExponentialShadowMap = true;
|
|
558
|
+
shadowGenerator.blurKernel = 16;
|
|
559
|
+
shadowGenerator.darkness = 0.5;
|
|
393
560
|
this.#scene.meshes.forEach((mesh) => {
|
|
394
|
-
if (mesh.id.startsWith("__root__")
|
|
561
|
+
if (mesh.id.startsWith("__root__")) {
|
|
395
562
|
return false;
|
|
396
563
|
}
|
|
397
|
-
|
|
398
|
-
|
|
564
|
+
if (mesh.name !== "hdri") {
|
|
565
|
+
shadowGenerator.addShadowCaster(mesh, true);
|
|
566
|
+
}
|
|
567
|
+
mesh.receiveShadows = true;
|
|
568
|
+
});
|
|
569
|
+
this.#shadowGen.push(shadowGenerator);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Rebuilds the shadow generators contributed by the environment container.
|
|
574
|
+
* Keeps only the generator bound to the built-in `PrefViewerDirLight` so its shadows persist,
|
|
575
|
+
* then adds generators for every directional or spot light coming from the environment asset container.
|
|
576
|
+
* @private
|
|
577
|
+
* @returns {void}
|
|
578
|
+
*/
|
|
579
|
+
#initializeEnvironmentShadows() {
|
|
580
|
+
this.#shadowGen = this.#shadowGen.filter((generator) => {
|
|
581
|
+
if (!generator || typeof generator.getLight !== "function") {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
return generator.getLight()?.name === "PrefViewerDirLight";
|
|
399
585
|
});
|
|
400
586
|
|
|
401
|
-
this.#
|
|
402
|
-
|
|
587
|
+
this.#containers.environment?.AssetContainer?.lights.forEach((light) => {
|
|
588
|
+
// Only shadows for DirectionalLight and SpotLight types
|
|
589
|
+
if (!(light instanceof DirectionalLight || light instanceof SpotLight)) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const shadowGenerator = new ShadowGenerator(1024, light);
|
|
593
|
+
shadowGenerator.useBlurExponentialShadowMap = true;
|
|
594
|
+
shadowGenerator.blurKernel = 16;
|
|
595
|
+
shadowGenerator.darkness = 0.5;
|
|
596
|
+
this.#scene.meshes.forEach((mesh) => {
|
|
597
|
+
if (mesh.id.startsWith("__root__")) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
if (mesh.name !== "hdri") {
|
|
601
|
+
shadowGenerator.addShadowCaster(mesh, true);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
this.#shadowGen.push(shadowGenerator);
|
|
403
605
|
});
|
|
404
606
|
}
|
|
405
607
|
|
|
@@ -412,20 +614,24 @@ export default class BabylonJSController {
|
|
|
412
614
|
* Otherwise, sets up shadow casting and receiving for all relevant meshes using the shadow generator.
|
|
413
615
|
*/
|
|
414
616
|
#initializeShadows() {
|
|
415
|
-
if (this.#
|
|
416
|
-
|
|
417
|
-
return true;
|
|
617
|
+
if (!this.#shadowsEnabled) {
|
|
618
|
+
return false;
|
|
418
619
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
620
|
+
if (this.#scene.environmentTexture) {
|
|
621
|
+
if (this.#options.ibl.shadows) {
|
|
622
|
+
if (this.#scene.environmentTexture.isReady()) {
|
|
623
|
+
this.#initializeIBLShadows();
|
|
624
|
+
} else {
|
|
625
|
+
const self = this;
|
|
626
|
+
this.#scene.environmentTexture.onLoadObservable.addOnce(() => {
|
|
627
|
+
self.#initializeIBLShadows();
|
|
628
|
+
});
|
|
629
|
+
}
|
|
427
630
|
}
|
|
428
|
-
}
|
|
631
|
+
} else {
|
|
632
|
+
this.#initializeDefaultLightShadows();
|
|
633
|
+
}
|
|
634
|
+
this.#initializeEnvironmentShadows();
|
|
429
635
|
}
|
|
430
636
|
|
|
431
637
|
/**
|
|
@@ -435,7 +641,7 @@ export default class BabylonJSController {
|
|
|
435
641
|
* @returns {void}
|
|
436
642
|
*/
|
|
437
643
|
#setMaxSimultaneousLights() {
|
|
438
|
-
let lightsNumber = 1; //
|
|
644
|
+
let lightsNumber = 1; // At least one light coming from the environment texture contribution
|
|
439
645
|
this.#scene.lights.forEach((light) => {
|
|
440
646
|
if (light.isEnabled()) {
|
|
441
647
|
++lightsNumber;
|
|
@@ -543,7 +749,7 @@ export default class BabylonJSController {
|
|
|
543
749
|
this.#engine.dispose();
|
|
544
750
|
this.#engine = this.#scene = this.#camera = null;
|
|
545
751
|
this.#hemiLight = this.#dirLight = this.#cameraLight = null;
|
|
546
|
-
this.#shadowGen =
|
|
752
|
+
this.#shadowGen = [];
|
|
547
753
|
}
|
|
548
754
|
|
|
549
755
|
/**
|
|
@@ -689,7 +895,7 @@ export default class BabylonJSController {
|
|
|
689
895
|
const modelContainer = this.#containers.model;
|
|
690
896
|
const environmentContainer = this.#containers.environment;
|
|
691
897
|
|
|
692
|
-
if (!cameraState.isPending && !modelContainer.state.
|
|
898
|
+
if (!cameraState.isPending && !modelContainer.state.isSuccess && !environmentContainer.state.isSuccess) {
|
|
693
899
|
return false;
|
|
694
900
|
}
|
|
695
901
|
|
|
@@ -733,6 +939,7 @@ export default class BabylonJSController {
|
|
|
733
939
|
cameraState.setSuccess(true);
|
|
734
940
|
}
|
|
735
941
|
}
|
|
942
|
+
this.#scene.activeCamera?.detachControl();
|
|
736
943
|
if (!cameraState.locked && cameraState.value !== null) {
|
|
737
944
|
camera.attachControl(this.#canvas, true);
|
|
738
945
|
}
|
|
@@ -740,6 +947,21 @@ export default class BabylonJSController {
|
|
|
740
947
|
return true;
|
|
741
948
|
}
|
|
742
949
|
|
|
950
|
+
/**
|
|
951
|
+
* Applies pending image-based lighting (IBL) option updates.
|
|
952
|
+
* Marks the IBL state as successful, recreates lights so the new environment takes effect, and reports whether anything changed.
|
|
953
|
+
* @private
|
|
954
|
+
* @returns {boolean} True when lights were refreshed due to pending IBL changes, otherwise false.
|
|
955
|
+
*/
|
|
956
|
+
#setOptions_IBL() {
|
|
957
|
+
if (this.#options.ibl.isPending) {
|
|
958
|
+
this.#options.ibl.setSuccess(true);
|
|
959
|
+
this.#createLights();
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
|
|
743
965
|
/**
|
|
744
966
|
* Finds and returns the asset container object by its name.
|
|
745
967
|
* @private
|
|
@@ -915,8 +1137,9 @@ export default class BabylonJSController {
|
|
|
915
1137
|
if (!this.#gltfResolver) {
|
|
916
1138
|
this.#gltfResolver = new GLTFResolver();
|
|
917
1139
|
}
|
|
918
|
-
|
|
919
|
-
|
|
1140
|
+
//let sourceData = await this.#gltfResolver.getSource(container.state.update.storage, container.state.size, container.state.timeStamp);
|
|
1141
|
+
// TEMPORARY: We never pass 'size' or 'timeStamp' to always force a reload and avoid issues with the active camera in reloading assetContainers
|
|
1142
|
+
let sourceData = await this.#gltfResolver.getSource(container.state.update.storage, 0, null);
|
|
920
1143
|
if (!sourceData) {
|
|
921
1144
|
return [container, false];
|
|
922
1145
|
}
|
|
@@ -949,6 +1172,7 @@ export default class BabylonJSController {
|
|
|
949
1172
|
*/
|
|
950
1173
|
async #loadContainers() {
|
|
951
1174
|
this.#stopRender();
|
|
1175
|
+
this.#scene.postProcessRenderPipelineManager?.dispose();
|
|
952
1176
|
|
|
953
1177
|
let oldModelMetadata = { ...(this.#containers.model?.state?.metadata ?? {}) };
|
|
954
1178
|
let newModelMetadata = {};
|
|
@@ -986,6 +1210,7 @@ export default class BabylonJSController {
|
|
|
986
1210
|
|
|
987
1211
|
this.#setOptions_Materials();
|
|
988
1212
|
this.#setOptions_Camera();
|
|
1213
|
+
this.#setOptions_IBL();
|
|
989
1214
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
990
1215
|
detail.success = true;
|
|
991
1216
|
})
|
|
@@ -998,13 +1223,25 @@ export default class BabylonJSController {
|
|
|
998
1223
|
.finally(async () => {
|
|
999
1224
|
this.#checkModelMetadata(oldModelMetadata, newModelMetadata);
|
|
1000
1225
|
this.#setMaxSimultaneousLights();
|
|
1001
|
-
this.#
|
|
1226
|
+
this.#loadCameraDepentEffects();
|
|
1002
1227
|
this.#babylonJSAnimationController = new BabylonJSAnimationController(this.#scene);
|
|
1003
1228
|
this.#startRender();
|
|
1004
1229
|
});
|
|
1005
1230
|
return detail;
|
|
1006
1231
|
}
|
|
1007
1232
|
|
|
1233
|
+
/**
|
|
1234
|
+
* Reinstalls every camera-sensitive post-process (default pipeline, SSAO, shadows) after loads or option changes.
|
|
1235
|
+
* Ensures the active camera always owns fresh pipelines so render quality remains consistent across reload cycles.
|
|
1236
|
+
* @private
|
|
1237
|
+
* @returns {void}
|
|
1238
|
+
*/
|
|
1239
|
+
#loadCameraDepentEffects() {
|
|
1240
|
+
this.#initializeVisualImprovements();
|
|
1241
|
+
this.#initializeAmbientOcclussion();
|
|
1242
|
+
this.#initializeShadows();
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1008
1245
|
/**
|
|
1009
1246
|
* Checks and applies model metadata changes after asset loading.
|
|
1010
1247
|
* @private
|
|
@@ -1233,6 +1470,17 @@ export default class BabylonJSController {
|
|
|
1233
1470
|
this.#engine = new Engine(this.#canvas, true, { alpha: true, stencil: true, preserveDrawingBuffer: false });
|
|
1234
1471
|
this.#engine.disableUniformBuffers = true;
|
|
1235
1472
|
this.#scene = new Scene(this.#engine);
|
|
1473
|
+
|
|
1474
|
+
// Activate the rendering of geometry data into a G-buffer, essential for advanced effects like deferred shading,
|
|
1475
|
+
// SSAO, and Velocity-Texture-Animation (VAT), allowing for complex post-processing by separating rendering into
|
|
1476
|
+
// different buffers (depth, normals, velocity) for later use in shaders.
|
|
1477
|
+
const geometryBufferRenderer = this.#scene.enableGeometryBufferRenderer();
|
|
1478
|
+
if (geometryBufferRenderer) {
|
|
1479
|
+
geometryBufferRenderer.enableScreenspaceDepth = true;
|
|
1480
|
+
geometryBufferRenderer.enableDepth = false;
|
|
1481
|
+
geometryBufferRenderer.generateNormalsInWorldSpace = true;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1236
1484
|
this.#scene.clearColor = new Color4(1, 1, 1, 1);
|
|
1237
1485
|
this.#createCamera();
|
|
1238
1486
|
this.#createLights();
|
|
@@ -1276,6 +1524,7 @@ export default class BabylonJSController {
|
|
|
1276
1524
|
setCameraOptions() {
|
|
1277
1525
|
this.#stopRender();
|
|
1278
1526
|
const cameraOptionsSetted = this.#setOptions_Camera();
|
|
1527
|
+
this.#loadCameraDepentEffects();
|
|
1279
1528
|
this.#startRender();
|
|
1280
1529
|
return cameraOptionsSetted;
|
|
1281
1530
|
}
|
|
@@ -1293,6 +1542,20 @@ export default class BabylonJSController {
|
|
|
1293
1542
|
return materialsOptionsSetted;
|
|
1294
1543
|
}
|
|
1295
1544
|
|
|
1545
|
+
/**
|
|
1546
|
+
* Reapplies image-based lighting configuration (HDR URL, intensity, shadow mode).
|
|
1547
|
+
* Stops rendering, pushes pending IBL state into the scene, rebuilds camera-dependent effects, then resumes rendering.
|
|
1548
|
+
* @public
|
|
1549
|
+
* @returns {void}
|
|
1550
|
+
*/
|
|
1551
|
+
setIBLOptions() {
|
|
1552
|
+
this.#stopRender();
|
|
1553
|
+
const IBLOptionsSetted = this.#setOptions_IBL();
|
|
1554
|
+
this.#loadCameraDepentEffects();
|
|
1555
|
+
this.#startRender();
|
|
1556
|
+
return IBLOptionsSetted;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1296
1559
|
/**
|
|
1297
1560
|
* Sets the visibility of a container (model, environment, etc.) by name.
|
|
1298
1561
|
* Adds or removes the container from the scene and updates wall/floor visibility.
|
|
@@ -181,3 +181,53 @@ export class CameraData {
|
|
|
181
181
|
return this.update.success === true;
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* IBLData - Tracks configurable settings for image-based lighting assets (IBL/HDR environments).
|
|
187
|
+
*
|
|
188
|
+
* Responsibilities:
|
|
189
|
+
* - Stores the HDR url, intensity scalar, whether environment-provided shadows should render, and cache timestamp.
|
|
190
|
+
* - Exposes a lightweight pending/success state machine so UI flows know when a new IBL selection is being fetched.
|
|
191
|
+
* - Provides helpers to update values atomically via `setValues`, toggle pending state, and mark completion.
|
|
192
|
+
*
|
|
193
|
+
* Usage:
|
|
194
|
+
* - Instantiate with defaults: `const ibl = new IBLData();`
|
|
195
|
+
* - Call `setPending()` before kicking off an async download, `setValues()` as metadata streams in, and `setSuccess(true)` once loading finishes.
|
|
196
|
+
* - Inspect `isPending`/`isSuccess` to drive UI or re-render logic.
|
|
197
|
+
*/
|
|
198
|
+
export class IBLData {
|
|
199
|
+
constructor(url = null, intensity = 1.0, shadows = false, timeStamp = null) {
|
|
200
|
+
this.url = url;
|
|
201
|
+
this.intensity = intensity;
|
|
202
|
+
this.shadows = shadows;
|
|
203
|
+
this.timeStamp = timeStamp;
|
|
204
|
+
this.reset();
|
|
205
|
+
}
|
|
206
|
+
reset() {
|
|
207
|
+
this.pending = false;
|
|
208
|
+
this.success = false;
|
|
209
|
+
}
|
|
210
|
+
setValues(url, intensity, shadows, timeStamp) {
|
|
211
|
+
this.url = url !== undefined ? url : this.url;
|
|
212
|
+
this.intensity = intensity !== undefined ? intensity : this.intensity;
|
|
213
|
+
this.shadows = shadows !== undefined ? shadows : this.shadows;
|
|
214
|
+
this.timeStamp = timeStamp !== undefined ? timeStamp : this.timeStamp;
|
|
215
|
+
}
|
|
216
|
+
setSuccess(success = false) {
|
|
217
|
+
if (success) {
|
|
218
|
+
this.success = true;
|
|
219
|
+
} else {
|
|
220
|
+
this.success = false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
setPending() {
|
|
224
|
+
this.pending = true;
|
|
225
|
+
this.success = false;
|
|
226
|
+
}
|
|
227
|
+
get isPending() {
|
|
228
|
+
return this.pending === true;
|
|
229
|
+
}
|
|
230
|
+
get isSuccess() {
|
|
231
|
+
return this.success === true;
|
|
232
|
+
}
|
|
233
|
+
}
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CameraData, ContainerData, MaterialData } from "./pref-viewer-3d-data.js";
|
|
1
|
+
import { CameraData, ContainerData, MaterialData, IBLData } from "./pref-viewer-3d-data.js";
|
|
2
2
|
import BabylonJSController from "./babylonjs-controller.js";
|
|
3
3
|
import { PrefViewer3DStyles } from "./styles.js";
|
|
4
|
+
import { FileStorage } from "./file-storage.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* PrefViewer3D - Custom Web Component for interactive 3D visualization and configuration.
|
|
@@ -8,7 +9,7 @@ import { PrefViewer3DStyles } from "./styles.js";
|
|
|
8
9
|
* Overview:
|
|
9
10
|
* - Encapsulates a Babylon.js-powered 3D viewer for displaying models, environments, and materials.
|
|
10
11
|
* - Manages internal state for containers (model, environment, materials) and options (camera, materials).
|
|
11
|
-
* - Handles asset loading, configuration, and option updates through attributes and public methods.
|
|
12
|
+
* - Handles asset loading, configuration, and option updates (camera, materials, IBL) through attributes and public methods.
|
|
12
13
|
* - Provides API for showing/hiding the viewer, model, and environment, and for downloading assets.
|
|
13
14
|
* - Emits custom events for loading, loaded, and option-setting states.
|
|
14
15
|
*
|
|
@@ -26,7 +27,7 @@ import { PrefViewer3DStyles } from "./styles.js";
|
|
|
26
27
|
* - show(): Shows the 3D viewer component.
|
|
27
28
|
* - hide(): Hides the 3D viewer component.
|
|
28
29
|
* - load(config): Loads the provided configuration into the viewer.
|
|
29
|
-
* - setOptions(options): Sets viewer options such as camera and
|
|
30
|
+
* - setOptions(options): Sets viewer options such as camera, materials, and image-based lighting (IBL).
|
|
30
31
|
* - showModel(): Shows the 3D model.
|
|
31
32
|
* - hideModel(): Hides the 3D model.
|
|
32
33
|
* - showEnvironment(): Shows the 3D environment/scene.
|
|
@@ -46,6 +47,14 @@ import { PrefViewer3DStyles } from "./styles.js";
|
|
|
46
47
|
* - isLoaded: Indicates whether the GLTF/GLB content is loaded and ready.
|
|
47
48
|
* - isVisible: Indicates whether the component is currently visible.
|
|
48
49
|
*
|
|
50
|
+
* Internal Helpers:
|
|
51
|
+
* - #checkNeedToUpdateContainers(config): Flags model/environment/material containers that must reload.
|
|
52
|
+
* - #checkNeedToUpdateCamera(options): Detects camera option changes and marks them pending.
|
|
53
|
+
* - #checkNeedToUpdateMaterials(options): Resolves material overrides that require updates.
|
|
54
|
+
* - #checkNeedToUpdateIBL(options): Fetches HDR URLs/timestamps and enqueues IBL updates when needed.
|
|
55
|
+
* - #resetUpdateFlags(): Clears pending/success flags after loads or option-setting flows.
|
|
56
|
+
* - #onLoading/#onLoaded/#onSettingOptions/#onSetOptions(): Dispatch lifecycle events and synchronize attributes.
|
|
57
|
+
*
|
|
49
58
|
* Events:
|
|
50
59
|
* - "scene-loading": Dispatched when a loading operation starts.
|
|
51
60
|
* - "scene-loaded": Dispatched when a loading operation completes.
|
|
@@ -202,6 +211,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
202
211
|
innerFloor: new MaterialData("innerFloor", undefined, undefined, ["innerFloor"]),
|
|
203
212
|
outerFloor: new MaterialData("outerFloor", undefined, undefined, ["outerFloor"]),
|
|
204
213
|
},
|
|
214
|
+
ibl: new IBLData(),
|
|
205
215
|
},
|
|
206
216
|
};
|
|
207
217
|
}
|
|
@@ -225,6 +235,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
225
235
|
Object.values(this.#data.containers).forEach((container) => container.reset());
|
|
226
236
|
Object.values(this.#data.options.materials).forEach((material) => material.reset());
|
|
227
237
|
this.#data.options.camera.reset();
|
|
238
|
+
this.#data.options.ibl.reset();
|
|
228
239
|
}
|
|
229
240
|
|
|
230
241
|
/**
|
|
@@ -306,6 +317,51 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
306
317
|
return someNeedUpdate;
|
|
307
318
|
}
|
|
308
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Resolves incoming IBL settings (HDR URL, timestamp, intensity, shadows) and marks the option as pending when changed.
|
|
322
|
+
* Fetches signed URLs/time stamps when storage keys are provided so the Babylon controller can reload the environment map.
|
|
323
|
+
* @private
|
|
324
|
+
* @param {object} options - Options payload that may contain an `ibl` block with url, intensity, or shadow flags.
|
|
325
|
+
* @returns {Promise<boolean>} Resolves to true when any IBL property differs from the cached state, otherwise false.
|
|
326
|
+
*/
|
|
327
|
+
async #checkNeedToUpdateIBL(options) {
|
|
328
|
+
if (!options || options.ibl === undefined) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
const iblState = this.#data.options.ibl;
|
|
332
|
+
|
|
333
|
+
let url = undefined;
|
|
334
|
+
let timeStamp = undefined;
|
|
335
|
+
let shadows = undefined;
|
|
336
|
+
let intensity = undefined;
|
|
337
|
+
|
|
338
|
+
if (options.ibl.url) {
|
|
339
|
+
const fileStorage = new FileStorage("PrefViewer", "Files");
|
|
340
|
+
const newURL = await fileStorage.getURL(options.ibl.url);
|
|
341
|
+
if (newURL) {
|
|
342
|
+
url = newURL;
|
|
343
|
+
timeStamp = await fileStorage.getTimeStamp(options.ibl.url);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (options.ibl.shadows !== undefined) {
|
|
347
|
+
shadows = options.ibl.shadows;
|
|
348
|
+
}
|
|
349
|
+
if (options.ibl.intensity !== undefined) {
|
|
350
|
+
intensity = options.ibl.intensity;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const needUpdate = url !== undefined && url !== iblState.url ||
|
|
354
|
+
timeStamp !== undefined && timeStamp !== iblState.timeStamp ||
|
|
355
|
+
shadows !== undefined && shadows !== iblState.shadows ||
|
|
356
|
+
intensity !== undefined && intensity !== iblState.intensity;
|
|
357
|
+
if (needUpdate) {
|
|
358
|
+
iblState.setValues(url, intensity, shadows, timeStamp);
|
|
359
|
+
iblState.setPending(true);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return needUpdate;
|
|
363
|
+
}
|
|
364
|
+
|
|
309
365
|
/**
|
|
310
366
|
* Dispatches a "prefviewer3d-loading" event and updates loading state attributes.
|
|
311
367
|
* Used internally when a loading operation starts.
|
|
@@ -490,6 +546,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
490
546
|
if (config.options) {
|
|
491
547
|
this.#checkNeedToUpdateCamera(config.options);
|
|
492
548
|
this.#checkNeedToUpdateMaterials(config.options);
|
|
549
|
+
this.#checkNeedToUpdateIBL(config.options);
|
|
493
550
|
}
|
|
494
551
|
|
|
495
552
|
const loadDetail = await this.#babylonJSController.load();
|
|
@@ -518,6 +575,9 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
518
575
|
if (this.#checkNeedToUpdateMaterials(options)) {
|
|
519
576
|
someSetted = someSetted || this.#babylonJSController.setMaterialOptions();
|
|
520
577
|
}
|
|
578
|
+
if (this.#checkNeedToUpdateIBL(options)) {
|
|
579
|
+
someSetted = someSetted || this.#babylonJSController.setIBLOptions();
|
|
580
|
+
}
|
|
521
581
|
const detail = this.#onSetOptions();
|
|
522
582
|
return { success: someSetted, detail: detail };
|
|
523
583
|
}
|