Files
fparkan/docs/specs/msh-animation.md
Valentin Popov 0d7ae6a017
Some checks failed
Test / Lint (push) Failing after 1m10s
Test / Test (push) Has been skipped
Test / Render parity (push) Has been skipped
Документирование и обновление спецификаций
- Обновлены спецификации `runtime-pipeline`, `sound`, `terrain-map-loading`, `texture`, `ui` и `wear`.
- Добавлены разделы о статусе покрытия и оставшихся задачах для достижения 100% завершенности.
- Внесены уточнения по архитектурным ролям, минимальным контрактам и требованиям к toolchain для каждой подсистемы.
- Уточнены форматы данных и правила взаимодействия между компонентами системы.
2026-02-19 11:07:04 +04:00

5.0 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.
  • Корпусная валидация анимационных инвариантов включена в прогон tools/msh_doc_validator.py на полном retail-наборе.

7. Статус покрытия и что осталось до 100%

Закрыто:

  1. Контракт Res8 + Res19 и fallback-логика выбора ключа.
  2. Базовая интерполяция поз и blending двух сэмплов.
  3. Канонические инварианты writer path для существующих ассетов.

Осталось:

  1. Полная фиксация численного поведения на всех FP-edge-case (включая платформенные различия округления).
  2. Полный writer-профиль для авторинга новых анимаций без опоры на reference copy-through.
  3. Набор runtime parity-тестов «frame-by-frame pose equivalence» на длинных анимациях.