@driftengine/core 4.4.2 → 4.6.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 +97 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +28 -3
- package/dist/index.js +20 -1
- package/dist/render/backend/api.d.ts +34 -14
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +201 -16
- package/dist/render/backend/webgl2/renderer.js +596 -224
- package/dist/render/backend/webgpu/buffers.d.ts +29 -1
- package/dist/render/backend/webgpu/buffers.js +61 -4
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +7 -2
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +6 -1
- package/dist/render/backend/webgpu/flatPass.js +57 -9
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +206 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +23 -2
- package/dist/render/backend/webgpu/instanced.js +41 -4
- package/dist/render/backend/webgpu/latePass.d.ts +30 -0
- package/dist/render/backend/webgpu/latePass.js +140 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
- package/dist/render/backend/webgpu/motionPass.js +491 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
- package/dist/render/backend/webgpu/pipelineCache.js +11 -1
- package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
- package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
- package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
- package/dist/render/backend/webgpu/postPass.d.ts +3 -13
- package/dist/render/backend/webgpu/renderer.d.ts +271 -64
- package/dist/render/backend/webgpu/renderer.js +1395 -580
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
- package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
- package/dist/render/backend/webgpu/snapshotMips.js +52 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +22 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +78 -39
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/glass.d.ts +45 -0
- package/dist/render/glass.js +39 -0
- package/dist/render/glassCasters.d.ts +40 -0
- package/dist/render/glassCasters.js +84 -0
- package/dist/render/glassShadow.d.ts +78 -0
- package/dist/render/glassShadow.js +125 -0
- package/dist/render/globalMediumPass.d.ts +2 -3
- package/dist/render/globalMediumPass.js +2 -8
- package/dist/render/gpudriven/lit.d.ts +12 -0
- package/dist/render/gpudriven/lit.js +23 -0
- package/dist/render/gpudriven/materialTable.d.ts +15 -5
- package/dist/render/gpudriven/materialTable.js +26 -6
- package/dist/render/gpudriven/pipeline.d.ts +1 -1
- package/dist/render/gpudriven/pipeline.js +4 -3
- package/dist/render/gpudriven/sunGlass.d.ts +35 -0
- package/dist/render/gpudriven/sunGlass.js +89 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +36 -35
- package/dist/render/lightBudget.js +38 -37
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/lateRouting.d.ts +14 -0
- package/dist/render/recon/lateRouting.js +18 -0
- package/dist/render/recon/mover.d.ts +57 -0
- package/dist/render/recon/mover.js +86 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +16 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +60 -12
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +94 -15
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/glassTint.d.ts +6 -0
- package/dist/render/shaders/flat/glassTint.js +25 -0
- package/dist/render/shaders/flat/index.d.ts +13 -0
- package/dist/render/shaders/flat/index.js +9 -3
- package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
- package/dist/render/shaders/flat/lampGlass.js +183 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +190 -38
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -0
- package/dist/render/shaders/flat/preamble.js +96 -26
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +230 -7
- package/dist/render/shaders/generated/depth.wgsl.js +230 -7
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1644 -1596
- package/dist/render/shaders/generated/flat.wgsl.js +1683 -1621
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.js +21 -17
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/globalMedium.js +6 -6
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
- package/dist/render/shaders/lightVolume.js +6 -6
- package/dist/render/shaders/octahedralResolve.d.ts +8 -0
- package/dist/render/shaders/octahedralResolve.js +48 -17
- package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
- package/dist/render/shaders/recon/lateDepth.wgsl.js +69 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +84 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/shadowCasters.d.ts +20 -1
- package/dist/render/shadowCasters.js +15 -1
- package/dist/render/shadowMap.d.ts +54 -5
- package/dist/render/shadowMap.js +190 -36
- package/dist/render/sunGlassTint.d.ts +25 -0
- package/dist/render/sunGlassTint.js +79 -0
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +61 -1
- package/src/render/backend/api.ts +36 -11
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +700 -250
- package/src/render/backend/webgpu/buffers.ts +92 -5
- package/src/render/backend/webgpu/depthPass.ts +7 -2
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +62 -11
- package/src/render/backend/webgpu/glassTintPass.ts +245 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +42 -4
- package/src/render/backend/webgpu/latePass.ts +161 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
- package/src/render/backend/webgpu/motionPass.ts +535 -0
- package/src/render/backend/webgpu/pipelineCache.ts +12 -1
- package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
- package/src/render/backend/webgpu/renderer.ts +1671 -670
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
- package/src/render/backend/webgpu/snapshotMips.ts +80 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +91 -51
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/glass.ts +74 -0
- package/src/render/glassCasters.ts +164 -0
- package/src/render/glassShadow.ts +153 -0
- package/src/render/globalMediumPass.ts +4 -11
- package/src/render/gpudriven/lit.ts +31 -0
- package/src/render/gpudriven/materialTable.ts +28 -6
- package/src/render/gpudriven/pipeline.ts +4 -3
- package/src/render/gpudriven/sunGlass.ts +117 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +41 -38
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +16 -0
- package/src/render/sceneTarget.ts +62 -11
- package/src/render/shaders/depth.ts +118 -15
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +22 -3
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +190 -38
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +96 -26
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +237 -7
- package/src/render/shaders/generated/flat.wgsl.ts +1685 -1621
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +23 -17
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/globalMedium.ts +6 -6
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
- package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
- package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
- package/src/render/shaders/lightVolume.ts +6 -6
- package/src/render/shaders/octahedralResolve.ts +50 -17
- package/src/render/shaders/recon/lateDepth.wgsl.ts +72 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/translucentQueue.ts +20 -1
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -2,6 +2,7 @@ import { mat3 } from 'gl-matrix';
|
|
|
2
2
|
|
|
3
3
|
import { createEmptyTexture2D } from './emptyTexture.ts';
|
|
4
4
|
import { LIVE_POINT_SHADOW_MAPS, POINT_SHADOW_POOL } from './lightBudget.ts';
|
|
5
|
+
import { PointGlassTint } from './pointGlassTint.ts';
|
|
5
6
|
import { pointShadowFaceRotation } from './pointShadowImage.ts';
|
|
6
7
|
import { compileProgram, uniformLocations } from './shader.ts';
|
|
7
8
|
import { OCTAHEDRAL_RESOLVE_FRAG, OCTAHEDRAL_RESOLVE_VERT } from './shaders/octahedralResolve.ts';
|
|
@@ -34,6 +35,37 @@ export interface PointShadowTarget {
|
|
|
34
35
|
far: number,
|
|
35
36
|
near: number,
|
|
36
37
|
): void;
|
|
38
|
+
/** Whether the array has grown its glass layers: `growForGlass`. */
|
|
39
|
+
readonly hasGlass: boolean;
|
|
40
|
+
/** Whether `face` of `layer` holds glass, which a face that offers none must clear. */
|
|
41
|
+
heldGlass(layer: number, face: number): boolean;
|
|
42
|
+
/** The depth scratch again, cleared, for the face's glass. */
|
|
43
|
+
beginGlassFace(gl: WebGL2RenderingContext): void;
|
|
44
|
+
resolveGlassFace(
|
|
45
|
+
gl: WebGL2RenderingContext,
|
|
46
|
+
layer: number,
|
|
47
|
+
face: number,
|
|
48
|
+
far: number,
|
|
49
|
+
near: number,
|
|
50
|
+
): void;
|
|
51
|
+
/** The colour scratch, cleared to white, for what the face's glass lets through. */
|
|
52
|
+
beginTintFace(gl: WebGL2RenderingContext): void;
|
|
53
|
+
resolveTintFace(gl: WebGL2RenderingContext, layer: number, face: number, drawn: boolean): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Every published layer as the array stores it: `layer × stride`, and −1 left as −1.
|
|
58
|
+
*
|
|
59
|
+
* **Applied to the resolved arrays right after each resolve**, so every consumer of an uploaded
|
|
60
|
+
* index reads the physical layer: once glass arrives, light L's depth is at 2L and its glass at
|
|
61
|
+
* 2L + 1 (`PointShadowArray.growForGlass`). In place, because the resolve rewrites these every time.
|
|
62
|
+
*/
|
|
63
|
+
export function strideLayers(layers: Int32Array, stride: number): void {
|
|
64
|
+
if (stride === 1) return;
|
|
65
|
+
for (let i = 0; i < layers.length; i++) {
|
|
66
|
+
const layer = layers[i] as number;
|
|
67
|
+
if (layer >= 0) layers[i] = layer * stride;
|
|
68
|
+
}
|
|
37
69
|
}
|
|
38
70
|
|
|
39
71
|
/**
|
|
@@ -53,12 +85,16 @@ export interface PointShadowTarget {
|
|
|
53
85
|
* never the same object.
|
|
54
86
|
*/
|
|
55
87
|
export class PointShadowArray implements PointShadowTarget {
|
|
56
|
-
readonly texture: WebGLTexture;
|
|
57
88
|
/**
|
|
58
|
-
* How many
|
|
59
|
-
* for each rectangle a world said would cast. See the constructor's `extraLayers`.
|
|
89
|
+
* How many lights this array holds: the point pool, the two live transition maps, and two more
|
|
90
|
+
* for each rectangle a world said would cast. See the constructor's `extraLayers`. Counted in
|
|
91
|
+
* lights, so it does not move when glass doubles the storage under it.
|
|
60
92
|
*/
|
|
61
93
|
readonly layers: number;
|
|
94
|
+
/** Replaced once, when glass first casts: see `growForGlass`. */
|
|
95
|
+
private current: WebGLTexture;
|
|
96
|
+
/** What the lights' glass lets through, made with the glass layers. */
|
|
97
|
+
private tint: PointGlassTint | null = null;
|
|
62
98
|
|
|
63
99
|
private readonly framebuffer: WebGLFramebuffer;
|
|
64
100
|
private readonly scratchTexture: WebGLTexture;
|
|
@@ -96,23 +132,8 @@ export class PointShadowArray implements PointShadowTarget {
|
|
|
96
132
|
this.layers =
|
|
97
133
|
Math.min(lightCount, POINT_SHADOW_POOL) + LIVE_POINT_SHADOW_MAPS + Math.max(0, extraLayers);
|
|
98
134
|
|
|
99
|
-
const texture = gl.
|
|
100
|
-
|
|
101
|
-
this.texture = texture;
|
|
102
|
-
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
103
|
-
gl.texStorage3D(
|
|
104
|
-
gl.TEXTURE_2D_ARRAY,
|
|
105
|
-
1,
|
|
106
|
-
gl.DEPTH_COMPONENT24,
|
|
107
|
-
OCTAHEDRAL_EDGE,
|
|
108
|
-
OCTAHEDRAL_EDGE,
|
|
109
|
-
this.layers,
|
|
110
|
-
);
|
|
111
|
-
/* One storage level and no filtering, so textureLod(..., 0.0) is the only fetch there is. */
|
|
112
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
113
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
114
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
115
|
-
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
135
|
+
const texture = depthArray(gl, this.layers);
|
|
136
|
+
this.current = texture;
|
|
116
137
|
|
|
117
138
|
const scratchTexture = gl.createTexture();
|
|
118
139
|
if (scratchTexture === null) throw new Error('PointShadowArray: createTexture failed');
|
|
@@ -183,6 +204,117 @@ export class PointShadowArray implements PointShadowTarget {
|
|
|
183
204
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
184
205
|
}
|
|
185
206
|
|
|
207
|
+
/** The array texture the lit pass samples. Replaced once, when glass first casts. */
|
|
208
|
+
get texture(): WebGLTexture {
|
|
209
|
+
return this.current;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
get hasGlass(): boolean {
|
|
213
|
+
return this.tint !== null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Physical layers a light: 1, or 2 once glass has interleaved the array. */
|
|
217
|
+
get stride(): number {
|
|
218
|
+
return this.tint === null ? 1 : 2;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The glass tint, one layer a light, once glass has cast; the lit pass samples it. */
|
|
222
|
+
get tintTexture(): WebGLTexture | null {
|
|
223
|
+
return this.tint?.texture ?? null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Interleave the array for glass: light L's depth moves to 2L and its glass takes 2L + 1, and
|
|
228
|
+
* the tint is made. Returns whether it grew — once, on the first face whose casters held glass.
|
|
229
|
+
*
|
|
230
|
+
* **Interleaved rather than stacked above the opaque half** because a shader cannot ask an array
|
|
231
|
+
* its layer count under naga, so it cannot find `L + N`; `layer + 1` it can. **Every map already
|
|
232
|
+
* baked is carried across** by a depth blit, so nothing goes dark, and **no map needs baking
|
|
233
|
+
* again**: this runs on the first face that saw glass, so no map baked before it saw any, and a
|
|
234
|
+
* glass layer born at the far plane with a white tint is exactly what its bake would draw. What
|
|
235
|
+
* it costs is the doubled depth (4.19 MB a light at 1024) and the tint (5.6 MB a light with its
|
|
236
|
+
* chain, 1.4 MB at `'half'`), from then on.
|
|
237
|
+
*/
|
|
238
|
+
growForGlass(gl: WebGL2RenderingContext, tintEdge: number): boolean {
|
|
239
|
+
if (this.tint !== null) return false;
|
|
240
|
+
const old = this.current;
|
|
241
|
+
const texture = depthArray(gl, this.layers * 2);
|
|
242
|
+
/* Off the unit it was made on, as the constructor leaves its own: see `beginFace`. */
|
|
243
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
244
|
+
const read = gl.createFramebuffer();
|
|
245
|
+
if (read === null) throw new Error('PointShadowArray: createFramebuffer failed');
|
|
246
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read);
|
|
247
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
|
|
248
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
249
|
+
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, old, 0, layer);
|
|
250
|
+
gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texture, 0, 2 * layer);
|
|
251
|
+
gl.blitFramebuffer(
|
|
252
|
+
0,
|
|
253
|
+
0,
|
|
254
|
+
OCTAHEDRAL_EDGE,
|
|
255
|
+
OCTAHEDRAL_EDGE,
|
|
256
|
+
0,
|
|
257
|
+
0,
|
|
258
|
+
OCTAHEDRAL_EDGE,
|
|
259
|
+
OCTAHEDRAL_EDGE,
|
|
260
|
+
gl.DEPTH_BUFFER_BIT,
|
|
261
|
+
gl.NEAREST,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
/* Nothing is nearer a lamp than a pane that has not been drawn. */
|
|
265
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
266
|
+
gl.framebufferTextureLayer(
|
|
267
|
+
gl.DRAW_FRAMEBUFFER,
|
|
268
|
+
gl.DEPTH_ATTACHMENT,
|
|
269
|
+
texture,
|
|
270
|
+
0,
|
|
271
|
+
2 * layer + 1,
|
|
272
|
+
);
|
|
273
|
+
gl.clearBufferfv(gl.DEPTH, 0, FAR_PLANE);
|
|
274
|
+
}
|
|
275
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
|
|
276
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
277
|
+
gl.deleteFramebuffer(read);
|
|
278
|
+
gl.deleteTexture(old);
|
|
279
|
+
this.current = texture;
|
|
280
|
+
this.tint = new PointGlassTint(gl, this.faceSize, tintEdge, this.layers);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
heldGlass(layer: number, face: number): boolean {
|
|
285
|
+
return this.tint?.held(layer, face) ?? false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The depth scratch, cleared, for the face's glass: `beginFace` exactly. */
|
|
289
|
+
beginGlassFace(gl: WebGL2RenderingContext): void {
|
|
290
|
+
this.beginFace(gl);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** The face's glass depth into light `layer`'s glass layer, 2L + 1. */
|
|
294
|
+
resolveGlassFace(
|
|
295
|
+
gl: WebGL2RenderingContext,
|
|
296
|
+
layer: number,
|
|
297
|
+
face: number,
|
|
298
|
+
far: number,
|
|
299
|
+
near: number,
|
|
300
|
+
): void {
|
|
301
|
+
if (this.tint === null) return;
|
|
302
|
+
this.resolveInto(gl, 2 * layer + 1, face, far, near);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
beginTintFace(gl: WebGL2RenderingContext): void {
|
|
306
|
+
this.tint?.beginFace(gl);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
resolveTintFace(gl: WebGL2RenderingContext, layer: number, face: number, drawn: boolean): void {
|
|
310
|
+
this.tint?.resolveFace(gl, layer, face, drawn);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** The chain frost reads, for every light whose tint a round touched: once a round. */
|
|
314
|
+
finishTints(gl: WebGL2RenderingContext): void {
|
|
315
|
+
this.tint?.fillMips(gl);
|
|
316
|
+
}
|
|
317
|
+
|
|
186
318
|
/**
|
|
187
319
|
* Bind the scratch as the render target for one face of one bake, and clear it.
|
|
188
320
|
*
|
|
@@ -224,9 +356,20 @@ export class PointShadowArray implements PointShadowTarget {
|
|
|
224
356
|
face: number,
|
|
225
357
|
far: number,
|
|
226
358
|
near: number,
|
|
359
|
+
): void {
|
|
360
|
+
this.resolveInto(gl, layer * this.stride, face, far, near);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** The scratch into physical layer `physical`'s region for `face`. */
|
|
364
|
+
private resolveInto(
|
|
365
|
+
gl: WebGL2RenderingContext,
|
|
366
|
+
physical: number,
|
|
367
|
+
face: number,
|
|
368
|
+
far: number,
|
|
369
|
+
near: number,
|
|
227
370
|
): void {
|
|
228
371
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
229
|
-
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, this.
|
|
372
|
+
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, this.current, 0, physical);
|
|
230
373
|
gl.viewport(0, 0, OCTAHEDRAL_EDGE, OCTAHEDRAL_EDGE);
|
|
231
374
|
|
|
232
375
|
gl.useProgram(this.program);
|
|
@@ -267,10 +410,33 @@ export class PointShadowArray implements PointShadowTarget {
|
|
|
267
410
|
dispose(gl: WebGL2RenderingContext): void {
|
|
268
411
|
gl.deleteFramebuffer(this.framebuffer);
|
|
269
412
|
gl.deleteFramebuffer(this.scratchFramebuffer);
|
|
270
|
-
gl.deleteTexture(this.
|
|
413
|
+
gl.deleteTexture(this.current);
|
|
271
414
|
gl.deleteTexture(this.scratchTexture);
|
|
415
|
+
this.tint?.dispose(gl);
|
|
272
416
|
gl.deleteTexture(this.parked);
|
|
273
417
|
gl.deleteProgram(this.program);
|
|
274
418
|
gl.deleteVertexArray(this.vao);
|
|
275
419
|
}
|
|
276
420
|
}
|
|
421
|
+
|
|
422
|
+
const FAR_PLANE = new Float32Array([1]);
|
|
423
|
+
|
|
424
|
+
/** The depth array: one storage level and no filtering, so textureLod(..., 0.0) is the only fetch. */
|
|
425
|
+
function depthArray(gl: WebGL2RenderingContext, layers: number): WebGLTexture {
|
|
426
|
+
const texture = gl.createTexture();
|
|
427
|
+
if (texture === null) throw new Error('PointShadowArray: createTexture failed');
|
|
428
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
429
|
+
gl.texStorage3D(
|
|
430
|
+
gl.TEXTURE_2D_ARRAY,
|
|
431
|
+
1,
|
|
432
|
+
gl.DEPTH_COMPONENT24,
|
|
433
|
+
OCTAHEDRAL_EDGE,
|
|
434
|
+
OCTAHEDRAL_EDGE,
|
|
435
|
+
layers,
|
|
436
|
+
);
|
|
437
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
438
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
439
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
440
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
441
|
+
return texture;
|
|
442
|
+
}
|
|
@@ -36,6 +36,19 @@ export { FACE_COUNT, POINT_SHADOW_NEAR };
|
|
|
36
36
|
|
|
37
37
|
const scratchViewProj = mat4.create();
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* What a bake asks of the renderer about glass, after each face's opaque depth.
|
|
41
|
+
*
|
|
42
|
+
* **Asked before it is drawn** because the answer decides whether the face binds a glass target at
|
|
43
|
+
* all: a face that offers no glass and holds none from a previous owner does no glass work.
|
|
44
|
+
*/
|
|
45
|
+
export interface PointGlassDraw {
|
|
46
|
+
/** Whether the face just drawn offered any glass. */
|
|
47
|
+
offered(): boolean;
|
|
48
|
+
/** Draw that glass: its depth, or what it lets through lit from the bake origin. */
|
|
49
|
+
draw(pass: 'depth' | 'tint', x: number, y: number, z: number): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
export class PointShadowMap {
|
|
40
53
|
readonly size: number;
|
|
41
54
|
/**
|
|
@@ -163,6 +176,8 @@ export class PointShadowMap {
|
|
|
163
176
|
near = POINT_SHADOW_NEAR,
|
|
164
177
|
sourceRadius = DEFAULT_SOURCE_RADIUS,
|
|
165
178
|
maxFaces = FACE_COUNT,
|
|
179
|
+
/** The face's glass, when glass shadows are on: `PointGlassDraw`. */
|
|
180
|
+
glass: PointGlassDraw | null = null,
|
|
166
181
|
): number {
|
|
167
182
|
/*
|
|
168
183
|
* What to render now, **and what to render it for**. A bake in flight owns the parameters
|
|
@@ -220,6 +235,7 @@ export class PointShadowMap {
|
|
|
220
235
|
* next face's pass does care about.
|
|
221
236
|
*/
|
|
222
237
|
array.resolveFace(gl, this.layer, face, plan.range, plan.near);
|
|
238
|
+
if (glass !== null) this.bakeGlassFace(gl, array, glass, face, plan);
|
|
223
239
|
}
|
|
224
240
|
|
|
225
241
|
gl.enable(gl.CULL_FACE);
|
|
@@ -237,5 +253,30 @@ export class PointShadowMap {
|
|
|
237
253
|
* needing disposal is exactly the shape of a leak nobody notices. The array is disposed by the
|
|
238
254
|
* renderer that built it.
|
|
239
255
|
*/
|
|
256
|
+
/**
|
|
257
|
+
* The face's glass, twice: where the nearest pane is, then what the panes let through.
|
|
258
|
+
*
|
|
259
|
+
* A face that offers no glass still clears a region a previous owner's glass is in — the depth to
|
|
260
|
+
* the far plane and the tint to white, from scratches cleared and not drawn into — because a pool
|
|
261
|
+
* slot changes hands with its layer.
|
|
262
|
+
*/
|
|
263
|
+
private bakeGlassFace(
|
|
264
|
+
gl: WebGL2RenderingContext,
|
|
265
|
+
array: PointShadowTarget,
|
|
266
|
+
glass: PointGlassDraw,
|
|
267
|
+
face: number,
|
|
268
|
+
plan: FaceRange,
|
|
269
|
+
): void {
|
|
270
|
+
if (!array.hasGlass) return;
|
|
271
|
+
const offered = glass.offered();
|
|
272
|
+
if (!offered && !array.heldGlass(this.layer, face)) return;
|
|
273
|
+
array.beginGlassFace(gl);
|
|
274
|
+
if (offered) glass.draw('depth', plan.x, plan.y, plan.z);
|
|
275
|
+
array.resolveGlassFace(gl, this.layer, face, plan.range, plan.near);
|
|
276
|
+
array.beginTintFace(gl);
|
|
277
|
+
if (offered) glass.draw('tint', plan.x, plan.y, plan.z);
|
|
278
|
+
array.resolveTintFace(gl, this.layer, face, offered);
|
|
279
|
+
}
|
|
280
|
+
|
|
240
281
|
dispose(_gl: WebGL2RenderingContext): void {}
|
|
241
282
|
}
|
package/src/render/rainField.ts
CHANGED
|
@@ -53,6 +53,17 @@ export interface RainFieldOptions {
|
|
|
53
53
|
streakSec?: number;
|
|
54
54
|
/** The ground drops land on, and the roofs they do not fall through. */
|
|
55
55
|
ground?: GroundSurface;
|
|
56
|
+
/**
|
|
57
|
+
* How near the viewer a streak may pass and still be drawn, metres. Default 1.
|
|
58
|
+
*
|
|
59
|
+
* **A drop that close is out of any focus a camera holds**, and drawn sharp it was a bar across
|
|
60
|
+
* the frame: a streak is a world-space width, so a 6 mm drop 30 cm from the eye drew fourteen
|
|
61
|
+
* pixels thick and as long as the frame, from a city seen from the air in the rain. A streak
|
|
62
|
+
* whose nearest point is inside this distance is not drawn; the drop keeps falling, as a
|
|
63
|
+
* sheltered one does. What it gives up is rain on a lens or a visor, which this field never
|
|
64
|
+
* drew as such anyway. What would make it wrong is a view that wants drops in macro: pass 0.
|
|
65
|
+
*/
|
|
66
|
+
clearM?: number;
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
export class RainField {
|
|
@@ -97,6 +108,8 @@ export class RainField {
|
|
|
97
108
|
*/
|
|
98
109
|
streakSec: number;
|
|
99
110
|
private readonly ground: GroundSurface | null;
|
|
111
|
+
/** How near the viewer a streak may pass and still be drawn, squared. */
|
|
112
|
+
private readonly clear2: number;
|
|
100
113
|
/** Drops not drawn this update because something was over them. */
|
|
101
114
|
private sheltered = 0;
|
|
102
115
|
/**
|
|
@@ -125,6 +138,8 @@ export class RainField {
|
|
|
125
138
|
this.speedMps = options.speedMps ?? 9;
|
|
126
139
|
this.streakSec = options.streakSec ?? 0.04;
|
|
127
140
|
this.ground = options.ground ?? null;
|
|
141
|
+
const clear = options.clearM ?? 1;
|
|
142
|
+
this.clear2 = clear * clear;
|
|
128
143
|
this.segments = createLineSegments(options.count);
|
|
129
144
|
this.x = new Float32Array(options.count);
|
|
130
145
|
this.y = new Float32Array(options.count);
|
|
@@ -212,15 +227,34 @@ export class RainField {
|
|
|
212
227
|
continue;
|
|
213
228
|
}
|
|
214
229
|
|
|
230
|
+
/* Where it was a shutter-time ago, which is the streak: down by its fall and back along the
|
|
231
|
+
wind, so a gust leans every drop at the same angle without anything trigonometric. */
|
|
232
|
+
const backX = -windX * this.streakSec;
|
|
233
|
+
const backY = this.speedMps * this.streakSec;
|
|
234
|
+
const backZ = -windZ * this.streakSec;
|
|
235
|
+
/* Too near the eye to be drawn (`clearM`): the streak's nearest point to the viewer. */
|
|
236
|
+
if (this.clear2 > 0) {
|
|
237
|
+
const ox = dropX - viewerX;
|
|
238
|
+
const oy = dropY - viewerY;
|
|
239
|
+
const oz = dropZ - viewerZ;
|
|
240
|
+
const length2 = backX * backX + backY * backY + backZ * backZ;
|
|
241
|
+
const along =
|
|
242
|
+
length2 > 0
|
|
243
|
+
? Math.min(Math.max(-(ox * backX + oy * backY + oz * backZ) / length2, 0), 1)
|
|
244
|
+
: 0;
|
|
245
|
+
const nx = ox + backX * along;
|
|
246
|
+
const ny = oy + backY * along;
|
|
247
|
+
const nz = oz + backZ * along;
|
|
248
|
+
if (nx * nx + ny * ny + nz * nz < this.clear2) continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
215
251
|
const at = this.segments.count;
|
|
216
252
|
this.segments.from[at * 3] = dropX;
|
|
217
253
|
this.segments.from[at * 3 + 1] = dropY;
|
|
218
254
|
this.segments.from[at * 3 + 2] = dropZ;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.segments.to[at * 3] =
|
|
222
|
-
this.segments.to[at * 3 + 1] = dropY + this.speedMps * this.streakSec;
|
|
223
|
-
this.segments.to[at * 3 + 2] = dropZ - windZ * this.streakSec;
|
|
255
|
+
this.segments.to[at * 3] = dropX + backX;
|
|
256
|
+
this.segments.to[at * 3 + 1] = dropY + backY;
|
|
257
|
+
this.segments.to[at * 3 + 2] = dropZ + backZ;
|
|
224
258
|
this.segments.count = at + 1;
|
|
225
259
|
}
|
|
226
260
|
this.primed = true;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which frame a change to a dynamic mesh or an instanced batch belongs to.
|
|
3
|
+
*
|
|
4
|
+
* **A consumer may change geometry in its simulation step, before `beginFrame`.** That change is
|
|
5
|
+
* what the *next* frame draws, so it is that frame's — counting it against the frame last begun
|
|
6
|
+
* would say the geometry changed a frame early and then did not change at all, which is a motion
|
|
7
|
+
* vector on the wrong frame and none on the right one.
|
|
8
|
+
*
|
|
9
|
+
* Only the **first** change in a frame keeps the state before it: a second rewrite in the same
|
|
10
|
+
* frame must not replace "where it was last frame" with "where it was a moment ago".
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export function frameOfChange(serial: number, inFrame: boolean): number {
|
|
14
|
+
return inFrame ? serial : serial + 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function keepsPrevious(lastChange: number, frame: number): boolean {
|
|
18
|
+
return lastChange !== frame;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Slots `0 … n-1` pair with last frame's; a slot past last frame's count is new and has no past. */
|
|
22
|
+
export function pairedInstances(previousCount: number, count: number): number {
|
|
23
|
+
return Math.min(previousCount, count);
|
|
24
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The depth a reconstructing frame's late draws are tested against at an output pixel, taken from
|
|
3
|
+
* the render's jittered depth. `shaders/recon/lateDepth.wgsl.ts` is the same arithmetic on the device.
|
|
4
|
+
*
|
|
5
|
+
* **Each surface taken to the pixel, and the nearest of them.** A late draw — a glow, a pane, a
|
|
6
|
+
* caption — is drawn unjittered at the output size, and it has to meet the depth of what it lies on
|
|
7
|
+
* where *it* is. Each of the four render texels about the pixel, placed where the jitter put it,
|
|
8
|
+
* carries its surface on to the pixel's unjittered centre along that surface's own slope; the
|
|
9
|
+
* nearest of the four so carried stands, and a quarter of its slope is given back toward the far
|
|
10
|
+
* side so that a draw lying exactly on the surface is not decided by rounding. On a plane all four
|
|
11
|
+
* arrive at the plane's own depth at the pixel: exact, and the same whatever the jitter was.
|
|
12
|
+
*
|
|
13
|
+
* **A texel's slope is the smaller of its two differences along each axis, and none where they
|
|
14
|
+
* disagree in sign** — the minmod limiter. Beside an edge one difference is the jump, so it is the
|
|
15
|
+
* other, the surface's own, that is taken; a sliver one texel wide disagrees both ways and keeps its
|
|
16
|
+
* own depth, the nearest of the four as before. So a translucent draw still never comes through an
|
|
17
|
+
* opaque edge the render covered by more than the render's own texel.
|
|
18
|
+
*
|
|
19
|
+
* **Why this replaced the nearest of the four.** On a wall seen aslant the nearest of four neighbours
|
|
20
|
+
* stands up to a texel's step in front of the wall at the pixel, so a glow lying a few centimetres
|
|
21
|
+
* proud of it was judged behind the wall; and which neighbour was nearest moved with the jitter, so
|
|
22
|
+
* the glow was drawn one frame and not the next. Measured on a street of lit facades: every strip of
|
|
23
|
+
* glow on every tower flickered frame by frame, 12,415 pixels of one tower between two frames.
|
|
24
|
+
*
|
|
25
|
+
* What it gives up: a surface curving within a texel is carried on as a plane, which a quarter-step
|
|
26
|
+
* tolerance covers. What would make it wrong is depth that is not affine on a plane — a depth written
|
|
27
|
+
* from the fragment stage — which nothing drawn before the late pass writes.
|
|
28
|
+
*/
|
|
29
|
+
import { REVERSED_DEPTH } from '../depthConvention.ts';
|
|
30
|
+
|
|
31
|
+
/** The share of a surface's slope given back toward the far side, for a draw lying on it. */
|
|
32
|
+
const TOLERANCE_SHARE = 0.25;
|
|
33
|
+
|
|
34
|
+
/** The smaller of two differences, or none where they disagree in sign. */
|
|
35
|
+
function minmod(a: number, b: number): number {
|
|
36
|
+
if (a * b <= 0) return 0;
|
|
37
|
+
return Math.abs(a) < Math.abs(b) ? a : b;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Where output pixel (`outX`, `outY`) — its centre, in pixels — falls among the render texels
|
|
42
|
+
* jittered by (`jitterX`, `jitterY`) render texels: into `out`, the cell's first texel and the
|
|
43
|
+
* fraction across it, `[x, y, fx, fy]`.
|
|
44
|
+
*/
|
|
45
|
+
export function lateDepthCell(
|
|
46
|
+
outX: number,
|
|
47
|
+
outY: number,
|
|
48
|
+
renderWidth: number,
|
|
49
|
+
renderHeight: number,
|
|
50
|
+
outputWidth: number,
|
|
51
|
+
outputHeight: number,
|
|
52
|
+
jitterX: number,
|
|
53
|
+
jitterY: number,
|
|
54
|
+
out: Float64Array | Float32Array,
|
|
55
|
+
): void {
|
|
56
|
+
/* A texel stands at its index plus a half less the jitter, as the resolve places it. */
|
|
57
|
+
const x = (outX * renderWidth) / outputWidth - 0.5 + jitterX;
|
|
58
|
+
const y = (outY * renderHeight) / outputHeight - 0.5 + jitterY;
|
|
59
|
+
out[0] = Math.floor(x);
|
|
60
|
+
out[1] = Math.floor(y);
|
|
61
|
+
out[2] = x - Math.floor(x);
|
|
62
|
+
out[3] = y - Math.floor(y);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The depth to test against, from `taps` — a 4x4 block of render texels, row-major, starting one
|
|
67
|
+
* texel up and left of the cell's first — at (`fx`, `fy`) across the cell.
|
|
68
|
+
*/
|
|
69
|
+
export function lateDepthAt(taps: ArrayLike<number>, fx: number, fy: number): number {
|
|
70
|
+
const t = (x: number, y: number): number => taps[(y + 1) * 4 + x + 1] as number;
|
|
71
|
+
let depth = REVERSED_DEPTH ? -Infinity : Infinity;
|
|
72
|
+
for (let oy = 0; oy < 2; oy += 1) {
|
|
73
|
+
for (let ox = 0; ox < 2; ox += 1) {
|
|
74
|
+
const d = t(ox, oy);
|
|
75
|
+
const gx = minmod(t(ox + 1, oy) - d, d - t(ox - 1, oy));
|
|
76
|
+
const gy = minmod(t(ox, oy + 1) - d, d - t(ox, oy - 1));
|
|
77
|
+
const carried = d + gx * (fx - ox) + gy * (fy - oy);
|
|
78
|
+
const give = TOLERANCE_SHARE * Math.max(Math.abs(gx), Math.abs(gy));
|
|
79
|
+
depth = REVERSED_DEPTH ? Math.max(depth, carried - give) : Math.min(depth, carried + give);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return depth;
|
|
83
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two decisions about one draw in a reconstructing frame, and why they are the same set.
|
|
3
|
+
*
|
|
4
|
+
* A blended draw goes **late** — after the reconstruction, at output resolution, unjittered — and a
|
|
5
|
+
* mover's motion is **tracked**, only for draws that land in the picture being reconstructed. A
|
|
6
|
+
* planar mirror and a probe bake are other pictures drawn through the same verbs, and the overlay
|
|
7
|
+
* after `endFrame` is past the picture altogether; routing any of them late, or counting a mirrored
|
|
8
|
+
* draw as a mover's second draw of the frame, would be wrong in the way that reads as a ghost.
|
|
9
|
+
*
|
|
10
|
+
* Plain booleans rather than a state object, so the per-draw call allocates nothing.
|
|
11
|
+
*/
|
|
12
|
+
export function routesLate(
|
|
13
|
+
reconstructing: boolean,
|
|
14
|
+
mirror: boolean,
|
|
15
|
+
probe: boolean,
|
|
16
|
+
presented: boolean,
|
|
17
|
+
): boolean {
|
|
18
|
+
return reconstructing && !mirror && !probe && !presented;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The same set as `routesLate`, named for the other reader. */
|
|
22
|
+
export function tracksMotion(
|
|
23
|
+
reconstructing: boolean,
|
|
24
|
+
mirror: boolean,
|
|
25
|
+
probe: boolean,
|
|
26
|
+
presented: boolean,
|
|
27
|
+
): boolean {
|
|
28
|
+
return routesLate(reconstructing, mirror, probe, presented);
|
|
29
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which object a draw is, so a reconstruction can find where that object was last frame.
|
|
3
|
+
*
|
|
4
|
+
* **The engine cannot infer this and does not try.** `drawMesh` is immediate: a mesh drawn three
|
|
5
|
+
* times is three objects, and nothing in the call says which of last frame's three each one is. A
|
|
6
|
+
* consumer that makes one `Mover` per moving object and passes it with every draw of that object
|
|
7
|
+
* says exactly that, and the renderer keeps the rest — last frame's model and, for a skinned
|
|
8
|
+
* draw, last frame's palette.
|
|
9
|
+
*
|
|
10
|
+
* **Plain data, advanced by the renderer.** No renderer is needed to make one, so a consumer holds
|
|
11
|
+
* it beside the object it belongs to. Only a reconstructing WebGPU renderer reads or writes it; it
|
|
12
|
+
* costs nothing anywhere else. The fields past `kind` are the renderer's and a caller leaves them
|
|
13
|
+
* alone.
|
|
14
|
+
*
|
|
15
|
+
* **What would make this wrong** is a consumer whose objects are rebuilt every frame with no state
|
|
16
|
+
* of their own to hold a mover in; the identity then has to live in whatever *does* persist.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const MOVER_KIND = 'drift.mover' as const;
|
|
20
|
+
|
|
21
|
+
export interface Mover {
|
|
22
|
+
readonly kind: typeof MOVER_KIND;
|
|
23
|
+
/** The renderer that last drew it, from `claimRendererId`; -1 before its first draw. */
|
|
24
|
+
renderer: number;
|
|
25
|
+
/** That renderer's frame serial at the last draw. */
|
|
26
|
+
frame: number;
|
|
27
|
+
readonly model: Float32Array;
|
|
28
|
+
readonly previousModel: Float32Array;
|
|
29
|
+
/**
|
|
30
|
+
* This frame's palette, exactly `paletteLength` floats long once a skinned draw has set it.
|
|
31
|
+
* Swapped with the previous one each frame and reallocated only when the rig changes.
|
|
32
|
+
*/
|
|
33
|
+
palette: Float32Array;
|
|
34
|
+
previousPalette: Float32Array;
|
|
35
|
+
paletteLength: number;
|
|
36
|
+
previousPaletteLength: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Nothing to reproject from: first draw, a missed frame, another renderer, or a changed rig. */
|
|
40
|
+
export const MOVER_NONE = 0;
|
|
41
|
+
/** Drawn on the frame before by this renderer: `previousModel` and `previousPalette` are real. */
|
|
42
|
+
export const MOVER_MOTION = 1;
|
|
43
|
+
/** Drawn already this frame. The second draw is a different object and the caller's error. */
|
|
44
|
+
export const MOVER_TWICE = 2;
|
|
45
|
+
export type MoverVerdict = typeof MOVER_NONE | typeof MOVER_MOTION | typeof MOVER_TWICE;
|
|
46
|
+
|
|
47
|
+
let nextRendererId = 1;
|
|
48
|
+
|
|
49
|
+
/** A number no other renderer in this page holds, so a mover can tell renderers apart. */
|
|
50
|
+
export function claimRendererId(): number {
|
|
51
|
+
const id = nextRendererId;
|
|
52
|
+
nextRendererId += 1;
|
|
53
|
+
return id;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createMover(): Mover {
|
|
57
|
+
return {
|
|
58
|
+
kind: MOVER_KIND,
|
|
59
|
+
renderer: -1,
|
|
60
|
+
frame: -2,
|
|
61
|
+
model: new Float32Array(16),
|
|
62
|
+
previousModel: new Float32Array(16),
|
|
63
|
+
palette: new Float32Array(0),
|
|
64
|
+
previousPalette: new Float32Array(0),
|
|
65
|
+
paletteLength: 0,
|
|
66
|
+
previousPaletteLength: 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function isMover(value: unknown): value is Mover {
|
|
71
|
+
return (
|
|
72
|
+
typeof value === 'object' && value !== null && (value as { kind?: unknown }).kind === MOVER_KIND
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Advance a mover to this draw, and say whether last frame's state is usable.
|
|
78
|
+
*
|
|
79
|
+
* **A second draw in one frame changes nothing**, so the first draw's motion stands; the caller
|
|
80
|
+
* warns. Otherwise what the mover held becomes its previous and this draw becomes its current —
|
|
81
|
+
* the palettes by swapping two arrays, so a steady rig allocates nothing after its first frame.
|
|
82
|
+
*
|
|
83
|
+
* **A changed joint count is no motion**: skinning a vertex with a palette of a different rig is
|
|
84
|
+
* not an old pose of this one.
|
|
85
|
+
*/
|
|
86
|
+
export function advanceMover(
|
|
87
|
+
mover: Mover,
|
|
88
|
+
renderer: number,
|
|
89
|
+
frame: number,
|
|
90
|
+
model: ArrayLike<number>,
|
|
91
|
+
palette: Float32Array | null,
|
|
92
|
+
): MoverVerdict {
|
|
93
|
+
if (mover.renderer === renderer && mover.frame === frame) return MOVER_TWICE;
|
|
94
|
+
const drawnLast = mover.renderer === renderer && mover.frame === frame - 1;
|
|
95
|
+
|
|
96
|
+
mover.previousModel.set(mover.model);
|
|
97
|
+
const spare = mover.previousPalette;
|
|
98
|
+
mover.previousPalette = mover.palette;
|
|
99
|
+
mover.palette = spare;
|
|
100
|
+
mover.previousPaletteLength = mover.paletteLength;
|
|
101
|
+
|
|
102
|
+
mover.model.set(model);
|
|
103
|
+
if (palette === null) {
|
|
104
|
+
mover.paletteLength = 0;
|
|
105
|
+
} else {
|
|
106
|
+
/* Exactly the rig's length, because the array is uploaded as it stands; reallocated only when
|
|
107
|
+
the rig changes, which a steady character never does. */
|
|
108
|
+
if (mover.palette.length !== palette.length) mover.palette = new Float32Array(palette.length);
|
|
109
|
+
mover.palette.set(palette);
|
|
110
|
+
mover.paletteLength = palette.length;
|
|
111
|
+
}
|
|
112
|
+
mover.renderer = renderer;
|
|
113
|
+
mover.frame = frame;
|
|
114
|
+
|
|
115
|
+
if (!drawnLast) return MOVER_NONE;
|
|
116
|
+
if (mover.paletteLength !== mover.previousPaletteLength) return MOVER_NONE;
|
|
117
|
+
return MOVER_MOTION;
|
|
118
|
+
}
|