From ae0ccfb4f6d41d2033be03b48e2538d1cf0cd86f Mon Sep 17 00:00:00 2001 From: Daniikk1012 Date: Tue, 31 May 2022 17:14:12 +0000 Subject: [PATCH] Make ScalingMode more flexible (#3253) Adds ability to specify scaling factor for `WindowSize`, size of the fixed axis for `FixedVertical` and `FixedHorizontal` and a new `ScalingMode` that is a mix of `FixedVertical` and `FixedHorizontal` # The issue Currently, only available options are to: * Have one of the axes fixed to value 1 * Have viewport size match the window size * Manually adjust viewport size In most of the games these options are not enough and more advanced scaling methods have to be used ## Solution The solution is to provide additional parameters to current scaling modes, like scaling factor for `WindowSize`. Additionally, a more advanced `Auto` mode is added, which dynamically switches between behaving like `FixedVertical` and `FixedHorizontal` depending on the window's aspect ratio. Co-authored-by: Daniikk1012 <49123959+Daniikk1012@users.noreply.github.com> --- crates/bevy_gltf/src/loader.rs | 4 +- crates/bevy_render/src/camera/bundle.rs | 2 +- crates/bevy_render/src/camera/projection.rs | 86 +++++++++++---------- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 35025aded6246..3b317778c9761 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -716,8 +716,8 @@ fn load_node( let orthographic_projection: OrthographicProjection = OrthographicProjection { far: orthographic.zfar(), near: orthographic.znear(), - scaling_mode: ScalingMode::FixedHorizontal, - scale: xmag / 2.0, + scaling_mode: ScalingMode::FixedHorizontal(1.0), + scale: xmag, ..Default::default() }; diff --git a/crates/bevy_render/src/camera/bundle.rs b/crates/bevy_render/src/camera/bundle.rs index 39bda7802bbd8..ee2363fd7e6bf 100644 --- a/crates/bevy_render/src/camera/bundle.rs +++ b/crates/bevy_render/src/camera/bundle.rs @@ -83,7 +83,7 @@ pub struct OrthographicCameraBundle { impl OrthographicCameraBundle { pub fn new_3d() -> Self { let orthographic_projection = OrthographicProjection { - scaling_mode: ScalingMode::FixedVertical, + scaling_mode: ScalingMode::FixedVertical(2.0), depth_calculation: DepthCalculation::Distance, ..Default::default() }; diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index 3c2a7d745da21..86fba6a3d4b3a 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -66,10 +66,15 @@ pub enum ScalingMode { None, /// Match the window size. 1 world unit = 1 pixel. WindowSize, + /// Use minimal possible viewport size while keeping the aspect ratio. + /// Arguments are in world units. + Auto { min_width: f32, min_height: f32 }, /// Keep vertical axis constant; resize horizontal with aspect ratio. - FixedVertical, + /// The argument is the desired height of the viewport in world units. + FixedVertical(f32), /// Keep horizontal axis constant; resize vertical with aspect ratio. - FixedHorizontal, + /// The argument is the desired width of the viewport in world units. + FixedHorizontal(f32), } #[derive(Component, Debug, Clone, Reflect)] @@ -102,52 +107,51 @@ impl CameraProjection for OrthographicProjection { } fn update(&mut self, width: f32, height: f32) { - match (&self.scaling_mode, &self.window_origin) { - (ScalingMode::WindowSize, WindowOrigin::Center) => { - let half_width = (width / 2.0).round(); - let half_height = (height / 2.0).round(); - // Assign left and bottom such that (right-left)==width and (top-bottom)==height - // still hold with with rounded half_width and half_height - self.left = half_width - width; - self.right = half_width; - self.top = half_height; - self.bottom = half_height - height; + let (viewport_width, viewport_height) = match self.scaling_mode { + ScalingMode::WindowSize => (width, height), + ScalingMode::Auto { + min_width, + min_height, + } => { + if width * min_height > min_width * height { + (width * min_height / height, min_height) + } else { + (min_width, height * min_width / width) + } } - (ScalingMode::WindowSize, WindowOrigin::BottomLeft) => { - self.left = 0.0; - self.right = width; - self.top = height; - self.bottom = 0.0; + ScalingMode::FixedVertical(viewport_height) => { + (width * viewport_height / height, viewport_height) } - (ScalingMode::FixedVertical, WindowOrigin::Center) => { - let aspect_ratio = width / height; - self.left = -aspect_ratio; - self.right = aspect_ratio; - self.top = 1.0; - self.bottom = -1.0; + ScalingMode::FixedHorizontal(viewport_width) => { + (viewport_width, height * viewport_width / width) } - (ScalingMode::FixedVertical, WindowOrigin::BottomLeft) => { - let aspect_ratio = width / height; - self.left = 0.0; - self.right = aspect_ratio; - self.top = 1.0; - self.bottom = 0.0; - } - (ScalingMode::FixedHorizontal, WindowOrigin::Center) => { - let aspect_ratio = height / width; - self.left = -1.0; - self.right = 1.0; - self.top = aspect_ratio; - self.bottom = -aspect_ratio; + ScalingMode::None => return, + }; + + match self.window_origin { + WindowOrigin::Center => { + let half_width = viewport_width / 2.0; + let half_height = viewport_height / 2.0; + self.left = -half_width; + self.bottom = -half_height; + self.right = half_width; + self.top = half_height; + + if let ScalingMode::WindowSize = self.scaling_mode { + if self.scale == 1.0 { + self.left = self.left.floor(); + self.bottom = self.bottom.floor(); + self.right = self.right.floor(); + self.top = self.top.floor(); + } + } } - (ScalingMode::FixedHorizontal, WindowOrigin::BottomLeft) => { - let aspect_ratio = height / width; + WindowOrigin::BottomLeft => { self.left = 0.0; - self.right = 1.0; - self.top = aspect_ratio; self.bottom = 0.0; + self.right = viewport_width; + self.top = viewport_height; } - (ScalingMode::None, _) => {} } }