use anyhow::Result;
use cad_import::structure::{IndexData, Mesh, PrimitiveType, Vertices};
use glow::HasContext;
use crate::{gl_call, viewer::gl_call::handle_glow_error};
use super::buffer::Buffer;
type VertexBuffer<C> = Buffer<C, { glow::ARRAY_BUFFER }>;
type IndexBuffer<C> = Buffer<C, { glow::ELEMENT_ARRAY_BUFFER }>;
struct VertexAttributes<C: HasContext> {
pub position: VertexBuffer<C>,
pub normal: Option<VertexBuffer<C>>,
}
pub struct GPUMesh<C: HasContext> {
vertex_array: C::VertexArray,
vertices: VertexAttributes<C>,
primitive_type: u32,
num_indices: u32,
indices: Option<IndexBuffer<C>>,
}
impl<C: HasContext> GPUMesh<C> {
pub fn new(context: &C, mesh: &Mesh) -> Result<Self> {
let primitives = mesh.get_primitives();
let primitive_type = Self::translate_primitive_type(primitives.get_primitive_type())?;
let num_indices = primitives.get_raw_index_data().num_indices() as u32;
let vertices = mesh.get_vertices();
let (vertices, vertex_array) = Self::create_vertex_data(context, vertices)?;
let indices = match primitives.get_raw_index_data() {
IndexData::Indices(raw_indices) => {
let indices = IndexBuffer::<C>::new(context)?;
indices.set_data(context, raw_indices, super::buffer::Usage::Static);
Some(indices)
}
IndexData::NonIndexed(_) => None,
};
Ok(Self {
vertex_array,
vertices,
primitive_type,
num_indices,
indices,
})
}
pub fn draw(&self, context: &C) {
gl_call!(context, bind_vertex_array, Some(self.vertex_array));
match &self.indices {
Some(indices) => {
indices.bind(context);
gl_call!(
context,
draw_elements,
self.primitive_type,
self.num_indices as i32,
glow::UNSIGNED_INT,
0
);
}
None => {
gl_call!(
context,
draw_arrays,
self.primitive_type,
0,
self.num_indices as i32
);
}
}
gl_call!(context, bind_vertex_array, None);
}
pub fn has_normals(&self) -> bool {
self.vertices.normal.is_some()
}
fn translate_primitive_type(primitive_type: PrimitiveType) -> Result<u32> {
match primitive_type {
PrimitiveType::Point => Ok(glow::POINTS),
PrimitiveType::Line => Ok(glow::LINES),
PrimitiveType::LineLoop => Ok(glow::LINE_LOOP),
PrimitiveType::LineStrip => Ok(glow::LINE_STRIP),
PrimitiveType::Triangles => Ok(glow::TRIANGLES),
PrimitiveType::TriangleFan => Ok(glow::TRIANGLE_FAN),
PrimitiveType::TriangleStrip => Ok(glow::TRIANGLE_STRIP),
}
}
fn create_vertex_data(
context: &C,
vertices: &Vertices,
) -> Result<(VertexAttributes<C>, C::VertexArray)> {
let position = VertexBuffer::<C>::new(context)?;
position.set_data(
context,
vertices.get_positions().as_slice(),
super::buffer::Usage::Static,
);
let normal = match vertices.get_normals() {
Some(normal_data) => {
let normal = VertexBuffer::<C>::new(context)?;
normal.set_data(
context,
normal_data.as_slice(),
super::buffer::Usage::Static,
);
Some(normal)
}
None => None,
};
let vertex_attributes = VertexAttributes { position, normal };
let vertex_array = handle_glow_error(gl_call!(context, create_vertex_array))?;
Self::initialize_vertex_array(context, vertex_array, &vertex_attributes);
gl_call!(context, bind_vertex_array, None);
Ok((vertex_attributes, vertex_array))
}
fn initialize_vertex_array(
context: &C,
vertex_array: C::VertexArray,
attributes: &VertexAttributes<C>,
) {
gl_call!(context, bind_vertex_array, Some(vertex_array));
attributes.position.bind(context);
gl_call!(context, enable_vertex_attrib_array, 0);
gl_call!(
context,
vertex_attrib_pointer_f32,
0,
3,
glow::FLOAT,
false,
0,
0
);
match &attributes.normal {
Some(normals) => {
normals.bind(context);
gl_call!(context, enable_vertex_attrib_array, 1);
gl_call!(
context,
vertex_attrib_pointer_f32,
1,
3,
glow::FLOAT,
false,
0,
0
);
}
None => {
gl_call!(context, disable_vertex_attrib_array, 1);
}
}
gl_call!(context, bind_vertex_array, None);
}
}