babylon.quarks 0.27.0 → 0.29.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 +585 -0
- package/README.md +3 -1
- 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 +4811 -974
- package/dist/types/index.d.ts +4811 -974
- package/package.json +7 -2
- package/src/BatchedRenderer.ts +106 -79
- package/src/EffectPool.ts +96 -16
- package/src/MeshSurfaceEmitter.ts +24 -13
- package/src/ParticleEmitter.ts +119 -26
- package/src/ParticleSystem.ts +305 -55
- package/src/QuarksLoader.ts +298 -171
- package/src/QuarksPrefab.ts +301 -94
- package/src/QuarksSerializer.ts +44 -16
- package/src/QuarksUtil.ts +88 -11
- package/src/SpriteBatch.ts +24 -6
- package/src/TrailBatch.ts +20 -7
- package/src/VFXBatch.ts +182 -528
- package/src/batchKey.ts +6 -2
- package/src/batchSettings.ts +548 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +21 -24
- package/src/envAtlas.ts +8 -2
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +41 -4
- package/src/nodeId.ts +25 -2
- package/src/shaderProgram.ts +213 -16
- package/src/shaderVariants.ts +124 -7
- package/src/shaders/chunks/blend.ts +39 -0
- package/src/shaders/chunks/fog.ts +8 -3
- package/src/shaders/chunks/imageProcessing.ts +143 -0
- package/src/shaders/chunks/soft.ts +6 -5
- package/src/shaders/chunks/surface.ts +2 -2
- package/src/shaders/particle_frag.glsl.ts +5 -1
- package/src/shaders/particle_frag.wgsl.ts +4 -0
- package/src/shaders/particle_physics_frag.glsl.ts +7 -2
- package/src/shaders/particle_physics_frag.wgsl.ts +4 -0
- package/src/shaders/trail_frag.glsl.ts +6 -1
- package/src/shaders/trail_frag.wgsl.ts +4 -0
- package/src/system/EmissionController.ts +23 -20
- package/src/system/EventDispatcher.ts +26 -10
- package/src/system/ParticleSpawner.ts +96 -19
- package/src/system/geometry.ts +22 -29
- package/src/system/integrate.ts +61 -4
- package/src/system/materialResolve.ts +56 -7
- package/src/system/normalize.ts +34 -2
- package/src/system/serialization.ts +54 -33
- package/src/system/settingsSchema.ts +57 -17
- package/src/system/types.ts +150 -42
- package/src/trailSettings.ts +9 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,591 @@ 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.29.0] — 2026-10-24
|
|
8
|
+
|
|
9
|
+
Phase 7 of the road to 1.0: what still behaves differently from Unity is brought in line before
|
|
10
|
+
the API and the look of effects are frozen. Cones tilt and fill like Unity's and emit from their
|
|
11
|
+
volume, Scaling Mode has Unity's Local, Hierarchy and Shape, curves can be random between two,
|
|
12
|
+
Limit Velocity has Separate Axes, a system has a Stop Action and a Ring Buffer Mode, and the
|
|
13
|
+
particles take the scene's image processing as Babylon's own materials do.
|
|
14
|
+
|
|
15
|
+
### Upgrading from 0.28
|
|
16
|
+
|
|
17
|
+
Effects in a scene without image processing, at scale 1 and without cones, look as before. Check
|
|
18
|
+
these, in this order:
|
|
19
|
+
|
|
20
|
+
1. **Scenes with image processing** (tone mapping, exposure, contrast, a vignette or grading, also
|
|
21
|
+
when set up by `createDefaultEnvironment()` or a `DefaultRenderingPipeline`): the particles now
|
|
22
|
+
take it. Re-tune colours that were authored to look right without it, or set
|
|
23
|
+
`applyImageProcessing: false` on a system to keep the old look.
|
|
24
|
+
2. **Scaled emitters or scaled parents:** the default Scaling Mode is now `'local'`. Set
|
|
25
|
+
`scalingMode: 'hierarchy'` where a parent's scale should reach the effect. Files saved before
|
|
26
|
+
0.29 read as `'hierarchy'` and keep their look; save them again to write format 2.
|
|
27
|
+
3. **Cones:** particles between the centre and the rim lean out more (Unity's tilt). Re-tune an
|
|
28
|
+
effect that relied on the narrower middle.
|
|
29
|
+
4. **`autoDestroy`** still works but is deprecated: use `stopAction: 'destroy'`. Files are
|
|
30
|
+
written with `stopAction`, which a 0.28 runtime does not read.
|
|
31
|
+
5. **TypeScript:** narrow `LimitSpeedOverLife.speed` before calling `genValue` on it
|
|
32
|
+
(`!(speed instanceof Vector3Function)`); a custom behavior reading `emitter.matrixWorld` gets
|
|
33
|
+
the matrix with the Scaling Mode's scale.
|
|
34
|
+
6. **Files are format 2.** babylon.quarks 0.28 warns about them and reads their systems with its
|
|
35
|
+
own Scaling Mode. Install `babylon.quarks-editor` 0.29 with `babylon.quarks` 0.29, and export
|
|
36
|
+
from the Unity exporter 0.29 for this runtime.
|
|
37
|
+
|
|
38
|
+
### Breaking
|
|
39
|
+
|
|
40
|
+
- `autoDestroy` gives way to `stopAction` (`'none'`, `'disable'` or `'destroy'`, Unity's Stop
|
|
41
|
+
Action). Files are written with `stopAction` and no longer with `autoDestroy`; reading
|
|
42
|
+
`autoDestroy: true` from an older file, or passing it to the constructor, still gives
|
|
43
|
+
`'destroy'`, and the `autoDestroy` property and `QuarksUtil.setAutoDestroy` remain as deprecated
|
|
44
|
+
aliases. An older runtime does not read `stopAction`.
|
|
45
|
+
- `LimitSpeedOverLife.speed` is typed `FunctionValueGenerator | ValueGenerator | Vector3Function`
|
|
46
|
+
(it was `FunctionValueGenerator`), for Separate Axes and constant limits. Code that reads it
|
|
47
|
+
with `genValue(memory, t)` narrows it first: `!(speed instanceof Vector3Function)`.
|
|
48
|
+
- **Cone particles between the centre and the rim tilt by `atan(r · tan(angle))`**, as Unity's
|
|
49
|
+
Cone, instead of `angle · r`. Each particle follows the cone's side line through its start
|
|
50
|
+
point, so the cone's far end is a disc of radius `radius + length · tan(angle)` filled as evenly
|
|
51
|
+
as its base. The rim and the centre are unchanged; particles in between lean out more than
|
|
52
|
+
before. Re-tune a cone effect that relied on the old, narrower middle. Cones in three.quarks
|
|
53
|
+
files tilt the new way too, so they lean out further in the middle than in three.quarks.
|
|
54
|
+
- **Scaling Mode works as Unity's, in world and local space, and defaults to `'local'`.** The
|
|
55
|
+
emitter's scale now reaches the shape, the particles' size and their motion in every mode except
|
|
56
|
+
where the mode leaves it out: `'local'` (new, the default) uses only the emitter's own `scaling`
|
|
57
|
+
and leaves its parents' out; `'hierarchy'` uses the whole world scale, as before; `'shape'` now
|
|
58
|
+
scales only the emission shape, leaving the particles' size and speed alone (it used to scale
|
|
59
|
+
their size too). Before, a local-space system took its emitter's whole world scale whatever the
|
|
60
|
+
mode. An effect whose emitter or its parents are scaled looks different unless it was
|
|
61
|
+
`'hierarchy'`: set `scalingMode: 'hierarchy'` to keep a parent's scale on it, or leave it at
|
|
62
|
+
`'local'` to keep the effect its own size under a scaled parent. Effects at scale 1 are
|
|
63
|
+
unchanged.
|
|
64
|
+
- **Files from before this release keep their look:** the format goes to 2, and a system in a
|
|
65
|
+
file of format 1 or without `quarksFormat` (older babylon.quarks, three.quarks, the Unity
|
|
66
|
+
exporter before 0.29) reads as `'hierarchy'`, which gives it the emitter's whole world scale
|
|
67
|
+
as before (except a world-space system's speed, which now scales too). Save it again to keep
|
|
68
|
+
that, or set the mode you want. A 0.28 loader warns about a format 2 file and reads its
|
|
69
|
+
systems as its own default.
|
|
70
|
+
- **`ParticleEmitter.matrixWorld` (`IEmitter.matrixWorld`) is the matrix the system simulates
|
|
71
|
+
and draws in:** the world matrix with each axis's length set by the mode. A custom behavior
|
|
72
|
+
that reads it gets that scale. `ParticleEmitter.shapeScale` is what `'shape'` gives the shape.
|
|
73
|
+
- **Negative scales mirror the start velocity as they mirror the position,** in every mode; a
|
|
74
|
+
world-space `'hierarchy'` system used to mirror the position only.
|
|
75
|
+
- **A sub emission's speed** takes its parent particle's frame, as its size did: it no longer
|
|
76
|
+
scales with the sub emitter's own scale in `'hierarchy'`.
|
|
77
|
+
- **The scene's image processing applies to the particles by default**, as it does to Babylon's
|
|
78
|
+
own `ParticleSystem` and its standard and PBR materials. A scene whose
|
|
79
|
+
`imageProcessingConfiguration` does nothing (Babylon's default) looks the same as before. One
|
|
80
|
+
that tone maps, sets exposure or contrast, grades, vignettes or dithers now does it to the
|
|
81
|
+
particles too, so they sit in the frame like the meshes around them. Under a
|
|
82
|
+
`DefaultRenderingPipeline` with image processing on (`applyByPostProcess`), the particles now
|
|
83
|
+
leave their colour in linear space for the post process, as Babylon's materials do; before they
|
|
84
|
+
were gamma-corrected twice and looked washed out. Set `applyImageProcessing: false` on a system
|
|
85
|
+
to keep the old look. Check effects in scenes set up by any of these, which turn image
|
|
86
|
+
processing on or hand it to a post process without saying so: `scene.createDefaultEnvironment()`
|
|
87
|
+
(tone mapping, exposure and contrast), a `DefaultRenderingPipeline` with
|
|
88
|
+
`imageProcessingEnabled` (its default), a `PrePassRenderer` (SSAO2, SSR, motion blur) and a
|
|
89
|
+
`ReflectionProbe` rendering in linear space.
|
|
90
|
+
Tone mapping clamps HDR particle colours, and a contrast below 1 or colour curves lift black,
|
|
91
|
+
which an additive particle then adds.
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- **Scene image processing** (`ParticleSystem.applyImageProcessing`, default true; JSON writes it
|
|
96
|
+
only when false): the scene's `imageProcessingConfiguration` (exposure, contrast, tone mapping,
|
|
97
|
+
vignette, colour curves, colour grading, dithering) is compiled into every render mode, in GLSL
|
|
98
|
+
and WGSL, through Babylon's own includes, bound on every draw, and switched in place, on
|
|
99
|
+
the same material, when the configuration changes what the shader needs. A post process that applies it gets the colour in
|
|
100
|
+
linear space. It runs on the straight colour after fog and before the blend resolve, so alpha
|
|
101
|
+
stays coverage; particles that multiply the frame (`ALPHA_MULTIPLY`) are a factor rather than
|
|
102
|
+
light and are not tone mapped. Systems that differ are batched apart. On WebGPU, a 2D colour
|
|
103
|
+
grading texture works through a WGSL port of Babylon's slice sampler, which Babylon's own WGSL
|
|
104
|
+
include lacks. The Image Processing demo compares a flame with and without it.
|
|
105
|
+
|
|
106
|
+
- **Cone Emit from Volume and Length** (Unity's Cone Volume): `ConeEmitter.emitFrom`
|
|
107
|
+
(`ConeEmitFrom.Base`, the default, or `ConeEmitFrom.Volume`) and `ConeEmitter.length` (5 by
|
|
108
|
+
default, as in Unity). In Volume, particles start anywhere on their side line from the base up
|
|
109
|
+
to `length` along z. The Unity exporter writes Cone Volume with its Length instead of warning,
|
|
110
|
+
and the editor's Shape module and wireframe show both.
|
|
111
|
+
- **Random Between Two Curves** (Unity's MinMaxCurve mode of the same name): `RandomBetweenCurves`
|
|
112
|
+
holds two `PiecewiseBezier` curves and rolls each particle a mix between them, kept for its
|
|
113
|
+
life, so every particle follows its own curve within the band. It goes wherever a curve does
|
|
114
|
+
(start values over the system's cycle, emission rates, every over-lifetime and by-speed
|
|
115
|
+
behavior, Noise) and reads and writes as `{type: 'RandomBetweenCurves', curve1, curve2}`. Where
|
|
116
|
+
a system-level setting takes it (emission over time or distance, a burst's count, a shape's
|
|
117
|
+
speed, Noise's scroll speed) the mix is rolled once per play, as a random range is there, rather
|
|
118
|
+
than at each emission step as in Unity. A bound that is missing or not a curve warns and reads as
|
|
119
|
+
a flat 0 curve. The Unity exporter writes it instead of the upper curve, and the editor offers it
|
|
120
|
+
in its value fields and in every module that edits a curve over life.
|
|
121
|
+
- **Grid sampling** (Niagara's Grid Location by particle index): `GridEmitter.sampling`
|
|
122
|
+
(`GridSampling.Random`, the default, or `GridSampling.Sequential`). Sequential fills the cells in
|
|
123
|
+
turn, row by row from −x, −y, so a burst of `row × column` particles puts one in every cell; a
|
|
124
|
+
restart starts over at the first cell. The editor's Shape module offers it.
|
|
125
|
+
- **Separate Axes for Limit Velocity over Lifetime** (Unity's option of the same name):
|
|
126
|
+
`LimitSpeedOverLife.speed` takes a `Vector3Function`, a limit per axis, and the new
|
|
127
|
+
`LimitSpeedOverLife.space` (`'local'`, the default, or `'world'`) says which axes they are
|
|
128
|
+
measured along. Each component of the velocity is limited on its own and keeps its sign; a world
|
|
129
|
+
limit is scaled by the system's `motionScale` in a world-space system. `space` is written only
|
|
130
|
+
when not `'local'`. One limit for the speed works as before. The editor's Limit Speed, Size over
|
|
131
|
+
Lifetime and Size by Speed modules get a Separate Axes switch, and the new Limit Velocity Axes
|
|
132
|
+
demo compares the two.
|
|
133
|
+
- **Stop Action** (Unity's option of the same name): `ParticleSystem.stopAction` says what a
|
|
134
|
+
finished system does: nothing (`'none'`, the default), disable its emitter node (`'disable'`,
|
|
135
|
+
once per finish), or dispose itself (`'destroy'`). Unity's Callback is the `finished` event,
|
|
136
|
+
which fires whatever the action. `ignoreStopAction`, runtime only, skips it for tools that replay
|
|
137
|
+
an effect; `EffectPool` and the editor set it, so pooled and previewed effects keep their
|
|
138
|
+
authored stop action. `QuarksUtil.setStopAction` sets it on a whole effect. As in Unity, the
|
|
139
|
+
action waits until the systems the system emits into have no particles left, and a sub emitter
|
|
140
|
+
target takes none of its own. `ParticleSystem.isAlive(withSubEmitters = true)` is Unity's
|
|
141
|
+
`IsAlive`. quarks.core's `IParticleSystem` gains `stopAction` (`autoDestroy` is deprecated),
|
|
142
|
+
`ringBufferActive`, and `hasPendingEmission(state?)` for a sub emission's state.
|
|
143
|
+
- **Ring Buffer Mode** (Unity's option of the same name): with `maxParticles`, a full system's
|
|
144
|
+
new particles replace the oldest instead of being dropped. `ringBufferMode: 'pauseUntilReplaced'`
|
|
145
|
+
freezes a particle at the end of its life until then (footprints, decals);
|
|
146
|
+
`'loopUntilReplaced'` replays the part of its life `ringBufferLoopRange` names (0 to 1 by
|
|
147
|
+
default); a range of no length holds the particle at that point. Particles of a ring buffer
|
|
148
|
+
never die by age, so the system keeps them, fires no Death sub emitter for them and never
|
|
149
|
+
finishes. Lowering `maxParticles` drops the oldest over the new limit. Every render mode but
|
|
150
|
+
Trail, whose particles always die. The Ring Buffer demo shows both.
|
|
151
|
+
- `EmitterFrame.fromSimulation`, the inverse of `toSimulation`, for behaviors that measure a
|
|
152
|
+
vector in their own space.
|
|
153
|
+
|
|
154
|
+
### Fixed
|
|
155
|
+
|
|
156
|
+
- A **Death sub emitter** fired for the particles that died on a non-looping system's last step
|
|
157
|
+
queued its burst and never emitted it: the system counted itself finished first. A system now
|
|
158
|
+
finishes once the emissions its sub emitters run have nothing left to emit, so `finished` fires
|
|
159
|
+
after those bursts.
|
|
160
|
+
- A local-space force, orbit or per-axis speed limit under an emitter scaled to 0 on an axis read
|
|
161
|
+
a NaN rotation from it, and turned the velocity into NaN (forces) or stopped limiting
|
|
162
|
+
(limits). `EmitterFrame` rebuilds the rotation from the axes that remain.
|
|
163
|
+
- **Noise** rolls a random Frequency, Strength, Position or Rotation amount, or Remap (a range or
|
|
164
|
+
two curves) into every particle. Only the first particle got its own value; the others read a
|
|
165
|
+
missing one and their position became NaN.
|
|
166
|
+
|
|
167
|
+
## [0.28.0] — 2026-10-23
|
|
168
|
+
|
|
169
|
+
The release after phase 6 of the audit and the 0.28 cleanup: alpha means coverage in every blend
|
|
170
|
+
mode, as in Unity; shapes fill like Unity's; `play()`, `resume()` and the `QuarksPrefab` timeline
|
|
171
|
+
behave like Unity's `Play` and `PlayableDirector`; the renderer's transform and the camera layers
|
|
172
|
+
no longer change what is drawn; effect files hold only what differs from the defaults; and no
|
|
173
|
+
`any` is left in the public types. The Unity exporter is out of experimental, and the editor gets a
|
|
174
|
+
headless `createEditorSession` and WebGPU. 0.27.1 was never published to npm: its packaging changes
|
|
175
|
+
(below) ship in this release.
|
|
176
|
+
|
|
177
|
+
### Upgrading from 0.27
|
|
178
|
+
|
|
179
|
+
Most effects load and play unchanged. Check these, in this order:
|
|
180
|
+
|
|
181
|
+
1. **Effects under additive, premultiplied, screen, subtract or max blending whose alpha falls
|
|
182
|
+
below 1** now fade with it. If one looks dimmer than you want, raise its alpha or its colour; a
|
|
183
|
+
texture authored premultiplied should be re-exported with straight alpha. Multiply and min
|
|
184
|
+
particles lighten toward white as their alpha falls instead of staying dark.
|
|
185
|
+
2. **Fog follows the blend mode.** Subtract and screen effects fade toward black with distance, like
|
|
186
|
+
additive ones, and multiply effects toward white; only those that lay over the frame still mix
|
|
187
|
+
toward the fog colour.
|
|
188
|
+
3. **Soft particles** fade linearly instead of quadratically under `ALPHA_ADD` and
|
|
189
|
+
`ALPHA_COMBINE`; re-tune `softFarFade` if a soft edge looks different.
|
|
190
|
+
4. **Shapes with a thickness above 0, cones, and grids** spread particles differently (evenly over
|
|
191
|
+
the volume, every cone particle at its start speed, grid cells centred). Re-tune an effect that
|
|
192
|
+
relied on the old crowding at the centre.
|
|
193
|
+
5. **Behaviors no longer reset at every loop.** A looping `Noise` or `TurbulenceField` keeps going
|
|
194
|
+
instead of jumping back to its start once a loop, and the sub emissions of particles still alive
|
|
195
|
+
carry on into the next loop.
|
|
196
|
+
6. **`play()` on a finished system restarts it.** To undo a pause and leave a finished system
|
|
197
|
+
finished, call `resume()` (or `QuarksUtil.resume(root)`).
|
|
198
|
+
7. **`QuarksPrefab`'s end lets the particles die out.** Call `stop()` yourself where you want them
|
|
199
|
+
cleared on the spot. `update()` with no argument now steps by the engine's frame time, and
|
|
200
|
+
`autoUpdate` drives the timeline for you. A prefab saved with a `timeScale` other than 1 now
|
|
201
|
+
plays its systems at that speed, and `addParticleSystemAnimation` no longer pauses the prefab.
|
|
202
|
+
8. **Move emitters, not the `BatchedRenderer`.** Its transform no longer moves the particles.
|
|
203
|
+
9. **Camera layers count for culling.** Only systems with a `cullingMode` other than the default
|
|
204
|
+
`'always'` are affected: one whose `layers.mask` no active camera renders now pauses as its
|
|
205
|
+
`cullingMode` says. Give the camera a `layerMask` that includes it.
|
|
206
|
+
10. **Files are written with only their non-default fields.** babylon.quarks 0.27 and later read
|
|
207
|
+
them; three.quarks and babylon.quarks before 0.27 do not. Keep the old files if a three.quarks
|
|
208
|
+
tool must read them.
|
|
209
|
+
11. **TypeScript and plugins:**
|
|
210
|
+
- `v.isVector2` instead of `Vector2.isVector2`; `setAxisAngleFromRotationMatrix` takes a
|
|
211
|
+
`Matrix4`.
|
|
212
|
+
- JSON readers and `QuarksLoader` take typed JSON or `unknown` (see the `any` items below), and
|
|
213
|
+
a behavior's `toJSON` returns a `BehaviorJSON` with a `type`.
|
|
214
|
+
- A plugin's `loadJSON` must only read its JSON: saving now calls it to find the defaults.
|
|
215
|
+
- `BatchedRenderer.batches` and `systemToBatchIndex` are read-only.
|
|
216
|
+
- A `VFXBatch` subclass implements `resizeBuffers()` and `initialCapacity` and creates its own
|
|
217
|
+
`mesh` in `setupBuffers`.
|
|
218
|
+
- `EulerGenerator.eular` is now `euler` (the old name is a deprecated alias).
|
|
219
|
+
12. **Editor:** a `resolveTexture` callback returns a Babylon `Texture`, and `ValueField` with
|
|
220
|
+
`curve={false}` reports a `ValueGenerator` (see the editor's changelog). Install
|
|
221
|
+
`babylon.quarks-editor` 0.28 with `babylon.quarks` 0.28.
|
|
222
|
+
|
|
223
|
+
### Breaking
|
|
224
|
+
|
|
225
|
+
- **Alpha means coverage in every blend mode.** A particle's alpha (its colour over life, a soft
|
|
226
|
+
fade, erosion, a rim fade, a `tint`) now fades it out whatever `blendMode` it draws with, as
|
|
227
|
+
Unity's particle shaders do for their Alpha, Premultiply, Additive and Multiply modes. Before,
|
|
228
|
+
modes whose blend takes the colour at full weight ignored alpha. What changes on screen:
|
|
229
|
+
- `ALPHA_ONEONE`, `ALPHA_PREMULTIPLIED`, `ALPHA_SCREENMODE`, `ALPHA_SUBTRACT`, `ALPHA_MAXIMIZED`,
|
|
230
|
+
`ALPHA_MAX` and the other modes that take the colour at full weight multiply it by its alpha
|
|
231
|
+
in the shader. An effect whose alpha is below 1 under these modes draws dimmer than before, as
|
|
232
|
+
it would in Unity; one with alpha 1 draws as before. Textures stay straight alpha, as in
|
|
233
|
+
Unity's Premultiply mode: a texture authored premultiplied should be re-exported straight.
|
|
234
|
+
- `ALPHA_MULTIPLY` and `ALPHA_MIN` move the colour toward white as alpha falls, so a fading
|
|
235
|
+
multiply particle lightens away instead of staying dark (Babylon's own particles do the same).
|
|
236
|
+
- The soft-particle fade lowers alpha only. Under `ALPHA_ADD` and `ALPHA_COMBINE` it used to fade
|
|
237
|
+
colour and alpha together, so the blend weighed it twice and the fade looked quadratic; it is
|
|
238
|
+
now linear over the soft distance, as in Unity and Unreal's Depth Fade. An opaque system
|
|
239
|
+
(`transparent: false`) or one under `ALPHA_INTERPOLATE` or `ALPHA_REPLACE_COLOR` has no
|
|
240
|
+
coverage to fade, so soft particles no longer darken it; they never faded it out either.
|
|
241
|
+
- `ALPHA_MAXIMIZED` takes the colour by alpha twice (its blend weighs it once more), so its fade
|
|
242
|
+
is quadratic; every other coverage mode fades linearly.
|
|
243
|
+
- **Fog follows the blend mode.** Particles that subtract from or screen over the frame fade
|
|
244
|
+
toward black with distance, like additive ones; multiply particles fade toward white, which
|
|
245
|
+
leaves the frame alone; particles that lay over the frame (`ALPHA_COMBINE`,
|
|
246
|
+
`ALPHA_PREMULTIPLIED`, opaque) still mix toward the fog colour. Before, every mode other than
|
|
247
|
+
`ALPHA_ADD` and `ALPHA_ONEONE` mixed toward the fog colour, which fogged a multiply or subtract
|
|
248
|
+
effect into a coloured haze.
|
|
249
|
+
- **`Vector2.isVector2` is an instance flag**, set on the prototype as `isVector3` and `isVector4`
|
|
250
|
+
are, instead of a static field. Read it on a vector (`v.isVector2`); `Vector2.isVector2` on the
|
|
251
|
+
class is gone.
|
|
252
|
+
- **`Vector4.setAxisAngleFromRotationMatrix` takes a `Matrix4`**, the matrix it has always read
|
|
253
|
+
(see Fixed). Code that passed a `Matrix3` no longer compiles; it got a wrong axis and angle.
|
|
254
|
+
- **Shape thickness fills evenly, as in Unity.** A sphere's or hemisphere's Radius Thickness now
|
|
255
|
+
spreads particles evenly over the volume of the shell, and a circle's and donut tube's over its
|
|
256
|
+
area (the cone already did); a rectangle spreads each edge's particles evenly over its band. Before, the radius was rolled linearly, so a
|
|
257
|
+
full sphere put half its particles within half its radius instead of an eighth. Effects with a
|
|
258
|
+
thickness above 0 look less crowded at the centre.
|
|
259
|
+
- **Cone particles all start at their start speed.** The direction tilted toward the rim was not
|
|
260
|
+
normalised, so particles inside the base disc moved slower than the rim's, by up to 3 % at the
|
|
261
|
+
default 30° and up to 24 % at 90°.
|
|
262
|
+
- **A grid emits from its cells' centres**, symmetric about the origin: a grid of width 2 with two
|
|
263
|
+
columns emits from x = ±0.5 instead of −1 and 0.
|
|
264
|
+
- Every shape's `fromJSON` takes a `ShapeJSON` instead of `any`, `ShapeJSON.speed` is a
|
|
265
|
+
`FunctionJSON`, and `ValueGenerator.startGen` and `genValue` take a `GeneratorMemory`.
|
|
266
|
+
- `EmitterShapes` lists the fields its shapes have: the grid's `row` (it said `rows`), no `angle`
|
|
267
|
+
on the sphere and hemisphere, the box's `emitFrom` as the new `'boxEmitFrom'` field type, and a
|
|
268
|
+
`transform` (the new `'shapeTransform'` type) on every shape.
|
|
269
|
+
- **Files hold only the shape and behavior fields that differ from their defaults.** A system's
|
|
270
|
+
shape and each behavior are written through the new `EmitterToJSON` and `BehaviorToJSON`, which
|
|
271
|
+
leave out every field equal to the default their type reads a missing field as, as the system's
|
|
272
|
+
own settings already were; a field the type needs, such as a behavior's curve, stays. babylon.quarks
|
|
273
|
+
0.27 and later read these files; **three.quarks and babylon.quarks before 0.27 no longer do**.
|
|
274
|
+
`toJSON()` on a shape or behavior still writes every field.
|
|
275
|
+
- `Behavior.toJSON()` returns the new `BehaviorJSON` (a `type` and the settings) instead of `any`. A
|
|
276
|
+
behavior whose `toJSON` was declared to return something without a `type` no longer compiles.
|
|
277
|
+
`EmitterShapePlugin.loadJSON` and `BehaviorPlugin.loadJSON` are declared to take `ShapeJSON` and
|
|
278
|
+
`BehaviorJSON`, as methods, so a plugin that types its own JSON still compiles; they must only
|
|
279
|
+
read, since saving calls them to find the defaults.
|
|
280
|
+
- **`play()` on a finished system plays it again**, as Unity's `Play` does: it restarts it from
|
|
281
|
+
the beginning. Before, it only unpaused, and a finished system stayed empty. A paused or stopped
|
|
282
|
+
system still resumes where it was. `QuarksUtil.play(root)` does the same for each system, so a
|
|
283
|
+
short burst that had finished inside a longer effect plays again; to undo a pause without that,
|
|
284
|
+
use the new `resume()` and `QuarksUtil.resume(root)`.
|
|
285
|
+
- **Behaviors no longer reset at every loop.** A looping system called each behavior's `reset()`
|
|
286
|
+
as it began its next loop, so a `Noise` or `TurbulenceField` jumped back to its start offset
|
|
287
|
+
once a loop and the sub emissions of particles still alive were dropped. `reset()` now runs only
|
|
288
|
+
on `restart()` (and `stop()`), as `Behavior.reset` documents.
|
|
289
|
+
- **`QuarksPrefab` timeline:**
|
|
290
|
+
- The end of the timeline ends the emission of the systems still playing and lets their
|
|
291
|
+
particles die out, stops the clips, stops the prefab and rewinds it; it used to call `stop()`,
|
|
292
|
+
which cleared every particle on the spot. Call `stop()` yourself for that.
|
|
293
|
+
- A track's window excludes its end in `update` too, as `setTime` did: a system whose track ends
|
|
294
|
+
at 0.5 s stops emitting at 0.5 s, not one frame later.
|
|
295
|
+
- `update()` with no argument advances by the engine's frame time (game time) instead of the
|
|
296
|
+
wall clock.
|
|
297
|
+
- `addParticleSystemAnimation` no longer pauses the prefab.
|
|
298
|
+
- A looping track that ends with the timeline does not start over, and one restarted earlier
|
|
299
|
+
ends its emission with the timeline too.
|
|
300
|
+
- `timeScale` now speeds up and slows down the particle systems on the timeline (see Added), and
|
|
301
|
+
a negative, NaN or infinite one is read as 0. A prefab saved with a `timeScale` other than 1
|
|
302
|
+
plays its systems at that speed now.
|
|
303
|
+
- `pause()` then `play()` resumes exactly the systems the pause paused, those dying out after
|
|
304
|
+
their window included (they stayed frozen before), and `setTime` on a paused prefab leaves the
|
|
305
|
+
systems it starts paused.
|
|
306
|
+
- `ParticleEmitter.toJSON` writes `visible` from the emitter's own enabled flag, as `clone()` and
|
|
307
|
+
`serializeEffect` do, not from its parents': an emitter under a disabled template is saved
|
|
308
|
+
visible.
|
|
309
|
+
- `retainEffectResources` takes the effect's materials as any iterable, and counts holders of a
|
|
310
|
+
material as it does of a texture.
|
|
311
|
+
- **The renderer's transform no longer moves particles.** The batch meshes stay under the
|
|
312
|
+
`BatchedRenderer` node (disabling or disposing it still hides or frees them), but their world
|
|
313
|
+
matrix is frozen at identity: particles are drawn where their emitters put them, wherever the
|
|
314
|
+
renderer is, and the batch bounds Babylon culls by are the particles' own. Before, a moved,
|
|
315
|
+
turned or scaled renderer moved every particle again and only warned once; the warning is gone.
|
|
316
|
+
- `BatchedRenderer.batches` and `systemToBatchIndex` are read-only (`ReadonlyArray`,
|
|
317
|
+
`ReadonlyMap`); the renderer has always owned them, and code that wrote to them no longer
|
|
318
|
+
compiles; assigning `batches` throws at runtime.
|
|
319
|
+
- **Culling only counts the cameras that render a system's layer**: a camera whose `layerMask`
|
|
320
|
+
shares no bit with the system's `layers.mask` no longer keeps it simulating, so a system on a
|
|
321
|
+
layer only an inactive camera renders pauses as its `cullingMode` says. The `CullingMode` docs
|
|
322
|
+
say which cameras are tested, and that one rendering into a render target texture is not.
|
|
323
|
+
- A `VFXBatch` subclass implements `resizeBuffers()` and `initialCapacity`, which the shrinking
|
|
324
|
+
instance buffers call, and creates `mesh` in `setupBuffers`; the base class no longer makes one.
|
|
325
|
+
- **quarks.core's JSON and data types have no `any` left.** What changes for code that
|
|
326
|
+
compiles against them:
|
|
327
|
+
- `FunctionJSON` is the union of each generator's JSON (`ConstantValueJSON`, `GradientJSON`,
|
|
328
|
+
`EulerJSON` and the rest, grouped as `ValueGeneratorJSON`, `ColorGeneratorJSON`,
|
|
329
|
+
`RotationGeneratorJSON` and `Vector3GeneratorJSON`), with `type` as the tag. It was an open
|
|
330
|
+
record of `any`. Each generator's `toJSON` and `fromJSON` use its own type, and the
|
|
331
|
+
`…GeneratorFromJSON` readers take their group's. A generator class of your own that
|
|
332
|
+
implements `ValueGenerator` or a sibling interface writes one of these, or casts its `toJSON`;
|
|
333
|
+
no reader would know its type anyway.
|
|
334
|
+
- `BehaviorJSON` is `{type: string}`, no longer an open record, and every built-in behavior has its
|
|
335
|
+
own JSON type (`ColorOverLifeJSON`, `EmitSubParticleSystemJSON`, …) for its `toJSON` and
|
|
336
|
+
`fromJSON`. A plugin's `loadJSON` may take its own JSON interface with a `type`, as
|
|
337
|
+
`examples/src/demos/customPlugin.ts` does. An object literal passed to `BehaviorFromJSON` is
|
|
338
|
+
typed as its behavior's JSON, e.g. `const json: NoiseJSON = {type: 'Noise', …}`.
|
|
339
|
+
- `GeneratorMemory` is `number[]`. `ContinuousLinearFunction`'s values are `number`, `Vector3` or
|
|
340
|
+
`Vector4`, its `subType` follows from them, and its `fromJSON` returns the matching type for
|
|
341
|
+
the `subType`. `GradientJSON`'s `color` and `alpha` say their `subType`.
|
|
342
|
+
- `IEmitter.matrixWorld` is a `Matrix4`; `IParticleSystem.toJSON` and `IAnimationData.target` are
|
|
343
|
+
`unknown`, which a renderer narrows; `Constructable` takes `never[]` arguments.
|
|
344
|
+
- `EmitSubParticleSystem.subParticleSystem` is declared `IEmitter | string | undefined`: a loaded
|
|
345
|
+
behavior holds the uuid until the loader links it, as it always has.
|
|
346
|
+
- `Sequencer.toJSON` returns `SequencerJSON`; `Euler.fromArray` and `toArray` take numbers and an
|
|
347
|
+
order.
|
|
348
|
+
- In babylon.quarks, `ParticleSystemJSONParameters.rendererEmitterSettings` is the new
|
|
349
|
+
`RendererEmitterSettingsJSON`: a trail's `TrailSettingsJSON` or a stretched billboard's
|
|
350
|
+
settings.
|
|
351
|
+
- The `isVector3`-style flags are set on the prototypes as non-enumerable, read-only properties,
|
|
352
|
+
so they no longer show up in a `for…in` over a vector.
|
|
353
|
+
- **babylon.quarks's effect format and loader have no `any` left either.** The parts of an effect
|
|
354
|
+
file have named types: `EffectDocumentJSON` for what `QuarksLoader` reads, with
|
|
355
|
+
`QuarksMaterialJSON`, `EffectGeometryJSON` (quarks' own, three.js `BufferGeometry`,
|
|
356
|
+
`PlaneGeometry` and `SphereGeometry`), `EffectImageJSON`, `QuarksPrefabJSON`,
|
|
357
|
+
`ParticleEmitterJSON` and the rest. What changes for code that compiles against them:
|
|
358
|
+
- `QuarksLoader`'s `loadFromJSON`, `parse`, `resolveResources`, `resolveLinkedResources` and
|
|
359
|
+
`resolveExternalGeometries` take the document as `unknown`, as parsed from its file. `resolveLinkedResources` takes the new `QuarksResolveOptions`.
|
|
360
|
+
- `ParticleSystem.material` and `ParticleSystemParameters.material` are a
|
|
361
|
+
`ParticleMaterialSource`: the fields a system reads from a Babylon material or a loaded
|
|
362
|
+
material record. Any Babylon material fits.
|
|
363
|
+
- `BabylonMetaData` holds `BaseTexture | null` textures, `QuarksMaterialJSON` materials and
|
|
364
|
+
`GeometryEntry` geometries; its unused maps are `unknown`.
|
|
365
|
+
- `QuarksNodeJSON` lists its fields instead of an open record, `matrix` is optional, `visible`
|
|
366
|
+
is a boolean, and it has the prefab and three.js transform fields the loader reads.
|
|
367
|
+
`QuarksEffectJSON.materials` is `QuarksMaterialJSON[]`.
|
|
368
|
+
- `ParticleSystemJSONParameters` types its generators by group (`ValueGeneratorJSON`,
|
|
369
|
+
`ColorGeneratorJSON`, …), its `shape` as `ShapeJSON`, its `behaviors` as `BehaviorJSON[]`,
|
|
370
|
+
and `instancingGeometry` as a uuid or an entry. `BurstParametersJSON` has the older
|
|
371
|
+
`cycleCount`.
|
|
372
|
+
- `QuarksPrefab.toJSON` returns a `QuarksPrefabJSON`, `ParticleEmitter.toJSON` a
|
|
373
|
+
`ParticleEmitterJSON`, and `MeshSurfaceEmitter.fromJSON` takes a `ShapeJSON`.
|
|
374
|
+
- quarks.core's `JsonMetaData.textures` may hold null.
|
|
375
|
+
|
|
376
|
+
### Deprecated
|
|
377
|
+
|
|
378
|
+
- `RecordState`, the sample of the old linked-list trail history. Trails record into the flat ring
|
|
379
|
+
buffer of `TrailParticle` (`historyPositions` and its siblings); nothing fills or draws the list.
|
|
380
|
+
- `TrailParticle.previous`, that list, is `@internal` and out of the documented API.
|
|
381
|
+
- `MeshSettings.startRotationX`, `startRotationY` and `startRotationZ`, which nothing reads, are
|
|
382
|
+
optional and deprecated; a mesh particle's start rotation about each axis is the system's
|
|
383
|
+
`startRotation`, an `EulerGenerator`.
|
|
384
|
+
- `EulerGenerator.eular`, a misspelling: the Euler is `EulerGenerator.euler`, and `eular` is an
|
|
385
|
+
alias of it.
|
|
386
|
+
|
|
387
|
+
### Added
|
|
388
|
+
|
|
389
|
+
- **`blendCategory(settings)`** says how a blend mode's colour leaves the shader (`'straight'`,
|
|
390
|
+
`'premultiply'` or `'multiply'`) and where fog takes it (`'color'`, `'black'` or `'white'`), with
|
|
391
|
+
the `BlendCategory`, `BlendOutput` and `FogTarget` types. Opaque materials, `ALPHA_INTERPOLATE`,
|
|
392
|
+
`ALPHA_REPLACE_COLOR` and `ALPHA_DUAL_SRC0_ADD_SRC1xDST` have no coverage and stay `'straight'`.
|
|
393
|
+
- The shader variants cover every blend resolve with and without fog and soft particles, so
|
|
394
|
+
`validate:shaders` compiles the new `BLEND_PREMULTIPLY`, `BLEND_MULTIPLY` and `FOG_MULTIPLY`
|
|
395
|
+
defines in GLSL and WGSL.
|
|
396
|
+
- **The whole simulation core is documented.** Every vector, quaternion, matrix and math helper,
|
|
397
|
+
every shape, value, colour and rotation generator, behavior field and plugin type re-exported from
|
|
398
|
+
`quarks.core` now carries TSDoc, and `npm run docs:check` fails on one that does not, as it does
|
|
399
|
+
for the rest of the package.
|
|
400
|
+
- `ContinuousLinearFunction` (the keys of a `Gradient`) and its `ObjectValueType` are exported, and
|
|
401
|
+
`Bezier` has `BezierJSON` and `BezierSplit` types for what `toJSON` and `split` return.
|
|
402
|
+
- **Format warnings name the part of the file they are about**, by its JSON path
|
|
403
|
+
(`object.children[2].ps.behaviors[3]`). `QuarksLoaderOptions.onFormatWarning` receives them
|
|
404
|
+
instead of the console, and `QuarksLoaderOptions.strict` makes a load throw `QuarksFormatError`
|
|
405
|
+
for them, after reading the whole effect and freeing what it built, with every part in its
|
|
406
|
+
`problems`, to check in CI that a set of assets loads cleanly. The
|
|
407
|
+
loader's own format warnings (a newer format, an unresolved sub emitter or mesh surface link, an
|
|
408
|
+
image url that is not an image) go the same way. `readFormat`, `readFormatPart`,
|
|
409
|
+
`currentFormatPath`, `warnFormat` and `QuarksFormatError` (now defined in `quarks.core`) give plugin
|
|
410
|
+
readers and other hosts the same.
|
|
411
|
+
- `BehaviorTypes.EmitSubParticleSystem` lists `inherit`, with the new `'subEmitterInherit'` field
|
|
412
|
+
type.
|
|
413
|
+
- **`EffectPool.acquire` takes a placement** (`EffectPlacement`: `position`, `rotation` or
|
|
414
|
+
`rotationQuaternion`, `scaling`, `parent`), so a hit can face its surface normal or a muzzle
|
|
415
|
+
flash its barrel. Every acquire first puts the copy back to the template's transform, so a
|
|
416
|
+
rotation or scale from its last use does not stay. `acquire(position, parent)` still works, with
|
|
417
|
+
any vector that has `x`, `y` and `z`.
|
|
418
|
+
- **`ParticleSystem.timeDilation`**, a runtime multiplier on `playbackSpeed` that is not part of
|
|
419
|
+
the effect (never saved, cloned or loaded), like Unreal's Custom Time Dilation or Unity's
|
|
420
|
+
`VisualEffect.playRate`: for slow motion, a pause menu, or a timeline.
|
|
421
|
+
- **`QuarksPrefab.timeScale` reaches the systems**: it sets the `timeDilation` of each track's
|
|
422
|
+
system, so they slow down and speed up with the timeline; it is normalised like
|
|
423
|
+
`playbackSpeed`. Removing a system's last track sets it back to 1. Clips keep their own speed.
|
|
424
|
+
- **`ParticleSystem.resume()` and `QuarksUtil.resume(root)`** undo a pause and leave a finished
|
|
425
|
+
system finished, for a pause button over a scene; the editor's transport uses them.
|
|
426
|
+
- **`QuarksPrefab.autoUpdate`** advances the timeline before every render by the engine's frame
|
|
427
|
+
time, as a Unity `PlayableDirector` on Game Time does; it is turned off on dispose.
|
|
428
|
+
- `removeAllEventListeners()` with no event type removes every listener.
|
|
429
|
+
- A **Timeline** demo: a charge, a release burst on a window shorter than a frame and lingering
|
|
430
|
+
smoke on a `QuarksPrefab`, every other blast in slow motion.
|
|
431
|
+
- **`renderFace`** on a system (`'both'`, `'front'`, `'back'`; the new `RenderFace` type), Unity's
|
|
432
|
+
Render Face and Unreal's Two Sided: a mesh particle can cull its back faces, or show only its
|
|
433
|
+
inside. `'both'` by default, as before; written to JSON only when set; mesh systems that differ
|
|
434
|
+
are batched apart; the flat render modes (billboards, stretched, beam, trail) always draw both
|
|
435
|
+
faces. The culling is per batch, so a mesh mirrored by a negative size on an odd number of axes
|
|
436
|
+
swaps which side `'front'` and `'back'` show. The editor has a Render face field for mesh systems.
|
|
437
|
+
|
|
438
|
+
### Fixed
|
|
439
|
+
|
|
440
|
+
- `new Gradient()` with no keys is opaque white from 0 to 1, as Unity's default gradient is. Its two
|
|
441
|
+
colour keys were both at 0 (black, then white), so it already looked white after the start; now
|
|
442
|
+
its keys span the range.
|
|
443
|
+
- `PiecewiseBezier.toSVG` returns an empty path for a curve with no pieces instead of throwing.
|
|
444
|
+
- A shape transform's rotation is read as a unit quaternion, and a zero one as the identity, so a
|
|
445
|
+
hand-written or rounded rotation no longer scales the shape.
|
|
446
|
+
- `Rotation3DOverLife` no longer allocates a quaternion for each mesh particle it starts.
|
|
447
|
+
- `BatchedRenderer.addSystem` throws for a system of another scene instead of building a batch
|
|
448
|
+
that holds two scenes' meshes and materials.
|
|
449
|
+
- `configureAdaptivePerformance` keeps `minQuality` at 0.1 or more, the floor
|
|
450
|
+
`ParticleSystem.setQualityFactor` clamps to and the documented range; it allowed 0.05.
|
|
451
|
+
- The 1×1 white map mesh batches share is made again after something outside disposes it, instead
|
|
452
|
+
of binding a disposed texture.
|
|
453
|
+
- An env atlas that fails to build from a cube's faces warns once instead of once per system: it
|
|
454
|
+
is not tried again until the cube's faces change or `disposeEnvAtlas` is called for the cube.
|
|
455
|
+
- `ApplySequences` keeps each particle's place in a `WeakMap` instead of fields added to the
|
|
456
|
+
particle, reuses that state when a pooled particle is emitted again instead of allocating two
|
|
457
|
+
vectors, and moves a particle straight to its point when a sequence's range has no length (it
|
|
458
|
+
divided by zero).
|
|
459
|
+
- An `EmitSubParticleSystem` read from a file without `useVelocityAsBasis` saves without it, as a
|
|
460
|
+
default, instead of writing `false`.
|
|
461
|
+
- `PiecewiseBezier.toSVG` draws the curve itself, not its sampled table.
|
|
462
|
+
- A `Gradient` read from JSON without its `color` or `alpha` keys warns, and keeps the default for
|
|
463
|
+
the missing part.
|
|
464
|
+
- A particle system reads its texture from the first material slot that holds a `Texture`; a cube
|
|
465
|
+
or other non-`Texture` in a slot is passed over instead of drawn as a broken 2D texture.
|
|
466
|
+
- A list of an effect file (`images`, `textures`, `materials`, `geometries`, a node's `children`)
|
|
467
|
+
that is not a list, or an entry in one that is not an object, warns and is skipped, like any
|
|
468
|
+
other part the loader cannot read, instead of failing the load with a `TypeError`.
|
|
469
|
+
- `ParticleSystem.fromJSON` with a material entry whose `reflectionAtlas` is a texture uuid
|
|
470
|
+
resolves it, as it does the other slots, instead of failing on the string.
|
|
471
|
+
- The fragment's `alpha < 0.01` discard is right in every blend mode: under one-one or
|
|
472
|
+
premultiplied blending a discarded fragment used to be one that would still have drawn.
|
|
473
|
+
- Erosion and a mesh's rim fade no longer show a hard cut under one-one and premultiplied
|
|
474
|
+
blending; they fade like under alpha blending.
|
|
475
|
+
- **`Bezier.split(t)` split the curve at `1 - t`.** Its de Casteljau steps weighted the two points
|
|
476
|
+
the wrong way round, so the halves met at the mirrored point; a curve split in the editor away
|
|
477
|
+
from a segment's middle changed shape. It now splits at `t`, and both halves trace the curve.
|
|
478
|
+
- `Matrix3.makeTranslation(vector)` read a `Vector2` as a number and built a broken matrix; it
|
|
479
|
+
checked a flag that `Vector2` only had as a static.
|
|
480
|
+
- `Vector4.setAxisAngleFromRotationMatrix` is typed to take the `Matrix4` it reads (as three.js
|
|
481
|
+
does); it was typed `Matrix3` and read a 3×3 matrix's entries from the wrong places.
|
|
482
|
+
- Shapes read out-of-range settings safely: a thickness is clamped to [0, 1] (NaN reads as 1), a negative radius,
|
|
483
|
+
width or height is read as 0, a cone's angle is clamped to [0, π/2] and a box's thickness to 1, a
|
|
484
|
+
grid's rows and columns are read as whole numbers of at least 1 (0 or Infinity gave NaN
|
|
485
|
+
positions), and a
|
|
486
|
+
short `boxThickness` array reads its missing axes as 0.
|
|
487
|
+
- A donut with no tube, and a rectangle with no size, started particles with no speed; they head
|
|
488
|
+
out from the centre line, or along the edge's normal, at their start speed.
|
|
489
|
+
- `AxisAngleGenerator` normalises its axis when it rotates (a zero axis is read as +z): an axis of
|
|
490
|
+
any other length gave a quaternion that was not a unit and skewed mesh particles.
|
|
491
|
+
- `RandomQuatGenerator` and `RandomColorBetweenGradient` used before `startGen` read slots that
|
|
492
|
+
belong to other generators, and the gradient mix rolled a new value every frame, flickering; both
|
|
493
|
+
now roll their own slots once, as `IntervalValue` does.
|
|
494
|
+
- `PiecewiseFunction.insertFunction` at a `t` outside every piece inserted at the front and
|
|
495
|
+
unsorted the pieces; it inserts in order and clamps `t` to [0, 1].
|
|
496
|
+
- A circle or rectangle in `Random` mode no longer samples its sweep speed every frame, as the
|
|
497
|
+
other shapes do not; a random sweep speed no longer draws its roll, which shifts a seeded
|
|
498
|
+
effect's random sequence by one draw.
|
|
499
|
+
- Shape docs give the units and axes of `arc` (radians around +z from +x), the ranges of every
|
|
500
|
+
size and thickness, and say that `RectangleEmitter` emits from the outline (Unity's Rectangle is a
|
|
501
|
+
`BoxEmitter` with no depth); the donut's `donutRadius` is the tube's radius, not the hole's.
|
|
502
|
+
- `docs/FORMAT.md` gave 0.28 as the release of format version 1; 0.27.0 is. It now says how a
|
|
503
|
+
system's blend mode is read when its material entry, or the system, has none, and that
|
|
504
|
+
`ps.version` is written for three.quarks only.
|
|
505
|
+
- **`QuarksPrefab` plays a window a single frame steps over.** A track shorter than a frame (a
|
|
506
|
+
release burst, say) was never entered when one update jumped from before its start to after its
|
|
507
|
+
end; its system now restarts and ends its emission once it has simulated a step (checked on
|
|
508
|
+
each update and after each render), and its clip plays and stops.
|
|
509
|
+
- **An `EffectPool` never hands out a disposed copy.** A copy disposed from outside, by its own
|
|
510
|
+
`dispose()` or with a parent it was placed under, leaves the pool, which makes a new one when
|
|
511
|
+
it needs it; before, `acquire` handed out the disposed node and it never came back.
|
|
512
|
+
- **`QuarksUtil.instantiate` copies meshes as meshes**, sharing the source's geometry and
|
|
513
|
+
material, where it made plain transform nodes of them; an instance of a mesh in the tree becomes
|
|
514
|
+
an instance of its copy, and a `MeshSurfaceEmitter` whose mesh is in the copied tree emits from
|
|
515
|
+
the copy. The copy holds the source's materials as it held its
|
|
516
|
+
textures, so `disposeEffect(source)` leaves a copy's meshes and materials whole.
|
|
517
|
+
- **`MeshSurfaceEmitter` emits from a mesh without indices**, read as a triangle list, instead of
|
|
518
|
+
from the origin. A stray vertex count past the last whole triangle is ignored.
|
|
519
|
+
- **A listener removed while an event fires no longer makes the next one skip**: the listener
|
|
520
|
+
lists are copied on write, so every listener registered when the event fired is called once,
|
|
521
|
+
and one added during it waits for the next event.
|
|
522
|
+
- `particleDied` allocates nothing per death: one event object is reused, its `particle` set only
|
|
523
|
+
while the listeners run (documented, as is when a trail's death fires).
|
|
524
|
+
- `EmitSubParticleSystem.reset()` drops the running sub emissions, as `ParticleSystem.restart`
|
|
525
|
+
did through a special case it no longer needs; any host gets the same reset.
|
|
526
|
+
- Culling reuses its frustum planes; it allocated a new array per extra camera every frame.
|
|
527
|
+
- **Instance buffers shrink.** A batch whose buffers a burst grew halves them after it has used
|
|
528
|
+
under a quarter of them for 300 renderer updates (about five seconds at 60 fps), down to where
|
|
529
|
+
it started, so one big explosion no longer holds that memory for the rest of the session.
|
|
530
|
+
Trails too.
|
|
531
|
+
- A batch no longer creates a placeholder mesh that it disposes straight away.
|
|
532
|
+
- `SortMode` documents that `'distance'` sorts once a frame, for the first active camera.
|
|
533
|
+
|
|
534
|
+
### Internal
|
|
535
|
+
|
|
536
|
+
- Tests cover the vectors, quaternions, Euler angles, matrices and math helpers, and the coverage
|
|
537
|
+
floors rise: the simulation core's from 54 % to 88 % of lines, the library's and the editor's to
|
|
538
|
+
one point under what the suites reach.
|
|
539
|
+
- Behaviour tests for every shape: even filling, bounds, start speed, the rectangle's outline,
|
|
540
|
+
grid cells, out-of-range settings, JSON round trips of every field and the registry's fields.
|
|
541
|
+
- Golden fixtures in `packages/babylon.quarks/test/fixtures`: effects written by three.quarks, by
|
|
542
|
+
the Unity exporter and by babylon.quarks 0.27.0. Each loads with the warnings its snapshot holds and
|
|
543
|
+
saves to the format it holds, saving is stable, and each 0.27.0 file loads as the one it was
|
|
544
|
+
written from.
|
|
545
|
+
- Loaded node ids and mesh surface links live in module `WeakMap`s instead of `as any` fields on
|
|
546
|
+
nodes and systems.
|
|
547
|
+
- The batch settings, their defaults and the helpers that normalise and compare them move from
|
|
548
|
+
`VFXBatch.ts` to `batchSettings.ts`, which `VFXBatch.ts` re-exports; the batch key reads them
|
|
549
|
+
there, so the build no longer reports a circular import. Exports are unchanged.
|
|
550
|
+
- A clean build and install print no warnings: the core build stops passing `declarationDir`
|
|
551
|
+
without declarations, CI runs on Node 24 with current GitHub Actions, dev dependencies have no
|
|
552
|
+
known vulnerabilities, and the demo smoke test fails on a console warning as well as an error.
|
|
553
|
+
- The toolchain moves to ESLint 10 (with `defineConfig` and react-hooks 7's React Compiler rules),
|
|
554
|
+
TypeScript 6, Vite 8 (Rolldown: the examples' chunks all fit under its 500 kB warning) and
|
|
555
|
+
cross-env 10. Babel stays on 7, which Jest's own preset still needs.
|
|
556
|
+
- No explicit `any` is left in the repository, tests included, and ESLint now reports one as an
|
|
557
|
+
error. Tests reach private members with `obj['member']`, and use typed helpers in
|
|
558
|
+
`test/internals.ts` for shader options, empty resources, fetch responses and values the types
|
|
559
|
+
rule out.
|
|
560
|
+
|
|
561
|
+
## [0.27.1] — 2026-10-23 (not published)
|
|
562
|
+
|
|
563
|
+
Packaging and release checks; the runtime is the same as 0.27.0. It was never published to npm; its
|
|
564
|
+
changes ship in 0.28.0.
|
|
565
|
+
|
|
566
|
+
### Changed
|
|
567
|
+
|
|
568
|
+
- **`@babylonjs/core` peer range is `^9.0.0`** instead of `>=9.0.0`. The package is built and
|
|
569
|
+
tested against Babylon 9; a future major is a separate release, so npm no longer accepts it
|
|
570
|
+
silently. No Babylon 10 exists yet, so no install that worked stops working.
|
|
571
|
+
- **`sideEffects` names the built bundles**, the only files with import-time work (they register
|
|
572
|
+
the mesh-surface emitter with the shape table, so a bare `import 'babylon.quarks'` keeps doing
|
|
573
|
+
that); bundlers may treat everything else in the package as side-effect free.
|
|
574
|
+
- The README says what `require('babylon.quarks')` needs: `@babylonjs/core` is ES modules only, so
|
|
575
|
+
CommonJS consumers need a Node that can `require` ES modules (20.19 or 22.12 and later).
|
|
576
|
+
|
|
577
|
+
### Internal
|
|
578
|
+
|
|
579
|
+
- **`npm run check:pack` checks that the editor moves with the library.** The editor's peer
|
|
580
|
+
dependency on `babylon.quarks` must be the library's own minor line (`~X.Y.0`), and CONTRIBUTING's
|
|
581
|
+
release steps say so: an editor published with an older range would install a babylon.quarks
|
|
582
|
+
without the API it imports.
|
|
583
|
+
- The examples compile with `strict` and are part of `npm run typecheck`, in CI too.
|
|
584
|
+
- The vendored `quarks.core` workspace is marked `private` (it is bundled into babylon.quarks and
|
|
585
|
+
never published on its own), points at this repository, and lists `types` first in its
|
|
586
|
+
`exports`. Its unused Babel and ESLint configs are gone, and so is `composite` in the library's
|
|
587
|
+
`tsconfig.json`, which nothing referenced.
|
|
588
|
+
- The examples dev server accepts only localhost and Cloudflare quick tunnel hosts instead of
|
|
589
|
+
any `Host` header. The repository's Node requirement is 20.19, the version `check:pack`'s
|
|
590
|
+
CommonJS import needs.
|
|
591
|
+
|
|
7
592
|
## [0.27.0] — 2026-10-23
|
|
8
593
|
|
|
9
594
|
The release that follows the October 2026 audit: every domain (emitters, behaviors, system and
|
package/README.md
CHANGED
|
@@ -32,7 +32,9 @@ High-performance particle system for [Babylon.js](https://www.babylonjs.com/). B
|
|
|
32
32
|
npm install babylon.quarks @babylonjs/core
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Peer dependency: `@babylonjs/core`
|
|
35
|
+
Peer dependency: `@babylonjs/core` 9.x. The package ships ES module and CommonJS builds;
|
|
36
|
+
`@babylonjs/core` is ES modules only, so `require('babylon.quarks')` needs a Node that can
|
|
37
|
+
`require` ES modules (20.19 or 22.12 and later). Bundlers and `import` work everywhere.
|
|
36
38
|
|
|
37
39
|
## Quick start
|
|
38
40
|
|