# bevy_feronia [![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/NicoZweifel/bevy_feronia?tab=readme-ov-file#licensecreditsinspirationsreferences) [![Crates.io](https://img.shields.io/crates/v/bevy_feronia.svg)](https://crates.io/crates/bevy_feronia) [![Downloads](https://img.shields.io/crates/d/bevy_feronia.svg)](https://crates.io/crates/bevy_feronia) [![Docs](https://docs.rs/bevy_feronia/badge.svg)](https://docs.rs/bevy_feronia/) [![CI](https://github.com/NicoZweifel/bevy_feronia/actions/workflows/ci.yaml/badge.svg?branch=dev&event=push)](https://github.com/NicoZweifel/bevy_feronia/actions/workflows/ci.yaml) Environment scattering tools and shaders/materials that prioritize visual fidelity/artistic freedom, a declarative API and modularity. ## Who is this for? In the current stage this is mostly for tinkerers and learners within the [bevy](https://github.com/bevyengine/bevy) ecosystem, but I am planning to use this for actual game dev myself eventually. > [!CAUTION] > This package is in early development and in an experimentation stage. > I wouldn't personally use this in production quite yet, but it's getting closer to that state incrementally. Screenshot 2025-11-28 144933 ## Getting started ```shell cargo add bevy_feronia ``` The possible use-cases are demonstrated in the [examples](/examples/EXAMPLES.md) ### Setup The setup depends on the use-case, but a typical setup would look like something like this: ```rust app.add_plugins(( MeshMaterialAssetBackendPlugin, // Or SceneAssetBackendPlugin, // ... ExtendedWindAffectedScatterPlugin )); ``` The Scatter system needs to know when it can set up since it can depend on height mapping. You need to insert the setup state at some point. > [!NOTE] > In complex setups that load assets and bake a height map, this can be after the `Startup`. ```rust app.insert_state(ScatterState::Setup) ``` Or ```rust ns_height_map.set(HeightMapState::Setup); ns_scatter.set(ScatterState::Setup); ``` For chunking or GPU driven culling to work, `Center` and `CullComputeCamera` need to be inserted on the Camera: ```rust cmd.spawn(( (Camera::default(), Camera3d::default()), (Center, CullComputeCamera), // etc... ``` ### Defining layers A `ScatterItem`'s `LOD`s are grouped by `Name`. If the names end in `LOD_1` or `lod1` etc., the LOD suffix will be stripped from the name to match it to the other lods of the asset. > [!CAUTION] > When defining multiple `ScatterItems` per `ScatterLayer` without names, a different asset will render when `LODs` are changing, leading to visual bugs. ```rust // Landscape cmd.spawn(( MeshMaterial3d(materials.add(StandardMaterial { base_color: GRAY_500.into(), ..default() })), Mesh3d(meshes.add(PlaneMeshBuilder::from_length(80.).build())), ScatterRoot::default(), // Scatter layers children![( // Make sure you use the correct `ScatterLayer` with the desired `ScatterLayerType`, e.g., // Standard, Extended or Instanced Material/Layer. extension::scatter_layer("Wind Affected Layer"), // Scatter Options DistributionDensity(50.), InstanceJitter::default(), // You can define material options on the full layer here WindAffected, children![ ( // Or overwrite on the item, e.g., // WindAffected, // // CAUTION: If you have multiple assets, all lods that belong to each other need to have the same name! // // You can have multiple assets in each layer; as long as all LODs have the same name, they will be matched correctly. Name::new("Wind Affected Example Item"), MeshMaterial3d(materials.add(StandardMaterial::default())), Mesh3d(mesh.clone()), ), ( Name::new("Wind Affected Example Item"), // We need to specify the LOD Level if it is not 0 (Highest level) LevelOfDetail(1), MeshMaterial3d(materials.add(StandardMaterial { base_color: RED_500.into(), ..default() })), Mesh3d(mesh.clone()), ), ] )] )); ``` ### Scattering Now you can start scattering! 🌱 πŸƒ 🌿 πŸ€ 🌳 🌲 🌴 🌺 ```rust cmd.trigger(Scatter::::new(*root)); ``` > [!NOTE] > `ScatterLayers` and their `ScatterItems` of the same `ScatterType` are always scattered in order, but layers of different `ScatterTypes` can be scattered at the same time. #### Ordered Scattering In complex scenes it is often required to scatter a complete hierarchy in order (rocks β†’ trees/foliage β†’ grass). > [!TIP] > If an ordered scatter is still required, and you can't or don't want to scatter in parallel, observers need to be used to chain the scattering of `ScatterTypes` in order. ```rust fn scatter_on_keypress( mut cmd: Commands, keyboard_input: Res>, root: Single> ) { if !keyboard_input.just_pressed(KeyCode::Space) { return; }; // Scatter the rocks. cmd.trigger(Scatter::::new(*root)); } fn scatter_extended( _: On>, mut cmd: Commands, root: Single>, ) { // Scatter the foliage after the rocks. cmd.trigger(Scatter::::new(*root)); } fn scatter_instanced( _: On>, mut cmd: Commands, root: Single>, ) { // Scatter the grass last so it doesn't grow on occupied areas. cmd.trigger(Scatter::::new(*root)); } ``` ## Compatibility There are very experimental releases before 0.5.0, but I wouldn't use them. | bevy | bevy_feronia | |-------------|--------------| | 0.18 | 0.8+ | | 0.17 | 0.7 | ## License/Credits/Inspirations/References The code is dual-licensed: * MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) Feel free to copy the grass assets. All the other assets used in the examples are licensed assets. > [!IMPORTANT] > If you intend to use them, make sure you comply with the license. - [Graswald](https://gscatter.com/gallery) for their amazing assets and [`GScatter`](https://gscatter.com/gscatter), which served as inspiration for the scatter tools. - Sucker Punch Productions for their Procedural Grass and Wind simulation in 'Ghost of Tsushima' and [GDC Talks](https://www.youtube.com/watch?v=Ibe1JBF5i5Y). - [bevy_procedural_grass](https://github.com/jadedbay/bevy_procedural_grass) by jadedbay - [warbler_grass](https://github.com/EmiOnGit/warbler_grass) by EmiOnGit - [GDC 2011 "Approximating Translucency"](https://www.gdcvault.com/play/1014538/Approximating-Translucency-for-a-Fast) - [Blinn–Phong reflection model](https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_reflection_model) - [All the other assets](/assets/LICENSE) ## Alternatives - If you want a lower level scattering API, just for scattering and sampling there is also [`bevy_map_scatter`](https://github.com/morgenthum/map_scatter), which I might use eventually as well but for now I want this crate to achieve a vision. ## Roadmap A bunch of issues are already open, but some of the larger milestones could be: - Allow physics-based and other entities to impact the displacement/wind. - Make use of compute shaders (Allow scattering on CPU and GPU, improve culling). - Allow for multiple `ScatterRoots` when baking height maps (otherwise it should work... probably but it isn't tested yet)