Transformations

This commit is contained in:
Piotr Siuszko 2020-08-18 20:29:25 +02:00
parent 4b606801ae
commit 1b4f2793ce
4 changed files with 205 additions and 80 deletions

View File

@ -1,20 +1,35 @@
// settings
pub const SCR_WIDTH: u32 = 1280;
pub const SCR_HEIGHT: u32 = 720;
pub const VERTEX_SHADER_SRC: &str = r#"
#version 330 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(aPos, 1.0);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
"#;
pub const FRAGMENT_SHADER_SRC: &str = r#"
#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
"#;
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
"#;

17
src/macros.rs Normal file
View File

@ -0,0 +1,17 @@
#![macro_use]
/// Macro to get c strings from literals without runtime overhead
/// Literal must not contain any interior nul bytes!
macro_rules! c_str {
($literal:expr) => {
std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($literal, "\0").as_bytes())
};
}
/// Get offset to struct member, similar to `offset_of` in C/C++
/// From https://stackoverflow.com/questions/40310483/how-to-get-pointer-offset-in-bytes/40310851#40310851
macro_rules! offset_of {
($ty:ty, $field:ident) => {
&(*(ptr::null() as *const $ty)).$field as *const _ as usize
};
}

View File

@ -1,17 +1,26 @@
extern crate glfw;
use self::glfw::{Context, Key, Action};
use self::glfw::{Action, Context, Key};
extern crate gl;
use self::gl::types::*;
use std::sync::mpsc::Receiver;
use std::ffi::CString;
use std::ptr;
use std::str;
use std::mem;
use std::os::raw::c_void;
use std::ptr;
use std::str;
use std::sync::mpsc::Receiver;
use std::path::Path;
extern crate image;
use image::GenericImage;
use cgmath::prelude::*;
use cgmath::{vec3, Matrix4, Rad};
mod consts;
mod macros;
mod shader;
pub fn main() {
@ -19,13 +28,21 @@ pub fn main() {
// ------------------------------
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));
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, "LearnOpenGL", glfw::WindowMode::Windowed)
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();
@ -36,46 +53,63 @@ pub fn main() {
// ---------------------------------------
gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
let (shaderObject, VAO) = unsafe {
let vShaderCode = CString::new(consts::VERTEX_SHADER_SRC.as_bytes()).unwrap();
let fShaderCode = CString::new(consts::FRAGMENT_SHADER_SRC.as_bytes()).unwrap();
let shader = crate::shader::Shader::new(vShaderCode, fShaderCode);
let (shader_object, vao, vbo, ebo, texture) = unsafe {
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; 12] = [
0.5, 0.5, 0.0, // top right
0.5, -0.5, 0.0, // bottom right
-0.5, -0.5, 0.0, // bottom left
-0.5, 0.5, 0.0 // top left
let vertices: [f32; 20] = [
// positions // texture coords
0.5, 0.5, 0.0, 1.0, 1.0, // top right
0.5, -0.5, 0.0, 1.0, 0.0, // bottom right
-0.5, -0.5, 0.0, 0.0, 0.0, // bottom left
-0.5, 0.5, 0.0, 0.0, 1.0, // top left
];
let indices = [ // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
let indices = [
// note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3, // second Triangle
];
let (mut VBO, mut VAO, mut EBO) = (0, 0, 0);
gl::GenVertexArrays(1, &mut VAO);
gl::GenBuffers(1, &mut VBO);
gl::GenBuffers(1, &mut EBO);
let (mut vbo, mut vao, mut ebo) = (0, 0, 0);
gl::GenVertexArrays(1, &mut vao);
gl::GenBuffers(1, &mut vbo);
gl::GenBuffers(1, &mut ebo);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
gl::BindVertexArray(VAO);
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);
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,
);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, EBO);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
&indices[0] as *const i32 as *const c_void,
gl::STATIC_DRAW);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
&indices[0] as *const i32 as *const c_void,
gl::STATIC_DRAW,
);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, 3 * mem::size_of::<GLfloat>() as GLsizei, ptr::null());
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);
// note that this is allowed, the call to gl::VertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
@ -90,14 +124,41 @@ pub fn main() {
// uncomment this call to draw in wireframe polygons.
// gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE);
(shader, VAO)
// -------------------------
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(&Path::new("resources/garlic_dog_space.jpg"))
.expect("Failed to load texture");
let flipped = img.flipv();
let data = flipped.raw_pixels();
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGB as i32,
img.width() as i32,
img.height() as i32,
0,
gl::RGB,
gl::UNSIGNED_BYTE,
&data[0] as *const u8 as *const c_void,
);
gl::GenerateMipmap(gl::TEXTURE_2D);
(shader, vao, vbo, ebo, texture)
};
// render loop
// -----------
let color_r = 0.3;
let color_g = 0.3;
let color_b = 0.6;
let color_r = 0.188;
let color_g = 0.22;
let color_b = 0.235;
while !window.should_close() {
// events
// -----
@ -109,9 +170,19 @@ pub fn main() {
gl::ClearColor(color_r, color_g, color_b, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
// draw our first triangle
shaderObject.useProgram();
gl::BindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
gl::BindTexture(gl::TEXTURE_2D, texture);
// create transformations
let mut transform: Matrix4<f32> = Matrix4::identity();
transform = transform * Matrix4::<f32>::from_translation(vec3(0.5, -0.5, 0.0));
transform = transform * Matrix4::<f32>::from_angle_z(Rad(glfw.get_time() as f32));
// get matrix's uniform location and set matrix
shader_object.useProgram();
let transform_loc =
gl::GetUniformLocation(shader_object.ID, c_str!("transform").as_ptr());
gl::UniformMatrix4fv(transform_loc, 1, gl::FALSE, transform.as_ptr());
gl::BindVertexArray(vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
}
@ -131,7 +202,9 @@ fn process_events(window: &mut glfw::Window, events: &Receiver<(f64, glfw::Windo
// height will be significantly larger than specified on retina displays.
unsafe { gl::Viewport(0, 0, width, height) }
}
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => window.set_should_close(true),
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
window.set_should_close(true)
}
glfw::WindowEvent::Key(_, _, Action::Press, _) => println!("Other key pressed"),
_ => {}
}

View File

@ -1,5 +1,5 @@
#![allow(non_snake_case)]
use std::ffi::{CString, CStr};
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::Read;
use std::ptr;
@ -8,8 +8,8 @@ use std::str;
use gl;
use gl::types::*;
use cgmath::{Matrix, Matrix4, Vector3};
use cgmath::prelude::*;
use cgmath::{Matrix, Matrix4, Vector3};
pub struct Shader {
pub ID: u32,
@ -19,7 +19,6 @@ pub struct Shader {
/// a few more setters for uniforms)
#[allow(dead_code)]
impl Shader {
pub fn new(vShaderCode: CString, fShaderCode: CString) -> Shader {
let mut shader = Shader { ID: 0 };
@ -52,10 +51,10 @@ impl Shader {
pub fn from_file(vertexPath: &str, fragmentPath: &str) -> Shader {
// 1. retrieve the vertex/fragment source code from filesystem
let mut vShaderFile = File::open(vertexPath)
.unwrap_or_else(|_| panic!("Failed to open {}", vertexPath));
let mut fShaderFile = File::open(fragmentPath)
.unwrap_or_else(|_| panic!("Failed to open {}", fragmentPath));
let mut vShaderFile =
File::open(vertexPath).unwrap_or_else(|_| panic!("Failed to open {}", vertexPath));
let mut fShaderFile =
File::open(fragmentPath).unwrap_or_else(|_| panic!("Failed to open {}", fragmentPath));
let mut vertexCode = String::new();
let mut fragmentCode = String::new();
vShaderFile
@ -92,7 +91,11 @@ impl Shader {
}
/// ------------------------------------------------------------------------
pub unsafe fn setVector3(&self, name: &CStr, value: &Vector3<f32>) {
gl::Uniform3fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, value.as_ptr());
gl::Uniform3fv(
gl::GetUniformLocation(self.ID, name.as_ptr()),
1,
value.as_ptr(),
);
}
/// ------------------------------------------------------------------------
pub unsafe fn setVec3(&self, name: &CStr, x: f32, y: f32, z: f32) {
@ -100,7 +103,12 @@ impl Shader {
}
/// ------------------------------------------------------------------------
pub unsafe fn setMat4(&self, name: &CStr, mat: &Matrix4<f32>) {
gl::UniformMatrix4fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, gl::FALSE, mat.as_ptr());
gl::UniformMatrix4fv(
gl::GetUniformLocation(self.ID, name.as_ptr()),
1,
gl::FALSE,
mat.as_ptr(),
);
}
/// utility function for checking shader compilation/linking errors.
@ -112,36 +120,48 @@ impl Shader {
if type_ != "PROGRAM" {
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
if success != gl::TRUE as GLint {
gl::GetShaderInfoLog(shader, 1024, ptr::null_mut(), infoLog.as_mut_ptr() as *mut GLchar);
println!("ERROR::SHADER_COMPILATION_ERROR of type: {}\n{}\n \
gl::GetShaderInfoLog(
shader,
1024,
ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar,
);
println!(
"ERROR::SHADER_COMPILATION_ERROR of type: {}\n{}\n \
-- --------------------------------------------------- -- ",
type_,
str::from_utf8(&infoLog).unwrap());
type_,
str::from_utf8(&infoLog).unwrap()
);
}
} else {
gl::GetProgramiv(shader, gl::LINK_STATUS, &mut success);
if success != gl::TRUE as GLint {
gl::GetProgramInfoLog(shader, 1024, ptr::null_mut(), infoLog.as_mut_ptr() as *mut GLchar);
println!("ERROR::PROGRAM_LINKING_ERROR of type: {}\n{}\n \
gl::GetProgramInfoLog(
shader,
1024,
ptr::null_mut(),
infoLog.as_mut_ptr() as *mut GLchar,
);
println!(
"ERROR::PROGRAM_LINKING_ERROR of type: {}\n{}\n \
-- --------------------------------------------------- -- ",
type_,
str::from_utf8(&infoLog).unwrap());
type_,
str::from_utf8(&infoLog).unwrap()
);
}
}
}
/// Only used in 4.9 Geometry shaders - ignore until then (shader.h in original C++)
pub fn with_geometry_shader(vertexPath: &str, fragmentPath: &str, geometryPath: &str) -> Self {
let mut shader = Shader { ID: 0 };
// 1. retrieve the vertex/fragment source code from filesystem
let mut vShaderFile = File::open(vertexPath)
.unwrap_or_else(|_| panic!("Failed to open {}", vertexPath));
let mut fShaderFile = File::open(fragmentPath)
.unwrap_or_else(|_| panic!("Failed to open {}", fragmentPath));
let mut gShaderFile = File::open(geometryPath)
.unwrap_or_else(|_| panic!("Failed to open {}", geometryPath));
let mut vShaderFile =
File::open(vertexPath).unwrap_or_else(|_| panic!("Failed to open {}", vertexPath));
let mut fShaderFile =
File::open(fragmentPath).unwrap_or_else(|_| panic!("Failed to open {}", fragmentPath));
let mut gShaderFile =
File::open(geometryPath).unwrap_or_else(|_| panic!("Failed to open {}", geometryPath));
let mut vertexCode = String::new();
let mut fragmentCode = String::new();
let mut geometryCode = String::new();