BMD/BDL (File Format): Difference between revisions
Super Hackio (talk | contribs) (Add TEX1 data) |
Super Hackio (talk | contribs) (→Index Buffer Formats: Note about the matrix types) |
||
| (11 intermediate revisions by the same user not shown) | |||
| Line 45: | Line 45: | ||
| 0x0A || Int16 || Padding (0xFFFF) | | 0x0A || Int16 || Padding (0xFFFF) | ||
|- | |- | ||
| 0x0C || Int32 || Matrix Group Count | | 0x0C || Int32 || Matrix Group Data Count<br/>Referring to the Matrix Group Data in [[#SHP1_-_Shape_Information|SHP1]]<br/><small>Stored in a variable by the game, but not used to dictate the actual number of Matrix Groups.</small> | ||
|- | |- | ||
| 0x10 || Int32 || Vertex Count<br/>Always equals the number of entries in the "POSITION" [[# | | 0x10 || Int32 || Vertex Count<br/>Always equals the number of entries in the [[#VTX1_-_Vertex_Data|VTX1]] "POSITION" [[#Vertex%20Buffer%20Formats|data buffer]]<br/><small>Stored in a variable by the game, but never used for anything. The game reads Vertex Data directly from the [[#Vertex%20Buffer%20Formats|data buffer]]s without bounds checking.</small> | ||
|- | |- | ||
| 0x14 || Int32 || Relative offset to the [[#Scene%20Hierarchy|Scene Hierarchy]] | | 0x14 || Int32 || Relative offset to the [[#Scene%20Hierarchy|Scene Hierarchy]] | ||
| Line 152: | Line 152: | ||
|} | |} | ||
=== Buffer Formats === | === Vertex Buffer Formats === | ||
Each of the Buffers in this section have a format descriptor. This allows different types of data storage, allowing for smaller files. | Each of the Buffers in this section have a format descriptor. This allows different types of data storage, allowing for smaller files. | ||
| Line 270: | Line 270: | ||
| 0x0A || Int16 || Padding (0xFFFF) | | 0x0A || Int16 || Padding (0xFFFF) | ||
|- | |- | ||
| 0x0C || Int32 || | | 0x0C || Int32 || Envelope Size Table offset | ||
|- | |- | ||
| 0x10 || Int32 || | | 0x10 || Int32 || Envelope Joint Index Table offset | ||
|- | |- | ||
| 0x14 || Int32 || | | 0x14 || Int32 || Envelope Weight Table offset | ||
|- | |- | ||
| 0x18 || Int32 || | | 0x18 || Int32 || Inverse Bind Matrix Table offset | ||
|} | |} | ||
This section is padded to the nearest 32. | |||
=== Envelope Size Table === | |||
This table simply consists of an array of UInt8, where each value dictates how many entries are in a given Envelope. | |||
=== Envelope Joint Index Table === | |||
This table consists of the actual [[#JNT1_-_Joint_Hierarchy|JNT1]] index values. Each value is a UInt16, and are ordered directly based on the Envelope Size Table. (The first Envelope's values come directly after each other, then the second envelope's values, etc.) | |||
=== Envelope Weight Table === | |||
This table consists of joint weighting values which determine how much of an effect the given Joint Index has on this envelope. Each value is a Float, and are ordered directly based on the Envelope Size Table. (The first Envelope's values come directly after each other, then the second envelope's values, etc.) | |||
This table ends with padding to the nearest 4th byte so the Floats that come after are aligned correctly. | |||
=== Inverse Bind Matrix Table === | |||
This table contains Inverse Bind Matrices for each joint in [[#JNT1_-_Joint_Hierarchy|JNT1]]. The EVP1 section itself contains no size of this table, but the number of Inverse Bind Matrices is always equal to the amount of Joints in the model.<br/>Each entry is a 3 Row 4 Column Matrix (Mtx34), and is stored as 3 Vector4 values. | |||
== DRW1 - Skinning Assignments == | == DRW1 - Skinning Assignments == | ||
This section contains a map which redirects skinning information to the correct location: either JNT1 (Single Matrix) or EVP1 (Multi-Matrix)<br/>The Destination Type list and Destination ID list must be equal in length. | |||
{| class="wikitable" | |||
|- | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x00 || String || Always '''DRW1''' (ASCII) | |||
|- | |||
| 0x04 || Int32 || The total size of this chunk | |||
|- | |||
| 0x08 || Int16 || The number of Entries stored. | |||
|- | |||
| 0x0A || Int16 || Padding (0xFFFF) | |||
|- | |||
| 0x0C || Int32 || Destination Type list offset | |||
|- | |||
| 0x10 || Int32 || Destination ID list offset | |||
|} | |||
=== Destination Type List === | |||
This is an array of bytes that dictates the destination type. It is ordered so that all the '''Joint'''s come first, and all the '''Envelope'''s second. | |||
{| class="wikitable" | |||
|- | |||
! Value !! Name !! Description | |||
|- | |||
| 0x00 || Joint || Redirects to a Joint within [[#JNT1_-_Joint_Hierarchy|JNT1]] | |||
|- | |||
| 0x01 || Envelope || Redirects to an Envelope in [[#EVP1_-_Skinning_Envelopes|EVP1]] | |||
|} | |||
It's worth noting that Nintendo's development tools had a bug in it where all the '''Envelope''' entries were duplicated. Some JSystem games do not care about this, but certain games try to explicitly ignore this data, and therefore require the wasteful duplicate data. | |||
=== Destination ID List === | |||
This is an array of UInt16 that simply dictate an index into the provided section. | |||
DRW1 ends with padding to the nearest 32. | |||
== JNT1 - Joint Hierarchy == | == JNT1 - Joint Hierarchy == | ||
| Line 299: | Line 348: | ||
| 0x10 || Int32 || Remap table offset | | 0x10 || Int32 || Remap table offset | ||
|- | |- | ||
| 0x14 || Int32 || Name table offset | | 0x14 || Int32 || Name table offset<br/><small>0 if not present.</small> | ||
|} | |} | ||
| Line 364: | Line 413: | ||
| 0x0A || Int16 || Padding (0xFFFF) | | 0x0A || Int16 || Padding (0xFFFF) | ||
|- | |- | ||
| 0x0C || Int32 || Shape Data | | 0x0C || Int32 || Shape Init Data offset | ||
|- | |- | ||
| 0x10 || Int32 || Remap table offset | | 0x10 || Int32 || Remap table offset | ||
|- | |- | ||
| 0x14 || Int32 || Name table offset<br/> | | 0x14 || Int32 || Name table offset<br/><small>0 if not present, which it usually isn't.</small> | ||
|- | |- | ||
| 0x18 || Int32 || | | 0x18 || Int32 || Vertex Index Attribute table offset | ||
|- | |- | ||
| 0x1C || Int32 || | | 0x1C || Int32 || Weight Index table offset | ||
|- | |- | ||
| 0x20 || Int32 || | | 0x20 || Int32 || Primitive Data offset | ||
|- | |- | ||
| 0x24 || Int32 || | | 0x24 || Int32 || Matrix Group Data offset (entry count must equal the entry count of the GXDisplayList group table) | ||
|- | |- | ||
| 0x28 || Int32 || | | 0x28 || Int32 || Matrix Group Definition table offset | ||
|} | |} | ||
=== Shape Data === | === Shape Data === | ||
Each shape denotes a Matrix Calculation type, which is used for Billboarding, as well as multi-joint skinning.<br/>A shape also | Each shape denotes a Matrix Calculation type, which is used for Billboarding, as well as multi-joint skinning.<br/>A shape also hosts a collection of Matrix Groups. If the geometry needs to be weighted to more than 10 [[#DRW1_-_Skinning_Assignments|DRW1]] matrices, more than one Matrix Group is needed.<br/>A single Matrix Group can have multiple Primitives in it, but these Primitives must share the same set of [[#DRW1_-_Skinning_Assignments|DRW1]] matrices. | ||
<small>TODO: Figure out what happens in the event that more than 10 are needed. Is it just straight up not allowed?</small> | <small>TODO: Figure out what happens in the event that more than 10 are needed (such as one vertex being weighted to 11 bones). Is it just straight up not allowed?</small> | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 402: | Line 451: | ||
| 0x01 || UInt8 || Padding (0xFF) | | 0x01 || UInt8 || Padding (0xFF) | ||
|- | |- | ||
| 0x02 || Int16 || | | 0x02 || Int16 || Matrix Group count | ||
|- | |- | ||
| 0x04 || Int16 || Vertex Attribute offset | | 0x04 || Int16 || Vertex Index Attribute offset<br/>Relative to the Vertex Index Attribute Table offset defined in SHP1 | ||
|- | |- | ||
| 0x06 || Int16 || | | 0x06 || Int16 || Matrix Group Data index | ||
|- | |- | ||
| 0x08 || Int16 || First | | 0x08 || Int16 || First Matrix Group Definition index | ||
|- | |- | ||
| 0x0A || Int16 || Padding (0xFFFF) | | 0x0A || Int16 || Padding (0xFFFF) | ||
| Line 426: | Line 475: | ||
| 0x24 || Float || Bounding Box Maximum Z | | 0x24 || Float || Bounding Box Maximum Z | ||
|} | |} | ||
=== Vertex Index Attribute Table === | |||
{| class="wikitable" | |||
|- | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x00 || Int32 || An [[#Index%20Buffer%20Formats|index buffer]] attribute target | |||
|- | |||
| 0x04 || Int32 || An [[#Index%20Buffer%20Formats|index buffer]] type | |||
|} | |||
==== Index Buffer Formats ==== | |||
Each of the Buffers in this section have a format descriptor. This allows different types of data storage, allowing for smaller files. | |||
These attributes line up with either a direct value that is stored in the shape, or an index into a [[#VTX1_-_Vertex_Data|VTX1]] [[#Vertex%20Buffer%20Formats|data buffer]].<br/>Only one indexer can exist for each attribute. | |||
The Attribute Target tells the game what this index buffer is used for. | |||
{| class="wikitable" | |||
|+ Attribute Targets | |||
|- | |||
! Value !! Name !! Description | |||
|- | |||
| 0x00000000 || POSITION_MATRIX || This is a direct list of indices into the Matrix Group's Weight collection. Required for Shapes that use Multiple Joint skinning. | |||
|- | |||
| 0x00000001 || TEXTURE_0_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000002 || TEXTURE_1_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000003 || TEXTURE_2_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000004 || TEXTURE_3_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000005 || TEXTURE_4_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000006 || TEXTURE_5_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000007 || TEXTURE_6_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000008 || TEXTURE_7_MATRIX || ''Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it.'' | |||
|- | |||
| 0x00000009 || POSITION || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "POSITION" buffer. Required. | |||
|- | |||
| 0x0000000A || NORMAL || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "NORMAL" buffer | |||
|- | |||
| 0x0000000B || COLOR_0 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "COLOR_0" buffer | |||
|- | |||
| 0x0000000C || COLOR_1 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "COLOR_1" buffer | |||
|- | |||
| 0x0000000D || TEXCOORD_0 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_0" buffer | |||
|- | |||
| 0x0000000E || TEXCOORD_1 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_1" buffer | |||
|- | |||
| 0x0000000F || TEXCOORD_2 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_2" buffer | |||
|- | |||
| 0x00000010 || TEXCOORD_3 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_3" buffer | |||
|- | |||
| 0x00000011 || TEXCOORD_4 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_4" buffer | |||
|- | |||
| 0x00000012 || TEXCOORD_5 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_5" buffer | |||
|- | |||
| 0x00000013 || TEXCOORD_6 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_6" buffer | |||
|- | |||
| 0x00000014 || TEXCOORD_7 || This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TEXCOORD_7" buffer | |||
|- | |||
| 0x00000019 || TANGENT || ''This indexes into the [[#VTX1_-_Vertex_Data|VTX1]] "TANGENT" buffer, but we don't yet know what that buffer does.'' | |||
|- | |||
| 0x000000FF || List End || A buffer using this target will end the descriptor list. | |||
|} | |||
The Data Type of an Index Buffer determines the size of each Index. Direct Buffers can only be 1 byte per value. | |||
{| class="wikitable" | |||
|+ Data Types | |||
|- | |||
! Value !! Name !! Description | |||
|- | |||
| 0x00000000 || None || Only used for the List End | |||
|- | |||
| 0x00000001 || Direct || 1 byte per value. Only used for data that is not stored within [[#VTX1_-_Vertex_Data|VTX1]]<br/>Needs to be divided by 3 for some reason... | |||
|- | |||
| 0x00000002 || UInt8 || 1 byte per value (0 to 255). Indexes a buffer in [[#VTX1_-_Vertex_Data|VTX1]]. | |||
|- | |||
| 0x00000003 || UInt16 || 2 bytes per value (0 to 65535). Indexes a buffer in [[#VTX1_-_Vertex_Data|VTX1]]. | |||
|} | |||
=== Primitive Data === | |||
This chunk contains raw primitives. These are what hold the index buffers.<br/>The Index Buffers are ordered the same way the Index Buffer Attributes are ordered. For example, POSITION (UInt16), NORMAL (UInt16), COLOR_0 (UInt8), TEXCOORD_0 (UInt16), would result in the following byte structure: '''PPPP NNNN C0 TXC0'''<br/>This repeats for each vertex in the primitive. | |||
{| class="wikitable" | |||
|- | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x00 || Uint8 || Primitive Type | |||
* 0x80 = Individual Quads | |||
* 0x90 = Individual Triangles | |||
* 0x98 = Triangle Strip | |||
* 0xA0 = Triangle Fan | |||
* 0xA8 = Individual Lines | |||
* 0xB0 = Line Strip | |||
* 0xB8 = Individual Points | |||
<small>TODO: Figure out if Quads, Lines, Line Strips, and Points work.</small><br/><small>TODO: Figure out if "Quad Strips" exist at 0x88.</small> | |||
|- | |||
| 0x04 || Int16 || Vertex Count | |||
|} | |||
Each Matrix Group's primitive collection MUST be padded to the nearest 32. The game dies if you don't. | |||
=== Matrix Group Definition Table === | |||
This table defines a list of Matrix Groups for each Shape. A Shape can have multiple Matrix Groups, but only stores the index of the first one. This means that all the Matrix Groups for a given Shape must come directly one after another.<br/>It contains only two values: The size of all Primitive Data within this group, and an offset to the first Primitive (relative to the Primitive Data offset defined in SHP1). | |||
{| class="wikitable" | |||
|- | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x00 || Int32 || Primitive Data size | |||
|- | |||
| 0x04 || Int32 || First Primitive offset | |||
|} | |||
=== Matrix Group Data Table === | |||
This table contains definitions of sub-segments inside the Weight Index Table. How these values are used differs based on the shape matrix calculation type, but official files generally set both usages up correctly regardless. | |||
{| class="wikitable" | |||
|- | |||
! Offset !! Type !! Description | |||
|- | |||
| 0x00 || Int16 || [[#DRW1_-_Skinning_Assignments|DRW1]] Matrix ID<br/>This is only used with Single Joint shape matrix calculation. | |||
|- | |||
| 0x02 || Int16 || Matrix Count<br/>This is only used with Multiple Joint shape matrix calculation. | |||
|- | |||
| 0x04 || Int32 || Starting Weight Index<br/>This is only used with Multiple Joint shape matrix calculation. | |||
|} | |||
=== Weight Index Table === | |||
This table contains several indices into [[#DRW1_-_Skinning_Assignments|DRW1]].<br/>Each index is a Signed 16 value.<br/>The values may also contain 0xFFFF, which indicates that the last used index should be used instead.<br/>Matrix Groups can select a segment of these indices (up to 10) for use. A Matrix Group must not start with 0xFFFF. | |||
== MAT2 - Material List (Old) == | == MAT2 - Material List (Old) == | ||
| Line 431: | Line 619: | ||
== MAT3 - Material List == | == MAT3 - Material List == | ||
Contains information for '''Mat'''erials to fill the rendered geometry with. | |||
== MDL3 - Display List == | == MDL3 - Display List == | ||
Contains raw material GX opcodes to speed up rendering. | |||
== TEX1 - Texture Data == | == TEX1 - Texture Data == | ||
Latest revision as of 18:44, 1 June 2026
| This page is in progress and may contain incomplete information or editor's notes. |
|---|
BMD (Binary Model) and BDL (Binary Display List) are two 3D model formats used in Super Mario Galaxy and Super Mario Galaxy 2, among a few other Wii and GameCube games, such as Super Mario Sunshine.
These two formats hold the same data, except BDL contains an additional set of data which is used for faster rendering speeds.
Super Mario Galaxy & Super Mario Galaxy 2 contain J3DLoader versions 21 and 26.
File Header
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | The magic that tells the game which format this file is. (ASCII) J3D2bmd2 for BMD (J3DLoader V21) J3D2bmd3 for BMD (J3DLoader v26) J3D2bdl4 for BDL (J3DLoader v26) |
| 0x08 | Int32 | The total size of the file |
| 0x0C | Int32 | The number of chunks present in this file. 8 for BMD 9 for BDL |
Subversion Header
This part of the header is not used, and can be anything.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | A 4 character value representing the Subversion. Usually SVR3 for SMG/2. |
| 0x04 | Byte[12] | Twelve bytes of padding. Usually these are 0xFF |
INF1 - Scene Information
The Information section hosts a hierarchy of Joints, Materials, and Shapes, which are used to render the model.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always INF1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | J3DLoader Flags |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | Matrix Group Data Count Referring to the Matrix Group Data in SHP1 Stored in a variable by the game, but not used to dictate the actual number of Matrix Groups. |
| 0x10 | Int32 | Vertex Count Always equals the number of entries in the VTX1 "POSITION" data buffer Stored in a variable by the game, but never used for anything. The game reads Vertex Data directly from the data buffers without bounds checking. |
| 0x14 | Int32 | Relative offset to the Scene Hierarchy |
J3DLoader Flags
The J3DLoader contains several flags which affect how models are handled. There are more than what are shown here, however, those must be applied by the game itself in the code.
| Value | Description |
|---|---|
| 0b0000000000000000 | No flags applied (TODO: Check if this defaults to basic matrix calculation) |
| 0b0000000000000001 | Use Autodesk Softimage matrix calculation |
| 0b0000000000000010 | Use Autodesk Maya matrix calculation |
| 0b0000000000000100 | Use Basic matrix calculation |
| 0b0000000000001000 | UNKNOWN |
| 0b0000000000010000 | UseImmidiateMtx (needs research) |
| 0b0000000000100000 | UsePostTexMtx (needs research) |
| 0b0000000001000000 | UNKNOWN |
| 0b0000000010000000 | UNKNOWN |
| 0b0000000100000000 | NoMatrixTransform (needs research) |
| 0b0000001000000000 | UNKNOWN |
| 0b0000010000000000 | UNKNOWN |
| 0b0000100000000000 | UNKNOWN |
| 0b0001000000000000 | UNKNOWN |
| 0b0010000000000000 | DoBdlMaterialCalc |
| 0b0100000000000000 | NoBdlMaterialPatch - If set, skips the call to J3DModelLoader::readPatchedMaterial. |
| 0b1000000000000000 | UNKNOWN |
For all BMD files, the game always includes load flags Material_PE_Full, Material_UseIndirect, UseUniqueMaterials, and UNKNOWN_21.
For all BDL files, the game always includes load flags Material_UseIndirect, and UseUniqueMaterials.
> Note: Changing the load flags by changing the code doesn't seem to do much.
Scene Hierarchy
Each model is constructed using a Scene Hierarchy. This simply determines the order of bones (in turn, defining the parent/child relations), material assignments, and Shape assignments.
Each node consists of the following:
| Offset | Type | Description |
|---|---|---|
| 0x00 | UInt16 | Node Type. 0x00 = Marks the end of the node list 0x01 = Creates a new child of the previous node 0x02 = Finishes the child node and returns ownership to the previous node 0x10 = Joint Assignment node. "Data" holds the Joint ID (index into JNT1) 0x11 = Material assignment. "Data" holds the Material ID (index into MAT3) 0x12 = Shape Assignment. "Data" holds the Shape ID (index into SHP1) |
| 0x02 | UInt16 | A Data value. Used by some nodes to index into other chunks. Usage depends on the node type. |
The node list is padded to the nearest 32.
VTX1 - Vertex Data
The Vertex Data section contains the actual number values used for the properties of the geometry. It also includes the information on how the values are formatted.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always VTX1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int32 | Buffer Format list offset |
| 0x0C | Int32 | "POSITION" data buffer offset 0 if not used. |
| 0x10 | Int32 | "NORMAL" data buffer offset 0 if not used. |
| 0x14 | Int32 | "TANGENT" data buffer offset 0 if not used. |
| 0x18 | Int32 | "COLOR_0" data buffer offset 0 if not used. |
| 0x1C | Int32 | "COLOR_1" data buffer offset 0 if not used. |
| 0x20 | Int32 | "TEXCOORD_0" data buffer offset 0 if not used. |
| 0x24 | Int32 | "TEXCOORD_1" data buffer offset 0 if not used. |
| 0x28 | Int32 | "TEXCOORD_2" data buffer offset 0 if not used. |
| 0x2C | Int32 | "TEXCOORD_3" data buffer offset 0 if not used. |
| 0x30 | Int32 | "TEXCOORD_4" data buffer offset 0 if not used. |
| 0x34 | Int32 | "TEXCOORD_5" data buffer offset 0 if not used. |
| 0x38 | Int32 | "TEXCOORD_6" data buffer offset 0 if not used. |
| 0x3C | Int32 | "TEXCOORD_7" data buffer offset 0 if not used. |
Vertex Buffer Formats
Each of the Buffers in this section have a format descriptor. This allows different types of data storage, allowing for smaller files.
Each format descriptor contains an Attribute Target, a Component Count, a Data Type, and a Mantissa shifter (to allow floats to be stored as smaller datatypes compared to FLOAT).
Only one descriptor can exist for each attribute.
The Attribute Target tells the game what this buffer is used for.
| Value | Name | Description |
|---|---|---|
| 0x00000009 | POSITION | This buffer represents the positions of vertices in the model. Basically required for all visual models. |
| 0x0000000A | NORMAL | This buffer represents vertex normals, used for the engine's vertex shading. Not needed if no material requires it. |
| 0x0000000B | COLOR_0 | This buffer is available for Vertex Colors. |
| 0x0000000C | COLOR_1 | This buffer is available for Vertex Colors. |
| 0x0000000D | TEXCOORD_0 | This buffer is available for Texture Coordinates. |
| 0x0000000E | TEXCOORD_1 | This buffer is available for Texture Coordinates. |
| 0x0000000F | TEXCOORD_2 | This buffer is available for Texture Coordinates. |
| 0x00000010 | TEXCOORD_3 | This buffer is available for Texture Coordinates. |
| 0x00000011 | TEXCOORD_4 | This buffer is available for Texture Coordinates. |
| 0x00000012 | TEXCOORD_5 | This buffer is available for Texture Coordinates. |
| 0x00000013 | TEXCOORD_6 | This buffer is available for Texture Coordinates. |
| 0x00000014 | TEXCOORD_7 | This buffer is available for Texture Coordinates. |
| 0x00000019 | TANGENT | Currently unknown exactly what goes in here. |
| 0x000000FF | List End | A buffer using this target will end the descriptor list. |
The component count tells the game how many parts of the component that there are. Note that the values are considered based on the attribute target they're assigned to.
| Value | Name | Description |
|---|---|---|
| 0x00000000 | POSITION_XY | Unknown |
| 0x00000001 | POSITION_XYZ | Represents a position in 3D space with 3 values: X, Y, and Z. |
| 0x00000000 | NORMAL_XYZ | Represents a normal vector in 3D space with 3 values: X, Y, and Z. |
| 0x00000001 | NORMAL_NBT | Unknown |
| 0x00000002 | NORMAL_NBT3 | Unknown |
| 0x00000000 | COLOR_RGB | Represents a color value with only Red, Green, and Blue values. |
| 0x00000001 | COLOR_RGBA | Represents a color value with Red, Green, Blue, and Alpha values. |
| 0x00000000 | TEXCOORD_S | Unknown |
| 0x00000001 | TEXCOORD_ST | Represents a UV texture coordinate with 2 values: X and Y. |
The Data Type tells the game how many bytes each value is, in the sense that an Int32 is 4 bytes, a Int16 is 2 bytes, a Float is 4 bytes, etc.
Note that the Color values have a different set of options.
| Value | Name | Description |
|---|---|---|
| 0x00000000 | UInt8 | 1 byte per value (0 to 255) |
| 0x00000001 | Int8 | 1 byte per value (-128 to 127) |
| 0x00000002 | UInt16 | 2 bytes per value (0 to 65535) |
| 0x00000003 | Int16 | 2 bytes per value (-32,768 to 32,767) |
| 0x00000004 | Float | 4 bytes per value |
| 0x00000000 | RGB565 | 2 bytes per color |
| 0x00000001 | RGB8 | 3 bytes per color |
| 0x00000002 | RGBX8 | 3 bytes per color |
| 0x00000003 | RGBA4 | 2 bytes per color |
| 0x00000004 | RGBA6 | 4 bytes per color |
| 0x00000005 | RGBA8 | 4 bytes per color |
> Note: Both games require colors to be 4 bytes wide at all times, so changing from RGBA8 is worthless.
EVP1 - Skinning Envelopes
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always EVP1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | The number of Envelopes stored. |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | Envelope Size Table offset |
| 0x10 | Int32 | Envelope Joint Index Table offset |
| 0x14 | Int32 | Envelope Weight Table offset |
| 0x18 | Int32 | Inverse Bind Matrix Table offset |
This section is padded to the nearest 32.
Envelope Size Table
This table simply consists of an array of UInt8, where each value dictates how many entries are in a given Envelope.
Envelope Joint Index Table
This table consists of the actual JNT1 index values. Each value is a UInt16, and are ordered directly based on the Envelope Size Table. (The first Envelope's values come directly after each other, then the second envelope's values, etc.)
Envelope Weight Table
This table consists of joint weighting values which determine how much of an effect the given Joint Index has on this envelope. Each value is a Float, and are ordered directly based on the Envelope Size Table. (The first Envelope's values come directly after each other, then the second envelope's values, etc.)
This table ends with padding to the nearest 4th byte so the Floats that come after are aligned correctly.
Inverse Bind Matrix Table
This table contains Inverse Bind Matrices for each joint in JNT1. The EVP1 section itself contains no size of this table, but the number of Inverse Bind Matrices is always equal to the amount of Joints in the model.
Each entry is a 3 Row 4 Column Matrix (Mtx34), and is stored as 3 Vector4 values.
DRW1 - Skinning Assignments
This section contains a map which redirects skinning information to the correct location: either JNT1 (Single Matrix) or EVP1 (Multi-Matrix)
The Destination Type list and Destination ID list must be equal in length.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always DRW1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | The number of Entries stored. |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | Destination Type list offset |
| 0x10 | Int32 | Destination ID list offset |
Destination Type List
This is an array of bytes that dictates the destination type. It is ordered so that all the Joints come first, and all the Envelopes second.
| Value | Name | Description |
|---|---|---|
| 0x00 | Joint | Redirects to a Joint within JNT1 |
| 0x01 | Envelope | Redirects to an Envelope in EVP1 |
It's worth noting that Nintendo's development tools had a bug in it where all the Envelope entries were duplicated. Some JSystem games do not care about this, but certain games try to explicitly ignore this data, and therefore require the wasteful duplicate data.
Destination ID List
This is an array of UInt16 that simply dictate an index into the provided section.
DRW1 ends with padding to the nearest 32.
JNT1 - Joint Hierarchy
The Joint Hierarchy section defines the rigging skeleton of a model. There can only be one skeleton per model.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always JNT1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | The number of Joints stored. |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | Joint Data table offset |
| 0x10 | Int32 | Remap table offset |
| 0x14 | Int32 | Name table offset 0 if not present. |
Joint Data
Each joint includes positional data and a bounding box which is used by the game for determining rendering range automatically.
| Offset | Type | Description |
|---|---|---|
| 0x00 | Int16 | Matrix Type |
| 0x01 | UInt8 | Parent Scale inheritance 0 = Follow parent joint's scale 1 = Ignore parent joint's scale |
| 0x02 | UInt8 | Padding (0xFF) |
| 0x04 | Float | Scale X |
| 0x08 | Float | Scale Y |
| 0x0C | Float | Scale Z |
| 0x10 | Int16 | Rotation X |
| 0x12 | Int16 | Rotation Y |
| 0x14 | Int16 | Rotation Z |
| 0x16 | Int16 | Padding (0xFFFF) |
| 0x18 | Float | Translation X |
| 0x1C | Float | Translation Y |
| 0x20 | Float | Translation Z |
| 0x24 | Float | Bounding Sphere Should encapsulate all vertices that the joint affects |
| 0x28 | Float | Bounding Box Minimum X |
| 0x2C | Float | Bounding Box Minimum Y |
| 0x30 | Float | Bounding Box Minimum Z |
| 0x34 | Float | Bounding Box Maximum X |
| 0x38 | Float | Bounding Box Maximum Y |
| 0x3C | Float | Bounding Box Maximum Z |
Rotation is stored between -0x7FFF and 0x7FFF, which ranges -180 degrees and 180 degrees.
SHP1 - Shape Information
The Shape Information section determines how to use the data in the VTX1 section to create shapes.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always SHP1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | The number of Shapes stored. |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | Shape Init Data offset |
| 0x10 | Int32 | Remap table offset |
| 0x14 | Int32 | Name table offset 0 if not present, which it usually isn't. |
| 0x18 | Int32 | Vertex Index Attribute table offset |
| 0x1C | Int32 | Weight Index table offset |
| 0x20 | Int32 | Primitive Data offset |
| 0x24 | Int32 | Matrix Group Data offset (entry count must equal the entry count of the GXDisplayList group table) |
| 0x28 | Int32 | Matrix Group Definition table offset |
Shape Data
Each shape denotes a Matrix Calculation type, which is used for Billboarding, as well as multi-joint skinning.
A shape also hosts a collection of Matrix Groups. If the geometry needs to be weighted to more than 10 DRW1 matrices, more than one Matrix Group is needed.
A single Matrix Group can have multiple Primitives in it, but these Primitives must share the same set of DRW1 matrices.
TODO: Figure out what happens in the event that more than 10 are needed (such as one vertex being weighted to 11 bones). Is it just straight up not allowed?
| Offset | Type | Description |
|---|---|---|
| 0x00 | UInt8 | Matrix Type
|
| 0x01 | UInt8 | Padding (0xFF) |
| 0x02 | Int16 | Matrix Group count |
| 0x04 | Int16 | Vertex Index Attribute offset Relative to the Vertex Index Attribute Table offset defined in SHP1 |
| 0x06 | Int16 | Matrix Group Data index |
| 0x08 | Int16 | First Matrix Group Definition index |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Float | Bounding Sphere |
| 0x10 | Float | Bounding Box Minimum X |
| 0x14 | Float | Bounding Box Minimum Y |
| 0x18 | Float | Bounding Box Minimum Z |
| 0x1C | Float | Bounding Box Maximum X |
| 0x20 | Float | Bounding Box Maximum Y |
| 0x24 | Float | Bounding Box Maximum Z |
Vertex Index Attribute Table
| Offset | Type | Description |
|---|---|---|
| 0x00 | Int32 | An index buffer attribute target |
| 0x04 | Int32 | An index buffer type |
Index Buffer Formats
Each of the Buffers in this section have a format descriptor. This allows different types of data storage, allowing for smaller files.
These attributes line up with either a direct value that is stored in the shape, or an index into a VTX1 data buffer.
Only one indexer can exist for each attribute.
The Attribute Target tells the game what this index buffer is used for.
| Value | Name | Description |
|---|---|---|
| 0x00000000 | POSITION_MATRIX | This is a direct list of indices into the Matrix Group's Weight collection. Required for Shapes that use Multiple Joint skinning. |
| 0x00000001 | TEXTURE_0_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000002 | TEXTURE_1_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000003 | TEXTURE_2_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000004 | TEXTURE_3_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000005 | TEXTURE_4_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000006 | TEXTURE_5_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000007 | TEXTURE_6_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000008 | TEXTURE_7_MATRIX | Uses the Direct type. Seems to break models that use it, and no official models in either galaxy game use it. |
| 0x00000009 | POSITION | This indexes into the VTX1 "POSITION" buffer. Required. |
| 0x0000000A | NORMAL | This indexes into the VTX1 "NORMAL" buffer |
| 0x0000000B | COLOR_0 | This indexes into the VTX1 "COLOR_0" buffer |
| 0x0000000C | COLOR_1 | This indexes into the VTX1 "COLOR_1" buffer |
| 0x0000000D | TEXCOORD_0 | This indexes into the VTX1 "TEXCOORD_0" buffer |
| 0x0000000E | TEXCOORD_1 | This indexes into the VTX1 "TEXCOORD_1" buffer |
| 0x0000000F | TEXCOORD_2 | This indexes into the VTX1 "TEXCOORD_2" buffer |
| 0x00000010 | TEXCOORD_3 | This indexes into the VTX1 "TEXCOORD_3" buffer |
| 0x00000011 | TEXCOORD_4 | This indexes into the VTX1 "TEXCOORD_4" buffer |
| 0x00000012 | TEXCOORD_5 | This indexes into the VTX1 "TEXCOORD_5" buffer |
| 0x00000013 | TEXCOORD_6 | This indexes into the VTX1 "TEXCOORD_6" buffer |
| 0x00000014 | TEXCOORD_7 | This indexes into the VTX1 "TEXCOORD_7" buffer |
| 0x00000019 | TANGENT | This indexes into the VTX1 "TANGENT" buffer, but we don't yet know what that buffer does. |
| 0x000000FF | List End | A buffer using this target will end the descriptor list. |
The Data Type of an Index Buffer determines the size of each Index. Direct Buffers can only be 1 byte per value.
| Value | Name | Description |
|---|---|---|
| 0x00000000 | None | Only used for the List End |
| 0x00000001 | Direct | 1 byte per value. Only used for data that is not stored within VTX1 Needs to be divided by 3 for some reason... |
| 0x00000002 | UInt8 | 1 byte per value (0 to 255). Indexes a buffer in VTX1. |
| 0x00000003 | UInt16 | 2 bytes per value (0 to 65535). Indexes a buffer in VTX1. |
Primitive Data
This chunk contains raw primitives. These are what hold the index buffers.
The Index Buffers are ordered the same way the Index Buffer Attributes are ordered. For example, POSITION (UInt16), NORMAL (UInt16), COLOR_0 (UInt8), TEXCOORD_0 (UInt16), would result in the following byte structure: PPPP NNNN C0 TXC0
This repeats for each vertex in the primitive.
| Offset | Type | Description |
|---|---|---|
| 0x00 | Uint8 | Primitive Type
TODO: Figure out if Quads, Lines, Line Strips, and Points work. |
| 0x04 | Int16 | Vertex Count |
Each Matrix Group's primitive collection MUST be padded to the nearest 32. The game dies if you don't.
Matrix Group Definition Table
This table defines a list of Matrix Groups for each Shape. A Shape can have multiple Matrix Groups, but only stores the index of the first one. This means that all the Matrix Groups for a given Shape must come directly one after another.
It contains only two values: The size of all Primitive Data within this group, and an offset to the first Primitive (relative to the Primitive Data offset defined in SHP1).
| Offset | Type | Description |
|---|---|---|
| 0x00 | Int32 | Primitive Data size |
| 0x04 | Int32 | First Primitive offset |
Matrix Group Data Table
This table contains definitions of sub-segments inside the Weight Index Table. How these values are used differs based on the shape matrix calculation type, but official files generally set both usages up correctly regardless.
| Offset | Type | Description |
|---|---|---|
| 0x00 | Int16 | DRW1 Matrix ID This is only used with Single Joint shape matrix calculation. |
| 0x02 | Int16 | Matrix Count This is only used with Multiple Joint shape matrix calculation. |
| 0x04 | Int32 | Starting Weight Index This is only used with Multiple Joint shape matrix calculation. |
Weight Index Table
This table contains several indices into DRW1.
Each index is a Signed 16 value.
The values may also contain 0xFFFF, which indicates that the last used index should be used instead.
Matrix Groups can select a segment of these indices (up to 10) for use. A Matrix Group must not start with 0xFFFF.
MAT2 - Material List (Old)
Technically supported by the game, but not known to be used by any model.
MAT3 - Material List
Contains information for Materials to fill the rendered geometry with.
MDL3 - Display List
Contains raw material GX opcodes to speed up rendering.
TEX1 - Texture Data
The Texture Information section holds several embedded BTI files that MAT3 can reference by index for texture assignment.
| Offset | Type | Description |
|---|---|---|
| 0x00 | String | Always TEX1 (ASCII) |
| 0x04 | Int32 | The total size of this chunk |
| 0x08 | Int16 | The number of BTI files stored. |
| 0x0A | Int16 | Padding (0xFFFF) |
| 0x0C | Int32 | BTI Header Table Offset |
| 0x10 | Int32 | Name table offset |
The texture data locations are defined by the BTI headers (which themselves are located directly one after another at the Header Table)
This structure allows multiple textures that have the same visual data to have their headers point to just one block of visual data. In other words, two identical textures can be "compressed" into one while still having their own properties (wrapping, filtering, etc.)