В предыдущем разделе была представлена концепция управления памятью в OSG. Если вам не знаком подсчет ссылок памяти, то лучше посмотреть на реальные OSG примеры, для лучшего понимания. В этом разделе представлена простая OSG программа, которая использует технику управления памятью описанную ранее, и знакомит вас с построением графа сцены с помощью OSG классов содержащих геометрию. На первый взгляд код может показаться громоздким, поскольку вы еще не знакомы со многими классами. Подробное объяснение геометрических классов дается в комментариях в исходном коде. Листинг 2-1 широко использует шаблонный класс ref_ptr<>, описанный в предыдущем разделе. Все выделения памяти в Листинге 2-1 с подсчетом ссылок. Даже функция createSceneGraph() возвращает ref_ptr<>, созданного графа сцены. (Проще говоря, код в Листинге 2-1 мог бы быть написан полностью с использованием обычных C++ указателей, а возвращенный указатель сохранялся бы в ref_ptr<>. Тем не менее, является хорошей практикой использование ref_ptr<> в вашем приложении, поскольку это автоматизирует освобождение памяти даже в случае исключений или преждевременного выхода из функции. В примерах кода этой книги используется ref_ptr<> для поддержки этой хорошей практики.) Листинг 2-1 Построение простого графа сцены Это отрывок из кода Простого примера идущего вместе с книгой. Функция 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 Запись графа сцены на диск В этом листинге показана точка входа main() примера Простой программы. Из 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 из Простого примера идущего вместе книгой. Если вы этого еще не сделали, скачайте исходный код примера с 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 текстурных координат. В дополнение к простому хранению эти классы обеспечивают полный набор методов для расчета длины, векторного и скалярного произведения, сложения векторов и векторноматричного умножения. В 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: