@driftengine/core 4.10.0 → 4.11.1
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 +107 -0
- package/README.md +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- package/dist/render/ambientOcclusionPass.d.ts +3 -2
- package/dist/render/ambientOcclusionPass.js +4 -2
- package/dist/render/backend/api.d.ts +21 -1
- package/dist/render/backend/webgl2/renderer.d.ts +141 -3
- package/dist/render/backend/webgl2/renderer.js +454 -42
- package/dist/render/backend/webgpu/buffers.d.ts +5 -0
- package/dist/render/backend/webgpu/buffers.js +98 -48
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/drawCommand.d.ts +2 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +22 -1
- package/dist/render/backend/webgpu/pipelineCache.js +39 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -1
- package/dist/render/backend/webgpu/renderer.js +423 -95
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/staticBundles.js +2 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/mesh.d.ts +9 -0
- package/dist/render/mesh.js +38 -14
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +24 -4
- package/dist/render/shaders/flat/layered.d.ts +27 -0
- package/dist/render/shaders/flat/layered.js +198 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +50 -16
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +23 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +41 -19
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +768 -368
- package/dist/render/shaders/generated/flat.wgsl.js +791 -391
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/surfaceLayers.d.ts +107 -0
- package/dist/render/surfaceLayers.js +82 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +39 -0
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/dist/ui/splash.d.ts +42 -2
- package/dist/ui/splash.js +78 -13
- package/package.json +6 -6
- package/src/index.ts +6 -2
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +519 -56
- package/src/render/backend/webgpu/buffers.ts +153 -54
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/drawCommand.ts +3 -0
- package/src/render/backend/webgpu/flatPass.ts +26 -28
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +46 -7
- package/src/render/backend/webgpu/renderer.ts +622 -164
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/staticBundles.ts +2 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +18 -1
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/mesh.ts +66 -14
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +39 -4
- package/src/render/shaders/flat/layered.ts +198 -0
- package/src/render/shaders/flat/main.ts +50 -16
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +23 -3
- package/src/render/shaders/flat/overlay.ts +41 -19
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +791 -391
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/surfaceLayers.ts +176 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +39 -0
- package/src/render/transmission.ts +27 -0
- package/src/render/vertexPacking.ts +109 -0
- package/src/ui/splash.ts +83 -14
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MeshData } from '@driftengine/drft';
|
|
2
|
+
/**
|
|
3
|
+
* How each of a mesh's vertex attributes travels to the device, decided once from its data and
|
|
4
|
+
* bound by each backend: as floats, as sixteen-bit fixed point, or as one value every vertex
|
|
5
|
+
* shares.
|
|
6
|
+
*
|
|
7
|
+
* **Only what reads back as it went, or near enough that no picture can tell.** One value every
|
|
8
|
+
* vertex shares is exact, and is a constant rather than a stream: a colour or an emissive naming
|
|
9
|
+
* no difference between vertices costs nothing a vertex, where both were always stored per vertex.
|
|
10
|
+
* A unit direction — a normal, a tangent and its handedness — in sixteen-bit signed fixed point is
|
|
11
|
+
* within 1/32767 a component, a few thousandths of a degree: measured against floats, 12 pixels of
|
|
12
|
+
* 921,600 moved in one published scene, by 2 of 255 on average, and none in the other eight.
|
|
13
|
+
* Colours and skin weights inside [0, 1] in sixteen-bit unsigned fixed point are within 1/65535. A
|
|
14
|
+
* textured vertex with a tangent frame, white and not glowing, goes from 64 bytes to 36 on WebGPU.
|
|
15
|
+
*
|
|
16
|
+
* **What it gives up.** Eight-bit normals would save four bytes more and are not taken: a normal in
|
|
17
|
+
* steps of 1/127 can band a highlight. **Texture coordinates stay floats**, in any range: a
|
|
18
|
+
* coordinate on a tile's far edge rounds past it in sixteen-bit fixed point, by 1/131,070, and a
|
|
19
|
+
* pixel there samples the neighbouring tile of an atlas — measured as 29 pixels moved by 62 of 255
|
|
20
|
+
* in the voxel scene, every one at a face's edge — and a half float, which steps by a 2048th near
|
|
21
|
+
* 1.0, is worse. A dynamic mesh's normals stay floats, because each rewrite would have to be packed
|
|
22
|
+
* on the CPU. What would make this wrong is a value outside its range, which the range check sends
|
|
23
|
+
* to floats rather than clamping, or a shader reading an attribute as something other than the
|
|
24
|
+
* value it carries.
|
|
25
|
+
*/
|
|
26
|
+
/** Floats; sixteen-bit fixed point, signed or not; or one value for every vertex. */
|
|
27
|
+
export type AttributePacking = 'float' | 'snorm16' | 'unorm16' | 'constant';
|
|
28
|
+
/** The attributes this decides for. Every other one travels as floats. */
|
|
29
|
+
export declare const PACKED_ATTRIBUTES: readonly ['normals', 'tangents', 'colors', 'emissive', 'weights', 'weights2'];
|
|
30
|
+
export type PackedAttribute = (typeof PACKED_ATTRIBUTES)[number];
|
|
31
|
+
export type VertexPacking = Readonly<Record<PackedAttribute, AttributePacking>>;
|
|
32
|
+
/** The choice for this mesh. `dynamic` is a mesh whose positions and normals are rewritten. */
|
|
33
|
+
export declare function vertexPackingOf(data: MeshData, dynamic: boolean): VertexPacking;
|
|
34
|
+
/**
|
|
35
|
+
* What 1 is in each fixed point, the values WebGPU's `snorm16` and `unorm16` and WebGL2's
|
|
36
|
+
* normalised `SHORT` and `UNSIGNED_SHORT` divide by. A value is stored rounded to nearest, so 1, 0
|
|
37
|
+
* and -1 come back exactly.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SNORM16_ONE = 32767;
|
|
40
|
+
export declare const UNORM16_ONE = 65535;
|
|
41
|
+
/** One value in the fixed point whose 1 is `one`, rounded to nearest: the only place it is. */
|
|
42
|
+
export declare function toFixed16(value: number, one: number): number;
|
|
43
|
+
/** An attribute's values in its fixed point, tightly, for a backend reading separate arrays. */
|
|
44
|
+
export declare function fixedArray(values: Float32Array, packing: 'snorm16' | 'unorm16'): Int16Array | Uint16Array;
|
|
45
|
+
/** How `name` travels under this packing: floats for an attribute it does not decide for. */
|
|
46
|
+
export declare function packingFor(packing: VertexPacking, name: string): AttributePacking;
|
|
47
|
+
/** The value every vertex shares, for an attribute packed as `'constant'`. */
|
|
48
|
+
export declare function sharedValue(values: Float32Array, width: number): number[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** The attributes this decides for. Every other one travels as floats. */
|
|
2
|
+
export const PACKED_ATTRIBUTES = [
|
|
3
|
+
'normals',
|
|
4
|
+
'tangents',
|
|
5
|
+
'colors',
|
|
6
|
+
'emissive',
|
|
7
|
+
'weights',
|
|
8
|
+
'weights2',
|
|
9
|
+
];
|
|
10
|
+
/** The choice for this mesh. `dynamic` is a mesh whose positions and normals are rewritten. */
|
|
11
|
+
export function vertexPackingOf(data, dynamic) {
|
|
12
|
+
return {
|
|
13
|
+
normals: dynamic ? 'float' : fixedOrFloat(data.normals, -1),
|
|
14
|
+
tangents: fixedOrFloat(data.tangents, -1),
|
|
15
|
+
colors: shared(data.colors, 3) ? 'constant' : fixedOrFloat(data.colors, 0),
|
|
16
|
+
emissive: shared(data.emissive, 1) ? 'constant' : 'float',
|
|
17
|
+
weights: fixedOrFloat(data.weights, 0),
|
|
18
|
+
weights2: fixedOrFloat(data.weights2, 0),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What 1 is in each fixed point, the values WebGPU's `snorm16` and `unorm16` and WebGL2's
|
|
23
|
+
* normalised `SHORT` and `UNSIGNED_SHORT` divide by. A value is stored rounded to nearest, so 1, 0
|
|
24
|
+
* and -1 come back exactly.
|
|
25
|
+
*/
|
|
26
|
+
export const SNORM16_ONE = 32767;
|
|
27
|
+
export const UNORM16_ONE = 65535;
|
|
28
|
+
/** One value in the fixed point whose 1 is `one`, rounded to nearest: the only place it is. */
|
|
29
|
+
export function toFixed16(value, one) {
|
|
30
|
+
return Math.round(value * one);
|
|
31
|
+
}
|
|
32
|
+
/** An attribute's values in its fixed point, tightly, for a backend reading separate arrays. */
|
|
33
|
+
export function fixedArray(values, packing) {
|
|
34
|
+
const out = packing === 'snorm16' ? new Int16Array(values.length) : new Uint16Array(values.length);
|
|
35
|
+
const one = packing === 'snorm16' ? SNORM16_ONE : UNORM16_ONE;
|
|
36
|
+
for (let at = 0; at < values.length; at++)
|
|
37
|
+
out[at] = toFixed16(values[at], one);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** How `name` travels under this packing: floats for an attribute it does not decide for. */
|
|
41
|
+
export function packingFor(packing, name) {
|
|
42
|
+
return packing[name] ?? 'float';
|
|
43
|
+
}
|
|
44
|
+
/** The value every vertex shares, for an attribute packed as `'constant'`. */
|
|
45
|
+
export function sharedValue(values, width) {
|
|
46
|
+
return Array.from(values.subarray(0, width));
|
|
47
|
+
}
|
|
48
|
+
/** Fixed point where every value lies in [low, 1]; floats otherwise, and for a NaN. */
|
|
49
|
+
function fixedOrFloat(values, low) {
|
|
50
|
+
if (values === undefined)
|
|
51
|
+
return 'float';
|
|
52
|
+
for (let at = 0; at < values.length; at++) {
|
|
53
|
+
const value = values[at];
|
|
54
|
+
if (!(value >= low && value <= 1))
|
|
55
|
+
return 'float';
|
|
56
|
+
}
|
|
57
|
+
return low < 0 ? 'snorm16' : 'unorm16';
|
|
58
|
+
}
|
|
59
|
+
/** Whether every vertex holds the first vertex's value. Not for a mesh of none. */
|
|
60
|
+
function shared(values, width) {
|
|
61
|
+
if (values.length < width)
|
|
62
|
+
return false;
|
|
63
|
+
for (let at = width; at < values.length; at++) {
|
|
64
|
+
if (values[at] !== values[at % width])
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
package/dist/ui/splash.d.ts
CHANGED
|
@@ -49,8 +49,9 @@ export declare const SPLASH_FADE_MS = 420;
|
|
|
49
49
|
/**
|
|
50
50
|
* When the badge gives the screen to the game. Two conditions and a cap, each for a named failure.
|
|
51
51
|
*
|
|
52
|
-
* - **The game has painted
|
|
53
|
-
*
|
|
52
|
+
* - **The game has painted**, and every promise it asked the badge to wait for has settled
|
|
53
|
+
* (`holdSplash`). Swapping before that shows the empty canvas, or the half-loaded first screen,
|
|
54
|
+
* the badge exists to cover.
|
|
54
55
|
* - **The minimum has passed.** A badge that vanishes the instant a fast machine paints is a
|
|
55
56
|
* flicker, and the machines that boot fastest are the ones a developer tests on.
|
|
56
57
|
* - **The cap overrides both**, and is checked first so that a minimum longer than the cap
|
|
@@ -75,7 +76,46 @@ export interface MountedSplash {
|
|
|
75
76
|
present(): void;
|
|
76
77
|
/** Take it down now, whatever the clock says. Idempotent. */
|
|
77
78
|
release(): void;
|
|
79
|
+
/** Keep it up until `until` settles as well, under `capMs`. False once it has left. */
|
|
80
|
+
holdUntil(until: PromiseLike<unknown>, capMs?: number): boolean;
|
|
78
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* What a badge waits for besides the clock: the game's first frame, and every promise the game asked
|
|
84
|
+
* it to wait for (`holdSplash`), under the cap the game named, the longest of several, or the engine's.
|
|
85
|
+
*
|
|
86
|
+
* **A hold settles whichever way its promise does.** A load that failed must not pin the logo: the
|
|
87
|
+
* game is shown, broken and visible, which is the cap's own argument. Pure apart from the promises,
|
|
88
|
+
* so the rule is testable in Node.
|
|
89
|
+
*/
|
|
90
|
+
export declare class SplashState {
|
|
91
|
+
/** Whether a frame of the game has reached the screen. */
|
|
92
|
+
painted: boolean;
|
|
93
|
+
private pending;
|
|
94
|
+
/** The longest cap a hold has named, or 0 for none. */
|
|
95
|
+
private asked;
|
|
96
|
+
/** The longest the badge may stay: the cap a hold named, the longest of several, or the engine's. */
|
|
97
|
+
get capMs(): number;
|
|
98
|
+
/** Wait for `until` too, under `capMs` where it names one; `settled` once it has. */
|
|
99
|
+
hold(until: PromiseLike<unknown>, capMs: number | undefined, settled: () => void): void;
|
|
100
|
+
/** Painted, with every hold settled: what `splashDecision` calls the content being ready. */
|
|
101
|
+
get ready(): boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Keep the engine's badge up until `until` settles, as well as until the game's first frame: a game
|
|
105
|
+
* whose first screen loads for longer than that frame — a stage, figures and their pictures on a
|
|
106
|
+
* first visit — is seen whole rather than loading. **`capMs` is the game's own ceiling** on the
|
|
107
|
+
* badge in place of the engine's twenty seconds, longer for a slow first visit or shorter, so a load
|
|
108
|
+
* that never settles is still shown in the end; the longest of several holds' wins, and the minimum
|
|
109
|
+
* hold is unchanged.
|
|
110
|
+
*
|
|
111
|
+
* **Frames run while the game loads behind the plate**, since a load may need them, and are held
|
|
112
|
+
* once it has settled, for whatever is left of the minimum, as they are after a first frame with no
|
|
113
|
+
* hold. Returns whether there was a badge to hold: none in a packaged build, whose shell shows its
|
|
114
|
+
* own until the first frame, nor where `splash: false` or `?splash=0` turned it off.
|
|
115
|
+
*/
|
|
116
|
+
export declare function holdSplash(until: PromiseLike<unknown>, options?: {
|
|
117
|
+
capMs?: number;
|
|
118
|
+
}): boolean;
|
|
79
119
|
/**
|
|
80
120
|
* Put the badge up, and hand back the two verbs that take it down.
|
|
81
121
|
*
|
package/dist/ui/splash.js
CHANGED
|
@@ -80,8 +80,9 @@ export const SPLASH_FADE_MS = 420;
|
|
|
80
80
|
/**
|
|
81
81
|
* When the badge gives the screen to the game. Two conditions and a cap, each for a named failure.
|
|
82
82
|
*
|
|
83
|
-
* - **The game has painted
|
|
84
|
-
*
|
|
83
|
+
* - **The game has painted**, and every promise it asked the badge to wait for has settled
|
|
84
|
+
* (`holdSplash`). Swapping before that shows the empty canvas, or the half-loaded first screen,
|
|
85
|
+
* the badge exists to cover.
|
|
85
86
|
* - **The minimum has passed.** A badge that vanishes the instant a fast machine paints is a
|
|
86
87
|
* flicker, and the machines that boot fastest are the ones a developer tests on.
|
|
87
88
|
* - **The cap overrides both**, and is checked first so that a minimum longer than the cap
|
|
@@ -99,6 +100,58 @@ export function splashDecision(elapsedMs, minMs, contentReady, capMs = SPLASH_HA
|
|
|
99
100
|
return 'hold';
|
|
100
101
|
return elapsedMs >= minMs ? 'swap' : 'hold';
|
|
101
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* What a badge waits for besides the clock: the game's first frame, and every promise the game asked
|
|
105
|
+
* it to wait for (`holdSplash`), under the cap the game named, the longest of several, or the engine's.
|
|
106
|
+
*
|
|
107
|
+
* **A hold settles whichever way its promise does.** A load that failed must not pin the logo: the
|
|
108
|
+
* game is shown, broken and visible, which is the cap's own argument. Pure apart from the promises,
|
|
109
|
+
* so the rule is testable in Node.
|
|
110
|
+
*/
|
|
111
|
+
export class SplashState {
|
|
112
|
+
/** Whether a frame of the game has reached the screen. */
|
|
113
|
+
painted = false;
|
|
114
|
+
pending = 0;
|
|
115
|
+
/** The longest cap a hold has named, or 0 for none. */
|
|
116
|
+
asked = 0;
|
|
117
|
+
/** The longest the badge may stay: the cap a hold named, the longest of several, or the engine's. */
|
|
118
|
+
get capMs() {
|
|
119
|
+
return this.asked > 0 ? this.asked : SPLASH_HARD_CAP_MS;
|
|
120
|
+
}
|
|
121
|
+
/** Wait for `until` too, under `capMs` where it names one; `settled` once it has. */
|
|
122
|
+
hold(until, capMs, settled) {
|
|
123
|
+
this.pending += 1;
|
|
124
|
+
if (capMs !== undefined && Number.isFinite(capMs) && capMs > this.asked)
|
|
125
|
+
this.asked = capMs;
|
|
126
|
+
const done = () => {
|
|
127
|
+
this.pending -= 1;
|
|
128
|
+
settled();
|
|
129
|
+
};
|
|
130
|
+
Promise.resolve(until).then(done, done);
|
|
131
|
+
}
|
|
132
|
+
/** Painted, with every hold settled: what `splashDecision` calls the content being ready. */
|
|
133
|
+
get ready() {
|
|
134
|
+
return this.painted && this.pending === 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** The badge on the page, which `holdSplash` holds. One screen, so one badge: see `bootGate.ts`. */
|
|
138
|
+
let mounted = null;
|
|
139
|
+
/**
|
|
140
|
+
* Keep the engine's badge up until `until` settles, as well as until the game's first frame: a game
|
|
141
|
+
* whose first screen loads for longer than that frame — a stage, figures and their pictures on a
|
|
142
|
+
* first visit — is seen whole rather than loading. **`capMs` is the game's own ceiling** on the
|
|
143
|
+
* badge in place of the engine's twenty seconds, longer for a slow first visit or shorter, so a load
|
|
144
|
+
* that never settles is still shown in the end; the longest of several holds' wins, and the minimum
|
|
145
|
+
* hold is unchanged.
|
|
146
|
+
*
|
|
147
|
+
* **Frames run while the game loads behind the plate**, since a load may need them, and are held
|
|
148
|
+
* once it has settled, for whatever is left of the minimum, as they are after a first frame with no
|
|
149
|
+
* hold. Returns whether there was a badge to hold: none in a packaged build, whose shell shows its
|
|
150
|
+
* own until the first frame, nor where `splash: false` or `?splash=0` turned it off.
|
|
151
|
+
*/
|
|
152
|
+
export function holdSplash(until, options = {}) {
|
|
153
|
+
return mounted?.holdUntil(until, options.capMs) ?? false;
|
|
154
|
+
}
|
|
102
155
|
/**
|
|
103
156
|
* The badge markup. One constant string, interpolated into nothing.
|
|
104
157
|
*
|
|
@@ -228,17 +281,18 @@ export function mountSplash(options = {}) {
|
|
|
228
281
|
the document and already styled; nothing here needs to be undone. */
|
|
229
282
|
}
|
|
230
283
|
}
|
|
231
|
-
|
|
284
|
+
const state = new SplashState();
|
|
232
285
|
let leaving = false;
|
|
233
286
|
let timer = null;
|
|
234
287
|
/*
|
|
235
|
-
* **Freeze the game the moment it has proved it can draw.**
|
|
288
|
+
* **Freeze the game the moment it has proved it can draw and has loaded.**
|
|
236
289
|
*
|
|
237
|
-
*
|
|
238
|
-
* game would otherwise spend running behind an opaque screen.
|
|
290
|
+
* Ready is the first `endFrame` with every hold settled (`SplashState`), and everything after it
|
|
291
|
+
* until the plate leaves is time the game would otherwise spend running behind an opaque screen.
|
|
292
|
+
* Before it, frames run: a load the game is holding the badge for may need them. `bootGate` is read by `startLoop`
|
|
239
293
|
* and named by neither of them; see its own comment for why the state is shared that way.
|
|
240
294
|
*/
|
|
241
|
-
const openGate = holdFrames(() =>
|
|
295
|
+
const openGate = holdFrames(() => state.ready && !leaving);
|
|
242
296
|
const remove = () => {
|
|
243
297
|
/* Leaving the top layer is what `hidePopover` is for. `remove()` alone takes the element out
|
|
244
298
|
of the document, and an element removed while shown is not a state worth relying on. */
|
|
@@ -256,6 +310,8 @@ export function mountSplash(options = {}) {
|
|
|
256
310
|
if (leaving)
|
|
257
311
|
return;
|
|
258
312
|
leaving = true;
|
|
313
|
+
if (mounted === badge)
|
|
314
|
+
mounted = null;
|
|
259
315
|
/* Before the fade, not after it: the game resumes as the plate becomes transparent, so what
|
|
260
316
|
shows through is a running frame rather than the still one it was frozen on. */
|
|
261
317
|
openGate();
|
|
@@ -278,23 +334,32 @@ export function mountSplash(options = {}) {
|
|
|
278
334
|
if (leaving)
|
|
279
335
|
return;
|
|
280
336
|
const elapsed = performance.now() - started;
|
|
281
|
-
if (splashDecision(elapsed, minMs,
|
|
337
|
+
if (splashDecision(elapsed, minMs, state.ready, state.capMs) === 'swap') {
|
|
282
338
|
release();
|
|
283
339
|
return;
|
|
284
340
|
}
|
|
285
341
|
if (timer !== null)
|
|
286
342
|
clearTimeout(timer);
|
|
287
|
-
const next =
|
|
343
|
+
const next = state.ready ? minMs - elapsed : state.capMs - elapsed;
|
|
288
344
|
timer = setTimeout(settle, Math.max(0, next));
|
|
289
345
|
};
|
|
290
|
-
|
|
291
|
-
return {
|
|
346
|
+
const badge = {
|
|
292
347
|
present() {
|
|
293
|
-
if (painted || leaving)
|
|
348
|
+
if (state.painted || leaving)
|
|
294
349
|
return;
|
|
295
|
-
painted = true;
|
|
350
|
+
state.painted = true;
|
|
296
351
|
settle();
|
|
297
352
|
},
|
|
298
353
|
release,
|
|
354
|
+
holdUntil(until, capMs) {
|
|
355
|
+
if (leaving)
|
|
356
|
+
return false;
|
|
357
|
+
state.hold(until, capMs, settle);
|
|
358
|
+
settle();
|
|
359
|
+
return true;
|
|
360
|
+
},
|
|
299
361
|
};
|
|
362
|
+
mounted = badge;
|
|
363
|
+
settle();
|
|
364
|
+
return badge;
|
|
300
365
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.11.1",
|
|
4
4
|
"description": "The runtime of DriftEngine, a WebGPU game engine with a WebGL2 fallback: renderer, loop, input, cameras, scene graph and physics",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.11.1",
|
|
33
|
+
"@driftengine/physics": "4.11.1",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@driftengine/nav": "4.
|
|
71
|
-
"@driftengine/network": "4.
|
|
72
|
-
"@driftengine/texture": "4.
|
|
70
|
+
"@driftengine/nav": "4.11.1",
|
|
71
|
+
"@driftengine/network": "4.11.1",
|
|
72
|
+
"@driftengine/texture": "4.11.1"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/index.ts
CHANGED
|
@@ -481,6 +481,7 @@ export type {
|
|
|
481
481
|
LineHandle,
|
|
482
482
|
MeshHandle,
|
|
483
483
|
MeshOptions,
|
|
484
|
+
PrepareOptions,
|
|
484
485
|
RenderBackend,
|
|
485
486
|
RendererApi,
|
|
486
487
|
ParticleHandle,
|
|
@@ -539,8 +540,8 @@ export type { LightmapPage, LightmapRegion } from './render/lightmap.ts';
|
|
|
539
540
|
* A skinned cloth solved where the renderer can: compute on WebGPU, the CPU under WebGL2. A function
|
|
540
541
|
* rather than a renderer method so only a game that imports it carries the solver.
|
|
541
542
|
*/
|
|
542
|
-
export { createSkinnedCloth } from './render/skinnedClothRun.ts';
|
|
543
|
-
export type { SkinnedClothSolver } from './render/skinnedClothRun.ts';
|
|
543
|
+
export { createSkinnedCloth, createSkinnedClothSet } from './render/skinnedClothRun.ts';
|
|
544
|
+
export type { SkinnedClothSet, SkinnedClothSolver } from './render/skinnedClothRun.ts';
|
|
544
545
|
|
|
545
546
|
/**
|
|
546
547
|
* What a frame asked of a backend, against the ceilings that backend imposes.
|
|
@@ -652,6 +653,7 @@ export type {
|
|
|
652
653
|
DirectionalShadowDepthLayers,
|
|
653
654
|
GlassShadows,
|
|
654
655
|
OutputTransform,
|
|
656
|
+
PipelineCompile,
|
|
655
657
|
RenderQuality,
|
|
656
658
|
RenderQualityOptions,
|
|
657
659
|
ShadowFilterTaps,
|
|
@@ -663,6 +665,7 @@ export { MAX_POINT_LIGHTS, SURFACE_TEXTURE_UNIT } from './render/lightBudget.ts'
|
|
|
663
665
|
export { MAX_CLUSTERED_LIGHTS } from './render/clusteredLights.ts';
|
|
664
666
|
export { SurfaceTexture } from './render/surfaceTexture.ts';
|
|
665
667
|
export type { SurfaceMaterial, SurfaceTextureOptions } from './render/surfaceTexture.ts';
|
|
668
|
+
export type { SurfaceProjection } from './render/surfaceProjection.ts';
|
|
666
669
|
/*
|
|
667
670
|
* Compressed textures — BC, ETC2 and EAC, ASTC: the source `createSurfaceTexture` takes as blocks,
|
|
668
671
|
* the one question to ask before handing one over — whether this device takes its format
|
|
@@ -991,6 +994,7 @@ export {
|
|
|
991
994
|
SPLASH_HARD_CAP_MS,
|
|
992
995
|
SPLASH_MIN_MS,
|
|
993
996
|
forcedSplash,
|
|
997
|
+
holdSplash,
|
|
994
998
|
mountSplash,
|
|
995
999
|
splashDecision,
|
|
996
1000
|
splashWanted,
|
|
@@ -113,7 +113,8 @@ export class AmbientOcclusionPass {
|
|
|
113
113
|
* by, or null if this pass could not run, which the caller must read as "no occlusion"
|
|
114
114
|
* rather than as black.
|
|
115
115
|
*
|
|
116
|
-
* `invProjection` is column-major, as `gl-matrix` produces it.
|
|
116
|
+
* `invProjection` is column-major, as `gl-matrix` produces it. `fade` is `uFade`, where the
|
|
117
|
+
* occlusion fades out with distance (`occlusionFade.ts`).
|
|
117
118
|
*/
|
|
118
119
|
run(
|
|
119
120
|
depth: WebGLTexture,
|
|
@@ -122,6 +123,7 @@ export class AmbientOcclusionPass {
|
|
|
122
123
|
radius: number,
|
|
123
124
|
projScale: Float32Array,
|
|
124
125
|
invProjection: Float32Array,
|
|
126
|
+
fade: Float32Array,
|
|
125
127
|
): WebGLTexture | null {
|
|
126
128
|
if (!this.ensureSize(width, height)) return null;
|
|
127
129
|
const { gl } = this;
|
|
@@ -142,6 +144,7 @@ export class AmbientOcclusionPass {
|
|
|
142
144
|
gl.uniform1i(this.estimateUniforms['uDepth'] ?? null, 0);
|
|
143
145
|
gl.uniform2fv(this.estimateUniforms['uProjScale'] ?? null, projScale);
|
|
144
146
|
gl.uniform1f(this.estimateUniforms['uRadius'] ?? null, radius);
|
|
147
|
+
gl.uniform2fv(this.estimateUniforms['uFade'] ?? null, fade);
|
|
145
148
|
gl.uniformMatrix4fv(this.estimateUniforms['uInvProjection'] ?? null, false, invProjection);
|
|
146
149
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
147
150
|
|
|
@@ -235,6 +235,15 @@ export interface SurfaceTextureHandle {
|
|
|
235
235
|
* `WebGLBuffer` in a private field, so no WebGPU object can satisfy it however identical its
|
|
236
236
|
* shape. It is not meant to be read; it is meant to be enforced.
|
|
237
237
|
*/
|
|
238
|
+
/** Which of a draw's pipelines `prepareMesh` and `prepareInstanced` compile beside the opaque ones. */
|
|
239
|
+
export interface PrepareOptions {
|
|
240
|
+
/**
|
|
241
|
+
* The blended ones too, for a draw made with `drawTranslucentMesh` or `drawTranslucentInstanced`.
|
|
242
|
+
* Off by default: an opaque surface would pay a compile it never draws with.
|
|
243
|
+
*/
|
|
244
|
+
readonly translucent?: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
238
247
|
/** What a mesh needs to be told at creation that its geometry cannot say. */
|
|
239
248
|
export interface MeshOptions {
|
|
240
249
|
/**
|
|
@@ -312,6 +321,8 @@ export type RendererApi = Omit<
|
|
|
312
321
|
| 'disposeSurfaceTexture'
|
|
313
322
|
| 'setSurfaceTexture'
|
|
314
323
|
| 'setMaterial'
|
|
324
|
+
| 'prepareMesh'
|
|
325
|
+
| 'prepareInstanced'
|
|
315
326
|
| 'setSurfaceOverlay'
|
|
316
327
|
| 'createClothBinding'
|
|
317
328
|
| 'createClothParticles'
|
|
@@ -455,6 +466,26 @@ export type RendererApi = Omit<
|
|
|
455
466
|
* setter each — and `setSurfaceTexture` is now a wrapper over it.
|
|
456
467
|
*/
|
|
457
468
|
setMaterial(material: SurfaceMaterial<SurfaceTextureHandle> | null): void;
|
|
469
|
+
/**
|
|
470
|
+
* Compile, off the main thread, every pipeline a draw of `mesh` in `material` takes that making
|
|
471
|
+
* the mesh did not — a two-sided, cut-out or shaded-by-a-model variant, a material's lit switches,
|
|
472
|
+
* a skin's halves — so the first frame that draws it waits on none; `ready()` resolves once they
|
|
473
|
+
* have. For `pipelineCompile: 'skip'` above all, where a draw whose pipeline is new is left out
|
|
474
|
+
* until it lands: a figure brought on screen whole rather than without its face for a few frames.
|
|
475
|
+
* A rigged mesh is prepared as drawn with a palette, and one with targets both with weights and
|
|
476
|
+
* without. Call it between frames: it sets `material` to read it and leaves none set after.
|
|
477
|
+
*/
|
|
478
|
+
prepareMesh(
|
|
479
|
+
mesh: MeshHandle,
|
|
480
|
+
material: SurfaceMaterial<SurfaceTextureHandle> | null,
|
|
481
|
+
options?: PrepareOptions,
|
|
482
|
+
): void;
|
|
483
|
+
/** `prepareMesh` for an instanced batch drawn in `material`, its own pipelines being its own. */
|
|
484
|
+
prepareInstanced(
|
|
485
|
+
batch: InstancedHandle,
|
|
486
|
+
material: SurfaceMaterial<SurfaceTextureHandle> | null,
|
|
487
|
+
options?: PrepareOptions,
|
|
488
|
+
): void;
|
|
458
489
|
|
|
459
490
|
/**
|
|
460
491
|
* A 3D text object: one string, drawn as instanced glyph cubes over the scene.
|