@driftengine/core 4.8.2 → 4.8.4

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 (259) hide show
  1. package/CHANGELOG.json +127 -0
  2. package/README.md +1 -1
  3. package/dist/geometry/meshConcat.js +2 -0
  4. package/dist/index.d.ts +11 -4
  5. package/dist/index.js +22 -1
  6. package/dist/math/scalar.js +1 -1
  7. package/dist/render/anisotropicLobe.d.ts +7 -0
  8. package/dist/render/anisotropicLobe.js +33 -0
  9. package/dist/render/backend/api.d.ts +41 -4
  10. package/dist/render/backend/materialChanges.d.ts +7 -2
  11. package/dist/render/backend/materialChanges.js +8 -3
  12. package/dist/render/backend/select.js +8 -0
  13. package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
  14. package/dist/render/backend/webgl2/clothTextures.js +93 -0
  15. package/dist/render/backend/webgl2/renderer.d.ts +159 -3
  16. package/dist/render/backend/webgl2/renderer.js +662 -80
  17. package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
  18. package/dist/render/backend/webgl2/skinScatter.js +256 -0
  19. package/dist/render/backend/webgpu/buffers.d.ts +25 -11
  20. package/dist/render/backend/webgpu/buffers.js +98 -49
  21. package/dist/render/backend/webgpu/causticsPass.js +2 -1
  22. package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
  23. package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
  24. package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
  25. package/dist/render/backend/webgpu/clothTextures.js +145 -0
  26. package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
  27. package/dist/render/backend/webgpu/compressedUpload.js +38 -0
  28. package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
  29. package/dist/render/backend/webgpu/computeHost.js +5 -0
  30. package/dist/render/backend/webgpu/depthPass.d.ts +29 -9
  31. package/dist/render/backend/webgpu/depthPass.js +43 -12
  32. package/dist/render/backend/webgpu/drawCommand.js +6 -2
  33. package/dist/render/backend/webgpu/filmPass.js +2 -1
  34. package/dist/render/backend/webgpu/flatPass.d.ts +43 -4
  35. package/dist/render/backend/webgpu/flatPass.js +111 -13
  36. package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
  37. package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
  38. package/dist/render/backend/webgpu/linePass.js +2 -1
  39. package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
  40. package/dist/render/backend/webgpu/motionPass.js +106 -9
  41. package/dist/render/backend/webgpu/panelPass.js +2 -1
  42. package/dist/render/backend/webgpu/particlePass.js +2 -1
  43. package/dist/render/backend/webgpu/plumePass.js +2 -1
  44. package/dist/render/backend/webgpu/postPass.d.ts +15 -3
  45. package/dist/render/backend/webgpu/postPass.js +25 -8
  46. package/dist/render/backend/webgpu/renderer.d.ts +123 -6
  47. package/dist/render/backend/webgpu/renderer.js +534 -58
  48. package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
  49. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
  50. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
  51. package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
  52. package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
  53. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
  54. package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
  55. package/dist/render/backend/webgpu/textPass.js +2 -1
  56. package/dist/render/backend/webgpu/waterPass.js +2 -1
  57. package/dist/render/backend/webgpu/windStreakPass.js +2 -1
  58. package/dist/render/boltBatch.js +2 -1
  59. package/dist/render/causticsRenderer.js +2 -1
  60. package/dist/render/clothBindingData.d.ts +41 -0
  61. package/dist/render/clothBindingData.js +72 -0
  62. package/dist/render/clothPlacement.d.ts +25 -0
  63. package/dist/render/clothPlacement.js +85 -0
  64. package/dist/render/compressedSource.d.ts +66 -0
  65. package/dist/render/compressedSource.js +138 -0
  66. package/dist/render/cutoutCaster.d.ts +5 -1
  67. package/dist/render/cutoutCaster.js +6 -0
  68. package/dist/render/cutoutDither.d.ts +51 -0
  69. package/dist/render/cutoutDither.js +90 -0
  70. package/dist/render/decalPass.js +2 -1
  71. package/dist/render/eyeRefraction.d.ts +35 -0
  72. package/dist/render/eyeRefraction.js +40 -0
  73. package/dist/render/glCompressed.d.ts +27 -0
  74. package/dist/render/glCompressed.js +53 -0
  75. package/dist/render/globalMediumPass.js +3 -2
  76. package/dist/render/hairLobes.d.ts +70 -0
  77. package/dist/render/hairLobes.js +120 -0
  78. package/dist/render/lightBudget.d.ts +22 -0
  79. package/dist/render/lightBudget.js +22 -0
  80. package/dist/render/lineBatch.js +5 -1
  81. package/dist/render/mesh.d.ts +7 -0
  82. package/dist/render/mesh.js +24 -0
  83. package/dist/render/oitPass.js +2 -1
  84. package/dist/render/particleBatch.js +3 -2
  85. package/dist/render/particlePool.d.ts +6 -2
  86. package/dist/render/plumeRenderer.js +3 -2
  87. package/dist/render/recon/motionVectors.d.ts +4 -2
  88. package/dist/render/recon/motionVectors.js +6 -3
  89. package/dist/render/renderQuality.d.ts +29 -2
  90. package/dist/render/renderQuality.js +5 -0
  91. package/dist/render/rendererHarness.js +4 -0
  92. package/dist/render/sceneCoverage.d.ts +54 -0
  93. package/dist/render/sceneCoverage.js +72 -0
  94. package/dist/render/sceneTarget.d.ts +12 -0
  95. package/dist/render/sceneTarget.js +14 -0
  96. package/dist/render/sdfTextRenderer.js +2 -1
  97. package/dist/render/shaders/clothBinding.d.ts +22 -0
  98. package/dist/render/shaders/clothBinding.js +80 -0
  99. package/dist/render/shaders/depth.js +45 -8
  100. package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
  101. package/dist/render/shaders/flat/anisotropic.js +68 -0
  102. package/dist/render/shaders/flat/eye.d.ts +1 -0
  103. package/dist/render/shaders/flat/eye.js +81 -0
  104. package/dist/render/shaders/flat/hair.d.ts +1 -0
  105. package/dist/render/shaders/flat/hair.js +101 -0
  106. package/dist/render/shaders/flat/index.js +42 -3
  107. package/dist/render/shaders/flat/main.d.ts +1 -1
  108. package/dist/render/shaders/flat/main.js +113 -17
  109. package/dist/render/shaders/flat/models.d.ts +14 -0
  110. package/dist/render/shaders/flat/models.js +273 -0
  111. package/dist/render/shaders/flat/preamble.js +26 -24
  112. package/dist/render/shaders/flat/skin.d.ts +1 -0
  113. package/dist/render/shaders/flat/skin.js +114 -0
  114. package/dist/render/shaders/generated/depth.wgsl.d.ts +66 -24
  115. package/dist/render/shaders/generated/depth.wgsl.js +66 -24
  116. package/dist/render/shaders/generated/flat.wgsl.d.ts +1815 -1704
  117. package/dist/render/shaders/generated/flat.wgsl.js +1853 -1742
  118. package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
  119. package/dist/render/shaders/generated/line.wgsl.js +1 -1
  120. package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
  121. package/dist/render/shaders/generated/particle.wgsl.js +3 -3
  122. package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
  123. package/dist/render/shaders/generated/rush.wgsl.js +1 -1
  124. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
  125. package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
  126. package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
  127. package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
  128. package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
  129. package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
  130. package/dist/render/shaders/line.d.ts +1 -1
  131. package/dist/render/shaders/outputTransform.d.ts +14 -1
  132. package/dist/render/shaders/outputTransform.js +23 -1
  133. package/dist/render/shaders/particle.d.ts +3 -3
  134. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  135. package/dist/render/shaders/recon/motion.wgsl.js +102 -0
  136. package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
  137. package/dist/render/shaders/rush.js +13 -2
  138. package/dist/render/shaders/sdfText.d.ts +1 -1
  139. package/dist/render/shaders/skinBlur.d.ts +1 -0
  140. package/dist/render/shaders/skinBlur.js +103 -0
  141. package/dist/render/shaders/skinning.d.ts +3 -1
  142. package/dist/render/shaders/skinning.js +31 -4
  143. package/dist/render/shaders/temporalResolve.js +12 -5
  144. package/dist/render/shadowCasters.d.ts +13 -1
  145. package/dist/render/skinBlur.d.ts +41 -0
  146. package/dist/render/skinBlur.js +49 -0
  147. package/dist/render/skinScatter.d.ts +67 -0
  148. package/dist/render/skinScatter.js +80 -0
  149. package/dist/render/skinnedClothRun.d.ts +26 -0
  150. package/dist/render/skinnedClothRun.js +84 -0
  151. package/dist/render/ssrPass.js +2 -1
  152. package/dist/render/surfaceModel.d.ts +129 -0
  153. package/dist/render/surfaceModel.js +152 -0
  154. package/dist/render/surfaceTexture.d.ts +55 -4
  155. package/dist/render/surfaceTexture.js +60 -46
  156. package/dist/render/textRenderer.js +2 -1
  157. package/dist/render/vertexDefaults.js +8 -0
  158. package/dist/render/waterRenderer.js +2 -1
  159. package/dist/render/windStreakRenderer.js +2 -1
  160. package/package.json +6 -6
  161. package/src/geometry/meshConcat.ts +2 -0
  162. package/src/index.ts +53 -1
  163. package/src/math/scalar.ts +1 -1
  164. package/src/render/anisotropicLobe.ts +41 -0
  165. package/src/render/backend/api.ts +52 -3
  166. package/src/render/backend/materialChanges.ts +8 -3
  167. package/src/render/backend/select.ts +8 -0
  168. package/src/render/backend/webgl2/clothTextures.ts +120 -0
  169. package/src/render/backend/webgl2/renderer.ts +750 -80
  170. package/src/render/backend/webgl2/skinScatter.ts +309 -0
  171. package/src/render/backend/webgpu/buffers.ts +137 -63
  172. package/src/render/backend/webgpu/causticsPass.ts +2 -1
  173. package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
  174. package/src/render/backend/webgpu/clothTextures.ts +194 -0
  175. package/src/render/backend/webgpu/compressedUpload.ts +65 -0
  176. package/src/render/backend/webgpu/computeHost.ts +25 -0
  177. package/src/render/backend/webgpu/depthPass.ts +43 -10
  178. package/src/render/backend/webgpu/drawCommand.ts +6 -2
  179. package/src/render/backend/webgpu/filmPass.ts +2 -1
  180. package/src/render/backend/webgpu/flatPass.ts +147 -8
  181. package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
  182. package/src/render/backend/webgpu/linePass.ts +2 -1
  183. package/src/render/backend/webgpu/motionPass.ts +128 -9
  184. package/src/render/backend/webgpu/panelPass.ts +2 -1
  185. package/src/render/backend/webgpu/particlePass.ts +2 -1
  186. package/src/render/backend/webgpu/plumePass.ts +2 -1
  187. package/src/render/backend/webgpu/postPass.ts +25 -6
  188. package/src/render/backend/webgpu/renderer.ts +701 -52
  189. package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
  190. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
  191. package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
  192. package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
  193. package/src/render/backend/webgpu/textPass.ts +2 -1
  194. package/src/render/backend/webgpu/waterPass.ts +2 -1
  195. package/src/render/backend/webgpu/windStreakPass.ts +2 -1
  196. package/src/render/boltBatch.ts +2 -1
  197. package/src/render/causticsRenderer.ts +2 -1
  198. package/src/render/clothBindingData.ts +89 -0
  199. package/src/render/clothPlacement.ts +114 -0
  200. package/src/render/compressedSource.ts +198 -0
  201. package/src/render/cutoutCaster.ts +20 -2
  202. package/src/render/cutoutDither.ts +99 -0
  203. package/src/render/decalPass.ts +2 -1
  204. package/src/render/eyeRefraction.ts +72 -0
  205. package/src/render/glCompressed.ts +86 -0
  206. package/src/render/globalMediumPass.ts +3 -2
  207. package/src/render/hairLobes.ts +188 -0
  208. package/src/render/lightBudget.ts +25 -0
  209. package/src/render/lineBatch.ts +3 -1
  210. package/src/render/mesh.ts +23 -0
  211. package/src/render/oitPass.ts +2 -1
  212. package/src/render/particleBatch.ts +3 -2
  213. package/src/render/particlePool.ts +6 -2
  214. package/src/render/plumeRenderer.ts +3 -2
  215. package/src/render/recon/motionVectors.ts +6 -3
  216. package/src/render/renderQuality.ts +37 -2
  217. package/src/render/rendererHarness.ts +4 -0
  218. package/src/render/sceneCoverage.ts +78 -0
  219. package/src/render/sceneTarget.ts +21 -0
  220. package/src/render/sdfTextRenderer.ts +2 -1
  221. package/src/render/shaders/clothBinding.ts +81 -0
  222. package/src/render/shaders/depth.ts +45 -8
  223. package/src/render/shaders/flat/anisotropic.ts +68 -0
  224. package/src/render/shaders/flat/eye.ts +82 -0
  225. package/src/render/shaders/flat/hair.ts +102 -0
  226. package/src/render/shaders/flat/index.ts +42 -3
  227. package/src/render/shaders/flat/main.ts +113 -17
  228. package/src/render/shaders/flat/models.ts +286 -0
  229. package/src/render/shaders/flat/preamble.ts +26 -24
  230. package/src/render/shaders/flat/skin.ts +116 -0
  231. package/src/render/shaders/generated/depth.wgsl.ts +66 -24
  232. package/src/render/shaders/generated/flat.wgsl.ts +1853 -1742
  233. package/src/render/shaders/generated/line.wgsl.ts +1 -1
  234. package/src/render/shaders/generated/particle.wgsl.ts +3 -3
  235. package/src/render/shaders/generated/rush.wgsl.ts +1 -1
  236. package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
  237. package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
  238. package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
  239. package/src/render/shaders/outputTransform.ts +24 -1
  240. package/src/render/shaders/recon/motion.wgsl.ts +102 -0
  241. package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
  242. package/src/render/shaders/rush.ts +13 -2
  243. package/src/render/shaders/skinBlur.ts +105 -0
  244. package/src/render/shaders/skinning.ts +31 -4
  245. package/src/render/shaders/temporalResolve.ts +12 -5
  246. package/src/render/shadowCasters.ts +13 -0
  247. package/src/render/skinBlur.ts +53 -0
  248. package/src/render/skinScatter.ts +92 -0
  249. package/src/render/skinnedClothRun.ts +128 -0
  250. package/src/render/ssrPass.ts +2 -1
  251. package/src/render/surfaceModel.ts +274 -0
  252. package/src/render/surfaceTexture.ts +134 -63
  253. package/src/render/textRenderer.ts +2 -1
  254. package/src/render/vertexDefaults.ts +8 -0
  255. package/src/render/waterRenderer.ts +2 -1
  256. package/src/render/windStreakRenderer.ts +2 -1
  257. package/dist/math/exact.d.ts +0 -76
  258. package/dist/math/exact.js +0 -327
  259. package/src/math/exact.ts +0 -340
@@ -0,0 +1,129 @@
1
+ /**
2
+ * A material's shading model: how its surface answers light, when the standard model is not what
3
+ * it is made of — brushed metal, hair, skin, an eye.
4
+ *
5
+ * **A frozen descriptor, made once by a factory, carrying a kind and that model's numbers.** The
6
+ * standard model is `null`, exactly today's shader, and a scene that never names a model compiles
7
+ * nothing of any of them: each is a pipeline constant of its own (`MODEL_ANISOTROPIC` and the rest
8
+ * in `shaders/flat/models.ts`), off in every pipeline but the ones a material asks for.
9
+ *
10
+ * **Two vectors of numbers a material, and no more.** The lit stage's eight-light build is 254 of
11
+ * the 256 uniform vectors an Adreno 740 offers, so a model's numbers are packed into
12
+ * `uModelParams[2]` — the last component of the second says whether a model map is bound — and
13
+ * every model fits. See `packModel` for each layout.
14
+ *
15
+ * **The eye's axis is the one number that moves.** It is the eye's forward direction in the world,
16
+ * which a turning head or a glance changes every frame; the lit stage has no room left for another
17
+ * varying (sixteen, WebGPU's ceiling), so the renderer turns the descriptor's mesh-local axis by
18
+ * the draw's model matrix and the eye's joint each draw, with `eyeAxisInWorld`.
19
+ */
20
+ import type { Vec3 } from '../math/color.ts';
21
+ /** Strands of brushed metal, satin or hair-like fibre: a highlight stretched across them. */
22
+ export interface AnisotropicModel {
23
+ readonly kind: 'anisotropic';
24
+ /** 0 to 1: how far the highlight is stretched along the tangent. */
25
+ readonly strength: number;
26
+ /** Radians, turning the stretch's direction from the tangent toward the bitangent. */
27
+ readonly rotation: number;
28
+ }
29
+ /**
30
+ * Hair: three lobes along the strand, which is the mesh's tangent, running from root to tip
31
+ * (`hairLobes.ts`). A model map's green turns each strand's tilt by up to half of it either way and
32
+ * its blue is occlusion.
33
+ */
34
+ export interface HairModel {
35
+ readonly kind: 'hair';
36
+ /**
37
+ * Radians the cuticle's scales tilt the strand's surface: positive moves the white highlight
38
+ * toward the root and the coloured one toward the tip. A card whose tangent runs tip to root
39
+ * takes it negative.
40
+ */
41
+ readonly shift: number;
42
+ /** 0 to 1: the light scattered through many strands, tinted by their colour. */
43
+ readonly scatter: number;
44
+ /** How strongly light from behind glows through: the transmitted lobe. */
45
+ readonly backlit: number;
46
+ }
47
+ /** Skin: light scattered beneath the surface, reddening its terminator and its thin parts. */
48
+ export interface SkinModel {
49
+ readonly kind: 'skin';
50
+ /** The colour light keeps travelling beneath the surface — the mean free path, as a colour. */
51
+ readonly scatterColor: Vec3;
52
+ /** Metres: how far light travels beneath it before it leaves. */
53
+ readonly radius: number;
54
+ /** 0 to 1: how much a light behind a thin part shows through it. */
55
+ readonly transmission: number;
56
+ /** Which of up to eight profiles the screen-space scattering blurs this skin by. */
57
+ readonly profile: number;
58
+ }
59
+ /** An eye: an iris seen through a cornea, which refracts it, under a mirror-smooth highlight. */
60
+ export interface EyeModel {
61
+ readonly kind: 'eye';
62
+ /** The iris's radius, in texture coordinates from the centre of the eye's map. */
63
+ readonly irisRadius: number;
64
+ /** Metres from the cornea down to the iris. */
65
+ readonly irisDepth: number;
66
+ /** The cornea's index of refraction. */
67
+ readonly ior: number;
68
+ /** How smooth the cornea's highlight is. */
69
+ readonly corneaRoughness: number;
70
+ /** The eye's forward direction, in the mesh's own space at its bind pose. */
71
+ readonly axis: Vec3;
72
+ /** The joint the eye turns with, or -1 for an eye placed by its model matrix alone. */
73
+ readonly joint: number;
74
+ }
75
+ /** Every model a material can name, by its `kind`: see `SurfaceMaterial.model`. */
76
+ export type SurfaceModel = AnisotropicModel | HairModel | SkinModel | EyeModel;
77
+ /** A model's name, which also picks its pipeline switch: `SURFACE_MODEL_SWITCH`. */
78
+ export type SurfaceModelKind = SurfaceModel['kind'];
79
+ /** Every kind, in the order the lit stage declares their switches. */
80
+ export declare const SURFACE_MODEL_KINDS: readonly SurfaceModelKind[];
81
+ /** The lit stage's switch for each kind. */
82
+ export declare const SURFACE_MODEL_SWITCH: Readonly<Record<SurfaceModelKind, string>>;
83
+ /** Floats a material's model takes: `uModelParams[2]`. */
84
+ export declare const MODEL_PARAM_FLOATS = 8;
85
+ /** Brushed metal and the like, after glTF's `KHR_materials_anisotropy`: a strength and a turn. */
86
+ export declare function anisotropicModel(options?: {
87
+ strength?: number;
88
+ rotation?: number;
89
+ }): AnisotropicModel;
90
+ /** Hair, along the mesh's tangent from root to tip. A tilt of 3°, within the 2.5° to 5° measured. */
91
+ export declare function hairModel(options?: {
92
+ shift?: number;
93
+ scatter?: number;
94
+ backlit?: number;
95
+ }): HairModel;
96
+ /** Skin. The defaults are a fair skin's: a red mean free path a little over a centimetre long. */
97
+ export declare function skinModel(options?: {
98
+ scatterColor?: Vec3;
99
+ radius?: number;
100
+ transmission?: number;
101
+ profile?: number;
102
+ }): SkinModel;
103
+ /** An eye. The defaults are a human eye's, its axis along the mesh's +Z. */
104
+ export declare function eyeModel(options?: {
105
+ irisRadius?: number;
106
+ irisDepth?: number;
107
+ ior?: number;
108
+ corneaRoughness?: number;
109
+ axis?: Vec3;
110
+ joint?: number;
111
+ }): EyeModel;
112
+ /**
113
+ * A model's numbers into `out` — eight floats, `uModelParams[0]` then `[1]` — and whether a model
114
+ * map is bound into the last. Zeros for the standard model. An eye's axis goes in with the
115
+ * descriptor's own until the draw turns it: see `eyeAxisInWorld`.
116
+ *
117
+ * - anisotropic: strength, cos and sin of the rotation · −, −, −, map
118
+ * - hair: shift, scatter, backlit, − · −, −, −, map
119
+ * - skin: the scatter colour, radius · transmission, profile, −, map
120
+ * - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
121
+ */
122
+ export declare function packModel(model: SurfaceModel | null, mapped: boolean, out: Float32Array): void;
123
+ /**
124
+ * An eye's axis in the world for one draw, into `out[at…at+2]`: the descriptor's mesh-local axis
125
+ * turned by its joint's palette entry, where it names one and a palette is set, and then by the
126
+ * draw's model matrix — the turn its vertices take, without the translation. Normalised, because
127
+ * a palette carries a joint's scale.
128
+ */
129
+ export declare function eyeAxisInWorld(model: EyeModel, placement: ArrayLike<number>, palette: ArrayLike<number> | null, out: Float32Array, at: number): void;
@@ -0,0 +1,152 @@
1
+ /** Every kind, in the order the lit stage declares their switches. */
2
+ export const SURFACE_MODEL_KINDS = [
3
+ 'anisotropic',
4
+ 'hair',
5
+ 'skin',
6
+ 'eye',
7
+ ];
8
+ /** The lit stage's switch for each kind. */
9
+ export const SURFACE_MODEL_SWITCH = {
10
+ anisotropic: 'MODEL_ANISOTROPIC',
11
+ hair: 'MODEL_HAIR',
12
+ skin: 'MODEL_SKIN',
13
+ eye: 'MODEL_EYE',
14
+ };
15
+ /** Floats a material's model takes: `uModelParams[2]`. */
16
+ export const MODEL_PARAM_FLOATS = 8;
17
+ function check(model, name, value, low, high) {
18
+ if (!Number.isFinite(value) || value < low || value > high) {
19
+ throw new Error(`${model}: ${name} must be from ${low} to ${high}, not ${String(value)}`);
20
+ }
21
+ return value;
22
+ }
23
+ function unit(model, name, v) {
24
+ const length = Math.hypot(v[0], v[1], v[2]);
25
+ if (!(length > 1e-6))
26
+ throw new Error(`${model}: ${name} must be a direction, not [${v.join(', ')}]`);
27
+ return [v[0] / length, v[1] / length, v[2] / length];
28
+ }
29
+ /** Brushed metal and the like, after glTF's `KHR_materials_anisotropy`: a strength and a turn. */
30
+ export function anisotropicModel(options = {}) {
31
+ return Object.freeze({
32
+ kind: 'anisotropic',
33
+ strength: check('anisotropicModel', 'strength', options.strength ?? 1, 0, 1),
34
+ rotation: check('anisotropicModel', 'rotation', options.rotation ?? 0, -2 * Math.PI, 2 * Math.PI),
35
+ });
36
+ }
37
+ /** Hair, along the mesh's tangent from root to tip. A tilt of 3°, within the 2.5° to 5° measured. */
38
+ export function hairModel(options = {}) {
39
+ return Object.freeze({
40
+ kind: 'hair',
41
+ shift: check('hairModel', 'shift', options.shift ?? (3 * Math.PI) / 180, -0.5, 0.5),
42
+ scatter: check('hairModel', 'scatter', options.scatter ?? 0.7, 0, 1),
43
+ backlit: check('hairModel', 'backlit', options.backlit ?? 1, 0, 4),
44
+ });
45
+ }
46
+ /** Skin. The defaults are a fair skin's: a red mean free path a little over a centimetre long. */
47
+ export function skinModel(options = {}) {
48
+ const color = options.scatterColor ?? [0.85, 0.35, 0.22];
49
+ for (let c = 0; c < 3; c++)
50
+ check('skinModel', 'scatterColor', color[c], 0, 1);
51
+ const profile = check('skinModel', 'profile', options.profile ?? 0, 0, 7);
52
+ if (!Number.isInteger(profile))
53
+ throw new Error(`skinModel: profile must be a whole number, not ${profile}`);
54
+ return Object.freeze({
55
+ kind: 'skin',
56
+ scatterColor: Object.freeze([color[0], color[1], color[2]]),
57
+ radius: check('skinModel', 'radius', options.radius ?? 0.012, 0, 0.1),
58
+ transmission: check('skinModel', 'transmission', options.transmission ?? 0.5, 0, 1),
59
+ profile,
60
+ });
61
+ }
62
+ /** An eye. The defaults are a human eye's, its axis along the mesh's +Z. */
63
+ export function eyeModel(options = {}) {
64
+ const joint = options.joint ?? -1;
65
+ if (!Number.isInteger(joint) || joint < -1) {
66
+ throw new Error(`eyeModel: joint must be a joint index or -1, not ${String(joint)}`);
67
+ }
68
+ return Object.freeze({
69
+ kind: 'eye',
70
+ irisRadius: check('eyeModel', 'irisRadius', options.irisRadius ?? 0.15, 0.001, 0.5),
71
+ irisDepth: check('eyeModel', 'irisDepth', options.irisDepth ?? 0.003, 0, 0.02),
72
+ ior: check('eyeModel', 'ior', options.ior ?? 1.336, 1, 2),
73
+ corneaRoughness: check('eyeModel', 'corneaRoughness', options.corneaRoughness ?? 0.02, 0, 1),
74
+ axis: Object.freeze(unit('eyeModel', 'axis', options.axis ?? [0, 0, 1])),
75
+ joint,
76
+ });
77
+ }
78
+ /**
79
+ * A model's numbers into `out` — eight floats, `uModelParams[0]` then `[1]` — and whether a model
80
+ * map is bound into the last. Zeros for the standard model. An eye's axis goes in with the
81
+ * descriptor's own until the draw turns it: see `eyeAxisInWorld`.
82
+ *
83
+ * - anisotropic: strength, cos and sin of the rotation · −, −, −, map
84
+ * - hair: shift, scatter, backlit, − · −, −, −, map
85
+ * - skin: the scatter colour, radius · transmission, profile, −, map
86
+ * - eye: iris radius, iris depth, ior, cornea roughness · the axis, map
87
+ */
88
+ export function packModel(model, mapped, out) {
89
+ out.fill(0);
90
+ if (model === null)
91
+ return;
92
+ out[7] = mapped ? 1 : 0;
93
+ switch (model.kind) {
94
+ case 'anisotropic':
95
+ out[0] = model.strength;
96
+ out[1] = Math.cos(model.rotation);
97
+ out[2] = Math.sin(model.rotation);
98
+ return;
99
+ case 'hair':
100
+ out[0] = model.shift;
101
+ out[1] = model.scatter;
102
+ out[2] = model.backlit;
103
+ return;
104
+ case 'skin':
105
+ out[0] = model.scatterColor[0];
106
+ out[1] = model.scatterColor[1];
107
+ out[2] = model.scatterColor[2];
108
+ out[3] = model.radius;
109
+ out[4] = model.transmission;
110
+ out[5] = model.profile;
111
+ return;
112
+ case 'eye':
113
+ out[0] = model.irisRadius;
114
+ out[1] = model.irisDepth;
115
+ out[2] = model.ior;
116
+ out[3] = model.corneaRoughness;
117
+ out[4] = model.axis[0];
118
+ out[5] = model.axis[1];
119
+ out[6] = model.axis[2];
120
+ return;
121
+ }
122
+ }
123
+ /**
124
+ * An eye's axis in the world for one draw, into `out[at…at+2]`: the descriptor's mesh-local axis
125
+ * turned by its joint's palette entry, where it names one and a palette is set, and then by the
126
+ * draw's model matrix — the turn its vertices take, without the translation. Normalised, because
127
+ * a palette carries a joint's scale.
128
+ */
129
+ export function eyeAxisInWorld(model, placement, palette, out, at) {
130
+ let [x, y, z] = model.axis;
131
+ const j = model.joint * 16;
132
+ if (palette !== null && model.joint >= 0 && j + 16 <= palette.length) {
133
+ const px = palette[j] * x + palette[j + 4] * y + palette[j + 8] * z;
134
+ const py = palette[j + 1] * x +
135
+ palette[j + 5] * y +
136
+ palette[j + 9] * z;
137
+ const pz = palette[j + 2] * x +
138
+ palette[j + 6] * y +
139
+ palette[j + 10] * z;
140
+ x = px;
141
+ y = py;
142
+ z = pz;
143
+ }
144
+ const m = placement;
145
+ const wx = m[0] * x + m[4] * y + m[8] * z;
146
+ const wy = m[1] * x + m[5] * y + m[9] * z;
147
+ const wz = m[2] * x + m[6] * y + m[10] * z;
148
+ const length = Math.hypot(wx, wy, wz) || 1;
149
+ out[at] = wx / length;
150
+ out[at + 1] = wy / length;
151
+ out[at + 2] = wz / length;
152
+ }
@@ -1,4 +1,17 @@
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 type { SurfaceModel } from './surfaceModel.ts';
12
+ import type { CompressedTextureFormat, SurfaceSource } from './compressedSource.ts';
1
13
  import type { SurfaceLayerEffect } from './surfaceEffects.ts';
14
+ import type { CutoutMode } from './cutoutDither.ts';
2
15
  /**
3
16
  * How a texture behaves past its edges and between its texels.
4
17
  *
@@ -57,8 +70,31 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
57
70
  /** Repeats across the mesh's own UV range, per axis. Applies to every map the material holds. */
58
71
  uScale?: number;
59
72
  vScale?: number;
60
- /** Alpha below which a fragment is discarded. See `uAlbedoCutout`. */
73
+ /**
74
+ * Where the texture starts, per axis, added after the scale: a surface samples
75
+ * `uv · scale + offset`. Applies to every map, and to a cutout's shadow and depth as well.
76
+ *
77
+ * **What picks a cell of a flipbook or an atlas through the material**, so one quad draws any
78
+ * cell: a strip of four frames is `uScale: 0.25` and `uOffset: frame * 0.25`. Without it a
79
+ * consumer built one quad mesh per cell — 106 meshes for four strips — because the only way to
80
+ * move a texture was to move the geometry's own coordinates.
81
+ */
82
+ uOffset?: number;
83
+ vOffset?: number;
84
+ /** Alpha below which a fragment is discarded. See `uCutout`. */
61
85
  cutout?: number;
86
+ /**
87
+ * How the cutout's edge is drawn: `'hard'`, the default and every cutout before 4.8.4, or
88
+ * `'dithered'`, which keeps a pixel by the share of it the texture covers so a strand of hair, a
89
+ * lash or a fringe of leaves has a soft edge instead of a stair of pixels.
90
+ *
91
+ * **The frame decides how a dithered edge is resolved**, because the material cannot know: under a
92
+ * temporal resolve (TAA or DriftTR) the pattern moves every frame and the resolve averages it;
93
+ * multisampled, the share goes to the GPU as alpha-to-coverage; with neither, the test is hard,
94
+ * since a dither nothing averages is grain. A translucent draw tests hard whatever this says. See
95
+ * `cutoutDither.ts`.
96
+ */
97
+ cutoutMode?: CutoutMode;
62
98
  /**
63
99
  * Whether both faces are seen: glTF's `doubleSided`, what a curtain or a leaf card is. Drawn
64
100
  * without culling, and a back face is lit as its front, with the normal turned to the viewer.
@@ -99,6 +135,19 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
99
135
  * consumer who wants a bound map twice as bright asks here rather than rebuilding the mesh.
100
136
  */
101
137
  emissiveScale?: readonly [number, number, number];
138
+ /**
139
+ * How the surface answers light, when the standard model is not what it is made of: brushed
140
+ * metal, hair, skin, an eye — `anisotropicModel`, `hairModel`, `skinModel`, `eyeModel`. Null or
141
+ * absent is the standard model, exactly as before. **A pipeline of its own**, compiled the first
142
+ * time a draw asks, so a scene that names no model pays for none. See `surfaceModel.ts`.
143
+ */
144
+ model?: SurfaceModel | null;
145
+ /**
146
+ * The model's own channels, in the albedo's coordinates and layers; what each channel means is
147
+ * the model's to say. Absent, each model takes its own neutral. Sampled linear, like any map
148
+ * that is not a colour.
149
+ */
150
+ modelMap?: Texture | null;
102
151
  }
103
152
  export interface SurfaceTextureOptions {
104
153
  /**
@@ -193,12 +242,14 @@ export declare class SurfaceTexture {
193
242
  readonly layers: number;
194
243
  /** The per-layer effects table, or null for a texture given none. See `surfaceEffects.ts`. */
195
244
  private effectsTable;
245
+ /** The block format a compressed texture holds, or null for one uploaded from images. */
246
+ private readonly blockFormat;
196
247
  /**
197
248
  * One image, or an array of images that must share one size (`layerSize` refuses otherwise).
198
- * Uploaded unflipped either way; see the constructor body for why.
249
+ * Uploaded unflipped either way; see the constructor body for why. Or blocks, uploaded as they are
250
+ * where `compressed` — the formats this context samples — has theirs; see `compressedSource.ts`.
199
251
  */
200
- constructor(gl: WebGL2RenderingContext, source: TexImageSource | readonly TexImageSource[], options?: SurfaceTextureOptions);
201
- /** Bind the effects table to `unit`, or `fallback` for a texture given none. */
252
+ constructor(gl: WebGL2RenderingContext, source: SurfaceSource | readonly SurfaceSource[], options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
202
253
  /** Whether this texture carries an effects table, which the lit program reads only once one exists. */
203
254
  get hasEffects(): boolean;
204
255
  bindEffects(gl: WebGL2RenderingContext, unit: number, fallback: WebGLTexture): void;
@@ -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
  *
@@ -1,6 +1,7 @@
1
1
  import { MAX_CELLS, TEXT_CUBE, TextLayout, deviceSnappedOrigin } from './textLayout.js';
2
2
  import { compileProgram, uniformLocations } from './shader.js';
3
3
  import { TEXT_FRAG, TEXT_VERT } from './shaders/text.js';
4
+ import { blendCovering } from './sceneCoverage.js';
4
5
  /**
5
6
  * Text as geometry, on WebGL2.
6
7
  *
@@ -98,7 +99,7 @@ export class TextRenderer {
98
99
  const reversed = current === gl.GREATER || current === gl.GEQUAL;
99
100
  gl.depthFunc(reversed ? gl.GEQUAL : gl.LEQUAL);
100
101
  gl.enable(gl.BLEND);
101
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
102
+ blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
102
103
  setVec2(gl, this.uniforms['uViewport'], viewportWidth, viewportHeight);
103
104
  /* Snapped with the cell below: a whole pitch on a fractional origin still splits a stroke.
104
105
  See `deviceSnappedOrigin`. */
@@ -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
  *
@@ -102,6 +109,7 @@ export const OUTPUT_TRANSFORM_CODE = {
102
109
  none: 0,
103
110
  srgb: 1,
104
111
  aces: 2,
112
+ shoulder: 3,
105
113
  };
106
114
  /**
107
115
  * How far the speed rush reaches, and how far a motion-blurred pixel may travel, both in UV.
@@ -5,6 +5,7 @@ import { bindAtmosphere } from './atmosphere.js';
5
5
  import { bindPointLights } from './lightBudget.js';
6
6
  import { DEFAULT_RENDER_QUALITY } from './renderQuality.js';
7
7
  import { createResolvedWater, resolveWater, waterAppearance } from './waterDraw.js';
8
+ import { blendCovering } from './sceneCoverage.js';
8
9
  /** Vertices per side of a bounded body's sheet. A pool is not an ocean. */
9
10
  export class WaterRenderer {
10
11
  program;
@@ -145,7 +146,7 @@ export class WaterRenderer {
145
146
  gl.uniform2f(u['uReflectionTexelSize'] ?? null, reflectionReady && reflection !== null ? 1 / reflection.textureWidth : 1, reflectionReady && reflection !== null ? 1 / reflection.textureHeight : 1);
146
147
  gl.uniform1i(u['uReflectionFilterTaps'] ?? null, this.reflectionFilterTaps);
147
148
  gl.enable(gl.BLEND);
148
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
149
+ blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
149
150
  // Waves are displaced far enough that back faces show at grazing angles.
150
151
  gl.disable(gl.CULL_FACE);
151
152
  gl.bindVertexArray(w.bounded ? this.patchVao : this.vao);
@@ -1,6 +1,7 @@
1
1
  import { compileProgram, uniformLocations } from './shader.js';
2
2
  import { WIND_STREAK_FRAG, WIND_STREAK_VERT } from './shaders/windStreaks.js';
3
3
  import { WIND_STREAK_DEFAULTS, buildWindStreakLattice, createResolvedWindStreaks, resolveWindStreaks, windStreakStrength, } from './windStreakDraw.js';
4
+ import { blendCovering } from './sceneCoverage.js';
4
5
  export class WindStreakRenderer {
5
6
  program;
6
7
  uniforms;
@@ -68,7 +69,7 @@ export class WindStreakRenderer {
68
69
  gl.uniform1f(u['uTime'] ?? null, timeSeconds);
69
70
  gl.uniform3fv(u['uTint'] ?? null, tint);
70
71
  gl.enable(gl.BLEND);
71
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
72
+ blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
72
73
  gl.depthMask(false);
73
74
  gl.disable(gl.CULL_FACE);
74
75
  gl.bindVertexArray(this.vao);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftengine/core",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
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.2",
33
- "@driftengine/physics": "4.8.2",
32
+ "@driftengine/drft": "4.8.4",
33
+ "@driftengine/physics": "4.8.4",
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.2",
71
- "@driftengine/network": "4.8.2",
72
- "@driftengine/texture": "4.8.2"
70
+ "@driftengine/nav": "4.8.4",
71
+ "@driftengine/network": "4.8.4",
72
+ "@driftengine/texture": "4.8.4"
73
73
  }
74
74
  }
@@ -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