docs: rewrite MkDocs documentation
Docs Deploy / Build and Deploy MkDocs (push) Successful in 34s
Test / Lint (push) Failing after 1m7s
Test / Test (push) Has been skipped
Test / Render parity (push) Has been skipped

This commit is contained in:
2026-06-22 01:58:51 +04:00
parent 50c2cf4686
commit 78fc5f1deb
44 changed files with 6336 additions and 2586 deletions
+69
View File
@@ -0,0 +1,69 @@
# WEAR и MAT0
MSH batch хранит только `material_index`. WEAR переводит этот индекс в имя
материала, а MAT0 по этому имени описывает phases, parameters и texture
references.
```text
Batch20.material_index
-> WEAR row
-> MAT0 entry
-> active phase
-> textureName
```
## WEAR
WEAR -- текстовый ресурс type ID `0x52414557`, обычно `*.wea` рядом с моделью.
```text
<wearCount>
<legacyId> <materialName>
...
[empty line]
[LIGHTMAPS
<lightmapCount>
<legacyId> <lightmapName>
...]
```
`legacyId` сохраняется, но выбор выполняется по позиции строки и имени. Между
основной таблицей и `LIGHTMAPS` нужен пустой разделитель.
## MAT0
MAT0 имеет type ID `0x3054414D`, обычно расположен в `Material.lib`. `attr1`
содержит runtime flags, `attr2` -- версию payload.
```c
#pragma pack(push, 1)
struct Mat0PrefixV4Plus {
uint16_t phase_count;
uint16_t animation_block_count;
uint8_t metadata_a;
uint8_t metadata_b;
uint32_t metadata_c_raw;
uint32_t metadata_d_raw;
};
struct Phase34 {
uint8_t parameters[18];
char texture_name[16];
};
#pragma pack(pop)
```
Versioned fields читаются только если версия их содержит. Для старых версий
используются runtime defaults, а raw values сохраняются.
## Fallback
Material resolve:
1. имя из WEAR;
2. `DEFAULT`;
3. entry с индексом 0.
Пустое texture name означает намеренно нетекстурированную поверхность. Lightmap
fallback отдельный: отсутствующий lightmap даёт slot `-1`.
+82
View File
@@ -0,0 +1,82 @@
# MSH
Файл `*.msh` является NRes-контейнером. Geometry, узлы, slots, batches,
animation и служебные streams лежат в entries с разными `type_id`.
## Entry map
```text
type 1 nodes and slot selection
type 2 header 0x8C + Slot68 records
type 3 positions float3
type 4 packed normals
type 5 packed UV0
type 6 index buffer u16
type 7 triangle descriptors
type 8 animation keys
type 9 service stream
type 10 strings and node names
type 13 Batch20 records
type 15 auxiliary stream
type 17 auxiliary data
type 18 rare stream
type 19 animation frame map
type 20 rare auxiliary table
```
Reader ищет entries по type, но сохраняет исходный порядок для roundtrip.
## Node and slot selection
Type 1 обычно состоит из records по 38 bytes:
```c
struct Node38 {
uint16_t hdr0;
uint16_t parent_or_link;
uint16_t anim_map_start;
uint16_t fallback_key;
uint16_t slot_index[15];
};
```
`slot_index[lod * 5 + group]` выбирает geometry slot. `0xFFFF` означает
отсутствие геометрии для комбинации LOD/group.
## Slot and batch
Type 2 содержит header `0x8C`, затем `Slot68`:
```c
struct Slot68 {
uint16_t tri_start;
uint16_t tri_count;
uint16_t batch_start;
uint16_t batch_count;
float aabb_min[3];
float aabb_max[3];
float sphere_center[3];
float sphere_radius;
uint32_t opaque[5];
};
```
Type 13 задаёт draw ranges:
```c
#pragma pack(push, 1)
struct Batch20 {
uint16_t batch_flags;
uint16_t material_index;
uint16_t opaque4;
uint16_t opaque6;
uint16_t index_count;
uint32_t index_start;
uint16_t opaque14;
uint32_t base_vertex;
};
#pragma pack(pop)
```
Index check выполняется как `base_vertex + index < vertex_count` для всего
используемого slice.
+61
View File
@@ -0,0 +1,61 @@
# NRes
`NRes` -- основной контейнер ресурсов Iron3D. Он используется как внешний
архив и как внутренний контейнер модели `*.msh`.
```text
[Header: 16 bytes]
[Data region: payload with alignment]
[Directory: entry_count * 64 bytes]
```
## Header
```c
struct NResHeader16 {
char magic[4]; // "NRes"
uint32_t version; // 0x00000100
int32_t entry_count; // >= 0
uint32_t total_size; // equals file size
};
```
`directory_offset = total_size - entry_count * 64`. Reader проверяет отсутствие
переполнений, `directory_offset >= 16` и точное окончание каталога на
`total_size`.
## Entry
```c
#pragma pack(push, 1)
struct NResEntry64 {
uint32_t type_id;
uint32_t attr1;
uint32_t attr2;
uint32_t size;
uint32_t attr3;
char name[36];
uint32_t data_offset;
uint32_t sort_index;
};
#pragma pack(pop)
```
Имя содержит bounded C-string до 35 полезных bytes. `sort_index` задаёт
отображение из sorted position в original entry index. В строгом режиме все
`sort_index` образуют перестановку `0..N-1`.
## Data region
Payload каждой записи лежит после header и до начала каталога. Игровые архивы
выравнивают следующий payload до 8 bytes нулями, но reader не должен требовать
плотного покрытия data region.
Различаются:
- active payload -- диапазон, на который указывает entry;
- gap/padding -- bytes между активными диапазонами;
- unindexed preserved region -- произвольные bytes, не принадлежащие entry.
Lossless editor сохраняет все три категории. Compact writer может исключить
unindexed regions только при явной операции repack.
+56
View File
@@ -0,0 +1,56 @@
# Render frame
Кадр является последней стадией цикла, а не самостоятельной функцией renderer-а.
До draw calls уже накоплен input, рассчитан tick, применены отложенные операции,
выбрана камера и обновлён 3D sound listener.
## Frame skeleton
```text
system messages and input
-> simulation calculation
-> deferred object operations
-> animation and transforms
-> camera and sound listener
-> visibility and render queues
-> materials and draw passes
-> renderer completion
-> end-of-render callbacks and UI
```
В `World3D::stdRenderGame` доказан крупный порядок: camera передаётся Terrain,
настраиваются viewport/matrices, вызываются renderer boundary slots,
устанавливается `in_render`, выполняется traversal мира, закрывается world/shade
pass, вызывается renderer completion, снимается `in_render`, рассылается
end-of-render.
## Draw item
Подготовленный draw item содержит:
- node world matrix;
- batch flags and index range;
- WEAR material handle;
- MAT0 active phase and coefficients;
- texture handle;
- optional lightmap handle;
- render phase and sorting key;
- legacy pipeline state.
Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase
или texture cache посреди прохода не должно менять уже собранную очередь.
## Parity risks
- x87 precision and rounding;
- scalar/SIMD `g_FastProc` differences;
- object, batch and transparent primitive order;
- depth, cull, alpha test and blend transitions;
- mip-skip, palette and Page coordinates;
- material fallback and phase selection;
- RNG sequence for FX and atmosphere;
- device capability fallback;
- simulation time quantization.
Для отладки нужен deterministic frame capture: camera state, visible object IDs,
draw-item list, pipeline keys, matrices и hashes промежуточных buffers.
+69
View File
@@ -0,0 +1,69 @@
# RsLi
`RsLi` -- библиотечный архив Iron3D с каталогом в начале файла и payloads после
него.
```text
[Header: 32 bytes]
[Entry table: entry_count * 32 bytes]
[Payloads]
[optional trailer]
```
## Header fields
```text
+0x00 char[2] "NL"
+0x02 u8 reserved
+0x03 u8 version = 1
+0x04 i16 entry_count
+0x0E u16 presorted_flag = 0xABBA
+0x14 u32 xor_seed
```
Остальные bytes сохраняются без нормализации.
## Entry
```c
struct RsLiEntry32 {
char name[12];
uint8_t service[4];
int16_t flags;
int16_t sort_to_original;
uint32_t unpacked_size;
uint32_t data_offset_raw;
uint32_t packed_size;
};
```
Имя обычно хранится в uppercase ASCII. `sort_to_original` связывает sorted
position с исходной записью.
## Table transform
Entry table проходит обратимое потоковое XOR-преобразование. Начальное
состояние берётся из младших 16 bits `xor_seed` и продолжается через всю
таблицу, не сбрасываясь на границе записи.
## Storage methods
```text
0x000 raw block
0x020 byte transform only
0x040 LZSS
0x060 transform + LZSS
0x080 adaptive Huffman + LZSS
0x0A0 transform + adaptive Huffman + LZSS
0x100 raw Deflate
```
После любого пути должно получиться ровно `unpacked_size` bytes. Методы
`0x080` и `0x0A0` подтверждены decoder-кодом, но не живыми payload демоверсии
или обеих частей.
## Compatibility quirk
`sprites.lib::INTERF8.TEX` объявляет Deflate range на один byte дальше EOF.
Совместимый reader допускает `packed_size - 1` только для этого именованного
случая. Строгий режим сообщает `deflate_eof_plus_one`.
+67
View File
@@ -0,0 +1,67 @@
# Texm
`Texm` -- основной формат изображений Iron3D. Payload содержит header,
необязательную палитру, mip chain и иногда `Page` chunk.
```c
struct TexmHeader32 {
uint32_t magic; // 'Texm'
uint32_t width;
uint32_t height;
uint32_t mip_count;
uint32_t flags4;
uint32_t flags5;
uint32_t unknown6;
uint32_t format;
};
```
## Pixel formats
```text
0 Indexed8 + palette 256 * 4 bytes
565 R5 G6 B5
556 R5 G5 B6
4444 A4 R4 G4 B4
88 L8 A8
888 RGB8 in four-byte element
8888 A8 R8 G8 B8
```
Короткие каналы расширяются до 8 bits повторением значимых bits. Для 888
служебный четвёртый byte сохраняется при roundtrip.
## Layout
```text
TexmHeader32
[palette 1024 bytes, only for format 0]
level 0 pixels
level 1 pixels
...
level mip_count-1 pixels
[optional Page chunk]
```
Размер mip level вычисляется через `max(1, width >> i)` и
`max(1, height >> i)`. Parser суммирует размеры с проверкой переполнения до
чтения данных.
## Page chunk
```c
struct PageHeader8 {
uint32_t magic; // 'Page'
uint32_t rect_count;
};
struct PageRect8 {
int16_t x;
int16_t width;
int16_t y;
int16_t height;
};
```
Chunk обязан иметь размер `8 + rect_count * 8`. Rectangles находятся в pixel
space базового mip и масштабируются после mip-skip.
+64
View File
@@ -0,0 +1,64 @@
# TMA
`data.tma` -- основное описание расстановки и логической конфигурации миссии.
Файл перечисляет paths, clans, objects, свойства, ссылку на ландшафт и extras.
## String primitive
```c
struct LpString {
uint32_t byte_length;
uint8_t bytes[byte_length];
};
```
Reader продвигается ровно на `4 + byte_length`. Завершающий NUL не является
обязательной частью framing. Для человекочитаемого вида используется legacy
ANSI/CP1251 view, но исходные bytes сохраняются.
## Top level
```text
u32 format_version
u32 path_count
PathRecord paths[path_count]
u32 clan_section_version
u32 clan_count
ClanRecord clans[clan_count]
u32 object_section_version
u32 object_count
PlacedObject objects[object_count]
LpString land_path
u32 mission_flag
LpString description_raw
u32 extra_section_version
u32 extra_count
ExtraRecord28 extras[extra_count]
```
Все 60 TMA Частей 1 и 2 проходят parser до точного EOF. Версии стабильны:
верхний уровень `1`, clan section `6`, object section `10`, property schema
`1`, trailing section `1`.
## PlacedObject
```text
u32 raw_kind
u32 class_or_flags
LpString resource_name
u32 raw_after_resource
u32 identity_or_clan_raw
f32 position[3]
f32 orientation[3]
f32 scale[3]
LpString instance_name
u32 raw_after_name
i32 link0
i32 link1
u32 property_schema_version
u32 property_count
Property properties[property_count]
```
`Property` состоит из четырёх raw `u32` и имени. Typed views разрешены только
для доказанных property names и consumers.