diff --git a/src/main.rs b/src/main.rs index a518f65..27f1eea 100755 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod render; mod input_event; mod resources; mod ecs; +mod math; use ecs::components::model_2d::Model2dComponent; diff --git a/src/math/angle.rs b/src/math/angle.rs new file mode 100755 index 0000000..2067a6d --- /dev/null +++ b/src/math/angle.rs @@ -0,0 +1,36 @@ +use std::f32::consts::PI; + +/// Convert degrees to radians +pub fn degrees_to_radians(degrees: f32) -> f32 { + degrees * PI / 180.0 +} + +/// Convert radians to degrees +pub fn radians_to_degrees(radians: f32) -> f32 { + radians * 180.0 / PI +} + +pub enum Angle { + Degrees(f32), + Radians(f32), +} + +impl Angle { + /// Converts Angle to radians. + /// (conversion is only done if the Angle is in degrees). + pub fn to_radians(&self) -> f32 { + match *self { + Self::Degrees(d) => degrees_to_radians(d), + Self::Radians(r) => r, + } + } + + /// Converts Angle to degrees. + /// (conversion is only done if the Angle is in radians). + pub fn to_degrees(&self) -> f32 { + match *self { + Self::Degrees(d) => d, + Self::Radians(r) => radians_to_degrees(r), + } + } +} \ No newline at end of file diff --git a/src/math/mod.rs b/src/math/mod.rs new file mode 100755 index 0000000..3de8448 --- /dev/null +++ b/src/math/mod.rs @@ -0,0 +1,5 @@ +/// Re-export of glam library +pub use glam::*; + +pub mod angle; +pub use angle::*; \ No newline at end of file diff --git a/src/render/render_job.rs b/src/render/render_job.rs index 5ed1cdd..852695b 100755 --- a/src/render/render_job.rs +++ b/src/render/render_job.rs @@ -1,19 +1,24 @@ use hecs::Entity; -use super::{mesh::Mesh, material::Material}; +use super::{mesh::Mesh, material::Material, transform::Transform}; pub struct RenderJob { mesh: Mesh, material: Material, entity: Entity, + + pub transform: Option, + pub last_transform: Option, // TODO: render interpolation } impl RenderJob { - pub fn new(mesh: Mesh, material: Material, entity: Entity) -> Self { + pub fn new(mesh: Mesh, material: Material, entity: Entity, transform: Option, last_transform: Option) -> Self { Self { mesh, material, entity, + transform, + last_transform, } } diff --git a/src/render/renderer.rs b/src/render/renderer.rs index a3443e5..51b0dd3 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use async_std::sync::Mutex; use async_trait::async_trait; +use tracing::debug; use wgpu::{BindGroup, BindGroupLayout}; use wgpu::util::DeviceExt; use winit::window::Window; @@ -55,7 +56,6 @@ pub struct BasicRenderer { buffer_storage: HashMap, // TODO: clean up left over buffers from deleted entities/components - transform_uniform: Transform, transform_bind_group: wgpu::BindGroup, transform_buffer: wgpu::Buffer, } @@ -115,10 +115,6 @@ impl BasicRenderer { }; surface.configure(&device, &config); - - /* let diffuse_bytes = include_bytes!("../../res/happy-tree.png"); - let diffuse_texture = RenderTexture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap(); */ - let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ @@ -144,52 +140,16 @@ impl BasicRenderer { label: Some("texture_bind_group_layout"), }); - let transform_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); - - /* let diffuse_bind_group = device.create_bind_group( - &wgpu::BindGroupDescriptor { - layout: &texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(diffuse_texture.view()), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(diffuse_texture.sampler()), - } - ], - label: Some("diffuse_bind_group"), - } - ); */ - - let shader_src = resources::load_string("shader/base_2d.wgsl").await.expect("Failed to load shader!"); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("Shader"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)), }); - let transform_uniform = Transform::new(); let transform_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[transform_uniform]), + label: Some("Transform Buffer"), + contents: bytemuck::cast_slice(&[Transform::new()]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, } ); @@ -220,8 +180,6 @@ impl BasicRenderer { ], label: Some("transform_bind_group"), }); - - let mut pipelines = HashMap::new(); pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader, @@ -244,7 +202,6 @@ impl BasicRenderer { render_jobs: HashMap::new(), buffer_storage: HashMap::new(), - transform_uniform, transform_buffer, transform_bind_group, } @@ -339,7 +296,14 @@ impl BasicRenderer { impl Renderer for BasicRenderer { async fn prepare(&mut self, main_world: &World) { for (entity, (model, transform)) in main_world.query::<(&Model2dComponent, Option<&TransformComponent>)>().iter() { - let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity); + let transform = match transform { + Some(transform) => { + Some(transform.transform) + }, + None => None, + }; + + let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity, transform, None); // Insert the new job into the queue if let Some(shader_jobs) = self.render_jobs.get_mut(&model.material.shader_id) { @@ -352,15 +316,7 @@ impl Renderer for BasicRenderer { let buffers = self.create_model_buffers(model); self.buffer_storage.insert(entity, buffers); } else { - let mesh = &model.mesh; - // update existing buffers - } - - if let Some(transform) = transform { - self.transform_uniform = transform.transform; - - self.queue.write_buffer(&self.transform_buffer, 0, bytemuck::cast_slice(&[self.transform_uniform])); } } } @@ -400,13 +356,19 @@ impl Renderer for BasicRenderer { if let Some(jobs) = pipeline_jobs { for job in jobs.iter() { let mesh = job.mesh(); - let buffers = self.buffer_storage.get(&job.entity()).unwrap(); // TODO: create buffers if not made + let buffers = self.buffer_storage.get(&job.entity()).unwrap(); if let Some(tex) = buffers.texture_bindgroup.as_ref() { render_pass.set_bind_group(0, &tex, &[]); } - render_pass.set_bind_group(1, &self.transform_bind_group, &[]); + // 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])); + render_pass.set_bind_group(1, &self.transform_bind_group, &[]); + } else { + debug!("//TODO: clear transform uniform if the RenderJob doesn't have a transform."); + } if let Some(indices) = buffers.buffer_indices.as_ref() { let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug diff --git a/src/render/transform.rs b/src/render/transform.rs index 6e566af..e91cc92 100755 --- a/src/render/transform.rs +++ b/src/render/transform.rs @@ -1,4 +1,6 @@ -use glam::Vec3; +use glam::{Vec3, Mat4, Quat}; + +use crate::math::angle::{self, Angle}; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -14,33 +16,115 @@ impl Default for Transform { } } +#[allow(dead_code)] impl Transform { pub fn new() -> Self { Self::default() } - pub fn from_glam(matrix: glam::Mat4) -> Self { + pub fn from_matrix(matrix: glam::Mat4) -> Self { Self { matrix, } } + /// Create Transform from scale, rotation (in degrees), and translation + pub fn from_scale_rotation_deg_translation(scale: Vec3, mut rotation: Vec3, translation: Vec3) -> Self { + rotation.x = angle::degrees_to_radians(rotation.x); + rotation.y = angle::degrees_to_radians(rotation.y); + rotation.z = angle::degrees_to_radians(rotation.z); + let rotation_quat = glam::Quat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z); + + let matrix = glam::Mat4::from_scale_rotation_translation(scale, rotation_quat, translation); + + Self::from_matrix(matrix) + } + + /// Create Transform from scale, rotation (in radians), and translation + pub fn from_scale_rotation_rad_translation(scale: Vec3, rotation: Vec3, translation: Vec3) -> Self { + let rotation_quat = glam::Quat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z); + let matrix = glam::Mat4::from_scale_rotation_translation(scale, rotation_quat, translation); + + Self::from_matrix(matrix) + } + + /// Create Transform from scale, rotation (as a quaternion), and translation + pub fn from_scale_rotation_quat_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self { + let matrix = glam::Mat4::from_scale_rotation_translation(scale, rotation, translation); + + Self::from_matrix(matrix) + } + pub fn translate_vec3(&mut self, vec: Vec3) -> &mut Self { - /* let first_col = self.matrix.col(0); - let second_col = self.matrix.col(1); - let third_col = self.matrix.col(2); - - //let fourth_col = self.matrix.col_mut(3); - let w_axis = &mut self.matrix.w_axis; - //first_col.w - //fourth_col = fourth_col.x * vec.x + second_col * vec.y + third_col * vec.z + fourth_col.clone(); - w_axis.x += vec.x; - w_axis.y += vec.y; - w_axis.z += vec.z; */ - let translation = glam::Mat4::from_translation(vec); self.matrix *= translation; self } + + /// Rotate around the given axis. + pub fn rotate_axis(&mut self, axis: Vec3, angle: Angle) -> &mut Self { + let radians = angle.to_radians(); + + let rotation = glam::Mat4::from_axis_angle(axis, radians); + self.matrix *= rotation; + + self + } + + // Rotate the transform with degrees + pub fn rotate_degrees(&mut self, mut x: f32, mut y: f32, mut z: f32) -> &mut Self { + // Convert degrees to radians + x = angle::degrees_to_radians(x); + y = angle::degrees_to_radians(y); + z = angle::degrees_to_radians(z); + + let rotation = glam::Mat4::from_euler(glam::EulerRot::XYZ, x, y, z); + self.matrix *= rotation; + + self + } + + // Rotate the transform with degrees in a vec + pub fn rotate_degrees_vec(&mut self, mut rotation: Vec3) -> &mut Self { + self.rotate_degrees(rotation.x, rotation.y, rotation.z) + } + + // Rotate the transform with radians + pub fn rotate_radians(&mut self, rotation: Vec3) -> &mut Self { + let rotation = glam::Mat4::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z); + self.matrix *= rotation; + + self + } + + /// Rotate the transform with a quaternion + pub fn rotate_quat(&mut self, quat: glam::Quat) -> &mut Self { + let rotation = glam::Mat4::from_quat(quat); + self.matrix *= rotation; + + self + } + + /// Rotate around the x axis. + pub fn rotate_x(&mut self, angle: Angle) -> &mut Self { + self.rotate_axis(Vec3::new(1.0, 0.0, 0.0), angle) + } + + /// Rotate around the y axis. + pub fn rotate_y(&mut self, angle: Angle) -> &mut Self { + self.rotate_axis(Vec3::new(0.0, 1.0, 0.0), angle) + } + + /// 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) + } + + pub fn scale(&mut self, scale: Vec3) -> &mut Self { + let scale = Mat4::from_scale(scale); + self.matrix *= scale; + + self + } } \ No newline at end of file