@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,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming world cells by the prediction that already streams texture tiles.
|
|
3
|
+
*
|
|
4
|
+
* **Almost no new mechanism, and that is the point.** Wave 3B built the speculative advance, the
|
|
5
|
+
* queue and the budget generically *for this* — its own header says so: "Wave 4B streams world
|
|
6
|
+
* cells by exactly this mechanism", and writing a second speculative advance here would be a
|
|
7
|
+
* second place for "put the simulation back exactly" to be got wrong, in a subsystem where being
|
|
8
|
+
* wrong means the world runs at double speed only while streaming is on. What is new here is
|
|
9
|
+
* `needs` — which cells a predicted view enters — and the load and the unload.
|
|
10
|
+
*
|
|
11
|
+
* **The predictor is satisfied structurally rather than imported.** `@driftengine/texture` has no
|
|
12
|
+
* dependencies and core must not gain it as one: `scripts/size-gate.test.mjs` asserts that
|
|
13
|
+
* importing core alone pulls in no optional package, and that assertion is worth more than an
|
|
14
|
+
* import. The shape here *is* `Predictor<number>`, and `cellStream.test.ts` proves it by running
|
|
15
|
+
* the real `runPrediction` over it through a dev dependency.
|
|
16
|
+
*
|
|
17
|
+
* **A cell is dropped after a hysteresis and never immediately.** A camera that turns round should
|
|
18
|
+
* not pay to reload what it just left, and dropping on the frame a cell leaves the radius makes
|
|
19
|
+
* walking back and forth across a boundary reload it every few frames — the most visible failure a
|
|
20
|
+
* streamer has.
|
|
21
|
+
*/
|
|
22
|
+
import { mat4 } from 'gl-matrix';
|
|
23
|
+
import { cellCoordsOf, cellsInFrustum, cellsInRadius, createCellGrid } from './cell.js';
|
|
24
|
+
export function createCellStream(options) {
|
|
25
|
+
return {
|
|
26
|
+
store: options.store,
|
|
27
|
+
size: options.size,
|
|
28
|
+
radius: options.radius,
|
|
29
|
+
hysteresis: options.hysteresis,
|
|
30
|
+
origin: options.origin === undefined
|
|
31
|
+
? new Float64Array(3)
|
|
32
|
+
: Float64Array.from(options.origin, Number),
|
|
33
|
+
lastWanted: new Map(),
|
|
34
|
+
frame: 0,
|
|
35
|
+
grid: createCellGrid(options.size, options.maxCells),
|
|
36
|
+
projection: options.projection ?? null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Move the origin the predicted views are relative to.
|
|
41
|
+
*
|
|
42
|
+
* Called with whatever `renderOrigin` answered for this frame, so the two cannot drift apart. It is
|
|
43
|
+
* a write rather than a parameter to `needs` because the predictor's shape is `Predictor<number>`
|
|
44
|
+
* and that interface is Wave 3B's, shared with texture tiles, which have no origin.
|
|
45
|
+
*/
|
|
46
|
+
export function setCellStreamOrigin(stream, x, y, z) {
|
|
47
|
+
stream.origin[0] = x;
|
|
48
|
+
stream.origin[1] = y;
|
|
49
|
+
stream.origin[2] = z;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Where the camera is, out of a view matrix, into `out`.
|
|
53
|
+
*
|
|
54
|
+
* **A view matrix is `R · T(−p)`, so its translation column is in *view* space** and negating it is
|
|
55
|
+
* right only while the camera is unrotated. The camera's world position is `−Rᵀ · t`, and the
|
|
56
|
+
* transpose is the inverse because a view rotation is orthonormal. Getting this wrong is invisible
|
|
57
|
+
* in any test whose camera is not turned, and wrong by the whole camera offset in every other.
|
|
58
|
+
*
|
|
59
|
+
* Column-major, as `gl-matrix` writes one: `m[0..2]` is the first column.
|
|
60
|
+
*/
|
|
61
|
+
export function cameraPositionFrom(view, out) {
|
|
62
|
+
const tx = view[12];
|
|
63
|
+
const ty = view[13];
|
|
64
|
+
const tz = view[14];
|
|
65
|
+
/* Rᵀ · t, which is a dot with each *column* of R — and a column of R is a row of Rᵀ. */
|
|
66
|
+
out[0] = -(view[0] * tx + view[1] * ty + view[2] * tz);
|
|
67
|
+
out[1] = -(view[4] * tx + view[5] * ty + view[6] * tz);
|
|
68
|
+
out[2] = -(view[8] * tx + view[9] * ty + view[10] * tz);
|
|
69
|
+
}
|
|
70
|
+
/* Reused across calls: this runs per predicted frame and these are its only allocations. */
|
|
71
|
+
const POSITION = new Float64Array(3);
|
|
72
|
+
const WANTED = [];
|
|
73
|
+
const AHEAD = [];
|
|
74
|
+
const AROUND = new Set();
|
|
75
|
+
const VIEW = new Float64Array(16);
|
|
76
|
+
const LENS = new Float64Array(16);
|
|
77
|
+
const VIEW_PROJECTION = new Float64Array(16);
|
|
78
|
+
const SHIFT = new Float64Array(3);
|
|
79
|
+
const EYE = new Float64Array(3);
|
|
80
|
+
const CELL = new Int32Array(3);
|
|
81
|
+
let cellEdge = 1;
|
|
82
|
+
/** How far a cell's centre is from `EYE`, squared. */
|
|
83
|
+
function reachOf(id) {
|
|
84
|
+
cellCoordsOf(id, CELL);
|
|
85
|
+
const x = (CELL[0] + 0.5) * cellEdge - EYE[0];
|
|
86
|
+
const y = (CELL[1] + 0.5) * cellEdge - EYE[1];
|
|
87
|
+
const z = (CELL[2] + 0.5) * cellEdge - EYE[2];
|
|
88
|
+
return x * x + y * y + z * z;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Nearest first. Two cells as near keep the order the query listed them in, which is identifier
|
|
92
|
+
* order, because a sort in this language is stable — so the order is the view's alone.
|
|
93
|
+
*/
|
|
94
|
+
function nearerFirst(a, b) {
|
|
95
|
+
return reachOf(a) - reachOf(b);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The cells a view looks into that the radius did not already name, appended to `WANTED`, nearest
|
|
99
|
+
* first — so that a budget smaller than both keeps the ground round the camera and then the view's
|
|
100
|
+
* nearest cells.
|
|
101
|
+
*/
|
|
102
|
+
function appendView(stream, projection, view) {
|
|
103
|
+
VIEW.set(view);
|
|
104
|
+
LENS.set(projection);
|
|
105
|
+
mat4.multiply(VIEW_PROJECTION, LENS, VIEW);
|
|
106
|
+
/* The views are render-space; the cells are absolute. Shift in double precision. */
|
|
107
|
+
SHIFT[0] = -stream.origin[0];
|
|
108
|
+
SHIFT[1] = -stream.origin[1];
|
|
109
|
+
SHIFT[2] = -stream.origin[2];
|
|
110
|
+
mat4.translate(VIEW_PROJECTION, VIEW_PROJECTION, SHIFT);
|
|
111
|
+
cellsInFrustum(stream.grid, VIEW_PROJECTION, AHEAD);
|
|
112
|
+
AROUND.clear();
|
|
113
|
+
for (const id of WANTED)
|
|
114
|
+
AROUND.add(id);
|
|
115
|
+
let kept = 0;
|
|
116
|
+
for (const id of AHEAD) {
|
|
117
|
+
if (!AROUND.has(id)) {
|
|
118
|
+
AHEAD[kept] = id;
|
|
119
|
+
kept += 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
AHEAD.length = kept;
|
|
123
|
+
cellEdge = stream.size;
|
|
124
|
+
AHEAD.sort(nearerFirst);
|
|
125
|
+
for (const id of AHEAD)
|
|
126
|
+
WANTED.push(id);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The stream as a `Predictor<number>` over cell identifiers.
|
|
130
|
+
*
|
|
131
|
+
* Structurally typed on purpose — see the header. A caller passes the result straight to
|
|
132
|
+
* `runPrediction` from `@driftengine/texture`.
|
|
133
|
+
*/
|
|
134
|
+
export function cellPredictor(stream) {
|
|
135
|
+
return {
|
|
136
|
+
needs(view, out, budget) {
|
|
137
|
+
cameraPositionFrom(view, POSITION);
|
|
138
|
+
/* Back to absolute, in double precision, before anything is quantised into a cell. */
|
|
139
|
+
EYE[0] = POSITION[0] + stream.origin[0];
|
|
140
|
+
EYE[1] = POSITION[1] + stream.origin[1];
|
|
141
|
+
EYE[2] = POSITION[2] + stream.origin[2];
|
|
142
|
+
cellsInRadius(EYE[0], EYE[1], EYE[2], stream.radius, stream.size, WANTED);
|
|
143
|
+
if (stream.projection !== null)
|
|
144
|
+
appendView(stream, stream.projection, view);
|
|
145
|
+
/*
|
|
146
|
+
* Wanting is recorded for every cell a predicted view enters, not only for the ones that fit
|
|
147
|
+
* inside the budget. Otherwise a budget-limited frame would let a cell the camera is heading
|
|
148
|
+
* straight for start its unload countdown.
|
|
149
|
+
*/
|
|
150
|
+
const take = Math.min(budget, WANTED.length);
|
|
151
|
+
out.length = 0;
|
|
152
|
+
for (let at = 0; at < WANTED.length; at += 1) {
|
|
153
|
+
const id = WANTED[at];
|
|
154
|
+
stream.lastWanted.set(id, stream.frame);
|
|
155
|
+
if (at < take)
|
|
156
|
+
out.push(id);
|
|
157
|
+
}
|
|
158
|
+
return out.length;
|
|
159
|
+
},
|
|
160
|
+
resident(id) {
|
|
161
|
+
return stream.store.loaded(id);
|
|
162
|
+
},
|
|
163
|
+
request(id, priority) {
|
|
164
|
+
stream.store.load(id, priority);
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Mark what is in range *now*, then drop what has gone unwanted long enough. Returns how many went.
|
|
170
|
+
*
|
|
171
|
+
* **The pump marks as well as drops, and the first draft only dropped.** Prediction marks what is
|
|
172
|
+
* coming; without the pump marking what is here, a camera standing still wants nothing — its
|
|
173
|
+
* predicted path never leaves the cell it is in, the horizon moves past every cell it already
|
|
174
|
+
* occupies, and the streamer unloads the ground somebody is standing on. That is not a case to
|
|
175
|
+
* guard against separately: it is the same question as "what does this camera want", asked about
|
|
176
|
+
* the present instead of the future.
|
|
177
|
+
*
|
|
178
|
+
* So there is no special case for the camera's own cell. `cellsInRadius` includes it at any
|
|
179
|
+
* radius, including zero, which means the guarantee follows from the marking rather than from a
|
|
180
|
+
* check somebody could delete without a test failing.
|
|
181
|
+
*/
|
|
182
|
+
export function pumpCellStream(stream, x, y, z) {
|
|
183
|
+
stream.frame += 1;
|
|
184
|
+
cellsInRadius(x, y, z, stream.radius, stream.size, WANTED);
|
|
185
|
+
for (const id of WANTED)
|
|
186
|
+
stream.lastWanted.set(id, stream.frame);
|
|
187
|
+
let dropped = 0;
|
|
188
|
+
for (const [id, wanted] of stream.lastWanted) {
|
|
189
|
+
if (stream.frame - wanted <= stream.hysteresis)
|
|
190
|
+
continue;
|
|
191
|
+
if (stream.store.loaded(id)) {
|
|
192
|
+
stream.store.unload(id);
|
|
193
|
+
dropped += 1;
|
|
194
|
+
}
|
|
195
|
+
stream.lastWanted.delete(id);
|
|
196
|
+
}
|
|
197
|
+
return dropped;
|
|
198
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an unloaded region does, decided rather than discovered.
|
|
3
|
+
*
|
|
4
|
+
* **An unloaded region is frozen: it is exactly reproducible and it does not simulate.** An agent
|
|
5
|
+
* in an unloaded cell does not walk anywhere, does not age, does not finish what it was doing. That
|
|
6
|
+
* is a decision with consequences a consumer has to know about, which is why it is written into
|
|
7
|
+
* `docs/CAPABILITIES.md` as well as here — a consumer discovering it from behaviour is the failure
|
|
8
|
+
* this file exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* **Freezing is a simulation decision and unloading is a memory one, and that separation is the
|
|
11
|
+
* whole design.** A frozen cell does not simulate whether or not its contents are resident, and
|
|
12
|
+
* only a frozen cell may be unloaded. So streaming cannot change what the simulation computes; it
|
|
13
|
+
* can only change where the bytes live. The alternative — unloading causes freezing — makes the
|
|
14
|
+
* simulation depend on how much memory a machine had, which is a divergence that appears on one
|
|
15
|
+
* player's computer and nowhere else.
|
|
16
|
+
*
|
|
17
|
+
* **A frozen cell still contributes to the fingerprint.** Dropping its contribution would make two
|
|
18
|
+
* runs agree only at the end, once everything had thawed, and agreeing only at the end means they
|
|
19
|
+
* disagreed for a while — in a rollback netcode, for a while during which those numbers were
|
|
20
|
+
* already sent.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* What freezing needs of a world.
|
|
24
|
+
*
|
|
25
|
+
* Taken as a parameter rather than imported, on `AGENTS.md`'s rule: core has no entity store and a
|
|
26
|
+
* consumer may be holding one of several. Six methods, all of which any store can answer.
|
|
27
|
+
*/
|
|
28
|
+
export interface FreezableWorld {
|
|
29
|
+
/** Every entity whose home is this cell, live or not. */
|
|
30
|
+
entitiesIn(cell: number): readonly number[];
|
|
31
|
+
/** Write an entity's whole state into `out` and return how many values were written. */
|
|
32
|
+
readState(entity: number, out: Float64Array): number;
|
|
33
|
+
/** Put a state back, exactly. */
|
|
34
|
+
writeState(entity: number, values: Float64Array, count: number): void;
|
|
35
|
+
simulating(entity: number): boolean;
|
|
36
|
+
setSimulating(entity: number, on: boolean): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* One cell's frozen contents. Entities in ascending order, so the packing is deterministic.
|
|
40
|
+
*
|
|
41
|
+
* Exported because whoever unloads a cell has to write this somewhere and read it back.
|
|
42
|
+
*/
|
|
43
|
+
export interface FrozenCell {
|
|
44
|
+
readonly entities: number[];
|
|
45
|
+
/** Each entity's values, one after another. */
|
|
46
|
+
readonly values: Float64Array;
|
|
47
|
+
/** How many values each entity contributed, parallel to `entities`. */
|
|
48
|
+
readonly counts: Int32Array;
|
|
49
|
+
/** Computed once when the cell froze. Frozen state does not change, so neither does this. */
|
|
50
|
+
readonly digest: number;
|
|
51
|
+
}
|
|
52
|
+
export interface FrozenCells {
|
|
53
|
+
readonly cells: Map<number, FrozenCell>;
|
|
54
|
+
}
|
|
55
|
+
export declare function createFrozenCells(): FrozenCells;
|
|
56
|
+
export declare function isCellFrozen(frozen: FrozenCells, cell: number): boolean;
|
|
57
|
+
/** The entities a cell froze, ascending. Empty for a cell that is not frozen. */
|
|
58
|
+
export declare function frozenEntities(frozen: FrozenCells, cell: number): readonly number[];
|
|
59
|
+
/**
|
|
60
|
+
* Stop a cell simulating and capture exactly what it held. `false` if it was already frozen.
|
|
61
|
+
*
|
|
62
|
+
* **Refuses rather than throws**, because the caller is a streamer inside a frame and the honest
|
|
63
|
+
* answer to "freeze this again" is that there is nothing to do.
|
|
64
|
+
*/
|
|
65
|
+
export declare function freezeCell(world: FreezableWorld, frozen: FrozenCells, cell: number): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Put a cell back exactly as it was frozen and let it simulate again. `false` if it was not frozen.
|
|
68
|
+
*
|
|
69
|
+
* **Exactly as it was frozen, and never where it would have got to.** An extrapolated thaw is how
|
|
70
|
+
* an unloaded region silently diverges from a loaded one: nothing throws, the numbers are
|
|
71
|
+
* plausible, and the fingerprints stop matching at a frame nobody can point at.
|
|
72
|
+
*/
|
|
73
|
+
export declare function thawCell(world: FreezableWorld, frozen: FrozenCells, cell: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* What a frozen cell contributes to the world's fingerprint. Zero for a cell nothing froze.
|
|
76
|
+
*
|
|
77
|
+
* **Constant for as long as the cell is frozen**, which is what makes a streamed run and an
|
|
78
|
+
* unstreamed one agree at every frame rather than only once everything has thawed. A consumer
|
|
79
|
+
* feeds this into whatever fingerprint it already keeps, alongside its live entities.
|
|
80
|
+
*/
|
|
81
|
+
export declare function frozenFingerprintContribution(frozen: FrozenCells, cell: number): number;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an unloaded region does, decided rather than discovered.
|
|
3
|
+
*
|
|
4
|
+
* **An unloaded region is frozen: it is exactly reproducible and it does not simulate.** An agent
|
|
5
|
+
* in an unloaded cell does not walk anywhere, does not age, does not finish what it was doing. That
|
|
6
|
+
* is a decision with consequences a consumer has to know about, which is why it is written into
|
|
7
|
+
* `docs/CAPABILITIES.md` as well as here — a consumer discovering it from behaviour is the failure
|
|
8
|
+
* this file exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* **Freezing is a simulation decision and unloading is a memory one, and that separation is the
|
|
11
|
+
* whole design.** A frozen cell does not simulate whether or not its contents are resident, and
|
|
12
|
+
* only a frozen cell may be unloaded. So streaming cannot change what the simulation computes; it
|
|
13
|
+
* can only change where the bytes live. The alternative — unloading causes freezing — makes the
|
|
14
|
+
* simulation depend on how much memory a machine had, which is a divergence that appears on one
|
|
15
|
+
* player's computer and nowhere else.
|
|
16
|
+
*
|
|
17
|
+
* **A frozen cell still contributes to the fingerprint.** Dropping its contribution would make two
|
|
18
|
+
* runs agree only at the end, once everything had thawed, and agreeing only at the end means they
|
|
19
|
+
* disagreed for a while — in a rollback netcode, for a while during which those numbers were
|
|
20
|
+
* already sent.
|
|
21
|
+
*/
|
|
22
|
+
export function createFrozenCells() {
|
|
23
|
+
return { cells: new Map() };
|
|
24
|
+
}
|
|
25
|
+
export function isCellFrozen(frozen, cell) {
|
|
26
|
+
return frozen.cells.has(cell);
|
|
27
|
+
}
|
|
28
|
+
/** The entities a cell froze, ascending. Empty for a cell that is not frozen. */
|
|
29
|
+
export function frozenEntities(frozen, cell) {
|
|
30
|
+
return frozen.cells.get(cell)?.entities ?? [];
|
|
31
|
+
}
|
|
32
|
+
const OFFSET_BASIS = 0x811c9dc5;
|
|
33
|
+
const PRIME = 0x01000193;
|
|
34
|
+
const SCRATCH = new Float64Array(64);
|
|
35
|
+
const BYTES = new DataView(new ArrayBuffer(8));
|
|
36
|
+
/**
|
|
37
|
+
* FNV-1a over the eight bytes of every value, which is the construction `Fingerprint` uses.
|
|
38
|
+
*
|
|
39
|
+
* **Over the bytes and not over the numbers**, for the reason that file gives: two doubles a
|
|
40
|
+
* hundredth apart may print the same and are a divergence, and the bytes are what tells them
|
|
41
|
+
* apart. `-0` and `+0` therefore hash differently, and that is correct — two peers holding
|
|
42
|
+
* different zeroes have genuinely diverged.
|
|
43
|
+
*/
|
|
44
|
+
function digestOf(entities, values, used) {
|
|
45
|
+
let h = OFFSET_BASIS;
|
|
46
|
+
const eat = (value) => {
|
|
47
|
+
BYTES.setFloat64(0, value);
|
|
48
|
+
for (let at = 0; at < 8; at += 1) {
|
|
49
|
+
h = Math.imul(h ^ BYTES.getUint8(at), PRIME) >>> 0;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
for (const entity of entities)
|
|
53
|
+
eat(entity);
|
|
54
|
+
for (let at = 0; at < used; at += 1)
|
|
55
|
+
eat(values[at]);
|
|
56
|
+
return h >>> 0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Stop a cell simulating and capture exactly what it held. `false` if it was already frozen.
|
|
60
|
+
*
|
|
61
|
+
* **Refuses rather than throws**, because the caller is a streamer inside a frame and the honest
|
|
62
|
+
* answer to "freeze this again" is that there is nothing to do.
|
|
63
|
+
*/
|
|
64
|
+
export function freezeCell(world, frozen, cell) {
|
|
65
|
+
if (frozen.cells.has(cell))
|
|
66
|
+
return false;
|
|
67
|
+
const entities = [...world.entitiesIn(cell)].sort((a, b) => a - b);
|
|
68
|
+
const counts = new Int32Array(entities.length);
|
|
69
|
+
let used = 0;
|
|
70
|
+
for (let at = 0; at < entities.length; at += 1) {
|
|
71
|
+
const entity = entities[at];
|
|
72
|
+
const count = world.readState(entity, SCRATCH.subarray(used));
|
|
73
|
+
counts[at] = count;
|
|
74
|
+
used += count;
|
|
75
|
+
}
|
|
76
|
+
const values = new Float64Array(used);
|
|
77
|
+
values.set(SCRATCH.subarray(0, used));
|
|
78
|
+
for (const entity of entities)
|
|
79
|
+
world.setSimulating(entity, false);
|
|
80
|
+
frozen.cells.set(cell, { entities, values, counts, digest: digestOf(entities, values, used) });
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Put a cell back exactly as it was frozen and let it simulate again. `false` if it was not frozen.
|
|
85
|
+
*
|
|
86
|
+
* **Exactly as it was frozen, and never where it would have got to.** An extrapolated thaw is how
|
|
87
|
+
* an unloaded region silently diverges from a loaded one: nothing throws, the numbers are
|
|
88
|
+
* plausible, and the fingerprints stop matching at a frame nobody can point at.
|
|
89
|
+
*/
|
|
90
|
+
export function thawCell(world, frozen, cell) {
|
|
91
|
+
const held = frozen.cells.get(cell);
|
|
92
|
+
if (held === undefined)
|
|
93
|
+
return false;
|
|
94
|
+
let at = 0;
|
|
95
|
+
for (let index = 0; index < held.entities.length; index += 1) {
|
|
96
|
+
const entity = held.entities[index];
|
|
97
|
+
const count = held.counts[index];
|
|
98
|
+
world.writeState(entity, held.values.subarray(at, at + count), count);
|
|
99
|
+
world.setSimulating(entity, true);
|
|
100
|
+
at += count;
|
|
101
|
+
}
|
|
102
|
+
frozen.cells.delete(cell);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* What a frozen cell contributes to the world's fingerprint. Zero for a cell nothing froze.
|
|
107
|
+
*
|
|
108
|
+
* **Constant for as long as the cell is frozen**, which is what makes a streamed run and an
|
|
109
|
+
* unstreamed one agree at every frame rather than only once everything has thawed. A consumer
|
|
110
|
+
* feeds this into whatever fingerprint it already keeps, alongside its live entities.
|
|
111
|
+
*/
|
|
112
|
+
export function frozenFingerprintContribution(frozen, cell) {
|
|
113
|
+
return frozen.cells.get(cell)?.digest ?? 0;
|
|
114
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where rendering puts its origin, and the rule that the simulation never moves.
|
|
3
|
+
*
|
|
4
|
+
* **Rebasing is a rendering concern. The simulation never rebases.** This is the rule the whole
|
|
5
|
+
* large-world design hangs on. A rebase that touched simulation state would change floating-point
|
|
6
|
+
* results — and therefore the replay fingerprint — for no reason a player could see, and the
|
|
7
|
+
* divergence would appear only in sessions that happened to cross an origin boundary. That is a
|
|
8
|
+
* defect nobody reproduces on demand and nobody attributes correctly.
|
|
9
|
+
*
|
|
10
|
+
* So: simulation coordinates stay absolute, in double precision, where the fixed-point kernel's
|
|
11
|
+
* range of roughly ±2³⁷ is more world than anybody has. Render coordinates are single precision
|
|
12
|
+
* relative to an origin that follows the camera.
|
|
13
|
+
*
|
|
14
|
+
* **The origin moves in whole cells and never continuously.** A continuously-moving origin
|
|
15
|
+
* re-quantises every vertex every frame, which is visible as the whole world shimmering — the
|
|
16
|
+
* precision problem it was meant to solve, wearing a different hat.
|
|
17
|
+
*/
|
|
18
|
+
/** The render origin for a camera at this position: its cell's minimum corner. */
|
|
19
|
+
export declare function renderOrigin(cameraX: number, cameraY: number, cameraZ: number, cellSize: number, out: Float64Array): void;
|
|
20
|
+
/**
|
|
21
|
+
* A world position in render space, as single precision.
|
|
22
|
+
*
|
|
23
|
+
* The subtraction happens in double precision and only the result narrows, which is the whole
|
|
24
|
+
* point: subtracting two large doubles and then narrowing keeps the precision, where narrowing
|
|
25
|
+
* first and subtracting loses it before the subtraction can help.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toRenderSpace(worldX: number, worldY: number, worldZ: number, origin: Float64Array, out: Float32Array): void;
|
|
28
|
+
/** Back to absolute, in double precision. Exact for anything `toRenderSpace` produced. */
|
|
29
|
+
export declare function toWorldSpace(render: Float32Array, origin: Float64Array, out: Float64Array): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where rendering puts its origin, and the rule that the simulation never moves.
|
|
3
|
+
*
|
|
4
|
+
* **Rebasing is a rendering concern. The simulation never rebases.** This is the rule the whole
|
|
5
|
+
* large-world design hangs on. A rebase that touched simulation state would change floating-point
|
|
6
|
+
* results — and therefore the replay fingerprint — for no reason a player could see, and the
|
|
7
|
+
* divergence would appear only in sessions that happened to cross an origin boundary. That is a
|
|
8
|
+
* defect nobody reproduces on demand and nobody attributes correctly.
|
|
9
|
+
*
|
|
10
|
+
* So: simulation coordinates stay absolute, in double precision, where the fixed-point kernel's
|
|
11
|
+
* range of roughly ±2³⁷ is more world than anybody has. Render coordinates are single precision
|
|
12
|
+
* relative to an origin that follows the camera.
|
|
13
|
+
*
|
|
14
|
+
* **The origin moves in whole cells and never continuously.** A continuously-moving origin
|
|
15
|
+
* re-quantises every vertex every frame, which is visible as the whole world shimmering — the
|
|
16
|
+
* precision problem it was meant to solve, wearing a different hat.
|
|
17
|
+
*/
|
|
18
|
+
/** The render origin for a camera at this position: its cell's minimum corner. */
|
|
19
|
+
export function renderOrigin(cameraX, cameraY, cameraZ, cellSize, out) {
|
|
20
|
+
out[0] = Math.floor(cameraX / cellSize) * cellSize;
|
|
21
|
+
out[1] = Math.floor(cameraY / cellSize) * cellSize;
|
|
22
|
+
out[2] = Math.floor(cameraZ / cellSize) * cellSize;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A world position in render space, as single precision.
|
|
26
|
+
*
|
|
27
|
+
* The subtraction happens in double precision and only the result narrows, which is the whole
|
|
28
|
+
* point: subtracting two large doubles and then narrowing keeps the precision, where narrowing
|
|
29
|
+
* first and subtracting loses it before the subtraction can help.
|
|
30
|
+
*/
|
|
31
|
+
export function toRenderSpace(worldX, worldY, worldZ, origin, out) {
|
|
32
|
+
out[0] = worldX - origin[0];
|
|
33
|
+
out[1] = worldY - origin[1];
|
|
34
|
+
out[2] = worldZ - origin[2];
|
|
35
|
+
}
|
|
36
|
+
/** Back to absolute, in double precision. Exact for anything `toRenderSpace` produced. */
|
|
37
|
+
export function toWorldSpace(render, origin, out) {
|
|
38
|
+
out[0] = render[0] + origin[0];
|
|
39
|
+
out[1] = render[1] + origin[1];
|
|
40
|
+
out[2] = render[2] + origin[2];
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "DriftEngine reusable browser 3D runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "
|
|
33
|
-
"@driftengine/physics": "
|
|
32
|
+
"@driftengine/drft": "4.0.0",
|
|
33
|
+
"@driftengine/physics": "4.0.0",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -61,5 +61,10 @@
|
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@driftengine/nav": "4.0.0",
|
|
67
|
+
"@driftengine/network": "4.0.0",
|
|
68
|
+
"@driftengine/texture": "4.0.0"
|
|
64
69
|
}
|
|
65
70
|
}
|
package/scripts/frames.mjs
CHANGED
|
@@ -143,6 +143,98 @@ export function speckle(image, { threshold = 12, region } = {}) {
|
|
|
143
143
|
return count;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Where the differing pixels are, as connected regions rather than as a count.
|
|
148
|
+
*
|
|
149
|
+
* **A defect arrives as a region and noise arrives as dust, and a pixel count cannot tell them
|
|
150
|
+
* apart.** Two shader compilers disagreeing in the last bits scatter single pixels along every
|
|
151
|
+
* silhouette of every blade of grass; a missing shadow or an unlit surface arrives as one solid
|
|
152
|
+
* block. Measured on `wind-field`, the same 29,094 changed pixels were 56% islands of one and two
|
|
153
|
+
* around a single region of 6,176 — and the 6,176 was a scatter batch casting no shadow at all on
|
|
154
|
+
* one backend, while the rest was nothing at all. Three of the four backend defects this
|
|
155
|
+
* repository has found were named by running this first and the query knobs second.
|
|
156
|
+
*
|
|
157
|
+
* **Four-connected, not eight.** A rasteriser leaves staircases of edge pixels that touch only at
|
|
158
|
+
* their corners, and joining those into one region reports a defect where one backend merely
|
|
159
|
+
* stepped a pixel earlier. The diagonal case is pinned by a test.
|
|
160
|
+
*
|
|
161
|
+
* Answers the islands sorted largest first, the largest size, and how many changed pixels sit in
|
|
162
|
+
* islands of four or fewer — the *dust*, which is the figure that says "this is the noise floor".
|
|
163
|
+
*/
|
|
164
|
+
export function islands(one, two, { region, tolerance = 1 } = {}) {
|
|
165
|
+
if (one.width !== two.width || one.height !== two.height) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`frames are ${one.width}x${one.height} and ${two.width}x${two.height}; nothing to compare`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const box = {
|
|
171
|
+
x0: region?.x0 ?? 0,
|
|
172
|
+
y0: region?.y0 ?? 0,
|
|
173
|
+
x1: region?.x1 ?? one.width,
|
|
174
|
+
y1: region?.y1 ?? one.height,
|
|
175
|
+
};
|
|
176
|
+
const width = one.width;
|
|
177
|
+
const changedAt = new Uint8Array(width * one.height);
|
|
178
|
+
let changed = 0;
|
|
179
|
+
for (let y = box.y0; y < box.y1; y++) {
|
|
180
|
+
for (let x = box.x0; x < box.x1; x++) {
|
|
181
|
+
const at = (y * width + x) * one.channels;
|
|
182
|
+
if (Math.abs(luminance(one.pixels, at) - luminance(two.pixels, at)) > tolerance) {
|
|
183
|
+
changedAt[y * width + x] = 1;
|
|
184
|
+
changed++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* An explicit stack rather than recursion: a region can be the whole frame, and a call stack
|
|
190
|
+
that deep is a crash rather than a slow answer. */
|
|
191
|
+
const seen = new Uint8Array(width * one.height);
|
|
192
|
+
const stack = [];
|
|
193
|
+
const found = [];
|
|
194
|
+
for (let start = 0; start < changedAt.length; start++) {
|
|
195
|
+
if (changedAt[start] === 0 || seen[start] === 1) continue;
|
|
196
|
+
let size = 0;
|
|
197
|
+
let x0 = width;
|
|
198
|
+
let y0 = one.height;
|
|
199
|
+
let x1 = -1;
|
|
200
|
+
let y1 = -1;
|
|
201
|
+
stack.push(start);
|
|
202
|
+
seen[start] = 1;
|
|
203
|
+
while (stack.length > 0) {
|
|
204
|
+
const at = stack.pop();
|
|
205
|
+
const x = at % width;
|
|
206
|
+
const y = (at - x) / width;
|
|
207
|
+
size++;
|
|
208
|
+
if (x < x0) x0 = x;
|
|
209
|
+
if (y < y0) y0 = y;
|
|
210
|
+
if (x > x1) x1 = x;
|
|
211
|
+
if (y > y1) y1 = y;
|
|
212
|
+
if (x + 1 < box.x1 && changedAt[at + 1] === 1 && seen[at + 1] === 0) {
|
|
213
|
+
seen[at + 1] = 1;
|
|
214
|
+
stack.push(at + 1);
|
|
215
|
+
}
|
|
216
|
+
if (x - 1 >= box.x0 && changedAt[at - 1] === 1 && seen[at - 1] === 0) {
|
|
217
|
+
seen[at - 1] = 1;
|
|
218
|
+
stack.push(at - 1);
|
|
219
|
+
}
|
|
220
|
+
if (y + 1 < box.y1 && changedAt[at + width] === 1 && seen[at + width] === 0) {
|
|
221
|
+
seen[at + width] = 1;
|
|
222
|
+
stack.push(at + width);
|
|
223
|
+
}
|
|
224
|
+
if (y - 1 >= box.y0 && changedAt[at - width] === 1 && seen[at - width] === 0) {
|
|
225
|
+
seen[at - width] = 1;
|
|
226
|
+
stack.push(at - width);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
found.push({ size, box: { x0, y0, x1, y1 } });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
found.sort((a, b) => b.size - a.size);
|
|
233
|
+
let dust = 0;
|
|
234
|
+
for (const island of found) if (island.size <= 4) dust += island.size;
|
|
235
|
+
return { changed, islands: found, largest: found[0]?.size ?? 0, dust };
|
|
236
|
+
}
|
|
237
|
+
|
|
146
238
|
/** One line per band, for printing a `compare` result without every caller writing the format. */
|
|
147
239
|
export function formatComparison(result) {
|
|
148
240
|
const lines = [
|