From 697e29e5ad4a09110180736294b8a1330a13eb6e Mon Sep 17 00:00:00 2001
From: HampusM <hampus@hampusmat.com>
Date: Fri, 1 Mar 2024 19:34:09 +0100
Subject: refactor(engine): create texture OpenGL objects when needed

---
 engine/src/opengl/texture.rs |  79 ++++++++--------------------
 engine/src/renderer/mod.rs   |  36 +++++++++++--
 engine/src/texture.rs        | 119 +++++++++++++++++--------------------------
 3 files changed, 101 insertions(+), 133 deletions(-)

(limited to 'engine')

diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs
index 56eb118..5d12729 100644
--- a/engine/src/opengl/texture.rs
+++ b/engine/src/opengl/texture.rs
@@ -1,7 +1,7 @@
 use std::ptr::null;
 
 use crate::opengl::currently_bound::CurrentlyBound;
-use crate::texture::Id;
+use crate::texture::{Id, Properties};
 use crate::vector::Vec2;
 
 #[derive(Debug)]
@@ -49,7 +49,24 @@ impl Texture
         }
     }
 
-    pub fn set_wrap(_: CurrentlyBound<Self>, wrapping: Wrapping)
+    pub fn apply_properties(&self, properties: &Properties)
+    {
+        self.bind(|texture_curr_bound| {
+            Texture::set_wrap(&texture_curr_bound, properties.wrap);
+
+            Texture::set_magnifying_filter(
+                &texture_curr_bound,
+                properties.magnifying_filter,
+            );
+
+            Texture::set_minifying_filter(
+                &texture_curr_bound,
+                properties.minifying_filter,
+            );
+        });
+    }
+
+    pub fn set_wrap(_: &CurrentlyBound<Self>, wrapping: Wrapping)
     {
         let wrapping_gl = wrapping.to_gl();
 
@@ -60,7 +77,7 @@ impl Texture
         }
     }
 
-    pub fn set_magnifying_filter(_: CurrentlyBound<Self>, filtering: Filtering)
+    pub fn set_magnifying_filter(_: &CurrentlyBound<Self>, filtering: Filtering)
     {
         let filtering_gl = filtering.to_gl();
 
@@ -74,7 +91,7 @@ impl Texture
         }
     }
 
-    pub fn set_minifying_filter(_: CurrentlyBound<Self>, filtering: Filtering)
+    pub fn set_minifying_filter(_: &CurrentlyBound<Self>, filtering: Filtering)
     {
         let filtering_gl = filtering.to_gl();
 
@@ -88,60 +105,6 @@ impl Texture
         }
     }
 
-    /// Creates a copy of the texture & the texture images.
-    ///
-    /// `src_offset` and `dst_offset` are source & destination offsets from the
-    /// bottom-left of the images.
-    ///
-    /// New mipmaps are generated using the largest mipmap.
-    pub fn copy(
-        &self,
-        dimensions: &Vec2<u32>,
-        pixel_data_format: PixelDataFormat,
-        src_offset: &Vec2<u32>,
-        dst_offset: &Vec2<u32>,
-    ) -> Self
-    {
-        let new_texture = Self::new();
-
-        new_texture.bind(|curr_bound| {
-            Self::alloc_image(&curr_bound, pixel_data_format, dimensions, None);
-
-            // Mipmap have to be generated since CopyImageSubData demands that the
-            // destination texture is completed
-            unsafe {
-                gl::GenerateMipmap(gl::TEXTURE_2D);
-            }
-        });
-
-        #[allow(clippy::cast_possible_wrap)]
-        unsafe {
-            gl::CopyImageSubData(
-                self.texture,
-                gl::TEXTURE_2D,
-                0,
-                src_offset.x as i32,
-                src_offset.y as i32,
-                0,
-                new_texture.texture,
-                gl::TEXTURE_2D,
-                0,
-                dst_offset.x as i32,
-                dst_offset.y as i32,
-                0,
-                dimensions.x as i32,
-                dimensions.y as i32,
-                1,
-            );
-        }
-
-        unsafe {
-            gl::GenerateMipmap(gl::TEXTURE_2D);
-        }
-
-        new_texture
-    }
-
     fn alloc_image(
         _: &CurrentlyBound<Self>,
         pixel_data_format: PixelDataFormat,
diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs
index 8977ac9..6c04323 100644
--- a/engine/src/renderer/mod.rs
+++ b/engine/src/renderer/mod.rs
@@ -24,11 +24,16 @@ use crate::opengl::shader::{
     Program as GlShaderProgram,
     Shader as GlShader,
 };
-use crate::opengl::texture::{set_active_texture_unit, TextureUnit};
+use crate::opengl::texture::{
+    set_active_texture_unit,
+    Texture as GlTexture,
+    TextureUnit,
+};
 use crate::opengl::vertex_array::{PrimitiveKind, VertexArray};
 use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability};
 use crate::projection::new_perspective;
 use crate::shader::Program as ShaderProgram;
+use crate::texture::{Id as TextureId, Texture};
 use crate::vector::{Vec2, Vec3};
 use crate::vertex::Vertex;
 
@@ -37,6 +42,7 @@ pub struct Renderer<CameraT>
 {
     camera: CameraT,
     shader_programs: HashMap<u64, GlShaderProgram>,
+    textures: HashMap<TextureId, GlTexture>,
 }
 
 impl<CameraT> Renderer<CameraT>
@@ -73,6 +79,7 @@ where
         Ok(Self {
             camera,
             shader_programs: HashMap::new(),
+            textures: HashMap::new(),
         })
     }
 
@@ -111,7 +118,7 @@ where
     }
 
     pub fn render<'obj>(
-        &self,
+        &mut self,
         objects: impl IntoIterator<Item = &'obj Object>,
         light_source: Option<&LightSource>,
         window_size: &WindowSize,
@@ -143,12 +150,17 @@ where
                 );
 
                 for (texture_id, texture) in obj.textures() {
+                    let gl_texture = self
+                        .textures
+                        .entry(*texture_id)
+                        .or_insert_with(|| create_gl_texture(texture));
+
                     let texture_unit = TextureUnit::from_texture_id(*texture_id)
                         .ok_or(Error::TextureIdIsInvalidTextureUnit)?;
 
                     set_active_texture_unit(texture_unit);
 
-                    texture.inner().bind(|_| {});
+                    gl_texture.bind(|_| {});
                 }
 
                 Self::draw_object(obj);
@@ -219,6 +231,24 @@ where
     }
 }
 
+fn create_gl_texture(texture: &Texture) -> GlTexture
+{
+    let gl_texture = GlTexture::new();
+
+    gl_texture.bind(|texture_curr_bound| {
+        GlTexture::generate(
+            &texture_curr_bound,
+            &texture.dimensions(),
+            texture.image().as_bytes(),
+            texture.pixel_data_format(),
+        );
+    });
+
+    gl_texture.apply_properties(texture.properties());
+
+    gl_texture
+}
+
 #[derive(Debug)]
 pub struct Renderable
 {
diff --git a/engine/src/texture.rs b/engine/src/texture.rs
index f644b58..2d7ba51 100644
--- a/engine/src/texture.rs
+++ b/engine/src/texture.rs
@@ -1,10 +1,10 @@
 use std::path::Path;
 
 use image::io::Reader as ImageReader;
-use image::{DynamicImage, EncodableLayout, ImageError, Rgb, RgbImage};
+use image::{DynamicImage, ImageError, Rgb, RgbImage};
 
 use crate::color::Color;
-use crate::opengl::texture::{PixelDataFormat, Texture as InnerTexture};
+use crate::opengl::texture::PixelDataFormat;
 use crate::vector::Vec2;
 
 mod reexports
@@ -14,12 +14,13 @@ mod reexports
 
 pub use reexports::*;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Texture
 {
-    inner: InnerTexture,
+    image: DynamicImage,
     pixel_data_format: PixelDataFormat,
     dimensions: Vec2<u32>,
+    properties: Properties,
 }
 
 impl Texture
@@ -46,26 +47,14 @@ impl Texture
             }
         };
 
-        let inner = InnerTexture::new();
-
         let dimensions = Vec2 { x: image.width(), y: image.height() };
 
-        inner.bind(|texture_curr_bound| {
-            InnerTexture::generate(
-                &texture_curr_bound,
-                &dimensions,
-                image.as_bytes(),
-                pixel_data_format,
-            );
-        });
-
-        let me = Self { inner, pixel_data_format, dimensions };
-
-        me.set_wrap(Wrapping::Repeat);
-        me.set_magnifying_filter(Filtering::Linear);
-        me.set_minifying_filter(Filtering::Nearest);
-
-        Ok(me)
+        Ok(Self {
+            image,
+            pixel_data_format,
+            dimensions,
+            properties: Properties::default(),
+        })
     }
 
     #[must_use]
@@ -77,73 +66,37 @@ impl Texture
             Rgb([color.red, color.green, color.blue]),
         );
 
-        let inner = InnerTexture::new();
-
-        inner.bind(|texture_curr_bound| {
-            InnerTexture::generate(
-                &texture_curr_bound,
-                dimensions,
-                image.as_bytes(),
-                PixelDataFormat::Rgb,
-            );
-        });
-
-        let me = Self {
-            inner,
+        Self {
+            image: image.into(),
             pixel_data_format: PixelDataFormat::Rgb,
             dimensions: dimensions.clone(),
-        };
-
-        me.set_wrap(Wrapping::Repeat);
-        me.set_magnifying_filter(Filtering::Linear);
-        me.set_minifying_filter(Filtering::Nearest);
-
-        me
+            properties: Properties::default(),
+        }
     }
 
-    pub fn set_wrap(&self, wrapping: Wrapping)
+    pub fn properties(&self) -> &Properties
     {
-        self.inner.bind(|texture_curr_bound| {
-            InnerTexture::set_wrap(texture_curr_bound, wrapping);
-        });
+        &self.properties
     }
 
-    pub fn set_magnifying_filter(&self, filtering: Filtering)
+    pub fn properties_mut(&mut self) -> &mut Properties
     {
-        self.inner.bind(|texture_curr_bound| {
-            InnerTexture::set_magnifying_filter(texture_curr_bound, filtering);
-        });
+        &mut self.properties
     }
 
-    pub fn set_minifying_filter(&self, filtering: Filtering)
+    pub fn dimensions(&self) -> &Vec2<u32>
     {
-        self.inner.bind(|texture_curr_bound| {
-            InnerTexture::set_minifying_filter(texture_curr_bound, filtering);
-        });
+        &self.dimensions
     }
 
-    pub(crate) fn inner(&self) -> &InnerTexture
+    pub fn pixel_data_format(&self) -> PixelDataFormat
     {
-        &self.inner
+        self.pixel_data_format
     }
-}
 
-impl Clone for Texture
-{
-    fn clone(&self) -> Self
+    pub fn image(&self) -> &DynamicImage
     {
-        let inner = self.inner.copy(
-            &self.dimensions,
-            self.pixel_data_format,
-            &Vec2::ZERO,
-            &Vec2::ZERO,
-        );
-
-        Self {
-            inner,
-            pixel_data_format: self.pixel_data_format,
-            dimensions: self.dimensions.clone(),
-        }
+        &self.image
     }
 }
 
@@ -161,6 +114,28 @@ pub enum Error
     UnsupportedImageDataKind,
 }
 
+/// Texture properties
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Properties
+{
+    pub wrap: Wrapping,
+    pub magnifying_filter: Filtering,
+    pub minifying_filter: Filtering,
+}
+
+impl Default for Properties
+{
+    fn default() -> Self
+    {
+        Self {
+            wrap: Wrapping::Repeat,
+            magnifying_filter: Filtering::Linear,
+            minifying_filter: Filtering::Nearest,
+        }
+    }
+}
+
 /// Texture ID.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Id
-- 
cgit v1.2.3-18-g5258