Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* Copyright 2011 See AUTHORS file.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
- package com.esotericsoftware.spine;
- import com.badlogic.gdx.Gdx;
- import com.badlogic.gdx.graphics.Color;
- import com.badlogic.gdx.graphics.GL10;
- import com.badlogic.gdx.graphics.GL11;
- import com.badlogic.gdx.graphics.GL20;
- import com.badlogic.gdx.graphics.GLCommon;
- import com.badlogic.gdx.graphics.Mesh;
- import com.badlogic.gdx.graphics.Mesh.VertexDataType;
- import com.badlogic.gdx.graphics.Texture;
- import com.badlogic.gdx.graphics.VertexAttribute;
- import com.badlogic.gdx.graphics.VertexAttributes.Usage;
- import com.badlogic.gdx.graphics.glutils.ShaderProgram;
- import com.badlogic.gdx.math.Matrix4;
- import com.badlogic.gdx.utils.Disposable;
- /** <p>
- * A SpineSpriteBatch is used to draw 2D rectangles that reference a texture (region). The class will batch the drawing commands and
- * optimize them for processing by the GPU.
- * </p>
- *
- * <p>
- * To draw something with a SpineSpriteBatch one has to first call the {@link SpineSpriteBatch#begin()} method which will setup appropriate
- * render states. When you are done with drawing you have to call {@link SpineSpriteBatch#end()} which will actually draw the things
- * you specified.
- * </p>
- *
- * <p>
- * All drawing commands of the SpineSpriteBatch operate in screen coordinates. The screen coordinate system has an x-axis pointing to
- * the right, an y-axis pointing upwards and the origin is in the lower left corner of the screen. You can also provide your own
- * transformation and projection matrices if you so wish.
- * </p>
- *
- * <p>
- * A SpineSpriteBatch is managed. In case the OpenGL context is lost all OpenGL resources a SpineSpriteBatch uses internally get
- * invalidated. A context is lost when a user switches to another application or receives an incoming call on Android. A
- * SpineSpriteBatch will be automatically reloaded after the OpenGL context is restored.
- * </p>
- *
- * <p>
- * A SpineSpriteBatch is a pretty heavy object so you should only ever have one in your program.
- * </p>
- *
- * <p>
- * A SpineSpriteBatch works with OpenGL ES 1.x and 2.0. In the case of a 2.0 context it will use its own custom shader to draw all
- * provided sprites. You can set your own custom shader via {@link #setShader(ShaderProgram)}.
- * </p>
- *
- * <p>
- * A SpineSpriteBatch has to be disposed if it is no longer used.
- * </p>
- *
- * @author mzechner */
- public class SpineSpriteBatch implements Disposable {
- private Mesh mesh;
- private Mesh[] buffers;
- private Texture lastTexture = null;
- private int idx = 0;
- private int currBufferIdx = 0;
- private final float[] vertices;
- private final Matrix4 transformMatrix = new Matrix4();
- private final Matrix4 projectionMatrix = new Matrix4();
- private final Matrix4 combinedMatrix = new Matrix4();
- private boolean drawing = false;
- private boolean blendingDisabled = false;
- private int blendSrcFunc = GL11.GL_SRC_ALPHA;
- private int blendDstFunc = GL11.GL_ONE_MINUS_SRC_ALPHA;
- private final ShaderProgram shader;
- private boolean ownsShader;
- float color = Color.WHITE.toFloatBits();
- /** number of render calls since last {@link #begin()} **/
- public int renderCalls = 0;
- /** number of rendering calls ever, will not be reset, unless it's done manually **/
- public int totalRenderCalls = 0;
- /** the maximum number of sprites rendered in one batch so far **/
- public int maxSpritesInBatch = 0;
- private ShaderProgram customShader = null;
- /** Constructs a new SpineSpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
- * point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
- * respect to the screen resolution. */
- public SpineSpriteBatch () {
- this(1000);
- }
- /** Constructs a SpineSpineSpriteBatch with the specified size and (if GL2) the default shader. See
- * {@link #SpineSpineSpriteBatch(int, ShaderProgram)}. */
- public SpineSpriteBatch (int size) {
- this(size, null);
- }
- /** <p>
- * Constructs a new SpineSpineSpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
- * point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
- * respect to the screen resolution.
- * </p>
- *
- * <p>
- * The size parameter specifies the maximum size of a single batch in number of sprites
- * </p>
- *
- * <p>
- * The defaultShader specifies the shader to use. Note that the names for uniforms for this default shader are different than
- * the ones expect for shaders set with {@link #setShader(ShaderProgram)}. See the {@link #createDefaultShader()} method.
- * </p>
- *
- * @param size the batch size in number of sprites
- * @param defaultShader the default shader to use. This is not owned by the SpineSpineSpriteBatch and must be disposed separately. */
- public SpineSpriteBatch (int size, ShaderProgram defaultShader) {
- this(size, 1, defaultShader);
- }
- /** Constructs a SpineSpineSpriteBatch with the specified size and number of buffers and (if GL2) the default shader. See
- * {@link #SpineSpineSpriteBatch(int, int, ShaderProgram)}. */
- public SpineSpriteBatch (int size, int buffers) {
- this(size, buffers, null);
- }
- /** <p>
- * Constructs a new SpineSpriteBatch. Sets the projection matrix to an orthographic projection with y-axis point upwards, x-axis
- * point to the right and the origin being in the bottom left corner of the screen. The projection will be pixel perfect with
- * respect to the screen resolution.
- * </p>
- *
- * <p>
- * The size parameter specifies the maximum size of a single batch in number of sprites
- * </p>
- *
- * <p>
- * The defaultShader specifies the shader to use. Note that the names for uniforms for this default shader are different than
- * the ones expect for shaders set with {@link #setShader(ShaderProgram)}. See the {@link #createDefaultShader()} method.
- * </p>
- *
- * @param size the batch size in number of sprites
- * @param buffers the number of buffers to use. only makes sense with VBOs. This is an expert function.
- * @param defaultShader the default shader to use. This is not owned by the SpineSpriteBatch and must be disposed separately. */
- public SpineSpriteBatch (int size, int buffers, ShaderProgram defaultShader) {
- this.buffers = new Mesh[buffers];
- for (int i = 0; i < buffers; i++) {
- this.buffers[i] = new Mesh(VertexDataType.VertexArray, false, size * 4, size * 6, new VertexAttribute(Usage.Position, 2,
- ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE),
- new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0"),
- new VertexAttribute(Usage.Generic, 1, "a_rot"));
- }
- projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
- vertices = new float[size * 24];
- int len = size * 6;
- short[] indices = new short[len];
- short j = 0;
- for (int i = 0; i < len; i += 6, j += 4) {
- indices[i + 0] = (short)(j + 0);
- indices[i + 1] = (short)(j + 1);
- indices[i + 2] = (short)(j + 2);
- indices[i + 3] = (short)(j + 2);
- indices[i + 4] = (short)(j + 3);
- indices[i + 5] = (short)(j + 0);
- }
- for (int i = 0; i < buffers; i++) {
- this.buffers[i].setIndices(indices);
- }
- mesh = this.buffers[0];
- if (Gdx.graphics.isGL20Available() && defaultShader == null) {
- shader = createDefaultShader();
- ownsShader = true;
- } else
- shader = defaultShader;
- }
- /** Returns a new instance of the default shader used by SpineSpriteBatch for GL2 when no shader is specified. */
- static public ShaderProgram createDefaultShader () {
- String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
- + "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
- + "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
- + "attribute float a_rot;\n" //
- + "uniform mat4 u_projTrans;\n" //
- + "varying vec4 v_color;\n" //
- + "varying vec2 v_texCoords;\n" //
- + "varying vec3 v_lightDir;\n" //
- + "const vec3 lightDir = normalize(vec3(-0.0, -1.0, 0.2));\n" //
- + "\n" //
- + "void main()\n" //
- + "{\n" //
- + " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
- + " vec2 rad = vec2(-sin(a_rot), cos(a_rot));\n" //
- + " v_lightDir = vec3(mat2(rad.y, -rad.x, rad.x, rad.y) * lightDir.xy,lightDir.z);\n" //
- + " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
- + " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
- + "}\n";
- String fragmentShader = "#ifdef GL_ES\n" //
- + "#define LOWP lowp\n" //
- + "precision mediump float;\n" //
- + "#else\n" //
- + "#define LOWP \n" //
- + "#endif\n" //
- + "varying LOWP vec4 v_color;\n" //
- + "varying vec2 v_texCoords;\n" //
- + "varying vec3 v_lightDir;\n" //
- + "uniform sampler2D u_texture;\n" //
- + "uniform sampler2D u_texture2;\n" //
- + "void main()\n"//
- + "{\n" //
- + " vec4 albedo = texture2D(u_texture, v_texCoords);\n" //
- + " vec3 normal = texture2D(u_texture2, v_texCoords).xyz * 2.0 - 1.0;\n" //
- + " float light = clamp(dot(normalize(v_lightDir), normal), 0.0, 1.0) ;\n" //
- + " gl_FragColor = v_color * vec4((light*light * albedo.rgb), albedo.a);\n" //
- + "}";
- ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
- if (shader.isCompiled() == false) throw new IllegalArgumentException("couldn't compile shader: " + shader.getLog());
- return shader;
- }
- /** Sets up the SpineSpriteBatch for drawing. This will disable depth buffer writting. It enables blending and texturing. If you have
- * more texture units enabled than the first one you have to disable them before calling this. Uses a screen coordinate system
- * by default where everything is given in pixels. You can specify your own projection and modelview matrices via
- * {@link #setProjectionMatrix(Matrix4)} and {@link #setTransformMatrix(Matrix4)}. */
- public void begin () {
- if (drawing) throw new IllegalStateException("you have to call SpineSpriteBatch.end() first");
- renderCalls = 0;
- Gdx.gl.glDepthMask(false);
- if (Gdx.graphics.isGL20Available()) {
- if (customShader != null)
- customShader.begin();
- else
- shader.begin();
- } else {
- Gdx.gl.glEnable(GL10.GL_TEXTURE_2D);
- }
- setupMatrices();
- idx = 0;
- lastTexture = null;
- drawing = true;
- }
- /** Finishes off rendering. Enables depth writes, disables blending and texturing. Must always be called after a call to
- * {@link #begin()} */
- public void end () {
- if (!drawing) throw new IllegalStateException("SpineSpriteBatch.begin must be called before end.");
- if (idx > 0) renderMesh();
- lastTexture = null;
- idx = 0;
- drawing = false;
- GLCommon gl = Gdx.gl;
- gl.glDepthMask(true);
- if (isBlendingEnabled()) gl.glDisable(GL10.GL_BLEND);
- if (Gdx.graphics.isGL20Available()) {
- if (customShader != null)
- customShader.end();
- else
- shader.end();
- } else {
- gl.glDisable(GL10.GL_TEXTURE_2D);
- }
- }
- /** Sets the color used to tint images when they are added to the SpineSpriteBatch. Default is {@link Color#WHITE}. */
- public void setColor (Color tint) {
- color = tint.toFloatBits();
- }
- /** Draws a rectangle using the given vertices. There must be 4 vertices, each made up of 5 elements in this order: x, y, color,
- * u, v. The {@link #getColor()} from the SpineSpriteBatch is not applied. */
- public void draw (Texture texture, float[] spriteVertices, int offset, int length) {
- if (!drawing) throw new IllegalStateException("SpineSpriteBatch.begin must be called before draw.");
- if (texture != lastTexture) {
- switchTexture(texture);
- }
- int remainingVertices = vertices.length - idx;
- if (remainingVertices == 0) {
- renderMesh();
- remainingVertices = vertices.length;
- }
- int vertexCount = Math.min(remainingVertices, length - offset);
- System.arraycopy(spriteVertices, offset, vertices, idx, vertexCount);
- offset += vertexCount;
- idx += vertexCount;
- while (offset < length) {
- renderMesh();
- vertexCount = Math.min(vertices.length, length - offset);
- System.arraycopy(spriteVertices, offset, vertices, 0, vertexCount);
- offset += vertexCount;
- idx += vertexCount;
- }
- }
- /** Causes any pending sprites to be rendered, without ending the SpineSpriteBatch. */
- public void flush () {
- renderMesh();
- }
- private void renderMesh () {
- if (idx == 0) return;
- renderCalls++;
- totalRenderCalls++;
- int spritesInBatch = idx / 20;
- if (spritesInBatch > maxSpritesInBatch) maxSpritesInBatch = spritesInBatch;
- lastTexture.bind();
- mesh.setVertices(vertices, 0, idx);
- mesh.getIndicesBuffer().position(0);
- mesh.getIndicesBuffer().limit(spritesInBatch * 6);
- if (blendingDisabled) {
- Gdx.gl.glDisable(GL20.GL_BLEND);
- } else {
- Gdx.gl.glEnable(GL20.GL_BLEND);
- if (blendSrcFunc != -1) Gdx.gl.glBlendFunc(blendSrcFunc, blendDstFunc);
- }
- if (Gdx.graphics.isGL20Available()) {
- if (customShader != null)
- mesh.render(customShader, GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
- else
- mesh.render(shader, GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
- } else {
- mesh.render(GL10.GL_TRIANGLES, 0, spritesInBatch * 6);
- }
- idx = 0;
- currBufferIdx++;
- if (currBufferIdx == buffers.length) currBufferIdx = 0;
- mesh = buffers[currBufferIdx];
- }
- /** Disables blending for drawing sprites. */
- public void disableBlending () {
- if (blendingDisabled) return;
- renderMesh();
- blendingDisabled = true;
- }
- /** Enables blending for sprites */
- public void enableBlending () {
- if (!blendingDisabled) return;
- renderMesh();
- blendingDisabled = false;
- }
- /** Sets the blending function to be used when rendering sprites.
- *
- * @param srcFunc the source function, e.g. GL11.GL_SRC_ALPHA. If set to -1, SpineSpriteBatch won't change the blending function.
- * @param dstFunc the destination function, e.g. GL11.GL_ONE_MINUS_SRC_ALPHA */
- public void setBlendFunction (int srcFunc, int dstFunc) {
- renderMesh();
- blendSrcFunc = srcFunc;
- blendDstFunc = dstFunc;
- }
- /** Disposes all resources associated with this SpineSpriteBatch */
- public void dispose () {
- for (int i = 0; i < buffers.length; i++)
- buffers[i].dispose();
- if (ownsShader && shader != null) shader.dispose();
- }
- /** Returns the current projection matrix. Changing this will result in undefined behaviour.
- *
- * @return the currently set projection matrix */
- public Matrix4 getProjectionMatrix () {
- return projectionMatrix;
- }
- /** Returns the current transform matrix. Changing this will result in undefined behaviour.
- *
- * @return the currently set transform matrix */
- public Matrix4 getTransformMatrix () {
- return transformMatrix;
- }
- /** Sets the projection matrix to be used by this SpineSpriteBatch. If this is called inside a {@link #begin()}/{@link #end()} block.
- * the current batch is flushed to the gpu.
- *
- * @param projection the projection matrix */
- public void setProjectionMatrix (Matrix4 projection) {
- if (drawing) flush();
- projectionMatrix.set(projection);
- if (drawing) setupMatrices();
- }
- /** Sets the transform matrix to be used by this SpineSpriteBatch. If this is called inside a {@link #begin()}/{@link #end()} block.
- * the current batch is flushed to the gpu.
- *
- * @param transform the transform matrix */
- public void setTransformMatrix (Matrix4 transform) {
- if (drawing) flush();
- transformMatrix.set(transform);
- if (drawing) setupMatrices();
- }
- private void setupMatrices () {
- if (!Gdx.graphics.isGL20Available()) {
- GL10 gl = Gdx.gl10;
- gl.glMatrixMode(GL10.GL_PROJECTION);
- gl.glLoadMatrixf(projectionMatrix.val, 0);
- gl.glMatrixMode(GL10.GL_MODELVIEW);
- gl.glLoadMatrixf(transformMatrix.val, 0);
- } else {
- combinedMatrix.set(projectionMatrix).mul(transformMatrix);
- if (customShader != null) {
- customShader.setUniformMatrix("u_projTrans", combinedMatrix);
- customShader.setUniformi("u_texture", 0);
- customShader.setUniformi("u_texture2", 1);
- } else {
- shader.setUniformMatrix("u_projTrans", combinedMatrix);
- shader.setUniformi("u_texture", 0);
- shader.setUniformi("u_texture2", 1);
- }
- }
- }
- private void switchTexture (Texture texture) {
- renderMesh();
- lastTexture = texture;
- }
- /** Sets the shader to be used in a GLES 2.0 environment. Vertex position attribute is called "a_position", the texture
- * coordinates attribute is called called "a_texCoord0", the color attribute is called "a_color". See
- * {@link ShaderProgram#POSITION_ATTRIBUTE}, {@link ShaderProgram#COLOR_ATTRIBUTE} and {@link ShaderProgram#TEXCOORD_ATTRIBUTE}
- * which gets "0" appened to indicate the use of the first texture unit. The combined transform and projection matrx is is
- * uploaded via a mat4 uniform called "u_projTrans". The texture sampler is passed via a uniform called "u_texture".</p>
- *
- * Call this method with a null argument to use the default shader.</p>
- *
- * This method will flush the batch before setting the new shader, you can call it in between {@link #begin()} and
- * {@link #end()}.
- *
- * @param shader the {@link ShaderProgram} or null to use the default shader. */
- public void setShader (ShaderProgram shader) {
- if (drawing) {
- flush();
- if (customShader != null)
- customShader.end();
- else
- this.shader.end();
- }
- customShader = shader;
- if (drawing) {
- if (customShader != null)
- customShader.begin();
- else
- this.shader.begin();
- setupMatrices();
- }
- }
- /** @return whether blending for sprites is enabled */
- public boolean isBlendingEnabled () {
- return !blendingDisabled;
- }
- static public final int X1 = 0;
- static public final int Y1 = 1;
- static public final int C1 = 2;
- static public final int U1 = 3;
- static public final int V1 = 4;
- static public final int R1 = 5;
- static public final int X2 = 6;
- static public final int Y2 = 7;
- static public final int C2 = 8;
- static public final int U2 = 9;
- static public final int V2 = 10;
- static public final int R2 = 11;
- static public final int X3 = 12;
- static public final int Y3 = 13;
- static public final int C3 = 14;
- static public final int U3 = 15;
- static public final int V3 = 16;
- static public final int R3 = 17;
- static public final int X4 = 18;
- static public final int Y4 = 19;
- static public final int C4 = 20;
- static public final int U4 = 21;
- static public final int V4 = 22;
- static public final int R4 = 23;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement