render: add more option for scaling modes of orthographic projection

This commit is contained in:
SeanOMik 2024-11-15 23:01:35 -05:00
parent 95b01613fe
commit 503ea5b450
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
4 changed files with 179 additions and 10 deletions

View File

@ -1,10 +1,40 @@
use glam::{Mat4, Vec2}; use glam::{Mat4, Vec2};
use lyra_ecs::{Bundle, Component}; use lyra_ecs::{Bundle, Component};
use lyra_math::Angle; use lyra_math::{Angle, Rect};
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
/// The method of scaling for an [`OrthographicProjection`].
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub enum ScaleMode {
/// No scaling, keep everything the same size as the viewport.
Viewport,
/// The width of the projection in world units.
///
/// Height will be set based off of the viewport's aspect ratio.
Width(f32),
/// The height of the projection in world units.
///
/// Width will be set based off of the viewport's aspect ratio.
Height(f32),
/// The exact size of the projection in world units.
///
/// Ignoring viewport size, likely causes stretching.
Size(Vec2),
/// Keep the projection size below a maximum value in world units, while keeping the aspect ratio.
MaxSize(Vec2),
/// Keep the projection size above a minimum value in world units, while keeping the aspect ratio.
MinSize(Vec2),
}
impl Default for ScaleMode {
fn default() -> Self {
ScaleMode::Viewport
}
}
#[derive(Debug, Clone, Copy, PartialEq, Reflect)] #[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub struct OrthographicProjection { pub struct OrthographicProjection {
pub scale_mode: ScaleMode,
pub scale: f32, pub scale: f32,
pub znear: f32, pub znear: f32,
pub zfar: f32, pub zfar: f32,
@ -13,6 +43,7 @@ pub struct OrthographicProjection {
impl Default for OrthographicProjection { impl Default for OrthographicProjection {
fn default() -> Self { fn default() -> Self {
Self { Self {
scale_mode: Default::default(),
scale: 1.0, scale: 1.0,
znear: 0.0, znear: 0.0,
zfar: 1000.0, zfar: 1000.0,
@ -21,15 +52,63 @@ impl Default for OrthographicProjection {
} }
impl OrthographicProjection { impl OrthographicProjection {
fn get_rect(&self, viewport_size: Vec2) -> Rect {
let origin;
let size;
match self.scale_mode {
ScaleMode::Viewport => {
origin = viewport_size * 0.5;
size = viewport_size;
},
ScaleMode::Width(width) => {
let aspect = viewport_size.x / viewport_size.y;
let origin_x = width * 0.5;
let scaled_height = width / aspect;
let origin_y = scaled_height * 0.5;
origin = Vec2::new(origin_x, origin_y);
size = Vec2::new(width, scaled_height);
},
ScaleMode::Height(height) => {
let aspect = viewport_size.x / viewport_size.y;
let origin_y = height * 0.5;
let scaled_width = height * aspect;
let origin_x = scaled_width * 0.5;
origin = Vec2::new(origin_x, origin_y);
size = Vec2::new(scaled_width, height);
},
ScaleMode::Size(s) => {
origin = s * 0.5;
size = s;
},
ScaleMode::MaxSize(s) => {
let clamped = s.min(viewport_size);
origin = clamped * 0.5;
size = clamped;
},
ScaleMode::MinSize(s) => {
let clamped = s.max(viewport_size);
origin = clamped * 0.5;
size = clamped;
}
}
Rect::new(self.scale * -origin.x,
self.scale * -origin.y,
self.scale * size.x - origin.x,
self.scale * size.y - origin.y)
}
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 { pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
let origin_x = viewport_size.x as f32 * 0.5; let rect = self.get_rect(viewport_size);
let origin_y = viewport_size.y as f32 * 0.5;
glam::Mat4::orthographic_rh( glam::Mat4::orthographic_rh(
self.scale * -origin_x, rect.min.x,
self.scale * viewport_size.x as f32 - origin_x, rect.max.x,
self.scale * -origin_y, rect.min.y,
self.scale * viewport_size.y as f32 - origin_y, rect.max.y,
-1000.0, -1000.0,
1000.0, 1000.0,
) )

View File

@ -7,6 +7,9 @@ pub use angle::*;
mod area; mod area;
pub use area::*; pub use area::*;
mod rect;
pub use rect::*;
pub mod transform; pub mod transform;
pub use transform::*; pub use transform::*;

View File

@ -0,0 +1,83 @@
use glam::Vec2;
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Rect {
pub min: Vec2,
pub max: Vec2,
}
impl Rect {
pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
Self {
min: Vec2::new(x1, y1),
max: Vec2::new(x2, y2),
}
}
pub fn from_vec(min: Vec2, max: Vec2) -> Self {
Self {
min,
max
}
}
}
impl std::ops::Add for Rect {
type Output = Rect;
fn add(self, rhs: Self) -> Self::Output {
Rect::from_vec(self.min + rhs.min, self.max + rhs.max)
}
}
impl std::ops::AddAssign for Rect {
fn add_assign(&mut self, rhs: Self) {
self.min += rhs.min;
self.max += rhs.max;
}
}
impl std::ops::Sub for Rect {
type Output = Rect;
fn sub(self, rhs: Self) -> Self::Output {
Rect::from_vec(self.min - rhs.min, self.max - rhs.max)
}
}
impl std::ops::SubAssign for Rect {
fn sub_assign(&mut self, rhs: Self) {
self.min -= rhs.min;
self.max -= rhs.max;
}
}
impl std::ops::Mul for Rect {
type Output = Rect;
fn mul(self, rhs: Self) -> Self::Output {
Rect::from_vec(self.min * rhs.min, self.max * rhs.max)
}
}
impl std::ops::MulAssign for Rect {
fn mul_assign(&mut self, rhs: Self) {
self.min *= rhs.min;
self.max *= rhs.max;
}
}
impl std::ops::Div for Rect {
type Output = Rect;
fn div(self, rhs: Self) -> Self::Output {
Rect::from_vec(self.min / rhs.min, self.max / rhs.max)
}
}
impl std::ops::DivAssign for Rect {
fn div_assign(&mut self, rhs: Self) {
self.min /= rhs.min;
self.max /= rhs.max;
}
}

View File

@ -9,7 +9,7 @@ use lyra_engine::{
math::{self, Transform, Vec3}, math::{self, Transform, Vec3},
render::light::directional::DirectionalLight, render::light::directional::DirectionalLight,
scene::{ scene::{
system_update_world_transforms, Camera2dBundle, CameraProjection, FreeFlyCamera, FreeFlyCameraPlugin, OrthographicProjection, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN system_update_world_transforms, Camera2dBundle, CameraProjection, FreeFlyCamera, FreeFlyCameraPlugin, OrthographicProjection, ScaleMode, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN
}, },
sprite::{self, Sprite}, sprite::{self, Sprite},
}; };
@ -162,12 +162,16 @@ fn setup_scene_plugin(app: &mut App) {
world.spawn(( world.spawn((
Camera2dBundle { Camera2dBundle {
projection: CameraProjection::Orthographic(OrthographicProjection { projection: CameraProjection::Orthographic(OrthographicProjection {
scale: 0.75, //scale_mode: ScaleMode::Width(960.0),
scale_mode: ScaleMode::Height(180.0),
//scale_mode: ScaleMode::Width(320.0),
//scale_mode: ScaleMode::Size(lyra_engine::math::Vec2::new(320.0, 180.0)),
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
}, },
Transform::from_xyz(200.0, 120.0, 0.0), //Transform::from_xyz(200.0, 120.0, 0.0),
Transform::from_xyz(0.0, 0.0, 0.0),
FreeFlyCamera::default(), FreeFlyCamera::default(),
)); ));
} }