@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
@@ -49,14 +49,6 @@ const ATTR_JOINTS = 11;
49
49
  * setting sway, watching nothing move, and having nothing to read about why.
50
50
  */
51
51
  const ATTR_CHANNEL = 13;
52
- /**
53
- * The locations `attachInstances` claims, which a plain draw through the same array must not read.
54
- *
55
- * Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
56
- * the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
57
- * `flatVert` refuses skinning with instancing. See `draw`.
58
- */
59
- const INSTANCE_COLUMNS = [11, 12, 13, 14, 15];
60
52
  const ATTR_WEIGHTS = 12;
61
53
  /**
62
54
  * One absent attribute's constant, at the width the table gave it.
@@ -84,6 +76,9 @@ function applyConstant(gl, location, value) {
84
76
  export class Mesh {
85
77
  vao;
86
78
  buffers = [];
79
+ /** Every attribute bound from a buffer, so a batch can build a vertex array of its own. */
80
+ layout = [];
81
+ indexBuffer = null;
87
82
  /**
88
83
  * The position and normal buffers, kept so `update` can rewrite them.
89
84
  *
@@ -93,6 +88,12 @@ export class Mesh {
93
88
  deformable = null;
94
89
  /** How many floats the position buffer holds, so an update of the wrong size is refused. */
95
90
  positionFloats;
91
+ /**
92
+ * Indices one draw of it issues; the batch-size rule reads it (`cullsInstances`). **At zero the
93
+ * mesh has nothing to draw and no verb draws it**, as on WebGPU (`GpuMesh.indexCount`): a draw of
94
+ * nothing is silent here but is still a call and a counted draw, and the two backends count the
95
+ * same frame.
96
+ */
96
97
  indexCount;
97
98
  /**
98
99
  * The constant values this mesh's *absent* attributes are shaded with, re-applied on
@@ -211,7 +212,14 @@ export class Mesh {
211
212
  gl.disableVertexAttribArray(ATTR_UV);
212
213
  this.constants.push({ location: ATTR_UV, value: ABSENT_ATTRIBUTE['uvs'] });
213
214
  }
215
+ else if (data.layers !== undefined) {
216
+ /* The texture-array layer rides as the third coordinate — every vertex location is spent —
217
+ so a layered mesh uploads (u, v, layer) packed. See `MeshData.layers`. */
218
+ this.attachAttribute(gl, ATTR_UV, packUvLayers(data.uvs, data.layers), 3);
219
+ }
214
220
  else {
221
+ /* Two components; the shader's vec3 reads z as 0 by the GL rule for a missing component,
222
+ which is layer 0 — the only layer a plain texture has. */
215
223
  this.attachAttribute(gl, ATTR_UV, data.uvs, 2);
216
224
  }
217
225
  /* Absent means "inherit albedo", supplied as the constant the shader tests for. */
@@ -297,6 +305,7 @@ export class Mesh {
297
305
  throw new Error('createBuffer failed');
298
306
  this.buffers.push(indexBuffer);
299
307
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
308
+ this.indexBuffer = indexBuffer;
300
309
  this.allocate(gl, gl.ELEMENT_ARRAY_BUFFER, indexBuffer, data.indices, gl.STATIC_DRAW);
301
310
  gl.bindVertexArray(null);
302
311
  }
@@ -304,29 +313,11 @@ export class Mesh {
304
313
  draw(gl) {
305
314
  gl.bindVertexArray(this.vao);
306
315
  /*
307
- * **A mesh with a batch has the instance columns enabled on this very array, and location 13
308
- * is the channel.** `attachInstances` claims 11 to 14 for the model matrix and 15 for the
309
- * tint; a `Mesh` owns one vertex array, so those bindings are still live for a *plain* draw
310
- * through it — and `aChannel` then reads the third column of instance zero's matrix instead
311
- * of the absent-attribute constant. For a placement with no rotation that column is
312
- * `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
313
- * term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
314
- * normals, correct albedo, no error, no warning.
315
- *
316
- * Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
317
- * compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
318
- * rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
319
- * them alike, which is what made it visible at all.
320
- *
321
- * Disabled here and put back by `drawInstances`, rather than given the batch an array of its
322
- * own: that is the larger change this class's own note about two batches already contemplates,
323
- * and it wants one for a different reason. Three calls on a mesh that has a batch and none on
324
- * a mesh that does not.
316
+ * No instance columns to disable: a batch draws through a vertex array of its own
317
+ * (`createInstanceArray`), so this array never carries them. It used to, and location 13 — the
318
+ * channel — then read the third column of instance zero's matrix on a plain draw, which lit a
319
+ * mesh by ambient alone on this backend and not the other.
325
320
  */
326
- if (this.instanced) {
327
- for (const location of INSTANCE_COLUMNS)
328
- gl.disableVertexAttribArray(location);
329
- }
330
321
  // The VAO restores the arrays; these it cannot. See `constants`.
331
322
  for (const constant of this.constants) {
332
323
  const value = constant.value;
@@ -335,32 +326,27 @@ export class Mesh {
335
326
  gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_INT, 0);
336
327
  }
337
328
  /**
338
- * Bind one buffer's per-instance attributes to this mesh's vertex array.
339
- *
340
- * **One batch per mesh, and the vertex array is why.** A `Mesh` owns exactly one, so a second
341
- * batch would rebind locations 11 to 15 on the array the first still draws through — and what
342
- * comes out is one batch's instances placed by the other's matrices, which reads as a transform
343
- * bug in the consumer's own code rather than as a misuse of this.
329
+ * A vertex array for one instanced batch: every attribute this mesh binds from a buffer, its index
330
+ * buffer, and the batch's instance columns — the model matrix at 11 to 14, the tint at 15.
344
331
  *
345
- * **What would make it wrong** is a consumer legitimately wanting two batches of one mesh,
346
- * which wants a vertex array per batch rather than a flag here.
332
+ * **A batch owns its own array**, which is what lets a mesh have more than one batch: a prop drawn
333
+ * once per region is one mesh and a batch per region. With one array a mesh had one batch, since a
334
+ * second would rebind the columns the first draws through and place one batch's instances by the
335
+ * other's matrices. The arrays share every buffer, so a batch costs its instances and nothing of
336
+ * the geometry. The caller deletes what this returns.
347
337
  */
348
- attachInstances(gl, buffer, stride) {
349
- if (this.instanced) {
350
- throw new Error('Mesh: this mesh already has an instanced batch — a Mesh owns one vertex array, so a ' +
351
- 'second batch would rebind the attributes the first draws through.');
338
+ createInstanceArray(gl, instances, stride) {
339
+ const vao = gl.createVertexArray();
340
+ if (vao === null)
341
+ throw new Error('createVertexArray failed');
342
+ gl.bindVertexArray(vao);
343
+ for (const attribute of this.layout) {
344
+ gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
345
+ gl.enableVertexAttribArray(attribute.location);
346
+ gl.vertexAttribPointer(attribute.location, attribute.size, gl.FLOAT, false, 0, 0);
352
347
  }
353
- this.instanced = true;
354
- this.instanceBuffer = buffer;
355
- this.instanceStride = stride;
356
- gl.bindVertexArray(this.vao);
357
- this.bindInstanceColumns(gl, buffer);
358
- gl.bindVertexArray(null);
359
- }
360
- /** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
361
- bindInstanceColumns(gl, buffer) {
362
- const stride = this.instanceStride;
363
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
348
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
349
+ gl.bindBuffer(gl.ARRAY_BUFFER, instances);
364
350
  /* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
365
351
  for (let column = 0; column < 4; column += 1) {
366
352
  const location = 11 + column;
@@ -371,22 +357,12 @@ export class Mesh {
371
357
  gl.enableVertexAttribArray(15);
372
358
  gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
373
359
  gl.vertexAttribDivisor(15, 1);
360
+ gl.bindVertexArray(null);
361
+ return vao;
374
362
  }
375
- instanceBuffer = null;
376
- instanceStride = 0;
377
- /** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
378
- instanced = false;
379
- /**
380
- * Draw `count` instances through the attributes `attachInstances` bound.
381
- *
382
- * The constants are re-applied exactly as `draw` does — a vertex array restores the arrays and
383
- * not the constant attributes, and an absent optional attribute is a constant here.
384
- */
385
- drawInstances(gl, count) {
386
- gl.bindVertexArray(this.vao);
387
- /* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
388
- if (this.instanceBuffer !== null)
389
- this.bindInstanceColumns(gl, this.instanceBuffer);
363
+ /** Draw `count` instances through a batch's own vertex array. See `createInstanceArray`. */
364
+ drawInstancesThrough(gl, vao, count) {
365
+ gl.bindVertexArray(vao);
390
366
  for (const constant of this.constants) {
391
367
  const value = constant.value;
392
368
  applyConstant(gl, constant.location, value);
@@ -438,6 +414,7 @@ export class Mesh {
438
414
  this.allocate(gl, gl.ARRAY_BUFFER, buffer, data, usage);
439
415
  gl.enableVertexAttribArray(location);
440
416
  gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
417
+ this.layout.push({ location, buffer, size });
441
418
  return buffer;
442
419
  }
443
420
  /**
@@ -508,3 +485,14 @@ export function createMeshIncremental(gl, data, dynamic = false) {
508
485
  const mesh = new Mesh(gl, data, dynamic, true);
509
486
  return { mesh, upload: mesh.uploads(gl) };
510
487
  }
488
+ /** Texture coordinates and layers interleaved as (u, v, layer), one row a vertex. */
489
+ export function packUvLayers(uvs, layers) {
490
+ const count = layers.length;
491
+ const packed = new Float32Array(count * 3);
492
+ for (let v = 0; v < count; v++) {
493
+ packed[v * 3] = uvs[v * 2];
494
+ packed[v * 3 + 1] = uvs[v * 2 + 1];
495
+ packed[v * 3 + 2] = layers[v];
496
+ }
497
+ return packed;
498
+ }
@@ -51,6 +51,16 @@ export declare class OcclusionBuffer {
51
51
  * something hidden costs a draw call; culling something visible is a hole.
52
52
  */
53
53
  occluded(bounds: Bounds, model: ReadonlyMat4): boolean;
54
+ /**
55
+ * Whether a world-space box is entirely behind the declared occluders — the tighter question for
56
+ * something that is a box, where the cube around its sphere reaches far past it. **A flat region
57
+ * is the case it exists for**: a city block's sphere is half as deep underground as it is wide,
58
+ * nothing there occludes it, and the sphere test abstains for every block in the city. False
59
+ * whenever anything is uncertain, exactly as `occluded`.
60
+ */
61
+ occludedBox(min: ArrayLike<number>, max: ArrayLike<number>): boolean;
62
+ /** The shared test: a world box's corners projected, its rectangle tested against the pyramid. */
63
+ private boxBehind;
54
64
  /**
55
65
  * A world point into buffer coordinates and depth, written into `CORNERS` at `slot`.
56
66
  *
@@ -33,6 +33,8 @@
33
33
  */
34
34
  /** What an unwritten texel holds: the far plane, which occludes nothing. */
35
35
  const FAR = 1;
36
+ /** Texels a tested rectangle may span at the level it is read from. See `boxBehind`. */
37
+ const TEXELS_ACROSS = 8;
36
38
  export class OcclusionBuffer {
37
39
  /**
38
40
  * The pyramid, level 0 first, each level a quarter of the one before.
@@ -169,15 +171,33 @@ export class OcclusionBuffer {
169
171
  const sz = Math.sqrt(m8 * m8 + m9 * m9 + m10 * m10);
170
172
  const radius = bounds.radius * Math.max(sx, sy, sz);
171
173
  /* The cube that contains the sphere, which contains the object. Conservative twice over. */
174
+ return this.boxBehind(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius);
175
+ }
176
+ /**
177
+ * Whether a world-space box is entirely behind the declared occluders — the tighter question for
178
+ * something that is a box, where the cube around its sphere reaches far past it. **A flat region
179
+ * is the case it exists for**: a city block's sphere is half as deep underground as it is wide,
180
+ * nothing there occludes it, and the sphere test abstains for every block in the city. False
181
+ * whenever anything is uncertain, exactly as `occluded`.
182
+ */
183
+ occludedBox(min, max) {
184
+ if (this.occluders === 0)
185
+ return false;
186
+ if (!this.built)
187
+ this.buildPyramid();
188
+ return this.boxBehind(min[0] ?? 0, min[1] ?? 0, min[2] ?? 0, max[0] ?? 0, max[1] ?? 0, max[2] ?? 0);
189
+ }
190
+ /** The shared test: a world box's corners projected, its rectangle tested against the pyramid. */
191
+ boxBehind(loX, loY, loZ, hiX, hiY, hiZ) {
172
192
  let minX = Infinity;
173
193
  let minY = Infinity;
174
194
  let minZ = Infinity;
175
195
  let maxX = -Infinity;
176
196
  let maxY = -Infinity;
177
197
  for (let corner = 0; corner < 8; corner++) {
178
- const px = corner & 1 ? x + radius : x - radius;
179
- const py = corner & 2 ? y + radius : y - radius;
180
- const pz = corner & 4 ? z + radius : z - radius;
198
+ const px = corner & 1 ? hiX : loX;
199
+ const py = corner & 2 ? hiY : loY;
200
+ const pz = corner & 4 ? hiZ : loZ;
181
201
  if (!this.project(px, py, pz, 0))
182
202
  return false;
183
203
  minX = Math.min(minX, CORNERS[0] ?? 0);
@@ -192,13 +212,22 @@ export class OcclusionBuffer {
192
212
  if (minX < 0 || minY < 0 || maxX > width || maxY > height)
193
213
  return false;
194
214
  /*
195
- * The coarsest level whose texel is at least as wide as the rectangle, so the test reads at
196
- * most two texels each way. `Math.log2` would say the same thing and is one of the calls this
197
- * repository's determinism gate refuses elsewhere; a shift loop says it in integers.
215
+ * The coarsest level at which the rectangle spans at most `TEXELS_ACROSS` texels, so the test
216
+ * reads at most that many plus one each way. `Math.log2` would say the same thing and is one of
217
+ * the calls this repository's determinism gate refuses elsewhere; a shift loop says it in integers.
218
+ *
219
+ * **Eight, not one, since 2026-09-30.** At one the level's texel was as wide as the rectangle, and
220
+ * two such texels aligned to the grid span up to twice it — so anything whose rectangle came near
221
+ * an occluder's edge, or the buffer's own unwritten border row, read unwritten texels and
222
+ * abstained. That was every city region behind a row of buildings, whose rectangle sits just
223
+ * under the skyline; measured on a box seven metres wide behind a wall filling the view, which
224
+ * one abstained at and eight culls. Reading up to 81 texels instead of 4 costs a few dozen
225
+ * compares and keeps the answer exactly as conservative: the maximum over every texel the
226
+ * rectangle touches.
198
227
  */
199
228
  let level = 0;
200
229
  let span = Math.max(maxX - minX, maxY - minY);
201
- while (span > 1 && level + 1 < this.levels.length) {
230
+ while (span > TEXELS_ACROSS && level + 1 < this.levels.length) {
202
231
  span *= 0.5;
203
232
  level++;
204
233
  }
@@ -1,4 +1,5 @@
1
1
  /** Allocation-free selection and packing of point lights for a fixed shader budget. */
2
+ import type { LightGrid } from './lightGrid.ts';
2
3
  export declare const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
3
4
  export interface PointLightSource {
4
5
  x: number;
@@ -189,3 +190,9 @@ export declare function createPointLightBuffer(capacity?: number): PointLightBuf
189
190
  * makes the distance the *same arithmetic on the same operands*, not merely a close number.
190
191
  */
191
192
  export declare function selectPointLights(sources: readonly PointLightSource[], x: number, y: number, z: number, out: PointLightBuffer, timeSeconds?: number, viewRange?: number, castX?: number, castY?: number, castZ?: number): void;
193
+ /**
194
+ * `selectPointLights` over a grid's sources, looking only at the lights near the eye: the same
195
+ * choice, tie for tie, for a world of thousands of fixed lights that the plain scan would visit
196
+ * every frame. See `lightGrid.ts` for why the result is the scan's exactly.
197
+ */
198
+ export declare function selectGridLights(grid: LightGrid, x: number, y: number, z: number, out: PointLightBuffer, timeSeconds?: number, viewRange?: number, castX?: number, castY?: number, castZ?: number): void;
@@ -1,6 +1,7 @@
1
1
  /** Allocation-free selection and packing of point lights for a fixed shader budget. */
2
2
  import { POINT_LIGHT_COS_INNER, POINT_LIGHT_COS_OUTER } from './clusteredLights.js';
3
3
  import { MAX_POINT_LIGHTS, POINT_SHADOW_POOL } from './lightBudget.js';
4
+ import { gatherLights } from './lightGrid.js';
4
5
  export const DEFAULT_POINT_LIGHT_VIEW_RANGE = 180;
5
6
  /**
6
7
  * How far out a light fades to nothing, in metres before its cull distance.
@@ -38,8 +39,15 @@ const FADE_BAND_M = 40;
38
39
  * while it is still too dim to show one.
39
40
  */
40
41
  const CONTENTION_BAND_M = 3;
41
- /** Sort keys for the shadow list, which is ordered independently of the shaded one. */
42
- const shadowRank = new Float32Array(POINT_SHADOW_POOL);
42
+ /**
43
+ * Sort keys for the shadow list, which is ordered independently of the shaded one.
44
+ *
45
+ * **Float64, and ties broken by source index**, the shaded list's rule. These were single precision
46
+ * compared against a double, so which of two lamps exactly as far away came first depended on
47
+ * which way the distance happened to round — and on the order the lamps were met, which is what a
48
+ * grid changes. Now the list is the same whatever order its candidates arrive in.
49
+ */
50
+ const shadowRank = new Float64Array(POINT_SHADOW_POOL);
43
51
  /**
44
52
  * Every light in range this frame, by source index and squared distance, before the nearest are
45
53
  * chosen. Grown when a world offers more than it has held, and never shrunk, so a frame allocates
@@ -177,6 +185,19 @@ export function createPointLightBuffer(capacity = MAX_POINT_LIGHTS) {
177
185
  * makes the distance the *same arithmetic on the same operands*, not merely a close number.
178
186
  */
179
187
  export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRange = DEFAULT_POINT_LIGHT_VIEW_RANGE, castX = x, castY = y, castZ = z) {
188
+ choose(sources, null, sources.length, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ);
189
+ }
190
+ /**
191
+ * `selectPointLights` over a grid's sources, looking only at the lights near the eye: the same
192
+ * choice, tie for tie, for a world of thousands of fixed lights that the plain scan would visit
193
+ * every frame. See `lightGrid.ts` for why the result is the scan's exactly.
194
+ */
195
+ export function selectGridLights(grid, x, y, z, out, timeSeconds = 0, viewRange = DEFAULT_POINT_LIGHT_VIEW_RANGE, castX = x, castY = y, castZ = z) {
196
+ const count = gatherLights(grid, x, y, z, viewRange + grid.maxRadius);
197
+ choose(grid.sources, grid.gathered, count, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ);
198
+ }
199
+ /** The choice itself, over every source or over the ones `visit` names, in source order. */
200
+ function choose(sources, visit, visitCount, x, y, z, out, timeSeconds, viewRange, castX, castY, castZ) {
180
201
  out.count = 0;
181
202
  out.shadowCount = 0;
182
203
  /*
@@ -193,7 +214,8 @@ export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRa
193
214
  */
194
215
  let cutSq = Infinity;
195
216
  let candidateCount = 0;
196
- for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
217
+ for (let visited = 0; visited < visitCount; visited++) {
218
+ const sourceIndex = visit === null ? visited : visit[visited];
197
219
  const light = sources[sourceIndex];
198
220
  if (light === undefined)
199
221
  continue;
@@ -265,7 +287,10 @@ export function selectPointLights(sources, x, y, z, out, timeSeconds = 0, viewRa
265
287
  if (castDistanceSq >= light.radius * light.radius)
266
288
  continue;
267
289
  let shadowSlot = out.shadowCount;
268
- while (shadowSlot > 0 && (shadowRank[shadowSlot - 1] ?? 0) > castDistanceSq)
290
+ while (shadowSlot > 0 &&
291
+ ((shadowRank[shadowSlot - 1] ?? 0) > castDistanceSq ||
292
+ ((shadowRank[shadowSlot - 1] ?? 0) === castDistanceSq &&
293
+ (out.shadowIndex[shadowSlot - 1] ?? -1) > sourceIndex)))
269
294
  shadowSlot--;
270
295
  if (shadowSlot >= POINT_SHADOW_POOL)
271
296
  continue;
@@ -49,6 +49,17 @@ export interface RainFieldOptions {
49
49
  streakSec?: number;
50
50
  /** The ground drops land on, and the roofs they do not fall through. */
51
51
  ground?: GroundSurface;
52
+ /**
53
+ * How near the viewer a streak may pass and still be drawn, metres. Default 1.
54
+ *
55
+ * **A drop that close is out of any focus a camera holds**, and drawn sharp it was a bar across
56
+ * the frame: a streak is a world-space width, so a 6 mm drop 30 cm from the eye drew fourteen
57
+ * pixels thick and as long as the frame, from a city seen from the air in the rain. A streak
58
+ * whose nearest point is inside this distance is not drawn; the drop keeps falling, as a
59
+ * sheltered one does. What it gives up is rain on a lens or a visor, which this field never
60
+ * drew as such anyway. What would make it wrong is a view that wants drops in macro: pass 0.
61
+ */
62
+ clearM?: number;
52
63
  }
53
64
  export declare class RainField {
54
65
  readonly segments: LineSegments;
@@ -71,6 +82,8 @@ export declare class RainField {
71
82
  */
72
83
  streakSec: number;
73
84
  private readonly ground;
85
+ /** How near the viewer a streak may pass and still be drawn, squared. */
86
+ private readonly clear2;
74
87
  /** Drops not drawn this update because something was over them. */
75
88
  private sheltered;
76
89
  /**
@@ -41,6 +41,8 @@ export class RainField {
41
41
  */
42
42
  streakSec;
43
43
  ground;
44
+ /** How near the viewer a streak may pass and still be drawn, squared. */
45
+ clear2;
44
46
  /** Drops not drawn this update because something was over them. */
45
47
  sheltered = 0;
46
48
  /**
@@ -68,6 +70,8 @@ export class RainField {
68
70
  this.speedMps = options.speedMps ?? 9;
69
71
  this.streakSec = options.streakSec ?? 0.04;
70
72
  this.ground = options.ground ?? null;
73
+ const clear = options.clearM ?? 1;
74
+ this.clear2 = clear * clear;
71
75
  this.segments = createLineSegments(options.count);
72
76
  this.x = new Float32Array(options.count);
73
77
  this.y = new Float32Array(options.count);
@@ -141,15 +145,33 @@ export class RainField {
141
145
  this.sheltered++;
142
146
  continue;
143
147
  }
148
+ /* Where it was a shutter-time ago, which is the streak: down by its fall and back along the
149
+ wind, so a gust leans every drop at the same angle without anything trigonometric. */
150
+ const backX = -windX * this.streakSec;
151
+ const backY = this.speedMps * this.streakSec;
152
+ const backZ = -windZ * this.streakSec;
153
+ /* Too near the eye to be drawn (`clearM`): the streak's nearest point to the viewer. */
154
+ if (this.clear2 > 0) {
155
+ const ox = dropX - viewerX;
156
+ const oy = dropY - viewerY;
157
+ const oz = dropZ - viewerZ;
158
+ const length2 = backX * backX + backY * backY + backZ * backZ;
159
+ const along = length2 > 0
160
+ ? Math.min(Math.max(-(ox * backX + oy * backY + oz * backZ) / length2, 0), 1)
161
+ : 0;
162
+ const nx = ox + backX * along;
163
+ const ny = oy + backY * along;
164
+ const nz = oz + backZ * along;
165
+ if (nx * nx + ny * ny + nz * nz < this.clear2)
166
+ continue;
167
+ }
144
168
  const at = this.segments.count;
145
169
  this.segments.from[at * 3] = dropX;
146
170
  this.segments.from[at * 3 + 1] = dropY;
147
171
  this.segments.from[at * 3 + 2] = dropZ;
148
- /* Where it was a shutter-time ago, which is the streak: down by its fall and back along the
149
- wind, so a gust leans every drop at the same angle without anything trigonometric. */
150
- this.segments.to[at * 3] = dropX - windX * this.streakSec;
151
- this.segments.to[at * 3 + 1] = dropY + this.speedMps * this.streakSec;
152
- this.segments.to[at * 3 + 2] = dropZ - windZ * this.streakSec;
172
+ this.segments.to[at * 3] = dropX + backX;
173
+ this.segments.to[at * 3 + 1] = dropY + backY;
174
+ this.segments.to[at * 3 + 2] = dropZ + backZ;
153
175
  this.segments.count = at + 1;
154
176
  }
155
177
  this.primed = true;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Where output pixel (`outX`, `outY`) — its centre, in pixels — falls among the render texels
3
+ * jittered by (`jitterX`, `jitterY`) render texels: into `out`, the cell's first texel and the
4
+ * fraction across it, `[x, y, fx, fy]`.
5
+ */
6
+ export declare function lateDepthCell(outX: number, outY: number, renderWidth: number, renderHeight: number, outputWidth: number, outputHeight: number, jitterX: number, jitterY: number, out: Float64Array | Float32Array): void;
7
+ /**
8
+ * The depth to test against, from `taps` — a 4x4 block of render texels, row-major, starting one
9
+ * texel up and left of the cell's first — at (`fx`, `fy`) across the cell.
10
+ */
11
+ export declare function lateDepthAt(taps: ArrayLike<number>, fx: number, fy: number): number;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The depth a reconstructing frame's late draws are tested against at an output pixel, taken from
3
+ * the render's jittered depth. `shaders/recon/lateDepth.wgsl.ts` is the same arithmetic on the device.
4
+ *
5
+ * **Each surface taken to the pixel, and the nearest of them.** A late draw — a glow, a pane, a
6
+ * caption — is drawn unjittered at the output size, and it has to meet the depth of what it lies on
7
+ * where *it* is. Each of the four render texels about the pixel, placed where the jitter put it,
8
+ * carries its surface on to the pixel's unjittered centre along that surface's own slope; the
9
+ * nearest of the four so carried stands, and a quarter of its slope is given back toward the far
10
+ * side so that a draw lying exactly on the surface is not decided by rounding. On a plane all four
11
+ * arrive at the plane's own depth at the pixel: exact, and the same whatever the jitter was.
12
+ *
13
+ * **A texel's slope is the smaller of its two differences along each axis, and none where they
14
+ * disagree in sign** — the minmod limiter. Beside an edge one difference is the jump, so it is the
15
+ * other, the surface's own, that is taken; a sliver one texel wide disagrees both ways and keeps its
16
+ * own depth, the nearest of the four as before. So a translucent draw still never comes through an
17
+ * opaque edge the render covered by more than the render's own texel.
18
+ *
19
+ * **Why this replaced the nearest of the four.** On a wall seen aslant the nearest of four neighbours
20
+ * stands up to a texel's step in front of the wall at the pixel, so a glow lying a few centimetres
21
+ * proud of it was judged behind the wall; and which neighbour was nearest moved with the jitter, so
22
+ * the glow was drawn one frame and not the next. Measured on a street of lit facades: every strip of
23
+ * glow on every tower flickered frame by frame, 12,415 pixels of one tower between two frames.
24
+ *
25
+ * What it gives up: a surface curving within a texel is carried on as a plane, which a quarter-step
26
+ * tolerance covers. What would make it wrong is depth that is not affine on a plane — a depth written
27
+ * from the fragment stage — which nothing drawn before the late pass writes.
28
+ */
29
+ import { REVERSED_DEPTH } from '../depthConvention.js';
30
+ /** The share of a surface's slope given back toward the far side, for a draw lying on it. */
31
+ const TOLERANCE_SHARE = 0.25;
32
+ /** The smaller of two differences, or none where they disagree in sign. */
33
+ function minmod(a, b) {
34
+ if (a * b <= 0)
35
+ return 0;
36
+ return Math.abs(a) < Math.abs(b) ? a : b;
37
+ }
38
+ /**
39
+ * Where output pixel (`outX`, `outY`) — its centre, in pixels — falls among the render texels
40
+ * jittered by (`jitterX`, `jitterY`) render texels: into `out`, the cell's first texel and the
41
+ * fraction across it, `[x, y, fx, fy]`.
42
+ */
43
+ export function lateDepthCell(outX, outY, renderWidth, renderHeight, outputWidth, outputHeight, jitterX, jitterY, out) {
44
+ /* A texel stands at its index plus a half less the jitter, as the resolve places it. */
45
+ const x = (outX * renderWidth) / outputWidth - 0.5 + jitterX;
46
+ const y = (outY * renderHeight) / outputHeight - 0.5 + jitterY;
47
+ out[0] = Math.floor(x);
48
+ out[1] = Math.floor(y);
49
+ out[2] = x - Math.floor(x);
50
+ out[3] = y - Math.floor(y);
51
+ }
52
+ /**
53
+ * The depth to test against, from `taps` — a 4x4 block of render texels, row-major, starting one
54
+ * texel up and left of the cell's first — at (`fx`, `fy`) across the cell.
55
+ */
56
+ export function lateDepthAt(taps, fx, fy) {
57
+ const t = (x, y) => taps[(y + 1) * 4 + x + 1];
58
+ let depth = REVERSED_DEPTH ? -Infinity : Infinity;
59
+ for (let oy = 0; oy < 2; oy += 1) {
60
+ for (let ox = 0; ox < 2; ox += 1) {
61
+ const d = t(ox, oy);
62
+ const gx = minmod(t(ox + 1, oy) - d, d - t(ox - 1, oy));
63
+ const gy = minmod(t(ox, oy + 1) - d, d - t(ox, oy - 1));
64
+ const carried = d + gx * (fx - ox) + gy * (fy - oy);
65
+ const give = TOLERANCE_SHARE * Math.max(Math.abs(gx), Math.abs(gy));
66
+ depth = REVERSED_DEPTH ? Math.max(depth, carried - give) : Math.min(depth, carried + give);
67
+ }
68
+ }
69
+ return depth;
70
+ }
@@ -54,6 +54,12 @@ export interface ResolveFrame {
54
54
  readonly inverseViewProj: ArrayLike<number>;
55
55
  /** Last frame's view-projection and its inverse, unjittered. */
56
56
  readonly previousViewProj: ArrayLike<number>;
57
+ /**
58
+ * Last frame's view-projection times this frame's inverse: where a texel's clip position was
59
+ * last frame. Derived from the two when absent; a caller holding its matrices at a lower precision
60
+ * than it multiplies at passes its own product, as the renderer does.
61
+ */
62
+ readonly reprojection?: ArrayLike<number>;
57
63
  readonly previousInverseViewProj: ArrayLike<number>;
58
64
  readonly eye: ArrayLike<number>;
59
65
  readonly previousEye: ArrayLike<number>;
@@ -62,6 +68,22 @@ export interface ResolveFrame {
62
68
  readonly previousJitter: ArrayLike<number>;
63
69
  readonly quality: ReconQuality;
64
70
  readonly disocclusion: DisocclusionParams;
71
+ /**
72
+ * Last frame's flicker records, output size, four floats a pixel — the spread, this period's
73
+ * running sum, the last period's mean, and how many periods running repeated, as `Flicker` holds
74
+ * them — or absent for the resolve without its anti-flicker.
75
+ */
76
+ readonly flicker?: ArrayLike<number> | null;
77
+ /**
78
+ * Last frame's depth swings, output size, one float a pixel: the largest miss, relative to the
79
+ * depth, of last frame's depth against the one this pixel's surface should have had, seen lately
80
+ * — held, and halving over a period. Carried with `flicker` and read only with it.
81
+ */
82
+ readonly depthSwing?: ArrayLike<number> | null;
83
+ /** Whether this frame samples the first phase of the jitter's period. */
84
+ readonly periodStart?: boolean;
85
+ /** The jitter's period: the reconstruction's own phase count, not the temporal resolve's. */
86
+ readonly flickerPhases?: number;
65
87
  }
66
88
  /**
67
89
  * How far a render sample's contribution reaches, in output pixels: a Gaussian's standard deviation.
@@ -78,13 +100,21 @@ export declare function reconstructionWeight(dx: number, dy: number): number;
78
100
  * pixel away.
79
101
  */
80
102
  export declare const CONFIDENT_WEIGHT = 1;
103
+ /**
104
+ * How many of its held depth swings a still pixel's depth test tolerates. The peak halves over a
105
+ * period, so the frame before the next once-a-period miss holds just over half of it: tolerating
106
+ * twice the peak passes that miss outright, and three leaves the same room again.
107
+ */
108
+ export declare const DEPTH_SWING_WIDTH = 3;
81
109
  /**
82
110
  * One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
83
- * four values into `out`.
111
+ * four values into `out` — and, where the frame carries records, the pixel's next one into `record`.
84
112
  */
85
- export declare function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: Float32Array): void;
113
+ export declare function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: Float32Array, record?: Float32Array, swingOut?: Float32Array): void;
86
114
  /**
87
115
  * The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
88
- * `shown`, three — the history sharpened against the four direct neighbours it has just been given.
116
+ * `shown`, three — the history sharpened against the four direct neighbours it has just been given —
117
+ * and, where the frame carries records, the next ones into `records`, four floats a pixel, and the
118
+ * next depth swings into `swings`, one.
89
119
  */
90
- export declare function resolveFrame(frame: ResolveFrame, history: Float32Array, shown: Float32Array): void;
120
+ export declare function resolveFrame(frame: ResolveFrame, history: Float32Array, shown: Float32Array, records?: Float32Array, swings?: Float32Array): void;