use std::{
marker::PhantomData,
mem::{self, size_of},
ops::{Deref, DerefMut},
ptr,
};
use ogl33::{
consts::{
GL_ARRAY_BUFFER, GL_BGRA, GL_CLAMP_TO_EDGE, GL_FALSE, GL_FLOAT, GL_NEAREST, GL_RGBA, GL_STATIC_DRAW,
GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T,
GL_TRIANGLES, GL_UNSIGNED_INT_8_8_8_8,
},
functions::{
glActiveTexture, glBindBuffer, glBindTexture, glBindVertexArray, glBufferData, glDeleteBuffers,
glDeleteTextures, glDeleteVertexArrays, glDrawArrays, glEnableVertexAttribArray, glGenBuffers, glGenTextures,
glGenVertexArrays, glTexImage2D, glTexParameteri, glTexSubImage2D, glVertexAttribPointer,
},
types::{GLint, GLsizei, GLsizeiptr, GLuint, GLvoid},
};
use crate::{Pel, RawSurface, Surface};
static VERTICES: [f32; QUAD_VERTEX_BUFFER_LEN] = [
-1.0, 1.0, 0.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 0.0,
];
const QUAD_VERTEX_BUFFER_LEN: usize = 24;
const QUAD_VERTEX_ELEMENTS: usize = 4;
const QUAD_VERTEX_COUNT: usize = QUAD_VERTEX_BUFFER_LEN / QUAD_VERTEX_ELEMENTS;
pub struct Texture {
vao: GLuint,
vbo: GLuint,
tex: GLuint,
inner: Surface,
}
impl Texture {
pub(crate) fn new(w: usize, h: usize) -> Self {
let mut vao: GLuint = 0;
let mut vbo: GLuint = 0;
let mut tex: GLuint = 0;
unsafe {
glGenVertexArrays(1, &mut vao);
glBindVertexArray(vao);
glGenBuffers(1, &mut vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(
GL_ARRAY_BUFFER,
(VERTICES.len() * size_of::<f32>()) as GLsizeiptr,
VERTICES.as_ptr() as *const GLvoid,
GL_STATIC_DRAW,
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, QUAD_VERTEX_ELEMENTS as GLint, GL_FLOAT, GL_FALSE, 0, ptr::null());
glGenTextures(1, &mut tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE as GLint);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE as GLint);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST as GLint);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST as GLint);
format_texture(w as GLsizei, h as GLsizei);
}
Self {
vao,
vbo,
tex,
inner: Surface::new(w, h),
}
}
pub(crate) fn resize(&mut self, w: usize, h: usize) {
mem::replace(&mut self.inner, Surface::new(w, h));
unsafe {
format_texture(w as GLsizei, h as GLsizei);
}
}
pub(crate) fn texture(&mut self) -> TextureHandle {
TextureHandle::new(&mut self.inner)
}
pub(crate) fn draw(&self) {
unsafe {
glDrawArrays(GL_TRIANGLES, 0, QUAD_VERTEX_COUNT as GLsizei);
}
}
}
impl Drop for Texture {
fn drop(&mut self) {
unsafe {
glDeleteVertexArrays(1, &self.vao);
glDeleteBuffers(1, &self.vbo);
glDeleteTextures(1, &self.tex);
}
}
}
pub struct TextureHandle<'s> {
inner: &'s mut Surface,
phantom: PhantomData<*const ()>, }
impl<'s> TextureHandle<'s> {
fn new(inner: &'s mut Surface) -> Self {
Self {
inner,
phantom: PhantomData,
}
}
}
impl<'s> Deref for TextureHandle<'s> {
type Target = RawSurface<Pel>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<'s> DerefMut for TextureHandle<'s> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
}
}
impl<'s> Drop for TextureHandle<'s> {
fn drop(&mut self) {
unsafe {
update_texture(
self.inner.width() as GLsizei,
self.inner.height() as GLsizei,
self.inner.as_ptr() as *const GLvoid,
);
}
}
}
unsafe fn format_texture(w: GLsizei, h: GLsizei) {
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA as GLint,
w,
h,
0,
GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8,
ptr::null(),
);
}
unsafe fn update_texture(w: GLsizei, h: GLsizei, data: *const GLvoid) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, data);
}