Files
fparkan/docs/reference/materials.md
T
Valentin Popov a8faba8aad feat: complete map viewer scene and static CTL pose preview
Complete the interactive mission viewer with environment rendering, audio events, dynamic shadows, free-flight camera controls, and per-component CTL pose sampling.
2026-10-11 13:12:50 +04:00

5.5 KiB
Raw Blame History

WEAR и MAT0

MSH batch хранит только material_index. WEAR переводит этот индекс в имя материала, а MAT0 по этому имени описывает phases, parameters и texture references.

Batch20.material_index
  -> WEAR row
  -> MAT0 entry
  -> active phase
  -> textureName

WEAR

WEAR -- текстовый ресурс type ID 0x52414557, обычно *.wea рядом с моделью.

<wearCount>
<legacyId> <materialName>
...

[empty line]
[LIGHTMAPS
<lightmapCount>
<legacyId> <lightmapName>
...]

legacyId сохраняется, но выбор выполняется по позиции строки и имени. Между основной таблицей и LIGHTMAPS нужен пустой разделитель.

MAT0

MAT0 имеет type ID 0x3054414D, обычно расположен в Material.lib. attr1 содержит runtime flags, attr2 -- версию payload.

#pragma pack(push, 1)
struct Mat0Header {
    uint16_t phase_count;
    uint16_t animation_block_count;
};

struct Phase34 {
    uint8_t parameters[18];
    char texture_name[16];
};
#pragma pack(pop)

attr1 читается из NRes entry metadata и сохраняется вместе с разрешённым материалом. Это runtime flags материала; его нельзя путать с attr2, который задаёт версию payload. Versioned prefix bytes читаются только если версия их содержит, а raw values сохраняются для последующих слоёв.

Pipeline category

Call site извлекает native category как (attr1 >> 2) & 0xF и передаёт её в LegacyPipelineState::with_material_category. Таблица содержит пять подтверждённых категорий; helper возвращает None для остальных значений и не меняет уже выбранные depth и cull state.

category source/destination colour factors alpha test
0 ONE / ZERO, blending disabled disabled
1 SRC_ALPHA / INV_SRC_ALPHA enabled, GREATER_EQUAL
2 SRC_ALPHA / ONE enabled, GREATER_EQUAL
3 ZERO / SRC_COLOR enabled, GREATER_EQUAL
4 DEST_COLOR / SRC_COLOR enabled, GREATER_EQUAL

The mapping comes from the native five render-state pairs selected by the material category. Alpha reference remains draw-range data, while the Vulkan pipeline key includes the blend mode, depth mode, cull mode and alpha-test variant so ranges with different native state do not share a pipeline.

Каждая phase занимает 34 байта: 18 parameter bytes и 16-byte C string имени текстуры. Loader переводит параметры в коэффициенты так:

bytes runtime value
p0..p2 additive RGB, p / 255
p3 opacity, p * 0.01
p4..p7 directional values, p / 255
p8..p11 specular values, p / 255
p12..p15 extra values, p / 255
p16 integer power
p17 signed TEXM page index

Texture name и page index выбираются из текущей phase. Они не смешиваются с соседней phase.

Animation

После phase table идут animation_block_count плотных блоков. Каждый блок имеет u32 header_raw, u16 key_count, затем key_count записей по три u16:

u32 header_raw
u16 key_count
repeat key_count:
    u16 phase_index
    u16 end_time_ms
    u16 raw_k2

header_raw & 7 задаёт режим: 0 loop, 1 ping-pong, 2 clamp, 3 random-per-query. Значения 4..7 сохраняются как unknown. header_raw >> 3 является interpolation mask: bits 1, 2, 4, 8 смешивают группы p0..p2, p4..p6, p8..p10, p12..p14, а bit 16 смешивает p3. Компоненты p7, p11, p15, p16 и p17 копируются из текущей phase.

Время для блока вычисляется как clock_ms - wear_row_start_ms с wrapping u32 subtraction. Для loop берётся остаток от последнего end_time_ms, для ping-pong нечётный цикл идёт в обратном направлении, clamp после duration выбирает последнюю phase, а random-per-query использует переданное случайное значение по модулю duration. Ключ выбирается по интервалу previous_end <= local_time < end_time; следующий ключ циклически замыкается на первый. На ping-pong turnaround и clamp после duration native routine проходит общий float interpolation path, включая unsigned subtraction endpoint arithmetic; это может дать extrapolated coefficients при ненулевой mask. Переданный в sampler animation_block_index уже выбран вызывающим runtime из block table; packed WEAR handle и номер строки WEAR являются отдельными значениями.

Fallback

Material resolve:

  1. имя из WEAR;
  2. DEFAULT;
  3. entry с индексом 0.

Пустое texture name означает намеренно нетекстурированную поверхность. Lightmap fallback отдельный: отсутствующий lightmap даёт slot -1.