@babylonjs/lite 1.6.0 → 1.8.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 +735 -0
- package/README.md +26 -1
- package/THIRD_PARTY_NOTICES.txt +139 -0
- package/dist/index.js +321 -301
- package/dist/index.js.map +1 -1
- package/index.d.ts +294 -13
- package/lib/_chunks/vendor/{manifold-Sh8xE2da.js → manifold-DtI3BJoR.js} +2 -2
- package/lib/_chunks/vendor/{manifold-Sh8xE2da.js.map → manifold-DtI3BJoR.js.map} +1 -1
- package/lib/animation/animation-group-task.js +1 -1
- package/lib/animation/animation-group-task.js.map +1 -1
- package/lib/animation/animation-group.js +20 -15
- package/lib/animation/animation-group.js.map +1 -1
- package/lib/animation/animation-tick.js +10 -0
- package/lib/animation/animation-tick.js.map +1 -0
- package/lib/animation/property-animation.js +2 -1
- package/lib/animation/property-animation.js.map +1 -1
- package/lib/animation/weighted-gltf-mixer.js +1 -1
- package/lib/animation/weighted-gltf-mixer.js.map +1 -1
- package/lib/animation/weighted-pointer-mixer.js +1 -1
- package/lib/animation/weighted-pointer-mixer.js.map +1 -1
- package/lib/engine/device-lost-recovery.js +1 -0
- package/lib/engine/device-lost-recovery.js.map +1 -1
- package/lib/engine/engine.js +1 -1
- package/lib/engine/null-engine.js +29 -0
- package/lib/engine/null-engine.js.map +1 -0
- package/lib/frame-graph/depth-pyramid.js +158 -0
- package/lib/frame-graph/depth-pyramid.js.map +1 -0
- package/lib/frame-graph/depth-resolve-task.js +117 -0
- package/lib/frame-graph/depth-resolve-task.js.map +1 -0
- package/lib/frame-graph/frame-graph-actions.js +10 -1
- package/lib/frame-graph/frame-graph-actions.js.map +1 -1
- package/lib/frame-graph/render-task.js +7 -0
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/frame-graph/transmission-depth-grab.js +88 -0
- package/lib/frame-graph/transmission-depth-grab.js.map +1 -0
- package/lib/frame-graph/transmission.js +34 -4
- package/lib/frame-graph/transmission.js.map +1 -1
- package/lib/gizmo/bounding-box-gizmo.js +1 -1
- package/lib/index.js +9 -6
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-gpu-instancing.js +1 -1
- package/lib/loader-gltf/gltf-interleave.js +1 -1
- package/lib/loader-gltf/load-gltf.js +1 -1
- package/lib/material/shader/shader-material.js +4 -2
- package/lib/material/shader/shader-material.js.map +1 -1
- package/lib/material/shader/shader-pipeline.js +11 -1
- package/lib/material/shader/shader-pipeline.js.map +1 -1
- package/lib/material/shader/shader-renderable.js +42 -14
- package/lib/material/shader/shader-renderable.js.map +1 -1
- package/lib/material/shader/shader-thin-instance.js +1 -1
- package/lib/material/shader/shader-thin-instance.js.map +1 -1
- package/lib/math/aabb.js +1 -1
- package/lib/math/compute-aabb.js +63 -1
- package/lib/math/compute-aabb.js.map +1 -1
- package/lib/math/index.js +1 -1
- package/lib/mesh/csg2.js +3 -2
- package/lib/mesh/csg2.js.map +1 -1
- package/lib/mesh/mesh-factories.js +1 -1
- package/lib/mesh/thin-instance-gpu-culling.js +4 -2
- package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
- package/lib/mesh/thin-instance.js +8 -1
- package/lib/mesh/thin-instance.js.map +1 -1
- package/lib/navigation/index.js +1 -1
- package/lib/navigation/navigation.js +9 -1
- package/lib/navigation/navigation.js.map +1 -1
- package/lib/physics/character-controller.js +2 -2
- package/lib/physics/character-controller.js.map +1 -1
- package/lib/physics/havok-floating-origin.js +2 -2
- package/lib/physics/havok-floating-origin.js.map +1 -1
- package/lib/physics/havok.js +40 -13
- package/lib/physics/havok.js.map +1 -1
- package/lib/picking/gpu-picker.js +16 -2
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/scene/scene-core.js +14 -5
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/scene/scene-remove.js +8 -0
- package/lib/scene/scene-remove.js.map +1 -1
- package/lib/shadow/csm-shadow-task-hooks.js +14 -6
- package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
- package/lib/shadow/pcf-shadow-task-hooks.js +6 -0
- package/lib/shadow/pcf-shadow-task-hooks.js.map +1 -1
- package/lib/texture/pixels-texture.js +31 -1
- package/lib/texture/pixels-texture.js.map +1 -1
- package/lib/vat/vat-baker.js +41 -2
- package/lib/vat/vat-baker.js.map +1 -1
- package/package.json +15 -3
- package/lib/_chunks/__vite-browser-external-BFTKbDcV.js +0 -68
- package/lib/_chunks/__vite-browser-external-BFTKbDcV.js.map +0 -1
|
@@ -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\";\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 _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 !== ((mat as { _csmGen?: number })._csmGen ?? 0)) {\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, (m.material as { _csmGen?: number })._csmGen ?? 0);\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\n // never recorded again. Its GPU buffers may still be referenced by a frame already submitted this\n // tick, so we must NOT dispose it synchronously; defer until the GPU drains the currently-submitted\n // work (onSubmittedWorkDone). Mirrors resizeMeshGeometry.\n const old = existing._task;\n void engine._device.queue\n .onSubmittedWorkDone()\n .then(() => {\n try {\n old.dispose();\n } catch {\n // Device may have been lost/disposed before the deferred dispose ran — nothing to free.\n }\n })\n .catch(() => {});\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 }, 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, (m.material as { _csmGen?: number })._csmGen ?? 0);\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 updateShadowCameraBase(cam, state._cameraVersion, cascades._near[i]!, cascades._far[i]!, cascades._views[i]!, _biasViewProjection(cascades._biased[i]!, cfg._bias));\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 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 const ax = Math.round(cx / texelWorld) * texelWorld;\n const ay = Math.round(cy / texelWorld) * texelWorld;\n const az = Math.round(cz / texelWorld) * texelWorld;\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, keyed on BOTH the instance-matrix version and the\n * prototype mesh's `worldMatrixVersion` — the drawn world transform is `mesh.world * instanceMatrix`, so the\n * AABB must invalidate when EITHER changes (a rebake, or the prototype moving/reparenting). Lazily allocated\n * 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 // The drawn transform is `mesh.world * instanceMatrix`, so the AABB depends on BOTH the instance matrices\n // and the prototype world matrix — invalidate when either version changes.\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\nfunction _biasViewProjection(viewProj: Float32Array, bias: number): Float32Array {\n const biased = new Float32Array(viewProj);\n const b = bias * 0.5;\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]! + b * 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\";\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 _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\n // never recorded again. Its GPU buffers may still be referenced by a frame already submitted this\n // tick, so we must NOT dispose it synchronously; defer until the GPU drains the currently-submitted\n // work (onSubmittedWorkDone). Mirrors resizeMeshGeometry.\n const old = existing._task;\n void engine._device.queue\n .onSubmittedWorkDone()\n .then(() => {\n try {\n old.dispose();\n } catch {\n // Device may have been lost/disposed before the deferred dispose ran — nothing to free.\n }\n })\n .catch(() => {});\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 }, 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 updateShadowCameraBase(cam, state._cameraVersion, cascades._near[i]!, cascades._far[i]!, cascades._views[i]!, _biasViewProjection(cascades._biased[i]!, cfg._bias));\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 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, keyed on BOTH the instance-matrix version and the\n * prototype mesh's `worldMatrixVersion` — the drawn world transform is `mesh.world * instanceMatrix`, so the\n * AABB must invalidate when EITHER changes (a rebake, or the prototype moving/reparenting). Lazily allocated\n * 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 // The drawn transform is `mesh.world * instanceMatrix`, so the AABB depends on BOTH the instance matrices\n // and the prototype world matrix — invalidate when either version changes.\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\nfunction _biasViewProjection(viewProj: Float32Array, bias: number): Float32Array {\n const biased = new Float32Array(viewProj);\n const b = bias * 0.5;\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]! + b * biased[w]!;\n }\n return biased;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -135,6 +135,12 @@ function getNoColorView(material, cache) {
|
|
|
135
135
|
if (cached) {
|
|
136
136
|
return cached;
|
|
137
137
|
}
|
|
138
|
+
const override = material._shadowCasterMaterial;
|
|
139
|
+
if (override) {
|
|
140
|
+
const overrideView = getNoColorView(override, cache);
|
|
141
|
+
cache.set(material, overrideView);
|
|
142
|
+
return overrideView;
|
|
143
|
+
}
|
|
138
144
|
const family = material._buildGroup._materialFamily;
|
|
139
145
|
let view;
|
|
140
146
|
if (family === "standard") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pcf-shadow-task-hooks.js","sources":["../../../src/shadow/pcf-shadow-task-hooks.ts"],"sourcesContent":["/** Internal PCF shadow task hooks owned by PCF shadow generators. */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Camera } from \"../camera/camera.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 { SceneContext } from \"../scene/scene-core.js\";\nimport type { SpotLight } from \"../light/spot-light.js\";\nimport { createRenderTask, type RenderTask } from \"../frame-graph/render-task.js\";\nimport { casterVersionSum, createShadowCamera, createShadowRenderTarget, updateShadowCameraBase, writeShadowUboFields } from \"./shadow-base.js\";\nimport type { ShadowGenerator, ShadowTaskInternalState } from \"./shadow-generator.js\";\nimport { packMat4IntoF32 } from \"../math/pack-mat4-into-f32.js\";\n\nexport interface PcfLightMatrix {\n /** @internal */\n _view: Float32Array;\n /** @internal */\n _viewProj: Float32Array;\n /** @internal */\n _near: number;\n /** @internal */\n _far: number;\n}\n\nexport interface PcfTaskState extends ShadowTaskInternalState {\n /** @internal */\n _task: RenderTask;\n /** @internal */\n _camera: Camera;\n /** @internal */\n _cameraVersion: number;\n /** @internal */\n _lastCasterVersion: number;\n /** @internal */\n _lastLightVersion: number;\n /** @internal Floating-origin offset version (active camera worldMatrixVersion) at last shadow-map render; -1 when never rendered. */\n _lastFoVersion: number;\n /** @internal */\n _shadowUboData: Float32Array;\n /** @internal */\n _casterMeshes: readonly Mesh[];\n /** @internal Owning scene — used to read the live floating-origin offset (camera world position). */\n _scene: SceneContext;\n}\n\ntype StandardNoColorFactory = typeof import(\"../material/standard/no-color-view.js\").createStandardNoColorMaterialView;\ntype PbrNoColorFactory = typeof import(\"../material/pbr/no-color-view.js\").createPbrNoColorMaterialView;\ntype NodeNoColorFactory = typeof import(\"../material/node/no-color-view.js\").createNodeNoColorMaterialView;\ntype ShaderNoColorFactory = typeof import(\"../material/shader/no-color-view.js\").createShaderNoColorMaterialView;\n\nlet createStandardNoColorMaterialView: StandardNoColorFactory;\nlet createPbrNoColorMaterialView: PbrNoColorFactory;\nlet createNodeNoColorMaterialView: NodeNoColorFactory;\nlet createShaderNoColorMaterialView: ShaderNoColorFactory;\n\nexport async function preloadPcfShadowTaskState(casterMeshes: readonly Mesh[]): Promise<void> {\n const loads: Promise<void>[] = [];\n let needsStandard = false;\n let needsPbr = false;\n let needsNode = false;\n let needsShader = false;\n for (const mesh of casterMeshes) {\n const family = mesh.material?._buildGroup._materialFamily;\n needsStandard ||= family === \"standard\";\n needsPbr ||= family === \"pbr\";\n needsNode ||= family === \"node\";\n needsShader ||= family === \"shader\";\n }\n if (needsStandard && !createStandardNoColorMaterialView) {\n loads.push(\n import(\"../material/standard/no-color-view.js\").then((module) => {\n createStandardNoColorMaterialView = module.createStandardNoColorMaterialView;\n })\n );\n }\n if (needsPbr && !createPbrNoColorMaterialView) {\n loads.push(\n import(\"../material/pbr/no-color-view.js\").then((module) => {\n createPbrNoColorMaterialView = module.createPbrNoColorMaterialView;\n })\n );\n }\n if (needsNode && !createNodeNoColorMaterialView) {\n loads.push(\n import(\"../material/node/no-color-view.js\").then((module) => {\n createNodeNoColorMaterialView = module.createNodeNoColorMaterialView;\n })\n );\n }\n if (needsShader && !createShaderNoColorMaterialView) {\n loads.push(\n import(\"../material/shader/no-color-view.js\").then((module) => {\n createShaderNoColorMaterialView = module.createShaderNoColorMaterialView;\n })\n );\n }\n await Promise.all(loads);\n}\n\nexport function ensurePcfShadowTaskState(\n engine: EngineContext,\n scene: SceneContext,\n sg: ShadowGenerator,\n casterMeshes: readonly Mesh[],\n existingState: ShadowTaskInternalState | null\n): PcfTaskState {\n const existing = existingState as PcfTaskState | null;\n if (existing) {\n if (existing._casterMeshes === casterMeshes) {\n return existing;\n }\n existing._task.dispose();\n }\n\n const materialViews = new Map<Material, MaterialView>();\n const camera = createShadowCamera(sg);\n const rt = createShadowRenderTarget(sg);\n const state: PcfTaskState = {\n _task: createRenderTask(\n {\n name: \"pcf\",\n rt,\n clr: true,\n cam: camera,\n },\n engine,\n scene\n ),\n _camera: camera,\n _cameraVersion: 0,\n _lastCasterVersion: -1,\n _lastLightVersion: -1,\n _lastFoVersion: -1,\n _shadowUboData: new F32(24),\n _casterMeshes: casterMeshes,\n _scene: scene,\n };\n\n for (const mesh of casterMeshes) {\n const material = mesh.material;\n if (material) {\n state._task.addMesh(mesh, { material: getNoColorView(material, materialViews) });\n }\n }\n\n return state;\n}\n\nexport function renderPcfShadowMap(\n engine: EngineContext,\n sg: ShadowGenerator,\n state: PcfTaskState,\n computeLightMatrix: (casterMeshes: readonly Mesh[], offX: number, offY: number, offZ: number) => PcfLightMatrix\n): number {\n const casterMeshes = state._casterMeshes;\n const casterVersion = casterVersionSum(casterMeshes);\n const lightVersion = sg._light.worldMatrixVersion;\n // Floating-origin offset = active camera world position (mirrors the mesh-world packer\n // and lights UBO). When the camera moves the offset changes, so every eye-relative GPU\n // matrix shifts even if light/casters are static — fold its version into the dirty check.\n const foCam = engine.useFloatingOrigin ? state._scene.camera : null;\n const foVersion = foCam ? foCam.worldMatrixVersion : 0;\n const offX = foCam ? foCam.worldMatrix[12]! : 0;\n const offY = foCam ? foCam.worldMatrix[13]! : 0;\n const offZ = foCam ? foCam.worldMatrix[14]! : 0;\n if (!sg._config._forceRefreshEveryFrame && casterVersion === state._lastCasterVersion && lightVersion === state._lastLightVersion && foVersion === state._lastFoVersion) {\n return 0;\n }\n\n const matrix = computeLightMatrix(casterMeshes, offX, offY, offZ);\n const matrixChanged = sg._light.lightType === \"directional\" || lightVersion !== state._lastLightVersion || foVersion !== state._lastFoVersion;\n if (matrixChanged) {\n packMat4IntoF32(sg._lightMatrix, matrix._viewProj, 0);\n sg._version++;\n writeShadowUboFields(state._shadowUboData, sg);\n engine._device.queue.writeBuffer(sg._shadowUBO, 0, state._shadowUboData as Float32Array<ArrayBuffer>);\n }\n updateShadowCamera(state, sg, matrix);\n\n state._lastCasterVersion = casterVersion;\n state._lastLightVersion = lightVersion;\n state._lastFoVersion = foVersion;\n return state._task.execute?.() ?? 0;\n}\n\nfunction updateShadowCamera(state: PcfTaskState, sg: ShadowGenerator, matrix: PcfLightMatrix): void {\n state._cameraVersion++;\n state._camera.fov = sg._light.lightType === \"spot\" ? (sg._light as SpotLight).angle : 1;\n updateShadowCameraBase(state._camera, state._cameraVersion, matrix._near, matrix._far, matrix._view, biasViewProjection(matrix._viewProj, sg._config._bias));\n}\n\nfunction biasViewProjection(viewProj: Float32Array, bias: number): Float32Array {\n const biased = new F32(viewProj);\n const b = bias * 0.5;\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]! + b * biased[w]!;\n }\n return biased;\n}\n\nexport function getNoColorView(material: Material, cache: Map<Material, MaterialView>): MaterialView {\n const cached = cache.get(material);\n if (cached) {\n return cached;\n }\n const family = material._buildGroup._materialFamily;\n let view: MaterialView;\n if (family === \"standard\") {\n view = createStandardNoColorMaterialView(material as Parameters<StandardNoColorFactory>[0]);\n } else if (family === \"pbr\") {\n view = createPbrNoColorMaterialView(material as Parameters<PbrNoColorFactory>[0]);\n } else if (family === \"node\") {\n view = createNodeNoColorMaterialView(material as Parameters<NodeNoColorFactory>[0]);\n } else if (family === \"shader\") {\n // Custom ShaderMaterial caster: the shader pipeline drops its fragment stage for the depth-only\n // shadow target on its own, so the view just hands it a private system UBO (shadow-camera VP).\n view = createShaderNoColorMaterialView(material as Parameters<typeof createShaderNoColorMaterialView>[0]);\n }\n cache.set(material, view!);\n return view!;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pcf-shadow-task-hooks.js","sources":["../../../src/shadow/pcf-shadow-task-hooks.ts"],"sourcesContent":["/** Internal PCF shadow task hooks owned by PCF shadow generators. */\n\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { Camera } from \"../camera/camera.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 { SceneContext } from \"../scene/scene-core.js\";\nimport type { SpotLight } from \"../light/spot-light.js\";\nimport { createRenderTask, type RenderTask } from \"../frame-graph/render-task.js\";\nimport { casterVersionSum, createShadowCamera, createShadowRenderTarget, updateShadowCameraBase, writeShadowUboFields } from \"./shadow-base.js\";\nimport type { ShadowGenerator, ShadowTaskInternalState } from \"./shadow-generator.js\";\nimport { packMat4IntoF32 } from \"../math/pack-mat4-into-f32.js\";\n\nexport interface PcfLightMatrix {\n /** @internal */\n _view: Float32Array;\n /** @internal */\n _viewProj: Float32Array;\n /** @internal */\n _near: number;\n /** @internal */\n _far: number;\n}\n\nexport interface PcfTaskState extends ShadowTaskInternalState {\n /** @internal */\n _task: RenderTask;\n /** @internal */\n _camera: Camera;\n /** @internal */\n _cameraVersion: number;\n /** @internal */\n _lastCasterVersion: number;\n /** @internal */\n _lastLightVersion: number;\n /** @internal Floating-origin offset version (active camera worldMatrixVersion) at last shadow-map render; -1 when never rendered. */\n _lastFoVersion: number;\n /** @internal */\n _shadowUboData: Float32Array;\n /** @internal */\n _casterMeshes: readonly Mesh[];\n /** @internal Owning scene — used to read the live floating-origin offset (camera world position). */\n _scene: SceneContext;\n}\n\ntype StandardNoColorFactory = typeof import(\"../material/standard/no-color-view.js\").createStandardNoColorMaterialView;\ntype PbrNoColorFactory = typeof import(\"../material/pbr/no-color-view.js\").createPbrNoColorMaterialView;\ntype NodeNoColorFactory = typeof import(\"../material/node/no-color-view.js\").createNodeNoColorMaterialView;\ntype ShaderNoColorFactory = typeof import(\"../material/shader/no-color-view.js\").createShaderNoColorMaterialView;\n\nlet createStandardNoColorMaterialView: StandardNoColorFactory;\nlet createPbrNoColorMaterialView: PbrNoColorFactory;\nlet createNodeNoColorMaterialView: NodeNoColorFactory;\nlet createShaderNoColorMaterialView: ShaderNoColorFactory;\n\nexport async function preloadPcfShadowTaskState(casterMeshes: readonly Mesh[]): Promise<void> {\n const loads: Promise<void>[] = [];\n let needsStandard = false;\n let needsPbr = false;\n let needsNode = false;\n let needsShader = false;\n for (const mesh of casterMeshes) {\n const family = mesh.material?._buildGroup._materialFamily;\n needsStandard ||= family === \"standard\";\n needsPbr ||= family === \"pbr\";\n needsNode ||= family === \"node\";\n needsShader ||= family === \"shader\";\n }\n if (needsStandard && !createStandardNoColorMaterialView) {\n loads.push(\n import(\"../material/standard/no-color-view.js\").then((module) => {\n createStandardNoColorMaterialView = module.createStandardNoColorMaterialView;\n })\n );\n }\n if (needsPbr && !createPbrNoColorMaterialView) {\n loads.push(\n import(\"../material/pbr/no-color-view.js\").then((module) => {\n createPbrNoColorMaterialView = module.createPbrNoColorMaterialView;\n })\n );\n }\n if (needsNode && !createNodeNoColorMaterialView) {\n loads.push(\n import(\"../material/node/no-color-view.js\").then((module) => {\n createNodeNoColorMaterialView = module.createNodeNoColorMaterialView;\n })\n );\n }\n if (needsShader && !createShaderNoColorMaterialView) {\n loads.push(\n import(\"../material/shader/no-color-view.js\").then((module) => {\n createShaderNoColorMaterialView = module.createShaderNoColorMaterialView;\n })\n );\n }\n await Promise.all(loads);\n}\n\nexport function ensurePcfShadowTaskState(\n engine: EngineContext,\n scene: SceneContext,\n sg: ShadowGenerator,\n casterMeshes: readonly Mesh[],\n existingState: ShadowTaskInternalState | null\n): PcfTaskState {\n const existing = existingState as PcfTaskState | null;\n if (existing) {\n if (existing._casterMeshes === casterMeshes) {\n return existing;\n }\n existing._task.dispose();\n }\n\n const materialViews = new Map<Material, MaterialView>();\n const camera = createShadowCamera(sg);\n const rt = createShadowRenderTarget(sg);\n const state: PcfTaskState = {\n _task: createRenderTask(\n {\n name: \"pcf\",\n rt,\n clr: true,\n cam: camera,\n },\n engine,\n scene\n ),\n _camera: camera,\n _cameraVersion: 0,\n _lastCasterVersion: -1,\n _lastLightVersion: -1,\n _lastFoVersion: -1,\n _shadowUboData: new F32(24),\n _casterMeshes: casterMeshes,\n _scene: scene,\n };\n\n for (const mesh of casterMeshes) {\n const material = mesh.material;\n if (material) {\n state._task.addMesh(mesh, { material: getNoColorView(material, materialViews) });\n }\n }\n\n return state;\n}\n\nexport function renderPcfShadowMap(\n engine: EngineContext,\n sg: ShadowGenerator,\n state: PcfTaskState,\n computeLightMatrix: (casterMeshes: readonly Mesh[], offX: number, offY: number, offZ: number) => PcfLightMatrix\n): number {\n const casterMeshes = state._casterMeshes;\n const casterVersion = casterVersionSum(casterMeshes);\n const lightVersion = sg._light.worldMatrixVersion;\n // Floating-origin offset = active camera world position (mirrors the mesh-world packer\n // and lights UBO). When the camera moves the offset changes, so every eye-relative GPU\n // matrix shifts even if light/casters are static — fold its version into the dirty check.\n const foCam = engine.useFloatingOrigin ? state._scene.camera : null;\n const foVersion = foCam ? foCam.worldMatrixVersion : 0;\n const offX = foCam ? foCam.worldMatrix[12]! : 0;\n const offY = foCam ? foCam.worldMatrix[13]! : 0;\n const offZ = foCam ? foCam.worldMatrix[14]! : 0;\n if (!sg._config._forceRefreshEveryFrame && casterVersion === state._lastCasterVersion && lightVersion === state._lastLightVersion && foVersion === state._lastFoVersion) {\n return 0;\n }\n\n const matrix = computeLightMatrix(casterMeshes, offX, offY, offZ);\n const matrixChanged = sg._light.lightType === \"directional\" || lightVersion !== state._lastLightVersion || foVersion !== state._lastFoVersion;\n if (matrixChanged) {\n packMat4IntoF32(sg._lightMatrix, matrix._viewProj, 0);\n sg._version++;\n writeShadowUboFields(state._shadowUboData, sg);\n engine._device.queue.writeBuffer(sg._shadowUBO, 0, state._shadowUboData as Float32Array<ArrayBuffer>);\n }\n updateShadowCamera(state, sg, matrix);\n\n state._lastCasterVersion = casterVersion;\n state._lastLightVersion = lightVersion;\n state._lastFoVersion = foVersion;\n return state._task.execute?.() ?? 0;\n}\n\nfunction updateShadowCamera(state: PcfTaskState, sg: ShadowGenerator, matrix: PcfLightMatrix): void {\n state._cameraVersion++;\n state._camera.fov = sg._light.lightType === \"spot\" ? (sg._light as SpotLight).angle : 1;\n updateShadowCameraBase(state._camera, state._cameraVersion, matrix._near, matrix._far, matrix._view, biasViewProjection(matrix._viewProj, sg._config._bias));\n}\n\nfunction biasViewProjection(viewProj: Float32Array, bias: number): Float32Array {\n const biased = new F32(viewProj);\n const b = bias * 0.5;\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]! + b * biased[w]!;\n }\n return biased;\n}\n\nexport function getNoColorView(material: Material, cache: Map<Material, MaterialView>): MaterialView {\n const cached = cache.get(material);\n if (cached) {\n return cached;\n }\n // Explicit caster override: this (receive) material casts its shadow through an ALTERNATE material (see\n // Material._shadowCasterMaterial). Take the override's OWN no-colour view (recurse) so the same mesh casts\n // with a sampler-free / alpha-clip caster instead of this material's shadow-map-aliasing view. Cache under\n // THIS material so the lookup at the call site (keyed by the receive material) hits.\n const override = material._shadowCasterMaterial;\n if (override) {\n const overrideView = getNoColorView(override, cache);\n cache.set(material, overrideView);\n return overrideView;\n }\n const family = material._buildGroup._materialFamily;\n let view: MaterialView;\n if (family === \"standard\") {\n view = createStandardNoColorMaterialView(material as Parameters<StandardNoColorFactory>[0]);\n } else if (family === \"pbr\") {\n view = createPbrNoColorMaterialView(material as Parameters<PbrNoColorFactory>[0]);\n } else if (family === \"node\") {\n view = createNodeNoColorMaterialView(material as Parameters<NodeNoColorFactory>[0]);\n } else if (family === \"shader\") {\n // Custom ShaderMaterial caster: the shader pipeline drops its fragment stage for the depth-only\n // shadow target on its own, so the view just hands it a private system UBO (shadow-camera VP).\n view = createShaderNoColorMaterialView(material as Parameters<typeof createShaderNoColorMaterialView>[0]);\n }\n cache.set(material, view!);\n return view!;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -63,6 +63,36 @@ function updateTexture2DFromPixels(engine, tex, data, x = 0, y = 0, width = tex.
|
|
|
63
63
|
{ width, height }
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
|
+
function createTexture3DFromPixels(engine, data, width, height, depth, options = {}) {
|
|
67
|
+
if (width < 1 || height < 1 || depth < 1) {
|
|
68
|
+
throw new Error(`createTexture3DFromPixels: width/height/depth must be >= 1 (got ${width}x${height}x${depth})`);
|
|
69
|
+
}
|
|
70
|
+
const expected = width * height * depth * 4;
|
|
71
|
+
if (data.length < expected) {
|
|
72
|
+
throw new Error(`createTexture3DFromPixels: data too short — need ${expected} bytes for ${width}x${height}x${depth} RGBA, got ${data.length}`);
|
|
73
|
+
}
|
|
74
|
+
const device = engine._device;
|
|
75
|
+
const format = options.srgb ? "rgba8unorm-srgb" : "rgba8unorm";
|
|
76
|
+
const texture = device.createTexture({
|
|
77
|
+
size: { width, height, depthOrArrayLayers: depth },
|
|
78
|
+
dimension: "3d",
|
|
79
|
+
format,
|
|
80
|
+
usage: TU.TEXTURE_BINDING | TU.COPY_DST
|
|
81
|
+
});
|
|
82
|
+
device.queue.writeTexture({ texture }, data, { bytesPerRow: width * 4, rowsPerImage: height }, { width, height, depthOrArrayLayers: depth });
|
|
83
|
+
const address = options.addressMode ?? "clamp-to-edge";
|
|
84
|
+
const filter = options.filter ?? "linear";
|
|
85
|
+
const sampler = getOrCreateSampler(engine, {
|
|
86
|
+
addressModeU: address,
|
|
87
|
+
addressModeV: address,
|
|
88
|
+
addressModeW: address,
|
|
89
|
+
minFilter: filter,
|
|
90
|
+
magFilter: filter
|
|
91
|
+
});
|
|
92
|
+
const tex = { texture, view: texture.createView({ dimension: "3d" }), sampler, width, height, depth };
|
|
93
|
+
acquireTexture(tex);
|
|
94
|
+
return tex;
|
|
95
|
+
}
|
|
66
96
|
|
|
67
|
-
export { createRenderTexture2D, createTexture2DFromPixels, updateTexture2DFromPixels };
|
|
97
|
+
export { createRenderTexture2D, createTexture2DFromPixels, createTexture3DFromPixels, updateTexture2DFromPixels };
|
|
68
98
|
//# sourceMappingURL=pixels-texture.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pixels-texture.js","sources":["../../../src/texture/pixels-texture.ts"],"sourcesContent":["/**\n * Create a 2D texture from raw pixel bytes (CPU-generated data).\n *\n * This is the generic analog to Babylon.js `RawTexture`: it uploads an\n * application-provided byte buffer into a GPU texture rather than decoding an\n * image from a URL. Use it for procedurally generated images, decoded asset\n * formats, or palette / lookup tables.\n *\n * The default sampler is nearest-neighbor with clamp-to-edge addressing and no\n * mipmaps — the common case for pixel-art / data textures. Override via options.\n */\n\nimport { TU } from \"../engine/gpu-flags.js\";\nimport type { Texture2D } from \"./texture-2d.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { acquireTexture, getOrCreateSampler } from \"../resource/gpu-pool.js\";\n\n/** Sampler and format overrides for `createTexture2DFromPixels()`. */\nexport interface PixelsTexture2DOptions {\n /** Address mode U. Default 'clamp-to-edge'. */\n addressModeU?: GPUAddressMode;\n /** Address mode V. Default 'clamp-to-edge'. */\n addressModeV?: GPUAddressMode;\n /** Min filter. Default 'nearest'. */\n minFilter?: GPUFilterMode;\n /** Mag filter. Default 'nearest'. */\n magFilter?: GPUFilterMode;\n /** Use sRGB format (rgba8unorm-srgb) so the hardware converts to linear on\n * sample. Use for color data; leave false for lookup tables. Default false. */\n srgb?: boolean;\n}\n\n/**\n * Create a `Texture2D` from a tightly-packed RGBA8 byte buffer.\n *\n * @param engine - Engine context.\n * @param data - `width * height * 4` bytes, row-major, top-to-bottom, straight alpha.\n * @param width - Texture width in pixels (\\>= 1).\n * @param height - Texture height in pixels (\\>= 1).\n * @param options - Sampler / format overrides.\n */\nexport function createTexture2DFromPixels(engine: EngineContext, data: Uint8Array, width: number, height: number, options: PixelsTexture2DOptions = {}): Texture2D {\n if (width < 1 || height < 1) {\n throw new Error(`createTexture2DFromPixels: width/height must be >= 1 (got ${width}x${height})`);\n }\n const expected = width * height * 4;\n if (data.length < expected) {\n throw new Error(`createTexture2DFromPixels: data too short — need ${expected} bytes for ${width}x${height} RGBA, got ${data.length}`);\n }\n\n const device = engine._device;\n const format: GPUTextureFormat = options.srgb ? \"rgba8unorm-srgb\" : \"rgba8unorm\";\n\n const texture = device.createTexture({\n size: { width, height },\n format,\n usage: TU.TEXTURE_BINDING | TU.COPY_DST,\n });\n\n device.queue.writeTexture({ texture }, data as Uint8Array<ArrayBuffer>, { bytesPerRow: width * 4, rowsPerImage: height }, { width, height });\n\n const sampler = getOrCreateSampler(engine, {\n addressModeU: options.addressModeU ?? \"clamp-to-edge\",\n addressModeV: options.addressModeV ?? \"clamp-to-edge\",\n minFilter: options.minFilter ?? \"nearest\",\n magFilter: options.magFilter ?? \"nearest\",\n });\n\n const tex: Texture2D = { texture, view: texture.createView(), sampler, width, height };\n acquireTexture(tex);\n return tex;\n}\n\n/** Sampler / format overrides for `createRenderTexture2D()`. */\nexport interface RenderTexture2DOptions {\n /** Address mode U. Default 'clamp-to-edge'. */\n addressModeU?: GPUAddressMode;\n /** Address mode V. Default 'clamp-to-edge'. */\n addressModeV?: GPUAddressMode;\n /** Min filter. Default 'linear'. */\n minFilter?: GPUFilterMode;\n /** Mag filter. Default 'linear'. */\n magFilter?: GPUFilterMode;\n /**\n * Color format. Default `engine.format` so it can be sampled and presented.\n *\n * ⚠️ Only the default `engine.format` is compatible with a `SpriteRenderer`\n * target (`setSpriteRendererTarget`): sprite pipelines are created with\n * `engine.format`, and a render pass whose color attachment format differs from\n * the bound pipeline fails WebGPU validation at pass begin. Override this **only**\n * for offscreen targets you render into by some OTHER means (a custom pass /\n * `EffectRenderer`), never as a sprite-render target.\n */\n format?: GPUTextureFormat;\n}\n\n/**\n * Create an empty `Texture2D` usable as **both a render target and a sampled texture**\n * (`RENDER_ATTACHMENT | TEXTURE_BINDING`). This is the building block for offscreen\n * render-to-texture: render a pass into `tex.view`, then sample `tex` in a later pass\n * (e.g. a fullscreen post-process). Defaults to the engine's swapchain format + a\n * linear sampler so the result can be presented directly.\n *\n * To use the result as a `SpriteRenderer` target (via `setSpriteRendererTarget`), leave\n * `format` at its default `engine.format` — sprite pipelines bake in that format, so a\n * differently-formatted target trips WebGPU validation at render-pass begin. A custom\n * `format` is for offscreen targets driven by some other pass, not the sprite renderer.\n */\nexport function createRenderTexture2D(engine: EngineContext, width: number, height: number, options: RenderTexture2DOptions = {}): Texture2D {\n if (width < 1 || height < 1) {\n throw new Error(`createRenderTexture2D: width/height must be >= 1 (got ${width}x${height})`);\n }\n const device = engine._device;\n const format = options.format ?? engine.format;\n const texture = device.createTexture({\n size: { width, height },\n format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST,\n });\n const sampler = getOrCreateSampler(engine, {\n addressModeU: options.addressModeU ?? \"clamp-to-edge\",\n addressModeV: options.addressModeV ?? \"clamp-to-edge\",\n minFilter: options.minFilter ?? \"linear\",\n magFilter: options.magFilter ?? \"linear\",\n });\n const tex: Texture2D = { texture, view: texture.createView(), sampler, width, height };\n acquireTexture(tex);\n return tex;\n}\n\n/**\n * Update a rectangular region of an existing `Texture2D` from a tightly-packed RGBA8 byte buffer.\n *\n * The texture must have been created with `COPY_DST` usage (as `createTexture2DFromPixels` does).\n * This is the runtime counterpart to `createTexture2DFromPixels` — for data textures the app mutates\n * each frame / on demand (e.g. a terrain carve heightmap stamped by a dig tool).\n *\n * @param engine - Engine context.\n * @param tex - Target texture (from `createTexture2DFromPixels`).\n * @param data - `width * height * 4` bytes for the sub-region, row-major, straight alpha.\n * @param x - Destination origin X in texels (default 0).\n * @param y - Destination origin Y in texels (default 0).\n * @param width - Region width in texels (default `tex.width`).\n * @param height - Region height in texels (default `tex.height`).\n */\nexport function updateTexture2DFromPixels(engine: EngineContext, tex: Texture2D, data: Uint8Array, x = 0, y = 0, width = tex.width, height = tex.height): void {\n if (width < 1 || height < 1) {\n throw new Error(`updateTexture2DFromPixels: width/height must be >= 1 (got ${width}x${height})`);\n }\n const expected = width * height * 4;\n if (data.length < expected) {\n throw new Error(`updateTexture2DFromPixels: data too short — need ${expected} bytes for ${width}x${height} RGBA, got ${data.length}`);\n }\n engine._device.queue.writeTexture(\n { texture: tex.texture, origin: { x, y } },\n data as Uint8Array<ArrayBuffer>,\n { bytesPerRow: width * 4, rowsPerImage: height },\n { width, height }\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pixels-texture.js","sources":["../../../src/texture/pixels-texture.ts"],"sourcesContent":["/**\n * Create a 2D texture from raw pixel bytes (CPU-generated data).\n *\n * This is the generic analog to Babylon.js `RawTexture`: it uploads an\n * application-provided byte buffer into a GPU texture rather than decoding an\n * image from a URL. Use it for procedurally generated images, decoded asset\n * formats, or palette / lookup tables.\n *\n * The default sampler is nearest-neighbor with clamp-to-edge addressing and no\n * mipmaps — the common case for pixel-art / data textures. Override via options.\n */\n\nimport { TU } from \"../engine/gpu-flags.js\";\nimport type { Texture2D } from \"./texture-2d.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport { acquireTexture, getOrCreateSampler } from \"../resource/gpu-pool.js\";\n\n/** Sampler and format overrides for `createTexture2DFromPixels()`. */\nexport interface PixelsTexture2DOptions {\n /** Address mode U. Default 'clamp-to-edge'. */\n addressModeU?: GPUAddressMode;\n /** Address mode V. Default 'clamp-to-edge'. */\n addressModeV?: GPUAddressMode;\n /** Min filter. Default 'nearest'. */\n minFilter?: GPUFilterMode;\n /** Mag filter. Default 'nearest'. */\n magFilter?: GPUFilterMode;\n /** Use sRGB format (rgba8unorm-srgb) so the hardware converts to linear on\n * sample. Use for color data; leave false for lookup tables. Default false. */\n srgb?: boolean;\n}\n\n/**\n * Create a `Texture2D` from a tightly-packed RGBA8 byte buffer.\n *\n * @param engine - Engine context.\n * @param data - `width * height * 4` bytes, row-major, top-to-bottom, straight alpha.\n * @param width - Texture width in pixels (\\>= 1).\n * @param height - Texture height in pixels (\\>= 1).\n * @param options - Sampler / format overrides.\n */\nexport function createTexture2DFromPixels(engine: EngineContext, data: Uint8Array, width: number, height: number, options: PixelsTexture2DOptions = {}): Texture2D {\n if (width < 1 || height < 1) {\n throw new Error(`createTexture2DFromPixels: width/height must be >= 1 (got ${width}x${height})`);\n }\n const expected = width * height * 4;\n if (data.length < expected) {\n throw new Error(`createTexture2DFromPixels: data too short — need ${expected} bytes for ${width}x${height} RGBA, got ${data.length}`);\n }\n\n const device = engine._device;\n const format: GPUTextureFormat = options.srgb ? \"rgba8unorm-srgb\" : \"rgba8unorm\";\n\n const texture = device.createTexture({\n size: { width, height },\n format,\n usage: TU.TEXTURE_BINDING | TU.COPY_DST,\n });\n\n device.queue.writeTexture({ texture }, data as Uint8Array<ArrayBuffer>, { bytesPerRow: width * 4, rowsPerImage: height }, { width, height });\n\n const sampler = getOrCreateSampler(engine, {\n addressModeU: options.addressModeU ?? \"clamp-to-edge\",\n addressModeV: options.addressModeV ?? \"clamp-to-edge\",\n minFilter: options.minFilter ?? \"nearest\",\n magFilter: options.magFilter ?? \"nearest\",\n });\n\n const tex: Texture2D = { texture, view: texture.createView(), sampler, width, height };\n acquireTexture(tex);\n return tex;\n}\n\n/** Sampler / format overrides for `createRenderTexture2D()`. */\nexport interface RenderTexture2DOptions {\n /** Address mode U. Default 'clamp-to-edge'. */\n addressModeU?: GPUAddressMode;\n /** Address mode V. Default 'clamp-to-edge'. */\n addressModeV?: GPUAddressMode;\n /** Min filter. Default 'linear'. */\n minFilter?: GPUFilterMode;\n /** Mag filter. Default 'linear'. */\n magFilter?: GPUFilterMode;\n /**\n * Color format. Default `engine.format` so it can be sampled and presented.\n *\n * ⚠️ Only the default `engine.format` is compatible with a `SpriteRenderer`\n * target (`setSpriteRendererTarget`): sprite pipelines are created with\n * `engine.format`, and a render pass whose color attachment format differs from\n * the bound pipeline fails WebGPU validation at pass begin. Override this **only**\n * for offscreen targets you render into by some OTHER means (a custom pass /\n * `EffectRenderer`), never as a sprite-render target.\n */\n format?: GPUTextureFormat;\n}\n\n/**\n * Create an empty `Texture2D` usable as **both a render target and a sampled texture**\n * (`RENDER_ATTACHMENT | TEXTURE_BINDING`). This is the building block for offscreen\n * render-to-texture: render a pass into `tex.view`, then sample `tex` in a later pass\n * (e.g. a fullscreen post-process). Defaults to the engine's swapchain format + a\n * linear sampler so the result can be presented directly.\n *\n * To use the result as a `SpriteRenderer` target (via `setSpriteRendererTarget`), leave\n * `format` at its default `engine.format` — sprite pipelines bake in that format, so a\n * differently-formatted target trips WebGPU validation at render-pass begin. A custom\n * `format` is for offscreen targets driven by some other pass, not the sprite renderer.\n */\nexport function createRenderTexture2D(engine: EngineContext, width: number, height: number, options: RenderTexture2DOptions = {}): Texture2D {\n if (width < 1 || height < 1) {\n throw new Error(`createRenderTexture2D: width/height must be >= 1 (got ${width}x${height})`);\n }\n const device = engine._device;\n const format = options.format ?? engine.format;\n const texture = device.createTexture({\n size: { width, height },\n format,\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST,\n });\n const sampler = getOrCreateSampler(engine, {\n addressModeU: options.addressModeU ?? \"clamp-to-edge\",\n addressModeV: options.addressModeV ?? \"clamp-to-edge\",\n minFilter: options.minFilter ?? \"linear\",\n magFilter: options.magFilter ?? \"linear\",\n });\n const tex: Texture2D = { texture, view: texture.createView(), sampler, width, height };\n acquireTexture(tex);\n return tex;\n}\n\n/**\n * Update a rectangular region of an existing `Texture2D` from a tightly-packed RGBA8 byte buffer.\n *\n * The texture must have been created with `COPY_DST` usage (as `createTexture2DFromPixels` does).\n * This is the runtime counterpart to `createTexture2DFromPixels` — for data textures the app mutates\n * each frame / on demand (e.g. a terrain carve heightmap stamped by a dig tool).\n *\n * @param engine - Engine context.\n * @param tex - Target texture (from `createTexture2DFromPixels`).\n * @param data - `width * height * 4` bytes for the sub-region, row-major, straight alpha.\n * @param x - Destination origin X in texels (default 0).\n * @param y - Destination origin Y in texels (default 0).\n * @param width - Region width in texels (default `tex.width`).\n * @param height - Region height in texels (default `tex.height`).\n */\nexport function updateTexture2DFromPixels(engine: EngineContext, tex: Texture2D, data: Uint8Array, x = 0, y = 0, width = tex.width, height = tex.height): void {\n if (width < 1 || height < 1) {\n throw new Error(`updateTexture2DFromPixels: width/height must be >= 1 (got ${width}x${height})`);\n }\n const expected = width * height * 4;\n if (data.length < expected) {\n throw new Error(`updateTexture2DFromPixels: data too short — need ${expected} bytes for ${width}x${height} RGBA, got ${data.length}`);\n }\n engine._device.queue.writeTexture(\n { texture: tex.texture, origin: { x, y } },\n data as Uint8Array<ArrayBuffer>,\n { bytesPerRow: width * 4, rowsPerImage: height },\n { width, height }\n );\n}\n\n/** Sampler / format overrides for `createTexture3DFromPixels()`. */\nexport interface PixelsTexture3DOptions {\n /** Address mode U/V/W. Default 'clamp-to-edge' (the right choice for a colour LUT — the cube edges\n * must not wrap). */\n addressMode?: GPUAddressMode;\n /** Min/mag filter. Default 'linear' so a colour-grading LUT interpolates trilinearly in one sample. */\n filter?: GPUFilterMode;\n /** Use sRGB format (rgba8unorm-srgb). Leave false for a linear/display LUT. Default false. */\n srgb?: boolean;\n}\n\n/** A `Texture2D` handle whose underlying GPU texture is `dimension:\"3d\"`, plus its depth. Bind it to a\n * fullscreen effect with `viewDimension:\"3d\"` and sample it in WGSL as `texture_3d<f32>`. */\nexport type Texture3D = Texture2D & { depth: number };\n\n/**\n * Create a **3D** texture from a tightly-packed RGBA8 byte buffer — the volumetric analog of\n * `createTexture2DFromPixels`. The primary use is a colour-grading LUT (a `.cube`/HALD colour cube):\n * upload the N×N×N RGBA volume once and sample it trilinearly with a single `textureSample`.\n *\n * The default sampler is linear with clamp-to-edge on all three axes — exactly what a LUT wants (linear\n * = trilinear interpolation between grid points; clamp = no wrap at the cube faces). The returned handle\n * is a `Texture2D` (so it drops straight into `setEffectTexture`) with an extra `depth` field; its `view`\n * is created with `dimension:\"3d\"`.\n *\n * @param engine - Engine context.\n * @param data - `width * height * depth * 4` bytes, RGBA8, ordered x fastest, then y, then z (slice-major).\n * @param width - Cube size along R (\\>= 1).\n * @param height - Cube size along G (\\>= 1).\n * @param depth - Cube size along B (\\>= 1).\n * @param options - Sampler / format overrides.\n */\nexport function createTexture3DFromPixels(engine: EngineContext, data: Uint8Array, width: number, height: number, depth: number, options: PixelsTexture3DOptions = {}): Texture3D {\n if (width < 1 || height < 1 || depth < 1) {\n throw new Error(`createTexture3DFromPixels: width/height/depth must be >= 1 (got ${width}x${height}x${depth})`);\n }\n const expected = width * height * depth * 4;\n if (data.length < expected) {\n throw new Error(`createTexture3DFromPixels: data too short — need ${expected} bytes for ${width}x${height}x${depth} RGBA, got ${data.length}`);\n }\n\n const device = engine._device;\n const format: GPUTextureFormat = options.srgb ? \"rgba8unorm-srgb\" : \"rgba8unorm\";\n\n const texture = device.createTexture({\n size: { width, height, depthOrArrayLayers: depth },\n dimension: \"3d\",\n format,\n usage: TU.TEXTURE_BINDING | TU.COPY_DST,\n });\n\n device.queue.writeTexture({ texture }, data as Uint8Array<ArrayBuffer>, { bytesPerRow: width * 4, rowsPerImage: height }, { width, height, depthOrArrayLayers: depth });\n\n const address = options.addressMode ?? \"clamp-to-edge\";\n const filter = options.filter ?? \"linear\";\n const sampler = getOrCreateSampler(engine, {\n addressModeU: address,\n addressModeV: address,\n addressModeW: address,\n minFilter: filter,\n magFilter: filter,\n });\n\n const tex: Texture3D = { texture, view: texture.createView({ dimension: \"3d\" }), sampler, width, height, depth };\n acquireTexture(tex);\n return tex;\n}\n"],"names":[],"mappings":"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"}
|
package/lib/vat/vat-baker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { goToFrame, stopAnimation } from '../animation/animation-group.js';
|
|
2
|
+
import { mat4Invert } from '../math/mat4-invert.js';
|
|
2
3
|
import { _registerPbrExt } from '../material/pbr/pbr-flags.js';
|
|
3
4
|
import { pbrExt } from '../material/pbr/fragments/vat-fragment.js';
|
|
4
5
|
|
|
@@ -10,7 +11,7 @@ function clipFrameCount(group) {
|
|
|
10
11
|
function bindingsOf(group) {
|
|
11
12
|
return group._gltfMixer?.[2];
|
|
12
13
|
}
|
|
13
|
-
function bakeVat(engine, mesh, groups) {
|
|
14
|
+
function bakeVat(engine, mesh, groups, opts) {
|
|
14
15
|
const skel = mesh.skeleton;
|
|
15
16
|
if (!skel) {
|
|
16
17
|
throw new Error("bakeVat: mesh has no skeleton to bake.");
|
|
@@ -25,17 +26,37 @@ function bakeVat(engine, mesh, groups) {
|
|
|
25
26
|
frameCount = Math.max(1, frameCount);
|
|
26
27
|
const data = new Float32Array(frameCount * floatsPerFrame);
|
|
27
28
|
const clips = {};
|
|
29
|
+
const captureBones = opts?.captureBoneOrigins;
|
|
30
|
+
const boneOrigins = captureBones ? {} : void 0;
|
|
31
|
+
let restOrigins = null;
|
|
32
|
+
if (captureBones && boneOrigins) {
|
|
33
|
+
for (const b of captureBones) {
|
|
34
|
+
boneOrigins[b] = new Float32Array(frameCount * 3);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
28
37
|
let row = 0;
|
|
29
38
|
for (const g of groups) {
|
|
30
39
|
const frames = clipFrameCount(g);
|
|
31
40
|
const fps = g.frameRate || DEFAULT_FRAME_RATE;
|
|
32
41
|
clips[g.name] = { fromRow: row, frameCount: frames, fps };
|
|
33
42
|
const binding = bindingsOf(g)?.[0];
|
|
43
|
+
if (captureBones && !restOrigins && binding) {
|
|
44
|
+
restOrigins = computeRestOrigins(captureBones, binding.inverseBindMatrices, boneCount);
|
|
45
|
+
}
|
|
34
46
|
for (let f = 0; f < frames; f++) {
|
|
35
47
|
goToFrame(g, f, engine);
|
|
36
48
|
const m = binding?.boneMatrices;
|
|
37
49
|
if (m) {
|
|
38
50
|
data.set(m.subarray(0, floatsPerFrame), row * floatsPerFrame);
|
|
51
|
+
if (captureBones && boneOrigins && restOrigins) {
|
|
52
|
+
for (const b of captureBones) {
|
|
53
|
+
const o = restOrigins.get(b);
|
|
54
|
+
const dst = boneOrigins[b];
|
|
55
|
+
if (o && dst && b < boneCount) {
|
|
56
|
+
transformPointInto(dst, row * 3, m, b * 16, o[0], o[1], o[2]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
61
|
row++;
|
|
41
62
|
}
|
|
@@ -48,7 +69,25 @@ function bakeVat(engine, mesh, groups) {
|
|
|
48
69
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
49
70
|
});
|
|
50
71
|
device.queue.writeTexture({ texture }, data.buffer, { bytesPerRow: texWidth * 16, rowsPerImage: frameCount }, { width: texWidth, height: frameCount });
|
|
51
|
-
return { texture, boneCount, frameCount, clips };
|
|
72
|
+
return boneOrigins ? { texture, boneCount, frameCount, clips, boneOrigins } : { texture, boneCount, frameCount, clips };
|
|
73
|
+
}
|
|
74
|
+
function computeRestOrigins(bones, ibm, boneCount) {
|
|
75
|
+
const out = /* @__PURE__ */ new Map();
|
|
76
|
+
for (const b of bones) {
|
|
77
|
+
if (b < 0 || b >= boneCount) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const inv = mat4Invert(ibm.subarray(b * 16, b * 16 + 16));
|
|
81
|
+
if (inv) {
|
|
82
|
+
out.set(b, [inv[12], inv[13], inv[14]]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function transformPointInto(dst, di, m, mo, px, py, pz) {
|
|
88
|
+
dst[di] = m[mo] * px + m[mo + 4] * py + m[mo + 8] * pz + m[mo + 12];
|
|
89
|
+
dst[di + 1] = m[mo + 1] * px + m[mo + 5] * py + m[mo + 9] * pz + m[mo + 13];
|
|
90
|
+
dst[di + 2] = m[mo + 2] * px + m[mo + 6] * py + m[mo + 10] * pz + m[mo + 14];
|
|
52
91
|
}
|
|
53
92
|
function attachVat(engine, mesh, baked, clip) {
|
|
54
93
|
const skel = mesh.skeleton;
|