diff --git a/docs/lite/architecture/00-overview.md b/docs/lite/architecture/00-overview.md index 025893b639..c7568a1395 100644 --- a/docs/lite/architecture/00-overview.md +++ b/docs/lite/architecture/00-overview.md @@ -381,6 +381,7 @@ createEsmDirectionalShadowGenerator(engine: Engine, light: DirectionalLight, con createPcfSpotlightShadowGenerator(engine: Engine, light: SpotLight, config?: PcfSpotlightShadowGeneratorConfig): ShadowGenerator createPcfDirectionalShadowGenerator(engine: Engine, light: DirectionalLight, config?: PcfDirectionalShadowGeneratorConfig): ShadowGenerator setShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]): void +setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void // Animation createAnimationController(skeleton, scene): AnimationController diff --git a/docs/lite/architecture/12-thin-instances.md b/docs/lite/architecture/12-thin-instances.md index fe127d08eb..193a93cfcb 100644 --- a/docs/lite/architecture/12-thin-instances.md +++ b/docs/lite/architecture/12-thin-instances.md @@ -35,6 +35,8 @@ export interface ThinInstanceData { _colorGpuBufferStorage: boolean; _colorGpuVersion: number; _gpuCullingEnabled: boolean; // opt-in GPU frustum culling + indirect draw + _lodPartner?: Mesh | null; // lower-detail mesh receiving the far cull bucket + _lodSource?: Mesh | null; // source mesh when this mesh is the LOD partner } ``` @@ -70,6 +72,17 @@ export function setThinInstanceColors(mesh: Mesh, colors: Float32Array): void; /** Enable/disable per-pass GPU frustum culling. Must be called before registerScene(). */ export function enableThinInstanceGpuCulling(mesh: Mesh, enabled?: boolean): void; + +interface ThinInstanceLodPartnerOptions { + distance: number; // camera-space switch distance in world units + band?: number; // deterministic per-instance dither width, default 0 +} + +/** Split one GPU-culling result into near/full-detail and far/LOD buckets. */ +export function setThinInstanceLodPartner(fullMesh: Mesh, lodMesh: Mesh, options: ThinInstanceLodPartnerOptions): void; + +/** Restore the two meshes to independent rendering. */ +export function clearThinInstanceLodPartner(fullMesh: Mesh): void; ``` `setThinInstanceDrawCount()` is the count-only path for fixed-capacity pools whose CPU array and GPU @@ -256,7 +269,7 @@ Returns the updated `slot` number — the next free vertex buffer slot after all GPU culling is opt-in through `enableThinInstanceGpuCulling(mesh)`. The helper only flips state on existing `ThinInstanceData`; the compute module is dynamically imported (via the shared `thin-instance-cull-binding.ts` lifecycle helper) by the Standard, PBR, and ShaderMaterial group builders only when at least one mesh in that material family has `_gpuCullingEnabled === true`. -The per-binding cull lifecycle is factored into one shared module, `packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts`, used identically by all three material families. Its `tryBind()` seam is called from each renderable's `bind()`: it gates on opt-in + opaque-only, marks the renderable `_direct`, creates the per-binding `ThinInstanceGpuCullState`, registers its disposal, and returns a `TiCullBinding` whose `update()` dispatches the compute cull pass and whose `draw()` issues `drawIndexedIndirect` (or falls back to a normal instanced draw when culling did not run). +The per-binding cull lifecycle is factored into one shared module, `packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts`, used identically by all three material families. Its `tryBind()` seam is called from each renderable's `bind()`: it gates on opt-in + opaque-only, creates the per-binding `ThinInstanceGpuCullState`, registers its disposal, and returns a `TiCullBinding` whose `update()` dispatches the compute cull pass and whose `draw()` issues `drawIndexedIndirect` (or falls back to a normal instanced draw when culling did not run). ### Scope @@ -289,7 +302,7 @@ Cull state is owned by each `DrawBinding`, not by `ThinInstanceData`, because th 6. The compute shader transforms the local sphere by `mesh.world * instanceWorld`, tests it against all planes, atomically appends visible instances to compacted buffers, and atomically increments `args[1]`. 7. The draw closure binds compacted buffers and calls `drawIndexedIndirect(argsBuffer, 0)`. -Cull-enabled renderables set `_direct = true` so they are encoded every frame with current buffer objects instead of being captured in the opaque render bundle. This avoids stale render-bundle references when thin-instance capacity grows. +Cull-enabled opaque renderables remain bundle-compatible because their compacted buffers and indirect argument buffers are stable. Any buffer replacement bumps the global visibility epoch, forcing the opaque render bundle to record the new handles before it executes. ### Compute Shader Outline @@ -307,6 +320,16 @@ if (sphereIntersectsFrustum(center, radius)) { The test is conservative: spheres touching a plane stay visible. This preserves parity with non-culled rendering. +### Distance LOD Pairing + +`setThinInstanceLodPartner(fullMesh, lodMesh, { distance, band })` extends the opt-in GPU culler with a second compacted output. Both meshes must already have thin-instance state and must be distinct, opaque, non-transmissive meshes. The full-detail mesh must be the only source for the partner, and neither mesh may simultaneously participate in another LOD chain. LOD-paired thin-instance state cannot be shared through `cloneTransformNode`; pair meshes only before cloning or use independent thin-instance data. `distance` and `band` are finite non-negative world-space values. + +The source mesh retains in-frustum instances closer than the threshold. Farther instances are compacted into stable matrix/color buffers and an indirect argument buffer consumed by the partner. `band` applies a deterministic instance-index hash in `[-band/2, +band/2]`, spreading transitions without frame-to-frame noise. The partner's geometry and material pipeline are its own, but its drawn matrices and optional instance colors come from the source. A partner that renders instance colors therefore requires the source draw to provide them too. + +The far bucket is published per `RenderTargetSignature`, so main, shadow, and auxiliary camera passes never share compacted state. Partner renderables use the direct-draw phase and resolve the current bucket after all binding updates and the shared compute batch have completed; scene insertion order therefore cannot introduce a one-frame lag or capture stale cross-renderable handles in an opaque bundle. Source renderables retain their normal stable-buffer bundle path. If source culling is unavailable, disabled, hidden, or empty, the source falls back to drawing all active instances and the paired partner draws nothing. + +Pairing is configured after `setThinInstances()` and before `registerScene()`. Repeating the call with the same pair updates `distance`/`band` live. `clearThinInstanceLodPartner()` makes the partner fall back to its independent instance draw immediately; pairing links are also detached automatically when either mesh is disposed so no draw retains destroyed far-bucket buffers. + --- ## Pipeline Configuration diff --git a/docs/lite/architecture/16-shadow-generator.md b/docs/lite/architecture/16-shadow-generator.md index fa17244745..db19730723 100644 --- a/docs/lite/architecture/16-shadow-generator.md +++ b/docs/lite/architecture/16-shadow-generator.md @@ -54,10 +54,14 @@ export interface ShadowTaskInputs { } export function setShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]): void; + +export function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void; ``` Caster meshes are task inputs. Scene setup creates the filter-specific `ShadowGenerator`, assigns it to the light, calls `setShadowTaskCasterMeshes()` to provide the caster list, then calls `registerSceneWithShadowSupport()` instead of `registerScene()` to install the scene-owned shadow task. +`setShadowCasterMaxCascade()` is CSM-specific: it limits a caster to layers `0..maxCascade`, accepts a non-negative integer or `Infinity`, and is applied when a new caster-array instance is supplied. ESM and single-map PCF ignore it. + ### Common ShadowGenerator Interface (`shadow-generator.ts`) ```typescript @@ -161,6 +165,7 @@ import { ensurePcfShadowTaskState, preloadPcfShadowTaskState, renderPcfShadowMap Shadow generators share math and UBO packing helpers only. Caster ownership lives in `ShadowTaskInputs`: - **`setShadowTaskCasterMeshes()`** — registers the caster mesh list for a generator in lazy task-owned input state. +- **`setShadowCasterMaxCascade()`** — stores a lazy per-mesh CSM cascade cap; `Infinity` clears it. - **`writeShadowUboFields()`** — packs a `ShadowGenerator`'s light matrix (16 floats), depth values (2 floats + 2 padding), and shadowsInfo (4 floats) into a 24-float array for downstream UBO upload. - **`buildLightViewMatrix()` / `multiply4x4()`** — shared light-space matrix math for ESM and PCF task paths. - **`createShadowParamsUBO()` / `createSharedShadowUBO()`** — shared GPU buffer setup for generator-owned receiver resources. diff --git a/docs/lite/architecture/17-cascaded-shadow.md b/docs/lite/architecture/17-cascaded-shadow.md index 73202d860a..718cb0968f 100644 --- a/docs/lite/architecture/17-cascaded-shadow.md +++ b/docs/lite/architecture/17-cascaded-shadow.md @@ -41,6 +41,8 @@ function createCsmDirectionalShadowGenerator(engine: EngineContext, light: Direc function getCsmReceiverTexture(shadowGenerator: ShadowGenerator): Texture2D; function onCsmReceiverUpdate(shadowGenerator: ShadowGenerator, callback: (data: Float32Array) => void): () => void; + +function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void; ``` Usage mirrors the other directional generators: @@ -54,6 +56,13 @@ setShadowTaskCasterMeshes(light.shadowGenerator, casterMeshes); await registerSceneWithShadowSupport(scene); ``` +`setShadowCasterMaxCascade(mesh, maxCascade)` limits a caster to cascade layers +`0..maxCascade` (`0` is nearest). The default is all cascades; pass `Infinity` to +restore it. Values must be non-negative integer indexes or `Infinity`. The cap is +snapshotted when `setShadowTaskCasterMeshes` supplies the caster set, so changing a +live cap requires re-supplying a new caster-array instance. CSM updates only the +changed caster's per-cascade task membership; ESM and single-map PCF ignore the cap. + Custom `ShaderMaterial` receivers use the same public generator without reading its internal WebGPU resources: @@ -233,6 +242,10 @@ Shadow-map recreation is not supported by the current fixed generator configurat therefore the wrapper remains valid until the generator's GPU resources are disposed with the scene. +Each CSM task state also snapshots every caster's maximum cascade. When a new caster +array is supplied without material changes, the incremental diff removes and re-adds +only new, removed, or re-capped casters, preserving all unchanged caster packets. + ## Babylon.js Equivalence Map | Babylon.js | Babylon Lite | @@ -290,6 +303,10 @@ adapter creates one explicit 2d-array depth wrapper, reuses the generator textur comparison sampler without exposing them in its signature, preserves wrapper identity, survives a ShaderMaterial acquire/release cycle, and rejects ESM/PCF generators. +`tests/lite/unit/shadow-caster-max-cascade.test.ts` validates cap input, default and +reset behavior, and incremental reassignment of an existing caster across cascade +tasks after a live cap change. + ## File Manifest - `shadow/csm-directional-shadow-generator.ts` — public factory, custom-receiver diff --git a/docs/lite/architecture/18-picking.md b/docs/lite/architecture/18-picking.md index 2129f6c795..d81a43d907 100644 --- a/docs/lite/architecture/18-picking.md +++ b/docs/lite/architecture/18-picking.md @@ -47,6 +47,22 @@ interface Ray { direction: [number, number, number]; length: number; } + +interface PickOptions { + filter?: (mesh: Mesh) => boolean; + discard?: PickDiscardRule; + debugLabel?: string; +} + +// WGSL shape injected for custom discard rules. +struct PickDiscardInput { + worldPos: vec3f, + fragmentCoord: vec2f, // selected pixel center in the original backing framebuffer + pickId: u32, + thinInstanceIndex: u32, + hasThinInstance: u32, + instanceExtras: vec4f, +} ``` ### Functions @@ -90,7 +106,7 @@ and .babylon loader. No copies needed — the arrays already exist in JS memory. 1. Each mesh (or thin instance) is assigned a sequential pick ID (1-based; 0 = miss). 2. A WGSL shader writes the 24-bit pick ID as RGB at `@location(0)` and the fragment's NDC depth at `@location(1)`. -3. The pass uses a **pick-zoomed view-projection** so only the picked pixel survives, drawing all meshes to a **1×1** target: two colour attachments (`rgba8unorm` pick ID + `r32float` NDC depth) plus a `depth24plus` depth buffer (reverse-Z, compare `greater`). +3. The pass uses a **pick-zoomed view-projection** so only the picked pixel survives, drawing all meshes to a **1×1** target: two colour attachments (`rgba8unorm` pick ID + `r32float` NDC depth) plus a `depth24plus` depth buffer (reverse-Z, compare `greater`). Because the 1×1 fragment position is always `(0.5, 0.5)`, the scene UBO separately carries the selected pixel center in original backing-framebuffer coordinates for custom discard WGSL. 4. The 1×1 pick-ID and depth texels are copied to staging buffers and read back. 5. The pick ID is decoded: `(r << 16) | (g << 8) | b`. 6. The world-space hit point is reconstructed by unprojecting NDC + the read-back depth through `inverse(VP)`. @@ -179,13 +195,13 @@ each attachment is a single texel: **Regular meshes:** | Group | Binding | Type | Content | |-------|---------|------|---------| -| 0 | 0 | uniform | `mat4x4f` — viewProjection (shared, 64 bytes) | +| 0 | 0 | uniform | `mat4x4f` viewProjection + `vec2f` original fragment coordinate (shared, 80 bytes) | | 1 | 0 | uniform | `mat4x4f` world + `u32` pickId (80 bytes, 16-aligned) | **Thin-instanced meshes:** | Group | Binding | Type | Content | |-------|---------|------|---------| -| 0 | 0 | uniform | `mat4x4f` — viewProjection (shared) | +| 0 | 0 | uniform | `mat4x4f` viewProjection + `vec2f` original fragment coordinate (shared, 80 bytes) | | 1 | 0 | uniform | `u32` baseMeshPickId (16 bytes, padded) | | 1 | 1 | read-only-storage | `array` — instance world matrices | diff --git a/lab/public/bundle/manifest/scene1.json b/lab/public/bundle/manifest/scene1.json index bd37ca9a15..6af457716f 100644 --- a/lab/public/bundle/manifest/scene1.json +++ b/lab/public/bundle/manifest/scene1.json @@ -9,7 +9,7 @@ "scene1-generate-mipmaps-DfKYIHYG.js", "scene1-gltf-glb-parser-BL74Wcec.js", "scene1-ibl-fragment-CeRrvT96.js", - "scene1-pbr-renderable-C_iqrKEA.js", + "scene1-pbr-renderable-98lkxJeN.js", "scene1-rgbd-decode-BqHBbXAD.js", "scene1-scene-uniforms-FTYH6Ct0.js", "scene1-singlelight-hemispheric-wgsl-BrmVaOY6.js", diff --git a/lab/public/bundle/manifest/scene10.json b/lab/public/bundle/manifest/scene10.json index 2a90e28e83..9de1e517a8 100644 --- a/lab/public/bundle/manifest/scene10.json +++ b/lab/public/bundle/manifest/scene10.json @@ -3,7 +3,7 @@ "gzipKB": 21.7, "ignoredRawKB": 0, "runtimeChunks": [ - "scene10-pbr-renderable-JEgGP_sP.js", + "scene10-pbr-renderable-BxJDR30z.js", "scene10-singlelight-hemispheric-wgsl-w0cK80Wf.js", "scene10.js" ] diff --git a/lab/public/bundle/manifest/scene104.json b/lab/public/bundle/manifest/scene104.json index 5ea3d586bf..6c311c8172 100644 --- a/lab/public/bundle/manifest/scene104.json +++ b/lab/public/bundle/manifest/scene104.json @@ -1,13 +1,13 @@ { - "rawKB": 101.5, + "rawKB": 101.6, "gzipKB": 39.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene104-generate-mipmaps-C2O_HNRi.js", - "scene104-gltf-feature-registry-DT7MsLnT.js", + "scene104-gltf-feature-registry-BbgFUYeR.js", "scene104-gltf-glb-parser-BJfNq64D.js", "scene104-shader-composer-DishuYLa.js", - "scene104-standard-renderable-DtgBdvNf.js", + "scene104-standard-renderable-Dndx3Y1u.js", "scene104-std-lightmap-fragment-BtZeffyx.js", "scene104-wgsl-fog-m3PkjS7i.js", "scene104.js" diff --git a/lab/public/bundle/manifest/scene105.json b/lab/public/bundle/manifest/scene105.json index eed207b4bb..1c0eb604ba 100644 --- a/lab/public/bundle/manifest/scene105.json +++ b/lab/public/bundle/manifest/scene105.json @@ -4,10 +4,10 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene105-generate-mipmaps-q97-3GGe.js", - "scene105-gltf-feature-registry-Bja0apwS.js", + "scene105-gltf-feature-registry-B3NVx5ri.js", "scene105-gltf-glb-parser-BsWC1iTB.js", "scene105-shader-composer-DishuYLa.js", - "scene105-standard-renderable-CJrIUUrL.js", + "scene105-standard-renderable-0fsnyKYk.js", "scene105-std-lightmap-fragment-DJHrjMOe.js", "scene105-wgsl-fog-m3PkjS7i.js", "scene105.js" diff --git a/lab/public/bundle/manifest/scene11.json b/lab/public/bundle/manifest/scene11.json index 1e937b4d3a..75d4ae967b 100644 --- a/lab/public/bundle/manifest/scene11.json +++ b/lab/public/bundle/manifest/scene11.json @@ -9,13 +9,13 @@ "scene11-gltf-ext-spec-gloss-D60sFZYX.js", "scene11-gltf-feature-animations-B4VuXvAy.js", "scene11-gltf-feature-extras-VHPteGay.js", - "scene11-gltf-feature-registry-CpagfJwp.js", + "scene11-gltf-feature-registry-C82Q-vMl.js", "scene11-gltf-feature-skeleton-lVTm_rgW.js", "scene11-gltf-glb-parser-B0CzN8nR.js", "scene11-gltf-sampler-desc-_twQF5pG.js", - "scene11-pbr-renderable-CQGHBAlM.js", + "scene11-pbr-renderable-BOdxod8U.js", "scene11-singlelight-hemispheric-wgsl-B7Q6UIxj.js", - "scene11-skeleton-fragment-BTrVps6p.js", + "scene11-skeleton-fragment-BZnAUfd1.js", "scene11.js" ] } diff --git a/lab/public/bundle/manifest/scene111.json b/lab/public/bundle/manifest/scene111.json index 545760c1b7..8047554b69 100644 --- a/lab/public/bundle/manifest/scene111.json +++ b/lab/public/bundle/manifest/scene111.json @@ -18,7 +18,7 @@ "scene111-node-registry-CZFoka3V.js", "scene111-node-renderable-DsHgimzd.js", "scene111-node-shadow-Ckjwl7eT.js", - "scene111-pbr-renderable-BpH7gTds.js", + "scene111-pbr-renderable-CXaEOwUg.js", "scene111-pbr-shadow-fragment-CFzpdZcF.js", "scene111-shader-composer-CdmK2apN.js", "scene111-shadow-fragment-core-Dbe1xNkt.js", diff --git a/lab/public/bundle/manifest/scene112.json b/lab/public/bundle/manifest/scene112.json index b0f4782678..74017b3de7 100644 --- a/lab/public/bundle/manifest/scene112.json +++ b/lab/public/bundle/manifest/scene112.json @@ -9,11 +9,11 @@ "scene112-generate-mipmaps-CC5rHiTf.js", "scene112-gltf-ext-basisu-DzvX1Byt.js", "scene112-gltf-ext-dielectric-C6hvXXiF.js", - "scene112-gltf-feature-registry-hfyja7sC.js", + "scene112-gltf-feature-registry-GURwUtgi.js", "scene112-gltf-json-asset-JDq89jct.js", "scene112-ibl-fragment-CeRrvT96.js", "scene112-pbr-refraction-D08DQGUU.js", - "scene112-pbr-renderable-BF6rrTS_.js", + "scene112-pbr-renderable-BT3V7Gxf.js", "scene112-pbr-transmission-ext-BrX8tW-7.js", "scene112-rgbd-decode-CZsPghZT.js", "scene112-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene114.json b/lab/public/bundle/manifest/scene114.json index f25b8ea900..cd758b078f 100644 --- a/lab/public/bundle/manifest/scene114.json +++ b/lab/public/bundle/manifest/scene114.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene114-morph-fragment-DLjixOml.js", - "scene114-pbr-renderable-PM3Fxc8W.js", + "scene114-pbr-renderable-DrwBCBSC.js", "scene114-pbr-template-ext-CiHpOV5G.js", "scene114-singlelight-hemispheric-wgsl-CEVgMDLE.js", "scene114-skeleton-fragment-qheSzR-l.js", diff --git a/lab/public/bundle/manifest/scene115.json b/lab/public/bundle/manifest/scene115.json index a46d417fc1..8b03e7d5a1 100644 --- a/lab/public/bundle/manifest/scene115.json +++ b/lab/public/bundle/manifest/scene115.json @@ -10,17 +10,17 @@ "scene115-gltf-color-normalize-4-WGzPL2.js", "scene115-gltf-feature-animations-BkTFKNwk.js", "scene115-gltf-feature-morph-DLWJRlR1.js", - "scene115-gltf-feature-registry-ChnXVhzz.js", + "scene115-gltf-feature-registry-BFrjFosk.js", "scene115-gltf-feature-skeleton-Bgw5Y0YP.js", "scene115-gltf-json-asset-EChxQgVl.js", "scene115-morph-fragment-BbCT_r5m.js", "scene115-morph-fragment-core-CTaOwZva.js", - "scene115-pbr-renderable-COSNhRzq.js", + "scene115-pbr-renderable-eMseufKo.js", "scene115-pbr-template-ext-CiHpOV5G.js", "scene115-shader-composer-DishuYLa.js", "scene115-singlelight-hemispheric-wgsl-Cyo3l2OW.js", "scene115-skeleton-fragment-B0VEBZFR.js", - "scene115-standard-renderable-veM92dGz.js", + "scene115-standard-renderable-B3xK1ePJ.js", "scene115-wgsl-fog-m3PkjS7i.js", "scene115.js" ] diff --git a/lab/public/bundle/manifest/scene116.json b/lab/public/bundle/manifest/scene116.json index e1ca30e96d..f02b4ffc57 100644 --- a/lab/public/bundle/manifest/scene116.json +++ b/lab/public/bundle/manifest/scene116.json @@ -3,7 +3,7 @@ "gzipKB": 31.3, "ignoredRawKB": 0, "runtimeChunks": [ - "scene116-pbr-renderable-BJ10F1sj.js", + "scene116-pbr-renderable-1mPrKn0F.js", "scene116-shader-composer-Bga-tI-0.js", "scene116-singlelight-hemispheric-wgsl-hXzjd_qm.js", "scene116-standard-renderable-B6DFgoG2.js", diff --git a/lab/public/bundle/manifest/scene118.json b/lab/public/bundle/manifest/scene118.json index 2094dcf7bb..d7915d3348 100644 --- a/lab/public/bundle/manifest/scene118.json +++ b/lab/public/bundle/manifest/scene118.json @@ -6,7 +6,7 @@ "scene118-billboard-pick-pipeline-efBlU1Ns.js", "scene118-billboard-renderable-BMoinrEu.js", "scene118-scene-uniforms-FTYH6Ct0.js", - "scene118-standard-renderable-IusTupZx.js", + "scene118-standard-renderable-DlgYfS07.js", "scene118.js" ] } diff --git a/lab/public/bundle/manifest/scene12.json b/lab/public/bundle/manifest/scene12.json index 00dc3790ca..69b54136ef 100644 --- a/lab/public/bundle/manifest/scene12.json +++ b/lab/public/bundle/manifest/scene12.json @@ -1,17 +1,17 @@ { "rawKB": 103.7, - "gzipKB": 42.4, + "gzipKB": 42.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene12-create-skeleton-yE34pa8K.js", "scene12-generate-mipmaps-WJd2dKx2.js", "scene12-gltf-animation-BjkCiBBl.js", "scene12-gltf-feature-animations-CZFTUgiy.js", - "scene12-gltf-feature-registry-D-WZZjm3.js", + "scene12-gltf-feature-registry-fRlEtyCQ.js", "scene12-gltf-feature-skeleton-Cxvue01k.js", "scene12-gltf-glb-parser-BrsOlymf.js", "scene12-ibl-fragment-CeRrvT96.js", - "scene12-pbr-renderable-AQ8a2h5D.js", + "scene12-pbr-renderable-b4c18C5f.js", "scene12-reflectance-fragment-BESl-7E9.js", "scene12-rgbd-decode-BLiTLohS.js", "scene12-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene122.json b/lab/public/bundle/manifest/scene122.json index f31f401b7a..743dbcbcd8 100644 --- a/lab/public/bundle/manifest/scene122.json +++ b/lab/public/bundle/manifest/scene122.json @@ -3,7 +3,7 @@ "gzipKB": 19.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene122-gaussian-splatting-pipeline-sh-DCM3WMGs.js", + "scene122-gaussian-splatting-pipeline-sh-7Pe9evxc.js", "scene122.js" ] } diff --git a/lab/public/bundle/manifest/scene123.json b/lab/public/bundle/manifest/scene123.json index 63137b666e..65a48aa89f 100644 --- a/lab/public/bundle/manifest/scene123.json +++ b/lab/public/bundle/manifest/scene123.json @@ -3,7 +3,7 @@ "gzipKB": 18.4, "ignoredRawKB": 0, "runtimeChunks": [ - "scene123-gaussian-splatting-pipeline-sh-DqvbGZYF.js", + "scene123-gaussian-splatting-pipeline-sh-VanKw4Jm.js", "scene123.js" ] } diff --git a/lab/public/bundle/manifest/scene124.json b/lab/public/bundle/manifest/scene124.json index 69d2adf6a1..f688f6e2b6 100644 --- a/lab/public/bundle/manifest/scene124.json +++ b/lab/public/bundle/manifest/scene124.json @@ -3,7 +3,7 @@ "gzipKB": 20.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene124-gaussian-splatting-pipeline-sh-DYZuF8gE.js", + "scene124-gaussian-splatting-pipeline-sh-CoCXUHtI.js", "scene124-splat-ply-compressed-4vnSZMqI.js", "scene124.js" ] diff --git a/lab/public/bundle/manifest/scene127.json b/lab/public/bundle/manifest/scene127.json index 3679511c8e..4ba46c2314 100644 --- a/lab/public/bundle/manifest/scene127.json +++ b/lab/public/bundle/manifest/scene127.json @@ -3,7 +3,7 @@ "gzipKB": 23.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene127-shader-renderable-C5moVC12.js", + "scene127-shader-renderable-CRu5OTTS.js", "scene127-uniform-copy-batch-C6lLhyWE.js", "scene127.js" ] diff --git a/lab/public/bundle/manifest/scene128.json b/lab/public/bundle/manifest/scene128.json index edd15c7f31..42113e7cb2 100644 --- a/lab/public/bundle/manifest/scene128.json +++ b/lab/public/bundle/manifest/scene128.json @@ -3,7 +3,7 @@ "gzipKB": 23.5, "ignoredRawKB": 0, "runtimeChunks": [ - "scene128-shader-renderable-Cm1fTjZA.js", + "scene128-shader-renderable-BfCRPVDX.js", "scene128-uniform-copy-batch-B2JNqIj3.js", "scene128.js" ] diff --git a/lab/public/bundle/manifest/scene129.json b/lab/public/bundle/manifest/scene129.json index 447e965423..f911de8e24 100644 --- a/lab/public/bundle/manifest/scene129.json +++ b/lab/public/bundle/manifest/scene129.json @@ -1,10 +1,10 @@ { - "rawKB": 84.5, - "gzipKB": 32.1, + "rawKB": 84.9, + "gzipKB": 32.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene129-gs-picking-pipeline-RhdQBaXc.js", - "scene129-standard-renderable-DvSP5bxO.js", + "scene129-standard-renderable-BxnuGE20.js", "scene129.js" ] } diff --git a/lab/public/bundle/manifest/scene13.json b/lab/public/bundle/manifest/scene13.json index 83ed3631b8..56c3025e5e 100644 --- a/lab/public/bundle/manifest/scene13.json +++ b/lab/public/bundle/manifest/scene13.json @@ -7,7 +7,7 @@ "scene13-generate-mipmaps-Dzr_rLG9.js", "scene13-gltf-glb-parser-HP2v-3hE.js", "scene13-ibl-fragment-CeRrvT96.js", - "scene13-pbr-renderable-Bxfa7NS_.js", + "scene13-pbr-renderable-GCiV1w2w.js", "scene13-rgbd-decode-D5Kyeyhk.js", "scene13-scene-uniforms-FTYH6Ct0.js", "scene13-singlelight-hemispheric-wgsl-BP3jraNx.js", diff --git a/lab/public/bundle/manifest/scene14.json b/lab/public/bundle/manifest/scene14.json index 76ea93e41b..4ee1f5bf0a 100644 --- a/lab/public/bundle/manifest/scene14.json +++ b/lab/public/bundle/manifest/scene14.json @@ -9,7 +9,7 @@ "scene14-generate-mipmaps-Drlt5FNB.js", "scene14-gltf-glb-parser-B--vN_CU.js", "scene14-ibl-fragment-CeRrvT96.js", - "scene14-pbr-renderable-CgnBCcHU.js", + "scene14-pbr-renderable-DctWukkj.js", "scene14-rgbd-decode-V270Sy3J.js", "scene14-scene-uniforms-FTYH6Ct0.js", "scene14-singlelight-hemispheric-wgsl-CzyIPChP.js", diff --git a/lab/public/bundle/manifest/scene141.json b/lab/public/bundle/manifest/scene141.json index 2e0288a233..767eff7972 100644 --- a/lab/public/bundle/manifest/scene141.json +++ b/lab/public/bundle/manifest/scene141.json @@ -15,7 +15,7 @@ "scene141-node-registry-BZ274bGA.js", "scene141-node-renderable-BMALB2gu.js", "scene141-node-shadow-BscttRKr.js", - "scene141-pbr-renderable-DPNX24Ph.js", + "scene141-pbr-renderable-RHsgRmE4.js", "scene141-pbr-shadow-fragment-DHDAZ1HL.js", "scene141-shader-composer-BEJi2Ku9.js", "scene141-shadow-fragment-core-Dbe1xNkt.js", diff --git a/lab/public/bundle/manifest/scene143.json b/lab/public/bundle/manifest/scene143.json index 92bb9d93ca..e6e67a2702 100644 --- a/lab/public/bundle/manifest/scene143.json +++ b/lab/public/bundle/manifest/scene143.json @@ -6,7 +6,7 @@ "scene143-generate-mipmaps-CiyhUZPA.js", "scene143-normal-map-fragment-BkmuQI98.js", "scene143-point-light-CHHO5fr1.js", - "scene143-standard-renderable-DDynxUoW.js", + "scene143-standard-renderable-Buds7pHK.js", "scene143-std-ambient-fragment-CU5U7ir2.js", "scene143-std-opacity-fragment-CbaZz9IY.js", "scene143-std-reflection-fragment-BMQE51J5.js", diff --git a/lab/public/bundle/manifest/scene144.json b/lab/public/bundle/manifest/scene144.json index e4596a153a..8b6e3da9c8 100644 --- a/lab/public/bundle/manifest/scene144.json +++ b/lab/public/bundle/manifest/scene144.json @@ -9,16 +9,16 @@ "scene144-gltf-ext-spec-gloss-D60sFZYX.js", "scene144-gltf-feature-animations-BraLxWQH.js", "scene144-gltf-feature-extras-VHPteGay.js", - "scene144-gltf-feature-registry-DpSvU6XJ.js", + "scene144-gltf-feature-registry-nk9bTCd0.js", "scene144-gltf-feature-skeleton-Ngt_ORuA.js", "scene144-gltf-glb-parser-CXf6hcyj.js", "scene144-gltf-pbr-builder-ext-DJ5Ci9Js.js", "scene144-ibl-fragment-CeRrvT96.js", - "scene144-pbr-renderable-D7uXvrM-.js", + "scene144-pbr-renderable-DvR72mfq.js", "scene144-pbr-template-ext-CiHpOV5G.js", "scene144-rgbd-decode-BpU0ODFZ.js", "scene144-scene-uniforms-FTYH6Ct0.js", - "scene144-skeleton-fragment-Du8ULMvD.js", + "scene144-skeleton-fragment-BgwOI1na.js", "scene144-texture-2d-DQo3n8D6.js", "scene144.js" ] diff --git a/lab/public/bundle/manifest/scene145.json b/lab/public/bundle/manifest/scene145.json index e4b4f93e32..72820101ae 100644 --- a/lab/public/bundle/manifest/scene145.json +++ b/lab/public/bundle/manifest/scene145.json @@ -6,7 +6,7 @@ "scene145-bake-local-matrix-67U-IT8C.js", "scene145-cube-texture-vbJP4eNk.js", "scene145-generate-mipmaps-Db4M86C8.js", - "scene145-geometry-view-Cu0n7NOB.js", + "scene145-geometry-view-RVgllDdK.js", "scene145-mesh-features-Dnub8q45.js", "scene145-mip-count-CdXF1U-X.js", "scene145-parse-camera-BXBDIYdM.js", @@ -20,7 +20,7 @@ "scene145-std-opacity-fragment-F2uWm_km.js", "scene145-std-specular-fragment-DBvtJbxm.js", "scene145-thin-instance-fragment-CBn2miAC.js", - "scene145-thin-instance-gpu-C_0KMJ2M.js", + "scene145-thin-instance-gpu-mihMDh58.js", "scene145-ubo-layout-Cs5YvLYZ.js", "scene145-wgsl-fog-m3PkjS7i.js", "scene145.js" diff --git a/lab/public/bundle/manifest/scene146.json b/lab/public/bundle/manifest/scene146.json index 4d0ef5c973..c9a9b692b8 100644 --- a/lab/public/bundle/manifest/scene146.json +++ b/lab/public/bundle/manifest/scene146.json @@ -9,8 +9,8 @@ "scene146-generate-mipmaps-DEgXRuY9.js", "scene146-gltf-json-asset-BXcXR8Vv.js", "scene146-ibl-fragment-CeRrvT96.js", - "scene146-pbr-geometry-view-D7brTh_v.js", - "scene146-pbr-renderable-DITMjNi1.js", + "scene146-pbr-geometry-view-rpUXV5y7.js", + "scene146-pbr-renderable-DrqJ7pym.js", "scene146-rgbd-decode-DZYt7kUp.js", "scene146-scene-uniforms-FTYH6Ct0.js", "scene146-shader-composer-DOjPUQGI.js", diff --git a/lab/public/bundle/manifest/scene147.json b/lab/public/bundle/manifest/scene147.json index 6b79ce798e..937e025f40 100644 --- a/lab/public/bundle/manifest/scene147.json +++ b/lab/public/bundle/manifest/scene147.json @@ -6,12 +6,12 @@ "scene147-directional-light-B_cQy-dV.js", "scene147-generate-mipmaps-7FMQd9Gb.js", "scene147-gltf-feature-lights-punctual-P-dqmGzd.js", - "scene147-gltf-feature-registry-BBFPYEPX.js", + "scene147-gltf-feature-registry-C3gUVyyC.js", "scene147-gltf-glb-parser-1VrmmUS3.js", "scene147-gltf-light-pointer-state-B7kFTRxK.js", "scene147-multilight-wgsl-EMm1KlC7.js", - "scene147-pbr-geometry-view-Cv4pphVK.js", - "scene147-pbr-renderable-BB6lUCnu.js", + "scene147-pbr-geometry-view-ChoGXKn2.js", + "scene147-pbr-renderable-C7FyhwDP.js", "scene147-shader-composer-B51nefZQ.js", "scene147-ubo-layout-Cs5YvLYZ.js", "scene147.js" diff --git a/lab/public/bundle/manifest/scene148.json b/lab/public/bundle/manifest/scene148.json index 20a6fb7aa0..6bcc5c7c70 100644 --- a/lab/public/bundle/manifest/scene148.json +++ b/lab/public/bundle/manifest/scene148.json @@ -6,11 +6,11 @@ "scene148-directional-light-DPgo0ygy.js", "scene148-generate-mipmaps-CfuW6q-h.js", "scene148-gltf-feature-lights-punctual-CIA82N9Z.js", - "scene148-gltf-feature-registry-Bvf0YndU.js", + "scene148-gltf-feature-registry-D2ka7Z5z.js", "scene148-gltf-glb-parser-BR2EHjxU.js", "scene148-gltf-light-pointer-state-B7kFTRxK.js", - "scene148-pbr-geometry-view-hOc3XjXD.js", - "scene148-pbr-renderable-aFMPnI-t.js", + "scene148-pbr-geometry-view-iW_0muL8.js", + "scene148-pbr-renderable-D1-CtuW5.js", "scene148-shader-composer-CUoJ8Zu3.js", "scene148-singlelight-hemispheric-wgsl-BLueMPfJ.js", "scene148-ubo-layout-Cs5YvLYZ.js", diff --git a/lab/public/bundle/manifest/scene149.json b/lab/public/bundle/manifest/scene149.json index b87cc568a8..388cb8f25f 100644 --- a/lab/public/bundle/manifest/scene149.json +++ b/lab/public/bundle/manifest/scene149.json @@ -9,7 +9,7 @@ "scene149-generate-mipmaps-gtcbk4S2.js", "scene149-geometry-texture-output-BfeQftmX.js", "scene149-gltf-feature-lights-punctual-CjELT808.js", - "scene149-gltf-feature-registry-qA5DqrYz.js", + "scene149-gltf-feature-registry-_EihUSN0.js", "scene149-gltf-glb-parser-CpRY-0L4.js", "scene149-gltf-light-pointer-state-B7kFTRxK.js", "scene149-input-block-Bz3jgH9m.js", diff --git a/lab/public/bundle/manifest/scene15.json b/lab/public/bundle/manifest/scene15.json index 0fcee2e492..81cf82fa81 100644 --- a/lab/public/bundle/manifest/scene15.json +++ b/lab/public/bundle/manifest/scene15.json @@ -1,6 +1,6 @@ { "rawKB": 47.3, - "gzipKB": 18.9, + "gzipKB": 18.8, "ignoredRawKB": 0, "runtimeChunks": [ "scene15-standard-renderable-cgZZ-rj3.js", diff --git a/lab/public/bundle/manifest/scene152.json b/lab/public/bundle/manifest/scene152.json index 93beb76e6c..e9c27b6945 100644 --- a/lab/public/bundle/manifest/scene152.json +++ b/lab/public/bundle/manifest/scene152.json @@ -9,13 +9,13 @@ "scene152-gltf-ext-spec-gloss-D60sFZYX.js", "scene152-gltf-feature-animations-CrpcHKuS.js", "scene152-gltf-feature-extras-VHPteGay.js", - "scene152-gltf-feature-registry-CuyF8p5S.js", + "scene152-gltf-feature-registry-YaRr_NOW.js", "scene152-gltf-feature-skeleton-DcuxDlqC.js", "scene152-gltf-glb-parser-Dr_B2hXm.js", "scene152-gltf-sampler-desc-CXNBnots.js", - "scene152-pbr-renderable-CAXJIBP4.js", + "scene152-pbr-renderable-CTsw9Ps2.js", "scene152-singlelight-hemispheric-wgsl-QwtwpgZc.js", - "scene152-skeleton-fragment-LS1h67EO.js", + "scene152-skeleton-fragment-4qf74n7B.js", "scene152.js" ] } diff --git a/lab/public/bundle/manifest/scene157.json b/lab/public/bundle/manifest/scene157.json index 824e3500e3..bcb0de0e2f 100644 --- a/lab/public/bundle/manifest/scene157.json +++ b/lab/public/bundle/manifest/scene157.json @@ -7,12 +7,12 @@ "scene157-generate-mipmaps-BYHWBGCp.js", "scene157-gltf-animation-DB1HlTHL.js", "scene157-gltf-feature-animations-C7GBcQJ9.js", - "scene157-gltf-feature-registry-BmCJjkxv.js", + "scene157-gltf-feature-registry-Yyjs-xbG.js", "scene157-gltf-feature-skeleton-CwEAKWgQ.js", "scene157-gltf-glb-parser-DZ4Xgxsa.js", "scene157-multilight-wgsl-wdU2i_N0.js", - "scene157-pbr-renderable-CEe3sAFK.js", - "scene157-skeleton-fragment-C4-DMLV-.js", + "scene157-pbr-renderable-m7l0mY8k.js", + "scene157-skeleton-fragment-DS9rUCuK.js", "scene157.js" ] } diff --git a/lab/public/bundle/manifest/scene158.json b/lab/public/bundle/manifest/scene158.json index 9a2a87b476..d7f2809089 100644 --- a/lab/public/bundle/manifest/scene158.json +++ b/lab/public/bundle/manifest/scene158.json @@ -7,12 +7,12 @@ "scene158-generate-mipmaps-bNvmU0dI.js", "scene158-gltf-animation-D6QAYu-h.js", "scene158-gltf-feature-animations-q0avtgSa.js", - "scene158-gltf-feature-registry-CaYM42v8.js", + "scene158-gltf-feature-registry-BJMzbt_X.js", "scene158-gltf-feature-skeleton-DF7erihZ.js", "scene158-gltf-glb-parser-BhqojsL9.js", "scene158-multilight-wgsl-SYMZu3J4.js", - "scene158-pbr-renderable-BQFOueb0.js", - "scene158-skeleton-fragment-s2X2RWRN.js", + "scene158-pbr-renderable-DQ-ABU9C.js", + "scene158-skeleton-fragment-Dj1GhYQs.js", "scene158.js" ] } diff --git a/lab/public/bundle/manifest/scene159.json b/lab/public/bundle/manifest/scene159.json index 794dbfe947..af8f68eec4 100644 --- a/lab/public/bundle/manifest/scene159.json +++ b/lab/public/bundle/manifest/scene159.json @@ -3,7 +3,7 @@ "gzipKB": 15.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene159-shader-renderable-CAII7wq8.js", + "scene159-shader-renderable-BBdRWQTx.js", "scene159.js" ] } diff --git a/lab/public/bundle/manifest/scene160.json b/lab/public/bundle/manifest/scene160.json index ca84db8dd8..3e6f7c6673 100644 --- a/lab/public/bundle/manifest/scene160.json +++ b/lab/public/bundle/manifest/scene160.json @@ -3,7 +3,7 @@ "gzipKB": 16.3, "ignoredRawKB": 0, "runtimeChunks": [ - "scene160-shader-renderable-BHJhdNXp.js", + "scene160-shader-renderable-BrUIqS_X.js", "scene160.js" ] } diff --git a/lab/public/bundle/manifest/scene161.json b/lab/public/bundle/manifest/scene161.json index b50b99ea4a..5114fcc9a8 100644 --- a/lab/public/bundle/manifest/scene161.json +++ b/lab/public/bundle/manifest/scene161.json @@ -3,7 +3,7 @@ "gzipKB": 15.8, "ignoredRawKB": 0, "runtimeChunks": [ - "scene161-shader-renderable-C_nLERpD.js", + "scene161-shader-renderable-BuB91IGe.js", "scene161.js" ] } diff --git a/lab/public/bundle/manifest/scene162.json b/lab/public/bundle/manifest/scene162.json index 83148dfb5b..e6b64bfc1a 100644 --- a/lab/public/bundle/manifest/scene162.json +++ b/lab/public/bundle/manifest/scene162.json @@ -3,7 +3,7 @@ "gzipKB": 15.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene162-shader-renderable-C8bEj6dU.js", + "scene162-shader-renderable-Cif9P8a1.js", "scene162.js" ] } diff --git a/lab/public/bundle/manifest/scene163.json b/lab/public/bundle/manifest/scene163.json index ba5e294ba1..9a042b1fe7 100644 --- a/lab/public/bundle/manifest/scene163.json +++ b/lab/public/bundle/manifest/scene163.json @@ -3,7 +3,7 @@ "gzipKB": 15.4, "ignoredRawKB": 0, "runtimeChunks": [ - "scene163-shader-renderable-DMuvkkvJ.js", + "scene163-shader-renderable-CPckCf4W.js", "scene163.js" ] } diff --git a/lab/public/bundle/manifest/scene164.json b/lab/public/bundle/manifest/scene164.json index b2fa1f9f6d..47fa43c5b7 100644 --- a/lab/public/bundle/manifest/scene164.json +++ b/lab/public/bundle/manifest/scene164.json @@ -10,14 +10,14 @@ "scene164-gltf-color-normalize-4-WGzPL2.js", "scene164-gltf-feature-animations-TCCQYB41.js", "scene164-gltf-feature-morph-Dcma2yce.js", - "scene164-gltf-feature-registry-IlxEgCLl.js", + "scene164-gltf-feature-registry-BrMWEpNw.js", "scene164-gltf-feature-skeleton-Bbwa5cpX.js", "scene164-gltf-json-asset-CBHXHRU5.js", - "scene164-morph-fragment-adSQpu3m.js", - "scene164-pbr-renderable-Dio5hQ2V.js", + "scene164-morph-fragment-CXRT0S_X.js", + "scene164-pbr-renderable-Nh9KVl5t.js", "scene164-pbr-template-ext-CiHpOV5G.js", "scene164-singlelight-hemispheric-wgsl-DPmYelrA.js", - "scene164-skeleton-fragment-Br0UG0WB.js", + "scene164-skeleton-fragment-Bs_i1h0Y.js", "scene164.js" ] } diff --git a/lab/public/bundle/manifest/scene165.json b/lab/public/bundle/manifest/scene165.json index e8dc03e732..b3ff18f7fb 100644 --- a/lab/public/bundle/manifest/scene165.json +++ b/lab/public/bundle/manifest/scene165.json @@ -3,7 +3,7 @@ "gzipKB": 17.9, "ignoredRawKB": 0, "runtimeChunks": [ - "scene165-shader-renderable-B02kzevN.js", + "scene165-shader-renderable-DPbJ5CtJ.js", "scene165-shader-thin-instance-CVyyUQwx.js", "scene165-thin-instance-gpu-BE5k2hGl.js", "scene165.js" diff --git a/lab/public/bundle/manifest/scene17.json b/lab/public/bundle/manifest/scene17.json index a4f5de723b..b0e7145067 100644 --- a/lab/public/bundle/manifest/scene17.json +++ b/lab/public/bundle/manifest/scene17.json @@ -5,13 +5,13 @@ "runtimeChunks": [ "scene17-generate-mipmaps-DNSbr_z3.js", "scene17-ibl-fragment-CeRrvT96.js", - "scene17-pbr-renderable-DEMRG6dK.js", + "scene17-pbr-renderable-Cc06cNAs.js", "scene17-rgbd-decode-aydmE8bD.js", "scene17-shader-composer-Bga-tI-0.js", "scene17-singlelight-hemispheric-wgsl-DpzOMoaT.js", "scene17-standard-renderable-C1KIECNI.js", "scene17-thin-instance-fragment-CBn2miAC.js", - "scene17-thin-instance-gpu-d94c-_y4.js", + "scene17-thin-instance-gpu-Bex3dl6z.js", "scene17-wgsl-fog-m3PkjS7i.js", "scene17.js" ] diff --git a/lab/public/bundle/manifest/scene171.json b/lab/public/bundle/manifest/scene171.json index c3a1c51f97..caed0f3d8e 100644 --- a/lab/public/bundle/manifest/scene171.json +++ b/lab/public/bundle/manifest/scene171.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene171-generate-mipmaps-Cpc8PT2N.js", "scene171-gltf-glb-parser-COBAbifQ.js", - "scene171-pbr-renderable-BHNPOiw0.js", + "scene171-pbr-renderable-CEkswKz7.js", "scene171-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene171-shader-composer-DishuYLa.js", "scene171-singlelight-hemispheric-wgsl-BfbzAtlS.js", - "scene171-standard-renderable-BX3feHP5.js", + "scene171-standard-renderable-CkeM8lNj.js", "scene171-wgsl-fog-m3PkjS7i.js", "scene171.js" ] diff --git a/lab/public/bundle/manifest/scene173.json b/lab/public/bundle/manifest/scene173.json index 92881a46cc..1b2d42c31a 100644 --- a/lab/public/bundle/manifest/scene173.json +++ b/lab/public/bundle/manifest/scene173.json @@ -1,6 +1,6 @@ { - "rawKB": 62.3, - "gzipKB": 24.2, + "rawKB": 62.7, + "gzipKB": 24.3, "ignoredRawKB": 1485.2, "runtimeChunks": [ "scene173-recast-navigation-CYBQI-zY-Dry_z7x_.js", diff --git a/lab/public/bundle/manifest/scene174.json b/lab/public/bundle/manifest/scene174.json index ef2383e6d9..0362603545 100644 --- a/lab/public/bundle/manifest/scene174.json +++ b/lab/public/bundle/manifest/scene174.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene174-generate-mipmaps-CU0CttiE.js", "scene174-gltf-glb-parser-V10AO6Mf.js", - "scene174-pbr-renderable-DUOxJCit.js", + "scene174-pbr-renderable-C_1QVnsH.js", "scene174-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene174-shader-composer-DishuYLa.js", "scene174-singlelight-hemispheric-wgsl-DYuLXC4p.js", - "scene174-standard-renderable-BTRwJ9QI.js", + "scene174-standard-renderable-BiyW0hqJ.js", "scene174-wgsl-fog-m3PkjS7i.js", "scene174.js" ] diff --git a/lab/public/bundle/manifest/scene175.json b/lab/public/bundle/manifest/scene175.json index ba8f606879..b6917df1a7 100644 --- a/lab/public/bundle/manifest/scene175.json +++ b/lab/public/bundle/manifest/scene175.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene175-generate-mipmaps-Nb0hz7dp.js", "scene175-gltf-glb-parser-BmlEZ507.js", - "scene175-pbr-renderable-D5PrQGKf.js", + "scene175-pbr-renderable-BuT9giXM.js", "scene175-recast-navigation-CYBQI-zY-Dry_z7x_.js", "scene175-shader-composer-DishuYLa.js", "scene175-singlelight-hemispheric-wgsl-C8FJZIgR.js", - "scene175-standard-renderable-BUWEnaOO.js", + "scene175-standard-renderable-CD7IqVFm.js", "scene175-wgsl-fog-m3PkjS7i.js", "scene175.js" ] diff --git a/lab/public/bundle/manifest/scene176.json b/lab/public/bundle/manifest/scene176.json index b4f46b3cde..03a41eee2c 100644 --- a/lab/public/bundle/manifest/scene176.json +++ b/lab/public/bundle/manifest/scene176.json @@ -6,12 +6,12 @@ "scene176-generate-mipmaps-LBrT9Lsp.js", "scene176-gltf-ext-dielectric-C6hvXXiF.js", "scene176-gltf-feature-extras-VHPteGay.js", - "scene176-gltf-feature-registry-CBoLdy3c.js", + "scene176-gltf-feature-registry-bBP4E8zQ.js", "scene176-gltf-json-asset-CoXeIaKr.js", "scene176-ibl-fragment-CeRrvT96.js", "scene176-ibl-skybox-wgsl-CFIBOHHx.js", "scene176-pbr-refraction-B24qt3OL.js", - "scene176-pbr-renderable-DNs7OJUZ.js", + "scene176-pbr-renderable-BufNo-2C.js", "scene176-pbr-transmission-ext-JmNHBrAe.js", "scene176-reflectance-fragment-X3VnKmHR.js", "scene176-rgbd-decode-BNh5jH-x.js", diff --git a/lab/public/bundle/manifest/scene177.json b/lab/public/bundle/manifest/scene177.json index 3b38ccc34e..39f1b47b91 100644 --- a/lab/public/bundle/manifest/scene177.json +++ b/lab/public/bundle/manifest/scene177.json @@ -6,7 +6,7 @@ "scene177-ibl-fragment-CeRrvT96.js", "scene177-ibl-skybox-wgsl-CFIBOHHx.js", "scene177-iridescence-fragment-DbYifzec.js", - "scene177-pbr-renderable-Bo6bqJpT.js", + "scene177-pbr-renderable-DBiTRFht.js", "scene177-rgbd-decode-DgyvGBcz.js", "scene177-scene-uniforms-FTYH6Ct0.js", "scene177.js" diff --git a/lab/public/bundle/manifest/scene178.json b/lab/public/bundle/manifest/scene178.json index dffcb5c8f7..5f3f295a70 100644 --- a/lab/public/bundle/manifest/scene178.json +++ b/lab/public/bundle/manifest/scene178.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene178-generate-mipmaps-BGzDQPIy.js", "scene178-gltf-ext-iridescence-b4LVniAM.js", - "scene178-gltf-feature-registry-HSY3U7a5.js", + "scene178-gltf-feature-registry-Ce-EXrcc.js", "scene178-gltf-glb-parser-d5msxooD.js", "scene178-ibl-fragment-CeRrvT96.js", "scene178-ibl-skybox-wgsl-CFIBOHHx.js", "scene178-iridescence-fragment-DhGVbSHo.js", - "scene178-pbr-renderable-CYd4Ncky.js", + "scene178-pbr-renderable-C-AYx6nQ.js", "scene178-rgbd-decode-BsmWs_Sj.js", "scene178-scene-uniforms-FTYH6Ct0.js", "scene178.js" diff --git a/lab/public/bundle/manifest/scene179.json b/lab/public/bundle/manifest/scene179.json index 408a47b5ee..d7bac2f95f 100644 --- a/lab/public/bundle/manifest/scene179.json +++ b/lab/public/bundle/manifest/scene179.json @@ -6,7 +6,7 @@ "scene179-alpha-test-fragment-5wiD0rFT.js", "scene179-generate-mipmaps-5DBI-Po-.js", "scene179-gltf-json-asset-DHBJMSDr.js", - "scene179-pbr-renderable-Cxeq7IBH.js", + "scene179-pbr-renderable-DPyHKBx0.js", "scene179.js" ] } diff --git a/lab/public/bundle/manifest/scene19.json b/lab/public/bundle/manifest/scene19.json index 8afdf8dcd3..83eef7400e 100644 --- a/lab/public/bundle/manifest/scene19.json +++ b/lab/public/bundle/manifest/scene19.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene19-clearcoat-fragment-BWTsF3_L.js", "scene19-ibl-fragment-CeRrvT96.js", - "scene19-pbr-renderable-DS1L4ZY8.js", + "scene19-pbr-renderable-DrqjKsei.js", "scene19-rgbd-decode-Bhk0icKs.js", "scene19-singlelight-hemispheric-wgsl-CWifOPl1.js", "scene19.js" diff --git a/lab/public/bundle/manifest/scene20.json b/lab/public/bundle/manifest/scene20.json index 12a4195dbe..b009abaa87 100644 --- a/lab/public/bundle/manifest/scene20.json +++ b/lab/public/bundle/manifest/scene20.json @@ -8,7 +8,7 @@ "scene20-cubemap-skybox-material-B495OxWx.js", "scene20-emissive-fragment-B_pIHOBu.js", "scene20-ibl-fragment-CeRrvT96.js", - "scene20-pbr-renderable-3SzkwL70.js", + "scene20-pbr-renderable-CYlpsFeS.js", "scene20-rgbd-decode-ChiiPMbr.js", "scene20-scene-uniforms-FTYH6Ct0.js", "scene20-singlelight-hemispheric-wgsl-TLZaCFzg.js", diff --git a/lab/public/bundle/manifest/scene201.json b/lab/public/bundle/manifest/scene201.json index 4a1520d92d..1c3dd8d132 100644 --- a/lab/public/bundle/manifest/scene201.json +++ b/lab/public/bundle/manifest/scene201.json @@ -1,6 +1,6 @@ { "rawKB": 48.6, - "gzipKB": 19.6, + "gzipKB": 19.7, "ignoredRawKB": 0, "runtimeChunks": [ "scene201-_mat4-storage-f64-BubF55_X.js", diff --git a/lab/public/bundle/manifest/scene205.json b/lab/public/bundle/manifest/scene205.json index b75579adee..e166da8ae3 100644 --- a/lab/public/bundle/manifest/scene205.json +++ b/lab/public/bundle/manifest/scene205.json @@ -8,7 +8,7 @@ "scene205-floating-origin-BpHUAtCM.js", "scene205-pack-mat4-with-offset-BS-Lz8k7.js", "scene205-scene-uniforms-FTYH6Ct0.js", - "scene205-standard-renderable-7LzvJzOL.js", + "scene205-standard-renderable-B6Epry6m.js", "scene205.js" ] } diff --git a/lab/public/bundle/manifest/scene206.json b/lab/public/bundle/manifest/scene206.json index 169f6fd9a7..6931d82683 100644 --- a/lab/public/bundle/manifest/scene206.json +++ b/lab/public/bundle/manifest/scene206.json @@ -8,7 +8,7 @@ "scene206-floating-origin-B3Cmn-Fn.js", "scene206-pack-mat4-with-offset-BS-Lz8k7.js", "scene206-scene-uniforms-FTYH6Ct0.js", - "scene206-standard-renderable-Dwsxsr4C.js", + "scene206-standard-renderable-ClYZ5I4J.js", "scene206.js" ] } diff --git a/lab/public/bundle/manifest/scene21.json b/lab/public/bundle/manifest/scene21.json index 37abef15f6..9b46ffdc8b 100644 --- a/lab/public/bundle/manifest/scene21.json +++ b/lab/public/bundle/manifest/scene21.json @@ -8,7 +8,7 @@ "scene21-generate-mipmaps-C6GFB7Gd.js", "scene21-gltf-glb-parser-GCKDEofq.js", "scene21-ibl-fragment-CeRrvT96.js", - "scene21-pbr-renderable-BdZpH1ZI.js", + "scene21-pbr-renderable-DfaFQUOo.js", "scene21-rgbd-decode-9DTol7mq.js", "scene21-scene-uniforms-FTYH6Ct0.js", "scene21-sheen-fragment-D_beAYtD.js", diff --git a/lab/public/bundle/manifest/scene210.json b/lab/public/bundle/manifest/scene210.json index f8f6804ec4..6e29cbf73e 100644 --- a/lab/public/bundle/manifest/scene210.json +++ b/lab/public/bundle/manifest/scene210.json @@ -4,12 +4,12 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene210-generate-mipmaps-BeLXoJVY.js", - "scene210-gltf-feature-registry-D3_-qnxa.js", + "scene210-gltf-feature-registry-hwCdsYr9.js", "scene210-gltf-feature-xmp-S8Tl-XNQ.js", "scene210-gltf-glb-parser-fJOQf1ny.js", "scene210-gltf-interleave-D_KaKI90.js", "scene210-gltf-normals-DYhLwXNX.js", - "scene210-pbr-renderable-_VnbDlWx.js", + "scene210-pbr-renderable-DSL6Evw7.js", "scene210-singlelight-hemispheric-wgsl-BnD5418z.js", "scene210.js" ] diff --git a/lab/public/bundle/manifest/scene211.json b/lab/public/bundle/manifest/scene211.json index 04f8f815f7..7b905866a4 100644 --- a/lab/public/bundle/manifest/scene211.json +++ b/lab/public/bundle/manifest/scene211.json @@ -9,14 +9,14 @@ "scene211-gltf-ext-quantization-DTtElLs7.js", "scene211-gltf-feature-animations-BsAaXHQL.js", "scene211-gltf-feature-meshopt-Dt65KW5n.js", - "scene211-gltf-feature-registry-DGtnwGxq.js", + "scene211-gltf-feature-registry-BlwXlnxg.js", "scene211-gltf-feature-skeleton-b0IQTFxr.js", "scene211-gltf-json-asset-DHq3uJ8A.js", "scene211-gltf-multi-buffer-CBbuw7_r.js", "scene211-gltf-sampler-denorm-C4cEwMSi.js", - "scene211-pbr-renderable-Dwu09XK5.js", + "scene211-pbr-renderable-DORBCgow.js", "scene211-singlelight-hemispheric-wgsl-ef2cMInq.js", - "scene211-skeleton-fragment-BncZjh4K.js", + "scene211-skeleton-fragment-BeX5MHCp.js", "scene211.js" ] } diff --git a/lab/public/bundle/manifest/scene212.json b/lab/public/bundle/manifest/scene212.json index bbe7eac61b..b2617ea9c0 100644 --- a/lab/public/bundle/manifest/scene212.json +++ b/lab/public/bundle/manifest/scene212.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene212-generate-mipmaps-ffZ1zox-.js", "scene212-gltf-ext-dielectric-C6hvXXiF.js", - "scene212-gltf-feature-registry-BKarV-UN.js", + "scene212-gltf-feature-registry-1HlLddN3.js", "scene212-gltf-glb-parser-DpbXuAuC.js", "scene212-ibl-fragment-CeRrvT96.js", "scene212-ibl-skybox-wgsl-CFIBOHHx.js", "scene212-pbr-refraction-F1UbgaVg.js", - "scene212-pbr-renderable-ACQTbmtT.js", + "scene212-pbr-renderable-Cx5qY7vm.js", "scene212-pbr-transmission-ext-CayZMP4B.js", "scene212-reflectance-fragment-BdYei-sw.js", "scene212-refraction-dispersion-wgsl-BKvBCmZU.js", diff --git a/lab/public/bundle/manifest/scene213.json b/lab/public/bundle/manifest/scene213.json index 67aa2b5c25..7530b26aab 100644 --- a/lab/public/bundle/manifest/scene213.json +++ b/lab/public/bundle/manifest/scene213.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene213-shader-pipeline-cache-Dga-Orlp.js", - "scene213-shader-renderable-DyZNJexF.js", + "scene213-shader-renderable-BTQJ-RWB.js", "scene213-uniform-copy-batch-Be4x2TDU.js", "scene213.js" ] diff --git a/lab/public/bundle/manifest/scene214.json b/lab/public/bundle/manifest/scene214.json index 379d19adb7..a3b11f71cd 100644 --- a/lab/public/bundle/manifest/scene214.json +++ b/lab/public/bundle/manifest/scene214.json @@ -1,5 +1,5 @@ { - "rawKB": 68.4, + "rawKB": 68.5, "gzipKB": 27.1, "ignoredRawKB": 0, "runtimeChunks": [ diff --git a/lab/public/bundle/manifest/scene215.json b/lab/public/bundle/manifest/scene215.json index ed7c78f631..759234b847 100644 --- a/lab/public/bundle/manifest/scene215.json +++ b/lab/public/bundle/manifest/scene215.json @@ -1,12 +1,12 @@ { - "rawKB": 80, - "gzipKB": 32.2, + "rawKB": 80.1, + "gzipKB": 32.3, "ignoredRawKB": 0, "runtimeChunks": [ "scene215-material-view-Dua1bl7W.js", "scene215-multilight-wgsl-DjVmEAHV.js", "scene215-no-color-view-DS70AbGo.js", - "scene215-pbr-renderable-BTy6p4uD.js", + "scene215-pbr-renderable-CvvSqjha.js", "scene215-pbr-shadow-fragment-CoRW6XHj.js", "scene215-shadow-task-CPnE-PjD.js", "scene215-singlelight-directional-wgsl-DzV54v_g.js", diff --git a/lab/public/bundle/manifest/scene216.json b/lab/public/bundle/manifest/scene216.json index d465410ccd..419103a901 100644 --- a/lab/public/bundle/manifest/scene216.json +++ b/lab/public/bundle/manifest/scene216.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene216-pbr-fog-wgsl-BuLKZvgv.js", - "scene216-pbr-renderable-02N1Ub1o.js", + "scene216-pbr-renderable-lLZb3L4E.js", "scene216-singlelight-hemispheric-wgsl-BXqEjdZQ.js", "scene216.js" ] diff --git a/lab/public/bundle/manifest/scene217.json b/lab/public/bundle/manifest/scene217.json index 157ab819d7..7fd4d16f8e 100644 --- a/lab/public/bundle/manifest/scene217.json +++ b/lab/public/bundle/manifest/scene217.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene217-multilight-wgsl-hb_hGU0D.js", - "scene217-pbr-renderable-Cksn-G0t.js", + "scene217-pbr-renderable-BaGcH4Mh.js", "scene217-shader-composer-3MUGhbIu.js", "scene217-standard-renderable-DO07_p5s.js", "scene217-wgsl-fog-m3PkjS7i.js", diff --git a/lab/public/bundle/manifest/scene218.json b/lab/public/bundle/manifest/scene218.json index 657d5dca64..fc467c970a 100644 --- a/lab/public/bundle/manifest/scene218.json +++ b/lab/public/bundle/manifest/scene218.json @@ -9,11 +9,11 @@ "scene218-gltf-ext-spec-gloss-D60sFZYX.js", "scene218-gltf-feature-animations-CUu9JLGT.js", "scene218-gltf-feature-extras-VHPteGay.js", - "scene218-gltf-feature-registry-m6vkAxAJ.js", + "scene218-gltf-feature-registry-5Qw14gWA.js", "scene218-gltf-feature-skeleton-D6qLJHV-.js", "scene218-gltf-glb-parser-BXHcqEF1.js", "scene218-gltf-sampler-desc-DuhcmShP.js", - "scene218-pbr-renderable-C22SMPYt.js", + "scene218-pbr-renderable-_9HoUtYr.js", "scene218-singlelight-hemispheric-wgsl-CCA9Z-0V.js", "scene218.js" ] diff --git a/lab/public/bundle/manifest/scene219.json b/lab/public/bundle/manifest/scene219.json index cf50b1f477..abee4b19ad 100644 --- a/lab/public/bundle/manifest/scene219.json +++ b/lab/public/bundle/manifest/scene219.json @@ -13,7 +13,7 @@ "scene219-gltf-feature-skeleton-BzJmV0gU.js", "scene219-gltf-glb-parser-PDyH09Yg.js", "scene219-gltf-sampler-desc-DMXka76e.js", - "scene219-pbr-renderable-DBbWRUb6.js", + "scene219-pbr-renderable-DtMa1uMs.js", "scene219-singlelight-hemispheric-wgsl-DKsxUnBs.js", "scene219-thin-instance-fragment-CBn2miAC.js", "scene219-thin-instance-gpu-BoCl2HMc.js", diff --git a/lab/public/bundle/manifest/scene22.json b/lab/public/bundle/manifest/scene22.json index ed5c84e2a4..d28cb19121 100644 --- a/lab/public/bundle/manifest/scene22.json +++ b/lab/public/bundle/manifest/scene22.json @@ -1,5 +1,5 @@ { - "rawKB": 97.7, + "rawKB": 97.6, "gzipKB": 39.5, "ignoredRawKB": 0, "runtimeChunks": [ @@ -8,7 +8,7 @@ "scene22-material-view-Dua1bl7W.js", "scene22-multilight-wgsl-My9Iptth.js", "scene22-no-color-view-DHPAYos4.js", - "scene22-pbr-renderable-bdIxhkGG.js", + "scene22-pbr-renderable-44MsA_2d.js", "scene22-pbr-shadow-fragment-CSEDArnD.js", "scene22-pbr-template-gamma-CVmXdy8B.js", "scene22-shader-composer-Bga-tI-0.js", diff --git a/lab/public/bundle/manifest/scene221.json b/lab/public/bundle/manifest/scene221.json index e1fd8afe05..b7dcdf6474 100644 --- a/lab/public/bundle/manifest/scene221.json +++ b/lab/public/bundle/manifest/scene221.json @@ -3,8 +3,8 @@ "gzipKB": 38.6, "ignoredRawKB": 0, "runtimeChunks": [ - "scene221-shader-renderable-e40HDD0m.js", - "scene221-standard-renderable-DpESG6NP.js", + "scene221-shader-renderable-DpGITp4F.js", + "scene221-standard-renderable-DedDiJB_.js", "scene221-swapchain-overlay-ilhN4El_.js", "scene221-ubo-layout-CnrP4RZD.js", "scene221.js" diff --git a/lab/public/bundle/manifest/scene222.json b/lab/public/bundle/manifest/scene222.json index e23f12c3bf..e000556d35 100644 --- a/lab/public/bundle/manifest/scene222.json +++ b/lab/public/bundle/manifest/scene222.json @@ -1,11 +1,11 @@ { - "rawKB": 118.5, + "rawKB": 118.4, "gzipKB": 43.9, "ignoredRawKB": 0, "runtimeChunks": [ "scene222-index-FlPA6jzr.js", "scene222-shader-pipeline-cache-CEwSrI-G.js", - "scene222-shader-renderable-CDZpSjhF.js", + "scene222-shader-renderable-BljVCk02.js", "scene222-standard-renderable-V34lXsdm.js", "scene222-swapchain-overlay-ilhN4El_.js", "scene222-ubo-layout-Cs5YvLYZ.js", diff --git a/lab/public/bundle/manifest/scene223.json b/lab/public/bundle/manifest/scene223.json index 2279a7a9ff..1c4ca0d158 100644 --- a/lab/public/bundle/manifest/scene223.json +++ b/lab/public/bundle/manifest/scene223.json @@ -1,6 +1,6 @@ { - "rawKB": 64, - "gzipKB": 24.3, + "rawKB": 64.4, + "gzipKB": 24.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene223-standard-renderable-W0J5Mh6Q.js", diff --git a/lab/public/bundle/manifest/scene224.json b/lab/public/bundle/manifest/scene224.json index e7b6d9ced4..a50353d4ba 100644 --- a/lab/public/bundle/manifest/scene224.json +++ b/lab/public/bundle/manifest/scene224.json @@ -1,6 +1,6 @@ { - "rawKB": 82, - "gzipKB": 30.3, + "rawKB": 81.9, + "gzipKB": 30.4, "ignoredRawKB": 0, "runtimeChunks": [ "scene224-standard-renderable-I6lj5uUi.js", diff --git a/lab/public/bundle/manifest/scene229.json b/lab/public/bundle/manifest/scene229.json index 271816efb5..a3cab1fc8d 100644 --- a/lab/public/bundle/manifest/scene229.json +++ b/lab/public/bundle/manifest/scene229.json @@ -7,7 +7,7 @@ "scene229-generate-mipmaps-Bd46GUr9.js", "scene229-gltf-json-asset-BmTwNnK_.js", "scene229-gltf-normals-DYhLwXNX.js", - "scene229-pbr-renderable-BZcPetXI.js", + "scene229-pbr-renderable-pQLILvNL.js", "scene229-unlit-fragment-DWtvt7yQ.js", "scene229.js" ] diff --git a/lab/public/bundle/manifest/scene23.json b/lab/public/bundle/manifest/scene23.json index e5f199a12d..ecc66b7547 100644 --- a/lab/public/bundle/manifest/scene23.json +++ b/lab/public/bundle/manifest/scene23.json @@ -8,7 +8,7 @@ "scene23-background-ground-DmmKHN9b.js", "scene23-cubemap-skybox-material-sjF5EPm1.js", "scene23-ibl-fragment-CeRrvT96.js", - "scene23-pbr-renderable-KainoH3-.js", + "scene23-pbr-renderable-DFkrpM9n.js", "scene23-rgbd-decode-BeChsxxY.js", "scene23-scene-uniforms-FTYH6Ct0.js", "scene23-wgsl-helpers-BgB2AJrF.js", diff --git a/lab/public/bundle/manifest/scene24.json b/lab/public/bundle/manifest/scene24.json index c140f5fc2d..f0c04e0a48 100644 --- a/lab/public/bundle/manifest/scene24.json +++ b/lab/public/bundle/manifest/scene24.json @@ -8,7 +8,7 @@ "scene24-generate-mipmaps-BaRC2la_.js", "scene24-parse-camera-D4iQaH48.js", "scene24-point-light-Dzvg6X6L.js", - "scene24-standard-renderable-COSPHzrf.js", + "scene24-standard-renderable-DVwP7F_D.js", "scene24-std-ambient-fragment-qLXGuA_5.js", "scene24-std-cube-reflection-fragment-B-EokvPj.js", "scene24-std-opacity-fragment-BMTURaOK.js", diff --git a/lab/public/bundle/manifest/scene240.json b/lab/public/bundle/manifest/scene240.json index 658dcdab3e..9160dec883 100644 --- a/lab/public/bundle/manifest/scene240.json +++ b/lab/public/bundle/manifest/scene240.json @@ -7,12 +7,12 @@ "scene240-generate-mipmaps-BQZ9ivTX.js", "scene240-gltf-animation-pz-83sgf.js", "scene240-gltf-feature-animations-eB2R-WAg.js", - "scene240-gltf-feature-registry-BRwpNE6V.js", + "scene240-gltf-feature-registry-jRaSGOCz.js", "scene240-gltf-json-asset-m1GV1G-5.js", "scene240-gltf-multi-buffer-DJnVI-01.js", "scene240-gltf-normals-DYhLwXNX.js", "scene240-ibl-fragment-CeRrvT96.js", - "scene240-pbr-renderable-CAFl-XHP.js", + "scene240-pbr-renderable-DCoDH7mE.js", "scene240-rgbd-decode-C4OQPoIw.js", "scene240-scene-uniforms-FTYH6Ct0.js", "scene240.js" diff --git a/lab/public/bundle/manifest/scene241.json b/lab/public/bundle/manifest/scene241.json index ea0d7bfcc1..664494c44d 100644 --- a/lab/public/bundle/manifest/scene241.json +++ b/lab/public/bundle/manifest/scene241.json @@ -21,7 +21,7 @@ "scene241-gltf-feature-animation-pointer-DsTUwbHj.js", "scene241-gltf-feature-animations-DdV2a23m.js", "scene241-gltf-feature-lights-punctual-Ch_m4nvz.js", - "scene241-gltf-feature-registry-DsDrRkQe.js", + "scene241-gltf-feature-registry-YRFQfXt_.js", "scene241-gltf-json-asset-BXfSqNoJ.js", "scene241-gltf-light-pointer-state-B7kFTRxK.js", "scene241-gltf-pbr-builder-ext-BSSTwiwZ.js", @@ -30,7 +30,7 @@ "scene241-light-base-3V3IExF0.js", "scene241-light-matrix-CtsX4GmG.js", "scene241-pbr-refraction-DUIIfYMt.js", - "scene241-pbr-renderable-ri7xhTeY.js", + "scene241-pbr-renderable-BXz4SQne.js", "scene241-pbr-template-ext-CiHpOV5G.js", "scene241-pbr-transmission-ext-CplG9mj1.js", "scene241-reflectance-fragment-DAukmyuS.js", diff --git a/lab/public/bundle/manifest/scene242.json b/lab/public/bundle/manifest/scene242.json index 925c227e67..e121739ecd 100644 --- a/lab/public/bundle/manifest/scene242.json +++ b/lab/public/bundle/manifest/scene242.json @@ -10,10 +10,10 @@ "scene242-gltf-ext-emissive-strength-CButLZnK.js", "scene242-gltf-feature-animation-pointer-FO2HwgIb.js", "scene242-gltf-feature-animations-Cn6xmd6j.js", - "scene242-gltf-feature-registry-DY_rSyEk.js", + "scene242-gltf-feature-registry-C8RoDTOA.js", "scene242-gltf-json-asset-B7qQ2GLF.js", "scene242-ibl-fragment-CeRrvT96.js", - "scene242-pbr-renderable-ir9ToGXE.js", + "scene242-pbr-renderable-CULRpAGU.js", "scene242-rgbd-decode-BQhAFkej.js", "scene242-scene-uniforms-FTYH6Ct0.js", "scene242-visibility-DI-JxJJu.js", diff --git a/lab/public/bundle/manifest/scene243.json b/lab/public/bundle/manifest/scene243.json index be8f0aa53d..40ca731571 100644 --- a/lab/public/bundle/manifest/scene243.json +++ b/lab/public/bundle/manifest/scene243.json @@ -9,12 +9,12 @@ "scene243-gltf-feature-animations-CqCIReXQ.js", "scene243-gltf-feature-extras-VHPteGay.js", "scene243-gltf-feature-morph-BCUQi4W_.js", - "scene243-gltf-feature-registry-C9Q6wITr.js", + "scene243-gltf-feature-registry-DPma2N_Z.js", "scene243-gltf-json-asset-BsQsGpFx.js", "scene243-gltf-pbr-builder-ext-D_B-T_v_.js", "scene243-ibl-fragment-CeRrvT96.js", - "scene243-morph-fragment-CW7eJgWH.js", - "scene243-pbr-renderable-hOzZwsME.js", + "scene243-morph-fragment-BrwjKotI.js", + "scene243-pbr-renderable-IEyBHxXt.js", "scene243-pbr-template-ext-CiHpOV5G.js", "scene243-rgbd-decode-DLCBrViD.js", "scene243-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene244.json b/lab/public/bundle/manifest/scene244.json index dd659001b2..979e631b4b 100644 --- a/lab/public/bundle/manifest/scene244.json +++ b/lab/public/bundle/manifest/scene244.json @@ -12,12 +12,12 @@ "scene244-gltf-ext-uv-transform-YGWG8v_J.js", "scene244-gltf-feature-animation-pointer-B1Q8LIHX.js", "scene244-gltf-feature-animations-DIz_2-L-.js", - "scene244-gltf-feature-registry-Btc-doiU.js", + "scene244-gltf-feature-registry-DazknV16.js", "scene244-gltf-json-asset-pcGJRW4r.js", "scene244-gltf-pbr-builder-ext-u9mmdul2.js", "scene244-ibl-fragment-CeRrvT96.js", "scene244-pbr-refraction-DU061bSP.js", - "scene244-pbr-renderable-DbkDRmLc.js", + "scene244-pbr-renderable-CJdBL4li.js", "scene244-pbr-template-ext-CiHpOV5G.js", "scene244-pbr-transmission-ext-D7nGvwg3.js", "scene244-reflectance-fragment-CgVvAfDW.js", diff --git a/lab/public/bundle/manifest/scene245.json b/lab/public/bundle/manifest/scene245.json index 6af6fb3460..24399f69bd 100644 --- a/lab/public/bundle/manifest/scene245.json +++ b/lab/public/bundle/manifest/scene245.json @@ -8,18 +8,18 @@ "scene245-generate-mipmaps-WM6bVf2v.js", "scene245-gltf-animation-ChRjIPz4.js", "scene245-gltf-feature-animations-BRodhGlz.js", - "scene245-gltf-feature-registry-BOEeRYBr.js", + "scene245-gltf-feature-registry-De-h9Nsa.js", "scene245-gltf-feature-skeleton-CtNw1Dan.js", "scene245-gltf-interleave-7NBE5rBz.js", "scene245-gltf-json-asset-BNblQA1h.js", "scene245-gltf-normals-DYhLwXNX.js", "scene245-gltf-strided-attribute-CGKc0Sx6.js", "scene245-ibl-fragment-CeRrvT96.js", - "scene245-pbr-renderable-BBXmiBHD.js", + "scene245-pbr-renderable-UHYT_A0_.js", "scene245-pbr-template-ext-CiHpOV5G.js", "scene245-rgbd-decode-CZuNE9iG.js", "scene245-scene-uniforms-FTYH6Ct0.js", - "scene245-skeleton-fragment-B6tZf1CT.js", + "scene245-skeleton-fragment-CrUu2NGb.js", "scene245.js" ] } diff --git a/lab/public/bundle/manifest/scene246.json b/lab/public/bundle/manifest/scene246.json index cc45c8e5b3..052348f66f 100644 --- a/lab/public/bundle/manifest/scene246.json +++ b/lab/public/bundle/manifest/scene246.json @@ -8,7 +8,7 @@ "scene246-generate-mipmaps-DzKvtUax.js", "scene246-gltf-animation-CetcJseW.js", "scene246-gltf-feature-animations-CC60XWQx.js", - "scene246-gltf-feature-registry-MiQX06s7.js", + "scene246-gltf-feature-registry-CObggpzn.js", "scene246-gltf-feature-skeleton-D4yy9-j2.js", "scene246-gltf-interleave-ZNL-XZje.js", "scene246-gltf-json-asset-CUl592aH.js", @@ -16,10 +16,10 @@ "scene246-gltf-normals-DYhLwXNX.js", "scene246-gltf-strided-attribute-R3NdoKEL.js", "scene246-ibl-fragment-CeRrvT96.js", - "scene246-pbr-renderable-BMLnd_CJ.js", + "scene246-pbr-renderable-DPmM_9fm.js", "scene246-rgbd-decode-Crvtlca5.js", "scene246-scene-uniforms-FTYH6Ct0.js", - "scene246-skeleton-fragment-CW0bct-Y.js", + "scene246-skeleton-fragment-DGOyP63P.js", "scene246.js" ] } diff --git a/lab/public/bundle/manifest/scene247.json b/lab/public/bundle/manifest/scene247.json index a501701c03..9a625f2978 100644 --- a/lab/public/bundle/manifest/scene247.json +++ b/lab/public/bundle/manifest/scene247.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene247-generate-mipmaps-Crazqgzv.js", "scene247-gltf-feature-extras-VHPteGay.js", - "scene247-gltf-feature-gpu-instancing-BcaJa4W-.js", - "scene247-gltf-feature-registry-gyH_j95a.js", + "scene247-gltf-feature-gpu-instancing-Dhtg-4aS.js", + "scene247-gltf-feature-registry-Q0cVl5BB.js", "scene247-gltf-json-asset-n4R_DhYq.js", "scene247-gltf-multi-buffer-B4rFn0t_.js", "scene247-ibl-fragment-CeRrvT96.js", - "scene247-pbr-renderable-Bpgoag35.js", + "scene247-pbr-renderable-2s-kFBca.js", "scene247-rgbd-decode-dXLwlw-U.js", "scene247-scene-uniforms-FTYH6Ct0.js", "scene247-thin-instance-fragment-CBn2miAC.js", diff --git a/lab/public/bundle/manifest/scene248.json b/lab/public/bundle/manifest/scene248.json index e71afc6f0e..8eefdf82d0 100644 --- a/lab/public/bundle/manifest/scene248.json +++ b/lab/public/bundle/manifest/scene248.json @@ -7,7 +7,7 @@ "scene248-gltf-json-asset-CY3IaG7V.js", "scene248-gltf-sampler-desc-8fgzibKT.js", "scene248-ibl-fragment-CeRrvT96.js", - "scene248-pbr-renderable-ClpWbHcC.js", + "scene248-pbr-renderable-B44u0-eq.js", "scene248-rgbd-decode-D6QLlMzp.js", "scene248-scene-uniforms-FTYH6Ct0.js", "scene248.js" diff --git a/lab/public/bundle/manifest/scene249.json b/lab/public/bundle/manifest/scene249.json index f6d04a8dc3..b34ff976d6 100644 --- a/lab/public/bundle/manifest/scene249.json +++ b/lab/public/bundle/manifest/scene249.json @@ -8,7 +8,7 @@ "scene249-gltf-color-normalize-4-WGzPL2.js", "scene249-gltf-json-asset-lgjq6_KJ.js", "scene249-ibl-fragment-CeRrvT96.js", - "scene249-pbr-renderable-DjgGDa1j.js", + "scene249-pbr-renderable-rPAKdmSs.js", "scene249-pbr-template-ext-CiHpOV5G.js", "scene249-rgbd-decode--JkGDjVJ.js", "scene249-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene251.json b/lab/public/bundle/manifest/scene251.json index f9c283046f..cc81ba8e83 100644 --- a/lab/public/bundle/manifest/scene251.json +++ b/lab/public/bundle/manifest/scene251.json @@ -7,12 +7,12 @@ "scene251-generate-mipmaps-QjbHE23R.js", "scene251-gltf-animation-OYGVVsxj.js", "scene251-gltf-feature-animations-CdpaR0Wq.js", - "scene251-gltf-feature-registry-cfp8ru8d.js", + "scene251-gltf-feature-registry-DynoVIqC.js", "scene251-gltf-feature-skeleton-hmBdKZDm.js", "scene251-gltf-glb-parser-8AD91ACR.js", "scene251-multilight-wgsl-CQ9Bk68V.js", - "scene251-pbr-renderable-Ck1SZbOg.js", - "scene251-skeleton-fragment-CcnoI6ES.js", + "scene251-pbr-renderable-WAdwdvZ4.js", + "scene251-skeleton-fragment-BDetpCmH.js", "scene251.js" ] } diff --git a/lab/public/bundle/manifest/scene253.json b/lab/public/bundle/manifest/scene253.json index 2869281ed5..6a68ab90f7 100644 --- a/lab/public/bundle/manifest/scene253.json +++ b/lab/public/bundle/manifest/scene253.json @@ -24,7 +24,7 @@ "scene253-gltf-feature-extras-VHPteGay.js", "scene253-gltf-feature-lights-punctual-DVQC21Yp.js", "scene253-gltf-feature-morph-OgvZWqfo.js", - "scene253-gltf-feature-registry-vbKkxi5d.js", + "scene253-gltf-feature-registry-CBxd43ei.js", "scene253-gltf-feature-skeleton-C4io4nVl.js", "scene253-gltf-json-asset-pJd1nhz_.js", "scene253-gltf-light-pointer-state-B7kFTRxK.js", @@ -34,16 +34,16 @@ "scene253-iridescence-fragment-T_Mzh3Ja.js", "scene253-light-base-DOz5OTA0.js", "scene253-light-matrix-BpE3lP6k.js", - "scene253-morph-fragment-CKHAU_VY.js", + "scene253-morph-fragment-CB5TLdX3.js", "scene253-multilight-wgsl-CDv_Swqr.js", "scene253-pbr-refraction-Q5OA2ejw.js", - "scene253-pbr-renderable-CggAdRyn.js", + "scene253-pbr-renderable-tU0tJHKK.js", "scene253-pbr-template-ext-CiHpOV5G.js", "scene253-pbr-transmission-ext-DUUJavHf.js", "scene253-reflectance-fragment-Dp_-lOcp.js", "scene253-rgbd-decode-ByCvSNXz.js", "scene253-scene-uniforms-FTYH6Ct0.js", - "scene253-skeleton-fragment-D8lNy0C7.js", + "scene253-skeleton-fragment-D9AVwSAP.js", "scene253-spot-light-CM_J7mjw.js", "scene253-texture-2d-DQo3n8D6.js", "scene253-unlit-fragment-DWtvt7yQ.js", diff --git a/lab/public/bundle/manifest/scene254.json b/lab/public/bundle/manifest/scene254.json index 6503decce0..c51fef8855 100644 --- a/lab/public/bundle/manifest/scene254.json +++ b/lab/public/bundle/manifest/scene254.json @@ -6,11 +6,11 @@ "scene254-generate-mipmaps-CYwo5pXb.js", "scene254-gltf-animation-CHzaih8c.js", "scene254-gltf-feature-animations-CUiCU9Kg.js", - "scene254-gltf-feature-registry-CJ96roQl.js", + "scene254-gltf-feature-registry-DqdvGJvL.js", "scene254-gltf-json-asset-Cy750zhW.js", "scene254-gltf-sampler-denorm-DDbhviuB.js", "scene254-ibl-fragment-CeRrvT96.js", - "scene254-pbr-renderable-C--VSGyf.js", + "scene254-pbr-renderable-BqCbprav.js", "scene254-rgbd-decode-1cqWvEkH.js", "scene254-scene-uniforms-FTYH6Ct0.js", "scene254-singlelight-hemispheric-wgsl-iaIOf9xl.js", diff --git a/lab/public/bundle/manifest/scene255.json b/lab/public/bundle/manifest/scene255.json index dab5c8cb88..ccc76c670a 100644 --- a/lab/public/bundle/manifest/scene255.json +++ b/lab/public/bundle/manifest/scene255.json @@ -8,15 +8,15 @@ "scene255-generate-mipmaps-B7LdcXNp.js", "scene255-gltf-animation-BMBShNo5.js", "scene255-gltf-feature-animations-C3FDPUoA.js", - "scene255-gltf-feature-registry-CcOIcJTR.js", + "scene255-gltf-feature-registry-nfQby86j.js", "scene255-gltf-feature-skeleton-0eBDsmho.js", "scene255-gltf-json-asset-YJrMammb.js", "scene255-gltf-normals-DYhLwXNX.js", "scene255-ibl-fragment-CeRrvT96.js", - "scene255-pbr-renderable-CRWCnbM3.js", + "scene255-pbr-renderable-Xu9QwqR0.js", "scene255-rgbd-decode-DGrHjJdO.js", "scene255-scene-uniforms-FTYH6Ct0.js", - "scene255-skeleton-fragment-CRfvxzkG.js", + "scene255-skeleton-fragment-BsY2gxnz.js", "scene255.js" ] } diff --git a/lab/public/bundle/manifest/scene257.json b/lab/public/bundle/manifest/scene257.json index ea4dbb6021..de0dc0e5b9 100644 --- a/lab/public/bundle/manifest/scene257.json +++ b/lab/public/bundle/manifest/scene257.json @@ -6,11 +6,11 @@ "scene257-flat-normal-wgsl-Cm9mM4tC.js", "scene257-generate-mipmaps-C2jNwHZ4.js", "scene257-gltf-feature-primitive-BMqT3MMH.js", - "scene257-gltf-feature-registry-IwtC-r7V.js", + "scene257-gltf-feature-registry-DBAiJLTS.js", "scene257-gltf-json-asset-DWytQMMR.js", "scene257-gltf-normals-DYhLwXNX.js", "scene257-ibl-fragment-CeRrvT96.js", - "scene257-pbr-renderable-P5UrL6lu.js", + "scene257-pbr-renderable-DbLFoE67.js", "scene257-rgbd-decode-Dqe669eK.js", "scene257-scene-uniforms-FTYH6Ct0.js", "scene257.js" diff --git a/lab/public/bundle/manifest/scene258.json b/lab/public/bundle/manifest/scene258.json index e9fa7b5007..ba0b9965a4 100644 --- a/lab/public/bundle/manifest/scene258.json +++ b/lab/public/bundle/manifest/scene258.json @@ -10,7 +10,7 @@ "scene258-gltf-normals-DYhLwXNX.js", "scene258-gltf-uv-denorm-BTmmiHmh.js", "scene258-ibl-fragment-CeRrvT96.js", - "scene258-pbr-renderable-C-vGNm2j.js", + "scene258-pbr-renderable-CyfZNTyZ.js", "scene258-pbr-template-ext-CiHpOV5G.js", "scene258-rgbd-decode-BIG_Dt6v.js", "scene258-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene259.json b/lab/public/bundle/manifest/scene259.json index b3db8213fd..fac8c7272b 100644 --- a/lab/public/bundle/manifest/scene259.json +++ b/lab/public/bundle/manifest/scene259.json @@ -9,7 +9,7 @@ "scene259-gltf-json-asset-DIKsnRaJ.js", "scene259-gltf-normals-DYhLwXNX.js", "scene259-ibl-fragment-CeRrvT96.js", - "scene259-pbr-renderable-Croa7mHs.js", + "scene259-pbr-renderable-DcBLDfQQ.js", "scene259-rgbd-decode-DyEYghQ3.js", "scene259-scene-uniforms-FTYH6Ct0.js", "scene259.js" diff --git a/lab/public/bundle/manifest/scene26.json b/lab/public/bundle/manifest/scene26.json index 58298a8a32..eb36a77b39 100644 --- a/lab/public/bundle/manifest/scene26.json +++ b/lab/public/bundle/manifest/scene26.json @@ -8,7 +8,7 @@ "scene26-gltf-glb-parser-wcPvSq_F.js", "scene26-ibl-fragment-CeRrvT96.js", "scene26-ibl-skybox-wgsl-CFIBOHHx.js", - "scene26-pbr-renderable-B6reAe_t.js", + "scene26-pbr-renderable-D0z3-LxQ.js", "scene26-rgbd-decode-Y-O5hyxs.js", "scene26-singlelight-point-wgsl-BFpv7Wxi.js", "scene26-subsurface-fragment-kEd_nF4G.js", diff --git a/lab/public/bundle/manifest/scene260.json b/lab/public/bundle/manifest/scene260.json index f848838ac0..73ae6430db 100644 --- a/lab/public/bundle/manifest/scene260.json +++ b/lab/public/bundle/manifest/scene260.json @@ -6,11 +6,11 @@ "scene260-flat-normal-wgsl-Cm9mM4tC.js", "scene260-generate-mipmaps-BDaYF730.js", "scene260-gltf-feature-primitive-BMqT3MMH.js", - "scene260-gltf-feature-registry-DAsUR7pA.js", + "scene260-gltf-feature-registry-Be87QLRX.js", "scene260-gltf-json-asset-BYJEJqvB.js", "scene260-gltf-normals-DYhLwXNX.js", "scene260-ibl-fragment-CeRrvT96.js", - "scene260-pbr-renderable-DkIWcl_Y.js", + "scene260-pbr-renderable-CngLQq0P.js", "scene260-rgbd-decode-DApx_NN-.js", "scene260-scene-uniforms-FTYH6Ct0.js", "scene260.js" diff --git a/lab/public/bundle/manifest/scene265.json b/lab/public/bundle/manifest/scene265.json index 7fb9e0d895..63950ae469 100644 --- a/lab/public/bundle/manifest/scene265.json +++ b/lab/public/bundle/manifest/scene265.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene265-generate-mipmaps-B94XvMrD.js", "scene265-gltf-ext-lights-image-based-Cy0y6Rcm.js", - "scene265-gltf-feature-registry-DwHc02WK.js", + "scene265-gltf-feature-registry-BitDAG68.js", "scene265-gltf-json-asset-Cszv8zST.js", "scene265-ibl-cubemap-upload-aeOmgodq.js", "scene265-ibl-env-assembly-c2UHDMKi.js", "scene265-ibl-fragment-CeRrvT96.js", - "scene265-pbr-renderable-QT5aRh9-.js", + "scene265-pbr-renderable-Bw2rEHov.js", "scene265.js" ] } diff --git a/lab/public/bundle/manifest/scene27.json b/lab/public/bundle/manifest/scene27.json index 7b0dc967c9..bee63e3887 100644 --- a/lab/public/bundle/manifest/scene27.json +++ b/lab/public/bundle/manifest/scene27.json @@ -5,12 +5,12 @@ "runtimeChunks": [ "scene27-generate-mipmaps-CaLzDGPp.js", "scene27-gltf-ext-dielectric-C6hvXXiF.js", - "scene27-gltf-feature-registry-BNakn8Cw.js", + "scene27-gltf-feature-registry-CNDCiXeA.js", "scene27-gltf-feature-variants-BB_eeMhX.js", "scene27-gltf-glb-parser-By8noE3G.js", "scene27-gltf-pbr-builder-ext-2vzfBQ-x.js", "scene27-gltf-variants-DDyPuyX-.js", - "scene27-pbr-renderable-DFqGk0hL.js", + "scene27-pbr-renderable-CXKVZ8TY.js", "scene27-reflectance-fragment-8TyuueJv.js", "scene27-singlelight-hemispheric-wgsl-3mejQG9t.js", "scene27-texture-2d-DQo3n8D6.js", diff --git a/lab/public/bundle/manifest/scene28.json b/lab/public/bundle/manifest/scene28.json index e8ff597cd2..61b1d4d1b3 100644 --- a/lab/public/bundle/manifest/scene28.json +++ b/lab/public/bundle/manifest/scene28.json @@ -6,10 +6,10 @@ "scene28-clearcoat-fragment-C_r2a0mp.js", "scene28-generate-mipmaps-7w1hCgCw.js", "scene28-gltf-ext-clearcoat-BgaLgSgD.js", - "scene28-gltf-feature-registry-CXvUQP_b.js", + "scene28-gltf-feature-registry-0CYa-dn-.js", "scene28-gltf-json-asset-zLp1R-pr.js", "scene28-ibl-fragment-CeRrvT96.js", - "scene28-pbr-renderable-BJPc1hpX.js", + "scene28-pbr-renderable-CU5GJ48k.js", "scene28-rgbd-decode-3bYlLKul.js", "scene28-scene-uniforms-FTYH6Ct0.js", "scene28.js" diff --git a/lab/public/bundle/manifest/scene29.json b/lab/public/bundle/manifest/scene29.json index ca41fc5f6e..5a0376c654 100644 --- a/lab/public/bundle/manifest/scene29.json +++ b/lab/public/bundle/manifest/scene29.json @@ -6,11 +6,11 @@ "scene29-generate-mipmaps-DJCYSY1_.js", "scene29-gltf-ext-sheen-Dv23b_3T.js", "scene29-gltf-ext-uv-transform-tUSEOqA7.js", - "scene29-gltf-feature-registry-BTXrjn6O.js", + "scene29-gltf-feature-registry-CVCtA6fk.js", "scene29-gltf-json-asset-0jGyvXBv.js", "scene29-gltf-pbr-builder-ext-Bu6SRlUL.js", "scene29-ibl-fragment-CeRrvT96.js", - "scene29-pbr-renderable-Su3eeLIL.js", + "scene29-pbr-renderable-Bc0gyCkw.js", "scene29-pbr-template-ext-CiHpOV5G.js", "scene29-rgbd-decode-DZV0-yI9.js", "scene29-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene30.json b/lab/public/bundle/manifest/scene30.json index a1c1681b39..d36ee67a3d 100644 --- a/lab/public/bundle/manifest/scene30.json +++ b/lab/public/bundle/manifest/scene30.json @@ -8,12 +8,12 @@ "scene30-gltf-ext-dielectric-C6hvXXiF.js", "scene30-gltf-ext-uv-transform-DQN5DAiE.js", "scene30-gltf-feature-draco-BSbw1ZBU.js", - "scene30-gltf-feature-registry-miXz7WjX.js", + "scene30-gltf-feature-registry-DLamT4od.js", "scene30-gltf-glb-parser-DJ0cTJ-4.js", "scene30-gltf-pbr-builder-ext-B7YAxFxy.js", "scene30-ibl-fragment-CeRrvT96.js", "scene30-pbr-refraction-DGvv96wJ.js", - "scene30-pbr-renderable-ds9npbv6.js", + "scene30-pbr-renderable-0Hgg3963.js", "scene30-pbr-template-ext-CiHpOV5G.js", "scene30-pbr-transmission-ext-yRQ1qBb2.js", "scene30-rgbd-decode-Cliwq_K6.js", diff --git a/lab/public/bundle/manifest/scene31.json b/lab/public/bundle/manifest/scene31.json index 0b1f271a5c..437accfdda 100644 --- a/lab/public/bundle/manifest/scene31.json +++ b/lab/public/bundle/manifest/scene31.json @@ -6,10 +6,10 @@ "scene31-emissive-fragment-C1TNSo0w.js", "scene31-generate-mipmaps-CoCbB-UD.js", "scene31-gltf-ext-emissive-strength-CButLZnK.js", - "scene31-gltf-feature-registry-pkTt5iE6.js", + "scene31-gltf-feature-registry-DZIwzJXw.js", "scene31-gltf-glb-parser-CzRZVo57.js", "scene31-ibl-fragment-CeRrvT96.js", - "scene31-pbr-renderable-DSgByxEJ.js", + "scene31-pbr-renderable-B3l4qX7v.js", "scene31-rgbd-decode-D1RTHJyW.js", "scene31-scene-uniforms-FTYH6Ct0.js", "scene31.js" diff --git a/lab/public/bundle/manifest/scene32.json b/lab/public/bundle/manifest/scene32.json index 7a1b07c525..bbf7842467 100644 --- a/lab/public/bundle/manifest/scene32.json +++ b/lab/public/bundle/manifest/scene32.json @@ -5,10 +5,10 @@ "runtimeChunks": [ "scene32-generate-mipmaps-DdnhjgK3.js", "scene32-gltf-ext-unlit-1vUYfF7v.js", - "scene32-gltf-feature-registry-BYplY_A6.js", + "scene32-gltf-feature-registry-B5_wz8yd.js", "scene32-gltf-glb-parser-BX2AS-bF.js", "scene32-ibl-fragment-CeRrvT96.js", - "scene32-pbr-renderable-BvjjNlRM.js", + "scene32-pbr-renderable-C1d0bXVW.js", "scene32-rgbd-decode-DEnCQHFZ.js", "scene32-scene-uniforms-FTYH6Ct0.js", "scene32-unlit-fragment-DWtvt7yQ.js", diff --git a/lab/public/bundle/manifest/scene33.json b/lab/public/bundle/manifest/scene33.json index d9c3580c60..2938ca3421 100644 --- a/lab/public/bundle/manifest/scene33.json +++ b/lab/public/bundle/manifest/scene33.json @@ -6,7 +6,7 @@ "scene33-generate-mipmaps-Z_0Gnbub.js", "scene33-gltf-ext-dielectric-C6hvXXiF.js", "scene33-gltf-feature-lights-punctual-B_6j-LQJ.js", - "scene33-gltf-feature-registry-DcsEdCRG.js", + "scene33-gltf-feature-registry-DCkS_4jx.js", "scene33-gltf-glb-parser-DMBUHtjv.js", "scene33-gltf-light-pointer-state-B7kFTRxK.js", "scene33-gltf-sampler-desc-CbONBci7.js", @@ -14,7 +14,7 @@ "scene33-light-base-Z-U1Eg-q.js", "scene33-multilight-wgsl-BaV2tE_w.js", "scene33-pbr-refraction-DOGlBaet.js", - "scene33-pbr-renderable-BpBc2Wfk.js", + "scene33-pbr-renderable-B5QaE6pR.js", "scene33-pbr-transmission-ext-DnNsWD0f.js", "scene33-point-light-DJneFfHW.js", "scene33-rgbd-decode-5e61V1zN.js", diff --git a/lab/public/bundle/manifest/scene34.json b/lab/public/bundle/manifest/scene34.json index ee69618b4c..5fce30f83c 100644 --- a/lab/public/bundle/manifest/scene34.json +++ b/lab/public/bundle/manifest/scene34.json @@ -8,11 +8,11 @@ "scene34-gltf-ext-node-visibility-Xu7_-gD2.js", "scene34-gltf-feature-animation-pointer-Dg1vdjKD.js", "scene34-gltf-feature-animations-DInWHC6L.js", - "scene34-gltf-feature-registry-ByvYV-Xn.js", + "scene34-gltf-feature-registry-DSEkQYDC.js", "scene34-gltf-glb-parser-BxoFTGKn.js", "scene34-gltf-sampler-denorm-DL1-ViwU.js", "scene34-ibl-fragment-CeRrvT96.js", - "scene34-pbr-renderable-LhohO3ui.js", + "scene34-pbr-renderable-D3je5-st.js", "scene34-rgbd-decode-BmSWzo3A.js", "scene34-scene-uniforms-FTYH6Ct0.js", "scene34-visibility-uTP-hWgN.js", diff --git a/lab/public/bundle/manifest/scene35.json b/lab/public/bundle/manifest/scene35.json index 16f719b135..de49897932 100644 --- a/lab/public/bundle/manifest/scene35.json +++ b/lab/public/bundle/manifest/scene35.json @@ -4,11 +4,11 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene35-generate-mipmaps-Dw2dswCB.js", - "scene35-gltf-feature-gpu-instancing-Bas3fQOg.js", - "scene35-gltf-feature-registry-CN741zXa.js", + "scene35-gltf-feature-gpu-instancing-DdNNdzxm.js", + "scene35-gltf-feature-registry-BoOUZk3N.js", "scene35-gltf-glb-parser-BJyme4Vk.js", "scene35-ibl-fragment-CeRrvT96.js", - "scene35-pbr-renderable-Bpe-e-6v.js", + "scene35-pbr-renderable-DEMbMgZW.js", "scene35-rgbd-decode-BysONDsX.js", "scene35-scene-uniforms-FTYH6Ct0.js", "scene35-thin-instance-fragment-CBn2miAC.js", diff --git a/lab/public/bundle/manifest/scene37.json b/lab/public/bundle/manifest/scene37.json index 20554670b8..402b282a80 100644 --- a/lab/public/bundle/manifest/scene37.json +++ b/lab/public/bundle/manifest/scene37.json @@ -8,11 +8,11 @@ "scene37-gltf-ext-dielectric-C6hvXXiF.js", "scene37-gltf-ext-sheen-Dv23b_3T.js", "scene37-gltf-ext-uv-transform-hVTzf5Q9.js", - "scene37-gltf-feature-registry-C8AtiN_z.js", + "scene37-gltf-feature-registry-CrAYTlqi.js", "scene37-gltf-glb-parser-CkbGcOCI.js", "scene37-gltf-pbr-builder-ext-py4n4BEr.js", "scene37-ibl-fragment-CeRrvT96.js", - "scene37-pbr-renderable-CrvjqgTN.js", + "scene37-pbr-renderable-fiqVUWXf.js", "scene37-pbr-template-ext-CiHpOV5G.js", "scene37-reflectance-fragment-DP9YZpYQ.js", "scene37-rgbd-decode-BWySBFNL.js", diff --git a/lab/public/bundle/manifest/scene39.json b/lab/public/bundle/manifest/scene39.json index bc451400c8..05d9b90533 100644 --- a/lab/public/bundle/manifest/scene39.json +++ b/lab/public/bundle/manifest/scene39.json @@ -12,7 +12,7 @@ "scene39-gltf-feature-animation-pointer-CJuvr0W4.js", "scene39-gltf-feature-animations-BXFnLO53.js", "scene39-gltf-feature-lights-punctual-CNjKT39l.js", - "scene39-gltf-feature-registry-CI4Cf7w6.js", + "scene39-gltf-feature-registry-CSDztFGl.js", "scene39-gltf-json-asset-Jn7N8KhP.js", "scene39-gltf-light-pointer-state-B7kFTRxK.js", "scene39-gltf-pbr-builder-ext-SwW9UeLU.js", @@ -20,7 +20,7 @@ "scene39-ibl-fragment-CeRrvT96.js", "scene39-light-base-BmeNaEp7.js", "scene39-light-matrix-CBTBdGsb.js", - "scene39-pbr-renderable-CcOqjo2u.js", + "scene39-pbr-renderable-ClPR-Zha.js", "scene39-pbr-template-ext-CiHpOV5G.js", "scene39-rgbd-decode-B15_Busv.js", "scene39-scene-uniforms-FTYH6Ct0.js", diff --git a/lab/public/bundle/manifest/scene41.json b/lab/public/bundle/manifest/scene41.json index 31bfa0cb6c..f47a063d12 100644 --- a/lab/public/bundle/manifest/scene41.json +++ b/lab/public/bundle/manifest/scene41.json @@ -1,16 +1,16 @@ { - "rawKB": 92.9, + "rawKB": 93, "gzipKB": 37, "ignoredRawKB": 0, "runtimeChunks": [ "scene41-generate-mipmaps-CaoM8To3.js", "scene41-gltf-ext-spec-gloss-D60sFZYX.js", - "scene41-gltf-feature-registry-BgRVxlZA.js", + "scene41-gltf-feature-registry-kN8u4qCu.js", "scene41-gltf-glb-parser-CkOP9avo.js", "scene41-gltf-sampler-desc-S8Gzfsza.js", "scene41-point-light-Bl-vru5p.js", "scene41-shader-composer-DishuYLa.js", - "scene41-standard-renderable-CRg1QH5T.js", + "scene41-standard-renderable-DGTNeky8.js", "scene41-wgsl-fog-m3PkjS7i.js", "scene41.js" ] diff --git a/lab/public/bundle/manifest/scene47.json b/lab/public/bundle/manifest/scene47.json index 43cf52d525..65cd6a5903 100644 --- a/lab/public/bundle/manifest/scene47.json +++ b/lab/public/bundle/manifest/scene47.json @@ -5,11 +5,11 @@ "runtimeChunks": [ "scene47-generate-mipmaps-DOnKpLaE.js", "scene47-gltf-ext-spec-gloss-D60sFZYX.js", - "scene47-gltf-feature-registry-BUDsYjGx.js", + "scene47-gltf-feature-registry-D80QlqDo.js", "scene47-gltf-glb-parser-6-tsF2C2.js", "scene47-gltf-sampler-desc-pdqd2Bvj.js", "scene47-shader-composer-DishuYLa.js", - "scene47-standard-renderable-BFhU18BD.js", + "scene47-standard-renderable-D5zCbf-d.js", "scene47-wgsl-fog-m3PkjS7i.js", "scene47.js" ] diff --git a/lab/public/bundle/manifest/scene48.json b/lab/public/bundle/manifest/scene48.json index ce41ea0d50..d3655ba065 100644 --- a/lab/public/bundle/manifest/scene48.json +++ b/lab/public/bundle/manifest/scene48.json @@ -1,6 +1,6 @@ { "rawKB": 54.4, - "gzipKB": 21.6, + "gzipKB": 21.5, "ignoredRawKB": 0, "runtimeChunks": [ "scene48-standard-renderable-B82OV5q4.js", diff --git a/lab/public/bundle/manifest/scene49.json b/lab/public/bundle/manifest/scene49.json index c410f1d188..bbe9b13fdb 100644 --- a/lab/public/bundle/manifest/scene49.json +++ b/lab/public/bundle/manifest/scene49.json @@ -3,7 +3,7 @@ "gzipKB": 35.2, "ignoredRawKB": 0, "runtimeChunks": [ - "scene49-standard-renderable-Bq1pTdOZ.js", + "scene49-standard-renderable-DH4fsXXy.js", "scene49-swapchain-overlay-ilhN4El_.js", "scene49-ubo-layout-CnrP4RZD.js", "scene49.js" diff --git a/lab/public/bundle/manifest/scene5.json b/lab/public/bundle/manifest/scene5.json index 8741f35f11..5f2a75e494 100644 --- a/lab/public/bundle/manifest/scene5.json +++ b/lab/public/bundle/manifest/scene5.json @@ -10,14 +10,14 @@ "scene5-gltf-color-normalize-4-WGzPL2.js", "scene5-gltf-feature-animations-Tgm8Zcj2.js", "scene5-gltf-feature-morph-BNcyLEaQ.js", - "scene5-gltf-feature-registry-Car-EehQ.js", + "scene5-gltf-feature-registry-C-vImlGO.js", "scene5-gltf-feature-skeleton-BDzV8bwz.js", "scene5-gltf-json-asset-DQeqUDGG.js", - "scene5-morph-fragment-CBlk1Xae.js", - "scene5-pbr-renderable-C0I1nCa_.js", + "scene5-morph-fragment-BzzJlMzQ.js", + "scene5-pbr-renderable-ZArqP8VH.js", "scene5-pbr-template-ext-CiHpOV5G.js", "scene5-singlelight-hemispheric-wgsl-CHPv9RNz.js", - "scene5-skeleton-fragment-CACsocnz.js", + "scene5-skeleton-fragment-DxanHkXT.js", "scene5.js" ] } diff --git a/lab/public/bundle/manifest/scene54.json b/lab/public/bundle/manifest/scene54.json index ea662f4bb1..f9bf698987 100644 --- a/lab/public/bundle/manifest/scene54.json +++ b/lab/public/bundle/manifest/scene54.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene54-billboard-renderable-D8TZU5pi.js", "scene54-scene-uniforms-FTYH6Ct0.js", - "scene54-standard-renderable-CmLRsEFS.js", + "scene54-standard-renderable-DGAG0eqv.js", "scene54.js" ] } diff --git a/lab/public/bundle/manifest/scene56.json b/lab/public/bundle/manifest/scene56.json index 49a6a6e1af..7d62d4645f 100644 --- a/lab/public/bundle/manifest/scene56.json +++ b/lab/public/bundle/manifest/scene56.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene56-billboard-renderable-DvUBDyDr.js", "scene56-scene-uniforms-FTYH6Ct0.js", - "scene56-standard-renderable-C_erBbhE.js", + "scene56-standard-renderable-Begr0fCz.js", "scene56.js" ] } diff --git a/lab/public/bundle/manifest/scene57.json b/lab/public/bundle/manifest/scene57.json index d67f649e06..7c85799ee2 100644 --- a/lab/public/bundle/manifest/scene57.json +++ b/lab/public/bundle/manifest/scene57.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene57-billboard-renderable-CutxZjMd.js", "scene57-scene-uniforms-FTYH6Ct0.js", - "scene57-standard-renderable-Cx4nBC4e.js", + "scene57-standard-renderable-BSoxEW0k.js", "scene57.js" ] } diff --git a/lab/public/bundle/manifest/scene59.json b/lab/public/bundle/manifest/scene59.json index 430b7ed57b..924f54e1f0 100644 --- a/lab/public/bundle/manifest/scene59.json +++ b/lab/public/bundle/manifest/scene59.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene59-billboard-renderable-CWKRAyxb.js", "scene59-scene-uniforms-FTYH6Ct0.js", - "scene59-standard-renderable-DSbHZBBT.js", + "scene59-standard-renderable-BqE0cLTR.js", "scene59.js" ] } diff --git a/lab/public/bundle/manifest/scene6.json b/lab/public/bundle/manifest/scene6.json index 1de781ecaa..02bf11e773 100644 --- a/lab/public/bundle/manifest/scene6.json +++ b/lab/public/bundle/manifest/scene6.json @@ -7,7 +7,7 @@ "scene6-background-ground-O1nPh-aw.js", "scene6-cubemap-skybox-material-VKHB2CT9.js", "scene6-ibl-fragment-CeRrvT96.js", - "scene6-pbr-renderable-sAAHCKk5.js", + "scene6-pbr-renderable-D3D5smo_.js", "scene6-rgbd-decode-DltNvrvh.js", "scene6-scene-uniforms-FTYH6Ct0.js", "scene6-wgsl-helpers-BgB2AJrF.js", diff --git a/lab/public/bundle/manifest/scene7.json b/lab/public/bundle/manifest/scene7.json index 4a563318a0..28b8c5b999 100644 --- a/lab/public/bundle/manifest/scene7.json +++ b/lab/public/bundle/manifest/scene7.json @@ -10,15 +10,15 @@ "scene7-gltf-animation-CaJMcwts.js", "scene7-gltf-feature-animations-BK0cKZGz.js", "scene7-gltf-feature-extras-VHPteGay.js", - "scene7-gltf-feature-registry-DWSNgyQe.js", + "scene7-gltf-feature-registry-B8hEg_Y_.js", "scene7-gltf-feature-skeleton-Ck0MZs6w.js", "scene7-gltf-json-asset-BVtidpmF.js", "scene7-ibl-fragment-CeRrvT96.js", - "scene7-pbr-renderable-_stC70u-.js", + "scene7-pbr-renderable-C3a5hors.js", "scene7-rgbd-decode-BGX-AyGV.js", "scene7-scene-uniforms-FTYH6Ct0.js", "scene7-singlelight-hemispheric-wgsl-BA37pI4_.js", - "scene7-skeleton-fragment-CL1DAIT0.js", + "scene7-skeleton-fragment-CCdA0MT6.js", "scene7-skybox.vertex-Ban7q6IM-D1KSfOUq.js", "scene7-wgsl-helpers-BgB2AJrF.js", "scene7.js" diff --git a/lab/public/bundle/manifest/scene73.json b/lab/public/bundle/manifest/scene73.json index 3d0fe70160..837a7c24d6 100644 --- a/lab/public/bundle/manifest/scene73.json +++ b/lab/public/bundle/manifest/scene73.json @@ -10,14 +10,14 @@ "scene73-fragment-output-CfRmfbPv.js", "scene73-generate-mipmaps-DcnO72FN.js", "scene73-gltf-ext-clearcoat-BgaLgSgD.js", - "scene73-gltf-feature-registry-nFEWSkdC.js", + "scene73-gltf-feature-registry-CbzJ06A9.js", "scene73-gltf-glb-parser-Bm1IrZjT.js", "scene73-ibl-fragment-CeRrvT96.js", "scene73-input-block-CImvS1Uw.js", "scene73-node-env-2j3ozSNZ.js", "scene73-node-renderable-Ik7zhWQu.js", "scene73-pbr-metallic-roughness-block-full-CUhffh-6.js", - "scene73-pbr-renderable-xwW6r3y_.js", + "scene73-pbr-renderable-IeUtZOEu.js", "scene73-perturb-normal-Bqx-TYaB.js", "scene73-reflection-block-CIXuH-z0.js", "scene73-rgbd-decode-BwN6b6f1.js", diff --git a/lab/public/bundle/manifest/scene8.json b/lab/public/bundle/manifest/scene8.json index 82ececccc5..c1b151e75c 100644 --- a/lab/public/bundle/manifest/scene8.json +++ b/lab/public/bundle/manifest/scene8.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene8-background-hdr-skybox-FeMZnJ2H.js", "scene8-ibl-fragment-CeRrvT96.js", - "scene8-pbr-renderable-B3qz3UVV.js", + "scene8-pbr-renderable-S7wEjLG3.js", "scene8-scene-size-CBRA6KcU.js", "scene8-scene-uniforms-FTYH6Ct0.js", "scene8-singlelight-point-wgsl-2QlXbmIF.js", diff --git a/lab/public/bundle/manifest/scene9.json b/lab/public/bundle/manifest/scene9.json index de955f70bb..661a06236e 100644 --- a/lab/public/bundle/manifest/scene9.json +++ b/lab/public/bundle/manifest/scene9.json @@ -6,7 +6,7 @@ "scene9-generate-mipmaps-xw5BdfZd.js", "scene9-normal-map-fragment-EnSQYVZ8.js", "scene9-point-light-ESYN5_k0.js", - "scene9-standard-renderable-DO9SAzPg.js", + "scene9-standard-renderable-H66bBEdG.js", "scene9-std-ambient-fragment-BT8qGSob.js", "scene9-std-opacity-fragment-Dwz7NrjB.js", "scene9-std-reflection-fragment-Cb2Nq9Do.js", diff --git a/lab/public/bundle/manifest/scene94.json b/lab/public/bundle/manifest/scene94.json index 40e0bab765..ba8f81e365 100644 --- a/lab/public/bundle/manifest/scene94.json +++ b/lab/public/bundle/manifest/scene94.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene94-billboard-renderable-BlUVAJut.js", - "scene94-standard-renderable-DWu49Znz.js", + "scene94-standard-renderable-DO3KlBh_.js", "scene94.js" ] } diff --git a/lab/public/bundle/manifest/scene95.json b/lab/public/bundle/manifest/scene95.json index d94b02fa5a..838ad94a8a 100644 --- a/lab/public/bundle/manifest/scene95.json +++ b/lab/public/bundle/manifest/scene95.json @@ -4,7 +4,7 @@ "ignoredRawKB": 0, "runtimeChunks": [ "scene95-billboard-renderable-BxuhE6OT.js", - "scene95-standard-renderable--BLgRvH3.js", + "scene95-standard-renderable-B5Ui0Ke3.js", "scene95.js" ] } diff --git a/lab/public/bundle/manifest/scene98.json b/lab/public/bundle/manifest/scene98.json index 2e2ee2cfcd..fcebe44360 100644 --- a/lab/public/bundle/manifest/scene98.json +++ b/lab/public/bundle/manifest/scene98.json @@ -5,7 +5,7 @@ "runtimeChunks": [ "scene98-billboard-renderable-C-kyjRwT.js", "scene98-scene-uniforms-FTYH6Ct0.js", - "scene98-standard-renderable-CeJ5VwgI.js", + "scene98-standard-renderable--UE4Nr0P.js", "scene98.js" ] } diff --git a/lab/public/bundle/manifest/scene99.json b/lab/public/bundle/manifest/scene99.json index 8e74289310..1024166a68 100644 --- a/lab/public/bundle/manifest/scene99.json +++ b/lab/public/bundle/manifest/scene99.json @@ -8,12 +8,12 @@ "scene99-generate-mipmaps-BFyY2NRa.js", "scene99-gltf-animation-X6oLI2ST.js", "scene99-gltf-feature-animations-BPSdDuNi.js", - "scene99-gltf-feature-registry-DOIh1SXt.js", + "scene99-gltf-feature-registry-DbdETcg2.js", "scene99-gltf-feature-skeleton-CicfKfRQ.js", "scene99-gltf-glb-parser-BtneRupN.js", "scene99-multilight-wgsl-Cbt-Yry0.js", - "scene99-pbr-renderable-DeE0ok3r.js", - "scene99-skeleton-fragment-3eKnBRUa.js", + "scene99-pbr-renderable-BbnV5HjF.js", + "scene99-skeleton-fragment-DzuEfkwE.js", "scene99-types-BVAXdTdf.js", "scene99.js" ] diff --git a/packages/babylon-lite/src/frame-graph/shadow-inputs.ts b/packages/babylon-lite/src/frame-graph/shadow-inputs.ts index 1e037f881f..42769c8c0d 100644 --- a/packages/babylon-lite/src/frame-graph/shadow-inputs.ts +++ b/packages/babylon-lite/src/frame-graph/shadow-inputs.ts @@ -22,6 +22,28 @@ export function _getShadowTaskCasterMeshes(shadowGenerator: ShadowGenerator): re return shadowTaskInputs?.get(shadowGenerator); } +/** + * Cap the cascades a caster mesh renders into: the mesh casts only into cascade layers `0..maxCascade` + * (0 = the nearest). Lets a scene keep small casters out of the far cascades, where their shadows are + * sub-texel anyway — each excluded layer saves that caster's draw + pipeline switch per frame. + * + * Unset (the default) casts into every cascade. Pass `Infinity` to restore that. + * The cap is read when a generator's caster set is (re)supplied through + * {@link setShadowTaskCasterMeshes}; to change it for an already-registered caster, re-supply the + * caster list (a new array) afterwards. Non-cascaded (single-map) generators ignore the cap. + */ +export function setShadowCasterMaxCascade(mesh: Mesh, maxCascade: number): void { + if (maxCascade !== Infinity && (!Number.isInteger(maxCascade) || maxCascade < 0)) { + throw new RangeError("setShadowCasterMaxCascade requires a non-negative integer or Infinity"); + } + mesh._shadowMaxCascade = maxCascade === Infinity ? undefined : maxCascade; +} + +/** @internal Cascade cap for a caster mesh (see {@link setShadowCasterMaxCascade}); Infinity when unset. */ +export function _getShadowCasterMaxCascade(mesh: Mesh): number { + return mesh._shadowMaxCascade ?? Infinity; +} + /** @internal */ export function _setShadowTaskInputPreloader(preloader: (shadowGenerator: ShadowGenerator, casterMeshes: readonly Mesh[]) => Promise): void { shadowTaskInputPreloader = preloader; diff --git a/packages/babylon-lite/src/index.ts b/packages/babylon-lite/src/index.ts index 4f408007f2..d6633fac58 100644 --- a/packages/babylon-lite/src/index.ts +++ b/packages/babylon-lite/src/index.ts @@ -292,7 +292,7 @@ export { createEsmDirectionalShadowGenerator } from "./shadow/esm-directional-sh export { createPcfSpotlightShadowGenerator } from "./shadow/pcf-spotlight-shadow-generator.js"; export { createPcfDirectionalShadowGenerator } from "./shadow/pcf-directional-shadow-generator.js"; export { createCsmDirectionalShadowGenerator, getCsmReceiverTexture, onCsmReceiverUpdate } from "./shadow/csm-directional-shadow-generator.js"; -export { setShadowTaskCasterMeshes } from "./frame-graph/shadow-inputs.js"; +export { setShadowTaskCasterMeshes, setShadowCasterMaxCascade } from "./frame-graph/shadow-inputs.js"; // ─── Animation ─────────────────────────────────────────────────────── export { createAnimationController } from "./skeleton/skeleton-updater.js"; @@ -395,6 +395,8 @@ export { setThinInstanceColor, enableThinInstanceGpuCulling, setThinInstanceCullBoundsPad, + setThinInstanceLodPartner, + clearThinInstanceLodPartner, } from "./mesh/thin-instance.js"; export { addHierarchyInstance, @@ -404,7 +406,7 @@ export { setHierarchyInstanceMatrix, } from "./mesh/hierarchy-instance-pool.js"; export type { HierarchyInstancePool } from "./mesh/hierarchy-instance-pool.js"; -export type { ThinInstanceData } from "./mesh/thin-instance.js"; +export type { ThinInstanceData, ThinInstanceLodPartnerOptions } from "./mesh/thin-instance.js"; // ─── Types ─────────────────────────────────────────────────────────── export type { SceneContext, ImageProcessingConfig, ClipPlane } from "./scene/scene.js"; diff --git a/packages/babylon-lite/src/mesh/mesh-dispose.ts b/packages/babylon-lite/src/mesh/mesh-dispose.ts index 97d8ff0e7f..8432746869 100644 --- a/packages/babylon-lite/src/mesh/mesh-dispose.ts +++ b/packages/babylon-lite/src/mesh/mesh-dispose.ts @@ -1,5 +1,6 @@ import type { Mesh } from "./mesh.js"; import { release } from "../resource/ref-count.js"; +import { _detachThinInstanceLodMesh } from "./thin-instance.js"; /** Destroy all GPU resources owned by a mesh (vertex buffers, skeleton, morph targets). * `_gpu`/`skeleton`/`morphTargets`/`thinInstances` may be SHARED with a clone made via @@ -19,6 +20,7 @@ export function disposeMeshGpu(mesh: Mesh): void { } const ti = mesh.thinInstances; if (ti && release(ti)) { + _detachThinInstanceLodMesh(mesh); ti._gpuBuffer?.destroy(); ti._colorGpuBuffer?.destroy(); ti._drawArgsBuffer?.destroy(); diff --git a/packages/babylon-lite/src/mesh/mesh.ts b/packages/babylon-lite/src/mesh/mesh.ts index eaf990e34e..f57e5e6692 100644 --- a/packages/babylon-lite/src/mesh/mesh.ts +++ b/packages/babylon-lite/src/mesh/mesh.ts @@ -118,6 +118,10 @@ export interface Mesh extends SceneNode { /** @internal */ _gpu: MeshGPU; + /** @internal Reason cloning this mesh is currently forbidden. */ + _clone?: string; + /** @internal Highest CSM cascade this mesh casts into; undefined means all cascades. */ + _shadowMaxCascade?: number; /** @internal */ _cpuPositions?: Float32Array; /** @internal */ diff --git a/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts b/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts index 4a3cf4858b..3a429f197e 100644 --- a/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts +++ b/packages/babylon-lite/src/mesh/thin-instance-cull-binding.ts @@ -19,12 +19,14 @@ import type { RenderTargetSignature } from "../engine/render-target.js"; import type { SceneContext } from "../scene/scene.js"; import type { DrawUpdateContext, Renderable } from "../render/renderable.js"; import type { Mesh } from "./mesh.js"; +import type { ThinInstanceData } from "./thin-instance.js"; import type { ThinInstanceDrawBuffers } from "./thin-instance-gpu.js"; import { createTiCullState, destroyTiCullState, getComputeDispatchBatch, prepareTiCull, + publishTiLodBucket, type ComputeDispatchBatch, type ThinInstanceGpuCullState, } from "./thin-instance-gpu-culling.js"; @@ -76,7 +78,21 @@ export function tryBind( signature: RenderTargetSignature ): TiCullBinding | undefined { const ti = mesh.thinInstances; - if (excluded || !ti?._gpuCullingEnabled) { + if (!ti) { + return undefined; + } + if (ti._lodSource) { + if (excluded) { + throw new Error("Thin-instance LOD partners require an opaque, non-transmissive material"); + } + // The partner consumes buffers published by another renderable's update, so its vertex/indirect + // handles must be resolved at draw time rather than captured in an opaque render bundle. + (renderable as { _direct?: boolean })._direct = true; + // This mesh is the LOD partner of a GPU-culled mesh: it consumes the far bucket that mesh's + // culling compacts for this pass and never draws its own instance count (see bindLodPartner). + return bindLodPartner(ti, signature, hasColor, baseUpdate); + } + if (excluded || !ti._gpuCullingEnabled) { return undefined; } const holder = renderable as CullCachingRenderable; @@ -101,9 +117,13 @@ export function tryBind( _updateBatch: updateBatch, update(context: DrawUpdateContext): void { baseUpdate?.(context); - const res = prepareTiCull(engine, state, mesh, mesh._gpu, ti, hasColor, context, updateBatch); + const partner = ti._lodPartner ?? null; + const res = prepareTiCull(engine, state, mesh, mesh._gpu, ti, hasColor, context, updateBatch, partner); binding.cullDrawBufs = res?.drawBuffers ?? null; binding._args = res?.argsBuffer ?? null; + if (ti._lodBuckets) { + publishTiLodBucket(ti, signature, res); + } }, draw(pass: GPURenderPassEncoder | GPURenderBundleEncoder, indexCount: number, instanceCount: number): void { if (binding._args) { @@ -117,3 +137,45 @@ export function tryBind( }; return binding; } + +/** @internal Binding for the LOD partner of a GPU-culled mesh (`setThinInstanceLodPartner`). Its update + * looks up the far bucket the source mesh's culling published for this pass's signature; its draw issues + * ONLY that bucket's indirect draw — with no bucket (source not culling this pass, or culling fell back) + * it draws nothing, so the partner can never fall back to its own instance count and double-draw. */ +function bindLodPartner(ti: ThinInstanceData, signature: RenderTargetSignature, hasColor: boolean, baseUpdate: ((context: DrawUpdateContext) => void) | undefined): TiCullBinding { + const currentBucket = () => { + const bucket = ti._lodBuckets?.get(signature); + if (!bucket?.active) { + return null; + } + if (hasColor && !bucket.colorBuffer) { + throw new Error("Thin-instance LOD partner requires the source draw to provide instance colors"); + } + return bucket; + }; + const binding: TiCullBinding = { + get cullDrawBufs() { + return currentBucket(); + }, + get _args() { + return currentBucket()?.argsBuffer ?? null; + }, + _updateBatch: getComputeDispatchBatch(signature), + update(context: DrawUpdateContext): void { + baseUpdate?.(context); + }, + draw(pass: GPURenderPassEncoder | GPURenderBundleEncoder, indexCount: number, instanceCount: number): void { + const bucket = currentBucket(); + if (bucket) { + pass.drawIndexedIndirect(bucket.argsBuffer, 0); + } else if (!ti._lodSource) { + if (ti._drawArgsBuffer) { + pass.drawIndexedIndirect(ti._drawArgsBuffer, 0); + } else { + pass.drawIndexed(indexCount, instanceCount); + } + } + }, + }; + return binding; +} diff --git a/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts b/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts index 9036c74db2..a9dc908cb1 100644 --- a/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts +++ b/packages/babylon-lite/src/mesh/thin-instance-gpu-culling.ts @@ -18,13 +18,19 @@ import type { ThinInstanceData } from "./thin-instance.js"; import { syncThinInstanceGpuData } from "./thin-instance-gpu.js"; import type { ThinInstanceDrawBuffers } from "./thin-instance-gpu.js"; import { bumpVisibilityEpoch } from "../engine/engine.js"; +import { retireGpuResources } from "../engine/gpu-resource-retirement.js"; const WORKGROUP_SIZE = 64; const PARAM_BYTES = 192; +// Two-bucket (LOD partner) params append camPosDist (vec4: camera xyz + threshold distance) +// and lodBand after the base struct's implicit 16-byte tail padding. +const LOD_PARAM_BYTES = 224; const COUNT_U32_OFFSET = 44; const MESH_WORLD_FLOAT_OFFSET = 24; const LOCAL_SPHERE_FLOAT_OFFSET = 40; const BOUNDS_PAD_F32_OFFSET = 45; +const CAM_POS_DIST_F32_OFFSET = 48; +const LOD_BAND_F32_OFFSET = 52; const INDIRECT_ARGS_BYTES = 20; const CULL_WGSL_NO_COLOR = /* wgsl */ ` @@ -69,6 +75,74 @@ dstMatrices[outIndex]=srcMatrices[i]; dstColors[outIndex]=srcColors[i]; }`; +// Two-bucket variant used when the mesh has a LOD partner: in-frustum instances partition by camera +// distance — near keeps the mesh's own compacted bucket, far fills the partner's bucket. `isNear` +// dithers the threshold per instance by ±lodBand/2 via a pure hash of the instance index (PCG), so +// the split is deterministic frame-to-frame with no time or randomness input. +const CULL_WGSL_LOD_NO_COLOR = /* wgsl */ ` +struct CullParams{planes:array,6>,meshWorld:mat4x4,localSphere:vec4,count:u32,boundsPad:f32,camPosDist:vec4,lodBand:f32}; +@group(0)@binding(0)var srcMatrices:array>; +@group(0)@binding(1)var dstMatrices:array>; +@group(0)@binding(2)var args:array>; +@group(0)@binding(3)var params:CullParams; +@group(0)@binding(6)var lodMatrices:array>; +@group(0)@binding(7)var lodArgs:array>; +fn visible(world:mat4x4)->bool{ +let center=(world*vec4(params.localSphere.xyz,1.0)).xyz; +let sx=length(world[0].xyz); +let sy=length(world[1].xyz); +let sz=length(world[2].xyz); +let radius=params.localSphere.w*max(max(sx,sy),sz)+params.boundsPad+0.0001; +for(var i=0u;i<6u;i++){ +let p=params.planes[i]; +if(dot(p.xyz,center)+p.w < -radius){return false;} +} +return true; +} +fn isNear(world:mat4x4,i:u32)->bool{ +let center=(world*vec4(params.localSphere.xyz,1.0)).xyz; +var h=i*747796405u+2891336453u; +h=((h>>((h>>28u)+4u))^h)*277803737u; +h=(h>>22u)^h; +let dither=(f32(h)*(1.0/4294967295.0)-0.5)*params.lodBand; +return distance(center,params.camPosDist.xyz)){ +let i=gid.x; +if(i>=params.count){return;} +let world=params.meshWorld*srcMatrices[i]; +if(!visible(world)){return;} +if(isNear(world,i)){ +let outIndex=atomicAdd(&args[1],1u); +dstMatrices[outIndex]=srcMatrices[i]; +}else{ +let outIndex=atomicAdd(&lodArgs[1],1u); +lodMatrices[outIndex]=srcMatrices[i]; +} +}`; + +const CULL_WGSL_LOD_COLOR = `${CULL_WGSL_LOD_NO_COLOR} +@group(0)@binding(4)var srcColors:array>; +@group(0)@binding(5)var dstColors:array>; +@group(0)@binding(8)var lodColors:array>; +@compute @workgroup_size(64) +fn mainLodColor(@builtin(global_invocation_id) gid:vec3){ +let i=gid.x; +if(i>=params.count){return;} +let world=params.meshWorld*srcMatrices[i]; +if(!visible(world)){return;} +if(isNear(world,i)){ +let outIndex=atomicAdd(&args[1],1u); +dstMatrices[outIndex]=srcMatrices[i]; +dstColors[outIndex]=srcColors[i]; +}else{ +let outIndex=atomicAdd(&lodArgs[1],1u); +lodMatrices[outIndex]=srcMatrices[i]; +lodColors[outIndex]=srcColors[i]; +} +}`; + /** Per-render-binding GPU culling state. */ export interface ThinInstanceGpuCullState { /** @internal */ @@ -113,12 +187,28 @@ export interface ThinInstanceGpuCullState { _indexCount: number; /** @internal */ _active: boolean; + /** @internal Whether the current pipeline/bind-group/params target the two-bucket LOD variant. */ + _lodActive: boolean; + /** @internal Far-bucket compacted matrices (two-bucket variant only). */ + _lodMatrixBuffer: GPUBuffer | null; + /** @internal Far-bucket compacted colors (two-bucket variant with instance colors only). */ + _lodColorBuffer: GPUBuffer | null; + /** @internal Far-bucket indirect draw args consumed by the LOD partner's draw. */ + _lodArgsBuffer: GPUBuffer | null; + /** @internal Last index count (the partner mesh's) written to `_lodArgsBuffer`. */ + _lodIndexCount: number; + /** @internal True once the far-bucket args were zeroed for a fallback frame (reset when culling runs). */ + _lodArgsZeroed: boolean; } /** Result consumed by a material draw closure after culling has run for the active pass. */ export interface ThinInstanceGpuCullResult { readonly drawBuffers: ThinInstanceDrawBuffers; readonly argsBuffer: GPUBuffer; + /** Far-bucket compacted instance buffers for the LOD partner's draw (two-bucket variant only). */ + readonly lodDrawBuffers?: ThinInstanceDrawBuffers; + /** Far-bucket indirect args for the LOD partner's draw (two-bucket variant only). */ + readonly lodArgsBuffer?: GPUBuffer; } interface ComputeDispatch { @@ -135,6 +225,8 @@ export interface ComputeDispatchBatch extends DrawUpdateBatch { let _cachedDevice: GPUDevice | null = null; let _pipelineNoColor: GPUComputePipeline | null = null; let _pipelineColor: GPUComputePipeline | null = null; +let _pipelineLodNoColor: GPUComputePipeline | null = null; +let _pipelineLodColor: GPUComputePipeline | null = null; let _dispatchBatches: WeakMap | null = null; /** @internal Return the compute batch associated with one render task. */ @@ -182,7 +274,9 @@ export function getComputeDispatchBatch(signature: RenderTargetSignature): Compu /** Create per-binding culling state. */ export function createTiCullState(): ThinInstanceGpuCullState { - const paramsBytes = new ArrayBuffer(PARAM_BYTES); + // CPU scratch is sized for the larger two-bucket variant; single-bucket states + // only ever upload the first PARAM_BYTES of it. + const paramsBytes = new ArrayBuffer(LOD_PARAM_BYTES); return { _capacity: 0, _visibleMatrixBuffer: null, @@ -202,6 +296,12 @@ export function createTiCullState(): ThinInstanceGpuCullState { _drawBuffers: null, _indexCount: -1, _active: false, + _lodActive: false, + _lodMatrixBuffer: null, + _lodColorBuffer: null, + _lodArgsBuffer: null, + _lodIndexCount: -1, + _lodArgsZeroed: false, }; } @@ -211,10 +311,16 @@ export function destroyTiCullState(state: ThinInstanceGpuCullState): void { state._visibleColorBuffer?.destroy(); state._argsBuffer?.destroy(); state._paramsBuffer?.destroy(); + state._lodMatrixBuffer?.destroy(); + state._lodColorBuffer?.destroy(); + state._lodArgsBuffer?.destroy(); state._visibleMatrixBuffer = null; state._visibleColorBuffer = null; state._argsBuffer = null; state._paramsBuffer = null; + state._lodMatrixBuffer = null; + state._lodColorBuffer = null; + state._lodArgsBuffer = null; state._bindGroup = null; state._drawBuffers = null; } @@ -228,25 +334,20 @@ export function prepareTiCull( ti: ThinInstanceData, hasColor: boolean, context: DrawUpdateContext, - dispatchBatch?: ComputeDispatchBatch + dispatchBatch?: ComputeDispatchBatch, + lodMesh?: Mesh | null ): ThinInstanceGpuCullResult | null { const camera = context._camera; if (!ti._gpuCullingEnabled || !camera || mesh.visible === false || ti.count === 0) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } if (hasColor && !ti.colors) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } const positions = mesh._cpuPositions; if (!state._localSphereReady || state._localPositions !== positions || state._localBoundMin !== mesh.boundMin || state._localBoundMax !== mesh.boundMax) { if (!computeLocalSphere(mesh as Mesh, state._localSphere)) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } state._localSphereReady = true; state._localPositions = positions; @@ -258,17 +359,16 @@ export function prepareTiCull( const sourceMatrixBuffer = ti._gpuBuffer; const sourceColorBuffer = hasColor ? ti._colorGpuBuffer : null; if (!sourceMatrixBuffer || (hasColor && !sourceColorBuffer)) { - setCullActive(state, false); - state._drawBuffers = null; - return null; + return cullFallback(engine, state); } - ensureCullBuffers(engine, state, ti._capacity, hasColor); + const lod = lodMesh ?? null; + ensureCullBuffers(engine, state, ti._capacity, hasColor, lod !== null); const visibleMatrixBuffer = state._visibleMatrixBuffer!; const visibleColorBuffer = hasColor ? state._visibleColorBuffer! : null; const argsBuffer = state._argsBuffer!; const paramsBuffer = state._paramsBuffer!; - const pipeline = getCullPipeline(engine, hasColor); + const pipeline = getCullPipeline(engine, hasColor, lod !== null); if (state._bindGroup === null || state._srcMatrixBuffer !== sourceMatrixBuffer || state._srcColorBuffer !== sourceColorBuffer || state._hasColor !== hasColor) { const entries: GPUBindGroupEntry[] = [ @@ -280,6 +380,12 @@ export function prepareTiCull( if (hasColor) { entries.push({ binding: 4, resource: { buffer: sourceColorBuffer! } }, { binding: 5, resource: { buffer: visibleColorBuffer! } }); } + if (lod) { + entries.push({ binding: 6, resource: { buffer: state._lodMatrixBuffer! } }, { binding: 7, resource: { buffer: state._lodArgsBuffer! } }); + if (hasColor) { + entries.push({ binding: 8, resource: { buffer: state._lodColorBuffer! } }); + } + } state._bindGroup = engine._device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries }); state._srcMatrixBuffer = sourceMatrixBuffer; state._srcColorBuffer = sourceColorBuffer; @@ -288,7 +394,7 @@ export function prepareTiCull( const v = camera.viewport; const aspect = (context.targetWidth / context.targetHeight) * (v ? v.width / v.height : 1); - writeCullParams(engine, state, mesh, gpu.indexCount, ti.count, camera, aspect); + writeCullParams(engine, state, mesh, gpu.indexCount, ti.count, camera, aspect, lod); const dispatch = { pipeline, @@ -307,11 +413,87 @@ export function prepareTiCull( state._drawBuffers = { matrixBuffer: visibleMatrixBuffer, colorBuffer: visibleColorBuffer }; setCullActive(state, true); + if (lod) { + return { + drawBuffers: state._drawBuffers, + argsBuffer, + lodDrawBuffers: { matrixBuffer: state._lodMatrixBuffer!, colorBuffer: hasColor ? state._lodColorBuffer : null }, + lodArgsBuffer: state._lodArgsBuffer!, + }; + } return { drawBuffers: state._drawBuffers, argsBuffer }; } -function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullState, capacity: number, hasColor: boolean): void { +/** Shared early-out: deactivate culling for this pass. When the mesh has (or had) a LOD partner, the + * partner's far-bucket args are zeroed so even a stale recorded indirect draw renders zero instances — + * the full mesh's non-culled fallback then draws ALL instances without ever double-drawing the far set. */ +function cullFallback(engine: EngineContext, state: ThinInstanceGpuCullState): null { + if (state._lodArgsBuffer && !state._lodArgsZeroed) { + engine._currentEncoder.clearBuffer(state._lodArgsBuffer, 4, 4); + state._lodArgsZeroed = true; + } + setCullActive(state, false); + state._drawBuffers = null; + return null; +} + +/** @internal Publish (or withdraw) one pass's far-bucket outputs for the LOD partner's binding. */ +export function publishTiLodBucket(ti: ThinInstanceData, signature: RenderTargetSignature, result: ThinInstanceGpuCullResult | null): void { + const lodDrawBuffers = result?.lodDrawBuffers; + const lodArgsBuffer = result?.lodArgsBuffer; + const buckets = ti._lodBuckets; + if (lodDrawBuffers && lodArgsBuffer && buckets) { + let bucket = buckets.get(signature); + if (!bucket) { + bucket = { matrixBuffer: lodDrawBuffers.matrixBuffer, colorBuffer: lodDrawBuffers.colorBuffer, argsBuffer: lodArgsBuffer, active: true }; + buckets.set(signature, bucket); + return; + } + bucket.matrixBuffer = lodDrawBuffers.matrixBuffer; + bucket.colorBuffer = lodDrawBuffers.colorBuffer; + bucket.argsBuffer = lodArgsBuffer; + bucket.active = true; + return; + } + const bucket = ti._lodBuckets?.get(signature); + if (bucket) { + bucket.active = false; + } +} + +function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullState, capacity: number, hasColor: boolean, lod: boolean): void { const device = engine._device; + if (state._lodActive !== lod) { + // The params uniform size and bind-group layout differ between the single-bucket and + // two-bucket pipeline variants — rebuild both on a pairing transition. + state._paramsBuffer?.destroy(); + state._paramsBuffer = null; + state._bindGroup = null; + if (!lod) { + const lodMat = state._lodMatrixBuffer; + const lodCol = state._lodColorBuffer; + const lodArgs = state._lodArgsBuffer; + if (lodArgs && !state._lodArgsZeroed) { + // A stale partner bundle may still reference these args this frame — make it draw zero instances. + engine._currentEncoder.clearBuffer(lodArgs, 4, 4); + } + if (lodMat || lodArgs) { + // Retire (not destroy): the partner's binding may hold last frame's handles until it re-records. + retireGpuResources(engine, () => { + lodMat?.destroy(); + lodCol?.destroy(); + lodArgs?.destroy(); + }); + } + state._lodMatrixBuffer = null; + state._lodColorBuffer = null; + state._lodArgsBuffer = null; + state._lodIndexCount = -1; + state._lodArgsZeroed = false; + } + state._lodActive = lod; + bumpVisibilityEpoch(); + } if (state._capacity < capacity) { state._visibleMatrixBuffer?.destroy(); state._visibleColorBuffer?.destroy(); @@ -338,6 +520,37 @@ function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullStat state._drawBuffers = null; bumpVisibilityEpoch(); } + if (lod && (!state._lodMatrixBuffer || state._lodMatrixBuffer.size < state._capacity * 64 || (hasColor && !state._lodColorBuffer))) { + const oldMat = state._lodMatrixBuffer; + const oldCol = state._lodColorBuffer; + if (oldMat) { + // Retire (not destroy): the partner's binding may hold last frame's handles until it re-records. + retireGpuResources(engine, () => { + oldMat.destroy(); + oldCol?.destroy(); + }); + } + state._lodMatrixBuffer = device.createBuffer({ + size: Math.max(state._capacity * 64, 4), + usage: BU.VERTEX | BU.STORAGE, + }); + state._lodColorBuffer = hasColor + ? device.createBuffer({ + size: Math.max(state._capacity * 16, 4), + usage: BU.VERTEX | BU.STORAGE, + }) + : null; + state._bindGroup = null; + bumpVisibilityEpoch(); + } + if (lod && !state._lodArgsBuffer) { + state._lodArgsBuffer = device.createBuffer({ + size: INDIRECT_ARGS_BYTES, + usage: BU.INDIRECT | BU.STORAGE | BU.COPY_DST, + }); + state._lodIndexCount = -1; + state._lodArgsZeroed = false; + } if (!state._argsBuffer) { state._argsBuffer = device.createBuffer({ size: INDIRECT_ARGS_BYTES, @@ -346,18 +559,34 @@ function ensureCullBuffers(engine: EngineContext, state: ThinInstanceGpuCullStat } if (!state._paramsBuffer) { state._paramsBuffer = device.createBuffer({ - size: PARAM_BYTES, + size: lod ? LOD_PARAM_BYTES : PARAM_BYTES, usage: BU.UNIFORM | BU.COPY_DST, }); } } -function getCullPipeline(engine: EngineContext, hasColor: boolean): GPUComputePipeline { +function getCullPipeline(engine: EngineContext, hasColor: boolean, lod: boolean): GPUComputePipeline { const device = engine._device; if (_cachedDevice !== device) { _cachedDevice = device; _pipelineNoColor = null; _pipelineColor = null; + _pipelineLodNoColor = null; + _pipelineLodColor = null; + } + if (lod) { + if (hasColor) { + _pipelineLodColor ??= device.createComputePipeline({ + layout: "auto", + compute: { module: device.createShaderModule({ code: CULL_WGSL_LOD_COLOR }), entryPoint: "mainLodColor" }, + }); + return _pipelineLodColor; + } + _pipelineLodNoColor ??= device.createComputePipeline({ + layout: "auto", + compute: { module: device.createShaderModule({ code: CULL_WGSL_LOD_NO_COLOR }), entryPoint: "mainLod" }, + }); + return _pipelineLodNoColor; } if (hasColor) { _pipelineColor ??= device.createComputePipeline({ @@ -373,7 +602,16 @@ function getCullPipeline(engine: EngineContext, hasColor: boolean): GPUComputePi return _pipelineNoColor; } -function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, mesh: Mesh, indexCount: number, instanceCount: number, camera: Camera, aspect: number): void { +function writeCullParams( + engine: EngineContext, + state: ThinInstanceGpuCullState, + mesh: Mesh, + indexCount: number, + instanceCount: number, + camera: Camera, + aspect: number, + lodMesh: Mesh | null +): void { const params = state._paramsF32; const viewProjection = getViewProjectionMatrix(camera, aspect); writeFrustumPlanes(params, viewProjection); @@ -381,6 +619,15 @@ function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, params.set(state._localSphere, LOCAL_SPHERE_FLOAT_OFFSET); state._paramsU32[COUNT_U32_OFFSET] = instanceCount; params[BOUNDS_PAD_F32_OFFSET] = mesh.thinInstances?._cullBoundsPad ?? 0; + if (lodMesh) { + // Distance test uses the culling pass's own camera (same one the frustum planes come from). + const cw = camera.worldMatrix; + params[CAM_POS_DIST_F32_OFFSET] = cw[12]!; + params[CAM_POS_DIST_F32_OFFSET + 1] = cw[13]!; + params[CAM_POS_DIST_F32_OFFSET + 2] = cw[14]!; + params[CAM_POS_DIST_F32_OFFSET + 3] = mesh.thinInstances?._lodDistance ?? 0; + params[LOD_BAND_F32_OFFSET] = mesh.thinInstances?._lodBand ?? 0; + } if (state._indexCount !== indexCount) { const args = state._argsData; @@ -394,7 +641,25 @@ function writeCullParams(engine: EngineContext, state: ThinInstanceGpuCullState, } else { engine._currentEncoder.clearBuffer(state._argsBuffer!, 4, 4); } - engine._device.queue.writeBuffer(state._paramsBuffer!, 0, state._paramsBytes); + if (lodMesh) { + // Far-bucket args carry the PARTNER mesh's index count; the compute pass fills its instance count. + const lodGpu = lodMesh._gpu as MeshGPU | undefined; + const lodIndexCount = lodGpu ? lodGpu.indexCount : 0; + if (state._lodIndexCount !== lodIndexCount) { + const args = state._argsData; + args[0] = lodIndexCount; + args[1] = 0; + args[2] = 0; + args[3] = 0; + args[4] = 0; + engine._device.queue.writeBuffer(state._lodArgsBuffer!, 0, args.buffer, args.byteOffset, args.byteLength); + state._lodIndexCount = lodIndexCount; + } else { + engine._currentEncoder.clearBuffer(state._lodArgsBuffer!, 4, 4); + } + state._lodArgsZeroed = false; + } + engine._device.queue.writeBuffer(state._paramsBuffer!, 0, state._paramsBytes, 0, lodMesh ? LOD_PARAM_BYTES : PARAM_BYTES); } function setCullActive(state: ThinInstanceGpuCullState, active: boolean): void { diff --git a/packages/babylon-lite/src/mesh/thin-instance.ts b/packages/babylon-lite/src/mesh/thin-instance.ts index 1618e71905..11bae2dfef 100644 --- a/packages/babylon-lite/src/mesh/thin-instance.ts +++ b/packages/babylon-lite/src/mesh/thin-instance.ts @@ -6,6 +6,19 @@ import { F32 } from "../engine/typed-arrays.js"; import type { Mat4 } from "../math/types.js"; import type { Mesh } from "./mesh.js"; +import type { RenderTargetSignature } from "../engine/render-target.js"; + +/** @internal One render pass's far-bucket output published by the GPU culler for the LOD partner's draw. */ +export interface ThinInstanceLodBucket { + /** Compacted far-bucket instance matrices (vertex + storage usage). */ + matrixBuffer: GPUBuffer; + /** Compacted far-bucket instance colors, when the culled mesh compacts colors. */ + colorBuffer: GPUBuffer | null; + /** Indirect draw args for the far bucket (partner indexCount, visible far count). */ + argsBuffer: GPUBuffer; + /** False while the source mesh's culling is not running — the partner must draw nothing. */ + active: boolean; +} /** CPU-side data backing a thin-instanced mesh: world matrices, optional colors, and GPU sync state. */ export interface ThinInstanceData { @@ -67,6 +80,18 @@ export interface ThinInstanceData { /** @internal Extra world-space radius added to every instance's culling sphere * (see `setThinInstanceCullBoundsPad`). Undefined reads as 0. */ _cullBoundsPad?: number; + /** @internal LOD partner mesh receiving this mesh's far bucket (set on the full-detail side). */ + _lodPartner?: Mesh | null; + /** @internal Camera distance (world units) at which an in-frustum instance moves to the LOD partner. */ + _lodDistance?: number; + /** @internal Width (world units) of the per-instance threshold dither window (0 = hard cut). */ + _lodBand?: number; + /** @internal Set on the LOD side: mesh whose culling produces this mesh's drawn instances. */ + _lodSource?: Mesh | null; + /** @internal Shared per-pass far-bucket outputs for an LOD pair. Its presence also marks both meshes as paired. */ + _lodBuckets?: WeakMap | null; + /** @internal True when pairing auto-enabled GPU culling on the LOD side (clearing restores it). */ + _lodAutoCull?: boolean; /** @internal Extra-owner count when shared with a clone via `cloneTransformNode` — see * resource/ref-count.ts. Absent/undefined means exactly one (implicit) owner. */ _refCount?: number; @@ -299,3 +324,141 @@ export function setThinInstanceCullBoundsPad(mesh: Mesh, pad: number): void { } ti._cullBoundsPad = pad; } + +/** Options for `setThinInstanceLodPartner`. */ +export interface ThinInstanceLodPartnerOptions { + /** Camera distance (world units) at which an in-frustum instance switches from `fullMesh` to the LOD partner. */ + distance: number; + /** Width (world units) of a per-instance threshold dither window centered on `distance` (default 0 — hard cut). */ + band?: number; +} + +/** Pair a GPU-culled thin-instanced mesh with a lower-detail partner: when `fullMesh`'s compute cull runs, + * in-frustum instances closer than `options.distance` keep drawing through `fullMesh` while farther ones are + * compacted into a second bucket drawn by `lodMesh` instead. `lodMesh` must be a normal scene mesh sharing the + * same thin-instance matrix layout (it may reference the same matrices array); while paired it draws ONLY the + * far bucket — never its own instance count — so when culling is disabled, unavailable, or the instance count + * is 0, `fullMesh` falls back to drawing ALL instances and `lodMesh` draws nothing. `options.band` dithers the + * threshold per instance by ±band/2 via a deterministic hash of the instance index. Like + * `enableThinInstanceGpuCulling`, call after `setThinInstances()` and before `registerScene()`; + * `distance`/`band` may be re-set live by calling again with the same pair. */ +export function setThinInstanceLodPartner(fullMesh: Mesh, lodMesh: Mesh, options: ThinInstanceLodPartnerOptions): void { + const ti = fullMesh.thinInstances; + const lodTi = lodMesh.thinInstances; + if (!ti || !lodTi) { + throw new Error("setThinInstanceLodPartner requires thinInstances on both meshes"); + } + if (fullMesh === lodMesh) { + throw new Error("setThinInstanceLodPartner requires two distinct meshes"); + } + if ((ti._refCount ?? 1) > 1 || (lodTi._refCount ?? 1) > 1) { + throw new Error("setThinInstanceLodPartner does not support thin-instance data shared by mesh clones"); + } + if (!Number.isFinite(options.distance) || options.distance < 0) { + throw new RangeError("setThinInstanceLodPartner distance must be a finite non-negative number"); + } + const band = options.band ?? 0; + if (!Number.isFinite(band) || band < 0) { + throw new RangeError("setThinInstanceLodPartner band must be a finite non-negative number"); + } + if (ti._lodSource) { + throw new Error("setThinInstanceLodPartner fullMesh cannot already be an LOD partner"); + } + if (lodTi._lodPartner) { + throw new Error("setThinInstanceLodPartner lodMesh cannot also own an LOD partner"); + } + if (lodTi._lodSource && lodTi._lodSource !== fullMesh) { + throw new Error("setThinInstanceLodPartner lodMesh is already paired with another source mesh"); + } + if (lodTi.colors && !ti.colors) { + throw new Error("setThinInstanceLodPartner requires source instance colors when the LOD partner uses instance colors"); + } + const previous = ti._lodPartner; + if (previous && previous !== lodMesh) { + const prevTi = previous.thinInstances; + if (prevTi && prevTi._lodSource === fullMesh) { + releaseLodConsumer(prevTi); + } + previous._clone = undefined; + ti._lodBuckets = null; + } + ti._lodPartner = lodMesh; + ti._lodDistance = options.distance; + ti._lodBand = band; + lodTi._lodSource = fullMesh; + fullMesh._clone = lodMesh._clone = "Cannot clone LOD-paired mesh"; + const buckets = ti._lodBuckets ?? new WeakMap(); + ti._lodBuckets = buckets; + lodTi._lodBuckets = buckets; + if (!lodTi._gpuCullingEnabled) { + // Route the partner through the culling draw path (so it can consume the far bucket and skip + // its own count) and make sure the cull module is fetched for its material group. + lodTi._lodAutoCull = true; + lodTi._gpuCullingEnabled = true; + lodTi._gpuVersion = -1; + lodTi._colorGpuVersion = -1; + } +} + +/** Dissolve a `setThinInstanceLodPartner` pairing: `fullMesh` culling reverts to its single-bucket output and + * the former partner returns to independent rendering (drawing its own instance count again after the next + * renderable rebuild; GPU culling auto-enabled by the pairing is switched back off). */ +export function clearThinInstanceLodPartner(fullMesh: Mesh): void { + const ti = fullMesh.thinInstances; + if (!ti?._lodPartner) { + return; + } + const lodMesh = ti._lodPartner; + const lodTi = lodMesh.thinInstances; + ti._lodPartner = null; + ti._lodDistance = undefined; + ti._lodBand = undefined; + ti._lodBuckets = null; + fullMesh._clone = undefined; + lodMesh._clone = undefined; + if (lodTi && lodTi._lodSource === fullMesh) { + releaseLodConsumer(lodTi); + } +} + +/** @internal Break any incoming or outgoing LOD pairing before a mesh's GPU resources are disposed. */ +export function _detachThinInstanceLodMesh(mesh: Mesh): void { + const ti = mesh.thinInstances; + if (!ti) { + return; + } + const source = ti._lodSource; + const sourceTi = source?.thinInstances; + if (source && sourceTi?._lodPartner === mesh) { + sourceTi._lodPartner = null; + sourceTi._lodDistance = undefined; + sourceTi._lodBand = undefined; + sourceTi._lodBuckets = null; + source._clone = undefined; + } + if (ti._lodSource) { + releaseLodConsumer(ti); + } + const partner = ti._lodPartner; + const partnerTi = partner?.thinInstances; + ti._lodPartner = null; + ti._lodDistance = undefined; + ti._lodBand = undefined; + ti._lodBuckets = null; + mesh._clone = undefined; + if (partner && partnerTi?._lodSource === mesh) { + partner._clone = undefined; + releaseLodConsumer(partnerTi); + } +} + +function releaseLodConsumer(lodTi: ThinInstanceData): void { + lodTi._lodSource = null; + lodTi._lodBuckets = null; + if (lodTi._lodAutoCull) { + lodTi._lodAutoCull = false; + lodTi._gpuCullingEnabled = false; + lodTi._gpuVersion = -1; + lodTi._colorGpuVersion = -1; + } +} diff --git a/packages/babylon-lite/src/picking/gpu-picker.ts b/packages/babylon-lite/src/picking/gpu-picker.ts index 2903c661b8..d2fa14ba87 100644 --- a/packages/babylon-lite/src/picking/gpu-picker.ts +++ b/packages/babylon-lite/src/picking/gpu-picker.ts @@ -15,7 +15,7 @@ import { resolveCameraViewport } from "../camera/viewport.js"; import { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from "../resource/gpu-buffers.js"; // ─── Scratch arrays — allocated once, reused across all picks ────── -const _pickVP = new F32(16); +const _pickVP = new F32(20); const PICK_MESH_UBO_BYTES = 80; const PICK_TI_UBO_BYTES = 16; const _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES); @@ -34,7 +34,7 @@ export interface GpuPicker { _scene: SceneContext; /** @internal 1×1 render targets (lazily created). */ _rt: PickTargets1x1 | null; - /** @internal Reusable scene UBO (64 bytes). */ + /** @internal Reusable scene UBO (80 bytes: pick VP + original framebuffer fragment coordinate). */ _sceneUbo: GPUBuffer | null; /** @internal Reusable scene bind group. */ _sceneBG: GPUBindGroup | null; @@ -99,7 +99,7 @@ function ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 function ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer { const device = engine._device; if (!picker._sceneUbo) { - picker._sceneUbo = createEmptyUniformBuffer(engine, 64, "pick-scene-ubo"); + picker._sceneUbo = createEmptyUniformBuffer(engine, 80, "pick-scene-ubo"); const sceneBGL = getPickingSceneBGL(engine); picker._sceneBG = device.createBindGroup({ label: "pick-scene-bg", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] }); } @@ -136,8 +136,9 @@ export interface PickOptions { * * `fn shouldDiscardPick(input: PickDiscardInput) -> bool` * - * The input exposes only generic picker data: `worldPos`, `fragmentCoord` (framebuffer pixel - * coordinates), `pickId`, `thinInstanceIndex`, `hasThinInstance`, and `instanceExtras` (the + * The input exposes only generic picker data: `worldPos`, `fragmentCoord` (the selected pixel + * centre in the original backing framebuffer), `pickId`, `thinInstanceIndex`, + * `hasThinInstance`, and `instanceExtras` (the * original thin-instance matrix w lanes, zero for non-instanced meshes). Storage entries are * uploaded and bound by Lite for the current pick only. */ discard?: PickDiscardRule; @@ -271,6 +272,12 @@ async function pickAsyncImpl(picker: GpuPicker, x: number, y: number, options?: // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ── computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h); + // The pick renders into a 1x1 target, whose fragment position is always (0.5, 0.5). Preserve the + // selected pixel's coordinate in the original backing framebuffer so consumer discard rules can + // reproduce screen-space dithering exactly. Include the camera viewport origin because visible + // material fragment coordinates are surface-wide, while px/py above are viewport-local. + _pickVP[16] = viewport.x + px + 0.5; + _pickVP[17] = viewport.y + py + 0.5; const rt = ensureTargets(engine, picker); const sceneUbo = ensureSceneUbo(engine, picker); diff --git a/packages/babylon-lite/src/picking/picking-pipeline.ts b/packages/babylon-lite/src/picking/picking-pipeline.ts index e46d9cf59a..a884817543 100644 --- a/packages/babylon-lite/src/picking/picking-pipeline.ts +++ b/packages/babylon-lite/src/picking/picking-pipeline.ts @@ -40,7 +40,7 @@ function invalidateIfNeeded(engine: EngineContext): void { export function getPickingSceneBGL(engine: EngineContext): GPUBindGroupLayout { invalidateIfNeeded(engine); if (!_sceneBGL) { - _sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX); + _sceneBGL = createSingleUniformBGL(engine, "picking-scene-bgl", SS.VERTEX | SS.FRAGMENT); } return _sceneBGL; } diff --git a/packages/babylon-lite/src/picking/picking-shader.ts b/packages/babylon-lite/src/picking/picking-shader.ts index a07af981a2..4f53a56d39 100644 --- a/packages/babylon-lite/src/picking/picking-shader.ts +++ b/packages/babylon-lite/src/picking/picking-shader.ts @@ -37,6 +37,14 @@ function pickDiscardStorageDecls(opts?: PickingShaderOptions): string { // ─── Shared structs + fragment shader ─────────────────────────────── +const PICK_SCENE = /* wgsl */ ` +struct SceneUniforms { +viewProjection: mat4x4f, +fragmentCoord: vec2f, +}; +@group(0) @binding(0) var scene: SceneUniforms; +`; + const PICK_FS = /* wgsl */ ` struct VsOut { @builtin(position) p: vec4f, @@ -48,7 +56,7 @@ struct VsOut { }; struct FsOut { @location(0) color: vec4f, @location(1) depth: vec4f }; @fragment fn fs(input: VsOut) -> FsOut { -if (shouldDiscardPick(PickDiscardInput(input.worldPos, input.p.xy, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; } +if (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; } let id = input.pickId; let r = f32((id >> 16u) & 0xFFu) / 255.0; let g = f32((id >> 8u) & 0xFFu) / 255.0; @@ -61,12 +69,11 @@ return FsOut(vec4f(r, g, b, 1.0), vec4f(input.p.z, 0.0, 0.0, 0.0)); export function pickingShaderSource(opts?: PickingShaderOptions): string { return /* wgsl */ ` -struct SceneUniforms { viewProjection: mat4x4f }; +${PICK_SCENE} struct MeshUniforms { world: mat4x4f, pickId: u32, }; -@group(0) @binding(0) var scene: SceneUniforms; @group(1) @binding(0) var mesh: MeshUniforms; ${PICK_DISCARD_INPUT} ${pickDiscardStorageDecls(opts)} @@ -90,11 +97,10 @@ return out; export function pickingThinInstanceShaderSource(opts?: PickingShaderOptions): string { return /* wgsl */ ` -struct SceneUniforms { viewProjection: mat4x4f }; +${PICK_SCENE} struct TIMeshUniforms { baseMeshPickId: u32, }; -@group(0) @binding(0) var scene: SceneUniforms; @group(1) @binding(0) var tiMesh: TIMeshUniforms; @group(1) @binding(1) var instances: array; ${PICK_DISCARD_INPUT} diff --git a/packages/babylon-lite/src/scene/transform-node.ts b/packages/babylon-lite/src/scene/transform-node.ts index 3997380728..a456b4335e 100644 --- a/packages/babylon-lite/src/scene/transform-node.ts +++ b/packages/babylon-lite/src/scene/transform-node.ts @@ -59,6 +59,9 @@ export function cloneTransformNode(src: SceneNode): SceneNode { } function cloneMeshNode(mesh: Mesh): Mesh { + if (mesh._clone) { + throw Error(mesh._clone); + } const meshClone = { ...mesh, name: mesh.name + "_clone", diff --git a/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts b/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts index ed0ef3dbdf..a40694b6fc 100644 --- a/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts +++ b/packages/babylon-lite/src/shadow/csm-shadow-task-hooks.ts @@ -95,6 +95,8 @@ export interface CsmTaskState extends ShadowTaskInternalState { * leave its cached no-color view dangling). This is precise, unlike the global `_materialEpoch` which also * bumps for swaps of unrelated (non-caster) materials. */ _casterMatGens: Map; + /** @internal Per-caster cascade-cap snapshot used to update task membership incrementally. */ + _casterMaxCascades: Map; } export const preloadCsmShadowTaskState = preloadPcfShadowTaskState; @@ -149,28 +151,34 @@ export function ensureCsmShadowTaskState( } } if (!casterMatChanged) { - const prevSet = new Set(existing._casterMeshes); const nextSet = new Set(casterMeshes); const views = existing._materialViews; const gens = existing._casterMatGens; + const caps = existing._casterMaxCascades; + const tasks = existing._tasks; for (const m of existing._casterMeshes) { - if (!nextSet.has(m)) { - for (const t of existing._tasks) { + if (!nextSet.has(m) || m._shadowMaxCascade !== caps.get(m)) { + caps.delete(m); + for (const t of tasks) { removeMeshFromTask(t, m); } } } for (const m of casterMeshes) { - if (!prevSet.has(m) && m.material) { + const maxCascade = m._shadowMaxCascade; + if (!caps.has(m) && m.material) { const view = getNoColorView(m.material, views); - for (const t of existing._tasks) { - t.addMesh(m, { material: view }); + for (let c = 0; c < tasks.length; c++) { + if (c <= (maxCascade ?? c)) { + tasks[c]!.addMesh(m, { material: view }); + } } gens.set(m.material, effectiveCasterGen(m.material)); } + caps.set(m, maxCascade); } // Force each cascade to re-resolve its newly-added pending casters + re-bucket its binding lists. - for (const t of existing._tasks) { + for (const t of tasks) { t._lastVersion = -1; } existing._casterMeshes = casterMeshes; @@ -184,8 +192,7 @@ export function ensureCsmShadowTaskState( // state — the caller swaps to it, so the OLD task is never recorded again. Its GPU buffers may still // be referenced by the next frame command buffer, especially during async pre-first-frame construction, // so retire it only after that frame has submitted and drained. Mirrors resizeMeshGeometry. - const old = existing._task; - retireGpuResources(engine, () => old.dispose()); + retireGpuResources(engine, existing._task.dispose); } const materialViews = new Map(); @@ -215,7 +222,9 @@ export function ensureCsmShadowTaskState( const task = createRenderTask({ name: `csm${i}`, rt, clr: true, cam: camera, _skipClusteredLights: true }, engine, scene); for (const mesh of casterMeshes) { const material = mesh.material; - if (material) { + // Per-caster cascade cap: a capped caster renders only into layers 0..maxCascade (its far-layer + // shadow is sub-texel anyway), saving the excluded layers' draws + pipeline switches. + if (material && i <= (mesh._shadowMaxCascade ?? i)) { task.addMesh(mesh, { material: getNoColorView(material, materialViews) }); } } @@ -246,7 +255,9 @@ export function ensureCsmShadowTaskState( // Snapshot each caster material's gen so the next caster-set change can tell whether a CASTER material was // rebuilt (→ full rebuild) or only the set changed (→ incremental, keeping unchanged casters' packets). const casterMatGens = new Map(); + const casterMaxCascades = new Map(); for (const m of casterMeshes) { + casterMaxCascades.set(m, m._shadowMaxCascade); if (m.material) { casterMatGens.set(m.material, effectiveCasterGen(m.material)); } @@ -266,6 +277,7 @@ export function ensureCsmShadowTaskState( _materialEpoch: scene._materialEpoch, _materialViews: materialViews, _casterMatGens: casterMatGens, + _casterMaxCascades: casterMaxCascades, }; } diff --git a/scripts/validate-pr-release-markers.ts b/scripts/validate-pr-release-markers.ts index bb6c7f9309..b72c7234c1 100644 --- a/scripts/validate-pr-release-markers.ts +++ b/scripts/validate-pr-release-markers.ts @@ -14,6 +14,21 @@ type GitHubPullRequestResponse = { const BREAKING_MARKER = /^(?:BREAKING[ -]CHANGE:|[a-z][a-z0-9-]*(?:\([^)]+\))?!:)/m; const MALFORMED_BREAKING_CHANGE = /^\s*BREAKING[ -]CHANGE(?!:)/im; +const GITHUB_FETCH_ATTEMPTS = 3; + +async function fetchGitHubPullRequest(url: string, headers: Record): Promise { + let response: Response | undefined; + for (let attempt = 1; attempt <= GITHUB_FETCH_ATTEMPTS; attempt++) { + response = await fetch(url, { headers }); + if (response.ok || (response.status !== 429 && response.status < 500) || attempt === GITHUB_FETCH_ATTEMPTS) { + return response; + } + await response.body?.cancel(); + console.warn(`GitHub PR metadata request returned ${response.status} ${response.statusText}; retrying (${attempt}/${GITHUB_FETCH_ATTEMPTS}).`); + await new Promise((resolve) => setTimeout(resolve, attempt * 250)); + } + return response!; +} function cleanAzureValue(value: string | undefined): string | undefined { if (!value || value.startsWith("$(")) { @@ -85,7 +100,7 @@ async function getPullRequestFromGitHub(): Promise }; headers.Authorization = `Bearer ${githubToken}`; - const response = await fetch(`https://api.github.com/repos/${repository}/pulls/${pullRequestNumber}`, { headers }); + const response = await fetchGitHubPullRequest(`https://api.github.com/repos/${repository}/pulls/${pullRequestNumber}`, headers); if (!response.ok) { fail(`Could not read GitHub PR metadata (${response.status} ${response.statusText}); refusing to skip breaking-label enforcement.`); } diff --git a/tests/lite/unit/audit/no-direct-mat4-writes.test.ts b/tests/lite/unit/audit/no-direct-mat4-writes.test.ts index c66c103291..f88bb855eb 100644 --- a/tests/lite/unit/audit/no-direct-mat4-writes.test.ts +++ b/tests/lite/unit/audit/no-direct-mat4-writes.test.ts @@ -78,7 +78,7 @@ const LINE_ALLOWLIST = new Set([ // produces the same F32 bytes packMat4IntoF32 would write for offset 0. // TODO(HPM-followup): collapse into packMat4IntoF32 to fully retire the // opaque-Mat4-as-array-like dependency. - "packages/babylon-lite/src/mesh/thin-instance.ts:167", + "packages/babylon-lite/src/mesh/thin-instance.ts:192", ]); /** Variable-name heuristic: matrix-like identifiers. */ diff --git a/tests/lite/unit/mesh-clone-gpu-dispose.test.ts b/tests/lite/unit/mesh-clone-gpu-dispose.test.ts index 4a32d0dc56..e3f6ab84f7 100644 --- a/tests/lite/unit/mesh-clone-gpu-dispose.test.ts +++ b/tests/lite/unit/mesh-clone-gpu-dispose.test.ts @@ -9,6 +9,7 @@ import type { SkeletonData } from "../../../packages/babylon-lite/src/animation/ import type { EngineContext } from "../../../packages/babylon-lite/src/engine/engine"; import { ObservableVec3 } from "../../../packages/babylon-lite/src/math/observable-vec3"; import { ObservableQuat } from "../../../packages/babylon-lite/src/math/observable-quat"; +import { setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; function fakeBuffer(): GPUBuffer { return { destroy: vi.fn() } as unknown as GPUBuffer; @@ -41,6 +42,44 @@ function makeMesh(gpu: MeshGPU): Mesh { } describe("mesh clone GPU buffer ownership", () => { + it("rejects cloning a mesh while its thin-instance data participates in an LOD pair", () => { + const gpu: MeshGPU = { + positionBuffer: fakeBuffer(), + normalBuffer: fakeBuffer(), + uvBuffer: fakeBuffer(), + indexBuffer: fakeBuffer(), + indexCount: 3, + indexFormat: "uint16", + }; + const makeTi = (): ThinInstanceData => + ({ + matrices: new Float32Array(16), + count: 1, + _capacity: 1, + _version: 1, + _gpuBuffer: null, + _gpuBufferStorage: false, + _gpuVersion: 0, + _dirtyMin: 0, + _dirtyMax: 1, + _colorVersion: 0, + _colorDirtyMin: 0, + _colorDirtyMax: 0, + _colorGpuBuffer: null, + _colorGpuBufferStorage: false, + _colorGpuVersion: 0, + _gpuCullingEnabled: false, + }) satisfies ThinInstanceData; + const src = makeMesh(gpu); + const lod = makeMesh(gpu); + src.thinInstances = makeTi(); + lod.thinInstances = makeTi(); + setThinInstanceLodPartner(src, lod, { distance: 10 }); + + expect(() => cloneTransformNode(src)).toThrow("LOD-paired"); + expect(() => cloneTransformNode(lod)).toThrow("LOD-paired"); + }); + it("resizing one clone releases its claim without retiring geometry still owned by its sibling", () => { const gpu: MeshGPU = { positionBuffer: fakeBuffer(), diff --git a/tests/lite/unit/picking-discard-api.test.ts b/tests/lite/unit/picking-discard-api.test.ts index 15b00f99d7..424134682e 100644 --- a/tests/lite/unit/picking-discard-api.test.ts +++ b/tests/lite/unit/picking-discard-api.test.ts @@ -63,9 +63,12 @@ interface MockBufferRecord { function makePickerEngine(): ReturnType & { pass: { drawCalls: { group2Bound: boolean }[] }; buffers: MockBufferRecord[]; + writes: { label: string | undefined; data: Float32Array }[]; } { const base = makeEngine(); const buffers: MockBufferRecord[] = []; + const labels = new WeakMap(); + const writes: { label: string | undefined; data: Float32Array }[] = []; const passState = { drawCalls: [] as { group2Bound: boolean }[], pipeline: null as (GPURenderPipeline & { _bindGroupLayoutCount?: number }) | null, @@ -96,7 +99,13 @@ function makePickerEngine(): ReturnType & { queue: { writeBuffer: GPUQueue["writeBuffer"]; submit: GPUQueue["submit"] }; }; device.queue = { - writeBuffer() {}, + writeBuffer(buffer, _offset, data, dataOffset, size) { + const source = ArrayBuffer.isView(data) + ? new Uint8Array(data.buffer, data.byteOffset + (dataOffset ?? 0), size ?? data.byteLength - (dataOffset ?? 0)) + : new Uint8Array(data, dataOffset ?? 0, size); + const copy = source.slice().buffer; + writes.push({ label: labels.get(buffer as object), data: new Float32Array(copy) }); + }, submit() {}, }; device.createTexture = () => @@ -133,6 +142,7 @@ function makePickerEngine(): ReturnType & { mapped = false; }, } as unknown as GPUBuffer; + labels.set(buffer as object, descriptor.label); buffers.push({ descriptor: { ...descriptor }, destroy }); return buffer; }; @@ -146,7 +156,7 @@ function makePickerEngine(): ReturnType & { (globalThis as unknown as { GPUMapMode: { READ: number } }).GPUMapMode ??= { READ: 1 }; base.engine._retirements = []; - return { ...base, pass: passState, buffers }; + return { ...base, pass: passState, buffers, writes }; } function makePickScene(engine: EngineContext): { scene: Parameters[0]; mesh: Mesh; discardBuffer: GPUBuffer } { @@ -203,12 +213,14 @@ describe("picking discard shader API", () => { expect(regular).toContain("return false;"); expect(regular).toContain("out.hasThinInstance = 0u;"); expect(regular).toContain("out.thinInstanceIndex = 0xffffffffu;"); + expect(regular).toContain("PickDiscardInput(input.worldPos, scene.fragmentCoord"); expect(thin).toContain("fn shouldDiscardPick(input: PickDiscardInput) -> bool"); expect(thin).toContain("return false;"); expect(thin).toContain("out.hasThinInstance = 1u;"); expect(thin).toContain("out.thinInstanceIndex = instanceIndex;"); expect(thin).toContain("out.instanceExtras = vec4f(m[0].w, m[1].w, m[2].w, m[3].w);"); + expect(thin).toContain("PickDiscardInput(input.worldPos, scene.fragmentCoord"); }); it("injects a custom discard rule into regular and thin-instance picking shaders", () => { @@ -223,7 +235,7 @@ return input.hasThinInstance == 1u && input.instanceExtras.x > 4.0; expect(regular).toContain(discardWgsl); expect(thin).toContain(discardWgsl); const discardCall = - "if (shouldDiscardPick(PickDiscardInput(input.worldPos, input.p.xy, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }"; + "if (shouldDiscardPick(PickDiscardInput(input.worldPos, scene.fragmentCoord, input.pickId, input.thinInstanceIndex, input.hasThinInstance, input.instanceExtras))) { discard; }"; expect(regular).toContain(discardCall); expect(thin).toContain(discardCall); expect(thin).toContain("let world = mat4x4f("); @@ -295,6 +307,40 @@ fn shouldDiscardPick(input: PickDiscardInput) -> bool { return data[0].x > 1.0 & expect(pass.drawCalls).toEqual([{ group2Bound: true }]); }); + it("uploads the selected pixel center in original framebuffer coordinates", async () => { + const { engine } = makePickerEngine(); + const { scene } = makePickScene(engine); + scene.camera!.viewport = { x: 0.25, y: 0.25, width: 0.5, height: 0.5 }; + const writeBuffer = vi.spyOn(engine._device.queue, "writeBuffer"); + const picker = createGpuPicker(scene); + + await pickAsync(picker, 20, 20); + + const sceneWrite = writeBuffer.mock.calls.find((call) => call[2] instanceof Float32Array && call[2].length === 20); + expect(sceneWrite).toBeDefined(); + expect(Array.from((sceneWrite![2] as Float32Array).subarray(16, 18))).toEqual([20.5, 20.5]); + }); + + it("publishes the selected original framebuffer pixel under DPR and a nonzero viewport", async () => { + const { engine, writes } = makePickerEngine(); + const { scene } = makePickScene(engine); + const surface = scene.surface as unknown as { canvas: { width: number; height: number; clientWidth: number; clientHeight: number } }; + surface.canvas = { width: 128, height: 96, clientWidth: 64, clientHeight: 48 }; + (scene.camera as unknown as { viewport: { x: number; y: number; width: number; height: number } }).viewport = { + x: 0.25, + y: 0.25, + width: 0.5, + height: 0.5, + }; + + await pickAsync(createGpuPicker(scene), 20, 15); + + const sceneWrite = writes.find((write) => write.label === "pick-scene-ubo"); + expect(sceneWrite?.data).toHaveLength(20); + expect(sceneWrite?.data[16]).toBe(40.5); + expect(sceneWrite?.data[17]).toBe(30.5); + }); + it("destroys temporary pick buffers after the pick submission readback completes", async () => { const { engine, buffers } = makePickerEngine(); const { scene } = makePickScene(engine); diff --git a/tests/lite/unit/shadow-caster-max-cascade.test.ts b/tests/lite/unit/shadow-caster-max-cascade.test.ts new file mode 100644 index 0000000000..9b6ccf343a --- /dev/null +++ b/tests/lite/unit/shadow-caster-max-cascade.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { EngineContext } from "../../../packages/babylon-lite/src/engine/engine"; +import type { RenderTask } from "../../../packages/babylon-lite/src/frame-graph/render-task"; +import type { Material, MaterialView } from "../../../packages/babylon-lite/src/material/material"; +import type { Mesh } from "../../../packages/babylon-lite/src/mesh/mesh"; +import type { SceneContext } from "../../../packages/babylon-lite/src/scene/scene-core"; +import { _getShadowCasterMaxCascade, setShadowCasterMaxCascade } from "../../../packages/babylon-lite/src/frame-graph/shadow-inputs"; +import { ensureCsmShadowTaskState, type CsmConfig, type CsmTaskState } from "../../../packages/babylon-lite/src/shadow/csm-shadow-task-hooks"; +import type { ShadowGenerator } from "../../../packages/babylon-lite/src/shadow/shadow-generator"; + +function makeMesh(material?: Material): Mesh { + return { material } as unknown as Mesh; +} + +function makeTask(mesh: Mesh): RenderTask { + const task = { + _pendingMeshes: [{ mesh, material: mesh.material }], + _renderables: [], + _opaqueBindings: [], + _directBindings: [], + _transparentBindings: [], + _opaqueBundles: [], + _lastVersion: 0, + addMesh: vi.fn((added: Mesh, opts?: { material?: Material }) => { + task._pendingMeshes.push({ mesh: added, material: opts?.material ?? added.material }); + }), + }; + return task as unknown as RenderTask; +} + +describe("setShadowCasterMaxCascade", () => { + it("defaults every mesh to casting into all cascades (Infinity)", () => { + expect(_getShadowCasterMaxCascade(makeMesh())).toBe(Infinity); + }); + + it("stores a per-mesh cap without affecting other meshes", () => { + const capped = makeMesh(); + const other = makeMesh(); + setShadowCasterMaxCascade(capped, 0); + expect(_getShadowCasterMaxCascade(capped)).toBe(0); + expect(_getShadowCasterMaxCascade(other)).toBe(Infinity); + }); + + it("rejects caps that are not non-negative integer cascade indexes", () => { + const mesh = makeMesh(); + expect(() => setShadowCasterMaxCascade(mesh, -1)).toThrow(RangeError); + expect(() => setShadowCasterMaxCascade(mesh, 0.5)).toThrow(RangeError); + expect(() => setShadowCasterMaxCascade(mesh, Number.NaN)).toThrow(RangeError); + }); + + it("restores all-cascade casting when re-set to Infinity", () => { + const mesh = makeMesh(); + setShadowCasterMaxCascade(mesh, 0); + setShadowCasterMaxCascade(mesh, Infinity); + expect(_getShadowCasterMaxCascade(mesh)).toBe(Infinity); + }); + + it("reassigns an existing caster when a re-supplied list changes its cap", () => { + const material = { _uboVersion: 0 } as Material; + const view = {} as MaterialView; + const mesh = makeMesh(material); + const tasks = [makeTask(mesh), makeTask(mesh), makeTask(mesh)]; + const state = { + _tasks: tasks, + _casterMeshes: [mesh], + _renderableVersion: 1, + _materialEpoch: 1, + _materialViews: new Map([[material, view]]), + _casterMatGens: new Map([[material, 0]]), + _casterMaxCascades: new Map([[mesh, undefined]]), + } as unknown as CsmTaskState; + const scene = { _renderableVersion: 2, _materialEpoch: 1 } as SceneContext; + + setShadowCasterMaxCascade(mesh, 0); + const result = ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [mesh], state); + + expect(result).toBe(state); + expect(tasks[0]!.addMesh).toHaveBeenCalledOnce(); + expect(tasks[1]!.addMesh).not.toHaveBeenCalled(); + expect(tasks[2]!.addMesh).not.toHaveBeenCalled(); + expect(tasks[0]!._pendingMeshes.map((entry) => entry.mesh)).toEqual([mesh]); + expect(tasks[1]!._pendingMeshes).toHaveLength(0); + expect(tasks[2]!._pendingMeshes).toHaveLength(0); + expect(state._casterMaxCascades.get(mesh)).toBe(0); + + ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [], state); + expect(state._casterMaxCascades.has(mesh)).toBe(false); + ensureCsmShadowTaskState({} as EngineContext, scene, {} as ShadowGenerator, {} as CsmConfig, [mesh], state); + expect(tasks[0]!.addMesh).toHaveBeenCalledTimes(2); + }); +}); diff --git a/tests/lite/unit/thin-instance-gpu-culling.test.ts b/tests/lite/unit/thin-instance-gpu-culling.test.ts index d63e08cc8f..c68ae366b0 100644 --- a/tests/lite/unit/thin-instance-gpu-culling.test.ts +++ b/tests/lite/unit/thin-instance-gpu-culling.test.ts @@ -6,9 +6,11 @@ import type { RenderTargetSignature } from "../../../packages/babylon-lite/src/e import type { Mat4 } from "../../../packages/babylon-lite/src/math/types"; import type { Mesh, MeshGPU } from "../../../packages/babylon-lite/src/mesh/mesh"; import { updateMeshGeometry } from "../../../packages/babylon-lite/src/mesh/mesh-factories"; -import { createTiCullState, getComputeDispatchBatch, prepareTiCull } from "../../../packages/babylon-lite/src/mesh/thin-instance-gpu-culling"; -import type { ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; -import type { DrawUpdateContext } from "../../../packages/babylon-lite/src/render/renderable"; +import { tryBind } from "../../../packages/babylon-lite/src/mesh/thin-instance-cull-binding"; +import { createTiCullState, getComputeDispatchBatch, prepareTiCull, publishTiLodBucket } from "../../../packages/babylon-lite/src/mesh/thin-instance-gpu-culling"; +import { clearThinInstanceLodPartner, setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; +import type { DrawUpdateContext, Renderable } from "../../../packages/babylon-lite/src/render/renderable"; +import type { SceneContext } from "../../../packages/babylon-lite/src/scene/scene"; function identity(): Mat4 { return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) as unknown as Mat4; @@ -145,4 +147,152 @@ describe("thin-instance GPU culling submission", () => { expect(writeBuffer.mock.calls.filter((call) => call[0] === state._argsBuffer && call[4] === 20)).toHaveLength(1); expect(buffers.some((buffer) => (buffer.descriptor.usage & GPUBufferUsage.INDIRECT) !== 0)).toBe(true); }); + + it("allocates and publishes a second compacted bucket for an LOD partner", () => { + const buffers: (GPUBuffer & { descriptor: GPUBufferDescriptor })[] = []; + const writeBuffer = vi.fn(); + const pipeline = { + getBindGroupLayout: vi.fn(() => ({}) as GPUBindGroupLayout), + } as unknown as GPUComputePipeline; + const device = { + createBuffer: vi.fn((descriptor: GPUBufferDescriptor) => { + const buffer = { descriptor, size: descriptor.size, destroy: vi.fn() } as unknown as GPUBuffer & { descriptor: GPUBufferDescriptor }; + buffers.push(buffer); + return buffer; + }), + createShaderModule: vi.fn(() => ({}) as GPUShaderModule), + createComputePipeline: vi.fn(() => pipeline), + createBindGroup: vi.fn(() => ({}) as GPUBindGroup), + queue: { writeBuffer }, + } as unknown as GPUDevice; + const engine = { + _device: device, + _currentEncoder: { + beginComputePass: () => + ({ + setPipeline() {}, + setBindGroup() {}, + dispatchWorkgroups() {}, + end() {}, + }) as unknown as GPUComputePassEncoder, + clearBuffer: vi.fn(), + } as unknown as GPUCommandEncoder, + } as unknown as EngineContext; + const ti = makeThinInstances(8); + const mesh = { + visible: true, + worldMatrix: identity(), + _cpuPositions: new Float32Array([-1, -1, -1, 1, 1, 1]), + boundMin: [-1, -1, -1], + boundMax: [1, 1, 1], + thinInstances: ti, + } as unknown as Mesh; + const gpu = { + positionBuffer: {} as GPUBuffer, + normalBuffer: {} as GPUBuffer, + uvBuffer: {} as GPUBuffer, + indexBuffer: {} as GPUBuffer, + indexCount: 3, + indexFormat: "uint32", + hasUv: false, + hasUv2: false, + hasTangent: false, + hasColor: false, + } satisfies MeshGPU; + mesh._gpu = gpu; + const lodMesh = { _gpu: { ...gpu, indexCount: 12 } } as unknown as Mesh; + const state = createTiCullState(); + + const result = prepareTiCull(engine, state, mesh, gpu, ti, false, { targetWidth: 800, targetHeight: 600, _camera: makeCamera() }, undefined, lodMesh); + + expect(result?.lodDrawBuffers?.matrixBuffer).toBe(state._lodMatrixBuffer); + expect(result?.lodDrawBuffers?.colorBuffer).toBeNull(); + expect(result?.lodArgsBuffer).toBe(state._lodArgsBuffer); + expect(state._paramsBuffer?.size).toBe(224); + expect(buffers.filter((buffer) => (buffer.descriptor.usage & GPUBufferUsage.INDIRECT) !== 0)).toHaveLength(2); + const lodArgsWrite = writeBuffer.mock.calls.find((call) => call[0] === state._lodArgsBuffer); + expect(lodArgsWrite).toBeDefined(); + expect(Array.from(new Uint32Array(lodArgsWrite![2] as ArrayBuffer, lodArgsWrite![3] as number, 5))).toEqual([12, 0, 0, 0, 0]); + }); +}); + +describe("thin-instance LOD cull binding", () => { + it("reads the current bucket at draw time and falls back after the pairing is cleared", () => { + const source = { thinInstances: makeThinInstances(2) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(2) } as unknown as Mesh; + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const signature = {} as RenderTargetSignature; + const scene = { _meshDisposables: new Map([[partner, []]]) } as unknown as SceneContext; + const renderable = {} as Renderable; + const binding = tryBind(renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)!; + + expect(renderable._direct).toBe(true); + binding.update({ targetWidth: 1, targetHeight: 1 }); + expect(binding.cullDrawBufs).toBeNull(); + + const matrixBuffer = {} as GPUBuffer; + const argsBuffer = {} as GPUBuffer; + publishTiLodBucket(source.thinInstances!, signature, { + drawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + argsBuffer: {} as GPUBuffer, + lodDrawBuffers: { matrixBuffer, colorBuffer: null }, + lodArgsBuffer: argsBuffer, + }); + + expect(binding.cullDrawBufs).toMatchObject({ matrixBuffer, colorBuffer: null }); + const culledPass = { drawIndexedIndirect: vi.fn(), drawIndexed: vi.fn() }; + binding.draw(culledPass as unknown as GPURenderPassEncoder, 36, 2); + expect(culledPass.drawIndexedIndirect).toHaveBeenCalledWith(argsBuffer, 0); + + clearThinInstanceLodPartner(source); + const fallbackPass = { drawIndexedIndirect: vi.fn(), drawIndexed: vi.fn() }; + binding.draw(fallbackPass as unknown as GPURenderPassEncoder, 36, 2); + expect(fallbackPass.drawIndexed).toHaveBeenCalledWith(36, 2); + }); + + it("rejects transparent partners and missing compacted color data", () => { + const source = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + source.thinInstances!.colors = new Float32Array(4); + partner.thinInstances!.colors = new Float32Array(4); + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const signature = {} as RenderTargetSignature; + const scene = { _meshDisposables: new Map([[partner, []]]) } as unknown as SceneContext; + + expect(() => tryBind({} as Renderable, scene, partner, {} as EngineContext, false, true, undefined, signature)).toThrow("opaque"); + + const binding = tryBind({} as Renderable, scene, partner, {} as EngineContext, true, false, undefined, signature)!; + publishTiLodBucket(source.thinInstances!, signature, { + drawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + argsBuffer: {} as GPUBuffer, + lodDrawBuffers: { matrixBuffer: {} as GPUBuffer, colorBuffer: null }, + lodArgsBuffer: {} as GPUBuffer, + }); + expect(() => binding.cullDrawBufs).toThrow("provide instance colors"); + }); + + it("reuses source cull-state disposal and does not add partner cleanup callbacks on rebind", () => { + const source = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + const partner = { thinInstances: makeThinInstances(1) } as unknown as Mesh; + source._gpu = { indexCount: 3 } as MeshGPU; + setThinInstanceLodPartner(source, partner, { distance: 10 }); + const sourceDisposers: Array<() => void> = []; + const partnerDisposers: Array<() => void> = []; + const scene = { + _meshDisposables: new Map([ + [source, sourceDisposers], + [partner, partnerDisposers], + ]), + } as unknown as SceneContext; + const signature = {} as RenderTargetSignature; + const sourceRenderable = {} as Renderable; + + expect(tryBind(sourceRenderable, scene, source, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(tryBind(sourceRenderable, scene, source, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(sourceDisposers).toHaveLength(1); + + expect(tryBind({} as Renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(tryBind({} as Renderable, scene, partner, {} as EngineContext, false, false, undefined, signature)).toBeDefined(); + expect(partnerDisposers).toHaveLength(0); + }); }); diff --git a/tests/lite/unit/thin-instance-lod.test.ts b/tests/lite/unit/thin-instance-lod.test.ts new file mode 100644 index 0000000000..a3308f4dee --- /dev/null +++ b/tests/lite/unit/thin-instance-lod.test.ts @@ -0,0 +1,148 @@ +import { describe, expect, it } from "vitest"; + +import type { Mesh } from "../../../packages/babylon-lite/src/mesh/mesh"; +import { disposeMeshGpu } from "../../../packages/babylon-lite/src/mesh/mesh-dispose"; +import { _detachThinInstanceLodMesh, clearThinInstanceLodPartner, setThinInstanceLodPartner, type ThinInstanceData } from "../../../packages/babylon-lite/src/mesh/thin-instance"; + +function makeThinInstances(withColors = false): ThinInstanceData { + return { + matrices: new Float32Array(16), + count: 1, + _capacity: 1, + _version: 1, + _gpuBuffer: null, + _gpuBufferStorage: false, + _gpuVersion: 0, + _dirtyMin: 0, + _dirtyMax: 1, + colors: withColors ? new Float32Array([1, 1, 1, 1]) : null, + _colorVersion: 0, + _colorDirtyMin: 0, + _colorDirtyMax: 0, + _colorGpuBuffer: null, + _colorGpuBufferStorage: false, + _colorGpuVersion: 0, + _gpuCullingEnabled: false, + }; +} + +function makeMesh(withColors = false): Mesh { + return { thinInstances: makeThinInstances(withColors) } as unknown as Mesh; +} + +function addDisposableGpu(mesh: Mesh): void { + const buffer = () => ({ destroy() {} }) as unknown as GPUBuffer; + mesh._gpu = { + positionBuffer: buffer(), + normalBuffer: buffer(), + uvBuffer: buffer(), + indexBuffer: buffer(), + indexCount: 3, + indexFormat: "uint16", + }; +} + +describe("thin-instance LOD pairing", () => { + it("pairs, updates, and clears a partner while restoring auto-enabled culling", () => { + const full = makeMesh(true); + const lod = makeMesh(true); + + setThinInstanceLodPartner(full, lod, { distance: 20, band: 4 }); + + expect(full.thinInstances!._lodPartner).toBe(lod); + expect(full.thinInstances!._lodDistance).toBe(20); + expect(full.thinInstances!._lodBand).toBe(4); + expect(lod.thinInstances!._lodSource).toBe(full); + expect(lod.thinInstances!._lodBuckets).toBe(full.thinInstances!._lodBuckets); + expect(full._clone).toContain("LOD-paired"); + expect(lod._clone).toBe(full._clone); + expect(lod.thinInstances!._lodAutoCull).toBe(true); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(true); + + setThinInstanceLodPartner(full, lod, { distance: 30 }); + expect(full.thinInstances!._lodDistance).toBe(30); + expect(full.thinInstances!._lodBand).toBe(0); + + clearThinInstanceLodPartner(full); + + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(full.thinInstances!._lodDistance).toBeUndefined(); + expect(full.thinInstances!._lodBand).toBeUndefined(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + expect(lod.thinInstances!._lodAutoCull).toBe(false); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); + + it("cleans the previous partner when the source is re-paired", () => { + const full = makeMesh(); + const first = makeMesh(); + const second = makeMesh(); + + setThinInstanceLodPartner(full, first, { distance: 10 }); + setThinInstanceLodPartner(full, second, { distance: 20 }); + + expect(first.thinInstances!._lodSource).toBeNull(); + expect(first._clone).toBeUndefined(); + expect(first.thinInstances!._gpuCullingEnabled).toBe(false); + expect(full.thinInstances!._lodPartner).toBe(second); + expect(second.thinInstances!._lodSource).toBe(full); + }); + + it("rejects invalid options, role conflicts, reused partners, and missing source colors", () => { + const full = makeMesh(); + const lod = makeMesh(); + + expect(() => setThinInstanceLodPartner(full, lod, { distance: -1 })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, lod, { distance: Number.NaN })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, lod, { distance: 1, band: -1 })).toThrow(RangeError); + expect(() => setThinInstanceLodPartner(full, full, { distance: 1 })).toThrow("two distinct meshes"); + expect(() => setThinInstanceLodPartner(full, makeMesh(true), { distance: 1 })).toThrow("source instance colors"); + full.thinInstances!._refCount = 2; + expect(() => setThinInstanceLodPartner(full, lod, { distance: 1 })).toThrow("shared by mesh clones"); + full.thinInstances!._refCount = 1; + + setThinInstanceLodPartner(full, lod, { distance: 1 }); + expect(() => setThinInstanceLodPartner(makeMesh(), lod, { distance: 1 })).toThrow("already paired"); + expect(() => setThinInstanceLodPartner(lod, makeMesh(), { distance: 1 })).toThrow("cannot already be an LOD partner"); + + const lodOwner = makeMesh(); + const chained = makeMesh(); + setThinInstanceLodPartner(lodOwner, chained, { distance: 1 }); + expect(() => setThinInstanceLodPartner(makeMesh(), lodOwner, { distance: 1 })).toThrow("cannot also own an LOD partner"); + }); + + it("detaches both sides when either mesh is disposed", () => { + const full = makeMesh(); + const lod = makeMesh(); + setThinInstanceLodPartner(full, lod, { distance: 10 }); + + _detachThinInstanceLodMesh(lod); + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + + setThinInstanceLodPartner(full, lod, { distance: 10 }); + _detachThinInstanceLodMesh(full); + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(full._clone).toBeUndefined(); + expect(lod._clone).toBeUndefined(); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); + + it("detaches the pairing when the last thin-instance owner is disposed", () => { + const full = makeMesh(); + const lod = makeMesh(); + addDisposableGpu(full); + setThinInstanceLodPartner(full, lod, { distance: 10 }); + + disposeMeshGpu(full); + + expect(full.thinInstances!._lodPartner).toBeNull(); + expect(lod.thinInstances!._lodSource).toBeNull(); + expect(lod.thinInstances!._gpuCullingEnabled).toBe(false); + }); +}); diff --git a/tests/lite/unit/validate-pr-release-markers.test.ts b/tests/lite/unit/validate-pr-release-markers.test.ts new file mode 100644 index 0000000000..9adbae8cf2 --- /dev/null +++ b/tests/lite/unit/validate-pr-release-markers.test.ts @@ -0,0 +1,63 @@ +import { spawnSync } from "child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "fs"; +import { tmpdir } from "os"; +import { join, resolve } from "path"; +import { pathToFileURL } from "url"; + +import { describe, expect, it } from "vitest"; + +describe("release marker PR metadata fetch", () => { + it("retries a transient GitHub server error before validating the PR", () => { + const tempDir = mkdtempSync(join(tmpdir(), "release-marker-retry-")); + const preloadPath = join(tempDir, "mock-github-fetch.mjs"); + writeFileSync( + preloadPath, + ` +import { createRequire, syncBuiltinESMExports } from "node:module"; +const require = createRequire(import.meta.url); +require("node:child_process").execFileSync = () => "feat: test"; +syncBuiltinESMExports(); +let attempts = 0; +globalThis.fetch = async () => { + attempts++; + console.log("MOCK_GITHUB_ATTEMPT=" + attempts); + if (attempts === 1) { + return new Response( + new ReadableStream({ + cancel() { + console.log("MOCK_GITHUB_BODY_CANCELLED"); + }, + }), + { status: 503, statusText: "Service Unavailable" } + ); + } + return new Response(JSON.stringify({ title: "feat: test", body: "", labels: [] }), { + status: 200, + headers: { "content-type": "application/json" }, + }); +}; +` + ); + + try { + const result = spawnSync(process.execPath, ["--import", "tsx", "--import", pathToFileURL(preloadPath).href, resolve("scripts/validate-pr-release-markers.ts")], { + cwd: resolve("."), + encoding: "utf8", + env: { + ...process.env, + GITHUB_REPOSITORY: "BabylonJS/Babylon-Lite", + PR_NUMBER: "421", + GITHUB_TOKEN: "test-token", + }, + }); + + expect(result.status, result.stderr).toBe(0); + expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=1"); + expect(result.stdout).toContain("MOCK_GITHUB_ATTEMPT=2"); + expect(result.stdout).toContain("MOCK_GITHUB_BODY_CANCELLED"); + expect(result.stdout).toContain("No breaking-change marker detected"); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); +});