phoria2 1.0.0 → 1.1.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 +6 -6
- package/dist/bloom.js +4 -4
- package/dist/bloom.js.map +1 -1
- package/dist/charts.js +377 -377
- package/dist/charts.js.map +1 -1
- package/dist/core.js +1052 -558
- package/dist/core.js.map +1 -1
- package/dist/edit.js +48 -48
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +1 -1
- package/dist/gltf.js +207 -162
- package/dist/gltf.js.map +1 -1
- package/dist/motion.js +27 -27
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +50 -50
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +199 -97
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +16 -21
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +3 -3
- package/dist/tooltip.js.map +1 -1
- package/dist/types/View.d.ts +5 -0
- package/dist/types/geometry/Geometry.d.ts +6 -0
- package/dist/types/material/Material.d.ts +125 -0
- package/dist/types/material/Texture.d.ts +37 -5
- package/dist/types/webgl/WebGLRenderer.d.ts +6 -0
- package/dist/types/webgl/passes.d.ts +37 -13
- package/dist/types/webgl/resources.d.ts +24 -2
- package/dist/types/webgl/shaders.d.ts +4 -2
- package/llms-full.txt +96 -7
- package/package.json +1 -1
- package/schema/scene.schema.json +127 -0
- package/src/View.js +4 -2
- package/src/geometry/Geometry.js +8 -1
- package/src/gltf/load.js +50 -6
- package/src/material/Material.js +104 -19
- package/src/material/Texture.js +116 -11
- package/src/watch.js +19 -0
- package/src/webgl/WebGLRenderer.js +51 -12
- package/src/webgl/passes.js +190 -32
- package/src/webgl/picking.js +0 -1
- package/src/webgl/resources.js +89 -8
- package/src/webgl/shaders.js +350 -48
package/dist/particles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as e, Ct as t, Dt as n, Ft as r, L as i, Ot as a, R as o, Tt as s, _t as c, at as l, w as u } from "./core.js";
|
|
2
2
|
// src/particles/Emitter.js
|
|
3
3
|
var d = [
|
|
4
4
|
"origin",
|
|
@@ -8,7 +8,7 @@ var d = [
|
|
|
8
8
|
"lifetime",
|
|
9
9
|
"color"
|
|
10
10
|
], f = [
|
|
11
|
-
...
|
|
11
|
+
...u.OPTIONS,
|
|
12
12
|
...d,
|
|
13
13
|
"max",
|
|
14
14
|
"rate",
|
|
@@ -26,23 +26,23 @@ var d = [
|
|
|
26
26
|
vertexColors: !0,
|
|
27
27
|
blending: "additive",
|
|
28
28
|
depthWrite: !1
|
|
29
|
-
}, h = /* @__PURE__ */ new Float32Array(16), g = class extends
|
|
29
|
+
}, h = /* @__PURE__ */ new Float32Array(16), g = class extends u {
|
|
30
30
|
static type = "emitter";
|
|
31
31
|
static get OPTIONS() {
|
|
32
32
|
return f;
|
|
33
33
|
}
|
|
34
34
|
static get HINTS() {
|
|
35
35
|
return {
|
|
36
|
-
...
|
|
36
|
+
...u.HINTS,
|
|
37
37
|
...p
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
rate = 100;
|
|
41
|
-
gravity = new
|
|
41
|
+
gravity = new t();
|
|
42
42
|
drag = 0;
|
|
43
|
-
origin = new
|
|
43
|
+
origin = new t();
|
|
44
44
|
originSpread = 0;
|
|
45
|
-
velocity = new
|
|
45
|
+
velocity = new t(0, 1, 0);
|
|
46
46
|
velocitySpread = 0;
|
|
47
47
|
lifetime = [1, 1];
|
|
48
48
|
follow = null;
|
|
@@ -63,32 +63,32 @@ var d = [
|
|
|
63
63
|
#l = 0;
|
|
64
64
|
#u = null;
|
|
65
65
|
#d = {
|
|
66
|
-
position: new
|
|
67
|
-
velocity: new
|
|
66
|
+
position: new t(),
|
|
67
|
+
velocity: new t(),
|
|
68
68
|
age: 0,
|
|
69
69
|
lifetime: 0
|
|
70
70
|
};
|
|
71
71
|
constructor(t = {}) {
|
|
72
|
-
t?.geometry !== void 0 &&
|
|
73
|
-
let
|
|
74
|
-
(!Number.isInteger(
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
let
|
|
72
|
+
t?.geometry !== void 0 && e("Emitter", "an Emitter makes its own geometry - size it with 'max'");
|
|
73
|
+
let n = t?.max ?? 1e3;
|
|
74
|
+
(!Number.isInteger(n) || n < 1) && e("Emitter", `'max' must be a whole number of 1 or more, got ${a(n)}`);
|
|
75
|
+
let r = t?.material;
|
|
76
|
+
r === void 0 || r instanceof i || r && typeof r == "object" && !Array.isArray(r) || e("Emitter", `'material' is a Material or material options such as { size: 0.2 }, got ${a(r)}`);
|
|
77
|
+
let s = r instanceof i ? r : {
|
|
78
78
|
...m,
|
|
79
|
-
...
|
|
80
|
-
},
|
|
81
|
-
positions: new Float32Array(
|
|
82
|
-
colors: new Float32Array(
|
|
79
|
+
...r
|
|
80
|
+
}, c = new o({
|
|
81
|
+
positions: new Float32Array(n * 3),
|
|
82
|
+
colors: new Float32Array(n * 3)
|
|
83
83
|
});
|
|
84
|
-
|
|
84
|
+
c.drawCount = 0, super({
|
|
85
85
|
...t,
|
|
86
|
-
geometry:
|
|
87
|
-
material:
|
|
88
|
-
}), this.#e =
|
|
86
|
+
geometry: c,
|
|
87
|
+
material: s
|
|
88
|
+
}), this.#e = n, this.#r = new Float32Array(n * 3), this.#i = new Float32Array(n), this.#a = new Float32Array(n), this.#c = new Uint8Array(n), t.frustumCulled === void 0 && (this.frustumCulled = !1), this.#v(t), this.#_(t), this.onUpdate((e, t) => this.step(t));
|
|
89
89
|
}
|
|
90
|
-
set(
|
|
91
|
-
let
|
|
90
|
+
set(t) {
|
|
91
|
+
let n = [
|
|
92
92
|
...d,
|
|
93
93
|
"rate",
|
|
94
94
|
"gravity",
|
|
@@ -96,8 +96,8 @@ var d = [
|
|
|
96
96
|
"follow",
|
|
97
97
|
"onParticle"
|
|
98
98
|
];
|
|
99
|
-
for (let
|
|
100
|
-
return this.#v(
|
|
99
|
+
for (let r of Object.keys(t ?? {})) n.includes(r) || e("Emitter", `set() doesn't take '${r}' - it takes ${n.join(", ")}`);
|
|
100
|
+
return this.#v(t), this.#_(t), this;
|
|
101
101
|
}
|
|
102
102
|
get max() {
|
|
103
103
|
return this.#e;
|
|
@@ -111,13 +111,13 @@ var d = [
|
|
|
111
111
|
get color() {
|
|
112
112
|
return this.#o[0].map((e) => [...e]);
|
|
113
113
|
}
|
|
114
|
-
burst(
|
|
115
|
-
for (let
|
|
116
|
-
let { color:
|
|
117
|
-
this.#_(
|
|
118
|
-
let
|
|
119
|
-
for (let
|
|
120
|
-
return this.#_(
|
|
114
|
+
burst(t, n = {}) {
|
|
115
|
+
for (let t of Object.keys(n)) d.includes(t) || e("Emitter", `burst() doesn't take '${t}' - it takes ${d.join(", ")}`);
|
|
116
|
+
let { color: r, ...i } = n, a = this.#y();
|
|
117
|
+
this.#_(i), r !== void 0 && (this.#l = this.#b(y(r)));
|
|
118
|
+
let o = this.origin.clone();
|
|
119
|
+
for (let e = 0; e < t; e++) this.#f(o, o, 0);
|
|
120
|
+
return this.#_(a), this.#l = 0, this.#h(), this;
|
|
121
121
|
}
|
|
122
122
|
clear() {
|
|
123
123
|
return this.#t = 0, this.#n = 0, this.#h(), this;
|
|
@@ -176,7 +176,7 @@ var d = [
|
|
|
176
176
|
velocity: this.velocity.toArray(),
|
|
177
177
|
velocitySpread: typeof this.velocitySpread == "number" ? this.velocitySpread : this.velocitySpread.toArray(),
|
|
178
178
|
lifetime: [...this.lifetime],
|
|
179
|
-
color: this.#o[0].map((e) => new
|
|
179
|
+
color: this.#o[0].map((e) => new c().setSRGB(e[0], e[1], e[2]).toJSON())
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
#f(e, t, n) {
|
|
@@ -211,19 +211,19 @@ var d = [
|
|
|
211
211
|
#g() {
|
|
212
212
|
if (!this.follow) return this.origin;
|
|
213
213
|
let e = this.follow.worldPosition;
|
|
214
|
-
return
|
|
214
|
+
return r(h, this.worldMatrix) ? this.origin.copy(e).applyMatrix4(h) : this.origin;
|
|
215
215
|
}
|
|
216
216
|
#_(e) {
|
|
217
|
-
let
|
|
218
|
-
if (e.origin !== void 0 && this.origin.copy(e.origin,
|
|
219
|
-
let
|
|
220
|
-
typeof
|
|
217
|
+
let n = "Emitter";
|
|
218
|
+
if (e.origin !== void 0 && this.origin.copy(e.origin, n), e.originSpread !== void 0 && (this.originSpread = Math.max(0, s(n, "originSpread", e.originSpread))), e.velocity !== void 0 && this.velocity.copy(e.velocity, n), e.velocitySpread !== void 0) {
|
|
219
|
+
let r = e.velocitySpread;
|
|
220
|
+
typeof r == "number" ? this.velocitySpread = s(n, "velocitySpread", r) : this.velocitySpread instanceof t ? this.velocitySpread.copy(r, n) : this.velocitySpread = t.fromInput(r, n);
|
|
221
221
|
}
|
|
222
222
|
e.lifetime !== void 0 && (this.lifetime = v(e.lifetime)), e.color !== void 0 && (this.#o[0] = y(e.color));
|
|
223
223
|
}
|
|
224
224
|
#v(e) {
|
|
225
225
|
let t = "Emitter";
|
|
226
|
-
e.rate !== void 0 && (this.rate = Math.max(0,
|
|
226
|
+
e.rate !== void 0 && (this.rate = Math.max(0, s(t, "rate", e.rate))), e.gravity !== void 0 && this.gravity.copy(e.gravity, t), e.drag !== void 0 && (this.drag = Math.max(0, s(t, "drag", e.drag))), e.follow !== void 0 && (this.follow = _(e.follow)), e.onParticle !== void 0 && (this.onParticle = e.onParticle);
|
|
227
227
|
}
|
|
228
228
|
#y() {
|
|
229
229
|
return {
|
|
@@ -245,15 +245,15 @@ var d = [
|
|
|
245
245
|
}
|
|
246
246
|
};
|
|
247
247
|
function _(t) {
|
|
248
|
-
return t === null || t instanceof
|
|
248
|
+
return t === null || t instanceof l ? t : e("Emitter", `'follow' must be a node, got ${a(t)}`);
|
|
249
249
|
}
|
|
250
250
|
function v(t) {
|
|
251
251
|
let n = typeof t == "number" ? [t, t] : t;
|
|
252
|
-
return Array.isArray(n) && n.length === 2 && n.every((e) => typeof e == "number" && e > 0) ||
|
|
252
|
+
return Array.isArray(n) && n.length === 2 && n.every((e) => typeof e == "number" && e > 0) || e("Emitter", `'lifetime' must be seconds above 0, or a [min, max] range, got ${a(t)}`), [Math.min(n[0], n[1]), Math.max(n[0], n[1])];
|
|
253
253
|
}
|
|
254
|
-
function y(
|
|
255
|
-
let
|
|
256
|
-
return
|
|
254
|
+
function y(t) {
|
|
255
|
+
let n = Array.isArray(t) && !(t.length === 3 && t.every((e) => typeof e == "number")) ? t : [t];
|
|
256
|
+
return n.length || e("Emitter", "'color' needs at least one colour"), n.map((e) => c.fromInput(e, "Emitter").toSRGB());
|
|
257
257
|
}
|
|
258
258
|
function b(e) {
|
|
259
259
|
if (!e) return [
|
|
@@ -282,14 +282,14 @@ var x = [
|
|
|
282
282
|
}, C = class {
|
|
283
283
|
static OPTIONS = x;
|
|
284
284
|
static HINTS = S;
|
|
285
|
-
velocity = new
|
|
286
|
-
gravity = new
|
|
285
|
+
velocity = new t();
|
|
286
|
+
gravity = new t();
|
|
287
287
|
drag = 0;
|
|
288
288
|
constructor(e = {}) {
|
|
289
|
-
|
|
289
|
+
n("Body", e, x, S), e.velocity !== void 0 && this.velocity.copy(e.velocity, "Body"), e.gravity !== void 0 && this.gravity.copy(e.gravity, "Body"), e.drag !== void 0 && (this.drag = Math.max(0, s("Body", "drag", e.drag)));
|
|
290
290
|
}
|
|
291
291
|
impulse(e) {
|
|
292
|
-
return this.velocity.add(
|
|
292
|
+
return this.velocity.add(t.fromInput(e, "Body")), this;
|
|
293
293
|
}
|
|
294
294
|
update = (e, t) => {
|
|
295
295
|
let n = this.velocity, r = Math.exp(-this.drag * t);
|
package/dist/particles.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"particles.js","names":["OPTIONS","HINTS","mat4.invert","describe","checkOptions","Material","Geometry","invert","Vec3","Color","Node","fail","Points","checkNumber","SPAWN_OPTIONS","OPTIONS$1","HINTS$1","DEFAULT_MATERIAL","tmpMat","Emitter","options","max","given","material","geometry","#max","#velocities","#ages","#lifetimes","#palette","#setBehaviour","#setSpawn","dt","known","key","#alive","#palettes","colorStops","v","s","spawn","saved","#saveSpawn","color","#spawnPalette","#paletteFor","origin","i","count","#spawn","#upload","#due","p","ages","lives","g","keep","hook","particle","#particle","age","#remove","i3","k","#paint","#followedOrigin","from","#lastOrigin","born","n","rest","t","o","randomInBall","dv","a","b","head","min","last","c","stops","x","f","world","owner","lifetimeRange","checkFollow","id","#paletteIds","range","list","r","y","z","OPTIONS","HINTS","Body"],"sources":["../src/particles/Emitter.js","../src/particles/Body.js"],"sourcesContent":["// A particle system as one Points node: particles are born at the emitter's origin, move under\n// velocity, gravity and drag, fade through a list of colours and die. The simulation runs in the\n// emitter's own update handler, so it follows the scene clock (frame-rate independent) and its\n// arrays go to the GPU as one buffer refill per frame.\nimport * as mat4 from '../math/mat4.js'\nimport { Geometry } from '../geometry/Geometry.js'\nimport { Material } from '../material/Material.js'\nimport { Points } from '../scene/drawables.js'\nimport { Node } from '../scene/Node.js'\nimport { Color } from '../value/Color.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, describe, fail } from '../validate.js'\n\n/** @import { MaterialOptions } from '../material/Material.js' */\n/** @import { NodeOptions } from '../scene/Node.js' */\n/** @import { ColorInput, Vec3Input } from '../types.js' */\n\n/**\n * A live particle, as passed to `onParticle`. Change `position` or `velocity` and the changes stick.\n * @typedef {object} Particle\n * @property {Vec3} position in the emitter's space\n * @property {Vec3} velocity units per second\n * @property {number} age seconds since birth\n * @property {number} lifetime seconds it lives for\n */\n\n/**\n * How particles are born. Each can also be passed to burst() for that burst alone.\n * @typedef {object} SpawnOptions\n * @property {Vec3Input} [origin] where particles start, in the emitter's space (default [0, 0, 0])\n * @property {number} [originSpread] start anywhere within this radius of the origin (default 0)\n * @property {Vec3Input} [velocity] starting velocity, units per second (default [0, 1, 0])\n * @property {number | Vec3Input} [velocitySpread] random extra velocity: a number spreads evenly in every\n * direction (a sphere - explosions), [x, y, z] spreads up to ± that much per axis (default 0)\n * @property {number | [number, number]} [lifetime] seconds, or a [min, max] range (default 1)\n * @property {ColorInput | ColorInput[]} [color] a colour, or colours each particle fades through over its\n * life - end on black to fade out with additive blending (default white)\n */\n\n/**\n * @typedef {NodeOptions & SpawnOptions & {\n * max?: number,\n * rate?: number,\n * gravity?: Vec3Input,\n * drag?: number,\n * follow?: Node | null,\n * material?: Material | MaterialOptions,\n * frustumCulled?: boolean,\n * renderOrder?: number,\n * onParticle?: ((p: Particle) => boolean | void) | null,\n * }} EmitterOptions\n * max: most live particles at once (default 1000). rate: particles born per second (default 100).\n * gravity: acceleration, e.g. [0, -9.8, 0] (default none). drag: share of velocity lost per second\n * (default 0). follow: a node whose position becomes the origin each frame - a trail that stays\n * behind it. onParticle: called for every live particle each frame after it moves; return false to\n * remove it (a particle hitting something).\n */\n\nconst SPAWN_OPTIONS = ['origin', 'originSpread', 'velocity', 'velocitySpread', 'lifetime', 'color']\nconst OPTIONS = [...Points.OPTIONS, ...SPAWN_OPTIONS,\n 'max', 'rate', 'gravity', 'drag', 'follow', 'onParticle']\nconst HINTS = {\n count: \"use 'max' for the most particles alive at once, 'rate' for how many are born per second\",\n lifetimeRnd: \"give 'lifetime' a [min, max] range\",\n velocityRnd: \"use 'velocitySpread'\",\n positionRnd: \"use 'originSpread'\",\n}\n// the look most particle effects want: glowing dots that don't hide each other\n/** @type {MaterialOptions} */\nconst DEFAULT_MATERIAL = { size: 0.1, vertexColors: true, blending: 'additive', depthWrite: false }\n\nconst tmpMat = new Float32Array(16)\n\n/**\n * Particles drawn as points. Add it to a scene and it runs itself.\n *\n * const sparks = scene.add(new Emitter({ rate: 200, velocitySpread: 2, gravity: [0, -9.8, 0], color: ['#ffd27f', '#000'] }))\n * sparks.burst(500, { velocitySpread: 8 })\n */\nexport class Emitter extends Points {\n static type = 'emitter'\n static get OPTIONS() { return OPTIONS }\n static get HINTS() { return { ...Points.HINTS, ...HINTS } }\n\n /** Particles born per second; 0 stops new ones (existing particles live out their lives). */\n rate = 100\n /** Acceleration on every particle, units per second². */\n gravity = new Vec3()\n /** Share of velocity lost per second. */\n drag = 0\n /** Where particles start, in the emitter's space. */\n origin = new Vec3()\n originSpread = 0\n velocity = new Vec3(0, 1, 0)\n /** @type {number | Vec3} */ velocitySpread = 0\n /** @type {[number, number]} */ lifetime = [1, 1]\n /** A node whose position becomes the origin each frame. @type {Node | null} */\n follow = null\n /** @type {((p: Particle) => boolean | void) | null} */\n onParticle = null\n\n #max\n #alive = 0\n #due = 0\n /** @type {Float32Array} */ #velocities\n /** @type {Float32Array} */ #ages\n /** @type {Float32Array} */ #lifetimes\n // Colour stop lists, sRGB like geometry colours. Palette 0 is the emitter's own colour; bursts with\n // their own colours add more, and each particle remembers which one it fades through.\n /** @type {number[][][]} */ #palettes = [[[1, 1, 1]]]\n /** @type {Map<string, number>} */ #paletteIds = new Map()\n /** @type {Uint8Array} */ #palette\n #spawnPalette = 0\n /** Last frame's origin, so a moving source leaves an even trail rather than clumps. */\n #lastOrigin = /** @type {Vec3 | null} */ (null)\n #particle = /** @type {Particle} */ ({ position: new Vec3(), velocity: new Vec3(), age: 0, lifetime: 0 })\n\n /** @param {EmitterOptions} [options] */\n constructor(options = {}) {\n if (/** @type {any} */ (options)?.geometry !== undefined) fail('Emitter', \"an Emitter makes its own geometry - size it with 'max'\")\n const max = options?.max ?? 1000\n if (!Number.isInteger(max) || max < 1) fail('Emitter', `'max' must be a whole number of 1 or more, got ${describe(max)}`)\n const given = options?.material\n if (given !== undefined && !(given instanceof Material) && (!given || typeof given !== 'object' || Array.isArray(given))) {\n fail('Emitter', `'material' is a Material or material options such as { size: 0.2 }, got ${describe(given)}`)\n }\n const material = given instanceof Material ? given : { ...DEFAULT_MATERIAL, ...given }\n const geometry = new Geometry({ positions: new Float32Array(max * 3), colors: new Float32Array(max * 3) })\n geometry.drawCount = 0\n super({ ...options, geometry, material })\n this.#max = max\n this.#velocities = new Float32Array(max * 3)\n this.#ages = new Float32Array(max)\n this.#lifetimes = new Float32Array(max)\n this.#palette = new Uint8Array(max)\n // particles spread anywhere, and stale slots would make bounds meaningless\n if (options.frustumCulled === undefined) this.frustumCulled = false\n\n this.#setBehaviour(options)\n this.#setSpawn(options)\n this.onUpdate((_, dt) => this.step(dt))\n }\n\n /**\n * Change several settings at once - a preset, say. Vectors are copied into the emitter's own,\n * so anything bound to `emitter.gravity` and friends stays bound. Live particles carry on.\n * @param {SpawnOptions & { rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null,\n * onParticle?: ((p: Particle) => boolean | void) | null }} options\n * @returns {this}\n */\n set(options) {\n const known = [...SPAWN_OPTIONS, 'rate', 'gravity', 'drag', 'follow', 'onParticle']\n for (const key of Object.keys(options ?? {})) {\n if (!known.includes(key)) fail('Emitter', `set() doesn't take '${key}' - it takes ${known.join(', ')}`)\n }\n this.#setBehaviour(options)\n this.#setSpawn(options)\n return this\n }\n\n /** Most live particles at once. */\n get max() { return this.#max }\n\n /** Particles alive now. */\n get count() { return this.#alive }\n\n /** The colours particles fade through - one or several. @param {ColorInput | ColorInput[]} v */\n set color(v) { this.#palettes[0] = colorStops(v) }\n /** @returns {[number, number, number][]} */\n get color() { return /** @type {[number, number, number][]} */ (this.#palettes[0].map((s) => [...s])) }\n\n /**\n * Birth `count` particles at once - explosions, splashes. Options override the emitter's own\n * spawn settings for this burst only.\n * @param {number} count\n * @param {SpawnOptions} [options]\n * @returns {this}\n */\n burst(count, options = {}) {\n for (const key of Object.keys(options)) {\n if (!SPAWN_OPTIONS.includes(key)) fail('Emitter', `burst() doesn't take '${key}' - it takes ${SPAWN_OPTIONS.join(', ')}`)\n }\n const { color, ...spawn } = options\n const saved = this.#saveSpawn()\n this.#setSpawn(spawn)\n if (color !== undefined) this.#spawnPalette = this.#paletteFor(colorStops(color))\n const origin = this.origin.clone()\n for (let i = 0; i < count; i++) this.#spawn(origin, origin, 0)\n this.#setSpawn(saved)\n this.#spawnPalette = 0\n this.#upload()\n return this\n }\n\n /** Remove every particle. @returns {this} */\n clear() {\n this.#alive = 0\n this.#due = 0\n this.#upload()\n return this\n }\n\n /**\n * Advance the simulation by dt seconds. The emitter's update handler calls this each frame; call\n * it yourself only for an emitter outside a running scene.\n * @param {number} dt\n */\n step(dt) {\n if (dt <= 0) return\n const p = this.geometry.positions\n const v = this.#velocities, ages = this.#ages, lives = this.#lifetimes\n const g = this.gravity, keep = Math.exp(-this.drag * dt)\n const hook = this.onParticle, particle = this.#particle\n\n for (let i = 0; i < this.#alive;) {\n const age = ages[i] += dt\n if (age >= lives[i]) {\n this.#remove(i)\n continue\n }\n const i3 = i * 3\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = (v[i3 + k] + g[k] * dt) * keep\n p[i3 + k] += v[i3 + k] * dt\n }\n this.#paint(i, age / lives[i])\n if (hook) {\n particle.position.set(p[i3], p[i3 + 1], p[i3 + 2])\n particle.velocity.set(v[i3], v[i3 + 1], v[i3 + 2])\n particle.age = age\n particle.lifetime = lives[i]\n if (hook(particle) === false) {\n this.#remove(i)\n continue\n }\n p.set(particle.position, i3)\n v.set(particle.velocity, i3)\n }\n i++\n }\n\n // new particles, spread along the path the origin took this frame\n const origin = this.#followedOrigin()\n const from = this.#lastOrigin ?? origin\n this.#due += this.rate * dt\n const born = Math.floor(this.#due)\n this.#due -= born\n for (let n = 0; n < born; n++) this.#spawn(from, origin, (n + Math.random()) / born * dt)\n this.#lastOrigin = origin.clone()\n this.#upload()\n }\n\n /** @internal @returns {Record<string, unknown>} */\n cloneOptions() {\n // an emitter makes its own geometry, so neither form carries it\n const rest = super.cloneOptions()\n delete rest.geometry\n return { ...rest, ...this.#saveSpawn(), max: this.#max, rate: this.rate, gravity: this.gravity.clone(),\n drag: this.drag, follow: this.follow, onParticle: this.onParticle, color: this.color }\n }\n\n /** @internal @returns {Record<string, unknown>} */\n jsonOptions() {\n const rest = super.jsonOptions()\n delete rest.geometry\n return { ...rest, max: this.#max, rate: this.rate, gravity: this.gravity.toArray(), drag: this.drag,\n origin: this.origin.toArray(), originSpread: this.originSpread, velocity: this.velocity.toArray(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.toArray(),\n lifetime: [...this.lifetime], color: this.#palettes[0].map((s) => new Color().setSRGB(s[0], s[1], s[2]).toJSON()) }\n }\n\n // birth one particle somewhere between a and b, already `head` seconds into its life\n /** @param {Vec3} a @param {Vec3} b @param {number} head */\n #spawn(a, b, head) {\n if (this.#alive >= this.#max) return\n const i = this.#alive++, i3 = i * 3\n const p = this.geometry.positions, v = this.#velocities\n const t = Math.random()\n const o = randomInBall(this.originSpread)\n const s = this.velocitySpread\n const dv = typeof s === 'number' ? randomInBall(s) : [(Math.random() * 2 - 1) * s[0], (Math.random() * 2 - 1) * s[1], (Math.random() * 2 - 1) * s[2]]\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = this.velocity[k] + dv[k]\n p[i3 + k] = a[k] + (b[k] - a[k]) * t + o[k] + v[i3 + k] * head\n }\n const [min, max] = this.lifetime\n this.#lifetimes[i] = min + Math.random() * (max - min)\n this.#ages[i] = head\n this.#palette[i] = this.#spawnPalette\n this.#paint(i, head / this.#lifetimes[i])\n }\n\n // move the last particle into slot i\n /** @param {number} i */\n #remove(i) {\n const last = --this.#alive\n if (i === last) return\n const p = this.geometry.positions, c = /** @type {Float32Array} */ (this.geometry.colors), v = this.#velocities\n p.copyWithin(i * 3, last * 3, last * 3 + 3)\n c.copyWithin(i * 3, last * 3, last * 3 + 3)\n v.copyWithin(i * 3, last * 3, last * 3 + 3)\n this.#ages[i] = this.#ages[last]\n this.#lifetimes[i] = this.#lifetimes[last]\n this.#palette[i] = this.#palette[last]\n }\n\n /** @param {number} i @param {number} t 0-1 through its life */\n #paint(i, t) {\n const c = /** @type {Float32Array} */ (this.geometry.colors), stops = this.#palettes[this.#palette[i]]\n if (stops.length === 1) {\n c.set(stops[0], i * 3)\n return\n }\n const x = Math.min(Math.max(t, 0), 1) * (stops.length - 1)\n const s = Math.min(Math.floor(x), stops.length - 2), f = x - s\n const a = stops[s], b = stops[s + 1]\n c[i * 3] = a[0] + (b[0] - a[0]) * f\n c[i * 3 + 1] = a[1] + (b[1] - a[1]) * f\n c[i * 3 + 2] = a[2] + (b[2] - a[2]) * f\n }\n\n #upload() {\n this.geometry.drawCount = this.#alive\n this.geometry.needsUpdate()\n }\n\n /** The followed node's position in the emitter's space, or the origin. @returns {Vec3} */\n #followedOrigin() {\n if (!this.follow) return this.origin\n const world = this.follow.worldPosition\n if (!mat4.invert(tmpMat, this.worldMatrix)) return this.origin\n return this.origin.copy(world).applyMatrix4(tmpMat)\n }\n\n /** @param {SpawnOptions} o */\n #setSpawn(o) {\n const owner = 'Emitter'\n // vectors are copied in place, so references to them stay live\n if (o.origin !== undefined) this.origin.copy(o.origin, owner)\n if (o.originSpread !== undefined) this.originSpread = Math.max(0, checkNumber(owner, 'originSpread', o.originSpread))\n if (o.velocity !== undefined) this.velocity.copy(o.velocity, owner)\n if (o.velocitySpread !== undefined) {\n const s = o.velocitySpread\n if (typeof s === 'number') this.velocitySpread = checkNumber(owner, 'velocitySpread', s)\n else if (this.velocitySpread instanceof Vec3) this.velocitySpread.copy(s, owner)\n else this.velocitySpread = Vec3.fromInput(s, owner)\n }\n if (o.lifetime !== undefined) this.lifetime = lifetimeRange(o.lifetime)\n if (o.color !== undefined) this.#palettes[0] = colorStops(o.color)\n }\n\n /** @param {{ rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null, onParticle?: ((p: Particle) => boolean | void) | null }} o */\n #setBehaviour(o) {\n const owner = 'Emitter'\n if (o.rate !== undefined) this.rate = Math.max(0, checkNumber(owner, 'rate', o.rate))\n if (o.gravity !== undefined) this.gravity.copy(o.gravity, owner)\n if (o.drag !== undefined) this.drag = Math.max(0, checkNumber(owner, 'drag', o.drag))\n if (o.follow !== undefined) this.follow = checkFollow(o.follow)\n if (o.onParticle !== undefined) this.onParticle = o.onParticle\n }\n\n /** The spawn settings other than colour, as options. @returns {SpawnOptions} */\n #saveSpawn() {\n return { origin: this.origin.clone(), originSpread: this.originSpread, velocity: this.velocity.clone(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.clone(),\n lifetime: /** @type {[number, number]} */ ([...this.lifetime]) }\n }\n\n // The palette id for a burst's colours, reused when the same colours come again. Ids are a byte\n // per particle, so past 255 distinct burst palettes the oldest is overwritten.\n /** @param {number[][]} stops @returns {number} */\n #paletteFor(stops) {\n const key = stops.flat().join()\n let id = this.#paletteIds.get(key)\n if (id === undefined) {\n id = this.#palettes.length < 256 ? this.#palettes.length : 1 + (this.#paletteIds.size % 255)\n for (const [k, v] of this.#paletteIds) if (v === id) this.#paletteIds.delete(k)\n this.#paletteIds.set(key, id)\n }\n this.#palettes[id] = stops\n return id\n }\n}\n\n/** @param {unknown} v @returns {Node | null} */\nfunction checkFollow(v) {\n if (v === null || v instanceof Node) return /** @type {Node | null} */ (v)\n return fail('Emitter', `'follow' must be a node, got ${describe(v)}`)\n}\n\n/** @param {number | [number, number]} v @returns {[number, number]} */\nfunction lifetimeRange(v) {\n const range = typeof v === 'number' ? [v, v] : v\n if (!Array.isArray(range) || range.length !== 2 || !range.every((n) => typeof n === 'number' && n > 0)) {\n fail('Emitter', `'lifetime' must be seconds above 0, or a [min, max] range, got ${describe(v)}`)\n }\n return [Math.min(range[0], range[1]), Math.max(range[0], range[1])]\n}\n\n/** @param {ColorInput | ColorInput[]} v @returns {number[][]} */\nfunction colorStops(v) {\n // a single [r, g, b] array is one colour, not three\n const list = Array.isArray(v) && !(v.length === 3 && v.every((n) => typeof n === 'number')) ? v : [v]\n if (!list.length) fail('Emitter', \"'color' needs at least one colour\")\n return list.map((c) => Color.fromInput(/** @type {ColorInput} */ (c), 'Emitter').toSRGB())\n}\n\n/** A random point within a ball of radius r, evenly spread. @param {number} r @returns {number[]} */\nfunction randomInBall(r) {\n if (!r) return [0, 0, 0]\n let x, y, z\n do {\n x = Math.random() * 2 - 1\n y = Math.random() * 2 - 1\n z = Math.random() * 2 - 1\n } while (x * x + y * y + z * z > 1)\n return [x * r, y * r, z * r]\n}\n","// Simple physics as a behaviour rather than a base class: any node moves with velocity, gravity\n// and drag once its body's update is attached - node.onUpdate(body.update).\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, checkOptions } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * @typedef {object} BodyOptions\n * @property {Vec3Input} [velocity] units per second (default [0, 0, 0])\n * @property {Vec3Input} [gravity] acceleration, e.g. [0, -9.8, 0] (default none)\n * @property {number} [drag] share of velocity lost per second (default 0)\n */\n\n/**\n * Velocity, gravity and drag for a node.\n *\n * const body = new Body({ velocity: [0, 20, 0], gravity: [0, -9.8, 0] })\n * rocket.onUpdate(body.update)\n */\nconst OPTIONS = ['velocity', 'gravity', 'drag']\nconst HINTS = {\n impulse: 'call body.impulse([x, y, z]) after creating it',\n mass: 'a Body has no mass - impulse() changes velocity directly',\n}\n\nexport class Body {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n velocity = new Vec3()\n gravity = new Vec3()\n drag = 0\n\n /** @param {BodyOptions} [options] */\n constructor(options = {}) {\n checkOptions('Body', options, OPTIONS, HINTS)\n if (options.velocity !== undefined) this.velocity.copy(options.velocity, 'Body')\n if (options.gravity !== undefined) this.gravity.copy(options.gravity, 'Body')\n if (options.drag !== undefined) this.drag = Math.max(0, checkNumber('Body', 'drag', options.drag))\n }\n\n /** Add to the velocity at once - a kick. @param {Vec3Input} v @returns {this} */\n impulse(v) {\n this.velocity.add(Vec3.fromInput(v, 'Body'))\n return this\n }\n\n /**\n * Move a node one step. Bound, so it can be handed straight to node.onUpdate().\n * @param {Node} node\n * @param {number} dt seconds\n */\n update = (node, dt) => {\n const v = this.velocity, keep = Math.exp(-this.drag * dt)\n for (let k = 0; k < 3; k++) {\n v[k] = (v[k] + this.gravity[k] * dt) * keep\n node.position[k] += v[k] * dt\n }\n }\n}\n"],"mappings":";;AA0DA,IAAMc,IAAgB;CAAC;CAAU;CAAgB;CAAY;CAAkB;CAAY;AAAO,GAC5Fd,IAAU;CAAC,GAAGY,EAAO;CAAS,GAAGE;CACrC;CAAO;CAAQ;CAAW;CAAQ;CAAU;AAAY,GACpDb,IAAQ;CACZ,OAAO;CACP,aAAa;CACb,aAAa;CACb,aAAa;AACf,GAGMgB,IAAmB;CAAE,MAAM;CAAK,cAAc;CAAM,UAAU;CAAY,YAAY;AAAM,GAE5FC,oBAAS,IAAI,aAAa,EAAE,GAQrBC,IAAb,cAA6BP,EAAO;CAClC,OAAO,OAAO;CACd,WAAW,UAAU;EAAE,OAAOZ;CAAQ;CACtC,WAAW,QAAQ;EAAE,OAAO;GAAE,GAAGY,EAAO;GAAO,GAAGX;EAAM;CAAE;CAG1D,OAAO;CAEP,UAAU,IAAIO,EAAK;CAEnB,OAAO;CAEP,SAAS,IAAIA,EAAK;CAClB,eAAe;CACf,WAAW,IAAIA,EAAK,GAAG,GAAG,CAAC;CACE,iBAAiB;CACd,WAAW,CAAC,GAAG,CAAC;CAEhD,SAAS;CAET,aAAa;CAEb;CACA,KAAS;CACT,KAAO;CACqB;CACA;CACA;CAGA,KAAY,CAAC,CAAC;EAAC;EAAG;EAAG;CAAC,CAAC,CAAC;CACjB,qBAAc,IAAI,IAAI;CAC/B;CAC1B,KAAgB;CAEhB,KAA0C;CAC1C,KAAqC;EAAE,UAAU,IAAIA,EAAK;EAAG,UAAU,IAAIA,EAAK;EAAG,KAAK;EAAG,UAAU;CAAE;CAGvG,YAAY,IAAU,CAAC,GAAG;EACxB,AAAwBY,GAAU,aAAa,KAAA,KAAWT,EAAK,WAAW,wDAAwD;EAClI,IAAMU,IAAMD,GAAS,OAAO;EAC5B,CAAI,CAAC,OAAO,UAAUC,CAAG,KAAKA,IAAM,MAAGV,EAAK,WAAW,kDAAkDR,EAASkB,CAAG,GAAG;EACxH,IAAMC,IAAQF,GAAS;EACvB,AAAIE,MAAU,KAAA,KAAeA,aAAiBjB,KAAeiB,KAAS,OAAOA,KAAU,YAAY,OAAM,QAAQA,CAAK,KACpHX,EAAK,WAAW,2EAA2ER,EAASmB,CAAK,GAAG;EAE9G,IAAMC,IAAWD,aAAiBjB,IAAWiB,IAAQ;GAAE,GAAGL;GAAkB,GAAGK;EAAM,GAC/EE,IAAW,IAAIlB,EAAS;GAAE,WAAW,IAAI,aAAae,IAAM,CAAC;GAAG,QAAQ,IAAI,aAAaA,IAAM,CAAC;EAAE,CAAC;EAazG,AAZA,EAAS,YAAY,GACrB,MAAM;GAAE,GAAGD;GAAS;GAAU;EAAS,CAAC,GACxC,KAAKK,KAAOJ,GACZ,KAAKK,KAAc,IAAI,aAAaL,IAAM,CAAC,GAC3C,KAAKM,KAAQ,IAAI,aAAaN,CAAG,GACjC,KAAKO,KAAa,IAAI,aAAaP,CAAG,GACtC,KAAKQ,KAAW,IAAI,WAAWR,CAAG,GAE9BD,EAAQ,kBAAkB,KAAA,MAAW,KAAK,gBAAgB,KAE9D,KAAKU,GAAcV,CAAO,GAC1B,KAAKW,GAAUX,CAAO,GACtB,KAAK,UAAU,GAAG,MAAO,KAAK,KAAKY,CAAE,CAAC;CACxC;CASA,IAAI,GAAS;EACX,IAAMC,IAAQ;GAAC,GAAGnB;GAAe;GAAQ;GAAW;GAAQ;GAAU;EAAY;EAClF,KAAK,IAAMoB,KAAO,OAAO,KAAKd,KAAW,CAAC,CAAC,GACzC,AAAKa,EAAM,SAASC,CAAG,KAAGvB,EAAK,WAAW,uBAAuBuB,EAAI,eAAeD,EAAM,KAAK,IAAI,GAAG;EAIxG,OAFA,KAAKH,GAAcV,CAAO,GAC1B,KAAKW,GAAUX,CAAO,GACf;CACT;CAGA,IAAI,MAAM;EAAE,OAAO,KAAKK;CAAK;CAG7B,IAAI,QAAQ;EAAE,OAAO,KAAKU;CAAO;CAGjC,IAAI,MAAM,GAAG;EAAE,KAAKC,GAAU,KAAKC,EAAWC,CAAC;CAAE;CAEjD,IAAI,QAAQ;EAAE,OAAkD,KAAKF,GAAU,EAAE,CAAC,KAAK,MAAM,CAAC,GAAGG,CAAC,CAAC;CAAG;CAStG,MAAM,GAAO,IAAU,CAAC,GAAG;EACzB,KAAK,IAAML,KAAO,OAAO,KAAKd,CAAO,GACnC,AAAKN,EAAc,SAASoB,CAAG,KAAGvB,EAAK,WAAW,yBAAyBuB,EAAI,eAAepB,EAAc,KAAK,IAAI,GAAG;EAE1H,IAAM,EAAE,UAAO,GAAG0B,MAAUpB,GACtBqB,IAAQ,KAAKC,GAAW;EAE9B,AADA,KAAKX,GAAUS,CAAK,GAChBG,MAAU,KAAA,MAAW,KAAKC,KAAgB,KAAKC,GAAYR,EAAWM,CAAK,CAAC;EAChF,IAAMG,IAAS,KAAK,OAAO,MAAM;EACjC,KAAK,IAAIC,IAAI,GAAGA,IAAIC,GAAO,KAAK,KAAKC,GAAOH,GAAQA,GAAQ,CAAC;EAI7D,OAHA,KAAKf,GAAUU,CAAK,GACpB,KAAKG,KAAgB,GACrB,KAAKM,GAAQ,GACN;CACT;CAGA,QAAQ;EAIN,OAHA,KAAKf,KAAS,GACd,KAAKgB,KAAO,GACZ,KAAKD,GAAQ,GACN;CACT;CAOA,KAAK,GAAI;EACP,IAAIlB,KAAM,GAAG;EACb,IAAMoB,IAAI,KAAK,SAAS,WAClBd,IAAI,KAAKZ,IAAa2B,IAAO,KAAK1B,IAAO2B,IAAQ,KAAK1B,IACtD2B,IAAI,KAAK,SAASC,IAAO,KAAK,IAAI,CAAC,KAAK,OAAOxB,CAAE,GACjDyB,IAAO,KAAK,YAAYC,IAAW,KAAKC;EAE9C,KAAK,IAAIZ,IAAI,GAAGA,IAAI,KAAKZ,KAAS;GAChC,IAAMyB,IAAM,EAAKb,MAAMf;GACvB,IAAI4B,KAAON,EAAMP,IAAI;IACnB,KAAKc,GAAQd,CAAC;IACd;GACF;GACA,IAAMe,IAAKf,IAAI;GACf,KAAK,IAAIgB,IAAI,GAAGA,IAAI,GAAG,KAErB,AADA,EAAED,IAAKC,MAAMzB,EAAEwB,IAAKC,KAAKR,EAAEQ,KAAK/B,KAAMwB,GACtC,EAAEM,IAAKC,MAAMzB,EAAEwB,IAAKC,KAAK/B;GAG3B,IADA,KAAKgC,GAAOjB,GAAGa,IAAMN,EAAMP,EAAE,GACzBU,GAAM;IAKR,IAJAC,EAAS,SAAS,IAAIN,EAAEU,IAAKV,EAAEU,IAAK,IAAIV,EAAEU,IAAK,EAAE,GACjDJ,EAAS,SAAS,IAAIpB,EAAEwB,IAAKxB,EAAEwB,IAAK,IAAIxB,EAAEwB,IAAK,EAAE,GACjD,EAAS,MAAMF,GACf,EAAS,WAAWN,EAAMP,IACtBU,EAAKC,CAAQ,MAAM,IAAO;KAC5B,KAAKG,GAAQd,CAAC;KACd;IACF;IAEA,AADAK,EAAE,IAAIM,EAAS,UAAUI,CAAE,GAC3BxB,EAAE,IAAIoB,EAAS,UAAUI,CAAE;GAC7B;GACA;EACF;EAGA,IAAMhB,IAAS,KAAKmB,GAAgB,GAC9BC,IAAO,KAAKC,MAAerB;EACjC,KAAKK,MAAQ,KAAK,OAAOnB;EACzB,IAAMoC,IAAO,KAAK,MAAM,KAAKjB,EAAI;EACjC,KAAKA,MAAQiB;EACb,KAAK,IAAIC,IAAI,GAAGA,IAAID,GAAM,KAAK,KAAKnB,GAAOiB,GAAMpB,IAASuB,IAAI,KAAK,OAAO,KAAKD,IAAOpC,CAAE;EAExF,AADA,KAAKmC,KAAcrB,EAAO,MAAM,GAChC,KAAKI,GAAQ;CACf;CAGA,eAAe;EAEb,IAAMoB,IAAO,MAAM,aAAa;EAEhC,OADA,OAAOA,EAAK,UACL;GAAE,GAAGA;GAAM,GAAG,KAAK5B,GAAW;GAAG,KAAK,KAAKjB;GAAM,MAAM,KAAK;GAAM,SAAS,KAAK,QAAQ,MAAM;GACnG,MAAM,KAAK;GAAM,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,OAAO,KAAK;EAAM;CACzF;CAGA,cAAc;EACZ,IAAM6C,IAAO,MAAM,YAAY;EAE/B,OADA,OAAOA,EAAK,UACL;GAAE,GAAGA;GAAM,KAAK,KAAK7C;GAAM,MAAM,KAAK;GAAM,SAAS,KAAK,QAAQ,QAAQ;GAAG,MAAM,KAAK;GAC7F,QAAQ,KAAK,OAAO,QAAQ;GAAG,cAAc,KAAK;GAAc,UAAU,KAAK,SAAS,QAAQ;GAChG,gBAAgB,OAAO,KAAK,kBAAmB,WAAW,KAAK,iBAAiB,KAAK,eAAe,QAAQ;GAC5G,UAAU,CAAC,GAAG,KAAK,QAAQ;GAAG,OAAO,KAAKW,GAAU,EAAE,CAAC,KAAK,MAAM,IAAI3B,EAAM,CAAC,CAAC,QAAQ8B,EAAE,IAAIA,EAAE,IAAIA,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;EAAE;CACtH;CAIA,GAAO,GAAG,GAAG,GAAM;EACjB,IAAI,KAAKJ,MAAU,KAAKV,IAAM;EAC9B,IAAMsB,IAAI,KAAKZ,MAAU2B,IAAKf,IAAI,GAC5BK,IAAI,KAAK,SAAS,WAAWd,IAAI,KAAKZ,IACtC6C,IAAI,KAAK,OAAO,GAChBC,IAAIC,EAAa,KAAK,YAAY,GAClClC,IAAI,KAAK,gBACTmC,IAAK,OAAOnC,KAAM,WAAWkC,EAAalC,CAAC,IAAI;IAAE,KAAK,OAAO,IAAI,IAAI,KAAKA,EAAE;IAAK,KAAK,OAAO,IAAI,IAAI,KAAKA,EAAE;IAAK,KAAK,OAAO,IAAI,IAAI,KAAKA,EAAE;EAAE;EACpJ,KAAK,IAAIwB,IAAI,GAAGA,IAAI,GAAG,KAErB,AADA,EAAED,IAAKC,KAAK,KAAK,SAASA,KAAKW,EAAGX,IAClC,EAAED,IAAKC,KAAKY,EAAEZ,MAAMa,EAAEb,KAAKY,EAAEZ,MAAMQ,IAAIC,EAAET,KAAKzB,EAAEwB,IAAKC,KAAKc;EAE5D,IAAM,CAACC,GAAKzD,KAAO,KAAK;EAIxB,AAHA,KAAKO,GAAWmB,KAAK+B,IAAM,KAAK,OAAO,KAAKzD,IAAMyD,IAClD,KAAKnD,GAAMoB,KAAK8B,GAChB,KAAKhD,GAASkB,KAAK,KAAKH,IACxB,KAAKoB,GAAOjB,GAAG8B,IAAO,KAAKjD,GAAWmB,EAAE;CAC1C;CAIA,GAAQ,GAAG;EACT,IAAMgC,IAAO,EAAE,KAAK5C;EACpB,IAAIY,MAAMgC,GAAM;EAChB,IAAM3B,IAAI,KAAK,SAAS,WAAW4B,IAAiC,KAAK,SAAS,QAAS1C,IAAI,KAAKZ;EAMpG,AALA0B,EAAE,WAAWL,IAAI,GAAGgC,IAAO,GAAGA,IAAO,IAAI,CAAC,GAC1CC,EAAE,WAAWjC,IAAI,GAAGgC,IAAO,GAAGA,IAAO,IAAI,CAAC,GAC1CzC,EAAE,WAAWS,IAAI,GAAGgC,IAAO,GAAGA,IAAO,IAAI,CAAC,GAC1C,KAAKpD,GAAMoB,KAAK,KAAKpB,GAAMoD,IAC3B,KAAKnD,GAAWmB,KAAK,KAAKnB,GAAWmD,IACrC,KAAKlD,GAASkB,KAAK,KAAKlB,GAASkD;CACnC;CAGA,GAAO,GAAG,GAAG;EACX,IAAMC,IAAiC,KAAK,SAAS,QAASC,IAAQ,KAAK7C,GAAU,KAAKP,GAASkB;EACnG,IAAIkC,EAAM,WAAW,GAAG;GACtB,EAAE,IAAIA,EAAM,IAAIlC,IAAI,CAAC;GACrB;EACF;EACA,IAAMmC,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,KAAKD,EAAM,SAAS,IAClD1C,IAAI,KAAK,IAAI,KAAK,MAAM2C,CAAC,GAAGD,EAAM,SAAS,CAAC,GAAGE,IAAID,IAAI3C,GACvDoC,IAAIM,EAAM1C,IAAIqC,IAAIK,EAAM1C,IAAI;EAGlC,AAFA,EAAEQ,IAAI,KAAK4B,EAAE,MAAMC,EAAE,KAAKD,EAAE,MAAMQ,GAClC,EAAEpC,IAAI,IAAI,KAAK4B,EAAE,MAAMC,EAAE,KAAKD,EAAE,MAAMQ,GACtC,EAAEpC,IAAI,IAAI,KAAK4B,EAAE,MAAMC,EAAE,KAAKD,EAAE,MAAMQ;CACxC;CAEA,KAAU;EAER,AADA,KAAK,SAAS,YAAY,KAAKhD,IAC/B,KAAK,SAAS,YAAY;CAC5B;CAGA,KAAkB;EAChB,IAAI,CAAC,KAAK,QAAQ,OAAO,KAAK;EAC9B,IAAMiD,IAAQ,KAAK,OAAO;EAE1B,OADKlF,EAAYgB,GAAQ,KAAK,WAAW,IAClC,KAAK,OAAO,KAAKkE,CAAK,CAAC,CAAC,aAAalE,CAAM,IADC,KAAK;CAE1D;CAGA,GAAU,GAAG;EACX,IAAMmE,IAAQ;EAKd,IAHIb,EAAE,WAAW,KAAA,KAAW,KAAK,OAAO,KAAKA,EAAE,QAAQa,CAAK,GACxDb,EAAE,iBAAiB,KAAA,MAAW,KAAK,eAAe,KAAK,IAAI,GAAG3D,EAAYwE,GAAO,gBAAgBb,EAAE,YAAY,CAAC,IAChHA,EAAE,aAAa,KAAA,KAAW,KAAK,SAAS,KAAKA,EAAE,UAAUa,CAAK,GAC9Db,EAAE,mBAAmB,KAAA,GAAW;GAClC,IAAMjC,IAAIiC,EAAE;GACZ,AAAI,OAAOjC,KAAM,WAAU,KAAK,iBAAiB1B,EAAYwE,GAAO,kBAAkB9C,CAAC,IAC9E,KAAK,0BAA0B/B,IAAM,KAAK,eAAe,KAAK+B,GAAG8C,CAAK,IAC1E,KAAK,iBAAiB7E,EAAK,UAAU+B,GAAG8C,CAAK;EACpD;EAEA,AADIb,EAAE,aAAa,KAAA,MAAW,KAAK,WAAWc,EAAcd,EAAE,QAAQ,IAClEA,EAAE,UAAU,KAAA,MAAW,KAAKpC,GAAU,KAAKC,EAAWmC,EAAE,KAAK;CACnE;CAGA,GAAc,GAAG;EACf,IAAMa,IAAQ;EAKd,AAJIb,EAAE,SAAS,KAAA,MAAW,KAAK,OAAO,KAAK,IAAI,GAAG3D,EAAYwE,GAAO,QAAQb,EAAE,IAAI,CAAC,IAChFA,EAAE,YAAY,KAAA,KAAW,KAAK,QAAQ,KAAKA,EAAE,SAASa,CAAK,GAC3Db,EAAE,SAAS,KAAA,MAAW,KAAK,OAAO,KAAK,IAAI,GAAG3D,EAAYwE,GAAO,QAAQb,EAAE,IAAI,CAAC,IAChFA,EAAE,WAAW,KAAA,MAAW,KAAK,SAASe,EAAYf,EAAE,MAAM,IAC1DA,EAAE,eAAe,KAAA,MAAW,KAAK,aAAaA,EAAE;CACtD;CAGA,KAAa;EACX,OAAO;GAAE,QAAQ,KAAK,OAAO,MAAM;GAAG,cAAc,KAAK;GAAc,UAAU,KAAK,SAAS,MAAM;GACnG,gBAAgB,OAAO,KAAK,kBAAmB,WAAW,KAAK,iBAAiB,KAAK,eAAe,MAAM;GAC1G,UAA2C,CAAC,GAAG,KAAK,QAAQ;EAAG;CACnE;CAKA,GAAY,GAAO;EACjB,IAAMtC,IAAM+C,EAAM,KAAK,CAAC,CAAC,KAAK,GAC1BO,IAAK,KAAKC,GAAY,IAAIvD,CAAG;EACjC,IAAIsD,MAAO,KAAA,GAAW;GACpB,IAAK,KAAKpD,GAAU,SAAS,MAAM,KAAKA,GAAU,SAAS,IAAK,KAAKqD,GAAY,OAAO;GACxF,KAAK,IAAM,CAAC1B,GAAGzB,MAAM,KAAKmD,IAAa,AAAInD,MAAMkD,KAAI,KAAKC,GAAY,OAAO1B,CAAC;GAC9E,KAAK0B,GAAY,IAAIvD,GAAKsD,CAAE;EAC9B;EAEA,OADA,KAAKpD,GAAUoD,KAAMP,GACdO;CACT;AACF;AAGA,SAASD,EAAY,GAAG;CAEtB,OADIjD,MAAM,QAAQA,aAAa5B,IAAyC4B,IACjE3B,EAAK,WAAW,gCAAgCR,EAASmC,CAAC,GAAG;AACtE;AAGA,SAASgD,EAAc,GAAG;CACxB,IAAMI,IAAQ,OAAOpD,KAAM,WAAW,CAACA,GAAGA,CAAC,IAAIA;CAI/C,OAHK,MAAM,QAAQoD,CAAK,KAAKA,EAAM,WAAW,KAAMA,EAAM,OAAO,MAAM,OAAOrB,KAAM,YAAYA,IAAI,CAAC,KACnG1D,EAAK,WAAW,kEAAkER,EAASmC,CAAC,GAAG,GAE1F,CAAC,KAAK,IAAIoD,EAAM,IAAIA,EAAM,EAAE,GAAG,KAAK,IAAIA,EAAM,IAAIA,EAAM,EAAE,CAAC;AACpE;AAGA,SAASrD,EAAW,GAAG;CAErB,IAAMsD,IAAO,MAAM,QAAQrD,CAAC,KAAK,EAAEA,EAAE,WAAW,KAAKA,EAAE,OAAO,MAAM,OAAO+B,KAAM,QAAQ,KAAK/B,IAAI,CAACA,CAAC;CAEpG,OADKqD,EAAK,UAAQhF,EAAK,WAAW,mCAAmC,GAC9DgF,EAAK,KAAK,MAAMlF,EAAM,UAAqCuE,GAAI,SAAS,CAAC,CAAC,OAAO,CAAC;AAC3F;AAGA,SAASP,EAAa,GAAG;CACvB,IAAI,CAACmB,GAAG,OAAO;EAAC;EAAG;EAAG;CAAC;CACvB,IAAIV,GAAGW,GAAGC;CACV;EAGE,AAFA,IAAI,KAAK,OAAO,IAAI,IAAI,GACxB,IAAI,KAAK,OAAO,IAAI,IAAI,GACxB,IAAI,KAAK,OAAO,IAAI,IAAI;QACjBZ,IAAIA,IAAIW,IAAIA,IAAIC,IAAIA,IAAI;CACjC,OAAO;EAACZ,IAAIU;EAAGC,IAAID;EAAGE,IAAIF;CAAC;AAC7B;;AC5YA,IAAMG,IAAU;CAAC;CAAY;CAAW;AAAM,GACxCC,IAAQ;CACZ,SAAS;CACT,MAAM;AACR,GAEaC,IAAb,MAAkB;CAEhB,OAAO,UAAUF;CACjB,OAAO,QAAQC;CACf,WAAW,IAAIxF,EAAK;CACpB,UAAU,IAAIA,EAAK;CACnB,OAAO;CAGP,YAAY,IAAU,CAAC,GAAG;EAIxB,AAHAJ,EAAa,QAAQgB,GAAS2E,GAASC,CAAK,GACxC5E,EAAQ,aAAa,KAAA,KAAW,KAAK,SAAS,KAAKA,EAAQ,UAAU,MAAM,GAC3EA,EAAQ,YAAY,KAAA,KAAW,KAAK,QAAQ,KAAKA,EAAQ,SAAS,MAAM,GACxEA,EAAQ,SAAS,KAAA,MAAW,KAAK,OAAO,KAAK,IAAI,GAAGP,EAAY,QAAQ,QAAQO,EAAQ,IAAI,CAAC;CACnG;CAGA,QAAQ,GAAG;EAET,OADA,KAAK,SAAS,IAAIZ,EAAK,UAAU8B,GAAG,MAAM,CAAC,GACpC;CACT;CAOA,UAAU,GAAM,MAAO;EACrB,IAAMA,IAAI,KAAK,UAAUkB,IAAO,KAAK,IAAI,CAAC,KAAK,OAAOxB,CAAE;EACxD,KAAK,IAAI+B,IAAI,GAAGA,IAAI,GAAG,KAErB,AADA,EAAEA,MAAMzB,EAAEyB,KAAK,KAAK,QAAQA,KAAK/B,KAAMwB,GACvC,EAAK,SAASO,MAAMzB,EAAEyB,KAAK/B;CAE/B;AACF"}
|
|
1
|
+
{"version":3,"file":"particles.js","names":["OPTIONS","HINTS","mat4.invert","fail","Vec3","checkOptions","invert","Material","describe","Geometry","checkNumber","Color","Node","Points","SPAWN_OPTIONS","OPTIONS$1","HINTS$1","DEFAULT_MATERIAL","tmpMat","Emitter","options","max","given","material","geometry","#max","#velocities","#ages","#lifetimes","#palette","#setBehaviour","#setSpawn","dt","known","key","#alive","#palettes","colorStops","v","s","spawn","saved","#saveSpawn","color","#spawnPalette","#paletteFor","origin","i","count","#spawn","#upload","#due","p","ages","lives","g","keep","hook","particle","#particle","age","#remove","i3","k","#paint","#followedOrigin","from","#lastOrigin","born","n","rest","t","o","randomInBall","dv","a","b","head","min","last","c","stops","x","f","world","owner","lifetimeRange","checkFollow","id","#paletteIds","range","list","r","y","z","OPTIONS","HINTS","Body"],"sources":["../src/particles/Emitter.js","../src/particles/Body.js"],"sourcesContent":["// A particle system as one Points node: particles are born at the emitter's origin, move under\n// velocity, gravity and drag, fade through a list of colours and die. The simulation runs in the\n// emitter's own update handler, so it follows the scene clock (frame-rate independent) and its\n// arrays go to the GPU as one buffer refill per frame.\nimport * as mat4 from '../math/mat4.js'\nimport { Geometry } from '../geometry/Geometry.js'\nimport { Material } from '../material/Material.js'\nimport { Points } from '../scene/drawables.js'\nimport { Node } from '../scene/Node.js'\nimport { Color } from '../value/Color.js'\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, describe, fail } from '../validate.js'\n\n/** @import { MaterialOptions } from '../material/Material.js' */\n/** @import { NodeOptions } from '../scene/Node.js' */\n/** @import { ColorInput, Vec3Input } from '../types.js' */\n\n/**\n * A live particle, as passed to `onParticle`. Change `position` or `velocity` and the changes stick.\n * @typedef {object} Particle\n * @property {Vec3} position in the emitter's space\n * @property {Vec3} velocity units per second\n * @property {number} age seconds since birth\n * @property {number} lifetime seconds it lives for\n */\n\n/**\n * How particles are born. Each can also be passed to burst() for that burst alone.\n * @typedef {object} SpawnOptions\n * @property {Vec3Input} [origin] where particles start, in the emitter's space (default [0, 0, 0])\n * @property {number} [originSpread] start anywhere within this radius of the origin (default 0)\n * @property {Vec3Input} [velocity] starting velocity, units per second (default [0, 1, 0])\n * @property {number | Vec3Input} [velocitySpread] random extra velocity: a number spreads evenly in every\n * direction (a sphere - explosions), [x, y, z] spreads up to ± that much per axis (default 0)\n * @property {number | [number, number]} [lifetime] seconds, or a [min, max] range (default 1)\n * @property {ColorInput | ColorInput[]} [color] a colour, or colours each particle fades through over its\n * life - end on black to fade out with additive blending (default white)\n */\n\n/**\n * @typedef {NodeOptions & SpawnOptions & {\n * max?: number,\n * rate?: number,\n * gravity?: Vec3Input,\n * drag?: number,\n * follow?: Node | null,\n * material?: Material | MaterialOptions,\n * frustumCulled?: boolean,\n * renderOrder?: number,\n * onParticle?: ((p: Particle) => boolean | void) | null,\n * }} EmitterOptions\n * max: most live particles at once (default 1000). rate: particles born per second (default 100).\n * gravity: acceleration, e.g. [0, -9.8, 0] (default none). drag: share of velocity lost per second\n * (default 0). follow: a node whose position becomes the origin each frame - a trail that stays\n * behind it. onParticle: called for every live particle each frame after it moves; return false to\n * remove it (a particle hitting something).\n */\n\nconst SPAWN_OPTIONS = ['origin', 'originSpread', 'velocity', 'velocitySpread', 'lifetime', 'color']\nconst OPTIONS = [...Points.OPTIONS, ...SPAWN_OPTIONS,\n 'max', 'rate', 'gravity', 'drag', 'follow', 'onParticle']\nconst HINTS = {\n count: \"use 'max' for the most particles alive at once, 'rate' for how many are born per second\",\n lifetimeRnd: \"give 'lifetime' a [min, max] range\",\n velocityRnd: \"use 'velocitySpread'\",\n positionRnd: \"use 'originSpread'\",\n}\n// the look most particle effects want: glowing dots that don't hide each other\n/** @type {MaterialOptions} */\nconst DEFAULT_MATERIAL = { size: 0.1, vertexColors: true, blending: 'additive', depthWrite: false }\n\nconst tmpMat = new Float32Array(16)\n\n/**\n * Particles drawn as points. Add it to a scene and it runs itself.\n *\n * const sparks = scene.add(new Emitter({ rate: 200, velocitySpread: 2, gravity: [0, -9.8, 0], color: ['#ffd27f', '#000'] }))\n * sparks.burst(500, { velocitySpread: 8 })\n */\nexport class Emitter extends Points {\n static type = 'emitter'\n static get OPTIONS() { return OPTIONS }\n static get HINTS() { return { ...Points.HINTS, ...HINTS } }\n\n /** Particles born per second; 0 stops new ones (existing particles live out their lives). */\n rate = 100\n /** Acceleration on every particle, units per second². */\n gravity = new Vec3()\n /** Share of velocity lost per second. */\n drag = 0\n /** Where particles start, in the emitter's space. */\n origin = new Vec3()\n originSpread = 0\n velocity = new Vec3(0, 1, 0)\n /** @type {number | Vec3} */ velocitySpread = 0\n /** @type {[number, number]} */ lifetime = [1, 1]\n /** A node whose position becomes the origin each frame. @type {Node | null} */\n follow = null\n /** @type {((p: Particle) => boolean | void) | null} */\n onParticle = null\n\n #max\n #alive = 0\n #due = 0\n /** @type {Float32Array} */ #velocities\n /** @type {Float32Array} */ #ages\n /** @type {Float32Array} */ #lifetimes\n // Colour stop lists, sRGB like geometry colours. Palette 0 is the emitter's own colour; bursts with\n // their own colours add more, and each particle remembers which one it fades through.\n /** @type {number[][][]} */ #palettes = [[[1, 1, 1]]]\n /** @type {Map<string, number>} */ #paletteIds = new Map()\n /** @type {Uint8Array} */ #palette\n #spawnPalette = 0\n /** Last frame's origin, so a moving source leaves an even trail rather than clumps. */\n #lastOrigin = /** @type {Vec3 | null} */ (null)\n #particle = /** @type {Particle} */ ({ position: new Vec3(), velocity: new Vec3(), age: 0, lifetime: 0 })\n\n /** @param {EmitterOptions} [options] */\n constructor(options = {}) {\n if (/** @type {any} */ (options)?.geometry !== undefined) fail('Emitter', \"an Emitter makes its own geometry - size it with 'max'\")\n const max = options?.max ?? 1000\n if (!Number.isInteger(max) || max < 1) fail('Emitter', `'max' must be a whole number of 1 or more, got ${describe(max)}`)\n const given = options?.material\n if (given !== undefined && !(given instanceof Material) && (!given || typeof given !== 'object' || Array.isArray(given))) {\n fail('Emitter', `'material' is a Material or material options such as { size: 0.2 }, got ${describe(given)}`)\n }\n const material = given instanceof Material ? given : { ...DEFAULT_MATERIAL, ...given }\n const geometry = new Geometry({ positions: new Float32Array(max * 3), colors: new Float32Array(max * 3) })\n geometry.drawCount = 0\n super({ ...options, geometry, material })\n this.#max = max\n this.#velocities = new Float32Array(max * 3)\n this.#ages = new Float32Array(max)\n this.#lifetimes = new Float32Array(max)\n this.#palette = new Uint8Array(max)\n // particles spread anywhere, and stale slots would make bounds meaningless\n if (options.frustumCulled === undefined) this.frustumCulled = false\n\n this.#setBehaviour(options)\n this.#setSpawn(options)\n this.onUpdate((_, dt) => this.step(dt))\n }\n\n /**\n * Change several settings at once - a preset, say. Vectors are copied into the emitter's own,\n * so anything bound to `emitter.gravity` and friends stays bound. Live particles carry on.\n * @param {SpawnOptions & { rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null,\n * onParticle?: ((p: Particle) => boolean | void) | null }} options\n * @returns {this}\n */\n set(options) {\n const known = [...SPAWN_OPTIONS, 'rate', 'gravity', 'drag', 'follow', 'onParticle']\n for (const key of Object.keys(options ?? {})) {\n if (!known.includes(key)) fail('Emitter', `set() doesn't take '${key}' - it takes ${known.join(', ')}`)\n }\n this.#setBehaviour(options)\n this.#setSpawn(options)\n return this\n }\n\n /** Most live particles at once. */\n get max() { return this.#max }\n\n /** Particles alive now. */\n get count() { return this.#alive }\n\n /** The colours particles fade through - one or several. @param {ColorInput | ColorInput[]} v */\n set color(v) { this.#palettes[0] = colorStops(v) }\n /** @returns {[number, number, number][]} */\n get color() { return /** @type {[number, number, number][]} */ (this.#palettes[0].map((s) => [...s])) }\n\n /**\n * Birth `count` particles at once - explosions, splashes. Options override the emitter's own\n * spawn settings for this burst only.\n * @param {number} count\n * @param {SpawnOptions} [options]\n * @returns {this}\n */\n burst(count, options = {}) {\n for (const key of Object.keys(options)) {\n if (!SPAWN_OPTIONS.includes(key)) fail('Emitter', `burst() doesn't take '${key}' - it takes ${SPAWN_OPTIONS.join(', ')}`)\n }\n const { color, ...spawn } = options\n const saved = this.#saveSpawn()\n this.#setSpawn(spawn)\n if (color !== undefined) this.#spawnPalette = this.#paletteFor(colorStops(color))\n const origin = this.origin.clone()\n for (let i = 0; i < count; i++) this.#spawn(origin, origin, 0)\n this.#setSpawn(saved)\n this.#spawnPalette = 0\n this.#upload()\n return this\n }\n\n /** Remove every particle. @returns {this} */\n clear() {\n this.#alive = 0\n this.#due = 0\n this.#upload()\n return this\n }\n\n /**\n * Advance the simulation by dt seconds. The emitter's update handler calls this each frame; call\n * it yourself only for an emitter outside a running scene.\n * @param {number} dt\n */\n step(dt) {\n if (dt <= 0) return\n const p = this.geometry.positions\n const v = this.#velocities, ages = this.#ages, lives = this.#lifetimes\n const g = this.gravity, keep = Math.exp(-this.drag * dt)\n const hook = this.onParticle, particle = this.#particle\n\n for (let i = 0; i < this.#alive;) {\n const age = ages[i] += dt\n if (age >= lives[i]) {\n this.#remove(i)\n continue\n }\n const i3 = i * 3\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = (v[i3 + k] + g[k] * dt) * keep\n p[i3 + k] += v[i3 + k] * dt\n }\n this.#paint(i, age / lives[i])\n if (hook) {\n particle.position.set(p[i3], p[i3 + 1], p[i3 + 2])\n particle.velocity.set(v[i3], v[i3 + 1], v[i3 + 2])\n particle.age = age\n particle.lifetime = lives[i]\n if (hook(particle) === false) {\n this.#remove(i)\n continue\n }\n p.set(particle.position, i3)\n v.set(particle.velocity, i3)\n }\n i++\n }\n\n // new particles, spread along the path the origin took this frame\n const origin = this.#followedOrigin()\n const from = this.#lastOrigin ?? origin\n this.#due += this.rate * dt\n const born = Math.floor(this.#due)\n this.#due -= born\n for (let n = 0; n < born; n++) this.#spawn(from, origin, (n + Math.random()) / born * dt)\n this.#lastOrigin = origin.clone()\n this.#upload()\n }\n\n /** @internal @returns {Record<string, unknown>} */\n cloneOptions() {\n // an emitter makes its own geometry, so neither form carries it\n const rest = super.cloneOptions()\n delete rest.geometry\n return { ...rest, ...this.#saveSpawn(), max: this.#max, rate: this.rate, gravity: this.gravity.clone(),\n drag: this.drag, follow: this.follow, onParticle: this.onParticle, color: this.color }\n }\n\n /** @internal @returns {Record<string, unknown>} */\n jsonOptions() {\n const rest = super.jsonOptions()\n delete rest.geometry\n return { ...rest, max: this.#max, rate: this.rate, gravity: this.gravity.toArray(), drag: this.drag,\n origin: this.origin.toArray(), originSpread: this.originSpread, velocity: this.velocity.toArray(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.toArray(),\n lifetime: [...this.lifetime], color: this.#palettes[0].map((s) => new Color().setSRGB(s[0], s[1], s[2]).toJSON()) }\n }\n\n // birth one particle somewhere between a and b, already `head` seconds into its life\n /** @param {Vec3} a @param {Vec3} b @param {number} head */\n #spawn(a, b, head) {\n if (this.#alive >= this.#max) return\n const i = this.#alive++, i3 = i * 3\n const p = this.geometry.positions, v = this.#velocities\n const t = Math.random()\n const o = randomInBall(this.originSpread)\n const s = this.velocitySpread\n const dv = typeof s === 'number' ? randomInBall(s) : [(Math.random() * 2 - 1) * s[0], (Math.random() * 2 - 1) * s[1], (Math.random() * 2 - 1) * s[2]]\n for (let k = 0; k < 3; k++) {\n v[i3 + k] = this.velocity[k] + dv[k]\n p[i3 + k] = a[k] + (b[k] - a[k]) * t + o[k] + v[i3 + k] * head\n }\n const [min, max] = this.lifetime\n this.#lifetimes[i] = min + Math.random() * (max - min)\n this.#ages[i] = head\n this.#palette[i] = this.#spawnPalette\n this.#paint(i, head / this.#lifetimes[i])\n }\n\n // move the last particle into slot i\n /** @param {number} i */\n #remove(i) {\n const last = --this.#alive\n if (i === last) return\n const p = this.geometry.positions, c = /** @type {Float32Array} */ (this.geometry.colors), v = this.#velocities\n p.copyWithin(i * 3, last * 3, last * 3 + 3)\n c.copyWithin(i * 3, last * 3, last * 3 + 3)\n v.copyWithin(i * 3, last * 3, last * 3 + 3)\n this.#ages[i] = this.#ages[last]\n this.#lifetimes[i] = this.#lifetimes[last]\n this.#palette[i] = this.#palette[last]\n }\n\n /** @param {number} i @param {number} t 0-1 through its life */\n #paint(i, t) {\n const c = /** @type {Float32Array} */ (this.geometry.colors), stops = this.#palettes[this.#palette[i]]\n if (stops.length === 1) {\n c.set(stops[0], i * 3)\n return\n }\n const x = Math.min(Math.max(t, 0), 1) * (stops.length - 1)\n const s = Math.min(Math.floor(x), stops.length - 2), f = x - s\n const a = stops[s], b = stops[s + 1]\n c[i * 3] = a[0] + (b[0] - a[0]) * f\n c[i * 3 + 1] = a[1] + (b[1] - a[1]) * f\n c[i * 3 + 2] = a[2] + (b[2] - a[2]) * f\n }\n\n #upload() {\n this.geometry.drawCount = this.#alive\n this.geometry.needsUpdate()\n }\n\n /** The followed node's position in the emitter's space, or the origin. @returns {Vec3} */\n #followedOrigin() {\n if (!this.follow) return this.origin\n const world = this.follow.worldPosition\n if (!mat4.invert(tmpMat, this.worldMatrix)) return this.origin\n return this.origin.copy(world).applyMatrix4(tmpMat)\n }\n\n /** @param {SpawnOptions} o */\n #setSpawn(o) {\n const owner = 'Emitter'\n // vectors are copied in place, so references to them stay live\n if (o.origin !== undefined) this.origin.copy(o.origin, owner)\n if (o.originSpread !== undefined) this.originSpread = Math.max(0, checkNumber(owner, 'originSpread', o.originSpread))\n if (o.velocity !== undefined) this.velocity.copy(o.velocity, owner)\n if (o.velocitySpread !== undefined) {\n const s = o.velocitySpread\n if (typeof s === 'number') this.velocitySpread = checkNumber(owner, 'velocitySpread', s)\n else if (this.velocitySpread instanceof Vec3) this.velocitySpread.copy(s, owner)\n else this.velocitySpread = Vec3.fromInput(s, owner)\n }\n if (o.lifetime !== undefined) this.lifetime = lifetimeRange(o.lifetime)\n if (o.color !== undefined) this.#palettes[0] = colorStops(o.color)\n }\n\n /** @param {{ rate?: number, gravity?: Vec3Input, drag?: number, follow?: Node | null, onParticle?: ((p: Particle) => boolean | void) | null }} o */\n #setBehaviour(o) {\n const owner = 'Emitter'\n if (o.rate !== undefined) this.rate = Math.max(0, checkNumber(owner, 'rate', o.rate))\n if (o.gravity !== undefined) this.gravity.copy(o.gravity, owner)\n if (o.drag !== undefined) this.drag = Math.max(0, checkNumber(owner, 'drag', o.drag))\n if (o.follow !== undefined) this.follow = checkFollow(o.follow)\n if (o.onParticle !== undefined) this.onParticle = o.onParticle\n }\n\n /** The spawn settings other than colour, as options. @returns {SpawnOptions} */\n #saveSpawn() {\n return { origin: this.origin.clone(), originSpread: this.originSpread, velocity: this.velocity.clone(),\n velocitySpread: typeof this.velocitySpread === 'number' ? this.velocitySpread : this.velocitySpread.clone(),\n lifetime: /** @type {[number, number]} */ ([...this.lifetime]) }\n }\n\n // The palette id for a burst's colours, reused when the same colours come again. Ids are a byte\n // per particle, so past 255 distinct burst palettes the oldest is overwritten.\n /** @param {number[][]} stops @returns {number} */\n #paletteFor(stops) {\n const key = stops.flat().join()\n let id = this.#paletteIds.get(key)\n if (id === undefined) {\n id = this.#palettes.length < 256 ? this.#palettes.length : 1 + (this.#paletteIds.size % 255)\n for (const [k, v] of this.#paletteIds) if (v === id) this.#paletteIds.delete(k)\n this.#paletteIds.set(key, id)\n }\n this.#palettes[id] = stops\n return id\n }\n}\n\n/** @param {unknown} v @returns {Node | null} */\nfunction checkFollow(v) {\n if (v === null || v instanceof Node) return /** @type {Node | null} */ (v)\n return fail('Emitter', `'follow' must be a node, got ${describe(v)}`)\n}\n\n/** @param {number | [number, number]} v @returns {[number, number]} */\nfunction lifetimeRange(v) {\n const range = typeof v === 'number' ? [v, v] : v\n if (!Array.isArray(range) || range.length !== 2 || !range.every((n) => typeof n === 'number' && n > 0)) {\n fail('Emitter', `'lifetime' must be seconds above 0, or a [min, max] range, got ${describe(v)}`)\n }\n return [Math.min(range[0], range[1]), Math.max(range[0], range[1])]\n}\n\n/** @param {ColorInput | ColorInput[]} v @returns {number[][]} */\nfunction colorStops(v) {\n // a single [r, g, b] array is one colour, not three\n const list = Array.isArray(v) && !(v.length === 3 && v.every((n) => typeof n === 'number')) ? v : [v]\n if (!list.length) fail('Emitter', \"'color' needs at least one colour\")\n return list.map((c) => Color.fromInput(/** @type {ColorInput} */ (c), 'Emitter').toSRGB())\n}\n\n/** A random point within a ball of radius r, evenly spread. @param {number} r @returns {number[]} */\nfunction randomInBall(r) {\n if (!r) return [0, 0, 0]\n let x, y, z\n do {\n x = Math.random() * 2 - 1\n y = Math.random() * 2 - 1\n z = Math.random() * 2 - 1\n } while (x * x + y * y + z * z > 1)\n return [x * r, y * r, z * r]\n}\n","// Simple physics as a behaviour rather than a base class: any node moves with velocity, gravity\n// and drag once its body's update is attached - node.onUpdate(body.update).\nimport { Vec3 } from '../value/Vec3.js'\nimport { checkNumber, checkOptions } from '../validate.js'\n\n/** @import { Node } from '../scene/Node.js' */\n/** @import { Vec3Input } from '../types.js' */\n\n/**\n * @typedef {object} BodyOptions\n * @property {Vec3Input} [velocity] units per second (default [0, 0, 0])\n * @property {Vec3Input} [gravity] acceleration, e.g. [0, -9.8, 0] (default none)\n * @property {number} [drag] share of velocity lost per second (default 0)\n */\n\n/**\n * Velocity, gravity and drag for a node.\n *\n * const body = new Body({ velocity: [0, 20, 0], gravity: [0, -9.8, 0] })\n * rocket.onUpdate(body.update)\n */\nconst OPTIONS = ['velocity', 'gravity', 'drag']\nconst HINTS = {\n impulse: 'call body.impulse([x, y, z]) after creating it',\n mass: 'a Body has no mass - impulse() changes velocity directly',\n}\n\nexport class Body {\n /** The options it takes, and hints for names people reach for instead. */\n static OPTIONS = OPTIONS\n static HINTS = HINTS\n velocity = new Vec3()\n gravity = new Vec3()\n drag = 0\n\n /** @param {BodyOptions} [options] */\n constructor(options = {}) {\n checkOptions('Body', options, OPTIONS, HINTS)\n if (options.velocity !== undefined) this.velocity.copy(options.velocity, 'Body')\n if (options.gravity !== undefined) this.gravity.copy(options.gravity, 'Body')\n if (options.drag !== undefined) this.drag = Math.max(0, checkNumber('Body', 'drag', options.drag))\n }\n\n /** Add to the velocity at once - a kick. @param {Vec3Input} v @returns {this} */\n impulse(v) {\n this.velocity.add(Vec3.fromInput(v, 'Body'))\n return this\n }\n\n /**\n * Move a node one step. Bound, so it can be handed straight to node.onUpdate().\n * @param {Node} node\n * @param {number} dt seconds\n */\n update = (node, dt) => {\n const v = this.velocity, keep = Math.exp(-this.drag * dt)\n for (let k = 0; k < 3; k++) {\n v[k] = (v[k] + this.gravity[k] * dt) * keep\n node.position[k] += v[k] * dt\n }\n }\n}\n"],"mappings":"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"}
|