BTK is the format that stores texture animation data for BMD and BDL models. It consists of the standard animation header, followed by BTK-specific data in the TTK1 chunk, with data stored in banks at the end separated by component type - scale, rotation, and translation.
This header is found at the start of all the animation formats. Below is what it looks like for the BTK format.
//0x20/32 bytes long /*0x00*/ char Magic; // "J3D1" /*0x04*/ char StudioType; // "btk1" /*0x08*/ int FileSize; /*0x0C*/ int NumChunks; /*0x10*/ char Svr1Magic; // "SVR1" // From 0x14 to 0x20 is an array of padding bytes with the value 0xFF/255.
Sometimes the standard header includes a chunk called "SVR1", as is the case in the BTK format. The purpose of this SVR1 chunk is unknown, since the chunk is filled with 0xFF/-1 bytes. Additionally, the spot where the SVR1 tag is in other versions of the standard header is filled with 0xFF/-1 instead.
This follows the standard header and contains data specific to the BTK format. It includes keyframe data for each texture being manipulated, as well as the actual key data in the form of data banks.
// 0x60/96 bytes long /*0x00*/ char Magic; // "TTK1" /*0x04*/ int Size; /*0x08*/ byte Flag; // Assuming from ANK1! /*0x09*/ byte AngleMultiplier; // Assuming from ANK1! /*0x0A*/ short AnimLength; /*0x0C*/ short KeyframeEntryCount; // 3 for each texture /*0x0E*/ short ScaleBankCount; /*0x10*/ short RotationBankCount; /*0x12*/ short TranslationBankCount; /*0x14*/ int KeyfameDataOffset; /*0x18*/ int RemapTableOffset; /*0x1C*/ int StringTableOffset; /*0x20*/ int TextureIndexTableOffset; /*0x24*/ int TextureCenterTableOffset; /*0x28*/ int ScaleFloatBankOffset; /*0x2C*/ int RotationShortBankOffset; /*0x30*/ int TranslationFloatBankOffset; // From 0x34 to 0x60 is an array of padding byte with the value 0
After the header is the keyframe data.
There are 9 keyframes for each texture that is being manipulated in the animation - 1 each for the U, V, and W components of scaling, rotation, and translation. They are grouped according to their components, and are always stored in the order of scale/rotation/translation. The layout of the keyframe data is shown below.
//0x36/54 bytes long // U components /*0x00*/ Keyframe ScaleU; /*0x06*/ Keyframe RotationU; /*0x0C*/ Keyframe TranslationU; // V components /*0x12*/ Keyframe ScaleV; /*0x18*/ Keyframe RotationV; /*0x1E*/ Keyframe TranslationV; // W components /*0x24*/ Keyframe ScaleW; /*0x2A*/ Keyframe RotationW; /*0x30*/ Keyframe TranslationW;
Each keyframe is 6 bytes long, with the following layout:
//0x6/6 bytes long /*0x00*/ short KeyCount; //Number of keys in this keyframe /*0x02*/ short StartingIndex; //Starting index of data in relevant bank /*0x04*/ short UnknownShort; //Either 0 or 1
It is currently unknown if the W component is actually used for animation, or if it is unused data left over from the BCK format, which utilizes the analagous Z component.
The keyframe data is added to the nearest 32 with the standard padding string.
To-do: This is confusing. Figure it out and re-write it.
This sections isn't completely understood, but it is common in the J3D formats. It is an array of shorts (int16) with as many shorts as there are textures being manipulated by the animation. The reason this is called a "remap" table is that it appears that these shorts tell the game to use a different set of material data than the data at the raw index of the texture itself.
For an example, imagine that there are 3 textures and 3 shorts in the remap table. The short for texture 1, located at remap_table might be 0, and the short for texture 3, located at remap_table might be 2, but the short for texture 2, at remap_table, might actually be 4 instead of 1. The "remap" value seems to tell the game to use different material data.
String Table and Texture Index Table
Together, the string table and the texture index table specify what texture in what material to manipulate. The string table gives the name of the material, and the texture index table gives the index of the texture within that material.
To get the index of the texture to manipulate, simply access the texture index table with the same index as the material you want to operate on from the string table.
The string table here uses the same format as the string table found in the BMD and BDL model format. More information can be found in the article describing them.
Texture Index Table
The texture index table is simply an array of bytes indicating the index of a texture within its corresponding material in the string table. It is padded to the nearest 8 bytes with the standard padding string.
Texture Center Table
This table contains Vector3's representing the U, V, and W components of the center point of each texture being animated. All operations will be applied relative to this point. Each Vector3 follows the format below.
//0x0C/12 bytes long /*0x00*/ float UComponent; /*0x04*/ float VComponent; /*0x08*/ float WComponent;
These banks hold the key data referenced by the keyframes. The banks for scaling and translation data use floats, while the bank for rotation data uses shorts (int16).
The values in the rotation bank are stored as shorts as part of a compression format for rotation values. To get the actual angle of rotation, you must multiply the short by 180.0f and then divide the result by 32767.0f.
float degrees = ((float)value * 180.0f) / 32767.0f;
Unlike the BCK format, which stores keys with their timestamp, actual value, and tangent value, key data in the BTK format is stored only as the actual value. Additionally, there does not seem to be a "default" value at the beginning of each bank, as there is in the BCK format.