diff options
-rw-r--r-- | .github/workflows/docs.yml | 50 | ||||
-rw-r--r-- | .github/workflows/rust.yml | 24 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | assets/shaders/dither_post_process.wgsl | 10 | ||||
-rw-r--r-- | src/components.rs | 22 | ||||
-rw-r--r-- | src/lib.rs | 49 | ||||
-rw-r--r-- | src/nodes.rs | 21 | ||||
-rw-r--r-- | src/resources.rs | 2 |
9 files changed, 153 insertions, 43 deletions
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..3191cc0 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,50 @@ +name: Docs +on: + push: + branches: [master] +permissions: + contents: read + pages: write + id-token: write +concurrency: + group: deploy + cancel-in-progress: false +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Dependencies + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + - name: Configure cache + uses: Swatinem/rust-cache@v2 + - name: Setup pages + id: pages + uses: actions/configure-pages@v4 + - name: Clean docs folder + run: cargo clean --doc + - name: Build docs + run: cargo doc --no-deps + - name: Add redirect + run: echo '<meta http-equiv="refresh" content="0;url=bevy_dither_post_process/index.html">' > target/doc/index.html + - name: Remove lock file + run: rm target/doc/.lock + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + deploy: + name: Deploy + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..e38739d --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,24 @@ +name: Rust + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Install Dependencies + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose @@ -1,6 +1,6 @@ [package] -name = "grex_dither_post_process" -version = "0.1.3" +name = "bevy_dither_post_process" +version = "0.1.4" edition = "2021" [dependencies.bevy] @@ -1,4 +1,4 @@ -# grex_dither_post_process +# bevy_dither_post_process A plugin for the [Bevy](https://bevyengine.org) engine which adds a dither post-processing effect. @@ -10,7 +10,7 @@ The effect is implemented as a bilevel ordered dither using a Bayer matrix with ![](./doc/screenshot_plant.png) Configuration Used: ```rs -grex_dither_post_process::components::DitherPostProcessSettings::new(3, &asset_server); +bevy_dither_post_process::components::DitherPostProcessSettings::new(3, &asset_server); ``` ## Compatibility @@ -22,8 +22,8 @@ grex_dither_post_process::components::DitherPostProcessSettings::new(3, &asset_s ### Using git URL in Cargo.toml ```toml -[dependencies.grex_dither_post_process] -git = "https://github.com/exvacuum/grex_dither_post_process.git" +[dependencies.bevy_dither_post_process] +git = "https://github.com/exvacuum/bevy_dither_post_process.git" ``` ## Usage @@ -31,13 +31,13 @@ git = "https://github.com/exvacuum/grex_dither_post_process.git" In `main.rs`: ```rs use bevy::prelude::*; -use grex_dither_post_process; +use bevy_dither_post_process; fn main() { App::new() .add_plugins(( DefaultPlugins, - grex_dither_post_process::DitherPostProcessPlugin, + bevy_dither_post_process::DitherPostProcessPlugin, )) .run(); } @@ -47,7 +47,7 @@ When spawning a camera: ```rs commands.spawn(( // Camera3dBundle... - grex_dither_post_process::components::DitherPostProcessSettings::new(level, &asset_server); + bevy_dither_post_process::components::DitherPostProcessSettings::new(level, &asset_server); )); ``` diff --git a/assets/shaders/dither_post_process.wgsl b/assets/shaders/dither_post_process.wgsl index 37c25fc..6440d0d 100644 --- a/assets/shaders/dither_post_process.wgsl +++ b/assets/shaders/dither_post_process.wgsl @@ -9,16 +9,16 @@ fn fragment( in: FullscreenVertexOutput ) -> @location(0) vec4<f32> { - let screen_size = vec2f(textureDimensions(screen_texture)); - let threshold_map_size = vec2f(textureDimensions(threshold_map_texture)); - let pixel_position = floor(in.uv * screen_size); - let map_position = (pixel_position % threshold_map_size) / threshold_map_size; + let screen_size = vec2i(textureDimensions(screen_texture)); + let threshold_map_size = vec2i(textureDimensions(threshold_map_texture)); + let pixel_position = vec2i(floor(in.uv * vec2f(screen_size))); + let map_position = vec2f(pixel_position % threshold_map_size) / vec2f(threshold_map_size); let threshold = textureSample(threshold_map_texture, threshold_map_sampler, map_position).r; let base_color = textureSample(screen_texture, screen_sampler, in.uv); let luma = (0.2126 * base_color.r + 0.7152 * base_color.g + 0.0722 * base_color.b); - let value = f32(luma > threshold); + let value = f32(luma >= threshold); return vec4f(value, value, value, 1.0); } diff --git a/src/components.rs b/src/components.rs index d22524c..1768448 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,19 +1,21 @@ use bevy::{ prelude::*, render::{ + extract_component::ExtractComponent, render_asset::RenderAssetUsages, - render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, extract_component::ExtractComponent, + render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, }, }; +/// Component which, when inserted into an entity with a camera, enables the dither post-processing +/// effect. #[derive(Component, ExtractComponent, Clone)] pub struct DitherPostProcessSettings(Handle<Image>); impl DitherPostProcessSettings { - pub fn new( - level: u32, - asset_server: &AssetServer, - ) -> Self { + /// Constructs a new instance ofthis component, enabling the dither effect using a bayer + /// matrix of the given level. A given level *n* will generate a square bayer matrix with a size of *(n+1)^2*. + pub fn new(level: u32, asset_server: &AssetServer) -> Self { let power = level + 1; let map_size: u32 = 1 << power; let mut buffer = Vec::<u8>::new(); @@ -38,7 +40,6 @@ impl DitherPostProcessSettings { let value = ((result as f32 / map_size.pow(2) as f32) * 255.0) as u8; buffer.push(value); } - } let mut image = Image::new( @@ -52,15 +53,16 @@ impl DitherPostProcessSettings { TextureFormat::R8Unorm, RenderAssetUsages::RENDER_WORLD, ); - image.texture_descriptor.usage = - TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING; + image.texture_descriptor.usage = TextureUsages::COPY_DST + | TextureUsages::STORAGE_BINDING + | TextureUsages::TEXTURE_BINDING; let handle = asset_server.add(image); Self(handle) } - - pub fn handle(&self) -> Handle<Image> { + /// Gets the handle of the texture representing this component's Bayer matrix + pub fn handle(&self) -> Handle<Image> { self.0.clone() } } @@ -1,40 +1,57 @@ -use bevy::{prelude::*, render::{RenderApp, render_graph::{RenderGraphApp, ViewNodeRunner}, extract_component::ExtractComponentPlugin}, asset::embedded_asset, core_pipeline::core_3d::graph::{Core3d, Node3d}}; +#![warn(missing_docs)] + +//! A plugin for the Bevy game engine which provides a black and white dither post-process effect +//! using Bayer ordered dithering. + +use bevy::{ + asset::embedded_asset, + core_pipeline::core_3d::graph::{Core3d, Node3d}, + prelude::*, + render::{ + extract_component::ExtractComponentPlugin, + render_graph::{RenderGraphApp, ViewNodeRunner}, + RenderApp, + }, +}; use crate::components::DitherPostProcessSettings; pub use nodes::DitherRenderLabel; +/// Plugin which provides dither post-processing functionality pub struct DitherPostProcessPlugin; +/// Components used by this plugin pub mod components; -mod resources; + mod nodes; +mod resources; impl Plugin for DitherPostProcessPlugin { fn build(&self, app: &mut App) { embedded_asset!(app, "../assets/shaders/dither_post_process.wgsl"); - app.add_plugins(( - ExtractComponentPlugin::<DitherPostProcessSettings>::default(), - )); + app.add_plugins((ExtractComponentPlugin::<DitherPostProcessSettings>::default(),)); let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; - render_app.add_render_graph_node::<ViewNodeRunner<nodes::DitherRenderNode>>( - Core3d, - nodes::DitherRenderLabel, - ).add_render_graph_edges( - Core3d, - ( - Node3d::Tonemapping, + render_app + .add_render_graph_node::<ViewNodeRunner<nodes::DitherRenderNode>>( + Core3d, nodes::DitherRenderLabel, - Node3d::EndMainPassPostProcessing, - ), - ); + ) + .add_render_graph_edges( + Core3d, + ( + Node3d::Tonemapping, + nodes::DitherRenderLabel, + Node3d::EndMainPassPostProcessing, + ), + ); } - + fn finish(&self, app: &mut App) { let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; diff --git a/src/nodes.rs b/src/nodes.rs index b33b2a0..377718a 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -1,8 +1,22 @@ -use bevy::{prelude::*, render::{render_graph::{ViewNode, NodeRunError, RenderGraphContext, RenderLabel}, view::ViewTarget, renderer::RenderContext, render_resource::{PipelineCache, BindGroupEntries, RenderPassDescriptor, RenderPassColorAttachment, Operations}, render_asset::RenderAssets}, ecs::query::QueryItem}; +use bevy::{ + ecs::query::QueryItem, + prelude::*, + render::{ + render_asset::RenderAssets, + render_graph::{NodeRunError, RenderGraphContext, RenderLabel, ViewNode}, + render_resource::{ + BindGroupEntries, Operations, PipelineCache, RenderPassColorAttachment, + RenderPassDescriptor, + }, + renderer::RenderContext, + view::ViewTarget, + }, +}; use super::components; use super::resources; +/// Label for dither post-process effect render node. #[derive(RenderLabel, Clone, Eq, PartialEq, Hash, Debug)] pub struct DitherRenderLabel; @@ -31,7 +45,10 @@ impl ViewNode for DitherRenderNode { let post_process = view_target.post_process_write(); - let Some(threshold_map) = world.resource::<RenderAssets<Image>>().get(dither_post_process_settings.handle()) else { + let Some(threshold_map) = world + .resource::<RenderAssets<Image>>() + .get(dither_post_process_settings.handle()) + else { warn!("Failed to get threshold map, skipping..."); return Ok(()); }; diff --git a/src/resources.rs b/src/resources.rs index d900f18..6dab3ee 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -43,7 +43,7 @@ impl FromWorld for DitherPostProcessPipeline { let threshold_map_sampler = render_device.create_sampler(&SamplerDescriptor::default()); let shader = world.resource::<AssetServer>().load::<Shader>( - "embedded://grex_dither_post_process/../assets/shaders/dither_post_process.wgsl", + "embedded://bevy_dither_post_process/../assets/shaders/dither_post_process.wgsl", ); let pipeline_id = |