Dairy-Drift/src/model/mesh.rs

163 lines
5.7 KiB
Rust

#![allow(non_snake_case)]
#![allow(dead_code)]
use std::ffi::CString;
use std::mem::size_of;
use std::os::raw::c_void;
use std::ptr;
use cgmath::{ Vector3, Vector2 };
use cgmath::prelude::*;
use gl;
use field_offset::offset_of;
use crate::shader::shader;
// NOTE: without repr(C) the compiler may reorder the fields or use different padding/alignment than C.
// Depending on how you pass the data to OpenGL, this may be bad. In this case it's not strictly
// necessary though because of the `offset!` macro used below in setupMesh()
#[repr(C)]
pub struct Vertex {
// position
pub Position: Vector3<f32>,
// normal
pub Normal: Vector3<f32>,
// texCoords
pub TexCoords: Vector2<f32>,
// tangent
pub Tangent: Vector3<f32>,
// bitangent
pub Bitangent: Vector3<f32>,
}
impl Default for Vertex {
fn default() -> Self {
Vertex {
Position: Vector3::zero(),
Normal: Vector3::zero(),
TexCoords: Vector2::zero(),
Tangent: Vector3::zero(),
Bitangent: Vector3::zero(),
}
}
}
#[derive(Clone)]
pub struct Texture {
pub id: u32,
pub type_: String,
pub path: String,
}
pub struct Mesh {
/* Mesh Data */
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub textures: Vec<Texture>,
pub VAO: u32,
/* Render data */
VBO: u32,
EBO: u32,
}
impl Mesh {
pub fn new(vertices: Vec<Vertex>, indices: Vec<u32>, textures: Vec<Texture>) -> Mesh {
let mut mesh = Mesh {
vertices, indices, textures,
VAO: 0, VBO: 0, EBO: 0
};
// now that we have all the required data, set the vertex buffers and its attribute pointers.
unsafe { mesh.setupMesh() }
mesh
}
/// render the mesh
pub unsafe fn Draw(&self, shader: &shader) {
// bind appropriate textures
let mut diffuseNr = 0;
let mut specularNr = 0;
let mut normalNr = 0;
let mut heightNr = 0;
for (i, texture) in self.textures.iter().enumerate() {
gl::ActiveTexture(gl::TEXTURE0 + i as u32); // active proper texture unit before binding
// retrieve texture number (the N in diffuse_textureN)
let name = &texture.type_;
let number = match name.as_str() {
"texture_diffuse" => {
diffuseNr += 1;
diffuseNr
},
"texture_specular" => {
specularNr += 1;
specularNr
}
"texture_normal" => {
normalNr += 1;
normalNr
}
"texture_height" => {
heightNr += 1;
heightNr
}
_ => panic!("unknown texture type")
};
// now set the sampler to the correct texture unit
let sampler = CString::new(format!("{}{}", name, number)).unwrap();
gl::Uniform1i(gl::GetUniformLocation(shader.ID, sampler.as_ptr()), i as i32);
// and finally bind the texture
gl::BindTexture(gl::TEXTURE_2D, texture.id);
}
// draw mesh
gl::BindVertexArray(self.VAO);
gl::DrawElements(gl::TRIANGLES, self.indices.len() as i32, gl::UNSIGNED_INT, ptr::null());
gl::BindVertexArray(0);
// always good practice to set everything back to defaults once configured.
gl::ActiveTexture(gl::TEXTURE0);
}
unsafe fn setupMesh(&mut self) {
// create buffers/arrays
gl::GenVertexArrays(1, &mut self.VAO);
gl::GenBuffers(1, &mut self.VBO);
gl::GenBuffers(1, &mut self.EBO);
gl::BindVertexArray(self.VAO);
// load data into vertex buffers
gl::BindBuffer(gl::ARRAY_BUFFER, self.VBO);
// A great thing about structs with repr(C) is that their memory layout is sequential for all its items.
// The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which
// again translates to 3/2 floats which translates to a byte array.
let size = (self.vertices.len() * size_of::<Vertex>()) as isize;
let data = &self.vertices[0] as *const Vertex as *const c_void;
gl::BufferData(gl::ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.EBO);
let size = (self.indices.len() * size_of::<u32>()) as isize;
let data = &self.indices[0] as *const u32 as *const c_void;
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
// set the vertex attribute pointers
let size = size_of::<Vertex>() as i32;
// vertex Positions
gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex => Position).get_byte_offset() as *const c_void);
// vertex normals
gl::EnableVertexAttribArray(1);
gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex => Normal).get_byte_offset() as *const c_void);
// vertex texture coords
gl::EnableVertexAttribArray(2);
gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex => TexCoords).get_byte_offset() as *const c_void);
// vertex tangent
gl::EnableVertexAttribArray(3);
gl::VertexAttribPointer(3, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex => Tangent).get_byte_offset() as *const c_void);
// vertex bitangent
gl::EnableVertexAttribArray(4);
gl::VertexAttribPointer(4, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex => Bitangent).get_byte_offset() as *const c_void);
gl::BindVertexArray(0);
}
}