3.4 KiB
3.4 KiB
MSH animation
Документ описывает анимационные ресурсы MSH: Res8, Res19 и runtime-интерполяцию.
1.13. Ресурсы анимации: Res8 и Res19
- Res8 — массив анимационных ключей фиксированного размера 24 байта.
- Res19 —
uint16mapping‑массив «frame → keyIndex` (с per-node смещением).
1.13.1. Формат Res8 (ключ 24 байта)
struct AnimKey24 {
float posX; // +0x00
float posY; // +0x04
float posZ; // +0x08
float time; // +0x0C
int16_t qx; // +0x10
int16_t qy; // +0x12
int16_t qz; // +0x14
int16_t qw; // +0x16
};
Декодирование quaternion-компонент:
q = s16 * (1.0f / 32767.0f)
1.13.2. Формат Res19
Res19 читается как непрерывный массив uint16:
uint16_t map[]; // размер = size(Res19)/2
Per-node управление mapping'ом берётся из заголовка узла Res1:
node.hdr2(Res1 + 0x04) =mapStart(0xFFFF=> map отсутствует);node.hdr3(Res1 + 0x06) =fallbackKeyIndexи одновременно верхняя граница валидногоmap‑значения.
1.13.3. Выбор ключа для времени t (sub_10012880)
- Вычислить frame‑индекс:
frame = (int64)(t - 0.5f); // x87 FISTP-путь
Для строгой 1:1 эмуляции используйте именно поведение x87 FISTP (а не «упрощённый floor»), т.к. путь в оригинале опирается на FPU rounding mode.
- Проверка условий fallback:
frame >= model.animFrameCount(model+0x9C, изNResEntry(Res19).attr2);mapStart == 0xFFFF;map[mapStart + frame] >= fallbackKeyIndex.
Если любое условие истинно:
keyIndex = fallbackKeyIndex;
Иначе:
keyIndex = map[mapStart + frame];
- Сэмплирование:
k0 = Res8[keyIndex]k1 = Res8[keyIndex + 1](для интерполяции сегмента)
Пути:
- если
t == k0.time→ взятьk0; - если
t == k1.time→ взятьk1; - иначе
alpha = (t - k0.time) / (k1.time - k0.time),pos = lerp(k0.pos, k1.pos, alpha), rotation смешивается через fastproc‑интерполятор quaternion.
1.13.4. Межкадровое смешивание (sub_10012560)
Функция смешивает два сэмпла (например, из двух animation time-позиций) с коэффициентом blend:
- получить два
(quat, pos)черезsub_10012880; - выполнить shortest‑path коррекцию знака quaternion:
if (|q0 + q1|^2 < |q0 - q1|^2) q1 = -q1;
- смешать quaternion (fastproc) и построить orientation‑матрицу;
- translation писать отдельно как
lerp(pos0, pos1, blend)в ячейкиm[3], m[7], m[11].
1.13.5. Что хранится в Res19.attr2
При загрузке sub_10015FD0 записывает NResEntry(Res19).attr2 в model+0x9C.
Это поле используется как верхняя граница frame‑индекса в п.1.13.3.