From 0084604875f23cc64cbd9a41ae6216857b7ab3a2 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sun, 3 Jul 2022 15:03:55 +0200 Subject: [PATCH 1/7] Viewport size as percentage of target --- .../src/core_2d/main_pass_2d_node.rs | 3 +- .../src/core_3d/main_pass_3d_node.rs | 9 ++-- crates/bevy_render/src/camera/camera.rs | 53 ++++++++++++++----- .../src/render_phase/draw_state.rs | 7 +-- examples/3d/split_screen.rs | 9 ++-- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index 626b022d46ede..0e5da5667b351 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -86,7 +86,8 @@ impl Node for MainPass2dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport); + let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); + tracked_pass.set_camera_viewport(viewport, physical_size); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index ec9aa7a6ff884..c9e11ca98ff2b 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -103,7 +103,8 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport); + let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); + tracked_pass.set_camera_viewport(viewport, physical_size); } for item in &opaque_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -142,7 +143,8 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport); + let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); + tracked_pass.set_camera_viewport(viewport, physical_size); } for item in &alpha_mask_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -186,7 +188,8 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport); + let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); + tracked_pass.set_camera_viewport(viewport, physical_size); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index e1e91c28486b8..dec4f99bac63f 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -39,16 +39,44 @@ pub struct Viewport { pub physical_position: UVec2, /// The physical size of the viewport rectangle to render to within the [`RenderTarget`] of this [`Camera`]. /// The origin of the rectangle is in the top-left corner. - pub physical_size: UVec2, + pub physical_size: AbsoluteOrPercentageVec, /// The minimum and maximum depth to render (on a scale from 0.0 to 1.0). pub depth: Range, } +#[derive(Reflect, Debug, Clone, Serialize, Deserialize)] +pub enum AbsoluteOrPercentageVec { + Absolute(UVec2), + Percentage(Vec2), +} + +impl AbsoluteOrPercentageVec { + pub fn as_absolute(&self, of: UVec2) -> UVec2 { + match self { + AbsoluteOrPercentageVec::Absolute(v) => *v, + AbsoluteOrPercentageVec::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), + } + } + + pub fn as_absolute_opt(&self, of_opt: Option) -> Option { + match self { + AbsoluteOrPercentageVec::Absolute(v) => Some(*v), + AbsoluteOrPercentageVec::Percentage(v) => { + if let Some(of) = of_opt { + Some((of.as_vec2() * *v).as_uvec2()) + } else { + None + } + } + } + } +} + impl Default for Viewport { fn default() -> Self { Self { physical_position: Default::default(), - physical_size: Default::default(), + physical_size: AbsoluteOrPercentageVec::Absolute(UVec2::default()), depth: 0.0..1.0, } } @@ -111,16 +139,18 @@ impl Camera { Some((physical_size.as_dvec2() / scale).as_vec2()) } + pub fn viewport_absolute_size(&self) -> Option { + self.viewport + .as_ref() + .and_then(|v| v.physical_size.as_absolute_opt(self.physical_target_size())) + } + /// The rendered physical bounds (minimum, maximum) of the camera. If the `viewport` field is /// set to [`Some`], this will be the rect of that custom viewport. Otherwise it will default to /// the full physical rect of the current [`RenderTarget`]. #[inline] pub fn physical_viewport_rect(&self) -> Option<(UVec2, UVec2)> { - let min = self - .viewport - .as_ref() - .map(|v| v.physical_position) - .unwrap_or(UVec2::ZERO); + let min = self.viewport_absolute_size().unwrap_or(UVec2::ZERO); let max = min + self.physical_viewport_size()?; Some((min, max)) } @@ -141,9 +171,8 @@ impl Camera { /// [`RenderTarget`], prefer [`Camera::logical_target_size`]. #[inline] pub fn logical_viewport_size(&self) -> Option { - self.viewport - .as_ref() - .and_then(|v| self.to_logical(v.physical_size)) + self.viewport_absolute_size() + .and_then(|s| self.to_logical(s)) .or_else(|| self.logical_target_size()) } @@ -153,9 +182,7 @@ impl Camera { /// For logic that requires the full physical size of the [`RenderTarget`], prefer [`Camera::physical_target_size`]. #[inline] pub fn physical_viewport_size(&self) -> Option { - self.viewport - .as_ref() - .map(|v| v.physical_size) + self.viewport_absolute_size() .or_else(|| self.physical_target_size()) } diff --git a/crates/bevy_render/src/render_phase/draw_state.rs b/crates/bevy_render/src/render_phase/draw_state.rs index a709c078f921d..14e8edfd2f38f 100644 --- a/crates/bevy_render/src/render_phase/draw_state.rs +++ b/crates/bevy_render/src/render_phase/draw_state.rs @@ -6,6 +6,7 @@ use crate::{ ShaderStages, }, }; +use bevy_math::UVec2; use bevy_utils::tracing::trace; use std::ops::Range; use wgpu::{IndexFormat, RenderPass}; @@ -340,12 +341,12 @@ impl<'a> TrackedRenderPass<'a> { /// Set the rendering viewport to the given [`Camera`](crate::camera::Viewport) [`Viewport`]. /// /// Subsequent draw calls will be projected into that viewport. - pub fn set_camera_viewport(&mut self, viewport: &Viewport) { + pub fn set_camera_viewport(&mut self, viewport: &Viewport, physical_size: UVec2) { self.set_viewport( viewport.physical_position.x as f32, viewport.physical_position.y as f32, - viewport.physical_size.x as f32, - viewport.physical_size.y as f32, + physical_size.x as f32, + physical_size.y as f32, viewport.depth.start, viewport.depth.end, ); diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 70cc464ea2593..aa6e9f0a1e0cb 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -3,7 +3,7 @@ use bevy::{ core_pipeline::clear_color::ClearColorConfig, prelude::*, - render::camera::Viewport, + render::camera::{AbsoluteOrPercentageVec, Viewport}, window::{WindowId, WindowResized}, }; @@ -97,14 +97,17 @@ fn set_camera_viewports( let mut left_camera = left_camera.single_mut(); left_camera.viewport = Some(Viewport { physical_position: UVec2::new(0, 0), - physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()), + physical_size: AbsoluteOrPercentageVec::Absolute(UVec2::new( + window.physical_width() / 2, + window.physical_height(), + )), ..default() }); let mut right_camera = right_camera.single_mut(); right_camera.viewport = Some(Viewport { physical_position: UVec2::new(window.physical_width() / 2, 0), - physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()), + physical_size: AbsoluteOrPercentageVec::Percentage(Vec2::new(0.5, 1.0)), ..default() }); } From bd3ee733aa75cc11e3489cd6ea5f36c78224300d Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sun, 3 Jul 2022 15:45:14 +0200 Subject: [PATCH 2/7] Viewport position as percentage of target --- .../src/core_2d/main_pass_2d_node.rs | 3 +-- .../src/core_3d/main_pass_3d_node.rs | 9 +++---- crates/bevy_render/src/camera/camera.rs | 24 ++++++++++++------- .../src/render_phase/draw_state.rs | 21 ++++++++++++---- examples/3d/split_screen.rs | 12 ++++++---- 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index 0e5da5667b351..cc9d5d69a047d 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -86,8 +86,7 @@ impl Node for MainPass2dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); - tracked_pass.set_camera_viewport(viewport, physical_size); + tracked_pass.set_camera_viewport(viewport, camera); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index c9e11ca98ff2b..29d6e9def3281 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -103,8 +103,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); - tracked_pass.set_camera_viewport(viewport, physical_size); + tracked_pass.set_camera_viewport(viewport, camera); } for item in &opaque_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -143,8 +142,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); - tracked_pass.set_camera_viewport(viewport, physical_size); + tracked_pass.set_camera_viewport(viewport, camera); } for item in &alpha_mask_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -188,8 +186,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - let physical_size = viewport.physical_size.as_absolute_opt(camera.physical_target_size).expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); - tracked_pass.set_camera_viewport(viewport, physical_size); + tracked_pass.set_camera_viewport(viewport, camera); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index dec4f99bac63f..6d21d63bbed63 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -36,32 +36,38 @@ use wgpu::Extent3d; pub struct Viewport { /// The physical position to render this viewport to within the [`RenderTarget`] of this [`Camera`]. /// (0,0) corresponds to the top-left corner - pub physical_position: UVec2, + pub physical_position: AbsOrPercVec, /// The physical size of the viewport rectangle to render to within the [`RenderTarget`] of this [`Camera`]. /// The origin of the rectangle is in the top-left corner. - pub physical_size: AbsoluteOrPercentageVec, + pub physical_size: AbsOrPercVec, /// The minimum and maximum depth to render (on a scale from 0.0 to 1.0). pub depth: Range, } #[derive(Reflect, Debug, Clone, Serialize, Deserialize)] -pub enum AbsoluteOrPercentageVec { +pub enum AbsOrPercVec { Absolute(UVec2), Percentage(Vec2), } -impl AbsoluteOrPercentageVec { +impl Default for AbsOrPercVec { + fn default() -> Self { + Self::Absolute(Default::default()) + } +} + +impl AbsOrPercVec { pub fn as_absolute(&self, of: UVec2) -> UVec2 { match self { - AbsoluteOrPercentageVec::Absolute(v) => *v, - AbsoluteOrPercentageVec::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), + AbsOrPercVec::Absolute(v) => *v, + AbsOrPercVec::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), } } pub fn as_absolute_opt(&self, of_opt: Option) -> Option { match self { - AbsoluteOrPercentageVec::Absolute(v) => Some(*v), - AbsoluteOrPercentageVec::Percentage(v) => { + AbsOrPercVec::Absolute(v) => Some(*v), + AbsOrPercVec::Percentage(v) => { if let Some(of) = of_opt { Some((of.as_vec2() * *v).as_uvec2()) } else { @@ -76,7 +82,7 @@ impl Default for Viewport { fn default() -> Self { Self { physical_position: Default::default(), - physical_size: AbsoluteOrPercentageVec::Absolute(UVec2::default()), + physical_size: Default::default(), depth: 0.0..1.0, } } diff --git a/crates/bevy_render/src/render_phase/draw_state.rs b/crates/bevy_render/src/render_phase/draw_state.rs index 14e8edfd2f38f..4a9c0909a8bf3 100644 --- a/crates/bevy_render/src/render_phase/draw_state.rs +++ b/crates/bevy_render/src/render_phase/draw_state.rs @@ -1,12 +1,11 @@ use crate::{ - camera::Viewport, + camera::{ExtractedCamera, Viewport}, prelude::Color, render_resource::{ BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId, ShaderStages, }, }; -use bevy_math::UVec2; use bevy_utils::tracing::trace; use std::ops::Range; use wgpu::{IndexFormat, RenderPass}; @@ -341,10 +340,22 @@ impl<'a> TrackedRenderPass<'a> { /// Set the rendering viewport to the given [`Camera`](crate::camera::Viewport) [`Viewport`]. /// /// Subsequent draw calls will be projected into that viewport. - pub fn set_camera_viewport(&mut self, viewport: &Viewport, physical_size: UVec2) { + pub fn set_camera_viewport(&mut self, viewport: &Viewport, camera: &ExtractedCamera) { + let physical_size = viewport + .physical_size + .as_absolute_opt(camera.physical_target_size) + .expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); + + let physical_position = viewport + .physical_position + .as_absolute_opt(camera.physical_target_size) + .expect( + "Couldn't get camera.physical_target_size (needed for relative viewport position)", + ); + self.set_viewport( - viewport.physical_position.x as f32, - viewport.physical_position.y as f32, + physical_position.x as f32, + physical_position.y as f32, physical_size.x as f32, physical_size.y as f32, viewport.depth.start, diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index aa6e9f0a1e0cb..8edded7a106e5 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -3,7 +3,7 @@ use bevy::{ core_pipeline::clear_color::ClearColorConfig, prelude::*, - render::camera::{AbsoluteOrPercentageVec, Viewport}, + render::camera::{AbsOrPercVec, Viewport}, window::{WindowId, WindowResized}, }; @@ -96,8 +96,9 @@ fn set_camera_viewports( let window = windows.primary(); let mut left_camera = left_camera.single_mut(); left_camera.viewport = Some(Viewport { - physical_position: UVec2::new(0, 0), - physical_size: AbsoluteOrPercentageVec::Absolute(UVec2::new( + // You can use absolute pixel values... + physical_position: AbsOrPercVec::Absolute(UVec2::ZERO), + physical_size: AbsOrPercVec::Absolute(UVec2::new( window.physical_width() / 2, window.physical_height(), )), @@ -106,8 +107,9 @@ fn set_camera_viewports( let mut right_camera = right_camera.single_mut(); right_camera.viewport = Some(Viewport { - physical_position: UVec2::new(window.physical_width() / 2, 0), - physical_size: AbsoluteOrPercentageVec::Percentage(Vec2::new(0.5, 1.0)), + // ... or specify a percentage of the render target. + physical_position: AbsOrPercVec::Percentage(Vec2::new(0.5, 0.0)), + physical_size: AbsOrPercVec::Percentage(Vec2::new(0.5, 1.0)), ..default() }); } From 943393e8d38e3c9e899c343e1a62f97100b499c7 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sun, 3 Jul 2022 16:03:17 +0200 Subject: [PATCH 3/7] Docs and cleanup --- crates/bevy_render/src/camera/camera.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 6d21d63bbed63..3e8218c3c113b 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -44,6 +44,10 @@ pub struct Viewport { pub depth: Range, } +/// A vector wrapper that represents either absolute value or value as a percentage. +/// +/// For example, used in representing viewport position, either as absolute pixel value, or as a +/// percentage of render target size. #[derive(Reflect, Debug, Clone, Serialize, Deserialize)] pub enum AbsOrPercVec { Absolute(UVec2), @@ -57,6 +61,8 @@ impl Default for AbsOrPercVec { } impl AbsOrPercVec { + /// Returns percentage of provided vec or absolute value from self. + #[inline] pub fn as_absolute(&self, of: UVec2) -> UVec2 { match self { AbsOrPercVec::Absolute(v) => *v, @@ -64,16 +70,13 @@ impl AbsOrPercVec { } } + /// Returns percentage of provided vec or absolute value. + /// Returns `None` if self is `Percentage` and argument is `None` + #[inline] pub fn as_absolute_opt(&self, of_opt: Option) -> Option { match self { AbsOrPercVec::Absolute(v) => Some(*v), - AbsOrPercVec::Percentage(v) => { - if let Some(of) = of_opt { - Some((of.as_vec2() * *v).as_uvec2()) - } else { - None - } - } + AbsOrPercVec::Percentage(_) => of_opt.map(|of| self.as_absolute(of)), } } } @@ -145,6 +148,14 @@ impl Camera { Some((physical_size.as_dvec2() / scale).as_vec2()) } + /// Returns absolute size of this `Camera`'s viewport. + /// + /// I.e. multiplies the percentage size of viewport with `physical_target_size` or uses + /// viewport's absolute size + /// + /// Returns `None` if viewport size is a `Percentage` and [`physical_target_size`] returns + /// `None` + #[inline] pub fn viewport_absolute_size(&self) -> Option { self.viewport .as_ref() From 911b93024e4bcc4b95d6937cfe037a67331133d4 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sun, 3 Jul 2022 17:30:15 +0200 Subject: [PATCH 4/7] Fix docs --- crates/bevy_render/src/camera/camera.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 3e8218c3c113b..fd2a6cb54d828 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -153,7 +153,7 @@ impl Camera { /// I.e. multiplies the percentage size of viewport with `physical_target_size` or uses /// viewport's absolute size /// - /// Returns `None` if viewport size is a `Percentage` and [`physical_target_size`] returns + /// Returns `None` if viewport size is a `Percentage` and [`Camera::physical_target_size`] returns /// `None` #[inline] pub fn viewport_absolute_size(&self) -> Option { From 359c6b4e0d09dc37837f9c7ebeb9c90b1d1f0d06 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Mon, 4 Jul 2022 08:36:22 +0000 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: Alice Cecile --- crates/bevy_render/src/camera/camera.rs | 9 +++++---- crates/bevy_render/src/render_phase/draw_state.rs | 4 ++-- examples/3d/split_screen.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index fd2a6cb54d828..3f8134f3ac54a 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -61,7 +61,7 @@ impl Default for AbsOrPercVec { } impl AbsOrPercVec { - /// Returns percentage of provided vec or absolute value from self. + /// Converts this size into a absolute size in pixels. #[inline] pub fn as_absolute(&self, of: UVec2) -> UVec2 { match self { @@ -70,10 +70,11 @@ impl AbsOrPercVec { } } - /// Returns percentage of provided vec or absolute value. + /// Converts this size into a absolute size in pixels if possible. + /// Returns `None` if self is `Percentage` and argument is `None` #[inline] - pub fn as_absolute_opt(&self, of_opt: Option) -> Option { + pub fn try_as_absolute(&self, of_opt: Option) -> Option { match self { AbsOrPercVec::Absolute(v) => Some(*v), AbsOrPercVec::Percentage(_) => of_opt.map(|of| self.as_absolute(of)), @@ -159,7 +160,7 @@ impl Camera { pub fn viewport_absolute_size(&self) -> Option { self.viewport .as_ref() - .and_then(|v| v.physical_size.as_absolute_opt(self.physical_target_size())) + .and_then(|v| v.physical_size.try_as_absolute(self.physical_target_size())) } /// The rendered physical bounds (minimum, maximum) of the camera. If the `viewport` field is diff --git a/crates/bevy_render/src/render_phase/draw_state.rs b/crates/bevy_render/src/render_phase/draw_state.rs index 4a9c0909a8bf3..26faaa32bbde9 100644 --- a/crates/bevy_render/src/render_phase/draw_state.rs +++ b/crates/bevy_render/src/render_phase/draw_state.rs @@ -343,12 +343,12 @@ impl<'a> TrackedRenderPass<'a> { pub fn set_camera_viewport(&mut self, viewport: &Viewport, camera: &ExtractedCamera) { let physical_size = viewport .physical_size - .as_absolute_opt(camera.physical_target_size) + .try_as_absolute(camera.physical_target_size) .expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); let physical_position = viewport .physical_position - .as_absolute_opt(camera.physical_target_size) + .try_as_absolute(camera.physical_target_size) .expect( "Couldn't get camera.physical_target_size (needed for relative viewport position)", ); diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 8edded7a106e5..9bd36343ef699 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -107,7 +107,7 @@ fn set_camera_viewports( let mut right_camera = right_camera.single_mut(); right_camera.viewport = Some(Viewport { - // ... or specify a percentage of the render target. + // ... or specify an adaptive percentage of the render target. physical_position: AbsOrPercVec::Percentage(Vec2::new(0.5, 0.0)), physical_size: AbsOrPercVec::Percentage(Vec2::new(0.5, 1.0)), ..default() From 868b4033b5ae04b71690ff4e3e88c2d1067c3abe Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sat, 9 Jul 2022 13:35:22 +0200 Subject: [PATCH 6/7] Use separate enums for position and size --- crates/bevy_render/src/camera/camera.rs | 61 +++++++++++++++++++------ examples/3d/split_screen.rs | 10 ++-- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 3f8134f3ac54a..985837678523a 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -36,48 +36,81 @@ use wgpu::Extent3d; pub struct Viewport { /// The physical position to render this viewport to within the [`RenderTarget`] of this [`Camera`]. /// (0,0) corresponds to the top-left corner - pub physical_position: AbsOrPercVec, + pub physical_position: ViewportPosition, /// The physical size of the viewport rectangle to render to within the [`RenderTarget`] of this [`Camera`]. /// The origin of the rectangle is in the top-left corner. - pub physical_size: AbsOrPercVec, + pub physical_size: ViewportSize, /// The minimum and maximum depth to render (on a scale from 0.0 to 1.0). pub depth: Range, } -/// A vector wrapper that represents either absolute value or value as a percentage. -/// -/// For example, used in representing viewport position, either as absolute pixel value, or as a -/// percentage of render target size. +/// An enum that represents viewport's top-left corner position either as absolute value (in pixels) +/// or as a percentage of camera's render target's size. +/// (0,0) corresponds to the top-left corner #[derive(Reflect, Debug, Clone, Serialize, Deserialize)] -pub enum AbsOrPercVec { +pub enum ViewportPosition { Absolute(UVec2), Percentage(Vec2), } -impl Default for AbsOrPercVec { +impl Default for ViewportPosition { fn default() -> Self { Self::Absolute(Default::default()) } } -impl AbsOrPercVec { +impl ViewportPosition { /// Converts this size into a absolute size in pixels. #[inline] pub fn as_absolute(&self, of: UVec2) -> UVec2 { match self { - AbsOrPercVec::Absolute(v) => *v, - AbsOrPercVec::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), + ViewportPosition::Absolute(v) => *v, + ViewportPosition::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), } } - /// Converts this size into a absolute size in pixels if possible. + /// Converts this size into a absolute size in pixels if possible. + /// + /// Returns `None` if self is `Percentage` and argument is `None` + #[inline] + pub fn try_as_absolute(&self, of_opt: Option) -> Option { + match self { + ViewportPosition::Absolute(v) => Some(*v), + ViewportPosition::Percentage(_) => of_opt.map(|of| self.as_absolute(of)), + } + } +} +/// An enum that represents viewport's size either as absolute value (in pixels) or as a percentage of camera's render target's size. +#[derive(Reflect, Debug, Clone, Serialize, Deserialize)] +pub enum ViewportSize { + Absolute(UVec2), + Percentage(Vec2), +} + +impl Default for ViewportSize { + fn default() -> Self { + Self::Absolute(Default::default()) + } +} + +impl ViewportSize { + /// Converts this size into a absolute size in pixels. + #[inline] + pub fn as_absolute(&self, of: UVec2) -> UVec2 { + match self { + ViewportSize::Absolute(v) => *v, + ViewportSize::Percentage(v) => (of.as_vec2() * *v).as_uvec2(), + } + } + /// Converts this size into a absolute size in pixels if possible. + /// /// Returns `None` if self is `Percentage` and argument is `None` #[inline] pub fn try_as_absolute(&self, of_opt: Option) -> Option { match self { - AbsOrPercVec::Absolute(v) => Some(*v), - AbsOrPercVec::Percentage(_) => of_opt.map(|of| self.as_absolute(of)), + ViewportSize::Absolute(v) => Some(*v), + ViewportSize::Percentage(_) => of_opt.map(|of| self.as_absolute(of)), } } } diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 9bd36343ef699..ab9f7a3b37e7d 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -3,7 +3,7 @@ use bevy::{ core_pipeline::clear_color::ClearColorConfig, prelude::*, - render::camera::{AbsOrPercVec, Viewport}, + render::camera::{Viewport, ViewportPosition, ViewportSize}, window::{WindowId, WindowResized}, }; @@ -97,8 +97,8 @@ fn set_camera_viewports( let mut left_camera = left_camera.single_mut(); left_camera.viewport = Some(Viewport { // You can use absolute pixel values... - physical_position: AbsOrPercVec::Absolute(UVec2::ZERO), - physical_size: AbsOrPercVec::Absolute(UVec2::new( + physical_position: ViewportPosition::Absolute(UVec2::ZERO), + physical_size: ViewportSize::Absolute(UVec2::new( window.physical_width() / 2, window.physical_height(), )), @@ -108,8 +108,8 @@ fn set_camera_viewports( let mut right_camera = right_camera.single_mut(); right_camera.viewport = Some(Viewport { // ... or specify an adaptive percentage of the render target. - physical_position: AbsOrPercVec::Percentage(Vec2::new(0.5, 0.0)), - physical_size: AbsOrPercVec::Percentage(Vec2::new(0.5, 1.0)), + physical_position: ViewportPosition::Percentage(Vec2::new(0.5, 0.0)), + physical_size: ViewportSize::Percentage(Vec2::new(0.5, 1.0)), ..default() }); } From 249364848a14204bc23e41a6910f65f968feb7d7 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Wed, 13 Jul 2022 19:13:15 +0200 Subject: [PATCH 7/7] Take just physical_target_size instead of ExtractedCamera --- .../src/core_2d/main_pass_2d_node.rs | 2 +- .../src/core_3d/main_pass_3d_node.rs | 6 +++--- crates/bevy_render/src/render_phase/draw_state.rs | 12 ++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index cc9d5d69a047d..a1636b8d02973 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -86,7 +86,7 @@ impl Node for MainPass2dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport, camera); + tracked_pass.set_camera_viewport(viewport, camera.physical_target_size); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index 29d6e9def3281..dbe0e7975696c 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -103,7 +103,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport, camera); + tracked_pass.set_camera_viewport(viewport, camera.physical_target_size); } for item in &opaque_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -142,7 +142,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport, camera); + tracked_pass.set_camera_viewport(viewport, camera.physical_target_size); } for item in &alpha_mask_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); @@ -186,7 +186,7 @@ impl Node for MainPass3dNode { let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); if let Some(viewport) = camera.viewport.as_ref() { - tracked_pass.set_camera_viewport(viewport, camera); + tracked_pass.set_camera_viewport(viewport, camera.physical_target_size); } for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); diff --git a/crates/bevy_render/src/render_phase/draw_state.rs b/crates/bevy_render/src/render_phase/draw_state.rs index 26faaa32bbde9..2f6cdc5d90d54 100644 --- a/crates/bevy_render/src/render_phase/draw_state.rs +++ b/crates/bevy_render/src/render_phase/draw_state.rs @@ -1,5 +1,5 @@ use crate::{ - camera::{ExtractedCamera, Viewport}, + camera::Viewport, prelude::Color, render_resource::{ BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId, @@ -340,15 +340,19 @@ impl<'a> TrackedRenderPass<'a> { /// Set the rendering viewport to the given [`Camera`](crate::camera::Viewport) [`Viewport`]. /// /// Subsequent draw calls will be projected into that viewport. - pub fn set_camera_viewport(&mut self, viewport: &Viewport, camera: &ExtractedCamera) { + pub fn set_camera_viewport( + &mut self, + viewport: &Viewport, + physical_target_size: Option, + ) { let physical_size = viewport .physical_size - .try_as_absolute(camera.physical_target_size) + .try_as_absolute(physical_target_size) .expect("Couldn't get camera.physical_target_size (needed for relative viewport size)"); let physical_position = viewport .physical_position - .try_as_absolute(camera.physical_target_size) + .try_as_absolute(physical_target_size) .expect( "Couldn't get camera.physical_target_size (needed for relative viewport position)", );