2020-02-16 23:32:35 +00:00
|
|
|
// Trivial wrappers around GLESv2 C functions that should be safe
|
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
|
2020-02-16 23:44:01 +00:00
|
|
|
use glam::{Mat4, Vec3, Vec4};
|
2020-02-17 00:32:52 +00:00
|
|
|
use std::collections::*;
|
|
|
|
use std::convert::TryInto;
|
|
|
|
use std::cmp;
|
|
|
|
use std::io::Cursor;
|
|
|
|
use std::ffi::c_void;
|
|
|
|
|
|
|
|
use crate::iqm;
|
2020-02-16 23:44:01 +00:00
|
|
|
|
2020-02-16 23:32:35 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 23:44:01 +00:00
|
|
|
|
2020-02-16 23:49:38 +00:00
|
|
|
pub fn enable_vertex_attrib_array (id: Option <u32>) {
|
|
|
|
if let Some (id) = id {
|
|
|
|
// Are safety checks really needed here?
|
|
|
|
unsafe {
|
|
|
|
gl::EnableVertexAttribArray (id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-16 23:48:26 +00:00
|
|
|
pub fn uniform_1i (uni: i32, x: i32) {
|
|
|
|
unsafe {
|
|
|
|
gl::Uniform1i (uni, x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-16 23:44:01 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-02-17 00:32:52 +00:00
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
// More abstract things
|
2020-02-17 00:32:52 +00:00
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
// Only contains f32 floats
|
2020-02-17 00:32:52 +00:00
|
|
|
|
|
|
|
pub struct VertexBuffer {
|
|
|
|
id: u32,
|
2020-02-17 01:22:05 +00:00
|
|
|
// Not bytes.
|
2020-02-17 00:32:52 +00:00
|
|
|
len: usize,
|
2020-02-17 01:22:05 +00:00
|
|
|
// Assuming arrays are packed, not interleaved
|
|
|
|
// Typically 3, or 2 for UV.
|
|
|
|
num_coords: i32,
|
2020-02-17 00:32:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl VertexBuffer {
|
2020-02-17 01:22:05 +00:00
|
|
|
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);
|
|
|
|
|
2020-02-17 00:32:52 +00:00
|
|
|
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 (),
|
2020-02-17 01:22:05 +00:00
|
|
|
num_coords,
|
2020-02-17 00:32:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-02-17 01:22:05 +00:00
|
|
|
// Not bytes. Number of indexes.
|
2020-02-17 00:32:52 +00:00
|
|
|
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;
|
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
const IDX_SIZE: usize = 4;
|
|
|
|
|
|
|
|
assert_eq! (slice.len () % IDX_SIZE, 0);
|
2020-02-17 00:32:52 +00:00
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
for _ in 0..slice.len () / IDX_SIZE {
|
2020-02-17 00:32:52 +00:00
|
|
|
let idx = rdr.read_u32::<LittleEndian> ().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,
|
2020-02-17 01:22:05 +00:00
|
|
|
len: slice.len () / IDX_SIZE,
|
2020-02-17 00:32:52 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
// 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>,
|
|
|
|
}
|
2020-02-17 00:32:52 +00:00
|
|
|
|
2020-02-17 01:22:05 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|