summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/src/file_format/wavefront/obj.rs171
-rw-r--r--engine/src/mesh.rs117
-rw-r--r--engine/src/mesh/cube.rs209
-rw-r--r--engine/src/mesh/vertex_buffer.rs247
-rw-r--r--engine/src/renderer/opengl.rs6
-rw-r--r--engine/src/renderer/opengl/graphics_mesh.rs95
-rw-r--r--engine/src/shader.rs194
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 }),
);
}
}