Reputation: 595
I cannot find any information on the proper way to render interleaved vertex arrays (not VBOs) in JOGL. Specifically, the use of glVertexAttribPointer() seems to not allow the specification of an offset within the interleaved data (a FloatBuffer). In C, C++, or Objective-C, I know exactly how to do this, but in Java there seems to be no clear path to victory.
The only thing I have tried that looked like it might have worked was to set the position of the buffer before using glVertexAttribPointer(), but this had no effect.
For clarity, here is a sample of code in the rendering function:
gl.glEnableVertexAttribArray( POSITION_ATTRIB );
gl.glEnableVertexAttribArray( NORMAL_ATTRIB );
gl.glVertexAttribPointer( POSITION_ATTRIB, 4, GL.GL_FLOAT, false, 32, vertexBuffer );
// vertexBuffer.position( 4 ); if I try to specify an offset
gl.glVertexAttribPointer( NORMAL_ATTRIB, 4, GL.GL_FLOAT, false, 32, vertexBuffer );
gl.glDrawArrays( GL.GL_TRIANGLES, 0, nummy );
gl.glDisableVertexAttribArray( NORMAL_ATTRIB );
gl.glDisableVertexAttribArray( POSITION_ATTRIB );
I am using 4 floats for every attribute that I have. I chose a stride of 32 based on
4 bytes per float * 4 floats per attribute * 2 attributes per vertex.
Upvotes: 2
Views: 594
Reputation: 1973
I was a bit stuck, so sharing another solution using direct buffers, needed in OpenGL as mentioned by Nathan. Also, I'm interleaving floats with bytes/ints in this example (color is represented as 4 bytes):
// vertex position: 3 floats; vertex color: 4 bytes
static final int COORDS_PER_VERTEX = 4;
static final int VERTS_PER_SPRITE = 4;
static final int MAX_NUM_SPRITES = 1000;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private FloatBuffer vertexBuffer;
private ByteBuffer colorBuffer;
public void init() {
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
COORDS_PER_VERTEX * VERTS_PER_SPRITE * 4 * MAX_NUM_SPRITES);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
bb.position(3 * 4);
colorBuffer = bb.slice();
}
Now, when drawing your vertices, just do
public void draw() {
GLES20.glUseProgram(myProgram);
// Enable a handle to the sprite vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glEnableVertexAttribArray(mColorHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, 3,
GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
GLES20.glVertexAttribPointer(mColorHandle, 4,
GLES20.GL_UNSIGNED_BYTE, false, vertexStride, colorBuffer);
// Draw the sprites GL_SHORT DOES NOT WORK!!!! Use UNSIGNED_SHORT
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6 * numSprites, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
}
When I need to update the buffer, I prepare a float buffer only, and then I convert the 4 color bytes into a float,
private void updateSpriteBuffer() {
float[] vData = new float[COORDS_PER_VERTEX * VERTS_PER_SPRITE * MAX_NUM_SPRITES];
for (int i = 0; i<numSprites; i++) {
// ... x, y, z
byte[] colorBytes = sprite.getColorBytes(); // {r, g, b, 1}
float colorAsFloat = ByteBuffer.wrap(colorBytes).order(ByteOrder.nativeOrder()).getFloat();
for (int k = 0; k < 4; k++) {
int j = COORDS_PER_VERTEX * 4 * i + COORDS_PER_VERTEX * k;
vData[j] = x[k];
vData[j + 1] = y[k];
vData[j + 2] = z[k];
vData[j + 3] = colorAsFloat;
}
}
vertexBuffer.put(vData);
vertexBuffer.position(0);
}
I hope this helps someone else trying to do the same.
Upvotes: 1
Reputation: 43389
I suspect the reason setting the position of your FloatBuffer does not give you the behavior you want is because you pass your FloatBuffer by reference. You might want to create another FloatBuffer that is basically just a different view of the same data (e.g. wrap a float []) and set the position in this other FloatBuffer to 4...
For example:
// Somewhere in your class
private float [] verts;
// You can fill this with actual data yourself, I did the hard part :P
verts = new float [] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
// ^^^^^^^^^ POS ^^^^^^^^ ^^^^^^^ NORMAL ^^^^^^^
//
// When it comes time to supply the vertex pointers, use this:
//
FloatBuffer vtx_base = FloatBuffer.wrap (verts);
FloatBuffer norm_base = FloatBuffer.wrap (verts, 4, verts.length () - 4);
gl.glVertexAttribPointer (POSITION_ATTRIB, 4, GL.GL_FLOAT, false, 32, vtx_base);
gl.glVertexAttribPointer (NORMAL_ATTRIB, 4, GL.GL_FLOAT, false, 32, norm_base);
DISCLAIMER: I have not used Java in years, so don't expect this to compile out of the box. The basic theory should be sound though.
I do have to wonder if you really need 4D vertex positions and normals, though? There are very real uses for w
, but if you don't actually need it save some storage / bandwidth and use 3D. I bring this up because when you use client memory instead of VBOs, there is a lot more CPU->GPU memory transfer going on and every bit of size reduction will help.
Upvotes: 2