broken state - now its complicated
This commit is contained in:
parent
0cf7d5eb43
commit
1775cf90bc
@ -13,7 +13,7 @@ use winit::{event, window};
|
|||||||
|
|
||||||
use crate::app::gl::{gl_config_picker, gl_create_context};
|
use crate::app::gl::{gl_config_picker, gl_create_context};
|
||||||
use crate::app::renderer::Renderer;
|
use crate::app::renderer::Renderer;
|
||||||
use crate::models::ModelData;
|
use crate::models::Scene;
|
||||||
|
|
||||||
enum GlDisplayCreationState {
|
enum GlDisplayCreationState {
|
||||||
Builder(Box<DisplayBuilder>),
|
Builder(Box<DisplayBuilder>),
|
||||||
@ -32,13 +32,12 @@ pub struct App {
|
|||||||
|
|
||||||
renderer: Option<Renderer>,
|
renderer: Option<Renderer>,
|
||||||
current_scene: Option<String>,
|
current_scene: Option<String>,
|
||||||
scene_data: ModelData,
|
|
||||||
|
|
||||||
state: Option<AppState>,
|
state: Option<AppState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(scene_data: ModelData) -> Self {
|
pub fn new() -> Self {
|
||||||
let template = ConfigTemplateBuilder::new()
|
let template = ConfigTemplateBuilder::new()
|
||||||
.with_alpha_size(8)
|
.with_alpha_size(8)
|
||||||
.with_transparency(cfg!(target_os = "macos"));
|
.with_transparency(cfg!(target_os = "macos"));
|
||||||
@ -51,7 +50,6 @@ impl App {
|
|||||||
gl_context: None,
|
gl_context: None,
|
||||||
renderer: None,
|
renderer: None,
|
||||||
state: None,
|
state: None,
|
||||||
scene_data,
|
|
||||||
current_scene: None,
|
current_scene: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +110,7 @@ impl ApplicationHandler for App {
|
|||||||
if self.renderer.is_none() {
|
if self.renderer.is_none() {
|
||||||
let mut renderer = Renderer::new(&gl_context.display());
|
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");
|
println!("Built Renderer");
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use glutin::prelude::GlDisplay;
|
use glutin::prelude::GlDisplay;
|
||||||
use crate::app::gl;
|
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::collections::HashMap;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ffi::{ CStr, CString };
|
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 {
|
pub struct Renderer {
|
||||||
gl: gl::Gl,
|
gl: std::rc::Rc<gl::Gl>,
|
||||||
shaders: HashMap<String, gl::types::GLuint>,
|
shaders: HashMap<String, gl::types::GLuint>,
|
||||||
scenes: HashMap<String, Scene>
|
scenes: HashMap<String, Scene>
|
||||||
}
|
}
|
||||||
@ -38,26 +39,13 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
gl,
|
gl: std::rc::Rc::new(gl),
|
||||||
shaders: HashMap::new(),
|
shaders: HashMap::new(),
|
||||||
scenes: HashMap::new()
|
scenes: HashMap::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload_scene_data(&mut self, scene_data: &ModelData) {
|
pub fn upload_mesh(&self, mesh: &models::Mesh) -> GpuMesh {
|
||||||
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::<Vec<(RenderObject, u32)>>();
|
|
||||||
println!("Uploading Scene {}", scene_data.name);
|
|
||||||
self.scenes.insert(scene_data.name.clone(), Model { objects });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload_mesh(&self, mesh: &models::Mesh) -> RenderObject {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let gl = &self.gl;
|
let gl = &self.gl;
|
||||||
|
|
||||||
@ -71,7 +59,7 @@ impl Renderer {
|
|||||||
let vertex_data = mesh.vertices
|
let vertex_data = mesh.vertices
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| {
|
.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::<Vec<[f32; 6]>>();
|
.collect::<Vec<[f32; 6]>>();
|
||||||
|
|
||||||
@ -106,7 +94,7 @@ impl Renderer {
|
|||||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
||||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
||||||
|
|
||||||
RenderObject {
|
GpuMesh {
|
||||||
vao,
|
vao,
|
||||||
vbo,
|
vbo,
|
||||||
_ebo: None,
|
_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 {
|
unsafe {
|
||||||
let vertex_shader = Renderer::compile_shader(&self.gl, gl::VERTEX_SHADER, shader_src.vertex_src);
|
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);
|
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(vertex_shader);
|
||||||
self.gl.DeleteShader(fragment_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 {
|
unsafe {
|
||||||
self.UseProgram(shader);
|
self.UseProgram(shader);
|
||||||
|
|
||||||
@ -182,9 +173,9 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scene) = self.scenes.get(current_scene) {
|
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);
|
self.draw_object(obj, *shader);
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Unknown Scene Requested: {}", current_scene);
|
eprintln!("Unknown Scene Requested: {}", current_scene);
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/lib.rs
12
src/lib.rs
@ -13,6 +13,11 @@ pub mod models {
|
|||||||
|
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub use material::*;
|
pub use material::*;
|
||||||
|
|
||||||
|
pub mod primitives;
|
||||||
|
pub use primitives::*;
|
||||||
|
|
||||||
|
pub mod object;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod app {
|
pub mod app {
|
||||||
@ -22,6 +27,9 @@ pub mod app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod shaders {
|
pub mod shaders {
|
||||||
pub mod shaders;
|
pub mod shader;
|
||||||
pub use shaders::*;
|
pub use shader::*;
|
||||||
|
|
||||||
|
pub mod constants;
|
||||||
|
pub use constants::*;
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/main.rs
27
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;
|
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() {
|
fn main() {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
let event_loop = EventLoop::new()
|
let event_loop = EventLoop::new()
|
||||||
.expect("Should be able to have Event Loop");
|
.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 {
|
let shader_src = ShaderSource {
|
||||||
vertex_src: VERTEX_SHADER_SOURCE,
|
vertex_src: VERTEX_SHADER_SOURCE,
|
||||||
fragment_src: FRAGMENT_SHADER_SOURCE
|
fragment_src: FRAGMENT_SHADER_SOURCE
|
||||||
};
|
};
|
||||||
|
|
||||||
let scene_data = ModelData {
|
let obj_data = match ObjData::load_obj(HOUSE_OBJ_PATH) {
|
||||||
name: "home".to_string(),
|
Ok(o) => o,
|
||||||
objects_data: vec![(mesh, shader_src)]
|
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");
|
app.set_current_scene("home");
|
||||||
|
|
||||||
|
|||||||
@ -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 struct Material {
|
||||||
pub ambient: [f32; 3],
|
pub name: String,
|
||||||
pub diffuse: [f32; 3],
|
|
||||||
pub specular: [f32; 3],
|
pub ambient: Vec3,
|
||||||
pub shader_name: String,
|
pub diffuse: Vec3,
|
||||||
|
pub specular: Vec3,
|
||||||
|
pub emissive: Vec3,
|
||||||
|
pub shininess: f32,
|
||||||
|
pub opacity: f32,
|
||||||
|
|
||||||
|
pub diffuse_texture: Option<TextureId>,
|
||||||
|
pub normal_texture: Option<TextureId>,
|
||||||
|
pub specular_texture: Option<TextureId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material {
|
impl Material {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Default::default()
|
||||||
ambient: Default::default(),
|
|
||||||
diffuse: Default::default(),
|
|
||||||
specular: Default::default(),
|
|
||||||
shader_name: String::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,124 +1,44 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::models::Material;
|
|
||||||
|
use crate::models::{VertexIndex, object::{ObjData, ObjMesh}};
|
||||||
|
|
||||||
use super::Vertex;
|
use super::Vertex;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
|
pub name: String,
|
||||||
pub vertices: Vec<Vertex>,
|
pub vertices: Vec<Vertex>,
|
||||||
pub material_name: String,
|
pub indices: Vec<u32>,
|
||||||
}
|
pub material_name: Option<String>,
|
||||||
|
|
||||||
const NEW_MATERIAL_DELIMITER: &str = "newmtl ";
|
|
||||||
|
|
||||||
fn parse_rgb(values: &str) -> Result<[f32; 3], Box<dyn std::error::Error>> {
|
|
||||||
let mut rgb = [0.0; 3];
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
let values = values.split_whitespace();
|
|
||||||
|
|
||||||
for v in values {
|
|
||||||
let parsed = v.parse::<f32>()?;
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mesh {
|
impl Mesh {
|
||||||
pub fn new() -> Self {
|
pub fn new(obj: &ObjData, obj_mash: &ObjMesh) -> Self {
|
||||||
Self {
|
let mut vertices: Vec<Vertex> = Vec::new();
|
||||||
vertices: vec![],
|
let mut indices: Vec<u32> = Vec::new();
|
||||||
material_name: String::new(),
|
let mut vertex_map: HashMap<VertexIndex, u32> = HashMap::new();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_obj(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
|
for face in &obj_mash.faces {
|
||||||
let content = std::fs::read_to_string(path)?;
|
for i in 1..(face.indices.len() - 1) {
|
||||||
|
for &idx in &[face.indices[0], face.indices[i], face.indices[i +1]] {
|
||||||
for line in content.lines() {
|
let index = *vertex_map.entry(idx).or_insert_with(|| {
|
||||||
if line.starts_with("v ") {
|
let v = Vertex {
|
||||||
let coords: Result<Vec<f32>, _> = line
|
position: obj.vertices[idx.position as usize],
|
||||||
.split_whitespace()
|
normal: idx.normal.map(|n| obj.normals[n as usize]).unwrap_or_default(),
|
||||||
.skip(1)
|
tex_coord: idx.tex_coord.map(|t| obj.tex_coords[t as usize]).unwrap_or_default(),
|
||||||
.take(3)
|
};
|
||||||
.map(|s| s.parse::<f32>())
|
vertices.push(v);
|
||||||
.collect();
|
(vertices.len() - 1) as u32
|
||||||
|
});
|
||||||
match coords {
|
indices.push(index);
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Mesh {
|
||||||
println!("{:#?}", self.vertices);
|
vertices,
|
||||||
|
indices,
|
||||||
Ok(())
|
name: obj_mash.name.clone(),
|
||||||
}
|
material_name: obj_mash.material_name.clone(),
|
||||||
|
|
||||||
pub fn load_material(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
println!("{:#?}", self.vertices);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::models::Mesh;
|
use crate::models::{Material, Mesh};
|
||||||
|
|
||||||
pub struct ShaderSource {
|
pub struct ShaderSource {
|
||||||
pub vertex_src: &'static [u8],
|
pub vertex_src: &'static [u8],
|
||||||
@ -6,10 +6,11 @@ pub struct ShaderSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
pub objects: Vec<RenderObject>
|
pub objects: Vec<GpuMesh>,
|
||||||
|
pub materials: Vec<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderObject {
|
pub struct GpuMesh {
|
||||||
pub vao: gl::types::GLuint,
|
pub vao: gl::types::GLuint,
|
||||||
pub vbo: gl::types::GLuint,
|
pub vbo: gl::types::GLuint,
|
||||||
pub _ebo: Option<gl::types::GLuint>,
|
pub _ebo: Option<gl::types::GLuint>,
|
||||||
@ -19,5 +20,10 @@ pub struct RenderObject {
|
|||||||
|
|
||||||
pub struct ModelData {
|
pub struct ModelData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub meshes: Vec<Mesh>
|
pub meshes: Vec<Mesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ObjData> for Model {
|
||||||
|
fn from(value: &ObjData) -> Self {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
200
src/models/object.rs
Normal file
200
src/models/object.rs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
use crate::models::{Material, Vec2, VertexIndex, primitives::Vec3};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ObjData {
|
||||||
|
pub normals: Vec<Vec3>,
|
||||||
|
pub vertices: Vec<Vec3>,
|
||||||
|
pub tex_coords: Vec<Vec2>,
|
||||||
|
pub meshes: Vec<ObjMesh>,
|
||||||
|
pub material_lib: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ObjMesh {
|
||||||
|
pub name: String,
|
||||||
|
pub material_name: Option<String>,
|
||||||
|
pub faces: Vec<ObjFace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ObjFace {
|
||||||
|
pub indices: Vec<VertexIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NEW_MATERIAL_DELIMITER: &str = "newmtl ";
|
||||||
|
|
||||||
|
fn parse_rgb(values: &str) -> Result<[f32; 3], Box<dyn std::error::Error>> {
|
||||||
|
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::<f32>()?;
|
||||||
|
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<Vec<Material>, Box<dyn std::error::Error>> {
|
||||||
|
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<Self, Box<dyn std::error::Error>> {
|
||||||
|
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::<f32>()?;
|
||||||
|
let y = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
let z = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
|
||||||
|
obj_data.vertices.push(Vec3 { x, y, z });
|
||||||
|
},
|
||||||
|
"vt" => {
|
||||||
|
let x = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
let y = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
|
||||||
|
obj_data.tex_coords.push(Vec2 { x, y })
|
||||||
|
},
|
||||||
|
"vn" => {
|
||||||
|
let x = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
let y = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
let z = line_split.next().unwrap().parse::<f32>()?;
|
||||||
|
|
||||||
|
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::<u32>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
194
src/models/primitives.rs
Normal file
194
src/models/primitives.rs
Normal file
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,15 @@
|
|||||||
|
use crate::models::{ Vec2, Vec3 };
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pub x: f32,
|
pub position: Vec3,
|
||||||
pub y: f32,
|
pub normal: Vec3,
|
||||||
pub z: f32,
|
pub tex_coord: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct VertexIndex {
|
||||||
|
pub position: u32,
|
||||||
|
pub tex_coord: Option<u32>,
|
||||||
|
pub normal: Option<u32>
|
||||||
|
}
|
||||||
|
|||||||
43
src/shaders/shader.rs
Normal file
43
src/shaders/shader.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::{app::gl, models::{Mat4, Vec3}};
|
||||||
|
|
||||||
|
pub struct Shader {
|
||||||
|
pub program: gl::types::GLuint,
|
||||||
|
pub gl: std::rc::Rc<gl::Gl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user