From fac31bf3d54b3808917c9c4643c330e6eb2affbc Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Wed, 15 Nov 2023 01:18:38 +0200 Subject: [PATCH] ran cargo fmt --- src/aabb.rs | 98 +++++- src/camera.rs | 20 +- src/imgui-glfw-support/mod.rs | 5 +- src/imgui-opengl-renderer/mod.rs | 559 ++++++++++++++++++------------- src/importer/mod.rs | 41 ++- src/importer/obj.rs | 117 ++++--- src/importer/stl.rs | 62 ++-- src/ipc.rs | 22 +- src/lib.rs | 22 +- src/logger.rs | 12 +- src/main.rs | 183 +++++++--- src/mesh.rs | 91 +++-- src/model.rs | 34 +- src/shader.rs | 101 ++++-- src/ui/ui.rs | 244 ++++++++++---- src/utils.rs | 64 ++-- 16 files changed, 1137 insertions(+), 538 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index 077e4cc..00157a4 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -19,14 +19,46 @@ impl AABB { let mut ebo = 0; let vertices = vec![ - Vertex { position: glm::vec3(min.x, min.y, min.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(max.x, min.y, min.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(max.x, max.y, min.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(min.x, max.y, min.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(min.x, min.y, max.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(max.x, min.y, max.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(max.x, max.y, max.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, - Vertex { position: glm::vec3(min.x, max.y, max.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, + Vertex { + position: glm::vec3(min.x, min.y, min.z), + tex_coords: glm::vec2(0.0, 0.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(max.x, min.y, min.z), + tex_coords: glm::vec2(1.0, 0.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(max.x, max.y, min.z), + tex_coords: glm::vec2(1.0, 1.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(min.x, max.y, min.z), + tex_coords: glm::vec2(0.0, 1.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(min.x, min.y, max.z), + tex_coords: glm::vec2(0.0, 0.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(max.x, min.y, max.z), + tex_coords: glm::vec2(1.0, 0.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(max.x, max.y, max.z), + tex_coords: glm::vec2(1.0, 1.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, + Vertex { + position: glm::vec3(min.x, max.y, max.z), + tex_coords: glm::vec2(0.0, 1.0), + normal: glm::vec3(0.0, 0.0, 0.0), + }, ]; let indices = vec![ @@ -46,22 +78,53 @@ impl AABB { gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); - gl::BufferData(gl::ARRAY_BUFFER, (std::mem::size_of::() * vertices.len() as usize) as isize, vertices.as_ptr() as *const std::ffi::c_void, gl::STATIC_DRAW); + gl::BufferData( + gl::ARRAY_BUFFER, + (std::mem::size_of::() * vertices.len() as usize) as isize, + vertices.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW, + ); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (std::mem::size_of::() * indices.len() as usize) as isize, indices.as_ptr() as *const std::ffi::c_void, gl::STATIC_DRAW); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + (std::mem::size_of::() * indices.len() as usize) as isize, + indices.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW, + ); // vertex positions gl::EnableVertexAttribArray(0); - gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, std::ptr::null()); + gl::VertexAttribPointer( + 0, + 3, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + std::ptr::null(), + ); // vertex normals gl::EnableVertexAttribArray(1); - gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, (3 * std::mem::size_of::()) as *const std::ffi::c_void); + gl::VertexAttribPointer( + 1, + 3, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + (3 * std::mem::size_of::()) as *const std::ffi::c_void, + ); // vertex texture coords gl::EnableVertexAttribArray(2); - gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, (6 * std::mem::size_of::()) as *const std::ffi::c_void); + gl::VertexAttribPointer( + 2, + 2, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + (6 * std::mem::size_of::()) as *const std::ffi::c_void, + ); gl::BindVertexArray(0); } @@ -72,7 +135,7 @@ impl AABB { indices_len: indices.len() as u32, vao, vbo, - ebo + ebo, } } @@ -88,7 +151,12 @@ impl AABB { gl::BindVertexArray(self.vao); gl::LineWidth(5.0); gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); - gl::DrawElements(gl::TRIANGLES, self.indices_len as i32, gl::UNSIGNED_INT, std::ptr::null()); + gl::DrawElements( + gl::TRIANGLES, + self.indices_len as i32, + gl::UNSIGNED_INT, + std::ptr::null(), + ); // reset stuff to default gl::BindVertexArray(0); diff --git a/src/camera.rs b/src/camera.rs index 35b5178..223eee6 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -30,7 +30,9 @@ impl Camera { } pub fn handle_mouse_scroll(&mut self, yoffset: f32, can_capture_cursor: bool, fov_zoom: bool) { - if !can_capture_cursor { return } + if !can_capture_cursor { + return; + } if fov_zoom { self.fov -= yoffset; @@ -41,7 +43,8 @@ impl Camera { self.fov = 85.0; } } else { - self.position = self.position + (self.front * self._speed) + glm::vec3(0.0, 0.0, -yoffset); + self.position = + self.position + (self.front * self._speed) + glm::vec3(0.0, 0.0, -yoffset); } } @@ -56,13 +59,19 @@ impl Camera { self._speed = self.speed * delta_time; } - pub fn focus_on_selected_model(&mut self, active_model: Option, objects: &Vec) { + pub fn focus_on_selected_model( + &mut self, + active_model: Option, + objects: &Vec, + ) { if let Some(id) = active_model { for obj in objects { if obj.id == id { // we scale the center of the object since the model (and therefore the AABB) is scaled - let center_x = ((obj.aabb.max.x / 2.0) + (obj.aabb.min.x / 2.0)) * obj.scaling_factor; - let center_y = ((obj.aabb.max.y / 2.0) + (obj.aabb.min.y / 2.0)) * obj.scaling_factor; + let center_x = + ((obj.aabb.max.x / 2.0) + (obj.aabb.min.x / 2.0)) * obj.scaling_factor; + let center_y = + ((obj.aabb.max.y / 2.0) + (obj.aabb.min.y / 2.0)) * obj.scaling_factor; let z = obj.aabb.max.z * obj.scaling_factor + 10.0; self.position = glm::vec3(center_x, center_y, z); self.front = glm::vec3(0.0, 0.0, -1.0); @@ -72,4 +81,3 @@ impl Camera { } } } - diff --git a/src/imgui-glfw-support/mod.rs b/src/imgui-glfw-support/mod.rs index bcda099..26552fb 100644 --- a/src/imgui-glfw-support/mod.rs +++ b/src/imgui-glfw-support/mod.rs @@ -54,7 +54,10 @@ struct Clipboard { impl imgui::ClipboardBackend for Clipboard { fn set(&mut self, s: &str) { unsafe { - glfw::ffi::glfwSetClipboardString(self.window_ptr, s.as_ptr() as *const std::ffi::c_char); + glfw::ffi::glfwSetClipboardString( + self.window_ptr, + s.as_ptr() as *const std::ffi::c_char, + ); } } fn get(&mut self) -> std::option::Option { diff --git a/src/imgui-opengl-renderer/mod.rs b/src/imgui-opengl-renderer/mod.rs index 73aa319..0adac6c 100644 --- a/src/imgui-opengl-renderer/mod.rs +++ b/src/imgui-opengl-renderer/mod.rs @@ -6,34 +6,30 @@ use std::mem; use glad_gl::gl; pub struct Renderer { - program: gl::GLuint, - locs: Locs, - vbo: gl::GLuint, - ebo: gl::GLuint, - font_texture: gl::GLuint, + program: gl::GLuint, + locs: Locs, + vbo: gl::GLuint, + ebo: gl::GLuint, + font_texture: gl::GLuint, } struct Locs { - texture: gl::GLint, - proj_mtx: gl::GLint, - position: gl::GLuint, - uv: gl::GLuint, - color: gl::GLuint, + texture: gl::GLint, + proj_mtx: gl::GLint, + position: gl::GLuint, + uv: gl::GLuint, + color: gl::GLuint, } impl Renderer { - pub fn new( - imgui: &mut Context, - ) -> Self - { + pub fn new(imgui: &mut Context) -> Self { + unsafe { + #[cfg(target_os = "macos")] + let glsl_version = b"#version 150\n\0"; + #[cfg(not(target_os = "macos"))] + let glsl_version = b"#version 130\n\0"; - unsafe { - #[cfg(target_os = "macos")] - let glsl_version = b"#version 150\n\0"; - #[cfg(not(target_os = "macos"))] - let glsl_version = b"#version 130\n\0"; - - let vert_source = b" + let vert_source = b" uniform mat4 ProjMtx; in vec2 Position; in vec2 UV; @@ -48,7 +44,7 @@ impl Renderer { } \0"; - let frag_source = b" + let frag_source = b" uniform sampler2D Texture; in vec2 Frag_UV; in vec4 Frag_Color; @@ -59,236 +55,341 @@ impl Renderer { } \0"; - let vert_sources = [glsl_version.as_ptr() as *const gl::GLchar, - vert_source.as_ptr() as *const gl::GLchar]; - let vert_sources_len = [glsl_version.len() as gl::GLint - 1, - vert_source.len() as gl::GLint - 1]; - let frag_sources = [glsl_version.as_ptr() as *const gl::GLchar, - frag_source.as_ptr() as *const gl::GLchar]; - let frag_sources_len = [glsl_version.len() as gl::GLint - 1, - frag_source.len() as gl::GLint - 1]; + let vert_sources = [ + glsl_version.as_ptr() as *const gl::GLchar, + vert_source.as_ptr() as *const gl::GLchar, + ]; + let vert_sources_len = [ + glsl_version.len() as gl::GLint - 1, + vert_source.len() as gl::GLint - 1, + ]; + let frag_sources = [ + glsl_version.as_ptr() as *const gl::GLchar, + frag_source.as_ptr() as *const gl::GLchar, + ]; + let frag_sources_len = [ + glsl_version.len() as gl::GLint - 1, + frag_source.len() as gl::GLint - 1, + ]; - let program = gl::CreateProgram(); - let vert_shader = gl::CreateShader(gl::VERTEX_SHADER); - let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER); - gl::ShaderSource(vert_shader, 2, vert_sources.as_ptr(), vert_sources_len.as_ptr()); - gl::ShaderSource(frag_shader, 2, frag_sources.as_ptr(), frag_sources_len.as_ptr()); - gl::CompileShader(vert_shader); - gl::CompileShader(frag_shader); - gl::AttachShader(program, vert_shader); - gl::AttachShader(program, frag_shader); - gl::LinkProgram(program); - gl::DeleteShader(vert_shader); - gl::DeleteShader(frag_shader); + let program = gl::CreateProgram(); + let vert_shader = gl::CreateShader(gl::VERTEX_SHADER); + let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER); + gl::ShaderSource( + vert_shader, + 2, + vert_sources.as_ptr(), + vert_sources_len.as_ptr(), + ); + gl::ShaderSource( + frag_shader, + 2, + frag_sources.as_ptr(), + frag_sources_len.as_ptr(), + ); + gl::CompileShader(vert_shader); + gl::CompileShader(frag_shader); + gl::AttachShader(program, vert_shader); + gl::AttachShader(program, frag_shader); + gl::LinkProgram(program); + gl::DeleteShader(vert_shader); + gl::DeleteShader(frag_shader); - let locs = Locs{ - texture: gl::GetUniformLocation(program, b"Texture\0".as_ptr() as _), - proj_mtx: gl::GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _), - position: gl::GetAttribLocation(program, b"Position\0".as_ptr() as _) as _, - uv: gl::GetAttribLocation(program, b"UV\0".as_ptr() as _) as _, - color: gl::GetAttribLocation(program, b"Color\0".as_ptr() as _) as _, - }; + let locs = Locs { + texture: gl::GetUniformLocation(program, b"Texture\0".as_ptr() as _), + proj_mtx: gl::GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _), + position: gl::GetAttribLocation(program, b"Position\0".as_ptr() as _) as _, + uv: gl::GetAttribLocation(program, b"UV\0".as_ptr() as _) as _, + color: gl::GetAttribLocation(program, b"Color\0".as_ptr() as _) as _, + }; - let vbo = return_param(|x| gl::GenBuffers(1, x) ); - let ebo = return_param(|x| gl::GenBuffers(1, x) ); + let vbo = return_param(|x| gl::GenBuffers(1, x)); + let ebo = return_param(|x| gl::GenBuffers(1, x)); - let mut current_texture = 0; - gl::GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture); + let mut current_texture = 0; + gl::GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture); + let font_texture = return_param(|x| gl::GenTextures(1, x)); + gl::BindTexture(gl::TEXTURE_2D, font_texture); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _); + gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _); + gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0); - let font_texture = return_param(|x| gl::GenTextures(1, x)); - gl::BindTexture(gl::TEXTURE_2D, font_texture); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _); - gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0); + { + let atlas = imgui.fonts(); - { - let atlas = imgui.fonts(); + let texture = atlas.build_rgba32_texture(); + gl::TexImage2D( + gl::TEXTURE_2D, + 0, + gl::RGBA as _, + texture.width as _, + texture.height as _, + 0, + gl::RGBA, + gl::UNSIGNED_BYTE, + texture.data.as_ptr() as _, + ); - let texture = atlas.build_rgba32_texture(); - gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as _, texture.width as _, texture.height as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, texture.data.as_ptr() as _); - - atlas.tex_id = (font_texture as usize).into(); - } - - gl::BindTexture(gl::TEXTURE_2D, current_texture as _); - - Self{ - program, - locs, - vbo, - ebo, - font_texture, - } - } - } - - pub fn render( - &self, - ctx: &mut Context, - ) { - use imgui::{DrawVert,DrawIdx,DrawCmd,DrawCmdParams}; - - unsafe { - let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x)); - gl::ActiveTexture(gl::TEXTURE0); - let last_program = return_param(|x| gl::GetIntegerv(gl::CURRENT_PROGRAM, x)); - let last_texture = return_param(|x| gl::GetIntegerv(gl::TEXTURE_BINDING_2D, x)); - let last_sampler = return_param(|x| gl::GetIntegerv(gl::SAMPLER_BINDING, x)); - let last_array_buffer = return_param(|x| gl::GetIntegerv(gl::ARRAY_BUFFER_BINDING, x)); - let last_element_array_buffer = return_param(|x| gl::GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x)); - let last_vertex_array = return_param(|x| gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, x)); - let last_polygon_mode = return_param(|x: &mut [gl::GLint; 2]| gl::GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr())); - let last_viewport = return_param(|x: &mut [gl::GLint; 4]| gl::GetIntegerv(gl::VIEWPORT, x.as_mut_ptr())); - let last_scissor_box = return_param(|x: &mut [gl::GLint; 4]| gl::GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr())); - let last_blend_src_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_RGB, x)); - let last_blend_dst_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_RGB, x)); - let last_blend_src_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_ALPHA, x)); - let last_blend_dst_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_ALPHA, x)); - let last_blend_equation_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_RGB, x)); - let last_blend_equation_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, x)); - let last_enable_blend = gl::IsEnabled(gl::BLEND) == gl::TRUE; - let last_enable_cull_face = gl::IsEnabled(gl::CULL_FACE) == gl::TRUE; - let last_enable_depth_test = gl::IsEnabled(gl::DEPTH_TEST) == gl::TRUE; - let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE; - - - gl::Enable(gl::BLEND); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - gl::Disable(gl::CULL_FACE); - gl::Disable(gl::DEPTH_TEST); - gl::Enable(gl::SCISSOR_TEST); - gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); - - - let [width, height] = ctx.io().display_size; - let [scale_w, scale_h] = ctx.io().display_framebuffer_scale; - - let fb_width = width * scale_w; - let fb_height = height * scale_h; - - gl::Viewport(0, 0, fb_width as _, fb_height as _); - let matrix = [ - [ 2.0 / width as f32, 0.0, 0.0, 0.0], - [ 0.0, 2.0 / -(height as f32), 0.0, 0.0], - [ 0.0, 0.0, -1.0, 0.0], - [-1.0, 1.0, 0.0, 1.0], - ]; - gl::UseProgram(self.program); - gl::Uniform1i(self.locs.texture, 0); - gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _); - gl::BindSampler(0, 0); - - - let vao = return_param(|x| gl::GenVertexArrays(1, x)); - gl::BindVertexArray(vao); - gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); - gl::EnableVertexAttribArray(self.locs.position); - gl::EnableVertexAttribArray(self.locs.uv); - gl::EnableVertexAttribArray(self.locs.color); - gl::VertexAttribPointer(self.locs.position, 2, gl::FLOAT, gl::FALSE, mem::size_of::() as _, field_offset::(|v| &v.pos) as _); - gl::VertexAttribPointer(self.locs.uv, 2, gl::FLOAT, gl::FALSE, mem::size_of::() as _, field_offset::(|v| &v.uv) as _); - gl::VertexAttribPointer(self.locs.color, 4, gl::UNSIGNED_BYTE, gl::TRUE, mem::size_of::() as _, field_offset::(|v| &v.col) as _); - - - let draw_data = ctx.render(); - - for draw_list in draw_data.draw_lists() { - let vtx_buffer = draw_list.vtx_buffer(); - let idx_buffer = draw_list.idx_buffer(); - - gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); - gl::BufferData(gl::ARRAY_BUFFER, (vtx_buffer.len() * mem::size_of::()) as _, vtx_buffer.as_ptr() as _, gl::STREAM_DRAW); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (idx_buffer.len() * mem::size_of::()) as _, idx_buffer.as_ptr() as _, gl::STREAM_DRAW); - - for cmd in draw_list.commands() { - match cmd { - DrawCmd::Elements { - count, - cmd_params: DrawCmdParams { - clip_rect: [x, y, z, w], - texture_id, - idx_offset, - .. - }, - } => { - gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _); - - gl::Scissor((x * scale_w) as gl::GLint, - (fb_height - w * scale_h) as gl::GLint, - ((z - x) * scale_w) as gl::GLint, - ((w - y) * scale_h) as gl::GLint); - - let idx_size = if mem::size_of::() == 2 { gl::UNSIGNED_SHORT } else { gl::UNSIGNED_INT }; - - gl::DrawElements(gl::TRIANGLES, count as _, idx_size, (idx_offset * mem::size_of::()) as _); - }, - DrawCmd::ResetRenderState => { - unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet"); - }, - DrawCmd::RawCallback { .. } => { - unimplemented!("Haven't implemented user callbacks yet"); + atlas.tex_id = (font_texture as usize).into(); + } + + gl::BindTexture(gl::TEXTURE_2D, current_texture as _); + + Self { + program, + locs, + vbo, + ebo, + font_texture, } - } } - } - - gl::DeleteVertexArrays(1, &vao); - - gl::UseProgram(last_program as _); - gl::BindTexture(gl::TEXTURE_2D, last_texture as _); - gl::BindSampler(0, last_sampler as _); - gl::ActiveTexture(last_active_texture as _); - gl::BindVertexArray(last_vertex_array as _); - gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _); - gl::BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _); - gl::BlendFuncSeparate(last_blend_src_rgb as _, last_blend_dst_rgb as _, last_blend_src_alpha as _, last_blend_dst_alpha as _); - if last_enable_blend { gl::Enable(gl::BLEND) } else { gl::Disable(gl::BLEND) }; - if last_enable_cull_face { gl::Enable(gl::CULL_FACE) } else { gl::Disable(gl::CULL_FACE) }; - if last_enable_depth_test { gl::Enable(gl::DEPTH_TEST) } else { gl::Disable(gl::DEPTH_TEST) }; - if last_enable_scissor_test { gl::Enable(gl::SCISSOR_TEST) } else { gl::Disable(gl::SCISSOR_TEST) }; - gl::PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _); - gl::Viewport(last_viewport[0] as _, last_viewport[1] as _, last_viewport[2] as _, last_viewport[3] as _); - gl::Scissor(last_scissor_box[0] as _, last_scissor_box[1] as _, last_scissor_box[2] as _, last_scissor_box[3] as _); - } - } + + pub fn render(&self, ctx: &mut Context) { + use imgui::{DrawCmd, DrawCmdParams, DrawIdx, DrawVert}; + + unsafe { + let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x)); + gl::ActiveTexture(gl::TEXTURE0); + let last_program = return_param(|x| gl::GetIntegerv(gl::CURRENT_PROGRAM, x)); + let last_texture = return_param(|x| gl::GetIntegerv(gl::TEXTURE_BINDING_2D, x)); + let last_sampler = return_param(|x| gl::GetIntegerv(gl::SAMPLER_BINDING, x)); + let last_array_buffer = return_param(|x| gl::GetIntegerv(gl::ARRAY_BUFFER_BINDING, x)); + let last_element_array_buffer = + return_param(|x| gl::GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x)); + let last_vertex_array = return_param(|x| gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, x)); + let last_polygon_mode = return_param(|x: &mut [gl::GLint; 2]| { + gl::GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr()) + }); + let last_viewport = return_param(|x: &mut [gl::GLint; 4]| { + gl::GetIntegerv(gl::VIEWPORT, x.as_mut_ptr()) + }); + let last_scissor_box = return_param(|x: &mut [gl::GLint; 4]| { + gl::GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr()) + }); + let last_blend_src_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_RGB, x)); + let last_blend_dst_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_RGB, x)); + let last_blend_src_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_ALPHA, x)); + let last_blend_dst_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_ALPHA, x)); + let last_blend_equation_rgb = + return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_RGB, x)); + let last_blend_equation_alpha = + return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, x)); + let last_enable_blend = gl::IsEnabled(gl::BLEND) == gl::TRUE; + let last_enable_cull_face = gl::IsEnabled(gl::CULL_FACE) == gl::TRUE; + let last_enable_depth_test = gl::IsEnabled(gl::DEPTH_TEST) == gl::TRUE; + let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE; + + gl::Enable(gl::BLEND); + gl::BlendEquation(gl::FUNC_ADD); + gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + gl::Disable(gl::CULL_FACE); + gl::Disable(gl::DEPTH_TEST); + gl::Enable(gl::SCISSOR_TEST); + gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); + + let [width, height] = ctx.io().display_size; + let [scale_w, scale_h] = ctx.io().display_framebuffer_scale; + + let fb_width = width * scale_w; + let fb_height = height * scale_h; + + gl::Viewport(0, 0, fb_width as _, fb_height as _); + let matrix = [ + [2.0 / width as f32, 0.0, 0.0, 0.0], + [0.0, 2.0 / -(height as f32), 0.0, 0.0], + [0.0, 0.0, -1.0, 0.0], + [-1.0, 1.0, 0.0, 1.0], + ]; + gl::UseProgram(self.program); + gl::Uniform1i(self.locs.texture, 0); + gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _); + gl::BindSampler(0, 0); + + let vao = return_param(|x| gl::GenVertexArrays(1, x)); + gl::BindVertexArray(vao); + gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); + gl::EnableVertexAttribArray(self.locs.position); + gl::EnableVertexAttribArray(self.locs.uv); + gl::EnableVertexAttribArray(self.locs.color); + gl::VertexAttribPointer( + self.locs.position, + 2, + gl::FLOAT, + gl::FALSE, + mem::size_of::() as _, + field_offset::(|v| &v.pos) as _, + ); + gl::VertexAttribPointer( + self.locs.uv, + 2, + gl::FLOAT, + gl::FALSE, + mem::size_of::() as _, + field_offset::(|v| &v.uv) as _, + ); + gl::VertexAttribPointer( + self.locs.color, + 4, + gl::UNSIGNED_BYTE, + gl::TRUE, + mem::size_of::() as _, + field_offset::(|v| &v.col) as _, + ); + + let draw_data = ctx.render(); + + for draw_list in draw_data.draw_lists() { + let vtx_buffer = draw_list.vtx_buffer(); + let idx_buffer = draw_list.idx_buffer(); + + gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); + gl::BufferData( + gl::ARRAY_BUFFER, + (vtx_buffer.len() * mem::size_of::()) as _, + vtx_buffer.as_ptr() as _, + gl::STREAM_DRAW, + ); + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + (idx_buffer.len() * mem::size_of::()) as _, + idx_buffer.as_ptr() as _, + gl::STREAM_DRAW, + ); + + for cmd in draw_list.commands() { + match cmd { + DrawCmd::Elements { + count, + cmd_params: + DrawCmdParams { + clip_rect: [x, y, z, w], + texture_id, + idx_offset, + .. + }, + } => { + gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _); + + gl::Scissor( + (x * scale_w) as gl::GLint, + (fb_height - w * scale_h) as gl::GLint, + ((z - x) * scale_w) as gl::GLint, + ((w - y) * scale_h) as gl::GLint, + ); + + let idx_size = if mem::size_of::() == 2 { + gl::UNSIGNED_SHORT + } else { + gl::UNSIGNED_INT + }; + + gl::DrawElements( + gl::TRIANGLES, + count as _, + idx_size, + (idx_offset * mem::size_of::()) as _, + ); + } + DrawCmd::ResetRenderState => { + unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet"); + } + DrawCmd::RawCallback { .. } => { + unimplemented!("Haven't implemented user callbacks yet"); + } + } + } + } + + gl::DeleteVertexArrays(1, &vao); + + gl::UseProgram(last_program as _); + gl::BindTexture(gl::TEXTURE_2D, last_texture as _); + gl::BindSampler(0, last_sampler as _); + gl::ActiveTexture(last_active_texture as _); + gl::BindVertexArray(last_vertex_array as _); + gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _); + gl::BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _); + gl::BlendFuncSeparate( + last_blend_src_rgb as _, + last_blend_dst_rgb as _, + last_blend_src_alpha as _, + last_blend_dst_alpha as _, + ); + if last_enable_blend { + gl::Enable(gl::BLEND) + } else { + gl::Disable(gl::BLEND) + }; + if last_enable_cull_face { + gl::Enable(gl::CULL_FACE) + } else { + gl::Disable(gl::CULL_FACE) + }; + if last_enable_depth_test { + gl::Enable(gl::DEPTH_TEST) + } else { + gl::Disable(gl::DEPTH_TEST) + }; + if last_enable_scissor_test { + gl::Enable(gl::SCISSOR_TEST) + } else { + gl::Disable(gl::SCISSOR_TEST) + }; + gl::PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _); + gl::Viewport( + last_viewport[0] as _, + last_viewport[1] as _, + last_viewport[2] as _, + last_viewport[3] as _, + ); + gl::Scissor( + last_scissor_box[0] as _, + last_scissor_box[1] as _, + last_scissor_box[2] as _, + last_scissor_box[3] as _, + ); + } + } } impl Drop for Renderer { - fn drop(&mut self) { - unsafe { - gl::DeleteBuffers(1, &self.vbo); - gl::DeleteBuffers(1, &self.ebo); + fn drop(&mut self) { + unsafe { + gl::DeleteBuffers(1, &self.vbo); + gl::DeleteBuffers(1, &self.ebo); - gl::DeleteProgram(self.program); + gl::DeleteProgram(self.program); - gl::DeleteTextures(1, &self.font_texture); + gl::DeleteTextures(1, &self.font_texture); + } } - } } fn field_offset FnOnce(&'a T) -> &'a U>(f: F) -> usize { - unsafe { - let instance = mem::zeroed::(); + unsafe { + let instance = mem::zeroed::(); - let offset = { - let field: &U = f(&instance); - field as *const U as usize - &instance as *const T as usize - }; + let offset = { + let field: &U = f(&instance); + field as *const U as usize - &instance as *const T as usize + }; - mem::forget(instance); + mem::forget(instance); - offset - } + offset + } } -fn return_param(f: F) -> T where F: FnOnce(&mut T) { - let mut val = unsafe{ mem::zeroed() }; - f(&mut val); - val +fn return_param(f: F) -> T +where + F: FnOnce(&mut T), +{ + let mut val = unsafe { mem::zeroed() }; + f(&mut val); + val } diff --git a/src/importer/mod.rs b/src/importer/mod.rs index a11fce2..b2b2ee4 100644 --- a/src/importer/mod.rs +++ b/src/importer/mod.rs @@ -3,7 +3,11 @@ mod stl; use std::path::PathBuf; -use crate::{mesh::Vertex, aabb::AABB, utils::{SupportedFileExtensions, self}}; +use crate::{ + aabb::AABB, + mesh::Vertex, + utils::{self, SupportedFileExtensions}, +}; #[derive(Debug, Clone)] pub enum TextureType { @@ -15,7 +19,7 @@ pub enum TextureType { Displacement, Decal, Reflection, - Emissive + Emissive, } impl TextureType { @@ -30,7 +34,7 @@ impl TextureType { "decal" => Some(TextureType::Decal), "refl" => Some(TextureType::Reflection), "map_Ke" => Some(TextureType::Emissive), - _ => None + _ => None, } } } @@ -43,11 +47,19 @@ pub struct Material { pub specular_color: glm::Vec3, pub specular_exponent: f32, pub opacity: f32, - pub textures: Vec + pub textures: Vec, } impl Material { - fn new(name: String, ambient: glm::Vec3, diffuse: glm::Vec3, specular: glm::Vec3, shininess: f32, opacity: f32, textures: Vec) -> Self { + fn new( + name: String, + ambient: glm::Vec3, + diffuse: glm::Vec3, + specular: glm::Vec3, + shininess: f32, + opacity: f32, + textures: Vec, + ) -> Self { Self { name, ambient_color: ambient, @@ -55,7 +67,7 @@ impl Material { specular_color: specular, specular_exponent: shininess, opacity, - textures + textures, } } } @@ -69,7 +81,7 @@ impl Default for Material { specular_color: glm::vec3(0.1, 0.1, 0.1), specular_exponent: 32.0, opacity: 1.0, - textures: Vec::new() + textures: Vec::new(), } } } @@ -96,17 +108,16 @@ pub struct Texture { } impl Texture { - pub fn new(path: std::path::PathBuf, typ: TextureType) -> Result> { + pub fn new( + path: std::path::PathBuf, + typ: TextureType, + ) -> Result> { let id = utils::load_texture(path)?; - Ok(Texture { - id, - typ, - }) + Ok(Texture { id, typ }) } } - #[derive(Debug)] pub struct ObjMesh { pub name: String, @@ -125,11 +136,11 @@ pub struct Object { pub fn load_from_file(path: &PathBuf) -> Result> { let path_str = match path.to_str() { Some(s) => s, - None => return Err("Failed to convert path to string".into()) + None => return Err("Failed to convert path to string".into()), }; let file = std::fs::File::open(path_str)?; - // TODO: if no extension, then test for binary STL magic bytes + // TODO: if no extension, then test for binary STL magic bytes // if no magic bytes, then try to guess based on the first line of text in the file let obj = match SupportedFileExtensions::from_str(path.extension().unwrap().to_str().unwrap()) { diff --git a/src/importer/obj.rs b/src/importer/obj.rs index 537c058..a44d00b 100644 --- a/src/importer/obj.rs +++ b/src/importer/obj.rs @@ -1,8 +1,16 @@ -use std::{io::{BufReader, BufRead}, collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + io::{BufRead, BufReader}, + path::PathBuf, +}; -use log::{error, warn, trace}; +use log::{error, trace, warn}; -use crate::{mesh::Vertex, aabb::AABB, importer::{ObjMesh, Object, Material, Texture, TextureType}}; +use crate::{ + aabb::AABB, + importer::{Material, ObjMesh, Object, Texture, TextureType}, + mesh::Vertex, +}; const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes @@ -86,7 +94,10 @@ impl MtlToken { } } -fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap) -> Result, Box> { +fn parse_mtl( + path: &PathBuf, + obj_textures: &mut HashMap, +) -> Result, Box> { let file = std::fs::File::open(path)?; let reader = BufReader::with_capacity(BUF_CAP, file); let mut material_name = String::new(); @@ -112,7 +123,15 @@ fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap) -> Res match MtlToken::from_str(token) { Some(MtlToken::NewMaterial) => { if !material_name.is_empty() { - material = Material::new(material_name.clone(), ambient, diffuse, specular, shininess, opacity, mat_textures.clone()); + material = Material::new( + material_name.clone(), + ambient, + diffuse, + specular, + shininess, + opacity, + mat_textures.clone(), + ); materials.insert(material_name, material); mat_textures.clear(); @@ -176,19 +195,32 @@ fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap) -> Res mat_textures.push(tex); } - _ => { warn!("Unhandled material token: {}", token) }, + _ => { + warn!("Unhandled material token: {}", token) + } } } } - material = Material::new(material_name.clone(), ambient, diffuse, specular, shininess, opacity, mat_textures); + material = Material::new( + material_name.clone(), + ambient, + diffuse, + specular, + shininess, + opacity, + mat_textures, + ); materials.insert(material_name, material); Ok(materials) } -pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result> { +pub fn load_obj( + obj_path: &PathBuf, + file: std::fs::File, +) -> Result> { let now = std::time::Instant::now(); let reader = BufReader::with_capacity(BUF_CAP, file); let mut object_name = String::new(); @@ -226,11 +258,11 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result Result Result { let vec = iter.collect::>(); if vec.len() < 3 { - return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData, "Incomplete vertex data"))); + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Incomplete vertex data", + ))); } - let mut iter = vec.iter() - .take(3) - .map(|i| i.parse::().unwrap()); + let mut iter = vec.iter().take(3).map(|i| i.parse::().unwrap()); let x = iter.next().unwrap(); let y = iter.next().unwrap(); let z = iter.next().unwrap(); @@ -277,26 +310,24 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result { let vec = iter.collect::>(); if vec.len() < 3 { - return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData, "Incomplete vertex normal data"))); + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Incomplete vertex normal data", + ))); } - let mut iter = vec.iter() - .take(3) - .map(|i| i.parse::().unwrap()); + let mut iter = vec.iter().take(3).map(|i| i.parse::().unwrap()); let x = iter.next().unwrap(); let y = iter.next().unwrap(); let z = iter.next().unwrap(); normals.push(glm::vec3(x, y, z)); } Some(ObjToken::TexCoord) => { - let mut iter = iter - .take(2) - .map(|i| i.parse::().unwrap()); + let mut iter = iter.take(2).map(|i| i.parse::().unwrap()); let u = iter.next().unwrap(); let v = iter.next().unwrap(); // vertically flip the texcoords because flipping the texture is expensive @@ -312,8 +343,10 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result().unwrap() as usize - 1] - temp_vertices[part0.parse::().unwrap() as usize - 1], - temp_vertices[part2.parse::().unwrap() as usize - 1] - temp_vertices[part0.parse::().unwrap() as usize - 1] + temp_vertices[part1.parse::().unwrap() as usize - 1] + - temp_vertices[part0.parse::().unwrap() as usize - 1], + temp_vertices[part2.parse::().unwrap() as usize - 1] + - temp_vertices[part0.parse::().unwrap() as usize - 1], )); } @@ -332,10 +365,10 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result Result Result().unwrap(); @@ -388,7 +421,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result Result { materials.extend(m); - }, + } Err(e) => { error!("Failed to parse mtl file {:?}: {}", material_path, e); } @@ -431,11 +464,11 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result Result { // idc about this } - _ => { warn!("Unhandled obj token: {}", token) }, + _ => { + warn!("Unhandled obj token: {}", token) + } } } } let elapsed = now.elapsed(); - trace!("Loaded in {}ms", elapsed.as_millis()); + trace!("Loaded in {}ms", elapsed.as_millis()); let mesh_name = { if current_mesh_name.is_empty() && !object_name.is_empty() { @@ -469,16 +504,16 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result { @@ -40,13 +40,23 @@ impl Iterator for FacetIterator { Ok(0) => return None, // Reached EOF Ok(_) => { if line.contains("normal") { - let mut norm_iter = line.split_whitespace().skip(2).map(|s| s.parse::().unwrap()); + let mut norm_iter = line + .split_whitespace() + .skip(2) + .map(|s| s.parse::().unwrap()); normal.x = norm_iter.next().unwrap(); normal.y = norm_iter.next().unwrap(); normal.z = norm_iter.next().unwrap(); } else if line.contains("vertex") { - let mut vertex_iter = line.split_whitespace().skip(1).map(|s| s.parse::().unwrap()); - vertices.push(glm::vec3(vertex_iter.next().unwrap(), vertex_iter.next().unwrap(), vertex_iter.next().unwrap())); + let mut vertex_iter = line + .split_whitespace() + .skip(1) + .map(|s| s.parse::().unwrap()); + vertices.push(glm::vec3( + vertex_iter.next().unwrap(), + vertex_iter.next().unwrap(), + vertex_iter.next().unwrap(), + )); } else if line.contains("endfacet") { break; } @@ -59,10 +69,14 @@ impl Iterator for FacetIterator { let mut verts_iter = vertices.iter(); - Some(STLTriangle{ + Some(STLTriangle { normal, - verts: [*verts_iter.next().unwrap(), *verts_iter.next().unwrap(), *verts_iter.next().unwrap()], - attribute_byte_count: 0 + verts: [ + *verts_iter.next().unwrap(), + *verts_iter.next().unwrap(), + *verts_iter.next().unwrap(), + ], + attribute_byte_count: 0, }) } } @@ -81,7 +95,7 @@ fn parse_ascii_stl(file: std::fs::File) -> Result Result { @@ -149,10 +163,10 @@ impl Iterator for TrianglesIter { ]; let abc = u16::from_le_bytes([self.buf[48], self.buf[49]]); - Some(STLTriangle{ + Some(STLTriangle { verts, normal, - attribute_byte_count: abc + attribute_byte_count: abc, }) } } @@ -179,7 +193,7 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result Result bool { for b in buf { diff --git a/src/ipc.rs b/src/ipc.rs index 6cae511..6aa6c1f 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,4 +1,12 @@ -use std::{os::unix::net::{UnixListener, UnixStream}, path::PathBuf, io::{Write, Read}, fs::File, process::exit, thread, sync::mpsc::Receiver}; +use std::{ + fs::File, + io::{Read, Write}, + os::unix::net::{UnixListener, UnixStream}, + path::PathBuf, + process::exit, + sync::mpsc::Receiver, + thread, +}; use fs4::FileExt; use log::error; @@ -19,12 +27,14 @@ fn send_args_to_existing_instance(pipe_path: PathBuf, args_paths: Vec) let data = bincode::serialize(&args_paths).expect("Failed to serialize arguments"); // Send the arguments to the first instance. - stream - .write_all(&data) - .expect("Failed to send arguments"); + stream.write_all(&data).expect("Failed to send arguments"); } -pub fn init(lock_file: &File, args_paths: Vec, one_instance: bool) -> Option>> { +pub fn init( + lock_file: &File, + args_paths: Vec, + one_instance: bool, +) -> Option>> { if !one_instance { return None; } @@ -34,7 +44,7 @@ pub fn init(lock_file: &File, args_paths: Vec, one_instance: bool) -> O let pipe_path = std::env::temp_dir().join(pipe_name); match lock_file.try_lock_exclusive() { - Ok(_) => {}, + Ok(_) => {} Err(_) => { println!("An instance of the program is already running"); send_args_to_existing_instance(pipe_path, args_paths); diff --git a/src/lib.rs b/src/lib.rs index e941393..07655c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,14 @@ -pub mod shader; -pub mod camera; -#[path="imgui-glfw-support/mod.rs"] -pub mod imgui_glfw_support; -#[path="imgui-opengl-renderer/mod.rs"] -pub mod imgui_opengl_renderer; -pub mod utils; -pub mod mesh; -pub mod model; -pub mod ui; -pub mod logger; pub mod aabb; +pub mod camera; +#[path = "imgui-glfw-support/mod.rs"] +pub mod imgui_glfw_support; +#[path = "imgui-opengl-renderer/mod.rs"] +pub mod imgui_opengl_renderer; pub mod importer; pub mod ipc; +pub mod logger; +pub mod mesh; +pub mod model; +pub mod shader; +pub mod ui; +pub mod utils; diff --git a/src/logger.rs b/src/logger.rs index a0919d3..736ac38 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,4 +1,7 @@ -use std::{io::Write, sync::{Arc, RwLock}}; +use std::{ + io::Write, + sync::{Arc, RwLock}, +}; #[derive(Copy, Clone)] pub enum LogLevel { @@ -63,12 +66,15 @@ impl Log { #[derive(Clone)] pub struct WritableLog { pub arc: Arc>, - buf: String + buf: String, } impl Default for WritableLog { fn default() -> Self { - Self{arc: Arc::new(RwLock::new(Log::default())), buf: String::with_capacity(1024)} + Self { + arc: Arc::new(RwLock::new(Log::default())), + buf: String::with_capacity(1024), + } } } diff --git a/src/main.rs b/src/main.rs index 002d12e..c5e66fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ -use std::fs::File; use std::env; +use std::fs::File; use std::path::PathBuf; -use glfw::{Action, Context, Key, Modifiers}; -use glad_gl::gl; use anyhow; +use glad_gl::gl; +use glfw::{Action, Context, Key, Modifiers}; use simplelog::*; -use threedobs::{shader, ui::ui, utils, ipc}; +use threedobs::{ipc, shader, ui::ui, utils}; fn main() -> anyhow::Result<(), Box> { let logger = threedobs::logger::WritableLog::default(); @@ -36,13 +36,17 @@ fn main() -> anyhow::Result<(), Box> { LevelFilter::Info }; - CombinedLogger::init( - vec![ - TermLogger::new(log_level, log_conf.clone(), TerminalMode::Mixed, ColorChoice::Auto), + CombinedLogger::init(vec![ + TermLogger::new( + log_level, + log_conf.clone(), + TerminalMode::Mixed, + ColorChoice::Auto, + ), WriteLogger::new(log_level, log_conf, File::create("3dobs.log")?), - WriteLogger::new(log_level, in_program_log_conf, logger.clone()) - ] - ).unwrap(); + WriteLogger::new(log_level, in_program_log_conf, logger.clone()), + ]) + .unwrap(); let settings: ui::Settings = confy::load("3dobs", "settings")?; @@ -64,13 +68,15 @@ fn main() -> anyhow::Result<(), Box> { glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core); glfw::WindowHint::OpenGlForwardCompat(true); - let (mut window, events) = glfw.create_window(1200, 800, "3dobs", glfw::WindowMode::Windowed).expect("Failed to create GLFW window"); + let (mut window, events) = glfw + .create_window(1200, 800, "3dobs", glfw::WindowMode::Windowed) + .expect("Failed to create GLFW window"); window.set_all_polling(true); window.set_cursor_mode(glfw::CursorMode::Disabled); window.make_current(); - let mut state = ui::State{ + let mut state = ui::State { settings, logger, ..Default::default() @@ -81,25 +87,25 @@ fn main() -> anyhow::Result<(), Box> { let (mut imgui, glfw_platform, renderer) = ui::init_imgui(&mut window); let mesh_shader = shader::Shader::new( - &mut shader::ShaderSource{ + &mut shader::ShaderSource { name: "vertex.glsl".to_string(), source: include_str!("../shaders/vertex.glsl").to_string(), }, - &mut shader::ShaderSource{ + &mut shader::ShaderSource { name: "frag.glsl".to_string(), source: include_str!("../shaders/frag.glsl").to_string(), }, - )?; + )?; let grid_shader = shader::Shader::new( - &mut shader::ShaderSource{ + &mut shader::ShaderSource { name: "grid_v.glsl".to_string(), source: include_str!("../shaders/grid_v.glsl").to_string(), }, - &mut shader::ShaderSource{ + &mut shader::ShaderSource { name: "grid_f.glsl".to_string(), source: include_str!("../shaders/grid_f.glsl").to_string(), }, - )?; + )?; let points_lights: [glm::Vec3; 4] = [ glm::vec3(0.7, 0.2, 2.0), @@ -132,9 +138,18 @@ fn main() -> anyhow::Result<(), Box> { mesh_shader.set_float(&format!("pointLights[{}].linear", i), 0.09); mesh_shader.set_float(&format!("pointLights[{}].quadratic", i), 0.032); - mesh_shader.set_3fv(&format!("pointLights[{}].ambient", i), glm::vec3(0.1, 0.1, 0.1)); - mesh_shader.set_3fv(&format!("pointLights[{}].diffuse", i), glm::vec3(0.7, 0.7, 0.7)); - mesh_shader.set_3fv(&format!("pointLights[{}].specular", i), glm::vec3(1.0, 1.0, 1.0)); + mesh_shader.set_3fv( + &format!("pointLights[{}].ambient", i), + glm::vec3(0.1, 0.1, 0.1), + ); + mesh_shader.set_3fv( + &format!("pointLights[{}].diffuse", i), + glm::vec3(0.7, 0.7, 0.7), + ); + mesh_shader.set_3fv( + &format!("pointLights[{}].specular", i), + glm::vec3(1.0, 1.0, 1.0), + ); } mesh_shader.set_float("spotLight.cutOff", glm::cos(glm::radians(12.5))); mesh_shader.set_float("spotLight.outerCutOff", glm::cos(glm::radians(15.0))); @@ -164,7 +179,9 @@ fn main() -> anyhow::Result<(), Box> { delta_time = current_frame - last_frame; last_frame = current_frame; - imgui.io_mut().update_delta_time(std::time::Duration::from_secs_f32(delta_time)); + imgui + .io_mut() + .update_delta_time(std::time::Duration::from_secs_f32(delta_time)); state.camera.update_speed(delta_time); @@ -176,26 +193,32 @@ fn main() -> anyhow::Result<(), Box> { } // camera matrices - let view_mat = glm::ext::look_at(state.camera.position, state.camera.position + state.camera.front, state.camera.up); - let projection_mat = glm::ext::perspective(glm::radians(state.camera.fov), state.viewport_size[0] / state.viewport_size[1], 0.01, 200.0); + let view_mat = glm::ext::look_at( + state.camera.position, + state.camera.position + state.camera.front, + state.camera.up, + ); + let projection_mat = glm::ext::perspective( + glm::radians(state.camera.fov), + state.viewport_size[0] / state.viewport_size[1], + 0.01, + 200.0, + ); if let Some(rx) = &ipc_rx { match rx.try_recv() { Ok(paths) => { window.focus(); utils::import_models_from_paths(&paths, &mut state); - }, - Err(e) => { - match e { - std::sync::mpsc::TryRecvError::Empty => {}, - std::sync::mpsc::TryRecvError::Disconnected => { - panic!("Error: IPC thread channel disconnected"); - } - } } + Err(e) => match e { + std::sync::mpsc::TryRecvError::Empty => {} + std::sync::mpsc::TryRecvError::Disconnected => { + panic!("Error: IPC thread channel disconnected"); + } + }, } } - for (_, event) in glfw::flush_messages(&events) { // order of handling events is important here @@ -219,14 +242,22 @@ fn main() -> anyhow::Result<(), Box> { last_x = xpos as f32; last_y = ypos as f32; - if state.can_capture_cursor && window.get_mouse_button(glfw::MouseButtonLeft) == Action::Press { + if state.can_capture_cursor + && window.get_mouse_button(glfw::MouseButtonLeft) == Action::Press + { if window.get_key(glfw::Key::LeftShift) == Action::Press { state.camera.move_camera(-xoffset, -yoffset); } else { if let Some(active_model) = state.active_model { - let x_rotation = xoffset * state.camera.sensitivity * state.rotation_speed; - let y_rotation = yoffset * state.camera.sensitivity * state.rotation_speed; - let model = state.objects.iter_mut().find(|m| m.id == active_model).unwrap(); + let x_rotation = + xoffset * state.camera.sensitivity * state.rotation_speed; + let y_rotation = + yoffset * state.camera.sensitivity * state.rotation_speed; + let model = state + .objects + .iter_mut() + .find(|m| m.id == active_model) + .unwrap(); // let x_rotation = glm::quat_angle_axis(xoffset * state.camera.sensitivity, &state.camera.up); model.rotate(x_rotation, y_rotation); } @@ -258,18 +289,27 @@ fn main() -> anyhow::Result<(), Box> { mesh_shader.set_3fv("spotLight.direction", state.camera.front); mesh_shader.set_3fv("viewPos", state.camera.position); + // BUG: for objects with semi-transparent materials/textures, the order of drawing is important. + // We must draw all opaque objects/meshes first, then perform a depth/distance sort + // on all semi-transparent objects/meshes and draw them in order from farthest to closest. + // Alternatively. We could implement a dual-depth peeling algorithm + // which seems to be a good one and done solution and is order independent. for obj in &state.objects { if state.wireframe { gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); } else { gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); } - if Some(obj.id) == state.active_model {obj.draw(&mesh_shader, state.draw_aabb, state.show_textures);} + if Some(obj.id) == state.active_model { + obj.draw(&mesh_shader, state.draw_aabb, state.show_textures); + } } gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); // draw grid - if state.draw_grid {draw_grid(&grid_shader, &view_mat, &projection_mat);} + if state.draw_grid { + draw_grid(&grid_shader, &view_mat, &projection_mat); + } // // draw ui @@ -279,7 +319,15 @@ fn main() -> anyhow::Result<(), Box> { gl::Clear(gl::COLOR_BUFFER_BIT); gl::Disable(gl::DEPTH_TEST); gl::Disable(gl::BLEND); - ui::draw_ui(&mut imgui, &renderer, &glfw_platform, &mut window, &mut state, &mut last_cursor, scene_texture); + ui::draw_ui( + &mut imgui, + &renderer, + &glfw_platform, + &mut window, + &mut state, + &mut last_cursor, + scene_texture, + ); glfw.poll_events(); window.swap_buffers(); @@ -303,7 +351,11 @@ fn draw_grid(shader: &threedobs::shader::Shader, view_mat: &glm::Mat4, projectio } } -fn handle_window_event(window: &mut glfw::Window, event: &glfw::WindowEvent, state: &mut ui::State) { +fn handle_window_event( + window: &mut glfw::Window, + event: &glfw::WindowEvent, + state: &mut ui::State, +) { match event { glfw::WindowEvent::Key(Key::O, _, Action::Press, Modifiers::Control) => { ui::import_model(state); @@ -318,26 +370,32 @@ fn handle_window_event(window: &mut glfw::Window, event: &glfw::WindowEvent, sta state.camera.speed /= 5.0; } glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Press, _) => { - if !state.can_capture_cursor { return; } + if !state.can_capture_cursor { + return; + } state.is_cursor_captured = true; window.set_cursor_mode(glfw::CursorMode::Disabled); } glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Release, _) => { - if !state.can_capture_cursor { return; } + if !state.can_capture_cursor { + return; + } state.is_cursor_captured = false; window.set_cursor_mode(glfw::CursorMode::Normal); } glfw::WindowEvent::Scroll(_, yoff) => { - state.camera.handle_mouse_scroll(*yoff as f32, state.can_capture_cursor, state.fov_zoom); + state.camera.handle_mouse_scroll( + *yoff as f32, + state.can_capture_cursor, + state.fov_zoom, + ); } glfw::WindowEvent::FileDrop(paths) => { utils::import_models_from_paths(paths, state); } - glfw::WindowEvent::FramebufferSize(w, h) => { - unsafe { - gl::Viewport(0, 0, *w, *h); - } - } + glfw::WindowEvent::FramebufferSize(w, h) => unsafe { + gl::Viewport(0, 0, *w, *h); + }, _ => {} } } @@ -367,15 +425,36 @@ fn create_scene_texture_and_renderbuffer(window: &glfw::Window, fbo: u32) -> (u3 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as i32, w, h, 0, gl::RGB, gl::UNSIGNED_BYTE, std::ptr::null()); + gl::TexImage2D( + gl::TEXTURE_2D, + 0, + gl::RGB as i32, + w, + h, + 0, + gl::RGB, + gl::UNSIGNED_BYTE, + std::ptr::null(), + ); - gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, fb_texture, 0); + gl::FramebufferTexture2D( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_2D, + fb_texture, + 0, + ); // renderbuffer for depth gl::GenRenderbuffers(1, &mut rbo); gl::BindRenderbuffer(gl::RENDERBUFFER, rbo); gl::RenderbufferStorage(gl::RENDERBUFFER, gl::DEPTH24_STENCIL8, w, h); - gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::DEPTH_STENCIL_ATTACHMENT, gl::RENDERBUFFER, rbo); + gl::FramebufferRenderbuffer( + gl::FRAMEBUFFER, + gl::DEPTH_STENCIL_ATTACHMENT, + gl::RENDERBUFFER, + rbo, + ); if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE { panic!("ERROR::FRAMEBUFFER:: Framebuffer is not complete!"); diff --git a/src/mesh.rs b/src/mesh.rs index 906b718..761dd7b 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -1,7 +1,11 @@ use glad_gl::gl; use log::warn; -use crate::{shader::Shader, utils, importer::{Material, TextureType}}; +use crate::{ + importer::{Material, TextureType}, + shader::Shader, + utils, +}; fn create_rotation_matrix(pitch: f32, yaw: f32, roll: f32, pivot: glm::Vec3) -> glm::Mat4 { let pitch = pitch.to_radians(); @@ -45,7 +49,12 @@ pub struct Mesh { } impl Mesh { - pub fn new(name: &str, vertices: Vec, indices: Vec, material: Option) -> Mesh { + pub fn new( + name: &str, + vertices: Vec, + indices: Vec, + material: Option, + ) -> Mesh { let mut vao = 0; let mut vbo = 0; let mut ebo = 0; @@ -58,22 +67,53 @@ impl Mesh { gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); - gl::BufferData(gl::ARRAY_BUFFER, (std::mem::size_of::() * vertices.len() as usize) as isize, vertices.as_ptr() as *const std::ffi::c_void, gl::STATIC_DRAW); + gl::BufferData( + gl::ARRAY_BUFFER, + (std::mem::size_of::() * vertices.len() as usize) as isize, + vertices.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW, + ); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (std::mem::size_of::() * indices.len() as usize) as isize, indices.as_ptr() as *const std::ffi::c_void, gl::STATIC_DRAW); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + (std::mem::size_of::() * indices.len() as usize) as isize, + indices.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW, + ); // vertex positions gl::EnableVertexAttribArray(0); - gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, std::ptr::null()); + gl::VertexAttribPointer( + 0, + 3, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + std::ptr::null(), + ); // vertex normals gl::EnableVertexAttribArray(1); - gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, (3 * std::mem::size_of::()) as *const std::ffi::c_void); + gl::VertexAttribPointer( + 1, + 3, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + (3 * std::mem::size_of::()) as *const std::ffi::c_void, + ); // vertex texture coords gl::EnableVertexAttribArray(2); - gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, std::mem::size_of::() as i32, (6 * std::mem::size_of::()) as *const std::ffi::c_void); + gl::VertexAttribPointer( + 2, + 2, + gl::FLOAT, + gl::FALSE, + std::mem::size_of::() as i32, + (6 * std::mem::size_of::()) as *const std::ffi::c_void, + ); gl::BindVertexArray(0); } @@ -98,15 +138,24 @@ impl Mesh { let model_mat = glm::ext::scale(&utils::mat_ident(), glm::vec3(scale, scale, scale)); let model_mat = apply_rotation(&model_mat, self.rotation, pivot); - let model_mat = glm::ext::translate(&model_mat, glm::vec3(self.position.x, self.position.y, self.position.z)); + let model_mat = glm::ext::translate( + &model_mat, + glm::vec3(self.position.x, self.position.y, self.position.z), + ); shader.set_mat4fv("model", &model_mat); if glm::ext::is_invertible(&model_mat) { let normal_mat = glm::transpose(&glm::inverse(&model_mat)); let normal_mat = glm::mat3( - normal_mat[0][0], normal_mat[0][1], normal_mat[0][2], - normal_mat[1][0], normal_mat[1][1], normal_mat[1][2], - normal_mat[2][0], normal_mat[2][1], normal_mat[2][2], + normal_mat[0][0], + normal_mat[0][1], + normal_mat[0][2], + normal_mat[1][0], + normal_mat[1][1], + normal_mat[1][2], + normal_mat[2][0], + normal_mat[2][1], + normal_mat[2][2], ); shader.set_mat3fv("normalMatrix", &normal_mat); shader.set_bool("useNormalMatrix", true); @@ -142,18 +191,18 @@ impl Mesh { match tex.typ { TextureType::Ambient => { shader.set_int("material.texture_ambient", i as i32); - }, + } TextureType::Diffuse => { shader.set_int("material.texture_diffuse", i as i32); - }, + } TextureType::Specular => { shader.set_int("material.texture_specular", i as i32); - }, + } TextureType::Emissive => { shader.set_int("material.texture_emission", i as i32); shader.set_bool("hasEmissionTexture", true); - }, - _ => {}, + } + _ => {} } gl::BindTexture(gl::TEXTURE_2D, tex.id); @@ -166,7 +215,12 @@ impl Mesh { unsafe { // draw Mesh gl::BindVertexArray(self.vao); - gl::DrawElements(gl::TRIANGLES, self.indices.len() as i32, gl::UNSIGNED_INT, std::ptr::null()); + gl::DrawElements( + gl::TRIANGLES, + self.indices.len() as i32, + gl::UNSIGNED_INT, + std::ptr::null(), + ); // reset stuff to default gl::ActiveTexture(gl::TEXTURE0); @@ -208,8 +262,7 @@ impl Vertex { Vertex { position, normal, - tex_coords + tex_coords, } } } - diff --git a/src/model.rs b/src/model.rs index 4ce673f..61850b3 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,4 +1,10 @@ -use crate::{mesh::{Mesh, apply_rotation}, shader::Shader, utils, ui::ui, aabb, importer}; +use crate::{ + aabb, importer, + mesh::{apply_rotation, Mesh}, + shader::Shader, + ui::ui, + utils, +}; const SCALING_FACTOR: f32 = 8.0; @@ -24,7 +30,12 @@ impl Model { let scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z); for mesh in obj.meshes.into_iter() { - meshes.push(Mesh::new(&mesh.name, mesh.vertices, mesh.indices, mesh.material)); + meshes.push(Mesh::new( + &mesh.name, + mesh.vertices, + mesh.indices, + mesh.material, + )); } let mut model = Model { @@ -47,10 +58,23 @@ impl Model { let center_z = ((self.aabb.max.z / 2.0) + (self.aabb.min.z / 2.0)) * self.scaling_factor; let pivot = glm::vec3(center_x, center_y, center_z); - let model_mat = glm::ext::scale(&utils::mat_ident(), glm::vec3(self.scaling_factor, self.scaling_factor, self.scaling_factor)); + let model_mat = glm::ext::scale( + &utils::mat_ident(), + glm::vec3( + self.scaling_factor, + self.scaling_factor, + self.scaling_factor, + ), + ); let model_mat = apply_rotation(&model_mat, self.meshes[0].rotation, pivot); - let model_mat = glm::ext::translate(&model_mat, glm::vec3(self.meshes[0].position.x, self.meshes[0].position.y, self.meshes[0].position.z)); - + let model_mat = glm::ext::translate( + &model_mat, + glm::vec3( + self.meshes[0].position.x, + self.meshes[0].position.y, + self.meshes[0].position.z, + ), + ); for mesh in &self.meshes { mesh.draw(shader, self.scaling_factor, pivot, show_textures); diff --git a/src/shader.rs b/src/shader.rs index 8283071..c6d2e1a 100644 --- a/src/shader.rs +++ b/src/shader.rs @@ -1,5 +1,5 @@ -use glad_gl::gl; use anyhow::Result; +use glad_gl::gl; use log::{debug, error}; pub struct ShaderSource { @@ -12,9 +12,13 @@ pub struct Shader { } impl Shader { - pub fn new(vertex_obj: &mut ShaderSource, frag_obj: &mut ShaderSource) -> Result> { + pub fn new( + vertex_obj: &mut ShaderSource, + frag_obj: &mut ShaderSource, + ) -> Result> { vertex_obj.source.push('\0'); - let vertex_shader_source = std::ffi::CStr::from_bytes_with_nul(vertex_obj.source.as_bytes())?; + let vertex_shader_source = + std::ffi::CStr::from_bytes_with_nul(vertex_obj.source.as_bytes())?; frag_obj.source.push('\0'); let frag_shader_source = std::ffi::CStr::from_bytes_with_nul(frag_obj.source.as_bytes())?; @@ -22,39 +26,65 @@ impl Shader { unsafe { let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); gl::CreateShader(vertex_shader); - gl::ShaderSource(vertex_shader, 1, &vertex_shader_source.as_ptr(), std::ptr::null()); + gl::ShaderSource( + vertex_shader, + 1, + &vertex_shader_source.as_ptr(), + std::ptr::null(), + ); gl::CompileShader(vertex_shader); let mut success1 = 0; gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success1); #[cfg(debug_assertions)] - debug!("vertex shader {:?} compiled with status: {}", - vertex_obj.name, - success1 + debug!( + "vertex shader {:?} compiled with status: {}", + vertex_obj.name, success1 ); if success1 == 0 { let info_buf = [0u8; 512]; - gl::GetShaderInfoLog(vertex_shader as u32, 512, std::ptr::null_mut(), info_buf.as_ptr() as *mut i8); + gl::GetShaderInfoLog( + vertex_shader as u32, + 512, + std::ptr::null_mut(), + info_buf.as_ptr() as *mut i8, + ); #[cfg(debug_assertions)] - error!("vertex shader info: {}", std::str::from_utf8(&info_buf).unwrap()); + error!( + "vertex shader info: {}", + std::str::from_utf8(&info_buf).unwrap() + ); } let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER); gl::CreateShader(frag_shader); - gl::ShaderSource(frag_shader, 1, &frag_shader_source.as_ptr(), std::ptr::null()); + gl::ShaderSource( + frag_shader, + 1, + &frag_shader_source.as_ptr(), + std::ptr::null(), + ); gl::CompileShader(frag_shader); let mut success2 = 0; gl::GetShaderiv(frag_shader, gl::COMPILE_STATUS, &mut success2); #[cfg(debug_assertions)] - debug!("frag shader {:?} compiled with status: {}", - frag_obj.name, - success2 + debug!( + "frag shader {:?} compiled with status: {}", + frag_obj.name, success2 ); if success2 == 0 { let info_buf2 = [0u8; 512]; - gl::GetShaderInfoLog(frag_shader as u32, 512, std::ptr::null_mut(), info_buf2.as_ptr() as *mut i8); + gl::GetShaderInfoLog( + frag_shader as u32, + 512, + std::ptr::null_mut(), + info_buf2.as_ptr() as *mut i8, + ); #[cfg(debug_assertions)] - error!("frag shader info: {}", std::str::from_utf8(&info_buf2).unwrap()); + error!( + "frag shader info: {}", + std::str::from_utf8(&info_buf2).unwrap() + ); } let shader_program = gl::CreateProgram(); @@ -80,21 +110,30 @@ impl Shader { pub fn set_bool(&self, name: &str, value: bool) { unsafe { let c_str = std::ffi::CString::new(name).unwrap(); - gl::Uniform1i(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), value as i32); + gl::Uniform1i( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + value as i32, + ); } } pub fn set_int(&self, name: &str, value: i32) { let c_str = std::ffi::CString::new(name).unwrap(); unsafe { - gl::Uniform1i(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), value); + gl::Uniform1i( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + value, + ); } } pub fn set_float(&self, name: &str, value: f32) { let c_str = std::ffi::CString::new(name).unwrap(); unsafe { - gl::Uniform1f(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), value); + gl::Uniform1f( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + value, + ); } } @@ -103,7 +142,11 @@ impl Shader { let mut value = 0.0; unsafe { - gl::GetUniformfv(self.program_id, gl::GetUniformLocation(self.program_id, c_str.as_ptr()), &mut value); + gl::GetUniformfv( + self.program_id, + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + &mut value, + ); value } } @@ -111,21 +154,35 @@ impl Shader { pub fn set_mat3fv(&self, name: &str, value: &glm::Mat3) { let c_str = std::ffi::CString::new(name).unwrap(); unsafe { - gl::UniformMatrix3fv(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), 1, gl::FALSE, value.as_array().as_ptr() as *const f32); + gl::UniformMatrix3fv( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + 1, + gl::FALSE, + value.as_array().as_ptr() as *const f32, + ); } } pub fn set_mat4fv(&self, name: &str, value: &glm::Mat4) { let c_str = std::ffi::CString::new(name).unwrap(); unsafe { - gl::UniformMatrix4fv(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), 1, gl::FALSE, value.as_array().as_ptr() as *const f32); + gl::UniformMatrix4fv( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + 1, + gl::FALSE, + value.as_array().as_ptr() as *const f32, + ); } } pub fn set_3fv(&self, name: &str, value: glm::Vec3) { let c_str = std::ffi::CString::new(name).unwrap(); unsafe { - gl::Uniform3fv(gl::GetUniformLocation(self.program_id, c_str.as_ptr()), 1, value.as_array() as *const f32); + gl::Uniform3fv( + gl::GetUniformLocation(self.program_id, c_str.as_ptr()), + 1, + value.as_array() as *const f32, + ); } } } diff --git a/src/ui/ui.rs b/src/ui/ui.rs index 3e582b9..6c05b1b 100644 --- a/src/ui/ui.rs +++ b/src/ui/ui.rs @@ -1,10 +1,12 @@ use std::time::{SystemTime, UNIX_EPOCH}; use glad_gl::gl; -use log::{info, debug}; -use serde::{Serialize, Deserialize}; +use log::{debug, info}; +use serde::{Deserialize, Serialize}; -use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, logger, utils}; +use crate::{ + camera::Camera, imgui_glfw_support, imgui_opengl_renderer, logger, mesh, model, ui, utils, +}; #[derive(Default, Debug, Serialize, Deserialize, Clone)] pub struct Settings { @@ -31,7 +33,7 @@ pub struct State { pub logger: logger::WritableLog, pub settings: Settings, pub fps: f32, - pub show_textures: bool + pub show_textures: bool, } impl Default for State { @@ -56,7 +58,7 @@ impl Default for State { logger: logger::WritableLog::default(), settings: Settings::default(), fps: 0.0, - show_textures: true + show_textures: true, } } } @@ -72,30 +74,40 @@ impl State { } } -pub fn init_imgui(window: &mut glfw::Window) -> (imgui::Context, imgui_glfw_support::GlfwPlatform, imgui_opengl_renderer::Renderer) { +pub fn init_imgui( + window: &mut glfw::Window, +) -> ( + imgui::Context, + imgui_glfw_support::GlfwPlatform, + imgui_opengl_renderer::Renderer, +) { let mut imgui = imgui::Context::create(); imgui.set_ini_filename(None); - imgui.io_mut().config_flags.insert(imgui::ConfigFlags::DOCKING_ENABLE); - imgui.io_mut().config_flags.set(imgui::ConfigFlags::NAV_ENABLE_KEYBOARD, true); + imgui + .io_mut() + .config_flags + .insert(imgui::ConfigFlags::DOCKING_ENABLE); + imgui + .io_mut() + .config_flags + .set(imgui::ConfigFlags::NAV_ENABLE_KEYBOARD, true); let mut glfw_platform = imgui_glfw_support::GlfwPlatform::init(&mut imgui); glfw_platform.attach_window( imgui.io_mut(), &window, - imgui_glfw_support::HiDpiMode::Default + imgui_glfw_support::HiDpiMode::Default, ); - imgui - .fonts() - .add_font(&[imgui::FontSource::TtfData { - data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"), - size_pixels: 20.0, - config: Some(imgui::FontConfig { - oversample_h: 3, - pixel_snap_h: true, - ..imgui::FontConfig::default() - }) - }]); + imgui.fonts().add_font(&[imgui::FontSource::TtfData { + data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"), + size_pixels: 20.0, + config: Some(imgui::FontConfig { + oversample_h: 3, + pixel_snap_h: true, + ..imgui::FontConfig::default() + }), + }]); imgui.io_mut().font_global_scale = (1.0 / glfw_platform.hidpi_factor()) as f32; @@ -114,17 +126,22 @@ pub fn import_model(state: &mut State) { .add_filter("All supported files", &["obj", "OBJ", "stl", "STL"]) .add_filter("Wavefront OBJ (.obj)", &["obj", "OBJ"]) .add_filter("STL (.stl)", &["stl", "STL"]) - .pick_files() { - Some(m) => m, - None => return, - }; + .pick_files() + { + Some(m) => m, + None => return, + }; utils::import_models_from_paths(&models, state); } pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window) { ui.main_menu_bar(|| { ui.menu("File", || { - if ui.menu_item_config("Import Model(s)").shortcut("Ctrl+O").build() { + if ui + .menu_item_config("Import Model(s)") + .shortcut("Ctrl+O") + .build() + { import_model(state); } if ui.menu_item_config("Settings").build() { @@ -135,24 +152,50 @@ pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw:: } }); ui.menu("View", || { - if ui.menu_item_config("Show Grid").selected(state.draw_grid).build() { + if ui + .menu_item_config("Show Grid") + .selected(state.draw_grid) + .build() + { state.draw_grid = !state.draw_grid; } - if ui.menu_item_config("Draw Bounding Box").selected(state.draw_aabb).build() { + if ui + .menu_item_config("Draw Bounding Box") + .selected(state.draw_aabb) + .build() + { state.draw_aabb = !state.draw_aabb; } }); ui.menu("Help", || { - if ui.menu_item_config("Keybinds").selected(state.show_keybinds).build() { + if ui + .menu_item_config("Keybinds") + .selected(state.show_keybinds) + .build() + { state.show_keybinds = !state.show_keybinds; } - if ui.menu_item_config("About").selected(state.show_help_menu_about).build() { + if ui + .menu_item_config("About") + .selected(state.show_help_menu_about) + .build() + { state.show_help_menu_about = !state.show_help_menu_about; } }); - let mem = state.objects.iter().fold(0 as usize, |acc, m| acc + m.mem_usage) as f32; - let mem_fps = format!("Mem: {:.1}MB | FPS: {:.1}", mem / (1024.0 * 1024.0), state.fps); - let avail_size = [*ui.content_region_avail().get(0).unwrap() - ui.calc_text_size(&mem_fps)[0], 0.0]; + let mem = state + .objects + .iter() + .fold(0 as usize, |acc, m| acc + m.mem_usage) as f32; + let mem_fps = format!( + "Mem: {:.1}MB | FPS: {:.1}", + mem / (1024.0 * 1024.0), + state.fps + ); + let avail_size = [ + *ui.content_region_avail().get(0).unwrap() - ui.calc_text_size(&mem_fps)[0], + 0.0, + ]; ui.dummy(avail_size); ui.text(&mem_fps); }); @@ -168,11 +211,18 @@ fn draw_about_window(ui: &imgui::Ui, state: &mut State) { .resizable(false) .movable(false) .opened(&mut state.show_help_menu_about) - .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) + .position( + [display_size[0] / 2.0, display_size[1] / 2.0], + imgui::Condition::Always, + ) .position_pivot([0.5, 0.5]) .build(|| { ui.text("3dobs - 3D Object Browser"); - ui.text(format!("Version: {}-{}", env!("CARGO_PKG_VERSION"), env!("GIT_HASH"))); + ui.text(format!( + "Version: {}-{}", + env!("CARGO_PKG_VERSION"), + env!("GIT_HASH") + )); ui.text(format!("{}", env!("CARGO_PKG_DESCRIPTION"))); ui.spacing(); ui.spacing(); @@ -189,10 +239,16 @@ pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) { ui.window("Settings") .opened(&mut state.show_settings) .movable(false) - .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) + .position( + [display_size[0] / 2.0, display_size[1] / 2.0], + imgui::Condition::Always, + ) .position_pivot([0.5, 0.5]) .build(|| { - if ui.checkbox("Only allow one program instance (Reboot required when enabling)", &mut state.settings.one_instance) { + if ui.checkbox( + "Only allow one program instance (Reboot required when enabling)", + &mut state.settings.one_instance, + ) { confy::store("3dobs", "settings", state.settings.clone()).unwrap(); } }); @@ -208,10 +264,19 @@ fn draw_keybinds_window(ui: &imgui::Ui, state: &mut State) { .opened(&mut state.show_keybinds) .resizable(false) .movable(false) - .position([display_size[0] / 2.0, display_size[1] / 2.0], imgui::Condition::Always) + .position( + [display_size[0] / 2.0, display_size[1] / 2.0], + imgui::Condition::Always, + ) .position_pivot([0.5, 0.5]) .build(|| { - if let Some(..) = ui.begin_table_with_sizing("Keybinds Table", 2, imgui::TableFlags::SIZING_STRETCH_SAME, [0.0, 0.0], 0.0) { + if let Some(..) = ui.begin_table_with_sizing( + "Keybinds Table", + 2, + imgui::TableFlags::SIZING_STRETCH_SAME, + [0.0, 0.0], + 0.0, + ) { ui.table_next_column(); ui.text_colored([0.7, 0.7, 0.6, 1.0], "Key"); ui.table_next_column(); @@ -226,7 +291,7 @@ fn draw_keybinds_window(ui: &imgui::Ui, state: &mut State) { ui.text("Ctrl + Q"); ui.table_next_column(); ui.text("Quit"); - + ui.table_next_column(); ui.text("Left Mouse Button"); ui.table_next_column(); @@ -269,33 +334,44 @@ fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) { } fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) { - ui.tree_node_config(format!("{}###{}", mesh.name.as_str(), i)).build(|| { - ui.text(format!("Vertices: {}", mesh.vertices.len())); - ui.text(format!("Triangles: {}", mesh.indices.len() / 3)); - ui.tree_node_config(mesh.material.name.as_str()).build(|| { - ui.text(format!("{}", mesh.material)); + ui.tree_node_config(format!("{}###{}", mesh.name.as_str(), i)) + .build(|| { + ui.text(format!("Vertices: {}", mesh.vertices.len())); + ui.text(format!("Triangles: {}", mesh.indices.len() / 3)); + ui.tree_node_config(mesh.material.name.as_str()).build(|| { + ui.text(format!("{}", mesh.material)); + }); + ui.tree_node_config("Transformations").build(|| { + draw_transformations(ui, mesh); + }) }); - ui.tree_node_config("Transformations").build(|| { - draw_transformations(ui, mesh); - }) - }); } fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool { ui.table_next_column(); - if ui.checkbox(format!("###{}", state.objects[idx].id), &mut (Some(state.objects[idx].id) == state.active_model)) { + if ui.checkbox( + format!("###{}", state.objects[idx].id), + &mut (Some(state.objects[idx].id) == state.active_model), + ) { state.objects[idx].reset_rotation(); state.active_model = Some(state.objects[idx].id); - state.camera.focus_on_selected_model(state.active_model, &state.objects); + state + .camera + .focus_on_selected_model(state.active_model, &state.objects); } ui.table_next_column(); - ui.tree_node_config(format!("{} ({:.1}MB)###{}", state.objects[idx].name.as_str(), state.objects[idx].mem_usage as f32 / (1024.0 * 1024.0), idx)) - .build(|| { - for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() { - draw_mesh_hierarchy(ui, mesh, j); - } - }); + ui.tree_node_config(format!( + "{} ({:.1}MB)###{}", + state.objects[idx].name.as_str(), + state.objects[idx].mem_usage as f32 / (1024.0 * 1024.0), + idx + )) + .build(|| { + for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() { + draw_mesh_hierarchy(ui, mesh, j); + } + }); ui.table_next_column(); if ui.small_button(format!("X###{}-{}", state.objects[idx].name.as_str(), idx)) { @@ -312,7 +388,13 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) { .build(|| { let mut i = 0; - if let Some(..) = ui.begin_table_with_sizing("Objects Table", 3, imgui::TableFlags::SIZING_FIXED_FIT, [0.0, 0.0], 0.0) { + if let Some(..) = ui.begin_table_with_sizing( + "Objects Table", + 3, + imgui::TableFlags::SIZING_FIXED_FIT, + [0.0, 0.0], + 0.0, + ) { ui.table_setup_column_with(imgui::TableColumnSetup { name: "", flags: imgui::TableColumnFlags::empty(), @@ -339,7 +421,9 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) { if state.active_model == Some(selected_obj_id) { let model = state.objects.last_mut().map(|m| m.reset_rotation()); state.active_model = model.and_then(|o| Some(o.id)); - state.camera.focus_on_selected_model(state.active_model, &state.objects); + state + .camera + .focus_on_selected_model(state.active_model, &state.objects); } continue; } @@ -415,12 +499,12 @@ fn create_initial_docking(ui: &imgui::Ui, state: &mut State) { }, |down| { down.dock_window("Console"); - } + }, ) }, |left| { left.dock_window("Viewer"); - } + }, ) } }); @@ -441,7 +525,9 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) { state.viewport_size = tex_size; if ui.button("Reset Camera") { - state.camera.focus_on_selected_model(state.active_model, &state.objects); + state + .camera + .focus_on_selected_model(state.active_model, &state.objects); } ui.same_line(); if ui.button("Capture Scene") { @@ -457,7 +543,14 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) { let mut pixels = vec![0u8; (w * h * 4) as usize]; unsafe { - gl::GetTextureImage(texture, 0, gl::RGBA, gl::UNSIGNED_BYTE, (w * h * 4) as i32, pixels.as_mut_ptr() as *mut std::ffi::c_void); + gl::GetTextureImage( + texture, + 0, + gl::RGBA, + gl::UNSIGNED_BYTE, + (w * h * 4) as i32, + pixels.as_mut_ptr() as *mut std::ffi::c_void, + ); } let timestamp = SystemTime::now() @@ -465,18 +558,27 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) { .expect("Current time to not be before the UNIX epoch"); let file_name = format!("capture-{}.png", timestamp.as_secs()); let save_path = std::path::Path::new(file_name.as_str()); - let capture = image::ImageBuffer::, _>::from_raw(w as u32, h as u32, pixels).unwrap(); + let capture = + image::ImageBuffer::, _>::from_raw(w as u32, h as u32, pixels) + .unwrap(); let capture = image::DynamicImage::ImageRgba8(capture); let capture = capture.flipv(); - let capture = capture.resize_exact(tex_size[0] as u32, tex_size[1] as u32, image::imageops::FilterType::Gaussian); + let capture = capture.resize_exact( + tex_size[0] as u32, + tex_size[1] as u32, + image::imageops::FilterType::Gaussian, + ); let _ = capture.save(save_path); let elapsed = now.elapsed(); - info!("Scene capture saved to: {} successfully", save_path - .canonicalize() - .expect("Capture path to be canonicalized") - .to_str() - .expect("Capture path to be valid unicode")); + info!( + "Scene capture saved to: {} successfully", + save_path + .canonicalize() + .expect("Capture path to be canonicalized") + .to_str() + .expect("Capture path to be valid unicode") + ); debug!("Scene capture took: {}ms", elapsed.as_millis()); @@ -524,7 +626,9 @@ pub fn draw_ui( last_cursor: &mut Option, scene_fb_texture: u32, ) { - glfw_platform.prepare_frame(imgui.io_mut(), window).expect("Failed to prepare imgui frame"); + glfw_platform + .prepare_frame(imgui.io_mut(), window) + .expect("Failed to prepare imgui frame"); let ui = imgui.new_frame(); create_initial_docking(ui, state); diff --git a/src/utils.rs b/src/utils.rs index fb03f7e..8591c1e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,15 +1,15 @@ use std::path::PathBuf; +use ::log::{error, info}; +use anyhow::{Context, Result}; use glad_gl::gl; use glm; -use anyhow::{Result, Context}; -use ::log::{info, error}; -use crate::{model, ui, importer}; +use crate::{importer, model, ui}; pub enum SupportedFileExtensions { OBJ, - STL + STL, } impl SupportedFileExtensions { @@ -17,7 +17,7 @@ impl SupportedFileExtensions { match s.to_ascii_lowercase().as_str() { "obj" => Some(Self::OBJ), "stl" => Some(Self::STL), - _ => None + _ => None, } } } @@ -34,7 +34,7 @@ pub fn load_texture(path: PathBuf) -> Result { 2 => gl::RG, 3 => gl::RGB, 4 => gl::RGBA, - _ => panic!("Unknown image format") + _ => panic!("Unknown image format"), }; unsafe { @@ -44,13 +44,27 @@ pub fn load_texture(path: PathBuf) -> Result { gl::GenTextures(1, &mut texture_id); gl::BindTexture(gl::TEXTURE_2D, texture_id); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR as i32); + gl::TexParameteri( + gl::TEXTURE_2D, + gl::TEXTURE_MIN_FILTER, + gl::LINEAR_MIPMAP_LINEAR as i32, + ); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32); - gl::TexImage2D(gl::TEXTURE_2D, 0, format as i32, tex.width() as i32, tex.height() as i32, 0, format, gl::UNSIGNED_BYTE, tex.as_bytes().as_ptr() as *const std::ffi::c_void); + gl::TexImage2D( + gl::TEXTURE_2D, + 0, + format as i32, + tex.width() as i32, + tex.height() as i32, + 0, + format, + gl::UNSIGNED_BYTE, + tex.as_bytes().as_ptr() as *const std::ffi::c_void, + ); gl::GenerateMipmap(gl::TEXTURE_2D); gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4); @@ -61,10 +75,7 @@ pub fn load_texture(path: PathBuf) -> Result { pub fn mat_ident() -> glm::Mat4 { glm::mat4( - 1., 0., 0., 0., - 0., 1., 0., 0., - 0., 0., 1., 0., - 0., 0., 0., 1. + 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., ) } @@ -72,18 +83,27 @@ pub fn import_models_from_paths(paths: &Vec, state: &mut ui::ui::State) for model_path in paths { let filename = model_path.file_name(); if model_path.is_dir() { - info!("Skipping directory \"{}\"", filename.unwrap().to_str().unwrap()); + info!( + "Skipping directory \"{}\"", + filename.unwrap().to_str().unwrap() + ); continue; } match model_path.extension() { Some(ext) => { if SupportedFileExtensions::from_str(ext.to_str().unwrap()).is_none() { - info!("Skipping file \"{}\" because it is not an OBJ or STL file", filename.unwrap().to_str().unwrap()); + info!( + "Skipping file \"{}\" because it is not an OBJ or STL file", + filename.unwrap().to_str().unwrap() + ); continue; } - }, + } None => { - info!("Skipping file \"{}\" because it has no extension", filename.unwrap().to_str().unwrap()); + info!( + "Skipping file \"{}\" because it has no extension", + filename.unwrap().to_str().unwrap() + ); continue; } } @@ -98,10 +118,16 @@ pub fn import_models_from_paths(paths: &Vec, state: &mut ui::ui::State) m.name = model_name.to_str().unwrap().to_string(); } state.objects.push(m); - state.camera.focus_on_selected_model(state.active_model, &state.objects); - }, + state + .camera + .focus_on_selected_model(state.active_model, &state.objects); + } Err(e) => { - error!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e); + error!( + "Error loading model \"{}\": {}", + model_path.to_str().unwrap(), + e + ); } } }