netlib.narod.ru | < Назад | Оглавление | Далее > |
Источник параллельно-точечного света подобен источнику направленного света, но испускает лучи в двух направлениях. На параллельно-точечный источник света оказывают влияние и его местоположение, и его ориентация. На быстродействие приложения такой источник света влияет так же как источник направленного света.
В приложении SpaceDonut параллельно-точечный источник света размещен между двумя пончиками с малиновой (а может быть черничной) глазурью. Окно приложения SpaceDonut показано на рис. 6.7.
Рис. 6.7. Приложение SpaceDonut
Приложение SpaceDonut демонстрирует применение следующих технологий:
Основная функциональность приложения SpaceDonut сосредоточена в классе SpaceDonutWin, объявление которого выглядит следующим образом:
class SpaceDonutWin : public RMWin { public: SpaceDonutWin(); BOOL CreateScene(); protected: //{{AFX_MSG(SpaceDonutWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESHBUILDER meshbuilder; };
Подобно всем демонстрационным приложениям на CD-ROM, программа SpaceDonut наследует свой класс окна от класса RMWin. Класс SpaceDonutWin предоставляет две открытых функции: конструктор и функцию CreateScene(). Конструктор отвечает за инициализацию любых членов данных, определенных в классе (в нашем случае, единственной переменной). Функция CreateScene() создает сцену приложения.
Шесть защищенных функций реализуют поддержку приложением меню Render. Для изменения параметров сетки эти функции используют переменную meshbuilder.
Сцена приложения SpaceDonut создается функцией CreateScene(), код которой приведен в листинге 6.4.
Листинг 6.4. Функция SpaceDonutWin::CreateScene() |
BOOL SpaceDonutWin::CreateScene() { // ------- СЕТКА ПОНЧИКА -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_DONUTMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetPerspective(TRUE); meshbuilder->SetColorRGB(D3DVALUE(1), D3DVALUE(1), D3DVALUE(1)); ScaleMesh(meshbuilder, D3DVALUE(20)); //------ ТЕКСТУРА ГЛАЗУРИ -------- LPDIRECT3DRMTEXTURE texture; HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_FROSTINGTEXTURE), "TEXTURE"); d3drm->LoadTextureFromResource(texture_id, &texture); meshbuilder->SetTexture(texture); texture->Release(); texture = 0; //------- НАЛОЖЕНИЕ ---------- D3DRMBOX box; meshbuilder->GetBox(&box); D3DVALUE w = box.max.x - box.min.x; D3DVALUE h = box.max.y - box.min.y; LPDIRECT3DRMWRAP wrap; d3drm->CreateWrap(D3DRMWRAP_FLAT, scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось Z наложения D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось Y наложения D3DVALUE(0.5), D3DVALUE(0.5), // начало координат текстуры D3DDivide(1,w), D3DDivide(1,h), // масштаб текстуры &wrap); wrap->Apply(meshbuilder); wrap->Release(); wrap = 0; //------- ФРЕЙМЫ ДЛЯ ПОНЧИКА -------- LPDIRECT3DRMFRAME leftframe; d3drm->CreateFrame(scene, &leftframe); leftframe->SetPosition(scene, D3DVALUE(-12), D3DVALUE(0), D3DVALUE(0)); leftframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0), D3DVALUE(1)); leftframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0.1)); leftframe->AddVisual(meshbuilder); leftframe->Release(); leftframe = 0; LPDIRECT3DRMFRAME rightframe; d3drm->CreateFrame(scene, &rightframe); rightframe->SetPosition(scene, D3DVALUE(12), D3DVALUE(0), D3DVALUE(0)); rightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0), D3DVALUE(1)); rightframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(-0.1)); rightframe->AddVisual(meshbuilder); rightframe->Release(); rightframe = 0; // --------- ПАРАЛЛЕЛЬНО-ТОЧЕЧНЫЙ СВЕТ -------- LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(1.0), &light); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(light); lightframe->Release(); lightframe = 0; light->Release(); light = 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() выполняет следующие действия:
Давайте рассмотрим код, выполняющий каждое из перечисленных действий. Мы начнем с первого этапа: создания и загрузки сетки пончика:
D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_DONUTMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetPerspective(TRUE); meshbuilder->SetColorRGB(D3DVALUE(1), D3DVALUE(1), D3DVALUE(1)); ScaleMesh(meshbuilder, D3DVALUE(20));
Приведенный код аналогичен коду для объекта meshbuilder в других демонстрационных программах. Загружаемая сетка идентифицируется с помощью структуры resinfo. Константа IDR_DONUTMESH — это идентификатор ресурса для импортированного в проект файла сетки. Строка "MESH" определяет категорию ресурса. Указатель meshbuilder инициализируется с помощью функции CreateMeshBuilder() интерфейса Direct3DRM, после чего, для загрузки сетки применяется функция Load(). Вызов функции SetPerspective() разрешает перспективную коррекцию текстур. Последнее действие является необязательным, и выполняется только для того, чтобы улучшить вид сетки после наложения текстуры.
Затем вызывается функция SetColorRGB() интерфейса Direct3DRMMeshBuilder, чтобы окрасить сетку в белый цвет. Это сделано потому, что используемая в данном примере сетка пончика имеет грани различных цветов. Чтобы одновременно установить цвет для всех граней сетки и используется функция SetColorRGB(). После этого вызывается функция ScaleMesh() чтобы задать размер сетки.
Код следующего этапа — создания и загрузка текстуры, изображающей глазурь на пончике — выглядит так:
LPDIRECT3DRMTEXTURE texture; HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_FROSTINGTEXTURE), "TEXTURE"); d3drm->LoadTextureFromResource(texture_id, &texture); meshbuilder->SetTexture(texture); texture->Release(); texture = 0;
Переменная texture_id идентифицирует загружаемую текстуру. Функция FindResource() применяется для задания идентификатора ресурса (IDR_FROSTINGTEXTURE) и типа ресурса ("TEXTURE").
Указатель texture инициализируется функцией LoadTextureFromResource(). Новая текстура связывается с объектом meshbuilder с помощью функции SetTexture(). После выполнения описанных действий указатель texture освобождается.
На третьем этапе работы функция SpaceDonutWin::CreateScene() создает и применяет наложение текстуры. Вот как выглядит предназначенный для этого код:
D3DRMBOX box; meshbuilder->GetBox(&box); D3DVALUE w = box.max.x - box.min.x; D3DVALUE h = box.max.y - box.min.y; LPDIRECT3DRMWRAP wrap; d3drm->CreateWrap(D3DRMWRAP_FLAT, scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось z наложения D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось y наложения D3DVALUE(0.5), D3DVALUE(0.5), // начало координат текстуры D3DDivide(1,w), D3DDivide(1,h), // масштаб текстуры &wrap); wrap->Apply(meshbuilder); wrap->Release(); wrap = 0;
Функция CreateWrap() интерфейса Direct3DRM подробно рассматривалась в главе 5 (в разделе, посвященном приложению Wraps), поэтому здесь данный код не обсуждается. Если говорить коротко, создается плоское наложение текстуры, растягивающее текстуру по размеру сетки. Затем наложение текстуры применяется к сетке с помощью функции Apply() интерфейса Direct3DRMWrap.
На четвертом этапе программа создает и размещает два фрейма. Для идентификации этих фреймов в функции CreateScene() используются переменные leftframe и rightframe. Ниже приведен код для указателя leftframe.
LPDIRECT3DRMFRAME leftframe; d3drm->CreateFrame(scene, &leftframe); leftframe->SetPosition(scene, D3DVALUE(-12), D3DVALUE(0), D3DVALUE(0)); leftframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0), D3DVALUE(1)); leftframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0.1)); leftframe->AddVisual(meshbuilder); leftframe->Release(); leftframe = 0;
Указатель leftframe инициализируется функцией CreateFrame(). Функция SetPosition() применяется, чтобы переместить фрейм на 12 единиц влево от его местоположения по умолчанию (начала координат).
Расположенный следом вызов функции SetOrientation(), необходим из-за ориентации сетки пончика в файле. Файл сетки создан таким образом, что пончик расположен вдоль оси Y (лежит лицом вниз). Мы же хотим, чтобы пончик был выровнен вдоль оси Z (стоял на боку). Чтобы сделать это, нам необходимо знать, где у сетки находятся лицевой и верхний векторы.
Значение по умолчанию для лицевого вектора объекта равно <0, 0, 1>, а значит лицевая грань объекта расположена в направлении положительных значений по оси Z. Значение по умолчанию для верхнего вектора объекта равно <0, 1, 0>. Из вышесказанного следует, что для того, чтобы сориентировать пончик желаемым образом, достаточно просто поменять местами лицевой и верхний векторы. Благодаря этому лицевая поверхность кольца будет направлена в ту сторону, куда раньше был направлен его верх, и наоборот.
Затем фрейму назначаются атрибуты вращения. Вращение осуществляется вокруг оси Y на 0.1 радиана за каждое обновление экрана. После завершения этих действий сетка пончика присоединяется к фрейму с помощью функции AddVisual() и указатель на фрейм освобождается.
Код для создания и настройки указателя rightframe выглядит аналогично коду для leftframe, поэтому здесь мы отметим только различия:
Обратите внимание, что в данном приложении один и тот же конструктор сеток присоединяется к разным фреймам. Это весьма полезная техника для отображения и анимации объектов. Однако не забывайте, что хотя на сцене будут присутствовать несколько объектов, в действительности существует только один объект. Если вы измените текстуру или цвет конструктора сеток, это изменение повлияет на все экземпляры данного объекта. Фактически, если вы запустите приложение SpaceDonut и измените параметры визуализации с помощью меню Render, вы увидите, что изменится изображение обоих экземпляров сетки.
На пятом этапе работы функции CreateScene() выполняется создание параллельно-точечного источника света и фрейма для его размещения. Код этой части выглядит так:
LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(1.0), &light); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(light); lightframe->Release(); lightframe = 0; light->Release(); light = 0;
Функция CreateLightRGB() интерфейса Direct3DRM используется для инициализации указателя light. Константа D3DRMLIGHT_PARALLELPOINT задает тип источника света, а три числовых значения определяют красную, зеленую и синюю составляющие цвета источника света (мы создаем источник белого света).
Далее указатель lightframe инициализируется с помощью функции CreateFrame() интерфейса Direct3DRM. Созданный ранее источник света присоединяется к новому фрейму с помощью функции AddLight(). Затем указатели light и lightframe освобождаются.
На заключительном, шестом этапе работы функции CreateScene() выполняется создание и размещение порта просмотра:
d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);Также, как и в других демонстрационных программах выполняется инициализация и размещение фрейма camera. В данном случае камера смещается на 50 единиц от начала координат по направлению к зрителю. Указатель viewport инициализируется функцией CreateViewport() интерфейса Direct3DRM.
netlib.narod.ru | < Назад | Оглавление | Далее > |