Приложение SpaceStation — это одна из самых простых демонстрационных программ на CD-ROM. В приложении используется одна сетка и один источник света, и нет никаких функций обратного вызова или текстур. Сцена приложения SpaceStation конструируется функцией SpaceStationWin::CreateScene(), код которой показан в листинге 6.3.
Листинг 6.3. Функция SpaceStationWin::CreateScene() |
BOOL SpaceStationWin::CreateScene() { // ------- СЕТКА -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_STATIONMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); ScaleMesh(meshbuilder, D3DVALUE(32)); // ------ ФРЕЙМ СЕТКИ -------- LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.05)); meshframe->Release(); meshframe = 0; // --------- ИСТОЧНИК СВЕТА И ЕГО ФРЕЙМ -------- LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &light); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(light); lightframe->SetOrientation(scene, D3DVALUE(-1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); light->Release(); light = 0; lightframe->Release(); lightframe = 0; //------ КАМЕРА ---------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(25), D3DVALUE(-50)); camera->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-23), D3DVALUE(50), D3DVALUE(0.7), D3DVALUE(1), D3DVALUE(0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; } |
Функция CreateScene() выполняет следующие действия:
Код, выполняющий первый из перечисленных этапов, показан ниже:
D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_STATIONMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); ScaleMesh(meshbuilder, D3DVALUE(32));Сначала объявляется экземпляр структуры D3DRMLOADRESOURCE, используемой для хранения данных о местоположении сетки космической станции в ресурсах программы. Затем для инициализации указателя meshbuilder вызывается функция CreateMeshBuilder() интерфейса Direct3DRM. Указатель meshbuilder является членом класса SpaceStationWin и поэтому не объявлен в функции CreateScene(). Потом функция Load() интерфейса Direct3DRMMeshBuilder используется для загрузки сетки космической станции. Здесь не осуществляется никакой проверки ошибок, поскольку сетка является частью исполняемого файла программы. Для прошедшей тестирование программы (как данный пример), успешное выполнение функции Load() гарантировано. После возврата из функции Load() вызывается функция ScaleMesh() чтобы придать сетке наиболее подходящий размер.
На следующем (втором) этапе создается фрейм для сетки космической станции:
LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.05)); meshframe->Release(); meshframe = 0;Как только фрейм создан, представляющий космическую станцию объект meshbuilder присоедияется к новому фрейму (meshframe) с помощью функции AddVisual() интерфейса Direct3DRMFrame. Затем вызывается функция SetRotation() для назначения фрейму атрибутов вращения. Аргументы, передаваемые функции SetRotation() задают поворот сетки вокруг оси Y на 0,5 радиана при каждом обновлении изображения сцены. После выполнения всех этих действий указатель meshframe освобождается и обнуляется.
Далее функция CreateScene() создает источник направленного света и присоединяет его к фрейму:
LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &light); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(light); lightframe->SetOrientation(scene, D3DVALUE(-1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); light->Release(); light = 0; lightframe->Release(); lightframe = 0;Здесь в коде объявлен указатель на интерфейс Direct3DRMLight с именем light, который инициализируется функцией CreateLightRGB() интерфейса Direct3DRM. Константа D3DRMLIGHT_DIRECTIONAL задает тип создаваемого источника света. Следующие три аргумента определяют цвет источника света (в данном случае — белый), а последний аргумент является адресом инициализируемого указателя light.
Теперь пора создать фрейм для источника света. Указатель lightframe инициализируется функцией CreateFrame(), после чего источник света присоединяется к новому фрейму с помощью функции AddLight() интерфейса Direct3DRMFrame.
Для ориентации источника света применяется функция SetOrientation(). Обратите внимание, что лицевой вектор (вектор, определенный первыми тремя аргументами) направлен от начала координат вдоль осей X и Z. Отрицательное значение по оси X указывает, что свет будет падать слева направо (с точки зрения зрителя), а положительное значение по оси Z указывает, что свет будет падать от зрителя в глубину сцены. На рис. 6.6 показано направление света по отношению к сетке космической станции (вид сверху).
Рис. 6.6. Вид сверху на сцену приложения SpaceStation
После выполнения ориентации источника света указатели light и lightframe освобождаются.
Четвертый, и последний фрагмент кода функции SpaceStationWin::CreateScene() создает порт просмотра и настраивает его параметры. Взгляните еще раз на приложение SpaceStation (рис. 6.5). Обратите внимание, что сетка космической станции наклонена. Это не вызвано тем, что мы наклонили фрейм к которому присоединена сетка космической станции. Фактически, мы только лишь назначили фрейму атрибуты вращения, чтобы он поворачивался вокруг оси Y. И все же, если вы запустите приложение SpaceStation, космическая станция будет наклонена и вращаться в том направлении, в котором наклонена.
Это вызвано тем, что вместо того, чтобы наклонять и сетку и вектор вращения, мы наклонили порт просмотра. Код создания порта просмотра для этого приложения выглядит так:
d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(25), D3DVALUE(-50)); camera->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-23), D3DVALUE(50), D3DVALUE(0.7), D3DVALUE(1), D3DVALUE(0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);Эта часть кода отличается от других приложений, рассмотренных нами к этому моменту. Во-первых, здесь по-другому позиционируется фрейм camera. Обычно мы перемещали камеру от начала координат в направлении зрителя по прямой, передавая функции SetPosition() в качестве аргументов значения <0, 0, –50>. В этот раз мы перемещаем фрейм camera от начала координат на 50 единиц к зрителю и на 25 единиц вверх. По умолчанию фрейм направлен вдоль оси Z. Это значит, что если мы не изменим ориентацию фрейма camera, то, скорее всего, вообще не увидим сетку космической станции, поскольку поле зрения камеры будет расположено выше сетки.
Вызов функции SetOrientation(), размещенный вслед за вызовом функции SetPosition(), используется для двух целей. Во-первых, он необходим, чтобы направить камеру на сетку. Во вторых, он применяется, чтобы наклонить камеру. Давайте внимательно посмотрим на вызов функции SetOrientation():
camera->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-23), D3DVALUE(50), D3DVALUE(0.7), D3DVALUE(1), D3DVALUE(0));Лицевой вектор (определяемый первыми тремя числовыми аргументами) направлен от начала координат к точке, расположенной на 23 единицы ниже и на 50 единиц дальше. Обратите внимание, что этот вектор почти полностью противоположен вектору, который был использован при позиционировании фрейма камеры. Это не случайность. Фактически, вы всегда можете добиться, чтобы камера была направлена на начало координат, указав в качестве лицевого вектора вектор, противоположный тому, который использовался при позиционировании камеры. Для создания дополнительного визуального эффекта лицевой вектор фрейма камеры был слегка изменен (методом проб и ошибок), поэтому два вектора в программе не являются в точности противоположными.
Теперь давайте взглянем на верхний, или небесный, вектор (определенный последними тремя числовыми аргументами функции SetOrientation()). В других демонстрационных программах мы использовали значения <0, 1, 0>, чтобы задать вектор, направленный вверх вдоль оси Y (отсюда и название вектора). На этот раз мы используем значения <0.7, 1, 0>. Задав вектор, который указывает почти настолько же далеко вправо, насколько вверх, мы наклоняем фрейм камеры вправо.
Последней функцией, вызываемой из CreateScene(), является функция CreateViewport() интерфейса Direct3DRM. Этот вызов функции ничем не отличается от других программ.