Texture Native (Секция RW)
Материал из GTAModding.ru
Версия от 00:32, 11 апреля 2014; DK22 (обсуждение | вклад)
Секция RenderWare |
Texture Native |
0x0015 |
Texture Native - секция-контейнер, используемая в TXD-файлах внутри секции Texture Dictionary. Как правило, одна или несколько таких секций, следуют после секции Struct внутри контейнера Texture Dictionary.
Содержание |
PC / XBOX
- Texture_Native
Общая структура
Структура начинается с т.н. заголовка размером в 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
- Sky Mipmap Val - 4 байта (пока неизвестно)
Информация ниже находится на этапе эксперементирования и проверки, а, следовательно, может оказаться неточной.
Платформа и фильтр
Информация о платформе и фильтрах хранится в стандартной 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.
Смотри также
- TXD - информация о TXD-архивах (Texture Dictionary)
- Texture Native Struct на gtamodding.com (англ.)