ГЛАВА 7. Пример работы с Direct3D в среде Visual Basic
Предположим вам поставили задачу написать программу, моделирующую прокладку трубопроводов в трехмерном пространстве. Для прокладки труб их приходится изгибать. Представим трубу из двенадцати полосок и соответственно из двадцати четырех треугольников, к вершинам треугольников проведены нормали (Рис.
1).
Рис. 1
В проекте из папки N15 вы найдете программу в которой подход к решению задачи изгиба трубы осуществляется на уровне треугольников. Без освещения труба выглядит неестественно. Для правильного расчета освещения необходимо задать нормаль, которая является перпендикуляром к плоскости в которой лежит треугольник. Для расчета нормали к произвольному треугольнику воспользуемся векторами. Вектором называется направленный отрезок. Операции над векторами бывают линейными и не линейными. К линейные операциям относятся сложение и вычитание векторов, а к не линейным векторное произведение двух векторов. Для нахождения нормали к треугольнику потребуются обе операции, они представлены на рисунке
2.
Рис. 2
Нормаль это тоже вектор. Нормаль направлена перпендикулярно плоскости треугольника и представлена на рисунке
2 как vecn. Векторы (радиус-векторы) vecta, vecb и vectc направлен из начала координат в вершины треугольника. Разность векторов vecta и vecb дает вектор vecp, а разность векторов veca и vecc дает вектор vecq. Вектор нормали к плоскости в которой лежит треугольник находится по формуле sin a * |vectp| * |vectq| = |vectn|. Для работы с углами и треугольниками необходимо воспользоваться тригонометрией, но наличие в DirectX библиотеки Direct3DX упрощает эту задачу. Последовательность действий для нахождения нормали, изложенная выше сохраняется и при использовании Direct3DX. Например функция D3DXVec3Subtract vecp, vecb, veca производит вычитание векторов, а результат записывает в вектор vecp. Функция D3DXVec3Subtract vecq, vecc, veca делает тоже самое для vecq. Имея два пересекающихся вектора vecq и vecp (в принципе они описывают плоскость) для нахождения нормали можно подставить их в функцию D3DXVec3Cross vecn, vecp, vecq. В результате получаем вектор vectn перпендикулярный плоскости в которой лежит треугольник ( тригонометрические преобразования скрыты и производятся не явно). В функции D3DXVec3Normalize vecn, vecn происходит нормализация вектора. Теперь DirectX знает как построить угол падения и отражения для света и на поверхности появятся блики. Построение трубы происходит в функции DrawTruba из модуля Vertex (Module3.bas). В самом начале функции DrawTruba объявлены переменные:
Dim Vertices(143) As CUSTOMVERTEX
Dim vect As D3DVECTOR
Dim veca As D3DVECTOR
Dim vecb As D3DVECTOR
Dim vecc As D3DVECTOR
Dim vecp As D3DVECTOR
Dim vecq As D3DVECTOR
Dim vecn As D3DVECTOR
Первая переменная это массив для хранения всех вершин трубы. Векторы имеют тоже самое значение, что и на рисунке
2. Код строящий первую из двенадцати полосок (два треугольника) из которых состоит труба имеет вид:
vect.X = 0: vect.Y = di * 0.2: vect.z = 0
Vertices(0).postion = vect '0
veca = vect
vect.X = 0: vect.Y = di * 0.2: vect.z = (r2 - 1) / 2
Vertices(1).postion = vect '1
vecb = vect
vect.X = di * -0.1: vect.Y = di * 0.16: vect.z = 0
Vertices(2).postion = vect '2
vecc = vect
D3DXVec3Subtract vecp, vecb, veca
D3DXVec3Subtract vecq, vecc, veca
D3DXVec3Cross vecn, vecp, vecq
D3DXVec3Normalize vecn, vecn
Vertices(0).normal = vecn
Vertices(1).normal = vecn
Vertices(2).normal = vecn
vect.X = di * -0.1: vect.Y = di * 0.16: vect.z = (r2 - 1) / 2
Vertices(3).postion = vect '3
Vertices(3).normal = vecn
vect.X = di * 0: vect.Y = di * 0.2: vect.z = (r2 - 1) / 2
Vertices(4).postion = vect '4
Vertices(4).normal = vecn
vect.X = di * -0.1: vect.Y = di * 0.16: vect.z = 0
Vertices(5).postion = vect '5
Vertices(5).normal = vecn
Таким методом строятся еще одиннадцать полосок первой трубы, вторая часть трубы (см. рис. 30) и вставка между трубами изображающая изгиб. Для одной из половинок трубы, которая изменяет угол применяется выражение:
teta = (r2 - 1) * -Sin(ugol / 57.32) / 2 + (di * 0.2) - (di * 0.2 * Cos(ugol / 57.32))
teta1 = ((r2 - 1) * Cos(ugol / 57.32) - (r2 - 1)) + (di * 0.4 * -Sin(ugol / 57.32))
Переменные r и di ведены для изменения радиуса и диаметра. Для вставки соединяющей две трубы применен тот же прием. Каждых из кусков трубы состоит из треугольников которые объединены в три вершинных буфера:
Set g_VertBufVstavka = g_DEVICE8.CreateVertexBuffer(24 * 72, _
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT)
D3DVertexBuffer8SetData g_VertBufVstavka, 0, 24 * 72, 0, VerticesVst(0)
Set g_VertBufTruba1 = g_DEVICE8.CreateVertexBuffer(24 * 72, _
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT)
D3DVertexBuffer8SetData g_VertBufTruba1, 0, 24 * 72, 0, Vertices(0)
Set g_VertBufTruba2 = g_DEVICE8.CreateVertexBuffer(24 * 72, _
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT)
D3DVertexBuffer8SetData g_VertBufTruba2, 0, 24 * 72, 0, Vertices(72)
Труба может перемещаться в трехмерной сцене. За изменение геометрии объекта отвечают матрицы, конечные преобразование которых производятся в пользовательской функции Truba, с помощью функций библиотеки Direct3DX - D3DXMatrixRotationX и D3DXMatrixMultiply. В первом случае входными параметрами являются угол и результирующая матрица, а во втором три матрицы описывающие начальное состояние объекта, трансформацию объекта и результирующую матрицу. Приложение в работе представлено на рисунке
3.
Рис. 3
Приложение имеет относительно большой размер. Это приложение предназначено для самостоятельного изучения в редакторе кода Visual Basic. В нем нет ничего, что бы мы не разобрали выше. Приложение снабжено интерфейсом состоящим из двух диалоговых окон для рисования сетки и изменения геометрии трубы. С помощью кнопок правой панели можно изменять положение камеры, а при нажатой левой клавиши мыши можно вращать всю сцену. В DirectX версии семь уровень треугольника программировать было еще сложнее, в DirectX восемь программировать на этом уровне проще, но все равно утомительно. К счастью в прикладных задачах работают с уже созданными трехмерными примитивами. Перемещение примитивов в трехмерной сцене осуществляется по средствам матриц. Матрица была придумана не для специфического представления координат в трехмерной графике, а для решения больших систем уравнений, задолго до появления персональных компьютеров. Матрица это математическая абстракция, представляющая собой двухмерный массив. Поставьте точку после любой переменной объявленной как матричный тип и сосчитайте число переменных входящих в структуру матрицы. Все матрицы в примерах этой книги имеют размер 4 на 4 или двухмерный массив [4][4] (четыре столбика и четыре строчки). Так как число строк и столбиков равно такая матрица называется квадратной. Две матрицы одинакового размера можно складывать в результате получится третья матрица с конечным результатом. Предположим у вас есть матрица описывающая положение наблюдателя в трехмерной сцене или камеры или объекта. Вы складываете ее с другой матрицей в которой записаны необходимые изменения над объектом (вращение, изменение позиции, масштабирование, деформации) в результате получается конечная матрица. Самое главное, что матрицы можно применять как бы в составе конвейера – сначала матрица перемещения (переноса), потом поворота, потом масштабирования. Так как матрицы разрабатывались для решения групп уравнений то и решением будут переменные удовлетворяющие каждому изменению объекта. Методы решения уравнений в матричной форме разрабатывались еще в восемнадцатом веке (швейцарский математик Крамер Г 1704 - 1752). Если вы серьезно хотите заняться разработкой графических движков трехмерной графики без предварительного изучения тригонометрии, аналитической геометрии, матриц, хотя бы основ высшей математики и языка подобного С++ это занятие не имеет смысла. Но если вы только хотите применять трехмерную графику в своей повседневной профессиональной деятельности прекрасно подойду такие графические движки, как DirectX. Как уже было сказано в вашем распоряжении имеется библиотека Direct3DX. Это библиотека инкапсулирует всю матричную математику. Для демонстрации матриц в работе я написал простое приложение (см. проект папка N16/), перемещающие простой примитив кубик (Рис.
4)
Рис. 4
В данном приложении нет работы на уровне треугольников, два объекта кубик и шарик достаются даром, так как поддержка этих примитивов уже встроена в Direct3D. Внимательно проработайте приложение в редакторе Visual Basic. Приемы работы с матрицами по перемещению кубика подойдут для любых объектов, которые вы создадите в будущем. Кроме того приложение демонстрирует очень полезный прием - выделение мышкой объекта в сцене. Такое выделение уже применялось в примере для Direct3D Retained Mode из седьмой версии DirectX, там где по щелчку мыши клапан менял свой цвет. В данном примере после щелчка мыши на примитиве его название появляется в заголовки формы это - “Шар” или “Куб” (см. рис.
4).
Работа с трехмерной сценой на уровне треугольников крайне утомительна и для ее облегчения предназначены различные файловые форматы хранения трехмерной графики. Родным форматом файла хранения трехмерной сцены в DirectX является файл формата X. Как правило трехмерную сцену (или ее элементы) создают в коммерческом редакторе способном создавать файлы с расширением 3DS с последующим преобразованием в файл форматаX. Для преобразования из формата 3DS в формат X в составе SDK имеется утилита conv3ds.exe. Для использования этой утилиты вам необходимо нарисовать в доступном вам трехмерном редакторе трехмерную сцену или ее элемент. Редактор не обязательно должен быть 3D Studio MAX или AutoCAD (хотя конечно это самое лучшее, но и самое дорогое решение). Существуют ознакомительные условно-бесплатные версии трехмерных редакторов, способные создавать файлы 3DS. Если вы применяете пиратские программы для создания коммерческих приложений вы должны быть готовы, что вам предъявят судебный иск (хотя для нашей страны это не очень актуально). Создайте простой файл в формате 3DS и с помощью утилиты conv3ds.exe конвертируйте его в формат X, но не в бинарную версию, а версию представленную ASCII последовательностью (проще говоря текстовый файл). Откройте файл в блокноте или другом текстовом редакторе поддерживающем текстовые файлы ASCII. Если не хотите возиться с рисованием и преобразованием можете воспользоваться файлом G.x из проекта в папке N17. Первым идет заголовок (так же как и файле типа BMP) который указывает, анализирующей его программе, что это файл содержит данные трехмерной графики, проще говоря это файл формата X в текстовой форме. Далее идет шаблон вернее его описание:
Header {
1;
0;
1;}
Удалите из файла этот шаблон он не понадобиться. Далее идет описание шаблона фрейма - Frame x3ds_Cylinder01. Фрейм это как бы коробка в которую можно положить цилиндр. Фреймы применяются в анимации и представляют собой фантомные объекты из которых строится анимационная последовательность. Например представьте руку робота которая держит автомат. Плече, предплечье, кисть и автомат, описываются самостоятельными фреймами. Тело робота главный (родительский) фрейм по отношению к частям руки. Фрейм включает в себя всю геометрию сцены. Удалите из файла запись с именем описания шаблона и открывающей его фигурной скобкой Frame x3ds_Cylinder01 {. Перейдите в самый конец файла и удалите самую последнюю фигурную скобку файла. Полностью удалите описание шаблона, в который описана матрица положения фрейма:
FrameTransformMatrix {
1.000000, 0.000000, 0.000000, 0.000000,
0.000000, -0.000000, -1.000000, 0.000000,
0.000000, 1.000000, -0.000000, 0.000000,
-1.859360, 2.579185, -0.000000, 1.000000;;
}
Переименуйте описание шаблона Mesh Cylinder01 в Mesh MyCylinder. За описанием шаблона сетки треугольников (из которых состоит труба) следует описание нормалей к этим треугольникам, которые описаны после шаблона MeshNormals. На этом переделки в текстовом файле формата X завершены. В результате мы получили практически те же самые данные из которых в предыдущем примере в ручную строили трубу. В проекта из папки N17 находятся файл L.x, который был получен из файла G.x. Просмотрите этот файл в текстовом редакторе:
xof 0303txt 0032
Mesh MyCylinder {
26;
0.000000; 0.000000; 0.000000;,
20.952505; 0.000000; 0.000000;,
18.145401; 0.000000; 10.476253;,
10.476252; 0.000000; 18.145403;,
-0.000001; -0.000000; 20.952505;,
-10.476254; -0.000000; 18.145401;,
-18.145403; -0.000000; 10.476252;,
-20.952505; 0.000000; -0.000002;,
-18.145401; -0.000000; -10.476254;,
-10.476251; -0.000000; -18.145403;,
0.000003; -0.000000; -20.952505;,
10.476255; 0.000000; -18.145401;,
18.145403; 0.000000; -10.476250;,
20.952505; 156.556290; -0.000000;,
18.145401; 156.556290; 10.476253;,
10.476252; 156.556290; 18.145403;,
-0.000001; 156.556290; 20.952505;,
-10.476254; 156.556290; 18.145401;,
-18.145403; 156.556290; 10.476252;,
-20.952505; 156.556290; -0.000002;,
-18.145401; 156.556290; -10.476254;,
-10.476251; 156.556290; -18.145403;,
0.000003; 156.556290; -20.952505;,
10.476255; 156.556290; -18.145401;,
18.145403; 156.556290; -10.476250;,
0.000000; 156.556290; -0.000000;;
48;
3;0,1,2;,
3;0,2,3;,
3;0,3,4;,
3;0,4,5;,
3;0,5,6;,
3;0,6,7;,
3;0,7,8;,
3;0,8,9;,
3;0,9,10;,
3;0,10,11;,
3;0,11,12;,
3;0,12,1;,
3;1,13,14;,
3;1,14,2;,
3;2,14,15;,
3;2,15,3;,
3;3,15,16;,
3;3,16,4;,
3;4,16,17;,
3;4,17,5;,
3;5,17,18;,
3;5,18,6;,
3;6,18,19;,
3;6,19,7;,
3;7,19,20;,
3;7,20,8;,
3;8,20,21;,
3;8,21,9;,
3;9,21,22;,
3;9,22,10;,
3;10,22,23;,
3;10,23,11;,
3;11,23,24;,
3;11,24,12;,
3;12,24,13;,
3;12,13,1;,
3;25,14,13;,
3;25,15,14;,
3;25,16,15;,
3;25,17,16;,
3;25,18,17;,
3;25,19,18;,
3;25,20,19;,
3;25,21,20;,
3;25,22,21;,
3;25,23,22;,
3;25,24,23;,
3;25,13,24;;
MeshNormals {
50;
0.000000;-1.000000;0.000000;,
0.000000;-1.000000;0.000000;,
1.000000;0.000000;0.000000;,
0.000000;-1.000000;0.000000;,
0.866025;0.000000;0.500000;,
-0.000000;-1.000000;0.000000;,
0.500000;-0.000000;0.866025;,
0.000000;-1.000000;-0.000000;,
-0.000000;0.000000;1.000000;,
-0.000000;-1.000000;-0.000000;,
-0.500000;0.000000;0.866025;,
0.000000;-1.000000;0.000000;,
-0.866025;0.000000;0.500000;,
-0.000000;-1.000000;-0.000000;,
-1.000000;0.000000;-0.000000;,
-0.000000;-1.000000;0.000000;,
-0.866025;0.000000;-0.500000;,
-0.000000;-1.000000;0.000000;,
-0.500000;0.000000;-0.866025;,
0.000000;-1.000000;0.000000;,
0.000000;0.000000;-1.000000;,
0.000000;-1.000000;0.000000;,
0.500000;0.000000;-0.866025;,
0.000000;-1.000000;0.000000;,
0.866026;-0.000000;-0.500000;,
1.000000;0.000000;0.000000;,
0.000000;1.000000;0.000000;,
0.866025;0.000000;0.500000;,
0.000000;1.000000;0.000000;,
0.500000;0.000000;0.866025;,
0.000000;1.000000;0.000000;,
-0.000000;0.000000;1.000000;,
0.000000;1.000000;0.000000;,
-0.500000;0.000000;0.866025;,
0.000000;1.000000;0.000000;,
-0.866025;0.000000;0.500000;,
0.000000;1.000000;0.000000;,
-1.000000;0.000000;-0.000000;,
0.000000;1.000000;0.000000;,
-0.866025;0.000000;-0.500000;,
0.000000;1.000000;0.000000;,
-0.500000;0.000000;-0.866026;,
0.000000;1.000000;0.000000;,
0.000000;0.000000;-1.000000;,
0.000000;1.000000;0.000000;,
0.500000;0.000000;-0.866025;,
0.000000;1.000000;0.000000;,
0.866026;0.000000;-0.500000;,
0.000000;1.000000;0.000000;,
0.000000;1.000000;0.000000;;
48;
3;0,1,3;,
3;0,3,5;,
3;0,5,7;,
3;0,7,9;,
3;0,9,11;,
3;0,11,13;,
3;0,13,15;,
3;0,15,17;,
3;0,17,19;,
3;0,19,21;,
3;0,21,23;,
3;0,23,1;,
3;2,25,27;,
3;2,27,4;,
3;4,27,29;,
3;4,29,6;,
3;6,29,31;,
3;6,31,8;,
3;8,31,33;,
3;8,33,10;,
3;10,33,35;,
3;10,35,12;,
3;12,35,37;,
3;12,37,14;,
3;14,37,39;,
3;14,39,16;,
3;16,39,41;,
3;16,41,18;,
3;18,41,43;,
3;18,43,20;,
3;20,43,45;,
3;20,45,22;,
3;22,45,47;,
3;22,47,24;,
3;24,47,25;,
3;24,25,2;,
3;49,28,26;,
3;49,30,28;,
3;49,32,30;,
3;49,34,32;,
3;49,36,34;,
3;49,38,36;,
3;49,40,38;,
3;49,42,40;,
3;49,44,42;,
3;49,46,44;,
3;49,48,46;,
3;49,26,48;;
}
}
Если вы хотите посмотреть, что же скрывается за всеми этими цифрами воспользуйтесь программой MeshView (поставляемой в составе SDK). Как видите программа MeshView загрузила текстовый файл оформленный определенным образом. Как видно из рисунка
5 программа может отображать треугольники и нормали, записанные в текстовой форме.
Рис. 5
Простые текстовые файлы, оформленные определенным образом могут загружаться и в приложения использующие DirectX, что избавляет разработчика от утомительного описания данных внутри программы. В проекте из папки N17 приведена программа конвертирующая файл из формата X в обычный текстовый файл TEST. Текст можно хранить в ресурсах, в динамических библиотеках, передавать по сети или даже оформить в виде XML тегов. Допустим вам надо описать не один объект “труба”, а много объектов (деталей какого либо механизма) в текстовом форме и подгружать из текстовых ресурсов графику в программу. Такой прием демонстрируется в следующей программе из папки N18. Благодаря трем интерфейсным переменным, объявленным в модуле Mesh.bas -
Dim pIDirectXF As DirectXFile, Dim pIDirectXFEnum As DirectXFileEnum и
Dim pIDirectXFData As DirectXFileData.
Первая переменная инкапсулирует возможности файл X. Ссылка на интерфейс для работы с данными X файлов в программе происходит с использованием ключевого слова Set в функции g_DX8.DirectXFileCreate. Обратите внимание, что переменная g_DX8 создается в модуле GlobalVar.bas, как глобальная переменная уровня приложения с помощью библиотеки типов, так же как и самый верхний уровень иерархии DirectX8. После создания ссылки на интерфейс DirectXFile нужно произвести регистрацию шаблонов. За регистрацию шаблонов отвечает функция pIDirectXF.RegisterDefaultTemplates (в нашем случае все шаблоны стандартные). Кстати если вы читали книгу Джима Адамса – “DIRECTX продвинутая анимация” то заметили, различия этого процесса в VC++ и VB. Теперь можно работать с данными трехмерной графики хранящимися в текстовом виде (ходьбы и в файлах с расширением txt). Теперь нужно создать перечисление объектов (каждой объект представляет готовый элемент сцены) из которых состоит трехмерная графика. На рисунке
6 показано из каких объектов состоит сцена.
Рис. 6
Каждый объект сцены можно выводить по отдельности или все сразу. Для управлением выводом технических объектов служат кнопки с флажками. Сейчас создан как бы пустой X файл. Для насыщения X файла с помощью текстовых данных создадим объект перечисления - Set pIDirectXFEnum = pIDirectXF.CreateEnumObject(App.Path & "\SETKA.txt"). Если все прошло нормально переменная pIDirectXFEnum будет указывать на первый элемент сцены с названием Mesh SistemP. Просмотрите файл SETKA.TXT в любом текстовом редакторе. Первыми идут данные для треугольников и нормалей для объектов SistemP, затем Pkorpus, Lkorpus и Mehanizm. Представьте, что как в примере с трубой их надо было вручную закодировать в программу. Это просто невозможно. Заполнение объекта перечисления данными происходит в функции - Set pIDirectXFData = pIDirectXFEnum.GetNextDataObject. Для вывода в рабочую область программы нужны объекты типа D3DXMesh, ссылки на которые хранятся в модуле GlobalVar.bas :
Public pIMesh As D3DXMesh
Public pIMesh1 As D3DXMesh
Public pIMesh2 As D3DXMesh
Public pIMesh3 As D3DXMesh
Загрузка в первый объект типа D3DXMesh происходит в функции –
Set pIMesh = g_D3DX8.LoadMeshFromXof(pIDirectXFData, D3DXMESH_MANAGED, g_DEVICE8, Nothing, Nothing, 0).
Переход к следующему объекту осуществляется новой инициализацией объекта перечисления (освобождением указателей полностью занимается среда VB, в C++ обычно вызывают pIDirectXFData->Release();). Полностью код создания из текстового файла объектов D3DXMesh выглядит так:
Set pIDirectXF = g_DX8.DirectXFileCreate()
pIDirectXF.RegisterDefaultTemplates
Set pIDirectXFEnum = pIDirectXF.CreateEnumObject(App.Path & "\SETKA.txt")
Set pIDirectXFData = pIDirectXFEnum.GetNextDataObject
If TypeName(pIDirectXFData) = "Nothing" Then Exit Sub
Set pIMesh = g_D3DX8.LoadMeshFromXof(pIDirectXFData, D3DXMESH_MANAGED, g_DEVICE8, Nothing, Nothing, 0)
Set pIDirectXFData = pIDirectXFEnum.GetNextDataObject
If TypeName(pIDirectXFData) = "Nothing" Then Exit Sub
Set pIMesh1 = g_D3DX8.LoadMeshFromXof(pIDirectXFData, D3DXMESH_MANAGED, g_DEVICE8, Nothing, Nothing, 0)
Set pIDirectXFData = pIDirectXFEnum.GetNextDataObject
If TypeName(pIDirectXFData) = "Nothing" Then Exit Sub
Set pIMesh2 = g_D3DX8.LoadMeshFromXof(pIDirectXFData, D3DXMESH_MANAGED, g_DEVICE8, Nothing, Nothing, 0)
Set pIDirectXFData = pIDirectXFEnum.GetNextDataObject
If TypeName(pIDirectXFData) = "Nothing" Then Exit Sub
Set pIMesh3 = g_D3DX8.LoadMeshFromXof(pIDirectXFData, D3DXMESH_MANAGED, g_DEVICE8, Nothing, Nothing, 0)
Часто перечисления объектов происходит в цикле на подобие циклов в которых перебор происходит пока не достигнут признак конца файла. Окончательный вывод трехмерных объектов происходит в функции Render с использованием следующего кода:
If FlagMesh = 1 Then
g_DEVICE8.SetMaterial MATERIAL(1)
pIMesh.DrawSubset 0
End If
If FlagMesh1 = 1 Then
g_DEVICE8.SetMaterial MATERIAL(2)
pIMesh1.DrawSubset 0
End If
If FlagMesh2 = 1 Then
g_DEVICE8.SetMaterial MATERIAL(3)
pIMesh2.DrawSubset 0
End If
If FlagMesh3 = 1 Then
g_DEVICE8.SetMaterial MATERIAL(4)
pIMesh3.DrawSubset 0
End If
If FlagSetka = 1 Then
Setka
End If
Создание каркаса приложения происходит так же как и в примерах описанных выше. В данном приложении отсутствует работа на уровне треугольников. Некоторые отечественные книги описывают создание вершинных буферов и только (с использованием учебных примеров SDK). Данный пример демонстрирует, что продуктивная работа возможна только при наличии мощного файлового формата для хранения трехмерной графики в паре с программой моделирования.