diff --git a/Cargo.lock b/Cargo.lock index ab8d181..a382229 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,12 +18,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "ahash" version = "0.8.11" @@ -139,12 +133,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -198,12 +186,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.6.0" @@ -295,12 +277,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "com" version = "0.6.0" @@ -391,46 +367,12 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "cursor-icon" version = "1.1.0" @@ -484,12 +426,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - [[package]] name = "equivalent" version = "1.0.1" @@ -506,50 +442,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "exr" -version = "1.72.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" -dependencies = [ - "bit_field", - "flume", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -689,16 +581,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gl_generator" version = "0.14.0" @@ -783,16 +665,6 @@ dependencies = [ "bitflags 2.5.0", ] -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -853,24 +725,6 @@ dependencies = [ "cc", ] -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "exr", - "gif", - "jpeg-decoder", - "num-traits", - "png", - "qoi", - "tiff", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -912,15 +766,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -947,12 +792,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "libc" version = "0.2.155" @@ -1026,7 +865,6 @@ dependencies = [ "chrono", "futures", "getrandom", - "image", "lock_api", "log", "lunar-engine-derive", @@ -1096,16 +934,6 @@ dependencies = [ "paste", ] -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", - "simd-adler32", -] - [[package]] name = "naga" version = "0.20.0" @@ -1505,19 +1333,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polling" version = "3.7.0" @@ -1569,15 +1384,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "quick-xml" version = "0.31.0" @@ -1638,26 +1444,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1761,12 +1547,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "slab" version = "0.4.9" @@ -1825,15 +1605,6 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -1906,17 +1677,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -2228,12 +1988,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "wgpu" version = "0.20.1" @@ -2747,12 +2501,3 @@ dependencies = [ "quote", "syn 2.0.65", ] - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index 78a6c9c..0894f1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ lunar-engine-derive= {path = "./lunar-engine-derive", version="0.1.0"} bytemuck = { version = "1.14.0", features = ["derive"] } chrono = "0.4.31" futures = "0.3.30" -image = "0.24.7" lock_api = "0.4.11" log = "0.4.20" parking_lot = "0.12.1" diff --git a/examples/blahaj/camera_movement.rs b/examples/blahaj/camera_movement.rs index 95da398..6430a38 100644 --- a/examples/blahaj/camera_movement.rs +++ b/examples/blahaj/camera_movement.rs @@ -8,6 +8,7 @@ use lunar_engine::{ input, math::{mat4x4::Mat4x4, vec4::Vec4}, }; +use lunar_engine_derive::as_any; use winit::keyboard::KeyCode; #[derive(Debug)] @@ -33,6 +34,7 @@ impl FreeCam { } impl Component for FreeCam { + #[as_any] fn mew() -> Self where Self: Sized, @@ -50,14 +52,6 @@ impl Component for FreeCam { self.transorm_reference = Some(reference.get_component().unwrap()) } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } - fn update(&mut self) { let trans = self.transorm_reference.as_ref().unwrap().borrow(); let old_pos = trans.position; diff --git a/examples/blahaj/main.rs b/examples/blahaj/main.rs index 617b1ff..5282983 100644 --- a/examples/blahaj/main.rs +++ b/examples/blahaj/main.rs @@ -11,7 +11,7 @@ use lunar_engine::{ system::rendering::{self, extensions::Base}, State, }; -use lunar_engine_derive::{dependencies, marker_component}; +use lunar_engine_derive::{as_any, dependencies, marker_component}; use winit::keyboard::KeyCode; use crate::camera_movement::FreeCam; @@ -37,6 +37,7 @@ struct Spiny { } impl Component for Spiny { + #[as_any] #[dependencies(Transform)] fn mew() -> Self @@ -57,14 +58,6 @@ impl Component for Spiny { self.transform.as_ref().unwrap().borrow_mut().rotation.y += self.speed * lunar_engine::delta_time(); } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } fn init(state: &mut MyState) { diff --git a/lunar-engine-derive/src/lib.rs b/lunar-engine-derive/src/lib.rs index ded4e81..807d8c7 100644 --- a/lunar-engine-derive/src/lib.rs +++ b/lunar-engine-derive/src/lib.rs @@ -1,6 +1,10 @@ //! Proc macros for easier use of the ECS -#![allow(clippy::missing_panics_doc, clippy::collapsible_if)] +#![allow( + clippy::missing_panics_doc, + clippy::collapsible_if, + clippy::too_many_lines +)] use proc_macro::{Group, Punct, TokenStream, TokenTree}; ///Adds a `compile_error` with the defined message, before the provided token stream @@ -15,7 +19,7 @@ fn comp_error(error: &str, item: TokenStream) -> TokenStream { ///Describes various struct types enum StructType { ///Normal struct - ///``` + ///```ignore ///struct A { /// ... ///} @@ -23,13 +27,13 @@ enum StructType { Regular, ///Tupple struct /// - ///``` + ///```ignore ///struct A(...); ///``` Tupple, ///Empty struct /// - ///``` + ///```ignore ///struct A; ///``` Empty, @@ -95,7 +99,7 @@ fn is_struct_declaration(item: &TokenStream) -> Option { ///component calls to the aliased component. /// ///# Examples -///``` +///```ignore ///struct CopmonentA { /// ... ///} @@ -211,6 +215,10 @@ pub fn alias(attr: TokenStream, item: TokenStream) -> TokenStream { .parse::() .unwrap(); + let comment = format!("///Alias of [`{base}`]") + .parse::() + .unwrap(); + let mut items = items; let tmp = if let TokenTree::Group(i) = items.last().unwrap() { @@ -223,8 +231,13 @@ pub fn alias(attr: TokenStream, item: TokenStream) -> TokenStream { *items.last_mut().unwrap() = tmp; - let mut o = items.into_iter().collect::(); - o.extend([deref, deref_mut, component_impl]); + let mut o = comment.into_iter().collect::(); + o.extend([ + items.into_iter().collect::(), + deref, + deref_mut, + component_impl, + ]); o } @@ -234,7 +247,7 @@ pub fn alias(attr: TokenStream, item: TokenStream) -> TokenStream { ///distinguish an entity. /// ///# Examples -///``` +///```ignore ///#[marker_component] ///struct Marker; /// @@ -309,7 +322,7 @@ pub fn marker_component(attr: TokenStream, item: TokenStream) -> TokenStream { ///Defines dependencies of a component. Must be placed inside the `impl Component` block /// ///# Examples -///``` +///```ignore ///struct Test; /// ///impl Component for Test { @@ -368,3 +381,42 @@ pub fn dependencies(attr: TokenStream, item: TokenStream) -> TokenStream { .chain(item) .collect::() } + +#[proc_macro_attribute] +///Implements `as_any` and `as_any_mut` functions for Components and Assets +/// +///# Examples +///```ignore +///struct TestAsset{ +/// ... +///} +/// +///impl Asset for TestAsset{ +///#[as_any] +/// ... +///} +///``` +/// +///```ignore +///struct TestComponent{ +/// ... +///} +/// +///impl Component for TestComponent{ +///#[as_any] +/// ... +///} +///``` +pub fn as_any(_: TokenStream, item: TokenStream) -> TokenStream { + let as_any = + " fn as_any(&self) -> &dyn std::any::Any { self as &dyn std::any::Any } ".to_string(); + let as_any_mut = + " fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self as &mut dyn std::any::Any } "; + + (as_any + as_any_mut) + .parse::() + .unwrap() + .into_iter() + .chain(item) + .collect() +} diff --git a/src/asset_managment/mod.rs b/src/asset_managment/mod.rs index 5e4d718..dbc1eae 100644 --- a/src/asset_managment/mod.rs +++ b/src/asset_managment/mod.rs @@ -84,7 +84,7 @@ pub type UUID = u128; /// ///ID must not be set before the asset is registered pub trait Asset: Send + Sync + std::any::Any { - ///Returns id of the entity + ///Returns id of the asset fn get_id(&self) -> UUID; ///Performs initialization of the asset /// @@ -98,13 +98,14 @@ pub trait Asset: Send + Sync + std::any::Any { ///# Errors ///Returns an error if the id was already set fn set_id(&mut self, id: UUID) -> Result<(), Error>; - ///Returns wether or not the asset is initialized + ///Returns whether or not the asset is initialized fn is_initialized(&self) -> bool; //Will not be needed after Rust 1.75.0 //Cannot be implemented automatically, well... likely can be, but i can't be bothered ///Converts trait object to a `std::any::Any` reference /// - ///This function should be implemented as follows + ///Please use [`lunar_engine_derive::as_any`] to implement this function automatically. + ///Alternatively this function should be implemented as follows ///``` ///# use lunar_engine::asset_managment::Asset; ///# use std::any::Any; @@ -126,7 +127,8 @@ pub trait Asset: Send + Sync + std::any::Any { fn as_any(&self) -> &dyn std::any::Any; ///Converts trait object to a mutable `std::any::Any` reference /// - ///This function should be implemented as follows + ///Please use [`lunar_engine_derive::as_any`] to implement this function automatically. + ///Alternatively this function should be implemented as follows ///``` ///# use lunar_engine::asset_managment::Asset; ///# use std::any::Any; @@ -160,6 +162,7 @@ pub type AssetGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, parking_lot::Ra pub type AssetGuardMut<'a, T> = lock_api::MappedRwLockWriteGuard<'a, parking_lot::RawRwLock, T>; impl AssetReference { + ///Borrows the asset immutably pub fn borrow(&self) -> AssetGuard<'_, T> { let read = self.refernce.read(); lock_api::RwLockReadGuard::<'_, parking_lot::RawRwLock, Box<(dyn Asset + 'static)>>::map( @@ -168,6 +171,7 @@ impl AssetReference { ) } + ///Borrows the asset mutably pub fn borrow_mut(&self) -> AssetGuardMut<'_, T> { let write = self.refernce.write(); lock_api::RwLockWriteGuard::<'_, parking_lot::RawRwLock, Box<(dyn Asset + 'static)>>::map( diff --git a/src/asset_managment/tests.rs b/src/asset_managment/tests.rs index 181ed2b..b038ab0 100644 --- a/src/asset_managment/tests.rs +++ b/src/asset_managment/tests.rs @@ -1,3 +1,5 @@ +use lunar_engine_derive::as_any; + use super::*; struct TestAsset { @@ -16,6 +18,7 @@ impl TestAsset { } impl Asset for TestAsset { + #[as_any] fn get_id(&self) -> UUID { self.id.unwrap() } @@ -40,14 +43,6 @@ impl Asset for TestAsset { } } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } - fn is_initialized(&self) -> bool { self.initialized } diff --git a/src/assets/material.rs b/src/assets/material.rs new file mode 100644 index 0000000..27e2592 --- /dev/null +++ b/src/assets/material.rs @@ -0,0 +1,87 @@ +use lunar_engine_derive::as_any; + +use crate::asset_managment::{Asset, AssetStore, UUID}; + +use super::BindgroupState; + +///Trait for implementing materials +#[allow(clippy::module_name_repetitions)] +pub trait MaterialTrait { + ///Render function of the material + fn render(&self, render_pass: &mut wgpu::RenderPass); + ///Initialization of the material + fn intialize(&mut self); + ///Disposal of the material + fn dispose(&mut self); + ///Creation of bindgroups and populating them with data + fn set_bindgroups(&mut self, asset_store: &AssetStore); + ///State of the bindgroups of the material + fn bindgroup_sate(&self) -> BindgroupState; +} + +///Stores material data, wrapper around the material trait object +pub struct Material { + id: Option, + initialized: bool, + material: Box, +} + +impl Asset for Material { + #[as_any] + + fn get_id(&self) -> UUID { + self.id.unwrap() + } + + fn initialize(&mut self) -> Result<(), Box> { + self.material.intialize(); + self.initialized = true; + Ok(()) + } + + fn dispose(&mut self) { + self.material.dispose(); + self.initialized = false; + } + + fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { + if self.id.is_some() { + Err(crate::asset_managment::Error::IdAlreadySet) + } else { + self.id = Some(id); + Ok(()) + } + } + + fn is_initialized(&self) -> bool { + self.initialized + } +} + +impl Material { + #[must_use] + ///Get the bindgroup state of the material + pub fn get_bindgroup_state(&self) -> BindgroupState { + self.material.bindgroup_sate() + } + + ///Initialize bindgroups of the material + pub fn initialize_bindgroups(&mut self, asset_store: &AssetStore) { + self.material.set_bindgroups(asset_store); + } + + ///Call the render function of the material + pub fn render(&self, render_pass: &mut wgpu::RenderPass) { + self.material.render(render_pass); + } +} + +impl From> for Material { + fn from(value: Box) -> Self { + Self { + id: None, + initialized: false, + material: value, + } + } +} diff --git a/src/assets/materials.rs b/src/assets/materials.rs index 855bb56..b2b9dc5 100644 --- a/src/assets/materials.rs +++ b/src/assets/materials.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use crate::{asset_managment::UUID, grimoire, DEVICE, FORMAT}; -use super::{BindgroupState, MaterialTrait, Texture}; +use super::{material::MaterialTrait, BindgroupState, Texture}; +///Basic material that renders an object with a given texture, without lighting pub struct TextureUnlit { #[cfg(target_arch = "wasm32")] pipeline: Option>>, @@ -25,6 +26,7 @@ pub struct TextureUnlit { impl TextureUnlit { #[allow(clippy::new_ret_no_self)] #[must_use] + ///Creates a new material with a give texture id pub fn new(texture_id: UUID) -> Box { Box::new(Self { pipeline: None, diff --git a/src/assets/mesh.rs b/src/assets/mesh.rs new file mode 100644 index 0000000..9b727f3 --- /dev/null +++ b/src/assets/mesh.rs @@ -0,0 +1,251 @@ +//============================================================ +//===========================Mesh============================= +//============================================================ +//Yea this trash is not expandale later on, gltf may be a bit too complex for loading into +//separate assets, who knows tho +// +//Actually, thinking about it, i think loading a gltf should produce an entire set of assets + +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; + +use lunar_engine_derive::as_any; +use wgpu::util::DeviceExt; + +use crate::{ + asset_managment::{Asset, UUID}, + DEVICE, +}; + +///Asset that stores mesh data +pub struct Mesh { + id: Option, + initialized: bool, + mode: MeshMode, + #[cfg(target_arch = "wasm32")] + vertex_buffer: Option>>, + #[cfg(target_arch = "wasm32")] + index_buffer: Option>>, + #[cfg(not(target_arch = "wasm32"))] + vertex_buffer: Option>, + #[cfg(not(target_arch = "wasm32"))] + index_buffer: Option>, + vert_count: Option, + tris_count: Option, + index_count: Option, +} + +///Ways the mesh can be loaded from file +enum MeshMode { + ///An obj file that contains a single mesh + SingleObjectOBJ(PathBuf), + StaticSingleObjectOBJ(&'static str), +} + +impl Mesh { + ///Creates anew asset that will load the first object in a waveform obj file that is statically + ///loaded + #[must_use] + pub const fn new_from_static_obj(mesh: &'static str) -> Self { + Self { + id: None, + initialized: false, + mode: MeshMode::StaticSingleObjectOBJ(mesh), + vertex_buffer: None, + index_buffer: None, + vert_count: None, + tris_count: None, + index_count: None, + } + } + + ///Creates a new asset that will load the first object in a waveform obj file + /// + ///Currently unsupported on the web target + /// + ///# Errors + ///Returns an error if the file does not exist + pub fn new_from_obj(path: &Path) -> Result { + //Verify that file exists + std::fs::File::options().read(true).open(path)?; + Ok(Self { + id: None, + initialized: false, + mode: MeshMode::SingleObjectOBJ(path.to_owned()), + vertex_buffer: None, + index_buffer: None, + tris_count: None, + vert_count: None, + index_count: None, + }) + } + + ///Returns the vertex buffer of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[cfg(target_arch = "wasm32")] + #[must_use] + pub fn get_vertex_buffer(&self) -> Arc> { + //THIS IS SO TRASH + self.vertex_buffer.clone().unwrap() + } + + ///Returns the vertex buffer of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[cfg(not(target_arch = "wasm32"))] + #[must_use] + pub fn get_vertex_buffer(&self) -> Arc { + self.vertex_buffer.clone().unwrap() + } + + ///Returns the index buffer of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[cfg(target_arch = "wasm32")] + #[must_use] + pub fn get_index_buffer(&self) -> Arc> { + self.index_buffer.clone().unwrap() + } + + ///Returns the index buffer of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[cfg(not(target_arch = "wasm32"))] + #[must_use] + pub fn get_index_buffer(&self) -> Arc { + self.index_buffer.clone().unwrap() + } + + ///Returns the vertex count of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[must_use] + pub fn get_tris_count(&self) -> u32 { + self.tris_count.unwrap() + } + + ///Returns the index count of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[must_use] + pub fn get_index_count(&self) -> u32 { + self.index_count.unwrap() + } + + ///Returns the vertex count of the mesh + /// + ///# Panics + ///Panics if the asset was not initialized + #[must_use] + pub fn get_vert_count(&self) -> u32 { + self.vert_count.unwrap() + } +} + +impl Asset for Mesh { + #[as_any] + + fn get_id(&self) -> UUID { + self.id.unwrap() + } + + #[allow(clippy::cast_possible_truncation)] + fn initialize(&mut self) -> Result<(), Box> { + //This is horrific, but i LOVE this :3 + let mesh = match &self.mode { + MeshMode::SingleObjectOBJ(path) => { + //Prase file + match crate::import::obj::parse( + //Load file + &(match std::fs::read_to_string(path) { + Ok(it) => it, + Err(err) => return Err(Box::new(err)), + }), + ) + //Get the first mesh + .and_then(|i| i.into_iter().nth(0)) + { + Some(it) => it, + None => { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid file", + ))); + } + } + } + MeshMode::StaticSingleObjectOBJ(mesh) => { + match crate::import::obj::parse(mesh).and_then(|i| i.into_iter().nth(0)) { + Some(it) => it, + None => { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid data", + ))); + } + } + } + }; + + let device = DEVICE.get().unwrap(); + let name = format!("Mesh {}", self.get_id()); + + let vb = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&name), + contents: bytemuck::cast_slice(mesh.vertices.as_slice()), + usage: wgpu::BufferUsages::VERTEX, + }); + + let ib = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&name), + contents: bytemuck::cast_slice(mesh.indecies.as_slice()), + usage: wgpu::BufferUsages::INDEX, + }); + + #[cfg(target_arch = "wasm32")] + { + self.vertex_buffer = Some(Arc::new(crate::wrappers::WgpuWrapper::new(vb))); + self.index_buffer = Some(Arc::new(crate::wrappers::WgpuWrapper::new(ib))); + } + #[cfg(not(target_arch = "wasm32"))] + { + self.vertex_buffer = Some(Arc::new(vb)); + self.index_buffer = Some(Arc::new(ib)); + } + self.vert_count = Some(mesh.vertices.len() as u32); + self.tris_count = Some((mesh.indecies.len() as u32) / 3u32); + self.index_count = Some(mesh.indecies.len() as u32); + + self.initialized = true; + Ok(()) + } + + fn dispose(&mut self) { + //Unload index and vertex buffers, clearing memory + self.vertex_buffer = None; + self.index_buffer = None; + self.initialized = false; + } + + fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { + if self.id.is_some() { + Err(crate::asset_managment::Error::IdAlreadySet) + } else { + self.id = Some(id); + Ok(()) + } + } + + fn is_initialized(&self) -> bool { + self.initialized + } +} diff --git a/src/assets/mod.rs b/src/assets/mod.rs index 3bf97a2..c2e99d5 100644 --- a/src/assets/mod.rs +++ b/src/assets/mod.rs @@ -1,586 +1,25 @@ -use std::{ - path::{Path, PathBuf}, - sync::{Arc, RwLock}, -}; - -use wgpu::util::DeviceExt; - -use crate::{ - asset_managment::{Asset, AssetStore, UUID}, - structures::Image, - DEVICE, -}; - -#[cfg(test)] -mod tests; +//! Implemented assets +///Material struct +pub mod material; ///Contains implemented materials pub mod materials; +///Mesh asset +pub mod mesh; +#[cfg(test)] +mod tests; +///Texture asset +pub mod texture; -//============================================================ -//===========================Texture========================== -//============================================================ - -///Stores texture data -pub struct Texture { - id: Option, - initialized: bool, - image_format: ImageFormat, - filepath: Option, - r#static: Static, - mip_count: u8, - sample_count: u8, - adress_mode: wgpu::AddressMode, - filter: wgpu::FilterMode, - #[cfg(target_arch = "wasm32")] - sampler: Option>, - #[cfg(not(target_arch = "wasm32"))] - sampler: Option, - #[cfg(target_arch = "wasm32")] - texture: Option>, - #[cfg(not(target_arch = "wasm32"))] - texture: Option, -} - -///Wether or not the texture is static -enum Static { - ///Is static, contains the byes of the data - //Christ, another arc refcell - Yes(Vec, Option>>), - ///Is not static - No, -} - -///Supported image formats for the asset -enum ImageFormat { - ///.bmp bitmap(only 32 bpp without compression) - Bmp, -} - -#[allow(unused_variables)] -impl Texture { - ///Initializes a texture to load a bmp file in runtime - /// - ///Currently unsupported on the web target - /// - #[must_use] - pub fn new_bmp(path: &Path) -> Self { - Self { - id: None, - initialized: false, - image_format: ImageFormat::Bmp, - filepath: Some(path.to_owned()), - r#static: Static::No, - mip_count: 1, - sample_count: 1, - adress_mode: wgpu::AddressMode::ClampToEdge, - filter: wgpu::FilterMode::Linear, - sampler: None, - texture: None, - } - } - - ///Initializes a texture to parse the texture in runtime, but being loaded at comp time - /// - ///Is only supposed to be used for small textures that are always needed - #[must_use] - pub fn static_bmp(data: &'static [u8]) -> Self { - Self { - id: None, - initialized: false, - image_format: ImageFormat::Bmp, - filepath: None, - r#static: Static::Yes(data.to_vec(), None), - mip_count: 1, - sample_count: 1, - adress_mode: wgpu::AddressMode::ClampToEdge, - filter: wgpu::FilterMode::Linear, - sampler: None, - texture: None, - } - } - - /// Loads image data into `wgpu::Texture` - fn load_into_gpu(&mut self, image: &Arc>) { - let device = crate::DEVICE.get().unwrap(); - let queue = crate::QUEUE.get().unwrap(); - let image = image.read().unwrap(); - - let var_name = &format!("{}", self.get_id()); - let label = Some(var_name.as_str()); - - let texture = device.create_texture_with_data( - queue, - &wgpu::TextureDescriptor { - label, - size: wgpu::Extent3d { - width: image.width, - height: image.height, - depth_or_array_layers: 1, - }, - mip_level_count: self.mip_count.into(), - sample_count: self.sample_count.into(), - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[wgpu::TextureFormat::Rgba8Unorm], - }, - wgpu::util::TextureDataOrder::LayerMajor, - bytemuck::cast_slice(&image.data), - ); - - drop(image); - - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label, - address_mode_u: self.adress_mode, - address_mode_v: self.adress_mode, - address_mode_w: self.adress_mode, - mag_filter: self.filter, - min_filter: self.filter, - mipmap_filter: self.filter, - lod_min_clamp: 1.0, - lod_max_clamp: 1.0, - compare: None, - anisotropy_clamp: 1, - border_color: None, - }); - - #[cfg(target_arch = "wasm32")] - { - self.texture = Some(crate::wrappers::WgpuWrapper::new(texture)); - self.sampler = Some(crate::wrappers::WgpuWrapper::new(sampler)); - } - #[cfg(not(target_arch = "wasm32"))] - { - self.texture = Some(texture); - self.sampler = Some(sampler); - } - } - - pub fn set_mip_count(&mut self, count: u8) { - todo!("Not yet implemented") - } - - pub fn set_sample_count(&mut self, count: u8) { - todo!("Not yet implemented") - } - - pub fn set_filter(&mut self, filter: wgpu::FilterMode) { - todo!("Not yet implemented") - } - - pub fn set_adress_mode(&mut self, filter: wgpu::AddressMode) { - todo!("Not yet implemented") - } -} - -impl Asset for Texture { - fn get_id(&self) -> UUID { - self.id.unwrap() - } - - fn initialize(&mut self) -> Result<(), Box> { - if let Static::Yes(_, Some(img)) = &self.r#static { - self.load_into_gpu(&img.clone()); - self.initialized = true; - return Ok(()); - } - - let image = match &self.r#static { - Static::Yes(d, _) => d.clone(), - Static::No => { - if let Some(file) = &self.filepath { - match std::fs::read(file) { - Ok(it) => it, - Err(err) => return Err(Box::new(err)), - } - } else { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "File not found", - ))); - } - } - }; - - let image = match self.image_format { - ImageFormat::Bmp => match crate::import::bmp::parse(&image) { - Ok(it) => it, - Err(err) => return Err(err), - }, - }; - - //This is so trash - let image = Arc::new(RwLock::new(image)); - self.load_into_gpu(&image); - - if let Static::Yes(_, arc) = &mut self.r#static { - *arc = Some(image); - } - self.initialized = true; - - Ok(()) - } - - fn dispose(&mut self) { - //Don't dispose of static textures - if let Static::Yes(..) = self.r#static { - return; - } - self.initialized = false; - } - - fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { - if self.id.is_some() { - Err(crate::asset_managment::Error::IdAlreadySet) - } else { - self.id = Some(id); - Ok(()) - } - } - - fn is_initialized(&self) -> bool { - self.initialized - } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } -} - -//============================================================ -//===========================Mesh============================= -//============================================================ -//Yea this trash is not expandale later on, gltf may be a bit too complex for loading into -//separate assets, who knows tho -// -//Actually, thinking about it, i think loading a gltf should produce an entire set of assets - -///Asset that stores mesh data -pub struct Mesh { - id: Option, - initialized: bool, - mode: MeshMode, - #[cfg(target_arch = "wasm32")] - vertex_buffer: Option>>, - #[cfg(target_arch = "wasm32")] - index_buffer: Option>>, - #[cfg(not(target_arch = "wasm32"))] - vertex_buffer: Option>, - #[cfg(not(target_arch = "wasm32"))] - index_buffer: Option>, - vert_count: Option, - tris_count: Option, - index_count: Option, -} - -///Ways the mesh can be loaded from file -enum MeshMode { - ///An obj file that contains a single mesh - SingleObjectOBJ(PathBuf), - StaticSingleObjectOBJ(&'static str), -} - -impl Mesh { - ///Creates anew asset that will load the first object in a waveform obj file that is statically - ///loaded - #[must_use] - pub const fn new_from_static_obj(mesh: &'static str) -> Self { - Self { - id: None, - initialized: false, - mode: MeshMode::StaticSingleObjectOBJ(mesh), - vertex_buffer: None, - index_buffer: None, - vert_count: None, - tris_count: None, - index_count: None, - } - } - - ///Creates a new asset that will load the first object in a waveform obj file - /// - ///Currently unsupported on the web target - /// - ///# Errors - ///Returns an error if the file does not exist - pub fn new_from_obj(path: &Path) -> Result { - //Verify that file exists - std::fs::File::options().read(true).open(path)?; - Ok(Self { - id: None, - initialized: false, - mode: MeshMode::SingleObjectOBJ(path.to_owned()), - vertex_buffer: None, - index_buffer: None, - tris_count: None, - vert_count: None, - index_count: None, - }) - } - - ///Returns the vertex buffer of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[cfg(target_arch = "wasm32")] - #[must_use] - pub fn get_vertex_buffer(&self) -> Arc> { - //THIS IS SO TRASH - self.vertex_buffer.clone().unwrap() - } - - ///Returns the vertex buffer of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[cfg(not(target_arch = "wasm32"))] - #[must_use] - pub fn get_vertex_buffer(&self) -> Arc { - self.vertex_buffer.clone().unwrap() - } - - ///Returns the index buffer of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[cfg(target_arch = "wasm32")] - #[must_use] - pub fn get_index_buffer(&self) -> Arc> { - self.index_buffer.clone().unwrap() - } - - ///Returns the index buffer of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[cfg(not(target_arch = "wasm32"))] - #[must_use] - pub fn get_index_buffer(&self) -> Arc { - self.index_buffer.clone().unwrap() - } - - ///Returns the vertex count of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[must_use] - pub fn get_tris_count(&self) -> u32 { - self.tris_count.unwrap() - } - - ///Returns the index count of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[must_use] - pub fn get_index_count(&self) -> u32 { - self.index_count.unwrap() - } - - ///Returns the vertex count of the mesh - /// - ///# Panics - ///Panics if the asset was not initialized - #[must_use] - pub fn get_vert_count(&self) -> u32 { - self.vert_count.unwrap() - } -} - -impl Asset for Mesh { - fn get_id(&self) -> UUID { - self.id.unwrap() - } - - #[allow(clippy::cast_possible_truncation)] - fn initialize(&mut self) -> Result<(), Box> { - //This is horrific, but i LOVE this :3 - let mesh = match &self.mode { - MeshMode::SingleObjectOBJ(path) => { - //Prase file - match crate::import::obj::parse( - //Load file - &(match std::fs::read_to_string(path) { - Ok(it) => it, - Err(err) => return Err(Box::new(err)), - }), - ) - //Get the first mesh - .and_then(|i| i.into_iter().nth(0)) - { - Some(it) => it, - None => { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Invalid file", - ))); - } - } - } - MeshMode::StaticSingleObjectOBJ(mesh) => { - match crate::import::obj::parse(mesh).and_then(|i| i.into_iter().nth(0)) { - Some(it) => it, - None => { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Invalid data", - ))); - } - } - } - }; - - let device = DEVICE.get().unwrap(); - let name = format!("Mesh {}", self.get_id()); - - let vb = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&name), - contents: bytemuck::cast_slice(mesh.vertices.as_slice()), - usage: wgpu::BufferUsages::VERTEX, - }); - - let ib = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&name), - contents: bytemuck::cast_slice(mesh.indecies.as_slice()), - usage: wgpu::BufferUsages::INDEX, - }); - - #[cfg(target_arch = "wasm32")] - { - self.vertex_buffer = Some(Arc::new(crate::wrappers::WgpuWrapper::new(vb))); - self.index_buffer = Some(Arc::new(crate::wrappers::WgpuWrapper::new(ib))); - } - #[cfg(not(target_arch = "wasm32"))] - { - self.vertex_buffer = Some(Arc::new(vb)); - self.index_buffer = Some(Arc::new(ib)); - } - self.vert_count = Some(mesh.vertices.len() as u32); - self.tris_count = Some((mesh.indecies.len() as u32) / 3u32); - self.index_count = Some(mesh.indecies.len() as u32); - - self.initialized = true; - Ok(()) - } - - fn dispose(&mut self) { - //Unload index and vertex buffers, clearing memory - self.vertex_buffer = None; - self.index_buffer = None; - self.initialized = false; - } - - fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { - if self.id.is_some() { - Err(crate::asset_managment::Error::IdAlreadySet) - } else { - self.id = Some(id); - Ok(()) - } - } - - fn is_initialized(&self) -> bool { - self.initialized - } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } -} - -//============================================================ -//===========================MATERIAL========================== -//============================================================ +pub use material::Material; +pub use mesh::Mesh; +pub use texture::Texture; #[derive(Clone, Copy)] +///Represents bindroup state of an asset that contains gpu related data pub enum BindgroupState { + ///Bindgroups are not initialized Uninitialized, + ///Bindgroups are initialized Initialized, } - -pub trait MaterialTrait { - fn render(&self, render_pass: &mut wgpu::RenderPass); - fn intialize(&mut self); - fn dispose(&mut self); - fn set_bindgroups(&mut self, asset_store: &AssetStore); - fn bindgroup_sate(&self) -> BindgroupState; -} - -///Stores material data -pub struct Material { - id: Option, - initialized: bool, - material: Box, -} - -impl Asset for Material { - fn get_id(&self) -> UUID { - self.id.unwrap() - } - - fn initialize(&mut self) -> Result<(), Box> { - self.material.intialize(); - self.initialized = true; - Ok(()) - } - - fn dispose(&mut self) { - self.material.dispose(); - self.initialized = false; - } - - fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { - if self.id.is_some() { - Err(crate::asset_managment::Error::IdAlreadySet) - } else { - self.id = Some(id); - Ok(()) - } - } - - fn is_initialized(&self) -> bool { - self.initialized - } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } -} - -impl Material { - #[must_use] - pub fn get_bindgroup_state(&self) -> BindgroupState { - self.material.bindgroup_sate() - } - - pub fn initialize_bindgroups(&mut self, asset_store: &AssetStore) { - self.material.set_bindgroups(asset_store); - } - - pub fn render(&self, render_pass: &mut wgpu::RenderPass) { - self.material.render(render_pass); - } -} - -impl From> for Material { - fn from(value: Box) -> Self { - Self { - id: None, - initialized: false, - material: value, - } - } -} diff --git a/src/assets/texture.rs b/src/assets/texture.rs new file mode 100644 index 0000000..57af152 --- /dev/null +++ b/src/assets/texture.rs @@ -0,0 +1,244 @@ +use std::{ + path::{Path, PathBuf}, + sync::{Arc, RwLock}, +}; + +use lunar_engine_derive::as_any; +use wgpu::util::DeviceExt; + +use crate::{ + asset_managment::{Asset, UUID}, + structures::Image, +}; + +///Stores texture data +pub struct Texture { + id: Option, + initialized: bool, + image_format: ImageFormat, + filepath: Option, + r#static: Static, + mip_count: u8, + sample_count: u8, + adress_mode: wgpu::AddressMode, + filter: wgpu::FilterMode, + #[cfg(target_arch = "wasm32")] + pub(crate) sampler: Option>, + #[cfg(not(target_arch = "wasm32"))] + pub(crate) sampler: Option, + #[cfg(target_arch = "wasm32")] + pub(crate) texture: Option>, + #[cfg(not(target_arch = "wasm32"))] + pub(crate) texture: Option, +} + +///Wether or not the texture is static +enum Static { + ///Is static, contains the byes of the data + //Christ, another arc refcell + Yes(Vec, Option>>), + ///Is not static + No, +} + +///Supported image formats for the asset +enum ImageFormat { + ///.bmp bitmap(only 32 bpp without compression) + Bmp, +} + +#[allow(unused_variables)] +impl Texture { + ///Initializes a texture to load a bmp file in runtime + /// + ///Currently unsupported on the web target + /// + #[must_use] + pub fn new_bmp(path: &Path) -> Self { + Self { + id: None, + initialized: false, + image_format: ImageFormat::Bmp, + filepath: Some(path.to_owned()), + r#static: Static::No, + mip_count: 1, + sample_count: 1, + adress_mode: wgpu::AddressMode::ClampToEdge, + filter: wgpu::FilterMode::Linear, + sampler: None, + texture: None, + } + } + + ///Initializes a texture to parse the texture in runtime, but being loaded at comp time + /// + ///Is only supposed to be used for small textures that are always needed + #[must_use] + pub fn static_bmp(data: &'static [u8]) -> Self { + Self { + id: None, + initialized: false, + image_format: ImageFormat::Bmp, + filepath: None, + r#static: Static::Yes(data.to_vec(), None), + mip_count: 1, + sample_count: 1, + adress_mode: wgpu::AddressMode::ClampToEdge, + filter: wgpu::FilterMode::Linear, + sampler: None, + texture: None, + } + } + + /// Loads image data into `wgpu::Texture` + fn load_into_gpu(&mut self, image: &Arc>) { + let device = crate::DEVICE.get().unwrap(); + let queue = crate::QUEUE.get().unwrap(); + let image = image.read().unwrap(); + + let var_name = &format!("{}", self.get_id()); + let label = Some(var_name.as_str()); + + let texture = device.create_texture_with_data( + queue, + &wgpu::TextureDescriptor { + label, + size: wgpu::Extent3d { + width: image.width, + height: image.height, + depth_or_array_layers: 1, + }, + mip_level_count: self.mip_count.into(), + sample_count: self.sample_count.into(), + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[wgpu::TextureFormat::Rgba8Unorm], + }, + wgpu::util::TextureDataOrder::LayerMajor, + bytemuck::cast_slice(&image.data), + ); + + drop(image); + + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label, + address_mode_u: self.adress_mode, + address_mode_v: self.adress_mode, + address_mode_w: self.adress_mode, + mag_filter: self.filter, + min_filter: self.filter, + mipmap_filter: self.filter, + lod_min_clamp: 1.0, + lod_max_clamp: 1.0, + compare: None, + anisotropy_clamp: 1, + border_color: None, + }); + + #[cfg(target_arch = "wasm32")] + { + self.texture = Some(crate::wrappers::WgpuWrapper::new(texture)); + self.sampler = Some(crate::wrappers::WgpuWrapper::new(sampler)); + } + #[cfg(not(target_arch = "wasm32"))] + { + self.texture = Some(texture); + self.sampler = Some(sampler); + } + } + + // ///Sets the mip count of the texture and generates those mips + // pub fn set_mip_count(&mut self, count: u8) { + // todo!("Not yet implemented") + // } + + // /// + // pub fn set_sample_count(&mut self, count: u8) { + // todo!("Not yet implemented") + // } + + ///Sets the filter of the texture + pub fn set_filter(&mut self, filter: wgpu::FilterMode) { + //Change the filter and recreate the sampler + todo!("Not yet implemented") + } + + ///Sets the address of the texture + pub fn set_adress_mode(&mut self, filter: wgpu::AddressMode) { + //Change the address mode and recreate the sampler + todo!("Not yet implemented") + } +} + +impl Asset for Texture { + #[as_any] + + fn get_id(&self) -> UUID { + self.id.unwrap() + } + + fn initialize(&mut self) -> Result<(), Box> { + if let Static::Yes(_, Some(img)) = &self.r#static { + self.load_into_gpu(&img.clone()); + self.initialized = true; + return Ok(()); + } + + let image = match &self.r#static { + Static::Yes(d, _) => d.clone(), + Static::No => { + if let Some(file) = &self.filepath { + match std::fs::read(file) { + Ok(it) => it, + Err(err) => return Err(Box::new(err)), + } + } else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "File not found", + ))); + } + } + }; + + let image = match self.image_format { + ImageFormat::Bmp => match crate::import::bmp::parse(&image) { + Ok(it) => it, + Err(err) => return Err(err), + }, + }; + + //This is so trash + let image = Arc::new(RwLock::new(image)); + self.load_into_gpu(&image); + + if let Static::Yes(_, arc) = &mut self.r#static { + *arc = Some(image); + } + self.initialized = true; + + Ok(()) + } + + fn dispose(&mut self) { + //Don't dispose of static textures + if let Static::Yes(..) = self.r#static { + return; + } + self.initialized = false; + } + + fn set_id(&mut self, id: UUID) -> Result<(), crate::asset_managment::Error> { + if self.id.is_some() { + Err(crate::asset_managment::Error::IdAlreadySet) + } else { + self.id = Some(id); + Ok(()) + } + } + + fn is_initialized(&self) -> bool { + self.initialized + } +} diff --git a/src/components/camera.rs b/src/components/camera.rs index b780503..e4300ce 100644 --- a/src/components/camera.rs +++ b/src/components/camera.rs @@ -1,7 +1,7 @@ use std::num::NonZeroU64; use log::debug; -use lunar_engine_derive::{alias, dependencies}; +use lunar_engine_derive::{alias, as_any, dependencies}; use crate as lunar_engine; // use lunar_engine::ecs; @@ -9,16 +9,20 @@ use crate as lunar_engine; use crate::{ ecs::{Component, ComponentReference}, grimoire::{CAMERA_BIND_GROUP_INDEX, CAMERA_BIND_GROUP_LAYOUT_DESCRIPTOR}, - math::{mat4x4::Mat4x4, vec4::Vec4}, + math::{Mat4x4, Vec4}, DEVICE, RESOLUTION, STAGING_BELT, }; use super::transform::Transform; #[derive(Debug, Default)] +///Camera used for rendering of the objects pub struct Camera { + ///Fov of the camera in radians pub fov: f32, + ///Near plane of the camera pub near: f32, + ///Far plane of the camera pub far: f32, transorm_reference: Option>, buffer: Option, @@ -26,6 +30,7 @@ pub struct Camera { } impl Component for Camera { + #[as_any] #[dependencies(Transform)] fn mew() -> Self where @@ -42,18 +47,11 @@ impl Component for Camera { debug!("set self reference called"); self.transorm_reference = Some(reference.get_component().unwrap()); } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } impl Camera { #[must_use] + ///Creates a new Camera pub fn new(fov: f32, near: f32, far: f32) -> Self { Self { fov, @@ -64,6 +62,7 @@ impl Camera { } #[must_use] + ///Returns the transformation matrix of the camera multiplied by the projection matrix pub fn matrix(&self) -> Mat4x4 { let binding = self.transorm_reference.as_ref().unwrap(); let transform = binding.borrow(); @@ -85,7 +84,8 @@ impl Camera { camera_matrix * projection_matrix } - pub fn initialize_gpu(&mut self) { + ///Initializes gpu related components of the camera: Buffers, bindgroups, etc. + pub(crate) fn initialize_gpu(&mut self) { let device = DEVICE.get().unwrap(); let buf = crate::helpers::create_uniform_matrix(Some("Camera")); @@ -109,7 +109,8 @@ impl Camera { self.bind_group = Some(bind_group); } - pub fn update_gpu(&mut self, encoder: &mut wgpu::CommandEncoder) { + ///Updates the buffer of the camera with the new camera matrix + pub(crate) fn update_gpu(&mut self, encoder: &mut wgpu::CommandEncoder) { let mut staging_belt = STAGING_BELT.get().unwrap().write().unwrap(); staging_belt @@ -123,7 +124,8 @@ impl Camera { .copy_from_slice(bytemuck::bytes_of(&self.matrix())); } - pub fn set_bindgroup<'a, 'b>(&'a self, render_pass: &mut wgpu::RenderPass<'b>) + ///Sets bindgroups of the camera for rendering + pub(crate) fn set_bindgroup<'a, 'b>(&'a self, render_pass: &mut wgpu::RenderPass<'b>) where 'a: 'b, { diff --git a/src/components/mesh.rs b/src/components/mesh.rs index 376e6c3..3dd52e6 100644 --- a/src/components/mesh.rs +++ b/src/components/mesh.rs @@ -1,14 +1,17 @@ #![allow(dead_code)] +use lunar_engine_derive::as_any; + use crate::{ asset_managment::UUID, ecs::{Component, ComponentReference}, - math::mat4x4::Mat4x4, + math::Mat4x4, }; use super::transform::Transform; #[derive(Debug, Default)] +///Mesh component used for rendering pub struct Mesh { mesh_id: Option, material_id: Option, @@ -16,6 +19,8 @@ pub struct Mesh { } impl Component for Mesh { + #[as_any] + fn mew() -> Self where Self: Sized, @@ -23,14 +28,6 @@ impl Component for Mesh { Self::default() } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } - #[allow(unused_variables)] fn set_self_reference(&mut self, reference: crate::ecs::SelfReferenceGuard) { self.transform_reference = Some(reference.get_component().unwrap()); @@ -39,6 +36,7 @@ impl Component for Mesh { impl Mesh { #[must_use] + ///Creates a new mesh with the given mesh and material ids pub const fn new(mesh: UUID, material: UUID) -> Self { Self { mesh_id: Some(mesh), @@ -75,7 +73,7 @@ impl Mesh { } #[must_use] - pub fn get_matrix(&self) -> Mat4x4 { + pub(crate) fn get_matrix(&self) -> Mat4x4 { self.transform_reference .as_ref() .unwrap() diff --git a/src/components/mod.rs b/src/components/mod.rs index 1d4cc0b..f8ef3a3 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,5 +1,9 @@ +//!Implemented components +///Camera component pub mod camera; +///Mesh component pub mod mesh; #[cfg(test)] mod tests; +///Transformation component pub mod transform; diff --git a/src/components/transform.rs b/src/components/transform.rs index 817c4e9..3b959f6 100644 --- a/src/components/transform.rs +++ b/src/components/transform.rs @@ -1,4 +1,6 @@ -use crate::math::{mat4x4::Mat4x4, vec3::Vec3}; +use lunar_engine_derive::as_any; + +use crate::math::{Mat4x4, Vec3}; use crate::ecs::{Component, ComponentReference}; @@ -7,10 +9,14 @@ use crate::ecs::{Component, ComponentReference}; ///Note: rotation is represented as Euler angles using degrees #[derive(Debug)] pub struct Transform { + ///Position of the object pub position: Vec3, + ///Rotation of the object pub rotation: Vec3, + ///Scale of the object pub scale: Vec3, - parent: Option>, + ///Parent transform of the object + pub parent: Option>, } impl Default for Transform { @@ -29,6 +35,8 @@ impl Default for Transform { } impl Component for Transform { + #[as_any] + fn mew() -> Self where Self: Sized, @@ -40,12 +48,6 @@ impl Component for Transform { parent: None, } } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } impl Transform { @@ -61,11 +63,12 @@ impl Transform { } ///Creates a new transform instance, with a parent - pub fn with_parent( + #[must_use] + pub const fn with_parent( position: Vec3, rotation: Vec3, scale: Vec3, - parent: ComponentReference, + parent: ComponentReference, ) -> Self { Self { position, @@ -78,23 +81,25 @@ impl Transform { ///Returns transformation of the entity taking transform of the parent into account #[must_use] pub fn matrix(&self) -> Mat4x4 { - if let Some(p) = &self.parent { - let parent_mat = p.borrow().matrix(); - parent_mat * Mat4x4::transform_matrix_euler(&self.position, &self.scale, &self.rotation) - } else { - Mat4x4::transform_matrix_euler(&self.position, &self.scale, &self.rotation) - } + self.parent.as_ref().map_or_else( + || Mat4x4::transform_matrix_euler(&self.position, &self.scale, &self.rotation), + |p| { + let parent_mat = p.borrow().matrix(); + parent_mat + * Mat4x4::transform_matrix_euler(&self.position, &self.scale, &self.rotation) + }, + ) } - //Returns transformation matrix of the entity, without taking the parent transformation into - //account + ///Returns transformation matrix of the entity, without taking the parent transformation into + ///account #[must_use] pub fn matrix_local(&self) -> Mat4x4 { Mat4x4::transform_matrix_euler(&self.position, &self.scale, &self.rotation) } ///Sets the parent of the entity, applying all parent transformations to this entity - pub fn set_parent(mut self, p: ComponentReference) { + pub fn set_parent(mut self, p: ComponentReference) { self.parent = Some(p); } } diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs index 56e5255..96579f3 100644 --- a/src/ecs/mod.rs +++ b/src/ecs/mod.rs @@ -32,7 +32,8 @@ pub trait Component: std::any::Any { //Cannot be implemented automatically, well... likely can be, but i can't be bothered ///Converts trait object to a `std::any::Any` reference /// - ///This function should be implemented as follows + ///Please use [`lunar_engine_derive::as_any`] to implement this function automatically. + ///Alternatively this function should be implemented as follows ///``` /// # use lunar_engine::ecs::Component; /// # struct A; @@ -49,7 +50,8 @@ pub trait Component: std::any::Any { fn as_any(&self) -> &dyn std::any::Any; ///Converts trait object to a mutable `std::any::Any` reference /// - ///This function should be implemented as follows + ///Please use [`lunar_engine_derive::as_any`] to implement this function automatically. + ///Alternatively this function should be implemented as follows ///``` /// # use lunar_engine::ecs::Component; /// # struct A; @@ -75,7 +77,7 @@ pub trait Component: std::any::Any { /// ///# Note /// - ///This function is not meant to be defined manually, use [`lunar_engine_derive::dependencies`] + ///This function is not meant to be implemented manually, use [`lunar_engine_derive::dependencies`] ///macro instead. #[allow(unused_variables)] fn check_dependencies(entity: &Entity) -> Result<(), &'static str> @@ -86,7 +88,7 @@ pub trait Component: std::any::Any { } #[allow(clippy::missing_errors_doc, unused_variables)] - ///See `check_dependencies` + ///See [`Component::check_dependencies`] fn check_dependencies_instanced(&self, entity: &Entity) -> Result<(), &'static str> { Ok(()) } diff --git a/src/ecs/tests.rs b/src/ecs/tests.rs index 7063410..8239522 100644 --- a/src/ecs/tests.rs +++ b/src/ecs/tests.rs @@ -1,4 +1,4 @@ -use lunar_engine_derive::{alias, dependencies}; +use lunar_engine_derive::{alias, as_any, dependencies}; use crate as lunar_engine; @@ -10,6 +10,8 @@ struct TestComponent1 { } impl Component for TestComponent1 { + #[as_any] + fn mew() -> Self where Self: Sized, @@ -20,14 +22,6 @@ impl Component for TestComponent1 { fn update(&mut self) { self.value += 10; } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } #[derive(Debug)] @@ -36,6 +30,7 @@ struct TestComponent { } impl Component for TestComponent { + #[as_any] fn mew() -> Self where Self: Sized, @@ -46,13 +41,6 @@ impl Component for TestComponent { fn update(&mut self) { self.value += 10; } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } struct TestComponent2 { @@ -65,19 +53,13 @@ impl std::fmt::Debug for TestComponent2 { } } impl Component for TestComponent2 { + #[as_any] fn mew() -> Self where Self: Sized, { Self { weak: None } } - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } fn set_self_reference(&mut self, reference: SelfReferenceGuard) { self.weak = Some(reference); @@ -88,6 +70,7 @@ impl Component for TestComponent2 { struct TestComponent3; impl Component for TestComponent3 { + #[as_any] #[dependencies(TestComponent)] fn mew() -> Self where @@ -95,14 +78,6 @@ impl Component for TestComponent3 { { Self } - - fn as_any(&self) -> &dyn std::any::Any { - self as &dyn std::any::Any - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self as &mut dyn std::any::Any - } } #[test] diff --git a/src/helpers.rs b/src/helpers.rs index 2162f2d..fd12c7d 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,69 +1,69 @@ #![allow(clippy::missing_errors_doc, dead_code)] -use std::io::Cursor; -use image::{ImageBuffer, ImageError, Rgba}; +// use std::io::Cursor; +// use image::{ImageBuffer, ImageError, Rgba}; use crate::DEVICE; -pub fn arr_to_image( - img: &[u8], - bytes_per_row: u32, - width: u32, - height: u32, - format: image::ImageOutputFormat, -) -> Result, ImageError> { - let img = img - .chunks_exact(4) - .map(|i| { - let mut array = [0; 4]; - array.copy_from_slice(i); - let tmp = Rgba(array); - let mut out = tmp; - out[0] = tmp[2]; - out[2] = tmp[0]; - out - }) - .collect::>>(); +// pub fn arr_to_image( +// img: &[u8], +// bytes_per_row: u32, +// width: u32, +// height: u32, +// format: image::ImageOutputFormat, +// ) -> Result, ImageError> { +// let img = img +// .chunks_exact(4) +// .map(|i| { +// let mut array = [0; 4]; +// array.copy_from_slice(i); +// let tmp = Rgba(array); +// let mut out = tmp; +// out[0] = tmp[2]; +// out[2] = tmp[0]; +// out +// }) +// .collect::>>(); - log::debug!("Collected the array"); - log::debug!("BPR = {bytes_per_row}, width = {width}"); - let mut image_buffer = ImageBuffer::, Vec>::new(width, height); - let mut x = 0; - let mut y = 0; +// log::debug!("Collected the array"); +// log::debug!("BPR = {bytes_per_row}, width = {width}"); +// let mut image_buffer = ImageBuffer::, Vec>::new(width, height); +// let mut x = 0; +// let mut y = 0; - for i in img { - //Remove padding - if width != bytes_per_row && x >= width { - x += 1; - if x == bytes_per_row { - x = 0; - y += 1; - } - continue; - } +// for i in img { +// //Remove padding +// if width != bytes_per_row && x >= width { +// x += 1; +// if x == bytes_per_row { +// x = 0; +// y += 1; +// } +// continue; +// } - *image_buffer.get_pixel_mut(x, y) = i; +// *image_buffer.get_pixel_mut(x, y) = i; - x += 1; - //This is kind of a hacky fix, but it works - if x == width && width == bytes_per_row { - x = 0; - y += 1; - } - } - log::debug!("Iterated through the array"); - let mut byte_stream = Vec::new(); - image_buffer.write_to(&mut Cursor::new(&mut byte_stream), format)?; +// x += 1; +// //This is kind of a hacky fix, but it works +// if x == width && width == bytes_per_row { +// x = 0; +// y += 1; +// } +// } +// log::debug!("Iterated through the array"); +// let mut byte_stream = Vec::new(); +// image_buffer.write_to(&mut Cursor::new(&mut byte_stream), format)?; - Ok(byte_stream) -} +// Ok(byte_stream) +// } pub fn create_uniform_matrix(label: Option<&str>) -> wgpu::Buffer { let device = DEVICE.get().unwrap(); device.create_buffer(&wgpu::BufferDescriptor { label, - size: std::mem::size_of::() as u64, + size: std::mem::size_of::() as u64, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }) diff --git a/src/import/mod.rs b/src/import/mod.rs index 6f9f0e1..9012056 100644 --- a/src/import/mod.rs +++ b/src/import/mod.rs @@ -1,2 +1,5 @@ +//! Asset import +///.bmp image loading pub mod bmp; +///.obj mesh loading pub mod obj; diff --git a/src/import/obj.rs b/src/import/obj.rs index 7ce9972..51e14f8 100644 --- a/src/import/obj.rs +++ b/src/import/obj.rs @@ -1,6 +1,6 @@ #![allow(clippy::cast_possible_truncation)] use crate::{ - math::{vec2::Vec2, vec3::Vec3}, + math::{Vec2, Vec3}, structures::{Mesh, Vertex}, }; diff --git a/src/input.rs b/src/input.rs index 04231ae..bb83bf5 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,9 +1,10 @@ +//! Functions for getting user input use std::sync::{OnceLock, RwLock}; use vec_key_value_pair::map::VecMap; -use winit::{event::MouseButton, keyboard::KeyCode}; +use winit::{dpi::PhysicalPosition, event::MouseButton, keyboard::KeyCode, window::CursorGrabMode}; -use crate::math::vec2::Vec2; +use crate::{math::Vec2, WINDOW}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] ///Represents the state of the key @@ -103,3 +104,97 @@ pub(crate) fn update() { *input.cursor_delta.write().unwrap() = *cur - *last; *last = *cur; } + +//Sets the cursor grab mode +// pub fn set_cursor_grab_mode(mode: CursorState) { +// let mut state = CURSOR_STATE.write().unwrap(); +// state.grab_mode = mode; +// state.modified = true; +// } + +//Sets the cursor grab mode +// pub fn set_cursor_visible(mode: bool) { +// let mut state = CURSOR_STATE.write().unwrap(); +// state.visible = mode; +// state.modified = true; +// } + +///Defines behaviour of the cursor inside the window +#[derive(Clone, Copy)] +pub enum CursorState { + ///Cursor is locked in place + Locked, + ///Cursor is free + Free, +} + +struct CursorStateInternal { + grab_mode: CursorState, + lock_failed: bool, + visible: bool, + modified: bool, +} +static CURSOR_STATE: RwLock = RwLock::new(CursorStateInternal { + grab_mode: CursorState::Free, + lock_failed: false, + visible: true, + modified: false, +}); + +fn reset_cursor() { + let window = WINDOW.get().unwrap(); + + let pos = window.inner_size(); + if let Err(e) = window.set_cursor_position(PhysicalPosition { + x: pos.width / 2, + y: pos.height / 2, + }) { + log::error!("Failed to move cursor {e}"); + } +} + +pub(crate) fn process_cursor() { + let mut state = CURSOR_STATE.write().unwrap(); + + if matches!(state.grab_mode, CursorState::Locked) && state.lock_failed { + reset_cursor(); + } + + if !state.modified { + return; + } + state.modified = false; + let window = WINDOW.get().unwrap(); + + window.set_cursor_visible(state.visible); + + let g_mode = state.grab_mode; + let res = window.set_cursor_grab(match g_mode { + CursorState::Locked => CursorGrabMode::Locked, + CursorState::Free => CursorGrabMode::None, + }); + if let Err(e) = res { + match e { + winit::error::ExternalError::NotSupported(_) => { + //Once a lock has failed, it can never unfail, so no need to reset this + //afterwards :3 + // + //This can only unfail if the user changes platform, buuuut, i literally don't + //think there's a way that could happen + state.lock_failed = true; + drop(state); + + log::warn!("Failed to lock cursor, doing manually"); + if let Err(e) = window.set_cursor_grab(CursorGrabMode::Confined) { + log::error!("Cursor is fucked :3 {e}"); + } + reset_cursor(); + } + + winit::error::ExternalError::Ignored => { + log::warn!("Cursor state change ignored"); + } + winit::error::ExternalError::Os(e) => log::error!("Cursor state change error: {e}"), + } + } +} diff --git a/src/internal.rs b/src/internal.rs new file mode 100644 index 0000000..918da0a --- /dev/null +++ b/src/internal.rs @@ -0,0 +1,35 @@ +//!Internal globals. +//! +//!May also be used for custom assets, rendering, etc. +use std::sync::{OnceLock, RwLock}; + +#[cfg(target_arch = "wasm32")] +use crate::wrappers::WgpuWrapper; +use winit::dpi::PhysicalSize; + +#[cfg(target_arch = "wasm32")] +///Connection to a graphics device (usually a gpu) used for creating objects +pub static DEVICE: OnceLock> = OnceLock::new(); +#[cfg(target_arch = "wasm32")] +///Command queue of the device, used for submitting render and data transfer commands +pub static QUEUE: OnceLock> = OnceLock::new(); +#[cfg(not(target_arch = "wasm32"))] +///Connection to a graphics device (usually a gpu) used for creating objects +pub static DEVICE: OnceLock = OnceLock::new(); +#[cfg(not(target_arch = "wasm32"))] +///Command queue of the device, used for submitting render and data transfer commands +pub static QUEUE: std::sync::OnceLock = OnceLock::new(); +///Format of the frame buffer +pub static FORMAT: OnceLock = OnceLock::new(); + +#[cfg(target_arch = "wasm32")] +///Staging belt used for easier RW of data +pub static STAGING_BELT: OnceLock>> = OnceLock::new(); +#[cfg(not(target_arch = "wasm32"))] +///Staging belt used for easier RW of data +pub static STAGING_BELT: OnceLock> = OnceLock::new(); +///Resolution of the frame buffer +pub static RESOLUTION: RwLock> = RwLock::new(PhysicalSize { + width: 0, + height: 0, +}); diff --git a/src/lib.rs b/src/lib.rs index 0c2a582..3e576b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,7 @@ //! } //! ``` //! - +#![deny(missing_docs)] #![allow( clippy::needless_doctest_main, clippy::cast_possible_truncation, @@ -50,16 +50,13 @@ use std::{ }; use chrono::DateTime; +#[allow(clippy::wildcard_imports)] +use internal::*; use wgpu::SurfaceConfiguration; -use winit::{ - application::ApplicationHandler, - dpi::{PhysicalPosition, PhysicalSize}, - event, - window::CursorGrabMode, -}; +use winit::{application::ApplicationHandler, dpi::PhysicalSize, event}; #[cfg(target_arch = "wasm32")] -use wrappers::WgpuWrapper; +use crate::wrappers::WgpuWrapper; pub mod asset_managment; pub mod assets; @@ -69,36 +66,18 @@ mod grimoire; mod helpers; pub mod import; pub mod input; +pub mod internal; mod logging; pub mod math; +pub mod rendering; +///Various structures pub mod structures; -pub mod system; #[cfg(test)] mod test_utils; -pub mod windowing; +mod windowing; #[cfg(target_arch = "wasm32")] mod wrappers; -#[cfg(target_arch = "wasm32")] -pub static DEVICE: OnceLock> = OnceLock::new(); -#[cfg(target_arch = "wasm32")] -pub static QUEUE: OnceLock> = OnceLock::new(); - -#[cfg(not(target_arch = "wasm32"))] -pub static DEVICE: OnceLock = OnceLock::new(); -#[cfg(not(target_arch = "wasm32"))] -pub static QUEUE: OnceLock = OnceLock::new(); -pub static FORMAT: OnceLock = OnceLock::new(); - -#[cfg(target_arch = "wasm32")] -pub static STAGING_BELT: OnceLock>> = - OnceLock::new(); -#[cfg(not(target_arch = "wasm32"))] -pub static STAGING_BELT: OnceLock> = OnceLock::new(); -pub static RESOLUTION: RwLock> = RwLock::new(PhysicalSize { - width: 0, - height: 0, -}); //TODO find a better way than just staticing it static WINDOW: OnceLock = OnceLock::new(); @@ -115,87 +94,7 @@ static DEPTH: OnceLock> = OnceLock::new(); static QUIT: OnceLock = OnceLock::new(); static DELTA_TIME: RwLock = RwLock::new(0.01); -///Defines behaviour of the cursor inside the window -#[derive(Clone, Copy)] -pub enum CursorState { - //Cursor is locked to the window - Locked, - //Cursor is free - Free, -} - -struct CursorStateInternal { - grab_mode: CursorState, - lock_failed: bool, - visible: bool, - modified: bool, -} -static CURSOR_STATE: RwLock = RwLock::new(CursorStateInternal { - grab_mode: CursorState::Free, - lock_failed: false, - visible: true, - modified: false, -}); - -fn reset_cursor() { - let window = WINDOW.get().unwrap(); - - let pos = window.inner_size(); - if let Err(e) = window.set_cursor_position(PhysicalPosition { - x: pos.width / 2, - y: pos.height / 2, - }) { - log::error!("Failed to move cursor {e}"); - } -} - -fn process_cursor() { - let mut state = CURSOR_STATE.write().unwrap(); - - if matches!(state.grab_mode, CursorState::Locked) && state.lock_failed { - reset_cursor(); - } - - if !state.modified { - return; - } - state.modified = false; - let window = WINDOW.get().unwrap(); - - window.set_cursor_visible(state.visible); - - let g_mode = state.grab_mode; - let res = window.set_cursor_grab(match g_mode { - CursorState::Locked => CursorGrabMode::Locked, - CursorState::Free => CursorGrabMode::None, - }); - if let Err(e) = res { - match e { - winit::error::ExternalError::NotSupported(_) => { - //Once a lock has failed, it can never unfail, so no need to reset this - //afterwards :3 - // - //This can only unfail if the user changes platform, buuuut, i literally don't - //think there's a way that could happen - state.lock_failed = true; - drop(state); - - log::warn!("Failed to lock cursor, doing manually"); - if let Err(e) = window.set_cursor_grab(CursorGrabMode::Confined) { - log::error!("Cursor is fucked :3 {e}"); - } - reset_cursor(); - } - - winit::error::ExternalError::Ignored => { - log::warn!("Cursor state change ignored"); - } - winit::error::ExternalError::Os(e) => log::error!("Cursor state change error: {e}"), - } - } -} - -//Exits the application and closes the window +///Exits the application and closes the window pub fn quit() { QUIT.set(true).unwrap(); } @@ -205,20 +104,6 @@ pub fn delta_time() -> f32 { *DELTA_TIME.read().unwrap() } -//Sets the cursor grab mode -// pub fn set_cursor_grab_mode(mode: CursorState) { -// let mut state = CURSOR_STATE.write().unwrap(); -// state.grab_mode = mode; -// state.modified = true; -// } - -//Sets the cursor grab mode -// pub fn set_cursor_visible(mode: bool) { -// let mut state = CURSOR_STATE.write().unwrap(); -// state.visible = mode; -// state.modified = true; -// } - ///Contains main state of the app #[allow(clippy::type_complexity)] pub struct State { @@ -239,7 +124,7 @@ impl Default for State { surface_config: OnceCell::default(), contents: Default::default(), closed: Default::default(), - frame_start: Default::default(), + frame_start: None, init: None, run: None, end: None, @@ -286,7 +171,10 @@ impl State { } //Initialize logging first - logging::initialize_logging(); + + if logging::initialize_logging().is_err() { + log::warn!("Logger already initialized"); + } let event_loop = winit::event_loop::EventLoop::new().expect("Failed to create event loop"); log::debug!("Created event loop"); @@ -304,13 +192,8 @@ impl State { } } } - -impl ApplicationHandler for State { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.first_resume { - return; - } - +impl State { + fn initialize(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { #[cfg(not(target_arch = "wasm32"))] let attributes = winit::window::Window::default_attributes(); let window; @@ -383,6 +266,66 @@ impl ApplicationHandler for State { event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait); } + fn resize(&mut self, size: PhysicalSize) { + RESOLUTION.write().unwrap().width = size.width; + RESOLUTION.write().unwrap().height = size.height; + self.surface_config.get_mut().unwrap().width = size.width; + self.surface_config.get_mut().unwrap().height = size.height; + let device = DEVICE.get().unwrap(); + + SURFACE + .get() + .unwrap() + .write() + .unwrap() + .configure(device, self.surface_config.get().unwrap()); + let desc = windowing::get_depth_descriptor(size.width, size.height); + + #[cfg(target_arch = "wasm32")] + { + **DEPTH.get().unwrap().write().unwrap() = device.create_texture(&desc); + } + + #[cfg(not(target_arch = "wasm32"))] + { + *DEPTH.get().unwrap().write().unwrap() = device.create_texture(&desc); + } + } + + fn redraw(&mut self) { + //Frame time includes the wait between frames + if let Some(start) = self.frame_start { + let finish = chrono::Local::now(); + + let delta = (finish - start).abs().num_microseconds().unwrap() as f32 / 1_000_000.0; + + *DELTA_TIME.write().unwrap() = delta; + } + self.frame_start = Some(chrono::Local::now()); + + input::process_cursor(); + + if self.closed { + //This should be fine but needs further testing + self.end.take().unwrap()(&mut self.contents); + + return; + } + self.run.as_ref().unwrap()(&mut self.contents); + input::update(); + + WINDOW.get().unwrap().request_redraw(); + } +} + +impl ApplicationHandler for State { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if self.first_resume { + return; + } + self.initialize(event_loop); + } + fn window_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, @@ -390,71 +333,18 @@ impl ApplicationHandler for State { event: event::WindowEvent, ) { match event { - event::WindowEvent::Resized(size) => { - RESOLUTION.write().unwrap().width = size.width; - RESOLUTION.write().unwrap().height = size.height; - self.surface_config.get_mut().unwrap().width = size.width; - self.surface_config.get_mut().unwrap().height = size.height; - let device = DEVICE.get().unwrap(); - - SURFACE - .get() - .unwrap() - .write() - .unwrap() - .configure(device, self.surface_config.get().unwrap()); - let desc = windowing::get_depth_descriptor(size.width, size.height); - - #[cfg(target_arch = "wasm32")] - { - **DEPTH.get().unwrap().write().unwrap() = device.create_texture(&desc); - } - - #[cfg(not(target_arch = "wasm32"))] - { - *DEPTH.get().unwrap().write().unwrap() = device.create_texture(&desc); - } - - // let bpr = helpers::calculate_bpr(size.width, *FORMAT.get().unwrap()); - // self.screenshot_buffer = device.create_buffer(&wgpu::BufferDescriptor { - // label: Some("Screenshot buffer"), - // size: bpr * size.height as u64, - // usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - // mapped_at_creation: false, - // }); - } + event::WindowEvent::Resized(size) => self.resize(size), event::WindowEvent::CloseRequested => { event_loop.exit(); self.closed = true; } event::WindowEvent::RedrawRequested => { - //Frame time includes the wait between frames - if let Some(start) = self.frame_start { - let finish = chrono::Local::now(); - - let delta = - (finish - start).abs().num_microseconds().unwrap() as f32 / 1_000_000.0; - - *DELTA_TIME.write().unwrap() = delta; - } - self.frame_start = Some(chrono::Local::now()); - - process_cursor(); - if QUIT.get().is_some() { event_loop.exit(); self.closed = true; } - if self.closed { - //This should be fine but needs further testing - self.end.take().unwrap()(&mut self.contents); - - return; - } - self.run.as_ref().unwrap()(&mut self.contents); - input::update(); - WINDOW.get().unwrap().request_redraw(); + self.redraw(); } event::WindowEvent::KeyboardInput { device_id: _, @@ -492,7 +382,7 @@ impl ApplicationHandler for State { device_id: _, position, } => { - input::set_cursor_position(math::vec2::Vec2 { + input::set_cursor_position(math::Vec2 { x: position.x as f32, y: position.y as f32, }); diff --git a/src/math/mat4x4.rs b/src/math/mat4x4.rs index 3acdb9f..8d50bae 100644 --- a/src/math/mat4x4.rs +++ b/src/math/mat4x4.rs @@ -6,6 +6,7 @@ use crate::math::vec3::Vec3; use super::traits::Vector; +#[allow(missing_docs)] #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, bytemuck::Pod, bytemuck::Zeroable)] ///A 4 by 4 matrix of `f32` @@ -30,28 +31,17 @@ pub struct Mat4x4 { impl Default for Mat4x4 { fn default() -> Self { - Self { - m00: 1.0, - m01: Default::default(), - m02: Default::default(), - m03: Default::default(), - m10: Default::default(), - m11: 1.0, - m12: Default::default(), - m13: Default::default(), - m20: Default::default(), - m21: Default::default(), - m22: 1.0, - m23: Default::default(), - m30: Default::default(), - m31: Default::default(), - m32: Default::default(), - m33: 1.0, - } + Self::identity() } } -pub static IDENTITY: Mat4x4 = Mat4x4 { + +impl Mat4x4 { + +///Identity matrix +#[must_use] +pub const fn identity() -> Self { + Self { m00: 1.0, m01: 0.0, m02: 0.0, @@ -67,11 +57,10 @@ pub static IDENTITY: Mat4x4 = Mat4x4 { m30: 0.0, m31: 0.0, m32: 0.0, - m33: 1.0, -}; - -impl Mat4x4 { + m33: 1.0,} +} #[must_use] + ///Creates a new matrix with the given data pub const fn new( m00: f32, m01: f32, @@ -110,6 +99,7 @@ impl Mat4x4 { } } #[must_use] + ///Transposes the matrix, consuming it in the process pub const fn transpose(self) -> Self { Self { m00: self.m00, @@ -253,8 +243,8 @@ impl Mat4x4 { } #[must_use] - ///Returns the determinant of `self` - fn determinant(&self) -> f32 { + ///Returns the determinant of the matrix + pub fn determinant(&self) -> f32 { (self.m00 * self.m11 * self.m22).mul_add(self.m33, (self.m01 * self.m10 * self.m22).mul_add(-self.m33, (self.m00 * self.m12 * self.m21).mul_add(-self.m33, (self.m02 * self.m10 * self.m21).mul_add(self.m33, (self.m01 * self.m12 * self.m20).mul_add(self.m33, (self.m02 * self.m11 * self.m20).mul_add(-self.m33, (self.m00 * self.m11 * self.m23).mul_add(-self.m32, (self.m01 * self.m10 * self.m23).mul_add(self.m32, (self.m00 * self.m13 * self.m21).mul_add(self.m32, (self.m03 * self.m10 * self.m21).mul_add(-self.m32, (self.m01 * self.m13 * self.m20).mul_add(-self.m32, (self.m03 * self.m11 * self.m20).mul_add( self.m32, (self.m00 * self.m12 * self.m23).mul_add( self.m31, @@ -293,14 +283,16 @@ impl Mat4x4 { )))))))))))) } - fn trace(&self) -> f32 { + ///Returns the trace of the matrix + #[must_use] + pub fn trace(&self) -> f32 { self.m00 + self.m11 + self.m22 + self.m33 } #[must_use] ///Inverts the matrix does not consume the matrix ///Returns None if the Matrix can not be inverted i.e. if the determenant is equal to zero - fn inverted(&self) -> Option { + pub fn inverted(&self) -> Option { let det = self.determinant(); if det == 0.0 { return None; @@ -310,7 +302,7 @@ impl Mat4x4 { let cubed = squared * *self; let trace = self.trace(); - let a = IDENTITY + let a = Self::identity() * (0.166_667 * 2.0f32.mul_add( cubed.trace(), @@ -325,7 +317,7 @@ impl Mat4x4 { #[must_use] ///Inverts the matrix consuming it the process ///Returns None if the Matrix can not be inverted i.e. if the determenant is equal to zero - fn invert(self) -> Option { + pub fn invert(self) -> Option { let det = self.determinant(); if det == 0.0 { return None; @@ -335,7 +327,7 @@ impl Mat4x4 { let cubed = squared * self; let trace = self.trace(); - let a = IDENTITY + let a = Self::identity() * (0.166_667 * 2.0f32.mul_add( cubed.trace(), @@ -399,7 +391,7 @@ impl Mat4x4 { ///Creates a rotation matrix for the given euler angles pub fn rotation_matrix_euler(rotation: &Vec3) -> Self { if rotation.x == 0.0 && rotation.y == 0.0 && rotation.z == 0.0 { - return IDENTITY; + return Self::identity(); } let sin_x = rotation.x.to_radians().sin(); @@ -552,147 +544,3 @@ impl Mul for Mat4x4 { self.transform(rhs) } } - -#[test] -fn test_matrix_float_mul() { - let a = Mat4x4::new( - 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 b = 2.0; - - let c = a * b; - - assert_eq!( - c, - Mat4x4::new( - 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0, 28.0, 30.0, - 32.0 - ) - ); -} - -#[test] -fn test_transformation() { - let a = IDENTITY; - let b = Vec4::new(1.0, 2.0, 3.0, 4.0); - let c = a.transform(b); - - assert_eq!(c, b); - - let a = Mat4x4::new( - 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 c = a.transform(b); - - assert_eq!(c, Vec4::new(30.0, 70.0, 110.0, 150.0)); -} - -#[test] -fn test_mat_mul_1() { - let a = Mat4x4::new( - 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 b = Mat4x4::new( - 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, 17.0, - ); - - let o = a.multiply(b); - let expected = Mat4x4::new( - 100.0, 110.0, 120.0, 130.0, 228.0, 254.0, 280.0, 306.0, 356.0, 398.0, 440.0, 482.0, 484.0, - 542.0, 600.0, 658.0, - ); - assert_eq!(o, expected); -} - -#[test] -fn test_mat_identity_mul() { - let a = Mat4x4::new( - 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 o = a.multiply(Mat4x4::default()); - let expected = Mat4x4::new( - 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, - ); - assert_eq!(o, expected); -} - -#[test] -fn test_mat_mat_mul() { - let a = Mat4x4::new( - 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 o = a.multiply(a); - let expected = Mat4x4::new( - 90.0, 100.0, 110.0, 120.0, 202.0, 228.0, 254.0, 280.0, 314.0, 356.0, 398.0, 440.0, 426.0, - 484.0, 542.0, 600.0, - ); - assert_eq!(o, expected); -} - -#[test] -fn test_determinant() { - let a = Mat4x4::new( - 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 o = a.determinant(); - let expected = 0.0; - - assert_eq!(o, expected); - - let a = Mat4x4::new( - 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 0.0, 0.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, - ); - - let o = a.determinant(); - let expected = -80.0; - assert_eq!(o, expected); -} - -#[test] -fn test_transpose() { - let a = Mat4x4::new( - 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 o = a.transpose(); - let expected = Mat4x4{ - m00: 1.0, m01: 5.0, m02: 9.0, m03: 13.0, - m10: 2.0, m11: 6.0, m12: 10.0, m13: 14.0, - m20: 3.0, m21: 7.0, m22: 11.0, m23: 15.0, - m30: 4.0, m31: 8.0, m32: 12.0, m33: 16.0, - }; - assert_eq!(o, expected); - - let a = Mat4x4::new( - 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 0.0, 0.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, - ); - - let o = a.determinant(); - let expected = -80.0; - assert_eq!(o, expected); -} -// #[test] -// fn test_inversion() { -// let a = Mat4x4::new( -// 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 o = a.inverted(); -// let expected = None; - -// assert_eq!(o, expected); - -// let a = Mat4x4::new( -// 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 0.0, 0.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, -// ); - -// let o = a.inverted().unwrap() * a; -// let expected = IDENTITY; -// assert_eq!(o, expected); -// assert_ -// } diff --git a/src/math/mod.rs b/src/math/mod.rs index 3566958..db9eb24 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,6 +1,17 @@ -pub mod mat4x4; -pub mod quaternion; -pub mod traits; -pub mod vec2; -pub mod vec3; -pub mod vec4; +//! The math library +//! +//! Contains implementations of vectors with length 2,3,4 and 4x4 matrices +mod mat4x4; +mod quaternion; +#[cfg(test)] +mod tests; +mod traits; +mod vec2; +mod vec3; +mod vec4; + +pub use mat4x4::Mat4x4; +pub use traits::Vector; +pub use vec2::Vec2; +pub use vec3::Vec3; +pub use vec4::Vec4; diff --git a/src/math/tests.rs b/src/math/tests.rs new file mode 100644 index 0000000..a294ea9 --- /dev/null +++ b/src/math/tests.rs @@ -0,0 +1,208 @@ +use super::*; + +#[test] +fn test_matrix_float_mul() { + let a = Mat4x4::new( + 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 b = 2.0; + + let c = a * b; + + assert_eq!( + c, + Mat4x4::new( + 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0, 28.0, 30.0, + 32.0 + ) + ); +} + +#[test] +fn test_transformation() { + let a = Mat4x4::identity(); + let b = Vec4::new(1.0, 2.0, 3.0, 4.0); + let c = a.transform(b); + + assert_eq!(c, b); + + let a = Mat4x4::new( + 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 c = a.transform(b); + + assert_eq!(c, Vec4::new(30.0, 70.0, 110.0, 150.0)); +} + +#[test] +fn test_mat_mul_1() { + let a = Mat4x4::new( + 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 b = Mat4x4::new( + 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, 17.0, + ); + + let o = a.multiply(b); + let expected = Mat4x4::new( + 100.0, 110.0, 120.0, 130.0, 228.0, 254.0, 280.0, 306.0, 356.0, 398.0, 440.0, 482.0, 484.0, + 542.0, 600.0, 658.0, + ); + assert_eq!(o, expected); +} + +#[test] +fn test_mat_identity_mul() { + let a = Mat4x4::new( + 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 o = a.multiply(Mat4x4::default()); + let expected = Mat4x4::new( + 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, + ); + assert_eq!(o, expected); +} + +#[test] +fn test_mat_mat_mul() { + let a = Mat4x4::new( + 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 o = a.multiply(a); + let expected = Mat4x4::new( + 90.0, 100.0, 110.0, 120.0, 202.0, 228.0, 254.0, 280.0, 314.0, 356.0, 398.0, 440.0, 426.0, + 484.0, 542.0, 600.0, + ); + assert_eq!(o, expected); +} + +#[test] +fn test_determinant() { + let a = Mat4x4::new( + 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 o = a.determinant(); + let expected = 0.0; + + assert_eq!(o, expected); + + let a = Mat4x4::new( + 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 0.0, 0.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + ); + + let o = a.determinant(); + let expected = -80.0; + assert_eq!(o, expected); +} + +#[test] +fn test_transpose() { + let a = Mat4x4::new( + 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 o = a.transpose(); + let expected = Mat4x4 { + m00: 1.0, + m01: 5.0, + m02: 9.0, + m03: 13.0, + m10: 2.0, + m11: 6.0, + m12: 10.0, + m13: 14.0, + m20: 3.0, + m21: 7.0, + m22: 11.0, + m23: 15.0, + m30: 4.0, + m31: 8.0, + m32: 12.0, + m33: 16.0, + }; + assert_eq!(o, expected); + + let a = Mat4x4::new( + 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 0.0, 0.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + ); + + let o = a.determinant(); + let expected = -80.0; + assert_eq!(o, expected); +} + +#[test] +fn test_vec2_dot_product() { + let a = Vec2::new(1.0, 0.0); + let b = Vec2::new(0.0, 1.0); + + let expected = 0.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); + let a = Vec2::new(1.0, 0.0); + let b = Vec2::new(1.0, 0.0); + + let expected = 1.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); +} + +#[test] +fn test_vec2_length() { + let a = Vec2::new(1.0, 2.0); + assert_eq!(a.square_length(), 5.0); +} + +#[test] +fn test_vec3_dot_product() { + let a = Vec3::new(1.0, 0.0, 0.0); + let b = Vec3::new(0.0, 1.0, 0.0); + + let expected = 0.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); + let a = Vec3::new(1.0, 0.0, 0.0); + let b = Vec3::new(1.0, 0.0, 0.0); + + let expected = 1.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); +} + +#[test] +fn test_vec3_length() { + let a = Vec3::new(1.0, 2.0, 2.0); + assert_eq!(a.square_length(), 9.0); + assert_eq!(a.length(), 3.0); +} + +#[test] +fn test_vec4_dot_product() { + let a = Vec4::new(1.0, 0.0, 0.0, 0.0); + let b = Vec4::new(0.0, 1.0, 0.0, 0.0); + + let expected = 0.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); + let a = Vec4::new(1.0, 0.0, 0.0, 0.0); + let b = Vec4::new(1.0, 0.0, 0.0, 0.0); + + let expected = 1.0; + let result = a.dot_product(&b); + + assert_eq!(expected, result); +} + +#[test] +fn test_vec4_length() { + let a = Vec4::new(1.0, 2.0, 2.0, 0.0); + assert_eq!(a.square_length(), 9.0); + assert_eq!(a.length(), 3.0); +} diff --git a/src/math/traits.rs b/src/math/traits.rs index b1c9c52..50ff7a4 100644 --- a/src/math/traits.rs +++ b/src/math/traits.rs @@ -1,5 +1,6 @@ use std::ops::Div; +///Trait all vectors must implement pub trait Vector: Div + Sized + Copy { ///Returns squared length of the vector, much faster than `length()` fn square_length(&self) -> f32; diff --git a/src/math/vec2.rs b/src/math/vec2.rs index 2d595bf..aa13c56 100644 --- a/src/math/vec2.rs +++ b/src/math/vec2.rs @@ -5,6 +5,7 @@ use bytemuck::{Pod, Zeroable}; pub use crate::math::traits::Vector; #[repr(C)] +#[allow(missing_docs)] #[derive(Clone, Copy, Default, Debug, PartialEq, PartialOrd, Pod, Zeroable)] ///A generic vector with 2 dimensions pub struct Vec2 { @@ -14,6 +15,7 @@ pub struct Vec2 { impl Vec2 { #[must_use] + ///Creates a new vector pub const fn new(x: f32, y: f32) -> Self { Self { x, y } } @@ -68,27 +70,3 @@ impl From for Vec2 { Self { x: value, y: value } } } - -#[test] -fn test_vec2_dot_product() { - let a = Vec2::new(1.0, 0.0); - let b = Vec2::new(0.0, 1.0); - - let expected = 0.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); - let a = Vec2::new(1.0, 0.0); - let b = Vec2::new(1.0, 0.0); - - let expected = 1.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); -} - -#[test] -fn test_vec2_length() { - let a = Vec2::new(1.0, 2.0); - assert_eq!(a.square_length(), 5.0); -} diff --git a/src/math/vec3.rs b/src/math/vec3.rs index 5a9da95..3f52af7 100644 --- a/src/math/vec3.rs +++ b/src/math/vec3.rs @@ -6,6 +6,7 @@ use rand::Rng; pub use crate::math::traits::Vector; #[repr(C)] +#[allow(missing_docs)] #[derive(Clone, Copy, Default, Debug, PartialEq, PartialOrd, Pod, Zeroable)] ///A generic vector with 3 dimensions pub struct Vec3 { @@ -16,10 +17,12 @@ pub struct Vec3 { impl Vec3 { #[must_use] + ///Creates a new vector pub const fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z } } #[must_use] + ///Cross product of the vector and another vector pub fn cross(&self, other: &Self) -> Self { Self::new( self.y.mul_add(other.z, -self.z * other.y), @@ -28,6 +31,7 @@ impl Vec3 { ) } #[must_use] + ///Creates a random vector with values being in the given range pub fn random(min: f32, max: f32) -> Self { let mut random = rand::thread_rng(); Self { @@ -98,28 +102,3 @@ impl From for Vec3 { } } } - -#[test] -fn test_vec3_dot_product() { - let a = Vec3::new(1.0, 0.0, 0.0); - let b = Vec3::new(0.0, 1.0, 0.0); - - let expected = 0.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); - let a = Vec3::new(1.0, 0.0, 0.0); - let b = Vec3::new(1.0, 0.0, 0.0); - - let expected = 1.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); -} - -#[test] -fn test_vec3_length() { - let a = Vec3::new(1.0, 2.0, 2.0); - assert_eq!(a.square_length(), 9.0); - assert_eq!(a.length(), 3.0); -} diff --git a/src/math/vec4.rs b/src/math/vec4.rs index 2326fa2..b35674e 100644 --- a/src/math/vec4.rs +++ b/src/math/vec4.rs @@ -7,6 +7,7 @@ pub use crate::math::traits::Vector; use super::vec3::Vec3; #[repr(C)] +#[allow(missing_docs)] #[derive(Clone, Copy, Default, Debug, PartialEq, PartialOrd, Pod, Zeroable)] ///A generic vector with 4 dimensions pub struct Vec4 { @@ -18,11 +19,13 @@ pub struct Vec4 { impl Vec4 { #[must_use] + ///Creates a new vector pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { Self { x, y, z, w } } #[must_use] + ///Returns the x,y,z as a [`Vec3`] pub const fn xyz(self) -> Vec3 { Vec3 { x: self.x, @@ -121,28 +124,3 @@ impl From for Vec4 { } } } - -#[test] -fn test_vec4_dot_product() { - let a = Vec4::new(1.0, 0.0, 0.0, 0.0); - let b = Vec4::new(0.0, 1.0, 0.0, 0.0); - - let expected = 0.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); - let a = Vec4::new(1.0, 0.0, 0.0, 0.0); - let b = Vec4::new(1.0, 0.0, 0.0, 0.0); - - let expected = 1.0; - let result = a.dot_product(&b); - - assert_eq!(expected, result); -} - -#[test] -fn test_vec4_length() { - let a = Vec4::new(1.0, 2.0, 2.0, 0.0); - assert_eq!(a.square_length(), 9.0); - assert_eq!(a.length(), 3.0); -} diff --git a/src/system/rendering/extensions.rs b/src/rendering/extensions.rs similarity index 91% rename from src/system/rendering/extensions.rs rename to src/rendering/extensions.rs index 5372244..24380da 100644 --- a/src/system/rendering/extensions.rs +++ b/src/rendering/extensions.rs @@ -14,8 +14,11 @@ use crate::{ DEVICE, STAGING_BELT, }; +///A color buffer and a depth stencil buffer pub struct AttachmentData { + ///Color buffer pub color: wgpu::TextureView, + ///Depth stencil buffer pub depth_stencil: wgpu::TextureView, } @@ -23,6 +26,7 @@ pub struct AttachmentData { /// ///Allows for extending the renderer pub trait RenderingExtension { + ///Uses the extension to render scene fn render( &mut self, encoder: &mut wgpu::CommandEncoder, @@ -31,12 +35,13 @@ pub trait RenderingExtension { attachments: &AttachmentData, ); - fn get_order(&self) -> u32; + ///Returns the priority of the extension, extensions with smaller priorities are rendered first. + fn get_priority(&self) -> u32; } impl std::cmp::PartialEq for dyn RenderingExtension { fn eq(&self, other: &Self) -> bool { - self.get_order().eq(&other.get_order()) + self.get_priority().eq(&other.get_priority()) } } @@ -50,13 +55,33 @@ impl std::cmp::PartialOrd for dyn RenderingExtension { impl std::cmp::Ord for dyn RenderingExtension { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.get_order().cmp(&other.get_order()) + self.get_priority().cmp(&other.get_priority()) } } #[derive(Default)] +///Basic renderer that renders all [`crate::components::mesh::Mesh`] components +/// +///# Usage +///``` +///# use lunar_engine::rendering::extensions::Base; +///# use lunar_engine::rendering::render; +///# struct State {world: lunar_engine::ecs::World, assets: lunar_engine::asset_managment::AssetStore, extension: Base} +/// +///fn init(state: &mut State) { +/// state.extension = Base::default(); +///} +/// +///fn update(state: &mut State) { +/// render( +/// &state.world, +/// &state.assets, +/// &mut [&mut state.extension] +/// ); +///} +///``` pub struct Base { - order: u32, + priority: u32, //Stores vector of (mesh_id, material_id) for caching identifier: Vec<(u128, u128)>, v_buffers: Vec, @@ -67,9 +92,10 @@ pub struct Base { impl Base { #[must_use] + ///Creates a new [`Base`] pub const fn new(order: u32) -> Self { Self { - order, + priority: order, identifier: Vec::new(), v_buffers: Vec::new(), mesh_materials: Vec::new(), @@ -219,7 +245,7 @@ impl RenderingExtension for Base { old = m.1 .1; } } - //Again insure at least one window + //Again ensure there's at least one window material_split_points.push(current_window.len()); let mut last = MeshMaterial { @@ -391,7 +417,7 @@ impl RenderingExtension for Base { drop(render_pass); } - fn get_order(&self) -> u32 { - self.order + fn get_priority(&self) -> u32 { + self.priority } } diff --git a/src/system/rendering/mod.rs b/src/rendering/mod.rs similarity index 87% rename from src/system/rendering/mod.rs rename to src/rendering/mod.rs index 91f35ef..e3ebe49 100644 --- a/src/system/rendering/mod.rs +++ b/src/rendering/mod.rs @@ -1,14 +1,13 @@ -//! Contains the rendering system, used with the ecs +//! The rendering system, used with the ecs //! //! # Rendering system //! //! A scene consists of a world and an asset store. +//! The asset store SHOULD consists only of the assets needed for the scene, however they MAY +//! contain other assets. //! -//! The asset store consists ONLY of the assets needed for the scene, nothing else. -//! -//! The render function accepts a world and an asset store -//! -//! The rendering function gets the asset ids and queries them from the store +//! The render function accepts a world and an asset store. +//! The rendering function gets the asset ids and queries them from the store. use log::trace; @@ -18,6 +17,7 @@ use crate::{ use self::extensions::{AttachmentData, RenderingExtension}; +///System for making custom renderers for objects, also contains implemented rendering extensions pub mod extensions; ///Renders all the entities in the world diff --git a/src/structures.rs b/src/structures.rs index a9fe178..2a638c3 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -1,34 +1,51 @@ use bytemuck::{Pod, Zeroable}; -use crate::math::{vec2::Vec2, vec3::Vec3, vec4::Vec4}; +use crate::math::{Vec2, Vec3, Vec4}; #[repr(C)] #[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Pod, Zeroable)] +///Representation of a vertex in a mesh pub struct Vertex { + ///Coordinate in the 3d space pub coords: Vec4, + ///Textrure coordinates pub texture: Vec2, + ///Normal direction pub normal: Vec3, } +///Indecies of a mesh pub type Index = u32; #[repr(C)] #[derive(Clone, Debug, Default, PartialEq, PartialOrd)] +///Mesh data pub struct Mesh { + ///Vertices of the mesh pub vertices: Vec, + ///Indecies of the mesh pub indecies: Vec, } #[repr(C)] #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] +///Pixel of an image pub struct Pixel { + ///Red pub r: u8, + ///Green pub g: u8, + ///Blue pub b: u8, + ///Alpha pub a: u8, } +///Image with some metadata pub struct Image { + ///Width of the image pub width: u32, + ///Height of the image pub height: u32, + ///Image data in scan lines going left to right top to bottom pub data: Vec, } diff --git a/src/system/mod.rs b/src/system/mod.rs deleted file mode 100644 index 2d00670..0000000 --- a/src/system/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rendering; diff --git a/src/windowing.rs b/src/windowing.rs index 3413cfb..c9edcb6 100644 --- a/src/windowing.rs +++ b/src/windowing.rs @@ -5,7 +5,7 @@ use vec_key_value_pair::map::VecMap; use wgpu::{util::StagingBelt, Backends, Surface, SurfaceConfiguration, Texture}; use winit::window::Window; -use crate::{input::InputState, math::vec2::Vec2, DEVICE, FORMAT, QUEUE, RESOLUTION, STAGING_BELT}; +use crate::{input::InputState, math::Vec2, DEVICE, FORMAT, QUEUE, RESOLUTION, STAGING_BELT}; pub fn initialize_gpu(window: &Window) -> (Surface, SurfaceConfiguration, Texture) { let mut size = window.inner_size(); @@ -169,7 +169,7 @@ async fn req_device<'a>( adapter.request_device(descriptor, None).await } -pub(crate) fn get_depth_descriptor<'a>(width: u32, height: u32) -> wgpu::TextureDescriptor<'a> { +pub fn get_depth_descriptor<'a>(width: u32, height: u32) -> wgpu::TextureDescriptor<'a> { wgpu::TextureDescriptor { label: Some("Depth stencil"), size: wgpu::Extent3d {