libVLC и видеотекстуры

Я давно искал удобное решение для воспроизведения видео в играх, и вот, наконец, оно найдено — libVLC, библиотека, на которой основан всеми любимый плеер VLC. В отличие от FFMPEG, это высокоуровневый API, который позволяет буквально в несколько строк добавить видеоплеер в любое окно по HWND (или X window ID под Linux). Но самое интересное, конечно, декодировать видео во внеэкранный буфер, чтобы потом передать его в OpenGL-текстуру — libVLC это также позволяет с минимальными телодвижениями. Я уже добавил в репозиторий Dagon экспериментальное расширение dagon:video, которое предоставляет класс Video, обертку над libvlc_media_t* и libvlc_media_player_t*. Использовать его очень просто — сначала создается VideoManager, общий для всего приложения (лучше хранить его в классе, наследующем от Game):

VideoManager videoManager = New!VideoManager(this);

Затем, уже в сцене, создается источник видео:

Video video = New!Video(videoManager, 1920, 1080, assetManager);
video.open("media/video.mp4");

auto videoMaterial = addMaterial();
videoMaterial.baseColorTexture = video.texture;
videoMaterial.alphaTestThreshold = 0.0f;

video.play();

Можно использовать видеотекстуру для любых целей — показать заставку в начале игры, натянув на screen-aligned плоскость, или применить к мешу для создания внутриигрового экрана. Можно даже воспроизводить 360-градусные ролики в равнопромежуточной проекции для отображения на панорамном фоне.

Dagon 0.24.0

Выпустил новую версию движка. Основное нововведение — рендеринг текста теперь является частью ядра движка в качестве модуля dagon.graphics.font (таким образом, FreeType становится зависимостью ядра). Расширение dagon:ftfont удалено. В связи с этим я решил добавить в Dagon встроенный простой GUI. В данный момент поддерживаются перетаскиваемые окна, вывод многострочного текста (TextView), строка ввода (TextInput) и консоль (Console). GUI-тулкит встроен в объект Scene — доступен как свойство Scene.ui.

Добавлен менеджер шрифтов (Application.fontManager). Он загружает шрифты по умолчанию (Liberation Sans и Liberation Mono, которые прилагаются к движку) и позволяет создавать новые.
Добавлена базовая поддержка библиотеки Assimp (расширение dagon:assimp). Поддерживаются меши, узлы и некоторые свойства материалов (диффузный цвет, текстуры).

Также реализована возможность изменить системный курсор мыши методом Application.setCursor. Добавлена поддержка логирования в буфер.

Ретроспектива: Bud, DSSS и другие старые системы сборки

До появления DUB в 2012 году в сообществе D-шников не было единой системы автоматизации сборки. Все использовали что-то свое — от классического Make до кастомных скриптов. Расскажу о нескольких таких проектах, которые застал лично.

Build/Bud

Согласно deadalnix, первой известной системой сборки для D была программа Build (впоследствии переименованная в Bud), которую написал Дерек Парнелл в середине нулевых. Является ли название DUB отсылкой к этому проекту, я точно сказать не могу, но вероятность такая есть) В 2011 году Bud 3 была портирована на D2, ее даже можно скачать по ссылке ниже.

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

Страница Build/Bud на DSource: https://dsource.org/projects/build

D Shared Software System (DSSS)

Довольно мощный сборщик, написанный Грегором Ричардсом в качестве альтернативы Build. DSSS, строго говоря, был комплектом из сборщика Rebuild и пакетного менеджера DSSS. Для конфигурации он использовал файлы *.conf, концептуально тоже близкие к INI — наборы пар «ключ=значение». Интересной фичей DSSS была поддержка нескольких таргетов в одном конфиге: конфиг мог быть поделен на секции, каждая из которых описывала сборку отдельного таргета, ассоциированного с указанным главным модулем:

[example.d]
target=example_binary

Можно было собирать один и тот же модуль в разных конфигурациях:

[example.d+withfeature]
target=example_binary_withfeature
buildflags=-version=withfeature

Также поддерживались хуки (prebuild, postbuild, preinstall, postinstall и др.), которые впоследствии были реализованы и в DUB. Другая концепция родом из DSSS — возможность установки собранного ПО в систему, аналогично dub fetch/dub run.

DSSS был написан на D1, поэтому я его не использовал — на D1 я никогда не писал, сразу начал с D2, так как было жутко неудобно ставить обе версии языка сразу. Позже появился проект по портированию DSSS на D2, но я с какого-то момента перестал за ним следить.

Страница оригинального DSSS на DSource: https://dsource.org/projects/dsss

Форк для D2 на GitHub: https://github.com/apriori/dsss

xfBuild

Тоже проект родом из нулевых, система сборки от легендарного h3r3tic. Одно время была перспективной альтернативой DSSS, но, к сожалению, тоже исключительно под D1, да еще под Tango — поэтому подробно о ней ничего сказать не могу, я ей никогда не пользовался.

Существует порт xfBuild на D2: https://github.com/SiegeLord/xfbuild

Cook

Был период в истории D, когда все пользователи поделились на два лагеря — сторонники Bud и любители DSSS, и эта ситуация создавала серьезные сложности для мейнтейнеров публичных проектов. Затем Парнелл и Ричардс пропали из сообщества, обе системы утратили активную поддержку, и вновь наступили «темные века». Именно в это время я открыл для себя D. Отсутствие сборщика под D2 побудило меня написать сначала простой скрипт для запуска через RDMD, а потом полноценную программу под названием Cook.

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

В Cook я тоже использовал собственный формат conf-файлов — JSON в те времена был еще не так популярен, да и не очень он удобен для конфигов, если честно. Cook могла работать без конфига, если проект не использует специфичные настройки тулчейна. В конфиге можно было указать пути к компилятору и линкеру, указать флаги (cflags, lflags), переопределить команды компиляции и линковки и многое другое — громадным преимуществом Cook была возможность запускать разные версии компилятора. Вот как выглядел мой типичный кроссплатформенный конфиг:

windows.compiler_dir: "C:/dlang/dmd-2.067/windows";
linux.compiler_dir: "/opt/dlang/dmd-2.067/linux";
linux.compiler: "%compiler_dir%/bin32/dmd";
linux.linker: "%compiler_dir%/bin32/dmd";
windows.compiler: "%compiler_dir%/bin/dmd";
windows.linker: "%compiler_dir%/bin/dmd";
windows.obj.path: "o_windows";
linux.obj.path: "o_linux";

Формат конфигов представлял собой, по сути, мини-DSL с поддержкой любых кастомных переменных (не такой мощный, как в CMake, но все же получше, чем INI). Для переменных поддерживались платформ-префиксы. Подобно другим старым сборщикам, Cook поддерживала мультитаргетные проекты. Еще она могла загружать зависимости из git-репозиториев и подключать к проекту зависимости, хранящиеся локально. Ее можно было даже настроить для кросс-компиляции windows-программ под Linux при помощи Wine!

Получилась до того удобная штука, что я использовал Cook аж до конца десятых, предпочитая ее DUB, и прекратил разработку лишь в 2019 году, когда уже стало ясно, что DUB теперь полноправный стандарт. Cook прекрасно работает до сих пор — можете скачать, собрать и протестировать: https://github.com/gecko0307/cook2. Есть определенные задачи, где она, имхо, подходит лучше, чем DUB — например, при сборке в экзотические целевые платформы с использованием LDC.

Не могу сказать, что я доволен DUB на 100%, есть задачи, которые он не покрывает (впрочем, как и альтернативные сборщики). Например, до сих пор нет инструментария для автоматической интеграции в проект библиотек, написанных на других языках. Но эта проблема, судя по всему, в общем случае нерешаема.

Можно ли программировать при помощи нейросетей?

Мне, как мейнтейнеру значимых OpenSource-проектов, Microsoft предоставил бесплатный доступ к Copilot. Пользуюсь им теперь регулярно, как и ChatGPT, но в зависимость, как многие, не впадаю. Могу сказать вот что. В среднем нейронки хорошо справляются с чем-то типовым — например, запросами вроде «напиши функцию-хелпер на JS, которая принимает на вход массив и делает то-то и то-то». То есть, алгоритмами, которые я бы и сам написал, но иногда тупо лень это делать. Ситуация становится резко сложнее с программами, которые должны работать с определенными интерфейсами, библиотеками, API. Чем экзотичнее библиотека, тем хуже результат. Зачастую ИИ просто не знает, как выглядит та или иная функция API, и пытается угадать. Попадает пальцем в небо очень часто, хотя концептуально его решения могут быть правильными. Бывает, что код достаточно легко подчистить от ляпов, а бывает, что он в принципе не может работать.

Абсолютно ничего не выходит, если в проекте много матана и всяких строгих теорем. В узкоспециализированных научных областях ChatGPT почти не разбирается. Также он плохо помогает в задачах оптимизации, отладки сложных систем, в вопросах архитектуры. Copilot, который вроде бы имеет доступ к полноценному контексту, тоже больше угадывает, чем реально что-то анализирует. А это и есть самое сложное в программировании — выбрать для проекта подходящую архитектуру, грамотно спроектировать модели данных, обезопаситься от побочных эффектов. Не все люди-то это умеют.

Нейронки хорошо справляются с рутинными задачами. Переписать код с одного языка на другой, переименовать множество функций по заданному шаблону, сгенерировать какие-то однотипные конструкции — все, что не требует интеллекта вообще. Но для такого они не особо нужны, это решается несложной автоматикой: текстовыми фильтрами, регулярными выражениями и т.д. Чем меньше в проекте рутины и чем больше инженерного творчества, тем сложнее заменить живого автора. Утверждение, что якобы с нейронками даже непрограммисты могут создавать полноценные продукты — это миф. Как в рассказе Роберта Шекли «Верный вопрос»: Абсолютный Ответчик помогает только тем, кто правильно формулирует вопросы — а для этого, как резюмирует писатель, нужно знать большую часть ответа.

Встроенный UI в Dagon

Наконец-то дошли руки начать пилить свой уютный UI-тулкит! Убийцу ImGui я, конечно, не планирую — это будет набор базовых виджетов для добавления в игру простых отладочных инструментов. Уже готовы окна и TextView — на скриншоте ниже демонстрируется вывод лога приложения.

Об администрировании сервера

Сегодня, в эпоху облачных провайдеров и SaaS, управлять сервером самостоятельно — крайне нишевое занятие. Для этого нужно очень хорошо разбираться в Linux и его многочисленных системных компонентах, которые, к тому же, постоянно обновляются и меняются, из-за чего знания и опыт быстро устаревают. Я много лет пользуюсь Linux, но и то не знаю всех тонкостей, постоянно учусь. Сама система принципиально несложная и понятная, но в ней постоянно появляется что-то новое, какие-то дополнительные слои абстракции. Linux многолик, его можно использовать в самых разных контекстах — отсюда и сложность. Ну и, конечно, знать систему на уровне десктопа — это одно, а админить по SSH — слегка другое.

(далее…)

C3: неужели наконец-то нормальный C-подобный язык?

Современная замена C — это то, чего очень не хватает всем, кто пишет что-то для десктопа с нуля, не полагаясь на популярнные платформы типа веба, Java/.Net, скриптовых языков и т.д. До настоящего времени реального аналога C, можно сказать, не было вовсе (исключением является BetterC-режим D, но это все-таки не отдельный язык, а подмножество, и он не для «зашедших с улицы», а совсем наоборот — для самых опытных программистов на D). Go и Rust не предлагать! 🤣

И вот, захожу я как-то раз в ньюсгруппу d.D и вижу ссылку на C3: https://c3-lang.org. «Продать» мне что-то новомодное сложно, но язык зацепил! Я тестировал еще не все фичи, но вот что понравилось практически сразу:

  • Компилятор довольно легко установить. Вначале, правда, выкачивается майкрософтовский SDK, но после этого компилятор становится самостоятельным и Студию не требует;
  • Не нужна отдельная система сборки, компилятор сам выполняет ее функции;
  • Простая и вменяемая система типов, полностью совместимая с C без костылей;
  • Векторы, матрицы и кватернионы в стандартной библиотеке — бесценно! Сразу уйма работы отпадает при написании игрового движка;
  • Вообще, в целом, читаемая и красивая стандартная либа. Открываешь любой модуль, и все понятно;
  • Управление памятью, чем-то похожее на то, что я делаю в dlib (аллокаторы, универсальный new). Сборщика мусора нет;
  • Красивая обработка ошибок со встроенным Optional и проверкой на null на уровне синтаксиса. Optional — это просто модификатор типа: если его использовать, то компилятор тупо не дает прочитать такую переменную без предварительного null-чекинга. Можно его делать самостоятельно, а можно просто использовать оператор «!!», тогда компилятор вставит проверку сам. Все выглядит очень просто и читаемо.

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

Обновления

Dagon 0.23.1

Небольшое обновление Dagon 0.23. Добавлена поддержка переключения отдельных рендер-таргетов в PassTerrain — то есть, для материалов ландшафта работают свойства outputColor, outputNormal и др. Движок теперь использует dlib 1.3.2. Добавлены новые уроки — 9, 13 и 15.

К следующему релизу готовлю поддержку Assimp, о которой уже писал, а также перенос расширения dagon:ftfont в ядро движка, что позволит реализовать различные встроенные UI-компоненты.

BindbC-Assimp

Биндинг к Assimp 5 размещен на GitHub и доступен в качестве DUB-пакета. К сожалению, название bindbc-assimp занято заброшенным и несуществующим ныне проектом, поэтому пришлось зарегать как bindbc-assimp5.

Оптимизация блога

Я настроил объектный кэш на основе Redis, а также добавил HTML-кэш и заголовок Cache-Control, что заметно ускорило загрузку страниц блога. Было исправлено множество мелких проблем верстки. Все архивные игры теперь размещены на одном сервере с блогом.

Тема, которую я использую, теперь также доступна на GitHub. Это форк WPEX Blogger 1.2 от WPExplorer, в котором я внес исправления для совместимости с PHP 8, исправил поиск в мобильном меню, добавил новые переводы на русский, а также внес патч, исправляющий совместимость с плагином Code Syntax Block.

Поддержка Assimp

Библиотека Assimp — относительно тяжеловесное, но самое функциональное решение для загрузки 3D-моделей. У меня наконец-то дошли руки добавить поддержку Assimp 5+ в Dagon как расширение dagon:assimp, благодаря чему можно будет использовать в движке модели форматов FBX, Collada, 3DS и многих других. Пока загружаются только меши, но в планах добавить поддержку материалов, узлов и анимации.

Перед загрузкой модели можно задать кастомные флаги постпроцессора:

AssimpAsset aModel;

override void beforeLoad()
{
    aModel = this.addAssimpAsset("assets/cacodemon.fbx");
    aModel.loaderOption =
        aiPostProcessSteps.Triangulate | aiPostProcessSteps.FlipUVs;
}

После этого можно использовать меши из массива aModel.meshes для рендеринга:

auto e = addEntity();
e.drawable = aModel.meshes[0];