Quake II Текстуры (Графика) [PSX]

Автор SPOT, 12 апреля 2016, 10:10

« назад - далее »

SPOT

В этой статье я хочу рассмотреть формат хранения текстур (графических изображений) в игре Quake 2 для Sony PlayStation. Так же думаю будет полезно ознакомится с темой «TIM файлы – Формат хранения графики PSX».
Инструкция по Формат хранения графики в игре Quake II будет полной, а вот про файл, где хранятся текстуры не совсем. Так же пока мне не удалось найти файл, где хранится палитра. Но это в принципе не столь важно, так как нас интересует принцип хранения графики, к тому же это что-то вроде сжатия.
Не помещает помощь экспертов, может кто опознает, что за формат сжатия используется для хранения текстур. Поможет найти палитру и т.п. В принципе, любая помощь будет полезна.
В завершение я хочу попробовать написать просмотрщик текстур, исключительно для проверки, так ли всё работает как я опишу в этом документе.
Думаю, по мере написания статья может правиться. Что-то дополняться, исправляться, уточнятся и т.п.
 
 
Устройство файла SNDVRAM.DAT
Хранение текстур (графики / изображения)
 
Устройство 3D модели
Хранение вершин и нормалей в 3D модели
Хранение полигонов и информации об объектах в 3D модели
Подмена 3D моделей
 
Устройство файла ZONE0.DAT (В подобных файлах хранятся 3D уровни)
 
Q2 Textures Viewer – программа для просмотра текстур
Q2 Textures Viewer v.1.1
 
HammerHead 3D To TMD - программа для просмотра 3D моделей
 
HammerHead 3D Map To PLY - программа для извлечения 3D моделей уровней
 
Полезная информация:
Quake II скрытые уровни

SPOT

Устройство файла SNDVRAM.DAT
 
Начнём с краткого обзора файла SNDVRAM.DAT. Это файл в котором хранятся текстуры и звуки.
Файл разделён на две области, в одной хранятся текстуры, а в другой звуки.
Для примера будем рассматривать файл находящийся по адресу Q2DATA -> LEVELS  -> QLOGOS2  -> SNDVRAM.DAT.
Открываем его в Hex-редакторе:
 
На рисунке изображён фрагмент файла (вид в Hex-редакторе), если быть более точным, то это «заголовок». Его-то в данном сообщение мы и рассмотрим.
 
Первые 4 байта всегда = 0x0C000000.
Это указатель на 0Ch, это первая часть файла, тут хранятся текстуры. Переворачиваем и получаем значение 0000000С.
 
 
Назначение следующих 4 байта.
Это указатель на следующую область файла, в ней хранятся звуки. Переворачиваем данные и получаем указатель на $45264.
 
Размер файла. Отсчёт начинается с самого начала 0h. Напомню, что данные хранятся в перевёрнутом виде. В файле данные имеют вид F8 C0 06 00. Мы их переворачиваем и получаем 00 06 C0 F8. Следовательно размер данных (файла) составляет 0x06C0F8.
 
Назначение следующих 4 байта.
 
Данный байт (02) обозначает сколько в файле (архиве) SNDVRAM.DAT хранится файлов с расширением *.opt. В нашем случае это два (2) файла tp0.opt и tp1.opt.
 
Далее так же идёт один байт (04). Он обозначает сколько в файле (архиве) SNDVRAM.DAT хранится файлов с расширением *. lbm. В нашем случае это четыре (4) файла chars.lbm, Legal.lbm, HamLogo.lbm и frontend.lbm.
Эти два байта (11 13) связаны с файлами *.opt. Что это конкретно, я пока не выяснил. Возможно количество элементов в одном изображении. Но данные байты нам важны для вычисления сколько место ниже отведено под *.opt.
 
Место, отведённое под *.opt, вычисляем по следующий формуле.
 
($F + $E - 16) * 0x20
 
В нашем случае:
(0x13 + 0x11 - 16) * 0x20  =  640 [280h]
 
0x20 в десятеричной системе равно 32. Видимо столько отведено под один элемент в изображении типа opt.
 
640 байт отведено тут под как-то данный для opt файлов.
 
 
Дальше идут данные по текстурам (графике).
 
Нас интересуют только текстуры, без пустого места под ними. Тут находится информация о шести текстурах с $10 по $3F включительно.
 
Информация об одной текстуре (графическом элементе) занимает 8 байт.
Если говорить коротко сначала идёт указатель (поинтер) на графические данные, а потом ширина и высота.
 
В начале идёт указатель на графику (текстуру), он занимает 4 байта. Напомню, что данные хранятся в перевёрнутом виде. В файле данные имеют вид F1 0C 00 00. Мы их переворачиваем и получаем 00 00 0C F1. Поинтер (указатель) имеет вид 0x0CF1.
Отсчёт указателя начинается с $C. То есть, чтобы узнать, где начало графики нам нужно к указателю добавить 0xС.
 
0x0CF1+0xС = 0xCFD [CFDh].
 
CFDh – тут начинаются «картинка».
 
2 байта = Ширина
 
Данные 2 байта обозначают Ширину изображения. В файле данные имеют вид 80 00. Мы их переворачиваем и получаем 00 80.  Это 128 пикселей. То есть Ширина изображения = 128 пикселей.
 
2 байта = Высота
Данные 2 байта обозначают Высоту изображения. В файле данные имеют вид 00 01. Мы их переворачиваем и получаем 01 00.  Это 256 пикселей. То есть Высота изображения = 256 пикселей.
 
После данных о текстурах идёт 512 байт пустоты с $40 по $240. Это конечно не буквальная пустота, а мусор из байт 00 80. Но никакого практического применения им нет.
 
Далее идут те самые данные об *.opt файлах, которые мы вычисляли выше по формуле. 640 байт с $240 по $4C0.
 
Далее идёт палитра первого изображения формата lbm. В нашем случае это палитра для файла chars.lbm. Палитра занимает 512 байт с $4C0 по $6C0. Эта палитра в формате TBGR / A1B5G5R5. Как её перевести в цвета написано в TIM 16-bit (actual colors) [TBGR / A1B5G5R5].
После идёт ещё три палитры для Legal.lbm, HamLogo.lbm и frontend.lbm.
 
После палитры идёт названия файлов.
 
По основной информации всё. Пожалуй, так же стоит упомянуть, что каждое из изображений имеет название (быть может это исходный файл).
 
Названия идут в том же порядке, что и изображения. Названия разделены между собой байтом 00.
 
Видим что первому элементу соответствует название tp0.opt.

SPOT

Хранение текстур (графики / изображения)
 
Переходим к самой интересной части, собственно говоря ради чего и была затеяна тема. Это формат хранения графики (текстур). Если разобраться в данном сжатие нет ничего сложного.
Продолжаем рассматривать всё тот же файл SNDVRAM.DAT хранящийся по адресу Q2DATA -> LEVELS -> QLOGOS2 -> SNDVRAM.DAT.
 
Чтобы было проще будем рассматривать самое первое лого, которое мы видим при загрузке игры.
К сожалению изображение тёмное и особо яркими цветами не пестрит. Так что рассматривать пиксели будет сложно.
 
Повторятся не буду, сразу приведу данные об изображении.
Название: Legal.lbm
Начало графики (изображения) – A0E9h
(0xA0DD+0xС = 0xA0E9 [A0E9h].)
Ширина - 512 пикселей
(200h)
Высота – 240 пикселей
(F0h)
Судя по всему, максимальное число цветов изображение 256.
 
Переходим в Hex-редакторе по адресу A0E9h. Это начало графических данных. С ними мы и будем разбираться в этом сообщении.
 
Большинство данных идут подряд парами по 2 байта.
 
Первый байт отвечает за количество пикселей в изображении, второй байт – это какай цвет в палитре из 256
 
Значение FC выведет 5 пикселей.
 
Табличка для наглядности:
FF – 2 пикселя
FE – 3 пикселя
FD – 4 пикселя
FC – 5 пикселей
...
F1 – 16 пикселей
F0 – 17 пикселей
EF – 18 пикселей
EE – 19 пикселей
...
81 - 128 пикселей
80 - 129 пикселей (Максимальное значение)
 
Минимальное количество пикселей, выводимых таким способом равно 2. Максимальное – 129.
Количество выводимых пикселей можно рассчитать по формуле.
 
0xFF – Число + 0x2.
 
Пример:
0xFF – 0xFC + 0x2 = 5 пикселей.
 
00 – это номер цвета в палитре.
Точно не уверен, но что-то мне подсказывает, что счёт цвета в палитре идёт «задом на перёд».
То есть в случае с 00 это 256 цвет в палитре. Если будет FF – то это 1 цвет. FE - 2 цвет и т.д. Но это только догадки.
 
По адресу $A0F1 мы встречаем ранее не рассмотренный случай. Если до этого всё было стандартно, то теперь мы видим значение байта равное 01. Не буду ходить, вокруг да около.
 
Это значение сколько пикселей подряд будут идти (сколько пикселей (байт) считывать). В нашем случае это 2 пикселя (байта).
 
Табличка для наглядности:
00 – 1 пиксель
01 – 2 пикселя
02 – 3 пикселя
...
 
Судя по всему, так может идти до 7F. Но я не проверял.
 
 
В принципе на это всё!

SPOT

Устройство 3D модели
В целом почти всё понятно для построения 3D модели, но остаются кое-какие неизвестные.
 
Работать мы будем с 3D моделью логотипа Q2.
Данная модель находится в файле COMMON.DAT по адресу Q2DATA -> LEVELS -> QFRONT. Но 3D модели так же хранятся и в других файлах, например, ZONE0.DAT.
 
Начиная с $33F0.
 
Мы извлечём модель в отдельный файл Qmodel, так с ней будет проще работать.
Внимание! Теперь отсчёт будет вестись с $0, а не с $33F0.
Qmodel.7z
 
Заголовок состоит из 8 байт. Второй и третий байт в заголовке могут меняться.
 
Следующие 12 байт занимает имя 3D модели. В нашем случае это Q2LOGO.
 
Два байта с $14 это количество полигонов в 3D модели. В нашем случае это 281 (0x0119) полигон. Помните, что данные хранятся в перевёрнутом виде.
 
Следующие два байта это количество объектов в модели. В нашем случае оно равно 6.
 
Предназначение следующих 8 байт установить не удалось.
 
4 байта начиная с $20 это указатель на построение полигонов. Отсчёт начинается с $00. В нашем случае указатель равен $00000EC8.
 
Следующие 4 байта это указатель на вершины (точки) и нормали. Указатель - $00000040
 
4 байта – указатель на информацию о построение объектов. Указатель - $00000E98
 
4 байта – указатель, скорее всего на свойства модели. В нашем случае он равен $00002058
 
Ещё четыре указателя по 4 байта. Возможно последний указатель указывает на начало следующий модели.

SPOT

Хранение вершин и нормалей в 3D модели
Ранее мы узнали адрес хранения вершин и нормалей.
Вершины и нормали хранятся с $40 по $ E98. Они занимают 3672 байт.
 
Одна "точка" занимает 6 байт.
 
Первые 2 байта это координата по оси X. Значение 0x0000.
 
Вторые 2 байта это координата по оси Y. Значение 0x0008.
 
Третьи 2 байта это координата по оси Z. Значение 0x004E.
 
Первая точка это вершина модели, вторая нормаль. Так они и идут чередуясь: вершина, нормаль, вершина, нормаль...
 
Теперь расшифруем координаты для наглядности. В качестве примера возьмём первую нормаль:
FF0F 0000 F6FF
Переворачиваем
0FFF 0000 FFF6
Это будет
4095 0 -10
 
Максимальное число: 7FFFh = 32767
Минимальное число: 8000h = -32767
Отрицательное число начинается когда первый перевёрнутый байт равен или больше 0x80. -1 это 0xFFFF.
 
То есть вычисляем по формуле:
 
(0xFFFF – 0xXXXX + 1) * (-1)
Пример для наглядности:
(0xFFFF – 0xFFF6 + 1) * (-1) = -10

SPOT

Хранение полигонов и информации об объектах в 3D модели
Вспомогательная информация об объектах начинается с $E98 по $EC8 и занимает 48 байта. Напомню, что всего у нас 6 объектов.
 
На хранение данных об одном объекте нужно 8 байт.
 
Первые 2 байта (хотя может второй байт относится к чему-то другому) это количество полигонов в объекте (модели). В нашем случае 61 (0x3D) полигон.
 
Следующий байт, это сколько нужно вычесть (отнять) от точек. Сложно объяснить, в конце будет приведён пример, возможно из него станет ясно.
 
Ещё 1 байт это количество используемых точек в этом объекте. В нашем случае оно равно 66 (0x42) точкам.
 
Последние 4 байта возможно тоже могут содержать какую-то информацию, или планировалось их под что-то использовать.
 
Полигоны
Информация об построение полигонов начинается с $EC8 по $2058. Всего у нас 281 полигон.
 
Как мы узнали из информации об объектах, первая модель состоит из 61 полигона. Начинается с $EC8 по $1298. Занимает 976 байт.
 
Информация о первом полигоне занимает 8 байт.
 
Первые четыре байта это соединение четырёх вершин (точек) между собой. По 1 байту на точку.
 
Идём из 0 точки в 1, потом из первой во 2, из второй в 3, и из третий возвращаемся в нулевую.
 
Следующие 12 байт, скорее всего какая-то информация об текстуре.
 
Хочу заметить, что максимальное число точек в модели 256.
При построение следующего объекта отсчёт начинается с 00. К вершинам (точкам) нужно добавить количество точек, используемое в предыдущих моделях и отнять своё число.
 
0x00 + 0x42 - 0x00= 0x42
0x01 + 0x42 - 0x00= 0x43
0x02 + 0x42 - 0x00= 0x44
0x03 + 0x42 - 0x00= 0x45

SPOT

Устройство файла ZONE0.DAT
(Будет очень краткий обзор, только тех фрагментов которые нам будут необходимы)
Начнём с краткого обзора файла ZONE0.DAT. Это файл в котором хранятся данный о игровой карте (локации, уровне). Проще говоря в нём хранится 3D карта уровня.
Для примера будем рассматривать файл находящийся по адресу Q2DATA -> LEVELS  -> BASE0  -> ZONE0.DAT.

Открываем его в Hex-редакторе:
Тут мы видим список параметров, которые присутствуют в файле. Есть ещё пара параметров, которых в данном фале нет.
Первые 12 байт это название параметра, следующие за ними 4 байта - указатель (поинтер). Напомню что здесь перевёрнутые указатели.
Нас же интересуют следующие:

Scene (указатель: 0x32C)
MapMod (указатель: 0x898C)
Points (указатель: 0x1F4B8)

Из Scene мы будем узнавать параметр, который нам понадобится для MapMod.

В MapMod хранится информация о полигонах.

В Points находится информация о точках (вершинах).

Последний параметр подчёркнутый зелёным это размер файла (0xA89004 -> 0x490A8).

Переходим по первому указателю 0x32C.
 
Тут находится блоки информации о моделях. Один блок занимает 52 байта.
Нас интересует первые 4 байта. Это указатель для MapMod. Он говорит о том, где находится информация о полигонах для каждой 3D модели.
 

Теперь переходим по указателю из MapMod - 0x898C. Тут хранится информация о полигонах. Отсчёт поинтеров взятых из Scene начинается с 898Ch.
То есть первый блок информации находится 0x898C + 0x0000 = 0x898C. Второй - 0x898C + 0x005С = 0x89E8.
 
Теперь рассмотрим сам блок с информацией, точнее ту часть которая нас интересует.
Первые 2 байта расположенные по адресу 898Ch это количество полигонов в модели.
Затем через 8 байт по адресу 8994h идёт первый блок информации о полигоне. Он состоит из 12 байт. Нас же в нём интересуют первые 4 байты. Это построение полигона.

Как устроены полигоны в игре Quake 2 можно узнать из статьи "Хранение полигонов и информации об объектах в 3D модели".
Скорее всего оставшиеся 8 байт это информация о текстурах (возможно, про цвет и освещение).
 
Далее переходим по адресу 1F4B8h указанному в Points. Тут нас ждёт информация о точках (вершинах).
В начале (первый блок) (1F4B8h) идёт информация о количестве точек и указателях на них. Второй (1FF84h) - это уже сами точки.
 
Первые 4 байта расположенные по адресу 1F4B8h это общее количество блоков с точками. Их тут 345 (0x0159).
Следующие 4 байта это указатель, в нашем случае это 0 (0x00000000). Отсчёт начинается с конца блока "указателей" с 1FF84h. Таким образом первый блок "точек" расположен по адресу 0x1FF84 + 0x00000000 = 0x1FF84.
(Второй блок точек будет расположен по адресу 0x1FF84 + 0x6C000000 = 0x1FFF0)
Последние 4 байта - это количество точек в блоке информации. В нашем случае их 9.
 
Первый блок состоящий из 9 точек находится по адресу 1FF84h. Первыми идут координаты точек (вершин) X, Y, Z. Информация о каждой точке занимает 2 байта.
Точки имеют вид:
Максимальное число: 7FFFh = 32767
Минимальное число: 8000h = -32767
Подробнее описано в статье "Хранение вершин и нормалей в 3D модели".
 
Внешний вид 3D модели рассматриваемой в примере.

SPOT

Начал реализовывать программу просмотрщик текстур в Q2. Пока достигнутый результат говорит о том, что я всё правильно описал выше.
Вот результат работы программы. Это второе лого, которое можно увидеть при запуске игры.
То, как лого выглядит в игре.

PSXDev

Спасибо за ваш труд, ещё нигде не встречал, что бы так подобно разжевали эту тему. Получается она задевает и тему запаковки и распаковки графики. Буду следить за темой и ждать просмотрщика тексту в играх для PSX.

SPOT

 
Завершил написание программы для просмотра текстур (графики) в игре Quake 2 для PSX. Где хранится палитра так найти и не удалось, но это и не было основной задачей. Главное, что теперь у всех желающих появился инструмент для просмотра текстур в игре Q2. Кому этого будет недостаточно, и он захочет их отредактировать, предоставленной выше информации вполне для этого достаточно.
Это хороший наглядный пример, вполне вероятно, что такой же или подобный формат хранения графики используется и в других играх для PlayStation.
Теперь коротко о программе.
Программу я назвал Q2 Textures Viewer. С её помощью можно просматривать содержимое файла SNDVRAM.DAT, а точнее содержащиеся там текстуры.
 
Скачать Q2 Textures Viewer:
Q2 Textures Viewer.7z

TTEMMA

Было бы интересно взглянуть на исходники:)
Поздравляю с релизом!

SPOT

Цитата: TTEMMA от 13 апреля 2016, 19:39Было бы интересно взглянуть на исходники :)
Поздравляю с релизом!
Там-то и смотреть не на что, большая часть кода это, загрузка файла, проверки и связь между элементами.
Сам основной код, грубо говоря, состоит из 20 строк.

int Start2= Chislo;
int x=0;
int y=0;
int Exit=0;

for (; ; ) {
int hex1 = traceL[Start2];

if (hex1>=128) {
int povtor=0xff - hex1 + 2;
for (int i2 = 0; i2 < povtor; i2++){
Image1->Canvas->Pixels[x][y]=RGB(0, traceL[Start2+1], traceL[Start2+1]);
x++;
if(x>=ShirinX){x=0;y++;}
Exit++;
}
Start2+=2;}
else  if (hex1<128) {
for (int i2 = 0; i2 <= hex1; i2++){ Start2++;
Image1->Canvas->Pixels[x][y]=RGB(0, traceL[Start2], traceL[Start2]);
x++;
if(x>=ShirinX){x=0;y++;}
Exit++;
}
Start2++;}
if(Exit>=ShirinX*ShirinY)break;
}

TTEMMA

Решил помочь с разбором данного формата.
Оказывается, это не только контейнер для текстур, но и для звука. Буду разбираться глубже.
Что говорить о 4 байтах, начинающихся с 0x4, то это указатель на новый блок. В случае с файлам Q2DATA\LEVELS\QFMV\SNDVRAM.DAT, это указатель на блок со звуковыми эффектами.

SPOT

Цитата: TTEMMA от 13 апреля 2016, 21:44Решил помочь с разбором данного формата.
Оказывается, это не только контейнер для текстур, но и для звука. Буду разбираться глубже.
Что говорить о 4 байтах, начинающихся с 0x4, то это указатель на новый блок. В случае с файлам Q2DATA\LEVELS\QFMV\SNDVRAM.DAT, это указатель на блок со звуковыми эффектами.
Я предполагал, что в них есть звук.

PSXDev

Будет круто, если ещё с палитрой разберётесь.

SPOT

Кстати, одно из моих предположений, что

Цитировать 
Константа (постоянное значение), видимо для обозначения, что в этом файле хранятся текстуры (возможно ещё какие-то данные).
Первые 4 байта всегда = 0x0C000000.
Это не константа, а указатель на 0Ch.

SPOT

Нарыл кое-что по 3D моделям в QII.
Судя по всему модели хранятся в файле COMMON.DAT.
Для примера возьмём файл находящийся по адресу Q2DATA -> LEVELS -> QFRONT -> COMMON.DAT.
Начиная с $33F0 по $5DFB идёт первая 3D модель. Это модель логотипа Q II.
 
Судя по всему сначала идёт скелет модели
 
Потом на него накладываются текстуры, а затем идёт поведение модели (наверное).
 
Кому интересно можете попробовать разобраться с системой хранения.

SPOT

Начал разбираться с подменой моделей. Вот результат:
 
Что это такое я не знаю :) , называется Chest.

TTEMMA

Тоже имеются маленькие продвижения:)

SPOT

Цитата: TTEMMA от 14 апреля 2016, 16:48Тоже имеются маленькие продвижения :)
Круто, как завершишь, расскажешь всё подробно?