use std::borrow::Cow;
use crate::buffer::helpers::{u8_to_f32_iter, u8_to_u16_iter, u8_to_u32_iter};
use crate::gltf::buffers::accessor::accessor_to_bytes;
use crate::gltf::buffers::MeshBufferSkinInfoWithOffset;
use crate::gltf::error::{AwsmGltfError, Result};
pub(super) fn convert_skin(
primitive: &gltf::Primitive,
buffers: &[Vec<u8>],
vertex_count: usize, skin_joint_index_weight_bytes: &mut Vec<u8>, ) -> Result<Option<MeshBufferSkinInfoWithOffset>> {
let has_joints_0 = primitive.get(&gltf::Semantic::Joints(0)).is_some();
let has_weights_0 = primitive.get(&gltf::Semantic::Weights(0)).is_some();
if !has_joints_0 || !has_weights_0 {
return Ok(None);
}
struct SkinSetData<'a> {
joints_data: Cow<'a, [u8]>,
joints_data_type: gltf::accessor::DataType,
weights_data: Cow<'a, [u8]>,
weights_data_type: gltf::accessor::DataType,
}
let mut skin_sets_data = Vec::new();
let mut set_index = 0;
loop {
let joints_semantic = gltf::Semantic::Joints(set_index);
let weights_semantic = gltf::Semantic::Weights(set_index);
let joints_accessor = primitive.get(&joints_semantic);
let weights_accessor = primitive.get(&weights_semantic);
match (joints_accessor, weights_accessor) {
(Some(joints_accessor), Some(weights_accessor)) => {
let joints_data = accessor_to_bytes(&joints_accessor, buffers)?;
let weights_data = accessor_to_bytes(&weights_accessor, buffers)?;
skin_sets_data.push(SkinSetData {
joints_data,
joints_data_type: joints_accessor.data_type(),
weights_data,
weights_data_type: weights_accessor.data_type(),
});
set_index += 1;
}
_ => break, }
}
if skin_sets_data.is_empty() {
return Ok(None);
}
let set_count = skin_sets_data.len();
let index_weights_offset = skin_joint_index_weight_bytes.len();
for vertex_index in 0..vertex_count {
for skin_set_data in &skin_sets_data {
let indices_u32 = convert_indices_to_u32(
&skin_set_data.joints_data,
skin_set_data.joints_data_type,
vertex_index,
)?;
let weights_f32 = convert_weights_to_f32(
&skin_set_data.weights_data,
skin_set_data.weights_data_type,
vertex_index,
)?;
for i in 0..4 {
skin_joint_index_weight_bytes.extend_from_slice(&indices_u32[i].to_le_bytes());
skin_joint_index_weight_bytes.extend_from_slice(&weights_f32[i].to_le_bytes());
}
}
}
let index_weights_size = skin_joint_index_weight_bytes.len() - index_weights_offset;
Ok(Some(MeshBufferSkinInfoWithOffset {
set_count,
index_weights_offset,
index_weights_size,
}))
}
fn convert_indices_to_u32(
data: &[u8],
data_type: gltf::accessor::DataType,
vertex_index: usize,
) -> Result<[u32; 4]> {
let mut indices = [0u32; 4];
match data_type {
gltf::accessor::DataType::U16 => {
let stride = 8; let offset = vertex_index * stride;
for (i, value) in u8_to_u16_iter(&data[offset..]).take(4).enumerate() {
indices[i] = value.into();
}
}
gltf::accessor::DataType::U32 => {
let stride = 16; let offset = vertex_index * stride;
for (i, value) in u8_to_u32_iter(&data[offset..]).take(4).enumerate() {
indices[i] = value;
}
}
gltf::accessor::DataType::U8 => {
let stride = 4; let offset = vertex_index * stride;
for (i, value) in data.iter().skip(offset).take(4).enumerate() {
indices[i] = (*value).into();
}
}
_ => {
return Err(AwsmGltfError::UnsupportedSkinDataType(data_type));
}
}
Ok(indices)
}
fn convert_weights_to_f32(
data: &[u8],
data_type: gltf::accessor::DataType,
vertex_index: usize,
) -> Result<[f32; 4]> {
let mut weights = [0.0f32; 4];
match data_type {
gltf::accessor::DataType::F32 => {
let stride = 16; let offset = vertex_index * stride;
for (i, value) in u8_to_f32_iter(&data[offset..]).take(4).enumerate() {
weights[i] = value;
}
}
gltf::accessor::DataType::U16 => {
let stride = 8; let offset = vertex_index * stride;
for (i, value) in u8_to_u16_iter(&data[offset..]).take(4).enumerate() {
weights[i] = value as f32 / 65535.0;
}
}
gltf::accessor::DataType::U8 => {
let stride = 4; let offset = vertex_index * stride;
for (i, value) in data.iter().skip(offset).take(4).enumerate() {
weights[i] = *value as f32 / 255.0;
}
}
_ => {
return Err(AwsmGltfError::SkinWeights(format!(
"Unsupported joint weight data type: {:?}",
data_type
)));
}
}
Ok(weights)
}