com.kauridev.lunarfever.graphics.SpriteBatch.java Source code

Java tutorial

Introduction

Here is the source code for com.kauridev.lunarfever.graphics.SpriteBatch.java

Source

/*
 * This file is part of the lunar-fever package.
 *
 * Copyright (c) 2014 Eric Fritz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.kauridev.lunarfever.graphics;

import com.kauridev.lunarfever.util.FileUtil;
import com.kauridev.lunarfever.util.MathUtil;
import java.util.Arrays;
import java.util.List;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;

/**
 * @author Eric Fritz
 */
public class SpriteBatch {
    private static final int VERTEX_SIZE = 6;

    public static final String U_TEXTURE = "u_texture";
    public static final String U_PROJ_VIEW = "u_projView";

    public static final String ATTR_COLOR = "Color";
    public static final String ATTR_POSITION = "Position";
    public static final String ATTR_TEXCOORD = "TexCoord";

    private static final List<VertexAttribute> ATTRIBUTES = Arrays.asList(new VertexAttribute(0, ATTR_POSITION, 2),
            new VertexAttribute(1, ATTR_COLOR, 4), new VertexAttribute(2, ATTR_TEXCOORD, 2));
    private static ShaderProgram defaultShader;

    private Matrix4f viewMatrix = new Matrix4f();
    private Matrix4f projMatrix = new Matrix4f();
    private Matrix4f tempMatrix = new Matrix4f();

    private Texture lastTexture;
    private ShaderProgram program;

    private int index;
    private float[] vertices;
    private VertexArray data;

    private boolean drawing = false;

    private static ShaderProgram getDefaultShader() {
        return defaultShader == null
                ? (defaultShader = new ShaderProgram(FileUtil.readFileContents("assets/shaders/default_vert.glsl"),
                        FileUtil.readFileContents("assets/shaders/default_frag.glsl"), ATTRIBUTES))
                : defaultShader;
    }

    public SpriteBatch() {
        this(1000);
    }

    public SpriteBatch(int size) {
        this(getDefaultShader(), size);
    }

    public SpriteBatch(ShaderProgram program) {
        this(program, 1000);
    }

    public SpriteBatch(ShaderProgram program, int size) {
        this.program = program;
        this.vertices = new float[size * VERTEX_SIZE * 8];
        this.data = new VertexArray(size * VERTEX_SIZE, ATTRIBUTES);

        MathUtil.toOrtho(projMatrix, 0, Display.getWidth(), 0, Display.getHeight());
    }

    public Matrix4f getProjectionMatrix() {
        return projMatrix;
    }

    public void setProjectionMatrix(Matrix4f projMatrix) {
        if (drawing) {
            flush();
        }

        this.projMatrix = projMatrix;

        if (drawing) {
            setupMatrices();
        }
    }

    public Matrix4f getViewMatrix() {
        return viewMatrix;
    }

    public void setViewMatrix(Matrix4f viewMatrix) {
        if (drawing) {
            flush();
        }

        this.viewMatrix = viewMatrix;

        if (drawing) {
            setupMatrices();
        }
    }

    public Matrix4f getCombinedMatrix() {
        return Matrix4f.mul(Matrix4f.transpose(projMatrix, tempMatrix), viewMatrix, tempMatrix);
    }

    private void setupMatrices() {
        program.setUniformi(U_TEXTURE, 0);
        program.setUniformMatrix(U_PROJ_VIEW, false, getCombinedMatrix());
    }

    public void begin() {
        if (drawing) {
            throw new IllegalStateException();
        }

        setupMatrices();

        drawing = true;
        program.use();
        lastTexture = null;
    }

    public void end() {
        if (!drawing) {
            throw new IllegalStateException();
        }

        drawing = false;
        flush();
    }

    public void flush() {
        if (index > 0) {
            data.put(vertices, 0, index);

            data.flip();
            render();
            index = 0;
            data.clear();
        }
    }

    private void render() {
        if (lastTexture != null) {
            lastTexture.bind();
        }

        data.bind();
        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, index / 8);
        data.unbind();
    }

    public void draw(TextureInterface tex, float x, float y) {
        draw(tex, x, y, Color.WHITE);
    }

    public void draw(TextureInterface tex, float x, float y, Color color) {
        draw(tex, x, y, color, 0, 0, 0);
    }

    public void draw(TextureInterface tex, float x, float y, Color color, float rotation, float originX,
            float originY) {
        draw(tex, x, y, color, rotation, originX, originY, SpriteEffects.None);
    }

    public void draw(TextureInterface tex, float x, float y, Color color, float rotation, float originX,
            float originY, SpriteEffects effects) {
        draw(tex, x, y, color, rotation, originX, originY, effects, 1, 1);
    }

    public void draw(TextureInterface tex, float x, float y, Color color, float rotation, float originX,
            float originY, SpriteEffects effects, float scaleX, float scaleY) {
        draw(tex, x, y, tex.getWidth(), tex.getHeight(), color, rotation, originX, originY, effects, scaleX, scaleY,
                tex.getU(), tex.getV(), tex.getU2(), tex.getV2());
    }

    public void draw(TextureInterface tex, float x, float y, float width, float height) {
        draw(tex, x, y, width, height, Color.WHITE);
    }

    public void draw(TextureInterface tex, float x, float y, float width, float height, Color color) {
        draw(tex, x, y, width, height, color, 0, 0, 0);
    }

    public void draw(TextureInterface tex, float x, float y, float width, float height, Color color, float rotation,
            float originX, float originY) {
        draw(tex, x, y, width, height, color, rotation, originX, originY, SpriteEffects.None);
    }

    public void draw(TextureInterface tex, float x, float y, float width, float height, Color color, float rotation,
            float originX, float originY, SpriteEffects effects) {
        draw(tex, x, y, width, height, color, rotation, originX, originY, effects, 1, 1, tex.getU(), tex.getV(),
                tex.getU2(), tex.getV2());
    }

    private void draw(TextureInterface tex, float x, float y, float width, float height, Color color,
            float rotation, float originX, float originY, SpriteEffects effects, float scaleX, float scaleY,
            float u1, float v1, float u2, float v2) {
        if (!drawing) {
            throw new IllegalStateException();
        }

        checkFlush(tex);

        final float worldOriginX = x + originX;
        final float worldOriginY = y + originY;

        float fx1 = -originX;
        float fy1 = -originY;
        float fx2 = width - originX;
        float fy2 = height - originY;

        if (scaleX != 1 || scaleY != 1) {
            fx1 *= scaleX;
            fy1 *= scaleY;
            fx2 *= scaleX;
            fy2 *= scaleY;
        }

        float x1;
        float y1;
        float x2;
        float y2;
        float x3;
        float y3;
        float x4;
        float y4;

        if (rotation != 0) {
            final float cos = (float) Math.cos(rotation);
            final float sin = (float) Math.sin(rotation);

            x1 = (cos * fx1 - sin * fy1);
            y1 = (sin * fx1 + cos * fy1);

            x2 = (cos * fx2 - sin * fy1);
            y2 = (sin * fx2 + cos * fy1);

            x3 = (cos * fx2 - sin * fy2);
            y3 = (sin * fx2 + cos * fy2);

            x4 = (cos * fx1 - sin * fy2);
            y4 = (sin * fx1 + cos * fy2);
        } else {
            x1 = fx1;
            y1 = fy1;

            x2 = fx2;
            y2 = fy1;

            x3 = fx2;
            y3 = fy2;

            x4 = fx1;
            y4 = fy2;
        }

        x1 += worldOriginX;
        y1 += worldOriginY;
        x2 += worldOriginX;
        y2 += worldOriginY;
        x3 += worldOriginX;
        y3 += worldOriginY;
        x4 += worldOriginX;
        y4 += worldOriginY;

        if (effects == SpriteEffects.FlipHorizontally) {
            float tmp = u1;
            u1 = u2;
            u2 = tmp;
        }

        if (effects == SpriteEffects.FlipVertically) {
            float tmp = v1;
            v1 = v2;
            v2 = tmp;
        }

        vertex(x1, y1, color.r, color.g, color.b, color.a, u1, v1); // top left
        vertex(x2, y2, color.r, color.g, color.b, color.a, u2, v1); // top right
        vertex(x4, y4, color.r, color.g, color.b, color.a, u1, v2); // bottom left
        vertex(x2, y2, color.r, color.g, color.b, color.a, u2, v1); // top right
        vertex(x3, y3, color.r, color.g, color.b, color.a, u2, v2); // bottom right
        vertex(x4, y4, color.r, color.g, color.b, color.a, u1, v2); // bottom left
    }

    private void checkFlush(TextureInterface sprite) {
        if (sprite == null || sprite.getTexture() == null) {
            throw new NullPointerException();
        }

        if (sprite.getTexture() != this.lastTexture || index >= vertices.length) {
            flush();
            this.lastTexture = sprite.getTexture();
        }
    }

    private void vertex(float x, float y, float r, float g, float b, float a, float u, float v) {
        vertices[index++] = x;
        vertices[index++] = y;
        vertices[index++] = r;
        vertices[index++] = g;
        vertices[index++] = b;
        vertices[index++] = a;
        vertices[index++] = u;
        vertices[index++] = v;
    }
}