From 5def012e85bc29a891aabd94763bfd7b0be01271 Mon Sep 17 00:00:00 2001 From: _ <> Date: Mon, 17 Feb 2020 01:22:05 +0000 Subject: [PATCH] Didn't end up adding GPU buffers, but I made the RenderableModel class. This class clones all the data from iqm::Model that's needed to draw a mesh / model safely, so I can draw it without unsafe. It can be upgraded mostly in-place to use GPU buffers later on. --- src/bin/pumpkin.rs | 36 +++--------- src/glezz.rs | 134 ++++++++++++++++++++++++++++++++++----------- src/iqm.rs | 30 +++++++--- 3 files changed, 133 insertions(+), 67 deletions(-) diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index eab6cf2..ef7e432 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -227,8 +227,7 @@ fn main () { let model_data = load_small_file ("pumpking.iqm", 1024 * 1024); let model = Model::from_slice (&model_data [..]); - let pumpkin_verts = glezz::VertexBuffer::from_slice (model.get_vertex_slice (0)); - let pumpkin_indexes = glezz::IndexBuffer::from_slice (model.get_index_slice (0)); + let renderable_model = glezz::RenderableModel::from_iqm (&model); let sky_data = load_small_file ("sky-sphere.iqm", 1024 * 1024); let sky_model = Model::from_slice (&sky_data [..]); @@ -311,35 +310,18 @@ fn main () { glezz::uniform_matrix_4fv (unis ["mvp"], &mvp_mat); glezz::uniform_3fv (unis ["object_space_light"], &object_space_light); - // This stuff actually still is unsafe because it's reading - // right from RAM I can still access. Using a VBO here - // might actually make it easier to make it safe. + renderable_model.draw (&attrs, 0); - if false { - unsafe { - point_to_model (&attrs, &model); - - gl::DrawElements (gl::TRIANGLES, (model.meshes [0].num_triangles * 3) as i32, gl::UNSIGNED_INT, &model.get_index_slice (0) [0] as *const u8 as *const c_void); - } - } - - unsafe { - gl::BindBuffer (gl::ARRAY_BUFFER, 0); - gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, 0); - } - - if true { - glezz::uniform_3fv (unis ["albedo"], &green); - - unsafe { - point_to_model (&attrs, &model); - - gl::DrawElements (gl::TRIANGLES, (model.meshes [1].num_triangles * 3) as i32, gl::UNSIGNED_INT, &model.get_index_slice (1) [0] as *const u8 as *const c_void); - } - } + glezz::uniform_3fv (unis ["albedo"], &green); + renderable_model.draw (&attrs, 1); let draw_sky = true; if draw_sky { + unsafe { + gl::BindBuffer (gl::ARRAY_BUFFER, 0); + gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, 0); + } + glezz::uniform_matrix_4fv (unis ["mvp"], &sky_mvp_mat); glezz::uniform_3fv (unis ["albedo"], &white); glezz::uniform_3fv (unis ["min_bright"], &white); diff --git a/src/glezz.rs b/src/glezz.rs index f2c18ba..afce475 100644 --- a/src/glezz.rs +++ b/src/glezz.rs @@ -1,6 +1,6 @@ // Trivial wrappers around GLESv2 C functions that should be safe -use byteorder::{LittleEndian, ReadBytesExt}; +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt}; use glam::{Mat4, Vec3, Vec4}; use std::collections::*; use std::convert::TryInto; @@ -69,41 +69,25 @@ pub fn uniform_matrix_4fv (uni: i32, m: &Mat4) { } } -// More abstract stuff +// More abstract things -unsafe fn vertex_attrib_pointer (id: Option , num_coords: i32, slice: &[u8]) { - 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 u8 as *const c_void); - } -} - -pub fn draw_model ( - attrs: &HashMap >, - model: &iqm::Model, - mesh_number: usize -) { - let index_slice: &[u8] = model.get_index_slice (mesh_number); - let num_indexes = model.meshes [mesh_number].num_triangles * 3; - - unsafe { - vertex_attrib_pointer (attrs ["pos"], 3, model.get_vertex_slice (0)); - vertex_attrib_pointer (attrs ["uv"], 2, model.get_vertex_slice (1)); - vertex_attrib_pointer (attrs ["normal"], 3, model.get_vertex_slice (2)); - - gl::DrawElements (gl::TRIANGLES, (num_indexes) as i32, gl::UNSIGNED_INT, &index_slice [0] as *const u8 as *const c_void); - } -} +// 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]) -> Self { + 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 { @@ -119,6 +103,7 @@ impl VertexBuffer { Self { id, len: slice.len (), + num_coords, } } @@ -145,6 +130,7 @@ impl Drop for VertexBuffer { pub struct IndexBuffer { id: u32, + // Not bytes. Number of indexes. len: usize, min: u32, max: u32, @@ -157,9 +143,11 @@ impl IndexBuffer { let mut min = None; let mut max = None; - assert_eq! (slice.len () % 4, 0); + const IDX_SIZE: usize = 4; - for _ in 0..slice.len () / 4 { + assert_eq! (slice.len () % IDX_SIZE, 0); + + for _ in 0..slice.len () / IDX_SIZE { let idx = rdr.read_u32:: ().unwrap (); min = match min { @@ -187,7 +175,7 @@ impl IndexBuffer { Self { id, - len: slice.len () / 4, + len: slice.len () / IDX_SIZE, min: min.unwrap (), max: max.unwrap (), } @@ -214,4 +202,88 @@ impl Drop for IndexBuffer { } } +// 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: Vec , + + 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 (); + + const IDX_SIZE: usize = 4; + + let mut indexes = vec! [0; index_slice.len () / IDX_SIZE]; + LittleEndian::read_u32_into (index_slice, &mut indexes [..]); + + let max_index: usize = (*indexes.iter ().max ().unwrap ()).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: indexes, + meshes, + } + } + + pub fn draw (&self, attrs: &HashMap >, mesh_num: usize) + { + let mesh = &self.meshes [mesh_num]; + + unsafe { + 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, &self.indexes [mesh.first_triangle * 3] as *const u32 as *const c_void); + } + } +} diff --git a/src/iqm.rs b/src/iqm.rs index fcb95f7..4e1967c 100644 --- a/src/iqm.rs +++ b/src/iqm.rs @@ -36,15 +36,15 @@ mod consts { pub const OFS_EXTENSIONS: usize = 26; } -mod types { - pub const POSITION: u32 = 0; - pub const TEXCOORD: u32 = 1; - pub const NORMAL: u32 = 2; - pub const TANGENT: u32 = 3; - pub const BLENDINDEXES: u32 = 4; - pub const BLENDWEIGHTS: u32 = 5; - pub const COLOR: u32 = 6; - pub const CUSTOM: u32 = 0x10; +pub mod types { + pub const POSITION: usize = 0; + pub const TEXCOORD: usize = 1; + pub const NORMAL: usize = 2; + pub const TANGENT: usize = 3; + pub const BLENDINDEXES: usize = 4; + pub const BLENDWEIGHTS: usize = 5; + pub const COLOR: usize = 6; + pub const CUSTOM: usize = 0x10; } pub mod formats { @@ -255,4 +255,16 @@ impl <'a> Model <'a> { &self.data [offset..offset + num_bytes] } + + pub fn get_all_indexes (&self) -> &[u8] { + let bytes_per_u32 = 4; + let indexes_per_tri = 3; + let stride = bytes_per_u32 * indexes_per_tri; + + let offset: usize = self.header.fields [consts::OFS_TRIANGLES].try_into ().unwrap (); + + let num_bytes: usize = (stride * self.header.fields [consts::NUM_TRIANGLES]).try_into ().unwrap (); + + &self.data [offset..offset + num_bytes] + } }