
Считается, что порт Doom можно запустить на любом технологическом устройстве. У игры изящный, быстрый, модульный код, который может работать практически везде. Порты выпустили даже для систем, появившихся до Doom, а некоторым портам помогает специальное оборудование как, например, в случае первого Doom на SNES. Чистый и изящный код написан на C, в нём не так сложно разобраться. В нём не используются никакие современные дополнения языка и какие-то устаревшие технологии, поэтому он замечательно компилируется. Кроме того, он удобен для разных endiannes, что очень важно для портируемости.
Поэтому неожиданно, что порт Doom так и не появился на Neo Geo. В конце концов, в Neo Geo работает тот же CPU Motorola 68000, что и в Sega Genesis с Commodore Amiga, а для обеих этих платформ, несмотря на их слабые характеристики, есть версии Doom. Для Neo Geo же её найти невозможно. Многие разработчики, работавшие с Neo Geo, часто говорили, что на ней практически невозможно запустить Doom. Но почему? У этой консоли есть видеодисплей, CPU, ОЗУ и всё необходимое для запуска видеоигр. Давайте же разберёмся.

Во-первых, в Neo Geo установлен процессор Motorola 68000 с частотой 12 МГц. Также у неё есть 64 КБ ОЗУ, 84 КБ VRAM и 2 КБ аудиопамяти. Её графический дисплей работает с разрешением 320×224 и может одновременно отображать 3840 цветов из палитры 65536 цветов. Уже одно это кажется довольно серьёзными ограничениями для Doom, но, как мы видели, он запускался на всевозможном оборудовании. Так что давайте двигаться дальше.
Проблема в том, что Neo Geo — это консоль, предназначенная для работы с 2D-изображениями на основе спрайтов, и именно спрайты она использует для рендеринга. Система отрисовывает их вертикальными полосами тайлов, имеющих размер 16×16 пикселей с ограничением в 96 спрайтов на растровую строку, а максимальный размер спрайта составляет 16×512 пикселей. Как и Amiga, Neo Geo не имеет битового графического режима, то есть в ней нельзя напрямую считывать и записывать пиксели в память дисплея и из неё. Но мы уже знаем, что Doom работает на Amiga, так как же это возможно?
На Amiga графика может отрисовываться на экран в память чипа при помощи записи значений в битовые плоскости Amiga. В портах Doom для Amiga используется преобразование «chunky-to-planar», позволяющее преобразовывать битовые данные, которые Doom выводит в свой буфер кадров, в подходящий формат, который может отображать Amiga. Это довольно медленный процесс, и поэтому для запуска Doom требуется довольно быстрый процессор 030 или более мощная разогнанная модель Amiga. Одно лишь это становится серьёзным минусом Neo Geo, ведь её оборудование неизменно: невозможно установить на её материнскую плату более мощный процессор.
Сила Neo Geo заключается в 2D. В консоли нет концепции 3D, отрисовки линий или векторной графики. Даже до игр наподобие Doom для Amiga выпускались такие игры, как Starglider 2 компании Argonaut, множество симуляторов полёта и трёхмерных векторных игр. Аппаратный блиттер Amiga может отрисовывать линии и достаточно быстро заполнять их. У Neo Geo же есть только 2D-спрайты. Разработчики некоторых игр для Neo Geo пытались имитировать 3D реального времени, например во Viewpoint и The Super Spy, но на самом деле всё это были заранее вычисленные спрайтовые данные, хранящиеся на картридже Neo Geo.
The Super Spy имитирует вид от первого лица при помощи аппаратной функции масштабирования спрайтов, которая позволяет сжимать спрайты с попиксельной точностью по обеим координатам. На самом деле, эта функция применяется во многих играх, это главная «фишка» Neo Geo, придающая консоли её уникальный стиль и очарование. Игры-файтинги часто использовали приближение и отдаление в синхронизации с геймплеем, чтобы усилить ощущение погружения.
Всё изложенное выше на самом деле не исключает полностью запуска порта Doom на Neo Geo. Можно было бы обойти все эти проблемы. Самой же серьёзной преградой я считаю то, что CPU 68000 не имел прямого доступа к графическим данным на картридже Neo Geo, и этот архитектурный фактор многие недооценивают. C-ROM, содержащий спрайтовую графику, соединён напрямую с видеочипом LSPC, но он не находится на адресной шине 68000. Из-за этого CPU не может выбирать данные непосредственно с C-ROM. Он не может получить текстурные данные, не может считать пиксель, вообще не может делать что-либо с графическими данными. Единственное, что он может — записывать во VRAM ссылки, например, номера тайлов, позиции и значения масштаба, а видеочип уже сам получает из C-ROM реальные пиксели.

Такая архитектура сильно отличается от архитектуры машины с буфером кадров и даже от Amiga с её битовыми плоскостями, которые свободно могут считывать и записывать пиксели. Даже если бы мы попробовали реализовать программный рендерер, Neo Geo попросту на это неспособна. Она не может читать графику из источника, и у неё нет буфера кадров для сохранения результатов.
Итак, мы поговорили о трудностях, но, что если мы всё-таки откажемся от идеи воссоздания Doom и начнём с чего-то попроще? Можно ли на Neo Geo рендерить простую систему рейкастинга наподобие используемого в Wolfenstein 3D?

Как мы говорили, Neo Geo отрисовывает всё при помощи спрайтов, а спрайт состоит из небольших изображений-тайлов размером 16×16 пикселей, выстроенных в вертикальную полосу. Neo Geo хранит список спрайтов, их позиции и свойства в специальной области памяти, а её оборудование в каждом кадре рендерит этот список. Также мы говорили, что Neo Geo практически без вычислительных затрат способна уменьшать масштаб спрайтов независимо по осям X и Y. То есть, теоретически, есть возможность создания системы рейкастинга, в котором каждый столбец стены — это уменьшенный спрайт.
Экран системы рейкастинга собирается из вертикальных полос. Для создания каждого столбца можно испустить луч в мир, определить, где он пересекается со стеной, измерить расстояние и отрисовать вертикальную линию, высота которой зависит от расстояния. И если разрешение достаточно большое, то наш мозг воспримет это как 3D сцену. Благодаря этому мне удалось создать простой рейкастер для Neo Geo. Работает он следующим образом: у нас есть 80 стоящих бок о бок спрайтов шириной по четыре пикселя, находящихся в фиксированной позиции X на экране. В каждом кадре значение X остаётся неизменным и меняется только значение масштаба по Y (вертикальное). Это создаёт ощущение, что стены движутся и меняют размер в зависимости от перемещения игрока, но на самом деле мы ничего не рисуем, а лишь меняем 80 значений масштаба.

У любого рейкастера есть 2D-сетка из нулей и единиц: единица означает стену, а ноль — пустое пространство. У игрока есть два значения: позиция и направление взгляда. Для определения высоты каждого из 80 экранных столбцов мы задаём простой вопрос: если испустить луч из глаз игрока в направлении, соответствующем столбцу, то какой ячейки стены он достигнет первой и как далеко она находится? Именно это и делает функция рендеринга: 80 раз за кадр выполняет raymarching и заполняет три массива: значений масштаба, позиций по Y и значений цветовой палитры для каждого столбца. Итак, теперь у нас всё сохранено в массивы значений, но на экран пока ничего не рендерится.

Дальше нам нужно вызвать функцию blit, при помощи которой мы записываем значения масштаба, позиции по X и Y и спрайтовые значения в SCB (Sprite Control Block). Для наглядности я добавил поверх сцены двухмерную мини-карту, воспользовавшись для этого фиксированным слоем Neo Geo. Фиксированный слой предназначен для текста и элементов HUD, например очков и жизней, и он всегда отрисовывается поверх спрайтов. Он гораздо менее гибкий, но идеально подходит для мини-карты. Получившийся результат выглядит так, вполне неплохо:
В каждом кадре мы считываем ввод с джойстика и перемещаем игрока, а для каждого из 80 экранных столбцов двигаем луч через карту в виде сетки, пока он не столкнётся со стеной, после его замеряем расстояние, которое превращается в высоту и цвет. Затем мы передаём эти 80 высот, позиций и цветов в Sprite Control Block, где каждый столбец представляет собой один текстурированный спрайт, ужимающийся до нужной высоты. Эту работу по масштабированию за нас выполняет Neo Geo. Буфер кадров не используется, нет никакого рисования пикселей и, разумеется, вычислений с плавающей запятой.
Здесь стоит отметить пару аспектов. Код далёк от оптимального, я не тратил на него много времени, поэтому он выполняет обновление с частотой примерно 8 кадров в секунду. Я уверен, что производительность можно сильно улучшить. Кроме того, код работал в эмуляторе, но не думаю, что на реальном железе что-то будет отличаться. Проект я выложил на Github, так что при желании можете его изучить.
Итак, благодаря этому простому доказательству концепции я считаю, что можно создать работающий Neo Geo движок в стиле Wolfenstein, использующий вертикальные полосы полной высоты. Но я согласен, что Doom, вероятно, нереализуем на Neo Geo. В этой игре стены не представляют собой полосы одинаковой длины. В Wolfenstein все стены имеют одну высоту и выровнены по сетке, поэтому столбец всегда представляет собой одну вертикальную полосу текстуры с масштабированием. В Doom есть секторы, состоящие из произвольной высоты полов и потолков. Там есть резкие перепады высот, лестницы, платформы, лифты, туннели и окна, через которые можно заглядывать в комнаты, то есть масштабируемыми полосами больше не обойтись, потребуется множество независимо позиционируемых и масштабируемых спрайтов на столбец, усекаемых друг относительно друга.
Кроме того, в Doom есть концепция диагональных стен. В Wolfenstein стены выстроены по сетке, то есть каждая стена направлена ровно на север, юг, восток или запад. В Doom располагаются по любым углом, и один столбец экрана, пересекающий диагональную стену, видит поверхность стены как постоянно варьирующееся расстояние вниз по столбцу. Поэтому на Neo Geo будет очень сложно, если не невозможно, применять попиксельный переменный масштаб и перспективное наложение одного спрайта, что требуется для отрисовки диагональных стен. Поэтому даже одну диагональную стену будет сложно накладывать как полосу с аппаратным масштабированием.

Серьёзную проблему представляют и другие элементы, например, полы и потолки. В рейкастере стены и потолки представляют собой однородно окрашенные области. В Doom полы и потолки текстурированы и отрисовываются в соответствии с перспективой. Кроме того, в Doom есть посекторное освещение на пиксель, реализованное при помощи цветовых карт. В нашем рейкастере мы реализуем полосное затенение столбцов палитрой, что выглядит неплохо. Однако в случае с освещением Doom, которое меняется в зависимости от поверхности и по пикселям пола, Neo Geo может менять палитры для каждого спрайта, но не для каждого пикселя на текстурированной плоскости. И это уже не говоря о спрайтах, монстрах, оружии и всём остальном, что делает Doom уникальной.
Не могу сказать, что это невозможно, потому что когда говоришь так, то бросаешь вызов, и, возможно, кто-то сможет реализовать всё это на CPU с частотой 12 МГц. Но я считаю, что единственный способ запуска Doom на Neo Geo — это использование дополнительного оборудования. Впрочем, разумеется, это лишь моё мнение, сложившееся после написания программы с рейкастингом, выполняемой на Neo Geo с неоптимизированным кодом.
Дополнение: настоящее безумие
Прошла неделя после выпуска моего видео, где я говорил о сложностях реализации игры наподобие Doom на консоли Neo Geo. За это время зрители уже добились любопытного прогресса и написали обновления моей программы.
Моя версия рейкастера была крайне неоптимизированной и работала с частотой примерно 8 кадров в секунду. Пользователь Bluesky под ником Morb, или Meson.ninja, смог оптимизировать мою программу и она стала работать намного быстрее. Похоже, частота кадров удвоилась. Он внёс в кодовую базу небольшие, но важные оптимизации, и теперь программа выглядит намного лучше. Я смог скачать его изменения, скомпилировать их и протестировать на собственной сборке. Как видите, работает она крайне хорошо.
И это замечательно, ведь, на мой взгляд, это ещё больше подтверждает, что игра наподобие Wolfenstein 3D могла бы работать на Neo Geo без дополнительного оборудования. И на этом разработчик не остановился: он начал реализовывать спрайты, монстров, а также спрайты оружия в слое HUD (фиксированном). Медленно, но верно мы приближаемся к игре в 3D-стиле с рейкастингом. И это очень здорово!
Но на этом история не заканчивается. Ещё один разработчик под ником Sabino тоже форкнул мою программу с рейкастингом и создал так называемый Doom Geo AES. Я был обязан собрать его сам, потому что не был абсолютно уверен, что это реальный проект. Вот его описание на GitHub: «Doom Geo AES — это исследовательский прототип Neo Geo AES, аппаратно реализующий геймплей в стиле Doom так, как машине удобно его отрисовывать: полосы спрайтов, заранее подготовленная графика, UI в фиксированном слое и очень малый объём данных среды исполнения».

Посмотрите на скриншот: выглядит неплохо, правда? И мы чётко можем видеть, что 80 полос спрайтовых данных растянуты по вьюпорту. Такая картинка характерна для Neo Geo. Но я был обязан убедиться в этом сам, потому что эти скриншоты казались почти неправдоподобно хорошими. Скриншоты на странице GitHub не похожи на то, чего можно ожидать всего спустя неделю после выхода моего видео. Поэтому в конечном итоге я скомпилировал проект сам. Это очень ранний proof of concept, но он уже выглядит многообещающе.

Разумеется, в нём по-прежнему используется рейкастер, поэтому хоть игра и кажется похожей на Doom, это, скорее, стилизация под неё. Многие возможности, присущие Doom, всё ещё недоступны в этой сборке. Это всё ещё преимущественно просто игра с рейкастингом. Но если начать двигаться по карте, то можно увидеть, как всё это работает. Особенно когда приближаешься к стене, становится заметен слой спрайтов и его масштабирование. К тому же стена слишком сильно масштабируется, что немного снижает иллюзию 3D в сцене. Впрочем, всё это можно исправить.
Кроме того, в игре реализовано оружие, монстры, смена оружия, подбор ключей и других предметов. Очевидно, что всё это находится на ранних стадиях разработки, но уже внушает оптимизм. И, повторюсь, всё это работает на реальном железе и в эмуляции, никакое дополнительное оборудование не требуется.
Разработчик сделал умный ход: Doom Geo AES берёт файлы WAD из Doom и преобразует блоки данных, начальную точку игрока, двери секреты, секторы и всё остальное в сетку Neo Geo, которая может работать на консоли. Эта функция тоже находится на ранних этапах разработки.
На мой взгляд, больше всего разрушают иллюзию настоящего 3D-мира полы и потолки. Как видите, они статичны, то есть при движении игрока пол не движется вместе с ним. Кажется, что вы висите в воздухе, а стены движутся на вас. Об этом я и говорил ранее: реализовать полы и потолки в игре будет сложно.
Но это не невозможно, и наверняка есть способы обхода этого ограничения. Думаю, даже если создать заранее сгенерированные стены и полы, движущиеся к игроку с заранее вычисленными значениями, картинка будет вполне неплохой. Но пока это кажется мне самым большим упущением сборки.
Sabino реализовал часть видов оружия: кулак, пистолет, дробовик, пулемёт, ракетомёт, плазма-пушку и BFG. Все они имеют играбельные пути среды исполнения, если выбранный IWAD предоставляет соответствующую графику PSprite.
Эти два обновления выпустили спустя неделю после публикации моего видео, поэтому многие люди сказали, что я забросил уловку, чтобы проверить, возможно ли это. И я даже не буду этого отрицать: в мире точно есть люди гораздо умнее меня, которые разбираются в Neo Geo и в оптимизации кода. Но меня это очень вдохновило.
