You are on page 1of 34

DirectX 9.

0 Programiranje u C++ programskom jeziku


Rastko Ili

Da li ste se ikada zapitali kako se prave video igrice? Odgovor je kao i uvek programiranjem. Ipak na polju video igrica pored programiranja postoji mnogo razliitih poslova koje treba uraditi kako bi ste dobili konaan rezultat koji ste osmislili. Tu su timovi modelara koji se bave iskljuivo 3D modelima vaih objekata, pa timovi ljudi koji su zadueni za nanoenje tekstura i materijala na te objekte, dizajneri koji se bave okruenjem i svetom u kome je smetena vaa video igra. Ipak na kraju je na programerima da udahnu ivot karakterima, objektima, prirodi i celom svetu video igre. To se postie uz pomo takozvanog "3D engine"-a u kome su sva prikazivanja i sve interakcije definisane. Tu na scenu stupa DirectX kao programski jezik uz koji sve ovo moete napraviti i definisati. Ovaj programski jezik nije jednostavan i veoma je kompleksan, to je zato to je osmiljen tako da kroz njega dobijete najveu moguu mo i da kada jednom ovladate njime jedina prepreka na koju moete naii je vaa matovitost. U ovom tekstu baviemo se naj osnovnijim strukturama DirectX-a i donekle vas uvesti u priu grafikog programiranja, to emo uiniti kroz C++ zato to sam DirectX i jeste i nije programski jezik za sebe, ve veliki broj poziva i predefinisanih funkcija koje koristite u nekom od ve poznatih programskih jezika kao to je VB, C++ ili C#. Pa da ponemo.

ta vam je sve potrebno?

- Microsoft Visual Studio (ili Dev C++) Microsoft Visual C++ je put koji smo mi izabrali. DirectX 9 podrava samo MS Visual C++ 7 verziju, i naravno verzije novije od nje, Visual C++ 6 je naputen. - DirectX SDK Software Development Kit za DirectX jeste veliki ali naalost za korisnike sa slabijim internet vezama i neophodan. Moete ga preuzeti na http://msdn.microsoft.com/directx/sdk/ .

Podeavanje Nakon instalacije i Microsoft Visual Studio (VS) programa i Direct X SDK, podeavanja su sledea. U osnovnom prozoru VS kliknite na Options i daljim klikom na direktorijum Projects dobiete sledeu listu:

U VC++ Directories podesite putanju na va SDK kao to je na slici prikazana (prva stavka), nemojte jo uvek kliknuti OK jer imamo jo par stvari da podesimo unutar ovog prozora.

U padajuoj listi u kojoj trenutno pie Executable files pronaite stavku Include files.

Kao i malopre odredite putanju na va include direktorijum na raunaru gde vam se nalazi SDK.(stavka jedan na slici) Ni ovde ne pritiskajte jo uvek OK jer ostala je jo jedna stvar koju treba uraditi. U padajuoj listi gde ste malopre odabrali Include files sada odaberite Library files.

I ovde kao i u prethodnim sluajevima odredite putanju vaeg lib direktorijuma unutar SDK na vaem raunaru. Mala napomena, ukoliko ste sa interneta preuzeli poslednju verziju DirectX SDK a to je u ovom momentu Decembar 2005, primetiete da se unutar njegovih direktorijuma nalaze esto dve opcije koje su X86 i X64. X64 se odnosi na raunare nove generacije koji su 64-bitni tako da pazite da odabere u svojim podeavanjima u prethodnim koracima ba X86 direktorijume. Sada kada smo sve podesili spremni smo da preemo na sam DirectX i ponemo sa poetnim objektima, strukutrama i funkcijama unutar njega. Prozori (Window klase) Ne preterano iznenaujue, osnovni element Windows aplikacija su upravo prozori. U tipinoj Windows aplikaciji, svaki vidljivi objekat je prozor. Bilo da je u pitanju dugme ili polje za unos, svi objekti su prozori. Tipina Direct3D aplikacija radi ipak malo drugaije. Veina aplikacija preuzme monitor i prozor primarno koristi kao medijum preko kojeg dobija poruke od operativnog sistema. Iako ove aplikacije intereaguju sa njihovim prozorima i sa standardnim WIN32 API pozivima mnogo manje, i dalje je njihova uloga veoma bitna a primena neophodna. Postoje tri glavna koncepta koje bi trebalo da nauite pri podeavanju i upravljanju sa prozorima unutar Direct3D aplikacije: window klase prozori upravljake jedinice nad porukama

Window klase Window klasa nije tipina C++ klasa, iako postoje slinosti. Pre svega ova klasa je ablon koji definie odreene atribute. Svaki prozor stvoren iz ove klase nasleuje te atribute. Evo uproene verzije deklaracije strukture window klase:

struct WNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; }; Poto je cilj ovog teksta da vas naui i uputi u Direct3D neemo zalaziti do besvesti u same detalje ove strukture, ve emo ukratko opisati najbitnije lanove, a onda vam ponuditi kodove koji e ilustrovati inicijalizaciju strukture, kreiranje window klase i konano njeno unitavanje. style - Kontrolie razliita ponaanja prozora. lpfnWndProc - Ovo je pokaziva na upravljaku jedinicu poruka u window klasi. cbClsExtra i cbWndExtra - Ovim obezbeujete dodatni memorijski prostor kao sastavni deo klase i prozora. Retko se koristi. hInstance - Ovo je upravlja vaom aplikcijom. Do njega se moe doi pozivanjem GetModuleHandle() funkcije sa parametrom NULL. Instanca se prosleuje takoe WinMain klasi pa vam je stoga i tu dostupna. hIcon, hCursor, hbrBackground - Ovo postavlja predefinisane postavke pri renderovanju ikona, kursora i pozadine aplikacije. lpszMenuName - Koristi se pri kreiranju menija.

lpszClassName - Ovaj parametar daje klasi javno ime po kojoj se prepoznaje. Prozor moe biti napravljen iz ovog ablona jednostavnim postavljanjem imena. Nakon to je window klasa popunjena, registrujete je putem RegisterClass funkciju. Iz primera koda (kompletni kodovi su priloeni uz ovaj tekst ), evo iseka koji ilustruje inicijalizaciju window klase i njene strukutre, registraciju i konano odjavljivanje. WNDCLASS window_class; HINSTANCE instance=GetModuleHandle(NULL); window_class.style = CS_OWNDC; window_class.cbClsExtra = 0; window_class.cbWndExtra = 0; window_class.hInstance = instance; window_class.hIcon = LoadIcon(NULL,IDI_APPLICATION); window_class.hCursor = LoadCursor(NULL,IDC_ARROW); window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); window_class.lpszMenuName = NULL; window_class.lpszClassName = "DH Class"; window_class.lpfnWndProc = p_wndproc; //Spoljna funkcija koja se bavi porukama prozora //Registracija klase sa prozorom if(!RegisterClass(&window_class)){ //Upravlja grekama ide ovde } // Ostatak aplikacije ide ovde //Konano kada smo zavrili sa klasom pozivamo UnregisterClass: UnregisterClass("DH Class",instance); Napomena: Svi prozori kreirani od strane klasa, moraju se zatvoriti pre nego to pozovete UnregisterClass za tu klasu.

Kreiranje prozora Sada kada smo napravili window klasu moemo kreirati i sam prozor. Evo primera: HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, y, int nWidth, nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); Sada emo kao i malopre proi kroz parametre ukratko, pa emo pogledati primer koda koji ih ilustruje. lpClassName - Ovo je ime klase koju ste kreirali u prethodnom koraku. lpWindowName - Ime prikljueno za va prozor, ukoliko imate naslov u svom prozoru ovo ime e se tu prikazati. dwStyle - Stilovi prozora odreuju da li prozor ima prostor za naslov, kakve su mu ivice i jo mnoge druge stvari. U aplikaciji koja se pokree preko celog ekrana, WS_POPUP|WS_VISIBLE su esto koriene jer mogu da naprave prozore bez ikakvih vidljivih ivica . x and y - Postavljanje Pozicije gledano iz gornjeg levog ugla. Pozicija (0,0) je stoga skroz u gornjem levom uglu. nWidth and nHeight - irina i Visina prozora u pikselima. hWndParent - Identifikuje prozor koji elite da postane roditelj novo nastalim prozorima. hMenu - Pokaziva i upravlja menijem. hInstance - Ovo je upravlja vaom aplikcijom. Do njega se moe doi pozivanjem GetModuleHandle() funkcije sa parametrom NULL. Instanca se prosleuje takoe WinMain klasi pa vam je stoga i tu dostupna.

lpParam - udni Windows-ov vudu, ostavite ga na NULL ukoliko niste sigurni ta radite. Deluje kao puno stvari koje treba nauiti samo da biste napravili prozor, ali injenica je da ete stalno korisiti jedan isti kod kad god vam zatreba sa malim izmenama. Treba napomenuti da ukoliko e vaa aplikacija preuzeti itav ekran, nije bitno koliko e veliki prozori biti jer ete u svakom sluaju zauzeti itav ekran. Ukoliko korisite prozorski mod, vai prozori mogu biti bilo koje veliine koju vi odredite, jedina stvar na koju treba da obratite panju jeste da vai prozori dolaze sa naslovom i ivicama, tako da ukoliko ste zatraili prozor rezolucije 640 x 480 zbog ivica moete dobiti samo 630 piksela radne povrine. Evo i primera: if(is_app_fullscreen){ //Upit sistemo da vrati veliinu desktop-a window_width=GetSystemMetrics(SM_CXSCREEN); window_height=GetSystemMetrics(SM_CYSCREEN); style=WS_POPUP|WS_VISIBLE; }else{ //U prozorskom modu, jednostavno samo pravimo prozor veliine koju mi elimo window_width=desired_width; window_height=desired_height; style=WS_OVERLAPPED|WS_SYSMENU|WS_VISIBLE; } p_window=CreateWindow("DH Class", ime nae klase window_name, //Ime prozora/naslov style, //Stilovi 0, //X pozicija 0, //Y pozicija window_width, //irina prozora window_height,//visina prozora NULL, //Roditelj prozora NULL, //Meni instance, //upravlja aplikacijom NULL); //pokaziva na prozor if(!p_window){ //upravlanje grekama } if(is_app_fullscreen){ ShowCursor(FALSE); }

if(!p_fullscreen){ GetClientRect(*p_window,&client_rect); MoveWindow(*p_window, 0, //leva ivica 0, //gornja ivica window_width+(window_width-client_rect.right), //Nova irina window_height+(window_height-client_rect.bottom), //Nova visina TRUE); } Unitavanje vaeg prozora Ovaj deo je dobio zasebno mesto u tekstu jer postoje dva naina da se rei, a mi emo ih pokriti oba. U standardnom Windows programiranju vi ne unitavate direktno prozor, ve mu aljete poruku da se samouniti. Ovo se esto primenjuje unutar upravljake jedinice nad porukama, koja je sama po sebi samo pozivajua funkcija. Tako da funkcija koja se indirektno poziva od strane vae aplikacije alje poruku prozoru da se uniti. Veina programera koja nije koristila Windows pre smatra da je ovo veoma udan pristup. Ukoliko ste alocirali resurse trebali biste i da ih dealocirate. Ukoliko pogledate istorijat iza WIN32 API poziva videete da ovo ima smisla. Aplikacija miruje i eka da dobije poruku i nakon to je dobije, opet se vraa u stanje mirovanja do sledee. Dok programiranje bazirano na dogaajima (eng. eventdriven) ima puno smisla u biznis aplikacijama ipak nije prikladna za veinu igara, uzimajui u obzir da igre konstanto auriraju i renderuju to bre mogu, i kao takve koriste se minimalno Windows modelom. esto je zgodno da se aplikacija pokrenuta preko celog ekrana ponaa kao da je zauzela itavu mainu. Ovo naravno nije istina, ali sa malo truda mogue je odrati iluziju. Jedan od omiljenih naina da se prozor uniti je njegovo eksplicitno unitavanje. Kreira se lepa kontinualnost vae aplikacije koja ujedno i vizuleno odgovara samom kodu. Kreirajte prozor, koristite prozor i onda ga unitite. Lepo i jednostavno. Iskreno obe metode su esencijalno iste. Jedna je manje direktna nego druga ali na kraju krajeva obe daju isti rezultat. Ovo je vie pitanje samog programera i njegovog stila. Neemo pokazati standardni metod ve emo se skoncentrisati na ovaj drugi:

Zbog naina na koji Windows radi, va prozor ne mora da se uniti odmah, zbog zakasnelih poruka koje se jo obrauju u redu. Kako bismo bili dodatno sigurni, proveravamo da je red prazan. Evo i koda: MSG msg; DestroyWindow(my_window); //Oisti svaku obraivanu poruku unutar reda while(PeekMessage(&msg, NULL, 0, 0,PM_REMOVE)){ TranslateMessage(&msg); DispatchMessage(&msg); } Procesiranje poruka prozora U ovom momentu znate da kada kreirate prozor morate proslediti pokaziva na funkciju obrade poruka. Kada god doe do nekog dogaaja poruka je poslata prozoru (korisnik pritiska dugme, sat je odbrojao...). Ova poruka se prosleuje upravljakoj funkciji za poruke koja proverava da li je poruka njemu poznata i da li zna ta sa njom dalje treba uraditi. Ukoliko mu poruka nije poznata dalje je prosleuje predefinisanoj proceduri DefWindowProc kako bi dao Windows-u do znanja da je poruka vratila kao odgovor nulu. Evo jedne jednostavne upravljake funkcije nad porukama: LRESULT CALLBACK default_window_proc(HWND p_hwnd,UINT p_msg,WPARAM p_wparam,LPARAM p_lparam){ switch(p_msg){ case WM_KEYDOWN: // Taster je pritisnut, ukini aplikaciju case WM_CLOSE: //Pritisnuto Close Window dugme, ukini aplikaciju case WM_LBUTTONDOWN: //pritisnuto levo dugme mia,ukini aplikaciju g_app_done=true; return 0; } return (DefWindowProc(p_hwnd,p_msg,p_wparam,p_lparam)); }

p_hwnd- je upravlja prozorima.Ukoliko vaa aplikacija ima vie prozora koji koriste istu window klasu uz pomo ovoga ete odrediti koji od prozora je dobio poslatu poruku. p_msg- Tip poruke. Tipovi poruka u prethodnom kodu su WM_KEYDOWN, WM_CLOSE, i WM_LBUTTONDOWN. p_wparam i p_lparam - Specifine poruke. U sluaju WM_KEYDOWN, i WPARAM uvaju podatke o tasteru koji je pritisnu dok LPARAM uva podatke o tome koliko je puta pritsnuto i jo mnoge druge informacije. U naem primeru postavili smo da se aplikacija ugasi kad god korisnik pritisne taster, klikne levim dugmeto mia ili klikne ba na dugme koje inae zatvara prozor. Sada smo proli kroz itav proces window klase i naina na koji moemo da napravimo prozor, koristimo ga i na kraju unitimo. Ovo je najosnovniji objekat unutar DirectX-a a moe se rei i samog Windows-a. U sledeem broju pozabaviemo se samom inicijalizacijom Direct3D-a.

DirectX 9.0 Programiranje u C++ programskom jeziku (2.deo)


Rastko Ili

UVOD U ovom delu nauiete da napravite svoju prvi Direct3D objekat i ureaj koji su nam neophodni za rad same aplikacije. Ta aplikacija nee biti grandiozni i kompleksni projekat ali e vas nauiti kako se pravi prozor i kako se inicijalizuje Direct3D. Takoe emo prikazati kako se uklanja sve sa prozora i kako se neke stavke prezentuju korisnicima. Samo renderovanja e biti uproeno ali ne brinite u nekom od sledeih nastavaka proiemo i kroz renderovanje detaljnije.

Kreiranje Direct3D-a Prvi korak je kreiranje IDirect3D9 objekta i to je veoma jednostavno. Pogledajte kod koji kreira objekat i nakon toga ga oslobaa: IDirect3D9 *g_D3D=NULL; g_D3D = Direct3DCreate9( D3D_SDK_VERSION); if(!g_D3D){ //Upravlja grekama } //Na samom kraju aplikacije if(g_D3D){ g_D3D->Release(); g_D3D=NULL; } Prvo, deklarisaemo pokaziva na IDirect3D9 objekat. Ovaj pokaziva pokazae se nepotrebnim u daljem radu jer ete na mnogim mestima videti deklaracije kao to je na primer LPDIRECT3D9. LPDIRECT3D9 je tipa typedef, to je samo po sebi pokaziva na IDIRECT3D9 objekat. Svi DirectX objekti imaju svoj typedef koji se prepoznaju po velikim slovima LP na poetku svog imena. Mi emo korisiti prvi nain kako bi se jasno videlo ta radimo i koji objekti se referiu na neke bitnije DirectX objekte. Direct3DCreate9 ima samo jedan parametar. U gotovo svakom primeru videete da je taj parametar D3D_SDK_VERSION. Ovo kreira Direct3D objekat koji je kompatibilan sa SDK-om koji koristite u datom momentu. Ako zbog nekog razloga morate da radite sa starijim Direct3D9 verzijama, preko ve spomenutog parametra moete proslediti da je upravo ta verzija ona sa kojom elite da radite. Nadamo se da ovo neete morati esto da radite u budunosti, ipak u svakom sluaju sada ostavite 3D_SDK_VERSION. Nakon to ste zavrili sa radom nad IDirect3D9 objektom, pozivate njegovu metodu za otputanje (Release). Najkrae objanjenje ove metode i jeste da ona govori vaoj aplikaciji da otpusti objekat. DirectX koristi COM (Component Object Model) i svi njegovi objekti nasleuju iz IUnknown klase. Kada je IUnknown objekat kreiran inkrementuje se broj interne reference i nakon to se Release metoda pozove, interna referenca kree da se dekrementuje. Kada referenca dostigne 0 objekat je puten.

Referenca objekta se moe inkrementovati direktno pozivanjem njegove AddRef metode. Napredni korisnici e eleti ovo da iskoriste pri organizaciji svojih objekata kada su oni deljeni na vie razliitih mesta. Broj referenci se najee poziva indirektno. Ukoliko renderujete mesh, Direct3D e inkrementovati svoju referncu i dekrementovati je nakon to je zavrio. Na ovaj nain iako ste uklonili svoj objekat za koji ste mislili da se ne koristi, Direct3D e i dalje renderovati mesh. Detaljno razumevanje COM-a nije neophodno da biste mogli da koristite DirectX, ali i te kako pomae pri razumevanju osnovnih koncepta DirectX-a. Kada pozovemo Release, prvo proveravamo da li je pokaziva NULL. Direferenciranje NULL pokazivaa e napraviti exception koji e ukinuti aplikaciju. Ukoliko pokaziva nije NULL, pozivamo Release i tada postavljamo pokaziva na NULL kako bismo ga spreili od pokuaja da se oslobodi vie puta. Microsoft definisani MACRO, SAFE_RELEASE, ponaa se identino kao i na Release. SAFE_RELEASE je definisan u dxutil.h biblioteci koja se nalazi u Common folderu u vaem SDK. Kreiranje Direct3D Ureaja Direct3D ureaj (IDirect3DDevice9), mi emo kako ne bismo komplikovali tekst ovo nazivati jednostavno ureaj, se koristi za koumnikaciju sa video karticom. U pitanju je 1 na 1 veza izmeu karitce i ureaja i ukoliko elite da renderujete na vie karica neophodno je napraviti ureaj za svaku od njih. Za razliku od IDirect3D-a stvaranje novog ureaja je malo komplikovanije. Kako je ovaj tekst u poetnikom duhu, kroz naprednije delove o ureaju priaemo u nekom od narednih tekstova. Postoji pomalo uasavajua koliina podataka koja se moe podesiti kada se kreira novi ureaj. Kako bismo izbegli postavljanja oko 80 razliitih parametara (veina njih je opcionalna) u jednoj funkciji, popuniemo strukturu u kojoj emo tano rei ta mi elimo od naeg ureaja. Ova struktura se zove D3DPRESENT_PARAMETERS, moda ete se zapitati da li je neko u Microsoft-u polomio Caps Lock taster kada je smiljao DirectX, ipak evo strukture:

struct D3DPRESENT_PARAMETERS{ UINT BackBufferWidth; UINT BackBufferHeight; D3DFORMAT BackBufferFormat; UINT BackBufferCount; D3DMULTISAMPLE_TYPE MultiSampleType; DWORD MultiSampleQuality; D3DSWAPEFFECT SwapEffect; HWND hDeviceWindow; BOOL Windowed; BOOL EnableAutoDepthStencil; D3DFORMAT AutoDepthStencilFormat; DWORD Flags; UINT UINT }; FullScreen_RefreshRateInHz; PresentationInterval;

A evo i kratkog objanjenja svakog od parametara: BackBufferWidth i BackBufferHeight irina i visina back buffer-a. U aplikaciji koja se pokree preko celog ekrana ovi parametri moraju odgovarati i samoj rezoluciji ekrana. U ovo tekstu koristiemo ve predefinisani model sa 640x480 px. BackBufferFormat Slui za opisivanje dubine back buffer-a (tipino 16 i 32bit-a). U DirectX9 verziji 24bit-ni pozadinski baferi nisu podrani. Format takoe opisuje i same formate pojedinanih pixela. Dva najpoznatija formata u 16 bit-a su D3DFMT_R5G6B5 i D3DFMT_X1R5G5B5. U prozorskom reimu moete koristiti D3DFMT_UNKNOWN koji automatski preuzima model koji koristi i va dektop. BackBufferCount Vrednosti od 0 do 3, gde se 0 tretira kao 1. Tipino se samo jedan pozadinski bafer koristi, a u koliko to nije sluaj onda se primenjuje duplo baferisanje. U duplom baferisanju iscrtavanje se vri u pozadinskom baferu a onda se scena prikazuje kao celina korisniku. Vie bafera mogu poboljati iscrtavanje i uiniti da vaa aplikacija dobije na glatkoi, naravno sve to po ceni vie potronje memorije.

MultiSampleType and MultiSampleQuality Kontorlie viestruko semplovanje koje je vrsta pomagaa u reimu rada preko celog ekrana i pogotovu anti-aliasing-a. Moe znaajno poboljati kvalitet vae renderovane scene, ali kotae vas memorije i performansi. Treba spomenuti da viestruko semplovanje nije podrano od veeg broja grafikih karti. U ovom tekstu se time neemo baviti pa tip naeg viestrukog semplovanja podeavamo na D3DMULTISAMPLE_NONE a kvalitet na 0. SwapEffect Vrednosti ovog parametra su D3DSWAPEFFECT_DISCARD, D3DSWAPEFFECT_FLIP i D3DSWAPEFFECT_COPY. D3DSWAPEFFECT_DISCARD e naterati va ureaj da koristi najbru metode koju moe pri prikazivanju pozadniskog bafera. Ostale efekte naalost ne moemo spominjati u ovom tekstu jer su isuvie komplikovane. hDeviceWindow Ovu metodu ostavljamo takvom kakva jeste. Napredniji korisnici koji su zainteresovani za podrku sistema sa vie grafikih karti, u dokumentaciji DirectX-a mogu pronai vie detalja. Windowed Ukoliko je false, Direct3D e preuzeti ekran i promeniti model kako bi odgovarao dimezijama zadatim u BackBufferWidth i BackBufferHeight. Ukoliko je true, model prikazivanja ekrana nee biti promenjen. EnableAutoDepthStencil Postavlja opciju da li e vaa aplikacija koristiti dubinu posebnog stencil bafera. Za sada postaviemo je na FALSE. AutoDepthStencilFormat Slino formatu pozadinskog bafera, odreuje dubinu bitova i formata korienih od strane Depth/Stencil buffer. Kada je EnableAutoDepthStencil FALSE, ovo stanje se ignorie.

Flags Za napredne korisnike. Postaviemo ih za sada na 0. FullScreen_RefreshRateInHz Mera u Hz, odreuje brzinu osveavanja ekrana. Ukoliko je brzina koju ste naveli vea nego to to ureaj ili ekran moe da podri, kreiranje ureaja e ili propasti, ili e poslati poruku ekranu da ne moe da se izbori sa zadatom brzinom. Kako bismo bili sigurni postaviemo je na D3DPRESENT_RATE_DEFAULT. PresentationInterval Kontrola nad kanjenjem u prikazivanju pozadinskog bafera korisnicima. D3DPRESENT_INTERVAL_DEFAULT e saekati da se neto desi po vertikali pa e tada iscrtavati. Sada kada smo sve objasnili, moemo da kreiramo na ureaj. Evo jednog prototipa metode: With that out of the way, we can create our device. Let's look at the prototype for the method. HRESULT CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface ); U sledeem delu pozabaviemo se svakom od metoda i atributa unutar ove strukture i nastaviti dalje ka naoj prvoj aplikaciji u DirectX-u.

DirectX 9.0 Programiranje u C++ programskom jeziku (3.deo)


Rastko Ili

UVOD

U ovom broju baviemo se metodama i atributima neophodnim za kreiranje ureaja, kao mali podesetnik evo i koda kod kojeg smo stali: HRESULT CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface );

Adapter Ovom metodom odreujemo koj adapter emo dodeliti naem ureaju. Najei sluaj je upravo dodeljivanje jedan-na-jedan sa grafikom karticom koja se nalazi u raunaru. Na karticama koje podravaju vie grafikih procesora, popularno nazvane kartice sa vie glava (kartice koje mogu da koriste vie monitora na jednoj grafikoj kartici) svaka od tih glava moe pretstavljati nezavisni adapter. Kako bismo dobili primarno korieni adapter koristimo D3DADAPTER_DEFAULT. DeviceType Najee se koristi D3DDEVTYPE_HAL. HAL je skraeno od Hardware Acceleration Layer i koristi se za izvlaenje maksimuma iz grafike karte. D3DDEVTYPE_REF vri svo procesiranje softverski. Iako je ovo procesiranje je neverovatno sporo, njegova dobra strana je to u mnogome pomae pri debagiranju aplikacije. REF je skraeno od Reference Rasterizer, i predstavlja kompletnu implementaciju Direct3D specifikacija. Podrava veliki broj operacija koje moda ak ni vaa grafika karta ne podrava. Ukoliko dok koristite HAL pronaete neto to ne radi kako treba, moete to isto pokrenuti na REF-u i ukoliko se i tamo pokae da ne radi, najverovatnije je bug u samom programu, u suprotnom mogu je bau i u samom drajveru grafike karte. REF dolazi sa standardnim SDK-om, tako da ne raunajte na to da e ga i vai korisnici imati. hFocusWindow Postavite iste vrednoti kao i kod metode DeviceWindow u vaim presentacionim parametrima. Ukoliko koristite sliku preko celog ekrana, fokus mora biti prozor velikog priroriteta (top level). BehaviorFlags Ovim kontroliete veliki deo ponaanja iza scene. Najvei broj tih kontrola je isuvie napredan pa emo se mi pozabaviti samo nekim od njih. Tri najee koriene zastavice su: D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DCREATE_HARDWARE_VERTEXPROCESSING, i D3DCREATE_MIXED_VERTEXPROCESSING.

Samo jedna od ove tri zastavice se moe koristiti u jednom procesiranju.

SOFTWARE podeava sva vertex procesiranja (na primer osvetljenje) da se izvrava softverski. Ova zastavica je podrana od svih grafikih kartica i ima veoma olabavljena ogranienja na to ta moe da uradi. S'obzirom da se izvrava u softveru, sva vertex procesiranja se vre u CPU (procesoru raunara). Sa odabranom HARDWARE zastavicom, svo procesiranje se vri u grafikoj karti. Ovaj metod je bri zato to su grafike karte specijalno dizajnirane za ovaj posao i zato to se time oslobaa CPU koji za to vreme moe da se pozabavi drugim poslovima. Veina modernijih grafikih karti podrava hardversko procesiranje vertexa. Korienjem MIXED zastavice kao to i samo ime kae dozvoljava skakanje izmeu ve smopenutih dva naina rada. Ukoliko postoje operacije koje va hardver ne podrava, moete se prebaciti na softver dok se te operacije ne zavre. pPresentationParameters Ovo je samo pokaziva na vau PRESENTATION_PARAMETERS strukturu. IDirect3DDevice9 Ukoliko poziv na CreateDevice uspe, va novi ureaj e se vratiti ovde. Evo primera koda svega to smo do sada spomenuli: IDirect3D9 *d3d; //Pretpostavlja da je sve ve inicijalizovano kako treba HWND wnd; //Prepostvalja da je inicijalizovano D3DFORMAT format=D3DFMT_R5G6B5; //Zbog jednostavnosti za sada emo ovo hardkodovati D3DPRESENT_PARAMETERS pp; IDirect3DDevice9 *p_device=NULL; HRESULT hr; //Iako smo podesili sve njegove lanove, dobra praksa je da ih //sve postavimo na tane vrednosti. ZeroMemory(&pp,sizeof(D3DPRESENT_PARAMETERS)); pp.BackBufferCount= 1; //Potreban nam je samo jedan bafer pp.MultiSampleType=D3DMULTISAMPLE_NONE; //Bez viestrukog semplovanja pp.MultiSampleQuality=0;

pp.SwapEffect = D3DSWAPEFFECT_DISCARD; // Uklanjamo prethodne frejmove, ne trebaju nam pp.hDeviceWindow=wnd; //Ovo je na main (jedini)prozor pp.Flags=0; //Bez zastavica pp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT; //Predefinisani Refresh Rate pp.PresentationInterval=D3DPRESENT_INTERVAL_DEFAULT; //Predefinisana Presentation vrednost pp.BackBufferFormat=format; //Format prikazivanja pp.EnableAutoDepthStencil=FALSE; //Bez dubinskog/stencil bufera if(is_app_fullscreen){ pp.Windowed = pp.BackBufferWidth pp.BackBufferHeight }else{ pp.Windowed = } FALSE; = 640; = 480; TRUE;

hr=p_d3d->CreateDevice(D3DADAPTER_DEFAULT, //The default adapter, on a multi-monitor system //there can be more than one. D3DDEVTYPE_HAL, //Koristite hardversku akceleraciju, pre nego softversku pri renderovanju. //Na prozor wnd, //Procesiranje vertexa u softveru. Sporije od hardvera,ali e -//raditi na svim grafikim kartama. D3DCREATE_SOFTWARE_VERTEXPROCESSING, //Naa D3DPRESENT_PARAMETERS struktura, kako bi znao ta //elimo da napravimo. &pp, //Ovo e biti pokaziva na na novi ureaj. &p_device); if(FAILED(hr)){ // Obraiva greaka } HRESULT i Testiranje Virtuelno svi metodi u DirectX-u vraaju HRESULT kako bi potvrdili da li je

neto uspeno ili neuspeno. Pravilan nain testiranja da li je metoda neuspena je FAILED makro. Postoji takoe i SUCCEEDED makro koji proverava da li je metoda uspena. U mnogim primerima, uspeh e se testirati poreenjem HRESULT i D3D_OK. Ovo nije pravilan nain kojim biste trebalo da proveravate. Jedini garantovani metod testiranja neega neuspenog je sa ve spomenutim makroima. HRESULT sadri vie od jedne uspeno/neuspeno zastavce. Zapravo moe vam pruiti detaljnije informacije koje vam govore ta se desilo. Na primer, ukoliko pozovete CreateDevice a nema dovoljno memorije, vratie D3DERR_OUTOFVIDEOMEMORY. Kako biste od neitljivih kodovanih greaka dobili neto to je lake i mnogo razumljivije moete koristiti DirectX Error Lookup alatku koja dolazi uz SDK. Sprovedite joj numeriku vrednost greke i alat e vam objasniti greku. Drugi nain rada sa isipisivanjem greaka je DXGetErrorString9 koji uzima jedan HRESULT kao parametar a vraa pokaziva na string koji objanjava greku. Odreivanje korisnog D3DFORMAT formata U prethodnim sluajevima koristili smo hardkodovanu vrednost za D3DFORMAT. Ovo je loa ideja jer time podrazumevamo da i grafika karta kod korisnika podrava isti format, to ako je zaista i sluaj e spreiti aplikaciju da napravi ureaj. Pravilan pristup ovom problemu je da upitamo ureaj da proveri da li podrava zadati format. Kako bismo ovo uradili koristimo CheckDeviceType metodu naeg IDirect3D9 objekta pre nego to kreiramo ureaj. HRESULT CheckDeviceType( UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL Windowed );

Adapter Ovo je ista vrednost koju emo koristiti u pozivanju CreateDevice-a, Ovime se identifikuje koja grafika karta e se koristiti.

DeviceType S'obzirom da elimo da testiramo podrane formate od strane nape grafike karte, koristimo D3DDEVTYPE_HAL. DisplayFormat Format korien od strane monitora. U prikazu preko celog ekrana bie isti kao i format pozadinskog bafera. Uz pomo IDirect3D9->GetAdapterDisplayMode moete doi do trenutno korienog reima prikaza. BackBufferFormat Ovo je format u kome e se va pozadniski bafer prikazivati. Windowed S'obzirom da postoji vie pravila korienja prikazivanja preko celog ekrana i prikazivanja u prozorskom reimu, morate odrediti koji koristite. Evo primera testiranja da li je D3DFMT_R5G6B5 podran u prikazu preko celog ekrana. HRESULT hr; hr=d3d->CheckDeviceType(D3DADAPTER_DEFAULT, //Adapter D3DDEVTYPE_HAL, //Tip ureaja D3DFMT_R5G6B5, //Format prikazivanja D3DFMT_R5G6B5, //BackBuffer Format false); //Prozorski reim if(SUCCEEDED(hr)){ //Format je validan }else{ //Pojava greke } U prozorskom reimu rada, najbolje je staviti D3DFMT_UNKNOWN u pozivu CreateDevice-a. Render Petlja

U ovom broju neemo jo uvek nita renderovati, ali upustiemo se u samo radno okruenje naeg rendera kako bi nam kasnije stvari koje dolaze bile jednostavnije za shvatanje. Da bacimo pogled na osnovnu main petlju: bool g_app_done=false; HRESULT hr; while(!g_app_done){ dhMessagePump(); hr=render(); } Prolazimo kroz petlju sve dok ne doemo do zastavice koja signalizira kraj nae aplikacije. Ova zastavica dobija vrednosti od naeg prozorskog rukovodioca sa porukama. Svaki put kada prolazimo kroz nau petlju mi pozivamo nau render funkciju. Ovo je u najkraim crtama kako se poziva render funkcija koju emo u sledeim brojevima daleko opirnije i detaljnije objasniti. //Proverava poruke od strane prozora

//Iscrtava nau neverovatnu grafiku

DirectX 9.0 Programiranje u C++ programskom jeziku (4.deo)

Rastko Ili

UVOD U ovom broju nastaviemo tamo gde smo i stali kod pozivanja funkcije render. Prva stvar na koju morate da obratite panju kada pozivate render funkciju je da joj zadate da pre svega isprazne pozadinski bafer. Trenutni pozadinski bafer bi mogao jo uvek da prikae sadraj iz prethodnog okvira ili neki nasumice zaostali deo koji ne elimo. Jedini izuzetak predstavlja korienje runtime debagera sa D3DSWAPEFFECT_DISCARD prezentacionom zastavicom. U ovom sluaju pozadinski bafer se prazni kako bi se smenjivale zelena i crvena boja. Ukoliko ikada vidite da se ove dve boje naizmenino menjaju u vaoj aplikaciji to znai da se pozadinski bafer ne prazni kako treba. Pozadinski bafer se prazni pozivanjem metode Clear vaeg ureaja. Ova metoda se moe koristiti nad kompletnim baferom ili pak nad odreenim njegovim pravougaonicima.

HRESULT Clear( DWORD Count, const D3DRECT *pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil ); Count Broj individualnih pravougaonika koji elite da ispraznite. Ukoliko praznite itav bafer podesite na 0. pRects Ovo je niz RECT struktura koje elite da ispraznite, ukoliko praznite itav bafer podesite na NULL. Flags Dozvoljene zastavice (svaka kombinacija je dozvoljena) su D3DCLEAR_STENCIL, D3DCLEAR_TARGET and D3DCLEAR_ZBUFFER. Dodatno moete odreditiD3DCLEAR_STENCIL ukoliko imate stencil bafer, a moda ete koristiti D3DCLEAR_ZBUFFER ukoliko imate Z (dubiski) baferr. Ukoliko odredite zastavicu bez odgovarajue povrine na koju se primenjuje, poziv e propasti. Takoe, makar jedna od ovih zastavica mora biti odreena. Color Ovo je pozadina koju elite da postavite za pozadinu vaeg pozadinskog bafera. Moete ga odrediti kao 32-bitni ARGB broj ili u heksadecimalnom zapisu 0xAARRGGBB. Z Ovo je vrednost koriena za pranjenje dubinskog bafera. Pravilne vrednosti koje se odreuju su u rasponu od 0.0 do 1.0, gde 0.0 predstavlja najbliu distancu a 1.0 najdalju.

Stencil Stencil bafer e se isprazniti do ove vrednosti. Ovo je integer vrednost u rasponu od 0 do 2n-1, gde je n dubina stencil bafera. Kako biste upozorili ureaj da ste spremni za renderovanje pozivate metodu BeginScene,a kako biste ga obavestili da ste zavrili, metodu EndScene. Kako obe ne zahtevaju nikakve parametre neemo se optereivati sa prikazom samih detalja. Sledee to sledi je prezentovanje pozadinskog bafera korisniku. Kako biste to uraili pozivate Present metodu. Evo kako ona izgleda. HRESULT Present( const RECT *pSourceRect, const RECT *pDestRect, HWND hDestWindowOverride, const RGNDATA *pDirtyRegion ); pSourceRect Ovim se odreuje pod-pravougaonik kao izvor. Generalno bie vam potrebno da prikaete itav bafer tako da postavite NULL. pDestRect Ovo je pod-pravouganoik displeja na koji elite da se prikae va pozadniski bafer. Ponovo, ovde se najee postavlja NULL kako biste pokrili itavu povrinu. hDestWindowOverride Ukoliko zadate NULL, pozadinski bafer e se kopirati na prozor koji ste vi odredili kada ste napravili ureaj. Postavljanjem ovoga na razliit prozor dozvoljava vam da i prikaete u drugom prozoru. pDirtyRegion Ovim se odreuje da se samo jedan deo slike mora aurirati i taj deo i definie ovaj region. Ovo je mali trik pri optimizaciji aplikacija, ali za sada emo ostaviti NULL kao vrednost.

Sada kada smo sve objasnili, probajmo da sve i spojimo. hr=g_d3d_device->Clear(0,NULL, D3DCLEAR_TARGET, 0x00000000,1.0f, 0 ); if(FAILED(hr)){ } hr=g_d3d_device->BeginScene(); //poetak iscrtavanja if(FAILED(hr)){ } //Ovde ete staviti vae sjajne kreacije //Obavestite ureaj da je kraj scene g_d3d_device->EndScene(); //Prikaite rezultat hr=g_d3d_device->Present(NULL,NULL,NULL,NULL, NULL ); if(FAILED(hr)){ } Da smo zapravo i renderovali neto, na mestu gde pie da ete staviti vae kreacije bi se nalazilo ono to se i renderuje. Izgubljeni ureaji Ovo je jedna od bitnijih stvari koje bi trebalo nauiti. Ukoliko imate aplikaciju koja se prikazjue preko celog ekrana i korisnik pritisne ALT+TAB kako bi se vratio na dektop, vaa aplikacija izgubi pristup displeju. Postoje i drugi dogaaji koji mogu prouzrokovati slinu neprijatnost, ali ipak je to najee ALT+TAB kombinacija. Jednom kada ste izgubili va ureaj vie niste u mogunosti da renderujete bilo ta. Pokuaji da se pozove Present e se zavriti vraenom grekom D3DERR_DEVICELOST. Jedan drastian nain da se stvari vrate na svoje mesto je da se sve oslobodi i ponovo kreira. Ali postoji i jednostavniji nain. Pozovite TestCooperativeLevel koji upituje ureaj za njegov status. Ukoliko se vrati upeno, va ureaj je u redu. Ukoliko se pak javi D3DERR_DEVICELOST va ureaj je nestao i ne moe jo uvek biti resetovan.

Pre nego to ureaj dozvoli da se ponovo pokrene, vi morate osloboditi odreene resurse. U naem primeru napravili smo funkciju FreeVolatileResources koja e nam to uraditi. Takoe smo napravili funkciju koja se zove InitVolatileResources koja se koristi nakon to je ureaj uspeno ponovo pokrenut. Vrednost koju vraa D3DERR_DEVICENOTRESET predstavlja znak da je bezbedno resetovati ureaj. TestCooperativeLevel ne uzima nikakve parametre, a da biste ponovno pokrenuli ureaj pozivate Reset metodu. HRESULT Reset( D3DPRESENT_PARAMETERS *pPresentationParameters ); Kao to i sami moete videti, Reset uzima samo jedan parametar, pokaziva na va D3DPRESENT_PARAMETERS. Iz ovog razloga dobra je ideja da uvate kopiju vaih trenutnih parametara negde u svom kodu. Na ovaj nain takoe neverovatno lako moete namerno menjati paramtre u svojoj aplikaciju, na primer kada elite da vaa aplikacija moe da prelazi iz prozorskog reima u reim preko celog ekrana. Evo koda koji obrauje izgubljeni ureaj. HRESULT hr; hr=p_device->TestCooperativeLevel(); if(hr == D3DERR_DEVICELOST) { //ureaj je izgubljen i nije spreman za resetovanje Sleep(500); FreeVolatileResources(); }else if(hr == D3DERR_DEVICENOTRESET){ //izgubljen ali ga sada moemo resetovati hr=p_device->Reset(p_pp); if(SUCCEEDED(hr)){ InitVolatileResources(); } }

Ovim smo pokrili sve to se dogaa u render fazi, u sledeem broju kreemo sa zanimljivijom temom, osnovnim grafikim objektima kao to su take,

primitive i kordinatni sistem. Od sada emo u okviru svakog broja dostavljati i kod u ovome dokumentu kako biste mogli da kopirate i isprobate ili pak poredite sa svojim kada budete i sami radili. Kod korien u ovom broju: #include <D3DX9.h> #include "../common/dhWindow.h" #include "../common/dhD3D.h" #include "../common/dhUtility.h" #include "../Common/dhUserPrefsDialog.h" #pragma comment(lib,"d3d9.lib") #pragma comment(lib,"dxerr9.lib") LRESULT CALLBACK default_window_proc(HWND p_hwnd,UINT p_msg,WPARAM p_wparam,LPARAM p_lparam); HRESULT init_scene(void); void kill_scene(void); HRESULT render(void); void InitVolatileResources(void); void FreeVolatileResources(void); const char *g_app_name="Incijalizovanje D3D9 aplikacije"; const int g_width=640; const int g_height=480; const int g_depth=16; bool g_app_done=false; IDirect3D9 *g_D3D=NULL; IDirect3DDevice9 *g_d3d_device=NULL; D3DPRESENT_PARAMETERS g_pp;

// Funkcija:WinMain

int APIENTRY WinMain(HINSTANCE ,HINSTANCE ,LPSTR ,int ){ bool fullscreen; HWND window=NULL; D3DFORMAT format; HRESULT hr; dhUserPrefs user_prefs(g_app_name); if (!user_prefs.QueryUser()) { dhLog("Izlazak\n"); return 0; } fullscreen = user_prefs.GetFullscreen(); hr=dhInitWindow(fullscreen,g_app_name,g_width,g_height,default_ window_proc,&window); if(FAILED(hr)){ dhLog("Neuspesno kreiranje prozora",hr); return 0; } hr=dhInitD3D(&g_D3D); if(FAILED(hr)){ dhKillWindow(&window); dhLog("Neuspesno kreiranje D3D-a",hr); return 0; } hr=dhGetFormat(g_D3D,fullscreen,g_depth,&format); if(FAILED(hr)){ dhKillWindow(&window); dhLog("Neuspelo dobavljanje displej formata ",hr); return 0; } DWORD adapter = user_prefs.GetAdapter(); D3DDEVTYPE dev_type = user_prefs.GetDeviceType();

//Inicijalizacija sadasnjih parametara

dhInitPresentParameters(fullscreen,window,g_width,g_height,forma t, D3DFMT_UNKNOWN,&g_pp); //Kreiranje uredjaja hr=dhInitDevice(g_D3D,adapter,dev_type,window, &g_pp,&g_d3d_device); if(FAILED(hr)){ dhKillD3D(&g_D3D,&g_d3d_device); dhKillWindow(&window); dhLog("Neuspeno kreiranje uredjaja",hr); return 0; } init_scene(); while(!g_app_done){ dhMessagePump(); //Provera windows poruka

hr=g_d3d_device->TestCooperativeLevel(); if(SUCCEEDED(hr)){ hr=render(); } //Uredjaj izgubljen if(hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET){ dhHandleLostDevice(g_d3d_device,&g_pp,hr); }else if(FAILED(hr)){ g_app_done=true; dhLog("Greska pri renderovanju",hr); } } kill_scene(); dhKillD3D(&g_D3D,&g_d3d_device);

dhKillWindow(&window); return 0; } void InitVolatileResources(void){

} void FreeVolatileResources(void){ } HRESULT init_scene(void){ HRESULT hr=D3D_OK; InitVolatileResources(); return hr; } void kill_scene(void){ FreeVolatileResources(); } HRESULT render(void){ HRESULT hr; hr=g_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0 ); if(FAILED(hr)){ return hr; }

hr=g_d3d_device->BeginScene(); if(FAILED(hr)){ return hr; } g_d3d_device->EndScene(); hr=g_d3d_device->Present(NULL,NULL,NULL, NULL ); return hr; } LRESULT CALLBACK default_window_proc(HWND p_hwnd,UINT p_msg,WPARAM p_wparam,LPARAM p_lparam){ switch(p_msg){ case WM_KEYDOWN: case WM_CLOSE: case WM_LBUTTONDOWN: g_app_done=true; return 0; } return (DefWindowProc(p_hwnd,p_msg,p_wparam,p_lparam)); }

You might also like