Files
fparkan/docs/specs/texture.md
Valentin Popov 0d7ae6a017
Some checks failed
Test / Lint (push) Failing after 1m10s
Test / Test (push) Has been skipped
Test / Render parity (push) Has been skipped
Документирование и обновление спецификаций
- Обновлены спецификации `runtime-pipeline`, `sound`, `terrain-map-loading`, `texture`, `ui` и `wear`.
- Добавлены разделы о статусе покрытия и оставшихся задачах для достижения 100% завершенности.
- Внесены уточнения по архитектурным ролям, минимальным контрактам и требованиям к toolchain для каждой подсистемы.
- Уточнены форматы данных и правила взаимодействия между компонентами системы.
2026-02-19 11:07:04 +04:00

5.0 KiB
Raw Permalink Blame History

Texture (Texm)

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

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

1. Контейнер

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

2. Заголовок

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

Расчёт ядра:

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;

4.1. Декодирование в RGBA8 (runtime/инструменты)

Для CPU-пути (preview, валидация, оффлайн-конвертация) используется декодирование:

  • 0 (Indexed8): index -> palette[index] (RGBA из палитры 256×4).
  • 565: R5 G6 B5, A=255.
  • 556: R5 G5 B6, A=255.
  • 4444: A4 R4 G4 B4 (с расширением 4-битных каналов в 8-битные).
  • 88: L8 A8 (R=G=B=L).
  • 888: R8 G8 B8 + padding/служебный байт, A=255.
  • 8888: A8 R8 G8 B8.

Это декодирование соответствует текущему test/demo pipeline проекта.

5. Page chunk

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.
  • На полном retail-корпусе testdata/Parkan - Iron Strategy проверено 518/518 текстурных payload (Texm) без ошибок.

11. Статус покрытия и что осталось до 100%

Закрыто:

  1. Заголовок Texm, mip-chain layout и Page chunk.
  2. Базовые decode-пути в RGBA8 для проверок/preview.
  3. Корпусная валидация структурных инвариантов.

Осталось:

  1. Полная формальная спецификация всех редких служебных комбинаций flags4/flags5/unk6.
  2. Канонический writer для полного набора форматов (indexed, 565, 556, 4444, 88, 888, 8888) с проверенным roundtrip-профилем.
  3. Pixel-parity тесты «оригинальный рендер vs новый рендер» с учетом mipSkip/atlas-page веток.