ran cargo fmt

This commit is contained in:
2023-11-15 01:18:38 +02:00
parent b5430822c6
commit fac31bf3d5
16 changed files with 1137 additions and 538 deletions
+83 -15
View File
@@ -19,14 +19,46 @@ impl AABB {
let mut ebo = 0; let mut ebo = 0;
let vertices = vec![ 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 {
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) }, position: glm::vec3(min.x, min.y, min.z),
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) }, tex_coords: glm::vec2(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) }, 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 {
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) }, position: glm::vec3(max.x, min.y, min.z),
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) }, 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![ let indices = vec![
@@ -46,22 +78,53 @@ impl AABB {
gl::BindVertexArray(vao); gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(gl::ARRAY_BUFFER, (std::mem::size_of::<Vertex>() * 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::<Vertex>() * 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::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (std::mem::size_of::<u32>() * 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::<u32>() * indices.len() as usize) as isize,
indices.as_ptr() as *const std::ffi::c_void,
gl::STATIC_DRAW,
);
// vertex positions // vertex positions
gl::EnableVertexAttribArray(0); gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, std::ptr::null()); gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
std::ptr::null(),
);
// vertex normals // vertex normals
gl::EnableVertexAttribArray(1); gl::EnableVertexAttribArray(1);
gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, (3 * std::mem::size_of::<f32>()) as *const std::ffi::c_void); gl::VertexAttribPointer(
1,
3,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
(3 * std::mem::size_of::<f32>()) as *const std::ffi::c_void,
);
// vertex texture coords // vertex texture coords
gl::EnableVertexAttribArray(2); gl::EnableVertexAttribArray(2);
gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, (6 * std::mem::size_of::<f32>()) as *const std::ffi::c_void); gl::VertexAttribPointer(
2,
2,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
(6 * std::mem::size_of::<f32>()) as *const std::ffi::c_void,
);
gl::BindVertexArray(0); gl::BindVertexArray(0);
} }
@@ -72,7 +135,7 @@ impl AABB {
indices_len: indices.len() as u32, indices_len: indices.len() as u32,
vao, vao,
vbo, vbo,
ebo ebo,
} }
} }
@@ -88,7 +151,12 @@ impl AABB {
gl::BindVertexArray(self.vao); gl::BindVertexArray(self.vao);
gl::LineWidth(5.0); gl::LineWidth(5.0);
gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); 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 // reset stuff to default
gl::BindVertexArray(0); gl::BindVertexArray(0);
+14 -6
View File
@@ -30,7 +30,9 @@ impl Camera {
} }
pub fn handle_mouse_scroll(&mut self, yoffset: f32, can_capture_cursor: bool, fov_zoom: bool) { 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 { if fov_zoom {
self.fov -= yoffset; self.fov -= yoffset;
@@ -41,7 +43,8 @@ impl Camera {
self.fov = 85.0; self.fov = 85.0;
} }
} else { } 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; self._speed = self.speed * delta_time;
} }
pub fn focus_on_selected_model(&mut self, active_model: Option<u32>, objects: &Vec<model::Model>) { pub fn focus_on_selected_model(
&mut self,
active_model: Option<u32>,
objects: &Vec<model::Model>,
) {
if let Some(id) = active_model { if let Some(id) = active_model {
for obj in objects { for obj in objects {
if obj.id == id { if obj.id == id {
// we scale the center of the object since the model (and therefore the AABB) is scaled // 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_x =
let center_y = ((obj.aabb.max.y / 2.0) + (obj.aabb.min.y / 2.0)) * obj.scaling_factor; ((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; let z = obj.aabb.max.z * obj.scaling_factor + 10.0;
self.position = glm::vec3(center_x, center_y, z); self.position = glm::vec3(center_x, center_y, z);
self.front = glm::vec3(0.0, 0.0, -1.0); self.front = glm::vec3(0.0, 0.0, -1.0);
@@ -72,4 +81,3 @@ impl Camera {
} }
} }
} }
+4 -1
View File
@@ -54,7 +54,10 @@ struct Clipboard {
impl imgui::ClipboardBackend for Clipboard { impl imgui::ClipboardBackend for Clipboard {
fn set(&mut self, s: &str) { fn set(&mut self, s: &str) {
unsafe { 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<String> { fn get(&mut self) -> std::option::Option<String> {
+154 -53
View File
@@ -22,11 +22,7 @@ struct Locs {
} }
impl Renderer { impl Renderer {
pub fn new( pub fn new(imgui: &mut Context) -> Self {
imgui: &mut Context,
) -> Self
{
unsafe { unsafe {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let glsl_version = b"#version 150\n\0"; let glsl_version = b"#version 150\n\0";
@@ -59,20 +55,38 @@ impl Renderer {
} }
\0"; \0";
let vert_sources = [glsl_version.as_ptr() as *const gl::GLchar, let vert_sources = [
vert_source.as_ptr() as *const gl::GLchar]; glsl_version.as_ptr() as *const gl::GLchar,
let vert_sources_len = [glsl_version.len() as gl::GLint - 1, vert_source.as_ptr() as *const gl::GLchar,
vert_source.len() as gl::GLint - 1]; ];
let frag_sources = [glsl_version.as_ptr() as *const gl::GLchar, let vert_sources_len = [
frag_source.as_ptr() as *const gl::GLchar]; glsl_version.len() as gl::GLint - 1,
let frag_sources_len = [glsl_version.len() as gl::GLint - 1, vert_source.len() as gl::GLint - 1,
frag_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 program = gl::CreateProgram();
let vert_shader = gl::CreateShader(gl::VERTEX_SHADER); let vert_shader = gl::CreateShader(gl::VERTEX_SHADER);
let frag_shader = gl::CreateShader(gl::FRAGMENT_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(
gl::ShaderSource(frag_shader, 2, frag_sources.as_ptr(), frag_sources_len.as_ptr()); 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(vert_shader);
gl::CompileShader(frag_shader); gl::CompileShader(frag_shader);
gl::AttachShader(program, vert_shader); gl::AttachShader(program, vert_shader);
@@ -95,7 +109,6 @@ impl Renderer {
let mut current_texture = 0; let mut current_texture = 0;
gl::GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture); gl::GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture);
let font_texture = return_param(|x| gl::GenTextures(1, x)); let font_texture = return_param(|x| gl::GenTextures(1, x));
gl::BindTexture(gl::TEXTURE_2D, font_texture); 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_MIN_FILTER, gl::LINEAR as _);
@@ -106,7 +119,17 @@ impl Renderer {
let atlas = imgui.fonts(); let atlas = imgui.fonts();
let texture = atlas.build_rgba32_texture(); 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 _); 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(); atlas.tex_id = (font_texture as usize).into();
} }
@@ -123,11 +146,8 @@ impl Renderer {
} }
} }
pub fn render( pub fn render(&self, ctx: &mut Context) {
&self, use imgui::{DrawCmd, DrawCmdParams, DrawIdx, DrawVert};
ctx: &mut Context,
) {
use imgui::{DrawVert,DrawIdx,DrawCmd,DrawCmdParams};
unsafe { unsafe {
let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x)); let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x));
@@ -136,23 +156,31 @@ impl Renderer {
let last_texture = return_param(|x| gl::GetIntegerv(gl::TEXTURE_BINDING_2D, 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_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_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_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_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_polygon_mode = return_param(|x: &mut [gl::GLint; 2]| {
let last_viewport = return_param(|x: &mut [gl::GLint; 4]| gl::GetIntegerv(gl::VIEWPORT, x.as_mut_ptr())); gl::GetIntegerv(gl::POLYGON_MODE, 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_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_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_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_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_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_rgb =
let last_blend_equation_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, x)); 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_blend = gl::IsEnabled(gl::BLEND) == gl::TRUE;
let last_enable_cull_face = gl::IsEnabled(gl::CULL_FACE) == 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_depth_test = gl::IsEnabled(gl::DEPTH_TEST) == gl::TRUE;
let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE; let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE;
gl::Enable(gl::BLEND); gl::Enable(gl::BLEND);
gl::BlendEquation(gl::FUNC_ADD); gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
@@ -161,7 +189,6 @@ impl Renderer {
gl::Enable(gl::SCISSOR_TEST); gl::Enable(gl::SCISSOR_TEST);
gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
let [width, height] = ctx.io().display_size; let [width, height] = ctx.io().display_size;
let [scale_w, scale_h] = ctx.io().display_framebuffer_scale; let [scale_w, scale_h] = ctx.io().display_framebuffer_scale;
@@ -180,17 +207,36 @@ impl Renderer {
gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _); gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
gl::BindSampler(0, 0); gl::BindSampler(0, 0);
let vao = return_param(|x| gl::GenVertexArrays(1, x)); let vao = return_param(|x| gl::GenVertexArrays(1, x));
gl::BindVertexArray(vao); gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::EnableVertexAttribArray(self.locs.position); gl::EnableVertexAttribArray(self.locs.position);
gl::EnableVertexAttribArray(self.locs.uv); gl::EnableVertexAttribArray(self.locs.uv);
gl::EnableVertexAttribArray(self.locs.color); gl::EnableVertexAttribArray(self.locs.color);
gl::VertexAttribPointer(self.locs.position, 2, gl::FLOAT, gl::FALSE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.pos) as _); gl::VertexAttribPointer(
gl::VertexAttribPointer(self.locs.uv, 2, gl::FLOAT, gl::FALSE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.uv) as _); self.locs.position,
gl::VertexAttribPointer(self.locs.color, 4, gl::UNSIGNED_BYTE, gl::TRUE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.col) as _); 2,
gl::FLOAT,
gl::FALSE,
mem::size_of::<DrawVert>() as _,
field_offset::<DrawVert, _, _>(|v| &v.pos) as _,
);
gl::VertexAttribPointer(
self.locs.uv,
2,
gl::FLOAT,
gl::FALSE,
mem::size_of::<DrawVert>() as _,
field_offset::<DrawVert, _, _>(|v| &v.uv) as _,
);
gl::VertexAttribPointer(
self.locs.color,
4,
gl::UNSIGNED_BYTE,
gl::TRUE,
mem::size_of::<DrawVert>() as _,
field_offset::<DrawVert, _, _>(|v| &v.col) as _,
);
let draw_data = ctx.render(); let draw_data = ctx.render();
@@ -199,16 +245,27 @@ impl Renderer {
let idx_buffer = draw_list.idx_buffer(); let idx_buffer = draw_list.idx_buffer();
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BufferData(gl::ARRAY_BUFFER, (vtx_buffer.len() * mem::size_of::<DrawVert>()) as _, vtx_buffer.as_ptr() as _, gl::STREAM_DRAW); gl::BufferData(
gl::ARRAY_BUFFER,
(vtx_buffer.len() * mem::size_of::<DrawVert>()) as _,
vtx_buffer.as_ptr() as _,
gl::STREAM_DRAW,
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (idx_buffer.len() * mem::size_of::<DrawIdx>()) as _, idx_buffer.as_ptr() as _, gl::STREAM_DRAW); gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(idx_buffer.len() * mem::size_of::<DrawIdx>()) as _,
idx_buffer.as_ptr() as _,
gl::STREAM_DRAW,
);
for cmd in draw_list.commands() { for cmd in draw_list.commands() {
match cmd { match cmd {
DrawCmd::Elements { DrawCmd::Elements {
count, count,
cmd_params: DrawCmdParams { cmd_params:
DrawCmdParams {
clip_rect: [x, y, z, w], clip_rect: [x, y, z, w],
texture_id, texture_id,
idx_offset, idx_offset,
@@ -217,18 +274,29 @@ impl Renderer {
} => { } => {
gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _); gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _);
gl::Scissor((x * scale_w) as gl::GLint, gl::Scissor(
(x * scale_w) as gl::GLint,
(fb_height - w * scale_h) as gl::GLint, (fb_height - w * scale_h) as gl::GLint,
((z - x) * scale_w) as gl::GLint, ((z - x) * scale_w) as gl::GLint,
((w - y) * scale_h) as gl::GLint); ((w - y) * scale_h) as gl::GLint,
);
let idx_size = if mem::size_of::<DrawIdx>() == 2 { gl::UNSIGNED_SHORT } else { gl::UNSIGNED_INT }; let idx_size = if mem::size_of::<DrawIdx>() == 2 {
gl::UNSIGNED_SHORT
} else {
gl::UNSIGNED_INT
};
gl::DrawElements(gl::TRIANGLES, count as _, idx_size, (idx_offset * mem::size_of::<DrawIdx>()) as _); gl::DrawElements(
}, gl::TRIANGLES,
count as _,
idx_size,
(idx_offset * mem::size_of::<DrawIdx>()) as _,
);
}
DrawCmd::ResetRenderState => { DrawCmd::ResetRenderState => {
unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet"); unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet");
}, }
DrawCmd::RawCallback { .. } => { DrawCmd::RawCallback { .. } => {
unimplemented!("Haven't implemented user callbacks yet"); unimplemented!("Haven't implemented user callbacks yet");
} }
@@ -246,15 +314,45 @@ impl Renderer {
gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _); gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_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::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 _); gl::BlendFuncSeparate(
if last_enable_blend { gl::Enable(gl::BLEND) } else { gl::Disable(gl::BLEND) }; last_blend_src_rgb as _,
if last_enable_cull_face { gl::Enable(gl::CULL_FACE) } else { gl::Disable(gl::CULL_FACE) }; last_blend_dst_rgb as _,
if last_enable_depth_test { gl::Enable(gl::DEPTH_TEST) } else { gl::Disable(gl::DEPTH_TEST) }; last_blend_src_alpha as _,
if last_enable_scissor_test { gl::Enable(gl::SCISSOR_TEST) } else { gl::Disable(gl::SCISSOR_TEST) }; 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::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::Viewport(
gl::Scissor(last_scissor_box[0] as _, last_scissor_box[1] as _, last_scissor_box[2] as _, last_scissor_box[3] as _); 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 _,
);
} }
} }
} }
@@ -287,7 +385,10 @@ fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
} }
} }
fn return_param<T, F>(f: F) -> T where F: FnOnce(&mut T) { fn return_param<T, F>(f: F) -> T
where
F: FnOnce(&mut T),
{
let mut val = unsafe { mem::zeroed() }; let mut val = unsafe { mem::zeroed() };
f(&mut val); f(&mut val);
val val
+25 -14
View File
@@ -3,7 +3,11 @@ mod stl;
use std::path::PathBuf; 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)] #[derive(Debug, Clone)]
pub enum TextureType { pub enum TextureType {
@@ -15,7 +19,7 @@ pub enum TextureType {
Displacement, Displacement,
Decal, Decal,
Reflection, Reflection,
Emissive Emissive,
} }
impl TextureType { impl TextureType {
@@ -30,7 +34,7 @@ impl TextureType {
"decal" => Some(TextureType::Decal), "decal" => Some(TextureType::Decal),
"refl" => Some(TextureType::Reflection), "refl" => Some(TextureType::Reflection),
"map_Ke" => Some(TextureType::Emissive), "map_Ke" => Some(TextureType::Emissive),
_ => None _ => None,
} }
} }
} }
@@ -43,11 +47,19 @@ pub struct Material {
pub specular_color: glm::Vec3, pub specular_color: glm::Vec3,
pub specular_exponent: f32, pub specular_exponent: f32,
pub opacity: f32, pub opacity: f32,
pub textures: Vec<Texture> pub textures: Vec<Texture>,
} }
impl Material { impl Material {
fn new(name: String, ambient: glm::Vec3, diffuse: glm::Vec3, specular: glm::Vec3, shininess: f32, opacity: f32, textures: Vec<Texture>) -> Self { fn new(
name: String,
ambient: glm::Vec3,
diffuse: glm::Vec3,
specular: glm::Vec3,
shininess: f32,
opacity: f32,
textures: Vec<Texture>,
) -> Self {
Self { Self {
name, name,
ambient_color: ambient, ambient_color: ambient,
@@ -55,7 +67,7 @@ impl Material {
specular_color: specular, specular_color: specular,
specular_exponent: shininess, specular_exponent: shininess,
opacity, opacity,
textures textures,
} }
} }
} }
@@ -69,7 +81,7 @@ impl Default for Material {
specular_color: glm::vec3(0.1, 0.1, 0.1), specular_color: glm::vec3(0.1, 0.1, 0.1),
specular_exponent: 32.0, specular_exponent: 32.0,
opacity: 1.0, opacity: 1.0,
textures: Vec::new() textures: Vec::new(),
} }
} }
} }
@@ -96,17 +108,16 @@ pub struct Texture {
} }
impl Texture { impl Texture {
pub fn new(path: std::path::PathBuf, typ: TextureType) -> Result<Self, Box<dyn std::error::Error>> { pub fn new(
path: std::path::PathBuf,
typ: TextureType,
) -> Result<Self, Box<dyn std::error::Error>> {
let id = utils::load_texture(path)?; let id = utils::load_texture(path)?;
Ok(Texture { Ok(Texture { id, typ })
id,
typ,
})
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ObjMesh { pub struct ObjMesh {
pub name: String, pub name: String,
@@ -125,7 +136,7 @@ pub struct Object {
pub fn load_from_file(path: &PathBuf) -> Result<Object, Box<dyn std::error::Error>> { pub fn load_from_file(path: &PathBuf) -> Result<Object, Box<dyn std::error::Error>> {
let path_str = match path.to_str() { let path_str = match path.to_str() {
Some(s) => s, 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)?; let file = std::fs::File::open(path_str)?;
+66 -31
View File
@@ -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 const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes
@@ -86,7 +94,10 @@ impl MtlToken {
} }
} }
fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap<String, Texture>) -> Result<HashMap<String, Material>, Box<dyn std::error::Error>> { fn parse_mtl(
path: &PathBuf,
obj_textures: &mut HashMap<String, Texture>,
) -> Result<HashMap<String, Material>, Box<dyn std::error::Error>> {
let file = std::fs::File::open(path)?; let file = std::fs::File::open(path)?;
let reader = BufReader::with_capacity(BUF_CAP, file); let reader = BufReader::with_capacity(BUF_CAP, file);
let mut material_name = String::new(); let mut material_name = String::new();
@@ -112,7 +123,15 @@ fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap<String, Texture>) -> Res
match MtlToken::from_str(token) { match MtlToken::from_str(token) {
Some(MtlToken::NewMaterial) => { Some(MtlToken::NewMaterial) => {
if !material_name.is_empty() { 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); materials.insert(material_name, material);
mat_textures.clear(); mat_textures.clear();
@@ -176,19 +195,32 @@ fn parse_mtl(path: &PathBuf, obj_textures: &mut HashMap<String, Texture>) -> Res
mat_textures.push(tex); 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); materials.insert(material_name, material);
Ok(materials) Ok(materials)
} }
pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> { pub fn load_obj(
obj_path: &PathBuf,
file: std::fs::File,
) -> Result<Object, Box<dyn std::error::Error>> {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let reader = BufReader::with_capacity(BUF_CAP, file); let reader = BufReader::with_capacity(BUF_CAP, file);
let mut object_name = String::new(); let mut object_name = String::new();
@@ -230,7 +262,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
name, name,
vertices: vertices.clone(), vertices: vertices.clone(),
indices: indices.clone(), indices: indices.clone(),
material: current_material.clone() material: current_material.clone(),
}); });
} }
vertices.clear(); vertices.clear();
@@ -252,7 +284,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
name, name,
vertices: vertices.clone(), vertices: vertices.clone(),
indices: indices.clone(), indices: indices.clone(),
material: current_material.clone() material: current_material.clone(),
}); });
} }
vertices.clear(); vertices.clear();
@@ -264,12 +296,13 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
Some(ObjToken::Vertex) => { Some(ObjToken::Vertex) => {
let vec = iter.collect::<Vec<_>>(); let vec = iter.collect::<Vec<_>>();
if vec.len() < 3 { 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() let mut iter = vec.iter().take(3).map(|i| i.parse::<f32>().unwrap());
.take(3)
.map(|i| i.parse::<f32>().unwrap());
let x = iter.next().unwrap(); let x = iter.next().unwrap();
let y = iter.next().unwrap(); let y = iter.next().unwrap();
let z = iter.next().unwrap(); let z = iter.next().unwrap();
@@ -277,26 +310,24 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
min_aabb = glm::vec3(min_aabb.x.min(x), min_aabb.y.min(y), min_aabb.z.min(z)); min_aabb = glm::vec3(min_aabb.x.min(x), min_aabb.y.min(y), min_aabb.z.min(z));
max_aabb = glm::vec3(max_aabb.x.max(x), max_aabb.y.max(y), max_aabb.z.max(z)); max_aabb = glm::vec3(max_aabb.x.max(x), max_aabb.y.max(y), max_aabb.z.max(z));
} }
Some(ObjToken::Normal) => { Some(ObjToken::Normal) => {
let vec = iter.collect::<Vec<_>>(); let vec = iter.collect::<Vec<_>>();
if vec.len() < 3 { 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() let mut iter = vec.iter().take(3).map(|i| i.parse::<f32>().unwrap());
.take(3)
.map(|i| i.parse::<f32>().unwrap());
let x = iter.next().unwrap(); let x = iter.next().unwrap();
let y = iter.next().unwrap(); let y = iter.next().unwrap();
let z = iter.next().unwrap(); let z = iter.next().unwrap();
normals.push(glm::vec3(x, y, z)); normals.push(glm::vec3(x, y, z));
} }
Some(ObjToken::TexCoord) => { Some(ObjToken::TexCoord) => {
let mut iter = iter let mut iter = iter.take(2).map(|i| i.parse::<f32>().unwrap());
.take(2)
.map(|i| i.parse::<f32>().unwrap());
let u = iter.next().unwrap(); let u = iter.next().unwrap();
let v = iter.next().unwrap(); let v = iter.next().unwrap();
// vertically flip the texcoords because flipping the texture is expensive // 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<Object, Box<d
let part2 = face[2].split("/").next().unwrap(); let part2 = face[2].split("/").next().unwrap();
calculated_normal = glm::normalize(glm::cross( calculated_normal = glm::normalize(glm::cross(
temp_vertices[part1.parse::<i32>().unwrap() as usize - 1] - temp_vertices[part0.parse::<i32>().unwrap() as usize - 1], temp_vertices[part1.parse::<i32>().unwrap() as usize - 1]
temp_vertices[part2.parse::<i32>().unwrap() as usize - 1] - temp_vertices[part0.parse::<i32>().unwrap() as usize - 1] - temp_vertices[part0.parse::<i32>().unwrap() as usize - 1],
temp_vertices[part2.parse::<i32>().unwrap() as usize - 1]
- temp_vertices[part0.parse::<i32>().unwrap() as usize - 1],
)); ));
} }
@@ -335,7 +368,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
vertices.push(Vertex { vertices.push(Vertex {
position: *temp_vertices.get(vert as usize).unwrap(), position: *temp_vertices.get(vert as usize).unwrap(),
normal: *normals.get(normal as usize).unwrap(), normal: *normals.get(normal as usize).unwrap(),
tex_coords: glm::vec2(0.0, 0.0) tex_coords: glm::vec2(0.0, 0.0),
}); });
} else if vert.matches("/").count() == 2 { } else if vert.matches("/").count() == 2 {
let mut it = vert.split("/"); let mut it = vert.split("/");
@@ -360,7 +393,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
vertices.push(Vertex { vertices.push(Vertex {
position: *temp_vertices.get(vertex as usize).unwrap(), position: *temp_vertices.get(vertex as usize).unwrap(),
normal: *normals.get(normal as usize).unwrap(), normal: *normals.get(normal as usize).unwrap(),
tex_coords: *tex_coords.get(t_coords as usize).unwrap() tex_coords: *tex_coords.get(t_coords as usize).unwrap(),
}); });
} else if vert.matches("/").count() == 1 { } else if vert.matches("/").count() == 1 {
let mut it = vert.split("/"); let mut it = vert.split("/");
@@ -379,7 +412,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
vertices.push(Vertex { vertices.push(Vertex {
position: *temp_vertices.get(vertex as usize).unwrap(), position: *temp_vertices.get(vertex as usize).unwrap(),
normal: calculated_normal, normal: calculated_normal,
tex_coords: *tex_coords.get(t_coords as usize).unwrap() tex_coords: *tex_coords.get(t_coords as usize).unwrap(),
}); });
} else { } else {
let mut vert = vert.parse::<i32>().unwrap(); let mut vert = vert.parse::<i32>().unwrap();
@@ -412,7 +445,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
match new_materials { match new_materials {
Ok(m) => { Ok(m) => {
materials.extend(m); materials.extend(m);
}, }
Err(e) => { Err(e) => {
error!("Failed to parse mtl file {:?}: {}", material_path, e); error!("Failed to parse mtl file {:?}: {}", material_path, e);
} }
@@ -435,7 +468,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
name, name,
vertices: vertices.clone(), vertices: vertices.clone(),
indices: indices.clone(), indices: indices.clone(),
material: current_material.clone() material: current_material.clone(),
}); });
} }
vertices.clear(); vertices.clear();
@@ -452,7 +485,9 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
Some(ObjToken::SmoothShading) => { Some(ObjToken::SmoothShading) => {
// idc about this // idc about this
} }
_ => { warn!("Unhandled obj token: {}", token) }, _ => {
warn!("Unhandled obj token: {}", token)
}
} }
} }
} }
@@ -473,7 +508,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
name: mesh_name, name: mesh_name,
vertices: vertices.clone(), vertices: vertices.clone(),
indices: indices.clone(), indices: indices.clone(),
material: current_material material: current_material,
}); });
let aabb = AABB::new(min_aabb, max_aabb); let aabb = AABB::new(min_aabb, max_aabb);
+25 -11
View File
@@ -1,8 +1,8 @@
use std::io::{Read, Seek, BufReader, BufRead}; use std::io::{BufRead, BufReader, Read, Seek};
use log::trace; use log::trace;
use crate::{importer::ObjMesh, importer::Object, importer::Material, aabb::AABB, mesh::Vertex}; use crate::{aabb::AABB, importer::Material, importer::ObjMesh, importer::Object, mesh::Vertex};
const STL_HEADER_SIZE: u64 = 80; const STL_HEADER_SIZE: u64 = 80;
const STL_TRIANGLE_SIZE: usize = 50; const STL_TRIANGLE_SIZE: usize = 50;
@@ -12,7 +12,7 @@ const STL_TRIANGLE_SIZE: usize = 50;
struct STLTriangle { struct STLTriangle {
normal: glm::Vec3, normal: glm::Vec3,
verts: [glm::Vec3; 3], verts: [glm::Vec3; 3],
attribute_byte_count: u16 attribute_byte_count: u16,
} }
struct FacetIterator<R: Read> { struct FacetIterator<R: Read> {
@@ -40,13 +40,23 @@ impl<R: Read> Iterator for FacetIterator<R> {
Ok(0) => return None, // Reached EOF Ok(0) => return None, // Reached EOF
Ok(_) => { Ok(_) => {
if line.contains("normal") { if line.contains("normal") {
let mut norm_iter = line.split_whitespace().skip(2).map(|s| s.parse::<f32>().unwrap()); let mut norm_iter = line
.split_whitespace()
.skip(2)
.map(|s| s.parse::<f32>().unwrap());
normal.x = norm_iter.next().unwrap(); normal.x = norm_iter.next().unwrap();
normal.y = norm_iter.next().unwrap(); normal.y = norm_iter.next().unwrap();
normal.z = norm_iter.next().unwrap(); normal.z = norm_iter.next().unwrap();
} else if line.contains("vertex") { } else if line.contains("vertex") {
let mut vertex_iter = line.split_whitespace().skip(1).map(|s| s.parse::<f32>().unwrap()); let mut vertex_iter = line
vertices.push(glm::vec3(vertex_iter.next().unwrap(), vertex_iter.next().unwrap(), vertex_iter.next().unwrap())); .split_whitespace()
.skip(1)
.map(|s| s.parse::<f32>().unwrap());
vertices.push(glm::vec3(
vertex_iter.next().unwrap(),
vertex_iter.next().unwrap(),
vertex_iter.next().unwrap(),
));
} else if line.contains("endfacet") { } else if line.contains("endfacet") {
break; break;
} }
@@ -61,8 +71,12 @@ impl<R: Read> Iterator for FacetIterator<R> {
Some(STLTriangle { Some(STLTriangle {
normal, normal,
verts: [*verts_iter.next().unwrap(), *verts_iter.next().unwrap(), *verts_iter.next().unwrap()], verts: [
attribute_byte_count: 0 *verts_iter.next().unwrap(),
*verts_iter.next().unwrap(),
*verts_iter.next().unwrap(),
],
attribute_byte_count: 0,
}) })
} }
} }
@@ -104,7 +118,7 @@ fn parse_ascii_stl(file: std::fs::File) -> Result<Object, Box<dyn std::error::Er
name: "default_mesh".to_string(), name: "default_mesh".to_string(),
vertices, vertices,
indices, indices,
material: Some(Material::default()) material: Some(Material::default()),
}], }],
aabb, aabb,
}) })
@@ -152,7 +166,7 @@ impl<R: Read> Iterator for TrianglesIter<R> {
Some(STLTriangle { Some(STLTriangle {
verts, verts,
normal, normal,
attribute_byte_count: abc attribute_byte_count: abc,
}) })
} }
} }
@@ -202,7 +216,7 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::erro
name: "default_mesh".to_string(), name: "default_mesh".to_string(),
vertices, vertices,
indices, indices,
material: Some(Material::default()) material: Some(Material::default()),
}], }],
aabb, aabb,
}) })
+16 -6
View File
@@ -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 fs4::FileExt;
use log::error; use log::error;
@@ -19,12 +27,14 @@ fn send_args_to_existing_instance(pipe_path: PathBuf, args_paths: Vec<PathBuf>)
let data = bincode::serialize(&args_paths).expect("Failed to serialize arguments"); let data = bincode::serialize(&args_paths).expect("Failed to serialize arguments");
// Send the arguments to the first instance. // Send the arguments to the first instance.
stream stream.write_all(&data).expect("Failed to send arguments");
.write_all(&data)
.expect("Failed to send arguments");
} }
pub fn init(lock_file: &File, args_paths: Vec<PathBuf>, one_instance: bool) -> Option<Receiver<Vec<PathBuf>>> { pub fn init(
lock_file: &File,
args_paths: Vec<PathBuf>,
one_instance: bool,
) -> Option<Receiver<Vec<PathBuf>>> {
if !one_instance { if !one_instance {
return None; return None;
} }
@@ -34,7 +44,7 @@ pub fn init(lock_file: &File, args_paths: Vec<PathBuf>, one_instance: bool) -> O
let pipe_path = std::env::temp_dir().join(pipe_name); let pipe_path = std::env::temp_dir().join(pipe_name);
match lock_file.try_lock_exclusive() { match lock_file.try_lock_exclusive() {
Ok(_) => {}, Ok(_) => {}
Err(_) => { Err(_) => {
println!("An instance of the program is already running"); println!("An instance of the program is already running");
send_args_to_existing_instance(pipe_path, args_paths); send_args_to_existing_instance(pipe_path, args_paths);
+7 -7
View File
@@ -1,14 +1,14 @@
pub mod shader; pub mod aabb;
pub mod camera; pub mod camera;
#[path = "imgui-glfw-support/mod.rs"] #[path = "imgui-glfw-support/mod.rs"]
pub mod imgui_glfw_support; pub mod imgui_glfw_support;
#[path = "imgui-opengl-renderer/mod.rs"] #[path = "imgui-opengl-renderer/mod.rs"]
pub mod imgui_opengl_renderer; 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 importer; pub mod importer;
pub mod ipc; pub mod ipc;
pub mod logger;
pub mod mesh;
pub mod model;
pub mod shader;
pub mod ui;
pub mod utils;
+9 -3
View File
@@ -1,4 +1,7 @@
use std::{io::Write, sync::{Arc, RwLock}}; use std::{
io::Write,
sync::{Arc, RwLock},
};
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum LogLevel { pub enum LogLevel {
@@ -63,12 +66,15 @@ impl Log {
#[derive(Clone)] #[derive(Clone)]
pub struct WritableLog { pub struct WritableLog {
pub arc: Arc<RwLock<Log>>, pub arc: Arc<RwLock<Log>>,
buf: String buf: String,
} }
impl Default for WritableLog { impl Default for WritableLog {
fn default() -> Self { 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),
}
} }
} }
+121 -42
View File
@@ -1,13 +1,13 @@
use std::fs::File;
use std::env; use std::env;
use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use glfw::{Action, Context, Key, Modifiers};
use glad_gl::gl;
use anyhow; use anyhow;
use glad_gl::gl;
use glfw::{Action, Context, Key, Modifiers};
use simplelog::*; use simplelog::*;
use threedobs::{shader, ui::ui, utils, ipc}; use threedobs::{ipc, shader, ui::ui, utils};
fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> { fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
let logger = threedobs::logger::WritableLog::default(); let logger = threedobs::logger::WritableLog::default();
@@ -36,13 +36,17 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
LevelFilter::Info LevelFilter::Info
}; };
CombinedLogger::init( CombinedLogger::init(vec![
vec![ TermLogger::new(
TermLogger::new(log_level, log_conf.clone(), TerminalMode::Mixed, ColorChoice::Auto), log_level,
log_conf.clone(),
TerminalMode::Mixed,
ColorChoice::Auto,
),
WriteLogger::new(log_level, log_conf, File::create("3dobs.log")?), WriteLogger::new(log_level, log_conf, File::create("3dobs.log")?),
WriteLogger::new(log_level, in_program_log_conf, logger.clone()) WriteLogger::new(log_level, in_program_log_conf, logger.clone()),
] ])
).unwrap(); .unwrap();
let settings: ui::Settings = confy::load("3dobs", "settings")?; let settings: ui::Settings = confy::load("3dobs", "settings")?;
@@ -64,7 +68,9 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core); glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core);
glfw::WindowHint::OpenGlForwardCompat(true); 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_all_polling(true);
window.set_cursor_mode(glfw::CursorMode::Disabled); window.set_cursor_mode(glfw::CursorMode::Disabled);
@@ -132,9 +138,18 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
mesh_shader.set_float(&format!("pointLights[{}].linear", i), 0.09); mesh_shader.set_float(&format!("pointLights[{}].linear", i), 0.09);
mesh_shader.set_float(&format!("pointLights[{}].quadratic", i), 0.032); 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(
mesh_shader.set_3fv(&format!("pointLights[{}].diffuse", i), glm::vec3(0.7, 0.7, 0.7)); &format!("pointLights[{}].ambient", i),
mesh_shader.set_3fv(&format!("pointLights[{}].specular", i), glm::vec3(1.0, 1.0, 1.0)); 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.cutOff", glm::cos(glm::radians(12.5)));
mesh_shader.set_float("spotLight.outerCutOff", glm::cos(glm::radians(15.0))); mesh_shader.set_float("spotLight.outerCutOff", glm::cos(glm::radians(15.0)));
@@ -164,7 +179,9 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
delta_time = current_frame - last_frame; delta_time = current_frame - last_frame;
last_frame = current_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); state.camera.update_speed(delta_time);
@@ -176,26 +193,32 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
} }
// camera matrices // camera matrices
let view_mat = glm::ext::look_at(state.camera.position, state.camera.position + state.camera.front, state.camera.up); let view_mat = glm::ext::look_at(
let projection_mat = glm::ext::perspective(glm::radians(state.camera.fov), state.viewport_size[0] / state.viewport_size[1], 0.01, 200.0); 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 { if let Some(rx) = &ipc_rx {
match rx.try_recv() { match rx.try_recv() {
Ok(paths) => { Ok(paths) => {
window.focus(); window.focus();
utils::import_models_from_paths(&paths, &mut state); utils::import_models_from_paths(&paths, &mut state);
}, }
Err(e) => { Err(e) => match e {
match e { std::sync::mpsc::TryRecvError::Empty => {}
std::sync::mpsc::TryRecvError::Empty => {},
std::sync::mpsc::TryRecvError::Disconnected => { std::sync::mpsc::TryRecvError::Disconnected => {
panic!("Error: IPC thread channel disconnected"); panic!("Error: IPC thread channel disconnected");
} }
},
} }
} }
}
}
for (_, event) in glfw::flush_messages(&events) { for (_, event) in glfw::flush_messages(&events) {
// order of handling events is important here // order of handling events is important here
@@ -219,14 +242,22 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
last_x = xpos as f32; last_x = xpos as f32;
last_y = ypos 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 { if window.get_key(glfw::Key::LeftShift) == Action::Press {
state.camera.move_camera(-xoffset, -yoffset); state.camera.move_camera(-xoffset, -yoffset);
} else { } else {
if let Some(active_model) = state.active_model { if let Some(active_model) = state.active_model {
let x_rotation = xoffset * state.camera.sensitivity * state.rotation_speed; let x_rotation =
let y_rotation = yoffset * state.camera.sensitivity * state.rotation_speed; xoffset * state.camera.sensitivity * state.rotation_speed;
let model = state.objects.iter_mut().find(|m| m.id == active_model).unwrap(); 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); // let x_rotation = glm::quat_angle_axis(xoffset * state.camera.sensitivity, &state.camera.up);
model.rotate(x_rotation, y_rotation); model.rotate(x_rotation, y_rotation);
} }
@@ -258,18 +289,27 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
mesh_shader.set_3fv("spotLight.direction", state.camera.front); mesh_shader.set_3fv("spotLight.direction", state.camera.front);
mesh_shader.set_3fv("viewPos", state.camera.position); 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 { for obj in &state.objects {
if state.wireframe { if state.wireframe {
gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE);
} else { } else {
gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); 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); gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
// draw grid // 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 // draw ui
@@ -279,7 +319,15 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
gl::Clear(gl::COLOR_BUFFER_BIT); gl::Clear(gl::COLOR_BUFFER_BIT);
gl::Disable(gl::DEPTH_TEST); gl::Disable(gl::DEPTH_TEST);
gl::Disable(gl::BLEND); 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(); glfw.poll_events();
window.swap_buffers(); 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 { match event {
glfw::WindowEvent::Key(Key::O, _, Action::Press, Modifiers::Control) => { glfw::WindowEvent::Key(Key::O, _, Action::Press, Modifiers::Control) => {
ui::import_model(state); 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; state.camera.speed /= 5.0;
} }
glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Press, _) => { glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Press, _) => {
if !state.can_capture_cursor { return; } if !state.can_capture_cursor {
return;
}
state.is_cursor_captured = true; state.is_cursor_captured = true;
window.set_cursor_mode(glfw::CursorMode::Disabled); window.set_cursor_mode(glfw::CursorMode::Disabled);
} }
glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Release, _) => { glfw::WindowEvent::MouseButton(glfw::MouseButtonLeft, Action::Release, _) => {
if !state.can_capture_cursor { return; } if !state.can_capture_cursor {
return;
}
state.is_cursor_captured = false; state.is_cursor_captured = false;
window.set_cursor_mode(glfw::CursorMode::Normal); window.set_cursor_mode(glfw::CursorMode::Normal);
} }
glfw::WindowEvent::Scroll(_, yoff) => { 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) => { glfw::WindowEvent::FileDrop(paths) => {
utils::import_models_from_paths(paths, state); utils::import_models_from_paths(paths, state);
} }
glfw::WindowEvent::FramebufferSize(w, h) => { glfw::WindowEvent::FramebufferSize(w, h) => unsafe {
unsafe {
gl::Viewport(0, 0, *w, *h); 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_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_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 // renderbuffer for depth
gl::GenRenderbuffers(1, &mut rbo); gl::GenRenderbuffers(1, &mut rbo);
gl::BindRenderbuffer(gl::RENDERBUFFER, rbo); gl::BindRenderbuffer(gl::RENDERBUFFER, rbo);
gl::RenderbufferStorage(gl::RENDERBUFFER, gl::DEPTH24_STENCIL8, w, h); 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 { if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
panic!("ERROR::FRAMEBUFFER:: Framebuffer is not complete!"); panic!("ERROR::FRAMEBUFFER:: Framebuffer is not complete!");
+72 -19
View File
@@ -1,7 +1,11 @@
use glad_gl::gl; use glad_gl::gl;
use log::warn; 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 { fn create_rotation_matrix(pitch: f32, yaw: f32, roll: f32, pivot: glm::Vec3) -> glm::Mat4 {
let pitch = pitch.to_radians(); let pitch = pitch.to_radians();
@@ -45,7 +49,12 @@ pub struct Mesh {
} }
impl Mesh { impl Mesh {
pub fn new(name: &str, vertices: Vec<Vertex>, indices: Vec<u32>, material: Option<Material>) -> Mesh { pub fn new(
name: &str,
vertices: Vec<Vertex>,
indices: Vec<u32>,
material: Option<Material>,
) -> Mesh {
let mut vao = 0; let mut vao = 0;
let mut vbo = 0; let mut vbo = 0;
let mut ebo = 0; let mut ebo = 0;
@@ -58,22 +67,53 @@ impl Mesh {
gl::BindVertexArray(vao); gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(gl::ARRAY_BUFFER, (std::mem::size_of::<Vertex>() * 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::<Vertex>() * 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::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (std::mem::size_of::<u32>() * 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::<u32>() * indices.len() as usize) as isize,
indices.as_ptr() as *const std::ffi::c_void,
gl::STATIC_DRAW,
);
// vertex positions // vertex positions
gl::EnableVertexAttribArray(0); gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, std::ptr::null()); gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
std::ptr::null(),
);
// vertex normals // vertex normals
gl::EnableVertexAttribArray(1); gl::EnableVertexAttribArray(1);
gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, (3 * std::mem::size_of::<f32>()) as *const std::ffi::c_void); gl::VertexAttribPointer(
1,
3,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
(3 * std::mem::size_of::<f32>()) as *const std::ffi::c_void,
);
// vertex texture coords // vertex texture coords
gl::EnableVertexAttribArray(2); gl::EnableVertexAttribArray(2);
gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, (6 * std::mem::size_of::<f32>()) as *const std::ffi::c_void); gl::VertexAttribPointer(
2,
2,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<Vertex>() as i32,
(6 * std::mem::size_of::<f32>()) as *const std::ffi::c_void,
);
gl::BindVertexArray(0); 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 = glm::ext::scale(&utils::mat_ident(), glm::vec3(scale, scale, scale));
let model_mat = apply_rotation(&model_mat, self.rotation, pivot); 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); shader.set_mat4fv("model", &model_mat);
if glm::ext::is_invertible(&model_mat) { if glm::ext::is_invertible(&model_mat) {
let normal_mat = glm::transpose(&glm::inverse(&model_mat)); let normal_mat = glm::transpose(&glm::inverse(&model_mat));
let normal_mat = glm::mat3( let normal_mat = glm::mat3(
normal_mat[0][0], normal_mat[0][1], normal_mat[0][2], normal_mat[0][0],
normal_mat[1][0], normal_mat[1][1], normal_mat[1][2], normal_mat[0][1],
normal_mat[2][0], normal_mat[2][1], normal_mat[2][2], 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_mat3fv("normalMatrix", &normal_mat);
shader.set_bool("useNormalMatrix", true); shader.set_bool("useNormalMatrix", true);
@@ -142,18 +191,18 @@ impl Mesh {
match tex.typ { match tex.typ {
TextureType::Ambient => { TextureType::Ambient => {
shader.set_int("material.texture_ambient", i as i32); shader.set_int("material.texture_ambient", i as i32);
}, }
TextureType::Diffuse => { TextureType::Diffuse => {
shader.set_int("material.texture_diffuse", i as i32); shader.set_int("material.texture_diffuse", i as i32);
}, }
TextureType::Specular => { TextureType::Specular => {
shader.set_int("material.texture_specular", i as i32); shader.set_int("material.texture_specular", i as i32);
}, }
TextureType::Emissive => { TextureType::Emissive => {
shader.set_int("material.texture_emission", i as i32); shader.set_int("material.texture_emission", i as i32);
shader.set_bool("hasEmissionTexture", true); shader.set_bool("hasEmissionTexture", true);
}, }
_ => {}, _ => {}
} }
gl::BindTexture(gl::TEXTURE_2D, tex.id); gl::BindTexture(gl::TEXTURE_2D, tex.id);
@@ -166,7 +215,12 @@ impl Mesh {
unsafe { unsafe {
// draw Mesh // draw Mesh
gl::BindVertexArray(self.vao); 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 // reset stuff to default
gl::ActiveTexture(gl::TEXTURE0); gl::ActiveTexture(gl::TEXTURE0);
@@ -208,8 +262,7 @@ impl Vertex {
Vertex { Vertex {
position, position,
normal, normal,
tex_coords tex_coords,
} }
} }
} }
+29 -5
View File
@@ -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; 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); let scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z);
for mesh in obj.meshes.into_iter() { 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 { 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 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 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 = 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 { for mesh in &self.meshes {
mesh.draw(shader, self.scaling_factor, pivot, show_textures); mesh.draw(shader, self.scaling_factor, pivot, show_textures);
+79 -22
View File
@@ -1,5 +1,5 @@
use glad_gl::gl;
use anyhow::Result; use anyhow::Result;
use glad_gl::gl;
use log::{debug, error}; use log::{debug, error};
pub struct ShaderSource { pub struct ShaderSource {
@@ -12,9 +12,13 @@ pub struct Shader {
} }
impl Shader { impl Shader {
pub fn new(vertex_obj: &mut ShaderSource, frag_obj: &mut ShaderSource) -> Result<Self, Box<dyn std::error::Error>> { pub fn new(
vertex_obj: &mut ShaderSource,
frag_obj: &mut ShaderSource,
) -> Result<Self, Box<dyn std::error::Error>> {
vertex_obj.source.push('\0'); 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'); frag_obj.source.push('\0');
let frag_shader_source = std::ffi::CStr::from_bytes_with_nul(frag_obj.source.as_bytes())?; let frag_shader_source = std::ffi::CStr::from_bytes_with_nul(frag_obj.source.as_bytes())?;
@@ -22,39 +26,65 @@ impl Shader {
unsafe { unsafe {
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
gl::CreateShader(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); gl::CompileShader(vertex_shader);
let mut success1 = 0; let mut success1 = 0;
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success1); gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success1);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
debug!("vertex shader {:?} compiled with status: {}", debug!(
vertex_obj.name, "vertex shader {:?} compiled with status: {}",
success1 vertex_obj.name, success1
); );
if success1 == 0 { if success1 == 0 {
let info_buf = [0u8; 512]; 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)] #[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); let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::CreateShader(frag_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); gl::CompileShader(frag_shader);
let mut success2 = 0; let mut success2 = 0;
gl::GetShaderiv(frag_shader, gl::COMPILE_STATUS, &mut success2); gl::GetShaderiv(frag_shader, gl::COMPILE_STATUS, &mut success2);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
debug!("frag shader {:?} compiled with status: {}", debug!(
frag_obj.name, "frag shader {:?} compiled with status: {}",
success2 frag_obj.name, success2
); );
if success2 == 0 { if success2 == 0 {
let info_buf2 = [0u8; 512]; 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)] #[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(); let shader_program = gl::CreateProgram();
@@ -80,21 +110,30 @@ impl Shader {
pub fn set_bool(&self, name: &str, value: bool) { pub fn set_bool(&self, name: &str, value: bool) {
unsafe { unsafe {
let c_str = std::ffi::CString::new(name).unwrap(); 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) { pub fn set_int(&self, name: &str, value: i32) {
let c_str = std::ffi::CString::new(name).unwrap(); let c_str = std::ffi::CString::new(name).unwrap();
unsafe { 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) { pub fn set_float(&self, name: &str, value: f32) {
let c_str = std::ffi::CString::new(name).unwrap(); let c_str = std::ffi::CString::new(name).unwrap();
unsafe { 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; let mut value = 0.0;
unsafe { 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 value
} }
} }
@@ -111,21 +154,35 @@ impl Shader {
pub fn set_mat3fv(&self, name: &str, value: &glm::Mat3) { pub fn set_mat3fv(&self, name: &str, value: &glm::Mat3) {
let c_str = std::ffi::CString::new(name).unwrap(); let c_str = std::ffi::CString::new(name).unwrap();
unsafe { 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) { pub fn set_mat4fv(&self, name: &str, value: &glm::Mat4) {
let c_str = std::ffi::CString::new(name).unwrap(); let c_str = std::ffi::CString::new(name).unwrap();
unsafe { 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) { pub fn set_3fv(&self, name: &str, value: glm::Vec3) {
let c_str = std::ffi::CString::new(name).unwrap(); let c_str = std::ffi::CString::new(name).unwrap();
unsafe { 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,
);
} }
} }
} }
+147 -43
View File
@@ -1,10 +1,12 @@
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use glad_gl::gl; use glad_gl::gl;
use log::{info, debug}; use log::{debug, info};
use serde::{Serialize, Deserialize}; 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)] #[derive(Default, Debug, Serialize, Deserialize, Clone)]
pub struct Settings { pub struct Settings {
@@ -31,7 +33,7 @@ pub struct State {
pub logger: logger::WritableLog, pub logger: logger::WritableLog,
pub settings: Settings, pub settings: Settings,
pub fps: f32, pub fps: f32,
pub show_textures: bool pub show_textures: bool,
} }
impl Default for State { impl Default for State {
@@ -56,7 +58,7 @@ impl Default for State {
logger: logger::WritableLog::default(), logger: logger::WritableLog::default(),
settings: Settings::default(), settings: Settings::default(),
fps: 0.0, fps: 0.0,
show_textures: true show_textures: true,
} }
} }
} }
@@ -72,29 +74,39 @@ 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(); let mut imgui = imgui::Context::create();
imgui.set_ini_filename(None); imgui.set_ini_filename(None);
imgui.io_mut().config_flags.insert(imgui::ConfigFlags::DOCKING_ENABLE); imgui
imgui.io_mut().config_flags.set(imgui::ConfigFlags::NAV_ENABLE_KEYBOARD, true); .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); let mut glfw_platform = imgui_glfw_support::GlfwPlatform::init(&mut imgui);
glfw_platform.attach_window( glfw_platform.attach_window(
imgui.io_mut(), imgui.io_mut(),
&window, &window,
imgui_glfw_support::HiDpiMode::Default imgui_glfw_support::HiDpiMode::Default,
); );
imgui imgui.fonts().add_font(&[imgui::FontSource::TtfData {
.fonts()
.add_font(&[imgui::FontSource::TtfData {
data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"), data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"),
size_pixels: 20.0, size_pixels: 20.0,
config: Some(imgui::FontConfig { config: Some(imgui::FontConfig {
oversample_h: 3, oversample_h: 3,
pixel_snap_h: true, pixel_snap_h: true,
..imgui::FontConfig::default() ..imgui::FontConfig::default()
}) }),
}]); }]);
imgui.io_mut().font_global_scale = (1.0 / glfw_platform.hidpi_factor()) as f32; imgui.io_mut().font_global_scale = (1.0 / glfw_platform.hidpi_factor()) as f32;
@@ -114,7 +126,8 @@ pub fn import_model(state: &mut State) {
.add_filter("All supported files", &["obj", "OBJ", "stl", "STL"]) .add_filter("All supported files", &["obj", "OBJ", "stl", "STL"])
.add_filter("Wavefront OBJ (.obj)", &["obj", "OBJ"]) .add_filter("Wavefront OBJ (.obj)", &["obj", "OBJ"])
.add_filter("STL (.stl)", &["stl", "STL"]) .add_filter("STL (.stl)", &["stl", "STL"])
.pick_files() { .pick_files()
{
Some(m) => m, Some(m) => m,
None => return, None => return,
}; };
@@ -124,7 +137,11 @@ pub fn import_model(state: &mut State) {
pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window) { pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window) {
ui.main_menu_bar(|| { ui.main_menu_bar(|| {
ui.menu("File", || { 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); import_model(state);
} }
if ui.menu_item_config("Settings").build() { 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", || { 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; 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; state.draw_aabb = !state.draw_aabb;
} }
}); });
ui.menu("Help", || { 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; 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; 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 = state
let mem_fps = format!("Mem: {:.1}MB | FPS: {:.1}", mem / (1024.0 * 1024.0), state.fps); .objects
let avail_size = [*ui.content_region_avail().get(0).unwrap() - ui.calc_text_size(&mem_fps)[0], 0.0]; .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.dummy(avail_size);
ui.text(&mem_fps); ui.text(&mem_fps);
}); });
@@ -168,11 +211,18 @@ fn draw_about_window(ui: &imgui::Ui, state: &mut State) {
.resizable(false) .resizable(false)
.movable(false) .movable(false)
.opened(&mut state.show_help_menu_about) .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]) .position_pivot([0.5, 0.5])
.build(|| { .build(|| {
ui.text("3dobs - 3D Object Browser"); 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.text(format!("{}", env!("CARGO_PKG_DESCRIPTION")));
ui.spacing(); ui.spacing();
ui.spacing(); ui.spacing();
@@ -189,10 +239,16 @@ pub fn draw_settings_window(ui: &imgui::Ui, state: &mut State) {
ui.window("Settings") ui.window("Settings")
.opened(&mut state.show_settings) .opened(&mut state.show_settings)
.movable(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]) .position_pivot([0.5, 0.5])
.build(|| { .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(); 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) .opened(&mut state.show_keybinds)
.resizable(false) .resizable(false)
.movable(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]) .position_pivot([0.5, 0.5])
.build(|| { .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.table_next_column();
ui.text_colored([0.7, 0.7, 0.6, 1.0], "Key"); ui.text_colored([0.7, 0.7, 0.6, 1.0], "Key");
ui.table_next_column(); ui.table_next_column();
@@ -269,7 +334,8 @@ fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) {
} }
fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) { 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.tree_node_config(format!("{}###{}", mesh.name.as_str(), i))
.build(|| {
ui.text(format!("Vertices: {}", mesh.vertices.len())); ui.text(format!("Vertices: {}", mesh.vertices.len()));
ui.text(format!("Triangles: {}", mesh.indices.len() / 3)); ui.text(format!("Triangles: {}", mesh.indices.len() / 3));
ui.tree_node_config(mesh.material.name.as_str()).build(|| { ui.tree_node_config(mesh.material.name.as_str()).build(|| {
@@ -283,14 +349,24 @@ fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) {
fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool { fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool {
ui.table_next_column(); 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.objects[idx].reset_rotation();
state.active_model = Some(state.objects[idx].id); 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.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)) 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(|| { .build(|| {
for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() { for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() {
draw_mesh_hierarchy(ui, mesh, j); draw_mesh_hierarchy(ui, mesh, j);
@@ -312,7 +388,13 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) {
.build(|| { .build(|| {
let mut i = 0; 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 { ui.table_setup_column_with(imgui::TableColumnSetup {
name: "", name: "",
flags: imgui::TableColumnFlags::empty(), 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) { if state.active_model == Some(selected_obj_id) {
let model = state.objects.last_mut().map(|m| m.reset_rotation()); let model = state.objects.last_mut().map(|m| m.reset_rotation());
state.active_model = model.and_then(|o| Some(o.id)); 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; continue;
} }
@@ -415,12 +499,12 @@ fn create_initial_docking(ui: &imgui::Ui, state: &mut State) {
}, },
|down| { |down| {
down.dock_window("Console"); down.dock_window("Console");
} },
) )
}, },
|left| { |left| {
left.dock_window("Viewer"); left.dock_window("Viewer");
} },
) )
} }
}); });
@@ -441,7 +525,9 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) {
state.viewport_size = tex_size; state.viewport_size = tex_size;
if ui.button("Reset Camera") { 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(); ui.same_line();
if ui.button("Capture Scene") { 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]; let mut pixels = vec![0u8; (w * h * 4) as usize];
unsafe { 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() 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"); .expect("Current time to not be before the UNIX epoch");
let file_name = format!("capture-{}.png", timestamp.as_secs()); let file_name = format!("capture-{}.png", timestamp.as_secs());
let save_path = std::path::Path::new(file_name.as_str()); let save_path = std::path::Path::new(file_name.as_str());
let capture = image::ImageBuffer::<image::Rgba<u8>, _>::from_raw(w as u32, h as u32, pixels).unwrap(); let capture =
image::ImageBuffer::<image::Rgba<u8>, _>::from_raw(w as u32, h as u32, pixels)
.unwrap();
let capture = image::DynamicImage::ImageRgba8(capture); let capture = image::DynamicImage::ImageRgba8(capture);
let capture = capture.flipv(); 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 _ = capture.save(save_path);
let elapsed = now.elapsed(); let elapsed = now.elapsed();
info!("Scene capture saved to: {} successfully", save_path info!(
"Scene capture saved to: {} successfully",
save_path
.canonicalize() .canonicalize()
.expect("Capture path to be canonicalized") .expect("Capture path to be canonicalized")
.to_str() .to_str()
.expect("Capture path to be valid unicode")); .expect("Capture path to be valid unicode")
);
debug!("Scene capture took: {}ms", elapsed.as_millis()); debug!("Scene capture took: {}ms", elapsed.as_millis());
@@ -524,7 +626,9 @@ pub fn draw_ui(
last_cursor: &mut Option<imgui::MouseCursor>, last_cursor: &mut Option<imgui::MouseCursor>,
scene_fb_texture: u32, 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(); let ui = imgui.new_frame();
create_initial_docking(ui, state); create_initial_docking(ui, state);
+45 -19
View File
@@ -1,15 +1,15 @@
use std::path::PathBuf; use std::path::PathBuf;
use ::log::{error, info};
use anyhow::{Context, Result};
use glad_gl::gl; use glad_gl::gl;
use glm; use glm;
use anyhow::{Result, Context};
use ::log::{info, error};
use crate::{model, ui, importer}; use crate::{importer, model, ui};
pub enum SupportedFileExtensions { pub enum SupportedFileExtensions {
OBJ, OBJ,
STL STL,
} }
impl SupportedFileExtensions { impl SupportedFileExtensions {
@@ -17,7 +17,7 @@ impl SupportedFileExtensions {
match s.to_ascii_lowercase().as_str() { match s.to_ascii_lowercase().as_str() {
"obj" => Some(Self::OBJ), "obj" => Some(Self::OBJ),
"stl" => Some(Self::STL), "stl" => Some(Self::STL),
_ => None _ => None,
} }
} }
} }
@@ -34,7 +34,7 @@ pub fn load_texture(path: PathBuf) -> Result<u32> {
2 => gl::RG, 2 => gl::RG,
3 => gl::RGB, 3 => gl::RGB,
4 => gl::RGBA, 4 => gl::RGBA,
_ => panic!("Unknown image format") _ => panic!("Unknown image format"),
}; };
unsafe { unsafe {
@@ -44,13 +44,27 @@ pub fn load_texture(path: PathBuf) -> Result<u32> {
gl::GenTextures(1, &mut texture_id); gl::GenTextures(1, &mut texture_id);
gl::BindTexture(gl::TEXTURE_2D, 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_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_S, gl::REPEAT as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, 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::GenerateMipmap(gl::TEXTURE_2D);
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4); gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4);
@@ -61,10 +75,7 @@ pub fn load_texture(path: PathBuf) -> Result<u32> {
pub fn mat_ident() -> glm::Mat4 { pub fn mat_ident() -> glm::Mat4 {
glm::mat4( glm::mat4(
1., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
0., 1., 0., 0.,
0., 0., 1., 0.,
0., 0., 0., 1.
) )
} }
@@ -72,18 +83,27 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
for model_path in paths { for model_path in paths {
let filename = model_path.file_name(); let filename = model_path.file_name();
if model_path.is_dir() { if model_path.is_dir() {
info!("Skipping directory \"{}\"", filename.unwrap().to_str().unwrap()); info!(
"Skipping directory \"{}\"",
filename.unwrap().to_str().unwrap()
);
continue; continue;
} }
match model_path.extension() { match model_path.extension() {
Some(ext) => { Some(ext) => {
if SupportedFileExtensions::from_str(ext.to_str().unwrap()).is_none() { 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; continue;
} }
}, }
None => { 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; continue;
} }
} }
@@ -98,10 +118,16 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
m.name = model_name.to_str().unwrap().to_string(); m.name = model_name.to_str().unwrap().to_string();
} }
state.objects.push(m); 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) => { Err(e) => {
error!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e); error!(
"Error loading model \"{}\": {}",
model_path.to_str().unwrap(),
e
);
} }
} }
} }