babylon.quarks 0.21.0 → 0.24.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 +184 -0
- 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.ts +936 -45
- package/package.json +2 -2
- package/src/BatchedRenderer.ts +205 -4
- package/src/EffectPool.ts +165 -0
- package/src/MeshSurfaceEmitter.ts +4 -4
- package/src/ParticleEmitter.ts +2 -1
- package/src/ParticleSystem.ts +975 -69
- package/src/QuarksLoader.ts +25 -4
- package/src/QuarksUtil.ts +55 -7
- package/src/SpriteBatch.ts +515 -114
- package/src/TrailBatch.ts +128 -75
- package/src/VFXBatch.ts +255 -3
- package/src/index.ts +5 -2
- package/src/instanceSort.ts +121 -0
- package/src/shaders/chunks/fog.ts +127 -0
- package/src/shaders/local_particle_physics_vert.glsl.ts +4 -0
- package/src/shaders/local_particle_physics_vert.wgsl.ts +4 -0
- package/src/shaders/particle_frag.glsl.ts +4 -0
- package/src/shaders/particle_frag.wgsl.ts +4 -0
- package/src/shaders/particle_physics_frag.glsl.ts +30 -1
- package/src/shaders/particle_physics_frag.wgsl.ts +30 -1
- package/src/shaders/particle_vert.glsl.ts +23 -4
- package/src/shaders/particle_vert.wgsl.ts +23 -4
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +17 -0
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +18 -1
- package/src/shaders/trail_frag.glsl.ts +4 -0
- package/src/shaders/trail_frag.wgsl.ts +4 -0
- package/src/shaders/trail_vert.glsl.ts +4 -0
- package/src/shaders/trail_vert.wgsl.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,190 @@ All notable changes to the `babylon.quarks` package are documented here.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and the project follows the `quarks.core` 0.x version line.
|
|
6
6
|
|
|
7
|
+
## [0.24.0] — 2026-10-20
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `ParticleSystem.renderingGroupId` (constructor option, JSON `renderingGroupId`, written only when
|
|
12
|
+
not 0): the Babylon rendering group the batch mesh draws in. Systems with different groups are
|
|
13
|
+
batched apart.
|
|
14
|
+
- `ParticleSystem.applyFog` (constructor option, JSON `applyFog`, written only when false): the
|
|
15
|
+
scene's fog now reaches the particles in every render mode, in GLSL and WGSL, the way it reaches
|
|
16
|
+
any other mesh. Additive blending fades toward black instead of the fog colour. Fog is bound from
|
|
17
|
+
the scene on every draw, so enabling or disabling it at runtime needs no recompile.
|
|
18
|
+
- `ParticleSystem.playbackSpeed` (constructor option, JSON `playbackSpeed`, written only when
|
|
19
|
+
not 1): a multiplier on the time the system is advanced by — Unity's Simulation Speed. The
|
|
20
|
+
Unity exporter writes it from Main → Simulation Speed.
|
|
21
|
+
- quarks.core `ApplySequences` is registered in `BehaviorTypes`, so an effect using it serialises
|
|
22
|
+
and loads like any other behavior.
|
|
23
|
+
- Bounds and culling. Every system measures the world bounds of what it draws
|
|
24
|
+
(`ParticleSystem.getWorldBounds`), from the store columns for sprites and meshes and from the
|
|
25
|
+
ribbons for trails; batch meshes carry the union, so Babylon's frustum culling skips the draw of
|
|
26
|
+
a batch no camera sees and transparent sorting has real bounds to work with. Measured in full
|
|
27
|
+
every few steps and carried, padded for the motion since, in between: no measurable cost on the
|
|
28
|
+
profiling scenarios.
|
|
29
|
+
- `ParticleSystem.cullingMode` (constructor option, JSON `cullingMode`, written only when not
|
|
30
|
+
`'always'`): Unity's Culling Mode. `'pause'` freezes a system while no active camera sees its
|
|
31
|
+
bounds; `'pauseAndCatchUp'` also replays the missed time (up to 2 s) when it comes back, within
|
|
32
|
+
`BatchedRenderer.catchUpBudgetMs` (2 ms per frame, shared by all systems; what does not fit is
|
|
33
|
+
dropped). Culled systems are neither simulated nor filled into their batch. Empty systems and sub
|
|
34
|
+
emitter targets are never culled; `BatchedRenderer.cullingEnabled = false` turns the test off.
|
|
35
|
+
With 400 systems in a ring around the camera and three quarters of them out of view, a frame goes
|
|
36
|
+
from 9.6 ms to 1.6 ms. The Unity exporter writes Pause and Pause And Catch-up.
|
|
37
|
+
- Deterministic randomness. Every roll the simulation makes now goes through quarks.core's
|
|
38
|
+
`random()`, whose source is replaceable: `setRandomSource(createSeededRandom(seed))` makes a whole
|
|
39
|
+
scene reproducible, `withRandomSource` scopes it, and `getRandomSource` reads it back. The noise
|
|
40
|
+
generators are seeded by slot, so a Noise behavior looks the same on every run. UUIDs keep using
|
|
41
|
+
`Math.random`.
|
|
42
|
+
- `ParticleSystem.seed` (constructor option, JSON `seed`, written only when set): the system rolls
|
|
43
|
+
on its own generator started from the seed while it steps, and restarts it on `restart()`, so the
|
|
44
|
+
effect plays out identically every time; systems it emits into roll on it too. The editor has a
|
|
45
|
+
"Fixed seed" field; the Unity exporter writes Main → Random Seed when Auto Random Seed is off.
|
|
46
|
+
- `EffectPool`: a pool of copies of one effect for things played many times over. `acquire(position,
|
|
47
|
+
parent)` takes an idle copy (or makes one, up to `maxSize`), places it and plays it; a copy whose
|
|
48
|
+
systems have all finished returns to the pool by itself; `stop(copy)` ends a looping copy's
|
|
49
|
+
emission and lets it die out first; `release(copy)` takes it back at once; `prewarm` makes copies
|
|
50
|
+
up front. Idle copies stay disabled in the renderer's batches, so acquiring allocates nothing.
|
|
51
|
+
- `QuarksUtil.instantiate(root)`: a deep copy of an effect tree, with sub emitter links pointing at
|
|
52
|
+
the copied emitters.
|
|
53
|
+
- `ParticleSystem.sortMode` (constructor option, JSON `sortMode`, written only when not `'none'`):
|
|
54
|
+
Unity's Sort Mode. `'distance'` draws the particles of the batch farthest from the active camera
|
|
55
|
+
first, so nearer alpha-blended particles cover farther ones; `'oldestInFront'` and
|
|
56
|
+
`'youngestInFront'` order by age. The order is a radix sort over the whole batch after each fill
|
|
57
|
+
(about 2.5 ms per 50k particles), so systems with different modes are batched apart; trails are
|
|
58
|
+
not sorted. The editor's Renderer module and the Unity exporter carry it (Distance and Depth both
|
|
59
|
+
map to `'distance'`).
|
|
60
|
+
- `ParticleSystem.minParticleSize` / `maxParticleSize` (constructor options, JSON, written only when
|
|
61
|
+
set): Unity's Min / Max Particle Size, the smallest and largest size a billboard or stretched
|
|
62
|
+
particle is drawn at as a fraction of the viewport height, clamped in the vertex shader (GLSL and
|
|
63
|
+
WGSL). Meshes and trails ignore them. The editor's Renderer module has them under "Limit screen
|
|
64
|
+
size"; the exporter writes Unity's values, its default maximum of 0.5 included.
|
|
65
|
+
- `ParticleSystem.alignToDirection` (constructor option, JSON `alignToDirection`, written only when
|
|
66
|
+
true): Unity's Align To Direction. A mesh particle's +Z is turned along the direction its shape
|
|
67
|
+
sends it; a billboard's top is turned toward it in the emitter's XY plane. The start rotation is
|
|
68
|
+
applied on top; a particle with no speed is left alone. Shape module checkbox in the editor; the
|
|
69
|
+
exporter writes Shape → Align To Direction.
|
|
70
|
+
- quarks.core `Noise` gains `scrollSpeed` (Unity's Scroll Speed: the field moves past the particles,
|
|
71
|
+
sampled once per frame, reset with the behavior) and `remap` (Unity's Remap: a curve read at the
|
|
72
|
+
raw noise mapped from [-1, 1] to [0, 1], whose value replaces it). Both in JSON, the editor's Noise
|
|
73
|
+
module and the exporter.
|
|
74
|
+
- quarks.core `VelocityOverLife` gains `radial` (Unity's Radial: speed away from the orbit centre)
|
|
75
|
+
and `orbitalOffset` (Unity's Orbital Offset: where the orbital speeds turn around, relative to
|
|
76
|
+
the emitter). In JSON, the editor's Velocity module and the exporter.
|
|
77
|
+
- quarks.core `EmitSubParticleSystem` gains `inherit` (Unity's sub emitter Inherit): `'color'` and
|
|
78
|
+
`'size'` multiply the sub particles' start values by the parent's current ones, `'rotation'` adds
|
|
79
|
+
the parent's, `'lifetime'` scales theirs by the parent's remaining lifetime fraction. The mask
|
|
80
|
+
travels in the emission state, so a host that emits into a system by hand can set it too. In
|
|
81
|
+
JSON, the editor's Sub Emitters module (four checkboxes) and the exporter.
|
|
82
|
+
- `BatchedRenderer.simulationBudgetMs` (unlimited by default): once a frame's simulation has taken
|
|
83
|
+
this long, the systems still to be advanced run one step instead of catching up on the long
|
|
84
|
+
frame, and the time the skipped steps would have covered is dropped — so a frame that already ran
|
|
85
|
+
long cannot make the next one longer still.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- Developed and tested against `@babylonjs/core` 9.31.0 (was 9.6.2); the peer range stays
|
|
90
|
+
`>=9.0.0`. The examples and the editor's dev dependencies follow.
|
|
91
|
+
- `ParticleSystem.isFinished()` is true for a looping system whose emission was ended with
|
|
92
|
+
`endEmit()` once its particles are gone, and for a sub emitter target whenever it is empty,
|
|
93
|
+
looping or not; `finished` fires accordingly. `QuarksUtil.isEffectFinished` follows, so an effect
|
|
94
|
+
with looping systems is finished after `endEmit` rather than never.
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- quarks.core `ApplySequences` threw on update while it had no sequences yet (an editor adds the
|
|
99
|
+
behavior before its first sequence); it now leaves the particles alone until one is appended.
|
|
100
|
+
- `ParticleSystem.clone()` left cloned `EmitSubParticleSystem` behaviors transforming by the
|
|
101
|
+
original system's emitter; they now follow the clone. `ParticleEmitter.clone()` no longer
|
|
102
|
+
disables the clone itself when only an ancestor of the original was disabled.
|
|
103
|
+
- `renderOrder` now reaches the batch mesh as `alphaIndex`, so it orders the batch among the
|
|
104
|
+
scene's transparent meshes instead of only keying the batching; it used to have no effect on
|
|
105
|
+
what was drawn first. `layerMask` likewise now reaches the batch mesh — cameras with a
|
|
106
|
+
non-matching mask used to draw the particles anyway.
|
|
107
|
+
|
|
108
|
+
## [0.23.0] — 2026-10-18
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
|
|
112
|
+
- CPU simulation and batch upload rewritten to work on the `ParticleStore` columns instead of
|
|
113
|
+
walking particle objects. Scalar attributes (`rotation`, `angularVelocity`, `uvTile`, `flip`,
|
|
114
|
+
`startSpeed`) and mesh quaternion rotations now live in store columns; the sprite batch copies
|
|
115
|
+
them as ranges and only visits particle objects for sub-emitted systems. Compiled behavior passes
|
|
116
|
+
(now built for single behaviors too) read and write the columns, interpolate sampled curves and
|
|
117
|
+
gradients inline and take per-particle random samples from a mirrored memory column, so a step
|
|
118
|
+
no longer touches the particle object for the built-in behaviors. Snapshots of size and colour
|
|
119
|
+
for the renderer are only kept when a behavior can change them, and the step's integration,
|
|
120
|
+
newborn fix-up and compaction run in one pass. Trail ribbons transform each recorded sample
|
|
121
|
+
once, upload indices straight to the GPU buffer (no per-frame sub mesh rebuild) and recompute
|
|
122
|
+
the emitter matrix only when its transform changed. `Noise` and `TurbulenceField` evaluate
|
|
123
|
+
constant generators once per frame and update through the columns. Measured on 50k particles:
|
|
124
|
+
billboards 2.1–2.9x faster per frame, trails 1.5x, noise/turbulence 1.2x.
|
|
125
|
+
- Assigning a `Quaternion` to a sprite particle's `rotation` or `angularVelocity` copies it into
|
|
126
|
+
the particle's store row; reading the property back returns a view of that row rather than the
|
|
127
|
+
assigned object. `RandomQuatGenerator` stores its sample as four numbers in the particle's
|
|
128
|
+
memory instead of a `Quaternion`. `TrailParticle.previous` (deprecated) is created on first use.
|
|
129
|
+
|
|
130
|
+
## [0.22.0] — 2026-10-17
|
|
131
|
+
|
|
132
|
+
### Added
|
|
133
|
+
|
|
134
|
+
- Mesh particle lighting modes: `ParticleSystem.lighting` (constructor option `lighting`, saved
|
|
135
|
+
as `lighting` on the effect's `QuarksMaterial` and read back by `QuarksLoader`) is `'fixed'`
|
|
136
|
+
(default — exactly the light mesh particles had before), `'unlit'` (the lighting term is
|
|
137
|
+
compiled out with `MESH_UNLIT`: texture × particle colour, for emissive meshes) or `'scene'`
|
|
138
|
+
(the scene's first enabled `DirectionalLight` / `HemisphericLight`, re-read every frame:
|
|
139
|
+
direction, diffuse × intensity, `scene.ambientColor`, plus a hemispheric light's ground colour
|
|
140
|
+
in the ambient; falls back to the fixed light when the scene has neither). Systems with
|
|
141
|
+
different lighting are batched apart. The default is not written to JSON.
|
|
142
|
+
- `BatchedRenderer.setMeshLighting({direction, color, ambient})` (Babylon `Vector3` / `Color3`,
|
|
143
|
+
any subset) sets the `'fixed'` light for every mesh batch of the renderer, including later ones;
|
|
144
|
+
`getMeshLighting()` returns a copy, `defaultMeshLighting()` the built-in values
|
|
145
|
+
(`(0.4, -1, 0.6)`, white, `0.35` grey). Exposed in the editor's Renderer module as "Lighting".
|
|
146
|
+
- Mesh particle rim (Fresnel) term: `ParticleSystem.rim` (constructor option `rim`, saved as
|
|
147
|
+
`rim` on the effect's `QuarksMaterial`) — `{color: [r, g, b], power, strength, mode}` with
|
|
148
|
+
defaults `[1, 1, 1]`, `2`, `1`, `'add'`; any field may be left out. The edge term is
|
|
149
|
+
`pow(1 - |N·V|, power) * strength`; `'add'` adds `color` (glowing edge), `'mix'` blends toward it
|
|
150
|
+
(outline). Compiled in only when set (`MESH_RIM` / `MESH_RIM_MIX`), batched by value, written to
|
|
151
|
+
JSON only when set, and exposed in the editor's Renderer module. `normalizeMeshRim` /
|
|
152
|
+
`meshRimEqual` are exported.
|
|
153
|
+
|
|
154
|
+
### Changed
|
|
155
|
+
|
|
156
|
+
- **Behaviour change:** quarks.core `WidthOverLength` (Width over Trail) now reads its curve as a
|
|
157
|
+
multiplier on the width each trail sample was recorded with (the particle's `size.x` at that
|
|
158
|
+
moment), the way Unity's Width over Trail works, and gives the head sample the curve's value at
|
|
159
|
+
0, so a trail no longer flares out behind its head or steps at the tip. A new `absolute` flag
|
|
160
|
+
(`new WidthOverLength(curve, absolute = false)`, JSON field `absolute`) keeps the old reading —
|
|
161
|
+
the curve as the width in world units, the head at the particle's size. `toJSON` always writes
|
|
162
|
+
the flag; JSON without it (everything written by 0.21 and earlier, including the Unity
|
|
163
|
+
exporter's output) loads as `absolute: true`, so authored effects keep their look. Code that
|
|
164
|
+
builds `new WidthOverLength(curve)` gets the new multiplier behaviour: divide an absolute curve
|
|
165
|
+
by the start size, or pass `true`. `TrailParticle` gains `historyBaseSizes` (the unscaled
|
|
166
|
+
recorded widths) and `headWidthScale`.
|
|
167
|
+
|
|
168
|
+
### Fixed
|
|
169
|
+
|
|
170
|
+
- Burst `cycle` / `interval` are honoured: a burst fires `cycle` waves, `interval` seconds apart,
|
|
171
|
+
each rolling `probability` on its own. Repeats past the system's `duration` are dropped (a
|
|
172
|
+
looping system starts them over every loop), a `cycle` below 1 fires once, and a zero interval
|
|
173
|
+
fires every wave at once. Previously every burst fired a single wave.
|
|
174
|
+
- Emission over distance spreads the particles it owes along the path the emitter travelled
|
|
175
|
+
this step, each at its own metre mark (the spacing carries across steps), instead of spawning
|
|
176
|
+
all of them at the emitter's current position — a fast world-space emitter now leaves a
|
|
177
|
+
continuous trail rather than one clump per frame. Only the position is interpolated (rotation
|
|
178
|
+
and scale are the emitter's current ones) and the particles start at age 0. Local-space
|
|
179
|
+
systems, whose particles move with the emitter, are unchanged.
|
|
180
|
+
- A sub emitter whose target uuid is not in the effect no longer throws on every frame.
|
|
181
|
+
`QuarksLoader` unlinks it (`subParticleSystem = undefined`) with one `console.warn` naming the
|
|
182
|
+
emitter and the uuid — also when the uuid names a node that is not a particle emitter — and
|
|
183
|
+
quarks.core `EmitSubParticleSystem` skips any value that is not a resolved emitter. An
|
|
184
|
+
unresolved uuid string is written back unchanged by `toJSON`.
|
|
185
|
+
- quarks.core `RotationOverLife` turns mesh particles too: a quaternion rotation is turned about
|
|
186
|
+
the particle's local Z axis (the axis a number `startRotation` uses, and what Unity's Rotation
|
|
187
|
+
over Lifetime without separate axes does to meshes), in its own pass and in the fused pass, and
|
|
188
|
+
the step is kept for the renderer's in-between-step interpolation. It used to do nothing on
|
|
189
|
+
meshes, silently. `Rotation3DOverLife` remains the behavior for any other axis.
|
|
190
|
+
|
|
7
191
|
## [0.21.0] — 2026-10-16
|
|
8
192
|
|
|
9
193
|
### Added
|