Введение в OpenSceneGraph: Групповые узлы дерева сцены
Класс osg::Group представляет собой так называемый групповой узел графа сцены в OSG. Он может иметь любое количество дочерних узлов, включая листовые ноды геометрии или другие групповые узлы. Это наиболее часто используемые узлы, обладающие широкими функциональными возможностями.
Класс osg::Group является производным от класса osg::Node, и соответственно наследуется и от класса osg::Referenced. osg::Group содержит список дочерних нод, где каждая дочерняя нода управляется умным указателем. Это гаранитирует отсутсвие утечек памяти при каскадном удалении ветки дерева сцены. Данный клас предоставляет разработчику ряд публичных методов
- addChild() - присоединяет узел в конец списка дочерних узлов. С другой стороны есть метод insertChild(), помещающий дочерний узел в конкретную позицию списка, которая задается целочисленным индексом или указателем на узел, передаваемыми в качестве параметра.
- removeChild() и removeChildren() - удаление одного узла или группы узлов.
- getChild() - получение указателя на ноду по её индексу в списке
- getNumChildren() - получение числа дочерних узлов, прикрепленных к данной группе.
Управление родительскими узлами
Как мы уже знаем, класс osg::Group управляет группами своих дочерних объектов, среди которых могут присутствовать и экземпляры osg::Geode, управляющие геометрией объектов сцены. Оба упомянутых класса имеют интерфейс для управления родительскими узлами.
OSG позволяет узлам сцены иметь несколько родительских узлов (об этом мы поговорим когда-нибудь потом). Пока же мы рассмотрим методы, определенные в osg::Node, используемые для манипуляций над родительскими узлами:
- getParent() - возвращаяет указатель типа osg::Group, содержащий перечень родительских узлов.
- getNumParants() - возвращает число родительских узлов.
- getParentalNodePath() - возвращает все возможные пути к корневой ноде сцены от текущей ноды. Он возвращает список переменных типа osg::NodePath.
osg::NodePath представляет собой std::vector указателей на узлы сцены.
Например, для сцены, изображенной на рисунке следующий код
osg::NodePath &nodePath = child3->getPArentalNodePaths()[0];
for (unsigned int i = 0; i < nodePath.size(); ++i)
{
osg::Node *node = nodePath[i];
// Что-нибудьь делаем с нодой
}
вернет ноды Root, Child1, Child2.
Вы не должны использовать механизмы управления памятью для ссылки на родительские ноды. При удалении родительской ноды автоматически удаляются и все дочерние ноды, что может привести приложение к краху.
Пример: добавление моделей в дерево сцены
Проиллюстрируем механизм использвания групп следующим примером
main.h
#ifndef MAIN_H
#define MAIN_H
#include <osg/Group>
#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> model1 = osgDB::readNodeFile("../data/cessna.osg");
osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile("../data/cow.osg");
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(model1.get());
root->addChild(model2.get());
osgViewer::Viewer viewer;
viewer.setSceneData(root.get());
return viewer.run();
}
Принципиально пример отличается от всех предыдущих тем, что мы загружаем две трехмерных модели, а для их добавления в сцену создаем групповую ноду root и добавляем в неё наши модельки как дочерние ноды
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(model1.get());
root->addChild(model2.get());
В итоге мы получаем сцену, состоящую из двух моделей - самолета и смешной зеркальной коровы. Кстати, зеркальная корова не будет зеркальной, если не скопировать её текстуру из OpenSceneGraph-Data/Images/reflect.rgb а каталог data/Images нашего проекта.
Класс osg::Group может принимать в качестве дочерних любые типы узлов, в том числе и узлы своего типа. Напротив, класс osg::Geode не содержит вообще каких-либо дочерних узлов - он является оконечным узлом, содержащим в себе геометрию объекта сцены. Этот факт удобен при выяснении вопроса является ли узел узлом типа osg::Group или другого типа производного от osg::Node. Рассмотрим маленький пример
osg::ref_ptr<osg::Group> model = dynamic_cast<osg::Group *>(osgDB::readNodeFile("../data/cessna.osg"));
Значение, возвращаемое функцией osgDB::readNodeFile() всегда имеет тип osg::Node, но оно может быть преобразовано к своему наследнику osg::Group. Если коневой узел модели Cessna это групповой узел, то преобразование будет успешным, в противном случае преобразование вернет NULL.
Можно выполнить так же такой фокус, работающий на большинстве компиляторов
// Загружаем модель в групповой узел
osg::ref_ptr<osg::Group> group = ...;
// Преобразуем его к узлу
osg::Node* node1 = dynamic_cast<osg::Node*>( group.get() );
// Преобразуем группу к узлу неявно
osg::Node* node2 = group.get();
В критических для производительности местах кода лучше использовать специальные методы преобразования
// Assumes the Cessna's root node is a group node.
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg");
osg::Group* convModel1 = model->asGroup(); // Отрабоает нормально
osg::Geode* convModel2 = model->asGeode(); // Вернет NULL.