2021-32-bit-holiday-jam/src/glezz.rs

280 lines
5.8 KiB
Rust

// 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 <u32>) {
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,
}
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 () * 4).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::<LittleEndian> ().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 <RenderableMesh>,
}
unsafe fn vertex_attrib_pointer (id: Option <u32>, 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 <String, Option <u32>>, 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::DrawElements (gl::TRIANGLES, mesh.num_triangles * 3, gl::UNSIGNED_INT, (mesh.first_triangle * 3 * 4) as *const u8 as *const c_void);
}
}
}