Skip to content

Commit

Permalink
remove ColorBlindnessParams
Browse files Browse the repository at this point in the history
This commit moves `mode` and `enabled` into `ColorBlindnessCamera`,
which allows for multiple cameras with different modes.

This should allow multiple windows to work correctly, but it hasn't been tested yet.
  • Loading branch information
annieversary committed Aug 14, 2022
1 parent 0a8a176 commit 02568fd
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 173 deletions.
282 changes: 123 additions & 159 deletions crates/bevy_color_blindness/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
//! color blindness.
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, AssetServer, Assets, Handle, HandleUntyped};
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_core_pipeline::core_2d::Camera2dBundle;
use bevy_ecs::{
component::Component,
entity::Entity,
query::Added,
query::{Added, Changed},
system::{Commands, Query, Res, ResMut},
};
use bevy_math::{Vec2, Vec3};
Expand Down Expand Up @@ -44,9 +44,6 @@ use bevy_window::Windows;
/// First, add the [`ColorBlindnessPlugin`] to your app, and add [`ColorBlindnessCamera`] to
/// your main camera.
///
/// You can change the selected mode by inserting [`ColorBlindnessParams`] before the plugin.
/// You can also skip this, and change the resource at any time in a system.
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins};
/// # use bevy_color_blindness::*;
Expand All @@ -57,10 +54,6 @@ use bevy_window::Windows;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .insert_resource(ColorBlindnessParams {
/// mode: ColorBlindnessMode::Deuteranomaly,
/// enable: true,
/// })
/// // add the plugin
/// .add_plugin(ColorBlindnessPlugin)
/// .add_startup_system(setup)
Expand All @@ -73,11 +66,13 @@ use bevy_window::Windows;
/// // create the camera
/// commands
/// .spawn_bundle(Camera3dBundle {
/// transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
/// ..Default::default()
/// transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
/// ..Default::default()
/// })
/// // IMPORTANT: add this component to your main camera
/// .insert(ColorBlindnessCamera);
/// .insert(ColorBlindnessCamera {
/// mode: ColorBlindnessMode::Deuteranopia,
/// enabled: true,
/// });
/// }
/// ```
///
Expand All @@ -89,19 +84,15 @@ use bevy_window::Windows;
pub struct ColorBlindnessPlugin;
impl Plugin for ColorBlindnessPlugin {
fn build(&self, app: &mut App) {
let world = &mut app.world;
world.get_resource_or_insert_with(ColorBlindnessParams::default);

load_internal_asset!(
app,
COLOR_BLINDNESS_SHADER_HANDLE,
"color_blindness.wgsl",
Shader::from_wgsl
);

app.add_plugin(Material2dPlugin::<PostProcessingMaterial>::default())
.add_startup_system(setup)
.add_system(set_camera_target)
app.add_plugin(Material2dPlugin::<ColorBlindnessMaterial>::default())
.add_system(setup_new_color_blindness_cameras)
.add_system(update_percentages);
}
}
Expand All @@ -110,41 +101,6 @@ impl Plugin for ColorBlindnessPlugin {
const COLOR_BLINDNESS_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3937837360667146578);

/// Resource which selects the type of color blindness to simulate
///
/// # Example
///
/// This system will only enable the simulation while `Space` is held, and will cycle through
/// the different modes when `N` is pressed.
///
/// ```rust
/// # use bevy_app::prelude::*;
/// # use bevy_color_blindness::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_input::prelude::*;
/// # use bevy_math::prelude::*;
/// # use bevy_transform::prelude::*;
/// fn cycle_mode(input: Res<Input<KeyCode>>, mut params: ResMut<ColorBlindnessParams>) {
/// if input.just_pressed(KeyCode::N) {
/// params.mode.cycle();
/// println!("Changed to {:?}", params.mode);
/// }
///
/// params.enable = input.pressed(KeyCode::Space);
/// }
/// ```
#[derive(Default, Debug)]
pub struct ColorBlindnessParams {
/// Selects the color blindness mode to use
///
/// Defaults to `ColorBlindnessMode::Normal`
pub mode: ColorBlindnessMode,
/// Controls whether color blindness simulation is enabled
///
/// Defaults to `false`
pub enable: bool,
}

/// The different modes of color blindness simulation supported.
#[derive(Clone, Default, Debug)]
pub enum ColorBlindnessMode {
Expand Down Expand Up @@ -267,11 +223,16 @@ impl ColorBlindnessMode {
/// # use bevy_color_blindness::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_input::prelude::*;
/// fn cycle_mode(input: Res<Input<KeyCode>>, mut params: ResMut<ColorBlindnessParams>) {
/// fn change_mode(input: Res<Input<KeyCode>>, mut cameras: Query<&mut ColorBlindnessCamera>) {
/// for mut camera in &mut cameras {
/// // cycle through the modes by pressing N
/// if input.just_pressed(KeyCode::N) {
/// params.mode.cycle();
/// println!("Changed to {:?}", params.mode);
/// camera.mode.cycle();
/// println!("Changed to {:?}", camera.mode);
/// }
///
/// camera.enabled = input.pressed(KeyCode::Space);
/// }
/// }
/// ```
pub fn cycle(&mut self) {
Expand All @@ -292,7 +253,7 @@ impl ColorBlindnessMode {
/// Post processing material that applies color blindness simulation to `image`
#[derive(AsBindGroup, TypeUuid, Clone)]
#[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"]
struct PostProcessingMaterial {
struct ColorBlindnessMaterial {
/// In this example, this image will be the result of the main camera.
#[texture(0)]
#[sampler(1)]
Expand All @@ -302,7 +263,7 @@ struct PostProcessingMaterial {
percentages: ColorBlindnessPercentages,
}

impl Material2d for PostProcessingMaterial {
impl Material2d for ColorBlindnessMaterial {
fn fragment_shader() -> ShaderRef {
ShaderRef::Handle(COLOR_BLINDNESS_SHADER_HANDLE.typed())
}
Expand All @@ -320,34 +281,31 @@ impl Material2d for PostProcessingMaterial {
/// If for some reason this behavior is not desired, please open an issue.
///
/// [`UiCameraConfig`]: bevy_ui::entity::UiCameraConfig
#[derive(Component)]
pub struct ColorBlindnessCamera;

/// sets the target for newly added `ColorBlindCamera`s
fn set_camera_target(
mut commands: Commands,
mut query: Query<(Entity, &mut Camera), Added<ColorBlindnessCamera>>,
inner: Res<InternalResource>,
) {
for (entity, mut camera) in query.iter_mut() {
camera.target = RenderTarget::Image(inner.image.clone());
commands
.entity(entity)
.insert(UiCameraConfig { show_ui: false });
}
#[derive(Component, Default)]
pub struct ColorBlindnessCamera {
/// Selects the color blindness mode to use
///
/// Defaults to `ColorBlindnessMode::Normal`
pub mode: ColorBlindnessMode,
/// Controls whether color blindness simulation is enabled
///
/// Defaults to `false`
pub enabled: bool,
}

/// updates the percentages in the post processing material when the `ColorBlindnessMode` changes in Params
/// updates the percentages in the post processing material when the values in `ColorBlindnessCamera` change
fn update_percentages(
params: Res<ColorBlindnessParams>,
inner: Res<InternalResource>,
mut materials: ResMut<Assets<PostProcessingMaterial>>,
cameras: Query<
(&Handle<ColorBlindnessMaterial>, &ColorBlindnessCamera),
Changed<ColorBlindnessCamera>,
>,
mut materials: ResMut<Assets<ColorBlindnessMaterial>>,
) {
if params.is_changed() {
let mut mat = materials.get_mut(&inner.post).unwrap();
for (handle, camera) in &cameras {
let mut mat = materials.get_mut(handle).unwrap();

let mode = if params.enable {
&params.mode
let mode = if camera.enabled {
&camera.mode
} else {
&ColorBlindnessMode::Normal
};
Expand All @@ -356,96 +314,102 @@ fn update_percentages(
}
}

/// internal resource which holds the handles
struct InternalResource {
image: Handle<Image>,
post: Handle<PostProcessingMaterial>,
}

/// creates the image, the material, the final camera, and the whole post-processing pipeline
///
/// based on the post-processing example
/// `https://github.com/bevyengine/bevy/blob/main/examples/shader/post_processing.rs`
fn setup(
/// sets up post processing for cameras that have had `ColorBlindnessCamera` added
fn setup_new_color_blindness_cameras(
mut commands: Commands,
mut windows: ResMut<Windows>,
windows: Res<Windows>,
mut meshes: ResMut<Assets<Mesh>>,
mut post_processing_materials: ResMut<Assets<PostProcessingMaterial>>,
mut post_processing_materials: ResMut<Assets<ColorBlindnessMaterial>>,
mut images: ResMut<Assets<Image>>,
asset_server: Res<AssetServer>,
params: Res<ColorBlindnessParams>,
mut cameras: Query<(Entity, &mut Camera, &ColorBlindnessCamera), Added<ColorBlindnessCamera>>,
) {
asset_server.watch_for_changes().unwrap();
for (entity, mut camera, color_blindness_camera) in &mut cameras {
// Get the size the camera is rendering to
let size = match &camera.target {
RenderTarget::Window(window_id) => {
let window = windows.get(*window_id).expect("ColorBlindnessCamera is rendering to a window, but this window could not be found");
Extent3d {
width: window.physical_width(),
height: window.physical_height(),
..Default::default()
}
}
RenderTarget::Image(handle) => {
let image = images.get(handle).expect(
"ColorBlindnessCamera is rendering to an Image, but this Image could not be found",
);
image.texture_descriptor.size
}
};

let window = windows.get_primary_mut().unwrap();
let size = Extent3d {
width: window.physical_width(),
height: window.physical_height(),
..Default::default()
};
// This is the texture that will be rendered to.
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::bevy_default(),
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
},
..Default::default()
};

// This is the texture that will be rendered to.
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::bevy_default(),
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
},
..Default::default()
};
// fill image.data with zeroes
image.resize(size);

// fill image.data with zeroes
image.resize(size);
let image_handle = images.add(image);

let image_handle = images.add(image);
// This specifies the layer used for the post processing camera, which will be attached to the post processing camera and 2d quad.
let post_processing_pass_layer =
RenderLayers::layer((RenderLayers::TOTAL_LAYERS - 1) as u8);

// This specifies the layer used for the post processing camera, which will be attached to the post processing camera and 2d quad.
let post_processing_pass_layer = RenderLayers::layer((RenderLayers::TOTAL_LAYERS - 1) as u8);
let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new(
size.width as f32,
size.height as f32,
))));

let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new(
size.width as f32,
size.height as f32,
))));
// This material has the texture that has been rendered.
let material_handle = post_processing_materials.add(ColorBlindnessMaterial {
source_image: image_handle.clone(),
percentages: color_blindness_camera.mode.percentages(),
});

// This material has the texture that has been rendered.
let material_handle = post_processing_materials.add(PostProcessingMaterial {
source_image: image_handle.clone(),
percentages: params.mode.percentages(),
});
commands
.entity(entity)
// add the handle to the camera so we can access it and change the percentages
.insert(material_handle.clone())
// also disable show_ui so UI elements don't get rendered twice
.insert(UiCameraConfig { show_ui: false });

commands.insert_resource(InternalResource {
image: image_handle,
post: material_handle.clone(),
});
camera.target = RenderTarget::Image(image_handle);

// Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader.
commands
.spawn_bundle(MaterialMesh2dBundle {
mesh: quad_handle.into(),
material: material_handle,
transform: Transform {
translation: Vec3::new(0.0, 0.0, 1.5),
// Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader.
commands
.spawn_bundle(MaterialMesh2dBundle {
mesh: quad_handle.into(),
material: material_handle,
transform: Transform {
translation: Vec3::new(0.0, 0.0, 1.5),
..Default::default()
},
..Default::default()
},
..Default::default()
})
.insert(post_processing_pass_layer);
})
.insert(post_processing_pass_layer);

// The post-processing pass camera.
commands
.spawn_bundle(Camera2dBundle {
camera: Camera {
// renders after the first main camera which has default value: 0.
priority: 1,
..Default::default()
},
..Camera2dBundle::default()
})
.insert(post_processing_pass_layer);
// The post-processing pass camera.
commands
.spawn_bundle(Camera2dBundle {
camera: Camera {
// renders after the first main camera which has default value: 0.
priority: 1,
..Default::default()
},
..Camera2dBundle::default()
})
.insert(post_processing_pass_layer);
}
}
Loading

0 comments on commit 02568fd

Please sign in to comment.