aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/msh-animation.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/specs/msh-animation.md')
-rw-r--r--docs/specs/msh-animation.md126
1 files changed, 0 insertions, 126 deletions
diff --git a/docs/specs/msh-animation.md b/docs/specs/msh-animation.md
deleted file mode 100644
index 1c0807a..0000000
--- a/docs/specs/msh-animation.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# 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. Статус валидации
-
-- Форматные проверки были покрыты legacy-валидатором.
-- Корпусная валидация анимационных инвариантов выполнена на полном 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» на длинных анимациях.