package space.earlygrey.shapedrawer; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Matrix4; /** * <p>Managers the vertex data and when it it sent to the batch.</p> * * @author earlygrey */ class BatchManager { //================================================================================ // MEMBERS //================================================================================ protected final Batch batch; protected TextureRegion r; protected float floatBits; protected float[] verts; protected int vertexCount; protected float pixelSize = 1, halfPixelSize = 0.5f * pixelSize; protected float offset = ShapeUtils.EPSILON * pixelSize; protected boolean cacheDraws = false; protected static final Matrix4 mat4 = new Matrix4(); Drawing drawing = null; // These are named just for clarity static final int DEFAULT_VERTEX_CACHE_SIZE = 2000; static final int VERTEX_SIZE = 5, QUAD_PUSH_SIZE = 4 * VERTEX_SIZE; BatchManager (Batch batch, TextureRegion region) { this.batch = batch; verts = new float[DEFAULT_VERTEX_CACHE_SIZE]; setTextureRegion(region); setColor(Color.WHITE); } public void update() { update(true); } public void update(boolean updatePixelSize) { if (updatePixelSize) updatePixelSize(); } public float updatePixelSize() { Matrix4 trans = getBatch().getTransformMatrix(), proj = getBatch().getProjectionMatrix(); mat4.set(proj).mul(trans); float scaleX = mat4.getScaleX(); float worldWidth = 2f / scaleX; float newPixelSize = worldWidth / Gdx.graphics.getWidth(); return setPixelSize(newPixelSize); } public float setPixelSize(float pixelSize) { float oldPixelSize = this.pixelSize; this.pixelSize = pixelSize; halfPixelSize = 0.5f * pixelSize; offset = ShapeUtils.EPSILON * pixelSize; return oldPixelSize; } public TextureRegion setTextureRegion(TextureRegion region) { TextureRegion oldRegion = this.r; this.r = region; float u = 0.5f * (r.getU() + r.getU2()); float v = 0.5f * (r.getV() + r.getV2()); for (int i = 0; i < verts.length; i+=VERTEX_SIZE) { verts[i + SpriteBatch.U1] = u; verts[i + SpriteBatch.V1] = v; } return oldRegion; } public float setColor(Color color) { return setColor(color.toFloatBits()); } public float setColor(float floatBits) { float oldColor = getPackedColor(); this.floatBits = floatBits; return oldColor; } public float getPackedColor() { return floatBits; } /** * * @return whether drawing is currently being cached */ boolean isCachingDraws() { return cacheDraws; } /** * <p>Begin caching draw calls by storing vertex information in a float[] until it all gets set to the * Batch with one call to {@link Batch#draw(Texture, float[], int, int)}.</p> * @return whether drawing was being cached before this method was called */ boolean startCaching() { boolean wasCaching = isCachingDraws(); this.cacheDraws = true; return wasCaching; } /** * <p>Stops caching and calls {@link Batch#draw(Texture, float[], int, int)} if anything is cached.</p> */ void endCaching() { this.cacheDraws = false; if (vertexCount>0) pushToBatch(); } public float getPixelSize() { return pixelSize; } public Batch getBatch() { return batch; } public TextureRegion getRegion() { return r; } //================================================================================ // RECORDING //================================================================================ void startRecording() { drawing = createDrawing(); } Drawing createDrawing() { return new Drawing(this); } Drawing stopRecording() { drawing.finalise(); Drawing returnVal = drawing; drawing = null; return returnVal; } boolean isRecording() { return drawing != null; } //================================================================================ // DRAWING //================================================================================ void pushVertex() { vertexCount++; } /** * <p>Adds the colour and texture coordinates of four vertices to the cache and progresses the index. If drawing is * not currently being cached, immediately calls {@link #pushToBatch()}.</p> * @return whether the vertex data was pushed to the Batch */ void pushQuad() { vertexCount += 4; } /** <p>Adds the colour and texture coordinates of three vertices to the cache and progresses the index. If drawing is * not currently being cached, immediately calls {@link #pushToBatch()}.</p> * @return whether the vertex data was pushed to the Batch */ void pushTriangle() { x4(x3()); y4(y3()); pushQuad(); } void ensureSpaceForTriangle() { ensureSpace(4); } void ensureSpaceForQuad() { ensureSpace(4); } void ensureSpace(int vertices) { if (vertices * VERTEX_SIZE > verts.length) { increaseCacheSize(vertices * VERTEX_SIZE); } else if (verticesRemaining() < vertices) { pushToBatch(); } } void increaseCacheSize(int minSize) { pushToBatch(); int newSize = verts.length; while (minSize > newSize) { newSize *= 2; } verts = new float[newSize]; } int verticesRemaining() { return (verts.length - QUAD_PUSH_SIZE * vertexCount) / VERTEX_SIZE; } /** * <p>Calls {@link Batch#draw(Texture, float[], int, int)} using the currently cached vertex information.</p> */ void pushToBatch() { if (vertexCount == 0) return; if (isRecording()) { drawing.pushVertices(); } else { batch.draw(r.getTexture(), verts, 0, getVerticesArrayIndex()); } vertexCount = 0; } int getVerticesArrayIndex() { return VERTEX_SIZE * vertexCount; } protected void x1(float x1){verts[getVerticesArrayIndex() + SpriteBatch.X1] = x1;} protected void y1(float y1){verts[getVerticesArrayIndex() + SpriteBatch.Y1] = y1;} protected void x2(float x2){verts[getVerticesArrayIndex() + SpriteBatch.X2] = x2;} protected void y2(float y2){verts[getVerticesArrayIndex() + SpriteBatch.Y2] = y2;} protected void x3(float x3){verts[getVerticesArrayIndex() + SpriteBatch.X3] = x3;} protected void y3(float y3){verts[getVerticesArrayIndex() + SpriteBatch.Y3] = y3;} protected void x4(float x4){verts[getVerticesArrayIndex() + SpriteBatch.X4] = x4;} protected void y4(float y4){verts[getVerticesArrayIndex() + SpriteBatch.Y4] = y4;} protected float x1() {return verts[getVerticesArrayIndex() + SpriteBatch.X1];} protected float y1() {return verts[getVerticesArrayIndex() + SpriteBatch.Y1];} protected float x2() {return verts[getVerticesArrayIndex() + SpriteBatch.X2];} protected float y2() {return verts[getVerticesArrayIndex() + SpriteBatch.Y2];} protected float x3() {return verts[getVerticesArrayIndex() + SpriteBatch.X3];} protected float y3() {return verts[getVerticesArrayIndex() + SpriteBatch.Y3];} protected float x4() {return verts[getVerticesArrayIndex() + SpriteBatch.X4];} protected float y4() {return verts[getVerticesArrayIndex() + SpriteBatch.Y4];} void color1(float c) {verts[getVerticesArrayIndex() + SpriteBatch.C1] = c;} void color2(float c) {verts[getVerticesArrayIndex() + SpriteBatch.C2] = c;} void color3(float c) {verts[getVerticesArrayIndex() + SpriteBatch.C3] = c;} void color4(float c) {verts[getVerticesArrayIndex() + SpriteBatch.C4] = c;} }