This commit is contained in:
Piotr 2020-09-23 15:49:30 +02:00
parent f30d198463
commit dd6b42e55d
10 changed files with 395 additions and 306 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
[![builds.sr.ht status](https://builds.sr.ht/~leinnan/doppler/commits/.build.yml.svg)](https://builds.sr.ht/~leinnan/doppler/commits/.build.yml?)
My OpenGL "engine". WIP

View File

@ -4,7 +4,7 @@ use imgui_inspect_derive::Inspect;
#[derive(Inspect)]
pub struct BgInfo {
#[inspect_slider(min_value = 0.0, max_value = 1.0)]
pub r : f32,
pub r: f32,
#[inspect_slider(min_value = 0.0, max_value = 1.0)]
pub g: f32,
#[inspect_slider(min_value = 0.0, max_value = 1.0)]
@ -13,10 +13,10 @@ pub struct BgInfo {
impl Default for BgInfo {
fn default() -> Self {
BgInfo{
BgInfo {
r: 0.1,
g: 0.2,
b: 0.4
b: 0.4,
}
}
}
}

View File

@ -29,7 +29,7 @@ const ZOOM: f32 = 45.0;
pub struct Camera {
// Camera Attributes
pub Position: Point3,
pub position: Point3,
pub Front: Vector3,
pub Up: Vector3,
pub Right: Vector3,
@ -46,7 +46,7 @@ pub struct Camera {
impl Default for Camera {
fn default() -> Camera {
let mut camera = Camera {
Position: Point3::new(0.0, 0.0, 0.0),
position: Point3::new(0.0, 0.0, 0.0),
Front: vec3(0.0, 0.0, -1.0),
Up: Vector3::zero(), // initialized later
Right: Vector3::zero(), // initialized later
@ -65,23 +65,23 @@ impl Default for Camera {
impl Camera {
/// Returns the view matrix calculated using Eular Angles and the LookAt Matrix
pub fn get_view_matrix(&self) -> Matrix4 {
Matrix4::look_at(self.Position, self.Position + self.Front, self.Up)
Matrix4::look_at(self.position, self.position + self.Front, self.Up)
}
/// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
pub fn process_keyboard(&mut self, direction: Camera_Movement, deltaTime: f32) {
let velocity = self.MovementSpeed * deltaTime;
if direction == FORWARD {
self.Position += self.Front * velocity;
self.position += self.Front * velocity;
}
if direction == BACKWARD {
self.Position += -(self.Front * velocity);
self.position += -(self.Front * velocity);
}
if direction == LEFT {
self.Position += -(self.Right * velocity);
self.position += -(self.Right * velocity);
}
if direction == RIGHT {
self.Position += self.Right * velocity;
self.position += self.Right * velocity;
}
}

265
src/gaia/engine.rs Normal file
View File

@ -0,0 +1,265 @@
use crate::gaia::bg_info::BgInfo;
use crate::gaia::camera::*;
use crate::gaia::consts;
use crate::gaia::*;
use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3};
use imgui_glfw_rs::glfw;
use imgui_glfw_rs::glfw::{Action, Context, Key};
use imgui_glfw_rs::imgui;
use imgui_glfw_rs::ImguiGLFW;
use imgui_inspect::InspectArgsStruct;
pub struct Engine {
pub camera: Camera,
pub bg_info: BgInfo,
pub window: imgui_glfw_rs::glfw::Window,
pub window_size: (f32, f32),
pub events: std::sync::mpsc::Receiver<(f64, imgui_glfw_rs::glfw::WindowEvent)>,
pub glfw: imgui_glfw_rs::glfw::Glfw,
pub imgui: imgui::Context,
pub imgui_glfw: ImguiGLFW,
pub shader: shader::Shader,
pub models: Vec<model::Model>,
}
impl Engine {
pub fn run(&mut self) {
let mut first_mouse = true;
let mut last_x: f32 = consts::SCR_WIDTH as f32 / 2.0;
let mut last_y: f32 = consts::SCR_HEIGHT as f32 / 2.0;
// timing
let mut delta_time: f32; // time between current frame and last frame
let mut last_frame: f32 = 0.0;
{
// build and compile shaders
// -------------------------
self.shader = shader::Shader::from_file(
"resources/shaders/model_loading.vs",
"resources/shaders/model_loading.fs",
);
// load models
// -----------
for _ in 0..10 {
self.models.push(model::Model::new("resources/objects/nanosuit/nanosuit.obj"));
}
};
// render loop
// -----------
while !self.window.should_close() {
// per-frame time logic
// --------------------
let cur_frame = self.glfw.get_time() as f32;
delta_time = cur_frame - last_frame;
last_frame = cur_frame;
// input
// -----
let skip_input =
self.imgui.io().want_capture_mouse || self.imgui.io().want_capture_keyboard;
if !skip_input {
self.process_input(delta_time);
}
// render
// ------
unsafe {
gl::ClearColor(self.bg_info.r, self.bg_info.g, self.bg_info.b, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
self.shader.useProgram();
// view/projection transformations
let projection: Matrix4<f32> = perspective(
Deg(self.camera.Zoom),
self.window_size.0 / self.window_size.1,
0.1,
100.0,
);
let view = self.camera.get_view_matrix();
self.shader.setMat4(c_str!("projection"), &projection);
self.shader.setMat4(c_str!("view"), &view);
let mut i = 0;
for m in &self.models {
// render the loaded model
let mut model = Matrix4::<f32>::from_translation(vec3(0.0, -1.75, -1.25 * (i as f32))); // translate it down so it's at the center of the scene
model = model * Matrix4::from_scale(0.2); // it's a bit too big for our scene, so scale it down
self.shader.setMat4(c_str!("model"), &model);
m.Draw(&self.shader);
i = i + 1;
}
}
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
let ui = self.imgui_glfw.frame(&mut self.window, &mut self.imgui);
{
use imgui::*;
Window::new(im_str!("Hello world"))
.size([300.0, 110.0], Condition::FirstUseEver)
.build(&ui, || {
ui.text(im_str!("Hello world!"));
ui.text(im_str!("こんにちは世界!"));
ui.text(im_str!("This...is...imgui-rs!"));
ui.separator();
ui.text(format!("Mouse position: ({:.1},{:.1})", last_x, last_y));
// let selected = vec![&self.bg_info];
// <BgInfo as imgui_inspect::InspectRenderStruct<BgInfo>>::render(
// &selected,
// "Example Struct - Read Only",
// &ui,
// &InspectArgsStruct::default(),
// );
// let mut selected_mut = vec![&mut self.bg_info];
// <BgInfo as imgui_inspect::InspectRenderStruct<BgInfo>>::render_mut(
// &mut selected_mut,
// "Example Struct - Writable",
// &ui,
// &InspectArgsStruct::default(),
// );
});
}
self.imgui_glfw.draw(ui, &mut self.window);
self.window.swap_buffers();
self.glfw.poll_events();
// events
// -----
self.process_events(&mut first_mouse, &mut last_x, &mut last_y, skip_input);
}
}
pub fn process_input(&mut self, delta_time: f32) {
if self.window.get_key(Key::Escape) == Action::Press {
self.window.set_should_close(true)
}
if self.window.get_key(Key::W) == Action::Press {
self.camera
.process_keyboard(Camera_Movement::FORWARD, delta_time);
}
if self.window.get_key(Key::S) == Action::Press {
self.camera
.process_keyboard(Camera_Movement::BACKWARD, delta_time);
}
if self.window.get_key(Key::A) == Action::Press {
self.camera
.process_keyboard(Camera_Movement::LEFT, delta_time);
}
if self.window.get_key(Key::D) == Action::Press {
self.camera
.process_keyboard(Camera_Movement::RIGHT, delta_time);
}
self.camera
.enable_mouse_movement(self.window.get_key(Key::LeftControl) != Action::Press);
}
pub fn process_events(
&mut self,
first_mouse: &mut bool,
last_x: &mut f32,
last_y: &mut f32,
skip_input: bool,
) {
for (_, event) in glfw::flush_messages(&self.events) {
self.imgui_glfw.handle_event(&mut self.imgui, &event);
match event {
glfw::WindowEvent::FramebufferSize(width, height) => {
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
unsafe { gl::Viewport(0, 0, width, height) }
self.window_size = (width as f32, height as f32);
}
glfw::WindowEvent::CursorPos(xpos, ypos) => {
if skip_input {
return;
}
let (xpos, ypos) = (xpos as f32, ypos as f32);
if *first_mouse {
*last_x = xpos;
*last_y = ypos;
*first_mouse = false;
}
let xoffset = xpos - *last_x;
let yoffset = *last_y - ypos; // reversed since y-coordinates go from bottom to top
*last_x = xpos;
*last_y = ypos;
self.camera.process_mouse_movement(xoffset, yoffset, true);
}
glfw::WindowEvent::Scroll(_xoffset, yoffset) => {
if skip_input {
return;
}
self.camera.process_mouse_scroll(yoffset as f32);
}
_ => {}
}
}
}
}
impl Default for Engine {
fn default() -> Self {
// glfw: initialize and configure
// ------------------------------
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(
glfw::OpenGlProfileHint::Core,
));
#[cfg(target_os = "macos")]
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
// glfw window creation
// --------------------
let (mut window, events) = glfw
.create_window(
consts::SCR_WIDTH,
consts::SCR_HEIGHT,
"chRustedGL",
glfw::WindowMode::Windowed,
)
.expect("Failed to create GLFW window");
window.make_current();
window.set_all_polling(true);
window.set_framebuffer_size_polling(true);
window.set_cursor_pos_polling(true);
window.set_scroll_polling(true);
// tell GLFW to capture our mouse
window.set_cursor_mode(glfw::CursorMode::Disabled);
// gl: load all OpenGL function pointers
// ---------------------------------------
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let mut imgui = imgui::Context::create();
let mut imgui_glfw = ImguiGLFW::new(&mut imgui, &mut window);
// configure global opengl state
// -----------------------------
unsafe{gl::Enable(gl::DEPTH_TEST);}
Engine {
camera: Camera {
position: Point3::new(0.0, 0.0, 3.0),
..Camera::default()
},
bg_info: BgInfo::default(),
window: window,
window_size: (consts::SCR_WIDTH as f32, consts::SCR_HEIGHT as f32),
events: events,
glfw: glfw,
imgui: imgui,
imgui_glfw: imgui_glfw,
shader: shader::Shader::default(),
models: vec![],
}
}
}

View File

@ -6,11 +6,11 @@ use std::mem::size_of;
use std::os::raw::c_void;
use std::ptr;
use cgmath::{ Vector3, Vector2 };
use cgmath::prelude::*;
use cgmath::{Vector2, Vector3};
use gl;
use crate::shader::Shader;
use crate::gaia::shader::Shader;
// NOTE: without repr(C) the compiler may reorder the fields or use different padding/alignment than C.
// Depending on how you pass the data to OpenGL, this may be bad. In this case it's not strictly
@ -18,7 +18,7 @@ use crate::shader::Shader;
#[repr(C)]
pub struct Vertex {
// position
pub Position: Vector3<f32>,
pub position: Vector3<f32>,
// normal
pub Normal: Vector3<f32>,
// texCoords
@ -32,7 +32,7 @@ pub struct Vertex {
impl Default for Vertex {
fn default() -> Self {
Vertex {
Position: Vector3::zero(),
position: Vector3::zero(),
Normal: Vector3::zero(),
TexCoords: Vector2::zero(),
Tangent: Vector3::zero(),
@ -63,8 +63,12 @@ pub struct Mesh {
impl Mesh {
pub fn new(vertices: Vec<Vertex>, indices: Vec<u32>, textures: Vec<Texture>) -> Mesh {
let mut mesh = Mesh {
vertices, indices, textures,
VAO: 0, VBO: 0, EBO: 0
vertices,
indices,
textures,
VAO: 0,
VBO: 0,
EBO: 0,
};
// now that we have all the required data, set the vertex buffers and its attribute pointers.
@ -75,19 +79,19 @@ impl Mesh {
/// render the mesh
pub unsafe fn Draw(&self, shader: &Shader) {
// bind appropriate textures
let mut diffuseNr = 0;
let mut diffuseNr = 0;
let mut specularNr = 0;
let mut normalNr = 0;
let mut heightNr = 0;
let mut normalNr = 0;
let mut heightNr = 0;
for (i, texture) in self.textures.iter().enumerate() {
gl::ActiveTexture(gl::TEXTURE0 + i as u32); // active proper texture unit before binding
// retrieve texture number (the N in diffuse_textureN)
// retrieve texture number (the N in diffuse_textureN)
let name = &texture.type_;
let number = match name.as_str() {
"texture_diffuse" => {
diffuseNr += 1;
diffuseNr
},
}
"texture_specular" => {
specularNr += 1;
specularNr
@ -100,18 +104,26 @@ impl Mesh {
heightNr += 1;
heightNr
}
_ => panic!("unknown texture type")
_ => panic!("unknown texture type"),
};
// now set the sampler to the correct texture unit
let sampler = CString::new(format!("{}{}", name, number)).unwrap();
gl::Uniform1i(gl::GetUniformLocation(shader.ID, sampler.as_ptr()), i as i32);
gl::Uniform1i(
gl::GetUniformLocation(shader.ID, sampler.as_ptr()),
i as i32,
);
// and finally bind the texture
gl::BindTexture(gl::TEXTURE_2D, texture.id);
}
// draw mesh
gl::BindVertexArray(self.VAO);
gl::DrawElements(gl::TRIANGLES, self.indices.len() as i32, gl::UNSIGNED_INT, ptr::null());
gl::DrawElements(
gl::TRIANGLES,
self.indices.len() as i32,
gl::UNSIGNED_INT,
ptr::null(),
);
gl::BindVertexArray(0);
// always good practice to set everything back to defaults once configured.
@ -143,19 +155,54 @@ impl Mesh {
let size = size_of::<Vertex>() as i32;
// vertex Positions
gl::EnableVertexAttribArray(0);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Position) as *const c_void);
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
size,
offset_of!(Vertex, position) as *const c_void,
);
// vertex normals
gl::EnableVertexAttribArray(1);
gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Normal) as *const c_void);
gl::VertexAttribPointer(
1,
3,
gl::FLOAT,
gl::FALSE,
size,
offset_of!(Vertex, Normal) as *const c_void,
);
// vertex texture coords
gl::EnableVertexAttribArray(2);
gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, TexCoords) as *const c_void);
gl::VertexAttribPointer(
2,
2,
gl::FLOAT,
gl::FALSE,
size,
offset_of!(Vertex, TexCoords) as *const c_void,
);
// vertex tangent
gl::EnableVertexAttribArray(3);
gl::VertexAttribPointer(3, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Tangent) as *const c_void);
gl::VertexAttribPointer(
3,
3,
gl::FLOAT,
gl::FALSE,
size,
offset_of!(Vertex, Tangent) as *const c_void,
);
// vertex bitangent
gl::EnableVertexAttribArray(4);
gl::VertexAttribPointer(4, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, Bitangent) as *const c_void);
gl::VertexAttribPointer(
4,
3,
gl::FLOAT,
gl::FALSE,
size,
offset_of!(Vertex, Bitangent) as *const c_void,
);
gl::BindVertexArray(0);
}

View File

@ -3,7 +3,8 @@ pub mod consts;
pub mod utils;
#[macro_use]
pub mod macros;
pub mod shader;
pub mod model;
pub mod bg_info;
pub mod engine;
pub mod mesh;
pub mod bg_info;
pub mod model;
pub mod shader;

View File

@ -11,15 +11,15 @@ use image::DynamicImage::*;
use image::GenericImage;
use tobj;
use crate::mesh::{ Mesh, Texture, Vertex };
use crate::shader::Shader;
use crate::utils::*;
use crate::gaia::mesh::{Mesh, Texture, Vertex};
use crate::gaia::shader::Shader;
use crate::gaia::utils::*;
// #[derive(Default)]
pub struct Model {
/* Model Data */
pub meshes: Vec<Mesh>,
pub textures_loaded: Vec<Texture>, // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.
pub textures_loaded: Vec<Texture>, // stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.
directory: String,
}
@ -27,10 +27,15 @@ impl Model {
/// constructor, expects a filepath to a 3D model.
pub fn new(path: &str) -> Model {
let pathObj = Path::new(path);
let mut model = Model{
let mut model = Model {
meshes: Vec::<Mesh>::new(),
textures_loaded: Vec::<Texture>::new(),
directory: pathObj.parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().into()
directory: pathObj
.parent()
.unwrap_or_else(|| Path::new(""))
.to_str()
.unwrap()
.into(),
};
model.loadModel(path);
model
@ -38,7 +43,9 @@ impl Model {
pub fn Draw(&self, shader: &Shader) {
for mesh in &self.meshes {
unsafe { mesh.Draw(shader); }
unsafe {
mesh.Draw(shader);
}
}
}
@ -48,7 +55,12 @@ impl Model {
println!("Started loading model from path: {}", path.display());
// retrieve the directory path of the filepath
self.directory = path.parent().unwrap_or_else(|| Path::new("")).to_str().unwrap().into();
self.directory = path
.parent()
.unwrap_or_else(|| Path::new(""))
.to_str()
.unwrap()
.into();
let obj = tobj::load_obj(path, true);
let (models, materials) = obj.unwrap();
@ -63,9 +75,9 @@ impl Model {
let (p, n, t) = (&mesh.positions, &mesh.normals, &mesh.texcoords);
for i in 0..num_vertices {
vertices.push(Vertex {
Position: vec3(p[i*3], p[i*3+1], p[i*3+2]),
Normal: vec3(n[i*3], n[i*3+1], n[i*3+2]),
TexCoords: vec2(t[i*2], t[i*2+1]),
position: vec3(p[i * 3], p[i * 3 + 1], p[i * 3 + 2]),
Normal: vec3(n[i * 3], n[i * 3 + 1], n[i * 3 + 2]),
TexCoords: vec2(t[i * 2], t[i * 2 + 1]),
..Vertex::default()
})
}
@ -77,17 +89,20 @@ impl Model {
// 1. diffuse map
if !material.diffuse_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.diffuse_texture, "texture_diffuse");
let texture =
self.loadMaterialTexture(&material.diffuse_texture, "texture_diffuse");
textures.push(texture);
}
// 2. specular map
if !material.specular_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.specular_texture, "texture_specular");
let texture =
self.loadMaterialTexture(&material.specular_texture, "texture_specular");
textures.push(texture);
}
// 3. normal map
if !material.normal_texture.is_empty() {
let texture = self.loadMaterialTexture(&material.normal_texture, "texture_normal");
let texture =
self.loadMaterialTexture(&material.normal_texture, "texture_normal");
textures.push(texture);
}
// NOTE: no height maps
@ -96,7 +111,6 @@ impl Model {
self.meshes.push(Mesh::new(vertices, indices, textures));
}
println!("Finished loading model from path: {}", path.display());
}
fn loadMaterialTexture(&mut self, path: &str, typeName: &str) -> Texture {
@ -108,12 +122,11 @@ impl Model {
}
let texture = Texture {
id: unsafe { loadTextureFromDir(path, &self.directory) },
id: unsafe { load_texture_from_dir(path, &self.directory) },
type_: typeName.into(),
path: path.into()
path: path.into(),
};
self.textures_loaded.push(texture.clone());
texture
}
}

View File

@ -8,9 +8,9 @@ use std::str;
use gl;
use gl::types::*;
use crate::gaia::consts;
use cgmath::prelude::*;
use cgmath::{Matrix, Matrix4, Vector3};
use crate::gaia::consts;
pub struct Shader {
pub ID: u32,

View File

@ -9,11 +9,11 @@ use image::DynamicImage::*;
use image::GenericImage;
use image::*;
pub unsafe fn loadTexture(path: &str) -> u32 {
pub unsafe fn load_texture(path: &str) -> u32 {
println!("Loading texture from path: {}", path);
let mut textureID = 0;
let mut id = 0;
gl::GenTextures(1, &mut textureID);
gl::GenTextures(1, &mut id);
let img = image::open(&Path::new(path)).expect("Texture failed to load");
let format = match img {
ImageLuma8(_) => gl::RED,
@ -26,7 +26,7 @@ pub unsafe fn loadTexture(path: &str) -> u32 {
let data = img.raw_pixels();
let dim = img.dimensions();
gl::BindTexture(gl::TEXTURE_2D, textureID);
gl::BindTexture(gl::TEXTURE_2D, id);
gl::TexImage2D(
gl::TEXTURE_2D,
0,
@ -49,11 +49,11 @@ pub unsafe fn loadTexture(path: &str) -> u32 {
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
textureID
id
}
pub unsafe fn loadTextureFromDir(filename: &str, directory: &str) -> u32 {
pub unsafe fn load_texture_from_dir(filename: &str, directory: &str) -> u32 {
let fullpath = format!("{}/{}", directory, filename);
loadTexture(&fullpath)
}
load_texture(&fullpath)
}

View File

@ -1,255 +1,15 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
extern crate gl;
extern crate imgui_glfw_rs;
extern crate image;
// Use the reexported glfw crate to avoid version conflicts.
use imgui_glfw_rs::glfw;
// Use the reexported imgui crate to avoid version conflicts.
use imgui_glfw_rs::imgui;
use imgui_glfw_rs::ImguiGLFW;
use imgui_inspect::InspectArgsStruct;
use self::gl::types::*;
use imgui_glfw_rs::glfw::{Action, Context, Key};
use cgmath::prelude::*;
use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3};
extern crate imgui_glfw_rs;
use human_panic::setup_panic;
#[macro_use]
mod gaia;
use crate::gaia::camera::*;
use crate::gaia::*;
use crate::gaia::bg_info::BgInfo;
use crate::gaia::engine::Engine;
pub fn main() {
setup_panic!();
let mut camera = Camera {
Position: Point3::new(0.0, 0.0, 3.0),
..Camera::default()
};
let mut engine = Engine::default();
let mut first_mouse = true;
let mut last_x: f32 = consts::SCR_WIDTH as f32 / 2.0;
let mut last_y: f32 = consts::SCR_HEIGHT as f32 / 2.0;
// timing
let mut delta_time: f32; // time between current frame and last frame
let mut last_frame: f32 = 0.0;
// glfw: initialize and configure
// ------------------------------
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(
glfw::OpenGlProfileHint::Core,
));
#[cfg(target_os = "macos")]
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
// glfw window creation
// --------------------
let (mut window, events) = glfw
.create_window(
consts::SCR_WIDTH,
consts::SCR_HEIGHT,
"chRustedGL",
glfw::WindowMode::Windowed,
)
.expect("Failed to create GLFW window");
window.make_current();
window.set_all_polling(true);
window.set_framebuffer_size_polling(true);
window.set_cursor_pos_polling(true);
window.set_scroll_polling(true);
// tell GLFW to capture our mouse
window.set_cursor_mode(glfw::CursorMode::Disabled);
// gl: load all OpenGL function pointers
// ---------------------------------------
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let (ourShader, ourModel) = unsafe {
// configure global opengl state
// -----------------------------
gl::Enable(gl::DEPTH_TEST);
// build and compile shaders
// -------------------------
let ourShader = shader::Shader::from_file(
"resources/shaders/model_loading.vs",
"resources/shaders/model_loading.fs");
// load models
// -----------
let ourModel = model::Model::new("resources/objects/nanosuit/nanosuit.obj");
// draw in wireframe
// gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE);
(ourShader, ourModel)
};
let mut imgui = imgui::Context::create();
let mut imgui_glfw = ImguiGLFW::new(&mut imgui, &mut window);
// render loop
// -----------
let mut bg = BgInfo::default();
while !window.should_close() {
// per-frame time logic
// --------------------
let cur_frame = glfw.get_time() as f32;
delta_time = cur_frame - last_frame;
last_frame = cur_frame;
// input
// -----
let skip_input = imgui.io().want_capture_mouse || imgui.io().want_capture_keyboard;
if !skip_input {
process_input(&mut window, delta_time, &mut camera);
}
// render
// ------
unsafe {
gl::ClearColor(bg.r, bg.g, bg.b, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
ourShader.useProgram();
// view/projection transformations
let projection: Matrix4<f32> = perspective(Deg(camera.Zoom), consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32, 0.1, 100.0);
let view = camera.get_view_matrix();
ourShader.setMat4(c_str!("projection"), &projection);
ourShader.setMat4(c_str!("view"), &view);
// render the loaded model
let mut model = Matrix4::<f32>::from_translation(vec3(0.0, -1.75, 0.0)); // translate it down so it's at the center of the scene
model = model * Matrix4::from_scale(0.2); // it's a bit too big for our scene, so scale it down
ourShader.setMat4(c_str!("model"), &model);
ourModel.Draw(&ourShader);
}
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
let ui = imgui_glfw.frame(&mut window, &mut imgui);
{
use imgui::*;
Window::new(im_str!("Hello world"))
.size([300.0, 110.0], Condition::FirstUseEver)
.build(&ui, || {
ui.text(im_str!("Hello world!"));
ui.text(im_str!("こんにちは世界!"));
ui.text(im_str!("This...is...imgui-rs!"));
ui.separator();
ui.text(format!(
"Mouse Position: ({:.1},{:.1})",
last_x, last_y
));
let selected = vec![&bg];
<BgInfo as imgui_inspect::InspectRenderStruct<
BgInfo,
>>::render(
&selected,
"Example Struct - Read Only",
&ui,
&InspectArgsStruct::default(),
);
let mut selected_mut = vec![&mut bg];
<BgInfo as imgui_inspect::InspectRenderStruct<
BgInfo,
>>::render_mut(
&mut selected_mut,
"Example Struct - Writable",
&ui,
&InspectArgsStruct::default(),
);
});
}
imgui_glfw.draw(ui, &mut window);
window.swap_buffers();
glfw.poll_events();
// events
// -----
for (_, event) in glfw::flush_messages(&events) {
imgui_glfw.handle_event(&mut imgui, &event);
process_events(
event,
&mut first_mouse,
&mut last_x,
&mut last_y,
&mut camera,
skip_input
);
}
}
}
pub fn process_events(
event: imgui_glfw_rs::glfw::WindowEvent,
first_mouse: &mut bool,
last_x: &mut f32,
last_y: &mut f32,
camera: &mut Camera,
skip_input: bool
) {
match event {
glfw::WindowEvent::FramebufferSize(width, height) => {
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
unsafe { gl::Viewport(0, 0, width, height) }
}
glfw::WindowEvent::CursorPos(xpos, ypos) => {
if skip_input { return; }
let (xpos, ypos) = (xpos as f32, ypos as f32);
if *first_mouse {
*last_x = xpos;
*last_y = ypos;
*first_mouse = false;
}
let xoffset = xpos - *last_x;
let yoffset = *last_y - ypos; // reversed since y-coordinates go from bottom to top
*last_x = xpos;
*last_y = ypos;
camera.process_mouse_movement(xoffset, yoffset, true);
}
glfw::WindowEvent::Scroll(_xoffset, yoffset) => {
if skip_input { return; }
camera.process_mouse_scroll(yoffset as f32);
}
_ => {}
}
}
/// Input processing function as introduced in 1.7.4 (Camera Class) and used in
/// most later tutorials
pub fn process_input(window: &mut glfw::Window, delta_time: f32, camera: &mut Camera) {
if window.get_key(Key::Escape) == Action::Press {
window.set_should_close(true)
}
if window.get_key(Key::W) == Action::Press {
camera.process_keyboard(Camera_Movement::FORWARD, delta_time);
}
if window.get_key(Key::S) == Action::Press {
camera.process_keyboard(Camera_Movement::BACKWARD, delta_time);
}
if window.get_key(Key::A) == Action::Press {
camera.process_keyboard(Camera_Movement::LEFT, delta_time);
}
if window.get_key(Key::D) == Action::Press {
camera.process_keyboard(Camera_Movement::RIGHT, delta_time);
}
camera.enable_mouse_movement(window.get_key(Key::LeftControl) != Action::Press);
engine.run();
}