Add better 3d camera, fix memory 'leak' caused by not clearing the RenderJob queue
This commit is contained in:
parent
3fc8cefa0e
commit
645dd93f21
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
winit = "0.28.1"
|
||||
#winit-modular = "0.1.1"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
||||
tracing-log = "0.1.3"
|
||||
|
@ -17,7 +16,6 @@ cfg-if = "1"
|
|||
bytemuck = { version = "1.12", features = [ "derive" ] }
|
||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
||||
anyhow = "1.0"
|
||||
#cgmath = "0.18"
|
||||
tobj = { version = "3.2.1", features = [
|
||||
"async",
|
||||
]}
|
||||
|
@ -25,4 +23,4 @@ instant = "0.1"
|
|||
async-trait = "0.1.65"
|
||||
specs = { version = "0.18.0", features = [ "derive" ] }
|
||||
hecs = "0.10.3"
|
||||
glam = { version = "0.24.0", features = ["bytemuck"] }
|
||||
glam = { version = "0.24.0", features = ["bytemuck"] }
|
|
@ -8,6 +8,8 @@ mkShell rec {
|
|||
openssl
|
||||
wasm-pack
|
||||
trunk
|
||||
valgrind
|
||||
heaptrack
|
||||
];
|
||||
buildInputs = [
|
||||
udev alsa-lib vulkan-loader
|
||||
|
|
|
@ -1,31 +1,19 @@
|
|||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use crate::math::{Angle, OPENGL_TO_WGPU_MATRIX};
|
||||
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX, Transform}, render::camera::CameraProjectionMode};
|
||||
|
||||
pub struct CameraComponent {
|
||||
eye: glam::Vec3,
|
||||
target: glam::Vec3,
|
||||
up: glam::Vec3,
|
||||
aspect_ratio: Option<f32>,
|
||||
fov_y: Angle,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
pub transform: Transform,
|
||||
pub fov: Angle,
|
||||
pub mode: CameraProjectionMode,
|
||||
}
|
||||
|
||||
impl Default for CameraComponent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// position the camera one unit up and 2 units back
|
||||
// +z is out of the screen
|
||||
eye: (0.0, 1.0, 2.0).into(),
|
||||
// have it look at the origin
|
||||
target: (0.0, 0.0, 0.0).into(),
|
||||
// which way is "up"
|
||||
up: (0.0, 1.0, 0.0).into(),
|
||||
aspect_ratio: None,//config.width as f32 / config.height as f32,
|
||||
fov_y: Angle::Degrees(45.0),
|
||||
znear: 0.1,
|
||||
zfar: 100.0,
|
||||
transform: Transform::new(),
|
||||
fov: Angle::Degrees(45.0),
|
||||
mode: CameraProjectionMode::Perspective,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,23 +22,4 @@ impl CameraComponent {
|
|||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Set the camera aspect ratio. This should only be used internally.
|
||||
pub(crate) fn set_aspect_ratio(&mut self, size: PhysicalSize<u32>) {
|
||||
self.aspect_ratio = Some(size.width as f32 / size.height as f32);
|
||||
}
|
||||
|
||||
/// Check if the camera is ready to be used in rendering.
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.aspect_ratio.is_some()
|
||||
}
|
||||
|
||||
pub fn get_view_projection_matrix(&self) -> glam::Mat4 {
|
||||
let aspect_ratio = self.aspect_ratio.expect("ERROR: Camera aspect ratio was not set!");
|
||||
|
||||
let view = glam::Mat4::look_at_rh(self.eye, self.target, self.up);
|
||||
let projection = glam::Mat4::perspective_rh_gl(self.fov_y.to_radians(), aspect_ratio, self.znear, self.zfar);
|
||||
|
||||
OPENGL_TO_WGPU_MATRIX * projection * view
|
||||
}
|
||||
}
|
70
src/game.rs
70
src/game.rs
|
@ -3,6 +3,7 @@ use std::{sync::Arc, cell::RefCell};
|
|||
use async_std::{task::block_on, sync::Mutex};
|
||||
|
||||
use hecs::World;
|
||||
use instant::Instant;
|
||||
use tracing::{metadata::LevelFilter, info, debug, warn};
|
||||
use tracing_subscriber::{
|
||||
layer::{Layer, SubscriberExt},
|
||||
|
@ -14,11 +15,67 @@ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, Keyboar
|
|||
|
||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::components::{mesh::MeshComponent, transform::TransformComponent}};
|
||||
|
||||
struct TickCounter {
|
||||
counter: u32,
|
||||
last_second: Instant,
|
||||
changed: bool,
|
||||
tps: f32,
|
||||
/// the time (in seconds) that passes between each tick
|
||||
tick_time: f32
|
||||
}
|
||||
|
||||
impl TickCounter {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
last_second: Instant::now(),
|
||||
tps: 0.0,
|
||||
changed: false,
|
||||
tick_time: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the tps changed
|
||||
fn tick(&mut self) -> bool {
|
||||
self.counter += 1;
|
||||
|
||||
if self.last_second.elapsed().as_secs() > 0 {
|
||||
self.tick_time = 1000.0 / self.counter as f32;
|
||||
self.tps = self.counter as f32;
|
||||
|
||||
self.changed = true;
|
||||
self.counter = 0;
|
||||
self.last_second = Instant::now();
|
||||
}
|
||||
|
||||
self.changed
|
||||
}
|
||||
|
||||
/// Gets the change in ticks per second
|
||||
fn get_change(&mut self) -> Option<f32> {
|
||||
match self.changed {
|
||||
true => {
|
||||
self.changed = false;
|
||||
|
||||
Some(self.tps)
|
||||
},
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the time (in seconds) between ticks
|
||||
fn get_tick_time(&self) -> f32 {
|
||||
self.tick_time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct GameLoop {
|
||||
window: Arc<Window>,
|
||||
renderer: Box<dyn Renderer>,
|
||||
|
||||
world: Arc<Mutex<World>>,
|
||||
fps_counter: TickCounter,
|
||||
}
|
||||
|
||||
impl GameLoop {
|
||||
|
@ -28,6 +85,7 @@ impl GameLoop {
|
|||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
||||
|
||||
world,
|
||||
fps_counter: TickCounter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,16 +178,12 @@ impl GameLoop {
|
|||
// Update the world
|
||||
self.update().await;
|
||||
|
||||
let mut world = self.world.lock().await;
|
||||
|
||||
/* for (ent, (transform,)) in world.query_mut::<(&mut TransformComponent,)>() {
|
||||
transform.transform.translate_vec3(glam::Vec3::new(0.0, 0.001, 0.0));
|
||||
}
|
||||
|
||||
for (ent, (transform,)) in world.query::<(&TransformComponent,)>().iter() {
|
||||
println!("transform pos: {:?}", transform.transform);
|
||||
/* self.fps_counter.tick();
|
||||
if let Some(fps) = self.fps_counter.get_change() {
|
||||
debug!("FPS: {}fps, {:.2}ms/frame", fps, self.fps_counter.get_tick_time());
|
||||
} */
|
||||
|
||||
let mut world = self.world.lock().await;
|
||||
self.renderer.as_mut().prepare(&mut world).await;
|
||||
drop(world);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use hecs::World;
|
|||
use crate::render::material::Material;
|
||||
use crate::render::texture::Texture;
|
||||
use crate::ecs::components::camera::CameraComponent;
|
||||
use crate::math::Angle;
|
||||
//use specs::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -79,7 +80,11 @@ async fn main() {
|
|||
TransformComponent::new(),
|
||||
));
|
||||
|
||||
world.spawn((CameraComponent::new(),));
|
||||
let mut camera = CameraComponent::new();
|
||||
camera.transform.translate_vec3(glam::Vec3::new(0.0, 0.0, 2.0));
|
||||
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
||||
camera.transform.rotate_z(Angle::Degrees(-90.0));
|
||||
world.spawn((camera,));
|
||||
|
||||
Game::initialize().await
|
||||
.with_world(world)
|
||||
|
|
|
@ -118,9 +118,37 @@ impl Transform {
|
|||
|
||||
/// Rotate around the z axis.
|
||||
pub fn rotate_z(&mut self, angle: Angle) -> &mut Self {
|
||||
self.rotate_axis(Vec3::new(0.0, 0.0, 0.0), angle)
|
||||
self.rotate_axis(Vec3::new(0.0, 0.0, 1.0), angle)
|
||||
}
|
||||
|
||||
/// Get translation
|
||||
pub fn get_translation(&self) -> glam::Vec3 {
|
||||
let (_, _, t) = self.matrix.to_scale_rotation_translation();
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
/// Get rotation in radians
|
||||
pub fn get_rotation_rad(&self) -> glam::Vec3 {
|
||||
let (_, quat, _) = self.matrix.to_scale_rotation_translation();
|
||||
let rot = quat.to_euler(glam::EulerRot::XYZ);
|
||||
|
||||
rot.into()
|
||||
}
|
||||
|
||||
/// Get rotation in degrees
|
||||
pub fn get_rotation_deg(&self) -> glam::Vec3 {
|
||||
let mut rot = self.get_rotation_rad();
|
||||
|
||||
rot.x = angle::radians_to_degrees(rot.x);
|
||||
rot.y = angle::radians_to_degrees(rot.y);
|
||||
rot.z = angle::radians_to_degrees(rot.z);
|
||||
|
||||
rot
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn scale(&mut self, scale: Vec3) -> &mut Self {
|
||||
let scale = Mat4::from_scale(scale);
|
||||
self.matrix *= scale;
|
||||
|
|
|
@ -1,21 +1,95 @@
|
|||
use tracing::debug;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, ecs::components::camera::CameraComponent};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Camera {
|
||||
projection: glam::Mat4
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum CameraProjectionMode {
|
||||
/// 3d camera projection
|
||||
Perspective,
|
||||
/// 2d camera projection
|
||||
Orthographic,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new() -> Self {
|
||||
#[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 {
|
||||
projection: glam::Mat4::IDENTITY,
|
||||
aspect: width as f32 / height as f32,
|
||||
znear,
|
||||
zfar,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_view_projection(&mut self, camera: &CameraComponent) {
|
||||
self.projection = camera.get_view_projection_matrix().into();
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderCamera {
|
||||
view_proj: glam::Mat4,
|
||||
|
||||
aspect: f32,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
}
|
||||
|
||||
impl RenderCamera {
|
||||
pub fn new(size: PhysicalSize<u32>) -> Self {
|
||||
Self {
|
||||
view_proj: glam::Mat4::IDENTITY,
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn update_view_projection(&mut self, camera: &CameraComponent) -> &glam::Mat4 {
|
||||
let rotation = camera.transform.get_rotation_rad();
|
||||
let position = camera.transform.get_translation();
|
||||
|
||||
let (sin_y, cos_y) = rotation.y.sin_cos();
|
||||
let (sin_z, cos_z) = rotation.z.sin_cos();
|
||||
|
||||
let target = glam::Vec3::new(
|
||||
cos_y * cos_z,
|
||||
sin_y,
|
||||
cos_y * sin_z
|
||||
).normalize();
|
||||
|
||||
/* debug!("Camera rotation: {:?}", rotation);
|
||||
debug!("Camera position: {:?}", position);
|
||||
debug!("Camera target: {:?}", target); */
|
||||
|
||||
let view = glam::Mat4::look_to_rh(
|
||||
position,
|
||||
target,
|
||||
glam::Vec3::new(0.0, 1.0, 0.0)
|
||||
);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn view_proj(&self) -> &glam::Mat4 {
|
||||
&self.view_proj
|
||||
}
|
||||
}
|
|
@ -16,10 +16,10 @@ use hecs::{World, Entity};
|
|||
use crate::ecs::components::camera::CameraComponent;
|
||||
use crate::ecs::components::mesh::MeshComponent;
|
||||
use crate::ecs::components::transform::TransformComponent;
|
||||
use crate::math::Transform;
|
||||
use crate::math::{Transform, Angle};
|
||||
use crate::resources;
|
||||
|
||||
use super::camera::Camera;
|
||||
use super::camera::RenderCamera;
|
||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||
use super::texture::RenderTexture;
|
||||
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
|
||||
|
@ -61,7 +61,7 @@ pub struct BasicRenderer {
|
|||
transform_buffer: wgpu::Buffer,
|
||||
transform_bind_group: wgpu::BindGroup,
|
||||
|
||||
inuse_camera: Camera,
|
||||
inuse_camera: RenderCamera,
|
||||
camera_buffer: wgpu::Buffer,
|
||||
camera_bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
@ -105,6 +105,13 @@ impl BasicRenderer {
|
|||
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
|
||||
let present_mode = surface_caps.present_modes[0]; /* match surface_caps.present_modes.contains(&wgpu::PresentMode::Immediate) {
|
||||
true => wgpu::PresentMode::Immediate,
|
||||
false => surface_caps.present_modes[0]
|
||||
}; */
|
||||
|
||||
println!("present mode: {:?}", present_mode);
|
||||
|
||||
let surface_format = surface_caps.formats.iter()
|
||||
.copied()
|
||||
.filter(|f| f.describe().srgb)
|
||||
|
@ -115,7 +122,7 @@ impl BasicRenderer {
|
|||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: surface_caps.present_modes[0],
|
||||
present_mode,
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
};
|
||||
|
@ -190,7 +197,7 @@ impl BasicRenderer {
|
|||
let camera_buffer = device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Camera Buffer"),
|
||||
contents: bytemuck::cast_slice(&[Camera::new()]),
|
||||
contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
@ -247,7 +254,7 @@ impl BasicRenderer {
|
|||
transform_buffer,
|
||||
transform_bind_group,
|
||||
|
||||
inuse_camera: Camera::new(),
|
||||
inuse_camera: RenderCamera::new(size),
|
||||
camera_buffer,
|
||||
camera_bind_group,
|
||||
}
|
||||
|
@ -365,12 +372,8 @@ impl Renderer for BasicRenderer {
|
|||
}
|
||||
|
||||
if let Some((_e, (camera,))) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
||||
//if !camera.is_ready() {
|
||||
camera.set_aspect_ratio(self.size);
|
||||
//}
|
||||
|
||||
self.inuse_camera.update_view_projection(camera);
|
||||
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[self.inuse_camera]));
|
||||
let view_proj = self.inuse_camera.update_view_projection(camera);
|
||||
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[view_proj.clone()]));
|
||||
} else {
|
||||
warn!("Missing camera!");
|
||||
}
|
||||
|
@ -419,7 +422,7 @@ impl Renderer for BasicRenderer {
|
|||
|
||||
// If the job has a transform, set the uniform to it
|
||||
if let Some(transform) = job.transform {
|
||||
self.queue.write_buffer(&self.transform_buffer, 0, bytemuck::cast_slice(&[transform]));
|
||||
//self.queue.write_buffer(&self.transform_buffer, 0, bytemuck::cast_slice(&[transform]));
|
||||
render_pass.set_bind_group(1, &self.transform_bind_group, &[]);
|
||||
} else {
|
||||
debug!("//TODO: clear transform uniform if the RenderJob doesn't have a transform.");
|
||||
|
@ -447,6 +450,9 @@ impl Renderer for BasicRenderer {
|
|||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
|
||||
// TODO: Use an actual queue instead of a hashmap
|
||||
self.render_jobs = HashMap::new();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -456,6 +462,8 @@ impl Renderer for BasicRenderer {
|
|||
self.config.width = new_size.width;
|
||||
self.config.height = new_size.height;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
|
||||
self.inuse_camera.update_aspect_ratio(self.size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue