2
2
//! color blindness.
3
3
4
4
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 } ;
6
6
use bevy_core_pipeline:: core_2d:: Camera2dBundle ;
7
7
use bevy_ecs:: {
8
8
component:: Component ,
9
9
entity:: Entity ,
10
- query:: Added ,
10
+ query:: { Added , Changed } ,
11
11
system:: { Commands , Query , Res , ResMut } ,
12
12
} ;
13
13
use bevy_math:: { Vec2 , Vec3 } ;
@@ -44,9 +44,6 @@ use bevy_window::Windows;
44
44
/// First, add the [`ColorBlindnessPlugin`] to your app, and add [`ColorBlindnessCamera`] to
45
45
/// your main camera.
46
46
///
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
- ///
50
47
/// ```rust,no_run
51
48
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins};
52
49
/// # use bevy_color_blindness::*;
@@ -57,10 +54,6 @@ use bevy_window::Windows;
57
54
/// fn main() {
58
55
/// App::new()
59
56
/// .add_plugins(DefaultPlugins)
60
- /// .insert_resource(ColorBlindnessParams {
61
- /// mode: ColorBlindnessMode::Deuteranomaly,
62
- /// enable: true,
63
- /// })
64
57
/// // add the plugin
65
58
/// .add_plugin(ColorBlindnessPlugin)
66
59
/// .add_startup_system(setup)
@@ -73,11 +66,13 @@ use bevy_window::Windows;
73
66
/// // create the camera
74
67
/// commands
75
68
/// .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()
78
71
/// })
79
- /// // IMPORTANT: add this component to your main camera
80
- /// .insert(ColorBlindnessCamera);
72
+ /// .insert(ColorBlindnessCamera {
73
+ /// mode: ColorBlindnessMode::Deuteranopia,
74
+ /// enabled: true,
75
+ /// });
81
76
/// }
82
77
/// ```
83
78
///
@@ -89,19 +84,15 @@ use bevy_window::Windows;
89
84
pub struct ColorBlindnessPlugin ;
90
85
impl Plugin for ColorBlindnessPlugin {
91
86
fn build ( & self , app : & mut App ) {
92
- let world = & mut app. world ;
93
- world. get_resource_or_insert_with ( ColorBlindnessParams :: default) ;
94
-
95
87
load_internal_asset ! (
96
88
app,
97
89
COLOR_BLINDNESS_SHADER_HANDLE ,
98
90
"color_blindness.wgsl" ,
99
91
Shader :: from_wgsl
100
92
) ;
101
93
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)
105
96
. add_system ( update_percentages) ;
106
97
}
107
98
}
@@ -110,41 +101,6 @@ impl Plugin for ColorBlindnessPlugin {
110
101
const COLOR_BLINDNESS_SHADER_HANDLE : HandleUntyped =
111
102
HandleUntyped :: weak_from_u64 ( Shader :: TYPE_UUID , 3937837360667146578 ) ;
112
103
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
-
148
104
/// The different modes of color blindness simulation supported.
149
105
#[ derive( Clone , Default , Debug ) ]
150
106
pub enum ColorBlindnessMode {
@@ -267,11 +223,16 @@ impl ColorBlindnessMode {
267
223
/// # use bevy_color_blindness::*;
268
224
/// # use bevy_ecs::prelude::*;
269
225
/// # 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
271
229
/// 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);
274
232
/// }
233
+ ///
234
+ /// camera.enabled = input.pressed(KeyCode::Space);
235
+ /// }
275
236
/// }
276
237
/// ```
277
238
pub fn cycle ( & mut self ) {
@@ -292,7 +253,7 @@ impl ColorBlindnessMode {
292
253
/// Post processing material that applies color blindness simulation to `image`
293
254
#[ derive( AsBindGroup , TypeUuid , Clone ) ]
294
255
#[ uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5" ]
295
- struct PostProcessingMaterial {
256
+ struct ColorBlindnessMaterial {
296
257
/// In this example, this image will be the result of the main camera.
297
258
#[ texture( 0 ) ]
298
259
#[ sampler( 1 ) ]
@@ -302,7 +263,7 @@ struct PostProcessingMaterial {
302
263
percentages : ColorBlindnessPercentages ,
303
264
}
304
265
305
- impl Material2d for PostProcessingMaterial {
266
+ impl Material2d for ColorBlindnessMaterial {
306
267
fn fragment_shader ( ) -> ShaderRef {
307
268
ShaderRef :: Handle ( COLOR_BLINDNESS_SHADER_HANDLE . typed ( ) )
308
269
}
@@ -320,34 +281,31 @@ impl Material2d for PostProcessingMaterial {
320
281
/// If for some reason this behavior is not desired, please open an issue.
321
282
///
322
283
/// [`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 ,
338
294
}
339
295
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
341
297
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 > > ,
345
303
) {
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 ( ) ;
348
306
349
- let mode = if params . enable {
350
- & params . mode
307
+ let mode = if camera . enabled {
308
+ & camera . mode
351
309
} else {
352
310
& ColorBlindnessMode :: Normal
353
311
} ;
@@ -356,96 +314,102 @@ fn update_percentages(
356
314
}
357
315
}
358
316
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 (
370
319
mut commands : Commands ,
371
- mut windows : ResMut < Windows > ,
320
+ windows : Res < Windows > ,
372
321
mut meshes : ResMut < Assets < Mesh > > ,
373
- mut post_processing_materials : ResMut < Assets < PostProcessingMaterial > > ,
322
+ mut post_processing_materials : ResMut < Assets < ColorBlindnessMaterial > > ,
374
323
mut images : ResMut < Assets < Image > > ,
375
- asset_server : Res < AssetServer > ,
376
- params : Res < ColorBlindnessParams > ,
324
+ mut cameras : Query < ( Entity , & mut Camera , & ColorBlindnessCamera ) , Added < ColorBlindnessCamera > > ,
377
325
) {
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
+ } ;
379
344
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
+ } ;
386
360
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) ;
402
363
403
- // fill image.data with zeroes
404
- image. resize ( size) ;
364
+ let image_handle = images. add ( image) ;
405
365
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 ) ;
407
369
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
+ ) ) ) ) ;
410
374
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
+ } ) ;
415
380
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 } ) ;
421
387
422
- commands. insert_resource ( InternalResource {
423
- image : image_handle,
424
- post : material_handle. clone ( ) ,
425
- } ) ;
388
+ camera. target = RenderTarget :: Image ( image_handle) ;
426
389
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
+ } ,
434
399
..Default :: default ( )
435
- } ,
436
- ..Default :: default ( )
437
- } )
438
- . insert ( post_processing_pass_layer) ;
400
+ } )
401
+ . insert ( post_processing_pass_layer) ;
439
402
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
+ }
451
415
}
0 commit comments