2.2 и Geometry

advertisement
Вводное Руководство в OpenSceneGraph
2.2 Geodes и Geometry
В предыдущем разделе была представлена концепция управления памятью в OSG. Если вам
не знаком подсчет ссылок памяти, то лучше посмотреть на реальные OSG примеры, для
лучшего понимания. В этом разделе представлена простая OSG программа, которая
использует технику управления памятью описанную ранее, и знакомит вас с построением
графа сцены с помощью OSG классов содержащих геометрию. На первый взгляд код может
показаться громоздким, поскольку вы еще не знакомы со многими классами. Подробное
объяснение геометрических классов дается в комментариях в исходном коде.
Листинг 2-1 широко использует шаблонный класс ref_ptr<>, описанный в предыдущем
разделе. Все выделения памяти в Листинге 2-1 с подсчетом ссылок. Даже функция
createSceneGraph() возвращает ref_ptr<>, созданного графа сцены. (Проще говоря, код в
Листинге 2-1 мог бы быть написан полностью с использованием обычных C++ указателей, а
возвращенный указатель сохранялся бы в ref_ptr<>. Тем не менее, является хорошей
практикой использование ref_ptr<> в вашем приложении, поскольку это автоматизирует
освобождение памяти даже в случае исключений или преждевременного выхода из функции.
В примерах кода этой книги используется ref_ptr<> для поддержки этой хорошей практики.)
Листинг 2-1
Построение простого графа сцены
Это отрывок из кода примера Simple идущего вместе с книгой. Функция
createSceneGraph() определяет геометрию для единственного прямоугольного
примитива. Прямоугольник имеет разный цвет вершин, но общую нормаль для
всего примитива.
#include <osg/Geode>
#include <osg/Geometry>
osg::ref_ptr<osg::Node> createSceneGraph()
{
// Создать объект для хранения в нем геометрии.
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
// Создать массив для хранения четырех вершин.
osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
geom->setVertexArray( v.get() );
v->push_back( osg::Vec3( -1.f, 0.f, -1.f ) );
v->push_back( osg::Vec3( 1.f, 0.f, -1.f ) );
v->push_back( osg::Vec3( 1.f, 0.f, 1.f ) );
v->push_back( osg::Vec3( -1.f, 0.f, 1.f ) );
// Создать массив их четырех цветов
osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
geom->setColorArray( c.get() );
geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
c->push_back( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
c->push_back( osg::Vec4( 0.f, 1.f, 0.f, 1.f ) );
c->push_back( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
c->push_back( osg::Vec4( 1.f, 1.f, 1.f, 1.f ) );
Построение Графа Сцены
// Создать массив содержащий одну нормаль.
osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
geom->setNormalArray( n.get() );
geom->setNormalBinding( osg::Geometry::BIND_OVERALL );
n->push_back( osg::Vec3( 0.f, -1.f, 0.f ) );
// Получить прямоугольник из четырех вершин из ранее
// подготовленных данных.
geom->addPrimitiveSet(
new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) );
// Добавить Geometry (Drawable) в Geode и вернуть Geode.
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable( geom.get() );
return geode.get();
}
Код в Листинге 2-1 создает граф сцены с одним узлом. На рисунке 2-3 показан этот
предельно простой граф сцены. Этот один узел графа сцены имеет образовательную цель
для новичков. Реальные графы сцен гораздо сложнее.
Обратите внимание, что код в Листинге 2-1 определяет четыре вершины в плоскости y=0.
Подобно OpenGL, OSG не налагает ограничений на систему координат, используемую
приложением. Тем не менее, по умолчанию
Рисунок 2-3
Граф сцены Листинга 2-1
В Листинге 2-1 создается граф сцены состоящий из одного Geode.
библиотека osgViewer использует мировую систему координат, которая ориентированна
положительный x вправо, положительный z вверх, и положительный y в экран. Это работает
в большинстве приложений, в которых земная поверхность находится в xy плоскости. Глава
3, Использование OpenSceneGraph в Вашем Приложении, описывает как изменить
систему мировых координат на отличную от по умолчанию. Код в Листинге 2-1 использует
ориентацию по умолчанию для отображения четырехугольника, обращенного к
наблюдателю.
В дополнение к созданному графу сцены, как показано в Листинге 2-1, вы так же захотите его
отобразить или анимировать. Примеры в этой главе используют приложение osgviewer для
отображения графа сцены, поскольку написание кода для отображения будет представлено
только в Глава 3, Использование OpenSceneGraph в Вашем Приложении. Для
отображения графа сцены в приложении osgviewer, вам необходимо записать его(граф
сцены) на диск. В Листинге 2-2 показан код, который вызывает функцию из Листинга 2-1, и
записывает граф сцены на диск в .osg файл. После того как граф сцены записан на диск, вы
можете использовать osgviewer для просмотра того, как выглядит результат.
Листинг 2-2
Вводное Руководство в OpenSceneGraph
Запись графа сцены на диск
В этом листинге показана точка входа main() примера программы Simple. Из main()
вызывается createSceneGraph() из Листинга 2-1, где создается граф сцены, затем
этот граф сцены записывается на диск в файл с именем “Simple.osg”.
#include <osg/ref_ptr>
#include <osgDB/Registry>
#include <osgDB/WriteFile>
#include <osg/Notify>
#include <iostream>
using std::endl;
osg::ref_ptr<osg::Node> createSceneGraph();
int main( int, char** )
{
osg::ref_ptr<osg::Node> root = createSceneGraph();
if (!root.valid())
osg::notify(osg::FATAL) << "Failed in createSceneGraph()." << endl;
bool result = osgDB::writeNodeFile(
*(root.get()), "Simple.osg" );
if ( !result )
osg::notify(osg::FATAL) << "Failed in osgDB::writeNodeFile()." << endl;
}
После вызова функции из Листинга 2-1 создающего граф сцены, код из Листинга 2-2
записывает его на диск в виде файла с именем “Simple.osg”. Формат файла .osg это
принадлежащий OSG текстовой ASCII формат файла. Поскольку это ASCII файл, файлы
.osg медленно загружаются да к тому же еще и велики, поэтому это формат редко
используется в промышленном коде. Тем не менее, очень полезно для отладки в процессе
разработки и быстрых демонстраций.
Код в Листинге 2-1 и 2-2 из примера Simple, идущего вместе c книгой. Если вы этого еще не
сделали, скачайте исходный код примера с Web сайта этой книги, откомпилируйте и
запустите его. После запуска примера вы найдете выходной файл Simple.osg в вашей рабочей
директории. Для того чтобы посмотреть, как выглядит граф сцены, используйте osgviewer:
osgviewer Simple.osg
osgviewer должен дать результат похожий представленному на Картинке 2-4. В Главе 1
описывается osgviewer и как им пользоваться. Например, вы можете вращать отображаемую
геометрию с помощью левой кнопки мыши и приближать или удалять с помощью правой.
Код из Листинга 2-1 широко использует классы геометрии OSG. Далее дается на более
высоком уровне объяснение как пользоваться этими классами.
Построение Графа Сцены
Рисунок 2-4
Простой пример графа сцены отображаемый в osgviewer’е
На картинке показан квадрат созданный в Листинге 2-1, а после записанный в
файл .osg в коде Листинга 2-2 и отображенный с помощью osgviewer’a.
2.2.1 Обзор Классов Геометрии
Код в Листинге 2-1 может сбивать с толку, но в сущности в нем выполняется только три
операции.
1. Он создает массив вершин, нормалей и данные о цвете
2. Он создает объект osg::Geometry и добавляет в него массивы. Он так же добавляет
объект osg::DrawArrays для определения как рисовать данные.
3. Он создает узел графа сцены osg::Geode и добавляет объект Geometry в него.
В этом разделе рассматривается каждый из этих шагов подробно.
Классы Vector и Array
В OSG определен богатый набор классов для хранения векторных данных, таких как
вершины, нормали, цвета и текстурные координаты. osg::Vec3 это массив трехкомпонентных
чисел с плавающей запятой; используйте его для определения данных векторов и нормалей.
Используйте osg::Vec4 для определения данных цвета и osg::Vec2 для 2D текстурных
координат. В дополнение к простому хранению эти классы обеспечивают полный набор
Вводное Руководство в OpenSceneGraph
методов для расчета длины, векторного и скалярного произведения, сложения векторов и
векторно-матричного умножения.
В OSG определены шаблонные классы массивов для хранения объектов. Наиболее часто
используемые шаблонные классы массивов предназначены для хранения векторных данных.
В связи с тем фактом, что они часто используются, OSG предоставляет определение типов
для массивов векторных данных - osg::Vec2Array, osg::Vec3Array, и osg::Vec4Array.
В Листинге 2-1 создаются отдельные трехкомпонентные вектора для каждой xyz вершины с
помощью Vec3, затем каждый Vec3 сохраняется в конец Vec3Array. Код использует
Vec3Array похожим образом для хранения xyz данных нормалей. В Листинге 2-1
используется Vec4 и Vec4Array для данных о цвете, поскольку цвет определяется четырьмя
компонентами (красный, зеленый, синий и альфа). Позже в этой главе будет представлен код
примера, который использует Vec2 и Vec2Array для хранения двухэлементных текстурных
координат.
Типы массивов наследуются от std::vector, поэтому они поддерживают метод push_back()
для добавления новых элементов, как показано в Листинге 2-1. Как дочерние классы от
std::vector, классы массивов так же поддерживают методы resize() и operator[]().
osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
geom->setVertexArray( v.get() );
v->resize( 4 );
(*v)[ 0 ] = osg::Vec3( -1.f, 0.f, -1.f );
(*v)[ 1 ] = osg::Vec3( 1.f, 0.f, -1.f );
(*v)[ 2 ] = osg::Vec3( 1.f, 0.f, 1.f );
(*v)[ 3 ] = osg::Vec3( -1.f, 0.f, 1.f );
Рисуемые
OSG определяет класс osg::Drawable, для хранения данных отображения. Drawable это
виртуальный базовый класс, который не создается непосредственно. OSG предоставляет три
производных класса от Drawable.



osg::DrawPixels—DrawPixels это надстройка вокруг команды glDrawPixels().
osg::ShapeDrawable—ShapeDrawable предоставляет доступ к нескольким
предопределенным геометрическим формам, таким как цилиндр и сфера.
osg::Geometry—Geometry это гибкий класс для хранения и отображения геометрии
общего назначения. Пример кода использует Geometry, который является наиболее
часто используемым производным классом.
Если вы уже знакомы с массивами вершин в OpenGL, класс Geometry вам будет легко
использовать. Geometry предоставляет интерфейс, который позволяет вашему приложению
определять массивы данных вершин, как их интерпретировать и отображать. Аналогично с
определением данных массива вершин в OpenGL (с помощью glVertexPointer() и
glNormalPointer()) и отображением массива вершин (с помощью glDrawArrays() и
glDrawElements()). Код из Листинга 2-1 использует следующие методы Geometry:

setVertexArray(), setColorArray() и setNormalArray()—Эти методы аналогичны
glVertexPointer(), glColorPointer() и glNormalPointer() в OpenGL. Ваше
приложение использует эти методы для определения массива вершин, цвета, и данных
Построение Графа Сцены
нормалей. Каждый из методов setVertexArray() и setNormalArray() принимает
указатель на Vec3Array как параметр, и setColorArray() принимает указатель на
Vec4Array.

setColorBinding() and setNormalBinding()—Эти методы говорят Geometry как
применять данные о цвете и нормалях. Они принимают перечисляемый тип,
определенный в классе Geometry, как параметр. В Листинге 2-1 назначение цвета как
osg::Geometry::BIND_PER_VERTEX означает, что цвет у каждой вершины будет
индивидуальным. Тем не менее, нормали определены как
osg::Geometry::BIND_OVERALL что означает применить одну нормаль на всю
Geometry.

addPrimitiveSet()—Этот метод говорит как Geometry отображать свои данные. Он
принимает указатель на osg::PrimitiveSet как параметр. PrimitiveSet это виртуальный
базовый класс который вы не можете создать непосредственно. Ваш код должен
добавлять множество PrimitiveSet объектов к одной и той же Geometry.
Метод addPrimitiveSet() позволяет вашему приложению определять как OSG должен
рисовать геометрические данные хранимые в Geometry объекте. В Листинге 2-1 определен
osg::DrawArrays объект. DrawArrays наследуется от PrimitiveSet. Думайте об этом как о
надстройке над командой glDrawArrays() для рисования массива вершин. Другие дочерние
классы от PrimitiveSet (DrawElementsUByte, DrawElementsUShort и DrawElementsUInt)
имитируют OpenGL команду glDrawElements(). OSG так же предоставляет класс
DrawArrayLengths, которому нет аналога в OpenGL. Функционально он схож с множеством
вызовов glDrawArrays() с различными диапазонами индексов и количеством элементов.
Наиболее часто используемый конструктор класса DrawArrays имеет следующие
определение:
osg::DrawArrays::DrawArrays(
GLenum mode, GLint first, GLsizei count );
mode это один из десяти типов примитивов OpenGL, таких как GL_POINTS, GL_LINES или
GL_TRIANGLE_STRIP. PrimitiveSet это базовый класс в котором определен эквивалент
перечислению OpenGL, например такой osg::PrimitiveSet::POINTS, и ваш код может
использовать их так же.
first является индексом на первый элемент в массиве данных вершин которые OSG должно
использовать для отображения, и count это общее количество элементов которое OSG должно
использовать. Например, если данные вершин содержат 6 вершин и вы хотите отобразить
полоску треугольников (triangle strip) из этих вершин, то вы можете добавить следующий
DrawArrays набор примитивов к вашей Geometry:
geom->addPrimitiveSet( new osg::DrawArrays(
osg::PrimitiveSet::TRIANGLE_STRIP, 0, 6 );
После добавления данных о вершинах, цвете, нормалях и DrawArrays наборе примитивов в
Geometry объект, код в Листинге 2-1 выполняет одну последнюю операцию с Geometry. Он
добавляет ее к узлу графа сцены. В следующем разделе описывается эта операция.
Как OSG Рисует
Вводное Руководство в OpenSceneGraph
В то время как классы, производные от PrimitiveSet, обеспечивают
функциональность схожую с возможностями OpenGL для массивов
вершин, не стоит полагаться что PrimitiveSet всегда использует
массивы вершин во внутренней реализации. В зависимости от
настроек отображения, OSG может использовать массивы вершин (с
или без буферизации объектов), дисплейные списки или даже
glBegin()/glEnd() для отображения геометрии.
Объекты, наследуемые от Drawable (такие как Geometry) используют
дисплейные списки по умолчанию. Вы можете изменять это поведение
вызвав osg::Drawable::setUseDisplayList( false ).
OSG перейдет на использование glBegin()/glEnd() в случае если вы
определите атрибут BIND_PER_PRIMITIVE, что вызовет установку
атрибутов для каждого примитива (например для каждого
треугольника в режиме GL_TRIANGLES.)
Geodes
Класс osg::Geode в OSG является листовым узлом, который хранит геометрию для
отображения. В Листинге 2-1 создается простейший граф сцены, какой только возможно—
граф сцены, состоящий из единственного листового узла. В конце Листинга 2-1 функция
createSceneGraph() возвращает адрес этого Geode в виде ref_ptr<> на osg::Node. Это
допустимо в C++ коде, поскольку Geode является производным классом от Node. (По
определению в OSG все узлы графа сцены наследуются от Node.)
osg::Geode не имеет потомков, поскольку он является листовым узлом, но он может
содержать геометрию. Имя Geode это сокращение от “узел геометрии”—узел который
содержит геометрию. Любая геометрия, которую ваше приложение отображает, должно быть
присоединено к Geode. Geode предоставляет метод addDrawable() который позволяет
вашему приложению присоединять геометрию.
Geode::addDrawable() принимает указатель на Drawable в качестве параметра. Как было
написано в предыдущем разделе, Drawable это виртуальный базовый класс от которого
наследуется множество дочерних классов, например такой как Geometry. Посмотрите в
Листинге 2-1 пример того, как добавлять объект Geometry в Geode с помощью
addDrawable(). В коде эта операция выполняется в конце функции createSceneGraph() прямо
перед возвращением Geode.
2.3 Узел Group
Узлом групп в OSG является osg::Group, который позволяет вашему приложению добавлять
в себя любое количество дочерних узлов, которые в свою очередь так же могут быть узлами
Group со своими потомками, как показано на Рисунке 2-5. Group является базовым классом
для многих полезных узлов, включая osg::Transform, osg::LOD и osg::Switch, которые
описываются далее в этом разделе.
Построение Графа Сцены
Group наследуется от Referenced. В обычном случае код ссылок дан в родители Group,
таким образом, если корневой узел графа сцены удален, это вызовет цепное удаление,
гарантирующие отсутствие утечек памяти.
На самом деле Group является сердцем OSG, поскольку он позволяет вашему приложению
организовывать данные в виду графа сцены. Сила объектов Group в их интерфейсе
управления дочерними узлами. В Group так же есть интерфейс управления родителями,
который он наследует от базового класса, osg::Node. В этом разделе дается обзор как
дочернего, так и родительского интерфейсов.
Далее описываются дочерние и родительские интерфейсы. Этот раздел описывает три часто
используемых класса наследуемых от Group—это узлы Transform, LOD и Switch.
2.3.1 Дочерний Интерфейс
Класс Group определяет интерфейс для потомков и все узлы, которые наследуются от
Group, наследуют этот интерфейс. Большинство OSG узлов, которые вы используете,
наследуются от Group (Geode является исключением), поэтому в общем случае вы можете
полагать, что большинство узлов поддерживают дочерний интерфейс.
Рисунок 2-5
Узел Group
Показанные зеленым, узлы Group могут иметь множество потомков, которые в
свою очередь так же могут быть узлами Group со своими потомками.
Group хранит указатели на свои дочерние узлы в std::vector< ref_ptr<Node> >—в массиве
ref_ptr<> переменных указывающих на Node. Вы можете получить доступ к потомку по
индексу, поскольку Group используется как массив. Group использует ref_ptr<>, что
позволяет работать системе управления памятью OSG. Следующий отрывок кода показывает
часть описания дочернего интерфейса объекта Group. Все классы в пространстве имен osg.
class Group : public Node {
Вводное Руководство в OpenSceneGraph
public:
...
// Добавит дочерний узел.
bool addChild( Node* child );
// Убрать дочерний узел. Если этот узел не является дочерним,
// ничего не делать и вернуть false.
bool removeChild( Node* child );
// Заменить дочерний узел на новый.
bool replaceChild( Node* origChild, Node* newChild );
// Вернуть количество детей.
unsigned int getNumChildren() const;
// Вернуть true если определенный узел является дочерним.
bool containsNode( const Node* node ) const;
...
};
Очень простой граф сцены может состоять из родительского узла Group с двумя потомками
Geode. Вы можете построить подобный граф сцены с помощью следующего кода.
osg::ref_ptr<osg::Group> group = new osg::Group;
osg::ref_ptr<osg::Geode> geode0 = new osg::Geode;
group->addChild( Geode0.get() );
osg::ref_ptr<osg::Geode> geode1 = new osg::Geode;
group->addChild( Geode1.get() );
Замете что Group использует ref_ptr<>, чтобы указывать на свои дочерние узлы. В этом
примере group принимает ссылки памяти на geode0 и geode1. Эта память остается занятой
после того как geode0
и geode1выйдут из области видимости, и освободится после удаления group.
2.3.2 Родительский Интерфейс
Group наследует интерфейс для управления родителем от Node. Geode имеет такой же
интерфейс, поскольку он так же наследуется от Node. OSG позволяет узлам иметь множество
родителей. Следующий отрывок кода показывает часть описания родительского интерфейса
объекта Node. Все классы в пространстве имен osg.
class Node : public Object {
public:
...
typedef std::vector<Group*> ParentList;
// Вернуть список всех родителей.
const ParentList& getParents() const;
Построение Графа Сцены
// Вернуть указатель на родителя по заданному индексу.
Group* getParent( unsigned int I );
// Вернуть количество родителей
unsigned int getNumParents() const;
...
};
Node наследуется от osg::Object. Object это виртуальный базовый класс который ваше
приложение не может создавать непосредственно. Object предоставляет интерфейс для
хранения и извлечения строки с именем, определяет режим хранения данных как статический
или динамически модифицируемый, и так же наследуется от Referenced для поддержки
управления памятью OSG. В Главе 3.2 Динамическая Модификация, обсуждаются имена и
интерфейс динамических данных более детально.
Множество Родителей
Когда вы
добавляете один
и тот же узел как
дочерний к
нескольким
узлам, дочерний
узел становится
обладателем
нескольких
родителей, как
показано на
диаграмме. Вы
можете захотеть
сделать так, что
бы отобразить
дочерний узел множество раз без необходимости создания
нескольких копий. В Главе 2.4.3 Пример Кода Настройки Состояния
показан пример отображения одного и тот же Geode с различными
трансформациями и состояниями отображения.
Когда узел имеет множество родителей, OSG обходит его множество
раз. Каждый из родителей хранит свой собственный ref_ptr<> на
своего потомка, таким образом, дочерний узел не будет удален до тех
пор, пока все родители не перестанут на него ссылаться.
Обратите внимание что osg::Node::ParentList это std::vector обычных C++ указателей.
Поскольку Node содержит адреса своих родителей, Node не нужно управление памятью
OSG для ссылок на родителей. При удалении родителя, родитель удаляет себя из всех
списков дочерних узлов.
Вводное Руководство в OpenSceneGraph
В обычном случае, когда узел имеет одного родителя (getNumParents() возвращает один),
вызов getParent( 0 ) получает указатель на родителя.
Вы часто используете дочерний и родительские интерфейсы при построении и управлении
графом сцены. Тем не менее классы, наследуемые от Group, предоставляют дополнительную
функциональность требуемую многим приложениям. В следующих разделах описываются
три из них.
2.3.3 Узлы Transform
OSG поддерживает трансформацию геометрических данных с помощью osg::Transform
семейства классов. Transform наследуется от Group, и может иметь множество потомков.
Это виртуальный базовый класс, который ваше приложение не может создавать
непосредственно. Вместо этого используйте osg::MatrixTransform или
osg::PositionAttitudeTransform, которые наследуются от Transform. Оба этих класса
обеспечивают различный интерфейс для трансформаций. В зависимости от требований
вашего приложения вы можете воспользоваться одним из них или двумя сразу.
Transform влияет на стек OpenGL модельных матриц. Иерархически расположенные узлы
Transform, создают последовательность трансформаций в манере, схожей с командами
OpenGL манипулирования матрицами (такими как glRotatef() или glScalef()) помещая
матрицы на вершину текущего стека матриц.
Узел Transform позволяет вам задавать его начало координат. По умолчанию установлен
режим относительного начала координат (osg::Transform::RELATIVE_RF), что дает в
результате последовательные трансформации, описанные ранее. Тем не менее, так же как и в
OpenGL вы вызывали glLoadMatrixf(), OSG позволяет вам задавать абсолютное начало
координат.
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
mt->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
Узел MatrixTransform
MatrixTransform внутри использует объект osg::Matrix (описание в сноске). Для создания MatrixTransform
узла, который выполняет трансформацию, создайте Matrix трансформацию и назначьте Matrix узлу
MatrixTransform.
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
osg::Matrix m;
m.setTranslate( x, y, z );
mt->setMatrix( m );
Matrix не наследуется от Referenced. Вы можете создать локальную переменную типа
Matrix в стеке. Метод MatrixTransform::setMatrix() копирует Matrix параметр в
переменную члена узла MatrixTransform.
Matrix предоставляет интерфейс для общих преобразований таких как, перемещение,
вращение и масштабирование. Вы так же можете задать матрицу явно:
Построение Графа Сцены
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
osg::Matrix m;
//Задать все 16 значений Matrix:
m.set( 1.f, 0.f, 0.f, 0.f,
0.f, 1.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
10.f, 0.f, 0.f, 1.f ); // переместить по x=10
mt->setMatrix( m );
Пример State, показанный в Главе 2.4.3. Пример Кода Установки Состояния, использует
множество MatrixTransform узлов для отображения одного и того же Geode для
различного расположения, и каждое со своими уникальными настройками состояния.
Узел PositionAttitudeTransform
Используйте узел PositionAttitudeTransform для задания трансформаций с помощью
позиции заданной через Vec3 и кватернионом. OSG предоставляет класс osg::Quat для
хранения данных о ориентации в виде кватерниона. Quat не наследуется от Referenced и
следовательно не ведет подсчет ссылок.
Matrices и osg::Matrix
Класс osg::Matrix хранит и позволяет оперировать матрицей 4x4,
состоящей из 16 чисел с плавающей запятой. Matrix не наследуется
от Referenced и не ведет подсчет ссылок.
Matrix предоставляет интерфейс, который в чем то схож с нотацией
определенной в OpenGL, используемой в большинстве руководств по
OpenGL. Интерфейс доступа представляет собой двумерный строкоориентированный C++ массив.
osg::Matrix m;
m( 0, 1 ) = 0.f; // Установить второй элемент (строка 0, столбец 1)
m( 1, 2 ) = 0.f; // Установить седьмой элемент (строка 1, столбец 2)
Матрицы в OpenGL представляют собой одномерные массивы, в
документации по OpenGL обычно ссылаются как на матрицу хранимую
по столбцам:
┌
┐
│m0 m4
m8
m12 │
│m1 m5
m9
m13 │
│m2 m6
m10 m14 │
│m3 m7
m11 m15 │
└
┘
GLfloat m[ 16 ];
Вводное Руководство в OpenSceneGraph
m[1] = 0.f; // Установить второй элемент
m[6] = 0.f; // Установить седьмой элемент
Не смотря на эти очевидные различия, расположение матриц OSG и
OpenGL в памяти совпадает—OSG не тратит время на
транспонирование, прежде чем передать ее в OpenGL. Тем не менее,
как разработчик вы должны помнить о транспонированном
представлении OSG, прежде чем обращаться к ее отдельным
элементам.
Matrix обладает широким набором операций для векторно-матричного
умножения и объединения матриц. Для преобразования Vec3 v
посредством вращения R вокруг положения T, используйте
следующий код:
osg::Matrix T;
T.makeTranslate( x, y, z );
osg::Matrix R;
R.makeRotate( angle, axis );
Vec3 vPrime = v * R * T;
Стиль прямого умножения операторов класса Matrix противоположен
тому, как это описывается в документации по
OpenGL:
v' = TRv
Такая запись в OpenGL дает такой же результат,
поскольку в OpenGL запись обратного умножения с
матрицами ориентированными по столбцам
эквивалентна прямому умножению OSG
операторов матриц ориентированных по строкам.
Для воздействия на весь Geode с помощью
данного преобразования, создайте узел
MatrixTransform содержащий T, добавьте к нему
дочерний узел MatrixTransform содержащий R, и
добавьте Geode как дочерний к узлу MatrixTransform содержащий
вращение, как показано на диаграмме. Это эквивалентно следующей
последовательности OpenGL команд:
glMatrixMode( GL_MODELVIEW );
glTranslatef( ... ); // Translation T
glRotatef( ... ); // Rotation R
Построение Графа Сцены
...
glVertex3f( ... );
glVertex3f( ... );
...
В общем OSG работает с интерфейсом ориентированным по строкам,
в то время как в OpenGL документирована запись по столбцам, OSG и
OpenGL внутри производят эквивалентные операции и их матрицы
100% совместимы.
Quat предоставляет богатый интерфейс настройки. Следующий код демонстрирует
несколько методов создания и настройки кватернионов.
// Создать кватернион, поворачивающий оси на угол theta радиан.
float theta( M_PI * .5f );
osg::Vec3 axis( .707f, .707f, 0.f );
osg::Quat q0( theta, axis );
// Создать кватернион, использующий рыск/крен/тангаж
osg::Vec3 yawAxis( 0.f, 0.f, 1.f );
osg::Vec3 pitchAxis( 1.f, 0.f, 0.f );
osg::Vec3 rollAxis( 0.f, 1.f, 0.f );
// (Код в этом примере предполагает что переменные
// объявлены и определены как flot ранее.)
osg::Quat q1( yawRad, yawAxis, pitchRad, pitchAxis, rollRad, rollAxis );
// Объединение двух кватернионов
q0 *= q1;
// Настройка PositionAttitudeTransform с использованием
// setPosition() and setAttitude() методов.
// (x, y, z, theta, и axis объявлены и определены
// ранее.)
osg::Vec3 pos( x, y, z );
osg::Quat att( theta, axis );
osg::ref_ptr<osg::PositionAttitudeTransform> pat = new
osg::PositionAttitudeTransform;
pat->setPosition( pos );
pat->setAttitude( att );
Вы можете добавлять в PositionAttitudeTransform столько дочерних узлов, сколько
необходимо вашему приложению, поскольку PositionAttitudeTransform наследует
дочерний интерфейс от Group. Подобно MatrixTransform, узел
PositionAttitudeTransform трансформирует дочернюю геометрию по заданной позиции и
отношению.
2.3.4 Узел LOD
Вводное Руководство в OpenSceneGraph
Используйте узел osg::LOD для отображения объектов с различными степенями
детализации. LOD наследуется от Group и следовательно наследует дочерний интерфейс.
LOD так же позволяет вам задавать диапазон для каждого потомка. Диапазон состоит из
значений минимума и максимума. Эти значения представляют собой расстояния по
умолчанию, и LOD отображает потомка в случае если расстояние до наблюдателя попадает в
диапазон потомка. LOD потомки могут храниться в любом порядке и нет необходимости в
сортировке по расстоянию степени детализации.
На Рисунке 2-6 показан узел LOD с тремя потомками. Первый потомок это узел Group со
своими потомками. Когда расстояние до точки наблюдения попадет в диапазон первого
потомка, OSG совершает обход его и его потомков. Узел LOD действует аналогично по
отношению ко второму и третьему потомкам. OSG может ничего не отобразить, какойнибудь или все потомки LOD, основываясь на их диапазонах.
osg::ref_ptr<osg::Geode> geode;
...
osg::ref_ptr<osg::LOD> lod = new osg::LOD;
// Отобразить geode в случае когда 0.f <= distance < 1000.f
lod->addChild( geode.get(), 0.f, 1000.f );
LOD отобразит множество потомков одновременно случае наложения их диапазонов.
osg::ref_ptr<osg::Geode> geode0, geode1;
// Инициализация Geode.
...
osg::ref_ptr<osg::LOD> lod = new osg::LOD;
Рисунок 2-6
Узел LOD
На этом рисунке изображен узел LOD с тремя потомками. Каждый потомок имеет
диапазон расстояний. Узел LOD позволяет отображать своих потомков в случае
если расстояние до точки наблюдения попадает в диапазон потомков.
// Отобразить geode0 когда 0.f <= distance < 1050.f
Построение Графа Сцены
lod->addChild( geode0.get(), 0.f, 1050.f );
// Отобразить geode1 когда 950.f <= distance < 2000.f
lod->addChild( geode1.get(), 950.f, 2000.f );
// Результат: отобразится geode0 и geode1 когда 950.f <= distance < 1050.f
По умолчанию узел LOD вычисляет расстояние от глаза до центра ограничивающего
объема. Если это не приемлемо в вашей ситуации, вы можете определить новый центр.
Следующий код настраивает определенный пользователем центр узла LOD.
osg::ref_ptr<osg::LOD> lod = new osg::LOD;
// Использовать определенный пользователем центр для расчетов дальности
lod->setCenterMode( osg::LOD::USER_DEFINED_CENTER );
// Задать определенный пользователем центр x=10 y=100
Lod->setCenter( osg::Vec3( 10.f, 100.f, 0.f ) );
Что бы восстановить расчет дальности на поведение по умолчанию использующее центр
ограничивающей сферы, вызовите osg::LOD::setCenterMode(
osg::LOD::USE_BOUNDING_SPHERE_CENTER ).
Значения минимума и максимума по умолчанию являются расстоянием. Тем не менее вы
можете настроить LOD так чтобы интерпретировать диапазон значений в размеры в
пикселях, и LOD отобразит потомка если его размер на экране в пикселях попадет в его
диапазон.
The minimum and maximum values represent distance by default. However, you can
configure LOD to interpret the range values as pixel sizes, and LOD displays a child if
its on-screen pixel size falls within the child’s corresponding range. To configure a
LOD node’s range mode, call osg::LOD::setRangeMode() and pass in either
osg::LOD::DISTANCE_FROM_EYE_POINT or
osg::LOD::PIXEL_SIZE_ON_SCREEN.
Download