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.main
parent
c415e9ed80
commit
5def012e85
|
@ -227,8 +227,7 @@ fn main () {
|
||||||
let model_data = load_small_file ("pumpking.iqm", 1024 * 1024);
|
let model_data = load_small_file ("pumpking.iqm", 1024 * 1024);
|
||||||
let model = Model::from_slice (&model_data [..]);
|
let model = Model::from_slice (&model_data [..]);
|
||||||
|
|
||||||
let pumpkin_verts = glezz::VertexBuffer::from_slice (model.get_vertex_slice (0));
|
let renderable_model = glezz::RenderableModel::from_iqm (&model);
|
||||||
let pumpkin_indexes = glezz::IndexBuffer::from_slice (model.get_index_slice (0));
|
|
||||||
|
|
||||||
let sky_data = load_small_file ("sky-sphere.iqm", 1024 * 1024);
|
let sky_data = load_small_file ("sky-sphere.iqm", 1024 * 1024);
|
||||||
let sky_model = Model::from_slice (&sky_data [..]);
|
let sky_model = Model::from_slice (&sky_data [..]);
|
||||||
|
@ -311,35 +310,18 @@ fn main () {
|
||||||
glezz::uniform_matrix_4fv (unis ["mvp"], &mvp_mat);
|
glezz::uniform_matrix_4fv (unis ["mvp"], &mvp_mat);
|
||||||
glezz::uniform_3fv (unis ["object_space_light"], &object_space_light);
|
glezz::uniform_3fv (unis ["object_space_light"], &object_space_light);
|
||||||
|
|
||||||
// This stuff actually still is unsafe because it's reading
|
renderable_model.draw (&attrs, 0);
|
||||||
// right from RAM I can still access. Using a VBO here
|
|
||||||
// might actually make it easier to make it safe.
|
|
||||||
|
|
||||||
if false {
|
glezz::uniform_3fv (unis ["albedo"], &green);
|
||||||
unsafe {
|
renderable_model.draw (&attrs, 1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let draw_sky = true;
|
let draw_sky = true;
|
||||||
if draw_sky {
|
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_matrix_4fv (unis ["mvp"], &sky_mvp_mat);
|
||||||
glezz::uniform_3fv (unis ["albedo"], &white);
|
glezz::uniform_3fv (unis ["albedo"], &white);
|
||||||
glezz::uniform_3fv (unis ["min_bright"], &white);
|
glezz::uniform_3fv (unis ["min_bright"], &white);
|
||||||
|
|
134
src/glezz.rs
134
src/glezz.rs
|
@ -1,6 +1,6 @@
|
||||||
// Trivial wrappers around GLESv2 C functions that should be safe
|
// 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 glam::{Mat4, Vec3, Vec4};
|
||||||
use std::collections::*;
|
use std::collections::*;
|
||||||
use std::convert::TryInto;
|
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 <u32>, num_coords: i32, slice: &[u8]) {
|
// Only contains f32 floats
|
||||||
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 <String, Option <u32>>,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VertexBuffer {
|
pub struct VertexBuffer {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
// Not bytes.
|
||||||
len: usize,
|
len: usize,
|
||||||
|
// Assuming arrays are packed, not interleaved
|
||||||
|
// Typically 3, or 2 for UV.
|
||||||
|
num_coords: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VertexBuffer {
|
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 id = {
|
||||||
let mut id = 0;
|
let mut id = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -119,6 +103,7 @@ impl VertexBuffer {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len: slice.len (),
|
len: slice.len (),
|
||||||
|
num_coords,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +130,7 @@ impl Drop for VertexBuffer {
|
||||||
|
|
||||||
pub struct IndexBuffer {
|
pub struct IndexBuffer {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
// Not bytes. Number of indexes.
|
||||||
len: usize,
|
len: usize,
|
||||||
min: u32,
|
min: u32,
|
||||||
max: u32,
|
max: u32,
|
||||||
|
@ -157,9 +143,11 @@ impl IndexBuffer {
|
||||||
let mut min = None;
|
let mut min = None;
|
||||||
let mut max = 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::<LittleEndian> ().unwrap ();
|
let idx = rdr.read_u32::<LittleEndian> ().unwrap ();
|
||||||
|
|
||||||
min = match min {
|
min = match min {
|
||||||
|
@ -187,7 +175,7 @@ impl IndexBuffer {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len: slice.len () / 4,
|
len: slice.len () / IDX_SIZE,
|
||||||
min: min.unwrap (),
|
min: min.unwrap (),
|
||||||
max: max.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 <f32>,
|
||||||
|
uv: Vec <f32>,
|
||||||
|
normals: Vec <f32>,
|
||||||
|
|
||||||
|
indexes: Vec <u32>,
|
||||||
|
|
||||||
|
meshes: Vec <RenderableMesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn vertex_attrib_pointer (id: Option <u32>, 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 <String, Option <u32>>, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
30
src/iqm.rs
30
src/iqm.rs
|
@ -36,15 +36,15 @@ mod consts {
|
||||||
pub const OFS_EXTENSIONS: usize = 26;
|
pub const OFS_EXTENSIONS: usize = 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod types {
|
pub mod types {
|
||||||
pub const POSITION: u32 = 0;
|
pub const POSITION: usize = 0;
|
||||||
pub const TEXCOORD: u32 = 1;
|
pub const TEXCOORD: usize = 1;
|
||||||
pub const NORMAL: u32 = 2;
|
pub const NORMAL: usize = 2;
|
||||||
pub const TANGENT: u32 = 3;
|
pub const TANGENT: usize = 3;
|
||||||
pub const BLENDINDEXES: u32 = 4;
|
pub const BLENDINDEXES: usize = 4;
|
||||||
pub const BLENDWEIGHTS: u32 = 5;
|
pub const BLENDWEIGHTS: usize = 5;
|
||||||
pub const COLOR: u32 = 6;
|
pub const COLOR: usize = 6;
|
||||||
pub const CUSTOM: u32 = 0x10;
|
pub const CUSTOM: usize = 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod formats {
|
pub mod formats {
|
||||||
|
@ -255,4 +255,16 @@ impl <'a> Model <'a> {
|
||||||
|
|
||||||
&self.data [offset..offset + num_bytes]
|
&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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue