From 78fc5f1debf1395d5df0bab7cc0dde54351205cb Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 22 Jun 2026 01:58:51 +0400 Subject: docs: rewrite MkDocs documentation --- docs/reference/materials.md | 69 +++++++++++++++++++++++++++++++++++ docs/reference/msh.md | 82 ++++++++++++++++++++++++++++++++++++++++++ docs/reference/nres.md | 61 +++++++++++++++++++++++++++++++ docs/reference/render-frame.md | 56 +++++++++++++++++++++++++++++ docs/reference/rsli.md | 69 +++++++++++++++++++++++++++++++++++ docs/reference/texm.md | 67 ++++++++++++++++++++++++++++++++++ docs/reference/tma.md | 64 +++++++++++++++++++++++++++++++++ 7 files changed, 468 insertions(+) create mode 100644 docs/reference/materials.md create mode 100644 docs/reference/msh.md create mode 100644 docs/reference/nres.md create mode 100644 docs/reference/render-frame.md create mode 100644 docs/reference/rsli.md create mode 100644 docs/reference/texm.md create mode 100644 docs/reference/tma.md (limited to 'docs/reference') diff --git a/docs/reference/materials.md b/docs/reference/materials.md new file mode 100644 index 0000000..8146a2c --- /dev/null +++ b/docs/reference/materials.md @@ -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 + + +... + +[empty line] +[LIGHTMAPS + + +...] +``` + +`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`. diff --git a/docs/reference/msh.md b/docs/reference/msh.md new file mode 100644 index 0000000..25aaad2 --- /dev/null +++ b/docs/reference/msh.md @@ -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. diff --git a/docs/reference/nres.md b/docs/reference/nres.md new file mode 100644 index 0000000..3b8384f --- /dev/null +++ b/docs/reference/nres.md @@ -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. diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md new file mode 100644 index 0000000..6f0975c --- /dev/null +++ b/docs/reference/render-frame.md @@ -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. diff --git a/docs/reference/rsli.md b/docs/reference/rsli.md new file mode 100644 index 0000000..a28aa1d --- /dev/null +++ b/docs/reference/rsli.md @@ -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`. diff --git a/docs/reference/texm.md b/docs/reference/texm.md new file mode 100644 index 0000000..db4321e --- /dev/null +++ b/docs/reference/texm.md @@ -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. diff --git a/docs/reference/tma.md b/docs/reference/tma.md new file mode 100644 index 0000000..30ee495 --- /dev/null +++ b/docs/reference/tma.md @@ -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. -- cgit v1.2.3