@babylonjs/lite 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/index.js +444 -368
- package/dist/index.js.map +1 -1
- package/index.d.ts +208 -6
- package/lib/asset-container.js.map +1 -1
- package/lib/audio/index.js +1 -0
- package/lib/audio/index.js.map +1 -1
- package/lib/audio/media-stream-output.js +44 -0
- package/lib/audio/media-stream-output.js.map +1 -0
- package/lib/engine/engine.js +1 -1
- package/lib/frame-graph/render-task.js +1 -1
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/frame-graph/shadow-inputs.js +10 -1
- package/lib/frame-graph/shadow-inputs.js.map +1 -1
- package/lib/frame-graph/transmission.js +1 -16
- package/lib/frame-graph/transmission.js.map +1 -1
- package/lib/index.js +6 -3
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-json-asset.js +1 -1
- package/lib/loader-gltf/gltf-json-asset.js.map +1 -1
- package/lib/loader-gltf/gltf-multi-buffer.js +2 -10
- package/lib/loader-gltf/gltf-multi-buffer.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder-ext.js +4 -18
- package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
- package/lib/loader-gltf/gltf-pbr-builder.js +17 -14
- package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
- package/lib/loader-gltf/gltf-sampler-desc.js +3 -16
- package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
- package/lib/material/node/blocks/ambient-occlusion-block.js +1 -4
- package/lib/material/node/blocks/ambient-occlusion-block.js.map +1 -1
- package/lib/material/node/blocks/biplanar-block.js +3 -6
- package/lib/material/node/blocks/biplanar-block.js.map +1 -1
- package/lib/material/node/blocks/image-source.js +2 -5
- package/lib/material/node/blocks/image-source.js.map +1 -1
- package/lib/material/node/blocks/input-block.js +5 -26
- package/lib/material/node/blocks/input-block.js.map +1 -1
- package/lib/material/node/blocks/pbr-mr-helper-full.js +5 -8
- package/lib/material/node/blocks/pbr-mr-helper-full.js.map +1 -1
- package/lib/material/node/blocks/reflection-texture-block.js +1 -4
- package/lib/material/node/blocks/reflection-texture-block.js.map +1 -1
- package/lib/material/node/blocks/texture-block.js +2 -5
- package/lib/material/node/blocks/texture-block.js.map +1 -1
- package/lib/material/node/blocks/triplanar-block.js +3 -6
- package/lib/material/node/blocks/triplanar-block.js.map +1 -1
- package/lib/material/node/node-emitter.js +24 -1
- package/lib/material/node/node-emitter.js.map +1 -1
- package/lib/material/node/node-geometry-renderable.js +2 -2
- package/lib/material/node/node-material.js +1 -22
- package/lib/material/node/node-material.js.map +1 -1
- package/lib/material/node/node-types.js.map +1 -1
- package/lib/material/pbr/fragments/shadow-only-fragment.js +68 -0
- package/lib/material/pbr/fragments/shadow-only-fragment.js.map +1 -0
- package/lib/material/pbr/pbr-flag-bits.js.map +1 -1
- package/lib/material/pbr/pbr-material.js.map +1 -1
- package/lib/material/pbr/pbr-primitive-resolver.js +2 -2
- package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -1
- package/lib/material/pbr/pbr-renderable.js +3 -0
- package/lib/material/pbr/pbr-renderable.js.map +1 -1
- package/lib/material/pbr/pbr-template.js +1 -0
- package/lib/material/pbr/pbr-template.js.map +1 -1
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js +2 -8
- package/lib/mesh/GaussianSplatting/gaussian-splatting-mesh.js.map +1 -1
- package/lib/mesh/get-mesh-geometry.js +24 -0
- package/lib/mesh/get-mesh-geometry.js.map +1 -0
- package/lib/mesh/mesh-dispose.js +2 -0
- package/lib/mesh/mesh-dispose.js.map +1 -1
- package/lib/mesh/mesh.js +2 -8
- package/lib/mesh/mesh.js.map +1 -1
- package/lib/mesh/thin-instance-cull-binding.js +54 -3
- package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
- package/lib/mesh/thin-instance-gpu-culling.js +241 -24
- package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
- package/lib/mesh/thin-instance.js +112 -1
- package/lib/mesh/thin-instance.js.map +1 -1
- package/lib/picking/gpu-picker.js +4 -2
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/picking/picking-pipeline.js +1 -1
- package/lib/picking/picking-pipeline.js.map +1 -1
- package/lib/picking/picking-shader.js +38 -16
- package/lib/picking/picking-shader.js.map +1 -1
- package/lib/scene/scene-core.js +7 -2
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/scene/scene-node.js +2 -6
- package/lib/scene/scene-node.js.map +1 -1
- package/lib/scene/scene-remove.js +70 -1
- package/lib/scene/scene-remove.js.map +1 -1
- package/lib/scene/transform-node.js +3 -0
- package/lib/scene/transform-node.js.map +1 -1
- package/lib/scene/world-matrix-state.js +7 -1
- package/lib/scene/world-matrix-state.js.map +1 -1
- package/lib/shadow/csm-directional-shadow-generator.js +22 -1
- package/lib/shadow/csm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/csm-shadow-task-hooks.js +19 -11
- package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
- package/lib/shadow/esm-directional-shadow-generator.js +2 -51
- package/lib/shadow/esm-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/pcf-directional-shadow-generator.js +2 -48
- package/lib/shadow/pcf-directional-shadow-generator.js.map +1 -1
- package/lib/shadow/shadow-base.js +48 -1
- package/lib/shadow/shadow-base.js.map +1 -1
- package/lib/text/text-renderable.js +2 -10
- package/lib/text/text-renderable.js.map +1 -1
- package/lib/texture/texture-array.js +96 -0
- package/lib/texture/texture-array.js.map +1 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-node.js","sources":["../../../src/scene/transform-node.ts"],"sourcesContent":["/** TransformNode — alias for SceneNode. A scene graph node with TRS, parent, and children.\n *\n * TransformNode is now a pure type alias for SceneNode, giving all scene entities\n * a common base. createTransformNode delegates to createSceneNode. */\n\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { initMeshTransform } from \"../mesh/mesh.js\";\nimport { retain } from \"../resource/ref-count.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport { createSceneNode, createSceneNodeFromMatrix } from \"./scene-node.js\";\n\nexport type { SceneNode } from \"./scene-node.js\";\n\n/** TransformNode is a SceneNode — pure type alias, no extra fields. */\nexport type TransformNode = SceneNode;\n\n/** Create a TransformNode (SceneNode) with TRS values and lazy world matrix.\n * Parameters: name, position (px,py,pz), rotation quaternion (qx,qy,qz,qw), scaling (sx,sy,sz). */\nexport function createTransformNode(name: string, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): TransformNode {\n return createSceneNode(name, px, py, pz, qx, qy, qz, qw, sx, sy, sz);\n}\n\n/** Deep-clone a SceneNode tree. Meshes are shallow-cloned (shared GPU buffers — see\n * `cloneMeshNode` and `resource/ref-count.ts` for how disposal safely handles the sharing).\n * Lights, cameras, and other non-mesh/non-TN children are shallow-cloned. */\nexport function cloneTransformNode(src: SceneNode): SceneNode {\n if (\"_gpu\" in src) {\n return cloneMeshNode(src as unknown as Mesh);\n }\n\n const clone = src._localMatrix\n ? createSceneNodeFromMatrix(src.name + \"_clone\", src._localMatrix)\n : createTransformNode(\n src.name + \"_clone\",\n src.position.x,\n src.position.y,\n src.position.z,\n src.rotationQuaternion.x,\n src.rotationQuaternion.y,\n src.rotationQuaternion.z,\n src.rotationQuaternion.w,\n src.scaling.x,\n src.scaling.y,\n src.scaling.z\n );\n for (const child of src.children) {\n if (!(\"lightType\" in child)) {\n const childClone = cloneTransformNode(child);\n childClone.parent = clone;\n clone.children.push(childClone);\n } else {\n // Lights, cameras, other node types — shallow clone with fresh children array\n const childClone = { ...(child as Record<string, unknown>), name: (child as SceneNode).name + \"_clone\", children: [] } as unknown as SceneNode;\n childClone.parent = clone;\n clone.children.push(childClone);\n }\n }\n return clone;\n}\n\nfunction cloneMeshNode(mesh: Mesh): Mesh {\n const meshClone = {\n ...mesh,\n name: mesh.name + \"_clone\",\n children: [],\n // Share the SAME `_gpu` object (not a copy) — the wrapper's identity is the ref-count\n // key in resource/ref-count.ts, so both meshes must point at the exact same instance\n // for `disposeMeshGpu` to know they're co-owners of the underlying GPUBuffers.\n _gpu: mesh._gpu,\n } as unknown as Mesh;\n // `skeleton`/`vat`/`morphTargets`/`thinInstances` are already shared by reference via the `...mesh`\n // spread above. Register the extra ownership for all shared GPU resources so `disposeMeshGpu`\n // only destroys their buffers once the LAST owning mesh (source or clone) releases them —\n // otherwise disposing one mesh would free buffers the other still renders with.\n for (const r of [mesh._gpu, mesh.skeleton, mesh.vat, mesh.morphTargets, mesh.thinInstances]) {\n if (r) {\n retain(r);\n }\n }\n initMeshTransform(meshClone, mesh.position.x, mesh.position.y, mesh.position.z, 0, 0, 0, mesh.scaling.x, mesh.scaling.y, mesh.scaling.z);\n // Copy the source rotation as a QUATERNION — the Euler round-trip (mesh.rotation.x/y/z) is lossy\n // near gimbal lock and would skew the clone. set() marks the world matrix dirty so it recomputes.\n const rq = mesh.rotationQuaternion;\n meshClone.rotationQuaternion.set(rq.x, rq.y, rq.z, rq.w);\n for (const child of mesh.children) {\n const childClone = cloneTransformNode(child);\n childClone.parent = meshClone;\n meshClone.children.push(childClone);\n }\n return meshClone;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transform-node.js","sources":["../../../src/scene/transform-node.ts"],"sourcesContent":["/** TransformNode — alias for SceneNode. A scene graph node with TRS, parent, and children.\n *\n * TransformNode is now a pure type alias for SceneNode, giving all scene entities\n * a common base. createTransformNode delegates to createSceneNode. */\n\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { initMeshTransform } from \"../mesh/mesh.js\";\nimport { retain } from \"../resource/ref-count.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport { createSceneNode, createSceneNodeFromMatrix } from \"./scene-node.js\";\n\nexport type { SceneNode } from \"./scene-node.js\";\n\n/** TransformNode is a SceneNode — pure type alias, no extra fields. */\nexport type TransformNode = SceneNode;\n\n/** Create a TransformNode (SceneNode) with TRS values and lazy world matrix.\n * Parameters: name, position (px,py,pz), rotation quaternion (qx,qy,qz,qw), scaling (sx,sy,sz). */\nexport function createTransformNode(name: string, px = 0, py = 0, pz = 0, qx = 0, qy = 0, qz = 0, qw = 1, sx = 1, sy = 1, sz = 1): TransformNode {\n return createSceneNode(name, px, py, pz, qx, qy, qz, qw, sx, sy, sz);\n}\n\n/** Deep-clone a SceneNode tree. Meshes are shallow-cloned (shared GPU buffers — see\n * `cloneMeshNode` and `resource/ref-count.ts` for how disposal safely handles the sharing).\n * Lights, cameras, and other non-mesh/non-TN children are shallow-cloned. */\nexport function cloneTransformNode(src: SceneNode): SceneNode {\n if (\"_gpu\" in src) {\n return cloneMeshNode(src as unknown as Mesh);\n }\n\n const clone = src._localMatrix\n ? createSceneNodeFromMatrix(src.name + \"_clone\", src._localMatrix)\n : createTransformNode(\n src.name + \"_clone\",\n src.position.x,\n src.position.y,\n src.position.z,\n src.rotationQuaternion.x,\n src.rotationQuaternion.y,\n src.rotationQuaternion.z,\n src.rotationQuaternion.w,\n src.scaling.x,\n src.scaling.y,\n src.scaling.z\n );\n for (const child of src.children) {\n if (!(\"lightType\" in child)) {\n const childClone = cloneTransformNode(child);\n childClone.parent = clone;\n clone.children.push(childClone);\n } else {\n // Lights, cameras, other node types — shallow clone with fresh children array\n const childClone = { ...(child as Record<string, unknown>), name: (child as SceneNode).name + \"_clone\", children: [] } as unknown as SceneNode;\n childClone.parent = clone;\n clone.children.push(childClone);\n }\n }\n return clone;\n}\n\nfunction cloneMeshNode(mesh: Mesh): Mesh {\n if (mesh._clone) {\n throw Error(mesh._clone);\n }\n const meshClone = {\n ...mesh,\n name: mesh.name + \"_clone\",\n children: [],\n // Share the SAME `_gpu` object (not a copy) — the wrapper's identity is the ref-count\n // key in resource/ref-count.ts, so both meshes must point at the exact same instance\n // for `disposeMeshGpu` to know they're co-owners of the underlying GPUBuffers.\n _gpu: mesh._gpu,\n } as unknown as Mesh;\n // `skeleton`/`vat`/`morphTargets`/`thinInstances` are already shared by reference via the `...mesh`\n // spread above. Register the extra ownership for all shared GPU resources so `disposeMeshGpu`\n // only destroys their buffers once the LAST owning mesh (source or clone) releases them —\n // otherwise disposing one mesh would free buffers the other still renders with.\n for (const r of [mesh._gpu, mesh.skeleton, mesh.vat, mesh.morphTargets, mesh.thinInstances]) {\n if (r) {\n retain(r);\n }\n }\n initMeshTransform(meshClone, mesh.position.x, mesh.position.y, mesh.position.z, 0, 0, 0, mesh.scaling.x, mesh.scaling.y, mesh.scaling.z);\n // Copy the source rotation as a QUATERNION — the Euler round-trip (mesh.rotation.x/y/z) is lossy\n // near gimbal lock and would skew the clone. set() marks the world matrix dirty so it recomputes.\n const rq = mesh.rotationQuaternion;\n meshClone.rotationQuaternion.set(rq.x, rq.y, rq.z, rq.w);\n for (const child of mesh.children) {\n const childClone = cloneTransformNode(child);\n childClone.parent = meshClone;\n meshClone.children.push(childClone);\n }\n return meshClone;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { mat4MultiplyInto } from '../math/mat4-multiply-into.js';
|
|
2
2
|
import { allocateMat4 } from '../math/_matrix-allocator.js';
|
|
3
|
+
import { mat4Compose } from '../math/mat4-compose.js';
|
|
4
|
+
import { mat4Identity } from '../math/mat4-identity.js';
|
|
3
5
|
|
|
4
6
|
const WM_STATE = Symbol("wmState");
|
|
5
7
|
function attachWorldMatrixState(host, state) {
|
|
@@ -15,6 +17,10 @@ function peekWorldMatrixState(p) {
|
|
|
15
17
|
function _markWorldMatrixDirty(host) {
|
|
16
18
|
peekWorldMatrixState(host)?._invalidate();
|
|
17
19
|
}
|
|
20
|
+
function composeTrsLocalMatrix(position, rotation, scaling) {
|
|
21
|
+
const isIdentity = position.x === 0 && position.y === 0 && position.z === 0 && rotation.x === 0 && rotation.y === 0 && rotation.z === 0 && rotation.w === 1 && scaling.x === 1 && scaling.y === 1 && scaling.z === 1;
|
|
22
|
+
return isIdentity ? mat4Identity() : mat4Compose(position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, scaling.x, scaling.y, scaling.z);
|
|
23
|
+
}
|
|
18
24
|
function createWorldMatrixState(getLocalMatrix) {
|
|
19
25
|
let _worldVersion = 0;
|
|
20
26
|
let _lastSeenParentVersion = -1;
|
|
@@ -98,5 +104,5 @@ function createWorldMatrixState(getLocalMatrix) {
|
|
|
98
104
|
return state;
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
export { _markWorldMatrixDirty, attachWorldMatrixState, createWorldMatrixState };
|
|
107
|
+
export { _markWorldMatrixDirty, attachWorldMatrixState, composeTrsLocalMatrix, createWorldMatrixState };
|
|
102
108
|
//# sourceMappingURL=world-matrix-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"world-matrix-state.js","sources":["../../../src/scene/world-matrix-state.ts"],"sourcesContent":["/** Shared version-based lazy world matrix computation.\n *\n * Each entity provides only getLocalMatrix(). This module handles:\n * - version tracking (_worldVersion bumped by own TRS changes and ancestor motion)\n * - caching and staleness detection (_cachedWorld nulled on any change)\n * - PUSH dirty propagation: a transform write invalidates the node AND all of its\n * descendants up front, so per-frame consumers can read worldMatrixVersion in\n * O(1) (a plain field read) instead of walking the parent chain.\n *\n * Ancestor-only motion (e.g. animating a parent transform node) must bump a\n * descendant's worldMatrixVersion so per-frame consumers re-upload its UBO.\n * Rather than have each node PULL up its parent chain on every read, this module\n * keeps its own child registry (driven by the reliably-called `parent` setter, NOT\n * the host's `children` array which loaders/setParent maintain inconsistently) and\n * PUSHES invalidation down on every local change or reparent. Reads are O(1) (a\n * plain field read); writers pay an O(subtree) push. This is a clear win for the\n * common case (few movers, many readers) and never worse than the old pull walk.\n *\n * Foreign parents — an IWorldMatrixProvider that is not tagged with our state\n * symbol (e.g. a user-supplied object) — cannot be pushed to because we never see\n * their writes. For those we keep a cheap PULL fallback that polls the parent's\n * public version on read. All in-engine hosts (mesh, scene node, camera, light,\n * Gaussian-splatting mesh) ARE tagged, so engine hierarchies are pure push.\n *\n * Backing storage comes from `allocateMat4()` — process-global lazy singleton\n * in `_matrix-allocator.ts`. F32 by default; F64 after an HPM engine is\n * constructed (see `docs/lite/architecture/36-high-precision-matrix.md`). */\n\nimport type { Mat4 } from \"../math/types.js\";\nimport type { IWorldMatrixProvider } from \"./parentable.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { allocateMat4 } from \"../math/_matrix-allocator.js\";\n\nexport interface WorldMatrixAccessors {\n /** Getter — returns lazily computed world matrix. */\n getWorldMatrix(): Mat4;\n /** Getter — returns current version. */\n getWorldMatrixVersion(): number;\n /** Call when own TRS changes. Invalidates cache + subtree, forces recompute. */\n markLocalDirty(): void;\n /** Reference to parent — set directly. */\n parent: IWorldMatrixProvider | null;\n /** @internal Push: mark this node and its whole subtree dirty (clean-guarded). */\n _invalidate(): void;\n /** @internal Register a same-module child so future invalidations reach it. */\n _addChild(child: WorldMatrixAccessors): void;\n /** @internal Deregister a child (reparent away). */\n _removeChild(child: WorldMatrixAccessors): void;\n}\n\nconst WM_STATE = Symbol(\"wmState\");\n\n/** Tag a host object (mesh, scene node, camera, light, …) with its world-matrix\n * state so children can register for push invalidation and call its accessor\n * closures directly, bypassing the host's property getters. */\nexport function attachWorldMatrixState(host: object, state: WorldMatrixAccessors): void {\n (host as unknown as Record<symbol, unknown>)[WM_STATE] = state;\n}\n\nfunction peekWorldMatrixState(p: IWorldMatrixProvider | null): WorldMatrixAccessors | null {\n if (p === null) {\n return null;\n }\n const s = (p as unknown as Record<symbol, unknown>)[WM_STATE];\n return (s as WorldMatrixAccessors | undefined) ?? null;\n}\n\n/** @internal Bump one engine object's world version without changing its transform values. */\nexport function _markWorldMatrixDirty(host: IWorldMatrixProvider): void {\n peekWorldMatrixState(host)?._invalidate();\n}\n\n/**\n * Create world matrix state for any entity type.\n *\n * @param getLocalMatrix - Entity-specific function that returns the local (pre-parent)\n * transform matrix. Called only when the cache is stale.\n */\nexport function createWorldMatrixState(getLocalMatrix: () => Mat4): WorldMatrixAccessors {\n let _worldVersion = 0;\n let _lastSeenParentVersion = -1;\n let _cachedWorld: Mat4 | null = null;\n const _ownedWorld: Mat4 = allocateMat4();\n let _parent: IWorldMatrixProvider | null = null;\n let _parentState: WorldMatrixAccessors | null = null;\n const _children: WorldMatrixAccessors[] = [];\n\n // Mark this node — and, transitively, its whole subtree — dirty, bumping the\n // version so per-frame consumers (which gate UBO uploads on worldMatrixVersion)\n // re-upload. Propagation is eager and unconditional: consumers may never read a\n // pure transform node's world matrix, so a \"skip if already dirty\" guard would\n // strand its descendants on a stale version when an ancestor moves every frame.\n // Caching is still lazy — getWorldMatrix recomputes only when _cachedWorld is\n // null — so reads stay cheap; only writers pay the O(subtree) push.\n function invalidate(): void {\n _cachedWorld = null;\n _worldVersion++;\n for (const child of _children) {\n child._invalidate();\n }\n }\n\n // Foreign parent (not tagged with our symbol): we never observe its writes, so\n // poll its public version on read and push a subtree invalidation when it moved.\n function pollForeignParent(): void {\n const pv = (_parent as IWorldMatrixProvider).worldMatrixVersion;\n if (pv !== _lastSeenParentVersion) {\n _lastSeenParentVersion = pv;\n invalidate();\n }\n }\n\n const state: WorldMatrixAccessors = {\n get parent(): IWorldMatrixProvider | null {\n return _parent;\n },\n set parent(p: IWorldMatrixProvider | null) {\n if (p === _parent) {\n return;\n }\n if (_parentState !== null) {\n _parentState._removeChild(state);\n }\n _parent = p;\n _parentState = peekWorldMatrixState(p);\n if (_parentState !== null) {\n _parentState._addChild(state);\n }\n _lastSeenParentVersion = -1;\n // Reparenting changes our world transform → dirty us and the subtree.\n invalidate();\n },\n\n markLocalDirty(): void {\n invalidate();\n },\n\n getWorldMatrix(): Mat4 {\n if (_parentState === null && _parent !== null) {\n pollForeignParent();\n }\n if (_cachedWorld !== null) {\n return _cachedWorld;\n }\n const local = getLocalMatrix();\n if (_parent !== null) {\n const pw = _parent.worldMatrix;\n mat4MultiplyInto(_ownedWorld as unknown as Mat4Storage, 0, pw as unknown as Mat4Storage, 0, local as unknown as Mat4Storage, 0);\n _cachedWorld = _ownedWorld;\n } else {\n _cachedWorld = local;\n }\n return _cachedWorld;\n },\n\n getWorldMatrixVersion(): number {\n if (_parentState === null && _parent !== null) {\n pollForeignParent();\n }\n return _worldVersion;\n },\n\n _invalidate(): void {\n invalidate();\n },\n\n _addChild(child: WorldMatrixAccessors): void {\n _children.push(child);\n },\n\n _removeChild(child: WorldMatrixAccessors): void {\n const i = _children.indexOf(child);\n if (i >= 0) {\n _children.splice(i, 1);\n }\n },\n };\n\n return state;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"world-matrix-state.js","sources":["../../../src/scene/world-matrix-state.ts"],"sourcesContent":["/** Shared version-based lazy world matrix computation.\n *\n * Each entity provides only getLocalMatrix(). This module handles:\n * - version tracking (_worldVersion bumped by own TRS changes and ancestor motion)\n * - caching and staleness detection (_cachedWorld nulled on any change)\n * - PUSH dirty propagation: a transform write invalidates the node AND all of its\n * descendants up front, so per-frame consumers can read worldMatrixVersion in\n * O(1) (a plain field read) instead of walking the parent chain.\n *\n * Ancestor-only motion (e.g. animating a parent transform node) must bump a\n * descendant's worldMatrixVersion so per-frame consumers re-upload its UBO.\n * Rather than have each node PULL up its parent chain on every read, this module\n * keeps its own child registry (driven by the reliably-called `parent` setter, NOT\n * the host's `children` array which loaders/setParent maintain inconsistently) and\n * PUSHES invalidation down on every local change or reparent. Reads are O(1) (a\n * plain field read); writers pay an O(subtree) push. This is a clear win for the\n * common case (few movers, many readers) and never worse than the old pull walk.\n *\n * Foreign parents — an IWorldMatrixProvider that is not tagged with our state\n * symbol (e.g. a user-supplied object) — cannot be pushed to because we never see\n * their writes. For those we keep a cheap PULL fallback that polls the parent's\n * public version on read. All in-engine hosts (mesh, scene node, camera, light,\n * Gaussian-splatting mesh) ARE tagged, so engine hierarchies are pure push.\n *\n * Backing storage comes from `allocateMat4()` — process-global lazy singleton\n * in `_matrix-allocator.ts`. F32 by default; F64 after an HPM engine is\n * constructed (see `docs/lite/architecture/36-high-precision-matrix.md`). */\n\nimport type { Mat4, Quat, Vec3 } from \"../math/types.js\";\nimport type { IWorldMatrixProvider } from \"./parentable.js\";\nimport { mat4MultiplyInto } from \"../math/mat4-multiply-into.js\";\nimport type { Mat4Storage } from \"../math/types.js\";\nimport { allocateMat4 } from \"../math/_matrix-allocator.js\";\nimport { mat4Compose } from \"../math/mat4-compose.js\";\nimport { mat4Identity } from \"../math/mat4-identity.js\";\n\nexport interface WorldMatrixAccessors {\n /** Getter — returns lazily computed world matrix. */\n getWorldMatrix(): Mat4;\n /** Getter — returns current version. */\n getWorldMatrixVersion(): number;\n /** Call when own TRS changes. Invalidates cache + subtree, forces recompute. */\n markLocalDirty(): void;\n /** Reference to parent — set directly. */\n parent: IWorldMatrixProvider | null;\n /** @internal Push: mark this node and its whole subtree dirty (clean-guarded). */\n _invalidate(): void;\n /** @internal Register a same-module child so future invalidations reach it. */\n _addChild(child: WorldMatrixAccessors): void;\n /** @internal Deregister a child (reparent away). */\n _removeChild(child: WorldMatrixAccessors): void;\n}\n\nconst WM_STATE = Symbol(\"wmState\");\n\n/** Tag a host object (mesh, scene node, camera, light, …) with its world-matrix\n * state so children can register for push invalidation and call its accessor\n * closures directly, bypassing the host's property getters. */\nexport function attachWorldMatrixState(host: object, state: WorldMatrixAccessors): void {\n (host as unknown as Record<symbol, unknown>)[WM_STATE] = state;\n}\n\nfunction peekWorldMatrixState(p: IWorldMatrixProvider | null): WorldMatrixAccessors | null {\n if (p === null) {\n return null;\n }\n const s = (p as unknown as Record<symbol, unknown>)[WM_STATE];\n return (s as WorldMatrixAccessors | undefined) ?? null;\n}\n\n/** @internal Bump one engine object's world version without changing its transform values. */\nexport function _markWorldMatrixDirty(host: IWorldMatrixProvider): void {\n peekWorldMatrixState(host)?._invalidate();\n}\n\n/** Compose a local TRS matrix, skipping the general composition path for the default transform. */\nexport function composeTrsLocalMatrix(position: Vec3, rotation: Quat, scaling: Vec3): Mat4 {\n const isIdentity =\n position.x === 0 &&\n position.y === 0 &&\n position.z === 0 &&\n rotation.x === 0 &&\n rotation.y === 0 &&\n rotation.z === 0 &&\n rotation.w === 1 &&\n scaling.x === 1 &&\n scaling.y === 1 &&\n scaling.z === 1;\n return isIdentity ? mat4Identity() : mat4Compose(position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, scaling.x, scaling.y, scaling.z);\n}\n\n/**\n * Create world matrix state for any entity type.\n *\n * @param getLocalMatrix - Entity-specific function that returns the local (pre-parent)\n * transform matrix. Called only when the cache is stale.\n */\nexport function createWorldMatrixState(getLocalMatrix: () => Mat4): WorldMatrixAccessors {\n let _worldVersion = 0;\n let _lastSeenParentVersion = -1;\n let _cachedWorld: Mat4 | null = null;\n const _ownedWorld: Mat4 = allocateMat4();\n let _parent: IWorldMatrixProvider | null = null;\n let _parentState: WorldMatrixAccessors | null = null;\n const _children: WorldMatrixAccessors[] = [];\n\n // Mark this node — and, transitively, its whole subtree — dirty, bumping the\n // version so per-frame consumers (which gate UBO uploads on worldMatrixVersion)\n // re-upload. Propagation is eager and unconditional: consumers may never read a\n // pure transform node's world matrix, so a \"skip if already dirty\" guard would\n // strand its descendants on a stale version when an ancestor moves every frame.\n // Caching is still lazy — getWorldMatrix recomputes only when _cachedWorld is\n // null — so reads stay cheap; only writers pay the O(subtree) push.\n function invalidate(): void {\n _cachedWorld = null;\n _worldVersion++;\n for (const child of _children) {\n child._invalidate();\n }\n }\n\n // Foreign parent (not tagged with our symbol): we never observe its writes, so\n // poll its public version on read and push a subtree invalidation when it moved.\n function pollForeignParent(): void {\n const pv = (_parent as IWorldMatrixProvider).worldMatrixVersion;\n if (pv !== _lastSeenParentVersion) {\n _lastSeenParentVersion = pv;\n invalidate();\n }\n }\n\n const state: WorldMatrixAccessors = {\n get parent(): IWorldMatrixProvider | null {\n return _parent;\n },\n set parent(p: IWorldMatrixProvider | null) {\n if (p === _parent) {\n return;\n }\n if (_parentState !== null) {\n _parentState._removeChild(state);\n }\n _parent = p;\n _parentState = peekWorldMatrixState(p);\n if (_parentState !== null) {\n _parentState._addChild(state);\n }\n _lastSeenParentVersion = -1;\n // Reparenting changes our world transform → dirty us and the subtree.\n invalidate();\n },\n\n markLocalDirty(): void {\n invalidate();\n },\n\n getWorldMatrix(): Mat4 {\n if (_parentState === null && _parent !== null) {\n pollForeignParent();\n }\n if (_cachedWorld !== null) {\n return _cachedWorld;\n }\n const local = getLocalMatrix();\n if (_parent !== null) {\n const pw = _parent.worldMatrix;\n mat4MultiplyInto(_ownedWorld as unknown as Mat4Storage, 0, pw as unknown as Mat4Storage, 0, local as unknown as Mat4Storage, 0);\n _cachedWorld = _ownedWorld;\n } else {\n _cachedWorld = local;\n }\n return _cachedWorld;\n },\n\n getWorldMatrixVersion(): number {\n if (_parentState === null && _parent !== null) {\n pollForeignParent();\n }\n return _worldVersion;\n },\n\n _invalidate(): void {\n invalidate();\n },\n\n _addChild(child: WorldMatrixAccessors): void {\n _children.push(child);\n },\n\n _removeChild(child: WorldMatrixAccessors): void {\n const i = _children.indexOf(child);\n if (i >= 0) {\n _children.splice(i, 1);\n }\n },\n };\n\n return state;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createShadowParamsUBO } from './shadow-base.js';
|
|
2
2
|
import { createUniformBuffer } from '../resource/gpu-buffers.js';
|
|
3
|
+
import { acquireTexture } from '../resource/gpu-pool.js';
|
|
3
4
|
import { preloadCsmShadowTaskState, ensureCsmShadowTaskState, renderCsmShadowMap } from './csm-shadow-task-hooks.js';
|
|
4
5
|
import { setCsmStdReceiverFactory, setCsmPbrReceiverFactory } from './csm-receiver-registry.js';
|
|
5
6
|
import { createStdCsmShadowFragment } from '../material/standard/fragments/std-csm-shadow-fragment.js';
|
|
@@ -61,6 +62,26 @@ function createCsmDirectionalShadowGenerator(engine, _light, cfg = {}) {
|
|
|
61
62
|
sg._renderShadowMap = (eng, state) => renderCsmShadowMap(eng, sg, state, csmCfg);
|
|
62
63
|
return sg;
|
|
63
64
|
}
|
|
65
|
+
function getCsmReceiverTexture(sg) {
|
|
66
|
+
if (sg._shadowType !== "csm") {
|
|
67
|
+
throw new Error("getCsmReceiverTexture requires a CSM shadow generator");
|
|
68
|
+
}
|
|
69
|
+
if (sg._csmReceiverTexture) {
|
|
70
|
+
return sg._csmReceiverTexture;
|
|
71
|
+
}
|
|
72
|
+
const texture = sg._depthTexture;
|
|
73
|
+
const receiverTexture = {
|
|
74
|
+
texture,
|
|
75
|
+
view: texture.createView({ dimension: "2d-array" }),
|
|
76
|
+
sampler: sg._depthSampler,
|
|
77
|
+
width: texture.width,
|
|
78
|
+
height: texture.height,
|
|
79
|
+
_sampleType: "depth"
|
|
80
|
+
};
|
|
81
|
+
acquireTexture(receiverTexture);
|
|
82
|
+
sg._csmReceiverTexture = receiverTexture;
|
|
83
|
+
return receiverTexture;
|
|
84
|
+
}
|
|
64
85
|
function onCsmReceiverUpdate(sg, cb) {
|
|
65
86
|
(sg._onReceiverData ??= []).push(cb);
|
|
66
87
|
return () => {
|
|
@@ -75,5 +96,5 @@ function onCsmReceiverUpdate(sg, cb) {
|
|
|
75
96
|
};
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
export { createCsmDirectionalShadowGenerator, onCsmReceiverUpdate };
|
|
99
|
+
export { createCsmDirectionalShadowGenerator, getCsmReceiverTexture, onCsmReceiverUpdate };
|
|
79
100
|
//# sourceMappingURL=csm-directional-shadow-generator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csm-directional-shadow-generator.js","sources":["../../../src/shadow/csm-directional-shadow-generator.ts"],"sourcesContent":["/** Cascaded Shadow Map (CSM) generator for directional lights.\n *\n * Renders the scene depth into N cascade layers of a `depth32float`\n * `texture_2d_array`, each fit to a successive slice of the camera frustum\n * (logarithmic/uniform split blend), and exposes a single large receiver UBO\n * holding the N cascade transforms + split distances. The receiver\n * (`csm-shadow-fragment-core`) selects a cascade per fragment and samples it\n * with a 5×5 PCF kernel — matching Babylon.js `CascadedShadowGenerator` with\n * the default PCF5 filter.\n *\n * All substantive CSM code lives in this module + `csm-shadow-task-hooks.ts` +\n * `csm-shadow-fragment-core.ts`, fetched only by scenes that create a CSM\n * generator, so ESM/PCF scenes are byte-unaffected.\n */\n\nimport type { DirectionalLight } from \"../light/directional-light.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { ShadowGenerator } from \"./shadow-generator.js\";\nimport { createShadowParamsUBO } from \"./shadow-base.js\";\nimport { createUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport { ensureCsmShadowTaskState, preloadCsmShadowTaskState, renderCsmShadowMap, type CsmConfig, type CsmTaskState } from \"./csm-shadow-task-hooks.js\";\nimport { setCsmStdReceiverFactory, setCsmPbrReceiverFactory } from \"./csm-receiver-registry.js\";\nimport { createStdCsmShadowFragment } from \"../material/standard/fragments/std-csm-shadow-fragment.js\";\nimport { createPbrCsmShadowFragment } from \"../material/pbr/fragments/pbr-csm-shadow-fragment.js\";\n\n/** Configuration for a directional-light cascaded shadow generator. */\nexport interface CsmDirectionalShadowGeneratorConfig {\n /** Shadow map resolution per cascade (square). Default 1024. */\n mapSize?: number;\n /** Number of cascades. Default 4 (max 4). */\n numCascades?: number;\n /** Split blend between logarithmic and uniform partitioning, 0..1. Default 0.5. */\n lambda?: number;\n /** Fraction of a cascade used to cross-fade into the next one. Default 0.1. Set 0 to disable blending. */\n cascadeBlendPercentage?: number;\n /** Use a stable bounding-sphere fit (no shimmering) instead of a tight AABB fit. Default false. */\n stabilizeCascades?: boolean;\n /** Maximum shadow distance in world units. Default = camera far plane. */\n shadowMaxZ?: number;\n /** Depth bias added during shadow-map generation. Default 0.00005. */\n bias?: number;\n /** Caster depth offset in world units. Overrides `bias` when supplied; non-positive or non-finite values disable bias. */\n worldSpaceBias?: number;\n /** Shadow darkness (0 = black shadow, 1 = no shadow). Default 0. */\n darkness?: number;\n /** Soft fade-out at the edge of each cascade frustum, 0..1. Default 0. */\n frustumEdgeFalloff?: number;\n /** Regenerate every cascade every frame even when nothing moved. Default false. */\n forceRefreshEveryFrame?: boolean;\n}\n\n/**\n * Creates a cascaded shadow map (CSM) generator for a directional light.\n *\n * The shadow map is a `depth32float` `texture_2d_array` with one layer per\n * cascade; each cascade is fit to a logarithmic/uniform slice of the active\n * camera frustum and sampled on the receiver with a 5×5 PCF kernel. Casters are\n * supplied via {@link setShadowTaskCasterMeshes} and the receiver is any mesh\n * with `receiveShadows = true`.\n *\n * @param engine - The engine providing the GPU device.\n * @param _light - The directional light that casts the shadows.\n * @param cfg - Optional cascade, map-size and bias configuration.\n * @returns A `ShadowGenerator` wired to the CSM render path.\n */\nexport function createCsmDirectionalShadowGenerator(engine: EngineContext, _light: DirectionalLight, cfg: CsmDirectionalShadowGeneratorConfig = {}): ShadowGenerator {\n // Register the material-family CSM receiver fragments lazily — only scenes that\n // create a CSM generator pull the cascade-receiver WGSL into their bundle.\n setCsmStdReceiverFactory(createStdCsmShadowFragment);\n setCsmPbrReceiverFactory(createPbrCsmShadowFragment);\n\n const device = engine._device;\n const mapSize = cfg.mapSize ?? 1024;\n const numCascades = Math.min(cfg.numCascades ?? 4, 4);\n const bias = cfg.bias ?? 0.00005;\n const worldSpaceBias = cfg.worldSpaceBias;\n const darkness = cfg.darkness ?? 0;\n const frustumEdgeFalloff = cfg.frustumEdgeFalloff ?? 0;\n\n const csmCfg: CsmConfig = {\n _numCascades: numCascades,\n _lambda: cfg.lambda ?? 0.5,\n _cascadeBlendPercentage: cfg.cascadeBlendPercentage ?? 0.1,\n _stabilizeCascades: cfg.stabilizeCascades ?? false,\n _depthClamp: false,\n _shadowMaxZ: cfg.shadowMaxZ ?? null,\n _bias: bias,\n _worldSpaceBias: worldSpaceBias === undefined ? null : Number.isFinite(worldSpaceBias) && worldSpaceBias > 0 ? worldSpaceBias : 0,\n _darkness: darkness,\n _frustumEdgeFalloff: frustumEdgeFalloff,\n _mapSize: mapSize,\n _forceRefreshEveryFrame: cfg.forceRefreshEveryFrame ?? false,\n };\n\n const depthTexture = device.createTexture({\n size: { width: mapSize, height: mapSize, depthOrArrayLayers: numCascades },\n format: \"depth32float\",\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,\n });\n\n const _shadowsInfo = new Float32Array([darkness, mapSize, 1.0 / mapSize, frustumEdgeFalloff]);\n\n const sg: ShadowGenerator = {\n _shadowType: \"csm\" as const,\n _light,\n _depthTexture: depthTexture,\n _depthSampler: device.createSampler({ compare: \"less\", magFilter: \"linear\", minFilter: \"linear\" }),\n _lightMatrix: new Float32Array(16),\n _shadowsInfo,\n _depthValues: new Float32Array([0, 1]),\n _shadowParamsUBO: createShadowParamsUBO(engine, bias, 1.0 / mapSize),\n _shadowUBO: createUniformBuffer(engine, new Float32Array(80)),\n _config: {\n _mapSize: mapSize,\n _bias: bias,\n _forceRefreshEveryFrame: csmCfg._forceRefreshEveryFrame,\n },\n _version: 0,\n _csmCascadeCount: numCascades,\n };\n\n sg._preloadShadowTask = preloadCsmShadowTaskState;\n sg._ensureShadowTaskState = (eng, scene, casterMeshes) => {\n const state = ensureCsmShadowTaskState(eng, scene, sg, csmCfg, casterMeshes, sg._shadowTaskState ?? null);\n sg._shadowTaskState = state;\n return state;\n };\n sg._renderShadowMap = (eng, state) => renderCsmShadowMap(eng, sg, state as CsmTaskState, csmCfg);\n return sg;\n}\n\n/**\n * Register a callback fired each frame the CSM cascades are recomputed, right after the receiver\n * UBO is updated and uploaded — and before the shadow map and main pass are rendered.\n *\n * Custom `ShaderMaterial` receivers that mirror the cascade transforms into their own uniforms\n * (rather than binding the generator's receiver UBO directly, as the built-in standard/PBR/node\n * receivers do) MUST sync from inside this callback. Syncing from an `onBeforeRender` callback\n * reads the *previous* frame's transforms — a one-frame lag that makes those shadows visibly swim\n * while the camera moves (the cascade window can slide many texels per frame during a zoom).\n *\n * Multiple receivers may register on the same generator; every registered callback is invoked each\n * frame. The callback receives the 80-float receiver UBO (layout: four `mat4x4` cascade transforms,\n * `viewFrustumZ`, `frustumLengths`, `shadowsInfo`, `csmParams` — see the cascaded-shadow\n * architecture doc). The array is reused each frame; copy what you need.\n *\n * @param sg - A cascaded-shadow generator from {@link createCsmDirectionalShadowGenerator}.\n * @param cb - Receiver-sync callback.\n * @returns A disposer that unregisters this callback.\n */\nexport function onCsmReceiverUpdate(sg: ShadowGenerator, cb: (data: Float32Array) => void): () => void {\n (sg._onReceiverData ??= []).push(cb);\n return () => {\n const list = sg._onReceiverData;\n if (!list) {\n return;\n }\n const i = list.indexOf(cb);\n if (i >= 0) {\n list.splice(i, 1);\n }\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"csm-directional-shadow-generator.js","sources":["../../../src/shadow/csm-directional-shadow-generator.ts"],"sourcesContent":["/** Cascaded Shadow Map (CSM) generator for directional lights.\n *\n * Renders the scene depth into N cascade layers of a `depth32float`\n * `texture_2d_array`, each fit to a successive slice of the camera frustum\n * (logarithmic/uniform split blend), and exposes a single large receiver UBO\n * holding the N cascade transforms + split distances. The receiver\n * (`csm-shadow-fragment-core`) selects a cascade per fragment and samples it\n * with a 5×5 PCF kernel — matching Babylon.js `CascadedShadowGenerator` with\n * the default PCF5 filter.\n *\n * All substantive CSM code lives in this module + `csm-shadow-task-hooks.ts` +\n * `csm-shadow-fragment-core.ts`, fetched only by scenes that create a CSM\n * generator, so ESM/PCF scenes are byte-unaffected.\n */\n\nimport type { DirectionalLight } from \"../light/directional-light.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { ShadowGenerator } from \"./shadow-generator.js\";\nimport type { Texture2D } from \"../texture/texture-2d.js\";\nimport { createShadowParamsUBO } from \"./shadow-base.js\";\nimport { createUniformBuffer } from \"../resource/gpu-buffers.js\";\nimport { acquireTexture } from \"../resource/gpu-pool.js\";\nimport { ensureCsmShadowTaskState, preloadCsmShadowTaskState, renderCsmShadowMap, type CsmConfig, type CsmTaskState } from \"./csm-shadow-task-hooks.js\";\nimport { setCsmStdReceiverFactory, setCsmPbrReceiverFactory } from \"./csm-receiver-registry.js\";\nimport { createStdCsmShadowFragment } from \"../material/standard/fragments/std-csm-shadow-fragment.js\";\nimport { createPbrCsmShadowFragment } from \"../material/pbr/fragments/pbr-csm-shadow-fragment.js\";\n\n/** Configuration for a directional-light cascaded shadow generator. */\nexport interface CsmDirectionalShadowGeneratorConfig {\n /** Shadow map resolution per cascade (square). Default 1024. */\n mapSize?: number;\n /** Number of cascades. Default 4 (max 4). */\n numCascades?: number;\n /** Split blend between logarithmic and uniform partitioning, 0..1. Default 0.5. */\n lambda?: number;\n /** Fraction of a cascade used to cross-fade into the next one. Default 0.1. Set 0 to disable blending. */\n cascadeBlendPercentage?: number;\n /** Use a stable bounding-sphere fit (no shimmering) instead of a tight AABB fit. Default false. */\n stabilizeCascades?: boolean;\n /** Maximum shadow distance in world units. Default = camera far plane. */\n shadowMaxZ?: number;\n /** Depth bias added during shadow-map generation. Default 0.00005. */\n bias?: number;\n /** Caster depth offset in world units. Overrides `bias` when supplied; non-positive or non-finite values disable bias. */\n worldSpaceBias?: number;\n /** Shadow darkness (0 = black shadow, 1 = no shadow). Default 0. */\n darkness?: number;\n /** Soft fade-out at the edge of each cascade frustum, 0..1. Default 0. */\n frustumEdgeFalloff?: number;\n /** Regenerate every cascade every frame even when nothing moved. Default false. */\n forceRefreshEveryFrame?: boolean;\n}\n\n/**\n * Creates a cascaded shadow map (CSM) generator for a directional light.\n *\n * The shadow map is a `depth32float` `texture_2d_array` with one layer per\n * cascade; each cascade is fit to a logarithmic/uniform slice of the active\n * camera frustum and sampled on the receiver with a 5×5 PCF kernel. Casters are\n * supplied via {@link setShadowTaskCasterMeshes} and the receiver is any mesh\n * with `receiveShadows = true`.\n *\n * @param engine - The engine providing the GPU device.\n * @param _light - The directional light that casts the shadows.\n * @param cfg - Optional cascade, map-size and bias configuration.\n * @returns A `ShadowGenerator` wired to the CSM render path.\n */\nexport function createCsmDirectionalShadowGenerator(engine: EngineContext, _light: DirectionalLight, cfg: CsmDirectionalShadowGeneratorConfig = {}): ShadowGenerator {\n // Register the material-family CSM receiver fragments lazily — only scenes that\n // create a CSM generator pull the cascade-receiver WGSL into their bundle.\n setCsmStdReceiverFactory(createStdCsmShadowFragment);\n setCsmPbrReceiverFactory(createPbrCsmShadowFragment);\n\n const device = engine._device;\n const mapSize = cfg.mapSize ?? 1024;\n const numCascades = Math.min(cfg.numCascades ?? 4, 4);\n const bias = cfg.bias ?? 0.00005;\n const worldSpaceBias = cfg.worldSpaceBias;\n const darkness = cfg.darkness ?? 0;\n const frustumEdgeFalloff = cfg.frustumEdgeFalloff ?? 0;\n\n const csmCfg: CsmConfig = {\n _numCascades: numCascades,\n _lambda: cfg.lambda ?? 0.5,\n _cascadeBlendPercentage: cfg.cascadeBlendPercentage ?? 0.1,\n _stabilizeCascades: cfg.stabilizeCascades ?? false,\n _depthClamp: false,\n _shadowMaxZ: cfg.shadowMaxZ ?? null,\n _bias: bias,\n _worldSpaceBias: worldSpaceBias === undefined ? null : Number.isFinite(worldSpaceBias) && worldSpaceBias > 0 ? worldSpaceBias : 0,\n _darkness: darkness,\n _frustumEdgeFalloff: frustumEdgeFalloff,\n _mapSize: mapSize,\n _forceRefreshEveryFrame: cfg.forceRefreshEveryFrame ?? false,\n };\n\n const depthTexture = device.createTexture({\n size: { width: mapSize, height: mapSize, depthOrArrayLayers: numCascades },\n format: \"depth32float\",\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,\n });\n\n const _shadowsInfo = new Float32Array([darkness, mapSize, 1.0 / mapSize, frustumEdgeFalloff]);\n\n const sg: ShadowGenerator = {\n _shadowType: \"csm\" as const,\n _light,\n _depthTexture: depthTexture,\n _depthSampler: device.createSampler({ compare: \"less\", magFilter: \"linear\", minFilter: \"linear\" }),\n _lightMatrix: new Float32Array(16),\n _shadowsInfo,\n _depthValues: new Float32Array([0, 1]),\n _shadowParamsUBO: createShadowParamsUBO(engine, bias, 1.0 / mapSize),\n _shadowUBO: createUniformBuffer(engine, new Float32Array(80)),\n _config: {\n _mapSize: mapSize,\n _bias: bias,\n _forceRefreshEveryFrame: csmCfg._forceRefreshEveryFrame,\n },\n _version: 0,\n _csmCascadeCount: numCascades,\n };\n\n sg._preloadShadowTask = preloadCsmShadowTaskState;\n sg._ensureShadowTaskState = (eng, scene, casterMeshes) => {\n const state = ensureCsmShadowTaskState(eng, scene, sg, csmCfg, casterMeshes, sg._shadowTaskState ?? null);\n sg._shadowTaskState = state;\n return state;\n };\n sg._renderShadowMap = (eng, state) => renderCsmShadowMap(eng, sg, state as CsmTaskState, csmCfg);\n return sg;\n}\n\n/**\n * Returns the borrowed depth-array texture wrapper used by a custom CSM receiver.\n *\n * The wrapper shares the generator's texture and comparison sampler, is cached per\n * generator, and remains valid for the generator's lifetime. Callers must not dispose\n * or release it independently.\n *\n * @param sg - A cascaded-shadow generator from {@link createCsmDirectionalShadowGenerator}.\n * @returns A stable `Texture2D` with a 2d-array depth view.\n */\nexport function getCsmReceiverTexture(sg: ShadowGenerator): Texture2D {\n if (sg._shadowType !== \"csm\") {\n throw new Error(\"getCsmReceiverTexture requires a CSM shadow generator\");\n }\n if (sg._csmReceiverTexture) {\n return sg._csmReceiverTexture;\n }\n const texture = sg._depthTexture;\n const receiverTexture: Texture2D = {\n texture,\n view: texture.createView({ dimension: \"2d-array\" }),\n sampler: sg._depthSampler,\n width: texture.width,\n height: texture.height,\n _sampleType: \"depth\",\n };\n // ShaderMaterial packets acquire/release every bound Texture2D. Keep the\n // generator's ownership ref so removing one receiver cannot destroy the map.\n acquireTexture(receiverTexture);\n sg._csmReceiverTexture = receiverTexture;\n return receiverTexture;\n}\n\n/**\n * Register a callback fired each frame the CSM cascades are recomputed, right after the receiver\n * UBO is updated and uploaded — and before the shadow map and main pass are rendered.\n *\n * Custom `ShaderMaterial` receivers that mirror the cascade transforms into their own uniforms\n * (rather than binding the generator's receiver UBO directly, as the built-in standard/PBR/node\n * receivers do) MUST sync from inside this callback. Syncing from an `onBeforeRender` callback\n * reads the *previous* frame's transforms — a one-frame lag that makes those shadows visibly swim\n * while the camera moves (the cascade window can slide many texels per frame during a zoom).\n *\n * Multiple receivers may register on the same generator; every registered callback is invoked each\n * frame. The callback receives the 80-float receiver UBO (layout: four `mat4x4` cascade transforms,\n * `viewFrustumZ`, `frustumLengths`, `shadowsInfo`, `csmParams` — see the cascaded-shadow\n * architecture doc). The array is reused each frame; copy what you need.\n *\n * @param sg - A cascaded-shadow generator from {@link createCsmDirectionalShadowGenerator}.\n * @param cb - Receiver-sync callback.\n * @returns A disposer that unregisters this callback.\n */\nexport function onCsmReceiverUpdate(sg: ShadowGenerator, cb: (data: Float32Array) => void): () => void {\n (sg._onReceiverData ??= []).push(cb);\n return () => {\n const list = sg._onReceiverData;\n if (!list) {\n return;\n }\n const i = list.indexOf(cb);\n if (i >= 0) {\n list.splice(i, 1);\n }\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -33,35 +33,40 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
if (!casterMatChanged) {
|
|
36
|
-
const prevSet = new Set(existing._casterMeshes);
|
|
37
36
|
const nextSet = new Set(casterMeshes);
|
|
38
37
|
const views = existing._materialViews;
|
|
39
38
|
const gens = existing._casterMatGens;
|
|
39
|
+
const caps = existing._casterMaxCascades;
|
|
40
|
+
const tasks2 = existing._tasks;
|
|
40
41
|
for (const m of existing._casterMeshes) {
|
|
41
|
-
if (!nextSet.has(m)) {
|
|
42
|
-
|
|
42
|
+
if (!nextSet.has(m) || m._shadowMaxCascade !== caps.get(m)) {
|
|
43
|
+
caps.delete(m);
|
|
44
|
+
for (const t of tasks2) {
|
|
43
45
|
removeMeshFromTask(t, m);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
for (const m of casterMeshes) {
|
|
48
|
-
|
|
50
|
+
const maxCascade = m._shadowMaxCascade;
|
|
51
|
+
if (!caps.has(m) && m.material) {
|
|
49
52
|
const view = getNoColorView(m.material, views);
|
|
50
|
-
for (
|
|
51
|
-
|
|
53
|
+
for (let c = 0; c < tasks2.length; c++) {
|
|
54
|
+
if (c <= (maxCascade ?? c)) {
|
|
55
|
+
tasks2[c].addMesh(m, { material: view });
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
gens.set(m.material, effectiveCasterGen(m.material));
|
|
54
59
|
}
|
|
60
|
+
caps.set(m, maxCascade);
|
|
55
61
|
}
|
|
56
|
-
for (const t of
|
|
62
|
+
for (const t of tasks2) {
|
|
57
63
|
t._lastVersion = -1;
|
|
58
64
|
}
|
|
59
65
|
existing._casterMeshes = casterMeshes;
|
|
60
66
|
existing._renderableVersion = scene._renderableVersion;
|
|
61
67
|
return existing;
|
|
62
68
|
}
|
|
63
|
-
|
|
64
|
-
retireGpuResources(engine, () => old.dispose());
|
|
69
|
+
retireGpuResources(engine, existing._task.dispose);
|
|
65
70
|
}
|
|
66
71
|
const materialViews = /* @__PURE__ */ new Map();
|
|
67
72
|
const n = cfg._numCascades;
|
|
@@ -91,7 +96,7 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
91
96
|
const task = createRenderTask({ name: `csm${i}`, rt, clr: true, cam: camera, _skipClusteredLights: true }, engine, scene);
|
|
92
97
|
for (const mesh of casterMeshes) {
|
|
93
98
|
const material = mesh.material;
|
|
94
|
-
if (material) {
|
|
99
|
+
if (material && i <= (mesh._shadowMaxCascade ?? i)) {
|
|
95
100
|
task.addMesh(mesh, { material: getNoColorView(material, materialViews) });
|
|
96
101
|
}
|
|
97
102
|
}
|
|
@@ -118,7 +123,9 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
118
123
|
}
|
|
119
124
|
};
|
|
120
125
|
const casterMatGens = /* @__PURE__ */ new Map();
|
|
126
|
+
const casterMaxCascades = /* @__PURE__ */ new Map();
|
|
121
127
|
for (const m of casterMeshes) {
|
|
128
|
+
casterMaxCascades.set(m, m._shadowMaxCascade);
|
|
122
129
|
if (m.material) {
|
|
123
130
|
casterMatGens.set(m.material, effectiveCasterGen(m.material));
|
|
124
131
|
}
|
|
@@ -137,7 +144,8 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
137
144
|
_renderableVersion: scene._renderableVersion,
|
|
138
145
|
_materialEpoch: scene._materialEpoch,
|
|
139
146
|
_materialViews: materialViews,
|
|
140
|
-
_casterMatGens: casterMatGens
|
|
147
|
+
_casterMatGens: casterMatGens,
|
|
148
|
+
_casterMaxCascades: casterMaxCascades
|
|
141
149
|
};
|
|
142
150
|
}
|
|
143
151
|
function renderCsmShadowMap(engine, sg, state, cfg) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csm-shadow-task-hooks.js","sources":["../../../src/shadow/csm-shadow-task-hooks.ts"],"sourcesContent":["/** Internal CSM (cascaded shadow map) task hooks owned by CSM shadow generators.\n *\n * Mirrors `pcf-shadow-task-hooks.ts`, but renders N cascade layers of a depth\n * `texture_2d_array` and computes per-cascade frustum-split + orthographic-fit\n * matrices from the active camera. All CSM-only math (frustum-corner fit,\n * ortho-off-center, texel snap) lives here so plain ESM/PCF scenes never bundle\n * it. The light view matrix + 4×4 multiply are shared helpers (already used by\n * ESM/PCF) so reusing them adds zero bytes.\n */\n\nimport type { Camera } from \"../camera/camera.js\";\nimport type { DirectionalLight } from \"../light/directional-light.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Material, MaterialView } from \"../material/material.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { RenderTarget } from \"../engine/render-target.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport { createRenderTask, removeMeshFromTask, type RenderTask } from \"../frame-graph/render-task.js\";\nimport { getViewProjectionMatrix } from \"../camera/camera.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { buildLightViewMatrix, casterVersionSum, createShadowCamera, multiply4x4, updateShadowCameraBase } from \"./shadow-base.js\";\nimport { getNoColorView, preloadPcfShadowTaskState } from \"./pcf-shadow-task-hooks.js\";\nimport type { ShadowGenerator, ShadowTaskInternalState } from \"./shadow-generator.js\";\nimport { retireGpuResources } from \"../engine/gpu-resource-retirement.js\";\n\n/** Generation of the material that ACTUALLY casts this caster mesh's shadow — the explicit\n * `_shadowCasterMaterial` override when set, else the mesh's own material. Lets the caster-set diff detect a\n * rebuild of the override caster material (which would otherwise be invisible to a check on the receive material). */\nfunction effectiveCasterGen(material: Material): number {\n const eff = material._shadowCasterMaterial ?? material;\n return (eff as { _csmGen?: number })._csmGen ?? 0;\n}\n\n/** CSM configuration captured by the generator and consumed by these hooks. */\nexport interface CsmConfig {\n /** @internal */\n _numCascades: number;\n /** @internal */\n _lambda: number;\n /** @internal */\n _cascadeBlendPercentage: number;\n /** @internal */\n _stabilizeCascades: boolean;\n /** @internal */\n _depthClamp: boolean;\n /** @internal */\n _shadowMaxZ: number | null;\n /** @internal */\n _bias: number;\n /** @internal */\n _worldSpaceBias: number | null;\n /** @internal */\n _darkness: number;\n /** @internal */\n _frustumEdgeFalloff: number;\n /** @internal */\n _mapSize: number;\n /** @internal */\n _forceRefreshEveryFrame: boolean;\n}\n\nexport interface CsmTaskState extends ShadowTaskInternalState {\n /** @internal */\n _tasks: RenderTask[];\n /** @internal */\n _cameras: Camera[];\n /** @internal */\n _scene: SceneContext;\n /** @internal */\n _cameraVersion: number;\n /** @internal */\n _lastCasterVersion: number;\n /** @internal */\n _lastLightVersion: number;\n /** @internal */\n _lastCamVersion: number;\n /** @internal */\n _uboData: Float32Array;\n /** @internal */\n _casterMeshes: readonly Mesh[];\n /** @internal Scene renderable version the cascade material views were built against. A material\n * swap (plugin/receiver variant change) rebuilds the swapped mesh's renderable + UBOs but leaves\n * this task's cached no-color material views pointing at the now-destroyed UBOs, so we rebuild when\n * the MATERIAL EPOCH changes — not on every renderable-version bump (a geometry resize bumps the\n * renderable version without touching materials, and is handled by a cheap re-record instead). */\n _renderableVersion: number;\n /** @internal Scene material epoch the cascade material views were built against (see `_renderableVersion`). */\n _materialEpoch: number;\n /** @internal Cached per-material no-color depth views, reused when a caster is added incrementally so a pure\n * caster-set change updates the existing cascade tasks instead of rebuilding and re-resolving every caster\n * (which leaked ~casters×cascades UBO handles each time the caster list was re-supplied). */\n _materialViews: Map<Material, MaterialView>;\n /** @internal Per-caster-material generation (`_csmGen`) snapshot at build. The incremental path is taken only\n * while every current caster's material gen is unchanged — i.e. no CASTER material was rebuilt (which would\n * leave its cached no-color view dangling). This is precise, unlike the global `_materialEpoch` which also\n * bumps for swaps of unrelated (non-caster) materials. */\n _casterMatGens: Map<Material, number>;\n}\n\nexport const preloadCsmShadowTaskState = preloadPcfShadowTaskState;\n\n/** Build (or reuse) the CSM task state: N per-layer depth render targets + cameras + tasks. */\nexport function ensureCsmShadowTaskState(\n engine: EngineContext,\n scene: SceneContext,\n sg: ShadowGenerator,\n cfg: CsmConfig,\n casterMeshes: readonly Mesh[],\n existingState: ShadowTaskInternalState | null\n): CsmTaskState {\n const existing = existingState as CsmTaskState | null;\n if (existing) {\n if (existing._casterMeshes === casterMeshes && existing._renderableVersion === scene._renderableVersion) {\n return existing;\n }\n // The caster set is unchanged and NO material was rebuilt/swapped since these tasks were built (the\n // material epoch matches): the only thing that changed is geometry (e.g. resizeMeshGeometry reallocated\n // a caster's GPU buffers, bumping the renderable version). The cascade tasks' cached no-color material\n // views are still valid — only the bundles need refreshing to pick up the new buffer handles, which the\n // shadow scheduler's execute() already does (it re-records when the renderable version moves). So adopt\n // the new state markers and REUSE the existing tasks instead of recreating them — recreating tasks every\n // geometry edit re-compiles pipelines + churns bind-groups/bundles for the whole caster set (multi-MB,\n // never returned by the GPU allocator). Only a real material change (epoch bump) needs a full rebuild,\n // because that destroys the caster UBOs the cached views point at.\n if (existing._casterMeshes === casterMeshes && existing._materialEpoch === scene._materialEpoch) {\n existing._renderableVersion = scene._renderableVersion;\n return existing;\n }\n // The caster SET changed (different array). Decide INCREMENTAL vs full rebuild by whether any CURRENT\n // caster's OWN material was rebuilt since we built (its cached no-color view would dangle) — tracked via\n // a precise per-material gen, NOT the global `_materialEpoch` (which also bumps when an UNRELATED, non-\n // caster material is swapped, e.g. a lit scene mesh added near a caster set re-supply). If NO caster\n // material changed, update the cascade tasks IN PLACE: keep the unchanged casters' resolved depth packets\n // (so nothing is destroyed — no \"buffer used in submit while destroyed\" — and nothing leaks — the old\n // code re-resolved EVERY caster into fresh per-cascade UBO packets and never freed the prior ones,\n // leaking ~casters×cascades handles every time the caster list was re-supplied, which a consumer may do\n // per frame). Only add the new casters / drop departed ones (a regenerated caster's old packet is freed\n // by removeFromScene when its mesh is disposed; a persistent caster simply keeps its packet).\n let casterMatChanged = false;\n for (const m of casterMeshes) {\n const mat = m.material;\n if (!mat) {\n continue;\n }\n const stored = existing._casterMatGens.get(mat);\n if (stored !== undefined && stored !== effectiveCasterGen(mat)) {\n casterMatChanged = true;\n break;\n }\n }\n if (!casterMatChanged) {\n const prevSet = new Set(existing._casterMeshes);\n const nextSet = new Set(casterMeshes);\n const views = existing._materialViews;\n const gens = existing._casterMatGens;\n for (const m of existing._casterMeshes) {\n if (!nextSet.has(m)) {\n for (const t of existing._tasks) {\n removeMeshFromTask(t, m);\n }\n }\n }\n for (const m of casterMeshes) {\n if (!prevSet.has(m) && m.material) {\n const view = getNoColorView(m.material, views);\n for (const t of existing._tasks) {\n t.addMesh(m, { material: view });\n }\n gens.set(m.material, effectiveCasterGen(m.material));\n }\n }\n // Force each cascade to re-resolve its newly-added pending casters + re-bucket its binding lists.\n for (const t of existing._tasks) {\n t._lastVersion = -1;\n }\n existing._casterMeshes = casterMeshes;\n existing._renderableVersion = scene._renderableVersion;\n return existing;\n }\n // A CASTER material was actually rebuilt (a material swap rebuilds its renderable + UBOs but\n // leaves our cached no-color material views dangling at the destroyed UBOs — the\n // \"Buffer used in submit while destroyed\" flood seen when a caster's material swaps variant on first\n // render). Rebuild the cascade tasks below with the casters' CURRENT materials and return the NEW\n // state — the caller swaps to it, so the OLD task is never recorded again. Its GPU buffers may still\n // be referenced by the next frame command buffer, especially during async pre-first-frame construction,\n // so retire it only after that frame has submitted and drained. Mirrors resizeMeshGeometry.\n const old = existing._task;\n retireGpuResources(engine, () => old.dispose());\n }\n\n const materialViews = new Map<Material, MaterialView>();\n const n = cfg._numCascades;\n const tasks: RenderTask[] = [];\n const cameras: Camera[] = [];\n for (let i = 0; i < n; i++) {\n const layerView = sg._depthTexture.createView({ dimension: \"2d\", baseArrayLayer: i, arrayLayerCount: 1 });\n const rt: RenderTarget = {\n _descriptor: {\n size: { width: cfg._mapSize, height: cfg._mapSize },\n dFormat: \"depth32float\",\n _depthClearValue: 1,\n _depthCompare: \"less-equal\",\n samples: 1,\n },\n _colorTexture: null,\n _colorView: null,\n _depthTexture: sg._depthTexture,\n _depthView: layerView,\n _width: cfg._mapSize,\n _height: cfg._mapSize,\n _eager: true,\n _ownsDepthTexture: false, // borrowed: the shared CSM depth array is owned by the generator\n };\n const camera = createShadowCamera(sg);\n const task = createRenderTask({ name: `csm${i}`, rt, clr: true, cam: camera, _skipClusteredLights: true }, engine, scene);\n for (const mesh of casterMeshes) {\n const material = mesh.material;\n if (material) {\n task.addMesh(mesh, { material: getNoColorView(material, materialViews) });\n }\n }\n tasks.push(task);\n cameras.push(camera);\n }\n\n const compositeTask = {\n record(): void {\n for (const t of tasks) {\n t.record();\n }\n },\n execute(): number {\n let draws = 0;\n for (const t of tasks) {\n draws += t.execute?.() ?? 0;\n }\n return draws;\n },\n dispose(): void {\n for (const t of tasks) {\n t.dispose();\n }\n },\n };\n\n // Snapshot each caster material's gen so the next caster-set change can tell whether a CASTER material was\n // rebuilt (→ full rebuild) or only the set changed (→ incremental, keeping unchanged casters' packets).\n const casterMatGens = new Map<Material, number>();\n for (const m of casterMeshes) {\n if (m.material) {\n casterMatGens.set(m.material, effectiveCasterGen(m.material));\n }\n }\n return {\n _task: compositeTask,\n _tasks: tasks,\n _cameras: cameras,\n _scene: scene,\n _cameraVersion: 0,\n _lastCasterVersion: -1,\n _lastLightVersion: -1,\n _lastCamVersion: -1,\n _uboData: new Float32Array(80),\n _casterMeshes: casterMeshes,\n _renderableVersion: scene._renderableVersion,\n _materialEpoch: scene._materialEpoch,\n _materialViews: materialViews,\n _casterMatGens: casterMatGens,\n };\n}\n\n/** Render every cascade layer for this frame, recomputing splits/matrices from the active camera. */\nexport function renderCsmShadowMap(engine: EngineContext, sg: ShadowGenerator, state: CsmTaskState, cfg: CsmConfig): number {\n const casterMeshes = state._casterMeshes;\n const camera = state._scene.camera;\n if (!camera) {\n return 0;\n }\n const casterVersion = casterVersionSum(casterMeshes);\n const lightVersion = sg._light.worldMatrixVersion;\n const camVersion = camera.worldMatrixVersion;\n if (!cfg._forceRefreshEveryFrame && casterVersion === state._lastCasterVersion && lightVersion === state._lastLightVersion && camVersion === state._lastCamVersion) {\n return 0;\n }\n\n const cascades = _computeCsmCascades(engine, camera, sg._light as DirectionalLight, cfg, casterMeshes);\n\n _writeCsmUbo(state._uboData, cascades, cfg);\n sg._version++;\n engine._device.queue.writeBuffer(sg._shadowUBO, 0, state._uboData as Float32Array<ArrayBuffer>);\n\n // Notify custom receivers (e.g. a ShaderMaterial that mirrors the cascade transforms into\n // its own uniforms) with this frame's freshly-computed receiver UBO. This fires inside the\n // shadow task — after the transforms are finalized but before the shadow map and main pass\n // render — so such receivers stay in lock-step with the depth map. Syncing from a\n // `onBeforeRender` callback instead would read the previous frame's transforms (a one-frame\n // lag that makes those shadows swim while the camera moves). The built-in standard/PBR/node\n // receivers don't need this: they bind `sg._shadowUBO` directly.\n const receiverCbs = sg._onReceiverData;\n if (receiverCbs) {\n for (let i = 0; i < receiverCbs.length; i++) {\n receiverCbs[i]!(state._uboData);\n }\n }\n\n state._cameraVersion++;\n for (let i = 0; i < cascades._transforms.length; i++) {\n const cam = state._cameras[i]!;\n cam.fov = 1;\n const clipBias = cfg._worldSpaceBias === null ? cfg._bias * 0.5 : csmWorldBiasClipOffset(cfg._worldSpaceBias, cascades._near[i]!, cascades._far[i]!);\n updateShadowCameraBase(cam, state._cameraVersion, cascades._near[i]!, cascades._far[i]!, cascades._views[i]!, _biasViewProjection(cascades._biased[i]!, clipBias));\n }\n\n state._lastCasterVersion = casterVersion;\n state._lastLightVersion = lightVersion;\n state._lastCamVersion = camVersion;\n return state._task.execute?.() ?? 0;\n}\n\n// ─── CSM math (isolated to this module) ─────────────────────────────\n\ninterface CsmCascades {\n /** @internal Unbiased receiver transform per cascade (col-major). */\n _transforms: Float32Array[];\n /** @internal Same as _transforms, used for the caster camera before bias. */\n _biased: Float32Array[];\n /** @internal Cascade light view matrix per cascade (col-major). */\n _views: Float32Array[];\n /** @internal Ortho near per cascade. */\n _near: number[];\n /** @internal Ortho far per cascade. */\n _far: number[];\n /** @internal Camera-view-space split distance per cascade. */\n _viewFrustumZ: number[];\n /** @internal Slice length per cascade. */\n _frustumLengths: number[];\n}\n\n/** Lite reverse-Z NDC frustum corners (near z=1, far z=0); xy each -1 or +1. */\nconst FRUSTUM_NDC: ReadonlyArray<readonly [number, number, number]> = [\n [-1, 1, 1],\n [1, 1, 1],\n [1, -1, 1],\n [-1, -1, 1],\n [-1, 1, 0],\n [1, 1, 0],\n [1, -1, 0],\n [-1, -1, 0],\n];\n\nfunction transformCoord(m: ArrayLike<number>, x: number, y: number, z: number): [number, number, number] {\n const X = m[0]! * x + m[4]! * y + m[8]! * z + m[12]!;\n const Y = m[1]! * x + m[5]! * y + m[9]! * z + m[13]!;\n const Z = m[2]! * x + m[6]! * y + m[10]! * z + m[14]!;\n const W = m[3]! * x + m[7]! * y + m[11]! * z + m[15]!;\n return [X / W, Y / W, Z / W];\n}\n\n/** Column-major OrthoOffCenterLH with half-z NDC (z: near→0, far→1). */\nfunction orthoOffCenterLH(l: number, r: number, b: number, t: number, n: number, f: number): Float32Array {\n const m = new Float32Array(16);\n m[0] = 2 / (r - l);\n m[5] = 2 / (t - b);\n m[10] = 1 / (f - n);\n m[12] = -(r + l) / (r - l);\n m[13] = -(t + b) / (t - b);\n m[14] = -n / (f - n);\n m[15] = 1;\n return m;\n}\n\nfunction _computeCsmCascades(engine: EngineContext, camera: Camera, light: DirectionalLight, cfg: CsmConfig, casterMeshes: readonly Mesh[]): CsmCascades {\n const near = camera.nearPlane;\n const far = camera.farPlane;\n const cameraRange = far - near;\n const shadowMaxZ = cfg._shadowMaxZ ?? far;\n const maxDistance = shadowMaxZ < far && shadowMaxZ >= near ? Math.min((shadowMaxZ - near) / (far - near), 1) : 1;\n const minDistance = 0;\n const minZ = near + minDistance * cameraRange;\n const maxZ = near + maxDistance * cameraRange;\n const range = maxZ - minZ;\n const ratio = maxZ / minZ;\n const n = cfg._numCascades;\n\n const breakDist: number[] = [];\n const viewFrustumZ: number[] = [];\n const frustumLengths: number[] = [];\n for (let i = 0; i < n; i++) {\n const p = (i + 1) / n;\n const log = minZ * ratio ** p;\n const uniform = minZ + range * p;\n const d = cfg._lambda * (log - uniform) + uniform;\n const prevBreak = i === 0 ? minDistance : breakDist[i - 1]!;\n breakDist[i] = (d - near) / cameraRange;\n viewFrustumZ[i] = d;\n frustumLengths[i] = (breakDist[i]! - prevBreak) * cameraRange;\n }\n\n // Light direction (normalized), avoiding a perfectly vertical degenerate case.\n let dx = light.direction.x;\n let dy = light.direction.y;\n let dz = light.direction.z;\n const dl = Math.hypot(dx, dy, dz) || 1;\n dx /= dl;\n dy /= dl;\n dz /= dl;\n if (Math.abs(dy) >= 1) {\n dz = 1e-13;\n }\n\n const aspect = engine.canvas.width / engine.canvas.height;\n const vp = getViewProjectionMatrix(camera, aspect) as unknown as ArrayLike<number>;\n const inv = mat4Invert(vp as never);\n const invViewProj: ArrayLike<number> = (inv as unknown as ArrayLike<number>) ?? vp;\n\n const aabb = _castersWorldAabb(casterMeshes);\n\n const transforms: Float32Array[] = [];\n const views: Float32Array[] = [];\n const nearOut: number[] = [];\n const farOut: number[] = [];\n\n for (let c = 0; c < n; c++) {\n const prevSplit = c === 0 ? 0 : breakDist[c - 1]!;\n const split = breakDist[c]!;\n\n // World-space frustum corners of this slice.\n const corners: [number, number, number][] = [];\n for (let k = 0; k < 8; k++) {\n const ndc = FRUSTUM_NDC[k]!;\n corners.push(transformCoord(invViewProj, ndc[0], ndc[1], ndc[2]));\n }\n for (let k = 0; k < 4; k++) {\n const nearC = corners[k]!;\n const farC = corners[k + 4]!;\n const rx = farC[0] - nearC[0];\n const ry = farC[1] - nearC[1];\n const rz = farC[2] - nearC[2];\n corners[k + 4] = [nearC[0] + rx * split, nearC[1] + ry * split, nearC[2] + rz * split];\n corners[k] = [nearC[0] + rx * prevSplit, nearC[1] + ry * prevSplit, nearC[2] + rz * prevSplit];\n }\n\n // Centroid.\n let cx = 0,\n cy = 0,\n cz = 0;\n for (const p of corners) {\n cx += p[0];\n cy += p[1];\n cz += p[2];\n }\n cx /= 8;\n cy /= 8;\n cz /= 8;\n\n let minX: number, maxX: number, minY: number, maxY: number, minEz: number, maxEz: number;\n let stableRadius = 0;\n if (cfg._stabilizeCascades) {\n let radius = 0;\n for (const p of corners) {\n radius = Math.max(radius, Math.hypot(p[0] - cx, p[1] - cy, p[2] - cz));\n }\n radius = Math.ceil(radius * 16) / 16;\n stableRadius = radius;\n minX = minY = minEz = -radius;\n maxX = maxY = maxEz = radius;\n } else {\n // Temp light view centred on the centroid to fit a tight AABB.\n const tmpView = buildLightViewMatrix(dx, dy, dz, cx, cy, cz);\n minX = minY = minEz = Infinity;\n maxX = maxY = maxEz = -Infinity;\n for (const p of corners) {\n const lp = transformCoord(tmpView, p[0], p[1], p[2]);\n minX = Math.min(minX, lp[0]);\n maxX = Math.max(maxX, lp[0]);\n minY = Math.min(minY, lp[1]);\n maxY = Math.max(maxY, lp[1]);\n minEz = Math.min(minEz, lp[2]);\n maxEz = Math.max(maxEz, lp[2]);\n }\n }\n\n // Shadow camera sits behind the slice along the light direction.\n const eyeX = cx + dx * minEz;\n const eyeY = cy + dy * minEz;\n const eyeZ = cz + dz * minEz;\n const view = buildLightViewMatrix(dx, dy, dz, eyeX, eyeY, eyeZ);\n\n let viewMinZ = 0;\n let viewMaxZ = maxEz - minEz;\n\n // Tighten Z to the caster bounding box in cascade view space (depthClamp = false behaviour:\n // keep all casters inside the frustum so no GPU depth-clip feature is required).\n if (aabb) {\n let cMinZ = Infinity;\n let cMaxZ = -Infinity;\n for (let k = 0; k < 8; k++) {\n const wx = k & 1 ? aabb._max[0] : aabb._min[0];\n const wy = k & 2 ? aabb._max[1] : aabb._min[1];\n const wz = k & 4 ? aabb._max[2] : aabb._min[2];\n const lz = view[2]! * wx + view[6]! * wy + view[10]! * wz + view[14]!;\n cMinZ = Math.min(cMinZ, lz);\n cMaxZ = Math.max(cMaxZ, lz);\n }\n if (cMinZ <= viewMaxZ) {\n viewMinZ = Math.min(viewMinZ, cMinZ);\n viewMaxZ = Math.min(viewMaxZ, cMaxZ);\n }\n\n // Z is intentionally NOT quantized here. The caster-AABB fit (cMinZ/cMaxZ) is C0-continuous in the\n // light direction — each is a min/max of linear functions of the light vector, so it has kinks but no\n // jumps — meaning the near/far drift SMOOTHLY as the light rotates. A constant NDC depth bias maps to a\n // WORLD bias of bias·(far−near), and the stored depths are likewise normalised by (far−near); the old\n // `zq = max(0.5, radius/128)` floor/ceil snapped that range to a grid, so both the effective bias AND\n // the stored depth STEPPED at each quantum boundary as the light direction changed — appearing as\n // self-shadow acne that VIBRATES. Removing the quantize makes those steps a sub-millimetre, imperceptible\n // drift, and still covers the moving-caster case it was added for (the range drifts, it never pops).\n }\n\n // The caster matrix adds the world-space bias toward clip Z=1. Reserve the same distance at the far plane\n // so geometry on the tightly fitted caster bound remains inside the clip volume after that offset.\n if (cfg._worldSpaceBias) {\n viewMaxZ += cfg._worldSpaceBias;\n }\n\n const proj0 = orthoOffCenterLH(minX, maxX, minY, maxY, viewMinZ, viewMaxZ);\n let transform = multiply4x4(proj0, view);\n\n // Texel-snap: lock the shadow grid to world space so it does not crawl as the camera moves, by rounding a\n // fixed WORLD anchor onto the shadow-map texel grid. BJS anchors the WORLD ORIGIN. With a STILL camera and\n // a slowly ROTATING light that is the cause of the visible \"vibration\": the eye is recentred on the cascade\n // centre every frame, so the snap residual is the anchor's offset from the centre measured along the light\n // axes — and the world origin's offset from the centre is large (≈ the cascade's distance from origin), so\n // its projection sweeps many texels per degree of light rotation and Math.round trips a full-texel correction\n // again and again → the whole map pops/boils. We instead anchor the world-grid point NEAREST the cascade\n // centre (cell = one texel in world units): it is still a fixed world point (a translating camera only ever\n // sees whole-cell anchor switches, ≤ a sub-texel grid wiggle that PCF hides), but its offset from the centre\n // is < 1 texel, so a full light rotation sweeps it < 1 texel → effectively no rotation pop. Non-stabilized\n // path keeps the origin anchor (no stableRadius → no texel-world size to build the world grid from).\n let aClipX = transform[12]!;\n let aClipY = transform[13]!;\n if (cfg._stabilizeCascades && stableRadius > 0) {\n const texelWorld = (2 * stableRadius) / cfg._mapSize;\n // Align the world anchor grid to the LIGHT's right/up axes (R,U), not world x/y/z. A translating camera then\n // switches cells by exactly ONE texel along R/U -> an integer texel shift in clip space (no crawl). The old\n // world-axis grid jumped texelWorld along a world axis, which projects to a NON-integer texel count (R,U are\n // not the world axes) -> the sub-texel wiggle that crawled during translation. R,U = view rows 0,1.\n const rX = view[0]!,\n rY = view[4]!,\n rZ = view[8]!;\n const uX = view[1]!,\n uY = view[5]!,\n uZ = view[9]!;\n // Project the centre onto R,U, round to the texel grid, rebuild the world anchor in the R-U plane (the\n // light-dir component only affects clip Z, so dropping it leaves clip X/Y, which depend on R,U, exact).\n const sr = Math.round((rX * cx + rY * cy + rZ * cz) / texelWorld) * texelWorld;\n const tr = Math.round((uX * cx + uY * cy + uZ * cz) / texelWorld) * texelWorld;\n const ax = sr * rX + tr * uX;\n const ay = sr * rY + tr * uY;\n const az = sr * rZ + tr * uZ;\n aClipX = transform[0]! * ax + transform[4]! * ay + transform[8]! * az + transform[12]!;\n aClipY = transform[1]! * ax + transform[5]! * ay + transform[9]! * az + transform[13]!;\n }\n const ox = aClipX * (cfg._mapSize / 2);\n const oy = aClipY * (cfg._mapSize / 2);\n const offX = (Math.round(ox) - ox) * (2 / cfg._mapSize);\n const offY = (Math.round(oy) - oy) * (2 / cfg._mapSize);\n const snap = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, offX, offY, 0, 1]);\n const proj = multiply4x4(snap, proj0);\n transform = multiply4x4(proj, view);\n\n transforms.push(transform);\n views.push(view);\n nearOut.push(viewMinZ);\n farOut.push(viewMaxZ);\n }\n\n return {\n _transforms: transforms,\n _biased: transforms.map((t) => new Float32Array(t)),\n _views: views,\n _near: nearOut,\n _far: farOut,\n _viewFrustumZ: viewFrustumZ,\n _frustumLengths: frustumLengths,\n };\n}\n\nfunction _castersWorldAabb(casterMeshes: readonly Mesh[]): { _min: [number, number, number]; _max: [number, number, number] } | null {\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity,\n maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (const mesh of casterMeshes) {\n // Thin-instanced casters are drawn at `finalWorld = mesh.world * instanceMatrix` (see\n // thin-instance-fragment.ts), so a single `mesh.worldMatrix × boundMin/Max` box ignores the per-instance\n // spread entirely — one prototype-sized box wrecks the cascade Z-fit (an off-world herd collapsed every\n // shadow). Bound the caster by the union of every drawn instance instead, using the SAME composition the\n // shader uses.\n const ti = mesh.thinInstances;\n if (ti && ti.count > 0 && ti.matrices) {\n const a = _thinInstanceWorldAabb(mesh, ti);\n if (a) {\n minX = Math.min(minX, a._min[0]);\n maxX = Math.max(maxX, a._max[0]);\n minY = Math.min(minY, a._min[1]);\n maxY = Math.max(maxY, a._max[1]);\n minZ = Math.min(minZ, a._min[2]);\n maxZ = Math.max(maxZ, a._max[2]);\n }\n continue;\n }\n const world = mesh.worldMatrix;\n const bmin = mesh.boundMin ?? [-0.5, -0.5, -0.5];\n const bmax = mesh.boundMax ?? [0.5, 0.5, 0.5];\n for (let k = 0; k < 8; k++) {\n const lx = k & 1 ? bmax[0]! : bmin[0]!;\n const ly = k & 2 ? bmax[1]! : bmin[1]!;\n const lz = k & 4 ? bmax[2]! : bmin[2]!;\n const wx = world[0]! * lx + world[4]! * ly + world[8]! * lz + world[12]!;\n const wy = world[1]! * lx + world[5]! * ly + world[9]! * lz + world[13]!;\n const wz = world[2]! * lx + world[6]! * ly + world[10]! * lz + world[14]!;\n minX = Math.min(minX, wx);\n maxX = Math.max(maxX, wx);\n minY = Math.min(minY, wy);\n maxY = Math.max(maxY, wy);\n minZ = Math.min(minZ, wz);\n maxZ = Math.max(maxZ, wz);\n }\n }\n if (!Number.isFinite(minX)) {\n return null;\n }\n return { _min: [minX, minY, minZ], _max: [maxX, maxY, maxZ] };\n}\n\ninterface ThinCasterAabb {\n _min: [number, number, number];\n _max: [number, number, number];\n}\n\n/** Per-mesh cache of a thin-instanced caster's world AABB. It keys on instance data, prototype\n * transform, and the shared non-transform caster epoch.\n * Lazily allocated so this module keeps zero import-time side effects and stays tree-shakable. */\nlet _thinCasterAabbCache: WeakMap<Mesh, { _version: number; _worldVersion: number; _aabb: ThinCasterAabb | null }> | null = null;\nfunction _getThinCasterAabbCache(): WeakMap<Mesh, { _version: number; _worldVersion: number; _aabb: ThinCasterAabb | null }> {\n if (!_thinCasterAabbCache) {\n _thinCasterAabbCache = new WeakMap();\n }\n return _thinCasterAabbCache;\n}\n\n/** World AABB of a thin-instanced caster. Matches the shader's `finalWorld = mesh.world * instanceMatrix`\n * exactly: each local bound corner is transformed by the per-instance matrix, then by the prototype mesh\n * world matrix. Parked/degenerate instances (zero linear part — drawn as zero-area, used to hide an unused\n * tail) are skipped so a tail parked far off-world can't balloon the box. */\nfunction _thinInstanceWorldAabb(mesh: Mesh, ti: NonNullable<Mesh[\"thinInstances\"]>): ThinCasterAabb | null {\n const cache = _getThinCasterAabbCache();\n const worldVersion = mesh.worldMatrixVersion;\n const cached = cache.get(mesh);\n if (cached && cached._version === ti._version && cached._worldVersion === worldVersion) {\n return cached._aabb;\n }\n // Hoist the prototype world matrix once (worldMatrix is a getter) — it is constant across all instances.\n const world = mesh.worldMatrix;\n const bmin = mesh.boundMin ?? [-0.5, -0.5, -0.5];\n const bmax = mesh.boundMax ?? [0.5, 0.5, 0.5];\n const mats = ti.matrices;\n const count = Math.min(ti.count, (mats.length / 16) | 0);\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity,\n maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (let i = 0; i < count; i++) {\n const o = i * 16;\n // Skip parked instances (zero 3×3 linear part → zero-area triangles that rasterize to nothing).\n const lin =\n Math.abs(mats[o]!) +\n Math.abs(mats[o + 1]!) +\n Math.abs(mats[o + 2]!) +\n Math.abs(mats[o + 4]!) +\n Math.abs(mats[o + 5]!) +\n Math.abs(mats[o + 6]!) +\n Math.abs(mats[o + 8]!) +\n Math.abs(mats[o + 9]!) +\n Math.abs(mats[o + 10]!);\n if (lin < 1e-9) {\n continue;\n }\n for (let k = 0; k < 8; k++) {\n const lx = k & 1 ? bmax[0]! : bmin[0]!;\n const ly = k & 2 ? bmax[1]! : bmin[1]!;\n const lz = k & 4 ? bmax[2]! : bmin[2]!;\n // 1) instance-local: ip = instanceMatrix * localCorner\n const ix = mats[o]! * lx + mats[o + 4]! * ly + mats[o + 8]! * lz + mats[o + 12]!;\n const iy = mats[o + 1]! * lx + mats[o + 5]! * ly + mats[o + 9]! * lz + mats[o + 13]!;\n const iz = mats[o + 2]! * lx + mats[o + 6]! * ly + mats[o + 10]! * lz + mats[o + 14]!;\n // 2) world: wp = mesh.world * ip (matches finalWorld = mesh.world * instanceMatrix)\n const wx = world[0]! * ix + world[4]! * iy + world[8]! * iz + world[12]!;\n const wy = world[1]! * ix + world[5]! * iy + world[9]! * iz + world[13]!;\n const wz = world[2]! * ix + world[6]! * iy + world[10]! * iz + world[14]!;\n if (wx < minX) {\n minX = wx;\n }\n if (wx > maxX) {\n maxX = wx;\n }\n if (wy < minY) {\n minY = wy;\n }\n if (wy > maxY) {\n maxY = wy;\n }\n if (wz < minZ) {\n minZ = wz;\n }\n if (wz > maxZ) {\n maxZ = wz;\n }\n }\n }\n const aabb: ThinCasterAabb | null = Number.isFinite(minX) ? { _min: [minX, minY, minZ], _max: [maxX, maxY, maxZ] } : null;\n cache.set(mesh, { _version: ti._version, _worldVersion: worldVersion, _aabb: aabb });\n return aabb;\n}\n\nfunction _writeCsmUbo(out: Float32Array, cascades: CsmCascades, cfg: CsmConfig): void {\n out.fill(0);\n const n = cascades._transforms.length;\n for (let i = 0; i < n; i++) {\n out.set(cascades._transforms[i]!, i * 16);\n }\n for (let i = 0; i < n; i++) {\n out[64 + i] = cascades._viewFrustumZ[i]!;\n out[68 + i] = cascades._frustumLengths[i]!;\n }\n out[72] = cfg._darkness;\n out[73] = cfg._mapSize;\n out[74] = 1 / cfg._mapSize;\n out[75] = cfg._frustumEdgeFalloff;\n out[76] = n;\n out[77] = cfg._cascadeBlendPercentage === 0 ? 10000 : 1 / cfg._cascadeBlendPercentage;\n}\n\n/** @internal Convert a physical caster offset to the clip-space Z offset for one orthographic cascade. */\nexport function csmWorldBiasClipOffset(worldSpaceBias: number, near: number, far: number): number {\n const range = far - near;\n if (!Number.isFinite(worldSpaceBias) || worldSpaceBias <= 0 || !Number.isFinite(range) || range <= 0) {\n return 0;\n }\n return worldSpaceBias / range;\n}\n\nfunction _biasViewProjection(viewProj: Float32Array, clipOffset: number): Float32Array {\n const biased = new Float32Array(viewProj);\n for (let col = 0; col < 4; col++) {\n const z = 2 + col * 4;\n const w = 3 + col * 4;\n biased[z] = biased[z]! + clipOffset * biased[w]!;\n }\n return biased;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"csm-shadow-task-hooks.js","sources":["../../../src/shadow/csm-shadow-task-hooks.ts"],"sourcesContent":["/** Internal CSM (cascaded shadow map) task hooks owned by CSM shadow generators.\n *\n * Mirrors `pcf-shadow-task-hooks.ts`, but renders N cascade layers of a depth\n * `texture_2d_array` and computes per-cascade frustum-split + orthographic-fit\n * matrices from the active camera. All CSM-only math (frustum-corner fit,\n * ortho-off-center, texel snap) lives here so plain ESM/PCF scenes never bundle\n * it. The light view matrix + 4×4 multiply are shared helpers (already used by\n * ESM/PCF) so reusing them adds zero bytes.\n */\n\nimport type { Camera } from \"../camera/camera.js\";\nimport type { DirectionalLight } from \"../light/directional-light.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type { Material, MaterialView } from \"../material/material.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { RenderTarget } from \"../engine/render-target.js\";\nimport type { SceneContext } from \"../scene/scene-core.js\";\nimport { createRenderTask, removeMeshFromTask, type RenderTask } from \"../frame-graph/render-task.js\";\nimport { getViewProjectionMatrix } from \"../camera/camera.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { buildLightViewMatrix, casterVersionSum, createShadowCamera, multiply4x4, updateShadowCameraBase } from \"./shadow-base.js\";\nimport { getNoColorView, preloadPcfShadowTaskState } from \"./pcf-shadow-task-hooks.js\";\nimport type { ShadowGenerator, ShadowTaskInternalState } from \"./shadow-generator.js\";\nimport { retireGpuResources } from \"../engine/gpu-resource-retirement.js\";\n\n/** Generation of the material that ACTUALLY casts this caster mesh's shadow — the explicit\n * `_shadowCasterMaterial` override when set, else the mesh's own material. Lets the caster-set diff detect a\n * rebuild of the override caster material (which would otherwise be invisible to a check on the receive material). */\nfunction effectiveCasterGen(material: Material): number {\n const eff = material._shadowCasterMaterial ?? material;\n return (eff as { _csmGen?: number })._csmGen ?? 0;\n}\n\n/** CSM configuration captured by the generator and consumed by these hooks. */\nexport interface CsmConfig {\n /** @internal */\n _numCascades: number;\n /** @internal */\n _lambda: number;\n /** @internal */\n _cascadeBlendPercentage: number;\n /** @internal */\n _stabilizeCascades: boolean;\n /** @internal */\n _depthClamp: boolean;\n /** @internal */\n _shadowMaxZ: number | null;\n /** @internal */\n _bias: number;\n /** @internal */\n _worldSpaceBias: number | null;\n /** @internal */\n _darkness: number;\n /** @internal */\n _frustumEdgeFalloff: number;\n /** @internal */\n _mapSize: number;\n /** @internal */\n _forceRefreshEveryFrame: boolean;\n}\n\nexport interface CsmTaskState extends ShadowTaskInternalState {\n /** @internal */\n _tasks: RenderTask[];\n /** @internal */\n _cameras: Camera[];\n /** @internal */\n _scene: SceneContext;\n /** @internal */\n _cameraVersion: number;\n /** @internal */\n _lastCasterVersion: number;\n /** @internal */\n _lastLightVersion: number;\n /** @internal */\n _lastCamVersion: number;\n /** @internal */\n _uboData: Float32Array;\n /** @internal */\n _casterMeshes: readonly Mesh[];\n /** @internal Scene renderable version the cascade material views were built against. A material\n * swap (plugin/receiver variant change) rebuilds the swapped mesh's renderable + UBOs but leaves\n * this task's cached no-color material views pointing at the now-destroyed UBOs, so we rebuild when\n * the MATERIAL EPOCH changes — not on every renderable-version bump (a geometry resize bumps the\n * renderable version without touching materials, and is handled by a cheap re-record instead). */\n _renderableVersion: number;\n /** @internal Scene material epoch the cascade material views were built against (see `_renderableVersion`). */\n _materialEpoch: number;\n /** @internal Cached per-material no-color depth views, reused when a caster is added incrementally so a pure\n * caster-set change updates the existing cascade tasks instead of rebuilding and re-resolving every caster\n * (which leaked ~casters×cascades UBO handles each time the caster list was re-supplied). */\n _materialViews: Map<Material, MaterialView>;\n /** @internal Per-caster-material generation (`_csmGen`) snapshot at build. The incremental path is taken only\n * while every current caster's material gen is unchanged — i.e. no CASTER material was rebuilt (which would\n * leave its cached no-color view dangling). This is precise, unlike the global `_materialEpoch` which also\n * bumps for swaps of unrelated (non-caster) materials. */\n _casterMatGens: Map<Material, number>;\n /** @internal Per-caster cascade-cap snapshot used to update task membership incrementally. */\n _casterMaxCascades: Map<Mesh, number | undefined>;\n}\n\nexport const preloadCsmShadowTaskState = preloadPcfShadowTaskState;\n\n/** Build (or reuse) the CSM task state: N per-layer depth render targets + cameras + tasks. */\nexport function ensureCsmShadowTaskState(\n engine: EngineContext,\n scene: SceneContext,\n sg: ShadowGenerator,\n cfg: CsmConfig,\n casterMeshes: readonly Mesh[],\n existingState: ShadowTaskInternalState | null\n): CsmTaskState {\n const existing = existingState as CsmTaskState | null;\n if (existing) {\n if (existing._casterMeshes === casterMeshes && existing._renderableVersion === scene._renderableVersion) {\n return existing;\n }\n // The caster set is unchanged and NO material was rebuilt/swapped since these tasks were built (the\n // material epoch matches): the only thing that changed is geometry (e.g. resizeMeshGeometry reallocated\n // a caster's GPU buffers, bumping the renderable version). The cascade tasks' cached no-color material\n // views are still valid — only the bundles need refreshing to pick up the new buffer handles, which the\n // shadow scheduler's execute() already does (it re-records when the renderable version moves). So adopt\n // the new state markers and REUSE the existing tasks instead of recreating them — recreating tasks every\n // geometry edit re-compiles pipelines + churns bind-groups/bundles for the whole caster set (multi-MB,\n // never returned by the GPU allocator). Only a real material change (epoch bump) needs a full rebuild,\n // because that destroys the caster UBOs the cached views point at.\n if (existing._casterMeshes === casterMeshes && existing._materialEpoch === scene._materialEpoch) {\n existing._renderableVersion = scene._renderableVersion;\n return existing;\n }\n // The caster SET changed (different array). Decide INCREMENTAL vs full rebuild by whether any CURRENT\n // caster's OWN material was rebuilt since we built (its cached no-color view would dangle) — tracked via\n // a precise per-material gen, NOT the global `_materialEpoch` (which also bumps when an UNRELATED, non-\n // caster material is swapped, e.g. a lit scene mesh added near a caster set re-supply). If NO caster\n // material changed, update the cascade tasks IN PLACE: keep the unchanged casters' resolved depth packets\n // (so nothing is destroyed — no \"buffer used in submit while destroyed\" — and nothing leaks — the old\n // code re-resolved EVERY caster into fresh per-cascade UBO packets and never freed the prior ones,\n // leaking ~casters×cascades handles every time the caster list was re-supplied, which a consumer may do\n // per frame). Only add the new casters / drop departed ones (a regenerated caster's old packet is freed\n // by removeFromScene when its mesh is disposed; a persistent caster simply keeps its packet).\n let casterMatChanged = false;\n for (const m of casterMeshes) {\n const mat = m.material;\n if (!mat) {\n continue;\n }\n const stored = existing._casterMatGens.get(mat);\n if (stored !== undefined && stored !== effectiveCasterGen(mat)) {\n casterMatChanged = true;\n break;\n }\n }\n if (!casterMatChanged) {\n const nextSet = new Set(casterMeshes);\n const views = existing._materialViews;\n const gens = existing._casterMatGens;\n const caps = existing._casterMaxCascades;\n const tasks = existing._tasks;\n for (const m of existing._casterMeshes) {\n if (!nextSet.has(m) || m._shadowMaxCascade !== caps.get(m)) {\n caps.delete(m);\n for (const t of tasks) {\n removeMeshFromTask(t, m);\n }\n }\n }\n for (const m of casterMeshes) {\n const maxCascade = m._shadowMaxCascade;\n if (!caps.has(m) && m.material) {\n const view = getNoColorView(m.material, views);\n for (let c = 0; c < tasks.length; c++) {\n if (c <= (maxCascade ?? c)) {\n tasks[c]!.addMesh(m, { material: view });\n }\n }\n gens.set(m.material, effectiveCasterGen(m.material));\n }\n caps.set(m, maxCascade);\n }\n // Force each cascade to re-resolve its newly-added pending casters + re-bucket its binding lists.\n for (const t of tasks) {\n t._lastVersion = -1;\n }\n existing._casterMeshes = casterMeshes;\n existing._renderableVersion = scene._renderableVersion;\n return existing;\n }\n // A CASTER material was actually rebuilt (a material swap rebuilds its renderable + UBOs but\n // leaves our cached no-color material views dangling at the destroyed UBOs — the\n // \"Buffer used in submit while destroyed\" flood seen when a caster's material swaps variant on first\n // render). Rebuild the cascade tasks below with the casters' CURRENT materials and return the NEW\n // state — the caller swaps to it, so the OLD task is never recorded again. Its GPU buffers may still\n // be referenced by the next frame command buffer, especially during async pre-first-frame construction,\n // so retire it only after that frame has submitted and drained. Mirrors resizeMeshGeometry.\n retireGpuResources(engine, existing._task.dispose);\n }\n\n const materialViews = new Map<Material, MaterialView>();\n const n = cfg._numCascades;\n const tasks: RenderTask[] = [];\n const cameras: Camera[] = [];\n for (let i = 0; i < n; i++) {\n const layerView = sg._depthTexture.createView({ dimension: \"2d\", baseArrayLayer: i, arrayLayerCount: 1 });\n const rt: RenderTarget = {\n _descriptor: {\n size: { width: cfg._mapSize, height: cfg._mapSize },\n dFormat: \"depth32float\",\n _depthClearValue: 1,\n _depthCompare: \"less-equal\",\n samples: 1,\n },\n _colorTexture: null,\n _colorView: null,\n _depthTexture: sg._depthTexture,\n _depthView: layerView,\n _width: cfg._mapSize,\n _height: cfg._mapSize,\n _eager: true,\n _ownsDepthTexture: false, // borrowed: the shared CSM depth array is owned by the generator\n };\n const camera = createShadowCamera(sg);\n const task = createRenderTask({ name: `csm${i}`, rt, clr: true, cam: camera, _skipClusteredLights: true }, engine, scene);\n for (const mesh of casterMeshes) {\n const material = mesh.material;\n // Per-caster cascade cap: a capped caster renders only into layers 0..maxCascade (its far-layer\n // shadow is sub-texel anyway), saving the excluded layers' draws + pipeline switches.\n if (material && i <= (mesh._shadowMaxCascade ?? i)) {\n task.addMesh(mesh, { material: getNoColorView(material, materialViews) });\n }\n }\n tasks.push(task);\n cameras.push(camera);\n }\n\n const compositeTask = {\n record(): void {\n for (const t of tasks) {\n t.record();\n }\n },\n execute(): number {\n let draws = 0;\n for (const t of tasks) {\n draws += t.execute?.() ?? 0;\n }\n return draws;\n },\n dispose(): void {\n for (const t of tasks) {\n t.dispose();\n }\n },\n };\n\n // Snapshot each caster material's gen so the next caster-set change can tell whether a CASTER material was\n // rebuilt (→ full rebuild) or only the set changed (→ incremental, keeping unchanged casters' packets).\n const casterMatGens = new Map<Material, number>();\n const casterMaxCascades = new Map<Mesh, number | undefined>();\n for (const m of casterMeshes) {\n casterMaxCascades.set(m, m._shadowMaxCascade);\n if (m.material) {\n casterMatGens.set(m.material, effectiveCasterGen(m.material));\n }\n }\n return {\n _task: compositeTask,\n _tasks: tasks,\n _cameras: cameras,\n _scene: scene,\n _cameraVersion: 0,\n _lastCasterVersion: -1,\n _lastLightVersion: -1,\n _lastCamVersion: -1,\n _uboData: new Float32Array(80),\n _casterMeshes: casterMeshes,\n _renderableVersion: scene._renderableVersion,\n _materialEpoch: scene._materialEpoch,\n _materialViews: materialViews,\n _casterMatGens: casterMatGens,\n _casterMaxCascades: casterMaxCascades,\n };\n}\n\n/** Render every cascade layer for this frame, recomputing splits/matrices from the active camera. */\nexport function renderCsmShadowMap(engine: EngineContext, sg: ShadowGenerator, state: CsmTaskState, cfg: CsmConfig): number {\n const casterMeshes = state._casterMeshes;\n const camera = state._scene.camera;\n if (!camera) {\n return 0;\n }\n const casterVersion = casterVersionSum(casterMeshes);\n const lightVersion = sg._light.worldMatrixVersion;\n const camVersion = camera.worldMatrixVersion;\n if (!cfg._forceRefreshEveryFrame && casterVersion === state._lastCasterVersion && lightVersion === state._lastLightVersion && camVersion === state._lastCamVersion) {\n return 0;\n }\n\n const cascades = _computeCsmCascades(engine, camera, sg._light as DirectionalLight, cfg, casterMeshes);\n\n _writeCsmUbo(state._uboData, cascades, cfg);\n sg._version++;\n engine._device.queue.writeBuffer(sg._shadowUBO, 0, state._uboData as Float32Array<ArrayBuffer>);\n\n // Notify custom receivers (e.g. a ShaderMaterial that mirrors the cascade transforms into\n // its own uniforms) with this frame's freshly-computed receiver UBO. This fires inside the\n // shadow task — after the transforms are finalized but before the shadow map and main pass\n // render — so such receivers stay in lock-step with the depth map. Syncing from a\n // `onBeforeRender` callback instead would read the previous frame's transforms (a one-frame\n // lag that makes those shadows swim while the camera moves). The built-in standard/PBR/node\n // receivers don't need this: they bind `sg._shadowUBO` directly.\n const receiverCbs = sg._onReceiverData;\n if (receiverCbs) {\n for (let i = 0; i < receiverCbs.length; i++) {\n receiverCbs[i]!(state._uboData);\n }\n }\n\n state._cameraVersion++;\n for (let i = 0; i < cascades._transforms.length; i++) {\n const cam = state._cameras[i]!;\n cam.fov = 1;\n const clipBias = cfg._worldSpaceBias === null ? cfg._bias * 0.5 : csmWorldBiasClipOffset(cfg._worldSpaceBias, cascades._near[i]!, cascades._far[i]!);\n updateShadowCameraBase(cam, state._cameraVersion, cascades._near[i]!, cascades._far[i]!, cascades._views[i]!, _biasViewProjection(cascades._biased[i]!, clipBias));\n }\n\n state._lastCasterVersion = casterVersion;\n state._lastLightVersion = lightVersion;\n state._lastCamVersion = camVersion;\n return state._task.execute?.() ?? 0;\n}\n\n// ─── CSM math (isolated to this module) ─────────────────────────────\n\ninterface CsmCascades {\n /** @internal Unbiased receiver transform per cascade (col-major). */\n _transforms: Float32Array[];\n /** @internal Same as _transforms, used for the caster camera before bias. */\n _biased: Float32Array[];\n /** @internal Cascade light view matrix per cascade (col-major). */\n _views: Float32Array[];\n /** @internal Ortho near per cascade. */\n _near: number[];\n /** @internal Ortho far per cascade. */\n _far: number[];\n /** @internal Camera-view-space split distance per cascade. */\n _viewFrustumZ: number[];\n /** @internal Slice length per cascade. */\n _frustumLengths: number[];\n}\n\n/** Lite reverse-Z NDC frustum corners (near z=1, far z=0); xy each -1 or +1. */\nconst FRUSTUM_NDC: ReadonlyArray<readonly [number, number, number]> = [\n [-1, 1, 1],\n [1, 1, 1],\n [1, -1, 1],\n [-1, -1, 1],\n [-1, 1, 0],\n [1, 1, 0],\n [1, -1, 0],\n [-1, -1, 0],\n];\n\nfunction transformCoord(m: ArrayLike<number>, x: number, y: number, z: number): [number, number, number] {\n const X = m[0]! * x + m[4]! * y + m[8]! * z + m[12]!;\n const Y = m[1]! * x + m[5]! * y + m[9]! * z + m[13]!;\n const Z = m[2]! * x + m[6]! * y + m[10]! * z + m[14]!;\n const W = m[3]! * x + m[7]! * y + m[11]! * z + m[15]!;\n return [X / W, Y / W, Z / W];\n}\n\n/** Column-major OrthoOffCenterLH with half-z NDC (z: near→0, far→1). */\nfunction orthoOffCenterLH(l: number, r: number, b: number, t: number, n: number, f: number): Float32Array {\n const m = new Float32Array(16);\n m[0] = 2 / (r - l);\n m[5] = 2 / (t - b);\n m[10] = 1 / (f - n);\n m[12] = -(r + l) / (r - l);\n m[13] = -(t + b) / (t - b);\n m[14] = -n / (f - n);\n m[15] = 1;\n return m;\n}\n\nfunction _computeCsmCascades(engine: EngineContext, camera: Camera, light: DirectionalLight, cfg: CsmConfig, casterMeshes: readonly Mesh[]): CsmCascades {\n const near = camera.nearPlane;\n const far = camera.farPlane;\n const cameraRange = far - near;\n const shadowMaxZ = cfg._shadowMaxZ ?? far;\n const maxDistance = shadowMaxZ < far && shadowMaxZ >= near ? Math.min((shadowMaxZ - near) / (far - near), 1) : 1;\n const minDistance = 0;\n const minZ = near + minDistance * cameraRange;\n const maxZ = near + maxDistance * cameraRange;\n const range = maxZ - minZ;\n const ratio = maxZ / minZ;\n const n = cfg._numCascades;\n\n const breakDist: number[] = [];\n const viewFrustumZ: number[] = [];\n const frustumLengths: number[] = [];\n for (let i = 0; i < n; i++) {\n const p = (i + 1) / n;\n const log = minZ * ratio ** p;\n const uniform = minZ + range * p;\n const d = cfg._lambda * (log - uniform) + uniform;\n const prevBreak = i === 0 ? minDistance : breakDist[i - 1]!;\n breakDist[i] = (d - near) / cameraRange;\n viewFrustumZ[i] = d;\n frustumLengths[i] = (breakDist[i]! - prevBreak) * cameraRange;\n }\n\n // Light direction (normalized), avoiding a perfectly vertical degenerate case.\n let dx = light.direction.x;\n let dy = light.direction.y;\n let dz = light.direction.z;\n const dl = Math.hypot(dx, dy, dz) || 1;\n dx /= dl;\n dy /= dl;\n dz /= dl;\n if (Math.abs(dy) >= 1) {\n dz = 1e-13;\n }\n\n const aspect = engine.canvas.width / engine.canvas.height;\n const vp = getViewProjectionMatrix(camera, aspect) as unknown as ArrayLike<number>;\n const inv = mat4Invert(vp as never);\n const invViewProj: ArrayLike<number> = (inv as unknown as ArrayLike<number>) ?? vp;\n\n const aabb = _castersWorldAabb(casterMeshes);\n\n const transforms: Float32Array[] = [];\n const views: Float32Array[] = [];\n const nearOut: number[] = [];\n const farOut: number[] = [];\n\n for (let c = 0; c < n; c++) {\n const prevSplit = c === 0 ? 0 : breakDist[c - 1]!;\n const split = breakDist[c]!;\n\n // World-space frustum corners of this slice.\n const corners: [number, number, number][] = [];\n for (let k = 0; k < 8; k++) {\n const ndc = FRUSTUM_NDC[k]!;\n corners.push(transformCoord(invViewProj, ndc[0], ndc[1], ndc[2]));\n }\n for (let k = 0; k < 4; k++) {\n const nearC = corners[k]!;\n const farC = corners[k + 4]!;\n const rx = farC[0] - nearC[0];\n const ry = farC[1] - nearC[1];\n const rz = farC[2] - nearC[2];\n corners[k + 4] = [nearC[0] + rx * split, nearC[1] + ry * split, nearC[2] + rz * split];\n corners[k] = [nearC[0] + rx * prevSplit, nearC[1] + ry * prevSplit, nearC[2] + rz * prevSplit];\n }\n\n // Centroid.\n let cx = 0,\n cy = 0,\n cz = 0;\n for (const p of corners) {\n cx += p[0];\n cy += p[1];\n cz += p[2];\n }\n cx /= 8;\n cy /= 8;\n cz /= 8;\n\n let minX: number, maxX: number, minY: number, maxY: number, minEz: number, maxEz: number;\n let stableRadius = 0;\n if (cfg._stabilizeCascades) {\n let radius = 0;\n for (const p of corners) {\n radius = Math.max(radius, Math.hypot(p[0] - cx, p[1] - cy, p[2] - cz));\n }\n radius = Math.ceil(radius * 16) / 16;\n stableRadius = radius;\n minX = minY = minEz = -radius;\n maxX = maxY = maxEz = radius;\n } else {\n // Temp light view centred on the centroid to fit a tight AABB.\n const tmpView = buildLightViewMatrix(dx, dy, dz, cx, cy, cz);\n minX = minY = minEz = Infinity;\n maxX = maxY = maxEz = -Infinity;\n for (const p of corners) {\n const lp = transformCoord(tmpView, p[0], p[1], p[2]);\n minX = Math.min(minX, lp[0]);\n maxX = Math.max(maxX, lp[0]);\n minY = Math.min(minY, lp[1]);\n maxY = Math.max(maxY, lp[1]);\n minEz = Math.min(minEz, lp[2]);\n maxEz = Math.max(maxEz, lp[2]);\n }\n }\n\n // Shadow camera sits behind the slice along the light direction.\n const eyeX = cx + dx * minEz;\n const eyeY = cy + dy * minEz;\n const eyeZ = cz + dz * minEz;\n const view = buildLightViewMatrix(dx, dy, dz, eyeX, eyeY, eyeZ);\n\n let viewMinZ = 0;\n let viewMaxZ = maxEz - minEz;\n\n // Tighten Z to the caster bounding box in cascade view space (depthClamp = false behaviour:\n // keep all casters inside the frustum so no GPU depth-clip feature is required).\n if (aabb) {\n let cMinZ = Infinity;\n let cMaxZ = -Infinity;\n for (let k = 0; k < 8; k++) {\n const wx = k & 1 ? aabb._max[0] : aabb._min[0];\n const wy = k & 2 ? aabb._max[1] : aabb._min[1];\n const wz = k & 4 ? aabb._max[2] : aabb._min[2];\n const lz = view[2]! * wx + view[6]! * wy + view[10]! * wz + view[14]!;\n cMinZ = Math.min(cMinZ, lz);\n cMaxZ = Math.max(cMaxZ, lz);\n }\n if (cMinZ <= viewMaxZ) {\n viewMinZ = Math.min(viewMinZ, cMinZ);\n viewMaxZ = Math.min(viewMaxZ, cMaxZ);\n }\n\n // Z is intentionally NOT quantized here. The caster-AABB fit (cMinZ/cMaxZ) is C0-continuous in the\n // light direction — each is a min/max of linear functions of the light vector, so it has kinks but no\n // jumps — meaning the near/far drift SMOOTHLY as the light rotates. A constant NDC depth bias maps to a\n // WORLD bias of bias·(far−near), and the stored depths are likewise normalised by (far−near); the old\n // `zq = max(0.5, radius/128)` floor/ceil snapped that range to a grid, so both the effective bias AND\n // the stored depth STEPPED at each quantum boundary as the light direction changed — appearing as\n // self-shadow acne that VIBRATES. Removing the quantize makes those steps a sub-millimetre, imperceptible\n // drift, and still covers the moving-caster case it was added for (the range drifts, it never pops).\n }\n\n // The caster matrix adds the world-space bias toward clip Z=1. Reserve the same distance at the far plane\n // so geometry on the tightly fitted caster bound remains inside the clip volume after that offset.\n if (cfg._worldSpaceBias) {\n viewMaxZ += cfg._worldSpaceBias;\n }\n\n const proj0 = orthoOffCenterLH(minX, maxX, minY, maxY, viewMinZ, viewMaxZ);\n let transform = multiply4x4(proj0, view);\n\n // Texel-snap: lock the shadow grid to world space so it does not crawl as the camera moves, by rounding a\n // fixed WORLD anchor onto the shadow-map texel grid. BJS anchors the WORLD ORIGIN. With a STILL camera and\n // a slowly ROTATING light that is the cause of the visible \"vibration\": the eye is recentred on the cascade\n // centre every frame, so the snap residual is the anchor's offset from the centre measured along the light\n // axes — and the world origin's offset from the centre is large (≈ the cascade's distance from origin), so\n // its projection sweeps many texels per degree of light rotation and Math.round trips a full-texel correction\n // again and again → the whole map pops/boils. We instead anchor the world-grid point NEAREST the cascade\n // centre (cell = one texel in world units): it is still a fixed world point (a translating camera only ever\n // sees whole-cell anchor switches, ≤ a sub-texel grid wiggle that PCF hides), but its offset from the centre\n // is < 1 texel, so a full light rotation sweeps it < 1 texel → effectively no rotation pop. Non-stabilized\n // path keeps the origin anchor (no stableRadius → no texel-world size to build the world grid from).\n let aClipX = transform[12]!;\n let aClipY = transform[13]!;\n if (cfg._stabilizeCascades && stableRadius > 0) {\n const texelWorld = (2 * stableRadius) / cfg._mapSize;\n // Align the world anchor grid to the LIGHT's right/up axes (R,U), not world x/y/z. A translating camera then\n // switches cells by exactly ONE texel along R/U -> an integer texel shift in clip space (no crawl). The old\n // world-axis grid jumped texelWorld along a world axis, which projects to a NON-integer texel count (R,U are\n // not the world axes) -> the sub-texel wiggle that crawled during translation. R,U = view rows 0,1.\n const rX = view[0]!,\n rY = view[4]!,\n rZ = view[8]!;\n const uX = view[1]!,\n uY = view[5]!,\n uZ = view[9]!;\n // Project the centre onto R,U, round to the texel grid, rebuild the world anchor in the R-U plane (the\n // light-dir component only affects clip Z, so dropping it leaves clip X/Y, which depend on R,U, exact).\n const sr = Math.round((rX * cx + rY * cy + rZ * cz) / texelWorld) * texelWorld;\n const tr = Math.round((uX * cx + uY * cy + uZ * cz) / texelWorld) * texelWorld;\n const ax = sr * rX + tr * uX;\n const ay = sr * rY + tr * uY;\n const az = sr * rZ + tr * uZ;\n aClipX = transform[0]! * ax + transform[4]! * ay + transform[8]! * az + transform[12]!;\n aClipY = transform[1]! * ax + transform[5]! * ay + transform[9]! * az + transform[13]!;\n }\n const ox = aClipX * (cfg._mapSize / 2);\n const oy = aClipY * (cfg._mapSize / 2);\n const offX = (Math.round(ox) - ox) * (2 / cfg._mapSize);\n const offY = (Math.round(oy) - oy) * (2 / cfg._mapSize);\n const snap = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, offX, offY, 0, 1]);\n const proj = multiply4x4(snap, proj0);\n transform = multiply4x4(proj, view);\n\n transforms.push(transform);\n views.push(view);\n nearOut.push(viewMinZ);\n farOut.push(viewMaxZ);\n }\n\n return {\n _transforms: transforms,\n _biased: transforms.map((t) => new Float32Array(t)),\n _views: views,\n _near: nearOut,\n _far: farOut,\n _viewFrustumZ: viewFrustumZ,\n _frustumLengths: frustumLengths,\n };\n}\n\nfunction _castersWorldAabb(casterMeshes: readonly Mesh[]): { _min: [number, number, number]; _max: [number, number, number] } | null {\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity,\n maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (const mesh of casterMeshes) {\n // Thin-instanced casters are drawn at `finalWorld = mesh.world * instanceMatrix` (see\n // thin-instance-fragment.ts), so a single `mesh.worldMatrix × boundMin/Max` box ignores the per-instance\n // spread entirely — one prototype-sized box wrecks the cascade Z-fit (an off-world herd collapsed every\n // shadow). Bound the caster by the union of every drawn instance instead, using the SAME composition the\n // shader uses.\n const ti = mesh.thinInstances;\n if (ti && ti.count > 0 && ti.matrices) {\n const a = _thinInstanceWorldAabb(mesh, ti);\n if (a) {\n minX = Math.min(minX, a._min[0]);\n maxX = Math.max(maxX, a._max[0]);\n minY = Math.min(minY, a._min[1]);\n maxY = Math.max(maxY, a._max[1]);\n minZ = Math.min(minZ, a._min[2]);\n maxZ = Math.max(maxZ, a._max[2]);\n }\n continue;\n }\n const world = mesh.worldMatrix;\n const bmin = mesh.boundMin ?? [-0.5, -0.5, -0.5];\n const bmax = mesh.boundMax ?? [0.5, 0.5, 0.5];\n for (let k = 0; k < 8; k++) {\n const lx = k & 1 ? bmax[0]! : bmin[0]!;\n const ly = k & 2 ? bmax[1]! : bmin[1]!;\n const lz = k & 4 ? bmax[2]! : bmin[2]!;\n const wx = world[0]! * lx + world[4]! * ly + world[8]! * lz + world[12]!;\n const wy = world[1]! * lx + world[5]! * ly + world[9]! * lz + world[13]!;\n const wz = world[2]! * lx + world[6]! * ly + world[10]! * lz + world[14]!;\n minX = Math.min(minX, wx);\n maxX = Math.max(maxX, wx);\n minY = Math.min(minY, wy);\n maxY = Math.max(maxY, wy);\n minZ = Math.min(minZ, wz);\n maxZ = Math.max(maxZ, wz);\n }\n }\n if (!Number.isFinite(minX)) {\n return null;\n }\n return { _min: [minX, minY, minZ], _max: [maxX, maxY, maxZ] };\n}\n\ninterface ThinCasterAabb {\n _min: [number, number, number];\n _max: [number, number, number];\n}\n\n/** Per-mesh cache of a thin-instanced caster's world AABB. It keys on instance data, prototype\n * transform, and the shared non-transform caster epoch.\n * Lazily allocated so this module keeps zero import-time side effects and stays tree-shakable. */\nlet _thinCasterAabbCache: WeakMap<Mesh, { _version: number; _worldVersion: number; _aabb: ThinCasterAabb | null }> | null = null;\nfunction _getThinCasterAabbCache(): WeakMap<Mesh, { _version: number; _worldVersion: number; _aabb: ThinCasterAabb | null }> {\n if (!_thinCasterAabbCache) {\n _thinCasterAabbCache = new WeakMap();\n }\n return _thinCasterAabbCache;\n}\n\n/** World AABB of a thin-instanced caster. Matches the shader's `finalWorld = mesh.world * instanceMatrix`\n * exactly: each local bound corner is transformed by the per-instance matrix, then by the prototype mesh\n * world matrix. Parked/degenerate instances (zero linear part — drawn as zero-area, used to hide an unused\n * tail) are skipped so a tail parked far off-world can't balloon the box. */\nfunction _thinInstanceWorldAabb(mesh: Mesh, ti: NonNullable<Mesh[\"thinInstances\"]>): ThinCasterAabb | null {\n const cache = _getThinCasterAabbCache();\n const worldVersion = mesh.worldMatrixVersion;\n const cached = cache.get(mesh);\n if (cached && cached._version === ti._version && cached._worldVersion === worldVersion) {\n return cached._aabb;\n }\n // Hoist the prototype world matrix once (worldMatrix is a getter) — it is constant across all instances.\n const world = mesh.worldMatrix;\n const bmin = mesh.boundMin ?? [-0.5, -0.5, -0.5];\n const bmax = mesh.boundMax ?? [0.5, 0.5, 0.5];\n const mats = ti.matrices;\n const count = Math.min(ti.count, (mats.length / 16) | 0);\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity,\n maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n for (let i = 0; i < count; i++) {\n const o = i * 16;\n // Skip parked instances (zero 3×3 linear part → zero-area triangles that rasterize to nothing).\n const lin =\n Math.abs(mats[o]!) +\n Math.abs(mats[o + 1]!) +\n Math.abs(mats[o + 2]!) +\n Math.abs(mats[o + 4]!) +\n Math.abs(mats[o + 5]!) +\n Math.abs(mats[o + 6]!) +\n Math.abs(mats[o + 8]!) +\n Math.abs(mats[o + 9]!) +\n Math.abs(mats[o + 10]!);\n if (lin < 1e-9) {\n continue;\n }\n for (let k = 0; k < 8; k++) {\n const lx = k & 1 ? bmax[0]! : bmin[0]!;\n const ly = k & 2 ? bmax[1]! : bmin[1]!;\n const lz = k & 4 ? bmax[2]! : bmin[2]!;\n // 1) instance-local: ip = instanceMatrix * localCorner\n const ix = mats[o]! * lx + mats[o + 4]! * ly + mats[o + 8]! * lz + mats[o + 12]!;\n const iy = mats[o + 1]! * lx + mats[o + 5]! * ly + mats[o + 9]! * lz + mats[o + 13]!;\n const iz = mats[o + 2]! * lx + mats[o + 6]! * ly + mats[o + 10]! * lz + mats[o + 14]!;\n // 2) world: wp = mesh.world * ip (matches finalWorld = mesh.world * instanceMatrix)\n const wx = world[0]! * ix + world[4]! * iy + world[8]! * iz + world[12]!;\n const wy = world[1]! * ix + world[5]! * iy + world[9]! * iz + world[13]!;\n const wz = world[2]! * ix + world[6]! * iy + world[10]! * iz + world[14]!;\n if (wx < minX) {\n minX = wx;\n }\n if (wx > maxX) {\n maxX = wx;\n }\n if (wy < minY) {\n minY = wy;\n }\n if (wy > maxY) {\n maxY = wy;\n }\n if (wz < minZ) {\n minZ = wz;\n }\n if (wz > maxZ) {\n maxZ = wz;\n }\n }\n }\n const aabb: ThinCasterAabb | null = Number.isFinite(minX) ? { _min: [minX, minY, minZ], _max: [maxX, maxY, maxZ] } : null;\n cache.set(mesh, { _version: ti._version, _worldVersion: worldVersion, _aabb: aabb });\n return aabb;\n}\n\nfunction _writeCsmUbo(out: Float32Array, cascades: CsmCascades, cfg: CsmConfig): void {\n out.fill(0);\n const n = cascades._transforms.length;\n for (let i = 0; i < n; i++) {\n out.set(cascades._transforms[i]!, i * 16);\n }\n for (let i = 0; i < n; i++) {\n out[64 + i] = cascades._viewFrustumZ[i]!;\n out[68 + i] = cascades._frustumLengths[i]!;\n }\n out[72] = cfg._darkness;\n out[73] = cfg._mapSize;\n out[74] = 1 / cfg._mapSize;\n out[75] = cfg._frustumEdgeFalloff;\n out[76] = n;\n out[77] = cfg._cascadeBlendPercentage === 0 ? 10000 : 1 / cfg._cascadeBlendPercentage;\n}\n\n/** @internal Convert a physical caster offset to the clip-space Z offset for one orthographic cascade. */\nexport function csmWorldBiasClipOffset(worldSpaceBias: number, near: number, far: number): number {\n const range = far - near;\n if (!Number.isFinite(worldSpaceBias) || worldSpaceBias <= 0 || !Number.isFinite(range) || range <= 0) {\n return 0;\n }\n return worldSpaceBias / range;\n}\n\nfunction _biasViewProjection(viewProj: Float32Array, clipOffset: number): Float32Array {\n const biased = new Float32Array(viewProj);\n for (let col = 0; col < 4; col++) {\n const z = 2 + col * 4;\n const w = 3 + col * 4;\n biased[z] = biased[z]! + clipOffset * biased[w]!;\n }\n return biased;\n}\n"],"names":["tasks"],"mappings":";;;;;;;AA4BA,SAAS,mBAAmB,QAAA,EAA4B;AACpD,EAAA,MAAM,GAAA,GAAM,SAAS,qBAAA,IAAyB,QAAA;AAC9C,EAAA,OAAQ,IAA6B,OAAA,IAAW,CAAA;AACpD;AAsEO,MAAM,yBAAA,GAA4B;AAGlC,SAAS,yBACZ,MAAA,EACA,KAAA,EACA,EAAA,EACA,GAAA,EACA,cACA,aAAA,EACY;AACZ,EAAA,MAAM,QAAA,GAAW,aAAA;AACjB,EAAA,IAAI,QAAA,EAAU;AACV,IAAA,IAAI,SAAS,aAAA,KAAkB,YAAA,IAAgB,QAAA,CAAS,kBAAA,KAAuB,MAAM,kBAAA,EAAoB;AACrG,MAAA,OAAO,QAAA;AAAA,IACX;AAUA,IAAA,IAAI,SAAS,aAAA,KAAkB,YAAA,IAAgB,QAAA,CAAS,cAAA,KAAmB,MAAM,cAAA,EAAgB;AAC7F,MAAA,QAAA,CAAS,qBAAqB,KAAA,CAAM,kBAAA;AACpC,MAAA,OAAO,QAAA;AAAA,IACX;AAWA,IAAA,IAAI,gBAAA,GAAmB,KAAA;AACvB,IAAA,KAAA,MAAW,KAAK,YAAA,EAAc;AAC1B,MAAA,MAAM,MAAM,CAAA,CAAE,QAAA;AACd,MAAA,IAAI,CAAC,GAAA,EAAK;AACN,QAAA;AAAA,MACJ;AACA,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,cAAA,CAAe,GAAA,CAAI,GAAG,CAAA;AAC9C,MAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,kBAAA,CAAmB,GAAG,CAAA,EAAG;AAC5D,QAAA,gBAAA,GAAmB,IAAA;AACnB,QAAA;AAAA,MACJ;AAAA,IACJ;AACA,IAAA,IAAI,CAAC,gBAAA,EAAkB;AACnB,MAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,YAAY,CAAA;AACpC,MAAA,MAAM,QAAQ,QAAA,CAAS,cAAA;AACvB,MAAA,MAAM,OAAO,QAAA,CAAS,cAAA;AACtB,MAAA,MAAM,OAAO,QAAA,CAAS,kBAAA;AACtB,MAAA,MAAMA,SAAQ,QAAA,CAAS,MAAA;AACvB,MAAA,KAAA,MAAW,CAAA,IAAK,SAAS,aAAA,EAAe;AACpC,QAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,IAAK,EAAE,iBAAA,KAAsB,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AACxD,UAAA,IAAA,CAAK,OAAO,CAAC,CAAA;AACb,UAAA,KAAA,MAAW,KAAKA,MAAAA,EAAO;AACnB,YAAA,kBAAA,CAAmB,GAAG,CAAC,CAAA;AAAA,UAC3B;AAAA,QACJ;AAAA,MACJ;AACA,MAAA,KAAA,MAAW,KAAK,YAAA,EAAc;AAC1B,QAAA,MAAM,aAAa,CAAA,CAAE,iBAAA;AACrB,QAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,IAAK,EAAE,QAAA,EAAU;AAC5B,UAAA,MAAM,IAAA,GAAO,cAAA,CAAe,CAAA,CAAE,QAAA,EAAU,KAAK,CAAA;AAC7C,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAIA,MAAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACnC,YAAA,IAAI,CAAA,KAAM,cAAc,CAAA,CAAA,EAAI;AACxB,cAAAA,MAAAA,CAAM,CAAC,CAAA,CAAG,OAAA,CAAQ,GAAG,EAAE,QAAA,EAAU,MAAM,CAAA;AAAA,YAC3C;AAAA,UACJ;AACA,UAAA,IAAA,CAAK,IAAI,CAAA,CAAE,QAAA,EAAU,kBAAA,CAAmB,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,QACvD;AACA,QAAA,IAAA,CAAK,GAAA,CAAI,GAAG,UAAU,CAAA;AAAA,MAC1B;AAEA,MAAA,KAAA,MAAW,KAAKA,MAAAA,EAAO;AACnB,QAAA,CAAA,CAAE,YAAA,GAAe,EAAA;AAAA,MACrB;AACA,MAAA,QAAA,CAAS,aAAA,GAAgB,YAAA;AACzB,MAAA,QAAA,CAAS,qBAAqB,KAAA,CAAM,kBAAA;AACpC,MAAA,OAAO,QAAA;AAAA,IACX;AAQA,IAAA,kBAAA,CAAmB,MAAA,EAAQ,QAAA,CAAS,KAAA,CAAM,OAAO,CAAA;AAAA,EACrD;AAEA,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAA4B;AACtD,EAAA,MAAM,IAAI,GAAA,CAAI,YAAA;AACd,EAAA,MAAM,QAAsB,EAAC;AAC7B,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,IAAA,MAAM,SAAA,GAAY,EAAA,CAAG,aAAA,CAAc,UAAA,CAAW,EAAE,SAAA,EAAW,IAAA,EAAM,cAAA,EAAgB,CAAA,EAAG,eAAA,EAAiB,CAAA,EAAG,CAAA;AACxG,IAAA,MAAM,EAAA,GAAmB;AAAA,MACrB,WAAA,EAAa;AAAA,QACT,MAAM,EAAE,KAAA,EAAO,IAAI,QAAA,EAAU,MAAA,EAAQ,IAAI,QAAA,EAAS;AAAA,QAClD,OAAA,EAAS,cAAA;AAAA,QACT,gBAAA,EAAkB,CAAA;AAAA,QAClB,aAAA,EAAe,YAAA;AAAA,QACf,OAAA,EAAS;AAAA,OACb;AAAA,MACA,aAAA,EAAe,IAAA;AAAA,MACf,UAAA,EAAY,IAAA;AAAA,MACZ,eAAe,EAAA,CAAG,aAAA;AAAA,MAClB,UAAA,EAAY,SAAA;AAAA,MACZ,QAAQ,GAAA,CAAI,QAAA;AAAA,MACZ,SAAS,GAAA,CAAI,QAAA;AAAA,MACb,MAAA,EAAQ,IAAA;AAAA,MACR,iBAAA,EAAmB;AAAA;AAAA,KACvB;AACA,IAAA,MAAM,MAAA,GAAS,mBAAmB,EAAE,CAAA;AACpC,IAAA,MAAM,OAAO,gBAAA,CAAiB,EAAE,IAAA,EAAM,CAAA,GAAA,EAAM,CAAC,CAAA,CAAA,EAAI,EAAA,EAAI,GAAA,EAAK,IAAA,EAAM,KAAK,MAAA,EAAQ,oBAAA,EAAsB,IAAA,EAAK,EAAG,QAAQ,KAAK,CAAA;AACxH,IAAA,KAAA,MAAW,QAAQ,YAAA,EAAc;AAC7B,MAAA,MAAM,WAAW,IAAA,CAAK,QAAA;AAGtB,MAAA,IAAI,QAAA,IAAY,CAAA,KAAM,IAAA,CAAK,iBAAA,IAAqB,CAAA,CAAA,EAAI;AAChD,QAAA,IAAA,CAAK,OAAA,CAAQ,MAAM,EAAE,QAAA,EAAU,eAAe,QAAA,EAAU,aAAa,GAAG,CAAA;AAAA,MAC5E;AAAA,IACJ;AACA,IAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,IAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,EACvB;AAEA,EAAA,MAAM,aAAA,GAAgB;AAAA,IAClB,MAAA,GAAe;AACX,MAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACnB,QAAA,CAAA,CAAE,MAAA,EAAO;AAAA,MACb;AAAA,IACJ,CAAA;AAAA,IACA,OAAA,GAAkB;AACd,MAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,MAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACnB,QAAA,KAAA,IAAS,CAAA,CAAE,WAAU,IAAK,CAAA;AAAA,MAC9B;AACA,MAAA,OAAO,KAAA;AAAA,IACX,CAAA;AAAA,IACA,OAAA,GAAgB;AACZ,MAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACnB,QAAA,CAAA,CAAE,OAAA,EAAQ;AAAA,MACd;AAAA,IACJ;AAAA,GACJ;AAIA,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAAsB;AAChD,EAAA,MAAM,iBAAA,uBAAwB,GAAA,EAA8B;AAC5D,EAAA,KAAA,MAAW,KAAK,YAAA,EAAc;AAC1B,IAAA,iBAAA,CAAkB,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,iBAAiB,CAAA;AAC5C,IAAA,IAAI,EAAE,QAAA,EAAU;AACZ,MAAA,aAAA,CAAc,IAAI,CAAA,CAAE,QAAA,EAAU,kBAAA,CAAmB,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,IAChE;AAAA,EACJ;AACA,EAAA,OAAO;AAAA,IACH,KAAA,EAAO,aAAA;AAAA,IACP,MAAA,EAAQ,KAAA;AAAA,IACR,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,KAAA;AAAA,IACR,cAAA,EAAgB,CAAA;AAAA,IAChB,kBAAA,EAAoB,EAAA;AAAA,IACpB,iBAAA,EAAmB,EAAA;AAAA,IACnB,eAAA,EAAiB,EAAA;AAAA,IACjB,QAAA,EAAU,IAAI,YAAA,CAAa,EAAE,CAAA;AAAA,IAC7B,aAAA,EAAe,YAAA;AAAA,IACf,oBAAoB,KAAA,CAAM,kBAAA;AAAA,IAC1B,gBAAgB,KAAA,CAAM,cAAA;AAAA,IACtB,cAAA,EAAgB,aAAA;AAAA,IAChB,cAAA,EAAgB,aAAA;AAAA,IAChB,kBAAA,EAAoB;AAAA,GACxB;AACJ;AAGO,SAAS,kBAAA,CAAmB,MAAA,EAAuB,EAAA,EAAqB,KAAA,EAAqB,GAAA,EAAwB;AACxH,EAAA,MAAM,eAAe,KAAA,CAAM,aAAA;AAC3B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA;AAC5B,EAAA,IAAI,CAAC,MAAA,EAAQ;AACT,IAAA,OAAO,CAAA;AAAA,EACX;AACA,EAAA,MAAM,aAAA,GAAgB,iBAAiB,YAAY,CAAA;AACnD,EAAA,MAAM,YAAA,GAAe,GAAG,MAAA,CAAO,kBAAA;AAC/B,EAAA,MAAM,aAAa,MAAA,CAAO,kBAAA;AAC1B,EAAA,IAAI,CAAC,GAAA,CAAI,uBAAA,IAA2B,aAAA,KAAkB,KAAA,CAAM,kBAAA,IAAsB,YAAA,KAAiB,KAAA,CAAM,iBAAA,IAAqB,UAAA,KAAe,KAAA,CAAM,eAAA,EAAiB;AAChK,IAAA,OAAO,CAAA;AAAA,EACX;AAEA,EAAA,MAAM,WAAW,mBAAA,CAAoB,MAAA,EAAQ,QAAQ,EAAA,CAAG,MAAA,EAA4B,KAAK,YAAY,CAAA;AAErG,EAAA,YAAA,CAAa,KAAA,CAAM,QAAA,EAAU,QAAA,EAAU,GAAG,CAAA;AAC1C,EAAA,EAAA,CAAG,QAAA,EAAA;AACH,EAAA,MAAA,CAAO,QAAQ,KAAA,CAAM,WAAA,CAAY,GAAG,UAAA,EAAY,CAAA,EAAG,MAAM,QAAqC,CAAA;AAS9F,EAAA,MAAM,cAAc,EAAA,CAAG,eAAA;AACvB,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,CAAY,QAAQ,CAAA,EAAA,EAAK;AACzC,MAAA,WAAA,CAAY,CAAC,CAAA,CAAG,KAAA,CAAM,QAAQ,CAAA;AAAA,IAClC;AAAA,EACJ;AAEA,EAAA,KAAA,CAAM,cAAA,EAAA;AACN,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,WAAA,CAAY,QAAQ,CAAA,EAAA,EAAK;AAClD,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,QAAA,CAAS,CAAC,CAAA;AAC5B,IAAA,GAAA,CAAI,GAAA,GAAM,CAAA;AACV,IAAA,MAAM,WAAW,GAAA,CAAI,eAAA,KAAoB,IAAA,GAAO,GAAA,CAAI,QAAQ,GAAA,GAAM,sBAAA,CAAuB,GAAA,CAAI,eAAA,EAAiB,SAAS,KAAA,CAAM,CAAC,GAAI,QAAA,CAAS,IAAA,CAAK,CAAC,CAAE,CAAA;AACnJ,IAAA,sBAAA,CAAuB,GAAA,EAAK,MAAM,cAAA,EAAgB,QAAA,CAAS,MAAM,CAAC,CAAA,EAAI,SAAS,IAAA,CAAK,CAAC,GAAI,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,EAAI,mBAAA,CAAoB,SAAS,OAAA,CAAQ,CAAC,CAAA,EAAI,QAAQ,CAAC,CAAA;AAAA,EACrK;AAEA,EAAA,KAAA,CAAM,kBAAA,GAAqB,aAAA;AAC3B,EAAA,KAAA,CAAM,iBAAA,GAAoB,YAAA;AAC1B,EAAA,KAAA,CAAM,eAAA,GAAkB,UAAA;AACxB,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,OAAA,IAAU,IAAK,CAAA;AACtC;AAsBA,MAAM,WAAA,GAAgE;AAAA,EAClE,CAAC,EAAA,EAAI,CAAA,EAAG,CAAC,CAAA;AAAA,EACT,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAAA,EACR,CAAC,CAAA,EAAG,EAAA,EAAI,CAAC,CAAA;AAAA,EACT,CAAC,EAAA,EAAI,EAAA,EAAI,CAAC,CAAA;AAAA,EACV,CAAC,EAAA,EAAI,CAAA,EAAG,CAAC,CAAA;AAAA,EACT,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAAA,EACR,CAAC,CAAA,EAAG,EAAA,EAAI,CAAC,CAAA;AAAA,EACT,CAAC,EAAA,EAAI,EAAA,EAAI,CAAC;AACd,CAAA;AAEA,SAAS,cAAA,CAAe,CAAA,EAAsB,CAAA,EAAW,CAAA,EAAW,CAAA,EAAqC;AACrG,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,EAAE,EAAE,CAAA;AAClD,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,EAAE,EAAE,CAAA;AAClD,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,CAAA,CAAE,EAAE,CAAA,GAAK,CAAA,GAAI,EAAE,EAAE,CAAA;AACnD,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,GAAI,CAAA,CAAE,EAAE,CAAA,GAAK,CAAA,GAAI,EAAE,EAAE,CAAA;AACnD,EAAA,OAAO,CAAC,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,IAAI,CAAC,CAAA;AAC/B;AAGA,SAAS,iBAAiB,CAAA,EAAW,CAAA,EAAW,CAAA,EAAW,CAAA,EAAW,GAAW,CAAA,EAAyB;AACtG,EAAA,MAAM,CAAA,GAAI,IAAI,YAAA,CAAa,EAAE,CAAA;AAC7B,EAAA,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,CAAA,CAAA;AAChB,EAAA,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,CAAA,CAAA;AAChB,EAAA,CAAA,CAAE,EAAE,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,CAAA,CAAA;AACjB,EAAA,CAAA,CAAE,EAAE,CAAA,GAAI,EAAE,CAAA,GAAI,MAAM,CAAA,GAAI,CAAA,CAAA;AACxB,EAAA,CAAA,CAAE,EAAE,CAAA,GAAI,EAAE,CAAA,GAAI,MAAM,CAAA,GAAI,CAAA,CAAA;AACxB,EAAA,CAAA,CAAE,EAAE,CAAA,GAAI,CAAC,CAAA,IAAK,CAAA,GAAI,CAAA,CAAA;AAClB,EAAA,CAAA,CAAE,EAAE,CAAA,GAAI,CAAA;AACR,EAAA,OAAO,CAAA;AACX;AAEA,SAAS,mBAAA,CAAoB,MAAA,EAAuB,MAAA,EAAgB,KAAA,EAAyB,KAAgB,YAAA,EAA4C;AACrJ,EAAA,MAAM,OAAO,MAAA,CAAO,SAAA;AACpB,EAAA,MAAM,MAAM,MAAA,CAAO,QAAA;AACnB,EAAA,MAAM,cAAc,GAAA,GAAM,IAAA;AAC1B,EAAA,MAAM,UAAA,GAAa,IAAI,WAAA,IAAe,GAAA;AACtC,EAAA,MAAM,WAAA,GAAc,UAAA,GAAa,GAAA,IAAO,UAAA,IAAc,IAAA,GAAO,IAAA,CAAK,GAAA,CAAA,CAAK,UAAA,GAAa,IAAA,KAAS,GAAA,GAAM,IAAA,CAAA,EAAO,CAAC,CAAA,GAAI,CAAA;AAC/G,EAAA,MAAM,WAAA,GAAc,CAAA;AACpB,EAAA,MAAM,IAAA,GAAO,OAAO,WAAA,GAAc,WAAA;AAClC,EAAA,MAAM,IAAA,GAAO,OAAO,WAAA,GAAc,WAAA;AAClC,EAAA,MAAM,QAAQ,IAAA,GAAO,IAAA;AACrB,EAAA,MAAM,QAAQ,IAAA,GAAO,IAAA;AACrB,EAAA,MAAM,IAAI,GAAA,CAAI,YAAA;AAEd,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,MAAM,eAAyB,EAAC;AAChC,EAAA,MAAM,iBAA2B,EAAC;AAClC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,IAAA,MAAM,CAAA,GAAA,CAAK,IAAI,CAAA,IAAK,CAAA;AACpB,IAAA,MAAM,GAAA,GAAM,OAAO,KAAA,IAAS,CAAA;AAC5B,IAAA,MAAM,OAAA,GAAU,OAAO,KAAA,GAAQ,CAAA;AAC/B,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,OAAA,IAAW,GAAA,GAAM,OAAA,CAAA,GAAW,OAAA;AAC1C,IAAA,MAAM,YAAY,CAAA,KAAM,CAAA,GAAI,WAAA,GAAc,SAAA,CAAU,IAAI,CAAC,CAAA;AACzD,IAAA,SAAA,CAAU,CAAC,CAAA,GAAA,CAAK,CAAA,GAAI,IAAA,IAAQ,WAAA;AAC5B,IAAA,YAAA,CAAa,CAAC,CAAA,GAAI,CAAA;AAClB,IAAA,cAAA,CAAe,CAAC,CAAA,GAAA,CAAK,SAAA,CAAU,CAAC,IAAK,SAAA,IAAa,WAAA;AAAA,EACtD;AAGA,EAAA,IAAI,EAAA,GAAK,MAAM,SAAA,CAAU,CAAA;AACzB,EAAA,IAAI,EAAA,GAAK,MAAM,SAAA,CAAU,CAAA;AACzB,EAAA,IAAI,EAAA,GAAK,MAAM,SAAA,CAAU,CAAA;AACzB,EAAA,MAAM,KAAK,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,IAAK,CAAA;AACrC,EAAA,EAAA,IAAM,EAAA;AACN,EAAA,EAAA,IAAM,EAAA;AACN,EAAA,EAAA,IAAM,EAAA;AACN,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,IAAK,CAAA,EAAG;AACnB,IAAA,EAAA,GAAK,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,KAAA,GAAQ,OAAO,MAAA,CAAO,MAAA;AACnD,EAAA,MAAM,EAAA,GAAK,uBAAA,CAAwB,MAAA,EAAQ,MAAM,CAAA;AACjD,EAAA,MAAM,GAAA,GAAM,WAAW,EAAW,CAAA;AAClC,EAAA,MAAM,cAAkC,GAAA,IAAwC,EAAA;AAEhF,EAAA,MAAM,IAAA,GAAO,kBAAkB,YAAY,CAAA;AAE3C,EAAA,MAAM,aAA6B,EAAC;AACpC,EAAA,MAAM,QAAwB,EAAC;AAC/B,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,IAAA,MAAM,YAAY,CAAA,KAAM,CAAA,GAAI,CAAA,GAAI,SAAA,CAAU,IAAI,CAAC,CAAA;AAC/C,IAAA,MAAM,KAAA,GAAQ,UAAU,CAAC,CAAA;AAGzB,IAAA,MAAM,UAAsC,EAAC;AAC7C,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,MAAA,MAAM,GAAA,GAAM,YAAY,CAAC,CAAA;AACzB,MAAA,OAAA,CAAQ,IAAA,CAAK,cAAA,CAAe,WAAA,EAAa,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAC,CAAC,CAAA;AAAA,IACpE;AACA,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,MAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvB,MAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,CAAA,GAAI,CAAC,CAAA;AAC1B,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,GAAI,MAAM,CAAC,CAAA;AAC5B,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,GAAI,MAAM,CAAC,CAAA;AAC5B,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,GAAI,MAAM,CAAC,CAAA;AAC5B,MAAA,OAAA,CAAQ,IAAI,CAAC,CAAA,GAAI,CAAC,KAAA,CAAM,CAAC,IAAI,EAAA,GAAK,KAAA,EAAO,KAAA,CAAM,CAAC,IAAI,EAAA,GAAK,KAAA,EAAO,MAAM,CAAC,CAAA,GAAI,KAAK,KAAK,CAAA;AACrF,MAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,CAAC,KAAA,CAAM,CAAC,IAAI,EAAA,GAAK,SAAA,EAAW,KAAA,CAAM,CAAC,IAAI,EAAA,GAAK,SAAA,EAAW,MAAM,CAAC,CAAA,GAAI,KAAK,SAAS,CAAA;AAAA,IACjG;AAGA,IAAA,IAAI,EAAA,GAAK,CAAA,EACL,EAAA,GAAK,CAAA,EACL,EAAA,GAAK,CAAA;AACT,IAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACrB,MAAA,EAAA,IAAM,EAAE,CAAC,CAAA;AACT,MAAA,EAAA,IAAM,EAAE,CAAC,CAAA;AACT,MAAA,EAAA,IAAM,EAAE,CAAC,CAAA;AAAA,IACb;AACA,IAAA,EAAA,IAAM,CAAA;AACN,IAAA,EAAA,IAAM,CAAA;AACN,IAAA,EAAA,IAAM,CAAA;AAEN,IAAA,IAAI,IAAA,EAAc,IAAA,EAAc,IAAA,EAAc,IAAA,EAAc,KAAA,EAAe,KAAA;AAC3E,IAAA,IAAI,YAAA,GAAe,CAAA;AACnB,IAAA,IAAI,IAAI,kBAAA,EAAoB;AACxB,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACrB,QAAA,MAAA,GAAS,KAAK,GAAA,CAAI,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,EAAE,CAAC,CAAA,GAAI,EAAA,EAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA,EAAI,EAAE,CAAC,CAAA,GAAI,EAAE,CAAC,CAAA;AAAA,MACzE;AACA,MAAA,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,MAAA,GAAS,EAAE,CAAA,GAAI,EAAA;AAClC,MAAA,YAAA,GAAe,MAAA;AACf,MAAA,IAAA,GAAO,IAAA,GAAO,QAAQ,CAAC,MAAA;AACvB,MAAA,IAAA,GAAO,OAAO,KAAA,GAAQ,MAAA;AAAA,IAC1B,CAAA,MAAO;AAEH,MAAA,MAAM,UAAU,oBAAA,CAAqB,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,IAAI,EAAE,CAAA;AAC3D,MAAA,IAAA,GAAO,OAAO,KAAA,GAAQ,QAAA;AACtB,MAAA,IAAA,GAAO,OAAO,KAAA,GAAQ,CAAA,QAAA;AACtB,MAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACrB,QAAA,MAAM,EAAA,GAAK,cAAA,CAAe,OAAA,EAAS,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AACnD,QAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAA,CAAG,CAAC,CAAC,CAAA;AAC3B,QAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAA,CAAG,CAAC,CAAC,CAAA;AAC3B,QAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAA,CAAG,CAAC,CAAC,CAAA;AAC3B,QAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAA,CAAG,CAAC,CAAC,CAAA;AAC3B,QAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,EAAA,CAAG,CAAC,CAAC,CAAA;AAC7B,QAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,EAAA,CAAG,CAAC,CAAC,CAAA;AAAA,MACjC;AAAA,IACJ;AAGA,IAAA,MAAM,IAAA,GAAO,KAAK,EAAA,GAAK,KAAA;AACvB,IAAA,MAAM,IAAA,GAAO,KAAK,EAAA,GAAK,KAAA;AACvB,IAAA,MAAM,IAAA,GAAO,KAAK,EAAA,GAAK,KAAA;AACvB,IAAA,MAAM,OAAO,oBAAA,CAAqB,EAAA,EAAI,IAAI,EAAA,EAAI,IAAA,EAAM,MAAM,IAAI,CAAA;AAE9D,IAAA,IAAI,QAAA,GAAW,CAAA;AACf,IAAA,IAAI,WAAW,KAAA,GAAQ,KAAA;AAIvB,IAAA,IAAI,IAAA,EAAM;AACN,MAAA,IAAI,KAAA,GAAQ,QAAA;AACZ,MAAA,IAAI,KAAA,GAAQ,CAAA,QAAA;AACZ,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,QAAA,MAAM,EAAA,GAAK,IAAI,CAAA,GAAI,IAAA,CAAK,KAAK,CAAC,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,EAAA,GAAK,IAAI,CAAA,GAAI,IAAA,CAAK,KAAK,CAAC,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,EAAA,GAAK,IAAI,CAAA,GAAI,IAAA,CAAK,KAAK,CAAC,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,GAAK,KAAK,IAAA,CAAK,CAAC,CAAA,GAAK,EAAA,GAAK,IAAA,CAAK,EAAE,CAAA,GAAK,EAAA,GAAK,KAAK,EAAE,CAAA;AACnE,QAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,EAAE,CAAA;AAC1B,QAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,EAAE,CAAA;AAAA,MAC9B;AACA,MAAA,IAAI,SAAS,QAAA,EAAU;AACnB,QAAA,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,KAAK,CAAA;AACnC,QAAA,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,KAAK,CAAA;AAAA,MACvC;AAAA,IAUJ;AAIA,IAAA,IAAI,IAAI,eAAA,EAAiB;AACrB,MAAA,QAAA,IAAY,GAAA,CAAI,eAAA;AAAA,IACpB;AAEA,IAAA,MAAM,QAAQ,gBAAA,CAAiB,IAAA,EAAM,MAAM,IAAA,EAAM,IAAA,EAAM,UAAU,QAAQ,CAAA;AACzE,IAAA,IAAI,SAAA,GAAY,WAAA,CAAY,KAAA,EAAO,IAAI,CAAA;AAavC,IAAA,IAAI,MAAA,GAAS,UAAU,EAAE,CAAA;AACzB,IAAA,IAAI,MAAA,GAAS,UAAU,EAAE,CAAA;AACzB,IAAA,IAAI,GAAA,CAAI,kBAAA,IAAsB,YAAA,GAAe,CAAA,EAAG;AAC5C,MAAA,MAAM,UAAA,GAAc,CAAA,GAAI,YAAA,GAAgB,GAAA,CAAI,QAAA;AAK5C,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,EACb,EAAA,GAAK,KAAK,CAAC,CAAA,EACX,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA;AACf,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,EACb,EAAA,GAAK,KAAK,CAAC,CAAA,EACX,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA;AAGf,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAA,CAAO,EAAA,GAAK,EAAA,GAAK,KAAK,EAAA,GAAK,EAAA,GAAK,EAAA,IAAM,UAAU,CAAA,GAAI,UAAA;AACpE,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAA,CAAO,EAAA,GAAK,EAAA,GAAK,KAAK,EAAA,GAAK,EAAA,GAAK,EAAA,IAAM,UAAU,CAAA,GAAI,UAAA;AACpE,MAAA,MAAM,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAC1B,MAAA,MAAM,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAC1B,MAAA,MAAM,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAC1B,MAAA,MAAA,GAAS,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,UAAU,EAAE,CAAA;AACpF,MAAA,MAAA,GAAS,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,SAAA,CAAU,CAAC,CAAA,GAAK,EAAA,GAAK,UAAU,EAAE,CAAA;AAAA,IACxF;AACA,IAAA,MAAM,EAAA,GAAK,MAAA,IAAU,GAAA,CAAI,QAAA,GAAW,CAAA,CAAA;AACpC,IAAA,MAAM,EAAA,GAAK,MAAA,IAAU,GAAA,CAAI,QAAA,GAAW,CAAA,CAAA;AACpC,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,EAAE,CAAA,GAAI,EAAA,KAAO,IAAI,GAAA,CAAI,QAAA,CAAA;AAC9C,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,EAAE,CAAA,GAAI,EAAA,KAAO,IAAI,GAAA,CAAI,QAAA,CAAA;AAC9C,IAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,CAAC,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAG,CAAA,EAAG,MAAM,IAAA,EAAM,CAAA,EAAG,CAAC,CAAC,CAAA;AACpF,IAAA,MAAM,IAAA,GAAO,WAAA,CAAY,IAAA,EAAM,KAAK,CAAA;AACpC,IAAA,SAAA,GAAY,WAAA,CAAY,MAAM,IAAI,CAAA;AAElC,IAAA,UAAA,CAAW,KAAK,SAAS,CAAA;AACzB,IAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,IAAA,OAAA,CAAQ,KAAK,QAAQ,CAAA;AACrB,IAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AAAA,EACxB;AAEA,EAAA,OAAO;AAAA,IACH,WAAA,EAAa,UAAA;AAAA,IACb,OAAA,EAAS,WAAW,GAAA,CAAI,CAAC,MAAM,IAAI,YAAA,CAAa,CAAC,CAAC,CAAA;AAAA,IAClD,MAAA,EAAQ,KAAA;AAAA,IACR,KAAA,EAAO,OAAA;AAAA,IACP,IAAA,EAAM,MAAA;AAAA,IACN,aAAA,EAAe,YAAA;AAAA,IACf,eAAA,EAAiB;AAAA,GACrB;AACJ;AAEA,SAAS,kBAAkB,YAAA,EAA0G;AACjI,EAAA,IAAI,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,UACP,IAAA,GAAO,CAAA,QAAA,EACP,IAAA,GAAO,CAAA,QAAA,EACP,IAAA,GAAO,CAAA,QAAA;AACX,EAAA,KAAA,MAAW,QAAQ,YAAA,EAAc;AAM7B,IAAA,MAAM,KAAK,IAAA,CAAK,aAAA;AAChB,IAAA,IAAI,EAAA,IAAM,EAAA,CAAG,KAAA,GAAQ,CAAA,IAAK,GAAG,QAAA,EAAU;AACnC,MAAA,MAAM,CAAA,GAAI,sBAAA,CAAuB,IAAA,EAAM,EAAE,CAAA;AACzC,MAAA,IAAI,CAAA,EAAG;AACH,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAC/B,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAC/B,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAC/B,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAC/B,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAC/B,QAAA,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,MACnC;AACA,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,QAAQ,IAAA,CAAK,WAAA;AACnB,IAAA,MAAM,OAAO,IAAA,CAAK,QAAA,IAAY,CAAC,IAAA,EAAM,MAAM,IAAI,CAAA;AAC/C,IAAA,MAAM,OAAO,IAAA,CAAK,QAAA,IAAY,CAAC,GAAA,EAAK,KAAK,GAAG,CAAA;AAC5C,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AACpC,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AACpC,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AACpC,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACtE,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACtE,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,EAAE,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACvE,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AACxB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AACxB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AACxB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AACxB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AACxB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,EAAE,CAAA;AAAA,IAC5B;AAAA,EACJ;AACA,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,EAAG;AACxB,IAAA,OAAO,IAAA;AAAA,EACX;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA,EAAG,IAAA,EAAM,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA,EAAE;AAChE;AAUA,IAAI,oBAAA,GAAwH,IAAA;AAC5H,SAAS,uBAAA,GAAoH;AACzH,EAAA,IAAI,CAAC,oBAAA,EAAsB;AACvB,IAAA,oBAAA,uBAA2B,OAAA,EAAQ;AAAA,EACvC;AACA,EAAA,OAAO,oBAAA;AACX;AAMA,SAAS,sBAAA,CAAuB,MAAY,EAAA,EAA+D;AACvG,EAAA,MAAM,QAAQ,uBAAA,EAAwB;AACtC,EAAA,MAAM,eAAe,IAAA,CAAK,kBAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAC7B,EAAA,IAAI,UAAU,MAAA,CAAO,QAAA,KAAa,GAAG,QAAA,IAAY,MAAA,CAAO,kBAAkB,YAAA,EAAc;AACpF,IAAA,OAAO,MAAA,CAAO,KAAA;AAAA,EAClB;AAEA,EAAA,MAAM,QAAQ,IAAA,CAAK,WAAA;AACnB,EAAA,MAAM,OAAO,IAAA,CAAK,QAAA,IAAY,CAAC,IAAA,EAAM,MAAM,IAAI,CAAA;AAC/C,EAAA,MAAM,OAAO,IAAA,CAAK,QAAA,IAAY,CAAC,GAAA,EAAK,KAAK,GAAG,CAAA;AAC5C,EAAA,MAAM,OAAO,EAAA,CAAG,QAAA;AAChB,EAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,EAAA,CAAG,OAAQ,IAAA,CAAK,MAAA,GAAS,KAAM,CAAC,CAAA;AACvD,EAAA,IAAI,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,UACP,IAAA,GAAO,CAAA,QAAA,EACP,IAAA,GAAO,CAAA,QAAA,EACP,IAAA,GAAO,CAAA,QAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC5B,IAAA,MAAM,IAAI,CAAA,GAAI,EAAA;AAEd,IAAA,MAAM,GAAA,GACF,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAC,CAAE,CAAA,GACjB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,CAAC,CAAE,CAAA,GACrB,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,GAAI,EAAE,CAAE,CAAA;AAC1B,IAAA,IAAI,MAAM,IAAA,EAAM;AACZ,MAAA;AAAA,IACJ;AACA,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AACpC,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AACpC,MAAA,MAAM,KAAK,CAAA,GAAI,CAAA,GAAI,KAAK,CAAC,CAAA,GAAK,KAAK,CAAC,CAAA;AAEpC,MAAA,MAAM,KAAK,IAAA,CAAK,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,CAAC,IAAK,EAAA,GAAK,IAAA,CAAK,IAAI,EAAE,CAAA;AAC9E,MAAA,MAAM,KAAK,IAAA,CAAK,CAAA,GAAI,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,CAAC,IAAK,EAAA,GAAK,IAAA,CAAK,IAAI,EAAE,CAAA;AAClF,MAAA,MAAM,KAAK,IAAA,CAAK,CAAA,GAAI,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,CAAC,CAAA,GAAK,EAAA,GAAK,KAAK,CAAA,GAAI,EAAE,IAAK,EAAA,GAAK,IAAA,CAAK,IAAI,EAAE,CAAA;AAEnF,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACtE,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACtE,MAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAK,KAAK,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,GAAK,KAAA,CAAM,EAAE,CAAA,GAAK,EAAA,GAAK,MAAM,EAAE,CAAA;AACvE,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AACA,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AACA,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AACA,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AACA,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AACA,MAAA,IAAI,KAAK,IAAA,EAAM;AACX,QAAA,IAAA,GAAO,EAAA;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AACA,EAAA,MAAM,OAA8B,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,GAAI,EAAE,MAAM,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,GAAG,IAAA,EAAM,CAAC,MAAM,IAAA,EAAM,IAAI,GAAE,GAAI,IAAA;AACrH,EAAA,KAAA,CAAM,GAAA,CAAI,IAAA,EAAM,EAAE,QAAA,EAAU,EAAA,CAAG,UAAU,aAAA,EAAe,YAAA,EAAc,KAAA,EAAO,IAAA,EAAM,CAAA;AACnF,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,YAAA,CAAa,GAAA,EAAmB,QAAA,EAAuB,GAAA,EAAsB;AAClF,EAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AACV,EAAA,MAAM,CAAA,GAAI,SAAS,WAAA,CAAY,MAAA;AAC/B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,IAAA,GAAA,CAAI,IAAI,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA,EAAI,IAAI,EAAE,CAAA;AAAA,EAC5C;AACA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,IAAA,GAAA,CAAI,EAAA,GAAK,CAAC,CAAA,GAAI,QAAA,CAAS,cAAc,CAAC,CAAA;AACtC,IAAA,GAAA,CAAI,EAAA,GAAK,CAAC,CAAA,GAAI,QAAA,CAAS,gBAAgB,CAAC,CAAA;AAAA,EAC5C;AACA,EAAA,GAAA,CAAI,EAAE,IAAI,GAAA,CAAI,SAAA;AACd,EAAA,GAAA,CAAI,EAAE,IAAI,GAAA,CAAI,QAAA;AACd,EAAA,GAAA,CAAI,EAAE,CAAA,GAAI,CAAA,GAAI,GAAA,CAAI,QAAA;AAClB,EAAA,GAAA,CAAI,EAAE,IAAI,GAAA,CAAI,mBAAA;AACd,EAAA,GAAA,CAAI,EAAE,CAAA,GAAI,CAAA;AACV,EAAA,GAAA,CAAI,EAAE,CAAA,GAAI,GAAA,CAAI,4BAA4B,CAAA,GAAI,GAAA,GAAQ,IAAI,GAAA,CAAI,uBAAA;AAClE;AAGO,SAAS,sBAAA,CAAuB,cAAA,EAAwB,IAAA,EAAc,GAAA,EAAqB;AAC9F,EAAA,MAAM,QAAQ,GAAA,GAAM,IAAA;AACpB,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,cAAc,CAAA,IAAK,cAAA,IAAkB,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AAClG,IAAA,OAAO,CAAA;AAAA,EACX;AACA,EAAA,OAAO,cAAA,GAAiB,KAAA;AAC5B;AAEA,SAAS,mBAAA,CAAoB,UAAwB,UAAA,EAAkC;AACnF,EAAA,MAAM,MAAA,GAAS,IAAI,YAAA,CAAa,QAAQ,CAAA;AACxC,EAAA,KAAA,IAAS,GAAA,GAAM,CAAA,EAAG,GAAA,GAAM,CAAA,EAAG,GAAA,EAAA,EAAO;AAC9B,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,GAAM,CAAA;AACpB,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,GAAM,CAAA;AACpB,IAAA,MAAA,CAAO,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAK,UAAA,GAAa,OAAO,CAAC,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,MAAA;AACX;;;;"}
|