BMD and BDL are the two formats that store model data in Nintendo games for the GameCube and the Wii, as part of Nintendo's J3DGraph library, which is part of their larger in-house JSYSTEM toolkit. 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 Super Mario Galaxy 2. BMD is an abbreviation for Binary MoDel, and BDL is an abbreviation for Binary Display List.
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 File Conventions
- 2 Model Info (INF1)
- 3 Vertex Data (VTX1)
- 4 Skinning Envelopes (EVP1)
- 5 Draw Matrix Array (DRW1)
- 6 Joint Data (JNT1)
- 7 Shape Data (SHP1)
- 8 Material Data (MAT3)
- 9 FIFO Data (MDL3)
- 10 Texture Data (TEX1)
This file follows JSYSTEM binary conventions, including the file's header and chunk layouts. It's also common practice to lay out an array of structures, instead, in structure-of-arrays style.
Model Info (INF1)
This has miscellaneous information about how to read the rest of the model, the most important being the hierarchy, which describes how the shapes, materials, and joints are combined. It's laid out as 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 total number of matrix groups, and the total number of vertexes in the model.
||int||Matrix Group Count||The total number of matrix groups in the file.|
||int||Vertex Count||The total number of vertices in the VTX1 section.|
||int||Hierarchy Data Offset||Offset to the hierarchy data.|
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:
||short||NodeType||See the Hierarchy Node Types below.|
||short||Data||Optional data value, used by the Joint, Material or Shape node types.|
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).
|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||Adds geometry data to the hierarchy|
Vertex Data (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.
||int||Vertex Format Offset||Offset to the VertexFormat section.|
||int||Position Data Array Offset||Offset to the data array for the Position attribute.|
||int||Normal Data Array Offset||Offset to the data array for the Normal attribute.|
||int||NBT Data Array Offset||Offset to the data array for the NBT attribute.|
||int||Color Data Array Offset||Offsets to the data array for the Color attributes.|
||int||Texcoord Data Array Offset||Offsets to the data array for the Texcoord attributes.|
If an attribute has no data, the data array offset for that attribute will be 0.
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.
||GXAttr||Attribute Type||The vertex attribute being defined|
||byte||Component Shift||Shift for fixed-point attribute types|
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.
GXAttr is an enum as follows:
|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|
|0xFF||Null Attribute||Terminate the attribute list. There are no more attributes.|
GXCompType is as follows. 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 array 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 array is aligned to the nearest 8 bytes with the standard padding string.
Skinning Envelopes (EVP1)
This chunk defines the envelope matrices for weighted skinning, allowing multiple bones to move one set of geometry data. See the DRW1 and SHP1 matrix group section to determine how these matrices are eventually used.
||short||Envelope Matrix Count||The number of envelope matrices in this section|
||int||Joint Count Table Offset|
||int||Index Table Offset|
||int||Weight Table Offset|
||int||Inverse Bind Pose Table Offset|
Each envelope matrix is made up of multiple joints which contribute to the matrix, in different weights. For an example, an envelope matrix for a typical human skeleton might want to define an "elbow" section which is influenced equally by the two different parts of the arm. To do this, it would specify two joints, specifying each of the arm joints in the index table, with 0.5 and 0.5 as weights.
Joint Count Table
This gives the number of joints that influence a particular envelope matrix.
Index and Weight tables
The joint indices and joint weights are used to scale each joint that affects the envelope matrix. The weights of each joints should add up to a sum of 1; otherwise, the vertexes do not move with the bones correctly.
These arrays are paired -- that is, both the joint index and joint weight arrays should be the same size.
Joint Index Table
||byte||Joint Index||Index of the joint, as defined by the JNT1 section|
Joint Weight Table
||float||Weight||Weight of this joint in the envelope matrix.|
Inverse Bind Pose Matrices
Vertices that are destined to be enveloped are not stored in joint-local space, as they do not have a singular joint parent to be local to. Instead, they are stored in the model's global space, which is typically a "bind pose" like an A pose or a T pose. To convert each enveloped vertex from the model's global space to the relative space for each joint so it can be skinned, then an inverse-bind-pose matrix must be first applied. This is equivalent to the inverse model matrix of each joint, but it is stored precomputed in the model file for efficiency purposes. Each joint referenced for the envelope should have a proper inverse bind pose matrix in this array, stored at its joint index.
||MTX34||Inverse Bind Pose Matrix|
Draw Matrix Array (DRW1)
This chunk determines how the global draw matrix array list is built. It is made up of a header, followed by the matrix type array and the data array.
||int||Matrix Type Array Offset|
||int||Data Array Offset|
Partial Weight Array
This is an array of matrix type values. The possible matrix types are "joint" (0x00), and "envelope" (0x01).
// 1 byte long /*0x00*/ DRWMatrixType MatrixType;
The data array is made up of a single short. If the respective matrix type is joint, then it contains a joint index (see the JNT1 section). If it is envelope, then it contains an envelope matrix index (see the EVP1 section).
// 2 bytes long /*0x00*/ short Data;
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.
||int||Joint Transform Table Offset|
||int||Remap Table Offset|
||int||Name Table Offset|
There are JointCount joints. Each joint is defined as a name in the name table, and an index in the remap table. The config table determines the joint's settings and initial configuration. It is possible, if unlikely, for two joints with different names to have the same configuration. In this case, JointConfigData will not have JointCount entries. These should be treated as two separate joints that share a single configuration.
Joint Transformation Data
This includes scaling, rotation, and translation data for the joint, as well as a bounding sphere radius and bounding box information.
||MatrixTypeFlags (short)||Matrix Type Flags||Affects Joint Matrix Calculation|
||bool||Do Not Inherit Parent Scale|
||float||Bounding Sphere Radius|
||float||Bounding Box Min X|
||float||Bounding Box Min Y|
||float||Bounding Box Min Z|
||float||Bounding Box Max X|
||float||Bounding Box Max Y|
||float||Bounding Box Max Z|
Rotation is stored in signed units that range from -0x7FFF to 0x7FFF, corresponding to -180 degrees and 180 degrees, respectively.
Shape Data (SHP1)
SHP1 stores the geometry data for the model. The data is split into "shapes", with each shape having multiple "matrix groups." Each shape can have different vertex attributes active, which translates into the actual geometry data differing in vertex structure between shapes. However, since the GameCube can only support 10 matrices per draw call, which might not be enough for some complex skinned meshes, each shape can be split up into different matrix group, each of which can use a separate draw matrix table. The matrices eventually referenced by this table are used together with the PNMTXIDX vertex attribute.
The chunk begins with a header. This contains counts and offsets to each section of the data. After this is the shape data, defining shapes with a bounding sphere radius, a bounding box, and an offset to the attributes that the shape uses. An index table, the attribute table, the matrix table, the geometry data, the matrix data, and the draw primitive data follow.
// 0x2C/44 bytes long /*0x00*/ int char Magic; // 'SHP1' /*0x04*/ int ChunkSize; // Total bytes of chunk /*0x08*/ short SectionCount; // How many shapes there are /*0x0A*/ short Padding; // 0xFFFF/-1 /*0x0C*/ int ShapeDataOffset; // Offset to Shape's (Relative to SHP1Header start) /*0x10*/ int RemapTableOffset; /*0x14*/ int NameTableOffset; // Usually zero /*0x18*/ int AttribTableOffset; // Offset to AttribTable /*0x1C*/ int MatrixTableOffset; // Offset to MatrixTable /*0x20*/ int PrimitiveDataOffset; // Offset to the actual primitive data /*0x24*/ int MatrixDataOffset; // Offset to MatrixData /*0x28*/ int MatrixGroupTableOffset;
Much like joints and materials, a remap table is provided to allow the shape at index N to be pulled from a different shape entry in the file. This seems to be identity most of the time. Similarly, the offset at 0x14 would normally point to a name table, but this has not been observed in the wild yet.
A shape is a series of draw calls that use the same vertex attributes. A shape also has a bounding box and a bounding sphere.
// 0x28/40 bytes long /*0x00*/ short DisplayFlags; // Contains some information about how the shape should be displayed (See below) /*0x02*/ short MatrixGroupCount; // How many matrix groups to this shape /*0x04*/ short AttributesOffset; // Relative to SHP1Header.AttribsTableOffset /*0x06*/ short FirstMatrixDataIndex; // Index of the first MatrixData for this shape /*0x08*/ short FirstMatrixGroupIndex; // Index to first matrix group ('MtxGroupCount' 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 shape. After this first attribute is enabled, the next is enabled, and so on, until an attribute with the type 0xFF (null attribute) is encountered.
DisplayFlags is an enum that determines how the position and normal matrices are calculated for the shape.
|0x03||Multi Matrix (Skinned Model)|
This is a list of the vertex attributes that are enabled for each shape. 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 of draw matrices, as defined by DRW1. There are 10 of them, and they map to the 10 hardware matrix slots available through GX.
// 20 bytes long /*0x00*/ short DrawMatrixIndex;
This index can also sometimes be 0xFFFF. If this happens, then the index for this slot should be taken from the previous matrix group. An index of 0xFFFF should not appear on the first matrix group.
Primitive Display List Data
This is the actual geometry data, formatted as a GX display list, which is a number of draw call commands. Each draw call command 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 shape 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.
// 8 bytes long /*0x00*/ short UseMtxIndex; /*0x02*/ short UseMtxCount; // `Count` many consecutive indexes into MatrixTable /*0x04*/ int FirstUseMtxIndex;
Each matrix group can reference a sub-section of the MatrixTable through this MatrixData chunk. This determines the matrix table for this matrix group. The matrix table references indexes into the draw matrix array (see the DRW1 chunk). The PNMTXIDX vertex attribute determines which matrix out of this table is applied for which vertex.
If the DisplayFlags is 0x03 (Multi), then UseMtxCount and FirstUseMtxIndex determine a variable-length matrix array and UseMtxIndex is unused. Otherwise, the matrix to use for the shape is defined by UseMtxIndex, and UseMtxCount and FirstUseMtxIndex are unused. However, it has been found by observation that official BMD/BDL files will set up a matrix table of size 1 with a single entry, equal to UseMtxIndex.
Primitive Location Data
This gives the size and location of every matrix group in the model.
// 4 bytes long /*0x00*/ int Size; //Size in bytes of the primitive draw display list /*0x04*/ int Offset; //Offset relative to `SHP1Header.PrimitiveDataOffset`
Note that the offset given by the location data is relative to the start of the primitive data - that is, the value of PrimitiveDataOffset 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.