Difference between revisions of "Texture Native Struct"

From GTAMods Wiki
Jump to navigation Jump to search
(fixing some PS2 and general stuff)
m (PlayStation 2 Header)
Line 68: Line 68:
  
 
     //size: 64 bytes
 
     //size: 64 bytes
 +
    // Wrapped in a CHUNK_STRUCT
 
     struct RasterFormat
 
     struct RasterFormat
 
     {
 
     {

Revision as of 05:50, 26 January 2015

{{{NAME}}}
RenderWare Stream Section
Vendor {{{VENDORNAME}}}
Module {{{MODULENAME}}}
Module ID 0x{{{MODULEID}}}
Identifier 0x{{{IDENTIFIER}}}
Chunk ID 0x{{{MODULEID}}}{{{IDENTIFIER}}}
Versions All
Hierarchy
Parents:
None
Children:
None
Extensions:
None
File Format

This article is about the Struct section accompanied by a Texture Native parent section.

The Texture Native Struct section can usually be found in texture archives used by the PC, XBOX and PS2 versions of the GTA III game trilogy. They are the most important sections inside a .txd file since they contain the names, dimensions and the actual image data for textures. The typical location in the file's section hierarchy is Texture Dictionary > Texture Native > Struct.

Images Header

Direct3D Header

There is a 86 byte header with general image information.

The name and maskName fields are strings that are expected to be zero terminated. This allows a maximum string length of 31 bytes (last byte taken by '\0').

struct NativeTexturePC_Header
{
    struct {
        unsigned int platformId;
        unsigned int filterMode : 8;
        unsigned int uAddressing : 4;
        unsigned int vAddressing : 4;
        unsigned int pad : 16;        // should be zeroed out for sanity
        char name[32];
        char maskName[32];
    } TextureFormat;

    struct {
        unsigned int rasterFormat;
        union {
            D3DFORMAT d3dFormat; // SA
            unsigned int hasAlpha // GTA3 & VC
        };
        unsigned short width;
        unsigned short height;
        unsigned char depth;
        unsigned char numLevels;
        unsigned char rasterType;
        union {
            unsigned char compression; // GTA3 & VC
            struct { // SA
                unsigned char alpha : 1;
                unsigned char cubeTexture : 1;
                unsigned char autoMipMaps : 1;
                unsigned char compressed : 1;
            };
        };
    } RasterFormat;
};

PlayStation 2 Header

The PlayStation 2 architecture uses header data of dynamic size. While the name and maskName fields are 32 bytes in length each on the Direct3D architecture, here they can be any size. It is not recommended to overshoot 32 bytes though, as there will be cross-platform compatibility issues.

It is important to align all data to 4 bytes, since the PlayStation 2 implementation may fail to read your data properly otherwise.

struct NativeTexturePS2_Header
{
    struct {
        unsigned int platformId;
        unsigned int filterMode : 8;
        unsigned int uAddressing : 4;
        unsigned int vAddressing : 4;
        unsigned int pad : 16;         // should be zero'ed out for sanity
    } TextureFormat;

    // CHUNK_STRING for name (aligned to 4 bytes)
    // CHUNK_STRING for alpha-mask name (aligned to 4 bytes)

    //size: 64 bytes
    // Wrapped in a CHUNK_STRUCT
    struct RasterFormat
    {
        unsigned int width, height;
        unsigned int depth;
        unsigned int rasterFormat;
        
        // See http://mtz01-a.stanford.edu/resources/SonyPS2/pdf00005.pdf
        unsigned long long tex0;     // PS2 TEX0 GS register
        unsigned long long tex1;     // PS2 TEX1 GS register
        unsigned long long miptbp1;  // PS2 MIPTBP1 GS register
        unsigned long long miptbp2;  // PS2 MIPTBP2 GS register

        unsigned int texelDataSectionSize;    // stream size of the mipmap data
        unsigned int paletteDataSectionSize;  // stream size of the palette data (zero if no palette)
        unsigned int gpuDataAlignedSize;      // memory span of the texture mipmap data on the GS (aligned to pages/2048)

        unsigned int skyMipmapVal;    // always 4032
    };
};

Description

Platform ID
This is 8 for GTA3 and VC on the PC, 9 for GTA SA on the PC, PS2\0 FourCC on PS2 (any game version), and 5 for GTA3/VC/SA on the XBOX.
Filter Mode
FILTER_NONE                0x00
FILTER_NEAREST             0x01
FILTER_LINEAR              0x02
FILTER_MIP_NEAREST         0x03
FILTER_MIP_LINEAR          0x04
FILTER_LINEAR_MIP_NEAREST  0x05
FILTER_LINEAR_MIP_LINEAR   0x06
Addressing Mode
WRAP_NONE     0x00
WRAP_WRAP     0x01
WRAP_MIRROR   0x02
WRAP_CLAMP    0x03
Raster Format
FORMAT_DEFAULT         0x0000
FORMAT_1555            0x0100 (1 bit alpha, RGB 5 bits each; also used for DXT1 with alpha)
FORMAT_565             0x0200 (5 bits red, 6 bits green, 5 bits blue; also used for DXT1 without alpha)
FORMAT_4444            0x0300 (RGBA 4 bits each; also used for DXT3)
FORMAT_LUM8            0x0400 (gray scale)
FORMAT_8888            0x0500 (RGBA 8 bits each)
FORMAT_888             0x0600 (RGB 8 bits each, D3DFMT_X8R8G8B8)
FORMAT_555             0x0A00 (RGB 5 bits each - rare, use 565 instead, D3DFMT_X1R5G5B5)

FORMAT_EXT_AUTO_MIPMAP 0x1000 (RW has generated mipmaps)
FORMAT_EXT_PAL8        0x2000 (2^8 = 256 palette colors)
FORMAT_EXT_PAL4        0x4000 (2^4 = 16 palette colors)
FORMAT_EXT_MIPMAP      0x8000 (mipmaps included)
Depth
4, 8, 16 or 32; 4 and 8 usually come with palette
Compression
for more information, refer to S3 Texture Compression.

Raster Data

The header is followed by an optional palette and the raster data (pixels), according to the Raster Format flags.

If Raster Format is FORMAT_EXT_PAL8 | FORMAT_8888 or FORMAT_EXT_PAL8 | FORMAT_888, then a color palette of (up to) 256 RGBA colors is included. This is used in GTA3 quite often. The raster's one byte for each pixel is an index into the palette.

1024 byte  - BYTE[256][4] - palette of RGBA colors
4 byte     - DWORD        - raster size
w*h*1 byte - BYTE[w*h]    - pixels, 1 byte each

NOTE: palette colors use RGBA color order!

If no palette is included, the header is followed by raster size and data. If mipmaps are included (FORMAT_EXT_MIPMAP) then this is repeated for each mipmap. With increasing mipmap levels the raster size is decreased by the factor 4 (side lengths halved). Small initial textures can result in mipmaps of the size 0. For uncompressed images:

4 byte         - DWORD            - raster size
w*h*bpp/8 byte - BYTE[w*h][bpp/8] - pixels

WARNING: RASTER_888(8) uses BGRA color order!

If DXT compression is used, blocks of 4x4 pixels occupy only 8 or 16 bytes (depending on the type).

Color Ordering

The order of the RGBA colors depends on the platform.

  • PlayStation 2 uses RGBA colors
  • Direct3D 9 uses BGRA or RGBA depending on the D3DFORMAT field

For example, let's investigate how to convert color formats from PS2 to Direct3D. Since RASTER_8888 always is RGBA on the PlayStation 2, the color struct should look like this.

struct raster8888_texel_rgba
{
    uint8 red;
    uint8 green;
    uint8 blue;
    uint8 alpha;
};

Now we have two choices.

  • Use D3DFMT_A8B8G8R8 so the texel data is correctly ordered already, but with less compatibility with GTA community tools.
  • Use D3DFMT_A8R8G8B8 and swap red with blue.

If we go for the latter, the new color struct should look like this.

struct raster8888_texel_bgra
{
    uint8 blue;
    uint8 green;
    uint8 red;
    uint8 alpha;
};

The conversion between RGBA to BGRA can be performed using the following algorithm.

void convertRGBA2BGRA(void *theTexels, uint32 texelCount)
{
    for (uint32 n = 0; n < texelCount; n++)
    {
        uint8 r, g, b, a;

        // Read the color from the texels.
        {
            raster8888_texel_rgba *rgba8888 = (raster8888_texel_rgba*)theTexels + n;

            r = rgba8888->red;
            g = rgba8888->green;
            b = rgba8888->blue;
            a = rgba8888->alpha;
        }

        // Write back the color in a different ordering.
        {
            raster8888_texel_bgra *bgra8888 = (raster8888_texel_bgra*)theTexels + n;

            bgra8888->red = r;
            bgra8888->green = g;
            bgra8888->blue = b;
            bgra8888->alpha = a;
        }
    }
}

PlayStation 2

Example of texture allocation.

The image and palette data is both handled in a (generic) texture format. There are special raster flags reserved.

#define RASTER_SWIZZLED          0x10000    // the images are "swizzled"
#define RASTER_HAS_TEXEL_HEADERS 0x20000    // the image data is preceeded by headers

If the rasterFlags contain the RASTER_HAS_TEXEL_HEADERS flag, then each image data and palette data are a chain of GIF packets that are directly uploaded to the GS. Each mipmap consists of one register list and the image data. The GIFtag struct that is the header of each packet could look like this.

struct GIFtag
{
    unsigned long long nloop : 15;
    unsigned long long eop : 1;
    unsigned long long pad1 : 30;
    unsigned long long pre : 1;
    unsigned long long prim : 11;
    unsigned long long flg : 2;
    unsigned long long nreg : 4;
    
    unsigned long long regs;

    uint32 getRegisterID(uint32 i) const
    {
        assert(i < 16);

        unsigned long long shiftPos = i * 4;

        return ( this->regs & ( 0xF << shiftPos ) ) >> shiftPos;
    }

    void setRegisterID(uint32 i, uint32 regContent)
    {
        assert(i < 16);

        unsigned long long shiftPos = i * 4;

        this->regs &= ~( 0xF << shiftPos );

        this->regs |= regContent >> shiftPos;
    }
};

By default, each texture that is made of GIF packets has the TRXPOS, TRXREG and TRXDIR registers including the TEX0, TEX1, MIPTBP1 and MIPTBP2 registers from the native header. This practically makes the image and palette headers look like this.

// Note: this is just an illustration, not an actual header.
struct commonTexelHeader
{
    GIFtag regListTag;
    /*
        regListTag.flg = 0;
        regListTag.eop = false;
        regListTag.pre = false;
        regListTag.prim = 0;
        regListTag.nreg = 1;
        regListTag.regs = 0;
        regListTag.setRegisterID(0, 0xE);
        regListTag.pad1 = 0;
        regListTag.nloop = 3;
    */

    unsigned long long trxpos;       // ssax = 0, ssay = 0, dsax = ?, dsay = ?, dir = 0
    unsigned long long trxpos_id;    // = 0x51

    unsigned long long trxreg;       // width = ?, height = ?
    unsigned long long trxreg_id;    // = 0x52

    unsigned long long trxdir;       // xdir = 0
    unsigned long long trxdir_id;    // = 0x53

    GIFtag imgDataTag;
    /*
        imgDataTag.flg = 2;
        imgDataTag.eop = false;
        imgDataTag.pre = false;
        imgDataTag.prim = 0;
        imgDataTag.nreg = 0;
        imgDataTag.regs = 0;
        imgDataTag.pad1 = 0;
        imgDataTag.nloop = (size of image data in bytes / 16);
    */
};

For more details, see the Emotion Engine User Manual under the GIF Interface chapter.

The actual image data is stored afterward. It is packed in either PSMCT32, PSMCT16, PSMT8 or PSMT4 internal GS types according to the documentation and game version. So before you can get usable images in linear format out of PS2 textures, you have to "unswizzle" them. This is done by permuting the data.

Related Pages