docs: rewrite MkDocs documentation
This commit is contained in:
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user