Skip to content

Commit

Permalink
Add WebAssembly support (#357)
Browse files Browse the repository at this point in the history
Add support for the WebAssembly target (`wasm`). This requires
disabling the `serde` feature, as the `typetag` used to serialize trait
objects is not available for that target.

Add a web page with all the examples, and move all the GIFs there.
Remove the list of examples from the README; instead point users to the
web demo at `examples/wasm/index.html`. This should be eventually
published live for even easier reference.

Temporarily copy `assets/` to `examples/wasm/` so that web examples can
directly access them when serving locally. This avoids complicated
multi-folder setups.

Fixes #41
  • Loading branch information
djeedai authored Aug 1, 2024
1 parent 05016ec commit d7d2d9c
Show file tree
Hide file tree
Showing 82 changed files with 1,032 additions and 190 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Added a new `serde` feature, and moved all `Serialization` and `Deserialization` derives under that feature.
This allows disabling support on wasm, where `typetag` is not available.
The feature is enabled by default, so the behavior doesn't change on non-wasm targets.
- Added support for WebAssembly (`wasm`). This requires disabling the `serde` feature. (#41)

### 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.

## [0.12.1] 2024-07-28

### Fixed
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_hanabi"
version = "0.12.1"
version = "0.13.0-dev"
authors = ["Jerome Humbert <[email protected]>"]
edition = "2021"
description = "Hanabi GPU particle system for the Bevy game engine"
Expand All @@ -22,7 +22,8 @@ default = ["2d", "3d", "serde", "gpu_tests", "examples_world_inspector"]
# Enable support for rendering through a 3D camera (Camera3dBundle)
3d = []

# Enable serializing and deserializing of assets.
# Enable serializing and deserializing of assets. This doesn't work on WASM,
# because typetag is not available for the wasm target.
serde = ["typetag"]

# Enable tracing annotations
Expand Down
178 changes: 9 additions & 169 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

🎆 Hanabi — a GPU particle system for the Bevy game engine.

![firework](examples/firework.gif)

## Overview

The Hanabi particle system is a modern GPU-based particle system for the Bevy game engine. It focuses on scale to produce stunning visual effects (VFX) in real time, offloading most of the work to the GPU, with minimal CPU intervention. The design is inspired by modern particle systems found in other industry-leading game engines.

🚧 _This project is under heavy development, and is currently lacking both features and performance / usability polish. However, for moderate-size effects, it can already be used in your project. Feedback and contributions on both design and features are very much welcome._

🎆 Hanabi makes heavy use of compute shaders to offload work to the GPU in a performant way. Support for compute shaders on the `wasm` target (WebAssembly) via WebGPU is only available since Bevy v0.11, and is not yet available in 🎆 Hanabi. See [#41](https://github.com/djeedai/bevy_hanabi/issues/41) for details on progress.
🎆 Hanabi makes heavy use of compute shaders to offload work to the GPU in a performant way. Support for compute shaders on the `wasm` target (WebAssembly) is available as of v0.13 (not yet published), and only through WebGPU. See the [WebAssembly support](./docs/wasm.md) documentation for details.

## Usage

Expand Down Expand Up @@ -126,174 +128,9 @@ commands

See the [`examples/`](https://github.com/djeedai/bevy_hanabi/tree/4874c48f2d92c9a8a1f980bf808add9378c74402/examples) folder.

Note for Linux users: The examples build with the `bevy/x11` feature by default to enable support for the X11 display server. If you want to use the Wayland display server instead, add the `bevy/wayland` feature.

### Firework

Combine the `SetPositionSphereModifier` for spawning and `LinearDragModifier` to slow down particles, to create a firework effect. This example makes use of an HDR camera with Bloom. See the example file for more details about how the effect is designed.

```shell
cargo run --example firework --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
```

![firework](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/firework.gif)

### Portal

Combine the `SetVelocityTangentModifier` for tangential rotation of particles around a circle and the `OrientAlongVelocityModifier` to create elongated sparks, to produce a kind of "magic portal" effect. This example makes use of an HDR camera with Bloom. See the example file for more details about how the effect is designed.

```shell
cargo run --example portal --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
```

![portal](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/portal.gif)

### Expressions

Demonstrate the use of the Expression API to build a visual effect simulated and animated entirely on the GPU. The animation is hard-coded into the simulation compute shader generated by 🎆 Hanabi based on the expression designed by the effect author.

```shell
cargo run --example expr --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![expr](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/expr.gif)

### Gradient

Animate an emitter by moving its `Transform` component, and emit textured quad particles with a `ColorOverLifetimeModifier`.

```shell
cargo run --example gradient --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
```

![gradient](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/gradient.gif)

### Force Field

This example demonstrates the force field modifier `ForceFieldModifier`, which allows creating some attraction and repulsion sources affecting the motion of the particles. It also demonstrates the use of the `AabbKillModifier` to either kill the particles exiting an "allowed" space (green box) or entering a "forbidden" space (red box).

```shell
cargo run --example force_field --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![force_field](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/force_field.gif)

### 2D

This example shows how to use 🎆 Hanabi with a 2D camera.

The white square mesh is moving forward and backward along the camera depth. The 2D effect itself remains at a constant position. When the square mesh moves behind the effect, the particles are rendered in front of it, and conversely when it moves forward the particles are rendered behind it.

```shell
cargo run --example 2d --features="bevy/bevy_winit bevy/bevy_sprite 2d"
```

![2d](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/2d.gif)

### Multi-camera

The example demonstrates the use of multiple cameras and render layers to selectively render effects. Each camera uses a different combination of layers, and each effect is assigned a different layer.

```shell
cargo run --example multicam --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![multicam](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/multicam.gif)

### Activate

This example demonstrates manual activation and deactivation of a `Spawner`, from code (CPU). The circle bobs up and down in the water, spawning bubbles when in the water only. The bubble particles are constrained to the water with a `KillAabbModifier`, and a small vertical acceleration simulate some pseudo buoyancy.

```shell
cargo run --example activate --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![activate](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/activate.gif)

### Spawn

This example demonstrates three spawn modes:

- **Left:** Continuous emission with a fixed rate (particles/second).
- **Center:** One-shot burst emission of a fixed count of particles.
- **Right:** Continuous bursts of particles, an hybrid between the previous two. This effect also uses a property to change over time the direction of the acceleration applied to all particles.

It also shows the applying of constant acceleration to all particles. The right spawner's acceleration (gravity) is controlled by a custom property, which is slowly rotated by a Bevy system (CPU side).

```shell
cargo run --example spawn --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![spawn](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/spawn.gif)

### Spawn on command

This example demonstrates how to emit a burst of particles when an event occurs. A property is also used to modify the color of the particles spawned. This gives total control of the spawning to the user code.

```shell
cargo run --example spawn_on_command --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![spawn_on_command](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/spawn_on_command.gif)

### Circle

This example demonstrates the `circle` spawner type, which emits particles along a circle perimeter or a disk surface. This allows for example simulating a dust ring around an object colliding with the ground.

The example also uses the `FlipbookModifier` to animate the particles with a spritesheet texture.

```shell
cargo run --example circle --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
```

![circle](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/circle.gif)

### Visibility

This example demonstrates the difference between the default `SimulationCondition::WhenVisible` which simulates an effect when it's visible only, and `SimulationCondition::Always` which always simulates an effect even if the entity is hidden.
A web demo (using the WebAssembly target) showing all examples is availabe in the [`examples/wasm/`](./examples/wasm/) folder. You can open `index.html` in any browser to see a GIF of all the examples. Running the actual WebAssembly example however requires serving the files with an HTTP server. If you have NodeJS installed, you can do that for example by running `npx http-server examples/wasm`.

- The **top** effect uses `SimulationCondition::Always`, continuing to simulate even when hidden, moving to the right.
- The **bottom** effect uses `SimulationCondition::WhenVisible`, pausing simulation while hidden, and resuming its motion once visible again from the position where it was last visible.

```shell
cargo run --example visibility --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![circle](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/visibility.gif)

### Random

This example spawns particles with randomized parameters.

```shell
cargo run --example random --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![random](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/random.gif)

### Lifetime

This example demonstrates particle effects with different lifetimes. Each effect emits particles every 3 seconds, with a particle lifetime of:

- **Left:** 12 seconds, longer than the emit rate, so multiple bursts accumulate.
- **Center:** 3 seconds, like the emit rate, so particles die when new ones are emitted.
- **Right:** 0.75 second, shorter than the emit rate, so particles die much earlier than the next burst.

```shell
cargo run --example lifetime --features="bevy/bevy_winit bevy/bevy_pbr 3d"
```

![lifetime](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/lifetime.gif)

### Billboard

This example demonstrates particles with the billboard render modifier, making them always face the camera. It also demonstrates the use of alpha cutoff to filter out texture samples below a certain threshold, varying this threshold back and forth between 0 and 1. Finally, the example uses attributes to store per-particle data like a color or in-plane rotation.

```shell
cargo run --example billboard --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
```

![billboard](https://raw.githubusercontent.com/djeedai/bevy_hanabi/4874c48f2d92c9a8a1f980bf808add9378c74402/examples/billboard.gif)
Note for Linux users: The examples build with the `bevy/x11` feature by default to enable support for the X11 display server. If you want to use the Wayland display server instead, add the `bevy/wayland` feature.

## Feature List

Expand Down Expand Up @@ -378,11 +215,14 @@ This list contains the major fixed features provided by 🎆 Hanabi. Beyond that
|---|:-:|---|
| `2d` || Enable rendering through 2D cameras ([`Camera2dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_2d/struct.Camera2dBundle.html)) |
| `3d` || Enable rendering through 3D cameras ([`Camera3dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_3d/struct.Camera3dBundle.html)) |
| `serde`* || Use `serde` to derive `Serialization` and `Deserialization` on asset-related types. |

(*) `serde` is not compatible with WASM (due to the `typetag` dependency not being available on `wasm`).

For optimization purpose, users of a single type of camera can disable the other type by skipping default features in their `Cargo.toml`. For example to use only the 3D mode:

```toml
bevy_hanabi = { version = "0.12", default-features = false, features = [ "3d" ] }
bevy_hanabi = { version = "0.12", default-features = false, features = [ "3d", "serde" ] }
```

## Compatible Bevy versions
Expand Down
55 changes: 55 additions & 0 deletions build_examples_wasm.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@echo on

echo Check that wasm-bindgen -V returns 0.2.92, otherwise cargo install wasm-bindgen-cli --version 0.2.92

echo Setting RUSTFLAGS to enable unstable web_sys APIs...
set RUSTFLAGS=--cfg=web_sys_unstable_apis

echo Build all examples for WASM...
REM 3D
cargo b --release --example firework --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example portal --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example expr --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example spawn --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example multicam --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example visibility --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example random --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example spawn_on_command --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example activate --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/bevy_ui bevy/default_font 3d"
cargo b --release --example force_field --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example init --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example lifetime --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example ordering --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
cargo b --release --example ribbon --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr 3d"
REM 3D + PNG
cargo b --release --example gradient --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
cargo b --release --example circle --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
cargo b --release --example billboard --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
cargo b --release --example worms --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
cargo b --release --example instancing --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_pbr bevy/png 3d"
REM 2D
cargo b --release --example 2d --target wasm32-unknown-unknown --no-default-features --features="bevy/bevy_winit bevy/bevy_sprite 2d"

echo Bindgen all examples...
wasm-bindgen --out-name wasm_firework --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/firework.wasm
wasm-bindgen --out-name wasm_portal --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/portal.wasm
wasm-bindgen --out-name wasm_expr --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/expr.wasm
wasm-bindgen --out-name wasm_spawn --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/spawn.wasm
wasm-bindgen --out-name wasm_multicam --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/multicam.wasm
wasm-bindgen --out-name wasm_visibility --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/visibility.wasm
wasm-bindgen --out-name wasm_random --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/random.wasm
wasm-bindgen --out-name wasm_spawn_on_command --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/spawn_on_command.wasm
wasm-bindgen --out-name wasm_activate --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/activate.wasm
wasm-bindgen --out-name wasm_force_field --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/force_field.wasm
wasm-bindgen --out-name wasm_init --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/init.wasm
wasm-bindgen --out-name wasm_lifetime --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/lifetime.wasm
wasm-bindgen --out-name wasm_ordering --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/ordering.wasm
wasm-bindgen --out-name wasm_ribbon --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/ribbon.wasm
wasm-bindgen --out-name wasm_gradient --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gradient.wasm
wasm-bindgen --out-name wasm_circle --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/circle.wasm
wasm-bindgen --out-name wasm_billboard --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/billboard.wasm
wasm-bindgen --out-name wasm_worms --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/worms.wasm
wasm-bindgen --out-name wasm_instancing --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/instancing.wasm
wasm-bindgen --out-name wasm_2d --out-dir examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/2d.wasm

echo Done. See docs/wasm.md for help on running the examples locally.
Loading

0 comments on commit d7d2d9c

Please sign in to comment.