use crate::{ context::{KayakRootContext, WidgetName}, node::Node, CameraUIKayak, }; use bevy::{ prelude::*, render::{ render_resource::{DynamicUniformBuffer, ShaderType}, renderer::{RenderDevice, RenderQueue}, view::ColorGrading, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }, window::{PrimaryWindow, Window, WindowRef}, }; use kayak_font::KayakFont; use super::{ font::FontMapping, ui_pass::{TransparentUI, UIRenderPhase}, unified::pipeline::ExtractedQuads, }; // mod nine_patch; // mod texture_atlas; pub struct BevyKayakUIExtractPlugin; impl Plugin for BevyKayakUIExtractPlugin { fn build(&self, app: &mut bevy::prelude::App) { let render_app = app.sub_app_mut(RenderApp); render_app .init_resource::<UIViewUniforms>() .add_systems( ExtractSchedule, ( extract, extract_default_ui_camera_view::<Camera2d>, extract_default_ui_camera_view::<Camera3d>, ), ) .add_systems(Render, prepare_view_uniforms.in_set(RenderSet::Prepare)); } } pub fn extract( mut commands: Commands, 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>>>, cameras: Extract<Query<&Camera>>, mut extracted_quads: ResMut<ExtractedQuads>, ) { extracted_quads.clear(); extracted_quads.new_layer(None); for (_entity, context) in context_query.iter() { let dpi = if let Ok(camera) = cameras.get(context.camera_entity) { if let bevy::render::camera::RenderTarget::Window(WindowRef::Primary) = &camera.target { if let Ok(window) = primary_window.get_single() { window.scale_factor() } else { 1.0 } } else { 1.0 } } else { 1.0 }; context.build_render_primitives( &mut commands, context.camera_entity, dpi, &node_query, &widget_names, &fonts, &font_mapping, &images, &mut extracted_quads, ); // Resolve extracted quads if let Ok(mut layout_cache) = context.layout_cache.try_write() { extracted_quads.resolve(&mut commands, &mut layout_cache); // extracted_quads.debug(); } } // let mut extracted = extracted_quads.iter().map(|e| (e.quad_type, e.z_index, e.rect, e.c)).collect::<Vec<_>>(); // extracted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); // dbg!("Start"); // let mut last_type = super::unified::pipeline::UIQuadType::Clip; // for (qt, z, r, c) in extracted.iter() { // // if !(last_type == super::unified::pipeline::UIQuadType::Text && *qt == super::unified::pipeline::UIQuadType::Text) { // if *qt == super::unified::pipeline::UIQuadType::Text { // println!("qt: {:?}, c: {}, z: {}, r: {:?}", qt, c, z, r); // } else { // println!("qt: {:?}, z: {}, r: {:?}", qt, z, r); // } // last_type = *qt; // } } const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; #[derive(Component)] pub struct UIExtractedView { pub projection: Mat4, pub transform: GlobalTransform, // The view-projection matrix. When provided it is used instead of deriving it from // `projection` and `transform` fields, which can be helpful in cases where numerical // stability matters and there is a more direct way to derive the view-projection matrix. pub view_projection: Option<Mat4>, pub hdr: bool, // uvec4(origin.x, origin.y, width, height) pub viewport: UVec4, pub color_grading: ColorGrading, } pub fn extract_default_ui_camera_view<T: Component>( mut commands: Commands, query: Extract< Query<(Entity, &Camera, &OrthographicProjection), (With<CameraUIKayak>, With<T>)>, >, ) { for (entity, camera, ortho) 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(), ) { let logical_size = ortho.area.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); commands.get_or_spawn(entity).insert(( UIExtractedView { 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.min.x, physical_origin.min.y, physical_size.x, physical_size.y, ), view_projection: None, color_grading: ColorGrading::default(), }, UIRenderPhase::<TransparentUI>::default(), )); } } } #[derive(Resource, Default)] pub struct UIViewUniforms { pub uniforms: DynamicUniformBuffer<UIViewUniform>, } #[derive(Clone, ShaderType)] pub struct UIViewUniform { pub view_proj: Mat4, pub unjittered_view_proj: Mat4, pub inverse_view_proj: Mat4, pub view: Mat4, pub inverse_view: Mat4, pub projection: Mat4, pub inverse_projection: Mat4, pub world_position: Vec3, pub viewport: Vec4, pub frustum: [Vec4; 6], pub color_grading: ColorGrading, pub mip_bias: f32, } #[derive(Component, Debug)] pub struct UIViewUniformOffset { pub offset: u32, } use bevy::math::Vec4Swizzles; use bevy::render::camera::{MipBias, TemporalJitter}; pub fn prepare_view_uniforms( mut commands: Commands, render_device: Res<RenderDevice>, render_queue: Res<RenderQueue>, mut view_uniforms: ResMut<UIViewUniforms>, views: Query<( Entity, &UIExtractedView, Option<&TemporalJitter>, Option<&MipBias>, )>, ) { view_uniforms.uniforms.clear(); for (entity, camera, temporal_jitter, mip_bias) in &views { let viewport = camera.viewport.as_vec4(); let unjittered_projection = camera.projection; let mut projection = unjittered_projection; if let Some(temporal_jitter) = temporal_jitter { temporal_jitter.jitter_projection(&mut projection, viewport.zw()); } let inverse_projection = projection.inverse(); let view = camera.transform.compute_matrix(); let inverse_view = view.inverse(); // Map Frustum type to shader array<vec4<f32>, 6> let frustum = [Vec4::ZERO; 6]; let view_uniforms = UIViewUniformOffset { offset: view_uniforms.uniforms.push(&UIViewUniform { view_proj: camera .view_projection .unwrap_or_else(|| projection * inverse_view), unjittered_view_proj: unjittered_projection * inverse_view, inverse_view_proj: view * inverse_projection, view, inverse_view, projection, inverse_projection, world_position: camera.transform.translation(), viewport, color_grading: camera.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, frustum, }), }; commands.entity(entity).insert(view_uniforms); } view_uniforms .uniforms .write_buffer(&render_device, &render_queue); }