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/vertex_array.rs | |
parent | 0008b374c7f3a9ef6b30ea31a4a8c98bce64649f (diff) |
feat: add OpenGL bindings crate
Diffstat (limited to 'opengl-bindings/src/vertex_array.rs')
-rw-r--r-- | opengl-bindings/src/vertex_array.rs | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/opengl-bindings/src/vertex_array.rs b/opengl-bindings/src/vertex_array.rs new file mode 100644 index 0000000..9942fe7 --- /dev/null +++ b/opengl-bindings/src/vertex_array.rs @@ -0,0 +1,260 @@ +use std::ffi::{c_int, c_void}; +use std::mem::size_of; + +use safer_ffi::layout::ReprC; + +use crate::buffer::Buffer; +use crate::CurrentContextWithFns; + +#[derive(Debug)] +pub struct VertexArray +{ + array: crate::sys::types::GLuint, +} + +impl VertexArray +{ + #[must_use] + pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self + { + let mut array = 0; + + unsafe { + current_context.fns().CreateVertexArrays(1, &raw mut array); + } + + Self { array } + } + + /// Draws the currently bound vertex array. + /// + /// # Errors + /// Returns `Err` if: + /// - `start_index` is too large + /// - `cnt` is too large + pub fn draw_arrays( + current_context: &CurrentContextWithFns<'_>, + primitive_kind: PrimitiveKind, + start_index: u32, + cnt: u32, + ) -> Result<(), DrawError> + { + let start_index: crate::sys::types::GLint = + start_index + .try_into() + .map_err(|_| DrawError::StartIndexValueTooLarge { + value: start_index, + max_value: crate::sys::types::GLint::MAX as u32, + })?; + + let cnt: crate::sys::types::GLsizei = + cnt.try_into().map_err(|_| DrawError::CountValueTooLarge { + value: cnt, + max_value: crate::sys::types::GLsizei::MAX as u32, + })?; + + unsafe { + current_context + .fns() + .DrawArrays(primitive_kind.into_gl(), start_index, cnt); + } + + Ok(()) + } + + /// Draws the currently bound vertex array. + /// + /// # Errors + /// Returns `Err` if `cnt` is too large. + pub fn draw_elements( + current_context: &CurrentContextWithFns<'_>, + primitive_kind: PrimitiveKind, + start_index: u32, + cnt: u32, + ) -> Result<(), DrawError> + { + let cnt: crate::sys::types::GLsizei = + cnt.try_into().map_err(|_| DrawError::CountValueTooLarge { + value: cnt, + max_value: crate::sys::types::GLsizei::MAX as u32, + })?; + + unsafe { + current_context.fns().DrawElements( + primitive_kind.into_gl(), + cnt, + crate::sys::UNSIGNED_INT, + // TODO: Make this not sometimes UB. DrawElements expects a actual + // pointer to a memory location when no VBO is bound. + // See: https://stackoverflow.com/q/21706113 + std::ptr::without_provenance::<c_void>(start_index as usize), + ); + } + + Ok(()) + } + + pub fn bind_element_buffer( + &self, + current_context: &CurrentContextWithFns<'_>, + element_buffer: &Buffer<u32>, + ) + { + unsafe { + current_context + .fns() + .VertexArrayElementBuffer(self.array, element_buffer.object()); + } + } + + pub fn bind_vertex_buffer<VertexT: ReprC>( + &self, + current_context: &CurrentContextWithFns<'_>, + binding_index: u32, + vertex_buffer: &Buffer<VertexT>, + offset: isize, + ) + { + let vertex_size = const { cast_usize_to_c_int(size_of::<VertexT>()) }; + + unsafe { + current_context.fns().VertexArrayVertexBuffer( + self.array, + binding_index, + vertex_buffer.object(), + offset, + vertex_size, + ); + } + } + + pub fn enable_attrib( + &self, + current_context: &CurrentContextWithFns<'_>, + attrib_index: u32, + ) + { + unsafe { + current_context.fns().EnableVertexArrayAttrib( + self.array, + attrib_index as crate::sys::types::GLuint, + ); + } + } + + pub fn set_attrib_format( + &self, + current_context: &CurrentContextWithFns<'_>, + attrib_index: u32, + data_type: DataType, + normalized: bool, + offset: u32, + ) + { + unsafe { + current_context.fns().VertexArrayAttribFormat( + self.array, + attrib_index, + data_type.size(), + data_type as u32, + if normalized { + crate::sys::TRUE + } else { + crate::sys::FALSE + }, + offset, + ); + } + } + + /// Associate a vertex attribute and a vertex buffer binding. + pub fn set_attrib_vertex_buf_binding( + &self, + current_context: &CurrentContextWithFns<'_>, + attrib_index: u32, + vertex_buf_binding_index: u32, + ) + { + unsafe { + current_context.fns().VertexArrayAttribBinding( + self.array, + attrib_index, + vertex_buf_binding_index, + ); + } + } + + pub fn bind(&self, current_context: &CurrentContextWithFns<'_>) + { + unsafe { current_context.fns().BindVertexArray(self.array) } + } +} + +#[derive(Debug)] +pub enum PrimitiveKind +{ + Triangles, +} + +impl PrimitiveKind +{ + fn into_gl(self) -> crate::sys::types::GLenum + { + match self { + Self::Triangles => crate::sys::TRIANGLES, + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +pub enum DataType +{ + Float = crate::sys::FLOAT, +} + +impl DataType +{ + fn size(self) -> crate::sys::types::GLint + { + match self { + Self::Float => const { cast_usize_to_c_int(size_of::<f32>()) }, + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum DrawError +{ + #[error("Start index value {value} is too large. Must be < {max_value}")] + StartIndexValueTooLarge + { + value: u32, max_value: u32 + }, + + #[error("Count value {value} is too large. Must be < {max_value}")] + CountValueTooLarge + { + value: u32, max_value: u32 + }, +} + +const fn cast_usize_to_c_int(num: usize) -> c_int +{ + assert!(num <= c_int::MAX.cast_unsigned() as usize); + + c_int::from_ne_bytes(shorten_byte_array(num.to_ne_bytes())) +} + +const fn shorten_byte_array<const SRC_LEN: usize, const DST_LEN: usize>( + src: [u8; SRC_LEN], +) -> [u8; DST_LEN] +{ + assert!(DST_LEN < SRC_LEN); + + let mut ret = [0; DST_LEN]; + + ret.copy_from_slice(src.split_at(DST_LEN).0); + + ret +} |