diff options
author | HampusM <hampus@hampusmat.com> | 2025-09-19 16:36:57 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-10-02 16:55:33 +0200 |
commit | ea1d70c8c28e3b96da6264021fa1c62e28fcd8e4 (patch) | |
tree | 62ae9b75ee84602899b51483ed26fa664df36888 /opengl-bindings/src/texture.rs | |
parent | 0008b374c7f3a9ef6b30ea31a4a8c98bce64649f (diff) |
feat: add OpenGL bindings crate
Diffstat (limited to 'opengl-bindings/src/texture.rs')
-rw-r--r-- | opengl-bindings/src/texture.rs | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/opengl-bindings/src/texture.rs b/opengl-bindings/src/texture.rs new file mode 100644 index 0000000..1859beb --- /dev/null +++ b/opengl-bindings/src/texture.rs @@ -0,0 +1,236 @@ +use crate::data_types::Dimens; +use crate::CurrentContextWithFns; + +#[derive(Debug)] +pub struct Texture +{ + texture: crate::sys::types::GLuint, +} + +impl Texture +{ + #[must_use] + pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self + { + let mut texture = crate::sys::types::GLuint::default(); + + unsafe { + current_context.fns().CreateTextures( + crate::sys::TEXTURE_2D, + 1, + &raw mut texture, + ); + }; + + Self { texture } + } + + pub fn bind_to_texture_unit( + &self, + current_context: &CurrentContextWithFns<'_>, + texture_unit: u32, + ) + { + unsafe { + current_context + .fns() + .BindTextureUnit(texture_unit, self.texture); + } + } + + /// Allocates the texture storage, stores pixel data & generates a mipmap. + /// + /// # Errors + /// Returns `Err` if any value in `size` does not fit into a `i32`. + pub fn generate( + &self, + current_context: &CurrentContextWithFns<'_>, + size: &Dimens<u32>, + data: &[u8], + pixel_data_format: PixelDataFormat, + ) -> Result<(), GenerateError> + { + let size = Dimens::<crate::sys::types::GLsizei> { + width: size.width.try_into().map_err(|_| { + GenerateError::SizeWidthValueTooLarge { + value: size.width, + max_value: crate::sys::types::GLsizei::MAX as u32, + } + })?, + height: size.height.try_into().map_err(|_| { + GenerateError::SizeHeightValueTooLarge { + value: size.height, + max_value: crate::sys::types::GLsizei::MAX as u32, + } + })?, + }; + + self.alloc_image(current_context, pixel_data_format, &size, data); + + unsafe { + current_context.fns().GenerateTextureMipmap(self.texture); + } + + Ok(()) + } + + pub fn set_wrap( + &self, + current_context: &CurrentContextWithFns<'_>, + wrapping: Wrapping, + ) + { + unsafe { + current_context.fns().TextureParameteri( + self.texture, + crate::sys::TEXTURE_WRAP_S, + wrapping as i32, + ); + + current_context.fns().TextureParameteri( + self.texture, + crate::sys::TEXTURE_WRAP_T, + wrapping as i32, + ); + } + } + + pub fn set_magnifying_filter( + &self, + current_context: &CurrentContextWithFns<'_>, + filtering: Filtering, + ) + { + unsafe { + current_context.fns().TextureParameteri( + self.texture, + crate::sys::TEXTURE_MAG_FILTER, + filtering as i32, + ); + } + } + + pub fn set_minifying_filter( + &self, + current_context: &CurrentContextWithFns<'_>, + filtering: Filtering, + ) + { + unsafe { + current_context.fns().TextureParameteri( + self.texture, + crate::sys::TEXTURE_MIN_FILTER, + filtering as i32, + ); + } + } + + pub fn delete(self, current_context: &CurrentContextWithFns<'_>) + { + unsafe { + current_context + .fns() + .DeleteTextures(1, &raw const self.texture); + } + } + + fn alloc_image( + &self, + current_context: &CurrentContextWithFns<'_>, + pixel_data_format: PixelDataFormat, + size: &Dimens<crate::sys::types::GLsizei>, + data: &[u8], + ) + { + unsafe { + current_context.fns().TextureStorage2D( + self.texture, + 1, + pixel_data_format.to_sized_internal_format(), + size.width, + size.height, + ); + + current_context.fns().TextureSubImage2D( + self.texture, + 0, + 0, + 0, + size.width, + size.height, + pixel_data_format.to_format(), + crate::sys::UNSIGNED_BYTE, + data.as_ptr().cast(), + ); + } + } +} + +const fn try_cast_u32_to_i32(val: u32) -> i32 +{ + assert!(val <= i32::MAX as u32); + + val.cast_signed() +} + +/// Texture wrapping. +#[derive(Debug, Clone, Copy)] +#[repr(i32)] +pub enum Wrapping +{ + Repeat = const { try_cast_u32_to_i32(crate::sys::REPEAT) }, + MirroredRepeat = const { try_cast_u32_to_i32(crate::sys::MIRRORED_REPEAT) }, + ClampToEdge = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_EDGE) }, + ClampToBorder = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_BORDER) }, +} + +#[derive(Debug, Clone, Copy)] +#[repr(i32)] +pub enum Filtering +{ + Nearest = const { try_cast_u32_to_i32(crate::sys::NEAREST) }, + Linear = const { try_cast_u32_to_i32(crate::sys::LINEAR) }, +} + +/// Texture pixel data format. +#[derive(Debug, Clone, Copy)] +pub enum PixelDataFormat +{ + Rgb8, + Rgba8, +} + +impl PixelDataFormat +{ + fn to_sized_internal_format(self) -> crate::sys::types::GLenum + { + match self { + Self::Rgb8 => crate::sys::RGB8, + Self::Rgba8 => crate::sys::RGBA8, + } + } + + fn to_format(self) -> crate::sys::types::GLenum + { + match self { + Self::Rgb8 => crate::sys::RGB, + Self::Rgba8 => crate::sys::RGBA, + } + } +} + +/// Error generating texture. +#[derive(Debug, thiserror::Error)] +pub enum GenerateError +{ + #[error("Size width value ({value}) is too large. Must be < {max_value}")] + SizeWidthValueTooLarge + { + value: u32, max_value: u32 + }, + #[error("Size height value ({value}) is too large. Must be < {max_value}")] + SizeHeightValueTooLarge + { + value: u32, max_value: u32 + }, +} |