package org.andengine.opengl.vbo;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.andengine.opengl.shader.ShaderProgram;
import org.andengine.opengl.util.BufferUtils;
import org.andengine.opengl.util.GLState;
import org.andengine.opengl.vbo.attribute.VertexBufferObjectAttributes;
import org.andengine.util.adt.DataConstants;
import android.opengl.GLES20;
/**
* TODO Extract a common base class from {@link VertexBufferObject} and {@link ZeroMemoryVertexBufferObject} (due to significant code duplication).
* For naming, maybe be inspired by the java ByteBuffer naming (i.e. HeapBackedFloatArrayVertexBufferObject, StreamBufferVertexBufferObject, SharedBufferStreamVertexBufferObject).
*
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 14:22:56 - 07.04.2010
*/
public abstract class VertexBufferObject implements IVertexBufferObject {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
protected final int mCapacity;
protected final boolean mAutoDispose;
protected final int mUsage;
protected final ByteBuffer mByteBuffer;
protected int mHardwareBufferID = IVertexBufferObject.HARDWARE_BUFFER_ID_INVALID;
protected boolean mDirtyOnHardware = true;
protected boolean mDisposed;
protected final VertexBufferObjectManager mVertexBufferObjectManager;
protected final VertexBufferObjectAttributes mVertexBufferObjectAttributes;
// ===========================================================
// Constructors
// ===========================================================
/**
* @param pVertexBufferObjectManager (Optional, if you manage reloading on your own.)
* @param pCapacity
* @param pDrawType
* @param pAutoDispose when passing <code>true</code> this {@link VertexBufferObject} loads itself to the active {@link VertexBufferObjectManager}. <b><u>WARNING:</u></b> When passing <code>false</code> one needs to take care of that by oneself!
* @param pVertexBufferObjectAttributes to be automatically enabled on the {@link ShaderProgram} used in {@link VertexBufferObject#bind(ShaderProgram)}.
*/
public VertexBufferObject(final VertexBufferObjectManager pVertexBufferObjectManager, final int pCapacity, final DrawType pDrawType, final boolean pAutoDispose, final VertexBufferObjectAttributes pVertexBufferObjectAttributes) {
this.mVertexBufferObjectManager = pVertexBufferObjectManager;
this.mCapacity = pCapacity;
this.mUsage = pDrawType.getUsage();
this.mAutoDispose = pAutoDispose;
this.mVertexBufferObjectAttributes = pVertexBufferObjectAttributes;
this.mByteBuffer = BufferUtils.allocateDirectByteBuffer(pCapacity * DataConstants.BYTES_PER_FLOAT);
this.mByteBuffer.order(ByteOrder.nativeOrder());
}
// ===========================================================
// Getter & Setter
// ===========================================================
@Override
public VertexBufferObjectManager getVertexBufferObjectManager() {
return this.mVertexBufferObjectManager;
}
@Override
public boolean isDisposed() {
return this.mDisposed;
}
@Override
public boolean isAutoDispose() {
return this.mAutoDispose;
}
@Override
public int getHardwareBufferID() {
return this.mHardwareBufferID;
}
@Override
public boolean isLoadedToHardware() {
return this.mHardwareBufferID != IVertexBufferObject.HARDWARE_BUFFER_ID_INVALID;
}
@Override
public void setNotLoadedToHardware() {
this.mHardwareBufferID = IVertexBufferObject.HARDWARE_BUFFER_ID_INVALID;
this.mDirtyOnHardware = true;
}
@Override
public boolean isDirtyOnHardware() {
return this.mDirtyOnHardware;
}
@Override
public void setDirtyOnHardware() {
this.mDirtyOnHardware = true;
}
@Override
public int getCapacity() {
return this.mCapacity;
}
@Override
public int getByteCapacity() {
return this.mByteBuffer.capacity();
}
@Override
public int getGPUMemoryByteSize() {
if(this.isLoadedToHardware()) {
return this.getByteCapacity();
} else {
return 0;
}
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
protected abstract void onBufferData();
@Override
public void bind(final GLState pGLState) {
if(this.mHardwareBufferID == IVertexBufferObject.HARDWARE_BUFFER_ID_INVALID) {
this.loadToHardware(pGLState);
if(this.mVertexBufferObjectManager != null) {
this.mVertexBufferObjectManager.onVertexBufferObjectLoaded(this);
}
}
pGLState.bindArrayBuffer(this.mHardwareBufferID);
if(this.mDirtyOnHardware) {
this.onBufferData();
this.mDirtyOnHardware = false;
}
}
@Override
public void bind(final GLState pGLState, final ShaderProgram pShaderProgram) {
this.bind(pGLState);
pShaderProgram.bind(pGLState, this.mVertexBufferObjectAttributes);
}
@Override
public void unbind(final GLState pGLState, final ShaderProgram pShaderProgram) {
pShaderProgram.unbind(pGLState);
// pGLState.bindBuffer(0); // TODO Does this have an positive/negative impact on performance?
}
@Override
public void unloadFromHardware(final GLState pGLState) {
pGLState.deleteArrayBuffer(this.mHardwareBufferID);
this.mHardwareBufferID = IVertexBufferObject.HARDWARE_BUFFER_ID_INVALID;
}
@Override
public void draw(final int pPrimitiveType, final int pCount) {
GLES20.glDrawArrays(pPrimitiveType, 0, pCount);
}
@Override
public void draw(final int pPrimitiveType, final int pOffset, final int pCount) {
GLES20.glDrawArrays(pPrimitiveType, pOffset, pCount);
}
@Override
public void dispose() {
if(!this.mDisposed) {
this.mDisposed = true;
if(this.mVertexBufferObjectManager != null) {
this.mVertexBufferObjectManager.onUnloadVertexBufferObject(this);
}
BufferUtils.freeDirectByteBuffer(this.mByteBuffer);
} else {
throw new AlreadyDisposedException();
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if(!this.mDisposed) {
this.dispose();
}
}
// ===========================================================
// Methods
// ===========================================================
private void loadToHardware(final GLState pGLState) {
this.mHardwareBufferID = pGLState.generateBuffer();
this.mDirtyOnHardware = true;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}