Obsah
1. Pohled pod kapotu JVM – složitější tělesa, textury a sprity v knihovně SDLJava
4. ByteBuffer versus pole v Javě
5. Výběr prvků z pole/bufferu a přenos jednotlivých vrcholů na grafický akcelerátor
6. Demonstrační příklad SDLTest17 – použití metody glArrayElement()
7. Demonstrační příklad SDLTest18 – použití metody glDrawArrays()
8. Objekt typu Sprite v knihovně SDLJava
9. Demonstrační příklad SDLTest19 – texturování a objekt typu Sprite
10. Repositář se zdrojovými kódy všech tří dnešních demonstračních příkladů
1. Pohled pod kapotu JVM – složitější tělesa, textury a sprity v knihovně SDLJava
V předchozí části tohoto seriálu jsme si řekli, že pomocí grafické knihovny OpenGL a SDLJava je možné vykreslovat pouze několik základních grafických prvků – grafických primitiv. Veškeré složitější tvary těles se musí složit z těchto grafických primitiv, což ve většině případů při zobrazování trojrozměrných scén znamená využití trojúhelníků a čtyřúhelníků. Kromě obecných vlastností těchto prvků je v některých případech zapotřebí zadávat i vlastnosti jednotlivých vrcholů. Mezi tyto vlastnosti, které se typicky nastavují uvnitř takzvaných „programových závorek“ glBegin() a glEnd(), patří:
- Pozice vrcholu (vertexu) v rovině či prostoru. Nastavuje se metodouGL.glVertex*(), přičemž lze zadat dvě souřadnice (rovina), tři souřadnice (prostor) nebo čtyři souřadnice (prostor+„váha“ vrcholu reprezentovaná čtvrtou souřadicí).
- Barva vrcholu, která se nastavuje metodou GL.glColor*(). Barva plošky mezi vrcholy (stejně jako některé další parametry) může být dopočítána pomocí interpolace.
- Index barvy vrcholu v barvové paletě, který se nastavuje metodouGL.glIndex*(). Barvové palety a indexované barvy byly do knihovny OpenGL zařazeny pro podporu vykreslování na starších grafických subsystémech, které nepodporovaly režim True-Color. V dnešní době však ztratily smysl (kromě diskutabilní animace pomocí změny jednotlivých barev) a přestávají být také v nových grafických kartách podporovány, proto se jimi dále nebudeme zabývat (od OpenGL 3.1 už ani tato vlastnost oficiálně není v základním režimu povolena, tuto vlastnost nemá smysl hledat ani v OpenGL ES 1.x/ES 2.x apod., což je možná ještě důležitější, především pro vývojáře aplikací pro tablety apod.).
- Normálový vektor ve vrcholu, který se nastavuje metodouGL.glNormal*(). Normála (tj. vektor kolmý k povrchu) vrcholu je sice z matematického hlediska nesmysl, ale v počítačové grafice se normály rovin, které se ve vrcholech stýkají, sečítají a výsledek se používá například při výpočtech osvětlení.
- Souřadnice do textury, která se nastavuje metodouGL.glTexCoord(). Tato souřadnice se používá při texturování, kterým se budeme podrobněji zabývat v následujících kapitolách.
- Mapovací souřadnice zadávaná metodouGL.glEvalCoord*().
- Jednorozměrný či dvourozměrný bod v síti (mesh), který se zadává metodou GL.glEvalPoint*().
- Optické vlastnosti materiálu zadané metodouGL.glMaterial().
- Vlastnosti hrany (tj. zda má být hrana viditelná či nikoliv) nastavované metodou GL.glEdgeFlag().
2. Pole vrcholů
Vlastnosti jednotlivých vrcholů se v nejjednodušším případě zadávají přímým voláním jednoduchých metod, například GL.glVertex*(), GL.glColor*(), GL.glNormal*() a glGL.Material*(). Toto řešení je snadno pochopitelné i použitelné, jeho obrovskou nevýhodou však je, že pro vykreslení složitých prostorových scén by se muselo těchto funkcí volat několik tisíc i desítek tisíc (podle složitosti těchto scén, tj. počtu vrcholů).
Volání každé funkce s sebou nese poměrně značné množství práce pro procesor, zejména přesuny dat mezi registry procesoru, pamětí a zásobníkem a také skok do funkce a návrat z funkce (snížení účinnosti cache paměti z hlediska lokálnosti odkazů a přepínání mezi ringy), nemluvě již o nutnosti používat JNI při volání těchto nativních funkcí z Javy. Proto by bylo vhodné co nejvíce snížit množství volání funkcí nutných pro vykreslení dané scény. Existuje více možností vedoucích ke snížení počtu volaných funkcí, které se liší svou složitostí, vyjadřovací schopností (tj. které grafické efekty lze popř. naopak nelze vytvořit) a rychlostí vlastního vykreslení:
- První možností, která je pro použití nejjednodušší, je využití faktu, že se OpenGL chová jako stavový automat, tj. pokud zadáme nějakou vlastnost, zůstane tato vlastnost nastavena až do doby, než ji změníme.
- Druhou možností je použití těch grafických primitiv, u kterých se redukuje počet přenesených vrcholů. Jedná se o již popsané trsy a pruhy trojúhelníků a o pruh čtyřúhelníků (triangle fan, triangle strip, quad strip). Redukce přenesených vrcholů spočívá v tom, že vrcholy společné více hranám (a tím pádem i ploškám) jsou přeneseny pouze jednou.
- Třetí možností je použití display listů, kdy se příkazy nutné pro vytvoření tělesa uloží do display listu a ten se poté zavolá pomocí jediné funkce. Předností tohoto postupu je, že data uložená v display listu jsou v ideálním případě zapsána přímo do paměti grafického akcelerátoru a při každém vykreslení tedy není nutné tato data přenášet po sběrnici, což je v dnešní době (se současnými sběrnicemi a grafickými akcelerátory) nejvíce zpomalující operace při vykreslování trojrozměrných scén.
- Čtvrtou možností, kterou se dnes budeme podrobněji zabývat, je použití takzvaného pole vrcholů (vertex arrays), kdy jsou data pro jednotlivé vrcholy uložena ve vhodně organizovaném poli a následně je umožněno poslání dat vrcholů pomocí několika málo funkčních volání. Největší předností tohoto přístupu je, že se mezi jednotlivými snímky mohou měnit data jednotlivých vrcholů (barva, poloha apod.), což v případě display listů nebylo možné, protože by se při jakékoliv změně musel vytvořit nový display list.
- Předchozí možnost lze dále vylepšit použitím Vertex Buffer Object (VBO) typu GL_STATIC_DRAW, což jsou data ukládaná přímo do paměti grafického akcelerátoru bez možnosti je někdy měnit (což s sebou může přinášet jak výhody, tak i nevýhody).
Při vývoji nových aplikací je taktéž nutné mít na paměti, že v OpenGL verze 3.0 už je vykreslování s využitím „příkazových závorek“ glBegin a glEnd označeno jako deprecated a od OpenGL 3.1 už nemusí být tyto vlastnosti v základní variantě OpenGL vůbec podporovány. Jedná se samozřejmě o reakci na směr vývoje moderních grafických akcelerátorů a GPU, kde se namísto toho používá funkcí glDrawArrays, glDrawArraysInstanced, glDrawElements atd.
3. Vytvoření pole vrcholů
Před prvním použitím pole vrcholů v programu je zapotřebí použití pole vrcholů povolit zavoláním metody GL.glEnableClientState(array), kde parametr array může podle potřeby nabývat několika hodnot:
- GL.GL_COLOR_ARRAY – v poli je možné mít uloženy RGB barvy vrcholů (používá se v True-color RGB barevném režimu).
- GL.GL_INDEX_ARRAY – v poli je možné mít uloženy indexové barvy vykreslování (používá se paletovém barevném režimu, již většinou nepodporováno).
- GL.GL_EDGE_FLAG_ARRAY – v poli je možné mít uloženy příznaky hrany, tj. zda má být vykreslovaná hrana viditelná.
- GL.GL_NORMAL_ARRAY – v poli je možné mít uloženy normálové vektory ve vrcholech.
- GL.GL_TEXTURE_COORD_ARRAY – v poli je možné mít uloženy souřadnice do textury.
- GL.GL_VERTEX_ARRAY – v poli je možné mít uloženy souřadnice vrcholů v rovině či prostoru.
Pokud některé z těchto polí nebudeme chtít použít, je možné zavolat metodu GL.glDisableClientState(array), kde parametr array nabývá stejných hodnot jako u předchozí funkce. Stav povolení či zakázání některého typu pole lze zjistit voláním metody GL.glIsEnabled().
4. ByteBuffer versus pole v Javě
Druhým krokem je vlastní vytvoření pole vrcholů. Toto pole se musí naplnit relevantními daty a poté předat knihovně OpenGL ukazatel na toto pole. To se v případě nativního rozhraní OpenGL provede příkazem gl*Pointer(), kde se za znak * doplní typ pole. To znamená, že podle typu uložených dat existují funkce:
glVertexPointer(),
glColorPointer(),
glIndexPointer(),
glNormalPointer(),
glTexCoordPointer() a
glEdgeFlagPointer().
V případě použití knihovny SDLJava však samozřejmě není možné specifikovat ukazatel na pole a dokonce pro uložení dat o jednotlivých vrcholech nelze použít javovská pole, a to z toho důvodu, že tato pole mohou být kdykoli v paměti přemístěna při spuštění správce paměti. Z tohoto důvodu používá knihovna SDLJava třídu java.nio.ByteBuffer, protože zde je při alokaci zaručeno, že se vytvořený buffer (interně pole) nebude přesouvat při zavolání GC. To znamená, že předchozí céčkové funkce gl*Pointer() se při použití knihovny SDLJava sice stále jmenují GL.gl*Pointer, ale posledním parametrem těchto metod je ve skutečnosti reference na ByteBuffer!
5. Výběr prvků z pole/bufferu a zobrazení jednotlivých vrcholů
Po povolení použití polí vrcholů a naplnění těchto polí je zapotřebí vhodným způsobem vybrat prvky z pole a vykreslit jednotlivé vrcholy. Je nutné si uvědomit, že pole zůstává uloženo na straně klienta (v případě PC v paměti procesoru) do té doby, dokud poprvé nepřistoupíme k jeho prvkům. Poté jsou pole přenesena na server (v případě PC na grafický akcelerátor, ale zde hodně záleží na použitém ovladači a velikosti volné paměti na grafickém akcelerátoru). Změníme-li později hodnoty v poli, musíme si vynutit zaslání nového pole na server.
Prvky pole lze vybrat několik různými způsoby:
- Po jednotlivých vrcholech metodou GL.glArrayElement(int i), která vybere data z i-tého prvku ze všech zapnutých polí. Zavolání této metody má stejný efekt jako volání metody GL.glVertex(), GL.glColor() atd. Tento způsob se jako jediný používá mezi příkazovými závorkami glBegin() a glEnd() a tudíž už nemusí být v případě OpenGL 3.1 a vyšší vždy podporovaný.
- Zadáním seznamu několika vrcholů pomocí metodyGL.glDrawElements(mode, count, type, buffer). Tato metoda vybere count vrcholů ze všech zapnutých polí, přičemž posloupnost indexů vybíraných vrcholů je uložena v bufferu indices. Typ tohoto pole pak určuje parametr type, který může nabývat hodnot GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT nebo GL_UNSIGNED_INT. Parametr mode určuje, jaká geometrická primitiva mají být z vybíraných vrcholů konstruována (GL_TRIANGLES, GL_POLYGON atd., tedy stejné jako parametr funkce glBegin()).
- V novějších verzích OpenGL lze využít i podobnou funkciglDrawElementsInstanced díky níž lze jedno pole použít pro několik těles.
- Zadáním seznamu několika po sobě jdoucích vrcholů pomocí metodyGL.glDrawArrays(mode, first, count). Tato funkce vybere count vrcholů, které jsou uloženy v poli za sebou. Index prvního vrcholu je určen parametrem first.
- I pro glDrawArrays existuje v novějších verzích OpenGL alternativní funkce glDrawArraysInstanced použitelná pro vykreslení několika těles uložených v jednom poli.
6. Demonstrační příklad SDLTest17 – použití metody glArrayElement()
V dnešním prvním demonstračním příkladu nazvaném SDLTest17 je ukázán způsob vytvoření polí typu GL_VERTEX_ARRAY a GL_COLOR_ARRAY a následné vykreslení dvou úseček s použitím metody glArrayElement(). Povšimněte si především způsobu alokace obou bufferů i metod, pomocí nichž se do objektu typu ByteBuffer mohou ukládat data jiného datového typu. Pro konstrukci bufferu je nutné použít metodu ByteBuffer.allocateDirect(velikost) s následným voláním metody ByteBuffer.order(ByteOrder.nativeOrder(), díky níž je zajištěno uložení bajtů v bufferu takovým způsobem, který je na použité platformě nativní (little endian či big endian). Pokud by se tato metoda nezavolala, ukládaly by se bajty hodnot typu short/int/float/long/double v opačném pořadí, než je na platformě x86 očekáváno a vykreslení scény by nebylo korektní!
import java.nio.ByteBuffer; import java.nio.ByteOrder; import sdljava.SDLMain; import sdljava.SDLException; import sdljava.video.SDLPixelFormat; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import org.gljava.opengl.GL; /** * Sedmnacty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Priklad vyuziti metody Gl.glArrayElement() pro vykreslovani * komplikovanejsich objektu a teles. * * @author Pavel Tisnovsky */ public class SDLTest17 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 200; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 200; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Buffer obsahujici vrcholy usecek. */ private static ByteBuffer verticesBuffer = null; /** * Buffer obsahujici barvy vrcholu usecek. */ private static ByteBuffer colorsBuffer = null; /* Inicializace bufferu */ static { /* 8 souradnic * 4 bajtu/int = 32 bajtu */ /* je nutne zachovat nativni usporadani bajtu ve slovech! */ verticesBuffer = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()); verticesBuffer.putInt(10); verticesBuffer.putInt(10); verticesBuffer.putInt(190); verticesBuffer.putInt(190); verticesBuffer.putInt(10); verticesBuffer.putInt(190); verticesBuffer.putInt(190); verticesBuffer.putInt(10); verticesBuffer.flip(); /* 12 barvovych slozek * 4 bajtu/float = 48 bajtu */ /* je nutne zachovat nativni usporadani bajtu ve slovech! */ colorsBuffer = ByteBuffer.allocateDirect(48).order(ByteOrder.nativeOrder()); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.flip(); } /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_OPENGL | SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Nastaveni transformacnich matic a barvy pozadi framebufferu. */ private static void initScene(GL gl, int width, int height) { // nastaveni projekcni matice gl.glMatrixMode(gl.GL_PROJECTION); // jednotkova matice gl.glLoadIdentity(); // mapovani abstraktnich souradnic do souradnic okna ci cele obrazovky gl.glOrtho(0, width, 0, height, -1, 1); // nastaveni modelview matice gl.glMatrixMode(gl.GL_MODELVIEW); gl.glLoadIdentity(); // viditelna oblast gl.glViewport(0, 0, width, height); // vypnuti pameti hloubky pri vykreslovani gl.glDisable(gl.GL_DEPTH_TEST); // barva pozadi framebufferu pro volani glClear() gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f); } /** * Vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL. */ private static void drawScene(GL gl) { // vymazani framebufferu gl.glClear(gl.GL_COLOR_BUFFER_BIT); gl.glEnable(gl.GL_LINE_SMOOTH); // povoleni antialiasingu usecek gl.glEnableClientState(gl.GL_VERTEX_ARRAY); // povoleni pouziti pole vrcholu gl.glEnableClientState(gl.GL_COLOR_ARRAY); // povoleni pouziti pole barev gl.glVertexPointer(2, gl.GL_INT, 0, verticesBuffer); // naplneni pole vrcholu gl.glColorPointer(3, gl.GL_FLOAT, 0, colorsBuffer); // naplneni pole barev gl.glBegin(gl.GL_LINES); for (int i=0; i<4; i++) { // vykreslit vsechny vrcholy gl.glArrayElement(i); // (vcetne zmeny barvy) } gl.glEnd(); gl.glFlush(); // provedeni a vykresleni vsech zmen } /** * Spusteni osmeho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace video subsystemu knihovny SDL SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani SDLSurface screen = initVideo(); // inicializace celoobrazovkoveho grafickeho rezimu GL gl = screen.getGL(); // nastaveni transformacnich matic a barvy pozadi framebufferu initScene(gl, GFX_WIDTH, GFX_HEIGHT); // vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL drawScene(gl); // prepnuti predniho a zadniho bufferu screen.glSwapBuffers(); // ukonceni cele aplikace po peti sekundach Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } // zobrazit ziskane informace } }
Skript pro překlad na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest17.java
Dávkový soubor pro překlad na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest17.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest17
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest17
Obrázek 1: Scéna vykreslená po spuštění demonstračního příkladu SDLTest17.
7. Demonstrační příklad SDLTest18 – použití metody glDrawArrays()
Demonstrační příklad SDLTest18 se v mnoha ohledech podobá předchozímu demonstračnímu příkladu, ovšem namísto volání metody GL.glArrayElement() se zde používá mnohem efektivnějšího způsobu – metody GL.glDrawArrays(), která jediným zavoláním dokáže vykreslit i složitou scénu. Této metodě se předávají tři parametry – typ vykreslovaných grafických primitiv (body, úsečky, trojúhelníky …), počáteční index v polích, od kterého se má zahájit čtení (0=první prvky v polích) a konečně počet prvků polí nutných pro získání všech dat pro vykreslované grafické primitivum (konstanta 4 znamená, že se přečtou souřadnice a barvy čtyř vrcholů, z nichž se vytvoří dvě úsečky, stejně, jako kdyby se čtyřikrát zavolala metoda glVertex*):
import java.nio.ByteBuffer; import java.nio.ByteOrder; import sdljava.SDLMain; import sdljava.SDLException; import sdljava.video.SDLPixelFormat; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import org.gljava.opengl.GL; /** * Osmnacty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Priklad vyuziti metody Gl.glDrawArrays() pro vykreslovani * komplikovanejsich objektu a teles. * * @author Pavel Tisnovsky */ public class SDLTest18 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 200; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 200; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Buffer obsahujici vrcholy usecek. */ private static ByteBuffer verticesBuffer = null; /** * Buffer obsahujici barvy vrcholu usecek. */ private static ByteBuffer colorsBuffer = null; /* Inicializace bufferu */ static { /* 8 souradnic * 4 bajtu/int = 32 bajtu */ /* je nutne zachovat nativni usporadani bajtu ve slovech! */ verticesBuffer = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()); verticesBuffer.putInt(10); verticesBuffer.putInt(10); verticesBuffer.putInt(190); verticesBuffer.putInt(190); verticesBuffer.putInt(10); verticesBuffer.putInt(190); verticesBuffer.putInt(190); verticesBuffer.putInt(10); verticesBuffer.flip(); /* 12 barvovych slozek * 4 bajtu/float = 48 bajtu */ /* je nutne zachovat nativni usporadani bajtu ve slovech! */ colorsBuffer = ByteBuffer.allocateDirect(48).order(ByteOrder.nativeOrder()); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(0.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.putFloat(1.0f); colorsBuffer.flip(); } /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_OPENGL | SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Nastaveni transformacnich matic a barvy pozadi framebufferu. */ private static void initScene(GL gl, int width, int height) { // nastaveni projekcni matice gl.glMatrixMode(gl.GL_PROJECTION); // jednotkova matice gl.glLoadIdentity(); // mapovani abstraktnich souradnic do souradnic okna ci cele obrazovky gl.glOrtho(0, width, 0, height, -1, 1); // nastaveni modelview matice gl.glMatrixMode(gl.GL_MODELVIEW); gl.glLoadIdentity(); // viditelna oblast gl.glViewport(0, 0, width, height); // vypnuti pameti hloubky pri vykreslovani gl.glDisable(gl.GL_DEPTH_TEST); // barva pozadi framebufferu pro volani glClear() gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f); } /** * Vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL. */ private static void drawScene(GL gl) { // vymazani framebufferu gl.glClear(gl.GL_COLOR_BUFFER_BIT); gl.glEnable(gl.GL_LINE_SMOOTH); // povoleni antialiasingu usecek gl.glEnableClientState(gl.GL_VERTEX_ARRAY); // povoleni pouziti pole vrcholu gl.glEnableClientState(gl.GL_COLOR_ARRAY); // povoleni pouziti pole barev gl.glVertexPointer(2, gl.GL_INT, 0, verticesBuffer); // naplneni pole vrcholu gl.glColorPointer(3, gl.GL_FLOAT, 0, colorsBuffer); // naplneni pole barev gl.glDrawArrays(gl.GL_LINES, 0, 4); // vykreslit vsechny vrcholy // (vcetne zmeny barvy) gl.glFlush(); // provedeni a vykresleni vsech zmen } /** * Spusteni osmeho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace video subsystemu knihovny SDL SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani SDLSurface screen = initVideo(); // inicializace celoobrazovkoveho grafickeho rezimu GL gl = screen.getGL(); // nastaveni transformacnich matic a barvy pozadi framebufferu initScene(gl, GFX_WIDTH, GFX_HEIGHT); // vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL drawScene(gl); // prepnuti predniho a zadniho bufferu screen.glSwapBuffers(); // ukonceni cele aplikace po peti sekundach Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } // zobrazit ziskane informace } }
Skript pro překlad na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest18.java
Dávkový soubor pro překlad na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest18.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest18
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest18
Obrázek 1: Scéna vykreslená po spuštění demonstračního příkladu SDLTest18.
8. Objekt typu Sprite v knihovně SDLJava
Knihovna OpenGL a tím pádem i SDLJava samozřejmě umožňuje nanášet na vykreslované plochy rastrové textury. Velkou výhodou rastrových textur je jejich snadná implementace ve vykreslovacím řetězci. Jednoduché grafické akcelerátory používané v minulosti řešily texturování takovým způsobem, že se do jejich vykreslovacího řetězce přidala vyrovnávací paměť pro textury a několik interpolátorů, pomocí kterých se zajistil přístup do texturovací paměti. Dnešní moderní grafické akcelerátory a GPU jdou mnohem dál: textury je možné komprimovat, jsou podporovány mipmapy (textury ve více rozlišeních), antialiasing, multitextury apod. Texturovací jednotka však stále patří k těm částem vizuálního systému, která má velmi dobrý poměr složitost/vizuální efekt.
Vzhledem k tomu, že se knihovna SDL a tím pádem i SDLJava často používá pro tvorbu počítačových her (ať již s 2D či 3D scénami), byla do balíčku org.gljava.opengl přidána třída nazvaná Sprite, kterou lze využít pro načtení bitmapy z externího souboru a následné vykreslení této bitmapy jednoduchou operací Sprite.draw(GL, x, y), kde GL je kontext OpenGL a x a y jsou pozice spritu ve scéně. Samotná knihovna SDLJava zajistí převod spritu na formát textury a vykreslení objektu (čtverce či obdélníku) pokrytého touto texturou, což je mnohem jednodušší, než běžný postup při texturování v OpenGL.
Mezi metody, které třída Sprite programátorům nabízí, patří:
# | Metoda | Význam |
---|---|---|
1 | Sprite(Texture t) | vytvoření spritu z textury (bude popsáno příště) |
2 | Sprite(GL gl, java.net.URL url) | načtení spritu z rastrového obrázku |
3 | Sprite(GL gl, java.lang.String texturePath) | načtení spritu z rastrového obrázku |
4 | draw(GL gl, float x, float y) | vykreslení spritu |
5 | getWidth() | získání šířky spritu v pixelech |
6 | getHeight() | získání výšky spritu v pixelech |
Pokud je zapotřebí sprite při vykreslování natočit, lze tuto operaci provést jen nepřímo a to natočením celého souřadného systému. Posléze je většinou nutné obnovit původní transformační matici naprosto stejným způsobem, jaký se provádí například při vykreslení natočeného či zvětšeného/zmenšeného tělesa.
9. Demonstrační příklad SDLTest19 – texturování a objekt typu Sprite
V dnešním posledním demonstračním příkladu SDLTest19 je ukázán způsob načtení spritu z rastrového obrázku (opět je použita již několikrát zobrazená obrazovka hry Xscorch) s následným vykreslením tohoto spritu. Nejprve je sprite vykreslen v takovém souřadném systému, že jsou jeho hrany rovnoběžné s hranami okna, podruhé je sprite otočen o 45 stupňů, přesněji řečeno souřadný systém je před vykreslením spritu otočen o tuto hodnotu (což je ve výsledku to samé):
import java.nio.ByteBuffer; import java.nio.ByteOrder; import sdljava.SDLMain; import sdljava.SDLException; import sdljava.video.SDLPixelFormat; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import org.gljava.opengl.GL; import org.gljava.opengl.Sprite; /** * Devatenacty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Texturovani a objekt typu Sprite. * * @author Pavel Tisnovsky */ public class SDLTest19 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 200; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 200; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_OPENGL | SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Nastaveni transformacnich matic a barvy pozadi framebufferu. */ private static void initScene(GL gl, int width, int height) { // nastaveni projekcni matice gl.glMatrixMode(gl.GL_PROJECTION); // jednotkova matice gl.glLoadIdentity(); // mapovani abstraktnich souradnic do souradnic okna ci cele obrazovky gl.glOrtho(0, width, height, 0, -1, 1); // nastaveni modelview matice gl.glMatrixMode(gl.GL_MODELVIEW); gl.glLoadIdentity(); // viditelna oblast gl.glViewport(0, 0, width, height); // vypnuti pameti hloubky pri vykreslovani gl.glDisable(gl.GL_DEPTH_TEST); // povoleni prace s texturami gl.glEnable(gl.GL_TEXTURE_2D); // barva pozadi framebufferu pro volani glClear() gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f); } /** * Vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL. */ private static void drawScene(GL gl, Sprite sprite) { // vymazani framebufferu gl.glClear(gl.GL_COLOR_BUFFER_BIT); // vykresleni spritu sprite.draw(gl, 200, 400); // otoceni souradneho systemu gl.glMatrixMode(gl.GL_MODELVIEW); gl.glRotatef(45.0f, 0.0f, 0.0f, 1.0f); // vykresleni druheho spritu sprite.draw(gl, 200, 0); gl.glFlush(); // provedeni a vykresleni vsech zmen } /** * Spusteni osmeho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace video subsystemu knihovny SDL SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani SDLSurface screen = initVideo(); // inicializace celoobrazovkoveho grafickeho rezimu GL gl = screen.getGL(); // nastaveni transformacnich matic a barvy pozadi framebufferu initScene(gl, GFX_WIDTH, GFX_HEIGHT); // nacteni spritu z rastroveho obrazku Sprite sprite = new Sprite(gl, "xscorch.bmp"); // vykresleni velmi jednoduche sceny s vyuzitim funkci OpenGL drawScene(gl, sprite); // prepnuti predniho a zadniho bufferu screen.glSwapBuffers(); // ukonceni cele aplikace po peti sekundach Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } // zobrazit ziskane informace } }
Skript pro překlad na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest19.java
Dávkový soubor pro překlad na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest19.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest19
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest19
Obrázek 1: Scéna vykreslená po spuštění demonstračního příkladu SDLTest19.
10. Repositář se zdrojovými kódy všech tří dnešních demonstračních příkladů
Všechny tři dnes popsané demonstrační příklady byly společně s podpůrnými skripty určenými pro jejich překlad a následné spuštění uloženo do Mercurial repositáře dostupného na adrese http://icedtea.classpath.org/people/ptisnovs/jvm-tools/. Podobně jako tomu bylo i v předchozích čtyřech dílech tohoto seriálu, i ke dnešním příkladům jsou přiloženy skripty využitelné pro jejich překlad a spuštění. Navíc byly přidány i skripty využitelné ve Windows:
11. Odkazy na Internetu
- glDrawArrays
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArrays.xml - glDrawElements
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElements.xml - glDrawArraysInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArraysInstanced.xml - glDrawElementsInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElementsInstanced.xml - Root.cz: Seriál Grafická knihovna OpenGL
http://www.root.cz/serialy/graficka-knihovna-opengl/ - Root.cz: Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
http://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/ - Best Practices for Working with Vertex Data
https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html - SDL 1.2 Documentation: SDL_Surface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html - SDL 1.2 Documentation: SDL_PixelFormat
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html - SDL 1.2 Documentation: SDL_LockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html - SDL 1.2 Documentation: SDL_UnlockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html - SDL 1.2 Documentation: SDL_LoadBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html - SDL 1.2 Documentation: SDL_SaveBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html - SDL 1.2 Documentation: SDL_BlitSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html - SDL 1.2 Documentation: SDL_VideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html - SDL 1.2 Documentation: SDL_GetVideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html - Class BufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html - Class Graphics
http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics.html - Double Buffering and Page Flipping
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html - BufferStrategy and BufferCapabilities
http://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html - Java:Tutorials:Double Buffering
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering - Double buffer in standard Java AWT
http://www.codeproject.com/Articles/2136/Double-buffer-in-standard-Java-AWT - Java 2D: Hardware Accelerating – Part 1 – Volatile Images
http://www.javalobby.org/forums/thread.jspa?threadID=16840&tstart=0 - Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
http://www.javalobby.org/java/forums/t16867.html - How does paintComponent work?
http://stackoverflow.com/questions/15544549/how-does-paintcomponent-work - A Swing Architecture Overview
http://www.oracle.com/technetwork/java/architecture-142923.html - Class javax.swing.JComponent
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html - Class java.awt.Component
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html - Class java.awt.Component.BltBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.BltBufferStrategy.html - Class java.awt.Component.FlipBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.FlipBufferStrategy.html - Metoda java.awt.Component.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#isDoubleBuffered() - Metoda javax.swing.JComponent.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#isDoubleBuffered() - Metoda javax.swing.JComponent.setDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setDoubleBuffered(boolean) - Javadoc – třída GraphicsDevice
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsDevice.html - Javadoc – třída GraphicsEnvironment
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsEnvironment.html - Javadoc – třída GraphicsConfiguration
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsConfiguration.html - Javadoc – třída DisplayMode
http://docs.oracle.com/javase/7/docs/api/java/awt/DisplayMode.html - Lesson: Full-Screen Exclusive Mode API
http://docs.oracle.com/javase/tutorial/extra/fullscreen/ - Full-Screen Exclusive Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html - Display Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/displaymode.html - Using the Full-Screen Exclusive Mode API in Java
http://www.developer.com/java/other/article.php/3609776/Using-the-Full-Screen-Exclusive-Mode-API-in-Java.htm - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html