diff --git a/.gitignore b/.gitignore index 9d03560..164bcbd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tools res resources/*.png crates/blue_engine_core/Cargo.lock +**dev** \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 39432ca..22099b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,15 +160,6 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arbitrary" version = "1.4.1" @@ -307,7 +298,7 @@ dependencies = [ [[package]] name = "blue_engine" -version = "0.6.5" +version = "0.7.0" dependencies = [ "blue_engine_core", "blue_engine_dynamic", @@ -315,17 +306,17 @@ dependencies = [ [[package]] name = "blue_engine_core" -version = "0.6.5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b19075a86cbddc80275bab8f131f5c3fca21a4ec6f100f64637213bc18f60e" +checksum = "45baa33b4181e0993a4ea441a7c3c815074daa1f82e063afc6d0d2af2121d480" dependencies = [ "android_logger", "bytemuck", "downcast", "env_logger", + "glam", "image", "log", - "nalgebra-glm", "pollster", "thiserror 2.0.9", "wgpu", @@ -334,9 +325,9 @@ dependencies = [ [[package]] name = "blue_engine_dynamic" -version = "0.6.5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86550ddc6fb6455656ebe19545929b0e458e17a8a764d9c9a636d0c20e7f8665" +checksum = "8c4e111620ed6065959985e399e31349e583abf62d902fbd977d670d849c738d" dependencies = [ "blue_engine_core", ] @@ -779,6 +770,16 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glam" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9" +dependencies = [ + "bytemuck", + "serde", +] + [[package]] name = "glow" version = "0.16.0" @@ -1122,16 +1123,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matrixmultiply" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "maybe-rayon" version = "0.1.1" @@ -1210,33 +1201,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "nalgebra" -version = "0.33.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" -dependencies = [ - "approx", - "matrixmultiply", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", -] - -[[package]] -name = "nalgebra-glm" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e441f43bccdf40cb6bd4294321e6983c5bc7b9886112d19fd4c9813976b117e4" -dependencies = [ - "approx", - "nalgebra", - "num-traits", - "simba", -] - [[package]] name = "ndk" version = "0.9.0" @@ -1308,15 +1272,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-derive" version = "0.4.2" @@ -1901,12 +1856,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "rayon" version = "1.10.0" @@ -2011,15 +1960,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "same-file" version = "1.0.6" @@ -2089,19 +2029,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "simba" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", - "wide", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -2389,12 +2316,6 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-ident" version = "1.0.14" @@ -2668,9 +2589,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "24.0.1" +version = "24.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f55718f85c2fa756edffa0e7f0e0a60aba463d1362b57e23123c58f035e4b6" +checksum = "35904fb00ba2d2e0a4d002fcbbb6e1b89b574d272a50e5fc95f6e81cf281c245" dependencies = [ "arrayvec", "bitflags 2.9.0", @@ -2775,16 +2696,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wide" -version = "0.7.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "winapi-util" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index ce4125d..8c65b65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "blue_engine" -version = "0.6.5" +version = "0.7.0" authors = ["Elham Aryanpur "] edition = "2024" description = "General-Purpose, Easy-to-use, Fast, and Portable graphics engine" -documentation = "https://aryanpurtech.github.io/BlueEngineDocs/" +documentation = "https://docs.rs/blue_engine" repository = "https://github.com/AryanpurTech/BlueEngine" keywords = ["gamedev", "graphics", "3D", "2D", "rendering"] categories = ["game-development", "gui", "graphics", "rendering"] @@ -15,7 +15,7 @@ exclude = ["/examples", "/resources"] name = "blue_engine" [features] -default = ["debug", "u16", "dep:blue_engine_core"] +default = ["debug", "dep:blue_engine_core"] # Use the default engine dynamic_link = ["dep:blue_engine_dynamic"] @@ -29,18 +29,16 @@ android_game_activity = [ "blue_engine_core?/android_game_activity", "blue_engine_dynamic?/android_game_activity", ] -# using u16 for indices and others -u16 = ["blue_engine_core?/u16", "blue_engine_dynamic?/u16"] # using u32 for indices and others u32 = ["blue_engine_core?/u32", "blue_engine_dynamic?/u32"] [dependencies] -blue_engine_core = { version = "0.6.5", optional = true } +blue_engine_core = { version = "0.7.0", optional = true } # blue_engine_core = { path = "crates/blue_engine_core", optional = true } # Wasm does not support dynamic linking. [target.'cfg(not(target_family = "wasm"))'.dependencies] -blue_engine_dynamic = { version = "0.6.5", optional = true } +blue_engine_dynamic = { version = "0.7.0", optional = true } # blue_engine_dynamic = { path = "crates/blue_engine_dynamic", optional = true } # ========== EXAMPLES ========== # diff --git a/README.md b/README.md index decd688..faceada 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Hello World: ```rust use blue_engine::{ - header::{ Engine, ObjectSettings }, + prelude::{ Engine, ObjectSettings }, primitive_shapes::triangle }; @@ -33,12 +33,12 @@ fn main() { } ``` -* [WIP] [Guide](https://aryanpurtech.github.io/BlueEngineDocs/) +- [WIP] [Guide](https://aryanpurtech.github.io/BlueEngineDocs/) -* Check out the [examples](https://github.com/AryanpurTech/BlueEngine/tree/master/examples) folder to get a sense of how things are done +- Check out the [examples](https://github.com/AryanpurTech/BlueEngine/tree/master/examples) folder to get a sense of how things are done -* Check out the [utilities library](https://github.com/AryanpurTech/BlueEngineUtilities) for extra functionality with the engine +- Check out the [utilities library](https://github.com/AryanpurTech/BlueEngineUtilities) for extra functionality with the engine -*the credits to the image on top: NotPB* +_the credits to the image on top: NotPB_ -*the development might seem slow sometimes, its due to multiple repositories being handled and due to my education taking a large chunk of my time. The project isn't dead, just slow.* +_the development might seem slow sometimes, its due to multiple repositories being handled and due to my education taking a large chunk of my time. The project isn't dead, just slow._ diff --git a/crates/blue_engine_core/Cargo.toml b/crates/blue_engine_core/Cargo.toml index 9acc025..e8f53e2 100644 --- a/crates/blue_engine_core/Cargo.toml +++ b/crates/blue_engine_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blue_engine_core" -version = "0.6.5" +version = "0.7.0" authors = ["Elham Aryanpur "] edition = "2024" description = "USE blue_engine THIS IS FOR INTERNAL USE" @@ -10,30 +10,30 @@ license = "Apache-2.0" name = "blue_engine_core" [features] -default = ["debug", "u16"] +default = ["debug"] debug = ["dep:env_logger"] android = ["dep:log", "dep:android_logger"] android_native_activity = ["winit/android-native-activity"] android_game_activity = ["winit/android-game-activity"] -# using u16 for indices and others -u16 = [] # using u32 for indices and others u32 = [] +glam_fast_math = ["glam/fast-math"] + [dependencies] -image = { version = "0.25.2" } +winit = { version = "0.30", features = ["rwh_06"] } +wgpu = { version = "24.0.3" } +image = { version = "0.25" } pollster = "0.4" -winit = { version = "0.30.5", features = ["rwh_06"] } -wgpu = { version = "24.0.1" } bytemuck = { version = "1.16", features = ["derive"] } downcast = "0.11" -nalgebra-glm = "0.19" -# debug logs +thiserror = "2.0" env_logger = { version = "0.11", optional = true } +glam = { version = "0.30.1", features = ["bytemuck", "serde"] } + # android log = { version = "0.4", optional = true } android_logger = { version = "0.15.0", optional = true } -thiserror = "2.0.9" [target.'cfg(target_arch = "wasm32")'.dependencies] -wgpu = { version = "24.0.1", features = ["webgl"] } +wgpu = { version = "24.0.3", features = ["webgl"] } diff --git a/crates/blue_engine_core/src/definition.rs b/crates/blue_engine_core/src/definition.rs index c813026..1d66499 100644 --- a/crates/blue_engine_core/src/definition.rs +++ b/crates/blue_engine_core/src/definition.rs @@ -9,13 +9,159 @@ use wgpu::{BindGroupLayout, Sampler, Texture, TextureView, util::DeviceExt}; use crate::{ InstanceRaw, UnsignedIntType, - header::{ - Pipeline, PipelineData, ShaderSettings, Shaders, StringBuffer, TextureData, TextureMode, - Textures, UniformBuffers, Vertex, VertexBuffers, - }, + prelude::{Shaders, StringBuffer, Textures, UniformBuffers, Vertex}, }; -impl crate::header::Renderer { +/// Container for pipeline values. Each pipeline takes only 1 vertex shader, +/// 1 fragment shader, 1 texture data, and optionally a vector of uniform data. +#[derive(Debug)] +pub struct Pipeline { + /// the shader buffer that's sent to the gpu + pub shader: PipelineData, + /// The vertex buffer that's sent to the gpu. This includes indices as well + pub vertex_buffer: PipelineData, + /// The texture that's sent to the gpu. + pub texture: PipelineData, + /// the Uniform buffers that are sent to the gpu + pub uniform: PipelineData>, +} +unsafe impl Send for Pipeline {} +unsafe impl Sync for Pipeline {} + +/// Container for pipeline data. Allows for sharing resources with other objects +#[derive(Debug)] +pub enum PipelineData { + /// No data, just a reference to a buffer + Copy(String), + /// The actual data + Data(T), +} + +/// Container for vertex and index buffer +#[derive(Debug)] +pub struct VertexBuffers { + /// An array of vertices. A vertex is a point in 3D space containing + /// an X, Y, and a Z coordinate between -1 and +1 + pub vertex_buffer: wgpu::Buffer, + /// An array of indices. Indices are a way to reuse vertices, + /// this in turn helps greatly in reduction of amount of vertices needed to be sent to the GPU + pub index_buffer: wgpu::Buffer, + /// The length of the vertex buffer + pub length: u32, +} +unsafe impl Send for VertexBuffers {} +unsafe impl Sync for VertexBuffers {} + +/// Defines how the texture data is +#[derive(Debug, Clone)] +pub enum TextureData { + /// the texture file bytes directly + Bytes(Vec), + /// the texture as a [`image::DynamicImage`] + Image(image::DynamicImage), + /// path to a texture file to load + Path(String), +} +unsafe impl Send for TextureData {} +unsafe impl Sync for TextureData {} + +/// Defines how the borders of texture would look like +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TextureMode { + /// Expands the texture to fit the object + Clamp, + /// Repeats the texture instead of stretching + Repeat, + /// Repeats the texture, but mirrors it on edges + MirrorRepeat, +} +unsafe impl Send for TextureMode {} +unsafe impl Sync for TextureMode {} + +/// These definitions are taken from wgpu API docs +#[derive(Debug, Clone, Copy)] +pub struct ShaderSettings { + // ===== PRIMITIVE ===== // + /// The primitive topology used to interpret vertices + pub topology: crate::ShaderPrimitive, + /// When drawing strip topologies with indices, this is the + /// required format for the index buffer. This has no effect + /// on non-indexed or non-strip draws. + pub strip_index_format: Option, + /// The face to consider the front for the purpose of + /// culling and stencil operations. + pub front_face: crate::FrontFace, + /// The face culling mode + pub cull_mode: Option, + /// Controls the way each polygon is rasterized. Can be + /// either `Fill` (default), `Line` or `Point` + /// + /// Setting this to something other than `Fill` requires + /// `NON_FILL_POLYGON_MODE` feature to be enabled + pub polygon_mode: crate::PolygonMode, + /// If set to true, the polygon depth is clamped to 0-1 + /// range instead of being clipped. + /// + /// Enabling this requires the `DEPTH_CLAMPING` feature + /// to be enabled + pub clamp_depth: bool, + /// If set to true, the primitives are rendered with + /// conservative overestimation. I.e. any rastered + /// pixel touched by it is filled. Only valid for PolygonMode::Fill! + /// + /// Enabling this requires `CONSERVATIVE_RASTERIZATION` + /// features to be enabled. + pub conservative: bool, + // ===== Multisample ===== // + /// The number of samples calculated per pixel (for MSAA). + /// For non-multisampled textures, this should be `1` + pub count: u32, + /// Bitmask that restricts the samples of a pixel modified + /// by this pipeline. All samples can be enabled using the + /// value `!0` + pub mask: u64, + /// When enabled, produces another sample mask per pixel + /// based on the alpha output value, that is ANDead with the + /// sample_mask and the primitive coverage to restrict the + /// set of samples affected by a primitive. + + /// The implicit mask produced for alpha of zero is guaranteed + /// to be zero, and for alpha of one is guaranteed to be all + /// 1-s. + pub alpha_to_coverage_enabled: bool, +} +impl Default for ShaderSettings { + fn default() -> Self { + Self { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + clamp_depth: false, + conservative: false, + count: 1, + mask: !0, + alpha_to_coverage_enabled: true, + } + } +} +unsafe impl Send for ShaderSettings {} +unsafe impl Sync for ShaderSettings {} + +/// This function helps in converting pixel value to the value that is between -1 and +1 +pub fn pixel_to_cartesian(value: f32, max: u32) -> f32 { + let mut result = value / max as f32; + + if value == max as f32 { + result = 0.0; + } else if result < max as f32 / 2.0 { + } + + if result > -1.0 { result } else { -1.0 } +} + +impl crate::prelude::Renderer { /// Creates a new render pipeline. Could be thought of as like materials in game engines. pub fn build_pipeline( &mut self, diff --git a/crates/blue_engine_core/src/header.rs b/crates/blue_engine_core/src/header.rs deleted file mode 100644 index a70c721..0000000 --- a/crates/blue_engine_core/src/header.rs +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Blue Engine by Elham Aryanpur - * - * The license is Apache-2.0 -*/ - -/// re-exports from dependencies that are useful -pub mod imports; -/// few commonly used uniform buffer structures -pub mod uniform_buffer; -pub use imports::*; -pub use uniform_buffer::*; - -use downcast::{downcast, Any}; - -/// The uint type used for indices and more -#[cfg(feature = "u16")] -pub type UnsignedIntType = u16; -#[cfg(feature = "u32")] -pub type UnsignedIntType = u32; - -macro_rules! impl_deref { - ($struct:ty,$type:ty) => { - impl std::ops::Deref for $struct { - type Target = $type; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl std::ops::DerefMut for $struct { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - }; -} - -macro_rules! impl_deref_field { - ($struct:ty,$type:ty,$field:ident) => { - impl std::ops::Deref for $struct { - type Target = $type; - - fn deref(&self) -> &Self::Target { - &self.$field - } - } - impl std::ops::DerefMut for $struct { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.$field - } - } - }; -} - -/// Will contain all details about a vertex and will be sent to GPU -// Will be turned to C code and sent to GPU -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Vertex { - /// Contains position data for the vertex in 3D space - pub position: Vector3, - /// Contains uv position data for the vertex - pub uv: Vector2, - /// Contains the normal face of the vertex - pub normal: Vector3, -} -impl Vertex { - pub(crate) fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - // This should be replaced with `std::mem::size_of::() as wgpu::BufferAddress` - offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, - shader_location: 1, - format: wgpu::VertexFormat::Float32x2, - }, - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, - shader_location: 2, - format: wgpu::VertexFormat::Float32x3, - }, - ], - } - } -} -unsafe impl Send for Vertex {} -unsafe impl Sync for Vertex {} - -/// Objects make it easier to work with Blue Engine, it automates most of work needed for -/// creating 3D objects and showing them on screen. A range of default objects are available -/// as well as ability to customize each of them and even create your own! You can also -/// customize almost everything there is about them! -pub struct Object { - /// Give your object a name, which can help later on for debugging. - pub name: std::sync::Arc, - /// A list of Vertex - pub vertices: Vec, - /// A list of indices that dictates the order that vertices appear - pub indices: Vec, - /// Describes how to uniform buffer is structures - pub uniform_layout: wgpu::BindGroupLayout, - /// Pipeline holds all the data that is sent to GPU, including shaders and textures - pub pipeline: Pipeline, - /// List of instances of this object - pub instances: Vec, - /// instance buffer - pub instance_buffer: wgpu::Buffer, - /// Dictates the size of your object in relation to the world - pub size: Vector3, - /// Dictates the position of your object in pixels - pub position: Vector3, - /// Dictates the rotation of your object - pub rotation: Vector3, - // flags the object to be updated until next frame - pub(crate) changed: bool, - /// Transformation matrices helps to apply changes to your object, including position, orientation, ... - /// Best choice is to let the Object system handle it - pub position_matrix: nalgebra_glm::Mat4, - /// Transformation matrices helps to apply changes to your object, including position, orientation, ... - /// Best choice is to let the Object system handle it - pub scale_matrix: nalgebra_glm::Mat4, - /// Transformation matrices helps to apply changes to your object, including position, orientation, ... - /// Best choice is to let the Object system handle it - pub rotation_matrix: nalgebra_glm::Mat4, - /// Transformation matrix, but inversed - pub inverse_transformation_matrix: crate::uniform_type::Matrix, - /// The main color of your object - pub color: crate::uniform_type::Array4, - /// A struct making it easier to manipulate specific parts of shader - pub shader_builder: crate::objects::ShaderBuilder, - /// Shader settings - pub shader_settings: ShaderSettings, - /// Camera have any effect on the object? - pub camera_effect: Option>, - /// Uniform Buffers to be sent to GPU. These are raw and not compiled for GPU yet - pub uniform_buffers: Vec, - /// Should be rendered or not - pub is_visible: bool, - /// Objects with higher number get rendered later and appear "on top" when occupying the same space - pub render_order: usize, -} -unsafe impl Send for Object {} -unsafe impl Sync for Object {} - -/// Extra settings to customize objects on time of creation -#[derive(Debug, Clone)] -pub struct ObjectSettings { - /// Should it be affected by camera? - pub camera_effect: Option>, - /// Shader Settings - pub shader_settings: ShaderSettings, -} -impl Default for ObjectSettings { - fn default() -> Self { - Self { - camera_effect: Some("main".into()), - shader_settings: ShaderSettings::default(), - } - } -} -unsafe impl Send for ObjectSettings {} -unsafe impl Sync for ObjectSettings {} - -/// The engine is the main starting point of using the Blue Engine. -/// Everything that runs on Blue Engine will be under this struct. -/// The structure of engine is monolithic, but the underlying data and the way it works is not. -/// It gives a set of default data to work with, -/// but also allow you to go beyond that and work as low level as you wish to. -/// -/// You can also use the Engine to build you own custom structure the way you wish for it to be. -/// Possibilities are endless! -/// -/// To start using the Blue Engine, you can start by creating a new Engine like follows: -/// ``` -/// use blue_engine::header::{Engine, WindowDescriptor}; -/// -/// fn main() { -/// let engine = Engine::new().expect("Couldn't create the engine"); -/// } -/// ``` -/// The WindowDescriptor simply holds what features you would like for your window. -/// If you are reading this on later version of -/// the engine, you might be able to even run the engine in headless mode -/// meaning there would not be a need for a window and the -/// renders would come as image files. -/// -/// If you so wish to have a window, you would need to start a window update loop. -/// The update loop of window runs a frame every few millisecond, -/// and gives you details of what is happening during this time, like input events. -/// You can also modify existing parts of the engine during -/// this update loop, such as changing camera to look differently, -/// or creating a new object on the scene, or even changing window details! -/// -/// The update loop is just a method of the Engine struct -/// that have one argument which is a callback function. -/// ``` -/// -/// ``` -/// [THE DATA HERE IS WORK IN PROGRESS!] -pub struct Engine { - /// The renderer does exactly what it is called. - /// It works with the GPU to render frames according to the data you gave it. - pub renderer: Renderer, - /// The event_loop handles the events of the window and inputs. - /// - /// #### USED INTERNALLY - pub event_loop_control_flow: crate::winit::event_loop::ControlFlow, - /// The window handles everything about window and inputs. - /// This includes ability to modify window and listen toinput devices for changes. - /// - /// ### The window is not available before update_loop. - pub window: Window, - /// The object system is a way to make it easier to work with the engine. - /// Obviously you can work without it, but it's for those who - /// do not have the know-how, or wish to handle all the work of rendering data manually. - pub objects: ObjectStorage, - /// The camera handles the way the scene looks when rendered. - /// You can modify everything there is to camera through this. - pub camera: CameraContainer, - /// Handles all engine plugins - pub signals: SignalStorage, - - /// holds the update_loop function - /// - /// #### USED INTERNALLY - #[allow(clippy::type_complexity)] - pub update_loop: Option< - Box< - dyn 'static - + FnMut( - // Core - &mut Renderer, - &mut Window, - &mut ObjectStorage, - &crate::utils::winit_input_helper::WinitInputHelper, - &mut CameraContainer, - &mut crate::SignalStorage, - ), - >, - >, - - /// input events - /// - /// #### USED INTERNALLY - pub input_events: crate::utils::winit_input_helper::WinitInputHelper, -} -unsafe impl Send for Engine {} -unsafe impl Sync for Engine {} - -/// Container for pipeline values. Each pipeline takes only 1 vertex shader, -/// 1 fragment shader, 1 texture data, and optionally a vector of uniform data. -#[derive(Debug)] -pub struct Pipeline { - /// the shader buffer that's sent to the gpu - pub shader: PipelineData, - /// The vertex buffer that's sent to the gpu. This includes indices as well - pub vertex_buffer: PipelineData, - /// The texture that's sent to the gpu. - pub texture: PipelineData, - /// the Uniform buffers that are sent to the gpu - pub uniform: PipelineData>, -} -unsafe impl Send for Pipeline {} -unsafe impl Sync for Pipeline {} - -/// Container for pipeline data. Allows for sharing resources with other objects -#[derive(Debug)] -pub enum PipelineData { - /// No data, just a reference to a buffer - Copy(String), - /// The actual data - Data(T), -} - -/// Container for vertex and index buffer -#[derive(Debug)] -pub struct VertexBuffers { - /// An array of vertices. A vertex is a point in 3D space containing - /// an X, Y, and a Z coordinate between -1 and +1 - pub vertex_buffer: wgpu::Buffer, - /// An array of indices. Indices are a way to reuse vertices, - /// this in turn helps greatly in reduction of amount of vertices needed to be sent to the GPU - pub index_buffer: wgpu::Buffer, - /// The length of the vertex buffer - pub length: u32, -} -unsafe impl Send for VertexBuffers {} -unsafe impl Sync for VertexBuffers {} - -/// Main renderer class. this will contain all methods and data related to the renderer -#[derive(Debug)] -pub struct Renderer { - /// A [`wgpu::Surface`] represents a platform-specific surface - /// (e.g. a window) onto which rendered images may be presented. - pub surface: Option>, - /// Context for all of the gpu objects - pub instance: wgpu::Instance, - /// Handle to a physical graphics and/or compute device. - #[allow(unused)] - pub adapter: wgpu::Adapter, - /// Open connection to a graphics and/or compute device. - pub device: wgpu::Device, - /// Handle to a command queue on a device. - pub queue: wgpu::Queue, - /// Describes a [`wgpu::Surface`] - pub config: wgpu::SurfaceConfiguration, - /// The size of the window - pub size: winit::dpi::PhysicalSize, - /// The texture bind group layout - pub texture_bind_group_layout: wgpu::BindGroupLayout, - /// The uniform bind group layout - pub default_uniform_bind_group_layout: wgpu::BindGroupLayout, - /// The depth buffer, used to render object depth - pub depth_buffer: (wgpu::Texture, wgpu::TextureView, wgpu::Sampler), - /// The default data used within the renderer - pub default_data: Option<(crate::Textures, crate::Shaders, crate::UniformBuffers)>, - /// The camera used in the engine - pub camera: Option, - /// Background clear color - pub clear_color: wgpu::Color, - /// Scissor cut section of the screen to render to - /// (x, y, width, height) - pub scissor_rect: Option<(u32, u32, u32, u32)>, -} -unsafe impl Sync for Renderer {} -unsafe impl Send for Renderer {} - -/// Descriptor and settings for a window. -#[derive(Debug, Clone)] -pub struct WindowDescriptor { - /// The width of the window - pub width: u32, - /// The height of the window - pub height: u32, - /// The title of the window - pub title: &'static str, - /// Should the window contain the keys like minimize, maximize, or resize? - pub decorations: bool, - /// Should the window be resizable - pub resizable: bool, - /// Define how much power should the app ask for - pub power_preference: crate::PowerPreference, - /// The backend to use for the draw - pub backends: crate::Backends, - /// The features to be enabled on a backend - /// - /// read more at [wgpu::Features] - pub features: crate::wgpu::Features, - /// Controls how the events are processed - /// - /// read more at [winit::event_loop::ControlFlow] - pub control_flow: crate::winit::event_loop::ControlFlow, - /// The presentation mode of renderer for things like VSync - /// - /// read more at [wgpu::PresentMode] - pub present_mode: crate::wgpu::PresentMode, - /// Limits to be required based on the generation of the GPU and the API. - /// - /// read more at [wgpu::Limits] - pub limits: crate::wgpu::Limits, - /// The alpha mode which specifies how the alpha channel of - /// the textures should be handled during compositing. - pub alpha_mode: crate::wgpu::CompositeAlphaMode, - /// The desired frame latency. - /// - /// read more at [wgpu::SurfaceConfiguration::desired_maximum_frame_latency] - pub desired_maximum_frame_latency: u32, - /// How the memory should be utilized - /// - /// read more at [wgpu::MemoryHints] - pub memory_hints: crate::wgpu::MemoryHints, -} -impl std::default::Default for WindowDescriptor { - /// Will quickly create a window with default settings - fn default() -> Self { - let backends = crate::Backends::all(); - Self { - width: 800, - height: 600, - title: "Blue Engine", - decorations: true, - resizable: true, - power_preference: crate::PowerPreference::LowPower, - backends, - features: if backends == wgpu::Backends::VULKAN { - wgpu::Features::POLYGON_MODE_LINE | wgpu::Features::POLYGON_MODE_POINT - } else if backends - .contains(wgpu::Backends::VULKAN | wgpu::Backends::METAL | wgpu::Backends::DX12) - { - wgpu::Features::POLYGON_MODE_LINE - } else { - wgpu::Features::empty() - }, - control_flow: crate::winit::event_loop::ControlFlow::Poll, - present_mode: crate::wgpu::PresentMode::AutoNoVsync, - limits: crate::wgpu::Limits::default(), - alpha_mode: crate::wgpu::CompositeAlphaMode::Auto, - desired_maximum_frame_latency: 2, - memory_hints: crate::MemoryHints::Performance, - } - } -} -unsafe impl Send for WindowDescriptor {} -unsafe impl Sync for WindowDescriptor {} - -/// Container for the projection used by the camera -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum Projection { - /// Perspective projection - /// - /// This is the default project used by the video games and majority of graphics - Perspective { - /// The field of view - fov: f32, - }, - /// Orthographic projection - /// - /// This projection gives you a 2D view of the scene - Orthographic { - /// The size of the view - zoom: f32, - }, -} - -/// Container for the camera feature. The settings here are needed for -/// algebra equations needed for camera vision and movement. Please leave it to the renderer to handle -#[derive(Debug)] -pub struct Camera { - /// The position of the camera in 3D space - pub position: Vector3, - /// The target at which the camera should be looking - pub target: Vector3, - /// The up vector of the camera. This defines the elevation of the camera - pub up: Vector3, - /// The resolution of the camera view - pub resolution: (f32, f32), //maybe this should be a Vector2i - /// The projection of the camera - pub projection: Projection, - /// The closest view of camera - pub near: f32, - /// The furthest view of camera - pub far: f32, - /// The final data that will be sent to GPU - pub view_data: nalgebra_glm::Mat4, - // For checking and rebuilding it's uniform buffer - pub(crate) changed: bool, - /// The uniform data of the camera to be sent to the gpu - pub uniform_data: UniformBuffers, - /// The position and target of the camera - pub(crate) add_position_and_target: bool, -} -unsafe impl Send for Camera {} -unsafe impl Sync for Camera {} - -/// Container for Cameras -/// -/// This allows for different objects have a different camera perspective. -#[derive(Debug)] -pub struct CameraContainer { - /// The list of cameras - // Arc is used instead of String for performance - pub cameras: std::collections::HashMap, Camera>, -} -impl_deref_field!( - CameraContainer, - std::collections::HashMap, Camera>, - cameras -); - -/// These definitions are taken from wgpu API docs -#[derive(Debug, Clone, Copy)] -pub struct ShaderSettings { - // ===== PRIMITIVE ===== // - /// The primitive topology used to interpret vertices - pub topology: crate::ShaderPrimitive, - /// When drawing strip topologies with indices, this is the - /// required format for the index buffer. This has no effect - /// on non-indexed or non-strip draws. - pub strip_index_format: Option, - /// The face to consider the front for the purpose of - /// culling and stencil operations. - pub front_face: crate::FrontFace, - /// The face culling mode - pub cull_mode: Option, - /// Controls the way each polygon is rasterized. Can be - /// either `Fill` (default), `Line` or `Point` - /// - /// Setting this to something other than `Fill` requires - /// `NON_FILL_POLYGON_MODE` feature to be enabled - pub polygon_mode: crate::PolygonMode, - /// If set to true, the polygon depth is clamped to 0-1 - /// range instead of being clipped. - /// - /// Enabling this requires the `DEPTH_CLAMPING` feature - /// to be enabled - pub clamp_depth: bool, - /// If set to true, the primitives are rendered with - /// conservative overestimation. I.e. any rastered - /// pixel touched by it is filled. Only valid for PolygonMode::Fill! - /// - /// Enabling this requires `CONSERVATIVE_RASTERIZATION` - /// features to be enabled. - pub conservative: bool, - - // ===== Multisample ===== // - /// The number of samples calculated per pixel (for MSAA). - /// For non-multisampled textures, this should be `1` - pub count: u32, - /// Bitmask that restricts the samples of a pixel modified - /// by this pipeline. All samples can be enabled using the - /// value `!0` - pub mask: u64, - /// When enabled, produces another sample mask per pixel - /// based on the alpha output value, that is ANDead with the - /// sample_mask and the primitive coverage to restrict the - /// set of samples affected by a primitive. - - /// The implicit mask produced for alpha of zero is guaranteed - /// to be zero, and for alpha of one is guaranteed to be all - /// 1-s. - pub alpha_to_coverage_enabled: bool, -} -impl Default for ShaderSettings { - fn default() -> Self { - Self { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - clamp_depth: false, - conservative: false, - count: 1, - mask: !0, - alpha_to_coverage_enabled: true, - } - } -} -unsafe impl Send for ShaderSettings {} -unsafe impl Sync for ShaderSettings {} - -/// Instance buffer data that is sent to GPU -#[repr(C)] -#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] -pub struct InstanceRaw { - /// The transformation matrix of the instance - pub model: uniform_type::Matrix, -} - -/// Instance buffer data storage -#[derive(Debug, Clone, Copy)] -pub struct Instance { - /// The position of the instance - pub position: Vector3, - /// The rotation of the instance - pub rotation: Vector3, - /// The scale of the instance - pub scale: Vector3, -} - -/// Allows all events to be fetched directly, making it easier to add custom additions to the engine. -pub trait Signal: Any { - /// This is ran as soon as the engine is properly initialized and all components are ready - #[allow(clippy::too_many_arguments)] - fn init( - &mut self, - _renderer: &mut crate::Renderer, - _window: &crate::Window, - _objects: &mut ObjectStorage, - _camera: &mut crate::CameraContainer, - ) { - } - - /// This is ran at the device events when available - #[allow(clippy::too_many_arguments)] - fn device_events( - &mut self, - _renderer: &mut crate::Renderer, - _window: &crate::Window, - _objects: &mut ObjectStorage, - _events: &crate::DeviceEvent, - _input: &crate::InputHelper, - _camera: &mut crate::CameraContainer, - ) { - } - - /// This is ran at the window events when available - #[allow(clippy::too_many_arguments)] - fn window_events( - &mut self, - _renderer: &mut crate::Renderer, - _window: &crate::Window, - _objects: &mut ObjectStorage, - _events: &crate::WindowEvent, - _input: &crate::InputHelper, - _camera: &mut crate::CameraContainer, - ) { - } - - /// ran before the frame is rendered - #[allow(clippy::too_many_arguments)] - fn frame( - &mut self, - _renderer: &mut crate::Renderer, - _window: &crate::Window, - _objects: &mut ObjectStorage, - _camera: &mut crate::CameraContainer, - _input: &crate::InputHelper, - _encoder: &mut crate::CommandEncoder, - _view: &crate::TextureView, - ) { - } -} -// The engine needs to know the functions of Signal to do things internally, -// so we use downcast and not the std::any::Any -downcast!(dyn Signal); - -/// Defines how the rotation axis is -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RotateAxis { - #[doc(hidden)] - X, - #[doc(hidden)] - Y, - #[doc(hidden)] - Z, -} -unsafe impl Send for RotateAxis {} -unsafe impl Sync for RotateAxis {} - -/// Defines how the rotation amount is -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum RotateAmount { - #[doc(hidden)] - Radians(f32), - #[doc(hidden)] - Degrees(f32), -} -unsafe impl Send for RotateAmount {} -unsafe impl Sync for RotateAmount {} - -/// Defines how the texture data is -#[derive(Debug, Clone)] -pub enum TextureData { - /// the texture file bytes directly - Bytes(Vec), - /// the texture as a [`image::DynamicImage`] - Image(image::DynamicImage), - /// path to a texture file to load - Path(String), -} -unsafe impl Send for TextureData {} -unsafe impl Sync for TextureData {} - -/// Defines how the borders of texture would look like -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TextureMode { - /// Expands the texture to fit the object - Clamp, - /// Repeats the texture instead of stretching - Repeat, - /// Repeats the texture, but mirrors it on edges - MirrorRepeat, -} -unsafe impl Send for TextureMode {} -unsafe impl Sync for TextureMode {} - -/// This function helps in converting pixel value to the value that is between -1 and +1 -pub fn pixel_to_cartesian(value: f32, max: u32) -> f32 { - let mut result = value / max as f32; - - if value == max as f32 { - result = 0.0; - } else if result < max as f32 / 2.0 { - } - - if result > -1.0 { - result - } else { - -1.0 - } -} - -/// A unified way to handle strings -pub trait StringBuffer: StringBufferTrait + Clone {} -/// A trait for [StringBuffer] -pub trait StringBufferTrait { - /// Returns the string as &[`str`] - fn as_str(&self) -> &str; - /// Returns the string as [`String`] - fn as_string(&self) -> String; - /// Returns Arc for ease of computation - fn as_arc(&self) -> std::sync::Arc; -} - -impl StringBufferTrait for String { - fn as_str(&self) -> &str { - self.as_ref() - } - fn as_string(&self) -> String { - self.clone() - } - fn as_arc(&self) -> std::sync::Arc { - self.as_str().into() - } -} -impl StringBuffer for String {} -impl StringBufferTrait for &str { - fn as_str(&self) -> &str { - self - } - fn as_string(&self) -> String { - self.to_string() - } - fn as_arc(&self) -> std::sync::Arc { - self.as_str().into() - } -} -impl StringBuffer for &str {} - -/// A unified way to handle objects -/// -/// This is a container for objects that is used to apply different operations on the objects at the same time. -/// It can deref to the object hashmap itself when needed. -pub struct ObjectStorage(std::collections::HashMap); -impl ObjectStorage { - /// Creates a new object storage - pub fn new() -> Self { - ObjectStorage(std::collections::HashMap::new()) - } -} -impl Default for ObjectStorage { - fn default() -> Self { - Self::new() - } -} -unsafe impl Send for ObjectStorage {} -unsafe impl Sync for ObjectStorage {} - -impl_deref!(ObjectStorage, std::collections::HashMap); - -/// Depth format -pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - -/// Handles the live events in the engine -pub struct SignalStorage { - /// list of events with key and the event - pub events: Vec<(String, Box)>, -} - -/// Handles the order in which a functionality in the engine should be executed -pub enum ExecuteOrder { - /// The main function that is the update_loop - UpdateLoopFunction, -} - -/// A wrapper for winit window to make it easier to use and more ergonomic. -#[derive(Debug)] -pub struct Window { - /// The winit window itself. - pub window: Option>, - /// Default attributes of the window - pub default_attributes: winit::window::WindowAttributes, - /// Whether the engine should close. - pub should_close: bool, -} -impl_deref_field!( - Window, - Option>, - window -); - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Zeroable)] -#[repr(C)] -/// General purposes 3D vector -pub struct Vector3 { - /// X coordinate in 3D space - pub x: f32, - /// Y coordinate in 3D space - pub y: f32, - /// Z coordinate in 3D space - pub z: f32, -} - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Zeroable)] -#[repr(C)] -/// General purposes 2D vector -pub struct Vector2 { - /// X coordinate in 2D space - pub x: f32, - /// Y coordinate in 2D space - pub y: f32, -} diff --git a/crates/blue_engine_core/src/header/uniform_buffer.rs b/crates/blue_engine_core/src/header/uniform_buffer.rs deleted file mode 100644 index 9f4fcd1..0000000 --- a/crates/blue_engine_core/src/header/uniform_buffer.rs +++ /dev/null @@ -1,164 +0,0 @@ -/// A container for uniform buffer types -pub mod uniform_type { - - /// 4 by 4, 32 bit float matrix uniform buffer - #[repr(C)] - #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] - pub struct Matrix { - /// data structure for the matrix - pub data: [[f32; 4]; 4], - } - impl Matrix { - /// Replaces it's values by the new values provided - pub fn update(&mut self, uniform: Matrix) { - self.data = uniform.data; - } - - /// Converts internal matrix to the uniform matrix - pub fn from_im(matrix: nalgebra_glm::Mat4) -> Self { - let mtx = matrix.as_slice(); - - Self { - data: [ - [mtx[0], mtx[1], mtx[2], mtx[3]], - [mtx[4], mtx[5], mtx[6], mtx[7]], - [mtx[8], mtx[9], mtx[10], mtx[11]], - [mtx[12], mtx[13], mtx[14], mtx[15]], - ], - } - } - - /// Converts uniform matrix to internal matrix - pub fn to_im(&self) -> nalgebra_glm::Mat4 { - let mtx = self.data; - - nalgebra_glm::mat4( - mtx[0][0], mtx[0][1], mtx[0][2], mtx[0][3], mtx[1][0], mtx[1][1], mtx[1][2], - mtx[1][3], mtx[2][0], mtx[2][1], mtx[2][2], mtx[2][3], mtx[3][0], mtx[3][1], - mtx[3][2], mtx[3][3], - ) - } - } - impl std::ops::Mul for Matrix { - type Output = Matrix; - - fn mul(self, rhs: Self) -> Self::Output { - let a = self.data; - let b = rhs.data; - Matrix { - data: [ - [ - a[0][0] * b[0][0], - a[0][1] * b[1][0], - a[0][2] * b[2][0], - a[0][3] * b[3][0], - ], - [ - a[1][0] * b[0][1], - a[1][1] * b[1][1], - a[1][2] * b[2][1], - a[1][3] * b[3][1], - ], - [ - a[2][0] * b[0][2], - a[2][1] * b[1][2], - a[2][2] * b[2][2], - a[2][3] * b[3][2], - ], - [ - a[3][0] * b[0][3], - a[3][1] * b[1][3], - a[3][2] * b[2][3], - a[3][3] * b[3][3], - ], - ], - } - } - } - - /// An array with length 3, each 32 bit float value, uniform buffer - #[repr(C)] - #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] - pub struct Array3 { - /// data structure for the array - pub data: [f32; 3], - } - impl std::ops::Mul for Array3 { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - Self { - data: [ - self.data[0] * rhs.data[0], - self.data[1] * rhs.data[1], - self.data[2] * rhs.data[2], - ], - } - } - } - impl std::ops::Mul for Array3 { - type Output = Self; - - fn mul(self, rhs: f32) -> Self::Output { - Self { - data: [self.data[0] * rhs, self.data[1] * rhs, self.data[2] * rhs], - } - } - } - - /// An array with length 4, each 32 bit float value, uniform buffer - #[repr(C)] - #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] - pub struct Array4 { - /// data structure for the array - pub data: [f32; 4], - } - impl Array4 { - /// Replaces it's values by the new values provided - pub fn update(&mut self, uniform: Array4) { - self.data = uniform.data; - } - } - impl std::ops::Mul for Array4 { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - Array4 { - data: [ - self.data[0] * rhs.data[0], - self.data[1] * rhs.data[1], - self.data[2] * rhs.data[2], - self.data[3] * rhs.data[3], - ], - } - } - } - impl std::ops::Mul for Array4 { - type Output = Array4; - - fn mul(self, rhs: f32) -> Self::Output { - Array4 { - data: [ - self.data[0] * rhs, - self.data[1] * rhs, - self.data[2] * rhs, - self.data[3] * rhs, - ], - } - } - } - - /// A 32 bit float uniform buffer - #[repr(C)] - #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] - pub struct Float { - /// data structure for the float - pub data: f32, - } - impl Float { - /// Replaces it's values by the new values provided - pub fn update(&mut self, uniform: Float) { - self.data = uniform.data; - } - } -} diff --git a/crates/blue_engine_core/src/lib.rs b/crates/blue_engine_core/src/lib.rs index 00a760c..e8bca53 100644 --- a/crates/blue_engine_core/src/lib.rs +++ b/crates/blue_engine_core/src/lib.rs @@ -12,21 +12,17 @@ pub(crate) mod definition; /// interal error definitions of the engine pub mod error; -/// contains all the declarations such as structs, exports, enums, ... -pub mod header; /// contains the definition for Object type, which is a type that make it easier to manage data for rendering. pub mod objects; -/// contains definition for some 2D and 3D shapes. They are basic shapes and -/// can be used as examples of how to create your own content. -pub mod primitive_shapes; +/// contains all the declarations such as structs, exports, enums, ... +pub mod prelude; /// contains definition for rendering part of the engine. pub mod render; /// Utilities for the engine (soon moving to it's own /// [crate](https://github.com/AryanpurTech/BlueEngineUtilities)). pub mod utils; -/// contains definition for 2D and 3D vectors. -pub mod vector; +pub use utils::*; /// contains definition for creation of window and instance creation. pub mod window; #[doc(inline)] -pub use crate::header::*; +pub use crate::prelude::*; diff --git a/crates/blue_engine_core/src/objects.rs b/crates/blue_engine_core/src/objects.rs index dbe51c3..35e375a 100644 --- a/crates/blue_engine_core/src/objects.rs +++ b/crates/blue_engine_core/src/objects.rs @@ -4,13 +4,151 @@ * The license is same as the one on the root. */ -use crate::header::{ - glm, pixel_to_cartesian, uniform_type, Instance, InstanceRaw, Object, ObjectSettings, Pipeline, - PipelineData, Renderer, RotateAxis, TextureData, Textures, Vertex, +use crate::utils::default_resources::{DEFAULT_SHADER, DEFAULT_TEXTURE}; +use crate::{ + Matrix4, Pipeline, PipelineData, Quaternion, Renderer, ShaderSettings, StringBuffer, + TextureData, Textures, UnsignedIntType, Vector3, Vector4, Vertex, pixel_to_cartesian, }; -use crate::uniform_type::{Array4, Matrix}; -use crate::utils::default_resources::{DEFAULT_MATRIX_4, DEFAULT_SHADER, DEFAULT_TEXTURE}; -use crate::{ObjectStorage, RotateAmount, StringBuffer, UnsignedIntType, Vector3}; + +/// Objects make it easier to work with Blue Engine, it automates most of work needed for +/// creating 3D objects and showing them on screen. A range of default objects are available +/// as well as ability to customize each of them and even create your own! You can also +/// customize almost everything there is about them! +pub struct Object { + /// Give your object a name, which can help later on for debugging. + pub name: std::sync::Arc, + /// A list of Vertex + pub vertices: Vec, + /// A list of indices that dictates the order that vertices appear + pub indices: Vec, + /// Describes how to uniform buffer is structures + pub uniform_layout: wgpu::BindGroupLayout, + /// Pipeline holds all the data that is sent to GPU, including shaders and textures + pub pipeline: Pipeline, + /// List of instances of this object + pub instances: Vec, + /// instance buffer + pub instance_buffer: wgpu::Buffer, + /// Dictates the size of your object in relation to the world + pub size: Vector3, + /// Dictates the position of your object in pixels + pub position: Vector3, + /// Dictates the rotation of your object + pub rotation: Quaternion, + // flags the object to be updated until next frame + pub(crate) changed: bool, + /// Transformation matrices helps to apply changes to your object, including position, orientation, ... + /// Best choice is to let the Object system handle it + pub position_matrix: Matrix4, + /// Transformation matrices helps to apply changes to your object, including position, orientation, ... + /// Best choice is to let the Object system handle it + pub scale_matrix: Matrix4, + /// Transformation matrices helps to apply changes to your object, including position, orientation, ... + /// Best choice is to let the Object system handle it + pub rotation_quaternion: Quaternion, + /// Transformation matrix, but inversed + pub inverse_transformation_matrix: Matrix4, + /// The main color of your object + pub color: Vector4, + /// A struct making it easier to manipulate specific parts of shader + pub shader_builder: crate::objects::ShaderBuilder, + /// Shader settings + pub shader_settings: ShaderSettings, + /// Camera have any effect on the object? + pub camera_effect: Option>, + /// Uniform Buffers to be sent to GPU. These are raw and not compiled for GPU yet + pub uniform_buffers: Vec, + /// Should be rendered or not + pub is_visible: bool, + /// Objects with higher number get rendered later and appear "on top" when occupying the same space + pub render_order: usize, +} +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + +/// Extra settings to customize objects on time of creation +#[derive(Debug, Clone)] +pub struct ObjectSettings { + /// Should it be affected by camera? + pub camera_effect: Option>, + /// Shader Settings + pub shader_settings: ShaderSettings, +} +impl Default for ObjectSettings { + fn default() -> Self { + Self { + camera_effect: Some("main".into()), + shader_settings: ShaderSettings::default(), + } + } +} +unsafe impl Send for ObjectSettings {} +unsafe impl Sync for ObjectSettings {} + +/// A unified way to handle objects +/// +/// This is a container for objects that is used to apply different operations on the objects at the same time. +/// It can deref to the object hashmap itself when needed. +pub struct ObjectStorage(std::collections::HashMap); +impl ObjectStorage { + /// Creates a new object storage + pub fn new() -> Self { + ObjectStorage(std::collections::HashMap::new()) + } +} +impl Default for ObjectStorage { + fn default() -> Self { + Self::new() + } +} +unsafe impl Send for ObjectStorage {} +unsafe impl Sync for ObjectStorage {} +crate::macros::impl_deref!(ObjectStorage, std::collections::HashMap); + +/// Instance buffer data that is sent to GPU +#[repr(C)] +#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +pub struct InstanceRaw { + /// The transformation matrix of the instance + pub model: Matrix4, +} + +/// Instance buffer data storage +#[derive(Debug, Clone, Copy)] +pub struct Instance { + /// The position of the instance + pub position: Vector3, + /// The rotation of the instance + pub rotation: Vector3, + /// The scale of the instance + pub scale: Vector3, +} + +/// Defines how the rotation axis is +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RotateAxis { + #[doc(hidden)] + X, + #[doc(hidden)] + Y, + #[doc(hidden)] + Z, +} +unsafe impl Send for RotateAxis {} +unsafe impl Sync for RotateAxis {} + +/// Defines how the rotation amount is +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum RotateAmount { + #[doc(hidden)] + Radians(f32), + #[doc(hidden)] + Degrees(f32), +} +unsafe impl Send for RotateAmount {} +unsafe impl Sync for RotateAmount {} + +/// Defines full axes rotation information impl Renderer { /// Creates a new object @@ -33,13 +171,8 @@ impl Renderer { let vertex_buffer = self.build_vertex_buffer(&vertices, &indices); let uniform = self.build_uniform_buffer(&vec![ - self.build_uniform_buffer_part("Transformation Matrix", DEFAULT_MATRIX_4), - self.build_uniform_buffer_part( - "Color", - crate::uniform_type::Array4 { - data: crate::utils::default_resources::DEFAULT_COLOR, - }, - ), + self.build_uniform_buffer_part("Transformation Matrix", Matrix4::IDENTITY), + self.build_uniform_buffer_part("Color", crate::utils::default_resources::DEFAULT_COLOR), ]); let shader_source = @@ -55,8 +188,8 @@ impl Renderer { let texture = self.build_texture( "Default Texture", TextureData::Bytes(DEFAULT_TEXTURE.to_vec()), - crate::header::TextureMode::Clamp, - //crate::header::TextureFormat::PNG + crate::prelude::TextureMode::Clamp, + //crate::prelude::TextureFormat::PNG )?; let instance = Instance::new([0f32, 0f32, 0f32], [0f32, 0f32, 0f32], [1f32, 1f32, 1f32]); @@ -76,29 +209,25 @@ impl Renderer { instances: vec![instance], instance_buffer, uniform_layout: uniform.1, - size: Vector3::new(1f32, 1f32, 1f32), - position: Vector3::default(), - rotation: Vector3::new(0f32, 0f32, 0f32), + size: Vector3::ONE, + position: Vector3::ZERO, + rotation: Quaternion::IDENTITY, changed: false, - position_matrix: DEFAULT_MATRIX_4.to_im(), - scale_matrix: DEFAULT_MATRIX_4.to_im(), - rotation_matrix: DEFAULT_MATRIX_4.to_im(), - inverse_transformation_matrix: Matrix::from_im(nalgebra_glm::transpose( - &nalgebra_glm::inverse(&DEFAULT_MATRIX_4.to_im()), + position_matrix: Matrix4::IDENTITY, + scale_matrix: Matrix4::IDENTITY, + rotation_quaternion: Quaternion::IDENTITY, + inverse_transformation_matrix: Matrix4::transpose(&Matrix4::inverse( + &Matrix4::IDENTITY, )), - color: crate::uniform_type::Array4 { - data: crate::utils::default_resources::DEFAULT_COLOR, - }, + color: crate::utils::default_resources::DEFAULT_COLOR, shader_builder: shader_source, shader_settings: settings.shader_settings, camera_effect: settings.camera_effect, uniform_buffers: vec![ - self.build_uniform_buffer_part("Transformation Matrix", DEFAULT_MATRIX_4), + self.build_uniform_buffer_part("Transformation Matrix", Matrix4::IDENTITY), self.build_uniform_buffer_part( "Color", - crate::uniform_type::Array4 { - data: crate::utils::default_resources::DEFAULT_COLOR, - }, + crate::utils::default_resources::DEFAULT_COLOR, ), ], is_visible: true, @@ -163,7 +292,7 @@ impl Object { self.size *= scale; let transformation_matrix = self.scale_matrix; - let result = nalgebra_glm::scale(&transformation_matrix, &scale.into()); + let result = transformation_matrix * Matrix4::from_scale(scale); self.scale_matrix = result; self.inverse_matrices(); @@ -182,11 +311,7 @@ impl Object { let difference_in_width = if self.size.x != 0.0 && width != 0.0 { let a = pixel_to_cartesian(width, window_size.width); let b = pixel_to_cartesian(self.size.x, window_size.width); - if a != 0f32 && b != 0f32 { - a / b - } else { - b - } + if a != 0f32 && b != 0f32 { a / b } else { b } } else { 0.0 }; @@ -194,22 +319,14 @@ impl Object { let difference_in_height = if self.size.y != 0.0 && height != 0.0 { let a = pixel_to_cartesian(height, window_size.height); let b = pixel_to_cartesian(self.size.y, window_size.height); - if a != 0f32 && b != 0f32 { - a / b - } else { - b - } + if a != 0f32 && b != 0f32 { a / b } else { b } } else { 0.0 }; let difference_in_depth = if self.size.z != 0.0 && depth != 0.0 { let a = pixel_to_cartesian(depth, window_size.width); let b = pixel_to_cartesian(self.size.z, window_size.width); - if a != 0f32 && b != 0f32 { - a / b - } else { - b - } + if a != 0f32 && b != 0f32 { a / b } else { b } } else { 0.0 }; @@ -222,79 +339,13 @@ impl Object { self } - /// Rotates the object in the axis you specify - /// - /// THIS METHOD IS DEPRECATED, USE [crate::Object::set_rotation] or [crate::Object::rotate] - #[deprecated] - pub fn set_rotatation(&mut self, angle: f32, axis: RotateAxis) -> &mut Self { - let mut rotation_matrix = self.rotation_matrix; - let axis = match axis { - RotateAxis::X => { - self.rotation.x += angle; - Vector3::x_axis() - } - RotateAxis::Y => { - self.rotation.y += angle; - Vector3::y_axis() - } - RotateAxis::Z => { - self.rotation.z += angle; - Vector3::z_axis() - } - }; - - rotation_matrix = nalgebra_glm::rotate(&rotation_matrix, angle.to_radians(), &axis.into()); - self.rotation_matrix = rotation_matrix; - self.inverse_matrices(); - - self.changed = true; - self - } - - fn rotation_single_axis(angle: f32, axis_from: usize, axis_into: usize) -> nalgebra_glm::Mat4 { - // angle.cos(), -angle.sin() - // angle.sin(), angle.cos() - let mut result = nalgebra_glm::Mat4::identity(); - - result[(axis_from, axis_from)] = angle.cos() as f32; - result[(axis_from, axis_into)] = -angle.sin() as f32; - result[(axis_into, axis_from)] = angle.sin() as f32; - result[(axis_into, axis_into)] = angle.cos() as f32; - - result - } - fn rotation_full(euler_angles: nalgebra_glm::Vec3) -> nalgebra_glm::Mat4 { - const X: usize = 0; - const Y: usize = 1; - const Z: usize = 2; - - Self::rotation_single_axis(euler_angles[2], X, Y) * // Rotation around Z (rotation of X into Y) - Self::rotation_single_axis(euler_angles[1], Z, X) * // Rotation around Y (rotation of Z into X) - Self::rotation_single_axis(euler_angles[0], Y, Z) // Rotation around X (rotation of Y into Z) - } - /// Sets the rotation of the object in the axis you specify - pub fn set_rotation(&mut self, amount: RotateAmount, axis: RotateAxis) -> &mut Self { - let amount_radians = match amount { - RotateAmount::Radians(amount) => amount, - RotateAmount::Degrees(amount) => amount.to_radians(), - }; - match axis { - RotateAxis::X => { - self.rotation.x = amount_radians; - Vector3::x_axis() - } - RotateAxis::Y => { - self.rotation.y = amount_radians; - Vector3::y_axis() - } - RotateAxis::Z => { - self.rotation.z = amount_radians; - Vector3::z_axis() - } - }; - - self.rotation_matrix = Self::rotation_full(self.rotation.into()); + /// + /// This function does NOT normalize the rotation. + pub fn set_rotation(&mut self, rotation: Vector3) -> &mut Self { + self.rotation_quaternion = Quaternion::from_rotation_x(rotation.x) + * Quaternion::from_rotation_y(rotation.y) + * Quaternion::from_rotation_z(rotation.z); self.inverse_matrices(); self.changed = true; @@ -303,29 +354,27 @@ impl Object { /// Rotates the object in the axis you specify pub fn rotate(&mut self, amount: RotateAmount, axis: RotateAxis) -> &mut Self { - let mut rotation_matrix = self.rotation_matrix; - let amount_radians = match amount { RotateAmount::Radians(amount) => amount, RotateAmount::Degrees(amount) => amount.to_radians(), }; + let axis = match axis { RotateAxis::X => { self.rotation.x += amount_radians; - Vector3::x_axis() + Quaternion::from_rotation_x(amount_radians) } RotateAxis::Y => { self.rotation.y += amount_radians; - Vector3::y_axis() + Quaternion::from_rotation_y(amount_radians) } RotateAxis::Z => { self.rotation.z += amount_radians; - Vector3::z_axis() + Quaternion::from_rotation_z(amount_radians) } }; - rotation_matrix = nalgebra_glm::rotate(&rotation_matrix, amount_radians, &axis.into()); - self.rotation_matrix = rotation_matrix; + self.rotation_quaternion *= axis; self.inverse_matrices(); self.changed = true; @@ -335,10 +384,7 @@ impl Object { /// Moves the object by the amount you specify in the axis you specify pub fn set_translation(&mut self, new_pos: impl Into) -> &mut Self { self.position -= new_pos.into(); - - let mut position_matrix = self.position_matrix; - position_matrix = nalgebra_glm::translate(&position_matrix, &self.position.into()); - self.position_matrix = position_matrix; + self.position_matrix *= Matrix4::from_translation(self.position); self.inverse_matrices(); self.changed = true; @@ -348,11 +394,11 @@ impl Object { /// Sets the position of the object in 3D space relative to the window pub fn set_position(&mut self, new_pos: impl Into) -> &mut Self { let new_pos = new_pos.into(); - + self.position.x = new_pos.x; self.position.y = new_pos.y; self.position.z = new_pos.z; - + // self.set_translation((self.position - new_pos) * -1f32); self.update_position_matrix(); self.inverse_matrices(); @@ -370,17 +416,17 @@ impl Object { // 0.0, 0.0, 0.0, 1.0; // ] - self.position_matrix = DEFAULT_MATRIX_4.to_im(); - self.position_matrix[(0, 3)] = self.position[0]; - self.position_matrix[(1, 3)] = self.position[1]; - self.position_matrix[(2, 3)] = self.position[2]; + self.position_matrix = Matrix4 { + x_axis: Vector4::new(1f32, 0f32, 0f32, self.position[0]), + y_axis: Vector4::new(0f32, 1f32, 0f32, self.position[1]), + z_axis: Vector4::new(0f32, 0f32, 1f32, self.position[2]), + w_axis: Vector4::new(0f32, 0f32, 0f32, 1f32), + }; } /// Changes the color of the object. If textures exist, the color of textures will change pub fn set_color(&mut self, red: f32, green: f32, blue: f32, alpha: f32) -> &mut Self { - self.color = Array4 { - data: [red, green, blue, alpha], - }; + self.color = Vector4::new(red, green, blue, alpha); self.changed = true; self } @@ -416,10 +462,11 @@ impl Object { /// build an inverse of the transformation matrix to be sent to the gpu for lighting and other things. pub fn inverse_matrices(&mut self) { - self.inverse_transformation_matrix = - Matrix::from_im(nalgebra_glm::transpose(&nalgebra_glm::inverse( - &(self.position_matrix * self.rotation_matrix * self.scale_matrix), - ))); + self.inverse_transformation_matrix = Matrix4::transpose(&Matrix4::inverse( + &(self.position_matrix + * Matrix4::from_quat(self.rotation_quaternion) + * self.scale_matrix), + )); } /// Update and apply changes done to an object @@ -495,9 +542,7 @@ impl Object { pub fn update_uniform_buffer(&mut self, renderer: &mut Renderer) { self.uniform_buffers[0] = renderer.build_uniform_buffer_part( "Transformation Matrix", - uniform_type::Matrix::from_im( - self.position_matrix * self.rotation_matrix * self.scale_matrix, - ), + self.position_matrix * Matrix4::from_quat(self.rotation_quaternion) * self.scale_matrix, ); self.uniform_buffers[1] = renderer.build_uniform_buffer_part("Color", self.color); @@ -514,9 +559,7 @@ impl Object { ) -> crate::UniformBuffers { self.uniform_buffers[0] = renderer.build_uniform_buffer_part( "Transformation Matrix", - uniform_type::Matrix::from_im( - self.position_matrix * self.rotation_matrix * self.scale_matrix, - ), + self.position_matrix * Matrix4::from_quat(self.rotation_quaternion) * self.scale_matrix, ); self.uniform_buffers[1] = renderer.build_uniform_buffer_part("Color", self.color); @@ -671,12 +714,15 @@ impl Instance { /// Gathers all information and builds a Raw Instance to be sent to GPU pub fn to_raw(&self) -> InstanceRaw { - let position_matrix = glm::translate(&DEFAULT_MATRIX_4.to_im(), &self.position.into()); - let rotation_matrix = - nalgebra_glm::rotate(&DEFAULT_MATRIX_4.to_im(), 0f32, &self.rotation.into()); - let scale_matrix = glm::scale(&DEFAULT_MATRIX_4.to_im(), &self.scale.into()); + let position_matrix = Matrix4::IDENTITY * Matrix4::from_translation(self.position); + let rotation_matrix = Matrix4::from_quat( + Quaternion::from_rotation_x(self.rotation.x) + * Quaternion::from_rotation_y(self.rotation.y) + * Quaternion::from_rotation_z(self.rotation.z), + ); + let scale_matrix = Matrix4::IDENTITY * Matrix4::from_scale(self.scale); InstanceRaw { - model: Matrix::from_im(position_matrix * rotation_matrix * scale_matrix), + model: position_matrix * rotation_matrix * scale_matrix, } } diff --git a/crates/blue_engine_core/src/header/imports.rs b/crates/blue_engine_core/src/prelude/imports.rs similarity index 57% rename from crates/blue_engine_core/src/header/imports.rs rename to crates/blue_engine_core/src/prelude/imports.rs index 310bf0e..cce03d0 100644 --- a/crates/blue_engine_core/src/header/imports.rs +++ b/crates/blue_engine_core/src/prelude/imports.rs @@ -1,70 +1,83 @@ -/// Shaders are programs that runs on the GPU -pub type Shaders = wgpu::RenderPipeline; -/// Uniform Buffers are small amount of data that are sent from CPU to GPU -pub type UniformBuffers = wgpu::BindGroup; -/// Textures are image data that are sent to GPU to be set to a surface -pub type Textures = wgpu::BindGroup; -/// Primitive type the input mesh is composed of. -pub type ShaderPrimitive = wgpu::PrimitiveTopology; -/// Format of indices used with pipeline. -pub type IndexFormat = wgpu::IndexFormat; -/// Vertex winding order which classifies the "front" face of a triangle. -pub type FrontFace = wgpu::FrontFace; -/// Face of a vertex. -pub type CullMode = wgpu::Face; -/// Type of drawing mode for polygons -pub type PolygonMode = wgpu::PolygonMode; -/// Power Preference when choosing a physical adapter. -pub type PowerPreference = wgpu::PowerPreference; - -/// Pod trait for custom uniform buffer structure -pub use bytemuck::Pod; -/// Zeroable trait for custom uniform buffer structure -pub use bytemuck::Zeroable; +pub use downcast; +pub use glam; +pub use image; +pub use wgpu; +pub use winit; -/// Backends pub use wgpu::Backends; -/// Encoder from wgpu pub use wgpu::CommandEncoder; pub use wgpu::LoadOp; -/// Memory hints pub use wgpu::MemoryHints; pub use wgpu::Operations; pub use wgpu::RenderPassColorAttachment; pub use wgpu::RenderPassDescriptor; -/// Surface Texture pub use wgpu::TextureView; -/// Input helper -pub use crate::utils::winit_input_helper::WinitInputHelper as InputHelper; -/// all of downcast -pub use downcast; -/// all of image -pub use image; -/// all of nalgebra_glm -pub use nalgebra_glm as glm; -/// all of wgpu -pub use wgpu; -/// all of winit -pub use winit; -/// WindowSize pub use winit::dpi::*; -/// Device Events pub use winit::event::DeviceEvent; -/// Element State pub use winit::event::ElementState; -/// Winit Events pub use winit::event::Event; -/// Keyboard input identifier pub use winit::event::KeyEvent; -/// The mouse button identifier pub use winit::event::MouseButton; -/// WindowEvents pub use winit::event::WindowEvent; -/// Event Loop pub use winit::event_loop::EventLoop; -/// Keyboard keys identifier pub use winit::keyboard::Key; pub use winit::keyboard::KeyCode; -/// Fullscreen enum pub use winit::window::Fullscreen; + +// Math types +/// A 2-dimensional vector. +pub type Vector2 = glam::Vec2; +/// A 3-dimensional vector. +pub type Vector3 = glam::Vec3; +/// A 4-dimensional vector. +pub type Vector4 = glam::Vec4; +/// A 2x2 column major matrix. +/// +/// SIMD vector types are used for storage on supported platforms. +/// +/// This type is 16 byte aligned. +pub type Matrix2 = glam::Mat2; +/// A 3x3 column major matrix. +/// +/// This 3x3 matrix type features convenience methods for creating and using linear and +/// affine transformations. +pub type Matrix3 = glam::Mat3; +/// A 4x4 column major matrix. +/// +/// This 4x4 matrix type features convenience methods for creating and using affine transforms and perspective projections. +pub type Matrix4 = glam::Mat4; +/// A quaternion representing an orientation. +/// +/// This quaternion is intended to be of unit length but may denormalize due to floating point "error creep" which can occur when successive quaternion operations are applied. +/// +/// SIMD vector types are used for storage on supported platforms. +/// +/// This type is 16 byte aligned. +pub type Quaternion = glam::Quat; + +/// Input helper +pub use crate::utils::winit_input_helper::WinitInputHelper as InputHelper; +pub use bytemuck::Pod; +pub use bytemuck::Zeroable; + +/// Depth Format +pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; +/// Shaders are programs that runs on the GPU +pub type Shaders = wgpu::RenderPipeline; +/// Uniform Buffers are small amount of data that are sent from CPU to GPU +pub type UniformBuffers = wgpu::BindGroup; +/// Textures are image data that are sent to GPU to be set to a surface +pub type Textures = wgpu::BindGroup; +/// Primitive type the input mesh is composed of. +pub type ShaderPrimitive = wgpu::PrimitiveTopology; +/// Format of indices used with pipeline. +pub type IndexFormat = wgpu::IndexFormat; +/// Vertex winding order which classifies the "front" face of a triangle. +pub type FrontFace = wgpu::FrontFace; +/// Face of a vertex. +pub type CullMode = wgpu::Face; +/// Type of drawing mode for polygons +pub type PolygonMode = wgpu::PolygonMode; +/// Power Preference when choosing a physical adapter. +pub type PowerPreference = wgpu::PowerPreference; diff --git a/crates/blue_engine_core/src/prelude/mod.rs b/crates/blue_engine_core/src/prelude/mod.rs new file mode 100644 index 0000000..051ace4 --- /dev/null +++ b/crates/blue_engine_core/src/prelude/mod.rs @@ -0,0 +1,290 @@ +use downcast::{Any, downcast}; +/// re-exports from dependencies that are useful +pub mod imports; +pub use imports::*; +/// contains definition for some 2D and 3D shapes. They are basic shapes and +/// can be used as examples of how to create your own content. +pub mod primitive_shapes; +pub use crate::camera::{Camera, CameraContainer, Projection}; +pub use crate::definition::{ + Pipeline, PipelineData, ShaderSettings, TextureData, TextureMode, VertexBuffers, + pixel_to_cartesian, +}; +pub use crate::objects::{ + Instance, InstanceRaw, Object, ObjectSettings, ObjectStorage, RotateAmount, RotateAxis, +}; +pub use crate::render::Renderer; +pub use crate::window::{Window, WindowDescriptor}; + +/// The uint type used for indices and more +#[cfg(not(feature = "u32"))] +pub type UnsignedIntType = u16; +#[cfg(feature = "u32")] +pub type UnsignedIntType = u32; + +/// +pub mod macros { + macro_rules! impl_deref { + ($struct:ty,$type:ty) => { + impl std::ops::Deref for $struct { + type Target = $type; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for $struct { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + }; + } + + macro_rules! impl_deref_field { + ($struct:ty,$type:ty,$field:ident) => { + impl std::ops::Deref for $struct { + type Target = $type; + + fn deref(&self) -> &Self::Target { + &self.$field + } + } + impl std::ops::DerefMut for $struct { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.$field + } + } + }; + } + + pub(crate) use impl_deref; + pub(crate) use impl_deref_field; +} + +/// Will contain all details about a vertex and will be sent to GPU +// Will be turned to C code and sent to GPU +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +pub struct Vertex { + /// Contains position data for the vertex in 3D space + pub position: [f32; 3], + /// Contains uv position data for the vertex + pub uv: [f32; 2], + /// Contains the normal face of the vertex + pub normal: [f32; 3], +} +impl Vertex { + pub(crate) fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + // This should be replaced with `std::mem::size_of::() as wgpu::BufferAddress` + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x2, + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, + shader_location: 2, + format: wgpu::VertexFormat::Float32x3, + }, + ], + } + } +} +unsafe impl Send for Vertex {} +unsafe impl Sync for Vertex {} + +/// The engine is the main starting point of using the Blue Engine. +/// Everything that runs on Blue Engine will be under this struct. +/// The structure of engine is monolithic, but the underlying data and the way it works is not. +/// It gives a set of default data to work with, +/// but also allow you to go beyond that and work as low level as you wish to. +/// +/// You can also use the Engine to build you own custom structure the way you wish for it to be. +/// Possibilities are endless! +/// +/// To start using the Blue Engine, you can start by creating a new Engine like follows: +/// ``` +/// use blue_engine::prelude::{Engine, WindowDescriptor}; +/// +/// fn main() { +/// let engine = Engine::new().expect("Couldn't create the engine"); +/// } +/// ``` +/// The WindowDescriptor simply holds what features you would like for your window. +/// If you are reading this on later version of +/// the engine, you might be able to even run the engine in headless mode +/// meaning there would not be a need for a window and the +/// renders would come as image files. +/// +/// If you so wish to have a window, you would need to start a window update loop. +/// The update loop of window runs a frame every few millisecond, +/// and gives you details of what is happening during this time, like input events. +/// You can also modify existing parts of the engine during +/// this update loop, such as changing camera to look differently, +/// or creating a new object on the scene, or even changing window details! +/// +/// The update loop is just a method of the Engine struct +/// that have one argument which is a callback function. +/// ``` +/// +/// ``` +/// [THE DATA HERE IS WORK IN PROGRESS!] +pub struct Engine { + /// The renderer does exactly what it is called. + /// It works with the GPU to render frames according to the data you gave it. + pub renderer: Renderer, + /// The event_loop handles the events of the window and inputs. + /// + /// #### USED INTERNALLY + pub event_loop_control_flow: crate::winit::event_loop::ControlFlow, + /// The window handles everything about window and inputs. + /// This includes ability to modify window and listen toinput devices for changes. + /// + /// ### The window is not available before update_loop. + pub window: Window, + /// The object system is a way to make it easier to work with the engine. + /// Obviously you can work without it, but it's for those who + /// do not have the know-how, or wish to handle all the work of rendering data manually. + pub objects: ObjectStorage, + /// The camera handles the way the scene looks when rendered. + /// You can modify everything there is to camera through this. + pub camera: CameraContainer, + /// Handles all engine plugins + pub signals: SignalStorage, + + /// holds the update_loop function + /// + /// #### USED INTERNALLY + #[allow(clippy::type_complexity)] + pub update_loop: Option< + Box< + dyn 'static + + FnMut( + // Core + &mut Renderer, + &mut Window, + &mut ObjectStorage, + &crate::utils::winit_input_helper::WinitInputHelper, + &mut CameraContainer, + &mut crate::SignalStorage, + ), + >, + >, + + /// input events + /// + /// #### USED INTERNALLY + pub input_events: crate::utils::winit_input_helper::WinitInputHelper, +} +unsafe impl Send for Engine {} +unsafe impl Sync for Engine {} + +/// Allows all events to be fetched directly, making it easier to add custom additions to the engine. +pub trait Signal: Any { + /// This is ran as soon as the engine is properly initialized and all components are ready + #[allow(clippy::too_many_arguments)] + fn init( + &mut self, + _renderer: &mut crate::Renderer, + _window: &crate::Window, + _objects: &mut ObjectStorage, + _camera: &mut crate::CameraContainer, + ) { + } + + /// This is ran at the device events when available + #[allow(clippy::too_many_arguments)] + fn device_events( + &mut self, + _renderer: &mut crate::Renderer, + _window: &crate::Window, + _objects: &mut ObjectStorage, + _events: &crate::DeviceEvent, + _input: &crate::InputHelper, + _camera: &mut crate::CameraContainer, + ) { + } + + /// This is ran at the window events when available + #[allow(clippy::too_many_arguments)] + fn window_events( + &mut self, + _renderer: &mut crate::Renderer, + _window: &crate::Window, + _objects: &mut ObjectStorage, + _events: &crate::WindowEvent, + _input: &crate::InputHelper, + _camera: &mut crate::CameraContainer, + ) { + } + + /// ran before the frame is rendered + #[allow(clippy::too_many_arguments)] + fn frame( + &mut self, + _renderer: &mut crate::Renderer, + _window: &crate::Window, + _objects: &mut ObjectStorage, + _camera: &mut crate::CameraContainer, + _input: &crate::InputHelper, + _encoder: &mut crate::CommandEncoder, + _view: &crate::TextureView, + ) { + } +} +// The engine needs to know the functions of Signal to do things internally, +// so we use downcast and not the std::any::Any +downcast!(dyn Signal); + +/// Handles the live events in the engine +pub struct SignalStorage { + /// list of events with key and the event + pub events: Vec<(String, Box)>, +} + +/// A unified way to handle strings +pub trait StringBuffer: StringBufferTrait + Clone {} +/// A trait for [StringBuffer] +pub trait StringBufferTrait { + /// Returns the string as &[`str`] + fn as_str(&self) -> &str; + /// Returns the string as [`String`] + fn as_string(&self) -> String; + /// Returns Arc for ease of computation + fn as_arc(&self) -> std::sync::Arc; +} + +impl StringBufferTrait for String { + fn as_str(&self) -> &str { + self.as_ref() + } + fn as_string(&self) -> String { + self.clone() + } + fn as_arc(&self) -> std::sync::Arc { + self.as_str().into() + } +} +impl StringBuffer for String {} +impl StringBufferTrait for &str { + fn as_str(&self) -> &str { + self + } + fn as_string(&self) -> String { + self.to_string() + } + fn as_arc(&self) -> std::sync::Arc { + self.as_str().into() + } +} +impl StringBuffer for &str {} diff --git a/crates/blue_engine_core/src/prelude/primitive_shapes.rs b/crates/blue_engine_core/src/prelude/primitive_shapes.rs new file mode 100644 index 0000000..18d85cf --- /dev/null +++ b/crates/blue_engine_core/src/prelude/primitive_shapes.rs @@ -0,0 +1,330 @@ +/* + * For the sake of example we never use Vector3::default() or Vector3::x_axis() or any axis. + */ + +use crate::{ + ObjectStorage, Renderer, StringBuffer, + prelude::{ObjectSettings, UnsignedIntType, Vertex}, +}; +use std::f32::consts::PI; + +/// Creates a 2D triangle +pub fn triangle( + name: impl StringBuffer, + settings: ObjectSettings, + renderer: &mut Renderer, + objects: &mut ObjectStorage, +) { + objects.new_object( + name.clone(), + vec![ + Vertex { + position: [0.0, 1.0, 0.0], + uv: [0.5, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, 0.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, 0.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + ], + vec![0, 1, 2], + settings, + renderer, + ); +} + +/// Creates a 2D square +pub fn square( + name: impl StringBuffer, + settings: ObjectSettings, + renderer: &mut Renderer, + objects: &mut ObjectStorage, +) { + objects.new_object( + name.clone(), + vec![ + Vertex { + position: [1.0, 1.0, 0.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, 0.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, 0.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, 0.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + ], + vec![2, 1, 0, 2, 0, 3], + settings, + renderer, + ); +} + +/// Create a 2D rectangle based on a width and height +pub fn rectangle( + width: f32, + height: f32, + name: impl StringBuffer, + settings: ObjectSettings, + renderer: &mut Renderer, + objects: &mut ObjectStorage, +) { + objects.new_object( + name.clone(), + vec![ + Vertex { + position: [width / 2.0, height / 2.0, 0.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [width / 2.0, -height / 2.0, 0.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-width / 2.0, -height / 2.0, 0.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-width / 2.0, height / 2.0, 0.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + ], + vec![2, 1, 0, 2, 0, 3], + settings, + renderer, + ); +} + +// MARK: 3D + +/// Creates a 3D cube +pub fn cube(name: impl StringBuffer, renderer: &mut Renderer, objects: &mut ObjectStorage) { + objects.new_object( + name.clone(), + vec![ + // Front Face + Vertex { + position: [-1.0, -1.0, 1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, 1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, 1.0, 1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, 1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + // Back Face + Vertex { + position: [-1.0, 1.0, -1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, 1.0, -1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, -1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, -1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + // Right face + Vertex { + position: [1.0, -1.0, -1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, 1.0, -1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, 1.0, 1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, 1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + // Left face + Vertex { + position: [-1.0, -1.0, 1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, 1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, -1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, -1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + // Top face + Vertex { + position: [1.0, 1.0, -1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, -1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, 1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, 1.0, 1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + // Bottom face + Vertex { + position: [1.0, -1.0, 1.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, 1.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, -1.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, -1.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + ], + vec![ + 0, 1, 2, 2, 3, 0, // top + 4, 5, 6, 6, 7, 4, // bottom + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // front + 20, 21, 22, 22, 23, 20, // back + ], + ObjectSettings::default(), + renderer, + ); +} + +/// Create a 3D UV Sphere +/// +/// ``` +/// details = (stacks, sectors, radius) +/// example = (18, 36, 1.0) +/// ``` +pub fn uv_sphere( + name: impl StringBuffer, + details: (usize, usize, f32), + renderer: &mut Renderer, + objects: &mut ObjectStorage, +) { + let sectors = details.1 as f32; + let stacks = details.0 as f32; + let length_inv = 1. / details.2; + let sector_step = 2. * PI / sectors; + let stack_step = PI / stacks; + + let mut vertices: Vec = Vec::with_capacity(details.0 * details.1); + let mut indices: Vec = Vec::with_capacity(details.0 * details.1 * 2 * 3); + + for i in 0..details.0 + 1 { + let stack_angle = PI / 2.0 - (i as f32) * stack_step; + let xy: f32 = details.2 * stack_angle.cos(); + let z: f32 = details.2 * stack_angle.sin(); + + for j in 0..details.1 + 1 { + let sector_angle = (j as f32) * sector_step; + let x: f32 = xy * sector_angle.cos(); + let y: f32 = xy * sector_angle.sin(); + + vertices.push(Vertex { + position: [x, y, z].into(), + uv: [(j as f32) / sectors, (i as f32) / stacks], + normal: [x * length_inv, y * length_inv, z * length_inv], + }); + } + } + for i in 0..details.0 { + let mut k1 = i * (details.1 + 1); + let mut k2 = k1 + details.1 + 1; + for _j in 0..details.1 { + if i != 0 { + indices.push(k1 as UnsignedIntType); + indices.push(k2 as UnsignedIntType); + indices.push((k1 + 1) as UnsignedIntType); + } + if i != details.0 - 1 { + indices.push((k1 + 1) as UnsignedIntType); + indices.push(k2 as UnsignedIntType); + indices.push((k2 + 1) as UnsignedIntType); + } + k1 += 1; + k2 += 1; + } + } + + objects.new_object( + name.clone(), + vertices, + indices, + ObjectSettings::default(), + renderer, + ); +} diff --git a/crates/blue_engine_core/src/primitive_shapes.rs b/crates/blue_engine_core/src/primitive_shapes.rs deleted file mode 100644 index 762325f..0000000 --- a/crates/blue_engine_core/src/primitive_shapes.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// Predefined 3D shapes -pub mod three_dimensions; -/// Predefined 2D shapes -pub mod two_dimensions; -pub use three_dimensions::*; -pub use two_dimensions::*; diff --git a/crates/blue_engine_core/src/primitive_shapes/three_dimensions.rs b/crates/blue_engine_core/src/primitive_shapes/three_dimensions.rs deleted file mode 100644 index 69cb3bc..0000000 --- a/crates/blue_engine_core/src/primitive_shapes/three_dimensions.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::{ - ObjectSettings, ObjectStorage, Renderer, StringBuffer, UnsignedIntType, Vector2, Vector3, - Vertex, -}; -use std::f32::consts::PI; - -/// Creates a 3D cube -pub fn cube(name: impl StringBuffer, renderer: &mut Renderer, objects: &mut ObjectStorage) { - objects.new_object( - name.clone(), - vec![ - // Front Face - Vertex { - position: Vector3::new(-1.0, -1.0, 1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, 1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, 1.0, 1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, 1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - // Back Face - Vertex { - position: Vector3::new(-1.0, 1.0, -1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, 1.0, -1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, -1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, -1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - // Right face - Vertex { - position: Vector3::new(1.0, -1.0, -1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, 1.0, -1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, 1.0, 1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, 1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - // Left face - Vertex { - position: Vector3::new(-1.0, -1.0, 1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, 1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, -1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, -1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - // Top face - Vertex { - position: Vector3::new(1.0, 1.0, -1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, -1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, 1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, 1.0, 1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - // Bottom face - Vertex { - position: Vector3::new(1.0, -1.0, 1.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, 1.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, -1.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, -1.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - ], - vec![ - 0, 1, 2, 2, 3, 0, // top - 4, 5, 6, 6, 7, 4, // bottom - 8, 9, 10, 10, 11, 8, // right - 12, 13, 14, 14, 15, 12, // left - 16, 17, 18, 18, 19, 16, // front - 20, 21, 22, 22, 23, 20, // back - ], - ObjectSettings::default(), - renderer, - ); -} - -/// Create a 3D UV Sphere -/// -/// ``` -/// details = (stacks, sectors, radius) -/// example = (18, 36, 1.0) -/// ``` -pub fn uv_sphere( - name: impl StringBuffer, - details: (usize, usize, f32), - renderer: &mut Renderer, - objects: &mut ObjectStorage, -) { - let sectors = details.1 as f32; - let stacks = details.0 as f32; - let length_inv = 1. / details.2; - let sector_step = 2. * PI / sectors; - let stack_step = PI / stacks; - - let mut vertices: Vec = Vec::with_capacity(details.0 * details.1); - let mut indices: Vec = Vec::with_capacity(details.0 * details.1 * 2 * 3); - - for i in 0..details.0 + 1 { - let stack_angle = PI / 2.0 - (i as f32) * stack_step; - let xy: f32 = details.2 * stack_angle.cos(); - let z: f32 = details.2 * stack_angle.sin(); - - for j in 0..details.1 + 1 { - let sector_angle = (j as f32) * sector_step; - let x: f32 = xy * sector_angle.cos(); - let y: f32 = xy * sector_angle.sin(); - - vertices.push(Vertex { - position: [x, y, z].into(), - uv: Vector2::new((j as f32) / sectors, (i as f32) / stacks), - normal: Vector3::new(x * length_inv, y * length_inv, z * length_inv), - }); - } - } - for i in 0..details.0 { - let mut k1 = i * (details.1 + 1); - let mut k2 = k1 + details.1 + 1; - for _j in 0..details.1 { - if i != 0 { - indices.push(k1 as UnsignedIntType); - indices.push(k2 as UnsignedIntType); - indices.push((k1 + 1) as UnsignedIntType); - } - if i != details.0 - 1 { - indices.push((k1 + 1) as UnsignedIntType); - indices.push(k2 as UnsignedIntType); - indices.push((k2 + 1) as UnsignedIntType); - } - k1 += 1; - k2 += 1; - } - } - - objects.new_object( - name.clone(), - vertices, - indices, - ObjectSettings::default(), - renderer, - ); -} diff --git a/crates/blue_engine_core/src/primitive_shapes/two_dimensions.rs b/crates/blue_engine_core/src/primitive_shapes/two_dimensions.rs deleted file mode 100644 index 7f0ee0c..0000000 --- a/crates/blue_engine_core/src/primitive_shapes/two_dimensions.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Blue Engine by Elham Aryanpur - * - * The license is same as the one on the root. - */ - -/* - * For the sake of example we never use Vector3::default() or Vector3::x_axis() or any axis. - */ - -use crate::{ - header::{ObjectSettings, Vertex}, - ObjectStorage, Renderer, StringBuffer, Vector2, Vector3, -}; - -/// Creates a 2D triangle -pub fn triangle( - name: impl StringBuffer, - settings: ObjectSettings, - renderer: &mut Renderer, - objects: &mut ObjectStorage, -) { - objects.new_object( - name.clone(), - vec![ - Vertex { - position: Vector3::new(0.0, 1.0, 0.0), - uv: Vector2::new(0.5, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, 0.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, 0.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - ], - vec![0, 1, 2], - settings, - renderer, - ); -} - -/// Creates a 2D square -pub fn square( - name: impl StringBuffer, - settings: ObjectSettings, - renderer: &mut Renderer, - objects: &mut ObjectStorage, -) { - objects.new_object( - name.clone(), - vec![ - Vertex { - position: Vector3::new(1.0, 1.0, 0.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, 0.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, 0.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, 0.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - ], - vec![2, 1, 0, 2, 0, 3], - settings, - renderer, - ); -} - -/// Create a 2D rectangle based on a width and height -pub fn rectangle( - width: f32, - height: f32, - name: impl StringBuffer, - settings: ObjectSettings, - renderer: &mut Renderer, - objects: &mut ObjectStorage, -) { - objects.new_object( - name.clone(), - vec![ - Vertex { - position: Vector3::new(width / 2.0, height / 2.0, 0.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(width / 2.0, -height / 2.0, 0.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-width / 2.0, -height / 2.0, 0.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-width / 2.0, height / 2.0, 0.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - ], - vec![2, 1, 0, 2, 0, 3], - settings, - renderer, - ); -} diff --git a/crates/blue_engine_core/src/render.rs b/crates/blue_engine_core/src/render.rs index 731afdb..ac186e7 100644 --- a/crates/blue_engine_core/src/render.rs +++ b/crates/blue_engine_core/src/render.rs @@ -1,23 +1,51 @@ -/* - * Blue Engine by Elham Aryanpur - * - * The license is same as the one on the root. -*/ - // ? ADD VISIBILITY TAGS FOR DIFFERENT RENDER PASS TO USE AND RENDER ONLY THE OBJECTS THEY NEED use crate::{ CameraContainer, ObjectStorage, PipelineData, - header::{Renderer, ShaderSettings, TextureData, uniform_type}, - utils::default_resources::{DEFAULT_COLOR, DEFAULT_MATRIX_4, DEFAULT_SHADER, DEFAULT_TEXTURE}, + prelude::{ShaderSettings, TextureData}, + utils::default_resources::{DEFAULT_COLOR, DEFAULT_SHADER, DEFAULT_TEXTURE}, }; +/// Main renderer class. this will contain all methods and data related to the renderer +#[derive(Debug)] +pub struct Renderer { + /// A [`wgpu::Surface`] represents a platform-specific surface + /// (e.g. a window) onto which rendered images may be presented. + pub surface: Option>, + /// Context for all of the gpu objects + pub instance: wgpu::Instance, + /// Handle to a physical graphics and/or compute device. + #[allow(unused)] + pub adapter: wgpu::Adapter, + /// Open connection to a graphics and/or compute device. + pub device: wgpu::Device, + /// Handle to a command queue on a device. + pub queue: wgpu::Queue, + /// Describes a [`wgpu::Surface`] + pub config: wgpu::SurfaceConfiguration, + /// The size of the window + pub size: winit::dpi::PhysicalSize, + /// The texture bind group layout + pub texture_bind_group_layout: wgpu::BindGroupLayout, + /// The uniform bind group layout + pub default_uniform_bind_group_layout: wgpu::BindGroupLayout, + /// The depth buffer, used to render object depth + pub depth_buffer: (wgpu::Texture, wgpu::TextureView, wgpu::Sampler), + /// The default data used within the renderer + pub default_data: Option<(crate::Textures, crate::Shaders, crate::UniformBuffers)>, + /// The camera used in the engine + pub camera: Option, + /// Background clear color + pub clear_color: wgpu::Color, + /// Scissor cut section of the screen to render to + /// (x, y, width, height) + pub scissor_rect: Option<(u32, u32, u32, u32)>, +} +unsafe impl Sync for Renderer {} +unsafe impl Send for Renderer {} + impl Renderer { /// Creates a new renderer. - /// - /// # Arguments - /// * `window` - The window to create the renderer for. - /// * `power_preference` - The power preference to use. pub(crate) async fn new( size: winit::dpi::PhysicalSize, settings: crate::WindowDescriptor, @@ -148,17 +176,12 @@ impl Renderer { if let Ok(default_texture) = self.build_texture( "Default Texture", TextureData::Bytes(DEFAULT_TEXTURE.to_vec()), - crate::header::TextureMode::Clamp, - //crate::header::TextureFormat::PNG + crate::prelude::TextureMode::Clamp, + //crate::prelude::TextureFormat::PNG ) { let default_uniform = self.build_uniform_buffer(&vec![ - self.build_uniform_buffer_part("Transformation Matrix", DEFAULT_MATRIX_4), - self.build_uniform_buffer_part( - "Color", - uniform_type::Array4 { - data: DEFAULT_COLOR, - }, - ), + self.build_uniform_buffer_part("Transformation Matrix", crate::Matrix4::IDENTITY), + self.build_uniform_buffer_part("Color", DEFAULT_COLOR), ]); let default_shader = self.build_shader( @@ -176,8 +199,6 @@ impl Renderer { } /// Resize the window. - /// # Arguments - /// * `new_size` - The new window size. pub(crate) fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { // check if new_size is non-zero if new_size.width != 0 && new_size.height != 0 { @@ -196,10 +217,6 @@ impl Renderer { } /// Render the scene. Returns the command encoder, the texture view, and the surface texture. - /// - /// # Arguments - /// * `objects` - The object storage. - /// * `camera` - The camera. pub(crate) fn pre_render( &mut self, objects: &ObjectStorage, @@ -303,7 +320,7 @@ impl Renderer { render_pass.set_vertex_buffer(1, i.instance_buffer.slice(..)); render_pass.set_index_buffer( vertex_buffer.index_buffer.slice(..), - #[cfg(feature = "u16")] + #[cfg(not(feature = "u32"))] wgpu::IndexFormat::Uint16, #[cfg(feature = "u32")] wgpu::IndexFormat::Uint32, @@ -331,10 +348,6 @@ impl Renderer { } /// Render the scene. - /// - /// # Arguments - /// * `encoder` - The command encoder. - /// * `frame` - The surface texture. pub(crate) fn render(&mut self, encoder: wgpu::CommandEncoder, frame: wgpu::SurfaceTexture) { // submit will accept anything that implements IntoIter self.queue.submit(std::iter::once(encoder.finish())); @@ -348,61 +361,34 @@ impl Renderer { } // =========================== Extract Pipeline Data =========================== -// I couldn't make them into one function, so here they are, four of them - -/// Get the pipeline vertex buffer. -fn get_pipeline_vertex_buffer<'a>( - data: &'a PipelineData, - objects: &'a ObjectStorage, -) -> Option<&'a crate::VertexBuffers> { - match data { - PipelineData::Copy(object_id) => { - let data = objects.get(object_id.as_str()); - if let Some(data) = data { - get_pipeline_vertex_buffer(&data.pipeline.vertex_buffer, objects) - } else { - None - } - } - PipelineData::Data(data) => Some(data), - } -} - -/// Get the pipeline shader. -fn get_pipeline_shader<'a>( - data: &'a PipelineData, - objects: &'a ObjectStorage, -) -> Option<&'a crate::Shaders> { - match data { - PipelineData::Copy(object_id) => { - let data = objects.get(object_id.as_str()); - if let Some(data) = data { - get_pipeline_shader(&data.pipeline.shader, objects) - } else { - None +macro_rules! gen_pipeline { + ($function_name:ident, $buffer_type:ty, $buffer_field:ident) => { + fn $function_name<'a>( + data: &'a PipelineData<$buffer_type>, + objects: &'a ObjectStorage, + ) -> Option<&'a $buffer_type> { + match data { + PipelineData::Copy(object_id) => { + let data = objects.get(object_id.as_str()); + if let Some(data) = data { + $function_name(&data.pipeline.$buffer_field, objects) + } else { + None + } + } + PipelineData::Data(data) => Some(data), } } - PipelineData::Data(data) => Some(data), - } + }; } -/// Get the pipeline texture. -fn get_pipeline_texture<'a>( - data: &'a PipelineData, - objects: &'a ObjectStorage, -) -> Option<&'a crate::Textures> { - match data { - PipelineData::Copy(object_id) => { - let data = objects.get(object_id.as_str()); - if let Some(data) = data { - get_pipeline_texture(&data.pipeline.texture, objects) - } else { - None - } - } - PipelineData::Data(data) => Some(data), - } -} +gen_pipeline!( + get_pipeline_vertex_buffer, + crate::VertexBuffers, + vertex_buffer +); +gen_pipeline!(get_pipeline_shader, crate::Shaders, shader); +gen_pipeline!(get_pipeline_texture, crate::Textures, texture); /// Get the pipeline uniform_buffer. fn get_pipeline_uniform_buffer<'a>( diff --git a/crates/blue_engine_core/src/utils/camera.rs b/crates/blue_engine_core/src/utils/camera.rs index fa5a02e..8821dd2 100644 --- a/crates/blue_engine_core/src/utils/camera.rs +++ b/crates/blue_engine_core/src/utils/camera.rs @@ -4,19 +4,82 @@ * The license is same as the one on the root. */ +use super::default_resources::OPENGL_TO_WGPU_MATRIX; use crate::{ - header::{uniform_type::Matrix, Camera, Renderer, Vector3}, - CameraContainer, Projection, + Matrix4, UniformBuffers, + prelude::{Renderer, Vector3}, }; use winit::dpi::PhysicalSize; -use super::default_resources::{DEFAULT_MATRIX_4, OPENGL_TO_WGPU_MATRIX}; +/// Container for the projection used by the camera +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum Projection { + /// Perspective projection + /// + /// This is the default project used by the video games and majority of graphics + Perspective { + /// The field of view + fov: f32, + }, + /// Orthographic projection + /// + /// This projection gives you a 2D view of the scene + Orthographic { + /// The size of the view + zoom: f32, + }, +} + +/// Container for the camera feature. The settings here are needed for +/// algebra equations needed for camera vision and movement. Please leave it to the renderer to handle +#[derive(Debug)] +pub struct Camera { + /// The position of the camera in 3D space + pub position: Vector3, + /// The target at which the camera should be looking + pub target: Vector3, + /// The up vector of the camera. This defines the elevation of the camera + pub up: Vector3, + /// The resolution of the camera view + pub resolution: (f32, f32), //maybe this should be a Vector2i + /// The projection of the camera + pub projection: Projection, + /// The closest view of camera + pub near: f32, + /// The furthest view of camera + pub far: f32, + /// The final data that will be sent to GPU + pub view_data: Matrix4, + // For checking and rebuilding it's uniform buffer + pub(crate) changed: bool, + /// The uniform data of the camera to be sent to the gpu + pub uniform_data: UniformBuffers, + /// The position and target of the camera + pub(crate) add_position_and_target: bool, +} +unsafe impl Send for Camera {} +unsafe impl Sync for Camera {} + +/// Container for Cameras +/// +/// This allows for different objects have a different camera perspective. +#[derive(Debug)] +pub struct CameraContainer { + /// The list of cameras + // Arc is used instead of String for performance + pub cameras: std::collections::HashMap, Camera>, +} +crate::macros::impl_deref_field!( + CameraContainer, + std::collections::HashMap, Camera>, + cameras +); impl Camera { /// Creates a new camera. this should've been automatically done at the time of creating an engine pub fn new(window_size: PhysicalSize, renderer: &mut Renderer) -> Self { let camera_uniform = renderer.build_uniform_buffer(&[ - renderer.build_uniform_buffer_part("Camera Uniform", DEFAULT_MATRIX_4) + renderer.build_uniform_buffer_part("Camera Uniform", crate::Matrix4::IDENTITY) ]); let mut camera = Self { @@ -29,7 +92,7 @@ impl Camera { }, near: 0.1, far: 100.0, - view_data: DEFAULT_MATRIX_4.to_im(), + view_data: Matrix4::IDENTITY, changed: true, uniform_data: camera_uniform.0, add_position_and_target: false, @@ -50,7 +113,7 @@ impl Camera { /// Updates the view uniform matrix that decides how camera works pub fn build_view_orthographic_matrix(&mut self) { let view = self.build_view_matrix(); - let ortho = nalgebra_glm::ortho( + let ortho = Matrix4::orthographic_rh( 0f32, self.resolution.0, 0f32, @@ -63,8 +126,8 @@ impl Camera { } /// Returns a matrix uniform buffer from camera data that can be sent to GPU - pub fn camera_uniform_buffer(&self) -> Matrix { - Matrix::from_im(self.view_data) + pub fn camera_uniform_buffer(&self) -> Matrix4 { + self.view_data } /// Sets the position of camera @@ -141,25 +204,25 @@ impl Camera { } /// Builds a view matrix for camera projection - pub fn build_view_matrix(&self) -> nalgebra_glm::Mat4 { - nalgebra_glm::look_at_rh( - &self.position.into(), - &if self.add_position_and_target { + pub fn build_view_matrix(&self) -> Matrix4 { + Matrix4::look_at_rh( + self.position.into(), + if self.add_position_and_target { (self.position + self.target).into() } else { self.target.into() }, - &self.up.into(), + self.up.into(), ) } /// Builds a projection matrix for camera - pub fn build_projection_matrix(&self) -> nalgebra_glm::Mat4 { + pub fn build_projection_matrix(&self) -> Matrix4 { let aspect = self.resolution.0 / self.resolution.1; match self.projection { crate::Projection::Perspective { fov } => { - nalgebra_glm::perspective(aspect, fov, self.near, self.far) + Matrix4::perspective_rh(aspect, fov, self.near, self.far) } crate::Projection::Orthographic { zoom } => { let width = zoom; @@ -170,7 +233,7 @@ impl Camera { let bottom = height * -0.5; let top = height * 0.5; - nalgebra_glm::ortho(left, right, bottom, top, self.near, self.far) + Matrix4::orthographic_rh(left, right, bottom, top, self.near, self.far) } } } @@ -199,9 +262,9 @@ impl CameraContainer { } } /// Returns a matrix uniform buffer from camera data that can be sent to GPU - pub fn camera_uniform_buffer(&self) -> Option { + pub fn camera_uniform_buffer(&self) -> Option { if let Some(main_camera) = self.cameras.get("main") { - Some(Matrix::from_im(main_camera.view_data)) + Some(main_camera.view_data) } else { None } @@ -271,7 +334,7 @@ impl CameraContainer { } } /// Builds a view matrix for camera projection - pub fn build_view_matrix(&self) -> Option { + pub fn build_view_matrix(&self) -> Option { if let Some(main_camera) = self.cameras.get("main") { Some(main_camera.build_view_matrix()) } else { @@ -279,7 +342,7 @@ impl CameraContainer { } } /// Builds a projection matrix for camera - pub fn build_projection_matrix(&self) -> Option { + pub fn build_projection_matrix(&self) -> Option { if let Some(main_camera) = self.cameras.get("main") { Some(main_camera.build_projection_matrix()) } else { diff --git a/crates/blue_engine_core/src/utils/current_input.rs b/crates/blue_engine_core/src/utils/current_input.rs index 11f4b10..ea0c0c7 100644 --- a/crates/blue_engine_core/src/utils/current_input.rs +++ b/crates/blue_engine_core/src/utils/current_input.rs @@ -1,3 +1,5 @@ +// taken from -- https://github.com/rukai/winit_input_helper + use winit::event::{DeviceEvent, ElementState, MouseButton, MouseScrollDelta, WindowEvent}; use winit::keyboard::{Key, PhysicalKey}; diff --git a/crates/blue_engine_core/src/utils/default_resources.rs b/crates/blue_engine_core/src/utils/default_resources.rs index 93741ae..d8772e6 100644 --- a/crates/blue_engine_core/src/utils/default_resources.rs +++ b/crates/blue_engine_core/src/utils/default_resources.rs @@ -36,24 +36,13 @@ pub const DEFAULT_TEXTURE: &[u8] = &[ ]; /// The default color used for each object -pub const DEFAULT_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 0.0]; - -/// A default matrix 4x4 used in the engine -pub const DEFAULT_MATRIX_4: crate::header::uniform_type::Matrix = - crate::header::uniform_type::Matrix { - data: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - ], - }; +pub const DEFAULT_COLOR: crate::Vector4 = crate::Vector4::new(1.0, 1.0, 1.0, 1.0); /// A transformation matrix used to convert opengl projections to wgpu #[rustfmt::skip] -pub const OPENGL_TO_WGPU_MATRIX: nalgebra_glm::Mat4 = nalgebra_glm::Mat4::new( - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0, -); +pub const OPENGL_TO_WGPU_MATRIX: crate::Matrix4 = crate::Matrix4 { + x_axis: crate::Vector4::new(1.0, 0.0, 0.0, 0.0), + y_axis: crate::Vector4::new(0.0, 1.0, 0.0, 0.0), + z_axis: crate::Vector4::new(0.0, 0.0, 0.5, 0.5), + w_axis: crate::Vector4::new(0.0, 0.0, 0.0, 1.0), +}; diff --git a/crates/blue_engine_core/src/utils.rs b/crates/blue_engine_core/src/utils/mod.rs similarity index 50% rename from crates/blue_engine_core/src/utils.rs rename to crates/blue_engine_core/src/utils/mod.rs index 3e3badb..1006009 100644 --- a/crates/blue_engine_core/src/utils.rs +++ b/crates/blue_engine_core/src/utils/mod.rs @@ -1,16 +1,8 @@ -/* - * Blue Engine by Elham Aryanpur - * - * The license is same as the one on the root. -*/ - -/// The camera utilities -pub mod camera; -/// default resources used in the engine -pub mod default_resources; - -// taken from -- https://github.com/rukai/winit_input_helper -/// input helper -pub mod winit_input_helper; - -mod current_input; +/// The camera utilities +pub mod camera; +/// Input wrapping +mod current_input; +/// default resources used in the engine +pub mod default_resources; +/// input helper +pub mod winit_input_helper; diff --git a/crates/blue_engine_core/src/vector.rs b/crates/blue_engine_core/src/vector.rs deleted file mode 100644 index d909842..0000000 --- a/crates/blue_engine_core/src/vector.rs +++ /dev/null @@ -1,983 +0,0 @@ -use std::ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Neg, Sub, SubAssign}; - -use crate::{ - header::{Vector2, Vector3}, - RotateAmount, RotateAxis, -}; -use bytemuck::Pod; - -// Constructors -impl Vector3 { - /// A vector with all components set to 0. - pub const ZERO: Self = Self::new(0.0, 0.0, 0.0); - /// A vector with all components set to 1. - pub const ONE: Self = Self::new(1.0, 1.0, 1.0); - /// A vector with all components set to 0 except for the x component, which is set to 1. - pub const UNIT_X: Self = Self::new(1.0, 0.0, 0.0); - /// A vector with all components set to 0 except for the y component, which is set to 1. - pub const UNIT_Y: Self = Self::new(0.0, 1.0, 0.0); - /// A vector with all components set to 0 except for the z component, which is set to 1. - pub const UNIT_Z: Self = Self::new(0.0, 0.0, 1.0); - - /// Create a new 3D position with the given coordinates - pub const fn new(x: f32, y: f32, z: f32) -> Self { - Self { x, y, z } - } - /// Returns a vector with all components set to 0 except for the x component, which is set to 1. - pub const fn x_axis() -> Self { - Self::new(1.0, 0.0, 0.0) - } - /// Returns a vector with all components set to 0 except for the y component, which is set to 1. - pub const fn y_axis() -> Self { - Self::new(0.0, 1.0, 0.0) - } - /// Returns a vector with all components set to 0 except for the z component, which is set to 1. - pub const fn z_axis() -> Self { - Self::new(0.0, 0.0, 1.0) - } - /// Returns a 2D vector with the x and y coordinates of the 3D vector - pub const fn xy(&self) -> Vector2 { - Vector2::new(self.x, self.y) - } - /// Returns a 2D vector with the x and z coordinates of the 3D vector - pub const fn xz(&self) -> Vector2 { - Vector2::new(self.x, self.z) - } - /// Returns a 2D vector with the y and z coordinates of the 3D vector - pub const fn yz(&self) -> Vector2 { - Vector2::new(self.y, self.z) - } -} - -// Methods -impl Vector3 { - /// Returns a new vector with all components in absolute values (i.e. positive). - pub fn abs(&self) -> Self { - Self { - x: self.x.abs(), - y: self.y.abs(), - z: self.z.abs(), - } - } - /// Returns the unsigned minimum angle to the given vector, in radians. - pub fn angle_to(&self, to: Self) -> f32 { - let dot = self.dot(to); - let len = self.length() * to.length(); - (dot / len).acos() - } - /// Returns the vector "bounced off" from a plane defined by the given normal ``n``. - /// - ///> Note: bounce performs the operation that most engines and frameworks call ``reflect()``. - pub fn bounce(&self, n: Self) -> Self { - *self - n * 2.0 * self.dot(n) - } - /// Returns a new vector with all components rounded up (towards positive infinity). - pub fn ceil(&self) -> Self { - Self { - x: self.x.ceil(), - y: self.y.ceil(), - z: self.z.ceil(), - } - } - /// Returns a new vector with all components clamped between the components of ``min`` and ``max`` - pub fn clamp(&self, min: f32, max: f32) -> Self { - Self { - x: self.x.clamp(min, max), - y: self.y.clamp(min, max), - z: self.z.clamp(min, max), - } - } - /// Returns a new vector with all components clamped between ``min`` and ``max`` - pub fn clampf(&self, min: f32, max: f32) -> Self { - Self { - x: self.x.clamp(min, max), - y: self.y.clamp(min, max), - z: self.z.clamp(min, max), - } - } - /// Returns the cross product of this vector and ``with``. - /// - /// This returns a vector perpendicular to both ``self`` and ``with``, which would be the normal vector of the plane defined by the two vectors. - /// As there are two such vectors, in opposite directions, this method returns the vector defined by a right-handed coordinate system. - /// If the two vectors are parallel this returns an empty vector, making it useful for testing if two vectors are parallel. - pub const fn cross(&self, with: Self) -> Self { - Self { - x: self.y * with.z - self.z * with.y, - y: self.z * with.x - self.x * with.z, - z: self.x * with.y - self.y * with.x, - } - } - /// Returns the normalized vector pointing from this vector to ``to``. This is equivalent to using ``(b - a).normalized()``. - pub fn direction_to(&self, to: Self) -> Self { - (to - *self).normalize() - } - /// Returns the squared distance between this vector and ``to``. - /// - /// This method runs faster than [``Vector3::distance_to``], so prefer it if you need to compare vectors or need the squared distance for some formula. - pub fn distance_squared_to(&self, to: Self) -> f32 { - (*self - to).length_squared() - } - /// Returns the distance between this vector and ``to``. - pub fn distance_to(&self, to: Self) -> f32 { - (*self - to).length() - } - /// Returns the dot product of this vector and ``with``. This can be used to compare the angle between two vectors. For example, this can be used to determine whether an enemy is facing the player. - /// - /// The dot product will be ``0`` for a right angle (90 degrees), greater than 0 for angles narrower than 90 degrees and lower than 0 for angles wider than 90 degrees. - /// - /// When using unit (normalized) vectors, the result will always be between ``-1.0`` (180 degree angle) when the vectors are facing opposite directions, and ``1.0`` (0 degree angle) when the vectors are aligned. - /// - ///> Note: ``a.dot(b)`` is equivalent to ``b.dot(a)``. - pub fn dot(&self, with: Self) -> f32 { - self.x * with.x + self.y * with.y + self.z * with.z - } - /// Returns a new vector with all components rounded down (towards negative infinity). - pub fn floor(&self) -> Self { - Self { - x: self.x.floor(), - y: self.y.floor(), - z: self.z.floor(), - } - } - /// Returns the inverse of the vector. This is the same as: - ///```rs - /// Vector3 { - /// x: 1.0 / self.x, - /// y: 1.0 / self.y, - /// z: 1.0 / self.z - /// } - /// ``` - pub const fn inverse(&self) -> Self { - Self { - x: 1.0 / self.x, - y: 1.0 / self.y, - z: 1.0 / self.z, - } - } - /// Returns true if the vector is normalized, i.e. its length is approximately equal to 1. - #[must_use] - pub fn is_normalized(&self) -> bool { - (self.x * self.x + self.y * self.y + self.z * self.z - 1.0).abs() < 0.0001 - } - /// Returns the length (magnitude) of this vector. - pub fn length(&self) -> f32 { - (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() - } - /// Returns the squared length (squared magnitude) of this vector. - /// - /// This method runs faster than [`Vector3::length`], so prefer it if you need to compare vectors or need the squared distance for some formula. - pub const fn length_squared(&self) -> f32 { - self.x * self.x + self.y * self.y + self.z * self.z - } - /// Returns the result of the linear interpolation between this vector and ``to`` by amount ``weight``. - /// ``weight`` is on the range of ``0.0`` to ``1.0``, representing the amount of interpolation. - pub fn lerp(&self, to: Self, weight: f32) -> Self { - *self * (1.0 - weight) + to * weight - } - /// Returns the vector with a maximum length by limiting its length to ``length``. - pub fn limit_length(&self, max_length: f32) -> Self { - let length = self.length(); - if length > max_length { - *self * (max_length / length) - } else { - *self - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector3 { - /// x: self.x.max(with.x), - /// y: self.y.max(with.y), - /// z: self.z.max(with.z), - /// } - /// ``` - pub fn max(&self, with: Vector3) -> Self { - Self { - x: self.x.max(with.x), - y: self.y.max(with.y), - z: self.z.max(with.z), - } - } - /// Returns the component-wise maximum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector3 { - /// x: self.x.max(with), - /// y: self.y.max(with), - /// z: self.z.max(with), - /// } - /// ``` - pub fn maxf(&self, with: f32) -> Self { - Self { - x: self.x.max(with), - y: self.y.max(with), - z: self.z.max(with), - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector3 { - /// x: self.x.min(with.x), - /// y: self.y.min(with.y), - /// z: self.z.min(with.z), - /// } - /// ``` - pub fn min(&self, with: Vector3) -> Self { - Self { - x: self.x.min(with.x), - y: self.y.min(with.y), - z: self.z.min(with.z), - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector3 { - /// x: self.x.min(with), - /// y: self.y.min(with), - /// z: self.z.min(with), - /// } - /// ``` - pub fn minf(&self, with: f32) -> Self { - Self { - x: self.x.min(with), - y: self.y.min(with), - z: self.z.min(with), - } - } - /// Normalize the vector (make the length of the vector 1) - pub fn normalize(&self) -> Self { - let length = (self.x * self.x + self.y * self.y + self.z * self.z).sqrt(); - Self { - x: self.x / length, - y: self.y / length, - z: self.z / length, - } - } - /// Returns a new vector with all components rounded to the nearest integer, with halfway cases rounded away from zero. - pub fn round(&self) -> Self { - Self { - x: self.x.round(), - y: self.y.round(), - z: self.z.round(), - } - } - /// Rotates the vector around the given axis by the given angle. - pub fn rotate(&self, axis: RotateAxis, angle: RotateAmount) -> Self { - let angle = match angle { - RotateAmount::Degrees(d) => d.to_radians(), - RotateAmount::Radians(r) => r, - }; - match axis { - RotateAxis::X => { - let cos = angle.cos(); - let sin = angle.sin(); - Self { - x: self.x, - y: self.y * cos - self.z * sin, - z: self.y * sin + self.z * cos, - } - } - RotateAxis::Y => { - let cos = angle.cos(); - let sin = angle.sin(); - Self { - x: self.x * cos + self.z * sin, - y: self.y, - z: -self.x * sin + self.z * cos, - } - } - RotateAxis::Z => { - let cos = angle.cos(); - let sin = angle.sin(); - Self { - x: self.x * cos - self.y * sin, - y: self.x * sin + self.y * cos, - z: self.z, - } - } - } - } -} - -unsafe impl Send for Vector3 {} -unsafe impl Sync for Vector3 {} - -unsafe impl Pod for Vector3 {} - -impl Add for Vector3 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Self { - x: self.x + other.x, - y: self.y + other.y, - z: self.z + other.z, - } - } -} - -impl Add for Vector3 { - type Output = Self; - - fn add(self, scalar: f32) -> Self { - Self { - x: self.x + scalar, - y: self.y + scalar, - z: self.z + scalar, - } - } -} - -impl AddAssign for Vector3 { - fn add_assign(&mut self, other: Self) { - self.x += other.x; - self.y += other.y; - self.z += other.z; - } -} - -impl AddAssign for Vector3 { - fn add_assign(&mut self, scalar: f32) { - self.x += scalar; - self.y += scalar; - self.z += scalar; - } -} - -impl Sub for Vector3 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Self { - x: self.x - other.x, - y: self.y - other.y, - z: self.z - other.z, - } - } -} - -impl Sub for Vector3 { - type Output = Self; - - fn sub(self, scalar: f32) -> Self { - Self { - x: self.x - scalar, - y: self.y - scalar, - z: self.z - scalar, - } - } -} - -impl SubAssign for Vector3 { - fn sub_assign(&mut self, other: Self) { - self.x -= other.x; - self.y -= other.y; - self.z -= other.z; - } -} - -impl SubAssign for Vector3 { - fn sub_assign(&mut self, scalar: f32) { - self.x -= scalar; - self.y -= scalar; - self.z -= scalar; - } -} - -impl Mul for Vector3 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - Self { - x: self.x * other.x, - y: self.y * other.y, - z: self.z * other.z, - } - } -} - -impl Mul for Vector3 { - type Output = Self; - - fn mul(self, scalar: f32) -> Self { - Self { - x: self.x * scalar, - y: self.y * scalar, - z: self.z * scalar, - } - } -} - -impl MulAssign for Vector3 { - fn mul_assign(&mut self, other: Self) { - self.x *= other.x; - self.y *= other.y; - self.z *= other.z; - } -} - -impl MulAssign for Vector3 { - fn mul_assign(&mut self, scalar: f32) { - self.x *= scalar; - self.y *= scalar; - self.z *= scalar; - } -} - -impl Div for Vector3 { - type Output = Self; - - fn div(self, other: Self) -> Self { - Self { - x: self.x / other.x, - y: self.y / other.y, - z: self.z / other.z, - } - } -} - -impl Div for Vector3 { - type Output = Self; - - fn div(self, scalar: f32) -> Self { - Self { - x: self.x / scalar, - y: self.y / scalar, - z: self.z / scalar, - } - } -} - -impl DivAssign for Vector3 { - fn div_assign(&mut self, other: Self) { - self.x /= other.x; - self.y /= other.y; - self.z /= other.z; - } -} - -impl DivAssign for Vector3 { - fn div_assign(&mut self, scalar: f32) { - self.x /= scalar; - self.y /= scalar; - self.z /= scalar; - } -} - -impl Index for Vector3 { - type Output = f32; - - fn index(&self, index: usize) -> &Self::Output { - match index { - 0 => &self.x, - 1 => &self.y, - 2 => &self.z, - _ => panic!("Index out of bounds"), - } - } -} - -impl From<[f32; 3]> for Vector3 { - fn from(pos: [f32; 3]) -> Self { - Self { - x: pos[0], - y: pos[1], - z: pos[2], - } - } -} - -impl From for [f32; 3] { - fn from(pos: Vector3) -> Self { - [pos.x, pos.y, pos.z] - } -} - -impl From<(f32, f32, f32)> for Vector3 { - fn from(pos: (f32, f32, f32)) -> Self { - Self { - x: pos.0, - y: pos.1, - z: pos.2, - } - } -} - -impl From for (f32, f32, f32) { - fn from(pos: Vector3) -> Self { - (pos.x, pos.y, pos.z) - } -} - -impl From for Vector3 { - fn from(pos: nalgebra_glm::Vec3) -> Self { - Self { - x: pos.x, - y: pos.y, - z: pos.z, - } - } -} - -impl From for nalgebra_glm::Vec3 { - fn from(pos: Vector3) -> Self { - nalgebra_glm::vec3(pos.x, pos.y, pos.z) - } -} - -impl Neg for Vector3 { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: -self.x, - y: -self.y, - z: -self.z, - } - } -} - -// Constructors -impl Vector2 { - /// A vector with all components set to 0. - pub const ZERO: Self = Self::new(0.0, 0.0); - /// A vector with all components set to 1. - pub const ONE: Self = Self::new(1.0, 1.0); - /// A vector with all components set to 0 except for the x component, which is set to 1. - pub const UNIT_X: Self = Self::new(1.0, 0.0); - /// A vector with all components set to 0 except for the y component, which is set to 1. - pub const UNIT_Y: Self = Self::new(0.0, 1.0); - - /// Create a new 2D position with the given coordinates - pub const fn new(x: f32, y: f32) -> Self { - Self { x, y } - } - /// Returns a vector with all components set to 0 except for the x component, which is set to 1. - pub const fn x_axis() -> Self { - Self::new(1.0, 0.0) - } - /// Returns a vector with all components set to 0 except for the y component, which is set to 1. - pub const fn y_axis() -> Self { - Self::new(0.0, 1.0) - } -} - -// Methods -impl Vector2 { - /// Returns a new vector with all components in absolute values (i.e. positive). - pub fn abs(&self) -> Self { - Self { - x: self.x.abs(), - y: self.y.abs(), - } - } - /// Returns the unsigned minimum angle to the given vector, in radians. - pub fn angle_to(&self, to: Self) -> f32 { - let dot = self.dot(to); - let len = self.length() * to.length(); - (dot / len).acos() - } - /// Returns the vector "bounced off" from a plane defined by the given normal ``n``. - /// - ///> Note: bounce performs the operation that most engines and frameworks call ``reflect()``. - pub fn bounce(&self, n: Self) -> Self { - *self - n * 2.0 * self.dot(n) - } - /// Returns a new vector with all components rounded up (towards positive infinity). - pub fn ceil(&self) -> Self { - Self { - x: self.x.ceil(), - y: self.y.ceil(), - } - } - /// Returns a new vector with all components clamped between the components of ``min`` and ``max`` - pub fn clamp(&self, min: f32, max: f32) -> Self { - Self { - x: self.x.clamp(min, max), - y: self.y.clamp(min, max), - } - } - /// Returns a new vector with all components clamped between ``min`` and ``max`` - pub fn clampf(&self, min: f32, max: f32) -> Self { - Self { - x: self.x.clamp(min, max), - y: self.y.clamp(min, max), - } - } - /// Returns the cross product of this vector and `with`. - /// - /// This returns a scalar representing the magnitude of the vector perpendicular to the plane defined by the two vectors. - /// If the two vectors are parallel, this returns `0.0`, making it useful for testing if two vectors are parallel. - pub const fn cross(&self, with: Self) -> f32 { - self.x * with.y - self.y * with.x - } - /// Returns the normalized vector pointing from this vector to ``to``. This is equivalent to using ``(b - a).normalized()``. - pub fn direction_to(&self, to: Self) -> Self { - (to - *self).normalize() - } - /// Returns the squared distance between this vector and ``to``. - /// - /// This method runs faster than [``Vector2::distance_to``], so prefer it if you need to compare vectors or need the squared distance for some formula. - pub fn distance_squared_to(&self, to: Self) -> f32 { - (*self - to).length_squared() - } - /// Returns the distance between this vector and ``to``. - pub fn distance_to(&self, to: Self) -> f32 { - (*self - to).length() - } - /// Returns the dot product of this vector and ``with``. This can be used to compare the angle between two vectors. For example, this can be used to determine whether an enemy is facing the player. - /// - /// The dot product will be ``0`` for a right angle (90 degrees), greater than 0 for angles narrower than 90 degrees and lower than 0 for angles wider than 90 degrees. - /// - /// When using unit (normalized) vectors, the result will always be between ``-1.0`` (180 degree angle) when the vectors are facing opposite directions, and ``1.0`` (0 degree angle) when the vectors are aligned. - /// - ///> Note: ``a.dot(b)`` is equivalent to ``b.dot(a)``. - pub fn dot(&self, with: Self) -> f32 { - self.x * with.x + self.y * with.y - } - /// Returns a new vector with all components rounded down (towards negative infinity). - pub fn floor(&self) -> Self { - Self { - x: self.x.floor(), - y: self.y.floor(), - } - } - /// Returns the inverse of the vector. This is the same as: - ///```rs - /// Vector3 { - /// x: 1.0 / self.x, - /// y: 1.0 / self.y, - /// z: 1.0 / self.z - /// } - /// ``` - pub const fn inverse(&self) -> Self { - Self { - x: 1.0 / self.x, - y: 1.0 / self.y, - } - } - /// Returns true if the vector is normalized, i.e. its length is approximately equal to 1. - #[must_use] - pub fn is_normalized(&self) -> bool { - (self.x * self.x + self.y * self.y - 1.0).abs() < 0.0001 - } - /// Returns the length (magnitude) of this vector. - pub fn length(&self) -> f32 { - (self.x * self.x + self.y * self.y).sqrt() - } - /// Returns the squared length (squared magnitude) of this vector. - /// - /// This method runs faster than [`Vector2::length`], so prefer it if you need to compare vectors or need the squared distance for some formula. - pub const fn length_squared(&self) -> f32 { - self.x * self.x + self.y * self.y - } - /// Returns the result of the linear interpolation between this vector and ``to`` by amount ``weight``. - /// ``weight`` is on the range of ``0.0`` to ``1.0``, representing the amount of interpolation. - pub fn lerp(&self, to: Self, weight: f32) -> Self { - *self * (1.0 - weight) + to * weight - } - /// Returns the vector with a maximum length by limiting its length to ``length``. - pub fn limit_length(&self, max_length: f32) -> Self { - let length = self.length(); - if length > max_length { - *self * (max_length / length) - } else { - *self - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector2 { - /// x: self.x.max(with.x), - /// y: self.y.max(with.y), - /// } - /// ``` - pub fn max(&self, with: Vector3) -> Self { - Self { - x: self.x.max(with.x), - y: self.y.max(with.y), - } - } - /// Returns the component-wise maximum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector2 { - /// x: self.x.max(with), - /// y: self.y.max(with), - /// } - /// ``` - pub fn maxf(&self, with: f32) -> Self { - Self { - x: self.x.max(with), - y: self.y.max(with), - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector2 { - /// x: self.x.min(with.x), - /// y: self.y.min(with.y), - /// } - /// ``` - pub fn min(&self, with: Vector3) -> Self { - Self { - x: self.x.min(with.x), - y: self.y.min(with.y), - } - } - /// Returns the component-wise minimum of ``self`` and ``with``, equivalent to: - /// ```rs - /// Vector2 { - /// x: self.x.min(with), - /// y: self.y.min(with), - /// } - /// ``` - pub fn minf(&self, with: f32) -> Self { - Self { - x: self.x.min(with), - y: self.y.min(with), - } - } - /// Normalize the vector (make the length of the vector 1) - pub fn normalize(&self) -> Self { - let length = (self.x * self.x + self.y * self.y).sqrt(); - Self { - x: self.x / length, - y: self.y / length, - } - } - /// Returns a new vector with all components rounded to the nearest integer, with halfway cases rounded away from zero. - pub fn round(&self) -> Self { - Self { - x: self.x.round(), - y: self.y.round(), - } - } - /// Rotates the vector around the given axis by the given angle. - pub fn rotate(&self, axis: RotateAxis, angle: RotateAmount) -> Self { - // axis might be unused - let angle = match angle { - RotateAmount::Degrees(d) => d.to_radians(), - RotateAmount::Radians(r) => r, - }; - - let (sin, cos) = angle.sin_cos(); - - match axis { - RotateAxis::Z => Self { - x: self.x * cos - self.y * sin, - y: self.x * sin + self.y * cos, - }, - _ => *self, // For Vector2, only Z-axis rotation makes sense - } - } -} - -unsafe impl Send for Vector2 {} -unsafe impl Sync for Vector2 {} - -unsafe impl Pod for Vector2 {} - -impl Add for Vector2 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Self { - x: self.x + other.x, - y: self.y + other.y, - } - } -} - -impl Add for Vector2 { - type Output = Self; - - fn add(self, scalar: f32) -> Self { - Self { - x: self.x + scalar, - y: self.y + scalar, - } - } -} - -impl AddAssign for Vector2 { - fn add_assign(&mut self, other: Self) { - self.x += other.x; - self.y += other.y; - } -} - -impl AddAssign for Vector2 { - fn add_assign(&mut self, scalar: f32) { - self.x += scalar; - self.y += scalar; - } -} - -impl Sub for Vector2 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Self { - x: self.x - other.x, - y: self.y - other.y, - } - } -} - -impl Sub for Vector2 { - type Output = Self; - - fn sub(self, scalar: f32) -> Self { - Self { - x: self.x - scalar, - y: self.y - scalar, - } - } -} - -impl SubAssign for Vector2 { - fn sub_assign(&mut self, other: Self) { - self.x -= other.x; - self.y -= other.y; - } -} - -impl SubAssign for Vector2 { - fn sub_assign(&mut self, scalar: f32) { - self.x -= scalar; - self.y -= scalar; - } -} - -impl Mul for Vector2 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - Self { - x: self.x * other.x, - y: self.y * other.y, - } - } -} - -impl Mul for Vector2 { - type Output = Self; - - fn mul(self, scalar: f32) -> Self { - Self { - x: self.x * scalar, - y: self.y * scalar, - } - } -} - -impl MulAssign for Vector2 { - fn mul_assign(&mut self, other: Self) { - self.x *= other.x; - self.y *= other.y; - } -} - -impl MulAssign for Vector2 { - fn mul_assign(&mut self, scalar: f32) { - self.x *= scalar; - self.y *= scalar; - } -} - -impl Div for Vector2 { - type Output = Self; - - fn div(self, other: Self) -> Self { - Self { - x: self.x / other.x, - y: self.y / other.y, - } - } -} - -impl Div for Vector2 { - type Output = Self; - - fn div(self, scalar: f32) -> Self { - Self { - x: self.x / scalar, - y: self.y / scalar, - } - } -} - -impl DivAssign for Vector2 { - fn div_assign(&mut self, other: Self) { - self.x /= other.x; - self.y /= other.y; - } -} - -impl DivAssign for Vector2 { - fn div_assign(&mut self, scalar: f32) { - self.x /= scalar; - self.y /= scalar; - } -} - -impl From<[f32; 2]> for Vector2 { - fn from(pos: [f32; 2]) -> Self { - Self { - x: pos[0], - y: pos[1], - } - } -} - -impl From for [f32; 2] { - fn from(pos: Vector2) -> Self { - [pos.x, pos.y] - } -} - -impl From<(f32, f32)> for Vector2 { - fn from(pos: (f32, f32)) -> Self { - Self { x: pos.0, y: pos.1 } - } -} - -impl From for (f32, f32) { - fn from(pos: Vector2) -> Self { - (pos.x, pos.y) - } -} - -impl From for Vector2 { - fn from(pos: nalgebra_glm::Vec2) -> Self { - Self { x: pos.x, y: pos.y } - } -} - -impl From for nalgebra_glm::Vec2 { - fn from(pos: Vector2) -> Self { - nalgebra_glm::vec2(pos.x, pos.y) - } -} - -impl Index for Vector2 { - type Output = f32; - - fn index(&self, index: usize) -> &Self::Output { - match index { - 0 => &self.x, - 1 => &self.y, - _ => panic!("Index out of bounds"), - } - } -} - -impl Neg for Vector2 { - type Output = Self; - - fn neg(self) -> Self { - Self { - x: -self.x, - y: -self.y, - } - } -} diff --git a/crates/blue_engine_core/src/window.rs b/crates/blue_engine_core/src/window.rs index 28b36cf..7ad9c11 100644 --- a/crates/blue_engine_core/src/window.rs +++ b/crates/blue_engine_core/src/window.rs @@ -5,8 +5,8 @@ */ use crate::{ - CameraContainer, ObjectStorage, Window, - header::{Engine, Renderer, WindowDescriptor}, + CameraContainer, ObjectStorage, + prelude::{Engine, Renderer}, }; use winit::{ @@ -16,6 +16,100 @@ use winit::{ window::WindowAttributes, }; +/// A wrapper for winit window to make it easier to use and more ergonomic. +#[derive(Debug)] +pub struct Window { + /// The winit window itself. + pub window: Option>, + /// Default attributes of the window + pub default_attributes: winit::window::WindowAttributes, + /// Whether the engine should close. + pub should_close: bool, +} +crate::macros::impl_deref_field!( + Window, + Option>, + window +); + +/// Descriptor and settings for a window. +#[derive(Debug, Clone)] +pub struct WindowDescriptor { + /// The width of the window + pub width: u32, + /// The height of the window + pub height: u32, + /// The title of the window + pub title: &'static str, + /// Should the window contain the keys like minimize, maximize, or resize? + pub decorations: bool, + /// Should the window be resizable + pub resizable: bool, + /// Define how much power should the app ask for + pub power_preference: crate::PowerPreference, + /// The backend to use for the draw + pub backends: crate::Backends, + /// The features to be enabled on a backend + /// + /// read more at [wgpu::Features] + pub features: crate::wgpu::Features, + /// Controls how the events are processed + /// + /// read more at [winit::event_loop::ControlFlow] + pub control_flow: crate::winit::event_loop::ControlFlow, + /// The presentation mode of renderer for things like VSync + /// + /// read more at [wgpu::PresentMode] + pub present_mode: crate::wgpu::PresentMode, + /// Limits to be required based on the generation of the GPU and the API. + /// + /// read more at [wgpu::Limits] + pub limits: crate::wgpu::Limits, + /// The alpha mode which specifies how the alpha channel of + /// the textures should be handled during compositing. + pub alpha_mode: crate::wgpu::CompositeAlphaMode, + /// The desired frame latency. + /// + /// read more at [wgpu::SurfaceConfiguration::desired_maximum_frame_latency] + pub desired_maximum_frame_latency: u32, + /// How the memory should be utilized + /// + /// read more at [wgpu::MemoryHints] + pub memory_hints: crate::wgpu::MemoryHints, +} +impl std::default::Default for WindowDescriptor { + /// Will quickly create a window with default settings + fn default() -> Self { + let backends = crate::Backends::all(); + Self { + width: 800, + height: 600, + title: "Blue Engine", + decorations: true, + resizable: true, + power_preference: crate::PowerPreference::LowPower, + backends, + features: if backends == wgpu::Backends::VULKAN { + wgpu::Features::POLYGON_MODE_LINE | wgpu::Features::POLYGON_MODE_POINT + } else if backends + .contains(wgpu::Backends::VULKAN | wgpu::Backends::METAL | wgpu::Backends::DX12) + { + wgpu::Features::POLYGON_MODE_LINE + } else { + wgpu::Features::empty() + }, + control_flow: crate::winit::event_loop::ControlFlow::Poll, + present_mode: crate::wgpu::PresentMode::AutoNoVsync, + limits: crate::wgpu::Limits::default(), + alpha_mode: crate::wgpu::CompositeAlphaMode::Auto, + desired_maximum_frame_latency: 2, + memory_hints: crate::MemoryHints::Performance, + } + } +} +unsafe impl Send for WindowDescriptor {} +unsafe impl Sync for WindowDescriptor {} + impl Engine { /// Creates a new window in current thread using default settings. pub fn new() -> Result { @@ -339,6 +433,19 @@ impl ApplicationHandler for Engine { } } +macro_rules! gen_window_component_functions { + ($fn_name:ident, $name:ident, $data_type:ty) => { + /// see [winit::window::Window::$fn_name] + pub fn $fn_name(&mut self, value: $data_type) { + if let Some(window) = self.window.as_mut() { + window.$fn_name(value); + } else { + self.default_attributes.$name = value; + } + } + }; +} + impl Window { /// create a new window pub fn new(default_attributes: winit::window::WindowAttributes) -> Self { @@ -357,23 +464,28 @@ impl Window { // ====================================================== WINDOW SETTERS ====================================================== // //MARK: SETTERS - /// see [winit::window::Window::set_min_inner_size] - pub fn set_min_inner_size(&mut self, value: Option) { - if let Some(window) = self.window.as_mut() { - window.set_min_inner_size(value); - } else { - self.default_attributes.min_inner_size = value; - } - } - - /// see [winit::window::Window::set_max_inner_size] - pub fn set_max_inner_size(&mut self, value: Option) { - if let Some(window) = self.window.as_mut() { - window.set_max_inner_size(value); - } else { - self.default_attributes.max_inner_size = value; - } - } + gen_window_component_functions!(set_min_inner_size, min_inner_size, Option); + gen_window_component_functions!(set_max_inner_size, max_inner_size, Option); + gen_window_component_functions!(set_resizable, resizable, bool); + gen_window_component_functions!( + set_enabled_buttons, + enabled_buttons, + winit::window::WindowButtons + ); + gen_window_component_functions!(set_maximized, maximized, bool); + gen_window_component_functions!(set_visible, visible, bool); + gen_window_component_functions!(set_transparent, transparent, bool); + gen_window_component_functions!(set_blur, blur, bool); + gen_window_component_functions!(set_decorations, decorations, bool); + gen_window_component_functions!(set_window_icon, window_icon, Option); + gen_window_component_functions!( + set_resize_increments, + resize_increments, + Option + ); + gen_window_component_functions!(set_content_protected, content_protected, bool); + gen_window_component_functions!(set_window_level, window_level, winit::window::WindowLevel); + gen_window_component_functions!(set_cursor, cursor, winit::window::Cursor); /// see [winit::window::Window::set_outer_position] pub fn set_outer_position(&mut self, value: winit::dpi::Position) { @@ -384,24 +496,6 @@ impl Window { } } - /// see [winit::window::Window::set_resizable] - pub fn set_resizable(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_resizable(value); - } else { - self.default_attributes.resizable = value; - } - } - - /// see [winit::window::Window::set_enabled_buttons] - pub fn set_enabled_buttons(&mut self, value: winit::window::WindowButtons) { - if let Some(window) = self.window.as_mut() { - window.set_enabled_buttons(value); - } else { - self.default_attributes.enabled_buttons = value; - } - } - /// see [winit::window::Window::set_title] pub fn set_title(&mut self, value: String) { if let Some(window) = self.window.as_mut() { @@ -411,61 +505,7 @@ impl Window { } } - /// see [winit::window::Window::set_maximized] - pub fn set_maximized(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_maximized(value); - } else { - self.default_attributes.maximized = value; - } - } - - /// see [winit::window::Window::set_visible] - pub fn set_visible(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_visible(value); - } else { - self.default_attributes.visible = value; - } - } - - /// see [winit::window::Window::set_transparent] - pub fn set_transparent(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_transparent(value); - } else { - self.default_attributes.transparent = value; - } - } - - /// see [winit::window::Window::set_blur] - pub fn set_blur(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_blur(value); - } else { - self.default_attributes.blur = value; - } - } - - /// see [winit::window::Window::set_decorations] - pub fn set_decorations(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_decorations(value); - } else { - self.default_attributes.decorations = value; - } - } - - /// see [winit::window::Window::set_window_icon] - pub fn set_window_icon(&mut self, value: Option) { - if let Some(window) = self.window.as_mut() { - window.set_window_icon(value); - } else { - self.default_attributes.window_icon = value; - } - } - - /// see [winit::window::Window::set_preferred_theme] + /// see [winit::window::Window::set_theme] pub fn set_preferred_theme(&mut self, value: Option) { if let Some(window) = self.window.as_mut() { window.set_theme(value); @@ -474,42 +514,6 @@ impl Window { } } - /// see [winit::window::Window::set_resize_increments] - pub fn set_resize_increments(&mut self, value: Option) { - if let Some(window) = self.window.as_mut() { - window.set_resize_increments(value); - } else { - self.default_attributes.resize_increments = value; - } - } - - /// see [winit::window::Window::set_content_protected] - pub fn set_content_protected(&mut self, value: bool) { - if let Some(window) = self.window.as_mut() { - window.set_content_protected(value); - } else { - self.default_attributes.content_protected = value; - } - } - - /// see [winit::window::Window::set_window_level] - pub fn set_window_level(&mut self, value: winit::window::WindowLevel) { - if let Some(window) = self.window.as_mut() { - window.set_window_level(value); - } else { - self.default_attributes.window_level = value; - } - } - - /// see [winit::window::Window::set_cursor] - pub fn set_cursor(&mut self, value: winit::window::Cursor) { - if let Some(window) = self.window.as_mut() { - window.set_cursor(value); - } else { - self.default_attributes.cursor = value; - } - } - /// see [winit::window::Window::set_fullscreen] pub fn set_fullscreen_borderless(&mut self, value: bool) { let full_screen_result = if value { diff --git a/crates/blue_engine_dynamic/Cargo.toml b/crates/blue_engine_dynamic/Cargo.toml index 9aa3492..dfd1435 100644 --- a/crates/blue_engine_dynamic/Cargo.toml +++ b/crates/blue_engine_dynamic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blue_engine_dynamic" -version = "0.6.5" +version = "0.7.0" authors = ["Elham Aryanpur "] edition = "2024" description = "USE blue_engine THIS IS FOR INTERNAL USE" @@ -11,16 +11,14 @@ name = "blue_engine_dynamic" crate-type = ["dylib"] [features] -default = ["debug", "u16"] +default = ["debug"] debug = ["blue_engine_core/debug"] android = ["blue_engine_core/android"] android_native_activity = ["blue_engine_core/android_native_activity"] android_game_activity = ["blue_engine_core/android_game_activity"] -# using u16 for indices and others -u16 = ["blue_engine_core/u16"] # using u32 for indices and others u32 = ["blue_engine_core/u32"] [dependencies] -blue_engine_core = { version = "0.6.5" } +blue_engine_core = { version = "0.7.0" } # blue_engine_core = { path = "../blue_engine_core" } diff --git a/crates/docs/main.md b/crates/docs/main.md index f5a1b3c..0bba811 100644 --- a/crates/docs/main.md +++ b/crates/docs/main.md @@ -14,7 +14,7 @@ A basic program in Blue Engine is as follow: ```rust use blue_engine::{ - header::{ Engine, ObjectSettings }, + prelude::{ Engine, ObjectSettings }, primitive_shapes::triangle }; diff --git a/examples/camera/rotate_around.rs b/examples/camera/rotate_around.rs index 1e7cc83..2a8c43c 100644 --- a/examples/camera/rotate_around.rs +++ b/examples/camera/rotate_around.rs @@ -5,9 +5,9 @@ */ use blue_engine::{ - header::{Engine, ObjectSettings, ShaderSettings}, - primitive_shapes::square, Vector3, + prelude::{Engine, ObjectSettings, ShaderSettings}, + primitive_shapes::square, }; fn main() { diff --git a/examples/dev/README.md b/examples/dev/README.md deleted file mode 100644 index 5a72b23..0000000 --- a/examples/dev/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# - -this folder and it's files are for testing and development only. diff --git a/examples/dev/dev.rs b/examples/dev/dev.rs deleted file mode 100644 index 3d9982d..0000000 --- a/examples/dev/dev.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![allow(unused)] - -use blue_engine::{ - primitive_shapes::{cube, square, triangle, uv_sphere}, - uniform_type::Matrix, - utils::default_resources::DEFAULT_MATRIX_4, - Engine, Instance, ObjectSettings, PolygonMode, PowerPreference, RotateAxis, ShaderSettings, - TextureData, Vector3, Vertex, WindowDescriptor, -}; - -fn main() { - let mut engine = Engine::new_config(blue_engine::WindowDescriptor { - power_preference: blue_engine::PowerPreference::HighPerformance, - present_mode: blue_engine::wgpu::PresentMode::Fifo, - ..Default::default() - }) - .expect("win"); - - //let test_instance = Instance::default(); - //println!("{:?}", test_instance.to_raw()); - - let texture = engine - .renderer - .build_texture( - "background", - TextureData::Path("resources/BlueLogoDiscord.png".to_string()), - blue_engine::TextureMode::Clamp, - ) - .unwrap(); - let texture2 = engine - .renderer - .build_texture( - "background", - TextureData::Path("resources/BlueLogoDiscord.png".to_string()), - blue_engine::TextureMode::Clamp, - ) - .unwrap(); - - let texture3 = engine - .renderer - .build_texture( - "background", - TextureData::Path("resources/BlueLogoDiscord.png".to_string()), - blue_engine::TextureMode::Clamp, - ) - .unwrap(); - - square( - "main", - ObjectSettings::default(), - &mut engine.renderer, - &mut engine.objects, - ); - - engine.objects.get_mut("main").unwrap().set_texture(texture); - engine - .objects - .get_mut("main") - .unwrap() - .set_position(Vector3::new(-1f32, 0f32, 0f32)); - - square( - "alt", - ObjectSettings::default(), - &mut engine.renderer, - &mut engine.objects, - ); - engine.objects.get_mut("alt").unwrap().set_texture(texture2); - engine - .objects - .get_mut("alt") - .unwrap() - .set_position(Vector3::new(0.2f32, 0f32, 0.001f32)); - - square( - "alt2", - ObjectSettings::default(), - &mut engine.renderer, - &mut engine.objects, - ); - engine - .objects - .get_mut("alt2") - .unwrap() - .set_texture(texture3); - engine - .objects - .get_mut("alt2") - .unwrap() - .set_position(Vector3::new(-0.2f32, 0f32, 0.001f32)); - - engine.window.set_fullscreen_borderless(true); - - let speed = -0.05; - - let mut last_time = std::time::Instant::now(); - let mut frames = 0; - engine - .update_loop(move |renderer, _window, objects, input, camera, plugins| { - // calculate FPS - let current_time = std::time::Instant::now(); - frames += 1; - if current_time - last_time >= std::time::Duration::from_secs(1) { - println!("{}ms/frame", 1000f32 / frames as f32); - frames = 0; - last_time = current_time; - } - - let sprite = objects.get_mut("alt").unwrap(); - - if input.key_held(blue_engine::KeyCode::Escape) { - _window.close_engine(); - } - if input.key_held(blue_engine::KeyCode::ArrowUp) { - sprite.set_position(( - sprite.position.x, - sprite.position.y - speed, - sprite.position.z, - )); - //lm.ambient_color.data = [1f32, 1f32, 1f32, 1f32]; - } - if input.key_held(blue_engine::KeyCode::ArrowDown) { - sprite.set_position(( - sprite.position.x, - sprite.position.y + speed, - sprite.position.z, - )); - //lm.ambient_color.data = [0.1f32, 0.1f32, 0.1f32, 1f32]; - } - - if input.key_held(blue_engine::KeyCode::ArrowLeft) { - sprite.set_position(( - sprite.position.x + speed, - sprite.position.y, - sprite.position.z, - )); - } - if input.key_held(blue_engine::KeyCode::ArrowRight) { - sprite.set_position(( - sprite.position.x - speed, - sprite.position.y, - sprite.position.z, - )); - } - - if input.key_held(blue_engine::KeyCode::KeyE) { - sprite.set_position(( - sprite.position.x, - sprite.position.y, - sprite.position.z + speed, - )); - } - if input.key_held(blue_engine::KeyCode::KeyQ) { - sprite.set_position(( - sprite.position.x, - sprite.position.y, - sprite.position.z - speed, - )); - } - }) - .expect("Error during update loop"); - - println!("Engine have been closed") -} diff --git a/examples/dev/resource/BlueLogoDiscord.png b/examples/dev/resource/BlueLogoDiscord.png deleted file mode 100644 index 9c579d2..0000000 Binary files a/examples/dev/resource/BlueLogoDiscord.png and /dev/null differ diff --git a/examples/dev/resource/DefaultTexture.png b/examples/dev/resource/DefaultTexture.png deleted file mode 100644 index 9265b9a..0000000 Binary files a/examples/dev/resource/DefaultTexture.png and /dev/null differ diff --git a/examples/dev/resource/JetBrainsMono-Medium.ttf b/examples/dev/resource/JetBrainsMono-Medium.ttf deleted file mode 100644 index ae9a99e..0000000 Binary files a/examples/dev/resource/JetBrainsMono-Medium.ttf and /dev/null differ diff --git a/examples/dev/resource/default_shader.wgsl b/examples/dev/resource/default_shader.wgsl deleted file mode 100644 index b0a8994..0000000 --- a/examples/dev/resource/default_shader.wgsl +++ /dev/null @@ -1,46 +0,0 @@ -// Vertex Stage - -[[block]] -struct VertexUniforms { - camera_matrix: mat4x4; -}; -[[group(1), binding(0)]] -var vertex_uniforms: VertexUniforms; - -struct VertexInput { - [[location(0)]] position: vec3; - [[location(1)]] texture_coordinates: vec2; -}; - -struct VertexOutput { - [[builtin(position)]] position: vec4; - [[location(0)]] texture_coordinates: vec2; -}; - -[[stage(vertex)]] -fn main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; - out.position = vertex_uniforms.camera_matrix * vec4(input.position, 1.0); - out.texture_coordinates = input.texture_coordinates; - return out; -} - -// Fragment Stage - -[[block]] -struct FragmentUniforms { - color: vec4; -}; -[[group(1), binding(1)]] -var fragment_uniforms: FragmentUniforms; - -[[group(0), binding(0)]] -var texture_diffuse: texture_2d; - -[[group(0), binding(1)]] -var sampler_diffuse: sampler; - -[[stage(fragment)]] -fn main(input: VertexOutput) -> [[location(0)]] vec4 { - return textureSample(texture_diffuse, sampler_diffuse, input.texture_coordinates) * fragment_uniforms.color; -} \ No newline at end of file diff --git a/examples/shapes/cube.rs b/examples/shapes/cube.rs index 3f2029e..3048a9c 100644 --- a/examples/shapes/cube.rs +++ b/examples/shapes/cube.rs @@ -4,7 +4,7 @@ * The license is same as the one on the root. */ -use blue_engine::{header::Engine, primitive_shapes::cube}; +use blue_engine::{prelude::Engine, primitive_shapes::cube}; fn main() { let mut engine = Engine::new().expect("win"); diff --git a/examples/shapes/square.rs b/examples/shapes/square.rs index 899d0a7..ada13b1 100644 --- a/examples/shapes/square.rs +++ b/examples/shapes/square.rs @@ -7,35 +7,37 @@ */ use blue_engine::{ - header::{Engine, ObjectSettings, Vertex}, - StringBuffer, Vector2, Vector3, + StringBuffer, + prelude::{Engine, ObjectSettings, Vertex}, }; pub fn square(name: impl StringBuffer, engine: &mut Engine) { + let vertices = vec![ + Vertex { + position: [1.0, 1.0, 0.0], + uv: [1.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [1.0, -1.0, 0.0], + uv: [1.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, -1.0, 0.0], + uv: [0.0, 1.0], + normal: [0.0, 0.0, 0.0], + }, + Vertex { + position: [-1.0, 1.0, 0.0], + uv: [0.0, 0.0], + normal: [0.0, 0.0, 0.0], + }, + ]; + engine.objects.new_object( name, - vec![ - Vertex { - position: Vector3::new(1.0, 1.0, 0.0), - uv: Vector2::new(1.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(1.0, -1.0, 0.0), - uv: Vector2::new(1.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, -1.0, 0.0), - uv: Vector2::new(0.0, 1.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - Vertex { - position: Vector3::new(-1.0, 1.0, 0.0), - uv: Vector2::new(0.0, 0.0), - normal: Vector3::new(0.0, 0.0, 0.0), - }, - ], + vertices, vec![2, 1, 0, 2, 0, 3], ObjectSettings { camera_effect: None, diff --git a/examples/shapes/triangle.rs b/examples/shapes/triangle.rs index dcb42c7..dcb333b 100644 --- a/examples/shapes/triangle.rs +++ b/examples/shapes/triangle.rs @@ -7,7 +7,7 @@ */ use blue_engine::{ - header::{Engine, ObjectSettings}, + prelude::{Engine, ObjectSettings}, primitive_shapes::triangle, }; diff --git a/examples/utils/instancing.rs b/examples/utils/instancing.rs index 4eead37..03e6f27 100644 --- a/examples/utils/instancing.rs +++ b/examples/utils/instancing.rs @@ -7,7 +7,7 @@ */ use blue_engine::{ - header::{Engine, ObjectSettings}, + prelude::{Engine, ObjectSettings}, primitive_shapes::triangle, Instance, Vector3, }; diff --git a/examples/utils/signals.rs b/examples/utils/signals.rs index b5f6525..ba45993 100644 --- a/examples/utils/signals.rs +++ b/examples/utils/signals.rs @@ -5,7 +5,7 @@ */ use blue_engine::{ - header::{Engine, ObjectSettings}, + prelude::{Engine, ObjectSettings}, primitive_shapes::triangle, Signal, };