Допустим, вы хотите перенести модельку из одной игры в другую.

Классический путь — извлечь, перегнать в промежуточный формат, адаптировать под целевой движок и собрать заново. Звучит рационально, практично и таких решений немало:

OpenAssetTools / ZoneTool автоматизируют процесс конвертации для Call of Duty

UE Viewer экспортирует данные Unreal в GLTF, PSK, DDS и др.

Мы сегодня рассмотрим альтернативный путь. Как вам идея переписать рантайм целевой игры с нуля и добавить нативную поддержку чужих ассетов? Моя попытка сделать что‑то подобное привела к созданию экспериментального движка IW4L, основой для которого является IW4 (Call of Duty Modern Warfare 2 2009).

Карта MW2 (Afghan) с оружем MW3 (MSR)
Карта MW2 (Afghan) с оружем MW3 (MSR)
Карта BlackOps (Firing Range) с оружием BlackOps (метательный нож)
Карта BlackOps (Firing Range) с оружием BlackOps (метательный нож)

Вместо такого пути:

ассет игры A -> конвертация отдельным софтом -> формат игры B -> рантайм игры B

В IW4L я попытался убрать среднюю часть:

ассеты игр A В С -> IW4L

Рантайм должен сам найти данные, понять откуда пришли, как их разобрать и как правильно исполнить. Modern Warfare 2, Black Ops 1 и Modern Warfare 3 используют разные поколения одного семейства движков, а их fastfile‑архивы отличаются как разрядностью так и внутренними структурами, это если говорить о файлах. Но прочитать файл, увы, оказалось самой простой частью((

Шейдеры

Представьте, что сцена состоит из карты MW2, оружия из BlackOps и ещё одной модели на спине из MW3. Геометрию всех трёх можно сравнительно легко довести до общего представления: вершины и текстуры... терпимо! Но как теперь всё это отобразить? Отображением вершин, текстур, освещения, материалов, эффектов занимаются небольшие програмы, которые выполняются на GPU — шейдеры.

В оригинальных играх шейдеры хранятся в виде байт‑кода Shader Model 3 для Direct3D 9. У нас Direct3D нет: рантайм написан на модном молодёжном Rust / Bevy, графическое API у нас wgpu. Нужен слой SM3 → WGSL.

К счастью, сама иде не новая. DXVK решает гораздо более общую задачу: принимает Direct3D и исполняет его поверх Vulkan. В том числе внутри него есть полноценный путь трансляции нужного нам D3D9 в SPIR‑V. Так что устройство SM3, декодирование инструкций и огромное количество неприятных кейсов уже исследованы до нас.

Но вот только MW2, Black Ops и MW3 пусть и объединяет формат Shader Model 3, содержимое этих инструкций заметно отличаются. Причём иногда довольно радикально отличаются программы, имеющие одинаковые имена.

Просто сделать глобальную таблицу вида

shader_name -> translated_shader

не получится!

Нам приходится сохранять ещё одно измерение:

(game, shader) -> translated_shader

Рантайм должен помнить происхождение ресурса вплоть до самого этапа отрисовки. Если перед нами модель из Black Ops, недостаточно знать название её материала. Нужно:

  • знать, что материал относится к T5;

  • выбрать соответствующую T5-технику отрисовки;

  • загрузить нужные для T5 вершинные и пиксельные шейдеры;

  • правильно подготовить ожидаемые ими константы;

  • передать всё это в общий конвейер отрисовки.

Шейдер ожидает: матрицы, положение камеры, параметры света, тумана, материала и другие значения, которые исходный движок готовил перед отрисовкой. Между MW2, Black Ops и MW3 различается как набор этих данных, так и интерпретация.

Освещение

Вы видите на видео: карта Afghan загружена из MW2, а FAMAS в руках игрока — из Black Ops. Геометрия, текстуры и шейдер оружия загружены правильно. Но само оружие выглядит слишком ярким.

Причина в том, что карта формирует освещение по правилам IW4, а материал оружия ожидает данные по правилам T5. Шейдер успешно выполняется, ничего не падает, но получает не совсем те значения, на которые рассчитывал.

Получается сцена, где одновременно могут существовать:

IW5 карта -> IW4/T5 оружие -> IW5 освещение -> IW4 пайплайн отрисовки одного несчастного кадра

Именно здесь поддержка чужих файлов превращается в задачу совместимости нескольких поколений движка внутри одного кадра. Получится ли решить все такие пограничные кейсы взаимодействия карт/оружия/и так далее? Понятия не имею.

Для справки приведу скриншоты того, какие результаты показывал этот движок 11 августа (чуть меньше 4 недель назад):

Карта MW2 Scrapyard
Карта MW2 Scrapyard
Глубоко...
Глубоко...

Мне будет очень интересно закинуть статью ещё через месяц и показать прогресс, если зайдёт (:

Ремарки касательно использования AI: Проект написан десятками различных ЛЛМ и состоит из порядка ~370тыс строк на сегодняшний день. Статья написана человеком.