Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming / GameProg / RPG_Programming_2ed.pdf
Скачиваний:
240
Добавлен:
12.02.2016
Размер:
12.06 Mб
Скачать

Джим Адамс

Таблица 8.1.(продолжение) Переменные cNodeTreeMesh

Переменная Описание

m_ParentNode m_NumGroups m_Groups m_NumPolygons m_MaxPolygons

Голова связанного списка узлов Количество групп материалов Массив групп материалов

Количество полигонов в исходной сетке

Хранит максимальное количество полигонов, допустимое для узла (при его превышении происходит разделение узла)

m_PolygonList

Массив структур sPolygon, хранящих информацию о

 

каждом полигоне сетки

m_Mesh

m_VertexPtr

m_VertexFVF m_VertexSize

Указатель на исходную сетку (структуру sMesh, используемую графическим ядром)

Используется для доступа к буферу вершин исходной сетки

Дескриптор FVF исходной сетки

Хранит размер одной вершины исходной сетки (в байтах)

Продолжайте чтение главы, и я укажу, когда переменные класса

вступают в игру, а сейчас пришло время больше узнать о функциях класса cNodeTreeMesh.

cNodeTreeMesh::Create и cNodeTreeMesh::Free

Функция cNodeTreeMesh::Create — это место где начинается весь

процесс. Для создания структуры NodeTree вам необходима исходная сетка с которой мы будем работать, и лучше всего взять ее из объекта cMesh.

Объект cMesh хранит список сеток, содержащихся в одном X-файле, и мы для простоты будем иметь дело только с первой сеткой из списка.

Передав функции Create указатель на инициализированный объект cGraphics, предварительно загруженный объект cMesh и тип дерева, который вы хотите использовать (QUADTREE или OCTREE), вы

инициализируете класс, после чего он готов к визуализации.

Помимо уже упомянутых аргументов вам надо задать максимальное количество полигонов для узла и максимальный размер узла, прежде чем ему потребуется разделение (когда он содержит максимально возможное для узла количество полигонов).

BOOL cNodeTreeMesh::Create(cGraphics *Graphics, cMesh *Mesh,

 

 

int TreeType,

 

{

float MaxSize, long MaxPolygons)

 

*LoadMesh;

 

ID3DXMesh

 

unsigned short *IndexPtr;

 

 

 

 

 

netlib.narod.ru

403

Глава 8. Создание трехмерного графического движка

unsigned long

*Attributes;

float

MaxX, MaxY, MaxZ;

unsigned long

i;

//Освобождаем предыдущую сетку

Free();

//Проверка ошибок

if((m_Graphics = Graphics) == NULL) return FALSE;

if(Mesh == NULL) return FALSE;

if(!Mesh->GetParentMesh()->m_NumMaterials) return FALSE;

// Получаем

сведения о сетке

m_Mesh

 

= Mesh->GetParentMesh();

LoadMesh

 

= m_Mesh->m_Mesh;

m_VertexFVF

 

= LoadMesh->GetFVF();

m_VertexSize

 

= D3DXGetFVFVertexSize(m_VertexFVF);

m_NumPolygons

= LoadMesh->GetNumFaces();

m_MaxPolygons

= MaxPolygons;

// Создаем список полигонов и групп

m_Polygons

=

new sPolygon[m_NumPolygons]();

m_NumGroups

=

m_Mesh->m_NumMaterials;

m_Groups

=

new sGroup[m_NumGroups]();

// Блокируем буфер индексов и буфер атрибутов

LoadMesh->LockIndexBuffer(D3DLOCK_READONLY,(void**)&IndexPtr); LoadMesh->LockAttributeBuffer(D3DLOCK_READONLY, &Attributes);

Здесь мы видим кое-что новое. ID3DXMesh использует буфер вершин; кроме того, ID3DXMesh использует буфер индексов! Вы только что читали

об этом в предыдущем разделе; D3DX использует буферы вершин и индексов для экономии памяти и ускорения визуализации, поскольку буфер индексов позволяет во время визуализации несколько раз использовать одну и ту же вершину без необходимости несколько раз сохранять координаты вершины в буфере вершин.

Также у объекта ID3DXMesh есть нечто, называемое буфер атрибутов (attribute buffer), представляющий собой массив значений, определяющих какой материал какой полигон использует. Между элементами массива и полигонами существует отношение один к одному. Именно этот буфер атрибутов используется, чтобы включать полигоны в соответствующие им группы материалов.

В последующем коде начинается перебор всех полигонов сетки с извлечением трех вершин, используемых для конструирования каждого полигона. Помимо этого, вы в структуре данных полигона сохраняете материал, применяемый для рисования этого полигона.

// Загрузка данных полигонов в структуры for(i = 0; i < m_NumPolygons; i++) {

m_Polygons[i].Vertex0 = *IndexPtr++; m_Polygons[i].Vertex1 = *IndexPtr++; m_Polygons[i].Vertex2 = *IndexPtr++;

404

netlib.narod.ru

Джим Адамс

m_Polygons[i].Group = Attributes[i]; m_Polygons[i].Timer = 0; m_Groups[Attributes[i]].NumPolygons++;

}

// Разблокирование буферов

LoadMesh->UnlockAttributeBuffer(); LoadMesh->UnlockIndexBuffer();

// Построение индексных буферов групп вершин for(i = 0; i < m_NumGroups; i++) {

if(m_Groups[i].NumPolygons != 0) m_Graphics->GetDeviceCOM()->CreateIndexBuffer(

m_Groups[i].NumPolygons * 3 * sizeof(unsigned short),

D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED,

&m_Groups[i].IndexBuffer, NULL);

}

В показанном выше коде массив индексов и буфер атрибутов сетки заблокированы. После этого вы просто строите список данных полигонов (какие вершины образуют полигон и какая группа материалов используется). Завершив построение списка данных полигонов, вы разблокируете буферы индексов и атрибутов и формируете фактические группы материалов.

Каждая группа материалов содержит объект IDirectIndexBuffer9

для хранения индексов вершин, которые надо рисовать. Чтобы создать этот буфер индексов вы используете функцию CreateIndexBuffer, во многом похожую на функцию CreateVertexBuffer, о которой вы

читали в главе 2. Единственное различие в том, что буфер индексов использует для значений индексов переменные типа short (2 байта), и

выделяет по три индекса на полигон. Обратите внимание, что нужно выделить достаточно индексов для каждого полигона, относящегося к каждой группе материалов.

// Получаем размер ограничивающего куба

MaxX = (float)max(fabs(Mesh->GetParentMesh()->m_Min.x), fabs(Mesh->GetParentMesh()->m_Max.x)); MaxY = (float)max(fabs(Mesh->GetParentMesh()->m_Min.y), fabs(Mesh->GetParentMesh()->m_Max.y));

MaxZ = (float)max(fabs(Mesh->GetParentMesh()->m_Min.z), fabs(Mesh->GetParentMesh()->m_Max.z));

m_Size = max(MaxX, max(MaxY, MaxZ)) * 2.0f; m_MaxSize = MaxSize;

// Создаем родительский узел m_ParentNode = new sNode();

Здесь вычисляется точка наиболее удаленная от центра исходной сетки. Для упрощения вычислений мир считается идеальным кубом, поэтому мы используем расстояние от центра сетки до внешнего края в качестве размера

мирового ограничивающего параллелепипеда. После создания родительского узла (m_ParentNode) вы блокируете буфер вершин сетки чтобы прочитать

координаты вершин. И, наконец, узлы сортируются путем вызова функции

SortNode.

netlib.narod.ru

405

Глава 8. Создание трехмерного графического движка

// Сортировка полигонов в узлах

LoadMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&m_VertexPtr);

SortNode(m_ParentNode, 0.0f, 0.0f, 0.0f, m_Size); LoadMesh->UnlockVertexBuffer();

m_Timer = 0;

// Сброс таймера

return TRUE;

}

После сортировки буфер вершин сетки разблокируется и функция возвращает TRUE, сообщая об успешном завершении. Теперь представление

сетки в виде узлов дерева готово к использованию. Завершив работу с этим представлением вы вызываете cNodeTreeMesh::Free для освобождения

ресурсов сетки:

BOOL cNodeTreeMesh::Free()

 

{

// Удаляем родительский и дочерние узлы

delete m_ParentNode;

m_ParentNode = NULL;

 

m_NumPolygons = 0;

// Больше нет полигонов

delete [] m_PolygonList; // Удаляем массив полигонов

m_PolygonList = NULL;

 

m_NumGroups = 0;

// Больше нет групп материалов

delete [] m_Groups;

// Удаляем группы материалов

m_Groups = NULL;

 

m_Graphics = NULL;

return TRUE;

}

cNodeTreeMesh::SortNode

cNodeTreeMesh::SortNode — это рекурсивная функция (вызывающая

сама себя), которая подсчитывает количество полигонов, находящихся в узле трехмерного пространства и решает надо ли разделить узел на четыре или

восемь дочерних узлов (в зависимости от типа дерева). После того, как узлы соответствующим образом разделены, функция SortNode строит список

полигонов, находящихся в узле трехмерного пространства.

Код функции SortNode начинается с проверки правильности

переданных ей аргументов. Затем функция сохраняет координаты узла и начинает разделение узлов.

ПРИМЕЧАНИЕ Если вы используете квадродерево, в операциях отсечения и разделения надо игнорировать высоту.

Обратите внимание, что в зависимости от режима в коде класса cNodeTreeMesh значения высоты игнорируются

или обрабатываются по-другому.

406

netlib.narod.ru

Джим Адамс

void cNodeTreeMesh::SortNode(sNode *Node,

 

float XPos, float YPos, float ZPos,

{

float Size)

 

unsigned long i, Num;

float

XOff, YOff, ZOff;

//Проверка ошибок if(Node == NULL)

return;

//Сохраняем координаты и размер узла

Node->XPos = XPos;

Node->YPos = (m_TreeType==QUADTREE)?0.0f:YPos;

Node->ZPos = ZPos;

Node->Size = Size;

// Смотрим, есть ли полигоны в узле

if(!(Num = CountPolygons(XPos, YPos, ZPos, Size))) return;

//Разделяем узел, если его размер больше максимума

//и в нем слишком много полигонов

if(Size > m_MaxSize

&& Num

> m_MaxPolygons) {

 

 

for(i=0;

i < (unsigned

long)((m_TreeType==QUADTREE)?4:8); i++) {

XOff

= (((i

% 2)

<

1)

? -1.0f

: 1.0f)

* (Size / 4.0f);

ZOff

=

(((i

%

4)

<

2)

? -1.0f

:

1.0f)

*

(Size / 4.0f);

YOff

=

(((i

%

8)

<

4)

? -1.0f

:

1.0f)

*

(Size / 4.0f);

//Смотрим, есть ли полигоны в ограничивающем

//параллелепипеде нового узла

if(CountPolygons(XPos + XOff, YPos + YOff, ZPos + ZOff, Size/2.0f)) {

//Создаем новый дочерний узел

Node->Nodes[i] = new sNode();

//Сортируем полигоны с новым дочерним узлом

SortNode(Node->Nodes[i], XPos+XOff, YPos+YOff,

ZPos+ZOff, Size/2.0f);

}

}

return;

}

К этому моменту все полигоны, находящиеся в ограничивающем параллелепипеде узла посчитаны. Если полигонов слишком много и размер ограничивающего параллелепипеда узла достаточно большой, узел разделяется. Полученные в результате разделения узлы проходят тот же

самый процесс. В процессе пустые узлы исключаются из последующей обработки, а узлы с полигонами отправляются назад функции SortNode

(это и есть рекурсия).

//Выделяем место для списка указателей на полигоны

Node->NumPolygons = Num;

Node->PolygonList = new unsigned long[Num];

//Сканируем список полигонов, сохраняя указатели и назначая их

Num = 0;

netlib.narod.ru

407

Глава 8. Создание трехмерного графического движка

for(i = 0; i < m_NumPolygons; i++) {

//Добавляем полигон к списку, если он находится

//в заданной области пространства if(IsPolygonContained(&m_PolygonList[i],

XPos, YPos, ZPos, Size) == TRUE) Node->PolygonList[Num++] = i;

}

}

Последний фрагмент кода SortNode выделяет массив индексов, указывающих на массив m_Polygons. Поскольку массив m_Polygons

содержит данные всех полигонов, имеет смысл сократить объем используемой памяти, сохраняя в массиве PolygonList индекс элемента массива m_Polygons. (Индекс 0 в PolygonList указывает на данные полигона в m_Polygons[0], индекс 1 в PolygonList указывает на данные полигона в m_Polygons[1] и т.д.)

 

Когда приходит время визуализации, этот массив указателей

 

используется для получения информации о каждом полигоне, включая

 

индексы вершин и используемую группу материалов. Рисуемые полигоны

 

позднее добавляются к буферу индексов соответствующей группы

 

материалов с помощью функции AddNode.

 

cNodeTreeMesh::IsPolygonContained и

 

cNodeTreeMesh::CountPolygons

 

Этот дуэт функций вы используете для того, чтобы определить, находится ли

 

полигон в области трехмерного пространства, заданной ограничивающим

 

параллелепипедом, и подсчитать общее количество полигонов, находящихся

 

в заданной области.

 

BOOL cNodeTreeMesh::IsPolygonContained(sPolygon *Polygon,

 

float XPos, float YPos, float ZPos,

 

float Size)

 

{

 

float XMin, XMax, YMin, YMax, ZMin, ZMax;

 

sVertex *Vertex[3];

 

// Получаем вершины полигона

 

Vertex[0] = (sVertex*)&m_VertexPtr[m_VertexSize *

 

Polygon->Vertex[0]];

 

Vertex[1] = (sVertex*)&m_VertexPtr[m_VertexSize *

 

Polygon->Vertex[1]];

 

Vertex[2] = (sVertex*)&m_VertexPtr[m_VertexSize *

 

Polygon->Vertex[2]];

 

// Проверяем ось X заданной области пространства

 

XMin = min(Vertex[0]->x, min(Vertex[1]->x, Vertex[2]->x));

 

XMax = max(Vertex[0]->x, max(Vertex[1]->x, Vertex[2]->x));

 

if(XMax < (XPos - Size / 2.0f))

 

return FALSE;

 

if(XMin > (XPos + Size / 2.0f))

 

return FALSE;

 

В показанном выше коде (а также и в показанном ниже) определяется

 

вершина, наиболее удаленная по оси X. Если полигон слишком далеко

 

 

408

netlib.narod.ru

Соседние файлы в папке GameProg