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

127 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MSH animation
`MSH animation` описывает связку `Res8 + Res19` и runtime-правила сэмплирования/смешивания поз.
Связанные страницы:
- [MSH core](msh-core.md)
- [Render pipeline](render.md)
## 1. Ресурсы анимации
### 1.1. `Res8` (пул ключей)
```c
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` (карта кадров)
```c
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» на длинных анимациях.