various QOL improvements and bug fixes

This commit is contained in:
2023-10-17 01:14:56 +02:00
parent 99363b2fc5
commit 2ff98a52bc
10 changed files with 549 additions and 463 deletions
+11 -13
View File
@@ -29,22 +29,20 @@ impl Camera {
}
}
pub fn handle_mouse_scroll(&mut self, yoffset: f32, can_capture_cursor: bool) {
pub fn handle_mouse_scroll(&mut self, yoffset: f32, can_capture_cursor: bool, fov_zoom: bool) {
if !can_capture_cursor { return }
self.fov -= yoffset;
if fov_zoom {
self.fov -= yoffset;
if self.fov <= 0.5 {
self.fov = 0.5;
if self.fov <= 0.5 {
self.fov = 0.5;
}
if self.fov >= 85.0 {
self.fov = 85.0;
}
} else {
self.position = self.position + (self.front * self._speed) + glm::vec3(0.0, 0.0, -yoffset);
}
if self.fov >= 45.0 {
self.fov = 45.0;
}
// if yoffset > 0.0 {
// self.position = self.position + (self.front * self._speed) + glm::vec3(0.0, 0.0, yoffset);
// } else {
// self.position = self.position - (self.front * self._speed) + glm::vec3(0.0, 0.0, yoffset);
// }
}
pub fn move_camera(&mut self, xoffset: f32, yoffset: f32) {
+39 -6
View File
@@ -2,7 +2,7 @@ use glfw::{Action, Context, Key, Modifiers};
use glad_gl::gl;
use anyhow;
use threedobs::{shader, ui::ui};
use threedobs::{shader, ui::ui, utils};
fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS)?;
@@ -23,9 +23,36 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
let (mut imgui, glfw_platform, renderer) = ui::init_imgui(&mut window);
let mesh_shader = shader::Shader::new("shaders/vertex.glsl", "shaders/frag.glsl")?;
let light_shader = shader::Shader::new("shaders/vertex.glsl", "shaders/light_f.glsl")?;
let grid_shader = shader::Shader::new("shaders/grid_v.glsl", "shaders/grid_f.glsl")?;
let mesh_shader = shader::Shader::new(
&mut shader::ShaderSource{
name: "vertex.glsl".to_string(),
source: include_str!("../shaders/vertex.glsl").to_string(),
},
&mut shader::ShaderSource{
name: "frag.glsl".to_string(),
source: include_str!("../shaders/frag.glsl").to_string(),
},
)?;
let light_shader = shader::Shader::new(
&mut shader::ShaderSource{
name: "vertex.glsl".to_string(),
source: include_str!("../shaders/vertex.glsl").to_string(),
},
&mut shader::ShaderSource{
name: "light_f.glsl".to_string(),
source: include_str!("../shaders/light_f.glsl").to_string(),
},
)?;
let grid_shader = shader::Shader::new(
&mut shader::ShaderSource{
name: "grid_v.glsl".to_string(),
source: include_str!("../shaders/grid_v.glsl").to_string(),
},
&mut shader::ShaderSource{
name: "grid_f.glsl".to_string(),
source: include_str!("../shaders/grid_f.glsl").to_string(),
},
)?;
let vertices: [f32; 288] = [
// positions // normals // texture coords
@@ -198,7 +225,10 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
} else {
if let Some(active_model) = state.active_model {
let model = state.objects.iter_mut().find(|m| m.id == active_model).unwrap();
model.rotate(xoffset * state.camera.sensitivity, yoffset * state.camera.sensitivity);
// let x_rotation = glm::quat_angle_axis(xoffset * state.camera.sensitivity, &state.camera.up);
let x_rotation = xoffset * state.camera.sensitivity * state.rotation_speed;
let y_rotation = yoffset * state.camera.sensitivity * state.rotation_speed;
model.rotate(x_rotation, y_rotation);
}
}
}
@@ -313,7 +343,10 @@ fn handle_window_event(window: &mut glfw::Window, event: &glfw::WindowEvent, sta
window.set_cursor_mode(glfw::CursorMode::Normal);
}
glfw::WindowEvent::Scroll(_, yoff) => {
state.camera.handle_mouse_scroll(*yoff as f32, state.can_capture_cursor);
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 {
+38 -20
View File
@@ -84,6 +84,8 @@ pub struct Mesh {
pub material: Material,
vao: u32,
vbo: u32,
ebo: u32,
}
impl Mesh {
@@ -128,7 +130,9 @@ impl Mesh {
indices,
textures,
material,
vbo,
vao,
ebo,
position,
rotation,
scale,
@@ -137,8 +141,8 @@ impl Mesh {
}
pub fn draw(&self, shader: &Shader, scale: f32, pivot: glm::Vec3) {
let mut diffuse = 1;
let mut specular = 1;
// let mut diffuse = 1;
// let mut specular = 1;
shader.use_shader();
@@ -152,25 +156,25 @@ impl Mesh {
shader.set_3fv("material.specular", self.material.specular);
shader.set_float("material.shininess", self.material.shininess);
for i in 0..self.textures.len() {
unsafe {
gl::ActiveTexture(gl::TEXTURE0 + i as u32);
match self.textures[i].typ {
russimp::material::TextureType::Diffuse => {
diffuse += 1;
shader.set_int(format!("material.texture_diffuse{}", diffuse).as_str(), i as i32);
},
russimp::material::TextureType::Specular => {
specular += 1;
shader.set_int(format!("material.texture_specular{}", specular).as_str(), i as i32);
},
_ => {}, // don't do anything because unsupported texture types are logged
// when the model is loaded
}
// for i in 0..self.textures.len() {
// unsafe {
// gl::ActiveTexture(gl::TEXTURE0 + i as u32);
// match self.textures[i].typ {
// russimp::material::TextureType::Diffuse => {
// diffuse += 1;
// shader.set_int(format!("material.texture_diffuse{}", diffuse).as_str(), i as i32);
// },
// russimp::material::TextureType::Specular => {
// specular += 1;
// shader.set_int(format!("material.texture_specular{}", specular).as_str(), i as i32);
// },
// _ => {}, // don't do anything because unsupported texture types are logged
// // when the model is loaded
// }
gl::BindTexture(gl::TEXTURE_2D, self.textures[i].id);
}
}
// gl::BindTexture(gl::TEXTURE_2D, self.textures[i].id);
// }
// }
unsafe {
// draw Mesh
@@ -186,8 +190,22 @@ impl Mesh {
pub fn rotate(&mut self, rotation: glm::Vec3) {
self.rotation = self.rotation + rotation;
}
pub fn reset_rotation(&mut self) {
self.rotation = glm::vec3(0.0, 0.0, 0.0);
}
}
impl Drop for Mesh {
fn drop(&mut self) {
unsafe {
gl::BindVertexArray(0);
gl::DeleteBuffers(1, &self.vbo);
gl::DeleteBuffers(1, &self.ebo);
gl::DeleteVertexArrays(1, &self.vao);
}
}
}
#[derive(Clone, Debug)]
#[repr(packed(2))]
+14 -6
View File
@@ -137,11 +137,11 @@ fn process_mesh(
}
fn process_material(mat: &russimp::material::Material) -> Material {
let mut mat_name = String::from("Default_Mat");
let mut ambient = glm::vec3(0.2, 0.2, 0.2);
let mut diffuse = glm::vec3(0.7, 0.7, 0.7);
let mut specular = glm::vec3(0.1, 0.1, 0.1);
let mut shininess = 32.0;
let mat_name = String::from("Default_Mat");
let ambient = glm::vec3(0.2, 0.2, 0.2);
let diffuse = glm::vec3(0.7, 0.7, 0.7);
let specular = glm::vec3(0.1, 0.1, 0.1);
let shininess = 32.0;
// TODO: better way of mapping properties
// for property in mat.properties.iter() {
@@ -316,10 +316,18 @@ impl Model {
}
}
pub fn rotate(&mut self, xoffset: f32, yoffset: f32) {
pub fn rotate(&mut self, xoffset: f32, yoffset: f32) -> &mut Self {
let rotation = glm::vec3(-yoffset, xoffset, 0.0);
for mesh in &mut self.meshes {
mesh.rotate(rotation);
}
self
}
pub fn reset_rotation(&mut self) -> &mut Self {
for mesh in &mut self.meshes {
mesh.reset_rotation();
}
self
}
}
+17 -10
View File
@@ -1,19 +1,22 @@
use glad_gl::gl;
use anyhow::{Context, Result};
use anyhow::Result;
pub struct ShaderSource {
pub name: String,
pub source: String,
}
pub struct Shader {
pub program_id: gl::GLuint,
}
impl Shader {
pub fn new(vertex_path: &str, frag_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let mut vertex_shader_source = std::fs::read_to_string(vertex_path).with_context(|| format!("Failed to read vertex shader file: {}", vertex_path))?;
vertex_shader_source.push('\0');
let vertex_shader_source = std::ffi::CStr::from_bytes_with_nul(vertex_shader_source.as_bytes())?;
pub fn new(vertex_obj: &mut ShaderSource, frag_obj: &mut ShaderSource) -> Result<Self, Box<dyn std::error::Error>> {
vertex_obj.source.push('\0');
let vertex_shader_source = std::ffi::CStr::from_bytes_with_nul(vertex_obj.source.as_bytes())?;
let mut frag_shader_source = std::fs::read_to_string(frag_path).with_context(|| format!("Failed to read frag shader file: {}", frag_path))?;
frag_shader_source.push('\0');
let frag_shader_source = std::ffi::CStr::from_bytes_with_nul(frag_shader_source.as_bytes())?;
frag_obj.source.push('\0');
let frag_shader_source = std::ffi::CStr::from_bytes_with_nul(frag_obj.source.as_bytes())?;
unsafe {
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
@@ -22,13 +25,15 @@ impl Shader {
gl::CompileShader(vertex_shader);
let mut success1 = 0;
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success1);
#[cfg(debug_assertions)]
println!("vertex shader {:?} compiled with status: {}",
std::path::Path::new(vertex_path).file_name().unwrap(),
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);
#[cfg(debug_assertions)]
println!("vertex shader info: {}", std::str::from_utf8(&info_buf).unwrap());
}
@@ -39,13 +44,15 @@ impl Shader {
let mut success2 = 0;
gl::GetShaderiv(frag_shader, gl::COMPILE_STATUS, &mut success2);
#[cfg(debug_assertions)]
println!("frag shader {:?} compiled with status: {}",
std::path::Path::new(frag_path).file_name().unwrap(),
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);
#[cfg(debug_assertions)]
println!("frag shader info: {}", std::str::from_utf8(&info_buf2).unwrap());
}
+38 -24
View File
@@ -1,6 +1,6 @@
use glad_gl::gl;
use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, log};
use crate::{camera::Camera, model, imgui_glfw_support, imgui_opengl_renderer, mesh, ui, log, utils};
use std::time::{SystemTime, UNIX_EPOCH};
@@ -12,6 +12,8 @@ pub struct State {
pub can_capture_cursor: bool,
pub draw_grid: bool,
pub draw_aabb: bool,
pub fov_zoom: bool,
pub rotation_speed: f32,
pub wireframe: bool,
pub first_frame_drawn: bool,
pub camera: Camera,
@@ -30,6 +32,8 @@ impl Default for State {
can_capture_cursor: false,
draw_grid: false,
draw_aabb: false,
fov_zoom: true,
rotation_speed: 1.0,
show_help_menu_about: false,
wireframe: false,
camera: Camera::new(),
@@ -89,22 +93,7 @@ pub fn import_model(state: &mut State) {
Some(m) => m,
None => return,
};
for model_path in &models {
let model = model::Model::new(model_path.to_str().unwrap(), state);
match model {
Ok(m) => {
state.active_model = Some(m.id);
state.objects.push(m);
state.camera.update_position(state.active_model, &state.objects);
},
Err(e) => {
let error = format!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e);
println!("{}", error);
state.logger.log(&error, log::LogLevel::Error);
},
}
}
utils::import_models_from_paths(&models, state);
}
pub fn draw_main_menu_bar(ui: &imgui::Ui, state: &mut State, window: &mut glfw::Window, delta_time: f32) {
@@ -197,6 +186,7 @@ fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool
return true;
}
if ui.checkbox("Selected", &mut (Some(state.objects[idx].id) == state.active_model)) {
state.objects[idx].reset_rotation();
state.active_model = Some(state.objects[idx].id);
state.camera.update_position(state.active_model, &state.objects);
}
@@ -213,9 +203,13 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) {
while i < state.objects.len() {
if draw_object_hierarchy(ui, state, i) {
let selected_obj_id = state.objects[i].id;
state.objects.remove(i);
state.active_model = state.objects.last().and_then(|o| Some(o.id));
state.camera.update_position(state.active_model, &state.objects);
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.update_position(state.active_model, &state.objects);
}
continue;
}
@@ -277,10 +271,18 @@ fn create_initial_docking(ui: &imgui::Ui, state: &mut State) {
if !state.first_frame_drawn {
space.split(
imgui::Direction::Right,
0.3,
300.0 / ui.io().display_size[0],
|right| {
right.dock_window("Objects");
right.dock_window("Console");
right.split(
imgui::Direction::Up,
0.6,
|up| {
up.dock_window("Objects");
},
|down| {
down.dock_window("Console");
}
)
},
|left| {
left.dock_window("Viewer");
@@ -297,6 +299,7 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) {
ui.window("Viewer")
.size(ui.content_region_avail(), imgui::Condition::FirstUseEver)
.no_decoration()
.scrollable(!state.can_capture_cursor)
.resizable(true)
.build(|| {
let mut tex_size = ui.content_region_avail();
@@ -360,12 +363,21 @@ fn draw_viewport(ui: &imgui::Ui, state: &mut State, texture: u32) {
ui.same_line();
ui.checkbox("Bounding box", &mut state.draw_aabb);
ui.same_line();
ui.set_next_item_width(200.0);
ui.checkbox("FOV zoom", &mut state.fov_zoom);
ui.same_line();
ui.set_next_item_width(150.0);
imgui::Drag::new("Camera Speed")
.range(1.0, 10000.0)
.speed(1.0)
.display_format("%.3f")
.build(ui, &mut state.camera.speed);
ui.same_line();
ui.set_next_item_width(150.0);
imgui::Drag::new("Rotation Speed")
.range(0.1, 100.0)
.speed(0.5)
.display_format("%.3f")
.build(ui, &mut state.rotation_speed);
imgui::Image::new(imgui::TextureId::new(texture.try_into().unwrap()), tex_size)
// flip the image vertically
.uv0([0.0, 1.0])
@@ -398,6 +410,8 @@ pub fn draw_ui(
if state.show_help_menu_about {
ui.window("About")
.size([300.0, 150.0], imgui::Condition::FirstUseEver)
.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_pivot([0.5, 0.5])
@@ -417,7 +431,7 @@ pub fn draw_ui(
ui.end_frame_early();
if !state.is_cursor_captured {
if !state.can_capture_cursor {
let cursor = ui.mouse_cursor();
if *last_cursor != cursor {
*last_cursor = cursor;
+39
View File
@@ -1,7 +1,13 @@
use std::path::PathBuf;
use glad_gl::gl;
use glm;
use anyhow::{Result, Context};
use crate::{ui, log, model};
const SUPPORTED_FILE_EXTENSIONS: [&str; 2] = ["obj", "stl"];
pub fn load_texture(path: &str) -> Result<u32> {
let tex = image::io::Reader::open(path)
.with_context(|| format!("Failed to open texture file: {}", path))?
@@ -43,3 +49,36 @@ pub fn mat_ident() -> glm::Mat4 {
)
}
pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State) {
for model_path in paths {
let filename = model_path.file_name();
if model_path.is_dir() {
state.logger.log(&format!("Skipping directory \"{}\"", filename.unwrap().to_str().unwrap()), log::LogLevel::Info);
continue;
}
if let Some(ext) = model_path.extension() {
if !SUPPORTED_FILE_EXTENSIONS.contains(&ext.to_ascii_lowercase().to_str().unwrap()) {
state.logger.log(&format!("Skipping file \"{}\" because it is not an OBJ or STL file", filename.unwrap().to_str().unwrap()), log::LogLevel::Info);
continue;
}
}
let model = model::Model::new(model_path.to_str().unwrap(), state);
match model {
Ok(mut m) => {
state.active_model = Some(m.id);
if let Some(model_name) = filename {
state.logger.log(&format!("Loaded model \"{}\"", model_name.to_str().unwrap()), log::LogLevel::Info);
m.name = model_name.to_str().unwrap().to_string();
}
state.objects.push(m);
state.camera.update_position(state.active_model, &state.objects);
},
Err(e) => {
let error = format!("Error loading model \"{}\": {}", model_path.to_str().unwrap(), e);
println!("{}", error);
state.logger.log(&error, log::LogLevel::Error);
},
}
}
}