@preference-sl/pref-viewer 2.17.0-beta.3 → 2.17.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/README.md CHANGED
@@ -88,8 +88,8 @@ viewer.setOptions({
88
88
  intensity: 1,
89
89
  shadows: true,
90
90
  },
91
- lighting: {
92
- timeOfDay: 0.5,
91
+ render: {
92
+ lightingTimeOfDay: 0.5,
93
93
  },
94
94
  });
95
95
  ```
@@ -501,6 +501,23 @@ Render settings are persisted in `localStorage` under the `pref-viewer/render-se
501
501
 
502
502
  The showroom daylight background uses a shared linear HDR color calibrated with exposure and ACES tone mapping for the current light studio gray target, `#d9d9d9`, in a scene-less view. Both the static look and time-of-day cycle use this same daylight value; the cycle retains its dark night endpoint. Scene geometry can cover the background. The internal `BabylonJSController.config.look.showroom.backgroundDay` value is not a public background-color option and must be recalibrated if tone mapping or exposure changes.
503
503
 
504
+ ## Environment map and scene lights
505
+
506
+ `options.ibl.url` accepts two formats, chosen by the URL's extension (query and hash ignored):
507
+
508
+ | Extension | Loaded as | Notes |
509
+ | --- | --- | --- |
510
+ | `.env` | Prefiltered Babylon environment (`CubeTexture.CreateFromPrefilteredData`) | No prefiltering in the browser; a 4K environment is typically under 1 MB. |
511
+ | anything else | Radiance HDR (`HDRCubeTexture`) | Prefiltered on load, which costs time and memory for large files. |
512
+
513
+ Either file is downloaded once, cached in IndexedDB, and reused across reloads.
514
+
515
+ Directional and spot lights that come with the scene glTF (`KHR_lights_punctual`) cast shadows when `render.shadowsEnabled` is `true` and `ibl.shadows` is off, alongside the built-in light's. Every scene and model mesh casts unless its node sets `extras.castShadows: false`, and receives unless it sets `extras.receiveShadows: false`. A light's shadow darkness comes from its node's `extras.shadowDarkness`, from `0` (black) to `1` (no shadow), and is `0.5` when absent:
516
+
517
+ ```json
518
+ { "name": "Sun", "rotation": [-0.5, 0, 0, 0.866], "extras": { "shadowDarkness": 0.15 }, "extensions": { "KHR_lights_punctual": { "light": 0 } } }
519
+ ```
520
+
504
521
  ## Runtime styles
505
522
 
506
523
  Runtime styles can be passed through the `styles` attribute, the `styles` property setter, `setStyles(styles)`, or `loadConfig({ styles })`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.17.0-beta.3",
3
+ "version": "2.17.0-beta.4",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -1,4 +1,4 @@
1
- import { ArcRotateCamera, AssetContainer, BRDFTextureTools, Camera, Color3, Color4, DefaultRenderingPipeline, DirectionalLight, Engine, FreeCamera, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, ImageProcessingConfiguration, LoadAssetContainerAsync, Material, Matrix, MeshBuilder, PBRMaterial, PointerEventTypes, PointLight, RenderTargetTexture, Scene, ShadowGenerator, SpotLight, SSAORenderingPipeline, Texture, Tools, UniversalCamera, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager, WebXRState, WhenTextureReadyAsync } from "@babylonjs/core";
1
+ import { ArcRotateCamera, AssetContainer, BRDFTextureTools, Camera, Color3, Color4, CubeTexture, DefaultRenderingPipeline, DirectionalLight, Engine, FreeCamera, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, ImageProcessingConfiguration, LoadAssetContainerAsync, Material, Matrix, MeshBuilder, PBRMaterial, PointerEventTypes, PointLight, RenderTargetTexture, Scene, ShadowGenerator, SpotLight, SSAORenderingPipeline, Texture, Tools, UniversalCamera, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager, WebXRState, WhenTextureReadyAsync } 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";
@@ -1334,7 +1334,7 @@ export default class BabylonJSController {
1334
1334
  * @returns {Promise<boolean>} Returns true if the environment texture was changed, false if it was already up to date or failed to load.
1335
1335
  * @description
1336
1336
  * Lifecycle implemented here:
1337
- * 1. If `options.ibl.cachedUrl` exists, create `HDRCubeTexture` from it.
1337
+ * 1. If `options.ibl.cachedUrl` exists, create `HDRCubeTexture` from it (a prefiltered `CubeTexture` when `options.ibl.url` is a `.env`).
1338
1338
  * 2. Wait for readiness, clone it into `#hdrTexture` for reuse.
1339
1339
  * 3. Call `options.ibl.consumeCachedUrl(true)` to revoke temporary object URLs and clear `cachedUrl`.
1340
1340
  * 4. On following reloads, if `options.ibl.valid === true` and no `cachedUrl` is present, use `#hdrTexture.clone()`.
@@ -1348,8 +1348,13 @@ export default class BabylonJSController {
1348
1348
  let hdrTexture = null;
1349
1349
  if (this.#options.ibl?.cachedUrl) {
1350
1350
  const hdrTextureURI = this.#options.ibl.cachedUrl;
1351
- const envTextureSize = BabylonJSController.config.quality.environmentTextureSize || 1024;
1352
- hdrTexture = new HDRCubeTexture(hdrTextureURI, this.#scene, envTextureSize, false, false, false, true, undefined, undefined, false, true, true);
1351
+ if (/\.env$/i.test(String(this.#options.ibl.url ?? "").split(/[?#]/)[0])) {
1352
+ // The cached file is a blob: URL with no extension, so the loader is told it is a .env.
1353
+ hdrTexture = CubeTexture.CreateFromPrefilteredData(hdrTextureURI, this.#scene, ".env");
1354
+ } else {
1355
+ const envTextureSize = BabylonJSController.config.quality.environmentTextureSize || 1024;
1356
+ hdrTexture = new HDRCubeTexture(hdrTextureURI, this.#scene, envTextureSize, false, false, false, true, undefined, undefined, false, true, true);
1357
+ }
1353
1358
  } else if (this.#hdrTexture && this.#options.ibl?.valid === true) {
1354
1359
  hdrTexture = this.#hdrTexture.clone();
1355
1360
  } else {
@@ -1586,7 +1591,7 @@ export default class BabylonJSController {
1586
1591
  return generator.getLight()?.name === "PrefViewerDirLight";
1587
1592
  });
1588
1593
 
1589
- this.#containers.environment?.AssetContainer?.lights.forEach((light) => {
1594
+ this.#containers.environment?.assetContainer?.lights.forEach((light) => {
1590
1595
  // Only shadows for DirectionalLight and SpotLight types
1591
1596
  if (!(light instanceof DirectionalLight || light instanceof SpotLight)) {
1592
1597
  return;
@@ -1595,7 +1600,9 @@ export default class BabylonJSController {
1595
1600
  const shadowGenerator = new ShadowGenerator(1024, light);
1596
1601
  shadowGenerator.useBlurExponentialShadowMap = true;
1597
1602
  shadowGenerator.blurKernel = 16;
1598
- shadowGenerator.darkness = 0.5;
1603
+ // KHR_lights_punctual parents the light to its glTF node, which carries the node's extras.
1604
+ const extrasShadowDarkness = light.parent?.metadata?.gltf?.extras?.shadowDarkness;
1605
+ shadowGenerator.darkness = typeof extrasShadowDarkness === "number" && Number.isFinite(extrasShadowDarkness) ? Math.min(Math.max(extrasShadowDarkness, 0), 1) : 0.5;
1599
1606
  shadowGenerator.bias = 0.0005;
1600
1607
  shadowGenerator.normalBias = 0.02;
1601
1608
  shadowGenerator.getShadowMap().refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;