diff --git a/Cargo.toml b/Cargo.toml index b0920d9ecd0fa..e8bdbaeaa1df1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ default = [ "animation", "bevy_asset", "bevy_audio", + "bevy_color_blindness", "bevy_gilrs", "bevy_scene", "bevy_winit", @@ -57,6 +58,7 @@ render = [ bevy_animation = ["bevy_internal/bevy_animation"] bevy_asset = ["bevy_internal/bevy_asset"] bevy_audio = ["bevy_internal/bevy_audio"] +bevy_color_blindness = ["bevy_internal/bevy_color_blindness"] bevy_core_pipeline = ["bevy_internal/bevy_core_pipeline"] bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] bevy_gilrs = ["bevy_internal/bevy_gilrs"] @@ -484,6 +486,17 @@ description = "Showcases wireframe rendering" category = "3D Rendering" wasm = true +# Accessibility +[[example]] +name = "color_blindness_simulation" +path = "examples/accessibility/color_blindness_simulation.rs" + +[package.metadata.example.color_blindness_simulation] +name = "Color Blindness Simulation" +description = "Modify your app's rendering output to simulate color blindness" +category = "Accessibility" +wasm = true + # Animation [[example]] name = "animated_fox" diff --git a/crates/bevy_color_blindness/Cargo.toml b/crates/bevy_color_blindness/Cargo.toml new file mode 100644 index 0000000000000..304ed77a72692 --- /dev/null +++ b/crates/bevy_color_blindness/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "bevy_color_blindness" +version = "0.9.0-dev" +edition = "2021" +description = "Provides color blindness simulation for Bevy Engine" +homepage = "https://bevyengine.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[dependencies] +# bevy +bevy_app = { path = "../bevy_app", version = "0.9.0-dev" } +bevy_asset = { path = "../bevy_asset", version = "0.9.0-dev" } +bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.9.0-dev" } +bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" } +bevy_math = { path = "../bevy_math", version = "0.9.0-dev" } +bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = [ + "bevy", +] } +bevy_render = { path = "../bevy_render", version = "0.9.0-dev" } +bevy_sprite = { path = "../bevy_sprite", version = "0.9.0-dev" } +bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" } +bevy_ui = { path = "../bevy_ui", version = "0.9.0-dev" } +bevy_window = { path = "../bevy_window", version = "0.9.0-dev" } + +[dev-dependencies] +bevy_input = { path = "../bevy_input", version = "0.9.0-dev" } diff --git a/crates/bevy_color_blindness/src/color_blindness.wgsl b/crates/bevy_color_blindness/src/color_blindness.wgsl new file mode 100644 index 0000000000000..73c3074b4b6cf --- /dev/null +++ b/crates/bevy_color_blindness/src/color_blindness.wgsl @@ -0,0 +1,34 @@ +#import bevy_pbr::mesh_view_bindings + +struct Percentages { + red: vec3, + green: vec3, + blue: vec3, +}; + +@group(1) @binding(0) +var texture: texture_2d; + +@group(1) @binding(1) +var our_sampler: sampler; + +@group(1) @binding(2) +var p: Percentages; + +@fragment +fn fragment( + @builtin(position) position: vec4, + #import bevy_sprite::mesh2d_vertex_output +) -> @location(0) vec4 { + // Get screen position with coordinates from 0 to 1 + let uv = position.xy / vec2(view.width, view.height); + + var c = textureSample(texture, our_sampler, uv); + + return vec4( + c.r * p.red.x + c.g * p.red.y + c.b * p.red.z, + c.r * p.green.x + c.g * p.green.y + c.b * p.green.z, + c.r * p.blue.x + c.g * p.blue.y + c.b * p.blue.z, + c.a + ); +} diff --git a/crates/bevy_color_blindness/src/lib.rs b/crates/bevy_color_blindness/src/lib.rs new file mode 100644 index 0000000000000..42b6c27c9576a --- /dev/null +++ b/crates/bevy_color_blindness/src/lib.rs @@ -0,0 +1,419 @@ +//! Plugin to simulate and preview different types of +//! color blindness. + +use bevy_app::{App, Plugin}; +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, Changed}, + system::{Commands, Query, Res, ResMut}, +}; +use bevy_math::{Vec2, Vec3}; +use bevy_reflect::TypeUuid; +use bevy_render::{ + camera::{Camera, RenderTarget}, + mesh::{shape, Mesh}, + prelude::Image, + render_resource::{ + AsBindGroup, Extent3d, Shader, ShaderRef, ShaderType, TextureDescriptor, TextureDimension, + TextureFormat, TextureUsages, + }, + texture::BevyDefault, + view::RenderLayers, +}; +use bevy_sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle}; +use bevy_transform::prelude::Transform; +use bevy_ui::entity::UiCameraConfig; +use bevy_window::Windows; + +/// Plugin to simulate and preview different types of +/// color blindness. +/// +/// This lets you ensure that your game is accessible to all players by testing how it +/// will be seen under different conditions. While this is important, +/// please also consider not relying on color alone to convey important information to your players. +/// A common option is to add identifying symbols, like in the game +/// [Hue](https://gameaccessibilityguidelines.com/hue-colorblind-mode/). +/// +/// Based on [Alan Zucconi's post](https://www.alanzucconi.com/2015/12/16/color-blindness/). +/// Supports: Normal, Protanopia, Protanomaly, Deuteranopia, Deuteranomaly, +/// Tritanopia, Tritanomaly, Achromatopsia, and Achromatomaly. +/// +/// First, add the [`ColorBlindnessPlugin`] to your app, and add [`ColorBlindnessCamera`] to +/// your main camera. +/// +/// ```rust,no_run +/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins}; +/// # use bevy_color_blindness::*; +/// # use bevy_core_pipeline::core_3d::Camera3dBundle; +/// # use bevy_ecs::system::Commands; +/// # use bevy_math::prelude::*; +/// # use bevy_transform::prelude::*; +/// fn main() { +/// App::new() +/// .add_plugins(DefaultPlugins) +/// // add the plugin +/// .add_plugin(ColorBlindnessPlugin) +/// .add_startup_system(setup) +/// .run(); +/// } +/// +/// fn setup(mut commands: Commands) { +/// // set up your scene... +/// +/// // 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() +/// }) +/// .insert(ColorBlindnessCamera { +/// mode: ColorBlindnessMode::Deuteranopia, +/// enabled: true, +/// }); +/// } +/// ``` +/// +/// # Important note +/// +/// This plugin only simulates how color blind players will see your game. +/// It does not correct for color blindness to make your game more accessible. +/// This plugin should only be used during development, and removed on final builds. +pub struct ColorBlindnessPlugin; +impl Plugin for ColorBlindnessPlugin { + fn build(&self, app: &mut App) { + load_internal_asset!( + app, + COLOR_BLINDNESS_SHADER_HANDLE, + "color_blindness.wgsl", + Shader::from_wgsl + ); + + app.add_plugin(Material2dPlugin::::default()) + .add_system(setup_new_color_blindness_cameras) + .add_system(update_percentages); + } +} + +/// handle to the color blindness simulation shader +const COLOR_BLINDNESS_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3937837360667146578); + +/// The different modes of color blindness simulation supported. +#[derive(Clone, Default, Debug)] +pub enum ColorBlindnessMode { + /// Normal full color vision + #[default] + Normal, + // Descriptions of the different types of color blindness are sourced from: + // https://www.nei.nih.gov/learn-about-eye-health/eye-conditions-and-diseases/color-blindness/types-color-blindness + /// Inability to differentiate between green and red. + Protanopia, + /// Condition where red looks more green. + Protanomaly, + /// Inability to differentiate between green and red. + Deuteranopia, + /// Condition where green looks more red. + Deuteranomaly, + /// Inability to differentiate between blue and green, purple and red, and yellow and pink. + Tritanopia, + /// Difficulty differentiating between blue and green, and between yellow and red + Tritanomaly, + /// Absence of color discrimination. + Achromatopsia, + /// All color cones have some form of deficiency. + Achromatomaly, +} + +/// Indicates how to mix the RGB channels to obtain output colors. +/// +/// Normal vision corresponds to the following: +/// ```rust +/// # use bevy_math::prelude::*; +/// # use bevy_color_blindness::*; +/// # fn _none() -> ColorBlindnessPercentages { +/// ColorBlindnessPercentages { +/// // red channel output is 100% red, 0% green, 0% blue +/// red: Vec3::X, +/// // green channel is 0% red, 100% green, 0% blue +/// green: Vec3::Y, +/// // blue channel is 0% red, 0% green, 100% blue +/// blue: Vec3::Z +/// } +/// # } +/// ``` +#[derive(ShaderType, Clone, Debug)] +pub struct ColorBlindnessPercentages { + /// Percentages of red, green, and blue to mix on the red channel. + pub red: Vec3, + /// Percentages of red, green, and blue to mix on the green channel. + pub green: Vec3, + /// Percentages of red, green, and blue to mix on the blue channel. + pub blue: Vec3, +} + +impl ColorBlindnessPercentages { + /// Creates a new `ColorBlindnessPercentages` + fn new(red: Vec3, green: Vec3, blue: Vec3) -> Self { + Self { red, green, blue } + } +} + +impl ColorBlindnessMode { + /// Returns the percentages of colors to mix corresponding to each type of color blindness. + /// + /// [Source](https://web.archive.org/web/20081014161121/http://www.colorjack.com/labs/colormatrix/) + pub fn percentages(&self) -> ColorBlindnessPercentages { + // table from https://www.alanzucconi.com/2015/12/16/color-blindness/ + // https://web.archive.org/web/20081014161121/http://www.colorjack.com/labs/colormatrix/ + + match self { + ColorBlindnessMode::Normal => ColorBlindnessPercentages::new(Vec3::X, Vec3::Y, Vec3::Z), + ColorBlindnessMode::Protanopia => ColorBlindnessPercentages::new( + [0.56667, 0.43333, 0.0].into(), + [0.55833, 0.44167, 0.0].into(), + [0.0, 0.24167, 0.75833].into(), + ), + ColorBlindnessMode::Protanomaly => ColorBlindnessPercentages::new( + [0.81667, 0.18333, 0.0].into(), + [0.33333, 0.66667, 0.0].into(), + [0.0, 0.125, 0.875].into(), + ), + ColorBlindnessMode::Deuteranopia => ColorBlindnessPercentages::new( + [0.625, 0.375, 0.0].into(), + [0.70, 0.30, 0.0].into(), + [0.0, 0.30, 0.70].into(), + ), + ColorBlindnessMode::Deuteranomaly => ColorBlindnessPercentages::new( + [0.80, 0.20, 0.0].into(), + [0.25833, 0.74167, 0.0].into(), + [0.0, 0.14167, 0.85833].into(), + ), + ColorBlindnessMode::Tritanopia => ColorBlindnessPercentages::new( + [0.95, 0.5, 0.0].into(), + [0.0, 0.43333, 0.56667].into(), + [0.0, 0.475, 0.525].into(), + ), + ColorBlindnessMode::Tritanomaly => ColorBlindnessPercentages::new( + [0.96667, 0.3333, 0.0].into(), + [0.0, 0.73333, 0.26667].into(), + [0.0, 0.18333, 0.81667].into(), + ), + ColorBlindnessMode::Achromatopsia => ColorBlindnessPercentages::new( + [0.299, 0.587, 0.114].into(), + [0.299, 0.587, 0.114].into(), + [0.299, 0.587, 0.114].into(), + ), + ColorBlindnessMode::Achromatomaly => ColorBlindnessPercentages::new( + [0.618, 0.32, 0.62].into(), + [0.163, 0.775, 0.62].into(), + [0.163, 0.320, 0.516].into(), + ), + } + } + + /// Changes `self` to the next `ColorBlindnessMode`. + /// + /// Useful for writing something like the following: + /// + /// ```rust + /// # use bevy_app::prelude::*; + /// # use bevy_color_blindness::*; + /// # use bevy_ecs::prelude::*; + /// # use bevy_input::prelude::*; + /// fn change_mode(input: Res>, mut cameras: Query<&mut ColorBlindnessCamera>) { + /// for mut camera in &mut cameras { + /// // cycle through the modes by pressing N + /// if input.just_pressed(KeyCode::N) { + /// camera.mode.cycle(); + /// println!("Changed to {:?}", camera.mode); + /// } + /// + /// camera.enabled = input.pressed(KeyCode::Space); + /// } + /// } + /// ``` + pub fn cycle(&mut self) { + *self = match self { + ColorBlindnessMode::Normal => ColorBlindnessMode::Protanopia, + ColorBlindnessMode::Protanopia => ColorBlindnessMode::Protanomaly, + ColorBlindnessMode::Protanomaly => ColorBlindnessMode::Deuteranopia, + ColorBlindnessMode::Deuteranopia => ColorBlindnessMode::Deuteranomaly, + ColorBlindnessMode::Deuteranomaly => ColorBlindnessMode::Tritanopia, + ColorBlindnessMode::Tritanopia => ColorBlindnessMode::Tritanomaly, + ColorBlindnessMode::Tritanomaly => ColorBlindnessMode::Achromatopsia, + ColorBlindnessMode::Achromatopsia => ColorBlindnessMode::Achromatomaly, + ColorBlindnessMode::Achromatomaly => ColorBlindnessMode::Normal, + }; + } +} + +/// Post processing material that applies color blindness simulation to `image` +#[derive(AsBindGroup, TypeUuid, Clone)] +#[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"] +struct ColorBlindnessMaterial { + /// In this example, this image will be the result of the main camera. + #[texture(0)] + #[sampler(1)] + source_image: Handle, + + #[uniform(2)] + percentages: ColorBlindnessPercentages, +} + +impl Material2d for ColorBlindnessMaterial { + fn fragment_shader() -> ShaderRef { + ShaderRef::Handle(COLOR_BLINDNESS_SHADER_HANDLE.typed()) + } +} + +/// Component to identify your main camera +/// +/// Adding this component to a camera will set up the post-processing pipeline +/// which simulates color blindness. This is done by changing the render target +/// to be an image, and then using another camera to render that image. +/// +/// Cameras with `ColorBlindnessCamera` will have [`UiCameraConfig`] inserted with +/// `show_ui` set to `false`. This is to ensure that UI elements are not rendered twice. +/// In most cases, you will want to render UI using the final post-processing camera. +/// If for some reason this behavior is not desired, please open an issue. +/// +/// [`UiCameraConfig`]: bevy_ui::entity::UiCameraConfig +#[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 values in `ColorBlindnessCamera` change +fn update_percentages( + cameras: Query< + (&Handle, &ColorBlindnessCamera), + Changed, + >, + mut materials: ResMut>, +) { + for (handle, camera) in &cameras { + let mut mat = materials.get_mut(handle).unwrap(); + + let mode = if camera.enabled { + &camera.mode + } else { + &ColorBlindnessMode::Normal + }; + + mat.percentages = mode.percentages(); + } +} + +/// sets up post processing for cameras that have had `ColorBlindnessCamera` added +fn setup_new_color_blindness_cameras( + mut commands: Commands, + windows: Res, + mut meshes: ResMut>, + mut post_processing_materials: ResMut>, + mut images: ResMut>, + mut cameras: Query<(Entity, &mut Camera, &ColorBlindnessCamera), Added>, +) { + for (entity, mut camera, color_blindness_camera) in &mut cameras { + let original_target = camera.target.clone(); + + // 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 + } + }; + + // 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); + + 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); + + 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(), + }); + + 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 }); + + 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), + ..Default::default() + }, + ..Default::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, + // set this new camera to render to where the other camera was rendering + target: original_target, + ..Default::default() + }, + ..Camera2dBundle::default() + }) + .insert(post_processing_pass_layer); + } +} diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index d4be55fc72878..ae9086b853e56 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -85,6 +85,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.9.0-dev" } bevy_animation = { path = "../bevy_animation", optional = true, version = "0.9.0-dev" } bevy_asset = { path = "../bevy_asset", optional = true, version = "0.9.0-dev" } bevy_audio = { path = "../bevy_audio", optional = true, version = "0.9.0-dev" } +bevy_color_blindness = { path = "../bevy_color_blindness", optional = true, version = "0.9.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", optional = true, version = "0.9.0-dev" } bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.9.0-dev" } bevy_pbr = { path = "../bevy_pbr", optional = true, version = "0.9.0-dev" } diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index a9158f0bafacb..eedb45a9b3579 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -109,6 +109,14 @@ pub mod audio { pub use bevy_audio::*; } +#[cfg(feature = "bevy_color_blindness")] +pub mod color_blindness { + //! Plugin to simulate and preview different types of + //! color blindness. + + pub use bevy_color_blindness::*; +} + #[cfg(feature = "bevy_core_pipeline")] pub mod core_pipeline { //! Core render pipeline. diff --git a/crates/bevy_internal/src/prelude.rs b/crates/bevy_internal/src/prelude.rs index cc3614544d0bb..fd053d8bf78ab 100644 --- a/crates/bevy_internal/src/prelude.rs +++ b/crates/bevy_internal/src/prelude.rs @@ -19,6 +19,10 @@ pub use crate::audio::prelude::*; #[cfg(feature = "bevy_animation")] pub use crate::animation::prelude::*; +#[doc(hidden)] +#[cfg(feature = "bevy_color_blindness")] +pub use crate::color_blindness::*; + #[doc(hidden)] #[cfg(feature = "bevy_core_pipeline")] pub use crate::core_pipeline::prelude::*; diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 8d0db4604380e..f4ec6abd387b9 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -7,6 +7,7 @@ |animation|Animation support and glTF animation loading.| |bevy_asset|Provides asset functionality for Bevy Engine.| |bevy_audio|Audio support. Support for all audio formats depends on this.| +|bevy_color_blindness|Provides color blindness simulation features.| |bevy_gilrs|Adds gamepad support.| |bevy_gltf|[glTF](https://www.khronos.org/gltf/) support.| |bevy_scene|Provides scene functionality for Bevy Engine.| diff --git a/examples/README.md b/examples/README.md index b23260cdcb5e0..7ecad68559f47 100644 --- a/examples/README.md +++ b/examples/README.md @@ -39,6 +39,7 @@ git checkout v0.4.0 - [Cross-Platform Examples](#cross-platform-examples) - [2D Rendering](#2d-rendering) - [3D Rendering](#3d-rendering) + - [Accessibility](#accessibility) - [Animation](#animation) - [Application](#application) - [Assets](#assets) @@ -127,6 +128,12 @@ Example | Description [Vertex Colors](../examples/3d/vertex_colors.rs) | Shows the use of vertex colors [Wireframe](../examples/3d/wireframe.rs) | Showcases wireframe rendering +## Accessibility + +Example | Description +--- | --- +[Color Blindness Simulation](../examples/accessibility/color_blindness_simulation.rs) | Modify your app's rendering output to simulate color blindness + ## Animation Example | Description diff --git a/examples/accessibility/color_blindness_simulation.rs b/examples/accessibility/color_blindness_simulation.rs new file mode 100644 index 0000000000000..190ac3f8b013c --- /dev/null +++ b/examples/accessibility/color_blindness_simulation.rs @@ -0,0 +1,89 @@ +//! Small demo of how to use color blindness simulation +//! Shows a small scene, with four different cubes +//! +//! Holding the Space key enables the simulation +//! Pressing N cycles through the modes + +use bevy::{prelude::*, window::close_on_esc}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + // add the plugin + .add_plugin(ColorBlindnessPlugin) + .add_startup_system(setup) + .add_system(close_on_esc) + .add_system(change_mode) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // create a small world + commands.spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..default() + }); + let cube = meshes.add(Mesh::from(shape::Cube { size: 0.5 })); + commands.spawn_bundle(PbrBundle { + mesh: cube.clone(), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(0.0, 0.5, 0.0), + ..default() + }); + commands.spawn_bundle(PbrBundle { + mesh: cube.clone(), + material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()), + transform: Transform::from_xyz(2.0, 0.5, 0.0), + ..default() + }); + commands.spawn_bundle(PbrBundle { + mesh: cube.clone(), + material: materials.add(Color::rgb(0.0, 1.0, 0.0).into()), + transform: Transform::from_xyz(3.0, 0.5, 0.0), + ..default() + }); + commands.spawn_bundle(PbrBundle { + mesh: cube, + material: materials.add(Color::rgb(0.0, 0.0, 1.0).into()), + transform: Transform::from_xyz(4.0, 0.5, 0.0), + ..default() + }); + commands.spawn_bundle(PointLightBundle { + point_light: PointLight { + intensity: 1500.0, + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(4.0, 8.0, 4.0), + ..default() + }); + + // create the camera + commands + .spawn_bundle(Camera3dBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }) + .insert(ColorBlindnessCamera { + mode: ColorBlindnessMode::Deuteranopia, + enabled: false, + }); +} + +fn change_mode(input: Res>, mut cameras: Query<&mut ColorBlindnessCamera>) { + for mut camera in &mut cameras { + // cycle through the modes by pressing N + if input.just_pressed(KeyCode::N) { + camera.mode.cycle(); + println!("Changed to {:?}", camera.mode); + } + + camera.enabled = input.pressed(KeyCode::Space); + } +} diff --git a/tools/publish.sh b/tools/publish.sh index d5e880c2e7791..9ff7f22493618 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -35,6 +35,7 @@ crates=( bevy_sprite bevy_text bevy_ui + bevy_color_blindness bevy_winit bevy_internal bevy_dylib