feat: attempt to implement math library
Adds many files, but the next thing is to implement the MeshRegistry properly.
This commit is contained in:
1 parent
9e4f531bd4
commit
e7659ba387
18 files changed
+1232
-136
No files matched your search
+19
-44
@@ -1,6 +1,6 @@
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||
use vulkano::command_buffer::allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo};
|
||||
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, SubpassEndInfo};
|
||||
use vulkano::descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator};
|
||||
@@ -28,37 +28,10 @@ use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use crate::math::Vector2;
|
||||
use crate::common::mesh::MeshRegistry;
|
||||
use crate::gpu::vertex3::Vertex3;
|
||||
use crate::triangle;
|
||||
|
||||
/// The `Vertex2` is an intermediate structure used to represent the [`Vector2`] in the graphics
|
||||
/// pipeline. It specifies a positional value and can be passed directly into the shaders.
|
||||
/// Note that conversion between a `Vector2` and a `Vertex2` may take extra computation time.
|
||||
///
|
||||
/// Honestly, I'm still not sure if this should be handled on a per-shader basis, as the position
|
||||
/// argument changes with each shader.
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
struct Vertex2 {
|
||||
#[format(R32G32_SFLOAT)]
|
||||
position: [f32; 2],
|
||||
}
|
||||
|
||||
impl From<Vector2> for Vertex2 {
|
||||
fn from(v: Vector2) -> Self {
|
||||
Self {
|
||||
position: [v.x, v.y],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
struct Vertex3 {
|
||||
#[format(R32G32B32_SFLOAT)]
|
||||
position: [f32; 3],
|
||||
}
|
||||
|
||||
struct VulkanContext {
|
||||
window: Arc<Window>,
|
||||
device: Arc<Device>,
|
||||
@@ -73,9 +46,8 @@ struct VulkanContext {
|
||||
render_pass: Arc<RenderPass>,
|
||||
framebuffers: Vec<Arc<Framebuffer>>,
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
vertex_buffer: Arc<Subbuffer<[Vertex2]>>,
|
||||
index_buffer: Arc<Subbuffer<[u32]>>,
|
||||
cmd_buffers: Vec<Arc<PrimaryAutoCommandBuffer>>,
|
||||
mesh_registry: Arc<MeshRegistry>,
|
||||
}
|
||||
|
||||
impl VulkanContext {
|
||||
@@ -201,10 +173,10 @@ impl VulkanContext {
|
||||
..Default::default()
|
||||
},
|
||||
vec![
|
||||
Vertex2 { position: [0.5, 0.5] },
|
||||
Vertex2 { position: [-0.5, 0.5] },
|
||||
Vertex2 { position: [-0.5, -0.5] },
|
||||
Vertex2 { position: [0.5, -0.5] },
|
||||
Vertex3 { position: [0.5, 0.5, 0.0] },
|
||||
Vertex3 { position: [-0.5, 0.5, 0.0] },
|
||||
Vertex3 { position: [-0.5, -0.5, 0.0] },
|
||||
Vertex3 { position: [0.5, -0.5, 0.0] },
|
||||
]
|
||||
)?);
|
||||
|
||||
@@ -234,6 +206,10 @@ impl VulkanContext {
|
||||
&index_buffer
|
||||
)?;
|
||||
|
||||
let mesh_registry = Arc::new(MeshRegistry::new(
|
||||
mem_allocator.clone(),
|
||||
)?);
|
||||
|
||||
Ok(Self {
|
||||
window,
|
||||
device,
|
||||
@@ -248,9 +224,8 @@ impl VulkanContext {
|
||||
frag_shader,
|
||||
framebuffers,
|
||||
pipeline,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
cmd_buffers
|
||||
cmd_buffers,
|
||||
mesh_registry,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -378,7 +353,7 @@ impl VulkanContext {
|
||||
let fs = frag_shader.entry_point("main")
|
||||
.ok_or("Failed to find fragment shader entry point")?;
|
||||
|
||||
let vertex_input_state = Vertex2::per_vertex()
|
||||
let vertex_input_state = Vertex3::per_vertex()
|
||||
.definition(&vs)?;
|
||||
|
||||
let stages = vec![
|
||||
@@ -432,7 +407,7 @@ impl VulkanContext {
|
||||
queue: &Arc<Queue>,
|
||||
pipeline: &Arc<GraphicsPipeline>,
|
||||
framebuffers: &Vec<Arc<Framebuffer>>,
|
||||
vertex_buffer: &Subbuffer<[Vertex2]>,
|
||||
vertex_buffer: &Arc<Subbuffer<[Vertex3]>>,
|
||||
index_buffer: &Subbuffer<[u32]>
|
||||
) -> Result<Vec<Arc<PrimaryAutoCommandBuffer>>, Box<dyn Error>> {
|
||||
let buffers = framebuffers.iter()
|
||||
@@ -456,7 +431,7 @@ impl VulkanContext {
|
||||
}
|
||||
).ok()?
|
||||
.bind_pipeline_graphics(pipeline.clone()).ok()?
|
||||
.bind_vertex_buffers(0, vertex_buffer.clone()).ok()?
|
||||
.bind_vertex_buffers(0, vertex_buffer.as_ref().clone()).ok()?
|
||||
.bind_index_buffer(index_buffer.clone()).ok()?
|
||||
// .draw(vertex_buffer.len() as u32, 1, 0, 0).ok()?
|
||||
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0).ok()?
|
||||
@@ -506,8 +481,8 @@ impl VulkanContext {
|
||||
&self.queue,
|
||||
&new_pipeline,
|
||||
&self.framebuffers,
|
||||
&self.vertex_buffer,
|
||||
&self.index_buffer
|
||||
&self.mesh_registry.vertex_buffer,
|
||||
&self.mesh_registry.index_buffer
|
||||
)?;
|
||||
|
||||
self.pipeline = new_pipeline;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use nalgebra::{Matrix4, Vector3, Vector4};
|
||||
use crate::common::scene::SceneInstance;
|
||||
|
||||
/// Contains all information about an instance of a mesh, specifically its translation, rotation,
|
||||
/// and scale. Note that the rotation is a [`Vector4`], and not a `Quaternion`.
|
||||
///
|
||||
/// This may be taken from a scene instance by calling the `Instance::from` method on the
|
||||
/// corresponding [`SceneInstance`]. This is important, as the `Vector4` is not serializable.
|
||||
///
|
||||
/// Before passing this instance into the shaders, construct a transformation matrix by calling
|
||||
/// [`MeshInstance::transform_matrix`].
|
||||
pub struct MeshInstance {
|
||||
/// The ID of the mesh corresponding to this instance.
|
||||
pub mesh_id: String,
|
||||
/// The translation of the instance from the center of the standard space. It is possible that
|
||||
/// local translation will be required in the future.
|
||||
pub translation: Vector4<f32>,
|
||||
/// The rotation of the instance. Note that since this is a [`Vector4`], or Euler angle, it
|
||||
/// suffers from gimbal lock. If this becomes a serious issue, an [`nalgebra::UnitQuaternion`]
|
||||
/// should be used instead.
|
||||
pub rotation: Vector4<f32>,
|
||||
/// The scale of the instance, represented as the scaling in XYZ coordinates.
|
||||
pub scale: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl MeshInstance {
|
||||
/// Constructs a transformation matrix from the current translation, rotation, and scale.
|
||||
pub fn transform_matrix(&self) -> Matrix4<f32> {
|
||||
Matrix4::new_translation(&self.translation.xyz()) *
|
||||
// There's a possibility that these can be altered. Sometimes they're multiplied in the
|
||||
// order ZYX. They are technically applied in the opposite order written here, so in
|
||||
// this case it's actually applying Z, then Y, then X. They *do* produce different results.
|
||||
Matrix4::from_axis_angle(&Vector3::x_axis(), self.rotation.x) *
|
||||
Matrix4::from_axis_angle(&Vector3::y_axis(), self.rotation.y) *
|
||||
Matrix4::from_axis_angle(&Vector3::z_axis(), self.rotation.z) *
|
||||
Matrix4::new_nonuniform_scaling(&self.scale)
|
||||
}
|
||||
|
||||
/// Constructs a `MeshInstance` from the given [`SceneInstance`] and mesh ID.
|
||||
fn from_scene_instance(
|
||||
mesh_id: String,
|
||||
SceneInstance {
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
}: SceneInstance,
|
||||
) -> Self {
|
||||
Self {
|
||||
mesh_id,
|
||||
translation: Vector3::from(translation).to_homogeneous(),
|
||||
rotation: Vector3::from(rotation).to_homogeneous(),
|
||||
scale: Vector3::from(scale),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use nalgebra::Vector3;
|
||||
use uuid::Uuid;
|
||||
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
|
||||
use wavefront::Obj;
|
||||
use crate::common::scene::Scene;
|
||||
use crate::gpu::vertex3::Vertex3;
|
||||
|
||||
/// This struct is slightly misleading - it doesn't actually contain the mesh information, but
|
||||
/// the locations of the mesh's information in the overall application's vertex buffer, index buffer,
|
||||
/// and other such structures.
|
||||
///
|
||||
/// Once loaded, the mesh should be accessible via ID through a registry-type struct, which simply
|
||||
/// maintains the list of meshes that have been loaded by the application so far and which assigns
|
||||
/// the correct indices for buffer-to-render logic.
|
||||
///
|
||||
/// That means that this `Mesh` struct is actually just an entry within that registry. They should
|
||||
/// rarely be used directly, and should never be edited.
|
||||
#[derive(Debug)]
|
||||
pub struct Mesh {
|
||||
pub id: String,
|
||||
pub vertex_loc: usize,
|
||||
pub index_loc: usize,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn new(
|
||||
id: impl Into<String>,
|
||||
vertex_loc: usize,
|
||||
index_loc: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
vertex_loc,
|
||||
index_loc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Mesh Registry
|
||||
/// A wrapper around a series of [`Mesh`] structs, keyed by a unique ID, and the buffers that are
|
||||
/// used by the rendering pipeline.
|
||||
#[derive(Debug)]
|
||||
pub struct MeshRegistry {
|
||||
meshes: HashMap<String, Mesh>,
|
||||
allocator: Arc<dyn MemoryAllocator>,
|
||||
pub vertex_buffer: Arc<Subbuffer<[Vertex3]>>,
|
||||
pub index_buffer: Arc<Subbuffer<[u32]>>,
|
||||
}
|
||||
|
||||
impl MeshRegistry {
|
||||
pub fn new(
|
||||
allocator: Arc<dyn MemoryAllocator>,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let vertex_buffer = Arc::new(Buffer::new_slice::<Vertex3>(
|
||||
allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
|
||||
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
1_000_000,
|
||||
)?);
|
||||
|
||||
let index_buffer = Arc::new(Buffer::new_slice::<u32>(
|
||||
allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::INDEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
|
||||
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
3_000_000,
|
||||
)?);
|
||||
|
||||
Ok(Self {
|
||||
meshes: HashMap::new(),
|
||||
allocator,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `MeshRegistry` from the provided [`Scene`].
|
||||
///
|
||||
/// This method will load all the meshes indicated by the scene and allocate the required
|
||||
/// buffers. This significantly simplifies the allocation process, as the buffers only need to
|
||||
/// be allocated once.
|
||||
pub fn from_scene(scene: &Scene, allocator: Arc<dyn MemoryAllocator>) -> Self {
|
||||
// First, load all the meshes into a single map.
|
||||
println!("Loading meshes from scene");
|
||||
let meshes = scene.mesh_paths.iter().map(|(id, path)|
|
||||
(id.clone(), Obj::from_file(path).expect("Couldn't read wavefront file"))
|
||||
).collect::<HashMap<String, Obj>>();
|
||||
|
||||
// Second, build two more maps that contain the vertex and index information.
|
||||
let verts = meshes.iter().map(|(id, obj)|
|
||||
(id.clone(), obj.vertices().map(|v| v.position().into()).collect())
|
||||
).collect::<HashMap<String, Vec<Vector3<f32>>>>();
|
||||
|
||||
let indices = meshes.iter().map(|(id, obj)|
|
||||
(id.clone(), obj.vertices().map(|v| v.position_index()).collect())
|
||||
).collect::<HashMap<String, Vec<usize>>>();
|
||||
|
||||
// Apply static transformations here. The general idea is that we don't want to have to read
|
||||
// any information from the buffer without the correct information. We do want to keep track
|
||||
// of what mesh is at what location in the list though.
|
||||
println!("Applying static mesh transformations");
|
||||
|
||||
// Get Vector3's as a list of Vertex3's (CPU to GPU)
|
||||
|
||||
// Third, allocate and fill buffers with this information.
|
||||
// let vert_buffer = Self::alloc_vert_buffer(allocator, verts.values().collect());
|
||||
|
||||
// Fourth, return the struct.
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn alloc_vert_buffer(
|
||||
alloc: Arc<dyn MemoryAllocator>,
|
||||
verts: Vec<Vertex3>
|
||||
) -> Arc<Subbuffer<[Vertex3]>> {
|
||||
println!("Allocating vertex buffer");
|
||||
Arc::new(Buffer::from_iter(
|
||||
alloc.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
|
||||
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
verts
|
||||
).expect("Couldn't allocate vertex buffer"))
|
||||
}
|
||||
|
||||
/// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found.
|
||||
pub fn get(&self, uuid: &str) -> Option<&Mesh> {
|
||||
self.meshes.get(uuid)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod mesh;
|
||||
pub mod scene;
|
||||
pub mod instance;
|
||||
@@ -0,0 +1,74 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A scene, which holds all information required to render a world.
|
||||
///
|
||||
/// This includes all mesh information, and all the information that is required to place
|
||||
/// instances of these meshes in the world.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct Scene {
|
||||
/// The name of the scene.
|
||||
pub name: String,
|
||||
/// The collection of paths to the meshes, relative to the current scene file.
|
||||
/// This collection is keyed on the ID of the mesh, which can be used later on to place
|
||||
/// the meshes in the scenes with specific instances. This ID will also be used to register
|
||||
/// the meshes with the application's registries, which will allow the program to associate
|
||||
/// specific behavior with the mesh.
|
||||
pub mesh_paths: HashMap<String, String>,
|
||||
/// A collection of instances that are keyed by the mesh's IDs. Each instance can be used to
|
||||
/// place a [`Mesh`] in the rendered scene.
|
||||
pub instances: HashMap<String, SceneInstance>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
/// Converts this `Scene` into its byte representation.
|
||||
pub fn bytes(&self) -> Vec<u8> {
|
||||
postcard::to_allocvec(self)
|
||||
.expect("Couldn't serialize scene")
|
||||
}
|
||||
|
||||
/// Reads a `scene` from the given path. Automatically resolves all paths for the meshes that
|
||||
/// are referenced in the struct.
|
||||
///
|
||||
/// Reading a scene file allows for all meshes to be loaded at once, which therefore allows
|
||||
/// the program to allocate pipeline buffers just once during the load of this. It would likely
|
||||
/// be reasonable to create a `MeshRegistry` and `InstanceRegistry` at the same time, with
|
||||
/// respect to a single loaded `Scene`.
|
||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
|
||||
let path = path.as_ref();
|
||||
let mut file = std::fs::File::open(path).unwrap();
|
||||
let mut contents = Vec::new();
|
||||
let _ = file.read_to_end(&mut contents).unwrap();
|
||||
|
||||
let mut scene: Self = postcard::from_bytes(contents.as_slice())
|
||||
.expect("Couldn't deserialize scene");
|
||||
|
||||
let base = path.parent().unwrap_or(&Path::new("."));
|
||||
scene.mesh_paths = scene.mesh_paths
|
||||
.into_iter()
|
||||
.map(|(id, mesh_path)| {
|
||||
let res = base.join(mesh_path);
|
||||
let str = res.to_str().unwrap_or_default();
|
||||
|
||||
(id, str.to_string())
|
||||
}).collect::<HashMap<String, String>>();
|
||||
|
||||
scene
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains all information about an instance in the scene, specifically the name of the mesh it
|
||||
/// should use and the translation, rotation, and scale of it.
|
||||
///
|
||||
/// After deserialization, this struct should be converted into an [`Instance`], which contains
|
||||
/// defined vectors that can be used to work with the object before sending it to the shaders.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct SceneInstance {
|
||||
pub translation: [f32; 3],
|
||||
pub rotation: [f32; 3],
|
||||
pub scale: [f32; 3],
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
use vulkano::buffer::BufferContents;
|
||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
||||
use crate::common::instance::MeshInstance;
|
||||
|
||||
/// A transformation matrix for a mesh that can be passed into the shaders.
|
||||
#[derive(BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
pub struct GpuInstance {
|
||||
#[format(R32G32B32A32_SFLOAT)]
|
||||
pub transform_col1: [f32; 4],
|
||||
#[format(R32G32B32A32_SFLOAT)]
|
||||
pub transform_col2: [f32; 4],
|
||||
#[format(R32G32B32A32_SFLOAT)]
|
||||
pub transform_col3: [f32; 4],
|
||||
#[format(R32G32B32A32_SFLOAT)]
|
||||
pub transform_col4: [f32; 4],
|
||||
}
|
||||
|
||||
impl Into<GpuInstance> for MeshInstance {
|
||||
fn into(self) -> GpuInstance {
|
||||
let m = self.transform_matrix();
|
||||
GpuInstance {
|
||||
transform_col1: m.column(0).into(),
|
||||
transform_col2: m.column(1).into(),
|
||||
transform_col3: m.column(2).into(),
|
||||
transform_col4: m.column(3).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod vertex3;
|
||||
pub mod instance;
|
||||
@@ -0,0 +1,15 @@
|
||||
use vulkano::buffer::BufferContents;
|
||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
||||
|
||||
#[derive(Debug, BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex3 {
|
||||
#[format(R32G32B32_SFLOAT)]
|
||||
pub(crate) position: [f32; 3],
|
||||
}
|
||||
|
||||
impl From<&[f32; 3]> for Vertex3 {
|
||||
fn from(v: &[f32; 3]) -> Self {
|
||||
Self { position: v.clone() }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod app;
|
||||
pub mod math;
|
||||
mod common;
|
||||
pub mod gpu;
|
||||
|
||||
use std::error::Error;
|
||||
use vulkano::swapchain::Surface;
|
||||
|
||||
+78
-64
@@ -1,11 +1,11 @@
|
||||
use std::ops::{Add, Div, Index, IndexMut, Mul, Sub};
|
||||
use crate::math::traits::Matrix;
|
||||
use crate::math::Vector2;
|
||||
use std::ops::{Add, Index, IndexMut, Mul, Sub};
|
||||
use crate::math::traits::{Matrix, Vector};
|
||||
use crate::math::{Vector2};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Copy, Default)]
|
||||
pub struct Matrix2 {
|
||||
row1: [f32; 2],
|
||||
row2: [f32; 2],
|
||||
row1: Vector2,
|
||||
row2: Vector2,
|
||||
}
|
||||
|
||||
impl Matrix2 {
|
||||
@@ -29,23 +29,23 @@ impl Matrix2 {
|
||||
}
|
||||
|
||||
impl Index<usize> for Matrix2 {
|
||||
type Output = [f32; 2];
|
||||
type Output = Vector2;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if index == 0 {
|
||||
&self.row1
|
||||
} else {
|
||||
&self.row2
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
match idx {
|
||||
0 => &self.row1,
|
||||
1 => &self.row2,
|
||||
_ => panic!("Index out of bounds"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Matrix2 {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index == 0 {
|
||||
&mut self.row1
|
||||
} else {
|
||||
&mut self.row2
|
||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
||||
match idx {
|
||||
0 => &mut self.row1,
|
||||
1 => &mut self.row2,
|
||||
_ => panic!("Index out of bounds"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,8 +55,8 @@ impl Add for Matrix2 {
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
row1: [self[0][0] + rhs[0][0], self[0][1] + rhs[0][1]],
|
||||
row2: [self[1][0] + rhs[1][0], self[1][1] + rhs[1][1]],
|
||||
row1: [self[0][0] + rhs[0][0], self[0][1] + rhs[0][1]].into(),
|
||||
row2: [self[1][0] + rhs[1][0], self[1][1] + rhs[1][1]].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,19 +66,29 @@ impl Sub for Matrix2 {
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
row1: [self[0][0] - rhs[0][0], self[0][1] - rhs[0][1]],
|
||||
row2: [self[1][0] - rhs[1][0], self[1][1] - rhs[1][1]],
|
||||
row1: [self[0][0] - rhs[0][0], self[0][1] - rhs[0][1]].into(),
|
||||
row2: [self[1][0] - rhs[1][0], self[1][1] - rhs[1][1]].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Matrix2 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
type Output = Matrix2;
|
||||
fn mul(self, rhs: f32) -> Matrix2 {
|
||||
Self {
|
||||
row1: [self[0][0] * rhs, self[0][1] * rhs],
|
||||
row2: [self[1][0] * rhs, self[1][1] * rhs],
|
||||
row1: self.row1 * rhs,
|
||||
row2: self.row2 * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vector2> for Matrix2 {
|
||||
type Output = Vector2;
|
||||
|
||||
fn mul(self, rhs: Vector2) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self[0].dot(rhs),
|
||||
y: self[1].dot(rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,58 +105,62 @@ impl Mul for Matrix2 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Matrix2) -> Self::Output {
|
||||
let other = rhs.transpose();
|
||||
Self {
|
||||
row1: [self[0][0] * rhs[0][0] + self[0][1] * rhs[1][0], self[0][0] * rhs[0][1] + self[0][1] * rhs[1][1]],
|
||||
row2: [self[1][0] * rhs[0][0] + self[1][1] * rhs[1][0], self[1][0] * rhs[0][1] + self[1][1] * rhs[1][1]],
|
||||
row1: [self[0].dot(other[0]), self[0].dot(other[1])].into(),
|
||||
row2: [self[1].dot(other[0]), self[1].dot(other[1])].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Matrix2 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Matrix2 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Matrix for Matrix2 {
|
||||
impl Matrix<Vector2> for Matrix2 {
|
||||
fn identity() -> Self {
|
||||
Self {
|
||||
row1: [1.0, 0.0],
|
||||
row2: [0.0, 1.0],
|
||||
row1: [1.0, 0.0].into(),
|
||||
row2: [0.0, 1.0].into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn transpose(self) -> Self {
|
||||
fn transpose(&self) -> Self {
|
||||
Self {
|
||||
row1: [self[0][0], self[1][0]].into(),
|
||||
row2: [self[0][1], self[1][1]].into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn determinant(&self) -> f32 {
|
||||
self[0][0] * self[1][1] - self[0][1] * self[1][0]
|
||||
}
|
||||
|
||||
fn minor(&self) -> Self {
|
||||
Self {
|
||||
row1: [self[1][1], self[1][0]].into(),
|
||||
row2: [self[0][1], self[0][0]].into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cofactor(&self) -> Self {
|
||||
let mut minor = self.minor();
|
||||
for row in 0..2 {
|
||||
for col in 0..2 {
|
||||
let even = (row + col) % 2 == 0;
|
||||
if even {
|
||||
minor[row][col] = -minor[row][col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
minor
|
||||
}
|
||||
|
||||
fn adjugate(&self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn determinant(self) -> f32 {
|
||||
todo!()
|
||||
}
|
||||
fn inverse(&self) -> Self {
|
||||
let adj = self.adjugate();
|
||||
let det = self.determinant();
|
||||
|
||||
fn minor(self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn cofactor(self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn adjugate(self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn inverse(self) -> Self {
|
||||
todo!()
|
||||
(1.0 / det) * adj
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use crate::math::traits::{Matrix, Vector};
|
||||
use crate::math::Vector4;
|
||||
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq)]
|
||||
pub struct Matrix4 {
|
||||
row1: Vector4,
|
||||
row2: Vector4,
|
||||
row3: Vector4,
|
||||
row4: Vector4,
|
||||
}
|
||||
|
||||
impl Matrix4 {
|
||||
}
|
||||
|
||||
impl Add for Matrix4 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
row1: self.row1 + rhs.row1,
|
||||
row2: self.row2 + rhs.row2,
|
||||
row3: self.row3 + rhs.row3,
|
||||
row4: self.row4 + rhs.row4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Matrix4 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
row1: self.row1 - rhs.row1,
|
||||
row2: self.row2 - rhs.row2,
|
||||
row3: self.row3 - rhs.row3,
|
||||
row4: self.row4 - rhs.row4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Matrix4 {
|
||||
type Output = Self;
|
||||
|
||||
/// Equivalent to multiplying by the reciprocal of `rhs`.
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
let rhs = 1.0 / rhs;
|
||||
self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Matrix4 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Self {
|
||||
row1: self.row1 * rhs,
|
||||
row2: self.row2 * rhs,
|
||||
row3: self.row3 * rhs,
|
||||
row4: self.row4 * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Matrix4 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
let rhs = rhs.transpose();
|
||||
Self {
|
||||
row1: [self.row1.dot(rhs.row1), self.row1.dot(rhs.row2), self.row1.dot(rhs.row3), self.row1.dot(rhs.row4)].into(),
|
||||
row2: [self.row2.dot(rhs.row1), self.row2.dot(rhs.row2), self.row2.dot(rhs.row3), self.row2.dot(rhs.row4)].into(),
|
||||
row3: [self.row3.dot(rhs.row1), self.row3.dot(rhs.row2), self.row3.dot(rhs.row3), self.row3.dot(rhs.row4)].into(),
|
||||
row4: [self.row4.dot(rhs.row1), self.row4.dot(rhs.row2), self.row4.dot(rhs.row3), self.row4.dot(rhs.row4)].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vector4> for Matrix4 {
|
||||
type Output = Vector4;
|
||||
|
||||
fn mul(self, rhs: Vector4) -> Vector4 {
|
||||
Vector4 {
|
||||
x: self.row1.dot(rhs),
|
||||
y: self.row2.dot(rhs),
|
||||
z: self.row3.dot(rhs),
|
||||
w: self.row4.dot(rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Matrix<Vector4> for Matrix4 {
|
||||
fn identity() -> Self {
|
||||
Self {
|
||||
row1: Vector4::new(1.0, 0.0, 0.0, 0.0),
|
||||
row2: Vector4::new(0.0, 1.0, 0.0, 0.0),
|
||||
row3: Vector4::new(0.0, 0.0, 1.0, 0.0),
|
||||
row4: Vector4::new(0.0, 0.0, 0.0, 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn transpose(&self) -> Self {
|
||||
Self {
|
||||
row1: [self.row1.x, self.row2.x, self.row3.x, self.row4.x].into(),
|
||||
row2: [self.row1.y, self.row2.y, self.row3.y, self.row4.y].into(),
|
||||
row3: [self.row1.z, self.row2.z, self.row3.z, self.row4.z].into(),
|
||||
row4: [self.row1.w, self.row2.w, self.row3.w, self.row4.w].into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn determinant(&self) -> f32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn minor(&self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn cofactor(&self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn adjugate(&self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
+22
-10
@@ -1,6 +1,8 @@
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use crate::math::Vector4;
|
||||
|
||||
pub trait Vector: Copy + Default + PartialEq + PartialOrd + Add + Sub + Div<f32, Output = Self> + Mul<f32, Output = Self> {
|
||||
pub trait Vector: Clone + Copy + Default + PartialEq + Add + Sub + PartialOrd
|
||||
+ Div<f32, Output = Self> + Mul<f32, Output = Self> {
|
||||
/// Gets the dot product of the two vectors.
|
||||
fn dot(self, other: Self) -> f32;
|
||||
|
||||
@@ -26,32 +28,42 @@ pub trait Vector: Copy + Default + PartialEq + PartialOrd + Add + Sub + Div<f32,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Matrix: Copy + Default + PartialEq + PartialOrd + Add + Sub
|
||||
+ Div<f32, Output = Self> + Div
|
||||
+ Mul<f32, Output = Self> + Mul {
|
||||
/// # Matrix Trait
|
||||
///
|
||||
/// Requires a few manual implementations, specifically the `identity`, `transpose`, and `determinant`.
|
||||
/// All other information can be found by the trait itself.
|
||||
///
|
||||
/// Note that this trait does not require implementation of the `Div` trait at all - this is because
|
||||
/// matrices do not have division, only multiplication by a scalar value. This doesn't mean that the
|
||||
/// scalar cannot be a decimal value, however.
|
||||
pub trait Matrix<TVec>: Clone + Copy + Default + PartialEq + Add + Sub
|
||||
+ Mul<f32, Output = Self> + Mul<TVec, Output = TVec>
|
||||
+ Mul<Output = Self>
|
||||
where TVec: Vector
|
||||
{
|
||||
/// Creates an identity matrix.
|
||||
fn identity() -> Self;
|
||||
|
||||
/// Gets the transpose of this matrix.
|
||||
fn transpose(self) -> Self;
|
||||
fn transpose(&self) -> Self;
|
||||
|
||||
/// Gets the determinant of this matrix.
|
||||
fn determinant(self) -> f32;
|
||||
fn determinant(&self) -> f32;
|
||||
|
||||
/// Finds the minor of this matrix. The element at location (`i`, `j`) of the minor is the
|
||||
/// determinant of the submatrix obtained by removing row `i` and column `j` in the original
|
||||
/// matrix.
|
||||
fn minor(self) -> Self;
|
||||
fn minor(&self) -> Self;
|
||||
|
||||
/// Finds the cofactor of this matrix. The element at location (`i`, `j`) of the cofactor is
|
||||
/// defined as -1^ij * Mij, where Mij is the element at (`i`, `j`) in the minor of the matrix.
|
||||
/// This is effectively flipping the sign of every other element in the minor of this matrix.
|
||||
fn cofactor(self) -> Self;
|
||||
fn cofactor(&self) -> Self;
|
||||
|
||||
/// Gets the adjugate of this matrix. This is the transpose of the cofactor of this matrix.
|
||||
fn adjugate(self) -> Self;
|
||||
fn adjugate(&self) -> Self;
|
||||
|
||||
/// Gets the inverse of this matrix. Note that this is a very expensive operation and should
|
||||
/// be used as sparingly as possible.
|
||||
fn inverse(self) -> Self;
|
||||
fn inverse(&self) -> Self;
|
||||
}
|
||||
+22
-1
@@ -1,4 +1,4 @@
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::ops::{Add, Div, Mul, Sub, Index, IndexMut};
|
||||
use crate::math::traits::Vector;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||
@@ -17,6 +17,27 @@ impl Vector2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Vector2 {
|
||||
type Output = f32;
|
||||
fn index(&self, idx: usize) -> &f32 {
|
||||
match idx {
|
||||
0 => &self.x,
|
||||
1 => &self.y,
|
||||
_ => panic!("Index out of bounds"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Vector2 {
|
||||
fn index_mut(&mut self, idx: usize) -> &mut f32 {
|
||||
match idx {
|
||||
0 => &mut self.x,
|
||||
1 => &mut self.y,
|
||||
_ => panic!("Index out of bounds"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vector2 {
|
||||
type Output = Self;
|
||||
|
||||
|
||||
+53
-7
@@ -11,8 +11,24 @@ pub struct Vector4 {
|
||||
}
|
||||
|
||||
impl Vector4 {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub fn new(
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
w: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
x, y, z, w
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one() -> Self {
|
||||
Self {
|
||||
x: 1.0,
|
||||
y: 1.0,
|
||||
z: 1.0,
|
||||
w: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xyzw(&self) -> (f32, f32, f32, f32) {
|
||||
@@ -83,6 +99,20 @@ impl Mul<Vector4> for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Vector4 {
|
||||
type Output = Self;
|
||||
|
||||
/// Computes the Hadamard product of the two vectors.
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
x: self.x * rhs.x,
|
||||
y: self.y * rhs.y,
|
||||
z: self.z * rhs.z,
|
||||
w: self.w * rhs.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Vector4 {
|
||||
type Output = Self;
|
||||
|
||||
@@ -119,12 +149,28 @@ impl Vector for Vector4 {
|
||||
}
|
||||
|
||||
impl From<[f32; 4]> for Vector4 {
|
||||
fn from(v: [f32; 4]) -> Self {
|
||||
fn from([x, y, z, w]: [f32; 4]) -> Self {
|
||||
Self {
|
||||
x: v[0],
|
||||
y: v[1],
|
||||
z: v[2],
|
||||
w: v[3],
|
||||
x, y, z, w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 3]> for Vector4 {
|
||||
/// Creates a homogenous `Vector4` from a 3D positional vector.
|
||||
fn from([x, y, z]: [f32; 3]) -> Self {
|
||||
Self {
|
||||
x, y, z,
|
||||
w: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vector3> for Vector4 {
|
||||
fn from(Vector3 { x, y, z }: Vector3) -> Self {
|
||||
Self {
|
||||
x, y, z,
|
||||
w: 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#version 460
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
layout(location = 0) in vec3 position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
Reference in new issue
Block a user