UV Animations

UV animations are used to animate texture coordinates. They're present in the material data of the Metroid Prime trilogy and Donkey Kong Country Returns. There are 13 animation types across those four games.

Format
UV animations generally contain an int indicating what animation type they are, followed by float parameters. The number of parameters depends on the animation mode; some modes don't have any parameters while some have as many as 9.

Metroid Prime 1/2
At the end of the material data, there's a 32-bit animations size value, then an animation count. Each animation is structured as:

Metroid Prime 3/Donkey Kong Country Returns
Animations are located at the end of each PASS section. There's no animation count; instead, one animation can be supplied per pass and can be directly associated with that pass. Like Prime 1, the animation data follows a 32-bit animations size value.

Animation 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. The purpose of it is to adjust the normalized texture coordinates so that they range from 0 to 1.

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.5

Mode 7: Cylinder Environment
Mode 7 takes two parameters and generates both a texture matrix and a post-transform matrix, projecting the UVs onto the surface of a cylinder, used almost exclusively on pipes.

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

Mode 10: Another reflection mode
This one needs more research done, but the only thing the game does with this mode is load this post-transform matrix:

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

The function of this matrix is to transform normalized texture coords so that they range from 0 to 1, instead of from -1 to 1. This mode seems to work well with mode 0's settings, though, so it's possible that the game is creating a texture matrix somewhere else. This needs some looking into.