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