Русский
Русский
English
Статистика
Реклама

Doom

Расшифровка эфира с Джоном Ромеро байки о том, как создавался DOOM

29.08.2020 12:21:18 | Автор: admin

10 августа в наших соцсетях прошел стрим с Джоном Ромеро создателем игр Doom, Quake и Wolfenstein 3D. Это был вечер теплых ламповых историй по заявкам: вы задавали вопросы в комментариях, а Джон рассказывал, как все было.

Вопросы задавал автор телеграм-канала и подкаста Запуск завтра Самат Галимов.

Мы подготовили для вас расшифровку и перевод на русский, но если вы хорошо говорите на английском, рекомендуем посмотреть в записи все же это вечер ламповых историй.

Тебя знают во всём мире как создателя DOOM и Dangerous Dave. Это очень популярные игры, а какую игру ты создал первой?


Наверное, приключенческую игру, когда начал учиться программировать на языке BASIC на мэйнфрейме в университете.


Ты прогал прямо на настоящем мэйнфрейме с доступом по телетайпу?


С доступом через терминал. Компьютер находился в соседней комнате, мне тогда было одиннадцать.

Это была текстовая игра?


Да, текстовая игра, как Zorg или Adventure.

Разве интересно было играть в собственную текстовую игру? Ты же знал все ответы



Это все равно что сказать: всем создателям игр скучно играть в свои собственные игры. Конечно, было здорово научиться и впервые сделать игру.

Кто-нибудь еще играл в неё?


Нет, только я.

Как ты заполучил доступ к этому первому компьютеру?


Я практически заперся там со своими друзьями, у одного из них был доступ к аккаунту друга, который учился в университете. Так что мы использовали учетную запись этого студента, чтобы получить доступ к мэйнфрейму.

Это было законно или не совсем?


Нет, нам там быть не полагалось, но я хотел научиться программировать.

Вас кто-нибудь выгонял оттуда?


Нет, ни разу не выгоняли.

У тебя был свой компьютер?


Нет, компьютер у меня появился только через три года. Значит, на мэйнфрейме я программировал в 1979 году.

Джон, а какую игру ты опубликовал первой?


Она называлась Scout Search, в 1984 году ее напечатали в журнале.

В журнале? Как это?


Тогда, в 80-х, программы публиковались в журналах, и люди сами перепечатывали весь код оттуда. Если они ошибались при наборе текста, игра не запускалась.

Сколько же это строк кода?


Ну, обычно люди старались делать игры небольшими, так что не длиннее пары сотен строк.

Итак, как мы выяснили, первым компьютером, который ты увидел, был этот мэйнфрейм, а какой был первый нормальный компьютер, на котором ты работал?


После мэйнфрейма я ходил в компьютерные магазины, потому что там были компьютеры, и пытался программировать на них, пока меня оттуда не выгоняли. Я так делал ещё три года, пока мы наконец не смогли купить домой компьютер. Так что все эти три года я садился за разные компьютеры, но на всех был BASIC, так что я мог программировать на любом из них.

И сколько удавалось поработать в магазине за один заход? По моему опыту, оттуда довольно быстро выгоняют


Некоторые не возражали, они видели, что я программирую и действительно чему-то учусь. Мне разрешали там сидеть, потому что, когда кто-нибудь заходил в магазин и видел ребенка за компьютером, продажи увеличивались. Создавалось впечатление, будто всё настолько просто.

Не думаю, что если ребенок программирует, это так уж просто. Дети очень упорны. А какая игра была у тебя любимой в детстве?


Безусловно, Pac-Man.

Прямо настоящий, на игровом автомате?


Да.

И ты играл за настоящие монеты?


Да, я потратил три года и несколько тысяч долларов на игровые автоматы. Тогда автоматы были повсюду, мир был полон ими. Их больше нет, даже представить невозможно, что автоматы были везде, куда ни глянь. В Pac-Man играло столько народа, что в Японии и США начался дефицит монет.

Давай переместимся на 15 лет вперед и поговорим о DOOM. У DOOM было три создателя: ты, Джон Кармак и Дэйв Тейлор. Как вы все познакомились?


DOOM мы делали впятером: я, Джон, Том, Адриан и Кевин. Мы познакомились, когда работали вместе в компании SoftDisk в 1990 году.

Вы были там программистами?


Да, я, Джон и Том программировали. Я возглавлял только что созданный отдел разработки игр, и мне нужны были программисты, поэтому я нанял Джона Кармака. Том Холл пришел нам помогать, а я перевел к нам на постоянную основу Адриана Кармака из соседнего кабинета, где сидели художники, чтобы он делал для нас графику. В общем, я посадил нас троих в один кабинет, а Том приходил по вечерам. Днем мы работали в компании, а ночью начали делать свои игры.

Когда же вы спали?


В два часа ночи ложились спать, а в десять утра возвращались на работу. Так что мы работали с 10:00 до 18:00, а затем принимались за создание новой игры. Работали по 16 часов в сутки.

Почему ты этим занимался? Столько времени уходило, ты посвящал этому всю жизнь. Вы просто наслаждались процессом или у вас была цель?


Дело в том, что Джон Кармак придумал способ сделать на ПК плавную прокрутку, как у Nintendo. Такой революционной технологии не было ни у кого. Мы чувствовали, что у нас есть огромный секрет, который разом всё перевернёт. И мы были невероятно воодушевлены, что можем использовать эту технологию для создания собственных игр, а затем выпустить ее в мир в виде ПО.

То есть, вы, по сути, знали, что сидите на мешке с золотом, но не в денежном смысле, а в смысле создания чего-то выдающегося


Да. Меньше чем за три месяца мы сделали три игры, где использовали эту технологию, а потом выпустили их. Это был Commander Keen. Первая трилогия.

Я должен сознаться, я каждый день играю в нее в DOS-боксе


Если у тебя Mac, можно загрузить Commander Genius это замечательная версия всех игр Commander Keen специально для Mac, но внутри нее изначальная DOS игра.

Меня поражает, сколько усилий люди вкладывают в игры. Если бы люди уделяли так много внимания программному обеспечению, мы бы жили в другом мире. Люди гораздо равнодушнее относятся к сайтам или обычному софту. Работает и ладно.

Я знаю, что ты участвовал в работе над демо Super Mario III. Можешь рассказать нам об этом?


Когда мы придумали этот технологический приём, первое, для чего была использована демоверсия, это создание первого уровня Super Mario III, но без использования самого Марио. Мы двигались по миру Марио, а один из моих персонажей, Опасный Дейв, прыгал в этом мире. Это было просто изначальное демо под названием Опасный Дэйв и нарушение авторских прав. Это выглядело потрясающе. Сразу же возникла идея сделать первые два уровня Super Mario III со всеми монстрами, Марио и всем остальным и отправить их в Nintendo. Вот почему мы сделали эту демку чтобы узнать, заинтересована ли Nintendo в издании нашей версии Super Mario III для ПК.

И они, видимо, не были заинтересованы?


Они посчитали, что версия действительно отличная, но решили, что не хотят переносить свою собственность на чужую платформу, поэтому мы сделали Commander Keen.

И эта игра гораздо лучше, на мой взгляд. Говорят, что создание DOOM было риском, прыжком веры. Но действительно ли это так? У вас же уже была эта уникальная технология. Или это было раньше?


Wolfenstein. Мы сделали Wolfenstein в 1992 году, это был наш третий шутер. До этого у нас было ещё два, они помогли нам отработать технологию и понять, что вообще делают в играх от первого лица. К тому времени, как мы сделали Wolfenstein, мы поняли, что использование оружия делает игру интересной. Сразу после этого мы выпустили приквел к Wolfenstein, Spear of Destiny. Мы сделали его довольно быстро, всего за два месяца. А затем начали планировать DOOM. Технически нужно было всё делать заново, с нуля, потому что мы не могли использовать ничего из Wolfenstein, так как планировали написать совершенно другой движок. Еще до того, как начать работу на DOOM, мы знали, чего сможем достичь в игре, и были очень воодушевлены этим знанием. Мы не думали, что рискуем, потому что знали, какие проблемы надо решить, и знали, что дадим себе столько времени, сколько понадобится на их решение. Получается, создание Wolfenstein от идеи до выпуска заняло четыре месяца, и нас было четверо. После Wolfenstein мы наняли еще двоих. Всего получилось шесть человек, один из которых был бизнес-менеджером и не участвовал в разработке, так что нас было пятеро.

Когда мы впятером работали над DOOM, то знали, что можем сделать эту игру. Мы знали, что в нашем видении игры нет ничего, что было бы нам не под силу. Мы только начали работать над игрой и сталкивались со всякими проблемами со скоростью или просто с творческими трудностями. Например, до DOOM во всех играх с лабиринтами стены были под углом 90 градусов. Начиная с самых первых игр на мэйнфреймах в 70-х. Ни в одной игре не было других лабиринтов.

Это из-за того, что нарисовать кривую линию сложнее?


Да, с технической точки зрения, вам нужно больше процессорной мощности для обработки отрезков, конечные точки которых не зафиксированы на определённой грани. Существует множество оптимизаций, которые возможны, если у вас прямые стены и повороты на 90 градусов. Но если они поворачивают и встречаются в произвольных точках, вам придется решить множество задач, чтобы сделать области выпуклыми, какой-нибудь причудливой формы с множеством изгибов и т.д. Джон Кармак был уверен, что сможет это сделать. Он не беспокоился, сможет, или нет. Так что мы задумали игру, подобной которой еще никто не видел. DOOM была первой игрой, стены в которой не были коридорами с поворотами на 90 градусов.

Даже Wolfenstein был просто быстрой версией Даже в нём, хотя там и можно плавно перемещаться, сам мир состоял из прямых углов. Не считая Wolfenstein, очень немногие игры позволяли двигаться плавно. В основном в играх с прямоугольными лабиринтами игроки двигались блок за блоком. Мы изменили всё, позволив пользователю свободно двигаться в любом направлении, но сам мир был по-прежнему ограничен, а с DOOM мы практически убрали границы мира. Мы сняли эти ограничения, чтобы создать невиданные раньше уровни Вот ещё одна причина, по которой люди были в таком восторге. Они никогда не видели мира, в котором не было бы прямоугольных лабиринтов.

Это очень серьезный прогресс в технологии, он потребовал больших вложений


Ты говорил, что эта задача заняла несколько месяцев. Было ли какое-то давление относительно времени выпуска игры деньги кончаются, надо выпустить игру через неделю, а то вылетим из бизнеса? То есть, это был скорее бриллиант, созданный благодаря давлению, или у вас была возможность выложиться на максимум и сделать игру без спешки?

У нас было практически сколько угодно времени. С того момента, как мы выпустили Commander Keen в декабре 1990 года, у нас не было проблем с деньгами. За Commander Keen в первый месяц мы заработали наши первые 10 500 долларов. Через год мы зарабатывали уже 50 000 долларов в месяц, и нас по-прежнему было всего четверо. Понятно, что мы тратили не всё. Мы просто продолжали вкладывать деньги в компанию, но наш банковский счет продолжал расти. Благодаря этому у нас было много времени для создания игр, нам никогда не приходилось ускорять разработку. На разработку Wolfenstein мы потратили четыре месяца, а на создание DOOM ушёл целый год. Когда вышел Wolfenstein, денег стало больше как минимум в пять раз.

Люди, не испытывающие внешнего давления, обычно рассказывают о проблеме расползания проекта

.
Когда вы всё добавляете и добавляете функции и постоянно переносите релиз, потому что хотите добавить ещё, и ещё, и ещё. Как вы остановились?

Мы знали, что именно пытаемся сделать. Скажем, с Wolfenstein мы действительно добавили в игру больше, а затем, поиграв в нее, быстро убрали добавленные нами функции, потому что они мешали самой сути игры, а суть была динамичная игра, в которой надо во всё палить. Если приходится останавливаться в коридоре, чтобы обыскать тело, вы останавливаете сущность игры. Игра была весьма скоростной, и это стало сутью. Суть заключалась в том, чтобы стрелять во всё подряд, получать ключи, открывать двери, продолжать во всё стрелять, собирать всё, что видишь, но никогда не останавливать игрока, за исключением момента, когда он добрался до двери, и ему надо ее открыть. Вот, в принципе, и всё останавливаешься только если наткнулся на дверь. Чем больше мы добавляли в игру того, что могло бы замедлить игрока, тем больше мы понимали, что игра не об этом: не о том, чтобы быть незаметным, не о том, чтобы шарить по чужим карманам или таскать трупы. Мы снова всё убрали, потому что суть игры не в этом. Не в том, чтобы перекладывать тела и со всем этим возиться. Она в том, чтобы нестись на всех парах через этот замок со скоростью 70 кадров в секунду и, по сути, мочить всех направо и налево. Мы убрали из игры всё, что этому не соответствовало. С DOOM было то же самое. Пока мы делали DOOM, у нас было много элементов, которые мы потом убрали. В Wolfenstein надо было подбирать предметы, за которые давали очки. Потом мы начали играть в DOOM, и поняли, что это не аркадная игра, не должно быть очков, нас не волнует счет. Нас волнует Твоя задача в том, чтобы убить всех демонов, выбраться с уровня, чтобы перейти на следующий, уничтожить всех демонов там и спасти оставшихся людей. Смысл не в том, чтобы набрать побольше очков. Больше того, DOOM не наказывает игрока за смерть. То есть, тебе не говорят, что у тебя три жизни: их столько, сколько хочешь. Если умираешь, то оказываешься в начале уровня или загружаешься с сохраненной игры и пробуешь снова. Ты не теряешь пройденные уровни, у тебя нет количества жизней, как в аркаде. Так что мы убрали жизни, убрали счёт и все предметы, дававшие очки.

Получается, что не было строгого плана. Всё постоянно пересматривалось. Вы что-то добавляете, потом понимаете, что это не работает, потом


Мы это убираем, да. Эти штуки долго были в игре, пока мы наконец не решили, что суть игры не в них, давайте их выкинем, они только все запутывают. Если у вас есть подсчёт очков, то игроки сразу думают: О, очки! Они решают, что очки это важно. Но мы не собирались ничего с ними делать, потому что главное в игре вторжение демонов. Цель не в том, чтобы собирать дьявольские библии и получать за них очки. Так что мы убрали всё, что не было сущностью игры.

Вы проверяли все это на ком-то или принимали решения внутри команды?


Внутри команды. У нас не было отдела обеспечения качества. Все эти годы мы сами занимались отладкой.

То есть вы показали её кому-то ещё только перед самым выходом?


У нас всё-таки было несколько тестировщиков со стороны, так как не было своих. Мы посылали игру им на проверку. Эти люди сотрудничали с нами много лет. Потом они рассказывали нам об обнаруженных проблемах. Если что-то всплывало.

Их замечания касались только найденных проблем, а не идей, да?


Чего угодно. Они делились с нами всеми своими соображениями. Обычно они находили ошибки, которые мы уже обнаружили и исправили. Круто, если им удавалось заметить баг, который мы пропустили. Это случалось редко, потому что мы играли постоянно, тысячи раз, так что мы видели всё.

Правда, что для создания текстур вы сканировали сапоги из змеиной кожи и содранные коленки? Что там за история с этим?


Да! В DOOM много чего отсканировано и оружие, и Думгай, и Кибердемон, и Барон ада. Все это отсканированные 3d-модели, которые мы создали

Постой-ка, 3d-модели? В девяностые? Ты о чем?


Да, мы использовали видеокамеру из 90-х с видеокассетами. Мы подключали камеру к компьютеру и направляли ее на стол, на который ставили монстра. Потом фотографировали фигурку и поворачивали ее разными сторонами к камере. Мы поворачивали ее восемь раз и каждый раз делали снимок экрана. Когда картинка попадала на компьютер, мы подчищали пиксели комнаты на фоне, а потом раскрашивали оставшиеся пиксели, потому что монстр был похож на монстра, но получался недостаточно ярким. Потом художник просто кликал на картинки и получал монстра во всех ракурсах.

Если монстр движется, мы используем картинку с другого ракурса. Если игрок стоит за монстром, мы видим его сзади или сбоку с определенного ракурса, потому что это была не настоящая 3d-модель, а двухмерный спрайт. Так что когда монстр движется, нам нужно передвинуть его ногу, затем повернуть ее восемь раз и сфотографировать с восьми ракурсов для каждого кадра будущей анимации. Так что художники проделали огромную работу, чтобы сканировать всех монстров со всех ракурсов, это был непосильный труд для двух человек. Один из художников занимался в основном персонажами, а второй делал текстуры для игры.

Все это делалось внутри компании? Все эти ребята сидели там?


Их было только двое, два художника.

Правда, что после выхода DOOM люди увольнялись с работы, бросали университеты, школы, колледжи и целыми днями играли? Это правда?


О да, это был невероятный хит.

Ты не чувствуешь себя виноватым?


Нет, мы ожидали, что DOOM станет причиной грандиозного падения продуктивности, мы так и написали в пресс-релизе.

Вы сами играли после релиза?


Да, постоянно играли.

Но вы ведь сотни раз играли в процессе разработки


Но в многопользовательской игре был смертельный бой, это было страшно весело, так что мы все время играли в этом режиме.

Значит, игре нет конца


Угу.

Вы сидели в одной комнате или использовали локальную сеть побольше?


У нас в компании была своя сеть, каждый был к ней подключен, так что все могли играть. Это была проводная сеть, поэтому скорость была отличной. Если я хотел поиграть с Шоном [Грином], который сидел за стенкой, в соседнем кабинете, то он просто заходил в игру, мы подключались и звонили друг другу по телефону, чтобы удостовериться, что оба подключены.

По проводному телефону, да?


Ага, по проводному телефону.

Кто играл лучше всех? У вас в компании были чемпионы?


Да, я. У нас было не так много людей, наша компания была очень маленькой. Играли в основном я, Американ [Макги] и Шон [Грин]. Остальные почти не играли. Они играли, только чтобы убедиться, что их текстуры работают правильно, дизайн работает, но смертельный бой их не интересовал. Играло только три человека, потому что всего в компании нас было восемь, включая секретаря девять, включая Американа.

Расскажи о смертельном бое, потому что на твоей вики-странице написано, что ты отец этого слова. Как это случилось?


Как только нам удалось запустить многопользовательский режим, где можно было видеть другого персонажа на экране и стрелять в него, мы сразу поняли: именно этим мы и будем заниматься. Мы уже не просто стреляли по монстрам, мы могли убивать других игроков ракетами настоящее веселье. Ты будто бьешься на ринге, ты не можешь покинуть уровень, но смертельная схватка звучит гораздо круче. Это бой не на жизнь, а на смерть. Это смертельный бой!

Итак, представим, что сегодня 10 декабря 1993 года. Игра только что вышла. Что ты чувствуешь?


Мы страшно устали, мы работали 30 часов, начали накануне, а загрузили игру в два часа дня. Мы просто Некоторые уже спали, лежа на полу у них кончились все силы. Джей, наш генеральный директор, смог выспаться, потому что он не занимался программированием. Так что он чувствовал себя хорошо и был готов разобраться с загрузкой игры. В два часа дня ее загрузили на ftp-сайт Висконсинского университета и на BBS с программным обеспечением в Массачусетсе, чтобы люди, использующие BBS и модемы, могли загрузить игру оттуда, а пользователи интернета могли скачать ее с университетского сайта. Как только игру выложили, мы все пошли домой спать и проспали до следующего дня. Мы невероятно устали. Но мы знали, что игру ждет успех.

Джон, единственное, чего я не могу понять у вас было полно времени, достаточно денег, вы не были ограничены по срокам, но все равно программировали по ночам. А после релиза высыпались Почему?


Потому что мы почти закончили. Мы практически доделали игру, а когда такой проект подходит к концу, все работают на износ, чтобы поскорее всё завершить. У нас не было даты релиза, но мы знали, что этот день не за горами. Так что мы просто хотели сделать как можно больше, потому что приближалось Рождество, а перед Рождеством можно хорошо заработать, потому что DOOM могут купить в подарок. Чем больше дней остается до Рождества, тем лучше.

Всё-таки кое-что ты в финансах понимал


Ага. Кстати, Quake мы выпустили в июне, как только доделали. Это произошло летом, а лето самое плохое время для продажи игры.

Одна из причин огромной популярности DOOM возможность создавать моды


Вам не было страшно? Вы не боялись, что люди создадут что-нибудь безумное?

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

Нужно будет обязательно заценить.


Да, заходите на doomworld.com.

Кстати, перед этим интервью мы связались с самым популярным геймерским сайтом в российском интернете


Он называется Канобу, они подготовили список вопросов путем голосования

Итак, первый вопрос: тебе правда нравится DOOM 2020 и DOOM 2016?


Да, я играл в обе игры, они отличные, просто супер. Я очень рад, что DOOM осовременили, потому что нельзя просто взять и воссоздать оригинал. Они сделали именно то, что нужно было сделать для современной аудитории.

Где стримы, Джон? :)


В какой-то момент я ими займусь, но сейчас у меня слишком много дел. Я люблю стримить, последний раз я это делал, когда играл в DOOM Eternal. Часто я говорю с другом по гарнитуре, когда мы играем, так что я взаимодействую с людьми, мое внимание поглощено именно этим. Во время стрима приходится развлекать народ, не хочу на это отвлекаться.

А ты когда-нибудь пробовал просто сосредоточиться на игре и посмотреть, что из этого выйдет?


Наверное, стоит попробовать [смеется].

Ты планируешь какие-нибудь неофициальные дополнения для DOOM 2 или, может, для SIGIL?


Да, SIGIL это эпизод, который я сделал на 25-летнюю годовщину DOOM. Это пятый эпизод DOOM. Круто, что софту он понравился и его официально добавили в DOOM. Так что если у вас есть DOOM на Switch, на айфоне или на приставке, то у вас есть и SIGIL. SIGIL один из редких эпизодов для DOOM I. Обычно дополнения и новые уровни разрабатывают для DOOM II, но в честь юбилея я решил выпустить эпизод для оригинальной версии.

Ещё какие-нибудь дополнения планируешь?


Да! Не могу сказать, что именно я делаю, но планирую кое-что сделать.

Ты сказал, что играешь, но не стримишь игры с друзьями. Речь идет об играх, которые разработал не ты? Расскажешь, во что ты любишь играть?


В основном в Ghost Recon Breakpoint, иногда в Wildlands.

Сколько часов в неделю ты играешь?


Играю где-то раз в неделю по 6 часов вместе с другом. Начинаем где-то в 9 вечера, а заканчиваем в 3 часа ночи или позже.

Будет ли переиздание Daikatana? Текущая версия очень далека от того, что ты планировал изначально


Я не собираюсь перевыпускать Daikatana. Проблема в том, что эту игру практически уничтожили во время релиза еще в 2000 году. Так что 20 лет назад люди просто Столько статей написано о том, насколько она плоха. У нее и в самом деле много недостатков. У этой игры такая плохая репутация, что мне незачем тратить время на ее перевыпуск.

Насколько для тебя важно то, нравится ли твоя игра людям или не очень?


Сколько радости испытываешь от того, что делаешь игру для себя, а сколько от того, что люди в восторге от твоей игры?

В идеале ты делаешь игру для себя и людям она нравится. Если ты вкладываешь много времени и сил в игру, которую любишь, тогда и людям она придется по душе. Когда я делал уровни для SIGIL, мне они очень нравились. Я потратил на них кучу времени, и, когда они вышли, многие были ими довольны.

Ты когда-нибудь начинал проект, зная, что людям он не понравится, но все равно хотел завершить его?


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

Так, давай закончим с вопросами от Канобу. Это очень сложный вопрос [оба смеются].

Скоро выйдет Cyberpunk 2077. В ней показана модель будущего, которое ждет человечество



Что ты, Джон Ромеро, думаешь о будущем игровой индустрии через 30-50 лет?

Ммм, господи Ну, думаю, что-то должно произойти с приставками, потому что телефоны становятся все мощнее, в какой-то момент они заменят приставки. Думаю, что концепция Switch когда у тебя есть портативный девайс, который проецирует игру на большой экран показывает нам, как будут выглядеть игры для тех, кто предпочитает сидеть перед большим экраном с телефоном в качестве контроллера. Возможно, к телефону нужно будет что-то подключить, чтобы можно было быстрее нажимать кнопки. Sony и Microsoft сделают что-то в этом духе. Потому что телефоны настолько мощны, что, возможно, они выпустят телефон, который захочет купить каждый, и он заменит приставку. Ведь телефоны настолько мощные, что могут транслировать сигнал на экран телевизора с помощью беспроводной сети. Так что же делать, если все сидят в телефонах? Мобильные устройства это другой сегмент рынка, новая техника позволяет играть в крутые игры, в которые не поиграешь на телефоне, только потому, что экран слишком маленький. Так что, на мой взгляд, это совсем разные вещи сидеть на диване с контроллером в руках, глядя на большой экран, скажем, в 4K, и сидеть с телефоном, играя во что-то попроще.

Ты играешь в игры с Apple Arcade?


Я подписался на этот сервис, так что кое в какие играю.

Что посоветуешь?


Grindstone, наверное, я в нее еще не играл, но я знаю, что она хороша.

Круто, спасибо, и второй короткий вопрос. У тебя есть Switch?


Да, обожаю Switch. Раньше я много играл в The Legend of Zelda. Мне страшно понравилась Breath of the Wild. Играл в Animal Crossing: New Horizons. Но сейчас у меня мало времени на игры.

Над какими проектами ты сейчас работаешь?


Прямо сейчас работаю над игрой Empire of Sin. Мы разрабатываем ее уже больше трех лет. Она должна выйти в этом году. Можете просто зайти на empireofsingame.com, посмотреть видео и вы увидите, что это стратегия о мафии 1920-х годов.

Ты все еще работаешь по ночам?


Да, конечно [смеется].

Стоит ли ждать от тебя шутер?


Надеюсь!



Если вам не хватило общения с Джоном, можно посмотреть наше предыдущее интервью с Ромеро Упрости и вырежь необходимое, где вопросы задавал управляющий партнер



Что было ранее


  1. Сания Галимова, маркетолог RUVDS как жить и работать с психиатрическим диагнозом. . .
  2. .
  3. .

Подробнее..

Сыграем в DOOM на серверах

06.10.2020 20:18:31 | Автор: admin


У меня возникла безумная идея это сыграть в классический DOOM по сети установленный на VPS под управлением Windows. В целом, это задумывалось как тестирование новых фич, которые мы внедряем: клонирование серверов и разворачивание локальной сети. Да, и на самом деле, это круто сыграть в DOOM прямо на серверах, особенно после просмотра баек от Джона Ромеро было невозможно не поиграть в легенду компьютерных игр.
Давайте посмотрим, что же из этого получилось, с какими проблемами столкнулся и как их решили.

В чём фишка?


У нас появились две интересные возможности: разворачивание локальной сети и создание снапшотов. Если с локальной сетью всё более-менее понятно это прекрасная альтернатива тому же VPN и разворачивается она сильно проще, то вот что такое снапшот не все понимают. Да, и буду честен, я тоже не сразу понял. Снапшот позволяет сделать текущий образ сервера и потом на его базе создать другой сервер, даже с другими параметрами, таких как размер диска, памяти, количество ядер. Это очень удобно, особенно если вам необходимо ваш VPS-север перенести из одного дата-центра в другой. Другая возможность, которую мы будем использовать, это однократная настройка рабочего сервера, и потом клонирование его для ваших клиентов.
Понимаю, что всё это выглядит слишком рекламно, но изначально это был боевой тест наших внедрённых фишек. И благодаря ему мы смогли оценить все возникшие проблемы и оперативно их исправить. Поэтому и родилась данная статья.

Разворачиваем мастерсервер


Для этого проекта выбрал сервер со следующими параметрами.



Думаю, для наших задач такой конфигурации более чем достаточно. Изначально хотел использовать Windows Server 2003, но Doom наотрез отказался на ней работать, поэтому всё же решил остановиться на 2016.
Спустя несколько минут, после разворачивания сервера и загрузки операционной системы, мы увидим готовую панель, а главное IP адрес нашего сервера.



В качестве сетевого DOOM я использовал какую-то хитрую сборку Zandronum, DoomSeeker, BrutalDoom, которую мне дали друзья. Но, по сути, вам нужен будет один Zandronum и DoomSeeker для создания сервера, плюс оригинальный .wad-файл, если хотите поиграть на тех самых картах. Ну или найти подобную сборку на просторах сети. Сразу признаюсь, я не большой знаток модов DOOM, попробовал несколько сборок, поэтому показываю работающий вариант. Возможно, если вы захотите это повторить, у вас должно получиться и с другими сборками. Тот же BrutalDoom намного более весёлый и резвый для игры по сети.
Итак, копируем эту сборку на наш сервер (я рекомендую использовать папку c:\game\doom), запускаем zandronum.exe.



И выбираем папку, содержащую WAD-файлы, на которых будем играть. Именно они и определят те карты, на которых вы будете играть. У меня они лежат в папке files вместе с игрой.





Всё готово, чтобы играть. Теперь сделаем копии нашего сервера и добавим их в единую локальную сеть, чтобы смогли поиграть и другие игроки по сети.

Создание копии серверов и объединение в единую локальную сеть


Для того, чтобы сделать копию сервера, нужно выключить сервер, зайти во вкладку Снапшоты.



И выбрать Сделать снапшот. Если сервер не выключен, то его предложат выключить в интерактивном режиме, так как снимок сервера для переноса осуществляется в выключенном состоянии (фактически копия диска).



В результате будет создан снапшот нашего сервера.



Основной сервер после этого можно запустить, а на базе данного снапшота мы можем создать новые сервера. Для этого идём во вкладку Заказать



В разделе, где выбирается Шаблон сервера, в самом низу будет наш Snapshot, выбираем его.



Обратите внимание, что можно выбрать сервер с другими параметрами. Это очень удобно, если вам необходимо увеличить объём жёсткого диска, ОЗУ и прочего. Разве, что нет возможности сохранить IP-адрес.
В результате, после описанных операций, через некоторое время мы получаем копию нашего сервера. Копия от оригинала отличается только паролём входа, и другими IP и MAC-адресами.



После создания второго сервера, давайте объединим их единую локальную сеть Для это переходим во вкладку Сеть любого сервера.



Там выбираем Настроить локальные сети.



Далее можно дать название нашей локальной сети, например Doom, выбираем диапазоны подсети; после чего можно добавляем сервера, которые мы хотим подключить в эту локальную сеть. После нажимаем Создать локальную сеть.



И окончательно сеть разворачивается после нажатия на кнопку Развернуть локальную сеть.



Вот тут кроется одна проблема: Windows очень любит обновления, не даёт перезагрузить сервер в момент установки изменений. Так Windows обновляется часто, то достаточно легко можно попасть на момент обновления в момент создания сети. Во время подготовки материала для статьи я так попал дважды. Для создания сети требуется перезагрузка сервера, и если перезагрузить не удалось, то сеть создаться с ошибкой.


Установка проклятых обновлений в момент создания сети.

Во избежание таких проблем при создании сети, самое правильное данные процедуры проводить на выключенных серверах, ибо всё равно потребуется перезагрузка для разворачивания сети. А у выключенного сервера внезапно обновления ставиться не будет.
После всех описанных процедур у нас будет развёрнута локальная сеть и в панели управления серверами будет такая картина.


Сервера в единой сети.

После воспоминаний о проблемах и тонкостях разворачивании OpenVPN, этот вариант создания локальной сети просто чудо-чудное! Лично для меня он оказался наиболее удобным.
Как вы понимаете, таким же точно способом мы создаём ещё несколько серверов, на которых уже смогут играть мои коллеги.

Создаём игровой сервер и играем.


Всё, теперь нам осталось только запустить сервер DOOM и играть. На мастер-сервере запускаем doomseeker.exe.



Выбираем Create Server.



После чего выбираем для какого движка создаём сервер, какой wad-файл использовать и карту.



И жмём Start server.



После этого на главной машине и на клиентских запускаем zandronum.exe и идём в Multiplayer. Browse servers->internal browse, там выбрать local. Это меню не всегда срабатывает, поэтому в него надо заходить несколько раз.



Не забываем выбрать наш сервер. Жмём join game и мы в игре!





Итог



Коллеги играют в DOOM на рабочем месте.

Наш коллектив достаточно классно порубился в игру, и теперь у нас есть удалённые рабочие машины с запущенной игрой. Их можно передавать другим коллегам или играть из дома. Это, кстати актуально, для пошаговых стратегий, когда можно таким образом сохранять прогресс.
Но главное, что удалось в игровой форме протестировать внедрение новых фич. Все остались довольны.



Подробнее..

Запускаем DOOM на калькуляторе HP Prime G2

26.02.2021 16:11:32 | Автор: admin

Установить DOOM на какое либо устройство, это как водрузить знамя победителя на павшей крепости. Мне задали вопрос ну что, doom запустил? не менее 35 раз, когда узнали что я вожусь с данным калькулятором. Решил не разочаровывать публику и добиться запуска DOOM. Попутно, это стало неплохим тестом работоспособности оборудования, а также выявления неприятных багов. В общем, поехали!

Новости по проекту


Тем, кому интересно как же я запустил DOOM, могут пропустить эту главу и перейти сразу к следующей. Тут просто представлен текущий статус проекта.
Как вы помните в прошлых частях (часть 1 и часть 2), я занимался тем что ставил Linux на калькулятор, пересобирал u-boot, kernel, rootfs. С тех пор достаточно плотно занимался калькулятором и даже основательно разобрался с тем, что же было сделано в u-boot, kernel и device tree. Надо понимать, что это моё хобби, в свободное от основной работы и семьи время, поэтому не всё идёт быстро, и порой несколько алогично, просто потому что сегодня есть настроение делать так, а не иначе.
Главная новость состоялась, благодаря пользователю Alx2000y, который пригласил меня в чатик в телеге, где на аналогичном процессоре народ пилит свою прошивку для Xiaomi Gateway. Даже есть статья на хабре по теме. Народ уже сильно продвинулся в данной теме, невероятно расширив функционал устройства. И мне очень сильно помогли победить проблему nand. Как вы помните, в самом начале я свой образ nand затёр по глупости. В результате, у меня получилось достаточно большое количество виртуальных битых секторов, самое неприятное что битые сектора находились в самом начале и не давали записать туда u-boot. Ниже привожу список битых секторов, большинство из них виртуальные.

=> nand badDevice 0 bad blocks:  00000000  00020000  00040000  00060000  012c0000  04e20000  05280000  094c0000  17b20000  1ff80000  1ffa0000  1ffc0000  1ffe0000=> 

Ленар, из вышеупомянутого чатика, очень сильно мне помог, проблема решилась буквально двумя командами в u-boot:

nand erase.chipnand scrub.chipReally scrub this NAND flash? <y/N>y

После чего, проверяем количество битых секторов, и, о чудо, их стало значительно меньше!

=> nand badDevice 0 bad blocks:  1ff80000  1ffa0000  1ffc0000  1ffe0000

В результате, я теперь могу загрузить u-boot в нулевой сектор и произвести загрузку. На данный момент, калькулятор может быть загружен просто подав питание и будет полностью загружен linux, с работающим дисплеем и возможностью запуска программ по UART. Там даже корректно работает DOOM. Но, есть нюанс (С). Видимо драйвер клавиатуры как-то пересекается с драйвером ubifs, и в результате, если нажать любую клавишу на клавиатуре, то происходит мгновенное зависание калькулятора. Мне разок даже прилетел kernel panic, но я не сообразил его сохранить, чтобы хотя бы найти место этого пересечения. Так что на данный момент, всё однозначно работает в initramfs. Видео с демонстрацией работы загрузки nand, запуска DOOM и зависания постил в своём телеграмм канале.

Из других хороших новостей, попробовал поставить ubuntu на nand, тоже корректно работает. Пакеты, конечно, ставить нельзя, но в целом можно работать и использовать её, что тоже удобно. Но без работающий клавиатуры, эти игры пока лишены практического смысла.
В последней части я жаловался, что u-boot имеет разное поведение, при работе на nand и из ОЗУ. Я потратил два дня, ковыряния в исходных кодах u-boot, чтобы понять в чём же дело. А всё оказалось банально (даже стыдно). Утилита uuu, при запуске u-boot из памяти, передаёт туда свои переменные окружения. А точнее вызывает mfgtool_args и в результате строка переменной окружения загрузки выглядит таким образом:

bootargs=rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber= mtdparts=gpmi-nand:4m(boot),8m(kernel),1m(dtb),1m(misc),-(rootfs) clk_ignore_unused

Разумеется, если загрузиться с nand, то с такими параметрами ubifs в четвёртом разделе виден не будет. Поэтому после загрузки u-boot в ОЗУ, я принудительно задаю ему следующие переменные окружения:

setenv bootargs  console=ttymxc0,115200 ubi.mtd=4 root=ubi0:rootfs rootfstype=ubifs mtdparts=gpmi-nand:4m(boot),8m(kernel),1m(dtb),1m(misc),-(rootfs)

И всё отлично работает.
Поясню, зачем это нужно: если прошить загрузчик в нулевой сектор, пропадает возможность работы через mfgtool (утилита uuu). А на данном этапе, состоящем из разработки и отладки это основной инструмент. Поэтому проще оставить возможность работы утилиты uuu, и загружать каждый раз u-boot вручную.

Запуск DOOM


Переходим к самой интересной части к запуску DOOM на калькуляторе. Как вы понимаете, я не зря вначале расписал обо всех проблемах. Можно запустить DOOM при загрузке на NAND-флеш, там можно поставить карты всех видов, все возможные версии DOOM и вообще всего что душа пожелает. Но при запуске в ОЗУ, мы ограничены размером образа rootfs примерно в 15 МБ (практика показала, что 16 ещё прокатывает). В связи с этим, пришлось подбирать версию DOOM и делать правильную сборку, а также научиться с ней работать.
Оказалось, что всё хорошее давно придумано за нас, и DOOM можно собрать прямо в buildroot не вставая с дивана. Это я узнал, когда гуглил все возможные варианты DOOM для встраиваемых систем и пытался их собрать. Как оказалось, достаточно запустить:

make menuconfig

И выбрать DOOM. Это делается в "Target packages ---> Games --->"


В нашем распоряжении две версии DOOM: chocolate-doom и prboom. После нескольких экспериментов, я понял что chocolate-doom ну никак не хочет влезать в initramfs. Разве, если вообще убрать wad-файлы. Пытался найти обрезанные wad-файлы, которые бы влезали вместе с шоколадным думом. Но она с ними на отрез отказалась работать. В результате, я попробовал шоколадную версию установить на nand (вместе с prboom), и пробовал там. Подбирал параметры и т.д. Результатом экспериментом стала следующий способ запуска:

export SDL_NOMOUSE=1 chocolate-doom -geometry 320x240 -bpp 24 -nomouse

Итог меня сильно разочаровал: эта версия doom некорректно (или может наоборот корректно) растягивает экран, оставляя широкие полосы по краям экрана, что мне очень не понравилось.


Шоколадная версия DOOM. Видна чёрная полоса снизу.

При запуске, мне шоколадный дум говорит о том, что делает изменение размера окна:

I_InitGraphics: 320x240 mode not supported on this machine.I_InitGraphics: Auto-adjusted to 320x200x32bpp.

Поэтому, я остановился на prboom. Сделал образ вместе с шароварными WAD-файлами и самим prboom, всё лишнее убрал. Но, всё равно очень долго не мог заставить его работать. Читал всевозможные мануалы, искал как сконфигурировать, чтобы всё корректно работало. Изображение выводит, на кнопки реагирует, но экран коряво растягивает и выводит кривые цвета. Пока на каком-то форуме не нашёл идеальные параметры запуска.
В общем, для нашего калькулятора запуск prboom такой: отключаем мышку, и далее запускаем prboom со следующими параметрами:

export SDL_NOMOUSE=1 /usr/games/prboom -width 320 -height 240 -nosound -vidmode 32bit

Ключевой параметр здесь: "-vidmode 32bit".


Долго искал подходящие параметры, и только с этим всё завелось. Для удобства всё записал в скрипт d.sh. Наконец всё работает, можно даже играть!


Специально для вас, я подготовил обновлённую сборку flash_utility с DOOM, который вы можете запустить на своём калькуляторе даже без перепрошивки, и показать друзьям, мол вот, DOOM у меня в калькуляторе работает. Достаточно разобрать калькулятор, замкнуть контакты, описанные в первой части и запустить

sudo uuu doom.uu

В конце всех действий, вы получите калькулятор, с linux и DOOM. Чтобы запустить DOOM, надо будет залогиниться и на калькуляторе выполнить:

./d.sh

Резюмируя


DOOM работает! Можно ли в него играть? Ну локально, загружая с компьютера можно. Это выглядит круто и красиво, но на деле, не совсем то что хочется получить. В действительности будет круто, когда ты едешь в метро, взять и достать из широких штанин калькулятор, включить его (на данный момент режим энергосбережения не работает), и запустить DOOM. Вот это реально круто, играть в метро на калькуляторе в DOOM, Duke Nukem 3D, Quake I, II, III и т.д. Но факт остаётся фактом DOOM на этой железке запущен. Но ещё очень много работы.
В целом, не хватает хотя бы небольшого сообщества вокруг этого калькулятора (хотя бы больше меня одного), чтобы были тестировщики проблем, было с кем поговорить и поделиться, услышать совет. Первоначальный автор явно остыл к данному проекту, хотя и проделал титаническую работу. Я его хорошо понимаю, и никак не могу укорять за то, что он не хочет помогать даже советом по данному проекту. Ну так, небольшие рекомендации давал, но ему явно уже не до него. Поэтому если у вас есть идеи, калькулятор, желание помочь, хотя бы советом, пишите тут или в телегу, буду рад!

P.S. Зачем я этим занимаюсь?


Очень часто спрашивают меня нафига? Умом понимаю, что на данный вопрос отвечать глупо, но тем не менее отвечу.
Зачем художник рисует картину или автор пишет книгу? Будем честны, 90% книг, картин да и других произведений могут вообще не увидеть свет, а из тех кто увидят, доли процента станут известными и обретут широкий круг читателей. Проще говоря, большинство творцов делают бесполезный труд. Более того, множество произведений даже никогда не находят своего читателя, но что же им этого не делать? Что движет этими людьми? Всё достаточно банально. Ими движет простое чувство:


Проще говоря, этим занимаешься, потому что это круто и это прёт. И, как это ни странно, в будущем приносит большую пользу, хоть и не такую явную как кажется.

Файлы для скачивания




Подробнее..

Перевод Клон Doom в 13 килобайтах JavaScript

17.06.2020 08:10:07 | Автор: admin
В прошлом году я участвовал в соревнованиях JS13K 2019, на которых людям предлагается разрабатывать игры в менее чем 13 КБ кода на JavaScript. Я участвовал с клоном Doom, который назвал Ещё один клон Doom (Yet Another Doom Clone).


Поиграть в него можно здесь. Исходный код выложен сюда.

Зачем создавать клон Doom?


Зачем писать FPS на JavaScript всего в 13 КБ (с учётом сжатия)? По нескольким причинам. Но лучше всего на этот вопрос отвечает раздел FAQ соревнований JS13K Можно ли использовать WebGL?:

Да, но может быть сложно уместить его в 13 килобайта, если вы планируете писать FPS.

Кроме того, в то время я как раз написал 3D-рендерер и хотел поработать над ним ещё. К тому же мне нравится создавать сильно сжатый код. (Например, много лет назад я создал язык и написал компилятор для нового языка, предназначенный специально для использования в код-гольфинге.)

Именно поэтому я выбрал FPS. Остаётся вопрос: Почему Doom? На него ответить проще: если вы хотите написать FPS, и чтобы он при этом был небольшим, то Doom практически самый минималистичный вариант.

Причина, по которой Doom настолько прост (по современным стандартам) понятна: Doom должен был работать на железе на пять порядков более медленном, чем сегодня. За ту же цену сейчас можно собрать машину, которая способна выполнять в сотню тысяч раз больше работы, чем Pentium 1994 года. Поэтому я решил, что будет интересно попробовать воссоздать нечто наподобие Doom, но вместо ограничений производительности использовать ограничения объёма кода.


Фундамент: 3D-рендерер на JavaScript


Движок игры я начал строить на основе написанного ранее 3D-рендерера, который я хотел реализовать как можно более простым. Оказалось, что благодаря своей простоте он к тому же довольно мал: ядро движка 3D-рендеринга заняло всего примерно 5 КБ сжатого JavaScript, то есть на игру осталось всего 8 КБ. Я решил, что ситуация не так уж и плоха. Если я не будут использовать большие спрайты или файлы объектов, то всё должно получиться.

Движение игрока



Для перехода от 3D-рендерера к движку 3D-игры, по сути, требуется только одно изменение: управляемое игроком движение камеры и повторный рендеринг после каждого движения. Для реализации этого я поместил игрока внутрь параллелепипеда и работал над движком, пока движение не стало правильным. Doom имел очень характерный стиль движения, который я стремился воссоздать.

Первый шаг к этому реализация ускорения движения игрока в направлении его взгляда до какой-то (высокой) максимальной скорости. Я добавил больше ускорения, чем в оригинальном Doom, потому что сегодня это ощущается привычнее. Важнее для воссоздания ощущения Doom было добавление раскачивания камеры при движении игрока.

В состоянии постоянного движения это просто синусоида, но при переходе от неподвижности к ходьбе я добавил обязательные детали, чтобы перемещение казалось естественным.

Если ничего не добавлять, то игрок будет продолжать раскачивание с того самого состояния, в котором он находился во время остановки. Это выглядит очень неестественно. Чтобы это исправить, потребовались крошечные изменения: у игрока есть переменная текущего состояния анимации в цикле (по модулю 2 пи), и когда игрок прекращает двигаться во всех направлениях, то переменная сбрасывается на ноль. Это делает движение более чётким, а его начало и завершение выглядят правильно.

Также я добавил одну особенность, которой не было в Doom небольшую величину поворота камеры вдоль продольной оси на каждом шаге. (В Doom его не было, потому что движок позволял выполнять поворот только по оси Z, то есть игрок даже не мог взглянуть вверх.)

Затем я добавил ограничение движения. Нет никакого смысле создавать сложные лабиринты в стиле Doom, если игрок может проходить сквозь стены. Для правильной реализации ограничений нужно сделать игрока сферой, стены плоскостями и вычислять пересечения сферы с плоскостью. Я беспокоился, что это займёт слишком большую часть наших 13 КБ.

Поэтому вместо этого игра испускает восемь лучей из текущей позиции игрока и определяет, пересекается ли какой-нибудь из этих лучей со стеной. Если да, то я отталкиваю игрока назад, чтобы он оставался не менее чем в пяти единицах от этой стены. Так я реализовал распознавание коллизий сферы для бедных, на что мне понадобилась всего сотня байтов.

Реализовать эту систему было довольно сложно, особенно обработать случаи одновременного пересечения лучей с несколькими стенами. Во некоторых ситуациях система даёт сбои, поэтому при создании дизайна уровней я вручную тестировал их на баги и проектировал их так, чтобы баги распознавания коллизий не возникали. (Главное ведь, чтобы работало...)

Object Lathing



Чтобы в игре были объекты, мне нужно каким-то образом их описать. Логичным способом было бы использование формата файлов .obj. К сожалению, он довольно большой и неуклюжий. (Я использовал его в своём предыдущем 3D-рендерере для загрузки стандартного чайника.)

Поэтому вместо него я решил написать минималистичный метод точёных объектов (object lathe):
мы задаём 2D-контур, представленный в виде ряда точек, обтачиванием вращаем точки по кругу для генерации 3D-объекта. Это позволяет создавать красивые объекты, занимающие мало пространства. Например, для описания показанного выше кувшина потребовалось примерно двадцать байтов.

Задолго до разработки игры я также создал тщательно минимизированную до 300 байт реализацию подразделения поверхностей (surface subdivisioning) (которую ранее использовал для 3D-рендерера). Но в конечном итоге мне не хватило места и от неё пришлось отказаться. Однако я особо не жалею, потому что она не добавляла в игру ничего важного.

Игровой цикл


Всё описанное выше было довольно статичным, по миру перемещалась только камера. Однако для создания игры требуется интерактивность. И здесь нам пригодится игровой цикл.

Мы будем использовать совершенно стандартный игровой цикл. Изначально в коде он выглядел так:

      function game_step(now) {          if (game_is_over) return;          frame = requestAnimationFrame(game_step);  player_check_dead();  player_move();          player_clip_walls();          player_fall();          player_set_position();          lights.map(light => light.compute_shadowmap());          camera.draw_scene();          objects.map(object => object.update(dt));          garbage_collect();      }

Да, это красивый и удобный код, но каждая из функций вызывается только один раз. Поэтому они превратились во встроенные функции, и цикл стал некрасивым.

Оружие и враги



Шутер от первого лица не особо интересен, если нам нечем и не в кого стрелять.

Я начал с реализации стрельбы. Я не был уверен, хватит ли у меня места на несколько видов оружия (в результате его и не хватило), поэтому решил, что в шутере логично будет использовать пулемёт (chaingun) из Doom.

Модель оружия это просто несколько цилиндров, созданных при помощи object lathe. Первым этапом здесь была реализация естественного движения с оружием. Для этого я снова воспользовался уже имеющимся отслеживанием движения вверх-вниз при ходьбе: пулемёт движется вверх-вниз и в стороны, в зависимости от текущего состояния цикла ходьбы игрока.


Следующим обязательным этапом стала реализация правильных ощущений от самой стрельбы. Она состоит из трёх компонентов. Во-первых, при стрельбе возникает небольшая отдача. Во-вторых, оружие вращается. В-третьих, при каждом выстреле появляется небольшая вспышка. Всё это хорошо влияет на ощущения от игры, которая посвящена стрельбе.

Распознавание попаданий реализовано как простое сканирование испускаемым лучом (в нём используется тот же код, что и в распознавании коллизий со стенами при движении игрока). При попадании создаются частицы взрыва с небольшой вспышкой.

Для сканирования попаданий пришлось внести в систему испускания лучей несколько изменений. Во-первых, поскольку первая версия проверяла коллизии только по горизонтали, мне нужно было теперь проверять, произошла ли коллизия на нужной высоте по Z. Это сделать было не особо сложно: вычисляем, насколько далеко объект от игрока, из наклона игрока по поперечной оси определяем, какой должна быть высота пули, и если она находится в пределах ограничивающего параллелепипеда объекта, то коллизия произошла.

(Чтобы можно было стрелять по потолку и полу, потребовалось ещё одно небольшое дополнение. В противном случае при высоком прицеливании не было бы взрывов, и это казалось бы неправильным. Вся остальная система распознавания коллизий определяет коллизии только по горизонтали, а эти коллизии ориентированы по вертикали, а значит, им требуется специализированный код. На самом деле, написать его было не так сложно: записываем начальную высоту по Z и наклон игрока по поперечной оси (угол относительно плоскости xy), а затем для каждого полигона всей карты проверяем, насколько далеко должен растянуться луч, чтобы попасть по потолку и полу этого полигона; затем проверяем, действительно ли при движении луча на такое расстояние он сталкивается с полигоном в этом месте.)

После этого я сортирую все потенциальные коллизии и выбираю ближайшую.

Для стрельбы также требуется тряска экрана, и, поскольку в игре нет боеприпасов, замедление движения игрока. Важно, чтобы у игрока была причина не держать всю игру палец на спусковом крючке.


Затем я перешёл к задаче стрельбы по врагам. Сначала я создал очень простую модель-коробку персонажа. Он полностью состоит из кубов, благодаря чему его очень легко хранить: сохраняя просто модель куба, я могу растягивать его в произвольных направлениях и создать врага. Простейший враг состоит из восьми кубов.

Сначала анимация смерти была очень простой, враги всего лишь падали на землю. Это выглядело неплохо, но стрельба ощущалась слабой и скучной. Поэтому я внёс несколько изменений: враг запоминает каждый из составляющих его кубов и при попадании взрывается на части. Это выглядит гораздо лучше.

Сначала блоки, из которых состоял враг, просто разлетались сквозь стены и пропадали с экрана. Это сбивало с толку, поэтому я добавил эффект отскакивания блоков от стен. Если говорить кратко, то отскоки совершенно физически недостоверны. Коллизии снова реализованы при помощи пересечения лучей. Если ближайшая стена полностью находится на плоскости xz, то объекты меняют знак свой скорости по y. Если стена полностью на плоскости, то меняется знак скорости по x. Во всех остальных случаях меняются знаки скоростей по x и y.

Когда позиция объекта меньше, чем высота пола текущей комнаты, скорость по z меняет свой знак и уменьшается на 20%. Каждую секунду объект замедляется на 50% (как будто к нему применяется какое-то гипотетическое трение).

ИИ врагов


Несмотря на то, что я изучал и имел дело в работе с "ИИ" (со множеством кавычек), враги в игре довольно неинтеллектуальны. У врагов есть позиция и поворот. Они постоянно ходят туда и обратно, пока не пробудятся по одной из двух причин:

(а) игрок находится в поле их зрения

или

(б) игрок подстреливает врага, находящегося в поле зрения.

Определение поля зрения тоже реализовано при помощи испускания лучей. Чтобы не тратить впустую такты ЦП на испускание лишних лучей, враг проверяет других врагов всего раз в секунду.

После пробуждения враг начинает двигаться к игроку по прямой. Если на его пути что-то встаёт, то он выбирает новое случайное направление и делает несколько шагов, а затем снова начинает двигаться к игроку. Иногда движущиеся враги стреляют по игроку.

Кроме того, в момент смерти враг запрашивает помощь у других врагов, которых может видеть (снова при помощи испускания лучей). Благодаря этому сложно устранять врагов по одному из-за угла, потому что это скучная механика, и не стоит её поощрять.

Текстуры


Пока картинка выглядит очень скучной, ведь используется всего одна текстура. Я знал, что никак не смогу уместить в игру нарисованные от руки текстуры, даже самые маленькие, поэтому сгенерировал их процедурно. Я решил использовать всего три текстуры: плитки пола, кирпичный паттерн для стен и простой шум Перлина для лавы и взрывов.




Генерация кирпичей довольно тривиальна. Отрисовываем горизонтальные линии через каждые N шагов, а затем вертикальные линии через каждые N со смещением на N/2 в чётных строках. Код выглядит так:

      make_texture(cartesian_product_map(range(256),range(256),(y,x) => {          if ((y%64) <= 2 || Math.abs(x-(((y/64)|0)%2)*128) <= 2) {              return [0,0,0,1];          } else {              var r = .9-perlin_noise[x*256+y]/20;              return [r,r,r,1];          }      }).flat())

С генерацией плиток всё чуть сложнее. Сначала я создал шестиугольную решётку, а затем для каждого пикселя плитки я вычисляю расстояние до ближайших двух точек решётки. Если расстояние до ближайшей точки равно удвоенному расстоянию до второй по близости, то рисуем линию. Так мы получаем стандартный паттерн из плиток.

Шум Перлина визуально гораздо красивее, чем другие типы шумов. Его случайность выглядит намного естественнее, чем простой белый шум. Генерация шума Перлина довольно сложна, поэтому я просто дам ссылку на Википедию и скажу, что сделал так же. Также я добавил небольшое количество шума Перлина к кирпичному паттерну, чтобы он был менее скучным.

Анимации врагов



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


Один тип врагов это слишком скучно, поэтому затем я добавил небольшого летающего врага, он быстрее движется и в него сложнее попасть. Внешний вид его опять-таки очень минималистичен: это просто две похожие на крылья трапецоида и прямоугольное тело. Чтобы показать, что он как будто летает, враг немного машет крыльями и раскачивается вверх-вниз; здесь снова используется код анимаций шагающего врага.

Звук


Казалось очевидным, что для добавления звука нужно использовать JSFXR, однако на этом этапе места уже начинало не хватать. Взглянув ещё раз на код, я увидел, что ещё многое можно оптимизировать.

Благодаря последовательности небольших изменений мне удалось сократить его почти в два раза. Самая большая экономия получилась благодаря удалению map из массива в воспроизводимый файл WAV и замене его на вызов функции WebAudio, получающей непосредственно массив float. Ещё одна серьёзная экономия получилась благодаря замене куче операторов case на операции поиска в массиве. То есть я реализовал принцип мультиплексора и вычислил все возможные случаи внутри массива, а затем просто выбирал индекс нужного значения. Это медленнее, зато код короче. После замены циклов for на map и создания вспомогательной функции clamp код оказался достаточно коротким, чтобы можно было написать красивые звуковые эффекты.

Затем я захотел сделать так, чтобы аудиосистема реагировала на то, где находится игрок в 3D-пространстве. Я думал, что эта часть потребует много усилий, но оказалось, что это довольно просто, и теперь в коде есть функция createStereoPanner, выполняющая эту задачу.

В качестве фоновой музыки я переписал среднюю часть токкаты и фуги ре минор. Она просто повторяется бесконечно.

Описание карты



Одной из уникальных особенностей Doom были уровни. Каждая карта больше напоминала лабиринт, по которому игрок бегал в поисках выхода. Для создания клона это был обязательный компонент.

Однако для простого хранения карт потребовалось бы слишком много места: при наивном задании (т.е. при сохранении позиций всех стен) даже несколько простейших уровней занимали более килобайта. Поэтому карты записываются как набор многоугольников (которые могут быть и вогнутыми). У каждого многоугольника есть высота пола и потолка, а если два соседних многоугольника имеют общее ребро, то игрок может пройти из одного в другой (иногда спускаясь ниже или поднимаясь).

Для создания кода, превращающего многоугольники в трёхмерные комнаты, потребовалось довольно много усилий. Хоть и легко разместить стены вокруг каждого многоугольника, основной сложностью стало определение того, когда стены быть не должно из-за двух соседних рёбер. Чтобы упростить процесс, проём создаётся только тогда, когда у двух стен обе координаты совпадают точно.

Изначально я создавал карты, вручную задавая координаты многоугольников, но у такого подхода было две проблемы: во-первых, они занимали много места, и, что более важно, на создание тратилась куча времени. Если я хотел успеть спроектировать достаточное количество карт, то нужно было придумать что-то получше.

Возможно, хорошая идея позволит одновременно решить обе проблемы. Я реализовал очень крошечный язык программирования turtle для задания расположения областей карты. То есть вместо использования черепашки для рисования я применил её для создания многоугольников, заменив карандаш функцией задания многоугольников. Изначально этот язык был очень похож на реализацию принципа черепашки: команды перемещали черепаху, поворачивали её и заставляли двигаться на определённое расстояние.

Я даже добавил функции, позволяющие создавать циклы for и вызовы/возвраты, чтобы можно было, например, изготавливать лестницы, а затем многократно применять функцию make-stairs там, где мне были нужны лестницы.

К сожалению, проектировать карты всё равно было сложно, потому что для этого требовалось программирование черепашки на языке ассемблера, который разрабатывался как максимально компактный. У меня был всего месяц на создание всей игры, поэтому я никак не мог себе позволить потратить несколько дней на проектирование карт. Более того, на самом деле это не экономило особо много пространства. Так как ограничение в 13 килобайта относится к размеру запакованного zip исходного кода, вполне можно было использовать дублирование одной функции: сжатие zip сэкономит пространство.

Поэтому код черепашки стал таким:

      function run_turtle(commands) {          var regions = []          var turtle_location = [ZERO];          var floor_height = 4;          var ceil_height = 40;          var do_later = [];          for (var i = 0; i < commands.length;) {              var cmd = commands[i++];              // Get the "opcode" and "argument" to the command      var [low, high] = [cmd&31, cmd>>5];              // Opcode 0 is a "make polygon" command              if (high <= 1) {                  var coordinates = [turtle_location[0]]                  coordinates.push(...range(low).map(x=> {                      var dydx = NewVector(((commands[i]>>4)-7)*8,                                           ((commands[i++]%16)-7)*8, 0)                      turtle_location.unshift(turtle_location[0].add(dydx));                      return turtle_location[0];                  }));                  regions.push(new MapPolygon(coordinates,                                              floor_height,                                              ceil_height))                  // Opcode 1 is a "goto" command                  if (high == 1) {                      regions.pop();                  }              }              // Opcodes 4: adjust ceiling              floor_height += 2*(low-15)*(high==4);              // Opcodes 5: adjust floor      ceil_height += 4*(low-15)*(high==5);              // Opcodes 6: backtrack              turtle_location.splice(0,low*(high==6));          }          return [regions, do_later];      }

Чтобы упростить создание карт, я избавился от большей части языка и создал гораздо более простой язык. Я убрал циклы, вызовы функций, повороты и реализовал всего два опкода: make-polygon и goto. Опкод make-polygon получает количество вершин и последовательность байтов, где старшие 4 бита определяют сдвиг по оси x, а младшие 4 бита сдвиг по оси y. После достижения последней вершины цикл завершается и черепашка создаёт многоугольник. Опкод move просто перемещает черепашку в новое место для создания нового многоугольника.

После создания нескольких карт я посмотрел, какие части моего языка turtle занимают больше всего места. Я понял, что трачу много места на перемещение черепашки от многоугольника к многоугольнику. Как можно сократить этот процесс? Обратим внимание, что обычно многоугольники не изолированы: они соединяются с другими многоугольниками вершиной. Поэтому я добавил ещё одну функцию backtrack. При каждом движении черепашки она добавляет своё местоположение в стек. Backtrack извлекает позицию черепашки определённое количество значений назад. Это очень эффективно: более 95% перемещений черепашки было заменено командами backtrack.

Редактор карт



Для удобства создания карт я быстро набросал WYSIWYG-интерфейс. Этот редактор компилирует карты на язык turtle, чтобы их можно было использовать в игре.

Обновление карты в редакторе в реальном времени обновляет рендерящуюся игру, чтобы я сразу видел, что происходит. В видео показан процесс создания новой карты.

Так как этот редактор не будет находиться в составе игры, я мог не волноваться о качестве кода или удобстве редактора. Он невероятно уродлив и некрасив, в нём используются непонятные горячие клавиши. (Хотите добавить новый многоугольник? Выберите созданную вершину и нажмите Shift-A для выделения соседнего ребра, а затем E (для экструдирования). Хотите добавить вершину к уже существующему ребру? Shift-A и S (для разбиения). Нужно удалить многоугольник? Shift-Q. Если знать, что делать, то всё вполне логично, но не особо интуитивно понятно.)

Мысли об экономии пространства


Для успешного создания полной игры в 13 килобайтах сжатого JavaScript требуется постоянно помнить о занимаемом кодом пространстве.

Основная цель заключается в том, чтобы всегда понимать размер кода. Я загрузил скрипт на Python для отслеживания каждого изменения файла исходников и пересборки пакета. Он показывал количество свободных байтов, оставшихся для самой игры, чтобы они всегда были видимы и я мог понять, стоит ли внесённое изменение повышения сложности.

Пригодилось и то, что я написал множество вспомогательных функций, которые использовал во многих частях программы. Вот некоторые из них, которые я считаю наиболее полезными:

      var pairs = (lst,fn) => lst.slice(0,-1).map((x,i)=>fn(x,lst[i+1],i))      var transpose = (mat) => mat[0].map((x,i) => mat.map(x => x[i]))      var range = (N,a) => Array(N).fill().map((_,x)=>x+(a||0));      var reshape = (A,m) =>          range(A.length/m).map(x=>A.slice(x*m,(x+1)*m));      var urandom = _ => Math.random()*2 - 1;      var push = (x,y) => (x.push(y), x);      var clamp = (x,low,high) => Math.min(Math.max(low, x), high)      var sum = (x) => x.reduce((a,b)=>a+b)

Также я реализовал собственную математику для матриц и векторов, чтобы код был максимально сжатым. Все повороты задаются как произведения матриц 4x4: гораздо более эффективными были бы кватернионы, но они занимают больше места, поэтому я их не использовал.

В самом некрасивом моём коде я сделал так, что умножения матриц 4x4 стали и очень эффективными, и одновременно очень короткими; для этого пришлось потрудиться:

    var mat_product_symbolic = B =>        `(a,b)=>[${reshape(range(16),4).map(c=>B[0].map((_,i)=> B.reduce((s,d,j)=>`${s}+b[${d[i]}]*a[${c[j]}]`,0))).flat()}]`;    multiply = eval(mat_product_symbolic(reshape(range(16),4));

Для постоянного отображения текущего размера сборки требовался автоматизированный процесс сборки. Сначала скрипт сборки просто запускал uglifier, за которым выполнялся стандартный zip, чтобы я мог отслеживать занимаемое пространство. Вскоре я осознал, что есть оптимизации, которые позволят мне автоматизировать процесс, чтобы ещё больше сжать код. Я написал короткий скрипт, определяющий все имена переменных webgl и заменяющий их короткими однобуквенными именами (потому что uglify этого не делал). Затем я модифицировал эту программу так, чтобы она переписывала длинные имена функций WebGL, например, framebufferTexture2D, коротким трёхсимвольным кодом вида e2m (я брал восьмой символ с конца, второй символ с конца и третий символ с начала). Ближе к концу разработки я узнал о advzip, который ещё больше помог мне с улучшением сжатия.
Подробнее..

Doom Boy ESP32. Вторая итерация

30.07.2020 02:22:19 | Автор: admin
Так и не дождавшись DoomWatch платы сделал новый заказ с доставкой Fedex. В этот раз развел платы в KiCad. Как ни странна, Kicad понравился даже больше Eagle AutoDesk



Основная цель была попробовать распаять чип esp32d0wdq6. Не сам готовый модуль, а именно отдельно микросхемой с отдельным Flash и PSRAM. Как это сделано и работает, с видеодемками, читаете и смотрите дальше

Здесь можно прочитать про первую итерация платы

Boot


Boot Log
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4
load:0x3fff0034,len:7076
load:0x40078000,len:14600
load:0x40080400,len:4160
0x40080400: _init at ??:?

entry 0x40080684
I (28) boot: ESP-IDF v4.2-dev-2243-gcf056a7d0-dirty 2nd stage bootloader
I (29) boot: compile time 18:51:57
I (30) boot: chip revision: 1
I (33) boot_comm: chip revision: 1, min. bootloader chip revision: 0
I (52) boot.esp32: SPI Speed: 40MHz
I (53) boot.esp32: SPI Mode: DIO
I (53) boot.esp32: SPI Flash Size: 8MB
I (56) boot: Enabling RNG early entropy source
I (62) boot: Partition Table:
I (65) boot: ## Label Usage Type ST Offset Length
I (73) boot: 0 factory factory app 00 00 00010000 000e8000
I (80) boot: 1 wifidata WiFi data 01 02 000fc000 00004000
I (88) boot: 2 wad unknown 42 06 00100000 004f4000
I (95) boot: End of partition table
I (99) boot_comm: chip revision: 1, min. application chip revision: 0
I (106) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x38f98 (233368) map
I (216) esp_image: segment 1: paddr=0x00048fc0 vaddr=0x3ffb0000 size=0x07058 ( 28760) load
I (230) esp_image: segment 2: paddr=0x00050020 vaddr=0x400d0020 size=0x86370 (549744) map
0x400d0020: _stext at ??:?

I (468) esp_image: segment 3: paddr=0x000d6398 vaddr=0x3ffb7058 size=0x04ec4 ( 20164) load
I (478) esp_image: segment 4: paddr=0x000db264 vaddr=0x40080000 size=0x00404 ( 1028) load
0x40080000: _WindowOverflow4 at /esp/v3.3.2/esp-idf/components/freertos/xtensa/xtensa_vectors.S:1730

I (479) esp_image: segment 5: paddr=0x000db670 vaddr=0x40080404 size=0x12b7c ( 76668) load
I (538) boot: Loaded app from partition at offset 0x10000
I (538) boot: Disabling RNG early entropy source
I (549) psram: This chip is ESP32-D0WD
I (551) spiram: Found 64MBit SPI RAM device
I (551) spiram: SPI RAM mode: flash 40m sram 40m
I (555) spiram: PSRAM initialized, cache is in low/high (2-core) mode.
I (562) cpu_start: Pro cpu up.
I (566) cpu_start: Starting app cpu, entry point is 0x4008191c
0x4008191c: start_cpu0_default at /esp/v3.3.2/esp-idf/components/esp32/cpu_start.c:466

I (0) cpu_start: App cpu up.
I (1454) spiram: SPI SRAM memory test OK
I (1462) cpu_start: Pro cpu start user code
I (1462) cpu_start: Application information:
I (1462) cpu_start: Project name: esp32_doom
I (1466) cpu_start: App version: 085f21b-dirty
I (1472) cpu_start: Compile time: Jul 26 2020 18:51:49
I (1478) cpu_start: ELF file SHA256: 9166eca39a0109f9
I (1484) cpu_start: ESP-IDF: v4.2-dev-2243-gcf056a7d0-dirty
I (1491) heap_init: Initializing. RAM available for dynamic allocation:
I (1498) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (1504) heap_init: At 3FFCF628 len 000109D8 (66 KiB): DRAM
I (1511) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (1517) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (1524) heap_init: At 40092F80 len 0000D080 (52 KiB): IRAM
I (1530) spiram: Adding pool of 4096K of external SPI memory to heap allocator
I (1539) spi_flash: detected chip: generic
I (1543) spi_flash: flash io: dio
I (1548) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (1557) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations


KiCad PCB


В KiCad как мне показалось все проще. На мой вкус конечно. Немного по другому чем в Eagle Autodesk. Главный плюс в том, что нет ограничения на размер платы если не хотите платить за лицензию. Напомню что в Eagle есть ограничение на размер 100x80мм для бесплатной разработки. Если плата больше надо платить годовую лицензию. По моему, есть также ограничение на количество слоев



Посмотреть 3D View в Eagle я так и не смог. Надо ставить Eagle Fusion 360. В Kicad это отдельный пункт меню. Хотя может я не там смотрел. В общем можно сказать перешел полностью на KiCad

Разделил все на модули, оставив на главной странице то что связано с ESP32 непосредственно. Сам чип и память


Ошибки


Рисовал плату в попыхах. В результате забыл вывести GPIO0. Подвел ее во внешний DAC как MCCLK, а вот вывести на кнопку режима загрузки забыл. Пришлось сдирать лак с дорожки для пайки вывода кнопки. Так же резистор в цепи кварца развел на XTAL_N а в мануале надо на XTAL_P. Повесил резистор несколько Ом, но можно было и перемычку кинуть

Не развелись резисторы Address и Reset для MCP23017. Чипу можно задать адрес I2C тремя выводами. 0x20h когда все три вывода на земле. Все потому что не указал FootPrint

На ошибку не обратил внимания
Error: Cannot add R10 (no footprint assigned).
Error: Cannot add R9 (no footprint assigned)


Перепутал i2C для ES8374. Бывает и такое. В принципе можно использовать переключив 21 -> 22 и 22 -> 21 выводы. Или перерезать дорожки и отскоблить лак, ну как это мы обычно делали с TX RX Uart (Грустный смайл)



На удивление запаяв ESP32 и FLASH плата завелась без каких то проблем. Но поставив SPRAM получил бесконечный reboot. Оказался банальный непропай по питанию. Вот бы я искал если ESP32 не припаялся!



Мультиплексор MCP23017


Предусмотрел возможность альтернативного опроса кнопок как через мультиплексор MCP23017 так и с помощью ЦАП. На резестивном делителе. Если нет возможности поставить мультиплексор можно задействовать внутренний ADC. По уровню напряжения на входе GPIO34 можно детектировать нажатие кнопки. Минус в том что невозможно опрашивать несколько кнопок одновременно. Плюс соответственно, что не надо дополнительной микросхемы. Только несколько резисторов

Мультиплексор имеет 2 порта по 8 выводов. Один порт, в нашем случае, можно сконфигурировать как выход и повесить на них светодиоды. Хотел предусмотреть мигание если жизни осталось меньше 20% или кончились патроны. Тогда можно будет играть без панели. Получилось реализовать. Остался один зеленый светодиод. Если в комнате нет врагов или в прямой видимости можно подсветить



Thread of the health and ammo indicator
void ledTask(void *arg){    while(1){            p = &players[cur_player];            if (p->mo != NULL && p->mo->health < 20) {                mcp23x17_set_level(&dev, 9, on);            } else {                mcp23x17_set_level(&dev, 9, true);            }            if(p->ammo[weaponinfo[p->readyweapon].ammo] < 5){              mcp23x17_set_level(&dev, 10, on);            } else {              mcp23x17_set_level(&dev, 10, true);            }            printf("p->ammo[am_clip] = %d\n", p->ammo[weaponinfo[p->readyweapon].ammo]);            if (p->mo != NULL) {                printf("p->mo->health = %d \n", p->mo->health);            }            printf("Ammo N = %d\n", weaponinfo[p->readyweapon].ammo);             on = !on;             vTaskDelay(1000/portTICK_PERIOD_MS);    }}



Аудио чип ES8374


Для звука, в качестве ЦАП и АЦП применил ES8374. Микросхема содержит встроенный усилитель низкой частоты ~ 1.25 Ватт. Она поддерживается Espressif Audio Development Framework из коробки. В маленьком корпусе QFN-28 получаем Моно ЦАП, АЦП для микрофона и УНЧ с поддержкой SDK. То что надо для такого устройства


Запустил BT Speaker из примера pipeline_bt_sink

Взял в настройках борду lyrat_v4_3. Исправил кодек на AUDIO_CODEC_ES8374_DEFAULT_HANDLE
Настроил порты GPIO
esp_err_t get_i2s_pins(i2s_port_t port, i2s_pin_config_t *i2s_config){    AUDIO_NULL_CHECK(TAG, i2s_config, return ESP_FAIL);    if (port == I2S_NUM_0 || port == I2S_NUM_1) {        i2s_config->bck_io_num = GPIO_NUM_18;        i2s_config->ws_io_num = GPIO_NUM_26;        i2s_config->data_out_num = GPIO_NUM_27;        i2s_config->data_in_num = GPIO_NUM_35;    } else {        memset(i2s_config, -1, sizeof(i2s_pin_config_t));        ESP_LOGE(TAG, "i2s port %d is not supported", port);        return ESP_FAIL;    }    return ESP_OK;}

И выкинул инициализацию audio_board_key_init и audio_board_led_init. По правильному надо определять свою кастомную плату

Гироскоп L3GD20


Решил добавить гироскоп L3GD20. Интересная особенность, что его можно подключить как SPI или I2C устройство. R27, R28 в случае подключения по I2C задают адрес
// L3GD20H addresses
#define L3GD20H_I2C_ADDRESS_1 0x6a // SDO pin is low
#define L3GD20H_I2C_ADDRESS_2 0x6b // SDO pin is high
На GitHub есть библиотека l3gd20h-esp-idf


Таким образом на I2C имеем 3 устройства. Управление ADC-DAC ES8374, кнопки на MCP23017 и собственно L3GD20. Запустил все три

i2cdetect
i2c-tools> i2cdetect
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: 10 -- -- -- -- -- -- --
20: -- -- 24 -- -- -- -- --
30: -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- --
60: -- -- -- -- -- 6a -- --
70: -- -- -- -- -- -- -- --


Но почемуто L3GD20 не отдает координаты. WHO_AM_I корректо отвечает 0xd4. углы не меняются. У меня нет конденсатора С1 10nF, попытался поставить который нашел самый близкий 100nF. Думал пусть хотя бы врет, но похоже не запускается внутрений преобразователь. И это важно

i2cdump
i2c-tools> i2cdump -c 0x6a
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: b6 5e 81 fc 05 50 31 83 c4 f9 85 d0 48 c6 00 d4 ?^???P1?????H?.?
10: 1a 15 16 ea c0 b9 4f 72 07 d8 a1 21 a1 00 14 02 ??????Or???!?.??
20: 07 00 00 80 00 00 0c 00 1a 00 13 00 16 00 00 20 ?..?..?.?.?.?..
30: 00 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 .........?..
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


Дисплей 18 pin ILI9341




Рассчитал ток подсветки так. Требуется 90mA. Поскольку падение на открытом транзисторе ~0.7V питание для светодиодов подсветки 3.3V 0.7V = 2.6V. И по закону Ома 2.6V / 0.090A = 28.8 Ohm. Поставил 47 Ohm. Получилось немного темновато. Надо будет уменьшить сопротивление


Также развел Touch XPT2046TS. SPI выводы повесил параллельно дисплею. XPT_CS выведен отдельным выводом. Есть сомнение что заработает. Если бы дисплей не завелся, сделал бы эксперименты. Для приставки он не особо нужен. Взял из примера



Демки


Запустил демо LVGL. В настройках примера пробовал выставлять 40MHz для шины SPI. Пример заработал немного быстрее чем на видео



А вот Doom запустился и стабильно работал только на 32MHz. Хотя 26MHz для ILI9341 уже считается разгон
spi_device_interface_config_t devcfg={        .clock_speed_hz=26000000,               //Clock out at 26 MHz. Yes, that's heavily overclocked.        .mode=0,                                //SPI mode 0        .spics_io_num=PIN_NUM_CS,               //CS pin        .queue_size=NO_SIM_TRANS,               //We want to be able to queue this many transfers        .pre_cb=ili_spi_pre_transfer_callback,  //Specify pre-transfer callback to handle D/C line    };




Заключение


Портов ввода вывода ESP32 не всегда хватает чтобы нафаршировать по максимуму. В этом плане STM32 выглядит привлекательнее. Но у нее нет встроенной поддержки Wi-Fi

В заключении отмечу, что на устройстве можно запустить Nintendo эмулятор ESP32-NESEMU, a Nintendo Entertainment System emulator for the ESP32
Я заказал Game Console с Али. Интересно будет посмотреть что там внутри. Возможно тоже ESP32



Обсуждение и советы жду в комментариях
Подробнее..

Перевод Играем в Doom в среде VMware ESXi на Raspberry Pi

01.11.2020 20:22:23 | Автор: admin

Недавно мы опубликовали статью о VMware ESXi на Raspberry Pi. В начале октября разработчики анонсировали техническое превью гипервизора VMware ESXi на архитектуре aarch64 под названием ESXi-Arm-Fling.

Повторять суть той статьи не будем, вспомним лучше основной вывод гипервизор на Raspberry Pi нужен для того, чтобы обкатать технологию работы ESXi на ARM-процессорах. Но еще можно развлечься. Если у вас есть свободное время, попробуйте тряхнуть стариной, поиграв в Doom в среде VMware ESXi на Raspberry Pi. Под катом подробности, как это сделать.

Главное, что нужно сделать, установить ESXi на вашей малинке. Процесс установки можно посмотреть на страничке Fling. Загрузить пакет по этой ссылке. Совет: даже если вы опытный пользователь/администратор vSphere, изучите подробности установки ESXi.


Как только все готово и у вас появился доступ к интерфейсу ESXi HTML5, можно установить виртуальную машину на базе ARM. Один из неплохих вариантов Ubuntu 20.04.1 LTS.

Загрузите .iso в Datastore. В качестве последнего можно использовать адаптер M.2-USB. Для того чтобы получить такую возможность, необходимо отключить USB Arbitrator.

~ # /etc/init.d/usbarbitrator stop
~ # chkconfig usbarbitrator off


После этого перезагрузите ESXi и создайте Datastore при помощи интерфейса HTML5. Если на этом этапе возникли какие-то проблемы, стоит ознакомиться с деталями установки вот здесь.

Теперь самое время создать виртуальную машину со следующими характеристиками:

  • OS: Ubuntu Linux (64-bit)
  • 2 vCPU
  • 2 GB ОЗУ
  • CD Drive: Datastore ISO File (ubuntu-20.04.1-live-server-arm64.iso)

Все остальное можно оставить по умолчанию. Кроме того, не стоит играться с настройками SCSI- или IDE-контроллеров, поскольку в один прекрасный момент виртуальная машина может просто перестать нормально работать. Просто оставьте то, что было по умолчанию, SATA.

После запуска виртуальной машины должна начаться установка Ubuntu. Проблем на этом этапе возникнуть не должно просто следуйте инструкции. После завершения установки залогиньтесь и установите дополнительные пакеты:

# sudo apt-get install libsdl-mixer1.2-dev libsdl-net1.2-dev gcc make unzip

Загружаем и компилируем Chocolate Doom:

# wget wget www.chocolate-doom.org/downloads/2.2.1/chocolate-doom-2.2.1.tar.gz
# tar xzf chocolate-doom-2.2.1.tar.gz
# cd chocolate-doom-2.2.1/
# ./configure make
# sudo make install


Ну а теперь момент откровения. Загружаем файл Doom WAD. Это оригинальная версия игры. Если нужны модифицированные версии, их можно поискать в сети, за прошедшее с момента выхода игры время чего только не придумали.

Модифицировать Doom не составляет особого труда звуковые файлы, текстуры и все прочие ресурсы хранятся в отдельных папках. В целом, Doom изначально задумывалась кастомизируемой игрой, которую могут с легкостью изменять игроки.

# wget www.doomworld.com/3ddownloads/ports/shareware_doom_iwad.zip
# unzip shareware_doom_iwad.zip


Самое время играть!

# chocolate-doom -iwad DOOM1.WAD



Да, не очень. Вот такая картинка получается, если играть по SSH. Лучше всего, конечно, использовать десктопную версию. У Ubuntu Server десктоп не установлен по умолчанию. Если он нужен, выбираем tasksel, запускаем и выбираем Ubuntu desktop.

# sudo apt-get install tasksel
# sudo tasksel




Установка займет какое-то время. После завершения перезагружаем систему для автоматической загрузки десктопной версии. Дальше все просто:

# cd chocolate-doom-2.2.1/
# chocolate-doom -iwad DOOM1.WAD




Подробнее..

STM32F429 IL9341 LVGL, DOOM1

27.01.2021 22:06:12 | Автор: admin

Воспользовавшись Новогодними праздниками, продолжил поднимать элементы на своей плате. Первым делом после того как запустился дисплей провел тест Lvgl графической библиотеки. Результаты показались удовлетворительным. Около 20 FPF. Иногда были просадки но в целом, без использования DMA и контроллера Chrom-ART, который есть на борту картинка плавная. ART использовать не получится, потому что дисплей с интерфейсом SPI. Это было не первое ограничение с которым я столкнулся на пути оптимизации с целью увеличения FPS


Статью скорее надо рассматривать в образовательных или исследовательских целях. Я пришел к выводу, что если разрабатывать устройство то надо использовать все фичи, а то получится такой испытательный стенд. На котором не работает Chrom-ART

LVGL

Подключить https://lvgl.io было не сложно, тем более есть два порта под Discovery STM32F429 https://github.com/lvgl/lvportstm32f746_disco

Выделение полнокадрового буфера у меня не получилось по причине нехватки памяти в одном сегменте. Решил задействовать SDRAM. Тут меня ждало разочарование с передачей буфера экрана за один раз. Экран 3202402 = 153600 байт на фрэйм. STM32F429 DMA на SPI имеет ограничения по причине 16 битного внутреннего счетчика. Соответственно, чтобы передать кадр надо выполнить 153600 / 65535 = ~2,3 транзакции. Не очень ситуация. Это и оказалось бутылочным горлышком. Надо ждать когда будет отправлен последний кусок чтобы начинать отрисовывать новый фрэйм

DMA restriction 16 bit

There's also an underlying hardware issue: the DMA's NDTR (counting) register is 16-bit long.

Ниже то что получил в ответ на вопрос по проблеме https://forum.lvgl.io/t/stm32f429-ili9341-spi/4187

В голову приходит решение сделать Double buffer. В одном рисуем, отдаем на отправку и продолжаем рисовать в другом, если он освободился. В свою очередь оправка смотрит, есть ли готовый буфер и отправляет его переходя к другому. ART позволяет это делать почти автоматически. В изначальной реализации https://github.com/floppes/stm32doom это используется

lvgl forum answer

Your buffer setting (NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX) is too high (as you already noticed by yourself

Normally the buffer is set around to be LVHORRESMAX * 20 (or 40, or more or less).You are using DMA. DMA in STM32F4/7 or STM32FH7 can only handle up buffer length up to 16 bit (means 65535 bytes).

You set your buffer to LVHORRESMAX * LVVERRES_MAX. For a 320 x 240 that is 76 800.
As you have a 16-bit color values, it means 76800 * 2 = 153600 bytes. Thats more than double of that what the DMA can handle at once.
As only less than a half of the necessary data is transferred to the display, frame rate gets more than doubled.

This is true if lvgl is updating the entire (or nearly the entire) display at once.
When only smaller parts of the display will be redrawn everything is ok. As the used buffer is smaller (less than the 16-bit limit of DMA).

You can of course change your flush function to split the display transfer into smaller parts,
but that will not change the overall time you need for transfer all the display data over SPI.
The bottleneck is SPI.

But with the method to have the buffer within the SDRAM (external memory), I think you will slow down lvgl.

Using the external SDRAM would make sense only when using it as a real frame buffer for display, in the case the STM32F4/7 would use it directly for (parallel) display driving.

Как приготовить Doom в Кубе

За основу взят порт https://github.com/floppes/stm32doom

https://github.com/floppes/stm32doomПо файловой системе изменений почти никаких. Надо только смонтировать диск. Подключаем по SDIO через 4е линии. Не забываем ставить делитель хотя бы на 4. Без него не проходит инициализация флэшки. Я ставлю на 10. После инициализации впрочем можно вернуть частоту назад.

 FRESULT res = f_mount(&SDFatFs, (TCHAR const*)SDPath, 1);

Добавляем секцию sdram. На ней будет организована куча. Так же обозначим для каких файлов будем использовать sdram для статических переменных. Еще я задействовал CCMRAM под стек

/* Highest address of the user mode stack */_estack = ORIGIN(CCMRAM) + LENGTH(CCMRAM);/* end of "RAM" Ram type memory *//* Internal Memory Map*/MEMORY{  CCMRAM    (xrw)    : ORIGIN = 0x10000000,   LENGTH = 64Krom (rx): ORIGIN = 0x08000000, LENGTH = 2048Kram (rwx)   : ORIGIN = 0x20000000, LENGTH = 192Ksdram (rwx) : ORIGIN = 0xD004B000, LENGTH = 7892K /* first 300K is used as LCD frame buffer */}SECTIONS {/* external SDRAM */.sdram (NOLOAD) :{. = ALIGN(4);*(.sdram .sdram.*)bin/chocdoom/i_video.o(COMMON)bin/chocdoom/r_bsp.o(COMMON)bin/chocdoom/w_wad.o(COMMON)bin/chocdoom/r_main.o(COMMON)bin/chocdoom/r_plane.o(COMMON)} > sdram...

Теперь подправим метод _sbrk для управления динамической памятью кучи через sdram. Реализация sbrkr на ваше усмотрение

#define HEAP_SIZE0x00700000 // 7 MB/* heap */__attribute__ ((section(".sdram")))static char heap[HEAP_SIZE];caddr_t _sbrk_r (struct _reent* r, int incr){...

Верхушка памяти отведена под два буфера экрана

    sdram (rwx) : ORIGIN = 0xС004B000, LENGTH = 7892K /* first 300K is used as LCD frame buffer */

Напоминаю, что FMC может управлять до двух банков, соответственно адреса банков

  1. 0xС0000000

  2. 0xD0000000

Отрисовка экрана находится в файле i_video.c

Надо еще было развернуть байты, но я это сделал в инициализации палитры

// swapped = (num>>8) | (num<<8)

Первый эксперимент был по отрисовке линии, второй всего буфера

void I_FinishUpdate (void){        int x, y;        byte index;        lcd_vsync = false;        for (y = 0; y < SCREENHEIGHT; y++)        {                for (x = 0; x < SCREENWIDTH; x++)                {                        index = I_VideoBuffer[y * SCREENWIDTH + x];                        ((uint16_t*)lcd_frame_buffer)[y * SCREENWIDTH + x] = rgb565_palette[index];                        // swapped = (num>>8) | (num<<8);                }//              lcd_draw_line (lcd_frame_buffer, y);        }        lcd_draw_buff (lcd_frame_buffer);        lcd_refresh ();        lcd_vsync = true;}

После вывода линий, стал подключать RTOS и уперся в выделение памяти. Тюнингом удалось дойти до заставки. Но потом сваливался в static void HardFault_Handler(void)

Еще момент, в том что автор немного подправил реализацию Chocdoom. Были падения по выходам за границы массива при внутреннем построении сцены. Как известно STM32F429 Discavery имеет 2.4" QVGA TFT LCD дисплей. Я же взял 320x240 SPI

Если бы не ограничение в передаче через DMA в устройство подключенное на SPI можно было бы обойтись без многопоточности. Просто отсылать после отрисовки буфер. И возможно ждать перед началом поставки следующего. Но STM32F4 может пересылать 0xFFFF байт за раз, а это при экране 320x240 и 2 байта на пиксель надо делать более двух раз

Первый и последний эксперимент был сделан с посылкой и ожиданием в 3 этапа. Ниже функция которая это делает

void fill_display(SPI_HandleTypeDef *hspi,                DMA_HandleTypeDef *hdma_spi_tx,                uint8_t *buff,                uint32_t size){        uint16_t max_transaction_size = 0xFFFF;        uint16_t send_size;        uint32_t offset = 0;        uint8_t done = 0;        HAL_GPIO_WritePin(GPIOC, DC_Pin, GPIO_PIN_SET);        HAL_GPIO_WritePin(GPIOC, CS_Pin, GPIO_PIN_RESET);        do {                if (offset + max_transaction_size <= size){                        send_size = max_transaction_size;                } else {                        send_size = size - offset;                        done = 1;                }                HAL_SPI_Transmit_DMA(hspi, buff + offset, send_size);                while (HAL_DMA_STATE_BUSY == HAL_DMA_GetState(hdma_spi_tx))                    { continue; }                offset += max_transaction_size;        } while(done == 0);        HAL_GPIO_WritePin(GPIOC, DC_Pin, GPIO_PIN_RESET);}

Сделал ради эксперимента. Скорость впечатлила. По сравнению с ESP32 из моих прошлых экспериментов http://personeltest.ru/aways/habr.com/ru/post/512130/ картинка побежала явно быстрее. ESP32 использовал RTOS но имел на борты PSRAM против SDRAM у STM32F429. Последовательный интерфейс против параллельного. Два ядра у ESP32 против одного у STM32F429. STM32F429 "немного" дороже раза этак в два

В дополнении я начал прикручивать кнопки и внезапно Doom стал работать задом на перед. Демка крутилась в обратную сторону. Открывать двери надо было спиной..

На этом моменте эксперимент был закончен

Материалы по теме

В планах

В планах собрать STM32F750x8. На борту у нее всего 64K flash но можно исполнять инструкции из внешней flash памяти. Расширяемый бюджетный STM32F7

3.9 Quad-SPI memory interface (QUADSPI)

All devices embed a Quad-SPI memory interface, which is a specialized communication interface targeting Single, Dual or Quad-SPI Flash memories. It can work in:

Direct mode through registers.

External flash status register polling mode.

Memory mapped mode. Up to 256 Mbytes external flash are memory mapped, supporting 8, 16 and 32-bit access. Code execution is supported. The opcode and the frame format are fully programmable. Communication can be either in Single Data Rate or Dual Data Rate.

Code execution is supported - Hello ESP32!

Схема всего этого поделья

Подробнее..

Запускаем DOOM на лампочке

14.06.2021 16:14:45 | Автор: admin
image


В DOOM уже поиграли на пианино и на клавиатуре, на тесте на беременность (кстати, это был фейк) и на паяльнике, на самолёте, банкомате, принтере и осциллографе.

Пришло время для лампочек.

imageВнутри лампочки TRDFRI RGB GU10 (IKEA model: LED1923R5) хакеры из Next-Hack нашли модуль Silicon lab's MGM210L RF module с 108кб оперативки и запустили на нем DOOM. Исследователям-хакерам пришлось попотеть над оптимизацией использования оперативки, потому что оригинальный DOOM требует 4мб, но они смогли.

Модуль имеет только 1 МБ внутренней флэш-памяти, поэтому умельцы добавили внешнюю флэш-память SPI для хранения файла WAD, который можно загрузить с помощью YMODEM. Процессор: 40-MHz Cortex M4.



image

Лампочка в разборе

image

Дисплей: 1.8 TFT 160128 SPI

image

Блок-схема железа

image

Схема токов

image

Блок питания

image

Прототип платы

image

Несущая плата

image

Клавиатура

image

Микроконтроллер, усановленный на несущую плату.

image

Система в сборе.

Фичи проекта:

  • Основан на прекрасном Doomhack's GBA Doom Port с некоторыми дополнениями Kippykip.
  • Поддерживает полную версию Doom Shareware WADS.
  • Полный движок Vanilla Render, включая Z-depth lighting. Однако составные текстуры имеют MIP-карту.
  • Реализовано поведение монстров и распространение звука.
  • Поддержка Sound FX.


Ограничения проекта:

  • Музыка.
  • Воспроизведение демо из-за несовместимости.
  • Полный WAD DOOM Не тестировал.
  • Требуется отладка.
  • Мультиплеер не реализован.
  • Оптимизация производительности.
  • Читы не проверяли, наверно с багами.


Инженеры недавно сделали обновление в репозитории Github и удалили mip-отображение на составных текстурах без потери производительности, поэтому графика будет более детальной, чем то, что показано в демонстрационном видео.

Условия DOOM-challenge:



  • Найдите готовое устройство, не предназначенное для игры в Doom или в другие игры.
  • Выбранное устройство должно иметь микроконтроллер с разумно ограниченной вычислительной мощностью и/или памятью в соответствии с минимальными требованиями DOOM (DOOM работает с приемлемой частотой кадров даже на 486 @ 33MHz [1], оборудованном 4MB RAM). В качестве примера мы должны исключить современные цифровые камеры, которые имеют систему на несколько сотен МГц на кристалле, с несколькими десятками мегабайт оперативной памяти.
  • Мы должны использовать именно тот микроконтроллер, который встроен в выбранное устройство. Замена невозможна. Никакого дополнительного микроконтроллера добавить нельзя. Тем не менее, разгон (например, даже если он ограничен только некоторыми периферийными устройствами или шинами) возможен при условии, что нам не нужны какие-либо методы охлаждения.
  • Для хранения файлов WAD можно добавить дополнительную флеш-память или карту памяти.
  • Можно добавить цветной дисплей, если на выбранном стандартном устройстве его нет. Разрешение должно быть достаточно высоким, чтобы можно было прилично играть в Doom. Например, экран 3216 пикселей слишком мал, но 128x64 может быть достаточно. С другой стороны, слишком большие разрешения, безусловно, потребуют очень мощного микроконтроллера, вопреки правилу 2.
  • Устройство ввода может быть любым, поэтому для этой цели можно добавить дополнительную электронику.
  • При необходимости блок питания можно заменить.
  • Мы хотели, чтобы движок был максимально приближен к исходному (ванильному) условно-бесплатному ПО Doom. Возможность играть в эпизоде 1 на карте 1 условно-бесплатной игры Doom (E1M1) это минимальная цель, даже если мы не скрываем, что мы мечтали иметь возможность сыграть полную условно-бесплатную версию без ограничений на всех картах.
  • К звуку требований нет, но звуковые эффекты были бы действительно плюсом. Если реализовано, то на аудиоподсистему ограничений нет.
  • Мультиплеер не обязателен


Тест на беременность




Принтер Canon Proxima




Осциллограф




Банкомат




DOOM в DOOM




Пианино




Minecraft




Валидатор билетов




Микроволновка


image

iPod Mini




Калькулятор




Apple Watch




MacBook Pro Touch Bar




Kodak DC260 Digital Camera from 1998




На клавиатуре




На билборде




На электронной книге




На самолёте




Porsche 911




Vectrex




Паяльник




PS


Как тебе такое, AlexeyNadezhin?

zhovner, ждём DOOM на FlipperZero.

image
Подробнее..

Перевод Запускаем DOOM на лампочке

14.06.2021 18:13:31 | Автор: admin
image


В DOOM уже поиграли на пианино и на клавиатуре, на тесте на беременность (кстати, это был фейк) и на паяльнике, на самолёте, банкомате, принтере и осциллографе.

Пришло время для лампочек.

imageВнутри лампочки TRDFRI RGB GU10 (IKEA model: LED1923R5) хакеры из Next-Hack нашли модуль Silicon lab's MGM210L RF module с 108кб оперативки и запустили на нем DOOM. Исследователям-хакерам пришлось попотеть над оптимизацией использования оперативки, потому что оригинальный DOOM требует 4мб, но они смогли.

Модуль имеет только 1 МБ внутренней флэш-памяти, поэтому умельцы добавили внешнюю флэш-память SPI для хранения файла WAD, который можно загрузить с помощью YMODEM. Процессор у лампочки 40-MHz Cortex M4.



image

Лампочка в разборе

image

Дисплей: 1.8 TFT 160128 SPI

image

Блок-схема железа

image

Схема токов

image

Блок питания

image

Прототип платы

image

Несущая плата

image

Клавиатура

image

Микроконтроллер, усановленный на несущую плату.

image

Система в сборе.

image

Фичи проекта:

  • Основан на прекрасном Doomhack's GBA Doom Port с некоторыми дополнениями Kippykip.
  • Поддерживает полную версию Doom Shareware WADS.
  • Полный движок Vanilla Render, включая Z-depth lighting. Однако составные текстуры имеют MIP-карту.
  • Реализовано поведение монстров и распространение звука.
  • Поддержка Sound FX.


Ограничения проекта:

  • Музыка.
  • Воспроизведение демо из-за несовместимости.
  • Полный WAD DOOM Не тестировал.
  • Требуется отладка.
  • Мультиплеер не реализован.
  • Оптимизация производительности.
  • Читы не проверяли, наверно с багами.


Инженеры недавно сделали обновление в репозитории Github и удалили mip-отображение на составных текстурах без потери производительности, поэтому графика будет более детальной, чем то, что показано в демонстрационном видео.

Условия DOOM-challenge:



  • Найдите готовое устройство, не предназначенное для игры в Doom или в другие игры.
  • Выбранное устройство должно иметь микроконтроллер с разумно ограниченной вычислительной мощностью и/или памятью в соответствии с минимальными требованиями DOOM (DOOM работает с приемлемой частотой кадров даже на 486 @ 33MHz [1], оборудованном 4MB RAM). В качестве примера мы должны исключить современные цифровые камеры, которые имеют систему на несколько сотен МГц на кристалле, с несколькими десятками мегабайт оперативной памяти.
  • Мы должны использовать именно тот микроконтроллер, который встроен в выбранное устройство. Замена невозможна. Никакого дополнительного микроконтроллера добавить нельзя. Тем не менее, разгон (например, даже если он ограничен только некоторыми периферийными устройствами или шинами) возможен при условии, что нам не нужны какие-либо методы охлаждения.
  • Для хранения файлов WAD можно добавить дополнительную флеш-память или карту памяти.
  • Можно добавить цветной дисплей, если на выбранном стандартном устройстве его нет. Разрешение должно быть достаточно высоким, чтобы можно было прилично играть в Doom. Например, экран 3216 пикселей слишком мал, но 128x64 может быть достаточно. С другой стороны, слишком большие разрешения, безусловно, потребуют очень мощного микроконтроллера, вопреки правилу 2.
  • Устройство ввода может быть любым, поэтому для этой цели можно добавить дополнительную электронику.
  • При необходимости блок питания можно заменить.
  • Мы хотели, чтобы движок был максимально приближен к исходному (ванильному) условно-бесплатному ПО Doom. Возможность играть в эпизоде 1 на карте 1 условно-бесплатной игры Doom (E1M1) это минимальная цель, даже если мы не скрываем, что мы мечтали иметь возможность сыграть полную условно-бесплатную версию без ограничений на всех картах.
  • К звуку требований нет, но звуковые эффекты были бы действительно плюсом. Если реализовано, то на аудиоподсистему ограничений нет.
  • Мультиплеер не обязателен


Тест на беременность




Принтер Canon Proxima




Осциллограф




Банкомат




DOOM в DOOM




Пианино




Minecraft




Валидатор билетов




iPod Mini




Калькулятор




Apple Watch




MacBook Pro Touch Bar




Kodak DC260 Digital Camera from 1998




На клавиатуре




На билборде




На электронной книге




На самолёте




Porsche 911




Vectrex




Паяльник




PS


Как тебе такое, AlexeyNadezhin?

zhovner, ждём DOOM на FlipperZero.

image
Подробнее..

Перевод Как рендерится кадр DOOM Ethernal

11.09.2020 14:12:59 | Автор: admin


Вступление


Doom Eternal не нуждается в отдельном представлении: это прямой преемник Doom 2016, разработанный благодаря седьмой итерации id Tech, внутреннего движка студии id Software. В свое время меня поразило и высокое качество визуальной составляющей Doom 2016, и простота и элегантность технических решений. В этом отношении Doom Eternal превосходит своего предшественника во многих областях, и некоторые из них достойны детального разбора. В этой аналитической статье я постараюсь обсудить их все.

Мой анализ вдохновлен трудом Adrian Courrges про Doom 2016 (перевод). Я считаю, что подобные работы позволяют взглянуть на подходы к решению некоторых проблем рендеринга AAA-проектов и тем самым становятся превосходными обучающими материалами. В этом анализе я планирую обсудить общие особенности и не погружаться слишком глубоко в тонкости каждого способа и прохода рендеринга. Кроме того, некоторые проходы в Doom Eternal почти не отличаются от своих аналогов в Doom 2016 и уже были разобраны в труде Adrian Courrges, поэтому я могу их пропустить.

Хочу особым образом отметить строго обучающий характер текущей статьи. Я никоим образом не поддерживаю реверс-инжиниринг продуктов с целью кражи интеллектуальной собственности или иного злого умысла. Если вы еще не успели сыграть в Doom Eternal, можете не беспокоиться: я разбирал лишь самое начало игры, поэтому спойлеры вам не грозят.

Итак, приступим.

С выходом id Tech 7 переход движка с OpenGL на Vulkan API позволил разработчикам эффективнее работать с особенностями текущего поколения графических процессоров, например несвязанными ресурсами (bindless resources).

Один кадр в Doom Eternal




Выше мы можем видеть близкую к началу секцию игры: интерьер с несколькими противниками и объемным освещением. По аналогии с его предшественником, процессом визуализации в Doom Eternal заведует прямой рендеринг, однако если Doom 2016 вынужден проводить прямой рендеринг совместно с G-буферизацией отражающих поверхностей, в нашем случае буфер не используется и прямой рендеринг берет на себя все задачи.

Уход от мегатекстур


С выходом созданной на движке id Tech 5 игры Rage мир познакомился с концептом реализации текстур под названием мегатекстуры. Этот метод применяется в Doom 2016 и на каждый кадр он рендерит так называемую виртуальную текстуру с информацией о видимых текстурах. Виртуальная текстура анализируется в следующем кадре, чтобы определить какие текстуры следует подгрузить с диска. Однако у мегатекстур есть очевидная проблема: как только текстура попадает поле зрения, подгружать ее уже поздновато, поэтому на первых нескольких кадрах после появления текстура выглядит размыто. С выходом id Tech 7 разработчики отказались от такого метода.

Скиннинг через графический процессор


Обычно еще до отрисовки каких-либо текстур и шейдинга оценку скиннинга проводит вершинный шейдер. Скиннинг id Tech 7 проводится заранее вычислительным шейдером с записью итоговых вершин в буфер. Благодаря такому подходу вершинному шейдеру больше не нужны данные скиннинга, и так как при каждом проходе геометрии он больше не проводится, в итоге перестановки шейдеров случаются реже.

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

По ссылке можно ознакомиться с отличной статьей о скиннинге через вычислительный шейдер за авторством Jnos Turnszki.

Полезно также отметить, что в Doom Eternal используется интересный вид кеширования Alembic Cache, сравнимый с сильно сжатым обратно воспроизводимым видео. В таких кешах хранится запеченная анимация для выдачи и разжатия в ходе исполнения программы. Цитируя технический анализ Digital Foundry, Alembic Cache применяется в широком диапазоне анимаций, начиная с масштабных кинематографичных сцен и заканчивая крохотными щупальцами на полу. Особенно удобен такой подход для анимаций со сложностями реализации через скиновую анимацию, например для органики и симуляции тканей. Если вас заинтересовала эта технология, рекомендую ознакомиться с презентацией Axel Gneiting на Siggraph 2014.

Карты теней


Следующим этапом является рендеринг теней, и подход к генерации их карт на первый взгляд у id Tech 7 и его предшественника не отличается.

Как можно видеть ниже, тени рендерятся в большую текстуру глубиной 24 бита и размером 4096 на 8196 пикселей, местами различаясь по уровню качества. Текстура не меняется между кадрами, и согласно презентации Devil is in the Details на Siggraph 2016, статичная геометрия кешируется в карте теней, чтобы не перерисовать ее для каждого кадра. Идея сама по себе проста: нам не нужно обновлять тени до тех пор, пока перед источником света не начнется движение, и таким образом мы можем объявить кешированную карту теней: обычную карту со статичной геометрией, так как мы полагаем, что геометрия не меняется. Если в конусе обзора движется динамический объект, кешированная карта теней копируется в основную, и поверх этого перерисовывается динамическая геометрия. Такой подход позволяет не перерисовывать всю сцену в конусе обзора при каждом ее обновлении. Естественно, при смещении света всю сцену придется перерисовать с нуля.

Для сглаживания краев теней при сэмплировании карты применяется 3х3 PCF сэмплинг. Поскольку солнечный свет обычно покрывает значительную часть окружения, для лучшего распределения качества используются каскадные теневые карты.

Для примера можно взглянуть на атлас теневых карт. Чем больше значимость света, крупнее область на экране или ближе объект к камере, тем крупнее будет выделенный сегмент атласа это необходимо для повышенной детализации. Подобные эвристики оцениваются динамически.



Скорость и предварительный проход обработки глубины


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


Оружие игрока


Статические объекты


Динамические объекты

В предварительном проходе рендерится не только глубина, но и целевой цвет. В динамической геометрии скорость рендерится через векторы движения, то есть положение текущей позиции, вычитаемое из положения пикселя на предыдущем кадре. Так как движение хранится в красном и зеленом каналах 16 битной цели рендеринга с плавающей точкой, нам достаточно знать движение по осям X и Y. В дальнейшем эта информация используется при постобработке для применения размытости и репроекции временного сглаживания. Статичной геометрии векторы движения не нужны, поскольку она двигается только относительно камеры и ее движение может быть высчитано из движения самой камеры. Как можно видеть на скриншоте ниже, движения в нашей сцене не так уж и много.



Z-иерархическая глубина


Следующий шаг это генерация иерархической mip-цепи буфера глубины: эта цепь похожа на mip карту, но вместо усреднения четырех соседних пикселей берет их максимальное значение. Такой подход часто используется в графике для множества задач, например для ускорения отражений и отбрасывания прегражденной геометрии. В нашем случае mip-цепь отбрасывает освещение и декальные текстуры, о которых мы поговорим позднее. В последнее время mip-генерацию проводят за один проход, с записью сразу в несколько mip-ов, но в Doom Eternal запись все еще ведется отдельно для каждого mip.



Декали сеток


Пока что мы не успели познакомиться с какими-либо серьезными отличиями процессов в Doom Eternal по сравнению с Doom 2016, но под эту категорию подходят декали сеток. Это небольшие декали (болты, решетки, выпуклости), которые, как и обычные декали, могут влиять на любые свойства поверхностей (нормаль, неровность, цвет). Однако типичная декаль сетки назначается художниками в ходе разработки сеток и, в отличие от стандартного размещения декалей в окружении, принадлежит своей сетке. Doom и раньше сильно полагался на декали, и текущий переход на декали сеток только повысил детализацию и гибкость графики.

Чтобы добиться такого положительного результата, следующий проход геометрии рендерит идентификаторы каждой декали в восьмибитную текстуру. В дальнейшем, при наложении теней, мы сэмплим текстуру и через идентификаторы получаем связанные с каждым вызовом отрисовки матрицы проекций. Матрица проецирует координаты пикселя из пространства мира в пространство текстур, а затем эти координаты используются для семплирования декали и ее слияния с материалом поверхности. Этот прием невероятно быстр в своем исполнении и открывает художникам широкий простор для работы с множеством декалей. Так как идентификаторы рендерятся в восьмибитную текстуру, потенциально в одной сетке может быть до 255 декалей.

Единственным условием для всего этого является привязка всех декальных текстур к процессам при отрисовке сеток. Благодаря полностью несвязанному процессу рендеринга разработчики могут связывать все декальные текстуры разом и динамически индексировать их в шейдере. Так как разработчики пользуются этим методом при реализации еще нескольких приемов в игре, подробнее о несвязанном процессе рендеринга мы поговорим позже.

Ниже мы можем видеть сетку декальных текстур. Для удобства визуализации идентификаторы покрашены в разные цвета.



Отбрасывание света и декалей


Свет в Doom Eternal полностью динамичен, и одновременно в область обзора может попадать до нескольких сотен источников. Кроме того, как мы уже отметили ранее, декали в игре имеют большое значение, например в том же Doom 2016 число декалей перевалило за тысячи. Все это требует особого подхода к отбрасыванию лишнего, иначе производительность не выдержит тяжести пиксельных шейдров.

В Doom 2016 использовался процессорный вариант кластерного отбрасываения света: свет и декали собирались в конусообрзаные фроксели, которые затем считывались в ходе шейдинга через определения индекса кластера из позиции пикселя. Размер каждого кластера составлял 256 пикселей и для сохранения квадратной формы логарифмически делился на 24 сегмента. Такой прием вскоре переняли многие другие разработчики, и похожие методы встречаются, например, в Detroit: Become Human и Just Cause.

Учитывая рост числа источников динамического освещения (сотни) и декалей (тысячи), процессорной кластеризации отбрасывания освещения в Doom Eternal уже не хватало, так как воксели становились слишком грубыми. В итоге для id Tech 7 разработчики придуман иной подход, и через исполняемые на различных этапах вычислительные шейдеры создали программный растеризатор. Сначала декали и свет связываются в гексаэдр (шестигранник) и передаются в вычислительный растеризатор, откуда вершины проецируются в экранное пространство. Затем второй вычислительный шейдер обрезает треугольники по границам экрана и собирает их в тайлы размером 256 на 256 пикселей. Одновременно с этим по аналогии с кластерным отбрасыванием отдельные элементы источников света и декалей записываются во фроксели, после чего следующий вычислительный шейдер проводит похожую процедуру под тайлы 32 на 32 пикселя. В каждом тайле прошедшие тест на глубину элементы помечаются в битовое поле. Последний вычислительный шейдер переводит битовые поля в список источников света, которые в итоге используются при проходе освещения. Что характерно, индексы элементов все еще записываются в трехмерные фроксели размером 256 на 256 пикселей по аналогии с кластерным подходом. В местах со значительным прерыванием глубины, для определения числа источников света в каждом тайле сравнивается минимальное значение и нового списка источников света, и старого списка кластерных источников.

Если вы не имели дела с традиционной растеризацией, столь насыщенное описание может оказаться для вас непонятным. При желании подробнее погрузиться в вопрос я рекомендую поисследовать общие принципы работы таких процессов, например на Scratchapixel есть очень хороший разбор темы.

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

Преграждение окружающего света в экранном пространстве


Преграждение окружающего света вычисляется в половинном разрешении вполне стандартным путем: сначала 16 случайных лучей исходят из позиции каждого пикселя в полусфере, а затем при помощи буфера глубины определяются пересекающиеся с геометрией лучи. Чем больше лучей пересекает геометрии, тем больше будет преграждение. Эта техника называется преграждение направленного света в экранном пространстве (Screen Space Directional Occlusion), или SSDO, и подробное его описание за авторством Yuriy O`Donnell можно прочитать по ссылке. Вместо традиционного хранения значений преграждения в одноканальной текстуре, направленное преграждение хранится в трехкомпонентной текстуре, а итоговое преграждение определяется через скалярное произведение над нормалью пикселя.

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



Непрозрачный прямой проход


В этом проходе многие элементы наконец встают на свои места. В отличие от Doom 2016, здесь все рендерится напрямую через несколько массивных мегашейдеров. Во всей игре предположительно около 500 процессорных состояний и дюжина макетов дескрипторов. Сначала рендерится оружие игрока, потом динамические объекты, и затем статические. Обратите внимание, что порядок не особо-то и важен, ведь благодаря препроходу глубины мы уже получили буфер глубины, и он может заранее исключать не соответствующие глубине пиксели.


Оружие игрока


Динамические объекты


Первый набор статических объектов


Второй набор статических объектов

У большинства движков AAA-игр графы шейдеров и особенности статических шейдеров позвляют разработчикам возможность креативно подходить к работе со всевозможными материалами и поверхностями, и каждый материал, каждая поверхность ведет к созданию своего собственного уникального шейдера. В результате мы сталкиваемся с невероятным многообразием перестановок шейдеров для всех возможных комбинаций особенностей движка. Однако id Tech сильно отличается от других AAA-проектов: он комбинирует почти все материалы и особенности во всего лишь несколько массивных мегашейдеров. Такой подход позволяет графическим процессам жестче объединять геометрию, что в свою очередь положительно сказывается на производительности. Позже мы еще это обсудим.

Несвязанные ресурсы


Стоит обратить внимание, что весь процесс формирования графики содержит в себе идею несвязанных ресурсов. Это значит, что вместо привязки размытия, отражений, неровности текстуры перед каждым вызовом отрисовки, весь список текстур в сцене привязывается разом. Доступ к текстурам из списка осуществляется в шейдере динамически через переданные шейдеру константами индексы. Таким образом, через любой вызов отрисовки можно получить любую текстуру, что открывает путь множеству оптимизаций, об одной из которых мы сейчас и поговорим.



Динамическое слияние вызовов отрисовки


Поверх архитектуры полностью несвязанных ресурсов все данные геометрии выделяются из одного большого буфера. В этом буфере попросту хранится смещение всей геометрии.

Здесь в игру вступает самая интересная технология idTech 7: динамическое слияние вызовов отрисовки. Она полагается на архитектуру несвязанных ресурсов и обобщенную память вершин, и в итоге значительно уменьшает число вызовов отрисовки и время работы процессора. Еще до начала какого-либо рендеринга вычислительный шейдер динамически создает непрямой буфер индексов для эффективного слияния геометрий из невзаимосвязанных сеток в единый непрямой вызов отрисовки. Без несвязанных ресурсов добиться слияния вызовов не получилось бы, поскольку оно работает с геометриями с несовпадающими свойствами материалов. В дальнейшем воспользоваться динамическим буфером индекса можно будет вновь, как для препрохода глубины, так и для препрохода освещения.

Отражения


Чаще всего для создания отражений экранного пространства вычислительный шейдер использует алгоритм raymarching. Алгоритм испускает из пикселя луч в мировое пространство в сторону отражения, которое зависит от неровности отражающей поверхности. Точно так же дело обстояло в Doom 2016, там как часть прямого прохода записывался небольшой G-буфер. Однако в Doom Eternal уже никакого G-буфера нет, и даже отражения экранного пространства вычисляются не в вычислительном шейдере по отдельности, а сразу в прямом шейдере. Интересно узнать, насколько такое отклонение в пиксельном шейдере влияет на производительность, поскольку создается ощущение, что ценой повышенной нагрузки на регистр разработчики пытались снизить количество целей рендера и как следствие сократить нагрузку на пропускную способность памяти.

Зачастую когда в текстуре экранного пространства нет необходимой информации, у соответствующих эффектов возникают артефакты визуализации. Чаще всего это заметно при отражениях экранного пространства в случаях, когда невидимые отражаемые объекты не могут отражаться. Проблема обычно решается традиционным подходом, через статичные кубические карты отражений в качестве резерва.



Но так как мегатекстуры в Doom Eternal больше не используются, в резервных текстурах тоже нет нужды.

Частицы


Симуляция


В Doom Eternal часть процессорной симуляции частиц ложится на плечи вычислительных шейдерв, поскольку у некоторых систем частиц есть зависимости от информации экранного пространства, например буфера глубины для симуляции столкновений. Тогда как другие системы частиц могут прогоняться в кадре сразу и вычисляться асинхронно, таким симуляциям предварительно необходимы данные препрохода глубины. Что характерно, в отличие от традиционной шейдерной симуляции частиц, здесь симуляция ведется через выполнение последовательности команд из хранящегося в вычислительном шейдере буфера. Каждый поток шейдера прогоняет все команды, среди которых может быть по несколько kill, emit или модификаций параметра частицы. Все это похоже на записанную в шейдере виртуальную машину. Я многое не понимаю в тонкостях работы такой симуляции, но основан подход на презентации Brandon Whitney The Destiny Particle Architecture на Siggraph 2017. Метод в презентации очень похож на описанный мною выше и используется во множестве других игр. Например, я уверен, что похожим образом в Unreal Engine 4 работает система симуляции частиц Niagara.

Освещение


По аналогии с Doom 2016 и описанному на Siggraph 2016 методу, разрешение частиц освещения отделено от собственно разрешения экрана, что дает разработчикам управлять разрешением каждой системы частиц в зависимости от качества, размера экрана и прямого управления. Для низкочастотных эффектов освещение можно предоставить в значительно более низком разрешении почти без потери в качестве по сравнению с, например, искрами, которым требуется высокое разрешение. Освещение и доминирующее направление света хранятся в двух атласах размером 2048 на 2048 пикселей, оба они доступны для каждого прохода благодаря несвязанным ресурсам, как и любая другая текстура. В дальнейшем для рендера частиц простая геометрия отрисовывается через сэмплинг этих атласов.


Увеличенный фрагмент атласа освещения.

Небо и рассеяние


Теперь мы поговорим про объемное освещение. Его генерация состоит из четырех проходов и начинается с создания 3D LUT текстуры для атмосферы неба через raymarching сквозь само небо в сторону источника света.



С первого раза можно не понять, что именно отображает текстура на картинке, но если мы повернем ее на 90 градусов и растянем по горизонтали, все станет ясно: перед нами рассеяние атмосферы. Поскольку оно более вариативно по вертикали чем по горизонтали, то и разрешение по вертикали больше. Атмосфера представлена сферой, поэтому горизонтальное вращение обычно называется долготой, а вертикальное широтой. Атмосферное рассеяние вычисляется полусферой и покрывает 360 градусов долготы и 180 градусов широты верхней части сферы. Для покрытия различных расстояний до наблюдателя в LUT текстуре содержится 32 сегмента глубины, и вместо перевычисления данных неба в каждом кадре процесс распределяется на 32 кадра.



Благодаря LUT текстуре, следующий проход вычисляет рассеяние света на наблюдаемый фроксель по аналогии с кластерным преграждением света в меньшем масштабе. Пронаблюдать несколько сегментов, от ближнего к дальнему, можно ниже.



В третьем проходе данные рассеяния для каждой клетки множатся в каждую последующую клетку в сторону обзора и пишутся в новую 3D текстуру.

В итоге поверх рендеририуемого изображения через сэмплинг только что сгенерированной 3D текстуры на основе глубины пикселей ставится объемное освещение.


До


После

Итоговое видимое небо рендерится на полусфере если попадает в обзор. В этой сцене небо в обзор не попало, но ниже можно взглянуть на пример рендера неба в сцене на открытом воздухе.



Прозрачность


По аналогии с Doom 2016, прозрачность рендерится прямым проходом после непрозрачной геометрии при наличии данных о рассеянии света. Текстура сцены при этом теряет в разрешении (downsamples), а для имитации прозрачности на основе гладкости поверхности подбирается подходящий mip-уровень. Данные о рассеянии света помогают создать изнутри поверхности визуально хорошее рассеяние.

Ниже можно видеть пример mip-цепи текстуры из сцены, где в область обзора попадает больше прозрачных поверхностей.


Для прозрачности теряют в разрешении только имеющие к ней отношение пиксели.

Пользовательский интерфейс


Обычно последним проходом в кадре становится пользовательский интерфейс. Как это обычно и происходит, интерфейс рендерится во вторичную с полным разрешением LDR (восьмибитную) цель рендера, и цвет предварительно умножается на альфа-канал. В ходе тональной компрессии интерфейс накладывается на HDR текстуру. Обычно заставить интерфейс работать с остальным HDR контентом кадра не так-то просто, но в Doom Eternal при тональной компресии интерфейс волшебным образом скалируется и выглядит естественно на фоне прочего 3D контента.



Постобработка


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

Далее идет целевое воздействие: эта RG (двухцветная) текстура форматом 1 на 1 содержит в себе среднее значение освещения всей сцены и вычисляется путем последовательного уменьшения разрешения цветовой текстуры и получения средней освещенности группы пикселей. Чаще всего такой прием используется для имитации привыкания человеческого глаза к резкой смене окружающей яркости. Также средняя освещенность используется при вычислении воздействия в ходе тональной компрессии.



После всего этого вычисляется Bloom. Этого эффекта маловато в нашем примере и широко визуализировать его не получится, но достаточно знать, что вычисление ведется путем получения данных о цвете выше определенного предела и последовательного уменьшения разрешения текстуры для ее размытия.

Затем тональная компрессия объединяет все эффекты. Один-единственный вычислительный шейдер делает следующее:

  • Применяет искажение
  • Рендерит поверх Bloom текстуру
  • Вычисляет виньетирование, грязь на камере, хроматическую аберрацию, отблеск от линз и множество других эффектов
  • Получает значение воздействия на основе средней освещенности
  • Дает тональной компрессии распределить HDR цвета по корректным диапазонам как для LDR, так и для HDR через кастомный оператор тональной компрессии.

И наконец, сверху накладывается интерфейс.

Рендер текстуры искажений проводится еще до прохода постобработки: геометрия типа марева огня от эффектов частиц рендерится в новую цель рендера форматом с четверть исходного разрешения. В этом рендере данные искажений хранятся в красном и зеленом каналах, а преграждение в синем. Полученные данные применяются при искажении изображения на шаге тональной компрессии.



Заключение


Наш поверхностный разбор одного кадра Doom Eternal подошел к концу, хоть я и уверен, что не затронул несколько влияющих на внешний вид игры моментов. На мой взгляд, Doom Eternal это невероятный успех в техническом плане, и в будущем id Software сможет еще больше повысить планку. Команда разработчиков успешно продемонстрировала нам, как разумный подход и эффективное планирование помогли создать высококачественную игру, и я верю, что это отличный пример для подражания, равно как и обучающий материал. С нетерпением жду будущих разработок id Software.

Rip and tear, until it is done.

Справочные материалы


Подробнее..

Самые креативные капчи DOOM, приседания, ползунки, резисторы, матан

26.05.2021 02:06:39 | Автор: admin
Своими действиями или бездействием нанесите вред человеку, чтобы доказать, что вы не робот.
капча по Азимову

Капча с DOOM уже несколько дней одна из самых обсуждаемых тем на Reddit и HackerNews. А какие еще бывают креативные капчи?

Doom Captcha


image


Шуточная капча, в которой пользователю необходимо сыграть в мини-версию Doom для доказательства того, что он не робот. Её создал программист Мигель Ортеза, а ознакомиться с ней можно на GitHub.

IDDQD тоже работает.


Squat Captcha




Самая отвратительная капча на свете. Заставляет сделать 10 приседаний, защищая вас от спонтанных покупок. чтобы продолжить. Работает с Chrome/Firefox на десктопе, требуется веб-камера.

Подробности тут.

Motion Captcha


image

Вам нужно нарисовать предложенную фигуру.

GitHub

They Make Apps


image

Специальный слайдер, который нужно немного покрутить, чтобы заполнить форму. Задача звучит примерно так: Докажите что вы человек; Расположите курсор в конце линии, чтобы зарегистрироваться на нашем сайте.

Resistor CAPTCHA


image

Матановая капча


image



Chesscaptcha

(по наводке grayfolk)

image

Нужно либо расположить фигурки правильно, либо сделать мат в один ход.

Подробнее тут.

Ёще идеи для капчи


image

Капча на возраст.

image

Игровые капчи.

image

Капчи, в которых надо перетаскивать предметы.

Подробнее..

Играем в DOOM на тесте на беременность. Что? Да

07.09.2020 18:22:46 | Автор: admin
А также на микроволновке, валидаторе билетов и многом другом.



Ко флэшмобу по переносу DOOM на любые устройства, у которых только есть дисплей, теперь добавился еще и тест на беременность.

Программист-энтузиаст и адепт реверс-инжиниринга Foone сумел запустить полнофункциональную игру DOOM на электронном тесте на беременность, а до этого то же самое проделал с The Elder Scrolls: Skyrim. Первое видео классического шутера на миниатюрном экране устройства было показано им на выходных в личном микроблоге в Twitter. Тогда он признался, что на самом деле просто проигрывал видео, но с тех пор поднял ставки и нашел способ действительно сыграть в DOOM на электронном тесте при помощи беспроводной клавиатуры.


Как такое возможно, спросите вы? Конечно, не обошлось без читерства.

В своем треде Foone ссылался на исследования другого юзера Twitter, Xtoff, в прошлом месяце решившем проверить, что кроется за корпусом теста на беременность компании ClearBlue. Foone использовал другой бренд, Equate, но обнаружил в нем похожую начинку: бумажную полоску, батарею CR1616 3V, восьмибитный микроконтроллер Holtek HT48C06 (64 байта RAM, работает на частоте 4 МГц либо 8 МГц) и оперативная память.

image

По словам Foone, этот наполнение этого теста на беременность быстрее в вычислении базовых математических операций и I/O, чем IBM PC.

Тест активируется при намокании бумажной полоски, обработанной химическим реагентом. После этого на LCD-дисплей выводится надпись о положительном либо отрицательном результате.

Чтобы полноценно запустить игру, ему пришлось заменить дисплей и микроконтроллер. Оригинальный дисплей мог показывать только четыре возможных экрана, а центральный процессор нельзя было перепрограммировать, поэтому пришлось пойти на дополнительные ухищрения. Кроме того, тест был подключен к ПК в качестве источника питания, при помощи него же запускалась игра. Конечно, даже после замены дисплей остался монохромным с разрешением 128х32 пикселя, так что играть на нем оказалось не очень-то удобно. Но само по себе отображение DOOM на дисплее теста на беременность уже забавно и достойно восхищения.

image

Началом истории Will it run DOOM? можно считать 2016 год, когда DOOM был запущен на миниатюрном Game Boy, помещенном в брелке для ключей. Времени с тех пор прошло прилично, и теперь уже кажется, что легендарный шутер можно запустить практически на чем угодно. Чтобы доказать это, ниже приведем список (не полный!), на каких устройствах это удалось.




Принтер Canon Proxima


Система безопасности линейки принтеров Canon оказалась не самой стойкой, и через софт для обновлений удалось добиться того, чтобы игра запускалась на дисплее устройства.



Осциллограф


Сам по себе осциллограф использует операционную систему Windows 95, поэтому, по словам пользователя Zekinn, впервые запустившего DOOM на таковом, дальше дело оставалось за малых отбросить лишние предубеждения во имя улучшения человеческого опыта.



Банкомат


Обычно банкомат используют для снятия или внесения денег, но энтузиасты решили вместо этого запустить на нем DOOM. С предустановленной на таких автоматах операционной системой Windows XP дальнейшие действия по установке игры упирались лишь в вопрос желания.



DOOM в DOOM


Почему бы не поиграть в DOOM, пока ты играешь в DOOM? При помощи мода можно запустить игру внутри игры на игровом автомате, являющемся объектом окружения на одном из уровней.



Пианино


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



Minecraft


Пару месяцев назад появился мод для Minecraft, при помощи которого можно использовать внутриигровые компьютеры в качестве настоящих виртуальных машин на которых, конечно же, со временем оказалось возможным запустить Doom.



Валидатор билетов




Микроволновка


image


iPod Mini


image

Больше примеров в треде на Reddit.
Подробнее..

Weapon wheel в Doom 1993

29.06.2020 18:10:05 | Автор: admin
Приветствую.
Многие из нас с теплотой относятся к олдскульным видеоиграм, вышедшим на стыке веков. У них превосходная атмосфера, бешеная динамика и множество оригинальных решений, которые не устарели спустя десятилетия. Однако в наши дни видение интерфейса игр несколько изменилось на смену запутанным уровням пришли линейные коридоры, на смену аптечкам регенерация, а вместо длинного ряда клавиш 0-9 для выбора арсенала пришли сначала колесико мыши, а затем виртуальное колесо. Именно о нем сегодня и пойдет речь.
image


Историческая сводка
Раньше, во время появления жанра шутеров как таковых, вопрос об управлении мышкой не стоял для управления протагонистом использовалась только клавиатура. Причем единого формата управления тоже не было WASD стал стандартом чуть позднее. Более подробно о старых игровых раскладках клавиатуры можно почитать вот тут
Соответственно, в тех играх, где была реализована возможность выбора снаряжения (Doom, Wolfenstein, Quake etc) был реализован единственным интуитивным на тот момент способом с помощью цифровых клавиш на клавиатуре. И на многие годы этот способ был единственным.
Потом, в конце 90х годов, появилась возможность смены вооружения колесиком мышки. Однозначной информации на эту тему найти не удалось, однако в CS 1.6 такая возможность включалась через консоль. Впрочем, возможно такие прецеденты были и ранее в таком случае, просьба указать на это в комментариях или в ЛС. А вот в привычном в наше время виде Weapon Wheel вошло в использование лишь с Crysis'ом и его Suit menu, Хотя попытки сделать нечто похожее были начиная с HL2, в массы колесо пошло лишь в конце 00х годов, а сейчас является мейнстримом.

Впрочем, это лишь историческая сводка, представляющая интерес только в качестве истории. В рамках данной статьи не будет пространных рассуждений о причинах популярности того или иного решения. а так же вкусовщины о том, какой селектор лучше. Просто потому, что ниже будет описан процесс адаптации старого-доброго Doom к выбору орудия с помощью мышки.

Постановка задач
Для того, что бы реализовать WW, нужно каким-либо образом перехватывать движения мышки, отслеживать ее перемещение, пока зажата клавиша селектора, и, по отпусканию, эмулировать нажатие на кнопку, соответствующую выбранному сектору.
Для этого мной был использован язык Java, в частности, перехват клавиш осуществляется за счет библиотеки jnativehook, а нажатие за счет awt.Robot. Обработка полученных хуков не представляет сложностей, поэтому производится вручную.

Реализация
Предварительно были разработаны классы, задающие пары координат, для определния вектора смещения.
В частности, класс Shift позволяет хранить двумерный вектор, а также определять его длину, а класс NormalisedShift, разработанный для хранения нормализованного вектора, помимо прочего, позволяет определить угол между перехваченным вектором и вектором (1,0)
Заголовок спойлера
class Shift{    int xShift;    int yShift;    public int getxShift() {        return xShift;    }    public int getyShift() {        return yShift;    }    public void setxShift(int xShift) {        this.xShift = xShift;    }    public void setyShift(int yShift) {        this.yShift = yShift;    }    double getLenght(){        return Math.sqrt(xShift*xShift+yShift*yShift);    }}class NormalisedShift{  double normalizedXShift;  double normalizedYShift;  double angle;  NormalisedShift (Shift shift){      if (shift.getLenght()>0)      {          normalizedXShift = -shift.getxShift()/shift.getLenght();        normalizedYShift = -shift.getyShift()/shift.getLenght();      }      else      {          normalizedXShift = 0;          normalizedYShift = 0;      }  }  void calcAngle(){      angle = Math.acos(normalizedXShift);  }  double getAngle(){      calcAngle();      return (normalizedYShift<0?angle*360/2/Math.PI:360-angle*360/2/Math.PI);    };};


Особого интереса они не представляют, и комментарий требуют только строки 73-74, нормализующие вектор. Помимо всего прочего, вектор переворачивается. у нег меняется система отсчета дело в том, что с точки зрения программного обеспечения и с точки зрения привычной математики вектора традиционно направляют по разному. Именно поэтому вектора класса Shift имеют начало координат слева сверху, а класса NormalizedShift слева снизу.

Для реализации работы программы был реализован класс Wheel, реализующий интерфейсы NativeMouseMotionListener и NativeKeyListener. Код под спойлером
Заголовок спойлера
public class Wheel  implements NativeMouseMotionListener, NativeKeyListener {    final int KEYCODE = 15;    Shift prev = new Shift();    Shift current = new Shift();    ButtomMatcher mathcer = new ButtomMatcher();    boolean wasPressed = false;    @Override    public void nativeMouseMoved(NativeMouseEvent nativeMouseEvent) {        current.setxShift(nativeMouseEvent.getX());        current.setyShift(nativeMouseEvent.getY());    }    @Override    public void nativeMouseDragged(NativeMouseEvent nativeMouseEvent) {    }    @Override    public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {    }    @Override    public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {        if (nativeKeyEvent.getKeyCode()==KEYCODE){            if (!wasPressed)            {                prev.setxShift(current.getxShift());                prev.setyShift(current.getyShift());            }            wasPressed = true;        }    }    @Override    public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {        if (nativeKeyEvent.getKeyCode() == KEYCODE){            Shift shift = new Shift();            shift.setxShift(prev.getxShift() - current.getxShift());            shift.setyShift(prev.getyShift() - current.getyShift());            NormalisedShift normalisedShift = new NormalisedShift(shift);            mathcer.pressKey(mathcer.getCodeByAngle(normalisedShift.getAngle()));            wasPressed = false;        }    }


Разберемся, что тут происходит.
В переменной KEYCODE хранится код клавиши, служащей для вызова селектора. Обычно это TAB, но при необходимости, его можно изменить в коде или в идеале подтянуть из файла конфига.
prev хранит положение курсора мыши, которое было на момент вызова селектора. В сurrent поддерживается актуальное положение курсора в настоящий момент времени. Соответственно, при отпускании клавиши селектора происходит вычитание векторов и в переменную shift записывается смещение курсора за время удержания клавиши селектора.
Затем, в строке 140, вектор нормализуется, т.е. приводится к виду, когда его длина близка к единице. После чего, нормализованный вектор передается в матчер, который устанавливает соответствие между кодом клавиши, которую нужно нажать и углом проворота вектора. Из соображений читаемости, угол переводится в градусы, а так же ориентируется по полному единичному кругу (acos работает только с углами до 180 градусов).
В классе ButtonMatcher определяется соответствие между углом и выбранным кодом клавиши.
Заголовок спойлера
class ButtomMatcher{    Robot robot;    final int numberOfButtons = 6;    int buttonSection = 360/numberOfButtons;    int baseShift = 90-buttonSection/2;    ArrayList<Integer> codes = new ArrayList<>();    void matchButtons(){        for (int i =49; i<55; i++)            codes.add(i);    }    int getCodeByAngle(double angle){        angle= (angle+360-baseShift)%360;        int section = (int) angle/buttonSection;        System.out.println(codes.get(section));        return codes.get(section);    }    ButtomMatcher() {        matchButtons();        try        {            robot = new Robot();        }        catch (AWTException e) {            e.printStackTrace();        }    }    void pressKey(int keyPress)    {        robot.keyPress(keyPress);        robot.keyRelease(keyPress);    }}


Кроме того, переменная numberOfButtons определяет количество сектором и соответствующих им кнопок, baseShift задает угол поворота (В частности, обеспечивает симметрию относительно вертикальной оси и проворот колеса на 90 градусов так, что бы орудие ближнего боя было сверху), а массив codes хранит в себе коды клавиш на случай, если кнопки будут изменены, и коды не будут идти подряд. В более доработанной версии можно было бы подтягивать их из конфигурационного файла, но при стандартном расположении клавиш текущая версия вполне жизнеспособна.

Заключение
В рамках данной статьи была описана возможность кастомизации интерфейса классических шутеров для современных стандартов. Конечно, ни аптечек, ни линейности мы тут не добавляем для этого есть множество модов, но зачастую именно в подобных деталях и кроется дружелюбный и удобный интерфейс. Автор осознает, что, вероятно, описал не самый оптимальный способ достижения требуемого результата, а так же ждет в комментариях картинку с буханкой и троллейбусом, но тем не менее это был интересный опыт, который, возможно, сподвигнет какого-нибудь геймера открыть для себя удивительный мир Java.
Конструктивная критика приветствуется.
Исходники
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru