From 8e1e4667665314609aee2e3e1d3a6206579f848b Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 18 Oct 2022 22:30:05 -0700 Subject: [PATCH 01/67] [add] basic vector operations. --- lambda/src/lib.rs | 1 + lambda/src/math/mod.rs | 54 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 2a51855e..97c2f056 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,3 +1,4 @@ pub mod components; pub mod core; +mod math; pub mod runtimes; diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index b2550100..015172e7 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1 +1,53 @@ -type Vec2 = (f32, f32); +//! Very simple math types built strictly from primitive types. + +pub type Vec2 = (f32, f32); + +pub type Vec3 = (f32, f32, f32); + +pub fn dot(a: Vec2, b: Vec2) -> f32 { + a.0 * b.0 + a.1 * b.1 +} + +pub fn cross(a: Vec3, b: Vec3) -> Vec3 { + ( + a.1 * b.2 - a.2 * b.1, + a.2 * b.0 - a.0 * b.2, + a.0 * b.1 - a.1 * b.0, + ) +} + +fn length(a: Vec2) -> f32 { + (a.0 * a.0 + a.1 * a.1).sqrt() +} + +fn normalize(a: Vec2) -> Vec2 { + let l = length(a); + (a.0 / l, a.1 / l) +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_dot() { + assert_eq!(super::dot((1.0, 2.0), (3.0, 4.0)), 11.0); + } + + #[test] + fn test_cross() { + assert_eq!( + super::cross((1.0, 2.0, 3.0), (4.0, 5.0, 6.0)), + (-3.0, 6.0, -3.0) + ); + } + + #[test] + fn test_length() { + assert_eq!(super::length((3.0, 4.0)), 5.0); + } + + #[test] + fn test_normalize() { + assert_eq!(super::normalize((3.0, 4.0)), (0.6, 0.8)); + } +} From 36f9f5caa50b1f5a010f105d466ab779572a2ce2 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 18 Oct 2022 22:38:26 -0700 Subject: [PATCH 02/67] [rename] types. --- lambda/src/math/mod.rs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 015172e7..39372930 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,14 +1,30 @@ //! Very simple math types built strictly from primitive types. -pub type Vec2 = (f32, f32); +// ------------------------------- VECTOR2 ----------------------------------- +pub type Vector2 = (f32, f32); -pub type Vec3 = (f32, f32, f32); - -pub fn dot(a: Vec2, b: Vec2) -> f32 { +/// Compute the dot product of two `Vec2`. +pub fn dot(a: Vector2, b: Vector2) -> f32 { a.0 * b.0 + a.1 * b.1 } -pub fn cross(a: Vec3, b: Vec3) -> Vec3 { +/// Compute the length of a vector. +fn length(a: Vector2) -> f32 { + (a.0 * a.0 + a.1 * a.1).sqrt() +} + +/// Normalized vector. +fn normalize(a: Vector2) -> Vector2 { + let l = length(a); + (a.0 / l, a.1 / l) +} + +// ------------------------------- VECTOR3 ----------------------------------- + +pub type Vector3 = (f32, f32, f32); + +/// Compute the cross product of two `Vec3`. +pub fn cross(a: Vector3, b: Vector3) -> Vector3 { ( a.1 * b.2 - a.2 * b.1, a.2 * b.0 - a.0 * b.2, @@ -16,14 +32,7 @@ pub fn cross(a: Vec3, b: Vec3) -> Vec3 { ) } -fn length(a: Vec2) -> f32 { - (a.0 * a.0 + a.1 * a.1).sqrt() -} - -fn normalize(a: Vec2) -> Vec2 { - let l = length(a); - (a.0 / l, a.1 / l) -} +// ---------------------------------- TESTS ----------------------------------- #[cfg(test)] mod tests { From 5f3e2bfb0fa696630add6f47e24479ca1768930e Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 21 Oct 2022 18:06:15 -0700 Subject: [PATCH 03/67] [update] internal modules to only be acessible via crates, fix rendering issue with vulkan, and some other minor fixes. --- crates/lambda-platform/Cargo.toml | 4 +++ crates/lambda-platform/src/gfx/api.rs | 3 +- crates/lambda-platform/src/gfx/assembler.rs | 2 +- crates/lambda-platform/src/gfx/fence.rs | 2 +- crates/lambda-platform/src/gfx/framebuffer.rs | 2 +- crates/lambda-platform/src/gfx/gpu.rs | 29 ++++++++++--------- crates/lambda-platform/src/gfx/surface.rs | 11 ++++--- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/crates/lambda-platform/Cargo.toml b/crates/lambda-platform/Cargo.toml index c69e5ecd..fd366f50 100644 --- a/crates/lambda-platform/Cargo.toml +++ b/crates/lambda-platform/Cargo.toml @@ -33,6 +33,10 @@ gfx-with-metal=["dep:gfx-backend-metal"] gfx-with-dx11=["dep:gfx-backend-dx11"] gfx-with-dx12=["dep:gfx-backend-dx12"] +[profile.dev] +crate-type = ["cdylib", "rlib"] +incremental = true + [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies.gfx-platform-backend] package = "gfx-backend-gl" version = "=0.9.0" diff --git a/crates/lambda-platform/src/gfx/api.rs b/crates/lambda-platform/src/gfx/api.rs index 8efabab0..3fe3fa65 100644 --- a/crates/lambda-platform/src/gfx/api.rs +++ b/crates/lambda-platform/src/gfx/api.rs @@ -14,6 +14,5 @@ cfg_if::cfg_if! { pub use gfx_backend_dx12 as RenderingAPI; } else if #[cfg(feature = "detect-platform")] { pub use gfx_platform_backend as RenderingAPI; - } else { - } + } else {} } diff --git a/crates/lambda-platform/src/gfx/assembler.rs b/crates/lambda-platform/src/gfx/assembler.rs index f2488dd1..3b068344 100644 --- a/crates/lambda-platform/src/gfx/assembler.rs +++ b/crates/lambda-platform/src/gfx/assembler.rs @@ -52,7 +52,7 @@ impl<'shader, RenderBackend: gfx_hal::Backend> /// Internal functions for the primitive assembler. User applications most /// likely should not use these functions directly nor should they need to. -pub mod internal { +pub(crate) mod internal { #[inline] pub fn into_primitive_assembler<'shader, RenderBackend: gfx_hal::Backend>( primitive_assembler: super::PrimitiveAssembler<'shader, RenderBackend>, diff --git a/crates/lambda-platform/src/gfx/fence.rs b/crates/lambda-platform/src/gfx/fence.rs index 29963cc8..3a3259a1 100644 --- a/crates/lambda-platform/src/gfx/fence.rs +++ b/crates/lambda-platform/src/gfx/fence.rs @@ -119,7 +119,7 @@ impl RenderSubmissionFence { } } -pub mod internal { +pub(crate) mod internal { /// Retrieve the underlying submission fence. pub fn mutable_fence_for( fence: &mut super::RenderSubmissionFence, diff --git a/crates/lambda-platform/src/gfx/framebuffer.rs b/crates/lambda-platform/src/gfx/framebuffer.rs index 329c290e..906270ca 100644 --- a/crates/lambda-platform/src/gfx/framebuffer.rs +++ b/crates/lambda-platform/src/gfx/framebuffer.rs @@ -62,7 +62,7 @@ impl FramebufferBuilder { /// Internal functions to work with gfx-hal framebuffers directly. Applications /// should not need to use these functions directly. -pub mod internal { +pub(crate) mod internal { pub fn frame_buffer_for( frame_buffer: &super::Framebuffer, ) -> &RenderBackend::Framebuffer { diff --git a/crates/lambda-platform/src/gfx/gpu.rs b/crates/lambda-platform/src/gfx/gpu.rs index 4519fc71..f575b5c5 100644 --- a/crates/lambda-platform/src/gfx/gpu.rs +++ b/crates/lambda-platform/src/gfx/gpu.rs @@ -1,6 +1,5 @@ use gfx_hal::{ adapter::Adapter, - device::Device, prelude::{ PhysicalDevice, QueueFamily, @@ -9,7 +8,6 @@ use gfx_hal::{ Queue, QueueGroup, }, - window::Extent2D, }; #[cfg(test)] use mockall::automock; @@ -136,16 +134,21 @@ impl Gpu { vec![super::command::internal::command_buffer_for(command_buffer)] .into_iter(); unsafe { - self.queue_group.queues[0].submit( - commands, - vec![].into_iter(), - // TODO(vmarcella): This was needed to allow the push constants to - // properly render to the screen. Look into a better way to do this. - signal_semaphores.into_iter().map(|semaphore| { - return super::fence::internal::semaphore_for(semaphore); - }), - Some(super::fence::internal::mutable_fence_for(fence)), - ); + self + .queue_group + .queues + .first_mut() + .expect("Couldn't find the primary queue to submit commands to. ") + .submit( + commands, + vec![].into_iter(), + // TODO(vmarcella): This was needed to allow the push constants to + // properly render to the screen. Look into a better way to do this. + signal_semaphores.into_iter().map(|semaphore| { + return super::fence::internal::semaphore_for(semaphore); + }), + Some(super::fence::internal::mutable_fence_for(fence)), + ); } } @@ -210,7 +213,7 @@ mod tests { // --------------------------------- GPU INTERNALS ----------------------------- -pub mod internal { +pub(crate) mod internal { use super::Gpu; /// Retrieves the gfx_hal logical device for a given GPU. diff --git a/crates/lambda-platform/src/gfx/surface.rs b/crates/lambda-platform/src/gfx/surface.rs index 8bd37170..75188971 100644 --- a/crates/lambda-platform/src/gfx/surface.rs +++ b/crates/lambda-platform/src/gfx/surface.rs @@ -69,7 +69,7 @@ pub struct Surface { frame_buffer_attachment: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Swapchain { config: gfx_hal::window::SwapchainConfig, format: gfx_hal::format::Format, @@ -118,6 +118,10 @@ impl Surface { } } + pub fn needs_swapchain(&self) -> bool { + return self.swapchain_is_valid; + } + /// Remove the swapchain configuration that this surface used on this given /// GPU. pub fn remove_swapchain(&mut self, gpu: &Gpu) { @@ -129,11 +133,6 @@ impl Surface { } } - /// private function to invalidate the surface swapchain. - fn invalidate_swapchain(&mut self) { - self.swapchain_is_valid = false; - } - /// Destroy the current surface and it's underlying resources. pub fn destroy(self, instance: &Instance) { println!("Destroying the surface: {}", self.name); From d963ca3cdcfd2fc04579191786e2023271ce1f8d Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 21 Oct 2022 18:12:44 -0700 Subject: [PATCH 04/67] [fix] high level rendering logic. --- lambda/src/core/events.rs | 12 ++++++++ lambda/src/core/render/command.rs | 5 +--- lambda/src/core/render/mod.rs | 50 +++++++++++++++++-------------- lambda/src/core/render/window.rs | 6 ++++ lambda/src/math/mod.rs | 2 +- lambda/src/runtimes/mod.rs | 15 ++++++---- tools/lambda_rs_demo/src/main.rs | 1 + 7 files changed, 58 insertions(+), 33 deletions(-) diff --git a/lambda/src/core/events.rs b/lambda/src/core/events.rs index ca9571d4..38b15442 100644 --- a/lambda/src/core/events.rs +++ b/lambda/src/core/events.rs @@ -41,6 +41,14 @@ pub enum KeyEvent { }, } +#[derive(Debug, Clone)] +pub enum Mouse { + MouseMoved { x: f32, y: f32, dx: f32, dy: f32 }, + MouseWheelPressed { x: f32, y: f32, button: u32 }, + MousePressed { x: f32, y: f32, button: u32 }, + MouseReleased { x: f32, y: f32, button: u32 }, +} + /// Generic Event Enum which encapsulates all possible events that will be /// emitted by the LambdaKernel #[derive(Debug, Clone)] @@ -61,4 +69,8 @@ pub enum Events { event: KeyEvent, issued_at: Instant, }, + Mouse { + event: Mouse, + issued_at: Instant, + }, } diff --git a/lambda/src/core/render/command.rs b/lambda/src/core/render/command.rs index cbc3391a..aacb3e90 100644 --- a/lambda/src/core/render/command.rs +++ b/lambda/src/core/render/command.rs @@ -1,7 +1,4 @@ -use std::{ - ops::Range, - rc::Rc, -}; +use std::ops::Range; use lambda_platform::gfx::viewport::ViewPort as PlatformViewPort; diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index cc5fe470..50392372 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -85,14 +85,6 @@ impl RenderContextBuilder { // Create the image extent and initial frame buffer attachment description // for rendering. let (width, height) = window.dimensions(); - let swapchain = SwapchainBuilder::new() - .with_size(width, height) - .build(&gpu, &surface); - - Rc::get_mut(&mut surface) - .expect("Failed to get mutable reference to surface.") - .apply_swapchain(&gpu, swapchain, self.render_timeout) - .expect("Failed to apply the swapchain to the surface."); return RenderContext { name, @@ -227,10 +219,18 @@ impl RenderContext { .with_size(width, height) .build(&self.gpu, &self.surface); - Rc::get_mut(&mut self.surface) - .expect("Failed to get mutable reference to surface.") - .apply_swapchain(&self.gpu, swapchain, 1_000_000_000) - .expect("Failed to apply the swapchain to the surface."); + if self.surface.needs_swapchain() { + Rc::get_mut(&mut self.surface) + .expect("Failed to get mutable reference to surface.") + .apply_swapchain(&self.gpu, swapchain, 1_000_000_000) + .expect("Failed to apply the swapchain to the surface."); + } + + self + .submission_fence + .as_mut() + .expect("Failed to get the submission fence.") + .block_until_ready(&mut self.gpu, None); let platform_command_list = commands .into_iter() @@ -240,7 +240,13 @@ impl RenderContext { let mut command_buffer = CommandBufferBuilder::new(CommandBufferLevel::Primary) .with_feature(CommandBufferFeatures::ResetEverySubmission) - .build(self.command_pool.as_mut().unwrap(), "primary"); + .build( + self + .command_pool + .as_mut() + .expect("No command pool to create a buffer from"), + "primary", + ); // Start recording commands, issue the high level render commands // that came from an application, and then submit the commands to the GPU @@ -252,10 +258,7 @@ impl RenderContext { self.gpu.submit_command_buffer( &mut command_buffer, vec![self.render_semaphore.as_ref().unwrap()], - self - .submission_fence - .as_mut() - .expect("Failed to get mutable reference to submission fence."), + self.submission_fence.as_mut().unwrap(), ); self @@ -281,10 +284,13 @@ impl RenderContext { let swapchain = SwapchainBuilder::new() .with_size(width, height) .build(&self.gpu, &self.surface); - Rc::get_mut(&mut self.surface) - .expect("Failed to acquire the surface while attempting to resize.") - .apply_swapchain(&self.gpu, swapchain, 1_000_000_000) - .expect("Failed to apply the swapchain to the surface while attempting to resize."); + + if self.surface.needs_swapchain() { + Rc::get_mut(&mut self.surface) + .expect("Failed to get mutable reference to surface.") + .apply_swapchain(&self.gpu, swapchain, 1_000_000_000) + .expect("Failed to apply the swapchain to the surface."); + } } pub fn get_render_pass(&self, id: ResourceId) -> &RenderPass { @@ -298,7 +304,7 @@ impl RenderContext { type PlatformRenderCommand = Command; -pub mod internal { +pub(crate) mod internal { use std::rc::Rc; use lambda_platform::gfx::api::RenderingAPI as RenderContext; diff --git a/lambda/src/core/render/window.rs b/lambda/src/core/render/window.rs index c3f30b1d..ea590a15 100644 --- a/lambda/src/core/render/window.rs +++ b/lambda/src/core/render/window.rs @@ -10,6 +10,7 @@ use crate::core::events::Events; pub struct WindowBuilder { name: String, dimensions: (u32, u32), + vsync: bool, } impl WindowBuilder { @@ -21,6 +22,7 @@ impl WindowBuilder { return Self { name: String::from("Window"), dimensions: (480, 360), + vsync: false, }; } @@ -36,6 +38,10 @@ impl WindowBuilder { return self; } + pub fn with_vsync(mut self, vsync: bool) -> Self { + return self; + } + // TODO(vmarcella): Remove new call for window and construct the window directly. pub fn build(self, event_loop: &mut Loop) -> Window { return Window::new(self.name.as_str(), self.dimensions, event_loop); diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 39372930..0c6bab57 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,4 +1,4 @@ -//! Very simple math types built strictly from primitive types. +//! Very simple math types built strictly off of Rust primitive types. // ------------------------------- VECTOR2 ----------------------------------- pub type Vector2 = (f32, f32); diff --git a/lambda/src/runtimes/mod.rs b/lambda/src/runtimes/mod.rs index 0c0acceb..18d2c644 100644 --- a/lambda/src/runtimes/mod.rs +++ b/lambda/src/runtimes/mod.rs @@ -237,7 +237,10 @@ impl Runtime for GenericRuntime { delta, phase, modifiers, - } => {} + } => { + + + } WinitWindowEvent::MouseInput { device_id, state, @@ -268,14 +271,14 @@ impl Runtime for GenericRuntime { component.on_update(duration); } - window.redraw(); - } - WinitEvent::RedrawRequested(_) => { + let active_render_context = active_render_context.as_mut().expect("Couldn't get the active render context. "); for component in &mut component_stack { - let commands = component.on_render(active_render_context.as_mut().unwrap()); - active_render_context.as_mut().unwrap().render(commands); + let commands = component.on_render(active_render_context); + active_render_context.render(commands); } } + WinitEvent::RedrawRequested(_) => { + } WinitEvent::NewEvents(_) => {} WinitEvent::DeviceEvent { device_id, event } => {} WinitEvent::UserEvent(lambda_event) => match lambda_event { diff --git a/tools/lambda_rs_demo/src/main.rs b/tools/lambda_rs_demo/src/main.rs index ca157f96..a842248b 100644 --- a/tools/lambda_rs_demo/src/main.rs +++ b/tools/lambda_rs_demo/src/main.rs @@ -102,6 +102,7 @@ impl Component for DemoComponent { println!("Component detached: {:?}", name); } }, + _ => {} } } From 4f03d83e22a8c87021070a385284201a5a8a8813 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 21 Oct 2022 23:58:09 -0700 Subject: [PATCH 05/67] [add] various minor changes. --- crates/lambda-platform/src/gfx/command.rs | 4 +++- crates/lambda-platform/src/gfx/gpu.rs | 4 ++++ lambda/src/core/component.rs | 4 ++-- lambda/src/core/render/command.rs | 15 ++++++++------- lambda/src/core/render/viewport.rs | 4 ++-- lambda/src/runtimes/mod.rs | 2 +- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/lambda-platform/src/gfx/command.rs b/crates/lambda-platform/src/gfx/command.rs index 8ffb9879..528f39cc 100644 --- a/crates/lambda-platform/src/gfx/command.rs +++ b/crates/lambda-platform/src/gfx/command.rs @@ -184,7 +184,9 @@ impl<'command_pool, RenderBackend: gfx_hal::Backend> offset, bytes.as_slice(), ), - Command::Draw { vertices } => self.command_buffer.draw(vertices, 0..1), + Command::Draw { vertices } => { + self.command_buffer.draw(vertices.clone(), 0..1) + } Command::EndRecording => self.command_buffer.finish(), } } diff --git a/crates/lambda-platform/src/gfx/gpu.rs b/crates/lambda-platform/src/gfx/gpu.rs index f575b5c5..997f1bc6 100644 --- a/crates/lambda-platform/src/gfx/gpu.rs +++ b/crates/lambda-platform/src/gfx/gpu.rs @@ -170,6 +170,10 @@ impl Gpu { }; if result.is_err() { + println!( + "Failed to present to the surface: {:?}", + result.err().unwrap() + ); surface.remove_swapchain(self); return Err( "Rendering failed. Swapchain for the surface needs to be reconfigured.", diff --git a/lambda/src/core/component.rs b/lambda/src/core/component.rs index b5cc1f3b..7e384a22 100644 --- a/lambda/src/core/component.rs +++ b/lambda/src/core/component.rs @@ -16,10 +16,10 @@ pub trait Component { fn on_detach(&mut self, render_context: &mut RenderContext); fn on_event(&mut self, event: Events); - /// When the application state should perform logic updates. + /// The update function is called every frame and is used to update + /// the state of the component. fn on_update(&mut self, last_frame: &Duration); - /// When the application state should perform rendering. fn on_render( &mut self, render_context: &mut RenderContext, diff --git a/lambda/src/core/render/command.rs b/lambda/src/core/render/command.rs index aacb3e90..1545edbf 100644 --- a/lambda/src/core/render/command.rs +++ b/lambda/src/core/render/command.rs @@ -9,6 +9,7 @@ use super::{ }; /// Commands that are used to render a frame within the RenderContext. +#[derive(Debug, Clone)] pub enum RenderCommand { /// sets the viewports for the render context. SetViewports { @@ -57,7 +58,7 @@ impl RenderCommand { start_at, viewports: viewports .into_iter() - .map(|viewport| viewport.into_gfx_viewport()) + .map(|viewport| viewport.clone_gfx_viewport()) .collect::>(), }, RenderCommand::SetScissors { @@ -67,7 +68,7 @@ impl RenderCommand { start_at, viewports: viewports .into_iter() - .map(|viewport| viewport.into_gfx_viewport()) + .map(|viewport| viewport.clone_gfx_viewport()) .collect::>(), }, RenderCommand::BeginRenderPass { @@ -88,7 +89,7 @@ impl RenderCommand { .into_gfx_render_pass(), surface: surface.clone(), frame_buffer: frame_buffer.clone(), - viewport: viewport.into_gfx_viewport(), + viewport: viewport.clone_gfx_viewport(), } } RenderCommand::EndRenderPass => PlatformRenderCommand::EndRenderPass, @@ -114,11 +115,11 @@ impl RenderCommand { .into_platform_render_pipeline(), stage, offset, - bytes, + bytes: bytes.clone(), + }, + RenderCommand::Draw { vertices } => PlatformRenderCommand::Draw { + vertices: vertices.clone(), }, - RenderCommand::Draw { vertices } => { - PlatformRenderCommand::Draw { vertices } - } }; } } diff --git a/lambda/src/core/render/viewport.rs b/lambda/src/core/render/viewport.rs index 053f4e44..22156937 100644 --- a/lambda/src/core/render/viewport.rs +++ b/lambda/src/core/render/viewport.rs @@ -8,8 +8,8 @@ pub struct Viewport { impl Viewport { /// Convert the viewport into a gfx platform viewport. // TODO(vmarcella): implement this using Into - pub fn into_gfx_viewport(self) -> gfx::viewport::ViewPort { - return self.viewport; + pub(crate) fn clone_gfx_viewport(&self) -> gfx::viewport::ViewPort { + return self.viewport.clone(); } } diff --git a/lambda/src/runtimes/mod.rs b/lambda/src/runtimes/mod.rs index 18d2c644..33665234 100644 --- a/lambda/src/runtimes/mod.rs +++ b/lambda/src/runtimes/mod.rs @@ -303,7 +303,7 @@ impl Runtime for GenericRuntime { }, WinitEvent::Suspended => {} WinitEvent::Resumed => {} - WinitEvent::RedrawEventsCleared => {} + WinitEvent::RedrawEventsCleared => { } WinitEvent::LoopDestroyed => { active_render_context .take() From c3606fe2b7ec280e34bb57a8f8c674df3c4c20bd Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 22 Oct 2022 00:13:43 -0700 Subject: [PATCH 06/67] [update] event loop to send events to the components immediately. --- lambda/src/runtimes/mod.rs | 93 +++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/lambda/src/runtimes/mod.rs b/lambda/src/runtimes/mod.rs index 33665234..25dad601 100644 --- a/lambda/src/runtimes/mod.rs +++ b/lambda/src/runtimes/mod.rs @@ -148,16 +148,15 @@ impl Runtime for GenericRuntime { let mut current_frame = Instant::now(); event_loop.run_forever(move |event, _, control_flow| { - match event { + let mapped_event: Option = match event { WinitEvent::WindowEvent { event, .. } => match event { WinitWindowEvent::CloseRequested => { // Issue a Shutdown event to deallocate resources and clean up. - publisher.publish_event(Events::Runtime { + *control_flow = ControlFlow::Exit; + Some(Events::Runtime { event: RuntimeEvent::Shutdown, issued_at: Instant::now(), - }); - - *control_flow = ControlFlow::Exit; + }) } WinitWindowEvent::Resized(dims) => { active_render_context @@ -165,7 +164,7 @@ impl Runtime for GenericRuntime { .unwrap() .resize(dims.width, dims.height); - publisher.publish_event(Events::Window { + Some(Events::Window { event: WindowEvent::Resize { width: dims.width, height: dims.height, @@ -179,7 +178,7 @@ impl Runtime for GenericRuntime { .unwrap() .resize(new_inner_size.width, new_inner_size.height); - publisher.publish_event(Events::Window { + Some(Events::Window { event: WindowEvent::Resize { width: new_inner_size.width, height: new_inner_size.height, @@ -187,20 +186,20 @@ impl Runtime for GenericRuntime { issued_at: Instant::now(), }) } - WinitWindowEvent::Moved(_) => {} - WinitWindowEvent::Destroyed => {} - WinitWindowEvent::DroppedFile(_) => {} - WinitWindowEvent::HoveredFile(_) => {} - WinitWindowEvent::HoveredFileCancelled => {} - WinitWindowEvent::ReceivedCharacter(_) => {} - WinitWindowEvent::Focused(_) => {} + WinitWindowEvent::Moved(_) => {None} + WinitWindowEvent::Destroyed => {None} + WinitWindowEvent::DroppedFile(_) => {None} + WinitWindowEvent::HoveredFile(_) => {None} + WinitWindowEvent::HoveredFileCancelled => {None} + WinitWindowEvent::ReceivedCharacter(_) => {None} + WinitWindowEvent::Focused(_) => {None} WinitWindowEvent::KeyboardInput { device_id: _, input, is_synthetic, } => match (input.state, is_synthetic) { (ElementState::Pressed, false) => { - publisher.publish_event(Events::Keyboard { + Some(Events::Keyboard { event: KeyEvent::KeyPressed { scan_code: input.scancode, virtual_key: input.virtual_keycode, @@ -209,7 +208,7 @@ impl Runtime for GenericRuntime { }) } (ElementState::Released, false) => { - publisher.publish_event(Events::Keyboard { + Some(Events::Keyboard { event: KeyEvent::KeyReleased { scan_code: input.scancode, virtual_key: input.virtual_keycode, @@ -222,44 +221,42 @@ impl Runtime for GenericRuntime { "[WARN] Unhandled synthetic keyboard event: {:?}", input ); + None } }, - WinitWindowEvent::ModifiersChanged(_) => {} + WinitWindowEvent::ModifiersChanged(_) => {None} WinitWindowEvent::CursorMoved { device_id, position, modifiers, - } => {} - WinitWindowEvent::CursorEntered { device_id } => {} - WinitWindowEvent::CursorLeft { device_id } => {} + } => {None} + WinitWindowEvent::CursorEntered { device_id } => {None} + WinitWindowEvent::CursorLeft { device_id } => {None} WinitWindowEvent::MouseWheel { device_id, delta, phase, modifiers, - } => { - - - } + } => { None } WinitWindowEvent::MouseInput { device_id, state, button, modifiers, - } => {} + } => {None} WinitWindowEvent::TouchpadPressure { device_id, pressure, stage, - } => {} + } => {None} WinitWindowEvent::AxisMotion { device_id, axis, value, - } => {} - WinitWindowEvent::Touch(_) => {} - WinitWindowEvent::ThemeChanged(_) => {} - _ => {} + } => {None} + WinitWindowEvent::Touch(_) => {None} + WinitWindowEvent::ThemeChanged(_) => {None} + _ => {None} }, WinitEvent::MainEventsCleared => { let last_frame = current_frame.clone(); @@ -276,11 +273,11 @@ impl Runtime for GenericRuntime { let commands = component.on_render(active_render_context); active_render_context.render(commands); } + None } - WinitEvent::RedrawRequested(_) => { - } - WinitEvent::NewEvents(_) => {} - WinitEvent::DeviceEvent { device_id, event } => {} + WinitEvent::RedrawRequested(_) => { None } + WinitEvent::NewEvents(_) => {None} + WinitEvent::DeviceEvent { device_id, event } => {None} WinitEvent::UserEvent(lambda_event) => match lambda_event { Events::Runtime { event, issued_at } => match event { RuntimeEvent::Initialized => { @@ -288,22 +285,20 @@ impl Runtime for GenericRuntime { for component in &mut component_stack { component.on_attach(active_render_context.as_mut().unwrap()); } + None } RuntimeEvent::Shutdown => { for component in &mut component_stack { component.on_detach(active_render_context.as_mut().unwrap()); } + None } }, - _ => { - for component in &mut component_stack { - component.on_event(lambda_event.clone()); - } - } + _ => { None} }, - WinitEvent::Suspended => {} - WinitEvent::Resumed => {} - WinitEvent::RedrawEventsCleared => { } + WinitEvent::Suspended => {None} + WinitEvent::Resumed => {None} + WinitEvent::RedrawEventsCleared => { None } WinitEvent::LoopDestroyed => { active_render_context .take() @@ -311,8 +306,22 @@ impl Runtime for GenericRuntime { .destroy(); println!("[INFO] All resources were successfully deleted."); + None + } + }; + + match mapped_event { + Some(event) => { + for component in &mut component_stack { + component.on_event(event.clone()); + } } + None => {} } + + + + }); } From 71ce73d8e17b811d4037279ab0e29b6104bb4731 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 4 Nov 2022 16:53:31 -0700 Subject: [PATCH 07/67] [add] a new vulkan push constants demo & add a translation function for mat4s. --- lambda/examples/push_constants.rs | 86 +++++++++++++++++++++++++++ lambda/src/core/render/render_pass.rs | 2 +- lambda/src/core/render/shader.rs | 13 +++- lambda/src/lib.rs | 2 +- lambda/src/math/mod.rs | 35 +++++++++++ 5 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 lambda/examples/push_constants.rs diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs new file mode 100644 index 00000000..18d4806d --- /dev/null +++ b/lambda/examples/push_constants.rs @@ -0,0 +1,86 @@ +use lambda::{ + core::{ + component::Component, + render::shader, + }, + math, + runtimes::GenericRuntimeBuilder, +}; + +const VERTEX_SHADER_SOURCE: &str = r#" +#version 450 + +layout (location = 0) in vec2 vertex_position; +layout (location = 1) in vec3 vertex_normal; +layout (location = 2) in vec3 vertex_color; + +layout (location = 0) out vec3 frag_color; + +layout ( push_constant ) uniform PushConstant { + vec4 data; + mat4 render_matrix; +} push_constants; + +void main() { + gl_Position = push_constants.render_matrix * vec4(vertex_position, 0.0, 1.0); + frag_color = vertex_color; +} + +"#; + +struct PushConstantsExample {} + +impl Component for PushConstantsExample { + fn on_attach( + &mut self, + render_context: &mut lambda::core::render::RenderContext, + ) { + let shader = shader::ShaderBuilder::new().build(); + } + + fn on_detach( + &mut self, + render_context: &mut lambda::core::render::RenderContext, + ) { + todo!() + } + + fn on_event(&mut self, event: lambda::core::events::Events) { + todo!() + } + + fn on_update(&mut self, last_frame: &std::time::Duration) { + todo!() + } + + fn on_render( + &mut self, + render_context: &mut lambda::core::render::RenderContext, + ) -> Vec { + todo!() + } +} + +impl Default for PushConstantsExample { + fn default() -> Self { + return Self {}; + } +} + +fn main() { + let runtime = GenericRuntimeBuilder::new("Multiple Triangles Demo") + .with_renderer_configured_as(move |render_context_builder| { + return render_context_builder.with_render_timeout(1_000_000_000); + }) + .with_window_configured_as(move |window_builder| { + return window_builder + .with_dimensions(800, 600) + .with_name("Triangles"); + }) + .with_component(move |runtime, triangles: PushConstantsExample| { + return (runtime, triangles); + }) + .build(); + + start_runtime(runtime); +} diff --git a/lambda/src/core/render/render_pass.rs b/lambda/src/core/render/render_pass.rs index 733da84d..fa31cc61 100644 --- a/lambda/src/core/render/render_pass.rs +++ b/lambda/src/core/render/render_pass.rs @@ -46,7 +46,7 @@ impl RenderPassBuilder { } } -pub mod internal { +pub(crate) mod internal { use crate::core::render::internal::RenderBackend; /// Converts a render pass into a platform render pass. diff --git a/lambda/src/core/render/shader.rs b/lambda/src/core/render/shader.rs index c4eeaa13..52e733ea 100644 --- a/lambda/src/core/render/shader.rs +++ b/lambda/src/core/render/shader.rs @@ -6,7 +6,18 @@ pub use lambda_platform::shaderc::{ VirtualShader, }; -/// Reusable shader builder that utilizes a lower level platform +#[macro_export] +macro_rules! vertex_shader { + ($source:ident) => { + VirtualShader::Source { + source: String::from($stringify!($source)), + kind: ShaderKind::Vertex, + name: String::from("vertex-shader"), + entry_point: String::from("main"), + } + }; +} + pub struct ShaderBuilder { compiler: ShaderCompiler, } diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 97c2f056..f622d8f1 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,4 +1,4 @@ pub mod components; pub mod core; -mod math; +pub mod math; pub mod runtimes; diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 0c6bab57..7f0979c6 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -32,6 +32,41 @@ pub fn cross(a: Vector3, b: Vector3) -> Vector3 { ) } +// ------------------------------- VECTOR4 ----------------------------------- +pub type Vector4 = (f32, f32, f32, f32); + +// ------------------------------- MATRIX4 ----------------------------------- + +pub type Matrix4 = (Vector, Vector, Vector, Vector); + +pub fn translate( + matrix: Matrix4, + vector: Vector3, +) -> Matrix4 { + ( + ( + matrix.0 .0 + vector.0, + matrix.0 .1 + vector.1, + matrix.0 .2 + vector.2, + ), + ( + matrix.1 .0 + vector.0, + matrix.1 .1 + vector.1, + matrix.1 .2 + vector.2, + ), + ( + matrix.2 .0 + vector.0, + matrix.2 .1 + vector.1, + matrix.2 .2 + vector.2, + ), + ( + matrix.3 .0 + vector.0, + matrix.3 .1 + vector.1, + matrix.3 .2 + vector.2, + ), + ) +} + // ---------------------------------- TESTS ----------------------------------- #[cfg(test)] From 6b7394262b735c40ee2722174f6999659166a401 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 13 Nov 2022 12:14:52 -0800 Subject: [PATCH 08/67] [add] vector trait. --- lambda/src/math/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 7f0979c6..f3fa7580 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,5 +1,12 @@ //! Very simple math types built strictly off of Rust primitive types. +trait Vector { + fn dot(&self, other: &Self) -> f64; + fn cross(&self, other: &Self) -> Self; + fn length(&self) -> f64; + fn normalize(&self) -> Self; +} + // ------------------------------- VECTOR2 ----------------------------------- pub type Vector2 = (f32, f32); @@ -10,13 +17,18 @@ pub fn dot(a: Vector2, b: Vector2) -> f32 { /// Compute the length of a vector. fn length(a: Vector2) -> f32 { - (a.0 * a.0 + a.1 * a.1).sqrt() + return (a.0 * a.0 + a.1 * a.1).sqrt(); } /// Normalized vector. fn normalize(a: Vector2) -> Vector2 { let l = length(a); - (a.0 / l, a.1 / l) + + if l == 0.0 { + return a; + } + + return (a.0 / l, a.1 / l); } // ------------------------------- VECTOR3 ----------------------------------- @@ -25,11 +37,11 @@ pub type Vector3 = (f32, f32, f32); /// Compute the cross product of two `Vec3`. pub fn cross(a: Vector3, b: Vector3) -> Vector3 { - ( + return ( a.1 * b.2 - a.2 * b.1, a.2 * b.0 - a.0 * b.2, a.0 * b.1 - a.1 * b.0, - ) + ); } // ------------------------------- VECTOR4 ----------------------------------- From 383367e2117eb01b8404ac5a64637e1742019eb4 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 13 Nov 2022 17:21:26 -0800 Subject: [PATCH 09/67] [update] vector trait and implement functions for Vector2. --- lambda/src/math/mod.rs | 89 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index f3fa7580..f9dab69e 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,34 +1,106 @@ //! Very simple math types built strictly off of Rust primitive types. -trait Vector { - fn dot(&self, other: &Self) -> f64; +/// Generalized Vector operations that can be implemented by any vector like +/// type. +trait Vector { + fn add(&self, other: &Self) -> Self; + fn subtract(&self, other: &Self) -> Self; + fn multiply(&self, other: &Self) -> Self; + fn dot(&self, other: &Self) -> T; fn cross(&self, other: &Self) -> Self; - fn length(&self) -> f64; + fn length(&self) -> T; fn normalize(&self) -> Self; } // ------------------------------- VECTOR2 ----------------------------------- + pub type Vector2 = (f32, f32); +impl Vector for Vector2 { + fn add(&self, other: &Self) -> Self { + return (self.0 + other.0, self.1 + other.1); + } + + fn subtract(&self, other: &Self) -> Self { + return (self.0 - other.0, self.1 - other.1); + } + + fn multiply(&self, other: &Self) -> Self { + return (self.0 * other.0, self.1 * other.1); + } + + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + self.1 * other.1; + } + + fn cross(&self, other: &Self) -> Self { + return (self.0 * other.1, self.1 * other.0); + } + + fn length(&self) -> f32 { + return (self.0 * self.0 + self.1 * self.1).sqrt(); + } + + fn normalize(&self) -> Self { + let length = self.length(); + + if length == 0.0 { + return (0.0, 0.0); + } + + return (self.0 / length, self.1 / length); + } +} + +impl Vector for Vector3 { + fn add(&self, other: &Self) -> Self { + todo!() + } + + fn subtract(&self, other: &Self) -> Self { + todo!() + } + + fn multiply(&self, other: &Self) -> Self { + todo!() + } + + fn dot(&self, other: &Self) -> f64 { + todo!() + } + + fn cross(&self, other: &Self) -> Self { + todo!() + } + + fn length(&self) -> f64 { + todo!() + } + + fn normalize(&self) -> Self { + todo!() + } +} + /// Compute the dot product of two `Vec2`. pub fn dot(a: Vector2, b: Vector2) -> f32 { a.0 * b.0 + a.1 * b.1 } /// Compute the length of a vector. -fn length(a: Vector2) -> f32 { +pub fn length(a: Vector2) -> f32 { return (a.0 * a.0 + a.1 * a.1).sqrt(); } /// Normalized vector. -fn normalize(a: Vector2) -> Vector2 { - let l = length(a); +pub fn normalize(a: Vector2) -> Vector2 { + let length = length(a); - if l == 0.0 { + if length == 0.0 { return a; } - return (a.0 / l, a.1 / l); + return (a.0 / length, a.1 / length); } // ------------------------------- VECTOR3 ----------------------------------- @@ -45,6 +117,7 @@ pub fn cross(a: Vector3, b: Vector3) -> Vector3 { } // ------------------------------- VECTOR4 ----------------------------------- + pub type Vector4 = (f32, f32, f32, f32); // ------------------------------- MATRIX4 ----------------------------------- From 9974b33c3665faed3a52151a09ac8f51431f0180 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 17 Nov 2022 17:26:29 -0800 Subject: [PATCH 10/67] [remove] free functions and implement all vector traits and vector2 tests. --- lambda/src/math/mod.rs | 74 +++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 48 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index f9dab69e..7006afe9 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -52,68 +52,48 @@ impl Vector for Vector2 { } } +// ------------------------------- VECTOR3 ----------------------------------- + +pub type Vector3 = (f32, f32, f32); + impl Vector for Vector3 { fn add(&self, other: &Self) -> Self { - todo!() + return (self.0 + other.0, self.1 + other.1, self.2 + other.2); } fn subtract(&self, other: &Self) -> Self { - todo!() + return (self.0 - other.0, self.1 - other.1, self.2 - other.2); } fn multiply(&self, other: &Self) -> Self { - todo!() + return (self.0 * other.0, self.1 * other.1, self.2 * other.2); } - fn dot(&self, other: &Self) -> f64 { - todo!() + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + self.1 * other.1 + self.2 * other.2; } fn cross(&self, other: &Self) -> Self { - todo!() + return ( + self.1 * other.2 - self.2 * other.1, + self.2 * other.0 - self.0 * other.2, + self.0 * other.1 - self.1 * other.0, + ); } - fn length(&self) -> f64 { - todo!() + fn length(&self) -> f32 { + return (self.0 * self.0 + self.1 * self.1 + self.2 * self.2).sqrt(); } fn normalize(&self) -> Self { - todo!() - } -} - -/// Compute the dot product of two `Vec2`. -pub fn dot(a: Vector2, b: Vector2) -> f32 { - a.0 * b.0 + a.1 * b.1 -} - -/// Compute the length of a vector. -pub fn length(a: Vector2) -> f32 { - return (a.0 * a.0 + a.1 * a.1).sqrt(); -} + let length = self.length(); -/// Normalized vector. -pub fn normalize(a: Vector2) -> Vector2 { - let length = length(a); + if length == 0.0 { + return (0.0, 0.0, 0.0); + } - if length == 0.0 { - return a; + return (self.0 / length, self.1 / length, self.2 / length); } - - return (a.0 / length, a.1 / length); -} - -// ------------------------------- VECTOR3 ----------------------------------- - -pub type Vector3 = (f32, f32, f32); - -/// Compute the cross product of two `Vec3`. -pub fn cross(a: Vector3, b: Vector3) -> Vector3 { - return ( - a.1 * b.2 - a.2 * b.1, - a.2 * b.0 - a.0 * b.2, - a.0 * b.1 - a.1 * b.0, - ); } // ------------------------------- VECTOR4 ----------------------------------- @@ -156,27 +136,25 @@ pub fn translate( #[cfg(test)] mod tests { + use super::*; #[test] fn test_dot() { - assert_eq!(super::dot((1.0, 2.0), (3.0, 4.0)), 11.0); + assert_eq!((1.0, 2.0).dot(&(3.0, 4.0)), 11.0); } #[test] fn test_cross() { - assert_eq!( - super::cross((1.0, 2.0, 3.0), (4.0, 5.0, 6.0)), - (-3.0, 6.0, -3.0) - ); + assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); } #[test] fn test_length() { - assert_eq!(super::length((3.0, 4.0)), 5.0); + assert_eq!((3.0, 4.0).length(), 5.0); } #[test] fn test_normalize() { - assert_eq!(super::normalize((3.0, 4.0)), (0.6, 0.8)); + assert_eq!((3.0, 4.0).normalize(), (0.6, 0.8)); } } From 9c97e9f0163dad9ffa2b78b6ab7ac8821c329451 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 10:19:36 -0800 Subject: [PATCH 11/67] [add] a matrix trait type and update vector test modules. --- lambda/src/math/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 7006afe9..2b050d38 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -102,8 +102,38 @@ pub type Vector4 = (f32, f32, f32, f32); // ------------------------------- MATRIX4 ----------------------------------- +trait Matrix> { + fn identity() -> Self; + fn translate(&self, translation: &V) -> Self; + fn rotate(&self, rotation: &V) -> Self; + fn scale(&self, scale: &V) -> Self; + fn multiply(&self, other: &Self) -> Self; +} + pub type Matrix4 = (Vector, Vector, Vector, Vector); +impl Matrix for Matrix4 { + fn identity() -> Self { + todo!() + } + + fn translate(&self, translation: &Vector3) -> Self { + todo!() + } + + fn rotate(&self, rotation: &Vector3) -> Self { + todo!() + } + + fn scale(&self, scale: &Vector3) -> Self { + todo!() + } + + fn multiply(&self, other: &Self) -> Self { + todo!() + } +} + pub fn translate( matrix: Matrix4, vector: Vector3, @@ -135,7 +165,7 @@ pub fn translate( // ---------------------------------- TESTS ----------------------------------- #[cfg(test)] -mod tests { +mod vector2_tests { use super::*; #[test] @@ -158,3 +188,6 @@ mod tests { assert_eq!((3.0, 4.0).normalize(), (0.6, 0.8)); } } + +#[cfg(test)] +mod vector3_tests {} From 5cd01c46379e590e093854f5034910846dd15bba Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 11:42:32 -0800 Subject: [PATCH 12/67] [add] vector 3 tests and fix incomplete example. --- lambda/examples/push_constants.rs | 2 +- lambda/src/math/mod.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 18d4806d..be40b234 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -2,6 +2,7 @@ use lambda::{ core::{ component::Component, render::shader, + runtime::start_runtime, }, math, runtimes::GenericRuntimeBuilder, @@ -35,7 +36,6 @@ impl Component for PushConstantsExample { &mut self, render_context: &mut lambda::core::render::RenderContext, ) { - let shader = shader::ShaderBuilder::new().build(); } fn on_detach( diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 2b050d38..ba0ac28a 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -166,7 +166,7 @@ pub fn translate( #[cfg(test)] mod vector2_tests { - use super::*; + use super::Vector; #[test] fn test_dot() { @@ -190,4 +190,29 @@ mod vector2_tests { } #[cfg(test)] -mod vector3_tests {} +mod vector3_tests { + use super::Vector; + + #[test] + fn test_dot() { + assert_eq!((1.0, 2.0, 3.0).dot(&(4.0, 5.0, 6.0)), 32.0); + } + + #[test] + fn test_cross() { + assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); + } + + #[test] + fn test_length() { + assert_eq!((1.0, 2.0, 2.0).length(), 3.0); + } + + #[test] + fn test_normalize() { + assert_eq!( + (1.0, 2.0, 2.0).normalize(), + (0.33333334, 0.6666667, 0.6666667) + ); + } +} From 40f80c00f16e1d95740b04d4fd97cab6547d118a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 13:31:34 -0800 Subject: [PATCH 13/67] [add] a matrix4x4 implementation block and implement the multiply function. --- lambda/src/math/mod.rs | 182 +++++++++++++++++++++++++++++++++-------- 1 file changed, 148 insertions(+), 34 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index ba0ac28a..3ceacafc 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -100,8 +100,75 @@ impl Vector for Vector3 { pub type Vector4 = (f32, f32, f32, f32); -// ------------------------------- MATRIX4 ----------------------------------- +impl Vector for Vector4 { + fn add(&self, other: &Self) -> Self { + return ( + self.0 + other.0, + self.1 + other.1, + self.2 + other.2, + self.3 + other.3, + ); + } + + fn subtract(&self, other: &Self) -> Self { + return ( + self.0 - other.0, + self.1 - other.1, + self.2 - other.2, + self.3 - other.3, + ); + } + + fn multiply(&self, other: &Self) -> Self { + return ( + self.0 * other.0, + self.1 * other.1, + self.2 * other.2, + self.3 * other.3, + ); + } + + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + + self.1 * other.1 + + self.2 * other.2 + + self.3 * other.3; + } + fn cross(&self, other: &Self) -> Self { + return ( + self.1 * other.2 - self.2 * other.1, + self.2 * other.0 - self.0 * other.2, + self.0 * other.1 - self.1 * other.0, + 0.0, + ); + } + + fn length(&self) -> f32 { + return (self.0 * self.0 + + self.1 * self.1 + + self.2 * self.2 + + self.3 * self.3) + .sqrt(); + } + + fn normalize(&self) -> Self { + let length = self.length(); + + if length == 0.0 { + return (0.0, 0.0, 0.0, 0.0); + } + + return ( + self.0 / length, + self.1 / length, + self.2 / length, + self.3 / length, + ); + } +} + +/// Common Matrix operations that can be implemented by any matrix like type. trait Matrix> { fn identity() -> Self; fn translate(&self, translation: &V) -> Self; @@ -110,58 +177,105 @@ trait Matrix> { fn multiply(&self, other: &Self) -> Self; } +// ------------------------------- MATRIX4 ----------------------------------- + pub type Matrix4 = (Vector, Vector, Vector, Vector); -impl Matrix for Matrix4 { +impl Matrix for Matrix4 { fn identity() -> Self { todo!() } - fn translate(&self, translation: &Vector3) -> Self { + fn translate(&self, translation: &Vector4) -> Self { todo!() } - fn rotate(&self, rotation: &Vector3) -> Self { + fn rotate(&self, rotation: &Vector4) -> Self { todo!() } - fn scale(&self, scale: &Vector3) -> Self { + fn scale(&self, scale: &Vector4) -> Self { todo!() } fn multiply(&self, other: &Self) -> Self { - todo!() + return ( + ( + self.0 .0 * other.0 .0 + + self.0 .1 * other.1 .0 + + self.0 .2 * other.2 .0 + + self.0 .3 * other.3 .0, + self.0 .0 * other.0 .1 + + self.0 .1 * other.1 .1 + + self.0 .2 * other.2 .1 + + self.0 .3 * other.3 .1, + self.0 .0 * other.0 .2 + + self.0 .1 * other.1 .2 + + self.0 .2 * other.2 .2 + + self.0 .3 * other.3 .2, + self.0 .0 * other.0 .3 + + self.0 .1 * other.1 .3 + + self.0 .2 * other.2 .3 + + self.0 .3 * other.3 .3, + ), + ( + self.1 .0 * other.0 .0 + + self.1 .1 * other.1 .0 + + self.1 .2 * other.2 .0 + + self.1 .3 * other.3 .0, + self.1 .0 * other.0 .1 + + self.1 .1 * other.1 .1 + + self.1 .2 * other.2 .1 + + self.1 .3 * other.3 .1, + self.1 .0 * other.0 .2 + + self.1 .1 * other.1 .2 + + self.1 .2 * other.2 .2 + + self.1 .3 * other.3 .2, + self.1 .0 * other.0 .3 + + self.1 .1 * other.1 .3 + + self.1 .2 * other.2 .3 + + self.1 .3 * other.3 .3, + ), + ( + self.2 .0 * other.0 .0 + + self.2 .1 * other.1 .0 + + self.2 .2 * other.2 .0 + + self.2 .3 * other.3 .0, + self.2 .0 * other.0 .1 + + self.2 .1 * other.1 .1 + + self.2 .2 * other.2 .1 + + self.2 .3 * other.3 .1, + self.2 .0 * other.0 .2 + + self.2 .1 * other.1 .2 + + self.2 .2 * other.2 .2 + + self.2 .3 * other.3 .2, + self.2 .0 * other.0 .3 + + self.2 .1 * other.1 .3 + + self.2 .2 * other.2 .3 + + self.2 .3 * other.3 .3, + ), + ( + self.3 .0 * other.0 .0 + + self.3 .1 * other.1 .0 + + self.3 .2 * other.2 .0 + + self.3 .3 * other.3 .0, + self.3 .0 * other.0 .1 + + self.3 .1 * other.1 .1 + + self.3 .2 * other.2 .1 + + self.3 .3 * other.3 .1, + self.3 .0 * other.0 .2 + + self.3 .1 * other.1 .2 + + self.3 .2 * other.2 .2 + + self.3 .3 * other.3 .2, + self.3 .0 * other.0 .3 + + self.3 .1 * other.1 .3 + + self.3 .2 * other.2 .3 + + self.3 .3 * other.3 .3, + ), + ); } } -pub fn translate( - matrix: Matrix4, - vector: Vector3, -) -> Matrix4 { - ( - ( - matrix.0 .0 + vector.0, - matrix.0 .1 + vector.1, - matrix.0 .2 + vector.2, - ), - ( - matrix.1 .0 + vector.0, - matrix.1 .1 + vector.1, - matrix.1 .2 + vector.2, - ), - ( - matrix.2 .0 + vector.0, - matrix.2 .1 + vector.1, - matrix.2 .2 + vector.2, - ), - ( - matrix.3 .0 + vector.0, - matrix.3 .1 + vector.1, - matrix.3 .2 + vector.2, - ), - ) -} - // ---------------------------------- TESTS ----------------------------------- #[cfg(test)] From d9f82e95736aa6d0c7037a9c5b9554bfcde0e50a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 14:24:13 -0800 Subject: [PATCH 14/67] [add] the identity function. --- lambda/src/math/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 3ceacafc..0bd3623d 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -183,7 +183,12 @@ pub type Matrix4 = (Vector, Vector, Vector, Vector); impl Matrix for Matrix4 { fn identity() -> Self { - todo!() + return ( + (1.0, 0.0, 0.0, 0.0), + (0.0, 1.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.0), + (0.0, 0.0, 0.0, 1.0), + ); } fn translate(&self, translation: &Vector4) -> Self { From dd5332db4cfc28264801c73fcecb6b7fae9d3ff2 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 17:41:36 -0800 Subject: [PATCH 15/67] [add] transform implementation. --- lambda/src/math/mod.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 0bd3623d..dd17d717 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -171,8 +171,8 @@ impl Vector for Vector4 { /// Common Matrix operations that can be implemented by any matrix like type. trait Matrix> { fn identity() -> Self; - fn translate(&self, translation: &V) -> Self; - fn rotate(&self, rotation: &V) -> Self; + fn translate(&self, vector: &V) -> V; + fn rotate(&self, vector: &V) -> V; fn scale(&self, scale: &V) -> Self; fn multiply(&self, other: &Self) -> Self; } @@ -191,11 +191,28 @@ impl Matrix for Matrix4 { ); } - fn translate(&self, translation: &Vector4) -> Self { - todo!() + fn translate(&self, vector: &Vector4) -> Vector4 { + return ( + vector.0 * self.0 .0 + + vector.1 * self.1 .0 + + vector.2 * self.2 .0 + + vector.3 * self.3 .0, + vector.0 * self.0 .1 + + vector.1 * self.1 .1 + + vector.2 * self.2 .1 + + vector.3 * self.3 .1, + vector.0 * self.0 .2 + + vector.1 * self.1 .2 + + vector.2 * self.2 .2 + + vector.3 * self.3 .2, + vector.0 * self.0 .3 + + vector.1 * self.1 .3 + + vector.2 * self.2 .3 + + vector.3 * self.3 .3, + ); } - fn rotate(&self, rotation: &Vector4) -> Self { + fn rotate(&self, rotation: &Vector4) -> Vector4 { todo!() } From 6c8cc3b74b16e5368429a000d91d7743accf8e47 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 19 Nov 2022 17:43:49 -0800 Subject: [PATCH 16/67] [add] transform function. --- lambda/src/math/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index dd17d717..23b9d5e0 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -171,7 +171,7 @@ impl Vector for Vector4 { /// Common Matrix operations that can be implemented by any matrix like type. trait Matrix> { fn identity() -> Self; - fn translate(&self, vector: &V) -> V; + fn transform(&self, vector: &V) -> V; fn rotate(&self, vector: &V) -> V; fn scale(&self, scale: &V) -> Self; fn multiply(&self, other: &Self) -> Self; @@ -191,7 +191,7 @@ impl Matrix for Matrix4 { ); } - fn translate(&self, vector: &Vector4) -> Vector4 { + fn transform(&self, vector: &Vector4) -> Vector4 { return ( vector.0 * self.0 .0 + vector.1 * self.1 .0 From 1d2f415d4bce1f5f0cd93a89eb017b6f6bd06402 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 20 Nov 2022 10:11:59 -0800 Subject: [PATCH 17/67] [add] vector and matrix modules to separate implementations. --- lambda/src/math/matrix.rs | 136 +++++++++++++++ lambda/src/math/mod.rs | 354 +------------------------------------- lambda/src/math/vector.rs | 224 ++++++++++++++++++++++++ 3 files changed, 362 insertions(+), 352 deletions(-) create mode 100644 lambda/src/math/matrix.rs create mode 100644 lambda/src/math/vector.rs diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs new file mode 100644 index 00000000..1d3560a8 --- /dev/null +++ b/lambda/src/math/matrix.rs @@ -0,0 +1,136 @@ +//! Matrix math types and functions. + +use super::vector::{ + Vector, + Vector4, +}; + +/// Common Matrix operations that can be implemented by any matrix like type. +pub trait Matrix> { + fn identity() -> Self; + fn transform(&self, vector: &V) -> V; + fn rotate(&self, vector: &V) -> V; + fn scale(&self, scale: &V) -> Self; + fn multiply(&self, other: &Self) -> Self; +} + +// ------------------------------- MATRIX4 ----------------------------------- + +pub type Matrix4 = (Vector, Vector, Vector, Vector); + +impl Matrix for Matrix4 { + fn identity() -> Self { + return ( + (1.0, 0.0, 0.0, 0.0), + (0.0, 1.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.0), + (0.0, 0.0, 0.0, 1.0), + ); + } + + fn transform(&self, vector: &Vector4) -> Vector4 { + return ( + vector.0 * self.0 .0 + + vector.1 * self.1 .0 + + vector.2 * self.2 .0 + + vector.3 * self.3 .0, + vector.0 * self.0 .1 + + vector.1 * self.1 .1 + + vector.2 * self.2 .1 + + vector.3 * self.3 .1, + vector.0 * self.0 .2 + + vector.1 * self.1 .2 + + vector.2 * self.2 .2 + + vector.3 * self.3 .2, + vector.0 * self.0 .3 + + vector.1 * self.1 .3 + + vector.2 * self.2 .3 + + vector.3 * self.3 .3, + ); + } + + fn rotate(&self, rotation: &Vector4) -> Vector4 { + todo!() + } + + fn scale(&self, scale: &Vector4) -> Self { + todo!() + } + + fn multiply(&self, other: &Self) -> Self { + return ( + ( + self.0 .0 * other.0 .0 + + self.0 .1 * other.1 .0 + + self.0 .2 * other.2 .0 + + self.0 .3 * other.3 .0, + self.0 .0 * other.0 .1 + + self.0 .1 * other.1 .1 + + self.0 .2 * other.2 .1 + + self.0 .3 * other.3 .1, + self.0 .0 * other.0 .2 + + self.0 .1 * other.1 .2 + + self.0 .2 * other.2 .2 + + self.0 .3 * other.3 .2, + self.0 .0 * other.0 .3 + + self.0 .1 * other.1 .3 + + self.0 .2 * other.2 .3 + + self.0 .3 * other.3 .3, + ), + ( + self.1 .0 * other.0 .0 + + self.1 .1 * other.1 .0 + + self.1 .2 * other.2 .0 + + self.1 .3 * other.3 .0, + self.1 .0 * other.0 .1 + + self.1 .1 * other.1 .1 + + self.1 .2 * other.2 .1 + + self.1 .3 * other.3 .1, + self.1 .0 * other.0 .2 + + self.1 .1 * other.1 .2 + + self.1 .2 * other.2 .2 + + self.1 .3 * other.3 .2, + self.1 .0 * other.0 .3 + + self.1 .1 * other.1 .3 + + self.1 .2 * other.2 .3 + + self.1 .3 * other.3 .3, + ), + ( + self.2 .0 * other.0 .0 + + self.2 .1 * other.1 .0 + + self.2 .2 * other.2 .0 + + self.2 .3 * other.3 .0, + self.2 .0 * other.0 .1 + + self.2 .1 * other.1 .1 + + self.2 .2 * other.2 .1 + + self.2 .3 * other.3 .1, + self.2 .0 * other.0 .2 + + self.2 .1 * other.1 .2 + + self.2 .2 * other.2 .2 + + self.2 .3 * other.3 .2, + self.2 .0 * other.0 .3 + + self.2 .1 * other.1 .3 + + self.2 .2 * other.2 .3 + + self.2 .3 * other.3 .3, + ), + ( + self.3 .0 * other.0 .0 + + self.3 .1 * other.1 .0 + + self.3 .2 * other.2 .0 + + self.3 .3 * other.3 .0, + self.3 .0 * other.0 .1 + + self.3 .1 * other.1 .1 + + self.3 .2 * other.2 .1 + + self.3 .3 * other.3 .1, + self.3 .0 * other.0 .2 + + self.3 .1 * other.1 .2 + + self.3 .2 * other.2 .2 + + self.3 .3 * other.3 .2, + self.3 .0 * other.0 .3 + + self.3 .1 * other.1 .3 + + self.3 .2 * other.2 .3 + + self.3 .3 * other.3 .3, + ), + ); + } +} diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 23b9d5e0..764c1936 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,354 +1,4 @@ //! Very simple math types built strictly off of Rust primitive types. -/// Generalized Vector operations that can be implemented by any vector like -/// type. -trait Vector { - fn add(&self, other: &Self) -> Self; - fn subtract(&self, other: &Self) -> Self; - fn multiply(&self, other: &Self) -> Self; - fn dot(&self, other: &Self) -> T; - fn cross(&self, other: &Self) -> Self; - fn length(&self) -> T; - fn normalize(&self) -> Self; -} - -// ------------------------------- VECTOR2 ----------------------------------- - -pub type Vector2 = (f32, f32); - -impl Vector for Vector2 { - fn add(&self, other: &Self) -> Self { - return (self.0 + other.0, self.1 + other.1); - } - - fn subtract(&self, other: &Self) -> Self { - return (self.0 - other.0, self.1 - other.1); - } - - fn multiply(&self, other: &Self) -> Self { - return (self.0 * other.0, self.1 * other.1); - } - - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 + self.1 * other.1; - } - - fn cross(&self, other: &Self) -> Self { - return (self.0 * other.1, self.1 * other.0); - } - - fn length(&self) -> f32 { - return (self.0 * self.0 + self.1 * self.1).sqrt(); - } - - fn normalize(&self) -> Self { - let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0); - } - - return (self.0 / length, self.1 / length); - } -} - -// ------------------------------- VECTOR3 ----------------------------------- - -pub type Vector3 = (f32, f32, f32); - -impl Vector for Vector3 { - fn add(&self, other: &Self) -> Self { - return (self.0 + other.0, self.1 + other.1, self.2 + other.2); - } - - fn subtract(&self, other: &Self) -> Self { - return (self.0 - other.0, self.1 - other.1, self.2 - other.2); - } - - fn multiply(&self, other: &Self) -> Self { - return (self.0 * other.0, self.1 * other.1, self.2 * other.2); - } - - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 + self.1 * other.1 + self.2 * other.2; - } - - fn cross(&self, other: &Self) -> Self { - return ( - self.1 * other.2 - self.2 * other.1, - self.2 * other.0 - self.0 * other.2, - self.0 * other.1 - self.1 * other.0, - ); - } - - fn length(&self) -> f32 { - return (self.0 * self.0 + self.1 * self.1 + self.2 * self.2).sqrt(); - } - - fn normalize(&self) -> Self { - let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0, 0.0); - } - - return (self.0 / length, self.1 / length, self.2 / length); - } -} - -// ------------------------------- VECTOR4 ----------------------------------- - -pub type Vector4 = (f32, f32, f32, f32); - -impl Vector for Vector4 { - fn add(&self, other: &Self) -> Self { - return ( - self.0 + other.0, - self.1 + other.1, - self.2 + other.2, - self.3 + other.3, - ); - } - - fn subtract(&self, other: &Self) -> Self { - return ( - self.0 - other.0, - self.1 - other.1, - self.2 - other.2, - self.3 - other.3, - ); - } - - fn multiply(&self, other: &Self) -> Self { - return ( - self.0 * other.0, - self.1 * other.1, - self.2 * other.2, - self.3 * other.3, - ); - } - - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 - + self.1 * other.1 - + self.2 * other.2 - + self.3 * other.3; - } - - fn cross(&self, other: &Self) -> Self { - return ( - self.1 * other.2 - self.2 * other.1, - self.2 * other.0 - self.0 * other.2, - self.0 * other.1 - self.1 * other.0, - 0.0, - ); - } - - fn length(&self) -> f32 { - return (self.0 * self.0 - + self.1 * self.1 - + self.2 * self.2 - + self.3 * self.3) - .sqrt(); - } - - fn normalize(&self) -> Self { - let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0, 0.0, 0.0); - } - - return ( - self.0 / length, - self.1 / length, - self.2 / length, - self.3 / length, - ); - } -} - -/// Common Matrix operations that can be implemented by any matrix like type. -trait Matrix> { - fn identity() -> Self; - fn transform(&self, vector: &V) -> V; - fn rotate(&self, vector: &V) -> V; - fn scale(&self, scale: &V) -> Self; - fn multiply(&self, other: &Self) -> Self; -} - -// ------------------------------- MATRIX4 ----------------------------------- - -pub type Matrix4 = (Vector, Vector, Vector, Vector); - -impl Matrix for Matrix4 { - fn identity() -> Self { - return ( - (1.0, 0.0, 0.0, 0.0), - (0.0, 1.0, 0.0, 0.0), - (0.0, 0.0, 1.0, 0.0), - (0.0, 0.0, 0.0, 1.0), - ); - } - - fn transform(&self, vector: &Vector4) -> Vector4 { - return ( - vector.0 * self.0 .0 - + vector.1 * self.1 .0 - + vector.2 * self.2 .0 - + vector.3 * self.3 .0, - vector.0 * self.0 .1 - + vector.1 * self.1 .1 - + vector.2 * self.2 .1 - + vector.3 * self.3 .1, - vector.0 * self.0 .2 - + vector.1 * self.1 .2 - + vector.2 * self.2 .2 - + vector.3 * self.3 .2, - vector.0 * self.0 .3 - + vector.1 * self.1 .3 - + vector.2 * self.2 .3 - + vector.3 * self.3 .3, - ); - } - - fn rotate(&self, rotation: &Vector4) -> Vector4 { - todo!() - } - - fn scale(&self, scale: &Vector4) -> Self { - todo!() - } - - fn multiply(&self, other: &Self) -> Self { - return ( - ( - self.0 .0 * other.0 .0 - + self.0 .1 * other.1 .0 - + self.0 .2 * other.2 .0 - + self.0 .3 * other.3 .0, - self.0 .0 * other.0 .1 - + self.0 .1 * other.1 .1 - + self.0 .2 * other.2 .1 - + self.0 .3 * other.3 .1, - self.0 .0 * other.0 .2 - + self.0 .1 * other.1 .2 - + self.0 .2 * other.2 .2 - + self.0 .3 * other.3 .2, - self.0 .0 * other.0 .3 - + self.0 .1 * other.1 .3 - + self.0 .2 * other.2 .3 - + self.0 .3 * other.3 .3, - ), - ( - self.1 .0 * other.0 .0 - + self.1 .1 * other.1 .0 - + self.1 .2 * other.2 .0 - + self.1 .3 * other.3 .0, - self.1 .0 * other.0 .1 - + self.1 .1 * other.1 .1 - + self.1 .2 * other.2 .1 - + self.1 .3 * other.3 .1, - self.1 .0 * other.0 .2 - + self.1 .1 * other.1 .2 - + self.1 .2 * other.2 .2 - + self.1 .3 * other.3 .2, - self.1 .0 * other.0 .3 - + self.1 .1 * other.1 .3 - + self.1 .2 * other.2 .3 - + self.1 .3 * other.3 .3, - ), - ( - self.2 .0 * other.0 .0 - + self.2 .1 * other.1 .0 - + self.2 .2 * other.2 .0 - + self.2 .3 * other.3 .0, - self.2 .0 * other.0 .1 - + self.2 .1 * other.1 .1 - + self.2 .2 * other.2 .1 - + self.2 .3 * other.3 .1, - self.2 .0 * other.0 .2 - + self.2 .1 * other.1 .2 - + self.2 .2 * other.2 .2 - + self.2 .3 * other.3 .2, - self.2 .0 * other.0 .3 - + self.2 .1 * other.1 .3 - + self.2 .2 * other.2 .3 - + self.2 .3 * other.3 .3, - ), - ( - self.3 .0 * other.0 .0 - + self.3 .1 * other.1 .0 - + self.3 .2 * other.2 .0 - + self.3 .3 * other.3 .0, - self.3 .0 * other.0 .1 - + self.3 .1 * other.1 .1 - + self.3 .2 * other.2 .1 - + self.3 .3 * other.3 .1, - self.3 .0 * other.0 .2 - + self.3 .1 * other.1 .2 - + self.3 .2 * other.2 .2 - + self.3 .3 * other.3 .2, - self.3 .0 * other.0 .3 - + self.3 .1 * other.1 .3 - + self.3 .2 * other.2 .3 - + self.3 .3 * other.3 .3, - ), - ); - } -} - -// ---------------------------------- TESTS ----------------------------------- - -#[cfg(test)] -mod vector2_tests { - use super::Vector; - - #[test] - fn test_dot() { - assert_eq!((1.0, 2.0).dot(&(3.0, 4.0)), 11.0); - } - - #[test] - fn test_cross() { - assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); - } - - #[test] - fn test_length() { - assert_eq!((3.0, 4.0).length(), 5.0); - } - - #[test] - fn test_normalize() { - assert_eq!((3.0, 4.0).normalize(), (0.6, 0.8)); - } -} - -#[cfg(test)] -mod vector3_tests { - use super::Vector; - - #[test] - fn test_dot() { - assert_eq!((1.0, 2.0, 3.0).dot(&(4.0, 5.0, 6.0)), 32.0); - } - - #[test] - fn test_cross() { - assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); - } - - #[test] - fn test_length() { - assert_eq!((1.0, 2.0, 2.0).length(), 3.0); - } - - #[test] - fn test_normalize() { - assert_eq!( - (1.0, 2.0, 2.0).normalize(), - (0.33333334, 0.6666667, 0.6666667) - ); - } -} +pub mod matrix; +pub mod vector; diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs new file mode 100644 index 00000000..33cc3f7d --- /dev/null +++ b/lambda/src/math/vector.rs @@ -0,0 +1,224 @@ +//! Vector math types and functions. + +/// Generalized Vector operations that can be implemented by any vector like +/// type. +pub trait Vector { + fn add(&self, other: &Self) -> Self; + fn subtract(&self, other: &Self) -> Self; + fn multiply(&self, other: &Self) -> Self; + fn dot(&self, other: &Self) -> T; + fn cross(&self, other: &Self) -> Self; + fn length(&self) -> T; + fn normalize(&self) -> Self; +} + +// ------------------------------- VECTOR2 ----------------------------------- + +pub type Vector2 = (f32, f32); + +impl Vector for Vector2 { + fn add(&self, other: &Self) -> Self { + return (self.0 + other.0, self.1 + other.1); + } + + fn subtract(&self, other: &Self) -> Self { + return (self.0 - other.0, self.1 - other.1); + } + + fn multiply(&self, other: &Self) -> Self { + return (self.0 * other.0, self.1 * other.1); + } + + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + self.1 * other.1; + } + + fn cross(&self, other: &Self) -> Self { + return (self.0 * other.1, self.1 * other.0); + } + + fn length(&self) -> f32 { + return (self.0 * self.0 + self.1 * self.1).sqrt(); + } + + fn normalize(&self) -> Self { + let length = self.length(); + + if length == 0.0 { + return (0.0, 0.0); + } + + return (self.0 / length, self.1 / length); + } +} + +// ------------------------------- VECTOR3 ----------------------------------- + +pub type Vector3 = (f32, f32, f32); + +impl Vector for Vector3 { + fn add(&self, other: &Self) -> Self { + return (self.0 + other.0, self.1 + other.1, self.2 + other.2); + } + + fn subtract(&self, other: &Self) -> Self { + return (self.0 - other.0, self.1 - other.1, self.2 - other.2); + } + + fn multiply(&self, other: &Self) -> Self { + return (self.0 * other.0, self.1 * other.1, self.2 * other.2); + } + + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + self.1 * other.1 + self.2 * other.2; + } + + fn cross(&self, other: &Self) -> Self { + return ( + self.1 * other.2 - self.2 * other.1, + self.2 * other.0 - self.0 * other.2, + self.0 * other.1 - self.1 * other.0, + ); + } + + fn length(&self) -> f32 { + return (self.0 * self.0 + self.1 * self.1 + self.2 * self.2).sqrt(); + } + + fn normalize(&self) -> Self { + let length = self.length(); + + if length == 0.0 { + return (0.0, 0.0, 0.0); + } + + return (self.0 / length, self.1 / length, self.2 / length); + } +} + +// ------------------------------- VECTOR4 ----------------------------------- + +pub type Vector4 = (f32, f32, f32, f32); + +impl Vector for Vector4 { + fn add(&self, other: &Self) -> Self { + return ( + self.0 + other.0, + self.1 + other.1, + self.2 + other.2, + self.3 + other.3, + ); + } + + fn subtract(&self, other: &Self) -> Self { + return ( + self.0 - other.0, + self.1 - other.1, + self.2 - other.2, + self.3 - other.3, + ); + } + + fn multiply(&self, other: &Self) -> Self { + return ( + self.0 * other.0, + self.1 * other.1, + self.2 * other.2, + self.3 * other.3, + ); + } + + fn dot(&self, other: &Self) -> f32 { + return self.0 * other.0 + + self.1 * other.1 + + self.2 * other.2 + + self.3 * other.3; + } + + fn cross(&self, other: &Self) -> Self { + return ( + self.1 * other.2 - self.2 * other.1, + self.2 * other.0 - self.0 * other.2, + self.0 * other.1 - self.1 * other.0, + 0.0, + ); + } + + fn length(&self) -> f32 { + return (self.0 * self.0 + + self.1 * self.1 + + self.2 * self.2 + + self.3 * self.3) + .sqrt(); + } + + fn normalize(&self) -> Self { + let length = self.length(); + + if length == 0.0 { + return (0.0, 0.0, 0.0, 0.0); + } + + return ( + self.0 / length, + self.1 / length, + self.2 / length, + self.3 / length, + ); + } +} + +// ---------------------------------- TESTS ----------------------------------- + +#[cfg(test)] +mod vector2_tests { + use super::Vector; + + #[test] + fn test_dot() { + assert_eq!((1.0, 2.0).dot(&(3.0, 4.0)), 11.0); + } + + #[test] + fn test_cross() { + assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); + } + + #[test] + fn test_length() { + assert_eq!((3.0, 4.0).length(), 5.0); + } + + #[test] + fn test_normalize() { + assert_eq!((3.0, 4.0).normalize(), (0.6, 0.8)); + } +} + +#[cfg(test)] +mod vector3_tests { + use super::Vector; + + #[test] + fn test_dot() { + assert_eq!((1.0, 2.0, 3.0).dot(&(4.0, 5.0, 6.0)), 32.0); + } + + #[test] + fn test_cross() { + assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); + } + + #[test] + fn test_length() { + assert_eq!((1.0, 2.0, 2.0).length(), 3.0); + } + + #[test] + fn test_normalize() { + assert_eq!( + (1.0, 2.0, 2.0).normalize(), + (0.33333334, 0.6666667, 0.6666667) + ); + } +} From 1ad0b12901ed94d5ee0af2bd6c5bb4de43abd20a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 25 Nov 2022 15:04:44 -0800 Subject: [PATCH 18/67] [update] matrix multiplications. --- lambda/src/math/matrix.rs | 149 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 5 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 1d3560a8..8cce1668 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -2,23 +2,162 @@ use super::vector::{ Vector, + Vector3, Vector4, }; +pub enum Axes { + X, + Y, + Z, +} + /// Common Matrix operations that can be implemented by any matrix like type. pub trait Matrix> { fn identity() -> Self; fn transform(&self, vector: &V) -> V; - fn rotate(&self, vector: &V) -> V; + fn rotate(&self, vector: &V, axis: Axes) -> V; fn scale(&self, scale: &V) -> Self; fn multiply(&self, other: &Self) -> Self; } -// ------------------------------- MATRIX4 ----------------------------------- +pub trait CanMultiplyAgainst { + fn can_multiply_against(left: &Left, right: &Right) -> bool; +} + +/// Support for Matrix multliplication. +pub trait MatrixMultiply { + fn multiply(&self, other: Right) -> Result; +} + +impl MatrixMultiply for Matrix4x4f { + fn multiply(&self, other: Vector4) -> Vector3 { + let x = self.0 .0 * other.0 + + self.1 .0 * other.1 + + self.2 .0 * other.2 + + self.3 .0 * other.3; + + let y = self.0 .1 * other.0 + + self.1 .1 * other.1 + + self.2 .1 * other.2 + + self.3 .1 * other.3; + + let z = self.0 .2 * other.0 + + self.1 .2 * other.1 + + self.2 .2 * other.2 + + self.3 .2 * other.3; + + let w = self.0 .3 * other.0 + + self.1 .3 * other.1 + + self.2 .3 * other.2 + + self.3 .3 * other.3; + + if w != 0.0 && w != 1.0 { + return (x / w, y / w, z / w); + } + + return (x, y, z); + } +} + +pub type Matrix1x3f = Vector3; +pub type Matrix2x3f = (Vector3, Vector3); +pub type Matrix3x3f = (Vector3, Vector3, Vector3); +pub type Matrix4x3f = (Vector3, Vector3, Vector3, Vector3); + +impl Matrix for Matrix4x3f { + fn identity() -> Self { + todo!() + } + + fn transform(&self, vector: &Vector3) -> Vector3 { + todo!(); + } + + fn rotate(&self, vector: &Vector3, axis: Axes) -> Vector3 { + todo!() + } + + fn scale(&self, scale: &Vector3) -> Self { + todo!() + } + + fn multiply(&self, other: &Self) -> Self { + todo!() + } +} + +pub type Matrix1x4f = Vector4; +pub type Matrix2x4f = (Vector4, Vector4); +pub type Matrix3x4f = (Vector4, Vector4, Vector4); +pub type Matrix4x4f = (Vector4, Vector4, Vector4, Vector4); + +impl Matrix for Matrix3x4f { + fn identity() -> Self { + todo!() + } + + fn transform(&self, vector: &Vector4) -> Vector4 { + todo!() + } + + fn rotate(&self, vector: &Vector4, axis: Axes) -> Vector4 { + todo!() + } + + fn scale(&self, scale: &Vector4) -> Self { + todo!() + } -pub type Matrix4 = (Vector, Vector, Vector, Vector); + fn multiply(&self, other: &Self) -> Self { + return ( + ( + self.0 .0 * other.0 .0 + + self.0 .1 * other.1 .0 + + self.0 .2 * other.2 .0, + self.0 .0 * other.0 .1 + + self.0 .1 * other.1 .1 + + self.0 .2 * other.2 .1, + self.0 .0 * other.0 .2 + + self.0 .1 * other.1 .2 + + self.0 .2 * other.2 .2, + self.0 .0 * other.0 .3 + + self.0 .1 * other.1 .3 + + self.0 .2 * other.2 .3, + ), + ( + self.1 .0 * other.0 .0 + + self.1 .1 * other.1 .0 + + self.1 .2 * other.2 .0, + self.1 .0 * other.0 .1 + + self.1 .1 * other.1 .1 + + self.1 .2 * other.2 .1, + self.1 .0 * other.0 .2 + + self.1 .1 * other.1 .2 + + self.1 .2 * other.2 .2, + self.1 .0 * other.0 .3 + + self.1 .1 * other.1 .3 + + self.1 .2 * other.2 .3, + ), + ( + self.2 .0 * other.0 .0 + + self.2 .1 * other.1 .0 + + self.2 .2 * other.2 .0, + self.2 .0 * other.0 .1 + + self.2 .1 * other.1 .1 + + self.2 .2 * other.2 .1, + self.2 .0 * other.0 .2 + + self.2 .1 * other.1 .2 + + self.2 .2 * other.2 .2, + self.2 .0 * other.0 .3 + + self.2 .1 * other.1 .3 + + self.2 .2 * other.2 .3, + ), + ); + } +} -impl Matrix for Matrix4 { +impl Matrix for Matrix4x4f { fn identity() -> Self { return ( (1.0, 0.0, 0.0, 0.0), @@ -49,7 +188,7 @@ impl Matrix for Matrix4 { ); } - fn rotate(&self, rotation: &Vector4) -> Vector4 { + fn rotate(&self, rotation: &Vector4, axis: Axes) -> Vector4 { todo!() } From 96af5e818a65fd47a38a0c927df5bc4d40b93f91 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 26 Nov 2022 15:09:48 -0800 Subject: [PATCH 19/67] [update] matrix traits and remove redundant traits. --- lambda/src/math/matrix.rs | 63 +++++++++++++-------------------------- 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 8cce1668..7ef9cc8d 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -13,7 +13,7 @@ pub enum Axes { } /// Common Matrix operations that can be implemented by any matrix like type. -pub trait Matrix> { +pub trait MatrixOperations> { fn identity() -> Self; fn transform(&self, vector: &V) -> V; fn rotate(&self, vector: &V, axis: Axes) -> V; @@ -21,51 +21,12 @@ pub trait Matrix> { fn multiply(&self, other: &Self) -> Self; } -pub trait CanMultiplyAgainst { - fn can_multiply_against(left: &Left, right: &Right) -> bool; -} - -/// Support for Matrix multliplication. -pub trait MatrixMultiply { - fn multiply(&self, other: Right) -> Result; -} - -impl MatrixMultiply for Matrix4x4f { - fn multiply(&self, other: Vector4) -> Vector3 { - let x = self.0 .0 * other.0 - + self.1 .0 * other.1 - + self.2 .0 * other.2 - + self.3 .0 * other.3; - - let y = self.0 .1 * other.0 - + self.1 .1 * other.1 - + self.2 .1 * other.2 - + self.3 .1 * other.3; - - let z = self.0 .2 * other.0 - + self.1 .2 * other.1 - + self.2 .2 * other.2 - + self.3 .2 * other.3; - - let w = self.0 .3 * other.0 - + self.1 .3 * other.1 - + self.2 .3 * other.2 - + self.3 .3 * other.3; - - if w != 0.0 && w != 1.0 { - return (x / w, y / w, z / w); - } - - return (x, y, z); - } -} - pub type Matrix1x3f = Vector3; pub type Matrix2x3f = (Vector3, Vector3); pub type Matrix3x3f = (Vector3, Vector3, Vector3); pub type Matrix4x3f = (Vector3, Vector3, Vector3, Vector3); -impl Matrix for Matrix4x3f { +impl MatrixOperations for Matrix4x3f { fn identity() -> Self { todo!() } @@ -87,12 +48,28 @@ impl Matrix for Matrix4x3f { } } +pub struct Matrix { + rows: usize, + columns: usize, + data: [[f32; columns]; rows], +} + +impl Matrix { + pub fn new(data: [[f32; columns]; rows]) -> Self { + Self { + rows, + columns, + data, + } + } +} + pub type Matrix1x4f = Vector4; pub type Matrix2x4f = (Vector4, Vector4); pub type Matrix3x4f = (Vector4, Vector4, Vector4); pub type Matrix4x4f = (Vector4, Vector4, Vector4, Vector4); -impl Matrix for Matrix3x4f { +impl MatrixOperations for Matrix3x4f { fn identity() -> Self { todo!() } @@ -157,7 +134,7 @@ impl Matrix for Matrix3x4f { } } -impl Matrix for Matrix4x4f { +impl MatrixOperations for Matrix4x4f { fn identity() -> Self { return ( (1.0, 0.0, 0.0, 0.0), From 9071846636317ec51c16da907c023c5d5bb6da53 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 10:29:11 -0800 Subject: [PATCH 20/67] [add] rand package for random number generation. --- Cargo.lock | 48 +++++++++++++++++++++++++++++++ crates/lambda-platform/Cargo.toml | 1 + 2 files changed, 49 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index eb87ec44..4f2a2c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,6 +910,17 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + [[package]] name = "gfx-auxil" version = "0.10.0" @@ -1299,6 +1310,7 @@ dependencies = [ "gfx-backend-vulkan", "gfx-hal", "mockall", + "rand", "shaderc", "winit", ] @@ -1899,6 +1911,12 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "predicates" version = "2.1.1" @@ -1987,6 +2005,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "range-alloc" version = "0.1.2" diff --git a/crates/lambda-platform/Cargo.toml b/crates/lambda-platform/Cargo.toml index 43af61a4..8f7d53bb 100644 --- a/crates/lambda-platform/Cargo.toml +++ b/crates/lambda-platform/Cargo.toml @@ -12,6 +12,7 @@ gfx-hal = "=0.9.0" winit = "=0.27.5" shaderc = "=0.7" cfg-if = "=1.0.0" +rand = "=0.8.5" # GFX-RS backends gfx-backend-gl = { version="=0.9.0", optional = true } From f34dcd2fb0ff7d186b66b06b282cd9a27d561e9b Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 10:37:30 -0800 Subject: [PATCH 21/67] [add] traits for common matrix initializers and properties. --- lambda/src/math/matrix.rs | 250 ++++++-------------------------------- lambda/src/math/mod.rs | 2 +- 2 files changed, 41 insertions(+), 211 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 7ef9cc8d..c1d5c8da 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -1,5 +1,7 @@ //! Matrix math types and functions. +use rand::thread_rng; + use super::vector::{ Vector, Vector3, @@ -13,49 +15,60 @@ pub enum Axes { } /// Common Matrix operations that can be implemented by any matrix like type. -pub trait MatrixOperations> { +pub trait MatrixProperties { fn identity() -> Self; - fn transform(&self, vector: &V) -> V; - fn rotate(&self, vector: &V, axis: Axes) -> V; - fn scale(&self, scale: &V) -> Self; - fn multiply(&self, other: &Self) -> Self; + fn is_square(&self) -> bool; + fn rows(&self) -> usize; + fn columns(&self) -> usize; } -pub type Matrix1x3f = Vector3; -pub type Matrix2x3f = (Vector3, Vector3); -pub type Matrix3x3f = (Vector3, Vector3, Vector3); -pub type Matrix4x3f = (Vector3, Vector3, Vector3, Vector3); +/// Common Initializers for Matrix +pub trait CommonMatrixInitializers { + fn identity() -> Self; + fn zeroed() -> Self; + fn random() -> Self; +} -impl MatrixOperations for Matrix4x3f { +impl CommonMatrixInitializers for Matrix4x4f { fn identity() -> Self { - todo!() + return Matrix4x4f::new([ + [1.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]); } - fn transform(&self, vector: &Vector3) -> Vector3 { - todo!(); + fn zeroed() -> Self { + return Matrix4x4f::new([[0.0; 4]; 4]); } - fn rotate(&self, vector: &Vector3, axis: Axes) -> Vector3 { - todo!() + fn random() -> Self { + todo!(); } +} - fn scale(&self, scale: &Vector3) -> Self { - todo!() - } +pub trait Operations { + fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; +} - fn multiply(&self, other: &Self) -> Self { - todo!() +impl Operations for Matrix4x4f { + fn multiply(&self, other: &Matrix4x4f) -> Matrix4x4f { + let mut result = Matrix4x4f::zeroed(); + return result; } } -pub struct Matrix { +pub struct Matrix { rows: usize, columns: usize, - data: [[f32; columns]; rows], + data: [[ValueType; columns]; rows], } -impl Matrix { - pub fn new(data: [[f32; columns]; rows]) -> Self { +impl + Matrix +{ + pub fn new(data: [[ValueType; columns]; rows]) -> Self { Self { rows, columns, @@ -64,189 +77,6 @@ impl Matrix { } } -pub type Matrix1x4f = Vector4; -pub type Matrix2x4f = (Vector4, Vector4); -pub type Matrix3x4f = (Vector4, Vector4, Vector4); -pub type Matrix4x4f = (Vector4, Vector4, Vector4, Vector4); - -impl MatrixOperations for Matrix3x4f { - fn identity() -> Self { - todo!() - } - - fn transform(&self, vector: &Vector4) -> Vector4 { - todo!() - } - - fn rotate(&self, vector: &Vector4, axis: Axes) -> Vector4 { - todo!() - } - - fn scale(&self, scale: &Vector4) -> Self { - todo!() - } - - fn multiply(&self, other: &Self) -> Self { - return ( - ( - self.0 .0 * other.0 .0 - + self.0 .1 * other.1 .0 - + self.0 .2 * other.2 .0, - self.0 .0 * other.0 .1 - + self.0 .1 * other.1 .1 - + self.0 .2 * other.2 .1, - self.0 .0 * other.0 .2 - + self.0 .1 * other.1 .2 - + self.0 .2 * other.2 .2, - self.0 .0 * other.0 .3 - + self.0 .1 * other.1 .3 - + self.0 .2 * other.2 .3, - ), - ( - self.1 .0 * other.0 .0 - + self.1 .1 * other.1 .0 - + self.1 .2 * other.2 .0, - self.1 .0 * other.0 .1 - + self.1 .1 * other.1 .1 - + self.1 .2 * other.2 .1, - self.1 .0 * other.0 .2 - + self.1 .1 * other.1 .2 - + self.1 .2 * other.2 .2, - self.1 .0 * other.0 .3 - + self.1 .1 * other.1 .3 - + self.1 .2 * other.2 .3, - ), - ( - self.2 .0 * other.0 .0 - + self.2 .1 * other.1 .0 - + self.2 .2 * other.2 .0, - self.2 .0 * other.0 .1 - + self.2 .1 * other.1 .1 - + self.2 .2 * other.2 .1, - self.2 .0 * other.0 .2 - + self.2 .1 * other.1 .2 - + self.2 .2 * other.2 .2, - self.2 .0 * other.0 .3 - + self.2 .1 * other.1 .3 - + self.2 .2 * other.2 .3, - ), - ); - } -} - -impl MatrixOperations for Matrix4x4f { - fn identity() -> Self { - return ( - (1.0, 0.0, 0.0, 0.0), - (0.0, 1.0, 0.0, 0.0), - (0.0, 0.0, 1.0, 0.0), - (0.0, 0.0, 0.0, 1.0), - ); - } - - fn transform(&self, vector: &Vector4) -> Vector4 { - return ( - vector.0 * self.0 .0 - + vector.1 * self.1 .0 - + vector.2 * self.2 .0 - + vector.3 * self.3 .0, - vector.0 * self.0 .1 - + vector.1 * self.1 .1 - + vector.2 * self.2 .1 - + vector.3 * self.3 .1, - vector.0 * self.0 .2 - + vector.1 * self.1 .2 - + vector.2 * self.2 .2 - + vector.3 * self.3 .2, - vector.0 * self.0 .3 - + vector.1 * self.1 .3 - + vector.2 * self.2 .3 - + vector.3 * self.3 .3, - ); - } - - fn rotate(&self, rotation: &Vector4, axis: Axes) -> Vector4 { - todo!() - } - - fn scale(&self, scale: &Vector4) -> Self { - todo!() - } - - fn multiply(&self, other: &Self) -> Self { - return ( - ( - self.0 .0 * other.0 .0 - + self.0 .1 * other.1 .0 - + self.0 .2 * other.2 .0 - + self.0 .3 * other.3 .0, - self.0 .0 * other.0 .1 - + self.0 .1 * other.1 .1 - + self.0 .2 * other.2 .1 - + self.0 .3 * other.3 .1, - self.0 .0 * other.0 .2 - + self.0 .1 * other.1 .2 - + self.0 .2 * other.2 .2 - + self.0 .3 * other.3 .2, - self.0 .0 * other.0 .3 - + self.0 .1 * other.1 .3 - + self.0 .2 * other.2 .3 - + self.0 .3 * other.3 .3, - ), - ( - self.1 .0 * other.0 .0 - + self.1 .1 * other.1 .0 - + self.1 .2 * other.2 .0 - + self.1 .3 * other.3 .0, - self.1 .0 * other.0 .1 - + self.1 .1 * other.1 .1 - + self.1 .2 * other.2 .1 - + self.1 .3 * other.3 .1, - self.1 .0 * other.0 .2 - + self.1 .1 * other.1 .2 - + self.1 .2 * other.2 .2 - + self.1 .3 * other.3 .2, - self.1 .0 * other.0 .3 - + self.1 .1 * other.1 .3 - + self.1 .2 * other.2 .3 - + self.1 .3 * other.3 .3, - ), - ( - self.2 .0 * other.0 .0 - + self.2 .1 * other.1 .0 - + self.2 .2 * other.2 .0 - + self.2 .3 * other.3 .0, - self.2 .0 * other.0 .1 - + self.2 .1 * other.1 .1 - + self.2 .2 * other.2 .1 - + self.2 .3 * other.3 .1, - self.2 .0 * other.0 .2 - + self.2 .1 * other.1 .2 - + self.2 .2 * other.2 .2 - + self.2 .3 * other.3 .2, - self.2 .0 * other.0 .3 - + self.2 .1 * other.1 .3 - + self.2 .2 * other.2 .3 - + self.2 .3 * other.3 .3, - ), - ( - self.3 .0 * other.0 .0 - + self.3 .1 * other.1 .0 - + self.3 .2 * other.2 .0 - + self.3 .3 * other.3 .0, - self.3 .0 * other.0 .1 - + self.3 .1 * other.1 .1 - + self.3 .2 * other.2 .1 - + self.3 .3 * other.3 .1, - self.3 .0 * other.0 .2 - + self.3 .1 * other.1 .2 - + self.3 .2 * other.2 .2 - + self.3 .3 * other.3 .2, - self.3 .0 * other.0 .3 - + self.3 .1 * other.1 .3 - + self.3 .2 * other.2 .3 - + self.3 .3 * other.3 .3, - ), - ); - } -} +pub type Matrix2x2f = Matrix<2, 2, f32>; +pub type Matrix3x3f = Matrix<3, 3, f32>; +pub type Matrix4x4f = Matrix<4, 4, f32>; diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index 764c1936..e5616234 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -1,4 +1,4 @@ -//! Very simple math types built strictly off of Rust primitive types. +//! Lambda Math Types and operations pub mod matrix; pub mod vector; From 813433566be59fc07b23795ab70254db87bed87e Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 11:00:15 -0800 Subject: [PATCH 22/67] [add] two functions for sampling random numbers. --- crates/lambda-platform/src/lib.rs | 1 + crates/lambda-platform/src/rand/mod.rs | 27 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 crates/lambda-platform/src/rand/mod.rs diff --git a/crates/lambda-platform/src/lib.rs b/crates/lambda-platform/src/lib.rs index c0836402..6166b9c4 100644 --- a/crates/lambda-platform/src/lib.rs +++ b/crates/lambda-platform/src/lib.rs @@ -1,3 +1,4 @@ pub mod gfx; +pub mod rand; pub mod shaderc; pub mod winit; diff --git a/crates/lambda-platform/src/rand/mod.rs b/crates/lambda-platform/src/rand/mod.rs new file mode 100644 index 00000000..4525231a --- /dev/null +++ b/crates/lambda-platform/src/rand/mod.rs @@ -0,0 +1,27 @@ +use rand::{ + distributions::Uniform, + Rng, +}; + +/// Generate a random float within any given range. +#[inline(always)] +pub fn get_random_float_between(min: f32, max: f32) -> f32 { + let mut rng = rand::thread_rng(); + return rng.gen_range(min..max); +} + +/// Generate a vector of uniformally distributed random floats within any given +/// range. +pub fn get_uniformally_random_floats_between( + min: f32, + max: f32, + count: usize, +) -> Vec { + let distribution = rand::distributions::Uniform::new(min, max); + let mut rng = rand::thread_rng(); + let mut result = Vec::with_capacity(count); + for _ in 0..count { + result.push(rng.sample(distribution)); + } + return result; +} From 5e096f38d3349721bef791e578e80fad25438796 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 11:00:42 -0800 Subject: [PATCH 23/67] [add] a random initializer for mat4x4. --- lambda/src/math/matrix.rs | 45 +++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index c1d5c8da..42069e14 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -1,6 +1,6 @@ //! Matrix math types and functions. -use rand::thread_rng; +use lambda_platform::rand::get_uniformally_random_floats_between; use super::vector::{ Vector, @@ -23,13 +23,17 @@ pub trait MatrixProperties { } /// Common Initializers for Matrix -pub trait CommonMatrixInitializers { +pub trait MatrixInitializers { fn identity() -> Self; fn zeroed() -> Self; fn random() -> Self; } -impl CommonMatrixInitializers for Matrix4x4f { +pub trait MatrixOperations { + fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; +} + +impl MatrixInitializers for Matrix4x4f { fn identity() -> Self { return Matrix4x4f::new([ [1.0, 0.0, 0.0, 0.0], @@ -44,15 +48,38 @@ impl CommonMatrixInitializers for Matrix4x4f { } fn random() -> Self { - todo!(); - } -} + let random_floats = get_uniformally_random_floats_between(0.0, 1.0, 16); -pub trait Operations { - fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; + return Matrix4x4f::new([ + [ + random_floats[0], + random_floats[1], + random_floats[2], + random_floats[3], + ], + [ + random_floats[4], + random_floats[5], + random_floats[6], + random_floats[7], + ], + [ + random_floats[8], + random_floats[9], + random_floats[10], + random_floats[11], + ], + [ + random_floats[12], + random_floats[13], + random_floats[14], + random_floats[15], + ], + ]); + } } -impl Operations for Matrix4x4f { +impl MatrixOperations for Matrix4x4f { fn multiply(&self, other: &Matrix4x4f) -> Matrix4x4f { let mut result = Matrix4x4f::zeroed(); return result; From 92040c4d27d392d314c439edc513001a2f1f2068 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 11:03:35 -0800 Subject: [PATCH 24/67] [add] todo. --- lambda/src/math/matrix.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 42069e14..787931f7 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -50,6 +50,9 @@ impl MatrixInitializers for Matrix4x4f { fn random() -> Self { let random_floats = get_uniformally_random_floats_between(0.0, 1.0, 16); + // TODO(vmarcella): Use an iterator over the returned vector to build the + // matrix as opposed to these accesses. This will currently check every + // array index for safety which incurs a performance penalty. return Matrix4x4f::new([ [ random_floats[0], From 2bfdf34f75242155f93ef1411682d63c63037a1a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 11:25:52 -0800 Subject: [PATCH 25/67] [add] generic implementations of MatrixProperties for all Matrix derived structs and reorganize code. --- lambda/src/math/matrix.rs | 81 ++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 787931f7..d9a9a301 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -2,21 +2,32 @@ use lambda_platform::rand::get_uniformally_random_floats_between; -use super::vector::{ - Vector, - Vector3, - Vector4, -}; - -pub enum Axes { - X, - Y, - Z, +// ------------------------------ MATRIX --------------------------------------- + +pub struct Matrix { + rows: usize, + columns: usize, + data: [[ValueType; columns]; rows], } +impl + Matrix +{ + pub fn new(data: [[ValueType; columns]; rows]) -> Self { + Self { + rows, + columns, + data, + } + } +} + +pub type Matrix2x2f = Matrix<2, 2, f32>; +pub type Matrix3x3f = Matrix<3, 3, f32>; +pub type Matrix4x4f = Matrix<4, 4, f32>; + /// Common Matrix operations that can be implemented by any matrix like type. -pub trait MatrixProperties { - fn identity() -> Self; +pub trait MatrixProperties { fn is_square(&self) -> bool; fn rows(&self) -> usize; fn columns(&self) -> usize; @@ -29,10 +40,32 @@ pub trait MatrixInitializers { fn random() -> Self; } -pub trait MatrixOperations { +/// Common Matrix operations that can be implemented by any matrix like type so +/// long as it implements the `MatrixProperties` trait. +pub trait MatrixOperations< + OtherMatrix: MatrixProperties, + ResultingMatrix: MatrixProperties, +> +{ fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; } +impl MatrixProperties + for Matrix +{ + fn is_square(&self) -> bool { + return self.rows == self.columns; + } + + fn rows(&self) -> usize { + return self.rows; + } + + fn columns(&self) -> usize { + return self.columns; + } +} + impl MatrixInitializers for Matrix4x4f { fn identity() -> Self { return Matrix4x4f::new([ @@ -88,25 +121,3 @@ impl MatrixOperations for Matrix4x4f { return result; } } - -pub struct Matrix { - rows: usize, - columns: usize, - data: [[ValueType; columns]; rows], -} - -impl - Matrix -{ - pub fn new(data: [[ValueType; columns]; rows]) -> Self { - Self { - rows, - columns, - data, - } - } -} - -pub type Matrix2x2f = Matrix<2, 2, f32>; -pub type Matrix3x3f = Matrix<3, 3, f32>; -pub type Matrix4x4f = Matrix<4, 4, f32>; From 55c93389d3ecff16071ae1b1ecb21d16a8138566 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 15:07:05 -0800 Subject: [PATCH 26/67] [update] naming. --- crates/lambda-platform/src/rand/mod.rs | 2 +- lambda/src/math/matrix.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/lambda-platform/src/rand/mod.rs b/crates/lambda-platform/src/rand/mod.rs index 4525231a..b0a5a14e 100644 --- a/crates/lambda-platform/src/rand/mod.rs +++ b/crates/lambda-platform/src/rand/mod.rs @@ -12,7 +12,7 @@ pub fn get_random_float_between(min: f32, max: f32) -> f32 { /// Generate a vector of uniformally distributed random floats within any given /// range. -pub fn get_uniformally_random_floats_between( +pub fn get_uniformly_random_floats_between( min: f32, max: f32, count: usize, diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index d9a9a301..b5c66429 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -1,6 +1,6 @@ //! Matrix math types and functions. -use lambda_platform::rand::get_uniformally_random_floats_between; +use lambda_platform::rand::get_uniformly_random_floats_between; // ------------------------------ MATRIX --------------------------------------- @@ -81,7 +81,7 @@ impl MatrixInitializers for Matrix4x4f { } fn random() -> Self { - let random_floats = get_uniformally_random_floats_between(0.0, 1.0, 16); + let random_floats = get_uniformly_random_floats_between(0.0, 1.0, 16); // TODO(vmarcella): Use an iterator over the returned vector to build the // matrix as opposed to these accesses. This will currently check every From 7093a4267270b0374d4586dd8dbc48291d7fb937 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 15:31:06 -0800 Subject: [PATCH 27/67] [add] matrix tests. --- lambda/src/math/matrix.rs | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index b5c66429..b2504016 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -121,3 +121,43 @@ impl MatrixOperations for Matrix4x4f { return result; } } + +#[cfg(test)] +mod tests { + use super::{ + Matrix4x4f, + MatrixInitializers, + }; + + #[test] + fn test_matrix4x4f_identity() { + let identity = Matrix4x4f::identity(); + assert_eq!(identity.data[0][0], 1.0); + assert_eq!(identity.data[0][1], 0.0); + assert_eq!(identity.data[0][2], 0.0); + assert_eq!(identity.data[0][3], 0.0); + assert_eq!(identity.data[1][0], 0.0); + assert_eq!(identity.data[1][1], 1.0); + assert_eq!(identity.data[1][2], 0.0); + assert_eq!(identity.data[1][3], 0.0); + assert_eq!(identity.data[2][0], 0.0); + assert_eq!(identity.data[2][1], 0.0); + assert_eq!(identity.data[2][2], 1.0); + assert_eq!(identity.data[2][3], 0.0); + assert_eq!(identity.data[3][0], 0.0); + assert_eq!(identity.data[3][1], 0.0); + assert_eq!(identity.data[3][2], 0.0); + assert_eq!(identity.data[3][3], 1.0); + } + + #[test] + fn test_matrix4x4f_zeroed() { + let zeroed = Matrix4x4f::zeroed(); + + for row in 0..4 { + for column in 0..4 { + assert_eq!(zeroed.data[row][column], 0.0); + } + } + } +} From 3019a7bf741fb9aa053f061747402c54f9b8d74a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 27 Nov 2022 17:02:00 -0800 Subject: [PATCH 28/67] [add] start/stop parameters for random initialization and tests. --- lambda/src/math/matrix.rs | 40 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index b2504016..827860f9 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -4,19 +4,19 @@ use lambda_platform::rand::get_uniformly_random_floats_between; // ------------------------------ MATRIX --------------------------------------- -pub struct Matrix { +pub struct Matrix { rows: usize, columns: usize, - data: [[ValueType; columns]; rows], + data: [[ValueType; COLUMNS]; ROWS], } -impl - Matrix +impl + Matrix { - pub fn new(data: [[ValueType; columns]; rows]) -> Self { + pub fn new(data: [[ValueType; COLUMNS]; ROWS]) -> Self { Self { - rows, - columns, + columns: COLUMNS, + rows: ROWS, data, } } @@ -34,10 +34,10 @@ pub trait MatrixProperties { } /// Common Initializers for Matrix -pub trait MatrixInitializers { +pub trait MatrixInitializers { fn identity() -> Self; fn zeroed() -> Self; - fn random() -> Self; + fn random(start: ValueType, stop: ValueType) -> Self; } /// Common Matrix operations that can be implemented by any matrix like type so @@ -50,8 +50,8 @@ pub trait MatrixOperations< fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; } -impl MatrixProperties - for Matrix +impl MatrixProperties + for Matrix { fn is_square(&self) -> bool { return self.rows == self.columns; @@ -66,7 +66,7 @@ impl MatrixProperties } } -impl MatrixInitializers for Matrix4x4f { +impl MatrixInitializers for Matrix4x4f { fn identity() -> Self { return Matrix4x4f::new([ [1.0, 0.0, 0.0, 0.0], @@ -80,8 +80,8 @@ impl MatrixInitializers for Matrix4x4f { return Matrix4x4f::new([[0.0; 4]; 4]); } - fn random() -> Self { - let random_floats = get_uniformly_random_floats_between(0.0, 1.0, 16); + fn random(start: f32, stop: f32) -> Self { + let random_floats = get_uniformly_random_floats_between(start, stop, 16); // TODO(vmarcella): Use an iterator over the returned vector to build the // matrix as opposed to these accesses. This will currently check every @@ -160,4 +160,16 @@ mod tests { } } } + + #[test] + fn test_matrix4x4f_random() { + let random = Matrix4x4f::random(0.0, 1.0); + + for row in 0..4 { + for column in 0..4 { + assert!(random.data[row][column] >= 0.0); + assert!(random.data[row][column] <= 1.0); + } + } + } } From bef2c39d621f0f40af25ac9dca78a4a2c084c458 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 17 Dec 2022 12:40:07 -0800 Subject: [PATCH 29/67] [update] vector implementation to not be around tuples & generalize the operations to any size using Vectors for storage --- lambda/src/math/matrix.rs | 172 +------------------------- lambda/src/math/vector.rs | 247 ++++++++++++++------------------------ 2 files changed, 89 insertions(+), 330 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 827860f9..6829b628 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -2,174 +2,4 @@ use lambda_platform::rand::get_uniformly_random_floats_between; -// ------------------------------ MATRIX --------------------------------------- - -pub struct Matrix { - rows: usize, - columns: usize, - data: [[ValueType; COLUMNS]; ROWS], -} - -impl - Matrix -{ - pub fn new(data: [[ValueType; COLUMNS]; ROWS]) -> Self { - Self { - columns: COLUMNS, - rows: ROWS, - data, - } - } -} - -pub type Matrix2x2f = Matrix<2, 2, f32>; -pub type Matrix3x3f = Matrix<3, 3, f32>; -pub type Matrix4x4f = Matrix<4, 4, f32>; - -/// Common Matrix operations that can be implemented by any matrix like type. -pub trait MatrixProperties { - fn is_square(&self) -> bool; - fn rows(&self) -> usize; - fn columns(&self) -> usize; -} - -/// Common Initializers for Matrix -pub trait MatrixInitializers { - fn identity() -> Self; - fn zeroed() -> Self; - fn random(start: ValueType, stop: ValueType) -> Self; -} - -/// Common Matrix operations that can be implemented by any matrix like type so -/// long as it implements the `MatrixProperties` trait. -pub trait MatrixOperations< - OtherMatrix: MatrixProperties, - ResultingMatrix: MatrixProperties, -> -{ - fn multiply(&self, other: &OtherMatrix) -> ResultingMatrix; -} - -impl MatrixProperties - for Matrix -{ - fn is_square(&self) -> bool { - return self.rows == self.columns; - } - - fn rows(&self) -> usize { - return self.rows; - } - - fn columns(&self) -> usize { - return self.columns; - } -} - -impl MatrixInitializers for Matrix4x4f { - fn identity() -> Self { - return Matrix4x4f::new([ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - ]); - } - - fn zeroed() -> Self { - return Matrix4x4f::new([[0.0; 4]; 4]); - } - - fn random(start: f32, stop: f32) -> Self { - let random_floats = get_uniformly_random_floats_between(start, stop, 16); - - // TODO(vmarcella): Use an iterator over the returned vector to build the - // matrix as opposed to these accesses. This will currently check every - // array index for safety which incurs a performance penalty. - return Matrix4x4f::new([ - [ - random_floats[0], - random_floats[1], - random_floats[2], - random_floats[3], - ], - [ - random_floats[4], - random_floats[5], - random_floats[6], - random_floats[7], - ], - [ - random_floats[8], - random_floats[9], - random_floats[10], - random_floats[11], - ], - [ - random_floats[12], - random_floats[13], - random_floats[14], - random_floats[15], - ], - ]); - } -} - -impl MatrixOperations for Matrix4x4f { - fn multiply(&self, other: &Matrix4x4f) -> Matrix4x4f { - let mut result = Matrix4x4f::zeroed(); - return result; - } -} - -#[cfg(test)] -mod tests { - use super::{ - Matrix4x4f, - MatrixInitializers, - }; - - #[test] - fn test_matrix4x4f_identity() { - let identity = Matrix4x4f::identity(); - assert_eq!(identity.data[0][0], 1.0); - assert_eq!(identity.data[0][1], 0.0); - assert_eq!(identity.data[0][2], 0.0); - assert_eq!(identity.data[0][3], 0.0); - assert_eq!(identity.data[1][0], 0.0); - assert_eq!(identity.data[1][1], 1.0); - assert_eq!(identity.data[1][2], 0.0); - assert_eq!(identity.data[1][3], 0.0); - assert_eq!(identity.data[2][0], 0.0); - assert_eq!(identity.data[2][1], 0.0); - assert_eq!(identity.data[2][2], 1.0); - assert_eq!(identity.data[2][3], 0.0); - assert_eq!(identity.data[3][0], 0.0); - assert_eq!(identity.data[3][1], 0.0); - assert_eq!(identity.data[3][2], 0.0); - assert_eq!(identity.data[3][3], 1.0); - } - - #[test] - fn test_matrix4x4f_zeroed() { - let zeroed = Matrix4x4f::zeroed(); - - for row in 0..4 { - for column in 0..4 { - assert_eq!(zeroed.data[row][column], 0.0); - } - } - } - - #[test] - fn test_matrix4x4f_random() { - let random = Matrix4x4f::random(0.0, 1.0); - - for row in 0..4 { - for column in 0..4 { - assert!(random.data[row][column] >= 0.0); - assert!(random.data[row][column] <= 1.0); - } - } - } -} +pub trait Matrix {} diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 33cc3f7d..0c7e07c1 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -2,223 +2,152 @@ /// Generalized Vector operations that can be implemented by any vector like /// type. -pub trait Vector { +pub trait Vector { + type Scalar: Copy; + fn new(values: &[Self::Scalar]) -> Self; fn add(&self, other: &Self) -> Self; fn subtract(&self, other: &Self) -> Self; fn multiply(&self, other: &Self) -> Self; - fn dot(&self, other: &Self) -> T; + fn scale(&self, scalar: Self::Scalar) -> Self; + fn dot(&self, other: &Self) -> Self::Scalar; fn cross(&self, other: &Self) -> Self; - fn length(&self) -> T; + fn length(&self) -> Self::Scalar; fn normalize(&self) -> Self; } -// ------------------------------- VECTOR2 ----------------------------------- +impl Vector for T +where + T: AsMut<[f32]> + AsRef<[f32]> + Default, +{ + type Scalar = f32; -pub type Vector2 = (f32, f32); - -impl Vector for Vector2 { fn add(&self, other: &Self) -> Self { - return (self.0 + other.0, self.1 + other.1); - } - - fn subtract(&self, other: &Self) -> Self { - return (self.0 - other.0, self.1 - other.1); - } + let mut result = Vec::with_capacity(self.as_ref().len()); - fn multiply(&self, other: &Self) -> Self { - return (self.0 * other.0, self.1 * other.1); - } - - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 + self.1 * other.1; - } - - fn cross(&self, other: &Self) -> Self { - return (self.0 * other.1, self.1 * other.0); - } + for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { + result.push(a + b) + } - fn length(&self) -> f32 { - return (self.0 * self.0 + self.1 * self.1).sqrt(); + return Self::new(result.as_slice()); } + fn subtract(&self, other: &Self) -> Self { + let mut result = Vec::with_capacity(self.as_ref().len()); - fn normalize(&self) -> Self { - let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0); + for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { + result.push(a - b) } - return (self.0 / length, self.1 / length); + return Self::new(result.as_slice()); } -} - -// ------------------------------- VECTOR3 ----------------------------------- -pub type Vector3 = (f32, f32, f32); - -impl Vector for Vector3 { - fn add(&self, other: &Self) -> Self { - return (self.0 + other.0, self.1 + other.1, self.2 + other.2); - } + fn multiply(&self, other: &Self) -> Self { + let mut result = Vec::with_capacity(self.as_ref().len()); - fn subtract(&self, other: &Self) -> Self { - return (self.0 - other.0, self.1 - other.1, self.2 - other.2); - } + for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { + result.push(a * b); + } - fn multiply(&self, other: &Self) -> Self { - return (self.0 * other.0, self.1 * other.1, self.2 * other.2); + return Self::new(result.as_slice()); } - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 + self.1 * other.1 + self.2 * other.2; + fn dot(&self, other: &Self) -> Self::Scalar { + let mut result = 0.0; + for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { + result += a * b; + } + return result; } fn cross(&self, other: &Self) -> Self { - return ( - self.1 * other.2 - self.2 * other.1, - self.2 * other.0 - self.0 * other.2, - self.0 * other.1 - self.1 * other.0, - ); + let mut result = Vec::with_capacity(self.as_ref().len()); + for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { + result.push(a * b); + } + return Self::new(result.as_slice()); } - fn length(&self) -> f32 { - return (self.0 * self.0 + self.1 * self.1 + self.2 * self.2).sqrt(); + fn length(&self) -> Self::Scalar { + let mut result = 0.0; + for a in self.as_ref().iter() { + result += a * a; + } + result.sqrt() } fn normalize(&self) -> Self { + let mut result = Vec::with_capacity(self.as_ref().len()); let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0, 0.0); + for a in self.as_ref().iter() { + result.push(a / length); } - - return (self.0 / length, self.1 / length, self.2 / length); - } -} - -// ------------------------------- VECTOR4 ----------------------------------- - -pub type Vector4 = (f32, f32, f32, f32); - -impl Vector for Vector4 { - fn add(&self, other: &Self) -> Self { - return ( - self.0 + other.0, - self.1 + other.1, - self.2 + other.2, - self.3 + other.3, - ); + Self::new(result.as_slice()) } - fn subtract(&self, other: &Self) -> Self { - return ( - self.0 - other.0, - self.1 - other.1, - self.2 - other.2, - self.3 - other.3, - ); - } - - fn multiply(&self, other: &Self) -> Self { - return ( - self.0 * other.0, - self.1 * other.1, - self.2 * other.2, - self.3 * other.3, - ); + fn new(values: &[Self::Scalar]) -> Self { + let mut vector = T::default(); + vector.as_mut().copy_from_slice(values); + return vector; } - fn dot(&self, other: &Self) -> f32 { - return self.0 * other.0 - + self.1 * other.1 - + self.2 * other.2 - + self.3 * other.3; - } - - fn cross(&self, other: &Self) -> Self { - return ( - self.1 * other.2 - self.2 * other.1, - self.2 * other.0 - self.0 * other.2, - self.0 * other.1 - self.1 * other.0, - 0.0, - ); - } - - fn length(&self) -> f32 { - return (self.0 * self.0 - + self.1 * self.1 - + self.2 * self.2 - + self.3 * self.3) - .sqrt(); - } - - fn normalize(&self) -> Self { - let length = self.length(); - - if length == 0.0 { - return (0.0, 0.0, 0.0, 0.0); + fn scale(&self, scalar: Self::Scalar) -> Self { + let mut result = Vec::with_capacity(self.as_ref().len()); + for a in self.as_ref().iter() { + result.push(a * scalar); } - - return ( - self.0 / length, - self.1 / length, - self.2 / length, - self.3 / length, - ); + Self::new(result.as_slice()) } } -// ---------------------------------- TESTS ----------------------------------- - #[cfg(test)] -mod vector2_tests { +mod tests { use super::Vector; #[test] - fn test_dot() { - assert_eq!((1.0, 2.0).dot(&(3.0, 4.0)), 11.0); + fn creating_a_vector() { + let v: [f32; 3] = Vector::new(&[1.0, 2.0, 3.0]); + assert_eq!(v.as_ref(), &[1.0, 2.0, 3.0]); } #[test] - fn test_cross() { - assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); - } + fn adding_vectors() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let c = [5.0, 7.0, 9.0]; - #[test] - fn test_length() { - assert_eq!((3.0, 4.0).length(), 5.0); + let result = a.add(&b); + + assert_eq!(result, c); } #[test] - fn test_normalize() { - assert_eq!((3.0, 4.0).normalize(), (0.6, 0.8)); - } -} + fn subtracting_vectors() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let c = [-3.0, -3.0, -3.0]; -#[cfg(test)] -mod vector3_tests { - use super::Vector; + let result = a.subtract(&b); - #[test] - fn test_dot() { - assert_eq!((1.0, 2.0, 3.0).dot(&(4.0, 5.0, 6.0)), 32.0); + assert_eq!(result, c); } #[test] - fn test_cross() { - assert_eq!((1.0, 2.0, 3.0).cross(&(4.0, 5.0, 6.0)), (-3.0, 6.0, -3.0)); - } + fn multiplying_vectors() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let c = [4.0, 10.0, 18.0]; - #[test] - fn test_length() { - assert_eq!((1.0, 2.0, 2.0).length(), 3.0); + let result = a.multiply(&b); + + assert_eq!(result, c); } #[test] - fn test_normalize() { - assert_eq!( - (1.0, 2.0, 2.0).normalize(), - (0.33333334, 0.6666667, 0.6666667) - ); + fn scaling_vectors() { + let a = [1.0, 2.0, 3.0]; + let b = [2.0, 4.0, 6.0]; + let scalar = 2.0; + + let result = a.scale(scalar); + assert_eq!(result, b); } } From 2aac3df14f01837cf949ce873c055387e7193994 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 18 Dec 2022 18:50:42 -0500 Subject: [PATCH 30/67] [update] vector implementations to not utilize Vector allocations for operations, remove new, and update determinant. --- lambda/src/math/matrix.rs | 30 +++++++++++- lambda/src/math/vector.rs | 98 ++++++++++++++++----------------------- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 6829b628..52bfd90f 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -2,4 +2,32 @@ use lambda_platform::rand::get_uniformly_random_floats_between; -pub trait Matrix {} +use super::vector::Vector; +pub trait Matrix { + type Scalar: Copy; + type Vector: Vector; + + fn transform(&self, vector: &Self::Vector) -> Self::Vector; + fn determinant(&self) -> Self::Scalar; +} + +impl Matrix for [[f32; 4]; 4] { + type Scalar = f32; + type Vector = [f32; 4]; + + fn transform(&self, vector: &Self::Vector) -> Self::Vector { + let mut result = [0.0; 4]; + for (i, row) in self.iter().enumerate() { + for (j, value) in row.iter().enumerate() { + result[i] += value * vector[j]; + } + } + return result; + } + + fn determinant(&self) -> Self::Scalar { + let mut result = 0.0; + for (i, value) in self[0].iter().enumerate() {} + return result; + } +} diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 0c7e07c1..7325a600 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -4,10 +4,8 @@ /// type. pub trait Vector { type Scalar: Copy; - fn new(values: &[Self::Scalar]) -> Self; fn add(&self, other: &Self) -> Self; fn subtract(&self, other: &Self) -> Self; - fn multiply(&self, other: &Self) -> Self; fn scale(&self, scalar: Self::Scalar) -> Self; fn dot(&self, other: &Self) -> Self::Scalar; fn cross(&self, other: &Self) -> Self; @@ -22,32 +20,29 @@ where type Scalar = f32; fn add(&self, other: &Self) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); + let mut result = Self::default(); - for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { - result.push(a + b) - } - - return Self::new(result.as_slice()); - } - fn subtract(&self, other: &Self) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); + self + .as_ref() + .iter() + .zip(other.as_ref().iter()) + .enumerate() + .for_each(|(i, (a, b))| result.as_mut()[i] = a + b); - for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { - result.push(a - b) - } - - return Self::new(result.as_slice()); + return result; } - fn multiply(&self, other: &Self) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); + fn subtract(&self, other: &Self) -> Self { + let mut result = Self::default(); - for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { - result.push(a * b); - } + self + .as_ref() + .iter() + .zip(other.as_ref().iter()) + .enumerate() + .for_each(|(i, (a, b))| result.as_mut()[i] = a - b); - return Self::new(result.as_slice()); + return result; } fn dot(&self, other: &Self) -> Self::Scalar { @@ -59,11 +54,16 @@ where } fn cross(&self, other: &Self) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); - for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { - result.push(a * b); - } - return Self::new(result.as_slice()); + let mut result = Self::default(); + self + .as_ref() + .iter() + .zip(other.as_ref().iter()) + .enumerate() + .for_each(|(i, (a, b))| { + result.as_mut()[i] = a * b; + }); + return result; } fn length(&self) -> Self::Scalar { @@ -75,26 +75,23 @@ where } fn normalize(&self) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); + let mut result = Self::default(); let length = self.length(); - for a in self.as_ref().iter() { - result.push(a / length); - } - Self::new(result.as_slice()) - } + self.as_ref().iter().enumerate().for_each(|(i, a)| { + result.as_mut()[i] = a / length; + }); - fn new(values: &[Self::Scalar]) -> Self { - let mut vector = T::default(); - vector.as_mut().copy_from_slice(values); - return vector; + return result; } fn scale(&self, scalar: Self::Scalar) -> Self { - let mut result = Vec::with_capacity(self.as_ref().len()); - for a in self.as_ref().iter() { - result.push(a * scalar); - } - Self::new(result.as_slice()) + let mut result = Self::default(); + let length = self.length(); + self.as_ref().iter().enumerate().for_each(|(i, a)| { + result.as_mut()[i] = a * scalar; + }); + + return result; } } @@ -102,12 +99,6 @@ where mod tests { use super::Vector; - #[test] - fn creating_a_vector() { - let v: [f32; 3] = Vector::new(&[1.0, 2.0, 3.0]); - assert_eq!(v.as_ref(), &[1.0, 2.0, 3.0]); - } - #[test] fn adding_vectors() { let a = [1.0, 2.0, 3.0]; @@ -130,17 +121,6 @@ mod tests { assert_eq!(result, c); } - #[test] - fn multiplying_vectors() { - let a = [1.0, 2.0, 3.0]; - let b = [4.0, 5.0, 6.0]; - let c = [4.0, 10.0, 18.0]; - - let result = a.multiply(&b); - - assert_eq!(result, c); - } - #[test] fn scaling_vectors() { let a = [1.0, 2.0, 3.0]; From 3798cfe6f6b949d19bce91eb34489a5a61c4d46f Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 25 Dec 2022 15:33:14 -0500 Subject: [PATCH 31/67] [fix] cross product implementation and add tests for the dot product & cross product. --- lambda/src/math/vector.rs | 63 ++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 7325a600..501df64d 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -46,6 +46,12 @@ where } fn dot(&self, other: &Self) -> Self::Scalar { + assert_eq!( + self.as_ref().len(), + other.as_ref().len(), + "Vectors must be the same length" + ); + let mut result = 0.0; for (a, b) in self.as_ref().iter().zip(other.as_ref().iter()) { result += a * b; @@ -54,15 +60,27 @@ where } fn cross(&self, other: &Self) -> Self { + assert_eq!( + self.as_ref().len(), + other.as_ref().len(), + "Vectors must be the same length" + ); + let mut result = Self::default(); - self - .as_ref() - .iter() - .zip(other.as_ref().iter()) - .enumerate() - .for_each(|(i, (a, b))| { - result.as_mut()[i] = a * b; - }); + let a = self.as_ref(); + let b = other.as_ref(); + + // TODO: This is only for 3D vectors + match a.len() { + 3 => { + result.as_mut()[0] = a[1] * b[2] - a[2] * b[1]; + result.as_mut()[1] = a[2] * b[0] - a[0] * b[2]; + result.as_mut()[2] = a[0] * b[1] - a[1] * b[0]; + } + _ => { + panic!("Cross product is only defined for 3 dimensional vectors.") + } + } return result; } @@ -130,4 +148,33 @@ mod tests { let result = a.scale(scalar); assert_eq!(result, b); } + + #[test] + fn dot_product() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let c = 32.0; + + let result = a.dot(&b); + assert_eq!(result, c); + } + + #[test] + fn cross_product() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let c = [-3.0, 6.0, -3.0]; + + let result = a.cross(&b); + assert_eq!(result, c); + } + + #[test] + fn cross_product_fails_for_non_3d_vectors() { + let a = [1.0, 2.0]; + let b = [4.0, 5.0]; + + let result = std::panic::catch_unwind(|| a.cross(&b)); + assert!(result.is_err()); + } } From f07f96e6ec6257069c69b02b0418653cd5a6f40d Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 25 Dec 2022 15:36:25 -0500 Subject: [PATCH 32/67] [add] test for vector length. --- lambda/src/math/vector.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 501df64d..58598131 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -19,6 +19,7 @@ where { type Scalar = f32; + /// Add two vectors of any size together. fn add(&self, other: &Self) -> Self { let mut result = Self::default(); @@ -32,6 +33,7 @@ where return result; } + /// Subtract two vectors of any size. fn subtract(&self, other: &Self) -> Self { let mut result = Self::default(); @@ -59,6 +61,7 @@ where return result; } + /// Cross product of two 3D vectors. Panics if the vectors are not 3D. fn cross(&self, other: &Self) -> Self { assert_eq!( self.as_ref().len(), @@ -177,4 +180,18 @@ mod tests { let result = std::panic::catch_unwind(|| a.cross(&b)); assert!(result.is_err()); } + + #[test] + fn length() { + let a = [1.0, 2.0, 3.0]; + let b = 3.7416573867739413; + + let result = a.length(); + assert_eq!(result, b); + + let c = [1.0, 2.0, 3.0, 4.0]; + let d = 5.477225575051661; + let result = c.length(); + assert_eq!(result, d); + } } From 83554628d46049620ab07bb5008e600b48bc7a11 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 25 Dec 2022 16:24:57 -0500 Subject: [PATCH 33/67] [add] tests for vector normalization. --- lambda/src/math/vector.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 58598131..47f95a09 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -96,8 +96,10 @@ where } fn normalize(&self) -> Self { + assert_ne!(self.length(), 0.0, "Cannot normalize a zero length vector"); let mut result = Self::default(); let length = self.length(); + self.as_ref().iter().enumerate().for_each(|(i, a)| { result.as_mut()[i] = a / length; }); @@ -194,4 +196,20 @@ mod tests { let result = c.length(); assert_eq!(result, d); } + + #[test] + fn normalize() { + let a = [4.0, 3.0, 2.0]; + let b = [0.74278135, 0.55708605, 0.37139067]; + let result = a.normalize(); + assert_eq!(result, b); + } + + #[test] + fn normalize_fails_for_zero_length_vector() { + let a = [0.0, 0.0, 0.0]; + + let result = std::panic::catch_unwind(|| a.normalize()); + assert!(result.is_err()); + } } From 9ea46f90f6e5f82430c8480148a71ce57769d676 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 30 Dec 2022 15:58:10 -0800 Subject: [PATCH 34/67] [add] scale test. --- lambda/src/math/vector.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 47f95a09..7dfcd738 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -109,7 +109,6 @@ where fn scale(&self, scalar: Self::Scalar) -> Self { let mut result = Self::default(); - let length = self.length(); self.as_ref().iter().enumerate().for_each(|(i, a)| { result.as_mut()[i] = a * scalar; }); @@ -212,4 +211,14 @@ mod tests { let result = std::panic::catch_unwind(|| a.normalize()); assert!(result.is_err()); } + + #[test] + fn scale() { + let a = [1.0, 2.0, 3.0]; + let b = [2.0, 4.0, 6.0]; + let scalar = 2.0; + + let result = a.scale(scalar); + assert_eq!(result, b); + } } From ee227b0f2317bbb798ddf83f73f081660d6c9b46 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 30 Dec 2022 15:58:22 -0800 Subject: [PATCH 35/67] [add] matrix implementations. --- lambda/src/core/render/mod.rs | 2 + lambda/src/math/matrix.rs | 70 ++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index 50392372..cb40481b 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -270,6 +270,8 @@ impl RenderContext { ) .expect("Failed to render to the surface"); + // Destroys the frame buffer after the commands have been submitted and the + // frame buffer is no longer needed. match self.frame_buffer { Some(_) => { Rc::try_unwrap(self.frame_buffer.take().unwrap()) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 52bfd90f..43ce4b87 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -3,31 +3,67 @@ use lambda_platform::rand::get_uniformly_random_floats_between; use super::vector::Vector; -pub trait Matrix { - type Scalar: Copy; - type Vector: Vector; - - fn transform(&self, vector: &Self::Vector) -> Self::Vector; - fn determinant(&self) -> Self::Scalar; +pub trait Matrix { + fn add(&self, other: &Self) -> Self; + fn subtract(&self, other: &Self) -> Self; + fn multiply(&self, other: &Self) -> Self; + fn transpose(&self) -> Self; + fn inverse(&self) -> Self; + fn transform(&self, other: &V) -> V; } -impl Matrix for [[f32; 4]; 4] { - type Scalar = f32; - type Vector = [f32; 4]; +/// Matrix implementations for arrays +impl Matrix for T +where + T: AsMut<[V]> + AsRef<[V]> + Default, + V: AsMut<[f32]> + AsRef<[f32]> + Vector + Sized, +{ + fn add(&self, other: &Self) -> Self { + let mut result = Self::default(); + for (i, (a, b)) in + self.as_ref().iter().zip(other.as_ref().iter()).enumerate() + { + result.as_mut()[i] = a.add(b); + } + return result; + } + + fn subtract(&self, other: &Self) -> Self { + let mut result = Self::default(); - fn transform(&self, vector: &Self::Vector) -> Self::Vector { - let mut result = [0.0; 4]; - for (i, row) in self.iter().enumerate() { - for (j, value) in row.iter().enumerate() { - result[i] += value * vector[j]; + for (i, (a, b)) in + self.as_ref().iter().zip(other.as_ref().iter()).enumerate() + { + result.as_mut()[i] = a.subtract(b); + } + return result; + } + + fn multiply(&self, other: &Self) -> Self { + let mut result = Self::default(); + for (i, a) in self.as_ref().iter().enumerate() { + for (j, b) in other.as_ref().iter().enumerate() { + todo!("Matrix multiplication"); } } return result; } - fn determinant(&self) -> Self::Scalar { - let mut result = 0.0; - for (i, value) in self[0].iter().enumerate() {} + fn transpose(&self) -> Self { + let mut result = Self::default(); + for (i, a) in self.as_ref().iter().enumerate() { + for (j, b) in a.as_ref().iter().enumerate() { + result.as_mut()[i].as_mut()[j] = self.as_ref()[j].as_ref()[i]; + } + } return result; } + + fn inverse(&self) -> Self { + todo!() + } + + fn transform(&self, other: &V) -> V { + todo!() + } } From e82b3aeb48221f251bf232f00dbe3bd8ca8819ef Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 30 Dec 2022 17:53:25 -0800 Subject: [PATCH 36/67] [add] square matrix multiplications & initial matrix tests. --- lambda/src/math/matrix.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 43ce4b87..9b3b7855 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -12,10 +12,10 @@ pub trait Matrix { fn transform(&self, other: &V) -> V; } -/// Matrix implementations for arrays -impl Matrix for T +/// Matrix implementations for arrays backed by vectors. +impl Matrix for Array where - T: AsMut<[V]> + AsRef<[V]> + Default, + Array: AsMut<[V]> + AsRef<[V]> + Default, V: AsMut<[f32]> + AsRef<[f32]> + Vector + Sized, { fn add(&self, other: &Self) -> Self { @@ -41,9 +41,15 @@ where fn multiply(&self, other: &Self) -> Self { let mut result = Self::default(); + + // We transpose the other matrix to convert the columns into rows, allowing + // us to compute the new values of each index using the dot product + // function. + let transposed = other.transpose(); + for (i, a) in self.as_ref().iter().enumerate() { - for (j, b) in other.as_ref().iter().enumerate() { - todo!("Matrix multiplication"); + for (j, b) in transposed.as_ref().iter().enumerate() { + result.as_mut()[i].as_mut()[j] += a.dot(&b); } } return result; @@ -67,3 +73,22 @@ where todo!() } } + +#[cfg(test)] +mod tests { + + use super::Matrix; + + #[test] + // Test square matrix multiplication. + fn square_matrix_multiply() { + let m1 = [[1.0, 2.0], [3.0, 4.0]]; + let m2 = [[2.0, 0.0], [1.0, 2.0]]; + + let mut result = m1.multiply(&m2); + assert_eq!(result, [[4.0, 4.0], [10.0, 8.0]]); + + result = m2.multiply(&m1); + assert_eq!(result, [[2.0, 4.0], [7.0, 10.0]]) + } +} From d613beac2f4cb0805f15d9ef6e218d4b476b737a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 31 Dec 2022 14:00:14 -0800 Subject: [PATCH 37/67] [add] addition/subtraction tests. --- lambda/src/math/matrix.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 9b3b7855..058a907d 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -79,6 +79,22 @@ mod tests { use super::Matrix; + #[test] + fn square_matrix_add() { + let a = [[1.0, 2.0], [3.0, 4.0]]; + let b = [[5.0, 6.0], [7.0, 8.0]]; + let c = a.add(&b); + assert_eq!(c, [[6.0, 8.0], [10.0, 12.0]]); + } + + #[test] + fn square_matrix_subtract() { + let a = [[1.0, 2.0], [3.0, 4.0]]; + let b = [[5.0, 6.0], [7.0, 8.0]]; + let c = a.subtract(&b); + assert_eq!(c, [[-4.0, -4.0], [-4.0, -4.0]]); + } + #[test] // Test square matrix multiplication. fn square_matrix_multiply() { From 75b3f175f2f5db9830d9fc7e07b37c178bac5c29 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 31 Dec 2022 14:47:58 -0800 Subject: [PATCH 38/67] [update] transpose implementation. --- lambda/src/math/matrix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 058a907d..93159390 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -58,7 +58,7 @@ where fn transpose(&self) -> Self { let mut result = Self::default(); for (i, a) in self.as_ref().iter().enumerate() { - for (j, b) in a.as_ref().iter().enumerate() { + for j in 0..a.as_ref().len() { result.as_mut()[i].as_mut()[j] = self.as_ref()[j].as_ref()[i]; } } From a5383aa922ca875e201fcb24b581cc30763068cc Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 31 Dec 2022 15:03:21 -0800 Subject: [PATCH 39/67] [add] test for square matrix transpose. --- lambda/src/math/matrix.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 93159390..97ee72ac 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -107,4 +107,11 @@ mod tests { result = m2.multiply(&m1); assert_eq!(result, [[2.0, 4.0], [7.0, 10.0]]) } + + #[test] + fn transpose_square_matrix() { + let m = [[1.0, 2.0], [5.0, 6.0]]; + let t = m.transpose(); + assert_eq!(t, [[1.0, 5.0], [2.0, 6.0]]); + } } From 0c2b6b866574d63d9937720fec6b6076b7af34ec Mon Sep 17 00:00:00 2001 From: vmarcella Date: Mon, 2 Jan 2023 14:08:16 -0800 Subject: [PATCH 40/67] [add] a matrix determinant implementation utilizing laplace expansion. --- lambda/src/math/matrix.rs | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 97ee72ac..6b171154 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -4,12 +4,15 @@ use lambda_platform::rand::get_uniformly_random_floats_between; use super::vector::Vector; pub trait Matrix { + const WIDTH: usize; + const HEIGHT: usize; fn add(&self, other: &Self) -> Self; fn subtract(&self, other: &Self) -> Self; fn multiply(&self, other: &Self) -> Self; fn transpose(&self) -> Self; fn inverse(&self) -> Self; fn transform(&self, other: &V) -> V; + fn determinant(&self) -> f32; } /// Matrix implementations for arrays backed by vectors. @@ -18,6 +21,8 @@ where Array: AsMut<[V]> + AsRef<[V]> + Default, V: AsMut<[f32]> + AsRef<[f32]> + Vector + Sized, { + const WIDTH: usize = 0; + const HEIGHT: usize = 0; fn add(&self, other: &Self) -> Self { let mut result = Self::default(); for (i, (a, b)) in @@ -72,6 +77,46 @@ where fn transform(&self, other: &V) -> V { todo!() } + + /// Computes the determinant of any square matrix using the Laplace expansion. + fn determinant(&self) -> f32 { + let (width, height) = + (self.as_ref()[0].as_ref().len(), self.as_ref().len()); + + if width != height { + panic!("Cannot compute determinant of non-square matrix"); + } + + return match height { + 1 => self.as_ref()[0].as_ref()[0], + 2 => { + let a = self.as_ref()[0].as_ref()[0]; + let b = self.as_ref()[0].as_ref()[1]; + let c = self.as_ref()[1].as_ref()[0]; + let d = self.as_ref()[1].as_ref()[1]; + a * d - b * c + } + _ => { + let mut result = 0.0; + for i in 0..height { + let mut submatrix: Vec> = Vec::with_capacity(height - 1); + for j in 1..height { + let mut row = Vec::new(); + for k in 0..height { + if k != i { + row.push(self.as_ref()[j].as_ref()[k]); + } + } + submatrix.push(row); + } + result += self.as_ref()[0].as_ref()[i] + * submatrix.determinant() + * (-1.0 as f32).powi(i as i32); + } + result + } + }; + } } #[cfg(test)] @@ -114,4 +159,13 @@ mod tests { let t = m.transpose(); assert_eq!(t, [[1.0, 5.0], [2.0, 6.0]]); } + + #[test] + fn square_matrix_determinant() { + let m = [[3.0, 8.0], [4.0, 6.0]]; + assert_eq!(m.determinant(), -14.0); + + let m2 = [[6.0, 1.0, 1.0], [4.0, -2.0, 5.0], [2.0, 8.0, 7.0]]; + assert_eq!(m2.determinant(), -306.0); + } } From 8909dc0d32371139864ab96e8e964640ea6d399d Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 3 Jan 2023 13:50:11 -0800 Subject: [PATCH 41/67] [add] test for non square matrix determinant calls. --- lambda/src/math/matrix.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 6b171154..57c7a76d 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -4,8 +4,6 @@ use lambda_platform::rand::get_uniformly_random_floats_between; use super::vector::Vector; pub trait Matrix { - const WIDTH: usize; - const HEIGHT: usize; fn add(&self, other: &Self) -> Self; fn subtract(&self, other: &Self) -> Self; fn multiply(&self, other: &Self) -> Self; @@ -21,8 +19,6 @@ where Array: AsMut<[V]> + AsRef<[V]> + Default, V: AsMut<[f32]> + AsRef<[f32]> + Vector + Sized, { - const WIDTH: usize = 0; - const HEIGHT: usize = 0; fn add(&self, other: &Self) -> Self { let mut result = Self::default(); for (i, (a, b)) in @@ -168,4 +164,11 @@ mod tests { let m2 = [[6.0, 1.0, 1.0], [4.0, -2.0, 5.0], [2.0, 8.0, 7.0]]; assert_eq!(m2.determinant(), -306.0); } + + #[test] + fn non_square_matrix_determinant() { + let m = [[3.0, 8.0], [4.0, 6.0], [0.0, 1.0]]; + let result = std::panic::catch_unwind(|| m.determinant()); + assert_eq!(false, result.is_ok()) + } } From 334f22bac9af7c0bfd3fb7b62cd77ad669c85d45 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sun, 8 Jan 2023 19:06:06 -0800 Subject: [PATCH 42/67] [update] matrix implementations, add submatrix function for slicing arrays. --- lambda/src/math/matrix.rs | 58 +++++++++++++++++++++++++++++++++++++-- lambda/src/math/vector.rs | 5 ++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 57c7a76d..8862b8fc 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -11,6 +11,32 @@ pub trait Matrix { fn inverse(&self) -> Self; fn transform(&self, other: &V) -> V; fn determinant(&self) -> f32; + fn size(&self) -> (usize, usize); + fn row(&self, row: usize) -> &V; + fn at(&self, row: usize, column: usize) -> V::Scalar; +} + +/// Obtain the submatrix of the input matrix where the submatrix +pub fn submatrix, M: Matrix>( + matrix: M, + row: usize, + column: usize, +) -> Vec> { + let mut submatrix = Vec::new(); + let (rows, columns) = matrix.size(); + + for k in 0..rows { + if k != row { + let mut row = Vec::new(); + for l in 0..columns { + if l != column { + row.push(matrix.at(k, l)); + } + } + submatrix.push(row); + } + } + return submatrix; } /// Matrix implementations for arrays backed by vectors. @@ -113,12 +139,30 @@ where } }; } + + /// Return the size as a (rows, columns). + fn size(&self) -> (usize, usize) { + return (self.as_ref().len(), self.as_ref()[0].as_ref().len()); + } + + /// Return a reference to the row. + fn row(&self, row: usize) -> &V { + return &self.as_ref()[row]; + } + + /// + fn at(&self, row: usize, column: usize) -> ::Scalar { + return self.as_ref()[row].as_ref()[column]; + } } #[cfg(test)] mod tests { - use super::Matrix; + use super::{ + submatrix, + Matrix, + }; #[test] fn square_matrix_add() { @@ -169,6 +213,16 @@ mod tests { fn non_square_matrix_determinant() { let m = [[3.0, 8.0], [4.0, 6.0], [0.0, 1.0]]; let result = std::panic::catch_unwind(|| m.determinant()); - assert_eq!(false, result.is_ok()) + assert_eq!(false, result.is_ok()); + } + + #[test] + fn submatrix_on_matrix_array() { + let matrix = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; + + let expected_submatrix = vec![vec![2.0, 3.0], vec![8.0, 9.0]]; + let actual_submatrix = submatrix(matrix, 1, 0); + + assert_eq!(expected_submatrix, actual_submatrix); } } diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 7dfcd738..5e42d0ee 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -11,6 +11,7 @@ pub trait Vector { fn cross(&self, other: &Self) -> Self; fn length(&self) -> Self::Scalar; fn normalize(&self) -> Self; + fn size(&self) -> usize; } impl Vector for T @@ -115,6 +116,10 @@ where return result; } + + fn size(&self) -> usize { + return self.as_ref().len(); + } } #[cfg(test)] From 3b6f5ef82a37f771837206a5515caede1eb1228e Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 13 Jan 2023 21:24:49 -0800 Subject: [PATCH 43/67] [add] generic translation matrix implementation. --- lambda/src/math/matrix.rs | 82 ++++++++++++++++++++++++++++++++++++--- lambda/src/math/vector.rs | 10 +++++ 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 8862b8fc..8a831d52 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -3,6 +3,9 @@ use lambda_platform::rand::get_uniformly_random_floats_between; use super::vector::Vector; + +// -------------------------------- MATRIX ------------------------------------- + pub trait Matrix { fn add(&self, other: &Self) -> Self; fn subtract(&self, other: &Self) -> Self; @@ -14,11 +17,15 @@ pub trait Matrix { fn size(&self) -> (usize, usize); fn row(&self, row: usize) -> &V; fn at(&self, row: usize, column: usize) -> V::Scalar; + fn update(&mut self, row: usize, column: usize, value: V::Scalar); } -/// Obtain the submatrix of the input matrix where the submatrix -pub fn submatrix, M: Matrix>( - matrix: M, +// -------------------------------- FUNCTIONS ---------------------------------- + +/// Obtain the submatrix of the input matrix starting from the given row & +/// column. +pub fn submatrix, MatrixLike: Matrix>( + matrix: MatrixLike, row: usize, column: usize, ) -> Vec> { @@ -39,6 +46,38 @@ pub fn submatrix, M: Matrix>( return submatrix; } +pub fn translation_matrix< + InputVector: Vector, + ResultingVector: Vector, + MatrixLike: Matrix + Default, +>( + vector: InputVector, +) -> MatrixLike { + let mut result = MatrixLike::default(); + let (rows, columns) = result.size(); + assert_eq!( + rows - 1, + vector.size(), + "Vector must contain one less element than the vectors of the input matrix" + ); + + for i in 0..rows { + for j in 0..columns { + if i == j { + result.update(i, j, 1.0); + } else if j == columns - 1 { + result.update(i, j, vector.at(i)); + } else { + result.update(i, j, 0.0); + } + } + } + + return result; +} + +// -------------------------- ARRAY IMPLEMENTATION ----------------------------- + /// Matrix implementations for arrays backed by vectors. impl Matrix for Array where @@ -100,7 +139,7 @@ where todo!() } - /// Computes the determinant of any square matrix using the Laplace expansion. + /// Computes the determinant of any square matrix using Laplace expansion. fn determinant(&self) -> f32 { let (width, height) = (self.as_ref()[0].as_ref().len(), self.as_ref().len()); @@ -154,8 +193,14 @@ where fn at(&self, row: usize, column: usize) -> ::Scalar { return self.as_ref()[row].as_ref()[column]; } + + fn update(&mut self, row: usize, column: usize, new_value: V::Scalar) { + self.as_mut()[row].as_mut()[column] = new_value; + } } +// ---------------------------------- TESTS ------------------------------------ + #[cfg(test)] mod tests { @@ -163,6 +208,7 @@ mod tests { submatrix, Matrix, }; + use crate::math::matrix::translation_matrix; #[test] fn square_matrix_add() { @@ -217,7 +263,7 @@ mod tests { } #[test] - fn submatrix_on_matrix_array() { + fn submatrix_for_matrix_array() { let matrix = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; let expected_submatrix = vec![vec![2.0, 3.0], vec![8.0, 9.0]]; @@ -225,4 +271,30 @@ mod tests { assert_eq!(expected_submatrix, actual_submatrix); } + + #[test] + fn translate_matrix() { + let matrix = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; + let translation: [[f32; 3]; 3] = translation_matrix([56.0, 5.0]); + assert_eq!( + translation, + [[1.0, 0.0, 56.0], [0.0, 1.0, 5.0], [0.0, 0.0, 1.0]] + ); + + let matrixfour = [ + [1.0, 2.0, 3.0, 4.0], + [4.0, 5.0, 6.0, 7.0], + [7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 13.0, 14.0], + ]; + + let translation: [[f32; 4]; 4] = translation_matrix([10.0, 2.0, 3.0]); + let expected = [ + [1.0, 0.0, 0.0, 10.0], + [0.0, 1.0, 0.0, 2.0], + [0.0, 0.0, 1.0, 3.0], + [0.0, 0.0, 0.0, 1.0], + ]; + assert_eq!(translation, expected); + } } diff --git a/lambda/src/math/vector.rs b/lambda/src/math/vector.rs index 5e42d0ee..d4993a2d 100644 --- a/lambda/src/math/vector.rs +++ b/lambda/src/math/vector.rs @@ -12,6 +12,8 @@ pub trait Vector { fn length(&self) -> Self::Scalar; fn normalize(&self) -> Self; fn size(&self) -> usize; + fn at(&self, index: usize) -> Self::Scalar; + fn update(&mut self, index: usize, value: Self::Scalar); } impl Vector for T @@ -120,6 +122,14 @@ where fn size(&self) -> usize { return self.as_ref().len(); } + + fn at(&self, index: usize) -> Self::Scalar { + return self.as_ref()[index]; + } + + fn update(&mut self, index: usize, value: Self::Scalar) { + self.as_mut()[index] = value; + } } #[cfg(test)] From cd7cc38b6476156d70a296f1ad18008d62088f53 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 13 Jan 2023 21:25:42 -0800 Subject: [PATCH 44/67] [remove] redundant matrices. --- lambda/src/math/matrix.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 8a831d52..938eb964 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -274,20 +274,12 @@ mod tests { #[test] fn translate_matrix() { - let matrix = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; let translation: [[f32; 3]; 3] = translation_matrix([56.0, 5.0]); assert_eq!( translation, [[1.0, 0.0, 56.0], [0.0, 1.0, 5.0], [0.0, 0.0, 1.0]] ); - let matrixfour = [ - [1.0, 2.0, 3.0, 4.0], - [4.0, 5.0, 6.0, 7.0], - [7.0, 8.0, 9.0, 10.0], - [11.0, 12.0, 13.0, 14.0], - ]; - let translation: [[f32; 4]; 4] = translation_matrix([10.0, 2.0, 3.0]); let expected = [ [1.0, 0.0, 0.0, 10.0], From 6983f5ecb31a67f47ec7396acce5dd002c7b06d3 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 13 Jan 2023 23:16:29 -0800 Subject: [PATCH 45/67] [add] basic perspective matrix implementation for 4x4 matrices. --- lambda/src/math/matrix.rs | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 938eb964..4ca03d67 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -76,6 +76,34 @@ pub fn translation_matrix< return result; } +pub fn perspective_matrix< + V: Vector, + MatrixLike: Matrix + Default, +>( + fov: V::Scalar, + aspect: V::Scalar, + z_near: V::Scalar, + z_far: V::Scalar, +) -> MatrixLike { + let mut result = MatrixLike::default(); + let (rows, columns) = result.size(); + debug_assert_eq!( + rows, columns, + "Matrix must be square to be a perspective matrix" + ); + debug_assert_eq!(rows, 4, "Matrix must be 4x4 to be a perspective matrix"); + let f = 1.0 / (fov / 2.0).tan(); + let range = z_near - z_far; + + result.update(0, 0, f / aspect); + result.update(1, 1, f); + result.update(2, 2, (z_near + z_far) / range); + result.update(2, 3, -1.0); + result.update(3, 2, (2.0 * z_near * z_far) / range); + + return result; +} + // -------------------------- ARRAY IMPLEMENTATION ----------------------------- /// Matrix implementations for arrays backed by vectors. @@ -205,6 +233,7 @@ where mod tests { use super::{ + perspective_matrix, submatrix, Matrix, }; @@ -289,4 +318,18 @@ mod tests { ]; assert_eq!(translation, expected); } + + #[test] + fn perspective_matrix_test() { + let perspective: [[f32; 4]; 4] = perspective_matrix(1.0, 1.0, 1.0, 0.0); + let f = 1.0 / (1.0 / 2.0 as f32).tan(); + let expected: [[f32; 4]; 4] = [ + [f / 1.0, 0.0, 0.0, 0.0], + [0.0, f, 0.0, 0.0], + [0.0, 0.0, 1.0, -1.0], + [0.0, 0.0, 0.0, 0.0], + ]; + + assert_eq!(perspective, expected); + } } From 9d2bb2d80e42ddebbb9ba5ec5afe06029436bf1b Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 14 Jan 2023 15:55:41 -0800 Subject: [PATCH 46/67] [update] fov in perspective test to be 1/4 turns (90 degrees) & add matrices to push_constants examples. --- lambda/examples/push_constants.rs | 7 +++++ lambda/src/math/matrix.rs | 46 +++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index be40b234..79473d7a 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -57,6 +57,13 @@ impl Component for PushConstantsExample { &mut self, render_context: &mut lambda::core::render::RenderContext, ) -> Vec { + use math::vector::Vector; + let mut camera = [0.0, 0.0, -2.0]; + let view: [[f32; 4]; 4] = math::matrix::translation_matrix(camera); + let mut projection: [[f32; 4]; 4] = + math::matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0); + projection.as_mut()[1].as_mut()[1] *= -1.0; + todo!() } } diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 4ca03d67..403b8ae2 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -46,14 +46,15 @@ pub fn submatrix, MatrixLike: Matrix>( return submatrix; } +/// Creates a translation matrix with the given translation vector. The output vector pub fn translation_matrix< InputVector: Vector, ResultingVector: Vector, - MatrixLike: Matrix + Default, + OutputMatrix: Matrix + Default, >( vector: InputVector, -) -> MatrixLike { - let mut result = MatrixLike::default(); +) -> OutputMatrix { + let mut result = OutputMatrix::default(); let (rows, columns) = result.size(); assert_eq!( rows - 1, @@ -76,30 +77,40 @@ pub fn translation_matrix< return result; } +/// Creates a 4x4 perspective matrix given the fov in turns (unit between +/// 0..2pi radians), aspect ratio, near clipping plane (also known as z_near), +/// and far clipping plane (also known as z_far). Enforces that the matrix being +/// created is square in both debug and release builds, but only enforces that +/// the output matrix is 4x4 in debug builds. pub fn perspective_matrix< V: Vector, MatrixLike: Matrix + Default, >( fov: V::Scalar, - aspect: V::Scalar, - z_near: V::Scalar, - z_far: V::Scalar, + aspect_ratio: V::Scalar, + near_clipping_plane: V::Scalar, + far_clipping_plane: V::Scalar, ) -> MatrixLike { let mut result = MatrixLike::default(); let (rows, columns) = result.size(); - debug_assert_eq!( + assert_eq!( rows, columns, "Matrix must be square to be a perspective matrix" ); debug_assert_eq!(rows, 4, "Matrix must be 4x4 to be a perspective matrix"); - let f = 1.0 / (fov / 2.0).tan(); - let range = z_near - z_far; + let fov_in_radians = fov * std::f32::consts::PI * 2.0; + let f = 1.0 / (fov_in_radians / 2.0).tan(); + let range = near_clipping_plane - far_clipping_plane; - result.update(0, 0, f / aspect); + result.update(0, 0, f / aspect_ratio); result.update(1, 1, f); - result.update(2, 2, (z_near + z_far) / range); + result.update(2, 2, (near_clipping_plane + far_clipping_plane) / range); result.update(2, 3, -1.0); - result.update(3, 2, (2.0 * z_near * z_far) / range); + result.update( + 3, + 2, + (2.0 * near_clipping_plane * far_clipping_plane) / range, + ); return result; } @@ -321,10 +332,15 @@ mod tests { #[test] fn perspective_matrix_test() { - let perspective: [[f32; 4]; 4] = perspective_matrix(1.0, 1.0, 1.0, 0.0); - let f = 1.0 / (1.0 / 2.0 as f32).tan(); + let perspective: [[f32; 4]; 4] = + perspective_matrix(1.0 / 4.0, 1.0, 1.0, 0.0); + + // Compute the field of view values used by the perspective matrix by hand. + let fov_radians = (1.0 / 4.0) * std::f32::consts::PI * 2.0; + let f = 1.0 / (fov_radians as f32 / 2.0).tan(); + let expected: [[f32; 4]; 4] = [ - [f / 1.0, 0.0, 0.0, 0.0], + [f, 0.0, 0.0, 0.0], [0.0, f, 0.0, 0.0], [0.0, 0.0, 1.0, -1.0], [0.0, 0.0, 0.0, 0.0], From 50869a4a4da0003beaa6a24e1bb08982f35d522e Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 14 Jan 2023 15:56:57 -0800 Subject: [PATCH 47/67] [remove] unused code. --- lambda/examples/push_constants.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 79473d7a..4097c106 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -36,6 +36,7 @@ impl Component for PushConstantsExample { &mut self, render_context: &mut lambda::core::render::RenderContext, ) { + todo!() } fn on_detach( From 96fd91ce5668a5399a5fec7f59908a48c99c3196 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 14 Jan 2023 21:50:15 -0800 Subject: [PATCH 48/67] [add] matrix rotations for 4x4 matrices & tests to validate. --- lambda/src/math/matrix.rs | 176 ++++++++++++++++++++++++++++++++++++-- lambda/src/math/mod.rs | 19 ++++ 2 files changed, 190 insertions(+), 5 deletions(-) diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 403b8ae2..1540b071 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -3,6 +3,10 @@ use lambda_platform::rand::get_uniformly_random_floats_between; use super::vector::Vector; +use crate::{ + assert_approximately_equal, + math::turns_to_radians, +}; // -------------------------------- MATRIX ------------------------------------- @@ -77,6 +81,70 @@ pub fn translation_matrix< return result; } +/// Rotates the input matrix by the given number of turns around the given axis. +/// The axis must be a unit vector and the turns must be in the range [0, 1). +/// The rotation is counter-clockwise when looking down the axis. +pub fn rotate_matrix< + InputVector: Vector, + ResultingVector: Vector, + OutputMatrix: Matrix + Default + Clone, +>( + matrix_to_rotate: OutputMatrix, + axis_to_rotate: InputVector, + angle_in_turns: f32, +) -> OutputMatrix { + let (rows, columns) = matrix_to_rotate.size(); + assert_eq!(rows, columns, "Matrix must be square"); + assert_eq!(rows, 4, "Matrix must be 4x4"); + assert_eq!( + axis_to_rotate.size(), + 3, + "Axis vector must have 3 elements (x, y, z)" + ); + + // Convert the angle from turns to radians + let angle_in_radians = turns_to_radians(angle_in_turns); + let cosine_of_angle = angle_in_radians.cos(); + let sin_of_angle = -angle_in_radians.sin(); + + let t = 1.0 - cosine_of_angle; + let x = axis_to_rotate.at(0); + let y = axis_to_rotate.at(1); + let z = axis_to_rotate.at(2); + + let mut rotation_matrix = OutputMatrix::default(); + + let rotation = [ + [ + t * x * x + cosine_of_angle, + t * x * y - (sin_of_angle * z), + t * x * z + (sin_of_angle * y), + 0.0, + ], + [ + t * x * y + (sin_of_angle * z), + t * y * y + cosine_of_angle, + t * y * z - (sin_of_angle * x), + 0.0, + ], + [ + t * x * z - (sin_of_angle * y), + t * y * z - (sin_of_angle * x), + t * z * z + cosine_of_angle, + 0.0, + ], + [0.0, 0.0, 0.0, 1.0], + ]; + + for i in 0..rows { + for j in 0..columns { + rotation_matrix.update(i, j, rotation[i][j]); + } + } + + return matrix_to_rotate.multiply(&rotation_matrix); +} + /// Creates a 4x4 perspective matrix given the fov in turns (unit between /// 0..2pi radians), aspect ratio, near clipping plane (also known as z_near), /// and far clipping plane (also known as z_far). Enforces that the matrix being @@ -98,7 +166,7 @@ pub fn perspective_matrix< "Matrix must be square to be a perspective matrix" ); debug_assert_eq!(rows, 4, "Matrix must be 4x4 to be a perspective matrix"); - let fov_in_radians = fov * std::f32::consts::PI * 2.0; + let fov_in_radians = turns_to_radians(fov); let f = 1.0 / (fov_in_radians / 2.0).tan(); let range = near_clipping_plane - far_clipping_plane; @@ -115,9 +183,70 @@ pub fn perspective_matrix< return result; } +pub fn zeroed_matrix< + V: Vector, + MatrixLike: Matrix + Default, +>( + rows: usize, + columns: usize, +) -> MatrixLike { + let mut result = MatrixLike::default(); + for i in 0..rows { + for j in 0..columns { + result.update(i, j, 0.0); + } + } + return result; +} + +/// Creates a new matrix with the given number of rows and columns, and fills it +/// with the given value. +pub fn filled_matrix< + V: Vector, + MatrixLike: Matrix + Default, +>( + rows: usize, + columns: usize, + value: V::Scalar, +) -> MatrixLike { + let mut result = MatrixLike::default(); + for i in 0..rows { + for j in 0..columns { + result.update(i, j, value); + } + } + return result; +} + +pub fn identity_matrix< + V: Vector, + MatrixLike: Matrix + Default, +>( + rows: usize, + columns: usize, +) -> MatrixLike { + assert_eq!( + rows, columns, + "Matrix must be square to be an identity matrix" + ); + let mut result = MatrixLike::default(); + for i in 0..rows { + for j in 0..columns { + if i == j { + result.update(i, j, 1.0); + } else { + result.update(i, j, 0.0); + } + } + } + return result; +} + // -------------------------- ARRAY IMPLEMENTATION ----------------------------- -/// Matrix implementations for arrays backed by vectors. +/// Matrix implementations for arrays of f32 arrays. Including the trait Matrix into +/// your code will allow you to use these function implementation for any array +/// of f32 arrays. impl Matrix for Array where Array: AsMut<[V]> + AsRef<[V]> + Default, @@ -244,11 +373,16 @@ where mod tests { use super::{ + filled_matrix, perspective_matrix, + rotate_matrix, submatrix, Matrix, }; - use crate::math::matrix::translation_matrix; + use crate::math::{ + matrix::translation_matrix, + turns_to_radians, + }; #[test] fn square_matrix_add() { @@ -336,8 +470,8 @@ mod tests { perspective_matrix(1.0 / 4.0, 1.0, 1.0, 0.0); // Compute the field of view values used by the perspective matrix by hand. - let fov_radians = (1.0 / 4.0) * std::f32::consts::PI * 2.0; - let f = 1.0 / (fov_radians as f32 / 2.0).tan(); + let fov_radians = turns_to_radians(1.0 / 4.0); + let f = 1.0 / (fov_radians / 2.0).tan(); let expected: [[f32; 4]; 4] = [ [f, 0.0, 0.0, 0.0], @@ -348,4 +482,36 @@ mod tests { assert_eq!(perspective, expected); } + + #[test] + fn rotate_matrices() { + let rotation_matrix: [[f32; 4]; 4] = filled_matrix(4, 4, 1.0); + let rotated_matrix = rotate_matrix(rotation_matrix, [0.0, 0.0, 1.0], 0.0); + assert_eq!(rotated_matrix, rotation_matrix); + + let matrix = [ + [1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0], + [9.0, 10.0, 11.0, 12.0], + [13.0, 14.0, 15.0, 16.0], + ]; + let rotated = rotate_matrix(matrix, [0.0, 1.0, 0.0], 0.25); + let expected = [ + [3.0, 1.9999999, -1.0000001, 4.0], + [7.0, 5.9999995, -5.0000005, 8.0], + [11.0, 9.999999, -9.000001, 12.0], + [14.999999, 13.999999, -13.000001, 16.0], + ]; + + println!("rotated: {:?}", rotated); + for i in 0..4 { + for j in 0..4 { + crate::assert_approximately_equal!( + rotated.at(i, j), + expected.at(i, j), + 0.1 + ); + } + } + } } diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index e5616234..a7a2f028 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -2,3 +2,22 @@ pub mod matrix; pub mod vector; + +/// Convert a turn into radians. +fn turns_to_radians(turns: f32) -> f32 { + return turns * std::f32::consts::PI * 2.0; +} + +#[macro_export] +macro_rules! assert_approximately_equal { + ($a:expr, $b:expr, $eps:expr) => {{ + let (a, b, eps) = ($a, $b, $eps); + assert!( + (a - b).abs() < eps, + "{} is not approximately equal to {} with an epsilon of {}", + a, + b, + eps + ); + }}; +} From 3018d8c95d88a547b1bf5da453c22e80208fab2a Mon Sep 17 00:00:00 2001 From: vmarcella Date: Sat, 14 Jan 2023 23:29:56 -0800 Subject: [PATCH 49/67] [update] pipeline so that the fragment shader is optional, add render commands & push constant implementation for the new demo. --- crates/lambda-platform/src/gfx/pipeline.rs | 15 ++- lambda/examples/push_constants.rs | 145 +++++++++++++++++++-- lambda/src/core/render/pipeline.rs | 22 ++-- lambda/src/math/matrix.rs | 18 ++- tools/lambda_rs_demo/src/main.rs | 6 +- tools/triangles_demo/src/main.rs | 2 +- 6 files changed, 173 insertions(+), 35 deletions(-) diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index 0aa608f5..b01766e1 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -85,7 +85,7 @@ impl RenderPipelineBuilder { gpu: &Gpu, render_pass: &super::render_pass::RenderPass, vertex_shader: &ShaderModule, - fragment_shader: &ShaderModule, + fragment_shader: Option<&ShaderModule>, ) -> RenderPipeline { // TODO(vmarcella): The pipeline layout should be configurable through the // RenderPipelineBuilder. @@ -104,10 +104,13 @@ impl RenderPipelineBuilder { let primitive_assembler = super::assembler::PrimitiveAssemblerBuilder::new().build(vertex_shader); - let fragment_entry = internal::EntryPoint { - entry: fragment_shader.entry(), - module: super::internal::module_for(fragment_shader), - specialization: fragment_shader.specializations().clone(), + let fragment_entry = match fragment_shader { + Some(shader) => Some(internal::EntryPoint:: { + entry: "main", + module: super::internal::module_for(shader), + specialization: gfx_hal::pso::Specialization::default(), + }), + None => None, }; let mut pipeline_desc = internal::GraphicsPipelineDesc::new( @@ -116,7 +119,7 @@ impl RenderPipelineBuilder { cull_face: internal::Face::BACK, ..internal::Rasterizer::FILL }, - Some(fragment_entry), + fragment_entry, &pipeline_layout, internal::Subpass { index: 0, diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 4097c106..3a54606b 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -1,12 +1,41 @@ use lambda::{ core::{ component::Component, - render::shader, + render::{ + command::RenderCommand, + pipeline::{ + RenderPipeline, + RenderPipelineBuilder, + }, + render_pass::{ + RenderPass, + RenderPassBuilder, + }, + shader::{ + Shader, + ShaderBuilder, + }, + viewport, + ResourceId, + }, runtime::start_runtime, }, - math, + math::{ + matrix::{ + self, + Matrix, + }, + vector::Vector, + }, runtimes::GenericRuntimeBuilder, }; +use lambda_platform::{ + gfx::pipeline::PipelineStage, + shaderc::{ + ShaderKind, + VirtualShader, + }, +}; const VERTEX_SHADER_SOURCE: &str = r#" #version 450 @@ -29,14 +58,44 @@ void main() { "#; -struct PushConstantsExample {} +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct PushConstant { + data: [f32; 4], + render_matrix: [[f32; 4]; 4], +} + +pub struct PushConstantsExample { + frame_number: u64, + shader: Shader, + render_pipeline: Option, + render_pass: Option, +} + +pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] { + let bytes = unsafe { + let size_in_bytes = std::mem::size_of::(); + let size_in_u32 = size_in_bytes / std::mem::size_of::(); + let ptr = push_constants as *const PushConstant as *const u32; + std::slice::from_raw_parts(ptr, size_in_u32) + }; + + return bytes; +} impl Component for PushConstantsExample { fn on_attach( &mut self, render_context: &mut lambda::core::render::RenderContext, ) { - todo!() + let render_pass = RenderPassBuilder::new().build(&render_context); + let push_constant_size = std::mem::size_of::() as u32; + let pipeline = RenderPipelineBuilder::new() + .with_push_constant(PipelineStage::VERTEX, push_constant_size) + .build(render_context, &render_pass, &self.shader, None); + + self.render_pass = Some(render_context.attach_render_pass(render_pass)); + self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); } fn on_detach( @@ -50,28 +109,94 @@ impl Component for PushConstantsExample { todo!() } + /// Update the frame number every frame. fn on_update(&mut self, last_frame: &std::time::Duration) { - todo!() + self.frame_number += 1; } fn on_render( &mut self, render_context: &mut lambda::core::render::RenderContext, ) -> Vec { - use math::vector::Vector; let mut camera = [0.0, 0.0, -2.0]; - let view: [[f32; 4]; 4] = math::matrix::translation_matrix(camera); + let view: [[f32; 4]; 4] = matrix::translation_matrix(camera); + + // Create a projection matrix. let mut projection: [[f32; 4]; 4] = - math::matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0); + matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0); projection.as_mut()[1].as_mut()[1] *= -1.0; - todo!() + // Rotate model. + let model: [[f32; 4]; 4] = matrix::rotate_matrix( + matrix::filled_matrix(4, 4, 1.0), + [0.0, 1.0, 0.0], + 0.4 * self.frame_number as f32, + ); + + // Create render matrix. + let mesh_matrix = projection.multiply(&view).multiply(&model); + + // Create viewport. + let viewport = viewport::ViewportBuilder::new().build(800, 600); + + let render_pipeline = self + .render_pipeline + .expect("No render pipeline actively set for rendering."); + + let mut commands = vec![ + RenderCommand::SetViewports { + start_at: 0, + viewports: vec![viewport.clone()], + }, + RenderCommand::SetScissors { + start_at: 0, + viewports: vec![viewport.clone()], + }, + RenderCommand::SetPipeline { + pipeline: render_pipeline.clone(), + }, + RenderCommand::BeginRenderPass { + render_pass: self + .render_pass + .expect("Cannot begin the render pass when it doesn't exist.") + .clone(), + viewport: viewport.clone(), + }, + RenderCommand::PushConstants { + pipeline: render_pipeline.clone(), + stage: PipelineStage::VERTEX, + offset: 0, + bytes: Vec::from(push_constants_to_bytes(&PushConstant { + data: [0.0, 0.0, 0.0, 0.0], + render_matrix: mesh_matrix, + })), + }, + RenderCommand::Draw { vertices: 0..3 }, + ]; + commands.push(RenderCommand::EndRenderPass); + + return commands; } } impl Default for PushConstantsExample { fn default() -> Self { - return Self {}; + let triangle_in_3d = VirtualShader::Source { + source: VERTEX_SHADER_SOURCE.to_string(), + kind: ShaderKind::Vertex, + entry_point: "main".to_string(), + name: "push_constants".to_string(), + }; + + let mut builder = ShaderBuilder::new(); + let shader = builder.build(triangle_in_3d); + + return Self { + frame_number: 0, + shader, + render_pipeline: None, + render_pass: None, + }; } } diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index f16507c3..2dc165c5 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -69,7 +69,7 @@ impl RenderPipelineBuilder { render_context: &mut RenderContext, render_pass: &super::render_pass::RenderPass, vertex_shader: &Shader, - fragment_shader: &Shader, + fragment_shader: Option<&Shader>, ) -> RenderPipeline { let vertex_shader_module = ShaderModuleBuilder::new().build( mut_gpu_from_context(render_context), @@ -77,11 +77,14 @@ impl RenderPipelineBuilder { ShaderModuleType::Vertex, ); - let fragment_shader_module = ShaderModuleBuilder::new().build( - mut_gpu_from_context(render_context), - &fragment_shader.as_binary(), - ShaderModuleType::Fragment, - ); + let fragment_shader_module = match fragment_shader { + Some(shader) => Some(ShaderModuleBuilder::new().build( + mut_gpu_from_context(render_context), + &shader.as_binary(), + ShaderModuleType::Fragment, + )), + None => None, + }; let render_pipeline = lambda_platform::gfx::pipeline::RenderPipelineBuilder::new() @@ -90,11 +93,14 @@ impl RenderPipelineBuilder { gpu_from_context(render_context), &platform_render_pass_from_render_pass(render_pass), &vertex_shader_module, - &fragment_shader_module, + fragment_shader_module.as_ref(), ); + // Clean up shader modules. vertex_shader_module.destroy(mut_gpu_from_context(render_context)); - fragment_shader_module.destroy(mut_gpu_from_context(render_context)); + if let Some(fragment_shader_module) = fragment_shader_module { + fragment_shader_module.destroy(mut_gpu_from_context(render_context)); + } return RenderPipeline { pipeline: Rc::new(render_pipeline), diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 1540b071..0408d0d1 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -2,10 +2,9 @@ use lambda_platform::rand::get_uniformly_random_floats_between; -use super::vector::Vector; -use crate::{ - assert_approximately_equal, - math::turns_to_radians, +use super::{ + turns_to_radians, + vector::Vector, }; // -------------------------------- MATRIX ------------------------------------- @@ -183,6 +182,7 @@ pub fn perspective_matrix< return result; } +/// Create a matrix of any size that is filled with zeros. pub fn zeroed_matrix< V: Vector, MatrixLike: Matrix + Default, @@ -218,6 +218,7 @@ pub fn filled_matrix< return result; } +/// Creates an identity matrix of the given size. pub fn identity_matrix< V: Vector, MatrixLike: Matrix + Default, @@ -483,12 +484,15 @@ mod tests { assert_eq!(perspective, expected); } + /// Test the rotation matrix for a 3D rotation. #[test] fn rotate_matrices() { - let rotation_matrix: [[f32; 4]; 4] = filled_matrix(4, 4, 1.0); - let rotated_matrix = rotate_matrix(rotation_matrix, [0.0, 0.0, 1.0], 0.0); - assert_eq!(rotated_matrix, rotation_matrix); + // Test a zero turn rotation. + let matrix: [[f32; 4]; 4] = filled_matrix(4, 4, 1.0); + let rotated_matrix = rotate_matrix(matrix, [0.0, 0.0, 1.0], 0.0); + assert_eq!(rotated_matrix, matrix); + // Test a 90 degree rotation. let matrix = [ [1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], diff --git a/tools/lambda_rs_demo/src/main.rs b/tools/lambda_rs_demo/src/main.rs index a842248b..1bc4ec46 100644 --- a/tools/lambda_rs_demo/src/main.rs +++ b/tools/lambda_rs_demo/src/main.rs @@ -26,7 +26,7 @@ use lambda::{ }; pub struct DemoComponent { - triangle_vertex: Shader, + fragment_shader: Shader, vertex_shader: Shader, render_pass_id: Option, render_pipeline_id: Option, @@ -44,7 +44,7 @@ impl Component for DemoComponent { render_context, &render_pass, &self.vertex_shader, - &self.triangle_vertex, + Some(&self.fragment_shader), ); // Attach the render pass and pipeline to the render context @@ -176,7 +176,7 @@ impl Default for DemoComponent { return DemoComponent { vertex_shader: vs, - triangle_vertex: fs, + fragment_shader: fs, render_pass_id: None, render_pipeline_id: None, width: 800, diff --git a/tools/triangles_demo/src/main.rs b/tools/triangles_demo/src/main.rs index 11160878..9ab2f4ce 100644 --- a/tools/triangles_demo/src/main.rs +++ b/tools/triangles_demo/src/main.rs @@ -51,7 +51,7 @@ impl Component for TrianglesComponent { render_context, &render_pass, &self.vertex_shader, - &self.triangle_vertex, + Some(&self.triangle_vertex), ); self.render_pass = Some(render_context.attach_render_pass(render_pass)); From 4b8328da4386399191a32b077dbbaf4d2a3e34a9 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Mon, 16 Jan 2023 17:39:05 -0800 Subject: [PATCH 50/67] [add] vertex buffer & mesh structs for implementing meshes. --- crates/lambda-platform/src/vertex.rs | 0 lambda/examples/push_constants.rs | 2 +- lambda/src/core/render/mesh.rs | 6 ++++++ lambda/src/core/render/mod.rs | 3 +++ lambda/src/core/render/vertex.rs | 6 ++++++ lambda/src/math/mod.rs | 7 +++++++ 6 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 crates/lambda-platform/src/vertex.rs create mode 100644 lambda/src/core/render/mesh.rs create mode 100644 lambda/src/core/render/vertex.rs diff --git a/crates/lambda-platform/src/vertex.rs b/crates/lambda-platform/src/vertex.rs new file mode 100644 index 00000000..e69de29b diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 3a54606b..11f711ef 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -123,7 +123,7 @@ impl Component for PushConstantsExample { // Create a projection matrix. let mut projection: [[f32; 4]; 4] = - matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0); + matrix::perspective_matrix(0.25, 1700.0 / 900.0, 0.1, 200.0); projection.as_mut()[1].as_mut()[1] *= -1.0; // Rotate model. diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs new file mode 100644 index 00000000..a1fbcaad --- /dev/null +++ b/lambda/src/core/render/mesh.rs @@ -0,0 +1,6 @@ +use super::vertex::Vertex; + +pub struct Mesh { + pub vertices: Vec, + pub indices: Vec, +} diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index cb40481b..86e12efb 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -1,10 +1,13 @@ //! High level Rendering API designed for cross platform rendering and //! windowing. +// Module Exports pub mod command; +pub mod mesh; pub mod pipeline; pub mod render_pass; pub mod shader; +pub mod vertex; pub mod viewport; pub mod window; diff --git a/lambda/src/core/render/vertex.rs b/lambda/src/core/render/vertex.rs new file mode 100644 index 00000000..13a18e1f --- /dev/null +++ b/lambda/src/core/render/vertex.rs @@ -0,0 +1,6 @@ +#[derive(Clone, Copy, Debug)] +pub struct Vertex { + pub position: [f32; 3], + pub normal: [f32; 3], + pub color: [f32; 3], +} diff --git a/lambda/src/math/mod.rs b/lambda/src/math/mod.rs index a7a2f028..cfbc1380 100644 --- a/lambda/src/math/mod.rs +++ b/lambda/src/math/mod.rs @@ -3,12 +3,19 @@ pub mod matrix; pub mod vector; +pub enum Angle { + Radians(f32), + Degrees(f32), + Turns(f32), +} + /// Convert a turn into radians. fn turns_to_radians(turns: f32) -> f32 { return turns * std::f32::consts::PI * 2.0; } #[macro_export] +/// Assert that two values are equal, with a given tolerance. macro_rules! assert_approximately_equal { ($a:expr, $b:expr, $eps:expr) => {{ let (a, b, eps) = ($a, $b, $eps); From 7ba90aa963d24cee27944ba618914b039f4bf54f Mon Sep 17 00:00:00 2001 From: vmarcella Date: Mon, 16 Jan 2023 22:10:01 -0800 Subject: [PATCH 51/67] [add] mesh and vertex builders alongside tests. --- lambda/src/core/render/mesh.rs | 61 +++++++++++++++++++++++++++++- lambda/src/core/render/vertex.rs | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index a1fbcaad..b057a595 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -1,6 +1,63 @@ use super::vertex::Vertex; +#[derive(Clone, Debug)] pub struct Mesh { - pub vertices: Vec, - pub indices: Vec, + vertices: Vec, + indices: Vec, +} + +#[derive(Clone, Debug)] +pub struct MeshBuilder { + size: usize, + vertices: Vec, + indices: Vec, +} + +impl MeshBuilder { + pub fn new() -> Self { + return Self { + size: 0, + vertices: vec![], + indices: vec![], + }; + } + + pub fn with_capacity(&mut self, size: usize) -> &mut Self { + self.size = size; + return self; + } + + pub fn with_vertex(&mut self, vertex: Vertex) -> &mut Self { + self.vertices.push(vertex); + return self; + } + + pub fn build(&self) -> Mesh { + return Mesh { + vertices: self.vertices.clone(), + indices: self.indices.clone(), + }; + } +} + +#[cfg(test)] +mod tests { + #[test] + fn mesh_building() { + let mut mesh = super::MeshBuilder::new(); + + assert_eq!(mesh.vertices.len(), 0); + assert_eq!(mesh.indices.len(), 0); + + let mut mesh = mesh + .with_capacity(10) + .with_vertex(crate::core::render::vertex::VertexBuilder::new().build()) + .build(); + + assert_eq!(mesh.vertices.len(), 1); + assert_eq!(mesh.indices.len(), 0); + assert_eq!(mesh.vertices[0].position, [0.0, 0.0, 0.0]); + assert_eq!(mesh.vertices[0].normal, [0.0, 0.0, 0.0]); + assert_eq!(mesh.vertices[0].color, [0.0, 0.0, 0.0]); + } } diff --git a/lambda/src/core/render/vertex.rs b/lambda/src/core/render/vertex.rs index 13a18e1f..3faa4c81 100644 --- a/lambda/src/core/render/vertex.rs +++ b/lambda/src/core/render/vertex.rs @@ -4,3 +4,68 @@ pub struct Vertex { pub normal: [f32; 3], pub color: [f32; 3], } + +/// Construction for +#[derive(Clone, Copy, Debug)] +pub struct VertexBuilder { + pub position: [f32; 3], + pub normal: [f32; 3], + pub color: [f32; 3], +} + +impl VertexBuilder { + pub fn new() -> Self { + return Self { + position: [0.0, 0.0, 0.0], + normal: [0.0, 0.0, 0.0], + color: [0.0, 0.0, 0.0], + }; + } + + /// Set the position of the vertex. + pub fn with_position(&mut self, position: [f32; 3]) -> &mut Self { + self.position = position; + return self; + } + + /// Set the normal of the vertex. + pub fn with_normal(&mut self, normal: [f32; 3]) -> &mut Self { + self.normal = normal; + return self; + } + + pub fn with_color(&mut self, color: [f32; 3]) -> &mut Self { + self.color = color; + return self; + } + + pub fn build(&self) -> Vertex { + return Vertex { + position: self.position, + normal: self.normal, + color: self.color, + }; + } +} + +#[cfg(test)] +mod test { + #[test] + fn vertex_building() { + let mut vertex = super::VertexBuilder::new(); + + assert_eq!(vertex.position, [0.0, 0.0, 0.0]); + assert_eq!(vertex.normal, [0.0, 0.0, 0.0]); + assert_eq!(vertex.color, [0.0, 0.0, 0.0]); + + let mut vertex = vertex + .with_position([1.0, 2.0, 3.0]) + .with_normal([4.0, 5.0, 6.0]) + .with_color([7.0, 8.0, 9.0]) + .build(); + + assert_eq!(vertex.position, [1.0, 2.0, 3.0]); + assert_eq!(vertex.normal, [4.0, 5.0, 6.0]); + assert_eq!(vertex.color, [7.0, 8.0, 9.0]); + } +} From 46566dd00e161939da75f84836060f7a4c13552f Mon Sep 17 00:00:00 2001 From: vmarcella Date: Mon, 16 Jan 2023 23:25:23 -0800 Subject: [PATCH 52/67] [update] push constants example. --- lambda/examples/push_constants.rs | 49 ++++++++++++++++++++----------- lambda/src/core/render/mesh.rs | 18 ++++++++---- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 11f711ef..c8407df8 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -3,28 +3,22 @@ use lambda::{ component::Component, render::{ command::RenderCommand, - pipeline::{ - RenderPipeline, - RenderPipelineBuilder, - }, - render_pass::{ - RenderPass, - RenderPassBuilder, - }, + mesh::MeshBuilder, + pipeline::RenderPipelineBuilder, + render_pass::RenderPassBuilder, shader::{ Shader, ShaderBuilder, }, + vertex::VertexBuilder, viewport, ResourceId, }, runtime::start_runtime, }, math::{ - matrix::{ - self, - Matrix, - }, + matrix, + matrix::Matrix, vector::Vector, }, runtimes::GenericRuntimeBuilder, @@ -96,6 +90,30 @@ impl Component for PushConstantsExample { self.render_pass = Some(render_context.attach_render_pass(render_pass)); self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); + + // Create triangle mesh. + let vertices = [ + VertexBuilder::new() + .with_position([0.0, 0.5, 0.0]) + .with_normal([0.0, 0.0, 0.0]) + .with_color([1.0, 0.0, 0.0]) + .build(), + VertexBuilder::new() + .with_position([-0.5, -0.5, 0.0]) + .with_normal([0.0, 0.0, 0.0]) + .with_color([0.0, 1.0, 0.0]) + .build(), + VertexBuilder::new() + .with_position([0.5, -0.5, 0.0]) + .with_normal([0.0, 0.0, 0.0]) + .with_color([0.0, 0.0, 1.0]) + .build(), + ]; + + let mut mesh_builder = MeshBuilder::new(); + vertices.iter().for_each(|vertex| { + mesh_builder.with_vertex(vertex.clone()); + }); } fn on_detach( @@ -201,14 +219,11 @@ impl Default for PushConstantsExample { } fn main() { - let runtime = GenericRuntimeBuilder::new("Multiple Triangles Demo") - .with_renderer_configured_as(move |render_context_builder| { - return render_context_builder.with_render_timeout(1_000_000_000); - }) + let runtime = GenericRuntimeBuilder::new("3D Push Constants Example") .with_window_configured_as(move |window_builder| { return window_builder .with_dimensions(800, 600) - .with_name("Triangles"); + .with_name("3D Push Constants Example"); }) .with_component(move |runtime, triangles: PushConstantsExample| { return (runtime, triangles); diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index b057a595..4f41247b 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -1,29 +1,35 @@ use super::vertex::Vertex; +// ---------------------------------- Mesh ------------------------------------ + +/// Collection of vertices and indices that define a 3D object. #[derive(Clone, Debug)] pub struct Mesh { vertices: Vec, - indices: Vec, + indices: Vec, } +// ------------------------------ MeshBuilder --------------------------------- + +/// Construction for a mesh. #[derive(Clone, Debug)] pub struct MeshBuilder { - size: usize, + capacity: usize, vertices: Vec, - indices: Vec, + indices: Vec, } impl MeshBuilder { pub fn new() -> Self { return Self { - size: 0, + capacity: 0, vertices: vec![], indices: vec![], }; } pub fn with_capacity(&mut self, size: usize) -> &mut Self { - self.size = size; + self.capacity = size; return self; } @@ -49,7 +55,7 @@ mod tests { assert_eq!(mesh.vertices.len(), 0); assert_eq!(mesh.indices.len(), 0); - let mut mesh = mesh + let mesh = mesh .with_capacity(10) .with_vertex(crate::core::render::vertex::VertexBuilder::new().build()) .build(); From af0b7dbd16653309f59e9a9959cd27a50b28c093 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 17 Jan 2023 00:21:32 -0800 Subject: [PATCH 53/67] [add] buffer implementation for gpu memory allocations, update matrix implementations. --- crates/lambda-platform/src/gfx/buffer.rs | 32 ++++++++++++++++++++++++ crates/lambda-platform/src/gfx/mod.rs | 1 + lambda/src/math/matrix.rs | 19 +++++++------- 3 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 crates/lambda-platform/src/gfx/buffer.rs diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs new file mode 100644 index 00000000..3950044a --- /dev/null +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -0,0 +1,32 @@ +pub struct Buffer; + +pub struct BufferBuilder { + buffer_length: usize, + usage: gfx_hal::buffer::Usage, +} + +impl BufferBuilder { + pub fn new() -> Self { + return Self { + buffer_length: 0, + usage: gfx_hal::buffer::Usage::empty(), + }; + } + + pub fn with_length(&mut self, length: usize) -> &mut Self { + self.buffer_length = length; + return self; + } + + pub fn with_usage(&mut self, usage: gfx_hal::buffer::Usage) -> &mut Self { + self.usage = usage; + return self; + } + + pub fn build( + self, + device: &mut RenderBackend::Device, + ) -> Buffer { + todo!(); + } +} diff --git a/crates/lambda-platform/src/gfx/mod.rs b/crates/lambda-platform/src/gfx/mod.rs index 067e8e06..d3edeb76 100644 --- a/crates/lambda-platform/src/gfx/mod.rs +++ b/crates/lambda-platform/src/gfx/mod.rs @@ -2,6 +2,7 @@ pub mod api; pub mod assembler; +pub mod buffer; pub mod command; pub mod fence; pub mod framebuffer; diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index 0408d0d1..c46c234b 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -101,7 +101,6 @@ pub fn rotate_matrix< "Axis vector must have 3 elements (x, y, z)" ); - // Convert the angle from turns to radians let angle_in_radians = turns_to_radians(angle_in_turns); let cosine_of_angle = angle_in_radians.cos(); let sin_of_angle = -angle_in_radians.sin(); @@ -284,7 +283,7 @@ where for (i, a) in self.as_ref().iter().enumerate() { for (j, b) in transposed.as_ref().iter().enumerate() { - result.as_mut()[i].as_mut()[j] += a.dot(&b); + result.update(i, j, a.dot(b)); } } return result; @@ -294,7 +293,7 @@ where let mut result = Self::default(); for (i, a) in self.as_ref().iter().enumerate() { for j in 0..a.as_ref().len() { - result.as_mut()[i].as_mut()[j] = self.as_ref()[j].as_ref()[i]; + result.update(i, j, self.at(j, i)); } } return result; @@ -320,10 +319,10 @@ where return match height { 1 => self.as_ref()[0].as_ref()[0], 2 => { - let a = self.as_ref()[0].as_ref()[0]; - let b = self.as_ref()[0].as_ref()[1]; - let c = self.as_ref()[1].as_ref()[0]; - let d = self.as_ref()[1].as_ref()[1]; + let a = self.at(0, 0); + let b = self.at(0, 1); + let c = self.at(1, 0); + let d = self.at(1, 1); a * d - b * c } _ => { @@ -334,12 +333,12 @@ where let mut row = Vec::new(); for k in 0..height { if k != i { - row.push(self.as_ref()[j].as_ref()[k]); + row.push(self.at(j, k)); } } submatrix.push(row); } - result += self.as_ref()[0].as_ref()[i] + result += self.at(0, i) * submatrix.determinant() * (-1.0 as f32).powi(i as i32); } @@ -350,7 +349,7 @@ where /// Return the size as a (rows, columns). fn size(&self) -> (usize, usize) { - return (self.as_ref().len(), self.as_ref()[0].as_ref().len()); + return (self.as_ref().len(), self.row(0).size()); } /// Return a reference to the row. From dce03c9db14ebfbd7a268716d7ab8f00983c7a32 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 17 Jan 2023 13:22:16 -0800 Subject: [PATCH 54/67] [add] buffer allocations. --- crates/lambda-platform/src/gfx/buffer.rs | 94 ++++++++++++++++++++++-- crates/lambda-platform/src/vertex.rs | 0 lambda/examples/push_constants.rs | 5 +- 3 files changed, 90 insertions(+), 9 deletions(-) delete mode 100644 crates/lambda-platform/src/vertex.rs diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index 3950044a..cd736308 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -1,8 +1,25 @@ -pub struct Buffer; +use gfx_hal::{ + memory::SparseFlags, + Backend, +}; + +use super::gpu::Gpu; + +// Reuse gfx-hal buffer usage & properties for now. +pub type Usage = gfx_hal::buffer::Usage; +pub type Properties = gfx_hal::memory::Properties; + +/// A buffer is a block of memory that can be used to store data that can be +/// accessed by the GPU. +pub struct Buffer { + buffer: RenderBackend::Buffer, + memory: RenderBackend::Memory, +} pub struct BufferBuilder { buffer_length: usize, - usage: gfx_hal::buffer::Usage, + usage: Usage, + properties: Properties, } impl BufferBuilder { @@ -10,6 +27,7 @@ impl BufferBuilder { return Self { buffer_length: 0, usage: gfx_hal::buffer::Usage::empty(), + properties: gfx_hal::memory::Properties::empty(), }; } @@ -18,15 +36,79 @@ impl BufferBuilder { return self; } - pub fn with_usage(&mut self, usage: gfx_hal::buffer::Usage) -> &mut Self { + pub fn with_usage(&mut self, usage: Usage) -> &mut Self { self.usage = usage; return self; } + pub fn with_properties(&mut self, properties: Properties) -> &mut Self { + return self; + } + pub fn build( self, - device: &mut RenderBackend::Device, - ) -> Buffer { - todo!(); + gpu: &mut Gpu, + ) -> Result, &'static str> { + use gfx_hal::{ + adapter::PhysicalDevice, + device::Device, + MemoryTypeId, + }; + let logical_device = super::internal::logical_device_for(gpu); + let physical_device = super::internal::physical_device_for(gpu); + + // Allocate buffer + let mut buffer_result = unsafe { + logical_device.create_buffer( + self.buffer_length as u64, + self.usage, + SparseFlags::empty(), + ) + }; + + if buffer_result.is_err() { + return Err("Failed to create buffer for allocating memory."); + } + + let mut buffer = buffer_result.unwrap(); + + let requirements = + unsafe { logical_device.get_buffer_requirements(&buffer) }; + let memory_types = physical_device.memory_properties().memory_types; + + // Find a memory type that supports the requirements of the buffer. + let memory_type = memory_types + .iter() + .enumerate() + .find(|(id, memory_type)| { + let type_supported = requirements.type_mask & (1 << id) != 0; + type_supported && memory_type.properties.contains(self.properties) + }) + .map(|(id, _)| MemoryTypeId(id)) + .unwrap(); + + let buffer_memory_allocation = + unsafe { logical_device.allocate_memory(memory_type, requirements.size) }; + + if buffer_memory_allocation.is_err() { + return Err("Failed to allocate memory for buffer."); + } + + let buffer_memory = buffer_memory_allocation.unwrap(); + + let buffer_binding = unsafe { + logical_device.bind_buffer_memory(&buffer_memory, 0, &mut buffer) + }; + + // Destroy the buffer if we failed to bind it to memory. + if buffer_binding.is_err() { + unsafe { logical_device.destroy_buffer(buffer) }; + return Err("Failed to bind buffer memory."); + } + + return Ok(Buffer { + buffer, + memory: buffer_memory, + }); } } diff --git a/crates/lambda-platform/src/vertex.rs b/crates/lambda-platform/src/vertex.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index c8407df8..4d2cff1c 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -128,14 +128,13 @@ impl Component for PushConstantsExample { } /// Update the frame number every frame. - fn on_update(&mut self, last_frame: &std::time::Duration) { - self.frame_number += 1; - } + fn on_update(&mut self, last_frame: &std::time::Duration) {} fn on_render( &mut self, render_context: &mut lambda::core::render::RenderContext, ) -> Vec { + self.frame_number += 1; let mut camera = [0.0, 0.0, -2.0]; let view: [[f32; 4]; 4] = matrix::translation_matrix(camera); From c90ba3a2a0b511335b6ff6df817ea1d0f74f900b Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 17 Jan 2023 15:02:12 -0800 Subject: [PATCH 55/67] [add] documentation. --- crates/lambda-platform/src/gfx/buffer.rs | 9 +++++++-- lambda/src/math/matrix.rs | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index cd736308..30fd8574 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -45,6 +45,8 @@ impl BufferBuilder { return self; } + /// Builds & binds a buffer of memory to the GPU. If the buffer cannot be + /// bound to the GPU, the buffer memory is freed before the error is returned. pub fn build( self, gpu: &mut Gpu, @@ -57,8 +59,9 @@ impl BufferBuilder { let logical_device = super::internal::logical_device_for(gpu); let physical_device = super::internal::physical_device_for(gpu); - // Allocate buffer - let mut buffer_result = unsafe { + // TODO(vmarcella): Add the ability for the user to specify the memory + // properties (I.E. SparseFlags::SPARSE_MEMORY). + let buffer_result = unsafe { logical_device.create_buffer( self.buffer_length as u64, self.usage, @@ -87,6 +90,7 @@ impl BufferBuilder { .map(|(id, _)| MemoryTypeId(id)) .unwrap(); + // Allocates the memory on the GPU for the buffer. let buffer_memory_allocation = unsafe { logical_device.allocate_memory(memory_type, requirements.size) }; @@ -96,6 +100,7 @@ impl BufferBuilder { let buffer_memory = buffer_memory_allocation.unwrap(); + // Bind the buffer to the GPU memory let buffer_binding = unsafe { logical_device.bind_buffer_memory(&buffer_memory, 0, &mut buffer) }; diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index c46c234b..d17fffd9 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -289,6 +289,7 @@ where return result; } + /// Transposes the matrix, swapping the rows and columns. fn transpose(&self) -> Self { let mut result = Self::default(); for (i, a) in self.as_ref().iter().enumerate() { From 80bab085da9c47e13e4d144cbc7d36c604813925 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 17 Jan 2023 15:59:32 -0800 Subject: [PATCH 56/67] [update] buffer to fill data upon being built, implement buffer allocation inside of mesh. --- crates/lambda-platform/src/gfx/buffer.rs | 54 +++++++++++++++++-- lambda/src/core/render/mesh.rs | 68 +++++++++++++++++------- lambda/src/core/render/vertex.rs | 1 + 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index 30fd8574..acc27e8d 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -1,5 +1,8 @@ use gfx_hal::{ - memory::SparseFlags, + memory::{ + Segment, + SparseFlags, + }, Backend, }; @@ -11,11 +14,14 @@ pub type Properties = gfx_hal::memory::Properties; /// A buffer is a block of memory that can be used to store data that can be /// accessed by the GPU. +#[derive(Debug, Clone, Copy)] pub struct Buffer { buffer: RenderBackend::Buffer, memory: RenderBackend::Memory, } +impl Buffer {} + pub struct BufferBuilder { buffer_length: usize, usage: Usage, @@ -47,9 +53,12 @@ impl BufferBuilder { /// Builds & binds a buffer of memory to the GPU. If the buffer cannot be /// bound to the GPU, the buffer memory is freed before the error is returned. - pub fn build( - self, + /// Data must represent the data that will be stored in the buffer, meaning + /// it must repr C and be the same size as the buffer length. + pub fn build( + &self, gpu: &mut Gpu, + data: Vec, ) -> Result, &'static str> { use gfx_hal::{ adapter::PhysicalDevice, @@ -98,7 +107,7 @@ impl BufferBuilder { return Err("Failed to allocate memory for buffer."); } - let buffer_memory = buffer_memory_allocation.unwrap(); + let mut buffer_memory = buffer_memory_allocation.unwrap(); // Bind the buffer to the GPU memory let buffer_binding = unsafe { @@ -111,6 +120,43 @@ impl BufferBuilder { return Err("Failed to bind buffer memory."); } + // Get address of the buffer memory on the GPU so that we can write to it. + let get_mapping_to_memory = + unsafe { logical_device.map_memory(&mut buffer_memory, Segment::ALL) }; + + if get_mapping_to_memory.is_err() { + unsafe { logical_device.destroy_buffer(buffer) }; + return Err("Failed to map memory."); + } + let mapped_memory = get_mapping_to_memory.unwrap(); + + // Copy the data to the GPU memory. + unsafe { + std::ptr::copy_nonoverlapping( + data.as_ptr() as *const u8, + mapped_memory, + self.buffer_length, + ); + }; + + // Flush the data to ensure it is written to the GPU memory. + let memory_flush = unsafe { + logical_device + .flush_mapped_memory_ranges(std::iter::once(( + &buffer_memory, + Segment::ALL, + ))) + .map_err(|_| "Failed to flush memory.") + }; + + if memory_flush.is_err() { + unsafe { logical_device.destroy_buffer(buffer) }; + return Err("No memory available on the GPU."); + } + + // Unmap the memory now that it's no longer needed by the CPU. + unsafe { logical_device.unmap_memory(&mut buffer_memory) }; + return Ok(Buffer { buffer, memory: buffer_memory, diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index 4f41247b..ef510c6a 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -1,12 +1,24 @@ -use super::vertex::Vertex; +use lambda_platform::gfx::buffer::{ + Buffer, + BufferBuilder, + Properties, + Usage, +}; + +use super::{ + vertex::Vertex, + RenderContext, +}; +use crate::core::render::internal::mut_gpu_from_context; // ---------------------------------- Mesh ------------------------------------ /// Collection of vertices and indices that define a 3D object. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Mesh { vertices: Vec, indices: Vec, + buffer: Buffer, } // ------------------------------ MeshBuilder --------------------------------- @@ -38,11 +50,38 @@ impl MeshBuilder { return self; } - pub fn build(&self) -> Mesh { - return Mesh { - vertices: self.vertices.clone(), - indices: self.indices.clone(), - }; + /// Builds a mesh from the vertices and indices that have been added to the + /// builder and allocates the memory for the mesh on the GPU. + pub fn build( + &self, + render_context: &mut RenderContext, + ) -> Result { + let gpu_memory_required = + self.vertices.len() * std::mem::size_of::(); + println!( + "Allocating {} bytes of GPU memory for mesh.", + gpu_memory_required + ); + + // Allocate memory for the mesh on the GPU. + let buffer_allocation = BufferBuilder::new() + .with_length(gpu_memory_required) + .with_usage(Usage::VERTEX) + .with_properties(Properties::CPU_VISIBLE | Properties::COHERENT) + .build(mut_gpu_from_context(render_context), self.vertices.clone()); + + match buffer_allocation { + Ok(buffer) => { + return Ok(Mesh { + vertices: self.vertices.clone(), + indices: self.indices.clone(), + buffer, + }); + } + Err(error) => { + return Err(error); + } + } } } @@ -54,16 +93,9 @@ mod tests { assert_eq!(mesh.vertices.len(), 0); assert_eq!(mesh.indices.len(), 0); - - let mesh = mesh - .with_capacity(10) - .with_vertex(crate::core::render::vertex::VertexBuilder::new().build()) - .build(); - - assert_eq!(mesh.vertices.len(), 1); - assert_eq!(mesh.indices.len(), 0); - assert_eq!(mesh.vertices[0].position, [0.0, 0.0, 0.0]); - assert_eq!(mesh.vertices[0].normal, [0.0, 0.0, 0.0]); - assert_eq!(mesh.vertices[0].color, [0.0, 0.0, 0.0]); } + + // TODO(vmarcella): Add more tests for mesh building once the render context + // is mockable. As of right now, testing would require the creation of a real + // render context to perform the GPU memory allocation & binding for the mesh. } diff --git a/lambda/src/core/render/vertex.rs b/lambda/src/core/render/vertex.rs index 3faa4c81..8a064ca0 100644 --- a/lambda/src/core/render/vertex.rs +++ b/lambda/src/core/render/vertex.rs @@ -1,3 +1,4 @@ +#[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Vertex { pub position: [f32; 3], From f9bdb39363d0b75f2dcc55f4c22b8a7e43fea6c0 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Tue, 17 Jan 2023 20:47:51 -0800 Subject: [PATCH 57/67] [add] high level buffer implementation & introduce buffer types for use in the render pipeline. --- crates/lambda-platform/src/gfx/buffer.rs | 32 ++++++--- crates/lambda-platform/src/gfx/pipeline.rs | 5 ++ lambda/src/core/render/buffer.rs | 82 ++++++++++++++++++++++ lambda/src/core/render/mesh.rs | 18 +++-- lambda/src/core/render/mod.rs | 1 + lambda/src/core/render/pipeline.rs | 17 +++-- 6 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 lambda/src/core/render/buffer.rs diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index acc27e8d..a2fd47a2 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -1,9 +1,6 @@ -use gfx_hal::{ - memory::{ - Segment, - SparseFlags, - }, - Backend, +use gfx_hal::memory::{ + Segment, + SparseFlags, }; use super::gpu::Gpu; @@ -12,12 +9,21 @@ use super::gpu::Gpu; pub type Usage = gfx_hal::buffer::Usage; pub type Properties = gfx_hal::memory::Properties; +/// A buffer is a block of memory that can be used to store data that is accessed +#[derive(Debug, Clone, Copy)] +pub enum BufferType { + Vertex, + Index, +} + /// A buffer is a block of memory that can be used to store data that can be /// accessed by the GPU. #[derive(Debug, Clone, Copy)] pub struct Buffer { buffer: RenderBackend::Buffer, memory: RenderBackend::Memory, + stride: usize, + buffer_type: BufferType, } impl Buffer {} @@ -26,14 +32,16 @@ pub struct BufferBuilder { buffer_length: usize, usage: Usage, properties: Properties, + buffer_type: BufferType, } impl BufferBuilder { pub fn new() -> Self { return Self { buffer_length: 0, - usage: gfx_hal::buffer::Usage::empty(), - properties: gfx_hal::memory::Properties::empty(), + usage: Usage::empty(), + properties: Properties::empty(), + buffer_type: BufferType::Vertex, }; } @@ -48,6 +56,12 @@ impl BufferBuilder { } pub fn with_properties(&mut self, properties: Properties) -> &mut Self { + self.properties = properties; + return self; + } + + pub fn with_buffer_type(&mut self, buffer_type: BufferType) -> &mut Self { + self.buffer_type = buffer_type; return self; } @@ -160,6 +174,8 @@ impl BufferBuilder { return Ok(Buffer { buffer, memory: buffer_memory, + stride: std::mem::size_of::(), + buffer_type: self.buffer_type, }); } } diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index b01766e1..c1792870 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -36,6 +36,7 @@ use std::ops::Range; use gfx_hal::device::Device; use super::{ + buffer::Buffer, gpu::Gpu, shader::ShaderModule, }; @@ -58,6 +59,10 @@ impl RenderPipelineBuilder { }; } + pub fn with_buffer(&mut self, buffer: &Buffer) -> &mut Self { + todo!() + } + /// Adds a push constant to the render pipeline at the set PipelineStage(s) pub fn with_push_constant( mut self, diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs new file mode 100644 index 00000000..6447f3d3 --- /dev/null +++ b/lambda/src/core/render/buffer.rs @@ -0,0 +1,82 @@ +mod internal { + pub use lambda_platform::gfx::buffer::{ + Buffer, + BufferBuilder, + }; +} + +// publicly use Properties and Usage from buffer.rs +pub use lambda_platform::gfx::buffer::{ + BufferType, + Properties, + Usage, +}; + +use super::{ + internal::mut_gpu_from_context, + RenderContext, +}; + +#[derive(Debug)] +pub struct Buffer { + buffer: internal::Buffer, + buffer_type: BufferType, +} + +pub struct BufferBuilder { + buffer_builder: internal::BufferBuilder, + buffer_type: BufferType, +} + +impl BufferBuilder { + pub fn new() -> Self { + return Self { + buffer_builder: internal::BufferBuilder::new(), + buffer_type: BufferType::Vertex, + }; + } + + pub fn with_length(&mut self, size: usize) -> &mut Self { + self.buffer_builder.with_length(size); + return self; + } + + pub fn with_buffer_type(&mut self, buffer_type: BufferType) -> &mut Self { + self.buffer_type = buffer_type; + self.buffer_builder.with_buffer_type(buffer_type); + return self; + } + + pub fn with_usage(&mut self, usage: Usage) -> &mut Self { + self.buffer_builder.with_usage(usage); + return self; + } + + pub fn with_properties(&mut self, properties: Properties) -> &mut Self { + self.buffer_builder.with_properties(properties); + return self; + } + + /// Build a buffer from the render context. + pub fn build( + &self, + render_context: &mut RenderContext, + data: Vec, + ) -> Result { + let buffer_allocation = self + .buffer_builder + .build(&mut mut_gpu_from_context(render_context), data); + + match buffer_allocation { + Ok(buffer) => { + return Ok(Buffer { + buffer, + buffer_type: self.buffer_type, + }); + } + Err(error) => { + return Err(error); + } + } + } +} diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index ef510c6a..55331c14 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -1,15 +1,13 @@ -use lambda_platform::gfx::buffer::{ - Buffer, - BufferBuilder, - Properties, - Usage, -}; - use super::{ + buffer::{ + Buffer, + BufferBuilder, + Properties, + Usage, + }, vertex::Vertex, RenderContext, }; -use crate::core::render::internal::mut_gpu_from_context; // ---------------------------------- Mesh ------------------------------------ @@ -18,7 +16,7 @@ use crate::core::render::internal::mut_gpu_from_context; pub struct Mesh { vertices: Vec, indices: Vec, - buffer: Buffer, + buffer: Buffer, } // ------------------------------ MeshBuilder --------------------------------- @@ -68,7 +66,7 @@ impl MeshBuilder { .with_length(gpu_memory_required) .with_usage(Usage::VERTEX) .with_properties(Properties::CPU_VISIBLE | Properties::COHERENT) - .build(mut_gpu_from_context(render_context), self.vertices.clone()); + .build(render_context, self.vertices.clone()); match buffer_allocation { Ok(buffer) => { diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index 86e12efb..b0b6ee45 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -2,6 +2,7 @@ //! windowing. // Module Exports +pub mod buffer; pub mod command; pub mod mesh; pub mod pipeline; diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index 2dc165c5..1dd27300 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -6,6 +6,7 @@ use lambda_platform::gfx::shader::{ }; use super::{ + buffer::Buffer, internal::{ gpu_from_context, mut_gpu_from_context, @@ -45,27 +46,35 @@ use lambda_platform::gfx::pipeline::PushConstantUpload; pub struct RenderPipelineBuilder { push_constants: Vec, + buffers: Vec, } impl RenderPipelineBuilder { pub fn new() -> Self { return Self { push_constants: Vec::new(), + buffers: Vec::new(), }; } + /// Adds a buffer to the render pipeline. + pub fn with_buffer(&mut self, buffer: Buffer) -> &mut Self { + self.buffers.push(buffer); + return self; + } + pub fn with_push_constant( - mut self, + &mut self, stage: PipelineStage, bytes: u32, - ) -> Self { + ) -> &mut Self { self.push_constants.push((stage, 0..bytes)); return self; } /// Builds a render pipeline based on your builder configuration. pub fn build( - self, + &self, render_context: &mut RenderContext, render_pass: &super::render_pass::RenderPass, vertex_shader: &Shader, @@ -88,7 +97,7 @@ impl RenderPipelineBuilder { let render_pipeline = lambda_platform::gfx::pipeline::RenderPipelineBuilder::new() - .with_push_constants(self.push_constants) + .with_push_constants(self.push_constants.clone()) .build( gpu_from_context(render_context), &platform_render_pass_from_render_pass(render_pass), From 41871eb425e1173eb5cb578e093bddaeb293c837 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Wed, 18 Jan 2023 16:54:10 -0800 Subject: [PATCH 58/67] [update] the pipeline to support vertex buffers & attributes. --- crates/lambda-platform/src/gfx/assembler.rs | 66 ++++++++++++++++++--- crates/lambda-platform/src/gfx/buffer.rs | 10 +++- crates/lambda-platform/src/gfx/pipeline.rs | 8 ++- lambda/src/core/render/buffer.rs | 11 +++- lambda/src/core/render/mesh.rs | 5 -- lambda/src/core/render/mod.rs | 4 +- 6 files changed, 85 insertions(+), 19 deletions(-) diff --git a/crates/lambda-platform/src/gfx/assembler.rs b/crates/lambda-platform/src/gfx/assembler.rs index 3b068344..b6ac0fd6 100644 --- a/crates/lambda-platform/src/gfx/assembler.rs +++ b/crates/lambda-platform/src/gfx/assembler.rs @@ -1,25 +1,77 @@ //! Primitive assembly for the graphics pipeline. -use gfx_hal::pso; +use gfx_hal::pso::{ + self, + AttributeDesc, + Element, + VertexBufferDesc, +}; + +use super::{ + buffer::Buffer, + surface::ColorFormat, +}; + +/// Attributes for a vertex. +pub struct VertexAttribute { + pub location: u32, + pub offset: u32, + pub element: Element, +} /// PrimitiveAssemblerBuilder for preparing PrimitiveAssemblers to use in the /// lambda-platform Rendering pipeline. -pub struct PrimitiveAssemblerBuilder {} +pub struct PrimitiveAssemblerBuilder { + buffer_descriptions: Vec, + attribute_descriptions: Vec, +} impl PrimitiveAssemblerBuilder { pub fn new() -> Self { - return Self {}; + return Self { + buffer_descriptions: Vec::new(), + attribute_descriptions: Vec::new(), + }; } /// Build a primitive assembler given the lambda-platform vertex shader - /// module. + /// module. Buffers & attributes do not have to be tied to pub fn build<'shader, RenderBackend: gfx_hal::Backend>( - self, + &'shader mut self, vertex_shader: &'shader super::shader::ShaderModule, + buffers: Option<&Vec>>, + attributes: Option<&Vec>, ) -> PrimitiveAssembler<'shader, RenderBackend> { + let binding = self.buffer_descriptions.len() as u32; + + match (buffers, attributes) { + (Some(buffers), Some(attributes)) => { + self.buffer_descriptions = buffers + .iter() + .map(|buffer| VertexBufferDesc { + binding, + stride: buffer.stride() as u32, + rate: pso::VertexInputRate::Vertex, + }) + .collect(); + + self.attribute_descriptions = attributes + .iter() + .map(|attribute| { + return AttributeDesc { + location: attribute.location, + binding, + element: attribute.element, + }; + }) + .collect(); + } + _ => {} + } + let primitive_assembler = pso::PrimitiveAssemblerDesc::Vertex { - buffers: &[], - attributes: &[], + buffers: self.buffer_descriptions.as_slice(), + attributes: self.attribute_descriptions.as_slice(), input_assembler: pso::InputAssemblerDesc::new( pso::Primitive::TriangleList, ), diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index a2fd47a2..4cdb668a 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -9,11 +9,13 @@ use super::gpu::Gpu; pub type Usage = gfx_hal::buffer::Usage; pub type Properties = gfx_hal::memory::Properties; -/// A buffer is a block of memory that can be used to store data that is accessed +/// The type of buffers that can be allocated on the GPU. #[derive(Debug, Clone, Copy)] pub enum BufferType { Vertex, Index, + Uniform, + Storage, } /// A buffer is a block of memory that can be used to store data that can be @@ -26,7 +28,11 @@ pub struct Buffer { buffer_type: BufferType, } -impl Buffer {} +impl Buffer { + pub fn stride(&self) -> usize { + return self.stride; + } +} pub struct BufferBuilder { buffer_length: usize, diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index c1792870..1eb755b8 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -106,12 +106,14 @@ impl RenderPipelineBuilder { ) }; - let primitive_assembler = - super::assembler::PrimitiveAssemblerBuilder::new().build(vertex_shader); + // TODO(vmarcella): The primitive assembler should be configurable through + // the RenderPipelineBuilder so that buffers & attributes can be bound. + let mut builder = super::assembler::PrimitiveAssemblerBuilder::new(); + let primitive_assembler = builder.build(vertex_shader, None, None); let fragment_entry = match fragment_shader { Some(shader) => Some(internal::EntryPoint:: { - entry: "main", + entry: shader.entry(), module: super::internal::module_for(shader), specialization: gfx_hal::pso::Specialization::default(), }), diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index 6447f3d3..19ac69e1 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -1,4 +1,6 @@ mod internal { + // Placed these in an internal module to avoid a name collision with the + // high level Buffer & BufferBuilder types in the parent module. pub use lambda_platform::gfx::buffer::{ Buffer, BufferBuilder, @@ -23,6 +25,9 @@ pub struct Buffer { buffer_type: BufferType, } +/// A buffer is a block of memory that can be used to store data that can be +/// accessed by the GPU. The buffer is created with a length, usage, and +/// properties that determine how the buffer can be used. pub struct BufferBuilder { buffer_builder: internal::BufferBuilder, buffer_type: BufferType, @@ -36,28 +41,32 @@ impl BufferBuilder { }; } + /// Sets the length of the buffer (In bytes). pub fn with_length(&mut self, size: usize) -> &mut Self { self.buffer_builder.with_length(size); return self; } + /// Sets the type of buffer to create. pub fn with_buffer_type(&mut self, buffer_type: BufferType) -> &mut Self { self.buffer_type = buffer_type; self.buffer_builder.with_buffer_type(buffer_type); return self; } + /// Sets the usage of the buffer. pub fn with_usage(&mut self, usage: Usage) -> &mut Self { self.buffer_builder.with_usage(usage); return self; } + /// Sets the properties of the buffer. pub fn with_properties(&mut self, properties: Properties) -> &mut Self { self.buffer_builder.with_properties(properties); return self; } - /// Build a buffer from the render context. + /// Build a buffer utilizing the current render context pub fn build( &self, render_context: &mut RenderContext, diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index 55331c14..99bb9362 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -15,7 +15,6 @@ use super::{ #[derive(Debug)] pub struct Mesh { vertices: Vec, - indices: Vec, buffer: Buffer, } @@ -26,7 +25,6 @@ pub struct Mesh { pub struct MeshBuilder { capacity: usize, vertices: Vec, - indices: Vec, } impl MeshBuilder { @@ -34,7 +32,6 @@ impl MeshBuilder { return Self { capacity: 0, vertices: vec![], - indices: vec![], }; } @@ -72,7 +69,6 @@ impl MeshBuilder { Ok(buffer) => { return Ok(Mesh { vertices: self.vertices.clone(), - indices: self.indices.clone(), buffer, }); } @@ -90,7 +86,6 @@ mod tests { let mut mesh = super::MeshBuilder::new(); assert_eq!(mesh.vertices.len(), 0); - assert_eq!(mesh.indices.len(), 0); } // TODO(vmarcella): Add more tests for mesh building once the render context diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index b0b6ee45..bede764f 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -212,7 +212,9 @@ impl RenderContext { return self.frame_buffer.as_ref().unwrap().clone(); } - /// Allocates a command buffer and records commands to the GPU. + /// Allocates a command buffer and records commands to the GPU. This is the + /// primary entry point for submitting commands to the GPU and where rendering + /// will occur. pub fn render(&mut self, commands: Vec) { let (width, height) = self .surface From d1ef60b27d480198ca98d7ed1099047bb4cc0bd5 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Wed, 18 Jan 2023 19:34:18 -0800 Subject: [PATCH 59/67] [update] how the render pipelines & primitive assembler process buffers. --- crates/lambda-platform/src/gfx/assembler.rs | 5 ++- crates/lambda-platform/src/gfx/gpu.rs | 4 ++ crates/lambda-platform/src/gfx/pipeline.rs | 35 +++++++++++---- crates/lambda-platform/src/gfx/surface.rs | 2 +- lambda/src/core/render/buffer.rs | 9 ++++ lambda/src/core/render/mesh.rs | 6 +++ lambda/src/core/render/pipeline.rs | 47 +++++++++++++++------ lambda/src/core/render/render_pass.rs | 20 ++++----- 8 files changed, 91 insertions(+), 37 deletions(-) diff --git a/crates/lambda-platform/src/gfx/assembler.rs b/crates/lambda-platform/src/gfx/assembler.rs index b6ac0fd6..072971c7 100644 --- a/crates/lambda-platform/src/gfx/assembler.rs +++ b/crates/lambda-platform/src/gfx/assembler.rs @@ -13,6 +13,7 @@ use super::{ }; /// Attributes for a vertex. +#[derive(Debug, Clone)] pub struct VertexAttribute { pub location: u32, pub offset: u32, @@ -39,8 +40,8 @@ impl PrimitiveAssemblerBuilder { pub fn build<'shader, RenderBackend: gfx_hal::Backend>( &'shader mut self, vertex_shader: &'shader super::shader::ShaderModule, - buffers: Option<&Vec>>, - attributes: Option<&Vec>, + buffers: Option<&Vec<&Buffer>>, + attributes: Option<&[VertexAttribute]>, ) -> PrimitiveAssembler<'shader, RenderBackend> { let binding = self.buffer_descriptions.len() as u32; diff --git a/crates/lambda-platform/src/gfx/gpu.rs b/crates/lambda-platform/src/gfx/gpu.rs index 997f1bc6..24e83356 100644 --- a/crates/lambda-platform/src/gfx/gpu.rs +++ b/crates/lambda-platform/src/gfx/gpu.rs @@ -182,6 +182,10 @@ impl Gpu { return Ok(()); } + + pub(super) fn logical_device(&self) -> &RenderBackend::Device { + return &self.gpu.device; + } } #[cfg(test)] diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index 1eb755b8..b21d7eff 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -36,6 +36,7 @@ use std::ops::Range; use gfx_hal::device::Device; use super::{ + assembler::VertexAttribute, buffer::Buffer, gpu::Gpu, shader::ShaderModule, @@ -45,6 +46,8 @@ use super::{ pub struct RenderPipelineBuilder { pipeline_layout: Option, push_constants: Vec, + buffers: Vec>, + attributes: Vec, } pub type PipelineStage = gfx_hal::pso::ShaderStageFlags; @@ -56,19 +59,27 @@ impl RenderPipelineBuilder { return Self { pipeline_layout: None, push_constants: Vec::new(), + buffers: Vec::new(), + attributes: Vec::new(), }; } - pub fn with_buffer(&mut self, buffer: &Buffer) -> &mut Self { - todo!() + pub fn with_buffer( + &mut self, + buffer: Buffer, + attributes: Vec, + ) -> &mut Self { + self.buffers.push(buffer); + self.attributes.extend(attributes); + return self; } /// Adds a push constant to the render pipeline at the set PipelineStage(s) pub fn with_push_constant( - mut self, + &mut self, stage: PipelineStage, bytes: u32, - ) -> Self { + ) -> &mut Self { self.push_constants.push((stage, 0..bytes)); return self; } @@ -91,6 +102,8 @@ impl RenderPipelineBuilder { render_pass: &super::render_pass::RenderPass, vertex_shader: &ShaderModule, fragment_shader: Option<&ShaderModule>, + buffers: &Vec<&Buffer>, + attributes: &[VertexAttribute], ) -> RenderPipeline { // TODO(vmarcella): The pipeline layout should be configurable through the // RenderPipelineBuilder. @@ -109,7 +122,8 @@ impl RenderPipelineBuilder { // TODO(vmarcella): The primitive assembler should be configurable through // the RenderPipelineBuilder so that buffers & attributes can be bound. let mut builder = super::assembler::PrimitiveAssemblerBuilder::new(); - let primitive_assembler = builder.build(vertex_shader, None, None); + let primitive_assembler = + builder.build(vertex_shader, Some(buffers), Some(attributes)); let fragment_entry = match fragment_shader { Some(shader) => Some(internal::EntryPoint:: { @@ -143,7 +157,8 @@ impl RenderPipelineBuilder { }); let pipeline = unsafe { - super::internal::logical_device_for(gpu) + gpu + .logical_device() .create_graphics_pipeline(&pipeline_desc, None) .expect("Failed to create graphics pipeline") }; @@ -151,6 +166,7 @@ impl RenderPipelineBuilder { return RenderPipeline { pipeline_layout, pipeline, + buffers: self.buffers, }; } } @@ -160,16 +176,19 @@ impl RenderPipelineBuilder { pub struct RenderPipeline { pipeline_layout: RenderBackend::PipelineLayout, pipeline: RenderBackend::GraphicsPipeline, + buffers: Vec>, } impl RenderPipeline { /// Destroys the pipeline layout and graphical pipeline pub fn destroy(self, gpu: &super::gpu::Gpu) { unsafe { - super::gpu::internal::logical_device_for(gpu) + gpu + .logical_device() .destroy_pipeline_layout(self.pipeline_layout); - super::gpu::internal::logical_device_for(gpu) + gpu + .logical_device() .destroy_graphics_pipeline(self.pipeline); } } diff --git a/crates/lambda-platform/src/gfx/surface.rs b/crates/lambda-platform/src/gfx/surface.rs index 75188971..25887bb2 100644 --- a/crates/lambda-platform/src/gfx/surface.rs +++ b/crates/lambda-platform/src/gfx/surface.rs @@ -39,7 +39,7 @@ impl SurfaceBuilder { let gfx_hal_surface = super::internal::create_surface(instance, window); let name = match self.name { Some(name) => name, - None => "LambdaSurface".to_string(), + None => "RenderSurface".to_string(), }; return Surface { diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index 19ac69e1..f8c5da66 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -25,6 +25,15 @@ pub struct Buffer { buffer_type: BufferType, } +impl Buffer { + /// Retrieve a reference to the internal buffer. + pub(super) fn internal_buffer( + &self, + ) -> &internal::Buffer { + return &self.buffer; + } +} + /// A buffer is a block of memory that can be used to store data that can be /// accessed by the GPU. The buffer is created with a length, usage, and /// properties that determine how the buffer can be used. diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index 99bb9362..22485011 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -18,6 +18,12 @@ pub struct Mesh { buffer: Buffer, } +impl Mesh { + pub(super) fn buffer(&self) -> &Buffer { + return &self.buffer; + } +} + // ------------------------------ MeshBuilder --------------------------------- /// Construction for a mesh. diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index 1dd27300..89ed45a2 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -1,8 +1,12 @@ use std::rc::Rc; -use lambda_platform::gfx::shader::{ - ShaderModuleBuilder, - ShaderModuleType, +use lambda_platform::gfx::{ + assembler::VertexAttribute, + buffer::Buffer as InternalBuffer, + shader::{ + ShaderModuleBuilder, + ShaderModuleType, + }, }; use super::{ @@ -12,7 +16,6 @@ use super::{ mut_gpu_from_context, RenderBackend, }, - render_pass::internal::platform_render_pass_from_render_pass, shader::Shader, RenderContext, }; @@ -47,6 +50,7 @@ use lambda_platform::gfx::pipeline::PushConstantUpload; pub struct RenderPipelineBuilder { push_constants: Vec, buffers: Vec, + attributes: Vec, } impl RenderPipelineBuilder { @@ -54,12 +58,18 @@ impl RenderPipelineBuilder { return Self { push_constants: Vec::new(), buffers: Vec::new(), + attributes: Vec::new(), }; } /// Adds a buffer to the render pipeline. - pub fn with_buffer(&mut self, buffer: Buffer) -> &mut Self { + pub fn with_buffer( + &mut self, + buffer: Buffer, + attributes: Vec, + ) -> &mut Self { self.buffers.push(buffer); + self.attributes.extend(attributes); return self; } @@ -95,15 +105,24 @@ impl RenderPipelineBuilder { None => None, }; - let render_pipeline = - lambda_platform::gfx::pipeline::RenderPipelineBuilder::new() - .with_push_constants(self.push_constants.clone()) - .build( - gpu_from_context(render_context), - &platform_render_pass_from_render_pass(render_pass), - &vertex_shader_module, - fragment_shader_module.as_ref(), - ); + let builder = lambda_platform::gfx::pipeline::RenderPipelineBuilder::new(); + + let internal_buffers = self + .buffers + .iter() + .map(|b| b.internal_buffer()) + .collect::>>(); + + let render_pipeline = builder + .with_push_constants(self.push_constants.clone()) + .build( + gpu_from_context(render_context), + render_pass.internal_render_pass(), + &vertex_shader_module, + fragment_shader_module.as_ref(), + &internal_buffers, + self.attributes.as_slice(), + ); // Clean up shader modules. vertex_shader_module.destroy(mut_gpu_from_context(render_context)); diff --git a/lambda/src/core/render/render_pass.rs b/lambda/src/core/render/render_pass.rs index fa31cc61..2f7558a6 100644 --- a/lambda/src/core/render/render_pass.rs +++ b/lambda/src/core/render/render_pass.rs @@ -20,8 +20,15 @@ impl RenderPass { .destroy(gpu_from_context(render_context)); } + /// Retrieve a reference to the lower level render pass. + pub(super) fn internal_render_pass( + &self, + ) -> &Rc> { + return &self.render_pass; + } + /// Converts - pub fn into_gfx_render_pass( + pub(super) fn into_gfx_render_pass( &self, ) -> Rc> { return self.render_pass.clone(); @@ -45,14 +52,3 @@ impl RenderPassBuilder { }; } } - -pub(crate) mod internal { - use crate::core::render::internal::RenderBackend; - - /// Converts a render pass into a platform render pass. - pub fn platform_render_pass_from_render_pass( - render_pass: &super::RenderPass, - ) -> &lambda_platform::gfx::render_pass::RenderPass { - return &render_pass.render_pass; - } -} From 9e5fdf920b6befb8d3e166a138ee85055074e37e Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 15:29:26 -0800 Subject: [PATCH 60/67] [update] Buffer & Mesh API, refactor internal api calls, implement vertex attributes & mesh into demo. --- crates/lambda-platform/src/gfx/assembler.rs | 4 +- crates/lambda-platform/src/gfx/buffer.rs | 20 ++++-- crates/lambda-platform/src/gfx/gpu.rs | 2 +- crates/lambda-platform/src/gfx/pipeline.rs | 6 +- crates/lambda-platform/src/gfx/surface.rs | 2 +- lambda/examples/push_constants.rs | 53 +++++++++++++--- lambda/src/core/render/buffer.rs | 42 ++++++++++++- lambda/src/core/render/mesh.rs | 69 ++++++++------------- lambda/src/core/render/mod.rs | 21 ++++--- lambda/src/core/render/pipeline.rs | 35 ++++++----- lambda/src/core/render/vertex.rs | 7 ++- 11 files changed, 173 insertions(+), 88 deletions(-) diff --git a/crates/lambda-platform/src/gfx/assembler.rs b/crates/lambda-platform/src/gfx/assembler.rs index 072971c7..ac7baabd 100644 --- a/crates/lambda-platform/src/gfx/assembler.rs +++ b/crates/lambda-platform/src/gfx/assembler.rs @@ -1,9 +1,9 @@ //! Primitive assembly for the graphics pipeline. +pub use gfx_hal::pso::Element as VertexElement; use gfx_hal::pso::{ self, AttributeDesc, - Element, VertexBufferDesc, }; @@ -17,7 +17,7 @@ use super::{ pub struct VertexAttribute { pub location: u32, pub offset: u32, - pub element: Element, + pub element: VertexElement, } /// PrimitiveAssemblerBuilder for preparing PrimitiveAssemblers to use in the diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index 4cdb668a..190beaad 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -1,6 +1,9 @@ -use gfx_hal::memory::{ - Segment, - SparseFlags, +use gfx_hal::{ + memory::{ + Segment, + SparseFlags, + }, + prelude::Device, }; use super::gpu::Gpu; @@ -29,6 +32,14 @@ pub struct Buffer { } impl Buffer { + /// Destroy the buffer and all it's resources with the GPU that + /// created it. + pub fn destroy(self, gpu: &Gpu) { + unsafe { + gpu.internal_logical_device().destroy_buffer(self.buffer); + gpu.internal_logical_device().free_memory(self.memory); + } + } pub fn stride(&self) -> usize { return self.stride; } @@ -82,10 +93,9 @@ impl BufferBuilder { ) -> Result, &'static str> { use gfx_hal::{ adapter::PhysicalDevice, - device::Device, MemoryTypeId, }; - let logical_device = super::internal::logical_device_for(gpu); + let logical_device = gpu.internal_logical_device(); let physical_device = super::internal::physical_device_for(gpu); // TODO(vmarcella): Add the ability for the user to specify the memory diff --git a/crates/lambda-platform/src/gfx/gpu.rs b/crates/lambda-platform/src/gfx/gpu.rs index 24e83356..e97f9aaa 100644 --- a/crates/lambda-platform/src/gfx/gpu.rs +++ b/crates/lambda-platform/src/gfx/gpu.rs @@ -183,7 +183,7 @@ impl Gpu { return Ok(()); } - pub(super) fn logical_device(&self) -> &RenderBackend::Device { + pub(super) fn internal_logical_device(&self) -> &RenderBackend::Device { return &self.gpu.device; } } diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index b21d7eff..1ddb29d5 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -158,7 +158,7 @@ impl RenderPipelineBuilder { let pipeline = unsafe { gpu - .logical_device() + .internal_logical_device() .create_graphics_pipeline(&pipeline_desc, None) .expect("Failed to create graphics pipeline") }; @@ -184,11 +184,11 @@ impl RenderPipeline { pub fn destroy(self, gpu: &super::gpu::Gpu) { unsafe { gpu - .logical_device() + .internal_logical_device() .destroy_pipeline_layout(self.pipeline_layout); gpu - .logical_device() + .internal_logical_device() .destroy_graphics_pipeline(self.pipeline); } } diff --git a/crates/lambda-platform/src/gfx/surface.rs b/crates/lambda-platform/src/gfx/surface.rs index 25887bb2..60cbae7c 100644 --- a/crates/lambda-platform/src/gfx/surface.rs +++ b/crates/lambda-platform/src/gfx/surface.rs @@ -85,7 +85,7 @@ impl Surface { swapchain: Swapchain, timeout_in_nanoseconds: u64, ) -> Result<(), &'surface str> { - let device = super::gpu::internal::logical_device_for(gpu); + let device = gpu.internal_logical_device(); self.extent = Some(swapchain.config.extent); unsafe { diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 4d2cff1c..59fd4256 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -2,6 +2,7 @@ use lambda::{ core::{ component::Component, render::{ + buffer::BufferBuilder, command::RenderCommand, mesh::MeshBuilder, pipeline::RenderPipelineBuilder, @@ -10,7 +11,11 @@ use lambda::{ Shader, ShaderBuilder, }, - vertex::VertexBuilder, + vertex::{ + VertexAttribute, + VertexBuilder, + VertexElement, + }, viewport, ResourceId, }, @@ -24,7 +29,10 @@ use lambda::{ runtimes::GenericRuntimeBuilder, }; use lambda_platform::{ - gfx::pipeline::PipelineStage, + gfx::{ + pipeline::PipelineStage, + surface::ColorFormat, + }, shaderc::{ ShaderKind, VirtualShader, @@ -82,14 +90,8 @@ impl Component for PushConstantsExample { &mut self, render_context: &mut lambda::core::render::RenderContext, ) { - let render_pass = RenderPassBuilder::new().build(&render_context); + let render_pass = RenderPassBuilder::new().build(render_context); let push_constant_size = std::mem::size_of::() as u32; - let pipeline = RenderPipelineBuilder::new() - .with_push_constant(PipelineStage::VERTEX, push_constant_size) - .build(render_context, &render_pass, &self.shader, None); - - self.render_pass = Some(render_context.attach_render_pass(render_pass)); - self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); // Create triangle mesh. let vertices = [ @@ -114,6 +116,39 @@ impl Component for PushConstantsExample { vertices.iter().for_each(|vertex| { mesh_builder.with_vertex(vertex.clone()); }); + + let mesh = mesh_builder + .with_attributes(vec![ + VertexAttribute { + location: 0, + offset: 0, + element: VertexElement { + format: ColorFormat::Rgb32Sfloat, + offset: 0, + }, + }, + VertexAttribute { + location: 1, + offset: 0, + element: VertexElement { + format: ColorFormat::Rgb32Sfloat, + offset: 12, + }, + }, + ]) + .build(); + + let pipeline = RenderPipelineBuilder::new() + .with_push_constant(PipelineStage::VERTEX, push_constant_size) + .with_buffer( + BufferBuilder::build_from_mesh(&mesh, render_context) + .expect("Failed to create buffer"), + mesh.attributes().to_vec(), + ) + .build(render_context, &render_pass, &self.shader, None); + + self.render_pass = Some(render_context.attach_render_pass(render_pass)); + self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); } fn on_detach( diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index f8c5da66..b180d26e 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -15,7 +15,7 @@ pub use lambda_platform::gfx::buffer::{ }; use super::{ - internal::mut_gpu_from_context, + mesh::Mesh, RenderContext, }; @@ -25,6 +25,16 @@ pub struct Buffer { buffer_type: BufferType, } +/// Public interface for a buffer. +impl Buffer { + /// Destroy the buffer and all it's resources with the render context that + /// created it. + pub fn destroy(self, render_context: &RenderContext) { + self.buffer.destroy(render_context.internal_gpu()); + } +} + +/// Internal interface for working with buffers. impl Buffer { /// Retrieve a reference to the internal buffer. pub(super) fn internal_buffer( @@ -50,6 +60,34 @@ impl BufferBuilder { }; } + pub fn build_from_mesh( + mesh: &Mesh, + render_context: &mut RenderContext, + ) -> Result { + let mut buffer_builder = Self::new(); + let internal_buffer = buffer_builder + .buffer_builder + .with_length(mesh.vertices().len()) + .with_usage(Usage::VERTEX) + .with_properties(Properties::CPU_VISIBLE | Properties::COHERENT) + .build( + render_context.internal_mutable_gpu(), + mesh.vertices().to_vec(), + ); + + match internal_buffer { + Ok(internal_buffer) => { + return Ok(Buffer { + buffer: internal_buffer, + buffer_type: BufferType::Vertex, + }); + } + Err(_) => { + return Err("Failed to create buffer from mesh."); + } + } + } + /// Sets the length of the buffer (In bytes). pub fn with_length(&mut self, size: usize) -> &mut Self { self.buffer_builder.with_length(size); @@ -83,7 +121,7 @@ impl BufferBuilder { ) -> Result { let buffer_allocation = self .buffer_builder - .build(&mut mut_gpu_from_context(render_context), data); + .build(render_context.internal_mutable_gpu(), data); match buffer_allocation { Ok(buffer) => { diff --git a/lambda/src/core/render/mesh.rs b/lambda/src/core/render/mesh.rs index 22485011..d89f3cae 100644 --- a/lambda/src/core/render/mesh.rs +++ b/lambda/src/core/render/mesh.rs @@ -1,11 +1,8 @@ use super::{ - buffer::{ - Buffer, - BufferBuilder, - Properties, - Usage, + vertex::{ + Vertex, + VertexAttribute, }, - vertex::Vertex, RenderContext, }; @@ -15,12 +12,16 @@ use super::{ #[derive(Debug)] pub struct Mesh { vertices: Vec, - buffer: Buffer, + attributes: Vec, } impl Mesh { - pub(super) fn buffer(&self) -> &Buffer { - return &self.buffer; + pub fn vertices(&self) -> &[Vertex] { + &self.vertices + } + + pub fn attributes(&self) -> &[VertexAttribute] { + &self.attributes } } @@ -31,13 +32,15 @@ impl Mesh { pub struct MeshBuilder { capacity: usize, vertices: Vec, + attributes: Vec, } impl MeshBuilder { pub fn new() -> Self { return Self { capacity: 0, - vertices: vec![], + vertices: Vec::new(), + attributes: Vec::new(), }; } @@ -51,37 +54,21 @@ impl MeshBuilder { return self; } + pub fn with_attributes( + &mut self, + attributes: Vec, + ) -> &mut Self { + self.attributes = attributes; + return self; + } + /// Builds a mesh from the vertices and indices that have been added to the /// builder and allocates the memory for the mesh on the GPU. - pub fn build( - &self, - render_context: &mut RenderContext, - ) -> Result { - let gpu_memory_required = - self.vertices.len() * std::mem::size_of::(); - println!( - "Allocating {} bytes of GPU memory for mesh.", - gpu_memory_required - ); - - // Allocate memory for the mesh on the GPU. - let buffer_allocation = BufferBuilder::new() - .with_length(gpu_memory_required) - .with_usage(Usage::VERTEX) - .with_properties(Properties::CPU_VISIBLE | Properties::COHERENT) - .build(render_context, self.vertices.clone()); - - match buffer_allocation { - Ok(buffer) => { - return Ok(Mesh { - vertices: self.vertices.clone(), - buffer, - }); - } - Err(error) => { - return Err(error); - } - } + pub fn build(&self) -> Mesh { + return Mesh { + vertices: self.vertices.clone(), + attributes: self.attributes.clone(), + }; } } @@ -93,8 +80,4 @@ mod tests { assert_eq!(mesh.vertices.len(), 0); } - - // TODO(vmarcella): Add more tests for mesh building once the render context - // is mockable. As of right now, testing would require the creation of a real - // render context to perform the GPU memory allocation & binding for the mesh. } diff --git a/lambda/src/core/render/mod.rs b/lambda/src/core/render/mod.rs index bede764f..62711bb6 100644 --- a/lambda/src/core/render/mod.rs +++ b/lambda/src/core/render/mod.rs @@ -310,6 +310,20 @@ impl RenderContext { } } +impl RenderContext { + /// Internal access to the RenderContext's GPU. + pub(super) fn internal_gpu(&self) -> &internal::Gpu { + return &self.gpu; + } + + /// Internal mutable access to the RenderContext's GPU. + pub(super) fn internal_mutable_gpu( + &mut self, + ) -> &mut internal::Gpu { + return &mut self.gpu; + } +} + type PlatformRenderCommand = Command; pub(crate) mod internal { @@ -360,13 +374,6 @@ pub(crate) mod internal { return &context.gpu; } - /// Returns a mutable GPU instance for the given render context. - pub fn mut_gpu_from_context( - context: &mut super::RenderContext, - ) -> &mut Gpu { - return &mut context.gpu; - } - /// Gets the surface for the given render context. pub fn surface_from_context( context: &super::RenderContext, diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index 89ed45a2..2653467c 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -1,7 +1,6 @@ use std::rc::Rc; use lambda_platform::gfx::{ - assembler::VertexAttribute, buffer::Buffer as InternalBuffer, shader::{ ShaderModuleBuilder, @@ -13,7 +12,6 @@ use super::{ buffer::Buffer, internal::{ gpu_from_context, - mut_gpu_from_context, RenderBackend, }, shader::Shader, @@ -27,6 +25,7 @@ pub struct RenderPipeline { super::internal::RenderBackend, >, >, + buffers: Vec, } impl RenderPipeline { @@ -35,6 +34,10 @@ impl RenderPipeline { Rc::try_unwrap(self.pipeline) .expect("Failed to destroy render pipeline") .destroy(gpu_from_context(render_context)); + + for buffer in self.buffers { + buffer.destroy(render_context); + } } pub fn into_platform_render_pipeline( @@ -44,8 +47,11 @@ impl RenderPipeline { } } -pub use lambda_platform::gfx::pipeline::PipelineStage; use lambda_platform::gfx::pipeline::PushConstantUpload; +pub use lambda_platform::gfx::{ + assembler::VertexAttribute, + pipeline::PipelineStage, +}; pub struct RenderPipelineBuilder { push_constants: Vec, @@ -64,41 +70,41 @@ impl RenderPipelineBuilder { /// Adds a buffer to the render pipeline. pub fn with_buffer( - &mut self, + mut self, buffer: Buffer, attributes: Vec, - ) -> &mut Self { + ) -> Self { self.buffers.push(buffer); self.attributes.extend(attributes); return self; } pub fn with_push_constant( - &mut self, + mut self, stage: PipelineStage, bytes: u32, - ) -> &mut Self { + ) -> Self { self.push_constants.push((stage, 0..bytes)); return self; } /// Builds a render pipeline based on your builder configuration. pub fn build( - &self, + self, render_context: &mut RenderContext, render_pass: &super::render_pass::RenderPass, vertex_shader: &Shader, fragment_shader: Option<&Shader>, ) -> RenderPipeline { let vertex_shader_module = ShaderModuleBuilder::new().build( - mut_gpu_from_context(render_context), + render_context.internal_mutable_gpu(), &vertex_shader.as_binary(), ShaderModuleType::Vertex, ); let fragment_shader_module = match fragment_shader { Some(shader) => Some(ShaderModuleBuilder::new().build( - mut_gpu_from_context(render_context), + render_context.internal_mutable_gpu(), &shader.as_binary(), ShaderModuleType::Fragment, )), @@ -107,8 +113,8 @@ impl RenderPipelineBuilder { let builder = lambda_platform::gfx::pipeline::RenderPipelineBuilder::new(); - let internal_buffers = self - .buffers + let buffers = self.buffers; + let internal_buffers = buffers .iter() .map(|b| b.internal_buffer()) .collect::>>(); @@ -125,13 +131,14 @@ impl RenderPipelineBuilder { ); // Clean up shader modules. - vertex_shader_module.destroy(mut_gpu_from_context(render_context)); + vertex_shader_module.destroy(render_context.internal_mutable_gpu()); if let Some(fragment_shader_module) = fragment_shader_module { - fragment_shader_module.destroy(mut_gpu_from_context(render_context)); + fragment_shader_module.destroy(render_context.internal_mutable_gpu()); } return RenderPipeline { pipeline: Rc::new(render_pipeline), + buffers, }; } } diff --git a/lambda/src/core/render/vertex.rs b/lambda/src/core/render/vertex.rs index 8a064ca0..c3f6778d 100644 --- a/lambda/src/core/render/vertex.rs +++ b/lambda/src/core/render/vertex.rs @@ -1,3 +1,8 @@ +pub use lambda_platform::gfx::assembler::{ + VertexAttribute, + VertexElement, +}; + #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Vertex { @@ -59,7 +64,7 @@ mod test { assert_eq!(vertex.normal, [0.0, 0.0, 0.0]); assert_eq!(vertex.color, [0.0, 0.0, 0.0]); - let mut vertex = vertex + let vertex = vertex .with_position([1.0, 2.0, 3.0]) .with_normal([4.0, 5.0, 6.0]) .with_color([7.0, 8.0, 9.0]) From 2aa49820ad6bed787bb5d04dd73f5b78bec65f43 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 15:34:54 -0800 Subject: [PATCH 61/67] [fix] order. --- crates/lambda-platform/src/gfx/buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index 190beaad..f04cfce8 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -36,8 +36,8 @@ impl Buffer { /// created it. pub fn destroy(self, gpu: &Gpu) { unsafe { - gpu.internal_logical_device().destroy_buffer(self.buffer); gpu.internal_logical_device().free_memory(self.memory); + gpu.internal_logical_device().destroy_buffer(self.buffer); } } pub fn stride(&self) -> usize { From c015425670dda9207e1683ab449d0126b5e20912 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 15:44:07 -0800 Subject: [PATCH 62/67] [fix] formatting for 3D demo. --- lambda/examples/push_constants.rs | 41 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 59fd4256..616fbd87 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -4,7 +4,10 @@ use lambda::{ render::{ buffer::BufferBuilder, command::RenderCommand, - mesh::MeshBuilder, + mesh::{ + Mesh, + MeshBuilder, + }, pipeline::RenderPipelineBuilder, render_pass::RenderPassBuilder, shader::{ @@ -39,6 +42,8 @@ use lambda_platform::{ }, }; +// ------------------------------ SHADER SOURCE -------------------------------- + const VERTEX_SHADER_SOURCE: &str = r#" #version 450 @@ -60,6 +65,21 @@ void main() { "#; +const FRAGMENT_SHADER_SOURCE: &str = r#" +#version 450 + +layout (location = 0) in vec3 frag_color; + +layout (location = 0) out vec4 fragment_color; + +void main() { + fragment_color = vec4(frag_color, 1.0); +} + +"#; + +// ------------------------------ PUSH CONSTANTS ------------------------------- + #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PushConstant { @@ -67,13 +87,6 @@ pub struct PushConstant { render_matrix: [[f32; 4]; 4], } -pub struct PushConstantsExample { - frame_number: u64, - shader: Shader, - render_pipeline: Option, - render_pass: Option, -} - pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] { let bytes = unsafe { let size_in_bytes = std::mem::size_of::(); @@ -85,6 +98,16 @@ pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] { return bytes; } +// --------------------------------- COMPONENT --------------------------------- + +pub struct PushConstantsExample { + frame_number: u64, + shader: Shader, + mesh: Option, + render_pipeline: Option, + render_pass: Option, +} + impl Component for PushConstantsExample { fn on_attach( &mut self, @@ -149,6 +172,7 @@ impl Component for PushConstantsExample { self.render_pass = Some(render_context.attach_render_pass(render_pass)); self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); + self.mesh = Some(mesh); } fn on_detach( @@ -246,6 +270,7 @@ impl Default for PushConstantsExample { return Self { frame_number: 0, shader, + mesh: None, render_pipeline: None, render_pass: None, }; From 4044e7a0a8889a4f95f60cc208e369bc3adda2fa Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 19:05:51 -0800 Subject: [PATCH 63/67] [update] render commands to support binding vertex buffers. --- crates/lambda-platform/src/gfx/buffer.rs | 9 ++++++ crates/lambda-platform/src/gfx/command.rs | 13 ++++++++- lambda/examples/push_constants.rs | 9 +++++- lambda/src/core/render/buffer.rs | 18 +++++++++--- lambda/src/core/render/command.rs | 34 +++++++++++++++++------ lambda/src/core/render/pipeline.rs | 28 +++++++++++++------ 6 files changed, 88 insertions(+), 23 deletions(-) diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index f04cfce8..c01698e6 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -40,11 +40,20 @@ impl Buffer { gpu.internal_logical_device().destroy_buffer(self.buffer); } } + + /// Size of each buffer element in bytes. pub fn stride(&self) -> usize { return self.stride; } } +impl Buffer { + /// Retrieve a reference to the internal buffer. + pub(super) fn internal_buffer(&self) -> &RenderBackend::Buffer { + return &self.buffer; + } +} + pub struct BufferBuilder { buffer_length: usize, usage: Usage, diff --git a/crates/lambda-platform/src/gfx/command.rs b/crates/lambda-platform/src/gfx/command.rs index 528f39cc..8ec6326a 100644 --- a/crates/lambda-platform/src/gfx/command.rs +++ b/crates/lambda-platform/src/gfx/command.rs @@ -96,6 +96,9 @@ pub enum Command { offset: u32, bytes: Vec, }, + BindVertexBuffer { + buffer: Rc>, + }, EndRecording, } @@ -187,6 +190,13 @@ impl<'command_pool, RenderBackend: gfx_hal::Backend> Command::Draw { vertices } => { self.command_buffer.draw(vertices.clone(), 0..1) } + Command::BindVertexBuffer { buffer } => { + self.command_buffer.bind_vertex_buffers( + 0, + vec![(buffer.internal_buffer(), gfx_hal::buffer::SubRange::WHOLE)] + .into_iter(), + ) + } Command::EndRecording => self.command_buffer.finish(), } } @@ -386,7 +396,8 @@ impl CommandPool { pub fn destroy(mut self, gpu: &super::gpu::Gpu) { unsafe { self.command_pool.reset(true); - super::gpu::internal::logical_device_for(gpu) + gpu + .internal_logical_device() .destroy_command_pool(self.command_pool); } } diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 616fbd87..4453b945 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -238,6 +238,10 @@ impl Component for PushConstantsExample { .clone(), viewport: viewport.clone(), }, + RenderCommand::BindVertexBuffer { + pipeline: render_pipeline.clone(), + buffer: 0, + }, RenderCommand::PushConstants { pipeline: render_pipeline.clone(), stage: PipelineStage::VERTEX, @@ -247,8 +251,11 @@ impl Component for PushConstantsExample { render_matrix: mesh_matrix, })), }, - RenderCommand::Draw { vertices: 0..3 }, + RenderCommand::Draw { + vertices: 0..self.mesh.as_ref().unwrap().vertices().len() as u32, + }, ]; + commands.push(RenderCommand::EndRenderPass); return commands; diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index b180d26e..531d6ca6 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -7,6 +7,8 @@ mod internal { }; } +use std::rc::Rc; + // publicly use Properties and Usage from buffer.rs pub use lambda_platform::gfx::buffer::{ BufferType, @@ -21,7 +23,7 @@ use super::{ #[derive(Debug)] pub struct Buffer { - buffer: internal::Buffer, + buffer: Rc>, buffer_type: BufferType, } @@ -30,13 +32,21 @@ impl Buffer { /// Destroy the buffer and all it's resources with the render context that /// created it. pub fn destroy(self, render_context: &RenderContext) { - self.buffer.destroy(render_context.internal_gpu()); + Rc::try_unwrap(self.buffer) + .expect("Failed to get inside buffer") + .destroy(render_context.internal_gpu()); } } /// Internal interface for working with buffers. impl Buffer { /// Retrieve a reference to the internal buffer. + pub(super) fn internal_buffer_rc( + &self, + ) -> Rc> { + return self.buffer.clone(); + } + pub(super) fn internal_buffer( &self, ) -> &internal::Buffer { @@ -78,7 +88,7 @@ impl BufferBuilder { match internal_buffer { Ok(internal_buffer) => { return Ok(Buffer { - buffer: internal_buffer, + buffer: Rc::new(internal_buffer), buffer_type: BufferType::Vertex, }); } @@ -126,7 +136,7 @@ impl BufferBuilder { match buffer_allocation { Ok(buffer) => { return Ok(Buffer { - buffer, + buffer: Rc::new(buffer), buffer_type: self.buffer_type, }); } diff --git a/lambda/src/core/render/command.rs b/lambda/src/core/render/command.rs index 1545edbf..39ec3b7d 100644 --- a/lambda/src/core/render/command.rs +++ b/lambda/src/core/render/command.rs @@ -37,6 +37,10 @@ pub enum RenderCommand { offset: u32, bytes: Vec, }, + BindVertexBuffer { + pipeline: super::ResourceId, + buffer: u32, + }, /// Draws a graphical primitive. Draw { vertices: Range, @@ -47,7 +51,7 @@ impl RenderCommand { /// Converts the RenderCommand into a platform compatible render command. // TODO(vmarcella): implement this using Into pub fn into_platform_command( - self, + &self, render_context: &mut RenderContext, ) -> PlatformRenderCommand { return match self { @@ -55,7 +59,7 @@ impl RenderCommand { start_at, viewports, } => PlatformRenderCommand::SetViewports { - start_at, + start_at: *start_at, viewports: viewports .into_iter() .map(|viewport| viewport.clone_gfx_viewport()) @@ -65,7 +69,7 @@ impl RenderCommand { start_at, viewports, } => PlatformRenderCommand::SetScissors { - start_at, + start_at: *start_at, viewports: viewports .into_iter() .map(|viewport| viewport.clone_gfx_viewport()) @@ -78,14 +82,14 @@ impl RenderCommand { let surface = surface_from_context(render_context); let frame_buffer = render_context.allocate_and_get_frame_buffer( render_context - .get_render_pass(render_pass) + .get_render_pass(*render_pass) .into_gfx_render_pass() .as_ref(), ); PlatformRenderCommand::BeginRenderPass { render_pass: render_context - .get_render_pass(render_pass) + .get_render_pass(*render_pass) .into_gfx_render_pass(), surface: surface.clone(), frame_buffer: frame_buffer.clone(), @@ -97,7 +101,7 @@ impl RenderCommand { PlatformRenderCommand::AttachGraphicsPipeline { pipeline: render_context .render_pipelines - .get(pipeline) + .get(*pipeline) .unwrap() .into_platform_render_pipeline(), } @@ -110,13 +114,25 @@ impl RenderCommand { } => PlatformRenderCommand::PushConstants { pipeline: render_context .render_pipelines - .get(pipeline) + .get(*pipeline) .unwrap() .into_platform_render_pipeline(), - stage, - offset, + stage: *stage, + offset: *offset, bytes: bytes.clone(), }, + RenderCommand::BindVertexBuffer { pipeline, buffer } => { + PlatformRenderCommand::BindVertexBuffer { + buffer: render_context + .render_pipelines + .get(*pipeline) + .unwrap() + .buffers() + .get(*buffer as usize) + .unwrap() + .internal_buffer_rc(), + } + } RenderCommand::Draw { vertices } => PlatformRenderCommand::Draw { vertices: vertices.clone(), }, diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index 2653467c..f37cff89 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -1,4 +1,8 @@ -use std::rc::Rc; +use std::{ + borrow::Borrow, + ops::Deref, + rc::Rc, +}; use lambda_platform::gfx::{ buffer::Buffer as InternalBuffer, @@ -25,7 +29,7 @@ pub struct RenderPipeline { super::internal::RenderBackend, >, >, - buffers: Vec, + buffers: Vec>, } impl RenderPipeline { @@ -33,14 +37,22 @@ impl RenderPipeline { pub fn destroy(self, render_context: &RenderContext) { Rc::try_unwrap(self.pipeline) .expect("Failed to destroy render pipeline") - .destroy(gpu_from_context(render_context)); + .destroy(render_context.internal_gpu()); for buffer in self.buffers { - buffer.destroy(render_context); + Rc::try_unwrap(buffer) + .expect("Failed to get high level buffer.") + .destroy(render_context); } } +} + +impl RenderPipeline { + pub(super) fn buffers(&self) -> &Vec> { + return &self.buffers; + } - pub fn into_platform_render_pipeline( + pub(super) fn into_platform_render_pipeline( &self, ) -> Rc> { return self.pipeline.clone(); @@ -55,7 +67,7 @@ pub use lambda_platform::gfx::{ pub struct RenderPipelineBuilder { push_constants: Vec, - buffers: Vec, + buffers: Vec>, attributes: Vec, } @@ -74,7 +86,7 @@ impl RenderPipelineBuilder { buffer: Buffer, attributes: Vec, ) -> Self { - self.buffers.push(buffer); + self.buffers.push(Rc::new(buffer)); self.attributes.extend(attributes); return self; } @@ -117,7 +129,7 @@ impl RenderPipelineBuilder { let internal_buffers = buffers .iter() .map(|b| b.internal_buffer()) - .collect::>>(); + .collect::>(); let render_pipeline = builder .with_push_constants(self.push_constants.clone()) From 2a8f379de512db943ec26eb96240a2c9c15417b1 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 19:11:03 -0800 Subject: [PATCH 64/67] [update] buffer flags, remove todo statements. --- crates/lambda-platform/src/gfx/buffer.rs | 2 +- lambda/examples/push_constants.rs | 4 ++-- lambda/src/core/render/buffer.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index c01698e6..579219f5 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -41,7 +41,7 @@ impl Buffer { } } - /// Size of each buffer element in bytes. + /// Size of the buffer in bytes. pub fn stride(&self) -> usize { return self.stride; } diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 4453b945..42a79089 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -179,11 +179,11 @@ impl Component for PushConstantsExample { &mut self, render_context: &mut lambda::core::render::RenderContext, ) { - todo!() + println!("Detaching component"); } fn on_event(&mut self, event: lambda::core::events::Events) { - todo!() + println!("Event: {:?}", event); } /// Update the frame number every frame. diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index 531d6ca6..9d917c86 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -79,7 +79,7 @@ impl BufferBuilder { .buffer_builder .with_length(mesh.vertices().len()) .with_usage(Usage::VERTEX) - .with_properties(Properties::CPU_VISIBLE | Properties::COHERENT) + .with_properties(Properties::CPU_VISIBLE) .build( render_context.internal_mutable_gpu(), mesh.vertices().to_vec(), From e82d7e43926ca2d0b73ec92fe18458b09381ab61 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 19 Jan 2023 21:34:39 -0800 Subject: [PATCH 65/67] [fix] 3D demo to load the color to the correct location. --- crates/lambda-platform/src/gfx/assembler.rs | 3 + crates/lambda-platform/src/gfx/buffer.rs | 3 + crates/lambda-platform/src/gfx/pipeline.rs | 29 +++++--- lambda/examples/push_constants.rs | 73 ++++++++++++++++----- lambda/src/core/render/buffer.rs | 3 +- lambda/src/core/render/pipeline.rs | 14 ++++ lambda/src/runtimes/mod.rs | 1 + 7 files changed, 100 insertions(+), 26 deletions(-) diff --git a/crates/lambda-platform/src/gfx/assembler.rs b/crates/lambda-platform/src/gfx/assembler.rs index ac7baabd..a0a5fcf9 100644 --- a/crates/lambda-platform/src/gfx/assembler.rs +++ b/crates/lambda-platform/src/gfx/assembler.rs @@ -47,6 +47,9 @@ impl PrimitiveAssemblerBuilder { match (buffers, attributes) { (Some(buffers), Some(attributes)) => { + println!( + "[DEBUG] Building primitive assembler with buffers and attributes" + ); self.buffer_descriptions = buffers .iter() .map(|buffer| VertexBufferDesc { diff --git a/crates/lambda-platform/src/gfx/buffer.rs b/crates/lambda-platform/src/gfx/buffer.rs index 579219f5..c8a08246 100644 --- a/crates/lambda-platform/src/gfx/buffer.rs +++ b/crates/lambda-platform/src/gfx/buffer.rs @@ -109,6 +109,7 @@ impl BufferBuilder { // TODO(vmarcella): Add the ability for the user to specify the memory // properties (I.E. SparseFlags::SPARSE_MEMORY). + println!("[DEBUG] Creating buffer of length: {}", self.buffer_length); let buffer_result = unsafe { logical_device.create_buffer( self.buffer_length as u64, @@ -127,6 +128,7 @@ impl BufferBuilder { unsafe { logical_device.get_buffer_requirements(&buffer) }; let memory_types = physical_device.memory_properties().memory_types; + println!("[DEBUG] Buffer requirements: {:?}", requirements); // Find a memory type that supports the requirements of the buffer. let memory_type = memory_types .iter() @@ -138,6 +140,7 @@ impl BufferBuilder { .map(|(id, _)| MemoryTypeId(id)) .unwrap(); + println!("Allocating memory for buffer."); // Allocates the memory on the GPU for the buffer. let buffer_memory_allocation = unsafe { logical_device.allocate_memory(memory_type, requirements.size) }; diff --git a/crates/lambda-platform/src/gfx/pipeline.rs b/crates/lambda-platform/src/gfx/pipeline.rs index 1ddb29d5..f01cf815 100644 --- a/crates/lambda-platform/src/gfx/pipeline.rs +++ b/crates/lambda-platform/src/gfx/pipeline.rs @@ -36,7 +36,10 @@ use std::ops::Range; use gfx_hal::device::Device; use super::{ - assembler::VertexAttribute, + assembler::{ + PrimitiveAssemblerBuilder, + VertexAttribute, + }, buffer::Buffer, gpu::Gpu, shader::ShaderModule, @@ -110,9 +113,8 @@ impl RenderPipelineBuilder { let push_constants = self.push_constants.into_iter(); let pipeline_layout = unsafe { - use internal::Device; - - super::internal::logical_device_for(gpu) + gpu + .internal_logical_device() .create_pipeline_layout(vec![].into_iter(), push_constants) .expect( "The GPU does not have enough memory to allocate a pipeline layout", @@ -121,7 +123,7 @@ impl RenderPipelineBuilder { // TODO(vmarcella): The primitive assembler should be configurable through // the RenderPipelineBuilder so that buffers & attributes can be bound. - let mut builder = super::assembler::PrimitiveAssemblerBuilder::new(); + let mut builder = PrimitiveAssemblerBuilder::new(); let primitive_assembler = builder.build(vertex_shader, Some(buffers), Some(attributes)); @@ -129,7 +131,7 @@ impl RenderPipelineBuilder { Some(shader) => Some(internal::EntryPoint:: { entry: shader.entry(), module: super::internal::module_for(shader), - specialization: gfx_hal::pso::Specialization::default(), + specialization: shader.specializations().clone(), }), None => None, }; @@ -157,10 +159,14 @@ impl RenderPipelineBuilder { }); let pipeline = unsafe { - gpu + let pipeline_build_result = gpu .internal_logical_device() - .create_graphics_pipeline(&pipeline_desc, None) - .expect("Failed to create graphics pipeline") + .create_graphics_pipeline(&pipeline_desc, None); + + match pipeline_build_result { + Ok(pipeline) => pipeline, + Err(e) => panic!("Failed to create graphics pipeline: {:?}", e), + } }; return RenderPipeline { @@ -182,7 +188,12 @@ pub struct RenderPipeline { impl RenderPipeline { /// Destroys the pipeline layout and graphical pipeline pub fn destroy(self, gpu: &super::gpu::Gpu) { + println!("Destroying render pipeline"); unsafe { + for buffer in self.buffers { + buffer.destroy(gpu); + } + gpu .internal_logical_device() .destroy_pipeline_layout(self.pipeline_layout); diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 42a79089..36c05cc6 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -1,6 +1,7 @@ use lambda::{ core::{ component::Component, + events::WindowEvent, render::{ buffer::BufferBuilder, command::RenderCommand, @@ -47,7 +48,7 @@ use lambda_platform::{ const VERTEX_SHADER_SOURCE: &str = r#" #version 450 -layout (location = 0) in vec2 vertex_position; +layout (location = 0) in vec3 vertex_position; layout (location = 1) in vec3 vertex_normal; layout (location = 2) in vec3 vertex_color; @@ -59,7 +60,7 @@ layout ( push_constant ) uniform PushConstant { } push_constants; void main() { - gl_Position = push_constants.render_matrix * vec4(vertex_position, 0.0, 1.0); + gl_Position = push_constants.render_matrix * vec4(vertex_position, 1.0); frag_color = vertex_color; } @@ -103,9 +104,13 @@ pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] { pub struct PushConstantsExample { frame_number: u64, shader: Shader, + fs: Shader, mesh: Option, render_pipeline: Option, render_pass: Option, + last_frame: std::time::Duration, + width: u32, + height: u32, } impl Component for PushConstantsExample { @@ -119,17 +124,17 @@ impl Component for PushConstantsExample { // Create triangle mesh. let vertices = [ VertexBuilder::new() - .with_position([0.0, 0.5, 0.0]) + .with_position([1.0, 1.0, 0.0]) .with_normal([0.0, 0.0, 0.0]) .with_color([1.0, 0.0, 0.0]) .build(), VertexBuilder::new() - .with_position([-0.5, -0.5, 0.0]) + .with_position([-1.0, 1.0, 0.0]) .with_normal([0.0, 0.0, 0.0]) .with_color([0.0, 1.0, 0.0]) .build(), VertexBuilder::new() - .with_position([0.5, -0.5, 0.0]) + .with_position([0.0, -1.0, 0.0]) .with_normal([0.0, 0.0, 0.0]) .with_color([0.0, 0.0, 1.0]) .build(), @@ -151,16 +156,18 @@ impl Component for PushConstantsExample { }, }, VertexAttribute { - location: 1, + location: 2, offset: 0, element: VertexElement { format: ColorFormat::Rgb32Sfloat, - offset: 12, + offset: 24, }, }, ]) .build(); + println!("mesh: {:?}", mesh); + let pipeline = RenderPipelineBuilder::new() .with_push_constant(PipelineStage::VERTEX, push_constant_size) .with_buffer( @@ -168,7 +175,7 @@ impl Component for PushConstantsExample { .expect("Failed to create buffer"), mesh.attributes().to_vec(), ) - .build(render_context, &render_pass, &self.shader, None); + .build(render_context, &render_pass, &self.shader, Some(&self.fs)); self.render_pass = Some(render_context.attach_render_pass(render_pass)); self.render_pipeline = Some(render_context.attach_pipeline(pipeline)); @@ -183,11 +190,26 @@ impl Component for PushConstantsExample { } fn on_event(&mut self, event: lambda::core::events::Events) { - println!("Event: {:?}", event); + match event { + lambda::core::events::Events::Window { event, issued_at } => { + match event { + WindowEvent::Resize { width, height } => { + self.width = width; + self.height = height; + println!("Window resized to {}x{}", width, height); + } + _ => {} + } + } + _ => {} + } } /// Update the frame number every frame. - fn on_update(&mut self, last_frame: &std::time::Duration) {} + fn on_update(&mut self, last_frame: &std::time::Duration) { + self.last_frame = *last_frame; + self.frame_number += 1; + } fn on_render( &mut self, @@ -198,22 +220,26 @@ impl Component for PushConstantsExample { let view: [[f32; 4]; 4] = matrix::translation_matrix(camera); // Create a projection matrix. - let mut projection: [[f32; 4]; 4] = - matrix::perspective_matrix(0.25, 1700.0 / 900.0, 0.1, 200.0); - projection.as_mut()[1].as_mut()[1] *= -1.0; + let mut projection: [[f32; 4]; 4] = matrix::perspective_matrix( + 0.25, + (self.width / self.height) as f32, + 0.1, + 200.0, + ); // Rotate model. let model: [[f32; 4]; 4] = matrix::rotate_matrix( - matrix::filled_matrix(4, 4, 1.0), + matrix::identity_matrix(4, 4), [0.0, 1.0, 0.0], - 0.4 * self.frame_number as f32, + 0.001 * self.frame_number as f32, ); // Create render matrix. let mesh_matrix = projection.multiply(&view).multiply(&model); // Create viewport. - let viewport = viewport::ViewportBuilder::new().build(800, 600); + let viewport = + viewport::ViewportBuilder::new().build(self.width, self.height); let render_pipeline = self .render_pipeline @@ -271,15 +297,27 @@ impl Default for PushConstantsExample { name: "push_constants".to_string(), }; + let triangle_fragment_shader = VirtualShader::Source { + source: FRAGMENT_SHADER_SOURCE.to_string(), + kind: ShaderKind::Fragment, + entry_point: "main".to_string(), + name: "push_constants".to_string(), + }; + let mut builder = ShaderBuilder::new(); let shader = builder.build(triangle_in_3d); + let fs = builder.build(triangle_fragment_shader); return Self { frame_number: 0, shader, + fs, + last_frame: std::time::Duration::from_secs(0), mesh: None, render_pipeline: None, render_pass: None, + width: 800, + height: 600, }; } } @@ -291,6 +329,9 @@ fn main() { .with_dimensions(800, 600) .with_name("3D Push Constants Example"); }) + .with_renderer_configured_as(|renderer_builder| { + return renderer_builder.with_render_timeout(1_000_000_000); + }) .with_component(move |runtime, triangles: PushConstantsExample| { return (runtime, triangles); }) diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index 9d917c86..ea0d7ad9 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -18,6 +18,7 @@ pub use lambda_platform::gfx::buffer::{ use super::{ mesh::Mesh, + vertex::Vertex, RenderContext, }; @@ -77,7 +78,7 @@ impl BufferBuilder { let mut buffer_builder = Self::new(); let internal_buffer = buffer_builder .buffer_builder - .with_length(mesh.vertices().len()) + .with_length(mesh.vertices().len() * std::mem::size_of::()) .with_usage(Usage::VERTEX) .with_properties(Properties::CPU_VISIBLE) .build( diff --git a/lambda/src/core/render/pipeline.rs b/lambda/src/core/render/pipeline.rs index f37cff89..b9955d12 100644 --- a/lambda/src/core/render/pipeline.rs +++ b/lambda/src/core/render/pipeline.rs @@ -108,12 +108,21 @@ impl RenderPipelineBuilder { vertex_shader: &Shader, fragment_shader: Option<&Shader>, ) -> RenderPipeline { + println!("[DEBUG] Building render pipeline..."); + + print!("[DEBUG] Building vertex shader... "); let vertex_shader_module = ShaderModuleBuilder::new().build( render_context.internal_mutable_gpu(), &vertex_shader.as_binary(), ShaderModuleType::Vertex, ); + println!( + " Done. (Vertex shader: {} bytes)", + vertex_shader.as_binary().len() + ); + + print!("[DEBUG] Building fragment shader... "); let fragment_shader_module = match fragment_shader { Some(shader) => Some(ShaderModuleBuilder::new().build( render_context.internal_mutable_gpu(), @@ -123,6 +132,11 @@ impl RenderPipelineBuilder { None => None, }; + println!( + " Done. (Fragment shader: {} bytes)", + fragment_shader.map(|s| s.as_binary().len()).unwrap_or(0) + ); + let builder = lambda_platform::gfx::pipeline::RenderPipelineBuilder::new(); let buffers = self.buffers; diff --git a/lambda/src/runtimes/mod.rs b/lambda/src/runtimes/mod.rs index 25dad601..af03473c 100644 --- a/lambda/src/runtimes/mod.rs +++ b/lambda/src/runtimes/mod.rs @@ -312,6 +312,7 @@ impl Runtime for GenericRuntime { match mapped_event { Some(event) => { + println!("Sending event: {:?} to all components", event); for component in &mut component_stack { component.on_event(event.clone()); } From 53f3586b08d676626d8181cdeaa69a4a3b26822b Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 20 Jan 2023 15:23:27 -0800 Subject: [PATCH 66/67] [update] mesh rendering in push_constants. --- lambda/examples/push_constants.rs | 38 +++++++++++++------- lambda/src/math/matrix.rs | 59 +++++++++++++++++++------------ 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/lambda/examples/push_constants.rs b/lambda/examples/push_constants.rs index 36c05cc6..cbe83baf 100644 --- a/lambda/examples/push_constants.rs +++ b/lambda/examples/push_constants.rs @@ -99,6 +99,24 @@ pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] { return bytes; } +fn make_transform( + translate: [f32; 3], + angle: f32, + scale: f32, +) -> [[f32; 4]; 4] { + let c = angle.cos() * scale; + let s = angle.sin() * scale; + + let [x, y, z] = translate; + + return [ + [c, 0.0, s, 0.0], + [0.0, scale, 0.0, 0.0], + [-s, 0.0, c, 0.0], + [x, y, z, 1.0], + ]; +} + // --------------------------------- COMPONENT --------------------------------- pub struct PushConstantsExample { @@ -190,6 +208,7 @@ impl Component for PushConstantsExample { } fn on_event(&mut self, event: lambda::core::events::Events) { + // Only handle resizes. match event { lambda::core::events::Events::Window { event, issued_at } => { match event { @@ -216,16 +235,12 @@ impl Component for PushConstantsExample { render_context: &mut lambda::core::render::RenderContext, ) -> Vec { self.frame_number += 1; - let mut camera = [0.0, 0.0, -2.0]; + let camera = [0.0, 0.0, -2.0]; let view: [[f32; 4]; 4] = matrix::translation_matrix(camera); // Create a projection matrix. - let mut projection: [[f32; 4]; 4] = matrix::perspective_matrix( - 0.25, - (self.width / self.height) as f32, - 0.1, - 200.0, - ); + let projection: [[f32; 4]; 4] = + matrix::perspective_matrix(0.25, (4 / 3) as f32, 0.1, 100.0); // Rotate model. let model: [[f32; 4]; 4] = matrix::rotate_matrix( @@ -236,6 +251,8 @@ impl Component for PushConstantsExample { // Create render matrix. let mesh_matrix = projection.multiply(&view).multiply(&model); + let mesh_matrix = + make_transform([0.0, 0.0, 0.5], self.frame_number as f32 * 0.01, 0.5); // Create viewport. let viewport = @@ -245,7 +262,7 @@ impl Component for PushConstantsExample { .render_pipeline .expect("No render pipeline actively set for rendering."); - let mut commands = vec![ + return vec![ RenderCommand::SetViewports { start_at: 0, viewports: vec![viewport.clone()], @@ -280,11 +297,8 @@ impl Component for PushConstantsExample { RenderCommand::Draw { vertices: 0..self.mesh.as_ref().unwrap().vertices().len() as u32, }, + RenderCommand::EndRenderPass, ]; - - commands.push(RenderCommand::EndRenderPass); - - return commands; } } diff --git a/lambda/src/math/matrix.rs b/lambda/src/math/matrix.rs index d17fffd9..417795ec 100644 --- a/lambda/src/math/matrix.rs +++ b/lambda/src/math/matrix.rs @@ -103,7 +103,7 @@ pub fn rotate_matrix< let angle_in_radians = turns_to_radians(angle_in_turns); let cosine_of_angle = angle_in_radians.cos(); - let sin_of_angle = -angle_in_radians.sin(); + let sin_of_angle = angle_in_radians.sin(); let t = 1.0 - cosine_of_angle; let x = axis_to_rotate.at(0); @@ -112,27 +112,42 @@ pub fn rotate_matrix< let mut rotation_matrix = OutputMatrix::default(); - let rotation = [ - [ - t * x * x + cosine_of_angle, - t * x * y - (sin_of_angle * z), - t * x * z + (sin_of_angle * y), - 0.0, - ], - [ - t * x * y + (sin_of_angle * z), - t * y * y + cosine_of_angle, - t * y * z - (sin_of_angle * x), - 0.0, - ], - [ - t * x * z - (sin_of_angle * y), - t * y * z - (sin_of_angle * x), - t * z * z + cosine_of_angle, - 0.0, - ], - [0.0, 0.0, 0.0, 1.0], - ]; + let rotation = match (x as u8, y as u8, z as u8) { + (0, 0, 0) => { + // No rotation + return matrix_to_rotate; + } + (0, 0, 1) => { + // Rotate around z-axis + [ + [cosine_of_angle, sin_of_angle, 0.0, 0.0], + [-sin_of_angle, cosine_of_angle, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ] + } + (0, 1, 0) => { + // Rotate around y-axis + [ + [cosine_of_angle, 0.0, -sin_of_angle, 0.0], + [0.0, 1.0, 0.0, 0.0], + [sin_of_angle, 0.0, cosine_of_angle, 0.0], + [0.0, 0.0, 0.0, 1.0], + ] + } + (1, 0, 0) => { + // Rotate around x-axis + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, cosine_of_angle, sin_of_angle, 0.0], + [0.0, -sin_of_angle, cosine_of_angle, 0.0], + [0.0, 0.0, 0.0, 1.0], + ] + } + _ => { + panic!("Axis must be a unit vector") + } + }; for i in 0..rows { for j in 0..columns { From 1079b57252ba7193345290f375c84597232fe213 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Fri, 20 Jan 2023 16:42:25 -0800 Subject: [PATCH 67/67] [add] comment. --- lambda/src/core/render/buffer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lambda/src/core/render/buffer.rs b/lambda/src/core/render/buffer.rs index ea0d7ad9..b228df0c 100644 --- a/lambda/src/core/render/buffer.rs +++ b/lambda/src/core/render/buffer.rs @@ -76,6 +76,8 @@ impl BufferBuilder { render_context: &mut RenderContext, ) -> Result { let mut buffer_builder = Self::new(); + + // Allocate a buffer with the size of the mesh's vertices. let internal_buffer = buffer_builder .buffer_builder .with_length(mesh.vertices().len() * std::mem::size_of::())