Сцена приложения 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.