From f3aba8fe74d6acefa720e1374abe4db0508cba41 Mon Sep 17 00:00:00 2001
From: StarToaster <startoaster23@gmail.com>
Date: Thu, 27 Oct 2022 20:02:31 -0400
Subject: [PATCH] Fixed some issues. Worked out render target example!

---
 examples/image.rs         |   2 +-
 examples/render_target.rs | 144 ++++++++++++++++++++++++++++++++++++++
 src/camera/camera.rs      |  13 ++--
 src/camera/mod.rs         |   4 +-
 src/render/mod.rs         |   4 +-
 src/render/ui_pass.rs     |  27 +++++--
 src/widgets/app.rs        |  26 +++++--
 src/widgets/image.rs      |  10 +--
 src/widgets/mod.rs        |   8 +--
 9 files changed, 210 insertions(+), 28 deletions(-)
 create mode 100644 examples/render_target.rs

diff --git a/examples/image.rs b/examples/image.rs
index ec2cd59..c2dabf9 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -17,7 +17,7 @@ fn startup(
     rsx! {
         <KayakAppBundle>
             <KImageBundle
-                image={Image(image.clone())}
+                image={KImage(image.clone())}
                 style={KStyle {
                     position_type: StyleProp::Value(KPositionType::SelfDirected),
                     left: StyleProp::Value(Units::Pixels(10.0)),
diff --git a/examples/render_target.rs b/examples/render_target.rs
new file mode 100644
index 0000000..763d5c6
--- /dev/null
+++ b/examples/render_target.rs
@@ -0,0 +1,144 @@
+use bevy::{
+    prelude::*,
+    render::{
+        camera::RenderTarget,
+        render_resource::{
+            Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
+        },
+    },
+};
+use kayak_ui::{
+    prelude::{widgets::*, *},
+    CameraUIKayak,
+};
+
+// Marks the main pass cube, to which the texture is applied.
+#[derive(Component)]
+struct MainPassCube;
+
+fn startup(
+    mut commands: Commands,
+    mut font_mapping: ResMut<FontMapping>,
+    asset_server: Res<AssetServer>,
+    mut meshes: ResMut<Assets<Mesh>>,
+    mut materials: ResMut<Assets<StandardMaterial>>,
+    mut images: ResMut<Assets<Image>>,
+) {
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+
+    let size = Extent3d {
+        width: 1024,
+        height: 1024,
+        ..default()
+    };
+
+    // This is the texture that will be rendered to.
+    let mut image = Image {
+        texture_descriptor: TextureDescriptor {
+            label: None,
+            size,
+            dimension: TextureDimension::D2,
+            format: TextureFormat::Bgra8UnormSrgb,
+            mip_level_count: 1,
+            sample_count: 1,
+            usage: TextureUsages::TEXTURE_BINDING
+                | TextureUsages::COPY_DST
+                | TextureUsages::RENDER_ATTACHMENT,
+        },
+        ..default()
+    };
+
+    // fill image.data with zeroes
+    image.resize(size);
+
+    let image_handle = images.add(image);
+
+    commands.spawn(UICameraBundle {
+        camera: Camera {
+            priority: -1,
+            target: RenderTarget::Image(image_handle.clone()),
+            ..Camera::default()
+        },
+        camera_ui: CameraUIKayak {
+            clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default,
+        },
+        ..UICameraBundle::new()
+    });
+
+    let mut widget_context = KayakRootContext::new();
+    let parent_id = None;
+    rsx! {
+        <KayakAppBundle
+            styles={KStyle {
+                padding: Edge::all(Units::Stretch(1.0)).into(),
+                ..Default::default()
+            }}
+        >
+            <TextWidgetBundle
+                text={TextProps {
+                    size: 150.0,
+                    content: "Hello World".into(),
+                    ..Default::default()
+                }}
+            />
+        </KayakAppBundle>
+    }
+    commands.insert_resource(widget_context);
+
+    // Setup 3D scene
+    // Light
+    commands.spawn(PointLightBundle {
+        transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
+        ..default()
+    });
+
+    let cube_size = 4.0;
+    let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size)));
+
+    // This material has the texture that has been rendered.
+    let material_handle = materials.add(StandardMaterial {
+        base_color_texture: Some(image_handle),
+        reflectance: 0.02,
+        unlit: false,
+        ..default()
+    });
+
+    // Main pass cube, with material containing the rendered first pass texture.
+    commands
+        .spawn(PbrBundle {
+            mesh: cube_handle,
+            material: material_handle,
+            transform: Transform {
+                translation: Vec3::new(0.0, 0.0, 1.5),
+                rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0),
+                ..default()
+            },
+            ..default()
+        })
+        .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()
+    });
+}
+
+/// Rotates the outer cube (main pass)
+fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) {
+    for mut transform in &mut query {
+        transform.rotate_x(1.0 * time.delta_seconds());
+        transform.rotate_y(0.7 * time.delta_seconds());
+    }
+}
+
+fn main() {
+    App::new()
+        .add_plugins(DefaultPlugins)
+        .add_plugin(KayakContextPlugin)
+        .add_plugin(KayakWidgets)
+        .add_startup_system(startup)
+        .add_system(cube_rotator_system)
+        .run()
+}
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
index edd4761..d7d990f 100644
--- a/src/camera/camera.rs
+++ b/src/camera/camera.rs
@@ -1,4 +1,5 @@
 use bevy::{
+    core_pipeline::clear_color::ClearColorConfig,
     ecs::query::QueryItem,
     prelude::{Bundle, Component, GlobalTransform, Transform, With},
     render::{
@@ -13,9 +14,11 @@ use super::ortho::UIOrthographicProjection;
 
 /// Kayak UI's default UI camera.
 #[derive(Component, Clone, Default)]
-pub struct CameraUiKayak;
+pub struct CameraUIKayak {
+    pub clear_color: ClearColorConfig,
+}
 
-impl ExtractComponent for CameraUiKayak {
+impl ExtractComponent for CameraUIKayak {
     type Query = &'static Self;
     type Filter = With<Camera>;
 
@@ -35,7 +38,7 @@ pub struct UICameraBundle {
     pub frustum: Frustum,
     pub transform: Transform,
     pub global_transform: GlobalTransform,
-    pub marker: CameraUiKayak,
+    pub camera_ui: CameraUIKayak,
 }
 
 impl UICameraBundle {
@@ -73,7 +76,9 @@ impl UICameraBundle {
             transform,
             // camera_2d: Camera2d::default(),
             global_transform: Default::default(),
-            marker: CameraUiKayak,
+            camera_ui: CameraUIKayak {
+                clear_color: ClearColorConfig::None,
+            },
         }
     }
 }
diff --git a/src/camera/mod.rs b/src/camera/mod.rs
index b569899..60e86c3 100644
--- a/src/camera/mod.rs
+++ b/src/camera/mod.rs
@@ -6,7 +6,7 @@ use bevy::{
 mod camera;
 mod ortho;
 
-pub use camera::{CameraUiKayak, UICameraBundle};
+pub use camera::{CameraUIKayak, UICameraBundle};
 pub(crate) use ortho::UIOrthographicProjection;
 
 pub struct KayakUICameraPlugin;
@@ -18,6 +18,6 @@ impl Plugin for KayakUICameraPlugin {
             bevy::render::camera::camera_system::<UIOrthographicProjection>,
         )
         .add_plugin(CameraProjectionPlugin::<UIOrthographicProjection>::default())
-        .add_plugin(ExtractComponentPlugin::<CameraUiKayak>::default());
+        .add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default());
     }
 }
diff --git a/src/render/mod.rs b/src/render/mod.rs
index 40bbbe7..001531b 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -9,7 +9,7 @@ use bevy::{
 
 use crate::{
     render::{ui_pass::MainPassUINode, unified::UnifiedRenderPlugin},
-    CameraUiKayak,
+    CameraUIKayak,
 };
 
 use self::{extract::BevyKayakUIExtractPlugin, ui_pass::TransparentUI};
@@ -167,7 +167,7 @@ fn get_ui_graph(render_app: &mut App) -> RenderGraph {
 
 pub fn extract_core_pipeline_camera_phases(
     mut commands: Commands,
-    active_camera: Extract<Query<Entity, With<CameraUiKayak>>>,
+    active_camera: Extract<Query<Entity, With<CameraUIKayak>>>,
 ) {
     if let Ok(entity) = active_camera.get_single() {
         commands
diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs
index 5595367..70ed333 100644
--- a/src/render/ui_pass.rs
+++ b/src/render/ui_pass.rs
@@ -1,4 +1,6 @@
+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::{
@@ -10,6 +12,8 @@ use bevy::render::{
 };
 use bevy::utils::FloatOrd;
 
+use crate::CameraUIKayak;
+
 pub struct TransparentUI {
     pub sort_key: FloatOrd,
     pub entity: Entity,
@@ -32,8 +36,14 @@ impl PhaseItem for TransparentUI {
 }
 
 pub struct MainPassUINode {
-    query:
-        QueryState<(&'static RenderPhase<TransparentUI>, &'static ViewTarget), With<ExtractedView>>,
+    query: QueryState<
+        (
+            &'static RenderPhase<TransparentUI>,
+            &'static ViewTarget,
+            &'static CameraUIKayak,
+        ),
+        With<ExtractedView>,
+    >,
 }
 
 impl MainPassUINode {
@@ -41,7 +51,7 @@ impl MainPassUINode {
 
     pub fn new(world: &mut World) -> Self {
         Self {
-            query: QueryState::new(world),
+            query: world.query_filtered(),
         }
     }
 }
@@ -64,7 +74,8 @@ impl Node for MainPassUINode {
         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) = match self.query.get_manual(world, view_entity) {
+        let (transparent_phase, target, camera_ui) = match self.query.get_manual(world, view_entity)
+        {
             Ok(it) => it,
             _ => return Ok(()),
         };
@@ -73,7 +84,13 @@ impl Node for MainPassUINode {
             let pass_descriptor = RenderPassDescriptor {
                 label: Some("main_transparent_pass_UI"),
                 color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
-                    load: LoadOp::Load,
+                    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,
diff --git a/src/widgets/app.rs b/src/widgets/app.rs
index 9051362..8aa40a1 100644
--- a/src/widgets/app.rs
+++ b/src/widgets/app.rs
@@ -7,6 +7,7 @@ use crate::{
     prelude::KayakWidgetContext,
     styles::{KStyle, RenderCommand, StyleProp},
     widget::{EmptyState, Widget, WidgetParam},
+    CameraUIKayak,
 };
 
 #[derive(Component, Default, Clone, PartialEq)]
@@ -62,14 +63,29 @@ pub fn app_render(
     _: Commands,
     windows: Res<Windows>,
     mut query: Query<(&mut KStyle, &KChildren)>,
+    camera: Query<&Camera, With<CameraUIKayak>>,
 ) -> bool {
-    let primary_window = windows.get_primary().unwrap();
+    let (mut width, mut height) = (0.0, 0.0);
+
+    if let Ok(camera) = camera.get_single() {
+        if let Some(size) = camera.logical_viewport_size() {
+            width = size.x;
+            height = size.y;
+        }
+    }
+
+    if width == 0.0 {
+        let primary_window = windows.get_primary().unwrap();
+        width = primary_window.width();
+        height = primary_window.height();
+    }
+
     if let Ok((mut app_style, children)) = query.get_mut(entity) {
-        if app_style.width != StyleProp::Value(Units::Pixels(primary_window.width())) {
-            app_style.width = StyleProp::Value(Units::Pixels(primary_window.width()));
+        if app_style.width != StyleProp::Value(Units::Pixels(width)) {
+            app_style.width = StyleProp::Value(Units::Pixels(width));
         }
-        if app_style.height != StyleProp::Value(Units::Pixels(primary_window.height())) {
-            app_style.height = StyleProp::Value(Units::Pixels(primary_window.height()));
+        if app_style.height != StyleProp::Value(Units::Pixels(height)) {
+            app_style.height = StyleProp::Value(Units::Pixels(height));
         }
         app_style.render_command = StyleProp::Value(RenderCommand::Layout);
         children.process(&widget_context, Some(entity));
diff --git a/src/widgets/image.rs b/src/widgets/image.rs
index 375d52d..eb85fa1 100644
--- a/src/widgets/image.rs
+++ b/src/widgets/image.rs
@@ -10,13 +10,13 @@ use crate::{
 /// Renders a bevy image asset within the GUI
 /// The rendered image respects the styles.
 #[derive(Component, PartialEq, Clone, Default)]
-pub struct Image(pub Handle<bevy::prelude::Image>);
+pub struct KImage(pub Handle<bevy::prelude::Image>);
 
-impl Widget for Image {}
+impl Widget for KImage {}
 
 #[derive(Bundle)]
 pub struct KImageBundle {
-    pub image: Image,
+    pub image: KImage,
     pub style: KStyle,
     pub widget_name: WidgetName,
 }
@@ -26,14 +26,14 @@ impl Default for KImageBundle {
         Self {
             image: Default::default(),
             style: Default::default(),
-            widget_name: Image::default().get_name(),
+            widget_name: KImage::default().get_name(),
         }
     }
 }
 
 pub fn image_render(
     In((_widget_context, entity)): In<(KayakWidgetContext, Entity)>,
-    mut query: Query<(&mut KStyle, &Image)>,
+    mut query: Query<(&mut KStyle, &KImage)>,
 ) -> bool {
     if let Ok((mut style, image)) = query.get_mut(entity) {
         style.render_command = StyleProp::Value(RenderCommand::Image {
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
index 0197591..586c634 100644
--- a/src/widgets/mod.rs
+++ b/src/widgets/mod.rs
@@ -40,7 +40,7 @@ pub use background::{Background, BackgroundBundle};
 pub use button::{KButton, KButtonBundle};
 pub use clip::{Clip, ClipBundle};
 pub use element::{Element, ElementBundle};
-pub use image::{Image, KImageBundle};
+pub use image::{KImage, KImageBundle};
 pub use nine_patch::{NinePatch, NinePatchBundle};
 pub use scroll::{
     scroll_bar::{ScrollBarBundle, ScrollBarProps},
@@ -93,7 +93,7 @@ fn add_widget_systems(mut context: ResMut<KayakRootContext>) {
     context.add_widget_data::<KWindow, KWindowState>();
     context.add_widget_data::<Background, EmptyState>();
     context.add_widget_data::<Clip, EmptyState>();
-    context.add_widget_data::<Image, EmptyState>();
+    context.add_widget_data::<KImage, EmptyState>();
     context.add_widget_data::<TextureAtlasProps, EmptyState>();
     context.add_widget_data::<NinePatch, EmptyState>();
     context.add_widget_data::<Element, EmptyState>();
@@ -130,8 +130,8 @@ fn add_widget_systems(mut context: ResMut<KayakRootContext>) {
         clip_render,
     );
     context.add_widget_system(
-        Image::default().get_name(),
-        widget_update::<Image, EmptyState>,
+        KImage::default().get_name(),
+        widget_update::<KImage, EmptyState>,
         image_render,
     );
     context.add_widget_system(
-- 
GitLab