diff --git a/Cargo.toml b/Cargo.toml index 817f1117de33057fb3176d30a608ad546c0fe804..6413a505c0f78256c7d138a02170f32278cad381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = ["kayak_ui_macros", "kayak_font"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_winit", "bevy_core_pipeline"] } +bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_winit", "bevy_core_pipeline", "bevy_ui"] } bytemuck = "1.12" dashmap = "5.4" kayak_font = { path = "./kayak_font", version = "0.2" } diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs index ce16c362322ab52cd230832fd28d251f4409b350..7750894247a12d778a4bd99787adb15e9e6691c7 100644 --- a/examples/bevy_scene.rs +++ b/examples/bevy_scene.rs @@ -116,7 +116,6 @@ fn on_color_change( /// A system that sets up the world fn world_setup(mut commands: Commands, active_color: Res<ActiveColor>) { - commands.spawn((Camera2dBundle::default(), WorldCamera)); commands .spawn(SpriteBundle { sprite: Sprite { @@ -172,9 +171,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + // The UI Camera and the world camera are the same. + // CameraUIKayak is used to tell kayak which camera should render UI. + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak, WorldCamera)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let handle_change_color = OnEvent::new( @@ -255,7 +260,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn((UICameraBundle::new(widget_context), GameUI)); + commands.spawn((widget_context, EventDispatcher::default(), GameUI)); } fn main() { diff --git a/examples/clipping.rs b/examples/clipping.rs index 9eeb05889354fa863a2d7b63766892ccd9a29d31..36203347a6d4db0e83e6327e7f172fa7f84489c9 100644 --- a/examples/clipping.rs +++ b/examples/clipping.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("lato-light.kttf")); let image = asset_server.load("panel.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -56,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs index a3dd07ff395b6f65c4d50fa86f71efc8fd2b5913..20d9c341212cfaac5062d668f750e267d59cd7b0 100644 --- a/examples/conditional_widget.rs +++ b/examples/conditional_widget.rs @@ -109,13 +109,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("lato-light.kttf")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidget, MyWidgetState>(); @@ -130,7 +130,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/context.rs b/examples/context.rs index 0b8cac2a6c37a47847da5816c348b9e4d4ae47a4..9c4603c16fe7908ba60fd7b823e4674537489946 100644 --- a/examples/context.rs +++ b/examples/context.rs @@ -325,13 +325,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<ThemeDemo, EmptyState>(); widget_context.add_widget_data::<ThemeButton, EmptyState>(); @@ -377,7 +377,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/demo.rs b/examples/demo.rs index 0ba501e2ec1b438fcfab777ade91007f1ae7384e..0d562a49bf41bc761f641ea04122782a6d4da336 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -21,7 +21,11 @@ fn my_widget_1_render( impl Widget for MyWidget {} fn startup(mut commands: Commands) { - let mut context = KayakRootContext::new(); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + + let mut context = KayakRootContext::new(camera_entity); context.add_plugin(KayakWidgetsContextPlugin); context.add_widget_system( MyWidget::default().get_name(), @@ -47,7 +51,7 @@ fn startup(mut commands: Commands) { }); context.add_widget(None, app_entity); - commands.spawn(UICameraBundle::new(context)); + commands.spawn((context, EventDispatcher::default())); } // Note this example shows prop changing not state changing which is quite different. diff --git a/examples/hello_world.rs b/examples/hello_world.rs index caf0660553c22cf932aa72ebfb63fcb1eafefffc..188504eb9d5bc0316036b79bbd7c15951b9175c8 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,14 +1,23 @@ use bevy::prelude::*; -use kayak_ui::prelude::{widgets::*, *}; +use kayak_ui::{ + prelude::{widgets::*, *}, + CameraUIKayak, +}; fn startup( mut commands: Commands, mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn(Camera2dBundle::default()) + .insert(CameraUIKayak) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); + // font_mapping.force_subpixel(&asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -23,7 +32,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs index 1b472f8eb962ed39ece9044244278655b1d0005b..7216668abb4043f7a24c896ab08fc824fb5149b2 100644 --- a/examples/hello_world_no_macro.rs +++ b/examples/hello_world_no_macro.rs @@ -5,8 +5,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn(Camera2dBundle::default()) + .insert(CameraUIKayak) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let app_entity = widget_context.spawn_widget(&mut commands, None); @@ -39,7 +44,7 @@ fn startup( // Add widget context as resource. - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { App::new() diff --git a/examples/image.rs b/examples/image.rs index ae984f6ebe19cee77666e543b6127fd89889414d..40c7edba142927b072af1cfeaf7e3963e994ba5b 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image = asset_server.load("generic-rpg-vendor.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -30,7 +34,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/layout.rs b/examples/layout.rs index 6fdc489262656d4c4e1dc796f472809824ce5572..f2a0e842befff80835b0279686ac37c149352099 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -6,13 +6,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -130,7 +130,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/main_menu.rs b/examples/main_menu.rs index d4dcb75124f27c8142909da7de6e7a9b57e2fba7..4f2d649fc3baa96b580a3b332b0b591368ae1996 100644 --- a/examples/main_menu.rs +++ b/examples/main_menu.rs @@ -123,9 +123,13 @@ fn startup( asset_server: Res<AssetServer>, mut preload_resource: ResMut<PreloadResource>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<MenuButton, ButtonState>(); widget_context.add_widget_system( @@ -218,7 +222,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/multi_context.rs b/examples/multi_context.rs deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/examples/multi_context.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs index 1aeeadaa5adaf3abacb88cfa9ebdf8ea3f856847..ea211a725baacca64ccb2df3f49c97496d85a64a 100644 --- a/examples/nine_patch.rs +++ b/examples/nine_patch.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image = asset_server.load("panel.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -51,7 +55,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/quads.rs b/examples/quads.rs index 431876e8ae9704f3e8814237c85a07ab0bc1d56b..771b6a211c0e59a55c5e2d83180a5e40f54bad0a 100644 --- a/examples/quads.rs +++ b/examples/quads.rs @@ -94,13 +94,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_system( MyQuad::default().get_name(), @@ -134,7 +134,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/render_target.rs b/examples/render_target.rs index 284771afa3e12d28adb673508e50bcad59a01d2c..e6ee73f9e5a4af1955cd62664ed9c68ddfc198c6 100644 --- a/examples/render_target.rs +++ b/examples/render_target.rs @@ -57,7 +57,22 @@ fn startup( let image_handle = images.add(image); - let mut widget_context = KayakRootContext::new(); + let camera_entity = commands + .spawn(Camera2dBundle { + camera: Camera { + priority: -1, + target: RenderTarget::Image(image_handle.clone()), + ..Camera::default() + }, + camera_2d: Camera2d { + clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default, + }, + ..Default::default() + }) + .insert(CameraUIKayak) + .id(); + + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -82,18 +97,7 @@ fn startup( /> </KayakAppBundle> }; - - commands.spawn(UICameraBundle { - camera: Camera { - order: -1, - target: RenderTarget::Image(image_handle.clone()), - ..Camera::default() - }, - camera_ui: CameraUIKayak { - clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default, - }, - ..UICameraBundle::new(widget_context) - }); + commands.spawn((widget_context, EventDispatcher::default())); // Setup 3D scene // Light @@ -128,14 +132,17 @@ fn startup( .insert(MainPassCube); // The main pass camera. - commands.spawn(Camera3dBundle { - transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) - .looking_at(Vec3::default(), Vec3::Y), - ..default() - }); + let camera_entity = commands + .spawn(Camera3dBundle { + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) + .looking_at(Vec3::default(), Vec3::Y), + ..default() + }) + .insert(CameraUIKayak) + .id(); // Spawn another UI in 2D space! - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -149,7 +156,7 @@ fn startup( /> </KayakAppBundle> }; - commands.spawn((UICameraBundle::new(widget_context), MainUI)); + commands.spawn((widget_context, EventDispatcher::default(), MainUI)); } /// Rotates the outer cube (main pass) diff --git a/examples/scrolling.rs b/examples/scrolling.rs index cac9c08d53e7aae9a0a4b3ff770dea080a243241..d77fa08a432ae3675ba93899b6bd621863c8c9b1 100644 --- a/examples/scrolling.rs +++ b/examples/scrolling.rs @@ -6,6 +6,10 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + let font_asset = asset_server.load("roboto.kayak_font"); font_mapping.set_default(font_asset.clone()); @@ -14,11 +18,7 @@ fn startup( // will be ignored if this setting is used. font_mapping.force_subpixel(&font_asset); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); - - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -60,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/simple_state.rs b/examples/simple_state.rs index 4b1b64cbf3bb0b107859bcac0acd25ac7b01e094..82e2c28ab7b8c922e73582a5045b804527e0f494 100644 --- a/examples/simple_state.rs +++ b/examples/simple_state.rs @@ -85,13 +85,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("lato-light.kttf")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<CurrentCount, CurrentCountState>(); @@ -129,7 +129,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs index 2539aecd9720908d149348c467003415284a2210..9ecb4b53a67c422678a65e0c1255a5cd25c1d3b8 100644 --- a/examples/tabs/tabs.rs +++ b/examples/tabs/tabs.rs @@ -15,13 +15,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<Tab, EmptyState>(); widget_context.add_widget_data::<TabContextProvider, EmptyState>(); @@ -87,7 +87,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/test_no_startup.rs b/examples/test_no_startup.rs index 4f59b2643d45e9415a25a9f913ce8a1795931ce4..503a4ed738ae95249df095724729591ecabeb240 100644 --- a/examples/test_no_startup.rs +++ b/examples/test_no_startup.rs @@ -17,9 +17,13 @@ fn second_sys( asset_server: Res<AssetServer>, mut font_mapping: ResMut<FontMapping>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -34,7 +38,7 @@ fn second_sys( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/text.rs b/examples/text.rs index fa49287c6f47b86904eb8466d09b5a13c6af626d..496023d6d5d3fc8ea978f5c25a687ae311360fe3 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -73,13 +73,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); @@ -92,7 +92,7 @@ fn startup( <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<MyResource>) { diff --git a/examples/text_box.rs b/examples/text_box.rs index 72b273156600b77fc3f2ef3436bca5bb6a02d0b0..edb8e9f48deb69bf07f60bd2991ca9fd53a29ff2 100644 --- a/examples/text_box.rs +++ b/examples/text_box.rs @@ -88,13 +88,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>(); @@ -120,7 +120,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs index d4e311ae9fa9825ef10fe6e3659aefc5d7239ff9..69f7d7d99ae1009bb6e87bed46684f40f0612fde 100644 --- a/examples/texture_atlas.rs +++ b/examples/texture_atlas.rs @@ -6,6 +6,10 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image_handle = asset_server.load("texture_atlas.png"); @@ -28,7 +32,7 @@ fn startup( //The flower is in the 6(-1) row and 15 collumn let flower_index = columns * 5 + 15; - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -68,7 +72,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs index 2dc0a11e451bd304c63ddb3c2c86b1ea1c30f9e1..66b12c2b7d34fd2acc04cfdb90424178519f2af1 100644 --- a/examples/todo/todo.rs +++ b/examples/todo/todo.rs @@ -53,13 +53,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TodoItemsProps, EmptyState>(); widget_context.add_widget_data::<TodoInputProps, EmptyState>(); @@ -99,7 +99,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/vec.rs b/examples/vec.rs index fa362a71ca9d2e76f780a8cbab998a538b3c030f..459e579371b2246ae5614c8253f969aa401ca12f 100644 --- a/examples/vec.rs +++ b/examples/vec.rs @@ -57,9 +57,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); @@ -72,7 +76,7 @@ fn startup( <KayakAppBundle><MyWidgetBundle /></KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 9751b184744a1101425cec96208ba46d11e81ffe..f260d649c4ff1ded0f0a3befe5b44e5f7697b1b2 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -1,23 +1,24 @@ -use bevy::{ - prelude::{CoreSet, IntoSystemConfig, Plugin}, - render::{camera::CameraProjectionPlugin, extract_component::ExtractComponentPlugin}, -}; - -mod camera; -mod ortho; - -pub use camera::{CameraUIKayak, UICameraBundle}; -pub(crate) use ortho::UIOrthographicProjection; - -pub struct KayakUICameraPlugin; - -impl Plugin for KayakUICameraPlugin { - fn build(&self, app: &mut bevy::prelude::App) { - app.add_system( - bevy::render::camera::camera_system::<UIOrthographicProjection> - .in_base_set(CoreSet::PostUpdate), - ) - .add_plugin(CameraProjectionPlugin::<UIOrthographicProjection>::default()) - .add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default()); - } -} +use bevy::{ + ecs::query::QueryItem, + prelude::*, + render::extract_component::{ExtractComponent, ExtractComponentPlugin}, +}; + +#[derive(Component, Default, Debug, Clone, Copy)] +pub struct CameraUIKayak; +impl ExtractComponent for CameraUIKayak { + type Query = &'static Self; + type Filter = With<Camera>; + type Out = CameraUIKayak; + + fn extract_component(item: QueryItem<Self::Query>) -> Option<Self::Out> { + Some(item.clone()) + } +} + +pub struct KayakUICameraPlugin; +impl Plugin for KayakUICameraPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default()); + } +} diff --git a/src/context.rs b/src/context.rs index 16229526648ee2ab90a753c089d9fc24d42bcf66..3b19a7f35783feee29afd7295e6e9721c6c36403 100644 --- a/src/context.rs +++ b/src/context.rs @@ -65,7 +65,7 @@ type WidgetSystems = HashMap< /// }).id(); /// // Stores the kayak app widget in the widget context's tree. /// widget_context.add_widget(None, app_entity); -/// commands.spawn(UICameraBundle::new(widget_context)); +/// commands.spawn((widget_context, EventDispatcher::default())); /// } /// /// fn main() { @@ -91,17 +91,18 @@ pub struct KayakRootContext { pub(crate) order_tree: Arc<RwLock<Tree>>, pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>, pub(crate) uninitilized_systems: HashSet<String>, + pub camera_entity: Entity, } impl Default for KayakRootContext { fn default() -> Self { - Self::new() + Self::new(Entity::from_raw(0)) } } impl KayakRootContext { /// Creates a new widget context. - pub fn new() -> Self { + pub fn new(camera_entity: Entity) -> Self { Self { tree: Arc::new(RwLock::new(Tree::default())), layout_cache: Arc::new(RwLock::new(LayoutCache::default())), @@ -116,6 +117,7 @@ impl KayakRootContext { index: Default::default(), order_tree: Default::default(), uninitilized_systems: Default::default(), + camera_entity, } } @@ -484,7 +486,7 @@ fn update_widgets_sys(world: &mut World) { world, ); - for (camera_entity, mut context) in context_data.drain(..) { + for (entity, mut context) in context_data.drain(..) { for system_id in context.uninitilized_systems.drain() { if let Some(system) = context.systems.get_mut(&system_id) { system.0.initialize(world); @@ -517,7 +519,7 @@ fn update_widgets_sys(world: &mut World) { // dbg!("Updating widgets!"); update_widgets( - camera_entity, + context.camera_entity, world, &context.tree, &context.layout_cache, @@ -565,7 +567,7 @@ fn update_widgets_sys(world: &mut World) { indices.clear(); } - world.entity_mut(camera_entity).insert(context); + world.entity_mut(entity).insert(context); } } @@ -587,14 +589,14 @@ fn update_widgets( ) { for entity in widgets.iter() { // A small hack to add parents to widgets - let mut command_queue = CommandQueue::default(); - { - let mut commands = Commands::new(&mut command_queue, &world); - if let Some(mut entity_commands) = commands.get_entity(entity.0) { - entity_commands.set_parent(camera_entity); - } - } - command_queue.apply(world); + // let mut command_queue = CommandQueue::default(); + // { + // let mut commands = Commands::new(&mut command_queue, &world); + // if let Some(mut entity_commands) = commands.get_entity(entity.0) { + // entity_commands.set_parent(camera_entity); + // } + // } + // command_queue.apply(world); if let Some(entity_ref) = world.get_entity(entity.0) { if let Some(widget_type) = entity_ref.get::<WidgetName>() { @@ -809,6 +811,7 @@ fn update_widget( let new_tick = widget_update_system.get_last_change_tick(); new_ticks.insert(widget_type.clone(), new_tick); widget_update_system.set_last_change_tick(old_tick); + widget_update_system.apply_buffers(world); if should_rerender { if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() { @@ -913,7 +916,7 @@ fn update_widget( // Mark node as needing a recalculation of rendering/layout. commands.entity(entity.0).insert(DirtyNode); - for (_index, changed_entity, _parent, changes) in diff.changes.iter() { + for (_index, changed_entity, parent, changes) in diff.changes.iter() { if changes.iter().any(|change| *change == Change::Deleted) { // commands.entity(changed_entity.0).despawn(); // commands.entity(changed_entity.0).remove::<DirtyNode>(); @@ -922,6 +925,10 @@ fn update_widget( if changes.iter().any(|change| *change == Change::Inserted) { if let Some(mut entity_commands) = commands.get_entity(changed_entity.0) { entity_commands.insert(Mounted); + entity_commands.set_parent(parent.0); + } + if let Some(mut parent_commands) = commands.get_entity(parent.0) { + parent_commands.add_child(changed_entity.0); } } } diff --git a/src/lib.rs b/src/lib.rs index d6e61f6eab0c82e6bbe55c357cc04a1579d96faf..02ac24264915c4f44ccfe4cbc0ab1f7a9f8ff887 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ pub use camera::*; pub const DEFAULT_FONT: &str = "Kayak-Default"; pub mod prelude { - pub use crate::camera::UICameraBundle; + pub use crate::camera::*; pub use crate::children::KChildren; pub use crate::clone_component::PreviousWidget; pub use crate::context::*; @@ -53,6 +53,7 @@ pub mod prelude { pub use crate::on_change::OnChange; pub use crate::on_event::OnEvent; pub use crate::on_layout::OnLayout; + pub use crate::render::draw_ui_graph; pub use crate::render::font::FontMapping; pub use crate::styles::*; pub use crate::tree::*; diff --git a/src/render/extract.rs b/src/render/extract.rs index 0990189be19f8f6fd8fe02ee357106f5af489d04..ce034cedd4ddddc980666397f51b8d2864c9f376 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -3,13 +3,19 @@ use crate::{ node::Node, render_primitive::RenderPrimitive, styles::Corner, + CameraUIKayak, }; use bevy::{ prelude::{ - Assets, Camera, Color, Commands, Entity, Image, IntoSystemAppConfig, Plugin, Query, Rect, - Res, Vec2, With, + Assets, Camera, Camera2d, Camera3d, Color, Commands, Component, Entity, GlobalTransform, + Image, IntoSystemAppConfig, IntoSystemAppConfigs, Mat4, Plugin, Query, Rect, Res, UVec4, + Vec2, With, + }, + render::{ + render_phase::RenderPhase, + view::{ColorGrading, ExtractedView}, + Extract, ExtractSchedule, RenderApp, }, - render::{Extract, ExtractSchedule, RenderApp}, window::{PrimaryWindow, Window, WindowRef}, }; use kayak_font::KayakFont; @@ -17,6 +23,7 @@ use kayak_font::KayakFont; use super::{ font::{self, FontMapping}, image, nine_patch, texture_atlas, + ui_pass::TransparentUI, unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType}, }; @@ -29,43 +36,52 @@ impl Plugin for BevyKayakUIExtractPlugin { fn build(&self, app: &mut bevy::prelude::App) { let render_app = app.sub_app_mut(RenderApp); render_app.add_system(extract.in_schedule(ExtractSchedule)); + render_app.add_systems( + ( + extract_default_ui_camera_view::<Camera2d>, + extract_default_ui_camera_view::<Camera3d>, + ) + .in_schedule(ExtractSchedule), + ); } } pub fn extract( mut commands: Commands, - context_query: Extract<Query<(Entity, &KayakRootContext, &Camera)>>, + context_query: Extract<Query<(Entity, &KayakRootContext)>>, fonts: Extract<Res<Assets<KayakFont>>>, font_mapping: Extract<Res<FontMapping>>, node_query: Extract<Query<&Node>>, widget_names: Extract<Query<&WidgetName>>, images: Extract<Res<Assets<Image>>>, primary_window: Extract<Query<&Window, With<PrimaryWindow>>>, - windows: Extract<Query<&Window>>, + cameras: Extract<Query<&Camera>>, ) { let mut render_primitives = Vec::new(); - for (entity, context, camera) in context_query.iter() { - let dpi = match &camera.target { - bevy::render::camera::RenderTarget::Window(window_ref) => match window_ref { - WindowRef::Primary => { - if let Ok(window) = primary_window.get_single() { - window.scale_factor() as f32 - } else { - 1.0 - } - } - WindowRef::Entity(window_entity) => { - if let Ok(window) = windows.get(*window_entity) { - window.scale_factor() as f32 - } else { - 1.0 + for (_entity, context) in context_query.iter() { + let dpi = if let Ok(camera) = cameras.get(context.camera_entity) { + match &camera.target { + bevy::render::camera::RenderTarget::Window(window_ref) => match window_ref { + WindowRef::Primary => { + if let Ok(window) = primary_window.get_single() { + window.scale_factor() as f32 + } else { + 1.0 + } } - } - }, - _ => 1.0, + _ => 1.0, + }, + _ => 1.0, + } + } else { + 1.0 }; let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names); - render_primitives.extend(new_render_primitives.drain(..).map(|r| (entity, dpi, r))); + render_primitives.extend( + new_render_primitives + .drain(..) + .map(|r| (context.camera_entity, dpi, r)), + ); } let mut extracted_quads = Vec::new(); @@ -133,3 +149,48 @@ pub fn extract( // dbg!(&extracted_quads); commands.spawn_batch(extracted_quads); } + +#[derive(Component)] +pub struct DefaultCameraView(pub Entity); + +const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; + +pub fn extract_default_ui_camera_view<T: Component>( + mut commands: Commands, + query: Extract<Query<(Entity, &Camera, &CameraUIKayak), With<T>>>, +) { + for (entity, camera, _camera_ui) in &query { + if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = ( + camera.logical_viewport_size(), + camera.physical_viewport_rect(), + camera.physical_viewport_size(), + ) { + // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection + let projection_matrix = + Mat4::orthographic_rh(0.0, logical_size.x, logical_size.y, 0.0, 0.0, 1000.0); + let default_camera_view = commands + .spawn(ExtractedView { + projection: projection_matrix, + transform: GlobalTransform::from_xyz( + 0.0, + 0.0, + 1000.0 + UI_CAMERA_TRANSFORM_OFFSET, + ), + hdr: camera.hdr, + viewport: UVec4::new( + physical_origin.x, + physical_origin.y, + physical_size.x, + physical_size.y, + ), + view_projection: None, + color_grading: ColorGrading::default(), + }) + .id(); + commands.get_or_spawn(entity).insert(( + DefaultCameraView(default_camera_view), + RenderPhase::<TransparentUI>::default(), + )); + } + } +} diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs index 89c510f3f0ada0505848f1d858ce75d5e98df690..d7c8edf588d5ff6e44ca721783e44b7ce57f9b60 100644 --- a/src/render/ui_pass.rs +++ b/src/render/ui_pass.rs @@ -1,117 +1,123 @@ -use bevy::core_pipeline::clear_color::ClearColorConfig; -use bevy::ecs::prelude::*; -use bevy::prelude::ClearColor; -use bevy::render::render_phase::{DrawFunctionId, PhaseItem}; -use bevy::render::render_resource::CachedRenderPipelineId; -use bevy::render::{ - render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, - render_phase::{DrawFunctions, RenderPhase}, - render_resource::{LoadOp, Operations, RenderPassDescriptor}, - renderer::RenderContext, - view::{ExtractedView, ViewTarget}, -}; -use bevy::utils::FloatOrd; - -use crate::CameraUIKayak; - -pub struct TransparentUI { - pub sort_key: FloatOrd, - pub entity: Entity, - pub pipeline: CachedRenderPipelineId, - pub draw_function: DrawFunctionId, -} - -impl PhaseItem for TransparentUI { - type SortKey = FloatOrd; - - #[inline] - fn sort_key(&self) -> Self::SortKey { - self.sort_key - } - - #[inline] - fn draw_function(&self) -> DrawFunctionId { - self.draw_function - } - - fn entity(&self) -> Entity { - self.entity - } -} - -pub struct MainPassUINode { - query: QueryState< - ( - &'static RenderPhase<TransparentUI>, - &'static ViewTarget, - &'static CameraUIKayak, - ), - With<ExtractedView>, - >, -} - -impl MainPassUINode { - pub const IN_VIEW: &'static str = "view"; - - pub fn new(world: &mut World) -> Self { - Self { - query: world.query_filtered(), - } - } -} - -impl Node for MainPassUINode { - fn input(&self) -> Vec<SlotInfo> { - vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)] - } - - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } - - fn run( - &self, - graph: &mut RenderGraphContext, - render_context: &mut RenderContext, - world: &World, - ) -> Result<(), NodeRunError> { - let view_entity = graph.get_input_entity(Self::IN_VIEW)?; - // adapted from bevy itself; - // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6> - let (transparent_phase, target, camera_ui) = match self.query.get_manual(world, view_entity) - { - Ok(it) => it, - _ => return Ok(()), - }; - // let clear_color = world.get_resource::<ClearColor>().unwrap(); - { - let pass_descriptor = RenderPassDescriptor { - label: Some("main_transparent_pass_UI"), - color_attachments: &[Some(target.get_unsampled_color_attachment(Operations { - load: match camera_ui.clear_color { - ClearColorConfig::Default => { - LoadOp::Clear(world.resource::<ClearColor>().0.into()) - } - ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), - ClearColorConfig::None => LoadOp::Load, - }, - store: true, - }))], - depth_stencil_attachment: None, - }; - - let draw_functions = world - .get_resource::<DrawFunctions<TransparentUI>>() - .unwrap(); - - let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); - let mut draw_functions = draw_functions.write(); - for item in transparent_phase.items.iter() { - let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); - draw_function.draw(world, &mut tracked_pass, view_entity, item); - } - } - - Ok(()) - } -} +use bevy::ecs::prelude::*; +use bevy::render::render_phase::{DrawFunctionId, PhaseItem}; +use bevy::render::render_resource::CachedRenderPipelineId; +use bevy::render::{ + render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, + render_phase::{DrawFunctions, RenderPhase}, + render_resource::{LoadOp, Operations, RenderPassDescriptor}, + renderer::RenderContext, + view::{ExtractedView, ViewTarget}, +}; +use bevy::utils::FloatOrd; + +use crate::CameraUIKayak; + +use super::extract::DefaultCameraView; + +pub struct TransparentUI { + pub sort_key: FloatOrd, + pub entity: Entity, + pub pipeline: CachedRenderPipelineId, + pub draw_function: DrawFunctionId, +} + +impl PhaseItem for TransparentUI { + type SortKey = FloatOrd; + + #[inline] + fn sort_key(&self) -> Self::SortKey { + self.sort_key + } + + #[inline] + fn draw_function(&self) -> DrawFunctionId { + self.draw_function + } + + fn entity(&self) -> Entity { + self.entity + } +} + +pub struct MainPassUINode { + query: QueryState< + ( + &'static RenderPhase<TransparentUI>, + &'static ViewTarget, + &'static CameraUIKayak, + ), + With<ExtractedView>, + >, + default_camera_view_query: QueryState<&'static DefaultCameraView>, +} + +impl MainPassUINode { + pub const IN_VIEW: &'static str = "view"; + + pub fn new(world: &mut World) -> Self { + Self { + query: world.query_filtered(), + default_camera_view_query: world.query(), + } + } +} + +impl Node for MainPassUINode { + fn input(&self) -> Vec<SlotInfo> { + vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)] + } + + fn update(&mut self, world: &mut World) { + self.query.update_archetypes(world); + self.default_camera_view_query.update_archetypes(world); + } + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + world: &World, + ) -> Result<(), NodeRunError> { + let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?; + // adapted from bevy itself; + // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6> + let (transparent_phase, target, _camera_ui) = + match self.query.get_manual(world, input_view_entity) { + Ok(it) => it, + _ => return Ok(()), + }; + + let view_entity = if let Ok(default_view) = self + .default_camera_view_query + .get_manual(world, input_view_entity) + { + default_view.0 + } else { + input_view_entity + }; + // let clear_color = world.get_resource::<ClearColor>().unwrap(); + { + let pass_descriptor = RenderPassDescriptor { + label: Some("main_transparent_pass_UI"), + color_attachments: &[Some(target.get_unsampled_color_attachment(Operations { + load: LoadOp::Load, + store: true, + }))], + depth_stencil_attachment: None, + }; + + let draw_functions = world + .get_resource::<DrawFunctions<TransparentUI>>() + .unwrap(); + + let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); + let mut draw_functions = draw_functions.write(); + for item in transparent_phase.items.iter() { + let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); + draw_function.draw(world, &mut tracked_pass, view_entity, item); + } + } + + Ok(()) + } +} diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs index adc75a1b383733e65c853aa6e4e9147ea145efff..96bf6f80e5047c780d1a8e8937c5c3c61f0d12a7 100644 --- a/src/render/unified/pipeline.rs +++ b/src/render/unified/pipeline.rs @@ -1,7 +1,8 @@ -use bevy::prelude::{Msaa, Rect, Resource}; +use bevy::prelude::{Rect, Resource}; use bevy::render::render_resource::{ DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines, }; +use bevy::render::view::{ExtractedView, ViewTarget}; use bevy::utils::FloatOrd; use bevy::{ ecs::system::{ @@ -67,27 +68,10 @@ impl FontRenderingPipeline for UnifiedPipeline { } } -bitflags::bitflags! { - #[repr(transparent)] - pub struct UnifiedPipelineKey: u32 { - const NONE = 0; - const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; - } -} - -impl UnifiedPipelineKey { - const MSAA_MASK_BITS: u32 = 0b111; - const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones(); - - pub fn from_msaa_samples(msaa_samples: u32) -> Self { - let msaa_bits = - (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS; - Self::from_bits(msaa_bits).unwrap() - } - - pub fn msaa_samples(&self) -> u32 { - 1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS) - } +#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnifiedPipelineKey { + pub msaa: u32, + pub hdr: bool, } impl FromWorld for UnifiedPipeline { @@ -244,7 +228,7 @@ impl FromWorld for UnifiedPipeline { impl SpecializedRenderPipeline for UnifiedPipeline { type Key = UnifiedPipelineKey; - fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor { + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { let vertex_buffer_layout = VertexBufferLayout { array_stride: 60, step_mode: VertexStepMode::Vertex, @@ -284,7 +268,11 @@ impl SpecializedRenderPipeline for UnifiedPipeline { shader_defs: vec![], entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: if key.hdr { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, blend: Some(BlendState { color: BlendComponent { src_factor: BlendFactor::SrcAlpha, @@ -317,7 +305,7 @@ impl SpecializedRenderPipeline for UnifiedPipeline { }, depth_stencil: None, multisample: MultisampleState { - count: 1, + count: key.msaa, mask: !0, alpha_to_coverage_enabled: false, }, @@ -359,7 +347,7 @@ pub struct ExtractedQuad { } #[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] +#[derive(Copy, Clone)] struct QuadVertex { pub position: [f32; 3], pub color: [f32; 4], @@ -367,6 +355,9 @@ struct QuadVertex { pub pos_size: [f32; 4], } +unsafe impl Zeroable for QuadVertex {} +unsafe impl Pod for QuadVertex {} + #[repr(C)] #[derive(Copy, Clone, ShaderType)] struct QuadType { @@ -537,11 +528,10 @@ pub fn queue_quads( mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>, mut pipeline_cache: ResMut<PipelineCache>, mut extracted_sprites: Query<(Entity, &ExtractedQuad)>, - mut views: Query<(Entity, &mut RenderPhase<TransparentUI>)>, + mut views: Query<(Entity, &mut RenderPhase<TransparentUI>, &ExtractedView)>, mut image_bind_groups: ResMut<ImageBindGroups>, unified_pipeline: Res<UnifiedPipeline>, gpu_images: Res<RenderAssets<Image>>, - msaa: Res<Msaa>, ) { if let Some(type_binding) = sprite_meta.types_buffer.binding() { sprite_meta.types_bind_group = @@ -565,13 +555,16 @@ pub fn queue_quads( layout: &quad_pipeline.view_layout, })); - let key = UnifiedPipelineKey::from_msaa_samples(msaa.samples()); - let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key); - let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap(); - for (camera_entity, mut transparent_phase) in views.iter_mut() { + for (camera_entity, mut transparent_phase, view) in views.iter_mut() { + let key = UnifiedPipelineKey { + msaa: 1, + hdr: view.hdr, + }; + let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key); + for (entity, quad) in extracted_sprites.iter_mut() { - if quad.camera_entity != camera_entity { + if camera_entity != quad.camera_entity { continue; } if let Some(image_handle) = quad.image.as_ref() { @@ -599,6 +592,7 @@ pub fn queue_quads( }); } } + transparent_phase.add(TransparentUI { draw_function: draw_quad, pipeline: spec_pipeline, diff --git a/src/render/unified/shader.wgsl b/src/render/unified/shader.wgsl index 4341b7e53853eb9b70eaa674b8d79523ba004cfa..122cfb872ee9a1025487f9500e67e9032db2f1bf 100644 --- a/src/render/unified/shader.wgsl +++ b/src/render/unified/shader.wgsl @@ -71,7 +71,7 @@ fn sample_sdf(coords: vec2<f32>, arr: i32, scale: f32) -> f32 { } fn range_curve(font_size: f32) -> f32 { - return (8.528 - 9.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0; + return (5.128 - 6.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0; } @fragment diff --git a/src/widget_context.rs b/src/widget_context.rs index c6e64c770389ec4737fb9c45fd6fd9ada62f9f69..5f5b95ed41c68cc876f0abd06ff53e345c68d4b0 100644 --- a/src/widget_context.rs +++ b/src/widget_context.rs @@ -225,6 +225,21 @@ impl KayakWidgetContext { } } + // Despawns a widget entity and it's decedents. This is done in a safe way by keeping entity id's around. + pub fn despawn_safe(&self, commands: &mut Commands, entity: Entity) { + if let Ok(mut tree) = self.old_tree.write() { + let mut down_iter = tree.down_iter(); + down_iter.current_node = Some(WrappedIndex(entity)); + for child in down_iter { + commands.entity(child.0).despawn(); + commands.get_or_spawn(child.0); + } + commands.entity(entity).despawn(); + commands.get_or_spawn(entity); + tree.remove(WrappedIndex(entity)); + } + } + /// Attempts to get the layout rect for the widget with the given ID /// /// # Arguments