netlib.narod.ru | < Назад | Оглавление | Далее > |
В приложении Cube мы создали сетку, содержащую только одну группу граней. Все шесть граней куба являлись частью одной группы и изменялись как единая сущность.
Однако, сетка может содержать и несколько групп граней. Каждая из групп добавляется к сетке с помощью функции AddGroup() интерфейса Direct3DRMMesh. Позднее для получения данных или модификации группы используется идентификатор, назначаемый группе функцией AddGroup(). Использование нескольких групп позволяет назначать различные цвета, текстуры и материалы отдельным фрагментам одной сетки.
Приложение Cube2 получило свое название по нескольким причинам. Во-первых, эта программа является непосредственным развитием приложения Cube. Во-вторых, куб в приложении Cube2 представлен с помощью двух групп граней.
Сетка в приложении Cube2 анимируется точно таким же образом, как и в приложении Cube, но в новом приложении также выполняется анимация цвета второй группы граней (трех граней куба). Внешний вид окна приложения Cube2 показан на рис. 8.2.
Рис. 8.2. Приложение Cube2
Приложение Cube2 демонстрирует следующие технологии:
Все эти темы мы обсудим подробнее в ходе изучения кода программы Cube2.
Функциональность приложения Cube2 реализована классом Cube2Win:
class Cube2Win : public RMWin { public: Cube2Win(); BOOL CreateScene(); protected: //{{AFX_MSG(Cube2Win) afx_msg void OnRenderGroup1Flat(); afx_msg void OnRenderGroup1Wireframe(); afx_msg void OnRenderGroup1Gouraud(); afx_msg void OnRenderGroup2Wireframe(); afx_msg void OnRenderGroup2Flat(); afx_msg void OnRenderGroup2Gouraud(); afx_msg void OnUpdateRenderGroup1Wireframe(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGroup1Flat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGroup1Gouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGroup2Wireframe(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGroup2Flat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGroup2Gouraud(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void UpdateCube(LPDIRECT3DRMFRAME, void*, D3DVALUE); static void UpdateColors(LPDIRECT3DRMFRAME, void*, D3DVALUE); private: LPDIRECT3DRMMESH mesh; D3DRMGROUPINDEX group1, group2; };
В классе объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор инициализирует члены данных класса. Функция CreateScene() создает сетку, источник света и порт просмотра.
Код функции CreateScene() из приложения Cube2 приведен в листинге 8.2.
Листинг 8.2. Функция Cube2Win::CreateScene() |
BOOL Cube2Win::CreateScene() { //---------- СЕТКА -------- d3drm->CreateMesh(&mesh); mesh->AddGroup(12, 3, 4, vertorder, &group1); mesh->AddGroup(12, 3, 4, vertorder, &group2); mesh->SetVertices(group1, 0, 12, vertexlist); mesh->SetVertices(group2, 0, 12, vertexlist + 12); mesh->Translate(D3DVALUE(-0.5), D3DVALUE(-0.5), D3DVALUE(-0.5)); mesh->Scale(D3DVALUE(15), D3DVALUE(15), D3DVALUE(15)); //--------- ТЕКСТУРА ---------- HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_WIN95TEXTURE), "TEXTURE"); LPDIRECT3DRMTEXTURE texture; d3drm->LoadTextureFromResource(texture_id, &texture); mesh->SetGroupTexture(group1, texture); mesh->SetGroupMapping(group1, D3DRMMAP_PERSPCORRECT); mesh->SetGroupTexture(group2, texture); mesh->SetGroupMapping(group2, D3DRMMAP_PERSPCORRECT); texture->Release(); texture = 0; //---------- ФРЕЙМ ---------- LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(mesh); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(-.05)); static CallbackData cbdata; cbdata.mesh = mesh; cbdata.group1 = group1; cbdata.group2 = group2; meshframe->AddMoveCallback(UpdateCube, &cbdata); meshframe->AddMoveCallback(UpdateColors, &cbdata); meshframe->Release(); meshframe = 0; // -------- СВЕТ -------- LPDIRECT3DRMLIGHT dlight, alight; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &dlight); d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.50), D3DVALUE(0.50), D3DVALUE(0.50), &alight); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(5), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); lightframe->AddLight(dlight); lightframe->AddLight(alight); dlight->Release(); dlight = 0; alight->Release(); alight = 0; lightframe->Release(); lightframe = 0; //------ КАМЕРА ---------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; } |
Функция CreateScene() выполняет следующие действия:
На первом этапе выполняется создание сетки:
d3drm->CreateMesh(&mesh); mesh->AddGroup(12, 3, 4, vertorder, &group1); mesh->AddGroup(12, 3, 4, vertorder, &group2); mesh->SetVertices(group1, 0, 12, vertexlist); mesh->SetVertices(group2, 0, 12, vertexlist + 12); mesh->Translate(D3DVALUE(-0.5), D3DVALUE(-0.5), D3DVALUE(-0.5)); mesh->Scale(D3DVALUE(15), D3DVALUE(15), D3DVALUE(15));
Сначала для инициализации указателя mesh вызывается функция CreateMesh() интерфейса Direct3DRM. Затем к пустой сетке добавляются две группы граней, каждая из которых создается функцией AddGroup(). Каждая группа включает 12 вершин: 3 грани по 4 вершины в каждой. В качестве третьего аргумента функции AddGroup() используется массив vertorder. В приложении Cube массив vertorder содержал 24 элемента; в приложении Cube2 в этом массиве только 12 элементов:
unsigned vertorder[] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
Последний аргумент функции AddGroup() является адресом идентификатора групп граней сетки. Функция AddGroup() назначает каждой группе граней уникальное значение идентификатора.
Теперь, чтобы присвоить значения вершинам каждой группы, воспользуемся функцией SetVertices(). Первым параметром этой функции является идентификатор группы вершин. Второй аргумент — это индекс первой изменяемой вершины. Третий аргумент — число вершин, которым будут присвоены новые значения. Последний, четвертый аргумент является массивом структур D3DRMVERTEX. Обратите внимание, что для инициализации двух групп граней используется один и тот же массив структур. Во втором вызове функции SetVertices() указано смещение, приводящее к тому, что для инициализации второй группы граней используется вторая половина массива.
Затем выполняется вызов функции Translate() интерфейса Direct3DRMMesh и функции Scale(). В отличие от функций AddGroup() и SetVertices(), функции Translate() и Scale() модифицируют все входящие в сетку группы. Вызов этих функций ничем не отличается от использованного в приложении Cube.
На втором этапе выполняется создание текстуры и ее наложение на обе группы граней сетки:
HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_WIN95TEXTURE), "TEXTURE"); LPDIRECT3DRMTEXTURE texture; d3drm->LoadTextureFromResource(texture_id, &texture); mesh->SetGroupTexture(group1, texture); mesh->SetGroupMapping(group1, D3DRMMAP_PERSPCORRECT); mesh->SetGroupTexture(group2, texture); mesh->SetGroupMapping(group2, D3DRMMAP_PERSPCORRECT); texture->Release(); texture = 0;
На третьем этапе создается фрейм для сетки и устанавливаются две функции обратного вызова:
LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(mesh); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(-.05)); static CallbackData cbdata; cbdata.mesh = mesh; cbdata.group1 = group1; cbdata.group2 = group2; meshframe->AddMoveCallback(UpdateCube, &cbdata); meshframe->AddMoveCallback(UpdateColors, &cbdata); meshframe->Release(); meshframe = 0;
Фрейм создается функцией CreateFrame() интерфейса Direct3DRM и используется для размещения сетки. После этого фрейму назначаются атрибуты вращения.
Затем инициализируется экземпляр статической структуры CallbackData. Структура модифицирована таким образом, чтобы в ней можно было хранить идентификаторы обеих групп граней сетки.
Функция AddMoveCallback() интерфейса Direct3DRMFrame используется для установки двух функций обратного вызова: UpdateCube() и UpdateColors(). Каждая функция обратного вызова будет получать при вызове указатель на объявленную ранее статическую структуру данных. Функция UpdateCube() выполняет анимацию вершин для обоих групп граней сетки. Функция UpdateColors() выполняет анимацию цвета второй группы граней.
На двух заключительных этапах выполняется создание двух источников света и порта просмотра. В этой главе мы не будем обсуждать данные действия.
Функция UpdateCube() выполняет две задачи: она осуществляет анимацию вершин и, кроме того, она периодически изменяет атрибуты вращения сетки.
void Cube2Win::UpdateCube(LPDIRECT3DRMFRAME frame, void* p, D3DVALUE) { CallbackData* data = (CallbackData*)p; static const D3DVALUE lim = D3DVALUE(5); static D3DVALUE control; static D3DVALUE inc = D3DVALUE(.25); static D3DRMVERTEX vert[12]; data->mesh->GetVertices(data->group1, 0, 12, vert); vert[0].position.x += inc; vert[0].position.y += inc; vert[0].position.z += inc; vert[6].position.x += inc; vert[6].position.y += inc; vert[6].position.z += inc; vert[8].position.x += inc; vert[8].position.y += inc; vert[8].position.z += inc; data->mesh->SetVertices(data->group1, 0, 12, vert); data->mesh->GetVertices(data->group2, 0, 12, vert); vert[2].position.x += inc; vert[2].position.y += inc; vert[2].position.z += inc; vert[6].position.x += inc; vert[6].position.y += inc; vert[6].position.z += inc; vert[8].position.x += inc; vert[8].position.y += inc; vert[8].position.z += inc; data->mesh->SetVertices(data->group2, 0, 12, vert); control += inc; if (control > lim || control < -lim) inc = -inc; static UINT delay; if (++delay < 20) return; delay = 0; LPDIRECT3DRMFRAME scene; frame->GetScene(&scene); D3DVECTOR spinvect; D3DRMVectorRandom(&spinvect); D3DVALUE spin = D3DDivide(rand() % 50 + 1, 400); frame->SetRotation(scene, spinvect.x, spinvect.y, spinvect.z, spin); }
При изменении местоположения вершин сетки функция UpdateCube() использует следующие статические переменные:
static const D3DVALUE lim = D3DVALUE(5); static D3DVALUE control; static D3DVALUE inc = D3DVALUE(.25); static D3DRMVERTEX vert[12];
Переменные lim, control и inc применяются для управления анимацией вершин. Массив vert используется для временного хранения данных каждой из групп граней сетки. Обратите внимание, что в данном случае массив состоит из 12 элементов, а в приложении Cube в массиве vert было 24 элемента.
В каждой из двух групп вершин сетки есть анимируемые вершины, поэтому функции GetVertices() и SetVertices() применяются для изменения параметров обоих групп вершин сетки:
data->mesh->GetVertices(data->group1, 0, 12, vert); vert[0].position.x += inc; vert[0].position.y += inc; vert[0].position.z += inc; vert[6].position.x += inc; vert[6].position.y += inc; vert[6].position.z += inc; vert[8].position.x += inc; vert[8].position.y += inc; vert[8].position.z += inc; data->mesh->SetVertices(data->group1, 0, 12, vert); data->mesh->GetVertices(data->group2, 0, 12, vert); vert[2].position.x += inc; vert[2].position.y += inc; vert[2].position.z += inc; vert[6].position.x += inc; vert[6].position.y += inc; vert[6].position.z += inc; vert[8].position.x += inc; vert[8].position.y += inc; vert[8].position.z += inc; data->mesh->SetVertices(data->group2, 0, 12, vert);
Массив vert используется для хранения данных обоих групп вершин. Оставшаяся часть функции UpdateCube() полностью аналогична функции UpdateCube() приложения Cube.
Функция UpdateColors() выполняет анимацию цветов для второй группы граней сетки. Код функции выглядит следующим образом:
void Cube2Win::UpdateColors(LPDIRECT3DRMFRAME, void* p, D3DVALUE) { CallbackData* data = (CallbackData*)p; static D3DVALUE clr = D3DVALUE(.5); static D3DVALUE inc = D3DVALUE(.2); clr += inc; if (clr < D3DVALUE(.3) || clr > D3DVALUE(1)) { inc = -inc; clr += inc; } data->mesh->SetGroupColorRGB(data->group2, clr, D3DVALUE(0), D3DVALUE(0)); }
Переменная clr используется для вычисления и хранения текущего цвета группы граней сетки. Сразу после вычисления значения переменной clr, оно устанавливается с помощью функции SetGroupColorRGB() интерфейса Direct3DRMMesh. Анимируется только красная составляющая цвета группы граней сетки. Зеленая и синяя составляющая цвета всегда равны нулю.
В приложении Cube2 меню Render немного изменено, по сравнению с его обычным вариантом, поскольку каждая группа граней может иметь свои собственные параметры визуализации. По этой причине меню Render состоит из двух подменю — по одному для каждой группы граней сетки. Структура меню показана на рис. 8.3.
Рис. 8.3. Меню Render приложения Cube2
Для реализации меню Render приложения Cube2 требуется двенадцать функций, но все они отличаются друг от друга весьма незначительно. Ниже приведен пример одной из функций, вызываемых при выборе одного из пунктов меню:
void Cube2Win::OnRenderGroup1Wireframe() { if (mesh) mesh->SetGroupQuality(group1, D3DRMRENDER_WIREFRAME); }
Данная функция отвечает за работу пункта меню Render|Group1|Wireframe и использует функцию SetGroupQuality() интерфейса Direct3DRMMesh чтобы установить каркасный метод визуализации. Обратите внимание, что в первом аргументе функции SetGroupQuality() передается идентификатор группы граней group1.
Приведем также пример функции, устанавливающей флажок рядом с пунктом меню, соответствующим текущему методу визуализации группы граней сетки:
void Cube2Win::OnUpdateRenderGroup1Flat(CCmdUI* pCmdUI) { if (mesh) { D3DRMRENDERQUALITY meshquality = mesh->GetGroupQuality(group1); pCmdUI->SetCheck(meshquality == D3DRMRENDER_FLAT); } }
Данная функция предназначена для плоского режима визуализации первой группы граней сетки.
netlib.narod.ru | < Назад | Оглавление | Далее > |