Visual Basic - мастерская разработчика
Библиотеки

DirectX

Обзоры
DirectDraw
Direct3D
DirectX Audio
DirectPlay
DirectInput
Fido Topics
SourceCode
Tools&Libs

OpenGL

Статьи и учебники
Fido Topics
SourceCode
Tools&Libs

Архив по Glide

Движки

Обзоры
Учебники
SourceCode
Downloads

Создание игр

Ваши игры

Обзорные статьи
Учебники
Fido Topics
SourceCode
Download

Stuff

Программер-Чат

Псевдо-FTP
Disclaimer
Оффтопик

 

Основы DirectDraw на C++

Second Edition      

[Предыдущая часть] [Следующая часть] [Приложения к этой части]


Часть вторая: фундамент для движка

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

Прошлая часть являлась по своей сути введением и не несла никакой практической нагрузки, хотя я и научил вас создавать объекты DirectDraw и даже менять разрешение экрана. Один вопрос: ну и что вы со всем этим будете делать?!

Создание универсального модуля
В прошлой части я привел процедуру загрузки "битмапа" в буфер DD. Много полезного она вам вряд ли принесла, но по крайней мере дала понять, с чем приходится иметь дело, программируя DirectDraw. Итак. в этой части я хочу, чтобы вы создали модуль, назовем его mdlDirectDraw7, в который будете пихать все стандартные процедуры, чтобы потом забыв о том, как они работают пользоваться ими с чистой душой. Я решил писать модуль в виде .h файла. Вы же всегда сможете придумать свою структуру - создать классы или нечто подобное. Главное во всем этом смысл.

Сперва, в наш модуль надо сбросить все переменные и ссылки на объекты, относящиеся к DD, затем, туда будут записаны все функции, служащие для работы, создания каких-либо обектов, установок, уничтожения и т. п. Далее, привожу начальный текст этого модуля с комментариями. Почему начальный? Просто сейчас, этот модуль будет на первом этапе работы, то-есть в него мы запишем минимальное количество функций, требуемых для работы. Затем, по мере дальнейшего изучения DirectDraw и DirectX, вы сможете добавлять в этот модуль все новые и новые функции.

//******************************************************************
// Name: mdlDirectDraw7.h
// Autor: Antiloop
// Desc: Модуль с разными функциями для работы с DirectDraw
//******************************************************************

 

#include <ddraw.h>

#pragma comment (lib,"dxguid.lib")
#pragma comment (lib,"ddraw.lib")

#define MSG(x) { MessageBox(NULL,x,"DirectDraw Test",MB_OK); }

LPDIRECTDRAW7 lpDD = NULL; //Объект IDirectDraw7
LPDIRECTDRAWSURFACE7 ddsPrimary; //Главная поверхность
LPDIRECTDRAWSURFACE7 ddsBack; //Задний буфер
LPDIRECTDRAWSURFACE7 ddsPic; //Поверхность для картники
DDSCAPS2 caps; //Структура возможностей "железа"
DDSURFACEDESC2 ddsd; //Структура определяющая поверхность
RECT rc; //Структура RECT
HRESULT rval; //То, что нам функции возвращают

//Объявления функций
IDirectDrawSurface7 *CreateDDSFromBitmap(LPCSTR szBitmap);
BOOL CreateDDFullscreen(HWND hwin, int dispX, int dispY, int dispColor);
VOID DestroyDD();
int ClearBuffer();
void Print(unsigned x,unsigned y,char *str,unsigned r,unsigned g,unsigned b);

Следующая проведура будет вызываться самой первой в будущих программах. Она инициализирует DirectDraw в полноэкранном режиме. То есть создает объект DirectDraw, устанавливает режим дисплея в dispXxdispYxdispColor

 

//*****CreateDDFullscreen*****
//Инициализация DirectDraw в полноэкранном режиме

BOOL CreateDDFullscreen(HWND hwin, int dispX, int dispY, int dispColor)
{
//Создать объект IDirectDraw7
DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL);
//Установить полноэкранный режим
lpDD->SetCooperativeLevel(hwin, DDSCL_EXCLUSIVE |DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
lpDD->SetDisplayMode(dispX, dispY, dispColor, 0, 0);

//Создать главную поверхность
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
lpDD->CreateSurface(&ddsd, &ddsPrimary, NULL);
//Получить задний буфер
ZeroMemory(&caps, sizeof(caps));
caps.dwCaps = DDSCAPS_BACKBUFFER;
ddsPrimary->GetAttachedSurface(&caps, &ddsBack);

return TRUE;
}

Хотя я и сделал эту функцию в виде BOOL, я особо не нагружал себя отловом ошибок :) Вам, я думаю, будет несложно сделать это в зависимости от степени важности вашей программы. А так, код получился компактным и понятным (помните? - главное смысл! )

Если создали, надо уничтожить! Вот процедура, убивающая DirectDraw. Внимание: оффскринные поверхности надо уничтожать отдельно!

//*****DestroyDD*****
//Уничтожение объектов DirectDraw

VOID DestroyDD()
{
if (lpDD!=NULL)
{
ddsPic->Release();
ddsPic=NULL;
if (ddsPrimary!=NULL)
{
ddsPrimary->Release();
ddsPrimary=NULL;
}
lpDD->Release();
lpDD=NULL;
}

}

Следующая функция разбиралась в прошлой главе - загрузка битмапа из файла

//*****CreateDDSFromBitmap*****
//Универсальная функция для создания поверхности DD из файла растра
//Сама создает оффскринную поверхность с размерами подходящими под
//загруженный bmp-файл. Возвращает эту самую созданную поверхность

IDirectDrawSurface7 *CreateDDSFromBitmap(LPCSTR szBitmap)
{
//Внутренние переменные
HBITMAP hbm;
BITMAP bm;
HDC hdcImage;
HDC hdc;
LPDIRECTDRAWSURFACE7 ddsTemp;
DDSURFACEDESC2 ddsdTemp;
DDCOLORKEY ddCK;

//Загрузить картинку из файла в оперативную память
hbm=(HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (hbm==NULL)
{
MSG("Не могу загрузить картинку");
return NULL;
}
//Получить объект картинки
GetObject(hbm, sizeof(bm), &bm);
hdcImage=CreateCompatibleDC(NULL);
HBITMAP tmp_select=(HBITMAP)SelectObject(hdcImage, hbm);

//Создать оффскринную поверхность с размерами, подходящими под
//картинку

ZeroMemory(&ddsdTemp, sizeof(ddsdTemp));
ddsdTemp.dwSize = sizeof(ddsd);
ddsdTemp.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
ddsdTemp.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsdTemp.dwWidth = bm.bmWidth;
ddsdTemp.dwHeight = bm.bmHeight;
lpDD->CreateSurface(&ddsdTemp, &ddsTemp, NULL);
ddsTemp->Restore();

//Перенести картинку из памяти на созданную поверхность
ddsTemp->GetDC(&hdc);
StretchBlt(hdc, 0, 0, ddsdTemp.dwWidth, ddsdTemp.dwHeight, hdcImage, 0, 0, ddsdTemp.dwWidth, ddsdTemp.dwHeight, SRCCOPY);
ddsTemp->ReleaseDC(hdc);

//Убрать картинку из памяти
SelectObject(hdcImage, tmp_select);
DeleteObject(hbm);
DeleteDC(hdcImage);

//Установить ключевой цвет - черный.
ddCK.dwColorSpaceLowValue=0;
ddCK.dwColorSpaceHighValue=0;
ddsTemp->SetColorKey(DDCKEY_SRCBLT, &ddCK);

//Вернуть созданную поверхность с загруженной картинкой
return ddsTemp;
};

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

//*****ClearBuffer*****
//Очищает задний буфер путем заполнения его черным цветом :)
//Будет работать и в более ранних версиях DirectX

int ClearBuffer()
{
DDBLTFX bltfx;
memset (&bltfx,0,sizeof(bltfx));
bltfx.dwSize=sizeof(bltfx);
bltfx.dwFillColor=0;
ddsBack->Blt(0,0,0,DDBLT_COLORFILL | DDBLT_WAIT, &bltfx);
return 1;
}

Понятно, что вы можете очищать не всю поверхность, а только нужные области для улучшения быстродействия.

Я тут еще приписал функцию Print. Не обращайте пока на нее внимание - она просто для отладки :)

//*****Print*****
//Печать на заднем буфере в заданной позиции и заданным цветом.
//Будет работать и в более ранних версиях DirectX

void Print(unsigned x,unsigned y,char *str,unsigned r,unsigned g,unsigned b)
{
HDC hdc;
HFONT Font=NULL;

if(ddsBack->GetDC(&hdc)==DD_OK)
{
SelectObject(hdc,Font);
SetTextColor(hdc, RGB(r,g,b));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, x, y, str, strlen(str));
ddsBack->ReleaseDC(hdc);
}
}

Начинаем рисовать
Ура! Все приготовления закончены и теперь можно чего-нибудь нарисовать.

Наша программа перейдет в режим экрана 640x480x16, очистит задний буфер и нарисует на нем два круга, взятых из файла. Круги будут частично наложены один на другой, чтобы вы поняли, что такое ColorKey и прозрачный цвет.

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

Я подразумеваю, что вы уже подготовили проект, поместили туда файл startup.cpp из прошлой главы, добавьте туда так же только что написанный модуль. Нам еще понадобится рисунок, который мы будем грузить. Он должен находиться в одной папке с исполняемым файлом. Вы можете взять его из архива с готовым проектом (ссылка в самом низу). Основная программа у нас будет опять в файле main.cpp Его написанием мы сейчас и займемся.

//******************************************************************
// Name: main.c
// Autor: Antiloop
// Desc: Собственно, сама программа
//******************************************************************

 

#include "mdlDirectDraw7.h"

Теперь инициализация и загрузка картинки.

//Инициализация DD, создание поверхностей и т.п.
int Start(HWND hwin)
{

//Инициализировать DirectDraw
CreateDDFullscreen(hwin, 640, 480, 16);

//Загрузить графику из файла на новую оффскринную поверхность
ddsPic=CreateDDSFromBitmap("ball.bmp");
if (ddsPic==NULL)
{
MSG("Буфер картинки пуст");
return 0;
}
return 1;
}

Далее мы видим процедуру прорисовки кадра. Обычно именно здесь я размещаю свои "движки", то есть циклы, постоянно обновляющие экран и вызывающие необходимые расчеты. Сейчас "движок" мы делать не будем, потому что задача наша предельно проста - нарисовать два шарика.

//Очередной кадр
int Update()
{
ClearBuffer(); //Очистить "полотно"
//Указать координаты спрайта на оффскринной поверхности
rc.top=0;
rc.left=0;
rc.bottom=100;
rc.right=100;
//Рисуем первый спрайт
ddsBack->BltFast(10,10,ddsPic,&rc,DDBLTFAST_SRCCOLORKEY);
//Рисуем второй спрайт
ddsBack->BltFast(50,50,ddsPic,&rc,DDBLTFAST_SRCCOLORKEY);
//Печатаем текст
Print(200,200,"Демонстрация блиттинга с ключевыми цветами.",255,0,0);
Print(200,220,"Нажми ESC, чтобы выйти...",255,0,0);
//Переводим задний буфер на видимую главную поверхность
ddsPrimary->Flip(NULL, DDFLIP_WAIT);
return 1;
}

//Убить все объекты DirectDraw
void End()
{
DestroyDD();
}

Вот и вся программа.

Для blitting'а, то есть перевода шаблона спрайта на задний буфер используется метод BltFast. Он достаточно быстр и легок в использовании. В качестве параметров у метода задается координаты верхнего левого угла на поверхности на которую происводится перевод, затем указывается исходный буфер, далее, передается структура RECT. В этой сруктуре указана позиция и размеры прямоугольника со спрайтом. И наконец, передаются флаги, влияющие на работу метода. Флаг DDBLTFAST_SRCCOLORKEY говорит, что используется ключевой цвет, указанный для источника, а флаг DDBLTFAST_WAIT говорит, что надо ждать до следующего обновления экрана лучевой трубкой.

Метод Flip применяется для главной поверхности. В качестве параметра ему передается исходная поверхность и флаги, влияющие на работу. Флаг DDFLIP_WAIT говорит, что нельзя совершать какие-либо действия с поверхностью до того, как флиппинг завершится.

Итак, ничего сложного нет. В следующей части мы приступим к созданию анимации.

Приложения

Пример 2

[Вверх]

Приятного программирования, Antiloop

Posted: 23.01.2k1
Autor: Antiloop
<anti_loop@mail.ru>

 

 


Проект
Создание Народного Учебника по OpenGL

Участвовать!
Поиск
Найдите статью или файл:


Рассылка
Новости сайта
La Vision в вашем почтовом ящике








Программирование на С++ Delphi и Паскаль
Центр демо-искусства в России