diff --git a/src/app/app.rs b/src/app/app.rs index ee3374b..0146165 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -13,7 +13,7 @@ use winit::{event, window}; use crate::app::gl::{gl_config_picker, gl_create_context}; use crate::app::renderer::Renderer; -use crate::models::ModelData; +use crate::models::Scene; enum GlDisplayCreationState { Builder(Box), @@ -32,13 +32,12 @@ pub struct App { renderer: Option, current_scene: Option, - scene_data: ModelData, state: Option, } impl App { - pub fn new(scene_data: ModelData) -> Self { + pub fn new() -> Self { let template = ConfigTemplateBuilder::new() .with_alpha_size(8) .with_transparency(cfg!(target_os = "macos")); @@ -51,7 +50,6 @@ impl App { gl_context: None, renderer: None, state: None, - scene_data, current_scene: None, } } @@ -112,7 +110,7 @@ impl ApplicationHandler for App { if self.renderer.is_none() { let mut renderer = Renderer::new(&gl_context.display()); - renderer.upload_scene_data(&self.scene_data); + // renderer.upload_scene_data(&self.scene_data); println!("Built Renderer"); diff --git a/src/app/renderer.rs b/src/app/renderer.rs index 92734da..ad496a3 100644 --- a/src/app/renderer.rs +++ b/src/app/renderer.rs @@ -1,6 +1,7 @@ use glutin::prelude::GlDisplay; use crate::app::gl; -use crate::models::{self, Model, ModelData, RenderObject, Scene, ShaderSource}; +use crate::models::{self, GpuMesh, Scene, ShaderSource}; +use crate::shaders::Shader; use std::collections::HashMap; use std::ops::Deref; use std::ffi::{ CStr, CString }; @@ -13,7 +14,7 @@ fn get_gl_string(gl: &gl::Gl, variant: gl::types::GLenum) -> Option<&'static CSt } pub struct Renderer { - gl: gl::Gl, + gl: std::rc::Rc, shaders: HashMap, scenes: HashMap } @@ -38,26 +39,13 @@ impl Renderer { } Self { - gl, + gl: std::rc::Rc::new(gl), shaders: HashMap::new(), scenes: HashMap::new() } } - pub fn upload_scene_data(&mut self, scene_data: &ModelData) { - let objects = scene_data.objects_data - .iter() - .map(|(mesh, shader_src)| { - let shader = self.create_shader(shader_src); - let render_object = self.upload_mesh(mesh); - (render_object, shader) - }) - .collect::>(); - println!("Uploading Scene {}", scene_data.name); - self.scenes.insert(scene_data.name.clone(), Model { objects }); - } - - pub fn upload_mesh(&self, mesh: &models::Mesh) -> RenderObject { + pub fn upload_mesh(&self, mesh: &models::Mesh) -> GpuMesh { unsafe { let gl = &self.gl; @@ -71,7 +59,7 @@ impl Renderer { let vertex_data = mesh.vertices .iter() .map(|v| { - [v.x, v.y, v.z, 1.0, 0.1, 0.1] + [v.position.x, v.position.y, v.position.z, 1.0, 0.1, 0.1] }) .collect::>(); @@ -106,7 +94,7 @@ impl Renderer { gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint); gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint); - RenderObject { + GpuMesh { vao, vbo, _ebo: None, @@ -125,7 +113,7 @@ impl Renderer { } } - pub fn create_shader(&self, shader_src: &ShaderSource) -> gl::types::GLuint { + pub fn create_shader(&self, shader_src: &ShaderSource) -> Shader { unsafe { let vertex_shader = Renderer::compile_shader(&self.gl, gl::VERTEX_SHADER, shader_src.vertex_src); let fragment_shader = Renderer::compile_shader(&self.gl, gl::FRAGMENT_SHADER, shader_src.fragment_src); @@ -140,7 +128,10 @@ impl Renderer { self.gl.DeleteShader(vertex_shader); self.gl.DeleteShader(fragment_shader); - program + Shader { + program, + gl: self.gl.clone() + } } } @@ -164,7 +155,7 @@ impl Renderer { } } - pub fn draw_object(&self, obj: &RenderObject, shader: gl::types::GLuint) { + pub fn draw_object(&self, obj: &GpuMesh, shader: gl::types::GLuint) { unsafe { self.UseProgram(shader); @@ -182,9 +173,9 @@ impl Renderer { } if let Some(scene) = self.scenes.get(current_scene) { - for model in scene.models { + scene.models.iter().for_each(|(obj, shader)| { self.draw_object(obj, *shader); - } + }); } else { eprintln!("Unknown Scene Requested: {}", current_scene); } diff --git a/src/lib.rs b/src/lib.rs index 230be3d..9c2b67d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,11 @@ pub mod models { pub mod material; pub use material::*; + + pub mod primitives; + pub use primitives::*; + + pub mod object; } pub mod app { @@ -22,6 +27,9 @@ pub mod app { } pub mod shaders { - pub mod shaders; - pub use shaders::*; + pub mod shader; + pub use shader::*; + + pub mod constants; + pub use constants::*; } diff --git a/src/main.rs b/src/main.rs index 51eb4b4..a3f1dc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,34 @@ -use proj::{app::app::App, models::{Mesh, ModelData, ShaderSource}, shaders::{FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_SOURCE}}; +use proj::{app::app::App, models::{ShaderSource, object::ObjData}, shaders::{FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_SOURCE}}; use winit::event_loop::EventLoop; -const TRIANGLE_OBJ_PATH: &str = "assets/models/small_house/model.obj"; +const HOUSE_OBJ_PATH: &str = "assets/models/small_house/model.obj"; +const HOUSE_MAT_PATH: &str = "assets/models/small_house/materials.mtl"; fn main() { dotenvy::dotenv().ok(); let event_loop = EventLoop::new() .expect("Should be able to have Event Loop"); - let mut mesh = Mesh::new(); - if let Err(e) = mesh.load_obj(TRIANGLE_OBJ_PATH) { - panic!("{}", e); - } - let shader_src = ShaderSource { vertex_src: VERTEX_SHADER_SOURCE, fragment_src: FRAGMENT_SHADER_SOURCE }; - let scene_data = ModelData { - name: "home".to_string(), - objects_data: vec![(mesh, shader_src)] + let obj_data = match ObjData::load_obj(HOUSE_OBJ_PATH) { + Ok(o) => o, + Err(e) => return eprintln!("Error: {}", e), }; - let mut app = App::new(scene_data); + println!("Done"); + + let materials = match ObjData::load_materials(HOUSE_MAT_PATH) { + Ok(m) => m, + Err(e) => return eprintln!("Error: {}", e), + }; + + dbg!(&materials); + + let mut app = App::new(); app.set_current_scene("home"); diff --git a/src/models/material.rs b/src/models/material.rs index a804b48..47185df 100644 --- a/src/models/material.rs +++ b/src/models/material.rs @@ -1,18 +1,26 @@ -#[derive(Debug)] +use super::Vec3; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct TextureId(pub u32); + +#[derive(Clone, Debug, Default)] pub struct Material { - pub ambient: [f32; 3], - pub diffuse: [f32; 3], - pub specular: [f32; 3], - pub shader_name: String, + pub name: String, + + pub ambient: Vec3, + pub diffuse: Vec3, + pub specular: Vec3, + pub emissive: Vec3, + pub shininess: f32, + pub opacity: f32, + + pub diffuse_texture: Option, + pub normal_texture: Option, + pub specular_texture: Option, } impl Material { pub fn new() -> Self { - Self { - ambient: Default::default(), - diffuse: Default::default(), - specular: Default::default(), - shader_name: String::new(), - } + Default::default() } } diff --git a/src/models/mesh.rs b/src/models/mesh.rs index 89e125e..342e4ea 100644 --- a/src/models/mesh.rs +++ b/src/models/mesh.rs @@ -1,124 +1,44 @@ use std::collections::HashMap; -use crate::models::Material; + +use crate::models::{VertexIndex, object::{ObjData, ObjMesh}}; use super::Vertex; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Mesh { + pub name: String, pub vertices: Vec, - pub material_name: String, -} - -const NEW_MATERIAL_DELIMITER: &str = "newmtl "; - -fn parse_rgb(values: &str) -> Result<[f32; 3], Box> { - let mut rgb = [0.0; 3]; - let mut i = 0; - - let values = values.split_whitespace(); - - for v in values { - let parsed = v.parse::()?; - rgb[i] = parsed; - i += 1; - if i > 2 { - todo!("Implement custom error handling: too many values"); - } - }; - - if i != 3 { - todo!("Implement custom error handling: too few values"); - } - - Ok(rgb) + pub indices: Vec, + pub material_name: Option, } impl Mesh { - pub fn new() -> Self { - Self { - vertices: vec![], - material_name: String::new(), - } - } + pub fn new(obj: &ObjData, obj_mash: &ObjMesh) -> Self { + let mut vertices: Vec = Vec::new(); + let mut indices: Vec = Vec::new(); + let mut vertex_map: HashMap = HashMap::new(); - pub fn load_obj(&mut self, path: &str) -> Result<(), Box> { - let content = std::fs::read_to_string(path)?; - - for line in content.lines() { - if line.starts_with("v ") { - let coords: Result, _> = line - .split_whitespace() - .skip(1) - .take(3) - .map(|s| s.parse::()) - .collect(); - - match coords { - Ok(v) if v.len() == 3 => { - self.vertices.push(Vertex { x: v[0], y: v[1], z: v[2] }) - }, - _ => eprintln!("Warning: Invalid vertex line: {}", line) + for face in &obj_mash.faces { + for i in 1..(face.indices.len() - 1) { + for &idx in &[face.indices[0], face.indices[i], face.indices[i +1]] { + let index = *vertex_map.entry(idx).or_insert_with(|| { + let v = Vertex { + position: obj.vertices[idx.position as usize], + normal: idx.normal.map(|n| obj.normals[n as usize]).unwrap_or_default(), + tex_coord: idx.tex_coord.map(|t| obj.tex_coords[t as usize]).unwrap_or_default(), + }; + vertices.push(v); + (vertices.len() - 1) as u32 + }); + indices.push(index); } } } - - println!("{:#?}", self.vertices); - - Ok(()) - } - - pub fn load_material(&mut self, path: &str) -> Result<(), Box> { - let content = std::fs::read_to_string(path)?; - let mut lines = content.lines().peekable(); - - while let Some(line) = lines.next() { - let line = line.trim(); - if line.is_empty() { - continue ; - } - - if line.starts_with(NEW_MATERIAL_DELIMITER) { - let mat_name = line.strip_prefix(NEW_MATERIAL_DELIMITER).unwrap(); - - while let Some(&next_line) = lines.peek() { - let next = next_line.trim(); - - if next.starts_with(NEW_MATERIAL_DELIMITER) || next.is_empty() { - lines.next(); - - let mut material = Material::new(); - if let Some((key, values)) = next.split_once(' ') { - match key { - "Ka" => { - match parse_rgb(values) { - Ok(v) => material.ka = v, - Err(e) => eprintln!("Error parsing Ka values: {}", e.to_string()) - } - }, - "Kd" => { - match parse_rgb(values) { - Ok(v) => material.kd = v, - Err(e) => eprintln!("Error parsing Kd values: {}", e.to_string()) - } - } - "Ks" => { - match parse_rgb(values) { - Ok(v) => material.ks = v, - Err(e) => eprintln!("Error parsing Ks values: {}", e.to_string()) - } - } - _ => { eprintln!("Unknown key: {key}") } - } - } - - self.materials.insert(mat_name.to_string(), material); - } - } - } + Mesh { + vertices, + indices, + name: obj_mash.name.clone(), + material_name: obj_mash.material_name.clone(), } - - println!("{:#?}", self.vertices); - - Ok(()) } } diff --git a/src/models/model.rs b/src/models/model.rs index 2eb220f..8a118fa 100644 --- a/src/models/model.rs +++ b/src/models/model.rs @@ -1,4 +1,4 @@ -use crate::models::Mesh; +use crate::models::{Material, Mesh}; pub struct ShaderSource { pub vertex_src: &'static [u8], @@ -6,10 +6,11 @@ pub struct ShaderSource { } pub struct Model { - pub objects: Vec + pub objects: Vec, + pub materials: Vec, } -pub struct RenderObject { +pub struct GpuMesh { pub vao: gl::types::GLuint, pub vbo: gl::types::GLuint, pub _ebo: Option, @@ -19,5 +20,10 @@ pub struct RenderObject { pub struct ModelData { pub name: String, - pub meshes: Vec + pub meshes: Vec, +} + +impl From<&ObjData> for Model { + fn from(value: &ObjData) -> Self { + } } diff --git a/src/models/object.rs b/src/models/object.rs new file mode 100644 index 0000000..c41386c --- /dev/null +++ b/src/models/object.rs @@ -0,0 +1,200 @@ +use crate::models::{Material, Vec2, VertexIndex, primitives::Vec3}; + +#[derive(Debug)] +pub struct ObjData { + pub normals: Vec, + pub vertices: Vec, + pub tex_coords: Vec, + pub meshes: Vec, + pub material_lib: Option +} + +#[derive(Debug)] +pub struct ObjMesh { + pub name: String, + pub material_name: Option, + pub faces: Vec, +} + +#[derive(Debug)] +pub struct ObjFace { + pub indices: Vec, +} + +const NEW_MATERIAL_DELIMITER: &str = "newmtl "; + +fn parse_rgb(values: &str) -> Result<[f32; 3], Box> { + let mut rgb = [0.0; 3]; + let mut i = 0; + + let values = values.split_whitespace(); + + for v in values { + if i > 3 { + todo!("Implement custom error handling: too many values"); + } + let parsed = v.parse::()?; + rgb[i] = parsed; + i += 1; + }; + + if i != 3 { + todo!("Implement custom error handling: too few values"); + } + + Ok(rgb) +} + +impl ObjData { + pub fn new() -> Self { + Self { + normals: vec![], + vertices: vec![], + tex_coords: vec![], + meshes: vec![], + material_lib: None + } + } + + pub fn load_materials(path: &str) -> Result, Box> { + let content = std::fs::read_to_string(path)?; + let mut lines = content.lines().peekable(); + let mut materials = vec![]; + + while let Some(line) = lines.next() { + println!("{line}"); + let line = line.trim(); + if line.is_empty() { + continue ; + } + + if line.starts_with(NEW_MATERIAL_DELIMITER) { + let mat_name = line.strip_prefix(NEW_MATERIAL_DELIMITER).unwrap(); + println!("mat_name {mat_name}"); + + while let Some(&next_line) = lines.peek() { + let next = next_line.trim(); + + println!("{next_line}"); + lines.next(); + + let mut material = Material::new(); + material.name = mat_name.to_string(); + + if let Some((key, values)) = next.split_once(' ') { + match key { + "Ka" => { + match parse_rgb(values) { + Ok(v) => material.ambient = Vec3 { x: v[0], y: v[1], z: v[2] }, + Err(e) => eprintln!("Error parsing Ka values: {}", e.to_string()) + } + }, + "Kd" => { + match parse_rgb(values) { + Ok(v) => material.diffuse = Vec3 { x: v[0], y: v[1], z: v[2] }, + Err(e) => eprintln!("Error parsing Kd values: {}", e.to_string()) + } + } + "Ks" => { + match parse_rgb(values) { + Ok(v) => material.specular = Vec3 { x: v[0], y: v[1], z: v[2] }, + Err(e) => eprintln!("Error parsing Ks values: {}", e.to_string()) + } + } + _ => { eprintln!("Unknown key: {key}") } + } + } + materials.push(material); + } + } + } + + Ok(materials) + } + + pub fn load_obj(path: &str) -> Result> { + let mut obj_data = Self::new(); + let content = std::fs::read_to_string(path)?; + let mut current_name = String::new(); + + let mut current_mash = ObjMesh { + name: String::new(), + material_name: None, + faces: vec![] + }; + + for line in content.lines() { + let line = line.trim(); + let mut line_split = line.split_whitespace(); + + while let Some(split) = line_split.next() { + match split { + "v" => { + let x = line_split.next().unwrap().parse::()?; + let y = line_split.next().unwrap().parse::()?; + let z = line_split.next().unwrap().parse::()?; + + obj_data.vertices.push(Vec3 { x, y, z }); + }, + "vt" => { + let x = line_split.next().unwrap().parse::()?; + let y = line_split.next().unwrap().parse::()?; + + obj_data.tex_coords.push(Vec2 { x, y }) + }, + "vn" => { + let x = line_split.next().unwrap().parse::()?; + let y = line_split.next().unwrap().parse::()?; + let z = line_split.next().unwrap().parse::()?; + + obj_data.normals.push(Vec3 { x, y, z }); + }, + "o" | "g" => { + current_name = line_split.next().unwrap_or("").to_string(); + current_mash.name = current_name.clone(); + } + "usemtl" => { + if !current_mash.faces.is_empty() { + obj_data.meshes.push(current_mash); + } + + let material_name = line_split.next().and_then(|l| Some(l.to_string())); + current_mash = ObjMesh { + name: current_name.clone(), + material_name, + faces: vec![] + }; + }, + "f" => { + let mut current_face = ObjFace { indices: vec![] }; + + while let Some(val) = line_split.next() { + let values_split = val.split('/'); + let mut face_index: [u32; 3] = [0; 3]; + + for (i, value) in values_split.enumerate() { + if !value.is_empty() { + face_index[i] = value.parse::()?; + } + } + + let vertex_index = VertexIndex { + position: face_index[0], + tex_coord: if face_index[1] != 0 { Some(face_index[1]) } else { None }, + normal: if face_index[2] != 0 { Some(face_index[2]) } else { None }, + }; + + current_face.indices.push(vertex_index); + } + current_mash.faces.push(current_face); + } + _ => {} + } + } + } + if !current_mash.faces.is_empty() { + obj_data.meshes.push(current_mash); + } + Ok(obj_data) + } +} diff --git a/src/models/primitives.rs b/src/models/primitives.rs new file mode 100644 index 0000000..ce98764 --- /dev/null +++ b/src/models/primitives.rs @@ -0,0 +1,194 @@ +use std::ops::Sub; + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct Vec2 { + pub x: f32, + pub y: f32, +} + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct Vec3 { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl Sub for Vec3 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } + } +} + +impl Vec3 { + pub fn length(&self) -> f32 { + (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() + } + + pub fn normalize(&mut self) -> Self { + let len = self.length(); + + if len == 0.0 { + return *self; + } + Self { + x: self.x / len, + y: self.y / len, + z: self.z / len, + } + } + + pub fn dot(&self, other: Vec3) -> f32 { + self.x * other.x + self.y * other.y + self.z * other.z + } + + pub fn cross(&self, other: Self) -> Self { + Self { + x: self.y * other.z - self.z * other.y, + y: self.z * other.x - self.x * other.z, + z: self.x * other.y - self.y * other.x, + } + } +} + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct Vec4 { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Mat4 { + pub data: [f32; 16], +} + +impl Mat4 { + pub fn identity() -> Self { + Self { + data: [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + ], + } + } + + pub fn translation(x: f32, y: f32, z: f32) -> Self { + Self { + data: [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + x, y, z, 1.0, + ], + } + } + + pub fn scale(x: f32, y: f32, z: f32) -> Self { + Self { + data: [ + x, 0.0, 0.0, 0.0, + 0.0, y, 0.0, 0.0, + 0.0, 0.0, z, 0.0, + 0.0, 0.0, 0.0, 1.0, + ], + } + } + + pub fn rotation_x(angle: f32) -> Self { + let c = angle.cos(); + let s = angle.sin(); + Self { + data: [ + 1.0, 0.0, 0.0, 0.0, + 0.0, c, s, 0.0, + 0.0, -s, c, 0.0, + 0.0, 0.0, 0.0, 1.0, + ], + } + } + + pub fn rotation_y(angle: f32) -> Self { + let c = angle.cos(); + let s = angle.sin(); + Self { + data: [ + c, 0.0, -s, 0.0, + 0.0, 1.0, 0.0, 0.0, + s, 0.0, c, 0.0, + 0.0, 0.0, 0.0, 1.0, + ], + } + } + + pub fn rotation_z(angle: f32) -> Self { + let c = angle.cos(); + let s = angle.sin(); + Self { + data: [ + c, s, 0.0, 0.0, + -s, c, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + ], + } + } + + pub fn perspective(fov: f32, aspect: f32, near: f32, far: f32) -> Self { + let f = 1.0 / (fov / 2.0).tan(); + let nf = 1.0 / (near - far); + Self { + data: [ + f / aspect, 0.0, 0.0, 0.0, + 0.0, f, 0.0, 0.0, + 0.0, 0.0, (far + near) * nf, -1.0, + 0.0, 0.0, 2.0 * far * near * nf, 0.0, + ], + } + } + + pub fn look_at(eye: Vec3, target: Vec3, up: Vec3) -> Self { + let f = (target - eye).normalize(); + let r = f.cross(up).normalize(); + let u = r.cross(f); + Self { + data: [ + r.x, u.x, -f.x, 0.0, + r.y, u.y, -f.y, 0.0, + r.z, u.z, -f.z, 0.0, + -r.dot(eye), -u.dot(eye), f.dot(eye), 1.0, + ], + } + } + + pub fn as_ptr(&self) -> *const f32 { + self.data.as_ptr() + } +} + +impl std::ops::Mul for Mat4 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + let mut result = [0.0f32; 16]; + for row in 0..4 { + for col in 0..4 { + for k in 0..4 { + result[col * 4 + row] += self.data[k * 4 + row] * rhs.data[col * 4 + k]; + } + } + } + Self { data: result } + } +} diff --git a/src/models/vertex.rs b/src/models/vertex.rs index 280107c..5e7925c 100644 --- a/src/models/vertex.rs +++ b/src/models/vertex.rs @@ -1,7 +1,15 @@ +use crate::models::{ Vec2, Vec3 }; + #[derive(Debug)] pub struct Vertex { - pub x: f32, - pub y: f32, - pub z: f32, + pub position: Vec3, + pub normal: Vec3, + pub tex_coord: Vec2, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct VertexIndex { + pub position: u32, + pub tex_coord: Option, + pub normal: Option +} diff --git a/src/shaders/shaders.rs b/src/shaders/constants.rs similarity index 100% rename from src/shaders/shaders.rs rename to src/shaders/constants.rs diff --git a/src/shaders/shader.rs b/src/shaders/shader.rs new file mode 100644 index 0000000..a78b1e3 --- /dev/null +++ b/src/shaders/shader.rs @@ -0,0 +1,43 @@ +use crate::{app::gl, models::{Mat4, Vec3}}; + +pub struct Shader { + pub program: gl::types::GLuint, + pub gl: std::rc::Rc, +} + +impl Shader { + pub fn use_program(&self) { + unsafe { self.gl.UseProgram(self.program) }; + } + + pub fn set_uniform_mat4(&self, name: &str, mat: &Mat4) { + unsafe { + let loc = self.gl.GetUniformLocation( + self.program, + name.as_ptr() as *const i8 + ); + self.gl.UniformMatrix4fv(loc, 1, gl::FALSE, mat.as_ptr()); + } + } + + pub fn set_uniform_vec3(&self, name: &str, v: &Vec3) { + unsafe { + let loc = self.gl.GetUniformLocation(self.program, name.as_ptr() as *const i8); + self.gl.Uniform3f(loc, v.x, v.y, v.z); + } + } + + pub fn set_uniform_f32(&self, name: &str, v: f32) { + unsafe { + let loc = self.gl.GetUniformLocation(self.program, name.as_ptr() as *const i8); + self.gl.Uniform1f(loc, v); + } + } + + pub fn set_uniform_i32(&self, name: &str, v: i32) { + unsafe { + let loc = self.gl.GetUniformLocation(self.program, name.as_ptr() as *const i8); + self.gl.Uniform1i(loc, v); // used for texture slots + } + } +}