Materials (Metroid Prime)

Revision as of 16:08, 13 March 2015 by >Aruki (→‎Color Channels)

The format for materials is seen in both the CMDL and MREA formats and is identical in both. This particular material format appears in both Metroid Prime and Metroid Prime 2 with minor differences.


This file format is almost completely documented
There are a couple unknown values left. Also, basically everything that was introduced in Echoes (two general settings and a vertex attribute) is unknown.


GX Overview

The materials system in Metroid Prime is heavily dependent on the GameCube and Wii's graphics system, GX. GX is a fixed-function graphics pipeline, similar to old versions of OpenGL. In GX, rendering is done through a series of steps called Texture EnVironment Stages, or or TEV stages for short.

Prior to TEV stages, per-vertex calculations are performed. Lighting calculations are performed and passed to GX as a rasterized vertex color; texture coordinate generation (texgen) is also performed and made available to TEV.

How TEV stages actually work: Each TEV stage takes in four color (RGB) inputs and four alpha inputs, from one of eight sources. Then these four input colors are combined into one output color, and the output is saved into one of four registers, which can subsequently be used as input in the next TEV stages. The final stage must always save its output into the "previous TEV stage" register; that register is used as the final pixel color that gets displayed onscreen.

Material Set Format

Materials come as part of a set; often there will only be one set per file, but many CMDLs can have more than one. The set begins with a short header before the actual material data begins.

Type Count Description
long 1 Texture count (TC)
long TC Texture file IDs
long 1 Material count (MC)
long MC Material end offsets (relative to the start of the first material)
Materials begin

Material Format

General Settings

Each material begins with a flags value, followed by a list of texture indices:

Type Count Description
long 1 Flags
long 1 Texture count (TC)
long TC Texture indices (these are indices into the material set texture array)
long 1 Vertex attribute flags
long 1 Unknown (note: only in Echoes)
long 1 Unknown (note: only in Echoes)
long 1 Unknown
long 1 Konst count (KC); max setting is 4. (note: only present if flag 0x8 is set!)
long KC Konst colors - 32-bit RGBA colors (note: not present if the Konst count isn't)
short 1 Blend destination factor
short 1 Blend source factor
long 1 Unknown (note: only present if flag 0x400 is set!)
long 1 Color channel count (CC)
long CC Color channel flags (note: only one flag value, but you must advance 4*CC bytes instead of 4 for some reason)
long 1 TEV stage count

Flags

These are the known flag settings:

Bit Hex Description
0 0x1 Appears unused, but always set
1 0x2 Appears unused, but always set
2 0x4 Appears unused, but always unset
3 0x8 Enable Konstant values
4 0x10 Mark transparent material
5 0x20 Enable punchthrough alpha
6 0x40 Enable Samus's reflection
7 0x80 Enable depth writing
8 0x100 Unknown
9 0x200 Don't draw
10 0x400 Enable second unknown value
11 0x800 Unknown
12 0x1000 Unknown
13 0x2000 Enable lightmaps; first UV coordinate uses short array instead of float array
14 0x4000 Unknown
15 0x8000 Unknown
16 0x10000 Unknown
17 0x20000 Unknown
18 0x40000 Unknown
19 0x80000 Unknown
20+ 0x100000+ Unused; never set

Vertex Attribute Flags

These flags are generally toggled in pairs, with each pair corresponding to a vertex attribute; if a pair is set, then vertices using this material will have the corresponding attribute. This is vital for reading geometry.

An important note is that while the leftmost byte is not used in Prime 1, in Echoes there's occasionally a value there that toggles an extra vertex attribute preceding position. This needs research to determine what the new attribute actually is. Also, GX supports up to 8 texture coords per vertex, but the game doesn't seem to allow you to assign more than 7 (though this could do with some double-checking).

These are the possible attributes:

Bit pair Attribute
0x3 Position
0xC Normal
0x30 Color 0
0xC0 Color 1
0x300 Tex 0
0xC00 Tex 1
0x3000 Tex 2
0xC000 Tex 3
0x30000 Tex 4
0xC0000 Tex 5
0x300000 Tex 6

Konst Colors

These values are only present when flag 0x8 is enabled. These allow you to set Konstant values, which can subsequently be used as inputs in TEV stages. The maximum number of Konst values you can set on one material is 4. The colors themselves are simply 32-bit RGBA values.

Blend Mode

The two blend factors set the blending mode used. The most common values you'll see are 0/1, which is used on opaque materials; transparent materials will usually have either 1/1, for additive blending, or 5/4, for alpha blending. Here's the full range of possible settings for each value:

Value Blend factor
0 GX_BL_ZERO
1 GX_BL_ONE
2 GX_BL_SRCCLR / GX_BL_DSTCLR
3 GX_BL_INVSRCCLR / GX_BL_INVSRCCLR
4 GX_BL_SRCALPHA
5 GX_BL_INVSRCALPHA
6 GX_BL_DSTALPHA
7 GX_BL_INVDSTALPHA

Color Channels

The color channels data consists of a count value indicating the number of color channels, and then one set of flags that controls the COLOR0 channel. The flags correspond to arguments passed to GX_SetChanCtrl(). These are the settings:

Bits Hex Description
0 0x1 Enable lighting (enable)
1 0x2 Ambient color source (ambsrc)
2 0x4 Material color source (matsrc)
3-10 0x7F8 Light mask; always 0, filled in at runtime (litmask)
11-12 0x1800 Diffuse function (diff_fn)
13-14 0x6000 Attenuation function (attn_fn)

There's only one set of flags, which operates on COLOR0. COLOR1 is reserved for dynamic shadowmaps, and its settings can't be modified. Here's how the other color channel settings work.

If shadowmaps are enabled:

  • No processing will be done if the count value is 0. Otherwise, the count value is ignored; the game will always set up both color channels.
  • COLOR1A1's ambient color is black, and its material color is white.
  • COLOR1 will use the diffuse function GX_DF_CLAMP and the attenuation function GX_AF_SPOT. It will have lighting disabled entirely if there are no lights in range.
  • COLOR0 will use the flags from the material, with the light mask merged in. It will also have lighting disabled if there are no lights in range.
  • If there are no lights in range, COLOR0A0 will have its material color set to white. Otherwise, its material color will be set to match its ambient color.

If shadowmaps are disabled:

  • COLOR1 is disabled if the count value is not 2. If it is 2, then its ambient color will be black and its material color will be white.
  • COLOR0 is disabled if the count value is 0. Otherwise, it's set up the same way as with shadowmaps.

Finally, an important note is that the formatting of the color channel info is bugged. There is always a color channel count and a single set of flags; 8 bytes total. However, for some reason the game is expecting one set of flags per color channel. To reach the TEV stages, you need to advance (4 * count) bytes past the count value, rather than 4. A likely explanation is that there was one set of flags per channel earlier in development, and the material loading code wasn't fully updated when this was changed. It's not normally an issue because the count is always 1 in every file the game uses, but beware for experimentation/custom materials.

TEV Stages

There'll be one of these structures per TEV stage:

Offset Size Description
0x0 4 Color Input Flags
0x4 4 Alpha Input Flags
0x8 4 Color Combine Flags
0xC 4 Alpha Combine Flags
0x10 1 Padding
0x11 1 Konst Alpha Input
0x12 1 Konst Color Input
0x13 1 Rasterized Color Input
0x14 End of TEV stage

After looping through each TEV stage, there will be one of these structures per stage:

Offset Size Description
0x0 2 Padding
0x2 1 Texture TEV Input
0x3 1 Tex Coord TEV Input
0x4 End of structure

Color Input Flags

These flags set the four color inputs that are used by the TEV stage. Each color is allocated 5 bits, even though only 4 are actually used; the lower bits correspond to the lower input numbers (eg. the bottom 5 bits refer to the first input).

There are 16 possible color sources:

Binary Hex Color Source
00000 0x0 Previous stage RGB
00001 0x1 Previous stage AAA
00010 0x2 Color 0 RGB
00011 0x3 Color 0 AAA
00100 0x4 Color 1 RGB
00101 0x5 Color 1 AAA
00110 0x6 Color 2 RGB
00111 0x7 Color 2 AAA
01000 0x8 Texture RGB
01001 0x9 Texture AAA
01010 0xA Rasterized RGB
01011 0xB Rasterized AAA
01100 0xC One
01101 0xD Half
01110 0xE Konstant RGB
01111 0xF Zero

Alpha Input Flags

Similar to the color input flags, these set the four alpha inputs used by the TEV stage. Each value is allocated 5 bits, although only 3 are used. The main difference with alpha is that there are only 8 possible sources instead of 16:

Binary Hex Alpha Source
00000 0x0 Previous stage alpha
00001 0x1 Color 0 alpha
00010 0x2 Color 1 alpha
00011 0x3 Color 2 alpha
00100 0x4 Texture alpha
00101 0x5 Rasterized alpha
00110 0x6 Konstant alpha
00111 0x7 Zero

Color Combine Flags

These flags specify how the operation that combines the four input colors into one output color is performed.

This is the combiner function:

tevrigid = (d (tevop) ((1.0-c)*a + c*b) + tevbias) * tevscale;

The values set in the color operation flags correspond to the parameters passed to GX_SetTevColorOp(). These are the settings:

Bits Hex Description
0-3 0xF Combiner operator (tevop)
4-5 0x30 Bias (tevbias); 00 does nothing; 01 adds 0.5; 10 subtracts 0.5; 11 does nothing.
6-7 0xC0 Scale (tevscale); 00 does nothing; 01 multiples by 2; 10 multiplies by 4; 11 divides by 2.
8 0x100 Clamp flag; enabling this will clamp the output color between 0 and 1.
9-10 0x600 Output register (tevrigid); can be Previous Stage or Color 0/1/2
11+ 0xFFFFF800 Unused.

Note that the vast majority of materials in the game, if not all of them, don't really bother with any of this; they enable clamping, set an output register, and leave everything else at their defaults of 0.

Alpha Combine Flags

This is exactly the same as the color combine flags; the only difference is it operates on alpha instead of color.

Texgen

After the TEV stages comes a sequence of flags determining how texgen is executed. Texgen is the process of taking input values and using them to generate texture coordinates (or UV coordinates), which can then be used by the TEV stages. It's important to note that any vertex attribute can be used as a texgen input; in fact, it's rather common for materials to use the vertex normal as an input to simulate reflections. That means the number of texture coords present on each vertex is not the same as the number of texture coords available to TEV.

Type Count Description
long 1 Texgen count (TC)
long TC Texgen flags

The flags correspond to arguments passed to GX_SetTexCoordGen2(). These are the settings:

Bits Hex Description
0-3 0xF Texture coord generation type (tgen_typ)
4-8 0x1F0 Texture coord source (tgen_src)
9-13 0x3E00 Texture matrix index (mtxsrc - add 30)
14 0x4000 Normalize flag - normalizes texture coord after it's multiplied by the texture matrix (normalize)
15-20 0x1F8000 Post-transform texture matrix index (postmtx - add 64)
21+ 0xFFE00000 Unused

UV Animations

The UV animations section immediately follows the texgen flags. Its purpose is to generate texture matrices and post-transform matrices, which are then used to transform UV coordinates. This is commonly used to animate textures via simple UV scrolls, but it's also often used to simulate reflective surfaces that move with the camera. Materials can have multiple UV animations; in that case, each animation generates a separate texture/post-transform matrix and are loaded into GX sequentially. Texgen is used to set which matrices are used by which UV coordinates (if any).

The section starts with this short header:

Offset Size Description
0x0 4 Material animations section size (includes animation count, so will be 4 at minimum)
0x4 4 Animation count

The structure of the animations themselves is somewhat simple. Each animation has a 32-bit mode setting, followed by a number of float parameters. The number and usage of these float parameters varies depending on what mode is set. There are 8 possible modes.

For all of the following modes, s refers to seconds mod 900.

Mode 0: Inverse ModelView Matrix (No Translation)

This mode is commonly used with vertex normals to simulate reflective surfaces using spheremaps that move with the camera. It takes no parameters, and will generate both a texture matrix and a post-transform matrix. Translation is ignored.

The texture matrix is calculated like this. Note that the multiplication of the view matrix by the model matrix should be modified slightly rather than being a straight multiplication to ignore translation on the model matrix. (The game uses a function called MultiplyIgnoreTranslation for this.)

texmtx = inverse(ViewMatrix) * ModelMatrix;
texmtx[0][3] = texmtx[1][3] = texmtx[2][3] = 0;

The post-transform matrix is a constant value. [1][1] and [1][2] may need to be swapped around for correct playback.

0.5, 0.0, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0

Mode 1: Inverse ModelView Matrix

This mode is nearly identical to mode 0; the only difference is that translation is left as-is. The multiplication of the view matrix by the model matrix actually is a straight multiplication in this mode, and the translation values on the texture matrix aren't set to 0.

Mode 2: UV Scroll

This mode is used to scroll both U and V at the same time. It will only generate a texture matrix. It has four float parameters: offsetA, offsetB, scaleA, and scaleB.

uOffset = (s * scaleA) + offsetA;
vOffset = (s * scaleB) + offsetB;

Mode 3: Rotation

This mode rotates the texture. It will only generate a texture matrix. It has two float parameters: offset and scale.

float angle = (s * scale) + offset;
float acos = cos(angle);
float asin = sin(angle);
float translateX = (1.0 - (acos - asin)) * 0.5;
float translateY = (1.0 - (asin + acos)) * 0.5;

The resulting texture matrix is laid out as:

acos, -asin, 0.0, translateX,
asin,  acos, 0.0, translateY,
 0.0,   0.0, 1.0,        0.0

Mode 4/5: Horizontal/Vertical Filmstrip

These modes can be used to create a filmstrip-like effect. The texture steps a set distance each animation frame, rather than the scroll being smoothly interpolated every game frame. The same calculation is done for both modes, with the only difference being whether the calculated offset is applied to the U or V coordinate. It will only generate a texture matrix. There are four float parameters: scale, numFrames, step, and offset.

The animation is made up of a number of pseudo-frames, where step controls the amount that the texture scrolls by each frame, and numFrames sets how many frames are iterated through before resetting back to 0. scale roughly controls animation playback speed, and offset modifies the time input value.

float value = step * scale * (offset + s);
float uv_offset = (float)(short)(float)(numFrames * fmod(value, 1.0f)) * step;

Mode 6: Model Matrix

This mode is similar to modes 0 and 1 in that it simulates reflective surfaces, but it only takes the model matrix into account, which means camera movement doesn't affect the reflection. It takes no parameters and it generates both a texture matrix and a post-transform matrix.

The texture matrix is simply the model matrix, with the translation values set to 0. The post-transform matrix is set to the following:

0.5, 0.0, 0.0, ModelMatrix[0][3] * 0.50000001,
0.0, 0.0, 0.5, ModelMatrix[1][3] * 0.50000001,
0.0, 0.0, 0.0, 1.0

Mode 7: Mode-Who-Must-Not-Be-Named

Mode 7 takes two parameters and generates both a texture matrix and a post-transform matrix. We don't know what to name it.

The texture matrix is generated the same way as in mode 0; translation is ignored.

texmtx = inverse(ViewMatrix) * ModelMatrix;
texmtx[0][3] = texmtx[1][3] = texmtx[2][3] = 0;

The post-transform matrix is where it gets a little complicated. A little math is required to calculate some values:

float xy = ((ViewMatrix[0][3] + ViewMatrix[1][3]) * 0.025f * ParamB;
xy = (xy - (int) xy); // This truncates the integer portion of the value, leaving only the fractional part (mantissa).

float z = ViewMatrix[2][3] * 0.05f * ParamB;
z = (z - (int) z);

float halfA = ParamA * 0.5f;

The post-transform matrix is then constructed as:

halfA, 0.0f,  0.0f,  xy,
 0.0f, 0.0f, halfA,   z,
 0.0f, 0.0f,  0.0f, 1.0f