use crate::{ buffer::{Buffer, Target}, error::Error, misc::{AsEnum, BindGuard, Bindable}, }; /* VertexArray */ pub struct VertexArray { id: gl::GLuint, buffers: Vec, } impl VertexArray { pub fn builder() -> Builder { Builder::default() } pub fn buffers(&self) -> &Vec { &self.buffers } pub fn buffers_mut(&mut self) -> &mut Vec { &mut self.buffers } } impl Drop for VertexArray { fn drop(&mut self) { gl::delete_vertex_arrays(1, &self.id); } } impl Bindable for VertexArray { fn bind(&self) { gl::bind_vertex_array(self.id); } fn unbind(&self) { gl::bind_vertex_array(0); } } /* Builder */ #[derive(Default)] pub struct Builder { bindings: Vec, } impl Builder { pub fn bind_buffer(mut self, buffer: Buffer) -> BindingBuilder { let binding = Binding { buffer, pointers: Vec::new(), }; self.bindings.push(binding); BindingBuilder { builder: self } } pub fn build(self) -> Result { let mut id = 0; Error::checked(|| gl::create_vertex_arrays(1, &mut id))?; let mut vertex_array = VertexArray { id, buffers: Vec::new(), }; let guard = BindGuard::new(&vertex_array); let mut buffers = Vec::new(); for binding in self.bindings { let guard = BindGuard::new((Target::ArrayBuffer, &binding.buffer)); for pointer in binding.pointers { Error::checked(|| gl::enable_vertex_attrib_array(pointer.index))?; Error::checked(|| { gl::vertex_attrib_pointer( pointer.index, pointer.size, pointer.type_.as_enum(), if pointer.normalize { gl::TRUE } else { gl::FALSE }, pointer.stride, pointer.offset as *const _, ) })?; if let Some(divisor) = pointer.divisor { Error::checked(|| gl::vertex_attrib_divisor(pointer.index, divisor))?; } } drop(guard); buffers.push(binding.buffer); } drop(guard); vertex_array.buffers = buffers; Ok(vertex_array) } } /* BindingBuilder */ pub struct BindingBuilder { builder: Builder, } impl BindingBuilder { pub fn bind_buffer(self, buffer: Buffer) -> Self { self.builder.bind_buffer(buffer) } pub fn vertex_attrib_pointer( mut self, index: gl::GLuint, size: gl::GLint, type_: DataType, normalize: bool, stride: gl::GLsizei, offset: gl::GLsizei, ) -> Result { for binding in &self.builder.bindings { for pointer in &binding.pointers { if pointer.index == index { return Err(Error::VertexArrayIndexAlreadyInUse(index)); } } } let pointer = Pointer { index, size, type_, normalize, stride, offset, divisor: None, }; self.builder .bindings .last_mut() .unwrap() .pointers .push(pointer); Ok(self) } pub fn vertex_attrib_divisor(mut self, divisor: gl::GLuint) -> Result { let binding = self.builder.bindings.last_mut().unwrap(); let pointer = binding .pointers .last_mut() .ok_or(Error::VertexArrayExpectedPointer)?; pointer.divisor = Some(divisor); Ok(self) } pub fn build(self) -> Result { self.builder.build() } } /* DataType */ #[allow(non_camel_case_types)] pub enum DataType { Byte, UnsignedByte, Short, UnsignedShort, Int, UnsignedInt, HalfFloat, Float, Double, Fixed, Int_2_10_10_10_Rev, UnsignedInt_2_10_10_10_Rev, UnsignedInt_10f_11f_11f_Rev, } impl AsEnum for DataType { fn as_enum(&self) -> gl::GLenum { match self { Self::Byte => gl::BYTE, Self::UnsignedByte => gl::UNSIGNED_BYTE, Self::Short => gl::SHORT, Self::UnsignedShort => gl::UNSIGNED_SHORT, Self::Int => gl::INT, Self::UnsignedInt => gl::UNSIGNED_INT, Self::HalfFloat => gl::HALF_FLOAT, Self::Float => gl::FLOAT, Self::Double => gl::DOUBLE, Self::Fixed => gl::FIXED, Self::Int_2_10_10_10_Rev => gl::INT_2_10_10_10_REV, Self::UnsignedInt_2_10_10_10_Rev => gl::UNSIGNED_INT_2_10_10_10_REV, Self::UnsignedInt_10f_11f_11f_Rev => gl::UNSIGNED_INT_10F_11F_11F_REV, } } } /* Binding */ struct Binding { buffer: Buffer, pointers: Vec, } /* Pointer */ struct Pointer { index: gl::GLuint, size: gl::GLint, type_: DataType, normalize: bool, stride: gl::GLsizei, offset: gl::GLsizei, divisor: Option, }