Add helper methods to Transform

This commit is contained in:
SeanOMik 2023-05-14 23:18:41 -04:00
parent 265c3dbc5a
commit 836afe2c0e
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
6 changed files with 166 additions and 73 deletions

View File

@ -3,6 +3,7 @@ mod render;
mod input_event; mod input_event;
mod resources; mod resources;
mod ecs; mod ecs;
mod math;
use ecs::components::model_2d::Model2dComponent; use ecs::components::model_2d::Model2dComponent;

36
src/math/angle.rs Executable file
View File

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

5
src/math/mod.rs Executable file
View File

@ -0,0 +1,5 @@
/// Re-export of glam library
pub use glam::*;
pub mod angle;
pub use angle::*;

View File

@ -1,19 +1,24 @@
use hecs::Entity; use hecs::Entity;
use super::{mesh::Mesh, material::Material}; use super::{mesh::Mesh, material::Material, transform::Transform};
pub struct RenderJob { pub struct RenderJob {
mesh: Mesh, mesh: Mesh,
material: Material, material: Material,
entity: Entity, entity: Entity,
pub transform: Option<Transform>,
pub last_transform: Option<Transform>, // TODO: render interpolation
} }
impl RenderJob { impl RenderJob {
pub fn new(mesh: Mesh, material: Material, entity: Entity) -> Self { pub fn new(mesh: Mesh, material: Material, entity: Entity, transform: Option<Transform>, last_transform: Option<Transform>) -> Self {
Self { Self {
mesh, mesh,
material, material,
entity, entity,
transform,
last_transform,
} }
} }

View File

@ -6,6 +6,7 @@ use std::borrow::Cow;
use async_std::sync::Mutex; use async_std::sync::Mutex;
use async_trait::async_trait; use async_trait::async_trait;
use tracing::debug;
use wgpu::{BindGroup, BindGroupLayout}; use wgpu::{BindGroup, BindGroupLayout};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use winit::window::Window; use winit::window::Window;
@ -55,7 +56,6 @@ pub struct BasicRenderer {
buffer_storage: HashMap<Entity, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components buffer_storage: HashMap<Entity, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
transform_uniform: Transform,
transform_bind_group: wgpu::BindGroup, transform_bind_group: wgpu::BindGroup,
transform_buffer: wgpu::Buffer, transform_buffer: wgpu::Buffer,
} }
@ -115,10 +115,6 @@ impl BasicRenderer {
}; };
surface.configure(&device, &config); 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 = let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
@ -144,52 +140,16 @@ impl BasicRenderer {
label: Some("texture_bind_group_layout"), 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_src = resources::load_string("shader/base_2d.wgsl").await.expect("Failed to load shader!");
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"), label: Some("Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
}); });
let transform_uniform = Transform::new();
let transform_buffer = device.create_buffer_init( let transform_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor { &wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"), label: Some("Transform Buffer"),
contents: bytemuck::cast_slice(&[transform_uniform]), contents: bytemuck::cast_slice(&[Transform::new()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
} }
); );
@ -221,8 +181,6 @@ impl BasicRenderer {
label: Some("transform_bind_group"), label: Some("transform_bind_group"),
}); });
let mut pipelines = HashMap::new(); let mut pipelines = HashMap::new();
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader, pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader,
vec![super::vertex::Vertex::desc(),], vec![&texture_bind_group_layout, &transform_bind_group_layout]))); vec![super::vertex::Vertex::desc(),], vec![&texture_bind_group_layout, &transform_bind_group_layout])));
@ -244,7 +202,6 @@ impl BasicRenderer {
render_jobs: HashMap::new(), render_jobs: HashMap::new(),
buffer_storage: HashMap::new(), buffer_storage: HashMap::new(),
transform_uniform,
transform_buffer, transform_buffer,
transform_bind_group, transform_bind_group,
} }
@ -339,7 +296,14 @@ impl BasicRenderer {
impl Renderer for BasicRenderer { impl Renderer for BasicRenderer {
async fn prepare(&mut self, main_world: &World) { async fn prepare(&mut self, main_world: &World) {
for (entity, (model, transform)) in main_world.query::<(&Model2dComponent, Option<&TransformComponent>)>().iter() { 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 // Insert the new job into the queue
if let Some(shader_jobs) = self.render_jobs.get_mut(&model.material.shader_id) { 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); let buffers = self.create_model_buffers(model);
self.buffer_storage.insert(entity, buffers); self.buffer_storage.insert(entity, buffers);
} else { } 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 { if let Some(jobs) = pipeline_jobs {
for job in jobs.iter() { for job in jobs.iter() {
let mesh = job.mesh(); 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() { if let Some(tex) = buffers.texture_bindgroup.as_ref() {
render_pass.set_bind_group(0, &tex, &[]); render_pass.set_bind_group(0, &tex, &[]);
} }
// 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, &[]); 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() { 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 let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug

View File

@ -1,4 +1,6 @@
use glam::Vec3; use glam::{Vec3, Mat4, Quat};
use crate::math::angle::{self, Angle};
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
@ -14,33 +16,115 @@ impl Default for Transform {
} }
} }
#[allow(dead_code)]
impl Transform { impl Transform {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
pub fn from_glam(matrix: glam::Mat4) -> Self { pub fn from_matrix(matrix: glam::Mat4) -> Self {
Self { Self {
matrix, 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 { 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); let translation = glam::Mat4::from_translation(vec);
self.matrix *= translation; self.matrix *= translation;
self 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
}
} }