OSDN Git Service

[changed] Very slightly more efficient SpriteBatch.
authornathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Mon, 15 Nov 2010 02:11:13 +0000 (02:11 +0000)
committernathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Mon, 15 Nov 2010 02:11:13 +0000 (02:11 +0000)
[added] Sprite#getVertices.
[changed] Mesh to use VBOs for static and VAs for dynamic.
[fixed] VertexBufferObject to return correct vertex count before vertices are set.
[added] SpriteCache which uses a VBO mesh to cache static geometry.

gdx/src/com/badlogic/gdx/graphics/Mesh.java
gdx/src/com/badlogic/gdx/graphics/Sprite.java
gdx/src/com/badlogic/gdx/graphics/SpriteBatch.java
gdx/src/com/badlogic/gdx/graphics/SpriteCache.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/graphics/glutils/VertexBufferObject.java
tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteCacheTest.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java

index a0e617e..8ca2f2a 100644 (file)
@@ -74,7 +74,7 @@ public class Mesh {
         */\r
        public Mesh(boolean isStatic, int maxVertices, int maxIndices,\r
                        VertexAttribute... attributes) {\r
-               if (Gdx.gl11 != null || Gdx.gl20 != null) {\r
+               if (isStatic && (Gdx.gl11 != null || Gdx.gl20 != null)) {\r
                        vertices = new VertexBufferObject(isStatic, maxVertices, attributes);\r
                        indices = new IndexBufferObject(isStatic, maxIndices);\r
                        isVertexArray = false;\r
index bd1e393..ef616a4 100644 (file)
@@ -18,6 +18,9 @@ import com.badlogic.gdx.utils.MathUtils;
  * @author Nathan Sweet <misc@n4te.com>\r
  */\r
 public class Sprite {\r
+       static final int VERTEX_SIZE = 2 + 1 + 2;\r
+       static final int SPRITE_SIZE = 4 * VERTEX_SIZE;\r
+\r
        Texture texture;\r
        private float[] vertices = new float[20];\r
        private float x, y;\r
@@ -366,7 +369,10 @@ public class Sprite {
                dirty = true;\r
        }\r
 \r
-       public void draw(SpriteBatch spriteBatch) {\r
+       /**\r
+        * Returns the packed vertices, colors, and texture coordinates for this sprite.\r
+        */\r
+       public float[] getVertices () {\r
                if (dirty) {\r
                        dirty = false;\r
 \r
@@ -398,7 +404,11 @@ public class Sprite {
                        vertices[X4] = x1 + (x3 - x2);\r
                        vertices[Y4] = y3 - (y2 - y1);\r
                }\r
-               spriteBatch.draw(texture, vertices, 0, 20);\r
+               return vertices;\r
+       }\r
+\r
+       public void draw(SpriteBatch spriteBatch) {\r
+               spriteBatch.draw(texture, getVertices(), 0, 20);\r
        }\r
 \r
        public void setTexture(Texture texture) {\r
index fdf2fbb..f08bf99 100644 (file)
@@ -69,9 +69,6 @@ import com.badlogic.gdx.utils.MathUtils;
  * \r
  */\r
 public class SpriteBatch {\r
-       private static final int VERTEX_SIZE = 2 + 1 + 2;\r
-       private static final int SPRITE_SIZE = 4 * VERTEX_SIZE;\r
-\r
        /** the mesh used to transfer the data to the GPU **/\r
        protected final Mesh mesh;\r
 \r
@@ -148,7 +145,7 @@ public class SpriteBatch {
                projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(),\r
                                Gdx.graphics.getHeight());\r
 \r
-               vertices = new float[size * SPRITE_SIZE];\r
+               vertices = new float[size * Sprite.SPRITE_SIZE];\r
 \r
                short[] indices = new short[size * 6];\r
                int len = size * 6;\r
@@ -338,7 +335,8 @@ public class SpriteBatch {
                        lastTexture = texture;\r
                        invTexWidth = 1.0f / texture.getWidth();\r
                        invTexHeight = 1.0f / texture.getHeight();\r
-               }\r
+               } else if (idx == vertices.length)\r
+                       renderMesh();\r
 \r
                // bottom left and top right corner points relative to origin\r
                final float worldOriginX = x + originX;\r
@@ -456,9 +454,6 @@ public class SpriteBatch {
                vertices[idx++] = color;\r
                vertices[idx++] = u2;\r
                vertices[idx++] = v;\r
-\r
-               if (idx == vertices.length)\r
-                       renderMesh();\r
        }\r
 \r
        /**\r
@@ -506,7 +501,8 @@ public class SpriteBatch {
                        lastTexture = texture;\r
                        invTexWidth = 1.0f / texture.getWidth();\r
                        invTexHeight = 1.0f / texture.getHeight();\r
-               }\r
+               } else if (idx == vertices.length)\r
+                       renderMesh();\r
 \r
                float u = srcX * invTexWidth;\r
                float v = (srcY + srcHeight) * invTexHeight;\r
@@ -552,9 +548,6 @@ public class SpriteBatch {
                vertices[idx++] = color;\r
                vertices[idx++] = u2;\r
                vertices[idx++] = v;\r
-\r
-               if (idx == vertices.length)\r
-                       renderMesh();\r
        }\r
 \r
        /**\r
@@ -591,7 +584,8 @@ public class SpriteBatch {
                        lastTexture = texture;\r
                        invTexWidth = 1.0f / texture.getWidth();\r
                        invTexHeight = 1.0f / texture.getHeight();\r
-               }\r
+               } else if (idx == vertices.length)\r
+                       renderMesh();\r
 \r
                final float u = srcX * invTexWidth;\r
                final float v = (srcY + srcHeight) * invTexHeight;\r
@@ -625,9 +619,6 @@ public class SpriteBatch {
                vertices[idx++] = color;\r
                vertices[idx++] = u2;\r
                vertices[idx++] = v;\r
-\r
-               if (idx == vertices.length)\r
-                       renderMesh();\r
        }\r
 \r
        /**\r
@@ -660,7 +651,8 @@ public class SpriteBatch {
                        lastTexture = texture;\r
                        invTexWidth = 1.0f / texture.getWidth();\r
                        invTexHeight = 1.0f / texture.getHeight();\r
-               }\r
+               } else if (idx == vertices.length)\r
+                       renderMesh();\r
 \r
                final float fx2 = x + srcWidth;\r
                final float fy2 = y + srcHeight;\r
@@ -688,9 +680,6 @@ public class SpriteBatch {
                vertices[idx++] = color;\r
                vertices[idx++] = u2;\r
                vertices[idx++] = v;\r
-\r
-               if (idx == vertices.length)\r
-                       renderMesh();\r
        }\r
 \r
        public void draw(Texture texture, float[] spriteVertices, int offset,\r
diff --git a/gdx/src/com/badlogic/gdx/graphics/SpriteCache.java b/gdx/src/com/badlogic/gdx/graphics/SpriteCache.java
new file mode 100644 (file)
index 0000000..0f3dc52
--- /dev/null
@@ -0,0 +1,492 @@
+\r
+package com.badlogic.gdx.graphics;\r
+\r
+import java.nio.FloatBuffer;\r
+import java.util.ArrayList;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.VertexAttributes.Usage;\r
+import com.badlogic.gdx.graphics.glutils.ShaderProgram;\r
+import com.badlogic.gdx.math.Matrix4;\r
+import com.badlogic.gdx.utils.MathUtils;\r
+\r
+public class SpriteCache {\r
+       static private final float[] tempVertices = new float[Sprite.SPRITE_SIZE];\r
+\r
+       final Mesh mesh;\r
+       boolean drawing = false;\r
+       private final Matrix4 transformMatrix = new Matrix4();\r
+       private final Matrix4 projectionMatrix = new Matrix4();\r
+\r
+       private final Matrix4 combinedMatrix = new Matrix4();\r
+       ShaderProgram shader;\r
+\r
+       private CacheBuidler builder;\r
+\r
+       public SpriteCache () {\r
+               this(1000);\r
+       }\r
+\r
+       public SpriteCache (int size) {\r
+               this.mesh = new Mesh(true, size * 4, size * 6, new VertexAttribute(Usage.Position, 2, "a_position"), new VertexAttribute(\r
+                       Usage.ColorPacked, 4, "a_color"), new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoords"));\r
+\r
+               short[] indices = new short[size * 6];\r
+               int len = size * 6;\r
+               short j = 0;\r
+               for (int i = 0; i < len; i += 6, j += 4) {\r
+                       indices[i + 0] = (short)(j + 0);\r
+                       indices[i + 1] = (short)(j + 1);\r
+                       indices[i + 2] = (short)(j + 2);\r
+                       indices[i + 3] = (short)(j + 2);\r
+                       indices[i + 4] = (short)(j + 3);\r
+                       indices[i + 5] = (short)(j + 0);\r
+               }\r
+               mesh.setIndices(indices);\r
+\r
+               projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());\r
+\r
+               if (Gdx.graphics.isGL20Available()) createShader();\r
+       }\r
+\r
+       private void createShader () {\r
+               String vertexShader = "attribute vec4 a_position;\n" //\r
+                       + "attribute vec4 a_color;\n" //\r
+                       + "attribute vec2 a_texCoords;\n" //\r
+                       + "uniform mat4 u_projectionViewMatrix;\n" //\r
+                       + "varying vec4 v_color; \n" //\r
+                       + "varying vec2 v_texCoords;\n" //\r
+                       + "void main()\n" //\r
+                       + "{\n" //\r
+                       + "   v_color = a_color; \n" //\r
+                       + "   v_texCoords = a_texCoords; \n" //\r
+                       + "   gl_Position =  u_projectionViewMatrix * a_position;\n" //\r
+                       + "}";\r
+               String fragmentShader = "precision mediump float;\n" //\r
+                       + "varying vec4 v_color;\n" //\r
+                       + "varying vec2 v_texCoords;\n" //\r
+                       + "uniform sampler2D u_texture;\n" //\r
+                       + "void main()\n" //\r
+                       + "{\n" //\r
+                       + "  gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n" //\r
+                       + "}";\r
+               shader = new ShaderProgram(vertexShader, fragmentShader);\r
+               if (shader.isCompiled() == false) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog());\r
+       }\r
+\r
+       public void beginCache () {\r
+               if (builder != null) throw new IllegalStateException("endCache must be called before begin.");\r
+               builder = new CacheBuidler(mesh.getNumVertices() / 6);\r
+       }\r
+\r
+       public Cache endCache () {\r
+               if (builder == null) throw new IllegalStateException("beginCache mustbe called before endCache.");\r
+               Cache cache = builder.finish();\r
+               builder = null;\r
+               return cache;\r
+       }\r
+\r
+       public void add (Texture texture, float[] vertices, int offset, int length) {\r
+               if (builder == null) throw new IllegalStateException("beginCache mustbe called before add.");\r
+               builder.add(texture, vertices.length / Sprite.SPRITE_SIZE);\r
+\r
+               FloatBuffer buffer = mesh.getVerticesBuffer();\r
+               buffer.compact();\r
+               buffer.put(vertices);\r
+               buffer.flip();\r
+       }\r
+\r
+       public void add (Texture texture, float x, float y, int srcWidth, int srcHeight, float u, float v, float u2, float v2,\r
+               float color) {\r
+               final float fx2 = x + srcWidth;\r
+               final float fy2 = y + srcHeight;\r
+\r
+               tempVertices[0] = x;\r
+               tempVertices[1] = y;\r
+               tempVertices[2] = color;\r
+               tempVertices[3] = u;\r
+               tempVertices[4] = v;\r
+\r
+               tempVertices[5] = x;\r
+               tempVertices[6] = fy2;\r
+               tempVertices[7] = color;\r
+               tempVertices[8] = u;\r
+               tempVertices[9] = v2;\r
+\r
+               tempVertices[10] = fx2;\r
+               tempVertices[11] = fy2;\r
+               tempVertices[12] = color;\r
+               tempVertices[13] = u2;\r
+               tempVertices[14] = v2;\r
+\r
+               tempVertices[15] = fx2;\r
+               tempVertices[16] = y;\r
+               tempVertices[17] = color;\r
+               tempVertices[18] = u2;\r
+               tempVertices[19] = v;\r
+\r
+               add(texture, tempVertices, 0, 20);\r
+       }\r
+\r
+       public void add (Texture texture, float x, float y, int srcX, int srcY, int srcWidth, int srcHeight, Color tint) {\r
+               float invTexWidth = 1.0f / texture.getWidth();\r
+               float invTexHeight = 1.0f / texture.getHeight();\r
+               final float u = srcX * invTexWidth;\r
+               final float v = (srcY + srcHeight) * invTexHeight;\r
+               final float u2 = (srcX + srcWidth) * invTexWidth;\r
+               final float v2 = srcY * invTexHeight;\r
+               final float fx2 = x + srcWidth;\r
+               final float fy2 = y + srcHeight;\r
+\r
+               final float color = tint.toFloatBits();\r
+\r
+               tempVertices[0] = x;\r
+               tempVertices[1] = y;\r
+               tempVertices[2] = color;\r
+               tempVertices[3] = u;\r
+               tempVertices[4] = v;\r
+\r
+               tempVertices[5] = x;\r
+               tempVertices[6] = fy2;\r
+               tempVertices[7] = color;\r
+               tempVertices[8] = u;\r
+               tempVertices[9] = v2;\r
+\r
+               tempVertices[10] = fx2;\r
+               tempVertices[11] = fy2;\r
+               tempVertices[12] = color;\r
+               tempVertices[13] = u2;\r
+               tempVertices[14] = v2;\r
+\r
+               tempVertices[15] = fx2;\r
+               tempVertices[16] = y;\r
+               tempVertices[17] = color;\r
+               tempVertices[18] = u2;\r
+               tempVertices[19] = v;\r
+\r
+               add(texture, tempVertices, 0, 20);\r
+       }\r
+\r
+       public void add (Texture texture, float x, float y, float width, float height, int srcX, int srcY, int srcWidth,\r
+               int srcHeight, Color tint, boolean flipX, boolean flipY) {\r
+\r
+               float invTexWidth = 1.0f / texture.getWidth();\r
+               float invTexHeight = 1.0f / texture.getHeight();\r
+               float u = srcX * invTexWidth;\r
+               float v = (srcY + srcHeight) * invTexHeight;\r
+               float u2 = (srcX + srcWidth) * invTexWidth;\r
+               float v2 = srcY * invTexHeight;\r
+               final float fx2 = x + width;\r
+               final float fy2 = y + height;\r
+\r
+               if (flipX) {\r
+                       float tmp = u;\r
+                       u = u2;\r
+                       u2 = tmp;\r
+               }\r
+               if (flipY) {\r
+                       float tmp = v;\r
+                       v = v2;\r
+                       v2 = tmp;\r
+               }\r
+\r
+               final float color = tint.toFloatBits();\r
+\r
+               tempVertices[0] = x;\r
+               tempVertices[1] = y;\r
+               tempVertices[2] = color;\r
+               tempVertices[3] = u;\r
+               tempVertices[4] = v;\r
+\r
+               tempVertices[5] = x;\r
+               tempVertices[6] = fy2;\r
+               tempVertices[7] = color;\r
+               tempVertices[8] = u;\r
+               tempVertices[9] = v2;\r
+\r
+               tempVertices[10] = fx2;\r
+               tempVertices[11] = fy2;\r
+               tempVertices[12] = color;\r
+               tempVertices[13] = u2;\r
+               tempVertices[14] = v2;\r
+\r
+               tempVertices[15] = fx2;\r
+               tempVertices[16] = y;\r
+               tempVertices[17] = color;\r
+               tempVertices[18] = u2;\r
+               tempVertices[19] = v;\r
+\r
+               add(texture, tempVertices, 0, 20);\r
+       }\r
+\r
+       public void add (Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX,\r
+               float scaleY, float rotation, int srcX, int srcY, int srcWidth, int srcHeight, Color tint, boolean flipX, boolean flipY) {\r
+\r
+               // bottom left and top right corner points relative to origin\r
+               final float worldOriginX = x + originX;\r
+               final float worldOriginY = y + originY;\r
+               float fx = -originX;\r
+               float fy = -originY;\r
+               float fx2 = width - originX;\r
+               float fy2 = height - originY;\r
+\r
+               // scale\r
+               if (scaleX != 1 || scaleY != 1) {\r
+                       fx *= scaleX;\r
+                       fy *= scaleY;\r
+                       fx2 *= scaleX;\r
+                       fy2 *= scaleY;\r
+               }\r
+\r
+               // construct corner points, start from top left and go counter clockwise\r
+               final float p1x = fx;\r
+               final float p1y = fy;\r
+               final float p2x = fx;\r
+               final float p2y = fy2;\r
+               final float p3x = fx2;\r
+               final float p3y = fy2;\r
+               final float p4x = fx2;\r
+               final float p4y = fy;\r
+\r
+               float x1;\r
+               float y1;\r
+               float x2;\r
+               float y2;\r
+               float x3;\r
+               float y3;\r
+               float x4;\r
+               float y4;\r
+\r
+               // rotate\r
+               if (rotation != 0) {\r
+                       final float cos = MathUtils.cosDeg(rotation);\r
+                       final float sin = MathUtils.sinDeg(rotation);\r
+\r
+                       x1 = cos * p1x - sin * p1y;\r
+                       y1 = sin * p1x + cos * p1y;\r
+\r
+                       x2 = cos * p2x - sin * p2y;\r
+                       y2 = sin * p2x + cos * p2y;\r
+\r
+                       x3 = cos * p3x - sin * p3y;\r
+                       y3 = sin * p3x + cos * p3y;\r
+\r
+                       x4 = x1 + (x3 - x2);\r
+                       y4 = y3 - (y2 - y1);\r
+               } else {\r
+                       x1 = p1x;\r
+                       y1 = p1y;\r
+\r
+                       x2 = p2x;\r
+                       y2 = p2y;\r
+\r
+                       x3 = p3x;\r
+                       y3 = p3y;\r
+\r
+                       x4 = p4x;\r
+                       y4 = p4y;\r
+               }\r
+\r
+               x1 += worldOriginX;\r
+               y1 += worldOriginY;\r
+               x2 += worldOriginX;\r
+               y2 += worldOriginY;\r
+               x3 += worldOriginX;\r
+               y3 += worldOriginY;\r
+               x4 += worldOriginX;\r
+               y4 += worldOriginY;\r
+\r
+               float invTexWidth = 1.0f / texture.getWidth();\r
+               float invTexHeight = 1.0f / texture.getHeight();\r
+               float u = srcX * invTexWidth;\r
+               float v = (srcY + srcHeight) * invTexHeight;\r
+               float u2 = (srcX + srcWidth) * invTexWidth;\r
+               float v2 = srcY * invTexHeight;\r
+\r
+               if (flipX) {\r
+                       float tmp = u;\r
+                       u = u2;\r
+                       u2 = tmp;\r
+               }\r
+\r
+               if (flipY) {\r
+                       float tmp = v;\r
+                       v = v2;\r
+                       v2 = tmp;\r
+               }\r
+\r
+               final float color = tint.toFloatBits();\r
+\r
+               tempVertices[0] = x1;\r
+               tempVertices[1] = y1;\r
+               tempVertices[2] = color;\r
+               tempVertices[3] = u;\r
+               tempVertices[4] = v;\r
+\r
+               tempVertices[5] = x2;\r
+               tempVertices[6] = y2;\r
+               tempVertices[7] = color;\r
+               tempVertices[8] = u;\r
+               tempVertices[9] = v2;\r
+\r
+               tempVertices[10] = x3;\r
+               tempVertices[11] = y3;\r
+               tempVertices[12] = color;\r
+               tempVertices[13] = u2;\r
+               tempVertices[14] = v2;\r
+\r
+               tempVertices[15] = x4;\r
+               tempVertices[16] = y4;\r
+               tempVertices[17] = color;\r
+               tempVertices[18] = u2;\r
+               tempVertices[19] = v;\r
+\r
+               add(texture, tempVertices, 0, 20);\r
+       }\r
+\r
+       public void add (Sprite sprite) {\r
+               add(sprite.getTexture(), sprite.getVertices(), 0, 20);\r
+       }\r
+\r
+       public void begin () {\r
+               if (drawing) throw new IllegalStateException("end must be called before begin.");\r
+\r
+               if (Gdx.graphics.isGL20Available() == false) {\r
+                       GL10 gl = Gdx.gl10;\r
+                       gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());\r
+                       gl.glDisable(GL10.GL_LIGHTING);\r
+                       gl.glDisable(GL10.GL_DEPTH_TEST);\r
+                       gl.glDisable(GL10.GL_CULL_FACE);\r
+                       gl.glDepthMask(false);\r
+\r
+                       gl.glEnable(GL10.GL_TEXTURE_2D);\r
+                       // gl.glActiveTexture( GL10.GL_TEXTURE0 );\r
+\r
+                       gl.glMatrixMode(GL10.GL_PROJECTION);\r
+                       gl.glLoadMatrixf(projectionMatrix.val, 0);\r
+                       gl.glMatrixMode(GL10.GL_MODELVIEW);\r
+                       gl.glLoadMatrixf(transformMatrix.val, 0);\r
+               } else {\r
+                       combinedMatrix.set(projectionMatrix).mul(transformMatrix);\r
+\r
+                       GL20 gl = Gdx.gl20;\r
+                       gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());\r
+                       gl.glDisable(GL20.GL_DEPTH_TEST);\r
+                       gl.glDisable(GL20.GL_CULL_FACE);\r
+                       gl.glDepthMask(false);\r
+\r
+                       gl.glEnable(GL20.GL_TEXTURE_2D);\r
+                       // gl.glActiveTexture( GL20.GL_TEXTURE0 );\r
+\r
+                       shader.begin();\r
+                       shader.setUniformMatrix("u_projectionViewMatrix", combinedMatrix);\r
+                       shader.setUniformi("u_texture", 0);\r
+               }\r
+               drawing = true;\r
+       }\r
+\r
+       public void end () {\r
+               if (!drawing) throw new IllegalStateException("begin must be called before end.");\r
+               drawing = false;\r
+\r
+               if (Gdx.graphics.isGL20Available() == false) {\r
+                       GL10 gl = Gdx.gl10;\r
+                       gl.glDepthMask(true);\r
+                       gl.glDisable(GL10.GL_BLEND);\r
+                       gl.glDisable(GL10.GL_TEXTURE_2D);\r
+               } else {\r
+                       shader.end();\r
+                       GL20 gl = Gdx.gl20;\r
+                       gl.glDepthMask(true);\r
+                       gl.glDisable(GL20.GL_BLEND);\r
+                       gl.glDisable(GL20.GL_TEXTURE_2D);\r
+               }\r
+       }\r
+\r
+       public void draw (Cache cache) {\r
+               if (!drawing) throw new IllegalStateException("SpriteCache.begin must be called before draw.");\r
+\r
+               int offset = cache.offset;\r
+               Texture[] textures = cache.textures;\r
+               int[] counts = cache.counts;\r
+               if (Gdx.graphics.isGL20Available()) {\r
+                       for (int i = 0, n = textures.length; i < n; i++) {\r
+                               Texture texture = textures[i];\r
+                               int count = counts[i];\r
+                               texture.bind();\r
+                               mesh.render(shader, GL10.GL_TRIANGLES, offset, count);\r
+                               offset += count;\r
+                       }\r
+               } else {\r
+                       for (int i = 0, n = textures.length; i < n; i++) {\r
+                               Texture texture = textures[i];\r
+                               int count = counts[i];\r
+                               texture.bind();\r
+                               mesh.render(GL10.GL_TRIANGLES, offset, count);\r
+                               offset += count;\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void dispose () {\r
+               mesh.dispose();\r
+               if (shader != null) shader.dispose();\r
+       }\r
+\r
+       public Matrix4 getProjectionMatrix () {\r
+               return projectionMatrix;\r
+       }\r
+\r
+       public void setProjectionMatrix (Matrix4 projection) {\r
+               if (drawing) throw new IllegalStateException("Can't set the matrix within begin/end.");\r
+               projectionMatrix.set(projection);\r
+       }\r
+\r
+       public Matrix4 getTransformMatrix () {\r
+               return transformMatrix;\r
+       }\r
+\r
+       public void setTransformMatrix (Matrix4 transform) {\r
+               if (drawing) throw new IllegalStateException("Can't set the matrix within begin/end.");\r
+               transformMatrix.set(transform);\r
+       }\r
+\r
+       private class CacheBuidler {\r
+               private final ArrayList<Texture> textures = new ArrayList(8);\r
+               private final ArrayList<Integer> counts = new ArrayList(8);\r
+               private final int offset;\r
+\r
+               CacheBuidler (int offset) {\r
+                       this.offset = offset;\r
+               }\r
+\r
+               void add (Texture texture, int count) {\r
+                       count *= 6;\r
+                       int lastIndex = textures.size() - 1;\r
+                       if (lastIndex < 0 || textures.get(lastIndex) != texture) {\r
+                               textures.add(texture);\r
+                               counts.add(count);\r
+                       } else\r
+                               counts.set(lastIndex, counts.get(lastIndex) + count);\r
+               }\r
+\r
+               Cache finish () {\r
+                       Cache cache = new Cache(offset, textures.toArray(new Texture[textures.size()]), new int[counts.size()]);\r
+                       for (int i = 0, n = counts.size(); i < n; i++)\r
+                               cache.counts[i] = counts.get(i);\r
+                       return cache;\r
+               }\r
+       }\r
+\r
+       public class Cache {\r
+               final int offset;\r
+               final Texture[] textures;\r
+               final int[] counts;\r
+\r
+               Cache (int offset, Texture[] textures, int[] counts) {\r
+                       this.offset = offset;\r
+                       this.textures = textures;\r
+                       this.counts = counts;\r
+               }\r
+       }\r
+}\r
index 8361945..59a5465 100644 (file)
@@ -85,6 +85,7 @@ public class VertexBufferObject implements VertexData {
                        isDirect = true;\r
 //             }\r
                buffer = byteBuffer.asFloatBuffer();\r
+               buffer.flip();\r
                bufferHandle = createBufferObject();\r
                usage = isStatic ? GL11.GL_STATIC_DRAW : GL11.GL_DYNAMIC_DRAW;\r
        }\r
@@ -110,7 +111,7 @@ public class VertexBufferObject implements VertexData {
         */\r
        @Override\r
        public int getNumVertices() {\r
-               return byteBuffer.limit() / attributes.vertexSize;\r
+               return buffer.limit();\r
        }\r
 \r
        /**\r
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteCacheTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteCacheTest.java
new file mode 100644 (file)
index 0000000..a33e51b
--- /dev/null
@@ -0,0 +1,214 @@
+/*******************************************************************************\r
+ * Copyright 2010 Mario Zechner (contact@badlogicgames.com)\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
+ * License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"\r
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language\r
+ * governing permissions and limitations under the License.\r
+ ******************************************************************************/\r
+\r
+package com.badlogic.gdx.tests;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.InputProcessor;\r
+import com.badlogic.gdx.graphics.Color;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.Pixmap;\r
+import com.badlogic.gdx.graphics.Pixmap.Format;\r
+import com.badlogic.gdx.graphics.Sprite;\r
+import com.badlogic.gdx.graphics.SpriteCache;\r
+import com.badlogic.gdx.graphics.Texture;\r
+import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+import com.badlogic.gdx.tests.utils.GdxTest;\r
+\r
+public class SpriteCacheTest extends GdxTest implements InputProcessor {\r
+       int SPRITES = 400 / 2;\r
+\r
+       long startTime = System.nanoTime();\r
+       int frames = 0;\r
+\r
+       Texture texture;\r
+       Texture texture2;\r
+       SpriteCache spriteCache;\r
+       SpriteCache.Cache normalCache, spritesCache;\r
+       int renderMethod = 0;\r
+\r
+       @Override public void render () {\r
+               if (renderMethod == 0) renderNormal();\r
+               ;\r
+               if (renderMethod == 1) renderSprites();\r
+\r
+               Gdx.input.processEvents(this);\r
+       }\r
+\r
+       private void renderNormal () {\r
+               GL10 gl = Gdx.graphics.getGL10();\r
+               gl.glClearColor(0.7f, 0.7f, 0.7f, 1);\r
+               gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
+\r
+               float begin = 0;\r
+               float end = 0;\r
+               float draw1 = 0;\r
+\r
+               long start = System.nanoTime();\r
+               spriteCache.begin();\r
+               begin = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               start = System.nanoTime();\r
+               spriteCache.draw(normalCache);\r
+               draw1 = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               start = System.nanoTime();\r
+               spriteCache.end();\r
+               end = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               if (System.nanoTime() - startTime > 1000000000) {\r
+// Gdx.app.log( "SpriteBatch", "fps: " + frames + ", render calls: " + spriteBatch.renderCalls + ", " + begin + ", " + draw1 +\r
+// ", " + draw2 + ", " + drawText + ", " + end );\r
+                       frames = 0;\r
+                       startTime = System.nanoTime();\r
+               }\r
+               frames++;\r
+       }\r
+\r
+       private void renderSprites () {\r
+               GL10 gl = Gdx.graphics.getGL10();\r
+               gl.glClearColor(0.7f, 0.7f, 0.7f, 1);\r
+               gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
+\r
+               float begin = 0;\r
+               float end = 0;\r
+               float draw1 = 0;\r
+               float draw2 = 0;\r
+               float drawText = 0;\r
+\r
+               long start = System.nanoTime();\r
+               spriteCache.begin();\r
+               begin = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               start = System.nanoTime();\r
+               spriteCache.draw(spritesCache);\r
+               draw1 = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               start = System.nanoTime();\r
+               spriteCache.end();\r
+               end = (System.nanoTime() - start) / 1000000000.0f;\r
+\r
+               if (System.nanoTime() - startTime > 1000000000) {\r
+// Gdx.app.log( "SpriteBatch", "fps: " + frames + ", render calls: " + spriteBatch.renderCalls + ", " + begin + ", " + draw1 +\r
+// ", " + draw2 + ", " + drawText + ", " + end );\r
+                       frames = 0;\r
+                       startTime = System.nanoTime();\r
+               }\r
+               frames++;\r
+       }\r
+\r
+       @Override public void create () {\r
+               spriteCache = new SpriteCache(1000);\r
+\r
+               Pixmap pixmap = Gdx.graphics.newPixmap(Gdx.files.getFileHandle("data/badlogicsmall.jpg", FileType.Internal));\r
+               texture = Gdx.graphics.newUnmanagedTexture(32, 32, Format.RGB565, TextureFilter.Linear, TextureFilter.Linear,\r
+                       TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);\r
+               texture.draw(pixmap, 0, 0);\r
+               pixmap.dispose();\r
+\r
+               pixmap = Gdx.graphics.newPixmap(32, 32, Format.RGBA8888);\r
+               pixmap.setColor(1, 1, 0, 0.5f);\r
+               pixmap.fill();\r
+               texture2 = Gdx.graphics.newUnmanagedTexture(pixmap, TextureFilter.Nearest, TextureFilter.Nearest, TextureWrap.ClampToEdge,\r
+                       TextureWrap.ClampToEdge);\r
+               pixmap.dispose();\r
+\r
+               float sprites[] = new float[SPRITES * 6];\r
+               float sprites2[] = new float[SPRITES * 6];\r
+               Sprite[] sprites3 = new Sprite[SPRITES * 2];\r
+\r
+               for (int i = 0; i < sprites.length; i += 6) {\r
+                       sprites[i] = (int)(Math.random() * (Gdx.graphics.getWidth() - 32));\r
+                       sprites[i + 1] = (int)(Math.random() * (Gdx.graphics.getHeight() - 32));\r
+                       sprites[i + 2] = 0;\r
+                       sprites[i + 3] = 0;\r
+                       sprites[i + 4] = 32;\r
+                       sprites[i + 5] = 32;\r
+                       sprites2[i] = (int)(Math.random() * (Gdx.graphics.getWidth() - 32));\r
+                       sprites2[i + 1] = (int)(Math.random() * (Gdx.graphics.getHeight() - 32));\r
+                       sprites2[i + 2] = 0;\r
+                       sprites2[i + 3] = 0;\r
+                       sprites2[i + 4] = 32;\r
+                       sprites2[i + 5] = 32;\r
+               }\r
+\r
+               for (int i = 0; i < SPRITES * 2; i++) {\r
+                       int x = (int)(Math.random() * (Gdx.graphics.getWidth() - 32));\r
+                       int y = (int)(Math.random() * (Gdx.graphics.getHeight() - 32));\r
+\r
+                       if (i >= SPRITES)\r
+                               sprites3[i] = new Sprite(texture2, 32, 32);\r
+                       else\r
+                               sprites3[i] = new Sprite(texture, 32, 32);\r
+                       sprites3[i].setPosition(x, y);\r
+                       sprites3[i].setOrigin(16, 16);\r
+               }\r
+\r
+               float scale = 1;\r
+               float angle = 0;\r
+\r
+               spriteCache.beginCache();\r
+               for (int i = 0; i < sprites.length; i += 6)\r
+                       spriteCache.add(texture, sprites[i], sprites[i + 1], 16, 16, 32, 32, scale, scale, angle, 0, 0, 32, 32, Color.WHITE,\r
+                               false, false);\r
+               for (int i = 0; i < sprites2.length; i += 6)\r
+                       spriteCache.add(texture2, sprites2[i], sprites2[i + 1], 16, 16, 32, 32, scale, scale, angle, 0, 0, 32, 32, Color.WHITE,\r
+                               false, false);\r
+               normalCache = spriteCache.endCache();\r
+\r
+               spriteCache.beginCache();\r
+               for (int i = 0; i < SPRITES; i++) {\r
+                       sprites3[i].setRotation(angle);\r
+                       sprites3[i].setScale(scale);\r
+                       spriteCache.add(sprites3[i]);\r
+               }\r
+               for (int i = SPRITES; i < SPRITES << 1; i++) {\r
+                       sprites3[i].setRotation(angle);\r
+                       sprites3[i].setScale(scale);\r
+                       spriteCache.add(sprites3[i]);\r
+               }\r
+               spritesCache = spriteCache.endCache();\r
+       }\r
+\r
+       @Override public boolean keyDown (int keycode) {\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean keyTyped (char character) {\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean keyUp (int keycode) {\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean touchDown (int x, int y, int pointer) {\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean touchDragged (int x, int y, int pointer) {\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean touchUp (int x, int y, int pointer) {\r
+               renderMethod = (renderMethod + 1) % 2;\r
+               return false;\r
+       }\r
+\r
+       @Override public boolean needsGL20 () {\r
+               return false;\r
+       }\r
+\r
+}\r
index 1427a53..8db8146 100644 (file)
@@ -35,6 +35,7 @@ import com.badlogic.gdx.tests.SoundTest;
 import com.badlogic.gdx.tests.SpriteBatchRotationTest;\r
 import com.badlogic.gdx.tests.SpriteBatchShaderTest;\r
 import com.badlogic.gdx.tests.SpriteBatchTest;\r
+import com.badlogic.gdx.tests.SpriteCacheTest;\r
 import com.badlogic.gdx.tests.StageTest;\r
 import com.badlogic.gdx.tests.TerrainTest;\r
 import com.badlogic.gdx.tests.TextureRenderTest;\r
@@ -87,6 +88,7 @@ public class GdxTests
                Pong.class,\r
                SimpleTest.class,\r
                SoundTest.class,\r
+               SpriteCacheTest.class,\r
                SpriteBatchRotationTest.class,\r
                SpriteBatchShaderTest.class,\r
                SpriteBatchTest.class,\r