Files
fparkan/docs/specs/msh-animation.md
Valentin Popov 0e19660eb5 Refactor documentation structure and add new specifications
- Updated MSH documentation to reflect changes in material, wear, and texture specifications.
- Introduced new `render.md` file detailing the render pipeline process.
- Removed outdated sections from `runtime-pipeline.md` and redirected to `render.md`.
- Added detailed specifications for `Texm` texture format and `WEAR` wear table.
- Updated navigation in `mkdocs.yml` to align with new documentation structure.
2026-02-19 04:46:23 +04:00

4.2 KiB
Raw Blame History

MSH animation

MSH animation описывает связку Res8 + Res19 и runtime-правила сэмплирования/смешивания поз.

Связанные страницы:

1. Ресурсы анимации

1.1. Res8 (пул ключей)

struct AnimKey24 {
    float   pos_x;
    float   pos_y;
    float   pos_z;
    float   time;
    int16_t qx;
    int16_t qy;
    int16_t qz;
    int16_t qw;
};

Декодирование quaternion-компонент: q = s16 / 32767.0.

1.2. Res19 (карта кадров)

uint16_t map_words[]; // size/2 элементов

Res19.attr2 хранит глобальную длину таймлайна (число кадров).

1.3. Связь с Res1

Для каждого узла:

  • anim_map_start (hdr2) — начало блока в Res19 или 0xFFFF.
  • fallback_key (hdr3) — индекс fallback-ключа в Res8.

2. Сэмплирование узла

Вход: время t, текущий узел.
Выход: quat(w,x,y,z) и pos(x,y,z).

2.1. Индекс кадра

Движок использует x87-совместимое округление для выражения t - 0.5.
Для 1:1 повторения нужно сохранить ту же политику плавающей точки.

2.2. Выбор key index

  1. Если кадр вне диапазона frame_count -> fallback_key.
  2. Если anim_map_start == 0xFFFF -> fallback_key.
  3. Иначе берётся map_words[anim_map_start + frame]:
    • если значение >= fallback_key, тоже используется fallback_key;
    • иначе используется значение из map.

2.3. Интерполяция

Если выбран fallback, возвращается ровно этот ключ без интерполяции.

Иначе:

  1. Берутся соседние ключи k0 и k1.
  2. Если t точно равен k0.time или k1.time, возвращается соответствующий ключ.
  3. Иначе:
    • alpha = (t - k0.time) / (k1.time - k0.time)
    • pos = lerp(k0.pos, k1.pos, alpha)
    • quat = slerp_like(k0.quat, k1.quat, alpha)

Кватернион в runtime хранится в порядке [w, x, y, z].

3. Смешивание двух сэмплов

При blending между позами A и B:

  1. Выбираются валидные стороны по blend и валидности времени.
  2. Если активна одна сторона, берётся она.
  3. Если активны обе:
    • применяется shortest-path flip для qB;
    • выполняется quaternion blend;
    • позиция смешивается линейно.

Матрица строится из quaternion, а translation подставляется отдельным шагом.

4. Каноника writer

Рекомендуемые правила:

  1. Ключи узлов писать подряд в Res8 в порядке узлов.
  2. fallback_key узла указывает на последний ключ его трека.
  3. Для узлов с map выделять блок длины frame_count в Res19.
  4. Для статических узлов: anim_map_start = 0xFFFF, один ключ с time=0.
  5. Res8.attr1 = key_count, Res8.attr3 = 4.
  6. Res19.attr1 = map_word_count, Res19.attr2 = frame_count, Res19.attr3 = 2.

5. Валидация перед сохранением

  • Res8.size % 24 == 0
  • Res19.size % 2 == 0
  • каждый fallback_key < key_count
  • для узла с map: anim_map_start + frame_count <= map_word_count
  • внутри трека времена ключей строго возрастают

6. Статус валидации

  • Форматные проверки включены в tools/msh_doc_validator.py.
  • В текущем окружении полный игровой корпус MSH не подключен в testdata, поэтому массовый прогон здесь не выполнялся.