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

       

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


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

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

BOOL RocketWin::CreateScene() { //-------- СЕТКА ---------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_ROCKETMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, LoadTexture, NULL); ScaleMesh(meshbuilder, D3DVALUE(10));

//-------- АНИМАЦИЯ -------- d3drm->CreateAnimation(&animation); for (int i = 0; i < 11; i++) { D3DRMQUATERNION quat; D3DRMQuaternionFromRotation(&quat, &vect[i], rot[i]); animation->AddRotateKey(D3DVALUE(i), &quat); animation->AddPositionKey(D3DVALUE(i), trans[i].x, trans[i].y, trans[i].z); } OnAnimationLinear(); //-------- ФРЕЙМ СЕТКИ ------ LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(UpdateScene, animation); animation->SetFrame(meshframe); meshframe->Release(); meshframe = 0;

//-------- СВЕТ -------- LPDIRECT3DRMLIGHT dlight; LPDIRECT3DRMLIGHT alight; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.5), D3DVALUE(0.5), D3DVALUE(0.5), &alight); d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(1.0), &dlight);

LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-2), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); lightframe->AddLight(dlight); lightframe->AddLight(alight);

dlight->Release(); dlight = 0; alight->Release(); alight = 0; lightframe->Release(); lightframe = 0;

//-------- ПОРТ ПРОСМОТРА -------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50.0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);

return TRUE; }

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

  • Создание сетки.




  • Создание анимационной последовательности.


  • Создание фрейма для размещения сетки и установка функции обратного вызова для обновления.


  • Создание двух источников света.


  • Создание порта просмотра.


  • Первым делом создается сетка:

    D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_ROCKETMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, LoadTexture, NULL); ScaleMesh(meshbuilder, D3DVALUE(10));

    Сетка загружается из ресурсов программы с помощью интерфейса Direct3DRMMeshBuilder. С одним исключением, данный код аналогичен коду загрузки сетки из любого другого демонстрационного приложения, рассматриваемого в этой книге. Отличие заключается в том, что для присоединения текстуры к сетке мы здесь используем функцию обратного вызова. Четвертый аргумент функции Load() интерфейса Direct3DRMMeshBuilder является указателем на необязательную функцию обратного вызова, используемую для загрузки текстур. Обычно в этом аргументе мы передавали ноль, но сейчас, ради разнообразия, давайте воспользуемся функцией обратного вызова LoadTexture(). Ее код выглядит так:

    HRESULT RocketWin::LoadTexture(char*, void*, LPDIRECT3DRMTEXTURE* texture) { HRSRC id = FindResource(NULL, MAKEINTRESOURCE(IDR_ROCKETTEXTURE), "TEXTURE"); RMWin::d3drm->LoadTextureFromResource(id, texture); return D3DRM_OK; }

    При вызове этой функции ей передается указатель на интерфейс Direct3DRMTexture. Все, что нам требуется сделать — это создать текстуру, используя полученный указатель. Применение текстуры к сетке Direct3D выполнит автоматически. Обратите внимание, что для применения текстуры мы не используем наложение текстуры. Это вызвано тем, что мы используем данные о размещении текстур, сохраненные в файле сетки.

    СОВЕТ Простой способ наложения текстур. Данные о наложении текстур могут быть сохранены в файле сетки путем назначения сетке атрибутов размещения текстур в программах визуального моделирования таких, как 3D Studio. По умолчанию данные о размещении текстур импортируются утилитой DirectX CONV3DS. Эта техника является альтернативой методам, рассмотренным в главе 5.
    <


    /p> Обратите также внимание, что первым параметром функции обратного вызова является указатель на строку. Он предназначен для передачи сохраненного в файле сетки имени текстуры. Этот параметр очень полезен, поскольку позволяет использовать одну и ту же функцию обратного вызова для нескольких сеток. Мы не используем его только потому, что в нашем случае накладывается единственная текстура.

    Вернемся к функции CreateScene(). На втором этапе ее работы выполняется создание анимационной последовательности:

    d3drm->CreateAnimation(&animation); for (int i = 0; i < 11; i++) { D3DRMQUATERNION quat; D3DRMQuaternionFromRotation(&quat, &vect[i], rot[i]); animation->AddRotateKey(D3DVALUE(i), &quat); animation->AddPositionKey(D3DVALUE(i), trans[i].x, trans[i].y, trans[i].z); } OnAnimationLinear();

    Сперва для инициализации экземпляра интерфейса Direct3DRMAnimation применяется функция CreateAnimation() интерфейса Direct3DRM. Затем в цикле добавляются ключи анимации. На каждой итерации цикла добавляются ключи двух типов: ключ вращения и позиционный ключ. Ключи вращения определяют ориентацию нашей анимированной ракеты на протяжении анимационной последовательности. Позиционные ключи указывают где будет находиться ракета. Для того чтобы лучше понять назначение этих ключей, давайте взглянем на их данные.

    Данные ключей вращения хранятся в двух массивах: vect и rot. Массив vect содержит векторы, определяющие ось вращения, а массив rot хранит величину поворота (в радианах). Эти два массива инициализируются следующим образом:

    D3DVECTOR vect[]= // вектор вращения для каждого ключевого кадра { { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(0), D3DVALUE(1), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(0), D3DVALUE(1), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(0), D3DVALUE(1), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, { D3DVALUE(1), D3DVALUE(0), D3DVALUE(0) }, };



    const D3DVALUE rot[]= // угол поворота для каждого ключевого кадра { PI/2, PI, -(PI/2), PI, PI/2, PI, -(PI/2), -PI, PI/2, PI, PI/2, };

    Массив vect инициализируется наборами значений X, Y и Z. Каждые три значения образуют вектор, определяющий ось вращения. При инициализации массива rot используется константа PI, чтобы показать насколько должен повернуться объект вокруг заданной оси. Использование значения PI эквивалентно повороту на 180 градусов. Для поворота на 90 градусов укажите значение PI/2. Отрицательные значения изменяют направление вращения на противоположное.

    Давайте снова взглянем на содержимое цикла:

    D3DRMQUATERNION quat; D3DRMQuaternionFromRotation(&quat, &vect[i], rot[i]); animation->AddRotateKey(D3DVALUE(i), &quat); animation->AddPositionKey(D3DVALUE(i), trans[i].x, trans[i].y, trans[i].z);

    Функция AddRotateKey() интерфейса Direct3DRMAnimation получает в качестве второго аргумента кватернионы. Кватернионом (quaternion) называется структура, в которой хранится и вектор вращения и скорость вращения. Чтобы преобразовать вектор и скорость вращения в кватернион воспользуемся функцией D3DRMQuaternionFromRotation(). Адрес полученного кватерниона передадим функции AddRotateKey() во втором аргументе. Первым аргументом функции AddRotateKey() является временная метка для нового ключа.

    Использовать функцию AddPositionKey() немного легче. Ей необходимо четыре аргумента: временная метка и три значения, определяющие позицию. Для хранения позиций анимационной последовательности наш код использует массив trans. Его определение выглядит так:

    const D3DVECTOR trans[]= // местоположение объекта в каждом ключевом кадре { { D3DVALUE(0), D3DVALUE(0), FARLIM }, { XOUTLIM, D3DVALUE(0), CLOSELIM }, { D3DVALUE(0), D3DVALUE(0), FARLIM }, { -XOUTLIM, D3DVALUE(0), CLOSELIM }, { D3DVALUE(0), D3DVALUE(0), FARLIM }, { D3DVALUE(0), -YOUTLIM, CLOSELIM }, { D3DVALUE(0), D3DVALUE(0), FARLIM }, { D3DVALUE(0), YOUTLIM, CLOSELIM }, { D3DVALUE(0), D3DVALUE(0), FARLIM }, { D3DVALUE(0), D3DVALUE(0), CLOSELIM-3 }, { D3DVALUE(0), D3DVALUE(0), FARLIM }, };



    Значения констант XOUTLIM, YOUTLIM, FARLIM и CLOSELIM получены методом проб и ошибок. Константа XOUTLIM определяет как далеко влево и вправо перемещается ракета. Константа YOUTLIM контролирует пределы перемещения по вертикали. Константы FARLIM и CLOSELIM управляют движением ракеты вдоль оси Z.

    На третьем этапе работы функции CreateScene() выполняется создание фрейма для сетки ракеты:

    LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(UpdateScene, animation); animation->SetFrame(meshframe); meshframe->Release(); meshframe = 0;

    Новый фрейм создается с помощью функции CreateFrame() интерфейса Direct3DRM. Ранее созданная сетка ракеты присоединяется к фрейму с помощью функции AddVisual() интерфейса Direct3DRMFrame.

    Функция обратного вызова UpdateScene(), которая будет использоваться во время работы приложения для изменения анимационной последовательности, устанавливается с помощью функции AddMoveCallback(). Обратите внимание, что в качестве второго аргумента ей передается указатель animation. Любое значение, указанное во втором аргументе функции AddMoveCallback() будет передаваться функции обратного вызова при каждом обращении к ней. Передача указателя на объект анимации позволяет нашей функции обратного вызова управлять анимационной последовательностью (вспомните, что функции обратного вызова всегда статические, и поэтому из них нельзя обращаться к переменным класса).

    Далее функция SetFrame() интерфейса Direct3DRMAnimation используется для присоединения фрейма к объекту Direct3DRMAnimation создание и инициализация которого были выполнены на этапе 2. Теперь объект анимации будет управлять перемещемнием, вращением и масштабированием фрейма.

    На четвертом и пятом этапах работы функции CreateScene() выполняется создание источника света и порта просмотра. Мы пропустим обсуждение этих этапов и перейдем к изучению функции обратного вызова UpdateScene().


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