extern crate gl;
extern crate glutin;
extern crate libc;
use crate::gameboy::Gameboy;
use crate::input::KeypadKey;
use std::ffi::CString;
use std::iter::repeat;
use std::mem;
use std::ptr;
use std::str;
use gl::types::*;
use glutin::event::{ElementState, VirtualKeyCode};
pub struct Glcx {
#[allow(unused)]
tex: GLuint,
#[allow(unused)]
program: GLuint,
#[allow(unused)]
frag: GLuint,
#[allow(unused)]
vert: GLuint,
#[allow(unused)]
ebo: GLuint,
#[allow(unused)]
vbo: GLuint,
#[allow(unused)]
vao: GLuint,
}
pub fn process_window(
_window: &glutin::window::Window,
wevent: &glutin::event::WindowEvent,
gameboy: &mut Gameboy,
focused: &mut bool,
) -> glutin::event_loop::ControlFlow {
match wevent {
glutin::event::WindowEvent::Focused(f) => {
*focused = *f;
glutin::event_loop::ControlFlow::Poll
}
glutin::event::WindowEvent::KeyboardInput { input, .. } => {
if let Some(virt_keycode) = input.virtual_keycode {
let button = match virt_keycode {
VirtualKeyCode::A => KeypadKey::A,
VirtualKeyCode::B => KeypadKey::B,
VirtualKeyCode::Z => KeypadKey::Select,
VirtualKeyCode::X => KeypadKey::Start,
VirtualKeyCode::Left => KeypadKey::Left,
VirtualKeyCode::Right => KeypadKey::Right,
VirtualKeyCode::Down => KeypadKey::Down,
VirtualKeyCode::Up => KeypadKey::Up,
_ => return glutin::event_loop::ControlFlow::Poll,
};
match input.state {
ElementState::Pressed => gameboy.keydown(button),
ElementState::Released => gameboy.keyup(button),
}
}
glutin::event_loop::ControlFlow::Poll
}
glutin::event::WindowEvent::Resized(glutin::dpi::PhysicalSize {
width: _,
height: _,
}) => glutin::event_loop::ControlFlow::Poll,
glutin::event::WindowEvent::CloseRequested => {
glutin::event_loop::ControlFlow::Exit
}
_ => glutin::event_loop::ControlFlow::Poll,
}
}
const VERTEX: &str = r"#version 150 core
in vec2 position;
in vec3 color;
in vec2 texcoord;
out vec3 Color;
out vec2 Texcoord;
void main() {
Color = color;
Texcoord = texcoord;
gl_Position = vec4(position, 0.0, 1.0);
}
";
const FRAGMENT: &str = r"#version 150 core
in vec3 Color;
in vec2 Texcoord;
out vec4 outColor;
uniform sampler2D tex;
void main() {
outColor = texture(tex, Texcoord);
}
";
impl Glcx {
pub fn new() -> Glcx {
unsafe {
let mut vao = 0;
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
let mut vbo = 0;
gl::GenBuffers(1, &mut vbo);
const VERTICES: &[f32] = &[
-1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, ];
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
(VERTICES.len() * 4) as libc::ssize_t,
VERTICES.as_ptr() as *const _,
gl::STATIC_DRAW,
);
let mut ebo = 0;
gl::GenBuffers(1, &mut ebo);
const ELEMENTS: &[GLuint] = &[0, 1, 2, 2, 3, 0];
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
std::mem::size_of_val(ELEMENTS) as libc::ssize_t,
ELEMENTS.as_ptr() as *const _,
gl::STATIC_DRAW,
);
let vert = gl::CreateShader(gl::VERTEX_SHADER);
let src = CString::new(VERTEX).unwrap();
gl::ShaderSource(vert, 1, &src.as_ptr(), std::ptr::null::<i32>());
gl::CompileShader(vert);
let frag = gl::CreateShader(gl::FRAGMENT_SHADER);
let src = CString::new(FRAGMENT).unwrap();
gl::ShaderSource(frag, 1, &src.as_ptr(), std::ptr::null::<i32>());
gl::CompileShader(frag);
let program = gl::CreateProgram();
gl::AttachShader(program, vert);
gl::AttachShader(program, frag);
let buf = CString::new("outColor").unwrap();
gl::BindFragDataLocation(program, 0, buf.as_ptr());
gl::LinkProgram(program);
assert_eq!(gl::GetError(), 0);
gl::UseProgram(program);
let buf = CString::new("position").unwrap();
let pos_attrib = gl::GetAttribLocation(program, buf.as_ptr());
gl::EnableVertexAttribArray(pos_attrib as u32);
gl::VertexAttribPointer(
pos_attrib as u32,
2,
gl::FLOAT,
gl::FALSE,
(7 * mem::size_of::<GLfloat>()) as i32,
std::ptr::null(),
);
let buf = CString::new("color").unwrap();
let col_attrib = gl::GetAttribLocation(program, buf.as_ptr());
gl::EnableVertexAttribArray(col_attrib as u32);
gl::VertexAttribPointer(
col_attrib as u32,
3,
gl::FLOAT,
gl::FALSE,
(7 * mem::size_of::<GLfloat>()) as i32,
(2 * mem::size_of::<GLfloat>()) as *const _,
);
let buf = CString::new("texcoord").unwrap();
let tex_attrib = gl::GetAttribLocation(program, buf.as_ptr());
gl::EnableVertexAttribArray(tex_attrib as u32);
gl::VertexAttribPointer(
tex_attrib as u32,
2,
gl::FLOAT,
gl::FALSE,
(7 * mem::size_of::<GLfloat>()) as i32,
(5 * mem::size_of::<GLfloat>()) as *const _,
);
let mut tex = 0;
gl::GenTextures(1, &mut tex);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, tex);
let buf = CString::new("tex").unwrap();
gl::Uniform1i(gl::GetUniformLocation(program, buf.as_ptr()), 0);
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::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);
Glcx {
tex,
program,
frag,
vert,
ebo,
vbo,
vao,
}
}
}
#[allow(dead_code)]
unsafe fn check_program_link(_gl: &Glcx, program: GLuint) {
let mut status = gl::FALSE as GLint;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
if status == (gl::TRUE as GLint) {
return;
}
let mut len: GLint = 0;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = repeat(0u8).take(len as usize).collect::<Vec<_>>();
gl::GetProgramInfoLog(
program,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
panic!("{}", str::from_utf8(&buf).unwrap());
}
pub fn draw(&self, width: u32, height: u32, data: &[u8]) {
unsafe {
gl::ClearColor(0.0, 0.0, 1.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGB as i32,
width as i32,
height as i32,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
data.as_ptr() as *const _,
);
assert_eq!(gl::GetError(), 0);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
}
}
}