Podnet k napísaniu tohto článku som dostal, keď som si čítal priania na články a našiel som tému „Direct X a C++“. Uvedomil som si, že na slovenskom a českom internete nie sú takmer žiadne informácie na túto tému a teraz je čas to napraviť.
Tento článok predpokladá aspoň základnú znalosť C++ jazyka. Znalosť Win 32 API a MFC nie je vyžadovaná, pretože ai tu všetko vysvetlíme. Nezabudnite, že keď si nebudete s niečím vedieť rady, pomoc nájdete na www.msdn.com, žiaľ iba v angličtine.
Aplikácie budeme vytvárať pomocou vývojového prostredia Microsoft Visual Studio 2008, ktoré je dostupné ako na stránkach spoločnosti Microsoft (časovo obmedzená verzia na 90 dní) alebo v rámci programu Microsoftu na podporu študentov - Dreamspark. Okrem toho skôr či neskôr budete potrebovať balík DirextX SDK
Keď sa na úvod zamyslíte, ako vlastne počítač zobrazuje 3D objekty na obrazovku (dvojrozmernú), zistíte, že na to potrebujete množstvo matematiky, vzorcov... Našťastie existujú spôsoby, ako sa týmto výpočtom vyhnúť. Jedným z týchto spôsobov je Direct3D. Direct3D je súčasťou DirectX, ktoré sa skladá okrem iného z:
- Direct3D: Umožňuje vykresľovať komplikované trojrozmerné animácie. Je navrhnuté tak, aby zabezpečovalo čo najrýchlejšiu komunikáciu medzi vašim programom a grafickou kartou počítača.
- DirectDraw: Umožňuje vykresľovať dvojrozmerné objekty.
- DirectSound: Umožňuje prácu so zvukom vo vysokej kvalite.
- a podobne...
- Otvoríte si MS Visual Studio.
- Vyberiete: File->New->Project
- V okne Project types (vľavo) vyberte: Visual C++, v pravom okne Templates: Win32 Projects. Potom vyberte kam chcete projekt uložiť a stlačte ok.
- Zobrazí sa vám okno Win32 Application Wizard. Kliknite na next.
- V okne Application Settings (Obr. 1.) zaškrtnite Windows Application a Empty project.
- Kliknite na Finish.
- Teraz máte vytvorený jednoduchý projekt, avšak bez akéhokoľvek kódu. Preto v podokne Solution Explorer kliknite pravým tlačidlom myši na Source File->Add->New Item (Obr. 2) . Pridajte C++ File (.cpp), a uložte ho napr. ako lekcia1 alebo main.
- Teraz máte konečne súbor na pridanie zdrojového kódu. Pridajte nasledujúci kód:
/////////////////////////////////////////
// Jednoducha aplikacia
/////////////////////////////////////////
#include
//Prototypy funkcii
LRESULT WINAPI ProceduraOkna (HWND hWnd, UINT sprava, WPARAM wParam, LPARAM lParam);
void RegistraciaTriedyOkna(HINSTANCE hInstance);
void VytvorenieOkna(HINSTANCE hInstance);
WPARAM KontrolaSprav();
//Globalne premenne
HWND g_hWnd; //manipulator okna
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE,LPSTR, int ) //Funkcia WinMain obdoba funkcie main z konzolovych aplikacii
{
RegistraciaTriedyOkna(hInstance);
VytvorenieOkna(hInstance);
ShowWindow (g_hWnd, SW_SHOWDEFAULT);
UpdateWindow(g_hWnd);
KontrolaSprav();
return 0;
}
LRESULT WINAPI ProceduraOkna (HWND hWnd, UINT sprava, WPARAM wParam, LPARAM lParam)
{
switch (sprava)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
}
return DefWindowProc(hWnd, sprava, wParam, lParam);
}
void RegistraciaTriedyOkna(HINSTANCE hInstance)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = ProceduraOkna;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WinApp";
wc.hIconSm = NULL;
RegisterClassEx(&wc);
}
void VytvorenieOkna(HINSTANCE hInstance)
{
g_hWnd = CreateWindowEx (
0,
"WinApp",
"Jednoducha aplikacia vo Windows",
WS_OVERLAPPEDWINDOW,
100,
100,
600,
450,
GetDesktopWindow(),
NULL,
hInstance,
NULL
);
}
WPARAM KontrolaSprav()
{
MSG sprava;
while(1) //"Nekonečna" smyčka
{
if(PeekMessage(&sprava, NULL, NULL, NULL, PM_REMOVE))
{
if(sprava.message == WM_QUIT)
break; //Ak dostaneme spravu WM_QUIT tak skončime smyčku
TranslateMessage(&sprava);
DispatchMessage(&sprava);
}
else
{
//V tomto momente je aplikacia nečinna a ideme na Direct 3D
}
}
return sprava.wParam;
}
Ak ste ešte doteraz neprogramovali pre Windows, môže sa vám zdať, že na vytvorenie jednoduchého okna je to priveľa práce. Avšak tento kód nemusíte (a pravdepodobne ani nebudete) písať zakaždým znova. Stačí ho skopírovať a vytvárať takmer akékoľvek aplikácie. Teraz sa pozrieme, čo jednotlivé časti znamenajú.
/////////////////////////////////////////
//Jednoducha aplikacia
/////////////////////////////////////////
#include
Bez hlavičkového súboru windows.h sa nezaobíde takmer žiadna aplikácia pracujúca pod Windows. Obsahuje množstvo klasických funkcií, ktoré k tvorbe týchto aplikácií potrebujete.
//Prototypy funkcii
LRESULT WINAPI ProceduraOkna (HWND hWnd, UINT sprava, WPARAM wParam, LPARAM lParam);
void RegistraciaTriedyOkna(HINSTANCE hInstance);
void VytvorenieOkna(HINSTANCE hInstance);
WPARAM KontrolaSprav();
//Globalne premenne
HWND g_hWnd; //manipulator okna
Keďže už iste máte základne znalosti C++, viete, že v tejto časti kódu sa nachádzajú deklarácie prototypov funkcií a jedna globálna premenná. Ich význam si vysvetlime za chvíľu.
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE,LPSTR, int ) //Funkcia WinMain obdoba funkcie main z konzolovych aplikacii
{
RegistraciaTriedyOkna(hInstance);
VytvorenieOkna(hInstance);
ShowWindow (g_hWnd, SW_SHOWDEFAULT);
UpdateWindow(g_hWnd);
KontrolaSprav();
return 0;
}
Funkcia WinMain Je obdoba funkcie main z konzolových aplikácii. Čiže keď spustíme aplikáciu, začne sa vykonávať práve ten kód, ktorý je obsiahnutý v tejto funkcii. Ako prvé je volaná funkcia RegistraciaTriedyOkna(hInstance);
void RegistraciaTriedyOkna(HINSTANCE hInstance)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = ProceduraOkna;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WinApp";
wc.hIconSm = NULL;
RegisterClassEx(&wc);
}
Táto funkcia vytvorí triedu okna a následne ju zaregistruje. Jednotlivé vlastnosti sú:
- style: Špecifikuje štýl triedy. Štýly sa dajú kombinovať pomocou bytového operátora OR(|). Môže mať nasledujúce hodnoty: CS_DBLCLKS, CS_HREDRAW, CS_NOCLOSE, CS_PARENTDC, CS_VREDRAW. Čo tieto štýly znamenajú si môžete pozrieť na stránke msdn.com.
- lpfnWndProc: Špecifikuje Proceduru okna,
- hIcon: Určuje ikonu okna,
- hCursor: Kurzor okna,
- lpszMenuName: Hodnota null znamená, že nebudeme mať menu. Menu v Direct3D aplikaciach nebudeme používať. Viac o ňom možno v seriali o MFC,
- lpszClassName: Názov triedy.
void VytvorenieOkna(HINSTANCE hInstance)
{
g_hWnd = CreateWindowEx (
0,
"WinApp",
"Jednoducha aplikacia vo Windows",
WS_OVERLAPPEDWINDOW,
100,
100,
600,
450,
GetDesktopWindow(),
NULL,
hInstance,
NULL
);
}
Táto funkcia v podstate vytvorí okno, ktoré však ešte nie je zobrazené na obrazovke. Parametre: Prvý parameter (0) sú rozširujúce štýly okna. Za ním nasleduje názov triedy okna, text zobrazený v záhlaví okna a štýly okna (týchto štýlov je skutočne veľa, preto si pozrite ich zoznam a popis na MSDN) . Za štýlmi nasleduje poloha, na akej má byť okno zobrazené (x, y), rozmery okna (tiež najprv x, potom y) a manipulátor okna rodiča, v tomto prípade pracovnej plochy. Poslednými parametrami je manipulátor menu, hodnota HINSTANCE, a NULL ako hodnota lpParam. Tieto parametre nie sú pre Direct3D také dôležité. V prípade, že by bolo treba akúkoľvek zmenu, dám o tom vedieť.
ShowWindow (g_hWnd, SW_SHOWDEFAULT);
UpdateWindow(g_hWnd);
Funkcia ShowWindow zobrazí okno ktoré sme vytvorili pomocou funkcie CreateWindowEx. Funkcia UpdateWindow zaistí, že okno prekreslí svoj obsah a bude v ňom zobrazené to, čo má (zatiaľ len biela plocha).
WPARAM KontrolaSprav()
{
MSG sprava;
while(1) //"Nekonečna" smyčka
{
if(PeekMessage(&sprava, NULL, NULL, NULL, PM_REMOVE))
{
if(sprava.message == WM_QUIT)
break; //Ak dostaneme spravu WM_QUIT tak skončime smyčku
TranslateMessage(&sprava);
DispatchMessage(&sprava);
}
else
{
//V tomto momente je aplikacia nečinna a ideme na Direct 3D
}
}
return sprava.wParam;
}
Teraz sa dostaváme k podstate celého programovania pre windows a tou sú správy a ich spracovanie. System Windows komunikuje so svojimi programami pomocou správ. Zasiela správy pri každej udalosti. Od vytvorenia okna, cez pohyb myšou, kliknutie myšou, stlačenie kláves, potrebe prekresliť okno...
O spracovanie týchto správ sa starajú dve funkcie: prvá je KontrolaSprav a ďalšia ProceduraOkna, o ktorej si povieme neskôr.
V našej funkcii KontrolaSprav beží neustále cyklus, ktorý najprv vyberie správu z fronty a potom rozhodne, či sa nejedná o správu WM_QUIT (túto správu dostane aplikácia, keď má byť ukončená). Ak sa jedná o túto správu, ukončíme cyklus a tým aj celú aplikáciu. Pri akejkoľvek inej správe zavoláme funkciu TranslateMessage, ktorá prekladá správy o stlačení klaves (viac na msdn.com). A nakoniec funkcia DispatchMessage odošle správu procedúre okna. Ak však funkcia PeekMessage na začiatku podmienky if nenájde vo fronte žiadne správy, aplikácia je v podstate nečinná a práve v tento okamih budeme volať Direct 3D. Nemali by ste však vykonávať veľmi (časovo) náročné funkcie, aby vaša aplikácia „nezamrzla“.
LRESULT WINAPI ProceduraOkna (HWND hWnd, UINT sprava, WPARAM wParam, LPARAM lParam)
{
switch (sprava)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
}
return DefWindowProc(hWnd, sprava, wParam, lParam);
}
Funkcia ProceduraOkna spracuváva správy, ktoré sú jej odoslané pomocou funkcie DispatchMessage. Niektoré správy spracujeme my v našich aplikáciach pomocou príkazu switch (v tomto prípade sú to spravy WM_CREATE a WM_DESTROY) a ostatné odošleme na spracovanie systému Windows (každá správa musí byť jedným z týchto dvoch spôsobov spracovaná!).
- hWnd – Manipulátor okna, z ktorého bola správa odoslana.
- sprava – Identifikátor správy.
- wParam – 32 bitový parameter správy (závisí od typu správy).
- lParam – 32 bitový parameter správy (závisí od typu správy).
Dnes sme si vytvorili jednoduchú aplikáciu pre Windows. Nabudúce sa pozrieme na vykreslovanie rovinných utvarov (ešte bez Direct 3D) a na zopár vzorcov okolo toho. A potom sa už konečne pustíme do Direct3D. Dúfam, že sa vám prvý článok páčil, a že sa so mnou podelíte o vaše názory.