- PVSM.RU - https://www.pvsm.ru -
OpenSceneGraph — это кроссплатформенная библиотека с открытыми исходниками для разработки высокопроизводительных 3D-приложений. Это не игровой движок, связывающий пользователя по рукам и ногам заложенными в него ограничениями, а именно библиотека — набор полезных модулей, которые отлично работают как поодиночке, так и в сборке.

Ядро OpenSceneGraph, собственно граф сцены, — довольно тонкая обёртка вокруг OpenGL, позволяющая задавать иерархию объектов и выполнять над ними любые желаемые преобразования:
Каждый узел графа сцены — это экземпляр какого-то из потомков класса Node. Стрелочки — это отношения «родитель-ребёнок»:

Узлы, имеющие детей, называются группами (Group). Обычный Group ничего не делает со своими детьми — все они будут отрендерены, как есть. Однако, наследники класса Group могут иметь дополнительное поведение. Например, MatrixTransform наследует класс Group, и позволяет применить матрицу преобразования ко всем детям разом. Например, если изменить матрицу, отвечающую за башню танка, то башня будет крутиться вместе со стволом:

Примитивы рисования OpenSceneGraph называются Drawables. Каждый Drawable соответствует какому-либо примитиву рисования OpenGL: сфере, кубу, произвольному mesh, чайнику OpenGL и т. д. Сами Drawables попадают на сцену только в контейнере Geode (сокращение от «geometry node»). Geode может содержать в себе любое количество Drawables:

Очень важным свойством OpenSceneGraph является то, что любой узел может иметь нескольких потомков. Это важно для того, чтобы не дублировать одинаковые объекты и не тратить память компьютера и видеоадаптера на хранение повторяющихся фрагментов. Например, у танка несколько колёс и несколько гусениц, но, поскольку они одинаковые, то можно хранить их в одном экземпляре, а чтобы они располагались в разных местах танка, их положение будем задавать индивидуальными MatrixTransforms:

Если мы хотим сделать полноценную модель танка, который умеет крутить колёсами и гусеницами, поворачивать башню и управлять стволом, то получится граф типа такого:

Чтобы крутить колёсами, достаточно изменить texture mapping на левых или правых колёсах. И пользователь увидит, что соответствующие колёса все разом крутятся. Аналогично с гусеницами — изменением текстуры можно добиться видимого эффекта движения.
Граф сцены может иметь весьма сложную структуру, и для того, чтобы упростить управление памятью, OpenSceneGraph использует сборщик мусора со счётчиком ссылок. Каждый класс, наследующий osg::Referenced, получает собственный счётчик ссылок, который автоматически инкрементируется и декрементируется при помощи системы умных указателей osg::ref_ptr. Вот простой пример:
{
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
}
В этом примере создаётся новый экземпляр osg::Geode, инициализируется умный указатель, затем указатель разрушается, и вместе с ним osg::Geode, поскольку больше на него не осталось ни одной ссылки. При добавлении детей в группу, Drawables в Geode и всех прочих перекрёстных ссылок между объектами графа, используются умные указатели. Это гарантирует, что при удалении ссылки на корневой узел графа все объекты будут корректно уничтожены.
Вот минимальное приложение OpenSceneGraph:
#include <osgViewer/Viewer>
int main()
{
osgViewer::Viewer viewer;
return viewer.run();
}
Здесь используется модуль osgViewer, который берёт на себя открытие графического окна, инициализацию OpenGL, создание камеры по умолчанию, инициализацию обработчика клавиши Escape и контроллера мыши, чтобы можно было двигать камеру с её помощью. При запуске программы мы увидим пустую сцену, которая по Escape закроется.
Следующий шаг — создание корневого узла. Например, поместим перед viewer.run() код создания сферы:
// Создание Drawable
osg::Sphere *shape = new osg::Sphere(
osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f);
osg::ShapeDrawable *drawable = new osg::ShapeDrawable(shape);
// Создание Geode
osg::Geode *geode = new osg::Geode;
geode->addDrawable(drawable);
// Регистрация корневого узла сцены
viewer.setSceneData(geode);
После запуска этого приложения мы увидим сферу:

Теперь можно перейти к загрузке шрифта и выводу текста. Нам понадобится библиотека osgText, которая отвечает за работу с текстом:
osgText::Font *font = osgText::readFontFile(
"/usr/share/fonts/truetype/msttcorefonts/arial.ttf");
osgText::Text *text = new osgText::Text;
text->setFont(font);
text->setAxisAlignment(osgText::Text::XZ_PLANE);
text->setText("Привет!", osgText::String::ENCODING_UTF8);
osg::Geode *geode = new osg::Geode;
geode->addDrawable(text);
Функция readFontFile загружает из файла шрифт, затем мы создаём объект Text, который является наследником Drawable. А значит, его можно при помощи метода addDrawable добавить в Geode.
После запуска программы появится текст:

Большинство графических приложений должно постоянно обновлять изображение на экране: создавать и удалять новые объекты, двигать их, менять им свойства и рендерить кадр за кадром:

Изменение сцены — это те операции, которые делает приложение. Пока приложение не закончит свои обновления, начинать рендерить следующий кадр невозможно — иначе в процессе обхода дерева системой рендеринга граф может оказаться в несогласованном состоянии, и приложение повредит себе память или просто упадёт. Это значит, что чем быстрее будут выполнены обновления, тем больший FPS будет на выходе.
При создании реальных приложений имеет смысл выполнять тяжёлые вычисления одновременно с фазой рендеринга в другом потоке. Там же можно создавать новые объекты сцены. А когда главный цикл дойдёт до фазы изменения сцены, можно будет быстро применить результаты расчётов к объектам сцены, прилинковать созданные поддеревья к графу сцены. Аналогично и с удалением большого числа объектов. Во время фазы изменения сцены можно их просто отлинковать от дерева и поместить в очередь удаления, а фактическое разрушение объектов выполнять в другом потоке.
Для примера сделаем, чтобы надпись «Привет, хабр» крутилась на экране. Для этого мы сначала обернём Geode в MatrixTransform:
osg::MatrixTransform *mat = new osg::MatrixTransform;
mat->addChild(geode);
viewer.setSceneData(mat);
Затем попросим Viewer зарегистрировать наш обработчик событий:
RotationHandler *handler = new RotationHandler(mat);
viewer.addEventHandler(handler);
Каждый обработчик событий — это объект, наследующий класс osgGA::GUIEventHandler. Нас сейчас интересует, как обработать событие FRAME, которое вызывается перед каждым кадром:
class RotationHandler: public osgGA::GUIEventHandler {
public:
RotationHandler(osg::MatrixTransform *mat):
m_mat(mat)
{
}
virtual bool handle(const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter &adapter)
{
osg::Matrix mat;
switch (ea.getEventType()) {
case osgGA::GUIEventAdapter::FRAME:
mat.makeRotate(ea.getTime(), osg::Vec3(0.0f, 0.0f, 1.0f));
m_mat->setMatrix(mat);
}
}
private:
osg::ref_ptr<osg::MatrixTransform> m_mat;
};
При запуске программы текст начнёт вращаться вокруг оси (0, 0, 1).
Мы рассмотрели базовые принципы построения сцены, оставив за кадром обход графа, назначение атрибутов узлам (материалов, освещённости), управление камерой, обработку мыши и клавиатуры и многое другое. Просто упомяну некоторые интересные возможности:
Официальный сайт — www.openscenegraph.org [1]
Лучшая документация — книги от авторов [2].
Лучшая документация, доступная бесплатно — это огромное множество примеров, поставляемых с библиотекой, и отличный код, который читать легко и приятно.
Автор: nereati
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/game-development/47607
Ссылки в тексте:
[1] www.openscenegraph.org: http://www.openscenegraph.org/
[2] книги от авторов: http://www.amazon.com/s?field-keywords=openscenegraph
[3] Источник: http://habrahabr.ru/post/200890/
Нажмите здесь для печати.