mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
ran cargo fmt
This commit is contained in:
+83
-15
@@ -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
@@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
+330
-229
@@ -6,34 +6,30 @@ use std::mem;
|
|||||||
use glad_gl::gl;
|
use glad_gl::gl;
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
program: gl::GLuint,
|
program: gl::GLuint,
|
||||||
locs: Locs,
|
locs: Locs,
|
||||||
vbo: gl::GLuint,
|
vbo: gl::GLuint,
|
||||||
ebo: gl::GLuint,
|
ebo: gl::GLuint,
|
||||||
font_texture: gl::GLuint,
|
font_texture: gl::GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Locs {
|
struct Locs {
|
||||||
texture: gl::GLint,
|
texture: gl::GLint,
|
||||||
proj_mtx: gl::GLint,
|
proj_mtx: gl::GLint,
|
||||||
position: gl::GLuint,
|
position: gl::GLuint,
|
||||||
uv: gl::GLuint,
|
uv: gl::GLuint,
|
||||||
color: gl::GLuint,
|
color: gl::GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(
|
pub fn new(imgui: &mut Context) -> Self {
|
||||||
imgui: &mut Context,
|
unsafe {
|
||||||
) -> Self
|
#[cfg(target_os = "macos")]
|
||||||
{
|
let glsl_version = b"#version 150\n\0";
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
let glsl_version = b"#version 130\n\0";
|
||||||
|
|
||||||
unsafe {
|
let vert_source = b"
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let glsl_version = b"#version 150\n\0";
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let glsl_version = b"#version 130\n\0";
|
|
||||||
|
|
||||||
let vert_source = b"
|
|
||||||
uniform mat4 ProjMtx;
|
uniform mat4 ProjMtx;
|
||||||
in vec2 Position;
|
in vec2 Position;
|
||||||
in vec2 UV;
|
in vec2 UV;
|
||||||
@@ -48,7 +44,7 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
\0";
|
\0";
|
||||||
|
|
||||||
let frag_source = b"
|
let frag_source = b"
|
||||||
uniform sampler2D Texture;
|
uniform sampler2D Texture;
|
||||||
in vec2 Frag_UV;
|
in vec2 Frag_UV;
|
||||||
in vec4 Frag_Color;
|
in vec4 Frag_Color;
|
||||||
@@ -59,236 +55,341 @@ 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,
|
||||||
gl::CompileShader(vert_shader);
|
2,
|
||||||
gl::CompileShader(frag_shader);
|
vert_sources.as_ptr(),
|
||||||
gl::AttachShader(program, vert_shader);
|
vert_sources_len.as_ptr(),
|
||||||
gl::AttachShader(program, frag_shader);
|
);
|
||||||
gl::LinkProgram(program);
|
gl::ShaderSource(
|
||||||
gl::DeleteShader(vert_shader);
|
frag_shader,
|
||||||
gl::DeleteShader(frag_shader);
|
2,
|
||||||
|
frag_sources.as_ptr(),
|
||||||
|
frag_sources_len.as_ptr(),
|
||||||
|
);
|
||||||
|
gl::CompileShader(vert_shader);
|
||||||
|
gl::CompileShader(frag_shader);
|
||||||
|
gl::AttachShader(program, vert_shader);
|
||||||
|
gl::AttachShader(program, frag_shader);
|
||||||
|
gl::LinkProgram(program);
|
||||||
|
gl::DeleteShader(vert_shader);
|
||||||
|
gl::DeleteShader(frag_shader);
|
||||||
|
|
||||||
let locs = Locs{
|
let locs = Locs {
|
||||||
texture: gl::GetUniformLocation(program, b"Texture\0".as_ptr() as _),
|
texture: gl::GetUniformLocation(program, b"Texture\0".as_ptr() as _),
|
||||||
proj_mtx: gl::GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _),
|
proj_mtx: gl::GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _),
|
||||||
position: gl::GetAttribLocation(program, b"Position\0".as_ptr() as _) as _,
|
position: gl::GetAttribLocation(program, b"Position\0".as_ptr() as _) as _,
|
||||||
uv: gl::GetAttribLocation(program, b"UV\0".as_ptr() as _) as _,
|
uv: gl::GetAttribLocation(program, b"UV\0".as_ptr() as _) as _,
|
||||||
color: gl::GetAttribLocation(program, b"Color\0".as_ptr() as _) as _,
|
color: gl::GetAttribLocation(program, b"Color\0".as_ptr() as _) as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
let vbo = return_param(|x| gl::GenBuffers(1, x) );
|
let vbo = return_param(|x| gl::GenBuffers(1, x));
|
||||||
let ebo = return_param(|x| gl::GenBuffers(1, x) );
|
let ebo = return_param(|x| gl::GenBuffers(1, x));
|
||||||
|
|
||||||
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));
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, font_texture);
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
|
||||||
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
|
||||||
|
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
|
||||||
|
|
||||||
let font_texture = return_param(|x| gl::GenTextures(1, x));
|
{
|
||||||
gl::BindTexture(gl::TEXTURE_2D, font_texture);
|
let atlas = imgui.fonts();
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
|
|
||||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
|
|
||||||
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
|
|
||||||
|
|
||||||
{
|
let texture = atlas.build_rgba32_texture();
|
||||||
let atlas = imgui.fonts();
|
gl::TexImage2D(
|
||||||
|
gl::TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl::RGBA as _,
|
||||||
|
texture.width as _,
|
||||||
|
texture.height as _,
|
||||||
|
0,
|
||||||
|
gl::RGBA,
|
||||||
|
gl::UNSIGNED_BYTE,
|
||||||
|
texture.data.as_ptr() as _,
|
||||||
|
);
|
||||||
|
|
||||||
let texture = atlas.build_rgba32_texture();
|
atlas.tex_id = (font_texture as usize).into();
|
||||||
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as _, texture.width as _, texture.height as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, texture.data.as_ptr() as _);
|
}
|
||||||
|
|
||||||
atlas.tex_id = (font_texture as usize).into();
|
gl::BindTexture(gl::TEXTURE_2D, current_texture as _);
|
||||||
}
|
|
||||||
|
Self {
|
||||||
gl::BindTexture(gl::TEXTURE_2D, current_texture as _);
|
program,
|
||||||
|
locs,
|
||||||
Self{
|
vbo,
|
||||||
program,
|
ebo,
|
||||||
locs,
|
font_texture,
|
||||||
vbo,
|
|
||||||
ebo,
|
|
||||||
font_texture,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
&self,
|
|
||||||
ctx: &mut Context,
|
|
||||||
) {
|
|
||||||
use imgui::{DrawVert,DrawIdx,DrawCmd,DrawCmdParams};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x));
|
|
||||||
gl::ActiveTexture(gl::TEXTURE0);
|
|
||||||
let last_program = return_param(|x| gl::GetIntegerv(gl::CURRENT_PROGRAM, x));
|
|
||||||
let last_texture = return_param(|x| gl::GetIntegerv(gl::TEXTURE_BINDING_2D, x));
|
|
||||||
let last_sampler = return_param(|x| gl::GetIntegerv(gl::SAMPLER_BINDING, x));
|
|
||||||
let last_array_buffer = return_param(|x| gl::GetIntegerv(gl::ARRAY_BUFFER_BINDING, x));
|
|
||||||
let last_element_array_buffer = return_param(|x| gl::GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x));
|
|
||||||
let last_vertex_array = return_param(|x| gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, x));
|
|
||||||
let last_polygon_mode = return_param(|x: &mut [gl::GLint; 2]| gl::GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr()));
|
|
||||||
let last_viewport = return_param(|x: &mut [gl::GLint; 4]| gl::GetIntegerv(gl::VIEWPORT, x.as_mut_ptr()));
|
|
||||||
let last_scissor_box = return_param(|x: &mut [gl::GLint; 4]| gl::GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr()));
|
|
||||||
let last_blend_src_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_RGB, x));
|
|
||||||
let last_blend_dst_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_RGB, x));
|
|
||||||
let last_blend_src_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_ALPHA, x));
|
|
||||||
let last_blend_dst_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_ALPHA, x));
|
|
||||||
let last_blend_equation_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_RGB, x));
|
|
||||||
let last_blend_equation_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, x));
|
|
||||||
let last_enable_blend = gl::IsEnabled(gl::BLEND) == gl::TRUE;
|
|
||||||
let last_enable_cull_face = gl::IsEnabled(gl::CULL_FACE) == gl::TRUE;
|
|
||||||
let last_enable_depth_test = gl::IsEnabled(gl::DEPTH_TEST) == gl::TRUE;
|
|
||||||
let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE;
|
|
||||||
|
|
||||||
|
|
||||||
gl::Enable(gl::BLEND);
|
|
||||||
gl::BlendEquation(gl::FUNC_ADD);
|
|
||||||
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
|
|
||||||
gl::Disable(gl::CULL_FACE);
|
|
||||||
gl::Disable(gl::DEPTH_TEST);
|
|
||||||
gl::Enable(gl::SCISSOR_TEST);
|
|
||||||
gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
|
|
||||||
|
|
||||||
|
|
||||||
let [width, height] = ctx.io().display_size;
|
|
||||||
let [scale_w, scale_h] = ctx.io().display_framebuffer_scale;
|
|
||||||
|
|
||||||
let fb_width = width * scale_w;
|
|
||||||
let fb_height = height * scale_h;
|
|
||||||
|
|
||||||
gl::Viewport(0, 0, fb_width as _, fb_height as _);
|
|
||||||
let matrix = [
|
|
||||||
[ 2.0 / width as f32, 0.0, 0.0, 0.0],
|
|
||||||
[ 0.0, 2.0 / -(height as f32), 0.0, 0.0],
|
|
||||||
[ 0.0, 0.0, -1.0, 0.0],
|
|
||||||
[-1.0, 1.0, 0.0, 1.0],
|
|
||||||
];
|
|
||||||
gl::UseProgram(self.program);
|
|
||||||
gl::Uniform1i(self.locs.texture, 0);
|
|
||||||
gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
|
|
||||||
gl::BindSampler(0, 0);
|
|
||||||
|
|
||||||
|
|
||||||
let vao = return_param(|x| gl::GenVertexArrays(1, x));
|
|
||||||
gl::BindVertexArray(vao);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
|
||||||
gl::EnableVertexAttribArray(self.locs.position);
|
|
||||||
gl::EnableVertexAttribArray(self.locs.uv);
|
|
||||||
gl::EnableVertexAttribArray(self.locs.color);
|
|
||||||
gl::VertexAttribPointer(self.locs.position, 2, gl::FLOAT, gl::FALSE, mem::size_of::<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();
|
|
||||||
|
|
||||||
for draw_list in draw_data.draw_lists() {
|
|
||||||
let vtx_buffer = draw_list.vtx_buffer();
|
|
||||||
let idx_buffer = draw_list.idx_buffer();
|
|
||||||
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
|
||||||
gl::BufferData(gl::ARRAY_BUFFER, (vtx_buffer.len() * mem::size_of::<DrawVert>()) as _, vtx_buffer.as_ptr() as _, gl::STREAM_DRAW);
|
|
||||||
|
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
|
||||||
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (idx_buffer.len() * mem::size_of::<DrawIdx>()) as _, idx_buffer.as_ptr() as _, gl::STREAM_DRAW);
|
|
||||||
|
|
||||||
for cmd in draw_list.commands() {
|
|
||||||
match cmd {
|
|
||||||
DrawCmd::Elements {
|
|
||||||
count,
|
|
||||||
cmd_params: DrawCmdParams {
|
|
||||||
clip_rect: [x, y, z, w],
|
|
||||||
texture_id,
|
|
||||||
idx_offset,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
} => {
|
|
||||||
gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _);
|
|
||||||
|
|
||||||
gl::Scissor((x * scale_w) as gl::GLint,
|
|
||||||
(fb_height - w * scale_h) as gl::GLint,
|
|
||||||
((z - x) * scale_w) as gl::GLint,
|
|
||||||
((w - y) * scale_h) as gl::GLint);
|
|
||||||
|
|
||||||
let idx_size = if mem::size_of::<DrawIdx>() == 2 { gl::UNSIGNED_SHORT } else { gl::UNSIGNED_INT };
|
|
||||||
|
|
||||||
gl::DrawElements(gl::TRIANGLES, count as _, idx_size, (idx_offset * mem::size_of::<DrawIdx>()) as _);
|
|
||||||
},
|
|
||||||
DrawCmd::ResetRenderState => {
|
|
||||||
unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet");
|
|
||||||
},
|
|
||||||
DrawCmd::RawCallback { .. } => {
|
|
||||||
unimplemented!("Haven't implemented user callbacks yet");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
gl::DeleteVertexArrays(1, &vao);
|
|
||||||
|
|
||||||
gl::UseProgram(last_program as _);
|
|
||||||
gl::BindTexture(gl::TEXTURE_2D, last_texture as _);
|
|
||||||
gl::BindSampler(0, last_sampler as _);
|
|
||||||
gl::ActiveTexture(last_active_texture as _);
|
|
||||||
gl::BindVertexArray(last_vertex_array as _);
|
|
||||||
gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _);
|
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _);
|
|
||||||
gl::BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _);
|
|
||||||
gl::BlendFuncSeparate(last_blend_src_rgb as _, last_blend_dst_rgb as _, last_blend_src_alpha as _, last_blend_dst_alpha as _);
|
|
||||||
if last_enable_blend { gl::Enable(gl::BLEND) } else { gl::Disable(gl::BLEND) };
|
|
||||||
if last_enable_cull_face { gl::Enable(gl::CULL_FACE) } else { gl::Disable(gl::CULL_FACE) };
|
|
||||||
if last_enable_depth_test { gl::Enable(gl::DEPTH_TEST) } else { gl::Disable(gl::DEPTH_TEST) };
|
|
||||||
if last_enable_scissor_test { gl::Enable(gl::SCISSOR_TEST) } else { gl::Disable(gl::SCISSOR_TEST) };
|
|
||||||
gl::PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _);
|
|
||||||
gl::Viewport(last_viewport[0] as _, last_viewport[1] as _, last_viewport[2] as _, last_viewport[3] as _);
|
|
||||||
gl::Scissor(last_scissor_box[0] as _, last_scissor_box[1] as _, last_scissor_box[2] as _, last_scissor_box[3] as _);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub fn render(&self, ctx: &mut Context) {
|
||||||
|
use imgui::{DrawCmd, DrawCmdParams, DrawIdx, DrawVert};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let last_active_texture = return_param(|x| gl::GetIntegerv(gl::ACTIVE_TEXTURE, x));
|
||||||
|
gl::ActiveTexture(gl::TEXTURE0);
|
||||||
|
let last_program = return_param(|x| gl::GetIntegerv(gl::CURRENT_PROGRAM, x));
|
||||||
|
let last_texture = return_param(|x| gl::GetIntegerv(gl::TEXTURE_BINDING_2D, x));
|
||||||
|
let last_sampler = return_param(|x| gl::GetIntegerv(gl::SAMPLER_BINDING, x));
|
||||||
|
let last_array_buffer = return_param(|x| gl::GetIntegerv(gl::ARRAY_BUFFER_BINDING, x));
|
||||||
|
let last_element_array_buffer =
|
||||||
|
return_param(|x| gl::GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x));
|
||||||
|
let last_vertex_array = return_param(|x| gl::GetIntegerv(gl::VERTEX_ARRAY_BINDING, x));
|
||||||
|
let last_polygon_mode = return_param(|x: &mut [gl::GLint; 2]| {
|
||||||
|
gl::GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr())
|
||||||
|
});
|
||||||
|
let last_viewport = return_param(|x: &mut [gl::GLint; 4]| {
|
||||||
|
gl::GetIntegerv(gl::VIEWPORT, x.as_mut_ptr())
|
||||||
|
});
|
||||||
|
let last_scissor_box = return_param(|x: &mut [gl::GLint; 4]| {
|
||||||
|
gl::GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr())
|
||||||
|
});
|
||||||
|
let last_blend_src_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_RGB, x));
|
||||||
|
let last_blend_dst_rgb = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_RGB, x));
|
||||||
|
let last_blend_src_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_SRC_ALPHA, x));
|
||||||
|
let last_blend_dst_alpha = return_param(|x| gl::GetIntegerv(gl::BLEND_DST_ALPHA, x));
|
||||||
|
let last_blend_equation_rgb =
|
||||||
|
return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_RGB, x));
|
||||||
|
let last_blend_equation_alpha =
|
||||||
|
return_param(|x| gl::GetIntegerv(gl::BLEND_EQUATION_ALPHA, x));
|
||||||
|
let last_enable_blend = gl::IsEnabled(gl::BLEND) == gl::TRUE;
|
||||||
|
let last_enable_cull_face = gl::IsEnabled(gl::CULL_FACE) == gl::TRUE;
|
||||||
|
let last_enable_depth_test = gl::IsEnabled(gl::DEPTH_TEST) == gl::TRUE;
|
||||||
|
let last_enable_scissor_test = gl::IsEnabled(gl::SCISSOR_TEST) == gl::TRUE;
|
||||||
|
|
||||||
|
gl::Enable(gl::BLEND);
|
||||||
|
gl::BlendEquation(gl::FUNC_ADD);
|
||||||
|
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
|
||||||
|
gl::Disable(gl::CULL_FACE);
|
||||||
|
gl::Disable(gl::DEPTH_TEST);
|
||||||
|
gl::Enable(gl::SCISSOR_TEST);
|
||||||
|
gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
|
||||||
|
|
||||||
|
let [width, height] = ctx.io().display_size;
|
||||||
|
let [scale_w, scale_h] = ctx.io().display_framebuffer_scale;
|
||||||
|
|
||||||
|
let fb_width = width * scale_w;
|
||||||
|
let fb_height = height * scale_h;
|
||||||
|
|
||||||
|
gl::Viewport(0, 0, fb_width as _, fb_height as _);
|
||||||
|
let matrix = [
|
||||||
|
[2.0 / width as f32, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 2.0 / -(height as f32), 0.0, 0.0],
|
||||||
|
[0.0, 0.0, -1.0, 0.0],
|
||||||
|
[-1.0, 1.0, 0.0, 1.0],
|
||||||
|
];
|
||||||
|
gl::UseProgram(self.program);
|
||||||
|
gl::Uniform1i(self.locs.texture, 0);
|
||||||
|
gl::UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
|
||||||
|
gl::BindSampler(0, 0);
|
||||||
|
|
||||||
|
let vao = return_param(|x| gl::GenVertexArrays(1, x));
|
||||||
|
gl::BindVertexArray(vao);
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
||||||
|
gl::EnableVertexAttribArray(self.locs.position);
|
||||||
|
gl::EnableVertexAttribArray(self.locs.uv);
|
||||||
|
gl::EnableVertexAttribArray(self.locs.color);
|
||||||
|
gl::VertexAttribPointer(
|
||||||
|
self.locs.position,
|
||||||
|
2,
|
||||||
|
gl::FLOAT,
|
||||||
|
gl::FALSE,
|
||||||
|
mem::size_of::<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();
|
||||||
|
|
||||||
|
for draw_list in draw_data.draw_lists() {
|
||||||
|
let vtx_buffer = draw_list.vtx_buffer();
|
||||||
|
let idx_buffer = draw_list.idx_buffer();
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
||||||
|
gl::BufferData(
|
||||||
|
gl::ARRAY_BUFFER,
|
||||||
|
(vtx_buffer.len() * mem::size_of::<DrawVert>()) as _,
|
||||||
|
vtx_buffer.as_ptr() as _,
|
||||||
|
gl::STREAM_DRAW,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
||||||
|
gl::BufferData(
|
||||||
|
gl::ELEMENT_ARRAY_BUFFER,
|
||||||
|
(idx_buffer.len() * mem::size_of::<DrawIdx>()) as _,
|
||||||
|
idx_buffer.as_ptr() as _,
|
||||||
|
gl::STREAM_DRAW,
|
||||||
|
);
|
||||||
|
|
||||||
|
for cmd in draw_list.commands() {
|
||||||
|
match cmd {
|
||||||
|
DrawCmd::Elements {
|
||||||
|
count,
|
||||||
|
cmd_params:
|
||||||
|
DrawCmdParams {
|
||||||
|
clip_rect: [x, y, z, w],
|
||||||
|
texture_id,
|
||||||
|
idx_offset,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} => {
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, texture_id.id() as _);
|
||||||
|
|
||||||
|
gl::Scissor(
|
||||||
|
(x * scale_w) as gl::GLint,
|
||||||
|
(fb_height - w * scale_h) as gl::GLint,
|
||||||
|
((z - x) * scale_w) as gl::GLint,
|
||||||
|
((w - y) * scale_h) as gl::GLint,
|
||||||
|
);
|
||||||
|
|
||||||
|
let idx_size = if mem::size_of::<DrawIdx>() == 2 {
|
||||||
|
gl::UNSIGNED_SHORT
|
||||||
|
} else {
|
||||||
|
gl::UNSIGNED_INT
|
||||||
|
};
|
||||||
|
|
||||||
|
gl::DrawElements(
|
||||||
|
gl::TRIANGLES,
|
||||||
|
count as _,
|
||||||
|
idx_size,
|
||||||
|
(idx_offset * mem::size_of::<DrawIdx>()) as _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
DrawCmd::ResetRenderState => {
|
||||||
|
unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet");
|
||||||
|
}
|
||||||
|
DrawCmd::RawCallback { .. } => {
|
||||||
|
unimplemented!("Haven't implemented user callbacks yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::DeleteVertexArrays(1, &vao);
|
||||||
|
|
||||||
|
gl::UseProgram(last_program as _);
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, last_texture as _);
|
||||||
|
gl::BindSampler(0, last_sampler as _);
|
||||||
|
gl::ActiveTexture(last_active_texture as _);
|
||||||
|
gl::BindVertexArray(last_vertex_array as _);
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _);
|
||||||
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _);
|
||||||
|
gl::BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _);
|
||||||
|
gl::BlendFuncSeparate(
|
||||||
|
last_blend_src_rgb as _,
|
||||||
|
last_blend_dst_rgb as _,
|
||||||
|
last_blend_src_alpha as _,
|
||||||
|
last_blend_dst_alpha as _,
|
||||||
|
);
|
||||||
|
if last_enable_blend {
|
||||||
|
gl::Enable(gl::BLEND)
|
||||||
|
} else {
|
||||||
|
gl::Disable(gl::BLEND)
|
||||||
|
};
|
||||||
|
if last_enable_cull_face {
|
||||||
|
gl::Enable(gl::CULL_FACE)
|
||||||
|
} else {
|
||||||
|
gl::Disable(gl::CULL_FACE)
|
||||||
|
};
|
||||||
|
if last_enable_depth_test {
|
||||||
|
gl::Enable(gl::DEPTH_TEST)
|
||||||
|
} else {
|
||||||
|
gl::Disable(gl::DEPTH_TEST)
|
||||||
|
};
|
||||||
|
if last_enable_scissor_test {
|
||||||
|
gl::Enable(gl::SCISSOR_TEST)
|
||||||
|
} else {
|
||||||
|
gl::Disable(gl::SCISSOR_TEST)
|
||||||
|
};
|
||||||
|
gl::PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _);
|
||||||
|
gl::Viewport(
|
||||||
|
last_viewport[0] as _,
|
||||||
|
last_viewport[1] as _,
|
||||||
|
last_viewport[2] as _,
|
||||||
|
last_viewport[3] as _,
|
||||||
|
);
|
||||||
|
gl::Scissor(
|
||||||
|
last_scissor_box[0] as _,
|
||||||
|
last_scissor_box[1] as _,
|
||||||
|
last_scissor_box[2] as _,
|
||||||
|
last_scissor_box[3] as _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Renderer {
|
impl Drop for Renderer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::DeleteBuffers(1, &self.vbo);
|
gl::DeleteBuffers(1, &self.vbo);
|
||||||
gl::DeleteBuffers(1, &self.ebo);
|
gl::DeleteBuffers(1, &self.ebo);
|
||||||
|
|
||||||
gl::DeleteProgram(self.program);
|
gl::DeleteProgram(self.program);
|
||||||
|
|
||||||
gl::DeleteTextures(1, &self.font_texture);
|
gl::DeleteTextures(1, &self.font_texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
|
fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
let instance = mem::zeroed::<T>();
|
let instance = mem::zeroed::<T>();
|
||||||
|
|
||||||
let offset = {
|
let offset = {
|
||||||
let field: &U = f(&instance);
|
let field: &U = f(&instance);
|
||||||
field as *const U as usize - &instance as *const T as usize
|
field as *const U as usize - &instance as *const T as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
mem::forget(instance);
|
mem::forget(instance);
|
||||||
|
|
||||||
offset
|
offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_param<T, F>(f: F) -> T where F: FnOnce(&mut T) {
|
fn return_param<T, F>(f: F) -> T
|
||||||
let mut val = unsafe{ mem::zeroed() };
|
where
|
||||||
f(&mut val);
|
F: FnOnce(&mut T),
|
||||||
val
|
{
|
||||||
|
let mut val = unsafe { mem::zeroed() };
|
||||||
|
f(&mut val);
|
||||||
|
val
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-14
@@ -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)?;
|
||||||
|
|||||||
+76
-41
@@ -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();
|
||||||
@@ -226,11 +258,11 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !vertices.is_empty() {
|
if !vertices.is_empty() {
|
||||||
meshes.push(ObjMesh{
|
meshes.push(ObjMesh {
|
||||||
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();
|
||||||
@@ -248,11 +280,11 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !vertices.is_empty() {
|
if !vertices.is_empty() {
|
||||||
meshes.push(ObjMesh{
|
meshes.push(ObjMesh {
|
||||||
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],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +365,10 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
} else {
|
} else {
|
||||||
normal -= 1;
|
normal -= 1;
|
||||||
}
|
}
|
||||||
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("/");
|
||||||
@@ -357,10 +390,10 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
} else {
|
} else {
|
||||||
normal -= 1;
|
normal -= 1;
|
||||||
}
|
}
|
||||||
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("/");
|
||||||
@@ -376,10 +409,10 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
} else {
|
} else {
|
||||||
t_coords -= 1;
|
t_coords -= 1;
|
||||||
}
|
}
|
||||||
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();
|
||||||
@@ -388,7 +421,7 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
} else {
|
} else {
|
||||||
vert -= 1;
|
vert -= 1;
|
||||||
}
|
}
|
||||||
vertices.push(Vertex{
|
vertices.push(Vertex {
|
||||||
position: *temp_vertices.get(vert as usize).unwrap(),
|
position: *temp_vertices.get(vert as usize).unwrap(),
|
||||||
normal: calculated_normal,
|
normal: calculated_normal,
|
||||||
tex_coords: glm::vec2(0.0, 0.0),
|
tex_coords: glm::vec2(0.0, 0.0),
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -431,11 +464,11 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !vertices.is_empty() {
|
if !vertices.is_empty() {
|
||||||
meshes.push(ObjMesh{
|
meshes.push(ObjMesh {
|
||||||
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,12 +485,14 @@ 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
trace!("Loaded in {}ms", elapsed.as_millis());
|
trace!("Loaded in {}ms", elapsed.as_millis());
|
||||||
|
|
||||||
let mesh_name = {
|
let mesh_name = {
|
||||||
if current_mesh_name.is_empty() && !object_name.is_empty() {
|
if current_mesh_name.is_empty() && !object_name.is_empty() {
|
||||||
@@ -469,16 +504,16 @@ pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<d
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
meshes.push(ObjMesh{
|
meshes.push(ObjMesh {
|
||||||
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);
|
||||||
|
|
||||||
Ok(Object{
|
Ok(Object {
|
||||||
name: object_name,
|
name: object_name,
|
||||||
meshes,
|
meshes,
|
||||||
aabb,
|
aabb,
|
||||||
|
|||||||
+36
-22
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -59,10 +69,14 @@ impl<R: Read> Iterator for FacetIterator<R> {
|
|||||||
|
|
||||||
let mut verts_iter = vertices.iter();
|
let mut verts_iter = vertices.iter();
|
||||||
|
|
||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +95,7 @@ fn parse_ascii_stl(file: std::fs::File) -> Result<Object, Box<dyn std::error::Er
|
|||||||
|
|
||||||
for (i, triangle) in facet_iter.enumerate() {
|
for (i, triangle) in facet_iter.enumerate() {
|
||||||
for vert in triangle.verts {
|
for vert in triangle.verts {
|
||||||
vertices.push(Vertex{
|
vertices.push(Vertex {
|
||||||
position: glm::vec3(vert.x, vert.y, vert.z),
|
position: glm::vec3(vert.x, vert.y, vert.z),
|
||||||
normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z),
|
normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z),
|
||||||
tex_coords,
|
tex_coords,
|
||||||
@@ -98,13 +112,13 @@ fn parse_ascii_stl(file: std::fs::File) -> Result<Object, Box<dyn std::error::Er
|
|||||||
|
|
||||||
let aabb = AABB::new(min_aabb, max_aabb);
|
let aabb = AABB::new(min_aabb, max_aabb);
|
||||||
|
|
||||||
Ok(Object{
|
Ok(Object {
|
||||||
name: "default_object".to_string(),
|
name: "default_object".to_string(),
|
||||||
meshes: vec![ObjMesh{
|
meshes: vec![ObjMesh {
|
||||||
name: "default_mesh".to_string(),
|
name: "default_mesh".to_string(),
|
||||||
vertices,
|
vertices,
|
||||||
indices,
|
indices,
|
||||||
material: Some(Material::default())
|
material: Some(Material::default()),
|
||||||
}],
|
}],
|
||||||
aabb,
|
aabb,
|
||||||
})
|
})
|
||||||
@@ -149,10 +163,10 @@ impl<R: Read> Iterator for TrianglesIter<R> {
|
|||||||
];
|
];
|
||||||
let abc = u16::from_le_bytes([self.buf[48], self.buf[49]]);
|
let abc = u16::from_le_bytes([self.buf[48], self.buf[49]]);
|
||||||
|
|
||||||
Some(STLTriangle{
|
Some(STLTriangle {
|
||||||
verts,
|
verts,
|
||||||
normal,
|
normal,
|
||||||
attribute_byte_count: abc
|
attribute_byte_count: abc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,7 +193,7 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::erro
|
|||||||
let tex_coords = glm::vec2(0.0, 0.0);
|
let tex_coords = glm::vec2(0.0, 0.0);
|
||||||
for (i, triangle) in triangles_reader.enumerate() {
|
for (i, triangle) in triangles_reader.enumerate() {
|
||||||
for vert in triangle.verts {
|
for vert in triangle.verts {
|
||||||
vertices.push(Vertex{
|
vertices.push(Vertex {
|
||||||
position: glm::vec3(vert.x, vert.y, vert.z),
|
position: glm::vec3(vert.x, vert.y, vert.z),
|
||||||
normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z),
|
normal: glm::vec3(triangle.normal.x, triangle.normal.y, triangle.normal.z),
|
||||||
tex_coords,
|
tex_coords,
|
||||||
@@ -189,20 +203,20 @@ fn parse_binary_stl(mut file: std::fs::File) -> Result<Object, Box<dyn std::erro
|
|||||||
max_aabb = glm::max(max_aabb, vert);
|
max_aabb = glm::max(max_aabb, vert);
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.push(i as u32*3);
|
indices.push(i as u32 * 3);
|
||||||
indices.push(i as u32*3 + 1);
|
indices.push(i as u32 * 3 + 1);
|
||||||
indices.push(i as u32*3 + 2);
|
indices.push(i as u32 * 3 + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let aabb = AABB::new(min_aabb, max_aabb);
|
let aabb = AABB::new(min_aabb, max_aabb);
|
||||||
|
|
||||||
Ok(Object{
|
Ok(Object {
|
||||||
name: "default_object".to_string(),
|
name: "default_object".to_string(),
|
||||||
meshes: vec![ObjMesh{
|
meshes: vec![ObjMesh {
|
||||||
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
@@ -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);
|
||||||
|
|||||||
+11
-11
@@ -1,14 +1,14 @@
|
|||||||
pub mod shader;
|
|
||||||
pub mod camera;
|
|
||||||
#[path="imgui-glfw-support/mod.rs"]
|
|
||||||
pub mod imgui_glfw_support;
|
|
||||||
#[path="imgui-opengl-renderer/mod.rs"]
|
|
||||||
pub mod imgui_opengl_renderer;
|
|
||||||
pub mod utils;
|
|
||||||
pub mod mesh;
|
|
||||||
pub mod model;
|
|
||||||
pub mod ui;
|
|
||||||
pub mod logger;
|
|
||||||
pub mod aabb;
|
pub mod aabb;
|
||||||
|
pub mod camera;
|
||||||
|
#[path = "imgui-glfw-support/mod.rs"]
|
||||||
|
pub mod imgui_glfw_support;
|
||||||
|
#[path = "imgui-opengl-renderer/mod.rs"]
|
||||||
|
pub mod imgui_opengl_renderer;
|
||||||
pub mod importer;
|
pub mod 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
@@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+131
-52
@@ -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,13 +68,15 @@ 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);
|
||||||
window.make_current();
|
window.make_current();
|
||||||
|
|
||||||
let mut state = ui::State{
|
let mut state = ui::State {
|
||||||
settings,
|
settings,
|
||||||
logger,
|
logger,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -81,25 +87,25 @@ fn main() -> anyhow::Result<(), Box<dyn std::error::Error>> {
|
|||||||
let (mut imgui, glfw_platform, renderer) = ui::init_imgui(&mut window);
|
let (mut imgui, glfw_platform, renderer) = ui::init_imgui(&mut window);
|
||||||
|
|
||||||
let mesh_shader = shader::Shader::new(
|
let mesh_shader = shader::Shader::new(
|
||||||
&mut shader::ShaderSource{
|
&mut shader::ShaderSource {
|
||||||
name: "vertex.glsl".to_string(),
|
name: "vertex.glsl".to_string(),
|
||||||
source: include_str!("../shaders/vertex.glsl").to_string(),
|
source: include_str!("../shaders/vertex.glsl").to_string(),
|
||||||
},
|
},
|
||||||
&mut shader::ShaderSource{
|
&mut shader::ShaderSource {
|
||||||
name: "frag.glsl".to_string(),
|
name: "frag.glsl".to_string(),
|
||||||
source: include_str!("../shaders/frag.glsl").to_string(),
|
source: include_str!("../shaders/frag.glsl").to_string(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let grid_shader = shader::Shader::new(
|
let grid_shader = shader::Shader::new(
|
||||||
&mut shader::ShaderSource{
|
&mut shader::ShaderSource {
|
||||||
name: "grid_v.glsl".to_string(),
|
name: "grid_v.glsl".to_string(),
|
||||||
source: include_str!("../shaders/grid_v.glsl").to_string(),
|
source: include_str!("../shaders/grid_v.glsl").to_string(),
|
||||||
},
|
},
|
||||||
&mut shader::ShaderSource{
|
&mut shader::ShaderSource {
|
||||||
name: "grid_f.glsl".to_string(),
|
name: "grid_f.glsl".to_string(),
|
||||||
source: include_str!("../shaders/grid_f.glsl").to_string(),
|
source: include_str!("../shaders/grid_f.glsl").to_string(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let points_lights: [glm::Vec3; 4] = [
|
let points_lights: [glm::Vec3; 4] = [
|
||||||
glm::vec3(0.7, 0.2, 2.0),
|
glm::vec3(0.7, 0.2, 2.0),
|
||||||
@@ -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,27 +193,33 @@ 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) => {
|
|
||||||
match e {
|
|
||||||
std::sync::mpsc::TryRecvError::Empty => {},
|
|
||||||
std::sync::mpsc::TryRecvError::Disconnected => {
|
|
||||||
panic!("Error: IPC thread channel disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Err(e) => match e {
|
||||||
|
std::sync::mpsc::TryRecvError::Empty => {}
|
||||||
|
std::sync::mpsc::TryRecvError::Disconnected => {
|
||||||
|
panic!("Error: IPC thread channel disconnected");
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (_, event) in glfw::flush_messages(&events) {
|
for (_, event) in glfw::flush_messages(&events) {
|
||||||
// order of handling events is important here
|
// order of handling events is important here
|
||||||
// we need to handle window events first to have an updated
|
// we need to handle window events first to have an updated
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-69
@@ -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,30 +74,40 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_imgui(window: &mut glfw::Window) -> (imgui::Context, imgui_glfw_support::GlfwPlatform, imgui_opengl_renderer::Renderer) {
|
pub fn init_imgui(
|
||||||
|
window: &mut glfw::Window,
|
||||||
|
) -> (
|
||||||
|
imgui::Context,
|
||||||
|
imgui_glfw_support::GlfwPlatform,
|
||||||
|
imgui_opengl_renderer::Renderer,
|
||||||
|
) {
|
||||||
let mut imgui = imgui::Context::create();
|
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()
|
data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"),
|
||||||
.add_font(&[imgui::FontSource::TtfData {
|
size_pixels: 20.0,
|
||||||
data: include_bytes!("../../assets/fonts/Exo-Regular.ttf"),
|
config: Some(imgui::FontConfig {
|
||||||
size_pixels: 20.0,
|
oversample_h: 3,
|
||||||
config: Some(imgui::FontConfig {
|
pixel_snap_h: true,
|
||||||
oversample_h: 3,
|
..imgui::FontConfig::default()
|
||||||
pixel_snap_h: true,
|
}),
|
||||||
..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,17 +126,22 @@ 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,
|
{
|
||||||
None => return,
|
Some(m) => m,
|
||||||
};
|
None => return,
|
||||||
|
};
|
||||||
utils::import_models_from_paths(&models, state);
|
utils::import_models_from_paths(&models, 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,33 +334,44 @@ fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) {
|
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))
|
||||||
ui.text(format!("Vertices: {}", mesh.vertices.len()));
|
.build(|| {
|
||||||
ui.text(format!("Triangles: {}", mesh.indices.len() / 3));
|
ui.text(format!("Vertices: {}", mesh.vertices.len()));
|
||||||
ui.tree_node_config(mesh.material.name.as_str()).build(|| {
|
ui.text(format!("Triangles: {}", mesh.indices.len() / 3));
|
||||||
ui.text(format!("{}", mesh.material));
|
ui.tree_node_config(mesh.material.name.as_str()).build(|| {
|
||||||
|
ui.text(format!("{}", mesh.material));
|
||||||
|
});
|
||||||
|
ui.tree_node_config("Transformations").build(|| {
|
||||||
|
draw_transformations(ui, mesh);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
ui.tree_node_config("Transformations").build(|| {
|
|
||||||
draw_transformations(ui, mesh);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_object_hierarchy(ui: &imgui::Ui, state: &mut State, idx: usize) -> bool {
|
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!(
|
||||||
.build(|| {
|
"{} ({:.1}MB)###{}",
|
||||||
for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() {
|
state.objects[idx].name.as_str(),
|
||||||
draw_mesh_hierarchy(ui, mesh, j);
|
state.objects[idx].mem_usage as f32 / (1024.0 * 1024.0),
|
||||||
}
|
idx
|
||||||
});
|
))
|
||||||
|
.build(|| {
|
||||||
|
for (j, mesh) in &mut state.objects[idx].meshes.iter_mut().enumerate() {
|
||||||
|
draw_mesh_hierarchy(ui, mesh, j);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ui.table_next_column();
|
ui.table_next_column();
|
||||||
if ui.small_button(format!("X###{}-{}", state.objects[idx].name.as_str(), idx)) {
|
if ui.small_button(format!("X###{}-{}", state.objects[idx].name.as_str(), idx)) {
|
||||||
@@ -312,7 +388,13 @@ fn draw_objects_window(ui: &imgui::Ui, state: &mut State) {
|
|||||||
.build(|| {
|
.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!(
|
||||||
.canonicalize()
|
"Scene capture saved to: {} successfully",
|
||||||
.expect("Capture path to be canonicalized")
|
save_path
|
||||||
.to_str()
|
.canonicalize()
|
||||||
.expect("Capture path to be valid unicode"));
|
.expect("Capture path to be canonicalized")
|
||||||
|
.to_str()
|
||||||
|
.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
@@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user