@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
package/src/render/mesh.ts
CHANGED
|
@@ -58,14 +58,6 @@ const ATTR_JOINTS = 11;
|
|
|
58
58
|
*/
|
|
59
59
|
const ATTR_CHANNEL = 13;
|
|
60
60
|
|
|
61
|
-
/**
|
|
62
|
-
* The locations `attachInstances` claims, which a plain draw through the same array must not read.
|
|
63
|
-
*
|
|
64
|
-
* Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
|
|
65
|
-
* the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
|
|
66
|
-
* `flatVert` refuses skinning with instancing. See `draw`.
|
|
67
|
-
*/
|
|
68
|
-
const INSTANCE_COLUMNS = [11, 12, 13, 14, 15] as const;
|
|
69
61
|
const ATTR_WEIGHTS = 12;
|
|
70
62
|
|
|
71
63
|
/**
|
|
@@ -95,6 +87,9 @@ function applyConstant(
|
|
|
95
87
|
export class Mesh {
|
|
96
88
|
private readonly vao: WebGLVertexArrayObject;
|
|
97
89
|
private readonly buffers: WebGLBuffer[] = [];
|
|
90
|
+
/** Every attribute bound from a buffer, so a batch can build a vertex array of its own. */
|
|
91
|
+
private readonly layout: { location: number; buffer: WebGLBuffer; size: number }[] = [];
|
|
92
|
+
private indexBuffer: WebGLBuffer | null = null;
|
|
98
93
|
/**
|
|
99
94
|
* The position and normal buffers, kept so `update` can rewrite them.
|
|
100
95
|
*
|
|
@@ -104,7 +99,8 @@ export class Mesh {
|
|
|
104
99
|
private readonly deformable: { positions: WebGLBuffer; normals: WebGLBuffer } | null = null;
|
|
105
100
|
/** How many floats the position buffer holds, so an update of the wrong size is refused. */
|
|
106
101
|
private readonly positionFloats: number;
|
|
107
|
-
|
|
102
|
+
/** Indices one draw of it issues; the batch-size rule reads it (`cullsInstances`). */
|
|
103
|
+
readonly indexCount: number;
|
|
108
104
|
/**
|
|
109
105
|
* The constant values this mesh's *absent* attributes are shaded with, re-applied on
|
|
110
106
|
* every draw.
|
|
@@ -227,7 +223,13 @@ export class Mesh {
|
|
|
227
223
|
if (data.uvs === undefined) {
|
|
228
224
|
gl.disableVertexAttribArray(ATTR_UV);
|
|
229
225
|
this.constants.push({ location: ATTR_UV, value: ABSENT_ATTRIBUTE['uvs'] });
|
|
226
|
+
} else if (data.layers !== undefined) {
|
|
227
|
+
/* The texture-array layer rides as the third coordinate — every vertex location is spent —
|
|
228
|
+
so a layered mesh uploads (u, v, layer) packed. See `MeshData.layers`. */
|
|
229
|
+
this.attachAttribute(gl, ATTR_UV, packUvLayers(data.uvs, data.layers), 3);
|
|
230
230
|
} else {
|
|
231
|
+
/* Two components; the shader's vec3 reads z as 0 by the GL rule for a missing component,
|
|
232
|
+
which is layer 0 — the only layer a plain texture has. */
|
|
231
233
|
this.attachAttribute(gl, ATTR_UV, data.uvs, 2);
|
|
232
234
|
}
|
|
233
235
|
|
|
@@ -313,6 +315,7 @@ export class Mesh {
|
|
|
313
315
|
if (indexBuffer === null) throw new Error('createBuffer failed');
|
|
314
316
|
this.buffers.push(indexBuffer);
|
|
315
317
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
318
|
+
this.indexBuffer = indexBuffer;
|
|
316
319
|
this.allocate(gl, gl.ELEMENT_ARRAY_BUFFER, indexBuffer, data.indices, gl.STATIC_DRAW);
|
|
317
320
|
|
|
318
321
|
gl.bindVertexArray(null);
|
|
@@ -322,28 +325,11 @@ export class Mesh {
|
|
|
322
325
|
draw(gl: WebGL2RenderingContext): void {
|
|
323
326
|
gl.bindVertexArray(this.vao);
|
|
324
327
|
/*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
* of the absent-attribute constant. For a placement with no rotation that column is
|
|
330
|
-
* `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
|
|
331
|
-
* term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
|
|
332
|
-
* normals, correct albedo, no error, no warning.
|
|
333
|
-
*
|
|
334
|
-
* Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
|
|
335
|
-
* compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
|
|
336
|
-
* rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
|
|
337
|
-
* them alike, which is what made it visible at all.
|
|
338
|
-
*
|
|
339
|
-
* Disabled here and put back by `drawInstances`, rather than given the batch an array of its
|
|
340
|
-
* own: that is the larger change this class's own note about two batches already contemplates,
|
|
341
|
-
* and it wants one for a different reason. Three calls on a mesh that has a batch and none on
|
|
342
|
-
* a mesh that does not.
|
|
328
|
+
* No instance columns to disable: a batch draws through a vertex array of its own
|
|
329
|
+
* (`createInstanceArray`), so this array never carries them. It used to, and location 13 — the
|
|
330
|
+
* channel — then read the third column of instance zero's matrix on a plain draw, which lit a
|
|
331
|
+
* mesh by ambient alone on this backend and not the other.
|
|
343
332
|
*/
|
|
344
|
-
if (this.instanced) {
|
|
345
|
-
for (const location of INSTANCE_COLUMNS) gl.disableVertexAttribArray(location);
|
|
346
|
-
}
|
|
347
333
|
// The VAO restores the arrays; these it cannot. See `constants`.
|
|
348
334
|
for (const constant of this.constants) {
|
|
349
335
|
const value = constant.value;
|
|
@@ -353,35 +339,30 @@ export class Mesh {
|
|
|
353
339
|
}
|
|
354
340
|
|
|
355
341
|
/**
|
|
356
|
-
*
|
|
342
|
+
* A vertex array for one instanced batch: every attribute this mesh binds from a buffer, its index
|
|
343
|
+
* buffer, and the batch's instance columns — the model matrix at 11 to 14, the tint at 15.
|
|
357
344
|
*
|
|
358
|
-
* **
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* **What would make it wrong** is a consumer legitimately wanting two batches of one mesh,
|
|
364
|
-
* which wants a vertex array per batch rather than a flag here.
|
|
345
|
+
* **A batch owns its own array**, which is what lets a mesh have more than one batch: a prop drawn
|
|
346
|
+
* once per region is one mesh and a batch per region. With one array a mesh had one batch, since a
|
|
347
|
+
* second would rebind the columns the first draws through and place one batch's instances by the
|
|
348
|
+
* other's matrices. The arrays share every buffer, so a batch costs its instances and nothing of
|
|
349
|
+
* the geometry. The caller deletes what this returns.
|
|
365
350
|
*/
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
351
|
+
createInstanceArray(
|
|
352
|
+
gl: WebGL2RenderingContext,
|
|
353
|
+
instances: WebGLBuffer,
|
|
354
|
+
stride: number,
|
|
355
|
+
): WebGLVertexArrayObject {
|
|
356
|
+
const vao = gl.createVertexArray();
|
|
357
|
+
if (vao === null) throw new Error('createVertexArray failed');
|
|
358
|
+
gl.bindVertexArray(vao);
|
|
359
|
+
for (const attribute of this.layout) {
|
|
360
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
|
|
361
|
+
gl.enableVertexAttribArray(attribute.location);
|
|
362
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, gl.FLOAT, false, 0, 0);
|
|
372
363
|
}
|
|
373
|
-
this.
|
|
374
|
-
|
|
375
|
-
this.instanceStride = stride;
|
|
376
|
-
gl.bindVertexArray(this.vao);
|
|
377
|
-
this.bindInstanceColumns(gl, buffer);
|
|
378
|
-
gl.bindVertexArray(null);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
|
|
382
|
-
private bindInstanceColumns(gl: WebGL2RenderingContext, buffer: WebGLBuffer): void {
|
|
383
|
-
const stride = this.instanceStride;
|
|
384
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
364
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
365
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, instances);
|
|
385
366
|
/* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
|
|
386
367
|
for (let column = 0; column < 4; column += 1) {
|
|
387
368
|
const location = 11 + column;
|
|
@@ -392,24 +373,17 @@ export class Mesh {
|
|
|
392
373
|
gl.enableVertexAttribArray(15);
|
|
393
374
|
gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
|
|
394
375
|
gl.vertexAttribDivisor(15, 1);
|
|
376
|
+
gl.bindVertexArray(null);
|
|
377
|
+
return vao;
|
|
395
378
|
}
|
|
396
379
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
* Draw `count` instances through the attributes `attachInstances` bound.
|
|
405
|
-
*
|
|
406
|
-
* The constants are re-applied exactly as `draw` does — a vertex array restores the arrays and
|
|
407
|
-
* not the constant attributes, and an absent optional attribute is a constant here.
|
|
408
|
-
*/
|
|
409
|
-
drawInstances(gl: WebGL2RenderingContext, count: number): void {
|
|
410
|
-
gl.bindVertexArray(this.vao);
|
|
411
|
-
/* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
|
|
412
|
-
if (this.instanceBuffer !== null) this.bindInstanceColumns(gl, this.instanceBuffer);
|
|
380
|
+
/** Draw `count` instances through a batch's own vertex array. See `createInstanceArray`. */
|
|
381
|
+
drawInstancesThrough(
|
|
382
|
+
gl: WebGL2RenderingContext,
|
|
383
|
+
vao: WebGLVertexArrayObject,
|
|
384
|
+
count: number,
|
|
385
|
+
): void {
|
|
386
|
+
gl.bindVertexArray(vao);
|
|
413
387
|
for (const constant of this.constants) {
|
|
414
388
|
const value = constant.value;
|
|
415
389
|
applyConstant(gl, constant.location, value);
|
|
@@ -472,6 +446,7 @@ export class Mesh {
|
|
|
472
446
|
this.allocate(gl, gl.ARRAY_BUFFER, buffer, data, usage);
|
|
473
447
|
gl.enableVertexAttribArray(location);
|
|
474
448
|
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
|
|
449
|
+
this.layout.push({ location, buffer, size });
|
|
475
450
|
return buffer;
|
|
476
451
|
}
|
|
477
452
|
|
|
@@ -579,3 +554,15 @@ export function createMeshIncremental(
|
|
|
579
554
|
const mesh = new Mesh(gl, data, dynamic, true);
|
|
580
555
|
return { mesh, upload: mesh.uploads(gl) };
|
|
581
556
|
}
|
|
557
|
+
|
|
558
|
+
/** Texture coordinates and layers interleaved as (u, v, layer), one row a vertex. */
|
|
559
|
+
export function packUvLayers(uvs: Float32Array, layers: Float32Array): Float32Array {
|
|
560
|
+
const count = layers.length;
|
|
561
|
+
const packed = new Float32Array(count * 3);
|
|
562
|
+
for (let v = 0; v < count; v++) {
|
|
563
|
+
packed[v * 3] = uvs[v * 2] as number;
|
|
564
|
+
packed[v * 3 + 1] = uvs[v * 2 + 1] as number;
|
|
565
|
+
packed[v * 3 + 2] = layers[v] as number;
|
|
566
|
+
}
|
|
567
|
+
return packed;
|
|
568
|
+
}
|
package/src/render/occlusion.ts
CHANGED
|
@@ -39,6 +39,9 @@ import type { Bounds } from '../math/bounds.ts';
|
|
|
39
39
|
/** What an unwritten texel holds: the far plane, which occludes nothing. */
|
|
40
40
|
const FAR = 1;
|
|
41
41
|
|
|
42
|
+
/** Texels a tested rectangle may span at the level it is read from. See `boxBehind`. */
|
|
43
|
+
const TEXELS_ACROSS = 8;
|
|
44
|
+
|
|
42
45
|
export interface OcclusionOptions {
|
|
43
46
|
/** Texels across. The height follows the aspect. */
|
|
44
47
|
readonly width: number;
|
|
@@ -189,15 +192,47 @@ export class OcclusionBuffer {
|
|
|
189
192
|
const radius = bounds.radius * Math.max(sx, sy, sz);
|
|
190
193
|
|
|
191
194
|
/* The cube that contains the sphere, which contains the object. Conservative twice over. */
|
|
195
|
+
return this.boxBehind(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Whether a world-space box is entirely behind the declared occluders — the tighter question for
|
|
200
|
+
* something that is a box, where the cube around its sphere reaches far past it. **A flat region
|
|
201
|
+
* is the case it exists for**: a city block's sphere is half as deep underground as it is wide,
|
|
202
|
+
* nothing there occludes it, and the sphere test abstains for every block in the city. False
|
|
203
|
+
* whenever anything is uncertain, exactly as `occluded`.
|
|
204
|
+
*/
|
|
205
|
+
occludedBox(min: ArrayLike<number>, max: ArrayLike<number>): boolean {
|
|
206
|
+
if (this.occluders === 0) return false;
|
|
207
|
+
if (!this.built) this.buildPyramid();
|
|
208
|
+
return this.boxBehind(
|
|
209
|
+
min[0] ?? 0,
|
|
210
|
+
min[1] ?? 0,
|
|
211
|
+
min[2] ?? 0,
|
|
212
|
+
max[0] ?? 0,
|
|
213
|
+
max[1] ?? 0,
|
|
214
|
+
max[2] ?? 0,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The shared test: a world box's corners projected, its rectangle tested against the pyramid. */
|
|
219
|
+
private boxBehind(
|
|
220
|
+
loX: number,
|
|
221
|
+
loY: number,
|
|
222
|
+
loZ: number,
|
|
223
|
+
hiX: number,
|
|
224
|
+
hiY: number,
|
|
225
|
+
hiZ: number,
|
|
226
|
+
): boolean {
|
|
192
227
|
let minX = Infinity;
|
|
193
228
|
let minY = Infinity;
|
|
194
229
|
let minZ = Infinity;
|
|
195
230
|
let maxX = -Infinity;
|
|
196
231
|
let maxY = -Infinity;
|
|
197
232
|
for (let corner = 0; corner < 8; corner++) {
|
|
198
|
-
const px = corner & 1 ?
|
|
199
|
-
const py = corner & 2 ?
|
|
200
|
-
const pz = corner & 4 ?
|
|
233
|
+
const px = corner & 1 ? hiX : loX;
|
|
234
|
+
const py = corner & 2 ? hiY : loY;
|
|
235
|
+
const pz = corner & 4 ? hiZ : loZ;
|
|
201
236
|
if (!this.project(px, py, pz, 0)) return false;
|
|
202
237
|
minX = Math.min(minX, CORNERS[0] ?? 0);
|
|
203
238
|
maxX = Math.max(maxX, CORNERS[0] ?? 0);
|
|
@@ -212,13 +247,22 @@ export class OcclusionBuffer {
|
|
|
212
247
|
if (minX < 0 || minY < 0 || maxX > width || maxY > height) return false;
|
|
213
248
|
|
|
214
249
|
/*
|
|
215
|
-
* The coarsest level
|
|
216
|
-
* most
|
|
217
|
-
* repository's determinism gate refuses elsewhere; a shift loop says it in integers.
|
|
250
|
+
* The coarsest level at which the rectangle spans at most `TEXELS_ACROSS` texels, so the test
|
|
251
|
+
* reads at most that many plus one each way. `Math.log2` would say the same thing and is one of
|
|
252
|
+
* the calls this repository's determinism gate refuses elsewhere; a shift loop says it in integers.
|
|
253
|
+
*
|
|
254
|
+
* **Eight, not one, since 2026-09-30.** At one the level's texel was as wide as the rectangle, and
|
|
255
|
+
* two such texels aligned to the grid span up to twice it — so anything whose rectangle came near
|
|
256
|
+
* an occluder's edge, or the buffer's own unwritten border row, read unwritten texels and
|
|
257
|
+
* abstained. That was every city region behind a row of buildings, whose rectangle sits just
|
|
258
|
+
* under the skyline; measured on a box seven metres wide behind a wall filling the view, which
|
|
259
|
+
* one abstained at and eight culls. Reading up to 81 texels instead of 4 costs a few dozen
|
|
260
|
+
* compares and keeps the answer exactly as conservative: the maximum over every texel the
|
|
261
|
+
* rectangle touches.
|
|
218
262
|
*/
|
|
219
263
|
let level = 0;
|
|
220
264
|
let span = Math.max(maxX - minX, maxY - minY);
|
|
221
|
-
while (span >
|
|
265
|
+
while (span > TEXELS_ACROSS && level + 1 < this.levels.length) {
|
|
222
266
|
span *= 0.5;
|
|
223
267
|
level++;
|
|
224
268
|
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { mat3 } from 'gl-matrix';
|
|
2
|
+
|
|
3
|
+
import { createEmptyTexture2D } from './emptyTexture.ts';
|
|
4
|
+
import { pointShadowFaceRotation } from './pointShadowImage.ts';
|
|
5
|
+
import { compileProgram, uniformLocations } from './shader.ts';
|
|
6
|
+
import {
|
|
7
|
+
OCTAHEDRAL_RESOLVE_TINT_FRAG,
|
|
8
|
+
OCTAHEDRAL_RESOLVE_VERT,
|
|
9
|
+
} from './shaders/octahedralResolve.ts';
|
|
10
|
+
|
|
11
|
+
/** Filled per resolved face, at module scope, because a bake runs inside the frame's budget. */
|
|
12
|
+
const faceRotation = mat3.create();
|
|
13
|
+
const CLEAR = new Float32Array([1, 1, 1, 1]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* What a lamp's light keeps through glass: an RGBA8 array of one layer a light, parallel to the
|
|
17
|
+
* point shadow array and indexed by the light, not by the interleaved depth layer. RGB is the
|
|
18
|
+
* product of every pane's `transmission × (1 − F) × tint`, A the product of their clarities
|
|
19
|
+
* (`glassShadow.ts`).
|
|
20
|
+
*
|
|
21
|
+
* **Drawn a face at a time, as the depth is**: into a face-sized colour scratch cleared to white,
|
|
22
|
+
* with a multiplying blend and no depth test, then resolved into the face's region of the layer by
|
|
23
|
+
* `OCTAHEDRAL_RESOLVE_TINT_FRAG`. One scratch for the whole engine, for the reason
|
|
24
|
+
* `PointShadowArray` gives for its own.
|
|
25
|
+
*
|
|
26
|
+
* **Which faces hold glass is remembered, six bits a layer**, so a face with no glass and none to
|
|
27
|
+
* forget costs nothing, and a face that inherited a previous owner's glass is cleared. **The chain
|
|
28
|
+
* frost reads is rebuilt once a round**, for the layers the round touched, through a 2D staging
|
|
29
|
+
* chain: `generateMipmap` on an array rebuilds every layer, which is the whole pool's texels for one
|
|
30
|
+
* lamp's glass. What that costs is one layer of memory with its chain — 5.6 MB at 1024, 1.4 MB at
|
|
31
|
+
* `'half'` — and a copy out and back per touched layer.
|
|
32
|
+
*/
|
|
33
|
+
export class PointGlassTint {
|
|
34
|
+
readonly texture: WebGLTexture;
|
|
35
|
+
readonly edge: number;
|
|
36
|
+
|
|
37
|
+
private readonly layers: number;
|
|
38
|
+
private readonly levels: number;
|
|
39
|
+
private readonly faceSize: number;
|
|
40
|
+
private readonly scratch: WebGLTexture;
|
|
41
|
+
private readonly scratchFramebuffer: WebGLFramebuffer;
|
|
42
|
+
private readonly staging: WebGLTexture;
|
|
43
|
+
private readonly framebuffer: WebGLFramebuffer;
|
|
44
|
+
private readonly readFramebuffer: WebGLFramebuffer;
|
|
45
|
+
/** Parked on the resolve's unit while the scratch is a render target; see `beginFace`. */
|
|
46
|
+
private readonly parked: WebGLTexture;
|
|
47
|
+
private readonly program: WebGLProgram;
|
|
48
|
+
private readonly uniforms: Record<string, WebGLUniformLocation>;
|
|
49
|
+
private readonly vao: WebGLVertexArrayObject;
|
|
50
|
+
/** Six bits a layer: which faces' regions hold glass. */
|
|
51
|
+
private readonly heldFaces: Uint8Array;
|
|
52
|
+
/** Layers whose level 0 changed since the chain was last rebuilt. */
|
|
53
|
+
private readonly dirty: Uint8Array;
|
|
54
|
+
|
|
55
|
+
constructor(gl: WebGL2RenderingContext, faceSize: number, edge: number, layers: number) {
|
|
56
|
+
this.edge = edge;
|
|
57
|
+
this.layers = layers;
|
|
58
|
+
this.faceSize = faceSize;
|
|
59
|
+
this.levels = Math.floor(Math.log2(Math.max(1, edge))) + 1;
|
|
60
|
+
this.heldFaces = new Uint8Array(layers);
|
|
61
|
+
this.dirty = new Uint8Array(layers);
|
|
62
|
+
|
|
63
|
+
this.texture = colourTexture(gl, gl.TEXTURE_2D_ARRAY, this.levels, edge, layers);
|
|
64
|
+
this.scratch = colourTexture(gl, gl.TEXTURE_2D, 1, faceSize, 1);
|
|
65
|
+
this.staging = colourTexture(gl, gl.TEXTURE_2D, this.levels, edge, 1);
|
|
66
|
+
this.parked = createEmptyTexture2D(gl);
|
|
67
|
+
this.scratchFramebuffer = framebufferOf(gl);
|
|
68
|
+
this.framebuffer = framebufferOf(gl);
|
|
69
|
+
this.readFramebuffer = framebufferOf(gl);
|
|
70
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.scratchFramebuffer);
|
|
71
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.scratch, 0);
|
|
72
|
+
|
|
73
|
+
this.program = compileProgram(
|
|
74
|
+
gl,
|
|
75
|
+
OCTAHEDRAL_RESOLVE_VERT,
|
|
76
|
+
OCTAHEDRAL_RESOLVE_TINT_FRAG,
|
|
77
|
+
'octahedralResolveTint',
|
|
78
|
+
);
|
|
79
|
+
this.uniforms = uniformLocations(gl, this.program, 'octahedralResolveTint');
|
|
80
|
+
const vao = gl.createVertexArray();
|
|
81
|
+
if (vao === null) throw new Error('PointGlassTint: createVertexArray failed');
|
|
82
|
+
this.vao = vao;
|
|
83
|
+
|
|
84
|
+
/* Every layer born clear: a light no pane has reached lets everything through. */
|
|
85
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
86
|
+
for (let layer = 0; layer < layers; layer++) {
|
|
87
|
+
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, layer);
|
|
88
|
+
gl.clearBufferfv(gl.COLOR, 0, CLEAR);
|
|
89
|
+
}
|
|
90
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
91
|
+
/* Once, for every layer: white all the way down. */
|
|
92
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
|
|
93
|
+
gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
|
|
94
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Whether `face` of `layer` holds glass: a face that does must be cleared before it is reused. */
|
|
98
|
+
held(layer: number, face: number): boolean {
|
|
99
|
+
return (((this.heldFaces[layer] ?? 0) >> face) & 1) === 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Bind the colour scratch, clear it to white, and set the multiplying state.
|
|
104
|
+
*
|
|
105
|
+
* The previous resolve left the scratch bound for sampling on unit 0, and it is about to be the
|
|
106
|
+
* target: parked first, for the reason `PointShadowArray.beginFace` gives.
|
|
107
|
+
*/
|
|
108
|
+
beginFace(gl: WebGL2RenderingContext): void {
|
|
109
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
110
|
+
gl.bindTexture(gl.TEXTURE_2D, this.parked);
|
|
111
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.scratchFramebuffer);
|
|
112
|
+
gl.viewport(0, 0, this.faceSize, this.faceSize);
|
|
113
|
+
gl.clearBufferfv(gl.COLOR, 0, CLEAR);
|
|
114
|
+
gl.disable(gl.DEPTH_TEST);
|
|
115
|
+
gl.depthMask(false);
|
|
116
|
+
gl.enable(gl.BLEND);
|
|
117
|
+
gl.blendEquation(gl.FUNC_ADD);
|
|
118
|
+
gl.blendFuncSeparate(gl.DST_COLOR, gl.ZERO, gl.DST_ALPHA, gl.ZERO);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Copy the scratch into `face`'s region of `layer`, and remember whether it held glass.
|
|
123
|
+
*
|
|
124
|
+
* Leaves depth testing and writing on, which is what the next face's casters expect: the depth
|
|
125
|
+
* resolve that runs before them turns both on, and this one ran after it and turned them off.
|
|
126
|
+
*/
|
|
127
|
+
resolveFace(gl: WebGL2RenderingContext, layer: number, face: number, drawn: boolean): void {
|
|
128
|
+
gl.disable(gl.BLEND);
|
|
129
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
130
|
+
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, layer);
|
|
131
|
+
gl.viewport(0, 0, this.edge, this.edge);
|
|
132
|
+
gl.useProgram(this.program);
|
|
133
|
+
gl.bindVertexArray(this.vao);
|
|
134
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
135
|
+
gl.bindTexture(gl.TEXTURE_2D, this.scratch);
|
|
136
|
+
gl.uniform1i(this.uniforms['uFace'] ?? null, 0);
|
|
137
|
+
gl.uniform1i(this.uniforms['uFaceIndex'] ?? null, face);
|
|
138
|
+
gl.uniformMatrix3fv(
|
|
139
|
+
this.uniforms['uFaceRotation'] ?? null,
|
|
140
|
+
false,
|
|
141
|
+
pointShadowFaceRotation(face, faceRotation),
|
|
142
|
+
);
|
|
143
|
+
gl.uniform1f(this.uniforms['uEdge'] ?? null, this.edge);
|
|
144
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
145
|
+
gl.bindVertexArray(null);
|
|
146
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
147
|
+
gl.enable(gl.DEPTH_TEST);
|
|
148
|
+
gl.depthMask(true);
|
|
149
|
+
|
|
150
|
+
const bit = 1 << face;
|
|
151
|
+
const was = this.heldFaces[layer] ?? 0;
|
|
152
|
+
this.heldFaces[layer] = drawn ? was | bit : was & ~bit;
|
|
153
|
+
this.dirty[layer] = 1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Rebuild the chain of every layer touched since the last call, one layer at a time. */
|
|
157
|
+
fillMips(gl: WebGL2RenderingContext): void {
|
|
158
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
159
|
+
if (this.dirty[layer] !== 1) continue;
|
|
160
|
+
this.dirty[layer] = 0;
|
|
161
|
+
/* Level 0 out into the staging chain, which mipmaps as one 2D texture. */
|
|
162
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.readFramebuffer);
|
|
163
|
+
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.texture, 0, layer);
|
|
164
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
|
|
165
|
+
gl.framebufferTexture2D(
|
|
166
|
+
gl.DRAW_FRAMEBUFFER,
|
|
167
|
+
gl.COLOR_ATTACHMENT0,
|
|
168
|
+
gl.TEXTURE_2D,
|
|
169
|
+
this.staging,
|
|
170
|
+
0,
|
|
171
|
+
);
|
|
172
|
+
blit(gl, this.edge, this.edge);
|
|
173
|
+
gl.bindTexture(gl.TEXTURE_2D, this.staging);
|
|
174
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
175
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
176
|
+
/* And every level after it back into the layer. */
|
|
177
|
+
for (let level = 1; level < this.levels; level++) {
|
|
178
|
+
const size = Math.max(1, this.edge >> level);
|
|
179
|
+
gl.framebufferTexture2D(
|
|
180
|
+
gl.READ_FRAMEBUFFER,
|
|
181
|
+
gl.COLOR_ATTACHMENT0,
|
|
182
|
+
gl.TEXTURE_2D,
|
|
183
|
+
this.staging,
|
|
184
|
+
level,
|
|
185
|
+
);
|
|
186
|
+
gl.framebufferTextureLayer(
|
|
187
|
+
gl.DRAW_FRAMEBUFFER,
|
|
188
|
+
gl.COLOR_ATTACHMENT0,
|
|
189
|
+
this.texture,
|
|
190
|
+
level,
|
|
191
|
+
layer,
|
|
192
|
+
);
|
|
193
|
+
blit(gl, size, size);
|
|
194
|
+
}
|
|
195
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
|
|
196
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
dispose(gl: WebGL2RenderingContext): void {
|
|
201
|
+
gl.deleteFramebuffer(this.scratchFramebuffer);
|
|
202
|
+
gl.deleteFramebuffer(this.framebuffer);
|
|
203
|
+
gl.deleteFramebuffer(this.readFramebuffer);
|
|
204
|
+
gl.deleteTexture(this.texture);
|
|
205
|
+
gl.deleteTexture(this.scratch);
|
|
206
|
+
gl.deleteTexture(this.staging);
|
|
207
|
+
gl.deleteTexture(this.parked);
|
|
208
|
+
gl.deleteProgram(this.program);
|
|
209
|
+
gl.deleteVertexArray(this.vao);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** A colour texture with `levels` levels: nearest for a single level, trilinear for a chain. */
|
|
214
|
+
function colourTexture(
|
|
215
|
+
gl: WebGL2RenderingContext,
|
|
216
|
+
target: number,
|
|
217
|
+
levels: number,
|
|
218
|
+
edge: number,
|
|
219
|
+
layers: number,
|
|
220
|
+
): WebGLTexture {
|
|
221
|
+
const texture = gl.createTexture();
|
|
222
|
+
if (texture === null) throw new Error('PointGlassTint: createTexture failed');
|
|
223
|
+
gl.bindTexture(target, texture);
|
|
224
|
+
if (target === gl.TEXTURE_2D_ARRAY) {
|
|
225
|
+
gl.texStorage3D(target, levels, gl.RGBA8, edge, edge, layers);
|
|
226
|
+
} else {
|
|
227
|
+
gl.texStorage2D(target, levels, gl.RGBA8, edge, edge);
|
|
228
|
+
}
|
|
229
|
+
gl.texParameteri(
|
|
230
|
+
target,
|
|
231
|
+
gl.TEXTURE_MIN_FILTER,
|
|
232
|
+
levels > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST,
|
|
233
|
+
);
|
|
234
|
+
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, levels > 1 ? gl.LINEAR : gl.NEAREST);
|
|
235
|
+
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
236
|
+
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
237
|
+
gl.bindTexture(target, null);
|
|
238
|
+
return texture;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function framebufferOf(gl: WebGL2RenderingContext): WebGLFramebuffer {
|
|
242
|
+
const framebuffer = gl.createFramebuffer();
|
|
243
|
+
if (framebuffer === null) throw new Error('PointGlassTint: createFramebuffer failed');
|
|
244
|
+
return framebuffer;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The bound read framebuffer's colour into the bound draw framebuffer's, texel for texel. */
|
|
248
|
+
function blit(gl: WebGL2RenderingContext, width: number, height: number): void {
|
|
249
|
+
gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
250
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER } from './clusteredLights.ts';
|
|
4
4
|
import { MAX_POINT_LIGHTS, POINT_SHADOW_POOL } from './lightBudget.ts';
|
|
5
|
+
import { gatherLights } from './lightGrid.ts';
|
|
6
|
+
import type { LightGrid } from './lightGrid.ts';
|
|
5
7
|
|
|
6
8
|
export const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
|
|
7
9
|
|
|
@@ -43,8 +45,15 @@ const FADE_BAND_M = 40;
|
|
|
43
45
|
*/
|
|
44
46
|
const CONTENTION_BAND_M = 3;
|
|
45
47
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Sort keys for the shadow list, which is ordered independently of the shaded one.
|
|
50
|
+
*
|
|
51
|
+
* **Float64, and ties broken by source index**, the shaded list's rule. These were single precision
|
|
52
|
+
* compared against a double, so which of two lamps exactly as far away came first depended on
|
|
53
|
+
* which way the distance happened to round — and on the order the lamps were met, which is what a
|
|
54
|
+
* grid changes. Now the list is the same whatever order its candidates arrive in.
|
|
55
|
+
*/
|
|
56
|
+
const shadowRank = new Float64Array(POINT_SHADOW_POOL);
|
|
48
57
|
|
|
49
58
|
/**
|
|
50
59
|
* Every light in range this frame, by source index and squared distance, before the nearest are
|
|
@@ -344,6 +353,58 @@ export function selectPointLights(
|
|
|
344
353
|
castX = x,
|
|
345
354
|
castY = y,
|
|
346
355
|
castZ = z,
|
|
356
|
+
): void {
|
|
357
|
+
choose(sources, null, sources.length, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* `selectPointLights` over a grid's sources, looking only at the lights near the eye: the same
|
|
362
|
+
* choice, tie for tie, for a world of thousands of fixed lights that the plain scan would visit
|
|
363
|
+
* every frame. See `lightGrid.ts` for why the result is the scan's exactly.
|
|
364
|
+
*/
|
|
365
|
+
export function selectGridLights(
|
|
366
|
+
grid: LightGrid,
|
|
367
|
+
x: number,
|
|
368
|
+
y: number,
|
|
369
|
+
z: number,
|
|
370
|
+
out: PointLightBuffer,
|
|
371
|
+
timeSeconds = 0,
|
|
372
|
+
viewRange = DEFAULT_POINT_LIGHT_VIEW_RANGE,
|
|
373
|
+
castX = x,
|
|
374
|
+
castY = y,
|
|
375
|
+
castZ = z,
|
|
376
|
+
): void {
|
|
377
|
+
const count = gatherLights(grid, x, y, z, viewRange + grid.maxRadius);
|
|
378
|
+
choose(
|
|
379
|
+
grid.sources,
|
|
380
|
+
grid.gathered,
|
|
381
|
+
count,
|
|
382
|
+
x,
|
|
383
|
+
y,
|
|
384
|
+
z,
|
|
385
|
+
out,
|
|
386
|
+
timeSeconds,
|
|
387
|
+
viewRange,
|
|
388
|
+
castX,
|
|
389
|
+
castY,
|
|
390
|
+
castZ,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** The choice itself, over every source or over the ones `visit` names, in source order. */
|
|
395
|
+
function choose(
|
|
396
|
+
sources: readonly PointLightSource[],
|
|
397
|
+
visit: Int32Array | null,
|
|
398
|
+
visitCount: number,
|
|
399
|
+
x: number,
|
|
400
|
+
y: number,
|
|
401
|
+
z: number,
|
|
402
|
+
out: PointLightBuffer,
|
|
403
|
+
timeSeconds: number,
|
|
404
|
+
viewRange: number,
|
|
405
|
+
castX: number,
|
|
406
|
+
castY: number,
|
|
407
|
+
castZ: number,
|
|
347
408
|
): void {
|
|
348
409
|
out.count = 0;
|
|
349
410
|
out.shadowCount = 0;
|
|
@@ -362,7 +423,8 @@ export function selectPointLights(
|
|
|
362
423
|
let cutSq = Infinity;
|
|
363
424
|
let candidateCount = 0;
|
|
364
425
|
|
|
365
|
-
for (let
|
|
426
|
+
for (let visited = 0; visited < visitCount; visited++) {
|
|
427
|
+
const sourceIndex = visit === null ? visited : (visit[visited] as number);
|
|
366
428
|
const light = sources[sourceIndex];
|
|
367
429
|
if (light === undefined) continue;
|
|
368
430
|
/*
|
|
@@ -431,7 +493,13 @@ export function selectPointLights(
|
|
|
431
493
|
const castDistanceSq = cx * cx + cy * cy + cz * cz;
|
|
432
494
|
if (castDistanceSq >= light.radius * light.radius) continue;
|
|
433
495
|
let shadowSlot = out.shadowCount;
|
|
434
|
-
while (
|
|
496
|
+
while (
|
|
497
|
+
shadowSlot > 0 &&
|
|
498
|
+
((shadowRank[shadowSlot - 1] ?? 0) > castDistanceSq ||
|
|
499
|
+
((shadowRank[shadowSlot - 1] ?? 0) === castDistanceSq &&
|
|
500
|
+
(out.shadowIndex[shadowSlot - 1] ?? -1) > sourceIndex))
|
|
501
|
+
)
|
|
502
|
+
shadowSlot--;
|
|
435
503
|
if (shadowSlot >= POINT_SHADOW_POOL) continue;
|
|
436
504
|
const lastShadow = Math.min(out.shadowCount, POINT_SHADOW_POOL - 1);
|
|
437
505
|
for (let index = lastShadow; index > shadowSlot; index--) {
|