
Введение
OpenSceneGraph (далее OSG) — открытый кроссплатформенный фреймворк, написанный на C++ и представляющий собой графический движок, предоставляющий программисту объектный интерфейс к OpenGL. В нашей стране этот движок не особенно популярен, даже на Хабре я видел только одну более-менее приличную публикацию о нем. OSG применяется за рубежом много где, например он является основой для свободного авиасимулятора FlightGear, существует открытая реализация игры Morrowind, называемая OpenMW разработка которой так же перенесена на OSG с движка Ogre. Русскоязычной документации по нему исчезающе мало, а среди англоязычной можно отметить лишь серию книг от разработчиков: OpenSceneGraph 3.0. Beginner’s Guide и OpenSceneGraph 3. Cookbook.
Тем не менее, движок достаточно интересен по следующим причинам:
- Открытая кроссплатформенная реализация на C++.
- Модульная архитектура.
- Расширяемость за счет встроенной системы плагинов.
- Возможность многопоточной обработки графических данных и встроенный инструментарий для её реализации
- Управление динамической памятью через механизм умных указателей
Думаю, что читателям Хабра будет интересно более подробно ознакомится с этим проектом. Не лишним будет и пополнение русскоязычной базы знаний по OSG. Все материалы, которые будут публиковаться мной по данной теме основаны на книге OpenSceneGraph 3.0. Beginner’s Guide, но являются не её переводом, а скорее творческой переработкой изложенного там материала. Если вам интересна данная тема, прошу под кат
Единственным верным способом получить самую свежую версию OSG на своей машине — собрать библиотеку из исходных текстов. Существующий бинарный инсталлятор для Windows ориентируется на компилятор MS Visual C++. Мне же, для своих проектов необходимо использование компилятора GCC, вернее его варианта MinGW32, входящего в поставку средств разработки фреймворка Qt. Таким образом нам понадобится:
- Установленный и настроенный фреймворк Qt с компилятором MinGW32 версии 5.3 и IDE QtCreator
- Клиент Git для Windows
- Утилита сmake для Windows
Предполагается, что читатель знаком с IDE QtCreator и системой сборки qmake, используемой в проектах Qt. Кроме того, предполагается, что читатель владеет основами использования системы контроля версий Git и имеет ненулевые навыки в программировании в принципе.
1. Получение исходных текстов OpenSceneGraph
Создаем на своем жестком диске каталог, где будем производить сборку OSG, например по пути D:\OSG

Перейдем в этот каталог и стянем туда исходники с официального репозитория OSG на Github
D:\OSG> git clone https://github.com/openscenegraph/OpenSceneGraph.git
Длительность процесса скачивания зависит от того, насколько широк ваш канал доступа в Интернет. Рано или поздно мы получим у себя локальную копию репозитория OSG.
Скачав исходники создадим рядом каталог build-win32-debug

В этом каталоге мы будем осуществлять сборку отладочного комплекта OSG. Но прежде
2. Настройка cmake
Для корректной работы cmake нам следует отредактировать файл путь-установки-cmake\share\cmake-3.13\Modules\CMakeMinGWFindMake.cmake. По-умолчанию он выглядит так
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. find_program(CMAKE_MAKE_PROGRAM mingw32-make.exe PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin" c:/MinGW/bin /MinGW/bin "[HKEY_CURRENT_USER\\Software\\CodeBlocks;Path]/MinGW/bin" ) find_program(CMAKE_SH sh.exe ) if(CMAKE_SH) message(FATAL_ERROR "sh.exe was found in your PATH, here:\n${CMAKE_SH}\nFor MinGW make to work correctly sh.exe must NOT be in your path.\nRun cmake from a shell that does not have sh.exe in your PATH.\nIf you want to use a UNIX shell, then use MSYS Makefiles.\n") set(CMAKE_MAKE_PROGRAM NOTFOUND) endif() mark_as_advanced(CMAKE_MAKE_PROGRAM CMAKE_SH)
Закоментируем в нем несколько строк, дабы утилита не пыталась искать в нашей системе юниксовый шелл, и не найдя, завершалась с ошибкой
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. find_program(CMAKE_MAKE_PROGRAM mingw32-make.exe PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin" c:/MinGW/bin /MinGW/bin "[HKEY_CURRENT_USER\\Software\\CodeBlocks;Path]/MinGW/bin" ) #find_program(CMAKE_SH sh.exe ) #if(CMAKE_SH) # message(FATAL_ERROR "sh.exe was found in your PATH, here:\n${CMAKE_SH}\nFor MinGW make to work correctly sh.exe must NOT be in your path.\nRun cmake from a shell that does not have sh.exe in your PATH.\nIf you want to use a UNIX shell, then use MSYS Makefiles.\n") # set(CMAKE_MAKE_PROGRAM NOTFOUND) #endif() mark_as_advanced(CMAKE_MAKE_PROGRAM CMAKE_SH)
3. Сборка и установка отладочной и релизной версий движка
Теперь запускаем командный интерпретатор cmd, ярлык на который находится по пути Пуск->Программы->Qt->Qt 5.11.2->Qt 5.11.2 for Desktop (MinGW 5.3.0 32bit)

Запущенный сеанс командной строки настраивает всё окружение, необходимое для работы средств сборки mingw32. Переходим в каталог с исходниками OSG
C:\Qt\Qt5.11.2\5.11.2\mingw53_32>D: D:\> cd OSG\build-win32-debug
Даем команду
D:\OSG\build-win32-debug>cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=E:\Apps\OSG -DCMAKE_BUILD_TYPE=DEBUG ../OpenSceneGraph
Разберем смысл параметров подробнее:
- -G "MinGW Makefiles" — указывает, что необходимо сгенерировать Makefile для утилиты mingw32-make
- -DCMAKE_INSTALL_PREFIX=E:\Apps\OSG — устанавливаем путь, по которому будет установлен OSG
- -DCMAKE_BUILD_TYPE=DEBUG — указывает, что следует собирать отладочную версию движка.
Выполнение команды проверяет готовность окружения для сборки, генерирует сценарий сборки и следующий выхлоп
Выхлоп cmake при настройке сборки OSG
-- The C compiler identification is GNU 5.3.0 -- The CXX compiler identification is GNU 5.3.0 -- Check for working C compiler: C:/Qt/Qt5.11.2/Tools/mingw530_32/bin/gcc.exe -- Check for working C compiler: C:/Qt/Qt5.11.2/Tools/mingw530_32/bin/gcc.exe -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: C:/Qt/Qt5.11.2/Tools/mingw530_32/bin/g++.exe -- Check for working CXX compiler: C:/Qt/Qt5.11.2/Tools/mingw530_32/bin/g++.exe -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Looking for pthread.h -- Looking for pthread.h - found -- Looking for pthread_create -- Looking for pthread_create - found -- Found Threads: TRUE -- Found OpenGL: opengl32 -- Could NOT find EGL (missing: EGL_INCLUDE_DIR) -- Checking windows version... -- Performing Test GL_HEADER_HAS_GLINT64 -- Performing Test GL_HEADER_HAS_GLINT64 - Failed -- Performing Test GL_HEADER_HAS_GLUINT64 -- Performing Test GL_HEADER_HAS_GLUINT64 - Failed -- 32 bit architecture detected -- Could NOT find Freetype (missing: FREETYPE_LIBRARY FREETYPE_INCLUDE_DIRS) -- Could NOT find JPEG (missing: JPEG_LIBRARY JPEG_INCLUDE_DIR) -- Could NOT find Jasper (missing: JASPER_LIBRARIES JASPER_INCLUDE_DIR JPEG_LIBRARIES) -- Could NOT find LibXml2 (missing: LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) -- Could NOT find ZLIB (missing: ZLIB_INCLUDE_DIR) -- Could NOT find ZLIB (missing: ZLIB_INCLUDE_DIR) -- Could NOT find GDAL (missing: GDAL_LIBRARY GDAL_INCLUDE_DIR) -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) -- Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR) -- Trying to find DCMTK expecting DCMTKConfig.cmake -- Trying to find DCMTK expecting DCMTKConfig.cmake - failed -- Trying to find DCMTK relying on FindDCMTK.cmake -- Please set DCMTK_DIR and re-run configure (missing: DCMTK_config_INCLUDE_DIR DCMTK_dcmdata_INCLUDE_DIR DCMTK_dcmimage_INCLUDE_DIR DCMTK_dcmimgle_INCLUDE_DIR DCMTK_dcmjpeg_INCLUDE_DIR DCMTK_dcmjpls_INCLUDE_DIR DCMTK_dcmnet_INCLUDE_DIR DCMTK_dcmpstat_INCLUDE_DIR DCMTK_dcmqrdb_INCLUDE_DIR DCMTK_dcmsign_INCLUDE_DIR DCMTK_dcmsr_INCLUDE_DIR DCMTK_dcmtls_INCLUDE_DIR DCMTK_ofstd_INCLUDE_DIR DCMTK_oflog_INCLUDE_DIR) -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) -- Could NOT find GStreamer (missing: GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARIES GSTREAMER_VERSION GSTREAMER_BASE_INCLUDE_DIRS GSTREAMER_BASE_LIBRARIES GSTREAMER_APP_INCLUDE_DIRS GSTREAMER_APP_LIBRARIES GSTREAMER_PBUTILS_INCLUDE_DIRS GSTREAMER_PBUTILS_LIBRARIES) (found version "") -- Could NOT find SDL2 (missing: SDL2_LIBRARY SDL2_INCLUDE_DIR) -- Could NOT find SDL (missing: SDL_LIBRARY SDL_INCLUDE_DIR) -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) -- Could NOT find JPEG (missing: JPEG_LIBRARY JPEG_INCLUDE_DIR) -- Could NOT find ZLIB (missing: ZLIB_INCLUDE_DIR) -- Could NOT find PNG (missing: PNG_LIBRARY PNG_PNG_INCLUDE_DIR) -- Could NOT find TIFF (missing: TIFF_LIBRARY TIFF_INCLUDE_DIR) -- g++ version 5.3.0 -- Performing Test _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS -- Performing Test _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS - Success -- Performing Test _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS -- Performing Test _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS - Failed -- Performing Test _OPENTHREADS_ATOMIC_USE_SUN -- Performing Test _OPENTHREADS_ATOMIC_USE_SUN - Failed -- Performing Test _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED -- Performing Test _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED - Success -- Performing Test _OPENTHREADS_ATOMIC_USE_BSD_ATOMIC -- Performing Test _OPENTHREADS_ATOMIC_USE_BSD_ATOMIC - Failed -- Configuring done -- Generating done -- Build files have been written to: D:/OSG/build-win32-debug
говорит нам о том, что можно приступать к сборке. Даем команду
D:\OSG\build-win32-debug>mingw32-make -j9
можно, как в моем примере, указать число потоков сборки, если у вас многоядерный процессор (ключ -j). Начнется процесс сборки, занимающий на моем компьютере около восьми минут

По окончании сборки устанавливаем библиотеку
D:\OSG\build-win32-debug> mingw32-make install
после выполнения команды обнаруживаем библиотеку установленной по заранее заданному нами пути

Теперь соберем релизную версию движка, создав другой каталог сборки
D:\OSG\build-win32-debug>cd .. D:\OSG> mkdir build-win32-release D:\OSG>cd build-win32-release D:\OSG\build-win32-release> cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=E:\Apps\OSG ../OpenSceneGraph D:\OSG\build-win32-release> mingw32-make -j9 D:\OSG\build-win32-release> mingw32-make install
4. Настройка переменных окружения
Расположение библиотек OSG после инсталляции может быть любым — определяется это пожеланиями конкретного пользователя и его возможностями для размещения файлов на компьютере. При этом, при настройке конкретного проекта, использующего данные библиотеке требует стремится к некой унификации, абстрагируясь от конкретного расположения библиотек.
Создадим несколько системных переменных окружения, указывающих пути к библиотекам, заголовочным файлам и плагинам OSG. В приведенном мной примере это будет выглядеть так

Необходимо создать переменные, имена которых обведены на скриншоте красным. После создания переменных, для того, чтобы они были видны средствами разработки, в частности QtCreator-ом нужно как минимум перелогинится в системе (выйти и зайти от имени текущего пользователя) или, возможно, перезагрузить систему (это же Windows!)
После этого процедуру установки OSG на наш компьютер можно считать оконченной.
5. Пишем Hello World в QtCreator
Знакомство с графическим движком OpenSceneGraph начнем с простейшего примера, как это обычно принято в программировании с некоего "Hello world!".
Прежде чем писать какой-либо код, настроим наш проект для системы сборки qmake, которую мы будем использовать на протяжении всего цикла статей. В интересующем нас месте файловой системы создадим следующую структуру каталогов
OSG-lessons/ |-data/ |-OSG-lessons/ | |-hello/ |-include/ |-src/
В каталоге hello создадим файл hello.pro следующего содержания
Полный текст hello.pro
Разберем эти письме подробнее
TEMPLATE = app TARGET = hello DESTDIR = ../../bin win32 { OSG_LIB_DIRECTORY = $$(OSG_BIN_PATH) OSG_INCLUDE_DIRECTORY = $$(OSG_INCLUDE_PATH) CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) LIBS += -L$$OSG_LIB_DIRECTORY -losgd LIBS += -L$$OSG_LIB_DIRECTORY -losgViewerd LIBS += -L$$OSG_LIB_DIRECTORY -losgDBd LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreadsd } else { LIBS += -L$$OSG_LIB_DIRECTORY -losg LIBS += -L$$OSG_LIB_DIRECTORY -losgViewer LIBS += -L$$OSG_LIB_DIRECTORY -losgDB LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreads } INCLUDEPATH += $$OSG_INCLUDE_DIRECTORY } unix { CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) LIBS += -losgd LIBS += -losgViewerd LIBS += -losgDBd LIBS += -lOpenThreadsd } else { LIBS += -losg LIBS += -losgViewer LIBS += -losgDB LIBS += -lOpenThreads } } INCLUDEPATH += ./include HEADERS += $$files(./include/*.h) SOURCES += $$files(./src/*.cpp)
Разберем эти письме подробнее
TEMPLATE = app TARGET = hello DESTDIR = ../../bin
Переменные задают шаблон проекта (app — приложение), имя исполняемого файла (hello) и каталог, куда исполняемый файл помещается после сборки.
win32 { OSG_LIB_DIRECTORY = $$(OSG_BIN_PATH) OSG_INCLUDE_DIRECTORY = $$(OSG_INCLUDE_PATH) CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) LIBS += -L$$OSG_LIB_DIRECTORY -losgd LIBS += -L$$OSG_LIB_DIRECTORY -losgViewerd LIBS += -L$$OSG_LIB_DIRECTORY -losgDBd LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreadsd } else { LIBS += -L$$OSG_LIB_DIRECTORY -losg LIBS += -L$$OSG_LIB_DIRECTORY -losgViewer LIBS += -L$$OSG_LIB_DIRECTORY -losgDB LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreads } INCLUDEPATH += $$OSG_INCLUDE_DIRECTORY }
В зависимости от ОС, где собирается проект, определяем переменные, указывающие пути к каталогам библиотек и заголовочных файлов OSG. Вот тут-то нам и пригодились переменные окружения OSG_BIN_PATH и OSG_INCLUDE_PATH — теперь неважно, где установлена библиотека OSG. Любой желающий работать с этим проектом на своем компьютере просто пропишет соответствующие переменные окружения в своей системе, не редактируя сценарий сборки.
CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) LIBS += -L$$OSG_LIB_DIRECTORY -losgd LIBS += -L$$OSG_LIB_DIRECTORY -losgViewerd LIBS += -L$$OSG_LIB_DIRECTORY -losgDBd LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreadsd } else { LIBS += -L$$OSG_LIB_DIRECTORY -losg LIBS += -L$$OSG_LIB_DIRECTORY -losgViewer LIBS += -L$$OSG_LIB_DIRECTORY -losgDB LIBS += -L$$OSG_LIB_DIRECTORY -lOpenThreads }
Пишем сценарий для сборки в unix-подобных ОС
unix { CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) LIBS += -losgd LIBS += -losgViewerd LIBS += -losgDBd LIBS += -lOpenThreadsd } else { LIBS += -losg LIBS += -losgViewer LIBS += -losgDB LIBS += -lOpenThreads } }
Здесь мы настраиваем имя исполняемого файла и указываем библиотеки, которые следует компоновать с нашей программой для различных вариантов сборки: как отладочной так и релизной. Отладочные библиотеки OSG имеют суффикс "d" после имени файла. Суффикс "_d" мы добавим так же и к исполняемому файлу проекта, дабы отличать отладочный вариант от релизного.
INCLUDEPATH += $$OSG_INCLUDE_DIRECTORY INCLUDEPATH += ./include HEADERS += $$files(./include/*.h) SOURCES += $$files(./src/*.cpp)
Ну и наконец определяем пути поиска заголовочных файлов и файлы, включаемые в дерево проекта. Создаем в каталоге include/ пустой файл main.h, а в кталоге src/ — файл main.cpp. Открываем этот проект в QtCreator и настраиваем его так, как показано на скриншоте

После открытия проекта увидим следующую картину

Напишем такой код в файле main.h
#ifndef MAIN_H #define MAIN_H #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
Далее реализуем основное тело программы в файле main.cpp
#include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> root = osgDB::readNodeFile("../data/cessna.osg"); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Файл с моделькой самолета необходимо скопировать в каталог data/. Этот файл, а так же многое из того, что будет использовано в данном цикле статей, можно скачать из репозитория OpenSceneGraph-Data
После компиляции и запуска мы получим что-то вроде этого

Первые две строчки нашего кода
(void) argc; (void) argv;
помечают входные параметры функции main() как неиспользуемые, дабы избежать предупреждения компилятора. Далее создается корневая нода сцены, в качестве которой выступает модель самолета, загружаемая из файла cessna.osg
osg::ref_ptr<osg::Node> root = osgDB::readNodeFile("../data/cessna.osg");
Потом создается экземпляр класса osgViewer::Viewer — так называемый "вьювер" — объект управляющий отображением сцены на экране. Вьюверу передаются данные сцены
viewer.setSceneData(root.get());
и запускается цикл отрисовки сцены
return viewer.run();
В этом простейшем коде уже содержится ряд базовых концепций, используемых в OSG. Но разговор о них пойдет чуть попозже.
Заключение
Полный исходный код примеров, описываемых здесь и далее можно получить здесь. Надеюсь, что эта публикация заинтересовала читателей, продолжение следует...
