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,39 +1,86 @@
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)]
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub struct OrthographicProjection {
pub scale: f32,
pub znear: f32,
pub zfar: f32,
}
impl Default for OrthographicProjection {
fn default() -> Self {
Self {
scale: 1.0,
znear: 0.0,
zfar: 1000.0,
}
}
}
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,
)
}
}
#[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,
Perspective(PerspectiveProjection),
/// 2d camera projection
Orthographic,
Orthographic(OrthographicProjection),
}
#[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 {
Self {
aspect: width as f32 / height as f32,
znear,
zfar,
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),
}
}
pub fn resize(&mut self, width: u32, height: u32) {
self.aspect = width as f32 / height as f32;
}
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)
}
}
#[repr(C)]
@ -48,9 +95,8 @@ pub struct CameraUniform {
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,
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);
#[allow(dead_code)]
size: PhysicalSize<u32>,
aspect: f32,
znear: f32,
zfar: f32,
}
let projection = projection.to_mat4(viewport_size);
let view_projection = projection * view;
impl RenderCamera {
pub fn new(size: PhysicalSize<u32>) -> 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<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,
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,
inverse_projection: projection.inverse(),
view_projection,
projection,
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,
}
}
}

View File

@ -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!");

View File

@ -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()
}
}

View File

@ -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<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;
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<DeltaTime>, handler: Res<Actio
velocity += move_z * forward;
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);
@ -72,13 +71,13 @@ pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<Actio
if camera_rot != Vec3::ZERO {
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;
y += look_velocity.y;
x = x.clamp(-1.54, 1.54);
// 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);
}
}

View File

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

View File

@ -1,19 +1,66 @@
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<CameraProjectionMode> {
match s {
"perspective" => Some(CameraProjectionMode::Perspective),
"orthographic" => Some(CameraProjectionMode::Orthographic),
_ => None,
fn projection_as_table(lua: &mlua::Lua, projection: CameraProjectionMode) -> mlua::Result<mlua::Table> {
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<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'
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)?
},
)
}
);

View File

@ -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::<Image>("../assets/Egg_item.png")
.unwrap();
let image = resman.request::<Image>("../assets/Egg_item.png").unwrap();
image.wait_recurse_dependencies_load();
drop(resman);
@ -131,7 +148,7 @@ 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),
));
{
@ -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(),
));
}