diff options
| author | HampusM <hampus@hampusmat.com> | 2026-08-13 14:16:39 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-08-13 14:16:39 +0200 |
| commit | c611ee62477564fbe532f51e8526bae2e82dae93 (patch) | |
| tree | 376cd420163ebb0c55a84fa4c458e6922198b620 /engine | |
| parent | 47ac4d8ae714d562d969e8379e82e568a72d50fb (diff) | |
feat(engine): add standardized names for mesh vertex attrs & vertex shader input
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/res/default_shader.slang | 6 | ||||
| -rw-r--r-- | engine/res/imgui_shader.slang | 6 | ||||
| -rw-r--r-- | engine/src/file_format/wavefront/obj.rs | 15 | ||||
| -rw-r--r-- | engine/src/mesh.rs | 5 | ||||
| -rw-r--r-- | engine/src/mesh/cube.rs | 15 | ||||
| -rw-r--r-- | engine/src/mesh/vertex_buffer.rs | 51 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl/graphics_mesh.rs | 29 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 120 | ||||
| -rw-r--r-- | engine/src/ui/dear_imgui.rs | 19 | ||||
| -rw-r--r-- | engine/src/util.rs | 45 |
10 files changed, 234 insertions, 77 deletions
diff --git a/engine/res/default_shader.slang b/engine/res/default_shader.slang index 62b01f7..7da164c 100644 --- a/engine/res/default_shader.slang +++ b/engine/res/default_shader.slang @@ -224,9 +224,9 @@ struct VertexStageOutput struct Vertex { - float3 pos; - float2 texture_coords; - float3 normal; + float3 pos : STD_POSITION; + float2 texture_coords : STD_UV; + float3 normal : STD_NORMAL; }; struct Fragment diff --git a/engine/res/imgui_shader.slang b/engine/res/imgui_shader.slang index 2bdd718..29ad9bf 100644 --- a/engine/res/imgui_shader.slang +++ b/engine/res/imgui_shader.slang @@ -1,8 +1,8 @@ struct Vertex { - float3 pos; - float4 color; - float2 texture_coords; + float3 pos : STD_POSITION; + float4 color : STD_COLOR; + float2 texture_coords : STD_UV; }; struct VertexData diff --git a/engine/src/file_format/wavefront/obj.rs b/engine/src/file_format/wavefront/obj.rs index ebbbe45..3fcb17b 100644 --- a/engine/src/file_format/wavefront/obj.rs +++ b/engine/src/file_format/wavefront/obj.rs @@ -17,8 +17,9 @@ use crate::mesh::vertex_buffer::{ NamedVertexAttr, VertexAttrInfo, VertexBuffer as MeshVertexBuffer, + VertexLabel, }; -use crate::mesh::{Mesh, VertexAttrType, POSITION_VERTEX_ATTRIB_NAME}; +use crate::mesh::{Mesh, VertexAttrType}; use crate::util::try_option; use crate::vector::{Vec2, Vec3}; @@ -90,15 +91,15 @@ impl Obj let mut vertex_buf = MeshVertexBuffer::with_capacity( &[ VertexAttrInfo { - name: POSITION_VERTEX_ATTRIB_NAME.into(), + label: VertexLabel::Position, ty: VertexAttrType::Float32Array { length: 3 }, }, VertexAttrInfo { - name: "texture_coords".into(), + label: VertexLabel::Uv, ty: VertexAttrType::Float32Array { length: 2 }, }, VertexAttrInfo { - name: "normal".into(), + label: VertexLabel::Normal, ty: VertexAttrType::Float32Array { length: 3 }, }, ], @@ -170,15 +171,15 @@ impl Obj vertex_buf.push(( NamedVertexAttr { - name: POSITION_VERTEX_ATTRIB_NAME, + label: VertexLabel::Position, value: pos.into_array(), }, NamedVertexAttr { - name: "texture_coords", + label: VertexLabel::Uv, value: texture_pos.into_array(), }, NamedVertexAttr { - name: "normal", + label: VertexLabel::Normal, value: normal.into_array(), }, )); diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs index 9aa1f80..82e2877 100644 --- a/engine/src/mesh.rs +++ b/engine/src/mesh.rs @@ -1,12 +1,11 @@ use std::alloc::Layout; +use crate::mesh::vertex_buffer::VertexLabel; use crate::vector::Vec3; pub mod cube; pub mod vertex_buffer; -pub const POSITION_VERTEX_ATTRIB_NAME: &str = "pos"; - #[derive(Debug, Clone)] pub struct Mesh { @@ -58,7 +57,7 @@ impl Mesh { let mut pos_iter = self .vertex_buf() - .iter::<[f32; 3]>(POSITION_VERTEX_ATTRIB_NAME) + .iter::<[f32; 3]>(VertexLabel::Position) .map(|vertex_pos| Vec3::from(*vertex_pos)) .into_iter(); diff --git a/engine/src/mesh/cube.rs b/engine/src/mesh/cube.rs index 61d1e26..a89466a 100644 --- a/engine/src/mesh/cube.rs +++ b/engine/src/mesh/cube.rs @@ -7,8 +7,9 @@ use crate::mesh::vertex_buffer::{ NamedVertexAttr, VertexAttrInfo, VertexBuffer as MeshVertexBuffer, + VertexLabel, }; -use crate::mesh::{Mesh, VertexAttrType, POSITION_VERTEX_ATTRIB_NAME}; +use crate::mesh::{Mesh, VertexAttrType}; use crate::vector::{Vec2, Vec3}; builder! { @@ -98,15 +99,15 @@ impl Data let mut vertex_buf = MeshVertexBuffer::with_capacity( &[ VertexAttrInfo { - name: POSITION_VERTEX_ATTRIB_NAME.into(), + label: VertexLabel::Position, ty: VertexAttrType::Float32Array { length: 3 }, }, VertexAttrInfo { - name: "texture_coords".into(), + label: VertexLabel::Uv, ty: VertexAttrType::Float32Array { length: 2 }, }, VertexAttrInfo { - name: "normal".into(), + label: VertexLabel::Normal, ty: VertexAttrType::Float32Array { length: 3 }, }, ], @@ -156,15 +157,15 @@ impl Data vertex_buf.push(( NamedVertexAttr { - name: POSITION_VERTEX_ATTRIB_NAME, + label: VertexLabel::Position, value: vertex_pos.into_array(), }, NamedVertexAttr { - name: "texture_coords", + label: VertexLabel::Uv, value: vertex_uv.into_array(), }, NamedVertexAttr { - name: "normal", + label: VertexLabel::Normal, value: vertex_normal.into_array(), }, )); diff --git a/engine/src/mesh/vertex_buffer.rs b/engine/src/mesh/vertex_buffer.rs index 9d77270..4df3ffc 100644 --- a/engine/src/mesh/vertex_buffer.rs +++ b/engine/src/mesh/vertex_buffer.rs @@ -30,9 +30,9 @@ impl<const LEN: usize> VertexAttrValue for [f32; LEN] } #[derive(Debug)] -pub struct NamedVertexAttr<'name, Value: VertexAttrValue> +pub struct NamedVertexAttr<Value: VertexAttrValue> { - pub name: &'name str, + pub label: VertexLabel, pub value: Value, } @@ -40,7 +40,7 @@ pub struct NamedVertexAttr<'name, Value: VertexAttrValue> #[non_exhaustive] pub struct VertexAttrProperties { - pub name: Cow<'static, str>, + pub label: VertexLabel, pub ty: VertexAttrType, pub layout: Layout, pub byte_offset: usize, @@ -49,10 +49,20 @@ pub struct VertexAttrProperties #[derive(Debug)] pub struct VertexAttrInfo { - pub name: Cow<'static, str>, + pub label: VertexLabel, pub ty: VertexAttrType, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VertexLabel +{ + Position, + Normal, + Uv, + Color, + Other(Cow<'static, str>), +} + #[derive(Debug, Clone, Default)] pub struct VertexBuffer { @@ -67,8 +77,8 @@ impl VertexBuffer { let mut vertex_attr_props = vertex_attrs .iter() - .map(|VertexAttrInfo { name, ty }| VertexAttrProperties { - name: name.clone(), + .map(|VertexAttrInfo { label, ty }| VertexAttrProperties { + label: label.clone(), ty: ty.clone(), layout: ty.layout(), byte_offset: 0, @@ -78,10 +88,9 @@ impl VertexBuffer 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) = @@ -100,10 +109,7 @@ impl VertexBuffer } } - pub fn push<'name, VertexAttrs: NamedVertexAttrs<'name>>( - &mut self, - vertex: VertexAttrs, - ) + pub fn push<VertexAttrs: NamedVertexAttrs>(&mut self, vertex: VertexAttrs) { assert_eq!( self.vertex_attr_props.len(), @@ -119,11 +125,11 @@ impl VertexBuffer let vertex_attrs = vertex.vertex_attrs(); - for (vertex_attr_name, vertex_attr_bytes, vertex_attr_ty) in vertex_attrs { + for (vertex_label, 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) + .find(|vertex_attr_props| &vertex_attr_props.label == vertex_label) .unwrap(); assert_eq!(vertex_attr_ty, vertex_attr_props.ty); @@ -170,13 +176,13 @@ impl VertexBuffer pub fn iter<VertexAttr: VertexAttrValue>( &self, - vertex_attr_name: &str, + vertex_label: VertexLabel, ) -> Iter<'_, VertexAttr> { let vertex_attr_props = self .vertex_attr_props .iter() - .find(|vertex_attr_props| vertex_attr_props.name == vertex_attr_name) + .find(|vertex_attr_props| vertex_attr_props.label == vertex_label) .unwrap(); assert_eq!(VertexAttr::ty(), vertex_attr_props.ty); @@ -217,18 +223,19 @@ impl<'a, VertexAttr: VertexAttrValue> Iterator for Iter<'a, VertexAttr> } } -pub trait NamedVertexAttrs<'name> +pub trait NamedVertexAttrs { fn vertex_attr_cnt(&self) -> usize; - fn vertex_attrs(&self) -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)>; + fn vertex_attrs(&self) + -> impl Iterator<Item = (&VertexLabel, &[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>,)*) + impl<#(VertexAttr~I: VertexAttrValue,)*> + NamedVertexAttrs for (#(NamedVertexAttr<VertexAttr~I>,)*) { fn vertex_attr_cnt(&self) -> usize { @@ -236,10 +243,10 @@ macro_rules! impl_named_vertex_attrs { } fn vertex_attrs(&self) - -> impl Iterator<Item = (&'name str, &[u8], VertexAttrType)> + -> impl Iterator<Item = (&VertexLabel, &[u8], VertexAttrType)> { [#( - (self.I.name, self.I.value.as_bytes(), VertexAttr~I::ty()), + (&self.I.label, self.I.value.as_bytes(), VertexAttr~I::ty()), )*].into_iter() } } diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs index fba60c8..f6b7d56 100644 --- a/engine/src/rendering/backend/opengl/graphics_mesh.rs +++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs @@ -12,8 +12,12 @@ 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; +use crate::rendering::shader::{ + VertexDescription as ShaderVertexDescription, + VertexInputSemName, +}; use crate::rendering::MeshUsage; +use crate::util::DisplaySlice; const VERTEX_BUF_BINDING_INDEX: u32 = 0; @@ -171,7 +175,9 @@ impl GraphicsMesh for vertex_attr_props in &self.vertex_attr_props { let Some(vertex_input_desc) = shader_vertex_desc.inputs.iter().find(|vertex_input_desc| { - *vertex_input_desc.name == vertex_attr_props.name + vertex_input_desc + .semantic_name + .matches_vertex_label(&vertex_attr_props.label) }) else { continue; @@ -221,15 +227,15 @@ impl GraphicsMesh .inputs .iter() .filter_map(|vertex_input_desc| { - if self - .vertex_attr_props - .iter() - .any(|prop| prop.name == *vertex_input_desc.name) - { + if self.vertex_attr_props.iter().any(|props| { + vertex_input_desc + .semantic_name + .matches_vertex_label(&props.label) + }) { return None; } - Some(vertex_input_desc.name.clone()) + Some(vertex_input_desc.semantic_name.clone()) }) .collect(), )); @@ -262,8 +268,11 @@ pub enum Error #[derive(Debug, thiserror::Error)] pub enum VertexAttrsUpdatingError { - #[error("Mesh is missing vertex attribute(s) required by shader: {0:?}")] - MissingVertexAttrs(Vec<Box<str>>), + #[error( + "Mesh is missing equivalent vertex attribute(s) for shader's vertex inputs: {}", + DisplaySlice::new(.0) + )] + MissingVertexAttrs(Vec<VertexInputSemName>), } fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 3142400..6f50a79 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -1,7 +1,7 @@ use std::any::type_name; use std::borrow::Cow; -use std::collections::HashMap; -use std::fmt::Debug; +use std::collections::{HashMap, HashSet}; +use std::fmt::{Debug, Display, Write}; use std::path::Path; use std::str::Utf8Error; use std::sync::Arc; @@ -34,10 +34,16 @@ use crate::ecs::pair::ChildOf; use crate::ecs::phase::{Phase, POST_UPDATE as POST_UPDATE_PHASE}; use crate::ecs::sole::Single; use crate::ecs::{declare_entity, pair, Component, Sole}; +use crate::mesh::vertex_buffer::VertexLabel; pub mod cursor; pub mod default; +pub const STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION: &str = "STD_POSITION"; +pub const STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL: &str = "STD_NORMAL"; +pub const STD_VERTEX_INPUT_SEMANTIC_NAME_UV: &str = "STD_UV"; +pub const STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR: &str = "STD_COLOR"; + #[derive(Debug, Clone, Component)] pub struct Shader { @@ -983,15 +989,31 @@ impl VertexDescription ); }); + let mut seen_inputs = HashSet::<VertexInputSemName>::new(); + Ok(Self { inputs: inputs .filter(|var_input| var_input.type_layout.kind() != TypeKind::Struct) .map(|var_input| { - let name = var_input - .var_layout - .name() - .unwrap_or("<unnamed>") - .to_string(); + let name = var_input.var_layout.name().unwrap_or("<unnamed>"); + + let semantic_name = + var_input.var_layout.semantic_name().ok_or_else(|| { + VertexDescriptionError::VertexInputMissingSemanticName { + name: name.to_owned(), + } + })?; + + let semantic_name = + VertexInputSemName::from_semantic_name(semantic_name); + + if seen_inputs.contains(&semantic_name) { + return Err( + VertexDescriptionError::VertexInputHasOccupiedSemanticName { + name: name.to_owned(), + }, + ); + } let scalar_type = match ( var_input.type_layout.kind(), @@ -1002,14 +1024,16 @@ impl VertexDescription _ => { return Err( VertexDescriptionError::UnsupportedVertexInputType { - name, + name: name.to_owned(), }, ); } }; + seen_inputs.insert(semantic_name.clone()); + Ok(VertexInputDescription { - name: name.into_boxed_str(), + semantic_name, index: var_input.index, type_kind: var_input.type_layout.kind(), scalar_type, @@ -1022,14 +1046,76 @@ impl VertexDescription } #[derive(Debug)] +#[non_exhaustive] pub struct VertexInputDescription { - pub name: Box<str>, + pub semantic_name: VertexInputSemName, pub index: usize, pub type_kind: TypeKind, pub scalar_type: ScalarType, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum VertexInputSemName +{ + Position, + Normal, + Uv, + Color, + Other(Box<str>), +} + +impl VertexInputSemName +{ + fn from_semantic_name(semantic_name: &str) -> Self + { + match semantic_name { + STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION => Self::Position, + STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL => Self::Normal, + STD_VERTEX_INPUT_SEMANTIC_NAME_UV => Self::Uv, + STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR => Self::Color, + _ => Self::Other(semantic_name.to_lowercase().into_boxed_str()), + } + } + + pub fn matches_vertex_label(&self, vertex_label: &VertexLabel) -> bool + { + match (self, vertex_label) { + (Self::Position, VertexLabel::Position) + | (Self::Normal, VertexLabel::Normal) + | (Self::Uv, VertexLabel::Uv) + | (Self::Color, VertexLabel::Color) => true, + (Self::Other(other), VertexLabel::Other(other_vertex_label)) => { + **other == *other_vertex_label + } + _ => false, + } + } +} + +impl Display for VertexInputSemName +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self { + Self::Position => { + formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION) + } + Self::Normal => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL), + Self::Uv => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_UV), + Self::Color => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR), + Self::Other(other) => { + for character in other.chars() { + formatter.write_char(character.to_ascii_uppercase())?; + } + + Ok(()) + } + } + } +} + #[derive(Debug, thiserror::Error)] pub enum VertexDescriptionError { @@ -1041,6 +1127,20 @@ pub enum VertexDescriptionError { name: String }, + + #[error("Vertex input '{name}' is missing a semantic name")] + VertexInputMissingSemanticName + { + name: String + }, + + #[error( + "Vertex input '{name}' has a semantic name already used by another vertex input" + )] + VertexInputHasOccupiedSemanticName + { + name: String + }, } #[derive(Debug, thiserror::Error)] diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs index bcdefa1..8f58ff6 100644 --- a/engine/src/ui/dear_imgui.rs +++ b/engine/src/ui/dear_imgui.rs @@ -36,12 +36,9 @@ use crate::mesh::vertex_buffer::{ NamedVertexAttr as MeshNamedVertexAttr, VertexAttrInfo as MeshVertexAttrInfo, VertexBuffer as MeshVertexBuffer, + VertexLabel, }; -use crate::mesh::{ - Mesh, - VertexAttrType as MeshVertexAttrType, - POSITION_VERTEX_ATTRIB_NAME, -}; +use crate::mesh::{Mesh, VertexAttrType as MeshVertexAttrType}; use crate::projection::{ ClipVolume as ProjectionClipVolume, Orthographic as OrthographicProjection, @@ -393,15 +390,15 @@ fn initialize_context( .vertices(MeshVertexBuffer::with_capacity( &[ MeshVertexAttrInfo { - name: POSITION_VERTEX_ATTRIB_NAME.into(), + label: VertexLabel::Position, ty: MeshVertexAttrType::Float32Array { length: 3 }, }, MeshVertexAttrInfo { - name: "color".into(), + label: VertexLabel::Color, ty: MeshVertexAttrType::Float32Array { length: 4 }, }, MeshVertexAttrInfo { - name: "texture_coords".into(), + label: VertexLabel::Uv, ty: MeshVertexAttrType::Float32Array { length: 2 }, }, ], @@ -726,15 +723,15 @@ fn add_drawing_render_pass( for vertex in draw_list.vtx_buffer() { mesh.vertex_buf_mut().push(( MeshNamedVertexAttr::<[f32; 3]> { - name: POSITION_VERTEX_ATTRIB_NAME, + label: VertexLabel::Position, value: [vertex.pos[0], vertex.pos[1], 0.0], }, MeshNamedVertexAttr { - name: "color", + label: VertexLabel::Color, value: vertex.rgba().map(|elem| (elem as f32) / 255.0), }, MeshNamedVertexAttr { - name: "texture_coords", + label: VertexLabel::Uv, value: vertex.uv, }, )); diff --git a/engine/src/util.rs b/engine/src/util.rs index bd69734..688778b 100644 --- a/engine/src/util.rs +++ b/engine/src/util.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use crate::ecs::util::VecExt; @@ -161,6 +161,49 @@ impl<T> OptionExt<T> for Option<T> } } +pub struct DisplaySlice<'a, Item> +{ + slice: &'a [Item], +} + +impl<'a, Item> DisplaySlice<'a, Item> +{ + pub fn new(slice: &'a [Item]) -> Self + { + Self { slice } + } +} + +impl<Item> Display for DisplaySlice<'_, Item> +where + Item: Display, +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + let Some(first_item) = self.slice.first() else { + return formatter.write_str("<empty>"); + }; + + write!(formatter, "{first_item}")?; + + if self.slice.len() == 1 { + return Ok(()); + } + + for item in &self.slice[1..self.slice.len() - 1] { + write!(formatter, ", {item}")?; + } + + let Some(last_item) = self.slice.last() else { + unreachable!(); + }; + + write!(formatter, " & {last_item}")?; + + Ok(()) + } +} + macro_rules! try_option { ($expr: expr) => { match $expr { |
