insomni 0.2.0-alpha.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/LICENSE.md +674 -0
- package/README.md +234 -0
- package/dist/advanced.d.mts +76 -0
- package/dist/advanced.mjs +81 -0
- package/dist/assemble-BT3CXbSx.mjs +1574 -0
- package/dist/camera-view-DHmMiKvP.d.mts +326 -0
- package/dist/frame-mHNdKRpF.mjs +135 -0
- package/dist/index-CmMZCMJT.d.mts +39 -0
- package/dist/index-DkJfpntS.d.mts +2417 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +6612 -0
- package/dist/internal.d.mts +892 -0
- package/dist/internal.mjs +566 -0
- package/dist/logger-DSyBF3Y_.mjs +15 -0
- package/dist/particles.d.mts +816 -0
- package/dist/particles.mjs +4804 -0
- package/dist/pipeline-BWCAZTKx.mjs +470 -0
- package/dist/pipeline-DE3a1Pnk.d.mts +115 -0
- package/dist/reactivity-B7I0pvzm.mjs +191 -0
- package/dist/reactivity.d.mts +2 -0
- package/dist/reactivity.mjs +2 -0
- package/dist/renderer-DzZqd1bY.d.mts +4566 -0
- package/dist/root-CHradZKM.mjs +30 -0
- package/dist/shape-DfZP9Jdk.mjs +349 -0
- package/dist/space-CeDnj6eu.mjs +11240 -0
- package/dist/spatial-Bd3Ay8I2.d.mts +85 -0
- package/dist/spatial-hash-C1crBjTo.mjs +77 -0
- package/dist/spatial.d.mts +2 -0
- package/dist/spatial.mjs +121 -0
- package/dist/text-font-D7GGDtTK.d.mts +185 -0
- package/dist/text-ttf.d.mts +91 -0
- package/dist/text-ttf.mjs +298 -0
- package/dist/texture-dABoqFoP.mjs +131 -0
- package/dist/viewport.d.mts +2 -0
- package/dist/viewport.mjs +274 -0
- package/package.json +69 -0
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
import { d as CustomDrawableContext, ln as LayerSpace, r as Renderer2D, u as CustomDrawable } from "./renderer-DzZqd1bY.mjs";
|
|
2
|
+
import { g as GPUOwner } from "./text-font-D7GGDtTK.mjs";
|
|
3
|
+
import { TgpuRoot } from "typegpu";
|
|
4
|
+
|
|
5
|
+
//#region src/particles/codegen/pipeline-cache.d.ts
|
|
6
|
+
type PipelineFactory = () => GPUComputePipeline;
|
|
7
|
+
declare class PipelineCache {
|
|
8
|
+
private readonly cache;
|
|
9
|
+
/**
|
|
10
|
+
* Return the pipeline for `key`, calling `factory()` to build it on miss.
|
|
11
|
+
* Factories are only invoked on cold lookups.
|
|
12
|
+
*/
|
|
13
|
+
getOrCreate(key: string, factory: PipelineFactory): GPUComputePipeline;
|
|
14
|
+
/** Current cache size — useful for tests and introspection. */
|
|
15
|
+
get size(): number;
|
|
16
|
+
/**
|
|
17
|
+
* Drop every cached pipeline. WebGPU pipelines are reference-counted and
|
|
18
|
+
* released when the JS handle becomes unreferenced; nothing extra needed.
|
|
19
|
+
*/
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/particles/types.d.ts
|
|
24
|
+
/** WGSL-mappable scalar / vector types a user attribute may take. */
|
|
25
|
+
type AttributeType = "f32" | "u32" | "i32" | "vec2f" | "vec3f" | "vec4f" | "vec2u" | "vec4u";
|
|
26
|
+
/** Declared at system construction — name → WGSL type. */
|
|
27
|
+
type AttributeCatalog = Record<string, AttributeType>;
|
|
28
|
+
type BoundsMode = {
|
|
29
|
+
kind: "none";
|
|
30
|
+
} | {
|
|
31
|
+
kind: "clamp";
|
|
32
|
+
rect: [minX: number, minY: number, maxX: number, maxY: number];
|
|
33
|
+
} | {
|
|
34
|
+
kind: "wrap";
|
|
35
|
+
rect: [minX: number, minY: number, maxX: number, maxY: number];
|
|
36
|
+
} | {
|
|
37
|
+
kind: "bounce";
|
|
38
|
+
rect: [minX: number, minY: number, maxX: number, maxY: number];
|
|
39
|
+
restitution?: number;
|
|
40
|
+
};
|
|
41
|
+
interface ParticleSystemOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Hard cap on simultaneously live particles. Bounded above by the
|
|
44
|
+
* compaction module's single-pass-B scan limit (262144 in v1). Buffers
|
|
45
|
+
* are sized to this; there is no auto-grow.
|
|
46
|
+
*/
|
|
47
|
+
capacity: number;
|
|
48
|
+
/** User-declared extra attributes beyond the always-present built-ins. */
|
|
49
|
+
attributes?: AttributeCatalog;
|
|
50
|
+
/** Fixed simulation timestep. Default 1 / 60. */
|
|
51
|
+
dt?: number;
|
|
52
|
+
bounds?: BoundsMode;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/particles/emitter.d.ts
|
|
56
|
+
/** Local tuple form used by emitter samplers and specs. The math module's
|
|
57
|
+
* `Vec2` is object-shaped (`{x, y}`); this subsystem prefers tuples so
|
|
58
|
+
* samplers can produce/consume array literals without allocation churn. */
|
|
59
|
+
type Vec2 = readonly [x: number, y: number];
|
|
60
|
+
/** Scalar that can be sampled as a point in 2D space. */
|
|
61
|
+
type PositionShape = {
|
|
62
|
+
kind: "point";
|
|
63
|
+
at: Vec2 | (() => Vec2);
|
|
64
|
+
} | {
|
|
65
|
+
kind: "disc";
|
|
66
|
+
center: Vec2;
|
|
67
|
+
radius: number;
|
|
68
|
+
} | {
|
|
69
|
+
kind: "ring";
|
|
70
|
+
center: Vec2;
|
|
71
|
+
radius: number;
|
|
72
|
+
thickness?: number;
|
|
73
|
+
} | {
|
|
74
|
+
kind: "rect";
|
|
75
|
+
min: Vec2;
|
|
76
|
+
max: Vec2;
|
|
77
|
+
} | {
|
|
78
|
+
kind: "line";
|
|
79
|
+
a: Vec2;
|
|
80
|
+
b: Vec2;
|
|
81
|
+
} | {
|
|
82
|
+
kind: "grid";
|
|
83
|
+
origin: Vec2;
|
|
84
|
+
cols: number;
|
|
85
|
+
rows: number;
|
|
86
|
+
spacing: number | Vec2;
|
|
87
|
+
} | {
|
|
88
|
+
kind: "fn";
|
|
89
|
+
fn: (i: number, count: number) => Vec2;
|
|
90
|
+
};
|
|
91
|
+
type VelocityShape = {
|
|
92
|
+
kind: "zero";
|
|
93
|
+
} | {
|
|
94
|
+
kind: "uniform";
|
|
95
|
+
value: Vec2;
|
|
96
|
+
} | {
|
|
97
|
+
kind: "random";
|
|
98
|
+
speed: [min: number, max: number];
|
|
99
|
+
} | {
|
|
100
|
+
kind: "cone";
|
|
101
|
+
dir: Vec2 | (() => Vec2);
|
|
102
|
+
spread: number;
|
|
103
|
+
speed: [number, number];
|
|
104
|
+
} | {
|
|
105
|
+
kind: "tangential";
|
|
106
|
+
center: Vec2;
|
|
107
|
+
speed: [number, number];
|
|
108
|
+
ccw?: boolean;
|
|
109
|
+
} | {
|
|
110
|
+
kind: "fn";
|
|
111
|
+
fn: (i: number, count: number) => Vec2;
|
|
112
|
+
};
|
|
113
|
+
/** Either a fixed value or a `[min, max]` range to sample uniformly. */
|
|
114
|
+
type ScalarRange = number | readonly [number, number];
|
|
115
|
+
/**
|
|
116
|
+
* Per-attribute value at emit time. Scalar types accept a fixed number, a
|
|
117
|
+
* `[min, max]` range, or a sampler `(i, count) => number`. Vector types
|
|
118
|
+
* accept a fixed array, or a sampler `(i, count) => readonly number[]`.
|
|
119
|
+
*
|
|
120
|
+
* For integer attribute types (`u32`, `i32`, `vec2u`, `vec4u`) floats are
|
|
121
|
+
* truncated via `Math.floor` when packing. Omitted attributes default to
|
|
122
|
+
* zero.
|
|
123
|
+
*/
|
|
124
|
+
type AttributeSampler = number | readonly [number, number] | readonly number[] | ((i: number, count: number) => number | readonly number[]);
|
|
125
|
+
interface EmitSpec {
|
|
126
|
+
count: number;
|
|
127
|
+
position: PositionShape;
|
|
128
|
+
velocity?: VelocityShape;
|
|
129
|
+
/** Per-particle lifetime in seconds. Default `Infinity` (immortal). */
|
|
130
|
+
lifetime?: ScalarRange;
|
|
131
|
+
/** Per-particle user-attribute values. Unmentioned attrs default to zero. */
|
|
132
|
+
attributes?: Readonly<Record<string, AttributeSampler>>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* RNG interface. Defaults to `Math.random`. Tests inject a deterministic
|
|
136
|
+
* source for reproducible assertions.
|
|
137
|
+
*/
|
|
138
|
+
type Rng = () => number;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/particles/state.d.ts
|
|
141
|
+
interface BuiltinBuffers {
|
|
142
|
+
readonly positions: GPUBuffer;
|
|
143
|
+
readonly velocities: GPUBuffer;
|
|
144
|
+
readonly accelPrev: GPUBuffer;
|
|
145
|
+
readonly accel: GPUBuffer;
|
|
146
|
+
readonly ages: GPUBuffer;
|
|
147
|
+
readonly lifetimes: GPUBuffer;
|
|
148
|
+
readonly alive: GPUBuffer;
|
|
149
|
+
readonly aliveCount: GPUBuffer;
|
|
150
|
+
}
|
|
151
|
+
interface IndirectBuffers {
|
|
152
|
+
/** dispatchWorkgroupsIndirect args: [workgroupCountX, 1, 1] */
|
|
153
|
+
readonly dispatchArgs: GPUBuffer;
|
|
154
|
+
/** drawIndexedIndirect args: [indexCount, instanceCount, firstIndex, baseVertex, firstInstance] */
|
|
155
|
+
readonly drawArgs: GPUBuffer;
|
|
156
|
+
}
|
|
157
|
+
interface UserAttributeBuffer {
|
|
158
|
+
readonly name: string;
|
|
159
|
+
readonly type: AttributeType;
|
|
160
|
+
readonly strideBytes: number;
|
|
161
|
+
readonly buffer: GPUBuffer;
|
|
162
|
+
/**
|
|
163
|
+
* Declaration-order index. Matches `@group(1) @binding(<index>)` in the
|
|
164
|
+
* prelude — so caller code that needs to resolve an attribute's binding
|
|
165
|
+
* reads this field.
|
|
166
|
+
*/
|
|
167
|
+
readonly bindingIndex: number;
|
|
168
|
+
}
|
|
169
|
+
/** One attribute's main + scratch buffers + codegen metadata. */
|
|
170
|
+
interface ScatteredAttribute {
|
|
171
|
+
readonly name: string;
|
|
172
|
+
/** WGSL element type (e.g. `vec2f`, `f32`, `u32`). Used to pick a scatter pipeline. */
|
|
173
|
+
readonly wgslType: string;
|
|
174
|
+
/** Bytes per element on the GPU (std430-aligned). Used for copyBufferToBuffer. */
|
|
175
|
+
readonly strideBytes: number;
|
|
176
|
+
/** The main (live) attribute buffer. Forces / integrator / render read & write it. */
|
|
177
|
+
readonly main: GPUBuffer;
|
|
178
|
+
/** Compaction scratch — scatter writes here, then copyBufferToBuffer lifts it back. */
|
|
179
|
+
readonly scratch: GPUBuffer;
|
|
180
|
+
}
|
|
181
|
+
interface CompactionBuffers {
|
|
182
|
+
/**
|
|
183
|
+
* Per-slot exclusive prefix sum within its 1024-element block. Written by
|
|
184
|
+
* scan-local, read by scatter. Sized to `capacity` u32.
|
|
185
|
+
*/
|
|
186
|
+
readonly scanLocal: GPUBuffer;
|
|
187
|
+
/**
|
|
188
|
+
* Per-block total alive count. Written by scan-local (one entry per block),
|
|
189
|
+
* read by scan-blocks. Sized to `blockCount` u32.
|
|
190
|
+
*/
|
|
191
|
+
readonly blockTotals: GPUBuffer;
|
|
192
|
+
/**
|
|
193
|
+
* Per-block exclusive prefix of blockTotals. Written by scan-blocks, read
|
|
194
|
+
* by scatter. Sized to `blockCount` u32.
|
|
195
|
+
*/
|
|
196
|
+
readonly blockOffsets: GPUBuffer;
|
|
197
|
+
/**
|
|
198
|
+
* Two u32s: `[base, actualCount]`. Written by emit-reserve (single thread),
|
|
199
|
+
* read by the emit kernel. Exposing this lets the reserve + emit kernels
|
|
200
|
+
* share a GPU-resident hand-off with no CPU round trip.
|
|
201
|
+
*/
|
|
202
|
+
readonly emitReserve: GPUBuffer;
|
|
203
|
+
/**
|
|
204
|
+
* Number of scan blocks = `ceil(capacity / ELEMENTS_PER_BLOCK)`. Bounded by
|
|
205
|
+
* `COMPACT_BLOCK_SIZE` (= 256) by the capacity cap so pass B stays single-
|
|
206
|
+
* workgroup.
|
|
207
|
+
*/
|
|
208
|
+
readonly blockCount: number;
|
|
209
|
+
}
|
|
210
|
+
interface ParticleState {
|
|
211
|
+
readonly capacity: number;
|
|
212
|
+
readonly catalog: AttributeCatalog;
|
|
213
|
+
readonly builtins: BuiltinBuffers;
|
|
214
|
+
readonly indirect: IndirectBuffers;
|
|
215
|
+
readonly compaction: CompactionBuffers;
|
|
216
|
+
/** Declaration-ordered — `userAttrs[i].bindingIndex === i`. */
|
|
217
|
+
readonly userAttrs: readonly UserAttributeBuffer[];
|
|
218
|
+
/**
|
|
219
|
+
* Every attribute that must survive compaction — built-ins (sans `accel`
|
|
220
|
+
* and `alive`) followed by user attributes in declaration order. The
|
|
221
|
+
* compaction pipeline iterates this list once per step.
|
|
222
|
+
*/
|
|
223
|
+
readonly scatteredAttributes: readonly ScatteredAttribute[];
|
|
224
|
+
readonly stateBindGroupLayout: GPUBindGroupLayout;
|
|
225
|
+
readonly userAttrBindGroupLayout: GPUBindGroupLayout;
|
|
226
|
+
readonly stateBindGroup: GPUBindGroup;
|
|
227
|
+
readonly userAttrBindGroup: GPUBindGroup;
|
|
228
|
+
destroy(): void;
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/particles/spatial-hash.d.ts
|
|
232
|
+
interface ParticleSpatialHashOptions {
|
|
233
|
+
readonly root: TgpuRoot;
|
|
234
|
+
readonly state: ParticleState;
|
|
235
|
+
/** World-space maximum pair distance. Drives cell size (= 2 × reach). */
|
|
236
|
+
readonly reach: number;
|
|
237
|
+
readonly label?: string;
|
|
238
|
+
}
|
|
239
|
+
declare class ParticleSpatialHash {
|
|
240
|
+
readonly reach: number;
|
|
241
|
+
readonly cellSize: number;
|
|
242
|
+
readonly bucketCount: number;
|
|
243
|
+
/** Buffers consumers bind in their accumulate kernels (heads/next/cells/params). */
|
|
244
|
+
readonly consumeBindGroupLayout: GPUBindGroupLayout;
|
|
245
|
+
readonly consumeBindGroup: GPUBindGroup;
|
|
246
|
+
private readonly device;
|
|
247
|
+
private readonly heads;
|
|
248
|
+
private readonly next;
|
|
249
|
+
private readonly cells;
|
|
250
|
+
private readonly paramsBuffer;
|
|
251
|
+
private readonly clearBindGroup;
|
|
252
|
+
private readonly clearPipeline;
|
|
253
|
+
private readonly clearWorkgroups;
|
|
254
|
+
private readonly buildBindGroup;
|
|
255
|
+
private readonly buildPipeline;
|
|
256
|
+
constructor(opts: ParticleSpatialHashOptions);
|
|
257
|
+
/**
|
|
258
|
+
* Record clear + indirect build onto the open compute pass. Caller supplies
|
|
259
|
+
* the same `dispatchArgs` buffer `prepareIndirect` wrote this frame.
|
|
260
|
+
*/
|
|
261
|
+
record(pass: GPUComputePassEncoder, dispatchArgs: GPUBuffer): void;
|
|
262
|
+
destroy(): void;
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/particles/forces/types.d.ts
|
|
266
|
+
interface ForceAttachContext {
|
|
267
|
+
readonly root: TgpuRoot;
|
|
268
|
+
readonly state: ParticleState;
|
|
269
|
+
/**
|
|
270
|
+
* Label prefix for GPU-object debug names. Forces should concatenate their
|
|
271
|
+
* own kind: e.g. `${ctx.label}.drag.pipeline`.
|
|
272
|
+
*/
|
|
273
|
+
readonly label: string;
|
|
274
|
+
/**
|
|
275
|
+
* Request (or get the existing) spatial hash for this reach. Reaches within
|
|
276
|
+
* a small epsilon share an instance. Only pairwise forces use this; per-
|
|
277
|
+
* particle forces ignore it.
|
|
278
|
+
*/
|
|
279
|
+
readonly requestSpatialHash: (reach: number) => ParticleSpatialHash;
|
|
280
|
+
}
|
|
281
|
+
interface ForceHandle {
|
|
282
|
+
/**
|
|
283
|
+
* Record this force's dispatch onto an open compute pass. Forces may record
|
|
284
|
+
* one or more dispatches (e.g. pairwise = build + accumulate); no structural
|
|
285
|
+
* requirement beyond not ending the pass.
|
|
286
|
+
*/
|
|
287
|
+
record(pass: GPUComputePassEncoder, ctx: ForceStepContext): void;
|
|
288
|
+
destroy(): void;
|
|
289
|
+
}
|
|
290
|
+
interface ForceStepContext {
|
|
291
|
+
/** Buffer populated by `prepareIndirect` — 3×u32 `[wgCount, 1, 1]`. */
|
|
292
|
+
readonly dispatchArgs: GPUBuffer;
|
|
293
|
+
/** Fixed step size this frame, in seconds. */
|
|
294
|
+
readonly dt: number;
|
|
295
|
+
}
|
|
296
|
+
interface Force {
|
|
297
|
+
readonly kind: string;
|
|
298
|
+
attach(ctx: ForceAttachContext): ForceHandle;
|
|
299
|
+
/**
|
|
300
|
+
* Optional per-step CPU hook for forces that need to sample some JS-side
|
|
301
|
+
* state and write it into a uniform (e.g. pointer position). Called before
|
|
302
|
+
* `record()` each step.
|
|
303
|
+
*/
|
|
304
|
+
prepare?(ctx: ForceStepContext): void;
|
|
305
|
+
}
|
|
306
|
+
/** Opaque ID the system returns from `addForce` to identify a force later. */
|
|
307
|
+
type ForceId = number;
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/particles/render/shader.wgsl.d.ts
|
|
310
|
+
type ShapeKind = "circle" | "square" | "triangle" | "rounded-square";
|
|
311
|
+
type ShapeOption = ShapeKind | {
|
|
312
|
+
readonly sdf: string;
|
|
313
|
+
};
|
|
314
|
+
type ColorOption = "uniform" | {
|
|
315
|
+
readonly wgsl: string;
|
|
316
|
+
};
|
|
317
|
+
type FadeOption = "none" | "linear" | "lut";
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/particles/render/drawable-v3.d.ts
|
|
320
|
+
interface Color4 {
|
|
321
|
+
r: number;
|
|
322
|
+
g: number;
|
|
323
|
+
b: number;
|
|
324
|
+
a: number;
|
|
325
|
+
}
|
|
326
|
+
type FadeByAgeOption = boolean | {
|
|
327
|
+
readonly curve: (t: number) => number;
|
|
328
|
+
};
|
|
329
|
+
interface DrawableV3Options {
|
|
330
|
+
/** Shape SDF. Default 'circle'. */
|
|
331
|
+
shape?: ShapeOption;
|
|
332
|
+
/** World-unit half-size (particles are quads of 2·size). Default 4. */
|
|
333
|
+
size?: number;
|
|
334
|
+
/**
|
|
335
|
+
* Uniform RGBA for every live particle. Default opaque white. Ignored
|
|
336
|
+
* when `colorWgsl` is supplied.
|
|
337
|
+
*/
|
|
338
|
+
color?: Color4;
|
|
339
|
+
/**
|
|
340
|
+
* Custom fragment color WGSL. Must be a snippet that `return`s a `vec4f`.
|
|
341
|
+
* Receives a local `p: Particle` with fields `position, velocity, age,
|
|
342
|
+
* lifetime, index`. Mutually exclusive with `color`.
|
|
343
|
+
*/
|
|
344
|
+
colorWgsl?: string;
|
|
345
|
+
/**
|
|
346
|
+
* Seconds of velocity-direction travel added to the quad's length. `0`
|
|
347
|
+
* (default) keeps the particle round.
|
|
348
|
+
*/
|
|
349
|
+
velocityStretch?: number;
|
|
350
|
+
/**
|
|
351
|
+
* Fade each particle's alpha by its normalized age. `true` applies a linear
|
|
352
|
+
* 1→0 ramp. `{ curve }` samples the callback at 64 points in [0, 1] and
|
|
353
|
+
* LUTs it onto the GPU. Immortal particles (lifetime = Infinity) are
|
|
354
|
+
* unaffected.
|
|
355
|
+
*/
|
|
356
|
+
fadeByAge?: FadeByAgeOption;
|
|
357
|
+
/**
|
|
358
|
+
* Coordinate space the drawable occupies. Particles are world-space by
|
|
359
|
+
* default; the renderer binds the matching per-space camera bind group.
|
|
360
|
+
*/
|
|
361
|
+
space?: LayerSpace;
|
|
362
|
+
}
|
|
363
|
+
declare class ParticleDrawableV3 implements CustomDrawable {
|
|
364
|
+
readonly __customDrawable: true;
|
|
365
|
+
readonly space: LayerSpace;
|
|
366
|
+
readonly clipRect: undefined;
|
|
367
|
+
readonly opaque = false;
|
|
368
|
+
private readonly device;
|
|
369
|
+
private readonly bundle;
|
|
370
|
+
private readonly drawArgsBuffer;
|
|
371
|
+
private readonly scratch;
|
|
372
|
+
private lutTexture;
|
|
373
|
+
private lutSampler;
|
|
374
|
+
private lutBindGroup;
|
|
375
|
+
private size;
|
|
376
|
+
private color;
|
|
377
|
+
private velocityStretch;
|
|
378
|
+
private fadeByAge;
|
|
379
|
+
private destroyed;
|
|
380
|
+
constructor(renderer: Renderer2D, state: ParticleState, options?: DrawableV3Options);
|
|
381
|
+
/**
|
|
382
|
+
* Replace color / size / stretch / fade flag. Takes effect on the next
|
|
383
|
+
* frame. Does NOT rebuild the pipeline — shape and colorWgsl are baked in
|
|
384
|
+
* at construction. Swapping a curve function re-uploads the LUT texture.
|
|
385
|
+
*/
|
|
386
|
+
setOptions(options: DrawableV3Options): void;
|
|
387
|
+
record(pass: GPURenderPassEncoder, ctx: CustomDrawableContext): void;
|
|
388
|
+
destroy(): void;
|
|
389
|
+
private buildLut;
|
|
390
|
+
private uploadParams;
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/particles/system.d.ts
|
|
394
|
+
/**
|
|
395
|
+
* GPU particle system. Manages a fixed-capacity pool of particles with SoA
|
|
396
|
+
* attribute storage, a pluggable force chain, CPU-driven emission, and
|
|
397
|
+
* dedicated draw-indirect rendering.
|
|
398
|
+
*
|
|
399
|
+
* Typical usage:
|
|
400
|
+
*
|
|
401
|
+
* ```ts
|
|
402
|
+
* const system = new ParticleSystem(renderer.getRoot(), {
|
|
403
|
+
* capacity: 20_000,
|
|
404
|
+
* bounds: { kind: "wrap", rect: [-400, -300, 400, 300] },
|
|
405
|
+
* });
|
|
406
|
+
* system.addForce(drag({ coefficient: 0.2 }));
|
|
407
|
+
* system.addForce(gravity({ direction: [0, 1], strength: 300 }));
|
|
408
|
+
* const drawable = system.createDrawable(renderer, { fadeByAge: true });
|
|
409
|
+
*
|
|
410
|
+
* // per frame:
|
|
411
|
+
* system.emit({ count: 20, position: {...}, velocity: {...} });
|
|
412
|
+
* system.step(dt);
|
|
413
|
+
* renderer.render([drawable]);
|
|
414
|
+
* ```
|
|
415
|
+
*
|
|
416
|
+
* Capacity is bounded at construction (no auto-grow). The compaction
|
|
417
|
+
* module runs every step to keep the alive list dense; `getAliveCount()`
|
|
418
|
+
* returns the GPU-authoritative count via a non-blocking readback.
|
|
419
|
+
*/
|
|
420
|
+
declare class ParticleSystem {
|
|
421
|
+
private readonly options;
|
|
422
|
+
readonly capacity: number;
|
|
423
|
+
/** Attribute names in declaration order. Binding indices match this order. */
|
|
424
|
+
readonly attributeNames: readonly string[];
|
|
425
|
+
/** @internal — exposed for kernel attach in later phases. */
|
|
426
|
+
readonly state: ParticleState;
|
|
427
|
+
/** @internal — shared across every pipeline this system compiles. */
|
|
428
|
+
readonly pipelineCache: PipelineCache;
|
|
429
|
+
private readonly root;
|
|
430
|
+
private readonly dt;
|
|
431
|
+
private readonly drawables;
|
|
432
|
+
private readonly emitQueue;
|
|
433
|
+
private readonly forces;
|
|
434
|
+
/** Spatial hashes keyed by quantized reach. Shared across pairwise forces. */
|
|
435
|
+
private readonly hashes;
|
|
436
|
+
private nextForceId;
|
|
437
|
+
private emitStagingCursor;
|
|
438
|
+
private emitPipeline;
|
|
439
|
+
private prepareIndirectPipeline;
|
|
440
|
+
private zeroAccelPipeline;
|
|
441
|
+
private integratorPipeline;
|
|
442
|
+
private boundsPipeline;
|
|
443
|
+
private compactionPipeline;
|
|
444
|
+
private aliveCountCache;
|
|
445
|
+
private aliveCountReadback;
|
|
446
|
+
private aliveCountReadbackInFlight;
|
|
447
|
+
private destroyed;
|
|
448
|
+
constructor(owner: GPUOwner, options: ParticleSystemOptions);
|
|
449
|
+
getAttributeNames(): readonly string[];
|
|
450
|
+
/**
|
|
451
|
+
* Write raw data into a user-declared attribute buffer at the given element
|
|
452
|
+
* offset. Intended for one-shot initialisation (e.g. seeding `species` after
|
|
453
|
+
* emitting a fixed population) and interactive tooling — not a hot-path API;
|
|
454
|
+
* the emit kernel does not yet write user attributes, so callers that need
|
|
455
|
+
* per-emission attribute values fill them here directly.
|
|
456
|
+
*/
|
|
457
|
+
writeAttribute(name: string, data: ArrayBufferView, offsetElements?: number): void;
|
|
458
|
+
/**
|
|
459
|
+
* Most recent live-particle count read from the GPU. aliveCount is
|
|
460
|
+
* GPU-authoritative — the CPU does not mirror it exactly — so this returns
|
|
461
|
+
* a cached value from the last completed readback. Calling this schedules
|
|
462
|
+
* a fresh readback in the background (non-blocking); repeated callers
|
|
463
|
+
* (e.g. a per-frame HUD) converge within one frame of latency. Returns 0
|
|
464
|
+
* until the first readback completes, which normally takes 1-2 frames.
|
|
465
|
+
*/
|
|
466
|
+
getAliveCount(): number;
|
|
467
|
+
private scheduleAliveCountReadback;
|
|
468
|
+
/**
|
|
469
|
+
* Build a `CustomDrawable` that renders this system's live particles.
|
|
470
|
+
* Pass to the v3 renderer's `render([...])`. Multiple drawables may share
|
|
471
|
+
* a system.
|
|
472
|
+
*/
|
|
473
|
+
createV3Drawable(renderer: Renderer2D, options?: DrawableV3Options): ParticleDrawableV3;
|
|
474
|
+
/**
|
|
475
|
+
* Queue an emission. CPU-packs now; the staging upload + reserve + emit
|
|
476
|
+
* dispatch run at the start of the next `step()`. Overflow past capacity
|
|
477
|
+
* is dropped silently on the GPU (the reserve kernel clamps to the
|
|
478
|
+
* remaining room). Multiple emits in one frame coalesce into one upload.
|
|
479
|
+
*/
|
|
480
|
+
emit(spec: EmitSpec): void;
|
|
481
|
+
/**
|
|
482
|
+
* Attach a force. Returns a numeric id that can later be passed to
|
|
483
|
+
* `removeForce`. Forces run in registration order during each `step()`,
|
|
484
|
+
* accumulating into the per-particle acceleration buffer before the
|
|
485
|
+
* integrator sweep.
|
|
486
|
+
*/
|
|
487
|
+
addForce(force: Force): ForceId;
|
|
488
|
+
private getOrCreateHash;
|
|
489
|
+
removeForce(id: ForceId): void;
|
|
490
|
+
hasForce(id: ForceId): boolean;
|
|
491
|
+
/**
|
|
492
|
+
* Advance the simulation by one tick. Records the full pipeline
|
|
493
|
+
* (emission → prepareIndirect → zeroAccel → forces → integrate → bounds
|
|
494
|
+
* → compaction + copyback) into a single command encoder and submits.
|
|
495
|
+
*
|
|
496
|
+
* `dt` overrides the system's configured timestep for this call only.
|
|
497
|
+
* Useful for variable-rate rendering (e.g. reduce dt when catching up on
|
|
498
|
+
* multiple frames).
|
|
499
|
+
*/
|
|
500
|
+
step(dt?: number): void;
|
|
501
|
+
destroy(): void;
|
|
502
|
+
private flushEmissions;
|
|
503
|
+
private ensureEmitPipeline;
|
|
504
|
+
private ensurePrepareIndirect;
|
|
505
|
+
private ensureZeroAccel;
|
|
506
|
+
private ensureIntegrator;
|
|
507
|
+
private ensureBounds;
|
|
508
|
+
private ensureCompaction;
|
|
509
|
+
private pendingEmitCount;
|
|
510
|
+
private assertAlive;
|
|
511
|
+
}
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/particles/forces/attractor.d.ts
|
|
514
|
+
type AttractorFalloff = "inverseSquare" | "linear" | "spring";
|
|
515
|
+
interface AttractorOptions {
|
|
516
|
+
/** World-space anchor the attractor pulls toward (negative strength pushes). */
|
|
517
|
+
point: readonly [number, number];
|
|
518
|
+
/** Signed strength. Positive = pull, negative = push. */
|
|
519
|
+
strength: number;
|
|
520
|
+
/**
|
|
521
|
+
* Distance beyond which the force is zero. Omit / set to 0 for an
|
|
522
|
+
* unbounded field. Linear falloff interprets `reach` as the distance where
|
|
523
|
+
* the force has decayed to zero.
|
|
524
|
+
*/
|
|
525
|
+
reach?: number;
|
|
526
|
+
/** Default: `'inverseSquare'`. */
|
|
527
|
+
falloff?: AttractorFalloff;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Radial attraction / repulsion around a fixed world point. Use negative
|
|
531
|
+
* `strength` for repulsion. Falloff kinds control how force decays with
|
|
532
|
+
* distance; `reach` optionally hard-clips beyond that radius.
|
|
533
|
+
*/
|
|
534
|
+
declare function attractor(options: AttractorOptions): Force;
|
|
535
|
+
interface PointerOptions {
|
|
536
|
+
/** Callable returning the current world-space target each frame. */
|
|
537
|
+
target: () => readonly [number, number];
|
|
538
|
+
/**
|
|
539
|
+
* Attraction strength. A number is static; a callback is resampled every
|
|
540
|
+
* step — flip sign to toggle attract/repel, set to 0 to disable without
|
|
541
|
+
* pipeline churn.
|
|
542
|
+
*/
|
|
543
|
+
strength: number | (() => number);
|
|
544
|
+
reach?: number;
|
|
545
|
+
falloff?: AttractorFalloff;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Like `attractor`, but the anchor point is resampled from `target()` on
|
|
549
|
+
* every step. Built on the same kernel — pointer dragging the canvas is the
|
|
550
|
+
* canonical use.
|
|
551
|
+
*/
|
|
552
|
+
declare function pointer(options: PointerOptions): Force;
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/particles/forces/boids.d.ts
|
|
555
|
+
interface BoidsOptions {
|
|
556
|
+
/** Neighbourhood radius. Also the cell size driver for the shared hash. */
|
|
557
|
+
reach: number;
|
|
558
|
+
/** Alignment weight — match neighbours' heading. Typical 0.5–1.5. */
|
|
559
|
+
alignment?: number;
|
|
560
|
+
/** Cohesion weight — steer toward local centroid. Typical 0.3–1.0. */
|
|
561
|
+
cohesion?: number;
|
|
562
|
+
/** Separation weight — push away from close neighbours. Typical 1.0–2.0. */
|
|
563
|
+
separation?: number;
|
|
564
|
+
/**
|
|
565
|
+
* Distance at which separation kicks in. Should be smaller than `reach`;
|
|
566
|
+
* default = reach × 0.4.
|
|
567
|
+
*/
|
|
568
|
+
separationRadius?: number;
|
|
569
|
+
/**
|
|
570
|
+
* Target cruising speed used to build desired-velocity vectors. The kernel
|
|
571
|
+
* itself doesn't clamp speed — pair with a `drag` force or clamp in your
|
|
572
|
+
* integrator.
|
|
573
|
+
*/
|
|
574
|
+
maxSpeed?: number;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Reynolds boids — alignment, cohesion, separation over a spatial-hash
|
|
578
|
+
* neighbourhood. Shares the system's hash at the given `reach`.
|
|
579
|
+
*/
|
|
580
|
+
declare function boids(options: BoidsOptions): Force;
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region src/particles/forces/drag.d.ts
|
|
583
|
+
interface DragOptions {
|
|
584
|
+
/**
|
|
585
|
+
* Per-second velocity-decay coefficient. Larger → more damping. Applied as
|
|
586
|
+
* an acceleration, so the integrator step is `v' = v + (a_other - c*v)·dt`.
|
|
587
|
+
*/
|
|
588
|
+
coefficient: number;
|
|
589
|
+
}
|
|
590
|
+
/** Linear velocity damping. `a += -coefficient * v`. */
|
|
591
|
+
declare function drag(options: DragOptions): Force;
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region src/particles/forces/field.d.ts
|
|
594
|
+
interface FieldWorldBounds {
|
|
595
|
+
/** World-space x of the texture's left edge. */
|
|
596
|
+
readonly x: number;
|
|
597
|
+
/** World-space y of the texture's bottom edge. */
|
|
598
|
+
readonly y: number;
|
|
599
|
+
/** World-space width the texture spans. */
|
|
600
|
+
readonly width: number;
|
|
601
|
+
/** World-space height the texture spans. */
|
|
602
|
+
readonly height: number;
|
|
603
|
+
}
|
|
604
|
+
/** Row-major 2×3 affine as six numbers: [m00, m01, m02, m10, m11, m12]. */
|
|
605
|
+
type FieldAffine2x3 = readonly [number, number, number, number, number, number];
|
|
606
|
+
type FieldWrap = "clamp" | "repeat";
|
|
607
|
+
interface FieldOptions {
|
|
608
|
+
/** 2D GPU texture encoding a vec2 field in its R and G channels. */
|
|
609
|
+
readonly texture: GPUTexture;
|
|
610
|
+
/** Acceleration scalar. Multiplies the sampled vector. */
|
|
611
|
+
readonly strength: number;
|
|
612
|
+
/**
|
|
613
|
+
* Rectangle in world space that the texture covers, mapping to uv [0..1]².
|
|
614
|
+
* Mutually exclusive with `worldToUv`. If neither is given, the texture is
|
|
615
|
+
* treated as covering `[0, width] × [0, height]` world units 1:1.
|
|
616
|
+
*/
|
|
617
|
+
readonly worldBounds?: FieldWorldBounds;
|
|
618
|
+
/**
|
|
619
|
+
* Explicit world→uv affine matrix. Row-major 2×3:
|
|
620
|
+
* `uv = [[m00 m01 m02], [m10 m11 m12]] · [x, y, 1]ᵀ`.
|
|
621
|
+
*/
|
|
622
|
+
readonly worldToUv?: FieldAffine2x3;
|
|
623
|
+
/** Sampler address mode outside the unit UV square. Default 'clamp'. */
|
|
624
|
+
readonly wrap?: FieldWrap;
|
|
625
|
+
/** Sampler filter. Default 'linear' (falls back to 'nearest' for non-filterable formats). */
|
|
626
|
+
readonly filter?: "linear" | "nearest";
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Texture-sampled flow-field force. Particles receive
|
|
630
|
+
* `sampleRG(worldToUv·pos) * strength` as an acceleration each step.
|
|
631
|
+
*/
|
|
632
|
+
declare function field(options: FieldOptions): Force;
|
|
633
|
+
declare class FieldHandle implements ForceHandle {
|
|
634
|
+
private readonly device;
|
|
635
|
+
private readonly paramsBuffer;
|
|
636
|
+
private readonly paramsScratch;
|
|
637
|
+
private readonly bindGroup;
|
|
638
|
+
private readonly pipeline;
|
|
639
|
+
private strength;
|
|
640
|
+
private matrix;
|
|
641
|
+
constructor(ctx: ForceAttachContext, opts: FieldOptions);
|
|
642
|
+
/** Swap strength at runtime without rebuilding the pipeline. */
|
|
643
|
+
setStrength(strength: number): void;
|
|
644
|
+
record(pass: GPUComputePassEncoder, ctx: ForceStepContext): void;
|
|
645
|
+
destroy(): void;
|
|
646
|
+
}
|
|
647
|
+
//#endregion
|
|
648
|
+
//#region src/particles/forces/gravity.d.ts
|
|
649
|
+
interface GravityOptions {
|
|
650
|
+
/** Direction of the gravity vector. Does not need to be normalized. */
|
|
651
|
+
direction: [number, number];
|
|
652
|
+
/** Magnitude in world-units / second². Applied along the normalized direction. */
|
|
653
|
+
strength: number;
|
|
654
|
+
}
|
|
655
|
+
/** Uniform acceleration in a fixed direction. Classic "falling" force. */
|
|
656
|
+
declare function gravity(options: GravityOptions): Force;
|
|
657
|
+
//#endregion
|
|
658
|
+
//#region src/particles/forces/noise.d.ts
|
|
659
|
+
type NoiseKind = "simplex" | "curl";
|
|
660
|
+
interface NoiseOptions {
|
|
661
|
+
/** Noise frequency (cycles per world unit). Typical: 0.002–0.05. */
|
|
662
|
+
scale: number;
|
|
663
|
+
/** Force magnitude scalar. Rough guideline: match your drag coefficient. */
|
|
664
|
+
strength: number;
|
|
665
|
+
/** Default `'curl'` — the divergence-free flow-field look. */
|
|
666
|
+
kind?: NoiseKind;
|
|
667
|
+
/**
|
|
668
|
+
* How fast the field evolves over time. 0 freezes it. Default 1 — each
|
|
669
|
+
* second of simulation advances the noise seed by 1 unit.
|
|
670
|
+
*/
|
|
671
|
+
timeScale?: number;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Simplex-noise flow force. `curl` (default) gives a divergence-free field
|
|
675
|
+
* for wispy, flow-like motion; `simplex` applies the scalar gradient
|
|
676
|
+
* directly and looks more turbulent.
|
|
677
|
+
*/
|
|
678
|
+
declare function noise(options: NoiseOptions): Force;
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/particles/forces/pairwise.d.ts
|
|
681
|
+
interface PairwiseExtraBinding {
|
|
682
|
+
/**
|
|
683
|
+
* WGSL declaration line — e.g. `@group(3) @binding(1) var<storage, read> pairMatrix: array<f32>;`.
|
|
684
|
+
* Binding indices must be ≥ 1 (binding 0 is reserved for the params uniform).
|
|
685
|
+
* Note: `matrix` is a reserved WGSL identifier — use `pairMatrix` or similar.
|
|
686
|
+
*/
|
|
687
|
+
readonly wgsl: string;
|
|
688
|
+
readonly layoutEntry: GPUBindGroupLayoutEntry;
|
|
689
|
+
readonly bindGroupEntry: GPUBindGroupEntry;
|
|
690
|
+
}
|
|
691
|
+
interface PairwiseParamsSpec {
|
|
692
|
+
/** Full WGSL `struct PairwiseParams { ... };` declaration. */
|
|
693
|
+
readonly structWgsl: string;
|
|
694
|
+
/** Byte size of the packed struct (std140 uniform rules). */
|
|
695
|
+
readonly bytes: number;
|
|
696
|
+
/**
|
|
697
|
+
* Optional initial pack — receives a DataView into a fresh ArrayBuffer of
|
|
698
|
+
* `bytes` length in little-endian. Callers can also upload later via
|
|
699
|
+
* `PairwiseHandle.writeParams`.
|
|
700
|
+
*/
|
|
701
|
+
readonly write?: (view: DataView) => void;
|
|
702
|
+
}
|
|
703
|
+
interface PairwiseOptions {
|
|
704
|
+
/** Max pair distance in world units. Hash cell size = 2 × reach. */
|
|
705
|
+
readonly reach: number;
|
|
706
|
+
/** User attributes to expose on the `me` / `other` Particle structs. */
|
|
707
|
+
readonly reads?: readonly string[];
|
|
708
|
+
/**
|
|
709
|
+
* WGSL body run inside the neighbourhood loop. Has access to `me`, `other`,
|
|
710
|
+
* `addAccel(index, delta)`, `params.*`, `hashParams.*` and standard WGSL.
|
|
711
|
+
* Must not end the loop — a trailing `cursor = next[cursor];` is appended
|
|
712
|
+
* by the wrapper.
|
|
713
|
+
*/
|
|
714
|
+
readonly wgsl: string;
|
|
715
|
+
readonly params?: PairwiseParamsSpec;
|
|
716
|
+
readonly extraBindings?: readonly PairwiseExtraBinding[];
|
|
717
|
+
/** Debug label suffix appended to `ctx.label`. Default `pairwise`. */
|
|
718
|
+
readonly label?: string;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Generic pairwise force. Typically invoked by presets (`particleLife`,
|
|
722
|
+
* `boids`) but also exposed directly for ad-hoc user kernels.
|
|
723
|
+
*/
|
|
724
|
+
declare function pairwise(options: PairwiseOptions): Force;
|
|
725
|
+
declare class PairwiseHandle implements ForceHandle {
|
|
726
|
+
private readonly device;
|
|
727
|
+
private readonly hash;
|
|
728
|
+
private readonly pipeline;
|
|
729
|
+
private readonly stateBindGroup;
|
|
730
|
+
private readonly userAttrBindGroup;
|
|
731
|
+
private readonly hashBindGroup;
|
|
732
|
+
private readonly paramsGroup;
|
|
733
|
+
private readonly paramsBuffer;
|
|
734
|
+
private readonly paramsSize;
|
|
735
|
+
constructor(ctx: ForceAttachContext, opts: PairwiseOptions);
|
|
736
|
+
record(pass: GPUComputePassEncoder, ctx: ForceStepContext): void;
|
|
737
|
+
/**
|
|
738
|
+
* Overwrite the params uniform. Caller packs into a view matching the
|
|
739
|
+
* struct declared at construction. Bytes beyond `paramsSize` are ignored.
|
|
740
|
+
*/
|
|
741
|
+
writeParams(data: ArrayBufferView): void;
|
|
742
|
+
destroy(): void;
|
|
743
|
+
}
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/particles/forces/speed-limit.d.ts
|
|
746
|
+
interface SpeedLimitOptions {
|
|
747
|
+
/** Maximum allowed speed in world-units per second. */
|
|
748
|
+
maxSpeed: number;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Hard cap on particle speed. Applies a corrective acceleration so that
|
|
752
|
+
* after the next integrator step, speed ≤ maxSpeed. Register AFTER other
|
|
753
|
+
* forces to see the full accumulated excess.
|
|
754
|
+
*/
|
|
755
|
+
declare function speedLimit(options: SpeedLimitOptions): Force;
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/particles/forces/particle-life.d.ts
|
|
758
|
+
interface PairRule {
|
|
759
|
+
/** Signed long-range attraction coefficient. Negative = repel. */
|
|
760
|
+
attract: number;
|
|
761
|
+
/** Per-pair max reach (0 → fall back to global reach). */
|
|
762
|
+
reach?: number;
|
|
763
|
+
/** Per-pair short-range repel distance (0 → fall back to global minDist). */
|
|
764
|
+
minDist?: number;
|
|
765
|
+
}
|
|
766
|
+
declare class PairMatrix {
|
|
767
|
+
readonly speciesCount: number;
|
|
768
|
+
/** Flat f32 data, length = speciesCount² × 4. Row-major. */
|
|
769
|
+
readonly data: Float32Array;
|
|
770
|
+
constructor(speciesCount: number);
|
|
771
|
+
set(a: number, b: number, rule: PairRule): void;
|
|
772
|
+
get(a: number, b: number): PairRule;
|
|
773
|
+
}
|
|
774
|
+
interface RandomPairMatrixOptions {
|
|
775
|
+
/** `[min, max]` attract range, uniform. Default `[-1, 1]`. */
|
|
776
|
+
attract?: [number, number];
|
|
777
|
+
/** Optional per-pair reach range; omit to inherit the force's global reach. */
|
|
778
|
+
reach?: [number, number];
|
|
779
|
+
/** Optional per-pair minDist range; omit to inherit the force's global minDist. */
|
|
780
|
+
minDist?: [number, number];
|
|
781
|
+
/** PRNG — default `Math.random`. */
|
|
782
|
+
rand?: () => number;
|
|
783
|
+
}
|
|
784
|
+
declare function randomPairMatrix(speciesCount: number, opts?: RandomPairMatrixOptions): PairMatrix;
|
|
785
|
+
interface ParticleLifeOptions {
|
|
786
|
+
/** Global max pair distance. Per-pair `matrix[a,b].reach` may override per pair. */
|
|
787
|
+
reach: number;
|
|
788
|
+
/** Universal short-range repel distance. Per-pair override via matrix. */
|
|
789
|
+
minDist: number;
|
|
790
|
+
/** Repel strength when `d < minDist`. Default 20. */
|
|
791
|
+
repelStrength?: number;
|
|
792
|
+
/** Initial matrix. Size fixes `speciesCount`; swap via `updateMatrix`. */
|
|
793
|
+
matrix: PairMatrix;
|
|
794
|
+
/** Name of the declared `u32` species attribute. Default `species`. */
|
|
795
|
+
speciesAttribute?: string;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Create a particleLife force. The returned Force's handle exposes
|
|
799
|
+
* `updateMatrix(newMatrix)` so callers can regenerate the matrix without
|
|
800
|
+
* rebuilding the pipeline.
|
|
801
|
+
*/
|
|
802
|
+
declare function particleLife(options: ParticleLifeOptions): ParticleLifeForce;
|
|
803
|
+
declare class ParticleLifeForce implements Force {
|
|
804
|
+
readonly kind = "particleLife";
|
|
805
|
+
private readonly opts;
|
|
806
|
+
private handle;
|
|
807
|
+
constructor(opts: ParticleLifeOptions);
|
|
808
|
+
attach(ctx: ForceAttachContext): ForceHandle;
|
|
809
|
+
/**
|
|
810
|
+
* Replace the matrix contents on the GPU. Attach first; no-op if called
|
|
811
|
+
* before. The matrix size must match the speciesCount fixed at attach time.
|
|
812
|
+
*/
|
|
813
|
+
updateMatrix(matrix: PairMatrix): void;
|
|
814
|
+
}
|
|
815
|
+
//#endregion
|
|
816
|
+
export { type AttractorFalloff, type AttractorOptions, type AttributeCatalog, type AttributeSampler, type AttributeType, type BoidsOptions, type BoundsMode, type Color4, type ColorOption, type DragOptions, type DrawableV3Options, type EmitSpec, type FadeByAgeOption, type FadeOption, type FieldAffine2x3, FieldHandle, type FieldOptions, type FieldWorldBounds, type FieldWrap, type Force, type ForceAttachContext, type ForceHandle, type ForceId, type ForceStepContext, type GPUOwner, type GravityOptions, type NoiseKind, type NoiseOptions, PairMatrix, type PairRule, type PairwiseExtraBinding, PairwiseHandle, type PairwiseOptions, type PairwiseParamsSpec, ParticleDrawableV3, ParticleLifeForce, type ParticleLifeOptions, ParticleSpatialHash, ParticleSystem, type ParticleSystemOptions, type PointerOptions, type PositionShape, type RandomPairMatrixOptions, type Rng, type ScalarRange, type ShapeKind, type ShapeOption, type SpeedLimitOptions, type VelocityShape, attractor, boids, drag, field, gravity, noise, pairwise, particleLife, pointer, randomPairMatrix, speedLimit };
|