package com.blarg.gdx.graphics;

import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.decals.Decal;
import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
import com.badlogic.gdx.utils.Array;

/***
 * <p>
 * Wrapper over libgdx's included {@link DecalBatch} with automatic easy management of
 * {@link Decal} objects. This is intended to make "on the fly" rendering of decals/billboards
 * as easy as rendering 2D sprites is with SpriteBatch / DelayedSpriteBatch.
 * </p>
 */
public class BillboardSpriteBatch {
	static final int CAPACITY_INCREMENT = 128;

	Array<Decal> sprites;
	int pointer;
	boolean hasBegun;
	DecalBatch decalBatch;

	public BillboardSpriteBatch() {
		sprites = new Array<Decal>(true, CAPACITY_INCREMENT, Decal.class);
		pointer = 0;
		addNewSprites(CAPACITY_INCREMENT);

		hasBegun = false;
		decalBatch = null;
	}

	public void begin(DecalBatch decalBatch) {
		if (hasBegun)
			throw new IllegalStateException("Cannot be called within an existing begin/end block.");
		if (decalBatch == null)
			throw new IllegalArgumentException();

		this.decalBatch = decalBatch;
		hasBegun = true;
		pointer = 0;
	}

	public void flush() {
		if (!hasBegun)
			throw new IllegalStateException("Cannot call outside of a begin/end block.");

		// TODO: render decals with DecalBatch

		pointer = 0;
	}

	public void end() {
		if (!hasBegun)
			throw new IllegalStateException("Must call begin() first.");

		flush();

		hasBegun = false;
		decalBatch = null;    // don't need to hold on to this particular reference anymore
	}


	private void increaseCapacity() {
		int newCapacity = sprites.items.length + CAPACITY_INCREMENT;
		sprites.ensureCapacity(newCapacity);
		addNewSprites(CAPACITY_INCREMENT);
	}

	private void addNewSprites(int count) {
		for (int i = 0; i < count; ++i)
			sprites.add(Decal.newDecal(new TextureRegion()));
	}

	private int getRemainingSpace() {
		return sprites.size - pointer;
	}

	private Decal nextUsable() {
		if (getRemainingSpace() == 0)
			increaseCapacity();

		Decal usable = sprites.items[pointer];
		pointer++;
		return usable;
	}
}

@@ -26,16 +26,98 @@
 phics;%0A%0A
+import com.badlogic.gdx.graphics.Color;%0Aimport com.badlogic.gdx.graphics.Texture;%0A
 import c
@@ -1257,157 +1257,996 @@
 oid 
-flush() %7B%0A%09%09if (!hasBegun)%0A%09%09%09throw new IllegalStateException(%22Cannot call outside of a begin/end block.%22);%0A%0A%09%09// TODO: render decals with DecalBatch
+draw(Texture texture, float x, float y, float z, float width, float height) %7B%0A%09%09draw(texture, x, y, z, width, height, Color.WHITE);%0A%09%7D%0A%0A%09public void draw(Texture texture, float x, float y, float z, float width, float height, Color tint) %7B%0A%09%09Decal sprite = nextUsable();%0A%09%09TextureRegion textureRegion = sprite.getTextureRegion();%0A%09%09textureRegion.setRegion(texture);%0A%09%09sprite.setTextureRegion(textureRegion);%0A%09%09sprite.setWidth(width);%0A%09%09sprite.setHeight(height);%0A%09%09sprite.setPosition(x, y, z);%0A%09%09sprite.setColor(tint);%0A%09%7D%0A%0A%09public void flush() %7B%0A%09%09if (!hasBegun)%0A%09%09%09throw new IllegalStateException(%22Cannot call outside of a begin/end block.%22);%0A%0A%09%09for (int i = 0; i %3C pointer; ++i) %7B%0A%09%09%09Decal sprite = sprites.items%5Bi%5D;%0A%09%09%09decalBatch.add(sprite);%0A%09%09%7D%0A%09%09decalBatch.flush();%0A%0A%09%09// don't want to hang on to Texture object references%0A%09%09// TODO: is this overkill? does this really make a big difference?%0A%09%09for (int i = 0; i %3C pointer; ++i)%0A%09%09%09sprites.items%5Bi%5D.getTextureRegion().setTexture(null);
 %0A%0A%09%09

add basic billboard draw methods