Атомная бомба для C++ кодера. Обзор набора фреймворков массового поражения

Если коротко, openFrameworks — это проект с открытым исходным кодом на C++, представляющий собой набор укомплектованных инструментов (фреймворков) для разработки кросс-платформенных приложений, не только настольных, но и мобильных. Распространяется по лицензии MIT.

Разработка кросс-платформенных приложений еще никогда не была настолько простой. Да, мы уже знакомы с подобными либами (навскидку: Qt, Cinder), однако openFrameworks идет еще дальше, предоставляя программисту более широкие возможности.

openFrameworks is detected

С самого начала openFrameworks создавался как инструмент для разработки технических приложений, требующих звукового и графического сопровождения. В настоящее время в состав входят модули для интеграции с Kinect и Andruino, с системой машинного зрения, с сенсором Leap Motion (пока не включен в релизную ветку) и другие.

Созданный в соответствии с философией DIWO (сделай это с помощью других), openFrameworks представляет собой не просто набор разрозненных фреймворков, а стройную библиотеку для языка C++, позволяющую одинаковым образом работать с разными фреймворками. То есть openFrameworks — это своего рода прослойка между пользовательским кодом и компонентами, связывающая их и предоставляющая программисту лаконичный интерфейс для работы с ними.

OpenFrameworks включает следующие библиотеки:

  • для работы с графикой: OpenGL, GLEW, GLUT, libtess2, cairo;
  • для работы с аудио, в том числе для анализа звука: rtAudio, PortAudio, OpenAL, Kiss FFT, FMOD;
  • для шрифтов: FreeType;
  • загрузка и сохранение изображений в различных форматах обеспечивается либой FreeImage;
  • проигрывание и захват видео происходит посредством библиотек: QuickTime, GStreamer и videoInput;
  • либа Poco включает средства для сетевого взаимодействия, другими словами — служит для разработки десктопных, мобильных приложений и приложений для встраиваемых систем, работающих по сети, как клиентских, так и серверных;
  • OpenCV — библиотека для компьютерного зрения;
  • для загрузки 3D-моделей используется библиотека Assimp.

Вдобавок openFrameworks легко расширяется с помощью аддонов. Они позволяют добавлять новую функциональность, реализованную в сторонних фреймворках; ядерная функциональность (межкомпонентный интерфейс) также может быть модифицирована. Сторонние аддоны имеют префикс ofx, ядерные функции — of.

Втыкаем openFrameworks

Содержимое папки с openFrameworks включает следующие поддиректории:

  • addons — содержит все расширения;
  • apps — директория для твоих проектов;
  • examples — содержит примеры работы с openFrameworks;
  • каталог libs — содержит ядерную функциональность либы в целом.

Посмотрим на расширения — в папку addons:

  • ofxAssimpModelLoader — позволяет загрузить в приложение 3D-модели широкого ряда форматов, модуль основан на библиотеке Assimp;
  • ofxEmscripten — служит для экспорта десктопных приложений для выполнения в вебе;
  • ofxGui — как и следует из названия, предназначена для создания пользовательского интерфейса;
  • ofxAndroid — реализует интерфейс программирования под Android на языке C++;
  • ofxiOS — реализует поддержку программирования мобильных устройств на базе iOS (на языке C++);
  • ofxAccelerometer — позволяет работать с акселерометром на любом поддерживаемом мобильном девайсе;
  • ofxThreadedImageLoader — загружает в приложение изображения в независимом потоке;
  • ofxSVG — предоставляет загрузчик для векторного формата SVG;
  • ofxXmlSettings — сохраняет и/или загружает данные в XML-формате;
  • ofxKinect — реализует поддержку программирования сенсора Kinect первой версии;
  • ofxNetwork — создает сетевые TCP- и UDP-соединения и управляет ими;
  • ofxOpenCv — предоставляет поддержку компьютерного зрения из библиотеки OpenCV.

INFO


Сторонних модулей очень много. Среди них имеются: поддержка сенсорного контроллера Leap Motion, инструменты слежения и распознавания лиц с помощью Kinect, поддержка физики, внешних инструментов (к примеру, таких, как Spine для создания двумерной анимации или свободный инструмент трехмерного моделирования Blender), различных систем GUI (например, GTK), скриптовых языков (JavaScript, Lua) и многое, многое другое.

Среди ядерных функций присутствует поддержка создания оконных приложений (для разных операционных систем), двумерной и трехмерной графики, коммуникации по последовательному кабелю (Andruino, Raspberry Pi), мощный математический аппарат (векторная, матричная алгебра), программирование звука, работа с видео, в том числе чтение «сырых» данных с камеры, различные типы данных и утилиты для работы с ними.

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

Установка и создание проекта

OpenFrameworks позволяет разрабатывать приложения под Windows, OS X, iOS, Linux, Android не только для архитектуры x86, но также для ARM. openFrameworks уже обзавелся поддержкой Windows 10 вместе с Visual Studio 2015 — с этим тандемом мы первым делом и проведем тестирование. Кроме VS 2015, openFrameworks под Windows можно заточить под среду Qt Creator.

Для начала из раздела Download сайта скачай последнюю версию либы (на момент написания статьи была 0.9.0), также можно стянуть с GitHub’а. Разработчики обращают внимание: новая версия не на 100% совместима с предыдущей, следовательно, старые проекты без переделывания не будут работать в новой версии либы. Распакуй скачанный ZIP-архив. Для создания проекта у тебя есть два варианта.

Первый: запусти из папки с либой ProjectGenerator.exe:

Project Generator

С его помощью можно создать новый проект с поддержкой любого доступного фреймворка. Обрати внимание, создаваемый проект должен находиться в одной папке с openFrameworks, в подкаталоге не глубже двух уровней вложенности. После создания проекта его можно открыть в Visual Studio 2015.

Этот вариант хорош, но есть способ лучше. Создавать проекты удобнее прямо из Студии, для этого надо установить расширение: Tools -> Extension and Updates, в открывшемся окне для поиска набираем название либы, спокойно ставим расширение. После этого в заготовках для проектов появится пункт openFrameworks. После выбора и создания проекта данного типа (обрати внимание, чтобы вложенность папки с твоим проектом не была глубже двух уровней, начиная от каталога с либой) появится окно выбора подключаемых фреймворков. Любой из них можно добавить позже — в процессе работы над проектом.

INFO


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

Графический вывод

Для начала не будем подключать дополнительные фреймворки, а ограничимся ядерными, поэтому щелкнем на ОK. В результате будет создано решение, содержащее два проекта: собственно твой проект и второй — с необходимыми для компиляции первого файлами ядерных фреймворков. Любое приложение, построенное на основе openFrameworks, рендерит графику средствами OpenGL. Твой проект состоит из трех файлов: main.cpp — здесь происходит запуск основного устройства библиотеки (инициализация OpenGL для вывода) + запуск нашего кода.

Окно создается с помощью функции ofSetupOpenGL, она принимает три параметра: ширина, высота, режим окна (полноэкранный, оконный, режим игры); детально параметры окна и подсистемы OpenGL можно настроить с помощью объекта ofGLWindowSettings; запускается приложение командой ofRunApp, получающей экземпляр приложения — объект класса ofBaseApp. Во втором файле — ofApp.h — находится объявление класса приложения, ofApp.cpp — код реализации класса — все как обычно. В заголовочном файле присутствуют объявления ряда обработчиков системных событий. В файле реализации есть их определения. Для корректной работы приложения вовсе не обязательно заполнять их все, они выведены чисто для удобства. Если скомпилить и запустить проект, будет выведено полноценное UWP-окно.

Рассмотрим графическую составляющую библиотеки — вывод средствами OpenGL. Чтобы нарисовать окружность, достаточно в событие draw добавить одну строку: ofCircle(200, 300, 50). Первые два параметра — положение по X и Y, третий параметр — радиус окружности. В отличие от православного OpenGL, здесь начало координат находится в левом верхнем углу и совпадает с оконной системой. Для создания более сложных трехмерных фигур понадобится несколько другой подход. Но сначала включим свет: для этого в объявлении класса добавим переменную класса ofLight pointLight; кроме того, понадобится сама сфера: ofSpherePrimitive sphere. Включим источник и разместим сферу по центру, в функции Setup напишем:

ofBackground(0,0,0,255); // Заливаем фон черным цветом
ofSetSmoothLighting(true); // Включаем свет
pointLight.setDiffuseColor(ofFloatColor(.85, .85, .55)); // Настраиваем диффузный цвет
pointLight.setSpecularColor(ofFloatColor(1.f, 1.f, 1.f)); // Спекуляр
pointLight.setPosition(ofGetWidth()/2, ofGetHeight()*.25, 200); // Позиция точечного источника света
sphere.setPosition(ofGetWidth()/2, ofGetHeight()/2, 0); // Устанавливаем позицию сферы

Чтобы вывести трехмерную сцену со светом и сферой на экран, достаточно пяти строчек в методе Draw():

ofEnableDepthTest(); // Работаем с глубиной
ofEnableLighting(); // Включаем свет
pointLight.enable();
sphere.setScale(5); // Масштабируем сферу
sphere.draw(); // Выводим ее

Работать с 2D/3D-графикой с помощью openFrameworks вполне реально, это по-прежнему достаточно низкий уровень с эмулированными типами данных и функциями, повышающими удобство использования. Кстати, при запуске приложения openFrameworks также открывает консольное окно, куда можно постить дебажные сообщения.

На этом мы, пожалуй, закончим с графическими примитивами, поскольку редактор не приветствует это (зришь в корень! — Прим. ред.), и посмотрим, как в openFrameworks устроен GUI.

GUI

OpenFrameworks позволяет создать весьма специфичный интерфейс. Для этого уже заготовлена библиотека. Поэтому при создании нового проекта понадобится подключить фреймворк ofxGui в окне Addons. После создания проекта в заголовочный файл надо добавить инклуд либы: #include "ofxGui.h". Теперь можно создавать элементы пользовательского интерфейса. Коротко рассмотрим их.

OpenFrameworks реализует классы-элементы ofxVecSlider, ofxSlider, ofxButton, ofxGuiGroup, ofxColorSlider, ofxLabel, ofxPanel и базовые элементы ofxBaseGui, ofxGui.

Для того чтобы создать определенный элемент пользовательского интерфейса, сначала надо объявить экземпляр класса. Создадим, к примеру, кнопку: ofxButton button;. Для инициализации каждого элемента GUI вызывается его перегруженный метод setup. У разных классов этот метод имеет различное количество параметров. У метода setup кнопки это три параметра: toggleName — метка, которая будет отображаться на кнопке, width и height — ширина и высота, параметры по умолчанию. Вызываем этот метод из функции Setup оконного класса: button.setup("Push Me");. По умолчанию элемент располагается в правом верхнем углу окна (нулевых координатах), поэтому с помощью метода setPosition мы можем изменить эти значения: button.setPosition(100, 100);.

Вся графика в openFrameworks завязана на OpenGL, поэтому объекты сами себя не рисуют, в том числе для элементов пользовательского интерфейса надо вызвать метод draw в событии перерисовки оконного класса. Для закрепления создадим бегунок (slider). К слову, openFrameworks позволяет создать бегунок разного типа для изменения значения, например int, float. Создадим типа float: ofxFloatSlider slider;. Метод setup принимает четыре значения: надпись, шаг, минимальное значение, максимальное. Все элементы имеют большое количество свойств, среди которых позиция, элемент-родитель, цвет фона, цвет фаски, цвет текста, размер (ширина, высота) и многое другое.

Для любого элемента можно определить событие. Для примера добавим реакцию щелчка по кнопке, в результате чего воспроизведем звук. Для звука тоже в openFrameworks имеется объект: ofSoundPlayer. Как ни странно, у него нет метода setup, поскольку это не элемент интерфейса, зато в метод load передадим имя файла, который надо загрузить: ring.load("ring.wav");. Возвратимся к определению события щелчка по кнопке, нам понадобится метод оконного класса buttonClick, в котором начнет воспроизводиться звук: ring.play();. Чтобы связать событие с определенным методом, достаточно одной строчки кода: button.addListener(this, &ofApp::buttonClick);.

3D-объекты

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

Для примера разработаем приложение, которое загружает и отображает вращающуюся 3D-модель, управляемую мышью. При создании нового проекта openFrameworks в окне добавления аддонов надо выбрать ofxAssimpSimpleLoader. После создания проекта добавим объект класса ofxAssimpModelLoader, куда будет загружена модель, предварительно добавив заголовок:

#include "ofxAssimpModelLoader.h"

В обработчике события setup создаем фон, включаем тест глубины, двухсторонний материал, один или несколько источников света — стандартные подготовительные операции. Следующим действием загрузим модель, из интернетов я скачал свободную модель в obj-формате:

optimus.loadModel("Optimus/RB-OptimusBoss.obj", true);

Сразу настроим ее расположение. Объект класса ofxAssimpModelLoader обладает всеми унаследованными методами настройки его расположения и размера. Наконец, чтобы вывести на экран загруженную модель, надо в обработчике события draw вызвать метод drawFaces модели: optimus.drawFaces();. Этот метод рисует треугольниками, вдобавок есть методы для вывода только вершин и только каркасной модели — соответственно, drawVertices и drawWireframe.

Приложение, выводящее 3D-модель

Вдобавок класс ofxAssimpModelLoader загружает анимации из файла с моделью, если они предусмотрены. Их количество можно узнать методом getAnimationCount (в данном случае у модели нет анимаций), каждую вшитую анимацию можно в любой момент воспроизвести: playAllAnimations.

Communication

openFrameworks включает средства для работы по последовательному порту. Они делятся на два класса: ofArduino и ofSerial. Первый предназначен для работы исключительно с устройствами марки Arduino, тогда как второй — с любыми девайсами, подключаемыми к компу через последовательный порт. Для первого варианта предназначен класс ofArduino. Он имеет ограничения: в настоящее время поддерживается работа только с микроконтроллерами ATmega168 и ATmega328. Для подключения к устройству Arduino нужна одна строчка: ard.connect("COM3", 57600);. В первом параметре указывается имя порта (в данном случае в Windows-формате) и имя устройства, во втором параметре задается скорость передачи данных.

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

ofAddListener(ard.EInitialized, this, &ofApp::sendCommands);

То есть, когда девайс известит о готовности, вызовется функция sendCommands, которая пошлет устройству данные (посредством метода sendDigital с предварительной установкой режима передачи).
Класс ofSerial работает с любыми устройствами. С помощью метода listDevices можно получить список всех подключенных устройств. Список сохраняется внутри объекта, затем, воспользовавшись методом getDeviceList, надо заполнить вектор объектов ofSerialDeviceInfo:

  vector <ofSerialDeviceInfo> deviceList = serial.getDeviceList();

Далее с помощью метода setup можно подключиться к устройству, в первом параметре передав имя порта, а во втором — скорость: serial.setup("COM4", 9600);. Затем с помощью методов writeByte и readBytes производится запись в параллельный порт и чтение из него.

Kinect

Для работы с сенсором Kinect тоже существует открытый фреймворк — ofxKinect. Его работа с девайсом происходит на основе открытых драйверов libFreeNect. Именно это позволяет работать с Kinect на разных программных платформах. Пока он работает только с первой версией Кинекта.

Первым делом надо удалить установленные драйверы от Microsoft, в случае если они были установлены, конечно, все пять. После этого ребутни систему. Затем подключи Kinect к компу. В диспетчере устройств обнови драйверы для появившегося там девайса Xbox NUI Motor. Драйверы для обновления можно взять в каталоге с фреймворком ofxKinect (например, в моем случае путь к ним такой: \of_v0.9.0_vs_release\addons\ofxKinect\libs\libfreenect\platform\windows\inf). В целевой папке находятся три подкаталога: для motor, для camera, для audio. Последовательно установи их все: после установки одного появится второе устройство и так далее.

Установленные драйверы для Kinect

Для примера создадим простое приложение, которое в цветном формате выведет то, что видит камера Кинекта. Создай новый проект с поддержкой ofxKinect и ofxOpenCv. Последний нужен для компьютерного зрения.

Итак, перейдем к кодингу. В объявлении класса вначале приинклудь заголовки необходимых либ. Добавь объект ofxKinect kinect;. Также понадобится int-переменная и функция exit, ничего не принимающая и не возвращающая. Она будет вызываться каркасом приложения во время его уничтожения. В описании функции setup надо инициализировать Кинект — объект класса ofxKinect, для этого служит метод Init, который без параметров создает обычную цветную камеру, а если методу передать параметр true, то инфракрасную. После этого методом open запускаем Кинект.

Следующим действием устанавливаем желаемую частоту смены кадров — 60 FPS. Если железо слабое, то приложение будет пытаться выполняться с заданной скоростью, но скорее получится наоборот, и приложение станет ограничивать свою работоспособность. В обработчике события update обновляем состояние Кинекта методом update, а в обработчике события Draw выводим обновленный кадр с камеры Кинекта: kinect.draw(10, 10, 800, 600);. В методе exit формы отключим сенсор, подготавливая приложение к закрытию:

  void ofApp::exit() {
      if (kinect.isConnected())
      kinect.close();
  }

Чисто для веселья можно добавить обработчики нажатия клавиш «вверх», «вниз» для изменения угла обзора камеры. Это реализуется при помощи метода setCameraTiltAngle объекта kinect, метод принимает число градусов для наклона камеры.

На этом все. Запусти приложение, убедись, что все работает, как задумано, и обрати внимание на то ничтожное количество кода, которое потребовалось написать.

Вывод Kinect

Заключение

OpenFrameworks представляет собой открытую кросс-платформенную альтернативу для работы с массой средств: начиная от GUI и заканчивая аппаратными возможностями. Это как швейцарский перочинный ножик — включает все нужные инструменты, укомплектованные удобным образом.

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

OpenFrameworks — большая библиотека, состоящая из множества компонентов, которую можно дополнить внешними модулями. В статье мы рассмотрели только часть из них, не затронув сеть, многопоточность, XML, SVG и многое другое. Почувствовав вкус открытых фреймворков, объединенных в обобщенную модель, с остальными модулями ты разберешься сам без большого труда. Удачи во всех делах!

INFO


OpenFrameworks — это продукт, находящийся на стадии разработки, отсюда довольно много недоделок, однако все из них терпимые. Особенно огорчает отсутствие документации, вернее, она есть, но очень скудная, и видно, что сделана только для галочки. С развитием библиотеки этот минус, скорее всего, будет исправлен, а пока остается разбираться с openFrameworks через исходники.
Юрий Язев: Широко известен под псевдонимом yurembo. Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер».
Похожие материалы