use crate::{ buffer::{Buffer, BufferRef, 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) -> &[T] { &self.buffers } pub fn buffers_mut(&mut self) -> &mut [T] { &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 */ pub struct Builder { bindings: Vec>, } impl Builder { pub fn bind_buffer(mut self, buffer: T) -> BindingBuilder { let binding = Binding { buffer, pointers: Vec::new(), }; self.bindings.push(binding); BindingBuilder { builder: self } } } impl Builder where for<'a> &'a T: BufferRef, { pub fn build(self) -> Result, Error> { 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.as_ref())); for pointer in binding.pointers { Error::checked(|| gl::enable_vertex_attrib_array(pointer.index))?; match pointer.type_ { DataType::Byte | DataType::UnsignedByte | DataType::Short | DataType::UnsignedShort | DataType::Int | DataType::UnsignedInt => Error::checked(|| { gl::vertex_attrib_i_pointer( pointer.index, pointer.size, pointer.type_.as_enum(), pointer.stride, pointer.offset as *const _, ) })?, _ => 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) } } impl Clone for Builder where T: Clone, { fn clone(&self) -> Self { Self { bindings: self.bindings.clone(), } } } impl Default for Builder { fn default() -> Self { Self { bindings: Vec::new(), } } } /* BindingBuilder */ pub struct BindingBuilder { builder: Builder, } impl BindingBuilder { pub fn bind_buffer(self, buffer: T) -> 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) } } impl BindingBuilder where for<'a> &'a T: BufferRef, { pub fn build(self) -> Result, Error> { self.builder.build() } } impl Clone for BindingBuilder where T: Clone, { fn clone(&self) -> Self { Self { builder: self.builder.clone(), } } } /* DataType */ #[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] 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: T, pointers: Vec, } impl Clone for Binding where T: Clone, { fn clone(&self) -> Self { Self { buffer: self.buffer.clone(), pointers: self.pointers.clone(), } } } /* Pointer */ #[derive(Clone, Debug)] pub struct Pointer { pub index: gl::GLuint, pub size: gl::GLint, pub type_: DataType, pub normalize: bool, pub stride: gl::GLsizei, pub offset: gl::GLsizei, pub divisor: Option, }