Введение в программирование трехмерных игр с DX9

Пример приложения: прогрессивная сетка



11.3.4. Пример приложения: прогрессивная сетка

Приложение Progressive Mesh очень похоже на пример XFile, за исключением того, что в нем мы создаем и визуализируем прогрессивную сетку, которая, соответственно, будет представлена интерфейсом ID3DXPMesh. Пользователь может изменять уровень детализации сетки с помощью клавиатуры. Чтобы увеличить количество граней сетки надо нажать клавишу A, а чтобы удалить грани из сетки — нажать клавишу S.

Используемые в данном примере глобальные переменные похожи на глобальные переменные приложения XFile, за исключением того, что мы добавили переменную для хранения прогрессивной сетки:

ID3DXMesh* SourceMesh = 0; ID3DXPMesh* PMesh = 0; // прогрессивная сетка std::vector<D3DMATERIAL9> Mtrls(0); std::vector<IDirect3DTexture9*> Textures(0);

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

bool Setup() { HRESULT hr = 0;

// ...Код загрузки данных из X-файла в SourceMesh пропущен // // ...Извлечение материалов и текстур пропущено

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

// // Создание прогрессивной сетки //

hr = D3DXGeneratePMesh( SourceMesh, (DWORD*)adjBuffer->GetBufferPointer(), // смежность граней 0, // использовать веса атрибутов вершин по умолчанию 0, // использовать веса вершин по умолчанию 1, // упрощать насколько возможно D3DXMESHSIMP_FACE, // упрощать по числу граней &PMesh);

d3d::Release<ID3DXMesh*>(SourceMesh); // закончили работу с исходной сеткой d3d::Release<ID3DXBuffer*>(adjBuffer); // закончили работу с буфером

if(FAILED(hr)) { ::MessageBox(0, "D3DXGeneratePMesh() - FAILED", 0, 0); return false; }


Обратите внимание, что хотя мы и указали возможность упрощения сетки до одной грани, обычно такого упрощения не происходит из-за весов вершин и атрибутов; указание 1 приводит к упрощению сетки до минимально возможного разрешения.
Теперь прогрессивная сетка создана, но если мы ее визуализируем прямо сейчас, то она будет изображена с минимально возможным разрешением. Поскольку мы хотим визуализировать сетку с максимальным разрешением, необходимо установить его:
// установить максимальную детализацию DWORD maxFaces = PMesh->GetMaxFaces(); PMesh->SetNumFaces(maxFaces);
В функции Display мы проверяем нажатие клавиш A и S и обрабатываем их:
bool Display(float timeDelta) { if( Device ) { // // Обновление: Смена разрешения сетки //
// Получаем текущее количество граней в сетке pmesh int numFaces = PMesh->GetNumFaces();
// Добавляем грань. Обратите внимание, что SetNumFaces() // автоматически корректирует значение, если оно // выходит за допустимые границы. if(::GetAsyncKeyState('A') & 0x8000f) { // Иногда из-за деталей внутренней реализации // интерфейса ID3DXPMesh, для того, чтобы инвертировать // преобразование слияния граней необходимо добавить // более одной грани. Другими словами, иногда // в результате добавления одной грани количество граней // сетки может остаться неизменным. В таком случае // для увеличения счетчика количества граней // необходимо добавить сразу две грани. PMesh->SetNumFaces(numFaces + 1); if(PMesh->GetNumFaces() == numFaces) PMesh->SetNumFaces(numFaces + 2); }
// Удаляем грань. Обратите внимание, что SetNumFaces() // автоматически корректирует значение, если оно // выходит за допустимые границы. if(::GetAsyncKeyState('S') & 0x8000f) PMesh->SetNumFaces(numFaces - 1);
Код достаточно прямолинеен, следует только обратить внимание, что иногда для инверсии преобразования слияния граней необходимо добавить к сетке не одну грань, а две.
В завершение мы визуализируем объект ID3DXPMesh точно так же, как визуализировали объект ID3DXMesh.


Кроме того, мы желтыми линиями рисуем треугольные ячейки сетки, для чего рисуем сетку в каркасном режиме установив материал желтого цвета. Мы делаем это для того, чтобы можно было видеть добавление и удаление отдельных треугольников при увеличении и уменьшении уровня детализации прогрессивной сетки.
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene();
for(int i = 0; i < Mtrls.size(); i++) { Device->SetMaterial(&Mtrls[i]); Device->SetTexture(0, Textures[i]); PMesh->DrawSubset(i);
// рисуем каркас сетки Device->SetMaterial(&d3d::YELLOW_MTRL); Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); PMesh->DrawSubset(i); Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); }
Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } // конец функции Display

Содержание раздела