mirror of https://github.com/Leinnan/doppler.git
296 lines
11 KiB
Rust
296 lines
11 KiB
Rust
extern crate gl;
|
|
extern crate glfw;
|
|
extern crate image;
|
|
|
|
use self::camera::*;
|
|
use self::gl::types::*;
|
|
use self::glfw::{Action, Context, Key};
|
|
use cgmath::prelude::*;
|
|
use cgmath::{perspective, vec3, Deg, Matrix4, Point3, Rad, Vector3};
|
|
use human_panic::setup_panic;
|
|
use image::GenericImageView;
|
|
use std::ffi::CString;
|
|
use std::mem;
|
|
use std::os::raw::c_void;
|
|
use std::ptr;
|
|
use std::sync::mpsc::Receiver;
|
|
mod camera;
|
|
mod consts;
|
|
mod macros;
|
|
mod shader;
|
|
|
|
const CUBES_POS: [Vector3<f32>; 10] = [
|
|
vec3(0.0, 0.0, 0.0),
|
|
vec3(2.0, 5.0, -15.0),
|
|
vec3(-1.5, -2.2, -2.5),
|
|
vec3(-3.8, -2.0, -12.3),
|
|
vec3(2.4, -0.4, -3.5),
|
|
vec3(-1.7, 3.0, -7.5),
|
|
vec3(1.3, -2.0, -2.5),
|
|
vec3(1.5, 2.0, -2.5),
|
|
vec3(1.5, 0.2, -1.5),
|
|
vec3(-1.3, 1.0, -1.5),
|
|
];
|
|
|
|
pub fn main() {
|
|
setup_panic!();
|
|
let mut camera = Camera {
|
|
Position: Point3::new(0.0, 0.0, 3.0),
|
|
..Camera::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_key_polling(true);
|
|
window.set_framebuffer_size_polling(true);
|
|
|
|
// gl: load all OpenGL function pointers
|
|
// ---------------------------------------
|
|
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
|
|
|
|
let (shader_object, vbo, vao, texture) = unsafe {
|
|
gl::Enable(gl::DEPTH_TEST);
|
|
let v_shader = CString::new(consts::VERTEX_SHADER_SRC.as_bytes()).unwrap();
|
|
let f_shader = CString::new(consts::FRAGMENT_SHADER_SRC.as_bytes()).unwrap();
|
|
let shader = crate::shader::Shader::new(v_shader, f_shader);
|
|
// set up vertex data (and buffer(s)) and configure vertex attributes
|
|
// ------------------------------------------------------------------
|
|
// HINT: type annotation is crucial since default for float literals is f64
|
|
let vertices: [f32; 180] = [
|
|
-0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5,
|
|
0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5,
|
|
0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0,
|
|
1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0,
|
|
-0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0,
|
|
-0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5,
|
|
-0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5,
|
|
0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0,
|
|
1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0,
|
|
-0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5,
|
|
0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5,
|
|
0.0, 1.0,
|
|
];
|
|
let (mut vbo, mut vao) = (0, 0);
|
|
gl::GenVertexArrays(1, &mut vao);
|
|
gl::GenBuffers(1, &mut vbo);
|
|
|
|
gl::BindVertexArray(vao);
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
|
|
gl::BufferData(
|
|
gl::ARRAY_BUFFER,
|
|
(vertices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
|
|
&vertices[0] as *const f32 as *const c_void,
|
|
gl::STATIC_DRAW,
|
|
);
|
|
|
|
let stride = 5 * mem::size_of::<GLfloat>() as GLsizei;
|
|
// position attribute
|
|
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, ptr::null());
|
|
gl::EnableVertexAttribArray(0);
|
|
// texture coord attribute
|
|
gl::VertexAttribPointer(
|
|
1,
|
|
2,
|
|
gl::FLOAT,
|
|
gl::FALSE,
|
|
stride,
|
|
(3 * mem::size_of::<GLfloat>()) as *const c_void,
|
|
);
|
|
gl::EnableVertexAttribArray(1);
|
|
|
|
// uncomment this call to draw in wireframe polygons.
|
|
// gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE);
|
|
|
|
// -------------------------
|
|
let mut texture = 0;
|
|
gl::GenTextures(1, &mut texture);
|
|
gl::BindTexture(gl::TEXTURE_2D, texture); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object
|
|
// set the texture wrapping parameters
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32); // set texture wrapping to gl::REPEAT (default wrapping method)
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
|
|
// set texture filtering parameters
|
|
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);
|
|
// load image, create texture and generate mipmaps
|
|
let img = image::open("resources/garlic_dog_space.jpg")
|
|
.unwrap()
|
|
.flipv();
|
|
let data = img.raw_pixels();
|
|
|
|
// let data = flipped
|
|
let dimensions = (img.width(), img.height());
|
|
gl::TexImage2D(
|
|
gl::TEXTURE_2D,
|
|
0,
|
|
gl::RGB as i32,
|
|
dimensions.0 as i32,
|
|
dimensions.1 as i32,
|
|
0,
|
|
gl::RGB,
|
|
gl::UNSIGNED_BYTE,
|
|
&data[0] as *const u8 as *const c_void,
|
|
);
|
|
gl::GenerateMipmap(gl::TEXTURE_2D);
|
|
(shader, vbo, vao, texture)
|
|
};
|
|
|
|
// render loop
|
|
// -----------
|
|
let color_r = 0.188;
|
|
let color_g = 0.22;
|
|
let color_b = 0.235;
|
|
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;
|
|
|
|
// events
|
|
// -----
|
|
process_events(
|
|
&events,
|
|
&mut first_mouse,
|
|
&mut last_x,
|
|
&mut last_y,
|
|
&mut camera,
|
|
);
|
|
|
|
// input
|
|
// -----
|
|
process_input(&mut window, delta_time, &mut camera);
|
|
|
|
// render
|
|
// ------
|
|
unsafe {
|
|
gl::ClearColor(color_r, color_g, color_b, 1.0);
|
|
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, texture);
|
|
shader_object.useProgram();
|
|
|
|
// pass projection matrix to shader (note that in this case it could change every frame)
|
|
let projection: Matrix4<f32> = perspective(
|
|
Deg(camera.Zoom),
|
|
consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32,
|
|
0.1,
|
|
100.0,
|
|
);
|
|
shader_object.setMat4(c_str!("projection"), &projection);
|
|
|
|
// camera/view transformation
|
|
let view = camera.get_view_matrix();
|
|
shader_object.setMat4(c_str!("view"), &view);
|
|
|
|
gl::BindVertexArray(vao);
|
|
for (i, position) in CUBES_POS.iter().enumerate() {
|
|
// calculate the model matrix for each object and pass it to shader before drawing
|
|
let mut model: Matrix4<f32> = Matrix4::from_translation(*position);
|
|
let angle = 20.0 * i as f32;
|
|
model =
|
|
model * Matrix4::from_axis_angle(vec3(1.0, 0.3, 0.5).normalize(), Deg(angle));
|
|
shader_object.setMat4(c_str!("model"), &model);
|
|
|
|
gl::DrawArrays(gl::TRIANGLES, 0, 36);
|
|
}
|
|
}
|
|
|
|
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
|
|
// -------------------------------------------------------------------------------
|
|
window.swap_buffers();
|
|
glfw.poll_events();
|
|
}
|
|
|
|
unsafe {
|
|
gl::DeleteVertexArrays(1, &vao);
|
|
gl::DeleteBuffers(1, &vbo);
|
|
}
|
|
}
|
|
|
|
pub fn process_events(
|
|
events: &Receiver<(f64, glfw::WindowEvent)>,
|
|
first_mouse: &mut bool,
|
|
last_x: &mut f32,
|
|
last_y: &mut f32,
|
|
camera: &mut Camera,
|
|
) {
|
|
for (_, event) in glfw::flush_messages(events) {
|
|
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) => {
|
|
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) => {
|
|
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);
|
|
}
|
|
}
|