From 9aafb51e0be1720019db1c3d0a294ce9a42653df Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 2 Nov 2023 20:22:33 +0100 Subject: feat(engine): add texturing --- Cargo.lock | 342 ++++++++++++++++++++++++++++++++++++++++++- engine/Cargo.toml | 1 + engine/fragment-color.glsl | 9 ++ engine/fragment-texture.glsl | 11 ++ engine/fragment.glsl | 9 -- engine/src/lib.rs | 1 + engine/src/object.rs | 43 +++++- engine/src/opengl/mod.rs | 1 + engine/src/opengl/texture.rs | 144 ++++++++++++++++++ engine/src/renderer/mod.rs | 44 +++--- engine/src/texture.rs | 100 +++++++++++++ engine/src/vector.rs | 2 +- engine/src/vertex.rs | 26 +++- engine/vertex.glsl | 3 + 14 files changed, 697 insertions(+), 39 deletions(-) create mode 100644 engine/fragment-color.glsl create mode 100644 engine/fragment-texture.glsl delete mode 100644 engine/fragment.glsl create mode 100644 engine/src/opengl/texture.rs create mode 100644 engine/src/texture.rs diff --git a/Cargo.lock b/Cargo.lock index 7010e6c..5e04147 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.1.1" @@ -11,13 +17,19 @@ dependencies = [ "memchr", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bindgen" version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags", + "bitflags 2.4.0", "cexpr", "clang-sys", "lazy_static", @@ -34,12 +46,36 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.83" @@ -75,6 +111,60 @@ dependencies = [ "libloading", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "cstr" version = "0.2.11" @@ -95,10 +185,11 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" name = "engine" version = "0.1.0" dependencies = [ - "bitflags", + "bitflags 2.4.0", "cstr", "gl", "glfw", + "image", "thiserror", "tracing", ] @@ -124,6 +215,50 @@ dependencies = [ "libc", ] +[[package]] +name = "exr" +version = "1.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "game-newest" version = "0.1.0" @@ -133,6 +268,16 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gl" version = "0.14.0" @@ -168,6 +313,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "home" version = "0.5.5" @@ -177,6 +331,34 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -195,6 +377,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.148" @@ -217,6 +405,16 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" @@ -229,12 +427,31 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + [[package]] name = "nom" version = "7.1.3" @@ -255,6 +472,36 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -279,6 +526,19 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "prettyplease" version = "0.2.15" @@ -298,6 +558,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.33" @@ -307,6 +576,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.9.6" @@ -348,13 +637,19 @@ version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", "windows-sys", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sharded-slab" version = "0.1.7" @@ -370,12 +665,27 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "syn" version = "2.0.38" @@ -417,6 +727,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "tracing" version = "0.1.39" @@ -486,6 +807,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "which" version = "4.4.2" @@ -591,3 +918,12 @@ name = "xml-rs" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 023c711..d3de446 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -13,3 +13,4 @@ gl = "0.14.0" bitflags = "2.4.0" cstr = "0.2.11" tracing = { version = "0.1.39", optional = true } +image = "0.24.7" diff --git a/engine/fragment-color.glsl b/engine/fragment-color.glsl new file mode 100644 index 0000000..8b209c4 --- /dev/null +++ b/engine/fragment-color.glsl @@ -0,0 +1,9 @@ +#version 330 core +out vec4 FragColor; + +in vec3 in_frag_color; + +void main() +{ + FragColor = vec4(in_frag_color, 1.0); +} diff --git a/engine/fragment-texture.glsl b/engine/fragment-texture.glsl new file mode 100644 index 0000000..a4d378b --- /dev/null +++ b/engine/fragment-texture.glsl @@ -0,0 +1,11 @@ +#version 330 core +out vec4 FragColor; + +in vec2 in_texture_coords; + +uniform sampler2D input_texture; + +void main() +{ + FragColor = texture(input_texture, in_texture_coords); +} diff --git a/engine/fragment.glsl b/engine/fragment.glsl deleted file mode 100644 index 8b209c4..0000000 --- a/engine/fragment.glsl +++ /dev/null @@ -1,9 +0,0 @@ -#version 330 core -out vec4 FragColor; - -in vec3 in_frag_color; - -void main() -{ - FragColor = vec4(in_frag_color, 1.0); -} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index ec969f8..9f82b26 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -19,6 +19,7 @@ mod transform; pub mod camera; pub mod color; pub mod object; +pub mod texture; pub mod vector; pub mod vertex; diff --git a/engine/src/object.rs b/engine/src/object.rs index f2591f2..a8cd076 100644 --- a/engine/src/object.rs +++ b/engine/src/object.rs @@ -5,16 +5,21 @@ use crate::opengl::shader::{ Shader, }; use crate::renderer::Renderable; +use crate::texture::Texture; use crate::transform::Transform; use crate::vector::Vec3; use crate::vertex::Vertex; +const FRAG_SHADER_COLOR: &str = include_str!("../fragment-color.glsl"); +const FRAG_SHADER_TEXTURE: &str = include_str!("../fragment-texture.glsl"); + #[derive(Debug)] pub struct Object { id: Id, renderable: Renderable, transform: Transform, + texture: Option, } impl Object @@ -42,6 +47,12 @@ impl Object self.transform.set_scaling(scaling); } + #[must_use] + pub fn texture(&self) -> Option<&Texture> + { + self.texture.as_ref() + } + pub(crate) fn renderable(&self) -> &Renderable { &self.renderable @@ -54,11 +65,12 @@ impl Object } /// Object builder. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default)] pub struct Builder { vertices: Vec, indices: Option>, + texture: Option, } impl Builder @@ -86,21 +98,35 @@ impl Builder self } + #[must_use] + pub fn texture(mut self, texture: Texture) -> Self + { + self.texture = Some(texture); + + self + } + /// Builds a new [`Object`]. /// /// # Errors /// Will return `Err` if: /// - Creating shaders fails /// - Linking the shader program fails - pub fn build(&self, id: Id) -> Result + pub fn build(self, id: Id) -> Result { let vertex_shader = Self::create_shader(ShaderKind::Vertex, include_str!("../vertex.glsl")) .map_err(Error::CreateVertexShaderFailed)?; - let fragment_shader = - Self::create_shader(ShaderKind::Fragment, include_str!("../fragment.glsl")) - .map_err(Error::CreateFragmentShaderFailed)?; + let fragment_shader = Self::create_shader( + ShaderKind::Fragment, + if self.texture.is_some() { + FRAG_SHADER_TEXTURE + } else { + FRAG_SHADER_COLOR + }, + ) + .map_err(Error::CreateFragmentShaderFailed)?; let shader_program = ShaderProgram::new(); @@ -116,7 +142,12 @@ impl Builder let transform = Transform::new(); - Ok(Object { id, renderable, transform }) + Ok(Object { + id, + renderable, + transform, + texture: self.texture, + }) } fn create_shader(kind: ShaderKind, source: &str) -> Result diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs index 4f4f96f..d58ca50 100644 --- a/engine/src/opengl/mod.rs +++ b/engine/src/opengl/mod.rs @@ -6,6 +6,7 @@ use crate::vector::Vec2; pub mod buffer; pub mod currently_bound; pub mod shader; +pub mod texture; pub mod vertex_array; mod util; diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs new file mode 100644 index 0000000..c7bf75b --- /dev/null +++ b/engine/src/opengl/texture.rs @@ -0,0 +1,144 @@ +use crate::opengl::currently_bound::CurrentlyBound; +use crate::vector::Vec2; + +#[derive(Debug)] +pub struct Texture +{ + texture: gl::types::GLuint, +} + +impl Texture +{ + pub fn new() -> Self + { + let mut texture = gl::types::GLuint::default(); + + unsafe { + gl::GenTextures(1, &mut texture); + }; + + Self { texture } + } + + pub fn bind(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>)) + { + unsafe { + gl::BindTexture(gl::TEXTURE_2D, self.texture); + } + + // SAFETY: The buffer object is bound above + let currently_bound = unsafe { CurrentlyBound::new() }; + + cb(currently_bound); + } + + pub fn generate(_: &CurrentlyBound, dimens: &Vec2, data: &[u8]) + { + #[allow(clippy::cast_possible_wrap)] + unsafe { + gl::TexImage2D( + gl::TEXTURE_2D, + 0, + gl::RGB as i32, + dimens.x as i32, + dimens.y as i32, + 0, + gl::RGB, + gl::UNSIGNED_BYTE, + data.as_ptr().cast(), + ); + + gl::GenerateMipmap(gl::TEXTURE_2D); + } + } + + pub fn set_wrap(_: CurrentlyBound, wrapping: Wrapping) + { + let wrapping_gl = wrapping.to_gl(); + + #[allow(clippy::cast_possible_wrap)] + unsafe { + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, wrapping_gl as i32); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, wrapping_gl as i32); + } + } + + pub fn set_magnifying_filter(_: CurrentlyBound, filtering: Filtering) + { + let filtering_gl = filtering.to_gl(); + + #[allow(clippy::cast_possible_wrap)] + unsafe { + gl::TexParameteri( + gl::TEXTURE_2D, + gl::TEXTURE_MAG_FILTER, + filtering_gl as i32, + ); + } + } + + pub fn set_minifying_filter(_: CurrentlyBound, filtering: Filtering) + { + let filtering_gl = filtering.to_gl(); + + #[allow(clippy::cast_possible_wrap)] + unsafe { + gl::TexParameteri( + gl::TEXTURE_2D, + gl::TEXTURE_MIN_FILTER, + filtering_gl as i32, + ); + } + } +} + +impl Drop for Texture +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteTextures(1, &self.texture); + } + } +} + +/// Texture wrapping. +#[derive(Debug, Clone, Copy)] +pub enum Wrapping +{ + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder, +} + +impl Wrapping +{ + fn to_gl(self) -> gl::types::GLenum + { + match self { + Self::Repeat => gl::REPEAT, + Self::MirroredRepeat => gl::MIRRORED_REPEAT, + Self::ClampToEdge => gl::CLAMP_TO_EDGE, + Self::ClampToBorder => gl::CLAMP_TO_BORDER, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Filtering +{ + Nearest, + Linear, +} + +impl Filtering +{ + fn to_gl(self) -> gl::types::GLenum + { + match self { + Self::Linear => gl::LINEAR, + Self::Nearest => gl::NEAREST, + } + } +} diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs index 6c56964..3691f76 100644 --- a/engine/src/renderer/mod.rs +++ b/engine/src/renderer/mod.rs @@ -116,23 +116,33 @@ pub fn render<'obj>( apply_transformation_matrices(obj, camera, window_size); - obj.renderable().vertex_arr.bind(|vert_arr_curr_bound| { - if let Some(index_info) = &obj.renderable().index_info { - VertexArray::draw_elements( - &vert_arr_curr_bound, - PrimitiveKind::Triangles, - 0, - index_info.cnt, - ); - } else { - VertexArray::draw_arrays( - &vert_arr_curr_bound, - PrimitiveKind::Triangles, - 0, - 3, - ); - } - }); + let draw = || { + obj.renderable().vertex_arr.bind(|vert_arr_curr_bound| { + if let Some(index_info) = &obj.renderable().index_info { + VertexArray::draw_elements( + &vert_arr_curr_bound, + PrimitiveKind::Triangles, + 0, + index_info.cnt, + ); + } else { + VertexArray::draw_arrays( + &vert_arr_curr_bound, + PrimitiveKind::Triangles, + 0, + 3, + ); + } + }); + }; + + if let Some(texture) = obj.texture() { + texture.inner().bind(|_| { + draw(); + }); + } else { + draw(); + } } } diff --git a/engine/src/texture.rs b/engine/src/texture.rs new file mode 100644 index 0000000..7874df4 --- /dev/null +++ b/engine/src/texture.rs @@ -0,0 +1,100 @@ +use std::path::Path; + +use image::io::Reader as ImageReader; +use image::{DynamicImage, ImageError}; + +use crate::opengl::texture::Texture as InnerTexture; +use crate::vector::Vec2; + +mod reexports +{ + pub use crate::opengl::texture::{Filtering, Wrapping}; +} + +pub use reexports::*; + +#[derive(Debug)] +pub struct Texture +{ + inner: InnerTexture, +} + +impl Texture +{ + /// Opens a texture image. + /// + /// # Errors + /// Will return `Err` if: + /// - Opening the image fails + /// - The image data is not 8-bit/color RGB + #[allow(clippy::new_without_default)] + pub fn open(path: &Path) -> Result + { + let image = ImageReader::open(path) + .map_err(Error::OpenImageFailed)? + .decode() + .map_err(Error::DecodeImageFailed)?; + + if !matches!(image, DynamicImage::ImageRgb8(_)) { + return Err(Error::UnsupportedImageDataKind); + } + + let inner = InnerTexture::new(); + + inner.bind(|texture_curr_bound| { + InnerTexture::generate( + &texture_curr_bound, + &Vec2 { x: image.width(), y: image.height() }, + image.as_bytes(), + ); + }); + + let me = Self { inner }; + + me.set_wrap(Wrapping::Repeat); + me.set_magnifying_filter(Filtering::Linear); + me.set_minifying_filter(Filtering::Nearest); + + Ok(me) + } + + pub fn set_wrap(&self, wrapping: Wrapping) + { + self.inner.bind(|texture_curr_bound| { + InnerTexture::set_wrap(texture_curr_bound, wrapping); + }); + } + + pub fn set_magnifying_filter(&self, filtering: Filtering) + { + self.inner.bind(|texture_curr_bound| { + InnerTexture::set_magnifying_filter(texture_curr_bound, filtering); + }); + } + + pub fn set_minifying_filter(&self, filtering: Filtering) + { + self.inner.bind(|texture_curr_bound| { + InnerTexture::set_minifying_filter(texture_curr_bound, filtering); + }); + } + + pub(crate) fn inner(&self) -> &InnerTexture + { + &self.inner + } +} + +/// Texture error. +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("Failed to open texture image")] + OpenImageFailed(#[source] std::io::Error), + + #[error("Failed to decode texture image")] + DecodeImageFailed(#[source] ImageError), + + #[error("Unsupported image data kind")] + UnsupportedImageDataKind, +} diff --git a/engine/src/vector.rs b/engine/src/vector.rs index e5947d5..00d6a6f 100644 --- a/engine/src/vector.rs +++ b/engine/src/vector.rs @@ -1,6 +1,6 @@ use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; -#[derive(Debug)] +#[derive(Debug, Default, Clone)] pub struct Vec2 { pub x: Value, diff --git a/engine/src/vertex.rs b/engine/src/vertex.rs index 62f629b..9df646f 100644 --- a/engine/src/vertex.rs +++ b/engine/src/vertex.rs @@ -1,7 +1,7 @@ use std::mem::size_of; use crate::color::Color; -use crate::vector::Vec3; +use crate::vector::{Vec2, Vec3}; #[derive(Debug, Clone, Default)] #[repr(C)] @@ -9,6 +9,7 @@ pub struct Vertex { pos: Vec3, color: Color, + texture_coords: Vec2, } #[derive(Debug, Default)] @@ -16,6 +17,7 @@ pub struct Builder { pos: Option>, color: Option>, + texture_coords: Vec2, } impl Builder @@ -23,7 +25,7 @@ impl Builder #[must_use] pub fn new() -> Self { - Self { pos: None, color: None } + Self::default() } #[must_use] @@ -42,13 +44,25 @@ impl Builder self } + #[must_use] + pub fn texture_coords(mut self, texture_coords: Vec2) -> Self + { + self.texture_coords = texture_coords; + + self + } + #[must_use] pub fn build(self) -> Option { let pos = self.pos?; let color = self.color?; - Some(Vertex { pos, color }) + Some(Vertex { + pos, + color, + texture_coords: self.texture_coords, + }) } } @@ -69,6 +83,12 @@ impl Vertex component_cnt: AttributeComponentCnt::Three, component_size: size_of::(), }, + Attribute { + index: 2, + component_type: AttributeComponentType::Float, + component_cnt: AttributeComponentCnt::Two, + component_size: size_of::(), + }, ] } } diff --git a/engine/vertex.glsl b/engine/vertex.glsl index b0a2a13..61782c2 100644 --- a/engine/vertex.glsl +++ b/engine/vertex.glsl @@ -1,8 +1,10 @@ #version 330 core layout (location = 0) in vec3 pos; layout (location = 1) in vec3 color; +layout (location = 2) in vec2 texture_coords; out vec3 in_frag_color; +out vec2 in_texture_coords; uniform mat4 model; uniform mat4 view; @@ -13,4 +15,5 @@ void main() gl_Position = projection * view * model * vec4(pos, 1.0); in_frag_color = color; + in_texture_coords = texture_coords; } -- cgit v1.2.3-18-g5258