Skip to content

Instantly share code, notes, and snippets.

@JarrettBillingsley
Created December 16, 2013 07:03
Show Gist options
  • Save JarrettBillingsley/7983334 to your computer and use it in GitHub Desktop.
Save JarrettBillingsley/7983334 to your computer and use it in GitHub Desktop.
#[feature(globs)];
#[feature(macro_rules)];
#[allow(dead_code)];
#[allow(unused_mut)];
#[allow(unused_variable)];
#[allow(unused_imports)];
extern mod gl;
extern mod glfw;
extern mod cgmath;
use std::c_str::{CString};
use std::iter::{range_inclusive};
use std::libc::{c_void};
use std::mem::{size_of};
use std::ptr::{null};
use std::rand::*;
use std::vec::raw;
use std::vec;
use cgmath::angle::*;
use cgmath::matrix::*;
use cgmath::point::{Point3};
use cgmath::projection::*;
use cgmath::ptr::*;
use cgmath::vector::{Vec2, Vec3};
use gl::types::*;
#[link(name="glfw3")]
#[link(name="opengl32")]
#[link(name="gdi32")]
extern {}
#[start]
fn start(argc: int, argv: **u8) -> int
{
std::rt::start_on_main_thread(argc, argv, main)
}
type VertexAttrib = (GLuint, GLint, GLenum, bool, uint, uint);
struct Vao
{
handle: GLuint,
nelems: uint
}
impl Vao
{
fn new(vbo: Vbo, ebo: Ebo, attribs: ~[VertexAttrib]) -> Vao
{
let mut handle: GLuint = 0;
unsafe { gl::GenVertexArrays(1, &mut handle) };
gl::BindVertexArray(handle);
vbo.bind();
ebo.bind();
for attrib in attribs.iter()
{
gl::EnableVertexAttribArray(attrib.n0());
unsafe
{
gl::VertexAttribPointer(
attrib.n0(),
attrib.n1(),
attrib.n2(),
if attrib.n3() { gl::TRUE } else { gl::FALSE },
attrib.n4() as GLsizei,
attrib.n5() as *c_void)
};
}
let ret: Vao = Vao { handle: handle, nelems: ebo.num_elements() };
gl::BindVertexArray(0);
ret
}
fn delete(&mut self)
{
unsafe { gl::DeleteVertexArrays(1, &self.handle) };
self.handle = 0;
}
fn bind(&self)
{
gl::BindVertexArray(self.handle);
}
fn draw(&self)
{
self.bind();
unsafe { gl::DrawElements(gl::TRIANGLES, self.nelems as GLint, gl::UNSIGNED_INT, 0 as *GLvoid) };
}
}
struct Vbo
{
handle: GLuint
}
impl Vbo
{
fn new<T>(vertices: ~[T]) -> Vbo
{
let mut ret: GLuint = 0;
unsafe { gl::GenBuffers(1, &mut ret) };
gl::BindBuffer(gl::ARRAY_BUFFER, ret);
let size = (vertices.len() * size_of::<T>()) as i32;
let ptr = raw::to_ptr(vertices) as *c_void;
unsafe { gl::BufferData(gl::ARRAY_BUFFER, size, ptr, gl::STATIC_DRAW) };
Vbo { handle: ret }
}
fn delete(&mut self)
{
unsafe { gl::DeleteBuffers(1, &self.handle) };
self.handle = 0;
}
fn bind(&self)
{
gl::BindBuffer(gl::ARRAY_BUFFER, self.handle);
}
}
struct Ebo
{
handle: GLuint
}
impl Ebo
{
fn new(elements: ~[u32]) -> Ebo
{
let mut ret: GLuint = 0;
unsafe { gl::GenBuffers(1, &mut ret) };
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ret);
let size = (elements.len() * size_of::<u32>()) as i32;
let ptr = raw::to_ptr(elements) as *c_void;
unsafe { gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size, ptr, gl::STATIC_DRAW) };
Ebo { handle: ret }
}
fn delete(&mut self)
{
unsafe { gl::DeleteBuffers(1, &self.handle) };
self.handle = 0;
}
fn bind(&self)
{
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.handle);
}
fn num_elements(&self) -> uint
{
self.bind();
let mut size: GLint = 0;
unsafe { gl::GetBufferParameteriv(gl::ELEMENT_ARRAY_BUFFER, gl::BUFFER_SIZE, &mut size) };
size as uint
}
}
struct Mesh
{
vao: Vao,
vbo: Vbo,
ebo: Ebo
}
impl Mesh
{
fn new<V>(vertices: ~[V], indices: ~[u32], attrs: ~[VertexAttrib]) -> Mesh
{
let vbo = Vbo::new(vertices);
let ebo = Ebo::new(indices);
let vao = Vao::new(vbo, ebo, attrs);
Mesh
{
vbo: vbo,
ebo: ebo,
vao: vao
}
}
fn draw(&self)
{
self.vao.draw();
}
}
impl Drop for Mesh
{
fn drop(&mut self)
{
self.vao.delete();
self.vbo.delete();
self.ebo.delete();
}
}
fn compile_shader(source: &str, kind: GLuint) -> GLuint
{
let ret = gl::CreateShader(kind);
let tmp = unsafe { source.to_c_str().unwrap() };
unsafe { gl::ShaderSource(ret, 1, &tmp, null()) };
gl::CompileShader(ret);
let mut status: GLint = 0;
unsafe { gl::GetShaderiv(ret, gl::COMPILE_STATUS, &mut status); }
if status == gl::FALSE as i32
{
let mut len: GLsizei = 0;
let mut msg = [0 as GLchar, ..512];
unsafe { gl::GetShaderInfoLog(ret, msg.len() as i32, &mut len, raw::to_mut_ptr(msg)); }
let msgStr = unsafe { CString::new(raw::to_ptr(msg), false) };
println!("Error compiling shader: {}", msgStr.as_str());
}
ret
}
struct VertShader
{
handle: GLuint
}
impl VertShader
{
fn new(source: &str) -> VertShader
{
VertShader { handle: compile_shader(source, gl::VERTEX_SHADER) }
}
fn delete(&mut self)
{
gl::DeleteShader(self.handle);
self.handle = 0;
}
}
struct FragShader
{
handle: GLuint
}
impl FragShader
{
fn new(source: &str) -> FragShader
{
FragShader { handle: compile_shader(source, gl::FRAGMENT_SHADER) }
}
fn delete(&mut self)
{
gl::DeleteShader(self.handle);
self.handle = 0;
}
}
struct Program
{
handle: GLuint,
vs: VertShader,
fs: FragShader
}
impl Program
{
fn new(vs: VertShader, fs: FragShader, data: ~[(&str, GLuint)]) -> Program
{
let ret = Program
{
vs: vs,
fs: fs,
handle: gl::CreateProgram()
};
gl::AttachShader(ret.handle, ret.vs.handle);
gl::AttachShader(ret.handle, ret.fs.handle);
for &(name, loc) in data.iter()
{
unsafe { gl::BindFragDataLocation(ret.handle, loc, name.to_c_str().unwrap()) };
}
gl::LinkProgram(ret.handle);
gl::UseProgram(ret.handle);
ret
}
fn delete(&mut self)
{
gl::DeleteProgram(self.handle);
self.handle = 0;
}
fn get_uniform_location(&self, name: &str) -> GLint
{
unsafe { gl::GetUniformLocation(self.handle, name.to_c_str().unwrap()) }
}
fn get_attrib_location(&self, name: &str) -> GLuint
{
unsafe { gl::GetAttribLocation(self.handle, name.to_c_str().unwrap()) as GLuint }
}
fn bind(&self)
{
gl::UseProgram(self.handle);
}
}
static vertexSource: &'static str =
"#version 150 core
in vec2 position;
uniform mat4 model, view, proj;
void main()
{
gl_Position = proj * view * model * vec4(position, 0.0, 1.0);
}";
static fragmentSource: &'static str =
"#version 150 core
uniform vec3 triangleColor;
out vec4 outColor;
void main() {
outColor = vec4(triangleColor, 1.0);
}";
static fbVertexSource: &'static str =
"#version 150 core
in vec2 position;
in vec2 texCoord;
uniform vec2 shakePos;
// uniform float time;
out vec2 TexCoord;
// const float WiggleMag = 0.02;
void main()
{
TexCoord = texCoord;
gl_Position = vec4(shakePos + position, 0.0, 1.0);
// float angle = (texCoord.x + texCoord.y + time) * 30;
// vec2 wiggle = vec2(sin(angle) * WiggleMag, cos(angle) * WiggleMag);
// TexCoord = texCoord + wiggle;
// gl_Position = vec4(position + shakePos + wiggle, 0.0, 1.0);
}";
static fbFragmentSource: &'static str =
"#version 150 core
in vec2 TexCoord;
out vec4 outColor;
uniform sampler2D frameBuffer;
uniform float time;
// const float blurSizeH = 1.0 / 300.0;
// const float blurSizeV = 1.0 / 200.0;
// void main() {
// vec4 sum = vec4(0.0);
// for (int x = -4; x <= 4; x++)
// for (int y = -4; y <= 4; y++)
// sum += texture(
// frameBuffer,
// vec2(TexCoord.x + x * blurSizeH, TexCoord.y + y * blurSizeV)
// ) / 81.0;
// outColor = sum;
// }
const float WiggleMag = 0.01;
void main()
{
float angle = ((TexCoord.x - TexCoord.y) + time) * 60;
vec2 wiggle = vec2(sin(angle) * WiggleMag, cos(angle) * WiggleMag);
outColor = texture(frameBuffer, TexCoord + wiggle);
// outColor = texture(frameBuffer, TexCoord);
}";
fn makeScreenQuad(xdiv: uint, ydiv: uint) -> (Vbo, Ebo)
{
assert!(xdiv >= 1 && ydiv >= 1, "nooope");
let mut verts = vec::with_capacity::<f32>(4 * (xdiv + 1) * (ydiv + 1));
for yi in range_inclusive(0, ydiv)
{
for xi in range_inclusive(0, xdiv)
{
let x = ((2. * xi as f32) / (xdiv as f32)) - 1.;
let y = 1. - ((2. * yi as f32) / (ydiv as f32));
let u = (xi as f32) / (xdiv as f32);
let v = 1. - ((yi as f32) / (ydiv as f32));
verts.push(x);
verts.push(y);
verts.push(u);
verts.push(v);
}
}
let mut indices = vec::with_capacity::<u32>((xdiv + 1) * (ydiv + 1));
let stride = (xdiv + 1) as u32;
for yi in range(0, ydiv as u32)
{
for xi in range(0, xdiv as u32)
{
let a = ((yi * stride) + xi) as u32;
let b = (a + 1) as u32;
let c = (b + stride) as u32;
let d = (c - 1) as u32;
indices.push(a); indices.push(b); indices.push(c);
indices.push(c); indices.push(d); indices.push(a);
}
}
(Vbo::new(verts), Ebo::new(indices))
}
static ShakeTime: f32 = 0.8f32;
static ShakeMagnitude: f32 = 0.1f32;
fn main()
{
glfw::set_error_callback(~ErrorContext);
do glfw::start
{
let window = glfw::Window::create(800, 600, "Hello yes this is dog", glfw::Windowed).expect("Failed to create GLFW window.");
window.make_context_current();
gl::load_with(glfw::get_proc_address);
// Shaders
let mut vertexShader = VertShader::new(vertexSource);
let mut fragmentShader = FragShader::new(fragmentSource);
let mut shaderProgram = Program::new(vertexShader, fragmentShader, ~[("outColor", 0)]);
let posAttrib = shaderProgram.get_attrib_location("position");
let uniColor = shaderProgram.get_uniform_location("triangleColor");
let modelMat = shaderProgram.get_uniform_location("model");
let viewMat = shaderProgram.get_uniform_location("view");
let projMat = shaderProgram.get_uniform_location("proj");
// View/proj matrices
let view: Mat4<f32> = Mat4::look_at(
&Point3::new(0f32, 1.2f32, 1.2f32),
&Point3::new(0.0f32, 0.0f32, 0.0f32),
&Vec3::new(0.0f32, 0.0f32, 1.0f32)
);
unsafe { gl::UniformMatrix4fv(viewMat, 1, gl::FALSE, view.ptr()) };
let proj: Mat4<f32> = perspective(deg(45.0f32), 800.0f32 / 600.0f32, 1.0f32, 10.0f32);
unsafe { gl::UniformMatrix4fv(projMat, 1, gl::FALSE, proj.ptr()) };
// Triangle
let triMesh = Mesh::new
(
~[
0.0f32, 0.5f32,
0.5f32, -0.5f32,
-0.5f32, -0.5f32
],
~[0, 1, 2],
~[(posAttrib, 2, gl::FLOAT, false, 0, 0)]
);
// VAO2
let rectMesh = Mesh::new
(
~[
-0.5f32, 1f32,
0.5f32, 1f32,
0.5f32, 0.5f32,
-0.5f32, 0.5f32,
],
~[0, 1, 2, 2, 3, 0],
~[(posAttrib, 2, gl::FLOAT, false, 0, 0)]
);
// Framebuffer shaders
let mut fbVertexShader = VertShader::new(fbVertexSource);
let mut fbFragmentShader = FragShader::new(fbFragmentSource);
let mut fbShaderProgram = Program::new(fbVertexShader, fbFragmentShader, ~[("outColor", 0)]);
let fbPosAttrib = fbShaderProgram.get_attrib_location("position");
let fbTexCoordAttrib = fbShaderProgram.get_attrib_location("texCoord");
let frameBufferSampler = fbShaderProgram.get_attrib_location("frameBuffer");
let fbShakePos = fbShaderProgram.get_uniform_location("shakePos");
let fbTime = fbShaderProgram.get_uniform_location("time");
gl::Uniform1i(frameBufferSampler as i32, 0);
// Framebuffer VAO
let (mut fbvbo, mut fbebo) = makeScreenQuad(16, 12);
let mut fbvao = Vao::new(fbvbo, fbebo, ~[
(fbPosAttrib, 2, gl::FLOAT, false, 4 * size_of::<GLfloat>(), 0),
(fbTexCoordAttrib, 2, gl::FLOAT, false, 4 * size_of::<GLfloat>(), 2 * size_of::<GLfloat>())
]);
// Framebuffer render target
let mut fb: GLuint = 0;
unsafe { gl::GenFramebuffers(1, &mut fb); }
gl::BindFramebuffer(gl::FRAMEBUFFER, fb);
let mut fbColorBuffer: GLuint = 0;
unsafe { gl::GenTextures(1, &mut fbColorBuffer); }
gl::BindTexture(gl::TEXTURE_2D, fbColorBuffer);
unsafe { gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as i32, 800, 600, 0, gl::RGB, gl::UNSIGNED_BYTE, null()); }
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_WRAP_S, gl::CLAMP_TO_EDGE as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, fbColorBuffer, 0);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
let mut shakeTimer = 0f32;
let mut prevTime = glfw::get_time();
let mut wireframe = false;
while !window.should_close()
{
let time = glfw::get_time();
let dt = time - prevTime;
prevTime = time;
gl::BindFramebuffer(gl::FRAMEBUFFER, fb);
gl::ClearColor(0.2, 0.2, 0.2, 1.);
gl::Clear(gl::COLOR_BUFFER_BIT);
shaderProgram.bind();
let mut model: Mat4<f32> = Mat3::from_angle_z(deg(time as f32 * 30.).to_rad()).to_mat4();
unsafe { gl::UniformMatrix4fv(modelMat, 1, gl::FALSE, model.ptr()) };
gl::Uniform3f(uniColor, 1., 0., 0.);
triMesh.draw();
model = Mat3::from_angle_z(deg(-time as f32 * 30.).to_rad()).to_mat4();
unsafe { gl::UniformMatrix4fv(modelMat, 1, gl::FALSE, model.ptr()) };
gl::Uniform3f(uniColor, 0., 1., 0.);
rectMesh.draw();
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::ClearColor(0., 0., 0., 1.);
gl::Clear(gl::COLOR_BUFFER_BIT);
fbShaderProgram.bind();
let shakePos = if shakeTimer > 0.
{
let mag = shakeTimer / ShakeTime;
let xmag = mag * ((*random::<Closed01<f32>>() * 2. * ShakeMagnitude) - ShakeMagnitude);
let ymag = mag * ((*random::<Closed01<f32>>() * 2. * ShakeMagnitude) - ShakeMagnitude);
shakeTimer -= dt as f32;
Vec2::new(xmag, ymag)
}
else
{
Vec2::new(0f32, 0f32)
};
gl::Uniform2f(fbShakePos, shakePos.x, shakePos.y);
gl::Uniform1f(fbTime, (time / 2.) as f32);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, fbColorBuffer);
if wireframe { gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); }
fbvao.draw();
if wireframe { gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); }
window.swap_buffers();
glfw::poll_events();
if window.get_key(glfw::KeyEscape) == glfw::Press
{ window.set_should_close(true); }
else if window.get_key(glfw::KeySpace) == glfw::Press && shakeTimer <= 0.
{ shakeTimer = ShakeTime; }
else if window.get_key(glfw::KeyW) == glfw::Press
{ wireframe = !wireframe; }
}
fbShaderProgram.delete();
fbVertexShader.delete();
fbFragmentShader.delete();
shaderProgram.delete();
fragmentShader.delete();
vertexShader.delete();
fbvbo.delete();
fbebo.delete();
fbvao.delete();
unsafe { gl::DeleteTextures(1, &fbColorBuffer); }
unsafe { gl::DeleteFramebuffers(1, &fb); }
}
}
struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext
{
fn call(&self, _: glfw::Error, description: ~str)
{
println!("GLFW Error: {:s}", description);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment