diff options
| -rw-r--r-- | engine/src/file_format/wavefront/obj.rs | 171 | ||||
| -rw-r--r-- | engine/src/mesh.rs | 117 | ||||
| -rw-r--r-- | engine/src/mesh/cube.rs | 209 | ||||
| -rw-r--r-- | engine/src/mesh/vertex_buffer.rs | 247 | ||||
| -rw-r--r-- | engine/src/renderer/opengl.rs | 6 | ||||
| -rw-r--r-- | engine/src/renderer/opengl/graphics_mesh.rs | 95 | ||||
| -rw-r--r-- | engine/src/shader.rs | 194 |
7 files changed, 642 insertions, 397 deletions
diff --git a/engine/src/file_format/wavefront/obj.rs b/engine/src/file_format/wavefront/obj.rs index 88d566c..446903c 100644 --- a/engine/src/file_format/wavefront/obj.rs +++ b/engine/src/file_format/wavefront/obj.rs @@ -7,13 +7,18 @@ use std::fs::read_to_string; use std::path::PathBuf; use crate::file_format::wavefront::common::{ - keyword, - parse_statement_line, ParsingError, Statement, Triplet, + keyword, + parse_statement_line, +}; +use crate::mesh::vertex_buffer::{ + NamedVertexAttr, + VertexAttrInfo, + VertexBuffer as MeshVertexBuffer, }; -use crate::mesh::{Mesh, Vertex}; +use crate::mesh::{Mesh, POSITION_VERTEX_ATTRIB_NAME, VertexAttrType}; use crate::util::try_option; use crate::vector::{Vec2, Vec3}; @@ -82,7 +87,24 @@ impl Obj /// - A face index does not fit in a [`u32`] pub fn to_mesh(&self) -> Result<Mesh, Error> { - let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3); + let mut vertex_buf = MeshVertexBuffer::with_capacity( + &[ + VertexAttrInfo { + name: POSITION_VERTEX_ATTRIB_NAME.into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + VertexAttrInfo { + name: "texture_coords".into(), + ty: VertexAttrType::Float32Array { length: 2 }, + }, + VertexAttrInfo { + name: "normal".into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + ], + self.faces.len() * 3, + ); + let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3); let mut added_face_vertices = @@ -96,10 +118,75 @@ impl Obj continue; } - vertices.push(face_vertex.to_vertex(self)?); + let pos = self + .vertex_positions + .get(face_vertex.position as usize - 1) + .ok_or(Error::FaceVertexPositionNotFound { + vertex_pos_index: face_vertex.position, + })? + .clone(); + + let texture_pos = face_vertex.texture.map_or_else( + || { + if !self.texture_positions.is_empty() { + tracing::warn!(concat!( + "Wavefront OBJ has texture coordinates ", + "but face vertex does not specify one" + )); + } + + Ok(Vec2::default()) + }, + |face_vertex_texture| { + self.texture_positions + .get(face_vertex_texture as usize - 1) + .ok_or(Error::FaceTexturePositionNotFound { + texture_pos_index: face_vertex_texture, + }) + .cloned() + }, + )?; + + let normal = face_vertex.normal.map_or_else( + || { + if !self.vertex_normals.is_empty() { + tracing::warn!(concat!( + "Wavefront OBJ has normals ", + "but face vertex does not specify one" + )); + } + + Ok(Vec3::default()) + }, + |face_vertex_normal| { + self.vertex_normals + .get(face_vertex_normal as usize - 1) + .ok_or(Error::FaceVertexNormalNotFound { + vertex_normal_index: face_vertex_normal, + }) + .cloned() + }, + )?; + + vertex_buf.push(( + NamedVertexAttr { + name: POSITION_VERTEX_ATTRIB_NAME, + value: pos.into_array(), + }, + NamedVertexAttr { + name: "texture_coords", + value: texture_pos.into_array(), + }, + NamedVertexAttr { + name: "normal", + value: normal.into_array(), + }, + )); + + let vertex_index = vertex_buf.len() - 1; - let vertex_index = u32::try_from(vertices.len() - 1) - .map_err(|_| Error::FaceIndexTooBig(vertices.len() - 1))?; + let vertex_index = u32::try_from(vertex_index) + .map_err(|_| Error::FaceIndexTooBig(vertex_index))?; indices.push(vertex_index); @@ -107,7 +194,10 @@ impl Obj } } - Ok(Mesh::new(vertices, Some(indices))) + Ok(Mesh::builder() + .vertices(vertex_buf) + .indices(indices) + .build()) } /// Reads and parses the material libraries of this `Obj`. @@ -156,51 +246,6 @@ pub struct FaceVertex pub normal: Option<u32>, } -impl FaceVertex -{ - /// Tries to convert this face vertex into a [`Vertex`]. - /// - /// # Errors - /// Returns `Err` if: - /// - The face's vertex position cannot be found in the given [`Obj`] - /// - The face's texture position cannot be found in the given [`Obj`] - /// - The face's vertex normal cannot be found in the given [`Obj`] - pub fn to_vertex(&self, obj: &Obj) -> Result<Vertex, Error> - { - let mut vertex_builder = Vertex::builder(); - - let vertex_pos = *obj.vertex_positions.get(self.position as usize - 1).ok_or( - Error::FaceVertexPositionNotFound { vertex_pos_index: self.position }, - )?; - - vertex_builder = vertex_builder.pos(vertex_pos); - - if let Some(face_vertex_texture) = self.texture { - let texture_pos = obj - .texture_positions - .get(face_vertex_texture as usize - 1) - .ok_or(Error::FaceTexturePositionNotFound { - texture_pos_index: face_vertex_texture, - })?; - - vertex_builder = vertex_builder.texture_coords(*texture_pos); - } - - if let Some(face_vertex_normal) = self.normal { - let vertex_normal = *obj - .vertex_normals - .get(face_vertex_normal as usize - 1) - .ok_or(Error::FaceVertexNormalNotFound { - vertex_normal_index: face_vertex_normal, - })?; - - vertex_builder = vertex_builder.normal(vertex_normal); - } - - Ok(vertex_builder.build()) - } -} - impl From<Triplet> for FaceVertex { fn from(triplet: Triplet) -> Self @@ -219,9 +264,7 @@ pub enum Error #[error(transparent)] ParsingError(#[from] ParsingError), - #[error( - "Face vertex position with index {vertex_pos_index} (1-based) was not found" - )] + #[error("Face vertex position with index {vertex_pos_index} (1-based) was not found")] FaceVertexPositionNotFound { vertex_pos_index: u32 @@ -524,12 +567,16 @@ fn get_mtl_libs_from_statements( return None; } - let mtl_lib_paths = try_option!(statement - .arguments - .iter() - .enumerate() - .map(|(index, value)| Ok(PathBuf::from(value.to_text(index, *line_no)?))) - .collect::<Result<Vec<_>, ParsingError>>()); + let mtl_lib_paths = try_option!( + statement + .arguments + .iter() + .enumerate() + .map(|(index, value)| Ok(PathBuf::from( + value.to_text(index, *line_no)? + ))) + .collect::<Result<Vec<_>, ParsingError>>() + ); Some(Ok(mtl_lib_paths)) }) diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs index f26c9c1..a223f60 100644 --- a/engine/src/mesh.rs +++ b/engine/src/mesh.rs @@ -1,36 +1,29 @@ -use engine_macros::Reflection; -use zerocopy::{Immutable, IntoBytes}; +use std::alloc::Layout; -use crate::builder; use crate::vector::Vec3; pub mod cube; +pub mod vertex_buffer; -#[derive(Debug, Clone, Default)] +pub const POSITION_VERTEX_ATTRIB_NAME: &str = "pos"; + +#[derive(Debug, Clone)] pub struct Mesh { - vertices: Vec<Vertex>, + vertex_buf: vertex_buffer::VertexBuffer, indices: Option<Vec<u32>>, } impl Mesh { - #[must_use] - pub fn new(vertices: Vec<Vertex>, indices: Option<Vec<u32>>) -> Self + pub fn builder() -> Builder { - Self { vertices, indices } + Builder::default() } - #[must_use] - pub fn vertices(&self) -> &[Vertex] + pub fn vertex_buf(&self) -> &vertex_buffer::VertexBuffer { - &self.vertices - } - - #[must_use] - pub fn vertices_mut(&mut self) -> &mut [Vertex] - { - &mut self.vertices + &self.vertex_buf } #[must_use] @@ -49,23 +42,23 @@ impl Mesh /// that this can be quite time-expensive if the mesh has many vertices. pub fn find_furthest_vertex_positions(&self) -> DirectionPositions { - let mut point_iter = self - .vertices() - .iter() - .map(|vertex| Vec3::from(vertex.pos)) + let mut pos_iter = self + .vertex_buf() + .iter::<[f32; 3]>(POSITION_VERTEX_ATTRIB_NAME) + .map(|vertex_pos| Vec3::from(*vertex_pos)) .into_iter(); - let first_point = point_iter.next().unwrap(); + let first_pos = pos_iter.next().unwrap(); - point_iter + pos_iter .fold( FurthestPosAcc { - up: FurthestPos::new(first_point, &Vec3::UP), - down: FurthestPos::new(first_point, &Vec3::DOWN), - left: FurthestPos::new(first_point, &Vec3::LEFT), - right: FurthestPos::new(first_point, &Vec3::RIGHT), - back: FurthestPos::new(first_point, &Vec3::BACK), - front: FurthestPos::new(first_point, &Vec3::FRONT), + up: FurthestPos::new(first_pos, &Vec3::UP), + down: FurthestPos::new(first_pos, &Vec3::DOWN), + left: FurthestPos::new(first_pos, &Vec3::LEFT), + right: FurthestPos::new(first_pos, &Vec3::RIGHT), + back: FurthestPos::new(first_pos, &Vec3::BACK), + front: FurthestPos::new(first_pos, &Vec3::FRONT), }, |mut furthest_pos_acc, pos| { furthest_pos_acc.up.update_if_further(pos); @@ -82,50 +75,60 @@ impl Mesh } } -builder! { -#[builder(name = VertexBuilder, derives = (Debug, Default, Clone))] -#[derive(Debug, Clone, Default, PartialEq, IntoBytes, Immutable, Reflection)] -#[non_exhaustive] -pub struct Vertex +/// Mesh builder +#[derive(Debug, Clone, Default)] +pub struct Builder { - #[builder(skip_generate_fn)] - pub pos: [f32; 3], - - #[builder(skip_generate_fn)] - pub texture_coords: [f32; 2], - - #[builder(skip_generate_fn)] - pub normal: [f32; 3], -} + vertex_buf: vertex_buffer::VertexBuffer, + indices: Option<Vec<u32>>, } -impl Vertex +impl Builder { - #[must_use] - pub fn builder() -> VertexBuilder + pub fn new() -> Self { - VertexBuilder::default() + Self::default() } -} -impl VertexBuilder -{ - pub fn pos(mut self, pos: impl Into<[f32; 3]>) -> Self + pub fn vertices(mut self, vertices_bytes: vertex_buffer::VertexBuffer) -> Self { - self.pos = pos.into(); + self.vertex_buf = vertices_bytes; self } - pub fn texture_coords(mut self, texture_coords: impl Into<[f32; 2]>) -> Self + pub fn indices(mut self, indices: impl IntoIterator<Item = u32>) -> Self { - self.texture_coords = texture_coords.into(); + self.indices = Some(indices.into_iter().collect()); self } - pub fn normal(mut self, normal: impl Into<[f32; 3]>) -> Self + pub fn build(self) -> Mesh { - self.normal = normal.into(); - self + Mesh { + vertex_buf: self.vertex_buf, + indices: self.indices, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum VertexAttrType +{ + Float32, + Float32Array + { + length: usize, + }, +} + +impl VertexAttrType +{ + pub fn layout(&self) -> Layout + { + match self { + Self::Float32 => Layout::new::<f32>(), + Self::Float32Array { length } => Layout::array::<f32>(*length).unwrap(), + } } } diff --git a/engine/src/mesh/cube.rs b/engine/src/mesh/cube.rs index fe45a4a..dba6473 100644 --- a/engine/src/mesh/cube.rs +++ b/engine/src/mesh/cube.rs @@ -1,7 +1,14 @@ +use std::collections::HashMap; + use crate::builder; use crate::data_types::dimens::Dimens3; use crate::math::calc_triangle_surface_normal; -use crate::mesh::{Mesh, Vertex}; +use crate::mesh::vertex_buffer::{ + NamedVertexAttr, + VertexAttrInfo, + VertexBuffer as MeshVertexBuffer, +}; +use crate::mesh::{Mesh, POSITION_VERTEX_ATTRIB_NAME, VertexAttrType}; use crate::vector::{Vec2, Vec3}; builder! { @@ -39,29 +46,6 @@ impl CreationSpecBuilder } } -/// Describes a single side of a cube (obviously). -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Side -{ - /// +Z - Front, - - /// -Z - Back, - - /// -X - Left, - - /// +X - Right, - - /// +Y - Top, - - /// -Y - Bottom, -} - /// Describes what location on a side of a cube a face is. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum FaceLocation @@ -77,11 +61,10 @@ pub enum FaceLocation /// /// By default, the texture coordinates are arranged so that the full texture is visible /// on every side. This can be changed inside of the `face_cb` function. -pub fn create( - creation_spec: CreationSpec, - face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices, -) -> Mesh +pub fn create(creation_spec: CreationSpec) -> Mesh { + // TODO: Reimplement this mess + let mut data = Data::default(); create_side(&SidePositions::new_top(&creation_spec), &mut data); @@ -91,7 +74,7 @@ pub fn create( create_side(&SidePositions::new_back(&creation_spec), &mut data); create_side(&SidePositions::new_front(&creation_spec), &mut data); - data.into_mesh(face_cb) + data.into_mesh() } #[derive(Debug, Default)] @@ -110,88 +93,55 @@ struct VertexData impl Data { - fn into_mesh( - self, - mut face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices, - ) -> Mesh + fn into_mesh(self) -> Mesh { - let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3); + let mut vertex_buf = MeshVertexBuffer::with_capacity( + &[ + VertexAttrInfo { + name: POSITION_VERTEX_ATTRIB_NAME.into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + VertexAttrInfo { + name: "texture_coords".into(), + ty: VertexAttrType::Float32Array { length: 2 }, + }, + VertexAttrInfo { + name: "normal".into(), + ty: VertexAttrType::Float32Array { length: 3 }, + }, + ], + self.faces.len() * 3, + ); + let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3); + let mut added_face_vertices = HashMap::<FaceVertex, u32>::new(); + let mut face_location = FaceLocation::RightUp; let Self { faces, vertex_data } = self; for face in faces { - let side = face.side; - - let face_vertices = face_cb( - FaceVertices::new(face, &vertex_data) - .with_full_per_side_tex_coords(face_location), - side, - face_location, - ); - - for vertex in face_vertices.vertices { - if let Some((prev_vertex_index, _)) = vertices - .iter() - .enumerate() - .find(|(_, prev_vertex)| *prev_vertex == &vertex) - { - indices - .push(u32::try_from(prev_vertex_index).expect( - "Vertex index does not fit into 32-bit unsigned int", - )); - + let face_texture_coords = match face_location { + FaceLocation::RightUp => [ + Vec2 { x: 1.0, y: 1.0 }, + Vec2 { x: 0.0, y: 1.0 }, + Vec2 { x: 1.0, y: 0.0 }, + ], + FaceLocation::LeftDown => [ + Vec2 { x: 0.0, y: 1.0 }, + Vec2 { x: 0.0, y: 0.0 }, + Vec2 { x: 1.0, y: 0.0 }, + ], + }; + + for (face_vertex, vertex_uv) in face.vertices.iter().zip(face_texture_coords) + { + if let Some(vertex_index) = added_face_vertices.get(face_vertex) { + indices.push(*vertex_index); continue; } - vertices.push(vertex); - - let vertex_index = u32::try_from(vertices.len() - 1) - .expect("Vertex index does not fit into 32-bit unsigned int"); - - indices.push(vertex_index); - } - - match face_location { - FaceLocation::RightUp => face_location = FaceLocation::LeftDown, - FaceLocation::LeftDown => face_location = FaceLocation::RightUp, - } - } - - Mesh::new(vertices, Some(indices)) - } -} - -/// The vertices of a single face of a cube. -#[derive(Debug, Default, Clone)] -pub struct FaceVertices -{ - /// The three vertices of a face in counter-clockwise order. - /// - /// Order when [`FaceLocation::RightUp`]: - /// ```text - /// ₂ ₁ - /// 🮝 - /// ³ - /// ``` - /// - /// Order when [`FaceLocation::LeftDown`]: - /// ```text - /// ₁ - /// 🮟 - /// ² ³ - /// ``` - pub vertices: [Vertex; 3], -} - -impl FaceVertices -{ - fn new(face: Face, vertex_data: &VertexData) -> Self - { - Self { - vertices: face.vertices.map(|face_vertex| { let vertex_pos = vertex_data .vertex_positions .get(face_vertex.pos_index as usize) @@ -204,30 +154,39 @@ impl FaceVertices .expect("Vertex normal index is out of bounds") .clone(); - Vertex::builder() - .pos(vertex_pos) - .normal(vertex_normal) - .build() - }), - } - } + vertex_buf.push(( + NamedVertexAttr { + name: POSITION_VERTEX_ATTRIB_NAME, + value: vertex_pos.into_array(), + }, + NamedVertexAttr { + name: "texture_coords", + value: vertex_uv.into_array(), + }, + NamedVertexAttr { + name: "normal", + value: vertex_normal.into_array(), + }, + )); + + let vertex_index = u32::try_from(vertex_buf.len() - 1) + .expect("Vertex index does not fit into 32-bit unsigned int"); - fn with_full_per_side_tex_coords(mut self, face_location: FaceLocation) -> Self - { - match face_location { - FaceLocation::RightUp => { - self.vertices[0].texture_coords = Vec2 { x: 1.0, y: 1.0 }.into(); - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 1.0 }.into(); - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }.into(); + indices.push(vertex_index); + + added_face_vertices.insert(face_vertex.clone(), vertex_index); } - FaceLocation::LeftDown => { - self.vertices[0].texture_coords = Vec2 { x: 0.0, y: 1.0 }.into(); - self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 0.0 }.into(); - self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 }.into(); + + match face_location { + FaceLocation::RightUp => face_location = FaceLocation::LeftDown, + FaceLocation::LeftDown => face_location = FaceLocation::RightUp, } - }; + } - self + Mesh::builder() + .vertices(vertex_buf) + .indices(indices) + .build() } } @@ -235,7 +194,6 @@ impl FaceVertices struct Face { vertices: [FaceVertex; 3], - side: Side, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] @@ -253,7 +211,6 @@ struct SidePositions down_left: Vec3<f32>, down_right: Vec3<f32>, normal_calc_order: NormalCalcOrder, - side: Side, } impl SidePositions @@ -278,7 +235,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Top, } } @@ -302,7 +258,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Bottom, } } @@ -326,7 +281,6 @@ impl SidePositions down_left: Vec3 { z: up_left.z, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Left, } } @@ -350,7 +304,6 @@ impl SidePositions down_left: Vec3 { z: up_left.z, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Right, } } @@ -374,7 +327,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::Clockwise, - side: Side::Back, } } @@ -398,7 +350,6 @@ impl SidePositions down_left: Vec3 { x: up_left.x, ..down_right.clone() }, down_right, normal_calc_order: NormalCalcOrder::CounterClockwise, - side: Side::Front, } } } @@ -469,7 +420,6 @@ fn create_side(side_positions: &SidePositions, data: &mut Data) normal_index: top_normal_index as u32, }, ], - side: side_positions.side, }); // 🮟 @@ -488,6 +438,5 @@ fn create_side(side_positions: &SidePositions, data: &mut Data) normal_index: top_normal_index as u32, }, ], - side: side_positions.side, }); } diff --git a/engine/src/mesh/vertex_buffer.rs b/engine/src/mesh/vertex_buffer.rs new file mode 100644 index 0000000..3e3c467 --- /dev/null +++ b/engine/src/mesh/vertex_buffer.rs @@ -0,0 +1,247 @@ +use std::alloc::Layout; +use std::borrow::Cow; +use std::marker::PhantomData; + +use seq_macro::seq; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; + +use crate::mesh::VertexAttrType; + +pub trait VertexAttrValue: + IntoBytes + FromBytes + KnownLayout + Immutable + 'static +{ + fn ty() -> VertexAttrType; +} + +impl VertexAttrValue for f32 +{ + fn ty() -> VertexAttrType + { + VertexAttrType::Float32 + } +} + +impl<const LEN: usize> VertexAttrValue for [f32; LEN] +{ + fn ty() -> VertexAttrType + { + VertexAttrType::Float32Array { length: LEN } + } +} + +#[derive(Debug)] +pub struct NamedVertexAttr<'name, Value: VertexAttrValue> +{ + pub name: &'name str, + pub value: Value, +} + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct VertexAttrProperties +{ + pub name: Cow<'static, str>, + pub ty: VertexAttrType, + pub layout: Layout, + pub byte_offset: usize, +} + +#[derive(Debug)] +pub struct VertexAttrInfo +{ + pub name: Cow<'static, str>, + pub ty: VertexAttrType, +} + +#[derive(Debug, Clone, Default)] +pub struct VertexBuffer +{ + buf: Vec<u8>, + vertex_size: usize, + vertex_attr_props: Vec<VertexAttrProperties>, +} + +impl VertexBuffer +{ + pub fn with_capacity(vertex_attrs: &[VertexAttrInfo], capacity: usize) -> Self + { + let mut vertex_attr_props = vertex_attrs + .iter() + .map(|VertexAttrInfo { name, ty }| VertexAttrProperties { + name: name.clone(), + ty: ty.clone(), + layout: ty.layout(), + byte_offset: 0, + }) + .collect::<Vec<_>>(); + + let mut vertex_layout = Layout::new::<()>(); + + for VertexAttrProperties { + name: _, + ty: _, + layout: vertex_attr_layout, + byte_offset: vertex_attr_byte_offset, + } in &mut vertex_attr_props + { + let (new_struct_layout, byte_offset) = + vertex_layout.extend(*vertex_attr_layout).unwrap(); + + *vertex_attr_byte_offset = byte_offset; + vertex_layout = new_struct_layout; + } + + let vertex_layout = vertex_layout.pad_to_align(); + + Self { + buf: Vec::with_capacity(vertex_layout.size() * capacity), + vertex_size: vertex_layout.size(), + vertex_attr_props, + } + } + + pub fn push<'name, VertexAttrs: NamedVertexAttrs<'name>>( + &mut self, + vertex: VertexAttrs, + ) + { + assert_eq!( + self.vertex_attr_props.len(), + vertex.vertex_attr_cnt(), + "Vertex has incorrect amount of attributes" + ); + + if self.buf.spare_capacity_mut().len() < self.vertex_size { + self.buf.reserve_exact(self.vertex_size * (self.len() / 2)); + } + + let spare_capacity = self.buf.spare_capacity_mut(); + + let vertex_attrs = vertex.vertex_attrs(); + + for (vertex_attr_name, vertex_attr_bytes, vertex_attr_ty) in vertex_attrs { + let vertex_attr_props = self + .vertex_attr_props + .iter() + .find(|vertex_attr_props| vertex_attr_props.name == vertex_attr_name) + .unwrap(); + + assert_eq!(vertex_attr_ty, vertex_attr_props.ty); + + let start_offset = vertex_attr_props.byte_offset; + + let end_offset = start_offset + vertex_attr_props.layout.size(); + + spare_capacity[start_offset..end_offset] + .write_copy_of_slice(vertex_attr_bytes); + } + + unsafe { + self.buf.set_len(self.buf.len() + self.vertex_size); + } + } + + pub fn vertex_attr_props(&self) -> &[VertexAttrProperties] + { + &self.vertex_attr_props + } + + pub fn len(&self) -> usize + { + assert_eq!(self.buf.len() % self.vertex_size, 0, "Invalid length"); + + self.buf.len() / self.vertex_size + } + + pub fn vertex_size(&self) -> usize + { + self.vertex_size + } + + pub fn as_bytes(&self) -> &[u8] + { + &self.buf + } + + pub fn iter<VertexAttr: VertexAttrValue>( + &self, + vertex_attr_name: &str, + ) -> Iter<'_, VertexAttr> + { + let vertex_attr_props = self + .vertex_attr_props + .iter() + .find(|vertex_attr_props| vertex_attr_props.name == vertex_attr_name) + .unwrap(); + + assert_eq!(VertexAttr::ty(), vertex_attr_props.ty); + + Iter { + buf: self, + vertex_attr_props, + curr_index: 0, + _pd: PhantomData, + } + } +} + +pub struct Iter<'a, VertexAttr: VertexAttrValue> +{ + buf: &'a VertexBuffer, + vertex_attr_props: &'a VertexAttrProperties, + curr_index: usize, + _pd: PhantomData<VertexAttr>, +} + +impl<'a, VertexAttr: VertexAttrValue> Iterator for Iter<'a, VertexAttr> +{ + type Item = &'a VertexAttr; + + fn next(&mut self) -> Option<Self::Item> + { + let start_offset = + (self.buf.vertex_size * self.curr_index) + self.vertex_attr_props.byte_offset; + + let end_offset = start_offset + self.vertex_attr_props.layout.size(); + + let bytes = self.buf.buf.get(start_offset..end_offset)?; + + self.curr_index += 1; + + Some(VertexAttr::ref_from_bytes(bytes).unwrap()) + } +} + +pub trait NamedVertexAttrs<'name> +{ + fn vertex_attr_cnt(&self) -> usize; + + fn vertex_attrs(&self) -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)>; +} + +macro_rules! impl_named_vertex_attrs { + ($cnt: tt) => { + seq!(I in 0..$cnt { + impl<'name, #(VertexAttr~I: VertexAttrValue,)*> + NamedVertexAttrs<'name> for (#(NamedVertexAttr<'name, VertexAttr~I>,)*) + { + fn vertex_attr_cnt(&self) -> usize + { + $cnt + } + + fn vertex_attrs(&self) + -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)> + { + [#( + (self.I.name, self.I.value.as_bytes(), VertexAttr~I::ty()), + )*].into_iter() + } + } + }); + }; +} + +seq!(I in 0..16 { + impl_named_vertex_attrs!(I); +}); diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index ee440c8..1093292 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -949,10 +949,10 @@ fn handle_commands( .get_program_metadata(curr_shader_program_asset_id) .expect("Not possible"); - let Some(vertex_subset) = &curr_shader_program_metadata.vertex_subset + let Some(vertex_desc) = &curr_shader_program_metadata.vertex_desc else { tracing::error!( - "Current shader program does not have a vertex subset" + "Current shader program does not have a vertex description" ); continue; }; @@ -960,7 +960,7 @@ fn handle_commands( let key = *next_graphics_ctx_object_key; let graphics_mesh = - match GraphicsMesh::new(&curr_gl_ctx, &mesh, &vertex_subset) { + match GraphicsMesh::new(&curr_gl_ctx, &mesh, &vertex_desc) { Ok(graphics_mesh) => graphics_mesh, Err(err) => { tracing::error!("Failed to create mesh: {err}"); diff --git a/engine/src/renderer/opengl/graphics_mesh.rs b/engine/src/renderer/opengl/graphics_mesh.rs index cdd0e8d..7c7412c 100644 --- a/engine/src/renderer/opengl/graphics_mesh.rs +++ b/engine/src/renderer/opengl/graphics_mesh.rs @@ -1,5 +1,3 @@ -use std::any::TypeId; - use opengl_bindings::CurrentContextWithFns as GlCurrentContextWithFns; use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage}; use opengl_bindings::vertex_array::{ @@ -9,11 +7,9 @@ use opengl_bindings::vertex_array::{ VertexArray as GlVertexArray, VertexBufferSpec as GlVertexArrayVertexBufferSpec, }; -use zerocopy::IntoBytes; -use crate::mesh::Mesh; -use crate::reflection::Type; -use crate::shader::VertexSubset as ShaderVertexSubset; +use crate::mesh::{Mesh, VertexAttrType}; +use crate::shader::VertexDescription as ShaderVertexDescription; #[derive(Debug)] pub struct GraphicsMesh @@ -31,42 +27,19 @@ impl GraphicsMesh pub fn new( current_context: &GlCurrentContextWithFns<'_>, mesh: &Mesh, - vertex_subset: &ShaderVertexSubset, + vertex_desc: &ShaderVertexDescription, ) -> Result<Self, Error> { let vertex_arr = GlVertexArray::new(current_context); let vertex_buffer = GlBuffer::new(current_context); vertex_buffer - .init( + .store( current_context, - vertex_subset.layout.size() * mesh.vertices().len(), + mesh.vertex_buf().as_bytes(), GlBufferUsage::Static, ) - .map_err(Error::InitVertexBufferFailed)?; - - for (vertex_index, vertex) in mesh.vertices().iter().enumerate() { - let vertex_bytes = vertex.as_bytes(); - - for vertex_subset_field in vertex_subset.fields.iter().flatten() { - let vertex_buffer_offset = (vertex_subset.layout.size() * vertex_index) - + vertex_subset_field.offset; - - let vertex_field_start_offset = - vertex_subset_field.reflection.byte_offset; - - let vertex_field_size = vertex_subset_field.reflection.layout.size(); - - vertex_buffer - .store_at_byte_offset( - current_context, - vertex_buffer_offset, - &vertex_bytes[vertex_field_start_offset - ..vertex_field_start_offset + vertex_field_size], - ) - .map_err(Error::StoreVerticesFailed)?; - } - } + .map_err(Error::StoreVerticesFailed)?; let vertex_buf_binding_index = 0; @@ -76,7 +49,7 @@ impl GraphicsMesh &vertex_buffer, GlVertexArrayVertexBufferSpec { offset: 0, - vertex_size: vertex_subset.layout.size(), + vertex_size: mesh.vertex_buf().vertex_size(), }, ) { match err { @@ -96,47 +69,38 @@ impl GraphicsMesh } } - for vertex_subset_field in vertex_subset.fields.iter().flatten() { + for vertex_attr_props in mesh.vertex_buf().vertex_attr_props() { + let vertex_field_desc = vertex_desc + .fields + .iter() + .find(|vertex_field_desc| { + *vertex_field_desc.name == vertex_attr_props.name + }) + .unwrap(); + let attrib_index: u32 = - vertex_subset_field.varying_input_offset.try_into().unwrap(); + vertex_field_desc.varying_input_offset.try_into().unwrap(); vertex_arr.enable_attrib(current_context, attrib_index); vertex_arr.set_attrib_format( current_context, attrib_index, - match vertex_subset_field.reflection.type_reflection() { - Some(Type::Literal(_)) => { - if vertex_subset_field.reflection.type_id != TypeId::of::<f32>() { - panic!("Unsupported vertex field data type"); - } - + 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: 1, + count: (*length).try_into().unwrap(), normalized: false, - offset: vertex_subset_field.offset.try_into().unwrap(), + offset: vertex_attr_props.byte_offset.try_into().unwrap(), } } - Some(Type::Array(array_vertex_field)) => { - let Type::Literal(array_vertex_field_item) = - array_vertex_field.item_reflection - else { - panic!("Unsupported array item type in vertex field"); - }; - - if array_vertex_field_item.type_id != TypeId::of::<f32>() { - panic!("Unsupported array item type in vertex field"); - } - - GlVertexArrayAttributeFormat { - data_type: GlVertexArrayDataType::Float, - count: array_vertex_field.length.try_into().unwrap(), - normalized: false, - offset: vertex_subset_field.offset.try_into().unwrap(), - } - } - _ => panic!("Unsupported vertex field data type"), }, ); @@ -171,7 +135,7 @@ impl GraphicsMesh vertex_buffer: vertex_buffer, index_buffer: None, element_cnt: mesh - .vertices() + .vertex_buf() .len() .try_into() .expect("Mesh vertex count does not fit into a 32-bit unsigned int"), @@ -193,9 +157,6 @@ impl GraphicsMesh #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Failed to initialize vertex buffer")] - InitVertexBufferFailed(#[source] opengl_bindings::buffer::Error), - #[error("Failed to store vertices in vertex buffer")] StoreVerticesFailed(#[source] opengl_bindings::buffer::Error), diff --git a/engine/src/shader.rs b/engine/src/shader.rs index f23a366..c4bd709 100644 --- a/engine/src/shader.rs +++ b/engine/src/shader.rs @@ -1,4 +1,3 @@ -use std::alloc::Layout; use std::any::type_name; use std::borrow::Cow; use std::collections::HashMap; @@ -19,6 +18,7 @@ use shader_slang::{ GlobalSession as SlangGlobalSession, Module as SlangModule, ParameterCategory as SlangParameterCategory, + ScalarType as SlangScalarType, Session as SlangSession, TypeKind as SlangTypeKind, }; @@ -32,12 +32,6 @@ use crate::asset::{ Submitter as AssetSubmitter, }; use crate::builder; -use crate::mesh::Vertex; -use crate::reflection::{ - Reflection, - Struct as StructReflection, - StructField as StructFieldReflection, -}; use crate::shader::default::{ ASSET_LABEL, enqueue_set_shader_bindings as default_shader_enqueue_set_shader_bindings, @@ -341,6 +335,13 @@ impl<'a> TypeLayout<'a> TypeKind::from_slang_type_kind(self.inner.kind()) } + pub fn scalar_type(&self) -> Option<ScalarType> + { + Some(ScalarType::from_slang_scalar_type( + self.inner.scalar_type()?, + )) + } + pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>> { let index = self.inner.find_field_index_by_name(name); @@ -528,6 +529,53 @@ impl TypeKind } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[non_exhaustive] +pub enum ScalarType +{ + None, + Void, + Bool, + Int32, + Uint32, + Int64, + Uint64, + Float16, + Float32, + Float64, + Int8, + Uint8, + Int16, + Uint16, + Intptr, + Uintptr, +} + +impl ScalarType +{ + fn from_slang_scalar_type(scalar_type: SlangScalarType) -> Self + { + match scalar_type { + SlangScalarType::None => Self::None, + SlangScalarType::Void => Self::Void, + SlangScalarType::Bool => Self::Bool, + SlangScalarType::Int32 => Self::Int32, + SlangScalarType::Uint32 => Self::Uint32, + SlangScalarType::Int64 => Self::Int64, + SlangScalarType::Uint64 => Self::Uint64, + SlangScalarType::Float16 => Self::Float16, + SlangScalarType::Float32 => Self::Float32, + SlangScalarType::Float64 => Self::Float64, + SlangScalarType::Int8 => Self::Int8, + SlangScalarType::Uint8 => Self::Uint8, + SlangScalarType::Int16 => Self::Int16, + SlangScalarType::Uint16 => Self::Uint16, + SlangScalarType::Intptr => Self::Intptr, + SlangScalarType::Uintptr => Self::Uintptr, + } + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum ParameterCategory { @@ -728,37 +776,36 @@ impl Context } } +#[derive(Debug)] +#[non_exhaustive] pub struct ProgramMetadata { - pub vertex_subset: Option<VertexSubset>, + /// If the program has a entry point in the vertex stage, this field will contain a + /// description of the vertex type passed to the entry point. + pub vertex_desc: Option<VertexDescription>, } #[derive(Debug)] -pub struct VertexSubset +#[non_exhaustive] +pub struct VertexDescription { - pub layout: Layout, - pub fields: [Option<VertexSubsetField>; const { - Vertex::TYPE_REFLECTION.as_struct().unwrap().fields.len() - }], + pub fields: Box<[VertexFieldDescription]>, } -impl VertexSubset +impl VertexDescription { pub fn new( vs_entrypoint: &EntryPointReflection<'_>, - ) -> Result<Self, VertexSubsetError> + ) -> Result<Self, VertexDescriptionError> { - const VERTEX_REFLECTION: &StructReflection = - const { Vertex::TYPE_REFLECTION.as_struct().unwrap() }; - if vs_entrypoint.stage() != Stage::Vertex { - return Err(VertexSubsetError::EntrypointNotInVertexStage); + return Err(VertexDescriptionError::EntrypointNotInVertexStage); } let vs_entrypoint_vertex_param = vs_entrypoint .parameters() .find(|param| param.semantic_name() == Some(VERTEX_PARAM_SEMANTIC_NAME)) - .ok_or(VertexSubsetError::EntrypointMissingVertexParam)?; + .ok_or(VertexDescriptionError::EntrypointMissingVertexParam)?; let vs_entrypoint_vertex_param = vs_entrypoint_vertex_param .type_layout() @@ -767,76 +814,65 @@ impl VertexSubset if vs_entrypoint_vertex_param.parameter_category() != ParameterCategory::VaryingInput { - return Err(VertexSubsetError::EntryPointVertexParamNotVaryingInput); + return Err(VertexDescriptionError::EntryPointVertexParamNotVaryingInput); } if vs_entrypoint_vertex_param.kind() != TypeKind::Struct { - return Err(VertexSubsetError::EntrypointVertexTypeNotStruct); + return Err(VertexDescriptionError::EntrypointVertexTypeNotStruct); } - if let Some(unknown_vertex_field_name) = vs_entrypoint_vertex_param + let fields = vs_entrypoint_vertex_param .fields() - .find_map(|vertex_param_field| { - let vertex_param_field_name = - vertex_param_field.name().expect("Not possible"); - - if VERTEX_REFLECTION - .fields - .iter() - .all(|vertex_field| vertex_field.name != vertex_param_field_name) - { - return Some(vertex_param_field_name); - } + .map(|field| { + let varying_input_offset = + field.varying_input_offset().expect("Not possible"); + + let field_ty = field.type_layout().expect("Maybe not possible"); + + let scalar_type = match field_ty.kind() { + TypeKind::Scalar => field_ty.scalar_type().expect("Not possible"), + TypeKind::Vector => { + let Some(scalar_type) = field_ty.scalar_type() else { + return Err( + VertexDescriptionError::UnsupportedVertexFieldType { + field_name: field.name().unwrap_or("").to_string(), + }, + ); + }; + + scalar_type + } + _ => { + return Err(VertexDescriptionError::UnsupportedVertexFieldType { + field_name: field.name().unwrap_or("").to_string(), + }); + } + }; - None + Ok(VertexFieldDescription { + name: field.name().unwrap_or("").to_string().into_boxed_str(), + varying_input_offset, + type_kind: field_ty.kind(), + scalar_type, + }) }) - { - return Err(VertexSubsetError::EntrypointVertexTypeHasUnknownField { - field_name: unknown_vertex_field_name.to_string(), - }); - } - - let mut layout = Layout::new::<()>(); - - let mut fields = [const { None }; const { VERTEX_REFLECTION.fields.len() }]; - - for vertex_field in const { VERTEX_REFLECTION.fields } { - let Some(vertex_field_var_layout) = - vs_entrypoint_vertex_param.get_field_by_name(vertex_field.name) - else { - continue; - }; - - let (new_layout, vertex_field_offset) = - layout.extend(vertex_field.layout).expect("Not possible"); - - layout = new_layout; - - fields[vertex_field.index] = Some(VertexSubsetField { - offset: vertex_field_offset, - reflection: vertex_field, - varying_input_offset: vertex_field_var_layout - .varying_input_offset() - .expect("Not possible"), - }); - } - - layout = layout.pad_to_align(); + .collect::<Result<Vec<_>, _>>()?; - Ok(Self { layout, fields }) + Ok(Self { fields: fields.into_boxed_slice() }) } } #[derive(Debug)] -pub struct VertexSubsetField +pub struct VertexFieldDescription { - pub offset: usize, - pub reflection: &'static StructFieldReflection, + pub name: Box<str>, pub varying_input_offset: usize, + pub type_kind: TypeKind, + pub scalar_type: ScalarType, } #[derive(Debug, thiserror::Error)] -pub enum VertexSubsetError +pub enum VertexDescriptionError { #[error("Entrypoint is not in vertex stage")] EntrypointNotInVertexStage, @@ -853,8 +889,8 @@ pub enum VertexSubsetError #[error("Entrypoint vertex type is not a struct")] EntrypointVertexTypeNotStruct, - #[error("Entrypoint vertex type has unknown field {field_name}")] - EntrypointVertexTypeHasUnknownField + #[error("Type of field '{field_name}' of vertex type is not supported")] + UnsupportedVertexFieldType { field_name: String }, @@ -1069,11 +1105,11 @@ fn load_modules(mut context: Single<Context>, assets: Single<Assets>) } }; - let vertex_subset = if module_source + let vertex_desc = if module_source .link_entrypoints .contains(EntrypointFlags::VERTEX) { - VertexSubset::new( + VertexDescription::new( &shader_program .reflection(0) .expect("Not possible") @@ -1082,7 +1118,9 @@ fn load_modules(mut context: Single<Context>, assets: Single<Assets>) ) .inspect_err(|err| { tracing::error!( - "Failed to create vertex subset for shader {asset_label:?}: {err}" + "Failed to create a vertex description for shader {}: {}", + asset_label, + err ); }) .ok() @@ -1092,7 +1130,7 @@ fn load_modules(mut context: Single<Context>, assets: Single<Assets>) context.programs.insert( *asset_id, - (linked_shader_program, ProgramMetadata { vertex_subset }), + (linked_shader_program, ProgramMetadata { vertex_desc }), ); } } |
