Drawing a Rotating Triangle
use std::time::Instant;
pub struct State {
begin: Instant,
}
pub struct Renderer {
gl: gl::Gl,
program: gl::types::GLuint,
vao: gl::types::GLuint,
vbo: gl::types::GLuint,
rotation: gl::types::GLint,
}
const VERTEX_SHADER_SOURCE: &CStr = c"
#version 410 core
uniform mat3 rotation;
in vec3 position;
in vec3 color;
out vec3 v_color;
void main() {
gl_Position = vec4(rotation * position, 1.0);
v_color = color;
}
";
let rotation =
gl.GetUniformLocation(program, c"rotation".as_ptr() as *const _);
Self { gl, program, vao, vbo, rotation }
fn draw(&self, state: &mut State) {
let time = Instant::now().duration_since(state.begin).as_millis() % 5000;
let phi = (time as f32) / 5000.0 * 2.0 * std::f32::consts::PI;
let rotation: [f32; 9] = [ phi.cos(), 0.0, phi.sin(),
0.0, 1.0, 0.0,
-phi.sin(), 0.0, phi.cos()];
self.gl.UniformMatrix3fv(self.rotation, 1, 1, rotation.as_ptr());
let app_state = State{
begin: Instant::now(),
};
Full code
As always, here comes the full code of everything we've done in all the chapters before and this chapter (though some things might just reference previous chapters):
Cargo.toml
Unchanged from Chapter 2's Cargo.toml.
build.rs
Unchanged from Chapter 2's build.rs.
src/main.rs
use std::error::Error;
use std::ffi::{CStr, CString};
use std::time::Instant;
use glwindow::AppControl;
use glwindow::event::{WindowEvent, KeyEvent};
use glwindow::keyboard::{Key, NamedKey::Escape};
pub mod gl {
#![allow(clippy::all)]
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
pub use Gles2 as Gl;
}
pub struct State {
begin: Instant,
}
pub struct Renderer {
gl: gl::Gl,
program: gl::types::GLuint,
vao: gl::types::GLuint,
vbo: gl::types::GLuint,
rotation: gl::types::GLint,
}
static VERTEX_DATA: [f32; 18] = [
-0.5, -0.5, 0.0, 0.8, 0.8, 0.0,
0.0, 0.5, 0.0, 0.0, 0.8, 0.8,
0.5, -0.5, 0.0, 0.8, 0.0, 0.8,
];
const VERTEX_SHADER_SOURCE: &CStr = c"
#version 410 core
uniform mat3 rotation;
in vec3 position;
in vec3 color;
out vec3 v_color;
void main() {
gl_Position = vec4(rotation * position, 1.0);
v_color = color;
}
";
const FRAGMENT_SHADER_SOURCE: &CStr = c"
#version 410 core
out vec4 color;
in vec3 v_color;
void main() {
color = vec4(v_color, 1.0);
}
";
impl glwindow::AppRenderer for Renderer {
type AppState = State;
fn new<D: glwindow::GlDisplay>(gl_display: &D) -> Self {
unsafe {
let gl = gl::Gl::load_with(|symbol| {
let symbol = CString::new(symbol).unwrap();
gl_display.get_proc_address(symbol.as_c_str()).cast()
});
let vertex_shader = gl.CreateShader(gl::VERTEX_SHADER);
gl.ShaderSource(vertex_shader, 1, [VERTEX_SHADER_SOURCE.as_ptr()].as_ptr(), std::ptr::null());
gl.CompileShader(vertex_shader);
let fragment_shader = gl.CreateShader(gl::FRAGMENT_SHADER);
gl.ShaderSource(fragment_shader, 1, [FRAGMENT_SHADER_SOURCE.as_ptr()].as_ptr(), std::ptr::null());
gl.CompileShader(fragment_shader);
let program = gl.CreateProgram();
gl.AttachShader(program, vertex_shader);
gl.AttachShader(program, fragment_shader);
gl.LinkProgram(program);
gl.UseProgram(program);
gl.DeleteShader(vertex_shader);
gl.DeleteShader(fragment_shader);
let mut vao = std::mem::zeroed();
gl.GenVertexArrays(1, &mut vao);
gl.BindVertexArray(vao);
let mut vbo = std::mem::zeroed();
gl.GenBuffers(1, &mut vbo);
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
gl.BufferData(
gl::ARRAY_BUFFER,
(VERTEX_DATA.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
VERTEX_DATA.as_ptr() as *const _,
gl::STATIC_DRAW,
);
let pos_attrib =
gl.GetAttribLocation(program, c"position".as_ptr() as *const _);
gl.VertexAttribPointer(
pos_attrib as gl::types::GLuint,
3,
gl::FLOAT,
0,
6 * std::mem::size_of::<f32>() as gl::types::GLsizei,
std::ptr::null(),
);
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
let color_attrib =
gl.GetAttribLocation(program, c"color".as_ptr() as *const _);
gl.VertexAttribPointer(
color_attrib as gl::types::GLuint,
3,
gl::FLOAT,
0,
6 * std::mem::size_of::<f32>() as gl::types::GLsizei,
(3 * std::mem::size_of::<f32>()) as *const () as *const _,
);
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
let rotation =
gl.GetUniformLocation(program, c"rotation".as_ptr() as *const _);
Self { gl, program, vao, vbo, rotation }
}
}
fn draw(&self, state: &mut State) {
let time = Instant::now().duration_since(state.begin).as_millis() % 5000;
let phi = (time as f32) / 5000.0 * 2.0 * std::f32::consts::PI;
let rotation: [f32; 9] = [ phi.cos(), 0.0, phi.sin(),
0.0, 1.0, 0.0,
-phi.sin(), 0.0, phi.cos()];
unsafe {
self.gl.UseProgram(self.program);
self.gl.UniformMatrix3fv(self.rotation, 1, 1, rotation.as_ptr());
self.gl.BindVertexArray(self.vao);
self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
self.gl.ClearColor(0.1, 0.1, 0.1, 0.9);
self.gl.Clear(gl::COLOR_BUFFER_BIT);
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
}
}
fn resize(&mut self, width: i32, height: i32) {
unsafe {
self.gl.Viewport(0, 0, width, height);
}
}
}
impl Drop for Renderer {
fn drop(&mut self) {
unsafe {
self.gl.DeleteProgram(self.program);
self.gl.DeleteBuffers(1, &self.vbo);
self.gl.DeleteVertexArrays(1, &self.vao);
}
}
}
fn handle_event(_app_state: &mut State, event: WindowEvent)
-> Result<AppControl, Box<dyn Error>> {
let mut exit = false;
match event {
WindowEvent::CloseRequested => {
exit = true;
}
WindowEvent::KeyboardInput { event: KeyEvent { logical_key: Key::Named(Escape), .. }, .. } => {
exit = true;
}
_ => (),
}
Ok(if exit { AppControl::Exit } else { AppControl::Continue })
}
fn main() -> Result<(), Box<dyn Error>> {
let app_state = State{
begin: Instant::now(),
};
glwindow::Window::<_,_,Renderer>::new()
.run(app_state, handle_event as glwindow::HandleFn<_>)
}