use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use std::marker::PhantomData;
use std::mem::offset_of;
use anyhow::Context as _;
use anyhow::Result;
use crate::sys;
use crate::sys::Gl as _;
#[derive(Debug, Eq, PartialEq)]
pub enum AttribType {
Position,
Normal,
Texture,
Color,
}
impl Display for AttribType {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let ty = match self {
Self::Position => "position",
Self::Normal => "normal",
Self::Texture => "texture",
Self::Color => "color",
};
f.write_str(ty)
}
}
#[derive(Debug)]
pub struct Attrib {
pub size: i32,
pub type_: sys::Type,
pub normalize: bool,
pub stride: i32,
pub offset: i32,
}
pub trait Attribs {
const ATTRIBS: &'static [(AttribType, Attrib)];
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct VertexP3f {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Attribs for VertexP3f {
const ATTRIBS: &'static [(AttribType, Attrib)] = &[(
AttribType::Position,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: 0,
},
)];
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct VertexP3fT2f {
pub x: f32,
pub y: f32,
pub z: f32,
pub u: f32,
pub v: f32,
}
impl Attribs for VertexP3fT2f {
const ATTRIBS: &'static [(AttribType, Attrib)] = &[
(
AttribType::Position,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: 0,
},
),
(
AttribType::Texture,
Attrib {
size: 2,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: offset_of!(Self, u) as _,
},
),
];
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct VertexP3fN3f {
pub x: f32,
pub y: f32,
pub z: f32,
pub nx: f32,
pub ny: f32,
pub nz: f32,
}
impl Attribs for VertexP3fN3f {
const ATTRIBS: &'static [(AttribType, Attrib)] = &[
(
AttribType::Position,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: 0,
},
),
(
AttribType::Normal,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: offset_of!(Self, nx) as _,
},
),
];
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct VertexP3fT2fN3f {
pub x: f32,
pub y: f32,
pub z: f32,
pub u: f32,
pub v: f32,
pub nx: f32,
pub ny: f32,
pub nz: f32,
}
impl Attribs for VertexP3fT2fN3f {
const ATTRIBS: &'static [(AttribType, Attrib)] = &[
(
AttribType::Position,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: 0,
},
),
(
AttribType::Texture,
Attrib {
size: 2,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: offset_of!(Self, u) as _,
},
),
(
AttribType::Normal,
Attrib {
size: 3,
type_: sys::Type::Float,
normalize: false,
stride: size_of::<Self>() as _,
offset: offset_of!(Self, nx) as _,
},
),
];
}
#[derive(Debug)]
pub struct VertexBuffer<T> {
context: sys::Context,
vbo: sys::VertexBufferObject,
target: sys::VertexBufferTarget,
count: usize,
_phantom: PhantomData<T>,
}
impl<T> VertexBuffer<T>
where
T: Sized,
{
#[inline]
pub fn from_vertices(
vertices: &[T],
usage: sys::VertexBufferUsage,
context: &sys::Context,
) -> Result<Self> {
Self::from_data(sys::VertexBufferTarget::Array, usage, vertices, context)
}
#[inline]
pub fn from_indices(
indices: &[T],
usage: sys::VertexBufferUsage,
context: &sys::Context,
) -> Result<Self> {
Self::from_data(
sys::VertexBufferTarget::ElementArray,
usage,
indices,
context,
)
}
fn from_data(
target: sys::VertexBufferTarget,
usage: sys::VertexBufferUsage,
data: &[T],
context: &sys::Context,
) -> Result<Self> {
let vbo = context
.create_vertex_buffer()
.context("failed to create vertex buffer")?;
let slf = Self {
context: context.clone(),
vbo,
target,
count: data.len(),
_phantom: PhantomData,
};
let () = slf.bind();
let () = context.set_vertex_buffer_data(target, usage, data);
let () = slf.unbind();
Ok(slf)
}
pub fn update(&self, data: &[T], offset: usize) {
debug_assert!(
offset + data.len() <= self.count,
"{offset} | {} | {}",
data.len(),
self.count
);
let () = self.bind();
let () =
self
.context
.set_vertex_buffer_sub_data(self.target, data, i32::try_from(offset).unwrap());
let () = self.unbind();
}
#[inline]
pub fn bind(&self) {
let () = self
.context
.bind_vertex_buffer(self.target, Some(&self.vbo));
}
#[inline]
pub fn unbind(&self) {
let () = self.context.bind_vertex_buffer(self.target, None);
}
#[inline]
pub fn item_count(&self) -> usize {
self.count
}
}
impl<T> Drop for VertexBuffer<T> {
#[inline]
fn drop(&mut self) {
let () = self.context.delete_vertex_buffer(&self.vbo);
}
}
#[derive(Debug)]
pub struct VertexArray {
context: sys::Context,
vao: sys::VertexArrayObject,
}
impl VertexArray {
pub fn new<V>(
vertex_buffer: &VertexBuffer<V>,
attrib_indices: &[(u32, AttribType)],
context: &sys::Context,
) -> Result<Self>
where
V: Attribs,
{
let vertex_array = context.create_vertex_array()?;
let slf = Self {
context: context.clone(),
vao: vertex_array,
};
let () = slf.bind();
let () = vertex_buffer.bind();
let result = V::ATTRIBS.iter().try_for_each(|(attrib_type, attrib)| {
let (idx, _) = attrib_indices
.iter()
.find(|(_, ty)| ty == attrib_type)
.with_context(|| format!("failed to find {attrib_type} vertex attribute index"))?;
let () = context.enable_vertex_attrib_array(*idx);
let () = context.set_vertex_attrib_pointer(
*idx,
attrib.size,
attrib.type_,
attrib.normalize,
attrib.stride,
attrib.offset,
);
Ok(())
});
let () = vertex_buffer.unbind();
let () = slf.unbind();
result.map(|()| slf)
}
#[inline]
pub fn bind(&self) {
let () = self.context.bind_vertex_array(Some(&self.vao));
}
#[inline]
fn unbind(&self) {
let () = self.context.bind_vertex_array(None);
}
}
impl Drop for VertexArray {
#[inline]
fn drop(&mut self) {
let () = self.context.delete_vertex_array(&self.vao);
}
}