@babylonjs/lite 1.4.0 → 1.5.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.
Files changed (122) hide show
  1. package/dist/index.js +381 -375
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +757 -0
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/engine/engine.js +1 -1
  41. package/lib/index.js +11 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/light/types.js.map +1 -1
  44. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  45. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  46. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  47. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  48. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  49. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  50. package/lib/loader-gltf/animation-pointer.js +4 -1
  51. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  52. package/lib/loader-gltf/gltf-animation.js +5 -3
  53. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  54. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  55. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  56. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  57. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  59. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  60. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  61. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  62. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  63. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  65. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  66. package/lib/loader-gltf/gltf-interleave.js +3 -2
  67. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  68. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  69. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  70. package/lib/loader-gltf/gltf-parser.js +7 -1
  71. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  72. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  73. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  74. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  75. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  76. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  77. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  78. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  79. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  80. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  81. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  82. package/lib/loader-gltf/load-gltf.js +15 -6
  83. package/lib/loader-gltf/load-gltf.js.map +1 -1
  84. package/lib/material/material-rebuild.js +4 -0
  85. package/lib/material/material-rebuild.js.map +1 -1
  86. package/lib/material/mesh-features.js +8 -1
  87. package/lib/material/mesh-features.js.map +1 -1
  88. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  89. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  90. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  91. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  92. package/lib/material/pbr/pbr-pipeline.js +7 -3
  93. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  94. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  95. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  96. package/lib/material/pbr/pbr-renderable.js +1 -1
  97. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  98. package/lib/material/shader/shader-material.js +9 -5
  99. package/lib/material/shader/shader-material.js.map +1 -1
  100. package/lib/material/shader/shader-thin-instance.js +1 -1
  101. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  102. package/lib/material/standard/standard-renderable.js +1 -1
  103. package/lib/material/standard/standard-renderable.js.map +1 -1
  104. package/lib/mesh/mesh-dispose.js +1 -0
  105. package/lib/mesh/mesh-dispose.js.map +1 -1
  106. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  107. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  108. package/lib/scene/scene-core.js +1 -0
  109. package/lib/scene/scene-core.js.map +1 -1
  110. package/lib/scene/scene-material-swap.js +2 -0
  111. package/lib/scene/scene-material-swap.js.map +1 -1
  112. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  113. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  114. package/lib/sprite/sprite-2d.js +4 -0
  115. package/lib/sprite/sprite-2d.js.map +1 -1
  116. package/lib/sprite/sprite-pipeline.js +25 -22
  117. package/lib/sprite/sprite-pipeline.js.map +1 -1
  118. package/lib/text/_gpu/text-pipeline.js +1 -1
  119. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  120. package/lib/text/text-renderer.js +3 -1
  121. package/lib/text/text-renderer.js.map +1 -1
  122. package/package.json +3 -3
@@ -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, 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 * it changes — not only when the caster SET changes. */\n _renderableVersion: 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 OR a material changed (a material swap rebuilds a caster's renderable + UBOs but\n // leaves our cached no-color material views dangling at the destroyed UBOs — this is the\n // \"Buffer used in submit while destroyed\" flood seen when planting a shadow-casting fern/agave,\n // whose foliage material swaps variant on first render). Rebuild the cascade tasks below with the\n // casters' CURRENT materials and return the NEW 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 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 };\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 // Stabilize Z: with stabilizeCascades the XY fit is a fixed bounding sphere (eye + extents\n // are stable), but the caster-AABB tighten above moves the near/far planes whenever a caster moves\n if (cfg._stabilizeCascades && stableRadius > 0) {\n const zq = Math.max(0.5, stableRadius / 128);\n viewMinZ = Math.floor(viewMinZ / zq) * zq;\n viewMaxZ = Math.ceil(viewMaxZ / zq) * zq;\n }\n }\n\n const proj0 = orthoOffCenterLH(minX, maxX, minY, maxY, viewMinZ, viewMaxZ);\n let transform = multiply4x4(proj0, view);\n\n // Texel-snap: round the world origin to a shadow-map texel (always applied in BJS).\n const ox = transform[12]! * (cfg._mapSize / 2);\n const oy = transform[13]! * (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/** 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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"}
@@ -34,6 +34,7 @@ function createSprite2DLayer(atlas, opts = {}) {
34
34
  depth,
35
35
  blendMode,
36
36
  opacity: opts.opacity ?? 1,
37
+ coverageGamma: opts.coverageGamma ?? 1,
37
38
  visible: opts.visible ?? true,
38
39
  order: opts.order ?? 0,
39
40
  view,
@@ -52,6 +53,9 @@ function createSprite2DLayer(atlas, opts = {}) {
52
53
  if (uvScroll) {
53
54
  layer._uvScroll = true;
54
55
  }
56
+ if (opts.coverageGamma != null && opts.coverageGamma !== 1) {
57
+ layer._coverageGamma = true;
58
+ }
55
59
  _getSpriteFxHook()?.initLayer(layer, opts);
56
60
  return layer;
57
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sprite-2d.js","sources":["../../../src/sprite/sprite-2d.ts"],"sourcesContent":["/**\n * `Sprite2DLayer` — pixel-coordinate sprite layer. Pure-data interface +\n * standalone Index API for add / update / remove / setFrame. The layer is\n * owned by a `SpriteRenderer` (pure-2D / HUD `depth: \"none\"` path) or by a\n * scene renderable added through `addDepthHostedSpriteLayer`\n * (`depth: \"test\" | \"test-write\"`).\n *\n * The Index API stays the storage foundation. The optional Handle API lives in\n * `sprite-2d-handle.ts` and lazily installs hooks only when handles are used.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { SpriteAtlas } from \"./shared/sprite-atlas.js\";\nimport { resolveSpriteFrame } from \"./shared/sprite-atlas.js\";\nimport type { Sprite2DCustomShader } from \"./sprite-custom-shader.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { SpriteBlendDescriptor } from \"./sprite-blend.js\";\nimport { spriteBlendAlpha } from \"./sprite-blend.js\";\n\n/**\n * Output blend mode for a sprite layer — a pure-data descriptor value. Import one of\n * `spriteBlendAlpha` (default), `spriteBlendPremultiplied`, or `spriteBlendAdditive` and pass\n * it as `Sprite2DLayerOptions.blendMode`. (Type alias of {@link SpriteBlendDescriptor}.)\n */\nexport type SpriteBlendMode = SpriteBlendDescriptor;\n\n/** Depth participation. `\"none\"` uses `SpriteRenderer`; depth-enabled modes use `addToScene`. */\nexport type Sprite2DDepthMode = \"none\" | \"test\" | \"test-write\";\n\n/** Per-layer 2D camera (pan / zoom / rotation). Identity = pixel-perfect HUD. */\nexport interface Sprite2DView {\n positionPx: [number, number];\n zoom: number;\n rotation: number;\n}\n\n/** Options accepted by `createSprite2DLayer`. */\nexport interface Sprite2DLayerOptions {\n capacity?: number;\n blendMode?: SpriteBlendMode;\n opacity?: number;\n visible?: boolean;\n order?: number;\n view?: Partial<Sprite2DView>;\n depth?: Sprite2DDepthMode;\n /**\n * Layer-wide rotation / scaling pivot in normalised sprite-local space\n * (`[0,0]` = top-left, `[0.5, 0.5]` = center, `[1,1]` = bottom-right).\n * The pivot point of every sprite in the layer lands at its `positionPx`\n * and is the center of `rotation`. Defaults to `[0.5, 0.5]` (center) to\n * match Babylon.js sprite behavior. Per-sprite / per-frame pivot is a\n * future PR — most 2D HUD layers want one uniform pivot anyway.\n */\n pivot?: [number, number];\n /**\n * Opt-in per-layer custom fragment shader (see `createSprite2DCustomShader`). Works on both\n * pure-2D (`depth: \"none\"`) layers drawn by a `SpriteRenderer` and depth-hosted\n * (`depth: \"test\" | \"test-write\"`) layers. Drives procedural effects (animated sky, clouds,\n * water / heat shimmer, twinkle, vignette) from a built-in `fx.time` clock plus an optional\n * `fx.params` vec4 set via `setSprite2DShaderParams`.\n */\n customShader?: Sprite2DCustomShader;\n /**\n * Default NDC depth (`0` = near, `1` = far) for sprites added to this layer when their\n * `Sprite2DProps.z` is omitted. Only meaningful for `depth: \"test\" | \"test-write\"` layers\n * (depth-hosted sprites added to a `SceneContext` via `addDepthHostedSpriteLayer`).\n *\n * Depth-hosted layers store one Z per sprite (slot [13] of their 14-float instance buffer),\n * so a single layer can mix sprites at different depths — e.g. one in front of a box, one\n * behind it. Pure-2D layers (`depth: \"none\"`) use the 13-float HUD layout and carry no Z slot.\n * Defaults to `0.5`. Mutating `layer.layerZ` after sprites have been added does **not**\n * retroactively change them; it only affects sprites added afterwards. To move an existing\n * depth-hosted sprite, call `updateSprite2DIndex(layer, idx, { z: … })`.\n */\n layerZ?: number;\n /**\n * Opt-in per-sprite UV scroll offset. When `true`, every sprite gains two extra instance\n * floats (`uvOffset.xy`) added to its sampled UV in the vertex stage — enabling parallax /\n * infinite-scroll backgrounds without re-uploading texture coordinates. Set the offset per\n * sprite via `Sprite2DProps.uvOffset` (on add) or `setSprite2DUvOffset` (live).\n *\n * **Zero cross-scene cost:** layers created without `uvScroll` keep the narrow 13/14-float\n * layout, the base vertex attributes, and the base WGSL — they ship none of the uvScroll\n * widening. The wider stride, the extra `@location(7)` attribute, and the `+ iUvOffset` WGSL\n * are gated directly on this per-layer flag. Defaults to `false`.\n *\n * Pairs naturally with a tileable atlas texture sampled in `repeat` wrap mode\n * (`loadSpriteAtlas(..., { textureOptions: { addressModeU: \"repeat\", addressModeV: \"repeat\" } })`).\n */\n uvScroll?: boolean;\n}\n\n/** A `Sprite2DLayer` — pure data, no methods. */\nexport interface Sprite2DLayer {\n /** @internal */\n readonly _entityType: \"sprite-2d-layer\";\n readonly atlas: SpriteAtlas;\n readonly depth: Sprite2DDepthMode;\n readonly blendMode: SpriteBlendMode;\n opacity: number;\n visible: boolean;\n order: number;\n view: Sprite2DView;\n /** Layer-wide pivot in normalised sprite-local space; see `Sprite2DLayerOptions.pivot`. */\n pivot: [number, number];\n /**\n * Opt-in custom fragment shader for this layer; see `Sprite2DLayerOptions.customShader`.\n * **Absent** (not `null`) on plain layers — never default-initialized, so the always-loaded\n * path carries zero custom-shader bytes (see `sprite-fx-hook.ts`). Present only when the\n * layer was created with a `customShader`.\n */\n readonly customShader?: Sprite2DCustomShader;\n /**\n * User `fx.params` vec4 fed to a custom shader each frame; mutate via `setSprite2DShaderParams`.\n * **Absent** on plain layers (only allocated for custom-shader layers, or lazily by the setter).\n */\n shaderParams?: [number, number, number, number];\n /**\n * @internal Opt-in per-sprite UV-scroll flag; see `Sprite2DLayerOptions.uvScroll`. **Absent** (not `false`)\n * on plain layers — never default-initialized, so the always-loaded path and every non-scroll\n * sprite scene keep the narrow layout and base shader. Present (`true`) only when the layer was\n * created with `uvScroll: true`, which widens the instance stride by two floats (`uvOffset.xy`).\n */\n readonly _uvScroll?: boolean;\n /** Default NDC depth for newly added sprites; see `Sprite2DLayerOptions.layerZ`. */\n layerZ: number;\n readonly count: number;\n\n /** @internal Capacity of the per-instance buffer (in sprites). */\n _capacity: number;\n /** @internal Per-instance stride in floats; 13 for pure-2D, 14 for depth-hosted. */\n readonly _instanceFloatsPerSprite: number;\n /** @internal Per-instance stride in bytes; 52 for pure-2D, 56 for depth-hosted. */\n readonly _instanceStrideBytes: number;\n /** @internal Per-instance CPU staging buffer; layout = `_instanceFloatsPerSprite` per sprite. */\n _instanceData: Float32Array;\n /**\n * @internal CPU-only side buffer holding the **true** (un-hidden) size of every sprite,\n * laid out as `[w0, h0, w1, h1, …]` (`SAVED_SIZE_FLOATS_PER_SPRITE` = 2 floats per sprite).\n *\n * **Invariant:** this buffer always holds the sprite's real size, regardless of visibility.\n * It exists because `visible: false` is implemented by zeroing the GPU-side size slots\n * (degenerate quad → free rasterizer cull) — a free hide on the GPU at the cost of 8 B per\n * sprite on the CPU. Without this shadow, a `visible: true` patch that omits `sizePx`\n * would have no way to recover the original size. Grown in lockstep with `_instanceData`.\n */\n _savedSize: Float32Array;\n /** @internal Bumped on any structural / per-instance edit; renderer compares. */\n _version: number;\n /** @internal Min dirty index inclusive (for partial uploads). */\n _dirtyMin: number;\n /** @internal Max dirty index exclusive. */\n _dirtyMax: number;\n /** @internal Optional hooks installed by the opt-in handle module. */\n _handleHooks?: Sprite2DIndexHandleHooks;\n}\n\n/** @internal Lazy hooks used by the opt-in Handle API to track swap-removes. */\nexport interface Sprite2DIndexHandleHooks {\n readonly removeIndex: (index: number, last: number) => void;\n readonly clear: () => void;\n}\n\n/** Per-sprite init record passed to `addSprite2DIndex` / `updateSprite2DIndex`. */\nexport interface Sprite2DProps {\n positionPx: [number, number];\n sizePx?: [number, number];\n frame?: number;\n rotation?: number;\n color?: [number, number, number, number];\n flipX?: boolean;\n flipY?: boolean;\n visible?: boolean;\n /** Reserved for picking. Accepted but unused today. */\n pickable?: boolean;\n /** Reserved for clip animation. Accepted but unused today. */\n clip?: unknown;\n /**\n * Per-sprite NDC depth (`0` = near, `1` = far). Only stored and consumed by depth-hosted\n * layers (`depth: \"test\" | \"test-write\"`); pure-2D HUD layers use a 13-float layout and\n * do not allocate a Z slot. When omitted on add for a depth-hosted layer, defaults to the\n * **owning layer's** `layerZ` at the moment of insertion. When omitted on update, the\n * sprite's existing Z is preserved. Mutate freely — the next binding update will re-upload\n * only the dirty range.\n */\n z?: number;\n /**\n * Per-sprite UV scroll offset added to the sampled UV in the vertex stage. Only stored and\n * consumed by `uvScroll` layers (created with `Sprite2DLayerOptions.uvScroll: true`); non-scroll\n * layers use the narrow 13/14-float layout and do not allocate a uvOffset slot. When omitted on\n * add for a uvScroll layer, defaults to `[0, 0]`. When omitted on update, the sprite's existing\n * offset is preserved. Live updates: `setSprite2DUvOffset`.\n */\n uvOffset?: [number, number];\n}\n\n/**\n * Pure-2D per-instance vertex layout (13 floats = 52 bytes, `depth: \"none\"`):\n * ```\n * [0..1] positionPx.xy (float32x2 @ offset 0)\n * [2..3] sizePx.xy (float32x2 @ offset 8)\n * [4..5] uvMin.xy (float32x2 @ offset 16)\n * [6..7] uvMax.xy (float32x2 @ offset 24)\n * [8] rotation (float32 @ offset 32)\n * [9..12] colorRGBA (float32x4 @ offset 36)\n * ```\n *\n * Depth-hosted layers (`depth: \"test\" | \"test-write\"`) extend this to 14 floats = 56 bytes:\n * ```\n * [13] z (NDC depth) (float32 @ offset 52, consumed only by depth-hosted pipelines)\n * ```\n *\n * `uvScroll` layers (created with `Sprite2DLayerOptions.uvScroll: true`) append two more floats\n * (`uvOffset.xy`) *after* the base layout, orthogonally to depth:\n * ```\n * pure-2D + uvScroll (15 floats = 60 bytes): [13..14] uvOffset.xy (float32x2 @ offset 52)\n * depth + uvScroll (16 floats = 64 bytes): [14..15] uvOffset.xy (float32x2 @ offset 56)\n * ```\n * The depth Z stays at slot [13]; uvOffset is appended after it. Non-scroll layers carry no\n * uvOffset slot and ship none of the widening (see `Sprite2DLayerOptions.uvScroll`).\n *\n * Visibility (`visible: false`) is implemented by zeroing slots [2..3]; the sprite's true\n * size lives in `layer._savedSize` so a later `visible: true` (without re-supplying\n * `sizePx`) can restore it. See `_savedSize` for the invariant.\n */\nexport const PURE_2D_INSTANCE_FLOATS_PER_SPRITE = 13;\nexport const DEPTH_INSTANCE_FLOATS_PER_SPRITE = 14;\n/** @internal Pure-2D per-sprite stride in bytes. */\nexport const PURE_2D_INSTANCE_STRIDE_BYTES = PURE_2D_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted per-sprite stride in bytes. */\nexport const DEPTH_INSTANCE_STRIDE_BYTES = DEPTH_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Extra floats appended per sprite when `uvScroll` is enabled: `uvOffset.xy`. */\nexport const UVSCROLL_EXTRA_FLOATS_PER_SPRITE = 2;\n/** @internal Pure-2D + uvScroll per-sprite stride in floats (13 + 2). */\nexport const PURE_2D_UVSCROLL_FLOATS_PER_SPRITE = PURE_2D_INSTANCE_FLOATS_PER_SPRITE + UVSCROLL_EXTRA_FLOATS_PER_SPRITE;\n/** @internal Depth-hosted + uvScroll per-sprite stride in floats (14 + 2). */\nexport const DEPTH_UVSCROLL_FLOATS_PER_SPRITE = DEPTH_INSTANCE_FLOATS_PER_SPRITE + UVSCROLL_EXTRA_FLOATS_PER_SPRITE;\n/** @internal Pure-2D + uvScroll per-sprite stride in bytes (60). */\nexport const PURE_2D_UVSCROLL_STRIDE_BYTES = PURE_2D_UVSCROLL_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted + uvScroll per-sprite stride in bytes (64). */\nexport const DEPTH_UVSCROLL_STRIDE_BYTES = DEPTH_UVSCROLL_FLOATS_PER_SPRITE * 4;\n/** @internal Per-sprite stride (in floats) of the `_savedSize` shadow buffer: `[w, h]`. */\nexport const SAVED_SIZE_FLOATS_PER_SPRITE = 2;\n\nconst DEFAULT_CAPACITY = 16;\n\n/** Create a new (empty) `Sprite2DLayer` backed by `atlas`. */\nexport function createSprite2DLayer(atlas: SpriteAtlas, opts: Sprite2DLayerOptions = {}): Sprite2DLayer {\n const depth = opts.depth ?? \"none\";\n const blendMode = opts.blendMode ?? spriteBlendAlpha;\n\n const capacity = Math.max(1, opts.capacity ?? DEFAULT_CAPACITY);\n const view: Sprite2DView = {\n positionPx: [opts.view?.positionPx?.[0] ?? 0, opts.view?.positionPx?.[1] ?? 0],\n zoom: opts.view?.zoom ?? 1,\n rotation: opts.view?.rotation ?? 0,\n };\n\n const uvScroll = opts.uvScroll === true;\n const baseFloatsPerSprite = depth === \"none\" ? PURE_2D_INSTANCE_FLOATS_PER_SPRITE : DEPTH_INSTANCE_FLOATS_PER_SPRITE;\n const instanceFloatsPerSprite = uvScroll ? baseFloatsPerSprite + UVSCROLL_EXTRA_FLOATS_PER_SPRITE : baseFloatsPerSprite;\n const instanceStrideBytes = instanceFloatsPerSprite * 4;\n const instanceData = new F32(capacity * instanceFloatsPerSprite);\n const layer: Sprite2DLayer = {\n _entityType: \"sprite-2d-layer\",\n atlas,\n depth,\n blendMode,\n opacity: opts.opacity ?? 1,\n visible: opts.visible ?? true,\n order: opts.order ?? 0,\n view,\n pivot: [opts.pivot?.[0] ?? 0.5, opts.pivot?.[1] ?? 0.5],\n layerZ: opts.layerZ ?? 0.5,\n count: 0,\n _capacity: capacity,\n _instanceFloatsPerSprite: instanceFloatsPerSprite,\n _instanceStrideBytes: instanceStrideBytes,\n _instanceData: instanceData,\n _savedSize: new F32(capacity * SAVED_SIZE_FLOATS_PER_SPRITE),\n _version: 0,\n _dirtyMin: 0,\n _dirtyMax: 0,\n };\n // Zero-default-init discipline: the base layer never names `_uvScroll`. Set it only when the\n // caller opted in, so plain layers keep the field off the always-loaded path and read as narrow.\n if (uvScroll) {\n (layer as { _uvScroll?: boolean })._uvScroll = true;\n }\n // Zero-default-init discipline: the base layer never names `customShader` / `shaderParams`.\n // When (and only when) a custom shader was supplied, the registered hook copies it on — the\n // impl lives in the tree-shaken `sprite-custom-shader` module, so plain scenes ship none of it.\n _getSpriteFxHook()?.initLayer(layer, opts);\n return layer;\n}\n\n/**\n * Set the user `fx.params` vec4 fed to this layer's custom shader (`createSprite2DCustomShader`)\n * each frame. No visual effect unless the layer was created with a `customShader`. Read in WGSL\n * as `fx.params`. Mutates in place; the renderer re-uploads the small FX UBO next frame.\n */\nexport function setSprite2DShaderParams(layer: Sprite2DLayer, params: readonly [number, number, number, number]): void {\n // Lazy-allocate: the base layer never names `shaderParams` (the custom-shader hook sets it only\n // when `opts.customShader` is present), so a plain layer keeps the field off the always-loaded path.\n const target = (layer.shaderParams ??= [0, 0, 0, 0]);\n target[0] = params[0];\n target[1] = params[1];\n target[2] = params[2];\n target[3] = params[3];\n}\n\n/**\n * Set the per-sprite UV scroll offset for one sprite of a `uvScroll` layer (live). The two floats\n * are added to the sprite's sampled UV in the vertex stage — driving parallax / infinite-scroll\n * backgrounds without re-uploading texture coordinates. Marks only this sprite's range dirty.\n *\n * Throws if the layer was not created with `Sprite2DLayerOptions.uvScroll: true` (non-scroll layers\n * carry no uvOffset slot) or if `index` is out of range.\n */\nexport function setSprite2DUvOffset(layer: Sprite2DLayer, index: number, uvOffset: readonly [number, number]): void {\n if (!layer._uvScroll) {\n throw new Error(\"setSprite2DUvOffset: layer was not created with uvScroll: true.\");\n }\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DUvOffset: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const uvSlot = base + (layer.depth !== \"none\" ? 14 : 13);\n layer._instanceData[uvSlot] = uvOffset[0];\n layer._instanceData[uvSlot + 1] = uvOffset[1];\n markDirty(layer, index, index + 1);\n}\n\nfunction growCapacity(layer: Sprite2DLayer, minCapacity: number): void {\n let cap = layer._capacity;\n while (cap < minCapacity) {\n cap *= 2;\n }\n const next = new F32(cap * layer._instanceFloatsPerSprite);\n next.set(layer._instanceData);\n layer._instanceData = next;\n const nextSaved = new F32(cap * SAVED_SIZE_FLOATS_PER_SPRITE);\n nextSaved.set(layer._savedSize);\n layer._savedSize = nextSaved;\n layer._capacity = cap;\n}\n\nfunction setSprite2DCount(layer: Sprite2DLayer, count: number): void {\n (layer as { count: number }).count = count;\n}\n\n/**\n * Write one sprite's instance data into `layer._instanceData[base..base+layer._instanceFloatsPerSprite]`.\n *\n * Two call sites with different shapes:\n * - **add**: `prev === null`. `props` is a full `Sprite2DProps` (positionPx required).\n * Unspecified fields take their documented defaults (size=frame.sourceSizePx or 0,\n * UVs=[0,0,1,1], rotation=0, color=opaque white, visible=true).\n * - **update**: `prev` is the existing per-layer instance slice. Unspecified fields are preserved.\n *\n * Resolution rules (per field): `props` value if given, else (on add) the default, else `prev`.\n * `frame` is a higher-level intent: when supplied it stomps the four UV slots from the atlas\n * (then `flipX`/`flipY` swap them). It does **not** by itself imply a size change — `sizePx`\n * remains independent — but on add, a missing `sizePx` falls back to `frame.sourceSizePx`.\n *\n * **Visibility model (the part that needs explaining):**\n * - `_savedSize[slot]` always stores the sprite's *true* size (unaffected by visibility).\n * - `data[base+2..+3]` (the GPU-visible size) is `_savedSize` when visible, else `(0, 0)`.\n * - We detect previous visibility by checking `prev[2]==0 && prev[3]==0` (only hidden sprites\n * have zeroed GPU size). The CPU shadow gives us back the true size for free.\n */\nfunction writeInstance(layer: Sprite2DLayer, slotIndex: number, props: Partial<Sprite2DProps>, prev: Float32Array | null): void {\n const data = layer._instanceData;\n const base = slotIndex * layer._instanceFloatsPerSprite;\n const savedBase = slotIndex * SAVED_SIZE_FLOATS_PER_SPRITE; // [w, h] per sprite\n const isAdd = prev === null;\n\n // Optional frame lookup (used for UV stomp + size default on add).\n const frame = props.frame !== undefined ? layer.atlas.frames[resolveSpriteFrame(layer.atlas, props.frame)]! : null;\n\n // ── Position (required on add; preserved on update if omitted) ──────────────────────\n const posX = props.positionPx ? props.positionPx[0] : prev![0]!;\n const posY = props.positionPx ? props.positionPx[1] : prev![1]!;\n\n // ── True size (props.sizePx → frame default → previous true size) ───────────────────\n // The shadow buffer makes \"previous true size\" cheap and unambiguous regardless of visibility.\n let trueW: number;\n let trueH: number;\n if (props.sizePx) {\n trueW = props.sizePx[0];\n trueH = props.sizePx[1];\n } else if (frame) {\n trueW = frame.sourceSizePx[0];\n trueH = frame.sourceSizePx[1];\n } else if (isAdd) {\n trueW = 0;\n trueH = 0;\n } else {\n trueW = layer._savedSize[savedBase]!;\n trueH = layer._savedSize[savedBase + 1]!;\n }\n layer._savedSize[savedBase] = trueW;\n layer._savedSize[savedBase + 1] = trueH;\n\n // ── Visibility (props.visible → preserved → default true on add) ────────────────────\n let visible: boolean;\n if (props.visible !== undefined) {\n visible = props.visible;\n } else if (isAdd) {\n visible = true;\n } else {\n // Previous sprite was hidden iff its GPU size was zeroed.\n visible = prev![2]! !== 0 || prev![3]! !== 0;\n }\n\n // ── UVs (frame stomps; else preserved; else default [0,0,1,1] on add) ───────────────\n // flipX/flipY apply on top, by swapping the U/V endpoints.\n let uMin: number;\n let vMin: number;\n let uMax: number;\n let vMax: number;\n if (frame) {\n uMin = frame.uvMin[0];\n vMin = frame.uvMin[1];\n uMax = frame.uvMax[0];\n vMax = frame.uvMax[1];\n } else if (isAdd) {\n uMin = 0;\n vMin = 0;\n uMax = 1;\n vMax = 1;\n } else {\n uMin = prev![4]!;\n vMin = prev![5]!;\n uMax = prev![6]!;\n vMax = prev![7]!;\n }\n if (props.flipX === true) {\n const t = uMin;\n uMin = uMax;\n uMax = t;\n }\n if (props.flipY === true) {\n const t = vMin;\n vMin = vMax;\n vMax = t;\n }\n\n // ── Rotation ────────────────────────────────────────────────────────────────────────\n const rotation = props.rotation ?? (prev ? prev[8]! : 0);\n\n // ── Per-instance Z (depth-hosted only) ──────────────────────────────────────────────\n // Pure-2D layers intentionally have no slot [13]; `z` is accepted by the public API but\n // not allocated, uploaded, declared, or fetched by HUD pipelines.\n const hasDepthSlot = layer.depth !== \"none\";\n const z = hasDepthSlot ? (props.z ?? (prev ? prev[13]! : layer.layerZ)) : 0;\n\n // ── Write the float slots ──────────────────────────────────────────────────────────\n data[base + 0] = posX;\n data[base + 1] = posY;\n data[base + 2] = visible ? trueW : 0;\n data[base + 3] = visible ? trueH : 0;\n data[base + 4] = uMin;\n data[base + 5] = vMin;\n data[base + 6] = uMax;\n data[base + 7] = vMax;\n data[base + 8] = rotation;\n\n // ── Color (float32x4 to match Babylon.js SpriteRenderer's color precision) ─────────\n if (props.color) {\n data[base + 9] = props.color[0];\n data[base + 10] = props.color[1];\n data[base + 11] = props.color[2];\n data[base + 12] = props.color[3];\n } else if (isAdd) {\n data[base + 9] = 1;\n data[base + 10] = 1;\n data[base + 11] = 1;\n data[base + 12] = 1;\n }\n // else: previous color floats are already in place — nothing to write.\n\n // ── Per-instance Z (slot [13], depth-hosted layout only) ───────────────────────────\n if (hasDepthSlot) {\n data[base + 13] = z;\n }\n\n // ── Per-sprite uvOffset (uvScroll layout only) ─────────────────────────────────────\n // Appended after the base layout: slot [13] for pure-2D, slot [14] for depth-hosted\n // (the depth Z keeps slot [13]). props → preserved → default [0,0] on add.\n if (layer._uvScroll) {\n const uvSlot = base + (hasDepthSlot ? 14 : 13);\n if (props.uvOffset) {\n data[uvSlot] = props.uvOffset[0];\n data[uvSlot + 1] = props.uvOffset[1];\n } else if (isAdd) {\n data[uvSlot] = 0;\n data[uvSlot + 1] = 0;\n }\n // else: previous uvOffset floats are already in place — nothing to write.\n }\n}\n\nfunction markDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n if (layer._dirtyMin >= layer._dirtyMax) {\n layer._dirtyMin = lo;\n layer._dirtyMax = hi;\n } else {\n if (lo < layer._dirtyMin) {\n layer._dirtyMin = lo;\n }\n if (hi > layer._dirtyMax) {\n layer._dirtyMax = hi;\n }\n }\n layer._version = (layer._version + 1) | 0;\n}\n\n/** Add one sprite. Returns its index. Grows capacity as needed. */\nexport function addSprite2DIndex(layer: Sprite2DLayer, props: Sprite2DProps): number {\n if (props.positionPx === undefined) {\n throw new Error(\"addSprite2DIndex: props.positionPx is required.\");\n }\n const idx = layer.count;\n if (idx >= layer._capacity) {\n growCapacity(layer, idx + 1);\n }\n writeInstance(layer, idx, props, null);\n setSprite2DCount(layer, layer.count + 1);\n markDirty(layer, idx, idx + 1);\n return idx;\n}\n\n/** Patch one sprite. Unspecified fields are preserved. */\nexport function updateSprite2DIndex(layer: Sprite2DLayer, index: number, patch: Partial<Sprite2DProps>): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`updateSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const prev = layer._instanceData.subarray(base, base + layer._instanceFloatsPerSprite);\n writeInstance(layer, index, patch, prev);\n markDirty(layer, index, index + 1);\n}\n\n/** Swap-remove a sprite. The last sprite (if any) takes its slot. */\nexport function removeSprite2DIndex(layer: Sprite2DLayer, index: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`removeSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const last = layer.count - 1;\n layer._handleHooks?.removeIndex(index, last);\n if (index !== last) {\n layer._instanceData.copyWithin(index * layer._instanceFloatsPerSprite, last * layer._instanceFloatsPerSprite, (last + 1) * layer._instanceFloatsPerSprite);\n // Carry the swapped sprite's saved-size shadow with it (`[w, h]` per sprite).\n layer._savedSize.copyWithin(index * SAVED_SIZE_FLOATS_PER_SPRITE, last * SAVED_SIZE_FLOATS_PER_SPRITE, (last + 1) * SAVED_SIZE_FLOATS_PER_SPRITE);\n }\n // Clear the now-unused tail saved-size slot so a future re-add starts clean.\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE] = 0;\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE + 1] = 0;\n setSprite2DCount(layer, last);\n markDirty(layer, index, index + 1);\n}\n\n/** Clear all sprites from a layer while preserving allocated capacity. */\nexport function clearSprite2DLayer(layer: Sprite2DLayer): void {\n const count = layer.count;\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n layer._handleHooks?.clear();\n if (count === 0) {\n return;\n }\n layer._savedSize.fill(0, 0, count * SAVED_SIZE_FLOATS_PER_SPRITE);\n setSprite2DCount(layer, 0);\n layer._version = (layer._version + 1) | 0;\n}\n\n/**\n * Update only the frame UVs for one sprite.\n *\n * The sprite keeps its explicit `sizePx`/saved size. For atlas-driven size\n * changes, call `updateSprite2DIndex(layer, index, { frame, sizePx })`.\n * Existing flip state is preserved for non-degenerate UV ranges.\n */\nexport function setSprite2DFrameIndex(layer: Sprite2DLayer, index: number, frame: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DFrameIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const frameIdx = resolveSpriteFrame(layer.atlas, frame);\n const f = layer.atlas.frames[frameIdx]!;\n const base = index * layer._instanceFloatsPerSprite;\n const flipX = layer._instanceData[base + 4]! > layer._instanceData[base + 6]!;\n const flipY = layer._instanceData[base + 5]! > layer._instanceData[base + 7]!;\n layer._instanceData[base + 4] = flipX ? f.uvMax[0] : f.uvMin[0];\n layer._instanceData[base + 5] = flipY ? f.uvMax[1] : f.uvMin[1];\n layer._instanceData[base + 6] = flipX ? f.uvMin[0] : f.uvMax[0];\n layer._instanceData[base + 7] = flipY ? f.uvMin[1] : f.uvMax[1];\n markDirty(layer, index, index + 1);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sprite-2d.js","sources":["../../../src/sprite/sprite-2d.ts"],"sourcesContent":["/**\n * `Sprite2DLayer` — pixel-coordinate sprite layer. Pure-data interface +\n * standalone Index API for add / update / remove / setFrame. The layer is\n * owned by a `SpriteRenderer` (pure-2D / HUD `depth: \"none\"` path) or by a\n * scene renderable added through `addDepthHostedSpriteLayer`\n * (`depth: \"test\" | \"test-write\"`).\n *\n * The Index API stays the storage foundation. The optional Handle API lives in\n * `sprite-2d-handle.ts` and lazily installs hooks only when handles are used.\n */\nimport { F32 } from \"../engine/typed-arrays.js\";\nimport type { SpriteAtlas } from \"./shared/sprite-atlas.js\";\nimport { resolveSpriteFrame } from \"./shared/sprite-atlas.js\";\nimport type { Sprite2DCustomShader } from \"./sprite-custom-shader.js\";\nimport { _getSpriteFxHook } from \"./sprite-fx-hook.js\";\nimport type { SpriteBlendDescriptor } from \"./sprite-blend.js\";\nimport { spriteBlendAlpha } from \"./sprite-blend.js\";\n\n/**\n * Output blend mode for a sprite layer — a pure-data descriptor value. Import one of\n * `spriteBlendAlpha` (default), `spriteBlendPremultiplied`, or `spriteBlendAdditive` and pass\n * it as `Sprite2DLayerOptions.blendMode`. (Type alias of {@link SpriteBlendDescriptor}.)\n */\nexport type SpriteBlendMode = SpriteBlendDescriptor;\n\n/** Depth participation. `\"none\"` uses `SpriteRenderer`; depth-enabled modes use `addToScene`. */\nexport type Sprite2DDepthMode = \"none\" | \"test\" | \"test-write\";\n\n/** Per-layer 2D camera (pan / zoom / rotation). Identity = pixel-perfect HUD. */\nexport interface Sprite2DView {\n positionPx: [number, number];\n zoom: number;\n rotation: number;\n}\n\n/** Options accepted by `createSprite2DLayer`. */\nexport interface Sprite2DLayerOptions {\n capacity?: number;\n blendMode?: SpriteBlendMode;\n opacity?: number;\n /**\n * Coverage gamma for anti-aliased edges (text rendering). When set to a value other than 1,\n * the sampled texture alpha is raised to `1/coverageGamma` in the fragment shader, thickening\n * anti-aliased edges to mimic the gamma-space blending of native text rasterizers\n * (DirectWrite/CoreText \"stem darkening\"). Intended for glyph-atlas (bitmap text) layers drawn\n * into an sRGB (linear-blended) surface, where correct linear AA otherwise makes text look\n * lighter/thinner. Values \\>1 thicken; 1 (default) is a no-op and ships the base fragment.\n *\n * The per-fragment `pow` is gated on this create-time opt-in (like `uvScroll`), so only gamma\n * layers run it. The plumbing (one extra `Layer` UBO `vec4`, the gamma shader permutation, and\n * this option field) is, however, in the always-loaded sprite path, so it adds a small fixed\n * cost (~0.3 KB raw) to every sprite scene — reflected in the sprite-scene bundle ceilings.\n */\n coverageGamma?: number;\n visible?: boolean;\n order?: number;\n view?: Partial<Sprite2DView>;\n depth?: Sprite2DDepthMode;\n /**\n * Layer-wide rotation / scaling pivot in normalised sprite-local space\n * (`[0,0]` = top-left, `[0.5, 0.5]` = center, `[1,1]` = bottom-right).\n * The pivot point of every sprite in the layer lands at its `positionPx`\n * and is the center of `rotation`. Defaults to `[0.5, 0.5]` (center) to\n * match Babylon.js sprite behavior. Per-sprite / per-frame pivot is a\n * future PR — most 2D HUD layers want one uniform pivot anyway.\n */\n pivot?: [number, number];\n /**\n * Opt-in per-layer custom fragment shader (see `createSprite2DCustomShader`). Works on both\n * pure-2D (`depth: \"none\"`) layers drawn by a `SpriteRenderer` and depth-hosted\n * (`depth: \"test\" | \"test-write\"`) layers. Drives procedural effects (animated sky, clouds,\n * water / heat shimmer, twinkle, vignette) from a built-in `fx.time` clock plus an optional\n * `fx.params` vec4 set via `setSprite2DShaderParams`.\n */\n customShader?: Sprite2DCustomShader;\n /**\n * Default NDC depth (`0` = near, `1` = far) for sprites added to this layer when their\n * `Sprite2DProps.z` is omitted. Only meaningful for `depth: \"test\" | \"test-write\"` layers\n * (depth-hosted sprites added to a `SceneContext` via `addDepthHostedSpriteLayer`).\n *\n * Depth-hosted layers store one Z per sprite (slot [13] of their 14-float instance buffer),\n * so a single layer can mix sprites at different depths — e.g. one in front of a box, one\n * behind it. Pure-2D layers (`depth: \"none\"`) use the 13-float HUD layout and carry no Z slot.\n * Defaults to `0.5`. Mutating `layer.layerZ` after sprites have been added does **not**\n * retroactively change them; it only affects sprites added afterwards. To move an existing\n * depth-hosted sprite, call `updateSprite2DIndex(layer, idx, { z: … })`.\n */\n layerZ?: number;\n /**\n * Opt-in per-sprite UV scroll offset. When `true`, every sprite gains two extra instance\n * floats (`uvOffset.xy`) added to its sampled UV in the vertex stage — enabling parallax /\n * infinite-scroll backgrounds without re-uploading texture coordinates. Set the offset per\n * sprite via `Sprite2DProps.uvOffset` (on add) or `setSprite2DUvOffset` (live).\n *\n * **Zero cross-scene cost:** layers created without `uvScroll` keep the narrow 13/14-float\n * layout, the base vertex attributes, and the base WGSL — they ship none of the uvScroll\n * widening. The wider stride, the extra `@location(7)` attribute, and the `+ iUvOffset` WGSL\n * are gated directly on this per-layer flag. Defaults to `false`.\n *\n * Pairs naturally with a tileable atlas texture sampled in `repeat` wrap mode\n * (`loadSpriteAtlas(..., { textureOptions: { addressModeU: \"repeat\", addressModeV: \"repeat\" } })`).\n */\n uvScroll?: boolean;\n}\n\n/** A `Sprite2DLayer` — pure data, no methods. */\nexport interface Sprite2DLayer {\n /** @internal */\n readonly _entityType: \"sprite-2d-layer\";\n readonly atlas: SpriteAtlas;\n readonly depth: Sprite2DDepthMode;\n readonly blendMode: SpriteBlendMode;\n opacity: number;\n /** Coverage gamma applied to anti-aliased edges; see `Sprite2DLayerOptions.coverageGamma`.\n * Default 1 (no-op). Read each frame into the layer UBO. Mutating it live changes the\n * thickness, but the per-fragment `pow` only runs on layers created with `coverageGamma !== 1`. */\n coverageGamma: number;\n visible: boolean;\n order: number;\n view: Sprite2DView;\n /** Layer-wide pivot in normalised sprite-local space; see `Sprite2DLayerOptions.pivot`. */\n pivot: [number, number];\n /**\n * Opt-in custom fragment shader for this layer; see `Sprite2DLayerOptions.customShader`.\n * **Absent** (not `null`) on plain layers — never default-initialized, so the always-loaded\n * path carries zero custom-shader bytes (see `sprite-fx-hook.ts`). Present only when the\n * layer was created with a `customShader`.\n */\n readonly customShader?: Sprite2DCustomShader;\n /**\n * User `fx.params` vec4 fed to a custom shader each frame; mutate via `setSprite2DShaderParams`.\n * **Absent** on plain layers (only allocated for custom-shader layers, or lazily by the setter).\n */\n shaderParams?: [number, number, number, number];\n /**\n * @internal Opt-in per-sprite UV-scroll flag; see `Sprite2DLayerOptions.uvScroll`. **Absent** (not `false`)\n * on plain layers — never default-initialized, so the always-loaded path and every non-scroll\n * sprite scene keep the narrow layout and base shader. Present (`true`) only when the layer was\n * created with `uvScroll: true`, which widens the instance stride by two floats (`uvOffset.xy`).\n */\n readonly _uvScroll?: boolean;\n /**\n * @internal Opt-in coverage-gamma permutation flag; see `Sprite2DLayerOptions.coverageGamma`.\n * **Absent** (not `false`) on plain layers — never default-initialized, so the always-loaded\n * path keeps the base shader. Present (`true`) only when the layer was created with\n * `coverageGamma !== 1`, which selects the shader permutation that applies `pow` to edge coverage.\n */\n readonly _coverageGamma?: boolean;\n /** Default NDC depth for newly added sprites; see `Sprite2DLayerOptions.layerZ`. */\n layerZ: number;\n readonly count: number;\n\n /** @internal Capacity of the per-instance buffer (in sprites). */\n _capacity: number;\n /** @internal Per-instance stride in floats; 13 for pure-2D, 14 for depth-hosted. */\n readonly _instanceFloatsPerSprite: number;\n /** @internal Per-instance stride in bytes; 52 for pure-2D, 56 for depth-hosted. */\n readonly _instanceStrideBytes: number;\n /** @internal Per-instance CPU staging buffer; layout = `_instanceFloatsPerSprite` per sprite. */\n _instanceData: Float32Array;\n /**\n * @internal CPU-only side buffer holding the **true** (un-hidden) size of every sprite,\n * laid out as `[w0, h0, w1, h1, …]` (`SAVED_SIZE_FLOATS_PER_SPRITE` = 2 floats per sprite).\n *\n * **Invariant:** this buffer always holds the sprite's real size, regardless of visibility.\n * It exists because `visible: false` is implemented by zeroing the GPU-side size slots\n * (degenerate quad → free rasterizer cull) — a free hide on the GPU at the cost of 8 B per\n * sprite on the CPU. Without this shadow, a `visible: true` patch that omits `sizePx`\n * would have no way to recover the original size. Grown in lockstep with `_instanceData`.\n */\n _savedSize: Float32Array;\n /** @internal Bumped on any structural / per-instance edit; renderer compares. */\n _version: number;\n /** @internal Min dirty index inclusive (for partial uploads). */\n _dirtyMin: number;\n /** @internal Max dirty index exclusive. */\n _dirtyMax: number;\n /** @internal Optional hooks installed by the opt-in handle module. */\n _handleHooks?: Sprite2DIndexHandleHooks;\n}\n\n/** @internal Lazy hooks used by the opt-in Handle API to track swap-removes. */\nexport interface Sprite2DIndexHandleHooks {\n readonly removeIndex: (index: number, last: number) => void;\n readonly clear: () => void;\n}\n\n/** Per-sprite init record passed to `addSprite2DIndex` / `updateSprite2DIndex`. */\nexport interface Sprite2DProps {\n positionPx: [number, number];\n sizePx?: [number, number];\n frame?: number;\n rotation?: number;\n color?: [number, number, number, number];\n flipX?: boolean;\n flipY?: boolean;\n visible?: boolean;\n /** Reserved for picking. Accepted but unused today. */\n pickable?: boolean;\n /** Reserved for clip animation. Accepted but unused today. */\n clip?: unknown;\n /**\n * Per-sprite NDC depth (`0` = near, `1` = far). Only stored and consumed by depth-hosted\n * layers (`depth: \"test\" | \"test-write\"`); pure-2D HUD layers use a 13-float layout and\n * do not allocate a Z slot. When omitted on add for a depth-hosted layer, defaults to the\n * **owning layer's** `layerZ` at the moment of insertion. When omitted on update, the\n * sprite's existing Z is preserved. Mutate freely — the next binding update will re-upload\n * only the dirty range.\n */\n z?: number;\n /**\n * Per-sprite UV scroll offset added to the sampled UV in the vertex stage. Only stored and\n * consumed by `uvScroll` layers (created with `Sprite2DLayerOptions.uvScroll: true`); non-scroll\n * layers use the narrow 13/14-float layout and do not allocate a uvOffset slot. When omitted on\n * add for a uvScroll layer, defaults to `[0, 0]`. When omitted on update, the sprite's existing\n * offset is preserved. Live updates: `setSprite2DUvOffset`.\n */\n uvOffset?: [number, number];\n}\n\n/**\n * Pure-2D per-instance vertex layout (13 floats = 52 bytes, `depth: \"none\"`):\n * ```\n * [0..1] positionPx.xy (float32x2 @ offset 0)\n * [2..3] sizePx.xy (float32x2 @ offset 8)\n * [4..5] uvMin.xy (float32x2 @ offset 16)\n * [6..7] uvMax.xy (float32x2 @ offset 24)\n * [8] rotation (float32 @ offset 32)\n * [9..12] colorRGBA (float32x4 @ offset 36)\n * ```\n *\n * Depth-hosted layers (`depth: \"test\" | \"test-write\"`) extend this to 14 floats = 56 bytes:\n * ```\n * [13] z (NDC depth) (float32 @ offset 52, consumed only by depth-hosted pipelines)\n * ```\n *\n * `uvScroll` layers (created with `Sprite2DLayerOptions.uvScroll: true`) append two more floats\n * (`uvOffset.xy`) *after* the base layout, orthogonally to depth:\n * ```\n * pure-2D + uvScroll (15 floats = 60 bytes): [13..14] uvOffset.xy (float32x2 @ offset 52)\n * depth + uvScroll (16 floats = 64 bytes): [14..15] uvOffset.xy (float32x2 @ offset 56)\n * ```\n * The depth Z stays at slot [13]; uvOffset is appended after it. Non-scroll layers carry no\n * uvOffset slot and ship none of the widening (see `Sprite2DLayerOptions.uvScroll`).\n *\n * Visibility (`visible: false`) is implemented by zeroing slots [2..3]; the sprite's true\n * size lives in `layer._savedSize` so a later `visible: true` (without re-supplying\n * `sizePx`) can restore it. See `_savedSize` for the invariant.\n */\nexport const PURE_2D_INSTANCE_FLOATS_PER_SPRITE = 13;\nexport const DEPTH_INSTANCE_FLOATS_PER_SPRITE = 14;\n/** @internal Pure-2D per-sprite stride in bytes. */\nexport const PURE_2D_INSTANCE_STRIDE_BYTES = PURE_2D_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted per-sprite stride in bytes. */\nexport const DEPTH_INSTANCE_STRIDE_BYTES = DEPTH_INSTANCE_FLOATS_PER_SPRITE * 4;\n/** @internal Extra floats appended per sprite when `uvScroll` is enabled: `uvOffset.xy`. */\nexport const UVSCROLL_EXTRA_FLOATS_PER_SPRITE = 2;\n/** @internal Pure-2D + uvScroll per-sprite stride in floats (13 + 2). */\nexport const PURE_2D_UVSCROLL_FLOATS_PER_SPRITE = PURE_2D_INSTANCE_FLOATS_PER_SPRITE + UVSCROLL_EXTRA_FLOATS_PER_SPRITE;\n/** @internal Depth-hosted + uvScroll per-sprite stride in floats (14 + 2). */\nexport const DEPTH_UVSCROLL_FLOATS_PER_SPRITE = DEPTH_INSTANCE_FLOATS_PER_SPRITE + UVSCROLL_EXTRA_FLOATS_PER_SPRITE;\n/** @internal Pure-2D + uvScroll per-sprite stride in bytes (60). */\nexport const PURE_2D_UVSCROLL_STRIDE_BYTES = PURE_2D_UVSCROLL_FLOATS_PER_SPRITE * 4;\n/** @internal Depth-hosted + uvScroll per-sprite stride in bytes (64). */\nexport const DEPTH_UVSCROLL_STRIDE_BYTES = DEPTH_UVSCROLL_FLOATS_PER_SPRITE * 4;\n/** @internal Per-sprite stride (in floats) of the `_savedSize` shadow buffer: `[w, h]`. */\nexport const SAVED_SIZE_FLOATS_PER_SPRITE = 2;\n\nconst DEFAULT_CAPACITY = 16;\n\n/** Create a new (empty) `Sprite2DLayer` backed by `atlas`. */\nexport function createSprite2DLayer(atlas: SpriteAtlas, opts: Sprite2DLayerOptions = {}): Sprite2DLayer {\n const depth = opts.depth ?? \"none\";\n const blendMode = opts.blendMode ?? spriteBlendAlpha;\n\n const capacity = Math.max(1, opts.capacity ?? DEFAULT_CAPACITY);\n const view: Sprite2DView = {\n positionPx: [opts.view?.positionPx?.[0] ?? 0, opts.view?.positionPx?.[1] ?? 0],\n zoom: opts.view?.zoom ?? 1,\n rotation: opts.view?.rotation ?? 0,\n };\n\n const uvScroll = opts.uvScroll === true;\n const baseFloatsPerSprite = depth === \"none\" ? PURE_2D_INSTANCE_FLOATS_PER_SPRITE : DEPTH_INSTANCE_FLOATS_PER_SPRITE;\n const instanceFloatsPerSprite = uvScroll ? baseFloatsPerSprite + UVSCROLL_EXTRA_FLOATS_PER_SPRITE : baseFloatsPerSprite;\n const instanceStrideBytes = instanceFloatsPerSprite * 4;\n const instanceData = new F32(capacity * instanceFloatsPerSprite);\n const layer: Sprite2DLayer = {\n _entityType: \"sprite-2d-layer\",\n atlas,\n depth,\n blendMode,\n opacity: opts.opacity ?? 1,\n coverageGamma: opts.coverageGamma ?? 1,\n visible: opts.visible ?? true,\n order: opts.order ?? 0,\n view,\n pivot: [opts.pivot?.[0] ?? 0.5, opts.pivot?.[1] ?? 0.5],\n layerZ: opts.layerZ ?? 0.5,\n count: 0,\n _capacity: capacity,\n _instanceFloatsPerSprite: instanceFloatsPerSprite,\n _instanceStrideBytes: instanceStrideBytes,\n _instanceData: instanceData,\n _savedSize: new F32(capacity * SAVED_SIZE_FLOATS_PER_SPRITE),\n _version: 0,\n _dirtyMin: 0,\n _dirtyMax: 0,\n };\n // Zero-default-init discipline: the base layer never names `_uvScroll`. Set it only when the\n // caller opted in, so plain layers keep the field off the always-loaded path and read as narrow.\n if (uvScroll) {\n (layer as { _uvScroll?: boolean })._uvScroll = true;\n }\n // Zero-default-init discipline: the base layer never names `_coverageGamma`. Set it only when\n // the caller opted into a non-identity gamma, so plain layers keep the base shader permutation.\n if (opts.coverageGamma != null && opts.coverageGamma !== 1) {\n (layer as { _coverageGamma?: boolean })._coverageGamma = true;\n }\n // Zero-default-init discipline: the base layer never names `customShader` / `shaderParams`.\n // When (and only when) a custom shader was supplied, the registered hook copies it on — the\n // impl lives in the tree-shaken `sprite-custom-shader` module, so plain scenes ship none of it.\n _getSpriteFxHook()?.initLayer(layer, opts);\n return layer;\n}\n\n/**\n * Set the user `fx.params` vec4 fed to this layer's custom shader (`createSprite2DCustomShader`)\n * each frame. No visual effect unless the layer was created with a `customShader`. Read in WGSL\n * as `fx.params`. Mutates in place; the renderer re-uploads the small FX UBO next frame.\n */\nexport function setSprite2DShaderParams(layer: Sprite2DLayer, params: readonly [number, number, number, number]): void {\n // Lazy-allocate: the base layer never names `shaderParams` (the custom-shader hook sets it only\n // when `opts.customShader` is present), so a plain layer keeps the field off the always-loaded path.\n const target = (layer.shaderParams ??= [0, 0, 0, 0]);\n target[0] = params[0];\n target[1] = params[1];\n target[2] = params[2];\n target[3] = params[3];\n}\n\n/**\n * Set the per-sprite UV scroll offset for one sprite of a `uvScroll` layer (live). The two floats\n * are added to the sprite's sampled UV in the vertex stage — driving parallax / infinite-scroll\n * backgrounds without re-uploading texture coordinates. Marks only this sprite's range dirty.\n *\n * Throws if the layer was not created with `Sprite2DLayerOptions.uvScroll: true` (non-scroll layers\n * carry no uvOffset slot) or if `index` is out of range.\n */\nexport function setSprite2DUvOffset(layer: Sprite2DLayer, index: number, uvOffset: readonly [number, number]): void {\n if (!layer._uvScroll) {\n throw new Error(\"setSprite2DUvOffset: layer was not created with uvScroll: true.\");\n }\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DUvOffset: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const uvSlot = base + (layer.depth !== \"none\" ? 14 : 13);\n layer._instanceData[uvSlot] = uvOffset[0];\n layer._instanceData[uvSlot + 1] = uvOffset[1];\n markDirty(layer, index, index + 1);\n}\n\nfunction growCapacity(layer: Sprite2DLayer, minCapacity: number): void {\n let cap = layer._capacity;\n while (cap < minCapacity) {\n cap *= 2;\n }\n const next = new F32(cap * layer._instanceFloatsPerSprite);\n next.set(layer._instanceData);\n layer._instanceData = next;\n const nextSaved = new F32(cap * SAVED_SIZE_FLOATS_PER_SPRITE);\n nextSaved.set(layer._savedSize);\n layer._savedSize = nextSaved;\n layer._capacity = cap;\n}\n\nfunction setSprite2DCount(layer: Sprite2DLayer, count: number): void {\n (layer as { count: number }).count = count;\n}\n\n/**\n * Write one sprite's instance data into `layer._instanceData[base..base+layer._instanceFloatsPerSprite]`.\n *\n * Two call sites with different shapes:\n * - **add**: `prev === null`. `props` is a full `Sprite2DProps` (positionPx required).\n * Unspecified fields take their documented defaults (size=frame.sourceSizePx or 0,\n * UVs=[0,0,1,1], rotation=0, color=opaque white, visible=true).\n * - **update**: `prev` is the existing per-layer instance slice. Unspecified fields are preserved.\n *\n * Resolution rules (per field): `props` value if given, else (on add) the default, else `prev`.\n * `frame` is a higher-level intent: when supplied it stomps the four UV slots from the atlas\n * (then `flipX`/`flipY` swap them). It does **not** by itself imply a size change — `sizePx`\n * remains independent — but on add, a missing `sizePx` falls back to `frame.sourceSizePx`.\n *\n * **Visibility model (the part that needs explaining):**\n * - `_savedSize[slot]` always stores the sprite's *true* size (unaffected by visibility).\n * - `data[base+2..+3]` (the GPU-visible size) is `_savedSize` when visible, else `(0, 0)`.\n * - We detect previous visibility by checking `prev[2]==0 && prev[3]==0` (only hidden sprites\n * have zeroed GPU size). The CPU shadow gives us back the true size for free.\n */\nfunction writeInstance(layer: Sprite2DLayer, slotIndex: number, props: Partial<Sprite2DProps>, prev: Float32Array | null): void {\n const data = layer._instanceData;\n const base = slotIndex * layer._instanceFloatsPerSprite;\n const savedBase = slotIndex * SAVED_SIZE_FLOATS_PER_SPRITE; // [w, h] per sprite\n const isAdd = prev === null;\n\n // Optional frame lookup (used for UV stomp + size default on add).\n const frame = props.frame !== undefined ? layer.atlas.frames[resolveSpriteFrame(layer.atlas, props.frame)]! : null;\n\n // ── Position (required on add; preserved on update if omitted) ──────────────────────\n const posX = props.positionPx ? props.positionPx[0] : prev![0]!;\n const posY = props.positionPx ? props.positionPx[1] : prev![1]!;\n\n // ── True size (props.sizePx → frame default → previous true size) ───────────────────\n // The shadow buffer makes \"previous true size\" cheap and unambiguous regardless of visibility.\n let trueW: number;\n let trueH: number;\n if (props.sizePx) {\n trueW = props.sizePx[0];\n trueH = props.sizePx[1];\n } else if (frame) {\n trueW = frame.sourceSizePx[0];\n trueH = frame.sourceSizePx[1];\n } else if (isAdd) {\n trueW = 0;\n trueH = 0;\n } else {\n trueW = layer._savedSize[savedBase]!;\n trueH = layer._savedSize[savedBase + 1]!;\n }\n layer._savedSize[savedBase] = trueW;\n layer._savedSize[savedBase + 1] = trueH;\n\n // ── Visibility (props.visible → preserved → default true on add) ────────────────────\n let visible: boolean;\n if (props.visible !== undefined) {\n visible = props.visible;\n } else if (isAdd) {\n visible = true;\n } else {\n // Previous sprite was hidden iff its GPU size was zeroed.\n visible = prev![2]! !== 0 || prev![3]! !== 0;\n }\n\n // ── UVs (frame stomps; else preserved; else default [0,0,1,1] on add) ───────────────\n // flipX/flipY apply on top, by swapping the U/V endpoints.\n let uMin: number;\n let vMin: number;\n let uMax: number;\n let vMax: number;\n if (frame) {\n uMin = frame.uvMin[0];\n vMin = frame.uvMin[1];\n uMax = frame.uvMax[0];\n vMax = frame.uvMax[1];\n } else if (isAdd) {\n uMin = 0;\n vMin = 0;\n uMax = 1;\n vMax = 1;\n } else {\n uMin = prev![4]!;\n vMin = prev![5]!;\n uMax = prev![6]!;\n vMax = prev![7]!;\n }\n if (props.flipX === true) {\n const t = uMin;\n uMin = uMax;\n uMax = t;\n }\n if (props.flipY === true) {\n const t = vMin;\n vMin = vMax;\n vMax = t;\n }\n\n // ── Rotation ────────────────────────────────────────────────────────────────────────\n const rotation = props.rotation ?? (prev ? prev[8]! : 0);\n\n // ── Per-instance Z (depth-hosted only) ──────────────────────────────────────────────\n // Pure-2D layers intentionally have no slot [13]; `z` is accepted by the public API but\n // not allocated, uploaded, declared, or fetched by HUD pipelines.\n const hasDepthSlot = layer.depth !== \"none\";\n const z = hasDepthSlot ? (props.z ?? (prev ? prev[13]! : layer.layerZ)) : 0;\n\n // ── Write the float slots ──────────────────────────────────────────────────────────\n data[base + 0] = posX;\n data[base + 1] = posY;\n data[base + 2] = visible ? trueW : 0;\n data[base + 3] = visible ? trueH : 0;\n data[base + 4] = uMin;\n data[base + 5] = vMin;\n data[base + 6] = uMax;\n data[base + 7] = vMax;\n data[base + 8] = rotation;\n\n // ── Color (float32x4 to match Babylon.js SpriteRenderer's color precision) ─────────\n if (props.color) {\n data[base + 9] = props.color[0];\n data[base + 10] = props.color[1];\n data[base + 11] = props.color[2];\n data[base + 12] = props.color[3];\n } else if (isAdd) {\n data[base + 9] = 1;\n data[base + 10] = 1;\n data[base + 11] = 1;\n data[base + 12] = 1;\n }\n // else: previous color floats are already in place — nothing to write.\n\n // ── Per-instance Z (slot [13], depth-hosted layout only) ───────────────────────────\n if (hasDepthSlot) {\n data[base + 13] = z;\n }\n\n // ── Per-sprite uvOffset (uvScroll layout only) ─────────────────────────────────────\n // Appended after the base layout: slot [13] for pure-2D, slot [14] for depth-hosted\n // (the depth Z keeps slot [13]). props → preserved → default [0,0] on add.\n if (layer._uvScroll) {\n const uvSlot = base + (hasDepthSlot ? 14 : 13);\n if (props.uvOffset) {\n data[uvSlot] = props.uvOffset[0];\n data[uvSlot + 1] = props.uvOffset[1];\n } else if (isAdd) {\n data[uvSlot] = 0;\n data[uvSlot + 1] = 0;\n }\n // else: previous uvOffset floats are already in place — nothing to write.\n }\n}\n\nfunction markDirty(layer: Sprite2DLayer, lo: number, hi: number): void {\n if (layer._dirtyMin >= layer._dirtyMax) {\n layer._dirtyMin = lo;\n layer._dirtyMax = hi;\n } else {\n if (lo < layer._dirtyMin) {\n layer._dirtyMin = lo;\n }\n if (hi > layer._dirtyMax) {\n layer._dirtyMax = hi;\n }\n }\n layer._version = (layer._version + 1) | 0;\n}\n\n/** Add one sprite. Returns its index. Grows capacity as needed. */\nexport function addSprite2DIndex(layer: Sprite2DLayer, props: Sprite2DProps): number {\n if (props.positionPx === undefined) {\n throw new Error(\"addSprite2DIndex: props.positionPx is required.\");\n }\n const idx = layer.count;\n if (idx >= layer._capacity) {\n growCapacity(layer, idx + 1);\n }\n writeInstance(layer, idx, props, null);\n setSprite2DCount(layer, layer.count + 1);\n markDirty(layer, idx, idx + 1);\n return idx;\n}\n\n/** Patch one sprite. Unspecified fields are preserved. */\nexport function updateSprite2DIndex(layer: Sprite2DLayer, index: number, patch: Partial<Sprite2DProps>): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`updateSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const base = index * layer._instanceFloatsPerSprite;\n const prev = layer._instanceData.subarray(base, base + layer._instanceFloatsPerSprite);\n writeInstance(layer, index, patch, prev);\n markDirty(layer, index, index + 1);\n}\n\n/** Swap-remove a sprite. The last sprite (if any) takes its slot. */\nexport function removeSprite2DIndex(layer: Sprite2DLayer, index: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`removeSprite2DIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const last = layer.count - 1;\n layer._handleHooks?.removeIndex(index, last);\n if (index !== last) {\n layer._instanceData.copyWithin(index * layer._instanceFloatsPerSprite, last * layer._instanceFloatsPerSprite, (last + 1) * layer._instanceFloatsPerSprite);\n // Carry the swapped sprite's saved-size shadow with it (`[w, h]` per sprite).\n layer._savedSize.copyWithin(index * SAVED_SIZE_FLOATS_PER_SPRITE, last * SAVED_SIZE_FLOATS_PER_SPRITE, (last + 1) * SAVED_SIZE_FLOATS_PER_SPRITE);\n }\n // Clear the now-unused tail saved-size slot so a future re-add starts clean.\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE] = 0;\n layer._savedSize[last * SAVED_SIZE_FLOATS_PER_SPRITE + 1] = 0;\n setSprite2DCount(layer, last);\n markDirty(layer, index, index + 1);\n}\n\n/** Clear all sprites from a layer while preserving allocated capacity. */\nexport function clearSprite2DLayer(layer: Sprite2DLayer): void {\n const count = layer.count;\n layer._dirtyMin = 0;\n layer._dirtyMax = 0;\n layer._handleHooks?.clear();\n if (count === 0) {\n return;\n }\n layer._savedSize.fill(0, 0, count * SAVED_SIZE_FLOATS_PER_SPRITE);\n setSprite2DCount(layer, 0);\n layer._version = (layer._version + 1) | 0;\n}\n\n/**\n * Update only the frame UVs for one sprite.\n *\n * The sprite keeps its explicit `sizePx`/saved size. For atlas-driven size\n * changes, call `updateSprite2DIndex(layer, index, { frame, sizePx })`.\n * Existing flip state is preserved for non-degenerate UV ranges.\n */\nexport function setSprite2DFrameIndex(layer: Sprite2DLayer, index: number, frame: number): void {\n if (index < 0 || index >= layer.count) {\n throw new Error(`setSprite2DFrameIndex: index ${index} out of range [0, ${layer.count})`);\n }\n const frameIdx = resolveSpriteFrame(layer.atlas, frame);\n const f = layer.atlas.frames[frameIdx]!;\n const base = index * layer._instanceFloatsPerSprite;\n const flipX = layer._instanceData[base + 4]! > layer._instanceData[base + 6]!;\n const flipY = layer._instanceData[base + 5]! > layer._instanceData[base + 7]!;\n layer._instanceData[base + 4] = flipX ? f.uvMax[0] : f.uvMin[0];\n layer._instanceData[base + 5] = flipY ? f.uvMax[1] : f.uvMin[1];\n layer._instanceData[base + 6] = flipX ? f.uvMin[0] : f.uvMax[0];\n layer._instanceData[base + 7] = flipY ? f.uvMin[1] : f.uvMax[1];\n markDirty(layer, index, index + 1);\n}\n"],"names":[],"mappings":"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"}
@@ -12,12 +12,15 @@ const SPRITE_COLOR_OFFSET_BYTES = 36;
12
12
  const SPRITE_DEPTH_OFFSET_BYTES = 52;
13
13
  const SPRITE_UVOFFSET_OFFSET_PURE_2D_BYTES = 52;
14
14
  const SPRITE_UVOFFSET_OFFSET_DEPTH_BYTES = 56;
15
- function makeSpriteWgsl(hasDepth, spriteGroupIndex, uvScroll) {
15
+ function makeSpriteWgsl(hasDepth, spriteGroupIndex, uvScroll, coverageGamma = false) {
16
+ const coverageLine = coverageGamma ? `
17
+ let a = pow(s.a, L.aa.x);
18
+ return vec4<f32>(s.rgb, a) * in.tint * L.opacityMul;` : `
19
+ return s * in.tint * L.opacityMul;`;
16
20
  return `${makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll)}
17
21
  @fragment
18
22
  fn fs(in: VOut) -> @location(0) vec4<f32> {
19
- let s = textureSample(atlasTex, atlasSamp, in.uv);
20
- return s * in.tint * L.opacityMul;
23
+ let s = textureSample(atlasTex, atlasSamp, in.uv);${coverageLine}
21
24
  }`;
22
25
  }
23
26
  function makeSpritePrologueWgsl(hasDepth, spriteGroupIndex, uvScroll = false) {
@@ -38,6 +41,10 @@ pivot: vec2<f32>,
38
41
  // premultiplied: (opacity, opacity, opacity, opacity) — RGB and A scale together
39
42
  // One uniform, no shader branch.
40
43
  opacityMul: vec4<f32>,
44
+ // Coverage-gamma controls (text layers): .x = 1/coverageGamma applied to sampled alpha by the
45
+ // coverage-gamma shader permutation. .yzw reserved. Always present (UBO is a fixed 64 bytes);
46
+ // unused by the base shader permutation.
47
+ aa: vec4<f32>,
41
48
  };
42
49
  ${group} @binding(0) var<uniform> L: Layer;
43
50
  ${group} @binding(1) var atlasTex: texture_2d<f32>;
@@ -123,10 +130,7 @@ function getSpritePipelineDeviceCache(engine, cache) {
123
130
  let deviceCache = cache._devices.get(engine._device);
124
131
  if (!deviceCache) {
125
132
  deviceCache = {
126
- _shaderModule: null,
127
- _sceneShaderModule: null,
128
- _shaderModuleUv: null,
129
- _sceneShaderModuleUv: null,
133
+ _shaderModules: /* @__PURE__ */ new Map(),
130
134
  _pipelines: /* @__PURE__ */ new Map()
131
135
  };
132
136
  cache._devices.set(engine._device, deviceCache);
@@ -145,7 +149,8 @@ function normalizeDepthStencilFormat(hasDepth, depthStencilFormat) {
145
149
  function spritePipelineKey(format, sampleCount, blendMode, hasDepth, depthWrite, depthStencilFormat, layer) {
146
150
  const customKey = layer ? _getSpriteFxHook()?.pipelineKeyPart(layer) ?? "" : "";
147
151
  const uvKey = layer?._uvScroll ? "1" : "0";
148
- return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? "-"}:cs${customKey}:uv${uvKey}`;
152
+ const cgKey = layer?._coverageGamma ? "1" : "0";
153
+ return `${format}:${sampleCount}:${blendMode._key}:${hasDepth ? 1 : 0}:${depthWrite ? 1 : 0}:${depthStencilFormat ?? "-"}:cs${customKey}:uv${uvKey}:cg${cgKey}`;
149
154
  }
150
155
  function getShaderModule(engine, cache, hasDepth, layer) {
151
156
  const customModule = layer ? _getSpriteFxHook()?.shaderModule(engine, hasDepth, layer) : null;
@@ -153,20 +158,16 @@ function getShaderModule(engine, cache, hasDepth, layer) {
153
158
  return customModule;
154
159
  }
155
160
  const uvScroll = layer?._uvScroll === true;
156
- if (hasDepth) {
157
- if (uvScroll) {
158
- cache._sceneShaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, true) });
159
- return cache._sceneShaderModuleUv;
160
- }
161
- cache._sceneShaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(true, 1, false) });
162
- return cache._sceneShaderModule;
163
- }
164
- if (uvScroll) {
165
- cache._shaderModuleUv ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, true) });
166
- return cache._shaderModuleUv;
161
+ const coverageGamma = layer?._coverageGamma === true;
162
+ const key = `${hasDepth ? 1 : 0}:${uvScroll ? 1 : 0}:${coverageGamma ? 1 : 0}`;
163
+ let module = cache._shaderModules.get(key);
164
+ if (!module) {
165
+ module = engine._device.createShaderModule({
166
+ code: makeSpriteWgsl(hasDepth, hasDepth ? 1 : 0, uvScroll, coverageGamma)
167
+ });
168
+ cache._shaderModules.set(key, module);
167
169
  }
168
- cache._shaderModule ??= engine._device.createShaderModule({ code: makeSpriteWgsl(false, 0, false) });
169
- return cache._shaderModule;
170
+ return module;
170
171
  }
171
172
  function buildSpritePipeline(engine, cache, format, sampleCount, blendMode, hasDepth, depthWrite, depthStencilFormat, sceneBindGroupLayout, layer) {
172
173
  const device = engine._device;
@@ -237,7 +238,7 @@ function buildSpritePipeline(engine, cache, format, sampleCount, blendMode, hasD
237
238
  }
238
239
  return device.createRenderPipeline(descriptor);
239
240
  }
240
- const LAYER_UBO_BYTES = 48;
241
+ const LAYER_UBO_BYTES = 64;
241
242
  const LAYER_UBO_FLOATS = LAYER_UBO_BYTES / 4;
242
243
  const SHARED_SPRITE_INDEX_DATA = new U16([0, 1, 2, 0, 2, 3]);
243
244
  function createSpriteInstanceBuffer(device, layer, label) {
@@ -306,6 +307,8 @@ function buildSpriteLayerUbo(layer, screenWidth, screenHeight, ubo) {
306
307
  ubo[10] = 1;
307
308
  ubo[11] = op;
308
309
  }
310
+ const g = layer.coverageGamma;
311
+ ubo[12] = g > 0 ? 1 / g : 1;
309
312
  }
310
313
  function writeSpriteLayerUboIfDirty(device, uniformBuffer, scratchUbo, lastUbo, alreadyUploaded) {
311
314
  let dirty = !alreadyUploaded;