summaryrefslogtreecommitdiff
path: root/engine/src/rendering/backend/opengl/graphics_mesh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/rendering/backend/opengl/graphics_mesh.rs')
-rw-r--r--engine/src/rendering/backend/opengl/graphics_mesh.rs263
1 files changed, 263 insertions, 0 deletions
diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs
new file mode 100644
index 0000000..899f50f
--- /dev/null
+++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs
@@ -0,0 +1,263 @@
+use std::hint::cold_path;
+
+use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage};
+use opengl_bindings::vertex_array::{
+ AttributeFormat as GlVertexArrayAttributeFormat,
+ BindVertexBufferError as GlVertexArrayBindVertexBufferError,
+ DataType as GlVertexArrayDataType,
+ VertexArray as GlVertexArray,
+ VertexBufferSpec as GlVertexArrayVertexBufferSpec,
+};
+use opengl_bindings::MaybeCurrentContextWithFns as GlCurrentContextWithFns;
+
+use crate::mesh::vertex_buffer::VertexAttrProperties as MeshVertexAttrProperties;
+use crate::mesh::{Mesh, VertexAttrType};
+use crate::rendering::shader::{
+ VertexDescription as ShaderVertexDescription,
+ VertexInputSemName,
+};
+use crate::rendering::MeshUsage;
+
+const VERTEX_BUF_BINDING_INDEX: u32 = 0;
+
+#[derive(Debug)]
+pub struct GraphicsMesh
+{
+ /// Vertex and index buffer has to live as long as the vertex array
+ vertex_buffer: GlBuffer<u8>,
+ vertex_attr_props: Vec<MeshVertexAttrProperties>,
+ last_max_vertex_attr_index: u32,
+ pub index_buffer: Option<GlBuffer<u32>>,
+ pub element_cnt: u32,
+ pub vertex_arr: GlVertexArray,
+}
+
+impl GraphicsMesh
+{
+ #[tracing::instrument(skip_all)]
+ pub fn new(
+ current_context: &GlCurrentContextWithFns,
+ mesh: &Mesh,
+ mesh_usage: MeshUsage,
+ ) -> Result<Self, Error>
+ {
+ let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage);
+
+ let vertex_arr = GlVertexArray::new(current_context);
+ let vertex_buffer = GlBuffer::new(current_context);
+
+ vertex_buffer
+ .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage)
+ .map_err(Error::StoreVerticesFailed)?;
+
+ if let Err(err) = vertex_arr.bind_vertex_buffer(
+ current_context,
+ VERTEX_BUF_BINDING_INDEX,
+ &vertex_buffer,
+ GlVertexArrayVertexBufferSpec {
+ offset: 0,
+ vertex_size: mesh.vertex_buf().vertex_size(),
+ },
+ ) {
+ match err {
+ GlVertexArrayBindVertexBufferError::OffsetValueTooLarge {
+ value: _,
+ max_value: _,
+ } => unreachable!(),
+ GlVertexArrayBindVertexBufferError::VertexSizeValueTooLarge {
+ value,
+ max_value,
+ } => {
+ panic!(
+ "Size of vertex ({value}) is too large. Must be less than {max_value}"
+ );
+ }
+ }
+ }
+
+ if let Some(indices) = mesh.indices() {
+ let index_buffer = GlBuffer::new(current_context);
+
+ index_buffer
+ .store(current_context, indices, buffer_usage)
+ .map_err(Error::StoreIndicesFailed)?;
+
+ vertex_arr.bind_element_buffer(current_context, &index_buffer);
+
+ return Ok(Self {
+ vertex_buffer,
+ vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(),
+ last_max_vertex_attr_index: 0,
+ index_buffer: Some(index_buffer),
+ element_cnt: indices
+ .len()
+ .try_into()
+ .expect("Mesh index count does not fit into a 32-bit unsigned int"),
+ vertex_arr,
+ });
+ }
+
+ Ok(Self {
+ vertex_buffer,
+ vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(),
+ last_max_vertex_attr_index: 0,
+ index_buffer: None,
+ element_cnt: mesh
+ .vertex_buf()
+ .len()
+ .try_into()
+ .expect("Mesh vertex count does not fit into a 32-bit unsigned int"),
+ vertex_arr,
+ })
+ }
+
+ pub fn update(
+ &mut self,
+ current_context: &GlCurrentContextWithFns,
+ mesh: &Mesh,
+ mesh_usage: MeshUsage,
+ ) -> Result<(), Error>
+ {
+ let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage);
+
+ self.vertex_buffer
+ .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage)
+ .map_err(Error::StoreVerticesFailed)?;
+
+ if let Some(indices) = mesh.indices() {
+ let index_buffer = self
+ .index_buffer
+ .get_or_insert_with(|| GlBuffer::new(current_context));
+
+ index_buffer
+ .store(current_context, indices, buffer_usage)
+ .map_err(Error::StoreIndicesFailed)?;
+
+ self.vertex_arr
+ .bind_element_buffer(current_context, index_buffer);
+
+ self.element_cnt = indices
+ .len()
+ .try_into()
+ .expect("Mesh index count does not fit into a 32-bit unsigned int");
+
+ return Ok(());
+ }
+
+ if let Some(index_buffer) = self.index_buffer.take() {
+ index_buffer.delete(current_context);
+ self.vertex_arr.unbind_element_buffer(current_context);
+ }
+
+ self.element_cnt = mesh
+ .vertex_buf()
+ .len()
+ .try_into()
+ .expect("Mesh vertex count does not fit into a 32-bit unsigned int");
+
+ Ok(())
+ }
+
+ pub fn update_vertex_attrs_for_shader(
+ &mut self,
+ curr_gl_ctx: &GlCurrentContextWithFns,
+ shader_vertex_desc: &ShaderVertexDescription,
+ ) -> Result<(), VertexAttrsUpdatingError>
+ {
+ for index in 0..self.last_max_vertex_attr_index {
+ self.vertex_arr.disable_attrib(curr_gl_ctx, index);
+ }
+
+ let mut max_vertex_attr_index = 0u32;
+
+ for vertex_input_desc in &*shader_vertex_desc.inputs {
+ let Some(vertex_attr_props) =
+ self.vertex_attr_props.iter().find(|vertex_attr_props| {
+ vertex_input_desc
+ .semantic_name
+ .matches_vertex_label(&vertex_attr_props.label)
+ })
+ else {
+ cold_path();
+ return Err(VertexAttrsUpdatingError::MissingVertexAttr(
+ vertex_input_desc.semantic_name.clone(),
+ ));
+ };
+
+ let attrib_index: u32 = vertex_input_desc.index.try_into().unwrap();
+
+ self.vertex_arr.enable_attrib(curr_gl_ctx, attrib_index);
+
+ self.vertex_arr.set_attrib_format(
+ curr_gl_ctx,
+ attrib_index,
+ match &vertex_attr_props.ty {
+ VertexAttrType::Float32 => GlVertexArrayAttributeFormat {
+ data_type: GlVertexArrayDataType::Float,
+ count: 1,
+ normalized: false,
+ offset: vertex_attr_props.byte_offset.try_into().unwrap(),
+ },
+ VertexAttrType::Float32Array { length } => {
+ GlVertexArrayAttributeFormat {
+ data_type: GlVertexArrayDataType::Float,
+ count: (*length).try_into().unwrap(),
+ normalized: false,
+ offset: vertex_attr_props.byte_offset.try_into().unwrap(),
+ }
+ }
+ },
+ );
+
+ self.vertex_arr.set_attrib_vertex_buf_binding(
+ curr_gl_ctx,
+ attrib_index,
+ VERTEX_BUF_BINDING_INDEX,
+ );
+
+ max_vertex_attr_index = max_vertex_attr_index.max(attrib_index);
+ }
+
+ self.last_max_vertex_attr_index = max_vertex_attr_index;
+
+ Ok(())
+ }
+
+ pub fn destroy(&self, curr_gl_ctx: &GlCurrentContextWithFns)
+ {
+ self.vertex_arr.delete(curr_gl_ctx);
+ self.vertex_buffer.delete(curr_gl_ctx);
+
+ if let Some(index_buffer) = &self.index_buffer {
+ index_buffer.delete(curr_gl_ctx);
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Failed to store vertices in vertex buffer")]
+ StoreVerticesFailed(#[source] opengl_bindings::buffer::Error),
+
+ #[error("Failed to store indices in index buffer")]
+ StoreIndicesFailed(#[source] opengl_bindings::buffer::Error),
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum VertexAttrsUpdatingError
+{
+ #[error(
+ "Mesh is missing equivalent vertex attribute for shader's vertex input: {0}"
+ )]
+ MissingVertexAttr(VertexInputSemName),
+}
+
+fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage
+{
+ match mesh_usage {
+ MeshUsage::Stream => GlBufferUsage::Stream,
+ MeshUsage::Static => GlBufferUsage::Static,
+ MeshUsage::Dynamic => GlBufferUsage::Dynamic,
+ }
+}