@babylonjs/lite 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +735 -0
- package/README.md +26 -1
- package/THIRD_PARTY_NOTICES.txt +139 -0
- package/dist/index.js +321 -301
- package/dist/index.js.map +1 -1
- package/index.d.ts +294 -13
- package/lib/_chunks/vendor/{manifold-Sh8xE2da.js → manifold-DtI3BJoR.js} +2 -2
- package/lib/_chunks/vendor/{manifold-Sh8xE2da.js.map → manifold-DtI3BJoR.js.map} +1 -1
- package/lib/animation/animation-group-task.js +1 -1
- package/lib/animation/animation-group-task.js.map +1 -1
- package/lib/animation/animation-group.js +20 -15
- package/lib/animation/animation-group.js.map +1 -1
- package/lib/animation/animation-tick.js +10 -0
- package/lib/animation/animation-tick.js.map +1 -0
- package/lib/animation/property-animation.js +2 -1
- package/lib/animation/property-animation.js.map +1 -1
- package/lib/animation/weighted-gltf-mixer.js +1 -1
- package/lib/animation/weighted-gltf-mixer.js.map +1 -1
- package/lib/animation/weighted-pointer-mixer.js +1 -1
- package/lib/animation/weighted-pointer-mixer.js.map +1 -1
- package/lib/engine/device-lost-recovery.js +1 -0
- package/lib/engine/device-lost-recovery.js.map +1 -1
- package/lib/engine/engine.js +1 -1
- package/lib/engine/null-engine.js +29 -0
- package/lib/engine/null-engine.js.map +1 -0
- package/lib/frame-graph/depth-pyramid.js +158 -0
- package/lib/frame-graph/depth-pyramid.js.map +1 -0
- package/lib/frame-graph/depth-resolve-task.js +117 -0
- package/lib/frame-graph/depth-resolve-task.js.map +1 -0
- package/lib/frame-graph/frame-graph-actions.js +10 -1
- package/lib/frame-graph/frame-graph-actions.js.map +1 -1
- package/lib/frame-graph/render-task.js +7 -0
- package/lib/frame-graph/render-task.js.map +1 -1
- package/lib/frame-graph/transmission-depth-grab.js +88 -0
- package/lib/frame-graph/transmission-depth-grab.js.map +1 -0
- package/lib/frame-graph/transmission.js +34 -4
- package/lib/frame-graph/transmission.js.map +1 -1
- package/lib/gizmo/bounding-box-gizmo.js +1 -1
- package/lib/index.js +9 -6
- package/lib/index.js.map +1 -1
- package/lib/loader-gltf/gltf-feature-gpu-instancing.js +1 -1
- package/lib/loader-gltf/gltf-interleave.js +1 -1
- package/lib/loader-gltf/load-gltf.js +1 -1
- package/lib/material/shader/shader-material.js +4 -2
- package/lib/material/shader/shader-material.js.map +1 -1
- package/lib/material/shader/shader-pipeline.js +11 -1
- package/lib/material/shader/shader-pipeline.js.map +1 -1
- package/lib/material/shader/shader-renderable.js +42 -14
- package/lib/material/shader/shader-renderable.js.map +1 -1
- package/lib/material/shader/shader-thin-instance.js +1 -1
- package/lib/material/shader/shader-thin-instance.js.map +1 -1
- package/lib/math/aabb.js +1 -1
- package/lib/math/compute-aabb.js +63 -1
- package/lib/math/compute-aabb.js.map +1 -1
- package/lib/math/index.js +1 -1
- package/lib/mesh/csg2.js +3 -2
- package/lib/mesh/csg2.js.map +1 -1
- package/lib/mesh/mesh-factories.js +1 -1
- package/lib/mesh/thin-instance-gpu-culling.js +4 -2
- package/lib/mesh/thin-instance-gpu-culling.js.map +1 -1
- package/lib/mesh/thin-instance.js +8 -1
- package/lib/mesh/thin-instance.js.map +1 -1
- package/lib/navigation/index.js +1 -1
- package/lib/navigation/navigation.js +9 -1
- package/lib/navigation/navigation.js.map +1 -1
- package/lib/physics/character-controller.js +2 -2
- package/lib/physics/character-controller.js.map +1 -1
- package/lib/physics/havok-floating-origin.js +2 -2
- package/lib/physics/havok-floating-origin.js.map +1 -1
- package/lib/physics/havok.js +40 -13
- package/lib/physics/havok.js.map +1 -1
- package/lib/picking/gpu-picker.js +16 -2
- package/lib/picking/gpu-picker.js.map +1 -1
- package/lib/scene/scene-core.js +14 -5
- package/lib/scene/scene-core.js.map +1 -1
- package/lib/scene/scene-remove.js +8 -0
- package/lib/scene/scene-remove.js.map +1 -1
- package/lib/shadow/csm-shadow-task-hooks.js +14 -6
- package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
- package/lib/shadow/pcf-shadow-task-hooks.js +6 -0
- package/lib/shadow/pcf-shadow-task-hooks.js.map +1 -1
- package/lib/texture/pixels-texture.js +31 -1
- package/lib/texture/pixels-texture.js.map +1 -1
- package/lib/vat/vat-baker.js +41 -2
- package/lib/vat/vat-baker.js.map +1 -1
- package/package.json +15 -3
- package/lib/_chunks/__vite-browser-external-BFTKbDcV.js +0 -68
- package/lib/_chunks/__vite-browser-external-BFTKbDcV.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gpu-picker.js","sources":["../../../src/picking/gpu-picker.ts"],"sourcesContent":["import { F32, U32, U8 } from \"../engine/typed-arrays.js\";\nimport { TU, BU } from \"../engine/gpu-flags.js\";\nimport type { SceneContext } from \"../scene/scene.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { PickingInfo } from \"./picking-info.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type * as DeformedGeometry from \"./deformed-geometry.js\";\nimport type * as GsPickingPipeline from \"./gs-picking-pipeline.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { createEmptyPickingInfo } from \"./picking-info.js\";\nimport { createPickingRay } from \"./ray.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { getPickingPipelineSet, getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getViewProjectionMatrix, getCameraPosition } from \"../camera/camera.js\";\nimport { resolveCameraViewport } from \"../camera/viewport.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from \"../resource/gpu-buffers.js\";\n\n// ─── Scratch arrays — allocated once, reused across all picks ──────\nconst _pickVP = new F32(16);\nconst _gsPickMatrix = new F32(16);\nconst PICK_MESH_UBO_BYTES = 80;\nconst PICK_TI_UBO_BYTES = 16;\nconst _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);\nconst _uboF32 = new F32(_uboScratch);\nconst _uboU32 = new U32(_uboScratch);\nconst _uboView = new U8(_uboScratch);\nconst _tiUboScratch = new ArrayBuffer(PICK_TI_UBO_BYTES);\nconst _tiUboU32 = new U32(_tiUboScratch);\nconst _tiUboView = new U8(_tiUboScratch);\n\n/** GPU-based picker — pure state. Use pickAsync() and disposePicker() standalone functions. */\nexport interface GpuPicker {\n /** @internal Optional hook for detailed picking (Phase 2). */\n _detailedPick: ((info: PickingInfo, ray: { origin: [number, number, number]; direction: [number, number, number]; length: number }) => void | Promise<void>) | null;\n /** @internal */\n _scene: SceneContext;\n /** @internal 1×1 render targets (lazily created). */\n _rt: PickTargets1x1 | null;\n /** @internal Reusable scene UBO (64 bytes). */\n _sceneUbo: GPUBuffer | null;\n /** @internal Reusable scene bind group. */\n _sceneBG: GPUBindGroup | null;\n /** @internal Per-GS-mesh picking resources (created on demand). */\n _gsMeshResources: Map<GaussianSplattingMesh, GsPickingPipeline.GsPickMeshResources> | null;\n}\n\ninterface PickTargets1x1 {\n colorTex: GPUTexture;\n colorView: GPUTextureView;\n depthColorTex: GPUTexture;\n depthColorView: GPUTextureView;\n depthTex: GPUTexture;\n depthView: GPUTextureView;\n colorStaging: GPUBuffer;\n depthStaging: GPUBuffer;\n}\n\n/** Create a GPU picker bound to the given scene. */\nexport function createGpuPicker(scene: SceneContext): GpuPicker {\n return {\n _detailedPick: null,\n _scene: scene,\n _rt: null,\n _sceneUbo: null,\n _sceneBG: null,\n _gsMeshResources: null,\n };\n}\n\nfunction ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 {\n const device = engine._device;\n if (picker._rt) {\n return picker._rt;\n }\n const colorTex = device.createTexture({ label: \"pick-color\", size: [1, 1], format: \"rgba8unorm\", usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC });\n const depthColorTex = device.createTexture({\n label: \"pick-depth-color\",\n size: [1, 1],\n format: \"r32float\",\n usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC,\n });\n const depthTex = device.createTexture({ label: \"pick-depth\", size: [1, 1], format: \"depth24plus\", usage: TU.RENDER_ATTACHMENT });\n picker._rt = {\n colorTex,\n colorView: colorTex.createView(),\n depthColorTex,\n depthColorView: depthColorTex.createView(),\n depthTex,\n depthView: depthTex.createView(),\n colorStaging: device.createBuffer({ label: \"pick-color-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n depthStaging: device.createBuffer({ label: \"pick-depth-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n };\n return picker._rt;\n}\n\nfunction ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer {\n const device = engine._device;\n if (!picker._sceneUbo) {\n picker._sceneUbo = createEmptyUniformBuffer(engine, 64, \"pick-scene-ubo\");\n const sceneBGL = getPickingSceneBGL(engine);\n picker._sceneBG = device.createBindGroup({ label: \"pick-scene-bg\", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] });\n }\n return picker._sceneUbo;\n}\n\n/** Compute a VP matrix zoomed to a single pixel at (px, py) on a W×H canvas.\n * Renders to a 1×1 target — only fragments at the picked pixel survive. */\nfunction computePickVP(out: Float32Array, vp: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // pickVP = pickMatrix * VP (sparse multiply, see derivation in comments)\n for (let c = 0; c < 4; c++) {\n const base = c * 4;\n const w3 = vp[base + 3]!;\n out[base] = w * (vp[base]! - ndcX * w3);\n out[base + 1] = h * (vp[base + 1]! - ndcY * w3);\n out[base + 2] = vp[base + 2]!;\n out[base + 3] = w3;\n }\n}\n\n/** Options for {@link pickAsync}. */\nexport interface PickOptions {\n /** Restrict the pick to a subset of the scene's meshes — return `true` for a mesh that may be picked,\n * `false` to ignore it entirely (it neither occludes nor is returned). Lets a caller provide its\n * \"list of pickables\" so decorative meshes (grass, foliage, particles, …) can't swallow a pick of a\n * structure behind/around them. When omitted, every mesh is pickable (previous behaviour). Applied\n * identically to the id-assignment and id-resolve passes so ids stay consistent. */\n filter?: (mesh: Mesh) => boolean;\n /** Optional GPU fragment-discard extension for app-specific pick removal.\n *\n * `wgsl` is injected into the regular and thin-instance picking shaders and must define:\n *\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`\n *\n * The input exposes only generic picker data: `worldPos`, `pickId`, `thinInstanceIndex`,\n * `hasThinInstance`, and `instanceExtras` (the original thin-instance matrix w lanes, zero for\n * non-instanced meshes). Storage entries are uploaded and bound by Lite for the current pick only. */\n discard?: PickDiscardRule;\n /** Dev-only diagnostics: logs the pick ray, pixel, pick id/depth and resolved mesh. */\n debugLabel?: string;\n}\n\n/**\n * Optional GPU-side discard rule for {@link pickAsync}.\n *\n * This lets apps remove pick hits with custom WGSL while keeping the main scene\n * render untouched. The WGSL must define\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`.\n */\nexport interface PickDiscardRule {\n /** Stable cache key for the generated picking pipeline set. Change it when the WGSL or layout changes. */\n readonly key: string;\n /** WGSL source that defines `shouldDiscardPick(input: PickDiscardInput) -> bool`. */\n readonly wgsl: string;\n /** Optional typed-array storage inputs exposed to the discard WGSL at group 2. */\n readonly storage?: readonly PickDiscardStorage[];\n}\n\n/** Storage data for a pick discard rule. Lite injects the WGSL declaration and owns the GPU buffer upload. */\nexport interface PickDiscardStorage {\n /** WGSL variable name declared at `@group(2) @binding(index)`; must be unique within the rule. */\n readonly name: string;\n /** WGSL storage type, for example `array<vec4<f32>>`. */\n readonly type: string;\n /** Per-mesh data for the current pick. Return `null` to draw that mesh with the default picker. */\n readonly data: (mesh: Mesh) => ArrayBufferView | null | undefined;\n}\n\nfunction createPickDiscardBindGroup(engine: EngineContext, layout: GPUBindGroupLayout, discard: PickDiscardRule, mesh: Mesh, tempBuffers: GPUBuffer[]): GPUBindGroup | null {\n const storage = discard.storage;\n if (!storage || storage.length === 0) {\n return null;\n }\n const entries: GPUBindGroupEntry[] = [];\n for (let i = 0; i < storage.length; i++) {\n const data = storage[i]!.data(mesh);\n if (!data) {\n return null;\n }\n const buffer = createMappedBuffer(engine, data, BU.STORAGE);\n tempBuffers.push(buffer);\n entries.push({ binding: i, resource: { buffer } });\n }\n const device = engine._device;\n return device.createBindGroup({\n label: `pick-discard-${discard.key}-bg`,\n layout,\n entries,\n });\n}\n\n/** Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo. */\nexport async function pickAsync(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const scene = picker._scene;\n const pickFilter = options?.filter ?? null;\n const pickDiscard = options?.discard ?? null;\n const debugLabel = options?.debugLabel;\n const engine = scene.surface.engine;\n const device = engine._device;\n // Pick coordinates are relative to the scene's own surface canvas, not the engine's\n // primary canvas — they differ when the scene renders into an auxiliary surface.\n const canvas = scene.surface.canvas;\n const camera = scene.camera;\n if (!camera) {\n return createEmptyPickingInfo();\n }\n\n const backingWidth = canvas.width;\n const backingHeight = canvas.height;\n const clientWidth = (\"clientWidth\" in canvas ? canvas.clientWidth : 0) || backingWidth;\n const clientHeight = (\"clientHeight\" in canvas ? canvas.clientHeight : 0) || backingHeight;\n const scaleX = backingWidth / clientWidth;\n const scaleY = backingHeight / clientHeight;\n const pickX = x * scaleX;\n const pickY = y * scaleY;\n const viewport = resolveCameraViewport(camera, backingWidth, backingHeight);\n const w = viewport.width;\n const h = viewport.height;\n if (w === 0 || h === 0) {\n return createEmptyPickingInfo();\n }\n\n if (pickX < viewport.x || pickY < viewport.y || pickX >= viewport.x + viewport.width || pickY >= viewport.y + viewport.height) {\n return createEmptyPickingInfo();\n }\n\n const px = Math.max(0, Math.min(Math.floor(pickX - viewport.x), w - 1));\n const py = Math.max(0, Math.min(Math.floor(pickY - viewport.y), h - 1));\n const aspect = w / h;\n const vp = getViewProjectionMatrix(camera, aspect);\n const debugRay = debugLabel ? createPickingRay(px, py, vp, w, h) : null;\n\n // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ──\n computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h);\n\n const rt = ensureTargets(engine, picker);\n const sceneUbo = ensureSceneUbo(engine, picker);\n device.queue.writeBuffer(sceneUbo, 0, _pickVP);\n\n // ── Assign pick IDs (array-based, no Map for miss case) ──────────\n const meshes = scene.meshes;\n const meshCount = meshes.length;\n let nextId = 1;\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\n // ── Render pass (1×1 target) ─────────────────────────────────────\n const encoder = device.createCommandEncoder({ label: \"pick\" });\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n { view: rt.colorView, clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n { view: rt.depthColorView, clearValue: { r: 1, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n ],\n depthStencilAttachment: { view: rt.depthView, depthClearValue: 0, depthLoadOp: \"clear\", depthStoreOp: \"discard\" },\n });\n\n const defaultPipelines = getPickingPipelineSet(engine);\n const discardPipelines = pickDiscard ? getPickingPipelineSet(engine, pickDiscard) : null;\n\n const tempBuffers: GPUBuffer[] = [];\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n // Skip meshes explicitly marked non-pickable. Mirrors BJS\n // `isPickable = false` — lets gizmos toggle display-only helpers\n // (e.g. the rotation-sector display plane) out of the picker without\n // affecting their rendering or hiding them from picks via `visible`.\n if (mesh.pickable === false) {\n continue; // not drawn AND not given an id (skipped identically below)\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // excluded from picking → not drawn AND not given an id (skipped identically below)\n }\n const gpu = mesh._gpu;\n const ti = mesh.thinInstances;\n const discardBG = pickDiscard && discardPipelines?.discardBGL ? createPickDiscardBindGroup(engine, discardPipelines.discardBGL, pickDiscard, mesh, tempBuffers) : null;\n const pipelines = discardPipelines && (!discardPipelines.discardBGL || discardBG) ? discardPipelines : defaultPipelines;\n\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n _tiUboU32[0] = nextId;\n const tiUbo = createUniformBuffer(engine, _tiUboView);\n tempBuffers.push(tiUbo);\n\n pass.setPipeline(pipelines.thinInstancePipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.thinInstancePipeline.getBindGroupLayout(1),\n entries: [\n { binding: 0, resource: { buffer: tiUbo } },\n { binding: 1, resource: { buffer: ti._gpuBuffer } },\n ],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, gpu.positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount, ti.count);\n nextId += ti.count;\n } else {\n _uboF32.set(mesh.worldMatrix, 0);\n _uboU32[16] = nextId;\n const meshUbo = createUniformBuffer(engine, _uboView);\n tempBuffers.push(meshUbo);\n let positionBuffer = gpu.positionBuffer;\n if (deformedGeometry && (mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n const deformedPositions = deformedGeometry.computeDeformedPositions(mesh);\n if (deformedPositions) {\n positionBuffer = createMappedBuffer(engine, deformedPositions, BU.VERTEX);\n tempBuffers.push(positionBuffer);\n }\n }\n\n pass.setPipeline(pipelines.regularPipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.regularPipeline.getBindGroupLayout(1),\n entries: [{ binding: 0, resource: { buffer: meshUbo } }],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount);\n nextId++;\n }\n }\n\n // ── Gaussian-splatting meshes ────────────────────────────────────\n // Drawn from the same pass against the same depth target. Each GS mesh\n // gets one pick id (no thin-instance support — BJS GS picking is per-mesh\n // too). The GS picking pipeline applies an independent pickMatrix to the\n // GS clip-space output so the EWA Jacobian / `u.focal` math stays intact.\n const gsMeshes = (scene as unknown as { _gsMeshes: GaussianSplattingMesh[] })._gsMeshes;\n const gsMeshCount = gsMeshes.length;\n const gsNextIdStart = nextId;\n if (gsMeshCount > 0) {\n const gsModule = await import(\"./gs-picking-pipeline.js\");\n gsModule.computeGsPickMatrix(_gsPickMatrix, px, py, w, h);\n gsModule.gsPickWritePickMatrixAndBind(pass, engine, _gsPickMatrix);\n const resMap = picker._gsMeshResources ?? (picker._gsMeshResources = new Map());\n for (let gi = 0; gi < gsMeshCount; gi++) {\n const gsMesh = gsMeshes[gi]!;\n let res = resMap.get(gsMesh);\n if (!res) {\n res = gsModule.createGsPickMeshResources(engine, gsMesh);\n resMap.set(gsMesh, res);\n }\n gsModule.drawGsForPicking(pass, engine, scene, gsMesh, res, nextId, w, h);\n nextId++;\n }\n }\n pass.end();\n\n // ── Readback (both 1×1 — trivially small) ────────────────────────\n encoder.copyTextureToBuffer({ texture: rt.colorTex }, { buffer: rt.colorStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n encoder.copyTextureToBuffer({ texture: rt.depthColorTex }, { buffer: rt.depthStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n device.queue.submit([encoder.finish()]);\n\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n const pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n const depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n\n // Destroy temp per-mesh UBOs\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\n\n // ── Resolve pick ID to mesh ──────────────────────────────────────\n if (pickId === 0) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n });\n }\n return createEmptyPickingInfo();\n }\n let hitMesh: Mesh | GaussianSplattingMesh | null = null;\n let hitThinIdx = -1;\n let hitIsGs = false;\n let scanId = 1;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if (mesh.pickable === false) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n const ti = mesh.thinInstances;\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n if (pickId >= scanId && pickId < scanId + ti.count) {\n hitMesh = mesh;\n hitThinIdx = pickId - scanId;\n break;\n }\n scanId += ti.count;\n } else {\n if (pickId === scanId) {\n hitMesh = mesh;\n break;\n }\n scanId++;\n }\n }\n if (!hitMesh && gsMeshCount > 0 && pickId >= gsNextIdStart) {\n const gsIdx = pickId - gsNextIdStart;\n if (gsIdx < gsMeshCount) {\n hitMesh = gsMeshes[gsIdx]!;\n hitIsGs = true;\n }\n }\n if (!hitMesh) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n unresolved: true,\n });\n }\n return createEmptyPickingInfo();\n }\n\n const info = createEmptyPickingInfo();\n info.hit = true;\n info.pickedMesh = hitMesh;\n info.thinInstanceIndex = hitThinIdx;\n\n // Reconstruct world position from depth (using original full-res VP)\n const invVP = mat4Invert(vp);\n if (invVP) {\n const ndcX = (2 * (pickX - viewport.x)) / w - 1;\n const ndcY = 1 - (2 * (pickY - viewport.y)) / h;\n const wx = invVP[0]! * ndcX + invVP[4]! * ndcY + invVP[8]! * depth + invVP[12]!;\n const wy = invVP[1]! * ndcX + invVP[5]! * ndcY + invVP[9]! * depth + invVP[13]!;\n const wz = invVP[2]! * ndcX + invVP[6]! * ndcY + invVP[10]! * depth + invVP[14]!;\n const ww = invVP[3]! * ndcX + invVP[7]! * ndcY + invVP[11]! * depth + invVP[15]!;\n const invW = 1 / ww;\n info.pickedPoint = [wx * invW, wy * invW, wz * invW];\n\n const camPos = getCameraPosition(camera);\n const dx = info.pickedPoint[0] - camPos.x;\n const dy = info.pickedPoint[1] - camPos.y;\n const dz = info.pickedPoint[2] - camPos.z;\n info.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n if (picker._detailedPick && !hitIsGs) {\n const ray = createPickingRay(pickX - viewport.x, pickY - viewport.y, vp, w, h);\n if (ray) {\n info.ray = ray;\n await picker._detailedPick(info, ray);\n }\n }\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: info.ray ?? debugRay,\n pickId,\n depth,\n hit: true,\n mesh: (hitMesh as Mesh).name ?? \"(unnamed)\",\n thinInstanceIndex: hitThinIdx,\n pickedPoint: info.pickedPoint,\n distance: info.distance,\n });\n }\n\n return info;\n}\n\n/** Dispose GPU resources owned by this picker. */\nexport function disposePicker(picker: GpuPicker): void {\n if (picker._rt) {\n picker._rt.colorTex.destroy();\n picker._rt.depthColorTex.destroy();\n picker._rt.depthTex.destroy();\n picker._rt.colorStaging.destroy();\n picker._rt.depthStaging.destroy();\n picker._rt = null;\n }\n if (picker._sceneUbo) {\n picker._sceneUbo.destroy();\n picker._sceneUbo = null;\n picker._sceneBG = null;\n }\n if (picker._gsMeshResources) {\n // Async dispose — destroy() is synchronous so we can run inline once\n // the module is loaded. If the module was never imported (no GS pick\n // ever happened) the map is empty and there's nothing to do.\n void import(\"./gs-picking-pipeline.js\").then((m) => {\n if (!picker._gsMeshResources) {\n return;\n }\n for (const res of picker._gsMeshResources.values()) {\n m.disposeGsPickMeshResources(res);\n }\n picker._gsMeshResources = null;\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gpu-picker.js","sources":["../../../src/picking/gpu-picker.ts"],"sourcesContent":["import { F32, U32, U8 } from \"../engine/typed-arrays.js\";\nimport { TU, BU } from \"../engine/gpu-flags.js\";\nimport type { SceneContext } from \"../scene/scene.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport type { PickingInfo } from \"./picking-info.js\";\nimport type { EngineContext } from \"../engine/engine.js\";\nimport type * as DeformedGeometry from \"./deformed-geometry.js\";\nimport type * as GsPickingPipeline from \"./gs-picking-pipeline.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\nimport { createEmptyPickingInfo } from \"./picking-info.js\";\nimport { createPickingRay } from \"./ray.js\";\nimport { mat4Invert } from \"../math/mat4-invert.js\";\nimport { getPickingPipelineSet, getPickingSceneBGL } from \"./picking-pipeline.js\";\nimport { getViewProjectionMatrix, getCameraPosition } from \"../camera/camera.js\";\nimport { resolveCameraViewport } from \"../camera/viewport.js\";\nimport { createEmptyUniformBuffer, createMappedBuffer, createUniformBuffer } from \"../resource/gpu-buffers.js\";\n\n// ─── Scratch arrays — allocated once, reused across all picks ──────\nconst _pickVP = new F32(16);\nconst _gsPickMatrix = new F32(16);\nconst PICK_MESH_UBO_BYTES = 80;\nconst PICK_TI_UBO_BYTES = 16;\nconst _uboScratch = new ArrayBuffer(PICK_MESH_UBO_BYTES);\nconst _uboF32 = new F32(_uboScratch);\nconst _uboU32 = new U32(_uboScratch);\nconst _uboView = new U8(_uboScratch);\nconst _tiUboScratch = new ArrayBuffer(PICK_TI_UBO_BYTES);\nconst _tiUboU32 = new U32(_tiUboScratch);\nconst _tiUboView = new U8(_tiUboScratch);\n\n/** GPU-based picker — pure state. Use pickAsync() and disposePicker() standalone functions. */\nexport interface GpuPicker {\n /** @internal Optional hook for detailed picking (Phase 2). */\n _detailedPick: ((info: PickingInfo, ray: { origin: [number, number, number]; direction: [number, number, number]; length: number }) => void | Promise<void>) | null;\n /** @internal */\n _scene: SceneContext;\n /** @internal 1×1 render targets (lazily created). */\n _rt: PickTargets1x1 | null;\n /** @internal Reusable scene UBO (64 bytes). */\n _sceneUbo: GPUBuffer | null;\n /** @internal Reusable scene bind group. */\n _sceneBG: GPUBindGroup | null;\n /** @internal Per-GS-mesh picking resources (created on demand). */\n _gsMeshResources: Map<GaussianSplattingMesh, GsPickingPipeline.GsPickMeshResources> | null;\n /** @internal Tail of the serialized pick queue for this picker — see pickAsync(). */\n _pending: Promise<void> | null;\n}\n\ninterface PickTargets1x1 {\n colorTex: GPUTexture;\n colorView: GPUTextureView;\n depthColorTex: GPUTexture;\n depthColorView: GPUTextureView;\n depthTex: GPUTexture;\n depthView: GPUTextureView;\n colorStaging: GPUBuffer;\n depthStaging: GPUBuffer;\n}\n\n/** Create a GPU picker bound to the given scene. */\nexport function createGpuPicker(scene: SceneContext): GpuPicker {\n return {\n _detailedPick: null,\n _scene: scene,\n _rt: null,\n _sceneUbo: null,\n _sceneBG: null,\n _gsMeshResources: null,\n _pending: null,\n };\n}\n\nfunction ensureTargets(engine: EngineContext, picker: GpuPicker): PickTargets1x1 {\n const device = engine._device;\n if (picker._rt) {\n return picker._rt;\n }\n const colorTex = device.createTexture({ label: \"pick-color\", size: [1, 1], format: \"rgba8unorm\", usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC });\n const depthColorTex = device.createTexture({\n label: \"pick-depth-color\",\n size: [1, 1],\n format: \"r32float\",\n usage: TU.RENDER_ATTACHMENT | TU.COPY_SRC,\n });\n const depthTex = device.createTexture({ label: \"pick-depth\", size: [1, 1], format: \"depth24plus\", usage: TU.RENDER_ATTACHMENT });\n picker._rt = {\n colorTex,\n colorView: colorTex.createView(),\n depthColorTex,\n depthColorView: depthColorTex.createView(),\n depthTex,\n depthView: depthTex.createView(),\n colorStaging: device.createBuffer({ label: \"pick-color-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n depthStaging: device.createBuffer({ label: \"pick-depth-staging\", size: 256, usage: BU.COPY_DST | BU.MAP_READ }),\n };\n return picker._rt;\n}\n\nfunction ensureSceneUbo(engine: EngineContext, picker: GpuPicker): GPUBuffer {\n const device = engine._device;\n if (!picker._sceneUbo) {\n picker._sceneUbo = createEmptyUniformBuffer(engine, 64, \"pick-scene-ubo\");\n const sceneBGL = getPickingSceneBGL(engine);\n picker._sceneBG = device.createBindGroup({ label: \"pick-scene-bg\", layout: sceneBGL, entries: [{ binding: 0, resource: { buffer: picker._sceneUbo } }] });\n }\n return picker._sceneUbo;\n}\n\n/** Compute a VP matrix zoomed to a single pixel at (px, py) on a W×H canvas.\n * Renders to a 1×1 target — only fragments at the picked pixel survive. */\nfunction computePickVP(out: Float32Array, vp: Float32Array, px: number, py: number, w: number, h: number): void {\n const ndcX = (2 * (px + 0.5)) / w - 1;\n const ndcY = 1 - (2 * (py + 0.5)) / h;\n // pickVP = pickMatrix * VP (sparse multiply, see derivation in comments)\n for (let c = 0; c < 4; c++) {\n const base = c * 4;\n const w3 = vp[base + 3]!;\n out[base] = w * (vp[base]! - ndcX * w3);\n out[base + 1] = h * (vp[base + 1]! - ndcY * w3);\n out[base + 2] = vp[base + 2]!;\n out[base + 3] = w3;\n }\n}\n\n/** Options for {@link pickAsync}. */\nexport interface PickOptions {\n /** Restrict the pick to a subset of the scene's meshes — return `true` for a mesh that may be picked,\n * `false` to ignore it entirely (it neither occludes nor is returned). Lets a caller provide its\n * \"list of pickables\" so decorative meshes (grass, foliage, particles, …) can't swallow a pick of a\n * structure behind/around them. When omitted, every mesh is pickable (previous behaviour). Applied\n * identically to the id-assignment and id-resolve passes so ids stay consistent. */\n filter?: (mesh: Mesh) => boolean;\n /** Optional GPU fragment-discard extension for app-specific pick removal.\n *\n * `wgsl` is injected into the regular and thin-instance picking shaders and must define:\n *\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`\n *\n * The input exposes only generic picker data: `worldPos`, `pickId`, `thinInstanceIndex`,\n * `hasThinInstance`, and `instanceExtras` (the original thin-instance matrix w lanes, zero for\n * non-instanced meshes). Storage entries are uploaded and bound by Lite for the current pick only. */\n discard?: PickDiscardRule;\n /** Dev-only diagnostics: logs the pick ray, pixel, pick id/depth and resolved mesh. */\n debugLabel?: string;\n}\n\n/**\n * Optional GPU-side discard rule for {@link pickAsync}.\n *\n * This lets apps remove pick hits with custom WGSL while keeping the main scene\n * render untouched. The WGSL must define\n * `fn shouldDiscardPick(input: PickDiscardInput) -> bool`.\n */\nexport interface PickDiscardRule {\n /** Stable cache key for the generated picking pipeline set. Change it when the WGSL or layout changes. */\n readonly key: string;\n /** WGSL source that defines `shouldDiscardPick(input: PickDiscardInput) -> bool`. */\n readonly wgsl: string;\n /** Optional typed-array storage inputs exposed to the discard WGSL at group 2. */\n readonly storage?: readonly PickDiscardStorage[];\n}\n\n/** Storage data for a pick discard rule. Lite injects the WGSL declaration and owns the GPU buffer upload. */\nexport interface PickDiscardStorage {\n /** WGSL variable name declared at `@group(2) @binding(index)`; must be unique within the rule. */\n readonly name: string;\n /** WGSL storage type, for example `array<vec4<f32>>`. */\n readonly type: string;\n /** Per-mesh data for the current pick. Return `null` to draw that mesh with the default picker. */\n readonly data: (mesh: Mesh) => ArrayBufferView | null | undefined;\n}\n\nfunction createPickDiscardBindGroup(engine: EngineContext, layout: GPUBindGroupLayout, discard: PickDiscardRule, mesh: Mesh, tempBuffers: GPUBuffer[]): GPUBindGroup | null {\n const storage = discard.storage;\n if (!storage || storage.length === 0) {\n return null;\n }\n const entries: GPUBindGroupEntry[] = [];\n for (let i = 0; i < storage.length; i++) {\n const data = storage[i]!.data(mesh);\n if (!data) {\n return null;\n }\n const buffer = createMappedBuffer(engine, data, BU.STORAGE);\n tempBuffers.push(buffer);\n entries.push({ binding: i, resource: { buffer } });\n }\n const device = engine._device;\n return device.createBindGroup({\n label: `pick-discard-${discard.key}-bg`,\n layout,\n entries,\n });\n}\n\n/** Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n * Does the actual GPU render + readback for one pick — call `pickAsync` (below) instead; it serializes\n * concurrent calls on the same picker so their shared 1×1 staging buffers never race. */\nasync function pickAsyncImpl(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const scene = picker._scene;\n const pickFilter = options?.filter ?? null;\n const pickDiscard = options?.discard ?? null;\n const debugLabel = options?.debugLabel;\n const engine = scene.surface.engine;\n const device = engine._device;\n // Pick coordinates are relative to the scene's own surface canvas, not the engine's\n // primary canvas — they differ when the scene renders into an auxiliary surface.\n const canvas = scene.surface.canvas;\n const camera = scene.camera;\n if (!camera) {\n return createEmptyPickingInfo();\n }\n\n const backingWidth = canvas.width;\n const backingHeight = canvas.height;\n const clientWidth = (\"clientWidth\" in canvas ? canvas.clientWidth : 0) || backingWidth;\n const clientHeight = (\"clientHeight\" in canvas ? canvas.clientHeight : 0) || backingHeight;\n const scaleX = backingWidth / clientWidth;\n const scaleY = backingHeight / clientHeight;\n const pickX = x * scaleX;\n const pickY = y * scaleY;\n const viewport = resolveCameraViewport(camera, backingWidth, backingHeight);\n const w = viewport.width;\n const h = viewport.height;\n if (w === 0 || h === 0) {\n return createEmptyPickingInfo();\n }\n\n if (pickX < viewport.x || pickY < viewport.y || pickX >= viewport.x + viewport.width || pickY >= viewport.y + viewport.height) {\n return createEmptyPickingInfo();\n }\n\n const px = Math.max(0, Math.min(Math.floor(pickX - viewport.x), w - 1));\n const py = Math.max(0, Math.min(Math.floor(pickY - viewport.y), h - 1));\n const aspect = w / h;\n const vp = getViewProjectionMatrix(camera, aspect);\n const debugRay = debugLabel ? createPickingRay(px, py, vp, w, h) : null;\n\n // ── Compute pick-zoomed VP (renders single pixel to 1×1 target) ──\n computePickVP(_pickVP, vp as unknown as Float32Array, px, py, w, h);\n\n const rt = ensureTargets(engine, picker);\n const sceneUbo = ensureSceneUbo(engine, picker);\n device.queue.writeBuffer(sceneUbo, 0, _pickVP);\n\n // ── Assign pick IDs (array-based, no Map for miss case) ──────────\n const meshes = scene.meshes;\n const meshCount = meshes.length;\n let nextId = 1;\n let deformedGeometry: typeof DeformedGeometry | null = null;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if ((mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n deformedGeometry = await import(\"./deformed-geometry.js\");\n break;\n }\n }\n\n // ── Render pass (1×1 target) ─────────────────────────────────────\n const encoder = device.createCommandEncoder({ label: \"pick\" });\n const pass = encoder.beginRenderPass({\n colorAttachments: [\n { view: rt.colorView, clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n { view: rt.depthColorView, clearValue: { r: 1, g: 0, b: 0, a: 0 }, loadOp: \"clear\", storeOp: \"store\" },\n ],\n depthStencilAttachment: { view: rt.depthView, depthClearValue: 0, depthLoadOp: \"clear\", depthStoreOp: \"discard\" },\n });\n\n const defaultPipelines = getPickingPipelineSet(engine);\n const discardPipelines = pickDiscard ? getPickingPipelineSet(engine, pickDiscard) : null;\n\n const tempBuffers: GPUBuffer[] = [];\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n // Skip meshes explicitly marked non-pickable. Mirrors BJS\n // `isPickable = false` — lets gizmos toggle display-only helpers\n // (e.g. the rotation-sector display plane) out of the picker without\n // affecting their rendering or hiding them from picks via `visible`.\n if (mesh.pickable === false) {\n continue; // not drawn AND not given an id (skipped identically below)\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // excluded from picking → not drawn AND not given an id (skipped identically below)\n }\n const gpu = mesh._gpu;\n const ti = mesh.thinInstances;\n const discardBG = pickDiscard && discardPipelines?.discardBGL ? createPickDiscardBindGroup(engine, discardPipelines.discardBGL, pickDiscard, mesh, tempBuffers) : null;\n const pipelines = discardPipelines && (!discardPipelines.discardBGL || discardBG) ? discardPipelines : defaultPipelines;\n\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n _tiUboU32[0] = nextId;\n const tiUbo = createUniformBuffer(engine, _tiUboView);\n tempBuffers.push(tiUbo);\n\n pass.setPipeline(pipelines.thinInstancePipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.thinInstancePipeline.getBindGroupLayout(1),\n entries: [\n { binding: 0, resource: { buffer: tiUbo } },\n { binding: 1, resource: { buffer: ti._gpuBuffer } },\n ],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, gpu.positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount, ti.count);\n nextId += ti.count;\n } else {\n _uboF32.set(mesh.worldMatrix, 0);\n _uboU32[16] = nextId;\n const meshUbo = createUniformBuffer(engine, _uboView);\n tempBuffers.push(meshUbo);\n let positionBuffer = gpu.positionBuffer;\n if (deformedGeometry && (mesh.morphTargets || mesh.skeleton) && mesh._cpuPositions) {\n const deformedPositions = deformedGeometry.computeDeformedPositions(mesh);\n if (deformedPositions) {\n positionBuffer = createMappedBuffer(engine, deformedPositions, BU.VERTEX);\n tempBuffers.push(positionBuffer);\n }\n }\n\n pass.setPipeline(pipelines.regularPipeline);\n pass.setBindGroup(0, picker._sceneBG!);\n pass.setBindGroup(\n 1,\n device.createBindGroup({\n layout: pipelines.regularPipeline.getBindGroupLayout(1),\n entries: [{ binding: 0, resource: { buffer: meshUbo } }],\n })\n );\n if (discardBG) {\n pass.setBindGroup(2, discardBG);\n }\n pass.setVertexBuffer(0, positionBuffer);\n pass.setIndexBuffer(gpu.indexBuffer, gpu.indexFormat);\n pass.drawIndexed(gpu.indexCount);\n nextId++;\n }\n }\n\n // ── Gaussian-splatting meshes ────────────────────────────────────\n // Drawn from the same pass against the same depth target. Each GS mesh\n // gets one pick id (no thin-instance support — BJS GS picking is per-mesh\n // too). The GS picking pipeline applies an independent pickMatrix to the\n // GS clip-space output so the EWA Jacobian / `u.focal` math stays intact.\n const gsMeshes = (scene as unknown as { _gsMeshes: GaussianSplattingMesh[] })._gsMeshes;\n const gsMeshCount = gsMeshes.length;\n const gsNextIdStart = nextId;\n if (gsMeshCount > 0) {\n const gsModule = await import(\"./gs-picking-pipeline.js\");\n gsModule.computeGsPickMatrix(_gsPickMatrix, px, py, w, h);\n gsModule.gsPickWritePickMatrixAndBind(pass, engine, _gsPickMatrix);\n const resMap = picker._gsMeshResources ?? (picker._gsMeshResources = new Map());\n for (let gi = 0; gi < gsMeshCount; gi++) {\n const gsMesh = gsMeshes[gi]!;\n let res = resMap.get(gsMesh);\n if (!res) {\n res = gsModule.createGsPickMeshResources(engine, gsMesh);\n resMap.set(gsMesh, res);\n }\n gsModule.drawGsForPicking(pass, engine, scene, gsMesh, res, nextId, w, h);\n nextId++;\n }\n }\n pass.end();\n\n // ── Readback (both 1×1 — trivially small) ────────────────────────\n encoder.copyTextureToBuffer({ texture: rt.colorTex }, { buffer: rt.colorStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n encoder.copyTextureToBuffer({ texture: rt.depthColorTex }, { buffer: rt.depthStaging, bytesPerRow: 256 }, { width: 1, height: 1 });\n device.queue.submit([encoder.finish()]);\n\n await Promise.all([rt.colorStaging.mapAsync(GPUMapMode.READ), rt.depthStaging.mapAsync(GPUMapMode.READ)]);\n\n const colorData = new U8(rt.colorStaging.getMappedRange());\n const pickId = (colorData[0]! << 16) | (colorData[1]! << 8) | colorData[2]!;\n const depth = new F32(rt.depthStaging.getMappedRange())[0]!;\n rt.colorStaging.unmap();\n rt.depthStaging.unmap();\n\n // Destroy temp per-mesh UBOs\n for (let i = 0; i < tempBuffers.length; i++) {\n tempBuffers[i]!.destroy();\n }\n\n // ── Resolve pick ID to mesh ──────────────────────────────────────\n if (pickId === 0) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n });\n }\n return createEmptyPickingInfo();\n }\n let hitMesh: Mesh | GaussianSplattingMesh | null = null;\n let hitThinIdx = -1;\n let hitIsGs = false;\n let scanId = 1;\n for (let mi = 0; mi < meshCount; mi++) {\n const mesh = meshes[mi]!;\n if (mesh.pickable === false) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n if (pickFilter && !pickFilter(mesh)) {\n continue; // skipped identically to the draw pass above so scanId stays aligned with the ids\n }\n const ti = mesh.thinInstances;\n if (ti) {\n if (ti.count <= 0 || !ti._gpuBuffer) {\n continue;\n }\n if (pickId >= scanId && pickId < scanId + ti.count) {\n hitMesh = mesh;\n hitThinIdx = pickId - scanId;\n break;\n }\n scanId += ti.count;\n } else {\n if (pickId === scanId) {\n hitMesh = mesh;\n break;\n }\n scanId++;\n }\n }\n if (!hitMesh && gsMeshCount > 0 && pickId >= gsNextIdStart) {\n const gsIdx = pickId - gsNextIdStart;\n if (gsIdx < gsMeshCount) {\n hitMesh = gsMeshes[gsIdx]!;\n hitIsGs = true;\n }\n }\n if (!hitMesh) {\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: debugRay,\n pickId,\n depth,\n hit: false,\n unresolved: true,\n });\n }\n return createEmptyPickingInfo();\n }\n\n const info = createEmptyPickingInfo();\n info.hit = true;\n info.pickedMesh = hitMesh;\n info.thinInstanceIndex = hitThinIdx;\n\n // Reconstruct world position from depth (using original full-res VP)\n const invVP = mat4Invert(vp);\n if (invVP) {\n const ndcX = (2 * (pickX - viewport.x)) / w - 1;\n const ndcY = 1 - (2 * (pickY - viewport.y)) / h;\n const wx = invVP[0]! * ndcX + invVP[4]! * ndcY + invVP[8]! * depth + invVP[12]!;\n const wy = invVP[1]! * ndcX + invVP[5]! * ndcY + invVP[9]! * depth + invVP[13]!;\n const wz = invVP[2]! * ndcX + invVP[6]! * ndcY + invVP[10]! * depth + invVP[14]!;\n const ww = invVP[3]! * ndcX + invVP[7]! * ndcY + invVP[11]! * depth + invVP[15]!;\n const invW = 1 / ww;\n info.pickedPoint = [wx * invW, wy * invW, wz * invW];\n\n const camPos = getCameraPosition(camera);\n const dx = info.pickedPoint[0] - camPos.x;\n const dy = info.pickedPoint[1] - camPos.y;\n const dz = info.pickedPoint[2] - camPos.z;\n info.distance = Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n if (picker._detailedPick && !hitIsGs) {\n const ray = createPickingRay(pickX - viewport.x, pickY - viewport.y, vp, w, h);\n if (ray) {\n info.ray = ray;\n await picker._detailedPick(info, ray);\n }\n }\n if (debugLabel) {\n console.trace(\"pick-debug\", {\n label: debugLabel,\n input: { x, y, pickX, pickY, px, py, backingWidth, backingHeight, clientWidth, clientHeight, viewport },\n ray: info.ray ?? debugRay,\n pickId,\n depth,\n hit: true,\n mesh: (hitMesh as Mesh).name ?? \"(unnamed)\",\n thinInstanceIndex: hitThinIdx,\n pickedPoint: info.pickedPoint,\n distance: info.distance,\n });\n }\n\n return info;\n}\n\n/**\n * Pick the mesh at CSS-space canvas coordinates, matching Babylon.js Scene.pick. Returns a PickingInfo.\n *\n * A picker's 1×1 readback targets (`PickTargets1x1.colorStaging`/`depthStaging`) are lazily created ONCE\n * and reused for every pick — cheap, but it means two overlapping `pickAsync` calls on the SAME picker\n * race `mapAsync` on those shared buffers (\"Buffer already has an outstanding map pending\", since a\n * GPUBuffer allows only one pending map at a time). This is easy to trigger from a consumer: e.g. a\n * cursor-following hover preview that GPU-picks on every pointermove, racing a pick fired by a click that\n * lands before the hover's pick has unmapped. Queue concurrent calls per-picker instead of rejecting: each\n * pick's full map/unmap cycle completes before the next one starts.\n */\nexport function pickAsync(picker: GpuPicker, x: number, y: number, options?: PickOptions): Promise<PickingInfo> {\n const prior = picker._pending ?? Promise.resolve();\n const run = prior.then(\n () => pickAsyncImpl(picker, x, y, options),\n () => pickAsyncImpl(picker, x, y, options) // a prior pick's rejection must not wedge the queue for this caller\n );\n // Swallow so a rejection here doesn't propagate into the NEXT caller's chain (each caller gets its own\n // `run` promise and observes the real rejection via the returned promise, not via `_pending`).\n picker._pending = run.then(\n () => undefined,\n () => undefined\n );\n return run;\n}\n\n/** Dispose GPU resources owned by this picker. */\nexport function disposePicker(picker: GpuPicker): void {\n if (picker._rt) {\n picker._rt.colorTex.destroy();\n picker._rt.depthColorTex.destroy();\n picker._rt.depthTex.destroy();\n picker._rt.colorStaging.destroy();\n picker._rt.depthStaging.destroy();\n picker._rt = null;\n }\n if (picker._sceneUbo) {\n picker._sceneUbo.destroy();\n picker._sceneUbo = null;\n picker._sceneBG = null;\n }\n if (picker._gsMeshResources) {\n // Async dispose — destroy() is synchronous so we can run inline once\n // the module is loaded. If the module was never imported (no GS pick\n // ever happened) the map is empty and there's nothing to do.\n void import(\"./gs-picking-pipeline.js\").then((m) => {\n if (!picker._gsMeshResources) {\n return;\n }\n for (const res of picker._gsMeshResources.values()) {\n m.disposeGsPickMeshResources(res);\n }\n picker._gsMeshResources = null;\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
package/lib/scene/scene-core.js
CHANGED
|
@@ -2,6 +2,7 @@ import { unregisterRenderingContext, isRenderingContextRegistered, registerRende
|
|
|
2
2
|
import { disposeMeshGpu } from '../mesh/mesh-dispose.js';
|
|
3
3
|
import { registerMeshScene, enqueueMaterialSwap, unregisterMeshScene } from './mesh-scene-registry.js';
|
|
4
4
|
import { processMaterialSwaps } from './scene-material-swap.js';
|
|
5
|
+
import { tickAnimation } from '../animation/animation-tick.js';
|
|
5
6
|
import { _appendTask, createFrameGraph } from '../frame-graph/frame-graph.js';
|
|
6
7
|
import { createRenderTask } from '../frame-graph/render-task.js';
|
|
7
8
|
import { createRenderTarget } from '../engine/render-target.js';
|
|
@@ -29,10 +30,12 @@ function createSceneContext(surface, options) {
|
|
|
29
30
|
_groups: /* @__PURE__ */ new Map(),
|
|
30
31
|
_disposables: [],
|
|
31
32
|
_meshDisposables: /* @__PURE__ */ new Map(),
|
|
33
|
+
_meshAuxDisposables: /* @__PURE__ */ new Map(),
|
|
32
34
|
_materialSwapQueue: [],
|
|
33
35
|
_renderableVersion: 0,
|
|
34
36
|
_materialEpoch: 0,
|
|
35
37
|
_built: false,
|
|
38
|
+
_builtGroups: /* @__PURE__ */ new Set(),
|
|
36
39
|
_drawCallsPre: 0,
|
|
37
40
|
_update() {
|
|
38
41
|
if (eng.useFloatingOrigin && ctx.camera && !ctx.camera._useFloatingOrigin) {
|
|
@@ -114,9 +117,7 @@ function addToScene(scene, entity) {
|
|
|
114
117
|
ctx.animationGroups.push(...groups);
|
|
115
118
|
ctx._beforeRender.push((deltaMs) => {
|
|
116
119
|
for (const g of groups) {
|
|
117
|
-
|
|
118
|
-
g._ctrl.tick(deltaMs, engine);
|
|
119
|
-
}
|
|
120
|
+
tickAnimation(g, deltaMs, engine);
|
|
120
121
|
}
|
|
121
122
|
});
|
|
122
123
|
}
|
|
@@ -138,10 +139,11 @@ function addToScene(scene, entity) {
|
|
|
138
139
|
if (result.updater) {
|
|
139
140
|
ctx._uniformUpdaters.push(result.updater);
|
|
140
141
|
}
|
|
142
|
+
ctx._builtGroups.add(build);
|
|
141
143
|
});
|
|
142
144
|
}
|
|
143
145
|
group.push(mesh);
|
|
144
|
-
if (ctx._built) {
|
|
146
|
+
if (ctx._built || ctx._builtGroups.has(build)) {
|
|
145
147
|
enqueueMaterialSwap(ctx, mesh);
|
|
146
148
|
}
|
|
147
149
|
}
|
|
@@ -168,6 +170,12 @@ function disposeScene(scene) {
|
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
ctx._meshDisposables.clear();
|
|
173
|
+
for (const fns of ctx._meshAuxDisposables.values()) {
|
|
174
|
+
for (const fn of fns) {
|
|
175
|
+
fn();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
ctx._meshAuxDisposables.clear();
|
|
171
179
|
for (const mesh of ctx.meshes) {
|
|
172
180
|
if (unregisterMeshScene(ctx, mesh)) {
|
|
173
181
|
disposeMeshGpu(mesh);
|
|
@@ -189,12 +197,13 @@ function disposeScene(scene) {
|
|
|
189
197
|
}
|
|
190
198
|
async function buildScene(scene) {
|
|
191
199
|
const ctx = scene;
|
|
200
|
+
ctx._materialSwapQueue.length = 0;
|
|
192
201
|
while (ctx._deferredBuilders.length > 0) {
|
|
193
202
|
const builders = [...ctx._deferredBuilders];
|
|
194
203
|
ctx._deferredBuilders = [];
|
|
195
204
|
await Promise.all(builders.map(async (b) => b()));
|
|
196
205
|
}
|
|
197
|
-
ctx
|
|
206
|
+
processMaterialSwaps(ctx);
|
|
198
207
|
ctx._renderableVersion++;
|
|
199
208
|
ctx._built = true;
|
|
200
209
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-core.js","sources":["../../../src/scene/scene-core.ts"],"sourcesContent":["import type { EngineContext, RenderingContext } from \"../engine/engine.js\";\nimport { _vis, isRenderingContextRegistered, registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { registerMeshScene, unregisterMeshScene, enqueueMaterialSwap } from \"./mesh-scene-registry.js\";\nimport { processMaterialSwaps } from \"./scene-material-swap.js\";\nimport type { AnimationGroup } from \"../animation/animation-group.js\";\nimport type { ShadowGenerator } from \"../shadow/shadow-generator.js\";\nimport type { FogConfig } from \"../material/standard/standard-material.js\";\nimport type { Renderable, PrePassRenderable, SceneUniformUpdater, MeshGroupBuilder } from \"../render/renderable.js\";\nimport type { TransformNode } from \"./transform-node.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport type { EnvironmentTextures } from \"../loader-env/load-env.js\";\nimport type { FrameGraph } from \"../frame-graph/frame-graph.js\";\nimport { createFrameGraph, _appendTask } from \"../frame-graph/frame-graph.js\";\nimport { createRenderTask } from \"../frame-graph/render-task.js\";\nimport { createRenderTarget } from \"../engine/render-target.js\";\nimport type { AssetContainer } from \"../asset-container.js\";\nimport type { SceneLightGpuState } from \"../render/lights-ubo.js\";\nimport type { ClusteredLightContainer } from \"../light/clustered.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\n\n/** Image processing configuration. */\nexport interface ImageProcessingConfig {\n exposure: number;\n contrast: number;\n toneMappingEnabled: boolean;\n /** \"standard\" (BJS TONEMAPPING_STANDARD, default) or \"aces\" (BJS TONEMAPPING_ACES). */\n toneMappingType?: \"standard\" | \"aces\";\n}\n\n/** A clipping plane expressed as the coefficients `[a, b, c, d]` of `a·x + b·y + c·z + d`. */\nexport type ClipPlane = readonly [number, number, number, number];\n\n/** Top-level scene context — pure state, no attached methods. */\nexport interface SceneContext extends RenderingContext {\n /** Surface this scene renders into. Set at scene-creation time and immutable\n * afterwards — the default render task is sized and MSAA-matched to this surface,\n * and `registerScene` attaches the scene to it. For the engine's primary surface\n * (the common single-canvas case) this is the engine itself. The owning engine is\n * reachable via `scene.surface.engine`. */\n readonly surface: SurfaceContext;\n clearColor: GPUColorDict;\n camera: Camera | null;\n lights: LightBase[];\n imageProcessing: ImageProcessingConfig;\n\n /** All meshes added to the scene (standard + PBR). */\n meshes: Mesh[];\n\n /** Animation groups loaded from glTF or created manually. */\n animationGroups: AnimationGroup[];\n\n /** Fog configuration. Null = no fog. */\n fog: FogConfig | null;\n\n /** Scene clip plane as (normal.x, normal.y, normal.z, d). Matches Babylon.js Plane `dot(worldPosition, plane) > 0` discard semantics. */\n clipPlane: ClipPlane | null;\n\n /** Shadow generators registered on this scene. */\n shadowGenerators: ShadowGenerator[];\n\n /** Background material primaryColor (linear RGB). Default from Babylon createDefaultEnvironment. */\n environmentPrimaryColor?: [number, number, number];\n\n /** Environment cubemap Y rotation in radians. */\n envRotationY?: number;\n\n /** Fixed delta time in ms for deterministic animation. 0 = use real rAF delta. */\n fixedDeltaMs: number;\n\n /** All renderables in this scene. The active frame-graph tasks bucket them\n * (opaque / direct / transparent) at bind time based on `isTransparent`, `_direct`, and `_transmissive`. */\n /** @internal */\n _renderables: Renderable[];\n /** @internal Pre-pass work (shadow maps, compute, etc.). */\n _prePasses: PrePassRenderable[];\n /** GaussianSplatting meshes attached to this scene. Populated by\n * `attachGaussianSplattingMesh`. Scene-core stays GS-agnostic apart from\n * this opaque registry (used by `gpu-picker` to iterate GS meshes without\n * scanning `_renderables`). */\n /** @internal */\n _gsMeshes: GaussianSplattingMesh[];\n /** @internal Scene uniform updaters (one per shared UBO). */\n _uniformUpdaters: SceneUniformUpdater[];\n /** @internal Opt-in feature writers for the SceneUniforms UBO (fog, clip plane, env SH).\n * Populated lazily via the scene-ubo-extras seam; run by the render task. */\n _sceneUboContributors?: ((data: Float32Array, scene: SceneContext) => void)[];\n /** @internal Per-frame callbacks run before rendering (animation, physics, etc.). */\n _beforeRender: ((deltaMs: number) => void)[];\n /** @internal Deferred builders — registered by loaders/factories, run once at startEngine(). */\n _deferredBuilders: (() => void | Promise<void>)[];\n /** @internal Mesh group registry — maps builder to its mesh list (internal bookkeeping). */\n _groups: Map<MeshGroupBuilder, Mesh[]>;\n\n // ─── Dispose infrastructure ────────────────────────────────\n /** @internal Shared cleanup callbacks (scene UBOs, lights UBOs, etc.). Registered by builders. */\n _disposables: (() => void)[];\n /** @internal Per-mesh cleanup callbacks (mesh UBOs, bind groups). For material swap + dispose. */\n _meshDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Meshes whose material was changed via setter — drained before each render frame. */\n _materialSwapQueue: Mesh[];\n /** @internal Monotonic counter bumped when the renderable list changes (add/remove/rebuild). */\n _renderableVersion: number;\n /** @internal Monotonic counter bumped ONLY when a material's renderables are rebuilt/swapped (material\n * swap drain or `rebuildMaterial`) — NOT on a geometry resize (which bumps `_renderableVersion` alone).\n * Lets consumers that cache material-view-derived GPU state (e.g. the CSM shadow tasks' no-color material\n * views) cheaply re-record on a geometry-only edit and only fully rebuild when a caster's material UBOs\n * were actually destroyed/recreated (which would otherwise leave their cached views dangling). */\n _materialEpoch: number;\n /** True once the initial deferred build (buildScene) has run. Meshes added after\n * this point are materialized via the per-frame swap drain rather than the\n * boot-only deferred-builder path. */\n /** @internal */\n _built: boolean;\n\n // ─── Stashed internal state (typed to avoid `as any` casts) ────\n /** @internal */\n _envTextures?: EnvironmentTextures;\n /** @internal Scene-owned shared LightsUniforms UBO state (group 0 binding 1). */\n _lightGpuState?: SceneLightGpuState;\n\n /** Frame graph driving this scene's rendering. Created eagerly by\n * `createSceneContext` with a default `RenderTask` that mirrors\n * `_renderables` into the swapchain. User code may add additional tasks\n * (offscreen RTTs, post-FX, UI overlays, etc.). */\n /** @internal */\n _frameGraph: FrameGraph;\n\n /** @internal Optional clustered point-light container. Only populated by the clustered-light extension API. */\n _clusteredLightContainer?: ClusteredLightContainer;\n /** @internal Updates clustered light cells for the camera used by the current render pass. */\n _clusteredLightUpdater?: (camera: Camera | null | undefined, targetWidth: number, targetHeight: number) => void;\n}\n\n/** Options passed to the scene-context factory. */\nexport interface SceneContextOptions {\n defaultRenderTask?: boolean;\n}\n\n/** Create an empty scene context bound to the given `surface`. The default render task\n * is built against the surface's format, MSAA configuration, and swapchain RT — the\n * scene is permanently bound to that surface. Pass `engine` directly (since\n * `EngineContext extends SurfaceContext`) for the common single-canvas case, or pass\n * an auxiliary surface created via `createSurface`. */\nexport function createSceneContext(surface: SurfaceContext, options?: SceneContextOptions): SceneContext {\n const eng = surface.engine;\n\n // Closures below capture `ctx` by-reference via this object.\n const ctxLocal: Omit<SceneContext, \"_frameGraph\"> = {\n surface,\n clearColor: { r: 0.2, g: 0.2, b: 0.3, a: 1.0 },\n camera: null,\n lights: [],\n meshes: [],\n animationGroups: [],\n fog: null,\n clipPlane: null,\n shadowGenerators: [],\n imageProcessing: { exposure: 1.0, contrast: 1.0, toneMappingEnabled: false },\n _renderables: [],\n _prePasses: [],\n _gsMeshes: [],\n _uniformUpdaters: [],\n fixedDeltaMs: 0,\n _beforeRender: [],\n _deferredBuilders: [],\n _groups: new Map(),\n _disposables: [],\n _meshDisposables: new Map(),\n _materialSwapQueue: [],\n _renderableVersion: 0,\n _materialEpoch: 0,\n _built: false,\n _drawCallsPre: 0,\n\n _update(): void {\n // When the engine was created with `useFloatingOrigin: true`, mark\n // the active camera so `getViewMatrix` knows to zero its\n // translation column (the GPU view × world product is then the\n // eye-relative result the LWR offset trick produces). For non-LWR\n // engines `eng.useFloatingOrigin` is false and this is a single\n // boolean check per frame — the inner branch is dead.\n if (eng.useFloatingOrigin && ctx.camera && !ctx.camera._useFloatingOrigin) {\n ctx.camera._useFloatingOrigin = true;\n ctx.camera._viewVer = -1;\n ctx.camera._vpVer = -1;\n }\n const d = ctx.fixedDeltaMs > 0 ? ctx.fixedDeltaMs : eng._currentDelta;\n const encoder = eng._currentEncoder;\n let draws = 0;\n for (const cb of ctx._beforeRender) {\n cb(d);\n }\n if (ctx._materialSwapQueue.length > 0) {\n processMaterialSwaps(ctx);\n }\n for (const pp of ctx._prePasses) {\n draws += pp.execute(encoder, eng);\n }\n for (const u of ctx._uniformUpdaters) {\n u.update(eng);\n }\n ctx._drawCallsPre = draws;\n },\n _record(): number {\n return ctx._frameGraph.execute();\n },\n _resize(): void {\n // Canvas backing-store changed: rebuild the frame graph so canvas-sized\n // render targets get re-allocated at the new pixel size before the next record.\n ctx._frameGraph.build();\n },\n };\n\n const ctx = ctxLocal as SceneContext;\n // Eagerly attach the frame graph + a default swapchain render-pass task. The\n // graph drives all GPU work for this scene; user code can add more tasks\n // (offscreen RTTs, post-FX, UI overlays) before/after.\n const fg = createFrameGraph(eng);\n ctx._frameGraph = fg;\n if (options?.defaultRenderTask !== false) {\n // MSAA: render into an MSAA colour RT (which owns depth) and resolve into the\n // single-sample scRT. No MSAA: render straight into the colour-only\n // scRT with a task-owned single-sample depth buffer it builds/clears/frees.\n // All three reads (format / msaaSamples / scRT) come from the bound `surface`.\n const msaa = surface.msaaSamples > 1;\n const rt = msaa\n ? createRenderTarget({ lbl: \"scene-color\", format: surface.format, dFormat: \"depth24plus-stencil8\", samples: surface.msaaSamples, size: surface })\n : surface.scRT;\n const depth = msaa ? undefined : createRenderTarget({ lbl: \"scene-depth\", dFormat: \"depth24plus-stencil8\", samples: 1, size: surface });\n _appendTask(fg, createRenderTask({ name: \"scene\", rt, rst: msaa ? surface.scRT : undefined, depth, clrColor: ctx.clearColor }, eng, ctx));\n }\n ctx._disposables.push(() => fg.dispose());\n return ctx;\n}\n\n/** Register a callback to run before each rendered frame. */\nexport function onBeforeRender(scene: SceneContext, cb: (deltaMs: number) => void): void {\n (scene as SceneContext)._beforeRender.unshift(cb);\n}\n\n/** Register a callback to run when `disposeScene` is called. Used to tie\n * user-owned GPU resources (e.g. a `SpriteRenderer`) to the scene's lifetime. */\nexport function onSceneDispose(scene: SceneContext, cb: () => void): void {\n (scene as SceneContext)._disposables.push(cb);\n}\n\n/** Get the scene's frame graph. Always non-null — created in `createSceneContext`. */\nexport function getFrameGraph(scene: SceneContext): FrameGraph {\n return (scene as SceneContext)._frameGraph;\n}\n\nexport interface DeferredSceneRenderables {\n renderables: readonly Renderable[];\n dispose?: () => void;\n}\n\n/** @internal Register optional scene-hosted render work without teaching `addToScene` about the feature. */\nexport function addDeferredSceneRenderables(\n scene: SceneContext,\n build: (engine: EngineContext, scene: SceneContext) => DeferredSceneRenderables | Promise<DeferredSceneRenderables>\n): void {\n const ctx = scene as SceneContext;\n ctx._deferredBuilders.push(async () => {\n const built = await build(ctx.surface.engine, ctx);\n ctx._renderables.push(...built.renderables);\n if (built.dispose) {\n ctx._disposables.push(built.dispose);\n }\n });\n}\n\n/**\n * Adds an entity (mesh, light, camera, transform node, shadow generator, or asset container)\n * to the scene, dispatching on its type. Asset containers are unpacked and each contained\n * entity added recursively. Optional scene-hosted systems such as depth-hosted sprites\n * expose their own opt-in add functions so mesh-only scenes do not pay feature-specific\n * routing bytes here.\n * @param scene - The owning scene (pillar 4b: entities never reference the scene themselves).\n * @param entity - The entity (or asset container) to add.\n */\nexport function addToScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void {\n const ctx = scene as SceneContext;\n // AssetContainer from loadGltf / loadBabylon — process each field present\n if (\"entities\" in entity) {\n const result = entity as AssetContainer;\n for (const e of result.entities) {\n addToScene(scene, e);\n }\n if (result.clearColor) {\n ctx.clearColor = result.clearColor;\n }\n if (result.camera && !ctx.camera) {\n ctx.camera = result.camera;\n }\n if (result.animationGroups?.length) {\n const engine = ctx.surface.engine;\n const groups = result.animationGroups;\n ctx.animationGroups.push(...groups);\n ctx._beforeRender.push((deltaMs: number) => {\n for (const g of groups) {\n if (!g._stopped && g._ctrl) {\n g._ctrl.tick(deltaMs, engine);\n }\n }\n });\n }\n return;\n }\n if (\"_gpu\" in entity && \"material\" in entity) {\n const mesh = entity as unknown as Mesh;\n ctx.meshes.push(mesh);\n registerMeshScene(ctx, mesh);\n const build = mesh.material ? (mesh.material as unknown as { _buildGroup?: MeshGroupBuilder })._buildGroup : undefined;\n if (build) {\n let group = ctx._groups.get(build);\n if (!group) {\n group = [];\n ctx._groups.set(build, group);\n ctx._deferredBuilders.push(async () => {\n const result = await build(ctx, group!);\n ctx._renderables.push(...result.renderables);\n if (result.updater) {\n ctx._uniformUpdaters.push(result.updater);\n }\n });\n }\n group.push(mesh);\n // Added after the initial build: the deferred builder for this group has\n // already run (and only runs at boot), so materialize this mesh's renderable\n // through the per-frame material-swap drain instead.\n if (ctx._built) {\n enqueueMaterialSwap(ctx, mesh);\n }\n }\n } else if (\"lightType\" in entity) {\n ctx.lights.push(entity as LightBase);\n }\n // Recurse into children of meshes, lights, cameras — set parent links\n const kids = (entity as unknown as SceneNode).children;\n if (kids?.length) {\n for (const child of kids) {\n (child as unknown as SceneNode).parent = entity as unknown as SceneNode;\n addToScene(scene, child);\n }\n }\n}\n\n/** Release all GPU resources owned by this scene. */\nexport function disposeScene(scene: SceneContext): void {\n const ctx = scene as SceneContext;\n unregisterRenderingContext(ctx.surface, ctx);\n for (const fn of ctx._disposables) {\n fn();\n }\n for (const fns of ctx._meshDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshDisposables.clear();\n for (const mesh of ctx.meshes) {\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene.\n if (unregisterMeshScene(ctx, mesh)) {\n disposeMeshGpu(mesh);\n }\n }\n ctx.meshes.length = 0;\n ctx._renderables.length = 0;\n ctx._prePasses.length = 0;\n ctx._gsMeshes.length = 0;\n ctx._uniformUpdaters.length = 0;\n ctx._beforeRender.length = 0;\n ctx._deferredBuilders.length = 0;\n ctx._disposables.length = 0;\n ctx._materialSwapQueue.length = 0;\n ctx.lights.length = 0;\n ctx.animationGroups.length = 0;\n ctx.shadowGenerators.length = 0;\n ctx.camera = null;\n}\n\n/** @internal Run all deferred builders (called by registerScene's boot step before the first frame). */\nexport async function buildScene(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n while (ctx._deferredBuilders.length > 0) {\n const builders = [...ctx._deferredBuilders];\n ctx._deferredBuilders = [];\n await Promise.all(builders.map(async (b) => b()));\n }\n ctx._materialSwapQueue.length = 0;\n ctx._renderableVersion++;\n ctx._built = true;\n}\n\n/**\n * Register a scene with the engine. Builds deferred work, sorts renderables by order,\n * and adds the scene to its bound surface's render list in overlay order. The scene is\n * always attached to `scene.surface` (which equals the engine itself in the\n * single-canvas case).\n */\nexport async function registerScene(scene: SceneContext): Promise<void> {\n const ctx = scene;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\n/**\n * Register a scene with the engine and install the scene-owned shadow frame-graph task.\n * Use only for scenes that generate shadow maps. Like {@link registerScene}, the scene\n * is attached to `scene.surface` (and its owning engine is `scene.surface.engine`).\n */\nexport async function registerSceneWithShadowSupport(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await ensureShadowTask(surface.engine, ctx);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\nconst byOrder = (a: Renderable, b: Renderable): number => a.order - b.order;\n\nasync function ensureShadowTask(engine: EngineContext, scene: SceneContext): Promise<void> {\n const { createShadowTask } = await import(\"../frame-graph/shadow-task.js\");\n scene._frameGraph._tasks.unshift(createShadowTask(engine, scene));\n}\n\n/** Remove a previously-registered scene. Idempotent. Does not dispose scene resources.\n * The scene is always removed from `scene.surface`. */\nexport function unregisterScene(scene: SceneContext): void {\n unregisterRenderingContext(scene.surface, scene as SceneContext);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scene-core.js","sources":["../../../src/scene/scene-core.ts"],"sourcesContent":["import type { EngineContext, RenderingContext } from \"../engine/engine.js\";\nimport { _vis, isRenderingContextRegistered, registerRenderingContext, unregisterRenderingContext } from \"../engine/engine.js\";\nimport type { SurfaceContext } from \"../engine/surface.js\";\nimport type { Camera } from \"../camera/camera.js\";\nimport type { LightBase } from \"../light/types.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { registerMeshScene, unregisterMeshScene, enqueueMaterialSwap } from \"./mesh-scene-registry.js\";\nimport { processMaterialSwaps } from \"./scene-material-swap.js\";\nimport type { AnimationGroup } from \"../animation/animation-group.js\";\nimport { tickAnimation } from \"../animation/animation-tick.js\";\nimport type { ShadowGenerator } from \"../shadow/shadow-generator.js\";\nimport type { FogConfig } from \"../material/standard/standard-material.js\";\nimport type { Renderable, PrePassRenderable, SceneUniformUpdater, MeshGroupBuilder } from \"../render/renderable.js\";\nimport type { TransformNode } from \"./transform-node.js\";\nimport type { SceneNode } from \"./scene-node.js\";\nimport type { EnvironmentTextures } from \"../loader-env/load-env.js\";\nimport type { FrameGraph } from \"../frame-graph/frame-graph.js\";\nimport { createFrameGraph, _appendTask } from \"../frame-graph/frame-graph.js\";\nimport { createRenderTask } from \"../frame-graph/render-task.js\";\nimport { createRenderTarget } from \"../engine/render-target.js\";\nimport type { AssetContainer } from \"../asset-container.js\";\nimport type { SceneLightGpuState } from \"../render/lights-ubo.js\";\nimport type { ClusteredLightContainer } from \"../light/clustered.js\";\nimport type { GaussianSplattingMesh } from \"../mesh/GaussianSplatting/gaussian-splatting-mesh.js\";\n\n/** Image processing configuration. */\nexport interface ImageProcessingConfig {\n exposure: number;\n contrast: number;\n toneMappingEnabled: boolean;\n /** \"standard\" (BJS TONEMAPPING_STANDARD, default) or \"aces\" (BJS TONEMAPPING_ACES). */\n toneMappingType?: \"standard\" | \"aces\";\n}\n\n/** A clipping plane expressed as the coefficients `[a, b, c, d]` of `a·x + b·y + c·z + d`. */\nexport type ClipPlane = readonly [number, number, number, number];\n\n/** Top-level scene context — pure state, no attached methods. */\nexport interface SceneContext extends RenderingContext {\n /** Surface this scene renders into. Set at scene-creation time and immutable\n * afterwards — the default render task is sized and MSAA-matched to this surface,\n * and `registerScene` attaches the scene to it. For the engine's primary surface\n * (the common single-canvas case) this is the engine itself. The owning engine is\n * reachable via `scene.surface.engine`. */\n readonly surface: SurfaceContext;\n clearColor: GPUColorDict;\n camera: Camera | null;\n lights: LightBase[];\n imageProcessing: ImageProcessingConfig;\n\n /** All meshes added to the scene (standard + PBR). */\n meshes: Mesh[];\n\n /** Animation groups loaded from glTF or created manually. */\n animationGroups: AnimationGroup[];\n\n /** Fog configuration. Null = no fog. */\n fog: FogConfig | null;\n\n /** Scene clip plane as (normal.x, normal.y, normal.z, d). Matches Babylon.js Plane `dot(worldPosition, plane) > 0` discard semantics. */\n clipPlane: ClipPlane | null;\n\n /** Shadow generators registered on this scene. */\n shadowGenerators: ShadowGenerator[];\n\n /** Background material primaryColor (linear RGB). Default from Babylon createDefaultEnvironment. */\n environmentPrimaryColor?: [number, number, number];\n\n /** Environment cubemap Y rotation in radians. */\n envRotationY?: number;\n\n /** Fixed delta time in ms for deterministic animation. 0 = use real rAF delta. */\n fixedDeltaMs: number;\n\n /** All renderables in this scene. The active frame-graph tasks bucket them\n * (opaque / direct / transparent) at bind time based on `isTransparent`, `_direct`, and `_transmissive`. */\n /** @internal */\n _renderables: Renderable[];\n /** @internal Pre-pass work (shadow maps, compute, etc.). */\n _prePasses: PrePassRenderable[];\n /** GaussianSplatting meshes attached to this scene. Populated by\n * `attachGaussianSplattingMesh`. Scene-core stays GS-agnostic apart from\n * this opaque registry (used by `gpu-picker` to iterate GS meshes without\n * scanning `_renderables`). */\n /** @internal */\n _gsMeshes: GaussianSplattingMesh[];\n /** @internal Scene uniform updaters (one per shared UBO). */\n _uniformUpdaters: SceneUniformUpdater[];\n /** @internal Opt-in feature writers for the SceneUniforms UBO (fog, clip plane, env SH).\n * Populated lazily via the scene-ubo-extras seam; run by the render task. */\n _sceneUboContributors?: ((data: Float32Array, scene: SceneContext) => void)[];\n /** @internal Per-frame callbacks run before rendering (animation, physics, etc.). */\n _beforeRender: ((deltaMs: number) => void)[];\n /** @internal Deferred builders — registered by loaders/factories, run once at startEngine(). */\n _deferredBuilders: (() => void | Promise<void>)[];\n /** @internal Mesh group registry — maps builder to its mesh list (internal bookkeeping). */\n _groups: Map<MeshGroupBuilder, Mesh[]>;\n\n // ─── Dispose infrastructure ────────────────────────────────\n /** @internal Shared cleanup callbacks (scene UBOs, lights UBOs, etc.). Registered by builders. */\n _disposables: (() => void)[];\n /** @internal Per-mesh cleanup callbacks (mesh UBOs, bind groups). For material swap + dispose. */\n _meshDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Per-mesh cleanup callbacks for AUX (material-OVERRIDE) view packets that an explicit render\n * task registered on a mesh it does not own — e.g. a depth-prepass / SSAO no-colour view of a wall. Kept\n * SEPARATE from `_meshDisposables` because a MAIN-material swap (`processMaterialSwaps`, which rebuilds\n * only the main renderable) must NOT tear these down: they belong to another task, whose cached bundle\n * would then replay a destroyed system UBO (\"used in submit while destroyed\"). Drained only on a real mesh\n * removal (`removeFromScene`) and scene dispose, exactly like `_meshDisposables` minus the swap path. */\n _meshAuxDisposables: Map<Mesh, (() => void)[]>;\n /** @internal Meshes whose material was changed via setter — drained before each render frame. */\n _materialSwapQueue: Mesh[];\n /** @internal Monotonic counter bumped when the renderable list changes (add/remove/rebuild). */\n _renderableVersion: number;\n /** @internal Monotonic counter bumped ONLY when a material's renderables are rebuilt/swapped (material\n * swap drain or `rebuildMaterial`) — NOT on a geometry resize (which bumps `_renderableVersion` alone).\n * Lets consumers that cache material-view-derived GPU state (e.g. the CSM shadow tasks' no-color material\n * views) cheaply re-record on a geometry-only edit and only fully rebuild when a caster's material UBOs\n * were actually destroyed/recreated (which would otherwise leave their cached views dangling). */\n _materialEpoch: number;\n /** True once the initial deferred build (buildScene) has run. Meshes added after\n * this point are materialized via the per-frame swap drain rather than the\n * boot-only deferred-builder path. */\n /** @internal */\n _built: boolean;\n /** Builders whose deferred group build has COMPLETED. A mesh that joins one of these groups after the fact\n * (post-boot, or a glTF prop whose async load resolves DURING buildScene's drain and joins an already-built\n * group) is materialized via the per-frame swap drain: its group builder has already run and won't see it,\n * so without this it would cast shadows (it's in ctx.meshes) but never be drawn in the color pass. */\n /** @internal */\n _builtGroups: Set<MeshGroupBuilder>;\n\n // ─── Stashed internal state (typed to avoid `as any` casts) ────\n /** @internal */\n _envTextures?: EnvironmentTextures;\n /** @internal Scene-owned shared LightsUniforms UBO state (group 0 binding 1). */\n _lightGpuState?: SceneLightGpuState;\n\n /** Frame graph driving this scene's rendering. Created eagerly by\n * `createSceneContext` with a default `RenderTask` that mirrors\n * `_renderables` into the swapchain. User code may add additional tasks\n * (offscreen RTTs, post-FX, UI overlays, etc.). */\n /** @internal */\n _frameGraph: FrameGraph;\n\n /** @internal Optional clustered point-light container. Only populated by the clustered-light extension API. */\n _clusteredLightContainer?: ClusteredLightContainer;\n /** @internal Updates clustered light cells for the camera used by the current render pass. */\n _clusteredLightUpdater?: (camera: Camera | null | undefined, targetWidth: number, targetHeight: number) => void;\n}\n\n/** Options passed to the scene-context factory. */\nexport interface SceneContextOptions {\n defaultRenderTask?: boolean;\n}\n\n/** Create an empty scene context bound to the given `surface`. The default render task\n * is built against the surface's format, MSAA configuration, and swapchain RT — the\n * scene is permanently bound to that surface. Pass `engine` directly (since\n * `EngineContext extends SurfaceContext`) for the common single-canvas case, or pass\n * an auxiliary surface created via `createSurface`. */\nexport function createSceneContext(surface: SurfaceContext, options?: SceneContextOptions): SceneContext {\n const eng = surface.engine;\n\n // Closures below capture `ctx` by-reference via this object.\n const ctxLocal: Omit<SceneContext, \"_frameGraph\"> = {\n surface,\n clearColor: { r: 0.2, g: 0.2, b: 0.3, a: 1.0 },\n camera: null,\n lights: [],\n meshes: [],\n animationGroups: [],\n fog: null,\n clipPlane: null,\n shadowGenerators: [],\n imageProcessing: { exposure: 1.0, contrast: 1.0, toneMappingEnabled: false },\n _renderables: [],\n _prePasses: [],\n _gsMeshes: [],\n _uniformUpdaters: [],\n fixedDeltaMs: 0,\n _beforeRender: [],\n _deferredBuilders: [],\n _groups: new Map(),\n _disposables: [],\n _meshDisposables: new Map(),\n _meshAuxDisposables: new Map(),\n _materialSwapQueue: [],\n _renderableVersion: 0,\n _materialEpoch: 0,\n _built: false,\n _builtGroups: new Set(),\n _drawCallsPre: 0,\n\n _update(): void {\n // When the engine was created with `useFloatingOrigin: true`, mark\n // the active camera so `getViewMatrix` knows to zero its\n // translation column (the GPU view × world product is then the\n // eye-relative result the LWR offset trick produces). For non-LWR\n // engines `eng.useFloatingOrigin` is false and this is a single\n // boolean check per frame — the inner branch is dead.\n if (eng.useFloatingOrigin && ctx.camera && !ctx.camera._useFloatingOrigin) {\n ctx.camera._useFloatingOrigin = true;\n ctx.camera._viewVer = -1;\n ctx.camera._vpVer = -1;\n }\n const d = ctx.fixedDeltaMs > 0 ? ctx.fixedDeltaMs : eng._currentDelta;\n const encoder = eng._currentEncoder;\n let draws = 0;\n for (const cb of ctx._beforeRender) {\n cb(d);\n }\n if (ctx._materialSwapQueue.length > 0) {\n processMaterialSwaps(ctx);\n }\n for (const pp of ctx._prePasses) {\n draws += pp.execute(encoder, eng);\n }\n for (const u of ctx._uniformUpdaters) {\n u.update(eng);\n }\n ctx._drawCallsPre = draws;\n },\n _record(): number {\n return ctx._frameGraph.execute();\n },\n _resize(): void {\n // Canvas backing-store changed: rebuild the frame graph so canvas-sized\n // render targets get re-allocated at the new pixel size before the next record.\n ctx._frameGraph.build();\n },\n };\n\n const ctx = ctxLocal as SceneContext;\n // Eagerly attach the frame graph + a default swapchain render-pass task. The\n // graph drives all GPU work for this scene; user code can add more tasks\n // (offscreen RTTs, post-FX, UI overlays) before/after.\n const fg = createFrameGraph(eng);\n ctx._frameGraph = fg;\n if (options?.defaultRenderTask !== false) {\n // MSAA: render into an MSAA colour RT (which owns depth) and resolve into the\n // single-sample scRT. No MSAA: render straight into the colour-only\n // scRT with a task-owned single-sample depth buffer it builds/clears/frees.\n // All three reads (format / msaaSamples / scRT) come from the bound `surface`.\n const msaa = surface.msaaSamples > 1;\n const rt = msaa\n ? createRenderTarget({ lbl: \"scene-color\", format: surface.format, dFormat: \"depth24plus-stencil8\", samples: surface.msaaSamples, size: surface })\n : surface.scRT;\n const depth = msaa ? undefined : createRenderTarget({ lbl: \"scene-depth\", dFormat: \"depth24plus-stencil8\", samples: 1, size: surface });\n _appendTask(fg, createRenderTask({ name: \"scene\", rt, rst: msaa ? surface.scRT : undefined, depth, clrColor: ctx.clearColor }, eng, ctx));\n }\n ctx._disposables.push(() => fg.dispose());\n return ctx;\n}\n\n/** Register a callback to run before each rendered frame. */\nexport function onBeforeRender(scene: SceneContext, cb: (deltaMs: number) => void): void {\n (scene as SceneContext)._beforeRender.unshift(cb);\n}\n\n/** Register a callback to run when `disposeScene` is called. Used to tie\n * user-owned GPU resources (e.g. a `SpriteRenderer`) to the scene's lifetime. */\nexport function onSceneDispose(scene: SceneContext, cb: () => void): void {\n (scene as SceneContext)._disposables.push(cb);\n}\n\n/** Get the scene's frame graph. Always non-null — created in `createSceneContext`. */\nexport function getFrameGraph(scene: SceneContext): FrameGraph {\n return (scene as SceneContext)._frameGraph;\n}\n\nexport interface DeferredSceneRenderables {\n renderables: readonly Renderable[];\n dispose?: () => void;\n}\n\n/** @internal Register optional scene-hosted render work without teaching `addToScene` about the feature. */\nexport function addDeferredSceneRenderables(\n scene: SceneContext,\n build: (engine: EngineContext, scene: SceneContext) => DeferredSceneRenderables | Promise<DeferredSceneRenderables>\n): void {\n const ctx = scene as SceneContext;\n ctx._deferredBuilders.push(async () => {\n const built = await build(ctx.surface.engine, ctx);\n ctx._renderables.push(...built.renderables);\n if (built.dispose) {\n ctx._disposables.push(built.dispose);\n }\n });\n}\n\n/**\n * Adds an entity (mesh, light, camera, transform node, shadow generator, or asset container)\n * to the scene, dispatching on its type. Asset containers are unpacked and each contained\n * entity added recursively. Optional scene-hosted systems such as depth-hosted sprites\n * expose their own opt-in add functions so mesh-only scenes do not pay feature-specific\n * routing bytes here.\n * @param scene - The owning scene (pillar 4b: entities never reference the scene themselves).\n * @param entity - The entity (or asset container) to add.\n */\nexport function addToScene(scene: SceneContext, entity: Mesh | LightBase | Camera | ShadowGenerator | TransformNode | AssetContainer): void {\n const ctx = scene as SceneContext;\n // AssetContainer from loadGltf / loadBabylon — process each field present\n if (\"entities\" in entity) {\n const result = entity as AssetContainer;\n for (const e of result.entities) {\n addToScene(scene, e);\n }\n if (result.clearColor) {\n ctx.clearColor = result.clearColor;\n }\n if (result.camera && !ctx.camera) {\n ctx.camera = result.camera;\n }\n if (result.animationGroups?.length) {\n const engine = ctx.surface.engine;\n const groups = result.animationGroups;\n ctx.animationGroups.push(...groups);\n ctx._beforeRender.push((deltaMs: number) => {\n for (const g of groups) {\n tickAnimation(g, deltaMs, engine);\n }\n });\n }\n return;\n }\n if (\"_gpu\" in entity && \"material\" in entity) {\n const mesh = entity as unknown as Mesh;\n ctx.meshes.push(mesh);\n registerMeshScene(ctx, mesh);\n const build = mesh.material ? (mesh.material as unknown as { _buildGroup?: MeshGroupBuilder })._buildGroup : undefined;\n if (build) {\n let group = ctx._groups.get(build);\n if (!group) {\n group = [];\n ctx._groups.set(build, group);\n ctx._deferredBuilders.push(async () => {\n const result = await build(ctx, group!);\n ctx._renderables.push(...result.renderables);\n if (result.updater) {\n ctx._uniformUpdaters.push(result.updater);\n }\n // This group's meshes now have renderables; a mesh that joins it LATER (post-boot or\n // mid-drain) won't be seen by this builder and must be materialized via a swap instead.\n ctx._builtGroups.add(build);\n });\n }\n group.push(mesh);\n // Materialize this mesh's renderable through the per-frame material-swap drain when the boot-only\n // deferred builder won't cover it: either after the initial build (`_built`), or when joining a group\n // whose builder has ALREADY completed (`_builtGroups`) — e.g. a glTF prop whose async load resolves\n // mid-drain and joins an already-built group. A mesh joining a group whose builder has NOT yet run (a\n // brand-new group, or one still pending in the drain) is built by that builder, so it must NOT enqueue\n // here — that would insert a SECOND renderable for it. buildScene drains the queue at the end.\n if (ctx._built || ctx._builtGroups.has(build)) {\n enqueueMaterialSwap(ctx, mesh);\n }\n }\n } else if (\"lightType\" in entity) {\n ctx.lights.push(entity as LightBase);\n }\n // Recurse into children of meshes, lights, cameras — set parent links\n const kids = (entity as unknown as SceneNode).children;\n if (kids?.length) {\n for (const child of kids) {\n (child as unknown as SceneNode).parent = entity as unknown as SceneNode;\n addToScene(scene, child);\n }\n }\n}\n\n/** Release all GPU resources owned by this scene. */\nexport function disposeScene(scene: SceneContext): void {\n const ctx = scene as SceneContext;\n unregisterRenderingContext(ctx.surface, ctx);\n for (const fn of ctx._disposables) {\n fn();\n }\n for (const fns of ctx._meshDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshDisposables.clear();\n for (const fns of ctx._meshAuxDisposables.values()) {\n for (const fn of fns) {\n fn();\n }\n }\n ctx._meshAuxDisposables.clear();\n for (const mesh of ctx.meshes) {\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene.\n if (unregisterMeshScene(ctx, mesh)) {\n disposeMeshGpu(mesh);\n }\n }\n ctx.meshes.length = 0;\n ctx._renderables.length = 0;\n ctx._prePasses.length = 0;\n ctx._gsMeshes.length = 0;\n ctx._uniformUpdaters.length = 0;\n ctx._beforeRender.length = 0;\n ctx._deferredBuilders.length = 0;\n ctx._disposables.length = 0;\n ctx._materialSwapQueue.length = 0;\n ctx.lights.length = 0;\n ctx.animationGroups.length = 0;\n ctx.shadowGenerators.length = 0;\n ctx.camera = null;\n}\n\n/** @internal Run all deferred builders (called by registerScene's boot step before the first frame). */\nexport async function buildScene(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n // Discard material-swap requests enqueued during scene SETUP — a mesh added, then re-materialed before boot\n // via the mesh.material setter (e.g. scene12 assigns each row's material AFTER addToScene). The deferred\n // builders below build every group's meshes fresh with their FINAL material, so those swaps are redundant;\n // processing them would insert a SECOND renderable per mesh (double-draw). Only swaps enqueued DURING the\n // drain below — an async mesh that joins an already-built group (see addToScene/_builtGroups) — must survive.\n ctx._materialSwapQueue.length = 0;\n while (ctx._deferredBuilders.length > 0) {\n const builders = [...ctx._deferredBuilders];\n ctx._deferredBuilders = [];\n await Promise.all(builders.map(async (b) => b()));\n }\n // Build the renderables for any meshes that joined an already-built group mid-drain (queued above) before\n // the first frame, instead of leaving them casting shadows but invisible in the color pass.\n processMaterialSwaps(ctx);\n ctx._renderableVersion++;\n ctx._built = true;\n}\n\n/**\n * Register a scene with the engine. Builds deferred work, sorts renderables by order,\n * and adds the scene to its bound surface's render list in overlay order. The scene is\n * always attached to `scene.surface` (which equals the engine itself in the\n * single-canvas case).\n */\nexport async function registerScene(scene: SceneContext): Promise<void> {\n const ctx = scene;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\n/**\n * Register a scene with the engine and install the scene-owned shadow frame-graph task.\n * Use only for scenes that generate shadow maps. Like {@link registerScene}, the scene\n * is attached to `scene.surface` (and its owning engine is `scene.surface.engine`).\n */\nexport async function registerSceneWithShadowSupport(scene: SceneContext): Promise<void> {\n const ctx = scene as SceneContext;\n const surface = ctx.surface;\n if (isRenderingContextRegistered(surface, ctx)) {\n return;\n }\n await buildScene(scene);\n ctx._renderables.sort(byOrder);\n await ensureShadowTask(surface.engine, ctx);\n await Promise.all(ctx._frameGraph._tasks.map((task) => task._preload?.()).filter((preload): preload is Promise<void> => preload !== undefined));\n ctx._frameGraph.build();\n if (surface._renderingContexts.length > 0) {\n const overlay = await import(\"./swapchain-overlay.js\");\n overlay.configureSwapchainOverlayScene(surface, ctx);\n }\n registerRenderingContext(surface, ctx);\n}\n\nconst byOrder = (a: Renderable, b: Renderable): number => a.order - b.order;\n\nasync function ensureShadowTask(engine: EngineContext, scene: SceneContext): Promise<void> {\n const { createShadowTask } = await import(\"../frame-graph/shadow-task.js\");\n scene._frameGraph._tasks.unshift(createShadowTask(engine, scene));\n}\n\n/** Remove a previously-registered scene. Idempotent. Does not dispose scene resources.\n * The scene is always removed from `scene.surface`. */\nexport function unregisterScene(scene: SceneContext): void {\n unregisterRenderingContext(scene.surface, scene as SceneContext);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,14 @@ function removeFromScene(scene, mesh) {
|
|
|
12
12
|
}
|
|
13
13
|
scene._meshDisposables.delete(mesh);
|
|
14
14
|
}
|
|
15
|
+
const auxFns = scene._meshAuxDisposables.get(mesh);
|
|
16
|
+
if (auxFns) {
|
|
17
|
+
didMutate = true;
|
|
18
|
+
for (const fn of auxFns) {
|
|
19
|
+
fn();
|
|
20
|
+
}
|
|
21
|
+
scene._meshAuxDisposables.delete(mesh);
|
|
22
|
+
}
|
|
15
23
|
const mi2 = scene.meshes.indexOf(mesh);
|
|
16
24
|
if (mi2 >= 0) {
|
|
17
25
|
scene.meshes.splice(mi2, 1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-remove.js","sources":["../../../src/scene/scene-remove.ts"],"sourcesContent":["import type { SceneContext } from \"./scene-core.js\";\nimport { unregisterMeshScene } from \"./mesh-scene-registry.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { removeMeshFromTask } from \"../frame-graph/render-task.js\";\nimport type { RenderTask } from \"../frame-graph/render-task.js\";\n\n/** Remove a mesh from the scene and destroy its GPU resources.\n * Standalone function for tree-shaking — only included when actually used. */\nexport function removeFromScene(scene: SceneContext, mesh: Mesh): void {\n const fns = scene._meshDisposables.get(mesh);\n // Whether this call actually mutated scene state — used to gate the renderable\n // version bump so a no-op removal (mesh never registered) doesn't needlessly\n // invalidate the cached opaque bundle.\n let didMutate = false;\n if (fns) {\n didMutate = true;\n for (const fn of fns) {\n fn();\n }\n scene._meshDisposables.delete(mesh);\n }\n const mi2 = scene.meshes.indexOf(mesh);\n if (mi2 >= 0) {\n scene.meshes.splice(mi2, 1);\n didMutate = true;\n }\n const i = scene._renderables.findIndex((r) => r.mesh === mesh);\n if (i >= 0) {\n scene._renderables.splice(i, 1);\n didMutate = true;\n }\n // Invalidate any auto-mirroring render task so it rebuilds its binding lists +\n // cached opaque bundle without this mesh BEFORE its GPU buffers (vertex data +\n // per-packet system UBO) are touched again. Done whenever the mesh actually\n // belonged to the scene — NOT gated on owning a standalone renderable: meshes\n // sharing a material at the initial scene build are merged into one combined\n // renderable whose `mesh` is undefined, yet their now-destroyed buffers are\n // still referenced by that renderable's update()/draw() and the cached bundle.\n // Mirrors the version bump done on add (material-swap) and initial build.\n if (didMutate) {\n scene._renderableVersion++;\n }\n // Drop from the material group registry so a later full rebuild (e.g. device-lost\n // recovery) doesn't try to re-materialize a disposed mesh.\n const build = mesh.material?._buildGroup;\n const group = build ? scene._groups.get(build) : undefined;\n if (group) {\n const gi = group.indexOf(mesh);\n if (gi >= 0) {\n group.splice(gi, 1);\n }\n }\n // Drop any pending swap-queue entry (mesh added then removed before the drain).\n const qi = scene._materialSwapQueue.indexOf(mesh);\n if (qi >= 0) {\n scene._materialSwapQueue.splice(qi, 1);\n }\n // Deregister from the world-matrix push registry so a long-lived parent stops\n // retaining/traversing this disposed child on every invalidation. (The parent→\n // child reference is new with the push model; reparent already deregisters, but\n // removal does not go through the parent setter otherwise.)\n mesh.parent = null;\n // Frame-graph eviction: the scene always has a frame graph (created in\n // createSceneContext). Walk its render-pass tasks and drop any binding whose\n // source mesh matches. RenderTasks are identified by carrying `_renderables`\n // (a `_config` field alone is NOT sufficient — post/effect tasks also have one).\n for (const task of scene._frameGraph._tasks) {\n if (\"_renderables\" in (task as object)) {\n removeMeshFromTask(task as RenderTask, mesh);\n }\n }\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene — a single\n // `Mesh` may be added to several scenes, and `disposeMeshGpu` destroys buffers they all share.\n if (unregisterMeshScene(scene, mesh)) {\n disposeMeshGpu(mesh);\n }\n}\n"],"names":[],"mappings":";;;;AASO,SAAS,eAAA,CAAgB,OAAqB,IAAA,EAAkB;AACnE,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,gBAAA,CAAiB,GAAA,CAAI,IAAI,CAAA;AAI3C,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,GAAA,EAAK;AACL,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AAClB,MAAA,EAAA,EAAG;AAAA,IACP;AACA,IAAA,KAAA,CAAM,gBAAA,CAAiB,OAAO,IAAI,CAAA;AAAA,EACtC;AACA,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AACrC,EAAA,IAAI,OAAO,CAAA,EAAG;AACV,IAAA,KAAA,CAAM,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK,CAAC,CAAA;AAC1B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AACA,EAAA,MAAM,CAAA,GAAI,MAAM,YAAA,CAAa,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,IAAI,CAAA;AAC7D,EAAA,IAAI,KAAK,CAAA,EAAG;AACR,IAAA,KAAA,CAAM,YAAA,CAAa,MAAA,CAAO,CAAA,EAAG,CAAC,CAAA;AAC9B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AASA,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,KAAA,CAAM,kBAAA,EAAA;AAAA,EACV;AAGA,EAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,EAAU,WAAA;AAC7B,EAAA,MAAM,QAAQ,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AACjD,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAC7B,IAAA,IAAI,MAAM,CAAA,EAAG;AACT,MAAA,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IACtB;AAAA,EACJ;AAEA,EAAA,MAAM,EAAA,GAAK,KAAA,CAAM,kBAAA,CAAmB,OAAA,CAAQ,IAAI,CAAA;AAChD,EAAA,IAAI,MAAM,CAAA,EAAG;AACT,IAAA,KAAA,CAAM,kBAAA,CAAmB,MAAA,CAAO,EAAA,EAAI,CAAC,CAAA;AAAA,EACzC;AAKA,EAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAKd,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,WAAA,CAAY,MAAA,EAAQ;AACzC,IAAA,IAAI,kBAAmB,IAAA,EAAiB;AACpC,MAAA,kBAAA,CAAmB,MAAoB,IAAI,CAAA;AAAA,IAC/C;AAAA,EACJ;AAGA,EAAA,IAAI,mBAAA,CAAoB,KAAA,EAAO,IAAI,CAAA,EAAG;AAClC,IAAA,cAAA,CAAe,IAAI,CAAA;AAAA,EACvB;AACJ;;;;"}
|
|
1
|
+
{"version":3,"file":"scene-remove.js","sources":["../../../src/scene/scene-remove.ts"],"sourcesContent":["import type { SceneContext } from \"./scene-core.js\";\nimport { unregisterMeshScene } from \"./mesh-scene-registry.js\";\nimport type { Mesh } from \"../mesh/mesh.js\";\nimport { disposeMeshGpu } from \"../mesh/mesh-dispose.js\";\nimport { removeMeshFromTask } from \"../frame-graph/render-task.js\";\nimport type { RenderTask } from \"../frame-graph/render-task.js\";\n\n/** Remove a mesh from the scene and destroy its GPU resources.\n * Standalone function for tree-shaking — only included when actually used. */\nexport function removeFromScene(scene: SceneContext, mesh: Mesh): void {\n const fns = scene._meshDisposables.get(mesh);\n // Whether this call actually mutated scene state — used to gate the renderable\n // version bump so a no-op removal (mesh never registered) doesn't needlessly\n // invalidate the cached opaque bundle.\n let didMutate = false;\n if (fns) {\n didMutate = true;\n for (const fn of fns) {\n fn();\n }\n scene._meshDisposables.delete(mesh);\n }\n // AUX (override) view packets — depth/SSAO no-colour views another task registered on this mesh. A material\n // swap deliberately leaves these alone (see `_meshAuxDisposables`); a real removal must still free them.\n const auxFns = scene._meshAuxDisposables.get(mesh);\n if (auxFns) {\n didMutate = true;\n for (const fn of auxFns) {\n fn();\n }\n scene._meshAuxDisposables.delete(mesh);\n }\n const mi2 = scene.meshes.indexOf(mesh);\n if (mi2 >= 0) {\n scene.meshes.splice(mi2, 1);\n didMutate = true;\n }\n const i = scene._renderables.findIndex((r) => r.mesh === mesh);\n if (i >= 0) {\n scene._renderables.splice(i, 1);\n didMutate = true;\n }\n // Invalidate any auto-mirroring render task so it rebuilds its binding lists +\n // cached opaque bundle without this mesh BEFORE its GPU buffers (vertex data +\n // per-packet system UBO) are touched again. Done whenever the mesh actually\n // belonged to the scene — NOT gated on owning a standalone renderable: meshes\n // sharing a material at the initial scene build are merged into one combined\n // renderable whose `mesh` is undefined, yet their now-destroyed buffers are\n // still referenced by that renderable's update()/draw() and the cached bundle.\n // Mirrors the version bump done on add (material-swap) and initial build.\n if (didMutate) {\n scene._renderableVersion++;\n }\n // Drop from the material group registry so a later full rebuild (e.g. device-lost\n // recovery) doesn't try to re-materialize a disposed mesh.\n const build = mesh.material?._buildGroup;\n const group = build ? scene._groups.get(build) : undefined;\n if (group) {\n const gi = group.indexOf(mesh);\n if (gi >= 0) {\n group.splice(gi, 1);\n }\n }\n // Drop any pending swap-queue entry (mesh added then removed before the drain).\n const qi = scene._materialSwapQueue.indexOf(mesh);\n if (qi >= 0) {\n scene._materialSwapQueue.splice(qi, 1);\n }\n // Deregister from the world-matrix push registry so a long-lived parent stops\n // retaining/traversing this disposed child on every invalidation. (The parent→\n // child reference is new with the push model; reparent already deregisters, but\n // removal does not go through the parent setter otherwise.)\n mesh.parent = null;\n // Frame-graph eviction: the scene always has a frame graph (created in\n // createSceneContext). Walk its render-pass tasks and drop any binding whose\n // source mesh matches. RenderTasks are identified by carrying `_renderables`\n // (a `_config` field alone is NOT sufficient — post/effect tasks also have one).\n for (const task of scene._frameGraph._tasks) {\n if (\"_renderables\" in (task as object)) {\n removeMeshFromTask(task as RenderTask, mesh);\n }\n }\n // Free the mesh's shared GPU buffers only when this was its LAST owning scene — a single\n // `Mesh` may be added to several scenes, and `disposeMeshGpu` destroys buffers they all share.\n if (unregisterMeshScene(scene, mesh)) {\n disposeMeshGpu(mesh);\n }\n}\n"],"names":[],"mappings":";;;;AASO,SAAS,eAAA,CAAgB,OAAqB,IAAA,EAAkB;AACnE,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,gBAAA,CAAiB,GAAA,CAAI,IAAI,CAAA;AAI3C,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,GAAA,EAAK;AACL,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AAClB,MAAA,EAAA,EAAG;AAAA,IACP;AACA,IAAA,KAAA,CAAM,gBAAA,CAAiB,OAAO,IAAI,CAAA;AAAA,EACtC;AAGA,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,mBAAA,CAAoB,GAAA,CAAI,IAAI,CAAA;AACjD,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,KAAA,MAAW,MAAM,MAAA,EAAQ;AACrB,MAAA,EAAA,EAAG;AAAA,IACP;AACA,IAAA,KAAA,CAAM,mBAAA,CAAoB,OAAO,IAAI,CAAA;AAAA,EACzC;AACA,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AACrC,EAAA,IAAI,OAAO,CAAA,EAAG;AACV,IAAA,KAAA,CAAM,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK,CAAC,CAAA;AAC1B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AACA,EAAA,MAAM,CAAA,GAAI,MAAM,YAAA,CAAa,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,IAAI,CAAA;AAC7D,EAAA,IAAI,KAAK,CAAA,EAAG;AACR,IAAA,KAAA,CAAM,YAAA,CAAa,MAAA,CAAO,CAAA,EAAG,CAAC,CAAA;AAC9B,IAAA,SAAA,GAAY,IAAA;AAAA,EAChB;AASA,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,KAAA,CAAM,kBAAA,EAAA;AAAA,EACV;AAGA,EAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,EAAU,WAAA;AAC7B,EAAA,MAAM,QAAQ,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA,GAAI,MAAA;AACjD,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAC7B,IAAA,IAAI,MAAM,CAAA,EAAG;AACT,MAAA,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IACtB;AAAA,EACJ;AAEA,EAAA,MAAM,EAAA,GAAK,KAAA,CAAM,kBAAA,CAAmB,OAAA,CAAQ,IAAI,CAAA;AAChD,EAAA,IAAI,MAAM,CAAA,EAAG;AACT,IAAA,KAAA,CAAM,kBAAA,CAAmB,MAAA,CAAO,EAAA,EAAI,CAAC,CAAA;AAAA,EACzC;AAKA,EAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAKd,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,WAAA,CAAY,MAAA,EAAQ;AACzC,IAAA,IAAI,kBAAmB,IAAA,EAAiB;AACpC,MAAA,kBAAA,CAAmB,MAAoB,IAAI,CAAA;AAAA,IAC/C;AAAA,EACJ;AAGA,EAAA,IAAI,mBAAA,CAAoB,KAAA,EAAO,IAAI,CAAA,EAAG;AAClC,IAAA,cAAA,CAAe,IAAI,CAAA;AAAA,EACvB;AACJ;;;;"}
|
|
@@ -4,6 +4,10 @@ import { mat4Invert } from '../math/mat4-invert.js';
|
|
|
4
4
|
import { createShadowCamera, casterVersionSum, updateShadowCameraBase, buildLightViewMatrix, multiply4x4 } from './shadow-base.js';
|
|
5
5
|
import { preloadPcfShadowTaskState, getNoColorView } from './pcf-shadow-task-hooks.js';
|
|
6
6
|
|
|
7
|
+
function effectiveCasterGen(material) {
|
|
8
|
+
const eff = material._shadowCasterMaterial ?? material;
|
|
9
|
+
return eff._csmGen ?? 0;
|
|
10
|
+
}
|
|
7
11
|
const preloadCsmShadowTaskState = preloadPcfShadowTaskState;
|
|
8
12
|
function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existingState) {
|
|
9
13
|
const existing = existingState;
|
|
@@ -22,7 +26,7 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
22
26
|
continue;
|
|
23
27
|
}
|
|
24
28
|
const stored = existing._casterMatGens.get(mat);
|
|
25
|
-
if (stored !== void 0 && stored !== (mat
|
|
29
|
+
if (stored !== void 0 && stored !== effectiveCasterGen(mat)) {
|
|
26
30
|
casterMatChanged = true;
|
|
27
31
|
break;
|
|
28
32
|
}
|
|
@@ -45,7 +49,7 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
45
49
|
for (const t of existing._tasks) {
|
|
46
50
|
t.addMesh(m, { material: view });
|
|
47
51
|
}
|
|
48
|
-
gens.set(m.material, m.material
|
|
52
|
+
gens.set(m.material, effectiveCasterGen(m.material));
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
for (const t of existing._tasks) {
|
|
@@ -121,7 +125,7 @@ function ensureCsmShadowTaskState(engine, scene, sg, cfg, casterMeshes, existing
|
|
|
121
125
|
const casterMatGens = /* @__PURE__ */ new Map();
|
|
122
126
|
for (const m of casterMeshes) {
|
|
123
127
|
if (m.material) {
|
|
124
|
-
casterMatGens.set(m.material, m.material
|
|
128
|
+
casterMatGens.set(m.material, effectiveCasterGen(m.material));
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
return {
|
|
@@ -325,9 +329,13 @@ function _computeCsmCascades(engine, camera, light, cfg, casterMeshes) {
|
|
|
325
329
|
let aClipY = transform[13];
|
|
326
330
|
if (cfg._stabilizeCascades && stableRadius > 0) {
|
|
327
331
|
const texelWorld = 2 * stableRadius / cfg._mapSize;
|
|
328
|
-
const
|
|
329
|
-
const
|
|
330
|
-
const
|
|
332
|
+
const rX = view[0], rY = view[4], rZ = view[8];
|
|
333
|
+
const uX = view[1], uY = view[5], uZ = view[9];
|
|
334
|
+
const sr = Math.round((rX * cx + rY * cy + rZ * cz) / texelWorld) * texelWorld;
|
|
335
|
+
const tr = Math.round((uX * cx + uY * cy + uZ * cz) / texelWorld) * texelWorld;
|
|
336
|
+
const ax = sr * rX + tr * uX;
|
|
337
|
+
const ay = sr * rY + tr * uY;
|
|
338
|
+
const az = sr * rZ + tr * uZ;
|
|
331
339
|
aClipX = transform[0] * ax + transform[4] * ay + transform[8] * az + transform[12];
|
|
332
340
|
aClipY = transform[1] * ax + transform[5] * ay + transform[9] * az + transform[13];
|
|
333
341
|
}
|