@driftengine/core 3.61.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/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +9 -0
- package/dist/behavior/behaviorTree.d.ts +140 -0
- package/dist/behavior/behaviorTree.js +289 -0
- package/dist/build/shaderComments.d.ts +64 -0
- package/dist/build/shaderComments.js +0 -0
- package/dist/cinematic/player.d.ts +80 -0
- package/dist/cinematic/player.js +184 -0
- package/dist/cinematic/script.d.ts +63 -0
- package/dist/cinematic/script.js +141 -0
- package/dist/core/bootGate.d.ts +37 -0
- package/dist/core/bootGate.js +47 -0
- package/dist/core/loadTracker.d.ts +78 -0
- package/dist/core/loadTracker.js +106 -0
- package/dist/core/loop.d.ts +118 -0
- package/dist/core/loop.js +84 -0
- package/dist/core/messageQueue.d.ts +61 -0
- package/dist/core/messageQueue.js +104 -0
- package/dist/core/onceSet.d.ts +27 -0
- package/dist/core/onceSet.js +62 -0
- package/dist/core/preferences.d.ts +47 -0
- package/dist/core/preferences.js +140 -0
- package/dist/core/remoteSave.d.ts +154 -0
- package/dist/core/remoteSave.js +249 -0
- package/dist/core/rng.d.ts +64 -0
- package/dist/core/rng.js +72 -0
- package/dist/core/stepBudget.d.ts +91 -0
- package/dist/core/stepBudget.js +105 -0
- package/dist/core/storage.d.ts +52 -0
- package/dist/core/storage.js +85 -0
- package/dist/core/tickTrace.d.ts +72 -0
- package/dist/core/tickTrace.js +102 -0
- package/dist/core/timeCurve.d.ts +83 -0
- package/dist/core/timeCurve.js +107 -0
- package/dist/core/withDefaults.d.ts +21 -0
- package/dist/core/withDefaults.js +29 -0
- package/dist/dev/contactProbe.d.ts +41 -0
- package/dist/dev/contactProbe.js +58 -0
- package/dist/dev/fpsMeter.d.ts +45 -0
- package/dist/dev/fpsMeter.js +86 -0
- package/dist/environment/celestialClock.d.ts +141 -0
- package/dist/environment/celestialClock.js +262 -0
- package/dist/geometry/decal.d.ts +61 -0
- package/dist/geometry/decal.js +178 -0
- package/dist/geometry/filmPatch.d.ts +35 -0
- package/dist/geometry/filmPatch.js +112 -0
- package/dist/geometry/growable.d.ts +70 -0
- package/dist/geometry/growable.js +167 -0
- package/dist/geometry/lightVolume.d.ts +76 -0
- package/dist/geometry/lightVolume.js +65 -0
- package/dist/geometry/meshBuilder.d.ts +426 -0
- package/dist/geometry/meshBuilder.js +1184 -0
- package/dist/geometry/pixelFont.d.ts +59 -0
- package/dist/geometry/pixelFont.js +179 -0
- package/dist/geometry/ribbon.d.ts +101 -0
- package/dist/geometry/ribbon.js +546 -0
- package/dist/geometry/shellFill.d.ts +101 -0
- package/dist/geometry/shellFill.js +203 -0
- package/dist/geometry/spline.d.ts +162 -0
- package/dist/geometry/spline.js +527 -0
- package/dist/geometry/tangents.d.ts +30 -0
- package/dist/geometry/tangents.js +142 -0
- package/dist/geometry/textMesh.d.ts +51 -0
- package/dist/geometry/textMesh.js +57 -0
- package/dist/geometry/treeBuilder.d.ts +37 -0
- package/dist/geometry/treeBuilder.js +56 -0
- package/dist/host/display.d.ts +135 -0
- package/dist/host/display.js +114 -0
- package/dist/host/files.d.ts +25 -0
- package/dist/host/files.js +44 -0
- package/dist/host/lifecycle.d.ts +45 -0
- package/dist/host/lifecycle.js +28 -0
- package/dist/host/platformServices.d.ts +23 -0
- package/dist/host/platformServices.js +1 -0
- package/dist/host/screen.d.ts +49 -0
- package/dist/host/screen.js +90 -0
- package/dist/index.d.ts +289 -0
- package/dist/index.js +320 -0
- package/dist/input/actionMap.d.ts +181 -0
- package/dist/input/actionMap.js +314 -0
- package/dist/input/activeDevice.d.ts +55 -0
- package/dist/input/activeDevice.js +83 -0
- package/dist/input/gamepadIdentity.d.ts +43 -0
- package/dist/input/gamepadIdentity.js +108 -0
- package/dist/input/gamepadMapping.d.ts +56 -0
- package/dist/input/gamepadMapping.js +91 -0
- package/dist/input/input.d.ts +281 -0
- package/dist/input/input.js +703 -0
- package/dist/input/inputHarness.d.ts +42 -0
- package/dist/input/inputHarness.js +193 -0
- package/dist/input/rightGesture.d.ts +57 -0
- package/dist/input/rightGesture.js +49 -0
- package/dist/input/rumble.d.ts +71 -0
- package/dist/input/rumble.js +119 -0
- package/dist/input/touchControls.d.ts +138 -0
- package/dist/input/touchControls.js +352 -0
- package/dist/input/typingTarget.d.ts +6 -0
- package/dist/input/typingTarget.js +27 -0
- package/dist/math/bounds.d.ts +61 -0
- package/dist/math/bounds.js +121 -0
- package/dist/math/color.d.ts +8 -0
- package/dist/math/color.js +35 -0
- package/dist/math/exact.d.ts +66 -0
- package/dist/math/exact.js +262 -0
- package/dist/math/frustum.d.ts +44 -0
- package/dist/math/frustum.js +90 -0
- package/dist/math/intersect.d.ts +76 -0
- package/dist/math/intersect.js +185 -0
- package/dist/math/scalar.d.ts +51 -0
- package/dist/math/scalar.js +99 -0
- package/dist/math/spring.d.ts +59 -0
- package/dist/math/spring.js +88 -0
- package/dist/nav/navGraph.d.ts +71 -0
- package/dist/nav/navGraph.js +111 -0
- package/dist/nav/navPath.d.ts +93 -0
- package/dist/nav/navPath.js +162 -0
- package/dist/nav/navSearch.d.ts +74 -0
- package/dist/nav/navSearch.js +198 -0
- package/dist/physics/boxSurface.d.ts +48 -0
- package/dist/physics/boxSurface.js +73 -0
- package/dist/physics/colliderSurface.d.ts +46 -0
- package/dist/physics/colliderSurface.js +146 -0
- package/dist/physics/compositeSurface.d.ts +33 -0
- package/dist/physics/compositeSurface.js +108 -0
- package/dist/physics/heightSurface.d.ts +103 -0
- package/dist/physics/heightSurface.js +153 -0
- package/dist/physics/ribbonSurface.d.ts +239 -0
- package/dist/physics/ribbonSurface.js +522 -0
- package/dist/render/ambientOcclusionPass.d.ts +53 -0
- package/dist/render/ambientOcclusionPass.js +179 -0
- package/dist/render/areaLights.d.ts +116 -0
- package/dist/render/areaLights.js +91 -0
- package/dist/render/areaShadowSet.d.ts +184 -0
- package/dist/render/areaShadowSet.js +277 -0
- package/dist/render/atmosphere.d.ts +94 -0
- package/dist/render/atmosphere.js +91 -0
- package/dist/render/backend/api.d.ts +496 -0
- package/dist/render/backend/api.js +1 -0
- package/dist/render/backend/budget.d.ts +82 -0
- package/dist/render/backend/budget.js +99 -0
- package/dist/render/backend/createRenderer.d.ts +135 -0
- package/dist/render/backend/createRenderer.js +201 -0
- package/dist/render/backend/deadline.d.ts +24 -0
- package/dist/render/backend/deadline.js +38 -0
- package/dist/render/backend/probe.d.ts +30 -0
- package/dist/render/backend/probe.js +129 -0
- package/dist/render/backend/select.d.ts +70 -0
- package/dist/render/backend/select.js +249 -0
- package/dist/render/backend/timer.d.ts +50 -0
- package/dist/render/backend/timer.js +1 -0
- package/dist/render/backend/webgl2/instanced.d.ts +21 -0
- package/dist/render/backend/webgl2/instanced.js +42 -0
- package/dist/render/backend/webgl2/morphTexture.d.ts +21 -0
- package/dist/render/backend/webgl2/morphTexture.js +55 -0
- package/dist/render/backend/webgl2/renderer.d.ts +3027 -0
- package/dist/render/backend/webgl2/renderer.js +6041 -0
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +32 -0
- package/dist/render/backend/webgl2/skinPaletteTexture.js +68 -0
- package/dist/render/backend/webgpu/boltPass.d.ts +42 -0
- package/dist/render/backend/webgpu/boltPass.js +160 -0
- package/dist/render/backend/webgpu/buffers.d.ts +159 -0
- package/dist/render/backend/webgpu/buffers.js +402 -0
- package/dist/render/backend/webgpu/causticsPass.d.ts +36 -0
- package/dist/render/backend/webgpu/causticsPass.js +138 -0
- package/dist/render/backend/webgpu/clusterBinner.d.ts +51 -0
- package/dist/render/backend/webgpu/clusterBinner.js +206 -0
- package/dist/render/backend/webgpu/depthPass.d.ts +114 -0
- package/dist/render/backend/webgpu/depthPass.js +228 -0
- package/dist/render/backend/webgpu/device.d.ts +52 -0
- package/dist/render/backend/webgpu/device.js +121 -0
- package/dist/render/backend/webgpu/drawCommand.d.ts +43 -0
- package/dist/render/backend/webgpu/drawCommand.js +54 -0
- package/dist/render/backend/webgpu/environmentTexels.d.ts +28 -0
- package/dist/render/backend/webgpu/environmentTexels.js +79 -0
- package/dist/render/backend/webgpu/filmPass.d.ts +29 -0
- package/dist/render/backend/webgpu/filmPass.js +97 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +213 -0
- package/dist/render/backend/webgpu/flatPass.js +482 -0
- package/dist/render/backend/webgpu/flockPass.d.ts +34 -0
- package/dist/render/backend/webgpu/flockPass.js +129 -0
- package/dist/render/backend/webgpu/gpuTimestamps.d.ts +94 -0
- package/dist/render/backend/webgpu/gpuTimestamps.js +213 -0
- package/dist/render/backend/webgpu/insetPass.d.ts +19 -0
- package/dist/render/backend/webgpu/insetPass.js +108 -0
- package/dist/render/backend/webgpu/instanced.d.ts +27 -0
- package/dist/render/backend/webgpu/instanced.js +49 -0
- package/dist/render/backend/webgpu/lightVolumePass.d.ts +75 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +154 -0
- package/dist/render/backend/webgpu/linePass.d.ts +90 -0
- package/dist/render/backend/webgpu/linePass.js +220 -0
- package/dist/render/backend/webgpu/morphTexture.d.ts +18 -0
- package/dist/render/backend/webgpu/morphTexture.js +51 -0
- package/dist/render/backend/webgpu/panelPass.d.ts +37 -0
- package/dist/render/backend/webgpu/panelPass.js +114 -0
- package/dist/render/backend/webgpu/particlePass.d.ts +89 -0
- package/dist/render/backend/webgpu/particlePass.js +280 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +93 -0
- package/dist/render/backend/webgpu/pipelineCache.js +140 -0
- package/dist/render/backend/webgpu/plumePass.d.ts +69 -0
- package/dist/render/backend/webgpu/plumePass.js +213 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +88 -0
- package/dist/render/backend/webgpu/pointShadowPass.js +280 -0
- package/dist/render/backend/webgpu/postPass.d.ts +342 -0
- package/dist/render/backend/webgpu/postPass.js +366 -0
- package/dist/render/backend/webgpu/probePass.d.ts +108 -0
- package/dist/render/backend/webgpu/probePass.js +188 -0
- package/dist/render/backend/webgpu/reflectionPass.d.ts +29 -0
- package/dist/render/backend/webgpu/reflectionPass.js +87 -0
- package/dist/render/backend/webgpu/renderer.d.ts +3082 -0
- package/dist/render/backend/webgpu/renderer.js +10911 -0
- package/dist/render/backend/webgpu/scatterPass.d.ts +95 -0
- package/dist/render/backend/webgpu/scatterPass.js +266 -0
- package/dist/render/backend/webgpu/sdfTextPass.d.ts +86 -0
- package/dist/render/backend/webgpu/sdfTextPass.js +190 -0
- package/dist/render/backend/webgpu/shaderModules.d.ts +9 -0
- package/dist/render/backend/webgpu/shaderModules.js +45 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +23 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +208 -0
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +67 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +97 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +17 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +68 -0
- package/dist/render/backend/webgpu/skyPass.d.ts +102 -0
- package/dist/render/backend/webgpu/skyPass.js +70 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +41 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.js +228 -0
- package/dist/render/backend/webgpu/textPass.d.ts +73 -0
- package/dist/render/backend/webgpu/textPass.js +190 -0
- package/dist/render/backend/webgpu/uniformRing.d.ts +133 -0
- package/dist/render/backend/webgpu/uniformRing.js +208 -0
- package/dist/render/backend/webgpu/waterPass.d.ts +73 -0
- package/dist/render/backend/webgpu/waterPass.js +160 -0
- package/dist/render/backend/webgpu/windStreakPass.d.ts +46 -0
- package/dist/render/backend/webgpu/windStreakPass.js +153 -0
- package/dist/render/billboard.d.ts +27 -0
- package/dist/render/billboard.js +79 -0
- package/dist/render/bloomChain.d.ts +66 -0
- package/dist/render/bloomChain.js +83 -0
- package/dist/render/bloomPass.d.ts +54 -0
- package/dist/render/bloomPass.js +219 -0
- package/dist/render/boltBatch.d.ts +39 -0
- package/dist/render/boltBatch.js +121 -0
- package/dist/render/boltPool.d.ts +135 -0
- package/dist/render/boltPool.js +259 -0
- package/dist/render/boom.d.ts +46 -0
- package/dist/render/boom.js +81 -0
- package/dist/render/camera.d.ts +119 -0
- package/dist/render/camera.js +304 -0
- package/dist/render/causticsRenderer.d.ts +63 -0
- package/dist/render/causticsRenderer.js +119 -0
- package/dist/render/cinematicCamera.d.ts +102 -0
- package/dist/render/cinematicCamera.js +227 -0
- package/dist/render/clusteredLights.d.ts +260 -0
- package/dist/render/clusteredLights.js +479 -0
- package/dist/render/colourGrade.d.ts +62 -0
- package/dist/render/colourGrade.js +91 -0
- package/dist/render/compute.d.ts +74 -0
- package/dist/render/compute.js +8 -0
- package/dist/render/contextLoss.d.ts +32 -0
- package/dist/render/contextLoss.js +46 -0
- package/dist/render/debugLines.d.ts +75 -0
- package/dist/render/debugLines.js +281 -0
- package/dist/render/decalPass.d.ts +34 -0
- package/dist/render/decalPass.js +103 -0
- package/dist/render/decalProjector.d.ts +140 -0
- package/dist/render/decalProjector.js +282 -0
- package/dist/render/decalQueue.d.ts +53 -0
- package/dist/render/decalQueue.js +87 -0
- package/dist/render/depthConvention.d.ts +314 -0
- package/dist/render/depthConvention.js +346 -0
- package/dist/render/drawingBuffer.d.ts +52 -0
- package/dist/render/drawingBuffer.js +67 -0
- package/dist/render/emptyTexture.d.ts +60 -0
- package/dist/render/emptyTexture.js +103 -0
- package/dist/render/envProbeArray.d.ts +85 -0
- package/dist/render/envProbeArray.js +164 -0
- package/dist/render/equirectToCube.d.ts +54 -0
- package/dist/render/equirectToCube.js +123 -0
- package/dist/render/flockGeometry.d.ts +23 -0
- package/dist/render/flockGeometry.js +43 -0
- package/dist/render/flockRenderer.d.ts +37 -0
- package/dist/render/flockRenderer.js +70 -0
- package/dist/render/frame/arena.d.ts +32 -0
- package/dist/render/frame/arena.js +65 -0
- package/dist/render/frame/index.d.ts +18 -0
- package/dist/render/frame/index.js +4 -0
- package/dist/render/frame/replay.d.ts +34 -0
- package/dist/render/frame/replay.js +48 -0
- package/dist/render/frame/resources.d.ts +25 -0
- package/dist/render/frame/resources.js +55 -0
- package/dist/render/frame/schedule.d.ts +37 -0
- package/dist/render/frame/schedule.js +61 -0
- package/dist/render/frameView.d.ts +19 -0
- package/dist/render/frameView.js +1 -0
- package/dist/render/gizmo.d.ts +236 -0
- package/dist/render/gizmo.js +727 -0
- package/dist/render/globalMedium.d.ts +124 -0
- package/dist/render/globalMedium.js +100 -0
- package/dist/render/globalMediumPass.d.ts +101 -0
- package/dist/render/globalMediumPass.js +250 -0
- package/dist/render/gpuCapability.d.ts +111 -0
- package/dist/render/gpuCapability.js +194 -0
- package/dist/render/gpuTimer.d.ts +87 -0
- package/dist/render/gpuTimer.js +145 -0
- package/dist/render/halfFloat.d.ts +4 -0
- package/dist/render/halfFloat.js +38 -0
- package/dist/render/handleRegistry.d.ts +54 -0
- package/dist/render/handleRegistry.js +84 -0
- package/dist/render/iesProfile.d.ts +64 -0
- package/dist/render/iesProfile.js +179 -0
- package/dist/render/imageTexels.d.ts +50 -0
- package/dist/render/imageTexels.js +91 -0
- package/dist/render/instancedMesh.d.ts +48 -0
- package/dist/render/instancedMesh.js +127 -0
- package/dist/render/instances.d.ts +69 -0
- package/dist/render/instances.js +52 -0
- package/dist/render/lightBudget.d.ts +397 -0
- package/dist/render/lightBudget.js +524 -0
- package/dist/render/lightMatrix.d.ts +3 -0
- package/dist/render/lightMatrix.js +45 -0
- package/dist/render/lightVolumeDraw.d.ts +132 -0
- package/dist/render/lightVolumeDraw.js +181 -0
- package/dist/render/lineBatch.d.ts +60 -0
- package/dist/render/lineBatch.js +160 -0
- package/dist/render/linePoints.d.ts +33 -0
- package/dist/render/linePoints.js +46 -0
- package/dist/render/livePointShadowSet.d.ts +72 -0
- package/dist/render/livePointShadowSet.js +258 -0
- package/dist/render/lod.d.ts +35 -0
- package/dist/render/lod.js +65 -0
- package/dist/render/mesh.d.ts +177 -0
- package/dist/render/mesh.js +466 -0
- package/dist/render/occlusion.d.ts +81 -0
- package/dist/render/occlusion.js +378 -0
- package/dist/render/oitPass.d.ts +67 -0
- package/dist/render/oitPass.js +213 -0
- package/dist/render/orderIndependent.d.ts +92 -0
- package/dist/render/orderIndependent.js +108 -0
- package/dist/render/particleBatch.d.ts +140 -0
- package/dist/render/particleBatch.js +186 -0
- package/dist/render/particleMaterial.d.ts +54 -0
- package/dist/render/particleMaterial.js +42 -0
- package/dist/render/particlePool.d.ts +124 -0
- package/dist/render/particlePool.js +182 -0
- package/dist/render/pass.d.ts +195 -0
- package/dist/render/pass.js +9 -0
- package/dist/render/passTarget.d.ts +86 -0
- package/dist/render/passTarget.js +121 -0
- package/dist/render/pickable.d.ts +31 -0
- package/dist/render/pickable.js +172 -0
- package/dist/render/planarReflection.d.ts +59 -0
- package/dist/render/planarReflection.js +194 -0
- package/dist/render/planarReflectionDraw.d.ts +31 -0
- package/dist/render/planarReflectionDraw.js +65 -0
- package/dist/render/plumeGeometry.d.ts +25 -0
- package/dist/render/plumeGeometry.js +61 -0
- package/dist/render/plumeMaterial.d.ts +31 -0
- package/dist/render/plumeMaterial.js +64 -0
- package/dist/render/plumeRenderer.d.ts +96 -0
- package/dist/render/plumeRenderer.js +185 -0
- package/dist/render/pointLightSelection.d.ts +175 -0
- package/dist/render/pointLightSelection.js +301 -0
- package/dist/render/pointShadowArray.d.ts +95 -0
- package/dist/render/pointShadowArray.js +219 -0
- package/dist/render/pointShadowBudget.d.ts +118 -0
- package/dist/render/pointShadowBudget.js +221 -0
- package/dist/render/pointShadowImage.d.ts +265 -0
- package/dist/render/pointShadowImage.js +339 -0
- package/dist/render/pointShadowMap.d.ts +93 -0
- package/dist/render/pointShadowMap.js +167 -0
- package/dist/render/pointShadowSystem.d.ts +195 -0
- package/dist/render/pointShadowSystem.js +336 -0
- package/dist/render/prefilterEnvMap.d.ts +124 -0
- package/dist/render/prefilterEnvMap.js +146 -0
- package/dist/render/prefilterPass.d.ts +44 -0
- package/dist/render/prefilterPass.js +98 -0
- package/dist/render/probeGrid.d.ts +122 -0
- package/dist/render/probeGrid.js +174 -0
- package/dist/render/rainField.d.ts +99 -0
- package/dist/render/rainField.js +168 -0
- package/dist/render/reflectionProbe.d.ts +188 -0
- package/dist/render/reflectionProbe.js +437 -0
- package/dist/render/renderQuality.d.ts +699 -0
- package/dist/render/renderQuality.js +293 -0
- package/dist/render/rendererHarness.d.ts +39 -0
- package/dist/render/rendererHarness.js +185 -0
- package/dist/render/resolutionGovernor.d.ts +107 -0
- package/dist/render/resolutionGovernor.js +144 -0
- package/dist/render/scatterDeform.d.ts +17 -0
- package/dist/render/scatterDeform.js +46 -0
- package/dist/render/sceneTarget.d.ts +288 -0
- package/dist/render/sceneTarget.js +856 -0
- package/dist/render/screenSpaceReflection.d.ts +233 -0
- package/dist/render/screenSpaceReflection.js +415 -0
- package/dist/render/sdfFont.d.ts +77 -0
- package/dist/render/sdfFont.js +86 -0
- package/dist/render/sdfTextLayout.d.ts +46 -0
- package/dist/render/sdfTextLayout.js +145 -0
- package/dist/render/sdfTextRenderer.d.ts +75 -0
- package/dist/render/sdfTextRenderer.js +194 -0
- package/dist/render/seaState.d.ts +25 -0
- package/dist/render/seaState.js +34 -0
- package/dist/render/segmentQuads.d.ts +29 -0
- package/dist/render/segmentQuads.js +75 -0
- package/dist/render/shader.d.ts +5 -0
- package/dist/render/shader.js +172 -0
- package/dist/render/shaders/ambientOcclusion.d.ts +45 -0
- package/dist/render/shaders/ambientOcclusion.js +352 -0
- package/dist/render/shaders/arcane.d.ts +19 -0
- package/dist/render/shaders/arcane.js +102 -0
- package/dist/render/shaders/bloom.d.ts +70 -0
- package/dist/render/shaders/bloom.js +227 -0
- package/dist/render/shaders/bolt.d.ts +17 -0
- package/dist/render/shaders/bolt.js +161 -0
- package/dist/render/shaders/caustics.d.ts +38 -0
- package/dist/render/shaders/caustics.js +167 -0
- package/dist/render/shaders/conditionals.d.ts +21 -0
- package/dist/render/shaders/conditionals.js +55 -0
- package/dist/render/shaders/decalProject.d.ts +25 -0
- package/dist/render/shaders/decalProject.js +125 -0
- package/dist/render/shaders/depth.d.ts +14 -0
- package/dist/render/shaders/depth.js +114 -0
- package/dist/render/shaders/depthOfField.d.ts +41 -0
- package/dist/render/shaders/depthOfField.js +139 -0
- package/dist/render/shaders/film.d.ts +15 -0
- package/dist/render/shaders/film.js +283 -0
- package/dist/render/shaders/fire.d.ts +12 -0
- package/dist/render/shaders/fire.js +178 -0
- package/dist/render/shaders/flat/directionalShadow.d.ts +1 -0
- package/dist/render/shaders/flat/directionalShadow.js +195 -0
- package/dist/render/shaders/flat/index.d.ts +135 -0
- package/dist/render/shaders/flat/index.js +387 -0
- package/dist/render/shaders/flat/lobes.d.ts +1 -0
- package/dist/render/shaders/flat/lobes.js +398 -0
- package/dist/render/shaders/flat/main.d.ts +2 -0
- package/dist/render/shaders/flat/main.js +1630 -0
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -0
- package/dist/render/shaders/flat/pointShadow.js +322 -0
- package/dist/render/shaders/flat/preamble.d.ts +15 -0
- package/dist/render/shaders/flat/preamble.js +864 -0
- package/dist/render/shaders/flat/probeGrid.d.ts +21 -0
- package/dist/render/shaders/flat/probeGrid.js +151 -0
- package/dist/render/shaders/flat/surface.d.ts +2 -0
- package/dist/render/shaders/flat/surface.js +40 -0
- package/dist/render/shaders/flock.d.ts +11 -0
- package/dist/render/shaders/flock.js +102 -0
- package/dist/render/shaders/fog.d.ts +37 -0
- package/dist/render/shaders/fog.js +98 -0
- package/dist/render/shaders/fullscreen.d.ts +16 -0
- package/dist/render/shaders/fullscreen.js +27 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +65 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +71 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +90 -0
- package/dist/render/shaders/generated/arcane.wgsl.js +96 -0
- package/dist/render/shaders/generated/bloom.wgsl.d.ts +69 -0
- package/dist/render/shaders/generated/bloom.wgsl.js +75 -0
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +118 -0
- package/dist/render/shaders/generated/bolt.wgsl.js +124 -0
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +118 -0
- package/dist/render/shaders/generated/caustics.wgsl.js +124 -0
- package/dist/render/shaders/generated/decalProject.wgsl.d.ts +61 -0
- package/dist/render/shaders/generated/decalProject.wgsl.js +67 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +131 -0
- package/dist/render/shaders/generated/depth.wgsl.js +137 -0
- package/dist/render/shaders/generated/film.wgsl.d.ts +134 -0
- package/dist/render/shaders/generated/film.wgsl.js +140 -0
- package/dist/render/shaders/generated/fire.wgsl.d.ts +128 -0
- package/dist/render/shaders/generated/fire.wgsl.js +134 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +9163 -0
- package/dist/render/shaders/generated/flat.wgsl.js +9192 -0
- package/dist/render/shaders/generated/flock.wgsl.d.ts +73 -0
- package/dist/render/shaders/generated/flock.wgsl.js +79 -0
- package/dist/render/shaders/generated/fullscreen.wgsl.d.ts +12 -0
- package/dist/render/shaders/generated/fullscreen.wgsl.js +18 -0
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +130 -0
- package/dist/render/shaders/generated/globalMedium.wgsl.js +136 -0
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +230 -0
- package/dist/render/shaders/generated/lightVolume.wgsl.js +239 -0
- package/dist/render/shaders/generated/line.wgsl.d.ts +128 -0
- package/dist/render/shaders/generated/line.wgsl.js +134 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +57 -0
- package/dist/render/shaders/generated/oitResolve.wgsl.d.ts +23 -0
- package/dist/render/shaders/generated/oitResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/panel.wgsl.d.ts +48 -0
- package/dist/render/shaders/generated/panel.wgsl.js +54 -0
- package/dist/render/shaders/generated/particle.wgsl.d.ts +332 -0
- package/dist/render/shaders/generated/particle.wgsl.js +338 -0
- package/dist/render/shaders/generated/prefilter.wgsl.d.ts +61 -0
- package/dist/render/shaders/generated/prefilter.wgsl.js +67 -0
- package/dist/render/shaders/generated/rush.wgsl.d.ts +131 -0
- package/dist/render/shaders/generated/rush.wgsl.js +137 -0
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +195 -0
- package/dist/render/shaders/generated/scatter.wgsl.js +201 -0
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +69 -0
- package/dist/render/shaders/generated/sdfText.wgsl.js +75 -0
- package/dist/render/shaders/generated/sky.wgsl.d.ts +100 -0
- package/dist/render/shaders/generated/sky.wgsl.js +106 -0
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +85 -0
- package/dist/render/shaders/generated/smoke.wgsl.js +91 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +18 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +24 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +86 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +92 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +46 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +52 -0
- package/dist/render/shaders/generated/text.wgsl.d.ts +98 -0
- package/dist/render/shaders/generated/text.wgsl.js +104 -0
- package/dist/render/shaders/generated/water.wgsl.d.ts +247 -0
- package/dist/render/shaders/generated/water.wgsl.js +253 -0
- package/dist/render/shaders/generated/windStreaks.wgsl.d.ts +73 -0
- package/dist/render/shaders/generated/windStreaks.wgsl.js +79 -0
- package/dist/render/shaders/gerstner.d.ts +21 -0
- package/dist/render/shaders/gerstner.js +110 -0
- package/dist/render/shaders/globalMedium.d.ts +56 -0
- package/dist/render/shaders/globalMedium.js +393 -0
- package/dist/render/shaders/lightVolume.d.ts +61 -0
- package/dist/render/shaders/lightVolume.js +527 -0
- package/dist/render/shaders/line.d.ts +18 -0
- package/dist/render/shaders/line.js +122 -0
- package/dist/render/shaders/morph.d.ts +26 -0
- package/dist/render/shaders/morph.js +61 -0
- package/dist/render/shaders/octahedral.d.ts +76 -0
- package/dist/render/shaders/octahedral.js +161 -0
- package/dist/render/shaders/octahedralResolve.d.ts +25 -0
- package/dist/render/shaders/octahedralResolve.js +81 -0
- package/dist/render/shaders/oitResolve.d.ts +17 -0
- package/dist/render/shaders/oitResolve.js +42 -0
- package/dist/render/shaders/outputTransform.d.ts +37 -0
- package/dist/render/shaders/outputTransform.js +106 -0
- package/dist/render/shaders/panel.d.ts +17 -0
- package/dist/render/shaders/panel.js +50 -0
- package/dist/render/shaders/particle.d.ts +82 -0
- package/dist/render/shaders/particle.js +431 -0
- package/dist/render/shaders/prefilter.d.ts +37 -0
- package/dist/render/shaders/prefilter.js +276 -0
- package/dist/render/shaders/rush.d.ts +21 -0
- package/dist/render/shaders/rush.js +355 -0
- package/dist/render/shaders/scatter.d.ts +14 -0
- package/dist/render/shaders/scatter.js +206 -0
- package/dist/render/shaders/sdfText.d.ts +9 -0
- package/dist/render/shaders/sdfText.js +65 -0
- package/dist/render/shaders/skinning.d.ts +17 -0
- package/dist/render/shaders/skinning.js +59 -0
- package/dist/render/shaders/sky.d.ts +10 -0
- package/dist/render/shaders/sky.js +242 -0
- package/dist/render/shaders/smoke.d.ts +8 -0
- package/dist/render/shaders/smoke.js +88 -0
- package/dist/render/shaders/ssrResolve.d.ts +15 -0
- package/dist/render/shaders/ssrResolve.js +28 -0
- package/dist/render/shaders/ssrTrace.d.ts +22 -0
- package/dist/render/shaders/ssrTrace.js +230 -0
- package/dist/render/shaders/tangentFrame.d.ts +17 -0
- package/dist/render/shaders/tangentFrame.js +70 -0
- package/dist/render/shaders/temporalResolve.d.ts +1 -0
- package/dist/render/shaders/temporalResolve.js +150 -0
- package/dist/render/shaders/text.d.ts +17 -0
- package/dist/render/shaders/text.js +155 -0
- package/dist/render/shaders/vertexChannel.d.ts +39 -0
- package/dist/render/shaders/vertexChannel.js +69 -0
- package/dist/render/shaders/water.d.ts +18 -0
- package/dist/render/shaders/water.js +354 -0
- package/dist/render/shaders/windStreaks.d.ts +14 -0
- package/dist/render/shaders/windStreaks.js +129 -0
- package/dist/render/shadowCasters.d.ts +107 -0
- package/dist/render/shadowCasters.js +1 -0
- package/dist/render/shadowMap.d.ts +10 -0
- package/dist/render/shadowMap.js +80 -0
- package/dist/render/shotPlacement.d.ts +80 -0
- package/dist/render/shotPlacement.js +183 -0
- package/dist/render/skinPalette.d.ts +90 -0
- package/dist/render/skinPalette.js +136 -0
- package/dist/render/ssrPass.d.ts +59 -0
- package/dist/render/ssrPass.js +196 -0
- package/dist/render/surfaceSheet.d.ts +52 -0
- package/dist/render/surfaceSheet.js +99 -0
- package/dist/render/surfaceTexture.d.ts +197 -0
- package/dist/render/surfaceTexture.js +116 -0
- package/dist/render/temporalAa.d.ts +104 -0
- package/dist/render/temporalAa.js +194 -0
- package/dist/render/temporalPass.d.ts +54 -0
- package/dist/render/temporalPass.js +135 -0
- package/dist/render/textLayout.d.ts +88 -0
- package/dist/render/textLayout.js +201 -0
- package/dist/render/textRenderer.d.ts +32 -0
- package/dist/render/textRenderer.js +176 -0
- package/dist/render/thirdPersonCamera.d.ts +272 -0
- package/dist/render/thirdPersonCamera.js +479 -0
- package/dist/render/trample.d.ts +57 -0
- package/dist/render/trample.js +134 -0
- package/dist/render/translucentQueue.d.ts +76 -0
- package/dist/render/translucentQueue.js +90 -0
- package/dist/render/uniformVectorBudget.d.ts +105 -0
- package/dist/render/uniformVectorBudget.js +201 -0
- package/dist/render/uploadStep.d.ts +14 -0
- package/dist/render/uploadStep.js +14 -0
- package/dist/render/vertexDefaults.d.ts +48 -0
- package/dist/render/vertexDefaults.js +111 -0
- package/dist/render/visibility.d.ts +22 -0
- package/dist/render/visibility.js +47 -0
- package/dist/render/waterDraw.d.ts +57 -0
- package/dist/render/waterDraw.js +102 -0
- package/dist/render/waterGrid.d.ts +62 -0
- package/dist/render/waterGrid.js +151 -0
- package/dist/render/waterRenderer.d.ts +163 -0
- package/dist/render/waterRenderer.js +165 -0
- package/dist/render/wind.d.ts +45 -0
- package/dist/render/wind.js +32 -0
- package/dist/render/windField.d.ts +34 -0
- package/dist/render/windField.js +13 -0
- package/dist/render/windStreakDraw.d.ts +73 -0
- package/dist/render/windStreakDraw.js +86 -0
- package/dist/render/windStreakRenderer.d.ts +37 -0
- package/dist/render/windStreakRenderer.js +86 -0
- package/dist/scene/node.d.ts +128 -0
- package/dist/scene/node.js +319 -0
- package/dist/scene/visit.d.ts +38 -0
- package/dist/scene/visit.js +43 -0
- package/dist/ui/clipMime.d.ts +46 -0
- package/dist/ui/clipMime.js +88 -0
- package/dist/ui/exportTarget.d.ts +305 -0
- package/dist/ui/exportTarget.js +368 -0
- package/dist/ui/frameOverlay.d.ts +59 -0
- package/dist/ui/frameOverlay.js +76 -0
- package/dist/ui/framePacer.d.ts +142 -0
- package/dist/ui/framePacer.js +284 -0
- package/dist/ui/frameRecorder.d.ts +188 -0
- package/dist/ui/frameRecorder.js +221 -0
- package/dist/ui/fullscreen.d.ts +72 -0
- package/dist/ui/fullscreen.js +92 -0
- package/dist/ui/pixelCursor.d.ts +28 -0
- package/dist/ui/pixelCursor.js +66 -0
- package/dist/ui/splash.d.ts +86 -0
- package/dist/ui/splash.js +300 -0
- package/dist/ui/stillFrame.d.ts +22 -0
- package/dist/ui/stillFrame.js +40 -0
- package/package.json +60 -0
- package/src/behavior/behaviorTree.ts +346 -0
- package/src/build/shaderComments.ts +0 -0
- package/src/cinematic/player.ts +209 -0
- package/src/cinematic/script.ts +231 -0
- package/src/core/bootGate.ts +49 -0
- package/src/core/loadTracker.ts +144 -0
- package/src/core/loop.ts +211 -0
- package/src/core/messageQueue.ts +145 -0
- package/src/core/onceSet.ts +65 -0
- package/src/core/preferences.ts +177 -0
- package/src/core/remoteSave.ts +347 -0
- package/src/core/rng.ts +101 -0
- package/src/core/stepBudget.ts +136 -0
- package/src/core/storage.ts +114 -0
- package/src/core/tickTrace.ts +129 -0
- package/src/core/timeCurve.ts +175 -0
- package/src/core/withDefaults.ts +28 -0
- package/src/dev/contactProbe.ts +115 -0
- package/src/dev/fpsMeter.ts +101 -0
- package/src/environment/celestialClock.ts +385 -0
- package/src/geometry/decal.ts +260 -0
- package/src/geometry/filmPatch.ts +158 -0
- package/src/geometry/growable.ts +165 -0
- package/src/geometry/lightVolume.ts +161 -0
- package/src/geometry/meshBuilder.ts +1397 -0
- package/src/geometry/pixelFont.ts +189 -0
- package/src/geometry/ribbon.ts +790 -0
- package/src/geometry/shellFill.ts +265 -0
- package/src/geometry/spline.ts +659 -0
- package/src/geometry/tangents.ts +173 -0
- package/src/geometry/textMesh.ts +96 -0
- package/src/geometry/treeBuilder.ts +114 -0
- package/src/host/display.ts +180 -0
- package/src/host/files.ts +77 -0
- package/src/host/lifecycle.ts +63 -0
- package/src/host/platformServices.ts +23 -0
- package/src/host/screen.ts +134 -0
- package/src/index.ts +730 -0
- package/src/input/actionMap.ts +377 -0
- package/src/input/activeDevice.ts +86 -0
- package/src/input/gamepadIdentity.ts +131 -0
- package/src/input/gamepadMapping.ts +121 -0
- package/src/input/input.ts +872 -0
- package/src/input/inputHarness.ts +226 -0
- package/src/input/rightGesture.ts +76 -0
- package/src/input/rumble.ts +138 -0
- package/src/input/touchControls.ts +451 -0
- package/src/input/typingTarget.ts +26 -0
- package/src/math/bounds.ts +153 -0
- package/src/math/color.ts +37 -0
- package/src/math/exact.ts +274 -0
- package/src/math/frustum.ts +115 -0
- package/src/math/intersect.ts +228 -0
- package/src/math/scalar.ts +109 -0
- package/src/math/spring.ts +106 -0
- package/src/nav/navGraph.ts +168 -0
- package/src/nav/navPath.ts +217 -0
- package/src/nav/navSearch.ts +200 -0
- package/src/physics/boxSurface.ts +96 -0
- package/src/physics/colliderSurface.ts +213 -0
- package/src/physics/compositeSurface.ts +111 -0
- package/src/physics/heightSurface.ts +234 -0
- package/src/physics/ribbonSurface.ts +642 -0
- package/src/render/ambientOcclusionPass.ts +200 -0
- package/src/render/areaLights.ts +194 -0
- package/src/render/areaShadowSet.ts +405 -0
- package/src/render/atmosphere.ts +186 -0
- package/src/render/backend/api.ts +802 -0
- package/src/render/backend/budget.ts +139 -0
- package/src/render/backend/createRenderer.ts +343 -0
- package/src/render/backend/deadline.ts +37 -0
- package/src/render/backend/probe.ts +152 -0
- package/src/render/backend/select.ts +288 -0
- package/src/render/backend/timer.ts +59 -0
- package/src/render/backend/webgl2/instanced.ts +46 -0
- package/src/render/backend/webgl2/morphTexture.ts +58 -0
- package/src/render/backend/webgl2/renderer.ts +7484 -0
- package/src/render/backend/webgl2/skinPaletteTexture.ts +70 -0
- package/src/render/backend/webgpu/boltPass.ts +205 -0
- package/src/render/backend/webgpu/buffers.ts +553 -0
- package/src/render/backend/webgpu/causticsPass.ts +174 -0
- package/src/render/backend/webgpu/clusterBinner.ts +259 -0
- package/src/render/backend/webgpu/depthPass.ts +261 -0
- package/src/render/backend/webgpu/device.ts +148 -0
- package/src/render/backend/webgpu/drawCommand.ts +79 -0
- package/src/render/backend/webgpu/environmentTexels.ts +96 -0
- package/src/render/backend/webgpu/filmPass.ts +123 -0
- package/src/render/backend/webgpu/flatPass.ts +635 -0
- package/src/render/backend/webgpu/flockPass.ts +163 -0
- package/src/render/backend/webgpu/gpuTimestamps.ts +255 -0
- package/src/render/backend/webgpu/insetPass.ts +123 -0
- package/src/render/backend/webgpu/instanced.ts +55 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +203 -0
- package/src/render/backend/webgpu/linePass.ts +270 -0
- package/src/render/backend/webgpu/morphTexture.ts +61 -0
- package/src/render/backend/webgpu/panelPass.ts +138 -0
- package/src/render/backend/webgpu/particlePass.ts +370 -0
- package/src/render/backend/webgpu/pipelineCache.ts +152 -0
- package/src/render/backend/webgpu/plumePass.ts +292 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +368 -0
- package/src/render/backend/webgpu/postPass.ts +470 -0
- package/src/render/backend/webgpu/probePass.ts +218 -0
- package/src/render/backend/webgpu/reflectionPass.ts +137 -0
- package/src/render/backend/webgpu/renderer.ts +13004 -0
- package/src/render/backend/webgpu/scatterPass.ts +354 -0
- package/src/render/backend/webgpu/sdfTextPass.ts +220 -0
- package/src/render/backend/webgpu/shaderModules.ts +49 -0
- package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +219 -0
- package/src/render/backend/webgpu/skinPaletteRing.ts +105 -0
- package/src/render/backend/webgpu/skinPaletteTexture.ts +79 -0
- package/src/render/backend/webgpu/skyPass.ts +85 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +257 -0
- package/src/render/backend/webgpu/textPass.ts +224 -0
- package/src/render/backend/webgpu/uniformRing.ts +228 -0
- package/src/render/backend/webgpu/waterPass.ts +211 -0
- package/src/render/backend/webgpu/windStreakPass.ts +196 -0
- package/src/render/billboard.ts +108 -0
- package/src/render/bloomChain.ts +101 -0
- package/src/render/bloomPass.ts +256 -0
- package/src/render/boltBatch.ts +141 -0
- package/src/render/boltPool.ts +360 -0
- package/src/render/boom.ts +123 -0
- package/src/render/camera.ts +348 -0
- package/src/render/causticsRenderer.ts +171 -0
- package/src/render/cinematicCamera.ts +278 -0
- package/src/render/clusteredLights.ts +598 -0
- package/src/render/colourGrade.ts +109 -0
- package/src/render/compute.ts +83 -0
- package/src/render/contextLoss.ts +50 -0
- package/src/render/debugLines.ts +411 -0
- package/src/render/decalPass.ts +124 -0
- package/src/render/decalProjector.ts +390 -0
- package/src/render/decalQueue.ts +110 -0
- package/src/render/depthConvention.ts +383 -0
- package/src/render/drawingBuffer.ts +89 -0
- package/src/render/emptyTexture.ts +130 -0
- package/src/render/envProbeArray.ts +201 -0
- package/src/render/equirectToCube.ts +160 -0
- package/src/render/flockGeometry.ts +53 -0
- package/src/render/flockRenderer.ts +111 -0
- package/src/render/frame/arena.ts +93 -0
- package/src/render/frame/index.ts +29 -0
- package/src/render/frame/replay.ts +50 -0
- package/src/render/frame/resources.ts +61 -0
- package/src/render/frame/schedule.ts +85 -0
- package/src/render/frameView.ts +20 -0
- package/src/render/gizmo.ts +923 -0
- package/src/render/globalMedium.ts +154 -0
- package/src/render/globalMediumPass.ts +323 -0
- package/src/render/gpuCapability.ts +244 -0
- package/src/render/gpuTimer.ts +182 -0
- package/src/render/halfFloat.ts +39 -0
- package/src/render/handleRegistry.ts +96 -0
- package/src/render/iesProfile.ts +215 -0
- package/src/render/imageTexels.ts +109 -0
- package/src/render/instancedMesh.ts +187 -0
- package/src/render/instances.ts +92 -0
- package/src/render/lightBudget.ts +655 -0
- package/src/render/lightMatrix.ts +69 -0
- package/src/render/lightVolumeDraw.ts +216 -0
- package/src/render/lineBatch.ts +180 -0
- package/src/render/linePoints.ts +61 -0
- package/src/render/livePointShadowSet.ts +285 -0
- package/src/render/lod.ts +86 -0
- package/src/render/mesh.ts +536 -0
- package/src/render/occlusion.ts +406 -0
- package/src/render/oitPass.ts +230 -0
- package/src/render/orderIndependent.ts +136 -0
- package/src/render/particleBatch.ts +299 -0
- package/src/render/particleMaterial.ts +84 -0
- package/src/render/particlePool.ts +291 -0
- package/src/render/pass.ts +203 -0
- package/src/render/passTarget.ts +225 -0
- package/src/render/pickable.ts +222 -0
- package/src/render/planarReflection.ts +233 -0
- package/src/render/planarReflectionDraw.ts +87 -0
- package/src/render/plumeGeometry.ts +75 -0
- package/src/render/plumeMaterial.ts +99 -0
- package/src/render/plumeRenderer.ts +272 -0
- package/src/render/pointLightSelection.ts +447 -0
- package/src/render/pointShadowArray.ts +276 -0
- package/src/render/pointShadowBudget.ts +305 -0
- package/src/render/pointShadowImage.ts +470 -0
- package/src/render/pointShadowMap.ts +228 -0
- package/src/render/pointShadowSystem.ts +408 -0
- package/src/render/prefilterEnvMap.ts +162 -0
- package/src/render/prefilterPass.ts +115 -0
- package/src/render/probeGrid.ts +248 -0
- package/src/render/rainField.ts +240 -0
- package/src/render/reflectionProbe.ts +530 -0
- package/src/render/renderQuality.ts +1054 -0
- package/src/render/rendererHarness.ts +214 -0
- package/src/render/resolutionGovernor.ts +189 -0
- package/src/render/scatterDeform.ts +72 -0
- package/src/render/sceneTarget.ts +1032 -0
- package/src/render/screenSpaceReflection.ts +524 -0
- package/src/render/sdfFont.ts +157 -0
- package/src/render/sdfTextLayout.ts +206 -0
- package/src/render/sdfTextRenderer.ts +233 -0
- package/src/render/seaState.ts +55 -0
- package/src/render/segmentQuads.ts +92 -0
- package/src/render/shader.ts +190 -0
- package/src/render/shaders/ambientOcclusion.ts +354 -0
- package/src/render/shaders/arcane.ts +103 -0
- package/src/render/shaders/bloom.ts +232 -0
- package/src/render/shaders/bolt.ts +164 -0
- package/src/render/shaders/caustics.ts +169 -0
- package/src/render/shaders/conditionals.ts +58 -0
- package/src/render/shaders/decalProject.ts +127 -0
- package/src/render/shaders/depth.ts +131 -0
- package/src/render/shaders/depthOfField.ts +141 -0
- package/src/render/shaders/film.ts +285 -0
- package/src/render/shaders/fire.ts +180 -0
- package/src/render/shaders/flat/directionalShadow.ts +196 -0
- package/src/render/shaders/flat/index.ts +502 -0
- package/src/render/shaders/flat/lobes.ts +399 -0
- package/src/render/shaders/flat/main.ts +1631 -0
- package/src/render/shaders/flat/pointShadow.ts +323 -0
- package/src/render/shaders/flat/preamble.ts +875 -0
- package/src/render/shaders/flat/probeGrid.ts +152 -0
- package/src/render/shaders/flat/surface.ts +41 -0
- package/src/render/shaders/flock.ts +103 -0
- package/src/render/shaders/fog.ts +99 -0
- package/src/render/shaders/fullscreen.ts +28 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +74 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +98 -0
- package/src/render/shaders/generated/bloom.wgsl.ts +79 -0
- package/src/render/shaders/generated/bolt.wgsl.ts +127 -0
- package/src/render/shaders/generated/caustics.wgsl.ts +127 -0
- package/src/render/shaders/generated/decalProject.wgsl.ts +69 -0
- package/src/render/shaders/generated/depth.wgsl.ts +142 -0
- package/src/render/shaders/generated/film.wgsl.ts +143 -0
- package/src/render/shaders/generated/fire.wgsl.ts +137 -0
- package/src/render/shaders/generated/flat.wgsl.ts +9195 -0
- package/src/render/shaders/generated/flock.wgsl.ts +82 -0
- package/src/render/shaders/generated/fullscreen.wgsl.ts +20 -0
- package/src/render/shaders/generated/globalMedium.wgsl.ts +139 -0
- package/src/render/shaders/generated/lightVolume.wgsl.ts +242 -0
- package/src/render/shaders/generated/line.wgsl.ts +137 -0
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +60 -0
- package/src/render/shaders/generated/oitResolve.wgsl.ts +31 -0
- package/src/render/shaders/generated/panel.wgsl.ts +57 -0
- package/src/render/shaders/generated/particle.wgsl.ts +343 -0
- package/src/render/shaders/generated/prefilter.wgsl.ts +69 -0
- package/src/render/shaders/generated/rush.wgsl.ts +139 -0
- package/src/render/shaders/generated/scatter.wgsl.ts +205 -0
- package/src/render/shaders/generated/sdfText.wgsl.ts +78 -0
- package/src/render/shaders/generated/sky.wgsl.ts +109 -0
- package/src/render/shaders/generated/smoke.wgsl.ts +93 -0
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +26 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +94 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +54 -0
- package/src/render/shaders/generated/text.wgsl.ts +107 -0
- package/src/render/shaders/generated/water.wgsl.ts +256 -0
- package/src/render/shaders/generated/windStreaks.wgsl.ts +82 -0
- package/src/render/shaders/gerstner.ts +110 -0
- package/src/render/shaders/globalMedium.ts +396 -0
- package/src/render/shaders/lightVolume.ts +546 -0
- package/src/render/shaders/line.ts +125 -0
- package/src/render/shaders/morph.ts +62 -0
- package/src/render/shaders/octahedral.ts +170 -0
- package/src/render/shaders/octahedralResolve.ts +83 -0
- package/src/render/shaders/oitResolve.ts +42 -0
- package/src/render/shaders/outputTransform.ts +107 -0
- package/src/render/shaders/panel.ts +51 -0
- package/src/render/shaders/particle.ts +438 -0
- package/src/render/shaders/prefilter.ts +278 -0
- package/src/render/shaders/rush.ts +357 -0
- package/src/render/shaders/scatter.ts +211 -0
- package/src/render/shaders/sdfText.ts +68 -0
- package/src/render/shaders/skinning.ts +59 -0
- package/src/render/shaders/sky.ts +243 -0
- package/src/render/shaders/smoke.ts +89 -0
- package/src/render/shaders/ssrResolve.ts +28 -0
- package/src/render/shaders/ssrTrace.ts +233 -0
- package/src/render/shaders/tangentFrame.ts +70 -0
- package/src/render/shaders/temporalResolve.ts +151 -0
- package/src/render/shaders/text.ts +156 -0
- package/src/render/shaders/vertexChannel.ts +71 -0
- package/src/render/shaders/water.ts +356 -0
- package/src/render/shaders/windStreaks.ts +130 -0
- package/src/render/shadowCasters.ts +124 -0
- package/src/render/shadowMap.ts +89 -0
- package/src/render/shotPlacement.ts +266 -0
- package/src/render/skinPalette.ts +157 -0
- package/src/render/ssrPass.ts +241 -0
- package/src/render/surfaceSheet.ts +158 -0
- package/src/render/surfaceTexture.ts +310 -0
- package/src/render/temporalAa.ts +221 -0
- package/src/render/temporalPass.ts +153 -0
- package/src/render/textLayout.ts +239 -0
- package/src/render/textRenderer.ts +201 -0
- package/src/render/thirdPersonCamera.ts +578 -0
- package/src/render/trample.ts +140 -0
- package/src/render/translucentQueue.ts +161 -0
- package/src/render/uniformVectorBudget.ts +246 -0
- package/src/render/uploadStep.ts +14 -0
- package/src/render/vertexDefaults.ts +115 -0
- package/src/render/visibility.ts +56 -0
- package/src/render/waterDraw.ts +167 -0
- package/src/render/waterGrid.ts +180 -0
- package/src/render/waterRenderer.ts +352 -0
- package/src/render/wind.ts +79 -0
- package/src/render/windField.ts +50 -0
- package/src/render/windStreakDraw.ts +131 -0
- package/src/render/windStreakRenderer.ts +130 -0
- package/src/scene/node.ts +336 -0
- package/src/scene/visit.ts +82 -0
- package/src/ui/clipMime.ts +109 -0
- package/src/ui/exportTarget.ts +491 -0
- package/src/ui/frameOverlay.ts +159 -0
- package/src/ui/framePacer.ts +351 -0
- package/src/ui/frameRecorder.ts +333 -0
- package/src/ui/fullscreen.ts +113 -0
- package/src/ui/pixelCursor.ts +99 -0
- package/src/ui/splash.ts +347 -0
- package/src/ui/stillFrame.ts +44 -0
|
@@ -0,0 +1,3027 @@
|
|
|
1
|
+
import { FrameBudget } from '../budget.ts';
|
|
2
|
+
import type { ReadonlyMat4 } from 'gl-matrix';
|
|
3
|
+
import type { Camera } from '../../camera.ts';
|
|
4
|
+
import type { PickHit, PickableSource } from '../../pickable.ts';
|
|
5
|
+
import type { ReflectiveSurface } from '../../screenSpaceReflection.ts';
|
|
6
|
+
import type { DecalProjector } from '../../decalProjector.ts';
|
|
7
|
+
import type { Bounds } from '../../../math/bounds.ts';
|
|
8
|
+
import type { ComputeDefinition, ComputeHandle } from '../../compute.ts';
|
|
9
|
+
import type { PassDefinition, PassHandle } from '../../pass.ts';
|
|
10
|
+
import type { Vec3 } from '../../../math/color.ts';
|
|
11
|
+
import { GpuTimer } from '../../gpuTimer.ts';
|
|
12
|
+
import type { ColourGradeLut } from '../../colourGrade.ts';
|
|
13
|
+
/**
|
|
14
|
+
* How far the rush's outermost tap reaches at full strength, in UV.
|
|
15
|
+
*
|
|
16
|
+
* In UV rather than pixels so the smear is the same fraction of the screen at any
|
|
17
|
+
* resolution — a blur measured in pixels is twice as strong on a phone at DPR 2, which is
|
|
18
|
+
* exactly backwards from where the budget is.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* How far a camera-blur smear may reach, in UV.
|
|
22
|
+
*
|
|
23
|
+
* A ceiling rather than a scale: a fast turn can reproject a pixel most of the way across
|
|
24
|
+
* the screen, and sampling that far turns a frame into streaks rather than into motion.
|
|
25
|
+
* Beyond this the velocity is shortened and the direction kept.
|
|
26
|
+
*/
|
|
27
|
+
import type { RendererApi } from '../api.ts';
|
|
28
|
+
import type { ProbeBakeOptions } from '../../reflectionProbe.ts';
|
|
29
|
+
import type { ProbeGridOptions } from '../../probeGrid.ts';
|
|
30
|
+
import type { AreaLightBuffer, AreaLightSource } from '../../areaLights.ts';
|
|
31
|
+
import type { PhotometricProfile } from '../../iesProfile.ts';
|
|
32
|
+
import type { ShadowLight } from '../../pointShadowSystem.ts';
|
|
33
|
+
import type { ShadowCasters } from '../../shadowCasters.ts';
|
|
34
|
+
import type { RenderQuality, RenderQualityOptions } from '../../renderQuality.ts';
|
|
35
|
+
import { Mesh } from '../../mesh.ts';
|
|
36
|
+
import type { IncrementalMesh } from '../../mesh.ts';
|
|
37
|
+
import type { MeshData } from '../../mesh.ts';
|
|
38
|
+
import type { MeshOptions } from '../api.ts';
|
|
39
|
+
import { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
40
|
+
import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
41
|
+
import { PlumeRenderer } from '../../plumeRenderer.ts';
|
|
42
|
+
import type { PlumeOptions, PlumePlacement } from '../../plumeRenderer.ts';
|
|
43
|
+
import { WaterRenderer } from '../../waterRenderer.ts';
|
|
44
|
+
import type { WaterBody } from '../../waterRenderer.ts';
|
|
45
|
+
import { CausticsRenderer } from '../../causticsRenderer.ts';
|
|
46
|
+
import type { CausticSheet } from '../../causticsRenderer.ts';
|
|
47
|
+
import type { Atmosphere } from '../../atmosphere.ts';
|
|
48
|
+
import { InstancedMesh } from '../../instancedMesh.ts';
|
|
49
|
+
import { ParticleBatch } from '../../particleBatch.ts';
|
|
50
|
+
import type { ParticleBatchOptions } from '../../particleBatch.ts';
|
|
51
|
+
import type { ParticleInstances } from '../../particlePool.ts';
|
|
52
|
+
import { BoltBatch } from '../../boltBatch.ts';
|
|
53
|
+
import type { BoltSegments } from '../../boltPool.ts';
|
|
54
|
+
import { LineBatch } from '../../lineBatch.ts';
|
|
55
|
+
import type { LineSegments } from '../../linePoints.ts';
|
|
56
|
+
import type { InstanceData } from '../../instancedMesh.ts';
|
|
57
|
+
import { FlockRenderer } from '../../flockRenderer.ts';
|
|
58
|
+
import { TextRenderer } from '../../textRenderer.ts';
|
|
59
|
+
import { SdfTextRenderer } from '../../sdfTextRenderer.ts';
|
|
60
|
+
import type { SdfFont } from '../../sdfFont.ts';
|
|
61
|
+
import type { SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
62
|
+
import type { RenderBackend } from '../api.ts';
|
|
63
|
+
import type { TextStyle } from '../../textLayout.ts';
|
|
64
|
+
import { WindStreakRenderer } from '../../windStreakRenderer.ts';
|
|
65
|
+
import type { WindStreakOptions } from '../../windStreakRenderer.ts';
|
|
66
|
+
import type { WindField } from '../../windField.ts';
|
|
67
|
+
import type { FlockParams } from '../../flockRenderer.ts';
|
|
68
|
+
import { InstancedBatch } from './instanced.ts';
|
|
69
|
+
import type { MeshInstances } from '../../instances.ts';
|
|
70
|
+
/** Reused each frame: which shader light slot each bound cubemap serves. */
|
|
71
|
+
export type { ShadowCasterSink, ShadowCasters } from '../../shadowCasters.ts';
|
|
72
|
+
/** Lighting and atmosphere for a frame. The game decides what goes in it. */
|
|
73
|
+
export interface Environment extends Atmosphere {
|
|
74
|
+
/** Dominant outdoor directional source: sun by day, moon by night. */
|
|
75
|
+
directionalDir: Vec3;
|
|
76
|
+
directionalColor: Vec3;
|
|
77
|
+
ambient: Vec3;
|
|
78
|
+
/**
|
|
79
|
+
* Ambient arriving from below. Omitted, it matches `ambient` and the fill is uniform.
|
|
80
|
+
*
|
|
81
|
+
* A hemispheric fill is the difference between a room whose floor and ceiling are made
|
|
82
|
+
* of different things and one where they are the same colour at different brightness.
|
|
83
|
+
*/
|
|
84
|
+
ambientGround?: Vec3;
|
|
85
|
+
/** Strength of self-illuminated geometry, once `nightFactor` lets it through. */
|
|
86
|
+
emissiveGain: number;
|
|
87
|
+
/**
|
|
88
|
+
* **Whether self-illuminated geometry emits at all. Despite the name, this is not a clock.**
|
|
89
|
+
*
|
|
90
|
+
* It reads as a time of day and it is the emissive master switch: `flat.ts` multiplies every
|
|
91
|
+
* emissive term by it, so at 0 nothing in the world glows, whatever `emissiveGain` says and
|
|
92
|
+
* whatever a mesh declared. The name is accurate for the case it was written for, an outdoor
|
|
93
|
+
* world whose lamps come on at dusk, and it is a trap for every other one.
|
|
94
|
+
*
|
|
95
|
+
* **A daylit scene containing a lit thing must set this to 1 and live with the name.** A
|
|
96
|
+
* studio with softboxes, a shop sign, a screen wall, a subway platform, a lava field: each is
|
|
97
|
+
* self-illuminated at noon. Reported from outside after an evening lost to a white cyclorama
|
|
98
|
+
* that shipped with four grey plastic rectangles where its lights should have been, because
|
|
99
|
+
* the field describing the time of day had been set correctly.
|
|
100
|
+
*
|
|
101
|
+
* It is not renamed because it is on the public surface and a second name for one field is
|
|
102
|
+
* worse than an inaccurate one. Read it as "does emissive emit", and use `emissiveGain` for
|
|
103
|
+
* how strongly.
|
|
104
|
+
*/
|
|
105
|
+
nightFactor: number;
|
|
106
|
+
/**
|
|
107
|
+
* Emission that only shows where the dominant light does not reach, 0 and off by default.
|
|
108
|
+
*
|
|
109
|
+
* **The case it exists for is a lamp field on something that turns.** City lights are a
|
|
110
|
+
* property of the ground, so they are authored into the surface where the ground is. Whether
|
|
111
|
+
* they should be *visible* is a property of where the sun is, which the ground turns past, and
|
|
112
|
+
* nothing could say the second thing: `emissive` is per vertex and the light direction is per
|
|
113
|
+
* frame, and no term multiplied them.
|
|
114
|
+
*
|
|
115
|
+
* Reported from outside with the arithmetic, on a rotating planet whose night lights were
|
|
116
|
+
* built, photographed and then deleted because they could not be made to work. With an ambient
|
|
117
|
+
* of 0.07 and a directional of 1.15, a patch bright enough to read on the night side arrived at
|
|
118
|
+
* sixteen times that on the day side, as an orange blot on a continent. The ratio is fixed by
|
|
119
|
+
* the lighting, so no choice of value separates the two.
|
|
120
|
+
*
|
|
121
|
+
* Added to the ordinary emissive rather than replacing it, and weighted by how far the surface
|
|
122
|
+
* faces away from `directionalDir`, so the same mesh can carry day-side paint and night-side
|
|
123
|
+
* lamps. It is deliberately a hard cut at the terminator: softening it is what puts the lights
|
|
124
|
+
* back on the day side, which is the defect.
|
|
125
|
+
*
|
|
126
|
+
* Independent of `nightFactor`, which is a fact about the world's clock. This is a fact about
|
|
127
|
+
* where a surface is pointing, and a planet has a night side at any hour.
|
|
128
|
+
*/
|
|
129
|
+
nightEmissive?: number;
|
|
130
|
+
/** World → light clip space, from `computeLightMatrix`. */
|
|
131
|
+
lightViewProj: ReadonlyMat4;
|
|
132
|
+
/** 0 disables shadow sampling — set to 0 whenever the map is not refreshed. */
|
|
133
|
+
shadowStrength: number;
|
|
134
|
+
/** Linear depth span returned by `computeLightMatrix`, in metres. */
|
|
135
|
+
shadowDepthSpan: number;
|
|
136
|
+
/** World-space box lit independently of the clock; 0 gain disables it. */
|
|
137
|
+
highlightMin: Vec3;
|
|
138
|
+
highlightMax: Vec3;
|
|
139
|
+
highlightGain: number;
|
|
140
|
+
/** Active point lights: 3 floats each for position/colour, 1 for radius. */
|
|
141
|
+
lightCount: number;
|
|
142
|
+
lightPositions: Float32Array;
|
|
143
|
+
lightColors: Float32Array;
|
|
144
|
+
lightRadii: Float32Array;
|
|
145
|
+
/** Each shaded light's emitter radius in metres. See `PointLightBuffer.sourceRadii`. */
|
|
146
|
+
lightSourceRadii: Float32Array;
|
|
147
|
+
/**
|
|
148
|
+
* How present each active light is, 0 to 1, from `selectPointLights`.
|
|
149
|
+
*
|
|
150
|
+
* Shorter than `MAX_POINT_LIGHTS` means a consumer that packs lights itself and has
|
|
151
|
+
* nothing to say about presence; those lights are taken as fully present, which is
|
|
152
|
+
* what every consumer got before this existed.
|
|
153
|
+
*/
|
|
154
|
+
lightWeights: Float32Array;
|
|
155
|
+
/**
|
|
156
|
+
* Three per active light: where a spot points, normalised. Zeroes make a light a point light.
|
|
157
|
+
*
|
|
158
|
+
* Optional in spirit and required in the type, like every other member of this family: a short
|
|
159
|
+
* or absent array falls back in `resolvePointLights` to a direction of nothing and a cone that
|
|
160
|
+
* admits everything, which is the arithmetic every scene had before spot lights existed.
|
|
161
|
+
*/
|
|
162
|
+
lightDirections: Float32Array;
|
|
163
|
+
/** Two per active light: the cosine of the inner cone angle, then of the outer. */
|
|
164
|
+
lightConeCos: Float32Array;
|
|
165
|
+
/** One per active light: a row of the photometric atlas, or −1 for none. */
|
|
166
|
+
lightIesProfiles: Float32Array;
|
|
167
|
+
/**
|
|
168
|
+
* Three per active light: where an asymmetric profile's azimuth zero points, in world space.
|
|
169
|
+
*
|
|
170
|
+
* Zeroes leave a profile on its first plane, which is what every axially symmetric fixture does
|
|
171
|
+
* anyway — so a scene that has never heard of this shades exactly as it did. See
|
|
172
|
+
* `PointLightSet.lightIesAxes` for why it cannot be derived from the light's direction.
|
|
173
|
+
*/
|
|
174
|
+
lightIesAxes: Float32Array;
|
|
175
|
+
/** One per active light: a tile of the cookie atlas, or −1 for none. */
|
|
176
|
+
lightCookies: Float32Array;
|
|
177
|
+
/**
|
|
178
|
+
* The rectangular emitters this frame, or null for none.
|
|
179
|
+
*
|
|
180
|
+
* A buffer rather than loose arrays, unlike the point lights: those grew their arrays one at a
|
|
181
|
+
* time over years and the family is now seven wide, which is exactly the shape `bindPointLights`
|
|
182
|
+
* exists to stop anybody forgetting a member of. One object cannot be half-passed.
|
|
183
|
+
*/
|
|
184
|
+
areaLights: AreaLightBuffer | null;
|
|
185
|
+
/**
|
|
186
|
+
* For each active light slot, which world light it holds. The shadow system
|
|
187
|
+
* needs this to translate a world light into the shader slot the fragment
|
|
188
|
+
* loop is iterating.
|
|
189
|
+
*/
|
|
190
|
+
activeLightWorldIndices: Int32Array;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* A complete `Environment`, with every field at a value that means "nothing special".
|
|
194
|
+
*
|
|
195
|
+
* The engine had no way to *make* one of these, so every caller assembled the whole
|
|
196
|
+
* interface by hand — and the first caller that wanted a small, self-contained lit scene
|
|
197
|
+
* (a character portrait in a box on a menu) wrote a partial literal, cast it to
|
|
198
|
+
* `Environment` to satisfy the compiler, and put `undefined` into `uniformMatrix4fv`. The
|
|
199
|
+
* type had been describing the contract all along; nothing was helping anyone meet it.
|
|
200
|
+
*
|
|
201
|
+
* So: a factory with a neutral baseline — no shadows, no point lights, an identity light
|
|
202
|
+
* matrix, no fog — and shallow overrides on top. A caller that forgets a field now gets
|
|
203
|
+
* the harmless value rather than a WebGL error, and a caller that needs the day's sky
|
|
204
|
+
* still assembles exactly what it did before.
|
|
205
|
+
*
|
|
206
|
+
* Fresh arrays every call. Sharing one `lightPositions` between two environments is the
|
|
207
|
+
* kind of aliasing that shows up as one scene's lamps appearing in another's.
|
|
208
|
+
*/
|
|
209
|
+
export declare function createEnvironment(overrides?: Partial<Environment>): Environment;
|
|
210
|
+
/** A rectangle of the canvas, in CSS pixels from its top-left corner — i.e. a `DOMRect`. */
|
|
211
|
+
export interface InsetRect {
|
|
212
|
+
readonly left: number;
|
|
213
|
+
readonly top: number;
|
|
214
|
+
readonly width: number;
|
|
215
|
+
readonly height: number;
|
|
216
|
+
}
|
|
217
|
+
export interface SkyColors {
|
|
218
|
+
top: Vec3;
|
|
219
|
+
horizon: Vec3;
|
|
220
|
+
deep: Vec3;
|
|
221
|
+
sunDir: Vec3;
|
|
222
|
+
sunColor: Vec3;
|
|
223
|
+
/** Apparent angular radius in radians; independent from emitted light. */
|
|
224
|
+
sunAngularRadius: number;
|
|
225
|
+
moonDir: Vec3;
|
|
226
|
+
moonColor: Vec3;
|
|
227
|
+
/** Apparent angular radius in radians; independent from emitted light. */
|
|
228
|
+
moonAngularRadius: number;
|
|
229
|
+
/** 0..1 across the lunar cycle. */
|
|
230
|
+
moonPhase: number;
|
|
231
|
+
nightFactor: number;
|
|
232
|
+
/** Accumulated cloud drift, metres of texture travel on each axis. */
|
|
233
|
+
cloudOffsetX: number;
|
|
234
|
+
cloudOffsetZ: number;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The whole WebGL2 surface of the engine. No raw `gl` calls may exist outside
|
|
238
|
+
* this directory (AGENTS.md): a shadow pass, a flat-shaded mesh pass, a sky
|
|
239
|
+
* pass, and any number of plume batches.
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* How far a press reaches into the foliage, metres, and how much of a plant it takes.
|
|
243
|
+
*
|
|
244
|
+
* A little wider than a body, so a character leaves a path rather than a line of holes;
|
|
245
|
+
* 80% of the height, because grass that flattens completely reads as grass that has
|
|
246
|
+
* been deleted. Passing through foliage has to move it, so that it reads as being
|
|
247
|
+
* pressed down toward the ground rather than as vanishing.
|
|
248
|
+
*/
|
|
249
|
+
/**
|
|
250
|
+
* How far an overlay mesh is pulled toward the camera — see `drawMesh`.
|
|
251
|
+
*
|
|
252
|
+
* Both terms are in the units `glPolygonOffset` takes: the slope factor multiplies
|
|
253
|
+
* the polygon's own depth gradient, and the constant is in smallest-resolvable-depth
|
|
254
|
+
* units. Negative pulls toward the viewer.
|
|
255
|
+
*
|
|
256
|
+
* Deliberately small. It has to beat rounding at a coincident surface, which is one
|
|
257
|
+
* or two units, and it must not be large enough to pull a kerb out through a wall it
|
|
258
|
+
* genuinely stands behind — two units is a fraction of a millimetre at the distances
|
|
259
|
+
* a kerb is read at, and grows with distance exactly as the ambiguity does.
|
|
260
|
+
*/
|
|
261
|
+
/**
|
|
262
|
+
* Depth offset applied per layer — see `drawMesh`.
|
|
263
|
+
*
|
|
264
|
+
* **The slope term does not scale with the layer, and that is the whole point of
|
|
265
|
+
* splitting these two.** It multiplies the polygon's own depth gradient, which at a
|
|
266
|
+
* grazing angle is enormous: one pixel of a deck seen almost edge-on spans a large
|
|
267
|
+
* depth range, so a slope factor that is safe head-on can drag a flush marking metres
|
|
268
|
+
* toward the camera and out in front of geometry that genuinely stands over it. Scaled
|
|
269
|
+
* by the layer as well, it did exactly that: on a terrace, elements *underneath* the
|
|
270
|
+
* main platform showed through it at certain camera angles, as if trying to emerge
|
|
271
|
+
* from beneath something standing over them.
|
|
272
|
+
*
|
|
273
|
+
* So the slope term is **zero**, and the constant term carries the whole ordering.
|
|
274
|
+
*
|
|
275
|
+
* Zero rather than one, in the end, because the slope factor is the only part of this
|
|
276
|
+
* that depends on where the camera is — it multiplies the polygon's own depth gradient —
|
|
277
|
+
* and "certain camera angles" was the one constant in every report. A constant offset
|
|
278
|
+
* biases a layer by the same amount from every direction, so it can order two surfaces
|
|
279
|
+
* without ever being able to drag one out through the other as the view swings round.
|
|
280
|
+
*
|
|
281
|
+
* Kept as a named constant at zero rather than deleted: the next person to see a flush
|
|
282
|
+
* marking lose to its floor at a grazing angle will reach for a slope factor, and this is
|
|
283
|
+
* where the reason not to is written down.
|
|
284
|
+
*/
|
|
285
|
+
/**
|
|
286
|
+
* Re-exported from the depth convention, which is where the ceiling and the offset it scales now
|
|
287
|
+
* live together — one decision, read by this backend around a draw and by the other one inside a
|
|
288
|
+
* pipeline. Kept exported from here because the barrel has always named it from this module.
|
|
289
|
+
*/
|
|
290
|
+
export { MAX_DEPTH_LAYER } from '../../depthConvention.ts';
|
|
291
|
+
/**
|
|
292
|
+
* What a film should do beyond shimmering, which until now was nothing.
|
|
293
|
+
*
|
|
294
|
+
* **A wet street at night is defined by everything above it appearing twice**, and `sheen`
|
|
295
|
+
* cannot supply that: it is an interference pattern, so raising it makes a road oilier rather
|
|
296
|
+
* than wetter. This is the other half, kept a separate number for exactly that reason.
|
|
297
|
+
*
|
|
298
|
+
* Requires a planar reflection to have been rendered this frame for the plane the film lies on,
|
|
299
|
+
* which needs `RenderQuality.planarReflections` where the scene has no water to allocate one.
|
|
300
|
+
* Absent or zero means the pass behaves as it always did, so nothing that predates it moves.
|
|
301
|
+
*
|
|
302
|
+
* **New, and honestly reported: the plumbing is proven and the look is not.** Measured on
|
|
303
|
+
* `demo/nightStreet.ts` held at frame 900, turning it from 0 to 1 changes 46,129 pixels and the
|
|
304
|
+
* largest change to any channel is **12 of 255**, so it is present and it does not yet read as a
|
|
305
|
+
* mirror. The reason is believed to be the film's own coverage alpha, which scales the mirrored
|
|
306
|
+
* term along with everything else, and a camera angle that keeps the grazing weight small. That
|
|
307
|
+
* is a weighting to tune rather than a mechanism to find, and the rig is where to tune it:
|
|
308
|
+
* `?scene=7&model=car&hold=900&wet=1` against `&wet=0`. Do not assume it looks right because it
|
|
309
|
+
* compiles.
|
|
310
|
+
*/
|
|
311
|
+
export interface FilmOptions {
|
|
312
|
+
/**
|
|
313
|
+
* How much of the mirrored scene shows through, 0 to 1.
|
|
314
|
+
*
|
|
315
|
+
* Weighted by the viewing angle on top of this, because that is what a reflection does: a wet
|
|
316
|
+
* surface underfoot shows almost nothing and the same surface down the street shows everything.
|
|
317
|
+
*/
|
|
318
|
+
readonly reflectionStrength?: number;
|
|
319
|
+
/**
|
|
320
|
+
* The plane the film lies on, defaulting to 0.
|
|
321
|
+
*
|
|
322
|
+
* Only used to check it against the plane the reflection was rendered for, so a film on a
|
|
323
|
+
* different plane gets no mirror rather than somebody else's. A patch lifted a centimetre clear
|
|
324
|
+
* of the road still names the road's own height: the lift is to stop it fighting for the depth
|
|
325
|
+
* buffer, not a different plane to mirror across.
|
|
326
|
+
*/
|
|
327
|
+
readonly reflectionPlaneY?: number;
|
|
328
|
+
/**
|
|
329
|
+
* How rough the film's own surface is, 0 to 1. Zero is a mirror, which is what this pass was.
|
|
330
|
+
*
|
|
331
|
+
* A film had one finish before this and it was the wrong one for the commonest use: a wet road
|
|
332
|
+
* is water lying *in* aggregate, so the stone under it scatters the reflection into a smear.
|
|
333
|
+
* Without this, a patch of film over a textured surface met it at a hard rim with mirror on one
|
|
334
|
+
* side and stone on the other — and softening the patch's coverage does not touch it, because
|
|
335
|
+
* the alpha was never what the eye was reading there.
|
|
336
|
+
*
|
|
337
|
+
* Displaces the reflection rather than blurring it. A rough surface scatters what it reflects
|
|
338
|
+
* rather than softening it, so a lamp becomes a streak and not a soft disc; it is also the only
|
|
339
|
+
* affordable choice, since the mirror has no mip chain and an honest blur costs taps.
|
|
340
|
+
*/
|
|
341
|
+
readonly roughness?: number;
|
|
342
|
+
/**
|
|
343
|
+
* How coarse that roughness is, in bumps per metre. Defaults to 60.
|
|
344
|
+
*
|
|
345
|
+
* The same quantity `setSurfaceRelief` takes, and **matching the two is the point**: given the
|
|
346
|
+
* same field at the same scale, the water is broken up by the aggregate the dry surface beside
|
|
347
|
+
* it is already showing, and the wet and the dry stop reading as two materials. Ignored when
|
|
348
|
+
* `roughness` is zero.
|
|
349
|
+
*/
|
|
350
|
+
readonly roughnessCyclesPerMetre?: number;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The two things a volume of light can have beyond its shape. See `drawLightVolume`.
|
|
354
|
+
*
|
|
355
|
+
* Both default to nothing, and that is not only compatibility: a torch beam is a smooth cone
|
|
356
|
+
* and should stay one. These are what turn a beam into a shaft of daylight, which is a
|
|
357
|
+
* different phenomenon that happens to be drawn by the same pass.
|
|
358
|
+
*/
|
|
359
|
+
export interface LightVolumeDrawOptions {
|
|
360
|
+
/**
|
|
361
|
+
* How much structure the air in the beam has, 0 to 1. Zero computes nothing.
|
|
362
|
+
*
|
|
363
|
+
* A shaft of sun through a window is not smooth: what is lit is the dust in it, and the dust
|
|
364
|
+
* is uneven. Useful values are low, around 0.2 to 0.5; past that the beam reads as smoke.
|
|
365
|
+
*/
|
|
366
|
+
readonly dust?: number;
|
|
367
|
+
/** Metres per cell of the dust field. Around a metre for motes, several for weather. */
|
|
368
|
+
readonly dustScaleM?: number;
|
|
369
|
+
/**
|
|
370
|
+
* Where the dust field has drifted to, in metres. Defaults to standing still.
|
|
371
|
+
*
|
|
372
|
+
* A position rather than a rate, so this pass never reads a clock and a caller asking for
|
|
373
|
+
* the same instant twice gets the same frame. Air moves with the scene's wind, so the drift
|
|
374
|
+
* to hand it is the one `WindField` has already accumulated — the same value the smoke and
|
|
375
|
+
* the grass are answering to, which is what stops a shaft of dust drifting one way while the
|
|
376
|
+
* smoke beside it goes another.
|
|
377
|
+
*/
|
|
378
|
+
readonly driftM?: Vec3;
|
|
379
|
+
/**
|
|
380
|
+
* How much the sun's own shadow map cuts the volume, 0 to 1. Zero samples nothing.
|
|
381
|
+
*
|
|
382
|
+
* **The directional light, which is to say the sun.** A shaft through a window carries the
|
|
383
|
+
* bars of that window because the light did; without this the floor under a mullioned window
|
|
384
|
+
* is barred and the shaft above it is smooth, and one frame disagreeing with itself about
|
|
385
|
+
* where the light went is most of what reads as fake. A beam from a lamp is not sunlight and
|
|
386
|
+
* wants this left at 0, which is why it is a per-draw amount and not a renderer setting.
|
|
387
|
+
*
|
|
388
|
+
* Needs `env`, and needs the renderer built with `directionalShadows`. Without either it is
|
|
389
|
+
* ignored rather than throwing, because this is a frame-loop call.
|
|
390
|
+
*
|
|
391
|
+
* The occlusion is evaluated at every step of the march, so this is a shadow carried through
|
|
392
|
+
* the air rather than stamped on a surface. What it still does not do is let one volume
|
|
393
|
+
* shadow another behind it: each is integrated on its own.
|
|
394
|
+
*/
|
|
395
|
+
readonly sunShadow?: number;
|
|
396
|
+
/** Where the sun's shadow map is. Required by `sunShadow` and unused without it. */
|
|
397
|
+
readonly env?: Environment;
|
|
398
|
+
/**
|
|
399
|
+
* The air this volume stands in, and the density at which it reads at full strength.
|
|
400
|
+
*
|
|
401
|
+
* **A beam is only as visible as the medium it is lighting**, and until 2026-08-28 nothing
|
|
402
|
+
* here knew what the weather was: the same headlight cone was drawn in clear air and in thick
|
|
403
|
+
* fog, because `strength` is a number a caller authored once. Given this, the cone fades out
|
|
404
|
+
* as the air clears and blooms as it thickens, which is what turns fog from a filter over the
|
|
405
|
+
* frame into something that changes how the road is read.
|
|
406
|
+
*
|
|
407
|
+
* `fullAtDensity` is in the units of `Atmosphere.fogDensity` and is the density at which the
|
|
408
|
+
* beam is drawn exactly as authored; below it the beam is proportionally dimmer, above it
|
|
409
|
+
* nothing further happens. Absent means the beam ignores the weather, which is every volume
|
|
410
|
+
* drawn before this existed and every torch beam indoors.
|
|
411
|
+
*
|
|
412
|
+
* Density is read at the *volume's* own height rather than the camera's, so a car in a valley
|
|
413
|
+
* of fog seen from the ridge above carries its own weather.
|
|
414
|
+
*/
|
|
415
|
+
readonly medium?: {
|
|
416
|
+
readonly atmosphere: Atmosphere;
|
|
417
|
+
readonly fullAtDensity: number;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Where the drawn volume starts along its own +Z, matching the `nearM` its hull was built
|
|
421
|
+
* with. Defaults to the apex.
|
|
422
|
+
*
|
|
423
|
+
* The march is clipped to the same slab the hull covers, so a shaft that is a slice taken
|
|
424
|
+
* far down a very wide cone has to say where its slice begins or the walk starts at an apex
|
|
425
|
+
* above the roof and integrates air that was never drawn.
|
|
426
|
+
*/
|
|
427
|
+
readonly nearM?: number;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The two things a translucent draw can turn off. See `Renderer.drawTranslucentMesh`.
|
|
431
|
+
*
|
|
432
|
+
* Both default to `true`, which is the world exactly as it drew before this existed: lit,
|
|
433
|
+
* fogged, shaded like everything else. An options object rather than a positional boolean —
|
|
434
|
+
* `drawTranslucentMesh(mesh, model, opacity, false)` reads as a flag on the call rather than
|
|
435
|
+
* as a description of what got drawn, which `AGENTS.md` rules out for the same reason a bare
|
|
436
|
+
* boolean is refused everywhere else in this file.
|
|
437
|
+
*/
|
|
438
|
+
export interface TranslucentMeshOptions {
|
|
439
|
+
/**
|
|
440
|
+
* `false` draws the mesh exactly its own vertex colour times its own texture, with no
|
|
441
|
+
* ambient, sun, point lights, specular, reflectivity, grain or emissive touching it —
|
|
442
|
+
* three.js calls this `meshBasicMaterial`. For a glow shell, a flat backdrop plate or
|
|
443
|
+
* anything else whose art direction says "this colour, unconditionally."
|
|
444
|
+
*/
|
|
445
|
+
readonly lit?: boolean;
|
|
446
|
+
/**
|
|
447
|
+
* `false` keeps this draw out of the atmospheric haze and the underwater tint, both —
|
|
448
|
+
* see the shader's own comment on `uFogEnabled` for why the two travel together and what
|
|
449
|
+
* a draw that opts out of both gives up.
|
|
450
|
+
*
|
|
451
|
+
* Independent of `lit`: an unlit marker at the edge of view range can still want to fade
|
|
452
|
+
* into the haze, so the two are separate switches rather than one covering both.
|
|
453
|
+
*/
|
|
454
|
+
readonly fog?: boolean;
|
|
455
|
+
/**
|
|
456
|
+
* `false` skips the *tone curve* for this draw, keeping the colour-space conversion — which
|
|
457
|
+
* is exactly what three.js's `toneMapped: false` does and what it is for.
|
|
458
|
+
*
|
|
459
|
+
* **A meter is not a surface.** ACES is a photographic response: it compresses highlights and
|
|
460
|
+
* desaturates as it goes, which is right for a world made of light and wrong for a swatch
|
|
461
|
+
* whose colour *is* the reading. A spectrum bar authored at `#00d84a` renders through the
|
|
462
|
+
* curve as `(106, 213, 102)` — a pale sage where the author wrote a pure green — and the
|
|
463
|
+
* error cannot be dialled out of the source colour either: the AP1 matrix mixes channels, so
|
|
464
|
+
* that stop's red is pinned at 113 of 255 even with the input's red at zero. There is no
|
|
465
|
+
* value a caller can author that comes out right, which is why this is a switch rather than a
|
|
466
|
+
* tuning problem.
|
|
467
|
+
*
|
|
468
|
+
* **Honoured where this pass grades itself, which is a renderer with no composite.** With one,
|
|
469
|
+
* the frame is graded as a whole after every pass has written into it, and no per-draw flag
|
|
470
|
+
* can reach back through that; the draw is graded like everything else. That is a real limit
|
|
471
|
+
* of grading once at the end rather than per material, stated rather than hidden: three.js
|
|
472
|
+
* can honour it always because it tone maps inside every material's own fragment stage.
|
|
473
|
+
*/
|
|
474
|
+
readonly toneMapped?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* How far this surface bends what is behind it, in screen UV at the frame's own resolution.
|
|
477
|
+
*
|
|
478
|
+
* **0 is off and is the default, which is what every call that predates this means**, so no
|
|
479
|
+
* existing draw and no published scene moves by a bit.
|
|
480
|
+
*
|
|
481
|
+
* Translucent only, and that is a statement about what the branch reads rather than a
|
|
482
|
+
* restriction: it samples the colour drawn *before* this surface, and an opaque draw has
|
|
483
|
+
* already occluded it.
|
|
484
|
+
*
|
|
485
|
+
* **What it costs is one copy of the frame's colour**, taken at the first refracting draw of a
|
|
486
|
+
* frame and reused by every later one. So glass does not refract other glass — which is right,
|
|
487
|
+
* a pane behind a pane should show the room — and opaque geometry drawn *after* the first pane
|
|
488
|
+
* is missing from what that pane shows. Draw the world, then the glass.
|
|
489
|
+
*/
|
|
490
|
+
readonly refraction?: number;
|
|
491
|
+
/**
|
|
492
|
+
* What survives one metre of this medium, per channel. White is clear glass and is the default.
|
|
493
|
+
*
|
|
494
|
+
* **Beer-Lambert, stated in the units a caller thinks in.** The transmitted colour is
|
|
495
|
+
* `pow(tint, pathLength)`, which is `exp(-absorption * d)` with `absorption = -log(tint)`. What
|
|
496
|
+
* it costs is that a caller holding absorption coefficients from a reference converts them
|
|
497
|
+
* once; what it buys is that clear glass is `(1, 1, 1)` with no special case, and that there is
|
|
498
|
+
* no `log(0)` at the API to trap on.
|
|
499
|
+
*
|
|
500
|
+
* **Named apart from `tint` below, which is a different quantity on the same object.** That one
|
|
501
|
+
* multiplies the surface's own colour; this one absorbs what passes *through* it. A draw wanting
|
|
502
|
+
* a coloured pane that does not bend already has the first, and one name for both would be a
|
|
503
|
+
* noun lying about what it does.
|
|
504
|
+
*
|
|
505
|
+
* Does nothing without `refraction`, deliberately.
|
|
506
|
+
*/
|
|
507
|
+
readonly refractTint?: Vec3;
|
|
508
|
+
/**
|
|
509
|
+
* Metres of medium at `dot(N, V) = 1`, multiplied by the per-vertex channel's `.w` lane.
|
|
510
|
+
*
|
|
511
|
+
* The lane is optional and absent means 1.0, so a mesh carrying no channel refracts at exactly
|
|
512
|
+
* this thickness. **An instanced draw cannot carry the lane at all** — location 13 is the
|
|
513
|
+
* instance matrix, and `InstancedMesh` refuses a channelled mesh at construction — so an
|
|
514
|
+
* instanced refracting draw is uniform in thickness rather than wrong.
|
|
515
|
+
*
|
|
516
|
+
* The path a ray takes is this divided by `dot(N, V)`, so a pane seen edge-on absorbs more than
|
|
517
|
+
* the same pane seen face-on. That is what makes a glass edge go green while its middle stays
|
|
518
|
+
* clear, and it is the whole difference between this and a flat tint.
|
|
519
|
+
*/
|
|
520
|
+
readonly thicknessM?: number;
|
|
521
|
+
/**
|
|
522
|
+
* Whether this draw writes depth. `true`, which is what it has always done, unless a caller
|
|
523
|
+
* says otherwise.
|
|
524
|
+
*
|
|
525
|
+
* **Writing depth is right for a piece and wrong for a set of them.** A sign hung in the air
|
|
526
|
+
* has to occupy the depth buffer or the sky, drawn last over everything the world left
|
|
527
|
+
* untouched, paints straight over it — that is why this pass writes depth at all, and the
|
|
528
|
+
* reason is in `drawTranslucentMesh`'s own docstring. But depth written by a blended surface
|
|
529
|
+
* *rejects* the blended surfaces behind it, so a model whose interior is blended draws the
|
|
530
|
+
* first of each overlapping pair and discards the rest, and which one is first is the order
|
|
531
|
+
* the caller happened to submit in.
|
|
532
|
+
*
|
|
533
|
+
* So `false` is the tool for the second case: a set of blended surfaces that belong to one
|
|
534
|
+
* object and have to blend through each other. Measured on an imported vehicle, a quarter of
|
|
535
|
+
* whose materials declare a blend: 96 interior surfaces drawn against the shell they sit
|
|
536
|
+
* inside.
|
|
537
|
+
*
|
|
538
|
+
* **What it costs, and it is the whole reason this is not the default.** Nothing in the depth
|
|
539
|
+
* buffer means nothing to sort by, so the caller owns the order — draw back to front, or accept
|
|
540
|
+
* that two surfaces overlapping in view blend in submission order. And a surface that writes no
|
|
541
|
+
* depth cannot occlude the sky. A caller wanting both wants the pieces sorted and this left
|
|
542
|
+
* alone.
|
|
543
|
+
*/
|
|
544
|
+
readonly depthWrite?: boolean;
|
|
545
|
+
/**
|
|
546
|
+
* Orders geometry that occupies the same surface as other geometry, exactly as `drawMesh`'s
|
|
547
|
+
* parameter of the same name and with the same ceiling.
|
|
548
|
+
*
|
|
549
|
+
* A decal lying on the surface it decorates is coplanar with it, and coplanar surfaces do not
|
|
550
|
+
* merely tie: their interpolated depths are equal in exact arithmetic, so which one survives is
|
|
551
|
+
* decided per pixel by which way the rounding fell. `drawMesh` has had the answer since the
|
|
552
|
+
* defect turned up in six places in one session, and this call did not — so the same marking
|
|
553
|
+
* declared over the same slab was ordered when it was opaque and a coin toss when it blended.
|
|
554
|
+
*
|
|
555
|
+
* A higher layer wins wherever two surfaces coincide. 0, the default, is the base world and
|
|
556
|
+
* takes no offset at all.
|
|
557
|
+
*/
|
|
558
|
+
readonly depthLayer?: number;
|
|
559
|
+
/**
|
|
560
|
+
* A per-draw colour multiplier, exactly as `drawMesh`'s fourth argument.
|
|
561
|
+
*
|
|
562
|
+
* **Here rather than as a fifth positional parameter**, because it belongs to the same set of
|
|
563
|
+
* per-draw decisions the rest of this interface holds and a fifth argument after an options bag
|
|
564
|
+
* reads as an afterthought.
|
|
565
|
+
*
|
|
566
|
+
* It exists because the opaque and blended paths were not interchangeable for a caller that
|
|
567
|
+
* tints: a consumer fading a coloured model in — every surface translucent for the length of the
|
|
568
|
+
* fade — lost its colour for exactly as long as the fade ran and then snapped to it. Reported
|
|
569
|
+
* from a game whose cars arrive that way.
|
|
570
|
+
*/
|
|
571
|
+
readonly tint?: Vec3 | null;
|
|
572
|
+
}
|
|
573
|
+
export declare class WebGL2Renderer implements RendererApi {
|
|
574
|
+
/**
|
|
575
|
+
* Private on purpose. The GL context never leaves this directory, so a
|
|
576
|
+
* second backend (WebGPU) stays a contained
|
|
577
|
+
* addition rather than a rewrite of every caller. Resources are created
|
|
578
|
+
* through the factories below.
|
|
579
|
+
*
|
|
580
|
+
* **`registerPass` is the one exception, and it is a deliberate one.** A package that draws
|
|
581
|
+
* cannot be given a lowest common denominator of two APIs without inventing a second renderer,
|
|
582
|
+
* so `PassContext` hands over this context by name and the WebGPU encoder by another. The
|
|
583
|
+
* reason above is unharmed: what kept the second backend a contained addition was that no
|
|
584
|
+
* *caller* holds a GL type, and a contributor that asks for one branches on the backend it
|
|
585
|
+
* asked for.
|
|
586
|
+
*/
|
|
587
|
+
private readonly gl;
|
|
588
|
+
private readonly canvas;
|
|
589
|
+
private readonly contextLostListeners;
|
|
590
|
+
private readonly contextRestoredListeners;
|
|
591
|
+
/** Removes the canvas listeners `attachContextLoss` installed. See `dispose`. */
|
|
592
|
+
private readonly detachContextLoss;
|
|
593
|
+
private disposed;
|
|
594
|
+
/**
|
|
595
|
+
* Whether the drawing context is currently gone.
|
|
596
|
+
*
|
|
597
|
+
* Every GL entry point below returns early while this is true, and that is a
|
|
598
|
+
* reliability fix rather than an optimisation. A lost context does not stop the
|
|
599
|
+
* caller's frame loop — `requestAnimationFrame` keeps firing, the game keeps calling
|
|
600
|
+
* `beginFrame`, and every object this renderer holds now belongs to a context that no
|
|
601
|
+
* longer exists. The browser answers each call with INVALID_OPERATION, so a single
|
|
602
|
+
* loss becomes an unbounded flood of "object does not belong to this context" at sixty
|
|
603
|
+
* frames a second, which buries the one message that mattered and makes the console
|
|
604
|
+
* useless for finding the cause.
|
|
605
|
+
*
|
|
606
|
+
* It was reported as a wall of console errors after the picture stopped, with the
|
|
607
|
+
* trace running through the frame loop — the renderer drawing long after there was
|
|
608
|
+
* anything to draw to.
|
|
609
|
+
*
|
|
610
|
+
* Public so a consumer can skip its own per-frame work too; the guard here only makes
|
|
611
|
+
* the engine's half harmless.
|
|
612
|
+
*
|
|
613
|
+
* **Asks the context, not only the event.** `webglcontextlost` is dispatched
|
|
614
|
+
* asynchronously, so there is a window — however many calls the caller makes before
|
|
615
|
+
* the task queue drains — in which the context is already dead and the flag is still
|
|
616
|
+
* false. Every guard built on the flag alone fails open in that window, which is how a
|
|
617
|
+
* `resize` reached `PlanarReflection.ensureSize` and threw FRAMEBUFFER_UNSUPPORTED
|
|
618
|
+
* (0x8CDD) against a context that no longer existed.
|
|
619
|
+
*
|
|
620
|
+
* `isContextLost()` is the authority and is cheap. The flag is kept because it is what
|
|
621
|
+
* `onContextLost` listeners fire from, and it holds across the gap until
|
|
622
|
+
* `webglcontextrestored` clears it.
|
|
623
|
+
*/
|
|
624
|
+
get contextLost(): boolean;
|
|
625
|
+
private contextLostFlag;
|
|
626
|
+
/**
|
|
627
|
+
* The material texture `setSurfaceTexture` last bound, or `null` when albedo is off.
|
|
628
|
+
*
|
|
629
|
+
* A boolean alone used to be enough, because nothing between two `drawMesh` calls
|
|
630
|
+
* ever touched `SURFACE_TEXTURE_UNIT`'s actual GL binding. `drawSdfText` does: the atlas
|
|
631
|
+
* borrows that same unit for the length of one draw (see `SDF_TEXT_TEXTURE_UNIT`), so
|
|
632
|
+
* after it returns the unit no longer necessarily holds what this flat-program state
|
|
633
|
+
* still says it holds. This is what lets `drawSdfText` put the *right* texture back
|
|
634
|
+
* rather than just a flag saying one should be there.
|
|
635
|
+
*/
|
|
636
|
+
private currentSurfaceTexture;
|
|
637
|
+
/**
|
|
638
|
+
* GPU time for the frame's three parts, when the driver will report it.
|
|
639
|
+
*
|
|
640
|
+
* Public because the consumer is a diagnostic, not the renderer: the game brackets
|
|
641
|
+
* its own frame with `beginFrame`/`endFrame` and reads samples with `poll`.
|
|
642
|
+
*/
|
|
643
|
+
readonly gpuTimer: GpuTimer;
|
|
644
|
+
/**
|
|
645
|
+
* The drawing-buffer density ceiling, in effect right now.
|
|
646
|
+
*
|
|
647
|
+
* Not `readonly`: `applyResolutionScale` moves it, which is what lets a governor hold a
|
|
648
|
+
* frame budget without rebuilding anything. It starts at the resolved quality's own
|
|
649
|
+
* `maxDevicePixelRatio` and is re-read by every `resize()`.
|
|
650
|
+
*/
|
|
651
|
+
private maxDpr;
|
|
652
|
+
/** Area cap in pixels, 0 for uncapped. Mutable: see `setMaxDrawingBufferPixels`. */
|
|
653
|
+
private maxDrawingBufferPixels;
|
|
654
|
+
/** Reused by `resize`, which runs every frame and may not allocate. */
|
|
655
|
+
private readonly budgeted;
|
|
656
|
+
/** Per-frame bake planning scratch, sized once. See `pointShadowBudget.ts`. */
|
|
657
|
+
/**
|
|
658
|
+
* How many frames running each slot has been stale, which light that was, and whether
|
|
659
|
+
* that run is long enough to call the map chronically stale.
|
|
660
|
+
*
|
|
661
|
+
* A light that moves, or animates its radius, is owed a bake again the frame after it
|
|
662
|
+
* gets one. Counting the run is what separates it from a static light that drifted once
|
|
663
|
+
* and would then hold its image indefinitely, so the budget can serve the bake that
|
|
664
|
+
* will *stick* first. The owner is tracked because pool slots change hands: a new light
|
|
665
|
+
* in an old slot starts at zero rather than inheriting the last tenant's record.
|
|
666
|
+
*/
|
|
667
|
+
/** The shaded lights that also cast, which is not the same list. See `updatePointShadows`. */
|
|
668
|
+
private readonly castingWorldIndices;
|
|
669
|
+
/** Per-slot scratch for the bake round; see `runPointShadowBakes`. */
|
|
670
|
+
private readonly bakeScratch;
|
|
671
|
+
/** Resolved once; a size/profile change recreates the renderer's resources. */
|
|
672
|
+
readonly quality: Readonly<RenderQuality>;
|
|
673
|
+
/**
|
|
674
|
+
* What `UNMASKED_RENDERER_WEBGL` calls this part, or `''` when the browser withholds it.
|
|
675
|
+
*
|
|
676
|
+
* Public because a bug report is worth more with it than without: every performance
|
|
677
|
+
* report this project has acted on was read GPU-string first.
|
|
678
|
+
*/
|
|
679
|
+
readonly rendererName: string;
|
|
680
|
+
/**
|
|
681
|
+
* Which backend is drawing, from the renderer rather than from the address bar.
|
|
682
|
+
*
|
|
683
|
+
* **`createRenderer` returns this too and says to report it and never infer it** — the
|
|
684
|
+
* inference being a query string, which says what was *asked for* and is wrong in exactly
|
|
685
|
+
* the case that matters, a silent fallback. A consumer holding only a renderer could not
|
|
686
|
+
* ask before this existed, so it inferred or it said nothing.
|
|
687
|
+
*
|
|
688
|
+
* Not the same question as `rendererName`, which is the GPU's own string and identifies the
|
|
689
|
+
* part rather than the API.
|
|
690
|
+
*/
|
|
691
|
+
readonly backend: RenderBackend;
|
|
692
|
+
/**
|
|
693
|
+
* Whether this part was recognised as one that cannot hold the profile it asked for, and
|
|
694
|
+
* had its pixel terms clamped at construction. Carried into bug snapshots, so a report
|
|
695
|
+
* says whether the clamp fired rather than leaving it to be inferred.
|
|
696
|
+
*/
|
|
697
|
+
readonly capabilityClamped: boolean;
|
|
698
|
+
/**
|
|
699
|
+
* How many frames this renderer has actually presented, monotonic for its whole lifetime.
|
|
700
|
+
*
|
|
701
|
+
* **Incremented where `endFrame` reaches its end, not where it is called.** WebGL2 has no
|
|
702
|
+
* swap chain to acquire the way WebGPU's `getCurrentTexture` gives one — the canvas *is*
|
|
703
|
+
* the default framebuffer, and `beginFrame` clears it directly, so there is no separate
|
|
704
|
+
* "taken" instant to hang this on. `endFrame`'s only early return is a lost context, so its
|
|
705
|
+
* far end, right beside `framePresented`, is this backend's honest equivalent: the one
|
|
706
|
+
* point every call is guaranteed to reach if and only if the frame it opened is what the
|
|
707
|
+
* browser is about to show. A consumer compositing an offline export reads this after
|
|
708
|
+
* `endFrame` and compares it with what it read last, which is how it tells a frame that
|
|
709
|
+
* drew something from one that repeated the canvas it already had — see `ExportTarget`.
|
|
710
|
+
*/
|
|
711
|
+
get presentedFrames(): number;
|
|
712
|
+
private presentedFrameCount;
|
|
713
|
+
private readonly flatProgram;
|
|
714
|
+
private readonly flatUniforms;
|
|
715
|
+
/**
|
|
716
|
+
* The lit fragment source every flat program is built from, and the budget it was built at.
|
|
717
|
+
*
|
|
718
|
+
* **One string for all four variants, built once.** The four flags `flatFrag` permutes on are
|
|
719
|
+
* fixed by the time the first program is compiled — three are resolved quality and the fourth is
|
|
720
|
+
* a `readonly` field — so the skinned, morphed and instanced programs differ only in their
|
|
721
|
+
* *vertex* stage. Building it three more times cost 0.84 ms each, measured, and made it possible
|
|
722
|
+
* for a lazily compiled variant to end up at a different light budget from the one the renderer
|
|
723
|
+
* uploads for, which is a picture nobody could explain.
|
|
724
|
+
*/
|
|
725
|
+
private readonly flatFragSource;
|
|
726
|
+
/**
|
|
727
|
+
* How many lights this renderer's shader has room for, after the device had its say.
|
|
728
|
+
*
|
|
729
|
+
* `FULL_LIGHT_BUDGET` on anything with room, which is every desktop part and most phones. See
|
|
730
|
+
* `uniformVectorBudget.ts`, and `shadedLights` for what a consumer should do about it.
|
|
731
|
+
*/
|
|
732
|
+
private readonly lightBudget;
|
|
733
|
+
/**
|
|
734
|
+
* The skinned twin of `flatProgram`, compiled the first time a rigged mesh is built.
|
|
735
|
+
*
|
|
736
|
+
* **Lazy, and at `createMesh` rather than at construction or in the frame loop.** At
|
|
737
|
+
* construction it would cost a compile to every game for a feature most never use; in the frame
|
|
738
|
+
* loop it could throw, and the rule is that initialisation fails loudly so a running frame never
|
|
739
|
+
* has to. `createMesh` is construction, it is off the frame, and it is where joints first exist.
|
|
740
|
+
*/
|
|
741
|
+
private flatSkinnedProgram;
|
|
742
|
+
private flatSkinnedUniforms;
|
|
743
|
+
/**
|
|
744
|
+
* The vertex stage's other two variants, compiled on the first mesh that needs each.
|
|
745
|
+
*
|
|
746
|
+
* Four programs where the shader has four variants, and each is compiled only if a mesh actually
|
|
747
|
+
* asks for it — a game with characters and no morph targets pays for two, and one with neither
|
|
748
|
+
* pays for one. `bindMeshPass` feeds every program that exists, so the frame state cannot go
|
|
749
|
+
* missing from one of them.
|
|
750
|
+
*/
|
|
751
|
+
private flatMorphedProgram;
|
|
752
|
+
private flatMorphedUniforms;
|
|
753
|
+
private flatBothProgram;
|
|
754
|
+
private flatBothUniforms;
|
|
755
|
+
/** The morph weights the following draws use, or null for none. */
|
|
756
|
+
private morphWeights;
|
|
757
|
+
/**
|
|
758
|
+
* Every flat program with the frame state it needs, preallocated so the loop allocates nothing.
|
|
759
|
+
*
|
|
760
|
+
* One entry until a rigged mesh is built, two after. `bindMeshPass` walks it.
|
|
761
|
+
*/
|
|
762
|
+
private readonly flatTargets;
|
|
763
|
+
/**
|
|
764
|
+
* The last camera and environment `bindMeshPass` was given.
|
|
765
|
+
*
|
|
766
|
+
* Held so a program compiled *after* this frame's pass was bound can be fed at once rather than
|
|
767
|
+
* drawing one frame with every uniform at zero. Two references rather than copies, so this
|
|
768
|
+
* costs no allocation.
|
|
769
|
+
*/
|
|
770
|
+
private lastPassCamera;
|
|
771
|
+
private lastPassEnv;
|
|
772
|
+
/** Light in the air, which is not a material. See `shaders/lightVolume.ts`. */
|
|
773
|
+
private readonly lightVolumeProgram;
|
|
774
|
+
private readonly lightVolumeUniforms;
|
|
775
|
+
/** Two triangles in screen space, for `fillPanel`. Built once. */
|
|
776
|
+
private readonly panelProgram;
|
|
777
|
+
private readonly panelUniforms;
|
|
778
|
+
private readonly panelVao;
|
|
779
|
+
private readonly filmProgram;
|
|
780
|
+
/**
|
|
781
|
+
* The off-screen colour target, when screen effects are on. Null is not a failure —
|
|
782
|
+
* it is the low-end path, and the scene goes straight to the canvas exactly as it did
|
|
783
|
+
* before this existed.
|
|
784
|
+
*/
|
|
785
|
+
private readonly sceneTarget;
|
|
786
|
+
/** So the refusal below is once per renderer rather than once per frame. See `setColourGrade`. */
|
|
787
|
+
private warnedGradeWithoutComposite;
|
|
788
|
+
/** Scratch for a volume's `uDepthToLocal`. Built per draw and never allocated in the loop. */
|
|
789
|
+
private readonly volumeDepthToLocal;
|
|
790
|
+
private readonly volumeDepthScratch;
|
|
791
|
+
/**
|
|
792
|
+
* What a declared-but-unused sampler slot reads, instead of nothing.
|
|
793
|
+
*
|
|
794
|
+
* A profile with shadows off still compiles the samplers that would sample them, and a
|
|
795
|
+
* unit bound to `null` is an *incomplete* texture whose descriptor a driver may fetch
|
|
796
|
+
* before it evaluates the runtime branch that would have skipped the read. See
|
|
797
|
+
* `emptyTexture.ts`: on RDNA4 that fetch page-faults and resets the GPU.
|
|
798
|
+
*/
|
|
799
|
+
/**
|
|
800
|
+
* The joint palette the next skinned `drawMesh` reads, or empty until one is set.
|
|
801
|
+
*
|
|
802
|
+
* Held rather than passed, like every other per-draw state on this renderer: `setSkinPalette`
|
|
803
|
+
* chooses and the following draws use it, which is the shape `setMaterial` already has.
|
|
804
|
+
*/
|
|
805
|
+
private readonly skinPalette;
|
|
806
|
+
/** Whether a palette is currently chosen. Distinct from whether one was ever uploaded. */
|
|
807
|
+
private skinPaletteSet;
|
|
808
|
+
private readonly emptyTexture2D;
|
|
809
|
+
private readonly emptyTexture2DArray;
|
|
810
|
+
private readonly emptyTextureCube;
|
|
811
|
+
/** How hard the rush blurs this frame, and how far its outermost tap reaches. */
|
|
812
|
+
private rushStrength;
|
|
813
|
+
/** How much of the motion-blur ceiling this frame takes. See `setCameraMotionBlur`. */
|
|
814
|
+
/**
|
|
815
|
+
* Order-independent transparency: the frame's translucent draws, and the pass that resolves them.
|
|
816
|
+
*
|
|
817
|
+
* **Recorded rather than drawn**, because the accumulation and the revealage are two blend states
|
|
818
|
+
* over the same geometry and this renderer otherwise draws immediately. See `TranslucentQueue`
|
|
819
|
+
* for why every field is copied.
|
|
820
|
+
*/
|
|
821
|
+
private readonly translucentQueue;
|
|
822
|
+
private oit;
|
|
823
|
+
/**
|
|
824
|
+
* The frame's drawn decals, and the pass that marks the scene with them.
|
|
825
|
+
*
|
|
826
|
+
* Recorded rather than drawn where the call is made, because a projected mark is decided
|
|
827
|
+
* from the depth the frame has drawn and therefore cannot run until the opaque world is
|
|
828
|
+
* finished. See `DecalQueue`.
|
|
829
|
+
*/
|
|
830
|
+
private readonly decalQueue;
|
|
831
|
+
/** Built on the first frame that submits a mark, and never for a scene with none. */
|
|
832
|
+
private decals;
|
|
833
|
+
/** Said once where a mark cannot be drawn at all, rather than dropped in silence. */
|
|
834
|
+
private decalsRefused;
|
|
835
|
+
/**
|
|
836
|
+
* The frame's reflective surfaces, and the pass that marches them.
|
|
837
|
+
*
|
|
838
|
+
* Recorded rather than traced where the call is made, because the march reads the *finished*
|
|
839
|
+
* picture: what a ray finds is a pixel of the scene the frame has already drawn.
|
|
840
|
+
*/
|
|
841
|
+
private readonly reflectionQueue;
|
|
842
|
+
/** Built on the first frame that submits a surface, and never for a scene with none. */
|
|
843
|
+
private ssr;
|
|
844
|
+
/** Said once where a reflection cannot be traced at all. */
|
|
845
|
+
private reflectionsRefused;
|
|
846
|
+
/** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
|
|
847
|
+
private readonly frameEye;
|
|
848
|
+
/** Whether this frame is recording translucent draws instead of blending them as they arrive. */
|
|
849
|
+
private oitActive;
|
|
850
|
+
/** True while the queue is being replayed, so a replayed draw does not record itself again. */
|
|
851
|
+
private oitReplaying;
|
|
852
|
+
/** Said once, not per frame, where the context cannot give the float target the sum needs. */
|
|
853
|
+
private oitRefused;
|
|
854
|
+
/** Said once for the whole renderer. See `refuseOitMultisampled`. */
|
|
855
|
+
private oitMultisampleRefused;
|
|
856
|
+
/**
|
|
857
|
+
* The options object the replay hands back to `drawTranslucentMesh`, reused.
|
|
858
|
+
*
|
|
859
|
+
* Mutable where `TranslucentMeshOptions` is readonly, and built once: a fresh literal per draw
|
|
860
|
+
* would allocate once per translucent surface per pass per frame, which is what `AGENTS.md`
|
|
861
|
+
* forbids in the frame loop.
|
|
862
|
+
*/
|
|
863
|
+
private readonly oitReplayOptions;
|
|
864
|
+
private motionBlurScale;
|
|
865
|
+
/** How much of the bloom ceiling this frame takes. See `setBloom`. */
|
|
866
|
+
private bloomScale;
|
|
867
|
+
/**
|
|
868
|
+
* This frame's exposure into the tone curve. See `setOutputExposure`.
|
|
869
|
+
*
|
|
870
|
+
* Starts at the construction-time grade, so a world that never calls the setter is exposed
|
|
871
|
+
* exactly as it asked to be and nothing about it changes.
|
|
872
|
+
*/
|
|
873
|
+
private exposure;
|
|
874
|
+
/**
|
|
875
|
+
* This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
|
|
876
|
+
*
|
|
877
|
+
* A mutable tuple rather than a fresh array per call, so a transition calling this every
|
|
878
|
+
* frame costs no allocation to hold it. `veilAlpha` is reset to zero at the end of every
|
|
879
|
+
* `endFrame` — unlike `rushStrength` and the two scales above, which are held until a caller
|
|
880
|
+
* changes them — because a forgotten veil is a stuck white or black frame, and that failure
|
|
881
|
+
* is worse than one that has to ask again. See `setFrameVeil` for the ordering ruling.
|
|
882
|
+
*/
|
|
883
|
+
private readonly veilColor;
|
|
884
|
+
private veilAlpha;
|
|
885
|
+
/**
|
|
886
|
+
* The view-projection the *previous* frame was drawn with, and the reprojection built
|
|
887
|
+
* from it. Held here because camera motion blur is the only thing that wants it and it
|
|
888
|
+
* costs one matrix either way.
|
|
889
|
+
*
|
|
890
|
+
* Scratch, allocated once: `endFrame` runs every frame and may not allocate.
|
|
891
|
+
*/
|
|
892
|
+
private readonly previousViewProj;
|
|
893
|
+
private readonly reprojection;
|
|
894
|
+
/**
|
|
895
|
+
* Temporal antialiasing: the history's state, this frame's sub-pixel offset, and the jittered
|
|
896
|
+
* matrix geometry is actually drawn with.
|
|
897
|
+
*
|
|
898
|
+
* **`frameViewProj` stays unjittered and that is the whole design.** The reprojection above is
|
|
899
|
+
* built from it, and the history holds a resolved picture standing for the *unjittered* scene —
|
|
900
|
+
* so reprojecting through a jittered matrix would look every sample up half a pixel from where
|
|
901
|
+
* it is and cancel the accumulation it is trying to build. Only `viewProjFor` jitters, and only
|
|
902
|
+
* for the frame's own camera.
|
|
903
|
+
*/
|
|
904
|
+
private readonly temporalHistory;
|
|
905
|
+
private readonly jitteredViewProj;
|
|
906
|
+
private temporalJitterX;
|
|
907
|
+
private temporalJitterY;
|
|
908
|
+
/** Whether this frame's geometry uploads are jittered. False for a mirror, a probe and when off. */
|
|
909
|
+
private temporalJittering;
|
|
910
|
+
/** Whether the resolve may sample what the last frame left. False on frame one, a resize, a cut. */
|
|
911
|
+
private temporalHistoryUsable;
|
|
912
|
+
private hasPreviousView;
|
|
913
|
+
/** The camera the frame was actually drawn with, captured by `bindMeshPass`. */
|
|
914
|
+
private frameViewProj;
|
|
915
|
+
/**
|
|
916
|
+
* That camera's projection, and its inverse, for ambient occlusion.
|
|
917
|
+
*
|
|
918
|
+
* The projection alone rather than the whole view-projection, because occlusion is measured
|
|
919
|
+
* in *view* space: distances there are metres from the eye, so a radius means the same
|
|
920
|
+
* thing wherever the camera is standing, and the numbers stay small enough that a depth
|
|
921
|
+
* reconstruction keeps its precision. A world-space reconstruction of the same points would
|
|
922
|
+
* be the camera's position plus a small offset, which is where a float loses the offset.
|
|
923
|
+
*
|
|
924
|
+
* Scratch, filled in `endFrame`, which may not allocate.
|
|
925
|
+
*/
|
|
926
|
+
private frameProjection;
|
|
927
|
+
private readonly aoInvProjection;
|
|
928
|
+
private readonly aoProjScale;
|
|
929
|
+
/**
|
|
930
|
+
* Whether this frame has already been resolved to the canvas.
|
|
931
|
+
*
|
|
932
|
+
* Anything drawn *after* `endFrame` — a UI inset, a portrait box — belongs on the
|
|
933
|
+
* presented image, not in a target that has already been sampled. So once the frame is
|
|
934
|
+
* presented, "back to the whole frame" means the canvas again.
|
|
935
|
+
*/
|
|
936
|
+
private framePresented;
|
|
937
|
+
/**
|
|
938
|
+
* A fence placed behind the first presented frame, and whether it has cleared.
|
|
939
|
+
*
|
|
940
|
+
* `endFrame` returning is not the frame being finished — it is the frame being
|
|
941
|
+
* *asked for*. The driver pays for every pipeline the first time it is used, and
|
|
942
|
+
* on a built game that bill is 150 ms of a saturated GPU process arriving after
|
|
943
|
+
* the renderer's own callbacks are back down to a millisecond each (traced
|
|
944
|
+
* 2026-08-07). A consumer covering the load with a plate has no way to see that
|
|
945
|
+
* from the main thread, so it uncovers the screen exactly in time for the player
|
|
946
|
+
* to watch it happen.
|
|
947
|
+
*
|
|
948
|
+
* The fence is the only thing that knows. `waited` bounds it: a driver that never
|
|
949
|
+
* signals must not be able to hold a plate up for ever, and two seconds of frames
|
|
950
|
+
* is far longer than any first frame and far shorter than a player waiting.
|
|
951
|
+
*/
|
|
952
|
+
private firstFrameFence;
|
|
953
|
+
private firstFrameCleared;
|
|
954
|
+
private firstFrameWaits;
|
|
955
|
+
private readonly filmUniforms;
|
|
956
|
+
private readonly skyProgram;
|
|
957
|
+
private readonly skyUniforms;
|
|
958
|
+
private readonly skyVao;
|
|
959
|
+
private readonly scatterProgram;
|
|
960
|
+
private readonly scatterUniforms;
|
|
961
|
+
private readonly depthProgram;
|
|
962
|
+
/**
|
|
963
|
+
* The depth program that reads a joint palette.
|
|
964
|
+
*
|
|
965
|
+
* A second program rather than a uniform gate on the first, which is the decision
|
|
966
|
+
* `skinning.ts` measured for the flat shader and which is cheaper here: this vertex stage is a
|
|
967
|
+
* fifth the size, so the second copy dedupes almost entirely.
|
|
968
|
+
*/
|
|
969
|
+
private readonly depthSkinnedProgram;
|
|
970
|
+
private readonly depthSkinnedUniforms;
|
|
971
|
+
private readonly depthUniforms;
|
|
972
|
+
private readonly depthInstancedProgram;
|
|
973
|
+
private readonly depthInstancedUniforms;
|
|
974
|
+
private readonly scatterDepthProgram;
|
|
975
|
+
private readonly scatterDepthUniforms;
|
|
976
|
+
private readonly shadowMap;
|
|
977
|
+
private readonly peeledShadowMap;
|
|
978
|
+
private readonly dynamicShadowMap;
|
|
979
|
+
private readonly pointShadows;
|
|
980
|
+
/**
|
|
981
|
+
* The layers a world's casting rectangles hold, above the point pool in the same array.
|
|
982
|
+
*
|
|
983
|
+
* Null exactly when `pointShadows` is, because an area shadow is read out of that array and
|
|
984
|
+
* WebGL2 has no seventeenth texture unit to put a second one in. See `areaShadowSet.ts`.
|
|
985
|
+
*/
|
|
986
|
+
private readonly areaShadows;
|
|
987
|
+
/** What the lit pass is told about each rectangle. Refilled per frame, never allocated. */
|
|
988
|
+
private readonly resolvedAreaShadows;
|
|
989
|
+
/** Per-slot scratch for the area bake round; caller-owned, because that runs every frame. */
|
|
990
|
+
private readonly areaBakeScratch;
|
|
991
|
+
/** Said once per renderer, naming the field a rectangle left out. */
|
|
992
|
+
private warnedAreaShadowRange;
|
|
993
|
+
/** Said once per renderer, naming the quality flag an area shadow rides. */
|
|
994
|
+
private warnedAreaShadowsOff;
|
|
995
|
+
private warnedAreaShadowLayers;
|
|
996
|
+
/**
|
|
997
|
+
* How many point lights the array was last sized for.
|
|
998
|
+
*
|
|
999
|
+
* Kept because `firstAreaShadowLayer` needs it and the pool does not publish it: the area layers
|
|
1000
|
+
* begin where the pool's end, and re-deriving that from anything else is the one number that must
|
|
1001
|
+
* not be guessed — a rectangle told the wrong first layer samples a lamp's map.
|
|
1002
|
+
*/
|
|
1003
|
+
private preparedPointLightCount;
|
|
1004
|
+
/**
|
|
1005
|
+
* Every point light's octahedral map, in one array texture, or null until a world says how
|
|
1006
|
+
* many lights it has.
|
|
1007
|
+
*
|
|
1008
|
+
* Not built in the constructor, because `texStorage3D` is immutable and the size depends on the
|
|
1009
|
+
* world. See `prepareStaticPointShadows`.
|
|
1010
|
+
*/
|
|
1011
|
+
private pointShadowArray;
|
|
1012
|
+
/** The photometric atlas, allocated on first use. See `setIesProfiles`. */
|
|
1013
|
+
private iesTexture;
|
|
1014
|
+
/** Rows in that atlas. One until a consumer loads a profile, which is the row of ones. */
|
|
1015
|
+
private iesRows;
|
|
1016
|
+
private cookieTexture;
|
|
1017
|
+
/** Tiles in the cookie atlas. 0 is the white placeholder and the whole off path. */
|
|
1018
|
+
private cookieTiles;
|
|
1019
|
+
/** Horizontal planes each profile occupies. 1 unless something asymmetric was loaded. */
|
|
1020
|
+
private iesPlanes;
|
|
1021
|
+
private readonly planarReflection;
|
|
1022
|
+
/**
|
|
1023
|
+
* The cube six faces are drawn into, and the only one there is however many probes a grid holds.
|
|
1024
|
+
*
|
|
1025
|
+
* **Scratch, and reused by every probe.** A face is consumed the moment the convolution reads
|
|
1026
|
+
* it, so a grid of thirty-two probes needs one capture rather than thirty-two — the same economy
|
|
1027
|
+
* `pointShadowArray.ts` makes with its own single scratch, arrived at the same way.
|
|
1028
|
+
*/
|
|
1029
|
+
private readonly probeCapture;
|
|
1030
|
+
/** Every probe of the grid, as one array texture. Allocated when a grid is declared. */
|
|
1031
|
+
private probeArray;
|
|
1032
|
+
/**
|
|
1033
|
+
* Where those probes stand. A single baked probe is a grid of one at that probe's own origin.
|
|
1034
|
+
*
|
|
1035
|
+
* Null until a caller bakes or declares one, because a grid's layer count is what the array is
|
|
1036
|
+
* sized from and `texStorage3D` is immutable.
|
|
1037
|
+
*/
|
|
1038
|
+
private probes;
|
|
1039
|
+
/** Whether the grid supplies the scene's ambient. `ProbeBakeOptions.irradiance`, at grid scope. */
|
|
1040
|
+
private probeAmbient;
|
|
1041
|
+
/** Where the probe being baked stands. Reused, so a bake allocates nothing. */
|
|
1042
|
+
private readonly probeOrigin;
|
|
1043
|
+
/**
|
|
1044
|
+
* The convolution that fills the probe's prefiltered cube. Null exactly when the probe is.
|
|
1045
|
+
*
|
|
1046
|
+
* Built beside the probe rather than on first bake, because a program compile in the frame a
|
|
1047
|
+
* caller happens to bake in is a stall in the middle of play — the argument `planarReflection.ts`
|
|
1048
|
+
* makes about its own target, and the same cost.
|
|
1049
|
+
*/
|
|
1050
|
+
private readonly environmentPrefilter;
|
|
1051
|
+
/** Said once per renderer; see `updatePointShadows`. */
|
|
1052
|
+
private warnedNoPointShadowArray;
|
|
1053
|
+
/** Said once, for the retired ambient dial. */
|
|
1054
|
+
private warnedEnvironmentAmbient;
|
|
1055
|
+
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
1056
|
+
private probePassActive;
|
|
1057
|
+
/** The clip-control extension, or null where this context does not offer it. */
|
|
1058
|
+
private clipControl;
|
|
1059
|
+
/**
|
|
1060
|
+
* Whether this context really is drawing reversed. See `RendererApi.reversedDepth`.
|
|
1061
|
+
*
|
|
1062
|
+
* Separate from `REVERSED_DEPTH`, which is what the engine wants: this is what it got. Every
|
|
1063
|
+
* depth decision below reads this one, so a context without `EXT_clip_control` behaves exactly
|
|
1064
|
+
* as it did before rather than half-reversed — and it is public because a consumer choosing a
|
|
1065
|
+
* near plane needs the granted answer and not the wished-for one.
|
|
1066
|
+
*/
|
|
1067
|
+
reversedDepth: boolean;
|
|
1068
|
+
/** `GL_DEPTH_REMAP * camera.viewProjection`, rebuilt once a frame. */
|
|
1069
|
+
private readonly sceneViewProj;
|
|
1070
|
+
/**
|
|
1071
|
+
* One buffer per shadow matrix that outlives a call, and that is not fussiness.
|
|
1072
|
+
*
|
|
1073
|
+
* A single scratch was enough while only the directional pass used it; two passes share the
|
|
1074
|
+
* correction now — the directional pass holds its result for the length of the pass and a point
|
|
1075
|
+
* shadow face holds it for the face — and one buffer means whichever ran last silently rewrites
|
|
1076
|
+
* the other. That is the same aliasing that sent a whole scene through the wrong projection when
|
|
1077
|
+
* the frame projection was cached.
|
|
1078
|
+
*/
|
|
1079
|
+
private readonly shadowViewProj;
|
|
1080
|
+
private readonly faceViewProjStore;
|
|
1081
|
+
/** The shim `frameViewFor` hands out. One object, never two live at once. */
|
|
1082
|
+
private readonly correctedView;
|
|
1083
|
+
/**
|
|
1084
|
+
* The scene's view-projection in the space this context's depth buffer expects.
|
|
1085
|
+
*
|
|
1086
|
+
* **The camera's own matrix stays OpenGL-convention and that is deliberate.** `frustum.ts`
|
|
1087
|
+
* extracts its planes on that assumption and says so, and the WebGPU backend corrects at the
|
|
1088
|
+
* last moment for the same reason — so the correction lives here, in the one place that uploads
|
|
1089
|
+
* it, and culling is untouched by any of this. Where the context is not reversed this hands back
|
|
1090
|
+
* the camera's matrix unchanged and multiplies nothing.
|
|
1091
|
+
*/
|
|
1092
|
+
private viewProjFor;
|
|
1093
|
+
/**
|
|
1094
|
+
* The same remap for a caller holding a matrix instead of a camera.
|
|
1095
|
+
*
|
|
1096
|
+
* **Only geometry wants this.** A matrix whose depth row never reaches a depth test must stay
|
|
1097
|
+
* raw: `uReflectionViewProj` is read as `.xy / .w` and nothing else, and the motion blur builds
|
|
1098
|
+
* its reprojection from `frameViewProj` and inverts it against a clip z the shader recovered in
|
|
1099
|
+
* OpenGL's sense — remapping either would be correcting something that was never wrong.
|
|
1100
|
+
*/
|
|
1101
|
+
/**
|
|
1102
|
+
* The camera as the frame's drawing paths should see it: corrected once, here.
|
|
1103
|
+
*
|
|
1104
|
+
* **This is the seam this backend did not have.** WebGPU applies `CLIP_CORRECTION` in one place
|
|
1105
|
+
* at the last moment; every path here read `camera.viewProjection` for itself, in this file and
|
|
1106
|
+
* in five renderers beside it, so reversing depth meant finding and remapping each one. The
|
|
1107
|
+
* frame now owns one corrected view and hands it over, which is why `FrameView` exists.
|
|
1108
|
+
*
|
|
1109
|
+
* Rebuilt per call rather than cached per frame: `sceneMatrix` writes into one buffer, so a
|
|
1110
|
+
* caller holding this across a second call would see it move. Every consumer uses it and drops
|
|
1111
|
+
* it within the same draw.
|
|
1112
|
+
*/
|
|
1113
|
+
private frameViewFor;
|
|
1114
|
+
/** The shadow passes' range correction, which changes the range and not the sense. */
|
|
1115
|
+
private shadowMatrix;
|
|
1116
|
+
private sceneMatrix;
|
|
1117
|
+
private shadowPassActive;
|
|
1118
|
+
/** Non-zero while the drawing buffer is pinned; see `lockDrawingBuffer`. */
|
|
1119
|
+
private lockedWidth;
|
|
1120
|
+
private lockedHeight;
|
|
1121
|
+
private activeShadowMap;
|
|
1122
|
+
private reflectionPassActive;
|
|
1123
|
+
private reflectionReadyThisFrame;
|
|
1124
|
+
private reflectionAtmosphereY;
|
|
1125
|
+
/** Callback state for allocation-free point-shadow depth passes. */
|
|
1126
|
+
private pointShadowCasters;
|
|
1127
|
+
/**
|
|
1128
|
+
* What the depth programs are currently configured to project by, and whether the
|
|
1129
|
+
* pass is peeling.
|
|
1130
|
+
*
|
|
1131
|
+
* Two programs record depth now — rigid meshes and instanced scatter — and only
|
|
1132
|
+
* one can be bound at a time. Rebinding one means re-uploading its matrix, so the
|
|
1133
|
+
* pass records what that matrix is instead of each draw guessing.
|
|
1134
|
+
*/
|
|
1135
|
+
private activeDepthViewProj;
|
|
1136
|
+
private activeDepthPeel;
|
|
1137
|
+
/**
|
|
1138
|
+
* Whether the depth pass this scatter draw interrupts wants back-face culling.
|
|
1139
|
+
*
|
|
1140
|
+
* Foliage is two-sided, so a scatter batch must be rasterised with culling off or
|
|
1141
|
+
* a blade facing away from the light casts nothing. The *directional* pass culls;
|
|
1142
|
+
* `PointShadowMap.bake` deliberately culls nothing for all six faces. Blindly
|
|
1143
|
+
* re-enabling after the draw would therefore start culling halfway through a
|
|
1144
|
+
* cubemap — so the pass states what it wants and this restores that.
|
|
1145
|
+
*/
|
|
1146
|
+
private depthPassCullsFaces;
|
|
1147
|
+
/**
|
|
1148
|
+
* The one sink handed to every caster enumeration.
|
|
1149
|
+
*
|
|
1150
|
+
* Built once at construction, arrow properties closing over `this`: a sink created
|
|
1151
|
+
* per pass would allocate six objects per light per frame.
|
|
1152
|
+
*/
|
|
1153
|
+
private readonly casterSink;
|
|
1154
|
+
private readonly drawPointShadowFace;
|
|
1155
|
+
/**
|
|
1156
|
+
* Viewport in CSS pixels rather than device pixels.
|
|
1157
|
+
*
|
|
1158
|
+
* What screen-space overlays lay out against: sizing text in the drawing
|
|
1159
|
+
* buffer makes it a third the size on a high-DPI phone, which is exactly the
|
|
1160
|
+
* screen where it is hardest to read.
|
|
1161
|
+
*/
|
|
1162
|
+
get cssWidth(): number;
|
|
1163
|
+
get cssHeight(): number;
|
|
1164
|
+
/**
|
|
1165
|
+
* Told when the drawing context is lost. See `attachContextLoss`.
|
|
1166
|
+
*
|
|
1167
|
+
* A lost context is a black screen the game does not otherwise notice: nothing
|
|
1168
|
+
* throws, the loop keeps running, and every draw call quietly does nothing.
|
|
1169
|
+
*/
|
|
1170
|
+
onContextLost(listener: () => void): void;
|
|
1171
|
+
/**
|
|
1172
|
+
* Told when the drawing context comes back. See `attachContextLoss`.
|
|
1173
|
+
*
|
|
1174
|
+
* **This does not rebuild anything, and cannot:** every resource field on this class is
|
|
1175
|
+
* `readonly` and set in the constructor, which is the same reason a quality change is
|
|
1176
|
+
* construction-time. A listener that wants to draw again has to take the page through a
|
|
1177
|
+
* reload. It is here so the moment is visible in a trace rather than silent, and so the
|
|
1178
|
+
* promise `preventDefault` makes has somebody on the other end of it.
|
|
1179
|
+
*/
|
|
1180
|
+
onContextRestored(listener: () => void): void;
|
|
1181
|
+
/** Rebuilt once per `bindMeshPass`, never per draw. */
|
|
1182
|
+
private readonly frustum;
|
|
1183
|
+
/**
|
|
1184
|
+
* Whether a mesh placed by this matrix is anywhere in the frame.
|
|
1185
|
+
*
|
|
1186
|
+
* Valid between `bindMeshPass` and `endFrame`, because that is when a camera has been given.
|
|
1187
|
+
* **A consumer is better placed to use this than `cullDraws` is**: it can skip the model
|
|
1188
|
+
* matrix, the animation update and the material switch as well, none of which the renderer can
|
|
1189
|
+
* avoid once `drawMesh` has been called.
|
|
1190
|
+
*/
|
|
1191
|
+
visible(bounds: Bounds, model: ReadonlyMat4): boolean;
|
|
1192
|
+
/**
|
|
1193
|
+
* The occlusion buffer, or null where the profile did not ask for one.
|
|
1194
|
+
*
|
|
1195
|
+
* **Built at the first `bindMeshPass` rather than at construction**, because a field initialiser
|
|
1196
|
+
* runs before the constructor body and `quality` is not resolved yet there. Resized when the
|
|
1197
|
+
* drawing buffer's aspect changes and cleared at every pass — which is also where the
|
|
1198
|
+
* view-projection it projects through comes from.
|
|
1199
|
+
*/
|
|
1200
|
+
private occlusion;
|
|
1201
|
+
/**
|
|
1202
|
+
* Declare a box that things behind it may safely be hidden by.
|
|
1203
|
+
*
|
|
1204
|
+
* **Between `bindMeshPass` and the first `drawMesh`**, because the pass is what supplies the
|
|
1205
|
+
* camera and the first test is what freezes the pyramid. A no-op when the profile asked for no
|
|
1206
|
+
* occlusion buffer, so a consumer may call it unconditionally.
|
|
1207
|
+
*
|
|
1208
|
+
* The box is in the model matrix's own space, and it is the box you may be *hidden by* rather
|
|
1209
|
+
* than the one you occupy — an occluder larger than the solid it stands for culls things that are
|
|
1210
|
+
* visible, which is a hole in the world. `OcclusionBuffer` carries the rest of the reasoning.
|
|
1211
|
+
*/
|
|
1212
|
+
addOccluder(min: ArrayLike<number>, max: ArrayLike<number>, model: ReadonlyMat4): void;
|
|
1213
|
+
/**
|
|
1214
|
+
* Whether a mesh is entirely behind the occluders declared this frame.
|
|
1215
|
+
*
|
|
1216
|
+
* **False whenever anything is uncertain**, including when no occluders were declared and when
|
|
1217
|
+
* the profile asked for no buffer — so a consumer may call it unconditionally and a scene that
|
|
1218
|
+
* declares nothing behaves exactly as it did before. Read it beside `visible`, which answers the
|
|
1219
|
+
* other half of the same question.
|
|
1220
|
+
*/
|
|
1221
|
+
occluded(bounds: Bounds, model: ReadonlyMat4): boolean;
|
|
1222
|
+
private readonly passes;
|
|
1223
|
+
/** Built once so invoking somebody else's pass allocates nothing. See `PassDefinition.draw`. */
|
|
1224
|
+
/**
|
|
1225
|
+
* Built once and mutated, because `drawPass` is a per-frame path and the house rule about
|
|
1226
|
+
* allocation binds it. The grade is written each call, since it depends on the frame.
|
|
1227
|
+
*/
|
|
1228
|
+
private passContext;
|
|
1229
|
+
private passDevice;
|
|
1230
|
+
/**
|
|
1231
|
+
* Let something outside this file draw into the frame.
|
|
1232
|
+
*
|
|
1233
|
+
* **The one thing that makes a rendering package possible.** The verb surface here is fixed and
|
|
1234
|
+
* a package cannot add to it, so `splats`, `ui2d` and `xr` had nowhere at all to put a draw
|
|
1235
|
+
* call. `init` runs now rather than at the first frame, because building a pipeline in the
|
|
1236
|
+
* frame loop is the allocation the house rules are about.
|
|
1237
|
+
*/
|
|
1238
|
+
registerPass(definition: PassDefinition): PassHandle;
|
|
1239
|
+
/**
|
|
1240
|
+
* How many registered passes declare `prepare`, so a frame with none walks nothing.
|
|
1241
|
+
*
|
|
1242
|
+
* The registry is a slot table and enumerating it every frame to discover that nobody wants a
|
|
1243
|
+
* step is exactly the per-frame work the house rules are about. A counter is one integer and
|
|
1244
|
+
* two increments over the life of a pass.
|
|
1245
|
+
*/
|
|
1246
|
+
private preparingPasses;
|
|
1247
|
+
/** Mutated in place, because `beginFrame` is a per-frame path. See `PassDefinition.prepare`. */
|
|
1248
|
+
private prepareContext;
|
|
1249
|
+
/**
|
|
1250
|
+
* Give every pass that owns a target its chance to fill it, before the frame's own is bound.
|
|
1251
|
+
*
|
|
1252
|
+
* **The default framebuffer is bound here and must still be bound when this returns** — the
|
|
1253
|
+
* contract `PrepareContext` writes out. Nothing verifies it, which is the honest state of this
|
|
1254
|
+
* backend: there is no verb-level graph to schedule against, so ordering is the caller's
|
|
1255
|
+
* politeness rather than a property of an encoder. What would make that wrong is two
|
|
1256
|
+
* target-owning passes and one of them misbehaving, and the answer then is a debug build that
|
|
1257
|
+
* reads the binding back after each `prepare` and names the pass that lost it.
|
|
1258
|
+
*/
|
|
1259
|
+
private runPreparePasses;
|
|
1260
|
+
/**
|
|
1261
|
+
* Run one, here, now.
|
|
1262
|
+
*
|
|
1263
|
+
* **Immediate, because this backend draws immediately**: the framebuffer the frame is going
|
|
1264
|
+
* into is already bound and a callback *is* a pass. The WebGPU side records it instead and
|
|
1265
|
+
* runs it at the flush, and the difference is invisible to the caller — which is the point of
|
|
1266
|
+
* both. The caller's position in its own draw order is the only thing that decides where this
|
|
1267
|
+
* geometry lands, exactly as it is for every built-in verb.
|
|
1268
|
+
*/
|
|
1269
|
+
drawPass(handle: PassHandle): void;
|
|
1270
|
+
/** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
|
|
1271
|
+
unregisterPass(handle: PassHandle): void;
|
|
1272
|
+
/**
|
|
1273
|
+
* Whether this backend can run a compute definition at all. It cannot.
|
|
1274
|
+
*
|
|
1275
|
+
* **A value-typed member answered honestly**, which the 2026-08-13 rule requires of every
|
|
1276
|
+
* backend: a missing method names itself and a missing property is `undefined`, which in
|
|
1277
|
+
* arithmetic is a picture rather than an error. `FrameTimer.available` is the same shape, and
|
|
1278
|
+
* its comment names the alternative as the worst number available to invent.
|
|
1279
|
+
*
|
|
1280
|
+
* What it gives up: a consumer must branch, and a package that wants a GPU sort has to carry a
|
|
1281
|
+
* second implementation. What would make it wrong: nothing short of WebGL2 gaining compute
|
|
1282
|
+
* shaders, which it will not — the specification has none and is closed.
|
|
1283
|
+
*/
|
|
1284
|
+
readonly computeSupported: boolean;
|
|
1285
|
+
/** Labels already refused, so a refusal is once a definition rather than once a frame. */
|
|
1286
|
+
private readonly refusedComputes;
|
|
1287
|
+
/**
|
|
1288
|
+
* The table the lit pass fetches, and the scratch the binner fills.
|
|
1289
|
+
*
|
|
1290
|
+
* **Allocated whether or not clustering is on**, because the sampler is declared
|
|
1291
|
+
* unconditionally and a declared sampler needs a complete texture bound to it — an incomplete
|
|
1292
|
+
* one is undefined behaviour a driver may read before it evaluates the branch that would have
|
|
1293
|
+
* skipped it, which is the argument `emptyTexture.ts` already makes. With clustering off this is
|
|
1294
|
+
* one texel rather than 292 KB.
|
|
1295
|
+
*/
|
|
1296
|
+
private clusterTexture;
|
|
1297
|
+
private clusterTable;
|
|
1298
|
+
/** Refilled per frame; holds references and allocates nothing. See `resolvePointLights`. */
|
|
1299
|
+
private readonly clusterLights;
|
|
1300
|
+
/** near, far, tan(fovY/2), aspect — written per frame, uploaded as one vec4. */
|
|
1301
|
+
private readonly clusterFrustum;
|
|
1302
|
+
/**
|
|
1303
|
+
* Bind the froxel table and the four numbers that address it.
|
|
1304
|
+
*
|
|
1305
|
+
* **Always, even with clustering off.** `uClustered` is what decides whether the shader reads
|
|
1306
|
+
* any of it, and the binding has to be valid either way; see `clusterTexture`.
|
|
1307
|
+
*/
|
|
1308
|
+
/**
|
|
1309
|
+
* Upload the photometric profiles a consumer loaded, and bind the atlas.
|
|
1310
|
+
*
|
|
1311
|
+
* **Called once per set rather than per frame**, because a profile is an asset: a fixture's
|
|
1312
|
+
* distribution does not change while the game runs, and re-uploading it every frame would be a
|
|
1313
|
+
* texture upload per frame for data that is constant.
|
|
1314
|
+
*
|
|
1315
|
+
* Passing an empty list is how a consumer clears them, and it leaves the single row of ones the
|
|
1316
|
+
* atlas falls back to — which is the multiplicative identity rather than darkness, so a scene
|
|
1317
|
+
* that drops its profiles goes back to plain lights rather than going black.
|
|
1318
|
+
*/
|
|
1319
|
+
/**
|
|
1320
|
+
* Upload the cookies a consumer loaded, and bind the atlas.
|
|
1321
|
+
*
|
|
1322
|
+
* **One row of square tiles**, `COOKIE_TILE` a side, each image scaled into its tile by the
|
|
1323
|
+
* driver. Called once per set rather than per frame, because a cookie is an asset: a fixture's
|
|
1324
|
+
* mask does not change while the game runs.
|
|
1325
|
+
*
|
|
1326
|
+
* Passing an empty list clears them and leaves the single white texel the atlas falls back to,
|
|
1327
|
+
* which is the multiplicative identity — a scene that drops its cookies goes back to plain
|
|
1328
|
+
* lights rather than going black.
|
|
1329
|
+
*
|
|
1330
|
+
* **The engine ships no cookie and never will**, which is the same line the glyph keys draw:
|
|
1331
|
+
* what a fixture throws is a consumer's art. `zero texture files` in `AGENTS.md` is a rule about
|
|
1332
|
+
* what the *engine* carries, and a consumer's own image has always been allowed — it is what
|
|
1333
|
+
* `createSurfaceTexture` and the material maps take.
|
|
1334
|
+
*/
|
|
1335
|
+
setSpotCookies(images: readonly TexImageSource[]): void;
|
|
1336
|
+
setIesProfiles(profiles: readonly PhotometricProfile[]): void;
|
|
1337
|
+
/** Bind the atlas and say how many rows it has. Always, for the reason `bindClusters` gives. */
|
|
1338
|
+
private bindIesAtlas;
|
|
1339
|
+
private bindClusters;
|
|
1340
|
+
/**
|
|
1341
|
+
* Refuse, in words, and hand back the handle that names nothing.
|
|
1342
|
+
*
|
|
1343
|
+
* **This is the defined-state half of the 2026-08-13 rule**, whose other half forbids a silent
|
|
1344
|
+
* no-op. There is nothing to degrade *to* here — a pass this backend cannot run can at least be
|
|
1345
|
+
* an empty method that leaves a visible hole in the picture, and a compute stage has no picture
|
|
1346
|
+
* to leave a hole in. So the degraded state is the refusal itself, spoken at the one moment a
|
|
1347
|
+
* consumer can still act on it: registration, which is init time, where the house rule puts
|
|
1348
|
+
* failures and never the frame loop, where it forbids them.
|
|
1349
|
+
*
|
|
1350
|
+
* **Zero rather than a live handle**, because `definitionAt` already reads zero as naming
|
|
1351
|
+
* nothing — handles start at 1 for exactly this reason — so the `dispatchCompute` that follows
|
|
1352
|
+
* is a no-op which has already announced itself rather than one that never speaks.
|
|
1353
|
+
*
|
|
1354
|
+
* `console.error` rather than a throw, for the reason `createGpuSurface` gives about the same
|
|
1355
|
+
* choice: a consumer registering during boot should get a diagnostic it can act on, not an
|
|
1356
|
+
* exception that takes the page down over a capability it can live without.
|
|
1357
|
+
*/
|
|
1358
|
+
registerCompute(definition: ComputeDefinition): ComputeHandle;
|
|
1359
|
+
/** Nothing was registered, so there is nothing to dispatch. See `registerCompute`. */
|
|
1360
|
+
dispatchCompute(_handle: ComputeHandle): void;
|
|
1361
|
+
/** Nothing was registered, so there is nothing to release. See `registerCompute`. */
|
|
1362
|
+
unregisterCompute(_handle: ComputeHandle): void;
|
|
1363
|
+
/** Side length of the depth map, so callers can size the light frustum. */
|
|
1364
|
+
get shadowMapSize(): number;
|
|
1365
|
+
private readonly pickables;
|
|
1366
|
+
private readonly pickOrigin;
|
|
1367
|
+
private readonly pickDirection;
|
|
1368
|
+
/**
|
|
1369
|
+
* Construct the WebGL2 renderer directly.
|
|
1370
|
+
*
|
|
1371
|
+
* @deprecated Use `createRenderer(canvas, quality)`. This constructor can only ever give
|
|
1372
|
+
* you WebGL2, so a consumer that calls it opts out of WebGPU permanently and silently.
|
|
1373
|
+
* `createRenderer` returns the best backend the browser will give, falls back to this one
|
|
1374
|
+
* on its own, and tells you which it built.
|
|
1375
|
+
*
|
|
1376
|
+
* **It keeps working and it is not going away.** Three applications construct one today
|
|
1377
|
+
* and none of them is being made to move on a schedule that is not theirs; the deprecation
|
|
1378
|
+
* is how a *new* consumer is pointed at the right door, not a removal notice. The engine's
|
|
1379
|
+
* own factory calls it, which is the point: this is the implementation of the WebGL2
|
|
1380
|
+
* branch rather than a path being retired.
|
|
1381
|
+
*/
|
|
1382
|
+
constructor(canvas: HTMLCanvasElement, qualityOptions?: RenderQualityOptions);
|
|
1383
|
+
/**
|
|
1384
|
+
* Release the drawing context and everything this renderer created on it.
|
|
1385
|
+
*
|
|
1386
|
+
* Needed by any consumer whose renderer does not live as long as its page. A game that
|
|
1387
|
+
* *is* the page builds its renderer once and never tears it down, and for a long time
|
|
1388
|
+
* nothing here noticed the other shape existed: a game embedded in a website is mounted
|
|
1389
|
+
* when the player enters it and unmounted when they leave.
|
|
1390
|
+
*
|
|
1391
|
+
* Without this, each mount leaks a WebGL context. Browsers cap how many a process may
|
|
1392
|
+
* hold — Chrome at around sixteen — and past the cap `getContext` starts returning
|
|
1393
|
+
* null, so the failure is not a slow leak but a game that abruptly refuses to start
|
|
1394
|
+
* with "WebGL2 is not available" on a machine that plainly has it. Entering and
|
|
1395
|
+
* leaving a level a dozen times is enough.
|
|
1396
|
+
*
|
|
1397
|
+
* **The context is not dropped by default, and that is the hard-won part.** The obvious
|
|
1398
|
+
* implementation ends with `WEBGL_lose_context.loseContext()`, since the context is the
|
|
1399
|
+
* capped resource and deleting objects does not hand it back. But the context belongs
|
|
1400
|
+
* to the *canvas*, and the canvas belongs to the consumer — and a canvas whose context
|
|
1401
|
+
* has been lost this way is dead until something calls `restoreContext` on it.
|
|
1402
|
+
*
|
|
1403
|
+
* A React consumer keeps the same `<canvas>` element across an effect re-run, so a
|
|
1404
|
+
* default `loseContext` poisons the element the next renderer is about to be built on:
|
|
1405
|
+
* it reports success, then every call fails with "object does not belong to this
|
|
1406
|
+
* context" and the screen is black. Measured exactly that way while porting an
|
|
1407
|
+
* embedded scene, where a hot reload made it look like the renderer had broken itself.
|
|
1408
|
+
*
|
|
1409
|
+
* So the default frees what this class allocated and leaves the canvas usable. A
|
|
1410
|
+
* consumer that is *discarding* the canvas — the normal unmount — can pass
|
|
1411
|
+
* `releaseContext` to hand the context back immediately rather than waiting for the
|
|
1412
|
+
* element to be collected, which is worth doing because browsers cap contexts per
|
|
1413
|
+
* process (Chrome at about sixteen) and past the cap `getContext` returns null.
|
|
1414
|
+
*
|
|
1415
|
+
* Idempotent, because a teardown path that can run twice — a React effect cleanup
|
|
1416
|
+
* racing an unmount — must not throw on the second call.
|
|
1417
|
+
*/
|
|
1418
|
+
/**
|
|
1419
|
+
* Resolve once every pipeline this renderer holds is ready to draw with.
|
|
1420
|
+
*
|
|
1421
|
+
* **Immediate on this backend, and that is the point of it being here.** WebGL2 links a
|
|
1422
|
+
* program synchronously inside `compileProgram`, so nothing is ever outstanding by the time
|
|
1423
|
+
* a caller holds a renderer. WebGPU is the opposite: `createRenderPipeline` returns before
|
|
1424
|
+
* the driver has compiled anything and leaves the shader to the first draw that needs it,
|
|
1425
|
+
* which puts the cost inside a frame with the queue stalled behind it. A consumer awaiting
|
|
1426
|
+
* this before its first frame gets the right behaviour on both without asking which backend
|
|
1427
|
+
* it has, which is the contract `RendererApi` exists to keep.
|
|
1428
|
+
*/
|
|
1429
|
+
ready(): Promise<void>;
|
|
1430
|
+
dispose(options?: {
|
|
1431
|
+
releaseContext?: boolean;
|
|
1432
|
+
}): void;
|
|
1433
|
+
createMesh(data: MeshData, options?: MeshOptions): Mesh;
|
|
1434
|
+
/**
|
|
1435
|
+
* Geometry in, handle out — but the geometry lands over as many frames as the caller gives it.
|
|
1436
|
+
*
|
|
1437
|
+
* See `RendererApi.createMeshIncremental` for what this is for and what a caller may do with a
|
|
1438
|
+
* mesh that has not finished arriving. The iterator here adds the one thing `mesh.ts` cannot
|
|
1439
|
+
* know about: **a lost context ends the upload**, because an upload that spans frames can
|
|
1440
|
+
* straddle one and every buffer it was filling is gone. Done with `complete` still false is
|
|
1441
|
+
* the signal that it was abandoned.
|
|
1442
|
+
*/
|
|
1443
|
+
createMeshIncremental(data: MeshData, options?: MeshOptions): IncrementalMesh;
|
|
1444
|
+
/**
|
|
1445
|
+
* Compile every flat variant this mesh can be drawn with.
|
|
1446
|
+
*
|
|
1447
|
+
* Shared by the two `createMesh` entry points: which variants a mesh needs is decided by the
|
|
1448
|
+
* data, not by whether the geometry lands in one call or over six frames.
|
|
1449
|
+
*/
|
|
1450
|
+
private warmVariants;
|
|
1451
|
+
/**
|
|
1452
|
+
* Rewrite a mesh's positions, and its normals where the caller has them.
|
|
1453
|
+
*
|
|
1454
|
+
* The VAO is left alone: an attribute pointer describes where a buffer's data *is*, and the
|
|
1455
|
+
* buffer is the same buffer. Rebinding here would cost a state change for nothing.
|
|
1456
|
+
*/
|
|
1457
|
+
updateMesh(mesh: Mesh, positions: Float32Array, normals?: Float32Array): void;
|
|
1458
|
+
/**
|
|
1459
|
+
* Compile the skinned flat program, once, on the first rigged mesh.
|
|
1460
|
+
*
|
|
1461
|
+
* The four quality flags are construction-time and give this renderer one flat program.
|
|
1462
|
+
* Skinning is not: one scene holds rigged characters and static walls, and both draw through
|
|
1463
|
+
* the lit pass — so it is a second program rather than a fifth flag, selected per draw.
|
|
1464
|
+
*
|
|
1465
|
+
* **It is fed immediately if a pass is already bound.** A program compiled after this frame's
|
|
1466
|
+
* `bindMeshPass` would otherwise carry every uniform at zero, and zero is a real value for most
|
|
1467
|
+
* of them — the failure this file records for `uGrain` and `uAmbientGround`. `createMesh` is
|
|
1468
|
+
* construction rather than a frame path, so the extra write costs nothing anyone measures.
|
|
1469
|
+
*
|
|
1470
|
+
* What it costs is a second compile and a program switch between skinned and unskinned batches,
|
|
1471
|
+
* which a caller controls by drawing its characters together. What would make it wrong is a
|
|
1472
|
+
* fifth quality flag arriving that skinning should have been: it should not, because a quality
|
|
1473
|
+
* flag is chosen once for a renderer and this is chosen per mesh.
|
|
1474
|
+
*/
|
|
1475
|
+
private ensureSkinnedProgram;
|
|
1476
|
+
/**
|
|
1477
|
+
* Compile one flat variant, once, and feed it this frame's state.
|
|
1478
|
+
*
|
|
1479
|
+
* Lazy and at `createMesh` for the reason the skinned one was: at construction it costs every
|
|
1480
|
+
* game a compile for a feature most never use, and in the frame loop it could throw where the
|
|
1481
|
+
* rule is that initialisation fails loudly so a running frame never has to.
|
|
1482
|
+
*
|
|
1483
|
+
* **Fed immediately if a pass is already bound.** A program compiled after this frame's
|
|
1484
|
+
* `bindMeshPass` would otherwise carry every uniform at zero, and zero is a real value for most
|
|
1485
|
+
* of them — the failure this file records for `uGrain` and `uAmbientGround`.
|
|
1486
|
+
*/
|
|
1487
|
+
private ensureVariant;
|
|
1488
|
+
/**
|
|
1489
|
+
* Upload an image the caller already has, for use as surface colour.
|
|
1490
|
+
*
|
|
1491
|
+
* Takes a `TexImageSource` rather than a URL, and that is the whole boundary: the
|
|
1492
|
+
* engine ships no image assets and fetches nothing, so where the pixels came from —
|
|
1493
|
+
* a canvas drawn procedurally at runtime, a decoded bitmap, a video frame — stays
|
|
1494
|
+
* the consumer's decision. What it gains is a GPU object with sane sampler state.
|
|
1495
|
+
*/
|
|
1496
|
+
createSurfaceTexture(source: TexImageSource, options?: SurfaceTextureOptions): SurfaceTexture;
|
|
1497
|
+
/**
|
|
1498
|
+
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
1499
|
+
*
|
|
1500
|
+
* Goes through the renderer because the GL context does not leave this directory, and
|
|
1501
|
+
* `SurfaceTexture.update` needs one — without this a consumer could create a texture
|
|
1502
|
+
* and dispose it but never change it, which is exactly the case an image that decodes
|
|
1503
|
+
* *after* the world is built runs into. Re-uploading beats constructing a second
|
|
1504
|
+
* texture: the binding a draw loop already holds stays valid, so the swap is a swap
|
|
1505
|
+
* rather than a rebuild.
|
|
1506
|
+
*
|
|
1507
|
+
* Not a hot path — it re-uploads the whole image and regenerates the mip chain.
|
|
1508
|
+
*/
|
|
1509
|
+
updateSurfaceTexture(texture: SurfaceTexture, source: TexImageSource): void;
|
|
1510
|
+
disposeSurfaceTexture(texture: SurfaceTexture): void;
|
|
1511
|
+
/**
|
|
1512
|
+
* Choose the surface texture the following `drawMesh` calls sample, or `null` for none.
|
|
1513
|
+
*
|
|
1514
|
+
* State rather than an argument to `drawMesh`, for two reasons. A texture belongs to a
|
|
1515
|
+
* *material*, and a material covers many draws — a biome's walls are one image and
|
|
1516
|
+
* twenty meshes — so setting it per draw would re-bind the same texture twenty times
|
|
1517
|
+
* to say the same thing. And `drawMesh` already carries a model, a layer and a tint;
|
|
1518
|
+
* a fifth and sixth positional argument is where a signature stops being readable.
|
|
1519
|
+
*
|
|
1520
|
+
* `bindMeshPass` resets this to none, so a pass cannot inherit a material from the
|
|
1521
|
+
* one before it and every caller starts from the same known state.
|
|
1522
|
+
*
|
|
1523
|
+
* `uScale`/`vScale` are repeats across the mesh's own UV range: geometry authored in
|
|
1524
|
+
* metres is textured at whatever density the material wants without rebuilding it.
|
|
1525
|
+
*/
|
|
1526
|
+
/**
|
|
1527
|
+
* Choose the joint palette the following `drawMesh` calls skin by, or null for none.
|
|
1528
|
+
*
|
|
1529
|
+
* Null is what an unskinned draw needs and is the default, so a game that never animates never
|
|
1530
|
+
* allocates a palette texture and never leaves one bound.
|
|
1531
|
+
*
|
|
1532
|
+
* The palette is sixteen floats a joint, column-major, exactly as `Skeleton.palette` produces —
|
|
1533
|
+
* this renderer never learns where it came from, which is the boundary that keeps the animation
|
|
1534
|
+
* runtime out of core.
|
|
1535
|
+
*/
|
|
1536
|
+
setSkinPalette(palette: Float32Array | null): void;
|
|
1537
|
+
/**
|
|
1538
|
+
* Choose the morph weights the following `drawMesh` calls deform by, or null for none.
|
|
1539
|
+
*
|
|
1540
|
+
* One weight per target the mesh carries. **The weights are per draw and the deltas are per
|
|
1541
|
+
* mesh**, which is the split that matters: two characters sharing one head mesh wear different
|
|
1542
|
+
* expressions without a second copy of the geometry.
|
|
1543
|
+
*/
|
|
1544
|
+
setMorphWeights(weights: Float32Array | null): void;
|
|
1545
|
+
setMaterial(material: SurfaceMaterial | null): void;
|
|
1546
|
+
/**
|
|
1547
|
+
* One flat program's view of the current material.
|
|
1548
|
+
*
|
|
1549
|
+
* **The `albedoEnabled` cache that used to guard the last two writes is gone.** It skipped a
|
|
1550
|
+
* `uniform1i` when the flag had not changed, which was correct for one program and is a wrong
|
|
1551
|
+
* answer for two: the second would keep whatever it was left with. One uniform write per
|
|
1552
|
+
* material change is not a cost worth a cache that can be wrong.
|
|
1553
|
+
*/
|
|
1554
|
+
private writeMaterialState;
|
|
1555
|
+
/**
|
|
1556
|
+
* @deprecated Use `setMaterial`.
|
|
1557
|
+
*
|
|
1558
|
+
* Kept because it has 56 call sites across four repositories, three of which deploy on push;
|
|
1559
|
+
* removing it is a major version taken deliberately rather than a side effect of adding a map.
|
|
1560
|
+
* `material.test.ts` asserts the two make the same calls, which is the only thing that makes a
|
|
1561
|
+
* wrapper safe to leave standing.
|
|
1562
|
+
*/
|
|
1563
|
+
setSurfaceTexture(texture: SurfaceTexture | null, uScale?: number, vScale?: number, cutout?: number): void;
|
|
1564
|
+
/**
|
|
1565
|
+
* Rebind `SURFACE_TEXTURE_UNIT` to whatever `setSurfaceTexture` last put there.
|
|
1566
|
+
*
|
|
1567
|
+
* Only `drawSdfText` calls this, after the atlas has finished borrowing the same unit.
|
|
1568
|
+
* No uniform is written here — the flat program's `uAlbedo`/`uAlbedoEnabled` were never
|
|
1569
|
+
* touched by the text draw and are still correct, since that draw runs its own separate
|
|
1570
|
+
* program. What moved is unit 15's actual `WebGLTexture` binding, which is GL state, not
|
|
1571
|
+
* program state, and this puts it back to what `currentSurfaceTexture` says it should be.
|
|
1572
|
+
*/
|
|
1573
|
+
private restoreSurfaceTextureUnit;
|
|
1574
|
+
/**
|
|
1575
|
+
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
1576
|
+
*
|
|
1577
|
+
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
1578
|
+
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
1579
|
+
* This stays as the override for the case that has no answer to state: an imported model
|
|
1580
|
+
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
1581
|
+
*
|
|
1582
|
+
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
1583
|
+
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
1584
|
+
* leaves geometry that did not exactly as it is.
|
|
1585
|
+
*/
|
|
1586
|
+
/**
|
|
1587
|
+
* How much of the environment the following draws mirror, 0 to 1.
|
|
1588
|
+
*
|
|
1589
|
+
* Pass state beside the grain, and the counterpart to it: grain is a surface being uneven,
|
|
1590
|
+
* this is a surface being smooth enough to carry an image. Polished paint, glass, chrome
|
|
1591
|
+
* and still water want it; plaster and stone want none.
|
|
1592
|
+
*
|
|
1593
|
+
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
1594
|
+
* exactly as it did.
|
|
1595
|
+
*/
|
|
1596
|
+
setSurfaceReflectivity(amount: number): void;
|
|
1597
|
+
/**
|
|
1598
|
+
* How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
|
|
1599
|
+
*
|
|
1600
|
+
* See `uEnvironmentGain` in the preamble for the whole argument. In one line: a metal's Fresnel
|
|
1601
|
+
* base is its own albedo, so it takes no ambient and no direct diffuse and *is* its reflection,
|
|
1602
|
+
* and when the only environment is a cube of nearby geometry that leaves it as dark as the room
|
|
1603
|
+
* happens to be. This is the stand-in for the image-based lighting that would carry the sky and
|
|
1604
|
+
* the sources instead, and it goes away when there is one.
|
|
1605
|
+
*
|
|
1606
|
+
* Not clamped above: the case it exists for is a room that is too dim, and a value below 1 is as
|
|
1607
|
+
* legitimate as one above. Held non-negative, because a negative environment is not a darker one.
|
|
1608
|
+
*/
|
|
1609
|
+
setEnvironmentGain(gain: number): void;
|
|
1610
|
+
/**
|
|
1611
|
+
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
1612
|
+
*
|
|
1613
|
+
* It mixed the ambient toward a box-filtered level of the probe, three below the coarsest,
|
|
1614
|
+
* lifted by a `max` so a partial probe could not darken a subject. Irradiance is nine projected
|
|
1615
|
+
* spherical-harmonic coefficients now, evaluated per fragment against the normal, and it is on
|
|
1616
|
+
* whenever a probe has been baked — with nothing to tune, because an approximation needed a dial
|
|
1617
|
+
* for the ways it was wrong and a cosine integral does not have them.
|
|
1618
|
+
*
|
|
1619
|
+
* **Deprecated rather than removed, and that is a compatibility decision rather than a
|
|
1620
|
+
* preference.** It is on the public surface and consumers call it; deleting it today would be a
|
|
1621
|
+
* breaking change across four repositories to save one method. It goes at the next major, where
|
|
1622
|
+
* a coordinated release is happening anyway.
|
|
1623
|
+
*/
|
|
1624
|
+
setEnvironmentAmbient(_amount: number): void;
|
|
1625
|
+
setSurfaceGrain(amount: number): void;
|
|
1626
|
+
/**
|
|
1627
|
+
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
1628
|
+
*
|
|
1629
|
+
* **The material half of `MeshData.relief`.** The geometry says which surfaces have texture;
|
|
1630
|
+
* this says what texture, because the same amount over a different scale is a different
|
|
1631
|
+
* material entirely. Asphalt is coarse and deep, roughly 60 bumps to the metre at full
|
|
1632
|
+
* strength; cast concrete is finer; orange peel on paint is finer again and shallow. The
|
|
1633
|
+
* geometry carrying all three is identical, which is exactly why the two halves are separate.
|
|
1634
|
+
*
|
|
1635
|
+
* `amount` scales whatever each vertex declared, so 0 switches it off for a pass without
|
|
1636
|
+
* rebuilding a mesh, the same way `setSurfaceGrain` does. `cyclesPerMetre` is how many bumps
|
|
1637
|
+
* fit in a metre of world.
|
|
1638
|
+
*
|
|
1639
|
+
* **The roughness of a surface grows with its relief**, automatically and not optionally: a
|
|
1640
|
+
* normal that wanders cannot hold a highlight narrower than the wander, and a narrow one on a
|
|
1641
|
+
* fast-varying normal is the sub-pixel sparkle 0.20.0 was spent removing. The amount is known
|
|
1642
|
+
* here rather than measured off the screen, which is what makes it safe; see the specular
|
|
1643
|
+
* speckle entry in docs/IMPROVEMENTS.md for the version that measured it instead and made
|
|
1644
|
+
* things three times worse.
|
|
1645
|
+
*
|
|
1646
|
+
* Defaults to off and `bindMeshPass` restores that, so a scene that never calls this is
|
|
1647
|
+
* unchanged.
|
|
1648
|
+
*/
|
|
1649
|
+
setSurfaceRelief(amount: number, cyclesPerMetre?: number): void;
|
|
1650
|
+
/**
|
|
1651
|
+
* How hard the bound surface texture's own luminance turns the shading normal, 0 for not at all.
|
|
1652
|
+
*
|
|
1653
|
+
* **Relief a photograph can give, where `setSurfaceRelief` invents it.** Noise is the right
|
|
1654
|
+
* answer for asphalt and cast concrete, whose structure has no particular arrangement. A rock,
|
|
1655
|
+
* a bark, a hammered plate has one: the bumps are where the picture says they are, and no noise
|
|
1656
|
+
* function can know where that is. This reads the height off the image `setSurfaceTexture`
|
|
1657
|
+
* already bound as colour, so a surface lights as the thing the photograph is of.
|
|
1658
|
+
*
|
|
1659
|
+
* **One image in both roles rather than a second sampler**, which is a decision with a cost
|
|
1660
|
+
* behind it: the fragment shader's widest permutation already declares more samplers than this
|
|
1661
|
+
* project's adapter offers and is legal only because several of them are marked as shared. It
|
|
1662
|
+
* is also the ordinary case rather than an unusual one, since a scene handing one image to
|
|
1663
|
+
* three.js's `map` and `bumpMap` together is exactly what this replaces.
|
|
1664
|
+
*
|
|
1665
|
+
* Scaled as three.js's `bumpScale` is, so a scene porting one carries the number over rather
|
|
1666
|
+
* than refitting it by eye, and a negative value inverts the relief there and here alike.
|
|
1667
|
+
* Deliberately not clamped to 0..1 the way `setSurfaceRelief` and `setSurfaceGrain` are: those
|
|
1668
|
+
* scale something the geometry declared, and this is an amplitude against a luminance gradient
|
|
1669
|
+
* with no natural ceiling.
|
|
1670
|
+
*
|
|
1671
|
+
* Nothing happens without a texture bound, because the shader gates on the same flag
|
|
1672
|
+
* `setSurfaceTexture(null)` clears. Defaults to off and `bindMeshPass` restores that, so a
|
|
1673
|
+
* scene that never calls this is unchanged.
|
|
1674
|
+
*/
|
|
1675
|
+
setSurfaceTextureRelief(scale: number): void;
|
|
1676
|
+
/**
|
|
1677
|
+
* Release a mesh's GPU buffers.
|
|
1678
|
+
*
|
|
1679
|
+
* Needed by anything that rebuilds geometry at runtime — a character
|
|
1680
|
+
* customiser recolouring an avatar, a level editor, a streamed chunk. Without
|
|
1681
|
+
* it every rebuild leaks a VAO and its buffers, which is invisible until a
|
|
1682
|
+
* player has spent a minute on one screen.
|
|
1683
|
+
*/
|
|
1684
|
+
/**
|
|
1685
|
+
* Scale the emissive of the following draws, or reset to what the environment says.
|
|
1686
|
+
*
|
|
1687
|
+
* Pass state, like `setSurfaceTexture`, and for the same reason: emissive strength
|
|
1688
|
+
* belongs to a *material* and a material covers many draws. `bindMeshPass` sets it from
|
|
1689
|
+
* `env.emissiveGain` and this overrides it between draws.
|
|
1690
|
+
*
|
|
1691
|
+
* It exists because emissive is per-vertex data, so anything that *pulses* — a threshold
|
|
1692
|
+
* breathing, a ceiling answering a kick drum — could not be expressed at all without
|
|
1693
|
+
* rebuilding geometry every frame. The light around such a thing could pulse and the
|
|
1694
|
+
* glowing surface itself could not, which reads as a lamp brightening while its own bulb
|
|
1695
|
+
* stays flat.
|
|
1696
|
+
*/
|
|
1697
|
+
setEmissiveGain(gain: number): void;
|
|
1698
|
+
/** The instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
1699
|
+
private flatInstancedProgram;
|
|
1700
|
+
private flatInstancedUniforms;
|
|
1701
|
+
/**
|
|
1702
|
+
* Compile the instanced variant, once, and feed it this frame's pass state.
|
|
1703
|
+
*
|
|
1704
|
+
* The same shape as `ensureVariant` and for the same reason: a program compiled after this
|
|
1705
|
+
* frame's `bindMeshPass` would otherwise carry every uniform at zero, and zero is a real value
|
|
1706
|
+
* for most of them — `uGrain` unwritten switches grain off across every surface rather than
|
|
1707
|
+
* weakening it.
|
|
1708
|
+
*/
|
|
1709
|
+
private ensureInstancedProgram;
|
|
1710
|
+
/**
|
|
1711
|
+
* Attach per-instance placement to a mesh already on the device.
|
|
1712
|
+
*
|
|
1713
|
+
* One batch per mesh: the attributes bind to the mesh's own vertex array, of which a mesh has
|
|
1714
|
+
* one. `Mesh.attachInstances` is what refuses a second, and says why.
|
|
1715
|
+
*/
|
|
1716
|
+
createInstanced(mesh: Mesh, capacity: number): InstancedBatch;
|
|
1717
|
+
/** Push placement and colour. Only the live prefix. */
|
|
1718
|
+
uploadInstanced(batch: InstancedBatch, data: MeshInstances): void;
|
|
1719
|
+
/** Draw every live instance, opaque. */
|
|
1720
|
+
drawInstanced(batch: InstancedBatch, data: MeshInstances): void;
|
|
1721
|
+
/** Draw every live instance, blended. See `drawTranslucentMesh` for what the options mean. */
|
|
1722
|
+
drawTranslucentInstanced(batch: InstancedBatch, data: MeshInstances, opacity: number, options?: TranslucentMeshOptions): void;
|
|
1723
|
+
/** Release the placement. The mesh is the caller's and is not released. */
|
|
1724
|
+
disposeInstanced(batch: InstancedBatch): void;
|
|
1725
|
+
/**
|
|
1726
|
+
* One draw and one material, however many instances.
|
|
1727
|
+
*
|
|
1728
|
+
* **No `uModel` and no `uTint` written**, because the instanced program declares neither — its
|
|
1729
|
+
* placement and colour arrive as attributes. Writing them would be a `uniform*` call against
|
|
1730
|
+
* location `null`, which GL ignores, so this would look like it worked; it is left out because
|
|
1731
|
+
* saying it is the point of the variant.
|
|
1732
|
+
*
|
|
1733
|
+
* **No frustum cull**, unlike `drawMesh`. A batch's bounds are the union of its instances and
|
|
1734
|
+
* the mesh's own bounds describe one of them at the origin, so testing that box would cull a
|
|
1735
|
+
* street of cars whenever the copy at the origin left the view. Culling a batch is the
|
|
1736
|
+
* consumer's, which is also where the placements are.
|
|
1737
|
+
*/
|
|
1738
|
+
private submitInstanced;
|
|
1739
|
+
disposeMesh(mesh: Mesh): void;
|
|
1740
|
+
/**
|
|
1741
|
+
* Release a resource this renderer created, for every kind that needs the context back.
|
|
1742
|
+
*
|
|
1743
|
+
* **Reported from outside, and it was a real leak.** `disposeMesh` and
|
|
1744
|
+
* `disposeSurfaceTexture` existed and nothing else did, so eight other kinds of resource
|
|
1745
|
+
* were handed out with no way to release them: each one's own `dispose` takes the
|
|
1746
|
+
* `WebGL2RenderingContext`, which this package deliberately never exposes. The engine's own
|
|
1747
|
+
* demos could call them because they hold their own `gl` from creating the canvas; nobody
|
|
1748
|
+
* outside could. It costs nothing when a consumer tears the whole renderer down, since
|
|
1749
|
+
* releasing the context takes the programs and buffers with it, and it costs a program per
|
|
1750
|
+
* rebuild for anyone who swaps weather or scenery without rebuilding the renderer. That is
|
|
1751
|
+
* the ordinary case for a scene that changes, and the consumer who reported it was leaking
|
|
1752
|
+
* one per scenario switch and living with it.
|
|
1753
|
+
*
|
|
1754
|
+
* Each guards on a lost context for the same reason `disposeMesh` does: the old context took
|
|
1755
|
+
* its objects with it, and asking the new one to delete them is an INVALID_OPERATION apiece,
|
|
1756
|
+
* which buries whatever caused the loss under a page of noise.
|
|
1757
|
+
*/
|
|
1758
|
+
disposePlumes(plumes: PlumeRenderer): void;
|
|
1759
|
+
/** Release a wind streak lattice. See `disposePlumes`. */
|
|
1760
|
+
disposeWindStreaks(streaks: WindStreakRenderer): void;
|
|
1761
|
+
/** Release a flock. See `disposePlumes`. */
|
|
1762
|
+
disposeFlock(flock: FlockRenderer): void;
|
|
1763
|
+
/** Release an instanced batch made by `createScatter`. See `disposePlumes`. */
|
|
1764
|
+
disposeScatter(scatter: InstancedMesh): void;
|
|
1765
|
+
/** Release a particle batch. See `disposePlumes`. */
|
|
1766
|
+
disposeParticles(particles: ParticleBatch): void;
|
|
1767
|
+
/** Release a bolt batch. See `disposePlumes`. */
|
|
1768
|
+
disposeBolts(bolts: BoltBatch): void;
|
|
1769
|
+
/** Release a line batch. See `disposePlumes`. */
|
|
1770
|
+
disposeLines(lines: LineBatch): void;
|
|
1771
|
+
/** Release a water renderer. See `disposePlumes`. */
|
|
1772
|
+
disposeWater(water: WaterRenderer): void;
|
|
1773
|
+
/** Release a caustics renderer. See `disposePlumes`. */
|
|
1774
|
+
disposeCaustics(caustics: CausticsRenderer): void;
|
|
1775
|
+
createPlumes(plumes: readonly PlumePlacement[], options: PlumeOptions): PlumeRenderer;
|
|
1776
|
+
createWindStreaks(options?: WindStreakOptions): WindStreakRenderer;
|
|
1777
|
+
/** Draw after the opaque scene: streaks are blended and write no depth. */
|
|
1778
|
+
drawWindStreaks(streaks: WindStreakRenderer, camera: Camera, wind: WindField, timeSeconds: number, tint: Vec3, env: Environment): void;
|
|
1779
|
+
/**
|
|
1780
|
+
* A 3D text object: one string, drawn as instanced glyph cubes over the
|
|
1781
|
+
* scene. Create one per message slot and reuse it — `setText` is a no-op when
|
|
1782
|
+
* the string has not changed.
|
|
1783
|
+
*/
|
|
1784
|
+
createText(): TextRenderer;
|
|
1785
|
+
/** Lay a string out. A no-op when it is the string already laid out. */
|
|
1786
|
+
setText(text: TextRenderer, content: string): void;
|
|
1787
|
+
/** A solid rectangle of cells, for a keycap or a backing plate. See `TextLayout.setPlate`. */
|
|
1788
|
+
setPlate(text: TextRenderer, widthCells: number, heightCells: number, bottomCell: number): void;
|
|
1789
|
+
/**
|
|
1790
|
+
* Draw a laid-out string over the scene.
|
|
1791
|
+
*
|
|
1792
|
+
* The viewport is the caller's rather than this renderer's: an overlay is positioned in
|
|
1793
|
+
* whatever box the caller lays out in, and the game's own card is not always the canvas.
|
|
1794
|
+
*/
|
|
1795
|
+
drawText(text: TextRenderer, viewportWidth: number, viewportHeight: number, originX: number, originY: number, style: TextStyle, timeSec: number): void;
|
|
1796
|
+
/** Width of the string this handle currently holds, in pixels at a given cell size. */
|
|
1797
|
+
textWidth(text: TextRenderer, cellSize: number): number;
|
|
1798
|
+
disposeText(text: TextRenderer): void;
|
|
1799
|
+
/**
|
|
1800
|
+
* A text label from an SDF font. Opt-in: a consumer that never calls this pays nothing,
|
|
1801
|
+
* which is what keeps `pixelFont.ts`'s no-asset promise true for everybody else.
|
|
1802
|
+
*/
|
|
1803
|
+
createSdfText(): SdfTextRenderer;
|
|
1804
|
+
/**
|
|
1805
|
+
* Lay a string out against a font, and retain the atlas `drawSdfText` will sample.
|
|
1806
|
+
*
|
|
1807
|
+
* The engine fetches nothing: `font` was parsed from a metrics document the caller already
|
|
1808
|
+
* had, and `atlas` is a `SurfaceTexture` the caller already uploaded. A no-op, and no
|
|
1809
|
+
* re-upload, when neither the text nor the style moved since the last call.
|
|
1810
|
+
*/
|
|
1811
|
+
setSdfText(handle: SdfTextRenderer, font: SdfFont, atlas: SurfaceTexture, content: string, style: SdfTextStyle): void;
|
|
1812
|
+
/**
|
|
1813
|
+
* Draw a laid-out SDF string into the scene, at `model`.
|
|
1814
|
+
*
|
|
1815
|
+
* Takes no camera: it draws against the view-projection `bindMeshPass` captured for the
|
|
1816
|
+
* frame, the same one `drawMesh` uses, so a caller positions text with a model matrix
|
|
1817
|
+
* alone — `billboardMatrixY` for a label that faces the camera, a surface's own matrix for
|
|
1818
|
+
* one painted flush against it. A no-op before the first `bindMeshPass` of a session, same
|
|
1819
|
+
* as every other pass that reads `frameViewProj`.
|
|
1820
|
+
*
|
|
1821
|
+
* **Puts `SURFACE_TEXTURE_UNIT` back the way it found it.** The atlas binds through
|
|
1822
|
+
* `SDF_TEXT_TEXTURE_UNIT`, which is that same unit — see the constant's own comment for
|
|
1823
|
+
* why that is safe — so once the glyphs are drawn this rebinds whatever `drawMesh`'s
|
|
1824
|
+
* material state currently says should be there. Without it, a `drawMesh` call *after*
|
|
1825
|
+
* this one would go on sampling unit 15 as its albedo and find the glyph atlas sitting on
|
|
1826
|
+
* it instead of its own texture. Skipped when nothing was actually drawn, since only a
|
|
1827
|
+
* real draw call touches the unit in the first place.
|
|
1828
|
+
*/
|
|
1829
|
+
/**
|
|
1830
|
+
* The output transform a forward pass should apply, and the exposure it should apply it at.
|
|
1831
|
+
*
|
|
1832
|
+
* **Only where that pass is genuinely the last one to touch the frame.** With a scene target
|
|
1833
|
+
* the resolve grades, so a pass that graded as well would be applying a curve to its own
|
|
1834
|
+
* output, and grading early throws away the range a float target exists to keep. With
|
|
1835
|
+
* `screenEffects` off there is no resolve at all and every pass writes straight to the canvas,
|
|
1836
|
+
* so each one has to grade itself or the frame comes out half converted: linear values in an
|
|
1837
|
+
* eight-bit buffer read as display values, which is a saturated, clipped version of the colour
|
|
1838
|
+
* the caller asked for rather than a slightly different one.
|
|
1839
|
+
*
|
|
1840
|
+
* One pair of accessors rather than the expression written out at each site, because that is
|
|
1841
|
+
* how the mesh pass and the particle pass came to disagree in the first place: the mesh pass
|
|
1842
|
+
* gated, and the particle pass uploaded the transform unconditionally to a stage that did not
|
|
1843
|
+
* yet declare it.
|
|
1844
|
+
*/
|
|
1845
|
+
private gradeCode;
|
|
1846
|
+
private gradeExposure;
|
|
1847
|
+
drawSdfText(handle: SdfTextRenderer, model: Float32Array, color: Vec3, opacity: number): void;
|
|
1848
|
+
/** Release an SDF text label. Takes no context; see `disposeText`. */
|
|
1849
|
+
disposeSdfText(handle: SdfTextRenderer): void;
|
|
1850
|
+
createFlock(count: number): FlockRenderer;
|
|
1851
|
+
drawFlock(flock: FlockRenderer, camera: Camera, timeSeconds: number, params: FlockParams, tint: Vec3, windX?: number, windZ?: number): void;
|
|
1852
|
+
/**
|
|
1853
|
+
* Build a scatter batch and fill it. Takes the data rather than handing back
|
|
1854
|
+
* something the caller has to upload, because uploading needs the GL context
|
|
1855
|
+
* and that never leaves this directory (AGENTS.md).
|
|
1856
|
+
*/
|
|
1857
|
+
createScatter(base: MeshData, data: InstanceData): InstancedMesh;
|
|
1858
|
+
/**
|
|
1859
|
+
* Push changed instances to the GPU.
|
|
1860
|
+
*
|
|
1861
|
+
* Only for batches that *move*. Foliage is placed once at generation and never
|
|
1862
|
+
* touched again, so `createScatter` uploads it and `drawScatter` deliberately does
|
|
1863
|
+
* not — a per-frame upload of every blade of grass in the world would be pure waste.
|
|
1864
|
+
*
|
|
1865
|
+
* A live particle system is the other case, and it was silently broken: the drift
|
|
1866
|
+
* plume's grains were drawn from the positions they happened to hold at boot, because
|
|
1867
|
+
* nothing ever re-uploaded them. Only the *count* animated, so a drift threw a
|
|
1868
|
+
* growing pile of stationary grit instead of a spray. Explicit rather than automatic,
|
|
1869
|
+
* so the cost stays visible at the call site that needs it.
|
|
1870
|
+
*/
|
|
1871
|
+
uploadScatter(scatter: InstancedMesh, data: InstanceData): void;
|
|
1872
|
+
/**
|
|
1873
|
+
* Build a particle material: one program, one VAO, one draw call per pool.
|
|
1874
|
+
*
|
|
1875
|
+
* A material rather than a renderer, and the distinction is the reason this
|
|
1876
|
+
* exists at all. Particles used to be drawn through `drawScatter` — the foliage
|
|
1877
|
+
* material — which is opaque, lit like a leaf and has no opacity channel, so
|
|
1878
|
+
* every effect in the game that emitted particles rendered as small solid cubes
|
|
1879
|
+
* that turned black instead of fading. The fix is not a tuning pass on the
|
|
1880
|
+
* emitters; it is a material that knows what a particle is.
|
|
1881
|
+
*/
|
|
1882
|
+
createParticles(capacity: number, options: ParticleBatchOptions): ParticleBatch;
|
|
1883
|
+
/**
|
|
1884
|
+
* Draw a live particle pool.
|
|
1885
|
+
*
|
|
1886
|
+
* Uploads and draws together, unlike the scatter path where the two are
|
|
1887
|
+
* deliberately separate: a particle pool's data changes *every* frame without
|
|
1888
|
+
* exception, so leaving the upload to the caller only creates the opportunity to
|
|
1889
|
+
* forget it — which is exactly what happened to the skate sparks, drawn for a
|
|
1890
|
+
* whole milestone from a buffer nothing had ever written.
|
|
1891
|
+
*/
|
|
1892
|
+
drawParticles(batch: ParticleBatch, data: ParticleInstances, camera: Camera, env: Environment, timeSeconds: number): void;
|
|
1893
|
+
/** Build a batch for electrical arcs. `capacity` is in segments, not arcs. */
|
|
1894
|
+
createBolts(segmentCapacity: number, label?: string): BoltBatch;
|
|
1895
|
+
/**
|
|
1896
|
+
* Draw a pool of arcs.
|
|
1897
|
+
*
|
|
1898
|
+
* `widthM` is the filament's own half-width and `minWidthPerMetre` the floor that
|
|
1899
|
+
* keeps a distant arc above a pixel — under one, a bright thin line does not fade,
|
|
1900
|
+
* it strobes as the rasteriser catches it on some frames and not others.
|
|
1901
|
+
*/
|
|
1902
|
+
drawBolts(batch: BoltBatch, data: BoltSegments, camera: Camera, env: Environment, timeSeconds: number, core: Vec3, edge: Vec3, widthM: number, coreGain: number, minWidthPerMetre?: number): void;
|
|
1903
|
+
/**
|
|
1904
|
+
* A polyline with a real width, for anything that is a stroke rather than a surface.
|
|
1905
|
+
*
|
|
1906
|
+
* Separate from `drawBolts` rather than a flag on it. The two share a vertex expansion
|
|
1907
|
+
* and nothing else: an arc is additive, unlit, unfogged and jittered along its own path,
|
|
1908
|
+
* a line is a flat fogged colour with a clean edge. A `jitter: 0` argument meaning
|
|
1909
|
+
* "actually draw a line" would be a noun lying about what it draws, which `AGENTS.md`
|
|
1910
|
+
* rules out for good reasons.
|
|
1911
|
+
*
|
|
1912
|
+
* There is no portable alternative. WebGL2 clamps `lineWidth` to one pixel on nearly
|
|
1913
|
+
* every driver and WebGPU has no line width at all, so a wide line is a triangle
|
|
1914
|
+
* everywhere or it is nowhere.
|
|
1915
|
+
*
|
|
1916
|
+
* **A handle holds one polyline's geometry for the frame it is drawn in.** Drawing it more
|
|
1917
|
+
* than once in a frame is fine — three widths of one shape is exactly that, and costs nothing
|
|
1918
|
+
* beyond the re-upload — because the data underneath does not change between those calls.
|
|
1919
|
+
* *Changing* what it holds and drawing it more than once in the same frame is not fine: on
|
|
1920
|
+
* WebGPU, `queue.writeBuffer` does not interleave with a pass's already-recorded draw
|
|
1921
|
+
* commands, so every draw against this handle in a frame reads whichever write landed last by
|
|
1922
|
+
* the time the frame submits, not the content that was current when each call was made.
|
|
1923
|
+
* WebGL2 draws immediately on each call and happens to give the answer a caller expects, so
|
|
1924
|
+
* the two backends disagree silently rather than loudly. A caller drawing several polylines
|
|
1925
|
+
* whose geometry differs within one frame needs one handle each, not one handle reused.
|
|
1926
|
+
*/
|
|
1927
|
+
createLines(segmentCapacity: number, label?: string): LineBatch;
|
|
1928
|
+
/**
|
|
1929
|
+
* Draw a polyline.
|
|
1930
|
+
*
|
|
1931
|
+
* `widthM` is the stroke's own half-width and `minWidthPerMetre` the floor that keeps a
|
|
1932
|
+
* distant line above a pixel — under one, a thin line does not fade, it strobes as the
|
|
1933
|
+
* rasteriser catches it on some frames and not others. `softness` widens the antialiased
|
|
1934
|
+
* edge inward, as a fraction of the half-width; zero is a clean edge.
|
|
1935
|
+
*/
|
|
1936
|
+
drawLines(lines: LineBatch, data: LineSegments, model: ReadonlyMat4, camera: Camera, env: Environment, color: Vec3, widthM: number, opacity: number, softness?: number, minWidthPerMetre?: number, additive?: boolean): void;
|
|
1937
|
+
/**
|
|
1938
|
+
* Draw a scatter batch. Wind arrives as a value rather than being sampled
|
|
1939
|
+
* here: every system in the world must answer to the same gust, and a
|
|
1940
|
+
* renderer that sampled its own would be a second wind by definition.
|
|
1941
|
+
*
|
|
1942
|
+
* **It binds no point lights, and that is a rule about what may be instanced rather than a
|
|
1943
|
+
* performance note.** A field of grass shaded by ten lamps is a cost nobody wanted, so this
|
|
1944
|
+
* material answers to the sun and the ambient alone. The consequence is invisible from the
|
|
1945
|
+
* call site and a consumer has to know it: anything a lamp in the scene is meant to reach has
|
|
1946
|
+
* to be merged into a mesh instead. Choosing wrongly is not slow, it is unlit. Reported from
|
|
1947
|
+
* outside, where it had become a rule written on a consumer's own wrapper.
|
|
1948
|
+
*/
|
|
1949
|
+
drawScatter(scatter: InstancedMesh, data: InstanceData, camera: Camera, env: Environment, windX: number, windZ: number, windGust: number, timeSeconds: number,
|
|
1950
|
+
/**
|
|
1951
|
+
* Recent presses, from a `TrampleField`, or null for foliage nothing walks
|
|
1952
|
+
* through. Four floats per slot: world position and how pressed.
|
|
1953
|
+
*/
|
|
1954
|
+
trample?: Float32Array | null): void;
|
|
1955
|
+
/**
|
|
1956
|
+
* Upload the instance transform's inputs — the shared half of the scatter material.
|
|
1957
|
+
*
|
|
1958
|
+
* One function for both scatter programs, so the picture and its shadow are bent
|
|
1959
|
+
* and pressed by identical numbers. Splitting these into two call sites is how the
|
|
1960
|
+
* two poses drift apart by a tuning constant nobody remembers changing twice.
|
|
1961
|
+
*/
|
|
1962
|
+
private bindScatterDeform;
|
|
1963
|
+
/** Refilled per call rather than allocated; see `createScatterDeform`. */
|
|
1964
|
+
private readonly deform;
|
|
1965
|
+
/**
|
|
1966
|
+
* The frame's wind, as the mesh and depth programs read it.
|
|
1967
|
+
*
|
|
1968
|
+
* **A second target from `deform` above, and one conversion between them.** The scatter batch
|
|
1969
|
+
* takes its wind as arguments to its own draw call, which predates this, so the two cannot share
|
|
1970
|
+
* a target without changing that signature. What they do share is `resolveScatterDeform`, which
|
|
1971
|
+
* is where every number that matters is decided — a normalised direction, metres of bend from a
|
|
1972
|
+
* speed, a gust amplitude and a scaled clock. `scatterDeform.ts` says why that has to be one
|
|
1973
|
+
* function: a copy of any of those conversions drifts, and a field of grass then leans
|
|
1974
|
+
* differently from the canopy above it.
|
|
1975
|
+
*
|
|
1976
|
+
* **What this does not defend against** is a consumer calling `setWind` with one gust and
|
|
1977
|
+
* `drawScatter` with another. That is the same hazard `drawFlock` already carries, and the
|
|
1978
|
+
* answer is the one AGENTS.md gives: sample the field once a frame and pass it down.
|
|
1979
|
+
*/
|
|
1980
|
+
private readonly frameWind;
|
|
1981
|
+
/**
|
|
1982
|
+
* The five wind uniforms, uploaded through one function wherever they are read.
|
|
1983
|
+
*
|
|
1984
|
+
* Separate from the trample array beside it because the mesh programs want the wind and have no
|
|
1985
|
+
* concept of something having walked through them.
|
|
1986
|
+
*/
|
|
1987
|
+
private uploadWind;
|
|
1988
|
+
/**
|
|
1989
|
+
* The frame's wind, sampled once by the caller and handed down.
|
|
1990
|
+
*
|
|
1991
|
+
* Every mesh carrying a per-vertex channel bends to this, in the colour pass and in the depth
|
|
1992
|
+
* pass, so a canopy and its own shadow move together. A caller that never calls it gets a still
|
|
1993
|
+
* world, which is exactly what every scene drew before the channel existed.
|
|
1994
|
+
*/
|
|
1995
|
+
setWind(windX: number, windZ: number, windGust: number, timeSeconds: number): void;
|
|
1996
|
+
/** The point-shadow set, chosen by the pool and bound below. Refilled, never allocated. */
|
|
1997
|
+
private readonly resolvedPointShadows;
|
|
1998
|
+
/**
|
|
1999
|
+
* Put the resolved point-shadow set on the GL program: one array texture and five arrays.
|
|
2000
|
+
*
|
|
2001
|
+
* **It was twelve cubemaps on twelve units.** They are one `TEXTURE_2D_ARRAY` now, a layer per
|
|
2002
|
+
* light, which is where eleven of WebGL2's sixteen guaranteed units came back from.
|
|
2003
|
+
*
|
|
2004
|
+
* **The thin half of a split, and the arrays are why it is worth splitting.** Everything
|
|
2005
|
+
* about *which* map a light samples and how present it is comes out of the pool, which both
|
|
2006
|
+
* backends share; this turns that into a texture unit and `uniform*v` calls, which is the only
|
|
2007
|
+
* part WebGL2 does differently.
|
|
2008
|
+
*
|
|
2009
|
+
* The array uniforms are addressed as `name[0]`, and that is not decoration. GLSL ES exposes
|
|
2010
|
+
* an array uniform under the name of its first element; `name[1]` returns nothing from the
|
|
2011
|
+
* cached table, so uploading element by element left slots 1–7 at zero and made their shadows
|
|
2012
|
+
* vanish.
|
|
2013
|
+
*/
|
|
2014
|
+
private bindPointShadows;
|
|
2015
|
+
/** The same, for a light volume's local-space camera; see `createResolvedLightVolume`. */
|
|
2016
|
+
private readonly volume;
|
|
2017
|
+
createWater(resolution?: number, nearExtent?: number, farHalfExtent?: number): WaterRenderer;
|
|
2018
|
+
/**
|
|
2019
|
+
* Surfaces lit from below by the water under them.
|
|
2020
|
+
*
|
|
2021
|
+
* Null when the profile has water off or there is nothing to light, so a world
|
|
2022
|
+
* with no covered water pays no program compile, no buffer and no draw. The
|
|
2023
|
+
* matching draw accepts null for the same reason: a caller should not need a
|
|
2024
|
+
* branch to express "this world has none".
|
|
2025
|
+
*/
|
|
2026
|
+
createCaustics(sheets: readonly CausticSheet[]): CausticsRenderer | null;
|
|
2027
|
+
drawPlumes(plumes: PlumeRenderer, camera: Camera, timeSeconds: number, env: Environment, windX?: number, windZ?: number, originX?: number, originY?: number, originZ?: number): void;
|
|
2028
|
+
drawWater(water: WaterRenderer, camera: Camera, timeSeconds: number, settings: WaterBody, env: Environment, windX?: number, windZ?: number): void;
|
|
2029
|
+
/**
|
|
2030
|
+
* Draw after the opaque scene: this is light added to surfaces already shaded,
|
|
2031
|
+
* from the water under them.
|
|
2032
|
+
*/
|
|
2033
|
+
drawCaustics(caustics: CausticsRenderer | null, camera: Camera, timeSeconds: number, env: Environment, windX?: number, windZ?: number, strength?: number): void;
|
|
2034
|
+
/**
|
|
2035
|
+
* Begin a scene render mirrored across a horizontal plane. Draw ordinary
|
|
2036
|
+
* mesh, sky and plume passes with the returned camera, then call
|
|
2037
|
+
* `endPlanarReflection`; the next water draw samples the completed target.
|
|
2038
|
+
*/
|
|
2039
|
+
/**
|
|
2040
|
+
* **This is a water feature, not a renderer one, and the name hides that.**
|
|
2041
|
+
*
|
|
2042
|
+
* The target is allocated only when `water` *and* `waterReflections` are both on, and the
|
|
2043
|
+
* only thing that samples it is `WaterRenderer`. So a scenario whose ground is a mesh slab
|
|
2044
|
+
* with `drawFilm` patches over it, which is what a wet street is, cannot mirror the scene
|
|
2045
|
+
* into it however it calls this. Reported from outside after being attempted, which is the
|
|
2046
|
+
* cost of a name that promises more than the wiring does.
|
|
2047
|
+
*
|
|
2048
|
+
* What is available to a surface that is not water: `bakeReflectionProbe`, which is a room
|
|
2049
|
+
* rather than a mirror, and `drawFilm`'s sheen, which is iridescence rather than reflection.
|
|
2050
|
+
* Neither is a substitute for a mirrored pass and this comment is not pretending otherwise.
|
|
2051
|
+
*/
|
|
2052
|
+
beginPlanarReflection(source: Camera, planeY: number, clearColor: Vec3): Camera | null;
|
|
2053
|
+
endPlanarReflection(): void;
|
|
2054
|
+
get aspect(): number;
|
|
2055
|
+
/**
|
|
2056
|
+
* Point rendering back at the frame's own target, whole.
|
|
2057
|
+
*
|
|
2058
|
+
* Every pass that borrows the viewport — the shadow maps, the reflection, the export
|
|
2059
|
+
* target, an inset — has to hand it back, and each of them was spelling the same three
|
|
2060
|
+
* arguments out. Five copies of "what does the full frame mean" is five chances to drift
|
|
2061
|
+
* from the drawing buffer's real size after a resize.
|
|
2062
|
+
*
|
|
2063
|
+
* It restores the **framebuffer** as well, and that is not tidiness. A borrowing pass
|
|
2064
|
+
* releases by binding `null`, which is the canvas — correct when the canvas was where the
|
|
2065
|
+
* frame was going, and wrong the moment a scene target exists: everything drawn after a
|
|
2066
|
+
* reflection would land on the canvas and then be overwritten by the resolve of a target
|
|
2067
|
+
* missing half its content. This is the one place that knows where a frame is going, so
|
|
2068
|
+
* it is the one place that can say so.
|
|
2069
|
+
*/
|
|
2070
|
+
private restoreViewport;
|
|
2071
|
+
/**
|
|
2072
|
+
* Match the drawing buffer to CSS size × clamped DPR, under the pixel budget.
|
|
2073
|
+
* Cheap when unchanged.
|
|
2074
|
+
*
|
|
2075
|
+
* The budget is deliberately not applied to a locked buffer: a lock is a caller naming
|
|
2076
|
+
* an exact size for a reason no quality setting knows about — a clip is 1080x1920
|
|
2077
|
+
* whatever anybody's frame rate is — and silently handing back a different one would
|
|
2078
|
+
* write a file that is not the size it claims.
|
|
2079
|
+
*/
|
|
2080
|
+
resize(): void;
|
|
2081
|
+
/**
|
|
2082
|
+
* Move the drawing-buffer area cap, in pixels. Zero is uncapped.
|
|
2083
|
+
*
|
|
2084
|
+
* Live, unlike every other quality lever, and that is the feature rather than an
|
|
2085
|
+
* inconsistency. The rest size an allocation at construction — shadow maps, reflection
|
|
2086
|
+
* targets, the wave surface — so moving one means rebuilding every GPU resource the
|
|
2087
|
+
* world hangs off. This one only changes how many pixels the next frame covers, and
|
|
2088
|
+
* everything sized from the drawing buffer already re-fits itself when it changes. A
|
|
2089
|
+
* player hunting their own frame rate can therefore move this and see the answer, which
|
|
2090
|
+
* is the only way anybody finds out that pixels were what it cost them.
|
|
2091
|
+
*/
|
|
2092
|
+
setMaxDrawingBufferPixels(pixels: number): void;
|
|
2093
|
+
/**
|
|
2094
|
+
* Move the drawing-buffer density cap at runtime, without rebuilding anything.
|
|
2095
|
+
*
|
|
2096
|
+
* Live for the same reason `setMaxDrawingBufferPixels` is, and it is the other half of
|
|
2097
|
+
* the same idea: `resize()` re-reads this cap, and the scene target and the planar
|
|
2098
|
+
* reflection are both sized *from* the drawing buffer, so nothing here owns an
|
|
2099
|
+
* allocation that a new density invalidates.
|
|
2100
|
+
*
|
|
2101
|
+
* This is the one quality lever a governor can drive, which is exactly why the governor
|
|
2102
|
+
* drives this and not the preset. A preset means shadow maps and wave surfaces sized at
|
|
2103
|
+
* construction; changing one of those mid-session means rebuilding every GPU resource the
|
|
2104
|
+
* world hangs off, and that is what a reload is for.
|
|
2105
|
+
*
|
|
2106
|
+
* Cheap to call every few hundred frames and pointless to call every frame: it forces a
|
|
2107
|
+
* `resize()`, which reallocates the scene target when the size actually changes.
|
|
2108
|
+
*/
|
|
2109
|
+
applyResolutionScale(scale: number): void;
|
|
2110
|
+
/**
|
|
2111
|
+
* The density actually in force, which is where a governor has to start from.
|
|
2112
|
+
*
|
|
2113
|
+
* `applyResolutionScale` could always be *written* and never read, and that was enough for
|
|
2114
|
+
* the consumer that drives it from a stored preset. It is not enough for one that drives it
|
|
2115
|
+
* from measurement: `ResolutionGovernor` is constructed around a starting scale and moves
|
|
2116
|
+
* relative to it, so a caller with no way to ask has to guess.
|
|
2117
|
+
*
|
|
2118
|
+
* **The effective density and not `maxDpr`, and the difference is not academic.** `maxDpr` is
|
|
2119
|
+
* a *ceiling*: `resize` takes `min(devicePixelRatio, maxDpr)`, so on an ordinary 1x display a
|
|
2120
|
+
* ceiling of 2 is already doing nothing. A governor seeded from the ceiling there spends its
|
|
2121
|
+
* first four decisions walking 2 down to 1 while the drawing buffer does not change by a
|
|
2122
|
+
* single pixel, which is a machine stuttering for several seconds through a fix that is
|
|
2123
|
+
* being applied to a number nobody is reading. Found exactly that way, at 20x throttle: three
|
|
2124
|
+
* decisions, no change in buffer size.
|
|
2125
|
+
*
|
|
2126
|
+
* `capabilityClamp` is the other half of the same point, and it argues the same direction:
|
|
2127
|
+
* `maxDevicePixelRatio` is a request the clamp is entitled to have already lowered.
|
|
2128
|
+
*/
|
|
2129
|
+
get resolutionScale(): number;
|
|
2130
|
+
/**
|
|
2131
|
+
* Pin the drawing buffer to an exact pixel size, ignoring CSS and DPR.
|
|
2132
|
+
*
|
|
2133
|
+
* For recording a frame at a size that has nothing to do with the window: a
|
|
2134
|
+
* clip is 1080x1920 whatever shape the browser is, and `aspect` follows the
|
|
2135
|
+
* lock, so a camera given `renderer.aspect` composes for the *export* rather
|
|
2136
|
+
* than for the screen. That is the difference between rendering at an aspect
|
|
2137
|
+
* and cropping to one, and cropping throws away the half of the frame the shot
|
|
2138
|
+
* was built around.
|
|
2139
|
+
*
|
|
2140
|
+
* Not a quality option, deliberately. `RenderQualityOptions` describes how good
|
|
2141
|
+
* the picture is for the whole session and is resolved before any GPU resource
|
|
2142
|
+
* exists; this is a temporary, caller-driven override that must be released.
|
|
2143
|
+
* The CSS box is untouched — a page that wants the preview letterboxed is
|
|
2144
|
+
* styling, not rendering.
|
|
2145
|
+
*/
|
|
2146
|
+
lockDrawingBuffer(width: number, height: number): void;
|
|
2147
|
+
/** Back to following the CSS box. The next `resize` restores it. */
|
|
2148
|
+
unlockDrawingBuffer(): void;
|
|
2149
|
+
/**
|
|
2150
|
+
* Render one directional shadow layer. Bracket `drawShadowCasters` calls
|
|
2151
|
+
* between this and `endShadowPass`. Static and dynamic layers are sampled
|
|
2152
|
+
* together; pass `shadowStrength: 0` whenever the dominant source is not
|
|
2153
|
+
* emitting, because a stale map must never be sampled.
|
|
2154
|
+
*/
|
|
2155
|
+
beginShadowPass(lightViewProj: ReadonlyMat4, layer?: 'static' | 'static-peel' | 'dynamic'): void;
|
|
2156
|
+
/**
|
|
2157
|
+
* Submit everything that casts into the layer `beginShadowPass` opened.
|
|
2158
|
+
*
|
|
2159
|
+
* The same `ShadowCasters` closure the point-light bakes are given, so a world has
|
|
2160
|
+
* exactly one answer to "what occludes light" and the sun and the lamps cannot
|
|
2161
|
+
* disagree about it. A no-op when the profile has directional shadows off.
|
|
2162
|
+
*/
|
|
2163
|
+
/**
|
|
2164
|
+
* The same enumeration, replayed into the colour pass that is open.
|
|
2165
|
+
*
|
|
2166
|
+
* See `SceneCasters` in `shadowCasters.ts` for why this exists and why scatter is declined. The
|
|
2167
|
+
* sink is built once and held, because a caster enumeration runs per pass and a sink allocated
|
|
2168
|
+
* per call would allocate per pass.
|
|
2169
|
+
*/
|
|
2170
|
+
private readonly sceneCasterSink;
|
|
2171
|
+
/**
|
|
2172
|
+
* The material the open replay has bound, or `undefined` before its first entry.
|
|
2173
|
+
*
|
|
2174
|
+
* The twin of the WebGPU field, which carries the whole argument: a sink's material is optional
|
|
2175
|
+
* and omitting it means *no material* rather than *unchanged*, so a run of entries sharing one
|
|
2176
|
+
* material had no shorter spelling and bound once per entry. What it costs here is not a ring
|
|
2177
|
+
* that runs out but `setMaterial`'s own body — a `useProgram` and a full `writeMaterialState`
|
|
2178
|
+
* for **every** flat program, texture binds included, per entry rather than per surface.
|
|
2179
|
+
*
|
|
2180
|
+
* Reference identity, and `undefined` rather than `null` for "nothing bound yet", both for the
|
|
2181
|
+
* reasons given there.
|
|
2182
|
+
*/
|
|
2183
|
+
private sceneCasterMaterial;
|
|
2184
|
+
/** Bind one replay entry's material, unless it is the one already standing. */
|
|
2185
|
+
private bindSceneCasterMaterial;
|
|
2186
|
+
/**
|
|
2187
|
+
* Draw everything a caster enumeration contains into the open colour pass, with its materials.
|
|
2188
|
+
*
|
|
2189
|
+
* The colour counterpart of `drawShadowCasters`, so one closure answers "what is in this frame"
|
|
2190
|
+
* for the shadow cascade, every cubemap face, and a second viewpoint such as a mirror or a probe
|
|
2191
|
+
* face. What it replaces is a consumer re-entering its whole draw path with a second camera.
|
|
2192
|
+
*/
|
|
2193
|
+
drawSceneCasters(casters: ShadowCasters): void;
|
|
2194
|
+
drawShadowCasters(casters: ShadowCasters): void;
|
|
2195
|
+
endShadowPass(): void;
|
|
2196
|
+
/**
|
|
2197
|
+
* The lit program, built at the largest light budget this device will actually link.
|
|
2198
|
+
*
|
|
2199
|
+
* **Why the engine fits the shader to the part rather than leaving it to a consumer.** `flat`
|
|
2200
|
+
* at the full budget declares **440** rows of the fragment uniform grid — twenty arrays sized by
|
|
2201
|
+
* `MAX_LIGHTS` and fourteen by `MAX_AREA_LIGHTS`, and GLSL ES gives an array a whole row per
|
|
2202
|
+
* element whatever its base type. An Adreno 740 offers **256**. So the program does not link,
|
|
2203
|
+
* `compileProgram` throws out of this constructor, and a game that awaited `createRenderer` gets
|
|
2204
|
+
* no renderer, no reason and no frame — the page's background colour for as long as the player
|
|
2205
|
+
* is willing to look at it, with the splash covering the first second of it. Reported from
|
|
2206
|
+
* outside exactly that way.
|
|
2207
|
+
*
|
|
2208
|
+
* The only lever a consumer had was `pointShadows: false`, and it is not enough to be a fix: it
|
|
2209
|
+
* pays a whole feature on every part under 440, and the lit path *without* point shadows is
|
|
2210
|
+
* still 248, so it cannot reach a conforming device at all — WebGL2 guarantees 224. Sizing the
|
|
2211
|
+
* arrays reaches it with everything compiled in: 8 lights and 2 rectangles is 252 rows **with**
|
|
2212
|
+
* point shadows, and 4 and 1 is 158.
|
|
2213
|
+
*
|
|
2214
|
+
* **Counted first, then linked, and both are load-bearing.** The count is an upper bound over
|
|
2215
|
+
* the source (see `countUniformVectors`), which is what lets the first attempt be the right one
|
|
2216
|
+
* on a part that is short; the link is the driver's own answer, and a refusal steps down a rung
|
|
2217
|
+
* rather than propagating. Only a shader that will not link at *any* budget throws, and it
|
|
2218
|
+
* throws a sentence with the device's numbers in it rather than a bare driver string.
|
|
2219
|
+
*/
|
|
2220
|
+
private fitFlatProgram;
|
|
2221
|
+
/**
|
|
2222
|
+
* How many point lights this renderer's shader shades at once.
|
|
2223
|
+
*
|
|
2224
|
+
* **Read it when sizing a `PointLightBuffer`.** `selectPointLights` fills a buffer to the
|
|
2225
|
+
* buffer's own capacity and evicts the weakest when it is full, so the winners are the nearest
|
|
2226
|
+
* ones but they are not in any order — which means a buffer wider than this leaves the shader
|
|
2227
|
+
* shading an arbitrary subset rather than the nearest N. Sizing the buffer from this number is
|
|
2228
|
+
* what makes a clamped budget a smaller scene instead of a flickering one. A wider upload is
|
|
2229
|
+
* otherwise harmless: GL ignores array elements past the end of a uniform, measured on ANGLE.
|
|
2230
|
+
*/
|
|
2231
|
+
get shadedLights(): number;
|
|
2232
|
+
/** How many rectangular emitters this renderer's shader shades at once. */
|
|
2233
|
+
get shadedAreaLights(): number;
|
|
2234
|
+
/** How many point-light shadows the shader samples at once. */
|
|
2235
|
+
get sampledShadowLights(): number;
|
|
2236
|
+
/**
|
|
2237
|
+
* Size the point-shadow pool for a world's lights. Call at load.
|
|
2238
|
+
*
|
|
2239
|
+
* It used to bake a map for every light here and keep them all forever, which is
|
|
2240
|
+
* why it was called `bakeStaticPointShadows`. That allocated one cubemap per
|
|
2241
|
+
* lamp — about 327 MB for 50 of them — so that the eight the shader can bind
|
|
2242
|
+
* could be read. See `POINT_SHADOW_POOL`.
|
|
2243
|
+
*
|
|
2244
|
+
* Nothing is baked now. Maps are borrowed from the pool and baked by
|
|
2245
|
+
* `updatePointShadows` the first time a light is actually sampled, which is the
|
|
2246
|
+
* only moment the result can be seen. Load is correspondingly faster: it no
|
|
2247
|
+
* longer renders six passes over the static world for every lamp in the day,
|
|
2248
|
+
* most of which were never looked at.
|
|
2249
|
+
*/
|
|
2250
|
+
prepareStaticPointShadows(lights: readonly ShadowLight[],
|
|
2251
|
+
/**
|
|
2252
|
+
* The rectangles this world will shade, so the ones that cast get layers of the same array.
|
|
2253
|
+
*
|
|
2254
|
+
* **A second parameter on this call rather than a call of its own, because there is one array
|
|
2255
|
+
* and `texStorage3D` sizes it once.** A separate `prepareAreaShadows` would either rebuild the
|
|
2256
|
+
* array — throwing away every baked lamp — or depend on being called first, and a consumer
|
|
2257
|
+
* getting that order wrong would see the rectangles cast and the lamps stop. Optional, so
|
|
2258
|
+
* every existing caller is unchanged and allocates exactly what it did before.
|
|
2259
|
+
*
|
|
2260
|
+
* The name says point and this takes area lights too. It stays, because what it prepares is
|
|
2261
|
+
* *the* shadow array and both kinds of light are layers in it — the alternative was a second
|
|
2262
|
+
* name for one thing.
|
|
2263
|
+
*/
|
|
2264
|
+
areaLights?: readonly AreaLightSource[]): void;
|
|
2265
|
+
/**
|
|
2266
|
+
* Say once that a rectangle asked to cast and named no range, naming the field.
|
|
2267
|
+
*
|
|
2268
|
+
* **A warning rather than a throw, because this is reachable from a frame.** `AGENTS.md` says
|
|
2269
|
+
* fail fast at init and never throw in the frame loop, and a consumer may re-prepare when its
|
|
2270
|
+
* world changes. What it must not be is silent: the picture of a rectangle that declined to cast
|
|
2271
|
+
* and one whose range was forgotten is the same picture, and the second is a typo.
|
|
2272
|
+
*/
|
|
2273
|
+
private warnAreaShadowRange;
|
|
2274
|
+
/**
|
|
2275
|
+
* Say once that a rectangle wants layers the array was not sized for.
|
|
2276
|
+
*
|
|
2277
|
+
* **This is the area-light shape of the failure `updatePointShadows` warns about above**, and it
|
|
2278
|
+
* is just as quiet: `prepareStaticPointShadows` sizes the array — `texStorage3D` and
|
|
2279
|
+
* `createTexture` are both immutable — so a consumer that passes its rectangles here and not
|
|
2280
|
+
* there gets an array with no room above the pool. Every declaration is then honoured except the
|
|
2281
|
+
* storage, the set holds no maps, every layer publishes -1, and the frame is indistinguishable
|
|
2282
|
+
* from one whose rectangles declined to cast.
|
|
2283
|
+
*/
|
|
2284
|
+
private warnAreaShadowLayers;
|
|
2285
|
+
/** Say once that a rectangle asked to cast while the array it would read is switched off. */
|
|
2286
|
+
private warnAreaShadowsOff;
|
|
2287
|
+
/**
|
|
2288
|
+
* Per frame: bind the shading pass's exact light set, refresh any sampled map
|
|
2289
|
+
* whose source moved, and rebuild the live-caster map nearest the supplied
|
|
2290
|
+
* focus. A second live map exists only during its ownership crossfade.
|
|
2291
|
+
* Fixed-light world maps remain untouched.
|
|
2292
|
+
*/
|
|
2293
|
+
updatePointShadows(lights: readonly ShadowLight[], activeWorldIndices: Int32Array, activeCount: number, liveX: number, liveY: number, liveZ: number, frameDt: number, staticCasters: ShadowCasters, dynamicCasters: ShadowCasters, warmWorldIndices?: Int32Array, warmCount?: number,
|
|
2294
|
+
/**
|
|
2295
|
+
* The rectangles this frame shades, in the order `selectAreaLights` was given them.
|
|
2296
|
+
*
|
|
2297
|
+
* **The same list and the same order, which is what makes a slot mean one thing.** The lit
|
|
2298
|
+
* pass reads `uAreaShadow*[a]` at the slot it is shading, and those arrays are filled from this
|
|
2299
|
+
* list's position — so handing a different order here than to `selectAreaLights` puts one
|
|
2300
|
+
* fixture's shadow under another. Optional, so an existing caller is unchanged.
|
|
2301
|
+
*
|
|
2302
|
+
* Their bakes ride this call rather than one of their own because the budget, the casters and,
|
|
2303
|
+
* on the other backend, the encoder are all already here. See `AreaShadowSet.update`.
|
|
2304
|
+
*/
|
|
2305
|
+
areaLights?: readonly AreaLightSource[]): void;
|
|
2306
|
+
/**
|
|
2307
|
+
* The rectangles' own round: their static layers under the same face budget, then their movers.
|
|
2308
|
+
*
|
|
2309
|
+
* **A separate allocation of the same two numbers rather than what the pool left over**, and the
|
|
2310
|
+
* reason is which failure each choice produces. Sharing one budget means a courtyard full of lamps
|
|
2311
|
+
* can starve a rectangle indefinitely, and a shadow that never arrives is worse than a shadow
|
|
2312
|
+
* that arrives late — the pool's own ordering rule says so. Allocating separately doubles the
|
|
2313
|
+
* worst case, and that worst case is bounded: `MAX_AREA_LIGHTS` is four against a pool of twenty,
|
|
2314
|
+
* and a fixture's static layer is baked once because a fixture does not move.
|
|
2315
|
+
*
|
|
2316
|
+
* Which leaves the recurring cost as the live layers alone: `liveShadowFacesPerFrame` shared
|
|
2317
|
+
* round-robin across the casting rectangles, of a handful of moving meshes each.
|
|
2318
|
+
*/
|
|
2319
|
+
private updateAreaShadows;
|
|
2320
|
+
/**
|
|
2321
|
+
* Every point-shadow bake, under the one set of conventions all of them need.
|
|
2322
|
+
*
|
|
2323
|
+
* **It exists because the bracket was written on one of the two bake paths and not the other.**
|
|
2324
|
+
* A point-shadow face is rendered from a light matrix `depthConvention.ts` deliberately leaves
|
|
2325
|
+
* conventional, so the pass has to compare and clear conventionally too. `bakeAreaOne` did that;
|
|
2326
|
+
* `bakeOne` — the ordinary lamp, which is nearly every point shadow a scene has — ran under the
|
|
2327
|
+
* frame's own `GEQUAL` against a scratch cleared to 0, so every fragment won or lost the wrong
|
|
2328
|
+
* comparison. That does not look broken. It looks *shaded*, which is why it outlived nine
|
|
2329
|
+
* attempts at reversed depth and was only named by neutralising the term and watching the
|
|
2330
|
+
* backends agree.
|
|
2331
|
+
*
|
|
2332
|
+
* Both callers go through here now, and the state is set unconditionally: conventional depth is
|
|
2333
|
+
* already in this configuration, so there is no branch to forget.
|
|
2334
|
+
*/
|
|
2335
|
+
private bakePointShadow;
|
|
2336
|
+
/** Spend up to `maxFaces` on one rectangle's layer, from its centre. */
|
|
2337
|
+
private bakeAreaOne;
|
|
2338
|
+
/** Spend up to `maxFaces` on this map, returning how many it used. */
|
|
2339
|
+
private bakeOne;
|
|
2340
|
+
/**
|
|
2341
|
+
* Draw into a rectangle of the canvas, on its own terms.
|
|
2342
|
+
*
|
|
2343
|
+
* For a *portrait*: a character preview standing in a box on a menu, lit and framed by
|
|
2344
|
+
* whatever the caller wants, with nothing of the world in it. The scissor is what makes
|
|
2345
|
+
* that true — the clear only touches the box, so the frame already drawn survives around
|
|
2346
|
+
* it, and only the meshes the caller issues appear inside.
|
|
2347
|
+
*
|
|
2348
|
+
* Kept as a pair of calls rather than a callback so the caller's draw sequence reads the
|
|
2349
|
+
* same inside a box as outside one: `beginInset`, bind a pass, draw meshes, `endInset`.
|
|
2350
|
+
* It returns the **aspect** of the rectangle it set up, for the camera that will fill it.
|
|
2351
|
+
*
|
|
2352
|
+
* The rectangle is in **CSS pixels with the DOM's top-left origin** — a `DOMRect`, in
|
|
2353
|
+
* other words, which is what a caller actually has. The first version took GL's own
|
|
2354
|
+
* convention and left the caller to scale by the device pixel ratio and flip the y axis;
|
|
2355
|
+
* that is arithmetic about *this* object's canvas, so it belongs here. A caller doing it
|
|
2356
|
+
* has two chances to be wrong about somebody else's state, and on a high-DPI screen the
|
|
2357
|
+
* mistake is a box in the wrong place at the wrong size.
|
|
2358
|
+
*
|
|
2359
|
+
* **`clearColor: null` keeps the picture and clears only depth**, which is the
|
|
2360
|
+
* difference between an object *in* the frame and an object in a box cut out of it.
|
|
2361
|
+
* A backdrop is right for a preview inside a panel, where the box is furniture and a
|
|
2362
|
+
* dark case is what makes a small object read. It is wrong the moment the same inset
|
|
2363
|
+
* is used over a live frame: the caller gets a filled rectangle pasted onto the
|
|
2364
|
+
* picture, whatever colour it picks, because any colour that is not the scene is a
|
|
2365
|
+
* rectangle. Depth alone still puts the meshes in front of everything already drawn
|
|
2366
|
+
* inside the box, which is what a caller compositing over its own frame wants.
|
|
2367
|
+
*/
|
|
2368
|
+
beginInset(rect: InsetRect, clearColor: Vec3 | null): number;
|
|
2369
|
+
/**
|
|
2370
|
+
* Hand a rectangle of the frame to a 2D canvas, at its own pixel size.
|
|
2371
|
+
*
|
|
2372
|
+
* For content that has to appear *in front of* the page rather than behind it. The
|
|
2373
|
+
* menu's panels carry `backdrop-filter: blur(6px)`, so anything drawn into the canvas
|
|
2374
|
+
* underneath a panel — including an inset in a hole cut through one — is shown blurred,
|
|
2375
|
+
* by design: the world behind a menu is meant to be out of focus. A character preview
|
|
2376
|
+
* shown through that hole inherits the blur along with everything else.
|
|
2377
|
+
*
|
|
2378
|
+
* A copy is what escapes it. The pixels become an ordinary image inside a DOM element,
|
|
2379
|
+
* so the blur has nothing to do with them. It is one `drawImage` of a few tens of
|
|
2380
|
+
* thousands of pixels, GPU-side, which is cheaper than any of the alternatives — a second
|
|
2381
|
+
* WebGL context, or a mask cut out of the blurring layer and kept in step with the
|
|
2382
|
+
* element it is hiding.
|
|
2383
|
+
*
|
|
2384
|
+
* **Must be called in the same task as the draw.** A WebGL drawing buffer is only
|
|
2385
|
+
* guaranteed readable until the page composites; afterwards this copies black. That is
|
|
2386
|
+
* the same rule the still export learned the hard way.
|
|
2387
|
+
*/
|
|
2388
|
+
copyRegionTo(rect: InsetRect, target: HTMLCanvasElement): void;
|
|
2389
|
+
/**
|
|
2390
|
+
* Fill a rectangle of the frame with one flat colour, blended.
|
|
2391
|
+
*
|
|
2392
|
+
* The plain surface an overlay is read against. Neither of the two things that
|
|
2393
|
+
* could nearly do this actually can: `beginInset` *clears*, so it is opaque by
|
|
2394
|
+
* definition, and `TextRenderer.setPlate` builds its rectangle from one lit cube
|
|
2395
|
+
* per cell — correct for a keycap and a grid of seams at panel size.
|
|
2396
|
+
*
|
|
2397
|
+
* `rect` is in CSS pixels from the top-left of the **canvas** — `cssWidth` and
|
|
2398
|
+
* `cssHeight`'s space, which is what an overlay laying itself out already has.
|
|
2399
|
+
* Deliberately *not* `beginInset`'s viewport-relative `DOMRect`: that one takes
|
|
2400
|
+
* what its callers hold, which is a measured DOM element, and this one takes what
|
|
2401
|
+
* its callers hold, which is a rectangle they computed against the frame. The two
|
|
2402
|
+
* spaces differ exactly when the canvas does not fill the window — a letterboxed
|
|
2403
|
+
* export — so a single convention would be silently wrong on one side or the
|
|
2404
|
+
* other in the case that matters most.
|
|
2405
|
+
*
|
|
2406
|
+
* Depth-test off and depth writes off: this is furniture drawn over a finished
|
|
2407
|
+
* frame, and it must neither be occluded by the scene nor occlude what is drawn
|
|
2408
|
+
* after it.
|
|
2409
|
+
*/
|
|
2410
|
+
fillPanel(rect: InsetRect, color: Vec3, alpha: number): void;
|
|
2411
|
+
/** Give the whole canvas back. Safe to call without a matching `beginInset`. */
|
|
2412
|
+
endInset(): void;
|
|
2413
|
+
/**
|
|
2414
|
+
* How much speed blur the frame about to be drawn should resolve with, 0 to 1.
|
|
2415
|
+
*
|
|
2416
|
+
* Set before `beginFrame`, held until changed, and ignored entirely when screen effects
|
|
2417
|
+
* are off. A *renderer* parameter rather than a scene one: it describes how the finished
|
|
2418
|
+
* image is presented, not anything in the world, which is why nothing about the world has
|
|
2419
|
+
* to know it exists.
|
|
2420
|
+
*/
|
|
2421
|
+
setSpeedRush(strength: number): void;
|
|
2422
|
+
/**
|
|
2423
|
+
* How much of the frame's camera motion blur to apply, 0 to 1. Scales `cameraMotionBlur`.
|
|
2424
|
+
*
|
|
2425
|
+
* **A ceiling and a dial, rather than one number.** `cameraMotionBlur` is
|
|
2426
|
+
* construction-time because it decides whether the pass exists at all; this decides how
|
|
2427
|
+
* much of it a given frame wants, and a frame is exactly the granularity that matters.
|
|
2428
|
+
* Blur that is always on is not a speed cue, it is a filter: a viewer stops reading it
|
|
2429
|
+
* within seconds, and it costs eight taps on every moving pixel while doing so. Reported
|
|
2430
|
+
* from play, twice, by two different people, one of whom turned the setting off inside a
|
|
2431
|
+
* minute.
|
|
2432
|
+
*
|
|
2433
|
+
* So a caller ramps it with whatever "fast" means in its world. 1 keeps what a scene
|
|
2434
|
+
* written before this got, so nothing changes for anyone who does not call it.
|
|
2435
|
+
*
|
|
2436
|
+
* Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
|
|
2437
|
+
* beside for the same reason: both describe how the finished image is presented rather
|
|
2438
|
+
* than anything in the world.
|
|
2439
|
+
*/
|
|
2440
|
+
setCameraMotionBlur(scale: number): void;
|
|
2441
|
+
/**
|
|
2442
|
+
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
2443
|
+
* to take.
|
|
2444
|
+
*
|
|
2445
|
+
* **The ceiling-and-dial split `setCameraMotionBlur` argues for, with a third number that is
|
|
2446
|
+
* neither.** `depthOfField` is construction-time and decides how far a defocused point may
|
|
2447
|
+
* spread, which is what the effect *costs*; `scale` decides how much of that this frame wants,
|
|
2448
|
+
* so 0 turns it off for a moment without paying for it. `distance` and `range` are not a
|
|
2449
|
+
* fraction of anything — they are where the camera is looking, in metres, and a caller changes
|
|
2450
|
+
* them when the subject moves.
|
|
2451
|
+
*
|
|
2452
|
+
* **Racking focus is the caller's, and that is the same boundary `setOutputExposure` draws.** A
|
|
2453
|
+
* game knows what its camera is looking at; the renderer could only find out by reading a depth
|
|
2454
|
+
* sample back a frame late. Easing `distance` toward a target is two lines wherever that state
|
|
2455
|
+
* already lives, and the clock belongs to the caller in this engine.
|
|
2456
|
+
*
|
|
2457
|
+
* `range` is clamped above zero because it is the divisor of the ramp: at zero, every pixel not
|
|
2458
|
+
* exactly on the plane would be at full blur, which is a different effect and not one anybody
|
|
2459
|
+
* asked for.
|
|
2460
|
+
*
|
|
2461
|
+
* Set before `beginFrame` and held until changed, like `setSpeedRush` and
|
|
2462
|
+
* `setCameraMotionBlur` beside it. Ignored entirely when `depthOfField` is 0.
|
|
2463
|
+
*/
|
|
2464
|
+
setDepthOfField(distance: number, range: number, scale?: number): void;
|
|
2465
|
+
/** Where the lens is focused and how deep the sharp zone is, in metres. See `setDepthOfField`. */
|
|
2466
|
+
private focusDistance;
|
|
2467
|
+
private focusRange;
|
|
2468
|
+
/** How much of `depthOfField` this frame takes. See `setDepthOfField`. */
|
|
2469
|
+
private dofScale;
|
|
2470
|
+
/** The inverse projection, and the four elements of it a depth-to-distance needs. */
|
|
2471
|
+
private readonly dofInvProjection;
|
|
2472
|
+
private readonly dofDepthToView;
|
|
2473
|
+
/**
|
|
2474
|
+
* How much of the frame's bloom to apply, 0 to 1. **Scales** `bloom`, like
|
|
2475
|
+
* `setCameraMotionBlur` scales `cameraMotionBlur`.
|
|
2476
|
+
*
|
|
2477
|
+
* A scale rather than a replacement, and the distinction is worth being explicit about
|
|
2478
|
+
* because the setter beside this one — `setOutputExposure` — is the other kind. A grade has
|
|
2479
|
+
* no natural full value to take a fraction of, so exposure replaces. A strength does: how
|
|
2480
|
+
* much a world blooms is that world's look, decided once, and what a frame gets to say is
|
|
2481
|
+
* how much of it this moment wants. So `bloom: 0.4` with `setBloom(0.5)` is 0.2, and a
|
|
2482
|
+
* caller that never calls this keeps exactly what it asked for at construction.
|
|
2483
|
+
*
|
|
2484
|
+
* That is also what keeps a quality profile meaningful: a part that cannot afford the effect
|
|
2485
|
+
* has it capped in one place rather than in every frame that sets a dial.
|
|
2486
|
+
*
|
|
2487
|
+
* Set before `beginFrame` and held until changed. Ignored entirely when `bloom` is 0, since
|
|
2488
|
+
* the chain is never built.
|
|
2489
|
+
*/
|
|
2490
|
+
setBloom(scale: number): void;
|
|
2491
|
+
/**
|
|
2492
|
+
* How thick the air is, this frame: a global participating medium filling the whole frustum.
|
|
2493
|
+
*
|
|
2494
|
+
* **The dial beside `globalMediumSteps`' ceiling**, on the split every other effect here uses.
|
|
2495
|
+
* The profile says what a march may cost, and this says what the weather is doing — so a game
|
|
2496
|
+
* can walk into a fog bank without the settings screen changing under it, and a device that
|
|
2497
|
+
* cannot afford a medium never draws one however thick the game says the air is.
|
|
2498
|
+
*
|
|
2499
|
+
* `density` is extinction per metre and **0 is a real off**: no target is allocated, no march
|
|
2500
|
+
* runs and no composite is drawn, so a frame at 0 is identical to a build with the feature
|
|
2501
|
+
* absent rather than merely close to it. Everything else is optional and holds until changed:
|
|
2502
|
+
* `albedo` is how much of what the air takes out comes back as light, `anisotropy` is the
|
|
2503
|
+
* Henyey-Greenstein g that makes haze glow toward a low sun, and `maxDistance` is where the
|
|
2504
|
+
* search for light to scatter stops — not where the fog stops, which is what extinction says.
|
|
2505
|
+
*
|
|
2506
|
+
* This is the air, not a beam. `drawLightVolume` is a shaft somebody placed inside a hull;
|
|
2507
|
+
* this is everything between the camera and whatever the frame already drew, and it is the one
|
|
2508
|
+
* of the two that can put the shape of a doorway on a floor.
|
|
2509
|
+
*/
|
|
2510
|
+
setGlobalMedium(density: number, albedo?: number, anisotropy?: number, maxDistance?: number): void;
|
|
2511
|
+
/** This frame's medium. Off until a caller says otherwise. See `setGlobalMedium`. */
|
|
2512
|
+
private mediumOptions;
|
|
2513
|
+
/** Built on the first frame that asks for a medium, and never on one that does not. */
|
|
2514
|
+
private medium;
|
|
2515
|
+
/**
|
|
2516
|
+
* How far this frame's scene is scaled into the tone curve. Replaces `outputExposure`.
|
|
2517
|
+
*
|
|
2518
|
+
* **The same ceiling-and-dial split as `setCameraMotionBlur`, for the same reason.**
|
|
2519
|
+
* `outputExposure` is construction-time and is a *grade*: it says how this world's light
|
|
2520
|
+
* levels are meant to sit in the curve. What it cannot express is that one scene has both a
|
|
2521
|
+
* lit showroom and a dark interior in it, which is where a single exposure is wrong twice —
|
|
2522
|
+
* blown out in the room and unreadable inside. So a frame gets to say what it wants, and a
|
|
2523
|
+
* caller that never says anything keeps the grade it asked for at construction.
|
|
2524
|
+
*
|
|
2525
|
+
* **Adaptation is the caller's, and that is a deliberate boundary rather than a shortcut.**
|
|
2526
|
+
* A game knows it walked into a cave; the renderer can only find out by measuring pixels a
|
|
2527
|
+
* frame late. Easing toward a target is two lines wherever the state already lives, and the
|
|
2528
|
+
* clock belongs to the caller in this engine — see the determinism rules. What the engine
|
|
2529
|
+
* *could* add on top, and has not, is the measurement half: a mip of the scene target read as
|
|
2530
|
+
* an average luminance and smoothed in a 1x1 ping-pong, so a scene that cannot say what it is
|
|
2531
|
+
* looking at gets a number anyway. RENDERING.md §4 has that written down as what is left.
|
|
2532
|
+
*
|
|
2533
|
+
* Ignored unless `outputTransform` is `aces`, because there is no curve to be exposed into
|
|
2534
|
+
* otherwise. Clamped above zero: a zero exposure is a black frame, and a negative one is a
|
|
2535
|
+
* black frame with a sign error in it.
|
|
2536
|
+
*/
|
|
2537
|
+
setOutputExposure(exposure: number): void;
|
|
2538
|
+
/**
|
|
2539
|
+
* Composite a flat colour over the finished frame — for a cut dipping to white or to black.
|
|
2540
|
+
*
|
|
2541
|
+
* `alpha` 0 leaves the picture untouched and is the default; 1 replaces it outright. Nothing
|
|
2542
|
+
* else on this renderer can reach either end: `setOutputExposure`'s own guard clamps above
|
|
2543
|
+
* zero, so black is unreachable by design, and white is not an exposure at all under `aces`,
|
|
2544
|
+
* which is built to roll off rather than clip — driving exposure up gives a bloomed, tinted,
|
|
2545
|
+
* still-legible frame, which is a different effect and a nice one. A veil is the operation
|
|
2546
|
+
* that gap was missing, not a knob left unturned on an existing one.
|
|
2547
|
+
*
|
|
2548
|
+
* **Composited after the tone map, before grain and vignette. That is a ruling, not a
|
|
2549
|
+
* measurement, and it is written out here because a caller reading this is exactly who needs
|
|
2550
|
+
* it.**
|
|
2551
|
+
*
|
|
2552
|
+
* After the tone map, because a transition has to hit exact black and exact white
|
|
2553
|
+
* deterministically: `setFrameVeil(0, 0, 0, 1)` must be pure black and
|
|
2554
|
+
* `setFrameVeil(1, 1, 1, 1)` pure white. Compositing before a curve built to roll off makes
|
|
2555
|
+
* "full white" an asymptote no caller could solve for — the same trap `setOutputExposure` is
|
|
2556
|
+
* already in, and this must not be that.
|
|
2557
|
+
*
|
|
2558
|
+
* Before grain and vignette, because that is the complaint this replaces: painted on top of
|
|
2559
|
+
* an already-finished frame, a dip to black flattens grain and a vignette instead of taking
|
|
2560
|
+
* them down with it, which reads as a sheet laid over the picture rather than the picture
|
|
2561
|
+
* going dark. Composited here, ahead of anything that still runs after the grade, a dip fades
|
|
2562
|
+
* the whole image together.
|
|
2563
|
+
*
|
|
2564
|
+
* This also answers the bloom question by construction rather than by choice: bloom is
|
|
2565
|
+
* resolved upstream of this pass, from the pre-tonemap scene — see `SceneTarget.resolve` —
|
|
2566
|
+
* so a veil composited here never feeds it. That is the right side of its own framing: a dip
|
|
2567
|
+
* is a cut rather than a light, and a veil that bloomed would mean dipping to white lit the
|
|
2568
|
+
* whole frame instead of covering it.
|
|
2569
|
+
*
|
|
2570
|
+
* **Cleared with the frame, unlike `setSpeedRush` and `setCameraMotionBlur` beside it: one
|
|
2571
|
+
* call per frame, and a frame that does not call it draws with no veil at all**, rather than
|
|
2572
|
+
* inheriting whatever the last cut left behind. A transition that forgets to turn itself off
|
|
2573
|
+
* is a worse failure than one that forgets to turn on, which is why this resets in `endFrame`
|
|
2574
|
+
* where those two are held until changed.
|
|
2575
|
+
*
|
|
2576
|
+
* Costs nothing at zero: the shader's `withVeil` is a single comparison that hands the pixel
|
|
2577
|
+
* back unmixed, and nothing extra is allocated on the CPU side to reach it.
|
|
2578
|
+
*/
|
|
2579
|
+
setFrameVeil(r: number, g: number, b: number, alpha: number): void;
|
|
2580
|
+
/**
|
|
2581
|
+
* The colour grade this and every later frame applies, until it is set again.
|
|
2582
|
+
*
|
|
2583
|
+
* A lookup table over **display** values, applied after the tone curve and before the veil —
|
|
2584
|
+
* `colourGrade.ts` argues both placements and `uGradeLut`'s own comment in `rush.ts` states
|
|
2585
|
+
* them. Held rather than per-frame, so a consumer sets it once; the table is uploaded only
|
|
2586
|
+
* when the object it was given changes, which makes calling this from a frame loop free.
|
|
2587
|
+
*
|
|
2588
|
+
* **It needs `screenEffects`, and says so rather than doing nothing.** Without a composite
|
|
2589
|
+
* every forward pass is the last thing to touch the frame and grades itself, so there is no
|
|
2590
|
+
* single place a look could be applied — the same rule and the same words `hdrScene` already
|
|
2591
|
+
* carries. A silent no-op here would be a consumer wondering why their grade does not apply,
|
|
2592
|
+
* which is precisely the failure this engine's rules exist to prevent. Once per renderer,
|
|
2593
|
+
* because a consumer setting a grade every frame would otherwise flood a console.
|
|
2594
|
+
*/
|
|
2595
|
+
setColourGrade(lut: ColourGradeLut | null, strength?: number): void;
|
|
2596
|
+
/**
|
|
2597
|
+
* What this frame asked for. **Every ceiling here is `null`, and that is the finding.**
|
|
2598
|
+
*
|
|
2599
|
+
* This backend imposes no per-frame limit on anything: it sets uniforms per draw and has no
|
|
2600
|
+
* ring to run out of, so it draws whatever it is handed. WebGPU holds thirteen ceilings and
|
|
2601
|
+
* skips the work past any of them. The same scene therefore renders differently on the two
|
|
2602
|
+
* backends with nothing failing anywhere, and a developer working here — which is where most
|
|
2603
|
+
* development happens, because it is the fallback that runs everywhere — cannot see a WebGPU
|
|
2604
|
+
* ceiling approaching.
|
|
2605
|
+
*
|
|
2606
|
+
* Reporting the count with an honest `null` is what makes that visible: a consumer reads the
|
|
2607
|
+
* same line name on both, compares its own number against the ceiling the other backend
|
|
2608
|
+
* publishes, and finds out before a player does rather than after.
|
|
2609
|
+
*/
|
|
2610
|
+
private readonly budget;
|
|
2611
|
+
private readonly drawBudget;
|
|
2612
|
+
/**
|
|
2613
|
+
* Declared here and never asked, because this backend builds none.
|
|
2614
|
+
*
|
|
2615
|
+
* A bind group is a WebGPU object; the equivalent here is loose uniform and sampler calls, which
|
|
2616
|
+
* are not a thing that is created and cached and so not a thing that can leak. Reporting a
|
|
2617
|
+
* standing zero rather than omitting the line is the same argument the `draws` line above makes
|
|
2618
|
+
* in the other direction: a consumer comparing the two backends reads one name on both, and a
|
|
2619
|
+
* line that is absent on one is a line no cross-backend check can compare. The number it is
|
|
2620
|
+
* being compared against should also be zero once the other backend's cache is warm.
|
|
2621
|
+
*/
|
|
2622
|
+
private readonly bindGroupBudget;
|
|
2623
|
+
/** What the frame just drawn asked for. See `budget.ts`, and the note on `budget` above. */
|
|
2624
|
+
get frameBudget(): FrameBudget;
|
|
2625
|
+
/**
|
|
2626
|
+
* Mark whatever the depth buffer holds inside a projector's box.
|
|
2627
|
+
*
|
|
2628
|
+
* **The drawn half of decals, and `projectDecal` is the other.** That one clips the receiving
|
|
2629
|
+
* surface's triangles once and hands back a mesh, which is cheaper every frame and exact — and
|
|
2630
|
+
* which cannot follow a surface that deforms or streams in afterwards. This decides the mark
|
|
2631
|
+
* from the frame's own depth instead, so the receiver may be a skinned mesh, a heightfield being
|
|
2632
|
+
* rewritten, an instanced crowd, or geometry that arrived after the mark was placed.
|
|
2633
|
+
*
|
|
2634
|
+
* **The mark multiplies.** A forward renderer has no G-buffer, so the pixel is already lit by
|
|
2635
|
+
* the time this runs: multiplying takes the receiver's lighting exactly, at the cost that a mark
|
|
2636
|
+
* can darken and tint and never brighten. `decalProjector.ts` argues that at length.
|
|
2637
|
+
*
|
|
2638
|
+
* Recorded here and drawn in `endFrame`, before the frame is composited, so the post chain sees
|
|
2639
|
+
* the mark. Silently nothing where the profile has no screen effects, since there is then no
|
|
2640
|
+
* depth to read: the same rule `hdrScene` and `orderIndependent` carry.
|
|
2641
|
+
*/
|
|
2642
|
+
drawDecal(projector: DecalProjector): void;
|
|
2643
|
+
/**
|
|
2644
|
+
* Reflect what the frame drew, in the surfaces inside a box.
|
|
2645
|
+
*
|
|
2646
|
+
* **What a planar reflection cannot reach.** `beginPlanarReflection` re-renders the world from a
|
|
2647
|
+
* mirrored camera for one horizontal plane at one height, which is exactly right for a lake and
|
|
2648
|
+
* gives nothing for a floor that undulates, a bonnet or a tilted pane — and it costs a second
|
|
2649
|
+
* pass over the scene. This costs one scissored fullscreen pass per surface, follows the surface
|
|
2650
|
+
* per pixel whatever shape it is, and is exact where an object meets the floor.
|
|
2651
|
+
*
|
|
2652
|
+
* **It can only reflect what is on screen.** Anything behind the camera or hidden behind the
|
|
2653
|
+
* reflecting surface is not in the colour buffer and cannot be recovered from it; the fades in
|
|
2654
|
+
* `screenSpaceReflection.ts` are what keep that from being an obvious limit rather than a
|
|
2655
|
+
* pretence that it is not one.
|
|
2656
|
+
*
|
|
2657
|
+
* Recorded here and traced in `endFrame`, after the decals so a mark appears in the reflection
|
|
2658
|
+
* and before the translucent resolve so a pane of glass is drawn over it. Needs `screenEffects`,
|
|
2659
|
+
* and says so once where there is none.
|
|
2660
|
+
*/
|
|
2661
|
+
drawReflection(surface: ReflectiveSurface): void;
|
|
2662
|
+
/** Said once, not per frame, where this renderer cannot read the picture a march samples. */
|
|
2663
|
+
private refuseReflections;
|
|
2664
|
+
/** Said once, not per frame, where a medium was asked for and there is no depth to march against. */
|
|
2665
|
+
private refuseMedium;
|
|
2666
|
+
private mediumRefused;
|
|
2667
|
+
/** Said once, not per frame, where this renderer cannot read the depth a mark is decided from. */
|
|
2668
|
+
private refuseDecals;
|
|
2669
|
+
/**
|
|
2670
|
+
* Said once, not per frame, where multisampling excludes the effect a profile asked for.
|
|
2671
|
+
*
|
|
2672
|
+
* **A refusal rather than a silent sort, and the reason is who is on the other side of it.** The
|
|
2673
|
+
* two are both switches on a graphics screen, so a player turning antialiasing on turns this off
|
|
2674
|
+
* — and a quality setting that is enabled and does nothing is the fault `capabilityClamp` and
|
|
2675
|
+
* the bloom warning in the constructor exist to prevent. The other backend refuses the same
|
|
2676
|
+
* profile from the same constant, which is what stops the two compositing different pictures
|
|
2677
|
+
* from one `RenderQuality` — see `OIT_MULTISAMPLE_REFUSAL` for why it is a constant and not two
|
|
2678
|
+
* literals kept in step by hand.
|
|
2679
|
+
*/
|
|
2680
|
+
private refuseOitMultisampled;
|
|
2681
|
+
beginFrame(clearColor: Vec3): void;
|
|
2682
|
+
/**
|
|
2683
|
+
* Present the frame.
|
|
2684
|
+
*
|
|
2685
|
+
* Called at the very end of a render pass, and **always resolves to the canvas** — a
|
|
2686
|
+
* frame recorder reads the canvas to build a clip and a still is a canvas copy, so a
|
|
2687
|
+
* scene left in a framebuffer would export black while the screen looked right. A no-op
|
|
2688
|
+
* when screen effects are off, so a caller may call it unconditionally.
|
|
2689
|
+
*/
|
|
2690
|
+
endFrame(): void;
|
|
2691
|
+
/**
|
|
2692
|
+
* Whether the driver has finished the first frame, not merely been asked for it.
|
|
2693
|
+
*
|
|
2694
|
+
* For whatever is covering the load: a plate lifted on the frame being submitted
|
|
2695
|
+
* uncovers the screen while the GPU process is still paying that frame's
|
|
2696
|
+
* first-use bill, and the player watches the last of the load rather than being
|
|
2697
|
+
* spared it. Poll it once a frame; it never goes back to false.
|
|
2698
|
+
*
|
|
2699
|
+
* Non-blocking by construction — a zero timeout asks the driver what it has
|
|
2700
|
+
* finished and takes the answer, rather than waiting for the one it wants.
|
|
2701
|
+
*/
|
|
2702
|
+
get firstFrameSettled(): boolean;
|
|
2703
|
+
/**
|
|
2704
|
+
* Make the flat program current, before anything writes one of its uniforms.
|
|
2705
|
+
*
|
|
2706
|
+
* **A uniform location belongs to a program, and writing it while a different program
|
|
2707
|
+
* is bound is an `INVALID_OPERATION` that uploads nothing.** Every method here that
|
|
2708
|
+
* writes `flatUniforms` calls this first, and that is a rule about this class rather
|
|
2709
|
+
* than a suggestion: the flat pass is bound once by `bindMeshPass` and then *interrupted*
|
|
2710
|
+
* by every other pass there is — water, particles, plumes, flock, bolts, streaks, film,
|
|
2711
|
+
* sky — each of which binds a program of its own. Any flat-pass call made after one of
|
|
2712
|
+
* those was writing to a program that had not been current since.
|
|
2713
|
+
*
|
|
2714
|
+
* Measured, on the demos, before this existed: `drawTranslucentMesh` in the chamber
|
|
2715
|
+
* scene wrote `uModel` and `uOpacity` to the flat program while the particle program was
|
|
2716
|
+
* bound, and `drawMesh` in the sea scene wrote `uModel` while the flock program was. Both
|
|
2717
|
+
* raised `INVALID_OPERATION` every frame, and both then drew the mesh through whichever
|
|
2718
|
+
* shader *was* bound, since a draw call takes the program it finds.
|
|
2719
|
+
*
|
|
2720
|
+
* That error mattered beyond the geometry it spoiled. A pending GL error is not attached
|
|
2721
|
+
* to the call that raised it — the next `getError` anywhere reads it and concludes
|
|
2722
|
+
* whatever it was checking has failed. The depth-blit guard in `sceneTarget.ts` did
|
|
2723
|
+
* exactly that: it read this error as a driver refusing a depth resolve and switched
|
|
2724
|
+
* camera motion blur off for the session.
|
|
2725
|
+
*
|
|
2726
|
+
* The other repair — having each interrupting pass hand the flat program back — was
|
|
2727
|
+
* rejected. It puts the obligation on every pass that will ever be added rather than on
|
|
2728
|
+
* the six places that write these uniforms, and a pass that forgets reintroduces exactly
|
|
2729
|
+
* this, silently.
|
|
2730
|
+
*
|
|
2731
|
+
* Not filtered for redundancy. A cache of "what is bound" would have to be invalidated
|
|
2732
|
+
* by every call that delegates to a pass module, which is the same forgettable
|
|
2733
|
+
* obligation in a new shape, and the wrong answer is a silent no-op rather than a
|
|
2734
|
+
* redundant bind. The engine's draw budget is under 100 calls a frame; measured on the
|
|
2735
|
+
* chamber and sea scenes, GPU time did not move.
|
|
2736
|
+
*/
|
|
2737
|
+
private useFlatProgram;
|
|
2738
|
+
/** Bind the flat pass once per frame; then issue any number of drawMesh calls. */
|
|
2739
|
+
bindMeshPass(camera: Camera, env: Environment): void;
|
|
2740
|
+
/**
|
|
2741
|
+
* Everything one flat program needs to know about this frame.
|
|
2742
|
+
*
|
|
2743
|
+
* Split out of `bindMeshPass` so it can be run against each program rather than only the one
|
|
2744
|
+
* that happened to be bound. Takes its uniform table as a parameter for exactly that reason: a
|
|
2745
|
+
* method reaching for `this.flatUniforms` would silently write the plain program's locations
|
|
2746
|
+
* whichever program is current, which is an `INVALID_OPERATION` that uploads nothing.
|
|
2747
|
+
*/
|
|
2748
|
+
private writeMeshPassState;
|
|
2749
|
+
/**
|
|
2750
|
+
* Draw world geometry.
|
|
2751
|
+
*
|
|
2752
|
+
* `depthLayer` orders geometry that **occupies the same surface** as other geometry
|
|
2753
|
+
* — a deck crossing another deck, a kerb fused into the slab it trims, a marking
|
|
2754
|
+
* inlaid in a floor. A higher layer wins the depth test wherever two surfaces
|
|
2755
|
+
* coincide. 0, the default, is the base world and takes no offset at all.
|
|
2756
|
+
*
|
|
2757
|
+
* The engine cannot guess which of two fused surfaces should win, so it does not
|
|
2758
|
+
* try: the consumer declares the order once, and the engine's promise is that the
|
|
2759
|
+
* answer is then the *same from every angle*. That is the whole defect — not that
|
|
2760
|
+
* the wrong surface wins, but that which one wins is not decided anywhere.
|
|
2761
|
+
*
|
|
2762
|
+
* Without it, two coplanar surfaces are a coin toss taken per pixel. Their
|
|
2763
|
+
* interpolated depths are equal in exact arithmetic, so the comparison is decided
|
|
2764
|
+
* by which way the rounding fell in each triangle's interpolators, and that
|
|
2765
|
+
* pattern changes with the view: a kerb reads as a full band from one angle and a
|
|
2766
|
+
* hairline from another, and the seam between two fused slabs crawls with a hatch.
|
|
2767
|
+
*
|
|
2768
|
+
* It turned up in six places in a single session, and the right call was to refuse
|
|
2769
|
+
* the workaround: this belongs at engine level, and level design should not be bent
|
|
2770
|
+
* around an engine bug. The workaround is real — consumers had been lifting markings
|
|
2771
|
+
* a centimetre or two off the surfaces they belong to, one hand-tuned constant at a
|
|
2772
|
+
* time, to dodge this.
|
|
2773
|
+
*
|
|
2774
|
+
* Two measurements narrowed it to exactly this and are worth keeping, because both
|
|
2775
|
+
* eliminate a plausible cure:
|
|
2776
|
+
*
|
|
2777
|
+
* - **Not depth precision.** With the near plane at 2.0 the buffer resolves seven
|
|
2778
|
+
* microns at the distances involved and the artefact was unchanged. So the
|
|
2779
|
+
* surfaces are not nearly-coincident, they are *coincident* — and no depth
|
|
2780
|
+
* format separates equal depths. That is what took a reversed-Z conversion off
|
|
2781
|
+
* the table.
|
|
2782
|
+
* - **Not the shadow pass.** With `?shadows=0`, unchanged.
|
|
2783
|
+
*
|
|
2784
|
+
* `POLYGON_OFFSET_FILL` is the feature built for this. The slope term matters as
|
|
2785
|
+
* much as the constant one: a band seen at a grazing angle covers many depth units
|
|
2786
|
+
* across one pixel, and a fixed nudge that suffices head-on vanishes there.
|
|
2787
|
+
*/
|
|
2788
|
+
drawMesh(mesh: Mesh, model: ReadonlyMat4, depthLayer?: number,
|
|
2789
|
+
/**
|
|
2790
|
+
* Optional per-draw colour multiplier, or null for the mesh's own colours.
|
|
2791
|
+
*
|
|
2792
|
+
* For anything that has to take on a colour decided at runtime — a part matching the
|
|
2793
|
+
* surface underneath it, a highlight, a team tint — where the alternative is
|
|
2794
|
+
* rebuilding vertex data every frame. Reset to white after the call, so a caller that
|
|
2795
|
+
* passes one cannot leak it into the next draw.
|
|
2796
|
+
*/
|
|
2797
|
+
tint?: Vec3 | null): void;
|
|
2798
|
+
/**
|
|
2799
|
+
* Draw a mesh you can see through, in the same material as everything else by default.
|
|
2800
|
+
*
|
|
2801
|
+
* For geometry that is *present* without being solid — a sign hung in the air, a
|
|
2802
|
+
* hologram, a marker over a course. Lit, fogged and shaded exactly as the world
|
|
2803
|
+
* is, because it belongs to the world; the only difference is that the world
|
|
2804
|
+
* carries on behind it.
|
|
2805
|
+
*
|
|
2806
|
+
* **`options` turns either of those off, and it is the same draw doing it rather than a
|
|
2807
|
+
* second one.** Geometry, blend state and depth behaviour are unchanged either way; only
|
|
2808
|
+
* whether the fragment stage runs lighting and fog differs. See `TranslucentMeshOptions`
|
|
2809
|
+
* for what each half means and why they are independent. A glow shell or a flat backdrop
|
|
2810
|
+
* plate that needs to read exactly its own colour — three.js's `meshBasicMaterial` — is
|
|
2811
|
+
* `{ lit: false, fog: false }`; everything that called this before keeps drawing lit and
|
|
2812
|
+
* fogged, since both default to `true`.
|
|
2813
|
+
*
|
|
2814
|
+
* **It writes depth**, unlike every other blended pass here, and that is not an
|
|
2815
|
+
* oversight. The sky is drawn last, as a full-screen triangle that fills every
|
|
2816
|
+
* pixel the world left untouched — so anything translucent that declined to
|
|
2817
|
+
* write depth is simply painted over wherever it stood against open air.
|
|
2818
|
+
* The first version showed it plainly: a gate came out half visible, clipped away
|
|
2819
|
+
* except for the part that overlapped the deck behind it. A sign hung in the
|
|
2820
|
+
* air is exactly the case that stands against the sky.
|
|
2821
|
+
*
|
|
2822
|
+
* The cost is the usual one: two translucent surfaces in this pass sort by depth instead of
|
|
2823
|
+
* blending through each other, and which of an overlapping pair survives is the order they
|
|
2824
|
+
* were submitted in. **`{ depthWrite: false }` is the tool for a caller with overlapping
|
|
2825
|
+
* glass**, and it was missing for as long as this paragraph named it without providing it —
|
|
2826
|
+
* a consumer importing vehicles found it on a model whose interior is 96 blended surfaces
|
|
2827
|
+
* inside a shell. `{ depthLayer }` is the other half, for a blended surface *coplanar* with
|
|
2828
|
+
* what it decorates, which no ordering can separate. What it buys is that the piece
|
|
2829
|
+
* exists in the depth buffer like everything else in the world.
|
|
2830
|
+
*
|
|
2831
|
+
* Back faces stay culled, so a closed shape blends once rather than twice, and
|
|
2832
|
+
* a marker seen from below reads the same weight as one seen from above.
|
|
2833
|
+
*
|
|
2834
|
+
* Call inside the flat pass, after the opaque draws. Everything it changes is
|
|
2835
|
+
* handed straight back.
|
|
2836
|
+
*/
|
|
2837
|
+
drawTranslucentMesh(mesh: Mesh, model: ReadonlyMat4, opacity: number, options?: TranslucentMeshOptions): void;
|
|
2838
|
+
/**
|
|
2839
|
+
* Capture the room into a cubemap, once, so reflective surfaces can mirror it.
|
|
2840
|
+
*
|
|
2841
|
+
* `drawFace` is handed a camera already aimed and submits the scene exactly as it would to
|
|
2842
|
+
* the screen: a `bindMeshPass`, the world's draws, the sky. It is called six times, and the
|
|
2843
|
+
* whole thing is one bake rather than anything per frame — six submissions at the moment a
|
|
2844
|
+
* caller says the room is finished, then a cubemap fetch per reflective pixel for ever after.
|
|
2845
|
+
*
|
|
2846
|
+
* **Called once the world exists and not before.** A probe baked in a constructor captures a
|
|
2847
|
+
* room that has not been built yet, which is a reflection of nothing that looks exactly like
|
|
2848
|
+
* a reflection of something.
|
|
2849
|
+
*
|
|
2850
|
+
* A no-op without `reflectionProbeSize`, and a no-op on a device with no spare texture unit,
|
|
2851
|
+
* so a caller may issue it unconditionally. Returns whether the room was captured, for a
|
|
2852
|
+
* caller that wants to say so.
|
|
2853
|
+
*
|
|
2854
|
+
* What it must not contain: the passes that bind framebuffers of their own. A shadow bake or
|
|
2855
|
+
* a planar reflection inside `drawFace` would unbind the probe's target halfway through a
|
|
2856
|
+
* face. Shadow maps baked *before* the probe are fine and are the normal arrangement, since
|
|
2857
|
+
* what the probe wants is the lit room.
|
|
2858
|
+
*/
|
|
2859
|
+
bakeReflectionProbe(origin: Vec3, clearColor: Vec3, drawFace: (camera: Camera) => void, options?: ProbeBakeOptions): boolean;
|
|
2860
|
+
/**
|
|
2861
|
+
* Declare where a grid's probes stand, and allocate the layers for them.
|
|
2862
|
+
*
|
|
2863
|
+
* **`texStorage3D` is immutable, so the layer count is fixed here and not grown later.** A grid
|
|
2864
|
+
* that already matches is left alone, which is what makes `bakeReflectionProbe` cheap to call
|
|
2865
|
+
* every time a scene rebakes its one probe.
|
|
2866
|
+
*
|
|
2867
|
+
* Answers false when no probe was allowed at all — no `reflectionProbeSize`, or a device with no
|
|
2868
|
+
* spare texture unit — so a caller may issue it unconditionally.
|
|
2869
|
+
*/
|
|
2870
|
+
setProbeGrid(options: ProbeGridOptions): boolean;
|
|
2871
|
+
/**
|
|
2872
|
+
* Bake one probe of the declared grid: six faces into the scratch cube, then one convolution.
|
|
2873
|
+
*
|
|
2874
|
+
* **One probe per call, and that is the budget question a grid actually raises.** A bake is six
|
|
2875
|
+
* passes plus a convolution per level, which `prefilterEnvMap.ts` says is affordable only because
|
|
2876
|
+
* it happens once per scene; a grid multiplies it by the probe count. A consumer spreading
|
|
2877
|
+
* thirty-two probes over thirty-two frames is doing what the point-shadow pool already does.
|
|
2878
|
+
*
|
|
2879
|
+
* The grid is not sampled until every layer has been filled, so a scene paced this way keeps the
|
|
2880
|
+
* gradient it had until the last probe lands rather than showing half a grid.
|
|
2881
|
+
*/
|
|
2882
|
+
bakeProbe(layer: number, clearColor: Vec3, drawFace: (camera: Camera) => void, options?: ProbeBakeOptions): boolean;
|
|
2883
|
+
/**
|
|
2884
|
+
* Bake every probe of the declared grid, in one call.
|
|
2885
|
+
*
|
|
2886
|
+
* The whole grid at once, for a scene that can afford a stall at load. `bakeProbe` is the same
|
|
2887
|
+
* work paced by the caller, and a grid of any size is worth pacing.
|
|
2888
|
+
*/
|
|
2889
|
+
bakeProbeGrid(clearColor: Vec3, drawFace: (camera: Camera) => void, options?: ProbeBakeOptions): boolean;
|
|
2890
|
+
/**
|
|
2891
|
+
* Light the scene from an environment it did not photograph.
|
|
2892
|
+
*
|
|
2893
|
+
* **Downstream of the capture this is exactly a bake**, which is the whole design: the faces are
|
|
2894
|
+
* uploaded into the same cube a bake fills and the same convolution runs over them. A loaded sky
|
|
2895
|
+
* is therefore not a second lighting path that can be wrong on its own.
|
|
2896
|
+
*
|
|
2897
|
+
* A loaded environment is a grid of one, at the origin, because it is the same everywhere.
|
|
2898
|
+
*/
|
|
2899
|
+
setEnvironmentImage(image: {
|
|
2900
|
+
readonly width: number;
|
|
2901
|
+
readonly height: number;
|
|
2902
|
+
readonly data: Float32Array;
|
|
2903
|
+
}, options?: ProbeBakeOptions): boolean;
|
|
2904
|
+
/**
|
|
2905
|
+
* Draw a volume of light: a beam from a lamp, a shaft through a window, the cone under a
|
|
2906
|
+
* street light.
|
|
2907
|
+
*
|
|
2908
|
+
* **Not a material, and that is the whole point of it being a separate call.** Everything
|
|
2909
|
+
* `drawMesh` does to a surface is wrong for light: it lights it, fogs it toward the medium's
|
|
2910
|
+
* colour, and scales its emissive by how dark the world is. Light is what the fog is *made
|
|
2911
|
+
* of*; a beam is more visible in mist, not less. This pass adds, and does nothing else.
|
|
2912
|
+
*
|
|
2913
|
+
* Two attempts to express a beam as geometry in the flat pass failed, in opposite
|
|
2914
|
+
* directions, and both are worth knowing before reaching for a material again. Alpha blended
|
|
2915
|
+
* it *subtracted*: blending moves the background toward the surface's own colour, so an unlit
|
|
2916
|
+
* wedge darker than the dusk behind it swept a solid dark shape across the frame, which was
|
|
2917
|
+
* reported five times as a bird. Made additive but still fogged, ninety metres of it
|
|
2918
|
+
* multiplied down to nothing at twenty times the emissive.
|
|
2919
|
+
*
|
|
2920
|
+
* **What the caller supplies is a closed hull, and the pass walks the view ray inside it.**
|
|
2921
|
+
* The geometry decides which pixels run and nothing else: every one of them integrates the
|
|
2922
|
+
* air along its own line of sight, so the path length that makes a volume read as a volume
|
|
2923
|
+
* comes out of the walk rather than being modelled.
|
|
2924
|
+
*
|
|
2925
|
+
* It used to rasterise a few flat panes through the axis and weight each fragment by how far
|
|
2926
|
+
* from face-on its pane was turned, which stands in for that path length and works from the
|
|
2927
|
+
* side. It cannot work down the barrel: every pane contains the axis, so a view lying near
|
|
2928
|
+
* that axis lies nearly within all of them at once, each collapses to a narrow bright wedge,
|
|
2929
|
+
* and their union reads as a six-armed asterisk instead of a disc. Reported twice from
|
|
2930
|
+
* outside — as a beam whose shape never changed however it was tuned, and as raw lines in the
|
|
2931
|
+
* opening of a room — and no correction to the weight could have fixed either, because the
|
|
2932
|
+
* fault was that a handful of flat sheets is not a volume.
|
|
2933
|
+
*
|
|
2934
|
+
* Culling is off, because a volume seen from inside is still lit. Depth *testing* stays on,
|
|
2935
|
+
* so a beam passes behind whatever stands in front of it; depth *writing* is off, because a
|
|
2936
|
+
* volume of light occludes nothing.
|
|
2937
|
+
*
|
|
2938
|
+
* **The volume opens from its own origin along its local +Z**, and `length` and `spread` say
|
|
2939
|
+
* how far and how wide: `spread` is half-width over distance, so the tangent of the
|
|
2940
|
+
* half-angle. The shader fades the light to nothing at that length and at that aperture,
|
|
2941
|
+
* which is what lets a beam dissolve into the air rather than stop at a bright square, and
|
|
2942
|
+
* it is why the hull can be a coarse frustum rather than anything shaped like light.
|
|
2943
|
+
* Building the gradient into the geometry instead is a staircase, because `MeshBuilder`
|
|
2944
|
+
* states one emissive per quad.
|
|
2945
|
+
*
|
|
2946
|
+
* **All three numbers describe geometry this call cannot see, and each one fails silently
|
|
2947
|
+
* when it disagrees with the mesh.** `buildLightVolume` in `src/geometry/` builds the hull
|
|
2948
|
+
* from the same `length` and `spread` passed here, which is the way to have none of this
|
|
2949
|
+
* apply; the following is what a caller building its own has to get right, and every one of
|
|
2950
|
+
* them has been paid for from outside this repository.
|
|
2951
|
+
*
|
|
2952
|
+
* - **`length` is measured in the geometry's own units, not in world metres**, because the
|
|
2953
|
+
* shader compares it against the untransformed vertex position. A beam authored as a unit
|
|
2954
|
+
* wedge and placed with an orthonormal basis is *one unit long* whatever `length` says, so
|
|
2955
|
+
* a length of 7.4 puts the whole mesh inside the first 13% of its own falloff and the beam
|
|
2956
|
+
* is invisible. Either the model matrix carries the scale, or the hull is built at world
|
|
2957
|
+
* size. There is no error and no warning: the draw succeeds and nothing appears.
|
|
2958
|
+
* - **`spread` has to be at or under the geometry's own flare**, since the across-axis fade
|
|
2959
|
+
* reaches zero at this aperture and clamps there. Wider than the hull and the fade is still
|
|
2960
|
+
* climbing where the hull ends, which cuts the volume instead of dissolving it. Narrower is
|
|
2961
|
+
* safe and merely reads as a beam thinner than its own silhouette.
|
|
2962
|
+
* - **`strength` is clamped to 1.** Raising it is the natural response to a beam that looks
|
|
2963
|
+
* faint, and above 1 it does nothing at all — so a faint beam is one of the two faults
|
|
2964
|
+
* above, or a colour and emissive that are too low in the mesh itself.
|
|
2965
|
+
*
|
|
2966
|
+
* `strength` fades the whole thing, for a lamp coming up at dusk or dimming at dawn. At 0 it
|
|
2967
|
+
* draws nothing at all rather than adding black, so a caller may keep the call in the frame.
|
|
2968
|
+
*/
|
|
2969
|
+
drawLightVolume(mesh: Mesh, model: ReadonlyMat4, camera: Camera, strength: number, length: number, spread: number, options?: LightVolumeDrawOptions): void;
|
|
2970
|
+
/**
|
|
2971
|
+
* Draw a thin wet film — an oil slick, a puddle, a wet patch — over the world.
|
|
2972
|
+
*
|
|
2973
|
+
* Its own pass because it is its own material: view-dependent iridescence and a
|
|
2974
|
+
* dissolving edge, neither of which the flat shader can express. It runs *after*
|
|
2975
|
+
* the world with depth writes off and blending on, so a slick lies on the
|
|
2976
|
+
* surface it was built against instead of fighting it for the depth buffer.
|
|
2977
|
+
*
|
|
2978
|
+
* The caller never sees a GL call: it hands over a mesh, the camera, the clock
|
|
2979
|
+
* and how strong the sheen should be.
|
|
2980
|
+
*
|
|
2981
|
+
* **`sheen` is iridescence, not wetness, and the two are not the same dial.** Raising it to
|
|
2982
|
+
* make a road look wetter makes it look oilier: the surface bands green to magenta like fuel
|
|
2983
|
+
* on water, because that is what the term models. A consumer found the usable range for wet
|
|
2984
|
+
* tarmac at night by bisection twice, at 0.7 and again at 0.24, and landed on **0.15 to
|
|
2985
|
+
* 0.18**. Start there rather than repeating the search.
|
|
2986
|
+
*
|
|
2987
|
+
* There is no mirror term here to raise instead. A surface that should reflect the scene
|
|
2988
|
+
* rather than shimmer needs a planar pass, and `beginPlanarReflection` is water-only; see the
|
|
2989
|
+
* comment on it. Splitting the two is an open request from outside and is not built.
|
|
2990
|
+
*/
|
|
2991
|
+
drawFilm(mesh: Mesh, camera: Camera, timeSeconds: number, env: Environment, sheen: number, options?: FilmOptions): void;
|
|
2992
|
+
/**
|
|
2993
|
+
* Scale one plume in a batch, 0 to hide it.
|
|
2994
|
+
*
|
|
2995
|
+
* For a fire that is not always burning — an oil slick on its ignition cycle. The caller owns
|
|
2996
|
+
* *when*; nothing here knows what a slick is.
|
|
2997
|
+
*/
|
|
2998
|
+
setPlumeScale(plumes: PlumeRenderer, index: number, scale: number): void;
|
|
2999
|
+
/** Draw last: the sky only fills pixels the world left untouched. */
|
|
3000
|
+
drawSky(camera: Camera, sky: SkyColors, env: Environment): void;
|
|
3001
|
+
/**
|
|
3002
|
+
* What is under a pixel, for a consumer that has to answer a click.
|
|
3003
|
+
*
|
|
3004
|
+
* The engine says *what*, and nothing else: enter, leave, capture, bubbling and the
|
|
3005
|
+
* difference between a click and a drag are the consumer's, which already has that
|
|
3006
|
+
* logic for anything with a drag in it. An event system here would be a second one,
|
|
3007
|
+
* disagreeing with the first.
|
|
3008
|
+
*/
|
|
3009
|
+
registerPickable(source: PickableSource, model: Float32Array): number;
|
|
3010
|
+
updatePickable(handle: number, model: Float32Array): void;
|
|
3011
|
+
unregisterPickable(handle: number): void;
|
|
3012
|
+
pickAt(camera: Camera, cssX: number, cssY: number): PickHit | null;
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* The name this class had until 2026-08-24, kept because it is on the public barrel.
|
|
3016
|
+
*
|
|
3017
|
+
* **An alias rather than a rename with a migration**, because nothing measured needs one: no
|
|
3018
|
+
* consumer in this repository binds either renderer class. Every one of them reaches the engine
|
|
3019
|
+
* through `createRenderer` and holds the result as `RendererApi`, which is the surface both
|
|
3020
|
+
* backends implement — so the concrete class is an implementation detail everywhere but here.
|
|
3021
|
+
*
|
|
3022
|
+
* **What it costs**: two names for one class, and a reader who meets `Renderer` has to learn it
|
|
3023
|
+
* means the WebGL2 one. **What would make it wrong**: a consumer starting to bind it, which would
|
|
3024
|
+
* turn a free alias into a migration owed. Nothing does today, and `docs/PORTING.md` says which
|
|
3025
|
+
* name to prefer. It goes when something else forces a major, and not before.
|
|
3026
|
+
*/
|
|
3027
|
+
export { WebGL2Renderer as Renderer };
|