You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+32Lines changed: 32 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
4
4
5
5
This project adheres to [Semantic Versioning](https://semver.org/) and uses [Conventional Commits](https://www.conventionalcommits.org/).
6
6
7
+
## [Unreleased]
8
+
9
+
### Changed (BREAKING)
10
+
11
+
-**`SimulationSpace.WORLD` rewritten** to follow Unity's world-simulation-space model. The previous implementation wrapped the particle system in a `three/examples``Gyroscope`, cancelled the parent rotation, and compensated buffer positions each frame by subtracting the emitter's world movement. The new path stores world coordinates directly in the particle buffer and holds `particleSystem.matrixWorld` at identity so rendering is decoupled from the emitter's scene-graph transform.
12
+
-**Fixed** a gravity direction bug in WORLD mode: gravity is now applied as a constant world vector. Previously it was derived from the emitter's world position treated as a direction vector, causing particles to drift sideways or upward when the emitter moved or rotated.
13
+
-**Fixed** a directional force field bug in WORLD mode: field directions now stay world-aligned regardless of emitter rotation. Previously the direction was pre-rotated by `particleSystem.getWorldQuaternion()`, which did not invoke the Gyroscope's rotation cancellation and therefore picked up the parent rotation.
14
+
-**Fixed** frame-lag jitter on fast-moving emitters (no more `worldPositionChange` subtraction in the integrator, on either the CPU or the GPU compute path).
15
+
-**Fixed** death/birth sub-emitter spawn position on moving emitters: the sub-emitter now spawns at the parent particle's world location, not at the parent emitter's current world position.
16
+
-**Fixed** LOCAL-mode gravity magnitude under scaled parents: gravity is now divided by the emitter's world scale so the rendered fall matches world m/s² regardless of parent scale (Unity parity).
17
+
-**Fixed** WORLD-mode shape-emission spawn offsets not honouring parent scale. Spawn offsets are now multiplied by the emitter's world scale (Unity Shape-module parity with `Scaling Mode = Local/Hierarchy`); live particles remain unaffected by post-spawn scale changes.
18
+
-**Fixed** a stale-matrix bug in LOCAL-mode sub-emitter death/birth callbacks: `particleSystem.updateMatrixWorld()` is now called before `localToWorld(...)` so the sub-emitter spawns at the parent's current world position.
19
+
-**Fixed** a silent CPU→GPU upload regression: the `positionNeedsUpdate` guard no longer flags re-uploads for stationary particles when the emitter moves (`worldPositionChange` conditions removed from the guard, matching the fact that the compensation subtraction was already gone).
20
+
-**Color pipeline standardised to the three.js linear workflow.** User color inputs (`startColor`, `backgroundColor`) and color map textures are now treated as sRGB — the same convention every other three.js material uses. The library decodes to linear on input, shaders operate in linear, and the renderer's output pass converts back to sRGB on the way to the framebuffer. Previously the library wrote raw values to the framebuffer and required consumers to set `renderer.outputColorSpace = LinearSRGBColorSpace`, which broke every non-particle material in the same scene.
21
+
- GLSL fragment shaders (`particle-system`, `instanced-particle`, `mesh-particle`, `trail`) now include `<colorspace_fragment>` so they participate in the renderer's standard color-space conversion.
22
+
- TSL materials no longer force `map.colorSpace = NoColorSpace`; user-tagged sRGB textures get the hardware decode the rest of three.js expects.
23
+
- Per-particle color buffer writes now apply `sRGBToLinear` on user `startColor` values. `colorOverLifetime` multipliers are scalars and keep their existing semantics (applied in linear space).
24
+
-`backgroundColor` uniforms are converted to linear on upload so `discardBackgroundColor` compares against the (now linear) texture sample on equal footing.
25
+
-**Fixed**`discardBackgroundColor` not firing in WebGPU TSL materials. `Discard()` was wrapped inside a TSL `Fn` helper, which prevented the fragment `discard` statement from propagating to the main shader — so black-background cutouts silently stopped working on the POINTS, INSTANCED, and MESH renderers (Shield, Fireworks, Magnetic Field, Implosion, Explosion with Smoke, etc.).
26
+
-**Fixed**`updateConfig({ simulationSpace })` leaving the system in an inconsistent state. Live-switching simulation space now deactivates existing particles (their buffer positions are in the old frame and would render at random locations) and flips `matrixWorldAutoUpdate` to match what `createParticleSystem` would have set for the new frame. Previously the simulationSpace scalar was updated but the buffer and `matrixWorld` flags were not, causing particles to snap between origins and jump around after a LOCAL↔WORLD toggle.
27
+
28
+
### Migration
29
+
30
+
-`ParticleSystem.instance` is now always `THREE.Points | THREE.Mesh` — the union with `Gyroscope` is gone. Any code that checked `instance instanceof Gyroscope` or reached into `instance.children[0]` to find the inner `Points` must use `instance` directly.
31
+
-`ParticleSystemInstance.wrapper` is removed.
32
+
- In WORLD mode, `instance.matrixWorld` is identity. The emitter's pose is still read from the parent chain for emission; `instance.position` / `instance.rotation` act as a spawn-origin offset under the parent and do **not** drag already-emitted particles (matches Unity).
33
+
- The dependency on `three/examples/jsm/misc/Gyroscope.js` is removed.
34
+
- WebGPU-internal uniforms `worldPositionChange` and `simulationSpaceWorld` on the compute pipeline are removed; this only affects code that reached into the compute uniform object directly.
35
+
-**Remove any `renderer.outputColorSpace = LinearSRGBColorSpace` override** that was added for this library. The three.js default (`SRGBColorSpace`) is now correct for both WebGL and WebGPU paths. Leaving the old override in place will double-darken the particles.
36
+
-**`startColor`, `backgroundColor` values are now interpreted as sRGB.**`{ r: 1, g: 0, b: 0 }` renders as "CSS/Photoshop pure red," not as a raw linear 1.0. Configs authored under earlier versions will render slightly differently — to keep the old look exactly, run each color through `linearToSRGB` (exported from the package) once when loading a legacy config. Re-authoring in an updated editor against the new rendering is usually easier.
37
+
-**User color map textures should be tagged `SRGBColorSpace`** (the three.js default when loading via `TextureLoader`). The library no longer overrides this. Non-color / data textures intended as masks should keep `NoColorSpace` as usual.
All RGB values in particle configs (`startColor`, `backgroundColor`) are
246
+
**sRGB** — the same convention used everywhere else in three.js. Pass the
247
+
value a color picker gives you (e.g. `{ r: 1, g: 0, b: 0 }` for pure red)
248
+
and the renderer will display it correctly.
249
+
250
+
Internally the library decodes these to linear for shader math and relies
251
+
on the renderer's standard output pass to convert back to sRGB on the way
252
+
to the framebuffer. No special `outputColorSpace` setup is required; the
253
+
three.js default (`SRGBColorSpace`) works.
254
+
255
+
User-supplied color map textures should also be tagged as sRGB
256
+
(`texture.colorSpace = THREE.SRGBColorSpace`) — this is also the
257
+
three.js default for color textures loaded via `TextureLoader`.
258
+
239
259
### Color Over Lifetime
240
260
241
261
The `colorOverLifetime` feature uses a **multiplier-based approach** (similar to Unity's particle system), where each RGB channel curve acts as a multiplier applied to the particle's `startColor`.
0 commit comments