diff --git a/lab/lite/babylon-ref-scene265.html b/lab/lite/babylon-ref-scene265.html new file mode 100644 index 0000000000..1a50bc9629 --- /dev/null +++ b/lab/lite/babylon-ref-scene265.html @@ -0,0 +1,27 @@ + + + + + Babylon.js Reference — Scene 265: EnvironmentTest (EXT_lights_image_based) + + + + + + + diff --git a/lab/lite/bundle-bjs-scene265.html b/lab/lite/bundle-bjs-scene265.html new file mode 100644 index 0000000000..65b650293b --- /dev/null +++ b/lab/lite/bundle-bjs-scene265.html @@ -0,0 +1,27 @@ + + + + + Babylon.js Reference — Scene 265: EnvironmentTest (EXT_lights_image_based) (Bundle) + + + + + + + diff --git a/lab/lite/bundle-scene265.html b/lab/lite/bundle-scene265.html new file mode 100644 index 0000000000..e567a7b148 --- /dev/null +++ b/lab/lite/bundle-scene265.html @@ -0,0 +1,28 @@ + + + + + Babylon Lite — Scene 265: EnvironmentTest (EXT_lights_image_based) (Bundle) + + + + + + + + diff --git a/lab/lite/scene265.html b/lab/lite/scene265.html new file mode 100644 index 0000000000..c08dafd7c4 --- /dev/null +++ b/lab/lite/scene265.html @@ -0,0 +1,28 @@ + + + + + Babylon Lite — Scene 265: EnvironmentTest (EXT_lights_image_based) + + + + + + + + diff --git a/lab/lite/src/bjs/scene265.ts b/lab/lite/src/bjs/scene265.ts new file mode 100644 index 0000000000..a9673bc033 --- /dev/null +++ b/lab/lite/src/bjs/scene265.ts @@ -0,0 +1,45 @@ +import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; +import { Color4 } from "@babylonjs/core/Maths/math.color"; +import { WebGPUEngine } from "@babylonjs/core/Engines/webgpuEngine"; +import "@babylonjs/core/Helpers/sceneHelpers"; +import "@babylonjs/core/Loading/loadingScreen"; +import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader"; +import { Scene } from "@babylonjs/core/scene"; +import "@babylonjs/loaders/glTF"; + +const MODEL_URL = "https://cx20.github.io/gltf-test/tutorialModels/EnvironmentTest/glTF-IBL/EnvironmentTest.gltf"; + +(async function () { + const __initStart = performance.now(); + const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement; + const engine = new WebGPUEngine(canvas, { antialias: true, adaptToDeviceRatio: true }); + await engine.initAsync(); + engine.displayLoadingUI = function () {}; + + const scene = new Scene(engine); + scene.clearColor = new Color4(0.2, 0.2, 0.3, 1.0); + + // The glTF's EXT_lights_image_based extension installs scene.environmentTexture. + await SceneLoader.AppendAsync("", MODEL_URL, scene); + + // Match loadEnvironment's image-processing defaults (the Lite extension sets these). + scene.imageProcessingConfiguration.exposure = 0.8; + scene.imageProcessingConfiguration.contrast = 1.2; + scene.imageProcessingConfiguration.toneMappingEnabled = true; + + // Same auto-framing formula as Lite's createDefaultCamera; override angles to match. + scene.createDefaultCamera(true, true, true); + const camera = scene.activeCamera as ArcRotateCamera; + camera.alpha = Math.PI / 2; + camera.beta = Math.PI / 2.5; + + await scene.whenReadyAsync(); + engine.runRenderLoop(() => scene.render()); + window.addEventListener("resize", () => engine.resize()); + + await new Promise((resolve) => scene.onAfterRenderObservable.addOnce(() => resolve())); + canvas.dataset.camAlpha = String(camera.alpha); + canvas.dataset.camRadius = String(camera.radius); + canvas.dataset.initMs = String(performance.now() - __initStart); + canvas.dataset.ready = "true"; +})().catch(console.error); diff --git a/lab/lite/src/lite/scene265.ts b/lab/lite/src/lite/scene265.ts new file mode 100644 index 0000000000..c8a2fd7f98 --- /dev/null +++ b/lab/lite/src/lite/scene265.ts @@ -0,0 +1,34 @@ +// Scene 265 — EnvironmentTest (cx20 gltf-test parity). +// Exercises EXT_lights_image_based: the glTF carries its own image-based light +// (irradiance SH9 + prefiltered specular cubemap), so we do NOT call +// loadEnvironment — the extension installs the environment onto the scene. +import { addToScene, startEngine, createEngine, createSceneContext, createDefaultCamera, loadGltf, attachControl, registerScene } from "babylon-lite"; + +const MODEL_URL = "https://cx20.github.io/gltf-test/tutorialModels/EnvironmentTest/glTF-IBL/EnvironmentTest.gltf"; + +async function main(): Promise { + const __initStart = performance.now(); + const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement; + + const engine = await createEngine(canvas); + const scene = createSceneContext(engine); + scene.clearColor = { r: 0.2, g: 0.2, b: 0.3, a: 1.0 }; + + const root = await loadGltf(engine, MODEL_URL); + addToScene(scene, root); + + const cam = createDefaultCamera(scene); + cam.alpha = Math.PI / 2; + cam.beta = Math.PI / 2.5; + attachControl(cam, canvas, scene); + + await registerScene(scene); + await startEngine(engine); + (window as any).__scene = scene; + canvas.dataset.camAlpha = String(cam.alpha); + canvas.dataset.camRadius = String(cam.radius); + canvas.dataset.initMs = String(performance.now() - __initStart); + canvas.dataset.ready = "true"; +} + +main().catch(console.error); diff --git a/lab/public/bundle/manifest/scene104.json b/lab/public/bundle/manifest/scene104.json index 500c4ab804..5100e8a395 100644 --- a/lab/public/bundle/manifest/scene104.json +++ b/lab/public/bundle/manifest/scene104.json @@ -1,10 +1,10 @@ { - "rawKB": 101.4, + "rawKB": 101.5, "gzipKB": 39.2, "ignoredRawKB": 0, "runtimeChunks": [ "scene104-generate-mipmaps-C2O_HNRi.js", - "scene104-gltf-feature-registry-BOpBaZNy.js", + "scene104-gltf-feature-registry-B_rK3afU.js", "scene104-gltf-glb-parser-BJfNq64D.js", "scene104-shader-composer-DishuYLa.js", "scene104-standard-renderable-CHoHvWO9.js", diff --git a/lab/public/bundle/manifest/scene105.json b/lab/public/bundle/manifest/scene105.json index 126d071c1f..6dafbf037f 100644 --- a/lab/public/bundle/manifest/scene105.json +++ b/lab/public/bundle/manifest/scene105.json @@ -1,10 +1,10 @@ { - "rawKB": 102.1, - "gzipKB": 39.4, + "rawKB": 102.2, + "gzipKB": 39.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene105-generate-mipmaps-q97-3GGe.js", - "scene105-gltf-feature-registry-GXOsvmf1.js", + "scene105-gltf-feature-registry-DKfE0vzV.js", "scene105-gltf-glb-parser-BsWC1iTB.js", "scene105-shader-composer-DishuYLa.js", "scene105-standard-renderable-BXA9dKAx.js", diff --git a/lab/public/bundle/manifest/scene11.json b/lab/public/bundle/manifest/scene11.json index 07f2362213..73522f019b 100644 --- a/lab/public/bundle/manifest/scene11.json +++ b/lab/public/bundle/manifest/scene11.json @@ -1,6 +1,6 @@ { - "rawKB": 89, - "gzipKB": 37.4, + "rawKB": 89.1, + "gzipKB": 37.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene11-create-skeleton-D-3exD5E.js", @@ -9,7 +9,7 @@ "scene11-gltf-ext-spec-gloss-D60sFZYX.js", "scene11-gltf-feature-animations-CUKuLYEP.js", "scene11-gltf-feature-extras-VHPteGay.js", - "scene11-gltf-feature-registry-HI7s4KMP.js", + "scene11-gltf-feature-registry-CoWTXZ-o.js", "scene11-gltf-feature-skeleton-Djlep9zW.js", "scene11-gltf-glb-parser-B0CzN8nR.js", "scene11-gltf-sampler-desc-DP9TFn1W.js", diff --git a/lab/public/bundle/manifest/scene112.json b/lab/public/bundle/manifest/scene112.json index c39909d3df..342d7d509d 100644 --- a/lab/public/bundle/manifest/scene112.json +++ b/lab/public/bundle/manifest/scene112.json @@ -1,5 +1,5 @@ { - "rawKB": 120, + "rawKB": 120.1, "gzipKB": 48.8, "ignoredRawKB": 0, "runtimeChunks": [ @@ -9,7 +9,7 @@ "scene112-generate-mipmaps-r2o5vX6L.js", "scene112-gltf-ext-basisu-CRSWpXls.js", "scene112-gltf-ext-dielectric-C6hvXXiF.js", - "scene112-gltf-feature-registry-CsinE8Td.js", + "scene112-gltf-feature-registry-CBflmSLe.js", "scene112-gltf-json-asset-Cj8WBr3J.js", "scene112-ibl-fragment-CeRrvT96.js", "scene112-pbr-refraction-nAlT2yO6.js", diff --git a/lab/public/bundle/manifest/scene115.json b/lab/public/bundle/manifest/scene115.json index ba8bbc07f7..1f1c4d95a4 100644 --- a/lab/public/bundle/manifest/scene115.json +++ b/lab/public/bundle/manifest/scene115.json @@ -1,6 +1,6 @@ { - "rawKB": 126.4, - "gzipKB": 52.1, + "rawKB": 126.5, + "gzipKB": 52.2, "ignoredRawKB": 0, "runtimeChunks": [ "scene115-create-morph-targets-BxcR5t59.js", @@ -10,7 +10,7 @@ "scene115-gltf-color-normalize-4-WGzPL2.js", "scene115-gltf-feature-animations-DOqxEAHR.js", "scene115-gltf-feature-morph-DLWJRlR1.js", - "scene115-gltf-feature-registry-v5cVaPih.js", + "scene115-gltf-feature-registry-BjqhkauT.js", "scene115-gltf-feature-skeleton-BktZKWC9.js", "scene115-gltf-json-asset-DXBgrt5f.js", "scene115-morph-fragment-BbCT_r5m.js", diff --git a/lab/public/bundle/manifest/scene12.json b/lab/public/bundle/manifest/scene12.json index 0cb1cf7646..4cfb345e0b 100644 --- a/lab/public/bundle/manifest/scene12.json +++ b/lab/public/bundle/manifest/scene12.json @@ -1,13 +1,13 @@ { - "rawKB": 103.5, - "gzipKB": 42.2, + "rawKB": 103.6, + "gzipKB": 42.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene12-create-skeleton-yE34pa8K.js", "scene12-generate-mipmaps-WJd2dKx2.js", "scene12-gltf-animation-BQj3RAlu.js", "scene12-gltf-feature-animations-Db5mzwHK.js", - "scene12-gltf-feature-registry-BvvuIzLS.js", + "scene12-gltf-feature-registry-DvTgP0Jo.js", "scene12-gltf-feature-skeleton-eatI5LFE.js", "scene12-gltf-glb-parser-BrsOlymf.js", "scene12-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene127.json b/lab/public/bundle/manifest/scene127.json index 97abd23de6..aeb5d841d1 100644 --- a/lab/public/bundle/manifest/scene127.json +++ b/lab/public/bundle/manifest/scene127.json @@ -1,5 +1,5 @@ { - "rawKB": 61.3, + "rawKB": 61.4, "gzipKB": 23.5, "ignoredRawKB": 0, "runtimeChunks": [ diff --git a/lab/public/bundle/manifest/scene144.json b/lab/public/bundle/manifest/scene144.json index b3b1c2ef81..1a0eab11fa 100644 --- a/lab/public/bundle/manifest/scene144.json +++ b/lab/public/bundle/manifest/scene144.json @@ -1,6 +1,6 @@ { - "rawKB": 110.5, - "gzipKB": 44.7, + "rawKB": 110.6, + "gzipKB": 44.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene144-create-skeleton-DalWacs1.js", @@ -9,7 +9,7 @@ "scene144-gltf-ext-spec-gloss-D60sFZYX.js", "scene144-gltf-feature-animations-CvLpEQNh.js", "scene144-gltf-feature-extras-VHPteGay.js", - "scene144-gltf-feature-registry-ByT8hALD.js", + "scene144-gltf-feature-registry-C4zDl2lJ.js", "scene144-gltf-feature-skeleton-Dvlpv1pW.js", "scene144-gltf-glb-parser-CXf6hcyj.js", "scene144-gltf-pbr-builder-ext-g9qXikef.js", diff --git a/lab/public/bundle/manifest/scene146.json b/lab/public/bundle/manifest/scene146.json index b46ab447c6..fb0bd845df 100644 --- a/lab/public/bundle/manifest/scene146.json +++ b/lab/public/bundle/manifest/scene146.json @@ -1,6 +1,6 @@ { "rawKB": 111, - "gzipKB": 45.4, + "gzipKB": 45.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene146-alpha-test-fragment-D_P8KBFC.js", diff --git a/lab/public/bundle/manifest/scene147.json b/lab/public/bundle/manifest/scene147.json index e87fe6e482..3260b1b0e3 100644 --- a/lab/public/bundle/manifest/scene147.json +++ b/lab/public/bundle/manifest/scene147.json @@ -1,12 +1,12 @@ { - "rawKB": 103.7, + "rawKB": 103.8, "gzipKB": 41.6, "ignoredRawKB": 0, "runtimeChunks": [ "scene147-directional-light-Bhj3CbsK.js", "scene147-generate-mipmaps-7FMQd9Gb.js", "scene147-gltf-feature-lights-punctual-CwjriEB4.js", - "scene147-gltf-feature-registry-Dj5aS1Qx.js", + "scene147-gltf-feature-registry-f8l66LCT.js", "scene147-gltf-glb-parser-1VrmmUS3.js", "scene147-gltf-light-pointer-state-B7kFTRxK.js", "scene147-multilight-wgsl-EMm1KlC7.js", diff --git a/lab/public/bundle/manifest/scene148.json b/lab/public/bundle/manifest/scene148.json index fb8cd01768..1b7b4ac7cc 100644 --- a/lab/public/bundle/manifest/scene148.json +++ b/lab/public/bundle/manifest/scene148.json @@ -1,12 +1,12 @@ { - "rawKB": 109.6, - "gzipKB": 43.1, + "rawKB": 109.8, + "gzipKB": 43.2, "ignoredRawKB": 0, "runtimeChunks": [ "scene148-directional-light-UoLzTD31.js", "scene148-generate-mipmaps-CfuW6q-h.js", "scene148-gltf-feature-lights-punctual-BTZaThsN.js", - "scene148-gltf-feature-registry-BeLwYcCC.js", + "scene148-gltf-feature-registry-CG_xHMUj.js", "scene148-gltf-glb-parser-BR2EHjxU.js", "scene148-gltf-light-pointer-state-B7kFTRxK.js", "scene148-pbr-geometry-view-VyZljgCl.js", diff --git a/lab/public/bundle/manifest/scene149.json b/lab/public/bundle/manifest/scene149.json index dbfd7589bc..3778b35b6b 100644 --- a/lab/public/bundle/manifest/scene149.json +++ b/lab/public/bundle/manifest/scene149.json @@ -1,6 +1,6 @@ { - "rawKB": 109, - "gzipKB": 44.5, + "rawKB": 109.1, + "gzipKB": 44.6, "ignoredRawKB": 6.8, "runtimeChunks": [ "scene149-_math-factory-DFVyS8gB.js", @@ -9,7 +9,7 @@ "scene149-generate-mipmaps-gtcbk4S2.js", "scene149-geometry-texture-output-BfeQftmX.js", "scene149-gltf-feature-lights-punctual-D7xjFqdg.js", - "scene149-gltf-feature-registry-D8cKsNpu.js", + "scene149-gltf-feature-registry-ZWJbcsHU.js", "scene149-gltf-glb-parser-CpRY-0L4.js", "scene149-gltf-light-pointer-state-B7kFTRxK.js", "scene149-input-block-BTUtjdzr.js", diff --git a/lab/public/bundle/manifest/scene152.json b/lab/public/bundle/manifest/scene152.json index 831b8f48f2..db5717ed7e 100644 --- a/lab/public/bundle/manifest/scene152.json +++ b/lab/public/bundle/manifest/scene152.json @@ -1,6 +1,6 @@ { - "rawKB": 94.1, - "gzipKB": 39.1, + "rawKB": 94.2, + "gzipKB": 39.2, "ignoredRawKB": 0, "runtimeChunks": [ "scene152-create-skeleton-BvRoBRkx.js", @@ -9,7 +9,7 @@ "scene152-gltf-ext-spec-gloss-D60sFZYX.js", "scene152-gltf-feature-animations-B_C5kwG5.js", "scene152-gltf-feature-extras-VHPteGay.js", - "scene152-gltf-feature-registry-B7gVMsrH.js", + "scene152-gltf-feature-registry-CLQZbp4N.js", "scene152-gltf-feature-skeleton-CUsQ3cht.js", "scene152-gltf-glb-parser-Dr_B2hXm.js", "scene152-gltf-sampler-desc-D0Q2VM93.js", diff --git a/lab/public/bundle/manifest/scene157.json b/lab/public/bundle/manifest/scene157.json index f96b7c4294..d317c3e064 100644 --- a/lab/public/bundle/manifest/scene157.json +++ b/lab/public/bundle/manifest/scene157.json @@ -1,13 +1,13 @@ { - "rawKB": 95.5, - "gzipKB": 38.9, + "rawKB": 95.6, + "gzipKB": 39, "ignoredRawKB": 0, "runtimeChunks": [ "scene157-create-skeleton-CSKHrTlw.js", "scene157-generate-mipmaps-BYHWBGCp.js", "scene157-gltf-animation-PnQa26Zh.js", "scene157-gltf-feature-animations-at7-WE4C.js", - "scene157-gltf-feature-registry-CUqIEUA4.js", + "scene157-gltf-feature-registry-DlyUXqwJ.js", "scene157-gltf-feature-skeleton-Cxp-WFUs.js", "scene157-gltf-glb-parser-DZ4Xgxsa.js", "scene157-multilight-wgsl-BVR6TJvC.js", diff --git a/lab/public/bundle/manifest/scene158.json b/lab/public/bundle/manifest/scene158.json index b2a47a12d5..ce7d502a05 100644 --- a/lab/public/bundle/manifest/scene158.json +++ b/lab/public/bundle/manifest/scene158.json @@ -1,5 +1,5 @@ { - "rawKB": 96, + "rawKB": 96.1, "gzipKB": 39.1, "ignoredRawKB": 0, "runtimeChunks": [ @@ -7,7 +7,7 @@ "scene158-generate-mipmaps-bNvmU0dI.js", "scene158-gltf-animation-D4bTYTv1.js", "scene158-gltf-feature-animations-BPofhcKw.js", - "scene158-gltf-feature-registry-DT7ENkGn.js", + "scene158-gltf-feature-registry-BcxdxuWM.js", "scene158-gltf-feature-skeleton-D2diH2X_.js", "scene158-gltf-glb-parser-BhqojsL9.js", "scene158-multilight-wgsl-DtA6qLNj.js", diff --git a/lab/public/bundle/manifest/scene164.json b/lab/public/bundle/manifest/scene164.json index 6adabb8653..82a95a45fc 100644 --- a/lab/public/bundle/manifest/scene164.json +++ b/lab/public/bundle/manifest/scene164.json @@ -1,6 +1,6 @@ { - "rawKB": 98.9, - "gzipKB": 40.9, + "rawKB": 99, + "gzipKB": 41, "ignoredRawKB": 0, "runtimeChunks": [ "scene164-create-morph-targets-DRqKS4bi.js", @@ -10,7 +10,7 @@ "scene164-gltf-color-normalize-4-WGzPL2.js", "scene164-gltf-feature-animations-CoptQtIt.js", "scene164-gltf-feature-morph-Dcma2yce.js", - "scene164-gltf-feature-registry-B_YhQvuM.js", + "scene164-gltf-feature-registry-C_lojFmB.js", "scene164-gltf-feature-skeleton-DRjn81A9.js", "scene164-gltf-json-asset-B2lSB7pA.js", "scene164-morph-fragment-7aHPckU3.js", diff --git a/lab/public/bundle/manifest/scene176.json b/lab/public/bundle/manifest/scene176.json index 74415d7a41..01ec27c8c5 100644 --- a/lab/public/bundle/manifest/scene176.json +++ b/lab/public/bundle/manifest/scene176.json @@ -1,12 +1,12 @@ { - "rawKB": 104, - "gzipKB": 41.9, + "rawKB": 104.1, + "gzipKB": 42, "ignoredRawKB": 0, "runtimeChunks": [ "scene176-generate-mipmaps-LBrT9Lsp.js", "scene176-gltf-ext-dielectric-C6hvXXiF.js", "scene176-gltf-feature-extras-VHPteGay.js", - "scene176-gltf-feature-registry-C9Nk5MCW.js", + "scene176-gltf-feature-registry-vDUh2x2R.js", "scene176-gltf-json-asset-C-LYB2bT.js", "scene176-ibl-fragment-CeRrvT96.js", "scene176-ibl-skybox-wgsl-CFIBOHHx.js", diff --git a/lab/public/bundle/manifest/scene178.json b/lab/public/bundle/manifest/scene178.json index 4131a1b938..7c10a991d6 100644 --- a/lab/public/bundle/manifest/scene178.json +++ b/lab/public/bundle/manifest/scene178.json @@ -1,11 +1,11 @@ { - "rawKB": 87.7, - "gzipKB": 35.7, + "rawKB": 87.8, + "gzipKB": 35.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene178-generate-mipmaps-BGzDQPIy.js", "scene178-gltf-ext-iridescence-b4LVniAM.js", - "scene178-gltf-feature-registry-CQxgwmB5.js", + "scene178-gltf-feature-registry-2IMHz2-v.js", "scene178-gltf-glb-parser-d5msxooD.js", "scene178-ibl-fragment-CeRrvT96.js", "scene178-ibl-skybox-wgsl-CFIBOHHx.js", diff --git a/lab/public/bundle/manifest/scene210.json b/lab/public/bundle/manifest/scene210.json index dc54315ba7..1a6a32a9cd 100644 --- a/lab/public/bundle/manifest/scene210.json +++ b/lab/public/bundle/manifest/scene210.json @@ -1,10 +1,10 @@ { - "rawKB": 76.8, + "rawKB": 76.9, "gzipKB": 31.9, "ignoredRawKB": 0, "runtimeChunks": [ "scene210-generate-mipmaps-BeLXoJVY.js", - "scene210-gltf-feature-registry-CvVtn6EL.js", + "scene210-gltf-feature-registry-D0nALpTm.js", "scene210-gltf-feature-xmp-S8Tl-XNQ.js", "scene210-gltf-glb-parser-fJOQf1ny.js", "scene210-gltf-interleave-D_KaKI90.js", diff --git a/lab/public/bundle/manifest/scene211.json b/lab/public/bundle/manifest/scene211.json index 7a8c879ae6..21ec38a4f3 100644 --- a/lab/public/bundle/manifest/scene211.json +++ b/lab/public/bundle/manifest/scene211.json @@ -1,5 +1,5 @@ { - "rawKB": 90.7, + "rawKB": 90.8, "gzipKB": 38.3, "ignoredRawKB": 0, "runtimeChunks": [ @@ -9,7 +9,7 @@ "scene211-gltf-ext-quantization-DTtElLs7.js", "scene211-gltf-feature-animations-CsN6DvHw.js", "scene211-gltf-feature-meshopt-Dt65KW5n.js", - "scene211-gltf-feature-registry-BBXWh-H3.js", + "scene211-gltf-feature-registry-BrpLabr3.js", "scene211-gltf-feature-skeleton-DpuvZ9VR.js", "scene211-gltf-json-asset-CHRQZ19d.js", "scene211-gltf-multi-buffer-Cwm5u1Qq.js", diff --git a/lab/public/bundle/manifest/scene212.json b/lab/public/bundle/manifest/scene212.json index 9bb8befb47..9414b9620a 100644 --- a/lab/public/bundle/manifest/scene212.json +++ b/lab/public/bundle/manifest/scene212.json @@ -1,11 +1,11 @@ { - "rawKB": 104.1, + "rawKB": 104.2, "gzipKB": 41.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene212-generate-mipmaps-ffZ1zox-.js", "scene212-gltf-ext-dielectric-C6hvXXiF.js", - "scene212-gltf-feature-registry-B_jczvrv.js", + "scene212-gltf-feature-registry-B1FTzveb.js", "scene212-gltf-glb-parser-DpbXuAuC.js", "scene212-ibl-fragment-CeRrvT96.js", "scene212-ibl-skybox-wgsl-CFIBOHHx.js", diff --git a/lab/public/bundle/manifest/scene217.json b/lab/public/bundle/manifest/scene217.json index 31f0a31dac..0cd507d17d 100644 --- a/lab/public/bundle/manifest/scene217.json +++ b/lab/public/bundle/manifest/scene217.json @@ -1,6 +1,6 @@ { "rawKB": 77.9, - "gzipKB": 31.4, + "gzipKB": 31.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene217-multilight-wgsl-hb_hGU0D.js", diff --git a/lab/public/bundle/manifest/scene218.json b/lab/public/bundle/manifest/scene218.json index edd82bebe7..61953b29e7 100644 --- a/lab/public/bundle/manifest/scene218.json +++ b/lab/public/bundle/manifest/scene218.json @@ -1,6 +1,6 @@ { - "rawKB": 94.8, - "gzipKB": 39.3, + "rawKB": 94.9, + "gzipKB": 39.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene218-create-skeleton-i4S0gk21.js", @@ -9,7 +9,7 @@ "scene218-gltf-ext-spec-gloss-D60sFZYX.js", "scene218-gltf-feature-animations-u2H7ejDl.js", "scene218-gltf-feature-extras-VHPteGay.js", - "scene218-gltf-feature-registry-C6aCvKmN.js", + "scene218-gltf-feature-registry-wh_ox79H.js", "scene218-gltf-feature-skeleton-n7Xbyk0V.js", "scene218-gltf-glb-parser-BXHcqEF1.js", "scene218-gltf-sampler-desc-CS2bAJuk.js", diff --git a/lab/public/bundle/manifest/scene219.json b/lab/public/bundle/manifest/scene219.json index d2bfaa02c4..0d77bfed8d 100644 --- a/lab/public/bundle/manifest/scene219.json +++ b/lab/public/bundle/manifest/scene219.json @@ -1,6 +1,6 @@ { - "rawKB": 98, - "gzipKB": 40.7, + "rawKB": 98.1, + "gzipKB": 40.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene219-create-skeleton-DIqv-tev.js", @@ -9,7 +9,7 @@ "scene219-gltf-ext-spec-gloss-D60sFZYX.js", "scene219-gltf-feature-animations-B_YvITgV.js", "scene219-gltf-feature-extras-VHPteGay.js", - "scene219-gltf-feature-registry-Dd1yymM7.js", + "scene219-gltf-feature-registry-Dp5eqqoy.js", "scene219-gltf-feature-skeleton-CpmBaHpL.js", "scene219-gltf-glb-parser-PDyH09Yg.js", "scene219-gltf-sampler-desc-CKWNJQQv.js", diff --git a/lab/public/bundle/manifest/scene240.json b/lab/public/bundle/manifest/scene240.json index ccdb208255..835b7d3b5b 100644 --- a/lab/public/bundle/manifest/scene240.json +++ b/lab/public/bundle/manifest/scene240.json @@ -1,5 +1,5 @@ { - "rawKB": 91, + "rawKB": 91.1, "gzipKB": 38.1, "ignoredRawKB": 0, "runtimeChunks": [ @@ -7,7 +7,7 @@ "scene240-generate-mipmaps-BQZ9ivTX.js", "scene240-gltf-animation-aBVjSk7N.js", "scene240-gltf-feature-animations-Cm8MU3RN.js", - "scene240-gltf-feature-registry-DJN0wxrV.js", + "scene240-gltf-feature-registry-1jiIj5sv.js", "scene240-gltf-json-asset-C-b8cTJb.js", "scene240-gltf-multi-buffer-Cwm5u1Qq.js", "scene240-gltf-normals-DYhLwXNX.js", diff --git a/lab/public/bundle/manifest/scene241.json b/lab/public/bundle/manifest/scene241.json index 0c963b1dab..7cbaff28bf 100644 --- a/lab/public/bundle/manifest/scene241.json +++ b/lab/public/bundle/manifest/scene241.json @@ -1,6 +1,6 @@ { - "rawKB": 161.8, - "gzipKB": 66.2, + "rawKB": 161.9, + "gzipKB": 66.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene241-alpha-test-fragment-BpzeKSEk.js", @@ -21,7 +21,7 @@ "scene241-gltf-feature-animation-pointer-Ch_iHImp.js", "scene241-gltf-feature-animations-DEv1D7Id.js", "scene241-gltf-feature-lights-punctual-BYIHIGm7.js", - "scene241-gltf-feature-registry-CZAnajUH.js", + "scene241-gltf-feature-registry-BLRhzZDS.js", "scene241-gltf-json-asset-Dn-uYlO4.js", "scene241-gltf-light-pointer-state-B7kFTRxK.js", "scene241-gltf-pbr-builder-ext-D5jBhNyS.js", diff --git a/lab/public/bundle/manifest/scene242.json b/lab/public/bundle/manifest/scene242.json index 9f20380e87..98408e8ef1 100644 --- a/lab/public/bundle/manifest/scene242.json +++ b/lab/public/bundle/manifest/scene242.json @@ -1,5 +1,5 @@ { - "rawKB": 97.2, + "rawKB": 97.3, "gzipKB": 40.3, "ignoredRawKB": 0, "runtimeChunks": [ @@ -10,7 +10,7 @@ "scene242-gltf-ext-emissive-strength-CButLZnK.js", "scene242-gltf-feature-animation-pointer-tuVf2ksY.js", "scene242-gltf-feature-animations-c59Hsp8m.js", - "scene242-gltf-feature-registry-B3JqMJCl.js", + "scene242-gltf-feature-registry-B-t_oxJU.js", "scene242-gltf-json-asset-89J8Oj0_.js", "scene242-ibl-fragment-CeRrvT96.js", "scene242-pbr-renderable-D53YrDns.js", diff --git a/lab/public/bundle/manifest/scene243.json b/lab/public/bundle/manifest/scene243.json index 80271918c7..b8d970bd5e 100644 --- a/lab/public/bundle/manifest/scene243.json +++ b/lab/public/bundle/manifest/scene243.json @@ -1,5 +1,5 @@ { - "rawKB": 97.5, + "rawKB": 97.6, "gzipKB": 41.1, "ignoredRawKB": 0, "runtimeChunks": [ @@ -9,7 +9,7 @@ "scene243-gltf-feature-animations-CX3jw94s.js", "scene243-gltf-feature-extras-VHPteGay.js", "scene243-gltf-feature-morph-BCUQi4W_.js", - "scene243-gltf-feature-registry-Cj6WQCqE.js", + "scene243-gltf-feature-registry-fDksuXuF.js", "scene243-gltf-json-asset-Cgf3mpUp.js", "scene243-gltf-pbr-builder-ext-CVrSOAh6.js", "scene243-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene244.json b/lab/public/bundle/manifest/scene244.json index 7c70b27d97..2198ca590d 100644 --- a/lab/public/bundle/manifest/scene244.json +++ b/lab/public/bundle/manifest/scene244.json @@ -1,5 +1,5 @@ { - "rawKB": 133.7, + "rawKB": 133.8, "gzipKB": 54.2, "ignoredRawKB": 0, "runtimeChunks": [ @@ -12,7 +12,7 @@ "scene244-gltf-ext-uv-transform-YGWG8v_J.js", "scene244-gltf-feature-animation-pointer-IIOn7FNX.js", "scene244-gltf-feature-animations-C9Kp7IJh.js", - "scene244-gltf-feature-registry-D5-R55AG.js", + "scene244-gltf-feature-registry-Bnc9qOZU.js", "scene244-gltf-json-asset-BJhL5QXR.js", "scene244-gltf-pbr-builder-ext-BI2CisGc.js", "scene244-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene245.json b/lab/public/bundle/manifest/scene245.json index 3c98b4af28..3e007f4c8d 100644 --- a/lab/public/bundle/manifest/scene245.json +++ b/lab/public/bundle/manifest/scene245.json @@ -1,5 +1,5 @@ { - "rawKB": 100.7, + "rawKB": 100.8, "gzipKB": 42.7, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene245-generate-mipmaps-WM6bVf2v.js", "scene245-gltf-animation-l40-wjM0.js", "scene245-gltf-feature-animations-JDRZSSUs.js", - "scene245-gltf-feature-registry-gM33Awmp.js", + "scene245-gltf-feature-registry-CV2u0xO_.js", "scene245-gltf-feature-skeleton-D19NTiji.js", "scene245-gltf-interleave-7NBE5rBz.js", "scene245-gltf-json-asset-D_ddxWqm.js", diff --git a/lab/public/bundle/manifest/scene246.json b/lab/public/bundle/manifest/scene246.json index 486195a816..fe0c264ae1 100644 --- a/lab/public/bundle/manifest/scene246.json +++ b/lab/public/bundle/manifest/scene246.json @@ -1,5 +1,5 @@ { - "rawKB": 99.5, + "rawKB": 99.6, "gzipKB": 42.3, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene246-generate-mipmaps-DzKvtUax.js", "scene246-gltf-animation-Bg1KL3K9.js", "scene246-gltf-feature-animations-DxZ057Zf.js", - "scene246-gltf-feature-registry-CBbXbqJx.js", + "scene246-gltf-feature-registry-BOTdaBTS.js", "scene246-gltf-feature-skeleton-CD4nSwQl.js", "scene246-gltf-interleave-ZNL-XZje.js", "scene246-gltf-json-asset-BLYKbDyg.js", diff --git a/lab/public/bundle/manifest/scene247.json b/lab/public/bundle/manifest/scene247.json index 7b8aff3d1f..0db386293a 100644 --- a/lab/public/bundle/manifest/scene247.json +++ b/lab/public/bundle/manifest/scene247.json @@ -1,12 +1,12 @@ { - "rawKB": 84.7, + "rawKB": 84.8, "gzipKB": 35.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene247-generate-mipmaps-Crazqgzv.js", "scene247-gltf-feature-extras-VHPteGay.js", "scene247-gltf-feature-gpu-instancing-BQnnR0-8.js", - "scene247-gltf-feature-registry-BfM-Z3oM.js", + "scene247-gltf-feature-registry-j6KavomJ.js", "scene247-gltf-json-asset-CILluwFg.js", "scene247-gltf-multi-buffer-Cwm5u1Qq.js", "scene247-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene251.json b/lab/public/bundle/manifest/scene251.json index 08c1152e66..13cbec764b 100644 --- a/lab/public/bundle/manifest/scene251.json +++ b/lab/public/bundle/manifest/scene251.json @@ -1,5 +1,5 @@ { - "rawKB": 89.7, + "rawKB": 89.8, "gzipKB": 37, "ignoredRawKB": 0, "runtimeChunks": [ @@ -7,7 +7,7 @@ "scene251-generate-mipmaps-QjbHE23R.js", "scene251-gltf-animation-Cn2klEOG.js", "scene251-gltf-feature-animations-BBOyTs9Y.js", - "scene251-gltf-feature-registry-DXI3KYwX.js", + "scene251-gltf-feature-registry-DUX8T-vK.js", "scene251-gltf-feature-skeleton-C4O8Sov-.js", "scene251-gltf-glb-parser-8AD91ACR.js", "scene251-multilight-wgsl-C4_Zdm3l.js", diff --git a/lab/public/bundle/manifest/scene253.json b/lab/public/bundle/manifest/scene253.json index e61ae62b87..1a4c5ae6ac 100644 --- a/lab/public/bundle/manifest/scene253.json +++ b/lab/public/bundle/manifest/scene253.json @@ -1,5 +1,5 @@ { - "rawKB": 153, + "rawKB": 153.1, "gzipKB": 64.6, "ignoredRawKB": 0, "runtimeChunks": [ @@ -24,7 +24,7 @@ "scene253-gltf-feature-extras-VHPteGay.js", "scene253-gltf-feature-lights-punctual-BD0mrLuD.js", "scene253-gltf-feature-morph-OgvZWqfo.js", - "scene253-gltf-feature-registry-DllfL8b_.js", + "scene253-gltf-feature-registry-B2fkk9xC.js", "scene253-gltf-feature-skeleton-BYmZVC5E.js", "scene253-gltf-json-asset-CPydyTRL.js", "scene253-gltf-light-pointer-state-B7kFTRxK.js", diff --git a/lab/public/bundle/manifest/scene254.json b/lab/public/bundle/manifest/scene254.json index 2e25b44cec..4da454ab35 100644 --- a/lab/public/bundle/manifest/scene254.json +++ b/lab/public/bundle/manifest/scene254.json @@ -1,12 +1,12 @@ { - "rawKB": 92.4, - "gzipKB": 38.4, + "rawKB": 92.6, + "gzipKB": 38.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene254-generate-mipmaps-CYwo5pXb.js", "scene254-gltf-animation-BROLRbEL.js", "scene254-gltf-feature-animations-XsSEzprH.js", - "scene254-gltf-feature-registry-swQjGwuy.js", + "scene254-gltf-feature-registry-CVmt1bu7.js", "scene254-gltf-json-asset-CpRpkx7j.js", "scene254-gltf-sampler-denorm-Bui_WQIw.js", "scene254-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene255.json b/lab/public/bundle/manifest/scene255.json index cc09d12f0d..cfb573cbb9 100644 --- a/lab/public/bundle/manifest/scene255.json +++ b/lab/public/bundle/manifest/scene255.json @@ -1,5 +1,5 @@ { - "rawKB": 94.1, + "rawKB": 94.3, "gzipKB": 39.5, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene255-generate-mipmaps-B7LdcXNp.js", "scene255-gltf-animation-BbNxs5BN.js", "scene255-gltf-feature-animations-Cd2IBM3n.js", - "scene255-gltf-feature-registry-BTGiXViM.js", + "scene255-gltf-feature-registry-C4D9gW7Q.js", "scene255-gltf-feature-skeleton-DHaKW6Xm.js", "scene255-gltf-json-asset-vxujGMAS.js", "scene255-gltf-normals-DYhLwXNX.js", diff --git a/lab/public/bundle/manifest/scene257.json b/lab/public/bundle/manifest/scene257.json index f5dea42556..ac8bda74d4 100644 --- a/lab/public/bundle/manifest/scene257.json +++ b/lab/public/bundle/manifest/scene257.json @@ -1,12 +1,12 @@ { - "rawKB": 80, + "rawKB": 80.1, "gzipKB": 33.1, "ignoredRawKB": 0, "runtimeChunks": [ "scene257-flat-normal-wgsl-Cm9mM4tC.js", "scene257-generate-mipmaps-C2jNwHZ4.js", "scene257-gltf-feature-primitive-BMqT3MMH.js", - "scene257-gltf-feature-registry-C3q-THmF.js", + "scene257-gltf-feature-registry-BHQCyd-Y.js", "scene257-gltf-json-asset-DAx_-Tyr.js", "scene257-gltf-normals-DYhLwXNX.js", "scene257-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene260.json b/lab/public/bundle/manifest/scene260.json index e34c80e848..6530907d97 100644 --- a/lab/public/bundle/manifest/scene260.json +++ b/lab/public/bundle/manifest/scene260.json @@ -1,12 +1,12 @@ { - "rawKB": 80, + "rawKB": 80.1, "gzipKB": 33.1, "ignoredRawKB": 0, "runtimeChunks": [ "scene260-flat-normal-wgsl-Cm9mM4tC.js", "scene260-generate-mipmaps-BDaYF730.js", "scene260-gltf-feature-primitive-BMqT3MMH.js", - "scene260-gltf-feature-registry-zt2COmYK.js", + "scene260-gltf-feature-registry-C_jWvWmz.js", "scene260-gltf-json-asset-B1CNeG6-.js", "scene260-gltf-normals-DYhLwXNX.js", "scene260-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene265.json b/lab/public/bundle/manifest/scene265.json new file mode 100644 index 0000000000..7a1c2f9c7c --- /dev/null +++ b/lab/public/bundle/manifest/scene265.json @@ -0,0 +1,16 @@ +{ + "rawKB": 80, + "gzipKB": 33.4, + "ignoredRawKB": 0, + "runtimeChunks": [ + "scene265-generate-mipmaps-B94XvMrD.js", + "scene265-gltf-ext-lights-image-based-Cy0y6Rcm.js", + "scene265-gltf-feature-registry-CBqzfUe8.js", + "scene265-gltf-json-asset-D5Ypa449.js", + "scene265-ibl-cubemap-upload-aeOmgodq.js", + "scene265-ibl-env-assembly-c2UHDMKi.js", + "scene265-ibl-fragment-CeRrvT96.js", + "scene265-pbr-renderable-BW7BE1kN.js", + "scene265.js" + ] +} diff --git a/lab/public/bundle/manifest/scene27.json b/lab/public/bundle/manifest/scene27.json index 7b40139d3c..c76dfa8ff7 100644 --- a/lab/public/bundle/manifest/scene27.json +++ b/lab/public/bundle/manifest/scene27.json @@ -1,11 +1,11 @@ { - "rawKB": 82.6, + "rawKB": 82.7, "gzipKB": 33.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene27-generate-mipmaps-CaLzDGPp.js", "scene27-gltf-ext-dielectric-C6hvXXiF.js", - "scene27-gltf-feature-registry-DpatAEqC.js", + "scene27-gltf-feature-registry-SQsTbQJ2.js", "scene27-gltf-feature-variants-BNhy8bR8.js", "scene27-gltf-glb-parser-By8noE3G.js", "scene27-gltf-pbr-builder-ext-OvOYi-mh.js", diff --git a/lab/public/bundle/manifest/scene28.json b/lab/public/bundle/manifest/scene28.json index 9a1cc27883..d44d97f384 100644 --- a/lab/public/bundle/manifest/scene28.json +++ b/lab/public/bundle/manifest/scene28.json @@ -1,12 +1,12 @@ { - "rawKB": 86.6, - "gzipKB": 35.3, + "rawKB": 86.7, + "gzipKB": 35.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene28-clearcoat-fragment-DOT6cYUU.js", "scene28-generate-mipmaps-7w1hCgCw.js", "scene28-gltf-ext-clearcoat-BgaLgSgD.js", - "scene28-gltf-feature-registry-DcWcd3j1.js", + "scene28-gltf-feature-registry-C6Rkd9pb.js", "scene28-gltf-json-asset-BJmxzYNI.js", "scene28-ibl-fragment-CeRrvT96.js", "scene28-pbr-renderable-CzHHWfVB.js", diff --git a/lab/public/bundle/manifest/scene29.json b/lab/public/bundle/manifest/scene29.json index 0d9a0b2728..9fbb0adf9e 100644 --- a/lab/public/bundle/manifest/scene29.json +++ b/lab/public/bundle/manifest/scene29.json @@ -1,12 +1,12 @@ { - "rawKB": 90.2, - "gzipKB": 37.3, + "rawKB": 90.3, + "gzipKB": 37.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene29-generate-mipmaps-DJCYSY1_.js", "scene29-gltf-ext-sheen-Dv23b_3T.js", "scene29-gltf-ext-uv-transform-tUSEOqA7.js", - "scene29-gltf-feature-registry-DZ6jUlur.js", + "scene29-gltf-feature-registry-CJkVEMgl.js", "scene29-gltf-json-asset-BCPycsr9.js", "scene29-gltf-pbr-builder-ext-CpjrBrd7.js", "scene29-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene30.json b/lab/public/bundle/manifest/scene30.json index 2c68605fa4..05aaa4761b 100644 --- a/lab/public/bundle/manifest/scene30.json +++ b/lab/public/bundle/manifest/scene30.json @@ -1,5 +1,5 @@ { - "rawKB": 103.7, + "rawKB": 103.8, "gzipKB": 42.9, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene30-gltf-ext-dielectric-C6hvXXiF.js", "scene30-gltf-ext-uv-transform-DQN5DAiE.js", "scene30-gltf-feature-draco-BSbw1ZBU.js", - "scene30-gltf-feature-registry-61PwWdOS.js", + "scene30-gltf-feature-registry-DdgFJ0nT.js", "scene30-gltf-glb-parser-DJ0cTJ-4.js", "scene30-gltf-pbr-builder-ext-B4o2oNmo.js", "scene30-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene31.json b/lab/public/bundle/manifest/scene31.json index 1493a08055..3da3066986 100644 --- a/lab/public/bundle/manifest/scene31.json +++ b/lab/public/bundle/manifest/scene31.json @@ -1,12 +1,12 @@ { - "rawKB": 80, - "gzipKB": 32.9, + "rawKB": 80.1, + "gzipKB": 33, "ignoredRawKB": 0, "runtimeChunks": [ "scene31-emissive-fragment-C1TNSo0w.js", "scene31-generate-mipmaps-CoCbB-UD.js", "scene31-gltf-ext-emissive-strength-CButLZnK.js", - "scene31-gltf-feature-registry-DbXHy-RL.js", + "scene31-gltf-feature-registry-COb_OD3O.js", "scene31-gltf-glb-parser-CzRZVo57.js", "scene31-ibl-fragment-CeRrvT96.js", "scene31-pbr-renderable-Du_q1nFe.js", diff --git a/lab/public/bundle/manifest/scene32.json b/lab/public/bundle/manifest/scene32.json index 721ef66536..dd44d48165 100644 --- a/lab/public/bundle/manifest/scene32.json +++ b/lab/public/bundle/manifest/scene32.json @@ -1,11 +1,11 @@ { - "rawKB": 79.8, + "rawKB": 79.9, "gzipKB": 32.9, "ignoredRawKB": 0, "runtimeChunks": [ "scene32-generate-mipmaps-DdnhjgK3.js", "scene32-gltf-ext-unlit-1vUYfF7v.js", - "scene32-gltf-feature-registry-DV4vP3Ve.js", + "scene32-gltf-feature-registry-BCljR88z.js", "scene32-gltf-glb-parser-BX2AS-bF.js", "scene32-ibl-fragment-CeRrvT96.js", "scene32-pbr-renderable-BZrTmnOw.js", diff --git a/lab/public/bundle/manifest/scene33.json b/lab/public/bundle/manifest/scene33.json index f8bb85ae12..96498aae0c 100644 --- a/lab/public/bundle/manifest/scene33.json +++ b/lab/public/bundle/manifest/scene33.json @@ -1,12 +1,12 @@ { - "rawKB": 103.7, + "rawKB": 103.8, "gzipKB": 42.7, "ignoredRawKB": 0, "runtimeChunks": [ "scene33-generate-mipmaps-Z_0Gnbub.js", "scene33-gltf-ext-dielectric-C6hvXXiF.js", "scene33-gltf-feature-lights-punctual-Bo3_Vssl.js", - "scene33-gltf-feature-registry-BJIjfGjc.js", + "scene33-gltf-feature-registry-C0aOXsyt.js", "scene33-gltf-glb-parser-DMBUHtjv.js", "scene33-gltf-light-pointer-state-B7kFTRxK.js", "scene33-gltf-sampler-desc-ChMUJ3dG.js", diff --git a/lab/public/bundle/manifest/scene34.json b/lab/public/bundle/manifest/scene34.json index cfac1112f3..94108f6768 100644 --- a/lab/public/bundle/manifest/scene34.json +++ b/lab/public/bundle/manifest/scene34.json @@ -1,5 +1,5 @@ { - "rawKB": 96.8, + "rawKB": 96.9, "gzipKB": 40, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene34-gltf-ext-node-visibility-DWsJZC5Q.js", "scene34-gltf-feature-animation-pointer-CE1BnN9s.js", "scene34-gltf-feature-animations-BoKR3Ok5.js", - "scene34-gltf-feature-registry-CJICailA.js", + "scene34-gltf-feature-registry-DJtUXEQr.js", "scene34-gltf-glb-parser-BxoFTGKn.js", "scene34-gltf-sampler-denorm-C_vs8OEN.js", "scene34-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene35.json b/lab/public/bundle/manifest/scene35.json index 6863602671..379b389009 100644 --- a/lab/public/bundle/manifest/scene35.json +++ b/lab/public/bundle/manifest/scene35.json @@ -1,11 +1,11 @@ { - "rawKB": 83.7, + "rawKB": 83.8, "gzipKB": 34.6, "ignoredRawKB": 0, "runtimeChunks": [ "scene35-generate-mipmaps-Dw2dswCB.js", "scene35-gltf-feature-gpu-instancing-D1uNjxTA.js", - "scene35-gltf-feature-registry-BByeuJvD.js", + "scene35-gltf-feature-registry-D5wdCRl6.js", "scene35-gltf-glb-parser-BJyme4Vk.js", "scene35-ibl-fragment-CeRrvT96.js", "scene35-pbr-renderable-BaquMftV.js", diff --git a/lab/public/bundle/manifest/scene37.json b/lab/public/bundle/manifest/scene37.json index 7685ccd4bd..9c5a10843e 100644 --- a/lab/public/bundle/manifest/scene37.json +++ b/lab/public/bundle/manifest/scene37.json @@ -1,5 +1,5 @@ { - "rawKB": 97, + "rawKB": 97.1, "gzipKB": 39.8, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene37-gltf-ext-dielectric-C6hvXXiF.js", "scene37-gltf-ext-sheen-Dv23b_3T.js", "scene37-gltf-ext-uv-transform-hVTzf5Q9.js", - "scene37-gltf-feature-registry-CsR7M1E5.js", + "scene37-gltf-feature-registry-tU68dbia.js", "scene37-gltf-glb-parser-CkbGcOCI.js", "scene37-gltf-pbr-builder-ext-rpTZqaAk.js", "scene37-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene39.json b/lab/public/bundle/manifest/scene39.json index 1f2f5f5816..d1171ad283 100644 --- a/lab/public/bundle/manifest/scene39.json +++ b/lab/public/bundle/manifest/scene39.json @@ -1,5 +1,5 @@ { - "rawKB": 108.8, + "rawKB": 108.9, "gzipKB": 46.1, "ignoredRawKB": 0, "runtimeChunks": [ @@ -12,7 +12,7 @@ "scene39-gltf-feature-animation-pointer-CLES_-mr.js", "scene39-gltf-feature-animations-eOJFbLQK.js", "scene39-gltf-feature-lights-punctual-5HuRS6Ip.js", - "scene39-gltf-feature-registry-CmrMeHhc.js", + "scene39-gltf-feature-registry-DWIeQH-G.js", "scene39-gltf-json-asset-BoHskl0t.js", "scene39-gltf-light-pointer-state-B7kFTRxK.js", "scene39-gltf-pbr-builder-ext-WVY5V9c8.js", diff --git a/lab/public/bundle/manifest/scene41.json b/lab/public/bundle/manifest/scene41.json index 8aa2cd343e..c7f40d0d4c 100644 --- a/lab/public/bundle/manifest/scene41.json +++ b/lab/public/bundle/manifest/scene41.json @@ -1,11 +1,11 @@ { - "rawKB": 93, - "gzipKB": 37, + "rawKB": 93.1, + "gzipKB": 37.1, "ignoredRawKB": 0, "runtimeChunks": [ "scene41-generate-mipmaps-CaoM8To3.js", "scene41-gltf-ext-spec-gloss-D60sFZYX.js", - "scene41-gltf-feature-registry-Dy_lcWPj.js", + "scene41-gltf-feature-registry-DPchpuak.js", "scene41-gltf-glb-parser-CkOP9avo.js", "scene41-gltf-sampler-desc-E_7RaQlV.js", "scene41-point-light-CnkkP9oB.js", diff --git a/lab/public/bundle/manifest/scene46.json b/lab/public/bundle/manifest/scene46.json index 0fcae7f034..fe5c08b798 100644 --- a/lab/public/bundle/manifest/scene46.json +++ b/lab/public/bundle/manifest/scene46.json @@ -1,6 +1,6 @@ { "rawKB": 54.4, - "gzipKB": 21.2, + "gzipKB": 21.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene46-standard-renderable-CRXAGvV-.js", diff --git a/lab/public/bundle/manifest/scene47.json b/lab/public/bundle/manifest/scene47.json index 1fe6043505..208cbb6d24 100644 --- a/lab/public/bundle/manifest/scene47.json +++ b/lab/public/bundle/manifest/scene47.json @@ -1,11 +1,11 @@ { - "rawKB": 92.4, - "gzipKB": 36.9, + "rawKB": 92.5, + "gzipKB": 37, "ignoredRawKB": 0, "runtimeChunks": [ "scene47-generate-mipmaps-DOnKpLaE.js", "scene47-gltf-ext-spec-gloss-D60sFZYX.js", - "scene47-gltf-feature-registry-CdyWFmG5.js", + "scene47-gltf-feature-registry-Cl-zcH-q.js", "scene47-gltf-glb-parser-6-tsF2C2.js", "scene47-gltf-sampler-desc-BJMmnJDF.js", "scene47-shader-composer-DishuYLa.js", diff --git a/lab/public/bundle/manifest/scene5.json b/lab/public/bundle/manifest/scene5.json index 3b5ed13b45..d62feafd7c 100644 --- a/lab/public/bundle/manifest/scene5.json +++ b/lab/public/bundle/manifest/scene5.json @@ -1,5 +1,5 @@ { - "rawKB": 91.9, + "rawKB": 92, "gzipKB": 38.9, "ignoredRawKB": 0, "runtimeChunks": [ @@ -10,7 +10,7 @@ "scene5-gltf-color-normalize-4-WGzPL2.js", "scene5-gltf-feature-animations-Db-wL1ps.js", "scene5-gltf-feature-morph-BNcyLEaQ.js", - "scene5-gltf-feature-registry-DXE8nKBz.js", + "scene5-gltf-feature-registry-CtefggdW.js", "scene5-gltf-feature-skeleton-BYbOC8HD.js", "scene5-gltf-json-asset-DXOveXY1.js", "scene5-morph-fragment-CAPye0MH.js", diff --git a/lab/public/bundle/manifest/scene7.json b/lab/public/bundle/manifest/scene7.json index 500b92f3ff..4baa72fd97 100644 --- a/lab/public/bundle/manifest/scene7.json +++ b/lab/public/bundle/manifest/scene7.json @@ -1,5 +1,5 @@ { - "rawKB": 105.3, + "rawKB": 105.4, "gzipKB": 44.6, "ignoredRawKB": 0, "runtimeChunks": [ @@ -10,7 +10,7 @@ "scene7-gltf-animation-RP7A9LpW.js", "scene7-gltf-feature-animations-C9A-KG60.js", "scene7-gltf-feature-extras-VHPteGay.js", - "scene7-gltf-feature-registry-pKXLy51C.js", + "scene7-gltf-feature-registry-DY1Jo4GK.js", "scene7-gltf-feature-skeleton-DaFXylO1.js", "scene7-gltf-json-asset-DelZuh7Y.js", "scene7-ibl-fragment-CeRrvT96.js", diff --git a/lab/public/bundle/manifest/scene73.json b/lab/public/bundle/manifest/scene73.json index 5a0d955c22..dc1cb7e81f 100644 --- a/lab/public/bundle/manifest/scene73.json +++ b/lab/public/bundle/manifest/scene73.json @@ -1,5 +1,5 @@ { - "rawKB": 143.6, + "rawKB": 143.7, "gzipKB": 58.1, "ignoredRawKB": 3.3, "runtimeChunks": [ @@ -10,7 +10,7 @@ "scene73-fragment-output-CfRmfbPv.js", "scene73-generate-mipmaps-DcnO72FN.js", "scene73-gltf-ext-clearcoat-BgaLgSgD.js", - "scene73-gltf-feature-registry-BHYeIm-u.js", + "scene73-gltf-feature-registry-CSNKAtfS.js", "scene73-gltf-glb-parser-Bm1IrZjT.js", "scene73-ibl-fragment-CeRrvT96.js", "scene73-input-block-lrbZBZJO.js", diff --git a/lab/public/bundle/manifest/scene99.json b/lab/public/bundle/manifest/scene99.json index cfab1f7fbb..1b3c672760 100644 --- a/lab/public/bundle/manifest/scene99.json +++ b/lab/public/bundle/manifest/scene99.json @@ -1,5 +1,5 @@ { - "rawKB": 92, + "rawKB": 92.1, "gzipKB": 38.3, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene99-generate-mipmaps-BFyY2NRa.js", "scene99-gltf-animation-T1Ovedwq.js", "scene99-gltf-feature-animations-CIfvZioj.js", - "scene99-gltf-feature-registry-Cnxn65i3.js", + "scene99-gltf-feature-registry-BrSEYdFf.js", "scene99-gltf-feature-skeleton-BUITWXe0.js", "scene99-gltf-glb-parser-BtneRupN.js", "scene99-multilight-wgsl-Cni0HYFi.js", diff --git a/lab/public/thumbnails/scene265.jpg b/lab/public/thumbnails/scene265.jpg new file mode 100644 index 0000000000..74b6bec7f7 Binary files /dev/null and b/lab/public/thumbnails/scene265.jpg differ diff --git a/packages/babylon-lite/src/asset-container.ts b/packages/babylon-lite/src/asset-container.ts index d2760e610a..524c027299 100644 --- a/packages/babylon-lite/src/asset-container.ts +++ b/packages/babylon-lite/src/asset-container.ts @@ -4,6 +4,7 @@ import type { AnimationGroup } from "./animation/animation-group.js"; import type { MaterialVariantData } from "./loader-gltf/material-variants.js"; import type { Mesh } from "./mesh/mesh.js"; import type { Skeleton } from "./skeleton/bone-control.js"; +import type { SceneContext } from "./scene/scene-core.js"; /** * Result returned by loadGltf / loadBabylon. @@ -30,6 +31,14 @@ export interface AssetContainer { * `enableBoneControl()` was called before loading; otherwise `undefined`. * Drive bones via `getBoneByName()` + the `setBone*` functions. */ skeletons?: Skeleton[]; + /** Deferred scene-wiring hook contributed by a loader feature that needs the + * target `SceneContext` (which the loader itself never sees). `addToScene()` + * invokes it once, synchronously, while processing the container. Used by + * `EXT_lights_image_based` to install its image-based-light environment + * (spherical harmonics + specular cubemap) onto the scene. Lazy features own + * the closure so the core loader/scene stay feature-agnostic. + * @internal */ + _sceneSetup?: (scene: SceneContext) => void; } /** diff --git a/packages/babylon-lite/src/loader-gltf/gltf-ext-lights-image-based.ts b/packages/babylon-lite/src/loader-gltf/gltf-ext-lights-image-based.ts new file mode 100644 index 0000000000..ac9855ecd3 --- /dev/null +++ b/packages/babylon-lite/src/loader-gltf/gltf-ext-lights-image-based.ts @@ -0,0 +1,175 @@ +/** EXT_lights_image_based glTF extension. + * + * Installs a document-level image-based light (diffuse irradiance SH9 + a set of + * prefiltered specular mip cubemaps, plus intensity/rotation) referenced by the + * active scene, driving the PBR IBL through Lite's existing environment path. + * + * Reuse (KISS, maximum reuse per GUIDANCE): + * - Specular mips are RGBD-encoded PNGs (identical encoding to Babylon `.env` + * faces), so they upload through the shared `uploadCubemapRGBD` decoder. + * - The BRDF split-sum LUT is generated procedurally on-GPU (`generateBrdfLut`, + * shared with the HDR loader) — no external asset URL is needed. + * - The final `EnvironmentTextures` are assembled with `assembleEnvironmentTextures` + * and wired onto the scene exactly like `loadEnvironment` / `loadHdrEnvironment` + * (`scene._envTextures` + `registerEnvSceneUniforms` + tone-mapping defaults). + * + * The glTF `irradianceCoefficients` are raw SH9 coefficients. Babylon converts + * them SH → intensity scale → Lambertian radiance (×1/π) → SphericalPolynomial, + * and its shader later expands that polynomial to the pre-scaled harmonics the + * scene UBO consumes. We reproduce the SH→polynomial step here so the shared + * `polynomialToPreScaledHarmonics` (inside `assembleEnvironmentTextures`) yields + * byte-for-byte the same rendering coefficients as Babylon. + */ + +import type { GltfFeature } from "./gltf-feature.js"; +import type { SceneContext } from "../scene/scene-core.js"; + +// NOTE: this module is a lazily-imported feature chunk (see gltf-feature-registry) +// and keeps ZERO static runtime imports — every dependency is pulled in via dynamic +// import inside applyAsset. Scenes that never trigger EXT_lights_image_based never +// fetch this chunk (or its dependencies) at runtime. The heavy IBL helpers it reuses +// (cubemap upload, env-texture assembly, BRDF LUT, SH conversion, samplers) are kept +// as PRIVATE copies (ibl-cubemap-upload.js + ibl-env-assembly.js) rather than shared +// with loader-env / loader-hdr. This is a deliberate, user-approved exception to the +// max-reuse mandate: those canonical helpers are single-consumer and thus inlined +// into every glTF scene's main chunk; importing them here would make this feature a +// second consumer and force rollup to hoist them into shared chunks that all 44 glTF +// scenes would pay for. The private copies isolate the feature's byte cost to the one +// scene that uses it. See ibl-env-assembly.js for the canonical-source mapping. + +interface GltfImageBasedLight { + name?: string; + intensity?: number; + rotation?: [number, number, number, number]; + irradianceCoefficients?: number[][]; + specularImageSize: number; + specularImages: number[][]; +} + +/** Convert glTF `EXT_lights_image_based` SH9 irradiance coefficients into the + * 27-float spherical-polynomial layout `[x,y,z,xx,yy,zz,yz,zx,xy]` that + * `assembleEnvironmentTextures` → `polynomialToPreScaledHarmonics` expects. + * + * Mirrors Babylon's chain: `SphericalHarmonics.FromArray(coeffs)` → + * `scaleInPlace(intensity)` → `convertIrradianceToLambertianRadiance()` (×1/π) → + * `SphericalPolynomial.FromHarmonics()` (`updateFromHarmonics`, which folds a + * final ×1/π). All band constants are copied from Babylon `sphericalPolynomial`. */ +function irradianceCoefficientsToPolynomial(coeffs: number[][], intensity: number): Float32Array { + // Harmonic scale = intensity (radiance) × 1/π (Lambertian normalisation). + const s = intensity / Math.PI; + const poly = new Float32Array(27); + for (let c = 0; c < 3; c++) { + const l00 = coeffs[0]![c]! * s; + const l1_1 = coeffs[1]![c]! * s; + const l10 = coeffs[2]![c]! * s; + const l11 = coeffs[3]![c]! * s; + const l2_2 = coeffs[4]![c]! * s; + const l2_1 = coeffs[5]![c]! * s; + const l20 = coeffs[6]![c]! * s; + const l21 = coeffs[7]![c]! * s; + const l22 = coeffs[8]![c]! * s; + + // updateFromHarmonics(), then the trailing ×(1/π). + const k = 1 / Math.PI; + poly[0 + c] = -1.02333 * l11 * k; // x + poly[3 + c] = -1.02333 * l1_1 * k; // y + poly[6 + c] = 1.02333 * l10 * k; // z + poly[9 + c] = (0.886277 * l00 - 0.247708 * l20 + 0.429043 * l22) * k; // xx + poly[12 + c] = (0.886277 * l00 - 0.247708 * l20 - 0.429043 * l22) * k; // yy + poly[15 + c] = (0.886277 * l00 + 0.495417 * l20) * k; // zz + poly[18 + c] = -0.858086 * l2_1 * k; // yz + poly[21 + c] = -0.858086 * l21 * k; // zx + poly[24 + c] = 0.858086 * l2_2 * k; // xy + } + return poly; +} + +/** Extract the Y-axis rotation angle (radians) from an image-based-light + * quaternion. The extension's rotations are pure Y-yaw; Lite drives environment + * yaw through `scene.envRotationY`. Babylon inverts the rotation for its + * left-handed target scene, so we negate to match its sampling direction. */ +function envYawFromQuaternion(q: [number, number, number, number]): number { + return -2 * Math.atan2(q[1], q[3]); +} + +const feature: GltfFeature = { + id: "EXT_lights_image_based", + async applyAsset(_meshes, _root, ctx) { + const json = ctx._json; + const lights: GltfImageBasedLight[] | undefined = json.extensions?.EXT_lights_image_based?.lights; + const sceneDef = json.scenes?.[json.scene ?? 0]; + const lightIdx: number | undefined = sceneDef?.extensions?.EXT_lights_image_based?.light; + if (!lights?.length || lightIdx === undefined) { + return {}; + } + const light = lights[lightIdx]; + if (!light?.specularImages?.length || !light.irradianceCoefficients) { + return {}; + } + + const engine = ctx._engine; + const specularImageSize = light.specularImageSize; + const mipCount = light.specularImages.length; + + // Decode every specular face (flat, mip-major face-minor — the order + // uploadCubemapRGBD expects; glTF face order +X,-X,+Y,-Y,+Z,-Z matches + // WebGPU cube layers). + const flatIndices = light.specularImages.flat(); + const { resolveImage } = await import("./ibl-env-assembly.js"); + const faceImages = await Promise.all(flatIndices.map((imgIdx) => resolveImage(json, ctx._binChunk, imgIdx, ctx._baseUrl))); + + const { uploadCubemapRGBD } = await import("./ibl-cubemap-upload.js"); + const specularCube = uploadCubemapRGBD(engine, faceImages, specularImageSize, mipCount); + for (const img of faceImages) { + img.close(); + } + + const { generateBrdfLut } = await import("./ibl-env-assembly.js"); + const brdfLut = generateBrdfLut(engine); + + const intensity = light.intensity ?? 1; + const irradianceSH = irradianceCoefficientsToPolynomial(light.irradianceCoefficients, intensity); + // Fit the LOD scale to the available mip count exactly as Babylon does. + const lodGenerationScale = (mipCount - 1) / Math.log2(specularImageSize); + + const { assembleEnvironmentTextures } = await import("./ibl-env-assembly.js"); + const textures = assembleEnvironmentTextures(specularCube, brdfLut, irradianceSH, lodGenerationScale, engine); + + const envRotationY = light.rotation ? envYawFromQuaternion(light.rotation) : 0; + + // Pull the scene-wiring helper in now (still lazy — part of this feature + // chunk) so the synchronous _sceneSetup closure can use it directly. + // registerEnvSceneUniforms is a private copy (see ibl-env-assembly) to + // avoid pinning scene-ubo-extras into every glTF scene. + const { registerEnvSceneUniforms } = await import("./ibl-env-assembly.js"); + + // Defer the scene wiring: applyAsset has no SceneContext, so hand the core + // loader a closure that addToScene() runs against the real scene. + const _sceneSetup = (scene: SceneContext): void => { + scene._envTextures = textures; + if (envRotationY) { + scene.envRotationY = envRotationY; + } + registerEnvSceneUniforms(scene); + + // specularCube + brdfLut are created fresh here and owned solely by + // this scene (nothing else acquires them), so a direct destroy on + // scene teardown is equivalent to the gpu-pool refcount path used by + // load-env — and it avoids pinning gpu-pool's exports into every + // glTF scene's main chunk, keeping the 43 non-IBL scenes at baseline. + scene._disposables.push(() => { + specularCube.destroy(); + brdfLut.destroy(); + }); + + // Match loadEnvironment's image-processing defaults (tone mapping on, + // exposure 0.8, contrast 1.2) so IBL-lit output matches Babylon. + scene.imageProcessing.toneMappingEnabled = true; + scene.imageProcessing.exposure = 0.8; + scene.imageProcessing.contrast = 1.2; + }; + + return { _sceneSetup }; + }, +}; +export default feature; diff --git a/packages/babylon-lite/src/loader-gltf/gltf-feature-registry.ts b/packages/babylon-lite/src/loader-gltf/gltf-feature-registry.ts index fd9165500f..c387dc5c6d 100644 --- a/packages/babylon-lite/src/loader-gltf/gltf-feature-registry.ts +++ b/packages/babylon-lite/src/loader-gltf/gltf-feature-registry.ts @@ -59,6 +59,7 @@ const _features: [Trigger, Loader][] = [ // Per-asset features [hasGltfExtras, () => import("./gltf-feature-extras.js")], ["KHR_lights_punctual", () => import("./gltf-feature-lights-punctual.js")], + ["EXT_lights_image_based", () => import("./gltf-ext-lights-image-based.js")], [(j) => !!j.animations?.length, () => import("./gltf-feature-animations.js")], // Non-Float32 / normalized animation sampler accessors (e.g. Animation_SamplerType normalized // BYTE/SHORT rotation) need the lazy denorm converter; plain float samplers never load it. diff --git a/packages/babylon-lite/src/loader-gltf/ibl-cubemap-upload.ts b/packages/babylon-lite/src/loader-gltf/ibl-cubemap-upload.ts new file mode 100644 index 0000000000..f3bcc8f401 --- /dev/null +++ b/packages/babylon-lite/src/loader-gltf/ibl-cubemap-upload.ts @@ -0,0 +1,100 @@ +/** RGBD cubemap uploader for EXT_lights_image_based. + * + * A self-contained copy of the RGBD `->` rgba16float cubemap decode used by the + * image-based-lights extension. It is intentionally NOT shared with + * `loader-env/rgbd-decode.ts`: that module is a static dependency of the `.env` + * loader (`load-env.ts`) and is therefore inlined into every environment scene's + * chunk graph. Importing `uploadCubemapRGBD` from it would make the extension a + * second consumer of that shared module, pinning the (otherwise tree-shaken) + * cubemap path into every `.env`/DDS scene's bundle. Keeping a private copy here + * — reachable only from this lazily-imported feature chunk — means non-IBL scenes + * pay zero bytes for it and only `EXT_lights_image_based` scenes fetch it. */ + +import { TU } from "../engine/gpu-flags.js"; +import type { EngineContext } from "../engine/engine.js"; + +const WGSL = `override f:bool=false;@group(0)@binding(0)var t:texture_2d;@group(0)@binding(1)var o:texture_storage_2d;@compute @workgroup_size(8,8)fn main(@builtin(global_invocation_id)g:vec3u){let d=textureDimensions(t);if(any(g.xy>=d)){return;}let c=textureLoad(t,vec2u(g.x,select(g.y,d.y-1u-g.y,f)),0);textureStore(o,g.xy,vec4f(pow(c.rgb,vec3f(2.2))/max(c.a,1.0/255.0),1));}`; + +let _device: GPUDevice | null = null; +let _module: GPUShaderModule | null = null; +let _pipeline: GPUComputePipeline | null = null; + +function getPipeline(device: GPUDevice): GPUComputePipeline { + if (device !== _device) { + _device = device; + _module = device.createShaderModule({ code: WGSL }); + _pipeline = null; + } + if (_pipeline) { + return _pipeline; + } + _pipeline = device.createComputePipeline({ + layout: "auto", + compute: { module: _module!, entryPoint: "main", constants: { f: 1 } }, + }); + return _pipeline; +} + +/** Decode and upload a RGBD cubemap (6 faces × N mips) → rgba16float cube texture. + * Y-flipped on read (BJS uploads cubemap faces with invertY=true). */ +export function uploadCubemapRGBD(engine: EngineContext, images: ImageBitmap[], width: number, mipCount: number): GPUTexture { + const device = engine._device; + const pipeline = getPipeline(device); + + const texture = device.createTexture({ + size: { width, height: width, depthOrArrayLayers: 6 }, + format: "rgba16float", + mipLevelCount: mipCount, + usage: TU.TEXTURE_BINDING | TU.COPY_DST | TU.COPY_SRC | TU.RENDER_ATTACHMENT, + dimension: "2d", + }); + + for (let mip = 0; mip < mipCount; mip++) { + const mipSize = Math.max(1, width >> mip); + + const inputTex = device.createTexture({ + size: { width: mipSize, height: mipSize }, + format: "rgba8unorm", + usage: TU.TEXTURE_BINDING | TU.COPY_DST | TU.RENDER_ATTACHMENT, + }); + + const outputTex = device.createTexture({ + size: { width: mipSize, height: mipSize }, + format: "rgba16float", + usage: TU.STORAGE_BINDING | TU.COPY_SRC, + }); + + const bindGroup = device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: inputTex.createView() }, + { binding: 1, resource: outputTex.createView() }, + ], + }); + + for (let face = 0; face < 6; face++) { + const idx = mip * 6 + face; + if (idx >= images.length) { + break; + } + + device.queue.copyExternalImageToTexture({ source: images[idx]!, flipY: false }, { texture: inputTex, premultipliedAlpha: false }, { width: mipSize, height: mipSize }); + + const encoder = device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.dispatchWorkgroups(Math.ceil(mipSize / 8), Math.ceil(mipSize / 8)); + pass.end(); + encoder.copyTextureToTexture({ texture: outputTex }, { texture, origin: { x: 0, y: 0, z: face }, mipLevel: mip }, { width: mipSize, height: mipSize }); + + // One submit per face ensures sequential hazards on the reused input/output. + device.queue.submit([encoder.finish()]); + } + + inputTex.destroy(); + outputTex.destroy(); + } + + return texture; +} diff --git a/packages/babylon-lite/src/loader-gltf/ibl-env-assembly.ts b/packages/babylon-lite/src/loader-gltf/ibl-env-assembly.ts new file mode 100644 index 0000000000..bacf93a280 --- /dev/null +++ b/packages/babylon-lite/src/loader-gltf/ibl-env-assembly.ts @@ -0,0 +1,180 @@ +/** Private IBL assembly helpers for the EXT_lights_image_based feature. + * + * ⚠️ DELIBERATE DUPLICATION — user-approved exception to the max-reuse mandate. + * + * The functions below are byte-for-byte copies of shared environment/HDR IBL + * helpers whose canonical homes are: + * - sampler descriptors (bilinear/trilinear) → ../resource/samplers.ts + * - polynomialToPreScaledHarmonics → ../loader-env/load-env.ts + * - assembleEnvironmentTextures → ../loader-env/env-helpers.ts + * - generateBrdfLut → ../loader-hdr/hdr-ibl-pipeline.ts + * - resolveImage → ./gltf-parser.ts + * - registerEnvSceneUniforms (+ its writer) → ../scene/scene-ubo-extras.ts + * + * WHY the copy exists: those canonical modules are each SINGLE-consumer on + * master (env-helpers ← load-env, generateBrdfLut ← load-hdr, samplers ← + * generate-mipmaps), so rollup INLINES them into every glTF scene's main chunk. + * If the (lazily-imported) EXT_lights_image_based feature imported them directly + * it would become a SECOND consumer, forcing rollup to hoist each into a shared + * chunk that all 44 glTF scenes then fetch separately (~0.5–1.0 KB of chunk + * boilerplate per scene, ~32 KB total across the suite). By duplicating them + * here — reachable only from this lazy feature chunk — the 43 non-IBL glTF + * scenes keep their original inlined copies untouched (zero bundle delta) and + * the feature's cost is isolated to the single scene that uses it + * (EnvironmentTest / scene264), whose ceiling absorbs the ~few KB. + * + * If the canonical helpers change, update these copies to match (they exist + * purely to keep the feature self-contained, not to diverge in behaviour). + */ + +import { F32 } from "../engine/typed-arrays.js"; +import { TU } from "../engine/gpu-flags.js"; +import brdfLutWGSL from "../../shaders/hdr-brdf-lut.compute.wgsl?raw"; +import type { EngineContext } from "../engine/engine.js"; +import type { EnvironmentTextures } from "../loader-env/load-env.js"; +import type { SceneContext } from "../scene/scene-core.js"; + +// Sampler descriptors copied from resource/samplers.ts. Created directly here +// (device.createSampler) rather than via gpu-pool.getOrCreateSampler so this +// feature does not add getOrCreateSampler to gpu-pool's retained export set in +// every glTF scene. The only cost is two extra (immutable, stateless) sampler +// objects on the single IBL load — negligible and side-effect-free. +const _bilinearDesc: GPUSamplerDescriptor = { magFilter: "linear", minFilter: "linear" }; +const _trilinearDesc: GPUSamplerDescriptor = { magFilter: "linear", minFilter: "linear", mipmapFilter: "linear" }; + +/** Copy of loader-env/load-env.ts `polynomialToPreScaledHarmonics`. */ +function polynomialToPreScaledHarmonics(poly: Float32Array): Float32Array { + const C00xy = 0.3333338747897695; + const C00z = 0.33333298856284405; + const C1 = 1.4999984284682104; + const C2 = 3.999982863580422; + const C20zz = 1.3333326611423701; + const C20xy = 0.6666653397393608; + const C22 = 1.999991431790211; + + const out = new F32(36); + for (let i = 0; i < 3; i++) { + const x = poly[i]!; + const y = poly[3 + i]!; + const z = poly[6 + i]!; + const xx = poly[9 + i]!; + const yy = poly[12 + i]!; + const zz = poly[15 + i]!; + const yz = poly[18 + i]!; + const zx = poly[21 + i]!; + const xy = poly[24 + i]!; + out[i] = (xx + yy) * C00xy + zz * C00z; // L00 + out[4 + i] = y * C1; // L1_1 + out[8 + i] = z * C1; // L10 + out[12 + i] = x * C1; // L11 + out[16 + i] = xy * C2; // L2_2 + out[20 + i] = yz * C2; // L2_1 + out[24 + i] = zz * C20zz - (xx + yy) * C20xy; // L20 + out[28 + i] = zx * C2; // L21 + out[32 + i] = (xx - yy) * C22; // L22 + } + return out; +} + +/** Copy of loader-env/env-helpers.ts `assembleEnvironmentTextures`. */ +export function assembleEnvironmentTextures( + specularCube: GPUTexture, + brdfLut: GPUTexture, + irradianceSH: Float32Array, + lodGenerationScale: number, + engine: EngineContext +): EnvironmentTextures { + return { + specularCube, + specularCubeView: specularCube.createView({ dimension: "cube" }), + brdfLut, + brdfLutView: brdfLut.createView(), + cubeSampler: engine._device.createSampler(_trilinearDesc), + brdfSampler: engine._device.createSampler(_bilinearDesc), + irradianceSH, + sphericalHarmonics: polynomialToPreScaledHarmonics(irradianceSH), + lodGenerationScale, + }; +} + +// Copy of loader-hdr/hdr-ibl-pipeline.ts `generateBrdfLut` (split-sum BRDF LUT). +let _brdfPipeline: GPUComputePipeline | null = null; +let _brdfPipelineDevice: GPUDevice | null = null; + +/** Copy of loader-hdr/hdr-ibl-pipeline.ts `generateBrdfLut`. */ +export function generateBrdfLut(engine: EngineContext): GPUTexture { + const device = engine._device; + if (!_brdfPipeline || _brdfPipelineDevice !== device) { + _brdfPipeline = device.createComputePipeline({ + layout: "auto", + compute: { module: device.createShaderModule({ code: brdfLutWGSL }), entryPoint: "main" }, + }); + _brdfPipelineDevice = device; + } + const size = 256; + const texture = device.createTexture({ + size: { width: size, height: size }, + format: "rgba16float", + usage: TU.TEXTURE_BINDING | TU.STORAGE_BINDING, + }); + const bindGroup = device.createBindGroup({ + layout: _brdfPipeline.getBindGroupLayout(0), + entries: [{ binding: 0, resource: texture.createView() }], + }); + const encoder = device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.setPipeline(_brdfPipeline); + pass.setBindGroup(0, bindGroup); + pass.dispatchWorkgroups(Math.ceil(size / 8), Math.ceil(size / 8)); + pass.end(); + device.queue.submit([encoder.finish()]); + return texture; +} + +/** Copy of gltf-parser.ts `resolveImage` — resolves a glTF image (GLB bufferView + * or external URI) to an ImageBitmap. Kept private so this feature does not pin + * gltf-parser's export surface into every glTF scene's main chunk. */ +export async function resolveImage(json: any, binChunk: DataView, imageIdx: number, baseUrl: string): Promise { + const image = json.images[imageIdx]; + + if (image.bufferView !== undefined) { + const bv = json.bufferViews[image.bufferView]; + const offset = binChunk.byteOffset + (bv.byteOffset ?? 0); + const slice = binChunk.buffer.slice(offset, offset + bv.byteLength); + const blob = new Blob([slice as ArrayBuffer], { type: image.mimeType ?? "image/png" }); + return createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }); + } + + if (image.uri) { + const imageUrl = new URL(image.uri, baseUrl + "x").href; + const response = await fetch(imageUrl); + if (!response.ok) { + throw new Error(`Failed to load image: ${response.status} ${response.statusText}`); + } + const blob = await response.blob(); + return createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }); + } + + throw new Error("Image has neither bufferView nor uri"); +} + +/** Copy of scene-ubo-extras.ts `writeEnvShUbo` — writes the environment + * spherical-harmonics slice (float offsets 40–75) of the SceneUniforms struct. */ +function writeEnvShUbo(data: Float32Array, scene: SceneContext): void { + const sh = scene._envTextures?.sphericalHarmonics; + if (sh) { + data.set(sh, 40); + } +} + +/** Copy of scene-ubo-extras.ts `registerEnvSceneUniforms` (+ its dedup helper) — + * registers the env-SH scene-UBO contributor. The render task invokes whatever + * contributors are on `scene._sceneUboContributors`, so a private copy of the + * writer is functionally identical to the shared one. Kept private so this + * feature does not pin scene-ubo-extras into every glTF scene's main chunk. */ +export function registerEnvSceneUniforms(scene: SceneContext): void { + const list = (scene._sceneUboContributors ??= []); + if (!list.includes(writeEnvShUbo)) { + list.push(writeEnvShUbo); + } +} diff --git a/packages/babylon-lite/src/loader-gltf/load-gltf.ts b/packages/babylon-lite/src/loader-gltf/load-gltf.ts index 26c654695c..97fd8d340c 100644 --- a/packages/babylon-lite/src/loader-gltf/load-gltf.ts +++ b/packages/babylon-lite/src/loader-gltf/load-gltf.ts @@ -180,16 +180,27 @@ export async function loadGltf(engine: EngineContext, source: string | ArrayBuff // Run every feature's per-asset hook (animations, variants, metadata, …) and // merge the returned AssetContainer fragments. `entities` is appended (never // overwritten) so features like KHR_lights_punctual can contribute lights - // without trampling the root TransformNode. + // without trampling the root TransformNode. `_sceneSetup` is composed (chained + // in feature order) rather than overwritten, so multiple features can each + // contribute deferred scene wiring without the last one winning. const assetFragments = await Promise.all(features.flatMap((f) => (f.applyAsset ? [f.applyAsset(meshes, root, ctx)] : []))); const container: AssetContainer = { entities: [root] }; for (const frag of assetFragments) { if (frag.entities?.length) { container.entities.push(...frag.entities); } - const { entities: _ignored, ...rest } = frag; + const { entities: _ignored, _sceneSetup, ...rest } = frag; void _ignored; Object.assign(container, rest); + if (_sceneSetup) { + const prev = container._sceneSetup; + container._sceneSetup = prev + ? (scene) => { + prev(scene); + _sceneSetup(scene); + } + : _sceneSetup; + } } return container; } diff --git a/packages/babylon-lite/src/scene/scene-core.ts b/packages/babylon-lite/src/scene/scene-core.ts index 6402eeb080..f71e37cfc9 100644 --- a/packages/babylon-lite/src/scene/scene-core.ts +++ b/packages/babylon-lite/src/scene/scene-core.ts @@ -332,6 +332,10 @@ export function addToScene(scene: SceneContext, entity: Mesh | LightBase | Camer } }); } + // Feature-owned scene wiring (e.g. EXT_lights_image_based installs its IBL + // environment). Runs synchronously so the environment is registered before + // registerScene() builds the scene UBO / PBR renderables. + result._sceneSetup?.(ctx); return; } if ("_gpu" in entity && "material" in entity) { diff --git a/reference/lite/scene265-environment-test/babylon-ref-golden.png b/reference/lite/scene265-environment-test/babylon-ref-golden.png new file mode 100644 index 0000000000..564e10fe54 Binary files /dev/null and b/reference/lite/scene265-environment-test/babylon-ref-golden.png differ diff --git a/scene-config.json b/scene-config.json index 854229b071..6fee3735a9 100644 --- a/scene-config.json +++ b/scene-config.json @@ -122,7 +122,7 @@ "slug": "scene12-shader-balls", "name": "Scene 12 — PBR Shader Balls", "maxMad": 0.027, - "maxRawKB": 103.6, + "maxRawKB": 103.7, "description": "3 shader balls with metallic reflectance textures, directional light, env rotation.", "tags": ["pbr", "gltf"], "skipPerf": true @@ -286,7 +286,7 @@ "slug": "scene28-clearcoat-gltf", "name": "Scene 28 — Clearcoat glTF", "maxMad": 0.02, - "maxRawKB": 86.7, + "maxRawKB": 86.8, "description": "ClearCoatTest.gltf (6×3 grid: clearcoat variants) with default environment (IBL only). Matches PG #YG3BBF#33.", "tags": ["pbr", "gltf", "env", "clearcoat"], "compatParity": true @@ -362,7 +362,7 @@ "slug": "scene35-gltf-instancing", "name": "Scene 35 — EXT_mesh_gpu_instancing", "maxMad": 0.02, - "maxRawKB": 83.8, + "maxRawKB": 83.9, "description": "SimpleInstancing.glb (EXT_mesh_gpu_instancing) — 5×5×5 grid of 125 unit cubes driven by per-instance TRS accessors. Default IBL env. Matches PG #YG3BBF#57.", "tags": ["pbr", "gltf", "env", "gpu-instancing"], "compatParity": true @@ -404,7 +404,7 @@ "slug": "scene39-animation-pointer-waterfall", "name": "Scene 39 — KHR_animation_pointer (Animated Waterfall)", "maxMad": 0.6, - "maxRawKB": 108.9, + "maxRawKB": 109, "description": "AnimatedWaterfall.gltf — grass blades rotate via /nodes/{n}/rotation pointers and water/foam surfaces scroll via /materials/{m}/.../KHR_texture_transform offset+scale pointers. Default IBL env, no skybox/ground. Validates KHR_animation_pointer node-TRS + texture-transform handlers.", "tags": ["pbr", "gltf", "env", "lights-punctual", "texture-transform", "animation-pointer"], "skipPerf": true, @@ -436,7 +436,7 @@ "slug": "scene41-physics-shapes", "name": "Scene 41 — Physics Shape Debug Viewer", "maxMad": 10, - "maxRawKB": 93.2, + "maxRawKB": 93.4, "maxGzipKB": 36, "description": "Havok Physics V2 mesh, convex hull, and aggregate collision shapes with PhysicsViewer wireframes. Loaded skull and seagull port of PG #LKPBW5.", "tags": ["std", "procedural", "physics"], @@ -502,7 +502,7 @@ "slug": "scene47-physics-heightfield", "name": "Scene 47 — Physics Heightfield", "maxMad": 0.05, - "maxRawKB": 92.5, + "maxRawKB": 92.7, "maxGzipKB": 35, "description": "Havok Physics V2 heightfield from heightMap.png with two rows of falling shapes: row 1 PhysicsAggregate all types (box, sphere, capsule, cylinder, plus convex hull and mesh from the seagull glb), row 2 body+shape all types including convex hull and mesh from the seagull glb.", "tags": ["physics", "gltf", "procedural", "std"], @@ -1188,7 +1188,7 @@ "slug": "scene115-alien-picking-frame100", "name": "Scene 115 — Alien Picking Frame 100", "maxMad": 0.01, - "maxRawKB": 126.5, + "maxRawKB": 126.6, "description": "Precise picking validation on the animated Alien glTF at frame 100: BJS and Lite CPU PickInfo values drive a surface sphere and normal marker.", "tags": ["pbr", "gltf", "anim", "skeleton", "picking"], "skipPerf": true @@ -1456,7 +1456,7 @@ "slug": "scene152-gltf-manager", "name": "Scene 152 — Unified AnimationManager", "maxMad": 0.02, - "maxRawKB": 94.2, + "maxRawKB": 94.3, "description": "One explicit AnimationManager drives a glTF shark skeleton group and a manual camera orbit property group.", "tags": ["pbr", "gltf", "anim"], "skipPerf": true, @@ -1594,7 +1594,7 @@ "slug": "scene165-shader-material-thin-instances", "name": "Scene 165 - ShaderMaterial Thin Instances", "maxMad": 0.01, - "maxRawKB": 45.2, + "maxRawKB": 45.3, "description": "Custom WGSL ShaderMaterial rendered with thin instances + per-instance color: an 8×8×8 grid of unit cubes with a deterministic color ramp, fixed arc-rotate camera. Validates the auto-injected world0..3/instanceColor vertex attributes and the GPU-culling indirect-draw path.", "tags": ["shader", "instances", "procedural"], "skipPerf": true @@ -1894,7 +1894,7 @@ "slug": "scene215-cascaded-shadows-pbr", "name": "Scene 215 — Cascaded Shadow Maps (PBR)", "maxMad": 0.4, - "maxRawKB": 80.0, + "maxRawKB": 80.1, "description": "PBR counterpart of scene214: a deterministic field of 200 green PBR torus-knot casters drops cascaded shadows onto a large PBR ground receiver, lit by a single DirectionalLight with a 4-cascade CascadedShadowGenerator (PCF5). Proves CSM works on PBR receivers, not just Standard; a seeded PRNG matches the Babylon.js CSM oracle.", "tags": ["shadows", "csm", "pbr", "directional"], "skipPerf": true @@ -1925,7 +1925,7 @@ "slug": "scene218-vat", "name": "Scene 218 — Vertex Animation Texture (VAT)", "maxMad": 0.02, - "maxRawKB": 94.9, + "maxRawKB": 95, "description": "Baked vertex animation: the scene-11 shark's skeletal animation is pre-evaluated into one rgba32float texture (bakeVat) and played through the GPU VAT vertex path (mesh.vat) with NO live CPU skeleton - the vertex shader reads each bone matrix from the baked texture at the current frame row.", "tags": ["vat", "animation", "skeleton", "instancing", "pbr", "gltf"], "skipPerf": true, @@ -1936,7 +1936,7 @@ "slug": "scene219-vat-instanced", "name": "Scene 219 — Per-instance VAT", "maxMad": 0.02, - "maxRawKB": 98.1, + "maxRawKB": 98.2, "description": "Per-instance VAT: the scene-11 shark baked to a VAT texture and rendered through the GPU thin-instance VAT path (one identity-matrix instance) frozen via ?seekTime. finalWorld = instanceMatrix * mesh.world * skin equals the plain skinned pose at identity, so it matches the BJS live-skeleton golden exactly.", "tags": ["vat", "animation", "skeleton", "instancing", "pbr", "gltf"], "skipPerf": true, @@ -2033,7 +2033,7 @@ "slug": "scene240-animated-triangle", "name": "Scene 240 — AnimatedTriangle", "maxMad": 0.2, - "maxRawKB": 91.1, + "maxRawKB": 91.2, "description": "AnimatedTriangle.gltf — cx20 gltf-test. Node-rotation animation; material-less no-NORMAL mesh (smooth normals).", "tags": ["gltf"], "skipPerf": true @@ -2053,7 +2053,7 @@ "slug": "scene242-emissive-fireflies", "name": "Scene 242 — EmissiveFireflies", "maxMad": 0.2, - "maxRawKB": 97.3, + "maxRawKB": 97.4, "description": "EmissiveFireflies.gltf — cx20 gltf-test. KHR_animation_pointer material emissiveFactor/emissiveStrength/baseColorFactor.", "tags": ["gltf"], "skipPerf": true @@ -2063,7 +2063,7 @@ "slug": "scene243-morph-stress-test", "name": "Scene 243 — MorphStressTest", "maxMad": 0.2, - "maxRawKB": 97.6, + "maxRawKB": 97.7, "description": "MorphStressTest.gltf — cx20 gltf-test. Morph-target weight animation.", "tags": ["gltf"], "skipPerf": true @@ -2073,7 +2073,7 @@ "slug": "scene244-pot-of-coals-animation-pointer", "name": "Scene 244 — PotOfCoalsAnimationPointer", "maxMad": 0.2, - "maxRawKB": 133.8, + "maxRawKB": 133.9, "description": "PotOfCoalsAnimationPointer.gltf — cx20 gltf-test. KHR_animation_pointer texture-transform rotation.", "tags": ["gltf"], "skipPerf": true @@ -2083,7 +2083,7 @@ "slug": "scene245-recursive-skeletons", "name": "Scene 245 — RecursiveSkeletons", "maxMad": 0.2, - "maxRawKB": 100.8, + "maxRawKB": 100.9, "description": "RecursiveSkeletons.gltf — cx20 gltf-test. Deep recursive skeletons; interleaved JOINTS/WEIGHTS/COLOR_0.", "tags": ["gltf"], "skipPerf": true @@ -2093,7 +2093,7 @@ "slug": "scene246-simple-skin", "name": "Scene 246 — SimpleSkin", "maxMad": 0.2, - "maxRawKB": 99.6, + "maxRawKB": 99.7, "description": "SimpleSkin.gltf — cx20 gltf-test. Material-less skinned mesh; interleaved JOINTS/WEIGHTS, no-NORMAL smooth normals.", "tags": ["gltf"], "skipPerf": true @@ -2103,7 +2103,7 @@ "slug": "scene247-teapots-galore", "name": "Scene 247 — TeapotsGalore", "maxMad": 0.4, - "maxRawKB": 84.8, + "maxRawKB": 84.9, "description": "TeapotsGalore.gltf — cx20 gltf-test. EXT_mesh_gpu_instancing, top-down view.", "tags": ["gltf"], "skipPerf": true @@ -2154,7 +2154,7 @@ "slug": "scene253-animate-all-the-things", "name": "Scene 253 — AnimateAllTheThings", "maxMad": 0.3, - "maxRawKB": 153.1, + "maxRawKB": 153.2, "description": "cx20 gltf-test AnimateAllTheThings, viewed top-down over the full grid of test objects. Exercises KHR_lights_punctual, KHR_texture_transform, KHR_materials_unlit/ior/transmission/iridescence/volume/emissive_strength and KHR_animation_pointer, plus skinning and morph targets. Frozen at a deterministic frame.", "tags": ["gltf", "animation", "animation-pointer", "transmission", "iridescence", "skeleton", "morph"], "skipPerf": true @@ -2165,7 +2165,7 @@ "name": "Scene 254 — Signed Accessor Animation", "maxMad": 0.15, "maxRegionMad": 0.5, - "maxRawKB": 92.5, + "maxRawKB": 92.6, "description": "glTF Animation_SamplerType_01: rotation animation whose sampler output quaternions are a normalized signed BYTE accessor (componentType 5120). Validates loader support for signed BYTE/SHORT accessor component types (glTF-Asset-Generator Category A). Frozen at seekTime=2.0.", "tags": ["gltf", "animation", "accessor"], "skipPerf": true @@ -2220,7 +2220,7 @@ "name": "Scene 260 — Triangle Strip", "maxMad": 0.1, "maxRegionMad": 0.5, - "maxRawKB": 80, + "maxRawKB": 80.2, "description": "glTF Mesh_PrimitiveMode_11: a quad drawn as a TRIANGLE_STRIP (primitive mode 5) with uint32 indices. Validates non-triangle-list primitive topologies (glTF-Asset-Generator Category C); Lite hardcoded a triangle-list pipeline and rendered POINTS/LINES/strips garbled. The loader now reads the primitive mode and the pipeline honors the matching WebGPU topology.", "tags": ["gltf", "topology"], "skipPerf": true @@ -2268,5 +2268,15 @@ "tags": ["npe", "particles", "billboards"], "skipPerf": true, "compatParity": true + }, + { + "id": 265, + "slug": "scene265-environment-test", + "name": "Scene 265 — EnvironmentTest (EXT_lights_image_based)", + "maxMad": 0.5, + "maxRawKB": 82, + "description": "EnvironmentTest.gltf (glTF-IBL) — cx20 gltf-test. Exercises EXT_lights_image_based: the glTF carries its own document-level image-based light (irradiance SH9 + prefiltered specular cubemap mips + intensity/rotation), driving PBR IBL through Lite's existing environment path (EnvironmentTextures + scene UBO spherical harmonics). Grid of metallic-roughness spheres.", + "tags": ["gltf", "pbr", "env", "ibl"], + "skipPerf": true } ] diff --git a/tests/lite/parity/scenes/scene265-environment-test.spec.ts b/tests/lite/parity/scenes/scene265-environment-test.spec.ts new file mode 100644 index 0000000000..b42a1ea741 --- /dev/null +++ b/tests/lite/parity/scenes/scene265-environment-test.spec.ts @@ -0,0 +1,33 @@ +/** + * Scene 265 — EnvironmentTest / glTF-IBL (cx20 gltf-test parity). + * + * Exercises the EXT_lights_image_based glTF extension: the model carries its own + * document-level image-based light (irradiance SH9 + prefiltered specular cubemap + * mips), which Lite drives through its existing environment/IBL path. + */ +import { test, expect } from "../parity-fixtures"; +import * as path from "path"; +import { attachCompareArtifacts, captureGolden, compareImages, getSceneConfig } from "../compare-utils"; + +const sceneConfig = getSceneConfig(265); +const REFERENCE_DIR = path.resolve(__dirname, "../../../../reference/lite/scene265-environment-test"); +const GOLDEN_REF = path.join(REFERENCE_DIR, "babylon-ref-golden.png"); + +test.skip(!!sceneConfig.skipParity, "Scene 265 skipped via skipParity in scene-config.json"); + +test("Scene 265 — EnvironmentTest matches Babylon.js reference", async ({ page }, testInfo) => { + const browser = page.context().browser()!; + await captureGolden(browser, { sceneId: 265, timeout: 90_000 }); + + await page.goto("/scene265.html"); + await page.waitForFunction(() => document.querySelector("canvas")?.dataset.ready === "true", { timeout: 60_000 }); + await page.waitForTimeout(500); + + const screenshotPath = path.join(REFERENCE_DIR, "test-actual.png"); + await page.locator("canvas").screenshot({ path: screenshotPath }); + + const full = compareImages(screenshotPath, GOLDEN_REF); + await attachCompareArtifacts(testInfo, screenshotPath, GOLDEN_REF, REFERENCE_DIR); + console.log(`Full image MAD: ${full.mad.toFixed(3)} (limit ${sceneConfig.maxMad})`); + expect(full.mad, `Full image MAD should be <= ${sceneConfig.maxMad}`).toBeLessThanOrEqual(sceneConfig.maxMad); +});