diff --git a/crates/lyra-game/src/render/camera.rs b/crates/lyra-game/src/render/camera.rs index 67a96ee..ad76a81 100755 --- a/crates/lyra-game/src/render/camera.rs +++ b/crates/lyra-game/src/render/camera.rs @@ -1,38 +1,85 @@ +use glam::{Mat4, Vec2}; +use lyra_math::Transform; 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)] -pub enum CameraProjectionMode { - /// 3d camera projection - Perspective, - /// 2d camera projection - Orthographic, +#[derive(Debug, Clone, Copy, PartialEq, Reflect)] +pub struct OrthographicProjection { + pub scale: f32, + pub znear: f32, + pub zfar: f32, } -#[derive(Debug, Clone)] -pub struct Projection { - aspect: f32, - znear: f32, - zfar: f32, -} - -impl Projection { - pub fn new(width: u32, height: u32, znear: f32, zfar: f32) -> Self { +impl Default for OrthographicProjection { + fn default() -> Self { Self { - aspect: width as f32 / height as f32, - znear, - zfar, + scale: 1.0, + znear: 0.0, + zfar: 1000.0, } } +} - pub fn resize(&mut self, width: u32, height: u32) { - self.aspect = width as f32 / height as f32; +impl OrthographicProjection { + 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 { - OPENGL_TO_WGPU_MATRIX * glam::Mat4::perspective_rh_gl(fov.to_radians(), self.aspect, self.znear, self.zfar) +#[derive(Debug, Clone, Copy, PartialEq, Reflect)] +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), + } } } @@ -43,14 +90,13 @@ pub struct CameraUniform { pub view: glam::Mat4, /// The inverse of the projection matrix of the camera pub inverse_projection: glam::Mat4, - /// The view projection matrix + /// The view projection matrix pub view_projection: glam::Mat4, pub projection: glam::Mat4, /// The position of the camera pub position: glam::Vec3, - pub tile_debug: u32, + _padding: u32, //_padding: [u8; 3], - } impl Default for CameraUniform { @@ -61,106 +107,45 @@ impl Default for CameraUniform { view_projection: glam::Mat4::IDENTITY, projection: glam::Mat4::IDENTITY, position: Default::default(), - tile_debug: 0, - //_padding: 0, + _padding: 0, } } } 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 { view, inverse_projection, view_projection, projection, position, - tile_debug: 0 + _padding: 0, } } -} -#[derive(Debug, Clone)] -pub struct RenderCamera { - view_proj: glam::Mat4, - - #[allow(dead_code)] - size: PhysicalSize, - aspect: f32, - znear: f32, - zfar: f32, -} + pub fn from_component(transform: Transform, projection: CameraProjectionMode, viewport_size: Vec2) -> Self { + let position = transform.translation; + let forward = transform.forward(); + let up = transform.up(); + let view = glam::Mat4::look_to_rh(position, forward, up); + + let projection = projection.to_mat4(viewport_size); + let view_projection = projection * view; -impl RenderCamera { - pub fn new(size: PhysicalSize) -> Self { Self { - view_proj: glam::Mat4::IDENTITY, - - size, - aspect: size.width as f32 / size.height as f32, - znear: 0.1, - zfar: 100.0, - } - } - - pub fn update_aspect_ratio(&mut self, size: PhysicalSize) { - 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( + view, + inverse_projection: projection.inverse(), + view_projection, + projection, position, - forward, - 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, - } - }, + _padding: 0, } } -} \ No newline at end of file +} diff --git a/crates/lyra-game/src/render/graph/passes/base.rs b/crates/lyra-game/src/render/graph/passes/base.rs index 9bb45bb..c6599a8 100644 --- a/crates/lyra-game/src/render/graph/passes/base.rs +++ b/crates/lyra-game/src/render/graph/passes/base.rs @@ -2,12 +2,12 @@ use std::sync::Arc; use glam::UVec2; use lyra_game_derive::RenderGraphLabel; +use lyra_math::Transform; use tracing::warn; -use winit::dpi::PhysicalSize; use crate::{ render::{ - camera::{CameraUniform, RenderCamera}, + camera::CameraUniform, graph::{ 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) { - 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 mut render_cam = - RenderCamera::new(PhysicalSize::new(screen_size.x, screen_size.y)); - let uniform = render_cam.calc_view_projection(&camera); - + let uniform = CameraUniform::from_component(*transform, camera.projection, screen_size.as_vec2()); context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform) } else { warn!("Missing camera!"); diff --git a/crates/lyra-game/src/scene/camera.rs b/crates/lyra-game/src/scene/camera.rs index 4ede942..cb869d7 100755 --- a/crates/lyra-game/src/scene/camera.rs +++ b/crates/lyra-game/src/scene/camera.rs @@ -1,23 +1,18 @@ use lyra_ecs::Component; use lyra_reflect::Reflect; -use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode}; +use crate::render::camera::{CameraProjectionMode, OrthographicProjection, PerspectiveProjection}; #[derive(Clone, Component, Reflect)] pub struct CameraComponent { - pub transform: Transform, - pub fov: Angle, - pub mode: CameraProjectionMode, - pub debug: bool, + pub projection: CameraProjectionMode, + //pub debug: bool, } impl Default for CameraComponent { fn default() -> Self { Self { - transform: Transform::default(), - fov: Angle::Degrees(45.0), - mode: CameraProjectionMode::Perspective, - debug: false, + projection: CameraProjectionMode::Perspective(PerspectiveProjection::default()), } } } @@ -29,7 +24,7 @@ impl CameraComponent { pub fn new_2d() -> Self { CameraComponent { - mode: CameraProjectionMode::Orthographic, + projection: CameraProjectionMode::Orthographic(OrthographicProjection::default()), ..Default::default() } } diff --git a/crates/lyra-game/src/scene/free_fly_camera.rs b/crates/lyra-game/src/scene/free_fly_camera.rs index d43a883..794a8a0 100644 --- a/crates/lyra-game/src/scene/free_fly_camera.rs +++ b/crates/lyra-game/src/scene/free_fly_camera.rs @@ -1,11 +1,10 @@ use glam::{EulerRot, Quat, Vec3}; use lyra_ecs::{query::{Res, View}, Component}; +use lyra_math::Transform; use lyra_reflect::Reflect; use crate::{game::App, input::ActionHandler, plugin::Plugin, DeltaTime}; -use super::CameraComponent; - pub const ACTLBL_MOVE_UP_DOWN: &str = "MoveUpDown"; pub const ACTLBL_MOVE_LEFT_RIGHT: &str = "MoveLeftRight"; pub const ACTLBL_MOVE_FORWARD_BACKWARD: &str = "MoveForwardBackward"; @@ -40,11 +39,11 @@ impl FreeFlyCamera { } } -pub fn free_fly_camera_controller(delta_time: Res, handler: Res, view: View<(&mut CameraComponent, &FreeFlyCamera)>) -> anyhow::Result<()> { +pub fn free_fly_camera_controller(delta_time: Res, handler: Res, view: View<(&mut Transform, &FreeFlyCamera)>) -> anyhow::Result<()> { let delta_time = **delta_time; - for (mut cam, fly) in view.into_iter() { - let forward = cam.transform.forward(); - let left = cam.transform.left(); + for (mut transform, fly) in view.into_iter() { + let forward = transform.forward(); + let left = transform.left(); let up = Vec3::Y; 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, handler: Res, handler: Res f32 { radians * 180.0 / PI } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Angle { Degrees(f32), Radians(f32), diff --git a/crates/lyra-scripting/src/lua/wrappers/camera.rs b/crates/lyra-scripting/src/lua/wrappers/camera.rs index cba35af..8a472c0 100644 --- a/crates/lyra-scripting/src/lua/wrappers/camera.rs +++ b/crates/lyra-scripting/src/lua/wrappers/camera.rs @@ -1,21 +1,68 @@ use crate::{ lua::{ - wrappers::{LuaAngle, LuaTransform}, - LuaWrapper + wrappers::LuaAngle, + LuaWrapper, }, ScriptBorrow, }; -use lyra_game::{render::camera::CameraProjectionMode, scene::CameraComponent}; -use lyra_reflect::Enum; +use lyra_game::{ + render::camera::{CameraProjectionMode, OrthographicProjection, PerspectiveProjection}, + scene::CameraComponent, +}; use lyra_scripting_derive::to_lua_convert; -fn projection_mode_from_str(s: &str) -> Option { - match s { - "perspective" => Some(CameraProjectionMode::Perspective), - "orthographic" => Some(CameraProjectionMode::Orthographic), - _ => None, +fn projection_as_table(lua: &mlua::Lua, projection: CameraProjectionMode) -> mlua::Result { + let table = lua.create_table()?; + + match projection { + 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 { + 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)), + }), + } +} to_lua_convert!( // Struct that is being wrapped @@ -27,20 +74,16 @@ to_lua_convert!( // Reflection type, can be 'component' or 'resource' reflect=component, fields={ - transform: wrap(LuaTransform), - fov: wrap(LuaAngle), ( - mode, - // Get the table from the value, result must be `CameraProjectionMode` + projection, get={ - let mode: String = table.get("mode")?; - projection_mode_from_str(&mode).unwrap() + let p: mlua::Table = table.get("projection")?; + projection_from_table(&p)? + }, - // Get the value from self, result must be the type in Lua, here its `String`. set={ - self.mode.variant_name().to_lowercase() - } - ), - debug: bool + projection_as_table(lua, self.projection)? + }, + ) } ); diff --git a/examples/2d/src/main.rs b/examples/2d/src/main.rs index 918090c..5bd8768 100644 --- a/examples/2d/src/main.rs +++ b/examples/2d/src/main.rs @@ -1,10 +1,24 @@ 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, 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 - }, sprite::{self, Sprite}, winit::WindowOptions + }, + math::{self, Transform, Vec3}, + 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] @@ -84,7 +98,11 @@ async fn main() { .with_plugin(action_handler_plugin) //.with_plugin(camera_debug_plugin) .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(); } @@ -113,8 +131,7 @@ fn setup_scene_plugin(app: &mut App) { cube_gltf.wait_recurse_dependencies_load(); let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0]; - let image = resman.request::("../assets/Egg_item.png") - .unwrap(); + let image = resman.request::("../assets/Egg_item.png").unwrap(); image.wait_recurse_dependencies_load(); drop(resman); @@ -131,9 +148,9 @@ fn setup_scene_plugin(app: &mut App) { pivot: sprite::Pivot::Center, }, WorldTransform::default(), - Transform::from_xyz(0.0, 0.0, -20.0), + Transform::from_xyz(0.0, 0.0, -10.0), )); - + { let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0); light_tran.scale = Vec3::new(0.5, 0.5, 0.5); @@ -149,7 +166,14 @@ fn setup_scene_plugin(app: &mut App) { )); } - let mut camera = CameraComponent::new_3d(); - camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5); - world.spawn((camera, FreeFlyCamera::default())); + world.spawn(( + CameraComponent { + projection: CameraProjectionMode::Orthographic(OrthographicProjection { + scale: 0.75, + ..Default::default() + }), + }, + Transform::from_xyz(200.0, 120.0, 0.0), + FreeFlyCamera::default(), + )); }