render: fix 2d camera

This commit is contained in:
SeanOMik 2024-11-02 21:46:21 -04:00
parent 315924f920
commit 617c4d69e8
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
7 changed files with 214 additions and 172 deletions

View File

@ -1,38 +1,85 @@
use glam::{Mat4, Vec2};
use lyra_math::Transform;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use winit::dpi::PhysicalSize;
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, scene::CameraComponent}; use crate::math::Angle;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)] #[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub enum CameraProjectionMode { pub struct OrthographicProjection {
/// 3d camera projection pub scale: f32,
Perspective, pub znear: f32,
/// 2d camera projection pub zfar: f32,
Orthographic,
} }
#[derive(Debug, Clone)] impl Default for OrthographicProjection {
pub struct Projection { fn default() -> Self {
aspect: f32,
znear: f32,
zfar: f32,
}
impl Projection {
pub fn new(width: u32, height: u32, znear: f32, zfar: f32) -> Self {
Self { Self {
aspect: width as f32 / height as f32, scale: 1.0,
znear, znear: 0.0,
zfar, zfar: 1000.0,
} }
} }
}
pub fn resize(&mut self, width: u32, height: u32) { impl OrthographicProjection {
self.aspect = width as f32 / height as f32; pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
let origin_x = viewport_size.x as f32 * 0.5;
let origin_y = viewport_size.y as f32 * 0.5;
glam::Mat4::orthographic_rh(
self.scale * -origin_x,
self.scale * viewport_size.x as f32 - origin_x,
self.scale * -origin_y,
self.scale * viewport_size.y as f32 - origin_y,
-1000.0,
1000.0,
)
} }
}
pub fn calc_matrix(&self, fov: Angle, _mode: CameraProjectionMode) -> glam::Mat4 { #[derive(Debug, Clone, Copy, PartialEq, Reflect)]
OPENGL_TO_WGPU_MATRIX * glam::Mat4::perspective_rh_gl(fov.to_radians(), self.aspect, self.znear, self.zfar) pub struct PerspectiveProjection {
pub fov: Angle,
pub znear: f32,
pub zfar: f32,
}
impl Default for PerspectiveProjection {
fn default() -> Self {
Self {
fov: Angle::Degrees(45.0),
znear: 0.0,
zfar: 100.0,
}
}
}
impl PerspectiveProjection {
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
let aspect = viewport_size.x / viewport_size.y;
glam::Mat4::perspective_rh(
self.fov.to_radians(),
aspect,
self.znear,
self.zfar,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub enum CameraProjectionMode {
/// 3d camera projection
Perspective(PerspectiveProjection),
/// 2d camera projection
Orthographic(OrthographicProjection),
}
impl CameraProjectionMode {
pub fn to_mat4(&self, viewport_size: Vec2) -> Mat4 {
match self {
CameraProjectionMode::Perspective(proj) => proj.to_mat(viewport_size),
CameraProjectionMode::Orthographic(proj) => proj.to_mat(viewport_size),
}
} }
} }
@ -48,9 +95,8 @@ pub struct CameraUniform {
pub projection: glam::Mat4, pub projection: glam::Mat4,
/// The position of the camera /// The position of the camera
pub position: glam::Vec3, pub position: glam::Vec3,
pub tile_debug: u32, _padding: u32,
//_padding: [u8; 3], //_padding: [u8; 3],
} }
impl Default for CameraUniform { impl Default for CameraUniform {
@ -61,106 +107,45 @@ impl Default for CameraUniform {
view_projection: glam::Mat4::IDENTITY, view_projection: glam::Mat4::IDENTITY,
projection: glam::Mat4::IDENTITY, projection: glam::Mat4::IDENTITY,
position: Default::default(), position: Default::default(),
tile_debug: 0, _padding: 0,
//_padding: 0,
} }
} }
} }
impl CameraUniform { impl CameraUniform {
pub fn new(view: glam::Mat4, inverse_projection: glam::Mat4, view_projection: glam::Mat4, projection: glam::Mat4, position: glam::Vec3) -> Self { pub fn new(
view: glam::Mat4,
inverse_projection: glam::Mat4,
view_projection: glam::Mat4,
projection: glam::Mat4,
position: glam::Vec3,
) -> Self {
Self { Self {
view, view,
inverse_projection, inverse_projection,
view_projection, view_projection,
projection, projection,
position, position,
tile_debug: 0 _padding: 0,
} }
} }
}
#[derive(Debug, Clone)] pub fn from_component(transform: Transform, projection: CameraProjectionMode, viewport_size: Vec2) -> Self {
pub struct RenderCamera { let position = transform.translation;
view_proj: glam::Mat4, let forward = transform.forward();
let up = transform.up();
let view = glam::Mat4::look_to_rh(position, forward, up);
#[allow(dead_code)] let projection = projection.to_mat4(viewport_size);
size: PhysicalSize<u32>, let view_projection = projection * view;
aspect: f32,
znear: f32,
zfar: f32,
}
impl RenderCamera {
pub fn new(size: PhysicalSize<u32>) -> Self {
Self { Self {
view_proj: glam::Mat4::IDENTITY, view,
inverse_projection: projection.inverse(),
size, view_projection,
aspect: size.width as f32 / size.height as f32, projection,
znear: 0.1,
zfar: 100.0,
}
}
pub fn update_aspect_ratio(&mut self, size: PhysicalSize<u32>) {
self.aspect = size.width as f32 / size.height as f32;
}
/// Calculates the view projection, and the view
///
/// Returns: A tuple with the view projection as the first element, and the
/// view matrix as the second.
pub fn calc_view_projection(&mut self, camera: &CameraComponent) -> CameraUniform {
let position = camera.transform.translation;
let forward = camera.transform.forward();
let up = camera.transform.up();
let view = glam::Mat4::look_to_rh(
position, position,
forward, _padding: 0,
up
);
match camera.mode {
CameraProjectionMode::Perspective => {
let proj = glam::Mat4::perspective_rh_gl(camera.fov.to_radians(), self.aspect, self.znear, self.zfar);
self.view_proj = OPENGL_TO_WGPU_MATRIX * proj * view;
//(&self.view_proj, view)
CameraUniform {
view,
inverse_projection: proj.inverse(),
view_projection: self.view_proj,
projection: proj,
position,
tile_debug: camera.debug as u32,
}
},
CameraProjectionMode::Orthographic => {
let target = camera.transform.rotation * glam::Vec3::new(0.0, 0.0, -1.0);
let target = target.normalize();
let ratio_size_per_depth = ((camera.fov.to_radians() / 2.0) * 2.0).atan();
let distance = (target - position).length();
let size_y = ratio_size_per_depth * distance;
let size_x = ratio_size_per_depth * distance * self.aspect;
let proj = glam::Mat4::orthographic_rh_gl(-size_x, size_x, -size_y, size_y, self.znear, self.zfar);
self.view_proj = OPENGL_TO_WGPU_MATRIX * proj;
CameraUniform {
view,
inverse_projection: proj.inverse(),
view_projection: self.view_proj,
projection: proj,
position,
tile_debug: camera.debug as u32,
}
},
} }
} }
} }

View File

@ -2,12 +2,12 @@ use std::sync::Arc;
use glam::UVec2; use glam::UVec2;
use lyra_game_derive::RenderGraphLabel; use lyra_game_derive::RenderGraphLabel;
use lyra_math::Transform;
use tracing::warn; use tracing::warn;
use winit::dpi::PhysicalSize;
use crate::{ use crate::{
render::{ render::{
camera::{CameraUniform, RenderCamera}, camera::CameraUniform,
graph::{ graph::{
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
}, },
@ -114,13 +114,9 @@ impl Node for BasePass {
} }
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
if let Some(camera) = world.view_iter::<&mut CameraComponent>().next() { if let Some((camera, transform)) = world.view_iter::<(&CameraComponent, &Transform)>().next() {
let screen_size = graph.view_target().size(); let screen_size = graph.view_target().size();
let uniform = CameraUniform::from_component(*transform, camera.projection, screen_size.as_vec2());
let mut render_cam =
RenderCamera::new(PhysicalSize::new(screen_size.x, screen_size.y));
let uniform = render_cam.calc_view_projection(&camera);
context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform) context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform)
} else { } else {
warn!("Missing camera!"); warn!("Missing camera!");

View File

@ -1,23 +1,18 @@
use lyra_ecs::Component; use lyra_ecs::Component;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode}; use crate::render::camera::{CameraProjectionMode, OrthographicProjection, PerspectiveProjection};
#[derive(Clone, Component, Reflect)] #[derive(Clone, Component, Reflect)]
pub struct CameraComponent { pub struct CameraComponent {
pub transform: Transform, pub projection: CameraProjectionMode,
pub fov: Angle, //pub debug: bool,
pub mode: CameraProjectionMode,
pub debug: bool,
} }
impl Default for CameraComponent { impl Default for CameraComponent {
fn default() -> Self { fn default() -> Self {
Self { Self {
transform: Transform::default(), projection: CameraProjectionMode::Perspective(PerspectiveProjection::default()),
fov: Angle::Degrees(45.0),
mode: CameraProjectionMode::Perspective,
debug: false,
} }
} }
} }
@ -29,7 +24,7 @@ impl CameraComponent {
pub fn new_2d() -> Self { pub fn new_2d() -> Self {
CameraComponent { CameraComponent {
mode: CameraProjectionMode::Orthographic, projection: CameraProjectionMode::Orthographic(OrthographicProjection::default()),
..Default::default() ..Default::default()
} }
} }

View File

@ -1,11 +1,10 @@
use glam::{EulerRot, Quat, Vec3}; use glam::{EulerRot, Quat, Vec3};
use lyra_ecs::{query::{Res, View}, Component}; use lyra_ecs::{query::{Res, View}, Component};
use lyra_math::Transform;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{game::App, input::ActionHandler, plugin::Plugin, DeltaTime}; use crate::{game::App, input::ActionHandler, plugin::Plugin, DeltaTime};
use super::CameraComponent;
pub const ACTLBL_MOVE_UP_DOWN: &str = "MoveUpDown"; pub const ACTLBL_MOVE_UP_DOWN: &str = "MoveUpDown";
pub const ACTLBL_MOVE_LEFT_RIGHT: &str = "MoveLeftRight"; pub const ACTLBL_MOVE_LEFT_RIGHT: &str = "MoveLeftRight";
pub const ACTLBL_MOVE_FORWARD_BACKWARD: &str = "MoveForwardBackward"; pub const ACTLBL_MOVE_FORWARD_BACKWARD: &str = "MoveForwardBackward";
@ -40,11 +39,11 @@ impl FreeFlyCamera {
} }
} }
pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<ActionHandler>, view: View<(&mut CameraComponent, &FreeFlyCamera)>) -> anyhow::Result<()> { pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<ActionHandler>, view: View<(&mut Transform, &FreeFlyCamera)>) -> anyhow::Result<()> {
let delta_time = **delta_time; let delta_time = **delta_time;
for (mut cam, fly) in view.into_iter() { for (mut transform, fly) in view.into_iter() {
let forward = cam.transform.forward(); let forward = transform.forward();
let left = cam.transform.left(); let left = transform.left();
let up = Vec3::Y; let up = Vec3::Y;
let move_y = handler.get_axis_modifier(ACTLBL_MOVE_UP_DOWN).unwrap_or(0.0); let move_y = handler.get_axis_modifier(ACTLBL_MOVE_UP_DOWN).unwrap_or(0.0);
@ -57,7 +56,7 @@ pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<Actio
velocity += move_z * forward; velocity += move_z * forward;
if velocity != Vec3::ZERO { if velocity != Vec3::ZERO {
cam.transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up
} }
let motion_x = handler.get_axis_modifier(ACTLBL_LOOK_LEFT_RIGHT).unwrap_or(0.0); let motion_x = handler.get_axis_modifier(ACTLBL_LOOK_LEFT_RIGHT).unwrap_or(0.0);
@ -72,13 +71,13 @@ pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<Actio
if camera_rot != Vec3::ZERO { if camera_rot != Vec3::ZERO {
let look_velocity = camera_rot * fly.look_speed * delta_time; let look_velocity = camera_rot * fly.look_speed * delta_time;
let (mut y, mut x, _) = cam.transform.rotation.to_euler(EulerRot::YXZ); let (mut y, mut x, _) = transform.rotation.to_euler(EulerRot::YXZ);
x += look_velocity.x; x += look_velocity.x;
y += look_velocity.y; y += look_velocity.y;
x = x.clamp(-1.54, 1.54); x = x.clamp(-1.54, 1.54);
// rotation is not commutative, keep this order to avoid unintended roll // rotation is not commutative, keep this order to avoid unintended roll
cam.transform.rotation = Quat::from_axis_angle(Vec3::Y, y) transform.rotation = Quat::from_axis_angle(Vec3::Y, y)
* Quat::from_axis_angle(Vec3::X, x); * Quat::from_axis_angle(Vec3::X, x);
} }
} }

View File

@ -10,7 +10,7 @@ pub fn radians_to_degrees(radians: f32) -> f32 {
radians * 180.0 / PI radians * 180.0 / PI
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Angle { pub enum Angle {
Degrees(f32), Degrees(f32),
Radians(f32), Radians(f32),

View File

@ -1,19 +1,66 @@
use crate::{ use crate::{
lua::{ lua::{
wrappers::{LuaAngle, LuaTransform}, wrappers::LuaAngle,
LuaWrapper LuaWrapper,
}, },
ScriptBorrow, ScriptBorrow,
}; };
use lyra_game::{render::camera::CameraProjectionMode, scene::CameraComponent}; use lyra_game::{
use lyra_reflect::Enum; render::camera::{CameraProjectionMode, OrthographicProjection, PerspectiveProjection},
scene::CameraComponent,
};
use lyra_scripting_derive::to_lua_convert; use lyra_scripting_derive::to_lua_convert;
fn projection_mode_from_str(s: &str) -> Option<CameraProjectionMode> { fn projection_as_table(lua: &mlua::Lua, projection: CameraProjectionMode) -> mlua::Result<mlua::Table> {
match s { let table = lua.create_table()?;
"perspective" => Some(CameraProjectionMode::Perspective),
"orthographic" => Some(CameraProjectionMode::Orthographic), match projection {
_ => None, CameraProjectionMode::Perspective(proj) => {
table.set("type", "perspective")?;
table.set("fov", LuaAngle(proj.fov))?;
table.set("znear", proj.znear)?;
table.set("zfar", proj.zfar)?;
},
CameraProjectionMode::Orthographic(proj) => {
table.set("type", "orthographic")?;
table.set("scale", proj.scale)?;
table.set("znear", proj.znear)?;
table.set("zfar", proj.zfar)?;
},
}
Ok(table)
}
fn projection_from_table(t: &mlua::Table) -> mlua::Result<CameraProjectionMode> {
let ty: String = t.get("type")?;
let ty = ty.as_str();
let znear: f32 = t.get("znear")?;
let zfar: f32 = t.get("zfar")?;
match ty {
"perspective" => {
let fov: LuaAngle = t.get("fov")?;
Ok(CameraProjectionMode::Perspective(PerspectiveProjection {
fov: fov.0,
znear,
zfar,
}))
}
"orthographic" => {
let scale: f32 = t.get("scale")?;
Ok(CameraProjectionMode::Orthographic(OrthographicProjection {
scale,
znear,
zfar,
}))
}
_ => Err(mlua::Error::FromLuaConversionError {
from: "Table",
to: "CameraProjection".into(),
message: Some(format!("unknown projection type: '{}'", ty)),
}),
} }
} }
@ -27,20 +74,16 @@ to_lua_convert!(
// Reflection type, can be 'component' or 'resource' // Reflection type, can be 'component' or 'resource'
reflect=component, reflect=component,
fields={ fields={
transform: wrap(LuaTransform),
fov: wrap(LuaAngle),
( (
mode, projection,
// Get the table from the value, result must be `CameraProjectionMode`
get={ get={
let mode: String = table.get("mode")?; let p: mlua::Table = table.get("projection")?;
projection_mode_from_str(&mode).unwrap() projection_from_table(&p)?
}, },
// Get the value from self, result must be the type in Lua, here its `String`.
set={ set={
self.mode.variant_name().to_lowercase() projection_as_table(lua, self.projection)?
} },
), )
debug: bool
} }
); );

View File

@ -1,10 +1,24 @@
use lyra_engine::{ use lyra_engine::{
assets::{Image, ResourceManager, Texture}, ecs::query::View, game::App, gltf::Gltf, input::{ assets::{Image, ResourceManager, Texture},
ecs::query::View,
game::App,
gltf::Gltf,
input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, InputActionPlugin, KeyCode, LayoutId,
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{ },
system_update_world_transforms, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN math::{self, Transform, Vec3},
}, sprite::{self, Sprite}, winit::WindowOptions render::{
camera::{CameraProjectionMode, OrthographicProjection},
light::directional::DirectionalLight,
},
scene::{
system_update_world_transforms, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin,
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},
winit::WindowOptions,
}; };
#[async_std::main] #[async_std::main]
@ -84,7 +98,11 @@ async fn main() {
.with_plugin(action_handler_plugin) .with_plugin(action_handler_plugin)
//.with_plugin(camera_debug_plugin) //.with_plugin(camera_debug_plugin)
.with_plugin(FreeFlyCameraPlugin) .with_plugin(FreeFlyCameraPlugin)
.with_system("system_update_world_transforms", system_update_world_transforms, &[]); .with_system(
"system_update_world_transforms",
system_update_world_transforms,
&[],
);
a.run(); a.run();
} }
@ -113,8 +131,7 @@ fn setup_scene_plugin(app: &mut App) {
cube_gltf.wait_recurse_dependencies_load(); cube_gltf.wait_recurse_dependencies_load();
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0]; let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
let image = resman.request::<Image>("../assets/Egg_item.png") let image = resman.request::<Image>("../assets/Egg_item.png").unwrap();
.unwrap();
image.wait_recurse_dependencies_load(); image.wait_recurse_dependencies_load();
drop(resman); drop(resman);
@ -131,7 +148,7 @@ fn setup_scene_plugin(app: &mut App) {
pivot: sprite::Pivot::Center, pivot: sprite::Pivot::Center,
}, },
WorldTransform::default(), WorldTransform::default(),
Transform::from_xyz(0.0, 0.0, -20.0), Transform::from_xyz(0.0, 0.0, -10.0),
)); ));
{ {
@ -149,7 +166,14 @@ fn setup_scene_plugin(app: &mut App) {
)); ));
} }
let mut camera = CameraComponent::new_3d(); world.spawn((
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5); CameraComponent {
world.spawn((camera, FreeFlyCamera::default())); projection: CameraProjectionMode::Orthographic(OrthographicProjection {
scale: 0.75,
..Default::default()
}),
},
Transform::from_xyz(200.0, 120.0, 0.0),
FreeFlyCamera::default(),
));
} }