babylon.quarks 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1194 -8
- package/README.md +13 -9
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.cts +10108 -0
- package/dist/types/index.d.ts +5794 -998
- package/package.json +18 -7
- package/src/BatchedRenderer.ts +274 -156
- package/src/EffectPool.ts +94 -12
- package/src/MeshSurfaceEmitter.ts +50 -14
- package/src/ParticleEmitter.ts +75 -17
- package/src/ParticleSystem.ts +981 -2066
- package/src/QuarksLoader.ts +666 -194
- package/src/QuarksPrefab.ts +367 -73
- package/src/QuarksSerializer.ts +512 -0
- package/src/QuarksUtil.ts +77 -7
- package/src/SpriteBatch.ts +127 -252
- package/src/TrailBatch.ts +545 -348
- package/src/VFXBatch.ts +238 -596
- package/src/batchKey.ts +239 -0
- package/src/batchSettings.ts +546 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +76 -0
- package/src/envAtlas.ts +126 -18
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +69 -35
- package/src/nodeId.ts +36 -0
- package/src/shaderProgram.ts +419 -0
- package/src/shaderVariants.ts +245 -0
- package/src/shaders/chunks/blend.ts +31 -0
- package/src/shaders/chunks/fog.ts +12 -5
- package/src/shaders/chunks/sheet.ts +111 -0
- package/src/shaders/chunks/soft.ts +85 -0
- package/src/shaders/chunks/surface.ts +12 -2
- package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
- package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
- package/src/shaders/particle_frag.glsl.ts +5 -22
- package/src/shaders/particle_frag.wgsl.ts +5 -23
- package/src/shaders/particle_physics_frag.glsl.ts +16 -26
- package/src/shaders/particle_physics_frag.wgsl.ts +14 -26
- package/src/shaders/particle_vert.glsl.ts +5 -27
- package/src/shaders/particle_vert.wgsl.ts +5 -26
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
- package/src/shaders/trail_frag.glsl.ts +13 -0
- package/src/shaders/trail_frag.wgsl.ts +13 -0
- package/src/shaders/trail_vert.glsl.ts +45 -36
- package/src/shaders/trail_vert.wgsl.ts +46 -40
- package/src/softDepth.ts +136 -0
- package/src/system/BehaviorPlan.ts +85 -0
- package/src/system/BoundsTracker.ts +181 -0
- package/src/system/EmissionController.ts +358 -0
- package/src/system/EventDispatcher.ts +60 -0
- package/src/system/ParticleSpawner.ts +274 -0
- package/src/system/SimulationClock.ts +130 -0
- package/src/system/geometry.ts +104 -0
- package/src/system/integrate.ts +428 -0
- package/src/system/materialResolve.ts +165 -0
- package/src/system/normalize.ts +100 -0
- package/src/system/serialization.ts +422 -0
- package/src/system/settingsSchema.ts +339 -0
- package/src/system/types.ts +449 -0
- package/src/textureSource.ts +30 -0
- package/src/trailSettings.ts +54 -0
- package/src/BatchedParticleRenderer.ts +0 -3
- package/src/materials/ParticleMaterials.ts +0 -23
- package/src/materials/index.ts +0 -1
- package/src/shaders/chunks/index.ts +0 -1
- package/src/shaders/chunks/register-shader-chunks.ts +0 -32
- package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
- package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
- package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
- package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
- package/src/shaders/index.ts +0 -9
- package/src/shaders/local_particle_vert.glsl.ts +0 -50
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import {Constants} from '@babylonjs/core/Engines/constants';
|
|
2
|
+
import type {ColorGeneratorJSON, RotationGeneratorJSON, ValueGeneratorJSON, Vector3GeneratorJSON} from 'quarks.core';
|
|
3
|
+
import {
|
|
4
|
+
ColorGenerator,
|
|
5
|
+
ColorGeneratorFromJSON,
|
|
6
|
+
ConstantColor,
|
|
7
|
+
ConstantValue,
|
|
8
|
+
FunctionColorGenerator,
|
|
9
|
+
FunctionValueGenerator,
|
|
10
|
+
GeneratorFromJSON,
|
|
11
|
+
readFormatPart,
|
|
12
|
+
RotationGenerator,
|
|
13
|
+
ValueGenerator,
|
|
14
|
+
ValueGeneratorFromJSON,
|
|
15
|
+
Vector2,
|
|
16
|
+
Vector3,
|
|
17
|
+
Vector3Generator,
|
|
18
|
+
Vector4,
|
|
19
|
+
} from 'quarks.core';
|
|
20
|
+
import type {ParticleSystem} from '../ParticleSystem';
|
|
21
|
+
import {RenderMode} from '../VFXBatch';
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_SOFT_FAR_FADE,
|
|
24
|
+
normalizeBursts,
|
|
25
|
+
validCullingMode,
|
|
26
|
+
validRenderFace,
|
|
27
|
+
validSortMode,
|
|
28
|
+
validTileCount,
|
|
29
|
+
} from './normalize';
|
|
30
|
+
import type {
|
|
31
|
+
BurstParameters,
|
|
32
|
+
BurstParametersJSON,
|
|
33
|
+
ParticleSystemJSONParameters,
|
|
34
|
+
ParticleSystemParameters,
|
|
35
|
+
} from './types';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One setting of a {@link ParticleSystem} that is a plain value or a generator: how to read it
|
|
39
|
+
* from a system, what its default is, and how it goes to and from JSON and into a copy. The
|
|
40
|
+
* table below drives `toJSON` (which writes only values that differ from the default),
|
|
41
|
+
* `fromJSON` and `clone`, so a setting cannot be added to one and forgotten in another.
|
|
42
|
+
*
|
|
43
|
+
* Settings that live in the document's meta (geometry, material, texture and the texture
|
|
44
|
+
* effects), the render mode's own settings, the shape and the behaviors are handled by the
|
|
45
|
+
* callers, since they need the meta or the system itself.
|
|
46
|
+
*/
|
|
47
|
+
export interface SettingDescriptor {
|
|
48
|
+
/** The constructor parameter. */
|
|
49
|
+
readonly param: keyof ParticleSystemParameters;
|
|
50
|
+
/** The JSON field, when it is named differently from the parameter. */
|
|
51
|
+
readonly json?: keyof ParticleSystemJSONParameters;
|
|
52
|
+
/** The current value on a system. */
|
|
53
|
+
get(system: ParticleSystem): unknown;
|
|
54
|
+
/** Whether `value` is what the constructor gives a system that leaves the setting out. */
|
|
55
|
+
isDefault(value: unknown): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Written even when it is the default, because the loaders of releases up to 0.27 fail on a
|
|
58
|
+
* file without it.
|
|
59
|
+
*/
|
|
60
|
+
readonly always?: boolean;
|
|
61
|
+
/** The value as JSON. */
|
|
62
|
+
toJSON(value: unknown): unknown;
|
|
63
|
+
/** The constructor parameter for a JSON value that is present; undefined takes the default. */
|
|
64
|
+
fromJSON(json: unknown): unknown;
|
|
65
|
+
/** A copy that shares no mutable state with `value`. */
|
|
66
|
+
clone(value: unknown): unknown;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type Generator = {toJSON(): unknown; clone(): unknown};
|
|
70
|
+
|
|
71
|
+
/** A burst as files hold it, any field left out. */
|
|
72
|
+
type BurstJSON = Partial<BurstParametersJSON>;
|
|
73
|
+
|
|
74
|
+
/** A number, boolean or string compared by value. */
|
|
75
|
+
function plain<T>(
|
|
76
|
+
param: keyof ParticleSystemParameters,
|
|
77
|
+
get: (system: ParticleSystem) => T,
|
|
78
|
+
defaultValue: T,
|
|
79
|
+
read: (json: unknown) => T | undefined,
|
|
80
|
+
options: {json?: keyof ParticleSystemJSONParameters} = {}
|
|
81
|
+
): SettingDescriptor {
|
|
82
|
+
return {
|
|
83
|
+
param,
|
|
84
|
+
json: options.json,
|
|
85
|
+
get,
|
|
86
|
+
isDefault: (value) => value === defaultValue,
|
|
87
|
+
toJSON: (value) => value,
|
|
88
|
+
fromJSON: read,
|
|
89
|
+
clone: (value) => value,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function number(json: unknown): number | undefined {
|
|
94
|
+
return typeof json === 'number' ? json : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A flipbook count: a whole number of at least 1, see {@link validTileCount}. */
|
|
98
|
+
function tileCount(json: unknown): number | undefined {
|
|
99
|
+
return typeof json === 'number' ? validTileCount(json) : undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** A soft fade distance: finite and at least 0, as `validSoftFade`; an invalid one takes the default. */
|
|
103
|
+
function softFade(json: unknown): number | undefined {
|
|
104
|
+
return typeof json === 'number' && Number.isFinite(json) && json >= 0 ? json : undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A boolean; a number counts as true unless it is 0, as files with 0/1 flags meant it. */
|
|
108
|
+
function boolean(json: unknown): boolean | undefined {
|
|
109
|
+
if (typeof json === 'number') return json !== 0;
|
|
110
|
+
return typeof json === 'boolean' ? json : undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A generator, compared with its default through its JSON. `read` turns the JSON back into one;
|
|
115
|
+
* a bare number is read as a constant.
|
|
116
|
+
*/
|
|
117
|
+
function generator<J>(
|
|
118
|
+
param: keyof ParticleSystemParameters,
|
|
119
|
+
get: (system: ParticleSystem) => Generator,
|
|
120
|
+
defaultValue: () => Generator,
|
|
121
|
+
read: (json: J) => unknown,
|
|
122
|
+
always = true
|
|
123
|
+
): SettingDescriptor {
|
|
124
|
+
const defaultJSON = JSON.stringify(defaultValue().toJSON());
|
|
125
|
+
return {
|
|
126
|
+
param,
|
|
127
|
+
always,
|
|
128
|
+
get,
|
|
129
|
+
isDefault: (value) => JSON.stringify((value as Generator).toJSON()) === defaultJSON,
|
|
130
|
+
toJSON: (value) => (value as Generator).toJSON(),
|
|
131
|
+
// The generator readers check the JSON they are given, and warn about what they cannot read.
|
|
132
|
+
fromJSON: (json) => (typeof json === 'number' ? new ConstantValue(json) : read(json as J)),
|
|
133
|
+
clone: (value) => (value as Generator).clone(),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function vectorJSON(value: unknown): number[] | undefined {
|
|
138
|
+
const v = value as {x: number; y: number; z?: number} | undefined;
|
|
139
|
+
if (v === undefined) return undefined;
|
|
140
|
+
return v.z === undefined ? [v.x, v.y] : [v.x, v.y, v.z];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Every plain and generator setting, in the order `toJSON` writes them. */
|
|
144
|
+
export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
|
|
145
|
+
plain('autoDestroy', (s) => s.autoDestroy, false, boolean),
|
|
146
|
+
plain('looping', (s) => s.looping, true, boolean),
|
|
147
|
+
plain('prewarm', (s) => s.prewarm, false, boolean),
|
|
148
|
+
plain('prewarmTime', (s) => s.prewarmTime, 0, number),
|
|
149
|
+
plain('duration', (s) => s.duration, 1, number),
|
|
150
|
+
generator(
|
|
151
|
+
'startDelay',
|
|
152
|
+
(s) => s.startDelay,
|
|
153
|
+
() => new ConstantValue(0),
|
|
154
|
+
ValueGeneratorFromJSON,
|
|
155
|
+
false
|
|
156
|
+
),
|
|
157
|
+
generator(
|
|
158
|
+
'startLife',
|
|
159
|
+
(s) => s.startLife,
|
|
160
|
+
() => new ConstantValue(5),
|
|
161
|
+
ValueGeneratorFromJSON
|
|
162
|
+
),
|
|
163
|
+
generator(
|
|
164
|
+
'startSpeed',
|
|
165
|
+
(s) => s.startSpeed,
|
|
166
|
+
() => new ConstantValue(0),
|
|
167
|
+
ValueGeneratorFromJSON
|
|
168
|
+
),
|
|
169
|
+
generator(
|
|
170
|
+
'startRotation',
|
|
171
|
+
(s) => s.startRotation,
|
|
172
|
+
() => new ConstantValue(0),
|
|
173
|
+
(json: ValueGeneratorJSON | RotationGeneratorJSON) =>
|
|
174
|
+
GeneratorFromJSON(json) as RotationGenerator | ValueGenerator | FunctionValueGenerator
|
|
175
|
+
),
|
|
176
|
+
generator(
|
|
177
|
+
'startSize',
|
|
178
|
+
(s) => s.startSize,
|
|
179
|
+
() => new ConstantValue(1),
|
|
180
|
+
(json: ValueGeneratorJSON | Vector3GeneratorJSON) =>
|
|
181
|
+
GeneratorFromJSON(json) as Vector3Generator | ValueGenerator | FunctionValueGenerator
|
|
182
|
+
),
|
|
183
|
+
generator(
|
|
184
|
+
'startColor',
|
|
185
|
+
(s) => s.startColor,
|
|
186
|
+
() => new ConstantColor(new Vector4(1, 1, 1, 1)),
|
|
187
|
+
(json: ColorGeneratorJSON) => ColorGeneratorFromJSON(json) as ColorGenerator | FunctionColorGenerator
|
|
188
|
+
),
|
|
189
|
+
generator(
|
|
190
|
+
'emissionOverTime',
|
|
191
|
+
(s) => s.emissionOverTime,
|
|
192
|
+
() => new ConstantValue(10),
|
|
193
|
+
ValueGeneratorFromJSON
|
|
194
|
+
),
|
|
195
|
+
generator(
|
|
196
|
+
'emissionOverDistance',
|
|
197
|
+
(s) => s.emissionOverDistance,
|
|
198
|
+
() => new ConstantValue(0),
|
|
199
|
+
ValueGeneratorFromJSON
|
|
200
|
+
),
|
|
201
|
+
{
|
|
202
|
+
param: 'emissionBursts',
|
|
203
|
+
get: (s) => s.emissionBursts,
|
|
204
|
+
isDefault: (value) => (value as BurstParameters[]).length === 0,
|
|
205
|
+
toJSON: (value) =>
|
|
206
|
+
(value as BurstParameters[]).map((burst) => ({
|
|
207
|
+
time: burst.time,
|
|
208
|
+
count: burst.count.toJSON(),
|
|
209
|
+
probability: burst.probability,
|
|
210
|
+
interval: burst.interval,
|
|
211
|
+
cycle: burst.cycle,
|
|
212
|
+
})),
|
|
213
|
+
fromJSON: (json) =>
|
|
214
|
+
Array.isArray(json)
|
|
215
|
+
? normalizeBursts(
|
|
216
|
+
json.map((burst: BurstJSON) => ({
|
|
217
|
+
time: burst.time,
|
|
218
|
+
count:
|
|
219
|
+
typeof burst.count === 'number'
|
|
220
|
+
? new ConstantValue(burst.count)
|
|
221
|
+
: ValueGeneratorFromJSON(burst.count),
|
|
222
|
+
probability: burst.probability,
|
|
223
|
+
interval: burst.interval,
|
|
224
|
+
cycle: burst.cycle ?? burst.cycleCount,
|
|
225
|
+
}))
|
|
226
|
+
)
|
|
227
|
+
: undefined,
|
|
228
|
+
clone: (value) => (value as BurstParameters[]).map((burst) => ({...burst, count: burst.count.clone()})),
|
|
229
|
+
},
|
|
230
|
+
plain('maxParticles', (s) => s.maxParticles, undefined, number),
|
|
231
|
+
plain('onlyUsedByOther', (s) => s.onlyUsedByOther, false, boolean),
|
|
232
|
+
plain('renderMode', (s) => s.renderMode, RenderMode.BillBoard, number),
|
|
233
|
+
plain('renderOrder', (s) => s.renderOrder, 0, number),
|
|
234
|
+
plain('renderingGroupId', (s) => s.renderingGroupId, 0, number),
|
|
235
|
+
plain(
|
|
236
|
+
'applyFog',
|
|
237
|
+
(s) => s.applyFog,
|
|
238
|
+
true,
|
|
239
|
+
(json) => json !== false
|
|
240
|
+
),
|
|
241
|
+
plain('playbackSpeed', (s) => s.playbackSpeed, 1, number),
|
|
242
|
+
plain('cullingMode', (s) => s.cullingMode, 'always', validCullingMode),
|
|
243
|
+
plain(
|
|
244
|
+
'scalingMode',
|
|
245
|
+
(s) => s.scalingMode,
|
|
246
|
+
'shape',
|
|
247
|
+
(json) => (json === 'hierarchy' ? 'hierarchy' : 'shape')
|
|
248
|
+
),
|
|
249
|
+
plain('seed', (s) => s.seed, undefined, number),
|
|
250
|
+
plain('sortMode', (s) => s.sortMode, 'none', validSortMode),
|
|
251
|
+
plain('renderFace', (s) => s.renderFace, 'both', validRenderFace),
|
|
252
|
+
plain(
|
|
253
|
+
'alignToDirection',
|
|
254
|
+
(s) => s.alignToDirection,
|
|
255
|
+
false,
|
|
256
|
+
(json) => json === true
|
|
257
|
+
),
|
|
258
|
+
plain('minParticleSize', (s) => s.minParticleSize, 0, number),
|
|
259
|
+
plain('maxParticleSize', (s) => s.maxParticleSize, Infinity, number),
|
|
260
|
+
plain('layerMask', (s) => s.layers.mask, 0x0fffffff, number, {json: 'layers'}),
|
|
261
|
+
generator(
|
|
262
|
+
'startTileIndex',
|
|
263
|
+
(s) => s.startTileIndex,
|
|
264
|
+
() => new ConstantValue(0),
|
|
265
|
+
ValueGeneratorFromJSON
|
|
266
|
+
),
|
|
267
|
+
plain('uTileCount', (s) => s.uTileCount, 1, tileCount),
|
|
268
|
+
plain('vTileCount', (s) => s.vTileCount, 1, tileCount),
|
|
269
|
+
plain('blendTiles', (s) => s.blendTiles, false, boolean),
|
|
270
|
+
plain('softParticles', (s) => s.softParticles, false, boolean),
|
|
271
|
+
plain('softFarFade', (s) => s.softFarFade, DEFAULT_SOFT_FAR_FADE, softFade),
|
|
272
|
+
plain('softNearFade', (s) => s.softNearFade, 0, softFade),
|
|
273
|
+
plain('blendMode', (s) => s.blending, Constants.ALPHA_ADD, number, {json: 'blending'}),
|
|
274
|
+
plain('transparent', (s) => s.transparent, true, boolean),
|
|
275
|
+
plain('depthTest', (s) => s.depthTest, true, boolean),
|
|
276
|
+
plain('depthWrite', (s) => s.depthWrite, false, boolean),
|
|
277
|
+
plain('alphaTest', (s) => s.alphaTest, 0, number),
|
|
278
|
+
plain('worldSpace', (s) => s.worldSpace, false, boolean),
|
|
279
|
+
{
|
|
280
|
+
param: 'pivot',
|
|
281
|
+
get: (s) => s.pivot,
|
|
282
|
+
isDefault: (value) => value === undefined,
|
|
283
|
+
toJSON: vectorJSON,
|
|
284
|
+
fromJSON: (json) => (Array.isArray(json) ? new Vector3(json[0] ?? 0, json[1] ?? 0, json[2] ?? 0) : undefined),
|
|
285
|
+
clone: (value) => (value as Vector3 | undefined)?.clone(),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
param: 'flip',
|
|
289
|
+
get: (s) => s.flip,
|
|
290
|
+
isDefault: (value) => value === undefined,
|
|
291
|
+
toJSON: vectorJSON,
|
|
292
|
+
fromJSON: (json) => (Array.isArray(json) ? new Vector2(json[0] ?? 0, json[1] ?? 0) : undefined),
|
|
293
|
+
clone: (value) => (value as Vector2 | undefined)?.clone(),
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
/** The JSON field a setting is written under. */
|
|
298
|
+
export function settingJSONKey(setting: SettingDescriptor): string {
|
|
299
|
+
return setting.json ?? setting.param;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** The settings of `system` that differ from their defaults, as JSON fields. */
|
|
303
|
+
export function settingsToJSON(system: ParticleSystem): Record<string, unknown> {
|
|
304
|
+
const json: Record<string, unknown> = {};
|
|
305
|
+
for (const setting of SETTINGS) {
|
|
306
|
+
const value = setting.get(system);
|
|
307
|
+
if (setting.always || !setting.isDefault(value)) {
|
|
308
|
+
json[settingJSONKey(setting)] = setting.toJSON(value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return json;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Constructor parameters for the settings a document has; missing ones are left out, so they take the default. */
|
|
315
|
+
export function settingsFromJSON(json: ParticleSystemJSONParameters): Partial<ParticleSystemParameters> {
|
|
316
|
+
const parameters: Record<string, unknown> = {};
|
|
317
|
+
const fields = json as unknown as Record<string, unknown>;
|
|
318
|
+
for (const setting of SETTINGS) {
|
|
319
|
+
const value = fields[settingJSONKey(setting)];
|
|
320
|
+
if (value === undefined || value === null) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const key = settingJSONKey(setting);
|
|
324
|
+
const read = readFormatPart(key, () => setting.fromJSON(value));
|
|
325
|
+
if (read !== undefined) {
|
|
326
|
+
parameters[setting.param] = read;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return parameters as Partial<ParticleSystemParameters>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Constructor parameters that copy every setting of `system`. */
|
|
333
|
+
export function settingsForClone(system: ParticleSystem): Partial<ParticleSystemParameters> {
|
|
334
|
+
const parameters: Record<string, unknown> = {};
|
|
335
|
+
for (const setting of SETTINGS) {
|
|
336
|
+
parameters[setting.param] = setting.clone(setting.get(system));
|
|
337
|
+
}
|
|
338
|
+
return parameters as Partial<ParticleSystemParameters>;
|
|
339
|
+
}
|