// Trivial wrappers around GLESv2 C functions that should be safe use byteorder::{ByteOrder, LittleEndian, ReadBytesExt}; use glam::{Mat4, Vec3, Vec4}; use std::collections::*; use std::convert::TryInto; use std::cmp; use std::io::Cursor; use std::ffi::c_void; use crate::iqm; pub fn clear_color (r: f32, g: f32, b: f32, a: f32) { unsafe { gl::ClearColor (1.0f32, 1.0f32, 1.0f32, 1.0f32); } } pub fn clear (flags: u32) { unsafe { gl::Clear (flags); } } pub fn disable (constant: u32) { unsafe { gl::Disable (constant); } } pub fn enable (constant: u32) { unsafe { gl::Enable (constant); } } pub fn enable_vertex_attrib_array (id: Option ) { if let Some (id) = id { // Are safety checks really needed here? unsafe { gl::EnableVertexAttribArray (id); } } } pub fn uniform_1i (uni: i32, x: i32) { unsafe { gl::Uniform1i (uni, x); } } pub fn uniform_3fv (uni: i32, v: &Vec3) { unsafe { gl::Uniform3fv (uni, 1, v as *const Vec3 as *const f32); } } pub fn uniform_4fv (uni: i32, v: &Vec4) { unsafe { gl::Uniform4fv (uni, 1, v as *const Vec4 as *const f32); } } pub fn uniform_matrix_4fv (uni: i32, m: &Mat4) { const FALSE_U8: u8 = 0; unsafe { gl::UniformMatrix4fv (uni, 1, FALSE_U8, m as *const Mat4 as *const f32); } } // More abstract things // Only contains f32 floats pub struct VertexBuffer { id: u32, // Not bytes. len: usize, // Assuming arrays are packed, not interleaved // Typically 3, or 2 for UV. num_coords: i32, } impl VertexBuffer { pub fn from_slice (slice: &[u8], num_coords: i32) -> Self { const FLOAT_SIZE: usize = 4; assert_eq! (slice.len () % (FLOAT_SIZE * (num_coords as usize)), 0); let id = { let mut id = 0; unsafe { gl::GenBuffers (1, &mut id); gl::BindBuffer (gl::ARRAY_BUFFER, id); gl::BufferData (gl::ARRAY_BUFFER, slice.len ().try_into ().unwrap (), &slice [0] as *const u8 as *const c_void, gl::STATIC_DRAW); } assert! (id != 0); id }; Self { id, len: slice.len (), num_coords, } } pub fn bind (&self) { unsafe { gl::BindBuffer (gl::ARRAY_BUFFER, self.id); } } } impl Drop for VertexBuffer { fn drop (&mut self) { if self.id == 0 { return; } unsafe { gl::DeleteBuffers (1, &self.id); } self.id = 0; } } struct IndexBuffer { id: u32, // Not bytes. Number of indexes. len: usize, min: u32, max: u32, } impl IndexBuffer { pub fn from_slice (slice: &[u8]) -> Self { let mut rdr = Cursor::new (slice); let mut min = None; let mut max = None; const IDX_SIZE: usize = 4; assert_eq! (slice.len () % IDX_SIZE, 0); for _ in 0..slice.len () / IDX_SIZE { let idx = rdr.read_u32:: ().unwrap (); min = match min { None => Some (idx), Some (min) => Some (cmp::min (min, idx)), }; max = match max { None => Some (idx), Some (max) => Some (cmp::max (max, idx)), }; } let id = { let mut id = 0; unsafe { gl::GenBuffers (1, &mut id); gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, id); gl::BufferData (gl::ELEMENT_ARRAY_BUFFER, slice.len ().try_into ().unwrap (), &slice [0] as *const u8 as *const c_void, gl::STATIC_DRAW); } assert! (id != 0); id }; Self { id, len: slice.len () / IDX_SIZE, min: min.unwrap (), max: max.unwrap (), } } pub fn bind (&self) { unsafe { gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, self.id); } } } impl Drop for IndexBuffer { fn drop (&mut self) { if self.id == 0 { return; } unsafe { gl::DeleteBuffers (1, &self.id); } self.id = 0; } } // Takes ownership of mesh stuff in an opaque way that's abstract // from the IQM model. IQM is zero-copy, but this is not. // Since it's opaque, I can drop in a VBO/IBO setup when I'm not lazy. struct RenderableMesh { first_triangle: usize, num_triangles: i32, } pub struct RenderableModel { pos: Vec , uv: Vec , normals: Vec , indexes: IndexBuffer, meshes: Vec , } unsafe fn vertex_attrib_pointer (id: Option , num_coords: i32, slice: &[f32]) { const FALSE_U8: u8 = 0; const FLOAT_SIZE: i32 = 4; if let Some (id) = id { gl::VertexAttribPointer (id, num_coords, gl::FLOAT, FALSE_U8, FLOAT_SIZE * num_coords, &slice [0] as *const f32 as *const c_void); } } impl RenderableModel { pub fn from_iqm (model: &iqm::Model) -> RenderableModel { let pos_bytes = model.get_vertex_slice (iqm::types::POSITION); let mut pos_vec = vec! [0.0; pos_bytes.len () / 4]; LittleEndian::read_f32_into (pos_bytes, &mut pos_vec [..]); let uv_bytes = model.get_vertex_slice (iqm::types::TEXCOORD); let mut uv_vec = vec! [0.0; uv_bytes.len () / 4]; LittleEndian::read_f32_into (uv_bytes, &mut uv_vec [..]); let normal_bytes = model.get_vertex_slice (iqm::types::NORMAL); let mut normal_vec = vec! [0.0; normal_bytes.len () / 4]; LittleEndian::read_f32_into (normal_bytes, &mut normal_vec [..]); let index_slice = model.get_all_indexes (); let indexes = IndexBuffer::from_slice (index_slice); let max_index: usize = indexes.max.try_into ().unwrap (); assert! (max_index * 3 < pos_vec.len ()); assert! (max_index * 2 < uv_vec.len ()); assert! (max_index * 3 < normal_vec.len ()); let meshes = model.meshes.iter () .map (|mesh| RenderableMesh { first_triangle: mesh.first_triangle.try_into ().unwrap (), num_triangles: mesh.num_triangles.try_into ().unwrap (), }) .collect (); Self { pos: pos_vec, uv: uv_vec, normals: normal_vec, indexes, meshes, } } pub fn draw (&self, attrs: &HashMap >, mesh_num: usize) { let mesh = &self.meshes [mesh_num]; unsafe { gl::BindBuffer (gl::ARRAY_BUFFER, 0); self.indexes.bind (); vertex_attrib_pointer (attrs ["pos"], 3, &self.pos); vertex_attrib_pointer (attrs ["uv"], 2, &self.uv); vertex_attrib_pointer (attrs ["normal"], 3, &self.normals); gl::DrawElements (gl::TRIANGLES, mesh.num_triangles * 3, gl::UNSIGNED_INT, (mesh.first_triangle * 3 * 4) as *const u8 as *const c_void); } } }