diff options
| author | HampusM <hampus@hampusmat.com> | 2026-08-04 21:07:57 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-08-06 18:10:56 +0200 |
| commit | f37d8409bff69e86b761cae5e9f0387e3d9708a5 (patch) | |
| tree | 3e026b89a3e5bf15fe4bb1c448f105536dd633a7 | |
| parent | 6f683a66582dd543b957216b466c07dc64d5b9a7 (diff) | |
feat(opengl-bindings): add support for cubemap textures
| -rw-r--r-- | opengl-bindings/Cargo.toml | 1 | ||||
| -rw-r--r-- | opengl-bindings/src/texture.rs | 300 |
2 files changed, 198 insertions, 103 deletions
diff --git a/opengl-bindings/Cargo.toml b/opengl-bindings/Cargo.toml index f2252a7..cabbf90 100644 --- a/opengl-bindings/Cargo.toml +++ b/opengl-bindings/Cargo.toml @@ -31,6 +31,7 @@ gl_commands = [ "BindVertexArray", "TextureStorage2D", "TextureSubImage2D", + "TextureSubImage3D", "DeleteTextures", "GenerateTextureMipmap", "TextureParameteri", diff --git a/opengl-bindings/src/texture.rs b/opengl-bindings/src/texture.rs index be52135..c317c92 100644 --- a/opengl-bindings/src/texture.rs +++ b/opengl-bindings/src/texture.rs @@ -4,28 +4,15 @@ use crate::data_types::{Dimens, Vec2}; use crate::MaybeCurrentContextWithFns; #[derive(Debug)] -pub struct Builder<'image> +pub struct Builder { - image: Cow<'image, [u8]>, - pixel_data_format: PixelDataFormat, size: Dimens<u32>, color_space: ColorSpace, mipmap_levels: u16, } -impl<'image> Builder<'image> +impl Builder { - pub fn image( - mut self, - image: impl Into<Cow<'image, [u8]>>, - pixel_data_format: PixelDataFormat, - ) -> Self - { - self.image = image.into(); - self.pixel_data_format = pixel_data_format; - self - } - pub fn size(mut self, size: Dimens<u32>) -> Self { self.size = size; @@ -45,51 +32,101 @@ impl<'image> Builder<'image> } #[must_use] - pub fn create( + pub fn create_2d<'image>( &self, current_context: &MaybeCurrentContextWithFns, + image: impl Into<Cow<'image, [u8]>>, + pixel_data_format: PixelDataFormat, ) -> Result<Texture, Error> { + let image = image.into(); + check_image_buffer_len_correct_for_size( - self.image.as_ref(), - self.size, - self.pixel_data_format, + image.as_ref(), + [self.size.width, self.size.height, 1], + pixel_data_format, )?; - let size = try_convert_size(self.size.clone())?; + let size = try_convert_size([self.size.width, self.size.height])?; - let texture = Texture::new(current_context); + let texture = Texture::new(current_context, crate::sys::TEXTURE_2D); - texture.alloc( + texture.alloc_2d( current_context, self.mipmap_levels, - self.pixel_data_format, + pixel_data_format, self.color_space, size, ); - texture.sub_image( + texture.sub_image_2d( current_context, 0, - Vec2 { x: 0, y: 0 }, + [0, 0], + size, + pixel_data_format, + image.as_ref(), + ); + + texture.generate_mipmap(current_context); + + Ok(texture) + } + + #[must_use] + pub fn create_cube_map<'image, Image>( + &self, + current_context: &MaybeCurrentContextWithFns, + images: [(CubeMapFace, Image); 6], + pixel_data_format: PixelDataFormat, + ) -> Result<Texture, Error> + where + Image: Into<Cow<'image, [u8]>>, + { + let images = images.map(|(face, image)| (face, image.into())); + + for (_, image) in &images { + check_image_buffer_len_correct_for_size( + image.as_ref(), + [self.size.width, self.size.height, 1], + pixel_data_format, + )?; + } + + let size = try_convert_size([self.size.width, self.size.height])?; + + let texture = Texture::new(current_context, crate::sys::TEXTURE_CUBE_MAP); + + texture.alloc_2d( + current_context, + self.mipmap_levels, + pixel_data_format, + self.color_space, size, - self.pixel_data_format, - self.image.as_ref(), ); + for (face, image) in images { + texture.sub_image_3d( + current_context, + 0, + [0, 0, face as crate::sys::types::GLint], + [size[0], size[1], 1], + pixel_data_format, + image.as_ref(), + ); + } + texture.generate_mipmap(current_context); Ok(texture) } } -impl<'image> Default for Builder<'image> +impl Default for Builder { fn default() -> Self { Self { - image: Cow::Borrowed(&[]), - pixel_data_format: PixelDataFormat::default(), size: Dimens::default(), color_space: ColorSpace::default(), mipmap_levels: 1, @@ -105,7 +142,7 @@ pub struct Texture impl Texture { - pub fn builder<'image>() -> Builder<'image> + pub fn builder() -> Builder { Builder::default() } @@ -130,7 +167,7 @@ impl Texture } } - pub fn store_image( + pub fn store_image_2d( &self, current_context: &MaybeCurrentContextWithFns, mipmap_level: u16, @@ -140,12 +177,43 @@ impl Texture image: &[u8], ) -> Result<(), Error> { + check_image_buffer_len_correct_for_size( + image, + [size.width, size.height, 1], + pixel_data_format, + )?; + + let offset = try_convert_offset([offset.x, offset.y])?; + let size = try_convert_size([size.width, size.height])?; + + self.sub_image_2d( + current_context, + mipmap_level, + offset, + size, + pixel_data_format, + image, + ); + + Ok(()) + } + + pub fn store_image_3d( + &self, + current_context: &MaybeCurrentContextWithFns, + mipmap_level: u16, + offset: [u32; 3], + size: [u32; 3], + pixel_data_format: PixelDataFormat, + image: &[u8], + ) -> Result<(), Error> + { check_image_buffer_len_correct_for_size(image, size, pixel_data_format)?; let offset = try_convert_offset(offset)?; let size = try_convert_size(size)?; - self.sub_image( + self.sub_image_3d( current_context, mipmap_level, offset, @@ -227,28 +295,29 @@ impl Texture self.texture } - fn new(current_context: &MaybeCurrentContextWithFns) -> Self + fn new( + current_context: &MaybeCurrentContextWithFns, + target: crate::sys::types::GLenum, + ) -> Self { let mut texture = crate::sys::types::GLuint::default(); unsafe { - current_context.fns().CreateTextures( - crate::sys::TEXTURE_2D, - 1, - &raw mut texture, - ); + current_context + .fns() + .CreateTextures(target, 1, &raw mut texture); }; Self { texture } } - fn alloc( + fn alloc_2d( &self, current_context: &MaybeCurrentContextWithFns, mipmap_levels: u16, pixel_data_format: PixelDataFormat, color_space: ColorSpace, - size: Dimens<crate::sys::types::GLsizei>, + size: [crate::sys::types::GLsizei; 2], ) { unsafe { @@ -256,18 +325,18 @@ impl Texture self.texture, mipmap_levels.into(), pixel_data_format.to_sized_internal_format(color_space), - size.width, - size.height, + size[0], + size[1], ); } } - fn sub_image( + fn sub_image_2d( &self, current_context: &MaybeCurrentContextWithFns, mipmap_level: u16, - offset: Vec2<crate::sys::types::GLint>, - size: Dimens<crate::sys::types::GLsizei>, + offset: [crate::sys::types::GLint; 2], + size: [crate::sys::types::GLsizei; 2], pixel_data_format: PixelDataFormat, image: &[u8], ) @@ -276,10 +345,37 @@ impl Texture current_context.fns().TextureSubImage2D( self.texture, mipmap_level.into(), - offset.x, - offset.y, - size.width, - size.height, + offset[0], + offset[1], + size[0], + size[1], + pixel_data_format.to_format(), + crate::sys::UNSIGNED_BYTE, + image.as_ptr().cast(), + ); + } + } + + fn sub_image_3d( + &self, + current_context: &MaybeCurrentContextWithFns, + mipmap_level: u16, + offset: [crate::sys::types::GLint; 3], + size: [crate::sys::types::GLsizei; 3], + pixel_data_format: PixelDataFormat, + image: &[u8], + ) + { + unsafe { + current_context.fns().TextureSubImage3D( + self.texture, + mipmap_level.into(), + offset[0], + offset[1], + offset[2], + size[0], + size[1], + size[2], pixel_data_format.to_format(), crate::sys::UNSIGNED_BYTE, image.as_ptr().cast(), @@ -365,33 +461,37 @@ pub enum ColorSpace Srgb, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CubeMapFace +{ + PositiveX = 0, + NegativeX = 1, + PositiveY = 2, + NegativeY = 3, + PositiveZ = 4, + NegativeZ = 5, +} + #[derive(Debug, thiserror::Error)] pub enum Error { - #[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 + #[error("Size value ({value}) is too large. Must be < {max_value}")] + ValueInSizeIsTooLarge { value: u32, max_value: u32 }, - #[error("Offset X value ({value}) is too large. Must be < {max_value}")] - OffsetXValueTooLarge - { - value: u32, max_value: u32 - }, - #[error("Offset Y value ({value}) is too large. Must be < {max_value}")] - OffsetYValueTooLarge + #[error("Offset value ({value}) is too large. Must be < {max_value}")] + ValueInOffsetIsTooLarge { value: u32, max_value: u32 }, #[error( - "Incorrect image buffer length for size {size}. Expected {}, found {}", + "Incorrect image buffer length for size {}x{}x{}. Expected {}, found {}", + size[0], + size[1], + size[2], expected_buffer_len, buffer_len )] @@ -399,62 +499,56 @@ pub enum Error { expected_buffer_len: usize, buffer_len: usize, - size: Dimens<u32>, + size: [u32; 3], }, } -fn try_convert_size( - size: Dimens<u32>, -) -> Result<Dimens<crate::sys::types::GLsizei>, Error> +fn try_convert_size<const LEN: usize>( + size: [u32; LEN], +) -> Result<[crate::sys::types::GLsizei; LEN], Error> { - Ok(Dimens::<crate::sys::types::GLsizei> { - width: size - .width - .try_into() - .map_err(|_| Error::SizeWidthValueTooLarge { - value: size.width, - max_value: crate::sys::types::GLsizei::MAX as u32, - })?, - height: size - .height - .try_into() - .map_err(|_| Error::SizeHeightValueTooLarge { - value: size.height, - max_value: crate::sys::types::GLsizei::MAX as u32, - })?, - }) + let mut output = [0; LEN]; + + for (value, output_val) in size.into_iter().zip(&mut output) { + *output_val = value.try_into().map_err(|_| Error::ValueInSizeIsTooLarge { + value: value, + max_value: crate::sys::types::GLsizei::MAX as u32, + })?; + } + + Ok(output) } -fn try_convert_offset(offset: Vec2<u32>) - -> Result<Vec2<crate::sys::types::GLint>, Error> +fn try_convert_offset<const LEN: usize>( + offset: [u32; LEN], +) -> Result<[crate::sys::types::GLint; LEN], Error> { - Ok(Vec2::<crate::sys::types::GLint> { - x: offset - .x - .try_into() - .map_err(|_| Error::OffsetXValueTooLarge { - value: offset.x, - max_value: crate::sys::types::GLint::MAX as u32, - })?, - y: offset - .y + let mut output = [0; LEN]; + + for (value, output_val) in offset.into_iter().zip(&mut output) { + *output_val = value .try_into() - .map_err(|_| Error::OffsetYValueTooLarge { - value: offset.y, + .map_err(|_| Error::ValueInOffsetIsTooLarge { + value: value, max_value: crate::sys::types::GLint::MAX as u32, - })?, - }) + })?; + } + + Ok(output) } fn check_image_buffer_len_correct_for_size( image: &[u8], - size: Dimens<u32>, + size: [u32; 3], pixel_data_format: PixelDataFormat, ) -> Result<(), Error> { let pixel_width = pixel_data_format.pixel_width(); - let expected_buffer_len = size.width as usize * size.height as usize * pixel_width; + let [width, height, depth] = size; + + let expected_buffer_len = + width as usize * height as usize * depth as usize * pixel_width; if expected_buffer_len != image.len() { return Err(Error::IncorrectImageBufferLengthForSize { |
