TODO: Finish draw function and scene rendering logic
This commit is contained in:
parent
1775cf90bc
commit
0f451c715c
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -49,7 +49,7 @@ dependencies = [
|
|||||||
"ndk-context",
|
"ndk-context",
|
||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -132,7 +132,7 @@ dependencies = [
|
|||||||
"polling",
|
"polling",
|
||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -490,7 +490,7 @@ dependencies = [
|
|||||||
"combine",
|
"combine",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
@ -599,7 +599,7 @@ dependencies = [
|
|||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -991,6 +991,7 @@ dependencies = [
|
|||||||
"glutin",
|
"glutin",
|
||||||
"glutin-winit",
|
"glutin-winit",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
"thiserror 2.0.17",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1163,7 +1164,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-csd-frame",
|
"wayland-csd-frame",
|
||||||
@ -1206,7 +1207,16 @@ version = "1.0.69"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1220,6 +1230,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-skia"
|
name = "tiny-skia"
|
||||||
version = "0.11.4"
|
version = "0.11.4"
|
||||||
|
|||||||
@ -9,6 +9,7 @@ gl = "0.14.0"
|
|||||||
glutin = "0.32.3"
|
glutin = "0.32.3"
|
||||||
glutin-winit = "0.5.0"
|
glutin-winit = "0.5.0"
|
||||||
raw-window-handle = "0.6.2"
|
raw-window-handle = "0.6.2"
|
||||||
|
thiserror = "2.0.17"
|
||||||
winit = "0.30.12"
|
winit = "0.30.12"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
161
src/app/app.rs
161
src/app/app.rs
@ -1,62 +1,21 @@
|
|||||||
use std::num::NonZero;
|
|
||||||
use glutin::config::{ ConfigTemplateBuilder, GetGlConfig };
|
|
||||||
use glutin::context::PossiblyCurrentContext;
|
|
||||||
use glutin::display::GetGlDisplay;
|
|
||||||
use glutin::prelude::{GlDisplay, NotCurrentGlContext, PossiblyCurrentGlContext};
|
|
||||||
use glutin::surface::{GlSurface, Surface, SwapInterval, WindowSurface};
|
|
||||||
use glutin_winit::{DisplayBuilder, GlWindow};
|
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::KeyEvent;
|
use winit::event::KeyEvent;
|
||||||
use winit::keyboard::{Key, NamedKey};
|
use winit::keyboard::{Key, NamedKey};
|
||||||
use winit::window::{ Window, WindowAttributes };
|
use winit::window::WindowAttributes;
|
||||||
use winit::{event, window};
|
use winit::{event, window};
|
||||||
|
|
||||||
use crate::app::gl::{gl_config_picker, gl_create_context};
|
use crate::backend::RenderBackend;
|
||||||
use crate::app::renderer::Renderer;
|
|
||||||
use crate::models::Scene;
|
|
||||||
|
|
||||||
enum GlDisplayCreationState {
|
pub struct App<B: RenderBackend> {
|
||||||
Builder(Box<DisplayBuilder>),
|
backend: B,
|
||||||
Init
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppState {
|
impl<B: RenderBackend> App<B> {
|
||||||
gl_surface: Surface<WindowSurface>,
|
|
||||||
window: Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
gl_display: GlDisplayCreationState,
|
|
||||||
gl_context: Option<PossiblyCurrentContext>,
|
|
||||||
template: ConfigTemplateBuilder,
|
|
||||||
|
|
||||||
renderer: Option<Renderer>,
|
|
||||||
current_scene: Option<String>,
|
|
||||||
|
|
||||||
state: Option<AppState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let template = ConfigTemplateBuilder::new()
|
|
||||||
.with_alpha_size(8)
|
|
||||||
.with_transparency(cfg!(target_os = "macos"));
|
|
||||||
|
|
||||||
let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes()));
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
template,
|
backend: B::new(),
|
||||||
gl_display: GlDisplayCreationState::Builder(Box::new(display_builder)),
|
|
||||||
gl_context: None,
|
|
||||||
renderer: None,
|
|
||||||
state: None,
|
|
||||||
current_scene: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_current_scene(&mut self, current_scene: &str) {
|
|
||||||
self.current_scene = Some(current_scene.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_attributes() -> WindowAttributes {
|
pub fn window_attributes() -> WindowAttributes {
|
||||||
@ -65,74 +24,13 @@ pub fn window_attributes() -> WindowAttributes {
|
|||||||
.with_title("Test Window")
|
.with_title("Test Window")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationHandler for App {
|
impl<B: RenderBackend> ApplicationHandler for App<B> {
|
||||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
let (window, gl_config) = match &self.gl_display {
|
self.backend.resume(event_loop);
|
||||||
GlDisplayCreationState::Builder(builder) => {
|
|
||||||
let (window, gl_config) = match builder.clone().build(
|
|
||||||
event_loop,
|
|
||||||
self.template.clone(),
|
|
||||||
gl_config_picker
|
|
||||||
) {
|
|
||||||
Ok((window, gl_config)) => (window.unwrap(), gl_config),
|
|
||||||
Err(e) => {
|
|
||||||
panic!("Encountered Error {:#?}", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.gl_display = GlDisplayCreationState::Init;
|
|
||||||
self.gl_context = Some(gl_create_context(&window, &gl_config).treat_as_possibly_current());
|
|
||||||
|
|
||||||
(window, gl_config)
|
|
||||||
},
|
|
||||||
GlDisplayCreationState::Init => {
|
|
||||||
let gl_config = self.gl_context.as_ref().unwrap().config();
|
|
||||||
match glutin_winit::finalize_window(event_loop, window_attributes(), &gl_config) {
|
|
||||||
Ok(window) => (window, gl_config),
|
|
||||||
Err(e) => {
|
|
||||||
panic!("Todo: Handle error for {:#?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let attrs = window
|
|
||||||
.build_surface_attributes(Default::default())
|
|
||||||
.expect("Failed to build surface attributes");
|
|
||||||
|
|
||||||
let gl_surface = unsafe {
|
|
||||||
gl_config.display().create_window_surface(&gl_config, &attrs).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let gl_context = self.gl_context.as_ref().unwrap();
|
|
||||||
gl_context.make_current(&gl_surface).unwrap();
|
|
||||||
|
|
||||||
if self.renderer.is_none() {
|
|
||||||
let mut renderer = Renderer::new(&gl_context.display());
|
|
||||||
|
|
||||||
// renderer.upload_scene_data(&self.scene_data);
|
|
||||||
|
|
||||||
println!("Built Renderer");
|
|
||||||
|
|
||||||
self.renderer.get_or_insert_with(|| renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(res) = gl_surface.set_swap_interval(
|
|
||||||
gl_context,
|
|
||||||
SwapInterval::Wait(NonZero::new(1).unwrap())
|
|
||||||
) {
|
|
||||||
eprintln!("Error setting vsync: {:?}", res)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(self.state.replace(AppState { gl_surface, window }).is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
println!("Android window removed");
|
println!("Android window removed");
|
||||||
self.state = None;
|
|
||||||
self.gl_context = Some(
|
|
||||||
self.gl_context.take().unwrap().make_not_current().unwrap().treat_as_possibly_current()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(
|
fn window_event(
|
||||||
@ -143,18 +41,6 @@ impl ApplicationHandler for App {
|
|||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
event::WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => {
|
event::WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => {
|
||||||
if let Some(AppState { gl_surface, window: _ }) = &self.state {
|
|
||||||
let gl_context = self.gl_context.as_ref().unwrap();
|
|
||||||
|
|
||||||
gl_surface.resize(
|
|
||||||
gl_context,
|
|
||||||
NonZero::new(size.width).unwrap(),
|
|
||||||
NonZero::new(size.height).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let renderer = self.renderer.as_ref().unwrap();
|
|
||||||
renderer.resize(size.width as i32, size.height as i32);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
event::WindowEvent::CloseRequested
|
event::WindowEvent::CloseRequested
|
||||||
| event::WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, .. } => {
|
| event::WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. }, .. } => {
|
||||||
@ -165,30 +51,17 @@ impl ApplicationHandler for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn exiting(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn exiting(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
let _gl_display = self.gl_context.take().unwrap().display();
|
// let _gl_display = self.gl_context.take().unwrap().display();
|
||||||
|
//
|
||||||
self.state = None;
|
// #[cfg(egl_backend)]
|
||||||
|
// #[allow(irrefutable_let_patterns)]
|
||||||
#[cfg(egl_backend)]
|
// if let glutin::display::Display::Egl(display) = _gl_display {
|
||||||
#[allow(irrefutable_let_patterns)]
|
// unsafe {
|
||||||
if let glutin::display::Display::Egl(display) = _gl_display {
|
// display.terminate();
|
||||||
unsafe {
|
// }
|
||||||
display.terminate();
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
if let Some(AppState { gl_surface, window }) = self.state.as_ref() {
|
|
||||||
let gl_context = self.gl_context.as_ref().unwrap();
|
|
||||||
let renderer = self.renderer.as_ref().unwrap();
|
|
||||||
|
|
||||||
if let Some(current_scene) = &self.current_scene {
|
|
||||||
renderer.render(current_scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.request_redraw();
|
|
||||||
gl_surface.swap_buffers(gl_context).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/app/camera.rs
Normal file
3
src/app/camera.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub struct Camera {
|
||||||
|
|
||||||
|
}
|
||||||
3
src/app/light.rs
Normal file
3
src/app/light.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub struct Light {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,199 +0,0 @@
|
|||||||
use glutin::prelude::GlDisplay;
|
|
||||||
use crate::app::gl;
|
|
||||||
use crate::models::{self, GpuMesh, Scene, ShaderSource};
|
|
||||||
use crate::shaders::Shader;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ffi::{ CStr, CString };
|
|
||||||
|
|
||||||
fn get_gl_string(gl: &gl::Gl, variant: gl::types::GLenum) -> Option<&'static CStr>{
|
|
||||||
unsafe {
|
|
||||||
let s = gl.GetString(variant);
|
|
||||||
(!s.is_null()).then(|| CStr::from_ptr(s.cast()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Renderer {
|
|
||||||
gl: std::rc::Rc<gl::Gl>,
|
|
||||||
shaders: HashMap<String, gl::types::GLuint>,
|
|
||||||
scenes: HashMap<String, Scene>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderer {
|
|
||||||
pub fn new<D: GlDisplay>(gl_display: &D) -> Self {
|
|
||||||
let gl = gl::Gl::load_with(|symbol| {
|
|
||||||
let symbol = CString::new(symbol).unwrap();
|
|
||||||
gl_display.get_proc_address(symbol.as_c_str()).cast()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
|
|
||||||
println!("Running on {}", renderer.to_string_lossy());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
|
|
||||||
println!("OpenGL version {}", version.to_string_lossy());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
|
|
||||||
println!("Shaders version on {}", shaders_version.to_string_lossy());
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
gl: std::rc::Rc::new(gl),
|
|
||||||
shaders: HashMap::new(),
|
|
||||||
scenes: HashMap::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload_mesh(&self, mesh: &models::Mesh) -> GpuMesh {
|
|
||||||
unsafe {
|
|
||||||
let gl = &self.gl;
|
|
||||||
|
|
||||||
let mut vao = std::mem::zeroed();
|
|
||||||
gl.GenVertexArrays(1, &mut vao);
|
|
||||||
gl.BindVertexArray(vao);
|
|
||||||
|
|
||||||
let mut vbo = std::mem::zeroed();
|
|
||||||
gl.GenBuffers(1, &mut vbo);
|
|
||||||
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
|
|
||||||
let vertex_data = mesh.vertices
|
|
||||||
.iter()
|
|
||||||
.map(|v| {
|
|
||||||
[v.position.x, v.position.y, v.position.z, 1.0, 0.1, 0.1]
|
|
||||||
})
|
|
||||||
.collect::<Vec<[f32; 6]>>();
|
|
||||||
|
|
||||||
gl.BufferData(
|
|
||||||
gl::ARRAY_BUFFER,
|
|
||||||
(vertex_data.len() * std::mem::size_of::<[f32; 6]>()) as gl::types::GLsizeiptr,
|
|
||||||
vertex_data.as_ptr() as *const _,
|
|
||||||
gl::STATIC_DRAW,
|
|
||||||
);
|
|
||||||
|
|
||||||
let pos_attrib = 0;
|
|
||||||
let color_attrib = 1;
|
|
||||||
|
|
||||||
gl.VertexAttribPointer(
|
|
||||||
pos_attrib as gl::types::GLuint,
|
|
||||||
3,
|
|
||||||
gl::FLOAT,
|
|
||||||
0,
|
|
||||||
6 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
|
||||||
std::ptr::null()
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.VertexAttribPointer(
|
|
||||||
color_attrib as gl::types::GLuint,
|
|
||||||
3,
|
|
||||||
gl::FLOAT,
|
|
||||||
0,
|
|
||||||
6 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
|
||||||
(3 * std::mem::size_of::<f32>()) as *const _,
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
|
||||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
|
||||||
|
|
||||||
GpuMesh {
|
|
||||||
vao,
|
|
||||||
vbo,
|
|
||||||
_ebo: None,
|
|
||||||
vertex_count: mesh.vertices.len() as i32,
|
|
||||||
_index_count: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_shader(gl: &gl::Gl, shader: gl::types::GLenum, source: &[u8]) -> gl::types::GLuint {
|
|
||||||
unsafe {
|
|
||||||
let shader = gl.CreateShader(shader);
|
|
||||||
gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null());
|
|
||||||
gl.CompileShader(shader);
|
|
||||||
shader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_shader(&self, shader_src: &ShaderSource) -> Shader {
|
|
||||||
unsafe {
|
|
||||||
let vertex_shader = Renderer::compile_shader(&self.gl, gl::VERTEX_SHADER, shader_src.vertex_src);
|
|
||||||
let fragment_shader = Renderer::compile_shader(&self.gl, gl::FRAGMENT_SHADER, shader_src.fragment_src);
|
|
||||||
|
|
||||||
let program = self.gl.CreateProgram();
|
|
||||||
|
|
||||||
self.gl.AttachShader(program, vertex_shader);
|
|
||||||
self.gl.AttachShader(program, fragment_shader);
|
|
||||||
|
|
||||||
self.gl.LinkProgram(program);
|
|
||||||
|
|
||||||
self.gl.DeleteShader(vertex_shader);
|
|
||||||
self.gl.DeleteShader(fragment_shader);
|
|
||||||
|
|
||||||
Shader {
|
|
||||||
program,
|
|
||||||
gl: self.gl.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_shader(&mut self, name: &str, shader: gl::types::GLuint) {
|
|
||||||
if let None = self.shaders.get(name) {
|
|
||||||
self.shaders.insert(name.to_string(), shader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_shader(&self, name: &str) -> Option<&gl::types::GLuint> {
|
|
||||||
self.shaders.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_shader(&mut self, name: &str) {
|
|
||||||
self.shaders.remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&self, width: i32, height: i32) {
|
|
||||||
unsafe {
|
|
||||||
self.gl.Viewport(0, 0, width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_object(&self, obj: &GpuMesh, shader: gl::types::GLuint) {
|
|
||||||
unsafe {
|
|
||||||
self.UseProgram(shader);
|
|
||||||
|
|
||||||
self.BindBuffer(gl::ARRAY_BUFFER, obj.vbo);
|
|
||||||
self.BindVertexArray(obj.vao);
|
|
||||||
|
|
||||||
self.DrawArrays(gl::TRIANGLES, 0, obj.vertex_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&self, current_scene: &String) {
|
|
||||||
unsafe {
|
|
||||||
self.ClearColor(1.0, 1.0, 1.0, 0.9);
|
|
||||||
self.Clear(gl::COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scene) = self.scenes.get(current_scene) {
|
|
||||||
scene.models.iter().for_each(|(obj, shader)| {
|
|
||||||
self.draw_object(obj, *shader);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
eprintln!("Unknown Scene Requested: {}", current_scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Renderer {
|
|
||||||
type Target = gl::Gl;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.gl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Renderer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
self.shaders.values().for_each(|shader| self.gl.DeleteProgram(*shader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
src/app/scene.rs
Normal file
36
src/app/scene.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{app::{camera::Camera, light::Light}, assets, backend::{backend, opengl::GlDrawCommand}, handles, models::Model};
|
||||||
|
|
||||||
|
pub struct Scene {
|
||||||
|
pub light: Light,
|
||||||
|
pub camera: Camera,
|
||||||
|
pub models: Vec<Model>,
|
||||||
|
pub materials: HashMap<handles::MaterialHandle, assets::mtl::Material>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
light: Light {},
|
||||||
|
camera: Camera {},
|
||||||
|
materials: HashMap::new(),
|
||||||
|
models: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, backend: &impl backend::RenderBackend) {
|
||||||
|
for model in &self.models {
|
||||||
|
for (mesh, mtl) in &model.parts {
|
||||||
|
let mtl = &self.materials[&mtl];
|
||||||
|
let draw_cmd = GlDrawCommand {
|
||||||
|
mesh: *mesh,
|
||||||
|
shader: mtl.shader,
|
||||||
|
uniforms: mtl.to_uniforms(),
|
||||||
|
textures: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/assets/error.rs
Normal file
33
src/assets/error.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ParseError {
|
||||||
|
#[error("\n expected {expected} values, got {got}")]
|
||||||
|
TooFewValues { expected: usize, got: usize },
|
||||||
|
|
||||||
|
#[error("\n expected {expected} values, got {got}")]
|
||||||
|
TooManyValues { expected: usize, got: usize },
|
||||||
|
|
||||||
|
#[error("\n invalid float: {0}")]
|
||||||
|
InvalidFloat(#[from] std::num::ParseFloatError),
|
||||||
|
|
||||||
|
#[error("\n invalid value: {0}")]
|
||||||
|
InvalidValue(#[from] Box<dyn std::error::Error + Send + Sync>),
|
||||||
|
|
||||||
|
#[error("\n unknown prefix: {0}")]
|
||||||
|
UnknownPrefix(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum AssetError {
|
||||||
|
#[error("\n Parse in {file} at line {line}: {source}")]
|
||||||
|
Parse {
|
||||||
|
file: String,
|
||||||
|
line: usize,
|
||||||
|
#[source]
|
||||||
|
source: ParseError
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I/O: {0}")]
|
||||||
|
IO(#[from] std::io::Error),
|
||||||
|
}
|
||||||
143
src/assets/mtl.rs
Normal file
143
src/assets/mtl.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use crate::{assets::{error::AssetError, parser::parse_args}, backend::opengl::Uniform, math::Vec3, shaders::ShaderHandle};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TextureId(pub u32);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
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,
|
||||||
|
|
||||||
|
pub diffuse_texture: Option<TextureId>,
|
||||||
|
pub normal_texture: Option<TextureId>,
|
||||||
|
pub specular_texture: Option<TextureId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::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,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NEW_MATERIAL_DELIMITER: &str = "newmtl ";
|
||||||
|
|
||||||
|
pub fn load_materials(path: &str) -> Result<Vec<Material>, AssetError> {
|
||||||
|
let content = std::fs::read_to_string(path)?;
|
||||||
|
let mut materials = vec![];
|
||||||
|
let mut current_material: Option<Material> = None;
|
||||||
|
|
||||||
|
for (i, line) in content.lines().enumerate() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() || line.starts_with('#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.starts_with(NEW_MATERIAL_DELIMITER) {
|
||||||
|
if let Some(mat) = current_material.take() {
|
||||||
|
materials.push(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mat_name = line.strip_prefix(NEW_MATERIAL_DELIMITER).unwrap().trim();
|
||||||
|
let mut material = Material::new();
|
||||||
|
|
||||||
|
material.name = mat_name.to_string();
|
||||||
|
current_material = Some(material);
|
||||||
|
|
||||||
|
} else if let Some(ref mut material) = current_material {
|
||||||
|
let delim = " ";
|
||||||
|
let i = i + 1;
|
||||||
|
|
||||||
|
if let Some((key, values)) = line.split_once(' ') {
|
||||||
|
match key.trim() {
|
||||||
|
"Ka" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(v) => material.ambient = Vec3(v),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Kd" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(v) => material.diffuse = Vec3(v),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Ks" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(v) => material.specular = Vec3(v),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Ke" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(v) => material.emissive = Vec3(v),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"Ns" => {
|
||||||
|
match parse_args::<1, f32>(values, delim) {
|
||||||
|
Ok(v) => material.shininess = v[0],
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d" => {
|
||||||
|
match parse_args::<1, f32>(values, delim) {
|
||||||
|
Ok(v) => material.opacity = v[0],
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mat) = current_material {
|
||||||
|
materials.push(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(materials)
|
||||||
|
}
|
||||||
105
src/assets/obj.rs
Normal file
105
src/assets/obj.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
use crate::{assets::parser::parse_args, math::{ Vec2, Vec3 }};
|
||||||
|
use super::types;
|
||||||
|
use super::error::AssetError;
|
||||||
|
|
||||||
|
pub fn load_obj(path: &str) -> Result<types::ObjData, AssetError> {
|
||||||
|
let mut obj_data = types::ObjData::new();
|
||||||
|
let content = std::fs::read_to_string(path)?;
|
||||||
|
let mut current_name = String::new();
|
||||||
|
|
||||||
|
let mut current_mash = types::ObjMesh {
|
||||||
|
name: String::new(),
|
||||||
|
material_name: None,
|
||||||
|
faces: vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let delim = " ";
|
||||||
|
|
||||||
|
for (i, line) in content.lines().enumerate() {
|
||||||
|
let i = i + 1;
|
||||||
|
let line = line.trim();
|
||||||
|
if let Some((prefix, values)) = line.split_once(' ') {
|
||||||
|
match prefix.trim() {
|
||||||
|
"v" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(vec) => obj_data.vertices.push(Vec3(vec)),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vt" => {
|
||||||
|
match parse_args::<2, f32>(values, delim) {
|
||||||
|
Ok(vec) => obj_data.tex_coords.push(Vec2(vec)),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vn" => {
|
||||||
|
match parse_args::<3, f32>(values, delim) {
|
||||||
|
Ok(vec) => obj_data.normals.push(Vec3(vec)),
|
||||||
|
Err(e) => return Err(AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"o" | "g" => {
|
||||||
|
current_name = values.to_string();
|
||||||
|
current_mash.name = current_name.clone();
|
||||||
|
}
|
||||||
|
"usemtl" => {
|
||||||
|
if !current_mash.faces.is_empty() {
|
||||||
|
obj_data.meshes.push(current_mash);
|
||||||
|
}
|
||||||
|
|
||||||
|
let material_name = Some(values.to_string());
|
||||||
|
current_mash = types::ObjMesh {
|
||||||
|
name: current_name.clone(),
|
||||||
|
material_name,
|
||||||
|
faces: vec![]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
"mtllib" => {
|
||||||
|
obj_data.material_lib = Some(values.to_string());
|
||||||
|
}
|
||||||
|
"f" => {
|
||||||
|
let mut current_face = types::ObjFace { indices: vec![] };
|
||||||
|
let mut line_split = values.split_whitespace();
|
||||||
|
|
||||||
|
while let Some(val) = line_split.next() {
|
||||||
|
let face_index: [u32; 3] = parse_args::<3, u32>(val, "/")
|
||||||
|
.map_err(|e| AssetError::Parse {
|
||||||
|
file: path.to_string(),
|
||||||
|
line: i,
|
||||||
|
source: e
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// OBJ indices are 1-based, convert to 0-based
|
||||||
|
let vertex_index = types::VertexIndex {
|
||||||
|
position: face_index[0] - 1,
|
||||||
|
tex_coord: if face_index[1] != 0 { Some(face_index[1] - 1) } else { None },
|
||||||
|
normal: if face_index[2] != 0 { Some(face_index[2] - 1) } 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)
|
||||||
|
}
|
||||||
62
src/assets/parser.rs
Normal file
62
src/assets/parser.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::assets::error::ParseError;
|
||||||
|
|
||||||
|
pub fn parse_args<const N: usize, T>(
|
||||||
|
values: &str,
|
||||||
|
delim: &str
|
||||||
|
) -> Result<[T; N], ParseError>
|
||||||
|
where
|
||||||
|
T: FromStr + Copy + Default,
|
||||||
|
T::Err: std::error::Error + 'static + Send + Sync
|
||||||
|
{
|
||||||
|
let mut result = [T::default(); N];
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
let values = values.split(delim);
|
||||||
|
|
||||||
|
for v in values {
|
||||||
|
if i < N {
|
||||||
|
let parsed = if !v.is_empty() {
|
||||||
|
v.parse::<T>().map_err(|e| {ParseError::InvalidValue(Box::new(e))})?
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
result[i] = parsed;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > N {
|
||||||
|
Err(ParseError::TooManyValues{ expected: N, got: i })
|
||||||
|
} else if i < N {
|
||||||
|
Err(ParseError::TooFewValues{ expected: N, got: i })
|
||||||
|
} else {
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_all_args<T>(
|
||||||
|
values: &str,
|
||||||
|
delim: &str
|
||||||
|
) -> Result<Vec<T>, ParseError>
|
||||||
|
where
|
||||||
|
T: FromStr + Copy + Default,
|
||||||
|
T::Err: std::error::Error + 'static + Send + Sync
|
||||||
|
{
|
||||||
|
let mut result: Vec<T> = Vec::new();
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
let values = values.split(delim);
|
||||||
|
|
||||||
|
for v in values {
|
||||||
|
let parsed = if !v.is_empty() {
|
||||||
|
v.parse::<T>().map_err(|e| {ParseError::InvalidValue(Box::new(e))})?
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
result[i] = parsed;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
41
src/assets/types.rs
Normal file
41
src/assets/types.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use crate::math::{ Vec2, 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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
normals: vec![],
|
||||||
|
vertices: vec![],
|
||||||
|
tex_coords: vec![],
|
||||||
|
meshes: vec![],
|
||||||
|
material_lib: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct VertexIndex {
|
||||||
|
pub position: u32,
|
||||||
|
pub tex_coord: Option<u32>,
|
||||||
|
pub normal: Option<u32>
|
||||||
|
}
|
||||||
33
src/backend/backend.rs
Normal file
33
src/backend/backend.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use crate::shaders;
|
||||||
|
|
||||||
|
pub trait RenderBackend {
|
||||||
|
type ShaderSource;
|
||||||
|
type DrawCommand;
|
||||||
|
fn new() -> Self where Self: Sized;
|
||||||
|
|
||||||
|
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<shaders::ShaderHandle, String>;
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub struct FrameContext<'a> {
|
||||||
|
// backend: &'a mut dyn RenderBackend,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl FrameContext<'_> {
|
||||||
|
// pub fn draw(&mut self, mesh: &Mesh, material: MaterialHandle, transform: Mat4);
|
||||||
|
// }
|
||||||
7
src/backend/gpu/mesh.rs
Normal file
7
src/backend/gpu/mesh.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub struct GpuMesh {
|
||||||
|
pub vao: gl::types::GLuint,
|
||||||
|
pub vbo: gl::types::GLuint,
|
||||||
|
pub ebo: Option<gl::types::GLuint>,
|
||||||
|
pub vertex_count: i32,
|
||||||
|
pub index_count: Option<i32>,
|
||||||
|
}
|
||||||
@ -2,7 +2,10 @@
|
|||||||
#![allow(unsafe_op_in_unsafe_fn)]
|
#![allow(unsafe_op_in_unsafe_fn)]
|
||||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
pub use Gles2 as Gl;
|
pub use Gles2 as Gl;
|
||||||
|
pub use crate::math;
|
||||||
|
|
||||||
use glutin::config::{Config, GlConfig};
|
use glutin::config::{Config, GlConfig};
|
||||||
use glutin::context::{ContextApi, ContextAttributesBuilder, NotCurrentContext, Version};
|
use glutin::context::{ContextApi, ContextAttributesBuilder, NotCurrentContext, Version};
|
||||||
@ -11,6 +14,25 @@ use glutin::prelude::GlDisplay;
|
|||||||
use raw_window_handle::HasWindowHandle;
|
use raw_window_handle::HasWindowHandle;
|
||||||
use winit::window::Window;
|
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<Vec<(u32, u32)>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gl_config_picker(configs: Box<dyn Iterator<Item = Config> + '_>) -> Config {
|
pub fn gl_config_picker(configs: Box<dyn Iterator<Item = Config> + '_>) -> Config {
|
||||||
configs
|
configs
|
||||||
.reduce(|accum, config| {
|
.reduce(|accum, config| {
|
||||||
@ -50,4 +72,3 @@ pub fn gl_create_context(window: &Window, gl_config: &Config) -> NotCurrentConte
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
163
src/backend/opengl/gl_backend.rs
Normal file
163
src/backend/opengl/gl_backend.rs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::num::NonZero;
|
||||||
|
use glutin::config::GetGlConfig;
|
||||||
|
use glutin::display::GetGlDisplay;
|
||||||
|
use glutin::prelude::{GlDisplay, NotCurrentGlContext, PossiblyCurrentGlContext};
|
||||||
|
use glutin::surface::{GlSurface, SwapInterval};
|
||||||
|
use glutin_winit::GlWindow;
|
||||||
|
use winit::window::Window;
|
||||||
|
use crate::backend::gpu;
|
||||||
|
|
||||||
|
use crate::{backend, handles, shaders};
|
||||||
|
use crate::app::app::window_attributes;
|
||||||
|
use crate::backend::opengl::{self, GlRenderer};
|
||||||
|
|
||||||
|
enum GlDisplayCreationState {
|
||||||
|
Builder(glutin_winit::DisplayBuilder),
|
||||||
|
Init
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppState {
|
||||||
|
gl_surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
|
||||||
|
window: Window,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlBackend {
|
||||||
|
gl_display: GlDisplayCreationState,
|
||||||
|
gl_context: Option<glutin::context::PossiblyCurrentContext>,
|
||||||
|
gl: Option<GlRenderer>,
|
||||||
|
template: glutin::config::ConfigTemplateBuilder,
|
||||||
|
state: Option<AppState>,
|
||||||
|
meshes: HashMap<handles::MeshHandle, gpu::GpuMesh>,
|
||||||
|
shaders: HashMap<shaders::ShaderHandle, shaders::Shader>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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_transparency(cfg!(target_os = "macos"));
|
||||||
|
|
||||||
|
let display_builder = glutin_winit::DisplayBuilder::new().with_window_attributes(Some(window_attributes()));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gl_display: GlDisplayCreationState::Builder(display_builder),
|
||||||
|
gl_context: None,
|
||||||
|
state: None,
|
||||||
|
gl: None,
|
||||||
|
meshes: HashMap::new(),
|
||||||
|
shaders: HashMap::new(),
|
||||||
|
template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
|
let (window, gl_config) = match &self.gl_display {
|
||||||
|
GlDisplayCreationState::Builder(builder) => {
|
||||||
|
let (window, gl_config) = match builder.clone().build(
|
||||||
|
event_loop,
|
||||||
|
self.template.clone(),
|
||||||
|
opengl::gl_config_picker
|
||||||
|
) {
|
||||||
|
Ok((window, gl_config)) => (window.unwrap(), gl_config),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {:#?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.gl_display = GlDisplayCreationState::Init;
|
||||||
|
self.gl_context = Some(opengl::gl_create_context(&window, &gl_config).treat_as_possibly_current());
|
||||||
|
|
||||||
|
(window, gl_config)
|
||||||
|
},
|
||||||
|
GlDisplayCreationState::Init => {
|
||||||
|
let gl_config = self.gl_context.as_ref().unwrap().config();
|
||||||
|
match glutin_winit::finalize_window(event_loop, window_attributes(), &gl_config) {
|
||||||
|
Ok(window) => (window, gl_config),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {:#?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let attrs = window
|
||||||
|
.build_surface_attributes(Default::default())
|
||||||
|
.expect("Failed to build surface attributes");
|
||||||
|
|
||||||
|
let gl_surface = unsafe {
|
||||||
|
gl_config.display().create_window_surface(&gl_config, &attrs).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let gl_context = self.gl_context.as_ref().unwrap();
|
||||||
|
gl_context.make_current(&gl_surface).unwrap();
|
||||||
|
|
||||||
|
if self.gl.is_none() {
|
||||||
|
let renderer = opengl::GlRenderer::new(
|
||||||
|
&gl_context.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
self.gl = Some(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(res) = gl_surface.set_swap_interval(
|
||||||
|
gl_context,
|
||||||
|
SwapInterval::Wait(NonZero::new(1).unwrap())
|
||||||
|
) {
|
||||||
|
eprintln!("Error setting vsync: {:?}", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(self.state.replace(AppState { gl_surface, window }).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspend(&mut self) {
|
||||||
|
self.state = None;
|
||||||
|
self.gl_context = Some(
|
||||||
|
self.gl_context.take().unwrap().make_not_current().unwrap().treat_as_possibly_current()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, width: u32, height: u32) {
|
||||||
|
if let Some(AppState { gl_surface, window: _ }) = &self.state && self.gl.is_some() {
|
||||||
|
let gl_context = self.gl_context.as_ref().unwrap();
|
||||||
|
|
||||||
|
gl_surface.resize(
|
||||||
|
gl_context,
|
||||||
|
NonZero::new(width).unwrap(),
|
||||||
|
NonZero::new(height).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.gl
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.Viewport(0, 0, width as i32, height as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_shader(&mut self, desc: Self::ShaderSource) -> Result<shaders::ShaderHandle, String> {
|
||||||
|
if let Some(gl) = &self.gl {
|
||||||
|
let shader = gl.create_shader(&desc);
|
||||||
|
let shader_handle = self.shaders.len() as u32;
|
||||||
|
self.shaders.insert(shader_handle, shader);
|
||||||
|
Ok(shader_handle)
|
||||||
|
} else {
|
||||||
|
todo!("implement error type for GlBackend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, cmd: &Self::DrawCommand) {
|
||||||
|
let mesh = self.meshes.get(&cmd.mesh);
|
||||||
|
let shader = self.shaders.get(&cmd.shader);
|
||||||
|
|
||||||
|
if let (Some(mesh), Some(shader)) = (mesh, shader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
240
src/backend/opengl/gl_renderer.rs
Normal file
240
src/backend/opengl/gl_renderer.rs
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
use glutin::prelude::GlDisplay;
|
||||||
|
use crate::backend::opengl::{self, 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 };
|
||||||
|
|
||||||
|
/// Render state containing camera and lighting information
|
||||||
|
pub struct RenderState {
|
||||||
|
pub projection: Mat4,
|
||||||
|
pub view: Mat4,
|
||||||
|
pub model: Mat4,
|
||||||
|
pub camera_pos: Vec3,
|
||||||
|
pub light_pos: Vec3,
|
||||||
|
pub light_color: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderState {
|
||||||
|
pub fn new(aspect_ratio: f32) -> Self {
|
||||||
|
let camera_pos = Vec3([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]),
|
||||||
|
),
|
||||||
|
model: Mat4::identity(),
|
||||||
|
camera_pos,
|
||||||
|
light_pos: Vec3([3.0, 5.0, 3.0]),
|
||||||
|
light_color: Vec3([1.0, 1.0, 1.0]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_gl_string(gl: &opengl::Gl, variant: gl::types::GLenum) -> Option<&'static CStr>{
|
||||||
|
unsafe {
|
||||||
|
let s = gl.GetString(variant);
|
||||||
|
(!s.is_null()).then(|| CStr::from_ptr(s.cast()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlRenderer {
|
||||||
|
gl: std::rc::Rc<opengl::Gl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlRenderer {
|
||||||
|
pub fn new<D: GlDisplay>(gl_display: &D) -> Self {
|
||||||
|
let gl = opengl::Gl::load_with(|symbol| {
|
||||||
|
let symbol = CString::new(symbol).unwrap();
|
||||||
|
gl_display.get_proc_address(symbol.as_c_str()).cast()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
|
||||||
|
println!("Running on {}", renderer.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
|
||||||
|
println!("OpenGL version {}", version.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gl: std::rc::Rc::new(gl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upload_mesh(&self, mesh: &geometry::Mesh) -> GpuMesh {
|
||||||
|
unsafe {
|
||||||
|
let gl = &self.gl;
|
||||||
|
|
||||||
|
let mut vao = std::mem::zeroed();
|
||||||
|
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<f32> = 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();
|
||||||
|
|
||||||
|
gl.BufferData(
|
||||||
|
gl::ARRAY_BUFFER,
|
||||||
|
(vertex_data.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
|
||||||
|
vertex_data.as_ptr() as *const _,
|
||||||
|
gl::STATIC_DRAW,
|
||||||
|
);
|
||||||
|
|
||||||
|
let stride = 8 * std::mem::size_of::<f32>() 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::<f32>()) as *const _);
|
||||||
|
gl.EnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
// Texcoord attribute (location = 2)
|
||||||
|
gl.VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, stride, (6 * std::mem::size_of::<f32>()) 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);
|
||||||
|
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
|
gl.BufferData(
|
||||||
|
gl::ELEMENT_ARRAY_BUFFER,
|
||||||
|
(mesh.indices.len() * std::mem::size_of::<u32>()) as gl::types::GLsizeiptr,
|
||||||
|
mesh.indices.as_ptr() as *const _,
|
||||||
|
gl::STATIC_DRAW,
|
||||||
|
);
|
||||||
|
(Some(ebo), Some(mesh.indices.len() as i32))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
|
||||||
|
GpuMesh {
|
||||||
|
vao,
|
||||||
|
vbo,
|
||||||
|
ebo,
|
||||||
|
vertex_count: mesh.vertices.len() as i32,
|
||||||
|
index_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_shader(gl: &opengl::Gl, shader: gl::types::GLenum, source: &[u8]) -> gl::types::GLuint {
|
||||||
|
unsafe {
|
||||||
|
let shader = gl.CreateShader(shader);
|
||||||
|
gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null());
|
||||||
|
gl.CompileShader(shader);
|
||||||
|
shader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_shader(&self, shader_src: &opengl::GlShaderSource) -> Shader {
|
||||||
|
unsafe {
|
||||||
|
let vertex_shader = Self::compile_shader(
|
||||||
|
&self.gl,
|
||||||
|
gl::VERTEX_SHADER,
|
||||||
|
shader_src.vertex_src.as_bytes()
|
||||||
|
);
|
||||||
|
let fragment_shader = Self::compile_shader(
|
||||||
|
&self.gl,
|
||||||
|
gl::FRAGMENT_SHADER,
|
||||||
|
shader_src.fragment_src.as_bytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
let program = self.gl.CreateProgram();
|
||||||
|
|
||||||
|
self.gl.AttachShader(program, vertex_shader);
|
||||||
|
self.gl.AttachShader(program, fragment_shader);
|
||||||
|
|
||||||
|
self.gl.LinkProgram(program);
|
||||||
|
|
||||||
|
self.gl.DeleteShader(vertex_shader);
|
||||||
|
self.gl.DeleteShader(fragment_shader);
|
||||||
|
|
||||||
|
Shader(program)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
Uniform::Int(name, value) => {
|
||||||
|
let location = self.GetUniformLocation(shader.0, name.as_ptr());
|
||||||
|
self.Uniform1i(location, value);
|
||||||
|
}
|
||||||
|
Uniform::Vec3(name, value) => {
|
||||||
|
let location = self.GetUniformLocation(shader.0, 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());
|
||||||
|
self.Uniform4f(location, value[0], value[1], value[2], value[3]);
|
||||||
|
}
|
||||||
|
Uniform::Mat4(name, value) => {
|
||||||
|
let location = self.GetUniformLocation(shader.0, name.as_ptr());
|
||||||
|
self.UniformMatrix4fv(location, 1, gl::FALSE, value.as_ptr());
|
||||||
|
}
|
||||||
|
Uniform::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&self, width: i32, height: i32) {
|
||||||
|
unsafe {
|
||||||
|
self.gl.Viewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_mesh(&self, mesh: &GpuMesh) {
|
||||||
|
unsafe {
|
||||||
|
self.BindVertexArray(mesh.vao);
|
||||||
|
|
||||||
|
if let (Some(ebo), Some(count)) = (mesh.ebo, mesh.index_count) {
|
||||||
|
self.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
|
self.DrawElements(gl::TRIANGLES, count, gl::UNSIGNED_INT, std::ptr::null());
|
||||||
|
} else {
|
||||||
|
self.DrawArrays(gl::TRIANGLES, 0, mesh.vertex_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GlRenderer {
|
||||||
|
type Target = opengl::Gl;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.gl
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/backend/opengl/gl_shader.rs
Normal file
4
src/backend/opengl/gl_shader.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub struct GlShaderSource {
|
||||||
|
pub vertex_src: String,
|
||||||
|
pub fragment_src: String,
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::models::{VertexIndex, object::{ObjData, ObjMesh}};
|
use crate::assets::types::{VertexIndex, ObjData, ObjMesh};
|
||||||
|
|
||||||
use super::Vertex;
|
use super::Vertex;
|
||||||
|
|
||||||
8
src/geometry/vertex.rs
Normal file
8
src/geometry/vertex.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use crate::math::{ Vec2, Vec3 };
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub position: Vec3,
|
||||||
|
pub normal: Vec3,
|
||||||
|
pub tex_coord: Vec2,
|
||||||
|
}
|
||||||
5
src/handles.rs
Normal file
5
src/handles.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MeshHandle(pub u32);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MaterialHandle(pub u32);
|
||||||
75
src/lib.rs
75
src/lib.rs
@ -1,35 +1,78 @@
|
|||||||
pub mod models {
|
pub mod models {
|
||||||
pub mod mesh;
|
|
||||||
pub use mesh::*;
|
|
||||||
|
|
||||||
pub mod vertex;
|
|
||||||
pub use vertex::*;
|
|
||||||
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub use model::*;
|
pub use model::*;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod scene;
|
pub mod assets {
|
||||||
pub use scene::*;
|
pub mod mtl;
|
||||||
|
pub mod obj;
|
||||||
|
pub mod types;
|
||||||
|
pub mod parser;
|
||||||
|
|
||||||
pub mod material;
|
pub mod error;
|
||||||
pub use material::*;
|
}
|
||||||
|
|
||||||
pub mod primitives;
|
pub mod geometry {
|
||||||
pub use primitives::*;
|
pub mod vertex;
|
||||||
|
pub use vertex::*;
|
||||||
|
|
||||||
pub mod object;
|
pub mod mesh;
|
||||||
|
pub use mesh::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod math {
|
||||||
|
pub use vector::*;
|
||||||
|
pub mod vector {
|
||||||
|
pub mod vec2;
|
||||||
|
pub mod vec3;
|
||||||
|
pub mod vec4;
|
||||||
|
|
||||||
|
pub use vec2::*;
|
||||||
|
pub use vec3::*;
|
||||||
|
pub use vec4::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use matrix::*;
|
||||||
|
pub mod matrix {
|
||||||
|
pub mod mat4;
|
||||||
|
pub use mat4::*;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod app {
|
pub mod app {
|
||||||
pub mod renderer;
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod gl;
|
pub mod light;
|
||||||
|
pub mod camera;
|
||||||
|
pub mod scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod shaders {
|
pub mod shaders {
|
||||||
pub mod shader;
|
pub mod types;
|
||||||
pub use shader::*;
|
pub use types::*;
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub use constants::*;
|
pub use constants::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod backend {
|
||||||
|
pub mod opengl {
|
||||||
|
pub mod gl_renderer;
|
||||||
|
pub use gl_renderer::*;
|
||||||
|
pub mod gl_backend;
|
||||||
|
pub use gl_backend::*;
|
||||||
|
pub mod gl_shader;
|
||||||
|
pub use gl_shader::*;
|
||||||
|
pub mod gl;
|
||||||
|
pub use gl::*;
|
||||||
|
}
|
||||||
|
pub mod gpu {
|
||||||
|
pub mod mesh;
|
||||||
|
pub use mesh::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod backend;
|
||||||
|
pub use backend::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod handles;
|
||||||
|
|||||||
28
src/main.rs
28
src/main.rs
@ -1,4 +1,6 @@
|
|||||||
use proj::{app::app::App, models::{ShaderSource, object::ObjData}, shaders::{FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_SOURCE}};
|
use proj::{
|
||||||
|
app::app::App, assets, backend::opengl, shaders::{FRAGMENT_SHADER_SOURCE, VERTEX_SHADER_SOURCE}
|
||||||
|
};
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
const HOUSE_OBJ_PATH: &str = "assets/models/small_house/model.obj";
|
const HOUSE_OBJ_PATH: &str = "assets/models/small_house/model.obj";
|
||||||
@ -6,31 +8,29 @@ 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 shader_src = ShaderSource {
|
let _shader_src = opengl::GlShaderSource {
|
||||||
vertex_src: VERTEX_SHADER_SOURCE,
|
vertex_src: VERTEX_SHADER_SOURCE.to_string(),
|
||||||
fragment_src: FRAGMENT_SHADER_SOURCE
|
fragment_src: FRAGMENT_SHADER_SOURCE.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let obj_data = match ObjData::load_obj(HOUSE_OBJ_PATH) {
|
// Load OBJ and materials (CPU-side, before GL context)
|
||||||
|
let obj_data = match assets::obj::load_obj(HOUSE_OBJ_PATH) {
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(e) => return eprintln!("Error: {}", e),
|
Err(e) => return eprintln!("Error loading OBJ: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Done");
|
let materials = match assets::mtl::load_materials(HOUSE_MAT_PATH) {
|
||||||
|
|
||||||
let materials = match ObjData::load_materials(HOUSE_MAT_PATH) {
|
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => return eprintln!("Error: {}", e),
|
Err(e) => return eprintln!("Error loading materials: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
dbg!(&materials);
|
println!("Loaded {} meshes, {} materials", obj_data.meshes.len(), materials.len());
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::<opengl::GlBackend>::new();
|
||||||
|
|
||||||
app.set_current_scene("home");
|
|
||||||
|
|
||||||
let event_res = event_loop.run_app(&mut app);
|
let event_res = event_loop.run_app(&mut app);
|
||||||
|
|
||||||
|
|||||||
@ -1,71 +1,4 @@
|
|||||||
use std::ops::Sub;
|
use crate::math::{ Vec3 };
|
||||||
|
|
||||||
#[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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -161,14 +94,14 @@ impl Mat4 {
|
|||||||
|
|
||||||
pub fn look_at(eye: Vec3, target: Vec3, up: Vec3) -> Self {
|
pub fn look_at(eye: Vec3, target: Vec3, up: Vec3) -> Self {
|
||||||
let f = (target - eye).normalize();
|
let f = (target - eye).normalize();
|
||||||
let r = f.cross(up).normalize();
|
let r = f.cross(&up).normalize();
|
||||||
let u = r.cross(f);
|
let u = r.cross(&f);
|
||||||
Self {
|
Self {
|
||||||
data: [
|
data: [
|
||||||
r.x, u.x, -f.x, 0.0,
|
r.x(), u.x(), -f.x(), 0.0,
|
||||||
r.y, u.y, -f.y, 0.0,
|
r.y(), u.y(), -f.y(), 0.0,
|
||||||
r.z, u.z, -f.z, 0.0,
|
r.z(), u.z(), -f.z(), 0.0,
|
||||||
-r.dot(eye), -u.dot(eye), f.dot(eye), 1.0,
|
-r.dot(&eye), -u.dot(&eye), f.dot(&eye), 1.0,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
155
src/math/vector/vec2.rs
Normal file
155
src/math/vector/vec2.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Vec2(pub [f32; 2]);
|
||||||
|
|
||||||
|
impl Vec2 {
|
||||||
|
pub const fn new(x: f32, y: f32) -> Self {
|
||||||
|
Self([x, y])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn zero() -> Self {
|
||||||
|
Self([0.0, 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length_squared(&self) -> f32 {
|
||||||
|
self.0[0] * self.0[0] + self.0[1] * self.0[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> f32 {
|
||||||
|
self.length_squared().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(&self) -> Self {
|
||||||
|
let len = self.length();
|
||||||
|
if len == 0.0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
Self([self.0[0] / len, self.0[1] / len])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dot(&self, other: &Self) -> f32 {
|
||||||
|
self.0[0] * other.0[0] + self.0[1] * other.0[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *const f32 {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut f32 {
|
||||||
|
self.0.as_mut_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f32> for Vec2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: f32) -> Self::Output {
|
||||||
|
Self([self.0[0] + rhs, self.0[1] + 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<f32> for Vec2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: f32) -> Self::Output {
|
||||||
|
Self([self.0[0] - rhs, self.0[1] - 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vec2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self([self.0[0] * rhs, self.0[1] * 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Vec2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
Self([self.0[0] / rhs, self.0[1] / rhs])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negation
|
||||||
|
impl Neg for Vec2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Self([-self.0[0], -self.0[1]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index operations
|
||||||
|
impl Index<usize> for Vec2 {
|
||||||
|
type Output = f32;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Vec2 {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
171
src/math/vector/vec3.rs
Normal file
171
src/math/vector/vec3.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Vec3(pub [f32; 3]);
|
||||||
|
|
||||||
|
impl Vec3 {
|
||||||
|
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||||
|
Self([x, y, z])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn zero() -> Self {
|
||||||
|
Self([0.0, 0.0, 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length_squared(&self) -> f32 {
|
||||||
|
self.0[0] * self.0[0] + self.0[1] * self.0[1] + self.0[2] * self.0[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> f32 {
|
||||||
|
self.length_squared().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(&self) -> Self {
|
||||||
|
let len = self.length();
|
||||||
|
if len == 0.0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
Self([self.0[0] / len, self.0[1] / len, self.0[2] / 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *const f32 {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut f32 {
|
||||||
|
self.0.as_mut_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negation
|
||||||
|
impl Neg for Vec3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Self([-self.0[0], -self.0[1], -self.0[2]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index operations
|
||||||
|
impl Index<usize> for Vec3 {
|
||||||
|
type Output = f32;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Vec3 {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
171
src/math/vector/vec4.rs
Normal file
171
src/math/vector/vec4.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Vec4(pub [f32; 4]);
|
||||||
|
|
||||||
|
impl Vec4 {
|
||||||
|
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||||
|
Self([x, y, z, w])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn zero() -> Self {
|
||||||
|
Self([0.0, 0.0, 0.0, 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> f32 {
|
||||||
|
self.length_squared().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(&self) -> Self {
|
||||||
|
let len = self.length();
|
||||||
|
if len == 0.0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
Self([self.0[0] / len, self.0[1] / len, self.0[2] / len, self.0[3] / 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *const f32 {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut f32 {
|
||||||
|
self.0.as_mut_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index operations
|
||||||
|
impl Index<usize> for Vec4 {
|
||||||
|
type Output = f32;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Vec4 {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/math/vector/vector.rs
Normal file
68
src/math/vector/vector.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
use super::Vec3;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TextureId(pub u32);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct Material {
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
pub ambient: Vec3,
|
|
||||||
pub diffuse: Vec3,
|
|
||||||
pub specular: Vec3,
|
|
||||||
pub emissive: Vec3,
|
|
||||||
pub shininess: f32,
|
|
||||||
pub opacity: f32,
|
|
||||||
|
|
||||||
pub diffuse_texture: Option<TextureId>,
|
|
||||||
pub normal_texture: Option<TextureId>,
|
|
||||||
pub specular_texture: Option<TextureId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +1,29 @@
|
|||||||
use crate::models::{Material, Mesh};
|
use crate::geometry::Mesh;
|
||||||
|
use crate::assets::mtl::Material;
|
||||||
pub struct ShaderSource {
|
use crate::handles::{ MeshHandle, MaterialHandle };
|
||||||
pub vertex_src: &'static [u8],
|
use crate::assets::types::ObjData;
|
||||||
pub fragment_src: &'static [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Model {
|
|
||||||
pub objects: Vec<GpuMesh>,
|
|
||||||
pub materials: Vec<Material>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GpuMesh {
|
|
||||||
pub vao: gl::types::GLuint,
|
|
||||||
pub vbo: gl::types::GLuint,
|
|
||||||
pub _ebo: Option<gl::types::GLuint>,
|
|
||||||
pub vertex_count: i32,
|
|
||||||
pub _index_count: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ModelData {
|
pub struct ModelData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub meshes: Vec<Mesh>,
|
pub meshes: Vec<Mesh>,
|
||||||
|
pub materials: Vec<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ObjData> for Model {
|
impl From<&ObjData> for ModelData {
|
||||||
fn from(value: &ObjData) -> Self {
|
fn from(obj: &ObjData) -> Self {
|
||||||
|
let meshes = obj.meshes.iter()
|
||||||
|
.map(|obj_mesh| Mesh::new(obj, obj_mesh))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
ModelData {
|
||||||
|
name: String::new(),
|
||||||
|
meshes,
|
||||||
|
materials: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Model {
|
||||||
|
pub name: String,
|
||||||
|
pub parts: Vec<(MeshHandle, MaterialHandle)>,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,200 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::models::{Material, Model};
|
|
||||||
|
|
||||||
pub struct Scene {
|
|
||||||
pub models: Vec<Model>,
|
|
||||||
pub materials: HashMap<String, Material>,
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
use crate::models::{ Vec2, Vec3 };
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Vertex {
|
|
||||||
pub position: Vec3,
|
|
||||||
pub normal: Vec3,
|
|
||||||
pub tex_coord: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct VertexIndex {
|
|
||||||
pub position: u32,
|
|
||||||
pub tex_coord: Option<u32>,
|
|
||||||
pub normal: Option<u32>
|
|
||||||
}
|
|
||||||
@ -1,26 +1,61 @@
|
|||||||
|
pub const VERTEX_SHADER_SOURCE: &str = r#"
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
pub const VERTEX_SHADER_SOURCE: &[u8] = b"
|
layout(location = 0) in vec3 a_position;
|
||||||
#version 100
|
layout(location = 1) in vec3 a_normal;
|
||||||
precision mediump float;
|
layout(location = 2) in vec2 a_texcoord;
|
||||||
|
|
||||||
attribute vec3 position;
|
uniform mat4 u_model;
|
||||||
attribute vec3 color;
|
uniform mat4 u_view;
|
||||||
|
uniform mat4 u_projection;
|
||||||
|
|
||||||
varying vec3 v_color;
|
out vec3 v_position;
|
||||||
|
out vec3 v_normal;
|
||||||
|
out vec2 v_texcoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 1.0);
|
vec4 world_pos = u_model * vec4(a_position, 1.0);
|
||||||
v_color = color;
|
v_position = world_pos.xyz;
|
||||||
}
|
v_normal = mat3(transpose(inverse(u_model))) * a_normal;
|
||||||
\0";
|
v_texcoord = a_texcoord;
|
||||||
|
gl_Position = u_projection * u_view * world_pos;
|
||||||
|
}"#;
|
||||||
|
|
||||||
pub const FRAGMENT_SHADER_SOURCE: &[u8] = b"
|
pub const FRAGMENT_SHADER_SOURCE: &str = r#"
|
||||||
#version 100
|
#version 330 core
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
varying vec3 v_color;
|
in vec3 v_position;
|
||||||
|
in vec3 v_normal;
|
||||||
|
in vec2 v_texcoord;
|
||||||
|
|
||||||
|
uniform vec3 u_light_pos;
|
||||||
|
uniform vec3 u_light_color;
|
||||||
|
uniform vec3 u_view_pos;
|
||||||
|
|
||||||
|
uniform vec3 u_ambient;
|
||||||
|
uniform vec3 u_diffuse;
|
||||||
|
uniform vec3 u_specular;
|
||||||
|
uniform float u_shininess;
|
||||||
|
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_FragColor = vec4(v_color, 1.0);
|
vec3 normal = normalize(v_normal);
|
||||||
}
|
vec3 light_dir = normalize(u_light_pos - v_position);
|
||||||
\0";
|
vec3 view_dir = normalize(u_view_pos - v_position);
|
||||||
|
vec3 halfway = normalize(light_dir + view_dir);
|
||||||
|
|
||||||
|
// Ambient
|
||||||
|
vec3 ambient = u_ambient * 0.1;
|
||||||
|
|
||||||
|
// Diffuse
|
||||||
|
float diff = max(dot(normal, light_dir), 0.0);
|
||||||
|
vec3 diffuse = diff * u_diffuse * u_light_color;
|
||||||
|
|
||||||
|
// Specular (Blinn-Phong)
|
||||||
|
float spec = pow(max(dot(normal, halfway), 0.0), u_shininess);
|
||||||
|
vec3 specular = spec * u_specular * u_light_color;
|
||||||
|
|
||||||
|
vec3 result = ambient + diffuse + specular;
|
||||||
|
frag_color = vec4(result, 1.0);
|
||||||
|
}"#;
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
src/shaders/types.rs
Normal file
5
src/shaders/types.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub struct ShaderSource {}
|
||||||
|
|
||||||
|
pub struct Shader(pub u32);
|
||||||
|
|
||||||
|
pub type ShaderHandle = u32;
|
||||||
Loading…
x
Reference in New Issue
Block a user