BMD and BDL
BMD and BDL are the two formats that store model data in Nintendo games for the GameCube and the Wii. It first appeared in Luigi's Mansion, where it was used only for effects and jewels. BMD was then used as the only model format in Super Mario Sunshine, and The Wind Waker introduced the BDL format. The most recent game to use this model format is probably Super Mario Galaxy 2.
BMD and BDL are almost entirely the same. BDL is an extension of BMD with an additional section for material FIFO instructions called MDL3. MDL3 is likely used for making rendering more efficient.
- 1 Standard Padding String
- 2 File Header
- 3 Scenegraph (INF1)
- 4 Vertex Attributes (VTX1)
- 5 Skinning Envelopes (EVP1)
- 6 Weight Check Data (DRW1)
- 7 Joint Data (JNT1)
- 8 Geometry Data (SHP1)
- 9 Material Data (MAT3)
- 10 FIFO Data (MDL3)
- 11 Texture Data (TEX1)
Standard Padding String
All J3D formats, including models and animations, use a standard string for padding data to the nearest 8 or 32 bytes. The most complete copy of this string is shown below.
This is padding data to alig
The header contains basic data about the model, including its type, the size of the file, and the section count.
//0x20/32 bytes long /*0x00*/ char Magic; // 'J3D2' /*0x04*/ char ModelType; // 'bmd3' for BMD, 'bdl4' for BDL /*0x08*/ int FileSize; /*0x0C*/ int ChunkCount; // 8 for BMD, 9 for BDL /*0x10*/ char Svr3Magic; // 'SVR3' // From 0x14 to 0x20 is an array of padding bytes with the value 0xFF/-1.
The model format contains an extra chunk in the header called SVR3. The purpose of this chunk is unknown, since it is filled with 0xFF/-1 bytes. SVR1, possibly an earlier version of this chunk, is sometimes found in the animation formats.
The scenegraph is a flattened hierarchy of joints, materials, and geometry data. It is used to determine the joint hierarchy, as well as the order in which to render the different parts of the geometry. It consists of a header followed by the hierarchy data.
The header contains the size of the chunk, the number of geometry groups, and the number of vertexes in the mesh.
// 0x18/24 bytes long /*0x00*/ char Magic; // 'INF1' /*0x04*/ int Size; // Size of the chunk /*0x06*/ short Unknown1; /*0x08*/ short Padding; // 0xFFFF/-1 /*0x0C*/ int PacketCount; /*0x10*/ int VertexCount; //Number of vertexes in VTX1 section /*0x14*/ int HierarchyDataOffset; //Offset from start of INF1 Chunk
The hierarchy is made up of nodes that give the node type and an index for the associated data, if necessary. A node looks like this:
// 0x4/4 bytes long /*0x00*/ HierarchyDataTypes Type //See Hierarchy Node Types below /*0x02*/ short Index //Index into Joint, Material, or Shape/Batch table.
The INF1 chunk is padded to the nearest 8 bytes by the standard padding string after the last node, which has a node type of 0x00.
Hierarchy Node Types
There are 6 node types - 3 for manipulating the hierarchy (Finish, New Node, and End Node) and 3 for actual model data (Joint, Material, Shape/Batch).
|0x00||Finish||Ends the hierarchy|
|0x01||New Node||Creates a child of the previous node|
|0x02||End Node||Closes the current child node and returns to the parent node|
|0x10||Joint||Adds a joint to the hierarchy|
|0x11||Material||Adds a material to the hierarchy|
|0x12||Shape/Batch||Adds geometry data to the hierarchy|
Vertex Attributes (VTX1)
This chunk defines vertexes and the attributes associated with them. A vertex always includes position data, but it may also include UVs for texturing, normals for lighting, or vertex colors. All of this data is stored here.
The chunk has a header, which is followed by the definitions of each vertex attribute that is defined within the chunk. After these definitions are banks holding the data for each attribute.
// 0x40/64 bytes long /*0x00*/ char Magic; // 'VTX1' /*0x04*/ int Size; //Total bytes of chunk /*0x08*/ int VertexFormatOffset; //Offset to VertexFormats from VTX1 chunk start. /*0x0C*/ int VertexDataOffsets; //13 offsets to Vertex data. 0 if no data. Relative to VTX1Header start.
Each vertex attribute gives its type, a component count, and a data type. It may also include a decimal point value to use for compressed data types.
// 0x10/16 bytes long /*0x00*/ int AttributeType; //Defines what type of attribute this is /*0x04*/ int ComponentCount; //Meaning depends on DataType (see gx.h: "CompCount") /*0x08*/ int DataType; //What type of data is stored /*0x0C*/ byte DecimalPoint; //Number of mantissa bits for fixed point numbers (position of decimal point) /*0x0D*/ byte Padding; // All 3 are 0xFF/-1
This area of the chunk is padded to the nearest 32 bytes with the padding string. For example, if there are only 3 attributes in the list, there are 16 bytes of the padding string after the last attribute, filling the space where a 4th attribute would go.
|0x01||Tex Matrix 0||?|
|0x02||Tex Matrix 1||?|
|0x03||Tex Matrix 2||?|
|0x04||Tex Matrix 3||?|
|0x05||Tex Matrix 4||?|
|0x06||Tex Matrix 5||?|
|0x07||Tex Matrix 6||?|
|0x08||Tex Matrix 7||?|
|0x09||Position||Position of the vertex in space|
|0x0A||Normal||Normal for the vertex used for lighting calculations|
|0x0B||Color 0||Vertex color for color slot 0|
|0x0C||Color 1||Vertex color for color slot 1|
|0x0D||Tex 0||UV coordinates for texture 0|
|0x0E||Tex 1||UV coordinates for texture 1|
|0x0F||Tex 2||UV coordinates for texture 2|
|0x10||Tex 3||UV coordinates for texture 3|
|0x11||Tex 4||UV coordinates for texture 4|
|0x12||Tex 5||UV coordinates for texture 5|
|0x13||Tex 6||UV coordinates for texture 6|
|0x14||Tex 7||UV coordinates for texture 7|
|0x15||Position Matrix Array||?|
|0x16||Normal Matrix Array||?|
|0x17||Texture Matrix Array||?|
|0x18||Light Matrix Array||?|
|0xFF||Null Attribute||Terminate the attribute list. There are no more attributes.|
There are two versions of the list of data types. The first version is for primitive data types such as bytes and shorts, while the other version is for how vertex colors are stored. Thus, the attributes Color 0 and Color 1 use a value from the Colors data type table, while everything else uses the Primitives table.
|0x02||Unsigned Short (int16)|
|0x03||Signed Short (int16)|
The last portion of this chunk is made up of the data banks holding the data for the vertex attributes. Each bank is located at the corresponding offset in the chunk's header, and is stored in the data type specified in the vertex attribute definition. Each data bank is aligned to the nearest 8 bytes with the standard padding string.
Skinning Envelopes (EVP1)
This chunk defines data for weighted skinning, allowing multiple bones to move one set of geometry data. It is heavily reliant on multiple layers of indexes, which makes it complex and hard to follow.
It is made up of a header followed by an array of bytes that indicate the number of bones that influence a particular packet of geometry. After this is the index data array, which identifies those bones by their indexes. The influence that each bone has on the packet of geometry is scaled by a weight, and these weights are stored as floats in a bank that follows the index data array. Finally, each bone has a 3x4 inverse bind matrix, which transforms the vertexes from the bind pose to where they would be if their bones were positioned at (0,0,0).
// 0x1C/28 bytes long /*0x00*/ char Magic; // 'EVP1' /*0x04*/ int Size; /*0x08*/ short IndexCount; /*0x0A*/ short Padding; // 0xFFFF/-1 /*0x0C*/ int IndexCountOffset; /*0x10*/ int IndexDataOffset; /*0x14*/ int WeightOffset; /*0x18*/ int InverseMatrixOffset;
Index Count Array
This gives the number of bones that influence a particular packet of geometry data. In order to get the correct count for the desired packet, you must use the index obtained from the weight check chunk (DRW1) to access this array.
// 1 byte long /*0x00*/ byte BoneCount;
Index Data Array
The next step in getting the weights for a packet of geometry data is figuring out which bones interact with the packet. This is defined in this section of the chunk.
This is one of the most confusing parts. The index of the first relevant bone ID in this array isn't stored anywhere. In order to get the index of this ID, we must add up all of the bone counts in the Index Count Array above up to the count for the packet that we are looking up, and then divide that sum by 2. The result is the index of the first bone ID in the Index Data Array for the packet.
We divide the sum by 2 because the bone IDs are stored as shorts (int16). The sum is the total number of bone IDs in the array up to the first ID that we need; dividing by 2, the size of a short, gives us the index in the array of the ID that we need.
These weights are used to scale the influence of each bone that affects the packet of geometry data. The weights of each bone should add up to a sum of 1; otherwise, the vertexes do not move with the bones correctly.
The index of the first weight to use for the packet is given by the index of the bone IDs in the Index Data Array above. For example, if the indexes for the bone IDs in the Index Data Array are 3 and 4, that means that the packet of geometry data uses weights 3 and 4, as well.
// 4 bytes long /*0x00*/ float Weight;
Inverse Bind Matrices
This is the easiest part of the skinning process. It is simply a 3x4 matrix that is used to transform each vertex from the binding pose to a coordinate space where the bone is at (0,0,0). The indexes of the matrices to use are simply the bone IDs obtained from the Index Data Array.
The matrices are stored as an array of floats.
// 0x30/48 bytes long // To-do: figure out row/column layout of matrices
Weight Check Data (DRW1)
This chunk determines if a packet of geometry data needs to grab skinning data from the envelope chunk (EVP1), or if the packet just moves 1:1 with a single bone. It is made up of a header, followed by the partial weight array and the index array.
// 0x14/20 bytes long /*0x00*/ char Magic; // 'DRW1' /*0x04*/ int Size; /*0x08*/ short ElementCount; /*0x0A*/ short Padding; /*0x0C*/ int PartialWeightArrayOffset; /*0x10*/ int IndexArrayOffset;
Partial Weight Array
This is an array of boolean values, which the packet indexes into based on its position matrix index.
// 1 byte long /*0x00*/ bool IsPartiallyWeighted;
If the bool is false, that means that the packet moves 1:1 with a single bone without being weighted. The bone to use is indicated by the bone ID found in the Index Array at the same index as the bool - that is, the position matrix index of the packet.
If the bool is true, that means that the packet has weighted skinning data stored in the envelope chunk (EVP1). The index of the packet's bone count in EVP1's Index Count Array is indicated by the index in the Index Array at the same index as the bool - that is, the position matrix index of the packet.
// 2 bytes long /*0x00*/ short Index;
For every bool in the Partial Weight Array that is false, there is the ID of a bone to use for the packet's movement.
For every bool in the Partial Weight Array that is true, there is an index to a count in EVP1's Index Count Array.
Joint Data (JNT1)
This chunk defines joint data, including position, scale, rotation, and names. It has a header followed by the joint data and a string table.
// 0x18/24 bytes long /*0x00*/ char Magic; //'JNT1' /*0x04*/ int Size; //Total bytes of chunk /*0x08*/ short SectionCount; //How many joints there are /*0x0A*/ short Padding; /*0x0C*/ int JointEntryOffset; //Relative to JNT1Header start /*0x10*/ int StringIdTable; //There are SectionCount ushorts at this point. /*0x14*/ int StringTableOffset; //Names of Joints
This is the actual joint data. It includes scaling, rotation, and translation data for the joint, as well as a bounding sphere radius and bounding box information.
// 0x40/64 bytes long /*0x00*/ short Unknown1; //Always 0, 1 or 2 /*0x02*/ short Padding; //0x00FF in Mario, not always in Zelda /*0x04*/ float ScaleX; /*0x08*/ float ScaleY; /*0x0C*/ float ScaleZ; /*0x10*/ short RotationX; //-32768 = -180 deg, 32767 = 180 deg. float degrees = ((float)rotShort * 180.0f) / 32767.0f; /*0x12*/ short RotationY; /*0x14*/ short RotationZ; /*0x16*/ short Padding; /*0x18*/ float TranslationX; /*0x1C*/ float TranslationY; /*0x20*/ float TranslationZ; /*0x24*/ float BoundingSphereRadius; /*0x28*/ float BBoxMinX; /*0x2C*/ float BBoxMinY; /*0x30*/ float BBoxMinZ; /*0x34*/ float BBoxMaxX; /*0x38*/ float BBoxMaxY; /*0x3C*/ float BBoxMaxZ;
Geometry Data (SHP1)
SHP1 stores the geometry data for the model. The data is split into "batches", with each batch having multiple "packets." Each batch can have different vertex attributes active, which translates into the actual geometry data differing in vertex structure between batches.
The chunk begins with a header. This contains counts and offsets to each section of the data. After this is the batch data, defining batches with a bounding sphere radius, a bounding box, and an offset to the attributes that the batch uses. An index table, the attribute table, the matrix table, the geometry data, the matrix data, and the packet location data follow.
// 0x2C/44 bytes long /*0x00*/ int char Magic; // 'SHP1' /*0x04*/ int ChunkSize; // Total bytes of chunk /*0x08*/ short SectionCount; // How many batches there are /*0x0A*/ short Padding; // 0xFFFF/-1 /*0x0C*/ int BatchDataOffset; // Offset to Batch's (Relative to SHP1Header start) /*0x10*/ int IndexTableOffset; // Offset into IndexTable... seen in other things here too. /*0x14*/ int Unknown1; // Seems to usually be zero /*0x18*/ int BatchAttribsOffset; // Offset to BatchAttribs /*0x1C*/ int MatrixTableOffset; // Offset to MatrixTable /*0x20*/ int PacketDataOffset; // Offset to the actual primitive data /*0x24*/ int MatrixDataOffset; // Offset to MatrixData /*0x28*/ int PacketLocationOffset;
Batches define sets of packets. A batch defines a bounding box and a bounding sphere. It also determines how many packets it contains, and what set of vertex attributes are enabled for the packets.
// 0x28/40 bytes long /*0x00*/ short Unknown; /*0x02*/ short PacketCount; // How many packets to this Batch /*0x04*/ short AttributesOffset; // Relative to SHP1Header.BatchAttribsOffset /*0x06*/ short MatrixDataOffset; // Index to 'PacketCount' consecutive indexes /*0x08*/ short PacketDataOffset; // Index to first packet ('PacketCount' consecutive Indexes) /*0x0A*/ short Padding; /*0x0C*/ float BoundingSphereRadius; /*0x10*/ float BBoxMinX; /*0x14*/ float BBoxMinY; /*0x18*/ float BBoxMinZ; /*0x1C*/ float BBoxMaxX; /*0x20*/ float BBoxMaxY; /*0x24*/ float BBoxMaxZ;
The AttributesOffset variable is the offset to the first vertex attribute to enable for the batch. After this first attribute is enabled, the next is enabled, and so on, until an attribute with the type 0xFF (null attribute) is encountered.
This is an array of shorts that counts up from 0 to the number of batches - 1. Its purpose is currently unknown. It's padded to the nearest 32 bytes with the standard padding string.
This is a list of the vertex attributes that are enabled for each batch. They are stored similarly to the attributes in the VTX1 chunk, but they consist of only the attribute type and the data type. This data type refers to the type of the index into the VTX1 data banks, and not the type of the data itself. Because of this, the data type for these attributes is typically a signed short (0x03).
// 8 bytes long /*0x00*/ int AttributeType; /*0x04*/ int DataType;
This section stores indexes that are used to access the Partial Weight Array in the DRW1 chunk. A vertex accesses this table using the value that it has for the position matrix vertex attribute, if it has one.
// 2 bytes long /*0x00*/ short PartialWeightArrayIndex;
This section has an oddity. If a batch contains more than one packet, the index referenced by a vertex's position matrix index may end up being 0xFFFF/-1. If this is the case, then that means that the position matrix index must be walked back through the table to find the first value that isn't 0xFFFF/-1. In other words, if the value in the matrix table is -1, that means that the last valid index must be used.
This is the actual geometry data. Each packet has a primitive type, a vertex count, and then the vertex data.
// Header is 3 bytes, vertex data varies in length /*0x00*/ byte PrimitiveType; /*0x01*/ short VertexCount;
Each vertex consists of an index corresponding to the active vertex attributes. For example, if a batch has the position, color 1, and tex 1 attributes active, one vertex would have an index into the position data bank, the color 1 data bank, and the tex 1 data bank.
Primitives are padded to the nearest 8 bytes with zero-value bytes.
Below are the primitive types supported by the GameCube's GPU. Note that although some of them exist, they may not be used in practice. It appears that BMD and BDL most often use Triangle Strips, and rarely Triangle Fans.
Not much is known about this section. It needs to be investigated more.
// 8 bytes long /*0x00*/ short Unknown; /*0x02*/ short Count; // `Count` many consecutive indexes into MatrixTable /*0x04*/ int Indexoffset; // Relative to ??
Packet Location Data
This gives the size and location of every packet in the model.
// 4 bytes long /*0x00*/ int Size; //Size in bytes of the packet /*0x04*/ int Offset; //Offset relative to `SHP1Header.PacketDataOffset`
Note that the offset given by the location data is relative to the start of the packet data - that is, the value of PacketDataOffset in the SHP1's header.
Material Data (MAT3)
FIFO Data (MDL3)
MDL3 is only found in the BDL format, and contains FIFO instructions. While the exact purpose of this chunk isn't known, it is assumed that it's used to speed up rendering by offering pre-compiled material data to feed to the GameCube's GPU.
It begins with a header, which is followed by a listing of the offset and size of each packet of FIFO instructions. After this is the instruction data itself, and then a set of offsets that identify blocks of instructions within the packets. It ends with a string table that gives a name to each packet of instructions. This name typically matches the name of a material found in MAT3.
// 0x24/36 bytes long /*0x00*/ char Magic; // 'MDL3' /*0x04*/ int Size; /*0x08*/ short EntryCount; /*0x0A*/ short Padding; // 0xFFFF/-1 /*0x0C*/ int PacketOffset; // Packet Location Data /*0x10*/ int SubPacketLocationOffset; // Sub-packet Location Offset /*0x14*/ int MatrixIndexOffset; // Matrix Index Offset /*0x18*/ int Unknown0Offset; // Unknown0 Offset /*0x1C*/ int IndexesOffset; /*0x20*/ int StringTableOffset; //See JNT1
The header is padded to the nearest 32 bytes with the standard padding string.
This gives the size and offset of a packet of FIFO instructions. The offset is relative to the start of the packet's entry in the listing, rather than the start of the listing itself or the start of the MDL3 chunk.
// 4 bytes long /*0x00*/ int Offset; /*0x04*/ int Size;
The listing is padded to the nearest 32 bytes with the standard padding string.
This is a set of FIFO instructions that affect specific areas of the GameCube's FIFO system. Each packet is split into sub-packets based on the area that the instructions at that offset in the packet work on. The offsets to these sub-packets are stored in the Sub-Packet Location Data, defined below.
There are two kinds of FIFO instructions found in a packet: BP and XF. They can be differentiated by their first byte, which is an opcode as interpreted by the GameCube. This opcode is 0x61 for BP commands, and 0x10 for XF commands.
// 5 bytes long /*0x00*/ byte OpcodeID; // 0x61 for BP commands /*0x01*/ byte RegisterAddress; // The address of the register to modify /*0x02*/ byte RegisterValue1; /*0x03*/ byte RegisterValue2; // RegisterValue1/2/3 are really part of 1, 24-bit value /*0x04*/ byte RegisterValue3;
The 3 RegisterValue variables are meant to be read together as a single 24-bit value, and this value is what is placed into the register specified by RegisterAddress. What effect this value has on the GPU is determined by the address of the register that is modified.
// Header is 5 bytes long; data is variable length /*0x00*/ byte OpcodeID; // 0x10 for XF commands /*0x01*/ short DataLengthMultiplierMinus1; /*0x03*/ short RegisterAddress; /*0x05*/ byte[(DataLengthMultiplierMinus1 + 1) * 4] Data;
For whatever reason, the length of the data that the command uses isn't stored; instead, it stores a multiplier that must be multiplied by 4 to get the actual data length. To make this even more odd, DataLengthMultiplierMinus1 must have 1 added to it before it can be used, as shown in the array definition for the Data variable.
It is currently unknown how the data stored in the command is used, but the effect that it has on the GameCube's GPU is based on the RegisterAddress that the command gives.
Sub-Packet Location Data
This needs to be investigated.
This gives the offsets within a packet of blocks of instructions that affect specific parts of the material that this packet belongs to. Exactly what these blocks do is not yet known, but we do currently have a general understanding of what they affect, as shown in the layout below.
// 0x10/16 bytes long /*0x00*/ short ChannelColorOffset; /*0x02*/ short ChannelControlOffset; /*0x04*/ short TextureGenOffset; /*0x06*/ short TextureOffset; /*0x08*/ short TevOffset; /*0x0A*/ short PixelOffset; /*0x0C*/ int Padding;
Unknown Section 2 ("Matrix Index Offset")
This needs to be investigated.
It appears to consist of at least a float and an int. However, the float is typically very small, casting doubt on the idea that it is a valid float.
// 8 bytes long /*0x00*/ float MaybeFloat; /*0x04*/ int MaybeInt;
The "default" value appears to be:
3C F3 CF 00 00 F3 CF 3C
This value is found in the majority of files, sometimes alongside other values.
Unknown Section 3
This needs to be investigated.
It is an array of bytes, padded to the nearest 8 with the standard padding string.
The byte seems to match the value of the Flag at 0x00 of the material that the packet is associated with - either a value of 1 or 4. What this means is currently unknown.
Texture Data (TEX1)
This chunk contains the images used for textures in materials. It is made up of a header and embedded BTI images.
// 0x14/20 bytes long /*0x00*/ char Magic; // 'TEX1' /*0x04*/ int Size; /*0x08*/ short TextureCount; // Number of textures /*0x0A*/ short Padding; // Usually 0xFFFF/-1 /*0x0C*/ int TextureHeaderOffset; // TextureCount bti image headers are stored here. Relative to TEX1Header start. /*0x10*/ int StringTableOffset; // Stores one filename for each texture. Relative to TEX1Header start.
Embedded BTI Images
These are the actual textures. Information about embedded images can be found in the article about the BTI format.