- Обновлены спецификации `runtime-pipeline`, `sound`, `terrain-map-loading`, `texture`, `ui` и `wear`. - Добавлены разделы о статусе покрытия и оставшихся задачах для достижения 100% завершенности. - Внесены уточнения по архитектурным ролям, минимальным контрактам и требованиям к toolchain для каждой подсистемы. - Уточнены форматы данных и правила взаимодействия между компонентами системы.
127 lines
5.0 KiB
Markdown
127 lines
5.0 KiB
Markdown
# 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» на длинных анимациях.
|