aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/texture.md
blob: 5fa1e9d90f9d7802752c99ff5b4981a73534c474 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Texture (`Texm`)

`Texm` — основной формат текстур движка.

Связанные страницы:

- [Material (`MAT0`)](material.md)
- [Wear table (`WEAR`)](wear.md)
- [Render pipeline](render.md)

## 1. Контейнер

- Тип ресурса: `0x6D786554` (`Texm`).
- Используется в `Textures.lib`, `LightMap.lib` и других `NRes` архивах.

## 2. Заголовок

```c
struct TexmHeader32 {
    uint32_t magic;    // 'Texm'
    uint32_t width;
    uint32_t height;
    uint32_t mipCount;
    uint32_t flags4;
    uint32_t flags5;
    uint32_t unk6;
    uint32_t format;
};
```

## 3. Поддерживаемые форматы

Базовые форматы:

- `0` (8-bit indexed + palette)
- `565`
- `4444`
- `888`
- `8888`

Дополнительные ветки загрузки поддерживают также `556` и `88`.

## 4. Layout payload

1. `TexmHeader32` (32 байта)
2. palette `1024` байта, если `format == 0`
3. mip-chain пикселей
4. optional `Page` chunk

Расчёт ядра:

```c
bytesPerPixel =
    (format == 0) ? 1 :
    (format == 565 || format == 556 || format == 4444 || format == 88) ? 2 :
    4;

pixelCount = sum(max(1, width>>i) * max(1, height>>i), i=0..mipCount-1);
sizeCore = 32 + (format==0 ? 1024 : 0) + bytesPerPixel * pixelCount;
```

## 5. `Page` chunk

```c
struct PageChunk {
    uint32_t magic;      // 'Page'
    uint32_t rectCount;
    Rect16   rects[rectCount];
};

struct Rect16 {
    int16_t x;
    int16_t w;
    int16_t y;
    int16_t h;
};
```

`Page` задаёт atlas-прямоугольники для выборки под-областей текстуры.

## 6. Mip-skip политика

Загрузчик может пропускать первые mip-уровни в зависимости от:

- `flags5`,
- размеров текстуры,
- количества mip.

После `mipSkip`:

- уменьшаются `width/height/mipCount`;
- сдвигается начало пиксельных данных;
- `Page`-координаты пересчитываются в соответствии с новым базовым уровнем.

## 7. Палитры

Для части текстур движок связывает палитру по суффиксу имени.

Практический формат:

- буква `A..Z` + вариант `""` или `0..9`
- всего `26 * 11 = 286` возможных слотов палитр.

Невалидные суффиксы нужно считать ошибкой входных данных в инструментах.

## 8. Кэширование

Движок ведёт отдельные кэши:

- общий texture cache;
- lightmap cache.

Для обычных текстур используется отложенный сбор неиспользуемых слотов (по времени нулевого refcount).

## 9. Правила writer/editor

1. Не нормализовать `flags4/flags5/unk6`.
2. Сохранять payload без лишних хвостовых байт.
3. Если есть `Page`, его размер должен быть ровно `8 + rectCount * 8`.
4. Проверять `width > 0`, `height > 0`, `mipCount > 0`.

## 10. Статус валидации

- Инварианты `Texm` реализованы в `tools/msh_doc_validator.py`.
- В текущем окружении нет полного игрового набора текстур в `testdata`, поэтому массовая перепроверка не запускалась.