@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
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* A GPU image the caller supplies, sampled by the flat shader as surface colour — or several, as
|
|
3
|
+
* the layers of one array.
|
|
4
|
+
*
|
|
5
|
+
* **Every surface texture is an array; a plain image is an array of one.** The lit pass has three of
|
|
6
|
+
* WebGL2's guaranteed sixteen texture units free, so a second set of array samplers beside the 2D
|
|
7
|
+
* ones was never affordable. Making the one set of samplers arrays costs no unit, and a one-layer
|
|
8
|
+
* array samples exactly as a 2D texture does. A mesh names its layer per vertex (`MeshData.layers`),
|
|
9
|
+
* so a merged block wearing forty facades is one draw.
|
|
10
|
+
*/
|
|
11
|
+
import { isSourceList, layerSize, refuseArrayUpdate, sourceSize } from './textureSource.js';
|
|
12
|
+
import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.js';
|
|
2
13
|
const DEFAULT_ANISOTROPY = 4;
|
|
3
14
|
/**
|
|
4
15
|
* An image uploaded once and bound per draw.
|
|
@@ -14,7 +25,18 @@ export class SurfaceTexture {
|
|
|
14
25
|
mipmapped;
|
|
15
26
|
/** Whether the sampler decodes sRGB on fetch. See `colorSpace`. */
|
|
16
27
|
srgb;
|
|
28
|
+
/** How many images the array holds: 1 for `createSurfaceTexture`, the list's length otherwise. */
|
|
29
|
+
layers;
|
|
30
|
+
/** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
|
|
31
|
+
effectsTable = null;
|
|
32
|
+
/**
|
|
33
|
+
* One image, or an array of images that must share one size (`layerSize` refuses otherwise).
|
|
34
|
+
* Uploaded unflipped either way; see the constructor body for why.
|
|
35
|
+
*/
|
|
17
36
|
constructor(gl, source, options = {}) {
|
|
37
|
+
const sources = isSourceList(source) ? source : [source];
|
|
38
|
+
const { width, height } = layerSize(sources);
|
|
39
|
+
this.layers = sources.length;
|
|
18
40
|
const texture = gl.createTexture();
|
|
19
41
|
if (texture === null)
|
|
20
42
|
throw new Error('SurfaceTexture: createTexture failed');
|
|
@@ -22,7 +44,7 @@ export class SurfaceTexture {
|
|
|
22
44
|
this.mipmapped = options.mipmap ?? true;
|
|
23
45
|
this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
24
46
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
25
|
-
gl.bindTexture(gl.
|
|
47
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
|
|
26
48
|
/*
|
|
27
49
|
* **Not flipped, and this used to be, which is the bug it is written down for.**
|
|
28
50
|
*
|
|
@@ -44,15 +66,20 @@ export class SurfaceTexture {
|
|
|
44
66
|
*
|
|
45
67
|
* A caller that was mirroring its own canvas to cancel this must stop.
|
|
46
68
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
gl.
|
|
69
|
+
/* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
|
|
70
|
+
new size, as texImage2D always could. Each layer is then written in place. */
|
|
71
|
+
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA, width, height, sources.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
72
|
+
for (let layer = 0; layer < sources.length; layer++) {
|
|
73
|
+
gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, width, height, 1, gl.RGBA, gl.UNSIGNED_BYTE, sources[layer]);
|
|
74
|
+
}
|
|
75
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
|
|
76
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
|
|
50
77
|
/* Nearest where the caller said their pixels are the subject; see `filter`. The mip chain is
|
|
51
78
|
still blended between levels, because that is minification and this option is about
|
|
52
79
|
magnification. */
|
|
53
80
|
const nearest = options.filter === 'nearest';
|
|
54
|
-
gl.texParameteri(gl.
|
|
55
|
-
gl.texParameteri(gl.
|
|
81
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
|
|
82
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, this.mipmapped
|
|
56
83
|
? nearest
|
|
57
84
|
? gl.NEAREST_MIPMAP_LINEAR
|
|
58
85
|
: gl.LINEAR_MIPMAP_LINEAR
|
|
@@ -60,9 +87,30 @@ export class SurfaceTexture {
|
|
|
60
87
|
? gl.NEAREST
|
|
61
88
|
: gl.LINEAR);
|
|
62
89
|
if (this.mipmapped)
|
|
63
|
-
gl.generateMipmap(gl.
|
|
90
|
+
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
64
91
|
applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
|
|
65
|
-
gl.bindTexture(gl.
|
|
92
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
93
|
+
/* Half floats, nearest, clamped: a table read by texelFetch, whose every whole number is under
|
|
94
|
+
what a half holds exactly. */
|
|
95
|
+
const effects = options.effects ?? [];
|
|
96
|
+
if (effects.length > 0) {
|
|
97
|
+
const table = gl.createTexture();
|
|
98
|
+
if (table === null)
|
|
99
|
+
throw new Error('SurfaceTexture: createTexture failed');
|
|
100
|
+
gl.bindTexture(gl.TEXTURE_2D, table);
|
|
101
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, SURFACE_EFFECT_TEXELS, effects.length, 0, gl.RGBA, gl.FLOAT, packSurfaceEffects(effects));
|
|
102
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
103
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
104
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
105
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
106
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
107
|
+
this.effectsTable = table;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Bind the effects table to `unit`, or `fallback` for a texture given none. */
|
|
111
|
+
bindEffects(gl, unit, fallback) {
|
|
112
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
113
|
+
gl.bindTexture(gl.TEXTURE_2D, this.effectsTable ?? fallback);
|
|
66
114
|
}
|
|
67
115
|
/**
|
|
68
116
|
* Replace the pixels, keeping the GPU object and its sampler state.
|
|
@@ -78,23 +126,28 @@ export class SurfaceTexture {
|
|
|
78
126
|
update(gl, source) {
|
|
79
127
|
if (this.texture === null)
|
|
80
128
|
return;
|
|
81
|
-
|
|
129
|
+
refuseArrayUpdate(this.layers);
|
|
130
|
+
const { width, height } = sourceSize(source);
|
|
131
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
82
132
|
/* Unflipped, matching the constructor. See it for why, and for what it cost. */
|
|
83
|
-
gl.
|
|
133
|
+
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA, width, height, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
84
134
|
if (this.mipmapped)
|
|
85
|
-
gl.generateMipmap(gl.
|
|
86
|
-
gl.bindTexture(gl.
|
|
135
|
+
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
136
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
87
137
|
}
|
|
88
138
|
/** Bind to a unit for sampling. Engine-internal: the renderer owns unit assignment. */
|
|
89
139
|
bind(gl, unit) {
|
|
90
140
|
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
91
|
-
gl.bindTexture(gl.
|
|
141
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
92
142
|
}
|
|
93
143
|
dispose(gl) {
|
|
94
144
|
if (this.texture === null)
|
|
95
145
|
return;
|
|
96
146
|
gl.deleteTexture(this.texture);
|
|
97
147
|
this.texture = null;
|
|
148
|
+
if (this.effectsTable !== null)
|
|
149
|
+
gl.deleteTexture(this.effectsTable);
|
|
150
|
+
this.effectsTable = null;
|
|
98
151
|
}
|
|
99
152
|
}
|
|
100
153
|
/**
|
|
@@ -112,5 +165,5 @@ function applyAnisotropy(gl, requested) {
|
|
|
112
165
|
if (ext === null)
|
|
113
166
|
return;
|
|
114
167
|
const max = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
|
|
115
|
-
gl.texParameterf(gl.
|
|
168
|
+
gl.texParameterf(gl.TEXTURE_2D_ARRAY, ext.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(requested, max));
|
|
116
169
|
}
|
|
@@ -123,6 +123,24 @@ export declare const FLICKER_MEMORY: number;
|
|
|
123
123
|
* Threads caught in a share nearer a half spread more and need fewer.
|
|
124
124
|
*/
|
|
125
125
|
export declare const FLICKER_WIDTH = 6;
|
|
126
|
+
/**
|
|
127
|
+
* The two numbers the proof takes from the jitter's period: how much of a period one frame is, and
|
|
128
|
+
* how many spreads a proven pixel's box widens by. This resolve's are `FLICKER_MEMORY` and
|
|
129
|
+
* `FLICKER_WIDTH`; a reconstruction jitters along a longer sequence and needs its own.
|
|
130
|
+
*/
|
|
131
|
+
export interface FlickerRule {
|
|
132
|
+
readonly memory: number;
|
|
133
|
+
readonly width: number;
|
|
134
|
+
}
|
|
135
|
+
/** The temporal resolve's own: eight phases. */
|
|
136
|
+
export declare const TEMPORAL_FLICKER: FlickerRule;
|
|
137
|
+
/**
|
|
138
|
+
* The rule for a jitter of `phases` positions. **The width keeps the room `FLICKER_WIDTH` leaves over
|
|
139
|
+
* what eight phases need** — 4.8 — in proportion: a longer period wears the spread down further
|
|
140
|
+
* between catches, so over eighteen phases the hardest thread needs eleven spreads, and a box that
|
|
141
|
+
* reached six reset the count of exactly the pixels this is for.
|
|
142
|
+
*/
|
|
143
|
+
export declare function flickerRule(phases: number): FlickerRule;
|
|
126
144
|
/**
|
|
127
145
|
* How far apart two periods' means may be, relative to the larger, and still repeat: fully below
|
|
128
146
|
* this, not at all at twice it. Above an eight-bit record's rounding and a lamp's slow breathing; far
|
|
@@ -192,7 +210,7 @@ export interface Flicker {
|
|
|
192
210
|
* record of another surface is no record**, so the resolve starts one over wherever last frame's
|
|
193
211
|
* depth says the surface is not the one it followed, before this frame widens by anything.
|
|
194
212
|
*/
|
|
195
|
-
export declare function freshFlicker(out: Flicker, sample: number): Flicker;
|
|
213
|
+
export declare function freshFlicker(out: Flicker, sample: number, rule?: FlickerRule): Flicker;
|
|
196
214
|
/** Rec. 709 luma, which is what the record measures in. */
|
|
197
215
|
export declare function luma(c: readonly [number, number, number]): number;
|
|
198
216
|
/** One for a pixel whose reprojection did not move, falling to none at a twentieth of a pixel. */
|
|
@@ -202,20 +220,21 @@ export declare function stillness(motionPixels: number): number;
|
|
|
202
220
|
* lands within the reach of the box it would widen to. A sample beyond that reach is something
|
|
203
221
|
* arriving, and it disqualifies the frame it lands in as well as the count it carries out.
|
|
204
222
|
*/
|
|
205
|
-
export declare function proven(record: Flicker, sample: number): boolean;
|
|
223
|
+
export declare function proven(record: Flicker, sample: number, rule?: FlickerRule): boolean;
|
|
206
224
|
/** How far a pixel's box widens this frame: by its spread, once proven, and only while still. */
|
|
207
|
-
export declare function flickerWidening(record: Flicker, sample: number, still: number): number;
|
|
225
|
+
export declare function flickerWidening(record: Flicker, sample: number, still: number, rule?: FlickerRule): number;
|
|
208
226
|
/** How much of the history this frame keeps: the base blend, or more once proven. */
|
|
209
|
-
export declare function flickerBlend(base: number, record: Flicker, sample: number, still: number): number;
|
|
227
|
+
export declare function flickerBlend(base: number, record: Flicker, sample: number, still: number, rule?: FlickerRule): number;
|
|
210
228
|
/** Whether two periods' means are the same, one to zero. */
|
|
211
229
|
export declare function repeats(mean: number, previous: number): number;
|
|
212
230
|
/**
|
|
213
231
|
* The record carried out, from the one carried in and this frame's sample in luma. `periodStart`
|
|
214
232
|
* is the first frame of the jitter's period, where the period just gathered is compared with the
|
|
215
233
|
* one before. Forgotten in proportion to motion — a moving pixel's record would be a record of
|
|
216
|
-
* somewhere else.
|
|
234
|
+
* somewhere else. `rule` is this resolve's eight phases unless a caller jitters along another
|
|
235
|
+
* sequence, as a reconstruction does.
|
|
217
236
|
*/
|
|
218
|
-
export declare function nextFlicker(out: Flicker, previous: Flicker, sample: number, still: number, periodStart: boolean): Flicker;
|
|
237
|
+
export declare function nextFlicker(out: Flicker, previous: Flicker, sample: number, still: number, periodStart: boolean, rule?: FlickerRule): Flicker;
|
|
219
238
|
/**
|
|
220
239
|
* Whether the last frame left a picture this one may sample, and where the jitter has got to.
|
|
221
240
|
*
|
|
@@ -176,6 +176,35 @@ export const FLICKER_MEMORY = 1 / JITTER_PERIOD;
|
|
|
176
176
|
* Threads caught in a share nearer a half spread more and need fewer.
|
|
177
177
|
*/
|
|
178
178
|
export const FLICKER_WIDTH = 6;
|
|
179
|
+
/** The temporal resolve's own: eight phases. */
|
|
180
|
+
export const TEMPORAL_FLICKER = { memory: FLICKER_MEMORY, width: FLICKER_WIDTH };
|
|
181
|
+
/**
|
|
182
|
+
* The running spread of the hardest thread — one caught in a single phase of `phases` — just before
|
|
183
|
+
* its catch, as a share of its contrast: the steady state `FLICKER_WIDTH`'s comment solves for eight
|
|
184
|
+
* phases, solved for any. With m one over the period and r = (1 − m)^(p − 1), the seven, or p − 1,
|
|
185
|
+
* misses that wear it down: a = (m + ((p − 1)m² − m)·r) / (1 − (1 − m)·r).
|
|
186
|
+
*/
|
|
187
|
+
function hardestSpread(phases) {
|
|
188
|
+
const m = 1 / phases;
|
|
189
|
+
const r = (1 - m) ** (phases - 1);
|
|
190
|
+
return (m + ((phases - 1) * m * m - m) * r) / (1 - (1 - m) * r);
|
|
191
|
+
}
|
|
192
|
+
/** How many of those spreads the hardest thread's catch lands from its period's mean. */
|
|
193
|
+
function widthNeeded(phases) {
|
|
194
|
+
return (1 - 1 / phases) / hardestSpread(phases);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The rule for a jitter of `phases` positions. **The width keeps the room `FLICKER_WIDTH` leaves over
|
|
198
|
+
* what eight phases need** — 4.8 — in proportion: a longer period wears the spread down further
|
|
199
|
+
* between catches, so over eighteen phases the hardest thread needs eleven spreads, and a box that
|
|
200
|
+
* reached six reset the count of exactly the pixels this is for.
|
|
201
|
+
*/
|
|
202
|
+
export function flickerRule(phases) {
|
|
203
|
+
return {
|
|
204
|
+
memory: 1 / phases,
|
|
205
|
+
width: (FLICKER_WIDTH * widthNeeded(phases)) / widthNeeded(JITTER_PERIOD),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
179
208
|
/**
|
|
180
209
|
* How far apart two periods' means may be, relative to the larger, and still repeat: fully below
|
|
181
210
|
* this, not at all at twice it. Above an eight-bit record's rounding and a lamp's slow breathing; far
|
|
@@ -246,9 +275,9 @@ export const STILL_TO = 0.05;
|
|
|
246
275
|
* record of another surface is no record**, so the resolve starts one over wherever last frame's
|
|
247
276
|
* depth says the surface is not the one it followed, before this frame widens by anything.
|
|
248
277
|
*/
|
|
249
|
-
export function freshFlicker(out, sample) {
|
|
278
|
+
export function freshFlicker(out, sample, rule = TEMPORAL_FLICKER) {
|
|
250
279
|
out.spread = 0;
|
|
251
|
-
out.sum = sample *
|
|
280
|
+
out.sum = sample * rule.memory;
|
|
252
281
|
out.mean = sample;
|
|
253
282
|
out.repeated = 0;
|
|
254
283
|
return out;
|
|
@@ -270,20 +299,20 @@ export function stillness(motionPixels) {
|
|
|
270
299
|
* lands within the reach of the box it would widen to. A sample beyond that reach is something
|
|
271
300
|
* arriving, and it disqualifies the frame it lands in as well as the count it carries out.
|
|
272
301
|
*/
|
|
273
|
-
export function proven(record, sample) {
|
|
274
|
-
return record.repeated > 0.75 && within(record, sample);
|
|
302
|
+
export function proven(record, sample, rule = TEMPORAL_FLICKER) {
|
|
303
|
+
return record.repeated > 0.75 && within(record, sample, rule);
|
|
275
304
|
}
|
|
276
305
|
/** Whether this frame's sample lands inside the reach of the box the record would widen to. */
|
|
277
|
-
function within(record, sample) {
|
|
278
|
-
return Math.abs(sample - record.mean) <=
|
|
306
|
+
function within(record, sample, rule) {
|
|
307
|
+
return Math.abs(sample - record.mean) <= rule.width * record.spread + 1 / 255;
|
|
279
308
|
}
|
|
280
309
|
/** How far a pixel's box widens this frame: by its spread, once proven, and only while still. */
|
|
281
|
-
export function flickerWidening(record, sample, still) {
|
|
282
|
-
return proven(record, sample) ?
|
|
310
|
+
export function flickerWidening(record, sample, still, rule = TEMPORAL_FLICKER) {
|
|
311
|
+
return proven(record, sample, rule) ? rule.width * record.spread * still : 0;
|
|
283
312
|
}
|
|
284
313
|
/** How much of the history this frame keeps: the base blend, or more once proven. */
|
|
285
|
-
export function flickerBlend(base, record, sample, still) {
|
|
286
|
-
return proven(record, sample) ? base + (FLICKER_BLEND - base) * still : base;
|
|
314
|
+
export function flickerBlend(base, record, sample, still, rule = TEMPORAL_FLICKER) {
|
|
315
|
+
return proven(record, sample, rule) ? base + (FLICKER_BLEND - base) * still : base;
|
|
287
316
|
}
|
|
288
317
|
/** Whether two periods' means are the same, one to zero. */
|
|
289
318
|
export function repeats(mean, previous) {
|
|
@@ -294,20 +323,21 @@ export function repeats(mean, previous) {
|
|
|
294
323
|
* The record carried out, from the one carried in and this frame's sample in luma. `periodStart`
|
|
295
324
|
* is the first frame of the jitter's period, where the period just gathered is compared with the
|
|
296
325
|
* one before. Forgotten in proportion to motion — a moving pixel's record would be a record of
|
|
297
|
-
* somewhere else.
|
|
326
|
+
* somewhere else. `rule` is this resolve's eight phases unless a caller jitters along another
|
|
327
|
+
* sequence, as a reconstruction does.
|
|
298
328
|
*/
|
|
299
|
-
export function nextFlicker(out, previous, sample, still, periodStart) {
|
|
329
|
+
export function nextFlicker(out, previous, sample, still, periodStart, rule = TEMPORAL_FLICKER) {
|
|
300
330
|
const apart = Math.abs(sample - previous.mean);
|
|
301
331
|
let { sum, mean, repeated } = previous;
|
|
302
|
-
if (apart >
|
|
332
|
+
if (apart > rule.width * previous.spread + 1 / 255)
|
|
303
333
|
repeated = 0;
|
|
304
334
|
if (periodStart) {
|
|
305
335
|
repeated = repeats(sum, mean) > 0.5 ? Math.min(1, repeated + 0.5) : 0;
|
|
306
336
|
mean = sum;
|
|
307
337
|
sum = 0;
|
|
308
338
|
}
|
|
309
|
-
out.spread = previous.spread + (apart - previous.spread) *
|
|
310
|
-
out.sum = sum + sample *
|
|
339
|
+
out.spread = previous.spread + (apart - previous.spread) * rule.memory;
|
|
340
|
+
out.sum = sum + sample * rule.memory;
|
|
311
341
|
out.mean = mean;
|
|
312
342
|
out.repeated = repeated * still;
|
|
313
343
|
return out;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The size of an image a caller hands over, and the one rule a texture array adds to it: every
|
|
3
|
+
* layer the same size.
|
|
4
|
+
*
|
|
5
|
+
* Backend-neutral, because both backends upload the same `TexImageSource` and must agree on its
|
|
6
|
+
* size, and on refusing an array whose layers disagree, before either touches a device.
|
|
7
|
+
*/
|
|
8
|
+
export interface SourceSize {
|
|
9
|
+
readonly width: number;
|
|
10
|
+
readonly height: number;
|
|
11
|
+
}
|
|
12
|
+
/** Width and height of any `TexImageSource`: a video by its frame, everything else by its own size. */
|
|
13
|
+
export declare function sourceSize(source: TexImageSource): SourceSize;
|
|
14
|
+
/**
|
|
15
|
+
* The one size every layer of an array shares, or a refusal naming the first that differs.
|
|
16
|
+
*
|
|
17
|
+
* **Refused rather than resized**, because an array layer has no size of its own — the device
|
|
18
|
+
* allocates one size for all of them — and quietly scaling a 128-pixel sign up to a 512 array, or a
|
|
19
|
+
* facade down, is a decision about quality the caller should make where they can see it. Sorting
|
|
20
|
+
* images into arrays by size is theirs; this makes sure they did.
|
|
21
|
+
*/
|
|
22
|
+
export declare function layerSize(sources: readonly TexImageSource[]): SourceSize;
|
|
23
|
+
/** Whether a constructor argument is a list of images rather than one. */
|
|
24
|
+
export declare function isSourceList(source: TexImageSource | readonly TexImageSource[]): source is readonly TexImageSource[];
|
|
25
|
+
/**
|
|
26
|
+
* `update` replaces the one image of a plain texture. An array's layers are its identity — a
|
|
27
|
+
* block's facades by index — and replacing "the image" of a forty-layer array has no single
|
|
28
|
+
* meaning, so it is refused rather than guessed at.
|
|
29
|
+
*/
|
|
30
|
+
export declare function refuseArrayUpdate(layers: number): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The size of an image a caller hands over, and the one rule a texture array adds to it: every
|
|
3
|
+
* layer the same size.
|
|
4
|
+
*
|
|
5
|
+
* Backend-neutral, because both backends upload the same `TexImageSource` and must agree on its
|
|
6
|
+
* size, and on refusing an array whose layers disagree, before either touches a device.
|
|
7
|
+
*/
|
|
8
|
+
/** Width and height of any `TexImageSource`: a video by its frame, everything else by its own size. */
|
|
9
|
+
export function sourceSize(source) {
|
|
10
|
+
const candidate = source;
|
|
11
|
+
const width = candidate.videoWidth ?? candidate.width ?? 0;
|
|
12
|
+
const height = candidate.videoHeight ?? candidate.height ?? 0;
|
|
13
|
+
return { width: Math.max(1, width), height: Math.max(1, height) };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The one size every layer of an array shares, or a refusal naming the first that differs.
|
|
17
|
+
*
|
|
18
|
+
* **Refused rather than resized**, because an array layer has no size of its own — the device
|
|
19
|
+
* allocates one size for all of them — and quietly scaling a 128-pixel sign up to a 512 array, or a
|
|
20
|
+
* facade down, is a decision about quality the caller should make where they can see it. Sorting
|
|
21
|
+
* images into arrays by size is theirs; this makes sure they did.
|
|
22
|
+
*/
|
|
23
|
+
export function layerSize(sources) {
|
|
24
|
+
const first = sources[0];
|
|
25
|
+
if (first === undefined) {
|
|
26
|
+
throw new Error('createSurfaceTextureArray: an array needs at least one image');
|
|
27
|
+
}
|
|
28
|
+
const size = sourceSize(first);
|
|
29
|
+
for (let layer = 1; layer < sources.length; layer++) {
|
|
30
|
+
const next = sourceSize(sources[layer]);
|
|
31
|
+
if (next.width !== size.width || next.height !== size.height) {
|
|
32
|
+
throw new Error(`createSurfaceTextureArray: layer ${layer} is ${next.width}×${next.height} and layer 0 is ` +
|
|
33
|
+
`${size.width}×${size.height}. Every layer of an array is one size on the device; group ` +
|
|
34
|
+
'images into arrays by size, or scale them to one, before building the array.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return size;
|
|
38
|
+
}
|
|
39
|
+
/** Whether a constructor argument is a list of images rather than one. */
|
|
40
|
+
export function isSourceList(source) {
|
|
41
|
+
return Array.isArray(source);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* `update` replaces the one image of a plain texture. An array's layers are its identity — a
|
|
45
|
+
* block's facades by index — and replacing "the image" of a forty-layer array has no single
|
|
46
|
+
* meaning, so it is refused rather than guessed at.
|
|
47
|
+
*/
|
|
48
|
+
export function refuseArrayUpdate(layers) {
|
|
49
|
+
if (layers === 1)
|
|
50
|
+
return;
|
|
51
|
+
throw new Error(`updateSurfaceTexture: this texture is an array of ${layers} layers, and update replaces the ` +
|
|
52
|
+
'one image of a plain texture. Build a new array for new layers.');
|
|
53
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReadonlyMat4 } from 'gl-matrix';
|
|
2
2
|
import type { TranslucentMeshOptions } from './backend/webgl2/renderer.ts';
|
|
3
|
+
import type { ResolvedGlass } from './glass.ts';
|
|
3
4
|
/**
|
|
4
5
|
* The frame's translucent draws, held so they can be submitted more than once.
|
|
5
6
|
*
|
|
@@ -46,11 +47,14 @@ export interface TranslucentDraw {
|
|
|
46
47
|
/** What survives one metre of the medium. Null where the draw named none; otherwise a copy. */
|
|
47
48
|
refractTint: Float32Array | null;
|
|
48
49
|
thicknessM: number;
|
|
50
|
+
/** Glass, resolved, or null for a draw that is not glass. See `glass.ts`. */
|
|
51
|
+
glass: ResolvedGlass | null;
|
|
49
52
|
}
|
|
50
53
|
export declare class TranslucentQueue {
|
|
51
54
|
private readonly pool;
|
|
52
55
|
private count;
|
|
53
56
|
private refracting;
|
|
57
|
+
private glassing;
|
|
54
58
|
/** How many draws this frame has recorded. */
|
|
55
59
|
get length(): number;
|
|
56
60
|
/**
|
|
@@ -58,6 +62,8 @@ export declare class TranslucentQueue {
|
|
|
58
62
|
* frame behind it first, and does not copy it for a set that never reads it.
|
|
59
63
|
*/
|
|
60
64
|
get refracts(): boolean;
|
|
65
|
+
/** Whether anything recorded this frame is glass, so the copy it reads needs its mip chain. */
|
|
66
|
+
get glassy(): boolean;
|
|
61
67
|
/** How many records are pooled, which a test uses to assert a steady scene stops allocating. */
|
|
62
68
|
get capacity(): number;
|
|
63
69
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveGlass } from './glass.js';
|
|
1
2
|
function newRecord() {
|
|
2
3
|
return {
|
|
3
4
|
mesh: null,
|
|
@@ -14,12 +15,15 @@ function newRecord() {
|
|
|
14
15
|
refractTint: null,
|
|
15
16
|
thicknessM: 0,
|
|
16
17
|
refractTintStore: new Float32Array(3),
|
|
18
|
+
glass: null,
|
|
19
|
+
glassStore: { transmission: 0, frost: 0, tint: [1, 1, 1] },
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
22
|
export class TranslucentQueue {
|
|
20
23
|
pool = [];
|
|
21
24
|
count = 0;
|
|
22
25
|
refracting = 0;
|
|
26
|
+
glassing = 0;
|
|
23
27
|
/** How many draws this frame has recorded. */
|
|
24
28
|
get length() {
|
|
25
29
|
return this.count;
|
|
@@ -31,6 +35,10 @@ export class TranslucentQueue {
|
|
|
31
35
|
get refracts() {
|
|
32
36
|
return this.refracting > 0;
|
|
33
37
|
}
|
|
38
|
+
/** Whether anything recorded this frame is glass, so the copy it reads needs its mip chain. */
|
|
39
|
+
get glassy() {
|
|
40
|
+
return this.glassing > 0;
|
|
41
|
+
}
|
|
34
42
|
/** How many records are pooled, which a test uses to assert a steady scene stops allocating. */
|
|
35
43
|
get capacity() {
|
|
36
44
|
return this.pool.length;
|
|
@@ -65,7 +73,12 @@ export class TranslucentQueue {
|
|
|
65
73
|
record.tint = record.tintStore;
|
|
66
74
|
}
|
|
67
75
|
record.refraction = options.refraction ?? 0;
|
|
68
|
-
|
|
76
|
+
/* Glass reads the copy too, whether or not it bends: see `glass.ts`. */
|
|
77
|
+
const glassy = resolveGlass(options.glass, record.glassStore);
|
|
78
|
+
record.glass = glassy ? record.glassStore : null;
|
|
79
|
+
if (glassy)
|
|
80
|
+
this.glassing++;
|
|
81
|
+
if (record.refraction > 0 || glassy)
|
|
69
82
|
this.refracting++;
|
|
70
83
|
record.thicknessM = options.thicknessM ?? 0;
|
|
71
84
|
const refractTint = options.refractTint ?? null;
|
|
@@ -97,5 +110,6 @@ export class TranslucentQueue {
|
|
|
97
110
|
reset() {
|
|
98
111
|
this.count = 0;
|
|
99
112
|
this.refracting = 0;
|
|
113
|
+
this.glassing = 0;
|
|
100
114
|
}
|
|
101
115
|
}
|
|
@@ -88,6 +88,8 @@ export const ABSENT_ATTRIBUTE = {
|
|
|
88
88
|
* the thing being measured.
|
|
89
89
|
*/
|
|
90
90
|
channel: [0, 1, 1, 1],
|
|
91
|
+
/* Layer 0: the only image a plain texture has, and the first of an array. */
|
|
92
|
+
layers: [0],
|
|
91
93
|
};
|
|
92
94
|
/**
|
|
93
95
|
* What `uOutputTransform` means, as the shader's own integers.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which level of detail each region of a world is drawn at, and the crossfade between two.
|
|
3
|
+
*
|
|
4
|
+
* **A region is a box and one geometric error per level**, finest first: how far, in metres, that
|
|
5
|
+
* level's surface may stand from the real one. A level is good enough once its error projects under
|
|
6
|
+
* `pixelTolerance` pixels, so the distance it takes over at is `error · projectionScale / tolerance`
|
|
7
|
+
* — the rule 3D Tiles uses, and the reason the file carries errors rather than distances: a
|
|
8
|
+
* distance is a decision about one screen, and an error is a fact about the geometry.
|
|
9
|
+
*
|
|
10
|
+
* **A band around every switch, so a still eye can never flip.** A region coarsens only once the
|
|
11
|
+
* eye is `1 + hysteresis` past the level's distance and refines only once it is `1 − hysteresis`
|
|
12
|
+
* inside it, so an eye standing on a threshold — or wobbling across it by less than the band —
|
|
13
|
+
* keeps the level it has. Distance is to the region's **box**, so the region under the eye is
|
|
14
|
+
* always at its finest, whatever its size.
|
|
15
|
+
*
|
|
16
|
+
* **A change crossfades rather than pops.** Each draw carries a dither for `setDitherFade`: the
|
|
17
|
+
* level arriving at `+t`, the one leaving at `−t`, which cover every pixel exactly once. `t` runs
|
|
18
|
+
* over `fadeSec` of the caller's `dtSec`, never a clock — so **a held clock never finishes a fade**,
|
|
19
|
+
* and a capture wants `fadeSec: 0`. A region nobody can see finishes any fade at once, and a region
|
|
20
|
+
* just added fades in from nothing.
|
|
21
|
+
*
|
|
22
|
+
* What it gives up: a region is one decision, so a long one seen end-on is drawn at the level its
|
|
23
|
+
* nearest corner asks for all the way along. Smaller regions are the lever.
|
|
24
|
+
*
|
|
25
|
+
* Nothing here allocates after construction except `add`, which is not per frame.
|
|
26
|
+
*/
|
|
27
|
+
import type { FrustumPlanes } from '../math/frustum.ts';
|
|
28
|
+
import type { BoxOccluder } from '../render/instanceCull.ts';
|
|
29
|
+
/** The level of a region drawn at none of its own: past `farDistance`, or not arrived yet. */
|
|
30
|
+
export declare const HLOD_NONE = 255;
|
|
31
|
+
export interface HlodOptions {
|
|
32
|
+
/** The most regions the set holds at once. */
|
|
33
|
+
readonly capacity: number;
|
|
34
|
+
/** The most levels a region may carry. 4 unless stated. */
|
|
35
|
+
readonly maxLevels?: number;
|
|
36
|
+
/** How many pixels of geometric error a level may show before a finer one is drawn. 1.5 unless stated. */
|
|
37
|
+
readonly pixelTolerance?: number;
|
|
38
|
+
/** The band around each switch, as a fraction of its distance. 0.15 unless stated. */
|
|
39
|
+
readonly hysteresis?: number;
|
|
40
|
+
/** Seconds a crossfade takes. 0.4 unless stated; 0 switches at once. */
|
|
41
|
+
readonly fadeSec?: number;
|
|
42
|
+
/** Beyond this, a region is drawn at no level at all. Unbounded unless stated. */
|
|
43
|
+
readonly farDistance?: number;
|
|
44
|
+
}
|
|
45
|
+
/** What `select` hands back: one entry per draw, a crossfading region taking two. */
|
|
46
|
+
export interface HlodDraws {
|
|
47
|
+
count: number;
|
|
48
|
+
/** The region's id, as it was added. */
|
|
49
|
+
readonly region: Uint32Array;
|
|
50
|
+
/** Which of its levels to draw. */
|
|
51
|
+
readonly level: Uint8Array;
|
|
52
|
+
/** What to pass `setDitherFade` for this draw: 0 whole, `+t` arriving, `−t` leaving. */
|
|
53
|
+
readonly dither: Float32Array;
|
|
54
|
+
}
|
|
55
|
+
/** Room for every region of a set of this capacity, each crossfading at once. */
|
|
56
|
+
export declare function createHlodDraws(capacity: number): HlodDraws;
|
|
57
|
+
/** Pixels a metre spans at one metre's distance: the screen's half of the switch distance. */
|
|
58
|
+
export declare function projectionScaleOf(fovYDeg: number, heightPx: number): number;
|
|
59
|
+
export declare class HlodSet {
|
|
60
|
+
readonly capacity: number;
|
|
61
|
+
readonly maxLevels: number;
|
|
62
|
+
private readonly tolerance;
|
|
63
|
+
private readonly band;
|
|
64
|
+
private readonly fadeSec;
|
|
65
|
+
private readonly far;
|
|
66
|
+
private readonly slots;
|
|
67
|
+
private readonly ids;
|
|
68
|
+
private readonly boxes;
|
|
69
|
+
private readonly errors;
|
|
70
|
+
private readonly levels;
|
|
71
|
+
/** The level each region is at, or arriving at. */
|
|
72
|
+
private readonly level;
|
|
73
|
+
/** The level a crossfade is leaving. */
|
|
74
|
+
private readonly from;
|
|
75
|
+
/** How far through its crossfade each region is; 1 is settled. */
|
|
76
|
+
private readonly fade;
|
|
77
|
+
private readonly box;
|
|
78
|
+
private count;
|
|
79
|
+
constructor(options: HlodOptions);
|
|
80
|
+
get size(): number;
|
|
81
|
+
has(id: number): boolean;
|
|
82
|
+
/** The level a region is at or arriving at, or `HLOD_NONE`. -1 for a region not in the set. */
|
|
83
|
+
levelOf(id: number): number;
|
|
84
|
+
/**
|
|
85
|
+
* Add a region: its box as min then max, and one geometric error per level, finest first and
|
|
86
|
+
* never shrinking. It arrives at no level and fades into the one the next `select` picks.
|
|
87
|
+
*/
|
|
88
|
+
add(id: number, box: ArrayLike<number>, errors: ArrayLike<number>): void;
|
|
89
|
+
/** Remove a region; nothing if it is not there. The last region takes its slot. */
|
|
90
|
+
remove(id: number): void;
|
|
91
|
+
/**
|
|
92
|
+
* Decide every region's level for this view and write what to draw into `out`, returning how
|
|
93
|
+
* many draws that is. `projectionScale` is `projectionScaleOf(fov, height)`; `occluder` may be
|
|
94
|
+
* the renderer, asked after this frame's occluders are declared.
|
|
95
|
+
*/
|
|
96
|
+
select(eye: ArrayLike<number>, projectionScale: number, frustum: FrustumPlanes, occluder: BoxOccluder | null, dtSec: number, out: HlodDraws): number;
|
|
97
|
+
/** The level a region at distance `d` should be at, from where it is, through the bands. */
|
|
98
|
+
private levelFor;
|
|
99
|
+
/** The distance level `index` takes over at; the level past the coarsest is the far limit. */
|
|
100
|
+
private switchAt;
|
|
101
|
+
/** Begin, reverse or redirect a crossfade toward `target`. */
|
|
102
|
+
private move;
|
|
103
|
+
private emit;
|
|
104
|
+
}
|