![]() |
| |
![]() |
![]() |
Расширяем наши знания о DirectX (примеры DDEX2 и DDEX3)(первую статью см. "Основы
программирования игр с использованием DirectDraw")
Пример DDEX1 содержит только основы применения DirectDraw. В нем создается поверхность DirectDraw и объекты DirectDrawSurface (создается основная поверхность и ассоциированный с нею бэк-буфер), в бэк-буфер заносится текст, который с помощью флиппинга отображается на экране. Второй пример в DirectX 5 SDK (DDEX2) является дальнейшим развитием приложения DDEX1. DDEX2 включает возможность загрузки битмапа в бэк-буфер. Третий пример DirectDraw еще более функционален. DDEX3 создает две дополнительные поверхности и загружает битмап на каждую из них. Затем используется метод IDirectDrawSurface::BltFast для копирования содержимого одной из дополнительных поверхностей в бэк-буфер, затем с помощью операции флиппинга бэк-буфер отображается на экране, основная поверхность становится бэк-буфером, и на нее копируется содержимое второй дополнительной поверхности. В следующем разделе функционирование этих примеров будет рассмотрено более подробно. Загрузка битмапа на поверхностьТак же, как и в DDEX1, doInit содержит код инициализации приложения DDEX2. Код инициализации DirectDraw выглядит абсолютно также, как и в DDEX1, за исключением следующих строк:
Создание палитрыПервая строка кода вызывает функцию DDLoadPalette. Если вам хочется посмотреть на прототип функции DDLoadPalette, вы можете найти ее в файле Ddutil.cpp, находящемся в каталоге \DXSDK\SDK\SAMPLES\MISC. Как вы увидите, Ddutil.cpp используется большинством примеров DirectDraw из DirectX 5 SDK. В этом файле содержатся подпрограммы для загрузки битмапов и палитр из файлов ресурсов. Замечание Если вы используете Microsoft Developer Studio для компиляции примера DDEX2 и других примеров, входящих в поставку DirectX 5 SDK, то вам нужно включить файл Ddutil.cpp в список файлов рабочего пространства DDEXx. Для включения Ddutil.cpp в рабочее пространство:
В примере DDEX2 функция DDLoadPalette создает объект DirectDrawPalette из файла Back.bmp. Функция DDLoadPalette проверяет, существует ли файл или ресурс. Если файла не существует, создается палитра по умолчанию. В нашем случае функция извлекает из битмапа информацию о палитре и сохраняет ее в структуре, адресуемой с помощью переменной ape. Затем создается объект DirectDrawPalette как показано в следующем коде:
После вызова метода IDirectDraw::CreatePalette параметр ddpal указывает на объект DirectDrawPalette, который используется в качестве возвращаемого значения функцией DDLoadPalette. Параметр ape - указатель на структуру, которая может содержать 2, 4, 16, или 256 элементов, организованных линейно. Количество элементов зависит от параметра dwFlags, передаваемого функции IDirectDraw::CreatePalette. В нашем случае dwFlags имеет значение DDPCAPS_8BIT, что означает, что структура будет содержать 256 элементов. Каждый элемент занимает 4 байта (канал красного, канал зеленого, канал синего и байт флагов). Установка палитрыПосле создания палитры можно передать указатель на объект DirectDrawPalette (ddpal) основной поверхности, вызвав метод IDirectDrawSurface::SetPalette как показано в следующем примере:
После вызова IDirectDrawSurface::SetPalette объект DirectDrawPalette "прикреплен" к объекту DirectDrawSurface. В любой момент, когда вам нужно сменить палитру все, что нужно сделать - создать новую палитру и переустановить ее. (Так сделано в этом примере. Есть другие способы изменения палитры, мы их разберем в следующих примерах.) Загрузка битмапа на бэк-буферПосле вышеописанных операций DDEX2 загружает Back.bmp на бэк-буфер используя следующий код:
DDReLoadBitmap - еще одна функция из файла Ddutil.cpp. Она загружает битмап из файла на уже существующую поверхность DirectDraw. (Можно использовать DDLoadBitmap для создания поверхности и загрузки на нее битмапа как это будет показано в DDEX5.) В примере DDEX2 функция загружает файл Back.bmp на бэк-буфер, адресуемый указателем lpDDSBack. DDReLoadBitmap вызывает метод DDCopyBitmap для копирования картинки на бэк-буфер и растягивания ее до нужных размеров. Функция DDCopyBitmap копирует битмап в память, затем использует функцию GetObject для определения размера картинки. Затем используется следующий код для определения размера бэк-буфера, на который мы помещаем битмап:
ddsd - указатель на структуру DDSURFACEDESC. В этой структуре хранится описание поверхности DirectDraw. В нашем случае нас интересуют поля DDSD_HEIGHT и DDSD_WIDTH, которые содержат высоту и ширину бэк-буфера. Вызов метода IDirectDrawSurface::GetSurfaceDesc заполняет структуру правильными значениями. В примере DDEX2 эти значения будут равны 480 и 640 (высота и ширина соответственно). Функция DDCopyBitmap блокирует поверхность и копирует битмап на бэк-буфер, растягивает или сжимает ее используя процедуру StretchBlt:
Флиппинг поверхностейОперация флиппинга в примере DDEX2 аналогична примеру DDEX1, за исключением того, что если поверхность окажется потерянной (DDERR_SURFACELOST), то после восстановления поверхности картинка должна быть снова загружена на бэк-буфер с помощью функции DDReLoadBitmap. Блиттинг из неотображаемой поверхностиВ примере DDEX2 картинка загружается в бэк-буфер, а затем выполняется флиппинг бэк-буфера и основной поверхности. Это не слишком хороший подход для отображения битмапов. DDEX3 расширяет возможности DDEX2 используя две неотображаемые поверхности на которых хранятся картинки (одна поверхность содержит картинку, отображаемую в четных кадрах, а вторая - в нечетных). Затем происходит операция блиттинга с первой поверхности на бэк-буфер, флиппинг поверхностей, блиттинг второй поверхности на бэк-буфер снова флиппинг. Создание неотображаемых поверхностейСледующий фрагмент кода добавлен к функции doInit примера DDEX3 для создания дополнительных неотображаемых поверхностей:
Как видно из приведенного фрагмента, поле dwFlags определяет, что в приложении будет использоваться структура DDSCAPS и что мы будем устанавливать высоту и ширину поверхности. Поверхность будет неотображаемым (off-screen) буфером, что определяется флагом DDSCAPS_OFFSCREEN, установленным в структуре DDSCAPS. Высота и ширина в структуре DDSURFACEDESC устанавливается соответственно в 480 и 640. Затем поверхность создается методом IDirectDraw::CreateSurface. Поскольку обе наши неотображаемые поверхности имеют одинаковую высоту и ширину, единственное, что нам нужно сделать для создания второй поверхности - вызвать метод IDirectDraw::CreateSurface еще раз (при этом, конечно, используется другой указатель). При создании буфера можно указать, где он будет создан - в системной памяти или памяти видеоадаптера. Делается это установкой флагов DDSCAPS_SYSTEMMEMORY или DDSCAPS_VIDEOMEMORY в структуре DDSCAPS. Помещая буфер в видеопамять, вы тем самым увеличиваете скорость копирования информации между бэк-буфером и неотображаемой поверхностью. Подробнее на этом мы остановимся позже, при обсуждении вопроса об анимации. Как бы то ни было, вы должны знать, что указывая флаг DDSCAPS_VIDEOMEMORY для неотображаемого буфера вы рискуете получить ошибку DDERR_OUTOFVIDEOMEMORY если для создания поверхности не хватает памяти видеокарты. Загрузка битмапов на неотображаемые поверхностиПосле создания двух дополнительных неотображаемых поверхностей DDEX3 использует подпрограмму InitSurfaces для загрузки изображений, хранящихся в файле Frntback.bmp на поверхности. Подпрограмма InitSurfaces в свою очередь вызывает функцию DDCopyBitmap, находящуюся в файле Ddutil.cpp для загрузки обеих картинок:
Если вы загрузите файл Frntback.bmp в Microsoft Paint или другой графический редактор, вы увидите, что изображение состоит из двух картинок, одна над другой. Функция DDCopyBitmap разбивает изображение на две картинки и загружает одну из них на первую поверхность (lpDDSOne), а вторую картинку - на вторую неотображаемую поверхность (lpDDSTwo). Блиттинг неотображаемой поверхности на бэк-буферНапомним, что WM_TIMER содержит код для записи и отображения поверхностей. В случае DDEX3, здесь находится следующий фрагмент кода для выбора соответствующей поверхности и копирования (блиттинга) ее в бэк-буфер:
Перменная phase определяет, какая поверхность копируется в бэк-буфер. Затем вызывается метод IDirectDrawSurface::BltFast для запуска операции блиттинга начиная с позиции 0, 0, верхнего левого угла. Параметр rcRect указывает на структуру RECT которая определяет верхний левый и правый нижний углы участка неотображаемой поверхности, который будет скопирован. Последний параметр, установленный в FALSE (или 0), показывает, что не указываются специфичные флаги копирования. Хотелось бы добавить несколько слов о том, какой из методов выбрать - IDirectDrawSurface::Blt или IDirectDrawSurface::BltFast. Если происходит копирование из поверхности, которая расположена в памяти видеоадаптера, то лучше использовать IDirectDrawSurface::BltFast. Хотя вы не получите выигрыша в скорости на аппаратуре, использующей аппаратный блиттинг, однако на видеоадаптерах, где применена эмуляция апппаратного блиттинга можно получить до 10 процентов прироста производительности. Поэтому желательно применять метод IDirectDrawSurface::BltFast для всех операций блиттинга, происходящих из памяти видеоадаптера в такую же память. Если копирование происходит из системной памяти или используются специфичные флаги, то лучше использовать IDirectDrawSurface::Blt. Как только неотображаемая поверхность загружена в бэк-буфер, происходит операция флиппинга точно так же, как это было показано в предыдущих примерах. |
![]() |
![]() |
![]() |
![]() |
|
|