From 8b9e08f076c798ec6072a9adb0f22d2bd30f3fb4 Mon Sep 17 00:00:00 2001 From: Victor Vobis Date: Fri, 16 Jan 2026 18:06:49 +0100 Subject: [PATCH] first rendering done --- .gitignore | 1 + assets/models/bugatti/README.txt | 9 + assets/models/bugatti/bugatti.mtl | 400 ++++++++++++++++++++++++++++++ src/app/app.rs | 92 ++++++- src/app/camera.rs | 34 ++- src/app/light.rs | 14 +- src/app/scene.rs | 56 ++++- src/assets/mtl.rs | 164 ++++++++++-- src/assets/obj.rs | 6 +- src/backend/backend.rs | 46 ++-- src/backend/gpu/mesh.rs | 1 + src/backend/opengl/gl.rs | 21 -- src/backend/opengl/gl_backend.rs | 80 +++++- src/backend/opengl/gl_renderer.rs | 100 ++++---- src/backend/opengl/gl_shader.rs | 1 + src/geometry/mesh.rs | 58 ++++- src/geometry/vertex.rs | 92 +++++++ src/handles.rs | 16 +- src/lib.rs | 3 +- src/main.rs | 41 +-- src/math/matrix/mat4.rs | 6 +- src/math/vector/vec2.rs | 110 ++++---- src/math/vector/vec3.rs | 142 +++++++---- src/math/vector/vec4.rs | 154 +++++++----- src/models/model.rs | 32 +++ src/shaders/constants.rs | 26 ++ src/shaders/inputs.rs | 55 ++++ src/shaders/types.rs | 16 +- 28 files changed, 1457 insertions(+), 319 deletions(-) create mode 100644 assets/models/bugatti/README.txt create mode 100644 assets/models/bugatti/bugatti.mtl create mode 100644 src/shaders/inputs.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..9fee95d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +assets/models/bugatti/* diff --git a/assets/models/bugatti/README.txt b/assets/models/bugatti/README.txt new file mode 100644 index 0000000..763cf86 --- /dev/null +++ b/assets/models/bugatti/README.txt @@ -0,0 +1,9 @@ +Hi, thanks for downloadiing this model.It was created by Bob Kimani (Kimz Auto). +if you like the model please feel free to donate to my PayPal account at + bobiankimani@gmail.com +Or simply buy the model for a price of your choice here + +https://sellfy.com/p/FrEB/ +I would reaaly appreciate it even if its just one dollar.Thanks;) +Please include this credit when you use the file. +Regards Bob :) \ No newline at end of file diff --git a/assets/models/bugatti/bugatti.mtl b/assets/models/bugatti/bugatti.mtl new file mode 100644 index 0000000..a26b5e3 --- /dev/null +++ b/assets/models/bugatti/bugatti.mtl @@ -0,0 +1,400 @@ +# Blender MTL File: 'bugatti.blend' +# Material Count: 40 + +newmtl BLUE.002 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl ENGINE +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl GRILL.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.026482 0.026482 0.026482 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Mirrors +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl NAVY +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.017778 0.047407 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl None +Ns 0 +Ka 0.000000 0.000000 0.000000 +Kd 0.8 0.8 0.8 +Ks 0.8 0.8 0.8 +d 1 +illum 2 + +newmtl Nut.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Studio_Lights.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Studio_Lights.005 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Trim.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl WHEEL_CURB.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl WHITE.002 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl aluminiumm +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl back_drop.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.102592 0.102592 0.102592 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black_cable +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black_fuel_tank.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black_side_vent +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.001651 0.001651 0.001651 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black_strip.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.009774 0.009774 0.009774 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl black_vent.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl brake_disk.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl breaks_ +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl cALLIPERS.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.014633 0.586055 0.640002 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl exhaust +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.204648 0.451652 0.640001 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl front_window.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl glass_front.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.054000 0.640003 0.568643 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl glossy +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.568960 0.640002 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl headlight_glass.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl headlight_square.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl red.002 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl red_glass +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640002 0.047774 0.026908 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl reflective_coat.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.026482 0.026482 0.026482 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl rim_blue.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.026033 0.152250 0.640001 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl rim_silver.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl silver_door_strip.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.601480 0.265530 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl silver_trim +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.043975 0.043975 0.043975 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl sun +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl tyre.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.011736 0.011736 0.011736 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl wheel_joint.004 +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl white_holders +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/src/app/app.rs b/src/app/app.rs index e5411b6..7b8629e 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -1,21 +1,74 @@ use winit::application::ApplicationHandler; -use winit::event::KeyEvent; -use winit::keyboard::{Key, NamedKey}; +use winit::keyboard::{KeyCode, PhysicalKey}; use winit::window::WindowAttributes; use winit::{event, window}; -use crate::backend::RenderBackend; +use crate::app::scene::Scene; +use crate::backend::{DrawCommand, RenderBackend}; +use crate::geometry::Mesh; +use crate::handles::{MeshHandle, ShaderHandle}; +use crate::models::{self, ModelData}; + +pub enum UploadData { + ModelData(ModelData), + Shader(B::ShaderSource), +} pub struct App { - backend: B, + pub backend: B, + pub scene: Scene, + pub meshes: Vec, + pub mesh_handles: Vec, + pub model_queue: Vec, + pub shader_queue: Vec, + pub shaders: Vec, } impl App { pub fn new() -> Self { Self { backend: B::new(), + scene: Default::default(), + meshes: Vec::new(), + mesh_handles: Vec::new(), + shaders: Vec::new(), + model_queue: Vec::new(), + shader_queue: Vec::new(), } } + + pub fn add_shader(&mut self, shader_src: B::ShaderSource) { + self.shader_queue.push(shader_src); + } + + pub fn add_model(&mut self, model_data: ModelData) { + self.model_queue.push(model_data); + } + + fn upload_data_queue(&mut self) { + for mesh in &self.meshes { + let handle = self.backend.create_mesh(mesh).expect("Should be able to create mesh"); + self.mesh_handles.push(handle); + } + let mut model: models::Model = Default::default(); + for shader_src in &self.shader_queue { + let shader = self.backend.create_shader(&shader_src).expect("Should be able to create shaders"); + self.shaders.push(shader); + } + for model_data in &mut self.model_queue { + for mesh in &mut model_data.meshes { + let mat = model_data.materials.iter_mut().find(|mat| mat.name == *mesh.material_name.as_ref() + .expect("Mesh should have associated Material!") + ).expect("Model should have Material requested by mesh"); + mat.shader = *self.shaders.get(0).expect("Should have a shader"); + let new_mesh = self.backend.create_mesh(mesh).unwrap(); + let mat_handle = self.scene.add_material(mat); + model.parts.push((new_mesh, mat_handle)) + } + model.set_transform(model_data.transform); + } + self.scene.models.push(model); + } } pub fn window_attributes() -> WindowAttributes { @@ -27,6 +80,7 @@ pub fn window_attributes() -> WindowAttributes { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { self.backend.resume(event_loop); + self.upload_data_queue(); } fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { @@ -42,10 +96,25 @@ impl ApplicationHandler for App { match event { event::WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => { }, - event::WindowEvent::CloseRequested - | event::WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, .. } => { + event::WindowEvent::CloseRequested => { event_loop.exit(); } + event::WindowEvent::KeyboardInput { event, .. } => { + println!("Hit key: {:#?}", &event.physical_key); + match event.physical_key { + PhysicalKey::Code(KeyCode::KeyW) => { + self.scene.camera.position.z -= 0.1; + } + PhysicalKey::Code(KeyCode::KeyS) => { + self.scene.camera.position.z += 0.1; + } + PhysicalKey::Code(KeyCode::Escape) => { + event_loop.exit(); + } + _ => { + } + } + } _ => () } } @@ -63,5 +132,16 @@ impl ApplicationHandler for App { } fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + self.backend.begin_frame(); + for mesh in &self.mesh_handles { + let cmd = DrawCommand { + mesh: mesh.clone(), + shader: ShaderHandle(0), + textures: &[], + uniforms: &[] + }; + self.backend.draw(&cmd); + } + self.backend.end_frame(); } } diff --git a/src/app/camera.rs b/src/app/camera.rs index b979b64..7bb64a4 100644 --- a/src/app/camera.rs +++ b/src/app/camera.rs @@ -1,3 +1,35 @@ -pub struct Camera { +use crate::math::{Mat4, Vec3}; +pub struct Camera { + pub position: Vec3, + pub target: Vec3, + pub up: Vec3, + pub fov: f32, + pub aspect_ratio: f32, + pub near: f32, + pub far: f32, +} + +impl Default for Camera { + fn default() -> Self { + Self { + position: Vec3::new(0.0, 2.0, 5.0), + target: Vec3::new(0.0, 0.0, 0.0), + up: Vec3::new(0.0, 1.0, 0.0), + fov: 45.0_f32.to_radians(), + aspect_ratio: 16.0 / 9.0, + near: 0.1, + far: 100.0, + } + } +} + +impl Camera { + pub fn view_matrix(&self) -> Mat4 { + Mat4::look_at(self.position, self.target, self.up) + } + + pub fn projection_matrix(&self) -> Mat4 { + Mat4::perspective(self.fov, self.aspect_ratio, self.near, self.far) + } } diff --git a/src/app/light.rs b/src/app/light.rs index 1f7c88c..4e339ca 100644 --- a/src/app/light.rs +++ b/src/app/light.rs @@ -1,3 +1,15 @@ -pub struct Light { +use crate::math::Vec3; +pub struct Light { + pub position: Vec3, + pub color: Vec3, +} + +impl Default for Light { + fn default() -> Self { + Self { + position: Vec3::new(3.0, 5.0, 3.0), + color: Vec3::new(1.0, 1.0, 1.0), + } + } } diff --git a/src/app/scene.rs b/src/app/scene.rs index 4ba472d..f12d32a 100644 --- a/src/app/scene.rs +++ b/src/app/scene.rs @@ -1,36 +1,74 @@ use std::collections::HashMap; -use crate::{app::{camera::Camera, light::Light}, assets, backend::{backend, opengl::GlDrawCommand}, handles, models::Model}; +use crate::{app::{camera::Camera, light::Light}, assets, backend::{Uniform, backend}, handles, math::Mat4, models::Model}; +#[derive(Default)] pub struct Scene { pub light: Light, pub camera: Camera, pub models: Vec, - pub materials: HashMap + pub materials: HashMap, + next_mat_id: u32, } impl Scene { pub fn new() -> Self { Self { - light: Light {}, - camera: Camera {}, + light: Light::default(), + camera: Camera::default(), materials: HashMap::new(), + next_mat_id: 0, models: vec![] } } - pub fn render(&self, backend: &impl backend::RenderBackend) { - for model in &self.models { + pub fn add_material(&mut self, material: &assets::mtl::Material) -> handles::MaterialHandle { + let mat_handle = handles::MaterialHandle(self.next_mat_id); + self.materials.insert(mat_handle, material.clone()); + self.next_mat_id += 1; + mat_handle + } + + pub fn render(&mut self, backend: &mut impl backend::RenderBackend) { + backend.begin_frame(); + + let view = self.camera.view_matrix(); + let projection = self.camera.projection_matrix(); + + let scene_uniforms = [ + Uniform::Mat4(c"u_view".into(), view), + Uniform::Mat4(c"u_projection".into(), projection), + Uniform::Vec3(c"u_view_pos".into(), self.camera.position), + Uniform::Vec3(c"u_light_pos".into(), self.light.position), + Uniform::Vec3(c"u_light_color".into(), self.light.color), + ]; + + for model in &mut self.models { + let current_transform = model.transform; + let transform = current_transform + * Mat4::rotation_y(0.05); + + model.set_transform(transform); + + let model_uniform = Uniform::Mat4(c"u_model".into(), model.transform); + for (mesh, mtl) in &model.parts { let mtl = &self.materials[&mtl]; - let draw_cmd = GlDrawCommand { + + let mut all_uniforms = vec![model_uniform.clone()]; + all_uniforms.extend(scene_uniforms.iter().cloned()); + all_uniforms.extend(mtl.uniforms().iter().cloned()); + + let draw_cmd = backend::DrawCommand { mesh: *mesh, shader: mtl.shader, - uniforms: mtl.to_uniforms(), - textures: None, + uniforms: &all_uniforms, + textures: &vec![], }; + backend.draw(&draw_cmd); } } + backend.end_frame(); } } diff --git a/src/assets/mtl.rs b/src/assets/mtl.rs index b95edb8..8ac27bf 100644 --- a/src/assets/mtl.rs +++ b/src/assets/mtl.rs @@ -1,41 +1,153 @@ -use crate::{assets::{error::AssetError, parser::parse_args}, backend::opengl::Uniform, math::Vec3, shaders::ShaderHandle}; +use core::ffi; + +use crate::{assets::{error::AssetError, parser::parse_args}, backend::Uniform, handles::ShaderHandle, math::{Mat4, Vec3, Vec4, Vec2}}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct TextureId(pub u32); -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Material { pub name: String, pub shader: ShaderHandle, - pub ambient: Vec3, - pub diffuse: Vec3, - pub specular: Vec3, - pub emissive: Vec3, - pub shininess: f32, - pub opacity: f32, + ambient: Vec3, + diffuse: Vec3, + specular: Vec3, + emissive: Vec3, + _shininess: f32, + opacity: f32, pub diffuse_texture: Option, pub normal_texture: Option, pub specular_texture: Option, + + uniforms: Vec, + textures: Vec, +} + +impl Default for Material { + fn default() -> Self { + let mut mat = Self { + name: String::new(), + shader: ShaderHandle::default(), + ambient: Vec3::new(0.1, 0.1, 0.1), + diffuse: Vec3::new(0.8, 0.8, 0.8), + specular: Vec3::new(0.5, 0.5, 0.5), + emissive: Vec3::new(0.0, 0.0, 0.0), + _shininess: 32.0, + opacity: 1.0, + diffuse_texture: None, + normal_texture: None, + specular_texture: None, + uniforms: Vec::new(), + textures: Vec::new(), + }; + // Set default uniforms + mat.set_uniform_v3(c"u_ambient", mat.ambient); + mat.set_uniform_v3(c"u_diffuse", mat.diffuse); + mat.set_uniform_v3(c"u_specular", mat.specular); + mat.set_uniform_f(c"u_shininess", mat._shininess); + mat + } } impl Material { pub fn new() -> Self { - Default::default() + Self::default() } - pub fn to_uniforms(&self) -> [Uniform; 8] { - [ - Uniform::Vec3(c"u_ambient".into(), self.ambient), - Uniform::Vec3(c"u_diffuse".into(), self.diffuse), - Uniform::Vec3(c"u_specular".into(), self.specular), - Uniform::Float(c"u_shininess".into(), self.shininess), - Uniform::None, - Uniform::None, - Uniform::None, - Uniform::None, - ] + pub fn set_ambient(&mut self, v: Vec3) { + self.ambient = v; + self.set_uniform_v3(c"u_ambient", v); + } + + pub fn set_diffuse(&mut self, v: Vec3) { + self.diffuse = v; + self.set_uniform_v3(c"u_diffuse", v); + } + + pub fn set_specular(&mut self, v: Vec3) { + self.specular = v; + self.set_uniform_v3(c"u_specular", v); + } + + pub fn set_emissive(&mut self, v: Vec3) { + self.emissive = v; + self.set_uniform_v3(c"u_emissive", v); + } + + pub fn set_shininess(&mut self, v: f32) { + self._shininess = v; + self.set_uniform_f(c"u_shininess", v); + } + + pub fn set_opacity(&mut self, v: f32) { + self.opacity = v; + self.set_uniform_f(c"u_opacity", v); + } + + + pub fn set_uniform_f(&mut self, name: &ffi::CStr, val: f32) { + if let Some(Uniform::Float(_, v)) = self.uniforms + .iter_mut() + .find(|u| matches!(u, Uniform::Float(n, _) if n == name)) + { + *v = val; + } else { + self.uniforms.push(Uniform::Float(name.into(), val)); + } + } + + pub fn set_uniform_v2(&mut self, name: &ffi::CStr, val: Vec2) { + if let Some(Uniform::Vec2(_, v)) = self.uniforms + .iter_mut() + .find(|u| matches!(u, Uniform::Vec2(n, _) if n == name)) + { + *v = val; + } else { + self.uniforms.push(Uniform::Vec2(name.into(), val)); + } + } + + pub fn set_uniform_v3(&mut self, name: &ffi::CStr, val: Vec3) { + if let Some(Uniform::Vec3(_, v)) = self.uniforms + .iter_mut() + .find(|u| matches!(u, Uniform::Vec3(n, _) if n == name)) + { + *v = val; + } else { + self.uniforms.push(Uniform::Vec3(name.into(), val)); + } + } + + pub fn set_uniform_v4(&mut self, name: &ffi::CStr, val: Vec4) { + if let Some(Uniform::Vec4(_, v)) = self.uniforms + .iter_mut() + .find(|u| matches!(u, Uniform::Vec4(n, _) if n == name)) + { + *v = val; + } else { + self.uniforms.push(Uniform::Vec4(name.into(), val)); + } + } + + pub fn set_uniform_m4(&mut self, name: &ffi::CStr, val: Mat4) { + if let Some(Uniform::Mat4(_, v)) = self.uniforms + .iter_mut() + .find(|u| matches!(u, Uniform::Mat4(n, _) if n == name)) + { + *v = val; + } else { + self.uniforms.push(Uniform::Mat4(name.into(), val)); + } + } + + pub fn uniforms(&self) -> &[Uniform] { + &self.uniforms + } + + pub fn textures(&self) -> &[TextureId] { + &self.textures } } @@ -71,7 +183,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { match key.trim() { "Ka" => { match parse_args::<3, f32>(values, delim) { - Ok(v) => material.ambient = Vec3(v), + Ok([x, y, z]) => material.set_ambient(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -81,7 +193,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { }, "Kd" => { match parse_args::<3, f32>(values, delim) { - Ok(v) => material.diffuse = Vec3(v), + Ok([x, y, z]) => material.set_diffuse(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -91,7 +203,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { }, "Ks" => { match parse_args::<3, f32>(values, delim) { - Ok(v) => material.specular = Vec3(v), + Ok([x, y, z]) => material.set_specular(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -101,7 +213,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { }, "Ke" => { match parse_args::<3, f32>(values, delim) { - Ok(v) => material.emissive = Vec3(v), + Ok([x, y, z]) => material.set_emissive(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -111,7 +223,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { } "Ns" => { match parse_args::<1, f32>(values, delim) { - Ok(v) => material.shininess = v[0], + Ok(v) => material.set_shininess(v[0]), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -121,7 +233,7 @@ pub fn load_materials(path: &str) -> Result, AssetError> { }, "d" => { match parse_args::<1, f32>(values, delim) { - Ok(v) => material.opacity = v[0], + Ok(v) => material.set_opacity(v[0]), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, diff --git a/src/assets/obj.rs b/src/assets/obj.rs index 6dfe99b..096b481 100644 --- a/src/assets/obj.rs +++ b/src/assets/obj.rs @@ -22,7 +22,7 @@ pub fn load_obj(path: &str) -> Result { match prefix.trim() { "v" => { match parse_args::<3, f32>(values, delim) { - Ok(vec) => obj_data.vertices.push(Vec3(vec)), + Ok([x, y, z]) => obj_data.vertices.push(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -32,7 +32,7 @@ pub fn load_obj(path: &str) -> Result { }, "vt" => { match parse_args::<2, f32>(values, delim) { - Ok(vec) => obj_data.tex_coords.push(Vec2(vec)), + Ok([x, y]) => obj_data.tex_coords.push(Vec2::new(x, y)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, @@ -42,7 +42,7 @@ pub fn load_obj(path: &str) -> Result { }, "vn" => { match parse_args::<3, f32>(values, delim) { - Ok(vec) => obj_data.normals.push(Vec3(vec)), + Ok([x, y, z]) => obj_data.normals.push(Vec3::new(x, y, z)), Err(e) => return Err(AssetError::Parse { file: path.to_string(), line: i, diff --git a/src/backend/backend.rs b/src/backend/backend.rs index 9fcf311..a308132 100644 --- a/src/backend/backend.rs +++ b/src/backend/backend.rs @@ -1,27 +1,43 @@ -use crate::shaders; +use crate::{geometry, handles::{self, MeshHandle}, math}; +use std::ffi::CString; + + +pub struct DrawCommand<'a> { + pub mesh: handles::MeshHandle, + pub shader: handles::ShaderHandle, + pub uniforms: &'a [Uniform], + pub textures: &'a [(u32, u32)], +} + +#[derive(Clone, Debug)] +pub enum Uniform { + Float(CString, f32), + Vec2(CString, math::Vec2), + Vec3(CString, math::Vec3), + Vec4(CString, math::Vec4), + Mat4(CString, math::Mat4), + Int(CString, i32) +} pub trait RenderBackend { - type ShaderSource; - type DrawCommand; - fn new() -> Self where Self: Sized; + type ShaderSource: Default; + fn new() -> Self; fn resume(&mut self, event_loop: &winit::event_loop::ActiveEventLoop); fn resize(&mut self, width: u32, height: u32); - fn draw(&self, cmd: &Self::DrawCommand); fn suspend(&mut self); - // // Frame - // fn begin_frame(&mut self) -> FrameContext; - // fn end_frame(&mut self); - // - // // Resources - fn create_shader(&mut self, desc: Self::ShaderSource) -> Result; + fn begin_frame(&mut self); + fn draw(&self, cmd: &DrawCommand); + fn end_frame(&self); + + fn create_shader(&mut self, desc: &Self::ShaderSource) -> Result; + fn create_mesh(&mut self, mesh: &geometry::Mesh) -> Result; // fn create_buffer(&mut self, data: &[u8], usage: BufferUsage) -> BufferHandle; // fn create_texture(&mut self, desc: TextureDescriptor) -> TextureHandle; - // - // // Material = shader + uniforms + textures bundled - // fn create_material(&mut self, shader: ShaderHandle) -> MaterialHandle; - // fn set_material_param(&mut self, material: MaterialHandle, name: &str, value: UniformValue); + + fn set_target_fps(&mut self, fps: u32); + } // pub struct FrameContext<'a> { diff --git a/src/backend/gpu/mesh.rs b/src/backend/gpu/mesh.rs index 9513ab0..938f00d 100644 --- a/src/backend/gpu/mesh.rs +++ b/src/backend/gpu/mesh.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct GpuMesh { pub vao: gl::types::GLuint, pub vbo: gl::types::GLuint, diff --git a/src/backend/opengl/gl.rs b/src/backend/opengl/gl.rs index 8032198..d3966ca 100644 --- a/src/backend/opengl/gl.rs +++ b/src/backend/opengl/gl.rs @@ -2,8 +2,6 @@ #![allow(unsafe_op_in_unsafe_fn)] include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); -use std::ffi::CString; - pub use Gles2 as Gl; pub use crate::math; @@ -14,25 +12,6 @@ use glutin::prelude::GlDisplay; use raw_window_handle::HasWindowHandle; use winit::window::Window; -use crate::handles::MeshHandle; -use crate::shaders::ShaderHandle; - -pub enum Uniform { - Float(CString, f32), - Vec3(CString, math::Vec3), - Vec4(CString, math::Vec4), - Mat4(CString, math::Mat4), - Int(CString, i32), - None, -} - -pub struct GlDrawCommand { - pub mesh: MeshHandle, - pub shader: ShaderHandle, - pub uniforms: [Uniform; 8], - pub textures: Option>, -} - pub fn gl_config_picker(configs: Box + '_>) -> Config { configs .reduce(|accum, config| { diff --git a/src/backend/opengl/gl_backend.rs b/src/backend/opengl/gl_backend.rs index 97b3d36..a94e58a 100644 --- a/src/backend/opengl/gl_backend.rs +++ b/src/backend/opengl/gl_backend.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; use std::num::NonZero; +use std::thread; +use std::time::{Duration, Instant}; use glutin::config::GetGlConfig; use glutin::display::GetGlDisplay; use glutin::prelude::{GlDisplay, NotCurrentGlContext, PossiblyCurrentGlContext}; @@ -8,15 +10,17 @@ use glutin_winit::GlWindow; use winit::window::Window; use crate::backend::gpu; -use crate::{backend, handles, shaders}; +use crate::{backend, geometry, handles, shaders}; use crate::app::app::window_attributes; use crate::backend::opengl::{self, GlRenderer}; +#[derive(Debug)] enum GlDisplayCreationState { Builder(glutin_winit::DisplayBuilder), Init } +#[derive(Debug)] struct AppState { gl_surface: glutin::surface::Surface, window: Window, @@ -29,16 +33,23 @@ pub struct GlBackend { template: glutin::config::ConfigTemplateBuilder, state: Option, meshes: HashMap, - shaders: HashMap, + shaders: HashMap, + + // tmp + next_mesh_id: u32, + next_shader_id: u32, + + frame_time: Duration, + start: Instant, } impl backend::RenderBackend for GlBackend { - type DrawCommand = opengl::GlDrawCommand; type ShaderSource = opengl::GlShaderSource; fn new() -> Self where Self: Sized { let template = glutin::config::ConfigTemplateBuilder::new() .with_alpha_size(8) + .with_multisampling(4) .with_transparency(cfg!(target_os = "macos")); let display_builder = glutin_winit::DisplayBuilder::new().with_window_attributes(Some(window_attributes())); @@ -50,7 +61,11 @@ impl backend::RenderBackend for GlBackend { gl: None, meshes: HashMap::new(), shaders: HashMap::new(), - template + next_mesh_id: 0, + next_shader_id: 0, + frame_time: Duration::from_secs_f32(1.0 / 60.0), + start: Instant::now(), + template, } } @@ -112,6 +127,8 @@ impl backend::RenderBackend for GlBackend { eprintln!("Error setting vsync: {:?}", res) } + window.request_redraw(); + assert!(self.state.replace(AppState { gl_surface, window }).is_none()); } @@ -141,23 +158,70 @@ impl backend::RenderBackend for GlBackend { } } - fn create_shader(&mut self, desc: Self::ShaderSource) -> Result { + fn create_shader(&mut self, desc: &Self::ShaderSource) -> Result { if let Some(gl) = &self.gl { let shader = gl.create_shader(&desc); - let shader_handle = self.shaders.len() as u32; + let shader_handle = handles::ShaderHandle(self.next_shader_id); self.shaders.insert(shader_handle, shader); + self.next_shader_id += 1; Ok(shader_handle) } else { todo!("implement error type for GlBackend") } } - fn draw(&self, cmd: &Self::DrawCommand) { + fn create_mesh(&mut self, mesh: &geometry::Mesh) -> Result { + if let Some(gl) = &self.gl { + let gpu_mesh = gl.upload_mesh(mesh); + let mesh_handle = handles::MeshHandle(self.next_mesh_id); + self.meshes.insert(mesh_handle, gpu_mesh); + self.next_mesh_id += 1; + Ok(mesh_handle) + } else { + todo!("implement error type for GlBackend") + } + } + + fn begin_frame(&mut self) { + if let Some(gl) = &self.gl { + gl.clear(0.1, 0.1, 0.1, 1.0); + self.start = Instant::now(); + } + } + + fn draw(&self, cmd: &backend::DrawCommand) { let mesh = self.meshes.get(&cmd.mesh); let shader = self.shaders.get(&cmd.shader); - if let (Some(mesh), Some(shader)) = (mesh, shader) { + if let (Some(mesh), Some(shader), Some(gl)) = (mesh, shader, &self.gl) { + gl.use_shader(*shader); + for u in cmd.uniforms { + gl.apply_uniform(*shader, &u); + } + + gl.draw_mesh(mesh); + + } else { + println!("{:#?}", mesh); + println!("{:#?}", shader); + println!("{:#?}", self.state); + println!("{:#?}", self.gl.is_some()); } } + + fn end_frame(&self) { + if let (Some(state), Some(gl_context)) = (&self.state, &self.gl_context) { + state.window.request_redraw(); + state.gl_surface.swap_buffers(gl_context).unwrap(); + let elapsed = self.start.elapsed(); + if elapsed < self.frame_time { + thread::sleep(self.frame_time - elapsed); + } + } + } + + fn set_target_fps(&mut self, fps: u32) { + self.frame_time = Duration::from_secs_f32(1.0 / (fps as f32)); + } } diff --git a/src/backend/opengl/gl_renderer.rs b/src/backend/opengl/gl_renderer.rs index 6db62d2..1d41f7d 100644 --- a/src/backend/opengl/gl_renderer.rs +++ b/src/backend/opengl/gl_renderer.rs @@ -1,12 +1,14 @@ +use gl::types::GLfloat; use glutin::prelude::GlDisplay; -use crate::backend::opengl::{self, Uniform}; +use crate::backend::{opengl, Uniform}; use crate::math::{ Mat4, Vec3 }; use crate::{geometry, backend::gpu::GpuMesh}; use crate::shaders::Shader; use std::ops::Deref; -use std::ffi::{ CStr, CString }; +use std::ffi::{ CStr, CString, c_void }; +use super::gl::Gl; -/// Render state containing camera and lighting information +#[derive(Debug)] pub struct RenderState { pub projection: Mat4, pub view: Mat4, @@ -18,18 +20,18 @@ pub struct RenderState { impl RenderState { pub fn new(aspect_ratio: f32) -> Self { - let camera_pos = Vec3([0.0, 2.0, 5.0]); + let camera_pos = Vec3::new(0.0, 2.0, 5.0); Self { projection: Mat4::perspective(45.0_f32.to_radians(), aspect_ratio, 0.1, 100.0), view: Mat4::look_at( camera_pos, - Vec3 ([0.0, 0.0, 0.0]), - Vec3 ([0.0, 1.0, 0.0]), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 1.0, 0.0), ), model: Mat4::identity(), camera_pos, - light_pos: Vec3([3.0, 5.0, 3.0]), - light_color: Vec3([1.0, 1.0, 1.0]), + light_pos: Vec3::new(3.0, 5.0, 3.0), + light_color: Vec3::new(1.0, 1.0, 1.0), } } } @@ -42,7 +44,7 @@ fn get_gl_string(gl: &opengl::Gl, variant: gl::types::GLenum) -> Option<&'static } pub struct GlRenderer { - gl: std::rc::Rc, + gl: Gl, } impl GlRenderer { @@ -64,14 +66,15 @@ impl GlRenderer { println!("Shaders version on {}", shaders_version.to_string_lossy()); } - // Enable depth testing for 3D rendering unsafe { gl.Enable(gl::DEPTH_TEST); gl.DepthFunc(gl::LESS); + + gl.Enable(gl::MULTISAMPLE); } Self { - gl: std::rc::Rc::new(gl), + gl } } @@ -83,43 +86,30 @@ impl GlRenderer { gl.GenVertexArrays(1, &mut vao); gl.BindVertexArray(vao); - // Upload vertex data (position, normal, texcoord) let mut vbo = std::mem::zeroed(); gl.GenBuffers(1, &mut vbo); gl.BindBuffer(gl::ARRAY_BUFFER, vbo); - // Vertex layout: [pos.x, pos.y, pos.z, norm.x, norm.y, norm.z, tex.x, tex.y] - let vertex_data: Vec = mesh.vertices - .iter() - .flat_map(|v| [ - v.position.x(), v.position.y(), v.position.z(), - v.normal.x(), v.normal.y(), v.normal.z(), - v.tex_coord.x(), v.tex_coord.y(), - ]) - .collect(); + for attribute in &mesh.format.attributes { + let location = attribute.semantic.location(); + gl.VertexAttribPointer( + attribute.semantic.location(), + attribute.comp_count as i32, + attribute.data_type.to_gpu(), + attribute.normalized as u8, + mesh.format.stride as i32, + attribute.offset as *const c_void, + ); + gl.EnableVertexAttribArray(location); + } gl.BufferData( gl::ARRAY_BUFFER, - (vertex_data.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, - vertex_data.as_ptr() as *const _, + mesh.vertex_data.len() as gl::types::GLsizeiptr, + mesh.vertex_data.as_ptr() as *const _, gl::STATIC_DRAW, ); - let stride = 8 * std::mem::size_of::() as gl::types::GLsizei; - - // Position attribute (location = 0) - gl.VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, std::ptr::null()); - gl.EnableVertexAttribArray(0); - - // Normal attribute (location = 1) - gl.VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, stride, (3 * std::mem::size_of::()) as *const _); - gl.EnableVertexAttribArray(1); - - // Texcoord attribute (location = 2) - gl.VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, stride, (6 * std::mem::size_of::()) as *const _); - gl.EnableVertexAttribArray(2); - - // Upload index data if available let (ebo, index_count) = if !mesh.indices.is_empty() { let mut ebo = std::mem::zeroed(); gl.GenBuffers(1, &mut ebo); @@ -141,7 +131,7 @@ impl GlRenderer { vao, vbo, ebo, - vertex_count: mesh.vertices.len() as i32, + vertex_count: mesh.vertex_count as i32, index_count, } } @@ -183,34 +173,48 @@ impl GlRenderer { } } - pub fn apply_uniform(&self, shader: Shader, uniform: Uniform) { + pub fn apply_uniform(&self, shader: Shader, uniform: &Uniform) { unsafe { match uniform { Uniform::Float(name, value) => { - let location = self.GetUniformLocation(shader.0, name.as_ptr()); - self.Uniform1f(location, value); + let location = self.GetUniformLocation(*shader, name.as_ptr()); + self.Uniform1f(location, *value); } Uniform::Int(name, value) => { - let location = self.GetUniformLocation(shader.0, name.as_ptr()); - self.Uniform1i(location, value); + let location = self.GetUniformLocation(*shader, name.as_ptr()); + self.Uniform1i(location, *value); + } + Uniform::Vec2(name, value) => { + let location = self.GetUniformLocation(*shader, name.as_ptr()); + self.Uniform2f(location, value[0], value[1]); } Uniform::Vec3(name, value) => { - let location = self.GetUniformLocation(shader.0, name.as_ptr()); + let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform3f(location, value[0], value[1], value[2]); } Uniform::Vec4(name, value) => { - let location = self.GetUniformLocation(shader.0, name.as_ptr()); + let location = self.GetUniformLocation(*shader, name.as_ptr()); self.Uniform4f(location, value[0], value[1], value[2], value[3]); } Uniform::Mat4(name, value) => { - let location = self.GetUniformLocation(shader.0, name.as_ptr()); + let location = self.GetUniformLocation(*shader, name.as_ptr()); self.UniformMatrix4fv(location, 1, gl::FALSE, value.as_ptr()); } - Uniform::None => {} } } } + pub fn clear(&self, red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) { + unsafe { + self.ClearColor(red, green, blue, alpha); + self.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); + } + } + + pub fn use_shader(&self, shader: Shader) { + unsafe { self.UseProgram(*shader) }; + } + pub fn resize(&self, width: i32, height: i32) { unsafe { self.gl.Viewport(0, 0, width, height); diff --git a/src/backend/opengl/gl_shader.rs b/src/backend/opengl/gl_shader.rs index 017ca68..3178032 100644 --- a/src/backend/opengl/gl_shader.rs +++ b/src/backend/opengl/gl_shader.rs @@ -1,3 +1,4 @@ +#[derive(Default)] pub struct GlShaderSource { pub vertex_src: String, pub fragment_src: String, diff --git a/src/geometry/mesh.rs b/src/geometry/mesh.rs index e2c9909..49eb6e6 100644 --- a/src/geometry/mesh.rs +++ b/src/geometry/mesh.rs @@ -1,18 +1,67 @@ use std::collections::HashMap; - -use crate::assets::types::{VertexIndex, ObjData, ObjMesh}; - +use crate::{assets::types::{ObjData, ObjMesh, VertexIndex}, geometry::VertexFormat, math::{Vec2, Vec4}}; use super::Vertex; #[derive(Debug, Default)] pub struct Mesh { pub name: String, + pub format: VertexFormat, + pub vertex_data: Vec, + pub vertex_count: usize, pub vertices: Vec, pub indices: Vec, pub material_name: Option, } +#[derive(Default)] +pub struct MeshBuilder { + mesh: Mesh, + current_vertex: Vec, +} + +impl MeshBuilder { + pub fn build(self) -> Mesh { + self.mesh + } + + pub fn begin_vertex(mut self) -> Self { + self.current_vertex.clear(); + self + } + + pub fn end_vertex(mut self) -> Self { + if self.current_vertex.len() > 0 { + self.mesh.vertex_data.extend_from_slice(&self.current_vertex); + self.mesh.vertex_count += 1; + } + self + } + + pub fn position2(mut self, pos: Vec2) -> Self { + self.current_vertex.extend_from_slice(&pos.to_bytes()); + self + } + + pub fn tex_coord(mut self, pos: Vec2) -> Self { + self.current_vertex.extend_from_slice(&pos.to_bytes()); + self + } + + pub fn color(mut self, color: Vec4) -> Self { + self.current_vertex.extend_from_slice(&color.to_bytes()); + self + } +} + impl Mesh { + pub fn builder(name: impl AsRef, format: VertexFormat) -> MeshBuilder { + let mesh: Self = Self { name: name.as_ref().to_string(), format, ..Default::default() }; + MeshBuilder { + mesh, + current_vertex: vec![], + } + } + pub fn new(obj: &ObjData, obj_mash: &ObjMesh) -> Self { let mut vertices: Vec = Vec::new(); let mut indices: Vec = Vec::new(); @@ -36,6 +85,9 @@ impl Mesh { } Mesh { vertices, + format: Default::default(), + vertex_data: vec![], + vertex_count: 0, indices, name: obj_mash.name.clone(), material_name: obj_mash.material_name.clone(), diff --git a/src/geometry/vertex.rs b/src/geometry/vertex.rs index 91f9b53..9feba0a 100644 --- a/src/geometry/vertex.rs +++ b/src/geometry/vertex.rs @@ -1,5 +1,97 @@ +use std::fmt::Debug; + use crate::math::{ Vec2, Vec3 }; +#[derive(Debug)] +pub enum VertexSemantic { + Position, + Normal, + TexCoord0, + TexCoord1, + Color, +} + +impl VertexSemantic { + pub fn location(&self) -> u32 { + match self { + VertexSemantic::Position => 0, + VertexSemantic::Normal => 1, + VertexSemantic::Color => 2, + VertexSemantic::TexCoord0 => 3, + VertexSemantic::TexCoord1 => 4, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AttributeType { + Float, + Int, + UnsignedInt, + Byte, + UnsignedByte, +} + +impl AttributeType { + pub fn to_gpu(&self) -> gl::types::GLenum { + match self { + AttributeType::Float => gl::FLOAT, + AttributeType::Int => gl::INT, + AttributeType::UnsignedInt => gl::UNSIGNED_INT, + AttributeType::Byte => gl::BYTE, + AttributeType::UnsignedByte => gl::UNSIGNED_BYTE, + } + } +} + +#[derive(Debug)] +pub struct VertexAttribute { + pub semantic: VertexSemantic, + pub comp_count: usize, + pub data_type: AttributeType, + pub normalized: bool, + pub offset: usize, +} + +#[derive(Debug)] +pub struct VertexFormat { + pub attributes: Vec, + pub stride: usize, +} + +impl VertexFormat { + pub fn ui_2d() -> Self { + Self { + attributes: vec![ + VertexAttribute { + semantic: VertexSemantic::Position, + comp_count: 2, + data_type: AttributeType::Float, + normalized: false, + offset: 0, + }, + VertexAttribute { + semantic: VertexSemantic::Color, + comp_count: 4, + data_type: AttributeType::Float, + normalized: false, + offset: 2 * std::mem::size_of::() + } + ], + stride: (2 + 4) * std::mem::size_of::() + } + } +} + +impl Default for VertexFormat { + fn default() -> Self { + Self { + attributes: vec![], + stride: 0 + } + } +} + #[derive(Debug)] pub struct Vertex { pub position: Vec3, diff --git a/src/handles.rs b/src/handles.rs index 74c0187..89b9b21 100644 --- a/src/handles.rs +++ b/src/handles.rs @@ -1,5 +1,17 @@ -#[derive(Copy, Clone, Hash, PartialEq, Eq)] +use std::ops::Deref; + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)] pub struct MeshHandle(pub u32); -#[derive(Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)] pub struct MaterialHandle(pub u32); + +#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ShaderHandle(pub u32); + +impl Deref for ShaderHandle { + type Target = u32; + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/src/lib.rs b/src/lib.rs index 258441f..fdf4646 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ pub mod models { - pub mod model; pub use model::*; } @@ -53,6 +52,8 @@ pub mod shaders { pub mod constants; pub use constants::*; + + pub mod inputs; } pub mod backend { diff --git a/src/main.rs b/src/main.rs index 623b3f5..355139f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,46 @@ use proj::{ - app::app::App, assets, backend::opengl, shaders::{FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_SOURCE} + app::app::App, assets, backend::opengl, geometry::{AttributeType, Mesh, MeshBuilder, VertexFormat, VertexSemantic}, math::{Mat4, Vec2, Vec4}, models::{self, Model}, shaders::{FRAGMENT_SHADER_2D, FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_2D, VERTEX_SHADER_SOURCE, inputs::ShaderInputs} }; use winit::event_loop::EventLoop; const HOUSE_OBJ_PATH: &str = "assets/models/small_house/model.obj"; const HOUSE_MAT_PATH: &str = "assets/models/small_house/materials.mtl"; +const BUGATTI_OBJ_PATH: &str = "assets/models/bugatti/bugatti.obj"; +const BUGATTI_MTL_PATH: &str = "assets/models/bugatti/bugatti.mtl"; + fn main() { dotenvy::dotenv().ok(); let event_loop = EventLoop::new() .expect("Should be able to have Event Loop"); - let _shader_src = opengl::GlShaderSource { - vertex_src: VERTEX_SHADER_SOURCE.to_string(), - fragment_src: FRAGMENT_SHADER_SOURCE.to_string() + let shader_src = opengl::GlShaderSource { + vertex_src: VERTEX_SHADER_2D.to_string(), + fragment_src: FRAGMENT_SHADER_2D.to_string(), }; - // Load OBJ and materials (CPU-side, before GL context) - let obj_data = match assets::obj::load_obj(HOUSE_OBJ_PATH) { - Ok(o) => o, - Err(e) => return eprintln!("Error loading OBJ: {}", e), - }; - - let materials = match assets::mtl::load_materials(HOUSE_MAT_PATH) { - Ok(m) => m, - Err(e) => return eprintln!("Error loading materials: {}", e), - }; - - println!("Loaded {} meshes, {} materials", obj_data.meshes.len(), materials.len()); let mut app = App::::new(); + let mesh = Mesh::builder("triangle", VertexFormat::ui_2d()) + .begin_vertex() + .position2(Vec2::new(0.0, 0.5)) + .color(Vec4::new(1.0, 0.0, 0.0, 1.0)) + .end_vertex() + .begin_vertex() + .position2(Vec2::new(-0.5, -0.5)) + .color(Vec4::new(1.0, 1.0, 0.0, 1.0)) + .end_vertex() + .begin_vertex() + .position2(Vec2::new(0.5, -0.5)) + .color(Vec4::new(0.0, 0.0, 1.0, 1.0)) + .end_vertex() + .build(); + + app.meshes.push(mesh); + app.add_shader(shader_src); + let event_res = event_loop.run_app(&mut app); println!("{:#?}", event_res); diff --git a/src/math/matrix/mat4.rs b/src/math/matrix/mat4.rs index f80fb88..56f0f80 100644 --- a/src/math/matrix/mat4.rs +++ b/src/math/matrix/mat4.rs @@ -98,9 +98,9 @@ impl Mat4 { 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.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, ], } diff --git a/src/math/vector/vec2.rs b/src/math/vector/vec2.rs index 4f4e277..99426eb 100644 --- a/src/math/vector/vec2.rs +++ b/src/math/vector/vec2.rs @@ -2,39 +2,26 @@ use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; #[derive(Clone, Copy, Debug, Default, PartialEq)] #[repr(C)] -pub struct Vec2(pub [f32; 2]); +pub struct Vec2 { + pub x: f32, + pub y: f32, +} impl Vec2 { pub const fn new(x: f32, y: f32) -> Self { - Self([x, y]) + Self { x, y } } pub const fn zero() -> Self { - Self([0.0, 0.0]) + Self { x: 0.0, y: 0.0 } } pub const fn one() -> Self { - Self([1.0, 1.0]) - } - - pub fn x(&self) -> f32 { - self.0[0] - } - - pub fn y(&self) -> f32 { - self.0[1] - } - - pub fn x_mut(&mut self) -> &mut f32 { - &mut self.0[0] - } - - pub fn y_mut(&mut self) -> &mut f32 { - &mut self.0[1] + Self { x: 1.0, y: 1.0 } } pub fn length_squared(&self) -> f32 { - self.0[0] * self.0[0] + self.0[1] * self.0[1] + self.x * self.x + self.y * self.y } pub fn length(&self) -> f32 { @@ -46,28 +33,40 @@ impl Vec2 { if len == 0.0 { return *self; } - Self([self.0[0] / len, self.0[1] / len]) + Self { + x: self.x / len, + y: self.y / len, + } } pub fn dot(&self, other: &Self) -> f32 { - self.0[0] * other.0[0] + self.0[1] * other.0[1] + self.x * other.x + self.y * other.y } pub fn as_ptr(&self) -> *const f32 { - self.0.as_ptr() + &self.x as *const f32 } pub fn as_mut_ptr(&mut self) -> *mut f32 { - self.0.as_mut_ptr() + &mut self.x as *mut f32 + } + + pub fn to_bytes(&self) -> [u8; 8] { + let mut bytes = [0u8; 8]; + bytes[0..4].copy_from_slice(&self.x.to_ne_bytes()); + bytes[4..8].copy_from_slice(&self.y.to_ne_bytes()); + bytes } } -// Add operations impl Add for Vec2 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]]) + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + } } } @@ -75,16 +74,21 @@ impl Add for Vec2 { type Output = Self; fn add(self, rhs: f32) -> Self::Output { - Self([self.0[0] + rhs, self.0[1] + rhs]) + Self { + x: self.x + rhs, + y: self.y + rhs, + } } } -// Subtraction operations impl Sub for Vec2 { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]]) + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + } } } @@ -92,16 +96,21 @@ impl Sub for Vec2 { type Output = Self; fn sub(self, rhs: f32) -> Self::Output { - Self([self.0[0] - rhs, self.0[1] - rhs]) + Self { + x: self.x - rhs, + y: self.y - rhs, + } } } -// Multiplication operations impl Mul for Vec2 { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - Self([self.0[0] * rhs.0[0], self.0[1] * rhs.0[1]]) + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + } } } @@ -109,16 +118,21 @@ impl Mul for Vec2 { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { - Self([self.0[0] * rhs, self.0[1] * rhs]) + Self { + x: self.x * rhs, + y: self.y * rhs, + } } } -// Division operations impl Div for Vec2 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { - Self([self.0[0] / rhs.0[0], self.0[1] / rhs.0[1]]) + Self { + x: self.x / rhs.x, + y: self.y / rhs.y, + } } } @@ -126,30 +140,42 @@ impl Div for Vec2 { type Output = Self; fn div(self, rhs: f32) -> Self::Output { - Self([self.0[0] / rhs, self.0[1] / rhs]) + Self { + x: self.x / rhs, + y: self.y / rhs, + } } } -// Negation impl Neg for Vec2 { type Output = Self; fn neg(self) -> Self::Output { - Self([-self.0[0], -self.0[1]]) + Self { + x: -self.x, + y: -self.y, + } } } -// Index operations impl Index for Vec2 { type Output = f32; fn index(&self, index: usize) -> &Self::Output { - &self.0[index] + match index { + 0 => &self.x, + 1 => &self.y, + _ => panic!("Vec2 index out of bounds: {}", index), + } } } impl IndexMut for Vec2 { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] + match index { + 0 => &mut self.x, + 1 => &mut self.y, + _ => panic!("Vec2 index out of bounds: {}", index), + } } } diff --git a/src/math/vector/vec3.rs b/src/math/vector/vec3.rs index 55153a4..6bf1dc3 100644 --- a/src/math/vector/vec3.rs +++ b/src/math/vector/vec3.rs @@ -2,47 +2,27 @@ use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; #[derive(Clone, Copy, Debug, Default, PartialEq)] #[repr(C)] -pub struct Vec3(pub [f32; 3]); +pub struct Vec3 { + pub x: f32, + pub y: f32, + pub z: f32, +} impl Vec3 { pub const fn new(x: f32, y: f32, z: f32) -> Self { - Self([x, y, z]) + Self { x, y, z } } pub const fn zero() -> Self { - Self([0.0, 0.0, 0.0]) + Self { x: 0.0, y: 0.0, z: 0.0 } } pub const fn one() -> Self { - Self([1.0, 1.0, 1.0]) - } - - pub fn x(&self) -> f32 { - self.0[0] - } - - pub fn y(&self) -> f32 { - self.0[1] - } - - pub fn z(&self) -> f32 { - self.0[2] - } - - pub fn x_mut(&mut self) -> &mut f32 { - &mut self.0[0] - } - - pub fn y_mut(&mut self) -> &mut f32 { - &mut self.0[1] - } - - pub fn z_mut(&mut self) -> &mut f32 { - &mut self.0[2] + Self { x: 1.0, y: 1.0, z: 1.0 } } pub fn length_squared(&self) -> f32 { - self.0[0] * self.0[0] + self.0[1] * self.0[1] + self.0[2] * self.0[2] + self.x * self.x + self.y * self.y + self.z * self.z } pub fn length(&self) -> f32 { @@ -54,36 +34,51 @@ impl Vec3 { if len == 0.0 { return *self; } - Self([self.0[0] / len, self.0[1] / len, self.0[2] / len]) + Self { + x: self.x / len, + y: self.y / len, + z: self.z / len, + } } pub fn dot(&self, other: &Self) -> f32 { - self.0[0] * other.0[0] + self.0[1] * other.0[1] + self.0[2] * other.0[2] + self.x * other.x + self.y * other.y + self.z * other.z } pub fn cross(&self, other: &Self) -> Self { - Self([ - self.0[1] * other.0[2] - self.0[2] * other.0[1], - self.0[2] * other.0[0] - self.0[0] * other.0[2], - self.0[0] * other.0[1] - self.0[1] * other.0[0], - ]) + 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, + } } pub fn as_ptr(&self) -> *const f32 { - self.0.as_ptr() + &self.x as *const f32 } pub fn as_mut_ptr(&mut self) -> *mut f32 { - self.0.as_mut_ptr() + &mut self.x as *mut f32 + } + + pub fn to_bytes(&self) -> [u8; 12] { + let mut bytes = [0u8; 12]; + bytes[0..4].copy_from_slice(&self.x.to_ne_bytes()); + bytes[4..8].copy_from_slice(&self.y.to_ne_bytes()); + bytes[8..12].copy_from_slice(&self.z.to_ne_bytes()); + bytes } } -// Add operations impl Add for Vec3 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1], self.0[2] + rhs.0[2]]) + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, + } } } @@ -91,16 +86,23 @@ impl Add for Vec3 { type Output = Self; fn add(self, rhs: f32) -> Self::Output { - Self([self.0[0] + rhs, self.0[1] + rhs, self.0[2] + rhs]) + Self { + x: self.x + rhs, + y: self.y + rhs, + z: self.z + rhs, + } } } -// Subtraction operations impl Sub for Vec3 { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1], self.0[2] - rhs.0[2]]) + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } } } @@ -108,16 +110,23 @@ impl Sub for Vec3 { type Output = Self; fn sub(self, rhs: f32) -> Self::Output { - Self([self.0[0] - rhs, self.0[1] - rhs, self.0[2] - rhs]) + Self { + x: self.x - rhs, + y: self.y - rhs, + z: self.z - rhs, + } } } -// Multiplication operations impl Mul for Vec3 { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - Self([self.0[0] * rhs.0[0], self.0[1] * rhs.0[1], self.0[2] * rhs.0[2]]) + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + z: self.z * rhs.z, + } } } @@ -125,16 +134,23 @@ impl Mul for Vec3 { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { - Self([self.0[0] * rhs, self.0[1] * rhs, self.0[2] * rhs]) + Self { + x: self.x * rhs, + y: self.y * rhs, + z: self.z * rhs, + } } } -// Division operations impl Div for Vec3 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { - Self([self.0[0] / rhs.0[0], self.0[1] / rhs.0[1], self.0[2] / rhs.0[2]]) + Self { + x: self.x / rhs.x, + y: self.y / rhs.y, + z: self.z / rhs.z, + } } } @@ -142,30 +158,46 @@ impl Div for Vec3 { type Output = Self; fn div(self, rhs: f32) -> Self::Output { - Self([self.0[0] / rhs, self.0[1] / rhs, self.0[2] / rhs]) + Self { + x: self.x / rhs, + y: self.y / rhs, + z: self.z / rhs, + } } } -// Negation impl Neg for Vec3 { type Output = Self; fn neg(self) -> Self::Output { - Self([-self.0[0], -self.0[1], -self.0[2]]) + Self { + x: -self.x, + y: -self.y, + z: -self.z, + } } } -// Index operations impl Index for Vec3 { type Output = f32; fn index(&self, index: usize) -> &Self::Output { - &self.0[index] + match index { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + _ => panic!("Vec3 index out of bounds: {}", index), + } } } impl IndexMut for Vec3 { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] + match index { + 0 => &mut self.x, + 1 => &mut self.y, + 2 => &mut self.z, + _ => panic!("Vec3 index out of bounds: {}", index), + } } } diff --git a/src/math/vector/vec4.rs b/src/math/vector/vec4.rs index 3dafca9..4fb9694 100644 --- a/src/math/vector/vec4.rs +++ b/src/math/vector/vec4.rs @@ -2,55 +2,28 @@ use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; #[derive(Clone, Copy, Debug, Default, PartialEq)] #[repr(C)] -pub struct Vec4(pub [f32; 4]); +pub struct Vec4 { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} impl Vec4 { pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { - Self([x, y, z, w]) + Self { x, y, z, w } } pub const fn zero() -> Self { - Self([0.0, 0.0, 0.0, 0.0]) + Self { x: 0.0, y: 0.0, z: 0.0, w: 0.0 } } pub const fn one() -> Self { - Self([1.0, 1.0, 1.0, 1.0]) - } - - pub fn x(&self) -> f32 { - self.0[0] - } - - pub fn y(&self) -> f32 { - self.0[1] - } - - pub fn z(&self) -> f32 { - self.0[2] - } - - pub fn w(&self) -> f32 { - self.0[3] - } - - pub fn x_mut(&mut self) -> &mut f32 { - &mut self.0[0] - } - - pub fn y_mut(&mut self) -> &mut f32 { - &mut self.0[1] - } - - pub fn z_mut(&mut self) -> &mut f32 { - &mut self.0[2] - } - - pub fn w_mut(&mut self) -> &mut f32 { - &mut self.0[3] + Self { x: 1.0, y: 1.0, z: 1.0, w: 1.0 } } pub fn length_squared(&self) -> f32 { - self.0[0] * self.0[0] + self.0[1] * self.0[1] + self.0[2] * self.0[2] + self.0[3] * self.0[3] + self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w } pub fn length(&self) -> f32 { @@ -62,28 +35,46 @@ impl Vec4 { if len == 0.0 { return *self; } - Self([self.0[0] / len, self.0[1] / len, self.0[2] / len, self.0[3] / len]) + Self { + x: self.x / len, + y: self.y / len, + z: self.z / len, + w: self.w / len, + } } pub fn dot(&self, other: &Self) -> f32 { - self.0[0] * other.0[0] + self.0[1] * other.0[1] + self.0[2] * other.0[2] + self.0[3] * other.0[3] + self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w } pub fn as_ptr(&self) -> *const f32 { - self.0.as_ptr() + &self.x as *const f32 } pub fn as_mut_ptr(&mut self) -> *mut f32 { - self.0.as_mut_ptr() + &mut self.x as *mut f32 + } + + pub fn to_bytes(&self) -> [u8; 16] { + let mut bytes = [0u8; 16]; + bytes[0..4].copy_from_slice(&self.x.to_ne_bytes()); + bytes[4..8].copy_from_slice(&self.y.to_ne_bytes()); + bytes[8..12].copy_from_slice(&self.z.to_ne_bytes()); + bytes[12..16].copy_from_slice(&self.w.to_ne_bytes()); + bytes } } -// Add operations impl Add for Vec4 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { - Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1], self.0[2] + rhs.0[2], self.0[3] + rhs.0[3]]) + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, + w: self.w + rhs.w, + } } } @@ -91,16 +82,25 @@ impl Add for Vec4 { type Output = Self; fn add(self, rhs: f32) -> Self::Output { - Self([self.0[0] + rhs, self.0[1] + rhs, self.0[2] + rhs, self.0[3] + rhs]) + Self { + x: self.x + rhs, + y: self.y + rhs, + z: self.z + rhs, + w: self.w + rhs, + } } } -// Subtraction operations impl Sub for Vec4 { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { - Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1], self.0[2] - rhs.0[2], self.0[3] - rhs.0[3]]) + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + w: self.w - rhs.w, + } } } @@ -108,16 +108,25 @@ impl Sub for Vec4 { type Output = Self; fn sub(self, rhs: f32) -> Self::Output { - Self([self.0[0] - rhs, self.0[1] - rhs, self.0[2] - rhs, self.0[3] - rhs]) + Self { + x: self.x - rhs, + y: self.y - rhs, + z: self.z - rhs, + w: self.w - rhs, + } } } -// Multiplication operations impl Mul for Vec4 { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { - Self([self.0[0] * rhs.0[0], self.0[1] * rhs.0[1], self.0[2] * rhs.0[2], self.0[3] * rhs.0[3]]) + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + z: self.z * rhs.z, + w: self.w * rhs.w, + } } } @@ -125,16 +134,25 @@ impl Mul for Vec4 { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { - Self([self.0[0] * rhs, self.0[1] * rhs, self.0[2] * rhs, self.0[3] * rhs]) + Self { + x: self.x * rhs, + y: self.y * rhs, + z: self.z * rhs, + w: self.w * rhs, + } } } -// Division operations impl Div for Vec4 { type Output = Self; fn div(self, rhs: Self) -> Self::Output { - Self([self.0[0] / rhs.0[0], self.0[1] / rhs.0[1], self.0[2] / rhs.0[2], self.0[3] / rhs.0[3]]) + Self { + x: self.x / rhs.x, + y: self.y / rhs.y, + z: self.z / rhs.z, + w: self.w / rhs.w, + } } } @@ -142,30 +160,50 @@ impl Div for Vec4 { type Output = Self; fn div(self, rhs: f32) -> Self::Output { - Self([self.0[0] / rhs, self.0[1] / rhs, self.0[2] / rhs, self.0[3] / rhs]) + Self { + x: self.x / rhs, + y: self.y / rhs, + z: self.z / rhs, + w: self.w / rhs, + } } } -// Negation impl Neg for Vec4 { type Output = Self; fn neg(self) -> Self::Output { - Self([-self.0[0], -self.0[1], -self.0[2], -self.0[3]]) + Self { + x: -self.x, + y: -self.y, + z: -self.z, + w: -self.w, + } } } -// Index operations impl Index for Vec4 { type Output = f32; fn index(&self, index: usize) -> &Self::Output { - &self.0[index] + match index { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + 3 => &self.w, + _ => panic!("Vec4 index out of bounds: {}", index), + } } } impl IndexMut for Vec4 { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] + match index { + 0 => &mut self.x, + 1 => &mut self.y, + 2 => &mut self.z, + 3 => &mut self.w, + _ => panic!("Vec4 index out of bounds: {}", index), + } } } diff --git a/src/models/model.rs b/src/models/model.rs index 755f4d8..a25eefa 100644 --- a/src/models/model.rs +++ b/src/models/model.rs @@ -2,11 +2,13 @@ use crate::geometry::Mesh; use crate::assets::mtl::Material; use crate::handles::{ MeshHandle, MaterialHandle }; use crate::assets::types::ObjData; +use crate::math::Mat4; pub struct ModelData { pub name: String, pub meshes: Vec, pub materials: Vec, + pub transform: Mat4, } impl From<&ObjData> for ModelData { @@ -19,11 +21,41 @@ impl From<&ObjData> for ModelData { name: String::new(), meshes, materials: Vec::new(), + transform: Mat4::identity(), } } } +#[derive(Debug)] pub struct Model { pub name: String, pub parts: Vec<(MeshHandle, MaterialHandle)>, + pub transform: Mat4, +} + +impl Default for Model { + fn default() -> Self { + Self { + name: String::new(), + parts: Vec::new(), + transform: Mat4::identity(), + } + } +} + +impl Model { + /// Set position (translation) + pub fn set_position(&mut self, x: f32, y: f32, z: f32) { + self.transform = Mat4::translation(x, y, z); + } + + /// Set scale + pub fn set_scale(&mut self, x: f32, y: f32, z: f32) { + self.transform = Mat4::scale(x, y, z); + } + + /// Set full transform (for combining translation * rotation * scale) + pub fn set_transform(&mut self, transform: Mat4) { + self.transform = transform; + } } diff --git a/src/shaders/constants.rs b/src/shaders/constants.rs index c2cbed1..70386f5 100644 --- a/src/shaders/constants.rs +++ b/src/shaders/constants.rs @@ -59,3 +59,29 @@ void main() { vec3 result = ambient + diffuse + specular; frag_color = vec4(result, 1.0); }"#; + +pub const VERTEX_SHADER_2D: &str = r#" +#version 330 core + +layout (location = 0) in vec2 a_position; +layout (location = 2) in vec4 a_color; + +out vec4 v_color; + +void main() { + gl_Position = vec4(a_position, 0.0, 1.0); + v_color = a_color; +} +"#; + +pub const FRAGMENT_SHADER_2D: &str = r#" +#version 330 core + +in vec4 v_color; + +out vec4 frag_color; + +void main() { + frag_color = v_color; +} +"#; diff --git a/src/shaders/inputs.rs b/src/shaders/inputs.rs new file mode 100644 index 0000000..dc50d18 --- /dev/null +++ b/src/shaders/inputs.rs @@ -0,0 +1,55 @@ +use crate::geometry; + +pub enum InputRequirement { + Required, + Optional +} + +pub struct ShaderInput { + pub semantic: geometry::VertexSemantic, + pub component_count: usize, + pub data_type: geometry::AttributeType, + pub requirement: InputRequirement +} + +pub struct ShaderInputs { + pub inputs: Vec, +} + +impl ShaderInputs { + pub fn new() -> Self { + Self { + inputs: vec![] + } + } + + pub fn required( + mut self, + semantic: geometry::VertexSemantic, + component_count: usize, + data_type: geometry::AttributeType + ) -> Self { + self.inputs.push(ShaderInput { + semantic, + data_type, + component_count, + requirement: InputRequirement::Required, + }); + self + } + + pub fn optional( + mut self, + semantic: geometry::VertexSemantic, + component_count: usize, + data_type: geometry::AttributeType + ) -> Self { + self.inputs.push(ShaderInput { + semantic, + data_type, + component_count, + requirement: InputRequirement::Optional, + }); + self + } +} diff --git a/src/shaders/types.rs b/src/shaders/types.rs index 16bdd83..dfb90d0 100644 --- a/src/shaders/types.rs +++ b/src/shaders/types.rs @@ -1,5 +1,19 @@ +use std::ops::Deref; + pub struct ShaderSource {} +#[derive(Clone, Copy, Debug)] pub struct Shader(pub u32); -pub type ShaderHandle = u32; +impl From for u32 { + fn from(value: Shader) -> Self { + value.0 + } +} + +impl Deref for Shader { + type Target = u32; + fn deref(&self) -> &Self::Target { + &self.0 + } +}