Введение в OpenSceneGraph: Наша первая программа
Знакомство с графическим движком OpenSceneGraph начнем с простейшего примера, как это обычно принято в программировании с некоего “Hello world!”.
Прежде чем писать какой-либо код, кастроим наш проект для системы сборки qmake, которую мы будем использовать на протяжении всего цикла статей. В интересующем нас месте файловой системы создадим следующию структуру каталогов
OSG-lessons/
|-data/
|-OSG-lessons/
|
|-hello/
|-include/
|-src/
В каталоге hello создадим файл 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. Но разговор о них пойдет чуть попозже.