Cerchiamo ora di capire come GameObject
realizza una mesh
definendone i triangoli, e come questi, con una proiezione parallela,
vengono visualizzati sul nostro schermo. OpenGL ES si aspetta
che i vertici dei triangoli, siano passati in forma di
buffer NIO, essenzialmente blocchi consecutivi di byte.
Usando i buffer NIO la memoria non viene allocata nell'heap
della Java Virtual Machine, ma direttamente nella memoria nativa. Per
costruire un buffer di byte NIO partiamo dal codice seguente:
ByteBuffer buffer = ByteBuffer.allocateDirect(NUMERO_DI_BYTES);
buffer.order(ByteOrder.nativeOrder());
Questo frammento di codice alloca una quantità di memoria in
byte pari a NUMERO_DI_BYTES
, e si assicura che
l'ordine sia lo stesso utilizzato dalla CPU sottostante.
Un buffer NIO ha 3 attributi:
- Capacity: il numero totale di elementi del buffer;
- Position: la posizione corrente del prossimo elemento da leggere o scrivere;
- Limit: l'indice dell'ultimo elemento definito, incrementato di 1.
Dal momento che le nostre coordinate per i vertici saranno di
tipo float
, abbiamo bisogno di ottenere, a partire
dal ByteBuffer
, un FloatBuffer
. Vediamo come.
Supponiamo di voler definire un buffer per rappresentare un singolo triangolo.
I vertici sono 3, ognuno dei quali è rappresentato da due float
(uno per la coordinata x ed uno per quella y).
Ogni triangolo in 2D è quindi composto da un totale di 6 float.
Dal momento che, in Java, un float
è rappresento con 4 byte,
il totale di byte necessario a rappresentare un triangolo è
pari a:
3 (vertici) × 2 (float) × 4 (byte per float) = 24 bytes
ByteBuffer buffer = ByteBuffer.allocateDirect(24);
buffer.order(ByteOrder.nativeOrder());
FloatBuffer vertices = byteBuffer.asFloatBuffer();
vertices.put(new float[]{
0.0f, 0.0f,
319.0f, 0.0f,
160.0f, 479.0f
});
vertices.flip();
Con questo codice, creiamo un ByteBuffer
di 24 byte,
e poi lo trasformiamo in un FloatBuffer
da 6 elementi float
.
Con il metodo put()
inseriamo nel buffer i valori x e y
di ciascun vertice del triangolo; inoltre, l'indice di position
diventa pari alla lunghezza dell'array di float
passato come argomento (nel nostro caso 6).
Con flip()
resettiamo la posizione al valore 0,
perchè vogliamo leggere dall'inizio del buffer.
Una volta inserito il nostro triangolo nel buffer, non resta che
disegnarlo:
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2,GL10.GL_FLOAT,0,vertices);
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
La prima istruzione è necessaria per abilitare la possibilità di
disegnare un array di vertici; la seconda istruzione, invece,
specifica dove essi sono memorizzati.
Il metodo glVertexPointer
accetta i seguenti parametri:
-
il primo specifica le dimensioni di ciascun vertice (nel
nostro caso 2: x ed y); -
il secondo rappresenta il tipo di queste coordinate.
La costanteGL10.GL_FLOAT
si utilizza per i
float
; -
il terzo indica di quanto sono distanziate le coordinate
di uno stesso vertice nel buffer. Poichè le abbiamo inserite
tutte in sequenza, il valore corretto sarà0
; - il quarto ed ultimo parametro è il buffer.
Il metodo glDrawArrays
disegna, infine, il triangolo.
Il primo parametro specifica infatti proprio l'intenzione di disegnare
questo tipo di primitiva, mentre il secondo è l'offset relativo al primo
vertice puntato. L'offset è misurato in numero di vertici;
partiamo quindi da 0, poichè abbiamo un solo triangolo. L'ultimo parametro,
infine, specifica il numero di vertici da disegnare.