xc3_lib/
vertex.rs

1//! Vertex and geometry data for models and map models.
2//!
3//! # Overview
4//! A [VertexData] file stores model geometry in a combined [buffer](struct.VertexData.html#structfield.buffer).
5//! The remaining fields describe the data stored in the buffer like vertices or morph targets.
6//!
7//! Each [MeshV112](crate::mxmd::MeshV112) draw call references a [VertexBufferDescriptor] and [IndexBufferDescriptor].
8//! Vertex buffers except the weights buffer have an associated [VertexBufferExtInfo]
9//! for assigning additional data like outline buffers or morph targets.
10//!
11//! The weights buffer just contains [DataType::SkinWeights] and [DataType::BoneIndices].
12//! This buffer is shared between all vertex buffers with
13//! each buffer selecting weight buffer "vertices" using [DataType::WeightIndex]
14//! and additional indexing information defined in [Weights].
15//! See [xc3_model](https://docs.rs/xc3_model) for the complete indexing implementation.
16//!
17//! Some vertex buffers have optional morph targets assigned in [VertexMorphs].
18//! Morph targets define a default target for the neutral pose as well as additional
19//! targets applied on top of the default target.
20//! Morph targets define attributes not present in the vertex buffer and have a
21//! final attribute value defined as `default + target_delta * weight`
22//! where `target_delta` is defined sparsely using a list of deltas and vertex indices.
23//!
24//! # Attribute Layout
25//! The sections of the byte buffer for each descriptor contain each attribute for each vertex in order.
26//! This interleaved or "array of structs" layout is cache friendly when accessing each attribute for each vertex
27//! like in the vertex shaders in game.
28//! ```text
29//! position 0
30//! normal 0
31//! position 1
32//! normal 1
33//! ...
34//! ```
35//! Applications tend to work better with a "struct of arrays" approach where
36//! dedicated arrays store the values for a single attribute for all items.
37//! This approach is cache friendly when accessing the same attribute for all vertices
38//! and allows for easily adding and removing attributes.
39//! This is the approach used by [xc3_model](https://docs.rs/xc3_model).
40//! ```text
41//! position 0
42//! position 1
43//! ...
44//! ```
45//! ```text
46//! normal 0
47//! normal 1
48//! ...
49//! ```
50use crate::{
51    parse_count16_offset32, parse_count32_offset32, parse_offset32_count32, parse_opt_ptr32,
52    parse_ptr32, xc3_write_binwrite_impl,
53};
54use bilge::prelude::*;
55use binrw::{args, binread, BinRead, BinWrite};
56use xc3_write::{Xc3Write, Xc3WriteOffsets};
57
58/// Vertex and vertex index buffer data used by a [ModelV112](crate::mxmd::ModelV112).
59#[binread]
60#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
61#[derive(Debug, Xc3Write, PartialEq, Clone)]
62#[br(stream = r)]
63#[xc3(base_offset)]
64pub struct VertexData {
65    #[br(temp, try_calc = r.stream_position())]
66    base_offset: u64,
67
68    // TODO: Sometimes 80 and sometimes 84?
69    #[br(parse_with = parse_offset32_count32)]
70    #[br(args { offset: base_offset, inner: base_offset })]
71    #[xc3(offset_count(u32, u32))]
72    pub vertex_buffers: Vec<VertexBufferDescriptor>,
73
74    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
75    #[xc3(offset_count(u32, u32))]
76    pub index_buffers: Vec<IndexBufferDescriptor>,
77
78    // padding?
79    pub unk0: u32,
80    pub unk1: u32,
81    pub unk2: u32,
82
83    #[br(parse_with = parse_ptr32)]
84    #[br(args { offset: base_offset, inner: args! { count: buffer_info_count(&vertex_buffers) }})]
85    #[xc3(offset(u32))]
86    pub vertex_buffer_info: Vec<VertexBufferExtInfo>,
87
88    // 332 bytes of data?
89    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
90    #[xc3(offset_count(u32, u32))]
91    pub outline_buffers: Vec<OutlineBufferDescriptor>,
92
93    #[br(parse_with = parse_opt_ptr32)]
94    #[br(args { offset: base_offset, inner: base_offset })]
95    #[xc3(offset(u32))]
96    pub vertex_morphs: Option<VertexMorphs>,
97
98    /// The data buffer containing all the geometry data aligned to 4096.
99    /// Buffers are typically packed in this buffer in the following order:
100    /// [vertex_buffers](#structfield.vertex_buffers),
101    /// [outline_buffers](#structfield.outline_buffers),
102    /// [index_buffers](#structfield.index_buffers),
103    /// [vertex_morphs](#structfield.vertex_morphs),
104    /// [unk7](#structfield.unk7).
105    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
106    #[xc3(count_offset(u32, u32), align(4096))]
107    pub buffer: Vec<u8>,
108
109    // TODO: particles?
110    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
111    #[xc3(offset(u32))]
112    pub unk_data: Option<UnkData>,
113
114    #[br(parse_with = parse_opt_ptr32)]
115    #[br(args { offset: base_offset, inner: base_offset })]
116    #[xc3(offset(u32))]
117    pub weights: Option<Weights>,
118
119    #[br(parse_with = parse_opt_ptr32, offset = base_offset)]
120    #[xc3(offset(u32))]
121    pub unk7: Option<Unk>,
122
123    // TODO: padding?
124    pub unks: [u32; 5],
125}
126
127#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
128#[derive(Debug, BinRead, Xc3Write, PartialEq, Eq, Clone)]
129#[br(import_raw(base_offset: u64))]
130pub struct VertexBufferDescriptor {
131    /// The offset into [buffer](struct.VertexData.html#structfield.buffer).
132    pub data_offset: u32,
133    pub vertex_count: u32,
134    /// The size or stride of the vertex in bytes.
135    pub vertex_size: u32,
136
137    /// A tightly packed list of attributes for the data for this buffer.
138    #[br(parse_with = parse_offset32_count32, offset = base_offset)]
139    #[xc3(offset_count(u32, u32))]
140    pub attributes: Vec<VertexAttribute>,
141
142    pub unk1: u32,
143    pub unk2: u32,
144    pub unk3: u32,
145}
146
147/// A single attribute in a [VertexBufferDescriptor] like positions or normals.
148///
149/// Attributes are tightly packed, so the relative offset is
150/// the sum of previous attribute sizes.
151#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
152#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Eq, Clone, Copy)]
153pub struct VertexAttribute {
154    pub data_type: DataType,
155    /// The size in bytes of [data_type](#structfield.data_type).
156    pub data_size: u16,
157}
158
159// Format is taken from RenderDoc debugging.
160// Names are taken from shader attribute metadata.
161/// The data type, usage, and component count for a [VertexAttribute].
162#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
163#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone, Copy)]
164#[brw(repr(u16))]
165pub enum DataType {
166    /// Float32x3 "vPos" in shaders.
167    Position = 0,
168    /// Float32x3 "fWeight" in shaders.
169    /// The fourth weight component is calculated as `w = 1.0 - x - y - z`.
170    /// Only used for Xenoblade X.
171    SkinWeights2 = 1,
172    /// Uint8x4 bone indices for up to 4 bone infuences.
173    /// Only used for Xenoblade X.
174    BoneIndices2 = 2,
175    /// Uint16x2 "nWgtIdx" in shaders.
176    ///
177    /// The index in the first component selects elements in the precomputed skinning matrices in the vertex shader.
178    /// See [Weights] for details.
179    WeightIndex = 3,
180    /// Uint16x2 "nWgtIdx" in shaders.
181    ///
182    /// Used for some stage and object models.
183    WeightIndex2 = 4,
184    /// Float32x2 "vTex0" in shaders.
185    TexCoord0 = 5,
186    /// Float32x2 "vTex1" in shaders.
187    TexCoord1 = 6,
188    /// Float32x2 "vTex2" in shaders.
189    TexCoord2 = 7,
190    /// Float32x2 "vTex3" in shaders.
191    TexCoord3 = 8,
192    /// Float32x2 "vTex4" in shaders.
193    TexCoord4 = 9,
194    /// Float32x2 "vTex5" in shaders.
195    TexCoord5 = 10,
196    /// Float32x2 "vTex6" in shaders.
197    TexCoord6 = 11,
198    /// Float32x2 "vTex7" in shaders.
199    TexCoord7 = 12,
200    /// Float32x2 "vTex8" in shaders.
201    TexCoord8 = 13,
202    /// Unorm8x4 "vBlend" in shaders.
203    Blend = 14,
204    /// Float32x3 ??? in shaders.
205    Unk15 = 15,
206    Unk16 = 16, // TODO: 2 snorm8x4?
207    /// Unorm8x4 "vColor" in shaders.
208    VertexColor = 17,
209    /// Float32x3 ??? in shaders.
210    Unk18 = 18,
211    /// ??? "vGmCal1" in shaders.
212    Unk24 = 24,
213    /// ??? "vGmCal2" in shaders.
214    Unk25 = 25,
215    /// ??? "vGmCal3" in shaders.
216    Unk26 = 26,
217    /// Snorm8x4 "vNormal" in shaders.
218    Normal = 28,
219    /// Snorm8x4 "vTan" in shaders with bitangent sign in the fourth component.
220    Tangent = 29,
221    /// ??? "fGmAl" in shaders.
222    Unk30 = 30,
223    Unk31 = 31, // TODO: xcx only?
224    /// Snorm8x4 "vNormal" in shaders.
225    Normal2 = 32,
226    /// Snorm8x4 "vValInf" in shaders.
227    // TODO: related to normals?
228    ValInf = 33,
229    /// Snorm8x4 "vNormal" in shaders.
230    Normal3 = 34,
231    /// Unorm8x4 "vColor" in shaders.
232    VertexColor3 = 35,
233    /// Float32x3 "vPos" in shaders.
234    Position2 = 36,
235    /// Unorm8x4 "vNormal" in shaders.
236    /// Component values are in the range `[0.0, 1.0]` instead of `[-1.0, 1.0]`.
237    /// Calculate the actual vector as `v * 2.0 -1.0`.
238    Normal4 = 37,
239    /// Float32x3 "vOldPos" in shaders.
240    OldPosition = 39,
241    /// Unorm8x4 "vTan" in shaders.
242    /// Component values are in the range `[0.0, 1.0]` instead of `[-1.0, 1.0]`.
243    /// Calculate the actual vector as `v * 2.0 -1.0`.
244    Tangent2 = 40,
245    /// Unorm16x4 skin weights for up to 4 bone influences.
246    SkinWeights = 41,
247    /// Uint8x4 bone indices for up to 4 bone infuences in the [Skinning](crate::mxmd::Skinning) in the [Mxmd](crate::mxmd::Mxmd).
248    BoneIndices = 42,
249    /// ??? "vFlow" in shaders.
250    Flow = 52,
251}
252
253// TODO: Is this data always u16?
254#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
255#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Eq, Clone)]
256pub struct IndexBufferDescriptor {
257    /// The offset into [buffer](struct.VertexData.html#structfield.buffer).
258    pub data_offset: u32,
259    pub index_count: u32,
260    pub primitive_type: PrimitiveType,
261    pub index_format: IndexFormat,
262    // TODO: padding?
263    pub unk3: u32,
264    pub unk4: u32,
265}
266
267#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
268#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone, Copy)]
269#[brw(repr(u16))]
270pub enum PrimitiveType {
271    TriangleList = 0,
272    QuadList = 1,
273    TriangleStrip = 2,
274    /// TODO: GL_TRIANGLES_ADJACENCY helps with geometry shaders?
275    TriangleListAdjacency = 3,
276}
277
278#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
279#[derive(Debug, BinRead, BinWrite, PartialEq, Eq, Clone, Copy)]
280#[brw(repr(u16))]
281pub enum IndexFormat {
282    Uint16 = 0,
283    Uint32 = 1,
284}
285
286/// Vertex animation data often called "vertex morphs", "shape keys", or "blend shapes".
287#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
288#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
289#[br(import_raw(base_offset: u64))]
290pub struct VertexMorphs {
291    #[br(parse_with = parse_count32_offset32)]
292    #[br(args { offset: base_offset, inner: base_offset })]
293    #[xc3(count_offset(u32, u32))]
294    pub descriptors: Vec<MorphDescriptor>,
295
296    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
297    #[xc3(count_offset(u32, u32))]
298    pub targets: Vec<MorphTarget>,
299
300    // TODO: padding?
301    pub unks: [u32; 4],
302}
303
304/// Morph targets assigned to a [VertexBufferDescriptor].
305///
306/// Each buffer has a blend target and default target followed by param targets.
307/// This means the actual target count is two more than
308/// the length of [param_indices](#structfield.param_indices).
309#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
310#[derive(Debug, BinRead, Xc3Write, PartialEq, Clone)]
311#[br(import_raw(base_offset: u64))]
312pub struct MorphDescriptor {
313    pub vertex_buffer_index: u32,
314    pub target_start_index: u32,
315
316    /// Indices into [controllers](../mxmd/struct.MorphControllers.html#structfield.controllers).
317    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
318    #[xc3(count_offset(u32, u32))]
319    pub param_indices: Vec<u16>,
320
321    // flags?
322    // TODO: 259 and 260 have twice as many targets (ch01031011)?
323    // TODO: 3 also adds extra "vertices" with all 0's for data?
324    pub unk2: u32, // 3, 4, 259, 260
325}
326
327// TODO: vertex attributes for vertex animation data?
328/// A set of target vertex values similar to a keyframe in traditional animations.
329#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
330#[derive(Debug, BinRead, BinWrite, PartialEq, Clone)]
331pub struct MorphTarget {
332    /// Relative to [data_base_offset](struct.ModelData.html#structfield.data_base_offset)
333    pub data_offset: u32,
334    pub vertex_count: u32,
335    pub vertex_size: u32,
336
337    pub flags: MorphTargetFlags,
338}
339
340#[bitsize(32)]
341#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
342#[derive(DebugBits, FromBits, BinRead, BinWrite, Clone, Copy, PartialEq)]
343#[br(map = u32::into)]
344#[bw(map = |&x| u32::from(x))]
345pub struct MorphTargetFlags {
346    pub unk1: u16, // always 0?
347    /// The base values for each vertex.
348    pub blend_target_buffer: bool,
349    /// The base values for each of the vertices modified by any of the param targets.
350    pub default_buffer: bool,
351    /// Values for vertices modified by a param target.
352    pub param_buffer: bool,
353    pub unk5: u13, // always 0?
354}
355
356/// Information used for precomputing skinning matrices
357/// based on a mesh's level of detail (LOD) and [RenderPassType](crate::mxmd::RenderPassType).
358#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
359#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
360#[br(import_raw(base_offset: u64))]
361pub struct Weights {
362    /// Selected based on the associated [WeightLod] for a [MeshV112](crate::mxmd::MeshV112).
363    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
364    #[xc3(count_offset(u32, u32))]
365    pub groups: Vec<WeightGroup>,
366
367    /// The descriptor in [vertex_buffers](struct.VertexData.html#structfield.vertex_buffer) containing the weight data.
368    /// This is typically the last element.
369    pub vertex_buffer_index: u16,
370
371    /// Selected based on the LOD of the [MeshV112](crate::mxmd::MeshV112).
372    #[br(parse_with = parse_count16_offset32, offset = base_offset)]
373    #[xc3(count_offset(u16, u32))]
374    pub weight_lods: Vec<WeightLod>,
375
376    // TODO: always 0 for xc2?
377    pub unk4: u32, // 0, 1
378
379    // TODO: padding?
380    pub unks: [u32; 4],
381}
382
383/// A range of elements in the weights buffer.
384/// Each element in the weights buffer is part of at least one [WeightGroup].
385///
386/// The [input_start_index](#structfield.input_start_index) and [count](#structfield.count)
387/// select a range of [DataType::BoneIndices] and [DataType::SkinWeights] from the weights buffer.
388/// The bone matrices for each bone index multiplied by the weights are written to the output buffer starting at [output_start_index](#structfield.output_start_index).
389/// This precomputed skinning buffer is used to select transforms in the vertex shader using [DataType::WeightIndex].
390#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
391#[derive(Debug, PartialEq, Clone, BinRead, Xc3Write, Xc3WriteOffsets)]
392pub struct WeightGroup {
393    /// Index into the skinning buffer used in the vertex shader with bone transforms multiplied by skin weights.
394    /// These weighted bone matrices are selected using [DataType::WeightIndex].
395    pub output_start_index: u32,
396    /// Start of the elements in the weights buffer at [vertex_buffer_index](struct.Weights.html#structfield.vertex_buffer_index).
397    pub input_start_index: u32,
398    /// Number of elements in the weights buffer.
399    pub count: u32,
400    pub unks: [u32; 4], // TODO: always 0?
401    /// Index into [group_indices_plus_one](struct.WeightLod.html#structfield.group_indices_plus_one)
402    /// pointing back to this group.
403    pub lod_group_index: u8,
404    /// Index into [weight_lods](struct.Weights.html#structfield.weight_lods)
405    /// for the [WeightLod] that references this [WeightGroup].
406    pub lod_index: u8,
407    /// The max number of non-zero bone influences per vertex
408    /// for the range of elements in the weights buffer.
409    pub max_influences: u8,
410    pub unk4: u8,
411    pub unks2: [u32; 2],
412}
413
414// TODO: The material's pass index indexes into this?
415// TODO: Figure out by finding files with no more groups than pass ids?
416// TODO: Is this actually the pass from mesh.flags2?
417/// References to [WeightGroup] for each of the [RenderPassType](crate::mxmd::RenderPassType).
418#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
419#[derive(Debug, PartialEq, Clone, BinRead, Xc3Write, Xc3WriteOffsets)]
420pub struct WeightLod {
421    /// One plus the indices pointing back to [groups](struct.Weights.html#structfield.groups).
422    /// Unused entries use the value `0`.
423    ///
424    /// Each [MeshV112](crate::mxmd::MeshV112) indexes into this list using a hardcoded remapping
425    /// for the [RenderPassType](crate::mxmd::RenderPassType) of the assigned material.
426    // TODO: Document each entry.
427    pub group_indices_plus_one: [u16; 9],
428}
429
430#[binread]
431#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
432#[derive(Debug, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
433#[br(stream = r)]
434#[xc3(base_offset)]
435pub struct Unk {
436    #[br(temp, try_calc = r.stream_position())]
437    base_offset: u64,
438
439    #[br(parse_with = parse_count32_offset32, offset = base_offset)]
440    #[xc3(count_offset(u32, u32))]
441    pub buffers: Vec<UnkBufferDescriptor>,
442
443    // The length of the data in bytes.
444    pub data_length: u32,
445
446    /// The offset into [buffer](struct.VertexData.html#structfield.buffer).
447    pub data_offset: u32,
448
449    // TODO: Padding?
450    pub unks: [u32; 8],
451}
452
453#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
454#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
455pub struct UnkBufferDescriptor {
456    pub unk1: u16,
457    pub unk2: u16, // TODO: index?
458    pub count: u32,
459    pub offset: u32,
460    pub unk5: u32,
461    pub start_index: u32,
462}
463
464/// Extra data assigned to a non skin weights buffer.
465#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
466#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
467pub struct VertexBufferExtInfo {
468    pub flags: VertexBufferExtInfoFlags,
469    // TODO: Extra attributes for outline meshes?
470    pub outline_buffer_index: u16,
471    /// Identical to [target_start_index](struct.MorphDescriptor.html#structfield.target_start_index)
472    /// for the corresponding [MorphDescriptor].
473    pub morph_target_start_index: u16,
474    // TODO: Why is this off by 2?
475    /// Identical to [target_count](struct.MorphDescriptor.html#structfield.target_count) + 2
476    /// for the corresponding [MorphDescriptor].
477    pub morph_target_count: u16,
478    // TODO: padding?
479    pub unk: u32,
480}
481
482#[bitsize(16)]
483#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
484#[derive(DebugBits, FromBits, BinRead, BinWrite, Clone, Copy, PartialEq)]
485#[br(map = u16::into)]
486#[bw(map = |&x| u16::from(x))]
487pub struct VertexBufferExtInfoFlags {
488    pub has_outline_buffer: bool,
489    pub has_morph_targets: bool,
490    pub unk: u14,
491}
492
493#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
494#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
495pub struct OutlineBufferDescriptor {
496    /// The offset into [buffer](struct.VertexData.html#structfield.buffer).
497    pub data_offset: u32,
498    pub vertex_count: u32,
499    /// The size or stride of the vertex in bytes.
500    pub vertex_size: u32,
501    // TODO: padding?
502    pub unk: u32,
503}
504
505#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
506#[derive(Debug, BinRead, Xc3Write, Xc3WriteOffsets, PartialEq, Clone)]
507pub struct UnkData {
508    pub unk1: u32, // 1
509    pub vertex_data_offset: u32,
510    pub vertex_data_length: u32,
511    pub uniform_data_offset: u32,
512    pub uniform_data_length: u32,
513    pub vertex_count: u32,
514    pub vertex_size: u32,
515    // TODO: AABB for vertices?
516    pub unk: [f32; 6],
517}
518
519impl DataType {
520    pub fn size_in_bytes(&self) -> usize {
521        match self {
522            DataType::Position => 12,
523            DataType::SkinWeights2 => 12,
524            DataType::BoneIndices2 => 4,
525            DataType::WeightIndex => 4,
526            DataType::WeightIndex2 => 4,
527            DataType::TexCoord0 => 8,
528            DataType::TexCoord1 => 8,
529            DataType::TexCoord2 => 8,
530            DataType::TexCoord3 => 8,
531            DataType::TexCoord4 => 8,
532            DataType::TexCoord5 => 8,
533            DataType::TexCoord6 => 8,
534            DataType::TexCoord7 => 8,
535            DataType::TexCoord8 => 8,
536            DataType::Blend => 4,
537            DataType::Unk15 => 12,
538            DataType::Unk16 => 8,
539            DataType::VertexColor => 4,
540            DataType::Unk18 => 12,
541            DataType::Unk24 => 16,
542            DataType::Unk25 => 16,
543            DataType::Unk26 => 16,
544            DataType::Normal => 4,
545            DataType::Tangent => 4,
546            DataType::Unk30 => 4, // TODO: size?
547            DataType::Unk31 => 4,
548            DataType::Normal2 => 4,
549            DataType::ValInf => 4,
550            DataType::Normal3 => 4,
551            DataType::VertexColor3 => 4,
552            DataType::Position2 => 12,
553            DataType::Normal4 => 4,
554            DataType::OldPosition => 12,
555            DataType::Tangent2 => 4,
556            DataType::SkinWeights => 8,
557            DataType::BoneIndices => 4,
558            DataType::Flow => 2,
559        }
560    }
561}
562
563impl From<DataType> for VertexAttribute {
564    fn from(data_type: DataType) -> Self {
565        Self {
566            data_type,
567            data_size: data_type.size_in_bytes() as u16,
568        }
569    }
570}
571
572xc3_write_binwrite_impl!(
573    DataType,
574    PrimitiveType,
575    IndexFormat,
576    MorphTarget,
577    VertexBufferExtInfoFlags
578);
579
580fn buffer_info_count(vertex_buffers: &[VertexBufferDescriptor]) -> usize {
581    // TODO: Extra data for every buffer except the single weights buffer?
582    vertex_buffers
583        .iter()
584        .filter(|b| {
585            !b.attributes
586                .iter()
587                .any(|a| a.data_type == DataType::SkinWeights)
588        })
589        .count()
590}
591
592impl Xc3WriteOffsets for VertexDataOffsets<'_> {
593    type Args = ();
594
595    fn write_offsets<W: std::io::Write + std::io::Seek>(
596        &self,
597        writer: &mut W,
598        _base_offset: u64,
599        data_ptr: &mut u64,
600        endian: xc3_write::Endian,
601        _args: Self::Args,
602    ) -> xc3_write::Xc3Result<()> {
603        let base_offset = self.base_offset;
604
605        let vertex_buffers = self
606            .vertex_buffers
607            .write(writer, base_offset, data_ptr, endian)?;
608        self.index_buffers
609            .write(writer, base_offset, data_ptr, endian)?;
610        self.vertex_buffer_info
611            .write(writer, base_offset, data_ptr, endian)?;
612
613        // TODO: Do all empty lists use offset 0?
614        if !self.outline_buffers.data.is_empty() {
615            self.outline_buffers
616                .write(writer, base_offset, data_ptr, endian)?;
617        }
618
619        // The first attribute is aligned to 16.
620        // TODO: This doesn't always happen?
621        // *data_ptr = data_ptr.next_multiple_of(16);
622        for vertex_buffer in vertex_buffers.0 {
623            vertex_buffer
624                .attributes
625                .write(writer, base_offset, data_ptr, endian)?;
626        }
627
628        self.weights
629            .write_full(writer, base_offset, data_ptr, endian, ())?;
630
631        self.unk_data.write(writer, base_offset, data_ptr, endian)?;
632
633        if let Some(vertex_animation) =
634            self.vertex_morphs
635                .write(writer, base_offset, data_ptr, endian)?
636        {
637            let descriptors =
638                vertex_animation
639                    .descriptors
640                    .write(writer, base_offset, data_ptr, endian)?;
641            vertex_animation
642                .targets
643                .write(writer, base_offset, data_ptr, endian)?;
644
645            for descriptor in descriptors.0 {
646                descriptor
647                    .param_indices
648                    .write(writer, base_offset, data_ptr, endian)?;
649            }
650        }
651
652        self.unk7
653            .write_full(writer, base_offset, data_ptr, endian, ())?;
654
655        self.buffer.write(writer, base_offset, data_ptr, endian)?;
656
657        Ok(())
658    }
659}