@driftengine/core 4.8.3 → 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.
- package/CHANGELOG.json +71 -0
- package/README.md +1 -1
- 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/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
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,75 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.8.4",
|
|
4
|
+
"date": "2026-10-05",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "added",
|
|
8
|
+
"short": "A material can name a shading model (anisotropic, hair, skin or eye) with `model`, and carry its own channels in a `modelMap`. A scene that names none compiles none of them.",
|
|
9
|
+
"text": "`SurfaceMaterial.model` takes a frozen descriptor from `anisotropicModel`, `hairModel`, `skinModel` or `eyeModel`, and `modelMap` an image of that model's own channels, read at the albedo's coordinates. Each model is a pipeline constant of its own, off in every pipeline but the ones a material asks for and compiled the first time a draw does, so the standard lit shader is the one it was: every published scene moves 0 pixels on WebGPU, and the lit shader's instruction count and registers hold on RADV. **`anisotropicModel`** is GGX stretched along the mesh's tangent by a strength and turned by a rotation, after `KHR_materials_anisotropy`, and is the standard highlight exactly at strength 0; the environment is reflected about a normal bent toward the stretch, so a brushed disc shows a streak of sky. Hair, skin and the eye ignore the per-vertex `specular`: their highlights are physical, set by their own index of refraction. The models belong to the forward path; a `GpuDrivenMaterial` names none. The manual's materials chapter says what each map channel means."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "added",
|
|
13
|
+
"short": "`hairModel` shades strands along the mesh's tangent: a white highlight toward the root, a coloured one toward the tip, a glow when lit from behind, and light scattered through the hair.",
|
|
14
|
+
"text": "`hairModel({ shift, scatter, backlit })` treats each strand as a rough dielectric cylinder along the tangent, running from root to tip, after Marschner et al. in the real-time form Karis gave: the reflection off the surface, white and shifted toward the root by twice the cuticle's tilt; the light through the strand, which is the glow of a backlit head and is nearly nothing with the light in front; and the light reflected once inside, the coloured second highlight. Each is a normalised longitudinal Gaussian times an azimuthal fit, so a lamp's size widens every lobe without brightening it, and a wrapped scatter term carries the light that has passed through many strands, deepening toward the fibre's hue in a penumbra. The sky and DriftLight reach it through that scatter, and the environment through the reflected lobe at the strand's own Fresnel. `shift` is the tilt in radians, 3° by default; a card whose tangents run tip to root takes it negative. Held by tests to the three lobes' tilted peaks, reciprocity, a white strand returning what it receives to within the fit, and finite answers where the geometry degenerates."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"kind": "added",
|
|
18
|
+
"short": "`skinModel` lights skin as light that leaves it a little way from where it entered: a softer, redder terminator on curved parts, a red edge to a shadow, light through thin parts.",
|
|
19
|
+
"text": "`skinModel({ scatterColor, radius, transmission, profile })` gives each colour its own scatter distance, `radius` times its share of `scatterColor`, and on a curved surface lights a point by its neighbours' angle to the light as well as its own: Burley's diffusion profile averaged around a ring of the surface's curvature, after Penner, evaluated as a closed-form fit rather than looked up, so it costs no texture. The fit splits the average into the cosine's first harmonic and a smoothed absolute value carrying the rest, is exactly Lambert on a flat surface, and stays within 0.0105 of the integral done numerically wherever it is used. The curvature is read from the screen or from the model map; light from behind a thin part comes through it, and a shadow's penumbra lets each colour in as far as it travels. The highlight is two normalised GGX lobes at skin's Fresnel. Measured at a person's scale: a lamp behind a six-millimetre fin moves it red by 99 levels and blue by 8, and the standard model by nothing."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"kind": "added",
|
|
23
|
+
"short": "`skinScattering: 'screen-space'` adds Burley's blur in the picture: skin's diffuse is spread across the pixels its scatter distance covers before anything blended is drawn.",
|
|
24
|
+
"text": "A renderer quality option. Each skin draw writes its specular to the frame and its diffuse to a half-float target of its own — drawn a second time to do it — which is spread along each screen axis by Burley's profile integrated across a line, every tap weighted by the profile's mass over its cell in closed form, refusing taps off the skin's depth and counting part-covered ones for their part, and added back before the frame's first blended draw. Under it the per-pixel fit steps back to Lambert's and the blur does the scattering, so light crosses a shadow's edge and a nostril's rim on screen; light through thin parts stays with the fit. The split adds back to the whole: with a blur narrower than half a pixel the two pictures differ only at silhouettes, on both backends, multisampled or not, and under a pane drawn over the skin. It needs the composite (`screenEffects`) and, on WebGL2, `EXT_color_buffer_float`; without either, skin is pre-integrated and the renderer says so once. What it costs: every skin draw twice, two half-float targets the size of the frame and a third under multisampling, and two full-screen passes that leave at once where no skin was drawn. `profile` picks one of eight scatter distances the blur uses."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"kind": "added",
|
|
28
|
+
"short": "`eyeModel` moves an iris under its cornea as the eye turns, lights it as the shallow cone it is, and puts the cornea's sharp highlight over the whole eye.",
|
|
29
|
+
"text": "`eyeModel({ irisRadius, irisDepth, ior, corneaRoughness, axis, joint })` refracts the eye's ray at the cornea and moves the iris's texture coordinate by how far that ray crosses the iris plane before any map is read, so colour, relief and the model's channels move as one. Measured on a flat cornea at 30°: the iris is seen across 1.21 mm of a 3 mm depth, where parallax alone would put it at 1.73, both as predicted. The iris is lit by the surface normal mirrored about the eye's axis, so light pools on the side away from it, the sclera by its own, and the cornea's highlight — normalised GGX at `corneaRoughness`, the Fresnel of index 1.336 — lies over both. `axis` and `joint` say which way the eye looks and which bone it turns with; the renderer turns the axis per draw."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"kind": "added",
|
|
33
|
+
"short": "`cutoutMode: 'dithered'` gives a cutout a soft edge for hair, lashes and leaves, resolved over time, as multisampled coverage, or as the hard test where neither can.",
|
|
34
|
+
"text": "A dithered cutout keeps a pixel by the share of it the texture covers rather than by a line through it. The material asks for the smooth edge and the frame decides how: under a temporal resolve (TAA or DriftTR) the threshold moves every frame and the resolve averages the pattern into coverage; multisampled, the share goes to the GPU as alpha-to-coverage; with neither, the test is hard, since a dither nothing averages is grain. Shadows and coloured glass shadows dither with a pattern that holds still, and a translucent draw tests hard whatever the material says. `'hard'` stays the default, and every cutout before this draws as it did. Measured on both backends: grain under TAA 11.7, DriftTR 13.1, and the hard test's edge unchanged to the pixel."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"kind": "added",
|
|
38
|
+
"short": "A vertex may follow up to eight bones: `joints2` and `weights2` carry the second four, the importers keep eight heaviest first, and the container writes them at 1.24.",
|
|
39
|
+
"text": "`MeshData.joints2` and `weights2` add a second four influences beside `joints` and `weights`. glTF's `JOINTS_1` and `WEIGHTS_1` are read, and FBX skins with more than four bones a vertex keep eight, sorted heaviest first and normalised across all eight. `.drft` 1.24 writes them after every array a 1.23 reader reads; a 1.23 reader refuses such a mesh rather than drawing it with its second four read as indices. A skinned draw with them takes the eight-influence vertex stage on both backends — lit, shadow-casting and DriftTR's motion — under a pipeline constant inside the existing skinned variant, so a four-influence mesh draws exactly as before and the generated shaders do not double."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"kind": "added",
|
|
43
|
+
"short": "BC1 to BC5 and BC7 textures upload as their blocks where the device samples them, a byte a texel for BC7 against four, and the asset loader decodes them in a worker where it cannot.",
|
|
44
|
+
"text": "`createSurfaceTexture` takes a `CompressedTextureSource` — format, size and the stored mip chain — and uploads it as it is where `renderer.compressedFormats` has the format: WebGPU asks for `texture-compression-bc` wherever the adapter offers it, and WebGL2 reads the four extensions BC is spread across. Core ships no decoder and refuses by name a source the device cannot take; `uploadsCompressed` is the question to ask first. The baker keeps a DDS as BC blocks in `.drft` 1.24's `CODEC_BC` payload (`--decode-dds` keeps PNG for an older reader), and `@driftengine/assets` uploads them as blocks or decodes level 0 in a module worker — on the main thread where no worker can be had — so every device gets the image. A two-channel BC5 normal map keeps two channels and the lit stage rebuilds the third, which also repairs every BC5 map baked to PNG before this release with its blue at zero. The decoders are checked against an independent one: BC7 to the byte across every partition."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"kind": "added",
|
|
48
|
+
"short": "Cloth on a character: a skinned cloth solved on the GPU (on the CPU under WebGL2), with a finer mesh bound to it in the vertex stage, colliders on bones, limits, wind and resets.",
|
|
49
|
+
"text": "`SkinnedCloth` in `@driftengine/physics` is XPBD over particles that carry up to eight bone influences: distance, dihedral bending and tether constraints in graph-colour batches, a max distance, a backstop and a frontstop measured from where skinning puts each particle, spheres and tapered capsules on joints, inertia that lets a free particle keep a share of the character's motion, and a teleport that resets, settles and blends back in — whole fixed steps, drawn on `alpha`, with `setWind` taking the frame's sample of the scene's one wind. `createSkinnedCloth(renderer, setup)` solves it as WebGPU compute, its particles never leaving the device, and as `SkinnedCloth` under WebGL2, one schedule over both. A skinned mesh bound to it with `createClothBinding` and `setCloth` is placed by the cloth in its vertex stage — lit, shadow and DriftTR's motion alike — so a garment uploads nothing a frame. On an RX 9070 XT, 4,500 particles and 40,000 bound vertices take 0.65 ms a frame on the device against 20 ms on the CPU. `examples/garment/` builds a cape by hand; the handbook maps a cooked garment onto the two arrays. Self-collision is accepted in a set-up and not simulated."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"kind": "changed",
|
|
53
|
+
"short": "`updateMesh` uploads the positions and normals it is given and nothing else on WebGPU: a dynamic mesh no longer keeps a copy of its whole vertex buffer to resend every frame.",
|
|
54
|
+
"text": "A dynamic mesh kept a CPU copy of its interleaved vertex buffer, patched the positions and normals into it and sent all of it, reported at 2.8 MB a frame for one character's garments. Positions and normals are buffers of their own now, an update is one write of each, and no copy is kept; last frame's positions, for DriftTR's motion, are copied on the device ahead of the write. The picture is identical to the pixel, plain and under DriftTR."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"kind": "fixed",
|
|
58
|
+
"short": "A turned, skinned or instanced mesh with tangents lit its normal map from the wrong side: the tangent now leaves the vertex stage in world space, as the normal does.",
|
|
59
|
+
"text": "The lit vertex stage handed the fragment its tangent in the mesh's own space and the tangent frame crossed it with the world-space normal, so any mesh with stored tangents that was not drawn unturned lit its normal map from a direction that turned with it. The tangent is now turned as the normal is: by the joints, the model or instance matrix, and a cloth. Measured on a mapped panel turned a quarter: 80.4 rms from the derived reference before, 14.8 now — what it is unturned — on both backends. Every published scene is unchanged on WebGPU."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"kind": "fixed",
|
|
63
|
+
"short": "On WebGL2 a draw no longer loses the material set before it when a lit shader is compiled in the middle of a frame, as when a feature turns on or a new kind of draw first appears.",
|
|
64
|
+
"text": "Feeding a lit program compiled mid-frame its pass state also reset the renderer's current material, so the draws after it — until the next `setMaterial` — drew with no maps. The pass's material is reset where the pass begins now, and a program compiled mid-frame is handed the material that is current."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"kind": "fixed",
|
|
68
|
+
"short": "The baker no longer bakes a DDS it finds elsewhere in a bundle, rather than beside the model, as a single white pixel.",
|
|
69
|
+
"text": "A texture the baker found by searching a bundle went through a path that knew PNG, JPEG and WEBP and nothing else, so a DDS there was refused as an image and baked white. Both paths share one now, and a DDS is kept as BC blocks or decoded like any other."
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
2
73
|
{
|
|
3
74
|
"version": "4.8.3",
|
|
4
75
|
"date": "2026-10-04",
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ carries the loop, the renderer, cameras, input, the scene graph, geometry, text
|
|
|
6
6
|
and the whole of [`@driftengine/physics`](https://driftengine.dev/docs/simulation/rigid-bodies),
|
|
7
7
|
which it re-exports. The rest of the engine is separate packages you add when you need them.
|
|
8
8
|
|
|
9
|
-
**Cost:
|
|
9
|
+
**Cost: 777.5 KB gzipped, importing `createRenderer`.** Measured by `scripts/size-gate.test.mjs`,
|
|
10
10
|
which fails the build if the figure drifts by more than 3%.
|
|
11
11
|
|
|
12
12
|
The manual is at **[driftengine.dev/docs](https://driftengine.dev/docs)**, with an example running
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export type { LoopHooks, FrameSource, LoopOptions } from './core/loop.ts';
|
|
|
5
5
|
export { StepBudget } from './core/stepBudget.ts';
|
|
6
6
|
export type { StepBudgetOptions } from './core/stepBudget.ts';
|
|
7
7
|
export { hashToUnit, mulberry32, pickBySeed, savableMulberry32 } from './core/rng.ts';
|
|
8
|
-
export { exactAcos, exactCos, exactExp, exactLog, exactSin } from './math/exact.ts';
|
|
9
8
|
export type { SavableRandom } from './core/rng.ts';
|
|
10
9
|
export { TickTrace } from './core/tickTrace.ts';
|
|
11
10
|
export type { TickTraceLike } from './core/tickTrace.ts';
|
|
@@ -148,7 +147,12 @@ export { GRADE_PLACEHOLDER_SIZE, MAX_GRADE_SIZE, identityGradeLut } from './rend
|
|
|
148
147
|
export type { ColourGradeLut } from './render/colourGrade.ts';
|
|
149
148
|
export { probeDevice } from './render/backend/probe.ts';
|
|
150
149
|
export type { ProbeVerdict } from './render/backend/probe.ts';
|
|
151
|
-
export type { BoltHandle, CausticsHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
150
|
+
export type { BoltHandle, CausticsHandle, ClothBindingHandle, ClothParticlesHandle, FlockHandle, IncrementalMeshHandle, LineHandle, MeshHandle, MeshOptions, RenderBackend, RendererApi, ParticleHandle, PlumeHandle, ScatterHandle, InstancedHandle, SdfTextHandle, SurfaceTextureHandle, TextHandle, WaterHandle, WindStreakHandle, } from './render/backend/api.ts';
|
|
151
|
+
export type { ClothBindingData } from './render/clothBindingData.ts';
|
|
152
|
+
export { anisotropicModel, eyeModel, hairModel, skinModel } from './render/surfaceModel.ts';
|
|
153
|
+
export type { AnisotropicModel, EyeModel, HairModel, SkinModel, SurfaceModel, SurfaceModelKind, } from './render/surfaceModel.ts';
|
|
154
|
+
export { createSkinnedCloth } from './render/skinnedClothRun.ts';
|
|
155
|
+
export type { SkinnedClothSolver } from './render/skinnedClothRun.ts';
|
|
152
156
|
/**
|
|
153
157
|
* What a frame asked of a backend, against the ceilings that backend imposes.
|
|
154
158
|
*
|
|
@@ -157,7 +161,7 @@ export type { BoltHandle, CausticsHandle, FlockHandle, IncrementalMeshHandle, Li
|
|
|
157
161
|
*/
|
|
158
162
|
export type { BudgetLine, FrameBudget } from './render/backend/budget.ts';
|
|
159
163
|
export type { FrameTimer } from './render/backend/timer.ts';
|
|
160
|
-
export type { SceneCasterMaterial, SceneCasters, ShadowCasterSink, ShadowCasters, } from './render/shadowCasters.ts';
|
|
164
|
+
export type { SceneCasterMaterial, SceneCasters, ShadowCasterCloth, ShadowCasterSink, ShadowCasters, } from './render/shadowCasters.ts';
|
|
161
165
|
export type { Atmosphere, UnderwaterAtmosphere } from './render/atmosphere.ts';
|
|
162
166
|
export { createImageTexelDecoder } from './render/imageTexels.ts';
|
|
163
167
|
export type { ImageTexelDecoder, ImageTexels } from './render/imageTexels.ts';
|
|
@@ -204,11 +208,13 @@ export type { PrefilterQuality } from './render/prefilterEnvMap.ts';
|
|
|
204
208
|
* `ProbeBakeOptions`, which is the whole of the reasoning.
|
|
205
209
|
*/
|
|
206
210
|
export type { ProbeBakeOptions } from './render/reflectionProbe.ts';
|
|
207
|
-
export type { DirectionalShadowDepthLayers, GlassShadows, OutputTransform, RenderQuality, RenderQualityOptions, ShadowFilterTaps, WaterReflectionFilterTaps, } from './render/renderQuality.ts';
|
|
211
|
+
export type { DirectionalShadowDepthLayers, GlassShadows, OutputTransform, RenderQuality, RenderQualityOptions, ShadowFilterTaps, SkinScattering, WaterReflectionFilterTaps, } from './render/renderQuality.ts';
|
|
208
212
|
export { MAX_POINT_LIGHTS, SURFACE_TEXTURE_UNIT } from './render/lightBudget.ts';
|
|
209
213
|
export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.ts';
|
|
210
214
|
export { SurfaceTexture } from './render/surfaceTexture.ts';
|
|
211
215
|
export type { SurfaceMaterial, SurfaceTextureOptions } from './render/surfaceTexture.ts';
|
|
216
|
+
export { compressedFormatName, isCompressedSource, uploadsCompressed, } from './render/compressedSource.ts';
|
|
217
|
+
export type { BlockFormat, CompressedTextureFormat, CompressedTextureSource, SurfaceSource, } from './render/compressedSource.ts';
|
|
212
218
|
export { DEFAULT_GOVERNOR_LIMITS, ResolutionGovernor } from './render/resolutionGovernor.ts';
|
|
213
219
|
export type { GovernorLimits } from './render/resolutionGovernor.ts';
|
|
214
220
|
export { isWeakGpuFamily } from './render/gpuCapability.ts';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
export { startLoop } from './core/loop.js';
|
|
4
4
|
export { StepBudget } from './core/stepBudget.js';
|
|
5
5
|
export { hashToUnit, mulberry32, pickBySeed, savableMulberry32 } from './core/rng.js';
|
|
6
|
-
export { exactAcos, exactCos, exactExp, exactLog, exactSin } from './math/exact.js';
|
|
7
6
|
export { TickTrace } from './core/tickTrace.js';
|
|
8
7
|
export { BrowserStore, MemoryStore, defaultStore } from './core/storage.js';
|
|
9
8
|
/* The same seam over a backend that answers later, with the queue, the retry and the status. */
|
|
@@ -211,6 +210,17 @@ export { placeCascade } from './render/gi/globalField.js';
|
|
|
211
210
|
*/
|
|
212
211
|
export { GRADE_PLACEHOLDER_SIZE, MAX_GRADE_SIZE, identityGradeLut } from './render/colourGrade.js';
|
|
213
212
|
export { probeDevice } from './render/backend/probe.js';
|
|
213
|
+
/*
|
|
214
|
+
* A material's shading model, for what the standard one is not: brushed metal, hair, skin, an eye.
|
|
215
|
+
* A frozen descriptor made by a factory and set as `SurfaceMaterial.model`; each is a pipeline of
|
|
216
|
+
* its own, compiled the first time a draw asks.
|
|
217
|
+
*/
|
|
218
|
+
export { anisotropicModel, eyeModel, hairModel, skinModel } from './render/surfaceModel.js';
|
|
219
|
+
/*
|
|
220
|
+
* A skinned cloth solved where the renderer can: compute on WebGPU, the CPU under WebGL2. A function
|
|
221
|
+
* rather than a renderer method so only a game that imports it carries the solver.
|
|
222
|
+
*/
|
|
223
|
+
export { createSkinnedCloth } from './render/skinnedClothRun.js';
|
|
214
224
|
/*
|
|
215
225
|
* Reading an image's texels back exactly, which no canvas and no `VideoFrame` will do.
|
|
216
226
|
*
|
|
@@ -260,6 +270,11 @@ export { MAX_POINT_LIGHTS, SURFACE_TEXTURE_UNIT } from './render/lightBudget.js'
|
|
|
260
270
|
/* The width a light buffer can usefully be with `clusteredLights` on; `createPointLightBuffer`. */
|
|
261
271
|
export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.js';
|
|
262
272
|
export { SurfaceTexture } from './render/surfaceTexture.js';
|
|
273
|
+
/*
|
|
274
|
+
* BC compressed textures: the source `createSurfaceTexture` takes as blocks, and the one question to
|
|
275
|
+
* ask before handing one over — whether this device takes its format (`renderer.compressedFormats`).
|
|
276
|
+
*/
|
|
277
|
+
export { compressedFormatName, isCompressedSource, uploadsCompressed, } from './render/compressedSource.js';
|
|
263
278
|
export { DEFAULT_GOVERNOR_LIMITS, ResolutionGovernor } from './render/resolutionGovernor.js';
|
|
264
279
|
export { isWeakGpuFamily } from './render/gpuCapability.js';
|
|
265
280
|
/**
|
package/dist/math/scalar.js
CHANGED
|
@@ -26,7 +26,7 @@ export function damp(a, b, lambda, dt) {
|
|
|
26
26
|
* **What a simulation wants instead**, if it ever wants exponential approach inside the fixed
|
|
27
27
|
* step, is a per-tick factor computed once from `lambda` and the fixed `dt` and passed in as a
|
|
28
28
|
* number: at a constant step the factor is a constant, so nothing has to call `exp` on a tick at
|
|
29
|
-
* all. `exactExp` in `
|
|
29
|
+
* all. `exactExp` in `@driftengine/physics`' `exact.ts` is the other answer, and is the one to reach for when the
|
|
30
30
|
* rate itself varies.
|
|
31
31
|
*/
|
|
32
32
|
// determinism: build-time — presentation smoothing; see the note above for a simulation
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The two widths, along the direction and across it, into `out` as [alongT, acrossB]. */
|
|
2
|
+
export declare function anisotropicAlphas(roughness: number, strength: number, out: Float64Array): void;
|
|
3
|
+
/**
|
|
4
|
+
* The lobe at a half vector whose components along the direction, across it and along the normal
|
|
5
|
+
* are `th`, `bh` and `nh`. 1 at the mirror direction.
|
|
6
|
+
*/
|
|
7
|
+
export declare function anisotropicLobe(th: number, bh: number, nh: number, alphaT: number, alphaB: number): number;
|
|
@@ -0,0 +1,33 @@
|
|
|
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.js';
|
|
18
|
+
/** The two widths, along the direction and across it, into `out` as [alongT, acrossB]. */
|
|
19
|
+
export function anisotropicAlphas(roughness, strength, out) {
|
|
20
|
+
const alpha = Math.max(roughness * roughness, MIN_LOBE_ALPHA);
|
|
21
|
+
out[0] = alpha + (1 - alpha) * strength * strength;
|
|
22
|
+
out[1] = alpha;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The lobe at a half vector whose components along the direction, across it and along the normal
|
|
26
|
+
* are `th`, `bh` and `nh`. 1 at the mirror direction.
|
|
27
|
+
*/
|
|
28
|
+
export function anisotropicLobe(th, bh, nh, alphaT, alphaB) {
|
|
29
|
+
const x = th / alphaT;
|
|
30
|
+
const y = bh / alphaB;
|
|
31
|
+
const d = x * x + y * y + nh * nh;
|
|
32
|
+
return 1 / Math.max(d * d, 1e-8);
|
|
33
|
+
}
|
|
@@ -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';
|
|
@@ -37,6 +39,15 @@ export interface MeshHandle {
|
|
|
37
39
|
*/
|
|
38
40
|
readonly complete: boolean;
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A mesh's cloth binding on the device, from `createClothBinding`: which simulation triangle each
|
|
44
|
+
* vertex follows. Opaque, for the reason `MeshHandle` gives.
|
|
45
|
+
*/
|
|
46
|
+
export interface ClothBindingHandle {
|
|
47
|
+
}
|
|
48
|
+
/** One character's cloth particles on the device, from `createClothParticles`. Opaque likewise. */
|
|
49
|
+
export interface ClothParticlesHandle {
|
|
50
|
+
}
|
|
40
51
|
/** A mesh on its way to the device, and the iterator that gets it there. */
|
|
41
52
|
export interface IncrementalMeshHandle {
|
|
42
53
|
/**
|
|
@@ -175,9 +186,35 @@ export interface MeshOptions {
|
|
|
175
186
|
}
|
|
176
187
|
export type RendererApi = Omit<{
|
|
177
188
|
[K in keyof Renderer]: Renderer[K];
|
|
178
|
-
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial'> & {
|
|
179
|
-
/**
|
|
180
|
-
|
|
189
|
+
}, 'gpuTimer' | 'createMesh' | 'createMeshIncremental' | 'updateMesh' | 'drawMesh' | 'disposeMesh' | 'drawTranslucentMesh' | 'drawLightVolume' | 'drawFilm' | 'createScatter' | 'drawScatter' | 'uploadScatter' | 'disposeScatter' | 'createInstanced' | 'uploadInstanced' | 'drawInstanced' | 'drawTranslucentInstanced' | 'disposeInstanced' | 'createWater' | 'drawWater' | 'disposeWater' | 'createPlumes' | 'drawPlumes' | 'disposePlumes' | 'setPlumeScale' | 'createCaustics' | 'drawCaustics' | 'disposeCaustics' | 'createParticles' | 'drawParticles' | 'disposeParticles' | 'createWindStreaks' | 'drawWindStreaks' | 'disposeWindStreaks' | 'createFlock' | 'drawFlock' | 'disposeFlock' | 'createBolts' | 'drawBolts' | 'disposeBolts' | 'createLines' | 'drawLines' | 'disposeLines' | 'createText' | 'setText' | 'setPlate' | 'drawText' | 'textWidth' | 'disposeText' | 'createSdfText' | 'setSdfText' | 'drawSdfText' | 'disposeSdfText' | 'createSurfaceTexture' | 'createSurfaceTextureArray' | 'updateSurfaceTexture' | 'disposeSurfaceTexture' | 'setSurfaceTexture' | 'setMaterial' | 'createClothBinding' | 'createClothParticles' | 'updateClothParticles' | 'setCloth' | 'disposeClothBinding' | 'disposeClothParticles'> & {
|
|
190
|
+
/**
|
|
191
|
+
* A mesh's cloth binding: which simulation triangle each of its vertices follows, where on it and
|
|
192
|
+
* by how much (`ClothBindingData`). The mesh must be skinned — a binding blends each vertex
|
|
193
|
+
* between its skinning and the cloth — and is checked against the binding by name.
|
|
194
|
+
*/
|
|
195
|
+
createClothBinding(mesh: MeshHandle, data: ClothBindingData): ClothBindingHandle;
|
|
196
|
+
/** One character's particles, `count` of them, updated every frame with `updateClothParticles`. */
|
|
197
|
+
createClothParticles(count: number): ClothParticlesHandle;
|
|
198
|
+
/**
|
|
199
|
+
* This frame's particles, three floats each, world space — a simulation's output as it is.
|
|
200
|
+
* **Once a frame, before the character's draws**: a second update in one frame reaches every
|
|
201
|
+
* draw of it on WebGPU, whose texture writes land ahead of the frame's commands. Last frame's
|
|
202
|
+
* are kept for the motion a temporal resolve needs, and a frame with no update did not move.
|
|
203
|
+
*/
|
|
204
|
+
updateClothParticles(particles: ClothParticlesHandle, positions: Float32Array): void;
|
|
205
|
+
/**
|
|
206
|
+
* Place the skinned draws that follow — lit and cast — by a cloth: the mesh's binding and the
|
|
207
|
+
* character's particles. Null for none, which every draw is until this is called; per-draw state,
|
|
208
|
+
* as `setSkinPalette` is.
|
|
209
|
+
*/
|
|
210
|
+
setCloth(binding: ClothBindingHandle | null, particles?: ClothParticlesHandle | null): void;
|
|
211
|
+
disposeClothBinding(binding: ClothBindingHandle): void;
|
|
212
|
+
disposeClothParticles(particles: ClothParticlesHandle): void;
|
|
213
|
+
/**
|
|
214
|
+
* Upload a caller's image, or a BC image's blocks with their stored chain where
|
|
215
|
+
* `compressedFormats` has the format. See `SurfaceTextureHandle` for the boundary this guards.
|
|
216
|
+
*/
|
|
217
|
+
createSurfaceTexture(source: SurfaceSource, options?: Parameters<Renderer['createSurfaceTexture']>[1]): SurfaceTextureHandle;
|
|
181
218
|
/**
|
|
182
219
|
* Upload several images of one size as the layers of one array texture.
|
|
183
220
|
*
|
|
@@ -191,7 +228,7 @@ export type RendererApi = Omit<{
|
|
|
191
228
|
* refusal names the first image that differs. A layer past the end reads the last one.
|
|
192
229
|
* `updateSurfaceTexture` refuses an array.
|
|
193
230
|
*/
|
|
194
|
-
createSurfaceTextureArray(sources: readonly
|
|
231
|
+
createSurfaceTextureArray(sources: readonly SurfaceSource[], options?: Parameters<Renderer['createSurfaceTexture']>[1]): SurfaceTextureHandle;
|
|
195
232
|
/**
|
|
196
233
|
* Replace a texture's pixels, keeping the GPU object and its sampler state.
|
|
197
234
|
*
|
|
@@ -206,6 +206,14 @@ async function acquireWebGpu(gpu, wantsGpuTiming, shaders, stage) {
|
|
|
206
206
|
if (wantsGpuTiming && adapter.features?.has('timestamp-query') === true) {
|
|
207
207
|
features.push('timestamp-query');
|
|
208
208
|
}
|
|
209
|
+
/*
|
|
210
|
+
* BC compressed textures, on the same terms: asked for wherever the adapter offers it, because
|
|
211
|
+
* a device without it still draws — the loader decodes for it — and one with it samples a BC7
|
|
212
|
+
* texture at a byte a texel instead of four. See `compressedUpload.ts`.
|
|
213
|
+
*/
|
|
214
|
+
if (adapter.features?.has('texture-compression-bc') === true) {
|
|
215
|
+
features.push('texture-compression-bc');
|
|
216
|
+
}
|
|
209
217
|
const descriptor = {};
|
|
210
218
|
if (Object.keys(required).length > 0)
|
|
211
219
|
descriptor.requiredLimits = required;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ClothBindingData } from '../../clothBindingData.ts';
|
|
2
|
+
/** A mesh's binding and its particles at rest. Validated by the renderer against the mesh. */
|
|
3
|
+
export declare class GlClothBinding {
|
|
4
|
+
readonly binding: WebGLTexture;
|
|
5
|
+
readonly rest: WebGLTexture;
|
|
6
|
+
/** How many particles the binding names. */
|
|
7
|
+
readonly particles: number;
|
|
8
|
+
constructor(gl: WebGL2RenderingContext, data: ClothBindingData);
|
|
9
|
+
dispose(gl: WebGL2RenderingContext): void;
|
|
10
|
+
}
|
|
11
|
+
/** One character's particles: this frame's and the last, in two textures that swap. */
|
|
12
|
+
export declare class GlClothParticles {
|
|
13
|
+
readonly count: number;
|
|
14
|
+
private readonly width;
|
|
15
|
+
private readonly height;
|
|
16
|
+
private readonly staging;
|
|
17
|
+
private readonly textures;
|
|
18
|
+
private latest;
|
|
19
|
+
constructor(gl: WebGL2RenderingContext, count: number);
|
|
20
|
+
/** This frame's particles. */
|
|
21
|
+
get current(): WebGLTexture;
|
|
22
|
+
/** Last frame's. */
|
|
23
|
+
get previous(): WebGLTexture;
|
|
24
|
+
/** Write `positions` (three floats a particle, world space) as this frame's; last frame's stays. */
|
|
25
|
+
update(gl: WebGL2RenderingContext, positions: Float32Array): void;
|
|
26
|
+
dispose(gl: WebGL2RenderingContext): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
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.js';
|
|
13
|
+
function floatTexture(gl, width, height, texels) {
|
|
14
|
+
const texture = gl.createTexture();
|
|
15
|
+
if (texture === null)
|
|
16
|
+
throw new Error('cloth: createTexture failed');
|
|
17
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
18
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, width, height);
|
|
19
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
20
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
21
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
22
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
23
|
+
if (texels !== null) {
|
|
24
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.FLOAT, texels);
|
|
25
|
+
}
|
|
26
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
27
|
+
return texture;
|
|
28
|
+
}
|
|
29
|
+
/** A mesh's binding and its particles at rest. Validated by the renderer against the mesh. */
|
|
30
|
+
export class GlClothBinding {
|
|
31
|
+
binding;
|
|
32
|
+
rest;
|
|
33
|
+
/** How many particles the binding names. */
|
|
34
|
+
particles;
|
|
35
|
+
constructor(gl, data) {
|
|
36
|
+
const vertices = data.weights.length;
|
|
37
|
+
const size = clothTextureSize(vertices * 2);
|
|
38
|
+
this.binding = floatTexture(gl, size.width, size.height, packClothBinding(data));
|
|
39
|
+
this.particles = data.rest.length / 3;
|
|
40
|
+
const rest = clothTextureSize(this.particles);
|
|
41
|
+
const texels = new Float32Array(rest.width * rest.height * 4);
|
|
42
|
+
packClothParticles(data.rest, texels);
|
|
43
|
+
this.rest = floatTexture(gl, rest.width, rest.height, texels);
|
|
44
|
+
}
|
|
45
|
+
dispose(gl) {
|
|
46
|
+
gl.deleteTexture(this.binding);
|
|
47
|
+
gl.deleteTexture(this.rest);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** One character's particles: this frame's and the last, in two textures that swap. */
|
|
51
|
+
export class GlClothParticles {
|
|
52
|
+
count;
|
|
53
|
+
width;
|
|
54
|
+
height;
|
|
55
|
+
staging;
|
|
56
|
+
textures;
|
|
57
|
+
latest = 0;
|
|
58
|
+
constructor(gl, count) {
|
|
59
|
+
this.count = count;
|
|
60
|
+
const size = clothTextureSize(count);
|
|
61
|
+
this.width = size.width;
|
|
62
|
+
this.height = size.height;
|
|
63
|
+
this.staging = new Float32Array(size.width * size.height * 4);
|
|
64
|
+
this.textures = [
|
|
65
|
+
floatTexture(gl, size.width, size.height, this.staging),
|
|
66
|
+
floatTexture(gl, size.width, size.height, this.staging),
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
/** This frame's particles. */
|
|
70
|
+
get current() {
|
|
71
|
+
return this.textures[this.latest];
|
|
72
|
+
}
|
|
73
|
+
/** Last frame's. */
|
|
74
|
+
get previous() {
|
|
75
|
+
return this.textures[1 - this.latest];
|
|
76
|
+
}
|
|
77
|
+
/** Write `positions` (three floats a particle, world space) as this frame's; last frame's stays. */
|
|
78
|
+
update(gl, positions) {
|
|
79
|
+
if (positions.length !== this.count * 3) {
|
|
80
|
+
throw new Error(`cloth: ${this.count} particles and an update of ${positions.length / 3}; a cloth's ` +
|
|
81
|
+
'particle count is fixed when its particles are created');
|
|
82
|
+
}
|
|
83
|
+
packClothParticles(positions, this.staging);
|
|
84
|
+
this.latest = 1 - this.latest;
|
|
85
|
+
gl.bindTexture(gl.TEXTURE_2D, this.current);
|
|
86
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, gl.RGBA, gl.FLOAT, this.staging);
|
|
87
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
88
|
+
}
|
|
89
|
+
dispose(gl) {
|
|
90
|
+
gl.deleteTexture(this.textures[0]);
|
|
91
|
+
gl.deleteTexture(this.textures[1]);
|
|
92
|
+
}
|
|
93
|
+
}
|