@driftengine/core 4.5.0 → 4.6.1

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.
Files changed (209) hide show
  1. package/CHANGELOG.json +62 -0
  2. package/README.md +1 -1
  3. package/dist/geometry/boxCsg.d.ts +28 -0
  4. package/dist/geometry/boxCsg.js +139 -0
  5. package/dist/geometry/csg.d.ts +36 -0
  6. package/dist/geometry/csg.js +329 -0
  7. package/dist/geometry/meshBuilder.js +13 -0
  8. package/dist/geometry/meshConcat.js +1 -0
  9. package/dist/geometry/pixelFont.js +2 -0
  10. package/dist/geometry/solid.d.ts +61 -0
  11. package/dist/geometry/solid.js +152 -0
  12. package/dist/geometry/solidFlat.d.ts +35 -0
  13. package/dist/geometry/solidFlat.js +428 -0
  14. package/dist/geometry/solidHarness.d.ts +19 -0
  15. package/dist/geometry/solidHarness.js +66 -0
  16. package/dist/geometry/solidNormals.d.ts +17 -0
  17. package/dist/geometry/solidNormals.js +86 -0
  18. package/dist/geometry/solidRound.d.ts +41 -0
  19. package/dist/geometry/solidRound.js +323 -0
  20. package/dist/geometry/solidWriter.d.ts +16 -0
  21. package/dist/geometry/solidWriter.js +42 -0
  22. package/dist/index.d.ts +23 -2
  23. package/dist/index.js +13 -1
  24. package/dist/render/backend/api.d.ts +17 -3
  25. package/dist/render/backend/drawFog.d.ts +26 -0
  26. package/dist/render/backend/drawFog.js +30 -0
  27. package/dist/render/backend/materialChanges.d.ts +2 -1
  28. package/dist/render/backend/materialChanges.js +6 -1
  29. package/dist/render/backend/webgl2/instanced.d.ts +24 -5
  30. package/dist/render/backend/webgl2/instanced.js +72 -15
  31. package/dist/render/backend/webgl2/renderer.d.ts +122 -8
  32. package/dist/render/backend/webgl2/renderer.js +249 -128
  33. package/dist/render/backend/webgpu/buffers.d.ts +17 -0
  34. package/dist/render/backend/webgpu/buffers.js +34 -3
  35. package/dist/render/backend/webgpu/depthPass.js +5 -1
  36. package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
  37. package/dist/render/backend/webgpu/drawCommand.js +1 -0
  38. package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
  39. package/dist/render/backend/webgpu/flatPass.js +38 -9
  40. package/dist/render/backend/webgpu/glassTintPass.js +2 -1
  41. package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
  42. package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
  43. package/dist/render/backend/webgpu/instanced.d.ts +11 -1
  44. package/dist/render/backend/webgpu/instanced.js +21 -3
  45. package/dist/render/backend/webgpu/latePass.d.ts +6 -2
  46. package/dist/render/backend/webgpu/latePass.js +11 -4
  47. package/dist/render/backend/webgpu/motionPass.js +2 -1
  48. package/dist/render/backend/webgpu/postPass.d.ts +1 -1
  49. package/dist/render/backend/webgpu/renderer.d.ts +100 -10
  50. package/dist/render/backend/webgpu/renderer.js +478 -214
  51. package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
  52. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
  53. package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
  54. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
  55. package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
  56. package/dist/render/driftLight/bake.d.ts +7 -0
  57. package/dist/render/driftLight/bake.js +61 -56
  58. package/dist/render/driftLight/denseField.d.ts +46 -0
  59. package/dist/render/driftLight/denseField.js +109 -0
  60. package/dist/render/driftLight/lightField.d.ts +10 -5
  61. package/dist/render/driftLight/lightField.js +25 -19
  62. package/dist/render/driftLight/presence.d.ts +50 -0
  63. package/dist/render/driftLight/presence.js +43 -0
  64. package/dist/render/driftLight/uniforms.d.ts +3 -3
  65. package/dist/render/driftLight/uniforms.js +5 -6
  66. package/dist/render/driftLight/worldLightField.d.ts +41 -0
  67. package/dist/render/driftLight/worldLightField.js +66 -0
  68. package/dist/render/fixtureAtlas.d.ts +20 -0
  69. package/dist/render/fixtureAtlas.js +49 -0
  70. package/dist/render/instanceCull.d.ts +63 -0
  71. package/dist/render/instanceCull.js +160 -0
  72. package/dist/render/instances.d.ts +15 -0
  73. package/dist/render/lightBudget.d.ts +22 -33
  74. package/dist/render/lightBudget.js +24 -35
  75. package/dist/render/lightGrid.d.ts +42 -0
  76. package/dist/render/lightGrid.js +87 -0
  77. package/dist/render/mesh.d.ts +23 -24
  78. package/dist/render/mesh.js +56 -68
  79. package/dist/render/occlusion.d.ts +10 -0
  80. package/dist/render/occlusion.js +36 -7
  81. package/dist/render/pointLightSelection.d.ts +7 -0
  82. package/dist/render/pointLightSelection.js +29 -4
  83. package/dist/render/rainField.d.ts +13 -0
  84. package/dist/render/rainField.js +27 -5
  85. package/dist/render/recon/lateDepth.d.ts +11 -0
  86. package/dist/render/recon/lateDepth.js +70 -0
  87. package/dist/render/recon/resolve.d.ts +34 -4
  88. package/dist/render/recon/resolve.js +161 -20
  89. package/dist/render/rendererHarness.js +4 -0
  90. package/dist/render/sceneTarget.d.ts +2 -0
  91. package/dist/render/sceneTarget.js +2 -0
  92. package/dist/render/shaders/depth.js +12 -9
  93. package/dist/render/shaders/flat/driftLight.d.ts +1 -1
  94. package/dist/render/shaders/flat/driftLight.js +32 -7
  95. package/dist/render/shaders/flat/index.js +5 -3
  96. package/dist/render/shaders/flat/main.d.ts +1 -1
  97. package/dist/render/shaders/flat/main.js +75 -23
  98. package/dist/render/shaders/flat/preamble.js +75 -20
  99. package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
  100. package/dist/render/shaders/flat/surfaceEffects.js +152 -0
  101. package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
  102. package/dist/render/shaders/generated/depth.wgsl.js +8 -8
  103. package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
  104. package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
  105. package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
  106. package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
  107. package/dist/render/shaders/generated/rush.wgsl.js +21 -16
  108. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
  109. package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
  110. package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
  111. package/dist/render/shaders/generated/text.wgsl.js +1 -1
  112. package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
  113. package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
  114. package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
  115. package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
  116. package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
  117. package/dist/render/shaders/rush.js +11 -1
  118. package/dist/render/shaders/sdfText.d.ts +1 -1
  119. package/dist/render/shaders/sdfText.js +3 -2
  120. package/dist/render/shaders/text.d.ts +1 -1
  121. package/dist/render/shaders/text.js +18 -9
  122. package/dist/render/surfaceEffects.d.ts +83 -0
  123. package/dist/render/surfaceEffects.js +75 -0
  124. package/dist/render/surfaceScene.d.ts +8 -0
  125. package/dist/render/surfaceScene.js +8 -0
  126. package/dist/render/surfaceTexture.d.ts +18 -2
  127. package/dist/render/surfaceTexture.js +68 -15
  128. package/dist/render/temporalAa.d.ts +25 -6
  129. package/dist/render/temporalAa.js +45 -15
  130. package/dist/render/textureSource.d.ts +30 -0
  131. package/dist/render/textureSource.js +53 -0
  132. package/dist/render/vertexDefaults.js +2 -0
  133. package/dist/world/hlod.d.ts +104 -0
  134. package/dist/world/hlod.js +214 -0
  135. package/package.json +6 -6
  136. package/src/geometry/boxCsg.ts +186 -0
  137. package/src/geometry/csg.ts +384 -0
  138. package/src/geometry/meshBuilder.ts +15 -0
  139. package/src/geometry/meshConcat.ts +1 -0
  140. package/src/geometry/pixelFont.ts +2 -0
  141. package/src/geometry/solid.ts +189 -0
  142. package/src/geometry/solidFlat.ts +522 -0
  143. package/src/geometry/solidHarness.ts +83 -0
  144. package/src/geometry/solidNormals.ts +103 -0
  145. package/src/geometry/solidRound.ts +443 -0
  146. package/src/geometry/solidWriter.ts +58 -0
  147. package/src/index.ts +51 -1
  148. package/src/render/backend/api.ts +21 -2
  149. package/src/render/backend/drawFog.ts +31 -0
  150. package/src/render/backend/materialChanges.ts +9 -2
  151. package/src/render/backend/webgl2/instanced.ts +80 -14
  152. package/src/render/backend/webgl2/renderer.ts +311 -150
  153. package/src/render/backend/webgpu/buffers.ts +49 -3
  154. package/src/render/backend/webgpu/depthPass.ts +5 -1
  155. package/src/render/backend/webgpu/drawCommand.ts +6 -0
  156. package/src/render/backend/webgpu/flatPass.ts +41 -11
  157. package/src/render/backend/webgpu/glassTintPass.ts +2 -1
  158. package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
  159. package/src/render/backend/webgpu/instanced.ts +21 -3
  160. package/src/render/backend/webgpu/latePass.ts +12 -3
  161. package/src/render/backend/webgpu/motionPass.ts +2 -1
  162. package/src/render/backend/webgpu/renderer.ts +556 -239
  163. package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
  164. package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
  165. package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
  166. package/src/render/driftLight/bake.ts +85 -62
  167. package/src/render/driftLight/denseField.ts +149 -0
  168. package/src/render/driftLight/lightField.ts +33 -18
  169. package/src/render/driftLight/presence.ts +69 -0
  170. package/src/render/driftLight/uniforms.ts +8 -9
  171. package/src/render/driftLight/worldLightField.ts +83 -0
  172. package/src/render/fixtureAtlas.ts +63 -0
  173. package/src/render/instanceCull.ts +205 -0
  174. package/src/render/instances.ts +16 -0
  175. package/src/render/lightBudget.ts +25 -36
  176. package/src/render/lightGrid.ts +130 -0
  177. package/src/render/mesh.ts +64 -72
  178. package/src/render/occlusion.ts +51 -7
  179. package/src/render/pointLightSelection.ts +72 -4
  180. package/src/render/rainField.ts +39 -5
  181. package/src/render/recon/lateDepth.ts +83 -0
  182. package/src/render/recon/resolve.ts +206 -20
  183. package/src/render/rendererHarness.ts +4 -0
  184. package/src/render/sceneTarget.ts +4 -0
  185. package/src/render/shaders/depth.ts +12 -9
  186. package/src/render/shaders/flat/driftLight.ts +32 -7
  187. package/src/render/shaders/flat/index.ts +5 -3
  188. package/src/render/shaders/flat/main.ts +75 -23
  189. package/src/render/shaders/flat/preamble.ts +75 -20
  190. package/src/render/shaders/flat/surfaceEffects.ts +152 -0
  191. package/src/render/shaders/generated/depth.wgsl.ts +8 -8
  192. package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
  193. package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
  194. package/src/render/shaders/generated/rush.wgsl.ts +21 -16
  195. package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
  196. package/src/render/shaders/generated/text.wgsl.ts +1 -1
  197. package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
  198. package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
  199. package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
  200. package/src/render/shaders/rush.ts +11 -1
  201. package/src/render/shaders/sdfText.ts +3 -2
  202. package/src/render/shaders/text.ts +18 -9
  203. package/src/render/surfaceEffects.ts +132 -0
  204. package/src/render/surfaceScene.ts +16 -0
  205. package/src/render/surfaceTexture.ts +110 -19
  206. package/src/render/temporalAa.ts +78 -14
  207. package/src/render/textureSource.ts +72 -0
  208. package/src/render/vertexDefaults.ts +2 -0
  209. package/src/world/hlod.ts +290 -0
package/dist/index.js CHANGED
@@ -180,6 +180,9 @@ export { countSplatTiles, fillSplatTiles, splatPixelBox, splatTileGrid, splatTil
180
180
  export { GI_SOURCE_FIELD, GI_SOURCE_PROBES, GI_SOURCE_SCREEN, newIndirectResult, traceIndirect, } from './render/gi/chain.js';
181
181
  export { GLOBAL_FIELD_BLEND, composeGlobalField, createGlobalField, sampleGlobalField, } from './render/gi/globalField.js';
182
182
  export { FIELD_FADE_SEC, LightField } from './render/driftLight/lightField.js';
183
+ export { DENSE_MAX_AXIS, DENSE_MAX_HEIGHT, bakeDenseField, } from './render/driftLight/denseField.js';
184
+ export { WorldLightField } from './render/driftLight/worldLightField.js';
185
+ export { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './render/surfaceEffects.js';
183
186
  export { GI_SCREEN_MARCH, SCREEN_TRACE_BIAS_M, screenRayOrigin, traceScreen, } from './render/gi/traceScreen.js';
184
187
  export { FIELD_MARCH, coneRadiusAt, newFieldHit, traceField } from './render/gi/traceField.js';
185
188
  export { PROBE_VISIBILITY_SHARPNESS, bakeProbeVisibility, createProbeVisibility, probeUpdateSchedule, probeVisibilityWeight, sampleProbeVolume, visibleProbes, } from './render/gi/probeVolume.js';
@@ -266,7 +269,8 @@ export { isWeakGpuFamily } from './render/gpuCapability.js';
266
269
  * which is the right time for a boot log and too late for a decision.
267
270
  */
268
271
  export { describeGpu } from './render/gpuCapability.js';
269
- export { DEFAULT_POINT_LIGHT_VIEW_RANGE, createPointLightBuffer, selectPointLights, } from './render/pointLightSelection.js';
272
+ export { DEFAULT_POINT_LIGHT_VIEW_RANGE, createPointLightBuffer, selectGridLights, selectPointLights, } from './render/pointLightSelection.js';
273
+ export { LIGHT_GRID_MAX_CELLS, createLightGrid, gatherLights } from './render/lightGrid.js';
270
274
  export { createFlame, flameFrequencyHz, updateFlame } from './render/flameLight.js';
271
275
  export { computeLightMatrix } from './render/lightMatrix.js';
272
276
  export { PlumeRenderer } from './render/plumeRenderer.js';
@@ -306,6 +310,12 @@ export { SMOKE_FRAG } from './render/shaders/smoke.js';
306
310
  export { WaterRenderer } from './render/waterRenderer.js';
307
311
  export { MeshBuilder } from './geometry/meshBuilder.js';
308
312
  export { concatMeshes, placeMesh } from './geometry/meshConcat.js';
313
+ export { emptySolid, mergeSolids, solidToMesh, solidVolume, transformSolid, } from './geometry/solid.js';
314
+ export { solidBox, solidExtrude, solidPrism, solidQuad, solidRoundedBox, solidSweep, } from './geometry/solidFlat.js';
315
+ export { solidCapsule, solidCone, solidCylinder, solidFrustum, solidHemisphere, solidIcosphere, solidLathe, solidSphere, solidTorus, solidTube, } from './geometry/solidRound.js';
316
+ export { solidIntersect, solidSubtract, solidUnion } from './geometry/csg.js';
317
+ export { boxBooleanCells, solidBoxBoolean } from './geometry/boxCsg.js';
318
+ export { smoothSolidNormals } from './geometry/solidNormals.js';
309
319
  /* A tangent frame, which every map in the material track reads. */
310
320
  export { generateTangents } from './geometry/tangents.js';
311
321
  export { Spline, createSplineSample } from './geometry/spline.js';
@@ -474,6 +484,7 @@ export { parseSdfFont } from './render/sdfFont.js';
474
484
  export { DEFAULT_SDF_TEXT_STYLE, SdfTextLayout } from './render/sdfTextLayout.js';
475
485
  export { WindStreakRenderer } from './render/windStreakRenderer.js';
476
486
  export { buildTree } from './geometry/treeBuilder.js';
487
+ export { batchBoxVisible, cullInstances, instancesBox } from './render/instanceCull.js';
477
488
  /*
478
489
  * Large worlds: the cell grid that streams and freezes, and the origin that rendering rebases
479
490
  * against while the simulation never does. See `world/rebase.ts` for why that asymmetry is the
@@ -483,3 +494,4 @@ export { cellBounds, cellCoord, cellCoordInRange, cellCoordsOf, cellIdFor, cellI
483
494
  export { renderOrigin, toRenderSpace, toWorldSpace } from './world/rebase.js';
484
495
  export { cameraPositionFrom, cellPredictor, createCellStream, pumpCellStream, setCellStreamOrigin, } from './world/cellStream.js';
485
496
  export { createFrozenCells, freezeCell, frozenEntities, frozenFingerprintContribution, isCellFrozen, thawCell, } from './world/freeze.js';
497
+ export { HLOD_NONE, HlodSet, createHlodDraws, projectionScaleOf } from './world/hlod.js';
@@ -4,7 +4,7 @@ import type { ReadonlyMat4 } from 'gl-matrix';
4
4
  import type { MeshData } from '../mesh.ts';
5
5
  import type { ProbeBakeOptions } from '../reflectionProbe.ts';
6
6
  import type { Renderer, TranslucentMeshOptions } from './webgl2/renderer.ts';
7
- import type { MeshInstances } from '../instances.ts';
7
+ import type { InstancedOptions, MeshInstances } from '../instances.ts';
8
8
  import type { SurfaceMaterial } from '../surfaceTexture.ts';
9
9
  import type { TextStyle } from '../textLayout.ts';
10
10
  import type { SdfFont } from '../sdfFont.ts';
@@ -175,9 +175,23 @@ export interface MeshOptions {
175
175
  }
176
176
  export type RendererApi = Omit<{
177
177
  [K in keyof Renderer]: Renderer[K];
178
- }, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial'> & {
178
+ }, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial'> & {
179
179
  /** Upload a caller's image. See `SurfaceTextureHandle` for the boundary this guards. */
180
180
  createSurfaceTexture(source: TexImageSource, options?: Parameters<Renderer['createSurfaceTexture']>[1]): SurfaceTextureHandle;
181
+ /**
182
+ * Upload several images of one size as the layers of one array texture.
183
+ *
184
+ * The same handle a single image returns — every surface texture is an array, and a plain image
185
+ * is an array of one — so it goes wherever a texture goes: any map of `setMaterial`. A mesh picks
186
+ * its image per vertex with `MeshData.layers`, and every map of the material is read at that
187
+ * layer, so an albedo array and an emissive array built in one order pair image with glow by
188
+ * index. **A merged mesh wearing many images is then one draw and one material change.**
189
+ *
190
+ * Every image must be the same size: the device allocates one size for every layer, and the
191
+ * refusal names the first image that differs. A layer past the end reads the last one.
192
+ * `updateSurfaceTexture` refuses an array.
193
+ */
194
+ createSurfaceTextureArray(sources: readonly TexImageSource[], options?: Parameters<Renderer['createSurfaceTexture']>[1]): SurfaceTextureHandle;
181
195
  /**
182
196
  * Replace a texture's pixels, keeping the GPU object and its sampler state.
183
197
  *
@@ -324,7 +338,7 @@ export type RendererApi = Omit<{
324
338
  * would wear one expression. Both are refused where the variant is built rather than dropped
325
339
  * quietly.
326
340
  */
327
- createInstanced(mesh: MeshHandle, capacity: number): InstancedHandle;
341
+ createInstanced(mesh: MeshHandle, capacity: number, options?: InstancedOptions): InstancedHandle;
328
342
  /**
329
343
  * Push placement and colour to the device. Only `data.count` instances are written.
330
344
  *
@@ -0,0 +1,26 @@
1
+ /**
2
+ * How a draw meets the medium between it and the eye: the value of the mesh shader's
3
+ * `uFogEnabled`, which both backends write from this one rule.
4
+ *
5
+ * **A surface recedes into the medium; light added to the frame fades in it.** A surface that
6
+ * covers what is behind it is mixed toward the medium's colour with distance, which is the haze
7
+ * standing in front of it. An additive draw covers nothing: what is behind it has already been
8
+ * fogged, and mixing the draw toward the medium's colour and then *adding* it puts that haze into
9
+ * the frame a second time. So a glow by day added the sky's grey in the shape of the glow, and
10
+ * added more of it the thicker the air — reported as solid grey cones under every street lamp on
11
+ * a hazy morning. Faded instead, added light loses what the medium scatters out of it and adds
12
+ * nothing of its own, which is how the same shader already treats the glow behind frosted glass.
13
+ *
14
+ * What fading gives up is the one thing mixing did for a glow: at great distance it disappears
15
+ * into the air instead of greying into it. That is what light in fog does. What would make it
16
+ * wrong is an additive draw standing for *lit air* rather than light — and that is what
17
+ * `drawLightVolume` is for, which is fogged by neither rule.
18
+ */
19
+ /** Out of the medium entirely: `TranslucentMeshOptions.fog` set to false. */
20
+ export declare const FOG_OFF = 0;
21
+ /** Mixed toward the medium's colour with distance: every surface, and the pass's default. */
22
+ export declare const FOG_RECEDE = 1;
23
+ /** Scaled down by what the medium scatters and given none of its colour: added light. */
24
+ export declare const FOG_FADE = 2;
25
+ /** The medium rule for a draw that asked for fog or not, and adds its light or covers. */
26
+ export declare function fogModeOf(fog: boolean, additive: boolean): number;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * How a draw meets the medium between it and the eye: the value of the mesh shader's
3
+ * `uFogEnabled`, which both backends write from this one rule.
4
+ *
5
+ * **A surface recedes into the medium; light added to the frame fades in it.** A surface that
6
+ * covers what is behind it is mixed toward the medium's colour with distance, which is the haze
7
+ * standing in front of it. An additive draw covers nothing: what is behind it has already been
8
+ * fogged, and mixing the draw toward the medium's colour and then *adding* it puts that haze into
9
+ * the frame a second time. So a glow by day added the sky's grey in the shape of the glow, and
10
+ * added more of it the thicker the air — reported as solid grey cones under every street lamp on
11
+ * a hazy morning. Faded instead, added light loses what the medium scatters out of it and adds
12
+ * nothing of its own, which is how the same shader already treats the glow behind frosted glass.
13
+ *
14
+ * What fading gives up is the one thing mixing did for a glow: at great distance it disappears
15
+ * into the air instead of greying into it. That is what light in fog does. What would make it
16
+ * wrong is an additive draw standing for *lit air* rather than light — and that is what
17
+ * `drawLightVolume` is for, which is fogged by neither rule.
18
+ */
19
+ /** Out of the medium entirely: `TranslucentMeshOptions.fog` set to false. */
20
+ export const FOG_OFF = 0;
21
+ /** Mixed toward the medium's colour with distance: every surface, and the pass's default. */
22
+ export const FOG_RECEDE = 1;
23
+ /** Scaled down by what the medium scatters and given none of its colour: added light. */
24
+ export const FOG_FADE = 2;
25
+ /** The medium rule for a draw that asked for fog or not, and adds its light or covers. */
26
+ export function fogModeOf(fog, additive) {
27
+ if (!fog)
28
+ return FOG_OFF;
29
+ return additive ? FOG_FADE : FOG_RECEDE;
30
+ }
@@ -21,7 +21,8 @@
21
21
  export interface DrawMaterialOptions {
22
22
  readonly opacity: number;
23
23
  readonly lit: boolean;
24
- readonly fog: boolean;
24
+ /** How it meets the medium, as `fogModeOf` says; the pass's own is `FOG_RECEDE`. */
25
+ readonly fog: number;
25
26
  readonly toneMapped: boolean;
26
27
  /** Whether the draw refracts this time, which a backend decides by whether it has a snapshot. */
27
28
  readonly refracting: boolean;
@@ -17,9 +17,14 @@
17
17
  * What it gives up: a setter called with the value already set still counts, because it takes a
18
18
  * slot on the backend that keeps slots, and comparing every field would cost more than the slot.
19
19
  */
20
+ import { FOG_RECEDE } from './drawFog.js';
20
21
  /** Whether a draw's options differ from the pass's, so it takes a material and leaves one to restore. */
21
22
  export function ownsMaterial(options) {
22
- return (options.opacity < 1 || !options.lit || !options.fog || !options.toneMapped || options.refracting);
23
+ return (options.opacity < 1 ||
24
+ !options.lit ||
25
+ options.fog !== FOG_RECEDE ||
26
+ !options.toneMapped ||
27
+ options.refracting);
23
28
  }
24
29
  /** Whether the next draw shares the open material or opens a new one. */
25
30
  export class MaterialChanges {
@@ -1,21 +1,40 @@
1
1
  import type { MeshInstances } from '../../instances.ts';
2
2
  import type { Mesh } from '../../mesh.ts';
3
+ import type { FrustumPlanes } from '../../../math/frustum.ts';
3
4
  /**
4
- * One mesh's per-instance buffer, bound to that mesh's own vertex array.
5
+ * One batch of a mesh: its per-instance buffer and a vertex array of its own.
5
6
  *
6
7
  * The twin of the WebGPU batch and deliberately the same shape, because which floats go where is
7
- * a decision shared through `instances.ts` and only the binding is per-backend.
8
+ * a decision shared through `instances.ts` and only the binding is per-backend. The vertex array is
9
+ * the batch's rather than the mesh's (`Mesh.createInstanceArray`), which is what lets one mesh carry
10
+ * a batch per region.
8
11
  *
9
- * The staging array is allocated here and rewritten in place, so a frame re-uploading a moving
12
+ * **A culling batch holds a second buffer and array**, for the instances `cullInstances` keeps each
13
+ * camera draw. The full set stays in the first, because a shadow pass, a mirror and a probe each
14
+ * see a different part of the batch than the camera does, and they draw from it.
15
+ *
16
+ * Staging arrays are allocated here and rewritten in place, so a frame re-uploading or re-culling a
10
17
  * batch allocates nothing.
11
18
  */
12
19
  export declare class InstancedBatch {
13
20
  readonly mesh: Mesh;
14
21
  readonly capacity: number;
22
+ /** Whether the camera draw culls this batch. See `InstancedOptions.cull`. */
23
+ readonly cull: boolean;
24
+ /** The world box around every live instance, fitted at upload. See `instancesBox`. */
25
+ readonly box: Float32Array<ArrayBuffer>;
15
26
  private readonly buffer;
27
+ private readonly vao;
16
28
  private readonly staging;
17
- constructor(gl: WebGL2RenderingContext, mesh: Mesh, capacity: number);
18
- /** Pack and push the live prefix, in one call. */
29
+ private readonly kept;
30
+ constructor(gl: WebGL2RenderingContext, mesh: Mesh, capacity: number, cull?: boolean);
19
31
  upload(gl: WebGL2RenderingContext, data: MeshInstances): void;
32
+ /** Draw the first `count` uploaded instances: every draw but a culling camera draw. */
33
+ draw(gl: WebGL2RenderingContext, count: number): void;
34
+ /**
35
+ * Cull `data` against `frustum`, upload what survives into the second buffer and draw it. Returns
36
+ * how many were drawn — 0 draws nothing. Only for a batch made with `cull`.
37
+ */
38
+ drawCulled(gl: WebGL2RenderingContext, data: MeshInstances, frustum: FrustumPlanes): number;
20
39
  dispose(gl: WebGL2RenderingContext): void;
21
40
  }
@@ -1,34 +1,53 @@
1
- import { INSTANCE_FLOATS, INSTANCE_STRIDE, packInstances } from '../../instances.js';
1
+ import { cullInstances, instancesBox } from '../../instanceCull.js';
2
+ import { INSTANCE_FLOATS, INSTANCE_STRIDE, createMeshInstances, packInstances, } from '../../instances.js';
2
3
  /**
3
- * One mesh's per-instance buffer, bound to that mesh's own vertex array.
4
+ * One batch of a mesh: its per-instance buffer and a vertex array of its own.
4
5
  *
5
6
  * The twin of the WebGPU batch and deliberately the same shape, because which floats go where is
6
- * a decision shared through `instances.ts` and only the binding is per-backend.
7
+ * a decision shared through `instances.ts` and only the binding is per-backend. The vertex array is
8
+ * the batch's rather than the mesh's (`Mesh.createInstanceArray`), which is what lets one mesh carry
9
+ * a batch per region.
7
10
  *
8
- * The staging array is allocated here and rewritten in place, so a frame re-uploading a moving
11
+ * **A culling batch holds a second buffer and array**, for the instances `cullInstances` keeps each
12
+ * camera draw. The full set stays in the first, because a shadow pass, a mirror and a probe each
13
+ * see a different part of the batch than the camera does, and they draw from it.
14
+ *
15
+ * Staging arrays are allocated here and rewritten in place, so a frame re-uploading or re-culling a
9
16
  * batch allocates nothing.
10
17
  */
11
18
  export class InstancedBatch {
12
19
  mesh;
13
20
  capacity;
21
+ /** Whether the camera draw culls this batch. See `InstancedOptions.cull`. */
22
+ cull;
23
+ /** The world box around every live instance, fitted at upload. See `instancesBox`. */
24
+ box = new Float32Array([1, 1, 1, -1, -1, -1]);
14
25
  buffer;
26
+ vao;
15
27
  staging;
16
- constructor(gl, mesh, capacity) {
17
- const buffer = gl.createBuffer();
18
- if (buffer === null)
19
- throw new Error('InstancedBatch: createBuffer failed');
28
+ kept;
29
+ constructor(gl, mesh, capacity, cull = false) {
20
30
  this.mesh = mesh;
21
31
  this.capacity = capacity;
22
- this.buffer = buffer;
32
+ this.cull = cull;
23
33
  this.staging = new Float32Array(capacity * INSTANCE_FLOATS);
24
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
25
- gl.bufferData(gl.ARRAY_BUFFER, capacity * INSTANCE_STRIDE, gl.DYNAMIC_DRAW);
26
- /* `DYNAMIC_DRAW` because a batch of vehicles is rewritten every frame — advisory, but a
27
- driver told the truth can place the buffer where a rewrite is cheap. */
28
- mesh.attachInstances(gl, buffer, INSTANCE_STRIDE);
34
+ this.buffer = allocate(gl, capacity);
35
+ this.vao = mesh.createInstanceArray(gl, this.buffer, INSTANCE_STRIDE);
36
+ if (cull) {
37
+ const buffer = allocate(gl, capacity);
38
+ this.kept = {
39
+ buffer,
40
+ vao: mesh.createInstanceArray(gl, buffer, INSTANCE_STRIDE),
41
+ instances: createMeshInstances(capacity),
42
+ };
43
+ }
44
+ else {
45
+ this.kept = null;
46
+ }
29
47
  }
30
- /** Pack and push the live prefix, in one call. */
31
48
  upload(gl, data) {
49
+ if (this.cull)
50
+ instancesBox(data, this.mesh.bounds, this.box);
32
51
  const count = Math.min(data.count, this.capacity);
33
52
  if (count === 0)
34
53
  return;
@@ -36,7 +55,45 @@ export class InstancedBatch {
36
55
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
37
56
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.staging, 0, count * INSTANCE_FLOATS);
38
57
  }
58
+ /** Draw the first `count` uploaded instances: every draw but a culling camera draw. */
59
+ draw(gl, count) {
60
+ this.mesh.drawInstancesThrough(gl, this.vao, count);
61
+ }
62
+ /**
63
+ * Cull `data` against `frustum`, upload what survives into the second buffer and draw it. Returns
64
+ * how many were drawn — 0 draws nothing. Only for a batch made with `cull`.
65
+ */
66
+ drawCulled(gl, data, frustum) {
67
+ const kept = this.kept;
68
+ if (kept === null)
69
+ return 0;
70
+ const count = cullInstances(data, this.mesh.bounds, frustum, kept.instances);
71
+ if (count === 0)
72
+ return 0;
73
+ packInstances(kept.instances, this.staging);
74
+ gl.bindBuffer(gl.ARRAY_BUFFER, kept.buffer);
75
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.staging, 0, count * INSTANCE_FLOATS);
76
+ this.mesh.drawInstancesThrough(gl, kept.vao, count);
77
+ return count;
78
+ }
39
79
  dispose(gl) {
80
+ gl.deleteVertexArray(this.vao);
40
81
  gl.deleteBuffer(this.buffer);
82
+ if (this.kept !== null) {
83
+ gl.deleteVertexArray(this.kept.vao);
84
+ gl.deleteBuffer(this.kept.buffer);
85
+ }
41
86
  }
42
87
  }
88
+ /**
89
+ * An instance buffer sized to capacity. `DYNAMIC_DRAW` because a batch of vehicles is rewritten
90
+ * every frame — advisory, but a driver told the truth can place the buffer where a rewrite is cheap.
91
+ */
92
+ function allocate(gl, capacity) {
93
+ const buffer = gl.createBuffer();
94
+ if (buffer === null)
95
+ throw new Error('InstancedBatch: createBuffer failed');
96
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
97
+ gl.bufferData(gl.ARRAY_BUFFER, Math.max(1, capacity) * INSTANCE_STRIDE, gl.DYNAMIC_DRAW);
98
+ return buffer;
99
+ }
@@ -15,6 +15,9 @@ import { GpuTimer } from '../../gpuTimer.ts';
15
15
  import type { ColourGradeLut } from '../../colourGrade.ts';
16
16
  import { LightField } from '../../driftLight/lightField.ts';
17
17
  import type { LightFieldOptions, LightFieldSource } from '../../driftLight/lightField.ts';
18
+ import type { DenseLightVolume } from '../../driftLight/denseField.ts';
19
+ import { WorldLightField } from '../../driftLight/worldLightField.ts';
20
+ import type { WorldLightFieldOptions } from '../../driftLight/worldLightField.ts';
18
21
  /**
19
22
  * How far the rush's outermost tap reaches at full strength, in UV.
20
23
  *
@@ -71,7 +74,7 @@ import type { WindStreakOptions } from '../../windStreakRenderer.ts';
71
74
  import type { WindField } from '../../windField.ts';
72
75
  import type { FlockParams } from '../../flockRenderer.ts';
73
76
  import { InstancedBatch } from './instanced.ts';
74
- import type { MeshInstances } from '../../instances.ts';
77
+ import type { InstancedOptions, MeshInstances } from '../../instances.ts';
75
78
  /** Reused each frame: which shader light slot each bound cubemap serves. */
76
79
  export type { ShadowCasterSink, ShadowCasters } from '../../shadowCasters.ts';
77
80
  /** Lighting and atmosphere for a frame. The game decides what goes in it. */
@@ -108,6 +111,29 @@ export interface Environment extends Atmosphere {
108
111
  * how strongly.
109
112
  */
110
113
  nightFactor: number;
114
+ /**
115
+ * Seconds on the caller's clock, for the surfaces that animate — a sign's pulse, a ticker's
116
+ * scroll, a flipbook. The caller's rather than a clock read here, so a held clock holds every
117
+ * surface still and a replay replays them. 0 unless stated.
118
+ */
119
+ surfaceTime?: number;
120
+ /**
121
+ * How wet the world is, 0 to 1: upward surfaces darken and polish, except materials that say
122
+ * they stay dry. The scene-wide counterpart of `drawFilm`, which stays for a puddle. 0 unless
123
+ * stated.
124
+ */
125
+ wetness?: number;
126
+ /**
127
+ * The share of facade windows lit, 0 to 1, which a scene sets from its time of day. Windows switch
128
+ * on in steps by a per-window hash, so a rising share lights more windows rather than brightening
129
+ * the ones already lit. 0 unless stated.
130
+ */
131
+ litWindows?: number;
132
+ /**
133
+ * The share of windows that stay lit however late it gets, 0 to 1, so the small hours are not a
134
+ * dark city. 0 unless stated.
135
+ */
136
+ lateWindows?: number;
111
137
  /**
112
138
  * Emission that only shows where the dominant light does not reach, 0 and off by default.
113
139
  *
@@ -441,6 +467,31 @@ export interface LightVolumeDrawOptions {
441
467
  * boolean is refused everywhere else in this file.
442
468
  */
443
469
  export interface TranslucentMeshOptions {
470
+ /**
471
+ * Add this surface's light to what is behind it instead of covering it: a glow, a beam, a pool
472
+ * of light under a lamp. Its colour times its opacity is added and nothing is darkened, so black
473
+ * is invisible and overlapping glows brighten — which is why such surfaces are authored nearly
474
+ * black where they fade. **Order does not matter** to a sum, so an additive draw is never held
475
+ * for the order-independent pass; it is drawn where it is asked for. Write depth off with it
476
+ * (`depthWrite: false`) or a glow hides the glows behind it.
477
+ */
478
+ /**
479
+ * Drawn into the picture a reconstruction resolves, rather than after it. For a surface that
480
+ * moves with what it lies on — a glow on a wall, a pane in a window — whose one motion is that
481
+ * surface's, so the resolve can carry it with the surface.
482
+ *
483
+ * **Late is the default, and it is right for anything else.** A translucent surface writes no
484
+ * depth the motion pass could test and may move apart from what shows through it — a caption
485
+ * over a moving car — so a history can only smear it; after the upscale it is sharp and has none.
486
+ * **What late gives up** is the edge: drawn unjittered against a depth the jittered render
487
+ * covered, it meets a silhouette the render drew differently every frame, and a glow along an
488
+ * edge is let through on one frame and not the next. Reconstructed, it is resolved with the edge.
489
+ *
490
+ * Only a reconstructing frame draws anything late, so this changes nothing on WebGL2 or with
491
+ * `reconstruction` off. An order-independent blended set replays where it replays.
492
+ */
493
+ readonly reconstructed?: boolean;
494
+ readonly additive?: boolean;
444
495
  /**
445
496
  * `false` draws the mesh exactly its own vertex colour times its own texture, with no
446
497
  * ambient, sun, point lights, specular, reflectivity, grain or emissive touching it —
@@ -881,6 +932,11 @@ export declare class WebGL2Renderer implements RendererApi {
881
932
  private oitActive;
882
933
  /** True while the queue is being replayed, so a replayed draw does not record itself again. */
883
934
  private oitReplaying;
935
+ /** `uSurfaceScene`, resolved once a pass. See `surfaceScene.ts`. */
936
+ private readonly surfaceScene;
937
+ /** The two halves of `uWriteMode`, kept here because a `vec2` is written whole. */
938
+ private oitWeight;
939
+ private ditherFade;
884
940
  /** Said once, not per frame, where the context cannot give the float target the sum needs. */
885
941
  private oitRefused;
886
942
  /** Said once for the whole renderer. See `refuseOitMultisampled`. */
@@ -963,6 +1019,8 @@ export declare class WebGL2Renderer implements RendererApi {
963
1019
  private readonly taaDepthToViewZ;
964
1020
  private readonly aoInvProjection;
965
1021
  private readonly aoProjScale;
1022
+ /** Where this frame's jitter put the occlusion, in the composite's uv. See `uAoOffset`. */
1023
+ private readonly aoOffset;
966
1024
  /**
967
1025
  * Whether this frame has already been resolved to the canvas.
968
1026
  *
@@ -1054,12 +1112,15 @@ export declare class WebGL2Renderer implements RendererApi {
1054
1112
  * world. See `prepareStaticPointShadows`.
1055
1113
  */
1056
1114
  private pointShadowArray;
1057
- /** The photometric atlas, allocated on first use. See `setIesProfiles`. */
1058
- private iesTexture;
1059
- /** Rows in that atlas. One until a consumer loads a profile, which is the row of ones. */
1115
+ /** The fixture atlas — cookies over profiles — allocated on first use. See `fixtureAtlas.ts`. */
1116
+ private fixtureTexture;
1117
+ /** The profiles as packed, kept so a new set of cookies can rebuild the atlas around them. */
1118
+ private iesAtlas;
1119
+ /** The cookie images, kept so a new set of profiles can rebuild the atlas around them. */
1120
+ private cookieImages;
1121
+ /** Rows of profile in the atlas. One until a consumer loads a profile, which is the row of ones. */
1060
1122
  private iesRows;
1061
- private cookieTexture;
1062
- /** Tiles in the cookie atlas. 0 is the white placeholder and the whole off path. */
1123
+ /** Cookie tiles in the atlas. 0 is none, and the whole off path. */
1063
1124
  private cookieTiles;
1064
1125
  /** Horizontal planes each profile occupies. 1 unless something asymmetric was loaded. */
1065
1126
  private iesPlanes;
@@ -1358,6 +1419,11 @@ export declare class WebGL2Renderer implements RendererApi {
1358
1419
  * other half of the same question.
1359
1420
  */
1360
1421
  occluded(bounds: Bounds, model: ReadonlyMat4): boolean;
1422
+ /**
1423
+ * Whether a world-space box is wholly behind the declared occluders — the tighter question for
1424
+ * something that is a box, such as a region. False with no occlusion buffer. See `occluded`.
1425
+ */
1426
+ occludedBox(min: ArrayLike<number>, max: ArrayLike<number>): boolean;
1361
1427
  private readonly passes;
1362
1428
  /** Built once so invoking somebody else's pass allocates nothing. See `PassDefinition.draw`. */
1363
1429
  /**
@@ -1473,7 +1539,17 @@ export declare class WebGL2Renderer implements RendererApi {
1473
1539
  */
1474
1540
  setSpotCookies(images: readonly TexImageSource[]): void;
1475
1541
  setIesProfiles(profiles: readonly PhotometricProfile[]): void;
1476
- /** Bind the atlas and say how many rows it has. Always, for the reason `bindClusters` gives. */
1542
+ /**
1543
+ * The fixture atlas from the profiles and cookies in hand: the profiles' rows at the bottom, the
1544
+ * cookies' tiles on top, one `RGBA16F` texture. See `fixtureAtlas.ts` for the layout.
1545
+ *
1546
+ * **Half float, matching what the other backend must use**: a 32-bit float texture is not
1547
+ * filterable in core WebGPU, and both tables are curves and images that want linear filtering.
1548
+ * Linear and clamped: a profile is sampled between two angles, and a cookie's own inset is what
1549
+ * keeps a tile from its neighbour — clamping alone cannot, the seam being interior to the row.
1550
+ */
1551
+ private buildFixtureAtlas;
1552
+ /** Bind the fixture atlas and say what is in it. Always, for the reason `bindClusters` gives. */
1477
1553
  private bindIesAtlas;
1478
1554
  private bindClusters;
1479
1555
  /**
@@ -1633,6 +1709,13 @@ export declare class WebGL2Renderer implements RendererApi {
1633
1709
  * the consumer's decision. What it gains is a GPU object with sane sampler state.
1634
1710
  */
1635
1711
  createSurfaceTexture(source: TexImageSource, options?: SurfaceTextureOptions): SurfaceTexture;
1712
+ /**
1713
+ * Upload several images of one size as the layers of one array, for a merged mesh that names a
1714
+ * layer per vertex (`MeshData.layers`). One material binding then covers every image, which is
1715
+ * what makes a block wearing forty facades one draw. Every layer must be the same size, and the
1716
+ * refusal names the first that is not.
1717
+ */
1718
+ createSurfaceTextureArray(sources: readonly TexImageSource[], options?: SurfaceTextureOptions): SurfaceTexture;
1636
1719
  /**
1637
1720
  * Replace a surface texture's pixels, keeping the GPU object and its sampler state.
1638
1721
  *
@@ -1762,6 +1845,29 @@ export declare class WebGL2Renderer implements RendererApi {
1762
1845
  */
1763
1846
  setEnvironmentAmbient(_amount: number): void;
1764
1847
  setSurfaceGrain(amount: number): void;
1848
+ /**
1849
+ * One float of material state, into every flat program.
1850
+ *
1851
+ * **Every one, for `setMaterial`'s reason**: an instanced or skinned draw runs a program of its
1852
+ * own and is entitled to the material its caller set before it. These setters wrote the plain
1853
+ * program alone until 2026-09-30, so on this backend a batch drew with the pass defaults — grain
1854
+ * full, no reflection, no relief — while the other backend, whose material block every variant
1855
+ * reads, drew what was asked.
1856
+ */
1857
+ private writeFlatFloat;
1858
+ /**
1859
+ * How much of the surfaces drawn next survives a screen-door dither, for crossfading two levels
1860
+ * of detail of one thing.
1861
+ *
1862
+ * 0 is off and is what `bindMeshPass` restores. A positive amount keeps that share of the
1863
+ * pattern's cells, a negative one keeps the complement, so the level fading in drawn at `t` and
1864
+ * the one fading out at `-t` cover every pixel exactly once — no hole, no pixel drawn twice.
1865
+ * Material state like `setSurfaceGrain`; instanced draws read it too, and shadow casters, which
1866
+ * are a pass of their own, do not. See `uDither` in `flat/preamble.ts` for what it costs.
1867
+ */
1868
+ setDitherFade(amount: number): void;
1869
+ /** `uWriteMode`: the order-independent weight and the dither, which share one uniform row. */
1870
+ private writeMode;
1765
1871
  /**
1766
1872
  * How strong the microscopic relief on the surfaces drawn next is, and how coarse.
1767
1873
  *
@@ -1852,7 +1958,7 @@ export declare class WebGL2Renderer implements RendererApi {
1852
1958
  * One batch per mesh: the attributes bind to the mesh's own vertex array, of which a mesh has
1853
1959
  * one. `Mesh.attachInstances` is what refuses a second, and says why.
1854
1960
  */
1855
- createInstanced(mesh: Mesh, capacity: number): InstancedBatch;
1961
+ createInstanced(mesh: Mesh, capacity: number, options?: InstancedOptions): InstancedBatch;
1856
1962
  /** Push placement and colour. Only the live prefix. */
1857
1963
  uploadInstanced(batch: InstancedBatch, data: MeshInstances): void;
1858
1964
  /** Draw every live instance, opaque. */
@@ -3051,6 +3157,14 @@ export declare class WebGL2Renderer implements RendererApi {
3051
3157
  * the volume is summed into the frame once every brick has landed.
3052
3158
  */
3053
3159
  createLightField(lights: readonly LightFieldSource[], options?: Omit<LightFieldOptions, 'falloff'>): LightField;
3160
+ /**
3161
+ * Hand a world's DriftLight volume to the renderer: every fixed light of a city, summed offline by
3162
+ * `bakeDenseField`, standing in past the frame's exact choice. Replaces any field made before, of
3163
+ * either kind; the scene calls `field.follow(buffer.complete, ...)` each frame as it would for
3164
+ * `createLightField`, and marks the lights the volume summed `inLightField` so none is counted
3165
+ * twice. See `driftLight/worldLightField.ts`.
3166
+ */
3167
+ createWorldLightField(volume: DenseLightVolume, options?: WorldLightFieldOptions): WorldLightField;
3054
3168
  /** Let go of the field and its volumes; the scene's lights are all exact again. */
3055
3169
  disposeLightField(): void;
3056
3170
  private forgetLightFieldVolumes;