Geometry (Metroid Prime)
The format for geometry appears in both the CMDL and MREA formats, and is identical between both. This format is used in all three Prime games, and is largely unchanged between all of them (with the exception of an extra vertex attribute that was added in Echoes and Corruption).
|This file format is almost completely documented |
There's two unknowns in the surface headers that appear in Prime 2.
An important thing to note off the bat is that both CMDL and MREA files are split up into a number of 32-byte-aligned sections; their respective headers both declare a section count, and list the sizes of each section contained in the file. This is required to navigate to different parts of the file, and is required in order to read the geometry data correctly. Sections are generally split based on the type of data contained in them, so one section might contain vertex coordinates, while another contains a surface (submesh) definition. For the purposes of this article, each subheader will mark the start of a new section; use that as your cue to advance to the next one. Check the CMDL or MREA pages for more info on how the sections work.
In the CMDL format, and in the MP1/2 MREA format, all data sections appear in the order that they are listed in this page. In the Prime 3/DKCR MREA format, the surface definitions, surface group IDs, and surface lookup table are shifted to a different part of the file.
For vertex attributes, note that there's no count value present anywhere. If you want a rough count, you can divide the size of that attribute's section by the attribute's size; this approach doesn't account for padding, however.
These are stored as a sequence of three floats.
Normals can appear as either floats or shorts. In CMDL, this usually corresponds to the 0x2 bit in the flags value, and in MREA they will always appear as shorts; this is technically supposed to be set by the vertex format flag in the primitive data, though. If they appear as shorts, they should be divided by the mantissa value that appears on each surface (that value is always 0x8000).
These are theoretically stored as 32-bit RGBA values, but vertex color isn't actually used by any mesh in any Retro games, so this section always appears completely empty.
Float UV Coordinates
These are stored as a sequence of two floats.
Short UV Coordinates
These are stored as a sequence of two shorts. Like normals, these should be divided by the surface's mantissa value. The short UV array is generally only used by terrain lightmaps, and as such this section always appears completely empty (listed with a size of 0) on CMDL. This array can only be used by the first UV coordinate on a given vertex, and whether it's used or not depends on one of the flags on the material. The rest will use the float array.
This section is very small and simple; it simply declares a surface count and then lists the offsets to the end of each surface. Following this section, there'll be one additional section per surface.
|0x4||u32||Surface Count||Surface End Offsets||Relative to the start address of the first surface.|
|Surface sections begin|
There will be one of these sections per surface. Each surface starts with a small header; following the end of the header, the file is padded with 0s to the next 32-byte boundary, then the primitive data begins.
|0x0||float||3||Center Point||Used for depth sorting on CMDL and used as the eye position for reflections|
|0x10||u32||1||Display List Size||In order to get the display list size you need to mask this value with 0x7FFFFFFF, the purpose of the topbit isn't known yet|
|0x14||u32||1||Parent Model Pointer Storage||Always 0; the game uses this space at runtime to store a pointer to the parent CCubeModel instance|
|0x18||u32||1||Next Surface Pointer Storage||Always 0; the game uses this space at runtime to store a pointer to the next surface|
|0x1C||u32||1||Extra Data Size||Amount of space taken up by extra data. Always 0 on CMDLs, 0x18 on MREAs.|
|0x20||float||3||Surface Normal||On materials that use the Samus reflection, this is used to project the reflection onto the surface|
|0x2C||u16||1||Unknown||Not in Prime 1.|
|0x2E||u16||1||Unknown||Not in Prime 1.|
|0x30||u8||Extra Data Size||Extra Data||Empty on CMDL; contains a bounding box used for depth sorting on MREA.|
|End of surface header; pad to multiple of 32 before primitive data starts|
The primitive data is a GX display list. Each primitive begins with a byte that contains the primitive type in the upper 5 bits, and the vertex format setting in the lower 3; see below for possible settings. After that is a 16-bit vertex count, followed by a series of 16-bit vertex attribute indices.
Note that you'll need to check some settings on the material to read the primitive data properly; the material determines which vertex attributes are present. The material also determines whether the surface is using lightmaps; if it is, then the first UV coordinate will be read out of the short UV array instead of the float one.
There is no primitive count value; you'll need to continue reading data until you hit the end of the display list. There are a couple values you can use for reference; either the surface's section size, the display list size, or the surface's end offset.
There are 7 primitive types supported by GX, indicated in the upper 5 bits of the flag value. Note that the game only ever actually uses triangles, triangle strips, and triangle fans; however, all of these primitives are supported by GX and are therefore supported by the game, and so they could be used in custom model files.
In addition, the game uses three vertex formats, indicated in the lower 3 bits of the flag value (0x7). The only attributes that change between formats are nrm and tex0, so those are the only ones listed in the table below.
|Format||GX_VA_NRM type/size||GX_VA_TEX0 type/size|
|0||GX_NRM_XYZ / GX_F32||GX_TEX_ST / GX_F32|
|1||GX_NRM_XYZ / GX_S16||GX_TEX_ST / GX_F32|
|2||GX_NRM_XYZ / GX_S16||GX_TEX_ST / GX_S16|
Surface Group IDs
This section (and the following one) appears in MREA files starting in Metroid Prime 2. Starting in MP2, surfaces in world geometry are now merged together to create as few meshes as possible (meshes are now only split when the vertex count exceeds 65535). Two extra data sections are present to manage the resulting super-meshes to enable the game to extract smaller meshes out when needed.
This first section assigns a group ID to each surface, forming smaller meshes. This ID is used in various places that operate on world geometry, including in the area octree as well as in the EGMC format, among others.
|u16||1||Surface Count||Matches the world model surface count|
|Surface Group ID||Surface Count||Surface Group ID Array||Sequentially assigns a surface group ID to every surface in the mesh.|
Surface Group ID
|0x0||u16||Model-Relative Surface Group ID||This is the ID of the group this surface belongs to, relative to the groups in this model (starting from 0).|
|0x2||u16||Surface Group ID||Same ID, except area-relative (starting where the previous world model's group IDs left off). This is generally the ID that appears in other places to reference this group.|
Surface Lookup Table
This is the second new MREA-exclusive section introduced in Metroid Prime 2. It provides a mapping of each surface group to the surfaces contained in it, so that surfaces can be quickly accessed by group ID.
|u16||1||Surface Group Count|
|u16||Surface Group Count||Lookup Table Index Array||The value of each element corresponds to the end index of the surfaces for this group in the lookup table. Each element is equal to the previous element + the number of surfaces contained in this group.|
|u16||Surface Count||Surface Lookup Table||List of surface indices ordered by group ID.|