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) 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 = Debug.isPropertyDefined("jogl.debug.ImmModeSink.BeginEnd", true);
protected static final boolean DEBUG_DRAW = Debug.isPropertyDefined("jogl.debug.ImmModeSink.Draw", true);
protected static final boolean 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);
}
/**
* Uses a GL2ES2 GLSL shader immediate mode sink.
* To issue the draw() command,
* a ShaderState must be current, using ShaderState.glUseProgram().
*
* 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
*
* @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) {
return new ImmModeSink(initialElementCount,
vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType,
true, glBufferUsage);
}
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);
}
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) {
vboSet = new VBOSet(initialElementCount,
vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType,
useGLSL, glBufferUsage);
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)
*/
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) {
this.glBufferUsage=glBufferUsage;
this.initialElementCount=initialElementCount;
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.useGLSL=useGLSL;
this.useVBO = 0 != glBufferUsage;
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;
}
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);
}
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);
}
}
}
protected void draw(GL gl, Buffer indices, boolean disableBufferAfterDraw, int i)
{
enableBuffer(gl, true);
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 {
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 {
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 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) {
enableBufferGLSL(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;
}
}
public 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 ");
}
}
public void enableBufferGLSL(GL gl, boolean enable) {
ShaderState st = ShaderState.getShaderState(gl);
if(null==st) {
throw new GLException("No ShaderState in "+gl);
}
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.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) {
st.enableVertexAttribArray(glsl, vArrayData);
st.vertexAttribPointer(glsl, vArrayData);
} else {
st.disableVertexAttribArray(glsl, vArrayData);
}
}
if(useC) {
if(enable) {
st.enableVertexAttribArray(glsl, cArrayData);
st.vertexAttribPointer(glsl, cArrayData);
} else {
st.disableVertexAttribArray(glsl, cArrayData);
}
}
if(useN) {
if(enable) {
st.enableVertexAttribArray(glsl, nArrayData);
st.vertexAttribPointer(glsl, nArrayData);
} else {
st.disableVertexAttribArray(glsl, nArrayData);
}
}
if(useT) {
if(enable) {
st.enableVertexAttribArray(glsl, tArrayData);
st.vertexAttribPointer(glsl, tArrayData);
} else {
st.disableVertexAttribArray(glsl, tArrayData);
}
}
if(enable && useVBO) {
glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
}
if(DEBUG_DRAW) {
System.err.println("ImmModeSink.enableGLSL.X ");
}
}
public String toString() {
return "VBOSet[mode "+mode+
", modeOrig "+modeOrig+
", use/count "+getElemUseCountStr()+
", sealed "+sealed+
", sealedGL "+sealedGL+
", bufferEnabled "+bufferEnabled+
", bufferWritten "+bufferWritten+" (once "+bufferWrittenOnce+")"+
", useVBO "+useVBO+", vboName "+vboName+
",\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;
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, useGLSL;
private boolean bufferEnabled, bufferWritten, bufferWrittenOnce;
}
}