reze-engine 0.42.3 → 0.50.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/README.md +40 -410
- package/dist/camera.d.ts +3 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +33 -8
- package/dist/engine.d.ts +924 -29
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4043 -278
- package/dist/graph/registry.d.ts +2 -2
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +1 -1
- package/dist/graph/slots.d.ts +0 -1
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +37 -9
- package/dist/hdr.d.ts +18 -0
- package/dist/hdr.d.ts.map +1 -0
- package/dist/hdr.js +162 -0
- package/dist/ibl.d.ts +19 -0
- package/dist/ibl.d.ts.map +1 -0
- package/dist/ibl.js +113 -0
- package/dist/ik-solver.d.ts +2 -1
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/math.d.ts +20 -1
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +23 -16
- package/dist/midi-loader.d.ts +10 -0
- package/dist/midi-loader.d.ts.map +1 -0
- package/dist/midi-loader.js +247 -0
- package/dist/model.d.ts +2 -13
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +29 -0
- package/dist/param-track.d.ts +48 -0
- package/dist/param-track.d.ts.map +1 -0
- package/dist/param-track.js +80 -0
- package/dist/physics/types.d.ts.map +1 -1
- package/dist/physics/types.js +3 -0
- package/dist/reflection.d.ts +27 -0
- package/dist/reflection.d.ts.map +1 -0
- package/dist/reflection.js +93 -0
- package/dist/shaders/anchor-table.d.ts +56 -0
- package/dist/shaders/anchor-table.d.ts.map +1 -0
- package/dist/shaders/anchor-table.js +128 -0
- package/dist/shaders/audio-api.d.ts +3 -0
- package/dist/shaders/audio-api.d.ts.map +1 -0
- package/dist/shaders/audio-api.js +81 -0
- package/dist/shaders/cast-api.d.ts +2 -0
- package/dist/shaders/cast-api.d.ts.map +1 -0
- package/dist/shaders/cast-api.js +121 -0
- package/dist/shaders/cast-layout.d.ts +21 -0
- package/dist/shaders/cast-layout.d.ts.map +1 -0
- package/dist/shaders/cast-layout.js +20 -0
- package/dist/shaders/lights.d.ts +79 -0
- package/dist/shaders/lights.d.ts.map +1 -0
- package/dist/shaders/lights.js +269 -0
- package/dist/shaders/lyrics-api.d.ts +39 -0
- package/dist/shaders/lyrics-api.d.ts.map +1 -0
- package/dist/shaders/lyrics-api.js +187 -0
- package/dist/shaders/materials/common.d.ts +2 -4
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +87 -35
- package/dist/shaders/midi-api.d.ts +10 -0
- package/dist/shaders/midi-api.d.ts.map +1 -0
- package/dist/shaders/midi-api.js +114 -0
- package/dist/shaders/passes/composite.d.ts +31 -15
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +225 -135
- package/dist/shaders/passes/cull.d.ts +2 -0
- package/dist/shaders/passes/cull.d.ts.map +1 -0
- package/dist/shaders/passes/cull.js +138 -0
- package/dist/shaders/passes/field-blit.d.ts +26 -0
- package/dist/shaders/passes/field-blit.d.ts.map +1 -0
- package/dist/shaders/passes/field-blit.js +65 -0
- package/dist/shaders/passes/grid.d.ts +31 -0
- package/dist/shaders/passes/grid.d.ts.map +1 -0
- package/dist/shaders/passes/grid.js +169 -0
- package/dist/shaders/passes/ground.d.ts +13 -1
- package/dist/shaders/passes/ground.d.ts.map +1 -1
- package/dist/shaders/passes/ground.js +170 -25
- package/dist/shaders/passes/hosted-api.d.ts +57 -0
- package/dist/shaders/passes/hosted-api.d.ts.map +1 -0
- package/dist/shaders/passes/hosted-api.js +166 -0
- package/dist/shaders/passes/id-debug.d.ts +28 -0
- package/dist/shaders/passes/id-debug.d.ts.map +1 -0
- package/dist/shaders/passes/id-debug.js +74 -0
- package/dist/shaders/passes/particles.d.ts +66 -0
- package/dist/shaders/passes/particles.d.ts.map +1 -0
- package/dist/shaders/passes/particles.js +279 -0
- package/dist/shaders/passes/scene-contract.d.ts +128 -0
- package/dist/shaders/passes/scene-contract.d.ts.map +1 -0
- package/dist/shaders/passes/scene-contract.js +207 -0
- package/dist/shaders/passes/sim.d.ts +34 -0
- package/dist/shaders/passes/sim.d.ts.map +1 -0
- package/dist/shaders/passes/sim.js +169 -0
- package/dist/shaders/passes/trails.d.ts +59 -0
- package/dist/shaders/passes/trails.d.ts.map +1 -0
- package/dist/shaders/passes/trails.js +340 -0
- package/dist/shaders/score-api.d.ts +10 -0
- package/dist/shaders/score-api.d.ts.map +1 -0
- package/dist/shaders/score-api.js +114 -0
- package/dist/shadow-cascades.d.ts +45 -0
- package/dist/shadow-cascades.d.ts.map +1 -0
- package/dist/shadow-cascades.js +70 -0
- package/dist/vmd-loader.d.ts +3 -2
- package/dist/vmd-loader.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/camera.ts +31 -8
- package/src/engine.ts +4535 -296
- package/src/graph/registry.ts +2 -2
- package/src/graph/slots.ts +37 -9
- package/src/hdr.ts +156 -0
- package/src/ibl.ts +115 -0
- package/src/ik-solver.ts +1 -1
- package/src/index.ts +12 -0
- package/src/math.ts +23 -17
- package/src/midi-loader.ts +246 -0
- package/src/model.ts +31 -3
- package/src/param-track.ts +83 -0
- package/src/physics/types.ts +4 -1
- package/src/reflection.ts +94 -0
- package/src/shaders/anchor-table.ts +147 -0
- package/src/shaders/audio-api.ts +82 -0
- package/src/shaders/cast-api.ts +123 -0
- package/src/shaders/cast-layout.ts +20 -0
- package/src/shaders/lights.ts +280 -0
- package/src/shaders/lyrics-api.ts +202 -0
- package/src/shaders/materials/common.ts +89 -35
- package/src/shaders/midi-api.ts +116 -0
- package/src/shaders/passes/composite.ts +244 -136
- package/src/shaders/passes/cull.ts +139 -0
- package/src/shaders/passes/grid.ts +178 -0
- package/src/shaders/passes/ground.ts +172 -25
- package/src/shaders/passes/hosted-api.ts +171 -0
- package/src/shaders/passes/id-debug.ts +75 -0
- package/src/shaders/passes/particles.ts +340 -0
- package/src/shaders/passes/scene-contract.ts +266 -0
- package/src/shaders/passes/trails.ts +390 -0
- package/src/shadow-cascades.ts +97 -0
- package/src/vmd-loader.ts +2 -2
package/dist/engine.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { Model } from "./model";
|
|
|
3
3
|
import { RezePhysics } from "./physics";
|
|
4
4
|
import type { WindOptions } from "./physics/world";
|
|
5
5
|
import { type AssetReader } from "./asset-reader";
|
|
6
|
+
import { type ParamKey } from "./param-track";
|
|
7
|
+
import { type HdrImage } from "./hdr";
|
|
8
|
+
import { type LyricLine, type LyricRect } from "./shaders/lyrics-api";
|
|
6
9
|
import { type CompileOptions, type StyleSlot } from "./graph/compile";
|
|
7
10
|
import type { AlphaMode, RenderClass } from "./graph/render-class";
|
|
8
11
|
import type { ApplyStyleGroupResult, ApplyStyleGroupsResult, StyleGroup } from "./graph/style-group";
|
|
@@ -26,18 +29,35 @@ type GroupInstall = {
|
|
|
26
29
|
* an unchanged group. */
|
|
27
30
|
signature: string;
|
|
28
31
|
};
|
|
29
|
-
|
|
32
|
+
type RaycastCallback = (modelName: string, material: string | null, bone: string | null, screenX: number, screenY: number) => void;
|
|
30
33
|
/** Select a folder (webkitdirectory) and pass FileList or File[]; pmxFile picks which .pmx when several exist. */
|
|
31
34
|
export type LoadModelFromFilesOptions = {
|
|
32
35
|
files: FileList | File[];
|
|
33
36
|
pmxFile?: File;
|
|
34
37
|
};
|
|
35
|
-
|
|
38
|
+
type WorldOptions = {
|
|
36
39
|
/** Linear scene-referred color of the World Background (Blender: World > Surface > Color). */
|
|
37
40
|
color?: Vec3;
|
|
38
41
|
/** Multiplier on world color (Blender: World > Surface > Strength). */
|
|
39
42
|
strength?: number;
|
|
40
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* One note in a score: when it sounds, for how long, at what pitch, how hard.
|
|
46
|
+
*
|
|
47
|
+
* Seconds and MIDI pitch (60 = middle C) rather than ticks and a tempo map — a
|
|
48
|
+
* score is consumed against the scene clock, so anything the engine stores in
|
|
49
|
+
* musical time would have to be resolved to seconds before every read.
|
|
50
|
+
*/
|
|
51
|
+
export interface MidiNote {
|
|
52
|
+
/** Seconds from the start of the piece. */
|
|
53
|
+
start: number;
|
|
54
|
+
/** Seconds the note sounds for. */
|
|
55
|
+
duration: number;
|
|
56
|
+
/** MIDI pitch, 0–127. */
|
|
57
|
+
pitch: number;
|
|
58
|
+
/** How hard it was struck, 0–1. Defaults to 1. */
|
|
59
|
+
velocity?: number;
|
|
60
|
+
}
|
|
41
61
|
export type ModelTransform = {
|
|
42
62
|
position: Vec3;
|
|
43
63
|
rotation: Quat;
|
|
@@ -45,7 +65,7 @@ export type ModelTransform = {
|
|
|
45
65
|
scale: number;
|
|
46
66
|
visible: boolean;
|
|
47
67
|
};
|
|
48
|
-
|
|
68
|
+
type SunOptions = {
|
|
49
69
|
/** Linear color of the sun lamp (Blender: Light > Color). */
|
|
50
70
|
color?: Vec3;
|
|
51
71
|
/** Lamp power in Blender units (Blender: Light > Strength). */
|
|
@@ -61,9 +81,19 @@ export type EffectParamValue = number | {
|
|
|
61
81
|
y: number;
|
|
62
82
|
z: number;
|
|
63
83
|
};
|
|
84
|
+
/** A vector by shape rather than by class — Vec3 satisfies it, and so does a
|
|
85
|
+
* JSON object out of a scene document or a literal typed into a console. */
|
|
86
|
+
export type XYZ = {
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
z: number;
|
|
90
|
+
};
|
|
64
91
|
export type EffectResult = {
|
|
65
92
|
ok: boolean;
|
|
66
|
-
/** Compile/validation errors, line:col relative to the USER's WGSL.
|
|
93
|
+
/** Compile/validation errors, line:col relative to the USER's WGSL. Also
|
|
94
|
+
* carries non-fatal warnings on an effect that DID install — a directive
|
|
95
|
+
* that parsed but will never fire, an anchor the scene had no slot for. So
|
|
96
|
+
* a non-empty list is not a failure; `ok` is. */
|
|
67
97
|
diagnostics: string[];
|
|
68
98
|
/** Which mounts the WGSL declared — `fn background` / `fn foreground`. Both
|
|
69
99
|
* false only on a failed compile, since defining neither IS the failure. */
|
|
@@ -72,7 +102,7 @@ export type EffectResult = {
|
|
|
72
102
|
foreground: boolean;
|
|
73
103
|
};
|
|
74
104
|
};
|
|
75
|
-
|
|
105
|
+
type CameraOptions = {
|
|
76
106
|
/** Orbit distance from target. */
|
|
77
107
|
distance?: number;
|
|
78
108
|
/** World-space orbit center. */
|
|
@@ -193,23 +223,6 @@ export type EngineOptions = {
|
|
|
193
223
|
/** See {@link GizmoDragCallback}. */
|
|
194
224
|
onGizmoDrag?: GizmoDragCallback;
|
|
195
225
|
};
|
|
196
|
-
export declare const DEFAULT_ENGINE_OPTIONS: {
|
|
197
|
-
world: {
|
|
198
|
-
color: Vec3;
|
|
199
|
-
strength: number;
|
|
200
|
-
};
|
|
201
|
-
sun: {
|
|
202
|
-
color: Vec3;
|
|
203
|
-
strength: number;
|
|
204
|
-
direction: Vec3;
|
|
205
|
-
};
|
|
206
|
-
camera: {
|
|
207
|
-
distance: number;
|
|
208
|
-
target: Vec3;
|
|
209
|
-
fov: number;
|
|
210
|
-
};
|
|
211
|
-
onRaycast: undefined;
|
|
212
|
-
};
|
|
213
226
|
export interface EngineStats {
|
|
214
227
|
fps: number;
|
|
215
228
|
frameTime: number;
|
|
@@ -238,6 +251,75 @@ interface DrawCall {
|
|
|
238
251
|
outline?: {
|
|
239
252
|
bindGroup: GPUBindGroup;
|
|
240
253
|
};
|
|
254
|
+
/** MODEL-SPACE AABB over this material's index range, computed at load:
|
|
255
|
+
* [minX, minY, minZ, maxX, maxY, maxZ]. Usable for culling only while the
|
|
256
|
+
* owning model is rigid (see ModelInstance.rigid) — animation moves vertices
|
|
257
|
+
* out of it, which is why a skinned model culls per model instead. */
|
|
258
|
+
bounds: Float32Array;
|
|
259
|
+
/** Slot in the cull metadata and indirect-argument buffers. Reassigned every
|
|
260
|
+
* time the flat draw list is rebuilt (model added/removed, draws re-sorted).
|
|
261
|
+
* -1 until the list is built. */
|
|
262
|
+
cullIndex: number;
|
|
263
|
+
}
|
|
264
|
+
/** What getCullDiagnostics() reports: the GPU's answer, an independent CPU
|
|
265
|
+
* answer over the same source data, and every draw where they disagree. */
|
|
266
|
+
export interface CullDiagnostics {
|
|
267
|
+
drawCount: number;
|
|
268
|
+
modelCount: number;
|
|
269
|
+
/** Draws the GPU compute left with instanceCount = 1. */
|
|
270
|
+
cameraVisibleGpu: number;
|
|
271
|
+
shadowVisibleGpu: number;
|
|
272
|
+
/** The same test run on the CPU from the same bounds and frusta. */
|
|
273
|
+
cameraVisibleCpu: number;
|
|
274
|
+
shadowVisibleCpu: number;
|
|
275
|
+
/** How each model was bounded this frame — the split that decides whether a
|
|
276
|
+
* stage culls per material or per model. */
|
|
277
|
+
rigidModels: number;
|
|
278
|
+
skinnedModels: number;
|
|
279
|
+
mismatches: {
|
|
280
|
+
model: string;
|
|
281
|
+
material: string;
|
|
282
|
+
pass: "camera" | "shadow";
|
|
283
|
+
gpu: boolean;
|
|
284
|
+
cpu: boolean;
|
|
285
|
+
}[];
|
|
286
|
+
/** What each model was actually tested against. Without this a report saying
|
|
287
|
+
* "everything visible" is unreadable — you cannot tell a working cull looking
|
|
288
|
+
* at geometry that genuinely fills the screen from a cull that never rejects
|
|
289
|
+
* anything. The radius against the camera distance answers it directly. */
|
|
290
|
+
models: {
|
|
291
|
+
name: string;
|
|
292
|
+
rigid: boolean;
|
|
293
|
+
visible: boolean;
|
|
294
|
+
draws: number;
|
|
295
|
+
/** How many of this model's draws survived the camera frustum. */
|
|
296
|
+
cameraVisible: number;
|
|
297
|
+
/** How many survived the light frustum AND carry the PMX cast-shadow flag.
|
|
298
|
+
* Split from `casters` so a zero here is readable: no casters at all means
|
|
299
|
+
* the author turned shadows off, while casters with zero survivors means
|
|
300
|
+
* the light-frustum test rejected them. */
|
|
301
|
+
shadowVisible: number;
|
|
302
|
+
/** Draws with the PMX cast-shadow flag set (bit 0x04), before any culling. */
|
|
303
|
+
casters: number;
|
|
304
|
+
/** Sphere path only: the world centre and radius tested against. Null for a
|
|
305
|
+
* rigid model, whose draws each carry their own box instead. */
|
|
306
|
+
sphere: [number, number, number, number] | null;
|
|
307
|
+
}[];
|
|
308
|
+
/** Where the camera was, so the numbers above can be read against it. */
|
|
309
|
+
camera: {
|
|
310
|
+
eye: [number, number, number];
|
|
311
|
+
target: [number, number, number];
|
|
312
|
+
};
|
|
313
|
+
/** Times the draw list has been rebuilt since the engine started. It should
|
|
314
|
+
* climb only when scene STRUCTURE changes — a model loaded, a style group
|
|
315
|
+
* applied — and then stop. A number that keeps rising while nothing but the
|
|
316
|
+
* animation is moving means something is dirtying the list every frame, which
|
|
317
|
+
* is the failure mode that makes render bundles cost more than they save. */
|
|
318
|
+
rebuilds: number;
|
|
319
|
+
/** Times the render bundles have been re-recorded. Held to the same standard
|
|
320
|
+
* as `rebuilds`, and for the sharper reason: a bundle exists to be replayed,
|
|
321
|
+
* so one re-recorded every frame is strictly worse than not having it. */
|
|
322
|
+
bundleRecords: number;
|
|
241
323
|
}
|
|
242
324
|
interface PickDrawCall {
|
|
243
325
|
count: number;
|
|
@@ -246,6 +328,10 @@ interface PickDrawCall {
|
|
|
246
328
|
}
|
|
247
329
|
interface ModelInstance {
|
|
248
330
|
name: string;
|
|
331
|
+
/** This model's id in the id attachment — 1-based, so 0 stays "nothing".
|
|
332
|
+
* The pick pass has always minted it; the cast carries it now too, so an
|
|
333
|
+
* effect can compare what it reads out of the id buffer against a subject. */
|
|
334
|
+
objectId: number;
|
|
249
335
|
model: Model;
|
|
250
336
|
basePath: string;
|
|
251
337
|
assetReader: AssetReader;
|
|
@@ -258,7 +344,7 @@ interface ModelInstance {
|
|
|
258
344
|
skinMatrixBuffer: GPUBuffer;
|
|
259
345
|
drawCalls: DrawCall[];
|
|
260
346
|
shadowDrawCalls: DrawCall[];
|
|
261
|
-
|
|
347
|
+
shadowBindGroups: GPUBindGroup[];
|
|
262
348
|
mainPerInstanceBindGroup: GPUBindGroup;
|
|
263
349
|
pickPerInstanceBindGroup: GPUBindGroup;
|
|
264
350
|
pickDrawCalls: PickDrawCall[];
|
|
@@ -283,6 +369,24 @@ interface ModelInstance {
|
|
|
283
369
|
styleGroups: Map<string, GroupInstall>;
|
|
284
370
|
materialToGroup: Map<string, string>;
|
|
285
371
|
styleGroupGen: Map<string, number>;
|
|
372
|
+
/** Slot in the per-model cull buffer, assigned when the draw list is rebuilt. */
|
|
373
|
+
cullModelIndex: number;
|
|
374
|
+
/** Every bone shares one skin matrix (within tolerance) and no vertex morph can
|
|
375
|
+
* move a vertex out of its material's box — so the model is a rigid transform
|
|
376
|
+
* of its bind pose and its per-material AABBs are live. True for a stage, and
|
|
377
|
+
* for any character still in bind pose. Re-evaluated only on the frames the
|
|
378
|
+
* skin matrices are re-uploaded, which is never for an idle stage. */
|
|
379
|
+
rigid: boolean;
|
|
380
|
+
/** The shared skin matrix, when rigid: model space → world, INCLUDING the
|
|
381
|
+
* scene placement (setModelTransform bakes the root into skinning). */
|
|
382
|
+
rigidXform: Float32Array;
|
|
383
|
+
/** Bound on how far skinning can carry a vertex from the bone that drives it:
|
|
384
|
+
* max over vertices of max over influencing joints of |v − bindPos(joint)|,
|
|
385
|
+
* plus the largest single vertex-morph displacement. An AABB over the model's
|
|
386
|
+
* POSED bone positions grown by this contains every skinned vertex, because a
|
|
387
|
+
* skinned position is a convex combination of rigid images of v, each within
|
|
388
|
+
* that distance of its bone's posed position. */
|
|
389
|
+
skinMargin: number;
|
|
286
390
|
}
|
|
287
391
|
/**
|
|
288
392
|
* One material a type-8 morph can reach, with the uniform block as it loaded.
|
|
@@ -339,6 +443,10 @@ export declare class Engine {
|
|
|
339
443
|
private zeroStyleBuffer;
|
|
340
444
|
private mainPipelineLayout;
|
|
341
445
|
private sceneTargets;
|
|
446
|
+
/** The scene pass's attachment formats, settled at init once the device has
|
|
447
|
+
* said which HDR format it will blend. Every scene-pass pipeline asks
|
|
448
|
+
* scene-contract for its targets against these. */
|
|
449
|
+
private get sceneFormats();
|
|
342
450
|
private fullVertexBufferLayouts;
|
|
343
451
|
private defaultToonRampTexture;
|
|
344
452
|
private groundShadowPipeline;
|
|
@@ -381,6 +489,16 @@ export declare class Engine {
|
|
|
381
489
|
private hdrResolveTexture;
|
|
382
490
|
private static readonly MULTISAMPLE_COUNT;
|
|
383
491
|
private hdrFormat;
|
|
492
|
+
/** Main-pass depth. Float when the adapter offers depth32float-stencil8, which
|
|
493
|
+
* is also what makes reversed-Z worth switching on. */
|
|
494
|
+
private depthFormat;
|
|
495
|
+
/** Near maps to 1 and far to 0. Set once at init and never toggled — every
|
|
496
|
+
* pipeline's compare function and both depth clears are chosen from it. */
|
|
497
|
+
private reversedZ;
|
|
498
|
+
/** The compare a "draw what is in front" pipeline wants, either way round. */
|
|
499
|
+
private get depthAhead();
|
|
500
|
+
/** The value a cleared depth buffer holds: the FAR plane, whichever end that is. */
|
|
501
|
+
private get depthClear();
|
|
384
502
|
/** Stencil value stamped by eye draws so hair can stencil-test against it and
|
|
385
503
|
* alpha-blend a second pass over eye silhouette pixels (see-through-hair effect). */
|
|
386
504
|
private static readonly STENCIL_EYE_VALUE;
|
|
@@ -393,15 +511,229 @@ export declare class Engine {
|
|
|
393
511
|
* cleared / edge-faded regions like before).
|
|
394
512
|
* rg8unorm at 4× MSAA is 8 bytes/texel — still fits Apple TBDR tile memory comfortably. */
|
|
395
513
|
private static readonly BLOOM_MASK_FORMAT;
|
|
514
|
+
/**
|
|
515
|
+
* The master switch for the id attachment. OFF — and off having been proven
|
|
516
|
+
* to work, not off because it was never finished.
|
|
517
|
+
*
|
|
518
|
+
* ON, because there is finally something that reads it: rzObjectAt and
|
|
519
|
+
* rzMaterialAt in the field module let an effect mask itself to one character
|
|
520
|
+
* or one material. It was switched off in the meantime rather than left
|
|
521
|
+
* running — rg16uint at the pass's sample count is around 33MB at 1080p,
|
|
522
|
+
* cleared and stored every frame, and paying that for a buffer nobody read
|
|
523
|
+
* would have handed back the same order of bandwidth the empty field-pass
|
|
524
|
+
* clears had just saved.
|
|
525
|
+
*
|
|
526
|
+
* Verified through setIdDebug against a real scene: flat colour per material
|
|
527
|
+
* with hard edges (so nothing interpolates or resolves them), the floor on
|
|
528
|
+
* its reserved id, black exactly where nothing drew. Turning it back off is
|
|
529
|
+
* this line, and the accessors then answer 0 rather than failing to compile.
|
|
530
|
+
*/
|
|
531
|
+
private static readonly MRT_IDS;
|
|
532
|
+
/** The id attachment. Multisampled with the pass and NEVER resolved: an
|
|
533
|
+
* averaged id belongs to nothing, so consumers textureLoad sample 0. */
|
|
534
|
+
private idTexture;
|
|
535
|
+
private idView;
|
|
536
|
+
/** The id buffer drawn to the screen — see setIdDebug. */
|
|
537
|
+
private idDebugPipeline;
|
|
538
|
+
private idDebugBindGroupLayout;
|
|
539
|
+
private idDebugBindGroup;
|
|
540
|
+
private idDebug;
|
|
396
541
|
private multisampleMaskTexture;
|
|
397
542
|
private maskResolveTexture;
|
|
398
543
|
private maskResolveView;
|
|
544
|
+
/**
|
|
545
|
+
* The installed effect's particle system, or null when it declared none.
|
|
546
|
+
*
|
|
547
|
+
* A fixed pool: the count is chosen at install and the slots recycle, so there
|
|
548
|
+
* is no allocation and no spawn-rate bookkeeping in the hot path. Dead slots
|
|
549
|
+
* cost a degenerate quad the rasteriser rejects, which is cheaper than the
|
|
550
|
+
* prefix sum and readback a compacted draw list would need every frame.
|
|
551
|
+
*/
|
|
552
|
+
/** Ceiling for `// @particles`. Past this an author is asking for a stall. */
|
|
553
|
+
private static readonly MAX_PARTICLES;
|
|
554
|
+
private particleFrame;
|
|
555
|
+
/**
|
|
556
|
+
* The installed effect's persistent grid, or null when it declared none.
|
|
557
|
+
*
|
|
558
|
+
* Two textures, not one, and read/write alternate between them every frame:
|
|
559
|
+
* a shader cannot coherently read and write the same texture, so this is not
|
|
560
|
+
* an optimisation but the only correct shape. `parity` says which one holds
|
|
561
|
+
* the CURRENT grid — the one everything else samples.
|
|
562
|
+
*/
|
|
563
|
+
private simSampler;
|
|
564
|
+
private simFallbackView;
|
|
565
|
+
/** 1×1 transparent stand-in, for every layer binding with nothing behind it. */
|
|
566
|
+
private trailFallbackView;
|
|
567
|
+
/**
|
|
568
|
+
* The field layer: user background/foreground mounts, ONE TARGET PAIR PER
|
|
569
|
+
* RESOLUTION. Index 0 is full, index 1 is half — coarsest last, so the
|
|
570
|
+
* composite reads them full-over-half.
|
|
571
|
+
*
|
|
572
|
+
* `@fullres` used to be a property of the shared targets: one effect
|
|
573
|
+
* declaring it promoted the pass for every effect installed, so a starfield
|
|
574
|
+
* that upsamples perfectly paid four times the pixels because a keyboard
|
|
575
|
+
* beside it needed crisp edges. Measured, that was the largest avoidable cost
|
|
576
|
+
* in the frame — Footprints went from about 1.2ms to 4.5ms purely by being
|
|
577
|
+
* dragged along.
|
|
578
|
+
*
|
|
579
|
+
* The price is that a resolution boundary is now a LAYER boundary. Within a
|
|
580
|
+
* pair, effects blend in document order; across pairs the full-res layer
|
|
581
|
+
* composites over the half-res one whatever the document said. Invisible for
|
|
582
|
+
* the additive glows this is nearly always used for, and stated because it is
|
|
583
|
+
* the one thing document order stopped deciding.
|
|
584
|
+
*/
|
|
585
|
+
private static readonly FIELD_SCALES;
|
|
586
|
+
/** Reused for the per-frame field-clock upload — one 16-byte write per
|
|
587
|
+
* drawing effect, and allocating a fresh array for each would be garbage
|
|
588
|
+
* every frame. */
|
|
589
|
+
private fieldClockScratch;
|
|
590
|
+
/** Material parameters driven by the scene clock — see setStyleParamTrack. */
|
|
591
|
+
private paramTracks;
|
|
592
|
+
private fieldBgTextures;
|
|
593
|
+
private fieldBgViews;
|
|
594
|
+
private fieldFgTextures;
|
|
595
|
+
private fieldFgViews;
|
|
596
|
+
/** One per scale: the field shader reconstructs the full-res pixel it stands
|
|
597
|
+
* in for, so each pass needs its own (w, h, fullW, fullH). */
|
|
598
|
+
private fieldUniformBuffers;
|
|
599
|
+
private fieldFullW;
|
|
600
|
+
private fieldFullH;
|
|
601
|
+
private fieldBindGroupLayout;
|
|
602
|
+
private fieldPipelineLayout;
|
|
603
|
+
/**
|
|
604
|
+
* The audio analysis buffer every effect module binds: header
|
|
605
|
+
* [frames, bands, secondsPerFrame, audioTime], then [level, band0..bandN-1]
|
|
606
|
+
* per frame. Precomputed by the host for the whole track — never a live
|
|
607
|
+
* analyser, which would render silence during an export. Falls back to four
|
|
608
|
+
* zeroes (frames = 0) so layouts always bind.
|
|
609
|
+
*/
|
|
610
|
+
private audioBuffer;
|
|
611
|
+
private audioFallbackBuffer;
|
|
612
|
+
private midiBuffer;
|
|
613
|
+
private midiFallbackBuffer;
|
|
614
|
+
/** Fixed-size (LYRICS_FLOATS): setLyrics is a write, never a reallocation,
|
|
615
|
+
* so lyric data arriving after any effect reaches it with no re-binding. */
|
|
616
|
+
private lyricsBuffer;
|
|
617
|
+
/** The rasterised lines, for rzLyricText. Sized to the track that arrives —
|
|
618
|
+
* a 1×1 placeholder until one does, so a scene with no lyrics pays nothing
|
|
619
|
+
* and a song's text is stored at the resolution it is drawn at. */
|
|
620
|
+
private lyricsTexture;
|
|
621
|
+
private lyricsTextureView;
|
|
622
|
+
/** The notes as installed, kept CPU-side because the per-pitch key map is
|
|
623
|
+
* rebuilt from them every time the clock moves. */
|
|
624
|
+
private midiNotes;
|
|
625
|
+
/** Header + key map, re-uploaded per clock write. */
|
|
626
|
+
private midiLiveScratch;
|
|
627
|
+
private midiRelease;
|
|
628
|
+
private audioTimeScratch;
|
|
399
629
|
private renderPassDescriptor;
|
|
400
630
|
private compositePassDescriptor;
|
|
401
631
|
private compositePipelineIdentity;
|
|
402
632
|
private compositePipelineGamma;
|
|
403
633
|
private morphComputePipeline;
|
|
404
634
|
private morphComputeBindGroupLayout;
|
|
635
|
+
/** Null once the pipeline has failed to compile — the pass then does nothing
|
|
636
|
+
* rather than invalidating every command buffer it touches. */
|
|
637
|
+
private cullPipeline;
|
|
638
|
+
private cullBindGroupLayout;
|
|
639
|
+
private cullBindGroup;
|
|
640
|
+
/** Every material draw in the scene, flat, in the order the passes walk them.
|
|
641
|
+
* A draw's position here IS its slot in every cull buffer. */
|
|
642
|
+
private cullDraws;
|
|
643
|
+
private cullModels;
|
|
644
|
+
/** Structure changed — model added or removed, draws re-sorted. NOT set by
|
|
645
|
+
* animation, physics or camera movement, which is the whole point. */
|
|
646
|
+
private cullListDirty;
|
|
647
|
+
private cullMetaBuffer;
|
|
648
|
+
private cullModelBuffer;
|
|
649
|
+
private cullHiddenBuffer;
|
|
650
|
+
private cullHidden;
|
|
651
|
+
private cullArgs;
|
|
652
|
+
/** Draws and models the current buffers can hold. A rebuild that fits inside
|
|
653
|
+
* these rewrites contents and allocates nothing. */
|
|
654
|
+
private cullCapacity;
|
|
655
|
+
private cullModelCapacity;
|
|
656
|
+
/** How many times the draw list has been rebuilt — a bundle-era regression
|
|
657
|
+
* guard, reported by getCullDiagnostics. Steady-state it must not climb. */
|
|
658
|
+
private cullRebuilds;
|
|
659
|
+
private opaqueBundle;
|
|
660
|
+
private transparentBundle;
|
|
661
|
+
private shadowBundles;
|
|
662
|
+
/** Set by scene STRUCTURE only. Every frame of animation, every physics step
|
|
663
|
+
* and every camera move must leave this alone — re-recording constantly is
|
|
664
|
+
* worse than having no bundles at all. */
|
|
665
|
+
private bundlesDirty;
|
|
666
|
+
/** Companion to cullRebuilds: how many times the bundles have been recorded.
|
|
667
|
+
* Steady-state it must not climb. */
|
|
668
|
+
private bundleRecords;
|
|
669
|
+
/** Every pass worth watching across a refactor. The query set, the resolve
|
|
670
|
+
* buffer and the readback are all sized from this, so adding one here is the
|
|
671
|
+
* whole change.
|
|
672
|
+
*
|
|
673
|
+
* `field` earns its place now that a scene runs SEVERAL field effects at
|
|
674
|
+
* once: it is one pass with N draws, its resolution is a property of the
|
|
675
|
+
* shared targets rather than of any one effect — so a single `@fullres`
|
|
676
|
+
* effect quadruples the pixel count for all of them — and it is the pass the
|
|
677
|
+
* field restructure moves. Restructuring it while it was the only untimed
|
|
678
|
+
* pass in the frame would have meant reasoning about the cost instead of
|
|
679
|
+
* reading it. */
|
|
680
|
+
private static readonly TIMED_PASSES;
|
|
681
|
+
private timestampQuerySet;
|
|
682
|
+
private timestampResolve;
|
|
683
|
+
private timestampRead;
|
|
684
|
+
/** A map is in flight; the readback buffer cannot be written while it is. */
|
|
685
|
+
private timestampBusy;
|
|
686
|
+
private gpuPassMs;
|
|
687
|
+
private cullCameraArgs;
|
|
688
|
+
private cullShadowArgs;
|
|
689
|
+
private cullMirrorArgs;
|
|
690
|
+
private static readonly REFLECTION_PLANE_Y;
|
|
691
|
+
private mirrorCameraData;
|
|
692
|
+
private mirrorCameraBuffer;
|
|
693
|
+
private mirrorVPData;
|
|
694
|
+
private mirrorVPBuffer;
|
|
695
|
+
private mirrorPerFrameBindGroup;
|
|
696
|
+
private mirrorColorMsTexture;
|
|
697
|
+
private mirrorColorTexture;
|
|
698
|
+
private mirrorColorView;
|
|
699
|
+
private mirrorMipCount;
|
|
700
|
+
private mirrorMipViews;
|
|
701
|
+
private mirrorBlurBindGroups;
|
|
702
|
+
private groundMirrorBlur;
|
|
703
|
+
private mirrorMaskMsTexture;
|
|
704
|
+
private mirrorIdMsTexture;
|
|
705
|
+
private mirrorDepthTexture;
|
|
706
|
+
private mirrorDepthReadView;
|
|
707
|
+
private mirrorPassDescriptor;
|
|
708
|
+
private mirrorOpaqueBundle;
|
|
709
|
+
private mirrorTransparentBundle;
|
|
710
|
+
/** setGroundMirror lands in step 7D; the debug dial is what exercises C. */
|
|
711
|
+
private groundMirror;
|
|
712
|
+
private reflectionDebug;
|
|
713
|
+
private reflectionDebugPipeline;
|
|
714
|
+
private reflectionDebugBindGroupLayout;
|
|
715
|
+
private reflectionDebugBindGroup;
|
|
716
|
+
private get reflectionActive();
|
|
717
|
+
private cullFrustaBuffer;
|
|
718
|
+
private cullFrustaBytes;
|
|
719
|
+
private cullFrustaF32;
|
|
720
|
+
private cullFrustaU32;
|
|
721
|
+
/** CPU-side mirrors of what was uploaded, so the reference test reads exactly
|
|
722
|
+
* the same numbers the compute did. */
|
|
723
|
+
private cullMetaBytes;
|
|
724
|
+
private cullMetaF32;
|
|
725
|
+
private cullMetaU32;
|
|
726
|
+
private cullModelData;
|
|
727
|
+
private cullModelFlags;
|
|
728
|
+
/** Per draw: bit0 = passes the camera frustum, bit1 = passes the light frustum
|
|
729
|
+
* and casts. Filled by the CPU reference; only computed when something asks. */
|
|
730
|
+
private cullReference;
|
|
731
|
+
private cullReferenceFrame;
|
|
732
|
+
private cullEnabled;
|
|
733
|
+
private cullFrame;
|
|
734
|
+
private cullScratchVp;
|
|
735
|
+
private cullReadback;
|
|
736
|
+
private cullReadbackInFlight;
|
|
405
737
|
private compositeBindGroupLayout;
|
|
406
738
|
private compositeBindGroup;
|
|
407
739
|
private depthOfField;
|
|
@@ -416,15 +748,50 @@ export declare class Engine {
|
|
|
416
748
|
private backgroundColor;
|
|
417
749
|
private backdropEquirectTexture;
|
|
418
750
|
private backdropEquirectView;
|
|
751
|
+
private backdropEquirectHDR;
|
|
752
|
+
private backdropStrength;
|
|
753
|
+
/** The installed HDRI's folded irradiance SH (27 floats), or null. */
|
|
754
|
+
private worldSH;
|
|
419
755
|
private fallbackEquirectTexture;
|
|
420
756
|
private fallbackEquirectView;
|
|
421
|
-
|
|
757
|
+
/**
|
|
758
|
+
* The scene's effects, in document order — the order they layer in.
|
|
759
|
+
*
|
|
760
|
+
* An array from here down even while setEffect installs exactly one, because
|
|
761
|
+
* the plural is the whole point of this step and a singleton that has to be
|
|
762
|
+
* "generalised later" is a singleton that shapes every call site against it.
|
|
763
|
+
*/
|
|
764
|
+
private effects;
|
|
765
|
+
/** The first installed effect, for the many places that legitimately want
|
|
766
|
+
* "is anything installed" or the singleton API's one effect. */
|
|
767
|
+
private get effect();
|
|
422
768
|
/** The cast, as the effect API sees it. Written per frame while an effect is
|
|
423
769
|
* installed, and only up to what that effect actually declared. */
|
|
424
770
|
private castBuffer;
|
|
771
|
+
/** The positional lights, as data — see shaders/lights.ts for the layout.
|
|
772
|
+
* Allocated once at full size and zero-filled, so "no lights" is a count of
|
|
773
|
+
* zero rather than an absent binding. */
|
|
774
|
+
private lightsBuffer;
|
|
775
|
+
private lightsData;
|
|
776
|
+
/** Just the header, for rewriting the total without touching a record. */
|
|
777
|
+
private lightHeader;
|
|
778
|
+
/** How many of the slots belong to the DOCUMENT. Effects get what follows. */
|
|
779
|
+
private docLightCount;
|
|
425
780
|
private castData;
|
|
426
781
|
/** Last frame's anchor world positions, for velocity. Keyed model id → slot. */
|
|
427
782
|
private anchorPrev;
|
|
783
|
+
/**
|
|
784
|
+
* The SCENE's bones: which ones are recorded into the cast buffer, and which
|
|
785
|
+
* address each one holds. Built at install from every effect's requests, so
|
|
786
|
+
* the buffer is written once per bone however many effects read it.
|
|
787
|
+
*
|
|
788
|
+
* Everything that touches the cast buffer iterates THIS rather than any one
|
|
789
|
+
* effect's declarations — that is the whole change, and it is what makes N
|
|
790
|
+
* effects a loop instead of a rewrite. With one effect installed the table is
|
|
791
|
+
* exactly that effect's anchors in declaration order, which is why this lands
|
|
792
|
+
* with no visible difference.
|
|
793
|
+
*/
|
|
794
|
+
private anchorTable;
|
|
428
795
|
private castLastMs;
|
|
429
796
|
/** Recent path per trailed anchor, keyed "model\0slot". Newest first, so the
|
|
430
797
|
* shader's index 0 is now — written by unshifting rather than by tracking a
|
|
@@ -446,7 +813,6 @@ export declare class Engine {
|
|
|
446
813
|
/** time=0 origin for the active effect — reset each setEffect. */
|
|
447
814
|
/** Scene-clock reading when the current effect was installed. The effect's
|
|
448
815
|
* `time` is measured from here — see where it is written. */
|
|
449
|
-
private effectEpochScene;
|
|
450
816
|
private compositeBloomView;
|
|
451
817
|
private bloomSampler;
|
|
452
818
|
private bloomBlitUniformBuffer;
|
|
@@ -473,16 +839,16 @@ export declare class Engine {
|
|
|
473
839
|
private groundVertexBuffer?;
|
|
474
840
|
private groundIndexBuffer?;
|
|
475
841
|
private hasGround;
|
|
476
|
-
private
|
|
477
|
-
private
|
|
842
|
+
private shadowMapTextures;
|
|
843
|
+
private shadowMapDepthViews;
|
|
478
844
|
private brdfLutTexture;
|
|
479
845
|
private brdfLutView;
|
|
480
846
|
private filmicLutTexture;
|
|
481
847
|
private filmicLutView;
|
|
482
848
|
private static readonly FILMIC_LUT_WIDTH;
|
|
483
|
-
private static readonly SHADOW_MAP_SIZE;
|
|
484
849
|
private shadowDepthPipeline;
|
|
485
850
|
private shadowLightVPBuffer;
|
|
851
|
+
private shadowCascadeVPBuffers;
|
|
486
852
|
private shadowLightVPMatrix;
|
|
487
853
|
private groundShadowBindGroup?;
|
|
488
854
|
private shadowComparisonSampler;
|
|
@@ -563,7 +929,118 @@ export declare class Engine {
|
|
|
563
929
|
/** Debug/diagnostic: skip every inverted-hull outline draw. */
|
|
564
930
|
private outlineEnabled;
|
|
565
931
|
setOutlineEnabled(on: boolean): void;
|
|
932
|
+
/**
|
|
933
|
+
* Draw the id attachment instead of the scene.
|
|
934
|
+
*
|
|
935
|
+
* The only way to SEE whether ids are right: with no consumer, a correct id
|
|
936
|
+
* buffer and a wrong one render the same frame. See id-debug.ts for what
|
|
937
|
+
* correct looks like and what each failure looks like instead.
|
|
938
|
+
*
|
|
939
|
+
* Returns false when there is nothing to show — ids compiled out, or a device
|
|
940
|
+
* that cannot multisample the format — rather than turning on and drawing
|
|
941
|
+
* black, which would read as "the ids are all zero".
|
|
942
|
+
*/
|
|
943
|
+
setIdDebug(on: boolean): boolean;
|
|
944
|
+
/** True when the id attachment exists on this device. */
|
|
945
|
+
hasObjectIds(): boolean;
|
|
946
|
+
/** The pass that draws it, built lazily so a scene that never asks for the
|
|
947
|
+
* debug view never compiles it. */
|
|
948
|
+
private ensureIdDebugPipeline;
|
|
949
|
+
private renderIdDebugPass;
|
|
950
|
+
/**
|
|
951
|
+
* Show the floor mirror's target instead of the finished frame — the
|
|
952
|
+
* instrument that makes the reflection pass checkable before the ground
|
|
953
|
+
* consumes it. Dev surface, like setIdDebug beside it.
|
|
954
|
+
*/
|
|
955
|
+
setReflectionDebug(on: boolean): void;
|
|
956
|
+
/**
|
|
957
|
+
* What is lighting the world seat right now — the dev-console answer to
|
|
958
|
+
* "did the HDRI actually arrive". Flat mode reports the same colour for
|
|
959
|
+
* every direction, which is what flat means.
|
|
960
|
+
*/
|
|
961
|
+
getWorldLighting(): {
|
|
962
|
+
source: "hdri" | "flat";
|
|
963
|
+
strength: number;
|
|
964
|
+
up: [number, number, number];
|
|
965
|
+
down: [number, number, number];
|
|
966
|
+
};
|
|
967
|
+
/**
|
|
968
|
+
* Switch the floor mirror without rebuilding the ground — the adjust-tier
|
|
969
|
+
* sibling of addGround's own options. False when there is no ground.
|
|
970
|
+
*
|
|
971
|
+
* ON OR OFF, deliberately not a strength: the reflection is an independent
|
|
972
|
+
* LAYER beneath the floor surface, and how much of it shows is the ground's
|
|
973
|
+
* own opacity covering it. Blur 0 is a polished mirror; 1 samples the
|
|
974
|
+
* softest level, scaled by how far the reflected geometry sits behind the
|
|
975
|
+
* surface.
|
|
976
|
+
*/
|
|
977
|
+
setGroundMirror(on: boolean, blur?: number): boolean;
|
|
978
|
+
private ensureReflectionDebugPipeline;
|
|
979
|
+
private renderReflectionDebugPass;
|
|
980
|
+
/** Refold the live camera with the reflection — a copy and a handful of
|
|
981
|
+
* sign flips; cheap enough to run every frame a mirror is on. */
|
|
982
|
+
private updateMirrorCamera;
|
|
983
|
+
/**
|
|
984
|
+
* The scene, mirrored about the floor, into the half-res reflection target.
|
|
985
|
+
*
|
|
986
|
+
* Models only: no ground (the mirror IS the ground), no particles, trails or
|
|
987
|
+
* field effects — the classic MMD stage-floor reflection is the cast, and
|
|
988
|
+
* each of those layers would need its own mirrored variant to join. Runs
|
|
989
|
+
* between emitLights (materials read the lights buffer) and the scene pass
|
|
990
|
+
* (whose ground will sample the resolve).
|
|
991
|
+
*
|
|
992
|
+
* KNOWN LIMIT, deliberate: geometry BELOW the floor plane would reflect up
|
|
993
|
+
* into the target — there is no oblique clip. MMD stages rarely have any;
|
|
994
|
+
* the clip is the follow-up if one shows.
|
|
995
|
+
*/
|
|
996
|
+
private renderMirrorPass;
|
|
997
|
+
/**
|
|
998
|
+
* Fill the mirror's mip levels — the bloom pyramid's own 13-tap downsample,
|
|
999
|
+
* one pass per level. Only when the blur dial is up: at zero the ground
|
|
1000
|
+
* samples level 0 exactly and the chain would be work nobody reads.
|
|
1001
|
+
*/
|
|
1002
|
+
private renderMirrorBlurChain;
|
|
1003
|
+
/**
|
|
1004
|
+
* Can this device multisample the id format at the pass's sample count?
|
|
1005
|
+
*
|
|
1006
|
+
* Asked by creating one and catching the validation error, because there is
|
|
1007
|
+
* no capability flag for it — WebGPU guarantees multisampling for renderable
|
|
1008
|
+
* colour formats but implementations have differed on uint targets, and the
|
|
1009
|
+
* cost of finding out the hard way is a device-lost on someone's machine and
|
|
1010
|
+
* a black canvas.
|
|
1011
|
+
*
|
|
1012
|
+
* The scope is popped in a finally: leaving an error scope pushed swallows
|
|
1013
|
+
* the NEXT error in this device, wherever it happens, and that error would
|
|
1014
|
+
* then be attributed to nothing.
|
|
1015
|
+
*/
|
|
1016
|
+
private probeMultisampledIds;
|
|
566
1017
|
private rebuildCompositeBindGroup;
|
|
1018
|
+
/**
|
|
1019
|
+
* Does any installed effect draw into field pair `layer`?
|
|
1020
|
+
*
|
|
1021
|
+
* Shared with renderFieldPass deliberately. The pass skips a pair nothing
|
|
1022
|
+
* draws into, so the composite must read the 1x1 fallback for that pair
|
|
1023
|
+
* rather than a target no one cleared. Two spellings of "empty" is two
|
|
1024
|
+
* spellings that eventually disagree, and the frame it disagreed on would
|
|
1025
|
+
* show last frame's effect after the effect was removed.
|
|
1026
|
+
*
|
|
1027
|
+
* It is only ever asked at bind-group build time, and setEffects rebuilds the
|
|
1028
|
+
* bind group after assigning this.effects — which is exactly the moment a
|
|
1029
|
+
* pair can change between empty and not.
|
|
1030
|
+
*/
|
|
1031
|
+
private fieldPairUsed;
|
|
1032
|
+
/** One half of one field pair, as the composite should read it. */
|
|
1033
|
+
private fieldLayerView;
|
|
1034
|
+
private createFieldTargets;
|
|
1035
|
+
/**
|
|
1036
|
+
* ONE PER GRID PARITY.
|
|
1037
|
+
*
|
|
1038
|
+
* The grid alternates which texture holds the current grid, so the field pass
|
|
1039
|
+
* needs a bind group for each — built once here rather than rebuilt every
|
|
1040
|
+
* frame, which is what a single group would force and is pure waste for a
|
|
1041
|
+
* change that only ever toggles between two known states.
|
|
1042
|
+
*/
|
|
1043
|
+
private rebuildFieldBindGroup;
|
|
567
1044
|
/**
|
|
568
1045
|
* Set a 360° backdrop from an equirectangular (2:1) image — a PhotoDome-style
|
|
569
1046
|
* skybox at infinity, sampled per-pixel by view direction so it follows the
|
|
@@ -571,7 +1048,10 @@ export declare class Engine {
|
|
|
571
1048
|
* affects lighting, bloom, or tonemapping. Pass null to remove (the background
|
|
572
1049
|
* color, or transparency, takes over again).
|
|
573
1050
|
*/
|
|
574
|
-
setBackdropEquirect(source: ImageBitmap | HTMLImageElement | HTMLCanvasElement | null
|
|
1051
|
+
setBackdropEquirect(source: ImageBitmap | HTMLImageElement | HTMLCanvasElement | HdrImage | null, options?: {
|
|
1052
|
+
/** World strength for an HDR source, Blender's dial. Default 1. */
|
|
1053
|
+
strength?: number;
|
|
1054
|
+
}): void;
|
|
575
1055
|
private makeCompositePipeline;
|
|
576
1056
|
/**
|
|
577
1057
|
* Install the scene's WGSL effect (shadertoy-style), rendered per-pixel in the
|
|
@@ -608,7 +1088,118 @@ export declare class Engine {
|
|
|
608
1088
|
* is KEPT and diagnostics are returned with line numbers relative to the
|
|
609
1089
|
* user's WGSL. Pass null to remove the effect.
|
|
610
1090
|
*/
|
|
1091
|
+
private compileEffect;
|
|
1092
|
+
/**
|
|
1093
|
+
* Compile an effect's particle stages and allocate its pool.
|
|
1094
|
+
*
|
|
1095
|
+
* Two modules, not one: the compute and render stages bind the same buffer
|
|
1096
|
+
* with different access (read_write vs read), and a single module would have
|
|
1097
|
+
* to pick one. Compiling them separately also means an author's helper names
|
|
1098
|
+
* live in their own compilation unit, which is what lets two effects both
|
|
1099
|
+
* define `hash21` without meeting.
|
|
1100
|
+
*/
|
|
1101
|
+
/**
|
|
1102
|
+
* Install a LIST of effects, in document order — the order they layer in.
|
|
1103
|
+
*
|
|
1104
|
+
* Each is compiled independently and a failure is contained: it is reported in
|
|
1105
|
+
* its own slot of the returned array and left out of the scene, while the rest
|
|
1106
|
+
* install. That is the style-group discipline, and it matters more here — with
|
|
1107
|
+
* four effects on screen, "one bad shader blanks the scene" is the first bug
|
|
1108
|
+
* report anyone would file.
|
|
1109
|
+
*
|
|
1110
|
+
* The bones are allocated ONCE for the whole list: two effects naming the same
|
|
1111
|
+
* wrist share one address and one recorded path, and the cap is eight distinct
|
|
1112
|
+
* bones across the scene rather than per file.
|
|
1113
|
+
*
|
|
1114
|
+
* Null or empty clears everything.
|
|
1115
|
+
*/
|
|
1116
|
+
setEffects(list: {
|
|
1117
|
+
wgsl: string;
|
|
1118
|
+
params?: Record<string, EffectParamValue>;
|
|
1119
|
+
}[] | null): Promise<EffectResult[]>;
|
|
1120
|
+
/**
|
|
1121
|
+
* Install ONE effect — the singleton API, kept because most scenes are one
|
|
1122
|
+
* effect and every existing caller uses it. A one-element setEffects.
|
|
1123
|
+
*/
|
|
611
1124
|
setEffect(wgsl: string | null, params?: Record<string, EffectParamValue>): Promise<EffectResult>;
|
|
1125
|
+
private buildParticles;
|
|
1126
|
+
/**
|
|
1127
|
+
* Step the pool, before the scene pass.
|
|
1128
|
+
*
|
|
1129
|
+
* Outside the render pass because a compute dispatch cannot be encoded inside
|
|
1130
|
+
* one — and it has to precede the draw that reads the same buffer, or the
|
|
1131
|
+
* quads render last frame's positions.
|
|
1132
|
+
*/
|
|
1133
|
+
/**
|
|
1134
|
+
* Compile an effect's lightEmit stage and give it a bind group.
|
|
1135
|
+
*
|
|
1136
|
+
* Its own layout rather than a shared one: this is the only place the lights
|
|
1137
|
+
* buffer is WRITABLE, and every other binding of it is read-only. Keeping the
|
|
1138
|
+
* writable view here means a material pipeline cannot accidentally acquire
|
|
1139
|
+
* one.
|
|
1140
|
+
*/
|
|
1141
|
+
private buildLightEmit;
|
|
1142
|
+
/**
|
|
1143
|
+
* Hand every emitting effect its slot range, and write the total.
|
|
1144
|
+
*
|
|
1145
|
+
* Document lights sit FIRST, at slot 0, and effects follow in document order.
|
|
1146
|
+
* That ordering is the stable one: a scene's own lamps are the thing a person
|
|
1147
|
+
* placed and can see in a list, so they should not move because an effect was
|
|
1148
|
+
* installed ahead of them.
|
|
1149
|
+
*
|
|
1150
|
+
* Called whenever either producer changes. The base lands in each effect's
|
|
1151
|
+
* uniform, so nothing recompiles.
|
|
1152
|
+
*/
|
|
1153
|
+
private allocateLightSlots;
|
|
1154
|
+
/** Run every effect's lightEmit, before the pass that reads the result. */
|
|
1155
|
+
private emitLights;
|
|
1156
|
+
private stepParticles;
|
|
1157
|
+
/** Draw the pool. Inside the scene pass, so it is depth-tested and pre-bloom. */
|
|
1158
|
+
private renderParticles;
|
|
1159
|
+
/**
|
|
1160
|
+
* Compile an effect's ribbon stage.
|
|
1161
|
+
*
|
|
1162
|
+
* One instance per (slot, subject, segment), so a scene with several dancers
|
|
1163
|
+
* and several declared bones is still one draw and nothing is computed per
|
|
1164
|
+
* frame on the CPU.
|
|
1165
|
+
*/
|
|
1166
|
+
private buildTrails;
|
|
1167
|
+
private releaseTrails;
|
|
1168
|
+
/**
|
|
1169
|
+
* Ribbons, drawn INSIDE the scene pass — as geometry, in HDR, before bloom.
|
|
1170
|
+
*
|
|
1171
|
+
* They used to own a colour target and be pasted over the finished frame
|
|
1172
|
+
* after tone mapping, which is exactly why they could not bloom: nothing
|
|
1173
|
+
* composited post-tonemap can. Here they are lit like anything else in the
|
|
1174
|
+
* scene, depth-tested against the body they circle, and their emission
|
|
1175
|
+
* reaches the bloom prefilter through the aux mask they now write.
|
|
1176
|
+
*
|
|
1177
|
+
* Takes the pass rather than opening one: that IS the change.
|
|
1178
|
+
*/
|
|
1179
|
+
private drawTrails;
|
|
1180
|
+
/** The user's field mounts, drawn at half resolution for the composite to
|
|
1181
|
+
* upsample. Runs the whole quad — uniform control flow, so effects may use
|
|
1182
|
+
* derivatives freely, which the old inline path had to forbid. */
|
|
1183
|
+
private renderFieldPass;
|
|
1184
|
+
/** The trail bind group holds the depth view, which a resize recreates. */
|
|
1185
|
+
private rebindTrails;
|
|
1186
|
+
private releaseParticles;
|
|
1187
|
+
/**
|
|
1188
|
+
* Compile and allocate the effect's persistent grid.
|
|
1189
|
+
*
|
|
1190
|
+
* The textures are created ZEROED, which is the contract a kernel is written
|
|
1191
|
+
* against: rzGridFrame() is 0 on the first step and every value it reads is
|
|
1192
|
+
* zero, so seeding is just "if frame is 0, return the initial state".
|
|
1193
|
+
*/
|
|
1194
|
+
private buildSim;
|
|
1195
|
+
private releaseGrid;
|
|
1196
|
+
/**
|
|
1197
|
+
* Step the grid, before anything reads it.
|
|
1198
|
+
*
|
|
1199
|
+
* Outside the render pass, like the particle step and for the same reason —
|
|
1200
|
+
* and before the field pass, or an effect samples a grid one frame stale.
|
|
1201
|
+
*/
|
|
1202
|
+
private stepSim;
|
|
612
1203
|
/** Which mounts the installed effect declared. Both false when none is set. */
|
|
613
1204
|
getEffectMounts(): {
|
|
614
1205
|
background: boolean;
|
|
@@ -684,6 +1275,67 @@ export declare class Engine {
|
|
|
684
1275
|
* loaded. A timeline cannot draw a lane to scale without it, and the camera's
|
|
685
1276
|
* length is its own: it does not have to match any model's clip. */
|
|
686
1277
|
getCameraVmdDuration(): number;
|
|
1278
|
+
/**
|
|
1279
|
+
* Install a track's precomputed analysis for the rzAudio* effect functions:
|
|
1280
|
+
* `data` is frames × (2 + bands) floats — loudness, bass onset, then the band
|
|
1281
|
+
* magnitudes, all 0..1 — sampled by the clock given to setAudioTime. Null
|
|
1282
|
+
* clears back to silence.
|
|
1283
|
+
*
|
|
1284
|
+
* Precomputed for the WHOLE track, never fed live from an analyser: an export
|
|
1285
|
+
* steps the engine frame by frame rather than playing in real time, so live
|
|
1286
|
+
* analysis would render silence into the exported video.
|
|
1287
|
+
*/
|
|
1288
|
+
setAudioData(data: Float32Array | null, bandsPerFrame: number, secondsPerFrame: number): void;
|
|
1289
|
+
/**
|
|
1290
|
+
* Install a score — note events — for the rzNote* and rzKey* effect functions.
|
|
1291
|
+
* Null clears it.
|
|
1292
|
+
*
|
|
1293
|
+
* The sibling of setAudioData, and deliberately a SEPARATE interface rather
|
|
1294
|
+
* than something derived from it: a spectrum cannot give back a discrete pitch
|
|
1295
|
+
* and onset, and that discreteness is the whole substance of a falling note.
|
|
1296
|
+
* A scene can hold both and read them together.
|
|
1297
|
+
*
|
|
1298
|
+
* `release` is how long a key keeps glowing after its note ends, in seconds.
|
|
1299
|
+
* It belongs here rather than in the effect because the key map it feeds is
|
|
1300
|
+
* computed on the CPU — see writeMidiClock.
|
|
1301
|
+
*/
|
|
1302
|
+
/**
|
|
1303
|
+
* Install the track's lyric lines for the rzLyric* effect functions — the
|
|
1304
|
+
* timing of the words on the scene clock, and optionally the words
|
|
1305
|
+
* themselves: `atlas.source` is a canvas/bitmap of rasterised lines (the
|
|
1306
|
+
* host draws them — Canvas2D handles every script the platform does) with
|
|
1307
|
+
* `atlas.rects` saying where each line sits, in 0..1 [u0, vTop, u1, vBottom].
|
|
1308
|
+
* Null clears. A plain buffer write plus at most a texture copy: buffer and
|
|
1309
|
+
* atlas are both fixed-size, so nothing re-binds whenever lyrics arrive.
|
|
1310
|
+
*/
|
|
1311
|
+
setLyrics(lines: LyricLine[] | null, atlas?: {
|
|
1312
|
+
source: GPUCopyExternalImageSource;
|
|
1313
|
+
width: number;
|
|
1314
|
+
height: number;
|
|
1315
|
+
rects: LyricRect[];
|
|
1316
|
+
}): void;
|
|
1317
|
+
setMidiNotes(notes: MidiNote[] | null, release?: number): void;
|
|
1318
|
+
/**
|
|
1319
|
+
* Move the score's clock, and rebuild the per-pitch key map from it.
|
|
1320
|
+
*
|
|
1321
|
+
* The map is why this is more than a header write. Falling notes index the
|
|
1322
|
+
* note list directly, but a keyboard glow asks the opposite question per
|
|
1323
|
+
* pixel — is anything sounding at THIS pitch — and answering that in the
|
|
1324
|
+
* shader would be a scan of the whole score per fragment. One O(notes) pass
|
|
1325
|
+
* here, once a frame, turns it into a single lookup.
|
|
1326
|
+
*
|
|
1327
|
+
* The pass is a full scan rather than a cursor because scrubbing exists: a
|
|
1328
|
+
* cursor is only correct while time moves forward, and a timeline drag is
|
|
1329
|
+
* exactly when a wrong answer is most visible. Ten thousand notes is ten
|
|
1330
|
+
* thousand comparisons, which is nothing beside the pose pass.
|
|
1331
|
+
*/
|
|
1332
|
+
setMidiTime(seconds: number, playing?: boolean): void;
|
|
1333
|
+
/**
|
|
1334
|
+
* Where the track is NOW, in seconds — written by whoever owns playback: the
|
|
1335
|
+
* editor's audio clock, the viewer's, or the export loop with its exact
|
|
1336
|
+
* per-frame time. A 4-byte header write, cheap enough for every frame.
|
|
1337
|
+
*/
|
|
1338
|
+
setAudioTime(seconds: number, playing?: boolean): void;
|
|
687
1339
|
/** Every camera keyframe's frame index — what a timeline draws as its cuts.
|
|
688
1340
|
* Empty when no camera VMD is loaded. */
|
|
689
1341
|
getCameraVmdKeyframes(): number[];
|
|
@@ -739,7 +1391,39 @@ export declare class Engine {
|
|
|
739
1391
|
noiseStrength?: number;
|
|
740
1392
|
/** Whole-ground opacity, 0–1 (multiplies the radial edge fade). Default 1. */
|
|
741
1393
|
opacity?: number;
|
|
1394
|
+
/** Floor mirror, on or off — NOT a strength: the reflection is its own
|
|
1395
|
+
* layer, and how much of it shows is the surface's own `opacity`
|
|
1396
|
+
* covering it. Off (default) never renders the reflection pass at all. */
|
|
1397
|
+
mirror?: boolean;
|
|
1398
|
+
/** Mirror softness, 0–1: 0 a polished mirror, 1 the softest blur level,
|
|
1399
|
+
* scaled by how far the reflected geometry sits behind the surface. */
|
|
1400
|
+
mirrorBlur?: number;
|
|
742
1401
|
}): void;
|
|
1402
|
+
/**
|
|
1403
|
+
* The scene's positional lights — an ADDITIVE layer over the sun, which stays
|
|
1404
|
+
* the key light and keeps the toon ramp to itself.
|
|
1405
|
+
*
|
|
1406
|
+
* Colour and intensity are multiplied here rather than stored apart: every
|
|
1407
|
+
* read is the product, and two numbers that are only ever multiplied are two
|
|
1408
|
+
* numbers that can disagree.
|
|
1409
|
+
*
|
|
1410
|
+
* Past MAX_LIGHTS the extras are DROPPED, not wrapped: the lights that fit
|
|
1411
|
+
* keep the meaning the caller gave them, which is the same rule the anchor
|
|
1412
|
+
* table follows. Passing none (or an empty list) turns the layer off and the
|
|
1413
|
+
* scene renders exactly as it did before lights existed.
|
|
1414
|
+
*/
|
|
1415
|
+
setLights(
|
|
1416
|
+
/** Structural {x,y,z} rather than the Vec3 class, the same choice effect
|
|
1417
|
+
* params make: a scene document's JSON passes straight in, and so does a
|
|
1418
|
+
* literal typed into a console. Vec3 satisfies it either way. */
|
|
1419
|
+
lights: {
|
|
1420
|
+
position: XYZ;
|
|
1421
|
+
color: XYZ;
|
|
1422
|
+
intensity?: number;
|
|
1423
|
+
radius?: number;
|
|
1424
|
+
}[] | null): void;
|
|
1425
|
+
/** How many positional lights the scene is carrying. */
|
|
1426
|
+
getLightCount(): number;
|
|
743
1427
|
private updateLightBuffer;
|
|
744
1428
|
getStats(): EngineStats;
|
|
745
1429
|
runRenderLoop(callback?: () => void): void;
|
|
@@ -852,10 +1536,170 @@ export declare class Engine {
|
|
|
852
1536
|
private updateInstances;
|
|
853
1537
|
private updateVertexBuffer;
|
|
854
1538
|
private dispatchMorphCompute;
|
|
1539
|
+
private static readonly CULL_META_BYTES;
|
|
1540
|
+
private static readonly CULL_MODEL_FLOATS;
|
|
1541
|
+
private static readonly CULL_ARG_WORDS;
|
|
1542
|
+
private static readonly CULL_DRAW_CASTS_SHADOW;
|
|
1543
|
+
private static readonly CULL_MODEL_VISIBLE;
|
|
1544
|
+
private static readonly CULL_MODEL_RIGID;
|
|
1545
|
+
/**
|
|
1546
|
+
* Flatten the scene's material draws into the order every cull buffer is
|
|
1547
|
+
* indexed by, and upload everything that only changes with STRUCTURE: the
|
|
1548
|
+
* per-draw metadata and the constant words of the indirect arguments.
|
|
1549
|
+
*
|
|
1550
|
+
* Runs on model add/remove and on any re-sort of a model's draws (a style
|
|
1551
|
+
* group assignment re-ranks them). Animation, physics and camera movement all
|
|
1552
|
+
* leave it alone — that is the same invalidation set the render bundles will
|
|
1553
|
+
* want, tested here first where being wrong is cheap.
|
|
1554
|
+
*/
|
|
1555
|
+
private rebuildCullList;
|
|
1556
|
+
/**
|
|
1557
|
+
* Per-draw hidden state, uploaded only when it actually changes.
|
|
1558
|
+
*
|
|
1559
|
+
* The two sets behind it are cheap to read but the buffer is not worth
|
|
1560
|
+
* rewriting every frame: applyMaterialMorphs rebuilds morphHiddenMaterials on
|
|
1561
|
+
* every frame of any character carrying a face VMD, and almost every one of
|
|
1562
|
+
* those rebuilds produces the same answer. So compare, then upload.
|
|
1563
|
+
*/
|
|
1564
|
+
private writeCullHidden;
|
|
1565
|
+
private releaseCullBuffers;
|
|
1566
|
+
/**
|
|
1567
|
+
* One record per model: the rigid transform its per-material boxes live under,
|
|
1568
|
+
* or the world sphere that bounds it in any pose. Written every frame, because
|
|
1569
|
+
* this is the part animation moves — a few hundred bytes per model.
|
|
1570
|
+
*/
|
|
1571
|
+
private writeCullModels;
|
|
1572
|
+
/**
|
|
1573
|
+
* The world sphere for a skinned model: an AABB over its POSED bone positions,
|
|
1574
|
+
* grown by the model's skin margin, then carried through the scene placement.
|
|
1575
|
+
*
|
|
1576
|
+
* Bone positions come from the pose that already ran this frame, so a jump, a
|
|
1577
|
+
* run across the stage or a physics-driven skirt are all inside it by
|
|
1578
|
+
* construction — none of which a bind-pose box would have contained. See
|
|
1579
|
+
* ModelInstance.skinMargin for why growing by that one number is a bound and
|
|
1580
|
+
* not an estimate.
|
|
1581
|
+
*/
|
|
1582
|
+
private writeCullSphere;
|
|
1583
|
+
/**
|
|
1584
|
+
* The camera's six frustum planes then the sun's, normalized, as inward
|
|
1585
|
+
* half-spaces (`dot(n, p) + d >= 0` is inside).
|
|
1586
|
+
*
|
|
1587
|
+
* Extracted from the combined view-projection rather than rebuilt from the
|
|
1588
|
+
* camera's own parameters, so a VMD-driven shot, an orbit and the shadow
|
|
1589
|
+
* volume's ortho box all go through one code path and none of them can
|
|
1590
|
+
* disagree with what the vertex shader actually projects.
|
|
1591
|
+
*
|
|
1592
|
+
* Culling shadow casters to the light's frustum looks like it should lose
|
|
1593
|
+
* casters that stand outside the volume and throw shade into it. It cannot:
|
|
1594
|
+
* the cull tests the OUTERMOST cascade's box, each cascade's rasterizer clips
|
|
1595
|
+
* to its own box, and every inner box lies inside the outer one (the
|
|
1596
|
+
* containment invariant in shadow-cascades.ts) — so anything rejected here
|
|
1597
|
+
* was contributing to no cascade at all.
|
|
1598
|
+
*/
|
|
1599
|
+
private writeCullFrusta;
|
|
1600
|
+
/**
|
|
1601
|
+
* Cull every material draw against the camera and the sun, writing
|
|
1602
|
+
* `instanceCount` into two indirect-argument buffers.
|
|
1603
|
+
*
|
|
1604
|
+
* Nothing draws from those buffers yet. This increment stands the pass up and
|
|
1605
|
+
* leaves the draw path issuing direct draws, so the bounds and the frusta can
|
|
1606
|
+
* be checked against a scene that is definitely rendering correctly — see
|
|
1607
|
+
* getCullDiagnostics and setCullApply.
|
|
1608
|
+
*/
|
|
1609
|
+
/**
|
|
1610
|
+
* Record the three bundles: the shadow pass, the opaque phase and the
|
|
1611
|
+
* transparent phase.
|
|
1612
|
+
*
|
|
1613
|
+
* The insight this rests on is that a character's DRAW COMMANDS are stable
|
|
1614
|
+
* frame to frame — same pipeline, same bind groups, same index ranges, with
|
|
1615
|
+
* only the contents of the skin-matrix buffer changing. So bundles apply to
|
|
1616
|
+
* the cast, not merely to scenery, and what invalidates one is scene
|
|
1617
|
+
* STRUCTURE. Animation, physics, camera movement, material morphs, a hidden
|
|
1618
|
+
* material and a hidden model all leave a bundle valid — the first three
|
|
1619
|
+
* because they touch buffers and not commands, the last two because their
|
|
1620
|
+
* switches live in the cull compute rather than in this encode loop.
|
|
1621
|
+
*
|
|
1622
|
+
* The ground and the particles are deliberately not in a bundle: the ground is
|
|
1623
|
+
* one draw that sits BETWEEN the two phases, and the particle count comes from
|
|
1624
|
+
* the CPU. Both are cheaper to leave direct than to invalidate around.
|
|
1625
|
+
*/
|
|
1626
|
+
private recordBundles;
|
|
1627
|
+
/** The two query slots for a pass, or undefined where the device has no
|
|
1628
|
+
* timestamps — which every pass descriptor accepts as "do not measure". */
|
|
1629
|
+
private stamps;
|
|
1630
|
+
/**
|
|
1631
|
+
* Resolve this frame's timings and start a readback, at most one in flight.
|
|
1632
|
+
*
|
|
1633
|
+
* Deliberately not awaited anywhere in the frame: a timing that costs a stall
|
|
1634
|
+
* to collect would change the thing it is measuring. The numbers are therefore
|
|
1635
|
+
* a frame or two old, which is exactly right for what they are for — watching
|
|
1636
|
+
* a pass get more expensive across a refactor, not attributing one frame.
|
|
1637
|
+
*/
|
|
1638
|
+
private resolveTimestamps;
|
|
1639
|
+
/**
|
|
1640
|
+
* Milliseconds on the GPU per pass, or null where the device cannot measure.
|
|
1641
|
+
*
|
|
1642
|
+
* The regression guard for the draw-path work: these are the numbers that say
|
|
1643
|
+
* whether restructuring cost anything, which is the claim being made — not
|
|
1644
|
+
* whether it made the scene faster, which was never the goal.
|
|
1645
|
+
*/
|
|
1646
|
+
getGpuTimings(): Record<string, number> | null;
|
|
1647
|
+
private dispatchCull;
|
|
1648
|
+
/**
|
|
1649
|
+
* The same test the compute runs, on the CPU, from the same uploaded numbers.
|
|
1650
|
+
*
|
|
1651
|
+
* Deliberately a second implementation rather than a shared one: two
|
|
1652
|
+
* independent readings of the same data is what makes agreement evidence. It
|
|
1653
|
+
* reads the mirrors written by writeCullModels/writeCullFrusta, so it answers
|
|
1654
|
+
* for the frame that was last dispatched, and caches per frame because both
|
|
1655
|
+
* the debug draw gate and the diagnostics want it.
|
|
1656
|
+
*/
|
|
1657
|
+
private cullReferencePass;
|
|
1658
|
+
/**
|
|
1659
|
+
* Issue one material draw, indirect when the cull owns it.
|
|
1660
|
+
*
|
|
1661
|
+
* The outline and the over-eyes pass share their material's slot rather than
|
|
1662
|
+
* getting their own: same index range, same bounds, so the same decision. That
|
|
1663
|
+
* also means an outline can never survive a material that was culled, which is
|
|
1664
|
+
* the only relationship between them that is ever correct.
|
|
1665
|
+
*
|
|
1666
|
+
* Falls back to a direct draw for anything outside the cull list — the ground,
|
|
1667
|
+
* and any draw whose slot has not been assigned yet.
|
|
1668
|
+
*/
|
|
1669
|
+
private issueDraw;
|
|
1670
|
+
/**
|
|
1671
|
+
* Turn frustum culling off without turning the pass off.
|
|
1672
|
+
*
|
|
1673
|
+
* The compute keeps running and keeps reporting; it just writes "visible" for
|
|
1674
|
+
* every draw. That is the A/B a missing-geometry report needs — if it is still
|
|
1675
|
+
* missing with culling off, the cull was not what removed it — and it costs
|
|
1676
|
+
* one uniform word rather than a rebuild.
|
|
1677
|
+
*/
|
|
1678
|
+
setCullEnabled(on: boolean): void;
|
|
1679
|
+
/**
|
|
1680
|
+
* Read the GPU's culling decisions back and diff them against the CPU
|
|
1681
|
+
* reference over the same frame's uploaded data.
|
|
1682
|
+
*
|
|
1683
|
+
* A clean report means the compute, its buffer layouts and the plane
|
|
1684
|
+
* extraction all agree with a second implementation. It does NOT prove the
|
|
1685
|
+
* bounds contain the geometry — that is what setCullApply(true) and looking at
|
|
1686
|
+
* the scene is for.
|
|
1687
|
+
*/
|
|
1688
|
+
getCullDiagnostics(): Promise<CullDiagnostics>;
|
|
1689
|
+
/** The GPU half of getCullDiagnostics: copy both argument buffers back and
|
|
1690
|
+
* diff them against the reference. */
|
|
1691
|
+
private readCullArgs;
|
|
855
1692
|
private setupModelInstance;
|
|
856
1693
|
private createGpuMorph;
|
|
857
1694
|
private createGroundGeometry;
|
|
858
1695
|
private createShadowGroundResources;
|
|
1696
|
+
/**
|
|
1697
|
+
* (Re)build the ground's bind group. Its own method because the RESIZE path
|
|
1698
|
+
* needs it too: the reflection resolve is recreated at every canvas size,
|
|
1699
|
+
* and a bind group holding the old view would sample a destroyed texture on
|
|
1700
|
+
* the first resized frame with a mirror on.
|
|
1701
|
+
*/
|
|
1702
|
+
private buildGroundBindGroup;
|
|
859
1703
|
/** Whether the shadow map needs clearing — see the shadow pass in `render`.
|
|
860
1704
|
*
|
|
861
1705
|
* Starts true so the very first frame runs the pass even with an empty scene. The
|
|
@@ -889,6 +1733,14 @@ export declare class Engine {
|
|
|
889
1733
|
*/
|
|
890
1734
|
private applyMaterialMorphs;
|
|
891
1735
|
private createUniformBuffer;
|
|
1736
|
+
/** Whether a material is switched on — the user's own toggle, or a material
|
|
1737
|
+
* morph having driven its alpha to zero.
|
|
1738
|
+
*
|
|
1739
|
+
* The material passes no longer consult this: their draws are indirect, and
|
|
1740
|
+
* the cull compute zeroes the instance count of anything hidden, which is
|
|
1741
|
+
* what lets a render bundle survive a face VMD rewriting the morph-hidden set
|
|
1742
|
+
* sixty times a second. It remains the answer for the passes that draw
|
|
1743
|
+
* directly, where there is no argument buffer to zero. */
|
|
892
1744
|
private shouldRenderDrawCall;
|
|
893
1745
|
private createTextureFromLogicalPath;
|
|
894
1746
|
private generateMipmaps;
|
|
@@ -950,6 +1802,31 @@ export declare class Engine {
|
|
|
950
1802
|
/** Clear all style groups on a model — every material returns to the hand-shader path. */
|
|
951
1803
|
resetStyleGroups(modelName: string): void;
|
|
952
1804
|
/** Instant adjust-tier write: set one exposed slider on a group's applied graph. */
|
|
1805
|
+
/**
|
|
1806
|
+
* Drive a material parameter from the SCENE CLOCK.
|
|
1807
|
+
*
|
|
1808
|
+
* The channel this writes into already existed — setStyleParam below puts a
|
|
1809
|
+
* value straight into the style uniform. What a track adds is WHEN: the value
|
|
1810
|
+
* is a pure function of scene time, so a scene describes a dissolve once and
|
|
1811
|
+
* playback, a re-open and an offline export stepped at another rate all
|
|
1812
|
+
* produce the same frames.
|
|
1813
|
+
*
|
|
1814
|
+
* Addressed BY NAME (model, group, param), not by id. Worth stating because
|
|
1815
|
+
* the id attachment landed alongside this and the two look related: ids
|
|
1816
|
+
* answer "which object is this PIXEL", which is a screen-space question, and
|
|
1817
|
+
* a track answers "what is this parameter NOW". Nothing here needs MRT.
|
|
1818
|
+
*
|
|
1819
|
+
* Null or empty clears the track and leaves the parameter wherever it was —
|
|
1820
|
+
* removing an animation is not the same as resetting a value, and guessing
|
|
1821
|
+
* which the caller meant would be worse than either.
|
|
1822
|
+
*/
|
|
1823
|
+
setStyleParamTrack(modelName: string, groupId: string, paramId: string, keys: ParamKey[] | null): boolean;
|
|
1824
|
+
/** Forget what a group's tracks last wrote, so the next frame writes it again.
|
|
1825
|
+
* Called whenever something else has written that uniform underneath them. */
|
|
1826
|
+
private invalidateParamTracks;
|
|
1827
|
+
/** Every track, at the current scene clock. Called once per frame, before the
|
|
1828
|
+
* pass that reads the uniforms it writes. */
|
|
1829
|
+
private evaluateParamTracks;
|
|
953
1830
|
setStyleParam(modelName: string, groupId: string, paramId: string, value: number | [number, number, number]): boolean;
|
|
954
1831
|
private currentClaims;
|
|
955
1832
|
private compileAndInstallGroup;
|
|
@@ -980,6 +1857,14 @@ export declare class Engine {
|
|
|
980
1857
|
* right after the material itself, like MMD's per-mesh outline stage.
|
|
981
1858
|
*/
|
|
982
1859
|
private setModelDrawState;
|
|
1860
|
+
/**
|
|
1861
|
+
* Which eye the scene is being drawn FOR — the main camera or the floor
|
|
1862
|
+
* mirror. Threaded explicitly through the phase draws rather than read off
|
|
1863
|
+
* the engine, because both sets of bundles are recorded in one call and
|
|
1864
|
+
* ambient state at record time is how a mirror bundle ends up baked with the
|
|
1865
|
+
* main camera's bind group.
|
|
1866
|
+
*/
|
|
1867
|
+
private sceneView;
|
|
983
1868
|
private renderModelOpaquePhase;
|
|
984
1869
|
private renderModelTransparentPhase;
|
|
985
1870
|
/** Depth-only re-draw of transparent-bucket materials (see depth-prepass.ts).
|
|
@@ -1019,6 +1904,16 @@ export declare class Engine {
|
|
|
1019
1904
|
* export record the same path at the same spacing. A frame that covers several
|
|
1020
1905
|
* intervals emits several samples rather than one, or a fast hand would tear.
|
|
1021
1906
|
*/
|
|
1907
|
+
/**
|
|
1908
|
+
* Forget every recorded path, because the slots have been re-dealt.
|
|
1909
|
+
*
|
|
1910
|
+
* Called on every effect swap. The rings are keyed by (model, slot) and a slot
|
|
1911
|
+
* is an ADDRESS, so re-allocating the table can leave a left wrist's recorded
|
|
1912
|
+
* history sitting at the address a right wrist now occupies — a ribbon drawn
|
|
1913
|
+
* confidently along a path that belongs to another bone. Refilling from live
|
|
1914
|
+
* samples costs about two seconds of trail and cannot be wrong.
|
|
1915
|
+
*/
|
|
1916
|
+
private clearTrailHistory;
|
|
1022
1917
|
private writeTrail;
|
|
1023
1918
|
private updateSkinMatrices;
|
|
1024
1919
|
private updateStats;
|