diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index 77fc9dd..ec14959 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -10,6 +10,7 @@ use opengl_rust::*; use file::load_small_file; use iqm::Model; +use renderable_model::RenderableModel; use shader::{ShaderProgram, ShaderObject}; use texture::Texture; use timestep::TimeStep; @@ -172,12 +173,12 @@ fn main () { let model_data = load_small_file ("pumpking.iqm", 1024 * 1024); let model = Model::from_slice (&model_data [..]); - let renderable_model = glezz::RenderableModel::from_iqm (&model); + let renderable_model = RenderableModel::from_iqm (&model); let sky_data = load_small_file ("sky-sphere.iqm", 1024 * 1024); let sky_model = Model::from_slice (&sky_data [..]); - let renderable_sky = glezz::RenderableModel::from_iqm (&sky_model); + let renderable_sky = RenderableModel::from_iqm (&sky_model); glezz::enable_vertex_attrib_array (attrs ["pos"]); glezz::enable_vertex_attrib_array (attrs ["uv"]); diff --git a/src/glezz.rs b/src/glezz.rs index f0d8fa6..e7ce579 100644 --- a/src/glezz.rs +++ b/src/glezz.rs @@ -1,14 +1,6 @@ // 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 { @@ -69,211 +61,6 @@ pub fn uniform_matrix_4fv (uni: i32, m: &Mat4) { } } -// More abstract things +// More abstract things below here -// Only contains f32 floats -pub struct VertexBuffer { - id: u32, - // Not bytes. - len: usize, -} - -impl VertexBuffer { - pub fn from_slice (slice: &[f32]) -> Self { - const FLOAT_SIZE: usize = 4; - - 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 () * FLOAT_SIZE).try_into ().unwrap (), &slice [0] as *const f32 as *const c_void, gl::STATIC_DRAW); - } - assert! (id != 0); - id - }; - - Self { - id, - len: slice.len (), - } - } - - 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, - max: u32, -} - -impl IndexBuffer { - pub fn from_slice (slice: &[u8]) -> Self { - let mut rdr = Cursor::new (slice); - - 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 (); - - 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, - 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 { - num_pos: usize, - num_uv: usize, - num_normal: usize, - - vertexes: VertexBuffer, - indexes: IndexBuffer, - - meshes: Vec , -} - -unsafe fn vertex_attrib_pointer (id: Option , num_coords: i32, float_offset: usize) { - 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, (float_offset * 4) as *const u8 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 uv_bytes = model.get_vertex_slice (iqm::types::TEXCOORD); - let normal_bytes = model.get_vertex_slice (iqm::types::NORMAL); - - let num_pos = pos_bytes.len () / 4; - let num_uv = uv_bytes.len () / 4; - let num_normal = normal_bytes.len () / 4; - - let mut vertex_vec = vec! [0.0; num_pos + num_uv + num_normal]; - - LittleEndian::read_f32_into (pos_bytes, &mut vertex_vec [0..num_pos]); - LittleEndian::read_f32_into (uv_bytes, &mut vertex_vec [num_pos..num_pos + num_uv]); - LittleEndian::read_f32_into (normal_bytes, &mut vertex_vec [num_pos + num_uv..num_pos + num_uv + num_normal]); - - let vertexes = VertexBuffer::from_slice (&vertex_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 < num_pos); - assert! (max_index * 2 < num_uv); - assert! (max_index * 3 < num_normal); - - 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 { - num_pos, - num_uv, - num_normal, - - vertexes, - indexes, - meshes, - } - } - - pub fn draw (&self, attrs: &HashMap >, mesh_num: usize) - { - let mesh = &self.meshes [mesh_num]; - - self.vertexes.bind (); - self.indexes.bind (); - - unsafe { - vertex_attrib_pointer (attrs ["pos"], 3, 0); - vertex_attrib_pointer (attrs ["uv"], 2, self.num_pos); - vertex_attrib_pointer (attrs ["normal"], 3, self.num_pos + self.num_uv); - - gl::DrawRangeElements (gl::TRIANGLES, 0, self.indexes.max, mesh.num_triangles * 3, gl::UNSIGNED_INT, (mesh.first_triangle * 3 * 4) as *const u8 as *const c_void); - } - } -} diff --git a/src/gpu_buffers.rs b/src/gpu_buffers.rs new file mode 100644 index 0000000..da24336 --- /dev/null +++ b/src/gpu_buffers.rs @@ -0,0 +1,126 @@ +use byteorder::{LittleEndian, ReadBytesExt}; + +use std::cmp; +use std::convert::TryInto; +use std::ffi::c_void; +use std::io::Cursor; + +// Only contains f32 floats + +pub struct VertexBuffer { + id: u32, + // Not bytes. + len: usize, +} + +impl VertexBuffer { + pub fn from_slice (slice: &[f32]) -> Self { + const FLOAT_SIZE: usize = 4; + + 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 () * FLOAT_SIZE).try_into ().unwrap (), &slice [0] as *const f32 as *const c_void, gl::STATIC_DRAW); + } + assert! (id != 0); + id + }; + + Self { + id, + len: slice.len (), + } + } + + 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; + } +} + +pub struct IndexBuffer { + id: u32, + // Not bytes. Number of indexes. + len: usize, + max: u32, +} + +impl IndexBuffer { + pub fn from_slice (slice: &[u8]) -> Self { + let mut rdr = Cursor::new (slice); + + 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 (); + + 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, + max: max.unwrap (), + } + } + + pub fn bind (&self) { + unsafe { + gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, self.id); + } + } + + pub fn max (&self) -> u32 { + self.max + } +} + +impl Drop for IndexBuffer { + fn drop (&mut self) { + if self.id == 0 { + return; + } + + unsafe { + gl::DeleteBuffers (1, &self.id); + } + + self.id = 0; + } +} diff --git a/src/iqm.rs b/src/iqm.rs index 260e2d4..35b0c0f 100644 --- a/src/iqm.rs +++ b/src/iqm.rs @@ -1,4 +1,3 @@ -use iota::iota; use nom::{ IResult, bytes::complete::{tag}, @@ -8,6 +7,7 @@ use nom::{ use std::convert::TryInto; pub mod consts { +use iota::iota; iota! { pub const VERSION: usize = iota; , FILESIZE diff --git a/src/lib.rs b/src/lib.rs index 3a4de51..599aa0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,9 @@ extern crate iota; pub mod file; pub mod glezz; +pub mod gpu_buffers; pub mod iqm; +pub mod renderable_model; pub mod shader; pub mod texture; pub mod timestep; diff --git a/src/renderable_model.rs b/src/renderable_model.rs new file mode 100644 index 0000000..116b315 --- /dev/null +++ b/src/renderable_model.rs @@ -0,0 +1,99 @@ +use byteorder::{ByteOrder, LittleEndian}; + +use std::collections::*; +use std::convert::TryInto; +use std::ffi::c_void; + +use crate::gpu_buffers::*; +use crate::iqm; + +// 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 { + num_pos: usize, + num_uv: usize, + num_normal: usize, + + vertexes: VertexBuffer, + indexes: IndexBuffer, + + meshes: Vec , +} + +unsafe fn vertex_attrib_pointer (id: Option , num_coords: i32, float_offset: usize) { + 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, (float_offset * 4) as *const u8 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 uv_bytes = model.get_vertex_slice (iqm::types::TEXCOORD); + let normal_bytes = model.get_vertex_slice (iqm::types::NORMAL); + + let num_pos = pos_bytes.len () / 4; + let num_uv = uv_bytes.len () / 4; + let num_normal = normal_bytes.len () / 4; + + let mut vertex_vec = vec! [0.0; num_pos + num_uv + num_normal]; + + LittleEndian::read_f32_into (pos_bytes, &mut vertex_vec [0..num_pos]); + LittleEndian::read_f32_into (uv_bytes, &mut vertex_vec [num_pos..num_pos + num_uv]); + LittleEndian::read_f32_into (normal_bytes, &mut vertex_vec [num_pos + num_uv..num_pos + num_uv + num_normal]); + + let vertexes = VertexBuffer::from_slice (&vertex_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 < num_pos); + assert! (max_index * 2 < num_uv); + assert! (max_index * 3 < num_normal); + + 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 { + num_pos, + num_uv, + num_normal, + + vertexes, + indexes, + meshes, + } + } + + pub fn draw (&self, attrs: &HashMap >, mesh_num: usize) + { + let mesh = &self.meshes [mesh_num]; + + self.vertexes.bind (); + self.indexes.bind (); + + unsafe { + vertex_attrib_pointer (attrs ["pos"], 3, 0); + vertex_attrib_pointer (attrs ["uv"], 2, self.num_pos); + vertex_attrib_pointer (attrs ["normal"], 3, self.num_pos + self.num_uv); + + gl::DrawRangeElements (gl::TRIANGLES, 0, self.indexes.max (), mesh.num_triangles * 3, gl::UNSIGNED_INT, (mesh.first_triangle * 3 * 4) as *const u8 as *const c_void); + } + } +}