- Обновлены спецификации `runtime-pipeline`, `sound`, `terrain-map-loading`, `texture`, `ui` и `wear`. - Добавлены разделы о статусе покрытия и оставшихся задачах для достижения 100% завершенности. - Внесены уточнения по архитектурным ролям, минимальным контрактам и требованиям к toolchain для каждой подсистемы. - Уточнены форматы данных и правила взаимодействия между компонентами системы.
6.5 KiB
6.5 KiB
MSH core
MSH core описывает геометрию, слоты, батчи и базовые таблицы модели.
Документ покрывает контракт, необходимый для 1:1 воспроизведения рендера и коллизии.
Связанные страницы:
1. Общая модель
MSH-модель хранится как NRes-контейнер.
Связь таблиц строится по type, а не по порядку записей.
Базовый путь геометрии:
Res1выбирает slot по(node, lod, group).Res2.slotзадаёт диапазоны треугольников и батчей.Res13задаёт диапазон индексов иbaseVertex.Res6даётuint16индексы.Res3/Res4/Res5дают вершины, нормали и UV.
2. Карта core-ресурсов
| Type | Ресурс | Обязательность | Stride / layout |
|---|---|---|---|
| 1 | Node table | обязательный | обычно 38 байт |
| 2 | Header + slots | обязательный | 0x8C + n*68 |
| 3 | Positions | обязательный | 12 |
| 4 | Packed normals | обычно обязательный | 4 |
| 5 | Packed UV0 | обычно обязательный | 4 |
| 6 | Index buffer | обязательный | 2 |
| 7 | Tri descriptors | для коллизии/пикинга | 16 |
| 8 | Anim key pool | для анимированных | 24 |
| 10 | Node strings | опциональный | variable |
| 13 | Batch table | обязательный | 20 |
| 15 | Доп. stream | опциональный | 8 |
| 16 | Доп. stream | опциональный | 8 |
| 18 | Доп. stream | опциональный | 4 |
| 19 | Anim map | для анимированных | 2 |
| 20 | Доп. таблица | опциональный | variable |
3. Основные структуры
3.1. Res1 (узлы)
struct Node38 {
uint16_t hdr0;
uint16_t parent_or_link;
uint16_t anim_map_start;
uint16_t fallback_key;
uint16_t slotIndex[15]; // lod0:g0..g4, lod1:g0..g4, lod2:g0..g4
};
Формула slot-выбора:
slot = node.slotIndex[lod * 5 + group]
0xFFFF означает отсутствие слота.
3.2. Res2 (header + slot records)
struct Slot68 {
uint16_t triStart;
uint16_t triCount;
uint16_t batchStart;
uint16_t batchCount;
float aabbMin[3];
float aabbMax[3];
float sphereCenter[3];
float sphereRadius;
uint32_t opaque[5];
};
opaque[5] должны сохраняться 1:1.
3.3. Res3, Res4, Res5, Res6
Res3:float3позиции (stride=12)Res4:int8[4]packed normal (stride=4)Res5:int16[2]UV (stride=4)Res6:uint16индексы (stride=2)
Декодирование:
- normal =
clamp(n / 127.0, -1..1) - uv =
packed / 1024.0
3.4. Res7 и Res13
struct TriDesc16 {
uint16_t triFlags;
uint16_t link0;
uint16_t link1;
uint16_t link2;
int16_t nx;
int16_t ny;
int16_t nz;
uint16_t selPacked;
};
struct Batch20 {
uint16_t batchFlags;
uint16_t materialIndex;
uint16_t opaque4;
uint16_t opaque6;
uint16_t indexCount;
uint32_t indexStart;
uint16_t opaque14;
uint32_t baseVertex;
};
selPacked хранит 3 селектора по 2 бита; значение 3 трактуется как 0xFFFF.
4. Runtime-обход модели
Псевдокод рендера:
for each node:
slot = resolve_slot(node, lod, group)
if slot == none: continue
if culled(slot.bounds, node_transform): continue
for b in slot.batchRange:
batch = batches[b]
bind_material(batch.materialIndex)
draw_indexed(
baseVertex = batch.baseVertex,
indexStart = batch.indexStart,
indexCount = batch.indexCount
)
5. Критические инварианты
Обязательно проверять:
Res2.size >= 0x8C(Res2.size - 0x8C) % 68 == 0batchStart + batchCountне выходит заRes13triStart + triCountне выходит заRes7indexStart + indexCountне выходит заRes6baseVertex + max(indexSlice) < vertexCountslotIndex == 0xFFFFили< slotCount
6. Важные edge-cases
- Встречается редкий вариант
Res1.attr3 = 24; для существующих ассетов нужен copy-through. - Для строгого writer лучше генерировать
Res1в основном формате38байт/узел. - Неизвестные поля таблиц нельзя нормализовать или обнулять.
7. Правила для writer/editor
- Сохранять неизвестные поля и неизвестные
type-ресурсы. - Пересчитывать только явно вычислимые атрибуты (
attr1/attr3и size-зависимые поля). - Не менять порядок/контент opaque-данных без явной цели.
- Сериализовать little-endian, без внутреннего padding.
8. Статус валидации
- Инварианты формата реализованы в
tools/msh_doc_validator.py. - На полном retail-корпусе
testdata/Parkan - Iron Strategyпроверено435/435MSH-моделей без структурных ошибок.
9. Статус покрытия и что осталось до 100%
Закрыто:
- Базовые таблицы geometry path (
Res1/2/3/4/5/6/7/13). - Критичные range-инварианты slot/batch/index.
- Правила совместимого writer/editor для lossless работы с существующими ассетами.
Осталось:
- Полная семантика части opaque-полей (
Slot68tail,Batch20opaque-поля) для authoring без copy-through. - Полная формализация редких веток (
Res1.attr3 != 38) на расширенном корпусе. - End-to-end writer для генерации новых игровых MSH с подтвержденным runtime-паритетом.