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



              

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


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

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

BOOL JadeWin::CreateScene() { HRESULT r; //------- ФОН -------- scene->SetSceneBackgroundRGB(D3DVALUE(.2), D3DVALUE(.2), D3DVALUE(.2)); //-------- СЕТКА -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_D3DMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->SetPerspective(TRUE); r = meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); if (r != D3DRM_OK) { TRACE("meshbuilder->Load() failed\n"); return FALSE; } ScaleMesh(meshbuilder, D3DVALUE(35)); //-------- ТЕКСТУРА -------- LPDIRECT3DRMTEXTURE texture; HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_JADETEXTURE), "TEXTURE"); r = d3drm->LoadTextureFromResource(texture_id, &texture); if (r != D3DRM_OK) { TRACE("d3drm->LoadTextureFromResource() failed\n"); return FALSE; } 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(0.0), D3DVALUE(1.0), // ось z наложения D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось y наложения D3DVALUE(0.5), D3DVALUE(0.5), // начало координат текстуры D3DDivide(1,w), D3DDivide(1,h), // масштаб текстуры &wrap); wrap->Apply(meshbuilder); wrap->Release(); wrap = 0; //------- ФРЕЙМ СЕТКИ ---------- LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(MoveFrame, NULL); meshframe->Release(); //---------- ОСВЕЩЕНИЕ ----------- LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1),D3DVALUE(1), D3DVALUE(1), &light); scene->AddLight(light); light->Release(); light = 0; //---------- КАМЕРА ----------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(-50.0)); d3drm->CreateViewport( device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; }

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

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

Давайте рассмотрим каждый из этих этапов по отдельности. Первое, что делает функция CreateScene() — это изменение цвета фона сцены с помощью функции SetSceneBackgroundRGB():

scene->SetSceneBackgroundRGB(D3DVALUE(.2), D3DVALUE(.2), D3DVALUE(.2));

Функция SetSceneBackgroundRGB() получает три аргумента, определяющие красную, зеленую и синюю составляющие нового цвета фона сцены. Для нашего примера мы выбрали темно-серый цвет.

Затем для загрузки сетки и настройки ее параметров используется интерфейс Direct3DRMMeshBuilder:

D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_D3DMESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->SetPerspective(TRUE); r = meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); if (r != D3DRM_OK) { TRACE("meshbuilder->Load() failed\n"); return FALSE; } ScaleMesh(meshbuilder, D3DVALUE(35));

Для загрузки сетки из ресурсов программы необходимо подготовить структуру D3DRMLOADRESOURCE, содержащую три поля: hModule, lpName и lpType. Поле hModule идентифицирует модуль, содержащий требуемый ресурс. Это полезно, если ресурсы загружаются из другого исполняемого модуля, но поскольку в нашем случае требуемый ресурс относится к той же программе, которая вызывает функцию, мы укажем NULL. Поле lpName хранит значение, идентифицирующее требуемый ресурс. Поле lpType задает тип ресурса.

СОВЕТ

Тип ресурса MESH. Нет ничего необычного в типе ресурса MESH (Visual C++ ничего не знает о сетках Direct3D). Приложения из этой книги хранят сетки в группе ресурсов с именем MESH, чтобы отделить ресурсы сеток от других ресурсов.

После того, как подготовлена структура D3DRMLOADRESOURCE, функция CreateMeshBuilder() интерфейса Direct3DRM инициализирует указатель meshbuilder. Функция SetPerspective() разрешает коррекцию перспективы для новой сетки. Коррекция перспективы предотвращает смещение текстуры во время анимации (чтобы увидеть этот эффект, закоменнтируйте данную строку кода и откомпилируйте пример заново).

Затем вызывается функция Load(), которой в качестве первого аргумента передается указатель на подготовленную структуру D3DRMLOADRESOURCE. Третий аргумент функции Load() (флаг D3DRMLOAD_FROMRESOURCE) указывает Direct3D, что загрузка происходит из ресурсов программы, а не из файла на диске. Возвращаемое функцией Load() значение, говорит о том, успешно ли завершилась функция. Если при выполнении функции Load() произошла ошибка, CreateScene() возвращает FALSE.

СОВЕТ

Проверка возвращаемых значений. Большинство функций Direct3D возвращают значение типа HRESULT, указывающее на состояние функции. Как правило, не требуется проверять возвращаемое значение для каждой функции, но рекомендуется проверять возвращаемые значения для тех функций, которые могут завершиться неудачно из-за внешних причин. Например, функции загрузки файла часто завершаются неудачно, из-за того, что не могут найти требуемый файл. В данном сучае неудачное завершение функции Load() маловероятно, поскольку сетка является частью EXE-файла программы.

Затем используется функция ScaleMesh() для указания идеального размера сетки. Первый аргумент ScaleMesh() — Direct3DRMMeshBuilder представляет масштабируемую сетку. Второй аргумент — это желаемый размер сетки.

На следующем (третьем) этапе выполняется загрузка текстуры. Загрузка тексуры из ресурсов программы слегка отличается от загрузки сеток. Для загрузки сеток применяется функция Load(), независимо от того, где находится сетка: в файле на диске или в ресурсах программы. Для загрузки текстуры из ресурсов и из файла применяются различные функции. Мы воспользуемся функцией LoadTextureFromResource() (вместо функции LoadTexture()):

LPDIRECT3DRMTEXTURE texture; HRSRC texture_id = FindResource(NULL, MAKEINTRESOURCE(IDR_JADETEXTURE), "TEXTURE"); r = d3drm->LoadTextureFromResource(texture_id, &texture); if (r != D3DRM_OK) { TRACE("d3drm->LoadTextureFromResource() failed\n"); return FALSE; } meshbuilder->SetTexture(texture); texture->Release(); texture = 0;

Функция LoadTextureFromResource() получает два аргумента: экземпляр структуры HRSRC и адрес указателя на интерфейс Direct3DRMTexture. HRSRC — это структура Win32, подобная структуре D3DRMLOADRESOURCE используемой функцией Load() интерфейса конструктора сеток. Структура HRSRC инициализируется функцией FindResource(). Три аргумента FindResource() идентифицируют загружаемый ресурс. Первый аргумент — это дескриптор модуля. Поскольку текстура загружается из того же исполняемого файла в котором содержится функция CreateScene(), мы используем значение NULL. Второе значение — это идентификатор загружаемого ресурса. Третий аргумент указывает тип ресурса.

После инициализации структуры HRSRC вызывается функция LoadTextureFromResource(). Если функция LoadTextureFromResource() завершается с ошибкой, CreateScene() возвращает FALSE (после того, как макрос TRACE выведет сообщение). Если функция завершилась успешно, то текстура присоединяется к ранее загруженной сетке с помощью функции SetTexture() интерфейса Direct3DRMMeshBuilder. Затем указатель texture освобождается, поскольку он больше нам не потребуется. После того, как указатель освобожден, ему присваивается нулевое значение.

СОВЕТ

Остерегайтесь висящих указателей. Я рекомендую вам избегать висящих указателей, присваивая им нулевые значения после освобождения. Эта привычка спасет вас от проблем, которые могут возникнуть, если вы нечаянно используете освобожденный указатель.

Функция SetTexture() не указывает, как должна быть наложена текстура, она только сообщает, что текстура накладывается на сетку. Чтобы определить способ покрытия текстурой необходимо использовать наложение текстуры. На этапе 4 мы создадим и применим плоское наложение текстуры чтобы текстура не изгибалась и не деформировалась, а прямо накладывалась на сетку. Нам потребуется масштабировать текстуру, чтобы она была такого же размера, как и сетка. Чтобы сделать это, нам необходимо узнать размер сетки. Приведенный ниже код расположен в функции CreateScene() перед созданием наложения текстуры:

D3DRMBOX box; meshbuilder->GetBox(&box); D3DVALUE w = box.max.x - box.min.x; D3DVALUE h = box.max.y - box.min.y;

Функция GetBox() применяется для заполнения структуры D3DRMBOX, содержащей координаты крайних точек сетки. Мы используем эти значения для вычисления длины и высоты сетки. Обратите внимание, что мы вычисляем длину и высоту сетки, но не глубину. Поскольку текстура является двумерной, третье измерение сетки нас не интересует.

Теперь мы можем создать и применить наложение текстуры:

LPDIRECT3DRMWRAP wrap; d3drm->CreateWrap(D3DRMWRAP_FLAT, scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось z D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось y D3DVALUE(0.5), D3DVALUE(0.5), // начало координат текстуры D3DDivide(1,w), D3DDivide(1,h), // масштаб текстуры &wrap); wrap->Apply(meshbuilder); wrap->Release(); wrap = 0;

Для создания наложения мы используем функцию CreateWrap() интерфейса Direct3DRM. Функция CreateWrap() получает 16 аргументов. Первый аргумент определяет способ наложения. Мы используем константу D3DRMWRAP_FLAT чтобы указать на использование плоского наложения текстуры. Второй аргумент задает базовый фрейм. Мы укажем здесь корневой фрейм сцены, чтобы значения остальных аргументов воспринимались как абсолютные координаты. Если мы укажем другой фрейм, то оставшиеся аргументы будут интерпретироваться как координаты относительно указанного фрейма.

СОВЕТ

Задание абсолютных значений. Существует два способа указать Direct3D, что заданные значения являются абсолютными, а не относительными. Первый способ — указать в качестве базового корневой фрейм сцены (как сделано в рассматриваемом коде). Второй — передать вместо указателя на базовый фрейм NULL.

Следующие девять аргументов CreateWrap() определяют местоположение и ориентацию наложения. Используемые в функции CreateScene() значения представлены в таблице 5.1.


Таблица 5.1.Параметры наложения для приложения Jade


Параметр Значение
Начало координат наложения <0.0, 0.0, 0.0>
Ось Z наложения <0.0, 0.0, 1.0>
Ось Y наложения <0.0, 1.0, 0.0>

Первые три аргумента определяют начало координат наложения. Используя значения <0.0, 0.0, 0.0>, мы указываем, что наложение размещается на локальных осях сетки (локальное начало координат для сетки обычно совпадает с ее центром).

Следующие три аргумента определяют вектор, задающий направление наложения. В данном примере мы используем вектор, совпадающий с осью Z и направленный от зрителя. Третий набор из трех аргументов задает вектор, определяющий направление вверх для покрытия. Мы используем направленный вверх вектор, который совпадает с осью Y.

Следующие четыре аргумента CreateWrap() задают начало координат и масштаб для текстуры. Параметры, используемые в функции CreateScene() представлены в таблице 5.2.


Таблица 5.2.Параметры текстуры для приложения Jade


Параметр Значение
Начало координат текстуры <0.5, 0.5>
Масштаб текстуры <D3DDivide(1,w), D3DDivide(1,h)>

Начало координат текстуры определяет точку текстуры, которая будет совпадать с началом координат наложения. Мы используем значение 0.5 и для координаты X и для координаты Y текстуры, чтобы начало координат наложения совпадало с центром текстуры (значение 0.5 означает середину текстуры, 0.0 означает верхний или левый край текстуры, а 1.0 означает правый или нижний край текстуры).

Масштаб текстуры определяет размер текстуры в пропорции к наложению. Вспомните, что мы собирались масштабировать текстуру так, чтобы она точно соответствовала размерам сетки. Мы уже отцентрировали текстуру, задав значения начала ее координат равными 0.5. Теперь, используя значения ширины и высоты сетки, мы масштабируем текстуру, чтобы она соответствовала размерам сетки. Для удобства Direct3D предоставляет макрос D3DDivide. Этот макрос приводит свои аргументы к типу D3DVALUE и делит первый аргумент на второй. Благодаря этому мы получаем коэффициент масштабирования, необходимый чтобы размеры текстуры после масштабирования соответствовали размерам сетки.

Последний аргумент CreateWrap() — это адрес указателя, который после выполненой функцией инициализации будет указывать на интерфейс Direct3DRMWrap. После того, как наложение создано, оно может быть применено к сетке с помощью функции Apply() интерфейса Direct3DRMWrap:

wrap->Apply(meshbuilder);

Функция Apply() вычисляет метод, которым текстура будет накладываться, для каждой из граней сетки. После вызова функции Apply() указатель wrap освобождается.

На пятом этапе создается фрейм для сетки:

LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->AddMoveCallback(MoveFrame, NULL); meshframe->Release(); meshframe = 0;

Функция CreateFrame() интерфейса Direct3DRM применяется для создания фрейма с именем meshframe. Конструктор сеток присоединяется к новому фрейму функцией AddVisual(). Затем с помощью функции AddMoveCallback() устанавливается функция обратного вызова MoveFrame(). После описаных действий указатель meshframe освобождается.

Шестой этап — это создание источника рассеянного света:

LPDIRECT3DRMLIGHT light; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1),D3DVALUE(1), D3DVALUE(1), &light); scene->AddLight(light); light->Release(); light = 0;

Источник рассеянного света применяется в нашем примере по причине простоты использования. Более подробно об источниках света мы поговорим в главе 6.

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

d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(-50.0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);

Переменные camera и viewport наследуются от класса RMWin и должны инициализироваться в функции CreateScene(). Переменная camera представляет собой указатель на интерфейс Direct3DRMFrame, инициализируемый функцией CreateFrame() интерфейса Direct3DRM. С помощью функции SetPosition() новый фрейм располагается на 50 единиц дальше начала координат. После этого функция CreateViewport() интерфейса Direct3DRM инициализирует указатель viewport.

Чтобы сообщить, что сцена успешно создана, функция CreateScene() возвращает TRUE. Если функция CreateScene() возвращает FALSE, выполнение приложения прекращается и выводится соответствующее сообщение.









Содержание    Назад    Вперед