diff options
| -rw-r--r-- | engine/src/rendering.rs | 41 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 234 | ||||
| -rw-r--r-- | engine/src/rendering/shader/cursor.rs | 15 | ||||
| -rw-r--r-- | engine/src/texture.rs | 35 | ||||
| -rw-r--r-- | engine/src/ui/dear_imgui.rs | 6 | ||||
| -rw-r--r-- | opengl-bindings/Cargo.toml | 1 | ||||
| -rw-r--r-- | opengl-bindings/src/texture.rs | 300 |
7 files changed, 473 insertions, 159 deletions
diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index dc3cb76..f40f774 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -216,6 +216,7 @@ pub enum Command ActivateShader(ObjectId), SetShaderBinding(ShaderBinding), CreateTexture(ObjectId, AssetOrValue<Texture>), + // TODO: Make UpdateTexture command able to handle textures other than 2D textures UpdateTexture { obj_id: ObjectId, @@ -321,6 +322,37 @@ impl Default for ScissorBox } } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum DepthFunction +{ + /// Never passes. + Never, + + /// Passes if the incoming depth value is less than the stored depth value. + #[default] + Less, + + /// Passes if the incoming depth value is equal to the stored depth value. + Equal, + + /// Passes if the incoming depth value is less than or equal to the stored depth + /// value. + LessOrEqual, + + /// Passes if the incoming depth value is greater than the stored depth value. + Greater, + + /// Passes if the incoming depth value is not equal to the stored depth value. + NotEqual, + + /// Passes if the incoming depth value is greater than or equal to the stored depth + /// value. + GreaterOrEqual, + + /// Always passes. + Always, +} + #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub struct DrawProperties @@ -329,6 +361,7 @@ pub struct DrawProperties pub blending_enabled: bool, pub blending_config: BlendingConfig, pub depth_test_enabled: bool, + pub depth_function: DepthFunction, pub scissor_test_enabled: bool, pub scissor_box: ScissorBox, pub face_culling_enabled: bool, @@ -343,6 +376,7 @@ impl Default for DrawProperties blending_enabled: false, blending_config: BlendingConfig::default(), depth_test_enabled: true, + depth_function: DepthFunction::default(), scissor_test_enabled: false, scissor_box: ScissorBox::default(), face_culling_enabled: false, @@ -358,9 +392,10 @@ bitflags! { const BLENDING_CONFIG = 1 << 1; const BLENDING_ENABLED = 1 << 2; const DEPTH_TEST_ENABLED = 1 << 3; - const SCISSOR_TEST_ENABLED = 1 << 4; - const SCISSOR_BOX = 1 << 5; - const FACE_CULLING_ENABLED = 1 << 6; + const DEPTH_FUNCTION = 1 << 4; + const SCISSOR_TEST_ENABLED = 1 << 5; + const SCISSOR_BOX = 1 << 6; + const FACE_CULLING_ENABLED = 1 << 7; } } diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index dc636ef..12aaaf0 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -35,10 +35,12 @@ use opengl_bindings::misc::{ define_scissor_box as gl_define_scissor_box, enable, get_viewport as gl_get_viewport, + set_depth_function as gl_set_depth_function, set_enabled, set_viewport as gl_set_viewport, BufferClearMask as GlBufferClearMask, Capability, + DepthFunction as GlDepthFunction, }; use opengl_bindings::shader::{ Error as GlShaderError, @@ -49,6 +51,7 @@ use opengl_bindings::shader::{ }; use opengl_bindings::texture::{ ColorSpace as GlTextureColorSpace, + CubeMapFace as GlCubeMapTextureFace, Error as GlTextureError, Filtering as GlTextureFiltering, PixelDataFormat as GlTexturePixelDataFormat, @@ -108,6 +111,7 @@ use crate::rendering::{ BufferClearMask, Command, CommandQueue, + DepthFunction, DrawMeshOptions, DrawPropertiesUpdateFlags, GraphicsProperties, @@ -118,8 +122,10 @@ use crate::rendering::{ RENDER_PHASE, }; use crate::texture::{ + CubeMapFace as CubeMapTextureFace, Filtering as TextureFiltering, Properties as TextureProperties, + TexCubeMap, Texture, Wrapping as TextureWrapping, }; @@ -1033,6 +1039,26 @@ fn handle_commands( } if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::DEPTH_FUNCTION) + { + gl_set_depth_function( + gl_context, + match draw_props.depth_function { + DepthFunction::Never => GlDepthFunction::Never, + DepthFunction::Less => GlDepthFunction::Less, + DepthFunction::Equal => GlDepthFunction::Equal, + DepthFunction::LessOrEqual => GlDepthFunction::LessOrEqual, + DepthFunction::Greater => GlDepthFunction::Greater, + DepthFunction::NotEqual => GlDepthFunction::NotEqual, + DepthFunction::GreaterOrEqual => { + GlDepthFunction::GreaterOrEqual + } + DepthFunction::Always => GlDepthFunction::Always, + }, + ); + } + + if draw_props_update_flags .contains(DrawPropertiesUpdateFlags::SCISSOR_TEST_ENABLED) { set_enabled( @@ -1118,28 +1144,64 @@ enum CreateGlContextError MakeContextCurrent(#[source] GlMakeContextCurrentError), } -fn retrieve_texture<'a>( - assets: &'a Assets, - texture: &'a AssetOrValue<Texture>, -) -> Option<(BorrowedOrOwned<'a, Image>, &'a TextureProperties)> +enum TextureCreationData<'a> { - let Texture { image, properties } = match &texture { - AssetOrValue::Asset(texture_asset) => { - let Some(texture) = assets.get(&texture_asset) else { - tracing::error!( - texture_asset_id=?texture_asset.id(), - "Texture asset does not exist" - ); - return None; - }; + Texture2D + { + image: BorrowedOrOwned<'a, Image> + }, + CubeMap + { + images: [(CubeMapTextureFace, BorrowedOrOwned<'a, Image>); 6], + color_type: ImageColorType, + is_srgb: bool, + size: Dimens<u32>, + }, +} - texture - } - AssetOrValue::Value(texture) => texture, - }; +fn validate_cube_map_texture(texture: &TexCubeMap) -> bool +{ + let first_image_color_type = texture.images[0].1.color_type(); + + if !texture.images[1..] + .into_iter() + .all(|(_, image)| image.color_type() == first_image_color_type) + { + tracing::error!("All texture images must have same color type"); + return false; + } + + let first_image_size = texture.images[0].1.dimensions(); + + if !texture.images[1..] + .into_iter() + .all(|(_, image)| image.dimensions() == first_image_size) + { + tracing::error!("All texture images must have same dimensions"); + return false; + } + + let first_image_is_srgb = texture.images[0].1.color_space_is_srgb(); + + if !texture.images[1..] + .into_iter() + .all(|(_, image)| image.color_space_is_srgb() == first_image_is_srgb) + { + tracing::error!("All texture images must have same color space"); + return false; + } + + true +} - let texture_image = match image.color_type() { - ImageColorType::Rgb8 if (image.dimensions().width * 3) % 4 != 0 => { +fn convert_textures_if_unaligned<const IMAGE_CNT: usize, Extra>( + images: [(Extra, &Image); IMAGE_CNT], + color_type: ImageColorType, + size: Dimens<u32>, +) -> [(Extra, BorrowedOrOwned<'_, Image>); IMAGE_CNT] +{ + match color_type { + ImageColorType::Rgb8 if (size.width * 3) % 4 != 0 => { // The texture will be corrupted if the alignment of each horizontal line of // the texture pixel array is not multiple of 4. // @@ -1156,12 +1218,10 @@ fn retrieve_texture<'a>( "texture image's pixel format to RGBA8" )); - BorrowedOrOwned::Owned(image.to_rgba8()) + images.map(|(extra, image)| (extra, BorrowedOrOwned::Owned(image.to_rgba8()))) } - _ => BorrowedOrOwned::Borrowned(image), - }; - - Some((texture_image, properties)) + _ => images.map(|(extra, image)| (extra, BorrowedOrOwned::Borrowned(image))), + } } #[tracing::instrument(skip_all)] @@ -1181,15 +1241,69 @@ fn create_texture_object( return Ok(()); } - let Some((texture_image, texture_properties)) = retrieve_texture(assets, &texture) - else { - return Ok(()); + let texture = match &texture { + AssetOrValue::Asset(texture_asset) => { + let Some(texture) = assets.get(&texture_asset) else { + tracing::error!( + texture_asset_id=?texture_asset.id(), + "Texture asset does not exist" + ); + return Ok(()); + }; + + texture + } + AssetOrValue::Value(texture) => texture, + }; + + let (texture_things, texture_properties) = match texture { + Texture::Texture2D(texture) => { + let [(_, image)] = convert_textures_if_unaligned( + [((), &texture.image)], + texture.image.color_type(), + texture.image.dimensions(), + ); + + ( + TextureCreationData::Texture2D { image }, + &texture.properties, + ) + } + Texture::CubeMap(texture) => { + if !validate_cube_map_texture(texture) { + return Ok(()); + } + + let first_image_color_type = texture.images[0].1.color_type(); + let first_image_size = texture.images[0].1.dimensions(); + let first_image_is_srgb = texture.images[0].1.color_space_is_srgb(); + + let images = convert_textures_if_unaligned( + std::array::from_fn::<_, 6, _>(|index| { + let (face, image) = &texture.images[index]; + + (*face, image) + }), + first_image_color_type, + first_image_size, + ); + + ( + TextureCreationData::CubeMap { + images, + color_type: first_image_color_type, + is_srgb: first_image_is_srgb, + size: first_image_size, + }, + &texture.properties, + ) + } }; object_store.insert( texture_object_id, Object::from_raw( - create_gl_texture(curr_gl_ctx, &*texture_image, texture_properties)? + create_gl_texture(curr_gl_ctx, texture_things, texture_properties)? .into_raw(), ObjectKind::Texture, ), @@ -1225,12 +1339,12 @@ fn update_texture_object( let gl_texture = GlTexture::from_raw(texture_obj.as_raw()); - gl_texture.store_image( + gl_texture.store_image_2d( curr_gl_ctx, 0, offset.into(), texture_image.dimensions().into(), - get_image_pixel_data_format(&texture_image), + color_type_to_pixel_data_format(texture_image.color_type()), texture_image.as_bytes(), )?; @@ -1269,9 +1383,10 @@ fn draw_mesh( Ok(()) } -fn get_image_pixel_data_format(image: &Image) -> GlTexturePixelDataFormat +fn color_type_to_pixel_data_format(color_type: ImageColorType) + -> GlTexturePixelDataFormat { - match image.color_type() { + match color_type { ImageColorType::Rgb8 => GlTexturePixelDataFormat::Rgb8, ImageColorType::Rgba8 => GlTexturePixelDataFormat::Rgba8, _ => { @@ -1282,19 +1397,42 @@ fn get_image_pixel_data_format(image: &Image) -> GlTexturePixelDataFormat fn create_gl_texture( curr_gl_context: &MaybeCurrentContextWithFns, - image: &Image, + texture_things: TextureCreationData<'_>, texture_properties: &TextureProperties, ) -> Result<GlTexture, GlTextureError> { - let gl_texture = GlTexture::builder() - .image(image.as_bytes(), get_image_pixel_data_format(image)) - .size(image.dimensions().into()) - .color_space(if image.color_space_is_srgb() { - GlTextureColorSpace::Srgb - } else { - GlTextureColorSpace::Linear - }) - .create(curr_gl_context)?; + let gl_texture = match texture_things { + TextureCreationData::Texture2D { image } => GlTexture::builder() + .size(image.dimensions().into()) + .color_space(if image.color_space_is_srgb() { + GlTextureColorSpace::Srgb + } else { + GlTextureColorSpace::Linear + }) + .create_2d( + curr_gl_context, + image.as_bytes(), + color_type_to_pixel_data_format(image.color_type()), + ), + TextureCreationData::CubeMap { images, color_type, is_srgb, size } => { + GlTexture::builder() + .size(size.into()) + .color_space(if is_srgb { + GlTextureColorSpace::Srgb + } else { + GlTextureColorSpace::Linear + }) + .create_cube_map( + curr_gl_context, + std::array::from_fn::<_, 6, _>(|index| { + let (face, image) = &images[index]; + + (cube_map_texture_face_to_gl(*face), image.as_bytes()) + }), + color_type_to_pixel_data_format(color_type), + ) + } + }?; gl_texture.set_wrap( curr_gl_context, @@ -1314,6 +1452,18 @@ fn create_gl_texture( Ok(gl_texture) } +fn cube_map_texture_face_to_gl(face: CubeMapTextureFace) -> GlCubeMapTextureFace +{ + match face { + CubeMapTextureFace::PositiveX => GlCubeMapTextureFace::PositiveX, + CubeMapTextureFace::NegativeX => GlCubeMapTextureFace::NegativeX, + CubeMapTextureFace::PositiveY => GlCubeMapTextureFace::PositiveY, + CubeMapTextureFace::NegativeY => GlCubeMapTextureFace::NegativeY, + CubeMapTextureFace::PositiveZ => GlCubeMapTextureFace::PositiveZ, + CubeMapTextureFace::NegativeZ => GlCubeMapTextureFace::NegativeZ, + } +} + fn create_shader_program( current_context: &MaybeCurrentContextWithFns, shader_program: &ShaderProgram, diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs index 169ad0b..5cc742d 100644 --- a/engine/src/rendering/shader/cursor.rs +++ b/engine/src/rendering/shader/cursor.rs @@ -275,15 +275,23 @@ impl BindingValue ( Self::Texture(_, BindingTextureKind::Texture2D), TypeKind::Resource, - _, - _, - _, + .., Some(res_shape), ) if (res_shape & ResourceShape::BASE) .contains(ResourceShape::TEXTURE_2D) => { Ok(()) } + ( + Self::Texture(_, BindingTextureKind::Cube), + TypeKind::Resource, + .., + Some(res_shape), + ) if (res_shape & ResourceShape::BASE) + .contains(ResourceShape::TEXTURE_CUBE) => + { + Ok(()) + } _ => { cold_path(); Err(BindingError::IncorrectValueType { @@ -356,6 +364,7 @@ pub struct Binding pub enum BindingTextureKind { Texture2D, + Cube, } #[derive(Debug, thiserror::Error)] diff --git a/engine/src/texture.rs b/engine/src/texture.rs index af56e97..3bfe171 100644 --- a/engine/src/texture.rs +++ b/engine/src/texture.rs @@ -13,12 +13,37 @@ pub static WHITE_1X1_ASSET_LABEL: LazyLock<AssetLabel> = LazyLock::new(|| AssetL }); #[derive(Debug, Clone)] -pub struct Texture +pub enum Texture +{ + Texture2D(Tex2D), + CubeMap(TexCubeMap), +} + +#[derive(Debug, Clone)] +pub struct Tex2D { pub image: Image, pub properties: Properties, } +#[derive(Debug, Clone)] +pub struct TexCubeMap +{ + pub images: [(CubeMapFace, Image); 6], + pub properties: Properties, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CubeMapFace +{ + PositiveX, + NegativeX, + PositiveY, + NegativeY, + PositiveZ, + NegativeZ, +} + builder! { /// Texture properties #[builder(name = PropertiesBuilder, derives=(Debug, Clone))] @@ -110,10 +135,10 @@ pub(crate) fn initialize(assets: &mut Assets) assets.store_with_label( WHITE_1X1_ASSET_LABEL.clone(), - Texture { + Texture::Texture2D(Tex2D { image: Image::from_color(Rgba::<u8>::white(), Dimens { width: 1, height: 1 }), properties: Properties::default(), - }, + }), ); } @@ -123,12 +148,12 @@ fn import( settings: Option<&'_ ImportSettings>, ) -> Result<(), ImageError> { - asset_submitter.submit_store(Texture { + asset_submitter.submit_store(Texture::Texture2D(Tex2D { image: Image::open(path)?, properties: settings .map(|settings| settings.properties.clone()) .unwrap_or_default(), - }); + })); Ok(()) } diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs index b9775ea..60067e1 100644 --- a/engine/src/ui/dear_imgui.rs +++ b/engine/src/ui/dear_imgui.rs @@ -74,7 +74,7 @@ use crate::rendering::{ SurfaceSpec, PRE_RENDER_PHASE, }; -use crate::texture::{Properties as TextureProperties, Texture}; +use crate::texture::{Properties as TextureProperties, Tex2D, Texture}; use crate::util::MapVec; use crate::vector::Vec2; use crate::windowing::dpi::PhysicalSize; @@ -542,10 +542,10 @@ fn add_drawing_render_pass( render_pass.commands.push(RenderingCommand::CreateTexture( texture_object_id, - AssetOrValue::Value(Texture { + AssetOrValue::Value(Texture::Texture2D(Tex2D { image, properties: TextureProperties::default(), - }), + })), )); let texture_lookup_id = TextureLookupId::new(); 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 { |
