use std::{borrow::Cow, collections::BTreeMap};
use gltf::{accessor::DataType, Semantic};
use crate::{
buffer::helpers::{i16_to_i32_vec, u16_to_u32_vec, u8_to_i16_vec, u8_to_u16_vec},
gltf::{
buffers::accessor::accessor_to_bytes,
error::{AwsmGltfError, Result},
},
meshes::buffer_info::{MeshBufferCustomVertexAttributeInfo, MeshBufferVertexAttributeInfo},
};
pub(super) fn load_attribute_data_by_kind<'a>(
gltf_attributes: &[(gltf::Semantic, gltf::Accessor<'_>)],
buffers: &'a [Vec<u8>],
) -> Result<BTreeMap<MeshBufferVertexAttributeInfo, Cow<'a, [u8]>>> {
let mut attribute_data = BTreeMap::new();
for (semantic, accessor) in gltf_attributes {
let mut attribute_kind = match convert_attribute_kind(semantic, accessor) {
Some(kind) => kind,
None => continue, };
let bytes = accessor_to_bytes(accessor, buffers)?;
let final_bytes = match (accessor.data_type(), accessor.normalized()) {
(DataType::U8, true) => {
attribute_kind.force_data_size(4); let f32_bytes: Vec<u8> = bytes
.iter()
.flat_map(|&v| {
let normalized = v as f32 / 255.0;
normalized.to_le_bytes()
})
.collect();
Cow::Owned(f32_bytes)
}
(DataType::U8, false) => {
bytes
}
(DataType::I8, true) => {
attribute_kind.force_data_size(4); let f32_bytes: Vec<u8> = bytes
.iter()
.flat_map(|&v| {
let normalized = (v as i8 as f32 / 127.0).max(-1.0);
normalized.to_le_bytes()
})
.collect();
Cow::Owned(f32_bytes)
}
(DataType::I8, false) => {
bytes
}
(DataType::U16, true) => {
attribute_kind.force_data_size(4); let u16_values = u8_to_u16_vec(&bytes);
let f32_bytes: Vec<u8> = u16_values
.iter()
.flat_map(|&v| {
let normalized = v as f32 / 65535.0;
normalized.to_le_bytes()
})
.collect();
Cow::Owned(f32_bytes)
}
(DataType::U16, false) => {
attribute_kind.force_data_size(4);
Cow::Owned(u16_to_u32_vec(&bytes))
}
(DataType::I16, true) => {
attribute_kind.force_data_size(4);
let i16_values = u8_to_i16_vec(&bytes);
let f32_bytes: Vec<u8> = i16_values
.iter()
.flat_map(|&v| {
let normalized = (v as f32 / 32767.0).max(-1.0);
normalized.to_le_bytes()
})
.collect();
Cow::Owned(f32_bytes)
}
(DataType::I16, false) => {
attribute_kind.force_data_size(4);
Cow::Owned(i16_to_i32_vec(&bytes))
}
(DataType::U32, _) | (DataType::F32, _) => {
bytes
}
};
attribute_data.insert(attribute_kind, final_bytes);
}
Ok(attribute_data)
}
pub(super) fn pack_vertex_attributes(
attribute_data: Vec<(&MeshBufferCustomVertexAttributeInfo, &Cow<'_, [u8]>)>,
vertex_attribute_bytes: &mut Vec<u8>,
) -> Result<()> {
if attribute_data.is_empty() {
return Ok(());
}
let vertex_count = attribute_data
.iter()
.map(|(attr_info, attr_data)| {
let stride = attr_info.vertex_size();
debug_assert!(stride > 0);
(attr_data.len() / stride, attr_data.len() % stride)
})
.try_fold(None, |acc, (count, remainder)| {
if remainder != 0 {
return Err(());
}
match acc {
None => Ok(Some(count)),
Some(prev) if prev == count => Ok(Some(prev)),
Some(_) => Err(()),
}
})
.map_err(|_| {
AwsmGltfError::AttributeData(
"vertex attribute buffers do not share a common vertex count".to_string(),
)
})?
.unwrap_or(0);
for vertex_index in 0..vertex_count {
for (attr_info, attr_data) in attribute_data.iter() {
let stride = attr_info.vertex_size();
let start = vertex_index * stride;
let end = start + stride;
vertex_attribute_bytes.extend_from_slice(&attr_data[start..end]);
}
}
Ok(())
}
pub(super) fn convert_attribute_kind(
semantic: &gltf::Semantic,
accessor: &gltf::Accessor<'_>,
) -> Option<MeshBufferVertexAttributeInfo> {
use crate::meshes::buffer_info::{
MeshBufferCustomVertexAttributeInfo, MeshBufferVisibilityVertexAttributeInfo,
};
match semantic {
Semantic::Positions => Some(MeshBufferVertexAttributeInfo::Visibility(
MeshBufferVisibilityVertexAttributeInfo::Positions {
data_size: accessor.data_type().size(),
component_len: accessor.dimensions().multiplicity(),
},
)),
Semantic::Normals => Some(MeshBufferVertexAttributeInfo::Visibility(
MeshBufferVisibilityVertexAttributeInfo::Normals {
data_size: accessor.data_type().size(),
component_len: accessor.dimensions().multiplicity(),
},
)),
Semantic::Tangents => Some(MeshBufferVertexAttributeInfo::Visibility(
MeshBufferVisibilityVertexAttributeInfo::Tangents {
data_size: accessor.data_type().size(),
component_len: accessor.dimensions().multiplicity(),
},
)),
Semantic::Colors(index) => Some(MeshBufferVertexAttributeInfo::Custom(
MeshBufferCustomVertexAttributeInfo::Colors {
data_size: accessor.data_type().size(),
component_len: accessor.dimensions().multiplicity(),
index: *index,
},
)),
Semantic::TexCoords(index) => Some(MeshBufferVertexAttributeInfo::Custom(
MeshBufferCustomVertexAttributeInfo::TexCoords {
data_size: accessor.data_type().size(),
component_len: accessor.dimensions().multiplicity(),
index: *index,
},
)),
Semantic::Joints(_) => {
None
}
Semantic::Weights(_) => {
None
}
}
}