14 November 2018

OpenSceneGraph: сборка из исходников и Hello World

ProgrammingWorking with 3D-graphicsGame developmentDevelopment for LinuxDevelopment for Windows
Tutorial


Введение


OpenSceneGraph (далее OSG) — открытый кроссплатформенный фреймворк, написанный на C++ и представляющий собой графический движок, предоставляющий программисту объектный интерфейс к OpenGL. В нашей стране этот движок не особенно популярен, даже на Хабре я видел только одну более-менее приличную публикацию о нем. OSG применяется за рубежом много где, например он является основой для свободного авиасимулятора FlightGear, существует открытая реализация игры Morrowind, называемая OpenMW разработка которой так же перенесена на OSG с движка Ogre. Русскоязычной документации по нему исчезающе мало, а среди англоязычной можно отметить лишь серию книг от разработчиков: OpenSceneGraph 3.0. Beginner’s Guide и OpenSceneGraph 3. Cookbook.

Тем не менее, движок достаточно интересен по следующим причинам:

  1. Открытая кроссплатформенная реализация на C++.
  2. Модульная архитектура.
  3. Расширяемость за счет встроенной системы плагинов.
  4. Возможность многопоточной обработки графических данных и встроенный инструментарий для её реализации
  5. Управление динамической памятью через механизм умных указателей

Думаю, что читателям Хабра будет интересно более подробно ознакомится с этим проектом. Не лишним будет и пополнение русскоязычной базы знаний по OSG. Все материалы, которые будут публиковаться мной по данной теме основаны на книге OpenSceneGraph 3.0. Beginner’s Guide, но являются не её переводом, а скорее творческой переработкой изложенного там материала. Если вам интересна данная тема, прошу под кат

Единственным верным способом получить самую свежую версию OSG на своей машине — собрать библиотеку из исходных текстов. Существующий бинарный инсталлятор для Windows ориентируется на компилятор MS Visual C++. Мне же, для своих проектов необходимо использование компилятора GCC, вернее его варианта MinGW32, входящего в поставку средств разработки фреймворка Qt. Таким образом нам понадобится:

  1. Установленный и настроенный фреймворк Qt с компилятором MinGW32 версии 5.3 и IDE QtCreator
  2. Клиент Git для Windows
  3. Утилита с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. Но разговор о них пойдет чуть попозже.

Заключение


Полный исходный код примеров, описываемых здесь и далее можно получить здесь. Надеюсь, что эта публикация заинтересовала читателей, продолжение следует...
Tags:3d-графикаграфический движокopenscenegraphqtcreatorqmakelinuxwindowsgamedev
Hubs: Programming Working with 3D-graphics Game development Development for Linux Development for Windows
+5
4.3k 40
Comments 15