[ RayDX @ 05.01.2008. 13:15 ] @
Pozz narode, evo meni se pričinio vaš forum prilično zanimljivim po pitanju 3d programiranja pa evo... Evo par tutorijala da počnete u DirectXu ako već niste, ja sam ih napisao pošto sam već duže vrijeme proveo proučavajući taj API.


[size=18]Što je to DirectX?[/size]

Veterani gaming industrije, bili oni developeri ili krajnji gameri sigurno se sjećaju dana kada su sve igre vrtile na low-level tekst baziranom operativnom sustavu zvanom DOS. Ja osobno se ne sjećam, jer u to vrijeme nisam bio ni u planu ali sam uspio zaigrati sa zakašnjenjem. U to vrijeme, Microsoft bijaše poštovan kao izbor Allmighty Poslovnjaka, a što se tiče nas gamera i gDevelopera, bila je jako siromašna u smislu da bi mogla podržavati u ondašnje vrijeme "napredni" razvoj igara i samo izvršavanje na istom. Gdje je bio problem, vjerojatno se pitate? Pa, problem se nalazio u činjenici da je programer/developer bio u potpunosti izoliran od podležnog grafičkog hardvera koristeći tadašnji tzv. Graphics Device Interface (GDI u nastavku teksta). Taj interface (sučelje, kako hoćete, meni je interface koolišniji) sadržavao je robusnu kolekciju 2D texta i nekoliko primitivnih funkcija za iscrtavanje koje je developer, naravno, koristio da iscrta 2D output na zaslon korisnika. Zapravo, ovo je imalo i svojih dobrih strana, developer se npr. više nije morao zabrinjavati oko hardverskih kompatibilnosti. Developeru je sve izgledalo isto. Ako ste željeli pravokutnik npr. mogli ste jednostavno dati instrukciju GDIju i on bi, kao međusloj/posrednik između hardvera i developera, napravio ostalo.

Dakle, GDI je izrađen da bude robustan i stabilan. Nažalost, "savršenstvo" je došlo po svoj danak jer developer nije izravno mogao pristupiti video memoriji. Situacija je jednostavno neprihvatljiva za GameDev projekte jer sve naredbe je morao GDI kao međusloj konvertirati tamo 'vamo i tako smanjujući programersku mogućnost da utječe na neke segmente developmenta. I kao šlag na govancu, da se izrazim, bio je izrazito spor.
Igre koje su se vrtile direktno kroz DOS nisu patile od sličnih problema. Bile su prilično brze, ali njihov razvoj i samo igranje bio je pravi izazov, tako da je i to brzo palo u vodu.

Gledajući situaciju iz perspektive developera, PC platforma je postala iznimno popularna da su mnogi proizvođači počeli razvijati grafičke kartice, svaka sa drugačijim čipsetom. To je značilo ogromne muke za developere s obzirom da su morali osigurati kompatibilnost sa različitim hardverom koji se svakim danom gomilao. Nije bilo nikakvog standardnog API-ja (Application Programming Interface) za grafičko renderiranje, a kamoli potpunog multimedia development frameworka. S obzirom da se svaka grafička kartica morala posebno programirati, developeri su morali napraviti gomilu različitih verzija funkcija za iscrtavanje da bi radila igra sa različitim grafičkim hardverom dostupnim na tržištu. Ovo je definitivno uništavalo hobby-developere poput nas ovdje jer nisu imali para da bi kupovali sav mogući grafički hardver da bi osigurali kompatibilnost svoje igre. Kao igrač, mnogi su uvidjeli da su vražja posla upletena sa instalacijom neke jednostavne igre jer većina nije bila dovoljno "tehnološki prosvjetljena". Stalno bi gnjavili, koja grafička, video memorija, blablabla, tako da je tehnička podrška u softverskim kompanijama definitivno bila golem trošak. Konzola poput Super Nintenda koja je bila tehnički nadmoćna nad PC-em imala je puno lakši način "instalacije"... Poznati plug&play heheh...

Microsoft je uvidio da će morati reagirati vrlo brzo ako žele da Windows postane dominantan u gaming industriji, te su ubrzo nakon izlaska Windowsa 95 izbacili van biblioteku za razvoj multimedijalnih sadržaja nazvanu 'The Game SDK'. Ovo je bio predak današnjeg DirectXa, kojeg smo svi naučili voljeti i tolerirati, pogotovo njegovu desetu inačicu *sarkazam*. DirectX kao ime je usvojeno tek u inačici 2.0, vrlo vjerojatno zbog uspjeha u razvoju potpune multimedijske biblioteke za razvoj bogatih aplikacija. Iako su početne inačice DirectXa bile malo grube oko rubova, možemo reći da je odrastao u velikog ekonomista tijekom proteklih par godina. DirectX 5 je vjerojatno prvi krenuo sa ozbiljnim developmentom, vrijednim spomena. Danas smo na već na verziji 10, i iako nam se ne sviđaju taktike Microsofta moramo priznati da je u ovo malo vremena koje je proteklo, gaming industrija stvarno evoluirala i danas je na vrlo visokoj stepenici uvelike zahvaljujući DirectXu i njegovim developerima, pripadnicima mrskog Microsofta.

DirectX je ponudio rješenje mnogim problemima koji su mučili razvoj igara od samih početaka. Kao prvo, razvijen je za Windows platformu, koju drugu, enivejz? Ovo je značilo da developeri mogu napraviti svoje igre u okružju gdje su Win32 API fičrsi poput multithreadinga i UIa već dostupni. No shit, Einstein? heheh. Kao drugo, ustupilo nam je jedinstven API, bolje rečeno framework danas baš kao što je GDI to napravio prije, ali ovaj puta je brže, mnogo brže. Developer se više nije morao brinuti o grafičkom hardveru krajnjeg korisnika, nego je prepustio DirectXu da se za to pobrine. Ovo je omogućeno korištenjem "driver programa". Danas grafičke kartice koje podržavaju DirectX (danas nema niti jedne na tržištu koja ne podržava mislim) dolaze sa driverom koji se instalira na sustav korisnika. Driver piše naravno proizvođač i to je naravno vrlo brz komad softvera i služi kao jako tanak sloj odmah iznad grafičke koji uzima različite zahtjeve od mnogih directx funkcija koje programer upotrijebi, i pretvori ih u instrukcije razumljive hardveru. Ovo znači da DirectX može "pričati" sa svim grafičkim karticama kao da su iste iako su veoma različite jedne od drugih. Dobra strana koja se uspjela zadržati pokraj svih ovih mogućnosti jest činjenica da nam api ostavi prilično kvalitetne slobode i pristupa hardveru krajnjih korisnika. Kad već hvalim toliko DirectX moram spomenuti da iskorištava 3d hardversku akceleraciju bez dodatnog kôdiranja od strane developera što štedi vrijeme i novac... Vrijeme je novac. A vrijeme ne postoji... Kako onda novac postoji... WTF... Heheh, ajmo dalje. Najnoviji grafički hardver također ubrzava 3d matematiku (što je prije bilo u domeni CPUa prije). To znači da mnoge grafičke kartice mogu preuzeti teret od nekoliko tisuća matematičkih kalkulacija potrebnih da bi se scena renderirala ostavljajući presvete resurse CPUa da se bave kreativnijim stvarima... AI anyone? :)

Podjela DirectX Frameworka

Kao i svaki kvalitetan API, DirectX isto ima podjelu u više zasebnih modula. Svaki pokriva određen segment u razvoju multimedijalnih aplikacija. Pođimo opisati ukratko svaki od tih modula da dobijete neku bolju sliku kako je sastavljen i što čini taj magični krug.

DirectX Graphics

U starijim verzijama DirectXa, 2D i 3D operacije bile su podijeljenje u dva zasebna API-ja zvana DirectDraw i Direct3D. Dolaskom DirectXa 8.0 i nadalje to se spaja u jedan API nazvan DirectX Graphics. Mnogi ljudi još uvijek imaju naviku nazivati DirectX Graphics kao Direct3D. Kao što ćete vidjeti u kasnijim tutorijalima, većina DirectX Graphics funkcija i sučelja počinju sa D3D (skraćeno od Direct3D ofkorz), pa naziv stari je još uvijek opravdan.

DirectX Audio

DirectX Audio modul sadržava funkcionalnost organiziranja i puštanja audio uzoraka i glazbe unutar vaših aplikacija. Uključuje podršku za 3D audio te hardversko procesiranje zvuka i okolišnih efekata. DirectX Audio je prijašnje također bio podijeljen na dva manja API-ja zvani DirectSound i DirectMusic, no spajanje se događa sa DirectXom 8.

DirectX Input

DirectInput modul sadržava funkcionalnosti koje kontroliraju ulaznu periferiju koju korisnik koristi. Nudi funkcije za očitavanje inputa sa "uređaja" poput gamepadova, joystickova, volana ali naravno i miša i tipkovnice.

DirectX Play

Ovaj modul nudi funkcionalnost korištenu uglavnom u implementaciji MP igara i sličnih aplikacija. Uključuje podršku za primanje i odašiljanje podataka kroz recimo LAN, Internet, craplikethat. Kao i sa većinom drugih aspekata DirectXa, ovaj modul dizajniran je kao aplikacijiski sloj koji koristi sustav za primanje/odašiljanje podataka bez obzira na podložeću mrežnu infrastrukturu na kojoj korisnikov PC leži.

DirectShow

Zadnji, ali ne i najnebitniji jest DirectShow. Nudi fičrse koje sjedinjavaju mogućnosti snimanja i plejbeka visokokvalitetnih streamova. Ovo uključuje podršku za mnoge popularne formate poput MPEG, AVI, ASF i MP3 fileove.
[ RayDX @ 05.01.2008. 13:16 ] @
Evo nakon nekog vremena vraćamo se u ovaj zapušteni topic... Jeste li spremni da krenemo u osnove DirectX programiranja?

No, kao prvo moramo postaviti neki starting point da ne krenemo skroz ispočetka. Na kraju krajeva, trebali biste imati neko znanje C++a (pokazivači, polja, OOP, strukture). Tijekom vašeg "obrazovanja" ne očekuje se da sve prototipe funkcija učite napamet niti išta drugo po tom pitanju, to bi bilo suludo. Bilo bi fino ako uhvatite čemu što služi, a za ostalo imate ovaj tutorial, dokumentaciju DirectXa te Google naravno ( i nas ovdje). Kao što znate, bez alata, nema ni zanata. A alate koje ćemo koristiti su: Visual C++ (Express Edition) sa DirectX SDK... I to je to. Spremni ste za lansiranje!

---------------------------------------------------------------------------

Osnove Win32 API-ja

Svi znamo što je DirectX iz prošle lekcije, također logično je zaključiti da se vrti na Windowsu (i kako Microsoft implicira, samo na njemu). Windows je zasnovan na konceptu prozora(windowsa) koji se grade uz pomoć Win32 API-ja. Vjerovali ili ne, DirectX pogonjene aplikacije su također prozori (no shit, Einstein!) pa nam nema druge, nego da krenemo od osnovama Win32 API-ja. Evo jedan primjer console aplikacije sa kojima smo se tako dugo družili:

Code:
#include <iostream>    // uobicajeni input/output header file

int main()    // program pocinje main funkcijom
{
    cout << "Pozz" << endl;    // ispisi "Pozz" na zaslon
    return 0;    // program/funkcija izvrsena uspješno
}


Vjerojatno znate već da je main() funkcija ulazna točka u svaki program. Ovaj program jednostavno ispisuje dosadnom bijelom bojom "Pozz" na crnu pozadinu Command Prompta. Ništa posebno, zabavno i teško da će ikoga sa mozgom impresionirati (možda neku pijanu plavušu). Ovo gore što vidite je primjer najednostavnijeg programa u C++u. Windows aplikacija, na drugu stranu, je različita po tome što ima dvije funkcije koje joj omogućuju rad sa Windowsom.

Jedna od tih je podjednaka već navedenoj main() funkciji. Druga omogućuje aplikaciji da bude pokretana događajima (iliti event-driven). Ti eventovi, ako ne znate, su interakcija korisnika i aplikacije poput klikanja štakorom, pritisak na tipku i tako dalje. Kada se takav neki event dogodi, Windows ga naravno dokumentira u poruku koju odašilje vašem programu da je procesira i interpretira. Dakle, drugo-spomenuta funkcija rukuje tim porukama i sadrži kôd koji treba pokrenuti pri zaprimanju određene poruke. Mi ćemo sada baciti se na pisanje na našeg prvog prozora i kao prvu funkciju definirati ćemo... WinMain()!

WinMain() funkcija je ekvivalent main funkciji u konzolnim programima. To je mjesto gdje aplikacija započinje i gdje se radi početna inicijalizacija. Inače bih definirao posebnu funkciju gdje bih napravio prozor no za potrebe ovog tutoriala, to ću napraviti izravno u WinMain() funkciji da dodatno ne kompliciramo stvari. Prvo ćemo proći kroz prototip WinMain() funkcije:

Code:
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow);


Ovako ga sveta dokumentacija deklarira... Idemo malo secirati ga na manje dijelove da skužite ovaj naizgled strašni komad kôda. U duši je to obrijani pekinezer ; )

WINAPI

Koji li je sad ovo vrag, pitate se? Da, zalegao je baš između povratnog tipa funkcije i imena WinMain. Radi se o metodi dodjeljivanja parametara, kraće rečeno, redoslijedu čitanja parametara. Normalno to ide od desno prema lijevo, ali WINAPI ga obrne pa ide lijevo-nadesno. Zašto je to tako, pa to baš i nije najbitnija stvar na svijetu ali Windows želi tako da bude. So it shall be then!

HINSTANCE hInstance

Ovo je prvi parametar i stoji za "handle to an instance". Handle je 32-bitni integer koji indentificira nešto, poput objekta. Instanca je kopija aplikacije. Zbog multitaskinga i mogućnosti da se program vrti u više kopija istodobno, Windows treba način da ih indentificira i "žiki" ko' je ko'. To radi tako da dodijeli svakoj instanci "ručicu iliti handle", tj. integer koji omogućuje razlikovanje kopije aplikacija.

HINSTANCE hPrevInstance

Drugi parametar je sličan prvom, stoji za "handle to the previous instance". Što ovo znači? Npr. ako ima više kopija otvoreno, hPrevInstance će sadržavati ručicu ili handle zadnje instance aplikacije koja je napravljena. U biti, ovo je sve što trebate znati o njoj...

LPSTR lpCmdLine

Možda ste zamijetili po nazivu, ovo je dugački pokazivač iliti Long Pointer. On pokazuje na string koji sadržava zapovjednu liniju koja poziva aplikaciju. Ne kužite? Ni ja isto (joke). Evo primjer: ako ste imali aplikaciju nazvanu "MačkeiKerovi.exe" i pokrenuli je sa Run "programom" iz Start Menua, mogli biste dodati na bazu imena "MačkeiKerovi.app devMode" ili "MačkeiKerovi.app cheatMode". U svakom slučaju, sada bi program tražio za dodatne parametre. Shvaćate sada?

int nCmdShow

Ovaj parametar pokazuje kako će se prozor prikazati kada se stvori. Npr, ovo bi moglo pozvati prozor da bude minimiziran, maximiziran i drugi -miziran... Ovo vam neće biti potrebno, ali tu su ako vam zatrebaju.

-------------------------------------------------------------------

V-v-v-v-vrijeme je za d-d-d-d-dvoboj... Dosta teorije...
Dosta smo zjakali o prototipima funkcije WinMain(). 'Ajmo nešto napraviti. Nešto novo, nešto divlje... Predlažem da napravimo napokon taj naš prozor. Može? Naravno da može :)

Ovo dolje je naša aplikacija, kao što vidite, više nismo na običnim console aplikacijama.

Code:

// osnovni header fileove koji su nam potrebni

#include <windows.h>
#include <windowsx.h>

//prototip WindowProc funkcije (sjećate se, ona koja rukuje sa porukama)

LRESULT CALLBACK WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);

// ulazna točka u svaku windows aplikaciju

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    // "ručica za prozor", ispuni se funkcijom
    HWND hWnd;
    // struktura prozora
    WNDCLASSEX wc;

    // očisti strukturu prozora
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // ajmo popuniti članove ove strukture
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass1";

    // registriraj klasu koristeći informacije date wc-u
    RegisterClassEx(&wc);

    // napravi prozor i koristi rezultat kao ručicu (hWnd)
    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",    // Ime windows klase
                          L"PCPlay Ucionica",   // Naziv prozora
                          WS_OVERLAPPEDWINDOW,    // stil prozora
                          300,    // x-pozicija prozora
                          300,    // y-pozicija prozora
                          500,    // širina prozora
                          400,    // visina prozora
                          NULL,    // nema roditeljskog prozora
                          NULL,    // ne koristimo izbornik
                          hInstance,    // ručica aplikacije (remember)
                          NULL);    // ne koristi se sa više prozora

    // napokon ju prikaži:
    ShowWindow(hWnd, nCmdShow);

    // ulaz u glavni loop

    // ova struktura sadrži predivne poruke koje nam Windows šalje
    MSG msg;

    // dakle, čekamo dok dobijemo poruku od Windowsa
    while(GetMessage(&msg, NULL, 0, 0))
    {
        // prevedi u programu razumljiv format
        TranslateMessage(&msg);

        // pošalji WindowProc() funkciji da ju procesira
        DispatchMessage(&msg);
    }

    // vrati ovaj dio WM_QUITa Windowsu
    return msg.wParam;
}

// ova funkcija rukuje sa svim porukama koje naša aplikacija prima
// dakle, poštar :)

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // traži određenu akciju koja treba biti provedena pri dobivanju poruke
    switch(message)
    {
        // ovo se pročita kada korisnik pritisne crveni X 
        case WM_DESTROY:
            {
                // zatvori aplikaciju
                PostQuitMessage(0);
                return 0;
            } break;
    }

    // rukuj sa svakom porukom koju WindowProc nije procesirao osobno
    return DefWindowProc (hWnd, message, wParam, lParam);
}


Sada kada bi ovo pokrenuli, dobili bi fini prozorček. Iscrtan vama pod nosom:



Uf, koliko toga ima... Komentari su na mjesto ali definitivno niste sve pohvatali... Idemo, top to bottom:

Osnovno što kod svake windows aplikacije je potrebno napraviti jest registrirati njenu klasu, napraviti prozor i prikazati ga naravno na zaslonu korisnika. Najjednostavnije postavljeno, windows klasa ili klasa prozora je osnovna struktura koju Windows koristi da bi rukovao osobinama i akcijama različitih prozora. Nećemo i ne da mi se ulaziti u detalje svega ovoga.. Za detalje uvijek imate MSDN. Uglavnom, napravite WNDCLASSEX strukture, date podatke njoj i onda koristeći to registrirate klasu prozora.

WNDCLASSEX wc;

Ovo je struktura koja sadrži informaciju o windows klasi. Nećemo ići skroz kroz njen sadržaj, pošto neki neće ni biti potrebni u game programiranju ali vidi se uglavnom kroz kontekst isto. Kao što sam rekao, MSDN je biblija za svakog Windows programera. A biblija kao biblija ima odgovor na sva vaša pitanja...

ZeroMemory(&wc, sizeof(WNDCLASSEX));

ZeroMemory je funkcija koja pokreće cijelu strukturu ili bolje da kažem briše sve iz strukture i postavlja na NULL. Cijeli blok memorije dakle. Adresa je dodijeljena kroz prvi parametar (&wc). Drugi parametar potreban je da kaže funkciji koliko je struktura velika. Dakle, smisao je da se pokrene struktura wc na NULL.

wc.cbSize = sizeof(WNDCLASSEX);

Ovo služi da postavi strukturu na potrebnu veličinu. A to radimo sa sizeof() operatorom.

wc.style = CS_HREDRAW |CS_VREDRAW;

U ovom članu definiramo kakav je stil našeg prozora. Ima mnoštvo vrijednosti koje ovdje možemo ubaciti, ali većinu nećemo koristiti za naše potrebe. Moguće vrijednosti pronađite u MSDNu pod WNDCLASSEX. Za sada koristit ćemo CS_HREDRAW i logički ili (|) sa CS_VREDRAW. Što ovo dvoje radi jest da kažu Windowsu da iscrta ponovno prozor u slučaju da se pomakne vertikalno ili horizontalno. Uglavnom, korisno samo kod aplikacija nevezanih uz igre.

wc.lpfnWndProc = (WNDPROC)WindowProc;

Što ova vrijednost radi jest da govori windows klasi koju funkciju koristiti kada dospije neka poruka od Windowsa. U našem programu, ova funkcija odgovara WindowProc(), ali također može imati drugačiji naziv poput WinProc(), get the point? Nije bitno kako se zove ako je pravilno damo kao vrijednost u član lpfnWndProc strukturi wc u ovom slučaju.

wc.hInstance = hInstance;

S ovim smo upoznati. Ovo je ručica kopiji naše aplikacije. Jednostavno treba postaviti vrijednost koju smo dali WinMain().

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

Ovaj član pohranjuje uobičajeni kursor miša za windows klasu. Ovo je napravljeno kroz return vrijednost funkcije LoadCursor(), koja sadrži dva parametra. Prvi je hInstance aplikacije koja pohranjuje kursor. Nećemo ulaziti u ovo, nema smisla, samo puknite NULL. Drugi parametar odnosi se na vrijednost koja sadrži uobičajeni kursor. Ako želite laprdekati i gledati druge mogućnosti... MSDN, mensch!

wc.hbrBackground = (HBRUSH)COLOR_WINDOW;

Ovaj član sadrži "brush" u nazivu koji se koristi da oboji pozadinu. Ovo nam neće biti potrebno u DirectXu, nego ćemo ga ostrakizmom protjerati iz naše aplikacije. Čitaj - iskomentirat ćemo ga :)

wc.lpszClassName = L"WindowsClass1";

Ovo je ime klas koju gradimo. Nazivamo ju "WindowClass1" jer nemamo mašte. Nije bitno kako se zove. Može biti "SečenovaBaka" ako se osjećate posebno kreativno. ^^ Što se tiče onog čudnovatog L ispred stringa. To govori compileru da string bi trebao biti napravljen od 16-bitni Unicode charactera, a ako ne znate što je unicode... GOOGLE!

RegisterClassEx(&wc);

Ova funkcija se praktički sama objašnjava. Ona konačno registrira sva ova čudesa iznad. Ide jedan parametar, a to je adresa wc strukture koju smo maloprije popunili. Komplicirano, zar ne?

-----------------------------------------------------------

Vjerovali ili ne, sve ovo dosad je bila registracija klase samo... Baf XD
Hajdemo sada napraviti taj prozorček. Kada je naša klasica fino napravljena možemo napraviti taj prokleti prozor. Samo ćemo opet morati proći kroz mnoštvo patnje i boli... Dakle prototip CreateWindowEx():

Code:
HWND CreateWindowEx(DWORD dwExStyle,
                    LPCTSTR lpClassName,
                    LPCTSTR lpWindowName,
                    DWORD dwStyle,
                    int x,
                    int y,
                    int nWidth,
                    int nHeight,
                    HWND hWndParent,
                    HMENU hMenu,
                    HINSTANCE hInstance,
                    LPVOID lpParam);


Divno, zar ne? Ah, ne prigovarajte, niste vi taj koji piše :P
Gladan sam, žedan sam, a u pozadini slušam Nightwish... Jesam li spomenuo da sam gladan? Hehe, dosta cviljenja... NA POSAO!


DWORD dwExStyle,

Ovo je nadodatak na četvrti parametar, dwStyle pa se nećemo previše bakćati sa detaljima, jednostavno puknite NULL i haj'mo dalje.

LPCTSTR lpClassName,

Naizgled dugačko, no ipak tako jednostavno. Ovo je ime klasu koju naš prozor koristi. Pošto imamo samo jednu klasu, koristit ćemo L"WindowsClass1". Ovaj string također koristi 16-bitne Unicode charactere i to je razlog L-a ispred njega.

LPCTSTR lpWindowName,

Ovo je ime prozora, ono što piše u gornjem lijevom kutu. Također koristi Unicode.

DWORD dwStyle,

Ovo je mjesto gdje deklariramo različite opcije za naš prozor. Možemo stilizirati ga u biti, izbaciti stvari koje smatramo da nam ne trebaju ili ne smiju biti omogućene. Više detalja o mogućim opcijama, naravno, po 3.14159265 put, MSDN pod CreateWindowEx(). Mi ćemo koristiti WS_OVERLAPPEDWINDOW, što je najjednostavniji način da dođemo do osnovnih fičra prozora.

int x, int y, int nWidth, int nHeight,

Ovo vjerojatno svi već kužite... Dakle pozicija prozora na zaslonu i širina te visina istog.

HWND hWndParent,

Ovo je parametar koji govori windowsu da li ima neki roditeljski /parent\ prozor koji sadrži druge prozorčiće. Nema ga, dakle null. PaintShop Pro npr sastoji se od jednog parent prozora i malih prozorčića. Svi povezani u jedan app.

HMENU hMenu,

Ovo je ručica za izbornik. Nemamo ga, dakle NULL.

LPVOID lpParam

Ovo je parametar koji bi koristili da tvorimo više prozora. Mi ih ne radimo, opet NULL postavljamo kao vrijednost.

Povratna vrijednost

Sve ovo što unutar ove funkcije uradimo (CreateWindowsEx()) spremamo direktno u našu hWnd varijablu. I to je sve što se tiče kreiranja prozora.

----------------------------------------------------------

Sljedeći zadatak jest da prikažemo taj prozor koji cijeli dan radimo. To je prilično jednostavan zadatak. Koristimo funkciju ShowWindow() sa povratnom vrijednosti BOOL.

Kako smo se navikli, sve započinjemo prototipom:
Code:

BOOL ShowWindow(HWND hWnd,
                int nCmdShow);



HWND hWnd,

Ovaj parametar je ručica prozoru kojeg smo toliko mukotrpno stvarali. Ovdje vraćamo vrijednost koju nam je Windows vratio nakon funkcije CreateWindowEx().

int nCmdShow

Ako zavirite u sjećanje, zadnji parametar u WinMain() funkciji... Ovo je mjesto gdje ga koristimo. Naravno, ne moramo, ali u ovom slučaju ići ćemo prema pravilima i dati Windowsu što želi. Ovo u fullscreenu neće biti bitno. Dakle, 'nough chit-chat.

---------------------------------------------------------


Iako smo napravili prve tri faze, ovo još nije gotovo ( OMG!!)... Hehe, ima još jedna mala fazica i funkcijica koja je prijeko potrebna. Prvo ćemo se pozabaviti glavnim loopom. On glasi u našem programu ovako:

Code:
// ova struktura sadrži predivne poruke koje nam Windows šalje
    MSG msg;

    // dakle, čekamo dok dobijemo poruku od Windowsa
    while(GetMessage(&msg, NULL, 0, 0))
    {
        // prevedi u programu razumljiv format
        TranslateMessage(&msg);

        // pošalji WindowProc() funkciji da ju procesira
        DispatchMessage(&msg);
    }


Ajmo od vrha prema dolje:

MSG msg;

MSG je struktura koja sadrži sve podatke potrebne za zasebnu poruku. Mislim da nećemo direktno pristupati sadržaju njenom. Tako da napravite to i jednostavno nastavite :)

while(GetMessage(&msg, NULL, 0, 0))

GetMessage() je funkcija koja preuzima svaku poruku iz reda čekanja i ubacuje ju u našu msg strukturu. Uvijek vraća true, osim kada program se gasi, tada vraća naravno, false. Na taj način, naš while() loop je razbijen samo kada je program izvršen uspješno.

GetMessage() funkcija neće biti nama od velike koristi jer ću vas već u sljedećoj lekciji upoznati sa boljim načinom.

WindowProc() funkcija

Sada kad imamo većinu toga dovršeno, ostaje nam još poštar da se sredi. Gore spomenuta GetMessage() funkcija dobiva poruku, prevodi ju u format razumljiv programu i šalje ju ovdje. Kada je WindowProc() pozvan četiri podatka moraju stići da bi bilo uspješno.

Code:
LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);



U ovom slučaju ćemo se usredotočiti na sadržaj ove funkcije, izostavljajući parametre koje ću objasniti prilikom sljedećeg tutoriala kada vam predstavim zamjenu za GetMessage() funkciju. Kada poruka uđe u WindowProc, možemo koristiti uMsg argument da ustanovimo koja je to poruka. 99% programera koristi switch() izjavu da ustanove o čemu se radi.

Code:
// traži određenu akciju koja treba biti provedena pri dobivanju poruke
    switch(message)
    {
        // ovo se pročita kada korisnik pritisne crveni X 
        case WM_DESTROY:
            {
                // kôd ide ovdje
            } break;
    }


Ovako izgleda switch izjava za kôd koji treba vrtiti. Naravno, imamo ovdje samo WM_DESTROY, što znači da će se druge poruke ignorirati. WM_DESTROY poziva se samo na kraju kada se prozor zatvara. Ovo činimo kada želimo zatvoriti potpuno program:

Code:
// traži određenu akciju koja treba biti provedena pri dobivanju poruke
    switch(message)
    {
        // ovo se pročita kada korisnik pritisne crveni X 
        case WM_DESTROY:
            {
                // zatvori aplikaciju
                PostQuitMessage(0);
                return 0;
            } break;
    }


PostQuitMessage() funkcija šalje vrijednost '0' WM_QUITu. Ako se prisjetite opet, u vašem glavnom loopu. GetMessage() vraća samo false ako se program gasi. 0 = false. Dakle, omogućuje da se WinMain() završi uspješno.


Evo ga... Ovo je tek mali uvod u Win32, ali moram reći ako ste naučili sve što sam vam ovdje prepričao da niste loši... Polako napredujete i uskoro ćete imati priliku baciti se u Direct3D.
[ RayDX @ 05.01.2008. 13:17 ] @
Evo me, jeste spremni da vam izlistam novu lekciju? Naravno da ste spremni! Kao prvo, isprike na podebljem kašnjenju. Ovo će biti prilično dugačka lekcija (u dva poglavlja jer ćemo postupno ići do našeg prvog trokuta u fullscreen prostoru. Bez previše zjakanja i okolišanja, here goes nothin'! Ovo će biti lekcija koja će zatvoriti Win32 poglavlje. Odmah pišem sljedeći dio, pa se strpite.

Voajerska poruka

Kao što naslov kaže, danas ćemo govoriti o nestašnom bratu GetMessage poruke koji će nam zapravo omogućiti razvoj 3D/2D aplikacija kako spada. Voajerska (nisam siguran da sam dobro napisao hehe) poruka iliti PeekMessage() funkcija, koji je to vrag? Pa naša zamjena na klupi koja upravo izlazi na teren. Kao prvo, osjećam čudni nagon da vam pojasnim zašto baš PeekMessage kad je GetMessage funkcija radila super. Istina, u Windows aplikacijama je prilično solidna, no kod 3D aplikacija moramo vrtiti minimalno 30 sličica u sekundi da bi mogli uživati u ugodnoj fluidnoj atmosferi igre. Eh, tu GetMessage zakaže jer kada mu windows da poruku on ju procesira i čeka dok se vrati informacija njemu da ide dalje. PeekMessage kako je maštovito nazvan, on ju primi, pošalje i ide dalje, ništa drugo ga ne zanima što nam je kritično kod 3D aplikacija kako ćete vidjeti kasnije kada budem objašnjavao swap chainove i back buffere. Zasada nadam se da ste shvatili ovu definiciju i držite se nje. Pređimo sada na prototip same PeekMessage funkcije:

Code:
BOOL PeekMessage(LPMSG lpMsg,
                 HWND hWnd,
                 UINT wMsgFilterMin,
                 UINT wMsgFilterMax,
                 UINT wRemoveMsg);


Ah... Prva četiri argumenta su nam dobro poznata, ako nisu to znači da ste lijeno čitali prvi dio pa vam preporučam da se vratite na prijašnju lekciju te da naučite to. Hehe, ako ste malo zaboravili, samo idite tamo i podsjetite se.

UINT wRemoveMsg

Čemu ovo služi? Prema onome što mi je dokumentacija rekla, indicira da li će zaprimljena poruka ostati ili ne u event queueu ( eng. queue (kjui) == red ). Tu stavljamo ili PM_REMOVE ili PM_NOREMOVE. Uglavnom, mi ćemo se koristiti prvom zasad jer ona vadi poruke iz queuea nakon što su pročitane. Kako će izgledati sada naš kôd:

Code:
// primijetite kako ovo zapravo stvara infinite loop
while(TRUE)
{
    // Provjeri za porukice u event queueu... Kako glupa riječ ^^
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        // Ako je poruka jednaka WM_QUIT, break out!
        if (msg.message == WM_QUIT)
            break;

        // Standarno procesiranje kroz WindowProc funkciju 
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

}


if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

Vjerojatno ste zamijetili ovo kao veću novost u promijenjenom kôdu. Vrlo je jednostavno zapravo, pošto više ne čekamo poruke dok se vrate nazad nego jednostavno jurimo dalje potrebno je samo da funkcija preleti preko sadržaja poruke i ide dalje. Koristimo if da nam vrati ili 1 (true) ukoliko ima poruke, a 0 (false) ako nema. Malo izgleda čudno ali sve je logično, rekao bi Tuvok.

if (msg.message == WM_QUIT)

Otkud sad ovo ovdje gore, pitate se? Ako poruka kaže WM_QUIT, znači da je vrijeme da izađemo iz našeg infinite loopa. Ako ste kao ja, dva sata u budućnosti stalno, vjerojatno se pitate, a kako ćemo samo izaći iz fullscreena poslije? Hehe, vidjet ćete...

Ako ste se malo zagubili u kôdu, evo vam moj:

Code:
// osnovni header fileove koji su nam potrebni

#include <windows.h>
#include <windowsx.h>

//prototip WindowProc funkcije (sjećate se, ona koja rukuje sa porukama)

LRESULT CALLBACK WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);

// ulazna točka u svaku windows aplikaciju

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    // "ručica za prozor", ispuni se funkcijom
    HWND hWnd;
    // struktura prozora
    WNDCLASSEX wc;

    // očisti strukturu prozora
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // ajmo popuniti članove ove strukture
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass1";

    // registriraj klasu koristeći informacije date wc-u
    RegisterClassEx(&wc);

    // napravi prozor i koristi rezultat kao ručicu (hWnd)
    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",    // Ime windows klase
                          L"PCPlay Ucionica",   // Naziv prozora
                          WS_OVERLAPPEDWINDOW,    // stil prozora
                          300,    // x-pozicija prozora
                          300,    // y-pozicija prozora
                          500,    // širina prozora
                          400,    // visina prozora
                          NULL,    // nema roditeljskog prozora
                          NULL,    // ne koristimo izbornik
                          hInstance,    // ručica aplikacije (remember)
                          NULL);    // ne koristi se sa više prozora

    // napokon ju prikaži:
    ShowWindow(hWnd, nCmdShow);

    // ulaz u glavni loop

    // ova struktura sadrži predivne poruke koje nam Windows šalje
    MSG msg;

    // primijetite kako ovo zapravo stvara infinite loop
    while(TRUE)
    {
        // Provjeri za porukice u event queueu... Kako glupa riječ ^^
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
            // Ako je poruka jednaka WM_QUIT, break out!
            if (msg.message == WM_QUIT)
            break;

            // Standarno procesiranje kroz WindowProc funkciju 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }

    }

    // vrati ovaj dio WM_QUITa Windowsu
    return msg.wParam;
}

// ova funkcija rukuje sa svim porukama koje naša aplikacija prima
// dakle, poštar :)

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // traži određenu akciju koja treba biti provedena pri dobivanju poruke
    switch(message)
    {
        // ovo se pročita kada korisnik pritisne crveni X
        case WM_DESTROY:
            {
                // zatvori aplikaciju
                PostQuitMessage(0);
                return 0;
            } break;
    }

    // rukuj sa svakom porukom koju WindowProc nije procesirao osobno
    return DefWindowProc (hWnd, message, wParam, lParam);
}


Razlike u izgledu programa nema, ali uskoro ćete shvatiti zašto smo prolazili kroz ovo.
[ RayDX @ 05.01.2008. 13:19 ] @
[size=18]Welcome to the World of DirectX (Additional fees may apply)[/size]



Ovo gore nećete još dugo proizvoditi... Ako i nakon ovog nastavite čitati, onda ste vrijedni DirectXa. Sada ću što kraće nastojati udarati sa teorijom, pretpostavljam da imate osnovno znanje srednjoškolske algebre, znate što su koordinate, osnovno poznavanje trigonometrije (valjda svi znaju, ja imam 16 godina pa sam upoznat s njom... oke, 15 :(). Nastojat ću održati ovo na što kraćim definicijama no malo teorije je potrebno. Za sve ostalo, tu je izvrsna dokumentacija od strane velikog M$a. Moram još napomenuti da ćemo za vrijeme igranja sa DirectXom nabasati na više pointera nego što u Bosni ima ćevapa (hehe, teško) ali probat ćemo, tako da definitivno ponovite pointere/pokazivače, kako god ih zovete.

Swap chain i Page Swapping

Dakle, sada ćemo raspraviti nešto od iznimne važnosti za bilo koju 2D/3D aplikaciju. Nadam se da će vam dati bolju sliku o načinu funkcioniranja runtime grafike. Grafički adapter u svojoj memoriji sadržava pointer prema spremniku pixela koji sadržavaju sliku koja se prikazuje na zaslonu. Svaki put kada želite prikazati neki 3D model, scenu, 2D lik... U biti, štogod vam padne napamet, treba se nadopuniti array koji sadrže potrebne podatke i dodati nove te ponovno iscrtati na monitor. Ovo npr. ne bi mogli napraviti sa GetMessage() funkcijom u glavnom loopu. Uskoro ćete saznati zašto. Nadovezujući se na prijašnju temu, dolazimo do prvog problema. Refresh rate... Zapravo, naši monitori ne refreshiraju se dovoljno brzo sa istinski realtime rendering, većina se kreće od 60 do 100 Hz (moj je 60Hz). Kada bi usred refreshiranja ubacili novi model u grafički adapter, slika bi prikazala prerezane dijelove novog rendera i starog rendera (refresha se odozgo prema dolje). Taj efekt se maštovito naziva deranjem (tearing in eng). DirectX je "prokužio" ovaj problem i uveo swapping.

Umjesto da izravno renderira novu sliku na monitor, Direct3D iscrtava sljedeću sliku u pomoćni spremnik pixela nazvan back buffer. Dakle, ono što je trenutno iscrtano je front buffer. Sve slike se iscrtavaju u back buffer i onda tek ih Direct3D ažurira na zaslon vašeg monitora, a nakon što je gotovo, jednostavno ju odbaci oslobađajući mjesto za sljedeće slike. Naravno problem još nije otišao, DirectX koristi pointer za svaki spremnik (buffer) i jednostavno mijenja njihove vrijednosti. Trik je mali, ali pali. Aplikacija se dodatno poboljšava dodavanjem dodatnih back buffera koji koriste vrijeme između iscrtavanja da bi se što bolje iskoristili resursi, a samim time i performanse ojačaju.

IDEMO!!!

Ubi' ja vas sa teorijom hehe, evo napokon, ali stvarno napokon krećemo sa DirectXom. Nadam se da ste instalirali DirectX i dodali putove u compiler, a ako niste ima hrpa odličnih tutorijala na netu da to napravite. U biti, nastavljamo na starom projektu, ne morate se ništa brinuti, samo moramo dodati par stvari na njega.

Code:
// osnovni header fileove koji su nam potrebni

#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

// uključi Direct3D biblioteku u projekt
#pragma comment (lib, "d3d9.lib")

// globalne deklaracije
LPDIRECT3D9 d3d;   // pointer prema našem D3D sučelju
LPDIRECT3DDEVICE9 d3ddev;   // pointer prema klasi uređaja

//prototip funkcija vezanih uz Direct3D
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);      //renderira jedan frejm/sliku/render
void cleanD3D(void);           // Čisti nered koji smo napravili

//prototip WindowProc funkcije (sjećate se, ona koja rukuje sa porukama)
LRESULT CALLBACK WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);



#include <d3d9.h>

Razmišljao sam da li da ovo uopće boldam i opisujem. Ovo je header file koji uključujemo u projekt da bi imali pristup Direct3D funkcijama, različitim klasama i structovima.

#pragma comment(lib, "d3d9.lib")

Ovo uključuje biblioteku D3Da u naš projekt. #pragma comment postavlja određeni komad informacije u naš object file (od projekta). Prvi parametar (lib) govori compileru da želimo dodati biblioteku u projekt, a poslije, logično, navodimo njeno ime i eventualno lokaciju ako to nismo napravili jednostavnijim putem.

LPDIRECT3D9 d3d;

Ovo je dugački pokazivač ilitiga Long Pointer (naglašavam LP) prema Direct3Du. Ovo znači da ćemo napraviti klasu zvanu iDirect3D9. Kada COM (Component Object Model) napravi ovu klasu, mi ćemo ju zapravo ignorirati i koristit ju samo indirektno preko našeg dugačkog pointera.

LPDIRECT3DDEVICE9 d3ddev;

Uf, pogle ovoga... Direct3D uređaj sadrži sve informacije koje se tiču grafičkih drivera, grafičke kartice i svega ostalog što ima svoje prste u hardverskoj strani grafike. Ovo je također dugački pointer prema klasi koja sprema sve ove podatke.

(Nadam se da shvaćate da ova imena nisu statična i da ih možete nazvati kako god vam padne napamet. D3D device možete umjesto d3ddev nazvati pade_ovca_sa_grane ako se osjećate vrlo kreativno.)

-----------------------------------------------------------------------------

U ovom segmentu ćemo obraditi funkcije čije smo prototipe definirali maloprije. Da bismo dobili naš današnji cilj što je zapravo čistka ekrana u totalno crnu boju. Joj, hvala meni, ja sam tako koristan... Oke, dosta egotripa. Na posao. Evo i prve funkcije, inicijalizacija D3Da.

initD3D()


Code:
// ova funkcija priprema Direct3D za korištenje
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);    // create the Direct3D interface

    D3DPRESENT_PARAMETERS d3dpp;    // stvaramo strukturu koja će sadržavate neke parametre, kul, zar ne?

    ZeroMemory(&d3dpp, sizeof(d3dpp));    // počisti istu za korištenje
    d3dpp.Windowed = TRUE;    // program je zasad prozor
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // odbaci stare slike
    d3dpp.hDeviceWindow = hWnd;    // odredi koji će prozor D3D koristiti

    // sada stvori uređaj na bazi svih podataka gore.
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    return;
}


Zamislite da mi sada nestane struje u kući... brrrr :o


d3d = Direct3DCreate9(D3D_SDK_VERSION);

Čestitam, ovo je vaša prva Direct3D funkcija koju ste potegnuli. Njena primarna svha jest da stvori Direct3D sučelje. Povratna vrijednost vraća adresu stvorenog uređaja pa ćemo ovo spremiti u pointer koji smo prije napravili, baš za ovo. Slatko kako se sve poklapa zar ne? A ovaj parametar, kako se on uklapa u pričicu? Pa on je uvijek isti i prilično nam pojednostavljuje cijelu stvar, u biti prepustite D3Du da odredi koja mu je verzija.

D3DPRESENT_PARAMETERS d3dpp;

Ovo je prilično bitno pri stvaranju d3d uređaja. Ima ih hrpa, a mi smo početne fino popunili. Zasad, D3DPRESENT_PARAMETERS je struktura čiji članovi sadržavaju vitalne informacije kojima ćemo nahraniti grafički d3d uređaj. Prvo što smo uradili, a to i od prije znate, koristili smo ZeroMemory funkciju da inicijaliziramo strukturu na početne vrijednosti. Kroz komentare sam pojasnio čemu služi određeni član, a vjerujem da se vidi iz aviona. Ako netko ne kuži, neka napiše dolje pa ću dodatno pojasniti.

d3d->CreateDevice()

Malo poveća funkcija, hehe. No, kao što sam i milju puta do sada rekao, sve je logično i jednostavno, samo ne pokazujte strah. Možda ste primijetili da je ova funkcija član d3d klase do koje dolazimo indirektno preko pointera (->). Pošto je ovo prilično bitna stvar za današnju lekciju, vrijeme je da ju disociramo. Evo ga prototip, behold:

Code:
HRESULT CreateDevice(
    UINT Adapter,
    D3DDEVTYPE DeviceType,
    HWND hFocusWindow,
    DWORD BehaviorFlags,
    D3DPRESENT_PARAMETERS *pPresentationParameters,
    IDirect3DDevice9 **ppReturnedDeviceInterface
);


Boo! Pa i nije toliko strašno, zar ne? Krenimo od početka.

UINT Adapter,

Ovo je nepotpisani (unsigned) integer koji pohranjuje uvijek pozitivnu vrijednost koja indicira koji grafički adapter, ili grafičku karticu, Direct3D treba koristiti. Pošto se trudim da održim sve što kraćim i jednostavnijim definicijama objašnjeno, neću ulaziti u detalje. Pošto je u 95% slučajeva (99% statistika je izmišljeno na mjestu btw.) samo jedan takav, pustit ćemo D3D da odluči. Dakle, stavljamo vrijednost D3DADAPTER_DEFAULT.

D3DDEVTYPE DeviceType,

Ovdje postoje 4 moguće vrijednosti koje možemo ubaciti, mi ćemo se koncentirati na jednu, nama najbitniju, a to je D3DEVTYPE_HAL.

D3DDEVTYPE_HAL govori D3Du da koristi ono što nazivamo hardverskim apstraktnim slojeva ( Hardware Abstraction Layer iliti HAL), njega koristimo da bi indicirali da D3D treba koristiti hardver da bi procesirao grafiku. Ako bi u kojem slučaj hardverski dio zakazao, softver bi uskočio... Što neće, ne brinite... Ako se dogodi, onda se zabrinite :rotfl:

HWND hFocusWindow,

Ovo je ručica našega prozora, ubacite samo hWnd i to je to.

DWORD BehaviorFlags,

Ovdje ima hrpa vrijednosti koje možemo ubaciti u ovaj parametar, ali samo ćemo ih par pokriti sad. D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DCREATE_MIXED_VERTEXPROCESSING i D3DCREATE_HARDWARE_VERTEXPROCESSING. Ove tri vrijednosti se skoro same objašnjavaju. Softverska indicira da sve 3D kalkulacije će se odvijati sa softwarem, hardverska na hardwareu, a kombinacija sa kombinacijom. Hehe, jednostavno. Mi ćemo naravno koristiti prvu navedenu jer softverska osigurava kompatibilnost s vašim kantama. I mojom naravno.

IDirect3DDevice9 **ppReturnedDeviceInterface

Ovo predstavlja pointer prema sučelju grafičkog uređaj. Otprije je definiran u našem "malom" programu kao d3ddev, pa ćemo staviti &d3ddev u ovaj parametar. Ampersand(&) operator znate čemu služi, nadam se. Uf, fala bogu, sector alpha je gotov ^^

render_frame()

Sada ćemo se baviti render_frame() funkcijom. Ona je više nego jednostavna i prilično kratka. Želimo renderirati jedan jedini frame, čudno? Ne, jer se ova funkcija stalno iterira kroz glavni loop programa pa tako 1 frame, pa 2 framea, 30 frameova. Get it? Pa naravno da kužite, hehe.

Ajmo sada malo pogledati izbliza tu famoznu funkciju.
Code:

// ovu funkciju koristimo da renderiramo jedan frame
void render_frame(void)
{
    // pozadinu oboji u crno.
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // započinje 3D scenu

    // ovdje dodajete stvari da se renderiraju u back bufferu.

    d3ddev->EndScene();    // završava 3D scenu

    d3ddev->Present(NULL, NULL, NULL, NULL);    // prikazuje renderiranu "scenu"

    return;
}


d3ddev->Clear()

Ova funkcija "čisti" spremnik na određenu boju. U našem slučaju, to je 0, 0, 0 (dakle, crna). Zbog činjenice da je većina parametara u ovom trenutku nebitna, jednostavno ćemo ih ostaviti na miru. Prva dva su postavljena na 0 i NULL kako se vidi u kôdu, tako indiciramo da se cijeli back buffer očisti. Treći parametar postavljen na D3DCLEAR_TARGET, indicira da se back buffer treba očistiti. Ima i drugih tipova buffera pa moramo biti specifični. Sljedeće na red dolazi boja gdje koristimo funkciju D3DCOLOR_XRGB() da bi ju dobili. Sljedeća dva parametra su zasad nebitna, stavite ih na 1.0f i 0.

d3ddev->BeginScene()

Ovdje pozivamo funkciju BeginScene() koja kaže Direct3Du (D3Du) da smo spremni renderirati. Ova funkcija mora biti pozvana zbog dva razloga. Prvo, treba reći D3Du da kontroliramo memoriju. Kao drugi razlog, ova funkcija radi nešto na hardverskom nivou što nazivamo zaključavanje (locking). Spremnik se na VRAMu doslovce zaključava te eksluzivno vama dopušta korištenje memorije.

d3ddev->EndScene()

Vjerojatno predpostavljate da ova funkcija djeluje suprotno od BeginScene() funkcije. Otključava pristup memoriji, čime dozvoljava drugim procesima da ju koriste.

d3ddev->Present()

Zadnji, ali i najbitniji bez čega ovo gore ne bi imalo nikakvog smisla. Ime ga samo objašnjava, a parametri su zasad totalni nebitni, dakle samo pobacajte NULL svuda (x4 ^^).

cleanD3D()

Uf, evo je... Zadnja funkcija ovog napornog ali vrijednog tutorijala. cleanD3D() čisti sav nered i oslobađa Direct3D i memoriju koju je gutao dosad.

Code:
// ova funkcija čisti nered za nama
void cleanD3D(void)
{
    d3ddev->Release();    // oslobodi D3D grafički uređaj
    d3d->Release();    // oslobodi Direct3D
    return;
}


Mislim da ovdje ne treba ništa posebno pojašnjavati, koristimo Release() funkciju. Bez ove funkcije, Direct3D bi nastavio raditi na vašoj pozadini i jednostavno bi gušio vaše dragocjene resurse. Gadno, a? Tako da ni slučajno ne promašite ovu funkciju. A sada, evo sav naš posao sa svim komentarima zasad, pa proučavajte i testirajte:

Code:
// osnovni header fileove koji su nam potrebni

#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

// uključi Direct3D biblioteku u projekt
#pragma comment (lib, "d3d9.lib")

// globalne deklaracije
LPDIRECT3D9 d3d;   // pointer prema našem D3D sučelju
LPDIRECT3DDEVICE9 d3ddev;   // pointer prema klasi uređaja

//prototip funkcija vezanih uz Direct3D
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);      //renderira jedan frejm/sliku/render
void cleanD3D(void);           // Čisti nered koji smo napravili

//prototip WindowProc funkcije (sjećate se, ona koja rukuje sa porukama)

LRESULT CALLBACK WindowProc(HWND hWnd,
                         UINT message,
                         WPARAM wParam,
                         LPARAM lParam);

// ulazna točka u svaku windows aplikaciju

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    // "ručica za prozor", ispuni se funkcijom
    HWND hWnd;
    // struktura prozora
    WNDCLASSEX wc;

    // očisti strukturu prozora
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // ajmo popuniti članove ove strukture
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass1";

    // registriraj klasu koristeći informacije date wc-u
    RegisterClassEx(&wc);

    // napravi prozor i koristi rezultat kao ručicu (hWnd)
    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",    // Ime windows klase
                          L"PCPlay Ucionica",   // Naziv prozora
                          WS_OVERLAPPEDWINDOW,    // stil prozora
                          300,    // x-pozicija prozora
                          300,    // y-pozicija prozora
                          500,    // širina prozora
                          400,    // visina prozora
                          NULL,    // nema roditeljskog prozora
                          NULL,    // ne koristimo izbornik
                          hInstance,    // ručica aplikacije (remember)
                          NULL);    // ne koristi se sa više prozora

    // napokon ju prikaži:
    ShowWindow(hWnd, nCmdShow);
    // inicijalizacija D3Da
    initD3D(hWnd);

    // ulaz u glavni loop

    // ova struktura sadrži predivne poruke koje nam Windows šalje
    MSG msg;

    // primijetite kako ovo zapravo stvara infinite loop
    while(TRUE)
    {
        // Provjeri za porukice u event queueu... Kako glupa riječ ^^
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
            // Ako je poruka jednaka WM_QUIT, break out!
            if (msg.message == WM_QUIT)
            break;

            // Standarno procesiranje kroz WindowProc funkciju 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        //pozivamo funkciju da renderira unutar loopa
        render_frame();

    }
    //kada izađe iz loopa, zatvori Direct3D uređaj i direct3D
    cleanD3D();
    // vrati ovaj dio WM_QUITa Windowsu
    return msg.wParam;
}

// ova funkcija rukuje sa svim porukama koje naša aplikacija prima
// dakle, poštar :)

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // traži određenu akciju koja treba biti provedena pri dobivanju poruke
    switch(message)
    {
        // ovo se pročita kada korisnik pritisne crveni X
        case WM_DESTROY:
            {
                // zatvori aplikaciju
                PostQuitMessage(0);
                return 0;
            } break;
    }

    // rukuj sa svakom porukom koju WindowProc nije procesirao osobno
    return DefWindowProc (hWnd, message, wParam, lParam);
}

// ova funkcija priprema Direct3D za korištenje
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);    // create the Direct3D interface

    D3DPRESENT_PARAMETERS d3dpp;    // stvaramo strukturu koja će sadržavate neke parametre, kul, zar ne?

    ZeroMemory(&d3dpp, sizeof(d3dpp));    // počisti istu za korištenje
    d3dpp.Windowed = TRUE;    // program je zasad prozor
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // odbaci stare slike
    d3dpp.hDeviceWindow = hWnd;    // odredi koji će prozor D3D koristiti

    // sada stvori uređaj na bazi svih podataka gore.
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    return;
}

// ovu funkciju koristimo da renderiramo jedan frame
void render_frame(void)
{
    // pozadinu oboji u crno.
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // započinje 3D scenu

    // ovdje dodajete stvari da se renderiraju u back bufferu.

    d3ddev->EndScene();    // završava 3D scenu

    d3ddev->Present(NULL, NULL, NULL, NULL);    // prikazuje renderiranu "scenu"

    return;
}

// ova funkcija čisti nered za nama
void cleanD3D(void)
{
    d3ddev->Release();    // oslobodi D3D grafički uređaj
    d3d->Release();    // oslobodi Direct3D
    return;
}

Ovo bi trebali dobiti kada kompajlirate program:

http://i5.tinypic.com/6u9s10p.jpg

Za zadaću, hoću ovdje da vidim vaš prozor u drugoj boji... Sutra
ćemo napokon krenuti na famozni 2D trokut. I da, čestitam, upravo ste postali istinski grafički programeri. John Carmack nije vam ni do bokserica.

PEACE!


Htio bih još nadodati da je ovo tek početni dio iz serije koju pišem o DirectX tutorialima, pa kako ih budem postao na naš podforum na PCPlayu, nastojat ću ga i vama ustupiti!
[ glorius @ 06.01.2008. 12:52 ] @
Nice move :) Nadam se da ce se javiti ljudi koje zanima da znaju vise o DirectX-u posto je scena stvarno zamrla...


[ Marijana007 @ 24.07.2011. 16:42 ] @
Ovo je odlicna stvar, ali prvo bih htela da znam neke osnovne stvari! Sta da skinem ( link) kako da pokrenem i da pocnem sa kucanjem programa!?