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::(start_index as usize), ); } Ok(()) } pub fn bind_element_buffer( &self, current_context: &CurrentContextWithFns<'_>, element_buffer: &Buffer, ) { unsafe { current_context .fns() .VertexArrayElementBuffer(self.array, element_buffer.object()); } } pub fn bind_vertex_buffer( &self, current_context: &CurrentContextWithFns<'_>, binding_index: u32, vertex_buffer: &Buffer, offset: isize, ) { let vertex_size = const { cast_usize_to_c_int(size_of::()) }; 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::()) }, } } } #[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( 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 }