Разжимаем древний формат сжатия анимаций

image

В один день я просматривал различные видео на YouTube, связанные с персонажами программы Vocaloid (не совсем точное описание, но дальше буду называть просто вокалоидами). Одним из таких видео было так называемое PV из игры Hatsune Miku: Project DIVA 2nd. А именно песня relations из The Idolmaster, которую исполняли вокалоиды Megurine Luka и Kagamine Rin. Оба персонажа от Crypton Future Media. Порыскав по сети я понял, что никто так и не смог сконвертировать анимации из этой игры? Но почему? Об этом под катом.

Сама игра использует Alchemy Engine, который разработала Intrinsic Graphics, а позже купила Vicarious Visions. Это можно увидеть по файлам, имеющим расширение ".igb" (далее — IGB), а также соответствующим строкам в них. Сами файлы бинарные. Погуглив немного я нашёл скрипт от тов. minmode для известной в определённых кругах программы Noesis. Запускаем её, с перекинутым в папку скриптом, пытаемся открыть файл анимаций и… Получаем тыкву.

image

Как объяснил тов. minmode в своём посте на DeviantArt, этот скрипт не может прочитать анимацию, сжатую некоей Enbaya. В Google Patents я смог найти только подобное. Самим патентам уже лет 19-20, поэтому я и предполагаю, что сам алгоритм сжатия тоже древний. Да и сам сайт на это тоже намекает (доступен только через веб-архив). Поискав ещё немного я понял, что этот алгоритм был в составе некоего ProGATE от компании Enbaya. Но это ничего нам не даёт.

Вернёмся же к IGB. Переписав код для IGB, который я смог найти, а также воспользовавшись скриптом для Noesis, на C#, картина начала проясняться.

Ниже я приведу таблицу элементов, как она была выстроена в файлах IGB в этой игре (простите за корявость. Иначе не умею). Приведу только нужные нам элементы

Уточнение — *List — массив из элементов *

igAnimationDatabase
--igSkeletonList
---igSkeleton - Скелет, который, возможно, будет использоваться нами
----igSkeletonBoneInfoList
-----igSkeletonBoneInfo - Нода скелета
--igAnimationList
---igAnimation - Наша анимация
----igAnimationBindingList
-----igAnimationBinding - Ссылается на igSkeleton. Служит для линковки скелета к анимации
----igAnimationTrackList
-----igAnimationTrack - Сам трек анимации
------igEnbayaTransformSource
-------igEnbayaAnimationSource
--------igData - Тут уже хранятся сырые данные Enbaya
igData - Нода с данными, которые уже не являются нодами.

Таким образом я смог достать сырые данные для дальнейшего изучения. С помощью PPSSPP, Ghidra и плагина для неё я начал изучать бинарник игры. Я уже не особо помню как именно нашёл нужные функции, но приведу конкретные функции из EBOOT.BIT из ULJM05681 (в данном случае это не первая, а вторая, так называемая Bargain Version или же Project Diva 2nd#):

0x08A08050 — инициализация функции декомпрессии на основе заголовка из igData
0x08A0876C — запрос данных по конкретному времени (да. Enbaya работает со временем, не кадрами).

Сам код декомпилирован и выложен на GitLab. Написан он на Си. Компилируется как в Visual Studio, так и в gcc. Работает как в x86, так и в x64.

Я не стану углубляться в сам алгоритм. За меня лучше расскажет мой код.

Но если кратко, то Enbaya использует дельту для данных о перемещении и кватернионах. Дельту оно применяет, просто прибавляя/отнимая её к/от предыдущим/текущих данных. Перемещение остаётся как есть, а кватернион нормализуется для дальнейшего использования. Алгоритм позволяет вернуться назад во времени, не перезагружая файл. При этом он оперирует не частотой кадров, а семплами в секунду. Для этого он в памяти хранит два состояния — предыдущий семпл и следующий, а движок сам интерполирует значение между ними. Однако в следствии того, что у нас данные в файле везде в целочисленном виде, мы должны их на что-то делить (точнее умножить. например на 0.0002), чтобы получить дробное число. Это число указывается в заголовке. Из-за этого деления (на самом деле умножения, но не суть) с каждым сложением и вычитанием точность немного уплывает.

А на этом всё. Честно говоря, мне было весело реверсить всё это. Надеюсь, что мои труды не прошли даром.

P.S. Используя данные igSkeleton мы уже можем получить готовую анимацию и экспортировать её, например в Maya. Через тот же Noesis.

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 6

    +1
    Средняя нога действительно есть в игре?
    Или это ошибка алгоритма?
      0
      Скорее используется как точка привязки.
        0

        Это не нога, это код для поддержки Ли (персонажа).

          0

          Через эту "ногу" осуществляется "перемещение" скелета. Сам скелет построен таким образом. Самый верхний узел же (по другому root bone) отвечает за перемещение персонажа в игровом мире. В случае данной игры это используют в так называемом "Edit Mode" или в некоторых PV.

          0
          Однако в следствии того, что у нас данные в файле везде в целочисленном виде, мы должны их на что-то делить (точнее умножить. например на 0.0002), чтобы получить дробное число. Это число указывается в заголовке. Из-за этого деления (на самом деле умножения, но не суть) с каждым сложением и вычитанием точность немного уплывает.

          Сдаётся мне, что там математика с фиксированной точкой. Вам нужно корректно считать именно таким способом, не переходя в float point, чтобы всё было так же точно, как в оригинале. Если нужно больше деталей — ищите по ключевым словам "fixed point math".

            0

            Математика не с фиксированной точкой. Тут используется floating point. В самом файле это "0.0002" в floating point. Да и в самом коде в игре используется floating point.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое