@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.
- package/CHANGELOG.json +82 -0
- package/README.md +1 -1
- package/dist/build/shaderComments.js +0 -0
- package/dist/geometry/meshConcat.js +2 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +16 -1
- package/dist/math/scalar.js +1 -1
- package/dist/render/anisotropicLobe.d.ts +7 -0
- package/dist/render/anisotropicLobe.js +33 -0
- package/dist/render/backend/api.d.ts +41 -4
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
- package/dist/render/backend/webgl2/clothTextures.js +93 -0
- package/dist/render/backend/webgl2/renderer.d.ts +136 -3
- package/dist/render/backend/webgl2/renderer.js +604 -63
- package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
- package/dist/render/backend/webgl2/skinScatter.js +256 -0
- package/dist/render/backend/webgpu/buffers.d.ts +25 -11
- package/dist/render/backend/webgpu/buffers.js +98 -49
- package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
- package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
- package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
- package/dist/render/backend/webgpu/clothTextures.js +145 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
- package/dist/render/backend/webgpu/compressedUpload.js +38 -0
- package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
- package/dist/render/backend/webgpu/computeHost.js +5 -0
- package/dist/render/backend/webgpu/depthPass.d.ts +16 -6
- package/dist/render/backend/webgpu/depthPass.js +39 -9
- package/dist/render/backend/webgpu/drawCommand.js +6 -2
- package/dist/render/backend/webgpu/flatPass.d.ts +38 -4
- package/dist/render/backend/webgpu/flatPass.js +109 -12
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
- package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
- package/dist/render/backend/webgpu/motionPass.js +106 -9
- package/dist/render/backend/webgpu/postPass.d.ts +3 -1
- package/dist/render/backend/webgpu/postPass.js +6 -3
- package/dist/render/backend/webgpu/renderer.d.ts +113 -5
- package/dist/render/backend/webgpu/renderer.js +498 -48
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
- package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
- package/dist/render/clothBindingData.d.ts +41 -0
- package/dist/render/clothBindingData.js +72 -0
- package/dist/render/clothPlacement.d.ts +25 -0
- package/dist/render/clothPlacement.js +85 -0
- package/dist/render/compressedSource.d.ts +66 -0
- package/dist/render/compressedSource.js +138 -0
- package/dist/render/cutoutCaster.d.ts +2 -0
- package/dist/render/cutoutCaster.js +2 -0
- package/dist/render/cutoutDither.d.ts +51 -0
- package/dist/render/cutoutDither.js +90 -0
- package/dist/render/eyeRefraction.d.ts +35 -0
- package/dist/render/eyeRefraction.js +40 -0
- package/dist/render/glCompressed.d.ts +27 -0
- package/dist/render/glCompressed.js +53 -0
- package/dist/render/hairLobes.d.ts +70 -0
- package/dist/render/hairLobes.js +120 -0
- package/dist/render/lightBudget.d.ts +22 -0
- package/dist/render/lightBudget.js +22 -0
- package/dist/render/mesh.d.ts +7 -0
- package/dist/render/mesh.js +24 -0
- package/dist/render/recon/motionVectors.d.ts +4 -2
- package/dist/render/recon/motionVectors.js +6 -3
- package/dist/render/renderQuality.d.ts +20 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +12 -0
- package/dist/render/sceneTarget.js +14 -0
- package/dist/render/shaders/clothBinding.d.ts +22 -0
- package/dist/render/shaders/clothBinding.js +80 -0
- package/dist/render/shaders/depth.js +42 -6
- package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
- package/dist/render/shaders/flat/anisotropic.js +68 -0
- package/dist/render/shaders/flat/eye.d.ts +1 -0
- package/dist/render/shaders/flat/eye.js +81 -0
- package/dist/render/shaders/flat/hair.d.ts +1 -0
- package/dist/render/shaders/flat/hair.js +101 -0
- package/dist/render/shaders/flat/index.js +36 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +113 -17
- package/dist/render/shaders/flat/models.d.ts +14 -0
- package/dist/render/shaders/flat/models.js +273 -0
- package/dist/render/shaders/flat/preamble.js +26 -24
- package/dist/render/shaders/flat/skin.d.ts +1 -0
- package/dist/render/shaders/flat/skin.js +114 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +38 -16
- package/dist/render/shaders/generated/depth.wgsl.js +38 -16
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1790 -1704
- package/dist/render/shaders/generated/flat.wgsl.js +1828 -1742
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
- package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +102 -0
- package/dist/render/shaders/skinBlur.d.ts +1 -0
- package/dist/render/shaders/skinBlur.js +103 -0
- package/dist/render/shaders/skinning.d.ts +3 -1
- package/dist/render/shaders/skinning.js +31 -4
- package/dist/render/shadowCasters.d.ts +13 -1
- package/dist/render/skinBlur.d.ts +41 -0
- package/dist/render/skinBlur.js +49 -0
- package/dist/render/skinScatter.d.ts +67 -0
- package/dist/render/skinScatter.js +80 -0
- package/dist/render/skinnedClothRun.d.ts +26 -0
- package/dist/render/skinnedClothRun.js +84 -0
- package/dist/render/surfaceModel.d.ts +129 -0
- package/dist/render/surfaceModel.js +152 -0
- package/dist/render/surfaceTexture.d.ts +44 -4
- package/dist/render/surfaceTexture.js +60 -46
- package/dist/render/vertexDefaults.js +7 -0
- package/package.json +6 -6
- package/src/build/shaderComments.ts +0 -0
- package/src/geometry/meshConcat.ts +2 -0
- package/src/index.ts +40 -1
- package/src/math/scalar.ts +1 -1
- package/src/render/anisotropicLobe.ts +41 -0
- package/src/render/backend/api.ts +52 -3
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/clothTextures.ts +120 -0
- package/src/render/backend/webgl2/renderer.ts +693 -64
- package/src/render/backend/webgl2/skinScatter.ts +309 -0
- package/src/render/backend/webgpu/buffers.ts +137 -63
- package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
- package/src/render/backend/webgpu/clothTextures.ts +194 -0
- package/src/render/backend/webgpu/compressedUpload.ts +65 -0
- package/src/render/backend/webgpu/computeHost.ts +25 -0
- package/src/render/backend/webgpu/depthPass.ts +39 -7
- package/src/render/backend/webgpu/drawCommand.ts +6 -2
- package/src/render/backend/webgpu/flatPass.ts +145 -7
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
- package/src/render/backend/webgpu/motionPass.ts +128 -9
- package/src/render/backend/webgpu/postPass.ts +5 -1
- package/src/render/backend/webgpu/renderer.ts +663 -43
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
- package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
- package/src/render/clothBindingData.ts +89 -0
- package/src/render/clothPlacement.ts +114 -0
- package/src/render/compressedSource.ts +198 -0
- package/src/render/cutoutCaster.ts +5 -0
- package/src/render/cutoutDither.ts +99 -0
- package/src/render/eyeRefraction.ts +72 -0
- package/src/render/glCompressed.ts +86 -0
- package/src/render/hairLobes.ts +188 -0
- package/src/render/lightBudget.ts +25 -0
- package/src/render/mesh.ts +23 -0
- package/src/render/recon/motionVectors.ts +6 -3
- package/src/render/renderQuality.ts +28 -0
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +21 -0
- package/src/render/shaders/clothBinding.ts +81 -0
- package/src/render/shaders/depth.ts +42 -6
- package/src/render/shaders/flat/anisotropic.ts +68 -0
- package/src/render/shaders/flat/eye.ts +82 -0
- package/src/render/shaders/flat/hair.ts +102 -0
- package/src/render/shaders/flat/index.ts +36 -2
- package/src/render/shaders/flat/main.ts +113 -17
- package/src/render/shaders/flat/models.ts +286 -0
- package/src/render/shaders/flat/preamble.ts +26 -24
- package/src/render/shaders/flat/skin.ts +116 -0
- package/src/render/shaders/generated/depth.wgsl.ts +38 -16
- package/src/render/shaders/generated/flat.wgsl.ts +1828 -1742
- package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
- package/src/render/shaders/recon/motion.wgsl.ts +102 -0
- package/src/render/shaders/skinBlur.ts +105 -0
- package/src/render/shaders/skinning.ts +31 -4
- package/src/render/shadowCasters.ts +13 -0
- package/src/render/skinBlur.ts +53 -0
- package/src/render/skinScatter.ts +92 -0
- package/src/render/skinnedClothRun.ts +128 -0
- package/src/render/surfaceModel.ts +274 -0
- package/src/render/surfaceTexture.ts +123 -63
- package/src/render/vertexDefaults.ts +7 -0
- package/dist/math/exact.d.ts +0 -76
- package/dist/math/exact.js +0 -327
- package/src/math/exact.ts +0 -340
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
38
|
-
const
|
|
39
|
-
this.
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
"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.
|
|
33
|
-
"@driftengine/physics": "4.8.
|
|
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.
|
|
71
|
-
"@driftengine/network": "4.8.
|
|
72
|
-
"@driftengine/texture": "4.8.
|
|
70
|
+
"@driftengine/nav": "4.8.5",
|
|
71
|
+
"@driftengine/network": "4.8.5",
|
|
72
|
+
"@driftengine/texture": "4.8.5"
|
|
73
73
|
}
|
|
74
74
|
}
|
|
Binary file
|
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';
|
package/src/math/scalar.ts
CHANGED
|
@@ -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 `
|
|
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
|
-
/**
|
|
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:
|
|
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
|
|
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
|
+
}
|