Программирование графики с использованием Direct3D

         

Функция TargetWin::CreateScene()


Код функции CreateScene() приложения Target приведен в листинге7.3.

Листинг 7.3. Функция TargetWin::CreateScene()

BOOL TargetWin::CreateScene() { // ------- СЕТКА ЦЕЛИ -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_SPHEREMESH); resinfo.lpType = "MESH"; LPDIRECT3DRMMESHBUILDER targetbuilder; d3drm->CreateMeshBuilder(&targetbuilder); targetbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); ScaleMesh(targetbuilder, D3DVALUE(.75)); // --------- АНИМАЦИЯ ЦЕЛИ ---------- LPDIRECT3DRMANIMATION animation; d3drm->CreateAnimation(&animation); animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); animation->AddPositionKey(D3DVALUE(0), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(-20)); animation->AddPositionKey(D3DVALUE(12), D3DVALUE(0), D3DVALUE(15), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(24), D3DVALUE(20), D3DVALUE(0), D3DVALUE(-20)); animation->AddPositionKey(D3DVALUE(35), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(49), D3DVALUE(20), D3DVALUE(0), D3DVALUE(20)); animation->AddPositionKey(D3DVALUE(65), D3DVALUE(0), D3DVALUE(15), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(74), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(20)); animation->AddPositionKey(D3DVALUE(85), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(99), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(-20)); // ---------- ФРЕЙМ ЦЕЛИ -------- LPDIRECT3DRMFRAME targetframe; d3drm->CreateFrame(scene, &targetframe); animation->SetFrame(targetframe); targetframe->AddVisual(targetbuilder); targetframe->AddMoveCallback(MoveTarget, animation); targetbuilder->Release(); targetbuilder = 0; // ------- СЕТКА РАКЕТЫ -------- resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_MISSLEMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetColorRGB( D3DVALUE(.67), D3DVALUE(.82), D3DVALUE(.94)); meshbuilder->SetQuality(D3DRMRENDER_FLAT); ScaleMesh(meshbuilder, D3DVALUE(7)); // ------- ФРЕЙМЫ РАКЕТ ------ for (int i = 0; i < 5; i++) { for (int j = 0; j < 3; j++) { LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->SetPosition(scene, D3DVALUE((i - 2) * 8), D3DVALUE(-12), D3DVALUE((j - 1) * 8)); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(OrientFrame, targetframe); meshframe->Release(); meshframe = 0; } }

// --------НАПРАВЛЕННЫЙ СВЕТ-------- LPDIRECT3DRMLIGHT dlight; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &dlight); LPDIRECT3DRMLIGHT alight; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.50), D3DVALUE(0.50), D3DVALUE(0.50), &alight); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->AddLight(dlight); lightframe->AddLight(alight); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0), D3DVALUE(1)); alight->Release(); alight = 0; dlight->Release(); dlight = 0; lightframe->Release(); lightframe = 0; //------ КАМЕРА---------- LPDIRECT3DRMFRAME cameradummy; d3drm->CreateFrame(scene, &cameradummy); cameradummy->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.01)); d3drm->CreateFrame(cameradummy, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; }

Функция CreateScene() выполняет следующие действия:

  1. Загружает сферическую сетку, которая будет представлять цель для ракет.
  2. Создает анимационную последовательность для сетки цели.
  3. Создает фрейм для сетки цели и устанавливает функцию обратного вызова для обновления анимационной последовательности.
  4. Загружает сетку ракеты.
  5. Создает 15 фреймов и к каждому из них присоединяет сетку ракеты.
  6. Создает два источника света.
  7. Создает порт просмотра.

Первый этап — это создание сетки для цели:

D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_SPHEREMESH); resinfo.lpType = "MESH"; LPDIRECT3DRMMESHBUILDER targetbuilder; d3drm->CreateMeshBuilder(&targetbuilder); targetbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); ScaleMesh(targetbuilder, D3DVALUE(.75));

Сетка загружается из ресурсов программы. Структура resinfo идентифицирует элемент ресурсов, содержащий сетку. Для загрузки сетки, как обычно, используется функция Load() интерфейса Direct3DRMMeshBuilder. После загрузки сетка масштабируется функцией RMWin::ScaleMesh().

На втором этапе создается анимационная последовательность, использующаяся для анимации сетки цели:

LPDIRECT3DRMANIMATION animation; d3drm->CreateAnimation(&animation); animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); animation->AddPositionKey(D3DVALUE(0), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(-20)); animation->AddPositionKey(D3DVALUE(12), D3DVALUE(0), D3DVALUE(15), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(24), D3DVALUE(20), D3DVALUE(0), D3DVALUE(-20)); animation->AddPositionKey(D3DVALUE(35), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(49), D3DVALUE(20), D3DVALUE(0), D3DVALUE(20)); animation->AddPositionKey(D3DVALUE(65), D3DVALUE(0), D3DVALUE(15), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(74), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(20)); animation->AddPositionKey(D3DVALUE(85), D3DVALUE(0), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(99), D3DVALUE(-20), D3DVALUE(0), D3DVALUE(-20));

Сначала для инициализации экземпляра интерфейса Direct3DRMAnimation вызывается функция CreateAnimation() интерфейса Direct3DRM. Затем вызывается функция SetOptions() интерфейса Direct3DRMAnimation,которой передается три флага. Флаг D3DRMANIMATION_SPLINEPOSITION указывает, что при расчете анимационной последовательности будут применяться сплайны. Флаг D3DRMANIMATION_CLOSED позволяет использовать непрерывно увеличивающиеся значения временных меток для повторного выполнения анимационной последовательности. Флаг D3DRMANIMATION_POSITION указывает объекту анимации, что нас интересует изменение местоположения анимируемого фрейма. Обратите внимание, что флаг D3DRMANIMATION_SCALEANDROTATION отсутствует (мы использовали его в приложении Rocket). Благодаря этому объект анимации не выполняет вычисления для изменения ориентации и масштаба, что позволяет анимационной последовательности выполняться быстрее.

Оставшаяся часть кода, относящегося ко второму этапу работы функции представляет собой несколько вызовов функции AddPositionKey() интерфейса Direct3DRMAnimation. Когда мы создавали анимационную последовательность в приложении Rocket, то использовали цикл для добавления ключей, которые были определены в элементах массива. В приложении Target не используется ни цикл ни массив. Каждый ключ добавляется с помощью отдельного вызова функции AddPositionKey(). Также, как и в приложении Rocket, используемые для анимационной последовательности позиции выбраны экспериментальным путем.

На следующем этапе выполняется создание фрейма для размещения и анимации сетки цели:

LPDIRECT3DRMFRAME targetframe; d3drm->CreateFrame(scene, &targetframe); animation->SetFrame(targetframe); targetframe->AddVisual(targetbuilder); targetframe->AddMoveCallback(MoveTarget, animation); targetbuilder->Release(); targetbuilder = 0;

Сначала для инициализации локального указателя targetframe используется функция CreateFrame() интерфейса Direct3DRM. Затем указатель на новый фрейм используется в качестве аргумента функции SetFrame() интерфейса Direct3DRMAnimation. Этот вызов функции связывает фрейм с анимационной последовательностью, после чего местоположение фрейма контролируется объектом анимации.

Функция AddVisual() интерфейса Direct3DRMFrame используется чтобы присоединить к фрейму сетку цели. Затем с помощью функции AddMoveCallback() интерфейса Direct3DRMFrame устанавливается функция обратного вызова MoveTarget(). Обратите внимание, что в качестве второго аргумента функции AddMoveCallback() передается указатель animation. Это обеспечивает возможность доступа к объекту анимации для функции обратного вызова. В заключение, освобождается указатель targetbuilder (который был инициализирован на первом этапе).

СОВЕТ Нарушение соглашений COM. Обычно, перед завершением функции освобождаются все локальные указатели на интерфейсы Direct3D. В приложении Target мы видим два исключения из этого правила. Указатели animation и targetframe не освобождаются, поскольку используются в функциях обратного вызова. Освобождение этих указателей приведет к тому, что COM уничтожит соответствующие объекты, и обращение к функции обратного вызова приведет к краху программы.

Другое возможное решение — оставить вызов функции Release(), но только после вызова функции AddRef(). Благодаря этому COM получает уведомление о создании дополнительной ссылки на объект. Согласно спецификации COM второй метод предпочтительнее. Мы используем первый метод только для того, чтобы сделать код приложения как можно более простым.

На четвертом этапе загружается сетка, изображающая ракету:

resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_MISSLEMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetColorRGB( D3DVALUE(.67), D3DVALUE(.82), D3DVALUE(.94)); meshbuilder->SetQuality(D3DRMRENDER_FLAT); ScaleMesh(meshbuilder, D3DVALUE(7));

Также как и сетка мишени, сетка ракеты хранится в ресурсах программы. После того, как сетка загружена, она окрашивается в светло-синий цвет, с помощью функции SetColorRGB() интерфейса Direct3DRMMeshBuilder. Функция SetQuality() вызывается для того, чтобы указать Direct3D, что при визуализации сетки ракеты должен использоваться плоский метод. Потом метод визуализации можно будет изменить с помощью меню Render. И, наконец, функция ScaleMesh() используется для масштабирования размеров сетки до семи единиц.

На пятом этапе чтобы создать 15 фреймов и присоединить к каждому из них сетку ракеты, используются вложенные циклы. Также на этом этапе для каждого фрейма устанавливается функция обратного вызова:

for (int i = 0; i < 5; i++) { for (int j = 0;j < 3; j++) { LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->SetPosition(scene, D3DVALUE((i - 2) * 8), D3DVALUE(-12), D3DVALUE((j - 1) * 8)); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(OrientFrame, targetframe); meshframe->Release(); meshframe = 0; } }

Указатель meshframe используется при инициализации каждого фрейма. Позиция нового фрейма зависит от текущей итерации двух вложенных циклов. Затем функция AddVisual() интерфейса Direct3DRMFrame используется для присоединения к фрейму сетки ракеты. Функция AddMoveCallback() применяется для установки функции обратного вызова OrientFrame(). Обратите внимание, что в качестве второго аргумента функции AddMoveCallback() используется указатель targetframe. Это обеспечивает функции обратного вызова доступ к фрейму за которым будут следить ракеты. После вызова функции AddMoveCallback() указатель meshframe освобождается.

На шестом и седьмом этапах выполняется создание источников света и порта просмотра. Давайте воздержимся от обсуждения этих этапов, поскольку источники света и порты просмотра подробно рассматриваются в главах 6 и 9 соответственно.



Содержание раздела