16 KiB
Документация формата MSH
Формат .msh используется игрой Parkan: Железная стратегия (1998) для хранения 3D-мешей.
MSH файлы — это NRes архивы, содержащие несколько типизированных компонентов.
Обзор
Существует два варианта формата MSH:
| Вариант | Применение | Ключевые компоненты | Хранение треугольников |
|---|---|---|---|
| Модель | Роботы, здания, объекты | 06, 0D, 07 | Индексированные треугольники |
| Ландшафт | Террейн | 0B, 15 | Прямые треугольники |
Автоопределение типа
Модель: Есть компонент 06 (индексы) И 0D (батчи)
Ландшафт: Есть компонент 0B (материалы) И НЕТ компонента 06
Сводка компонентов
| Тип | Название | Размер элемента | Описание |
|---|---|---|---|
| 01 | Node table | 38 (0x26), редко 24 | Узлы модели / тайлы; старое имя: Pieces |
| 02 | Header + slots | 0x8C + n*68 | Общий заголовок и slot records; старое имя: Submeshes |
| 03 | Positions | 12 (0x0C) | Позиции вершин (Vector3); старое имя: Vertices |
| 04 | PackedNormals | 4 | int8[4], normal = clamp(component / 127.0, -1..1) |
| 05 | PackedUV0 | 4 | int16[2], uv = component / 1024.0 |
| 06 | Index buffer | 2 | Индексы вершин треугольников |
| 07 | Tri descriptors | 16 | Описатели треугольников для коллизии/пикинга |
| 08 | AnimKeyPool | 24 | Кейфреймы анимации меша |
| 0A | Node strings | переменный | Строки узлов; старое имя: ExternalRefs |
| 0B | неизвестно | 4 | неизвестно (только Ландшафт) |
| 0D | Batch table | 20 (0x14) | Батчи рендера; FParkan Res13 decimal |
| 0E | неизвестно | 4 | неизвестно (только Ландшафт) |
| 12 | MicrotextureMap | 4 | неизвестно |
| 13 | AnimMap | 2 | Карта кадров анимации, на нее указывает AnimMapStart из 0x01 |
| 15 | TerrainTriangle table | 28 (0x1C) | Terrain-гипотеза |
Поток данных
Модель (роботы, здания)
Компонент 01 (Pieces - части)
│
└─► Lod[n] ──► Компонент 02 (индекс сабмеша)
│
├─► TriStart:TriCount ──► Компонент 07 (данные на треугольник)
│
└─► BatchStart:BatchCount ──► Компонент 0D (батчи)
│
├─► IndexStart:IndexCount ──► Компонент 06 (индексы)
│ │
│ └─► Компонент 03 (вершины)
│
└─► BaseVertex (базовое смещение вершины)
Ландшафт (террейн)
Компонент 01 (Тайлы, обычно 16×16 = 256)
│
└─► Lod[n] ──► Компонент 02 (индекс сабмеша)
│
└─► TriStart:TriCount ──► Компонент 15 (треугольники)
│
└─► Vertex1/2/3Index ──► Компонент 03 (вершины)
└─► TriStart:TriCount ──► Компонент 0B (материалы, параллельно 15)
Важно: В ландшафтных мешах поля TriStart и TriCount в Компоненте 02
используются для индексации в Компонент 15 (треугольники), а не в Компонент 07.
Структуры компонентов
Компонент 0x01 - Node table (0x26 = 38 байт)
Определяет узлы модели или тайлы terrain. Старое локальное имя: Pieces / SubMesh.
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 2 | uint16 | Header0 | Заголовочное слово узла; старые имена: Type1 + Type2 |
| 0x02 | 2 | uint16 | ParentOrLink | Индекс родителя/ссылка; старый локальный тип int16 показывал 0xFFFF как -1 |
| 0x04 | 2 | uint16 | AnimMapStart | Начало блока в 0x13 или 0xFFFF; старое имя: OffsetIntoFile13 |
| 0x06 | 2 | uint16 | FallbackKey | Индекс fallback-ключа в 0x08; старое имя: IndexInFile08 |
| 0x08 | 30 | ushort[15] | SlotIndex | Индексы slot в 0x02 по формуле lod * 5 + group; старое имя: Lod |
Ландшафт: 256 тайлов в сетке 16×16. Каждый тайл имеет 2 LOD (индексы 0-255 и 256-511).
Компонент 0x02 - Header + slots (Заголовок: 0x8C = 140 байт, slot: 0x44 = 68 байт)
Заголовок (140 байт)
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 96 | Vector3[8] | BoundingBox | 8-точечный баундинг-бокс |
| 0x60 | 12 | Vector3 | Center | Центральная точка |
| 0x6C | 4 | float | CenterW | W-компонента |
| 0x70 | 12 | Vector3 | Bottom | Нижняя точка |
| 0x7C | 12 | Vector3 | Top | Верхняя точка |
| 0x88 | 4 | float | XYRadius | Радиус в плоскости XY |
Элемент (68 байт)
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 2 | ushort | TriStart | Начальный индекс в Компоненте 07; в landscape-tooling может указывать в 15 |
| 0x02 | 2 | ushort | TriCount | Количество записей в Компоненте 07; в landscape-tooling может быть count для 15 |
| 0x04 | 2 | ushort | BatchStart | Начальное смещение в Компоненте 0D (только Модель) |
| 0x06 | 2 | ushort | BatchCount | Количество батчей в Компоненте 0D (только Модель) |
| 0x08 | 12 | Vector3 | LocalMinimum | Минимум локального баундинг-бокса |
| 0x14 | 12 | Vector3 | LocalMaximum | Максимум локального баундинг-бокса |
| 0x20 | 12 | Vector3 | Center | Центр сабмеша |
| 0x2C | 4 | float | SphereRadius | Радиус bounding sphere; старый Vector4 был overlay-гипотезой |
| 0x30 | 20 | uint32[5] | Opaque | Непонятый tail, сохранять 1:1; старый Vector5 был overlay-гипотезой |
Компонент 03 - Vertices (0x0C = 12 байт)
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 4 | float | X | Координата X |
| 0x04 | 4 | float | Y | Координата Y |
| 0x08 | 4 | float | Z | Координата Z |
Компонент 06 - Indices (2 байта) - Только Модель
Массив ushort значений — индексы вершин треугольников.
Используются группами по 3 для каждого треугольника. Ссылки через батчи Компонента 0D.
Компонент 0x07 - Tri descriptors (0x10 = 16 байт)
Описатели треугольников для коллизии/пикинга.
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 2 | ushort | TriFlags | Флаги треугольника; старое имя: Flags |
| 0x02 | 2 | ushort | Link0 | Связь/opaque поле 0; старое имя: Magic02 |
| 0x04 | 2 | ushort | Link1 | Связь/opaque поле 1; старое имя: Magic04 |
| 0x06 | 2 | ushort | Link2 | Связь/opaque поле 2; старое имя: Magic06 |
| 0x08 | 2 | int16 | NormalX | Упакованная X-компонента нормали; старое имя: OffsetX |
| 0x0A | 2 | int16 | NormalY | Упакованная Y-компонента нормали; старое имя: OffsetY |
| 0x0C | 2 | int16 | NormalZ | Упакованная Z-компонента нормали; старое имя: OffsetZ |
| 0x0E | 2 | ushort | SelectorPacked | Три 2-битных селектора; 3 трактуется как 0xFFFF; старое имя: Magic14 |
Компонент 0B - Material Data (4 байта) - Только Ландшафт
Информация о материале/текстуре на каждый треугольник. Параллельный массив к Компоненту 15.
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 2 | ushort | HighWord | Индекс материала/текстуры |
| 0x02 | 2 | ushort | LowWord | Индекс треугольника (последовательный) |
Компонент 0x0D - Batch table (0x14 = 20 байт)
Определяет батчи вызовов отрисовки. В терминах FParkan это Res13 decimal.
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 2 | ushort | BatchFlags / Flags.low | Флаги батча |
| 0x02 | 2 | ushort | MaterialIndex / Flags.high | Индекс material slot |
| 0x04 | 2 | ushort | Opaque4 | Opaque, старое имя TriangleCount не подтверждено |
| 0x06 | 2 | ushort | Opaque6 | Opaque |
| 0x08 | 2 | ushort | IndexCount | Количество индексов для отрисовки в 0x06 |
| 0x0A | 4 | uint32 | IndexStart | Начальный индекс в Компоненте 06 |
| 0x0E | 2 | ushort | Opaque14 | Opaque, старое имя CountOf03 не подтверждено |
| 0x10 | 4 | uint32 | BaseVertex | Базовое смещение вершины в Компоненте 03 |
Компонент 0x15 - TerrainTriangle table (0x1C = 28 байт)
Прямые определения terrain-треугольников. Это hex-компонент 0x15 проекта, не FParkan Res15 decimal.
| Смещение | Размер | Тип | Поле | Описание |
|---|---|---|---|---|
| 0x00 | 4 | uint32 | Flags | Флаги треугольника (0x20000 = коллизия) |
| 0x04 | 4 | uint32 | MaterialData | Данные материала; старое имя: Magic04 |
| 0x08 | 2 | ushort | Vertex1Index | Индекс первой вершины |
| 0x0A | 2 | ushort | Vertex2Index | Индекс второй вершины |
| 0x0C | 2 | ushort | Vertex3Index | Индекс третьей вершины |
| 0x0E | 4 | uint32 | Opaque0E | Opaque; старое имя: Magic0E |
| 0x12 | 4 | uint32 | Opaque12 | Opaque; старое имя: Magic12 |
| 0x16 | 4 | uint32 | Opaque16 | Opaque; старое имя: Magic16 |
| 0x1A | 2 | ushort | Opaque1A | Opaque; старое имя: Magic1A |
MaterialData (0x04) - Структура материала
MaterialData = 0xFFFF_SSPP
│ │└─ PP: Основной материал (byte 0)
│ └─── SS: Вторичный материал для блендинга (byte 1)
└────── Всегда 0xFFFF (байты 2-3)
| Значение SS | Описание |
|---|---|
| 0xFF | Сплошной материал (без блендинга) |
| 0x01-0xFE | Индекс вторичного материала для блендинга |
Примеры:
0xFFFFFF01= Сплошной материал 10xFFFF0203= Материал 3 с блендингом в материал 2
Компонент 0A - External References (переменный размер)
Таблица строк для внешних ссылок на части меша. Формат:
[4 байта: длина] [байты строки] [null-терминатор]
...повтор...
Длина 0 означает пустую запись. Строки типа "central" имеют особое значение (flag |= 1).
Пример: Ландшафт SC_1
Land.msh (SC_1):
├── 01: 256 тайлов (сетка 16×16)
├── 02: 512 сабмешей (256 LOD0 + 256 LOD1)
├── 03: 10 530 вершин
├── 04: 10 530 данных на вершину
├── 05: 10 530 данных на вершину
├── 0B: 7 882 записи материалов
├── 0E: 10 530 данных на вершину
├── 12: 10 530 микротекстурный маппинг
└── 15: 7 882 треугольника
├── LOD 0: 4 993 треугольника (тайлы 0-255 → сабмеши 0-255)
└── LOD 1: 2 889 треугольников (тайлы 0-255 → сабмеши 256-511)
Использование
var converter = new MshConverter();
// Автоопределение типа и конвертация в OBJ
converter.Convert("Land.msh", "terrain.obj", lodLevel: 0);
converter.Convert("robot.msh", "robot.obj", lodLevel: 0);
// Ручное определение типа
var archive = NResParser.ReadFile("mesh.msh").Archive;
var type = MshConverter.DetectMeshType(archive);
// Возвращает: MshType.Model или MshType.Landscape
Формат WEA - Файлы материалов ландшафта
Файлы .wea — текстовые файлы, определяющие таблицу материалов для ландшафта.
Формат
{count}
{index} {material_name}
{index} {material_name}
...
Связь с Land.msh
Каждая карта имеет два файла материалов:
| Файл | Используется для | Треугольники в Comp15 |
|---|---|---|
Land1.wea |
LOD0 (высокая детализация) | Первые N (сумма TriCount для LOD0) |
Land2.wea |
LOD1 (низкая детализация) | Остальные |
Пример (SC_1)
Land1.wea:
4
0 B_S0
1 L04
2 L02
3 L00
Land2.wea:
4
0 DEFAULT
1 L05
2 L03
3 L01
Маппинг материалов
Индекс материала в Comp15.MaterialData & 0xFF → строка в .wea файле.
Треугольник с MaterialData = 0xFFFF0102
└─ Основной материал = 02 → Land1.wea[2] = "L02"
└─ Блендинг с материалом = 01 → Land1.wea[1] = "L04"
Типичные имена материалов
| Префикс | Назначение |
|---|---|
| L00-L05 | Текстуры ландшафта (grass, dirt, etc.) |
| B_S0 | Базовая текстура |
| DEFAULT | Фолбэк для LOD1 |
| WATER | Вода (поверхность) |
| WATER_BOT | Вода (дно) |
| WATER_M | Вода LOD1 |
Источники
- Реверс-инжиниринг
Terrain.dll(класс CLandscape) - Декомпиляция Ghidra:
CLandscape::ctorиIMesh2_of_CLandscape::Render