@driftengine/core 4.9.0 → 4.10.0
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 +62 -11
- package/README.md +43 -38
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -3
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +90 -2
- package/dist/render/backend/webgl2/renderer.js +253 -24
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +136 -20
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/renderer.d.ts +152 -35
- package/dist/render/backend/webgpu/renderer.js +787 -142
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +241 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/index.d.ts +6 -0
- package/dist/render/shaders/flat/index.js +27 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +15 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.js +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +38 -24
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3077 -2903
- package/dist/render/shaders/generated/flat.wgsl.js +3100 -2925
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceTexture.d.ts +38 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/package.json +6 -6
- package/src/index.ts +8 -2
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +315 -24
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +42 -16
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +22 -4
- package/src/render/backend/webgpu/flatPass.ts +187 -14
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/renderer.ts +928 -155
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/index.ts +33 -5
- package/src/render/shaders/flat/main.ts +15 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +1 -1
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3100 -2925
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceTexture.ts +94 -27
|
@@ -77,7 +77,7 @@ import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
|
77
77
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
78
78
|
import { equirectToCubeFaces } from '../../equirectToCube.js';
|
|
79
79
|
import { SKY_FRAG, skyVertFor } from '../../shaders/sky.js';
|
|
80
|
-
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
80
|
+
import { DEPTH_CUTOUT_FRAG, DEPTH_CUTOUT_VERT, DEPTH_FRAG, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_INSTANCED_VERT, DEPTH_SKINNED_VERT, DEPTH_VERT, } from '../../shaders/depth.js';
|
|
81
81
|
import { OCTAHEDRAL_EDGE, PointShadowArray, strideLayers } from '../../pointShadowArray.js';
|
|
82
82
|
import { SunShadowArray } from '../../shadowMap.js';
|
|
83
83
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem } from '../../pointShadowSystem.js';
|
|
@@ -85,6 +85,7 @@ import { castsDepth, glassOf } from '../../shadowCasters.js';
|
|
|
85
85
|
import { GlassCasterList } from '../../glassCasters.js';
|
|
86
86
|
import { SunGlassTint } from '../../sunGlassTint.js';
|
|
87
87
|
import { GlassTintPrograms } from './glassTintPrograms.js';
|
|
88
|
+
import { captureStaticDraws, replayStaticDraws } from '../../staticDraws.js';
|
|
88
89
|
import { FACE_COUNT, PointShadowMap } from '../../pointShadowMap.js';
|
|
89
90
|
import { createResolvedPointShadows, DEFAULT_SOURCE_RADIUS } from '../../pointShadowImage.js';
|
|
90
91
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
@@ -117,6 +118,9 @@ import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_
|
|
|
117
118
|
import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
118
119
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
119
120
|
import { InstancedBatch } from './instanced.js';
|
|
121
|
+
import { GlBoneAnimation } from './boneAnimations.js';
|
|
122
|
+
import { packBoneAnimation } from '../../boneAnimation.js';
|
|
123
|
+
import { BONE_PLACES_TEXTURE_UNIT, BONE_TURNS_TEXTURE_UNIT, INSTANCE_CLOCKS_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
120
124
|
import { cutoutOf } from '../../cutoutCaster.js';
|
|
121
125
|
import { CUTOUT_RESOLVE_CODE, resolveCutout } from '../../cutoutDither.js';
|
|
122
126
|
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
@@ -802,6 +806,11 @@ export class WebGL2Renderer {
|
|
|
802
806
|
depthCutoutUniforms;
|
|
803
807
|
depthInstancedCutoutProgram;
|
|
804
808
|
depthInstancedCutoutUniforms;
|
|
809
|
+
/** A crowd's casters: the instanced depth programs that play a bone animation. */
|
|
810
|
+
depthAnimatedProgram;
|
|
811
|
+
depthAnimatedUniforms;
|
|
812
|
+
depthAnimatedCutoutProgram;
|
|
813
|
+
depthAnimatedCutoutUniforms;
|
|
805
814
|
scatterDepthProgram;
|
|
806
815
|
scatterDepthUniforms;
|
|
807
816
|
/** The sun's static, moving and peeled maps, as layers of one array. See `shadowMap.ts`. */
|
|
@@ -1205,14 +1214,21 @@ export class WebGL2Renderer {
|
|
|
1205
1214
|
const count = Math.min(data.count, gpuBatch.capacity);
|
|
1206
1215
|
if (count === 0)
|
|
1207
1216
|
return;
|
|
1217
|
+
const clip = gpuBatch.animation;
|
|
1218
|
+
if (clip !== null && clip.disposed)
|
|
1219
|
+
return;
|
|
1208
1220
|
this.shadowDrawBudget.ask();
|
|
1209
1221
|
const twoSided = material?.doubleSided === true;
|
|
1210
1222
|
if (twoSided)
|
|
1211
1223
|
gl.disable(gl.CULL_FACE);
|
|
1212
1224
|
const cutout = cutoutOf(material);
|
|
1213
1225
|
if (cutout !== null) {
|
|
1214
|
-
|
|
1215
|
-
this.
|
|
1226
|
+
/* A crowd casts where its clip has moved it, through the animated variant. */
|
|
1227
|
+
const uniforms = clip === null ? this.depthInstancedCutoutUniforms : this.depthAnimatedCutoutUniforms;
|
|
1228
|
+
gl.useProgram(clip === null ? this.depthInstancedCutoutProgram : this.depthAnimatedCutoutProgram);
|
|
1229
|
+
this.bindCutoutDepth(uniforms, cutout);
|
|
1230
|
+
if (clip !== null)
|
|
1231
|
+
this.bindAnimation(uniforms, gpuBatch);
|
|
1216
1232
|
gpuBatch.draw(gl, count);
|
|
1217
1233
|
if (twoSided && this.depthPassCullsFaces)
|
|
1218
1234
|
gl.enable(gl.CULL_FACE);
|
|
@@ -1220,12 +1236,14 @@ export class WebGL2Renderer {
|
|
|
1220
1236
|
this.restoreSurfaceTextureUnit();
|
|
1221
1237
|
return;
|
|
1222
1238
|
}
|
|
1223
|
-
const u = this.depthInstancedUniforms;
|
|
1224
|
-
gl.useProgram(this.depthInstancedProgram);
|
|
1239
|
+
const u = clip === null ? this.depthInstancedUniforms : this.depthAnimatedUniforms;
|
|
1240
|
+
gl.useProgram(clip === null ? this.depthInstancedProgram : this.depthAnimatedProgram);
|
|
1225
1241
|
gl.uniformMatrix4fv(u['uLightViewProj'] ?? null, false, this.activeDepthViewProj);
|
|
1226
1242
|
gl.uniform1i(u['uPeelShadowLayer'] ?? null, this.activeDepthPeel ? 1 : 0);
|
|
1227
1243
|
if (this.activeDepthPeel)
|
|
1228
1244
|
gl.uniform1i(u['uPreviousShadowMap'] ?? null, 0);
|
|
1245
|
+
if (clip !== null)
|
|
1246
|
+
this.bindAnimation(u, gpuBatch);
|
|
1229
1247
|
gpuBatch.draw(gl, count);
|
|
1230
1248
|
if (twoSided && this.depthPassCullsFaces)
|
|
1231
1249
|
gl.enable(gl.CULL_FACE);
|
|
@@ -2230,10 +2248,15 @@ export class WebGL2Renderer {
|
|
|
2230
2248
|
this.depthCutoutUniforms = uniformLocations(gl, this.depthCutoutProgram, 'depth-cutout');
|
|
2231
2249
|
this.depthInstancedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-cutout');
|
|
2232
2250
|
this.depthInstancedCutoutUniforms = uniformLocations(gl, this.depthInstancedCutoutProgram, 'depth-instanced-cutout');
|
|
2251
|
+
this.depthAnimatedProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_VERT, DEPTH_FRAG, 'depth-instanced-animated');
|
|
2252
|
+
this.depthAnimatedUniforms = uniformLocations(gl, this.depthAnimatedProgram, 'depth-instanced-animated');
|
|
2253
|
+
this.depthAnimatedCutoutProgram = compileProgram(gl, DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT, DEPTH_CUTOUT_FRAG, 'depth-instanced-animated-cutout');
|
|
2254
|
+
this.depthAnimatedCutoutUniforms = uniformLocations(gl, this.depthAnimatedCutoutProgram, 'depth-instanced-animated-cutout');
|
|
2233
2255
|
/* The cutout map is always read from the surface unit, which the draw borrows and gives back. */
|
|
2234
2256
|
for (const [program, uniforms] of [
|
|
2235
2257
|
[this.depthCutoutProgram, this.depthCutoutUniforms],
|
|
2236
2258
|
[this.depthInstancedCutoutProgram, this.depthInstancedCutoutUniforms],
|
|
2259
|
+
[this.depthAnimatedCutoutProgram, this.depthAnimatedCutoutUniforms],
|
|
2237
2260
|
]) {
|
|
2238
2261
|
gl.useProgram(program);
|
|
2239
2262
|
gl.uniform1i(uniforms['uCutoutMap'] ?? null, SURFACE_TEXTURE_UNIT);
|
|
@@ -2401,6 +2424,8 @@ export class WebGL2Renderer {
|
|
|
2401
2424
|
this.lightVolumeProgram,
|
|
2402
2425
|
this.depthCutoutProgram,
|
|
2403
2426
|
this.depthInstancedCutoutProgram,
|
|
2427
|
+
this.depthAnimatedProgram,
|
|
2428
|
+
this.depthAnimatedCutoutProgram,
|
|
2404
2429
|
]) {
|
|
2405
2430
|
gl.deleteProgram(program);
|
|
2406
2431
|
}
|
|
@@ -2718,7 +2743,7 @@ export class WebGL2Renderer {
|
|
|
2718
2743
|
updateSurfaceTexture(texture, source) {
|
|
2719
2744
|
if (this.contextLost)
|
|
2720
2745
|
return;
|
|
2721
|
-
texture.update(this.gl, source);
|
|
2746
|
+
texture.update(this.gl, source, this.compressedFormats);
|
|
2722
2747
|
/* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
|
|
2723
2748
|
if (texture === this.overlayMaps)
|
|
2724
2749
|
this.bindOverlayMaps(texture);
|
|
@@ -2929,6 +2954,13 @@ export class WebGL2Renderer {
|
|
|
2929
2954
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2930
2955
|
this.materialHasMaps = materialHasMaps(material);
|
|
2931
2956
|
this.currentMaterial = material;
|
|
2957
|
+
const reflectivity = material?.reflectivity;
|
|
2958
|
+
const environmentGain = material?.environmentGain;
|
|
2959
|
+
this.materialReflectivity =
|
|
2960
|
+
reflectivity === undefined ? null : Math.min(1, Math.max(0, reflectivity));
|
|
2961
|
+
this.materialEnvironmentGain =
|
|
2962
|
+
environmentGain === undefined ? null : Math.max(0, environmentGain);
|
|
2963
|
+
this.writeEnvironmentDials();
|
|
2932
2964
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
2933
2965
|
const model = material?.model;
|
|
2934
2966
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
@@ -2966,7 +2998,8 @@ export class WebGL2Renderer {
|
|
|
2966
2998
|
const v = this.cutoutVector;
|
|
2967
2999
|
v[0] = material?.cutout ?? 0;
|
|
2968
3000
|
v[1] = CUTOUT_RESOLVE_CODE[this.cutoutResolveNow(material, translucent)];
|
|
2969
|
-
|
|
3001
|
+
/* z unused: the dither's frame is the pass's, in `uHighlightMin.w`. See `uCutout`. */
|
|
3002
|
+
v[2] = 0;
|
|
2970
3003
|
/* The material's lighting channels, which the lamps' are compared with. See `uCutout`. */
|
|
2971
3004
|
v[3] = lightChannelsOf(material?.lightChannels);
|
|
2972
3005
|
return v;
|
|
@@ -3157,8 +3190,22 @@ export class WebGL2Renderer {
|
|
|
3157
3190
|
setSurfaceReflectivity(amount) {
|
|
3158
3191
|
if (this.contextLost)
|
|
3159
3192
|
return;
|
|
3193
|
+
this.passReflectivity = Math.min(1, Math.max(0, amount));
|
|
3194
|
+
this.writeEnvironmentDials();
|
|
3195
|
+
}
|
|
3196
|
+
/**
|
|
3197
|
+
* The reflectivity and environment gain the setters gave the pass, and the ones the material
|
|
3198
|
+
* set now states, or null where it states none (`SurfaceMaterial.reflectivity`). A draw wears the
|
|
3199
|
+
* material's where it has one and the pass's otherwise.
|
|
3200
|
+
*/
|
|
3201
|
+
passReflectivity = 0;
|
|
3202
|
+
passEnvironmentGain = 1;
|
|
3203
|
+
materialReflectivity = null;
|
|
3204
|
+
materialEnvironmentGain = null;
|
|
3205
|
+
writeEnvironmentDials() {
|
|
3160
3206
|
this.materials.dirty();
|
|
3161
|
-
this.environmentDials[1] =
|
|
3207
|
+
this.environmentDials[1] = this.materialReflectivity ?? this.passReflectivity;
|
|
3208
|
+
this.environmentDials[2] = this.materialEnvironmentGain ?? this.passEnvironmentGain;
|
|
3162
3209
|
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3163
3210
|
}
|
|
3164
3211
|
/**
|
|
@@ -3237,9 +3284,8 @@ export class WebGL2Renderer {
|
|
|
3237
3284
|
setEnvironmentGain(gain) {
|
|
3238
3285
|
if (this.contextLost)
|
|
3239
3286
|
return;
|
|
3240
|
-
this.
|
|
3241
|
-
this.
|
|
3242
|
-
this.writeFlatVector('uEnvironmentDials', this.environmentDials);
|
|
3287
|
+
this.passEnvironmentGain = Math.max(0, gain);
|
|
3288
|
+
this.writeEnvironmentDials();
|
|
3243
3289
|
}
|
|
3244
3290
|
/**
|
|
3245
3291
|
* Kept as a no-op that says so once, because it was a stand-in for a term that now exists.
|
|
@@ -3508,8 +3554,95 @@ export class WebGL2Renderer {
|
|
|
3508
3554
|
throw new Error('WebGL2: a skinned mesh cannot be instanced — an instanced draw binds the two attribute ' +
|
|
3509
3555
|
'locations the joint indices and weights occupy, and this mesh supplies them.');
|
|
3510
3556
|
}
|
|
3557
|
+
const animation = (options.animation ?? null);
|
|
3558
|
+
/* The bone a vertex follows is its second coordinates' u, which a mesh carries in its grain
|
|
3559
|
+
lane; a mesh with none has nothing to name a bone by. See `boneAnimation.ts`. */
|
|
3560
|
+
if (animation !== null && !mesh.hasGrain) {
|
|
3561
|
+
throw new Error('WebGL2: a bone animation reads each vertex’s bone from the mesh’s second coordinates ' +
|
|
3562
|
+
'(`MeshData.lightmapUvs`), and this mesh has none.');
|
|
3563
|
+
}
|
|
3511
3564
|
this.ensureInstancedProgram();
|
|
3512
|
-
|
|
3565
|
+
if (animation !== null)
|
|
3566
|
+
this.ensureAnimatedProgram();
|
|
3567
|
+
return new InstancedBatch(this.gl, mesh, capacity, options.cull ?? false, animation);
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* A bone animation an instanced batch can play (`InstancedOptions.animation`): per frame, a turn
|
|
3571
|
+
* and a place for each bone, which every vertex of the batch's mesh follows — the bone its second
|
|
3572
|
+
* coordinates' u names, times `boneScale` — at each instance's own moment of the clip. What a
|
|
3573
|
+
* crowd is made of: every instance moves at the frame's rate on its own clock, in the colour pass
|
|
3574
|
+
* and in every shadow, with nothing on the processor after this. See `boneAnimation.ts` for the
|
|
3575
|
+
* clip's layout and the arithmetic, which both backends run.
|
|
3576
|
+
*
|
|
3577
|
+
* What it gives up against skinning is the blend: a vertex follows one bone. Refused, with what
|
|
3578
|
+
* was wrong, for a clip whose numbers are not its size, or past 2,048 bones or frames.
|
|
3579
|
+
*/
|
|
3580
|
+
createBoneAnimation(clip) {
|
|
3581
|
+
return new GlBoneAnimation(this.gl, packBoneAnimation(clip));
|
|
3582
|
+
}
|
|
3583
|
+
/** Release a clip. A batch still playing it draws nothing, and casts nothing, after. */
|
|
3584
|
+
disposeBoneAnimation(animation) {
|
|
3585
|
+
const clip = animation;
|
|
3586
|
+
if (this.contextLost || clip.disposed)
|
|
3587
|
+
return;
|
|
3588
|
+
clip.dispose(this.gl);
|
|
3589
|
+
}
|
|
3590
|
+
/**
|
|
3591
|
+
* The clock a bone animation plays by this frame, in seconds on the caller's clock: an instance's
|
|
3592
|
+
* moment is this times its rate plus its phase (`MeshInstances.clocks`). **Set it before the
|
|
3593
|
+
* shadows are drawn**, once a frame, as the wind is set: a shadow drawn under one clock and its
|
|
3594
|
+
* caster under another come apart at the frame's rate. 0 until set, which holds every crowd at
|
|
3595
|
+
* the moment its phases name.
|
|
3596
|
+
*/
|
|
3597
|
+
setAnimationTime(seconds) {
|
|
3598
|
+
if (Number.isFinite(seconds))
|
|
3599
|
+
this.animationTime = seconds;
|
|
3600
|
+
}
|
|
3601
|
+
/** The clock `setAnimationTime` set. */
|
|
3602
|
+
animationTime = 0;
|
|
3603
|
+
/**
|
|
3604
|
+
* An animated batch's clip, clocks and numbers into whichever program is bound: the three vertex
|
|
3605
|
+
* textures on units of their own (`lightBudget.ts`), the clip's speed and bone scale, the clock.
|
|
3606
|
+
*/
|
|
3607
|
+
bindAnimation(u, batch) {
|
|
3608
|
+
const clip = batch.animation;
|
|
3609
|
+
const clocks = batch.clocks;
|
|
3610
|
+
if (clip === null || clocks === null)
|
|
3611
|
+
return;
|
|
3612
|
+
const { gl } = this;
|
|
3613
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_PLACES_TEXTURE_UNIT);
|
|
3614
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.places);
|
|
3615
|
+
gl.activeTexture(gl.TEXTURE0 + BONE_TURNS_TEXTURE_UNIT);
|
|
3616
|
+
gl.bindTexture(gl.TEXTURE_2D, clip.turns);
|
|
3617
|
+
gl.activeTexture(gl.TEXTURE0 + INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3618
|
+
gl.bindTexture(gl.TEXTURE_2D, clocks.texture);
|
|
3619
|
+
gl.uniform1i(u['uBonePlaces'] ?? null, BONE_PLACES_TEXTURE_UNIT);
|
|
3620
|
+
gl.uniform1i(u['uBoneTurns'] ?? null, BONE_TURNS_TEXTURE_UNIT);
|
|
3621
|
+
gl.uniform1i(u['uInstanceClocks'] ?? null, INSTANCE_CLOCKS_TEXTURE_UNIT);
|
|
3622
|
+
gl.uniform2f(u['uBoneClip'] ?? null, clip.framesPerSecond, clip.boneScale);
|
|
3623
|
+
gl.uniform1f(u['uSceneTime'] ?? null, this.animationTime);
|
|
3624
|
+
}
|
|
3625
|
+
/** The animated instanced program, compiled on first use. See `ensureInstancedProgram`. */
|
|
3626
|
+
flatAnimatedProgram = null;
|
|
3627
|
+
flatAnimatedUniforms = null;
|
|
3628
|
+
/** Compile the animated variant, once, and feed it this frame's pass state. */
|
|
3629
|
+
ensureAnimatedProgram() {
|
|
3630
|
+
if (this.contextLost || this.flatAnimatedProgram !== null)
|
|
3631
|
+
return;
|
|
3632
|
+
const { gl } = this;
|
|
3633
|
+
const program = compileProgram(gl, flatVert({ skinned: false, morphed: false, instanced: true, animated: true }), this.flatFragSource, 'flat.animated');
|
|
3634
|
+
const uniforms = uniformLocations(gl, program, 'flat.animated');
|
|
3635
|
+
this.flatAnimatedProgram = program;
|
|
3636
|
+
this.flatAnimatedUniforms = uniforms;
|
|
3637
|
+
this.flatTargets.push({ program, uniforms });
|
|
3638
|
+
const camera = this.lastPassCamera;
|
|
3639
|
+
const env = this.lastPassEnv;
|
|
3640
|
+
if (camera !== null && env !== null) {
|
|
3641
|
+
gl.useProgram(program);
|
|
3642
|
+
this.writeMeshPassState(uniforms, camera, env);
|
|
3643
|
+
this.writeMaterialState(uniforms, this.currentMaterial);
|
|
3644
|
+
this.useFlatProgram();
|
|
3645
|
+
}
|
|
3513
3646
|
}
|
|
3514
3647
|
/** Push placement and colour. Only the live prefix. */
|
|
3515
3648
|
uploadInstanced(batch, data) {
|
|
@@ -3562,18 +3695,29 @@ export class WebGL2Renderer {
|
|
|
3562
3695
|
const culling = !blend && batch.cull;
|
|
3563
3696
|
if (culling && !batchBoxVisible(batch.box, this.frustum, this.occlusion))
|
|
3564
3697
|
return;
|
|
3698
|
+
/* A batch playing a bone animation takes the animated variant; a disposed clip draws nothing. */
|
|
3699
|
+
const clip = batch.animation;
|
|
3700
|
+
if (clip !== null && clip.disposed)
|
|
3701
|
+
return;
|
|
3565
3702
|
this.drawBudget.ask();
|
|
3566
3703
|
this.ensureInstancedProgram();
|
|
3704
|
+
if (clip !== null)
|
|
3705
|
+
this.ensureAnimatedProgram();
|
|
3567
3706
|
/* A material with a model takes that model's instanced program; see `modelProgram`. */
|
|
3568
3707
|
const modelKind = this.currentMaterial?.model?.kind ?? null;
|
|
3569
|
-
const modelled = modelKind === null
|
|
3570
|
-
|
|
3571
|
-
|
|
3708
|
+
const modelled = modelKind === null
|
|
3709
|
+
? null
|
|
3710
|
+
: this.modelProgram(clip === null ? 'instanced' : 'animated', modelKind);
|
|
3711
|
+
const program = modelled?.program ?? (clip === null ? this.flatInstancedProgram : this.flatAnimatedProgram);
|
|
3712
|
+
const u = modelled?.uniforms ??
|
|
3713
|
+
(clip === null ? this.flatInstancedUniforms : this.flatAnimatedUniforms);
|
|
3572
3714
|
if (program === null || u === null)
|
|
3573
3715
|
return;
|
|
3574
3716
|
const { gl } = this;
|
|
3575
3717
|
gl.useProgram(program);
|
|
3576
3718
|
gl.uniform1i(u['uHasTangents'] ?? null, mesh.hasTangents ? 1 : 0);
|
|
3719
|
+
if (clip !== null)
|
|
3720
|
+
this.bindAnimation(u, batch);
|
|
3577
3721
|
const lit = options.lit ?? true;
|
|
3578
3722
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
3579
3723
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
@@ -3643,10 +3787,14 @@ export class WebGL2Renderer {
|
|
|
3643
3787
|
const keeps = !blend && this.reflectionKeeps();
|
|
3644
3788
|
if (keeps)
|
|
3645
3789
|
this.reflectionTargets?.on();
|
|
3646
|
-
|
|
3790
|
+
/* An animated batch reads its clocks by instance index, which the culled copy would reorder:
|
|
3791
|
+
it is culled whole and never instance by instance. */
|
|
3792
|
+
if (culling && clip === null && cullsInstances(count, mesh.indexCount)) {
|
|
3647
3793
|
batch.drawCulled(gl, data, this.frustum);
|
|
3648
|
-
|
|
3794
|
+
}
|
|
3795
|
+
else {
|
|
3649
3796
|
batch.draw(gl, count);
|
|
3797
|
+
}
|
|
3650
3798
|
if (keeps)
|
|
3651
3799
|
this.reflectionTargets?.off();
|
|
3652
3800
|
if (coverage)
|
|
@@ -3710,7 +3858,9 @@ export class WebGL2Renderer {
|
|
|
3710
3858
|
const glass = this.glassScratch;
|
|
3711
3859
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
3712
3860
|
gl.uniform4f(u['uSeeThrough'] ?? null, Math.max(refraction, 0), options.thicknessM ?? 0, glass.transmission, glass.frost);
|
|
3713
|
-
|
|
3861
|
+
/* w: this framebuffer's first row is the bottom, so up the screen is up the rows. */
|
|
3862
|
+
const tint = options.refractTint ?? WHITE_TINT;
|
|
3863
|
+
gl.uniform4f(u['uRefractTint'] ?? null, tint[0] ?? 1, tint[1] ?? 1, tint[2] ?? 1, 1);
|
|
3714
3864
|
gl.uniform3fv(u['uGlassTint'] ?? null, glass.tint);
|
|
3715
3865
|
return true;
|
|
3716
3866
|
}
|
|
@@ -4753,6 +4903,69 @@ export class WebGL2Renderer {
|
|
|
4753
4903
|
return;
|
|
4754
4904
|
casters(this.casterSink);
|
|
4755
4905
|
}
|
|
4906
|
+
/**
|
|
4907
|
+
* Draws that do not change between frames, enumerated once and kept: what a stage, a set or any
|
|
4908
|
+
* world that stands still draws, recorded so a frame replays it rather than issuing it.
|
|
4909
|
+
*
|
|
4910
|
+
* **The same enumeration `drawSceneCasters` takes**, called once, here: rigid meshes and instanced
|
|
4911
|
+
* batches, each with its material. A skinned mesh or a scatter batch is refused by name, since
|
|
4912
|
+
* both change every frame. What it captures is what the enumeration said at the time — the
|
|
4913
|
+
* matrices and the instance counts are copied, so a caller's scratch is free to move on — while
|
|
4914
|
+
* the meshes, batches, textures and materials stay the caller's: a list must be disposed, or made
|
|
4915
|
+
* again, before anything it holds is, and made again when a material it holds is changed.
|
|
4916
|
+
*
|
|
4917
|
+
* **What it is for is the frame's processor time.** WebGPU records the list into a render bundle
|
|
4918
|
+
* per view and replays it with one call, where a draw issued a frame at a time costs its encoding
|
|
4919
|
+
* every frame and again for every view that draws it. This backend has no bundles and replays the
|
|
4920
|
+
* entries through the scene-caster path every frame, at the cost a frame drawing them itself pays;
|
|
4921
|
+
* the picture is the same.
|
|
4922
|
+
*
|
|
4923
|
+
* What a list gives up against drawing each frame is culling: every entry is drawn in every view,
|
|
4924
|
+
* because a bundle cannot leave one out. A world split into lists by region culls by list.
|
|
4925
|
+
*/
|
|
4926
|
+
createStaticDraws(casters) {
|
|
4927
|
+
return captureStaticDraws(casters);
|
|
4928
|
+
}
|
|
4929
|
+
/**
|
|
4930
|
+
* Draw a list into the open mesh pass, under the camera, the lights and the fog `bindMeshPass`
|
|
4931
|
+
* set — the frame's, a mirror's, a capture's. Opaque draws, through each entry's material.
|
|
4932
|
+
*
|
|
4933
|
+
* **Its draws are rigid and wear no overlay**, so a skin palette, morph weights, cloth or a surface
|
|
4934
|
+
* overlay set before it is put back to none, as `bindMeshPass` puts an overlay back; and no
|
|
4935
|
+
* material is left set after it, which is what `setMaterial(null)` leaves. The state a pass set —
|
|
4936
|
+
* the grain, the relief, the fog, the grade — is the list's to draw with, as it is any draw's, and
|
|
4937
|
+
* a list drawn under a different one is recorded again for it on WebGPU.
|
|
4938
|
+
*/
|
|
4939
|
+
drawStaticDraws(draws) {
|
|
4940
|
+
if (this.contextLost)
|
|
4941
|
+
return;
|
|
4942
|
+
const list = draws;
|
|
4943
|
+
if (list.disposed) {
|
|
4944
|
+
this.warnStaticDisposed();
|
|
4945
|
+
return;
|
|
4946
|
+
}
|
|
4947
|
+
this.setSkinPalette(null);
|
|
4948
|
+
this.setMorphWeights(null);
|
|
4949
|
+
this.setCloth(null);
|
|
4950
|
+
this.setSurfaceOverlay(null);
|
|
4951
|
+
this.setMaterial(null);
|
|
4952
|
+
this.sceneCasterMaterial = undefined;
|
|
4953
|
+
replayStaticDraws(list, this.sceneCasterSink);
|
|
4954
|
+
this.setMaterial(null);
|
|
4955
|
+
this.sceneCasterMaterial = undefined;
|
|
4956
|
+
}
|
|
4957
|
+
/** Release a list. Its meshes, batches and materials are the caller's and are not released. */
|
|
4958
|
+
disposeStaticDraws(draws) {
|
|
4959
|
+
draws.disposed = true;
|
|
4960
|
+
}
|
|
4961
|
+
/** A disposed list drawn, said once: the frame loop does not throw. */
|
|
4962
|
+
warnStaticDisposed() {
|
|
4963
|
+
if (this.warnedStaticDisposed)
|
|
4964
|
+
return;
|
|
4965
|
+
this.warnedStaticDisposed = true;
|
|
4966
|
+
console.warn('drawStaticDraws: this list was disposed, and draws nothing.');
|
|
4967
|
+
}
|
|
4968
|
+
warnedStaticDisposed = false;
|
|
4756
4969
|
/**
|
|
4757
4970
|
* Draw the pass's glass twice: where the nearest pane is, and what the panes let through.
|
|
4758
4971
|
*
|
|
@@ -4965,10 +5178,10 @@ export class WebGL2Renderer {
|
|
|
4965
5178
|
const { gl } = this;
|
|
4966
5179
|
Object.assign(this.litOn, this.litWanted);
|
|
4967
5180
|
this.flatFragSource = this.flatFragAt(this.lightBudget);
|
|
4968
|
-
const recompile = (old, skinned, morphed, instanced, label, eight = false) => {
|
|
5181
|
+
const recompile = (old, skinned, morphed, instanced, label, eight = false, animated = false) => {
|
|
4969
5182
|
if (old === null)
|
|
4970
5183
|
return null;
|
|
4971
|
-
const vertex = flatVert({ skinned, morphed, instanced });
|
|
5184
|
+
const vertex = flatVert({ skinned, morphed, instanced, animated });
|
|
4972
5185
|
const program = compileProgram(gl, eight ? skinEight(vertex) : vertex, this.flatFragSource, label);
|
|
4973
5186
|
gl.deleteProgram(old);
|
|
4974
5187
|
return { program, uniforms: uniformLocations(gl, program, label) };
|
|
@@ -4996,6 +5209,9 @@ export class WebGL2Renderer {
|
|
|
4996
5209
|
const instanced = recompile(this.flatInstancedProgram, false, false, true, 'flat.instanced');
|
|
4997
5210
|
this.flatInstancedProgram = instanced?.program ?? null;
|
|
4998
5211
|
this.flatInstancedUniforms = instanced?.uniforms ?? null;
|
|
5212
|
+
const animated = recompile(this.flatAnimatedProgram, false, false, true, 'flat.animated', false, true);
|
|
5213
|
+
this.flatAnimatedProgram = animated?.program ?? null;
|
|
5214
|
+
this.flatAnimatedUniforms = animated?.uniforms ?? null;
|
|
4999
5215
|
for (const [key, entry] of this.clothFlat) {
|
|
5000
5216
|
const eight = key.startsWith('8');
|
|
5001
5217
|
const withMorph = key.endsWith('m');
|
|
@@ -5013,7 +5229,7 @@ export class WebGL2Renderer {
|
|
|
5013
5229
|
this.modelFlat.set(key, { program, uniforms: uniformLocations(gl, program, label) });
|
|
5014
5230
|
}
|
|
5015
5231
|
this.flatTargets.length = 0;
|
|
5016
|
-
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced]) {
|
|
5232
|
+
for (const target of [plain, skinned, morphed, both, skinned8, both8, instanced, animated]) {
|
|
5017
5233
|
if (target !== null)
|
|
5018
5234
|
this.flatTargets.push(target);
|
|
5019
5235
|
}
|
|
@@ -6782,6 +6998,10 @@ export class WebGL2Renderer {
|
|
|
6782
6998
|
/* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
|
|
6783
6999
|
this.ambientSH.fill(0);
|
|
6784
7000
|
/* No reflection and an environment at its own brightness; the emission the world asks for. */
|
|
7001
|
+
this.passReflectivity = 0;
|
|
7002
|
+
this.passEnvironmentGain = 1;
|
|
7003
|
+
this.materialReflectivity = null;
|
|
7004
|
+
this.materialEnvironmentGain = null;
|
|
6785
7005
|
this.environmentDials[1] = 0;
|
|
6786
7006
|
this.environmentDials[2] = 1;
|
|
6787
7007
|
this.emission[0] = env.emissiveGain;
|
|
@@ -6911,7 +7131,7 @@ export class WebGL2Renderer {
|
|
|
6911
7131
|
/* Neither refracting nor glass, which is every draw that does not say otherwise. See
|
|
6912
7132
|
`bindSeeThrough` for the four numbers in the one vector. */
|
|
6913
7133
|
gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
|
|
6914
|
-
gl.
|
|
7134
|
+
gl.uniform4f(u['uRefractTint'] ?? null, 1, 1, 1, 1);
|
|
6915
7135
|
gl.uniform3fv(u['uGlassTint'] ?? null, WHITE_TINT);
|
|
6916
7136
|
gl.uniform3fv(u['uDirectionalDir'] ?? null, env.directionalDir);
|
|
6917
7137
|
gl.uniform3fv(u['uDirectionalColor'] ?? null, env.directionalColor);
|
|
@@ -7025,7 +7245,10 @@ export class WebGL2Renderer {
|
|
|
7025
7245
|
gl.uniform1i(u['uPeeledShadowEnabled'] ?? null, this.peelFilled ? 1 : 0);
|
|
7026
7246
|
gl.uniform1f(u['uShadowMaxSlope'] ?? null, this.quality.directionalShadowMaxSlope);
|
|
7027
7247
|
gl.uniform1i(u['uShadowFilterTaps'] ?? null, this.quality.shadowFilterTaps);
|
|
7028
|
-
|
|
7248
|
+
/* The near corner, and in its w the frame the cutout dither moves with. See `uHighlightMin`.
|
|
7249
|
+
An environment written by hand may leave the box out, as the far corner says below. */
|
|
7250
|
+
const min = env.highlightMin;
|
|
7251
|
+
gl.uniform4f(u['uHighlightMin'] ?? null, min?.[0] ?? 0, min?.[1] ?? 0, min?.[2] ?? 0, this.cutoutFrame);
|
|
7029
7252
|
/* The far corner, and the gain in its w. See `uHighlightMax`. */
|
|
7030
7253
|
/* An environment written by hand may leave the box out; then there is none, as before. */
|
|
7031
7254
|
const max = env.highlightMax ?? NO_HIGHLIGHT;
|
|
@@ -8350,6 +8573,12 @@ function litVertexOf(variant) {
|
|
|
8350
8573
|
}
|
|
8351
8574
|
const skinned = variant.startsWith('skinned') || variant.startsWith('both');
|
|
8352
8575
|
const morphed = variant === 'morphed' || variant.startsWith('both');
|
|
8353
|
-
const
|
|
8576
|
+
const animated = variant === 'animated';
|
|
8577
|
+
const vertex = flatVert({
|
|
8578
|
+
skinned,
|
|
8579
|
+
morphed,
|
|
8580
|
+
instanced: variant === 'instanced' || animated,
|
|
8581
|
+
animated,
|
|
8582
|
+
});
|
|
8354
8583
|
return variant.endsWith('8') ? skinEight(vertex) : vertex;
|
|
8355
8584
|
}
|
|
@@ -19,9 +19,11 @@ export declare class SkinPaletteTexture {
|
|
|
19
19
|
/**
|
|
20
20
|
* Upload a palette, reallocating only when the joint count changes.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* **The texture grows and never shrinks**, so the steady state is one `texSubImage2D` whatever
|
|
23
|
+
* mix of rigs a frame draws. It was rebuilt whenever the joint count changed, which with one
|
|
24
|
+
* texture for every skinned draw meant a character and a prop drawn in turn reallocated it at
|
|
25
|
+
* every draw: a GPU allocation in the frame loop. The shader reads joints by index, so a texture
|
|
26
|
+
* wider than the rig is read exactly as one that fits.
|
|
25
27
|
*/
|
|
26
28
|
update(gl: WebGL2RenderingContext, palette: Float32Array): void;
|
|
27
29
|
/** Bind to a **vertex-stage** unit. See `SKIN_PALETTE_TEXTURE_UNIT` for why that is free. */
|
|
@@ -20,15 +20,17 @@ export class SkinPaletteTexture {
|
|
|
20
20
|
/**
|
|
21
21
|
* Upload a palette, reallocating only when the joint count changes.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* **The texture grows and never shrinks**, so the steady state is one `texSubImage2D` whatever
|
|
24
|
+
* mix of rigs a frame draws. It was rebuilt whenever the joint count changed, which with one
|
|
25
|
+
* texture for every skinned draw meant a character and a prop drawn in turn reallocated it at
|
|
26
|
+
* every draw: a GPU allocation in the frame loop. The shader reads joints by index, so a texture
|
|
27
|
+
* wider than the rig is read exactly as one that fits.
|
|
26
28
|
*/
|
|
27
29
|
update(gl, palette) {
|
|
28
30
|
validateSkinPalette(palette);
|
|
29
31
|
const joints = palette.length / 16;
|
|
30
32
|
const width = paletteTextureWidth(joints);
|
|
31
|
-
if (this.texture === null ||
|
|
33
|
+
if (this.texture === null || joints > this.joints) {
|
|
32
34
|
if (this.texture !== null)
|
|
33
35
|
gl.deleteTexture(this.texture);
|
|
34
36
|
const created = gl.createTexture();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bone animation's textures on this backend, and a batch's clocks: what
|
|
3
|
+
* `shaders/boneAnimation.ts` reads by index, made once and never filtered.
|
|
4
|
+
*
|
|
5
|
+
* **`rgba32float` places and turns, and `rg32float` clocks**, all read with `textureLoad` and bound
|
|
6
|
+
* `unfilterable-float`, as the joint palette is: a turn in half floats moves a crowd's hands by a
|
|
7
|
+
* thousandth of their reach, a jitter at the frame's rate, and the bytes are a clip's, once.
|
|
8
|
+
*/
|
|
9
|
+
import { type BoneAnimationHandle, type BoneAnimationTexels } from '../../boneAnimation.ts';
|
|
10
|
+
/** A clip on the device, from `createBoneAnimation`. */
|
|
11
|
+
export declare class GpuBoneAnimation implements BoneAnimationHandle {
|
|
12
|
+
readonly bones: number;
|
|
13
|
+
readonly frames: number;
|
|
14
|
+
readonly framesPerSecond: number;
|
|
15
|
+
readonly boneScale: number;
|
|
16
|
+
readonly placesView: GPUTextureView;
|
|
17
|
+
readonly turnsView: GPUTextureView;
|
|
18
|
+
disposed: boolean;
|
|
19
|
+
private readonly places;
|
|
20
|
+
private readonly turns;
|
|
21
|
+
constructor(device: GPUDevice, texels: BoneAnimationTexels);
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
24
|
+
/** A batch's clocks: two floats an instance, wrapped onto rows `CLOCK_TEXTURE_WIDTH` wide. */
|
|
25
|
+
export declare class GpuInstanceClocks {
|
|
26
|
+
readonly view: GPUTextureView;
|
|
27
|
+
readonly staging: Float32Array;
|
|
28
|
+
private readonly texture;
|
|
29
|
+
private readonly width;
|
|
30
|
+
constructor(device: GPUDevice, capacity: number, label: string);
|
|
31
|
+
/** The rows that hold the first `count` instances, from `staging`. */
|
|
32
|
+
upload(queue: GPUQueue, count: number): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bone animation's textures on this backend, and a batch's clocks: what
|
|
3
|
+
* `shaders/boneAnimation.ts` reads by index, made once and never filtered.
|
|
4
|
+
*
|
|
5
|
+
* **`rgba32float` places and turns, and `rg32float` clocks**, all read with `textureLoad` and bound
|
|
6
|
+
* `unfilterable-float`, as the joint palette is: a turn in half floats moves a crowd's hands by a
|
|
7
|
+
* thousandth of their reach, a jitter at the frame's rate, and the bytes are a clip's, once.
|
|
8
|
+
*/
|
|
9
|
+
import { CLOCK_TEXTURE_WIDTH, } from '../../boneAnimation.js';
|
|
10
|
+
const USAGE_TEXTURE = 0x4 | 0x2; // TEXTURE_BINDING | COPY_DST
|
|
11
|
+
/** A clip on the device, from `createBoneAnimation`. */
|
|
12
|
+
export class GpuBoneAnimation {
|
|
13
|
+
bones;
|
|
14
|
+
frames;
|
|
15
|
+
framesPerSecond;
|
|
16
|
+
boneScale;
|
|
17
|
+
placesView;
|
|
18
|
+
turnsView;
|
|
19
|
+
disposed = false;
|
|
20
|
+
places;
|
|
21
|
+
turns;
|
|
22
|
+
constructor(device, texels) {
|
|
23
|
+
this.bones = texels.bones;
|
|
24
|
+
this.frames = texels.frames;
|
|
25
|
+
this.framesPerSecond = texels.framesPerSecond;
|
|
26
|
+
this.boneScale = texels.boneScale;
|
|
27
|
+
const make = (label, data) => {
|
|
28
|
+
const texture = device.createTexture({
|
|
29
|
+
label,
|
|
30
|
+
size: [texels.bones, texels.frames],
|
|
31
|
+
format: 'rgba32float',
|
|
32
|
+
usage: USAGE_TEXTURE,
|
|
33
|
+
});
|
|
34
|
+
device.queue.writeTexture({ texture }, data, { bytesPerRow: texels.bones * 16, rowsPerImage: texels.frames }, [texels.bones, texels.frames]);
|
|
35
|
+
return texture;
|
|
36
|
+
};
|
|
37
|
+
this.places = make('bones.places', texels.places);
|
|
38
|
+
this.turns = make('bones.turns', texels.turns);
|
|
39
|
+
this.placesView = this.places.createView();
|
|
40
|
+
this.turnsView = this.turns.createView();
|
|
41
|
+
}
|
|
42
|
+
dispose() {
|
|
43
|
+
this.disposed = true;
|
|
44
|
+
this.places.destroy();
|
|
45
|
+
this.turns.destroy();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** A batch's clocks: two floats an instance, wrapped onto rows `CLOCK_TEXTURE_WIDTH` wide. */
|
|
49
|
+
export class GpuInstanceClocks {
|
|
50
|
+
view;
|
|
51
|
+
staging;
|
|
52
|
+
texture;
|
|
53
|
+
width;
|
|
54
|
+
constructor(device, capacity, label) {
|
|
55
|
+
this.width = Math.max(1, Math.min(capacity, CLOCK_TEXTURE_WIDTH));
|
|
56
|
+
const rows = Math.max(1, Math.ceil(capacity / this.width));
|
|
57
|
+
this.staging = new Float32Array(this.width * rows * 2);
|
|
58
|
+
this.texture = device.createTexture({
|
|
59
|
+
label,
|
|
60
|
+
size: [this.width, rows],
|
|
61
|
+
format: 'rg32float',
|
|
62
|
+
usage: USAGE_TEXTURE,
|
|
63
|
+
});
|
|
64
|
+
this.view = this.texture.createView();
|
|
65
|
+
}
|
|
66
|
+
/** The rows that hold the first `count` instances, from `staging`. */
|
|
67
|
+
upload(queue, count) {
|
|
68
|
+
if (count <= 0)
|
|
69
|
+
return;
|
|
70
|
+
const rows = Math.ceil(count / this.width);
|
|
71
|
+
queue.writeTexture({ texture: this.texture }, this.staging, { bytesPerRow: this.width * 8, rowsPerImage: rows }, [this.width, rows]);
|
|
72
|
+
}
|
|
73
|
+
dispose() {
|
|
74
|
+
this.texture.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|