Введение в программирование трехмерных игр с DX9

         

CDFont


DirectX SDK предоставляет полезный вспомогательный код, который находится а папке \Samples\C++\Common корневого каталога DXSDK. Среди этого кода есть класс CD3DFont, который отображает текст используя текстурированные треугольники и Direct3D. Поскольку CD3DFont использует для визуализации Direct3D, а не GDI, он работает гораздо быстрее, чем ID3DXFont. Однако, в отличие от ID3DXFont, CD3DFont не поддерживает сложные шрифты и форматирование. Если вам нужна скорость и достаточно простых шрифтов, класс CD3DFont— это ваш выбор.

Чтобы использовать класс CD3DFont необходимо добавить к приложению файлы d3dfont.h, d3dfont.cpp, d3dutil.h, d3dutil.cpp, dxutil.h и dxutil.cpp. Эти файлы находятся в папках Include и Src, которые расположены в ранее упоминавшейся папке Common.



DXCreateText


Функция D3DXCreateText создает трехмерную сетку, представляющую строку текста. На Рисунок 9.1 показана такая трехмерная сетка, отображаемая приложением FontMesh3D, которое находится в сопроводительных файлах к данной главе.



Шрифты


В ходе игры нам часто требуется показать пользователю какую-либо текстовую информацию. В этой главе мы рассмотрим три способа отображения текста, поддерживаемые Direct3D. Каждый способ иллюстрируется примером программы, находящимся на сайте этой книги и в сопроводительных файлах.

Цели

Узнать, как можно визуализировать текст с помощью интерфейса ID3DXFont.

Узнать, как можно визуализировать текст с помощью класса CD3DFont.

Узнать, как вычислить частоту кадров приложения.

Узнать, как создать и визуализировать трехмерный текст с помощью функции D3DXCreateText.



IDXFont




Библиотека D3DX предоставляет интерфейс ID3DXFont, который можно использовать для отображения текста в Direct3D-приложениях. Этот интерфейс использует для отображения текста GDI и его применение может значительно уменьшить быстродействие приложения. Однако, поскольку ID3DXFont использует GDI, он поддерживает сложные шрифты и форматирование.



Если вам необходима поддержка сложных


Если вам необходима поддержка сложных шрифтов и форматирования, используйте для визуализации текста интерфейс ID3DXFont. Он использует при визуализации текста GDI и поэтому работает медленно.
Для быстрой визуализации простого текста используйте класс CD3DFont. Он использует для визуализации текста текстурированные треугольники и Direct3D и поэтому работает гораздо быстрее, чем ID3DXFont.
Чтобы создать трехмерную сетку, изображающую строку текста, воспользуйтесь функцией D3DXCreateText.

Очистка



9.2.3. Очистка

Перед удалением объекта CD3DFont необходимо вызвать ряд процедур очистки, как показано в приведенном ниже фрагменте кода:

Font->InvalidateDeviceObjects(); Font->DeleteDeviceObjects(); delete Font;



Рисование текста



9.1.2. Рисование текста

После того, как мы получили указатель на интерфейс ID3DXFont, рисование текста осуществляется простым вызовом метода ID3DXFont::DrawText.

INT ID3DXFont::DrawText( LPCSTR pString, INT Count, LPRECT pRect, DWORD Format, D3DCOLOR Color );

pString— Указатель на отображаемую строку текста.

Count — Количество отображаемых символов строки. Если строка завершается нулевым символом можно указать –1, чтобы строка отображалась вся.

pRect — Указатель на структуру RECT, определяющую область экрана в которой будет отображаться текст.

Format — Необязательные флаги, определяющие форматирование выводимого текста; их описание находится в документации к SDK.

Color — Цвет текста.

Вот пример использования метода:

Font->DrawText( "Hello World", // Выводимая строка -1, // Строка завершается нулевым символом &rect, // Прямоугольная область для рисования строки DT_TOP | DT_LEFT, // Рисуем в верхнем левом углу области 0xff000000); // Черный цвет



9.2.2. Рисование текста

Теперь, когда мы создали и инициализировали объект CD3DFont, можно нарисовать какой-нибудь текст. Рисование текста выполняет следующий метод:

HRESULT CD3DFont::DrawText( FLOAT x, FLOAT y, DWORD dwColor, const TCHAR* strText, DWORD dwFlags=0L );

x— координата x в экранном пространстве с которой начинается рисование текста.

y — координата y в экранном пространстве с которой начинается рисование текста.

dwColor — Цвет текста.

strText — Указатель на рисуемую строку.

dwFlags — Необязательные флаги визуализации; можете присвоить этому параметру 0 или указать произвольную комбинацию флагов D3DFONT_CENTERED, D3DFONT_TWOSIDED, D3DFONT_FILTERED.

Пример использования метода:

Font->DrawText(20, 20, 0xff000000, "Hello, World");



Создание IDXFont


9.1.1. Создание ID3DXFont

Для создания интерфейса ID3DXFont может использоваться функция D3DXCreateFontIndirect.

HRESULT D3DXCreateFontIndirect( LPDIRECT3DDEVICE9 pDevice, // устройство, связанное со шрифтом CONST LOGFONT* pLogFont, // структура LOGFONT, описывающая шрифт LPD3DXFONT* ppFont // возвращает созданный шрифт );

Приведенный ниже фрагмент кода показывает использование этой функции:

LOGFONT lf; ZeroMemory(&lf, sizeof(LOGFONT)); lf.lfHeight = 25; // в логических единицах lf.lfWidth = 12; // в логических единицах lf.lfWeight = 500; // насыщенность, // диапазон 0(тонкий) - 1000(жирный) lf.lfItalic = false; lf.lfUnderline = false; lf.lfStrikeOut = false; lf.lfCharSet = DEFAULT_CHARSET; strcpy(lf.lfFaceName, "Times New Roman"); // гарнитура шрифта

ID3DXFont* font = 0; D3DXCreateFontIndirect(Device, &lf, &font);

Обратите внимание, что сперва требуется заполнить структуру LOGFONT, которая описывает параметры создаваемого шрифта.

ПРИМЕЧАНИЕ

Для получения указателя на интерфейс ID3DXFont вы можете также воспользоваться функцией D3DXCreateFont.

Создание экземпляра CDFont



9.2.1. Создание экземпляра CD3DFont

Экземпляр CD3DFont создается также как обычный объект C++ с помощью следующего конструктора:

CD3DFont( const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags=0L );

strFontName — Завершающаяся нулем строка, задающая имя гарнитуры шрифта.

dwHeight — Высота шрифта.

dwFlags — Необязательные дополнительные флаги; параметру можно присвоить 0 или использовать произвольную комбинацию флагов D3DFONT_BOLD, D3DFONT_ITALIC, D3DFONT_ZENABLE.

После создания объекта CD3DFont для инициализации шрифта мы должны вызвать следующие методы (в указанном порядке):

Font = new CD3DFont("Times New Roman", 16, 0); // создание экземпляра Font->InitDeviceObjects(Device); Font->RestoreDeviceObjects();



Трехмерный текст, созданный функцией DXCreateText



Рисунок 9.1. Трехмерный текст, созданный функцией D3DXCreateText


Прототип функции выглядит следующим образом:

HRESULT D3DXCreateText( LPDIRECT3DDEVICE9 pDevice, HDC hDC, LPCTSTR pText, FLOAT Deviation, FLOAT Extrusion, LPD3DXMESH* ppMesh, LPD3DXBUFFER* ppAdjacency, LPGLYPHMETRICSFLOAT pGlyphMetrics );

В случае успешного завершения функция возвращает D3D_OK.

pDevice — Устройство, связанное с сеткой.

hDC — Дескриптор контекста устройства, содержащего описание шрифта, которое будет использоваться для генерации сетки.

pText — Указатель на завершающуюся нулем строку с текстом, для которого будет создаваться сетка.

Deviation — Максимальное хордальное отклонение от контуров шрифта TrueType. Значение должно быть больше или равно нулю. Когда значение равно нулю, хордальное отклонение будет равно одной проектной единице оригинального шрифта.

Extrusion — Глубина шрифта, измеряемая вдоль отрицательного направления оси Z.

ppMesh — Возвращает созданную сетку.

ppAdjacency — Возвращает информацию о смежности для созданной сетки. Если она вам не нужна, укажите в данном параметре null.

pGlyphMetrics — Указатель на массив структур LPGLYPHMETRICSFLOAT, содержащий данные метрик глифов. Каждый элемент массива содержит информацию о местоположении и ориентации соответствующего глифа в строке. Количество элементов массива должно соответствовать количеству символов в строке. Если вы не хотите связываться с метриками глифов, просто укажите 0.

Следующий фрагмент кода показывает как создать изображающую текст трехмерную сетку с помощью рассматриваемой функции.

// Получение дескриптора контекста устройства HDC hdc = CreateCompatibleDC(0);

// Заполнение структуры LOGFONT, описывающей свойства шрифта LOGFONT lf; ZeroMemory(&lf, sizeof(LOGFONT));

lf.lfHeight = 25; // в логических единицах lf.lfWidth = 12; // в логических единицах lf.lfWeight = 500; // насыщенность, // диапазон 0(тонкий) - 1000(жирный) lf.lfItalic = false; lf.lfUnderline = false; lf.lfStrikeOut = false; lf.lfCharSet = DEFAULT_CHARSET; strcpy(lf.lfFaceName, "Times New Roman"); // гарнитура шрифта

// Создаем шрифт и выбираем его в контексте устройства HFONT hFont; HFONT hFontOld; hFont = CreateFontIndirect(&lf); hFontOld = (HFONT)SelectObject(hdc, hFont);

// Создаем представляющую текст трехмерную сетку ID3DXMesh* Text = 0; D3DXCreateText(_device, hdc, "Direct3D", 0.001f, 0.4f, &Text, 0, 0);

// Восстанавливаем бывший до этого шрифт и освобождаем ресурсы SelectObject(hdc, hFontOld); DeleteObject(hFont); DeleteDC(hdc);

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

Text->DrawSubset(0);



Вычисление частоты кадров



9.1.3. Вычисление частоты кадров

Примеры приложений к этой главе ID3DXFont и CFont вычисляют и отображают количество визуализируемых за секунду кадров (FPS). В этом разделе мы покажем как вычисляется FPS.

Сперва мы объявляем три глобальных переменных:

DWORD FrameCnt; // Количество выведенных кадров float TimeElapsed; // Прошедшее время float FPS; // Частота визуализации кадров

Мы вычисляем FPS каждую секунду; это дает нам достоверное среднее значение. Кроме того, мы храним вычисленное значение частоты кадров в течение одной секунды, что дает достаточно времени, чтобы прочитать его перед очередным изменением.

Итак, каждый кадр мы увеличиваем значение переменной FrameCnt и прибавляем к переменной TimeElapsed время, прошедшее с вывода предыдущего кадра:

FrameCnt++; TimeElapsed += timeDelta;

где timeDelta — это время, прошедшее между двумя кадрами.

После того, как пройдет одна секунда, мы вычисляем частоту кадров по следующей формуле:

FPS = (float)FrameCnt / TimeElapsed;

Затем мы обнуляем переменные FrameCnt и TimeElapsed и начинаем вычисление среднего значения частоты кадров для следующей секунды. Вот как выглядит весь код вместе:

void CalcFPS(float timeDelta) { FrameCnt++; TimeElapsed += timeDelta;

if(TimeElapsed >= 1.0f) { FPS = (float)FrameCnt / TimeElapsed; TimeElapsed = 0.0f; FrameCnt = 0; } }