@driftengine/core 4.9.0 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/CHANGELOG.json +133 -11
  2. package/README.md +43 -38
  3. package/dist/index.d.ts +7 -4
  4. package/dist/index.js +5 -4
  5. package/dist/render/ambientHarmonics.d.ts +1 -1
  6. package/dist/render/ambientHarmonics.js +1 -1
  7. package/dist/render/areaLights.d.ts +4 -4
  8. package/dist/render/areaLights.js +1 -1
  9. package/dist/render/backend/api.d.ts +10 -3
  10. package/dist/render/backend/select.js +11 -5
  11. package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
  12. package/dist/render/backend/webgl2/boneAnimations.js +80 -0
  13. package/dist/render/backend/webgl2/instanced.d.ts +5 -1
  14. package/dist/render/backend/webgl2/instanced.js +13 -1
  15. package/dist/render/backend/webgl2/renderer.d.ts +189 -4
  16. package/dist/render/backend/webgl2/renderer.js +614 -66
  17. package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
  18. package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
  19. package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
  20. package/dist/render/backend/webgpu/boneAnimations.js +76 -0
  21. package/dist/render/backend/webgpu/buffers.d.ts +24 -4
  22. package/dist/render/backend/webgpu/buffers.js +116 -59
  23. package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
  24. package/dist/render/backend/webgpu/clothKernels.js +76 -0
  25. package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
  26. package/dist/render/backend/webgpu/clothOps.js +78 -0
  27. package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
  28. package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
  29. package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
  30. package/dist/render/backend/webgpu/clothSetPack.js +144 -0
  31. package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
  32. package/dist/render/backend/webgpu/compressedUpload.js +53 -8
  33. package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
  34. package/dist/render/backend/webgpu/depthPass.js +49 -14
  35. package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
  36. package/dist/render/backend/webgpu/drawCommand.js +11 -3
  37. package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
  38. package/dist/render/backend/webgpu/flatPass.js +137 -20
  39. package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
  40. package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
  41. package/dist/render/backend/webgpu/instanced.d.ts +14 -1
  42. package/dist/render/backend/webgpu/instanced.js +24 -1
  43. package/dist/render/backend/webgpu/latePass.js +1 -0
  44. package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
  45. package/dist/render/backend/webgpu/materialStore.js +265 -0
  46. package/dist/render/backend/webgpu/motionPass.js +15 -6
  47. package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
  48. package/dist/render/backend/webgpu/pipelineCache.js +46 -1
  49. package/dist/render/backend/webgpu/postPass.d.ts +1 -1
  50. package/dist/render/backend/webgpu/renderer.d.ts +200 -35
  51. package/dist/render/backend/webgpu/renderer.js +993 -168
  52. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
  53. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
  54. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
  55. package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
  56. package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
  57. package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
  58. package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
  59. package/dist/render/backend/webgpu/staticBundles.js +242 -0
  60. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
  61. package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
  62. package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
  63. package/dist/render/backend/webgpu/uniformRing.js +9 -1
  64. package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
  65. package/dist/render/backend/webgpu/vertexFormats.js +97 -0
  66. package/dist/render/bloomChain.d.ts +3 -3
  67. package/dist/render/boneAnimation.d.ts +92 -0
  68. package/dist/render/boneAnimation.js +133 -0
  69. package/dist/render/compressedSource.d.ts +33 -13
  70. package/dist/render/compressedSource.js +70 -25
  71. package/dist/render/glCompressed.js +83 -0
  72. package/dist/render/globalMediumPass.d.ts +2 -0
  73. package/dist/render/globalMediumPass.js +1 -0
  74. package/dist/render/indexWidth.d.ts +17 -0
  75. package/dist/render/indexWidth.js +23 -0
  76. package/dist/render/instances.d.ts +26 -0
  77. package/dist/render/lightBudget.d.ts +10 -0
  78. package/dist/render/lightBudget.js +10 -0
  79. package/dist/render/lightMatrix.d.ts +11 -0
  80. package/dist/render/lightMatrix.js +14 -0
  81. package/dist/render/lightmap.d.ts +1 -1
  82. package/dist/render/mesh.d.ts +14 -0
  83. package/dist/render/mesh.js +44 -14
  84. package/dist/render/pointLightSelection.d.ts +4 -4
  85. package/dist/render/renderQuality.d.ts +30 -0
  86. package/dist/render/renderQuality.js +5 -0
  87. package/dist/render/rendererHarness.d.ts +7 -0
  88. package/dist/render/rendererHarness.js +12 -0
  89. package/dist/render/screenSpaceReflection.d.ts +2 -1
  90. package/dist/render/shader.d.ts +15 -0
  91. package/dist/render/shader.js +41 -0
  92. package/dist/render/shaders/boneAnimation.d.ts +19 -0
  93. package/dist/render/shaders/boneAnimation.js +63 -0
  94. package/dist/render/shaders/depth.d.ts +7 -0
  95. package/dist/render/shaders/depth.js +35 -10
  96. package/dist/render/shaders/flat/directionalShadow.js +84 -10
  97. package/dist/render/shaders/flat/glassTint.d.ts +1 -1
  98. package/dist/render/shaders/flat/glassTint.js +3 -0
  99. package/dist/render/shaders/flat/index.d.ts +21 -0
  100. package/dist/render/shaders/flat/index.js +42 -7
  101. package/dist/render/shaders/flat/layered.d.ts +19 -0
  102. package/dist/render/shaders/flat/layered.js +115 -0
  103. package/dist/render/shaders/flat/main.d.ts +1 -1
  104. package/dist/render/shaders/flat/main.js +58 -21
  105. package/dist/render/shaders/flat/mapReads.d.ts +10 -0
  106. package/dist/render/shaders/flat/mapReads.js +38 -0
  107. package/dist/render/shaders/flat/models.js +1 -1
  108. package/dist/render/shaders/flat/movingSun.d.ts +21 -0
  109. package/dist/render/shaders/flat/movingSun.js +46 -0
  110. package/dist/render/shaders/flat/normalMap.d.ts +6 -2
  111. package/dist/render/shaders/flat/normalMap.js +14 -3
  112. package/dist/render/shaders/flat/overlay.d.ts +3 -2
  113. package/dist/render/shaders/flat/overlay.js +42 -20
  114. package/dist/render/shaders/flat/pointShadow.js +2 -2
  115. package/dist/render/shaders/flat/preamble.js +59 -27
  116. package/dist/render/shaders/flat/worldUv.d.ts +22 -0
  117. package/dist/render/shaders/flat/worldUv.js +113 -0
  118. package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
  119. package/dist/render/shaders/generated/depth.wgsl.js +93 -0
  120. package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
  121. package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
  122. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
  123. package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
  124. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
  125. package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
  126. package/dist/render/shaders/globalMedium.js +9 -1
  127. package/dist/render/shaders/lightVolume.js +9 -1
  128. package/dist/render/shaders/outputTransform.d.ts +7 -0
  129. package/dist/render/shaders/outputTransform.js +28 -14
  130. package/dist/render/shaders/tangentFrame.d.ts +1 -1
  131. package/dist/render/shaders/tangentFrame.js +16 -3
  132. package/dist/render/shaders/vertexChannel.d.ts +7 -0
  133. package/dist/render/shaders/vertexChannel.js +26 -0
  134. package/dist/render/skinnedClothRun.d.ts +28 -0
  135. package/dist/render/skinnedClothRun.js +103 -35
  136. package/dist/render/staticDraws.d.ts +47 -0
  137. package/dist/render/staticDraws.js +43 -0
  138. package/dist/render/surfaceLayers.d.ts +39 -0
  139. package/dist/render/surfaceLayers.js +26 -0
  140. package/dist/render/surfaceOverlay.d.ts +16 -3
  141. package/dist/render/surfaceOverlay.js +11 -6
  142. package/dist/render/surfaceProjection.d.ts +34 -0
  143. package/dist/render/surfaceProjection.js +30 -0
  144. package/dist/render/surfaceTexture.d.ts +68 -7
  145. package/dist/render/surfaceTexture.js +56 -17
  146. package/dist/render/vertexPacking.d.ts +48 -0
  147. package/dist/render/vertexPacking.js +68 -0
  148. package/package.json +6 -6
  149. package/src/index.ts +12 -4
  150. package/src/render/ambientHarmonics.ts +1 -1
  151. package/src/render/areaLights.ts +4 -4
  152. package/src/render/backend/api.ts +13 -3
  153. package/src/render/backend/select.ts +10 -5
  154. package/src/render/backend/webgl2/boneAnimations.ts +105 -0
  155. package/src/render/backend/webgl2/instanced.ts +20 -1
  156. package/src/render/backend/webgl2/renderer.ts +723 -79
  157. package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
  158. package/src/render/backend/webgpu/boneAnimations.ts +97 -0
  159. package/src/render/backend/webgpu/buffers.ts +195 -70
  160. package/src/render/backend/webgpu/clothKernels.ts +86 -0
  161. package/src/render/backend/webgpu/clothOps.ts +86 -0
  162. package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
  163. package/src/render/backend/webgpu/clothSetPack.ts +197 -0
  164. package/src/render/backend/webgpu/compressedUpload.ts +59 -9
  165. package/src/render/backend/webgpu/depthPass.ts +51 -10
  166. package/src/render/backend/webgpu/drawCommand.ts +25 -4
  167. package/src/render/backend/webgpu/flatPass.ts +212 -41
  168. package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
  169. package/src/render/backend/webgpu/instanced.ts +32 -1
  170. package/src/render/backend/webgpu/latePass.ts +1 -0
  171. package/src/render/backend/webgpu/materialStore.ts +276 -0
  172. package/src/render/backend/webgpu/motionPass.ts +18 -7
  173. package/src/render/backend/webgpu/pipelineCache.ts +53 -7
  174. package/src/render/backend/webgpu/renderer.ts +1201 -202
  175. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
  176. package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
  177. package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
  178. package/src/render/backend/webgpu/staticBundles.ts +364 -0
  179. package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
  180. package/src/render/backend/webgpu/uniformRing.ts +10 -1
  181. package/src/render/backend/webgpu/vertexFormats.ts +113 -0
  182. package/src/render/bloomChain.ts +3 -3
  183. package/src/render/boneAnimation.ts +223 -0
  184. package/src/render/compressedSource.ts +121 -29
  185. package/src/render/glCompressed.ts +83 -0
  186. package/src/render/globalMediumPass.ts +7 -0
  187. package/src/render/indexWidth.ts +27 -0
  188. package/src/render/instances.ts +27 -0
  189. package/src/render/lightBudget.ts +11 -0
  190. package/src/render/lightMatrix.ts +15 -0
  191. package/src/render/lightmap.ts +1 -1
  192. package/src/render/mesh.ts +72 -14
  193. package/src/render/pointLightSelection.ts +4 -4
  194. package/src/render/renderQuality.ts +38 -0
  195. package/src/render/rendererHarness.ts +15 -0
  196. package/src/render/screenSpaceReflection.ts +2 -1
  197. package/src/render/shader.ts +59 -0
  198. package/src/render/shaders/boneAnimation.ts +63 -0
  199. package/src/render/shaders/depth.ts +45 -10
  200. package/src/render/shaders/flat/directionalShadow.ts +84 -10
  201. package/src/render/shaders/flat/glassTint.ts +7 -1
  202. package/src/render/shaders/flat/index.ts +63 -7
  203. package/src/render/shaders/flat/layered.ts +115 -0
  204. package/src/render/shaders/flat/main.ts +58 -21
  205. package/src/render/shaders/flat/mapReads.ts +38 -0
  206. package/src/render/shaders/flat/models.ts +1 -1
  207. package/src/render/shaders/flat/movingSun.ts +46 -0
  208. package/src/render/shaders/flat/normalMap.ts +14 -3
  209. package/src/render/shaders/flat/overlay.ts +42 -20
  210. package/src/render/shaders/flat/pointShadow.ts +2 -2
  211. package/src/render/shaders/flat/preamble.ts +59 -27
  212. package/src/render/shaders/flat/worldUv.ts +113 -0
  213. package/src/render/shaders/generated/depth.wgsl.ts +95 -0
  214. package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
  215. package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
  216. package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
  217. package/src/render/shaders/globalMedium.ts +9 -1
  218. package/src/render/shaders/lightVolume.ts +9 -1
  219. package/src/render/shaders/outputTransform.ts +30 -14
  220. package/src/render/shaders/tangentFrame.ts +16 -3
  221. package/src/render/shaders/vertexChannel.ts +27 -0
  222. package/src/render/skinnedClothRun.ts +138 -38
  223. package/src/render/staticDraws.ts +95 -0
  224. package/src/render/surfaceLayers.ts +57 -0
  225. package/src/render/surfaceOverlay.ts +24 -7
  226. package/src/render/surfaceProjection.ts +57 -0
  227. package/src/render/surfaceTexture.ts +124 -27
  228. package/src/render/vertexPacking.ts +109 -0
  229. package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
  230. package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
  231. package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
@@ -8,9 +8,16 @@
8
8
  * array samples exactly as a 2D texture does. A mesh names its layer per vertex (`MeshData.layers`),
9
9
  * so a merged block wearing forty facades is one draw.
10
10
  */
11
+ import type { SurfaceLayers } from './surfaceLayers.ts';
12
+ import type { SurfaceProjection } from './surfaceProjection.ts';
11
13
  import type { SurfaceModel } from './surfaceModel.ts';
12
14
  import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.ts';
13
- import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSource.ts';
15
+ import {
16
+ compressedLayers,
17
+ isCompressedSource,
18
+ planBlocks,
19
+ refuseBlockUpdate,
20
+ } from './compressedSource.ts';
14
21
  import type {
15
22
  BlockFormat,
16
23
  CompressedTextureFormat,
@@ -119,7 +126,7 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
119
126
  * which is every light's default too, so a scene that names no channel is lit as it always was.
120
127
  *
121
128
  * A point or spot light shades this surface only where its `PointLightSet.lightChannels` and this
122
- * share a bit — Unreal's lighting channels. A character's own key and rim lights on channel 2,
129
+ * share a bit — lighting channels. A character's own key and rim lights on channel 2,
123
130
  * and the character's materials on 3, light the character and leave the floor around it as the
124
131
  * stage's lights alone have it. **What it does not reach**: the sun, the sky and the probes, area
125
132
  * lights, DriftLight's volume and the GPU-driven pipeline light every surface whatever its mask.
@@ -153,6 +160,22 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
153
160
  * which is the opposite of what the caller asked for.
154
161
  */
155
162
  occlusionStrength?: number;
163
+ /**
164
+ * How much of the environment this material's draws mirror, 0 to 1: the number
165
+ * `setSurfaceReflectivity` sets for every draw, stated by the material for its own. Absent, its
166
+ * draws wear the setter's, as every material's did before this existed.
167
+ *
168
+ * **Here so a static list can carry it.** A list keeps each entry's material and none of the
169
+ * renderer's state between draws (`createStaticDraws`), so a stage whose batches each mirror their
170
+ * own share of a baked reflection says so in the material it records. What it gives up is nothing a
171
+ * setter could do: one called after this material is set is ignored for the material's draws.
172
+ */
173
+ reflectivity?: number;
174
+ /**
175
+ * How bright the environment this material's draws reflect is: `setEnvironmentGain`'s number,
176
+ * stated by the material for its own, and absent the setter's. Held non-negative.
177
+ */
178
+ environmentGain?: number;
156
179
  /**
157
180
  * Scales the emissive map, per channel. Default 1.
158
181
  *
@@ -176,14 +199,14 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
176
199
  modelMap?: Texture | null;
177
200
  /**
178
201
  * Whether a lamp's and the sun's highlight on this surface is GGX's own, `π · D · Vis · F · N·L`,
179
- * as Unreal and this engine's skin and eye shade it, rather than the engine's lobe scaled to a
180
- * peak of one. **False by default**, every surface as it was.
202
+ * as physically based renderers and this engine's skin and eye shade it, rather than the engine's
203
+ * lobe scaled to a peak of one. **False by default**, every surface as it was.
181
204
  *
182
205
  * The peak-normalised lobe is a look control: the specular attribute says how strong a highlight
183
206
  * is and roughness how wide, apart. Physical, they are entangled as they are in a real surface —
184
207
  * the peak is `1 / (4 α²)` of the light head-on, so a polished surface's highlight is many times
185
208
  * the look's and a rough one's lower and broader — and the specular attribute is read as the
186
- * reflectance at normal incidence, F0: Unreal's `0.08 × Specular`, 0.04 at its default. Fresnel
209
+ * reflectance at normal incidence, F0: `0.08 × Specular`, 0.04 at its default. Fresnel
187
210
  * then brightens every surface toward grazing, not only a metal. A rectangle's highlight is the
188
211
  * lobe integrated over it with the attribute as F0 already, so it is unchanged either way.
189
212
  *
@@ -192,6 +215,34 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
192
215
  * and an 8-bit frame clips the brighter peak without `hdrScene`.
193
216
  */
194
217
  physicalSpecular?: boolean;
218
+ /**
219
+ * Texture coordinates from where a point is in the world rather than from the mesh: `'planar'`
220
+ * across the horizontal axes for ground, `'triplanar'` on three planes for walls and rock, at so
221
+ * many repeats a metre. Absent, the mesh's, as every material's were. **A lit switch**, compiled in
222
+ * the first time a material asks, so a scene that never asks pays nothing. See
223
+ * `surfaceProjection.ts` for what it gives up.
224
+ */
225
+ projection?: SurfaceProjection | null;
226
+ /**
227
+ * How much light from behind a thin surface lets through, 0 to 1: a banner lit from behind, a
228
+ * leaf against the sun, a lampshade. The light falling on the far side — the sun, lamps, area
229
+ * lights, DriftLight — reaches the eye through the surface, coloured by its own colour; a metal
230
+ * lets none through. 0, the default, is every material as it was. Two-sided or not: a one-sided
231
+ * surface seen from its front shows the light behind it as well.
232
+ *
233
+ * **What it gives up**: a thin surface, not a volume, so a thick one lets through as much as a
234
+ * sheet; the light behind is not blurred by the surface; and none of it on glass, which lets
235
+ * light through by its own rule (`TranslucentMeshOptions.glass`).
236
+ */
237
+ diffuseTransmission?: number;
238
+ /**
239
+ * Up to five layers blended by a mask, each at its own repeat: `albedo`, `normal` and `orm` arrays
240
+ * whose layers are the material's, and a mask laying each over the ones before it. Absent, the
241
+ * material is one layer, as every material was. **A lit switch**, compiled in the first time a
242
+ * material asks. The mask is read where `modelMap` goes, so a material carries one or the other.
243
+ * See `surfaceLayers.ts` for what it gives up.
244
+ */
245
+ layers?: SurfaceLayers<Texture> | null;
195
246
  }
196
247
 
197
248
  export interface SurfaceTextureOptions {
@@ -292,7 +343,9 @@ export class SurfaceTexture {
292
343
  /** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
293
344
  private effectsTable: WebGLTexture | null = null;
294
345
  /** The block format a compressed texture holds, or null for one uploaded from images. */
295
- private readonly blockFormat: BlockFormat | null;
346
+ private blockFormat: BlockFormat | null;
347
+ /** How the texture is sampled, kept so blocks swapped in by `update` are sampled the same way. */
348
+ private readonly sampling: { wrap: number; nearest: boolean; anisotropy: number };
296
349
  /** Level 0's size, which `flatLayer` copies; kept as `update` changes it. */
297
350
  private width = 0;
298
351
  private height = 0;
@@ -404,27 +457,13 @@ export class SurfaceTexture {
404
457
  this.storage = this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA8;
405
458
  uploadImages(gl, sources, this.srgb);
406
459
  }
407
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
408
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
409
- /* Nearest where the caller said their pixels are the subject; see `filter`. The mip chain is
410
- still blended between levels, because that is minification and this option is about
411
- magnification. */
412
- const nearest = options.filter === 'nearest';
413
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
414
- gl.texParameteri(
415
- gl.TEXTURE_2D_ARRAY,
416
- gl.TEXTURE_MIN_FILTER,
417
- this.mipmapped
418
- ? nearest
419
- ? gl.NEAREST_MIPMAP_LINEAR
420
- : gl.LINEAR_MIPMAP_LINEAR
421
- : nearest
422
- ? gl.NEAREST
423
- : gl.LINEAR,
424
- );
425
-
460
+ this.sampling = {
461
+ wrap,
462
+ nearest: options.filter === 'nearest',
463
+ anisotropy: options.anisotropy ?? DEFAULT_ANISOTROPY,
464
+ };
465
+ this.applySampling(gl);
426
466
  if (this.mipmapped && this.blockFormat === null) gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
427
- applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
428
467
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
429
468
 
430
469
  /* Half floats, nearest, clamped: a table read by texelFetch, whose every whole number is under
@@ -475,9 +514,17 @@ export class SurfaceTexture {
475
514
  * Not a hot path: it re-uploads the whole image and regenerates the chain. A caller
476
515
  * doing this every frame at any size wants to know that it costs what it costs.
477
516
  */
478
- update(gl: WebGL2RenderingContext, source: TexImageSource): void {
517
+ update(
518
+ gl: WebGL2RenderingContext,
519
+ source: TexImageSource | CompressedTextureSource,
520
+ compressed: readonly CompressedTextureFormat[] = [],
521
+ ): void {
479
522
  if (this.texture === null) return;
480
523
  refuseArrayUpdate(this.layers);
524
+ if (isCompressedSource(source)) {
525
+ this.swapToBlocks(gl, source, compressed);
526
+ return;
527
+ }
481
528
  refuseBlockUpdate(this.blockFormat);
482
529
  const { width, height } = sourceSize(source);
483
530
  this.width = width;
@@ -501,6 +548,56 @@ export class SurfaceTexture {
501
548
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
502
549
  }
503
550
 
551
+ /**
552
+ * Blocks in place of the image, behind the same handle: **a new GL object** rather than the
553
+ * image's levels respecified, because a chain shorter than the image's would leave the levels
554
+ * past it allocated, and immutable storage could not be respecified at all. Sampled as the image
555
+ * was, and refused before anything changes where the context does not take the format.
556
+ */
557
+ private swapToBlocks(
558
+ gl: WebGL2RenderingContext,
559
+ source: CompressedTextureSource,
560
+ compressed: readonly CompressedTextureFormat[],
561
+ ): void {
562
+ const plan = planBlocks([source], this.srgb, compressed);
563
+ const texture = gl.createTexture();
564
+ if (texture === null) throw new Error('SurfaceTexture: createTexture failed');
565
+ this.dropFlat(gl);
566
+ if (this.texture !== null) gl.deleteTexture(this.texture);
567
+ this.texture = texture;
568
+ this.blockFormat = source.format;
569
+ this.width = source.width;
570
+ this.height = source.height;
571
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
572
+ uploadCompressedArray(gl, plan.name, [source], this.mipmapped ? plan.levels : 1);
573
+ this.applySampling(gl);
574
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
575
+ }
576
+
577
+ /**
578
+ * Wrap, filters and anisotropy on the bound texture. Nearest where the caller said their pixels
579
+ * are the subject; see `filter`. The mip chain is still blended between levels, because that is
580
+ * minification and this option is about magnification.
581
+ */
582
+ private applySampling(gl: WebGL2RenderingContext): void {
583
+ const { wrap, nearest, anisotropy } = this.sampling;
584
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
585
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
586
+ gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, nearest ? gl.NEAREST : gl.LINEAR);
587
+ gl.texParameteri(
588
+ gl.TEXTURE_2D_ARRAY,
589
+ gl.TEXTURE_MIN_FILTER,
590
+ this.mipmapped
591
+ ? nearest
592
+ ? gl.NEAREST_MIPMAP_LINEAR
593
+ : gl.LINEAR_MIPMAP_LINEAR
594
+ : nearest
595
+ ? gl.NEAREST
596
+ : gl.LINEAR,
597
+ );
598
+ applyAnisotropy(gl, anisotropy);
599
+ }
600
+
504
601
  /**
505
602
  * Attach the only layer as the bound framebuffer's colour: a scene capture's target, drawn into
506
603
  * by `captureScene`. Engine-internal, as `bind` is.
@@ -0,0 +1,109 @@
1
+ import type { MeshData } from '@driftengine/drft';
2
+
3
+ /**
4
+ * How each of a mesh's vertex attributes travels to the device, decided once from its data and
5
+ * bound by each backend: as floats, as sixteen-bit fixed point, or as one value every vertex
6
+ * shares.
7
+ *
8
+ * **Only what reads back as it went, or near enough that no picture can tell.** One value every
9
+ * vertex shares is exact, and is a constant rather than a stream: a colour or an emissive naming
10
+ * no difference between vertices costs nothing a vertex, where both were always stored per vertex.
11
+ * A unit direction — a normal, a tangent and its handedness — in sixteen-bit signed fixed point is
12
+ * within 1/32767 a component, a few thousandths of a degree: measured against floats, 12 pixels of
13
+ * 921,600 moved in one published scene, by 2 of 255 on average, and none in the other eight.
14
+ * Colours and skin weights inside [0, 1] in sixteen-bit unsigned fixed point are within 1/65535. A
15
+ * textured vertex with a tangent frame, white and not glowing, goes from 64 bytes to 36 on WebGPU.
16
+ *
17
+ * **What it gives up.** Eight-bit normals would save four bytes more and are not taken: a normal in
18
+ * steps of 1/127 can band a highlight. **Texture coordinates stay floats**, in any range: a
19
+ * coordinate on a tile's far edge rounds past it in sixteen-bit fixed point, by 1/131,070, and a
20
+ * pixel there samples the neighbouring tile of an atlas — measured as 29 pixels moved by 62 of 255
21
+ * in the voxel scene, every one at a face's edge — and a half float, which steps by a 2048th near
22
+ * 1.0, is worse. A dynamic mesh's normals stay floats, because each rewrite would have to be packed
23
+ * on the CPU. What would make this wrong is a value outside its range, which the range check sends
24
+ * to floats rather than clamping, or a shader reading an attribute as something other than the
25
+ * value it carries.
26
+ */
27
+
28
+ /** Floats; sixteen-bit fixed point, signed or not; or one value for every vertex. */
29
+ export type AttributePacking = 'float' | 'snorm16' | 'unorm16' | 'constant';
30
+
31
+ /** The attributes this decides for. Every other one travels as floats. */
32
+ export const PACKED_ATTRIBUTES = [
33
+ 'normals',
34
+ 'tangents',
35
+ 'colors',
36
+ 'emissive',
37
+ 'weights',
38
+ 'weights2',
39
+ ] as const;
40
+
41
+ export type PackedAttribute = (typeof PACKED_ATTRIBUTES)[number];
42
+
43
+ export type VertexPacking = Readonly<Record<PackedAttribute, AttributePacking>>;
44
+
45
+ /** The choice for this mesh. `dynamic` is a mesh whose positions and normals are rewritten. */
46
+ export function vertexPackingOf(data: MeshData, dynamic: boolean): VertexPacking {
47
+ return {
48
+ normals: dynamic ? 'float' : fixedOrFloat(data.normals, -1),
49
+ tangents: fixedOrFloat(data.tangents, -1),
50
+ colors: shared(data.colors, 3) ? 'constant' : fixedOrFloat(data.colors, 0),
51
+ emissive: shared(data.emissive, 1) ? 'constant' : 'float',
52
+ weights: fixedOrFloat(data.weights, 0),
53
+ weights2: fixedOrFloat(data.weights2, 0),
54
+ };
55
+ }
56
+
57
+ /**
58
+ * What 1 is in each fixed point, the values WebGPU's `snorm16` and `unorm16` and WebGL2's
59
+ * normalised `SHORT` and `UNSIGNED_SHORT` divide by. A value is stored rounded to nearest, so 1, 0
60
+ * and -1 come back exactly.
61
+ */
62
+ export const SNORM16_ONE = 32767;
63
+ export const UNORM16_ONE = 65535;
64
+
65
+ /** One value in the fixed point whose 1 is `one`, rounded to nearest: the only place it is. */
66
+ export function toFixed16(value: number, one: number): number {
67
+ return Math.round(value * one);
68
+ }
69
+
70
+ /** An attribute's values in its fixed point, tightly, for a backend reading separate arrays. */
71
+ export function fixedArray(
72
+ values: Float32Array,
73
+ packing: 'snorm16' | 'unorm16',
74
+ ): Int16Array | Uint16Array {
75
+ const out =
76
+ packing === 'snorm16' ? new Int16Array(values.length) : new Uint16Array(values.length);
77
+ const one = packing === 'snorm16' ? SNORM16_ONE : UNORM16_ONE;
78
+ for (let at = 0; at < values.length; at++) out[at] = toFixed16(values[at] as number, one);
79
+ return out;
80
+ }
81
+
82
+ /** How `name` travels under this packing: floats for an attribute it does not decide for. */
83
+ export function packingFor(packing: VertexPacking, name: string): AttributePacking {
84
+ return (packing as Readonly<Record<string, AttributePacking>>)[name] ?? 'float';
85
+ }
86
+
87
+ /** The value every vertex shares, for an attribute packed as `'constant'`. */
88
+ export function sharedValue(values: Float32Array, width: number): number[] {
89
+ return Array.from(values.subarray(0, width));
90
+ }
91
+
92
+ /** Fixed point where every value lies in [low, 1]; floats otherwise, and for a NaN. */
93
+ function fixedOrFloat(values: Float32Array | undefined, low: -1 | 0): AttributePacking {
94
+ if (values === undefined) return 'float';
95
+ for (let at = 0; at < values.length; at++) {
96
+ const value = values[at] as number;
97
+ if (!(value >= low && value <= 1)) return 'float';
98
+ }
99
+ return low < 0 ? 'snorm16' : 'unorm16';
100
+ }
101
+
102
+ /** Whether every vertex holds the first vertex's value. Not for a mesh of none. */
103
+ function shared(values: Float32Array, width: number): boolean {
104
+ if (values.length < width) return false;
105
+ for (let at = width; at < values.length; at++) {
106
+ if (values[at] !== values[at % width]) return false;
107
+ }
108
+ return true;
109
+ }
@@ -1,74 +0,0 @@
1
- import type { SkinnedClothSetup } from '@driftengine/physics';
2
- import { GpuClothParticles } from './clothTextures.ts';
3
- /** One garment's solver on the device, and the particle textures it publishes into. */
4
- export declare class GpuSkinnedCloth {
5
- private readonly device;
6
- /** The particles a bound mesh draws by: see `RendererApi.setCloth`. */
7
- readonly particles: GpuClothParticles;
8
- readonly count: number;
9
- private readonly control;
10
- private readonly packed;
11
- private readonly kernels;
12
- private readonly buffers;
13
- private readonly state;
14
- private readonly frameData;
15
- private readonly stepSlots;
16
- private readonly group;
17
- private readonly outputs;
18
- private readonly frameStaging;
19
- private readonly stepStaging;
20
- private readonly stepFloats;
21
- private readonly stepWords;
22
- /** Each batch's slot, by kind and by whether it opens a substep's iterations. */
23
- private readonly batchSlot;
24
- /** The air the steps drag toward: the set-up's until `setWind`, written into every step. */
25
- private readonly wind;
26
- private readonly groups;
27
- private readonly substeps;
28
- private readonly iterations;
29
- private encoder;
30
- private pass;
31
- private slot;
32
- /** Where the pose a step starts from and the one it moves toward sit in `pose`, in floats. */
33
- private fromAt;
34
- private toAt;
35
- constructor(device: GPUDevice, setup: SkinnedClothSetup);
36
- /**
37
- * One frame: the rig's pose, `dt` of the caller's time in whole fixed steps, and the particles at
38
- * the frame's `alpha` published for the draws that follow, as the particles of `frame`.
39
- */
40
- advance(globals: Float32Array, model: Float32Array, dt: number, frame: number): void;
41
- /** The frame's sample of the scene's wind, for the steps to come. See `SkinnedCloth.setWind`. */
42
- setWind(x: number, y: number, z: number): void;
43
- /** Back to the skinned pose, settled; the next `advance` publishes it. */
44
- reset(): void;
45
- /**
46
- * The particles where the last step left them, read back: for the parity check, which compares
47
- * them with the CPU solver's. Not for a frame — it waits on the device.
48
- */
49
- read(): Promise<Float32Array>;
50
- /** Its buffers; the particle textures are the renderer's to retire with the frame. */
51
- dispose(): void;
52
- private targetsChanged;
53
- private rest;
54
- private step;
55
- /** Every batch of one kind, in colour order, each a dispatch over its range. */
56
- private batches;
57
- private dispatch;
58
- private passFor;
59
- /** Send what has been recorded: every slot it reads was written ahead of it on the queue. */
60
- private flush;
61
- /**
62
- * A step's slot: its fraction, blend and alpha, the poses it reads, and — where `targets` is
63
- * given — the share of the character's motion the cloth is carried by, as `carryParticles` takes
64
- * it. Returns the slot's byte offset.
65
- */
66
- private takeSlot;
67
- /** `carryParticles`' rotation, origin and move, into the step's slot; carry 0 where none. */
68
- private writeCarry;
69
- /** The pose's skin matrices — or the model, for a cloth with no rig — and its colliders. */
70
- private writeFrameData;
71
- private constants;
72
- /** Each batch's range, twice — opening a substep's iterations and not — at a slot each. */
73
- private writeBatches;
74
- }