summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-08-04 21:07:57 +0200
committerHampusM <hampus@hampusmat.com>2026-08-06 18:10:56 +0200
commitf37d8409bff69e86b761cae5e9f0387e3d9708a5 (patch)
tree3e026b89a3e5bf15fe4bb1c448f105536dd633a7
parent6f683a66582dd543b957216b466c07dc64d5b9a7 (diff)
feat(opengl-bindings): add support for cubemap textures
-rw-r--r--opengl-bindings/Cargo.toml1
-rw-r--r--opengl-bindings/src/texture.rs300
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 {