Texture Native (Секция RW) — различия между версиями

Материал из GTAModding.ru
Перейти к: навигация, поиск
(Растр и палитра)
м (Общая структура)
 
(не показаны 8 промежуточных версий 2 участников)
Строка 1: Строка 1:
 
{{RW Section|Texture Native|0x0015}}
 
{{RW Section|Texture Native|0x0015}}
  
'''Texture Native''' - секция-контейнер, используемая в [[TXD|TXD-файлах]] внутри секции [[Texture Dictionary (Секция RW)|Texture Dictionary]]. Как правило, сопровождается секцией [[Struct (Секция RW)#Texture_Native|Struct]].
+
'''Texture Native''' - секция-контейнер, используемая в [[TXD|TXD-файлах]] внутри секции [[Texture Dictionary (Секция RW)|Texture Dictionary]]. Как правило, одна или несколько таких секций, следуют после секции [[Struct (Секция RW)|Struct]] внутри контейнера [[Texture Dictionary (Секция RW)|Texture Dictionary]].
  
 
== PC / XBOX ==
 
== PC / XBOX ==
 +
* [[Texture_Native_(Секция RW)|Texture_Native]]
 +
:* [[Struct_(Секция_RW)|Struct]] - заголовок и данные изображения
 +
:* [[Extension_(Секция_RW)|Extension]] - пусто
 +
===Общая структура===
 +
Структура начинается с т.н. заголовка размером в 92 байт. Заголовок содержит общую информацию о текстуре и растре.
 +
struct NativeTexturePC_Header
 +
{
 +
    struct {
 +
        unsigned int platformId;
 +
        unsigned int filterMode : 8;
 +
        unsigned int uAddressing : 4;
 +
        unsigned int vAddressing : 4;
 +
        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;
 +
};
 +
После заголовка следует информация о палитре (если изображение использует палитру). Дальше - информация о каждой плоскости (допустим, если изображение использует мип-маппинг или является кубической текстурой) - размер и данные о текселах. Информация о плоскости начинается с 4 байт - размере данных о текселах.
 +
struct TextureFormat
 +
{
 +
    unsigned int platformId;
 +
    unsigned int filterMode : 8;
 +
    unsigned int uAddressing : 4;
 +
    unsigned int vAddressing : 4;
 +
    char name[32];
 +
    char maskName[32];
 +
};
 +
 +
struct RasterFormat
 +
{
 +
    unsigned int rasterFormat;
 +
    D3DFORMAT d3dFormat;
 +
    unsigned short width;
 +
    unsigned short height;
 +
    unsigned char depth;
 +
    unsigned char numLevels;
 +
    unsigned char rasterType;
 +
    unsigned char alpha : 1;
 +
    unsigned char cubeTexture : 1;
 +
    unsigned char autoMipMaps : 1;
 +
    unsigned char compressed : 1;
 +
};
 +
 +
VALIDATE_SIZE(TextureFormat, 72);
 +
VALIDATE_SIZE(RasterFormat, 16);
 +
 +
enum RasterFormat8
 +
{
 +
    FORMATDEFAULT = 0x00,
 +
    FORMAT1555 = 0x01,
 +
    FORMAT565 = 0x02,
 +
    FORMAT4444 = 0x03,
 +
    FORMATLUM8 = 0x04,
 +
    FORMAT8888 = 0x05,
 +
    FORMAT888 = 0x06,
 +
    FORMAT16 = 0x07,
 +
    FORMAT24 = 0x08,
 +
    FORMAT32 = 0x09,
 +
    FORMAT555 = 0x0a,
 +
    FORMATAUTOMIPMAP = 0x10,
 +
    FORMATPAL8 = 0x20,
 +
    FORMATPAL4 = 0x40,
 +
    FORMATMIPMAP = 0x80,
 +
    FORMATFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
 +
};
 +
 +
bool rwD3D9NativeTextureRead(RwStream *stream, RwTexture **textureOut)
 +
{
 +
    TextureFormat textureInfo; RasterFormat rasterInfo;
 +
    unsigned int lengthOut, versionOut, numLevels; unsigned char savedFormat;
 +
    RwRaster *raster; RwTexture *texture;
 +
 +
    // Ошибка чтения или некорректная версия
 +
    if (!RwStreamFindChunk(stream, rwID_STRUCT, &lengthOut, &versionOut) || versionOut < 0x34000 || versionOut > 0x36003 ||
 +
        RwStreamRead(stream, &textureInfo, 72) != 72 || textureInfo.platformId != rwID_PCD3D9 ||
 +
        RwStreamRead(stream, &rasterInfo, 16) != 16)
 +
        return false;
 +
 +
    // Рассматриваем 3 варианта - компрессия, кубическая текстура, компрессия+кубическая текстура
 +
    if(rasterInfo.compressed)
 +
    {
 +
        if(rwD3D9CheckValidTextureFormat(rasterInfo.d3dFormat))
 +
        {
 +
            raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth, rasterInfo.rasterFormat |
 +
                rasterInfo.rasterType | rwRASTERDONTALLOCATE);
 +
            if(!raster)
 +
                return false;
 +
        }
 +
        else return false;
 +
        numLevels = (raster->cFormat & FORMATMIPMAP) ? rasterInfo.numLevels : 1;
 +
        if(rasterInfo.cubeTexture)
 +
        {
 +
            if(!(RwD3D9DeviceCaps->CubeTextureFilterCaps & (D3DPTFILTERCAPS_MIPFPOINT|D3DPTFILTERCAPS_MIPFLINEAR)) && numLevels > 1)
 +
            {
 +
                RwRasterDestroy(raster);
 +
                return false;
 +
            }
 +
            raster->d3dRaster.isCubeMap = 1;
 +
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) &&
 +
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
 +
                raster->d3dRaster.autoMipMapping = 1;
 +
            if(RwD3DDevice->CreateCubeTexture(raster->width, numLevels, raster->d3dRaster.autoMipMapping
 +
                ? D3DUSAGE_AUTOGENMIPMAP : 0, rasterInfo.d3dFormat, D3DPOOL_MANAGED, &raster->d3dRaster.cubeTexture, NULL) != D3D_OK)
 +
            {
 +
                RwRasterDestroy(raster);
 +
                return false;
 +
            }
 +
            raster->d3dRaster.isCubeMap = 1; // ??
 +
        }
 +
        else
 +
        {
 +
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) &&
 +
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
 +
                raster->d3dRaster.autoMipMapping = 1;
 +
            if(gtaD3D9TexCreate(raster->width, raster->height, numLevels, rasterInfo.d3dFormat, &raster->d3dRaster.texture) != D3D_OK)
 +
            {
 +
                RwRasterDestroy(raster);
 +
                return false;
 +
            }
 +
        }
 +
        raster->d3dRaster.hasCompression = 1;
 +
        raster->d3dRaster.format = rasterInfo.d3dFormat;
 +
    }
 +
    else
 +
    {
 +
        if(!rasterInfo.cubeTexture)
 +
        {
 +
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP))
 +
            {
 +
                raster = RwD3D9RasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.d3dFormat, rasterInfo.rasterType |
 +
                    (rasterInfo.rasterFormat & (rwRASTERFORMATMIPMAP|rwRASTERFORMATAUTOMIPMAP)));
 +
                if(!raster)
 +
                    return false;
 +
            }
 +
            else
 +
            {
 +
                raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth,
 +
                    rasterInfo.rasterFormat | rasterInfo.rasterType);
 +
                if(!raster)
 +
                    return false;
 +
                raster->d3dRaster.hasCompression = 0; // Зачем??
 +
            }
 +
        }
 +
        else
 +
        {
 +
            raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth, rasterInfo.rasterFormat |
 +
                rasterInfo.rasterType | rwRASTERDONTALLOCATE);
 +
            if(!raster)
 +
                return false;
 +
 +
            numLevels = (raster->cFormat & FORMATMIPMAP) ? rasterInfo.numLevels : 1;
 +
            if(!(RwD3D9DeviceCaps->CubeTextureFilterCaps & (D3DPTFILTERCAPS_MIPFPOINT|D3DPTFILTERCAPS_MIPFLINEAR)) && numLevels > 1)
 +
            {
 +
                RwRasterDestroy(raster);
 +
                return false;
 +
            }
 +
            raster->d3dRaster.isCubeMap = 1;
 +
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) &&
 +
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
 +
                raster->d3dRaster.autoMipMapping = 1;
 +
            if(RwD3DDevice->CreateCubeTexture(raster->width, numLevels, raster->d3dRaster.autoMipMapping
 +
                ? D3DUSAGE_AUTOGENMIPMAP : 0, rasterInfo.d3dFormat, D3DPOOL_MANAGED, &raster->d3dRaster.cubeTexture, NULL) != D3D_OK)
 +
            {
 +
                RwRasterDestroy(raster);
 +
                return false;
 +
            }
 +
            raster->d3dRaster.isCubeMap = 1;
 +
        }
 +
    }
 +
    raster->cFlags ^= rwRASTERDONTALLOCATE;
 +
    raster->d3dRaster.hasAlpha = rasterInfo.alpha;
 +
 +
    // Если у нас что-то не совпало - ну его...
 +
    if(rasterInfo.autoMipMaps && !raster->d3dRaster.autoMipMapping || rasterInfo.rasterFormat != ((raster->cFormat) << 8) ||
 +
        rasterInfo.d3dFormat != raster->d3dRaster.format || rasterInfo.width != raster->width || rasterInfo.height != raster->height)
 +
    {
 +
        RwRasterDestroy(raster);
 +
        return false;
 +
    }
 +
 +
    // Читаем палитру, если надо
 +
    if(rasterInfo.rasterFormat & rwRASTERFORMATPAL4)
 +
    {
 +
        if( RwStreamRead(stream, RwRasterLockPalette(raster, rwRASTERLOCKWRITE), 128) != 128)
 +
        {
 +
            RwRasterUnlockPalette(raster);
 +
            RwRasterDestroy(raster);
 +
            return false;
 +
        }
 +
    }
 +
    else if(rasterInfo.rasterFormat & rwRASTERFORMATPAL8)
 +
    {
 +
        if(RwStreamRead(stream, RwRasterLockPalette(raster, rwRASTERLOCKWRITE), 1024) != 1024)
 +
        {
 +
            RwRasterUnlockPalette(raster);
 +
            RwRasterDestroy(raster);
 +
            return false;
 +
        }
 +
    }
 +
    RwRasterUnlockPalette(raster);
 +
   
 +
    // Читаем сорфейсы
 +
    savedFormat = raster->cFormat; // Зачем-то выключаем автомипмаппинг на время чтения
 +
    raster->cFormat ^= FORMATAUTOMIPMAP;
 +
    for(int i = 0; i < (raster->d3dRaster.cubeTexture ? 6 : 1); i++)
 +
    {
 +
        raster->d3dRaster.selectedCubeFace = 0;
 +
        for(int j = 0; j < (raster->d3dRaster.autoMipMapping ? 1 : rasterInfo.numLevels); j++)
 +
        {
 +
            if(RwStreamRead(stream, &lengthOut, 4) == 4)
 +
            {
 +
                if(RwStreamRead(stream, RwRasterLock(raster, j, rwRASTERLOCKWRITE), lengthOut) == lengthOut)
 +
                {
 +
                    RwRasterUnlock(raster);
 +
                    continue;
 +
                }
 +
            }
 +
            RwRasterUnlock(raster);
 +
            RwRasterDestroy(raster);
 +
            return false;
 +
        }
 +
    }
 +
    raster->cFormat = savedFormat;
 +
 +
    texture = RwTextureCreate(raster);
 +
    if(!texture)
 +
    {
 +
        RwRasterDestroy(raster);
 +
        return false;
 +
    }
 +
    texture->filter = textureInfo.filterMode;
 +
    texture->uAddressing = textureInfo.uAddressing;
 +
    texture->vAddressing = textureInfo.vAddressing;
 +
    RwTextureSetName(texture, textureInfo.name);
 +
    RwTextureSetMaskName(texture, textureInfo.maskName);
 +
    *textureOut = texture;
 +
    return true;
 +
}
 
{{Заготовка}}
 
{{Заготовка}}
  
Строка 81: Строка 339:
 
  0x20  rwRASTERPIXELLOCKEDRAW      - the pixels are in platform specific
 
  0x20  rwRASTERPIXELLOCKEDRAW      - the pixels are in platform specific
 
* Также, почти все (на практике - все) текстуры имеют флаги 0x020000 и 0x04. Второй из них и означает "текстура" (есть ещё камера и ещё чё-то). Первый отвечает за фоормат структуры, содержащей растр:
 
* Также, почти все (на практике - все) текстуры имеют флаги 0x020000 и 0x04. Второй из них и означает "текстура" (есть ещё камера и ещё чё-то). Первый отвечает за фоормат структуры, содержащей растр:
  0x000000 - Изображение и палитра предваряются 80-байтовыми непонятными блоками.
+
  0x000000 - Swizzling не используется
  0x010000 - Только растр и палитра
+
  0x010000 - Swizzling используется
  0x020000 - Растр и палитра (и мипмапы) предваряются информационными блоками.
+
  0x020000 - Растр и палитра (и мипмапы) предваряются информационными блоками. Swizzling может не использоваться.
Первые два флага встречаются только в [[GTA 3]], всегов нескольких файлах.
+
Первые два флага встречаются только в [[GTA 3]], всегов нескольких файлах. В третьем флажке [[TIM2#Swizzling|swizzling]] может использоваться (а может и нет) в зависимости от других флагов и/или информационных блоков.
  
 
===Растр и палитра===
 
===Растр и палитра===
Строка 90: Строка 348:
 
Практически всегда изображение, имеющее палитру, находится под [[TIM2#Swizzling|swizzling]]'ом. Исключение составляют, например, 4-битные текстуры [[GTA 3]]. Алгоритм [[TIM2#Swizzling|swizzling]]'а одинаков и для 4 и для 8 бит (по крайней мерев RenderWare). Текстуры, имеющие глубину цвета более 8 (т.е. беспалитровые 16, 32 и, в теории, 24-битные текстуры) не используют swizzling.<br />
 
Практически всегда изображение, имеющее палитру, находится под [[TIM2#Swizzling|swizzling]]'ом. Исключение составляют, например, 4-битные текстуры [[GTA 3]]. Алгоритм [[TIM2#Swizzling|swizzling]]'а одинаков и для 4 и для 8 бит (по крайней мерев RenderWare). Текстуры, имеющие глубину цвета более 8 (т.е. беспалитровые 16, 32 и, в теории, 24-битные текстуры) не используют swizzling.<br />
 
Определить наличие swizzling'а на данный момент удаётся лишь сравнивая размеры в структуре информации и в блоке информации (см. ниже).<br />
 
Определить наличие swizzling'а на данный момент удаётся лишь сравнивая размеры в структуре информации и в блоке информации (см. ниже).<br />
В RenderWare начиная с версии 3.4 или 3.5 используются мипмапы. Мипмапы могут быть сгенерированы и сохранены заранее, наподобие того, как это часто используется в [[GTA SA|San Andreas]]. Каждый мипмап имеет заголовок наподобие оригинального растра и использует ту же палитру. После растра и мипмапов идёт палитра.
+
В RenderWare начиная с версии 3.4 или 3.5 используются мипмапы. Мипмапы могут быть сгенерированы и сохранены заранее, наподобие того, как это часто используется в PC-версии [[GTA SA|San Andreas]]. Каждый мипмап имеет заголовок наподобие оригинального растра и использует ту же палитру. После растра и мипмапов идёт палитра.
 
<br />
 
<br />
  <b>Совет:</b> удобней считать палитру, отсчитав её размер от конца структуры. Размер палитры равен <code>4 x 2<sup>depth</sup></code> плюс 32, если текстура 4-битная и под swizzling'ом.
+
  <b>Совет:</b> удобней считать палитру, отсчитав её размер от конца структуры. Размер палитры равен <code>4 x 2<sup>depth</sup></code>.
 +
Если текстура 4-битная и под swizzling'ом, то палитра содержит ещё 32 дополнительных байта!
  
 
===Блоки информация об изображении / палитре (заголовок палитры / изображения)===
 
===Блоки информация об изображении / палитре (заголовок палитры / изображения)===
Информация хранится в 80-байтовом блоке в любом из этих случаев. Здесь всегда содержится информация об размере последующей структуры (по всей видимости, размер выражен в 16-байтовых блоках).<br />
+
Если флаг условно-swizzling'а равен 0x020000, то изображение, палитра и мипмапы (если есть) предваряется инфо-блоками размером 80 байт.
Важно отметить, что некоторые текстуры не содержат этого блока вообще. Например, CAS_WOM.TXD в [[GTA 3]].
+
В любом из этих случаев, используется схожая структура. Здесь всегда содержится информация об размере последующих данных (размер выражен в 16-байтовых блоках).<br />
<br />{{Заготовка}}
+
Исходя из этих данных можно узнать наличие swizzling'а (но это не основной признак его наличия): если высота и ширина изображения указаны в да раза меньше тех, что в основной структуре информации, то текстура находится под swizzling'ом. Как следствие, в этом способе есть проблемы с текстурами, разрешение которых хотя бы одним из параметров меньше или равно 16px.<br />
 +
{{Заготовка}}
  
 
==Смотри также==
 
==Смотри также==

Текущая версия на 00:32, 11 апреля 2014

Секция RenderWare
Texture Native
0x0015

Texture Native - секция-контейнер, используемая в TXD-файлах внутри секции Texture Dictionary. Как правило, одна или несколько таких секций, следуют после секции Struct внутри контейнера Texture Dictionary.

Содержание

PC / XBOX

  • Texture_Native
  • Struct - заголовок и данные изображения
  • Extension - пусто

Общая структура

Структура начинается с т.н. заголовка размером в 92 байт. Заголовок содержит общую информацию о текстуре и растре.

struct NativeTexturePC_Header
{
    struct {
        unsigned int platformId;
        unsigned int filterMode : 8;
        unsigned int uAddressing : 4;
        unsigned int vAddressing : 4;
        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;
};

После заголовка следует информация о палитре (если изображение использует палитру). Дальше - информация о каждой плоскости (допустим, если изображение использует мип-маппинг или является кубической текстурой) - размер и данные о текселах. Информация о плоскости начинается с 4 байт - размере данных о текселах.

struct TextureFormat
{
    unsigned int platformId;
    unsigned int filterMode : 8;
    unsigned int uAddressing : 4;
    unsigned int vAddressing : 4;
    char name[32];
    char maskName[32];
};

struct RasterFormat
{
    unsigned int rasterFormat;
    D3DFORMAT d3dFormat;
    unsigned short width;
    unsigned short height;
    unsigned char depth;
    unsigned char numLevels;
    unsigned char rasterType;
    unsigned char alpha : 1;
    unsigned char cubeTexture : 1;
    unsigned char autoMipMaps : 1;
    unsigned char compressed : 1; 
};

VALIDATE_SIZE(TextureFormat, 72);
VALIDATE_SIZE(RasterFormat, 16);

enum RasterFormat8
{
    FORMATDEFAULT = 0x00,
    FORMAT1555 = 0x01,
    FORMAT565 = 0x02,
    FORMAT4444 = 0x03,
    FORMATLUM8 = 0x04,
    FORMAT8888 = 0x05,
    FORMAT888 = 0x06,
    FORMAT16 = 0x07,
    FORMAT24 = 0x08,
    FORMAT32 = 0x09,
    FORMAT555 = 0x0a,
    FORMATAUTOMIPMAP = 0x10,
    FORMATPAL8 = 0x20,
    FORMATPAL4 = 0x40,
    FORMATMIPMAP = 0x80,
    FORMATFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
};

bool rwD3D9NativeTextureRead(RwStream *stream, RwTexture **textureOut)
{
    TextureFormat textureInfo; RasterFormat rasterInfo;
    unsigned int lengthOut, versionOut, numLevels; unsigned char savedFormat;
    RwRaster *raster; RwTexture *texture;

    // Ошибка чтения или некорректная версия
    if (!RwStreamFindChunk(stream, rwID_STRUCT, &lengthOut, &versionOut) || versionOut < 0x34000 || versionOut > 0x36003 || 
        RwStreamRead(stream, &textureInfo, 72) != 72 || textureInfo.platformId != rwID_PCD3D9 || 
        RwStreamRead(stream, &rasterInfo, 16) != 16)
        return false;

    // Рассматриваем 3 варианта - компрессия, кубическая текстура, компрессия+кубическая текстура
    if(rasterInfo.compressed)
    {
        if(rwD3D9CheckValidTextureFormat(rasterInfo.d3dFormat))
        {
            raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth, rasterInfo.rasterFormat | 
                rasterInfo.rasterType | rwRASTERDONTALLOCATE);
            if(!raster)
                return false;
        }
        else return false;
        numLevels = (raster->cFormat & FORMATMIPMAP) ? rasterInfo.numLevels : 1;
        if(rasterInfo.cubeTexture)
        {
            if(!(RwD3D9DeviceCaps->CubeTextureFilterCaps & (D3DPTFILTERCAPS_MIPFPOINT|D3DPTFILTERCAPS_MIPFLINEAR)) && numLevels > 1)
            {
                RwRasterDestroy(raster);
                return false;
            }
            raster->d3dRaster.isCubeMap = 1;
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) && 
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
                raster->d3dRaster.autoMipMapping = 1;
            if(RwD3DDevice->CreateCubeTexture(raster->width, numLevels, raster->d3dRaster.autoMipMapping 
                ? D3DUSAGE_AUTOGENMIPMAP : 0, rasterInfo.d3dFormat, D3DPOOL_MANAGED, &raster->d3dRaster.cubeTexture, NULL) != D3D_OK)
            {
                RwRasterDestroy(raster);
                return false;
            }
            raster->d3dRaster.isCubeMap = 1; // ??
        }
        else
        {
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) && 
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
                raster->d3dRaster.autoMipMapping = 1;
            if(gtaD3D9TexCreate(raster->width, raster->height, numLevels, rasterInfo.d3dFormat, &raster->d3dRaster.texture) != D3D_OK)
            {
                RwRasterDestroy(raster);
                return false;
            }
        }
        raster->d3dRaster.hasCompression = 1;
        raster->d3dRaster.format = rasterInfo.d3dFormat;
    }
    else
    {
        if(!rasterInfo.cubeTexture)
        {
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP))
            {
                raster = RwD3D9RasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.d3dFormat, rasterInfo.rasterType | 
                    (rasterInfo.rasterFormat & (rwRASTERFORMATMIPMAP|rwRASTERFORMATAUTOMIPMAP)));
                if(!raster)
                    return false;
            }
            else
            {
                raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth,
                   rasterInfo.rasterFormat | rasterInfo.rasterType);
                if(!raster)
                    return false;
                raster->d3dRaster.hasCompression = 0; // Зачем??
            }
        }
        else
        {
            raster = RwRasterCreate(rasterInfo.width, rasterInfo.height, rasterInfo.depth, rasterInfo.rasterFormat | 
                rasterInfo.rasterType | rwRASTERDONTALLOCATE);
            if(!raster)
                return false;

            numLevels = (raster->cFormat & FORMATMIPMAP) ? rasterInfo.numLevels : 1;
            if(!(RwD3D9DeviceCaps->CubeTextureFilterCaps & (D3DPTFILTERCAPS_MIPFPOINT|D3DPTFILTERCAPS_MIPFLINEAR)) && numLevels > 1)
            {
                RwRasterDestroy(raster);
                return false;
            }
            raster->d3dRaster.isCubeMap = 1;
            if((rasterInfo.rasterFormat & rwRASTERFORMATMIPMAP) && (rasterInfo.rasterFormat & rwRASTERFORMATAUTOMIPMAP) && 
                rwD3D9CheckAutoMipmapGenCubeTextureFormat(rasterInfo.d3dFormat))
                raster->d3dRaster.autoMipMapping = 1;
            if(RwD3DDevice->CreateCubeTexture(raster->width, numLevels, raster->d3dRaster.autoMipMapping 
                ? D3DUSAGE_AUTOGENMIPMAP : 0, rasterInfo.d3dFormat, D3DPOOL_MANAGED, &raster->d3dRaster.cubeTexture, NULL) != D3D_OK)
            {
                RwRasterDestroy(raster);
                return false;
            }
            raster->d3dRaster.isCubeMap = 1;
        }
    }
    raster->cFlags ^= rwRASTERDONTALLOCATE;
    raster->d3dRaster.hasAlpha = rasterInfo.alpha;

    // Если у нас что-то не совпало - ну его...
    if(rasterInfo.autoMipMaps && !raster->d3dRaster.autoMipMapping || rasterInfo.rasterFormat != ((raster->cFormat) << 8) || 
        rasterInfo.d3dFormat != raster->d3dRaster.format || rasterInfo.width != raster->width || rasterInfo.height != raster->height)
    {
        RwRasterDestroy(raster);
        return false;
    }

    // Читаем палитру, если надо
    if(rasterInfo.rasterFormat & rwRASTERFORMATPAL4)
    {
        if( RwStreamRead(stream, RwRasterLockPalette(raster, rwRASTERLOCKWRITE), 128) != 128)
        {
            RwRasterUnlockPalette(raster);
            RwRasterDestroy(raster);
            return false;
        }
    }
    else if(rasterInfo.rasterFormat & rwRASTERFORMATPAL8)
    {
        if(RwStreamRead(stream, RwRasterLockPalette(raster, rwRASTERLOCKWRITE), 1024) != 1024)
        {
            RwRasterUnlockPalette(raster);
            RwRasterDestroy(raster);
            return false;
        }
    }
    RwRasterUnlockPalette(raster);
    
    // Читаем сорфейсы
    savedFormat = raster->cFormat; // Зачем-то выключаем автомипмаппинг на время чтения
    raster->cFormat ^= FORMATAUTOMIPMAP;
    for(int i = 0; i < (raster->d3dRaster.cubeTexture ? 6 : 1); i++)
    {
        raster->d3dRaster.selectedCubeFace = 0;
        for(int j = 0; j < (raster->d3dRaster.autoMipMapping ? 1 : rasterInfo.numLevels); j++)
        {
            if(RwStreamRead(stream, &lengthOut, 4) == 4)
            {
                if(RwStreamRead(stream, RwRasterLock(raster, j, rwRASTERLOCKWRITE), lengthOut) == lengthOut)
                {
                    RwRasterUnlock(raster);
                    continue;
                }
            }
            RwRasterUnlock(raster);
            RwRasterDestroy(raster);
            return false;
        }
    }
    raster->cFormat = savedFormat;

    texture = RwTextureCreate(raster);
    if(!texture)
    {
        RwRasterDestroy(raster);
        return false;
    }
    texture->filter = textureInfo.filterMode;
    texture->uAddressing = textureInfo.uAddressing;
    texture->vAddressing = textureInfo.vAddressing;
    RwTextureSetName(texture, textureInfo.name);
    RwTextureSetMaskName(texture, textureInfo.maskName);
    *textureOut = texture;
    return true;
}

PlayStation 2

  • Texture Native
  • Struct - 4 байта - Платформа (PS2), 4байта - Флаги Фильтра
  • String - Название текстуры (размер кратен 4 байтам)
  • String - Название альфа-канала (размер кратен 4 байтам)
  • Struct
  • Struct - 4 байта ширина, 4 байта - высота, 4 байта - глубина цвета (bpp), далее - неизвестно
  • Struct - 80 байт - информация о изображении, растр, (80 байт - информация о палитре), палитра

Информация ниже находится на этапе эксперементирования и проверки, а, следовательно, может оказаться неточной.

Платформа и фильтр

Информация о платформе и фильтрах хранится в стандартной 8-байтовой структуре:

BYTE[4] - 4 байта - Строка "PS2" с завершающим нулём
DWORD   - 4 байта - Флажки фильтра и UV Wrapping

Флажки фильтра (RwTextureFilterMode) предствалены ниже.
Использовать можно только один из фильтров.

0x01  rwFILTERNEAREST            - Point sampled
0x02  rwFILTERLINEAR             - Bilinear
0x03  rwFILTERMIPNEAREST         - Point sampled per pixel mip map
0x04  rwFILTERMIPLINEAR          - Bilinear per pixel mipmap
0x05  rwFILTERLINEARMIPNEAREST   - MipMap interp point sampled
0x06  rwFILTERLINEARMIPLINEAR    - Trilinear

По всей видимости, флажки UV на этапе разработки могут занимать по байту каждый, но в конечной версии используется лишь 1 байт под обе координаты. Можно добавить, что лишь пара-тройка текстур имеет этот байт отличный от 0x11.

Ниже предсталены флажки UV. По идее, wrapping может быть не использован вообще - т.е. 0x00.

0x00  rwTEXTUREADDRESSNATEXTUREADDRESS - ??? (вроде как N/A)
0x01  rwTEXTUREADDRESSWRAP       - UV wraps (tiles)
0x02  rwTEXTUREADDRESSMIRROR     - Alternate UV is flipped
0x03  rwTEXTUREADDRESSCLAMP      - UV is clamped to 0-1
0x04  rwTEXTUREADDRESSBORDER     - Border color takes effect outside of 0-1

Основная структура информации

Здесь содержится вся или практически вся информация об изображении.

DWORD   - 4 байта - Ширина изображения в пикселях
DWORD   - 4 байта - Высота изображения в пикселях
DWORD   - 4 байта - Глубина цвета (bpp)
DWORD   - 2 байта - Флаги изображения
BYTE[7] - 7 байт  - неизвестно
BYTE    - 1 байт  - Тип палитры или что-то в этом роде (всегда равно 0x32, если палитры нет - всегда ноль и последующие 8 байт тоже нули)
WORD[4] - 8 байт  - Флаги палитры (?) (TODO)
...
DWORD - 4 байта - дубль секции SkyMipMapVal, на деле - оба значения всегда равны 0x0FC0 (TODO)

Флаги изображения

Это обычные флаги RenderWare Graphics, соеденённые в двойном слове (DWORD) при помощи побитового AND:

  • RwRasterFormat (Как правило, встречаются лишь RGBA, 8bit pal и 4bit pal, но приведу остальные, которые могут встретится):
Одновременно может использоваться только один флаг из каждой "группы" (группы флажков тут условно разделены пустой строкой).
0x0100  rwRASTERFORMAT1555        - 16 бит  - 1 бит альфа, по 5 бит на красный, зелёный и синий (5-5-5, только первый бит - альфа)
0x0200  rwRASTERFORMAT565         - 16 бит  - 5 бит - красный, 6 бит  - зелёный, 5 бит - синий (5-6-5)
0x0300  rwRASTERFORMAT4444        - 16 бит  - по 4 бита на красный, зелёный и синий
0x0400  rwRASTERFORMATLUM8        -  8 бит  - белый (красный = зелёный = синий)
0x0500  rwRASTERFORMAT8888        - 32 бита - 8 bits per component
0x0600  rwRASTERFORMAT888         - 24 бита - 8 bits per component
...
0x0A00  rwRASTERFORMAT555         - 16 бит  - по 5 бит на красный, зелёный и синий (обычный 5-5-5)

0x1000  rwRASTERFORMATAUTOMIPMAP  - RenderWare генерирует мипмапы автоматически
 
0x2000  rwRASTERFORMATPAL8        - 8 бит с палитрой (в этих двух случаях тип палитры указан с помощью флагов выше)
0x4000  rwRASTERFORMATPAL4        - 4 бит с палитрой
 
0x8000  rwRASTERFORMATMIPMAP      - Флаг наличия мипмапов.
  • RwRasterPrivateFlag:
Здесь я в данный момент не уверен, но если значение равно единице - нужно как-то корректировать палитру (или наоборот - не корректировать). Пока что привожу с оригинальными комментами разработчиков.
0x01  rwRASTERGAMMACORRECTED
0x02  rwRASTERPIXELLOCKEDREAD     - pixels are locked for reading
0x04  rwRASTERPIXELLOCKEDWRITE    - pixels are locked for writing
0x08  rwRASTERPALETTELOCKEDREAD   - palette is locked for reading
0x10  rwRASTERPALETTELOCKEDWRITE  - palette is locked for writing
0x20  rwRASTERPIXELLOCKEDRAW      - the pixels are in platform specific
  • Также, почти все (на практике - все) текстуры имеют флаги 0x020000 и 0x04. Второй из них и означает "текстура" (есть ещё камера и ещё чё-то). Первый отвечает за фоормат структуры, содержащей растр:
0x000000 - Swizzling не используется
0x010000 - Swizzling используется
0x020000 - Растр и палитра (и мипмапы) предваряются информационными блоками. Swizzling может не использоваться.

Первые два флага встречаются только в GTA 3, всегов нескольких файлах. В третьем флажке swizzling может использоваться (а может и нет) в зависимости от других флагов и/или информационных блоков.

Растр и палитра

Само изображение хранится в растровом формате, характерном для Sony PlayStation 2, так сокращаемом называемом TIM2. Заголовки изображения и файла не используется. (Подробнее о самом формате можно почитать в отдельной статье).
Практически всегда изображение, имеющее палитру, находится под swizzling'ом. Исключение составляют, например, 4-битные текстуры GTA 3. Алгоритм swizzling'а одинаков и для 4 и для 8 бит (по крайней мерев RenderWare). Текстуры, имеющие глубину цвета более 8 (т.е. беспалитровые 16, 32 и, в теории, 24-битные текстуры) не используют swizzling.
Определить наличие swizzling'а на данный момент удаётся лишь сравнивая размеры в структуре информации и в блоке информации (см. ниже).
В RenderWare начиная с версии 3.4 или 3.5 используются мипмапы. Мипмапы могут быть сгенерированы и сохранены заранее, наподобие того, как это часто используется в PC-версии San Andreas. Каждый мипмап имеет заголовок наподобие оригинального растра и использует ту же палитру. После растра и мипмапов идёт палитра.

Совет: удобней считать палитру, отсчитав её размер от конца структуры. Размер палитры равен 4 x 2depth.
Если текстура 4-битная и под swizzling'ом, то палитра содержит ещё 32 дополнительных байта!

Блоки информация об изображении / палитре (заголовок палитры / изображения)

Если флаг условно-swizzling'а равен 0x020000, то изображение, палитра и мипмапы (если есть) предваряется инфо-блоками размером 80 байт. В любом из этих случаев, используется схожая структура. Здесь всегда содержится информация об размере последующих данных (размер выражен в 16-байтовых блоках).
Исходя из этих данных можно узнать наличие swizzling'а (но это не основной признак его наличия): если высота и ширина изображения указаны в да раза меньше тех, что в основной структуре информации, то текстура находится под swizzling'ом. Как следствие, в этом способе есть проблемы с текстурами, разрешение которых хотя бы одним из параметров меньше или равно 16px.

Смотри также

  1. TXD - информация о TXD-архивах (Texture Dictionary)
  2. Texture Native Struct на gtamodding.com (англ.)