Skip to content

Commit

Permalink
Merge pull request #74 from EmiOnGit/bevy-0.12
Browse files Browse the repository at this point in the history
Bevy 0.12
  • Loading branch information
EmiOnGit authored Feb 12, 2024
2 parents 940f2ff + 0c180cb commit ce362f3
Show file tree
Hide file tree
Showing 27 changed files with 363 additions and 266 deletions.
8 changes: 3 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ env:

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Check syntax
run: cargo check --all-features --tests --verbose
- name: Run tests
run: cargo test --verbose
run: cargo test --all-features --verbose
- name: format
run: |
rustup component add rustfmt
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Changelog
## 0.5
* Support for bevy 0.12
* `WarblerBundle` now contains the `NoAutomaticBatching` component
Besides that, a lot of internal restructering was needed

## 0.4
* Support for bevy 0.11
* The color of the grass is now a `Component`, meaning it can be configured on a `Chunk` basis.
Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "warbler_grass"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
readme = "README.md"
license = "MIT OR Apache-2.0"
Expand All @@ -18,15 +18,15 @@ editor = ["dep:bevy-inspector-egui", "dep:rfd"]
[dependencies]
bytemuck = "1.13.0"
bitflags = "2"
rfd = { version = "0.11.2", optional = true }
bevy-inspector-egui = { version = "0.19", optional = true }
rfd = { version = "0.13", optional = true }
bevy-inspector-egui = { version = "0.22", optional = true }
[dependencies.bevy]
version = "0.11.0"
version = "0.12"
default-features = false
features = [ "bevy_core_pipeline", "bevy_render", "bevy_asset", "bevy_pbr" , "png"]

[dev-dependencies]
bevy = { version = "0.11", default-features = false, features = ["bevy_winit","x11", "ktx2", "zstd", "tonemapping_luts"] }
bevy = { version = "0.12", default-features = false, features = ["bevy_winit","x11", "ktx2", "zstd", "tonemapping_luts", "multi-threaded"] }

[[example]]
name = "editor"
Expand Down
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
# <img src="branding/warbler_display.svg" width="500">
[![crates.io](https://img.shields.io/badge/crates.io-v0.4-orange)](https://crates.io/crates/warbler_grass)
[![docs.io](https://img.shields.io/badge/docs-v0.4-green)](https://docs.rs/warbler_grass/0.4/warbler_grass/)
[![crates.io](https://img.shields.io/badge/crates.io-v0.5-orange)](https://crates.io/crates/warbler_grass)
[![docs.io](https://img.shields.io/badge/docs-v0.5-green)](https://docs.rs/warbler_grass/0.5/warbler_grass/)

A `bevy` plugin for ergonomic integration of grass in 3d games.

Currently, bevy version `0.11` is supported.
Currently, bevy version `0.12` is supported.
Wasm builds are also supported!

**Don't use this project for serious projects, it is still under heavy development.
Currently, the project is not ready to be used besides side projects or for learning purposes**

<img src="images/preview.png" width="700">

The preview image comes from [my demo project](https://github.com/EmiOnGit/birdylook) where I use this crate for grass rending

Another cool project using this crate is the [foxtrot](https://github.com/janhohenheim/foxtrot) template. Check it out!
A cool project using this crate is the [foxtrot](https://github.com/janhohenheim/foxtrot) template. Check it out!
## Usage
Add `warbler_grass` as dependency to your project
```toml
[dependencies]
warbler_grass = "0.4"
warbler_grass = "0.5"
```
### Add grass to your game:
```rust

use bevy::{prelude::*, render::primitives::Aabb};
use warbler_grass::prelude::*;
mod helper;

fn main() {
App::new()
Expand Down Expand Up @@ -63,11 +61,12 @@ fn setup_grass(mut commands: Commands, asset_server: Res<AssetServer>) {
}

```
You can also use the editor to modify the maps in your game
Take a look at the `editor` example on how this might work
You can also use the editor to modify the maps in your game.
Take a look at the `editor` example on how this might work.
(This is very much just WIP, but can give you a quick and dirty tool or give you an example to work out your own editor)

## Examples
You can find them in the [`example folder`](https://github.com/EmiOnGit/warbler_grass/tree/master/examples)
You can find them in the [`example folder`](https://github.com/EmiOnGit/warbler_grass/tree/main/examples)


### Load grass
Expand Down Expand Up @@ -103,15 +102,22 @@ cargo run -r --example many_chunks
```
### Stress test
This is not much of a example. It is used to limit test the crate.
Of course you can also load it and see what happens.
Of course, you can also load it and see what happens.
```shell
# I'd run this demo in release mode first to see how your hardware can keep up
cargo run -r --example stress_test
```

## Version Table

| `warbler_grass` | Bevy |
|-----------------|--------|
| `0.5` | `0.12` |
| `0.4` | `0.11` |

## Contributing
If you read this part, you might consider helping this project grow.
I consider this project very beginner friendly.
I consider this project very beginner-friendly.
It is relatively easy to grasp the workings since the use case is clear; to draw grass efficiently.
Don't fear if you are a beginner in bevy or even rust!

Expand Down
2 changes: 1 addition & 1 deletion examples/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ fn refresh_texture_view(
let Ok((density_map, y_map, heights)) = chunk.get(active_entity) else {
return;
};
if let Some(mat) = materials.get_mut(&material) {
if let Some(mat) = materials.get_mut(material.clone()) {
match *selected_map {
SelectedMap::YMap => mat.base_color_texture = Some(y_map.y_map.clone()),
SelectedMap::DensityMap => {
Expand Down
2 changes: 1 addition & 1 deletion examples/grass_colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn setup_grass(mut commands: Commands, asset_server: Res<AssetServer>) {
// this can be useful if your game has seasons
fn change_colors(mut grass_colors: Query<&mut GrassColor>, time: Res<Time>) {
// Most likely you'd want to choose other colors
let r = ((time.raw_elapsed_seconds() / 2.).sin() / 2.) + 0.5;
let r = ((time.elapsed_seconds() / 2.).sin() / 2.) + 0.5;
let g = 1. - r;
for mut color in &mut grass_colors {
color.main_color.set_r(r);
Expand Down
2 changes: 1 addition & 1 deletion examples/grass_mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn setup_grass(
// so we can swap them later
let store = GrassMeshStore {
custom_handle: grass_mesh.clone(),
default_handle: GRASS_MESH_HANDLE.typed(),
default_handle: GRASS_MESH_HANDLE,
};
commands.insert_resource(store);

Expand Down
6 changes: 4 additions & 2 deletions examples/load_grass.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The basic example which simply spawns a chunk of grass the recommended way
use bevy::{prelude::*, render::primitives::Aabb};
use warbler_grass::prelude::*;
use bevy::{diagnostic::LogDiagnosticsPlugin, prelude::*, render::primitives::Aabb};
use warbler_grass::{diagnostic::WarblerDiagnosticsPlugin, prelude::*};
mod helper;

fn main() {
Expand All @@ -11,6 +11,8 @@ fn main() {
WarblersPlugin,
// Just a helper plugin for spawning a camera
// As in all examples, you can use the wasd keys for movement and qe for rotation
WarblerDiagnosticsPlugin,
LogDiagnosticsPlugin::default(),
helper::SimpleCamera,
))
.add_systems(Startup, setup_grass)
Expand Down
2 changes: 1 addition & 1 deletion examples/many_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn setup_grass_chunks(mut commands: Commands, asset_server: Res<AssetServer>) {
// the aabb defined the dimensions of the box the chunk lives in
aabb: Aabb::from_min_max(Vec3::ZERO, Vec3::new(chunk_width, 2., chunk_height)),
grass_color: GrassColor {
main_color: color.clone(),
main_color: color,
bottom_color: color * 0.4,
},

Expand Down
1 change: 0 additions & 1 deletion examples/stress_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ fn main() {
// more wind
.insert_resource(GrassConfiguration {
wind: Vec2::new(2., 2.),
..default()
})
.run();
}
Expand Down
17 changes: 9 additions & 8 deletions src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ use bevy::{
ecs::{bundle::Bundle, component::Component, query::QueryItem},
prelude::Color,
render::{
extract_component::ExtractComponent, mesh::Mesh, prelude::SpatialBundle, primitives::Aabb,
texture::Image, texture::DEFAULT_IMAGE_HANDLE,
batching::NoAutomaticBatching, extract_component::ExtractComponent, mesh::Mesh, prelude::SpatialBundle, primitives::Aabb, texture::Image
},
};

use crate::{
map::DensityMap,
map::NormalMap,
map::YMap,
warblers_plugin::{GRASS_MESH_HANDLE, DEFAULT_NORMAL_MAP_HANDLE},
warblers_plugin::{DEFAULT_IMAGE_HANDLE, DEFAULT_NORMAL_MAP_HANDLE, GRASS_MESH_HANDLE},
};

/// This [`Bundle`] spawns a grass chunk in the world.
Expand Down Expand Up @@ -44,25 +43,27 @@ pub struct WarblersBundle {
/// Note that the Aabb is used to define the world dimensions of the [`DensityMap`] and [`YMap`].
pub aabb: Aabb,
pub spatial: SpatialBundle,
pub no_automatic_batching: NoAutomaticBatching
}
impl Default for WarblersBundle {
fn default() -> Self {
Self {
grass_mesh: GRASS_MESH_HANDLE.typed(),
y_map: DEFAULT_IMAGE_HANDLE.typed().into(),
normal_map: DEFAULT_NORMAL_MAP_HANDLE.typed().into(),
density_map: DEFAULT_IMAGE_HANDLE.typed().into(),
grass_mesh: GRASS_MESH_HANDLE,
y_map: DEFAULT_IMAGE_HANDLE.into(),
normal_map: DEFAULT_NORMAL_MAP_HANDLE.into(),
density_map: DEFAULT_IMAGE_HANDLE.into(),
height: WarblerHeight::Uniform(1.),
grass_color: GrassColor::default(),
aabb: Aabb::default(),
spatial: SpatialBundle::default(),
no_automatic_batching: NoAutomaticBatching,
}
}
}
/// The height of the grass blades
///
/// Can be used in Combination with the [`WarblersBundle`] to spawn grass chunks
#[derive(Component, Clone)]
#[derive(Component, Clone, Debug, PartialEq)]
pub enum WarblerHeight {
/// Sets the height of the grass blades to a constant value.
Uniform(f32),
Expand Down
21 changes: 14 additions & 7 deletions src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bevy::{
diagnostic::{Diagnostic, DiagnosticId, Diagnostics, RegisterDiagnostic},
prelude::{Assets, ComputedVisibility, Handle, Plugin, Query, Res, Update},
prelude::{Assets, Handle, InheritedVisibility, Plugin, Query, Res, Update},
render::view::ViewVisibility,
};

use crate::dithering::DitheredBuffer;
Expand All @@ -15,9 +16,9 @@ use crate::dithering::DitheredBuffer;
///
/// App::new()
/// // add this plugin to log the values
/// .add_plugin(WarblerDiagnosticsPlugin)
/// .add_plugins(WarblerDiagnosticsPlugin)
/// // add bevys plugin to print all logged values to the terminal
/// .add_plugin(LogDiagnosticsPlugin::default());
/// .add_plugins(LogDiagnosticsPlugin::default());
/// ```
pub struct WarblerDiagnosticsPlugin;
impl Plugin for WarblerDiagnosticsPlugin {
Expand All @@ -31,22 +32,28 @@ impl Plugin for WarblerDiagnosticsPlugin {
}
}
impl WarblerDiagnosticsPlugin {
/// A id for the [`Diagnostic`] of the blade count.
/// An id for the [`Diagnostic`] of the blade count.
pub const GRASS_BLADE_COUNT: DiagnosticId =
DiagnosticId::from_u128(11_920_430_925_311_532_474_622_109_399_490_581_929);

/// Calculates the amount of blades that are drawn this frame and logs them
fn measure_blades(
blades: Query<(&Handle<DitheredBuffer>, &ComputedVisibility)>,
blades: Query<(
&Handle<DitheredBuffer>,
&InheritedVisibility,
&ViewVisibility,
)>,
dither: Res<Assets<DitheredBuffer>>,
mut diagnostics: Diagnostics,
) {
// entities spawned with the WarblersBundle
let count: u32 = blades
.iter()
// We are only interested in visible chunks
.filter(|(_handle, visible)| visible.is_visible())
.filter_map(|(handle, _visible)| dither.get(handle))
.filter(|(_handle, inherited_visibility, view_visibility)| {
inherited_visibility.get() && view_visibility.get()
})
.filter_map(|(handle, _visible, _view_visibility)| dither.get(handle))
.map(|buffer| buffer.positions.len() as u32)
.sum();

Expand Down
35 changes: 14 additions & 21 deletions src/dithering.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
use bevy::asset::Asset;
use bevy::ecs::system::lifetimeless::SRes;
use bevy::ecs::system::SystemParamItem;
use bevy::math::Vec3Swizzles;
use bevy::{
asset::Assets,
ecs::{
prelude::*,
system::{lifetimeless::SRes, SystemParamItem},
},
log::warn,
math::Vec2,
reflect::{Reflect, TypeUuid},
render::{
primitives::Aabb,
render_asset::{PrepareAssetError, RenderAsset},
render_resource::{Buffer, BufferInitDescriptor, BufferUsages},
renderer::RenderDevice,
texture::Image,
},
};
use bevy::prelude::*;
use bevy::render::primitives::Aabb;
use bevy::render::render_asset::{PrepareAssetError, RenderAsset};
use bevy::render::render_resource::{Buffer, BufferInitDescriptor, BufferUsages};
use bevy::render::renderer::RenderDevice;

use crate::map::DensityMap;

Expand Down Expand Up @@ -46,7 +37,9 @@ pub(crate) fn dither_density_map(
return None;
};
// Capacity is not precise but should be a good estimate
let mut dither_buffer = Vec::with_capacity(image.size().length() as usize);

let image_length = (image.size().length_squared() as f32).sqrt();
let mut dither_buffer = Vec::with_capacity(image_length as usize);
let buffer = dynamic_image.into_luma8();
let i_count = (density * field_size.x).abs() as usize;
let j_count = (density * field_size.y).abs() as usize;
Expand Down Expand Up @@ -74,12 +67,12 @@ pub(crate) fn dither_density_map(
/// A buffer containing the dithered density map
///
/// This struct shouldn't be modified by the user
#[derive(Reflect, Clone, Debug, TypeUuid)]
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
#[derive(Clone, Debug, TypePath, Asset)]
pub(crate) struct DitheredBuffer {
pub positions: Vec<Vec2>,
}
/// The gpu representation of a [`DitheredBuffer`]
#[derive(Debug)]
pub(crate) struct GpuDitheredBuffer {
pub buffer: Buffer,
pub instances: usize,
Expand Down Expand Up @@ -157,7 +150,7 @@ mod tests {
fn dither_density() {
let image = Image::default(); // 1x1x1 image all white
let dither = super::dither_density_map(&image, 2., Vec2::new(1., 1.));
assert_eq!(dither.unwrap().positions.len(), (1 * 2) * (1 * 2));
assert_eq!(dither.unwrap().positions.len(), 2 * 2);
let dither = super::dither_density_map(&image, 2., Vec2::new(10., 5.));
assert!(dither.unwrap().positions.len() == (10 * 2) * (5 * 2));
let dither = super::dither_density_map(&image, 5., Vec2::new(1., 1.));
Expand Down
4 changes: 2 additions & 2 deletions src/editor/brush.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ impl BrushBehavior for Airbrush {
}
}

fn pixel_positions(brush_size: u32, image_dimensions: Vec2, position: Vec2) -> Vec<(u32, u32)> {
let position = (image_dimensions * position).as_ivec2();
fn pixel_positions(brush_size: u32, image_dimensions: UVec2, position: Vec2) -> Vec<(u32, u32)> {
let position = (image_dimensions.as_vec2() * position).as_ivec2();
let range = brush_size as i32 * (image_dimensions.x + image_dimensions.y) as i32 / 100;
(-range..range)
.flat_map(|i| (-range..range).map(move |j| (i, j)))
Expand Down
2 changes: 1 addition & 1 deletion src/editor/draw_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn draw_map(
mut draw_events: EventReader<DrawEvent>,
mut images: ResMut<Assets<Image>>,
) {
for event in draw_events.iter() {
for event in draw_events.read() {
match event {
DrawEvent::Draw { positions, image } => {
if let Some(image) = images.get_mut(image) {
Expand Down
2 changes: 1 addition & 1 deletion src/editor/hot_reloading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn notify_image_change(
mut ev_asset: EventReader<DrawEvent>,
mut q: Query<(&mut YMap, &mut DensityMap, &mut WarblerHeight)>,
) {
for ev in ev_asset.iter() {
for ev in ev_asset.read() {
let Some(image) = ev.image_handle() else {
continue;
};
Expand Down
Loading

0 comments on commit ce362f3

Please sign in to comment.