3 Использоване OpenSceneGraph в Вашем

advertisement
3 Использование
OpenSceneGraph в Вашем
Приложении
Реальным приложениям необходимо делать больше чем строить граф сцены и записывать
его в файл. В этой главе объясняется как интегрировать OSG в ваше приложение. Вы узнаете
как отображать граф сцены, изменять положение наблюдения, выполнять операции выбора и
динамически модифицировать данные графа сцены.
3.1 Отображение
OSG не скрывает свою функциональность. OSG предоставляет доступ к низкоуровневым
деталям своей функциональность для того чтобы вы могли использовать ее в своем
приложении. Если вы хотите получить полный контроль над отображением графа сцены в
вашем приложении, вы можете написать код, выполняющий следующие действия.

Разрабатывать свой собственный код управления параметрами наблюдения с
помощью модификации матрицы вида OpenGL.

Создавать окно и OpenGL контекст, и сделать его текущим. Писать код управления
несколькими окнами и контекстами, если это требуется вашему приложению.

Если вашему приложению требуется использовать подгружаемые базы данных,
начинайте с osgDB::DatabasePager.

Создавать osgUtil::UpdateVisitor, osgUtil::CullVisitor, и osgUtil::RenderStage
объекты для реализации обходов обновления, отсечения и отрисовки.

Писать главный цикл управления событиями, получаемыми от операционной
системы. Обращаться к коду управления параметрами наблюдения для обновления
матрицы вида.

Вызывать glClear() перед отображением кадра. Выполнять обходы обновления,
отсечения и отрисовки прежде чем обменивать буфера кадров.

Писать дополнительный код для поддержки стерео и множественного отображения,
если вашему приложению или целевой платформе это необходимо.

Наконец писать весь этот код в платформо-независимой манере, чтобы это работало
на всех целевых платформах.
Все это возможно, но скучно и занимает много времени. Это так же потенциально
несовместимо с будущими версиями OSG, в которых могут измениться некоторые
низкоуровневые интерфейсы, используемые приложением.
К счастью за время развития OSG появилась функциональность, которая упрощает
приложение. Работая с OSG, вы могли встречать некоторые из этих утилит и библиотек.

osgUtil::SceneView—класс который является надстройкой над обходом обновления,
отсечения и отрисовки, но не запускает DatabasePager. Существует несколько
приложений, которые используют SceneView в качестве главного интерфейса для
отображения в OSG.

Producer и osgProducer—Producer это внешняя библиотека для управления камерой,
которая поддерживает множественное отображение. osgProducer это библиотека
которая объединяет OSG и Producer для использовании в приложении. Producer
имеет базу активных пользователей и сегодня существует много приложений
основанных на Producer- и osgProducer.
В OSG v2.0 в библиотеки ядра OSG добавлена новая библиотека — библиотека osgViewer.
osgViewer состоит из набора классов отображения, в которые включены множество
функциональности обычно требуемой приложениям, а именно управление представлением и
событиями, а так же отображением. В ней используется класс osg::Camera для управления
матрицей вида OpenGL. В отличие от класса SceneView, классы отображения библиотеки
osgViewer предоставляют полную поддержку DatabasePager. osgViewer так же упрощает
поддержку нескольких независимых точек зрения на один и тот же граф сцены.
В разделе 1.6.3 Компоненты представлен обзор двух классов из библиотеки osgViewer для
управления параметрами наблюдения, Viewer и CompositeViewer. В этой главе
демонстрируется как ваше приложение может использовать класс Viewer для реализации
функциональности отображения OSG.
3.1.1 Класс Viewer
В исходном коде примера Viewer, идущего к этой книге, демонстрируется минимально
необходимый код для OSG отображения в вашем приложении. В примере Viewer создается
объект osgViewer::Viewer, после присоединения к нему графа сцены можно производить
отображение. Исходный код занимает всего три строки, как показано в Листинге 3-1.
Листинг 3-1
Пример Viewer
В этом листинге демонстрируется минимально необходимый код, для того чтобы
ваше приложение могло осуществлять OSG отображение.
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
int main( int, char ** )
{
osgViewer::Viewer viewer;
viewer.setSceneData( osgDB::readNodeFile( "cow.osg" ) );
return viewer.run();
}
Эта простота не случайна. В нутрии приложения osgviewer используется Viewer для
отображения. osgviewer настраивает свой Viewer для дополнительной функциональности,
чтобы делать больше чем показано в коде Листинга 3-1.
Изменение Параметров Наблюдения
Внутри класса Viewer создается объект osg::Camera для управления матрицей вида
OpenGL. Существует два способа контролировать Camera.

Присоединить управление камерой к Viewer. Если ваше приложение не сделает
этого, тоViewer::run() создаст osgGA::TrackballManipulator чтобы
контролировать Camera. В библиотеке osgGA определено несколько классов для
управления, которые вы можете использовать. Вызов
Viewer::setCameraManipulator() позволяет задать ваш класс управления.

Передать ваши матрицы вида и проекции в Camera. Это даст вашему приложению
полный контроль над параметрами наблюдения.
Если вы выберите задание матриц в Camera напрямую, использование Viewer::run() не
оправдано, поскольку это не позволит изменять параметры наблюдения в каждом кадре.
Вместо этого вам понадобится небольшой цикл, в котором последовательно обновляются
параметры наблюдения, а затем отображается кадр. В Листинге 3-2 показан пример как это
сделать.
Листинг 3-2
Непосредственное управление параметрами наблюдения
В этом листинге демонстрируется как контролировать объект Camera, класса
Viewer, для изменения параметров наблюдения в каждом кадре.
osgViewer::Viewer viewer;
viewer.setSceneData( osgDB::readNodeFile( "cow.osg" ) );
viewer.getCamera()->setProjectionMatrixAsPerspective(
40., 1., 1., 100. );
// Создать матрицу, определяющую смещение от точки наблюдения.
osg::Matrix trans;
trans.makeTranslate( 0., 0., -12. );
// Угол вращения (в радианах)
double angle( 0. );
while (!viewer.done())
{
// Создать матрицу вращения.
osg::Matrix rot;
rot.makeRotate( angle, osg::Vec3( 1., 0., 0. ) );
angle += 0.01;
// Задать матрицу вида (объединение матриц вращения и перемещения).
viewer.getCamera()->setViewMatrix( rot * trans );
// Нарисовать следующий кадр.
viewer.frame();
}
В разделе 2.2 Geodes и Geometry кратко описывалась ориентация по умолчанию мировой
системы координат OSG. По умолчанию матрица в Camera ориентирует мировую систему
координат так: положительный x вправо, положительный z вверх, и положительный y в
экран. Далее описывается как изменять матрицы в Camera на отличные от по умолчанию.
Код в Листинге 3-2 настраивает матрицу проекции объекта Camera один раз за пределами
цикла отображения. Camera предоставляет несколько методов для задания матрицы
проекции, эти методы должны показаться знакомыми большинству OpenGL программистов.
void setProjectionMatrix( const osg::Matrix& matrix );
void setProjectionMatrixAsOrtho( double left, double right,
double bottom, double top, double zNear, double zFar );
void setProjectionMatrixAsOrtho2D( double left, double right,
double bottom, double top );
void setProjectionMatrixAsFrustum( double left, double right,
double bottom, double top, double zNear, double zFar );
void setProjectionMatrixAsPerspective( double fovy,
double aspectRatio, double zNear, double zFar );
Camera::setProjectionMatrix() принимает osg::Matrix в качестве параметра и аналогична
следующей последовательности команд:
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( m );
Camera::setProjectionMatrixAsOrtho() создает матрицу проекции используя алгоритм
идентичный OpenGL команде glOrtho(), в то время как метод
setProjectionMatrixAsOrtho2D() более похож на функцию из GLU, gluOrtho2D(). Camera
так же предоставляет методы для задания перспективной проекции аналогично командам
glFrustum() и gluPerspective().
Внутри цикла отображения, код обновляет матрицу вида объекта Camera в каждом кадре
путем увеличения угла вращения. И опять Camera предоставляет несколько функций,
которые должны быть знакомы OpenGL разработчикам. Код в Листинге 3-2 задает матрицу
вида явно с помощью метода setViewMatrix(), и Camera так же поддерживает метод
setViewMatrixAsLookat() который принимает параметры схожим образом с функцией
gluLookAt().
Задание Цвета Очистки
Объект Camera предоставляет интерфейс еще для нескольких операций помимо настройки
параметров отображения. Ваше приложение использует Camera для задания цвета очистки.
Следующий код показывает, как задать черный цвет очистки:
viewer.getCamera()->setClearColor( osg::Vec4( 0., 0., 0., 1. ) );
По умолчанию Camera очищает буферы глубины и цвета. Для изменения поведения на
отличное от по умолчанию используйте метод Camera::setClearMask() и передавайте
необходимые флаги OpenGL буферов.
viewer.getCamera()->setClearMask( GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
Отрывок кода выше настраивает Camera на очистку буферов цвета, глубины и шаблона в
начале каждого кадра.
3.1.2 CompositeViewer
Библиотека osgViewer поддерживает дополнительный класс viewer, который выходит за
рамки данной книги. В этом разделе он рассматривается лишь поверхностно.
В то время как Viewer управляет параметрами наблюдения одного наблюдателя (например
группой объектов Camera в режиме многоканального отображения), CompositeViewer
поддерживает множество наблюдателей, каждый со своими параметрами, находящимися в
одной или более сценах и позволяющий вашему приложению определять их порядок
отображения. CompositeViewer поддерживает операции отображения в текстуру (RTT), что
позволяет вашему приложению использовать результат одного отображения в качестве
текстуры для другого отображения.
За последней информацией о CompositeViewer, смотрите раздел сайта OSG Wiki Web
[OSGWiki].
3.2 Динамическая Модификация
OSG позволяет вам динамически модифицировать сцену, для создания анимации. Эта
способность является требованием любого интерактивного графического приложения. Вы
можете менять данные геометрии, параметры состояния, настройки узла Switch, или даже
структуру самого графа сцены.
Как объяснялось в Главе 1, обход отсечения сохраняет ссылки на геометрию и состояние в
графе отображения для последующей обработки во время обхода отрисовки. Библиотека
osgViewer поддерживает многопоточность, в некоторых случаях обход отсечения и
отрисовки выполняется одним или несколькими потоками. Для оптимизации
производительности OSG не применяет блокировок для безопасности потоков. Вместо этого
от приложения требуется, чтобы модификация графа сцены выполнялась только за
пределами обхода отсечения и отрисовки.
Существует несколько способов гарантировать что ваши модификации не вступят в
противоречие с потоками отсечения и отрисовки. Одно простое решение—
модифицировать граф сцены за пределами вызова Viewer::frame()—требуется
дополнительный код в главном цикле отображения. Если вам требуется более элегантное
решение, вы должны выполнять модификации во время обхода обновления.
В этом разделе раскрываются некоторые ключевые моменты, касающиеся динамической
модификации графа сцены.

Для оптимизации производительности и безопасности потоков, вам необходимо
сказать OSG какие части графа сцены предполагается модифицировать. Это делается
путем задания data variance (изменение данных) для любого Object (Node, Drawable,
StateSet, и т.д.).

OSG позволяет приложениям назначать обратные вызовы для объектов Node и
Drawable. OSG выполняет эти обратные вызовы во время определенных обходов.
Для модификации Node или Drawable во время обхода обновления, приложение
должно задать обратный вызов обновления.

Приложения не всегда знают точно, какую часть графа сцены они будут
модифицировать. Им, возможно, понадобится произвести поиск интересующего узла
графа сцены, или они могут позволить пользователю выбрать узел с помощью мыши
или другого механизма ввода.
Далее описывается каждый из этих ключевых моментов.
3.2.1 Изменение Данных
Библиотека osgViewer поддерживает многопоточность, что позволяет главному циклу
приложения продолжать свою работу после завершения обхода отрисовки. Это значит, что
обход отрисовки предыдущего кадра может совпасть с обходом обновления следующего
кадра. Рассматривая влияние многопоточности, может показаться, что избежать конфликта с
потоком обхода отрисовки не возможно. Тем не менее, решением является метод
osg::Object::setDataVariance().
Для того чтобы определить изменение данных Object, вызовите setDataVariance() с одним
из перечислений Object::DataVariance. Изначально, изменение данных имеет статус
UNSPECIFIED. Ваше приложение должно задать изменение данных как STATIC или
DYNAMIC.
OSG гарантирует что обход отрисовки вернет управление только после того, как будут
обработаны все DYNAMIC Drawable и StateSet данные объектов. Обход отрисовки может
продолжать отображение графа даже после возврата управления, но в этот момент остается
отобразить только STATIC данные графа. Если ваш граф сцены содержит очень мало
данных DYNAMIC, то поскольку обход отрисовки обрабатывает их в начале кадра, обход
отрисовки вернет управление очень быстро, давая возможность вашему приложению
выполнять другие задачи.
Если ваше приложение попытается модифицировать данные не-DYNAMIC Drawable или
StateSet, вы можете столкнуться с конфликтом потоков, это когда несколько потоков
пытаются получить доступ к одним и тем же данным. Большинство операционных систем
реагируют на эту ситуацию прерывая выполнение вашего приложения. По этой причине вы
всегда должны определять данные, которые предполагаете модифицировать, как DYNAMIC.
Граф отображения содержит ссылки только на объекты Drawable и StateSet. Тем не менее,
если ваше приложение собирается модифицировать Node, например переводя состояния on
или off узла Switch, вы должны установить изменения данных этого узла как DYNAMIC. Это
не даст osgUtil::Optimizer изменить структуру вашего графа сцены.
Причина Аварийного Завершения
Разрабатывая код, динамически модифицирующий граф сцены, вы можете
столкнуться с аварийным завершением работы приложения или нарушением
сегментации, происходящим во время модификации графа сцены. Подобного
рода аварийные завершения почти всегда случаются из-за модификации графа
сцены во время обходов отсечения или отрисовки.
3.2.2 Обратные Вызовы
OSG позволяет вам назначать обратные вызовы объектам Node и Drawable. OSG выполняет
обратные вызовы Node во время обходов обновления и отсечения, а обратные вызовы
Drawable во время обходов отсечения и отрисовки. В этом разделе описывается как
динамически модифицировать Node во время обхода обновления, используя
osg::NodeCallback. Интерфейс обратных вызовов OSG основан на паттерне
проектирования Callback [Gamma95].
Для того чтобы использовать NodeCallback, ваше приложение должно выполнить
следующие шаги.

Новый класс должен наследоваться от NodeCallback.

Переопределить метод NodeCallback::operator()(). Написать код в этом методе для
выполнения динамической модификации вашего графа сцены.

Создать новый класс, унаследованный от NodeCallback, и присоединить его к Node
который желаете модифицировать с помощью метода Node::setUpdateCallback().
OSG вызывает метод operator()() вашего унаследованного класса во время каждого обхода
обновления, позволяя вашему приложению модифицировать Node.
OSG передает два параметра в метод operator()(). Первый параметр это адрес Node,
которому вы назначили ваш обратный вызов. Это тот Node который ваш обратный вызов
должен модифицировать в методе operator()(). Второй параметр это адрес osg::NodeVisitor.
В следующем разделе описывается класс NodeVisitor, а пока можете его игнорировать.
Для того чтобы присоединить ваш NodeCallback к Node, используйте метод
Node::setUpdateCallback(). Node::setUpdateCallback() принимает один параметр, адрес
класса, унаследованного от NodeCallback. Следующий отрывок кода показывает, как
присоединять NodeCallback к узлу.
class RotateCB : public osg::NodeCallback
{
...
};
...
node->setUpdateCallback( new RotateCB );
Несколько узлов могут разделять один и тот же обратный вызов. NodeCallback наследуется
(неявно) от Referenced, и Node хранит ref_ptr<> на свой обратный вызов. Когда последняя
ссылка на обратный вызов будет удалена, счетчик ссылок NodeCallback достигнет нуля, и он
будет удален. В коде выше ваше приложение не сохраняет указатель на объект RotateCB, а
этого и не требуется.
Пример кода, идущего к книге, содержит пример Callback, который демонстрирует, как
использовать обратные вызовы обновления. В коде корова присоединяется к двум узлам
MatrixTransform. Далее класс, наследуемый от NodeCallback, присоединяется к одному из
двух объектов MatrixTransform. Во время обхода обновления новый NodeCallback
модифицирует матрицу для вращения одной из коров. Рисунок 3-1 показывает результат
примера Callback.
Рисунок 3-1
Динамическая модификация с помощью обратного вызова обновления
На этом рисунке показан результат работы примера Callback. Код вращает корову
с лева вокруг ее вертикальной оси, в то время как корова с права остается
неподвижной.
В Листинге 3-3 показан пример кода, который состоит из трех основных частей. Первая часть
определяет класс с именем RotateCB, который наследуется от NodeCallback. Второй часть
является функцией, именуемой createScene(), в ней создается граф сцены. Учите что, когда эта
функция создает первый объект MatrixTransform, именуемый mtLeft, ему назначается
обратный вызов обновления с помощью вызова mtLeft->setUpdateCallback( new
RotateCB ). Если закомментировать эту строку кода и запустить пример, корова не будет
вращаться. Последняя часть примера это функция main(), в которой создается viewer и
происходит отображение.
Листинг 3-3
Исходный Код Примера Callback
Этот пример демонстрирует процесс создания NodeCallback и обновления графа
сцены во время обхода обновления.
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osg/NodeCallback>
#include <osg/Camera>
#include <osg/Group>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
// Наследование класса от NodeCallback для манипулирования
// матрицей объекта MatrixTransform.
class RotateCB : public osg::NodeCallback
{
public:
RotateCB() : _angle( 0. ) {}
virtual void operator()( osg::Node* node,
osg::NodeVisitor* nv )
{
// Правильно было бы убедиться, что это посетитель обновления,
// в этом простом примере в этом нет необходимости.
osg::MatrixTransform* mtLeft =
dynamic_cast<osg::MatrixTransform*>( node );
osg::Matrix mR, mT;
mT.makeTranslate( -6., 0., 0. );
mR.makeRotate( _angle, osg::Vec3( 0., 0., 1. ) );
mtLeft->setMatrix( mR * mT );
// Увеличиваем угол на заданную величину.
_angle += 0.01;
// Продолжить обход так чтобы OSG смог обработать
// другие узлы имеющие обратные вызовы.
traverse( node, nv );
}
protected:
double _angle;
};
// Создание графа сцены. Это корневой узел Group с двумя дочерними
// MatrixTransform, они оба являются родителями одного
// Geode, загруженного из файла модели cow.osg.
osg::ref_ptr<osg::Node>
createScene()
{
// Загрузка модели коровы.
osg::Node* cow = osgDB::readNodeFile( "cow.osg" );
// Изменение данных STATIC, поскольку мы не хотим их модифицировать.
cow->setDataVariance( osg::Object::STATIC );
// Создаем MatrixTransform для отображения левой коровы.
osg::ref_ptr<osg::MatrixTransform> mtLeft =
new osg::MatrixTransform;
mtLeft->setName( "Left Cow\nDYNAMIC" );
// Устанавливаем изменение данных как DYNAMIC, что позволяет OSG
// знать о том, что мы намереваемся модифицировать этот узел во
// время обхода обновления.
mtLeft->setDataVariance( osg::Object::DYNAMIC );
// Установить обратный вызов обновления.
mtLeft->setUpdateCallback( new RotateCB );
osg::Matrix m;
m.makeTranslate( -6.f, 0.f, 0.f );
mtLeft->setMatrix( m );
mtLeft->addChild( cow );
// Создать MatrixTransform для отображения правой коровы.
osg::ref_ptr<osg::MatrixTransform> mtRight =
new osg::MatrixTransform;
mtRight->setName( "Right Cow\nSTATIC" );
// Изменение данных STATIC, поскольку мы не хотим его модифицировать.
mtRight->setDataVariance( osg::Object::STATIC );
m.makeTranslate( 6.f, 0.f, 0.f );
mtRight->setMatrix( m );
mtRight->addChild( cow );
// Создать корневой узел Group.
osg::ref_ptr<osg::Group> root = new osg::Group;
root->setName( "Root Node" );
// Изменение данных STATIC, поскольку мы не хотим его модифицировать.
root->setDataVariance( osg::Object::STATIC );
root->addChild( mtLeft.get() );
root->addChild( mtRight.get() );
return root.get();
}
int main(int, char **)
{
// Создать viewer и задать его данные сцены на наш граф сцены,
// созданный выше.
osgViewer::Viewer viewer;
viewer.setSceneData( createScene().get() );
// Задать цвет очистки, для того чтобы получить нечто
// отличное от светло голубого.
viewer.getCamera()->setClearColor(
osg::Vec4( 1., 1., 1., 1. ) );
// Цикл и отображение. OSG вызывает RotateCB::operator()()
// во время обхода обновления.
viewer.run();
}
RotateCB::operator()() содержит вызов traverse(). Метод является членом класса
osg::NodeCallback. Этот вызов позволяет обходу обновления (osgUtil::UpdateVisitor)
производить обход текущей группы дочерних узлов. Требование в вызове traverse()
предусмотрено дизайном, что позволяет вашему NodeCallback производить обработку как
перед так и после обхода, в зависимости от того куда вы поместите код относительно вызова
traverse(). Уберите этот вызов и это приведет к тому что OSG не будет выполнять обратные
вызовы дочерних узлов. В следующем разделе обсуждается класс NodeVisitor более
детально.
На рисунке 3-2 показан граф сцены, создаваемый примером Callback. Корневой узел Group
содержит два дочерних узла MatrixTransform, которые перемещают одну корову Geode в
различные положения в пространстве. Как показано на рисунке, один из двух
MatrixTransform объектов имеет установленное изменение данных как DYNAMIC, а другой
использует STATIC изменение данных, поскольку код никогда его не модифицирует. Левый
MatrixTransform имеет присоединенный обратный вызов обновления, который
динамически меняет матрицу во время обхода обновления.
Рисунок 3-2
Граф сцены примера программы Callback
На этом рисунке показана иерархия графа сцены примера программы Callback.
Учтите, что два MatrixTransform имеют различные режимы изменения данных.
Как показывает этот пример, динамически изменять узел просто, поскольку присоединение
обратного вызова обновления к известному узлу тривиально. Проблема становится сложнее,
если ваше приложение модифицирует узел, скрытый в глубине графа сцены, или
интерактивно выбираемый пользователем. В следующем разделе описываются методы как в
OSG идентифицировать узел во время выполнения.
3.2.3 NodeVisitors
NodeVisitor является OSG реализацией паттерна проектирования Visitor(Посетитель)
[Gamma95]. В сущности NodeVisitor обходит граф сцены и вызывает функцию каждого
посещаемого узла. Эта простая техника существует в базовом классе многих операций OSG,
включая osgUtil::Optimizer, в классах обработки геометрии из библиотеки osgUtil, и вывода
файлов. OSG использует класс osgUtil::UpdateVisitor (унаследованный от NodeVisitor) для
выполнения обхода обновления. В предыдущем разделе UpdateVisitor являлся NodeVisitor,
который вызывал метод NodeCallback::operator()(). Вообще класс NodeVisitor широко
используется в OSG.
NodeVisitor является базовым классом, который ваше приложение не может создать
непосредственно. Ваше приложение может использовать любой NodeVisitor
поддерживаемый OSG, и вы можете определить свой собственный класс, унаследованный от
NodeVisitor. NodeVisitor состоит из нескольких apply() методов, перегруженных для
большинства основных типов узлов OSG. Когда NodeVisitor обходит граф сцены, он
вызывает подходящий метод apply() для каждого посещаемого узла. Ваш собственный
NodeVisitor перегружает метод apply() только для того типа узла, которому требуется
обработка.
После загрузки графа сцены из файла, приложения обычно производят поиск
интересующих узлов. В качестве примера представьте модель руки робота содержащую
артикуляцию, смоделированную с помощью трансформаций соединений. После загрузки
этого файла с диска, приложение может воспользоваться NodeVisitor для выявления всех
узлов Transform, для того чтобы включить анимацию. В этом случае приложение должно
использовать свой собственный NodeVisitor и переопределить метод
apply(osg::Transform&). Созданный вами NodeVisitor, при обходе графа сцены, вызывает
метод apply() для каждого узла унаследованного от Transform, и приложение сможет
выполнять операции, необходимые для разрешения анимации в этом узле, например такие
как сохранение адреса узла в списке.
Разрешение Обходов NodeVisitor
Базовый класс NodeVisitor, по умолчанию, запрещает обход. В вашем
наследуемом классе вы должны инициализировать базовый класс с помощью
перечисления NodeVisitor::TRAVERSE_ALL_CHILDREN для того чтобы разрешить
обход. В противном случае OSG не станет вызывать никакие из ваших методов
apply().
Если ваш NodeVisitor переопределит несколько методов apply(), OSG вызовет более
подходящий метод apply() для данного узла. Например, Group наследуется от Node. Если
ваш NodeVisitor переопределит apply( Node& ) и apply( Group& ), то во время обхода
OSG вызовет apply( Group& ) если встретит Group, или любой другой узел
унаследованный Group. Если OSG встретит Geode, то в данном случае вызовется apply(
Node& ), поскольку Geode наследуется от Node, а не от Group.
Поиск узла по заданному имени простая и часто нужная операция. Код в Листинге 3-4
показывает как реализовать класс называемый FindNamedNode. Этот класс принимает строку
в конструктор в качестве параметра и сохраняет адрес узла, имеющего заданное имя.
Листинг 3-4
Определение класса FindNamedNode
В этом листинге показано объявление и определение простого NodeVisitor
который ищет узел с заданным именем. Класс FindNamedNode является частью
примера программы FindNode.
// Наследуем класс от NodeVisitor для поиска узла с заданным именем.
class FindNamedNode : public osg::NodeVisitor
{
public:
FindNamedNode( const std::string& name )
: osg::NodeVisitor( // Обходим всех потомков.
osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
_name( name ) {}
// Этот метод вызывается для каждого узла в графе
// сцены. Проверяем каждый узел и если имена совпадают
// значит это наш узел. Если нашли, сохраняем адрес узла.
virtual void apply( osg::Node& node )
{
if (node.getName() == _name)
_node = &node;
// Продолжаем обход оставшегося графа сцены.
traverse( node );
}
osg::Node* getNode() { return _node.get(); }
protected:
std::string _name;
osg::ref_ptr<osg::Node> _node;
};
Метод traverse() является членом NodeVisitor. В этом отличие и сходство с вызовом
traverse() из раздела 3.2.2 Обратные Вызовы, который является членом NodeCallback. Во
время обхода графа сцены NodeVisitor использует следующие правила.

NodeVisitor, сконфигурированный как TRAVERSE_ALL_CHILDREN, обходит
своих потомков.

Пользовательский класс NodeVisitor, в котором переопределяется один или более
методов apply(), ответственен за вызов NodeVisitor::traverse() для обхода потомков
узла. Это требование позволяет вашему классу NodeVisitor выполнять действия как
перед так и после обхода, а так же останавливать обход в случае необходимости.

Если в классе, к которому применяется NodeVisitor, используется обратный вызов,
например обратный вызов обновления, описанный в предыдущем разделе, то
NodeVisitor обходит только узлы без обратных вызовов. NodeVisitor не обходит
потомков узлов, содержащих обратные вызовы. Вместо этого, метод обратного вызова
operator()() ответственен за обход потомков с помощью вызова
NodeCallback::traverse(), в случае если приложению требуется обход.

Даже если к классу NodeVisitor применяются обратные вызовы, например обратный
вызов обновления, описанный в предыдущем разделе, NodeVisitor обходит узлы
потомки не с их помощью. NodeVisitor не осуществляет обход потомков узлов с
помощью обратных вызовов. В методе обратного вызова operator()() за обход
потомков отвечает NodeCallback::traverse(), в случае если он требуется.
Для обхода вашего графа сцены с помощью NodeVisitor, передайте NodeVisitor в качестве
параметра методу Node::accept(). Вы можете вызывать accept() у любого узла, и NodeVisitor
начнет обход графа сцены начиная с него. Для поиска по всему графу сцены, вызовите
accept() корневого узла.
Класс FindNamedNode из Листинга 3-4 является частью примера программы FindNode.
Пример FindNode загружает граф сцены с диска, находит узел с заданным именем и
модифицирует данные узла прежде чем отобразить его. Пример FindNode работает
совместно с примером State, обсуждаемым в разделе 2.4.3 Пример Кода Настройки
Состояния и сохраняющим свой граф сцены в файл. FindNode загружает этот файл, ищет
узел MatrixTransform с StateSet настроенным плоскую заливку и меняет состояние на
плавную заливку.
3.2.4 Выбор
В большинстве 3D приложений требуется некая функциональность для выбора, чтобы
конечный пользователь смог выбрать какую то часть отображаемого изображения. В
простейшем случае пользователь наводит курсор на отображаемую сцену и нажимает на
кнопку мыши. Внутри приложение производит операцию преобразования положения 2D xy
мыши на соответствующий узел графа сцены и сохраняет адрес узла для дальнейшей работы
с ним.
В сущности, приложения, основанные на OSG, выполняют две операции для реализации
выбора.

Получить событие от мыши. Библиотека osgGA предоставляет классы событий
которые позволяют приложениям получать события от мыши платформонезависимым образом.

Определить какая часть графа сцены находится под курсором. Библиотека osgUtil
предоставляет классы определения пересечений, создающие объем вокруг xy
положения мыши и позволяющие произвести пересечение этого объема с вашим
графом сцены. osgUtil возвращает список узлов пересекшихся с объемом уже
отсортированными от ближних к дальним.
В этом разделе описывается как реализовать обе эти операции.
Перехват Событий Мыши
Как упоминалось в разделе 1.6.3 Компоненты, библиотека osgGA предоставляет
платформо-независиммую поддержку GUI событий. Примеры этого раздела используют
osgGA::TrackballManipulator для манипулирования матрицей вида. TrackballManipulator
получает на вход события мыши и модифицирует матрицу вида osg::Camera в viewer’е.
TrackballManipulator наследуется от osgGA::GUIEventHandler. GUIEventHandler
является абстрактным базовым классом, который ваше приложение не может создать
непосредственно. Вместо этого приложение наследует классы от GUIEventHandler для
выполнения операций, основанных на GUI событиях. Для выполнения выбора основанного
на событиях мыши, унаследуйте класс от GUIEventHandler и переопределите метод
GUIEventHandler::handle() для получения событий от мыши. Создайте экземпляр вашего
нового класса и присоедините его к viewer’у приложения.
Метод handle() принимает два параметра, osgGA::GUIEventAdapter и
osgGA::GUIActionAdapter, как показано в следующем примере:
virtual bool GUIEventHandler::handle(
const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa );
Ваша реализация handle() принимает события GUI, включая события мыши, в
GUIEventAdapter. В заголовочном файле GUIEventAdapter объявлено перечисление
EventType, которое позволяет вашему приложению проверять только интересующие его
события, а именно события от мыши. Получить тип события можно с помощью метода
GUIEventAdapter::getEventType().
GUIActionAdapter предоставляет интерфейс, через который ваше приложение может
обращаться к системе GUI. Для того чтобы осуществить выбор с помощью мыши,
присоедините ваш GUIEventHandler, отвечающий за выбор, к вашему классу viewer. В
результате в GUIActionAdapter будет передан ваш класс viewer. Таким образом, вы можете
осуществить пересечение с текущей наблюдаемой сценой.
Прежде чем отображать с помощью viewer’а, создайте экземпляр вашего нового
GUIEventHandler класса и присоедините его к вашему viewer’у с помощью метода
Viewer::addEventHandler(). Как видно из имени, viewer’ы могут иметь множество
обработчиков событий, и Viewer добавляет ваш обработчик событий в свой список
возможных обработчиков. Viewer вызывает handle() для каждого GUI события до тех пор
пока один из них не вернет true.
Код из Листинга 3-5 содержит класс с именем PickHandler, который наследуется от
GUIEventHandler. Реализация метода handle() поддерживает PUSH, MOVE и RELEASE
типы событий мыши. При событиях PUSH и MOVE запоминается xy положение мыши, а
так же при отсутствии движения мыши, а при событии RELEASE срабатывает операция
выбора. Если выбор завершился успешно, handle() возвращает true. Во всех остальных
случаях возвращается false, для того чтобы другие обработчики смогли обработать событие.
Операция выбора выполняется по событию RELEASE и только тогда, когда мышь не
двигается. Это позволяет событиям перемещения мыши быть переданным другим
обработчикам, например такому как TrackballManipulator.
Листинг 3-5
Класс PickHandler
Для реализации выбора в OSG, используйте дочерний класс, унаследованный от
osgGA::GUIEventHandler. В этом листинге показан класс PickHandler из примера
программы Picking. В классе определенно два метода, один для получения
событий мыши, другой для реализации операции выбора при отпускании кнопки
мыши.
// PickHandler – это GUIEventHandler в котором реализован выбор.
class PickHandler : public osgGA::GUIEventHandler
{
public:
PickHandler() : _mX( 0. ),_mY( 0. ) {}
bool handle( const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa )
{
osgViewer::Viewer* viewer =
dynamic_cast<osgViewer::Viewer*>( &aa );
if (!viewer)
return false;
switch( ea.getEventType() )
{
case osgGA::GUIEventAdapter::PUSH:
case osgGA::GUIEventAdapter::MOVE:
{
// По событию перемещение и нажатие кнопки
// запоминаем положение мыши.
_mX = ea.getX();
_mY = ea.getY();
return false;
}
case osgGA::GUIEventAdapter::RELEASE:
{
// Если мышь не перемещалась со времен последнего
// нажатия на кнопку или события перемещения, то
// выполнить выбор. (В противном случае об этом
// позаботится trackball manipulator.)
if (_mX == ea.getX() && _mY == ea.getY())
{
if (pick( ea.getXnormalized(),
ea.getYnormalized(), viewer ))
return true;
}
return false;
}
default:
return false;
}
}
protected:
// Сюда сохраняется xy положение мыши при событиях
// нажатия на кнопку и перемещении.
float _mX,_mY;
// Выполнить операцию выбора.
bool pick( const double x, const double y,
osgViewer::Viewer* viewer )
{
if (!viewer->getSceneData())
// Нечего выбирать.
return false;
double w( .05 ), h( .05 );
osgUtil::PolytopeIntersector* picker =
new osgUtil::PolytopeIntersector(
osgUtil::Intersector::PROJECTION,
x-w, y-h, x+w, y+h );
osgUtil::IntersectionVisitor iv( picker );
viewer->getCamera()->accept( iv );
if (picker->containsIntersections())
{
Const osg::NodePath& nodePath =
picker->getFirstIntersection().nodePath;
unsigned int idx = nodePath.size();
while (idx--)
{
// Найти ПОСЛЕДНИЙ MatrixTransform в списке
// найденных узлов; это будет тот MatrixTransform
// к которому будет присоединен наш обратный вызов.
osg::MatrixTransform* mt =
dynamic_cast<osg::MatrixTransform*>(
nodePath[ idx ] );
if (mt == NULL)
continue;
// Если мы здесь, значит мы нашли MatrixTransform
// в списке найденных узлов.
if (_selectedNode.valid())
// Очистить обратный вызов предыдущего
// выбранного узла, для того
// чтобы он перестал вращаться.
_selectedNode->setUpdateCallback( NULL );
_selectedNode = mt;
_selectedNode->setUpdateCallback( new RotateCB );
break;
}
if (!_selectedNode.valid())
osg::notify() << "Pick failed." << std::endl;
}
else if (_selectedNode.valid())
{
_selectedNode->setUpdateCallback( NULL );
_selectedNode = NULL;
}
return _selectedNode.valid();
}
};
int main( int argc, char **argv )
{
// создать отображение сцены.
osgViewer::Viewer viewer;
viewer.setSceneData( createScene().get() );
// добавить обработчик выбора
viewer.addEventHandler( new PickHandler );
return viewer.run();
}
В Листинге 3-5 показана функция main() примера программы Picking для иллюстрации
использования метода Viewer::addEventHandler(), который присоединяет обработчик
событий к viewer’у.
В общем, для реализации выбора с помощью получения событий мыши, выполните
следующие шаги:

Унаследуйте класс от GUIEventHandler. Переопределите метод handle().

В handle() выясните тип события из параметра GUIEventAdapter для того чтобы
выбрать то, которое интересует вас, и выполните необходимые действия. Верните true
для предотвращения дальнейшей обработки полученного события.

Перед отображением, создайте экземпляр вашего обработчика событий и добавьте его
к вашему viewer’у с помощью метода addEventHandler(). OSG передаст ваш viewer в
метод handle() как GUIActionAdapter параметр.
Эта техника не ограничена только выбором с помощью мыши. Ваше приложение может
реализовывать классы схожие с TrackballManipulator для получения событий мыши
похожим образом. Вы так же можете получать события клавиатуры и реализовывать
операции, реагирующие на эти нажатия.
Следующий раздел завершает обсуждение выбора с помощью мыши, описанием как
определить какая часть вашего графа сцены находится под мышью в момент нажатия
пользователем.
Пересечения
Думайте о выборе с помощью мыши как об испускании луча из указателя мыши в вашу
сцену. Часть сцены, находящаяся под указателем мыши, пересечется с лучом. Луч не вернет
пересечений, в случае если сцена состоит из примитивов линий и точек, поскольку из-за
округления положения указателя мыши невозможно математически выполнить пересечение с
такого типа примитивами. Более того, при обычном перспективном отображении точность
пересечения с лучом обратно пропорциональна расстоянию от положения наблюдения.
Вместо луча, в OSG, можно осуществить пересечение с помощью объема пирамиды,
называемым политопом(выпуклый многогранник), который решает обе проблемы. Пирамида
имеет вершину в точке наблюдения, а ее центральная ось проходит прямо из положения
указателя мыши. Она расширяется с ростом расстояния от точки наблюдения подобно полю
зрения, а приложение контролирует параметр расширения.
OSG пользуется иерархической структурой графа сцены для эффективного вычисления
пересечений с помощью CPU хоста, избегая часто-медленных возможностей пересечения в
OpenGL. Класс osgUtil::IntersectionVisitor наследуется от NodeVisitor и тестирует каждый
ограничивающий объем Node с пересекающим объемом, что позволяет пропускать
дальнейший обход дочерних узлов в случае отсутствия пересечения.
IntersectionVisitor может быть настроен на проверку пересечений с несколькими
возможными геометрическими конструкциями, включая плоскости и сегменты линий.
Его конструктор принимает osgUtil::Intersector в качестве параметра, в котором определен
тип геометрии выбора и производится реальная проверка пересечений. Intersector является
чисто виртуальным базовым классом который ваше приложение не может создавать
непосредственно. В библиотеке osgUtil наследуется несколько классов от Intersector для того
чтобы представлять различные геометрические конструкции, включая
osgUtil::PolytopeIntersector, который идеально подходит для выбора с помощью указателя
мыши.
Некоторым приложениям требуется выбирать отдельные вершины или многоугольники.
Другим приложениям просто надо знать родительский узел Group или Transform,
содержащий выбранную геометрию. Чтобы удовлетворить всем этим требованиям
IntersectionVisitor возвращает osg::NodePath. NodePath это std::vector<osg::Node> в
котором представлена иерархия узлов начиная с корневого узла и далее вниз до листового
узла. Если вашему приложению необходим промежуточный узел, начните перебор
NodePath с последнего элемента к началу, и продолжайте до тех пор, пока не встретите то,
что требуется вашему приложению.
В общем, для выполнения выбора с помощью указателя мыши в OSG, ваше приложение
должно выполнить следующие шаги.

Создайте и настройте PolytopeIntersector, используя нормализованное положение
указателя мыши, хранимое в GUIEventAdapter.

Создайте IntersectionVisitor и передайте PolytopeIntersector в качестве параметра в
конструктор.

Запустите IntersectionVisitor в корневом узле вашего графа сцены, обычно с
помощью Viewer Camera, как показано в следующем коде:
// ‘iv’ является IntersectionVisitor
viewer->getCamera()->accept( iv );

Если PolytopeIntersector содержит пересечения, получите NodePath и выполните
поиск интересующего вас узла.
В Листинге 3-5 показан метод PickHandler::pick() из примера программы Picking. Пример
программы Picking создает граф сцены схожим образом с графом сцены из программы
Callback. Тем не менее граф сцены Picking использует иерархию из двух узлов
MatrixTransform, один для хранения перемещения и другой для осуществления вращения.
При успешном выборе, код перебирает NodePath до тех пор, пока не встретит
MatrixTransform вращения. Потом к найденному узлу присоединяется обратный вызов
обновления, чтобы динамически вращать дочернюю геометрию.
Когда вы запустите пример программы Picking, она отобразит две коровы подобно примеру
Callback. Тем не менее, вы можете выбрать любую из коров, и программа в ответ начнет
вращать выбранную вами корову.
Приложение: Куда
Двигаться Дальше
Надеюсь, эта книга послужила хорошим введением в OSG. Тем не менее, являясь вводным
руководством, эта книга не является полным источником OSG. В этом разделе описываются
дополнительные источники информации, которые многие разработчики OSG найдут для
себя полезными.
Исходный Код
С точки зрения разработчика, основная выгода от продукта с открытым исходным кодом в
доступе к исходному коду. Поскольку вы разрабатываете приложения на базе OSG,
множество вопросов встречаемых вами, может быть решено быстро и просто пройдясь
отладчиком по исходному коду OSG для выяснения, что же там случилось внутри.
Если вы этого еще не сделали, скачайте полный исходный код OSG, сделайте как описано в
разделе 1.2 Установка OSG и откомпилируйте OSG так чтобы библиотеки содержали
отладочную информацию. Компиляция OSG первый раз может показаться запутанной и
занимающей значительное время, но эта трата времени окупится замечательными
дивидендами на этапе разработки вашего приложения.
Распространяемый исходный код OSG так же содержит большую коллекцию хорошо
написанных и познавательных примеров программ, в которых демонстрируется корректное
использование множества возможностей OSG не освещенных в этой маленькой книге. Эти
примеры программ необходимы каждому, кто связан с разработкой OSG.
Можно писать OSG приложения, пользуясь только откомпилированными библиотеками, но
доступ к распространяемому исходному коду, примерам и библиотекам, содержащим
отладочную информацию, ускоряет процесс разработки.
OSG Wiki
OSG Wiki Web сайт [OSGWiki] содержит огромное количество информации относящиеся к
OSG, включая последние новости OSG, ссылки для скачивания, откомпилированные и
готовые к установке библиотеки, информация о событиях OSG сообщества, компоненты,
созданные OSG сообществом и совместимые с OSG, и информация поддержки.
http://www.openscenegraph.org/
Список Рассылок osg-пользователей
Список рассылок osg-пользователей даст вам возможность обмена информацией с другими
пользователями OSG, а так же разработчиками. Если вы столкнулись с проблемой в процессе
сборки OSG, но не можете понять как справится с проблемой в коде или у вас есть вопрос по
внутренней организации OSG, оставленное сообщение osg-пользователям, обычно дает
массу полезных ответов. Подписаться на рассылку можно посетив следующий URL.
http://www.openscenegraph.org/mailman/listinfo/osg-users
Профессиональная Поддержка
Из-за успеха OSG, несколько компаний предоставляют услуги разработки OSG,
консультации, обучение и обслуживание документирования. Качество, стоимость и
доступность зависит от компании. Основную часть информации об обслуживании можно
получить, оставив запрос в списке рассылок или посетив следующий URL.
http://www.openscenegraph.org/osgwiki/pmwiki.php/Support/Support
Глоссарий
.osg
Основанный на ASCII родной формат OSG, способный
хранить все элементы графа сцены.
Изменение данных
Свойство osg::Object, которое определяет, собирается
ли приложение динамически модифицировать данные
Object. Задается с помощью метода
Object::setDataVariance() и передачи параметра
Object::DYNAMIC или Object::STATIC. Смотрите
Object.
Список путей к файлам данных
OSG просматривает список директорий, когда ваше
приложение пытается прочитать 2D изображение или
файл с 3D моделью, с помощью osgDB интерфейса.
Dot OSG wrapper
Это подгружаемая библиотека OSG, которая позволяет
NodeKits выполнять файловые операции ввода/вывода.
Drawable
Класс osg::Drawable содержит геометрию для
отображения. Объекты типа Geode (смотрите Geode)
содержат списки объектов Drawable, вашего графа
сцены. Граф отображения (смотрите граф
отображения) содержит ссылки на объекты Drawable.
Geode
Класс osg::Geode является листовым узлом. Объекты
Geode не содержат потомков, но имеют список
osg::Drawable объектов и так же могут иметь
osg::StateSet (смотрите StateSet). Имя представляет
собой комбинацию слов “geometry” и “node”. Смотрите
так же листовой узел. Смотрите osg::Geode в
заголовочном файле Geode.
Group
Класс osg::Group поддерживает обобщенную
концепцию группировки узлов графа сцены. Он может
служить как групповым так и корневым узлом. Многие
классы графов сцены наследуются от osg::Group для
поддержки множества потомков. Смотрите так же
групповой узел.
Групповой узел
Групповые узлы имеют потомков. Групповой узел имеет
одного или более родителя, если конечно Group не
является корневым узлом. (смотрите корневой узел).
Листовой узел
Листовые узлы графа сцены это такие узлы, которые не
имеют потомков. В большинстве графов сцены листовые
узлы содержат отображаемые данные, такие как
геометрия.
Библиотека GPL
Формально известна как Стандартная Общественная
Лицензия Ограниченного Применения GNU. Менее
ограниченная Стандартная Общественная Лицензия
GNU является основной лицензирования OSG.
Многоканальное отображение
Процесс распараллеливания, который распределяет
нагрузку отображения на несколько графических карт
или систем. В типичном сценарии многоканального
отображения, дисплеи расположены друг возле друга, и
каждая графическая карта отображает свою часть сцены
на своем дисплее.
Node
Базовый класс всех узлов OSG. Смотрите класс
osg::Node в заголовочном файле Node.
OSG NodeKit
Является модулем, который расширяет базовую
функциональность OSG, путем добавления новых
классов Node графа сцены.
NodeVisitor
Класс, который обходит граф сцены, выполняя некие
операции (или собирая из них данные) над
встреченными узлами во время обхода. Класс
osg::NodeVisitor реализует паттерн проектирования
Visitor(Посетитель) [Gamma95].
Object
Чисто виртуальный базовый класс который определяет
некоторые основные свойства и методы общие для
классов Node, Drawable, StateAttribute, StateSet, и
других сущностей OSG.
Выбор
Это взаимодействие между пользователем и 3D
приложением, в котором пользователь выбирает
интересующий его объект на отображаемом
изображении. Обычно указывая курсором мыши на
объект и нажимая на кнопку.
Подгружаемый модуль
Архитектура, которая позволяет программе динамически
загружать библиотеки или модули, удовлетворяющие
стандартному интерфейсу; любая библиотека или модуль
соответствуют архитектуре подгружаемых модулей. OSG
применяет архитектуру подгружаемых модулей для
поддержки 2D и 3D файлов. Библиотеки,
соответствующие интерфейсу, определенному в
osgDB::ReaderWriter, известны под общим названием
подгружаемые модули OSG. OSG получает доступ к
подгружаемым модулям через библиотеку osgDB.
Состояние Позиционирования
Любое значение состояния, содержащие позицию,
которая влияет на текущую матрицу трансформации.
Плоскость отсечения и позиция источника света вот два
примера состояния позиционирования.
Псевдо загрузчик
Подгружаемый модуль OSG который предоставляет
функциональность графа сцены вместо реальной
загрузки файла. Псевдо загрузчик trans, например,
помещает узел Transform над корневым узлом данных
загруженного графа сцены.
Граф отображения
Набор ссылок на объекты Drawable и StateSet. Обход
отсечения формирует граф отображения из графа сцены.
Обход отрисовки посылает данные геометрии и
состояния из графа отображения низлежащему
графическому оборудованию для финального
отображения.
Состояние отображения
Внутренние переменные, которые контролируют как
геометрия будет обрабатываться и отображаться.
Состояние отображения OSG состоит из режимов
(Логических переменных представления, например
освещение и туман может быть разрешено и запрещено)
и атрибутов (переменные которые настраивают
разрешенные представления, например цвет тумана и
формулы смешивания).
Корневой узел
Родительский узел всех узлов графа сцены. По
умолчанию корневой узел не имеет потомка.
Умный указатель
Класс C++, содержащий указатель и поддерживающий
подсчет ссылок на указываемую память. Экземпляр
умного указателя обычно увеличивает счетчик ссылок в
конструкторе и уменьшает его в деструкторе. Когда
счетчик ссылок достигает нуля, память освобождается. В
OSG ref_ptr<> является умным указателем.
StateSet
Объект OSG, который хранит значение состояния. Они
связанны с объектами Node и Drawable, и могут быть
общими для повышения эффективности. Во время
обхода отсечения OSG сортирует некоторые Drawable
по их StateSet.
Stripification(Цепочканизация)
Процесс преобразования набора отдельных
треугольников, которые не явно разделяют общие
вершины в более эффективные наборы цепочек
треугольников с явным разделением общих вершин.
Viewer
Класс OSG, который управляет одним или более
отображением сцены. Класс Viewer управляет
поверхностями отображения (такими как окна или
объекты буфера кадров), манипуляторами камер для
изменения вида(ов), и управления событиями.
Библиография
[ARB05]
OpenGL ARB, Dave Shreiner, Mason Woo, Jackie Neider, and Tom
Davis, OpenGL® Programming Manual, Fifth Edition. Boston:
Addison-Wesley, 2005.
[Gamma95]
Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides,
Design Patterns: Elements of Reusable Object-Oriented Software.
Boston: Addison-Wesley, 1995.
[MacOSXTips]
OSG Apple QuickTime video documentation.
http://www.openscenegraph.org/index.php?page=Tutorials.Mac
OSXTips
[Martz06]
Martz, Paul, OpenGL® Distilled. Boston: Addison-Wesley, 2006.
[OSGWiki]
OpenSceneGraph Wiki Web site, http://www.openscenegraph.org/
[OSGBooks]
OpenSceneGraph Books Web site, http://www.osgbooks.com/
[Rost06]
Rost, Randi. OpenGL® Shading Language, Second Edition. Boston:
Addison-Wesley, 2006.
Download