Класс osg::Group представляет собой так называемый групповой узел графа сцены в OSG. Он может иметь любое количество дочерних узлов, включая листовые ноды геометрии или другие групповые узлы. Это наиболее часто используемые узлы, обладающие широкими функциональными возможностями.

Класс osg::Group является производным от класса osg::Node, и соответственно наследуется и от класса osg::Referenced. osg::Group содержит список дочерних нод, где каждая дочерняя нода управляется умным указателем. Это гаранитирует отсутсвие утечек памяти при каскадном удалении ветки дерева сцены. Данный клас предоставляет разработчику ряд публичных методов

  1. addChild() - присоединяет узел в конец списка дочерних узлов. С другой стороны есть метод insertChild(), помещающий дочерний узел в конкретную позицию списка, которая задается целочисленным индексом или указателем на узел, передаваемыми в качестве параметра.
  2. removeChild() и removeChildren() - удаление одного узла или группы узлов.
  3. getChild() - получение указателя на ноду по её индексу в списке
  4. getNumChildren() - получение числа дочерних узлов, прикрепленных к данной группе.

Управление родительскими узлами

Как мы уже знаем, класс osg::Group управляет группами своих дочерних объектов, среди которых могут присутствовать и экземпляры osg::Geode, управляющие геометрией объектов сцены. Оба упомянутых класса имеют интерфейс для управления родительскими узлами.

OSG позволяет узлам сцены иметь несколько родительских узлов (об этом мы поговорим когда-нибудь потом). Пока же мы рассмотрим методы, определенные в osg::Node, используемые для манипуляций над родительскими узлами:

  1. getParent() - возвращаяет указатель типа osg::Group, содержащий перечень родительских узлов.
  2. getNumParants() - возвращает число родительских узлов.
  3. 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.