initial commit

This commit is contained in:
Michael Gimle
2020-08-22 07:47:58 +02:00
commit 1975cf08c0
8 changed files with 1913 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
/.vscode

1514
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

14
Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "gloom-rs"
version = "0.1.0"
authors = ["Michael H. Gimle <michael.gimle@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glutin = "0.24.1"
gl = "0.14.0"
tobj = "2.0.2"
image = "0.23.8"
nalgebra-glm = "0.7.0"

8
shaders/simple.frag Normal file
View File

@@ -0,0 +1,8 @@
#version 460 core
layout(binding = 0) uniform sampler2D t;
in vec3 vColor;
in vec2 vUv;
out vec4 color;
void main() {
color = vec4(vUv, 0, 1.0f);
}

16
shaders/simple.vert Normal file
View File

@@ -0,0 +1,16 @@
#version 460 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 iColor;
layout (location = 2) in vec2 uv;
layout (location = 0) uniform float val;
// layout (location = 0) uniform mat4 P;
// layout (location = 1) uniform mat4 V;
// layout (location = 2) uniform mat4 M;
out vec3 vColor;
out vec2 vUv;
void main() {
// gl_Position = P * V * M * vec4(pos.xyz, 1.0f);
gl_Position = vec4(pos.x, pos.y + val, pos.z, 1.0f);
vColor = iColor;
vUv = uv;
}

190
src/main.rs Normal file
View File

@@ -0,0 +1,190 @@
extern crate nalgebra_glm as glm;
use gl::types::*;
use std::{
mem,
ptr,
str,
os::raw::c_void,
};
use std::thread;
use std::sync::{Mutex, Arc, RwLock};
mod shader;
mod util;
use glutin::event::{Event, WindowEvent, KeyboardInput, ElementState::{Pressed, Released}, VirtualKeyCode::{self, *}};
use glutin::event_loop::ControlFlow;
const SCREEN_W: u32 = 800;
const SCREEN_H: u32 = 600;
// Helper functions to make interacting with OpenGL a little bit prettier. You will need these!
// The names should be pretty self explanatory
fn byte_size_of_array<T>(val: &[T]) -> isize {
std::mem::size_of_val(&val[..]) as isize
}
// Get the OpenGL-compatible pointer to an arbitrary array of numbers
fn pointer_to_array<T>(val: &[T]) -> *const c_void {
&val[0] as *const T as *const c_void
}
// Get the size of the given type in bytes
fn size_of<T>() -> i32 {
mem::size_of::<T>() as i32
}
// Get an offset in bytes for n units of type T
fn offset<T>(n: u32) -> *const c_void {
(n * mem::size_of::<T>() as u32) as *const T as *const c_void
}
// == // Modify and complete the function below for the first task
// unsafe fn FUNCTION_NAME(ARGUMENT_NAME: &Vec<f32>, ARGUMENT_NAME: &Vec<u32>) -> u32 { }
fn main() {
// Set up the necessary objects to deal with windows and event handling
let el = glutin::event_loop::EventLoop::new();
let wb = glutin::window::WindowBuilder::new()
.with_title("Gloom-rs")
.with_resizable(false)
.with_inner_size(glutin::dpi::LogicalSize::new(SCREEN_W, SCREEN_H));
let cb = glutin::ContextBuilder::new()
.with_vsync(true);
let windowed_context = cb.build_windowed(wb, &el).unwrap();
// Set up a shared vector for keeping track of currently pressed keys
let arc_pressed_keys = Arc::new(Mutex::new(Vec::<VirtualKeyCode>::with_capacity(10)));
// Send a copy of this vector to send to the render thread
let pressed_keys = Arc::clone(&arc_pressed_keys);
// Spawn a separate thread for rendering, so event handling doesn't block rendering
let render_thread = thread::spawn(move || {
// Acquire the OpenGL Context and load the function pointers. This has to be done inside of the renderin thread, because
// an active OpenGL context cannot safely traverse a thread boundary
let context = unsafe {
let c = windowed_context.make_current().unwrap();
gl::load_with(|symbol| c.get_proc_address(symbol) as *const _);
c
};
// Set up openGL
unsafe {
gl::Enable(gl::CULL_FACE);
gl::Disable(gl::MULTISAMPLE);
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
gl::DebugMessageCallback(Some(util::debug_callback), ptr::null());
}
// == // Set up your VAO here
// Used to demonstrate keyboard handling -- feel free to remove
let mut _arbitrary_number = 0.0;
let first_frame_time = std::time::Instant::now();
let mut last_frame_time = first_frame_time;
// The main rendering loop
loop {
let now = std::time::Instant::now();
let elapsed = now.duration_since(first_frame_time).as_secs_f32();
let delta_time = now.duration_since(last_frame_time).as_secs_f32();
last_frame_time = now;
// Handle keyboard input
if let Ok(keys) = pressed_keys.lock() {
for key in keys.iter() {
match key {
VirtualKeyCode::A => {
_arbitrary_number += delta_time;
},
VirtualKeyCode::D => {
_arbitrary_number -= delta_time;
},
_ => { }
}
}
}
unsafe {
gl::ClearColor(0.163, 0.163, 0.163, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
// Issue the necessary commands to draw your scene here
}
context.swap_buffers().unwrap();
}
});
// Keep track of the health of the rendering thread
let render_thread_healthy = Arc::new(RwLock::new(true));
let render_thread_watchdog = Arc::clone(&render_thread_healthy);
thread::spawn(move || {
if !render_thread.join().is_ok() {
if let Ok(mut health) = render_thread_watchdog.write() {
println!("Render thread panicked!");
*health = false;
}
}
});
// Start the event loop -- This is where window events get handled
el.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
// Terminate program if render thread panics
if let Ok(health) = render_thread_healthy.read() {
if *health == false {
*control_flow = ControlFlow::Exit;
}
}
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
*control_flow = ControlFlow::Exit;
},
// Keep track of currently pressed keys to send to the rendering thread
Event::WindowEvent { event: WindowEvent::KeyboardInput {
input: KeyboardInput { state: key_state, virtual_keycode: Some(keycode), .. }, .. }, .. } => {
if let Ok(mut keys) = arc_pressed_keys.lock() {
match key_state {
Released => {
if keys.contains(&keycode) {
let i = keys.iter().position(|&k| k == keycode).unwrap();
keys.remove(i);
}
},
Pressed => {
if !keys.contains(&keycode) {
keys.push(keycode);
}
}
}
}
// Handle escape separately
match keycode {
Escape => {
*control_flow = ControlFlow::Exit;
},
_ => { }
}
},
_ => { }
}
});
}

141
src/shader.rs Normal file
View File

@@ -0,0 +1,141 @@
use gl;
use std::{
ptr,
str,
ffi::CString,
path::Path,
};
pub struct Shader {
pub program_id: u32,
}
pub struct ShaderBuilder {
program_id: u32,
shaders: Vec::<u32>,
}
#[allow(dead_code)]
pub enum ShaderType {
Vertex,
Fragment,
TessellationControl,
TessellationEvaluation,
Geometry,
}
impl Into<gl::types::GLenum> for ShaderType {
fn into(self) -> gl::types::GLenum {
match self {
ShaderType::Vertex => { gl::VERTEX_SHADER },
ShaderType::Fragment => { gl::FRAGMENT_SHADER },
ShaderType::TessellationControl => { gl::TESS_CONTROL_SHADER },
ShaderType::TessellationEvaluation => { gl::TESS_EVALUATION_SHADER } ,
ShaderType::Geometry => { gl::GEOMETRY_SHADER },
}
}
}
impl ShaderType {
fn from_ext(ext: &std::ffi::OsStr) -> Result<ShaderType, String> {
match ext.to_str().expect("Failed to read extension") {
"vert" => { Ok(ShaderType::Vertex) },
"frag" => { Ok(ShaderType::Fragment) },
"tcs" => { Ok(ShaderType::TessellationControl) },
"tes" => { Ok(ShaderType::TessellationEvaluation) },
"geom" => { Ok(ShaderType::Geometry) },
e => { Err(e.to_string()) },
}
}
}
impl ShaderBuilder {
pub unsafe fn new() -> ShaderBuilder {
ShaderBuilder {
program_id: gl::CreateProgram(),
shaders: vec![],
}
}
pub unsafe fn attach_file(self, shader_path: &str) -> ShaderBuilder {
let path = Path::new(shader_path);
if let Some(extension) = path.extension() {
let shader_type = ShaderType::from_ext(extension)
.expect("Failed to parse file extension.");
let shader_src = std::fs::read_to_string(path)
.expect(&format!("Failed to read shader source. {}", shader_path));
self.compile_shader(&shader_src, shader_type)
} else {
panic!("Failed to read extension of file with path: {}", shader_path);
}
}
pub unsafe fn compile_shader(mut self, shader_src: &str, shader_type: ShaderType) -> ShaderBuilder {
let shader = gl::CreateShader(shader_type.into());
let c_str_shader = CString::new(shader_src.as_bytes()).unwrap();
gl::ShaderSource(shader, 1, &c_str_shader.as_ptr(), ptr::null());
gl::CompileShader(shader);
if !self.check_shader_errors(shader) {
panic!("Shader failed to compile.");
}
self.shaders.push(shader);
self
}
unsafe fn check_shader_errors(&self, shader_id: u32) -> bool {
let mut success = i32::from(gl::FALSE);
let mut info_log = Vec::with_capacity(512);
info_log.set_len(512 - 1);
gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut success);
if success != i32::from(gl::TRUE) {
gl::GetShaderInfoLog(
shader_id,
512,
ptr::null_mut(),
info_log.as_mut_ptr() as *mut gl::types::GLchar,
);
println!("ERROR::Shader Compilation Failed!\n{}", String::from_utf8_lossy(&info_log));
return false;
}
true
}
unsafe fn check_linker_errors(&self) -> bool {
let mut success = i32::from(gl::FALSE);
let mut info_log = Vec::with_capacity(512);
info_log.set_len(512 - 1);
gl::GetProgramiv(self.program_id, gl::LINK_STATUS, &mut success);
if success != i32::from(gl::TRUE) {
gl::GetProgramInfoLog(
self.program_id,
512,
ptr::null_mut(),
info_log.as_mut_ptr() as *mut gl::types::GLchar,
);
println!("ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n{}", String::from_utf8_lossy(&info_log));
return false;
}
true
}
pub unsafe fn link(self) -> Shader {
for &shader in &self.shaders {
gl::AttachShader(self.program_id, shader);
}
gl::LinkProgram(self.program_id);
// todo:: use this to make safer abstraction
self.check_linker_errors();
for &shader in &self.shaders {
gl::DeleteShader(shader);
}
Shader {
program_id: self.program_id
}
}
}

28
src/util.rs Normal file
View File

@@ -0,0 +1,28 @@
use std::ffi::CString;
// Debug callback to panic upon enountering any OpenGL error
pub extern "system" fn debug_callback(
source: u32, e_type: u32, id: u32,
severity: u32, _length: i32,
msg: *const i8, _data: *mut std::ffi::c_void
) {
if e_type != gl::DEBUG_TYPE_ERROR { return }
if severity == gl::DEBUG_SEVERITY_HIGH ||
severity == gl::DEBUG_SEVERITY_MEDIUM ||
severity == gl::DEBUG_SEVERITY_LOW
{
let severity_string = match severity {
gl::DEBUG_SEVERITY_HIGH => "high",
gl::DEBUG_SEVERITY_MEDIUM => "medium",
gl::DEBUG_SEVERITY_LOW => "low",
_ => "unknown",
};
unsafe {
let string = CString::from_raw(msg as *mut i8);
let error_message = String::from_utf8_lossy(string.as_bytes()).to_string();
panic!("{}: Error of severity {} raised from {}: {}\n",
id, severity_string, source, error_message);
}
}
}