Я наконец-то созрел поделиться сакральным знанием о том, как создать .mrf файл из Blender, и вывести его в игре. Тем, кто не понимает о чём это, рекомендую посмотреть основную статью. В двух словах: плащ Артаса из фрозентроновского предфинального ролика — это, так называемая, морф таргет анимация, в ключевых кадрах которой записаны позиции и нормали для каждой вершины, и хранится это в файлах с расширением .mrf. Применив некоторые хитрости, мы можем сделать то же самое, то есть импортировать свою морф-анимацию в игру. Зачем? А просто так, прикольно же.
Сразу тезисно расскажу почему вам не стоит этим заниматься какие тут есть ограничения и проблемы:
- MRF анимация имеет очень узкое применение в игре. Её не получится отрендерить в мире (разве что как статику), она работает полноценно только в экранном пространстве, только при выводе модели через внутреннюю камеру, и только в SD режиме.
- При выводе через Frame API наследуется стандартная проблема спрайтов в варкрафте, а именно пересечение геометрии спрайта с геометрией мира. Да, спрайтовый меш может “провалиться” под ландшафт или пройти насквозь моделей из мира. Вроде как это связано с тем, что расстояние от внутренней камеры до вершин спрайта должно быть меньше (а может, наоборот, больше), чем расстояние от игровой камеры до игрового мира, но это не точно. Скрытие террейна нативкой и использование ORIGINAL_FRAME_PORTRAIT в качестве родителя иногда могут помочь. Говорят, что в UjAPI эта проблема исправлена.
- Это редкий баг, но если баловаться с окном варкрафта, типа менять размеры и активно альт-табаться, то можно получить небольшую рассинхронизацию MRF меша и его родительской модели.
- Файл MRF будет тяжёлый. Размер итоговой анимации будет составлять число ключевых кадров x число вершин x 24 байтов, а в файле не только анимация.
- Позиции и нормали вершин интерполируются в игре линейно, из-за чего при работе со сложным морфом можно словить фрустрацию, увидев, что красивая симуляция из 3D-редактора выглядит несколько топорно в варкрафте, хотя вроде бы двигается по тем же ключевым кадрам.
- Тупой, прибитый гвоздями шейдер. Из опций доступен только путь к диффузке, у которой ещё и альфа отрезается по дороге. Но запечённые нормали работают, мягкие тени от источника света присутствуют, см. пример с Сюзанной в начале статьи.
Учитывая всё вышесказанное, если есть необходимость использовать подобную морф-анимацию в игре, я бы рекомендовал рассмотреть возможность запекания её в костную. Вроде так кто-то уже делал.
Софт:
- Blender 4 +. Буду показывать на 4.4.3.
- Плагин экспортёр. Это то, с помощью чего сцена будет экспортирована в MRF файл
- Редактор моделей. Вот тут уже опциональная и вариативная штука. В принципе, никаких сопутствующих мешей, как в сцене с Артасом, может и не быть, но нам всё равно понадобится родительская модель, имеющая, как минимум событийный объект, камеру и секвенцию. Это можно сделать как угодно, любым удобным инструментом. Я форкнул наиболее актуальный аддон для экспорта .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.
Ну как бы вот.
Не думал попробовать сделать трансфер этой анимации вершин/нормалей в костную анимацию?
Типо каждую вершину прикрепить к отдельной кости и анимировать саму кость.
Тогда через перемещение кости будет двигаться вершина, а через поворот кости будет вращаться ее нормаль.