use std::{
collections::BTreeSet,
io::{Cursor, Seek, SeekFrom, Write},
};
use binrw::{BinRead, BinReaderExt, BinResult, BinWrite, Endian};
use glam::{Vec2, Vec3, Vec4};
use xc3_lib::vertex::{
DataType, IndexBufferDescriptor, MorphDescriptor, MorphTargetFlags, OutlineBufferDescriptor,
Unk, UnkBufferDescriptor, UnkData, VertexBufferDescriptor, VertexBufferExtInfo,
VertexBufferExtInfoFlags, VertexData,
};
pub use xc3_lib::vertex::{PrimitiveType, WeightGroup, WeightLod};
use crate::skinning::{SkinWeights, WeightGroups, Weights};
const MAX_VERT_CAPACITY: u32 = u16::MAX as u32;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct ModelBuffers {
pub vertex_buffers: Vec<VertexBuffer>,
pub outline_buffers: Vec<OutlineBuffer>,
pub index_buffers: Vec<IndexBuffer>,
pub unk_buffers: Vec<UnkBuffer>,
pub unk_data: Option<UnkDataBuffer>,
pub weights: Option<Weights>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct VertexBuffer {
pub attributes: Vec<AttributeData>,
pub morph_blend_target: Vec<AttributeData>,
pub morph_targets: Vec<MorphTarget>,
pub outline_buffer_index: Option<usize>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct MorphTarget {
pub morph_controller_index: usize,
pub position_deltas: Vec<Vec3>,
pub normals: Vec<Vec4>,
pub tangents: Vec<Vec4>,
pub vertex_indices: Vec<u32>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct OutlineBuffer {
pub attributes: Vec<AttributeData>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct UnkBuffer {
pub unk2: u16,
pub attributes: Vec<AttributeData>,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct IndexBuffer {
pub indices: Vec<u16>,
pub primitive_type: PrimitiveType,
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct UnkDataBuffer {
pub attribute1: Vec<[u32; 3]>,
pub attribute2: Vec<u32>,
pub uniform_data: Vec<u8>,
pub unk: [f32; 6],
}
impl VertexBuffer {
pub fn vertex_count(&self) -> usize {
self.attributes.first().map(|a| a.len()).unwrap_or_default()
}
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub enum AttributeData {
Position(Vec<Vec3>),
SkinWeights2(Vec<Vec3>),
BoneIndices2(Vec<[u8; 4]>),
WeightIndex(Vec<[u16; 2]>),
WeightIndex2(Vec<[u16; 2]>),
TexCoord0(Vec<Vec2>),
TexCoord1(Vec<Vec2>),
TexCoord2(Vec<Vec2>),
TexCoord3(Vec<Vec2>),
TexCoord4(Vec<Vec2>),
TexCoord5(Vec<Vec2>),
TexCoord6(Vec<Vec2>),
TexCoord7(Vec<Vec2>),
TexCoord8(Vec<Vec2>),
Blend(Vec<Vec4>),
Unk15(Vec<Vec3>),
Unk16(Vec<[u16; 2]>),
VertexColor(Vec<Vec4>),
Unk18(Vec<Vec3>),
Unk24(Vec<Vec4>),
Unk25(Vec<Vec4>),
Unk26(Vec<Vec4>),
Normal(Vec<Vec4>),
Tangent(Vec<Vec4>),
Unk30(Vec<Vec4>),
Unk31(Vec<Vec4>),
Normal2(Vec<Vec4>),
ValInf(Vec<Vec4>),
Normal3(Vec<Vec4>),
VertexColor3(Vec<Vec4>),
Position2(Vec<Vec3>),
Normal4(Vec<Vec4>),
OldPosition(Vec<Vec3>),
Tangent2(Vec<Vec4>),
SkinWeights(Vec<Vec4>),
BoneIndices(Vec<[u8; 4]>),
Flow(Vec<u16>),
}
impl AttributeData {
pub fn len(&self) -> usize {
match self {
AttributeData::Position(v) => v.len(),
AttributeData::SkinWeights2(v) => v.len(),
AttributeData::BoneIndices2(v) => v.len(),
AttributeData::WeightIndex(v) => v.len(),
AttributeData::WeightIndex2(v) => v.len(),
AttributeData::TexCoord0(v) => v.len(),
AttributeData::TexCoord1(v) => v.len(),
AttributeData::TexCoord2(v) => v.len(),
AttributeData::TexCoord3(v) => v.len(),
AttributeData::TexCoord4(v) => v.len(),
AttributeData::TexCoord5(v) => v.len(),
AttributeData::TexCoord6(v) => v.len(),
AttributeData::TexCoord7(v) => v.len(),
AttributeData::TexCoord8(v) => v.len(),
AttributeData::Blend(v) => v.len(),
AttributeData::Unk15(v) => v.len(),
AttributeData::Unk16(v) => v.len(),
AttributeData::VertexColor(v) => v.len(),
AttributeData::Unk18(v) => v.len(),
AttributeData::Unk24(v) => v.len(),
AttributeData::Unk25(v) => v.len(),
AttributeData::Unk26(v) => v.len(),
AttributeData::Normal(v) => v.len(),
AttributeData::Tangent(v) => v.len(),
AttributeData::Unk30(v) => v.len(),
AttributeData::Unk31(v) => v.len(),
AttributeData::Normal2(v) => v.len(),
AttributeData::ValInf(v) => v.len(),
AttributeData::Normal3(v) => v.len(),
AttributeData::VertexColor3(v) => v.len(),
AttributeData::Position2(v) => v.len(),
AttributeData::Normal4(v) => v.len(),
AttributeData::OldPosition(v) => v.len(),
AttributeData::Tangent2(v) => v.len(),
AttributeData::SkinWeights(v) => v.len(),
AttributeData::BoneIndices(v) => v.len(),
AttributeData::Flow(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn write<W: Write + Seek>(
&self,
writer: &mut W,
offset: u64,
stride: u64,
endian: Endian,
) -> BinResult<()> {
let a = AttributeWriteArgs {
offset,
stride,
endian,
};
match self {
AttributeData::Position(values) => a.write(writer, values, write_f32x3),
AttributeData::SkinWeights2(values) => a.write(writer, values, write_f32x3),
AttributeData::BoneIndices2(values) => a.write(writer, values, write_u8x4),
AttributeData::WeightIndex(values) => a.write(writer, values, write_u16x2),
AttributeData::WeightIndex2(values) => a.write(writer, values, write_u16x2),
AttributeData::TexCoord0(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord1(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord2(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord3(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord4(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord5(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord6(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord7(values) => a.write(writer, values, write_f32x2),
AttributeData::TexCoord8(values) => a.write(writer, values, write_f32x2),
AttributeData::Blend(values) => a.write(writer, values, write_unorm8x4),
AttributeData::Unk15(values) => a.write(writer, values, write_f32x3),
AttributeData::Unk16(values) => a.write(writer, values, write_u16x2),
AttributeData::VertexColor(values) => a.write(writer, values, write_unorm8x4),
AttributeData::Unk18(values) => a.write(writer, values, write_f32x3),
AttributeData::Unk24(values) => a.write(writer, values, write_f32x4),
AttributeData::Unk25(values) => a.write(writer, values, write_f32x4),
AttributeData::Unk26(values) => a.write(writer, values, write_f32x4),
AttributeData::Normal(values) => a.write(writer, values, write_snorm8x4),
AttributeData::Tangent(values) => a.write(writer, values, write_snorm8x4),
AttributeData::Unk30(values) => a.write(writer, values, write_unorm8x4),
AttributeData::Unk31(values) => a.write(writer, values, write_unorm8x4),
AttributeData::Normal2(values) => a.write(writer, values, write_snorm8x4),
AttributeData::ValInf(values) => a.write(writer, values, write_snorm8x4),
AttributeData::Normal3(values) => a.write(writer, values, write_snorm8x4),
AttributeData::VertexColor3(values) => a.write(writer, values, write_unorm8x4),
AttributeData::Position2(values) => a.write(writer, values, write_f32x3),
AttributeData::Normal4(values) => a.write(writer, values, write_unorm8x4_signed),
AttributeData::OldPosition(values) => a.write(writer, values, write_f32x3),
AttributeData::Tangent2(values) => a.write(writer, values, write_unorm8x4_signed),
AttributeData::SkinWeights(values) => a.write(writer, values, write_unorm16x4),
AttributeData::BoneIndices(values) => a.write(writer, values, write_u8x4),
AttributeData::Flow(values) => a.write(writer, values, write_u16),
}
}
pub fn data_type(&self) -> DataType {
match self {
AttributeData::Position(_) => DataType::Position,
AttributeData::SkinWeights2(_) => DataType::SkinWeights2,
AttributeData::BoneIndices2(_) => DataType::BoneIndices2,
AttributeData::WeightIndex(_) => DataType::WeightIndex,
AttributeData::WeightIndex2(_) => DataType::WeightIndex2,
AttributeData::TexCoord0(_) => DataType::TexCoord0,
AttributeData::TexCoord1(_) => DataType::TexCoord1,
AttributeData::TexCoord2(_) => DataType::TexCoord2,
AttributeData::TexCoord3(_) => DataType::TexCoord3,
AttributeData::TexCoord4(_) => DataType::TexCoord4,
AttributeData::TexCoord5(_) => DataType::TexCoord5,
AttributeData::TexCoord6(_) => DataType::TexCoord6,
AttributeData::TexCoord7(_) => DataType::TexCoord7,
AttributeData::TexCoord8(_) => DataType::TexCoord8,
AttributeData::VertexColor(_) => DataType::VertexColor,
AttributeData::Unk18(_) => DataType::Unk18,
AttributeData::Unk24(_) => DataType::Unk24,
AttributeData::Unk25(_) => DataType::Unk25,
AttributeData::Unk26(_) => DataType::Unk26,
AttributeData::Blend(_) => DataType::Blend,
AttributeData::Unk15(_) => DataType::Unk15,
AttributeData::Unk16(_) => DataType::Unk16,
AttributeData::Normal(_) => DataType::Normal,
AttributeData::Tangent(_) => DataType::Tangent,
AttributeData::Unk30(_) => DataType::Unk30,
AttributeData::Unk31(_) => DataType::Unk31,
AttributeData::Normal2(_) => DataType::Normal2,
AttributeData::ValInf(_) => DataType::ValInf,
AttributeData::Normal3(_) => DataType::Normal3,
AttributeData::VertexColor3(_) => DataType::VertexColor3,
AttributeData::Position2(_) => DataType::Position2,
AttributeData::Normal4(_) => DataType::Normal4,
AttributeData::OldPosition(_) => DataType::OldPosition,
AttributeData::Tangent2(_) => DataType::Tangent2,
AttributeData::SkinWeights(_) => DataType::SkinWeights,
AttributeData::BoneIndices(_) => DataType::BoneIndices,
AttributeData::Flow(_) => DataType::Flow,
}
}
}
fn read_vertex_buffers(
vertex_data: &VertexData,
) -> BinResult<(Vec<VertexBuffer>, Option<Weights>)> {
let mut buffers = vertex_data
.vertex_buffers
.iter()
.zip(vertex_data.vertex_buffer_info.iter())
.map(|(descriptor, ext)| {
let attributes = read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&vertex_data.buffer,
Endian::Little,
)?;
Ok(VertexBuffer {
attributes,
morph_blend_target: Vec::new(),
morph_targets: Vec::new(),
outline_buffer_index: ext
.flags
.has_outline_buffer()
.then_some(ext.outline_buffer_index as usize),
})
})
.collect::<BinResult<Vec<_>>>()?;
if let Some(vertex_morphs) = &vertex_data.vertex_morphs {
assign_morph_targets(vertex_morphs, &mut buffers, vertex_data)?;
}
let skin_weights = vertex_data.weights.as_ref().and_then(|vertex_weights| {
let weights_index = vertex_weights.vertex_buffer_index as usize;
let descriptor = vertex_data.vertex_buffers.get(weights_index)?;
let attributes = read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&vertex_data.buffer,
Endian::Little,
)
.ok()?;
let (weights, bone_indices) = skin_weights_bone_indices(&attributes)?;
Some(Weights {
weight_buffers: vec![SkinWeights {
bone_indices,
weights,
}],
weight_groups: WeightGroups::Groups {
weight_groups: vertex_weights.groups.clone(),
weight_lods: vertex_weights.weight_lods.clone(),
},
})
});
Ok((buffers, skin_weights))
}
fn outline_buffer(descriptor: &OutlineBufferDescriptor, buffer: &[u8]) -> BinResult<OutlineBuffer> {
Ok(OutlineBuffer {
attributes: read_outline_buffer(descriptor, buffer)?,
})
}
fn assign_morph_targets(
vertex_morphs: &xc3_lib::vertex::VertexMorphs,
buffers: &mut [VertexBuffer],
vertex_data: &VertexData,
) -> BinResult<()> {
for descriptor in &vertex_morphs.descriptors {
if let Some(buffer) = buffers.get_mut(descriptor.vertex_buffer_index as usize) {
if let Some((blend, _default, params)) = split_targets(descriptor, vertex_morphs) {
let attributes = read_morph_blend_target(blend, &vertex_data.buffer)?;
buffer.morph_blend_target = attributes;
buffer.morph_targets = params
.iter()
.zip(descriptor.param_indices.iter())
.map(|(target, param_index)| {
read_morph_target(target, &vertex_data.buffer, *param_index)
})
.collect::<BinResult<Vec<_>>>()?;
}
}
}
Ok(())
}
fn read_morph_target(
target: &xc3_lib::vertex::MorphTarget,
buffer: &[u8],
param_index: u16,
) -> BinResult<MorphTarget> {
let vertices = read_morph_buffer_target(target, buffer)?;
let mut position_deltas = Vec::new();
let mut normals = Vec::new();
let mut tangents = Vec::new();
let mut vertex_indices = Vec::new();
for vertex in vertices {
vertex_indices.push(vertex.vertex_index);
position_deltas.push(vertex.position_delta);
normals.push(vertex.normal);
tangents.push(vertex.tangent);
}
Ok(MorphTarget {
morph_controller_index: param_index as usize,
position_deltas,
normals,
tangents,
vertex_indices,
})
}
fn split_targets<'a>(
descriptor: &MorphDescriptor,
vertex_morphs: &'a xc3_lib::vertex::VertexMorphs,
) -> Option<(
&'a xc3_lib::vertex::MorphTarget,
&'a xc3_lib::vertex::MorphTarget,
&'a [xc3_lib::vertex::MorphTarget],
)> {
let start = descriptor.target_start_index as usize;
let count = descriptor.param_indices.len() + 2;
let targets = vertex_morphs.targets.get(start..start + count)?;
let (blend_target, targets) = targets.split_first()?;
let (default_target, param_targets) = targets.split_first()?;
Some((blend_target, default_target, param_targets))
}
fn skin_weights_bone_indices(attributes: &[AttributeData]) -> Option<(Vec<Vec4>, Vec<[u8; 4]>)> {
let weights = attributes.iter().find_map(|a| match a {
AttributeData::SkinWeights(values) => Some(values.clone()),
AttributeData::SkinWeights2(values) => Some(
values
.iter()
.map(|v| {
v.extend(1.0 - v.element_sum())
})
.collect(),
),
_ => None,
})?;
let indices = attributes.iter().find_map(|a| match a {
AttributeData::BoneIndices(values) => Some(values.clone()),
AttributeData::BoneIndices2(values) => Some(values.clone()),
_ => None,
})?;
Some((weights, indices))
}
fn read_index_buffers(vertex_data: &VertexData, endian: Endian) -> BinResult<Vec<IndexBuffer>> {
vertex_data
.index_buffers
.iter()
.map(|descriptor| read_index_buffer(descriptor, &vertex_data.buffer, endian))
.collect()
}
fn read_index_buffer(
descriptor: &IndexBufferDescriptor,
buffer: &[u8],
endian: Endian,
) -> BinResult<IndexBuffer> {
let mut reader = Cursor::new(buffer);
reader.seek(SeekFrom::Start(descriptor.data_offset as u64))?;
let mut indices = Vec::with_capacity(descriptor.index_count.min(MAX_VERT_CAPACITY) as usize);
for _ in 0..descriptor.index_count {
let index: u16 = reader.read_type(endian)?;
indices.push(index);
}
Ok(IndexBuffer {
indices,
primitive_type: descriptor.primitive_type,
})
}
fn read_attributes(
data_offset: u64,
vertex_count: u32,
vertex_size: u32,
attributes: &[xc3_lib::vertex::VertexAttribute],
buffer: &[u8],
endian: Endian,
) -> BinResult<Vec<AttributeData>> {
let mut offset = 0;
attributes
.iter()
.map(|a| {
let data = read_attribute(
a.data_type,
data_offset,
vertex_count,
vertex_size,
offset,
buffer,
endian,
);
offset += a.data_size as u64;
data
})
.collect()
}
fn read_attribute(
data_type: DataType,
data_offset: u64,
vertex_count: u32,
vertex_size: u32,
relative_offset: u64,
buffer: &[u8],
endian: Endian,
) -> BinResult<AttributeData> {
let a = AttributeReadArgs {
offset: data_offset,
count: vertex_count,
stride: vertex_size,
relative_offset,
endian,
};
let b = buffer;
match data_type {
DataType::Position => a.read(b, read_f32x3).map(AttributeData::Position),
DataType::SkinWeights2 => a.read(b, read_f32x3).map(AttributeData::SkinWeights2),
DataType::BoneIndices2 => a.read(b, read_u8x4).map(AttributeData::BoneIndices2),
DataType::WeightIndex => a.read(b, read_u16x2).map(AttributeData::WeightIndex),
DataType::WeightIndex2 => a.read(b, read_u16x2).map(AttributeData::WeightIndex2),
DataType::TexCoord0 => a.read(b, read_f32x2).map(AttributeData::TexCoord0),
DataType::TexCoord1 => a.read(b, read_f32x2).map(AttributeData::TexCoord1),
DataType::TexCoord2 => a.read(b, read_f32x2).map(AttributeData::TexCoord2),
DataType::TexCoord3 => a.read(b, read_f32x2).map(AttributeData::TexCoord3),
DataType::TexCoord4 => a.read(b, read_f32x2).map(AttributeData::TexCoord4),
DataType::TexCoord5 => a.read(b, read_f32x2).map(AttributeData::TexCoord5),
DataType::TexCoord6 => a.read(b, read_f32x2).map(AttributeData::TexCoord6),
DataType::TexCoord7 => a.read(b, read_f32x2).map(AttributeData::TexCoord7),
DataType::TexCoord8 => a.read(b, read_f32x2).map(AttributeData::TexCoord8),
DataType::Blend => a.read(b, read_unorm8x4).map(AttributeData::Blend),
DataType::Unk15 => a.read(b, read_f32x3).map(AttributeData::Unk15),
DataType::Unk16 => a.read(b, read_u16x2).map(AttributeData::Unk16),
DataType::VertexColor => a.read(b, read_unorm8x4).map(AttributeData::VertexColor),
DataType::Unk18 => a.read(b, read_f32x3).map(AttributeData::Unk18),
DataType::Unk24 => a.read(b, read_f32x4).map(AttributeData::Unk24),
DataType::Unk25 => a.read(b, read_f32x4).map(AttributeData::Unk25),
DataType::Unk26 => a.read(b, read_f32x4).map(AttributeData::Unk26),
DataType::Normal => a.read(b, read_snorm8x4).map(AttributeData::Normal),
DataType::Tangent => a.read(b, read_snorm8x4).map(AttributeData::Tangent),
DataType::Unk30 => a.read(b, read_unorm8x4).map(AttributeData::Unk30),
DataType::Unk31 => a.read(b, read_unorm8x4).map(AttributeData::Unk31),
DataType::Normal2 => a.read(b, read_snorm8x4).map(AttributeData::Normal2),
DataType::ValInf => a.read(b, read_snorm8x4).map(AttributeData::ValInf),
DataType::Normal3 => a.read(b, read_snorm8x4).map(AttributeData::Normal3),
DataType::VertexColor3 => a.read(b, read_unorm8x4).map(AttributeData::VertexColor3),
DataType::Position2 => a.read(b, read_f32x3).map(AttributeData::Position2),
DataType::Normal4 => a.read(b, read_unorm8x4_signed).map(AttributeData::Normal4),
DataType::OldPosition => a.read(b, read_f32x3).map(AttributeData::OldPosition),
DataType::Tangent2 => a.read(b, read_unorm8x4_signed).map(AttributeData::Tangent2),
DataType::SkinWeights => a.read(b, read_unorm16x4).map(AttributeData::SkinWeights),
DataType::BoneIndices => a.read(b, read_u8x4).map(AttributeData::BoneIndices),
DataType::Flow => a.read(b, read_u16).map(AttributeData::Flow),
}
}
struct AttributeReadArgs {
offset: u64,
count: u32,
stride: u32,
relative_offset: u64,
endian: Endian,
}
impl AttributeReadArgs {
fn read<T, F>(&self, buffer: &[u8], read_item: F) -> BinResult<Vec<T>>
where
F: Fn(&mut Cursor<&[u8]>, Endian) -> BinResult<T>,
{
if self.stride == 0 {
return Err(binrw::Error::AssertFail {
pos: self.offset,
message: "Attribute stride must not be 0".to_string(),
});
}
let mut reader = Cursor::new(buffer);
let mut values = Vec::with_capacity(self.count.min(MAX_VERT_CAPACITY) as usize);
for i in 0..self.count as u64 {
let offset = self.offset + i * self.stride as u64 + self.relative_offset;
reader.seek(SeekFrom::Start(offset))?;
values.push(read_item(&mut reader, self.endian)?);
}
Ok(values)
}
}
struct AttributeWriteArgs {
offset: u64,
stride: u64,
endian: Endian,
}
impl AttributeWriteArgs {
fn write<T, F, W>(&self, writer: &mut W, values: &[T], write_item: F) -> BinResult<()>
where
W: Write + Seek,
F: Fn(&mut W, &T, Endian) -> BinResult<()>,
{
for (i, value) in values.iter().enumerate() {
writer.seek(SeekFrom::Start(self.offset + i as u64 * self.stride))?;
write_item(writer, value, self.endian)?;
}
Ok(())
}
}
fn read_u16(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<u16> {
reader.read_type(endian)
}
fn read_u16x2(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<[u16; 2]> {
reader.read_type(endian)
}
fn read_u8x4(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<[u8; 4]> {
reader.read_type(endian)
}
fn read_f32x2(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec2> {
let value: [f32; 2] = reader.read_type(endian)?;
Ok(value.into())
}
fn read_f32x3(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec3> {
let value: [f32; 3] = reader.read_type(endian)?;
Ok(value.into())
}
fn read_f32x4(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec4> {
let value: [f32; 4] = reader.read_type(endian)?;
Ok(value.into())
}
fn read_unorm8x4(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec4> {
let value: [u8; 4] = reader.read_type(endian)?;
Ok(value.map(|u| u as f32 / 255.0).into())
}
fn read_snorm8x4(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec4> {
let value: [i8; 4] = reader.read_type(endian)?;
Ok(value.map(|i| i as f32 / 127.0).into())
}
fn read_unorm8x4_signed(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec4> {
let value: [u8; 4] = reader.read_type(endian)?;
Ok(value.map(|u| u as f32 / 127.5 - 1.0).into())
}
fn read_unorm16x4(reader: &mut Cursor<&[u8]>, endian: Endian) -> BinResult<Vec4> {
let value: [u16; 4] = reader.read_type(endian)?;
Ok(value.map(|u| u as f32 / 65535.0).into())
}
#[derive(BinRead, BinWrite)]
struct MorphBufferTargetVertex {
position_delta: [f32; 3],
_unk1: u32,
normal: [u8; 4],
tangent: [u8; 4],
_unk2: u32,
vertex_index: u32,
}
#[derive(Debug, PartialEq)]
struct MorphTargetVertex {
position_delta: Vec3,
normal: Vec4,
tangent: Vec4,
vertex_index: u32,
}
fn read_morph_blend_target(
base_target: &xc3_lib::vertex::MorphTarget,
model_bytes: &[u8],
) -> BinResult<Vec<AttributeData>> {
read_attributes(
base_target.data_offset as u64,
base_target.vertex_count,
base_target.vertex_size,
&[
DataType::Position2.into(),
DataType::Normal4.into(),
DataType::OldPosition.into(),
DataType::Tangent2.into(),
],
model_bytes,
Endian::Little,
)
}
fn read_morph_buffer_target(
morph_target: &xc3_lib::vertex::MorphTarget,
model_bytes: &[u8],
) -> BinResult<Vec<MorphTargetVertex>> {
let mut reader = Cursor::new(model_bytes);
(0..morph_target.vertex_count as u64)
.map(|i| {
reader.seek(SeekFrom::Start(
morph_target.data_offset as u64 + i * morph_target.vertex_size as u64,
))?;
let vertex: MorphBufferTargetVertex = reader.read_le()?;
Ok(MorphTargetVertex {
position_delta: vertex.position_delta.into(),
normal: vertex.normal.map(|u| u as f32 / 127.5 - 1.0).into(),
tangent: vertex.tangent.map(|u| u as f32 / 127.5 - 1.0).into(),
vertex_index: vertex.vertex_index,
})
})
.collect()
}
fn read_outline_buffer(
descriptor: &xc3_lib::vertex::OutlineBufferDescriptor,
buffer: &[u8],
) -> BinResult<Vec<AttributeData>> {
if descriptor.vertex_size == 8 {
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&[DataType::Normal.into(), DataType::VertexColor.into()],
buffer,
Endian::Little,
)
} else {
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&[DataType::VertexColor.into()],
buffer,
Endian::Little,
)
}
}
impl ModelBuffers {
pub fn from_vertex_data(vertex_data: &VertexData) -> BinResult<Self> {
let (vertex_buffers, weights) = read_vertex_buffers(vertex_data)?;
let index_buffers = read_index_buffers(vertex_data, Endian::Little)?;
let outline_buffers = vertex_data
.outline_buffers
.iter()
.map(|descriptor| outline_buffer(descriptor, &vertex_data.buffer))
.collect::<BinResult<Vec<_>>>()?;
let unk_buffers = match &vertex_data.unk7 {
Some(unk) => read_unk_buffers(unk, &vertex_data.buffer)?,
None => Vec::new(),
};
let unk_data = vertex_data
.unk_data
.as_ref()
.map(|u| read_unk_data_buffer(u, &vertex_data.buffer))
.transpose()?;
Ok(Self {
vertex_buffers,
outline_buffers,
index_buffers,
unk_buffers,
unk_data,
weights,
})
}
pub fn from_vertex_data_legacy(
vertex_data: &xc3_lib::mxmd::legacy::VertexData,
endian: Endian,
) -> BinResult<Self> {
let vertex_buffers = read_vertex_buffers_legacy(vertex_data, endian)?;
let index_buffers = read_index_buffers_legacy(vertex_data, endian)?;
let weights = weights_legacy(&vertex_buffers, vertex_data.weight_buffer_indices);
Ok(Self {
vertex_buffers,
outline_buffers: Vec::new(),
index_buffers,
unk_buffers: Vec::new(),
unk_data: None,
weights,
})
}
pub fn to_vertex_data(&self) -> BinResult<VertexData> {
let mut vertex_buffers = Vec::new();
let mut index_buffers = Vec::new();
let mut outline_buffers = Vec::new();
let mut writer = Cursor::new(Vec::new());
for buffer in &self.vertex_buffers {
let vertex_buffer =
write_vertex_buffer(&mut writer, &buffer.attributes, Endian::Little)?;
vertex_buffers.push(vertex_buffer);
}
if let Some(weights) = &self.weights {
let weights_buffer = write_vertex_buffer(
&mut writer,
&[
AttributeData::SkinWeights(weights.weight_buffers[0].weights.clone()),
AttributeData::BoneIndices(weights.weight_buffers[0].bone_indices.clone()),
],
Endian::Little,
)?;
vertex_buffers.push(weights_buffer);
}
for buffer in &self.outline_buffers {
let outline_buffer = write_outline_buffer(&mut writer, &buffer.attributes)?;
outline_buffers.push(outline_buffer);
}
for buffer in &self.index_buffers {
align(&mut writer, 4)?;
let index_buffer = write_index_buffer(&mut writer, buffer, Endian::Little)?;
index_buffers.push(index_buffer);
}
align(&mut writer, 256)?;
let vertex_morphs = if self
.vertex_buffers
.iter()
.any(|b| !b.morph_targets.is_empty())
{
Some(self.write_morph_targets(&mut writer)?)
} else {
None
};
align(&mut writer, 256)?;
let unk7 = if !self.unk_buffers.is_empty() {
Some(write_unk_buffers(&mut writer, &self.unk_buffers)?)
} else {
None
};
align(&mut writer, 256)?;
let unk_data = self
.unk_data
.as_ref()
.map(|unk_data| write_unk_data_buffer(&mut writer, unk_data))
.transpose()?;
align(&mut writer, 4096)?;
let mut vertex_buffer_info: Vec<_> = self
.vertex_buffers
.iter()
.map(|buffer| VertexBufferExtInfo {
flags: VertexBufferExtInfoFlags::new(
buffer.outline_buffer_index.is_some(),
!buffer.morph_targets.is_empty(),
0u8.into(),
),
outline_buffer_index: buffer.outline_buffer_index.unwrap_or_default() as u16,
morph_target_start_index: 0,
morph_target_count: 0,
unk: 0,
})
.collect();
if let Some(morphs) = &vertex_morphs {
for descriptor in &morphs.descriptors {
let count = if descriptor.param_indices.is_empty() {
0
} else {
descriptor.param_indices.len() + 2
};
let info = &mut vertex_buffer_info[descriptor.vertex_buffer_index as usize];
info.morph_target_start_index = descriptor.target_start_index as u16;
info.morph_target_count = count as u16;
}
}
let weights = self
.weights
.as_ref()
.and_then(|weights| match &weights.weight_groups {
WeightGroups::Legacy { .. } => None,
WeightGroups::Groups {
weight_groups,
weight_lods,
} => Some(xc3_lib::vertex::Weights {
groups: weight_groups.clone(),
vertex_buffer_index: vertex_buffers.len() as u16 - 1,
weight_lods: weight_lods.clone(),
unk4: 1, unks: [0; 4],
}),
});
Ok(VertexData {
vertex_buffers,
index_buffers,
unk0: 0,
unk1: 0,
unk2: 0,
vertex_buffer_info,
outline_buffers,
vertex_morphs,
buffer: writer.into_inner(),
unk_data,
weights,
unk7,
unks: [0; 5],
})
}
fn write_morph_targets(
&self,
writer: &mut Cursor<Vec<u8>>,
) -> BinResult<xc3_lib::vertex::VertexMorphs> {
let mut targets = Vec::new();
let mut descriptors = Vec::new();
for (i, buffer) in self
.vertex_buffers
.iter()
.enumerate()
.filter(|(_, b)| !b.morph_targets.is_empty())
{
let descriptor = MorphDescriptor {
vertex_buffer_index: i as u32,
target_start_index: targets.len() as u32,
param_indices: buffer
.morph_targets
.iter()
.map(|t| t.morph_controller_index as u16)
.collect(),
unk2: 3, };
descriptors.push(descriptor);
let target = write_morph_blend_target(writer, &buffer.morph_blend_target)?;
targets.push(target);
align(writer, 256)?;
let modified_indices: BTreeSet<_> = buffer
.morph_targets
.iter()
.flat_map(|t| &t.vertex_indices)
.copied()
.collect();
let target = write_morph_default_target(writer, modified_indices, buffer)?;
targets.push(target);
align(writer, 256)?;
for morph_target in &buffer.morph_targets {
let target = write_morph_param_target(writer, morph_target)?;
targets.push(target);
align(writer, 256)?;
}
}
Ok(xc3_lib::vertex::VertexMorphs {
descriptors,
targets,
unks: [0; 4],
})
}
}
fn write_unk_data_buffer(
writer: &mut Cursor<Vec<u8>>,
unk_data: &UnkDataBuffer,
) -> BinResult<UnkData> {
let uniform_data_offset = writer.stream_position()? as u32;
writer.write_all(&unk_data.uniform_data)?;
let vertex_data_offset = writer.stream_position()? as u32;
if unk_data.attribute2.is_empty() {
for a1 in &unk_data.attribute1 {
a1.write_le(writer)?;
}
} else {
for (a1, a2) in unk_data.attribute1.iter().zip(unk_data.attribute2.iter()) {
a1.write_le(writer)?;
a2.write_le(writer)?;
}
}
let vertex_size = if unk_data.attribute2.is_empty() {
12
} else {
16
};
let vertex_count = unk_data.attribute1.len() as u32;
Ok(UnkData {
unk1: if vertex_size == 12 { 1 } else { 3 },
vertex_data_offset,
vertex_data_length: vertex_size * vertex_count,
uniform_data_offset,
uniform_data_length: unk_data.uniform_data.len() as u32,
vertex_count,
vertex_size,
unk: unk_data.unk,
})
}
fn write_morph_default_target(
writer: &mut Cursor<Vec<u8>>,
modified_indices: BTreeSet<u32>,
buffer: &VertexBuffer,
) -> Result<xc3_lib::vertex::MorphTarget, binrw::Error> {
let offset = writer.stream_position()?;
let positions = buffer
.morph_blend_target
.iter()
.find_map(|a| {
if let AttributeData::Position2(values) = a {
Some(AttributeData::Position2(
modified_indices
.iter()
.map(|i| values[*i as usize])
.collect(),
))
} else {
None
}
})
.unwrap();
positions.write(writer, offset, 32, Endian::Little)?;
write_data(
writer,
&vec![0u32; modified_indices.len()],
offset + 12,
32,
Endian::Little,
write_u32,
)?;
let normals = buffer
.morph_blend_target
.iter()
.find_map(|a| {
if let AttributeData::Normal4(values) = a {
Some(AttributeData::Normal4(
modified_indices
.iter()
.map(|i| values[*i as usize])
.collect(),
))
} else {
None
}
})
.unwrap();
normals.write(writer, offset + 16, 32, Endian::Little)?;
let tangents = buffer
.morph_blend_target
.iter()
.find_map(|a| {
if let AttributeData::Tangent2(values) = a {
Some(AttributeData::Tangent2(
modified_indices
.iter()
.map(|i| values[*i as usize])
.collect(),
))
} else {
None
}
})
.unwrap();
tangents.write(writer, offset + 20, 32, Endian::Little)?;
write_data(
writer,
&vec![0u32; modified_indices.len()],
offset + 24,
32,
Endian::Little,
write_u32,
)?;
let indices: Vec<_> = modified_indices.iter().copied().collect();
write_data(writer, &indices, offset + 28, 32, Endian::Little, write_u32)?;
Ok(xc3_lib::vertex::MorphTarget {
data_offset: offset as u32,
vertex_count: modified_indices.len() as u32,
vertex_size: 32,
flags: MorphTargetFlags::new(0, false, true, false, 0u8.into()),
})
}
fn write_morph_param_target(
writer: &mut Cursor<Vec<u8>>,
morph_target: &MorphTarget,
) -> Result<xc3_lib::vertex::MorphTarget, binrw::Error> {
let offset = writer.stream_position()?;
for (((position, normal), tangent), index) in morph_target
.position_deltas
.iter()
.zip(morph_target.normals.iter())
.zip(morph_target.tangents.iter())
.zip(morph_target.vertex_indices.iter())
{
write_f32x3(writer, position, Endian::Little)?;
write_u32(writer, &0, Endian::Little)?;
write_unorm8x4_signed(writer, normal, Endian::Little)?;
write_unorm8x4_signed(writer, tangent, Endian::Little)?;
write_u32(writer, &0, Endian::Little)?;
write_u32(writer, index, Endian::Little)?;
}
Ok(xc3_lib::vertex::MorphTarget {
data_offset: offset as u32,
vertex_count: morph_target.position_deltas.len() as u32,
vertex_size: 32,
flags: MorphTargetFlags::new(0, false, false, true, 0u8.into()),
})
}
fn write_morph_blend_target(
writer: &mut Cursor<Vec<u8>>,
blend_target: &[AttributeData],
) -> Result<xc3_lib::vertex::MorphTarget, binrw::Error> {
let descriptor = write_vertex_buffer(writer, blend_target, Endian::Little)?;
Ok(xc3_lib::vertex::MorphTarget {
data_offset: descriptor.data_offset,
vertex_count: descriptor.vertex_count,
vertex_size: descriptor.vertex_size, flags: MorphTargetFlags::new(0, true, false, false, 0u8.into()),
})
}
fn read_index_buffers_legacy(
vertex_data: &xc3_lib::mxmd::legacy::VertexData,
endian: Endian,
) -> BinResult<Vec<IndexBuffer>> {
let data_offset = 0;
vertex_data
.index_buffers
.iter()
.map(|descriptor| {
read_index_buffer(
&IndexBufferDescriptor {
data_offset,
index_count: descriptor.index_count,
primitive_type: PrimitiveType::TriangleList,
index_format: xc3_lib::vertex::IndexFormat::Uint16,
unk3: 0,
unk4: 0,
},
&descriptor.data,
endian,
)
})
.collect()
}
fn read_vertex_buffers_legacy(
vertex_data: &xc3_lib::mxmd::legacy::VertexData,
endian: Endian,
) -> BinResult<Vec<VertexBuffer>> {
let data_offset = 0;
vertex_data
.vertex_buffers
.iter()
.map(|descriptor| {
Ok(VertexBuffer {
attributes: read_attributes(
data_offset,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&descriptor.data,
endian,
)?,
morph_blend_target: Vec::new(),
morph_targets: Vec::new(),
outline_buffer_index: None,
})
})
.collect()
}
fn weights_legacy(
vertex_buffers: &[VertexBuffer],
weight_buffer_indices: [u16; 6],
) -> Option<Weights> {
let weight_buffers = vertex_buffers
.iter()
.filter_map(|b| {
let (weights, bone_indices) = skin_weights_bone_indices(&b.attributes)?;
Some(SkinWeights {
bone_indices,
weights,
})
})
.collect();
let weight_buffer_start = vertex_buffers
.iter()
.position(|b| skin_weights_bone_indices(&b.attributes).is_some())
.unwrap_or_default();
Some(Weights {
weight_buffers,
weight_groups: WeightGroups::Legacy {
weight_buffer_indices: weight_buffer_indices
.map(|i| (i as usize).saturating_sub(weight_buffer_start)),
},
})
}
fn write_unk_buffers(
writer: &mut Cursor<Vec<u8>>,
unk_buffers: &[UnkBuffer],
) -> Result<Unk, binrw::Error> {
let data_offset = writer.stream_position()? as u32;
let mut buffers = Vec::new();
let mut start_index = 0;
for buffer in unk_buffers {
let unk_buffer = write_unk_buffer(writer, buffer, data_offset, start_index)?;
start_index += unk_buffer.count;
buffers.push(unk_buffer);
}
let data_length = writer.stream_position()? as u32 - data_offset;
Ok(Unk {
buffers,
data_length,
data_offset,
unks: [0; 8],
})
}
fn write_unk_buffer<W: Write + Seek>(
writer: &mut W,
buffer: &UnkBuffer,
data_offset: u32,
start_index: u32,
) -> BinResult<UnkBufferDescriptor> {
let descriptor = write_vertex_buffer(writer, &buffer.attributes, Endian::Little)?;
Ok(UnkBufferDescriptor {
unk1: if descriptor.vertex_size == 16 { 0 } else { 1 },
unk2: buffer.unk2,
count: descriptor.vertex_count,
offset: descriptor.data_offset - data_offset,
unk5: 0,
start_index,
})
}
fn read_unk_buffers(unk: &xc3_lib::vertex::Unk, buffer: &[u8]) -> BinResult<Vec<UnkBuffer>> {
unk.buffers
.iter()
.map(|descriptor| read_unk_buffer(descriptor, unk.data_offset as u64, buffer))
.collect()
}
fn read_unk_buffer(
descriptor: &UnkBufferDescriptor,
data_offset: u64,
buffer: &[u8],
) -> Result<UnkBuffer, binrw::Error> {
Ok(UnkBuffer {
unk2: descriptor.unk2,
attributes: if descriptor.unk1 == 0 {
read_attributes(
data_offset + descriptor.offset as u64,
descriptor.count,
16,
&[DataType::Position.into(), DataType::VertexColor.into()],
buffer,
Endian::Little,
)?
} else {
read_attributes(
data_offset + descriptor.offset as u64,
descriptor.count,
24,
&[
DataType::Position.into(),
DataType::VertexColor.into(),
DataType::VertexColor.into(),
DataType::VertexColor.into(),
],
buffer,
Endian::Little,
)?
},
})
}
fn read_unk_data_buffer(unk: &UnkData, buffer: &[u8]) -> BinResult<UnkDataBuffer> {
let mut reader = Cursor::new(buffer);
reader.set_position(unk.vertex_data_offset as u64);
let mut attribute1 = Vec::new();
let mut attribute2 = Vec::new();
for _ in 0..unk.vertex_count {
let xyz: [u32; 3] = reader.read_le()?;
attribute1.push(xyz);
if unk.vertex_size == 16 {
let unk: u32 = reader.read_le()?;
attribute2.push(unk);
}
}
let mut uniform_data = Vec::new();
reader.set_position(unk.uniform_data_offset as u64);
for _ in 0..unk.uniform_data_length {
uniform_data.push(reader.read_le()?);
}
Ok(UnkDataBuffer {
attribute1,
attribute2,
uniform_data,
unk: unk.unk,
})
}
fn align(buffer_writer: &mut Cursor<Vec<u8>>, align: u64) -> Result<(), binrw::Error> {
let aligned_size = buffer_writer.position().next_multiple_of(align);
let padding = aligned_size - buffer_writer.position();
buffer_writer.write_all(&vec![0u8; padding as usize])?;
Ok(())
}
fn write_index_buffer<W: Write + Seek>(
writer: &mut W,
buffer: &IndexBuffer,
endian: Endian,
) -> BinResult<IndexBufferDescriptor> {
let data_offset = writer.stream_position()? as u32;
buffer.indices.write_options(writer, endian, ())?;
Ok(IndexBufferDescriptor {
data_offset,
index_count: buffer.indices.len() as u32,
primitive_type: buffer.primitive_type,
index_format: xc3_lib::vertex::IndexFormat::Uint16,
unk3: 0,
unk4: 0,
})
}
fn write_vertex_buffer<W: Write + Seek>(
writer: &mut W,
attribute_data: &[AttributeData],
endian: Endian,
) -> BinResult<VertexBufferDescriptor> {
let data_offset = writer.stream_position()? as u32;
let attributes: Vec<xc3_lib::vertex::VertexAttribute> = attribute_data
.iter()
.map(|a| a.data_type().into())
.collect();
let vertex_size = attributes.iter().map(|a| a.data_size as u32).sum();
let vertex_count = attribute_data[0].len() as u32;
let mut offset = writer.stream_position()?;
for (a, data) in attributes.iter().zip(attribute_data) {
data.write(writer, offset, vertex_size as u64, endian)?;
offset += a.data_size as u64;
}
Ok(VertexBufferDescriptor {
data_offset,
vertex_count,
vertex_size,
attributes,
unk1: 0,
unk2: 0,
unk3: 0,
})
}
fn write_outline_buffer<W: Write + Seek>(
writer: &mut W,
attribute_data: &[AttributeData],
) -> BinResult<OutlineBufferDescriptor> {
let buffer = write_vertex_buffer(writer, attribute_data, Endian::Little)?;
Ok(OutlineBufferDescriptor {
data_offset: buffer.data_offset,
vertex_count: buffer.vertex_count,
vertex_size: buffer.vertex_size,
unk: 0,
})
}
fn write_data<T, F, W>(
writer: &mut W,
values: &[T],
offset: u64,
stride: u64,
endian: Endian,
write_item: F,
) -> BinResult<()>
where
W: Write + Seek,
F: Fn(&mut W, &T, Endian) -> BinResult<()>,
{
AttributeWriteArgs {
offset,
stride,
endian,
}
.write(writer, values, write_item)
}
fn write_u16<W: Write + Seek>(writer: &mut W, value: &u16, endian: Endian) -> BinResult<()> {
value.write_options(writer, endian, ())
}
fn write_u16x2<W: Write + Seek>(writer: &mut W, value: &[u16; 2], endian: Endian) -> BinResult<()> {
value.write_options(writer, endian, ())
}
fn write_u32<W: Write + Seek>(writer: &mut W, value: &u32, endian: Endian) -> BinResult<()> {
value.write_options(writer, endian, ())
}
fn write_u8x4<W: Write + Seek>(writer: &mut W, value: &[u8; 4], endian: Endian) -> BinResult<()> {
value.write_options(writer, endian, ())
}
fn write_f32x2<W: Write + Seek>(writer: &mut W, value: &Vec2, endian: Endian) -> BinResult<()> {
value.to_array().write_options(writer, endian, ())
}
fn write_f32x3<W: Write + Seek>(writer: &mut W, value: &Vec3, endian: Endian) -> BinResult<()> {
value.to_array().write_options(writer, endian, ())
}
fn write_f32x4<W: Write + Seek>(writer: &mut W, value: &Vec4, endian: Endian) -> BinResult<()> {
value.to_array().write_options(writer, endian, ())
}
fn write_unorm8x4<W: Write + Seek>(writer: &mut W, value: &Vec4, endian: Endian) -> BinResult<()> {
value
.to_array()
.map(|f| (f * 255.0) as u8)
.write_options(writer, endian, ())
}
fn write_unorm16x4<W: Write + Seek>(writer: &mut W, value: &Vec4, endian: Endian) -> BinResult<()> {
value
.to_array()
.map(|f| (f * 65535.0) as u16)
.write_options(writer, endian, ())
}
fn write_snorm8x4<W: Write + Seek>(writer: &mut W, value: &Vec4, endian: Endian) -> BinResult<()> {
value
.to_array()
.map(|f| (f * 127.0) as i8)
.write_options(writer, endian, ())
}
fn write_unorm8x4_signed<W: Write + Seek>(
writer: &mut W,
value: &Vec4,
endian: Endian,
) -> BinResult<()> {
value
.to_array()
.map(|f| (f * 127.5 + 127.5) as u8)
.write_options(writer, endian, ())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_hex_eq;
use glam::{vec2, vec3, vec4};
use hexlit::hex;
use xc3_lib::vertex::VertexAttribute;
#[test]
fn vertex_buffer_indices() {
let data = hex!(00000100 02000100);
let descriptor = IndexBufferDescriptor {
data_offset: 0,
index_count: 4,
primitive_type: xc3_lib::vertex::PrimitiveType::TriangleList,
index_format: xc3_lib::vertex::IndexFormat::Uint16,
unk3: 0,
unk4: 0,
};
let buffer = read_index_buffer(&descriptor, &data, Endian::Little).unwrap();
assert_eq!(
IndexBuffer {
indices: vec![0, 1, 2, 1],
primitive_type: PrimitiveType::TriangleList
},
buffer
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_index_buffer(&mut writer, &buffer, Endian::Little).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn vertex_buffer_vertices() {
let data = hex!(
0x459ecd3d 8660673f f2ad923d
13010000
fd8d423f aea11b3f
7f00ffff
21fb7a00
7a00df7f
0x8879143e 81d46a3f 54db4e3d
14010000
72904a3f 799d193f
7f00ffff
620c4f00
4f009e7f
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 36,
attributes: vec![
VertexAttribute {
data_type: DataType::Position,
data_size: 12,
},
VertexAttribute {
data_type: DataType::WeightIndex,
data_size: 4,
},
VertexAttribute {
data_type: DataType::TexCoord0,
data_size: 8,
},
VertexAttribute {
data_type: DataType::VertexColor,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Normal,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Tangent,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::Position(vec![
vec3(0.10039953, 0.9038166, 0.07162084),
vec3(0.14499485, 0.91730505, 0.050502136),
]),
AttributeData::WeightIndex(vec![[275, 0], [276, 0]]),
AttributeData::TexCoord0(vec![
vec2(0.75997907, 0.6079358),
vec2(0.79126656, 0.6000591),
]),
AttributeData::VertexColor(vec![
vec4(0.49803922, 0.0, 1.0, 1.0),
vec4(0.49803922, 0.0, 1.0, 1.0),
]),
AttributeData::Normal(vec![
vec4(0.25984251, -0.03937008, 0.96062994, 0.0),
vec4(0.77165353, 0.09448819, 0.62204725, 0.0),
]),
AttributeData::Tangent(vec![
vec4(0.96062994, 0.0, -0.25984251, 1.0),
vec4(0.62204725, 0.0, -0.77165353, 1.0),
]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Little
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Little).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn vertex_buffer_vertices_val_inf() {
let data = hex!(
0x95085ebd 7d3c9a3f 7ac87c3f
ee080000
60269e3e 3038873d
bb9be181
38bd5b7f
38bd5b7f
eb8274bd 098c9d3f e90e7d3f
ee080000
30259e3e 6017983c
a83dbd81
0f66497f
0f66497f
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 36,
attributes: vec![
VertexAttribute {
data_type: DataType::Position,
data_size: 12,
},
VertexAttribute {
data_type: DataType::WeightIndex,
data_size: 4,
},
VertexAttribute {
data_type: DataType::TexCoord0,
data_size: 8,
},
VertexAttribute {
data_type: DataType::Tangent,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Normal2,
data_size: 4,
},
VertexAttribute {
data_type: DataType::ValInf,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::Position(vec![
vec3(-0.054207403, 1.204971, 0.987434),
vec3(-0.059695166, 1.230836, 0.98850876),
]),
AttributeData::WeightIndex(vec![[2286, 0], [2286, 0]]),
AttributeData::TexCoord0(vec![
vec2(0.30888653, 0.06602514),
vec2(0.30887747, 0.018565834),
]),
AttributeData::Tangent(vec![
vec4(-0.54330707, -0.79527557, -0.24409449, -1.0),
vec4(-0.6929134, 0.48031497, -0.52755904, -1.0),
]),
AttributeData::Normal2(vec![
vec4(0.44094488, -0.52755904, 0.71653545, 1.0),
vec4(0.11811024, 0.8031496, 0.5748032, 1.0),
]),
AttributeData::ValInf(vec![
vec4(0.44094488, -0.52755904, 0.71653545, 1.0),
vec4(0.11811024, 0.8031496, 0.5748032, 1.0),
]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Little
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Little).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn weight_buffer_vertices() {
let data = hex!(
aec75138 00000000 18170000
0x1ec5e13a 00000000 18170000
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 12,
attributes: vec![
VertexAttribute {
data_type: DataType::SkinWeights,
data_size: 8,
},
VertexAttribute {
data_type: DataType::BoneIndices,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::SkinWeights(vec![
vec4(0.7800107, 0.21998931, 0.0, 0.0),
vec4(0.77000076, 0.22999924, 0.0, 0.0),
]),
AttributeData::BoneIndices(vec![[24, 23, 0, 0], [24, 23, 0, 0]]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Little
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Little).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn map_vertex_buffer_vertices() {
let data = hex!(
3c873845 d0a15c43 988cbcc3
dc92fd3f c6913dc2
588b0e40 9a103ec2
dc92fd3f c6913dc2
8e691940 d8cd16c0
b4401a40 113a17c0
8e691940 d8cd16c0
bca0333e d801133f
493e223f dec2e33e
0e5cd2be e062dd3d
7f007f00
ffffffff
f1782300
7d10017f
42823845 fe6b5c43 c159bcc3
42a1f83f 955b3dc2
0x1ecd0b40 3de23dc2
8898f83f ef5e3dc2
ce471940 9a9f16c0
401b1a40 811217c0
92471940 77a216c0
c0674f3e 8a09163f
1c78233f f2c31b3f
fbaedabe 20fa093e
0000ff00
ffffffff
e8752a00
7c1a007f
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 100,
attributes: vec![
VertexAttribute {
data_type: DataType::Position,
data_size: 12,
},
VertexAttribute {
data_type: DataType::TexCoord0,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord1,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord2,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord3,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord4,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord5,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord6,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord7,
data_size: 8,
},
VertexAttribute {
data_type: DataType::TexCoord8,
data_size: 8,
},
VertexAttribute {
data_type: DataType::Blend,
data_size: 4,
},
VertexAttribute {
data_type: DataType::VertexColor,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Normal,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Tangent,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::Position(vec![
vec3(2952.4521, 220.63208, -377.0984),
vec3(2952.141, 220.42184, -376.7012),
]),
AttributeData::TexCoord0(vec![
vec2(1.9810443, -47.392357),
vec2(1.9424212, -47.339436),
]),
AttributeData::TexCoord1(vec![
vec2(2.2272549, -47.516212),
vec2(2.1843944, -47.470936),
]),
AttributeData::TexCoord2(vec![
vec2(1.9810443, -47.392357),
vec2(1.9421549, -47.34271),
]),
AttributeData::TexCoord3(vec![
vec2(2.3970675, -2.3563137),
vec2(2.3950076, -2.3534913),
]),
AttributeData::TexCoord4(vec![
vec2(2.4101992, -2.362919),
vec2(2.4079132, -2.3605044),
]),
AttributeData::TexCoord5(vec![
vec2(2.3970675, -2.3563137),
vec2(2.3949933, -2.353666),
]),
AttributeData::TexCoord6(vec![
vec2(0.17541784, 0.5742469),
vec2(0.20254421, 0.58608305),
]),
AttributeData::TexCoord7(vec![
vec2(0.6337629, 0.4448461),
vec2(0.6385515, 0.60845864),
]),
AttributeData::TexCoord8(vec![
vec2(-0.41085857, 0.108098745),
vec2(-0.42711625, 0.13474321),
]),
AttributeData::Blend(vec![
vec4(0.49803922, 0.0, 0.49803922, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
]),
AttributeData::VertexColor(vec![vec4(1.0, 1.0, 1.0, 1.0), vec4(1.0, 1.0, 1.0, 1.0)]),
AttributeData::Normal(vec![
vec4(-0.11811024, 0.9448819, 0.27559054, 0.0),
vec4(-0.18897638, 0.9212598, 0.33070865, 0.0),
]),
AttributeData::Tangent(vec![
vec4(0.984252, 0.12598425, 0.007874016, 1.0),
vec4(0.97637796, 0.20472442, 0.0, 1.0),
]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Little
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Little).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn morph_blend_target_vertices() {
let data = hex!(
2828333d 9bdcae3f e9c508bd
e7415a01
2828333d 9bdcae3f e9c508bd
7dbe11ff
52c6463d 8cddaf3f 56bf0abd
ed4c5901
52c6463d 8cddaf3f 56bf0abd
7bc516ff
);
let target = xc3_lib::vertex::MorphTarget {
data_offset: 0,
vertex_count: 2,
vertex_size: 32,
flags: xc3_lib::vertex::MorphTargetFlags::new(0u16, true, false, false, 0u8.into()),
};
let attributes = vec![
AttributeData::Position2(vec![
vec3(0.043739468, 1.3661073, -0.033391867),
vec3(0.048528977, 1.3739486, -0.03387388),
]),
AttributeData::Normal4(vec![
vec4(0.8117647, -0.49019605, -0.29411763, -0.99215686),
vec4(0.85882354, -0.40392154, -0.30196077, -0.99215686),
]),
AttributeData::OldPosition(vec![
vec3(0.043739468, 1.3661073, -0.033391867),
vec3(0.048528977, 1.3739486, -0.03387388),
]),
AttributeData::Tangent2(vec![
vec4(-0.019607842, 0.4901961, -0.8666667, 1.0),
vec4(-0.035294116, 0.54509807, -0.827451, 1.0),
]),
];
assert_eq!(attributes, read_morph_blend_target(&target, &data).unwrap());
let mut writer = Cursor::new(Vec::new());
let new_target = write_morph_blend_target(&mut writer, &attributes).unwrap();
assert_eq!(new_target, target);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn read_morph_default_buffer_vertices() {
let data = hex!(
8c54023d bc27ac3f 72dd93bc
00000000
d6237601
a0a90cff
00000000
04000000
2b28153d 27e7ac3f 06d8b2bc
00000000
dd2c6b01
0x8ead0aff
00000000
06000000
);
let target = xc3_lib::vertex::MorphTarget {
data_offset: 0,
vertex_count: 2,
vertex_size: 32,
flags: xc3_lib::vertex::MorphTargetFlags::new(0u16, false, true, false, 0u8.into()),
};
assert_eq!(
vec![
MorphTargetVertex {
position_delta: vec3(0.03181891, 1.3449626, -0.01804993),
normal: vec4(0.6784314, -0.7254902, -0.0745098, -0.99215686),
tangent: vec4(0.254902, 0.32549024, -0.90588236, 1.0),
vertex_index: 4
},
MorphTargetVertex {
position_delta: vec3(0.03641526, 1.3508042, -0.021831524),
normal: vec4(0.73333335, -0.654902, -0.1607843, -0.99215686),
tangent: vec4(0.11372554, 0.35686278, -0.92156863, 1.0),
vertex_index: 6
}
],
read_morph_buffer_target(&target, &data).unwrap()
);
}
#[test]
fn read_morph_param_buffer_vertices() {
let data = hex!(
f0462abb 00f0a4bb 80b31a39
00000000
f770a800
6ad3ddff
00000000
d8000000
c03fd9ba 005245bb 002027b7
00000000
f66fa900
90fd83ff
00000000
d9000000
);
let target = xc3_lib::vertex::MorphTarget {
data_offset: 0,
vertex_count: 2,
vertex_size: 32,
flags: xc3_lib::vertex::MorphTargetFlags::new(0u16, false, false, true, 0u8.into()),
};
let actual_target = read_morph_target(&target, &data, 0).unwrap();
assert_eq!(
MorphTarget {
morph_controller_index: 0,
position_deltas: vec![
vec3(-0.0025982223, -0.005033493, 0.00014753453),
vec3(-0.0016574785, -0.003010869, -9.961426e-6)
],
normals: vec![
vec4(0.9372549, -0.12156862, 0.3176471, -1.0),
vec4(0.92941177, -0.12941176, 0.32549024, -1.0)
],
tangents: vec![
vec4(-0.16862744, 0.654902, 0.73333335, 1.0),
vec4(0.12941182, 0.9843137, 0.027451038, 1.0)
],
vertex_indices: vec![216, 217]
},
actual_target
);
let mut writer = Cursor::new(Vec::new());
let new_target = write_morph_param_target(&mut writer, &actual_target).unwrap();
assert_eq!(new_target, target);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn unk_buffer_vertices_size24() {
let data = hex!(
7db21bbd 32f3ce3f 9d9ddbbd
ff000000
02000000
c6e69300
2c1bdbbc 3dd3ce3f a664e2bd
ff000000
02000000
e1ed8700
);
let descriptor = xc3_lib::vertex::UnkBufferDescriptor {
unk1: 1,
unk2: 1,
count: 2,
offset: 0,
unk5: 0,
start_index: 0,
};
let buffer = read_unk_buffer(&descriptor, 0, &data).unwrap();
assert_eq!(
UnkBuffer {
unk2: 1,
attributes: vec![
AttributeData::Position(vec![
vec3(-0.038012017, 1.6167967, -0.10723422),
vec3(-0.026746355, 1.6158215, -0.110543534)
]),
AttributeData::VertexColor(vec![
vec4(1.0, 0.0, 0.0, 0.0),
vec4(1.0, 0.0, 0.0, 0.0)
]),
AttributeData::VertexColor(vec![
vec4(0.007843138, 0.0, 0.0, 0.0),
vec4(0.007843138, 0.0, 0.0, 0.0)
]),
AttributeData::VertexColor(vec![
vec4(0.7764706, 0.9019608, 0.5764706, 0.0),
vec4(0.88235295, 0.92941177, 0.5294118, 0.0)
])
]
},
buffer
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_unk_buffer(&mut writer, &buffer, 0, 0).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn unk_buffer_vertices_size16() {
let data = hex!(
80d31dbd 4565813c 573535be
b2fe9d00
94d1dbbc 5c83693c de9e37be
fa820000
);
let descriptor = xc3_lib::vertex::UnkBufferDescriptor {
unk1: 0,
unk2: 0,
count: 2,
offset: 0,
unk5: 0,
start_index: 0,
};
let buffer = read_unk_buffer(&descriptor, 0, &data).unwrap();
assert_eq!(
UnkBuffer {
unk2: 0,
attributes: vec![
AttributeData::Position(vec![
vec3(-0.03853178, 0.01579536, -0.17696129),
vec3(-0.026833333, 0.01425251, -0.17931697)
]),
AttributeData::VertexColor(vec![
vec4(0.69803923, 0.99607843, 0.6156863, 0.0),
vec4(0.98039216, 0.50980395, 0.0, 0.0)
])
]
},
buffer
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_unk_buffer(&mut writer, &buffer, 0, 0).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn read_outline_buffer_vertices_size4() {
let data = hex!(
5d2f1f00
5d2f1f0c
);
let descriptor = xc3_lib::vertex::OutlineBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 4,
unk: 0,
};
assert_eq!(
vec![AttributeData::VertexColor(vec![
vec4(0.3647059, 0.18431373, 0.12156863, 0.0),
vec4(0.3647059, 0.18431373, 0.12156863, 0.047058824)
])],
read_outline_buffer(&descriptor, &data).unwrap()
);
}
#[test]
fn read_outline_buffer_vertices_size8() {
let data = hex!(
7adffc00
4b37294c
7adffc00
4b37294c
);
let descriptor = xc3_lib::vertex::OutlineBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 8,
unk: 0,
};
assert_eq!(
vec![
AttributeData::Normal(vec![
vec4(0.96062994, -0.25984251, -0.031496063, 0.0),
vec4(0.96062994, -0.25984251, -0.031496063, 0.0)
]),
AttributeData::VertexColor(vec![
vec4(0.29411766, 0.21568628, 0.16078432, 0.29803923),
vec4(0.29411766, 0.21568628, 0.16078432, 0.29803923)
])
],
read_outline_buffer(&descriptor, &data).unwrap()
);
}
#[test]
fn vertex_buffer_vertices_legacy() {
let data = hex!(
bf2339ac be3e416c 3c94aa00
002a0000
3e11f7c1 3f255b32
ffffffff
e5a45300
e457577f
bf247df6 bdf6f646 3c6e6dc0
002a0000
0x3ec5d2b6 3f2253e6
ffffffff
9a004a00
007f007f
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 36,
attributes: vec![
VertexAttribute {
data_type: DataType::Position,
data_size: 12,
},
VertexAttribute {
data_type: DataType::WeightIndex,
data_size: 4,
},
VertexAttribute {
data_type: DataType::TexCoord0,
data_size: 8,
},
VertexAttribute {
data_type: DataType::VertexColor,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Normal,
data_size: 4,
},
VertexAttribute {
data_type: DataType::Tangent,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::Position(vec![
vec3(-0.63759875, -0.18579644, 0.018147469),
vec3(-0.642547, -0.12058692, 0.014552534),
]),
AttributeData::WeightIndex(vec![[42, 0], [42, 0]]),
AttributeData::TexCoord0(vec![
vec2(0.14254667, 0.6459228),
vec2(0.38637322, 0.6340927),
]),
AttributeData::VertexColor(vec![vec4(1.0, 1.0, 1.0, 1.0), vec4(1.0, 1.0, 1.0, 1.0)]),
AttributeData::Normal(vec![
vec4(-0.21259843, -0.72440946, 0.6535433, 0.0),
vec4(-0.8031496, 0.0, 0.5826772, 0.0),
]),
AttributeData::Tangent(vec![
vec4(-0.22047244, 0.68503934, 0.68503934, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Big
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Big).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn weight_buffer_vertices_legacy() {
let data = hex!(
3f800000 00000000 00000000
00000000
3f800000 00000000 00000000
01000000
);
let descriptor = VertexBufferDescriptor {
data_offset: 0,
vertex_count: 2,
vertex_size: 16,
attributes: vec![
VertexAttribute {
data_type: DataType::SkinWeights2,
data_size: 12,
},
VertexAttribute {
data_type: DataType::BoneIndices2,
data_size: 4,
},
],
unk1: 0,
unk2: 0,
unk3: 0,
};
let attributes = vec![
AttributeData::SkinWeights2(vec![vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0)]),
AttributeData::BoneIndices2(vec![[0, 0, 0, 0], [1, 0, 0, 0]]),
];
assert_eq!(
attributes,
read_attributes(
descriptor.data_offset as u64,
descriptor.vertex_count,
descriptor.vertex_size,
&descriptor.attributes,
&data,
Endian::Big
)
.unwrap()
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_vertex_buffer(&mut writer, &attributes, Endian::Big).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
#[test]
fn vertex_buffer_indices_legacy() {
let data = hex!(00000001 00020002);
let descriptor = IndexBufferDescriptor {
data_offset: 0,
index_count: 4,
primitive_type: xc3_lib::vertex::PrimitiveType::TriangleList,
index_format: xc3_lib::vertex::IndexFormat::Uint16,
unk3: 0,
unk4: 0,
};
let indices = read_index_buffer(&descriptor, &data, Endian::Big).unwrap();
assert_eq!(
IndexBuffer {
indices: vec![0, 1, 2, 2],
primitive_type: PrimitiveType::TriangleList
},
indices
);
let mut writer = Cursor::new(Vec::new());
let new_descriptor = write_index_buffer(&mut writer, &indices, Endian::Big).unwrap();
assert_eq!(new_descriptor, descriptor);
assert_hex_eq!(data, writer.into_inner());
}
}