Код функции 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() выполняет следующие действия:
Первый этап — это создание сетки для цели:
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 соответственно.