package org.anddev.andengine.entity.sprite.batch;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.entity.shape.Shape;
import org.anddev.andengine.entity.sprite.BaseSprite;
import org.anddev.andengine.opengl.texture.ITexture;
import org.anddev.andengine.opengl.texture.region.BaseTextureRegion;
import org.anddev.andengine.opengl.texture.region.buffer.SpriteBatchTextureRegionBuffer;
import org.anddev.andengine.opengl.util.GLHelper;
import org.anddev.andengine.opengl.vertex.SpriteBatchVertexBuffer;
/**
* TODO Texture could be semi-changeable, being resetting to null in end(...)
* TODO Make use of pGL.glColorPointer(size, type, stride, pointer) which should allow individual color tinting.
*
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 11:45:48 - 14.06.2011
*/
public class SpriteBatch extends Entity {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
protected final ITexture mTexture;
protected final int mCapacity;
protected int mIndex;
private int mVertices;
private int mSourceBlendFunction;
private int mDestinationBlendFunction;
private final SpriteBatchVertexBuffer mSpriteBatchVertexBuffer;
private final SpriteBatchTextureRegionBuffer mSpriteBatchTextureRegionBuffer;
// ===========================================================
// Constructors
// ===========================================================
public SpriteBatch(final ITexture pTexture, final int pCapacity) {
this(pTexture, pCapacity, new SpriteBatchVertexBuffer(pCapacity, GL11.GL_STATIC_DRAW, true), new SpriteBatchTextureRegionBuffer(pCapacity, GL11.GL_STATIC_DRAW, true));
}
public SpriteBatch(final ITexture pTexture, final int pCapacity, final SpriteBatchVertexBuffer pSpriteBatchVertexBuffer, final SpriteBatchTextureRegionBuffer pSpriteBatchTextureRegionBuffer) {
this.mTexture = pTexture;
this.mCapacity = pCapacity;
this.mSpriteBatchVertexBuffer = pSpriteBatchVertexBuffer;
this.mSpriteBatchTextureRegionBuffer = pSpriteBatchTextureRegionBuffer;
this.initBlendFunction();
}
// ===========================================================
// Getter & Setter
// ===========================================================
public void setBlendFunction(final int pSourceBlendFunction, final int pDestinationBlendFunction) {
this.mSourceBlendFunction = pSourceBlendFunction;
this.mDestinationBlendFunction = pDestinationBlendFunction;
}
public int getIndex() {
return this.mIndex;
}
public void setIndex(final int pIndex) {
this.assertCapacity(pIndex);
this.mIndex = pIndex;
final int vertexIndex = pIndex * 2 * SpriteBatchVertexBuffer.VERTICES_PER_RECTANGLE;
this.mSpriteBatchVertexBuffer.setIndex(vertexIndex);
this.mSpriteBatchTextureRegionBuffer.setIndex(vertexIndex);
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
protected void doDraw(final GL10 pGL, final Camera pCamera) {
this.onInitDraw(pGL);
this.begin(pGL);
this.onApplyVertices(pGL);
this.onApplyTextureRegion(pGL);
this.drawVertices(pGL, pCamera);
this.end(pGL);
}
@Override
public void reset() {
super.reset();
this.initBlendFunction();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if(this.mSpriteBatchVertexBuffer.isManaged()) {
this.mSpriteBatchVertexBuffer.unloadFromActiveBufferObjectManager();
}
if(this.mSpriteBatchTextureRegionBuffer.isManaged()) {
this.mSpriteBatchTextureRegionBuffer.unloadFromActiveBufferObjectManager();
}
}
// ===========================================================
// Methods
// ===========================================================
protected void begin(@SuppressWarnings("unused") final GL10 pGL) {
// GLHelper.disableDepthMask(pGL);
}
protected void end(@SuppressWarnings("unused") final GL10 pGL) {
// GLHelper.enableDepthMask(pGL);
}
/**
* @see {@link SpriteBatchVertexBuffer#add(float, float, float, float)}.
*/
public void draw(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight) {
this.assertCapacity();
this.assertTexture(pTextureRegion);
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
public void drawWithoutChecks(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight) {
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
/**
* @see {@link SpriteBatchVertexBuffer#add(float, float, float, float, float)}.
*/
public void draw(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pRotation) {
this.assertCapacity();
this.assertTexture(pTextureRegion);
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pRotation);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
public void drawWithoutChecks(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pRotation) {
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pRotation);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
/**
* @see {@link SpriteBatchVertexBuffer#add(float, float, float, float, float, float)}.
*/
public void draw(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pScaleX, final float pScaleY) {
this.assertCapacity();
this.assertTexture(pTextureRegion);
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pScaleX, pScaleY);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
public void drawWithoutChecks(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pScaleX, final float pScaleY) {
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pScaleX, pScaleY);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
/**
* @see {@link SpriteBatchVertexBuffer#add(float, float, float, float, float, float, float)}.
*/
public void draw(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pRotation, final float pScaleX, final float pScaleY) {
this.assertCapacity();
this.assertTexture(pTextureRegion);
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pRotation, pScaleX, pScaleY);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
public void drawWithoutChecks(final BaseTextureRegion pTextureRegion, final float pX, final float pY, final float pWidth, final float pHeight, final float pRotation, final float pScaleX, final float pScaleY) {
this.mSpriteBatchVertexBuffer.add(pX, pY, pWidth, pHeight, pRotation, pScaleX, pScaleY);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
/**
* {@link SpriteBatchVertexBuffer#add(float, float, float, float, float, float, float, float)}.
*/
public void draw(final BaseTextureRegion pTextureRegion, final float pX1, final float pY1, final float pX2, final float pY2, final float pX3, final float pY3, final float pX4, final float pY4) {
this.assertCapacity();
this.assertTexture(pTextureRegion);
this.mSpriteBatchVertexBuffer.addInner(pX1, pY1, pX2, pY2, pX3, pY3, pX4, pY4);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
public void drawWithoutChecks(final BaseTextureRegion pTextureRegion, final float pX1, final float pY1, final float pX2, final float pY2, final float pX3, final float pY3, final float pX4, final float pY4) {
this.mSpriteBatchVertexBuffer.addInner(pX1, pY1, pX2, pY2, pX3, pY3, pX4, pY4);
this.mSpriteBatchTextureRegionBuffer.add(pTextureRegion);
this.mIndex++;
}
/**
* {@link SpriteBatchVertexBuffer#add(BaseSprite)}.
*/
public void draw(final BaseSprite pBaseSprite) {
if(pBaseSprite.isVisible()) {
this.assertCapacity();
final BaseTextureRegion textureRegion = pBaseSprite.getTextureRegion();
this.assertTexture(textureRegion);
if(pBaseSprite.getRotation() == 0 && !pBaseSprite.isScaled()) {
this.mSpriteBatchVertexBuffer.add(pBaseSprite.getX(), pBaseSprite.getY(), pBaseSprite.getWidth(), pBaseSprite.getHeight());
} else {
this.mSpriteBatchVertexBuffer.add(pBaseSprite.getWidth(), pBaseSprite.getHeight(), pBaseSprite.getLocalToParentTransformation());
}
this.mSpriteBatchTextureRegionBuffer.add(textureRegion);
this.mIndex++;
}
}
public void drawWithoutChecks(final BaseSprite pBaseSprite) {
if(pBaseSprite.isVisible()) {
final BaseTextureRegion textureRegion = pBaseSprite.getTextureRegion();
if(pBaseSprite.getRotation() == 0 && !pBaseSprite.isScaled()) {
this.mSpriteBatchVertexBuffer.add(pBaseSprite.getX(), pBaseSprite.getY(), pBaseSprite.getWidth(), pBaseSprite.getHeight());
} else {
this.mSpriteBatchVertexBuffer.add(pBaseSprite.getWidth(), pBaseSprite.getHeight(), pBaseSprite.getLocalToParentTransformation());
}
this.mSpriteBatchTextureRegionBuffer.add(textureRegion);
this.mIndex++;
}
}
public void submit() {
this.onSubmit();
}
private void onSubmit() {
this.mVertices = this.mIndex * SpriteBatchVertexBuffer.VERTICES_PER_RECTANGLE;
this.mSpriteBatchVertexBuffer.submit();
this.mSpriteBatchTextureRegionBuffer.submit();
this.mIndex = 0;
this.mSpriteBatchVertexBuffer.setIndex(0);
this.mSpriteBatchTextureRegionBuffer.setIndex(0);
}
private void initBlendFunction() {
if(this.mTexture.getTextureOptions().mPreMultipyAlpha) {
this.setBlendFunction(Shape.BLENDFUNCTION_SOURCE_PREMULTIPLYALPHA_DEFAULT, Shape.BLENDFUNCTION_DESTINATION_PREMULTIPLYALPHA_DEFAULT);
}
}
protected void onInitDraw(final GL10 pGL) {
GLHelper.setColor(pGL, this.mRed, this.mGreen, this.mBlue, this.mAlpha);
GLHelper.enableVertexArray(pGL);
GLHelper.blendFunction(pGL, this.mSourceBlendFunction, this.mDestinationBlendFunction);
GLHelper.enableTextures(pGL);
GLHelper.enableTexCoordArray(pGL);
}
protected void onApplyVertices(final GL10 pGL) {
if(GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS) {
final GL11 gl11 = (GL11)pGL;
this.mSpriteBatchVertexBuffer.selectOnHardware(gl11);
GLHelper.vertexZeroPointer(gl11);
} else {
GLHelper.vertexPointer(pGL, this.mSpriteBatchVertexBuffer.getFloatBuffer());
}
}
private void onApplyTextureRegion(final GL10 pGL) {
if(GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS) {
final GL11 gl11 = (GL11)pGL;
this.mSpriteBatchTextureRegionBuffer.selectOnHardware(gl11);
this.mTexture.bind(pGL);
GLHelper.texCoordZeroPointer(gl11);
} else {
this.mTexture.bind(pGL);
GLHelper.texCoordPointer(pGL, this.mSpriteBatchTextureRegionBuffer.getFloatBuffer());
}
}
private void drawVertices(final GL10 pGL, @SuppressWarnings("unused") final Camera pCamera) {
pGL.glDrawArrays(GL10.GL_TRIANGLES, 0, this.mVertices);
}
private void assertCapacity(final int pIndex) {
if(pIndex >= this.mCapacity) {
throw new IllegalStateException("This supplied pIndex: '" + pIndex + "' is exceeding the capacity: '" + this.mCapacity + "' of this SpriteBatch!");
}
}
private void assertCapacity() {
if(this.mIndex == this.mCapacity) {
throw new IllegalStateException("This SpriteBatch has already reached its capacity (" + this.mCapacity + ") !");
}
}
protected void assertTexture(final BaseTextureRegion pTextureRegion) {
if(pTextureRegion.getTexture() != this.mTexture) {
throw new IllegalArgumentException("The supplied Texture does match the Texture of this SpriteBatch!");
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}