sparcoon 0.9.1 → 0.9.3
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 +176 -76
- package/dist/artifact/FXArtifact.d.ts +51 -3
- package/dist/behaviorTransformFeedbackLayout.d.ts +24 -0
- package/dist/behaviorTransformFeedbackLayout.js +24 -0
- package/dist/coreLayout.d.ts +9 -5
- package/dist/coreLayout.js +17 -4
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +1 -1
- package/dist/effect/FXEffect.d.ts +4 -0
- package/dist/effect/FXEffect.js +47 -14
- package/dist/effect/FXEffectSpec.d.ts +21 -4
- package/dist/emitter/FXApplyRenderArtifactOptions.d.ts +15 -0
- package/dist/emitter/FXApplyRenderArtifactOptions.js +1 -0
- package/dist/emitter/FXEmitter.d.ts +44 -14
- package/dist/emitter/FXEmitter.js +132 -156
- package/dist/emitter/FXEmitterDriver.Internal.d.ts +26 -0
- package/dist/emitter/FXEmitterDriver.Internal.js +70 -0
- package/dist/emitter/FXFromArtifactsOptions.d.ts +12 -1
- package/dist/emitter/FXGPUEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXGPUEmitterDriver.Internal.js +137 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.js +133 -0
- package/dist/emitter/FXSimulationHolder.js +7 -5
- package/dist/emitter/FXTransformFeedbackSimulationHolder.d.ts +1 -0
- package/dist/emitter/FXTransformFeedbackSimulationHolder.js +249 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/instancedParticle/FXInstancedParticle.js +28 -1
- package/dist/instancedParticle/FXTransformFeedbackParticle.d.ts +1 -0
- package/dist/instancedParticle/FXTransformFeedbackParticle.js +169 -0
- package/dist/miscellaneous/fxMath.d.ts +18 -11
- package/dist/miscellaneous/fxMath.js +89 -209
- package/dist/render/FXArtifactMaterial.Internal.js +66 -40
- package/dist/render/FXArtifactMaterial.js +1 -1
- package/dist/render/FXMesh.js +2 -4
- package/package.json +11 -3
package/dist/effect/FXEffect.js
CHANGED
|
@@ -29,6 +29,10 @@ function applyTransform(object, transform, liveChannels) {
|
|
|
29
29
|
function materializeValue(value) {
|
|
30
30
|
return typeof value === "number" ? value : [...value];
|
|
31
31
|
}
|
|
32
|
+
/** Picks one emitter/mesh's compiled artifact by render target (see {@link FXEffectOptions.renderer}). */
|
|
33
|
+
function resolveRenderArtifact(render, isWebGL2) {
|
|
34
|
+
return render[isWebGL2 ? "standard" : "baseline"];
|
|
35
|
+
}
|
|
32
36
|
// Duck-typed, not `instanceof`: the app's `three` and this package's peer-dep `three` can be two
|
|
33
37
|
// separate module instances (e.g. hoisted differently across a workspace), which would make
|
|
34
38
|
// `instanceof Texture`/`instanceof BufferGeometry` false for a perfectly good asset. Every `three`
|
|
@@ -115,6 +119,11 @@ export class FXEffect extends Group {
|
|
|
115
119
|
constructor(specification, assets, options = {}) {
|
|
116
120
|
super();
|
|
117
121
|
this.emitters = [];
|
|
122
|
+
// The single artifact resolved (by render target) from each emitter/mesh spec's
|
|
123
|
+
// FXRenderArtifactsByGLSLTier - parallel to `emitters`/`meshes`, read by the parameter-drive path
|
|
124
|
+
// instead of re-resolving the spec's map on every call.
|
|
125
|
+
this.emitterRenders = [];
|
|
126
|
+
this.meshRenders = [];
|
|
118
127
|
this.meshMaterials = [];
|
|
119
128
|
this.meshes = [];
|
|
120
129
|
// World-space velocity/angular-velocity tracker per mesh, parallel to `meshes`.
|
|
@@ -140,14 +149,22 @@ export class FXEffect extends Group {
|
|
|
140
149
|
this.world = options.world ?? FXWorld.getDefault();
|
|
141
150
|
applyTransform(this, specification.transform);
|
|
142
151
|
const geometries = geometriesFromAssets(assets);
|
|
152
|
+
const isWebGL2 = options.renderer?.capabilities.isWebGL2 ?? false;
|
|
143
153
|
for (const emitterSpec of specification.emitters) {
|
|
144
154
|
const textures = texturesForSlots(emitterSpec.externalSlots, assets);
|
|
145
|
-
const
|
|
155
|
+
const render = resolveRenderArtifact(emitterSpec.render, isWebGL2);
|
|
156
|
+
const emitter = FXEmitter.fromArtifacts(render, emitterSpec.behavior, {
|
|
146
157
|
textures,
|
|
147
158
|
geometries,
|
|
148
159
|
expectedCapacity: emitterSpec.expectedCapacity,
|
|
149
160
|
castShadow: emitterSpec.castShadow ?? false,
|
|
150
161
|
receiveShadow: emitterSpec.receiveShadow ?? false,
|
|
162
|
+
// Only offered on an actual WebGL2 negotiation - the same `isWebGL2` signal already
|
|
163
|
+
// picking `render`'s "standard" tier above. FXEmitter itself decides whether GPU setup
|
|
164
|
+
// then actually succeeds, falling back to `emitterSpec.behavior` in place if not - this
|
|
165
|
+
// effect never needs to know which one ends up driving a given emitter.
|
|
166
|
+
gpuKernel: isWebGL2 ? emitterSpec.gpuBehavior : undefined,
|
|
167
|
+
renderer: options.renderer,
|
|
151
168
|
}, this.world);
|
|
152
169
|
if (options.camera !== undefined && emitterSpec.sortInterval > 0) {
|
|
153
170
|
emitter.sortCamera = options.camera;
|
|
@@ -157,11 +174,13 @@ export class FXEffect extends Group {
|
|
|
157
174
|
this.add(emitter);
|
|
158
175
|
this.emitterIndexByName.set(emitterSpec.name, this.emitters.length);
|
|
159
176
|
this.emitters.push(emitter);
|
|
177
|
+
this.emitterRenders.push(render);
|
|
160
178
|
this.emitterDriven.push(new Set());
|
|
161
179
|
}
|
|
162
180
|
for (const meshSpec of specification.meshes) {
|
|
163
181
|
const textures = texturesForSlots(meshSpec.externalSlots, assets);
|
|
164
|
-
const
|
|
182
|
+
const render = resolveRenderArtifact(meshSpec.render, isWebGL2);
|
|
183
|
+
const material = new FXMeshMaterial(render, textures);
|
|
165
184
|
const mesh = new Mesh(resolveGeometrySource(meshSpec.geometry, geometries), material.buildThreeMaterial());
|
|
166
185
|
if (meshSpec.receiveShadow ?? false) {
|
|
167
186
|
mesh.receiveShadow = true;
|
|
@@ -174,6 +193,7 @@ export class FXEffect extends Group {
|
|
|
174
193
|
this.add(mesh);
|
|
175
194
|
this.meshIndexByName.set(meshSpec.name, this.meshes.length);
|
|
176
195
|
this.meshes.push(mesh);
|
|
196
|
+
this.meshRenders.push(render);
|
|
177
197
|
this.meshMaterials.push(material);
|
|
178
198
|
this.meshMotionTrackers.push(new FXObjectMotionTracker());
|
|
179
199
|
this.meshDriven.push(new Set());
|
|
@@ -283,7 +303,7 @@ export class FXEffect extends Group {
|
|
|
283
303
|
const specification = this.specification.emitters[index];
|
|
284
304
|
const uniforms = {};
|
|
285
305
|
const bindings = {};
|
|
286
|
-
writeParameterSlots(
|
|
306
|
+
writeParameterSlots(this.emitterRenders[index], specification.behavior, parameter, materializeValue(value), uniforms, bindings);
|
|
287
307
|
if (Object.keys(uniforms).length > 0 || Object.keys(bindings).length > 0) {
|
|
288
308
|
this.emitters[index].applyValues({ uniforms, bindings });
|
|
289
309
|
}
|
|
@@ -297,10 +317,9 @@ export class FXEffect extends Group {
|
|
|
297
317
|
if (index === undefined) {
|
|
298
318
|
return;
|
|
299
319
|
}
|
|
300
|
-
const specification = this.specification.meshes[index];
|
|
301
320
|
const uniforms = {};
|
|
302
321
|
const bindings = {};
|
|
303
|
-
writeParameterSlots(
|
|
322
|
+
writeParameterSlots(this.meshRenders[index], NO_BEHAVIOR_BINDINGS, parameter, materializeValue(value), uniforms, bindings);
|
|
304
323
|
if (Object.keys(uniforms).length > 0) {
|
|
305
324
|
this.meshMaterials[index].applyUniformValues(uniforms);
|
|
306
325
|
}
|
|
@@ -353,20 +372,33 @@ export class FXEffect extends Group {
|
|
|
353
372
|
const duration = this.specification.duration;
|
|
354
373
|
let now = this.time + deltaSeconds;
|
|
355
374
|
if (duration > 0 && now >= duration) {
|
|
356
|
-
// Loop wrap: finish this lap's tail
|
|
375
|
+
// Loop wrap: finish this lap's tail from the lap's last pose, then run on into the next lap
|
|
376
|
+
// without clearing what is alive - a looping effect never pops at the seam. The pose jumps
|
|
377
|
+
// back to the start, which is no motion: every motion baseline is dropped, or the jump would
|
|
378
|
+
// read as a one-frame velocity spike.
|
|
379
|
+
this.poseAt(duration);
|
|
357
380
|
this.fire(this.lastTime, duration);
|
|
358
381
|
for (const emitter of this.emitters) {
|
|
359
|
-
emitter.
|
|
360
|
-
|
|
382
|
+
emitter.loopTimeline();
|
|
383
|
+
}
|
|
384
|
+
for (const tracker of this.meshMotionTrackers) {
|
|
385
|
+
tracker.reset();
|
|
361
386
|
}
|
|
362
387
|
now = now % duration;
|
|
363
388
|
this.lastTime = 0;
|
|
364
389
|
this.lastFrame = -1;
|
|
365
390
|
}
|
|
391
|
+
// Pose and values first, events second: a burst spawns from the emitter's pose and parameters
|
|
392
|
+
// at the moment it is called, so this frame's bursts must see this frame's pose - not the one
|
|
393
|
+
// the previous frame left behind.
|
|
394
|
+
this.poseAt(now);
|
|
366
395
|
this.fire(this.lastTime, now);
|
|
367
396
|
this.lastTime = now;
|
|
368
397
|
this.time = now;
|
|
369
|
-
|
|
398
|
+
}
|
|
399
|
+
/** Poses every entity for the frame `time` falls in, unless it already is. */
|
|
400
|
+
poseAt(time) {
|
|
401
|
+
const frame = frameOfTime(time, this.specification.fps);
|
|
370
402
|
if (frame !== this.lastFrame) {
|
|
371
403
|
this.lastFrame = frame;
|
|
372
404
|
this.driveFrame(timeOfFrame(frame, this.specification.fps));
|
|
@@ -415,16 +447,17 @@ export class FXEffect extends Group {
|
|
|
415
447
|
}
|
|
416
448
|
driveEmitterValues(index, values) {
|
|
417
449
|
const specification = this.specification.emitters[index];
|
|
450
|
+
const render = this.emitterRenders[index];
|
|
418
451
|
const driven = this.emitterDriven[index];
|
|
419
452
|
const uniforms = {};
|
|
420
453
|
const bindings = {};
|
|
421
454
|
for (const [name, value] of values) {
|
|
422
|
-
writeParameterSlots(
|
|
455
|
+
writeParameterSlots(render, specification.behavior, name, value, uniforms, bindings);
|
|
423
456
|
driven.add(name);
|
|
424
457
|
}
|
|
425
458
|
for (const name of [...driven]) {
|
|
426
459
|
if (!values.has(name)) {
|
|
427
|
-
writeParameterBaseline(
|
|
460
|
+
writeParameterBaseline(render, specification.behavior, name, uniforms, bindings);
|
|
428
461
|
driven.delete(name);
|
|
429
462
|
}
|
|
430
463
|
}
|
|
@@ -433,17 +466,17 @@ export class FXEffect extends Group {
|
|
|
433
466
|
}
|
|
434
467
|
}
|
|
435
468
|
driveMeshValues(index, values) {
|
|
436
|
-
const
|
|
469
|
+
const render = this.meshRenders[index];
|
|
437
470
|
const driven = this.meshDriven[index];
|
|
438
471
|
const uniforms = {};
|
|
439
472
|
const bindings = {};
|
|
440
473
|
for (const [name, value] of values) {
|
|
441
|
-
writeParameterSlots(
|
|
474
|
+
writeParameterSlots(render, NO_BEHAVIOR_BINDINGS, name, value, uniforms, bindings);
|
|
442
475
|
driven.add(name);
|
|
443
476
|
}
|
|
444
477
|
for (const name of [...driven]) {
|
|
445
478
|
if (!values.has(name)) {
|
|
446
|
-
writeParameterBaseline(
|
|
479
|
+
writeParameterBaseline(render, NO_BEHAVIOR_BINDINGS, name, uniforms, bindings);
|
|
447
480
|
driven.delete(name);
|
|
448
481
|
}
|
|
449
482
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Camera } from "three";
|
|
2
|
-
import type { FXBehaviorArtifact, FXGeometrySource,
|
|
1
|
+
import type { Camera, WebGLRenderer } from "three";
|
|
2
|
+
import type { FXBehaviorArtifact, FXGeometrySource, FXParticleKernelArtifact, FXRenderArtifactsByGLSLTier } from "../artifact/FXArtifact.js";
|
|
3
3
|
import type { FXWorld } from "../world/FXWorld.js";
|
|
4
4
|
/**
|
|
5
5
|
* Data shapes for {@link FXEffect}: a whole exported project (every emitter + VFX mesh, its
|
|
@@ -48,8 +48,19 @@ export type FXEffectEvent = {
|
|
|
48
48
|
export interface FXEffectEmitterSpec {
|
|
49
49
|
/** The editor-authored name, addressing this emitter through {@link FXEffect.getEmitter} etc. */
|
|
50
50
|
readonly name: string;
|
|
51
|
-
readonly render:
|
|
51
|
+
readonly render: FXRenderArtifactsByGLSLTier;
|
|
52
52
|
readonly behavior: FXBehaviorArtifact;
|
|
53
|
+
/**
|
|
54
|
+
* The standard-tier (WebGL2, transform-feedback) behavior artifact - present only when this
|
|
55
|
+
* emitter's spawn node had "Try GPU simulation" on and the graph compiled to GLSL. `behavior`
|
|
56
|
+
* above is always present regardless - the mandatory JS fallback, including for the
|
|
57
|
+
* WebGL2->WebGL1 emergency downgrade - so a spec is never left with only this field and no JS
|
|
58
|
+
* twin. {@link FXEffect} passes this to
|
|
59
|
+
* `FXEmitter.fromArtifacts` only when the live renderer actually negotiated WebGL2 (the same
|
|
60
|
+
* `isWebGL2` signal that already picks `render`'s "standard" tier); the runtime decides for
|
|
61
|
+
* itself whether GPU setup then actually succeeds, falling back to `behavior` in place if not.
|
|
62
|
+
*/
|
|
63
|
+
readonly gpuBehavior?: FXParticleKernelArtifact;
|
|
53
64
|
readonly expectedCapacity: number;
|
|
54
65
|
readonly sortInterval: number;
|
|
55
66
|
/** Casts a shape-aware shadow; omitted (treated as false) by pre-shadow exports. */
|
|
@@ -72,7 +83,7 @@ export interface FXEffectEmitterSpec {
|
|
|
72
83
|
export interface FXEffectMeshSpec {
|
|
73
84
|
/** The editor-authored name, addressing this mesh through {@link FXEffect.getMesh} etc. */
|
|
74
85
|
readonly name: string;
|
|
75
|
-
readonly render:
|
|
86
|
+
readonly render: FXRenderArtifactsByGLSLTier;
|
|
76
87
|
readonly geometry: FXGeometrySource;
|
|
77
88
|
/** See {@link FXEffectEmitterSpec.castShadow}. */
|
|
78
89
|
readonly castShadow?: boolean;
|
|
@@ -112,4 +123,10 @@ export interface FXEffectOptions {
|
|
|
112
123
|
* own clock and particle pool (an independent time scale, or a group you tick separately).
|
|
113
124
|
*/
|
|
114
125
|
readonly world?: FXWorld;
|
|
126
|
+
/**
|
|
127
|
+
* The renderer this effect will draw through: `renderer.capabilities.isWebGL2` picks each
|
|
128
|
+
* emitter/mesh's `"standard"` or `"baseline"` artifact (see {@link FXRenderArtifactsByGLSLTier}).
|
|
129
|
+
* Omit to get the `"baseline"` artifact unconditionally (the pre-split behavior).
|
|
130
|
+
*/
|
|
131
|
+
readonly renderer?: WebGLRenderer;
|
|
115
132
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BufferGeometry, Texture } from "three";
|
|
2
|
+
/**
|
|
3
|
+
* Options for {@link FXEmitter.applyRenderArtifact}: the render-only subset of
|
|
4
|
+
* {@link FXFromArtifactsOptions} - no `expectedCapacity`/`capacityStep`/`gpuKernel`/`renderer`,
|
|
5
|
+
* since none of those can change without a full {@link FXEmitter.fromArtifacts} rebuild. The app
|
|
6
|
+
* owns `textures`/`geometries`' lifecycle; the runtime never disposes them.
|
|
7
|
+
*/
|
|
8
|
+
export interface FXApplyRenderArtifactOptions {
|
|
9
|
+
readonly textures?: Record<string, Texture>;
|
|
10
|
+
readonly geometries?: Record<string, BufferGeometry>;
|
|
11
|
+
/** default false */
|
|
12
|
+
readonly receiveShadow?: boolean;
|
|
13
|
+
/** Casts a shape-aware shadow (a customDepthMaterial built from the new render artifact). default false */
|
|
14
|
+
readonly castShadow?: boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Camera } from "three";
|
|
2
2
|
import { Object3D } from "three";
|
|
3
3
|
import type { FXBehaviorArtifact, FXRenderArtifact } from "../artifact/FXArtifact.js";
|
|
4
|
+
import type { FXApplyRenderArtifactOptions } from "./FXApplyRenderArtifactOptions.js";
|
|
4
5
|
import type { FXFromArtifactsOptions } from "./FXFromArtifactsOptions.js";
|
|
5
6
|
import { FXWorld } from "../world/FXWorld.js";
|
|
6
7
|
import type { FXApplyValues, FXEmitterBurstOptions, FXEmitterPlayOptions } from "./FXEmitter.Internal.js";
|
|
@@ -10,6 +11,12 @@ import type { FXApplyValues, FXEmitterBurstOptions, FXEmitterPlayOptions } from
|
|
|
10
11
|
* Three material. Built and ticked by {@link FXEffect} through an {@link FXWorld}. Not exported from
|
|
11
12
|
* the package (the runtime path is running artifacts, not hand-building emitters); reached only as
|
|
12
13
|
* the live handle returned by {@link FXEffect.getEmitter}.
|
|
14
|
+
*
|
|
15
|
+
* A thin facade over one {@link FXEmitterDriver} (JS or GPU - see `FXEmitterDriver.Internal.ts`),
|
|
16
|
+
* chosen once by {@link fromArtifacts} and never switched mid-playback. This class itself owns only
|
|
17
|
+
* what is genuinely backend-independent: play/burst/stop/prewarm scheduling, sort-fraction timing,
|
|
18
|
+
* object motion tracking, and the render material - the material itself CAN be replaced in place,
|
|
19
|
+
* by {@link applyRenderArtifact}, without touching the driver or its particle state.
|
|
13
20
|
*/
|
|
14
21
|
export declare class FXEmitter extends Object3D {
|
|
15
22
|
/** Camera for back-to-front depth sorting; `undefined` disables it. Distances are world-space, so any emitter/ancestor transform is handled correctly. */
|
|
@@ -17,10 +24,8 @@ export declare class FXEmitter extends Object3D {
|
|
|
17
24
|
/** Fraction of frames sorting runs on (`1` = every frame, `0.1` = ~every 10th); defaults to `0.1`. */
|
|
18
25
|
sortFraction: number;
|
|
19
26
|
private readonly world;
|
|
20
|
-
private readonly
|
|
21
|
-
private
|
|
22
|
-
private readonly simulation?;
|
|
23
|
-
private readonly collectedProperties;
|
|
27
|
+
private readonly driver;
|
|
28
|
+
private artifactMaterial;
|
|
24
29
|
private nextHandler;
|
|
25
30
|
private readonly pendingBursts;
|
|
26
31
|
private readonly activePlays;
|
|
@@ -32,10 +37,13 @@ export declare class FXEmitter extends Object3D {
|
|
|
32
37
|
private readonly motionScaleScratch;
|
|
33
38
|
private readonly motionTracker;
|
|
34
39
|
private destroyed;
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
/** Exact live count for a JS-driven emitter; for a GPU-driven one, there is no CPU-visible live
|
|
41
|
+
* count to report - `capacity` is the honest upper bound reported instead. */
|
|
37
42
|
get particleCount(): number;
|
|
43
|
+
/** The buffer's current allocated size - grows over time for a JS-driven emitter, fixed at
|
|
44
|
+
* construction for a GPU-driven one (no capacityStep growth path there). */
|
|
38
45
|
get particleCapacity(): number;
|
|
46
|
+
private get mesh();
|
|
39
47
|
/**
|
|
40
48
|
* Builds an emitter into `world` from a precompiled effect - the low-level building block behind
|
|
41
49
|
* {@link FXEffect}. Exposed for editor tooling through the `sparcoon/editor` entry (live preview
|
|
@@ -46,7 +54,10 @@ export declare class FXEmitter extends Object3D {
|
|
|
46
54
|
/** Unregisters, removes from the scene, disposes mesh and material. Idempotent. */
|
|
47
55
|
destroy(): void;
|
|
48
56
|
/**
|
|
49
|
-
* Spawns `count` particles immediately, or after `options.delay`.
|
|
57
|
+
* Spawns `count` particles immediately, or after `options.delay`. An immediate burst seeds its
|
|
58
|
+
* particles from this emitter's pose and parameter values at the moment of the call, on either
|
|
59
|
+
* backend - so moving the emitter between two bursts of one frame spawns them in two places.
|
|
60
|
+
* A delayed burst spawns from the pose and values of the tick it expires on.
|
|
50
61
|
* @returns Handler for {@link stop} to cancel a pending delayed burst.
|
|
51
62
|
*/
|
|
52
63
|
burst(count: number, options?: Partial<FXEmitterBurstOptions>): number;
|
|
@@ -65,8 +76,22 @@ export declare class FXEmitter extends Object3D {
|
|
|
65
76
|
* @param stepDuration - Maximum step size in seconds. Defaults to `1/60`.
|
|
66
77
|
*/
|
|
67
78
|
prewarm(duration: number, stepDuration?: number): void;
|
|
68
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Kills all live particles and cancels all active plays and pending bursts. For a GPU-driven
|
|
81
|
+
* emitter this is a known-narrower reset (see the mesh's own `drop()` doc comment): there is no
|
|
82
|
+
* CPU-visible per-particle state to instantly zero, so already-alive particles keep aging out
|
|
83
|
+
* naturally rather than vanishing on this call's own frame - only the spawn cursor and pending
|
|
84
|
+
* schedule reset immediately.
|
|
85
|
+
*/
|
|
69
86
|
reset(): void;
|
|
87
|
+
/**
|
|
88
|
+
* For a timeline that loops back to its start - {@link FXEffect}'s own, or a tool's that drives
|
|
89
|
+
* the emitter itself: cancels the active plays and pending bursts, as {@link stop} does, and
|
|
90
|
+
* forgets the motion baseline - the pose jumps from the lap's end to its start, which is no
|
|
91
|
+
* motion - but leaves live particles to finish their lives, so the loop runs on seamlessly.
|
|
92
|
+
* Unlike {@link reset}, which clears them.
|
|
93
|
+
*/
|
|
94
|
+
loopTimeline(): void;
|
|
70
95
|
/**
|
|
71
96
|
* Scrubs live parameter values into uniform/binding slots by name, picked up next frame - the
|
|
72
97
|
* runtime's value channel for a non-structural edit. Unknown name = safe no-op; never recompiles.
|
|
@@ -74,16 +99,21 @@ export declare class FXEmitter extends Object3D {
|
|
|
74
99
|
*/
|
|
75
100
|
applyValues(values: FXApplyValues): void;
|
|
76
101
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
102
|
+
* Swaps only this emitter's render half in place - a new shader, geometry primitive/mesh, or
|
|
103
|
+
* shadow flags - leaving the behavior driver, particle buffers, instance count, and playback
|
|
104
|
+
* schedule completely untouched (unlike a structural edit that reaches {@link fromArtifacts},
|
|
105
|
+
* this never resets a running effect). The editor calls this instead of rebuilding whenever a
|
|
106
|
+
* graph edit only changed the render half.
|
|
107
|
+
*
|
|
108
|
+
* @throws If the new render artifact's attribute reads disagree with this emitter's already-
|
|
109
|
+
* allocated buffers (a genuine layout change - see {@link FXEmitterDriver.applyRenderArtifact}).
|
|
110
|
+
* This emitter is left exactly as it was before the call in that case; the caller must fall back
|
|
111
|
+
* to a fresh {@link fromArtifacts} instead, since only a new driver can safely resize buffers.
|
|
79
112
|
*/
|
|
80
|
-
|
|
81
|
-
private buildCollectedProperties;
|
|
113
|
+
applyRenderArtifact(render: FXRenderArtifact, options?: FXApplyRenderArtifactOptions): void;
|
|
82
114
|
private setSortFraction;
|
|
83
115
|
private assertCanSpawn;
|
|
84
|
-
private kernelBuffers;
|
|
85
116
|
private worldTransform;
|
|
86
|
-
private markBuffersNeedUpdate;
|
|
87
117
|
private tick;
|
|
88
118
|
private readonly onRendering;
|
|
89
119
|
private spawnBurst;
|