MRF — бессмысленный и беспощадный

Содержание:
Я наконец-то созрел поделиться сакральным знанием о том, как создать .mrf файл из Blender, и вывести его в игре. Тем, кто не понимает о чём это, рекомендую посмотреть основную статью. В двух словах: плащ Артаса из фрозентроновского предфинального ролика — это, так называемая, морф таргет анимация, в ключевых кадрах которой записаны позиции и нормали для каждой вершины, и хранится это в файлах с расширением .mrf. Применив некоторые хитрости, мы можем сделать то же самое, то есть импортировать свою морф-анимацию в игру. Зачем? А просто так, прикольно же.

Сразу тезисно расскажу почему вам не стоит этим заниматься какие тут есть ограничения и проблемы:
  • MRF анимация имеет очень узкое применение в игре. Её не получится отрендерить в мире (разве что как статику), она работает полноценно только в экранном пространстве, только при выводе модели через внутреннюю камеру, и только в SD режиме.
  • При выводе через Frame API наследуется стандартная проблема спрайтов в варкрафте, а именно пересечение геометрии спрайта с геометрией мира. Да, спрайтовый меш может “провалиться” под ландшафт или пройти насквозь моделей из мира. Вроде как это связано с тем, что расстояние от внутренней камеры до вершин спрайта должно быть меньше (а может, наоборот, больше), чем расстояние от игровой камеры до игрового мира, но это не точно. Скрытие террейна нативкой и использование ORIGINAL_FRAME_PORTRAIT в качестве родителя иногда могут помочь. Говорят, что в UjAPI эта проблема исправлена.
  • Это редкий баг, но если баловаться с окном варкрафта, типа менять размеры и активно альт-табаться, то можно получить небольшую рассинхронизацию MRF меша и его родительской модели.
  • Файл MRF будет тяжёлый. Размер итоговой анимации будет составлять число ключевых кадров x число вершин x 24 байтов, а в файле не только анимация.
  • Позиции и нормали вершин интерполируются в игре линейно, из-за чего при работе со сложным морфом можно словить фрустрацию, увидев, что красивая симуляция из 3D-редактора выглядит несколько топорно в варкрафте, хотя вроде бы двигается по тем же ключевым кадрам.
  • Тупой, прибитый гвоздями шейдер. Из опций доступен только путь к диффузке, у которой ещё и альфа отрезается по дороге. Но запечённые нормали работают, мягкие тени от источника света присутствуют, см. пример с Сюзанной в начале статьи.
Учитывая всё вышесказанное, если есть необходимость использовать подобную морф-анимацию в игре, я бы рекомендовал рассмотреть возможность запекания её в костную. Вроде так кто-то уже делал.

Софт:

  • Blender 4 +. Буду показывать на 4.4.3.
  • Редактор моделей. Вот тут уже опциональная и вариативная штука. В принципе, никаких сопутствующих мешей, как в сцене с Артасом, может и не быть, но нам всё равно понадобится родительская модель, имеющая, как минимум событийный объект, камеру и секвенцию. Это можно сделать как угодно, любым удобным инструментом. Я форкнул наиболее актуальный аддон для экспорта .mdl из Блендера, и сделал там некоторые изменения, которые позволяют собрать готовую MDL сцену с интегрированным MRF прямо в Блендере, то есть вообще без дополнительной обработки файла. Показывать буду далее на нём. Найти можно тут.

Подготовка сцены

Чтобы экспортировать вертексную анимацию нужно для начала её создать. Подойдёт симуляция ткани, Shape Keys, разные анимированные деформации меша, короче любая анимация, которая соответствует правилу Число вершин не меняется, меняются только их позиции. Симуляция жидкости, например, не подходит.
Я создал простую сценку. Пол и арка с коллизией, на арку падает ткань. Потом арка почему-то прыгает и ткань сваливается на пол. Всё использует стандартные варкрафтовские текстуры. Через пару минут эта сцена будет в варкрафте.
Итак, начнём с камеры. Shift + A -> Camera. Наводим её на арку так, как хотим. Я упоминал, что есть проблема с провалом спрайта в мировые модели, поэтому я бы делал камеру поближе. В вид из камеры в Blender можно переключиться через Num0. Увы, это не совсем то, как будет выглядеть вид из камеры в игре, но что-то близкое.

Теперь нужно разметить секвенции на таймлайне с помощью маркеров. Для MDL экспортёра это является стандартной практикой, и эту же методику же я использовал в своём MRF экспортёре. Для создания маркера нужно использовать хоткей M, для его переименования — F2. ИЛИ можно воспользоваться гуи, который можно вызвать с помощью Shift + A -> MDL Add -> Add Warcraft 3 Sequence. ИЛИ можно перейти на вкладку блендера Scene, найти там свиток Warcraft 3 Sequences, и сделать там всё то же самое с помощью гуи.
Как это будет работать? Пара маркеров с одинаковыми именами = секвенция. Если нам нужна анимация Stand, то называем маркеры Stand. Всё просто. И ещё нам нужна секвенция с именем MRF. Именно кадры из этого участка анимационной линейки будут записаны во внешний файл. Отмечу, что при использовании моего форка MDL секвенция с именем MRF экспортирована НЕ будет, это сделано для удобства. То есть ещё раз. Размечаем MDL секвенции как хотим. Отмечаем MRF секвенцию с чётким понимаем, что это будет анимация в отдельном файле, которую мы в тот или иной момент вызовем из родительской MDL модели. В данном случае, я хочу, чтобы MRF и Stand анимации чётко совпадали по длине в игре, поэтому я даю им совпадающие начало и конец.
Ещё упомяну, что MRF экспортёр поддерживает маркер с именем MRF_START. Если поместить его между двумя маркерами с именем MRF, то в итоговый файл будет записан параметр elapsed time. В игре именно с этого кадра начнётся рендер морфинга на экране. Для чего близзы сделали эту опцию мне не понятно, но она поддерживается плагином. Если туда отправить отрицательное значение (можно сделать это непосредственно при настройке параметров экспорта), то получится задержка проигрывания анимации.
В обоих экспортёрах для расчёта длительности анимации используется параметр scene.render.fps. В интерфейсе он задаётся на вкладке Output, в свитке Format, в опции Frame Rate. Можно любой, главное, чтобы одинаковый в обоих случаях. 30 FPS рекомендуется (у близзов в плаще Артаса было так).

Пора добавить событийный объект. Shift + A -> MDL Add -> Add MDL Event Object. На вкладке Object можно его настроить. Во-первых, нужно имя. Формат имени MRFXString, где MRF — тип событийного объекта (может быть также MRD для уничтожения морфа), X — произвольный символ, служащий для уникальности имён используемых событийных объектов (в данном случае не имеет особого смысла, но это стандартный нейминг ивентов в варкрафте), String — произвольная строка, которую игровой лоадер подставит в захардкоженную строку doodads\cinematic\arthasillidanfight\arthascape%s.mrf вместо %s, чтобы найти нужный файл.
Теперь листаем в самый низ свойства ивента и находит свиток Custom Properties. Открываем, и находим event_track. Это то, что нам нужно. На таймлайне выбираем нужную позицию (я выбираю в самом начале секвенции Stand), наводим курсор на интерактивное поле для параметра event_track, и нажимаем хоткей I. Появится ключевой кадр, это тот самый кадр, на котором появится MRF анимация в игре.
Позиция ивента в сцене значения не имеет.

Ещё в настройках материала нужно указать путь к текстуре. Для этого необходимо перейти выделить объект, перейти на вкладку Material, и в активном материале найти свиток MRF Texture. В поле Path можно указать путь к файлу текстуры, как к стандартному, так и импортируемому. Материалы будущих MDL мешей настраиваются в свитке MDL Material Settings.

Ну как бы всё, к экспорту готово. Да, если будущий MRF меш использует симуляцию ткани, или что-то подобное, и Blender предоставляет возможность “запечь” анимацию, то лучше это сделать. В случае ткани это можно сделать в свитке Cache.

Экспорт

Так, левой кнопкой мыши выделяем нужный меш с вертексной анимацией, только его. File -> Export -> Warcraft MORF. Появится куча опций, но не пугайтесь, на самом деле это просто ребёнку дали игрушку, они по большей части не нужны.
Самая важная опция это Scale Factor. Блендеровские юниты слишком маленькие для варкрафта, так что лучше увеличить. Можно указать любое значение, НО в экспортёре MDL нужно будет использовать такое же самое значение. По умолчанию стоит 50, также как и в моём форке MDL экспортёра.
Вторая важная опция это чекбокс Pack for Import. Объясню. Если его включить, то при сохранении файла, например, MyMorph.mrf вместо него в заданном расположении будет создана папка doodads, в которой будет папка cinematic, в которой будет папка arthasillidanfight, в которой уже появится файл с именем arthascapeMyMorph.mrf. Это прям очень удобно при работе с картой в режиме папки в Reforged. Указал корень, тыкнул Pack for Import, и всё готово. Ну, для любителей подёргать менеджер импорта опция бесполезна. В любом случае, в карте нужно форматировать итоговый путь к файлу именно таким образом, поэтому я автоматизировал это для себя.
Ещё для себя я делал опцию Reverse Animation, при включении которой кадры анимации будут записаны задом наперёд. Чисто фановая штука, смятую ткань можно вернуть назад в плоскость, или что-то подобное. Все остальные опции экспорта для молодых мрфщиков не несут особой смысловой нагрузки, и рекомендую оставлять там значения по умолчанию.
При сохранении даём файлу именно такое имя, которое было указано в названии событийного объекта!

Так, теперь экспортируем родительскую модель. Выделяем всё, кроме морфируемого меша. Точнее не всё, а то, что хотим отправить в варкрафт. В моём случае это меш пола, меш арки, арматура арки, событийный объект и камеры. File -> Export -> Warcraft 3 MDL. Ставим галочку Selected Objects, задаём Scale такой же, как был в MRF и сохраняем в карту.

Проверка в игре

Всё, теперь пора вывести это в игре. Файлы Archway.mdl и arthascapeArchway.mrf уже лежат в папке моей карты по нужным путям, так что никаких дополнительных действий в редакторе не требуется.
Варианта вывода всего три: фрейм, нативка PlayCinematicModel и портрет. Напишу код для создания спрайта с помощью Frame API, и запущу проверку карты прямо из IDE с помощью cheapack.
local fr = BlzCreateFrameByType("SPRITE", "", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
BlzFrameSetAbsPoint(fr, FRAMEPOINT_CENTER, 0.4, 0.3)
BlzFrameSetSize(fr, 0.001, 0.001)
BlzFrameSetModel(fr, "Archway", 0)
Родителя для фрейма можно использовать любого другого, не забудьте указать правильный ID камеры в BlzFrameSetModel.

Ну как бы вот.

Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
18
Действительно бессмысленный, и действительно беспощадный.
Не думал попробовать сделать трансфер этой анимации вершин/нормалей в костную анимацию?
Типо каждую вершину прикрепить к отдельной кости и анимировать саму кость.
Тогда через перемещение кости будет двигаться вершина, а через поворот кости будет вращаться ее нормаль.
Ответы (2)
25
OVOgenez, я ни о чём таком не думал, потому что у меня никогда не стояло задачи сделать какую-то нужную анимацию и использовать её в реальном проекте. Просто было интересно полностью разобрать конкретно этот формат, и я периодически к нему возвращался.
Но вообще да, так можно, и готовые решения, видимо, существуют.
Единственное, что придётся учитывать ограничения MDX. Если не ошибаюсь, то там для скиннинга не более 256 групп вершин на геосет, а если использовать таблицу весов из MDX1100, то не более 256 костей на файл. Кстати, плащ Артаса бы влез, там 170 вершин.

Есть ещё один наркоманский вариант: каждый кадр анимированного меша запечь в отдельный геосет, и затем их показывать и скрывать по очереди. И самый прикол в том, что близзы реально так сделали! В той же самой модели битвы Артаса с Иллиданом симуляция волос Артаса выполнена именно таким образом. Очевидный минус — большой вес выходного файла, но в остальном вполне гибкая и перспективная штука (могла бы быть 15 лет назад).
Загруженные файлы
18
А, ну и я слепой, не увидел что ты видос скинул 12 летней давности)
11
А по идеи получится использовать на 3D экране кампании например ?
Ответы (1)
25
prizraknadache, нет. Почему-то в экранах кампаний ведёт себя так же, как и в мире, то есть рендерится статичный нулевой кадр, без анимации.
Зато в главном меню 1.26a работает (только освещение нужно нормальное в родительской модели организовать)
Загруженные файлы
Чтобы оставить комментарий, пожалуйста, войдите на сайт.