diff options
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 +}  | 
