@driftengine/core 4.8.3 → 4.8.5

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 (180) hide show
  1. package/CHANGELOG.json +82 -0
  2. package/README.md +1 -1
  3. package/dist/build/shaderComments.js +0 -0
  4. package/dist/geometry/meshConcat.js +2 -0
  5. package/dist/index.d.ts +10 -4
  6. package/dist/index.js +16 -1
  7. package/dist/math/scalar.js +1 -1
  8. package/dist/render/anisotropicLobe.d.ts +7 -0
  9. package/dist/render/anisotropicLobe.js +33 -0
  10. package/dist/render/backend/api.d.ts +41 -4
  11. package/dist/render/backend/select.js +8 -0
  12. package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
  13. package/dist/render/backend/webgl2/clothTextures.js +93 -0
  14. package/dist/render/backend/webgl2/renderer.d.ts +136 -3
  15. package/dist/render/backend/webgl2/renderer.js +604 -63
  16. package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
  17. package/dist/render/backend/webgl2/skinScatter.js +256 -0
  18. package/dist/render/backend/webgpu/buffers.d.ts +25 -11
  19. package/dist/render/backend/webgpu/buffers.js +98 -49
  20. package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
  21. package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
  22. package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
  23. package/dist/render/backend/webgpu/clothTextures.js +145 -0
  24. package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
  25. package/dist/render/backend/webgpu/compressedUpload.js +38 -0
  26. package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
  27. package/dist/render/backend/webgpu/computeHost.js +5 -0
  28. package/dist/render/backend/webgpu/depthPass.d.ts +16 -6
  29. package/dist/render/backend/webgpu/depthPass.js +39 -9
  30. package/dist/render/backend/webgpu/drawCommand.js +6 -2
  31. package/dist/render/backend/webgpu/flatPass.d.ts +38 -4
  32. package/dist/render/backend/webgpu/flatPass.js +109 -12
  33. package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
  34. package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
  35. package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
  36. package/dist/render/backend/webgpu/motionPass.js +106 -9
  37. package/dist/render/backend/webgpu/postPass.d.ts +3 -1
  38. package/dist/render/backend/webgpu/postPass.js +6 -3
  39. package/dist/render/backend/webgpu/renderer.d.ts +113 -5
  40. package/dist/render/backend/webgpu/renderer.js +498 -48
  41. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
  42. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
  43. package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
  44. package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
  45. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
  46. package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
  47. package/dist/render/clothBindingData.d.ts +41 -0
  48. package/dist/render/clothBindingData.js +72 -0
  49. package/dist/render/clothPlacement.d.ts +25 -0
  50. package/dist/render/clothPlacement.js +85 -0
  51. package/dist/render/compressedSource.d.ts +66 -0
  52. package/dist/render/compressedSource.js +138 -0
  53. package/dist/render/cutoutCaster.d.ts +2 -0
  54. package/dist/render/cutoutCaster.js +2 -0
  55. package/dist/render/cutoutDither.d.ts +51 -0
  56. package/dist/render/cutoutDither.js +90 -0
  57. package/dist/render/eyeRefraction.d.ts +35 -0
  58. package/dist/render/eyeRefraction.js +40 -0
  59. package/dist/render/glCompressed.d.ts +27 -0
  60. package/dist/render/glCompressed.js +53 -0
  61. package/dist/render/hairLobes.d.ts +70 -0
  62. package/dist/render/hairLobes.js +120 -0
  63. package/dist/render/lightBudget.d.ts +22 -0
  64. package/dist/render/lightBudget.js +22 -0
  65. package/dist/render/mesh.d.ts +7 -0
  66. package/dist/render/mesh.js +24 -0
  67. package/dist/render/recon/motionVectors.d.ts +4 -2
  68. package/dist/render/recon/motionVectors.js +6 -3
  69. package/dist/render/renderQuality.d.ts +20 -0
  70. package/dist/render/renderQuality.js +5 -0
  71. package/dist/render/rendererHarness.js +4 -0
  72. package/dist/render/sceneTarget.d.ts +12 -0
  73. package/dist/render/sceneTarget.js +14 -0
  74. package/dist/render/shaders/clothBinding.d.ts +22 -0
  75. package/dist/render/shaders/clothBinding.js +80 -0
  76. package/dist/render/shaders/depth.js +42 -6
  77. package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
  78. package/dist/render/shaders/flat/anisotropic.js +68 -0
  79. package/dist/render/shaders/flat/eye.d.ts +1 -0
  80. package/dist/render/shaders/flat/eye.js +81 -0
  81. package/dist/render/shaders/flat/hair.d.ts +1 -0
  82. package/dist/render/shaders/flat/hair.js +101 -0
  83. package/dist/render/shaders/flat/index.js +36 -2
  84. package/dist/render/shaders/flat/main.d.ts +1 -1
  85. package/dist/render/shaders/flat/main.js +113 -17
  86. package/dist/render/shaders/flat/models.d.ts +14 -0
  87. package/dist/render/shaders/flat/models.js +273 -0
  88. package/dist/render/shaders/flat/preamble.js +26 -24
  89. package/dist/render/shaders/flat/skin.d.ts +1 -0
  90. package/dist/render/shaders/flat/skin.js +114 -0
  91. package/dist/render/shaders/generated/depth.wgsl.d.ts +38 -16
  92. package/dist/render/shaders/generated/depth.wgsl.js +38 -16
  93. package/dist/render/shaders/generated/flat.wgsl.d.ts +1790 -1704
  94. package/dist/render/shaders/generated/flat.wgsl.js +1828 -1742
  95. package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
  96. package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
  97. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  98. package/dist/render/shaders/recon/motion.wgsl.js +102 -0
  99. package/dist/render/shaders/skinBlur.d.ts +1 -0
  100. package/dist/render/shaders/skinBlur.js +103 -0
  101. package/dist/render/shaders/skinning.d.ts +3 -1
  102. package/dist/render/shaders/skinning.js +31 -4
  103. package/dist/render/shadowCasters.d.ts +13 -1
  104. package/dist/render/skinBlur.d.ts +41 -0
  105. package/dist/render/skinBlur.js +49 -0
  106. package/dist/render/skinScatter.d.ts +67 -0
  107. package/dist/render/skinScatter.js +80 -0
  108. package/dist/render/skinnedClothRun.d.ts +26 -0
  109. package/dist/render/skinnedClothRun.js +84 -0
  110. package/dist/render/surfaceModel.d.ts +129 -0
  111. package/dist/render/surfaceModel.js +152 -0
  112. package/dist/render/surfaceTexture.d.ts +44 -4
  113. package/dist/render/surfaceTexture.js +60 -46
  114. package/dist/render/vertexDefaults.js +7 -0
  115. package/package.json +6 -6
  116. package/src/build/shaderComments.ts +0 -0
  117. package/src/geometry/meshConcat.ts +2 -0
  118. package/src/index.ts +40 -1
  119. package/src/math/scalar.ts +1 -1
  120. package/src/render/anisotropicLobe.ts +41 -0
  121. package/src/render/backend/api.ts +52 -3
  122. package/src/render/backend/select.ts +8 -0
  123. package/src/render/backend/webgl2/clothTextures.ts +120 -0
  124. package/src/render/backend/webgl2/renderer.ts +693 -64
  125. package/src/render/backend/webgl2/skinScatter.ts +309 -0
  126. package/src/render/backend/webgpu/buffers.ts +137 -63
  127. package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
  128. package/src/render/backend/webgpu/clothTextures.ts +194 -0
  129. package/src/render/backend/webgpu/compressedUpload.ts +65 -0
  130. package/src/render/backend/webgpu/computeHost.ts +25 -0
  131. package/src/render/backend/webgpu/depthPass.ts +39 -7
  132. package/src/render/backend/webgpu/drawCommand.ts +6 -2
  133. package/src/render/backend/webgpu/flatPass.ts +145 -7
  134. package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
  135. package/src/render/backend/webgpu/motionPass.ts +128 -9
  136. package/src/render/backend/webgpu/postPass.ts +5 -1
  137. package/src/render/backend/webgpu/renderer.ts +663 -43
  138. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
  139. package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
  140. package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
  141. package/src/render/clothBindingData.ts +89 -0
  142. package/src/render/clothPlacement.ts +114 -0
  143. package/src/render/compressedSource.ts +198 -0
  144. package/src/render/cutoutCaster.ts +5 -0
  145. package/src/render/cutoutDither.ts +99 -0
  146. package/src/render/eyeRefraction.ts +72 -0
  147. package/src/render/glCompressed.ts +86 -0
  148. package/src/render/hairLobes.ts +188 -0
  149. package/src/render/lightBudget.ts +25 -0
  150. package/src/render/mesh.ts +23 -0
  151. package/src/render/recon/motionVectors.ts +6 -3
  152. package/src/render/renderQuality.ts +28 -0
  153. package/src/render/rendererHarness.ts +4 -0
  154. package/src/render/sceneTarget.ts +21 -0
  155. package/src/render/shaders/clothBinding.ts +81 -0
  156. package/src/render/shaders/depth.ts +42 -6
  157. package/src/render/shaders/flat/anisotropic.ts +68 -0
  158. package/src/render/shaders/flat/eye.ts +82 -0
  159. package/src/render/shaders/flat/hair.ts +102 -0
  160. package/src/render/shaders/flat/index.ts +36 -2
  161. package/src/render/shaders/flat/main.ts +113 -17
  162. package/src/render/shaders/flat/models.ts +286 -0
  163. package/src/render/shaders/flat/preamble.ts +26 -24
  164. package/src/render/shaders/flat/skin.ts +116 -0
  165. package/src/render/shaders/generated/depth.wgsl.ts +38 -16
  166. package/src/render/shaders/generated/flat.wgsl.ts +1828 -1742
  167. package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
  168. package/src/render/shaders/recon/motion.wgsl.ts +102 -0
  169. package/src/render/shaders/skinBlur.ts +105 -0
  170. package/src/render/shaders/skinning.ts +31 -4
  171. package/src/render/shadowCasters.ts +13 -0
  172. package/src/render/skinBlur.ts +53 -0
  173. package/src/render/skinScatter.ts +92 -0
  174. package/src/render/skinnedClothRun.ts +128 -0
  175. package/src/render/surfaceModel.ts +274 -0
  176. package/src/render/surfaceTexture.ts +123 -63
  177. package/src/render/vertexDefaults.ts +7 -0
  178. package/dist/math/exact.d.ts +0 -76
  179. package/dist/math/exact.js +0 -327
  180. package/src/math/exact.ts +0 -340
@@ -1,14 +1,6 @@
1
- /**
2
- * A GPU image the caller supplies, sampled by the flat shader as surface colour — or several, as
3
- * the layers of one array.
4
- *
5
- * **Every surface texture is an array; a plain image is an array of one.** The lit pass has three of
6
- * WebGL2's guaranteed sixteen texture units free, so a second set of array samplers beside the 2D
7
- * ones was never affordable. Making the one set of samplers arrays costs no unit, and a one-layer
8
- * array samples exactly as a 2D texture does. A mesh names its layer per vertex (`MeshData.layers`),
9
- * so a merged block wearing forty facades is one draw.
10
- */
11
- import { isSourceList, layerSize, refuseArrayUpdate, sourceSize } from './textureSource.js';
1
+ import { layerSize, refuseArrayUpdate, sourceSize } from './textureSource.js';
2
+ import { compressedLayers, planBlocks, refuseBlockUpdate } from './compressedSource.js';
3
+ import { uploadCompressedArray } from './glCompressed.js';
12
4
  import { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './surfaceEffects.js';
13
5
  const DEFAULT_ANISOTROPY = 4;
14
6
  /**
@@ -29,48 +21,39 @@ export class SurfaceTexture {
29
21
  layers;
30
22
  /** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
31
23
  effectsTable = null;
24
+ /** The block format a compressed texture holds, or null for one uploaded from images. */
25
+ blockFormat;
32
26
  /**
33
27
  * One image, or an array of images that must share one size (`layerSize` refuses otherwise).
34
- * Uploaded unflipped either way; see the constructor body for why.
28
+ * Uploaded unflipped either way; see the constructor body for why. Or blocks, uploaded as they are
29
+ * where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
35
30
  */
36
- constructor(gl, source, options = {}) {
37
- const sources = isSourceList(source) ? source : [source];
38
- const { width, height } = layerSize(sources);
39
- this.layers = sources.length;
31
+ constructor(gl, source, options = {}, compressed = []) {
32
+ const listed = (Array.isArray(source) ? source : [source]);
33
+ const blocks = compressedLayers(listed);
34
+ this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
35
+ /* Refused before anything is allocated, so a refusal leaves no texture behind. */
36
+ const plan = blocks === null ? null : planBlocks(blocks, this.srgb, compressed);
40
37
  const texture = gl.createTexture();
41
38
  if (texture === null)
42
39
  throw new Error('SurfaceTexture: createTexture failed');
43
40
  this.texture = texture;
44
- this.mipmapped = options.mipmap ?? true;
45
- this.srgb = (options.colorSpace ?? 'linear') === 'srgb';
46
41
  const wrap = (options.wrap ?? 'repeat') === 'repeat' ? gl.REPEAT : gl.CLAMP_TO_EDGE;
47
42
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);
48
- /*
49
- * **Not flipped, and this used to be, which is the bug it is written down for.**
50
- *
51
- * The flip was here with a comment saying every source is a 2D drawing surface whose Y
52
- * runs down from the top-left, so without it "every texture arrives mirrored". That is
53
- * true of a canvas and **WebGL ignores `UNPACK_FLIP_Y_WEBGL` entirely for an
54
- * `ImageBitmap`**, which carries its own orientation. So the two source types the same
55
- * method accepts arrived the opposite way up from each other, and nothing caught it
56
- * because every canvas-sourced texture in this repository was symmetric under a vertical
57
- * flip: eroded noise, a radial halo, a sleeve blurred to 24 pixels and back. One consumer
58
- * painted lettering to a canvas beside a sleeve decoded as a bitmap and the text was
59
- * upside down.
60
- *
61
- * **Made to agree by dropping the flip rather than by extending it**, and the direction
62
- * is a deliberate choice about blast radius rather than a coin toss: `drftLoader` builds
63
- * every model texture from an `ImageBitmap`, so flipping bitmaps to match canvases would
64
- * turn over every painted surface on every loaded model. Dropping it changes only
65
- * canvas-sourced textures, and every one of those that exists today is symmetric.
66
- *
67
- * A caller that was mirroring its own canvas to cancel this must stop.
68
- */
69
- /* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
70
- new size, as texImage2D always could. Each layer is then written in place. */
71
- gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, this.srgb ? gl.SRGB8_ALPHA8 : gl.RGBA, width, height, sources.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
72
- for (let layer = 0; layer < sources.length; layer++) {
73
- gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, width, height, 1, gl.RGBA, gl.UNSIGNED_BYTE, sources[layer]);
43
+ if (blocks !== null && plan !== null) {
44
+ /* The stored chain, or level 0 alone where no chain was asked for. Nothing is generated:
45
+ `generateMipmap` cannot write a compressed format. */
46
+ this.layers = blocks.length;
47
+ this.blockFormat = blocks[0].format;
48
+ this.mipmapped = (options.mipmap ?? true) && plan.levels > 1;
49
+ uploadCompressedArray(gl, plan.name, blocks, this.mipmapped ? plan.levels : 1);
50
+ }
51
+ else {
52
+ const sources = listed;
53
+ this.layers = sources.length;
54
+ this.blockFormat = null;
55
+ this.mipmapped = options.mipmap ?? true;
56
+ uploadImages(gl, sources, this.srgb);
74
57
  }
75
58
  gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, wrap);
76
59
  gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, wrap);
@@ -86,7 +69,7 @@ export class SurfaceTexture {
86
69
  : nearest
87
70
  ? gl.NEAREST
88
71
  : gl.LINEAR);
89
- if (this.mipmapped)
72
+ if (this.mipmapped && this.blockFormat === null)
90
73
  gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
91
74
  applyAnisotropy(gl, options.anisotropy ?? DEFAULT_ANISOTROPY);
92
75
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
@@ -107,7 +90,6 @@ export class SurfaceTexture {
107
90
  this.effectsTable = table;
108
91
  }
109
92
  }
110
- /** Bind the effects table to `unit`, or `fallback` for a texture given none. */
111
93
  /** Whether this texture carries an effects table, which the lit program reads only once one exists. */
112
94
  get hasEffects() {
113
95
  return this.effectsTable !== null;
@@ -131,6 +113,7 @@ export class SurfaceTexture {
131
113
  if (this.texture === null)
132
114
  return;
133
115
  refuseArrayUpdate(this.layers);
116
+ refuseBlockUpdate(this.blockFormat);
134
117
  const { width, height } = sourceSize(source);
135
118
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.texture);
136
119
  /* Unflipped, matching the constructor. See it for why, and for what it cost. */
@@ -154,6 +137,37 @@ export class SurfaceTexture {
154
137
  this.effectsTable = null;
155
138
  }
156
139
  }
140
+ /** Images into the bound array, level 0 of each layer; the chain, if any, is generated after. */
141
+ function uploadImages(gl, sources, srgb) {
142
+ const { width, height } = layerSize(sources);
143
+ /*
144
+ * **Not flipped, and this used to be, which is the bug it is written down for.**
145
+ *
146
+ * The flip was here with a comment saying every source is a 2D drawing surface whose Y
147
+ * runs down from the top-left, so without it "every texture arrives mirrored". That is
148
+ * true of a canvas and **WebGL ignores `UNPACK_FLIP_Y_WEBGL` entirely for an
149
+ * `ImageBitmap`**, which carries its own orientation. So the two source types the same
150
+ * method accepts arrived the opposite way up from each other, and nothing caught it
151
+ * because every canvas-sourced texture in this repository was symmetric under a vertical
152
+ * flip: eroded noise, a radial halo, a sleeve blurred to 24 pixels and back. One consumer
153
+ * painted lettering to a canvas beside a sleeve decoded as a bitmap and the text was
154
+ * upside down.
155
+ *
156
+ * **Made to agree by dropping the flip rather than by extending it**, and the direction
157
+ * is a deliberate choice about blast radius rather than a coin toss: `drftLoader` builds
158
+ * every model texture from an `ImageBitmap`, so flipping bitmaps to match canvases would
159
+ * turn over every painted surface on every loaded model. Dropping it changes only
160
+ * canvas-sourced textures, and every one of those that exists today is symmetric.
161
+ *
162
+ * A caller that was mirroring its own canvas to cancel this must stop.
163
+ */
164
+ /* Allocated mutable rather than with texStorage3D, so `update` can hand a one-layer texture a
165
+ new size, as texImage2D always could. Each layer is then written in place. */
166
+ gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, srgb ? gl.SRGB8_ALPHA8 : gl.RGBA, width, height, sources.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
167
+ for (let layer = 0; layer < sources.length; layer++) {
168
+ gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, layer, width, height, 1, gl.RGBA, gl.UNSIGNED_BYTE, sources[layer]);
169
+ }
170
+ }
157
171
  /**
158
172
  * Set anisotropic filtering when the driver offers it, and stay silent when it does not.
159
173
  *
@@ -65,6 +65,13 @@ export const ABSENT_ATTRIBUTE = {
65
65
  */
66
66
  joints: [0, 0, 0, 0],
67
67
  weights: [1, 0, 0, 0],
68
+ /**
69
+ * The second four influences, absent: all zero, so a four-influence mesh drawn through a pipeline
70
+ * built for eight adds nothing. **Not the first set's trap**: there a zero sum collapses the
71
+ * vertex, here the first set already sums to one and zero is exactly "no more influences".
72
+ */
73
+ joints2: [0, 0, 0, 0],
74
+ weights2: [0, 0, 0, 0],
68
75
  /**
69
76
  * The four-lane channel: planted, fully sunlit, opaque, and a reserved lane.
70
77
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftengine/core",
3
- "version": "4.8.3",
3
+ "version": "4.8.5",
4
4
  "description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -29,8 +29,8 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
- "@driftengine/drft": "4.8.3",
33
- "@driftengine/physics": "4.8.3",
32
+ "@driftengine/drft": "4.8.5",
33
+ "@driftengine/physics": "4.8.5",
34
34
  "gl-matrix": "^3.4.3"
35
35
  },
36
36
  "author": "Drift Technologies",
@@ -67,8 +67,8 @@
67
67
  "prepack": "node ../../scripts/build.mjs @driftengine/core"
68
68
  },
69
69
  "devDependencies": {
70
- "@driftengine/nav": "4.8.3",
71
- "@driftengine/network": "4.8.3",
72
- "@driftengine/texture": "4.8.3"
70
+ "@driftengine/nav": "4.8.5",
71
+ "@driftengine/network": "4.8.5",
72
+ "@driftengine/texture": "4.8.5"
73
73
  }
74
74
  }
Binary file
@@ -28,6 +28,8 @@ const OPTIONAL = [
28
28
  ['channel', 4],
29
29
  ['joints', 4],
30
30
  ['weights', 4],
31
+ ['joints2', 4],
32
+ ['weights2', 4],
31
33
  ['layers', 1],
32
34
  ] as const;
33
35
 
package/src/index.ts CHANGED
@@ -6,7 +6,6 @@ export type { LoopHooks, FrameSource, LoopOptions } from './core/loop.ts';
6
6
  export { StepBudget } from './core/stepBudget.ts';
7
7
  export type { StepBudgetOptions } from './core/stepBudget.ts';
8
8
  export { hashToUnit, mulberry32, pickBySeed, savableMulberry32 } from './core/rng.ts';
9
- export { exactAcos, exactCos, exactExp, exactLog, exactSin } from './math/exact.ts';
10
9
  export type { SavableRandom } from './core/rng.ts';
11
10
  export { TickTrace } from './core/tickTrace.ts';
12
11
  export type { TickTraceLike } from './core/tickTrace.ts';
@@ -467,6 +466,8 @@ export type { ProbeVerdict } from './render/backend/probe.ts';
467
466
  export type {
468
467
  BoltHandle,
469
468
  CausticsHandle,
469
+ ClothBindingHandle,
470
+ ClothParticlesHandle,
470
471
  FlockHandle,
471
472
  IncrementalMeshHandle,
472
473
  LineHandle,
@@ -484,6 +485,27 @@ export type {
484
485
  WaterHandle,
485
486
  WindStreakHandle,
486
487
  } from './render/backend/api.ts';
488
+ export type { ClothBindingData } from './render/clothBindingData.ts';
489
+ /*
490
+ * A material's shading model, for what the standard one is not: brushed metal, hair, skin, an eye.
491
+ * A frozen descriptor made by a factory and set as `SurfaceMaterial.model`; each is a pipeline of
492
+ * its own, compiled the first time a draw asks.
493
+ */
494
+ export { anisotropicModel, eyeModel, hairModel, skinModel } from './render/surfaceModel.ts';
495
+ export type {
496
+ AnisotropicModel,
497
+ EyeModel,
498
+ HairModel,
499
+ SkinModel,
500
+ SurfaceModel,
501
+ SurfaceModelKind,
502
+ } from './render/surfaceModel.ts';
503
+ /*
504
+ * A skinned cloth solved where the renderer can: compute on WebGPU, the CPU under WebGL2. A function
505
+ * rather than a renderer method so only a game that imports it carries the solver.
506
+ */
507
+ export { createSkinnedCloth } from './render/skinnedClothRun.ts';
508
+ export type { SkinnedClothSolver } from './render/skinnedClothRun.ts';
487
509
 
488
510
  /**
489
511
  * What a frame asked of a backend, against the ceilings that backend imposes.
@@ -507,6 +529,7 @@ export type { FrameTimer } from './render/backend/timer.ts';
507
529
  export type {
508
530
  SceneCasterMaterial,
509
531
  SceneCasters,
532
+ ShadowCasterCloth,
510
533
  ShadowCasterSink,
511
534
  ShadowCasters,
512
535
  } from './render/shadowCasters.ts';
@@ -593,6 +616,7 @@ export type {
593
616
  RenderQuality,
594
617
  RenderQualityOptions,
595
618
  ShadowFilterTaps,
619
+ SkinScattering,
596
620
  WaterReflectionFilterTaps,
597
621
  } from './render/renderQuality.ts';
598
622
  export { MAX_POINT_LIGHTS, SURFACE_TEXTURE_UNIT } from './render/lightBudget.ts';
@@ -600,6 +624,21 @@ export { MAX_POINT_LIGHTS, SURFACE_TEXTURE_UNIT } from './render/lightBudget.ts'
600
624
  export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.ts';
601
625
  export { SurfaceTexture } from './render/surfaceTexture.ts';
602
626
  export type { SurfaceMaterial, SurfaceTextureOptions } from './render/surfaceTexture.ts';
627
+ /*
628
+ * BC compressed textures: the source `createSurfaceTexture` takes as blocks, and the one question to
629
+ * ask before handing one over — whether this device takes its format (`renderer.compressedFormats`).
630
+ */
631
+ export {
632
+ compressedFormatName,
633
+ isCompressedSource,
634
+ uploadsCompressed,
635
+ } from './render/compressedSource.ts';
636
+ export type {
637
+ BlockFormat,
638
+ CompressedTextureFormat,
639
+ CompressedTextureSource,
640
+ SurfaceSource,
641
+ } from './render/compressedSource.ts';
603
642
  export { DEFAULT_GOVERNOR_LIMITS, ResolutionGovernor } from './render/resolutionGovernor.ts';
604
643
  export type { GovernorLimits } from './render/resolutionGovernor.ts';
605
644
  export { isWeakGpuFamily } from './render/gpuCapability.ts';
@@ -30,7 +30,7 @@ export function damp(a: number, b: number, lambda: number, dt: number): number {
30
30
  * **What a simulation wants instead**, if it ever wants exponential approach inside the fixed
31
31
  * step, is a per-tick factor computed once from `lambda` and the fixed `dt` and passed in as a
32
32
  * number: at a constant step the factor is a constant, so nothing has to call `exp` on a tick at
33
- * all. `exactExp` in `math/exact.ts` is the other answer, and is the one to reach for when the
33
+ * all. `exactExp` in `@driftengine/physics`' `exact.ts` is the other answer, and is the one to reach for when the
34
34
  * rate itself varies.
35
35
  */
36
36
  // determinism: build-time — presentation smoothing; see the note above for a simulation
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The anisotropic model's lobe, as `shaders/flat/anisotropic.ts` computes it: the reference its
3
+ * tests hold the arithmetic to, and nothing at run time calls it.
4
+ *
5
+ * **GGX stretched along a direction in the surface**, after glTF's `KHR_materials_anisotropy`: the
6
+ * roughness across the direction is the material's, and along it is raised toward 1 by the
7
+ * strength squared — `alphaT = mix(alpha, 1, strength²)`, `alphaB = alpha` — so a brushed disc's
8
+ * highlight is a streak across its grooves.
9
+ *
10
+ * **Divided by its own peak, as `specularLobe` is**, because this engine's highlights are in those
11
+ * units: a look control that peaks at 1 whatever its width, rather than an energy-conserving BRDF
12
+ * (`lobes.ts` says why). So at strength 0 this *is* `specularLobe`, which is the property a material
13
+ * that turns anisotropy down to nothing must have, and which the tests hold. **What it gives up** is
14
+ * the Smith masking a physically based anisotropic lobe carries at grazing angles; the standard lobe
15
+ * carries none either, and the two must agree at strength 0.
16
+ */
17
+ import { MIN_LOBE_ALPHA } from './shaders/flat/lobes.ts';
18
+
19
+ /** The two widths, along the direction and across it, into `out` as [alongT, acrossB]. */
20
+ export function anisotropicAlphas(roughness: number, strength: number, out: Float64Array): void {
21
+ const alpha = Math.max(roughness * roughness, MIN_LOBE_ALPHA);
22
+ out[0] = alpha + (1 - alpha) * strength * strength;
23
+ out[1] = alpha;
24
+ }
25
+
26
+ /**
27
+ * The lobe at a half vector whose components along the direction, across it and along the normal
28
+ * are `th`, `bh` and `nh`. 1 at the mirror direction.
29
+ */
30
+ export function anisotropicLobe(
31
+ th: number,
32
+ bh: number,
33
+ nh: number,
34
+ alphaT: number,
35
+ alphaB: number,
36
+ ): number {
37
+ const x = th / alphaT;
38
+ const y = bh / alphaB;
39
+ const d = x * x + y * y + nh * nh;
40
+ return 1 / Math.max(d * d, 1e-8);
41
+ }
@@ -6,6 +6,8 @@ import type { ProbeBakeOptions } from '../reflectionProbe.ts';
6
6
  import type { Renderer, TranslucentMeshOptions } from './webgl2/renderer.ts';
7
7
  import type { InstancedOptions, MeshInstances } from '../instances.ts';
8
8
  import type { SurfaceMaterial } from '../surfaceTexture.ts';
9
+ import type { SurfaceSource } from '../compressedSource.ts';
10
+ import type { ClothBindingData } from '../clothBindingData.ts';
9
11
  import type { TextStyle } from '../textLayout.ts';
10
12
  import type { SdfFont } from '../sdfFont.ts';
11
13
  import type { SdfTextStyle } from '../sdfTextLayout.ts';
@@ -49,6 +51,21 @@ export interface MeshHandle {
49
51
  readonly complete: boolean;
50
52
  }
51
53
 
54
+ /**
55
+ * A mesh's cloth binding on the device, from `createClothBinding`: which simulation triangle each
56
+ * vertex follows. Opaque, for the reason `MeshHandle` gives.
57
+ */
58
+ /* eslint-disable-next-line @typescript-eslint/no-empty-object-type -- opaque by design */
59
+ export interface ClothBindingHandle {
60
+ /* Deliberately empty, for the weak-type reason `MeshHandle` explains. */
61
+ }
62
+
63
+ /** One character's cloth particles on the device, from `createClothParticles`. Opaque likewise. */
64
+ /* eslint-disable-next-line @typescript-eslint/no-empty-object-type -- opaque by design */
65
+ export interface ClothParticlesHandle {
66
+ /* Deliberately empty, for the weak-type reason `MeshHandle` explains. */
67
+ }
68
+
52
69
  /** A mesh on its way to the device, and the iterator that gets it there. */
53
70
  export interface IncrementalMeshHandle {
54
71
  /**
@@ -287,10 +304,42 @@ export type RendererApi = Omit<
287
304
  | 'disposeSurfaceTexture'
288
305
  | 'setSurfaceTexture'
289
306
  | 'setMaterial'
307
+ | 'createClothBinding'
308
+ | 'createClothParticles'
309
+ | 'updateClothParticles'
310
+ | 'setCloth'
311
+ | 'disposeClothBinding'
312
+ | 'disposeClothParticles'
290
313
  > & {
291
- /** Upload a caller's image. See `SurfaceTextureHandle` for the boundary this guards. */
314
+ /**
315
+ * A mesh's cloth binding: which simulation triangle each of its vertices follows, where on it and
316
+ * by how much (`ClothBindingData`). The mesh must be skinned — a binding blends each vertex
317
+ * between its skinning and the cloth — and is checked against the binding by name.
318
+ */
319
+ createClothBinding(mesh: MeshHandle, data: ClothBindingData): ClothBindingHandle;
320
+ /** One character's particles, `count` of them, updated every frame with `updateClothParticles`. */
321
+ createClothParticles(count: number): ClothParticlesHandle;
322
+ /**
323
+ * This frame's particles, three floats each, world space — a simulation's output as it is.
324
+ * **Once a frame, before the character's draws**: a second update in one frame reaches every
325
+ * draw of it on WebGPU, whose texture writes land ahead of the frame's commands. Last frame's
326
+ * are kept for the motion a temporal resolve needs, and a frame with no update did not move.
327
+ */
328
+ updateClothParticles(particles: ClothParticlesHandle, positions: Float32Array): void;
329
+ /**
330
+ * Place the skinned draws that follow — lit and cast — by a cloth: the mesh's binding and the
331
+ * character's particles. Null for none, which every draw is until this is called; per-draw state,
332
+ * as `setSkinPalette` is.
333
+ */
334
+ setCloth(binding: ClothBindingHandle | null, particles?: ClothParticlesHandle | null): void;
335
+ disposeClothBinding(binding: ClothBindingHandle): void;
336
+ disposeClothParticles(particles: ClothParticlesHandle): void;
337
+ /**
338
+ * Upload a caller's image, or a BC image's blocks with their stored chain where
339
+ * `compressedFormats` has the format. See `SurfaceTextureHandle` for the boundary this guards.
340
+ */
292
341
  createSurfaceTexture(
293
- source: TexImageSource,
342
+ source: SurfaceSource,
294
343
  options?: Parameters<Renderer['createSurfaceTexture']>[1],
295
344
  ): SurfaceTextureHandle;
296
345
 
@@ -308,7 +357,7 @@ export type RendererApi = Omit<
308
357
  * `updateSurfaceTexture` refuses an array.
309
358
  */
310
359
  createSurfaceTextureArray(
311
- sources: readonly TexImageSource[],
360
+ sources: readonly SurfaceSource[],
312
361
  options?: Parameters<Renderer['createSurfaceTexture']>[1],
313
362
  ): SurfaceTextureHandle;
314
363
 
@@ -247,6 +247,14 @@ async function acquireWebGpu(
247
247
  if (wantsGpuTiming && adapter.features?.has('timestamp-query') === true) {
248
248
  features.push('timestamp-query');
249
249
  }
250
+ /*
251
+ * BC compressed textures, on the same terms: asked for wherever the adapter offers it, because
252
+ * a device without it still draws — the loader decodes for it — and one with it samples a BC7
253
+ * texture at a byte a texel instead of four. See `compressedUpload.ts`.
254
+ */
255
+ if (adapter.features?.has('texture-compression-bc') === true) {
256
+ features.push('texture-compression-bc');
257
+ }
250
258
  const descriptor: GPUDeviceDescriptor = {};
251
259
  if (Object.keys(required).length > 0) descriptor.requiredLimits = required;
252
260
  if (features.length > 0) descriptor.requiredFeatures = features;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * A cloth binding's textures on WebGL2: the binding and the rest particles once, a mesh's; the
3
+ * particles every frame, a character's.
4
+ *
5
+ * `RGBA32F`, one storage level and `NEAREST` both ways, as the joint palette is: the vertex stage
6
+ * reads them with `texelFetch`, so nothing filters and nothing selects a mip. Rows of at most 2048
7
+ * texels; see `clothBindingData.ts`.
8
+ *
9
+ * **Two particle textures that swap on each update**, so last frame's positions are still there for
10
+ * a motion vector without a copy: an update writes the older of the two and makes it current.
11
+ */
12
+ import { clothTextureSize, packClothBinding, packClothParticles } from '../../clothBindingData.ts';
13
+ import type { ClothBindingData } from '../../clothBindingData.ts';
14
+
15
+ function floatTexture(
16
+ gl: WebGL2RenderingContext,
17
+ width: number,
18
+ height: number,
19
+ texels: Float32Array | null,
20
+ ): WebGLTexture {
21
+ const texture = gl.createTexture();
22
+ if (texture === null) throw new Error('cloth: createTexture failed');
23
+ gl.bindTexture(gl.TEXTURE_2D, texture);
24
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, width, height);
25
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
26
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
27
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
28
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
29
+ if (texels !== null) {
30
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.FLOAT, texels);
31
+ }
32
+ gl.bindTexture(gl.TEXTURE_2D, null);
33
+ return texture;
34
+ }
35
+
36
+ /** A mesh's binding and its particles at rest. Validated by the renderer against the mesh. */
37
+ export class GlClothBinding {
38
+ readonly binding: WebGLTexture;
39
+ readonly rest: WebGLTexture;
40
+ /** How many particles the binding names. */
41
+ readonly particles: number;
42
+
43
+ constructor(gl: WebGL2RenderingContext, data: ClothBindingData) {
44
+ const vertices = data.weights.length;
45
+ const size = clothTextureSize(vertices * 2);
46
+ this.binding = floatTexture(gl, size.width, size.height, packClothBinding(data));
47
+ this.particles = data.rest.length / 3;
48
+ const rest = clothTextureSize(this.particles);
49
+ const texels = new Float32Array(rest.width * rest.height * 4);
50
+ packClothParticles(data.rest, texels);
51
+ this.rest = floatTexture(gl, rest.width, rest.height, texels);
52
+ }
53
+
54
+ dispose(gl: WebGL2RenderingContext): void {
55
+ gl.deleteTexture(this.binding);
56
+ gl.deleteTexture(this.rest);
57
+ }
58
+ }
59
+
60
+ /** One character's particles: this frame's and the last, in two textures that swap. */
61
+ export class GlClothParticles {
62
+ readonly count: number;
63
+ private readonly width: number;
64
+ private readonly height: number;
65
+ private readonly staging: Float32Array;
66
+ private readonly textures: [WebGLTexture, WebGLTexture];
67
+ private latest = 0;
68
+
69
+ constructor(gl: WebGL2RenderingContext, count: number) {
70
+ this.count = count;
71
+ const size = clothTextureSize(count);
72
+ this.width = size.width;
73
+ this.height = size.height;
74
+ this.staging = new Float32Array(size.width * size.height * 4);
75
+ this.textures = [
76
+ floatTexture(gl, size.width, size.height, this.staging),
77
+ floatTexture(gl, size.width, size.height, this.staging),
78
+ ];
79
+ }
80
+
81
+ /** This frame's particles. */
82
+ get current(): WebGLTexture {
83
+ return this.textures[this.latest] as WebGLTexture;
84
+ }
85
+
86
+ /** Last frame's. */
87
+ get previous(): WebGLTexture {
88
+ return this.textures[1 - this.latest] as WebGLTexture;
89
+ }
90
+
91
+ /** Write `positions` (three floats a particle, world space) as this frame's; last frame's stays. */
92
+ update(gl: WebGL2RenderingContext, positions: Float32Array): void {
93
+ if (positions.length !== this.count * 3) {
94
+ throw new Error(
95
+ `cloth: ${this.count} particles and an update of ${positions.length / 3}; a cloth's ` +
96
+ 'particle count is fixed when its particles are created',
97
+ );
98
+ }
99
+ packClothParticles(positions, this.staging);
100
+ this.latest = 1 - this.latest;
101
+ gl.bindTexture(gl.TEXTURE_2D, this.current);
102
+ gl.texSubImage2D(
103
+ gl.TEXTURE_2D,
104
+ 0,
105
+ 0,
106
+ 0,
107
+ this.width,
108
+ this.height,
109
+ gl.RGBA,
110
+ gl.FLOAT,
111
+ this.staging,
112
+ );
113
+ gl.bindTexture(gl.TEXTURE_2D, null);
114
+ }
115
+
116
+ dispose(gl: WebGL2RenderingContext): void {
117
+ gl.deleteTexture(this.textures[0]);
118
+ gl.deleteTexture(this.textures[1]);
119
+ }
120
+ }