@driftengine/core 4.13.2 → 4.13.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 (49) hide show
  1. package/CHANGELOG.json +27 -0
  2. package/README.md +1 -1
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +1 -0
  5. package/dist/render/backend/webgl2/renderer.d.ts +12 -0
  6. package/dist/render/backend/webgl2/renderer.js +47 -2
  7. package/dist/render/backend/webgpu/clothSetPack.js +1 -0
  8. package/dist/render/backend/webgpu/clothSolvePack.d.ts +5 -2
  9. package/dist/render/backend/webgpu/clothSolvePack.js +2 -1
  10. package/dist/render/backend/webgpu/gpuClothSet.js +1 -1
  11. package/dist/render/backend/webgpu/renderer.d.ts +9 -0
  12. package/dist/render/backend/webgpu/renderer.js +33 -1
  13. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +1 -1
  14. package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +7 -5
  15. package/dist/render/hairLobes.d.ts +23 -0
  16. package/dist/render/hairLobes.js +50 -1
  17. package/dist/render/shaders/flat/detail.d.ts +21 -0
  18. package/dist/render/shaders/flat/detail.js +74 -0
  19. package/dist/render/shaders/flat/glassTint.d.ts +1 -1
  20. package/dist/render/shaders/flat/glassTint.js +1 -0
  21. package/dist/render/shaders/flat/hair.js +51 -25
  22. package/dist/render/shaders/flat/index.d.ts +5 -0
  23. package/dist/render/shaders/flat/index.js +3 -0
  24. package/dist/render/shaders/flat/main.d.ts +1 -1
  25. package/dist/render/shaders/flat/main.js +5 -2
  26. package/dist/render/shaders/flat/models.js +11 -4
  27. package/dist/render/shaders/generated/flat.wgsl.d.ts +144 -16
  28. package/dist/render/shaders/generated/flat.wgsl.js +183 -55
  29. package/dist/render/surfaceDetail.d.ts +79 -0
  30. package/dist/render/surfaceDetail.js +86 -0
  31. package/dist/render/surfaceTexture.d.ts +9 -0
  32. package/package.json +6 -6
  33. package/src/index.ts +2 -0
  34. package/src/render/backend/webgl2/renderer.ts +58 -2
  35. package/src/render/backend/webgpu/clothSetPack.ts +1 -0
  36. package/src/render/backend/webgpu/clothSolvePack.ts +6 -2
  37. package/src/render/backend/webgpu/gpuClothSet.ts +1 -1
  38. package/src/render/backend/webgpu/renderer.ts +42 -1
  39. package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +7 -5
  40. package/src/render/hairLobes.ts +76 -1
  41. package/src/render/shaders/flat/detail.ts +74 -0
  42. package/src/render/shaders/flat/glassTint.ts +3 -1
  43. package/src/render/shaders/flat/hair.ts +51 -25
  44. package/src/render/shaders/flat/index.ts +8 -0
  45. package/src/render/shaders/flat/main.ts +5 -2
  46. package/src/render/shaders/flat/models.ts +11 -4
  47. package/src/render/shaders/generated/flat.wgsl.ts +183 -55
  48. package/src/render/surfaceDetail.ts +136 -0
  49. package/src/render/surfaceTexture.ts +9 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * A material's tiled detail: small normal maps, and occlusion, repeated many times over the base
3
+ * maps, a region map choosing which one each texel wears. `SurfaceMaterial.detail`, packed into
4
+ * `uDetail` for `shaders/flat/detail.ts`.
5
+ *
6
+ * **What it is for.** A surface's finest grain — leather, a weave, brushed metal, a face's pores —
7
+ * is a map of a few hundred texels repeated tens of times across it. Baked into the base maps it is
8
+ * lost: a 256-texel detail tiled forty times over a 2048-texel map would want some ten thousand
9
+ * texels a side. So the base maps carry the shape and a detail map the grain, each garment region
10
+ * with its own grain at its own repeat, which is how such surfaces are authored.
11
+ *
12
+ * **Where the maps are.** The lit stage has no texture binding to spare on either backend, so the
13
+ * detail maps are layers of the array read where a shading model's map is (`uModelMap`), and the
14
+ * region map is a layer of the material's own ORM array, one index a texel in its red. What that
15
+ * gives up, said where each is refused: a material whose shading model has a map must put its
16
+ * detail layers in that same array; a layered material whose mask is a map cannot have detail; and
17
+ * the region map shares the ORM array's size and format, which must keep an index exactly, so an
18
+ * uncompressed one.
19
+ *
20
+ * **Sixteen regions, two vectors each**, behind one vector saying how many and where the region
21
+ * map is: thirty-three in all, which is why WebGL2 compiles them in only when a material first asks
22
+ * and refuses them on a part with no room for them.
23
+ */
24
+
25
+ /** Regions a material may name. */
26
+ export const MAX_DETAIL_REGIONS = 16;
27
+
28
+ /** Floats `uDetail` holds: a header vector, then two vectors a region. */
29
+ export const DETAIL_FLOATS = 4 + MAX_DETAIL_REGIONS * 8;
30
+
31
+ /** One region's detail. */
32
+ export interface SurfaceDetailRegion {
33
+ /** The layer of the detail array holding this region's tangent-space normal map. */
34
+ readonly normalLayer: number;
35
+ /** The layer holding its occlusion, in red; absent, the region adds none. */
36
+ readonly occlusionLayer?: number;
37
+ /** Repeats across the base coordinates, the same both ways or across and down apart. */
38
+ readonly repeat: number | readonly [number, number];
39
+ /** Added to the repeated coordinates, so neighbouring regions need not line up. Default none. */
40
+ readonly offset?: readonly [number, number];
41
+ /** How far the detail turns the normal: 0 lies flat, 1 as drawn, more steeper. Default 1. */
42
+ readonly normalStrength?: number;
43
+ /** How much of its occlusion is laid on, from 0 to 1. Default 1. */
44
+ readonly occlusionStrength?: number;
45
+ }
46
+
47
+ /** A material's tiled detail. */
48
+ export interface SurfaceDetail<Texture> {
49
+ /**
50
+ * The array whose layers the regions name, read where a shading model's map is. A material with
51
+ * a `modelMap` puts its detail layers in that array and names it here too.
52
+ */
53
+ readonly maps: Texture;
54
+ /**
55
+ * The layer of the material's ORM array holding the region map: in its red, each texel's region
56
+ * as an index from 0, stored as `index / 255`, read exactly at the base coordinates. Absent, every
57
+ * texel is region 0, which is a surface with one detail all over: a face's pores.
58
+ */
59
+ readonly regionLayer?: number;
60
+ /** Up to sixteen, by index. A texel naming a region past the last wears the last. */
61
+ readonly regions: readonly SurfaceDetailRegion[];
62
+ }
63
+
64
+ /**
65
+ * Pack a material's detail into `out`, `DETAIL_FLOATS` long; zeros for none, which the shader reads
66
+ * as no detail. `hasOrm` says whether the material carries the ORM array a region map is a layer
67
+ * of; without one every texel is region 0. Allocates nothing.
68
+ */
69
+ export function packSurfaceDetail(
70
+ detail: SurfaceDetail<unknown> | null,
71
+ hasOrm: boolean,
72
+ out: Float32Array,
73
+ ): void {
74
+ out.fill(0);
75
+ if (detail === null) return;
76
+ const count = Math.min(MAX_DETAIL_REGIONS, detail.regions.length);
77
+ out[0] = count;
78
+ const regionLayer = detail.regionLayer;
79
+ out[1] = hasOrm && regionLayer !== undefined ? Math.max(0, Math.floor(regionLayer)) : -1;
80
+ for (let i = 0; i < count; i++) {
81
+ const region = detail.regions[i] as SurfaceDetailRegion;
82
+ const at = 4 + i * 8;
83
+ const repeat = region.repeat;
84
+ const across = typeof repeat === 'number' ? repeat : repeat[0];
85
+ const down = typeof repeat === 'number' ? repeat : repeat[1];
86
+ out[at] = Math.max(0, Math.floor(region.normalLayer));
87
+ out[at + 1] =
88
+ region.occlusionLayer === undefined ? -1 : Math.max(0, Math.floor(region.occlusionLayer));
89
+ out[at + 2] = Math.max(0, region.normalStrength ?? 1);
90
+ out[at + 3] = Math.min(1, Math.max(0, region.occlusionStrength ?? 1));
91
+ /* A repeat at or below zero has no grain to show, so it is one rather than a mirror or nothing. */
92
+ out[at + 4] = across > 0 ? across : 1;
93
+ out[at + 5] = down > 0 ? down : 1;
94
+ out[at + 6] = region.offset?.[0] ?? 0;
95
+ out[at + 7] = region.offset?.[1] ?? 0;
96
+ }
97
+ }
98
+
99
+ /** Why a material's detail is drawn without, or null where nothing stands in its way. */
100
+ export type DetailRefusal = 'slot' | 'projection';
101
+
102
+ /**
103
+ * Whether a material's detail can be drawn, given what else wants the slot its maps are read
104
+ * through: a shading model's own map, or a layered material's mask where it is a map. Either must
105
+ * be the detail's array itself. And a material placed by the world has no base coordinates for a
106
+ * region map to be read at, so its detail is refused too.
107
+ */
108
+ export function detailRefusal(
109
+ detail: SurfaceDetail<unknown>,
110
+ slotTakenBy: unknown,
111
+ projected: boolean,
112
+ ): DetailRefusal | null {
113
+ if (projected) return 'projection';
114
+ if (slotTakenBy !== null && slotTakenBy !== undefined && slotTakenBy !== detail.maps) {
115
+ return 'slot';
116
+ }
117
+ return null;
118
+ }
119
+
120
+ /** A material's detail, or null where it names none or no region. */
121
+ export function detailOf<Texture>(
122
+ material: { readonly detail?: SurfaceDetail<Texture> | null } | null,
123
+ ): SurfaceDetail<Texture> | null {
124
+ const detail = material?.detail ?? null;
125
+ return detail === null || detail.regions.length === 0 ? null : detail;
126
+ }
127
+
128
+ /** The sentence each backend says once when it draws a material without its detail. */
129
+ export function detailRefusalWarning(refusal: DetailRefusal): string {
130
+ return refusal === 'slot'
131
+ ? "Renderer: a material's detail is drawn without: its maps are read where a shading model's " +
132
+ "map goes, and this material binds another array there, its `modelMap` or its layers' " +
133
+ 'mask. Put the detail layers in the `modelMap` array and name that array as `detail.maps`.'
134
+ : "Renderer: a material's detail is drawn without: it is placed by the world (`projection`), " +
135
+ 'so there are no base coordinates for its regions and repeats to be read at.';
136
+ }
@@ -8,6 +8,7 @@
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 { SurfaceDetail } from './surfaceDetail.ts';
11
12
  import type { SurfaceLayers } from './surfaceLayers.ts';
12
13
  import type { SurfaceProjection } from './surfaceProjection.ts';
13
14
  import type { SurfaceModel } from './surfaceModel.ts';
@@ -252,6 +253,14 @@ export interface SurfaceMaterial<Texture = SurfaceTexture> {
252
253
  * vertices. See `surfaceLayers.ts` for the rest and what it gives up.
253
254
  */
254
255
  layers?: SurfaceLayers<Texture> | null;
256
+ /**
257
+ * Fine grain tiled over the base maps: up to sixteen regions, each a detail normal map, and an
258
+ * occlusion, at its own repeat and strength, a region map in the ORM array choosing which one each
259
+ * texel wears. The detail maps are layers of an array read where `modelMap` goes, so a material
260
+ * with a shading model's map puts them in that array. **A lit switch**, compiled in the first time
261
+ * a material asks. See `surfaceDetail.ts` for the rest and what it gives up.
262
+ */
263
+ detail?: SurfaceDetail<Texture> | null;
255
264
  }
256
265
 
257
266
  export interface SurfaceTextureOptions {