summaryrefslogtreecommitdiff
path: root/opengl-bindings/src/vertex_array.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2025-09-19 16:36:57 +0200
committerHampusM <hampus@hampusmat.com>2025-10-02 16:55:33 +0200
commitea1d70c8c28e3b96da6264021fa1c62e28fcd8e4 (patch)
tree62ae9b75ee84602899b51483ed26fa664df36888 /opengl-bindings/src/vertex_array.rs
parent0008b374c7f3a9ef6b30ea31a4a8c98bce64649f (diff)
feat: add OpenGL bindings crate
Diffstat (limited to 'opengl-bindings/src/vertex_array.rs')
-rw-r--r--opengl-bindings/src/vertex_array.rs260
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
+}