Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add migration guide and update CHANGELOG #392

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Currently only supports color textures (no depth texture) and only sampling via WGSL `textureSample()`. (#355)
- Added a new `EffectMaterial` component holding the actual textures to bind to the various slots of a `Module`.
- Added a new `Module::add_texture()` function to declare a new texture slot in a module.
- Particle trails and ribbons can now be initialized with init modifiers.

### Changed

- The `ParticleTextureModifier` doesn't directly hold the texture handle to use anymore.
Instead, it holds an expression handle to the texture slot defined in the `Module` of the effect.
This allows dynamically changing the texture sampled.
- `RenderModifier::apply_render()` now returns a `Result<String, ExprError>`.
- Several API changes occurred around the cloning of particles for trails and ribbons. See also the [migration guide (v0.12 -> v0.13)](./docs/migration-v0.12-to-v0.13.md).
- `EffectSpawner` is now wrapped into a new `EffectInitializers`. Each effect group has an `EffectInitializer`, which can either be an `EffectSpawner` (CPU spawning) or an `EffectCloner` (GPU particle cloning).
- `EffectAsset::new()` takes again a single capacity argument for the default first group. Other groups are incrementally added with `with_trails()` and `with_ribbons()`, specifying their respective capacity via those functions.
- The age (`Attribute::AGE`) and lifetime (`Attribute::LIFETIME`) of cloned particles can no longer be assigned manually; instead it's set via an argument to `EffectAsset::with_trails()` and `EffectAsset::with_ribbons()`, and cannot be modified anymore with expressions (and properties in particular).

### Removed

- The `CloneModifier` was removed. Cloning is now controlled by the `EffectCloner`.
- The `RibbonModifier` was removed. Use `EffectAsset::with_ribbons()` to create ribbons.

### Fixed

- Fixed a bug where the generated render shader was declaring a binding as `storage<read>` (read-only)
but the `Spawner` struct contained an `atomic<i32>`, which requires write access.
The `Spawner` struct is now conditionally defining that field as `i32` instead.
- Fixed a race condition in ribbons leading to visual artifacts (particles linked to other unrelated particles). (#376)

## [0.12.2] 2024-08-05

Expand Down
130 changes: 130 additions & 0 deletions docs/migration-v0.12-to-v0.13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Migration Guide v0.12 -> v0.13

🎆 Hanabi v0.13 contains a few major API breaking changes.

This guide helps the user migrate from v0.12 to v0.13 of 🎆 Hanabi.
Users are encouraged to also read the [`CHANGELOG`](../CHANGELOG.md)
for an exhaustive list of all changes.

## Optional serialization and deserialization

Serialization and deserialization have been made optional
under a new feature flag `serde`.
This makes the `typetag` dependency also optional.

If your project relies on serialization and deserialization of 🎆 Hanabi types,
it should work by default out of the box,
because the new `serde` feature flag is active by default.

However, **if you disabled default features,
simply add the `serde` flag to re-enable serialization and deserialization**
of 🎆 Hanabi types.

```diff
- bevy_hanabi = { version = "0.12", default-features = false, features = [...] }
+ bevy_hanabi = { version = "0.13", default-features = false, features = ["serde", ...] }
```

## Trails and ribbons API change

The API for trails and ribbons changed,
primarily to fix a race condition occurring by design of the old API.
This also makes the new API somewhat more understandable.

In the previous version, the API for trails and ribbons
required the user to manually add the `CloneModifier` to spawn particles,
and the `RibbonModifier` to tie them together in a single ribbon.
However, the API had many implicit limitations:

- The particles forming the trail or ribbon were cloned in the Update pass,
unlike regular particles spawned in the Init pass.
- Cloned particles were exact copies of their source particle,
with no way for the user to initializer them
(we didn't run any init modifier on them).
- The lifetime of trail and ribbon particles was inherited from the cloned particle,
which is not always desirable.
- The age of trail and ribbon particles was always initialized to zero.

The new API in v0.13 fixes those issues,
by moving the particle cloning step into a separate pass similar to the Init one,
and running like it before the Update pass.

We introduce the concept of _initializer_,
to unify the existing _spawner_ (spawn from CPU),
and the new _cloner_ (clone existing GPU particle).
To that end, **the `EffectSpawner` component was wrapped into a new `EffectInitializers`**,
which takes a `Vec<EffectInitializer>` to define how each group initializes its particles.
A **new `EffectCloner`** holds the cloner configuration,
similar to how `EffectSpawner` holds the spawner configuration.

// OLD v0.12

```rust
// Spawn from CPU:
let spawner = EffectSpawner::new(asset);
```

// NEW v0.13

```rust
// Spawn from CPU:
let initializer: EffectInitializer = EffectSpawner::new(asset).into();

// Clone from existing GPU particle:
let initializer: EffectInitializer = EffectCloner::new(0, 3.0).into();
```

To simplify things further, `EffectAsset` adds **2 new helper methods**:

- `with_trails()` adds a new group which clones from the given source group
at regular fixed interval.
- `with_ribbons()` does the same, but also ties together cloned particles
to form a continuous chain ("ribbon").

The group creation relies heavily on those new API functions,
and moves away from indirectly declaring upfront an array of groups
via the array of capacities.
Instead, **`EffectAsset::new()` reverts to taking a single capacity for the default group #0**.

// OLD v0.12

```rust
let asset = EffectAsset::new(vec![256, RIBBONS_CAPACITY], ...)
.update_groups(clone_modifier, ParticleGroupSet::single(0))
.render(RibbonModifier);
```

// NEW v0.13

```rust
let asset = EffectAsset::new(256, ...)
.with_ribbons(RIBBONS_CAPACITY, 1.0 / RIBBONS_SPAWN_RATE, RIBBONS_LIFETIME, 0);
```

The lifetime of trails and ribbons also changes semantic.
Previously, the age of cloned particles was set to zero,
and the lifetime inherited from the cloned particle.
This was both inflexible, as you couldn't set a lifetime easily,
and caused issues with particles in the middle of a ribbon dying
before the particles following them, causing gaps and rendering artifacts.
To fix this, **the new API enforces a per-ribbon lifetime**,
specified via the `with_ribbons()` helper.
Any lifetime set with modifiers is ignored and overwritten.
This new approach is more restrictive than previously,
as you cannot set a per-ribbon lifetime, only a global one for the entire effect.
But it guarantees a last cloned / first dead (LIFO) order for ribbon particles,
which solves the previously mentioned render artifacts and gaps.

Because of all the above, **the `CloneModifier` has been removed**.
To create trails or ribbons, use the helper functions mentioned above.
Similarly, **the `RibbonModifier` was also removed**. Use `with_ribbons()` instead.
Note that you can only create a single ribbon group per effect,
because the ribbon particles are chained via a linked list,
and there's only one set of attributes per particle to do so.
This was already the case in the previous version, but is made more explicit now.

As a result of those changes, init modifiers can now run on trail and ribbon particles.
This makes it cleaner and easier to initialize cloned particles,
in the same way particles spawned from GPU are initialized.
All modifiers are supported, except those touching the age, lifetime,
and previous and next pointer attributes (for ribbon linked list).
Loading