Введение в OpenSceneGraph: Базовые сведения о механизме работы с геометрией
Библиотека OpenGL, на базе которой построен OpenSceneGraph, использует геометрические примитивы (такие как точки, линии, треугольники и полигональные грани) для построения всех объектов трехмерного мира.
Эти примитивы задаются данными об их вершинах, в которые входят координаты вершин, компоненты нормалей, данные о цвете и текстурные координаты. Эти данные могут хранится в специальных массивах. Примитивы могут быть сформированы путем разыменования и индексирования элементов массива. Этот метод называется методом массива вершин, он позволяет исключить хранение в памяти избыточных вершин и обладает хорошим быстродействием.
Кроме того, OpenGL может использовать механизм так называемых дисплейных списков, когда однажды подготовленные в видеопамяти примитивы могут использоваться повторно, что существенно ускоряет отображение статических объектов.
По-умолчанию OSG использует метод массивов вершин и метод дисплейных списков для рендеринга геометрии. Однако, стратегия рендеринга может быть изменена, в зависимости от того, каким образом представлены данные о геометрии.
Классы Geode и Drawable
Класс osg::Geode представляет собой оконечный, так называемый “листовой” узел дерева сцены. Он не может иметь дочерних узлов, но при этом содержит всю необходмую информацию для рендеринга геометрии. Его имя - Geode есть сокращение от слов geometry node.
Геометрические данные, подлежащие обработке движком запоминаются в наборе объектов класса osg::Drawable, управляемых классом osg::Geode. Класс osg::Drawable не инстанцируется и является чисто виртуальным классом. От него наследуются ряд подклассов, представляющих собой трехмерные модели, изображения и текст, обрабатываемые конвейером OpenGL. Под drawable в OSG понимаются все элементы, которые могут быть отрендерены движком.
Класс osg::Geode предоставляет ряд методов для присоединения и отсоединения drawables:
- Публичный метод addDrawable() - передает указатель на drawable элемент в экземпляр класса osg::Geode. Все эти элементы управляются посредством умных указателей osg::ref_ptr<>.
- Публичный метод removeDrawable() и removeDrawables() удаляет объект из osg::Geode и уменьшает счетчик ссылок на него. Метод removeDrawable() принимает в качестве единственного параметра указатель на интересующий элемент, а метод removeDrawables() принимает два параметра: начальный индекс и число элементов, подлежащих удалению из массива объектов osg::Geode.
- Метод getDrawable() возвращает указатель на элемент по передаваемому в качестве параметра индексу.
- Метод getNumDrawables() возвращает общее число элементов, прикрепленных к osg::Geode. Например, для удаления всех элементов из osg::Geode можно использовать такой код
geode->removeDrawables(0, geode->getNumDrawables());
Рендеринг простейших фигур
OSG предоставляет класс osg::ShapeDrawable, являющийся наследником класса osg::Drawable, и предназначенный для создания простейших трехмерных примитивов. Этот класс включает в себя объект osg::Shape, хранящий информацию о спецефической геометрии и ещё параметрах. Генерация примитивов осуществляется с помощью метода setShape(), например
shapeDrawable->setShape(new osg::Box(osg::Vec3(1.0f, 0.0f, 0.0f), 10.0f, 10.0f, 5.0f));
создает прямоугольный параллелепипед с геометрическим центром в точке (1.0, 0.0, 0.0) c шириной и высотой 10 и глубиной 5 единиц. Класс osg::Vec3 определяет вектор в трехмерном пространстве (кроме того, представлены и классы osg::Vec2 и osg::Vec4 описывающие векторы соответствующей размерности).
Наиболее популярные примитивы представлены в OSG классами osg::Box, osg::Capsule, osg::Cone, osg::Cylinder и osg::Sphere.
Рассмотрим пример применения данного механизма.
main.h
#ifndef MAIN_H
#define MAIN_H
#include <osg/ShapeDrawable>
#include <osg/Geode>
#include <osgViewer/Viewer>
#endif // MAIN_H
main.cpp
#include "main.h"
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
osg::ref_ptr<osg::ShapeDrawable> shape1 = new osg::ShapeDrawable;
shape1->setShape(new osg::Box(osg::Vec3(-3.0f, 0.0f, 0.0f), 2.0f, 2.0f, 1.0f));
osg::ref_ptr<osg::ShapeDrawable> shape2 = new osg::ShapeDrawable;
shape2->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 1.0f));
shape2->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
osg::ref_ptr<osg::ShapeDrawable> shape3 = new osg::ShapeDrawable;
shape3->setShape(new osg::Sphere(osg::Vec3(3.0f, 0.0f, 0.0f), 1.0f));
shape3->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
osg::ref_ptr<osg::Geode> root = new osg::Geode;
root->addDrawable(shape1.get());
root->addDrawable(shape2.get());
root->addDrawable(shape3.get());
osgViewer::Viewer viewer;
viewer.setSceneData(root.get());
return viewer.run();
}
Данный пример особенно не нуждается в комментариях: в программе создаются три простейшие фигуры, после компиляции и запуска мы увидим такой результат
Механизм, приведенный в примере прост и понятен, однако не является самым эффективным способом создания геометрии и может использоваться исключительно для тестов. Для создания геометрии в высокопроизводительных приложениях на базе OSG используется класс osg::Geometry.