Skip to content

Commit aa0fcb0

Browse files
committed
remove ColorBlindnessParams
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.
1 parent 0a8a176 commit aa0fcb0

File tree

2 files changed

+136
-173
lines changed

2 files changed

+136
-173
lines changed

crates/bevy_color_blindness/src/lib.rs

Lines changed: 123 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
//! color blindness.
33
44
use bevy_app::{App, Plugin};
5-
use bevy_asset::{load_internal_asset, AssetServer, Assets, Handle, HandleUntyped};
5+
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
66
use bevy_core_pipeline::core_2d::Camera2dBundle;
77
use bevy_ecs::{
88
component::Component,
99
entity::Entity,
10-
query::Added,
10+
query::{Added, Changed},
1111
system::{Commands, Query, Res, ResMut},
1212
};
1313
use bevy_math::{Vec2, Vec3};
@@ -44,9 +44,6 @@ use bevy_window::Windows;
4444
/// First, add the [`ColorBlindnessPlugin`] to your app, and add [`ColorBlindnessCamera`] to
4545
/// your main camera.
4646
///
47-
/// You can change the selected mode by inserting [`ColorBlindnessParams`] before the plugin.
48-
/// You can also skip this, and change the resource at any time in a system.
49-
///
5047
/// ```rust,no_run
5148
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins};
5249
/// # use bevy_color_blindness::*;
@@ -57,10 +54,6 @@ use bevy_window::Windows;
5754
/// fn main() {
5855
/// App::new()
5956
/// .add_plugins(DefaultPlugins)
60-
/// .insert_resource(ColorBlindnessParams {
61-
/// mode: ColorBlindnessMode::Deuteranomaly,
62-
/// enable: true,
63-
/// })
6457
/// // add the plugin
6558
/// .add_plugin(ColorBlindnessPlugin)
6659
/// .add_startup_system(setup)
@@ -73,11 +66,13 @@ use bevy_window::Windows;
7366
/// // create the camera
7467
/// commands
7568
/// .spawn_bundle(Camera3dBundle {
76-
/// transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
77-
/// ..Default::default()
69+
/// transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
70+
/// ..Default::default()
7871
/// })
79-
/// // IMPORTANT: add this component to your main camera
80-
/// .insert(ColorBlindnessCamera);
72+
/// .insert(ColorBlindnessCamera {
73+
/// mode: ColorBlindnessMode::Deuteranopia,
74+
/// enabled: true,
75+
/// });
8176
/// }
8277
/// ```
8378
///
@@ -89,19 +84,15 @@ use bevy_window::Windows;
8984
pub struct ColorBlindnessPlugin;
9085
impl Plugin for ColorBlindnessPlugin {
9186
fn build(&self, app: &mut App) {
92-
let world = &mut app.world;
93-
world.get_resource_or_insert_with(ColorBlindnessParams::default);
94-
9587
load_internal_asset!(
9688
app,
9789
COLOR_BLINDNESS_SHADER_HANDLE,
9890
"color_blindness.wgsl",
9991
Shader::from_wgsl
10092
);
10193

102-
app.add_plugin(Material2dPlugin::<PostProcessingMaterial>::default())
103-
.add_startup_system(setup)
104-
.add_system(set_camera_target)
94+
app.add_plugin(Material2dPlugin::<ColorBlindnessMaterial>::default())
95+
.add_system(setup_new_color_blindness_cameras)
10596
.add_system(update_percentages);
10697
}
10798
}
@@ -110,41 +101,6 @@ impl Plugin for ColorBlindnessPlugin {
110101
const COLOR_BLINDNESS_SHADER_HANDLE: HandleUntyped =
111102
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3937837360667146578);
112103

113-
/// Resource which selects the type of color blindness to simulate
114-
///
115-
/// # Example
116-
///
117-
/// This system will only enable the simulation while `Space` is held, and will cycle through
118-
/// the different modes when `N` is pressed.
119-
///
120-
/// ```rust
121-
/// # use bevy_app::prelude::*;
122-
/// # use bevy_color_blindness::*;
123-
/// # use bevy_ecs::prelude::*;
124-
/// # use bevy_input::prelude::*;
125-
/// # use bevy_math::prelude::*;
126-
/// # use bevy_transform::prelude::*;
127-
/// fn cycle_mode(input: Res<Input<KeyCode>>, mut params: ResMut<ColorBlindnessParams>) {
128-
/// if input.just_pressed(KeyCode::N) {
129-
/// params.mode.cycle();
130-
/// println!("Changed to {:?}", params.mode);
131-
/// }
132-
///
133-
/// params.enable = input.pressed(KeyCode::Space);
134-
/// }
135-
/// ```
136-
#[derive(Default, Debug)]
137-
pub struct ColorBlindnessParams {
138-
/// Selects the color blindness mode to use
139-
///
140-
/// Defaults to `ColorBlindnessMode::Normal`
141-
pub mode: ColorBlindnessMode,
142-
/// Controls whether color blindness simulation is enabled
143-
///
144-
/// Defaults to `false`
145-
pub enable: bool,
146-
}
147-
148104
/// The different modes of color blindness simulation supported.
149105
#[derive(Clone, Default, Debug)]
150106
pub enum ColorBlindnessMode {
@@ -267,11 +223,16 @@ impl ColorBlindnessMode {
267223
/// # use bevy_color_blindness::*;
268224
/// # use bevy_ecs::prelude::*;
269225
/// # use bevy_input::prelude::*;
270-
/// fn cycle_mode(input: Res<Input<KeyCode>>, mut params: ResMut<ColorBlindnessParams>) {
226+
/// fn change_mode(input: Res<Input<KeyCode>>, mut cameras: Query<&mut ColorBlindnessCamera>) {
227+
/// for mut camera in &mut cameras {
228+
/// // cycle through the modes by pressing N
271229
/// if input.just_pressed(KeyCode::N) {
272-
/// params.mode.cycle();
273-
/// println!("Changed to {:?}", params.mode);
230+
/// camera.mode.cycle();
231+
/// println!("Changed to {:?}", camera.mode);
274232
/// }
233+
///
234+
/// camera.enabled = input.pressed(KeyCode::Space);
235+
/// }
275236
/// }
276237
/// ```
277238
pub fn cycle(&mut self) {
@@ -292,7 +253,7 @@ impl ColorBlindnessMode {
292253
/// Post processing material that applies color blindness simulation to `image`
293254
#[derive(AsBindGroup, TypeUuid, Clone)]
294255
#[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"]
295-
struct PostProcessingMaterial {
256+
struct ColorBlindnessMaterial {
296257
/// In this example, this image will be the result of the main camera.
297258
#[texture(0)]
298259
#[sampler(1)]
@@ -302,7 +263,7 @@ struct PostProcessingMaterial {
302263
percentages: ColorBlindnessPercentages,
303264
}
304265

305-
impl Material2d for PostProcessingMaterial {
266+
impl Material2d for ColorBlindnessMaterial {
306267
fn fragment_shader() -> ShaderRef {
307268
ShaderRef::Handle(COLOR_BLINDNESS_SHADER_HANDLE.typed())
308269
}
@@ -320,34 +281,31 @@ impl Material2d for PostProcessingMaterial {
320281
/// If for some reason this behavior is not desired, please open an issue.
321282
///
322283
/// [`UiCameraConfig`]: bevy_ui::entity::UiCameraConfig
323-
#[derive(Component)]
324-
pub struct ColorBlindnessCamera;
325-
326-
/// sets the target for newly added `ColorBlindCamera`s
327-
fn set_camera_target(
328-
mut commands: Commands,
329-
mut query: Query<(Entity, &mut Camera), Added<ColorBlindnessCamera>>,
330-
inner: Res<InternalResource>,
331-
) {
332-
for (entity, mut camera) in query.iter_mut() {
333-
camera.target = RenderTarget::Image(inner.image.clone());
334-
commands
335-
.entity(entity)
336-
.insert(UiCameraConfig { show_ui: false });
337-
}
284+
#[derive(Component, Default)]
285+
pub struct ColorBlindnessCamera {
286+
/// Selects the color blindness mode to use
287+
///
288+
/// Defaults to `ColorBlindnessMode::Normal`
289+
pub mode: ColorBlindnessMode,
290+
/// Controls whether color blindness simulation is enabled
291+
///
292+
/// Defaults to `false`
293+
pub enabled: bool,
338294
}
339295

340-
/// updates the percentages in the post processing material when the `ColorBlindnessMode` changes in Params
296+
/// updates the percentages in the post processing material when the values in `ColorBlindnessCamera` change
341297
fn update_percentages(
342-
params: Res<ColorBlindnessParams>,
343-
inner: Res<InternalResource>,
344-
mut materials: ResMut<Assets<PostProcessingMaterial>>,
298+
cameras: Query<
299+
(&Handle<ColorBlindnessMaterial>, &ColorBlindnessCamera),
300+
Changed<ColorBlindnessCamera>,
301+
>,
302+
mut materials: ResMut<Assets<ColorBlindnessMaterial>>,
345303
) {
346-
if params.is_changed() {
347-
let mut mat = materials.get_mut(&inner.post).unwrap();
304+
for (handle, camera) in &cameras {
305+
let mut mat = materials.get_mut(handle).unwrap();
348306

349-
let mode = if params.enable {
350-
&params.mode
307+
let mode = if camera.enabled {
308+
&camera.mode
351309
} else {
352310
&ColorBlindnessMode::Normal
353311
};
@@ -356,96 +314,102 @@ fn update_percentages(
356314
}
357315
}
358316

359-
/// internal resource which holds the handles
360-
struct InternalResource {
361-
image: Handle<Image>,
362-
post: Handle<PostProcessingMaterial>,
363-
}
364-
365-
/// creates the image, the material, the final camera, and the whole post-processing pipeline
366-
///
367-
/// based on the post-processing example
368-
/// `https://github.com/bevyengine/bevy/blob/main/examples/shader/post_processing.rs`
369-
fn setup(
317+
/// sets up post processing for cameras that have had `ColorBlindnessCamera` added
318+
fn setup_new_color_blindness_cameras(
370319
mut commands: Commands,
371-
mut windows: ResMut<Windows>,
320+
windows: Res<Windows>,
372321
mut meshes: ResMut<Assets<Mesh>>,
373-
mut post_processing_materials: ResMut<Assets<PostProcessingMaterial>>,
322+
mut post_processing_materials: ResMut<Assets<ColorBlindnessMaterial>>,
374323
mut images: ResMut<Assets<Image>>,
375-
asset_server: Res<AssetServer>,
376-
params: Res<ColorBlindnessParams>,
324+
mut cameras: Query<(Entity, &mut Camera, &ColorBlindnessCamera), Added<ColorBlindnessCamera>>,
377325
) {
378-
asset_server.watch_for_changes().unwrap();
326+
for (entity, mut camera, color_blindness_camera) in &mut cameras {
327+
// Get the size the camera is rendering to
328+
let size = match &camera.target {
329+
RenderTarget::Window(window_id) => {
330+
let window = windows.get(*window_id).expect("ColorBlindnessCamera is rendering to a window, but this window could not be found");
331+
Extent3d {
332+
width: window.physical_width(),
333+
height: window.physical_height(),
334+
..Default::default()
335+
}
336+
}
337+
RenderTarget::Image(handle) => {
338+
let image = images.get(handle).expect(
339+
"ColorBlindnessCamera is rendering to an Image, but this Image could not be found",
340+
);
341+
image.texture_descriptor.size
342+
}
343+
};
379344

380-
let window = windows.get_primary_mut().unwrap();
381-
let size = Extent3d {
382-
width: window.physical_width(),
383-
height: window.physical_height(),
384-
..Default::default()
385-
};
345+
// This is the texture that will be rendered to.
346+
let mut image = Image {
347+
texture_descriptor: TextureDescriptor {
348+
label: None,
349+
size,
350+
dimension: TextureDimension::D2,
351+
format: TextureFormat::bevy_default(),
352+
mip_level_count: 1,
353+
sample_count: 1,
354+
usage: TextureUsages::TEXTURE_BINDING
355+
| TextureUsages::COPY_DST
356+
| TextureUsages::RENDER_ATTACHMENT,
357+
},
358+
..Default::default()
359+
};
386360

387-
// This is the texture that will be rendered to.
388-
let mut image = Image {
389-
texture_descriptor: TextureDescriptor {
390-
label: None,
391-
size,
392-
dimension: TextureDimension::D2,
393-
format: TextureFormat::bevy_default(),
394-
mip_level_count: 1,
395-
sample_count: 1,
396-
usage: TextureUsages::TEXTURE_BINDING
397-
| TextureUsages::COPY_DST
398-
| TextureUsages::RENDER_ATTACHMENT,
399-
},
400-
..Default::default()
401-
};
361+
// fill image.data with zeroes
362+
image.resize(size);
402363

403-
// fill image.data with zeroes
404-
image.resize(size);
364+
let image_handle = images.add(image);
405365

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

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

411-
let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new(
412-
size.width as f32,
413-
size.height as f32,
414-
))));
375+
// This material has the texture that has been rendered.
376+
let material_handle = post_processing_materials.add(ColorBlindnessMaterial {
377+
source_image: image_handle.clone(),
378+
percentages: color_blindness_camera.mode.percentages(),
379+
});
415380

416-
// This material has the texture that has been rendered.
417-
let material_handle = post_processing_materials.add(PostProcessingMaterial {
418-
source_image: image_handle.clone(),
419-
percentages: params.mode.percentages(),
420-
});
381+
commands
382+
.entity(entity)
383+
// add the handle to the camera so we can access it and change the percentages
384+
.insert(material_handle.clone())
385+
// also disable show_ui so UI elements don't get rendered twice
386+
.insert(UiCameraConfig { show_ui: false });
421387

422-
commands.insert_resource(InternalResource {
423-
image: image_handle,
424-
post: material_handle.clone(),
425-
});
388+
camera.target = RenderTarget::Image(image_handle);
426389

427-
// Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader.
428-
commands
429-
.spawn_bundle(MaterialMesh2dBundle {
430-
mesh: quad_handle.into(),
431-
material: material_handle,
432-
transform: Transform {
433-
translation: Vec3::new(0.0, 0.0, 1.5),
390+
// Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader.
391+
commands
392+
.spawn_bundle(MaterialMesh2dBundle {
393+
mesh: quad_handle.into(),
394+
material: material_handle,
395+
transform: Transform {
396+
translation: Vec3::new(0.0, 0.0, 1.5),
397+
..Default::default()
398+
},
434399
..Default::default()
435-
},
436-
..Default::default()
437-
})
438-
.insert(post_processing_pass_layer);
400+
})
401+
.insert(post_processing_pass_layer);
439402

440-
// The post-processing pass camera.
441-
commands
442-
.spawn_bundle(Camera2dBundle {
443-
camera: Camera {
444-
// renders after the first main camera which has default value: 0.
445-
priority: 1,
446-
..Default::default()
447-
},
448-
..Camera2dBundle::default()
449-
})
450-
.insert(post_processing_pass_layer);
403+
// The post-processing pass camera.
404+
commands
405+
.spawn_bundle(Camera2dBundle {
406+
camera: Camera {
407+
// renders after the first main camera which has default value: 0.
408+
priority: 1,
409+
..Default::default()
410+
},
411+
..Camera2dBundle::default()
412+
})
413+
.insert(post_processing_pass_layer);
414+
}
451415
}

0 commit comments

Comments
 (0)