Dairy-Drift/src/model.rs

141 lines
5.0 KiB
Rust

#![allow(non_snake_case)]
#![allow(dead_code)]
use std::os::raw::c_void;
use std::path::Path;
use std::sync::Arc;
use cgmath::{vec2, vec3};
use image;
use glow::*;
use image::DynamicImage::*;
use image::GenericImage;
use tobj;
mod mesh;
use mesh::{ Mesh, Texture, Vertex };
use crate::shader::shader;
#[derive(Default)]
pub struct Model {
/* Model Data */
pub meshes: Vec<Mesh>,
pub textures_loaded: Vec<Texture>, // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.
directory: String,
}
impl Model {
/// constructor, expects a filepath to a 3D model.
pub fn new(path: &str, gl: std::sync::Arc<glow::Context>) -> Model {
let mut model = Model::default();
model.loadModel(path, Arc::clone(&gl));
model
}
pub fn Draw(&self, shader: &shader) {
for mesh in &self.meshes {
unsafe { mesh.Draw(shader); }
}
}
// loads a model from file and stores the resulting meshes in the meshes vector.
fn loadModel(&mut self, path: &str, gl: Arc<glow::Context>) {
let path = Path::new(path);
// retrieve the directory path of the filepath
self.directory = path.parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().into();
let obj = tobj::load_obj(path, true/*, &tobj::LoadOptions::default()*/);
let (models, materials) = obj.unwrap();
for model in models {
let mesh = &model.mesh;
let num_vertices = mesh.positions.len() / 3;
// data to fill
let mut vertices: Vec<Vertex> = Vec::with_capacity(num_vertices);
let indices: Vec<u32> = mesh.indices.clone();
let (p, n, t) = (&mesh.positions, &mesh.normals, &mesh.texcoords);
for i in 0..num_vertices {
vertices.push(Vertex {
Position: vec3(p[i*3], p[i*3+1], p[i*3+2]),
Normal: vec3(n[i*3], n[i*3+1], n[i*3+2]),
TexCoords: vec2(t[i*2], t[i*2+1]),
..Vertex::default()
})
}
// process material
let mut textures = Vec::new();
if let Some(material_id) = mesh.material_id {
let material = &materials[material_id];
// 1. diffuse map
if !material.diffuse_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.diffuse_texture, "texture_diffuse", Arc::clone(&gl));
textures.push(texture);
}
// 2. specular map
if !material.specular_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.specular_texture, "texture_specular", Arc::clone(&gl));
textures.push(texture);
}
// 3. normal map
if !material.normal_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.normal_texture, "texture_normal", Arc::clone(&gl));
textures.push(texture);
}
// NOTE: no height maps
}
self.meshes.push(Mesh::new(vertices, indices, textures, Arc::clone(&gl)));
}
}
fn loadMaterialTexture(&mut self, path: &str, typeName: &str, gl: Arc<glow::Context>) -> Texture {
{
let texture = self.textures_loaded.iter().find(|t| t.path == path);
if let Some(texture) = texture {
return texture.clone();
}
}
let texture = Texture {
id: unsafe { TextureFromFile(path, &self.directory, Arc::clone(&gl)) },
type_: typeName.into(),
path: path.into()
};
self.textures_loaded.push(texture.clone());
texture
}
}
unsafe fn TextureFromFile(path: &str, directory: &str, gl: Arc<glow::Context>) -> glow::NativeTexture {
let filename = format!("{}/{}", directory, path);
let mut textureID = gl.create_texture().unwrap();
let img = image::open(&Path::new(&filename)).expect("Texture failed to load");
let img = img.flipv();
let format = match img {
ImageLuma8(_) => glow::RED,
ImageLumaA8(_) => glow::RG,
ImageRgb8(_) => glow::RGB,
ImageRgba8(_) => glow::RGBA,
_ => panic!("really weird image type"),
};
let data = img.as_bytes();
gl.bind_texture(glow::TEXTURE_2D, Some(textureID));
gl.tex_image_2d(glow::TEXTURE_2D, 0, format as i32, img.width() as i32, img.height() as i32,
0, format, glow::UNSIGNED_BYTE, Some(&data));
gl.generate_mipmap(glow::TEXTURE_2D);
gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::REPEAT as i32);
gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::REPEAT as i32);
gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR_MIPMAP_LINEAR as i32);
gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32);
textureID
}