#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeSemantic {
Position,
Normal,
TexCoord0,
TexCoord1,
Tangent,
Binormal,
BoneIndices,
BoneWeights,
Extra,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeFormat {
Float32x3,
PackedNormal,
PackedUV,
Raw4,
}
#[derive(Debug, Clone)]
pub struct VertexAttribute {
pub semantic: AttributeSemantic,
pub format: AttributeFormat,
pub offset: usize,
}
#[derive(Debug, Clone)]
pub struct VertexFormat {
pub attributes: Vec<VertexAttribute>,
pub stride: usize,
}
pub fn parse_vertex_format(format_name: &str) -> VertexFormat {
let code = format_name.rsplit('/').next().unwrap_or(format_name);
let mut attrs = Vec::new();
let mut offset = 0usize;
let mut uv_count = 0u32;
let mut chars = code.chars().peekable();
while let Some(&ch) = chars.peek() {
match ch {
'x' => {
chars.next(); if chars.peek() == Some(&'y') {
chars.next();
} if chars.peek() == Some(&'z') {
chars.next();
} attrs.push(VertexAttribute {
semantic: AttributeSemantic::Position,
format: AttributeFormat::Float32x3,
offset,
});
offset += 12;
}
'n' => {
chars.next();
attrs.push(VertexAttribute {
semantic: AttributeSemantic::Normal,
format: AttributeFormat::PackedNormal,
offset,
});
offset += 4;
}
'u' => {
chars.next(); if chars.peek() == Some(&'v') {
chars.next();
} if chars.peek() == Some(&'2') {
chars.next();
attrs.push(VertexAttribute {
semantic: AttributeSemantic::TexCoord0,
format: AttributeFormat::PackedUV,
offset,
});
offset += 4;
attrs.push(VertexAttribute {
semantic: AttributeSemantic::TexCoord1,
format: AttributeFormat::PackedUV,
offset,
});
offset += 4;
uv_count = 2;
} else {
let semantic =
if uv_count == 0 { AttributeSemantic::TexCoord0 } else { AttributeSemantic::TexCoord1 };
attrs.push(VertexAttribute { semantic, format: AttributeFormat::PackedUV, offset });
offset += 4;
uv_count += 1;
}
}
't' => {
chars.next(); if chars.peek() == Some(&'b') {
chars.next(); attrs.push(VertexAttribute {
semantic: AttributeSemantic::Tangent,
format: AttributeFormat::PackedNormal,
offset,
});
offset += 4;
attrs.push(VertexAttribute {
semantic: AttributeSemantic::Binormal,
format: AttributeFormat::PackedNormal,
offset,
});
offset += 4;
} else {
chars.next();
}
}
'i' => {
chars.next();
if chars.peek() == Some(&'i') {
chars.next(); if chars.peek() == Some(&'i') {
chars.next(); }
if chars.peek() == Some(&'w') {
chars.next(); if chars.peek() == Some(&'w') {
chars.next(); }
}
attrs.push(VertexAttribute {
semantic: AttributeSemantic::BoneIndices,
format: AttributeFormat::Raw4,
offset,
});
offset += 4;
attrs.push(VertexAttribute {
semantic: AttributeSemantic::BoneWeights,
format: AttributeFormat::Raw4,
offset,
});
offset += 4;
} else {
attrs.push(VertexAttribute {
semantic: AttributeSemantic::BoneIndices,
format: AttributeFormat::Raw4,
offset,
});
offset += 4;
}
}
'r' => {
chars.next();
attrs.push(VertexAttribute {
semantic: AttributeSemantic::Extra,
format: AttributeFormat::Raw4,
offset,
});
offset += 4;
}
'p' => {
chars.next();
if chars.peek() == Some(&'c') {
chars.next();
}
}
'o' => {
chars.next();
if chars.peek() == Some(&'i') {
chars.next();
}
}
'w' => {
chars.next();
}
_ => {
chars.next();
}
}
}
VertexFormat { attributes: attrs, stride: offset }
}
pub fn unpack_normal(packed: u32) -> [f32; 3] {
let bytes = packed.to_le_bytes();
[(bytes[0] as i8) as f32 / 127.0, (bytes[1] as i8) as f32 / 127.0, (bytes[2] as i8) as f32 / 127.0]
}
pub fn unpack_uv(packed: u32) -> [f32; 2] {
let bytes = packed.to_le_bytes();
let u_bits = u16::from_le_bytes([bytes[0], bytes[1]]);
let v_bits = u16::from_le_bytes([bytes[2], bytes[3]]);
[half::f16::from_bits(u_bits).to_f32() + 0.5, half::f16::from_bits(v_bits).to_f32() + 0.5]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xyznuv() {
let fmt = parse_vertex_format("set3/xyznuv");
assert_eq!(fmt.stride, 20);
assert_eq!(fmt.attributes.len(), 3);
assert_eq!(fmt.attributes[0].semantic, AttributeSemantic::Position);
assert_eq!(fmt.attributes[0].offset, 0);
assert_eq!(fmt.attributes[1].semantic, AttributeSemantic::Normal);
assert_eq!(fmt.attributes[1].offset, 12);
assert_eq!(fmt.attributes[2].semantic, AttributeSemantic::TexCoord0);
assert_eq!(fmt.attributes[2].offset, 16);
}
#[test]
fn test_xyznuvtb() {
let fmt = parse_vertex_format("set3/xyznuvtb");
assert_eq!(fmt.stride, 28);
assert_eq!(fmt.attributes.len(), 5);
assert_eq!(fmt.attributes[3].semantic, AttributeSemantic::Tangent);
assert_eq!(fmt.attributes[3].offset, 20);
assert_eq!(fmt.attributes[4].semantic, AttributeSemantic::Binormal);
assert_eq!(fmt.attributes[4].offset, 24);
}
#[test]
fn test_xyznuvr() {
let fmt = parse_vertex_format("set3/xyznuvr");
assert_eq!(fmt.stride, 24);
}
#[test]
fn test_xyznuvtbpc() {
let fmt = parse_vertex_format("set3/xyznuvtbpc");
assert_eq!(fmt.stride, 28);
}
#[test]
fn test_xyznuv2tb() {
let fmt = parse_vertex_format("set3/xyznuv2tb");
assert_eq!(fmt.stride, 32);
let uv_attrs: Vec<_> = fmt
.attributes
.iter()
.filter(|a| matches!(a.semantic, AttributeSemantic::TexCoord0 | AttributeSemantic::TexCoord1))
.collect();
assert_eq!(uv_attrs.len(), 2);
}
#[test]
fn test_xyznuvtbipc() {
let fmt = parse_vertex_format("set3/xyznuvtbipc");
assert_eq!(fmt.stride, 32);
}
#[test]
fn test_xyznuviiiwwtb() {
let fmt = parse_vertex_format("set3/xyznuviiiwwtb");
assert_eq!(fmt.stride, 36);
}
#[test]
fn test_unpack_normal() {
let packed = u32::from_le_bytes([127, 0, 0, 0]);
let n = unpack_normal(packed);
assert!((n[0] - 1.0).abs() < 0.01);
assert!(n[1].abs() < 0.01);
assert!(n[2].abs() < 0.01);
}
}