Máme tu nový díl seriálu, který navazuje na tradici seriálu o grafice v TP7, který navazuje na tradici mých keců o programování. Tentokrát nás čeká málo samostatného programování, které bude zahrnovat využití Linear Frame Bufferu, ale spíše využívání cizích jednotek, mezi které zahrneme Graph (z FP, ne tu ubohou z TP7), WinGraph a velmi okrajově také SD, Direct Draw (z Direct X 7) a spol.
Než ale začneme, rád bych doplnil některý z předchozích dílů, kde jsme se naučili provádět rotace ve 2D. Pokud budete někdy dělat 3D hru, tak budete potřebovat rotovat ve 3D. A nejen to, u objektů, které budou popsány pomocí 3D, budete muset provádět různé transformace při jejich otáčení, zrcadlení nebo přesouvání (až už po mapě (na což vám ale stačí pouhé INC a DEC funkce), nebo před kameru). Většinu z nich najdete v časopisu Výheň v článcích Vektorová grafika a 3D engine. Pro rotace uvedu vzoreček zde:
Chcete-li otočit pixel se souřadnicemi XYZ, musíte provést toto:
Y1 = Y*cos(a)+Z*sin(a) Z1 = -Y*sin(a)+Z*cos(a) X1 = X*cos(b)+Z1*sin(b) X' = X1*cos(c)+Y1*sin(c) Y' = -X1*sin(c)+Y1*cos(c) Z' = -X*sin(b)+Z1*cos(b)
Kde nové souřadnice budou X'Y'Z'. ABC jsou úhly, o které chcete rotovat v osách XYZ (tedy se nejedná jen o 1 úhel, jako v případě 2D). Pokud si pamatujete na náš přepočet z 3D na 2D, tak se vlastně jedná o výpočet perspektivy. Ta vypadá pro XYZ na X'Y' takto:
X' = (X SHL ShiftFactor) div (Z+ZDelta) Y' = (Y SHL ShiftFactor) div (Z+ZDelta)
Kde ShiftFactor je míra zkreslení a ZDelta je relativní prodloužení na ose Z. To jsou základní výpočty, které budete ve 3D potřebovat. Ale ono jich časem přibyde víc. Toto by Vám ale mělo pomoci hlavně do začátku. Pro zajímavost ještě přikládám tabulku grafických režimů pod nativním Linuxem (abyste viděli, jak je moc "nebezpečné" se držet statických čísel, která Vám např. poskytne ATHelp):
Colours 640x400 640x480 800x600 1024x768 1152x864 1280x1024 1600x1200 --------+-------------------------------------------------------------- 4 bits | ? ? 0x302 ? ? ? ? 8 bits | 0x300 0x301 0x303 0x305 0x161 0x307 0x31C 15 bits | ? 0x310 0x313 0x316 0x162 0x319 0x31D 16 bits | ? 0x311 0x314 0x317 0x163 0x31A 0x31E 24 bits | ? 0x312 0x315 0x318 ? 0x31B 0x31F 32 bits | ? ? ? ? 0x164 ?
Teď se na chvíli odmlčíme od Turbo Pascalu 7. Vrátíme se k němu na chvíli jen u zvukových karet a sítě IPX (nebo ještě sériového kabelu). Pravda, dá se v něm učit, a je i celkem snadný, ale nedají se v něm dělat profesionální aplikace (dají, pokud oželíte rychlost). Stejně tak v něm můžete udělat i Dooma, pokud Vám nebude vadit, že si ho na 486 a nižších moc nezahrajete. Chce to už něco lepšího a když už umíme Pascal, proč se mořit s učením nějakého Visual Basicu, Delphi (ač je Pascalu velmi podobné) nebo C (C++, Java (už existuje i kompilátor, který Vaše programy z Pascalu zkompiluje do Javy pro mobilní telefony!), atd.), když tu máme Free Pascal. Já jen podotknu, že jelikož jsem začal v jedné firmě zavádět Linux přes server, a programovat databázový síťový software, asi už nebudu mít čas (a náladu) psát články každý týden (někomu tím udělám možná radost). Jde o to, že člověk musí mít náladu po 10 hodinách práce s PC v práci ještě něco psát o víkendu. Ale tím Vás nechci zatěžovat (navíc se během práce mohu něco naučit a určitě o tom později napíšu).
Začneme metodou zobrazování, která se nazývá linear frame buffer. Podotýkám pár věcí: pod Windows XP to nemusí fungovat, pokud jedete v DOSovém programu. A také nejprve trochu teorie: LFB podporují hlavně novější karty (tím myslím cca. 10 let a více), hlavně na to musí být VESA verze 2.0 a vyšší, pokud se nepletu. Nejprve se tedy přepnete do režimu, který bude využívat LFB. To provedete tak, že k číslu režimu přičtete do registru BX hodnotu $4000:
function VESA_LFB(rezim : word) : word; assembler; asmmov eax,$4f02 mov ebx,rezim or ebx,$4000 int 10hend;
Podle výstupu funkce pak poznáte, zda daný režim jde nastavit, nebo zda je vůbec podporovaná funkce LFB (a pokud ne, ale daný režim ano, budete ho muset nastavit ještě jednou, ale viz. minule). Adresa VRAM nyní neleží na $a000 (i když by ještě stále mohla ležet), ale někde nad 1. MB paměti. Pokud si necháte zjistit informace o tomto režimu (viz. minule), tak v LFB najdete fyzickou adresu VRAM pro tento režim (nezapomeňte, že tento blok informací MUSÍ ležet v konvenční paměti, takže nejprve musíte alokovat přes "global_dos_alloc" funkci dolní paměť, tam zjistit informace a pak tuto paměť uvolnit (není jí zase tolik, aby se s ní mohlo plýtvat). Budeme také potřebovat velikost této paměti, a tu najdeme v položce PAMET v informacích o celé kartě. Tyto dva údaje si uložte někam stranou (nazvu je např. _LFB a _PAMET) do normálních VAR proměnných.
Tuto adresu však nemůžete přímo použít. Musíte nejprve namapovat fyzickou adresu do lineární pomocí DPMI, najít základní adresu DS selektoru, který používáte pro data, a odečíst tuto bázovou adresu od lineární, abyste dostali blízký offset VRAM, kam můžete psát pixely:
function ZjistiAdrLFB(Fyzicka : longint) : longint; var Linear : longint; begin Linear := get_linear_addr(_LFB,_PAMET); Set_Segment_Limit(get_ds,$FFFFFFFF); lock_linear_region(_LFB,_PAMET); Linear:=Linear-get_segment_base_address(get_ds); ZjistiAdrLFB := Linear; end;
Nyní už víte, kam můžete zapisovat svoje data (_LFBlin). Velikost X,Y,BPP a počet bytů na řádku (_BYTESLINE) máte uloženo opět v informacích o módu. Nyní je adresování už velmi jednoduché. Celá VRAM je totiž přístupná bez nějakého přepínání banků, takže adresa Vašeho pixelu je (X+Y*MaxX)*BPP. Zjednodušeně (bez optimalizací, které jsou závislé právě na tom MaxX) se to dá napsat takto (příklady pro 8, 15/16 a 24/32 bitů):
procedure PutPixel8(x,y : longint; barva : byte); assembler; asmmov eax,y mul _bytesline add eax,x mov bl,barva add eax,_lfblin mov [eax],blend; procedure PutPixel16(x,y : longint; barva : word); assembler; asmmov eax,y mul _bytesline add eax,x mov bx,barva add eax,_lfblin mov [eax],bxend; procedure PutPixel32(x,y : longint; barva : longint); assembler; asmmov eax,y mul _bytesline add eax,x mov ebx,barva add eax,_lfblin mov [eax],ebxend;
Všechno ostatní zůstává stejné. Jen nezapomeňte při přepínání režimu (ukončování VESA) provést ještě toto (INLINE používám proto, aby FP tuto proceduru vložil na místo jejího volání jako makro a ne jako volání procedury, čímž je urychlí vykonání jejího kódu (za cenu, že kód bude velký jako její příkazy a ne jako CALL)):
procedure KonecLFB; inline; begin unlock_linear_region(_LFB,_PAMET); end;
Tak, teď se trochu začneme spoléhat na cizí jednotky. Výhoda je jasná: pokud chcete programovat pro Windows (nebo i Linux), tak tam to, co jsme se doposud naučili, nemůžete použít (tím myslím, že budete vytvářet nativní WIN32 aplikace: DOSová aplikace spuštěná pod WIN se bude chovat naprosto stejně jako pod DOSem, za předpokladu, že je Windows dobře napsán - když ne, použijte emulátor, např. DOSbox (s tím můžete spustit Vaše DOSovky i pod Linuxem)).
Free Pascal nám pro základ poskytuje jednotku GRAPH. Je sice pomalejší než to, co si můžeme napsat vlastními silami, nebo za použití dalších jednotek (budeme brát dále alespoň co se týče Windows), ale má dvě výhody: a) podporuje i VESA režimy, jako 800x600/TC, b) běží pod DOSem, Windows a i Linuxem (zkrátka je určena pro všechny platformy, které FP podporuje). Pokud nepotřebujete psát náročné 3D hry, ale jen např. grafické účetní/databázové/grafové aplikace, jistě Vám přijde vhod, že Váš program budete moci zkompilovat pro 3 systémy současně. Protože jednotka GRAPH je dobře popsaná v Turbo Pascalu 7 (navíc i v nápovědě pro FP) a používá stejné funkce (OutText,SetColor,atd.), nebudu jí rozebírat do hloubky. Ukážu Vám jen příklad, jak jednoduše zapnout rozlišení 800x600 v 8bitových barvách:
uses Graph; begin initgraph(d8bit,m800x600,''); (* nepotřebujeme žádné BGI *) if error = grOk then begin (* zde si nakreslete něco na obrazovku, atd. *) closegraph; end; end;
Jak vidíte, je to velice snadné. Možná ne tak rychlé, ale pro vykreslení něčeho jako je Windows 3.11 bez nároku na přehrávání videa a animací dostačující. Pokud je Vašim primárním cílem hlavně Windows, můžete využít jednotku WinGRAPH. Ta přistupuje přímo přes Direct Draw a umožní Vám používat i Open GL, takže tedy i 3D objekty! Používá téměř stejné funkce jako jednotka GRAPH.
Můžete zde využít pár nových funkcí (jako např. reagovat na zavření aplikace vynucené uživatelem, podpora bézeriových křivek a jakžtakž i animací). Nevýhoda je, že Vaše aplikace může běžet buď v okně, kde si rozměry můžete nadefinovat sami, nebo ve Full Screen, kde jsou ovšem rozměry obrazovky závislé na aktuálním nastavení plochy Windows. Režimy, které můžete využívat se zadávají podobně jako v FP. Zde máte 2,16 a 256 barevné palety, nebo True Color (ovšem pravděpodobně 24 bitový). Rozlišení v okně sahá od m320x200 až po m1280x1024. Pokud pracujete na Full Screen (mFullSc), musíte se přizpůsobovat podle MaxX a MaxY. Podporovany jsou i původní zastaralé režimy, jako např. m640x350. Navíc si můžete pojmenovat titulek okna, pokud v něm bežíte. Můžete používat také všechny fonty, které jsou instalované ve Windows! Jednotka podporuje až 4 grafické stránky, do kterých můžete kreslit. Výběrem aktivní si určíte, která se ale bude zobrazovat. Jednotka podporuje obnovování VRAM podle určitých pravidel. Standardně se kreslí přímo do VRAM. Můžete si ale zapnout, abyste kreslili nejprve do Vašich stránek (UpdateOff), a teprve po kompletním dokreslení je zobrazíte (UpdateNow). To provádí funkce UpdateGraph. K jednotce se dodávají i jednotky pro práci s myší, zvukem (píp) a klávesnicí. Zde je nějaký ten příklad (k jednotce se dodává dost dobře popsaná dokumentace a spousta příkladů):
uses WinGraph; begin InitGraph(D8bit,_m640x480,'Test rozlišení 640x480'); UpdateGraph(UpdateOff); {nastavíme jeden z předvolených fontů na tučný a velikost 16 pixelů} {cokoliv pod 6 není vel.v pixelech, ale násobič 8 pixelů} SetTextStyle(TimesNewRomanFont or BoldFont,HorizDir,16); (* tady si něco vykreslete (používáme stránku 0) *) UpdateGraph(UpdateNow); (* tady můžeme třeba chvíli počkat *) CloseGraph; end;
Pokud Vám toto nevyhovuje, můžete použít rovnou Direct Draw. Budete potřebovat jednotku (z balíku "gx106") DXCommon a DirectDrawFPC společně se souborem Switches.inc (vše si zkompilujete). Zde jsou implementovány všechny funkce, které byste mohli volat ve Windows programovacích jazycích. Jednotky u sebe obsahují velkou spoustu příkazů. Nejhorší na vysvětlení je asi to, že ten nejblbější příklad ukazující metodu PageFlipp má 10 kB zdrojového textu! Naštěstí ale můžete využít i tutorialy Direct X, které jsou např. na adrese www.CeskeHry.cz, ale i v těch příkladech přímo u jednotky je popsán kód celkem přehledně. Problém je v tom, že než v Direct Draw vůbec inicializujete režim, trochu se zapotíte. Pokud chcete pracovat s grafikou o trochu lépe, než Vám nabízí např. WinGraph, budete se do toho muset ponořit (pod Linuxem Vám poběží takové programy samozřejmě také, pokud použijete WINE). Stručně byste mohli potřebovat informace o těchto věcech:
IDirectDraw4 objekty IDirectDrawSurface4 obrazovka IDirectDrawSurface4_Blt vykreslí obrazovku IDirectDrawSurface4_GetDC ? IDirectDrawSurface4_ReleaseDC ? PostMessage zprávy SetCursor kurzor IDirectDrawSurface4_Flip přesune obrazovku do VRAM IDirectDrawSurface4__Restore ? LoadIcon nahraje ikonu LoadCursor nahraje kurzor myši GetSystemMetrics informace o XY obrazovky ShowWindow zobrazí okno UpdateWindow, SetFocus ? IDirectDraw_QueryInterface ? IDirectDraw4_SetCooperativeLevel IDirectDraw4_SetDisplayMode mění rozlišení IDirectDraw4_CreateSurface vytvoří "obrazovku" IDirectDrawSurface4_GetAttachedSurface SetTimer nastavuje časovač
Pokud se nechcete mořit s Direct X a chcete raději zkusit něco, co Vám nabízí komfort, můžete zkusit jednotky VGFX (obsahuje i tutorial), která nabízí celkem rychlé zobrazování efektní grafiky pod Windows; grafickou část jednotek lx5suite_0760, které nabízí opravdu kompletní ovládání počítače (včetně zvuku (na ten Vám mohu ještě doporučit např. Bass20 nebo Fmod350)); Qgtk-Graphic; FPCx-dx (který zjednoduší používání Direct Draw); nebo využít docela oblíbené JediSDL, které obsahuje podporuje grafiky, sítě, ale i zvuků. Navíc SDL běží zároveň jak pod Windows, tak i pod Linuxem, tedy pokud napíšete aplikaci chodící pod jedním, můžete ji rovnou jen změnou targetu překompilovat pro druhou platformu. Samozřejmě, že naučit se plně využívat možností těchto jednotek nebude snadné, ale pokud to myslíte s programováním opravdu vážně, měli byste s tím začít. Pokud se rozhodnete pro některou z těchto věcí, asi budete potřebovat následující funkce a procedury:
--- VGFX --- (viz. "VGFX - making first steps.doc") Window_RegisterClass Window_CreateClass CheckWMCreate Init_Graph Load_Fonts Load_Pcx Init_VW ScaleSprite CursorVisible Flip_VW PutSprite Flip_DD GETALLMESSAGES HANDLEMINIMIZED ApplicationRunning : boolean Kill_VW Kill_Graph CheckMemory TERMINATE atd. --- LX5SUITE --- (dema jsou dostatečně přehledná) video_open video.hline LoadImage tsprite.create_assign sprite.show sprite.move video_update sprite.hide lx5suite_process keywaiting keyread mouse.leftbutton sprite.free video_close LoadAlpha CreateImage backdrop.paste_area workspace.alpha_tint video_lock video_unlock video.cls workspace.paste workspace.paste_region backdrop.convert_depth video.putpixel video.line video.box video.bar video.ellipse video.polygon video.circle backdrop.paste_scale scalar.resize scalar.paste_area - ovládání videa a grafiky - ovládání zvuků - práce s fonty TTF - vytváření GUI (okna, tlačítka, plochy) - efekty s grafikou - přístup na web --- SDL Jedi --- (obsahuje podrobnou HTML dokumentaci s příklady u každé funkce) - grafika a video - klávesnice - joystick - zvuková zařízení (WAV) - ovládání CD-ROM - časovač - Open GL - TCP přes sockety
Protože nám tento seriál vyšel tentokrát docela krátký (omlouvám se za to, že zde nejsou příklady pro předchozí dva odstavce, ale to bych se to musel nejprve všechno naučit, což udělám, až to osobně budu potřebovat), přidám sem jako bonbónek krátkou (!) informaci ovládání WinAPI, které Vám s pomocí FP umožní vytvářet aplikace typu Poznámkový blok (běžící ve Windows a s WINE i pod Linuxem). Pro tyto účely se nemusíte shánět po žádných extra jednotkách. Pokud si stáhnete FP pro Windows, tak v adresáři DEMOS a WIN32 najdete pár ukázek, jak vytvořit DLL knihovnu, a popř. i nějaký ten jednodušší program. Myslím, že je celkem zbytečné vysvětlovat princip těchto programů, neboť pokud se o ně budete chvíli zajímat, pochopíte jej sami:
SendMessage GetOpenFileName GetSaveFileName GetWindowTextLength GetWindowText SetStatusText MessageBox SetWindowText ChooseFont PostQuitMessage SetFocus GetClientRect MoveWindow GetSysColorBrush DefWindowProc GetStockObject LoadIcon LoadCursor WndProc RegisterClass CreateWindowEx ShowWindow UpdateWindow CreateMenu AppendMenu SetMenu CreateStatusWindow TranslateMessage DispatchMessage DeleteObject
Samozřejmě, budete-li mít zájem, napište na INT21H (někomu jinému než mě), zda-li by se nechtěl ujmout nějakého článku, který by vysvětloval některou konkrétní věc z posledních třech až čtyřech odstavců (např. grafika v SDL; pokud Vám nebudou stačit dodávaná efektní grafická dema a příklady); budu mezi těmi, kteří si dané články rádi přečtou.
Já tímto můj seriál o grafice uzavřu. Zároveň konstatuji, že pokud chcete programovat pod DOSem, můžete se nyní plně spolehnout na své síly. Pokud byste ale chtěli Váš program distribuovat a provozovat pod Windows (či Linuxem), využijte rozhodně raději nějakou s cizích jednotek (popř. WinAPI). Doporučuji buď SDL nebo lx5suite_0760 či VGFX-vesa3 podle toho, co Vám přijde jednodušší na naučení se (ideální je samozřejmě balík, který podporuje zároveň i zvuky, jako je lx5suite_0760 nebo JediSDL-Delphi, pokud se ovšem nespolehnete na zvukové jednotky cizích stran - ale o tom snad někdy příště).