extern crate futures_util;
extern crate gl;
extern crate glutin;
extern crate ktx_async as ktx;
extern crate lazy_static;
extern crate tokio;
use futures_util::stream::StreamExt as _;
use gl::types::*;
use lazy_static::lazy_static;
use std::path::Path;
#[tokio::main]
async fn main() {
let event_loop = glutin::event_loop::EventLoop::new();
let glctx = {
let wb = glutin::window::WindowBuilder::new()
.with_title("TestKTX: Basic")
.with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
let cb = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)));
let ctx = cb.build_windowed(wb, &event_loop).unwrap();
unsafe { ctx.make_current().unwrap() }
};
gl::load_with(|s| glctx.get_proc_address(s) as *const _);
let program = {
let vscode = include_bytes!("simple.essl100.vert");
let fscode = include_bytes!("simple.essl100.frag");
let vs = compile_shader(gl::VERTEX_SHADER, vscode).unwrap();
let fs = compile_shader(gl::FRAGMENT_SHADER, fscode).unwrap();
let prog = link_program(&[vs, fs]).unwrap();
unsafe {
gl::DeleteShader(vs);
gl::DeleteShader(fs);
}
prog
};
let position_attrib =
unsafe { gl::GetAttribLocation(program, b"position\0".as_ptr() as *const GLchar) };
let texcoord_attrib =
unsafe { gl::GetAttribLocation(program, b"texcoord\0".as_ptr() as *const GLchar) };
let tex_uniform =
unsafe { gl::GetUniformLocation(program, b"tex\0".as_ptr() as *const GLchar) };
let vertex_buffer = {
let data = &[
-0.5_f32, 0.5_f32, 0.0_f32, 0.0_f32, -0.5_f32, -0.5_f32, 0.0_f32, 1.0_f32, 0.5_f32,
0.5_f32, 1.0_f32, 0.0_f32, 0.5_f32, -0.5_f32, 1.0_f32, 1.0_f32,
];
let mut obj: GLuint = 0;
unsafe {
gl::GenBuffers(1, &mut obj);
gl::BindBuffer(gl::ARRAY_BUFFER, obj);
gl::BufferData(
gl::ARRAY_BUFFER,
(data.len() * 4) as GLsizeiptr,
data.as_ptr() as *const GLvoid,
gl::STATIC_DRAW,
);
}
obj
};
let texture = load_texture("data/pvr/block6.ktx").await;
event_loop.run(move |event, _target, control_flow| {
use glutin::event::{Event, WindowEvent};
use glutin::event_loop::ControlFlow;
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { ref event, .. } => match event {
WindowEvent::Resized(logical_size) => {
let dpi_factor = glctx.window().hidpi_factor();
glctx.resize(logical_size.to_physical(dpi_factor));
}
WindowEvent::RedrawRequested => {
unsafe {
gl::Clear(gl::COLOR_BUFFER_BIT);
}
unsafe {
gl::UseProgram(program);
}
unsafe {
if tex_uniform >= 0 {
gl::Uniform1i(tex_uniform, 0);
}
}
unsafe {
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, texture);
}
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
if position_attrib >= 0 {
let loc = position_attrib as GLuint;
let off = std::mem::transmute(0_usize);
gl::EnableVertexAttribArray(loc);
gl::VertexAttribPointer(loc, 2, gl::FLOAT, gl::FALSE, 16, off);
}
if texcoord_attrib >= 0 {
let loc = texcoord_attrib as GLuint;
let off = std::mem::transmute(8_usize);
gl::EnableVertexAttribArray(loc);
gl::VertexAttribPointer(loc, 2, gl::FLOAT, gl::FALSE, 16, off);
}
}
unsafe {
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
}
unsafe {
gl::Flush();
}
glctx.swap_buffers().unwrap();
}
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
return;
}
_ => return,
},
Event::LoopDestroyed => {
unsafe {
gl::DeleteBuffers(1, &vertex_buffer);
gl::DeleteTextures(1, &texture);
gl::DeleteProgram(program);
}
return;
}
_ => (),
}
});
}
fn compile_shader(ty: GLenum, src: &[u8]) -> Result<GLuint, String> {
use std::ptr::null_mut;
let id = unsafe { gl::CreateShader(ty) };
unsafe {
let len = src.len() as GLint;
let ptr = src.as_ptr() as *const GLchar;
gl::ShaderSource(id, 1, &ptr, &len);
}
unsafe {
gl::CompileShader(id);
}
let success = unsafe {
let mut status: GLint = 0;
gl::GetShaderiv(id, gl::COMPILE_STATUS, &mut status);
status == gl::TRUE as GLint
};
if success {
Ok(id)
} else {
let len = unsafe {
let mut x: GLint = 0;
gl::GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut x);
(x) as usize
};
let mut buf: Vec<u8> = vec![0; len];
unsafe {
gl::GetShaderInfoLog(id, len as _, null_mut(), buf.as_mut_ptr() as _);
gl::DeleteShader(id);
}
buf.pop(); let e = String::from_utf8(buf).unwrap();
Err(e)
}
}
fn link_program(shader_objs: &[GLuint]) -> Result<GLuint, String> {
use std::ptr::null_mut;
let id = unsafe { gl::CreateProgram() };
for &shader_obj in shader_objs {
unsafe {
gl::AttachShader(id, shader_obj);
}
}
unsafe {
gl::LinkProgram(id);
}
for &shader_obj in shader_objs {
unsafe {
gl::DetachShader(id, shader_obj);
}
}
let success = unsafe {
let mut status: GLint = 0;
gl::GetProgramiv(id, gl::LINK_STATUS, &mut status);
status == gl::TRUE as GLint
};
if success {
Ok(id)
} else {
let len = unsafe {
let mut x: GLint = 0;
gl::GetProgramiv(id, gl::INFO_LOG_LENGTH, &mut x);
(x) as usize
};
let mut buf: Vec<u8> = vec![0; len];
unsafe {
gl::GetProgramInfoLog(id, len as _, null_mut(), buf.as_mut_ptr() as _);
gl::DeleteProgram(id);
}
buf.pop(); let e = String::from_utf8(buf).unwrap();
Err(e)
}
}
async fn load_texture(path: impl AsRef<Path>) -> GLuint {
use tokio::fs::File;
let file = File::open(PROJECT_DIR.join(path.as_ref())).await.unwrap();
let decoder = ktx::Decoder::new(file);
let (info, mut stream) = decoder.read_async().await.unwrap();
let texture_obj = unsafe {
let mut x: GLuint = 0;
gl::GenTextures(1, &mut x);
gl::BindTexture(gl::TEXTURE_2D, x);
x
};
while let Some((frame, buf)) = stream.next().await.map(|r| r.unwrap()) {
unsafe {
gl::TexImage2D(
gl::TEXTURE_2D,
frame.level as GLint,
info.gl_internal_format as GLint,
frame.pixel_width as GLsizei,
frame.pixel_height as GLsizei,
0,
info.gl_format as GLenum,
info.gl_type as GLenum,
buf.as_ptr() as *const GLvoid,
);
}
}
texture_obj
}
lazy_static! {
static ref PROJECT_DIR: std::path::PathBuf = {
use std::env::var_os;
var_os("CARGO_MANIFEST_DIR")
.map(|s| std::path::PathBuf::from(s))
.unwrap()
};
}