@driftengine/core 3.63.0 → 4.0.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.json +111 -0
- package/NOTICE +20 -0
- package/README.md +1 -1
- package/dist/core/loop.js +18 -2
- package/dist/core/storage.js +5 -0
- package/dist/geometry/meshBuilder.d.ts +1 -1
- package/dist/geometry/meshBuilder.js +6 -2
- package/dist/index.d.ts +69 -4
- package/dist/index.js +112 -2
- package/dist/input/input.js +6 -1
- package/dist/math/exact.d.ts +13 -3
- package/dist/math/exact.js +68 -3
- package/dist/math/frustum.d.ts +17 -2
- package/dist/math/frustum.js +24 -0
- package/dist/render/backend/api.d.ts +34 -1
- package/dist/render/backend/api.js +22 -1
- package/dist/render/backend/budget.d.ts +7 -6
- package/dist/render/backend/budget.js +7 -6
- package/dist/render/backend/createRenderer.d.ts +57 -0
- package/dist/render/backend/createRenderer.js +51 -2
- package/dist/render/backend/materialChanges.d.ts +41 -0
- package/dist/render/backend/materialChanges.js +38 -0
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/renderer.d.ts +80 -1
- package/dist/render/backend/webgl2/renderer.js +208 -10
- package/dist/render/backend/webgpu/buffers.d.ts +20 -1
- package/dist/render/backend/webgpu/buffers.js +116 -58
- package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
- package/dist/render/backend/webgpu/fieldCompose.js +396 -0
- package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
- package/dist/render/backend/webgpu/giFieldPass.js +137 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
- package/dist/render/backend/webgpu/linePass.d.ts +4 -14
- package/dist/render/backend/webgpu/linePass.js +4 -33
- package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
- package/dist/render/backend/webgpu/probeBake.js +493 -0
- package/dist/render/backend/webgpu/renderer.d.ts +379 -24
- package/dist/render/backend/webgpu/renderer.js +1369 -76
- package/dist/render/backend/webgpu/scatterPass.js +15 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +4 -1
- package/dist/render/depthConvention.d.ts +12 -0
- package/dist/render/depthConvention.js +14 -0
- package/dist/render/deviceFeatures.d.ts +32 -0
- package/dist/render/deviceFeatures.js +41 -0
- package/dist/render/fog.d.ts +79 -0
- package/dist/render/fog.js +138 -0
- package/dist/render/frame/alias.d.ts +30 -0
- package/dist/render/frame/alias.js +61 -0
- package/dist/render/frame/deps.d.ts +37 -0
- package/dist/render/frame/deps.js +122 -0
- package/dist/render/frame/flushGraph.d.ts +43 -0
- package/dist/render/frame/flushGraph.js +84 -0
- package/dist/render/frame/graphSchedule.d.ts +49 -0
- package/dist/render/frame/graphSchedule.js +120 -0
- package/dist/render/frame/index.d.ts +15 -0
- package/dist/render/frame/index.js +8 -0
- package/dist/render/frame/lifetime.d.ts +24 -0
- package/dist/render/frame/lifetime.js +36 -0
- package/dist/render/frame/passTimings.d.ts +42 -0
- package/dist/render/frame/passTimings.js +38 -0
- package/dist/render/frame/validate.d.ts +15 -0
- package/dist/render/frame/validate.js +22 -0
- package/dist/render/frame/virtual.d.ts +44 -0
- package/dist/render/frame/virtual.js +61 -0
- package/dist/render/gi/chain.d.ts +82 -0
- package/dist/render/gi/chain.js +135 -0
- package/dist/render/gi/denoise.d.ts +114 -0
- package/dist/render/gi/denoise.js +169 -0
- package/dist/render/gi/fieldScene.d.ts +125 -0
- package/dist/render/gi/fieldScene.js +219 -0
- package/dist/render/gi/globalField.d.ts +157 -0
- package/dist/render/gi/globalField.js +373 -0
- package/dist/render/gi/probeGridFit.d.ts +24 -0
- package/dist/render/gi/probeGridFit.js +71 -0
- package/dist/render/gi/probeTrace.d.ts +82 -0
- package/dist/render/gi/probeTrace.js +128 -0
- package/dist/render/gi/probeVolume.d.ts +136 -0
- package/dist/render/gi/probeVolume.js +191 -0
- package/dist/render/gi/reflection.d.ts +73 -0
- package/dist/render/gi/reflection.js +109 -0
- package/dist/render/gi/traceField.d.ts +91 -0
- package/dist/render/gi/traceField.js +129 -0
- package/dist/render/gi/traceScreen.d.ts +68 -0
- package/dist/render/gi/traceScreen.js +82 -0
- package/dist/render/gpuScene.d.ts +38 -0
- package/dist/render/gpuScene.js +41 -0
- package/dist/render/gpudriven/alphaTest.d.ts +28 -0
- package/dist/render/gpudriven/alphaTest.js +32 -0
- package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
- package/dist/render/gpudriven/clusterUpload.js +75 -0
- package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
- package/dist/render/gpudriven/clusterWorld.js +155 -0
- package/dist/render/gpudriven/compact.d.ts +51 -0
- package/dist/render/gpudriven/compact.js +68 -0
- package/dist/render/gpudriven/cullClusters.d.ts +129 -0
- package/dist/render/gpudriven/cullClusters.js +237 -0
- package/dist/render/gpudriven/decodeTables.d.ts +64 -0
- package/dist/render/gpudriven/decodeTables.js +219 -0
- package/dist/render/gpudriven/frustum.d.ts +25 -0
- package/dist/render/gpudriven/frustum.js +59 -0
- package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
- package/dist/render/gpudriven/geometryHarness.js +31 -0
- package/dist/render/gpudriven/hzb.d.ts +60 -0
- package/dist/render/gpudriven/hzb.js +97 -0
- package/dist/render/gpudriven/ibl.d.ts +85 -0
- package/dist/render/gpudriven/ibl.js +129 -0
- package/dist/render/gpudriven/indirect.d.ts +67 -0
- package/dist/render/gpudriven/indirect.js +77 -0
- package/dist/render/gpudriven/instances.d.ts +43 -0
- package/dist/render/gpudriven/instances.js +172 -0
- package/dist/render/gpudriven/lit.d.ts +165 -0
- package/dist/render/gpudriven/lit.js +165 -0
- package/dist/render/gpudriven/lodCut.d.ts +28 -0
- package/dist/render/gpudriven/lodCut.js +38 -0
- package/dist/render/gpudriven/materialBin.d.ts +60 -0
- package/dist/render/gpudriven/materialBin.js +108 -0
- package/dist/render/gpudriven/materialTable.d.ts +88 -0
- package/dist/render/gpudriven/materialTable.js +115 -0
- package/dist/render/gpudriven/pipeline.d.ts +135 -0
- package/dist/render/gpudriven/pipeline.js +237 -0
- package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
- package/dist/render/gpudriven/rangeAlloc.js +112 -0
- package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
- package/dist/render/gpudriven/sceneUpload.js +115 -0
- package/dist/render/gpudriven/shadeBins.d.ts +92 -0
- package/dist/render/gpudriven/shadeBins.js +182 -0
- package/dist/render/gpudriven/shadow.d.ts +136 -0
- package/dist/render/gpudriven/shadow.js +224 -0
- package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
- package/dist/render/gpudriven/shadowCamera.js +345 -0
- package/dist/render/gpudriven/streamScene.d.ts +194 -0
- package/dist/render/gpudriven/streamScene.js +459 -0
- package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
- package/dist/render/gpudriven/surfaceFrame.js +51 -0
- package/dist/render/gpudriven/twoPhase.d.ts +122 -0
- package/dist/render/gpudriven/twoPhase.js +160 -0
- package/dist/render/gpudriven/visbuffer.d.ts +25 -0
- package/dist/render/gpudriven/visbuffer.js +37 -0
- package/dist/render/halfFloat.d.ts +10 -0
- package/dist/render/halfFloat.js +23 -4
- package/dist/render/inference/attentionTiled.d.ts +27 -0
- package/dist/render/inference/attentionTiled.js +117 -0
- package/dist/render/inference/denseKernels.d.ts +29 -0
- package/dist/render/inference/denseKernels.js +312 -0
- package/dist/render/inference/device.d.ts +21 -0
- package/dist/render/inference/device.js +32 -0
- package/dist/render/inference/deviceGraph.d.ts +34 -0
- package/dist/render/inference/deviceGraph.js +17 -0
- package/dist/render/inference/fuse.d.ts +15 -0
- package/dist/render/inference/fuse.js +37 -0
- package/dist/render/inference/kernelKit.d.ts +52 -0
- package/dist/render/inference/kernelKit.js +70 -0
- package/dist/render/inference/kernels.d.ts +3 -0
- package/dist/render/inference/kernels.js +11 -0
- package/dist/render/inference/plan.d.ts +13 -0
- package/dist/render/inference/plan.js +18 -0
- package/dist/render/inference/reuse.d.ts +39 -0
- package/dist/render/inference/reuse.js +65 -0
- package/dist/render/inference/runner.d.ts +31 -0
- package/dist/render/inference/runner.js +175 -0
- package/dist/render/inference/schedule.d.ts +39 -0
- package/dist/render/inference/schedule.js +59 -0
- package/dist/render/inference/shapeKernels.d.ts +10 -0
- package/dist/render/inference/shapeKernels.js +80 -0
- package/dist/render/inference/spatialKernels.d.ts +21 -0
- package/dist/render/inference/spatialKernels.js +179 -0
- package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
- package/dist/render/inference/splatRaster.wgsl.js +337 -0
- package/dist/render/inference/splatTiles.d.ts +79 -0
- package/dist/render/inference/splatTiles.js +161 -0
- package/dist/render/lineBatch.d.ts +2 -5
- package/dist/render/lineBatch.js +4 -24
- package/dist/render/mesh.d.ts +4 -0
- package/dist/render/mesh.js +45 -1
- package/dist/render/pass.d.ts +145 -8
- package/dist/render/prefilterEnvMap.d.ts +22 -0
- package/dist/render/prefilterEnvMap.js +31 -0
- package/dist/render/readback.d.ts +30 -0
- package/dist/render/readback.js +47 -0
- package/dist/render/recon/clamp.d.ts +36 -0
- package/dist/render/recon/clamp.js +92 -0
- package/dist/render/recon/disocclusion.d.ts +60 -0
- package/dist/render/recon/disocclusion.js +109 -0
- package/dist/render/recon/frameGen.d.ts +81 -0
- package/dist/render/recon/frameGen.js +150 -0
- package/dist/render/recon/frameSizes.d.ts +34 -0
- package/dist/render/recon/frameSizes.js +58 -0
- package/dist/render/recon/jitter.d.ts +60 -0
- package/dist/render/recon/jitter.js +118 -0
- package/dist/render/recon/motionVectors.d.ts +55 -0
- package/dist/render/recon/motionVectors.js +107 -0
- package/dist/render/recon/reproject.d.ts +41 -0
- package/dist/render/recon/reproject.js +131 -0
- package/dist/render/recon/resolve.d.ts +90 -0
- package/dist/render/recon/resolve.js +313 -0
- package/dist/render/recon/tier.d.ts +24 -0
- package/dist/render/recon/tier.js +8 -0
- package/dist/render/reflectionProbe.d.ts +26 -0
- package/dist/render/reflectionProbe.js +33 -0
- package/dist/render/renderQuality.d.ts +69 -0
- package/dist/render/renderQuality.js +22 -0
- package/dist/render/screenSpaceReflection.d.ts +23 -0
- package/dist/render/screenSpaceReflection.js +22 -3
- package/dist/render/sdfFont.js +2 -0
- package/dist/render/segmentQuads.d.ts +13 -0
- package/dist/render/segmentQuads.js +32 -0
- package/dist/render/shaders/flat/lobes.d.ts +27 -0
- package/dist/render/shaders/flat/lobes.js +33 -3
- package/dist/render/shaders/flat/preamble.d.ts +7 -0
- package/dist/render/shaders/flat/preamble.js +24 -1
- package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
- package/dist/render/shaders/flat/probeGrid.js +71 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
- package/dist/render/shaders/generated/flat.wgsl.js +64 -24
- package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
- package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
- package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
- package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
- package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
- package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
- package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
- package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
- package/dist/render/shaders/network.wgsl.d.ts +101 -0
- package/dist/render/shaders/network.wgsl.js +206 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
- package/dist/render/shaders/recon/motion.wgsl.js +96 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
- package/dist/render/temporalAa.js +8 -33
- package/dist/render/textLayout.js +12 -6
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +4 -1
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +11 -0
- package/dist/render/windStreakRenderer.d.ts +2 -1
- package/dist/render/windStreakRenderer.js +3 -1
- package/dist/world/cell.d.ts +63 -0
- package/dist/world/cell.js +181 -0
- package/dist/world/cellStream.d.ts +103 -0
- package/dist/world/cellStream.js +198 -0
- package/dist/world/freeze.d.ts +81 -0
- package/dist/world/freeze.js +114 -0
- package/dist/world/rebase.d.ts +29 -0
- package/dist/world/rebase.js +41 -0
- package/package.json +8 -3
- package/scripts/frames.mjs +92 -0
- package/scripts/gpuCompute.mjs +323 -0
- package/scripts/png.d.mts +16 -0
- package/scripts/png.mjs +25 -0
- package/src/core/loop.ts +19 -2
- package/src/core/storage.ts +5 -0
- package/src/geometry/meshBuilder.ts +5 -1
- package/src/index.ts +246 -4
- package/src/input/input.ts +6 -1
- package/src/math/exact.ts +69 -3
- package/src/math/frustum.ts +52 -3
- package/src/render/backend/api.ts +42 -1
- package/src/render/backend/budget.ts +7 -6
- package/src/render/backend/createRenderer.ts +92 -3
- package/src/render/backend/materialChanges.ts +54 -0
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/renderer.ts +211 -9
- package/src/render/backend/webgpu/buffers.ts +182 -69
- package/src/render/backend/webgpu/fieldCompose.ts +482 -0
- package/src/render/backend/webgpu/giFieldPass.ts +200 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
- package/src/render/backend/webgpu/linePass.ts +4 -39
- package/src/render/backend/webgpu/probeBake.ts +588 -0
- package/src/render/backend/webgpu/renderer.ts +1481 -76
- package/src/render/backend/webgpu/scatterPass.ts +15 -1
- package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
- package/src/render/causticsRenderer.ts +5 -2
- package/src/render/depthConvention.ts +15 -0
- package/src/render/deviceFeatures.ts +77 -0
- package/src/render/fog.ts +185 -0
- package/src/render/frame/alias.ts +97 -0
- package/src/render/frame/deps.ts +155 -0
- package/src/render/frame/flushGraph.ts +135 -0
- package/src/render/frame/graphSchedule.ts +170 -0
- package/src/render/frame/index.ts +52 -0
- package/src/render/frame/lifetime.ts +61 -0
- package/src/render/frame/passTimings.ts +69 -0
- package/src/render/frame/validate.ts +40 -0
- package/src/render/frame/virtual.ts +91 -0
- package/src/render/gi/chain.ts +225 -0
- package/src/render/gi/denoise.ts +276 -0
- package/src/render/gi/fieldScene.ts +264 -0
- package/src/render/gi/globalField.ts +543 -0
- package/src/render/gi/probeGridFit.ts +89 -0
- package/src/render/gi/probeTrace.ts +143 -0
- package/src/render/gi/probeVolume.ts +288 -0
- package/src/render/gi/reflection.ts +163 -0
- package/src/render/gi/traceField.ts +223 -0
- package/src/render/gi/traceScreen.ts +106 -0
- package/src/render/gpuScene.ts +80 -0
- package/src/render/gpudriven/alphaTest.ts +33 -0
- package/src/render/gpudriven/clusterUpload.ts +123 -0
- package/src/render/gpudriven/clusterWorld.ts +239 -0
- package/src/render/gpudriven/compact.ts +80 -0
- package/src/render/gpudriven/cullClusters.ts +333 -0
- package/src/render/gpudriven/decodeTables.ts +332 -0
- package/src/render/gpudriven/frustum.ts +70 -0
- package/src/render/gpudriven/geometryHarness.ts +51 -0
- package/src/render/gpudriven/hzb.ts +113 -0
- package/src/render/gpudriven/ibl.ts +147 -0
- package/src/render/gpudriven/indirect.ts +89 -0
- package/src/render/gpudriven/instances.ts +234 -0
- package/src/render/gpudriven/lit.ts +329 -0
- package/src/render/gpudriven/lodCut.ts +62 -0
- package/src/render/gpudriven/materialBin.ts +131 -0
- package/src/render/gpudriven/materialTable.ts +164 -0
- package/src/render/gpudriven/pipeline.ts +336 -0
- package/src/render/gpudriven/rangeAlloc.ts +122 -0
- package/src/render/gpudriven/sceneUpload.ts +173 -0
- package/src/render/gpudriven/shadeBins.ts +222 -0
- package/src/render/gpudriven/shadow.ts +285 -0
- package/src/render/gpudriven/shadowCamera.ts +406 -0
- package/src/render/gpudriven/streamScene.ts +575 -0
- package/src/render/gpudriven/surfaceFrame.ts +68 -0
- package/src/render/gpudriven/twoPhase.ts +214 -0
- package/src/render/gpudriven/visbuffer.ts +41 -0
- package/src/render/halfFloat.ts +22 -4
- package/src/render/inference/attentionTiled.ts +132 -0
- package/src/render/inference/denseKernels.ts +369 -0
- package/src/render/inference/device.ts +44 -0
- package/src/render/inference/deviceGraph.ts +39 -0
- package/src/render/inference/fuse.ts +47 -0
- package/src/render/inference/kernelKit.ts +120 -0
- package/src/render/inference/kernels.ts +18 -0
- package/src/render/inference/plan.ts +26 -0
- package/src/render/inference/reuse.ts +79 -0
- package/src/render/inference/runner.ts +204 -0
- package/src/render/inference/schedule.ts +80 -0
- package/src/render/inference/shapeKernels.ts +112 -0
- package/src/render/inference/spatialKernels.ts +220 -0
- package/src/render/inference/splatRaster.wgsl.ts +348 -0
- package/src/render/inference/splatTiles.ts +186 -0
- package/src/render/lineBatch.ts +4 -24
- package/src/render/mesh.ts +46 -1
- package/src/render/pass.ts +155 -10
- package/src/render/prefilterEnvMap.ts +33 -0
- package/src/render/readback.ts +73 -0
- package/src/render/recon/clamp.ts +113 -0
- package/src/render/recon/disocclusion.ts +143 -0
- package/src/render/recon/frameGen.ts +191 -0
- package/src/render/recon/frameSizes.ts +64 -0
- package/src/render/recon/jitter.ts +126 -0
- package/src/render/recon/motionVectors.ts +138 -0
- package/src/render/recon/reproject.ts +179 -0
- package/src/render/recon/resolve.ts +444 -0
- package/src/render/recon/tier.ts +31 -0
- package/src/render/reflectionProbe.ts +34 -0
- package/src/render/renderQuality.ts +87 -0
- package/src/render/screenSpaceReflection.ts +37 -3
- package/src/render/sdfFont.ts +2 -0
- package/src/render/segmentQuads.ts +39 -0
- package/src/render/shaders/flat/lobes.ts +35 -3
- package/src/render/shaders/flat/preamble.ts +25 -1
- package/src/render/shaders/flat/probeGrid.ts +77 -2
- package/src/render/shaders/generated/flat.wgsl.ts +64 -24
- package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
- package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
- package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
- package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
- package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
- package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
- package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
- package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
- package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
- package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
- package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
- package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
- package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
- package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
- package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
- package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
- package/src/render/shaders/network.wgsl.ts +238 -0
- package/src/render/shaders/recon/motion.wgsl.ts +101 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
- package/src/render/temporalAa.ts +9 -33
- package/src/render/textLayout.ts +12 -6
- package/src/render/textRenderer.ts +5 -2
- package/src/render/translucentQueue.ts +11 -0
- package/src/render/windStreakRenderer.ts +4 -2
- package/src/world/cell.ts +231 -0
- package/src/world/cellStream.ts +278 -0
- package/src/world/freeze.ts +157 -0
- package/src/world/rebase.ts +56 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Where the second pipeline's light stands, and the two corrections its map needs. */
|
|
2
|
+
/**
|
|
3
|
+
* A bounding sphere over every cluster, from the world bounds the cull buffer already holds.
|
|
4
|
+
*
|
|
5
|
+
* **It reads the transformed bounds rather than transforming them itself**, which it did until
|
|
6
|
+
* 2026-09-17 and which was one of two places in this pipeline that multiplied a cluster by its
|
|
7
|
+
* mesh's matrix. `clusterWorld.ts` is the other and is now the only one: two copies of that
|
|
8
|
+
* arithmetic can disagree about the scale a sphere takes, and a light's box that disagrees with the
|
|
9
|
+
* cull's bounds clips casters out of a map that the cull believes are in it — which is a surface
|
|
10
|
+
* lit with something standing in front of it.
|
|
11
|
+
*
|
|
12
|
+
* `stride` is how many floats a cluster occupies; `CLUSTER_CULL_FLOATS` for the cull buffer, where
|
|
13
|
+
* the centre and radius are the first four of eight.
|
|
14
|
+
*
|
|
15
|
+
* `out` is the centre and the radius, four floats.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sceneShadowBounds(
|
|
18
|
+
/** Centre and radius, four floats at every `stride`, in world space. */
|
|
19
|
+
bounds: Float32Array, stride: number, count: number, out: Float32Array): Float32Array;
|
|
20
|
+
export declare function fitDirectionalShadow(centre: ArrayLike<number>, radius: number, lightDir: readonly [number, number, number], out: Float32Array): number;
|
|
21
|
+
/**
|
|
22
|
+
* **The map for a frame: the whole scene while it fits inside `follow` metres, and a sphere of that
|
|
23
|
+
* radius around the eye once it does not.**
|
|
24
|
+
*
|
|
25
|
+
* The scene fit is right for what this pipeline was first given — rigs a few metres across, a box
|
|
26
|
+
* that never moves and so never shimmers — and wrong for a world, which is what the voxel
|
|
27
|
+
* sandbox's port made this pipeline draw: at radius 14 its sphere is hundreds of metres across and
|
|
28
|
+
* a 2048 map over it has texels a quarter of a metre wide. So past a radius the caller names, the
|
|
29
|
+
* map follows the eye, as the forward path's does in `lightMatrix.ts`. **At or under it nothing
|
|
30
|
+
* changes, to the bit**, and with no radius at all nothing ever does — which is what keeps the three
|
|
31
|
+
* rigs the control rather than a second fit's first customer.
|
|
32
|
+
*
|
|
33
|
+
* **Why the scene's size rather than whether the eye is inside it**: a fit that switched as the eye
|
|
34
|
+
* crossed a boundary would jump the whole map between two scales mid-walk, where a scene grows past
|
|
35
|
+
* the radius once and stays there.
|
|
36
|
+
*
|
|
37
|
+
* `sphere` is the scene's centre and radius, four floats, as `sceneShadowBounds` writes them.
|
|
38
|
+
* Returns the metres the depth range spans, which the lookup multiplies a stored difference by.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fitShadow(sphere: ArrayLike<number>, eye: ArrayLike<number>, follow: number | undefined, lightDir: readonly [number, number, number], mapSize: number, out: Float32Array): number;
|
|
41
|
+
/**
|
|
42
|
+
* **Two corrections, and the difference is one negated row.**
|
|
43
|
+
*
|
|
44
|
+
* The lookup wants the light-space position with `x` and `y` still in -1..1 — it remaps those
|
|
45
|
+
* itself — and `z` already in the 0..1 WebGPU clips to. That is `SHADOW_LOOKUP_CORRECTION`, and it
|
|
46
|
+
* is `SHADOW_CLIP_CORRECTION` in `webgpu/renderer.ts` under another name, for the same reason: a
|
|
47
|
+
* map is sampled rather than presented.
|
|
48
|
+
*
|
|
49
|
+
* The raster wants the same depth and the **opposite rows**, and this is the part that is easy to
|
|
50
|
+
* get backwards. A generated vertex stage ends with `gl_Position.y = -gl_Position.y`, which naga
|
|
51
|
+
* writes and nothing here does — so the forward path's shadow pass is handed the unflipped matrix
|
|
52
|
+
* and emits a flipped picture, while a hand-written stage handed the same matrix emits an unflipped
|
|
53
|
+
* one and stores every row at the mirror of where the lookup goes looking. Flipping it here is what
|
|
54
|
+
* puts the two pipelines' maps in the same orientation, so one lookup can read either.
|
|
55
|
+
*
|
|
56
|
+
* **And the flip reverses the winding with it**, which is why the pipeline that uses this declares
|
|
57
|
+
* `frontFace: 'cw'` — the same pair `depthPass.ts` carries, arrived at from the other side.
|
|
58
|
+
*
|
|
59
|
+
* Neither reverses depth. `depthConvention.ts` leaves shadow maps conventional, so the row is
|
|
60
|
+
* `0.5z + 0.5` where the frame's is `0.5 - 0.5z`, and the pass that renders one clears to
|
|
61
|
+
* `SHADOW_DEPTH_CLEAR` and compares with `less`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SHADOW_LOOKUP_CORRECTION: Float32Array<ArrayBuffer>;
|
|
64
|
+
export declare const SHADOW_RASTER_CORRECTION: Float32Array<ArrayBuffer>;
|
|
65
|
+
/**
|
|
66
|
+
* How far toward the light the light's cone test stands its eye, in metres.
|
|
67
|
+
*
|
|
68
|
+
* **A directional light has no eye, and the cone test wants one.** Stood this far toward the sun,
|
|
69
|
+
* every direction from the eye to a cluster in the map is the sun's own to within the cluster's
|
|
70
|
+
* distance from the camera over this — three hundred-thousandths of a radian across a city three
|
|
71
|
+
* kilometres wide — so the perspective test answers the directional one the map's raster applies
|
|
72
|
+
* when it culls the faces turned from the light. A triangle it could cull wrongly is that close to
|
|
73
|
+
* edge-on to the sun, and covers a thousandth of a texel of any map. In `float32` the eye is good
|
|
74
|
+
* to eight metres of the hundred million, which moves the direction by less again.
|
|
75
|
+
*/
|
|
76
|
+
export declare const LIGHT_CONE_DISTANCE = 100000000;
|
|
77
|
+
/** The eye the light's cone test reads: `eye` moved `LIGHT_CONE_DISTANCE` toward the light. */
|
|
78
|
+
export declare function lightConeEye(eye: ArrayLike<number>, lightDir: ArrayLike<number>, out: [number, number, number]): [number, number, number];
|
|
79
|
+
/**
|
|
80
|
+
* The raster's matrix with its depth turned over, `1 - z`, and nothing else moved: what the light's
|
|
81
|
+
* cull projects a cluster through, because the light's pyramid is stored turned over
|
|
82
|
+
* (`shadowPyramidBase`) so the camera's cull and reduction can read it. Applied after
|
|
83
|
+
* `SHADOW_RASTER_CORRECTION`, so the rows are the raster's, which are the map's and the pyramid's.
|
|
84
|
+
*/
|
|
85
|
+
export declare const SHADOW_CULL_CORRECTION: Float32Array<ArrayBuffer>;
|
|
86
|
+
/** `out = correction · light`, column-major. */
|
|
87
|
+
export declare function correctShadowMatrix(correction: Float32Array, light: Float32Array, out: Float32Array): Float32Array;
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/** Where the second pipeline's light stands, and the two corrections its map needs. */
|
|
2
|
+
/**
|
|
3
|
+
* **The light's projection, fitted to the scene's bounding sphere rather than the camera.**
|
|
4
|
+
*
|
|
5
|
+
* The forward path fits a cascade to the *view* frustum, which is what a large world wants: the map
|
|
6
|
+
* follows the camera and spends its texels where the viewer is. It also costs that path a snapping
|
|
7
|
+
* rule, because a box that moves continuously makes every shadow edge crawl as the camera turns.
|
|
8
|
+
*
|
|
9
|
+
* This one fitted the whole scene, and still does while the scene is no larger than the radius a
|
|
10
|
+
* caller names: its rigs are a few metres across, so a sphere around the lot is a box that never
|
|
11
|
+
* moves and therefore never shimmers. **What that gave up was stated rather than discovered** — a
|
|
12
|
+
* world too big for one map gets a soft, coarse shadow everywhere — and a world arrived: the voxel
|
|
13
|
+
* sandbox's port. So past that radius `fitShadow` follows the eye instead, snapped as the forward
|
|
14
|
+
* path's cascade is. One sphere rather than a cascade of them: `docs/CAPABILITIES.md` records what
|
|
15
|
+
* that costs at a distance.
|
|
16
|
+
*
|
|
17
|
+
* **A sphere rather than an eight-corner box**, because a box's extent in light space depends on
|
|
18
|
+
* the sun's direction and a sphere's does not — so the fit is the same size at every hour, and a
|
|
19
|
+
* shadow that grows softer as the sun moves is the sun moving rather than the fit breathing.
|
|
20
|
+
*/
|
|
21
|
+
import { SHADOW_DEPTH_FADE } from './shadow.js';
|
|
22
|
+
/** Where the basis stops taking `y` as its reference. Any sun steeper than this is overhead. */
|
|
23
|
+
const OVERHEAD = 0.99;
|
|
24
|
+
/**
|
|
25
|
+
* A bounding sphere over every cluster, from the world bounds the cull buffer already holds.
|
|
26
|
+
*
|
|
27
|
+
* **It reads the transformed bounds rather than transforming them itself**, which it did until
|
|
28
|
+
* 2026-09-17 and which was one of two places in this pipeline that multiplied a cluster by its
|
|
29
|
+
* mesh's matrix. `clusterWorld.ts` is the other and is now the only one: two copies of that
|
|
30
|
+
* arithmetic can disagree about the scale a sphere takes, and a light's box that disagrees with the
|
|
31
|
+
* cull's bounds clips casters out of a map that the cull believes are in it — which is a surface
|
|
32
|
+
* lit with something standing in front of it.
|
|
33
|
+
*
|
|
34
|
+
* `stride` is how many floats a cluster occupies; `CLUSTER_CULL_FLOATS` for the cull buffer, where
|
|
35
|
+
* the centre and radius are the first four of eight.
|
|
36
|
+
*
|
|
37
|
+
* `out` is the centre and the radius, four floats.
|
|
38
|
+
*/
|
|
39
|
+
export function sceneShadowBounds(
|
|
40
|
+
/** Centre and radius, four floats at every `stride`, in world space. */
|
|
41
|
+
bounds, stride, count, out) {
|
|
42
|
+
let lowX = Infinity;
|
|
43
|
+
let lowY = Infinity;
|
|
44
|
+
let lowZ = Infinity;
|
|
45
|
+
let highX = -Infinity;
|
|
46
|
+
let highY = -Infinity;
|
|
47
|
+
let highZ = -Infinity;
|
|
48
|
+
for (let c = 0; c < count; c += 1) {
|
|
49
|
+
const at = c * stride;
|
|
50
|
+
const cx = bounds[at];
|
|
51
|
+
const cy = bounds[at + 1];
|
|
52
|
+
const cz = bounds[at + 2];
|
|
53
|
+
const radius = bounds[at + 3];
|
|
54
|
+
/*
|
|
55
|
+
* **A record of no radius is a slot nobody holds, not a cluster at the origin.** A streaming
|
|
56
|
+
* scene's buffers are sized by capacity, so the records past what is live are zeros — and
|
|
57
|
+
* counting them stretches this box all the way back to the origin over empty space, which puts
|
|
58
|
+
* the light's whole depth range across a volume that is mostly nothing. A static scene has no
|
|
59
|
+
* such records and is unaffected: a baked cluster always has a positive radius.
|
|
60
|
+
*/
|
|
61
|
+
if (!(radius > 0))
|
|
62
|
+
continue;
|
|
63
|
+
lowX = Math.min(lowX, cx - radius);
|
|
64
|
+
lowY = Math.min(lowY, cy - radius);
|
|
65
|
+
lowZ = Math.min(lowZ, cz - radius);
|
|
66
|
+
highX = Math.max(highX, cx + radius);
|
|
67
|
+
highY = Math.max(highY, cy + radius);
|
|
68
|
+
highZ = Math.max(highZ, cz + radius);
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isFinite(lowX)) {
|
|
71
|
+
/*
|
|
72
|
+
* **A scene with no clusters is a unit sphere, not a point.** A radius of zero divides by zero
|
|
73
|
+
* in the fit below and every entry of the matrix comes back NaN — which reaches the device as
|
|
74
|
+
* a draw that covers nothing and a lookup that is never inside the map, so the frame is simply
|
|
75
|
+
* unshadowed and nothing says why.
|
|
76
|
+
*/
|
|
77
|
+
out[0] = 0;
|
|
78
|
+
out[1] = 0;
|
|
79
|
+
out[2] = 0;
|
|
80
|
+
out[3] = 1;
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
out[0] = (lowX + highX) * 0.5;
|
|
84
|
+
out[1] = (lowY + highY) * 0.5;
|
|
85
|
+
out[2] = (lowZ + highZ) * 0.5;
|
|
86
|
+
out[3] = Math.max(Math.hypot(highX - lowX, highY - lowY, highZ - lowZ) * 0.5,
|
|
87
|
+
/* Never zero, for the reason above: one flat quad is a legal scene. */
|
|
88
|
+
1e-3);
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The light's view-projection, in the OpenGL convention the rest of this engine builds matrices in:
|
|
93
|
+
* `x` and `y` in -1..1 and `z` in -1..1 as well. Returns the metres its depth range spans.
|
|
94
|
+
*
|
|
95
|
+
* **Written out rather than composed from a look-at and an orthographic**, because the product is
|
|
96
|
+
* four lines and the two halves are not independently useful. The whole of it is: project onto the
|
|
97
|
+
* light's basis, divide by the radius, and negate the depth axis so that *nearer the light* is
|
|
98
|
+
* smaller — which is the direction a depth test with `less` wants and is the opposite of the
|
|
99
|
+
* frame's reversed depth. `depthConvention.ts` carries why shadows are not reversed.
|
|
100
|
+
*
|
|
101
|
+
* `lightDir` points **toward** the light, as `Environment.directionalDir` does.
|
|
102
|
+
*/
|
|
103
|
+
/** The light's right, up and toward-the-light axes, nine numbers. See `lightBasis`. */
|
|
104
|
+
const BASIS = new Float64Array(9);
|
|
105
|
+
/**
|
|
106
|
+
* The light's three axes, which depend on its direction and on nothing else.
|
|
107
|
+
*
|
|
108
|
+
* **One basis for both fits, and the following fit needs it to be exactly this.** Snapping a map
|
|
109
|
+
* to whole texels only holds its edges still if the texel grid it snaps to is fixed, and the grid
|
|
110
|
+
* is this basis: `lightMatrix.ts` says the same about the forward path's, where anchoring the light
|
|
111
|
+
* at the focus instead would move light space under the snap and cancel it.
|
|
112
|
+
*/
|
|
113
|
+
function lightBasis(lightDir, out) {
|
|
114
|
+
const length = Math.hypot(lightDir[0], lightDir[1], lightDir[2]) || 1;
|
|
115
|
+
const ax = lightDir[0] / length;
|
|
116
|
+
const ay = lightDir[1] / length;
|
|
117
|
+
const az = lightDir[2] / length;
|
|
118
|
+
/*
|
|
119
|
+
* **The reference direction moves when the sun is overhead, and a sun overhead is the common
|
|
120
|
+
* case.** `cross(up, axis)` collapses as the two line up, so a light within a degree of straight
|
|
121
|
+
* down produces a basis of denormals and a map that is mostly one texel. Noon is not an edge
|
|
122
|
+
* case in a day cycle.
|
|
123
|
+
*/
|
|
124
|
+
const upX = Math.abs(ay) > OVERHEAD ? 0 : 0;
|
|
125
|
+
const upY = Math.abs(ay) > OVERHEAD ? 0 : 1;
|
|
126
|
+
const upZ = Math.abs(ay) > OVERHEAD ? 1 : 0;
|
|
127
|
+
let rx = upY * az - upZ * ay;
|
|
128
|
+
let ry = upZ * ax - upX * az;
|
|
129
|
+
let rz = upX * ay - upY * ax;
|
|
130
|
+
const rLength = Math.hypot(rx, ry, rz) || 1;
|
|
131
|
+
rx /= rLength;
|
|
132
|
+
ry /= rLength;
|
|
133
|
+
rz /= rLength;
|
|
134
|
+
/* The third axis is the cross of the other two, so the basis is orthonormal by construction. */
|
|
135
|
+
out[0] = rx;
|
|
136
|
+
out[1] = ry;
|
|
137
|
+
out[2] = rz;
|
|
138
|
+
out[3] = ay * rz - az * ry;
|
|
139
|
+
out[4] = az * rx - ax * rz;
|
|
140
|
+
out[5] = ax * ry - ay * rx;
|
|
141
|
+
out[6] = ax;
|
|
142
|
+
out[7] = ay;
|
|
143
|
+
out[8] = az;
|
|
144
|
+
}
|
|
145
|
+
export function fitDirectionalShadow(centre, radius, lightDir, out) {
|
|
146
|
+
lightBasis(lightDir, BASIS);
|
|
147
|
+
const rx = BASIS[0];
|
|
148
|
+
const ry = BASIS[1];
|
|
149
|
+
const rz = BASIS[2];
|
|
150
|
+
const ux = BASIS[3];
|
|
151
|
+
const uy = BASIS[4];
|
|
152
|
+
const uz = BASIS[5];
|
|
153
|
+
const ax = BASIS[6];
|
|
154
|
+
const ay = BASIS[7];
|
|
155
|
+
const az = BASIS[8];
|
|
156
|
+
const scale = 1 / Math.max(radius, 1e-6);
|
|
157
|
+
const put = (column, x, y, z) => {
|
|
158
|
+
out[column * 4] = x;
|
|
159
|
+
out[column * 4 + 1] = y;
|
|
160
|
+
out[column * 4 + 2] = z;
|
|
161
|
+
out[column * 4 + 3] = 0;
|
|
162
|
+
};
|
|
163
|
+
/* Rows are the basis and columns are the axes: row 0 is the light's right, row 2 its depth. */
|
|
164
|
+
put(0, rx * scale, ux * scale, -ax * scale);
|
|
165
|
+
put(1, ry * scale, uy * scale, -ay * scale);
|
|
166
|
+
put(2, rz * scale, uz * scale, -az * scale);
|
|
167
|
+
const dot = (x, y, z) => x * centre[0] + y * centre[1] + z * centre[2];
|
|
168
|
+
out[12] = -dot(rx, ry, rz) * scale;
|
|
169
|
+
out[13] = -dot(ux, uy, uz) * scale;
|
|
170
|
+
/* Positive, because the depth row is already negated: the centre of the scene lands at zero. */
|
|
171
|
+
out[14] = dot(ax, ay, az) * scale;
|
|
172
|
+
out[15] = 1;
|
|
173
|
+
/* The box is a diameter deep, which is what turns a stored depth difference into metres. */
|
|
174
|
+
return 2 * Math.max(radius, 1e-6);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* **The map for a frame: the whole scene while it fits inside `follow` metres, and a sphere of that
|
|
178
|
+
* radius around the eye once it does not.**
|
|
179
|
+
*
|
|
180
|
+
* The scene fit is right for what this pipeline was first given — rigs a few metres across, a box
|
|
181
|
+
* that never moves and so never shimmers — and wrong for a world, which is what the voxel
|
|
182
|
+
* sandbox's port made this pipeline draw: at radius 14 its sphere is hundreds of metres across and
|
|
183
|
+
* a 2048 map over it has texels a quarter of a metre wide. So past a radius the caller names, the
|
|
184
|
+
* map follows the eye, as the forward path's does in `lightMatrix.ts`. **At or under it nothing
|
|
185
|
+
* changes, to the bit**, and with no radius at all nothing ever does — which is what keeps the three
|
|
186
|
+
* rigs the control rather than a second fit's first customer.
|
|
187
|
+
*
|
|
188
|
+
* **Why the scene's size rather than whether the eye is inside it**: a fit that switched as the eye
|
|
189
|
+
* crossed a boundary would jump the whole map between two scales mid-walk, where a scene grows past
|
|
190
|
+
* the radius once and stays there.
|
|
191
|
+
*
|
|
192
|
+
* `sphere` is the scene's centre and radius, four floats, as `sceneShadowBounds` writes them.
|
|
193
|
+
* Returns the metres the depth range spans, which the lookup multiplies a stored difference by.
|
|
194
|
+
*/
|
|
195
|
+
export function fitShadow(sphere, eye, follow, lightDir, mapSize, out) {
|
|
196
|
+
const radius = sphere[3];
|
|
197
|
+
/* No radius is an infinite one: a scene is never larger, so the fit never follows. */
|
|
198
|
+
if (!(radius > (follow ?? Infinity))) {
|
|
199
|
+
return fitDirectionalShadow(sphere, radius, lightDir, out);
|
|
200
|
+
}
|
|
201
|
+
return fitFollowingShadow(eye, follow, sphere, lightDir, mapSize, out);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A sphere of `radius` around `focus`, **snapped to whole texels of a fixed light space**, and deep
|
|
205
|
+
* enough toward the light to hold every caster the scene has there.
|
|
206
|
+
*
|
|
207
|
+
* **Snapped, or every edge crawls.** The basis is the light's alone, so the grid is fixed; the
|
|
208
|
+
* focus's three light-space coordinates are rounded to it, and the map moves in whole texels or not
|
|
209
|
+
* at all. A world point then falls in the same texel until the map steps, rather than at a new
|
|
210
|
+
* sub-texel offset every frame.
|
|
211
|
+
*
|
|
212
|
+
* **Deep enough toward the sun for every caster.** A receiver is inside the sphere, and anything
|
|
213
|
+
* that shadows one lies between it and the light — so the range reaches toward the light as far as
|
|
214
|
+
* the scene's own far side, which is where the last possible caster is. `lightMatrix.ts` reaches
|
|
215
|
+
* three radii; a dusk sun over a city casts a tower's shadow further than that, and the scene's
|
|
216
|
+
* sphere is known exactly here. **The reach is rounded up to whole radii**, so it changes only when
|
|
217
|
+
* the eye has walked a radius along the light rather than rescaling every stored depth each frame.
|
|
218
|
+
*
|
|
219
|
+
* **And symmetric about the eye, which is not for casters but for the lookup.** The lookup gives
|
|
220
|
+
* up on the last stretch of stored depth, `SHADOW_DEPTH_FADE`, because in the scene fit that is the
|
|
221
|
+
* scene's far rim. The first version of this reached only a radius away from the sun, which put
|
|
222
|
+
* every receiver in that stretch — the ground below the eye came out less shadowed the further
|
|
223
|
+
* below it was, and the sphere's far side not at all. Symmetric, a receiver at most a radius from
|
|
224
|
+
* the eye is stored within `radius / reach` of the middle, and the least reach is the whole number
|
|
225
|
+
* of radii that keeps that short of the fade.
|
|
226
|
+
*/
|
|
227
|
+
function fitFollowingShadow(focus, radius, sphere, lightDir, mapSize, out) {
|
|
228
|
+
lightBasis(lightDir, BASIS);
|
|
229
|
+
const rx = BASIS[0];
|
|
230
|
+
const ry = BASIS[1];
|
|
231
|
+
const rz = BASIS[2];
|
|
232
|
+
const ux = BASIS[3];
|
|
233
|
+
const uy = BASIS[4];
|
|
234
|
+
const uz = BASIS[5];
|
|
235
|
+
const ax = BASIS[6];
|
|
236
|
+
const ay = BASIS[7];
|
|
237
|
+
const az = BASIS[8];
|
|
238
|
+
const fx = focus[0];
|
|
239
|
+
const fy = focus[1];
|
|
240
|
+
const fz = focus[2];
|
|
241
|
+
const texel = (2 * radius) / Math.max(1, mapSize);
|
|
242
|
+
const across = Math.round((rx * fx + ry * fy + rz * fz) / texel) * texel;
|
|
243
|
+
const up = Math.round((ux * fx + uy * fy + uz * fz) / texel) * texel;
|
|
244
|
+
const along = Math.round((ax * fx + ay * fy + az * fz) / texel) * texel;
|
|
245
|
+
const farSide = ax * sphere[0] +
|
|
246
|
+
ay * sphere[1] +
|
|
247
|
+
az * sphere[2] +
|
|
248
|
+
sphere[3];
|
|
249
|
+
/*
|
|
250
|
+
* A receiver a radius from the eye is stored at 0.5 + radius / (2 reach); keeping that below the
|
|
251
|
+
* fade's start needs reach >= radius / (2 (start - 0.5)), which is 1.25 radii and so two.
|
|
252
|
+
*/
|
|
253
|
+
const least = Math.ceil(1 / (2 * (SHADOW_DEPTH_FADE[0] - 0.5)));
|
|
254
|
+
const reach = radius * Math.max(least, Math.ceil((farSide - along) / radius));
|
|
255
|
+
const scale = 1 / radius;
|
|
256
|
+
const depth = 1 / reach;
|
|
257
|
+
const put = (column, x, y, z) => {
|
|
258
|
+
out[column * 4] = x;
|
|
259
|
+
out[column * 4 + 1] = y;
|
|
260
|
+
out[column * 4 + 2] = z;
|
|
261
|
+
out[column * 4 + 3] = 0;
|
|
262
|
+
};
|
|
263
|
+
put(0, rx * scale, ux * scale, -ax * depth);
|
|
264
|
+
put(1, ry * scale, uy * scale, -ay * depth);
|
|
265
|
+
put(2, rz * scale, uz * scale, -az * depth);
|
|
266
|
+
out[12] = -across * scale;
|
|
267
|
+
out[13] = -up * scale;
|
|
268
|
+
/* Nearer the light is smaller, as in the scene fit: the sunward end lands at -1. */
|
|
269
|
+
out[14] = along * depth;
|
|
270
|
+
out[15] = 1;
|
|
271
|
+
return 2 * reach;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* **Two corrections, and the difference is one negated row.**
|
|
275
|
+
*
|
|
276
|
+
* The lookup wants the light-space position with `x` and `y` still in -1..1 — it remaps those
|
|
277
|
+
* itself — and `z` already in the 0..1 WebGPU clips to. That is `SHADOW_LOOKUP_CORRECTION`, and it
|
|
278
|
+
* is `SHADOW_CLIP_CORRECTION` in `webgpu/renderer.ts` under another name, for the same reason: a
|
|
279
|
+
* map is sampled rather than presented.
|
|
280
|
+
*
|
|
281
|
+
* The raster wants the same depth and the **opposite rows**, and this is the part that is easy to
|
|
282
|
+
* get backwards. A generated vertex stage ends with `gl_Position.y = -gl_Position.y`, which naga
|
|
283
|
+
* writes and nothing here does — so the forward path's shadow pass is handed the unflipped matrix
|
|
284
|
+
* and emits a flipped picture, while a hand-written stage handed the same matrix emits an unflipped
|
|
285
|
+
* one and stores every row at the mirror of where the lookup goes looking. Flipping it here is what
|
|
286
|
+
* puts the two pipelines' maps in the same orientation, so one lookup can read either.
|
|
287
|
+
*
|
|
288
|
+
* **And the flip reverses the winding with it**, which is why the pipeline that uses this declares
|
|
289
|
+
* `frontFace: 'cw'` — the same pair `depthPass.ts` carries, arrived at from the other side.
|
|
290
|
+
*
|
|
291
|
+
* Neither reverses depth. `depthConvention.ts` leaves shadow maps conventional, so the row is
|
|
292
|
+
* `0.5z + 0.5` where the frame's is `0.5 - 0.5z`, and the pass that renders one clears to
|
|
293
|
+
* `SHADOW_DEPTH_CLEAR` and compares with `less`.
|
|
294
|
+
*/
|
|
295
|
+
export const SHADOW_LOOKUP_CORRECTION = new Float32Array([
|
|
296
|
+
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1,
|
|
297
|
+
]);
|
|
298
|
+
export const SHADOW_RASTER_CORRECTION = new Float32Array([
|
|
299
|
+
1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1,
|
|
300
|
+
]);
|
|
301
|
+
/**
|
|
302
|
+
* How far toward the light the light's cone test stands its eye, in metres.
|
|
303
|
+
*
|
|
304
|
+
* **A directional light has no eye, and the cone test wants one.** Stood this far toward the sun,
|
|
305
|
+
* every direction from the eye to a cluster in the map is the sun's own to within the cluster's
|
|
306
|
+
* distance from the camera over this — three hundred-thousandths of a radian across a city three
|
|
307
|
+
* kilometres wide — so the perspective test answers the directional one the map's raster applies
|
|
308
|
+
* when it culls the faces turned from the light. A triangle it could cull wrongly is that close to
|
|
309
|
+
* edge-on to the sun, and covers a thousandth of a texel of any map. In `float32` the eye is good
|
|
310
|
+
* to eight metres of the hundred million, which moves the direction by less again.
|
|
311
|
+
*/
|
|
312
|
+
export const LIGHT_CONE_DISTANCE = 1e8;
|
|
313
|
+
/** The eye the light's cone test reads: `eye` moved `LIGHT_CONE_DISTANCE` toward the light. */
|
|
314
|
+
export function lightConeEye(eye, lightDir, out) {
|
|
315
|
+
const x = lightDir[0];
|
|
316
|
+
const y = lightDir[1];
|
|
317
|
+
const z = lightDir[2];
|
|
318
|
+
const scale = LIGHT_CONE_DISTANCE / (Math.hypot(x, y, z) || 1);
|
|
319
|
+
out[0] = eye[0] + x * scale;
|
|
320
|
+
out[1] = eye[1] + y * scale;
|
|
321
|
+
out[2] = eye[2] + z * scale;
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The raster's matrix with its depth turned over, `1 - z`, and nothing else moved: what the light's
|
|
326
|
+
* cull projects a cluster through, because the light's pyramid is stored turned over
|
|
327
|
+
* (`shadowPyramidBase`) so the camera's cull and reduction can read it. Applied after
|
|
328
|
+
* `SHADOW_RASTER_CORRECTION`, so the rows are the raster's, which are the map's and the pyramid's.
|
|
329
|
+
*/
|
|
330
|
+
export const SHADOW_CULL_CORRECTION = new Float32Array([
|
|
331
|
+
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 1, 1,
|
|
332
|
+
]);
|
|
333
|
+
/** `out = correction · light`, column-major. */
|
|
334
|
+
export function correctShadowMatrix(correction, light, out) {
|
|
335
|
+
for (let column = 0; column < 4; column += 1) {
|
|
336
|
+
for (let row = 0; row < 4; row += 1) {
|
|
337
|
+
let sum = 0;
|
|
338
|
+
for (let k = 0; k < 4; k += 1) {
|
|
339
|
+
sum += correction[k * 4 + row] * light[column * 4 + k];
|
|
340
|
+
}
|
|
341
|
+
out[column * 4 + row] = sum;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's geometry, which arrives and leaves while the frame loop runs.
|
|
3
|
+
*
|
|
4
|
+
* **`GpuDrivenPass` uploaded its scene in a method called `buildStatic`**, and its constructor said
|
|
5
|
+
* why: this pipeline's geometry does not move. A voxel world streams chunks as a player walks and
|
|
6
|
+
* remeshes one on every block they break, so it has to.
|
|
7
|
+
*
|
|
8
|
+
* **Four `RangeAlloc`s over what the pass binds**: six arrays the scene keeps — the cluster and
|
|
9
|
+
* mesh records a frame's culling reads — and two it never keeps. The vertices and the indices go
|
|
10
|
+
* through a `GeometrySink` straight into the device's buffers when a mesh is placed, because
|
|
11
|
+
* holding them as well doubled what a world costs, and at the voxel sandbox's radius 32 the copy
|
|
12
|
+
* was an ArrayBuffer Chrome will not allocate. A mesh claims a run of each, writes its rows, and
|
|
13
|
+
* hands the runs back when it leaves.
|
|
14
|
+
*
|
|
15
|
+
* **One path, not two.** The static case is a scene filled once and never emptied — `streamingScene`
|
|
16
|
+
* is that, and it is what every caller of `buildGpuDrivenScene` plus `new GpuDrivenPass` becomes.
|
|
17
|
+
* Two paths would mean the three rigs in `DRAFT_SCENES` exercise the path no published scene uses,
|
|
18
|
+
* which is how a control stops being a control; `streamScene.test.ts` asserts this class packs a
|
|
19
|
+
* static scene byte for byte where `buildGpuDrivenScene` packed it.
|
|
20
|
+
*
|
|
21
|
+
* **`add` allocates only when a mesh larger than any before it arrives**, because it is called
|
|
22
|
+
* from a frame loop when a player walks: the scratch it interleaves into grows to the largest mesh
|
|
23
|
+
* and is reused. A scene filled before its pass mounts is the exception, and it is the static
|
|
24
|
+
* case: it holds each mesh until the pass attaches, and nothing after. `heldBytes` says which.
|
|
25
|
+
*/
|
|
26
|
+
import type { GpuDrivenMesh } from './sceneUpload.ts';
|
|
27
|
+
/** How many units of each axis a scene may hold. Declared once; a claim past it is refused. */
|
|
28
|
+
export interface StreamCapacity {
|
|
29
|
+
readonly vertices: number;
|
|
30
|
+
readonly indices: number;
|
|
31
|
+
readonly clusters: number;
|
|
32
|
+
readonly meshes: number;
|
|
33
|
+
}
|
|
34
|
+
/** What `add` hands back, and what `remove` needs to give the room back. */
|
|
35
|
+
export interface StreamHandle {
|
|
36
|
+
readonly mesh: number;
|
|
37
|
+
readonly vertexBase: number;
|
|
38
|
+
readonly vertexCount: number;
|
|
39
|
+
readonly indexBase: number;
|
|
40
|
+
readonly indexCount: number;
|
|
41
|
+
readonly clusterBase: number;
|
|
42
|
+
readonly clusterCount: number;
|
|
43
|
+
}
|
|
44
|
+
/** One buffer's changed run, in units of that buffer's record. */
|
|
45
|
+
export interface DirtySpan {
|
|
46
|
+
readonly from: number;
|
|
47
|
+
readonly to: number;
|
|
48
|
+
}
|
|
49
|
+
export interface StreamUpload {
|
|
50
|
+
readonly clusters: readonly DirtySpan[];
|
|
51
|
+
readonly meshes: readonly DirtySpan[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Where a scene's vertices and indices go, since it keeps no copy of them.
|
|
55
|
+
*
|
|
56
|
+
* **Handed a scratch array, and a sink must copy it before returning.** `rows` and `values` may be
|
|
57
|
+
* longer than `count` records: the scene interleaves every mesh into the same two arrays and
|
|
58
|
+
* reuses them on the next add, which is what keeps `add` from allocating in a frame loop.
|
|
59
|
+
* `queue.writeBuffer` copies when it is called, so `GpuDrivenPass` hands them straight on.
|
|
60
|
+
*/
|
|
61
|
+
export interface GeometrySink {
|
|
62
|
+
/** `count` vertices of twelve floats each, from the start of `rows`, placed at `firstVertex`. */
|
|
63
|
+
writeVertices(firstVertex: number, rows: Float32Array, count: number): void;
|
|
64
|
+
/** `count` indices from the start of `values`, already rebased onto the scene, at `firstIndex`. */
|
|
65
|
+
writeIndices(firstIndex: number, values: Uint32Array, count: number): void;
|
|
66
|
+
}
|
|
67
|
+
export declare class StreamingScene {
|
|
68
|
+
readonly capacity: StreamCapacity;
|
|
69
|
+
/** Eight floats a cluster: world centre, radius, cone axis, cutoff. */
|
|
70
|
+
readonly cull: Float32Array;
|
|
71
|
+
/** Six floats a cluster: world centre, radius, own error, parent error. */
|
|
72
|
+
readonly lod: Float32Array;
|
|
73
|
+
/** Four words a cluster: index offset, index count, cluster id, mesh index. */
|
|
74
|
+
readonly meta: Uint32Array;
|
|
75
|
+
readonly materialOf: Uint32Array;
|
|
76
|
+
readonly transforms: Float32Array;
|
|
77
|
+
readonly instanceSpheres: Float32Array;
|
|
78
|
+
private readonly vertexAlloc;
|
|
79
|
+
private readonly indexAlloc;
|
|
80
|
+
private readonly clusterAlloc;
|
|
81
|
+
private readonly meshAlloc;
|
|
82
|
+
private readonly dirtyClusters;
|
|
83
|
+
private readonly dirtyMeshes;
|
|
84
|
+
/**
|
|
85
|
+
* Scratch `worldClustersInto` needs, sized once.
|
|
86
|
+
*
|
|
87
|
+
* It wants `bounds` as four floats a cluster — centre and radius together — and `cones` as four,
|
|
88
|
+
* where a `ClusterSource` keeps `boundsCentre` / `boundsRadius` and `coneAxis` / `coneCutoff` as
|
|
89
|
+
* separate arrays. Packing them is a copy a frame loop must not allocate for.
|
|
90
|
+
*/
|
|
91
|
+
private readonly boundScratch;
|
|
92
|
+
private readonly coneScratch;
|
|
93
|
+
private readonly errorScratch;
|
|
94
|
+
private readonly meshScratch;
|
|
95
|
+
private live;
|
|
96
|
+
/**
|
|
97
|
+
* **Where the geometry is.** Held a mesh at a time until a pass attaches (`staging`), written
|
|
98
|
+
* straight to that pass's buffers (`attached`), or nowhere once the pass has gone (`spent`).
|
|
99
|
+
*/
|
|
100
|
+
private state;
|
|
101
|
+
private sink;
|
|
102
|
+
private readonly staged;
|
|
103
|
+
private rowScratch;
|
|
104
|
+
private indexScratch;
|
|
105
|
+
private waiting;
|
|
106
|
+
private waitingCount;
|
|
107
|
+
constructor(capacity: StreamCapacity);
|
|
108
|
+
/** How many clusters currently draw. A removed cluster's slot is still allocated until released. */
|
|
109
|
+
get liveClusters(): number;
|
|
110
|
+
/** Room left on each axis, which `scripts/stream-fragmentation.mjs` and its tests read. */
|
|
111
|
+
get freeVertices(): number;
|
|
112
|
+
/** The worst each axis ever reached, which is the occupancy a fragmentation run reports. */
|
|
113
|
+
get highWater(): StreamCapacity;
|
|
114
|
+
/**
|
|
115
|
+
* The geometry this scene holds on the CPU, in bytes: what is staged for a pass that has not
|
|
116
|
+
* attached, and the two scratch arrays an attached scene interleaves through. **Nothing past
|
|
117
|
+
* that** — which is the whole of what `GeometrySink` is for, as a number a test and a readout can
|
|
118
|
+
* read.
|
|
119
|
+
*/
|
|
120
|
+
get heldBytes(): number;
|
|
121
|
+
/**
|
|
122
|
+
* Give the scene the one place its geometry goes, and send it everything staged so far.
|
|
123
|
+
*
|
|
124
|
+
* **Once, and a second time is refused by name.** The scene keeps no copy of what it has sent,
|
|
125
|
+
* so a second pass would bind two empty buffers and draw nothing — silently, which is the one
|
|
126
|
+
* answer this cannot give. The dirty journal already made a scene one pass's: two passes would
|
|
127
|
+
* each take its spans and each miss the other's.
|
|
128
|
+
*/
|
|
129
|
+
attach(sink: GeometrySink): void;
|
|
130
|
+
/**
|
|
131
|
+
* The pass has gone. Adds and removes still keep the records, and the geometry goes nowhere.
|
|
132
|
+
*
|
|
133
|
+
* **Called by `GpuDrivenPass.dispose` before it destroys its buffers**, because a write to a
|
|
134
|
+
* destroyed buffer is a device error; and nothing waits after it, because nothing reads.
|
|
135
|
+
*/
|
|
136
|
+
detach(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Place a mesh, or refuse.
|
|
139
|
+
*
|
|
140
|
+
* **Every claim before any write, and a rollback if the last one fails.** A partial add is a mesh
|
|
141
|
+
* whose clusters point at vertices another mesh owns, which draws somebody else's geometry rather
|
|
142
|
+
* than failing — the shape of mistake `packClusters` documents one level up.
|
|
143
|
+
*
|
|
144
|
+
* **`null` rather than an exception**, because a streaming consumer calls this from a frame loop
|
|
145
|
+
* when the player walks: an exception there is a crashed demo where a refusal is a chunk that
|
|
146
|
+
* arrives a frame later.
|
|
147
|
+
*/
|
|
148
|
+
add(mesh: GpuDrivenMesh, transform: ArrayLike<number>, material: number): StreamHandle | null;
|
|
149
|
+
/** Take a mesh out. Its ranges go back and its clusters stop drawing. */
|
|
150
|
+
remove(handle: StreamHandle): void;
|
|
151
|
+
/** What changed since the last call, then cleared. An unchanged frame uploads nothing. */
|
|
152
|
+
takeDirty(): StreamUpload;
|
|
153
|
+
/**
|
|
154
|
+
* Where a mesh is interleaved: a fresh array while it has to be held until a pass attaches, and
|
|
155
|
+
* the one scratch otherwise, grown only when a mesh larger than any before it arrives.
|
|
156
|
+
*/
|
|
157
|
+
private rowsFor;
|
|
158
|
+
private valuesFor;
|
|
159
|
+
/** Hand a placed mesh's geometry on, or hold it for the pass that has not attached yet. */
|
|
160
|
+
private place;
|
|
161
|
+
/**
|
|
162
|
+
* Keep a removed mesh's vertex and index ranges from the allocator until the next `takeDirty`.
|
|
163
|
+
*
|
|
164
|
+
* **Because the geometry is written when a mesh is added, not when the frame is recorded.** The
|
|
165
|
+
* pass records its frame at `beginFrame` and submits it at `endFrame`, and a queue write lands
|
|
166
|
+
* before the frame it was made during runs. A mesh added in that window into a range freed that
|
|
167
|
+
* frame would be drawn through the removed mesh's clusters, which the device holds until the
|
|
168
|
+
* next upload. `takeDirty` is that upload's first step, so from then on nothing reads the range.
|
|
169
|
+
* The cluster and mesh slots do not wait: their records travel together through the journal.
|
|
170
|
+
*/
|
|
171
|
+
private hold;
|
|
172
|
+
private releaseWaiting;
|
|
173
|
+
/**
|
|
174
|
+
* A bounding sphere over every live cluster: centre in floats 0 to 2, radius in float 3.
|
|
175
|
+
*
|
|
176
|
+
* **`sceneShadowBounds` itself rather than a second spelling of it**, and that is not tidiness —
|
|
177
|
+
* the first version of this method wrote a *box* into the same four floats the pass reads a
|
|
178
|
+
* sphere out of, so the shadow camera was fitted to a sphere centred on the box's minimum corner
|
|
179
|
+
* with `maxX` as its radius. Two GPU-driven rigs lost their shadows and the maintainer saw it on
|
|
180
|
+
* the page before the capture diff was read.
|
|
181
|
+
*
|
|
182
|
+
* The function's own header already says why there is one copy of this arithmetic: a light's box
|
|
183
|
+
* that disagrees with the cull's bounds clips casters out of a map the cull believes are in it.
|
|
184
|
+
* A second copy here was the same mistake one level up.
|
|
185
|
+
*/
|
|
186
|
+
sceneBounds(out: Float32Array): void;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A scene filled once from a list of meshes, which is the static case.
|
|
190
|
+
*
|
|
191
|
+
* With no capacity given it sizes itself to exactly what the meshes need, so a static scene
|
|
192
|
+
* allocates nothing spare and packs where `buildGpuDrivenScene` packed.
|
|
193
|
+
*/
|
|
194
|
+
export declare function streamingScene(meshes: readonly GpuDrivenMesh[], transforms: Float32Array, capacity?: StreamCapacity): StreamingScene;
|