package com.jogamp.opengl.util; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES1; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLPointerFunc; import jogamp.opengl.Debug; import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.Platform; import com.jogamp.opengl.util.glsl.ShaderState; /** *

* Immediate mode sink, implementing OpenGL fixed function subset of immediate mode operations, i.e. *

 *   glBegin();
 *     glVertex3f(1f, 1f, 1f);
 *     glColor4f(1f, 1f, 1f, 1f);
 *     ...
 *   glEnd();
 * 
* Implementation buffers all vertex, colors, normal and texture-coord elements in their respective buffers * to be either rendered directly via {@link #glEnd(GL)} or to be added to an internal display list * via {@link #glEnd(GL, boolean) glEnd(gl, false)} for deferred rendering via {@link #draw(GL, boolean)}. *

*
Buffer storage and it's creation via {@link #createFixed(int, int, int, int, int, int, int, int, int, int) createFixed(..)} * and {@link #createGLSL(int, int, int, int, int, int, int, int, int, int, ShaderState) createGLSL(..)}
*

* If unsure whether colors, normals and textures will be used, * simply add them with an expected component count. * This implementation will only render buffers which are being filled.
* The buffer growing implementation will only grow the exceeded buffers, unused buffers are not resized. *

*

* Note: Optional types, i.e. color, must be either not used or used w/ the same element count as vertex, etc. * This is a semantic constraint, same as in the original OpenGL spec. *

*/ public class ImmModeSink { protected static final boolean DEBUG_BEGIN_END; protected static final boolean DEBUG_DRAW; protected static final boolean DEBUG_BUFFER; static { Debug.initSingleton(); DEBUG_BEGIN_END = Debug.isPropertyDefined("jogl.debug.ImmModeSink.BeginEnd", true); DEBUG_DRAW = Debug.isPropertyDefined("jogl.debug.ImmModeSink.Draw", true); DEBUG_BUFFER = Debug.isPropertyDefined("jogl.debug.ImmModeSink.Buffer", true); } public static final int GL_QUADS = 0x0007; // Needs data manipulation on ES1/ES2 public static final int GL_QUAD_STRIP = 0x0008; public static final int GL_POLYGON = 0x0009; /** * Uses a GL2ES1, or ES2 fixed function emulation immediate mode sink *

* See buffer storage details. *

* * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. * @param vComps mandatory vertex component count, should be 2, 3 or 4. * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} * @param cComps optional color component count, may be 0, 3 or 4 * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} * @param nComps optional normal component count, may be 0, 3 or 4 * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} * @param glBufferUsage VBO usage parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, * set to 0 for no VBO usage */ public static ImmModeSink createFixed(int initialElementCount, int vComps, int vDataType, int cComps, int cDataType, int nComps, int nDataType, int tComps, int tDataType, int glBufferUsage) { return new ImmModeSink(initialElementCount, vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, false, glBufferUsage, null, 0); } /** * Uses a GL2ES2 GLSL shader immediate mode sink, utilizing the given ShaderState. *

* See buffer storage details. *

* * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. * @param vComps mandatory vertex component count, should be 2, 3 or 4. * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} * @param cComps optional color component count, may be 0, 3 or 4 * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} * @param nComps optional normal component count, may be 0, 3 or 4 * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} * @param glBufferUsage VBO usage parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, * set to 0 for no VBO usage * @param st ShaderState to locate the vertex attributes * @see #draw(GL, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public static ImmModeSink createGLSL(int initialElementCount, int vComps, int vDataType, int cComps, int cDataType, int nComps, int nDataType, int tComps, int tDataType, int glBufferUsage, ShaderState st) { return new ImmModeSink(initialElementCount, vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, true, glBufferUsage, st, 0); } /** * Uses a GL2ES2 GLSL shader immediate mode sink, utilizing the given shader-program. *

* See buffer storage details. *

* * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. * @param vComps mandatory vertex component count, should be 2, 3 or 4. * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} * @param cComps optional color component count, may be 0, 3 or 4 * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} * @param nComps optional normal component count, may be 0, 3 or 4 * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} * @param glBufferUsage VBO usage parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, * set to 0 for no VBO usage * @param shaderProgram shader-program name to locate the vertex attributes * @see #draw(GL, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public static ImmModeSink createGLSL(int initialElementCount, int vComps, int vDataType, int cComps, int cDataType, int nComps, int nDataType, int tComps, int tDataType, int glBufferUsage, int shaderProgram) { return new ImmModeSink(initialElementCount, vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, true, glBufferUsage, null, shaderProgram); } public void destroy(GL gl) { destroyList(gl); vboSet.destroy(gl); } public void reset() { reset(null); } public void reset(GL gl) { destroyList(gl); vboSet.reset(gl); } @Override public String toString() { StringBuilder sb = new StringBuilder("ImmModeSink["); sb.append(",\n\tVBO list: "+vboSetList.size()+" ["); for(Iterator i=vboSetList.iterator(); i.hasNext() ; ) { sb.append("\n\t"); sb.append( i.next() ); } if(vboSetList.size()>0) { sb.append("\n\t],\nVBO current: NOP]"); } else { sb.append("\n\t],\nVBO current: \n"); sb.append(vboSet); sb.append("\n]"); } return sb.toString(); } public void draw(GL gl, boolean disableBufferAfterDraw) { if(DEBUG_DRAW) { System.err.println("ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); } int n=0; for(int i=0; i "+vboSet.mode+")"); } vboSet.checkSeal(false); } public final void glEnd(GL gl) { glEnd(gl, null, true); } public void glEnd(GL gl, boolean immediateDraw) { glEnd(gl, null, immediateDraw); } public final void glEnd(GL gl, Buffer indices) { glEnd(gl, indices, true); } private void glEnd(GL gl, Buffer indices, boolean immediateDraw) { if(DEBUG_BEGIN_END) { System.err.println("ImmModeSink START glEnd(immediate: "+immediateDraw+")"); } if(immediateDraw) { vboSet.seal(gl, true); vboSet.draw(gl, indices, true, -1); reset(gl); } else { vboSet.seal(gl, true); vboSet.enableBuffer(gl, false); vboSetList.add(vboSet); vboSet = vboSet.regenerate(gl); } if(DEBUG_BEGIN_END) { System.err.println("ImmModeSink END glEnd(immediate: "+immediateDraw+")"); } } public void glVertexv(Buffer v) { vboSet.glVertexv(v); } public void glNormalv(Buffer v) { vboSet.glNormalv(v); } public void glColorv(Buffer v) { vboSet.glColorv(v); } public void glTexCoordv(Buffer v) { vboSet.glTexCoordv(v); } public final void glVertex2f(float x, float y) { vboSet.glVertex2f(x,y); } public final void glVertex3f(float x, float y, float z) { vboSet.glVertex3f(x,y,z); } public final void glNormal3f(float x, float y, float z) { vboSet.glNormal3f(x,y,z); } public final void glColor3f(float x, float y, float z) { vboSet.glColor3f(x,y,z); } public final void glColor4f(float x, float y, float z, float a) { vboSet.glColor4f(x,y,z, a); } public final void glTexCoord2f(float x, float y) { vboSet.glTexCoord2f(x,y); } public final void glTexCoord3f(float x, float y, float z) { vboSet.glTexCoord3f(x,y,z); } public final void glVertex2s(short x, short y) { vboSet.glVertex2s(x,y); } public final void glVertex3s(short x, short y, short z) { vboSet.glVertex3s(x,y,z); } public final void glNormal3s(short x, short y, short z) { vboSet.glNormal3s(x,y,z); } public final void glColor3s(short x, short y, short z) { vboSet.glColor3s(x,y,z); } public final void glColor4s(short x, short y, short z, short a) { vboSet.glColor4s(x,y,z,a); } public final void glTexCoord2s(short x, short y) { vboSet.glTexCoord2s(x,y); } public final void glTexCoord3s(short x, short y, short z) { vboSet.glTexCoord3s(x,y,z); } public final void glVertex2b(byte x, byte y) { vboSet.glVertex2b(x,y); } public final void glVertex3b(byte x, byte y, byte z) { vboSet.glVertex3b(x,y,z); } public final void glNormal3b(byte x, byte y, byte z) { vboSet.glNormal3b(x,y,z); } public final void glColor3b(byte x, byte y, byte z) { vboSet.glColor3b(x,y,z); } public final void glColor3ub(byte x, byte y, byte z) { vboSet.glColor3ub(x,y,z); } public final void glColor4b(byte x, byte y, byte z, byte a) { vboSet.glColor4b(x,y,z,a); } public final void glColor4ub(byte x, byte y, byte z, byte a) { vboSet.glColor4ub(x,y,z,a); } public final void glTexCoord2b(byte x, byte y) { vboSet.glTexCoord2b(x,y); } public final void glTexCoord3b(byte x, byte y, byte z) { vboSet.glTexCoord3b(x,y,z); } protected ImmModeSink(int initialElementCount, int vComps, int vDataType, int cComps, int cDataType, int nComps, int nDataType, int tComps, int tDataType, boolean useGLSL, int glBufferUsage, ShaderState st, int shaderProgram) { vboSet = new VBOSet(initialElementCount, vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, useGLSL, glBufferUsage, st, shaderProgram); this.vboSetList = new ArrayList(); } public boolean getUseVBO() { return vboSet.getUseVBO(); } /** * Returns the additional element count if buffer resize is required. * @see #setResizeElementCount(int) */ public int getResizeElementCount() { return vboSet.getResizeElementCount(); } /** * Sets the additional element count if buffer resize is required, * defaults to initialElementCount of factory method. * @see #createFixed(int, int, int, int, int, int, int, int, int, int) * @see #createGLSL(int, int, int, int, int, int, int, int, int, int, ShaderState) */ public void setResizeElementCount(int v) { vboSet.setResizeElementCount(v); } private void destroyList(GL gl) { for(int i=0; i vboSetList; protected static class VBOSet { protected VBOSet (int initialElementCount, int vComps, int vDataType, int cComps, int cDataType, int nComps, int nDataType, int tComps, int tDataType, boolean useGLSL, int glBufferUsage, ShaderState st, int shaderProgram) { // final .. this.glBufferUsage=glBufferUsage; this.initialElementCount=initialElementCount; this.useVBO = 0 != glBufferUsage; this.useGLSL=useGLSL; this.shaderState = st; this.shaderProgram = shaderProgram; if(useGLSL && null == shaderState && 0 == shaderProgram) { throw new IllegalArgumentException("Using GLSL but neither a valid shader-program nor ShaderState has been passed!"); } // variable .. this.resizeElementCount=initialElementCount; this.vDataType=vDataType; this.vDataTypeSigned=GLBuffers.isSignedGLType(vDataType); this.vComps=vComps; this.vCompsBytes=vComps * GLBuffers.sizeOfGLType(vDataType); this.cDataType=cDataType; this.cDataTypeSigned=GLBuffers.isSignedGLType(cDataType); this.cComps=cComps; this.cCompsBytes=cComps * GLBuffers.sizeOfGLType(cDataType); this.nDataType=nDataType; this.nDataTypeSigned=GLBuffers.isSignedGLType(nDataType); this.nComps=nComps; this.nCompsBytes=nComps * GLBuffers.sizeOfGLType(nDataType); this.tDataType=tDataType; this.tDataTypeSigned=GLBuffers.isSignedGLType(tDataType); this.tComps=tComps; this.tCompsBytes=tComps * GLBuffers.sizeOfGLType(tDataType); this.vboName = 0; this.vCount=0; this.cCount=0; this.nCount=0; this.tCount=0; this.vElems=0; this.cElems=0; this.nElems=0; this.tElems=0; this.pageSize = Platform.getMachineDescription().pageSizeInBytes(); reallocateBuffer(initialElementCount); rewind(); this.sealed=false; this.sealedGL=false; this.mode = 0; this.modeOrig = 0; this.bufferEnabled=false; this.bufferWritten=false; this.bufferWrittenOnce=false; this.glslLocationSet = false; } protected int getResizeElementCount() { return resizeElementCount; } protected void setResizeElementCount(int v) { resizeElementCount=v; } protected boolean getUseVBO() { return useVBO; } protected final VBOSet regenerate(GL gl) { return new VBOSet(initialElementCount, vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, useGLSL, glBufferUsage, shaderState, shaderProgram); } protected void checkSeal(boolean test) throws GLException { if(0==mode) { throw new GLException("No mode set yet, call glBegin(mode) first:\n\t"+this); } if(sealed!=test) { if(test) { throw new GLException("Not Sealed yet, call glEnd() first:\n\t"+this); } else { throw new GLException("Already Sealed, can't modify VBO after glEnd():\n\t"+this); } } } private boolean usingShaderProgram = false; protected void useShaderProgram(GL2ES2 gl, boolean force) { if( force || !usingShaderProgram ) { if(null != shaderState) { shaderState.useProgram(gl, true); } else /* if( 0 != shaderProgram) */ { gl.glUseProgram(shaderProgram); } usingShaderProgram = true; } } protected void draw(GL gl, Buffer indices, boolean disableBufferAfterDraw, int i) { enableBuffer(gl, true); if(null != shaderState || 0 != shaderProgram) { useShaderProgram(gl.getGL2ES2(), false); } if(DEBUG_DRAW) { System.err.println("ImmModeSink.draw["+i+"].0 (disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); } if (buffer!=null) { if(null==indices) { if ( GL_QUADS == mode && !gl.isGL2() ) { for (int j = 0; j < vElems - 3; j += 4) { gl.glDrawArrays(GL.GL_TRIANGLE_FAN, j, 4); } } else { gl.glDrawArrays(mode, 0, vElems); } } else { // FIXME: Impl. VBO usage .. or unroll. if( !gl.getContext().isCPUDataSourcingAvail() ) { throw new GLException("CPU data sourcing n/a w/ "+gl.getContext()); } final int type; if(indices instanceof ByteBuffer) { type = GL.GL_UNSIGNED_BYTE; } else if(indices instanceof ShortBuffer) { type = GL.GL_UNSIGNED_SHORT; } else if(indices instanceof IntBuffer) { type = GL.GL_UNSIGNED_INT; } else { throw new GLException("Given Buffer Class not supported: "+indices.getClass()+", should be ubyte, ushort or uint:\n\t"+this); } final int idxLen = indices.remaining(); final int idx0 = indices.position(); if ( GL_QUADS == mode && !gl.isGL2() ) { if( GL.GL_UNSIGNED_BYTE == type ) { final ByteBuffer b = (ByteBuffer) indices; for (int j = 0; j < idxLen; j++) { gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0x000000ff & b.get(idx0+j)), 4); } } else if( GL.GL_UNSIGNED_SHORT == type ){ final ShortBuffer b = (ShortBuffer) indices; for (int j = 0; j < idxLen; j++) { gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0x0000ffff & b.get(idx0+j)), 4); } } else { final IntBuffer b = (IntBuffer) indices; for (int j = 0; j < idxLen; j++) { gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0xffffffff & b.get(idx0+j)), 4); } } } else { ((GL2ES1)gl).glDrawElements(mode, idxLen, type, indices); // GL2: gl.glDrawRangeElements(mode, 0, idxLen-1, idxLen, type, indices); } } } if(disableBufferAfterDraw) { enableBuffer(gl, false); } if(DEBUG_DRAW) { System.err.println("ImmModeSink.draw["+i+"].X (disableBufferAfterDraw: "+disableBufferAfterDraw+")"); } } public void glVertexv(Buffer v) { checkSeal(false); Buffers.put(vertexArray, v); } public void glNormalv(Buffer v) { checkSeal(false); Buffers.put(normalArray, v); } public void glColorv(Buffer v) { checkSeal(false); Buffers.put(colorArray, v); } public void glTexCoordv(Buffer v) { checkSeal(false); Buffers.put(textCoordArray, v); } public void glVertex2b(byte x, byte y) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNb(vertexArray, vDataTypeSigned, x, true); if(vComps>1) Buffers.putNb(vertexArray, vDataTypeSigned, y, true); countAndPadding(VERTEX, vComps-2); } public void glVertex3b(byte x, byte y, byte z) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNb(vertexArray, vDataTypeSigned, x, true); if(vComps>1) Buffers.putNb(vertexArray, vDataTypeSigned, y, true); if(vComps>2) Buffers.putNb(vertexArray, vDataTypeSigned, z, true); countAndPadding(VERTEX, vComps-3); } public void glVertex2s(short x, short y) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNs(vertexArray, vDataTypeSigned, x, true); if(vComps>1) Buffers.putNs(vertexArray, vDataTypeSigned, y, true); countAndPadding(VERTEX, vComps-2); } public void glVertex3s(short x, short y, short z) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNs(vertexArray, vDataTypeSigned, x, true); if(vComps>1) Buffers.putNs(vertexArray, vDataTypeSigned, y, true); if(vComps>2) Buffers.putNs(vertexArray, vDataTypeSigned, z, true); countAndPadding(VERTEX, vComps-3); } public void glVertex2f(float x, float y) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNf(vertexArray, vDataTypeSigned, x); if(vComps>1) Buffers.putNf(vertexArray, vDataTypeSigned, y); countAndPadding(VERTEX, vComps-2); } public void glVertex3f(float x, float y, float z) { checkSeal(false); growBuffer(VERTEX); if(vComps>0) Buffers.putNf(vertexArray, vDataTypeSigned, x); if(vComps>1) Buffers.putNf(vertexArray, vDataTypeSigned, y); if(vComps>2) Buffers.putNf(vertexArray, vDataTypeSigned, z); countAndPadding(VERTEX, vComps-3); } public void glNormal3b(byte x, byte y, byte z) { checkSeal(false); growBuffer(NORMAL); if(nComps>0) Buffers.putNb(normalArray, nDataTypeSigned, x, true); if(nComps>1) Buffers.putNb(normalArray, nDataTypeSigned, y, true); if(nComps>2) Buffers.putNb(normalArray, nDataTypeSigned, z, true); countAndPadding(NORMAL, nComps-3); } public void glNormal3s(short x, short y, short z) { checkSeal(false); growBuffer(NORMAL); if(nComps>0) Buffers.putNs(normalArray, nDataTypeSigned, x, true); if(nComps>1) Buffers.putNs(normalArray, nDataTypeSigned, y, true); if(nComps>2) Buffers.putNs(normalArray, nDataTypeSigned, z, true); countAndPadding(NORMAL, nComps-3); } public void glNormal3f(float x, float y, float z) { checkSeal(false); growBuffer(NORMAL); if(nComps>0) Buffers.putNf(normalArray, nDataTypeSigned, x); if(nComps>1) Buffers.putNf(normalArray, nDataTypeSigned, y); if(nComps>2) Buffers.putNf(normalArray, nDataTypeSigned, z); countAndPadding(NORMAL, nComps-3); } public void glColor3b(byte r, byte g, byte b) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNb(colorArray, cDataTypeSigned, r, true); if(cComps>1) Buffers.putNb(colorArray, cDataTypeSigned, g, true); if(cComps>2) Buffers.putNb(colorArray, cDataTypeSigned, b, true); countAndPadding(COLOR, cComps-3); } public void glColor3ub(byte r, byte g, byte b) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNb(colorArray, cDataTypeSigned, r, false); if(cComps>1) Buffers.putNb(colorArray, cDataTypeSigned, g, false); if(cComps>2) Buffers.putNb(colorArray, cDataTypeSigned, b, false); countAndPadding(COLOR, cComps-3); } public void glColor4b(byte r, byte g, byte b, byte a) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNb(colorArray, cDataTypeSigned, r, true); if(cComps>1) Buffers.putNb(colorArray, cDataTypeSigned, g, true); if(cComps>2) Buffers.putNb(colorArray, cDataTypeSigned, b, true); if(cComps>3) Buffers.putNb(colorArray, cDataTypeSigned, a, true); countAndPadding(COLOR, cComps-4); } public void glColor4ub(byte r, byte g, byte b, byte a) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNb(colorArray, cDataTypeSigned, r, false); if(cComps>1) Buffers.putNb(colorArray, cDataTypeSigned, g, false); if(cComps>2) Buffers.putNb(colorArray, cDataTypeSigned, b, false); if(cComps>3) Buffers.putNb(colorArray, cDataTypeSigned, a, false); countAndPadding(COLOR, cComps-4); } public void glColor3s(short r, short g, short b) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNs(colorArray, cDataTypeSigned, r, true); if(cComps>1) Buffers.putNs(colorArray, cDataTypeSigned, g, true); if(cComps>2) Buffers.putNs(colorArray, cDataTypeSigned, b, true); countAndPadding(COLOR, cComps-3); } public void glColor4s(short r, short g, short b, short a) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNs(colorArray, cDataTypeSigned, r, true); if(cComps>1) Buffers.putNs(colorArray, cDataTypeSigned, g, true); if(cComps>2) Buffers.putNs(colorArray, cDataTypeSigned, b, true); if(cComps>3) Buffers.putNs(colorArray, cDataTypeSigned, a, true); countAndPadding(COLOR, cComps-4); } public void glColor3f(float r, float g, float b) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNf(colorArray, cDataTypeSigned, r); if(cComps>1) Buffers.putNf(colorArray, cDataTypeSigned, g); if(cComps>2) Buffers.putNf(colorArray, cDataTypeSigned, b); countAndPadding(COLOR, cComps-3); } public void glColor4f(float r, float g, float b, float a) { checkSeal(false); growBuffer(COLOR); if(cComps>0) Buffers.putNf(colorArray, cDataTypeSigned, r); if(cComps>1) Buffers.putNf(colorArray, cDataTypeSigned, g); if(cComps>2) Buffers.putNf(colorArray, cDataTypeSigned, b); if(cComps>3) Buffers.putNf(colorArray, cDataTypeSigned, a); countAndPadding(COLOR, cComps-4); } public void glTexCoord2b(byte x, byte y) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNb(textCoordArray, tDataTypeSigned, x, true); if(tComps>1) Buffers.putNb(textCoordArray, tDataTypeSigned, y, true); countAndPadding(TEXTCOORD, tComps-2); } public void glTexCoord3b(byte x, byte y, byte z) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNb(textCoordArray, tDataTypeSigned, x, true); if(tComps>1) Buffers.putNb(textCoordArray, tDataTypeSigned, y, true); if(tComps>2) Buffers.putNb(textCoordArray, tDataTypeSigned, z, true); countAndPadding(TEXTCOORD, tComps-3); } public void glTexCoord2s(short x, short y) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNs(textCoordArray, tDataTypeSigned, x, true); if(tComps>1) Buffers.putNs(textCoordArray, tDataTypeSigned, y, true); countAndPadding(TEXTCOORD, tComps-2); } public void glTexCoord3s(short x, short y, short z) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNs(textCoordArray, tDataTypeSigned, x, true); if(tComps>1) Buffers.putNs(textCoordArray, tDataTypeSigned, y, true); if(tComps>2) Buffers.putNs(textCoordArray, tDataTypeSigned, z, true); countAndPadding(TEXTCOORD, tComps-3); } public void glTexCoord2f(float x, float y) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNf(textCoordArray, tDataTypeSigned, x); if(tComps>1) Buffers.putNf(textCoordArray, tDataTypeSigned, y); countAndPadding(TEXTCOORD, tComps-2); } public void glTexCoord3f(float x, float y, float z) { checkSeal(false); growBuffer(TEXTCOORD); if(tComps>0) Buffers.putNf(textCoordArray, tDataTypeSigned, x); if(tComps>1) Buffers.putNf(textCoordArray, tDataTypeSigned, y); if(tComps>2) Buffers.putNf(textCoordArray, tDataTypeSigned, z); countAndPadding(TEXTCOORD, tComps-3); } public void rewind() { if(null!=vertexArray) { vertexArray.rewind(); } if(null!=colorArray) { colorArray.rewind(); } if(null!=normalArray) { normalArray.rewind(); } if(null!=textCoordArray) { textCoordArray.rewind(); } } public void setShaderProgram(int program) { if(null == shaderState && 0 == program) { throw new IllegalArgumentException("Not allowed to zero shader program if no ShaderState is set"); } shaderProgram = program; glslLocationSet = false; // enforce location reset! } /** * @param gl * @return true if all locations for all used arrays are found (min 1 array), otherwise false. * Also sets 'glslLocationSet' to the return value! */ private boolean resetGLSLArrayLocation(GL2ES2 gl) { int iA = 0; int iL = 0; if(null != vArrayData) { iA++; if( vArrayData.setLocation(gl, shaderProgram) >= 0 ) { iL++; } } if(null != cArrayData) { iA++; if( cArrayData.setLocation(gl, shaderProgram) >= 0 ) { iL++; } } if(null != nArrayData) { iA++; if( nArrayData.setLocation(gl, shaderProgram) >= 0 ) { iL++; } } if(null != tArrayData) { iA++; if( tArrayData.setLocation(gl, shaderProgram) >= 0 ) { iL++; } } glslLocationSet = iA == iL; return glslLocationSet; } public void destroy(GL gl) { reset(gl); vCount=0; cCount=0; nCount=0; tCount=0; vertexArray=null; colorArray=null; normalArray=null; textCoordArray=null; vArrayData=null; cArrayData=null; nArrayData=null; tArrayData=null; buffer=null; } public void reset(GL gl) { enableBuffer(gl, false); reset(); } public void reset() { if(buffer!=null) { buffer.clear(); } rewind(); this.mode = 0; this.modeOrig = 0; this.sealed=false; this.sealedGL=false; this.bufferEnabled=false; this.bufferWritten=false; this.vElems=0; this.cElems=0; this.nElems=0; this.tElems=0; } public void seal(GL glObj, boolean seal) { seal(seal); if(sealedGL==seal) return; sealedGL = seal; GL gl = glObj.getGL(); if(seal) { if(useVBO) { if(0 == vboName) { int[] tmp = new int[1]; gl.glGenBuffers(1, tmp, 0); vboName = tmp[0]; } if(null!=vArrayData) { vArrayData.setVBOName(vboName); } if(null!=cArrayData) { cArrayData.setVBOName(vboName); } if(null!=nArrayData) { nArrayData.setVBOName(vboName); } if(null!=tArrayData) { tArrayData.setVBOName(vboName); } } enableBuffer(gl, true); } else { enableBuffer(gl, false); } } public void seal(boolean seal) { if(sealed==seal) return; sealed = seal; if(seal) { bufferWritten=false; rewind(); } } public void enableBuffer(GL gl, boolean enable) { if( bufferEnabled != enable && vElems>0 ) { if(enable) { checkSeal(true); } bufferEnabled = enable; if(useGLSL) { useShaderProgram(gl.getGL2ES2(), true); if(null != shaderState) { enableBufferGLSLShaderState(gl, enable); } else { enableBufferGLSLSimple(gl, enable); } } else { enableBufferFixed(gl, enable); } } } private final void writeBuffer(GL gl) { final int vBytes = vElems * vCompsBytes; final int cBytes = cElems * cCompsBytes; final int nBytes = nElems * nCompsBytes; final int tBytes = tElems * tCompsBytes; final int delta = buffer.limit() - (vBytes+cBytes+nBytes+tBytes); if( bufferWrittenOnce && delta > pageSize ) { if(0 < vBytes) { gl.glBufferSubData(GL.GL_ARRAY_BUFFER, vOffset, vBytes, vertexArray); } if(0 < cBytes) { gl.glBufferSubData(GL.GL_ARRAY_BUFFER, cOffset, cBytes, colorArray); } if(0 < nBytes) { gl.glBufferSubData(GL.GL_ARRAY_BUFFER, nOffset, nBytes, normalArray); } if(0 < tBytes) { gl.glBufferSubData(GL.GL_ARRAY_BUFFER, tOffset, tBytes, textCoordArray); } } else { gl.glBufferData(GL.GL_ARRAY_BUFFER, buffer.limit(), buffer, glBufferUsage); bufferWrittenOnce = true; } } private void enableBufferFixed(GL gl, boolean enable) { GL2ES1 glf = gl.getGL2ES1(); final boolean useV = vComps>0 && vElems>0 ; final boolean useC = cComps>0 && cElems>0 ; final boolean useN = nComps>0 && nElems>0 ; final boolean useT = tComps>0 && tElems>0 ; if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableFixed.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); } if(enable) { if(useVBO) { if(0 == vboName) { throw new InternalError("Using VBO but no vboName"); } glf.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); if(!bufferWritten) { writeBuffer(gl); } } bufferWritten=true; } if(useV) { if(enable) { glf.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY); glf.glVertexPointer(vArrayData); } else { glf.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); } } if(useC) { if(enable) { glf.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY); glf.glColorPointer(cArrayData); } else { glf.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); } } if(useN) { if(enable) { glf.glEnableClientState(GLPointerFunc.GL_NORMAL_ARRAY); glf.glNormalPointer(nArrayData); } else { glf.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY); } } if(useT) { if(enable) { glf.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); glf.glTexCoordPointer(tArrayData); } else { glf.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); } } if(enable && useVBO) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); } if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableFixed.X "); } } private void enableBufferGLSLShaderState(GL gl, boolean enable) { GL2ES2 glsl = gl.getGL2ES2(); final boolean useV = vComps>0 && vElems>0 ; final boolean useC = cComps>0 && cElems>0 ; final boolean useN = nComps>0 && nElems>0 ; final boolean useT = tComps>0 && tElems>0 ; if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableGLSL.A.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); } if(enable) { if(useVBO) { if(0 == vboName) { throw new InternalError("Using VBO but no vboName"); } glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); if(!bufferWritten) { writeBuffer(gl); } } bufferWritten=true; } if(useV) { if(enable) { shaderState.enableVertexAttribArray(glsl, vArrayData); shaderState.vertexAttribPointer(glsl, vArrayData); } else { shaderState.disableVertexAttribArray(glsl, vArrayData); } } if(useC) { if(enable) { shaderState.enableVertexAttribArray(glsl, cArrayData); shaderState.vertexAttribPointer(glsl, cArrayData); } else { shaderState.disableVertexAttribArray(glsl, cArrayData); } } if(useN) { if(enable) { shaderState.enableVertexAttribArray(glsl, nArrayData); shaderState.vertexAttribPointer(glsl, nArrayData); } else { shaderState.disableVertexAttribArray(glsl, nArrayData); } } if(useT) { if(enable) { shaderState.enableVertexAttribArray(glsl, tArrayData); shaderState.vertexAttribPointer(glsl, tArrayData); } else { shaderState.disableVertexAttribArray(glsl, tArrayData); } } glslLocationSet = true; // ShaderState does set the location implicit if(enable && useVBO) { glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); } if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableGLSL.A.X "); } } private void enableBufferGLSLSimple(GL gl, boolean enable) { GL2ES2 glsl = gl.getGL2ES2(); final boolean useV = vComps>0 && vElems>0 ; final boolean useC = cComps>0 && cElems>0 ; final boolean useN = nComps>0 && nElems>0 ; final boolean useT = tComps>0 && tElems>0 ; if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableGLSL.B.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); } if(!glslLocationSet) { if( !resetGLSLArrayLocation(glsl) ) { if(DEBUG_DRAW) { final int vLoc = null != vArrayData ? vArrayData.getLocation() : -1; final int cLoc = null != cArrayData ? cArrayData.getLocation() : -1; final int nLoc = null != nArrayData ? nArrayData.getLocation() : -1; final int tLoc = null != tArrayData ? tArrayData.getLocation() : -1; System.err.println("ImmModeSink.enableGLSL.B.X attribute locations in shader program "+shaderProgram+", incomplete ["+vLoc+", "+cLoc+", "+nLoc+", "+tLoc+"] - glslLocationSet "+glslLocationSet); } return; } } if(enable) { if(useVBO) { if(0 == vboName) { throw new InternalError("Using VBO but no vboName"); } glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); if(!bufferWritten) { writeBuffer(gl); } } bufferWritten=true; } if(useV) { if(enable) { glsl.glEnableVertexAttribArray(vArrayData.getLocation()); glsl.glVertexAttribPointer(vArrayData); } else { glsl.glDisableVertexAttribArray(vArrayData.getLocation()); } } if(useC) { if(enable) { glsl.glEnableVertexAttribArray(cArrayData.getLocation()); glsl.glVertexAttribPointer(cArrayData); } else { glsl.glDisableVertexAttribArray(cArrayData.getLocation()); } } if(useN) { if(enable) { glsl.glEnableVertexAttribArray(nArrayData.getLocation()); glsl.glVertexAttribPointer(nArrayData); } else { glsl.glDisableVertexAttribArray(nArrayData.getLocation()); } } if(useT) { if(enable) { glsl.glEnableVertexAttribArray(tArrayData.getLocation()); glsl.glVertexAttribPointer(tArrayData); } else { glsl.glDisableVertexAttribArray(tArrayData.getLocation()); } } if(enable && useVBO) { glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); } if(DEBUG_DRAW) { System.err.println("ImmModeSink.enableGLSL.B.X "); } } @Override public String toString() { final String glslS = useGLSL ? ", useShaderState "+(null!=shaderState)+ ", shaderProgram "+shaderProgram+ ", glslLocationSet "+glslLocationSet : ""; return "VBOSet[mode "+mode+ ", modeOrig "+modeOrig+ ", use/count "+getElemUseCountStr()+ ", sealed "+sealed+ ", sealedGL "+sealedGL+ ", bufferEnabled "+bufferEnabled+ ", bufferWritten "+bufferWritten+" (once "+bufferWrittenOnce+")"+ ", useVBO "+useVBO+", vboName "+vboName+ ", useGLSL "+useGLSL+ glslS+ ",\n\t"+vArrayData+ ",\n\t"+cArrayData+ ",\n\t"+nArrayData+ ",\n\t"+tArrayData+ "]"; } // non public matters protected String getElemUseCountStr() { return "[v "+vElems+"/"+vCount+", c "+cElems+"/"+cCount+", n "+nElems+"/"+nCount+", t "+tElems+"/"+tCount+"]"; } protected boolean fitElementInBuffer(int type) { final int addElems = 1; switch (type) { case VERTEX: return ( vCount - vElems ) >= addElems ; case COLOR: return ( cCount - cElems ) >= addElems ; case NORMAL: return ( nCount - nElems ) >= addElems ; case TEXTCOORD: return ( tCount - tElems ) >= addElems ; default: throw new InternalError("XXX"); } } protected boolean reallocateBuffer(int addElems) { final int vAdd = addElems - ( vCount - vElems ); final int cAdd = addElems - ( cCount - cElems ); final int nAdd = addElems - ( nCount - nElems ); final int tAdd = addElems - ( tCount - tElems ); if( 0>=vAdd && 0>=cAdd && 0>=nAdd && 0>=tAdd) { if(DEBUG_BUFFER) { System.err.println("ImmModeSink.realloc: "+getElemUseCountStr()+" + "+addElems+" -> NOP"); } return false; } if(DEBUG_BUFFER) { System.err.println("ImmModeSink.realloc: "+getElemUseCountStr()+" + "+addElems); } vCount += vAdd; cCount += cAdd; nCount += nAdd; tCount += tAdd; final int vBytes = vCount * vCompsBytes; final int cBytes = cCount * cCompsBytes; final int nBytes = nCount * nCompsBytes; final int tBytes = tCount * tCompsBytes; buffer = Buffers.newDirectByteBuffer( vBytes + cBytes + nBytes + tBytes ); vOffset = 0; if(vBytes>0) { vertexArray = GLBuffers.sliceGLBuffer(buffer, vOffset, vBytes, vDataType); } else { vertexArray = null; } cOffset=vOffset+vBytes; if(cBytes>0) { colorArray = GLBuffers.sliceGLBuffer(buffer, cOffset, cBytes, cDataType); } else { colorArray = null; } nOffset=cOffset+cBytes; if(nBytes>0) { normalArray = GLBuffers.sliceGLBuffer(buffer, nOffset, nBytes, nDataType); } else { normalArray = null; } tOffset=nOffset+nBytes; if(tBytes>0) { textCoordArray = GLBuffers.sliceGLBuffer(buffer, tOffset, tBytes, tDataType); } else { textCoordArray = null; } buffer.position(tOffset+tBytes); buffer.flip(); if(vComps>0) { vArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_VERTEX_ARRAY, vComps, vDataType, GLBuffers.isGLTypeFixedPoint(vDataType), 0, vertexArray, 0, vOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { vArrayData = null; } if(cComps>0) { cArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_COLOR_ARRAY, cComps, cDataType, GLBuffers.isGLTypeFixedPoint(cDataType), 0, colorArray, 0, cOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { cArrayData = null; } if(nComps>0) { nArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_NORMAL_ARRAY, nComps, nDataType, GLBuffers.isGLTypeFixedPoint(nDataType), 0, normalArray, 0, nOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { nArrayData = null; } if(tComps>0) { tArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_TEXTURE_COORD_ARRAY, tComps, tDataType, GLBuffers.isGLTypeFixedPoint(tDataType), 0, textCoordArray, 0, tOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { tArrayData = null; } bufferWrittenOnce = false; // new buffer data storage size! if(DEBUG_BUFFER) { System.err.println("ImmModeSink.realloc.X: "+this.toString()); Thread.dumpStack(); } return true; } /** grow buffer by initialElementCount if there is no space for one more element in the designated buffer */ protected final boolean growBuffer(int type) { if( null !=buffer && !sealed ) { if( !fitElementInBuffer(type) ) { // save olde values .. final Buffer _vertexArray=vertexArray, _colorArray=colorArray, _normalArray=normalArray, _textCoordArray=textCoordArray; if ( reallocateBuffer(resizeElementCount) ) { if(null!=_vertexArray) { _vertexArray.flip(); Buffers.put(vertexArray, _vertexArray); } if(null!=_colorArray) { _colorArray.flip(); Buffers.put(colorArray, _colorArray); } if(null!=_normalArray) { _normalArray.flip(); Buffers.put(normalArray, _normalArray); } if(null!=_textCoordArray) { _textCoordArray.flip(); Buffers.put(textCoordArray, _textCoordArray); } return true; } } } return false; } /** * Fourth element default value for color (alpha), vertex (w) is '1', * as specified w/ VertexAttributes (ES2/GL3). *

* vec4 v = vec4(0, 0, 0, 1); * vec4 c = vec4(0, 0, 0, 1); *

* * @param type * @param fill */ private void countAndPadding(int type, int fill) { if ( sealed ) return; final Buffer dest; final boolean dSigned; final int e; // either 0 or 1 switch (type) { case VERTEX: dest = vertexArray; dSigned = vDataTypeSigned; e = 4 == vComps ? 1 : 0; vElems++; break; case COLOR: dest = colorArray; dSigned = cDataTypeSigned; e = 4 == cComps ? 1 : 0; cElems++; break; case NORMAL: dest = normalArray; dSigned = nDataTypeSigned; e = 0; nElems++; break; case TEXTCOORD: dest = textCoordArray; dSigned = tDataTypeSigned; e = 0; tElems++; break; default: throw new InternalError("Invalid type "+type); } if ( null==dest ) return; while( fill > e ) { fill--; Buffers.putNf(dest, dSigned, 0f); } if( fill > 0 ) { // e == 1, add missing '1f end component' Buffers.putNf(dest, dSigned, 1f); } } final private int glBufferUsage, initialElementCount; final private boolean useVBO, useGLSL; final private ShaderState shaderState; private int shaderProgram; private int mode, modeOrig, resizeElementCount; private ByteBuffer buffer; private int vboName; private static final int VERTEX = 0; private static final int COLOR = 1; private static final int NORMAL = 2; private static final int TEXTCOORD = 3; private int vCount, cCount, nCount, tCount; // number of elements fit in each buffer private int vOffset, cOffset, nOffset, tOffset; // offset of specific array in common buffer private int vElems, cElems, nElems, tElems; // number of used elements in each buffer private final int vComps, cComps, nComps, tComps; // number of components for each elements [2, 3, 4] private final int vCompsBytes, cCompsBytes, nCompsBytes, tCompsBytes; // byte size of all components private final int vDataType, cDataType, nDataType, tDataType; private final boolean vDataTypeSigned, cDataTypeSigned, nDataTypeSigned, tDataTypeSigned; private final int pageSize; private Buffer vertexArray, colorArray, normalArray, textCoordArray; private GLArrayDataWrapper vArrayData, cArrayData, nArrayData, tArrayData; private boolean sealed, sealedGL; private boolean bufferEnabled, bufferWritten, bufferWrittenOnce; private boolean glslLocationSet; } }