netlib.narod.ru | < Назад | Оглавление | Далее > |
Приложение Jade использует плоское наложение текстуры. Direct3D поддерживает еще два способа наложения текстуры: цилиндрический и сферический. Для цилиндрического покрытия текстура изгибается в одном направлении так, чтобы соединились ее противоположные края. Для сферического покрытия текстура деформируется так, чтобы из нее была образована сфера. Как мы вскоре увидим — данные методы покрытия всего лишь аппроксимации.
Приложение Wraps отображает три сетки: куб, цилиндр и сферу. На каждую сетку накладывается единственная текстура, но метод наложения текстуры можно изменять. По умолчанию для куба используется плоское наложение, для цилиндра — цилиндрическое и для сферы — сферическое. Чтобы изменять тип наложения текстуры на сетку, приложение предоставляет соответствующие команды меню. Окно приложения Wraps показано на рис. 5.5.
Рис. 5.5. Приложение Wraps
В приложении Wraps используется слегка модифицированная версия текстуры из приложения Jade. Линии, нарисованные поперек текстуры, делают видимой деформацию, происходящую при наложении текстуры. Измененная текстура показана на рис. 5.6.
Рис. 5.6. Текстура, используемая в приложении Wraps
Приложение Wraps демонстрирует следующие технологии:
Большая часть функциональных возможностей приложения Wraps реализуется классом WrapsWin. Давайте взглянем на определение этого класса:
class WrapsWin : public RMWin { public: WrapsWin(); BOOL CreateScene(); protected: //{{AFX_MSG(WrapsWin) afx_msg void OnWrapsFlat(); afx_msg void OnWrapsCylinder(); afx_msg void OnWrapsSphere(); afx_msg void OnWrapsReset(); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: BOOL LoadMeshes(); BOOL LoadTexture(); void ApplyWraps(); void ApplyFlat(LPDIRECT3DRMMESHBUILDER); void ApplyCylinder(LPDIRECT3DRMMESHBUILDER); void ApplySphere(LPDIRECT3DRMMESHBUILDER); private: LPDIRECT3DRMMESHBUILDER box; LPDIRECT3DRMMESHBUILDER cyl; LPDIRECT3DRMMESHBUILDER sphere; D3DRMWRAPTYPE boxwraptype; D3DRMWRAPTYPE cylwraptype; D3DRMWRAPTYPE spherewraptype; };
Также как и класс JadeWin, класс WrapsWin предоставляет две открытые функции: конструктор и функцию CreateScene(). Конструктор используется для инициализации переменных класса. Функция CreateScene() создает сцену приложения. Чуть позже функция CreateScene() будет разобрана более подробно.
В приложении Wraps отсутствует меню Render, но есть меню Wraps. Меню Wraps позволяет пользователю выбрать используемый метод наложения текстуры. Для поддержки функциональных возможностей меню Wraps в классе WrapsWin объявлены четыре защищенные функции: OnWrapsFlat(), OnWrapsCylinder(), OnWrapsSphere() и OnWrapsReset().
Кроме того, в классе объявлены шесть закрытых функций. Функции LoadMeshes() и LoadTexture() применяются для упрощения функции CreateScene(). Оставшиеся четыре функции применяются для изменения параметров наложения текстуры во время работы приложения.
Также объявлены шесть закрытых переменных. Переменные box, cyl и sphere являются указателями на интерфейс Direct3DRMMeshBuilder. Они применяются для доступа к трем сеткам, образующим сцену. Оставшиеся три переменные используются для того, чтобы определить какой тип наложения текстуры используется для каждой из сеток.
Давайте взглянем на код приложения Wraps. Как и все приложения в этой книге, данное приложение использует в качестве базового класс RMWin и добавляет функциональные возможности переопределяя функцию CreateScene(). Приложение Wraps объявляет класс WrapsWin функция CreateScene() которого отвечает за загрузку трех используемых в приложении сеток. Поскольку это приложение несколько сложнее, чем те, которые были рассмотрены нами ранее, часть работы CreateScene() выполняют вспомогательные функции. Ниже приведен код функции CreateScene() для приложения Wraps.
BOOL WrapsWin::CreateScene() { //-------- СЕТКИ И ТЕКСТУРЫ -------- if (LoadMeshes() == FALSE) return FALSE; if (LoadWrapsTexture() == FALSE) return FALSE; OnWrapsReset(); //--------- СВЕТ ---------- LPDIRECT3DRMFRAME lightframe; LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1),D3DVALUE(1), D3DVALUE(1), &light); d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(light); lightframe->Release(); lightframe = 0; light->Release(); light = 0; //---------- КАМЕРА ------------ d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; }
Функция CreateScene() выполняет следующие действия:
Для создания и инициализации сеток, текстур и наложений текстур функция CreateScene() вызывает функции LoadMeshes(), LoadWrapsTexture() и OnWrapsReset(). Как явствует из названия, функция LoadMeshes() загружает три используемые приложением сетки. Кроме того, LoadMeshes() отвечает за создание и размещение фреймов, к которым будут присоединены эти сетки. Функция LoadWrapsTexture() загружает текстуру, которая будет наложена на каждую из трех сеток.
Функция OnWrapsReset() выполняет две задачи. Она применяется в функции CreateScene() для инициализации параметров программы и, кроме того, она же является обработчиком события, вызываемым каждый раз, когда пользователь выбирает пункт Reset в меню Wraps.
Функции LoadMeshes(), LoadWrapsTexture() и OnWrapsReset() выполняют для приложения Wraps значительный объем работы и заслуживают более пристального рассмотрения.
Функция LoadMeshes() создает три сетки и присоединяет каждую из них к отдельному фрейму. Фреймы располагаются на расстоянии друг от друга и им назначаются одинаковые атрибуты вращения. Текст функции LoadMeshes() приведен в листинге 5.2.
Листинг 5.2. Функция WrapsWin::LoadMeshes() |
BOOL WrapsWin::LoadMeshes() { HRESULT r; const D3DVALUE meshscale = D3DVALUE(13); const D3DVALUE meshspacing = D3DVALUE(15); D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_BOXMESH); resinfo.lpType = "MESH" d3drm->CreateMeshBuilder(&box); r = box->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); if (r != D3DRM_OK) { TRACE("failed to load internal box mesh\n"); return FALSE; } box->SetPerspective(TRUE); ScaleMesh(box, meshscale - D3DVALUE(2)); LPDIRECT3DRMFRAME boxframe; d3drm->CreateFrame(scene, &boxframe); boxframe->SetPosition(scene, -meshspacing, D3DVALUE(0), D3DVALUE(0)); boxframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(1), D3DVALUE(.1)); boxframe->AddVisual(box); resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_CYLMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&cyl); cyl->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); if (r != D3DRM_OK) { TRACE("failed to load internal cylinder mesh\n"); return FALSE; } cyl->SetPerspective(TRUE); ScaleMesh(cyl, meshscale); LPDIRECT3DRMFRAME cylframe; d3drm->CreateFrame(scene, &cylframe); cylframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(1), D3DVALUE(.1)); cylframe->AddVisual(cyl); resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_SPHEREMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&sphere); sphere->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); if (r != D3DRM_OK) { TRACE("failed to load internal sphere mesh\n"); return FALSE; } sphere->SetPerspective(TRUE); ScaleMesh(sphere, meshscale); LPDIRECT3DRMFRAME sphereframe; d3drm->CreateFrame(scene, &sphereframe); sphereframe->SetPosition(scene, D3DVALUE(meshspacing), D3DVALUE(0), D3DVALUE(0)); sphereframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(1), D3DVALUE(.1)); sphereframe->AddVisual(sphere); return TRUE; } |
В начале функции объявляется несколько констант, используемых в коде для позиционирования и масштабирования сеток. Затем осуществляется инициализация указателя на интерфейс Direct3DRMMeshBuilder. Функция Load() загружает изображающую куб сетку из ресурсов программы.
Затем для того чтобы разрешить перспективную коррекцию применяется функция SetPerspective(). Если вы установили демонстрационные приложения на свой компьютер, можете закомментировать вызов этой функции и заново скомпилировать приложение Wraps. Перспективная коррекция особенно заметна на сетке куба из-за его больших граней.
Затем выполняется масштабирование сетки с помощью функции ScaleMesh(). Мы уже обсуждали функцию ScaleMesh() в главе 4. ScaleMesh() определена в классе RMWin и предоставляет удобный способ для масштабирования сеток таким образом, чтобы они соответствовали заданному размеру. Константа, используемая для масштабирования куба, слегка изменяется, поскольку сетки кубической формы кажутся больше, чем другие сетки (поскольку куб занимает максимальный объем в минимальном пространстве).
После того, как масштабирование сетки выполнено, функция LoadMeshes() создает фрейм и позиционирует его в соответствии с константой meshspacing. Сетка куба появляется слева от других сеток, поэтому для нее указывается отрицательная координата по оси X. Далее функция SetRotation() назначает фрейму атрибуты вращения. И, в самом конце, с помощью функции AddVisual() интерфейса Direct3DRMFrame к фрейму присоединяется сетка.
Функция LoadWrapsTexture() загружает текстуру, изображенную на рис. 5.6 и связывает ее с тремя сетками, загруженными ранее функцией LoadMeshes(). Код функции приведен ниже:
BOOL WrapsWin::LoadWrapsTexture() { HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE( IDR_JADETEXTURE), "TEXTURE"); LPDIRECT3DRMTEXTURE texture; d3drm->LoadTextureFromResource(texture_id, &texture); box->SetTexture(texture); cyl->SetTexture(texture); sphere->SetTexture(texture); texture->Release(); texture = 0; return TRUE; }
Для инициализации экземпляра структуры HRSRC в функции LoadWrapsTexture() применяется функция FindResource() из Win32 API. Структура HRSRC идентифицирует текстуру, которую мы пытаемся загрузить, и используется в качестве аргумента функции LoadTextureFromResource(). Новая текстура связывается с тремя сетками с помощью функции SetTexture() интерфейса Direct3DRMMeshBuilder. В конце функции освобождается указатель на текстуру и возвращается значение TRUE, сообщающее об успешном завершении.
Вспомните, что перед созданием источника света и порта просмотра функция CreateScene() приложения Wraps вызывает три функции: LoadMeshes(), LoadWrapsTexture() и OnWrapsReset(). Функция OnWrapsReset() присваивает значения переменным класса WrapsWin таким образом, чтобы для сетки куба использовалось плоское наложение текстуры, для сетки цнлиндра — цилиндрическое, и для сетки сферы — сферическое наложение текстуры. Вот как выглядит код функции OnWrapsReset():
void WrapsWin::OnWrapsReset() { boxwraptype = D3DRMWRAP_FLAT; cylwraptype = D3DRMWRAP_CYLINDER; spherewraptype = D3DRMWRAP_SPHERE; ApplyWraps(); }
Переменные boxwraptype, cylwraptype и spherewraptype принадлежат к классу WrapsWin и относятся к объявленному в Direct3D типу D3DRMWRAPTYPE. Функция OnWrapReset() присваивает каждой из этих переменных константы, которые также предоставляются Direct3D. В следующем разделе мы обсудим функцию ApplyWraps().
Функция ApplyWraps() отвечает за назначение каждой из сеток правильного типа покрытия текстурой в соответствии со значениями переменных класса WrapsWin. Ниже приведен код функции ApplyWraps():
void WrapsWin::ApplyWraps() { if (boxwraptype == D3DRMWRAP_FLAT) ApplyFlat(box); else if (boxwraptype == D3DRMWRAP_CYLINDER) ApplyCylinder(box); else ApplySphere(box); if (cylwraptype == D3DRMWRAP_FLAT) ApplyFlat(cyl); else if (cylwraptype == D3DRMWRAP_CYLINDER) ApplyCylinder(cyl); else ApplySphere(cyl); if (spherewraptype == D3DRMWRAP_FLAT) ApplyFlat(sphere); else if (spherewraptype == D3DRMWRAP_CYLINDER) ApplyCylinder(sphere); else ApplySphere(sphere); }
Функция ApplyWraps() определяет используемый для каждой из сеток способ наложения текстуры и вызывает необходимую функцию, чтобы применить соответствующее наложение текстуры. Поскольку все эти функции практически идентичны, мы приведем только текст функции ApplyFlat():
void WrapsWin::ApplyFlat(LPDIRECT3DRMMESHBUILDER meshbuilder) { D3DRMBOX box; meshbuilder->GetBox(&box); D3DVALUE width = box.max.x - box.min.x; D3DVALUE height = box.max.y - box.min.y; LPDIRECT3DRMWRAP wrap; d3drm->CreateWrap(D3DRMWRAP_FLAT, NULL, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось z D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось y D3DVALUE(0.5), D3DVALUE(0.5), // начало координат текстуры D3DDivide(1,width), D3DDivide(1,height), // масштаб текстуры &wrap); wrap->Apply(meshbuilder); wrap->Release(); wrap = 0; }
Сначала функция ApplyFlat() вычисляет высоту и ширину сетки, на которую наносится текстура. Функция GetBox() интерфейса Direct3DRMMeshBuilder используется для инициализации структуры D3DRMBOX размерами сетки. Затем с помощью функции CreateWrap() интерфейса Direct3DRM создается экземпляр интерфейса Direct3DRMWrap. Новое наложение применяется к сетке функцией Apply() интерфейса Direct3DRMWrap. Перед завершением функции выполняется освобождение указателя wrap.
В классе WrapsWin есть еще три функции: OnWrapsFlat(), OnWrapsCylinder() и OnWrapsSphere(). Эти функции являются обработчиками событий для меню Wraps и выглядят аналогично функции OnWrapsReset(), за исключением того, что каждая из них назначает одинаковый тип наложения текстуры для всех трех сеток. Ниже для примера приведен текст функции OnWrapsFlat():
void WrapsWin::OnWrapsFlat() { boxwraptype = D3DRMWRAP_FLAT; cylwraptype = D3DRMWRAP_FLAT; spherewraptype = D3DRMWRAP_FLAT; ApplyWraps(); }
netlib.narod.ru | < Назад | Оглавление | Далее > |