Texture data contributes to a major part of the game client size, if it is not the largest. Combined with 3D objects model, it defines the majority of appearance of the game world. Modifying texture data is necessary in order for changing game character looking or landscape appearance or even localizing UI elements. There are different types of textures. I have only added description for Texture2D, a widely used texture type for 3D meshes since most of the work I have done with texture mod is modifying appearance of the game characters/clothes/doboks, whatever you want to call it.
Texture2D is used to "color" the 3D meshes in most scenarios. I use quote because there are several different types of 2D texture. In BNS game client (Unreal 3 powered), they include diffuse(D), material mask(M), specular(S), normal(N) and emissive(E). You may refer to any 3D design book or tutorial for the meaning of those textures. For game character clothes/doboks in BNS, the texture file are located by game engine using material UPK, a similar concept as material balls in 3DS MAX. For each clothes/doboks skeleton mesh UPK, there is a corresponding material UPK, which contains one or more MarterialInstanceConstant object. Each MarterialInstanceConstant has reference to the DMNSE textures used and some additional information for rendering. So much diversion. Let come back to the Texture2D object. Like other objects, it is composed of two parts, the property table part and the data part.
The property table of Texture2D objects usually contains the following items
Format, type ByteProperty, format information of the texture data,typical value 'PF_DXT1', 'PF_DXT5', etc.
SourceFilePath, type StrProperty, used for backtracking source texture file.
SourceFileTimeStamp, type StrProperty, used for backtracking source texture file version.
SizeX, type IntProperty, width of the texture in pixel. Note this maybe not used by the engine.
SizeY, type IntProperty, height of the texture in pixel. Note this maybe not used by the engine.
MipTailBaseIdx, type IntProperty, mip index of the first section of mip data. This equals to the log 2 of the largest actual dimension of the texture data. For example, if a texture is 1024x512, the largest dimension is 1024, log 2 of 1024 is 10 (you can use any calculator to verity), so MipTailBaseIdx for this texture should be 10.
LODGroup, type ByteProperty, some meta information for the game engine, typical value:
TEXTUREGROUP_Character
TEXTUREGROUP_CharacterSpecular
TEXTUREGROUP_CharacterNormalMap
Note that SizeX and SizeY may not corresponds to the actual dimension of the texture data.
After the property table section, there comes the texture data section. In BNS, the texture data is uniformly in DirectX DDS format. This format is very convenient as it can be directly used by the DirectX without further processing, which saves texture load time. For description of DDS file and data format, you can read Programming Guide for DDS from Microsoft. Notice that that data inside the BNS UPK only includes the texture data (most of time compressed and weakly encrypted) and do not contains the DDS file header.
Before continue, I would like to explain what is mip or mipmap first. Consider the following scenario: one object in game world sometimes is very close to camera and sometime can be rather far away from camera. To allow the the zoomed in look to have a decent quality, a large dimension texture should be used; however, when the object is far away, using such big texture is a waste of memory(video card or main memory). Thus, it is nature to have a few different version of texture, with different size, to accommodate all cases: if the object is close, use the high quality texture and when it is far away use the smaller one. Mip or mipmap is the pre-computed version of this concept. Mip contains a series of the same texture in different size (usually reduced by a factor of 2 for each level and thus the log 2 calculation mentioned previously). You can find more detailed explanation of mip on wiki.
The texture are stored from the largest mip to smallest one after another in BNS UPK file. Each mip the level has a structure like this according to Gildor analysis (I am using a slightly changed version of Gildor's representation but they are equivalent)
struct MIP_LEVEL
{
FCompressedChunk chunk;
int width;
int height;
};
where the contains the real texture data for a mip level, and width and height contains the true dimension for the data.
The FCompressedChunk is an internal data structure used by unreal 3 engine, which is further dissected as follows
struct FCompressedChunk
{
FCompressedChunkInfo info;
FCompressedChunkHeader header;
uchar data[?]; //compressed (encrypted data)
};
struct FCompressedChunkInfo
{
int UncompressedOffset;
int UncompressedSize;
int CompressedOffset;
int CompressedSize;
};
struct FCompressedChunkHeader
{
int Tag; // equals to PACKAGE_FILE_TAG (0x9E2A83C1)
int BlockSize; // max size of uncompressed block = 2^17
int CompressedSize; //for the entire chunk
int UncompressedSize; //for the entire chunk
FCompressedChunkBlock Blocks[?];
};
struct FCompressedChunkBlock
{
int CompressedSize;
int UncompressedSize; // always <=BlockSize
};
The compression/encryption used is depends on