com.andgate.ikou.render.TileMesh.java Source code

Java tutorial

Introduction

Here is the source code for com.andgate.ikou.render.TileMesh.java

Source

/*
This file is part of Ikou.
Ikou is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License.
Ikou is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ikou.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.andgate.ikou.render;

import com.andgate.ikou.Constants;
import com.andgate.ikou.model.TileStack;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ShortArray;

public class TileMesh implements Disposable {
    private static final String TAG = "TileMeshBuilder";

    private final static Object rebuilding = new Object();
    private boolean needsRebuild = false;
    private boolean rebuildingInProcess = false;

    private static final float WIDTH = Constants.TILE_LENGTH;
    private static final float HEIGHT = Constants.TILE_HEIGHT;
    private static final float DEPTH = Constants.TILE_LENGTH;

    // Position attribute - (x, y, z)
    public static final String POSITION_ATTRIBUTE = "a_position";
    public static final int POSITION_COMPONENTS = 3;
    // Packed Color attribute - abgr
    public static final String COLOR_ATTRIBUTE = "a_color";
    public static final int COLOR_COMPONENTS = 1;
    // Opengl still expects 4 color components
    public static final int COLOR_COMPONENTS_EXPECTED = 4;
    // Normal Attribute - (
    public static final String NORMAL_ATTRIBUTE = "a_normal";
    public static final int NORMAL_COMPONENTS = 3;
    // Total number of components for all attributes
    public static final int NUM_COMPONENTS = POSITION_COMPONENTS + COLOR_COMPONENTS + NORMAL_COMPONENTS;

    public static final int VERTICES_PER_FACE = 4;
    public static final int INDICES_PER_FACE = 6;

    public static final int SUBQUADS = 10;

    // Normal pointers for all the six sides. They never changes.
    protected static final Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);
    protected static final Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);
    protected static final Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);
    protected static final Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);
    protected static final Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);
    protected static final Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);

    protected final Vector3[] points = new Vector3[8];
    // To keep memory (and CPU) usage down we will reuse the same Vector3 instances.
    private static final Vector3 pointVector0 = new Vector3();
    private static final Vector3 pointVector1 = new Vector3();
    private static final Vector3 pointVector2 = new Vector3();
    private static final Vector3 pointVector3 = new Vector3();
    private static final Vector3 pointVector4 = new Vector3();
    private static final Vector3 pointVector5 = new Vector3();
    private static final Vector3 pointVector6 = new Vector3();
    private static final Vector3 pointVector7 = new Vector3();

    private float[] v;
    private short[] i;
    private FloatArray vertices;
    private ShortArray indicies;
    private Mesh mesh;
    private final Matrix4 transform = new Matrix4();

    public TileMesh() {
        vertices = new FloatArray();
        indicies = new ShortArray();
    }

    public Matrix4 getTransform() {
        return transform;
    }

    public Mesh getMesh() {
        synchronized (rebuilding) {
            if (rebuildingInProcess) {
                return null;
            }
            if (needsRebuild) {
                rebuild();
            }
            return mesh;
        }
    }

    public void setNeedsRebuild() {
        synchronized (rebuilding) {
            v = vertices.toArray();
            i = indicies.toArray();
            needsRebuild = true;
        }
    }

    public void addTile(Color color, float x, float y, float z) {
        calculateVerts(x, y, z);
        addFront(color);
        addBack(color);
        addRight(color);
        addLeft(color);
        addTop(color);
        addBottom(color);
    }

    public void calculateVerts(float x, float y, float z) {
        final float width = TileStack.WIDTH;
        final float height = TileStack.HEIGHT;
        final float depth = TileStack.DEPTH;

        calculateVerts(x, y, z, width, height, depth);
    }

    public void calculateVerts(float x, float y, float z, float width, float height, float depth) {
        // Creates the 8 vector points that exists on a box. Those will be used to create the vertex.
        points[0] = pointVector0.set(x, y, z + depth);
        points[1] = pointVector1.set(x + width, y, z + depth);
        points[2] = pointVector2.set(x + width, y + height, z + depth);
        points[3] = pointVector3.set(x, y + height, z + depth);
        points[4] = pointVector4.set(x + width, y, z);
        points[5] = pointVector5.set(x, y, z);
        points[6] = pointVector6.set(x, y + height, z);
        points[7] = pointVector7.set(x + width, y + height, z);
    }

    public void addFace(Color color, Vector3 point0, Vector3 point1, Vector3 point2, Vector3 point3,
            Vector3 normal) {
        int vertexOffset = vertices.size / NUM_COMPONENTS;
        float colorBits = color.toFloatBits();

        vertices.addAll(point0.x, point0.y, point0.z, colorBits, normal.x, normal.y, normal.z, point1.x, point1.y,
                point1.z, colorBits, normal.x, normal.y, normal.z, point2.x, point2.y, point2.z, colorBits,
                normal.x, normal.y, normal.z, point3.x, point3.y, point3.z, colorBits, normal.x, normal.y,
                normal.z);

        indicies.addAll((short) (vertexOffset), (short) (1 + vertexOffset), (short) (2 + vertexOffset),
                (short) (2 + vertexOffset), (short) (3 + vertexOffset), (short) (vertexOffset));
    }

    // Code to subdivide the top face.
    // Causes the map to have trouble rendering, cause too many polys,
    // when SUBQUADS is around 5.
    // Disabled for now, may renable for smooth lighting.
    // Or a shader may be used instead!
    /*void addTop(TileData tile, Color color, float x, float y, float z)
    {
    for (int hDiv = 0; hDiv < SUBQUADS; hDiv++)
    {
        for (int vDiv = 0; vDiv < SUBQUADS; vDiv++)
        {
            calculateVerts(
                    x + hDiv * WIDTH / SUBQUADS,
                    y,
                    z + vDiv * DEPTH / SUBQUADS,
                    WIDTH / SUBQUADS,
                    HEIGHT,
                    DEPTH / SUBQUADS
            );
        
            int vertexOffset = vertices.size / NUM_COMPONENTS;
        
            vertices.addAll(
                    points[3].x, points[3].y, points[3].z, color.toFloatBits(), topNormal[0], topNormal[1], topNormal[2],
                    points[2].x, points[2].y, points[2].z, color.toFloatBits(), topNormal[0], topNormal[1], topNormal[2],
                    points[7].x, points[7].y, points[7].z, color.toFloatBits(), topNormal[0], topNormal[1], topNormal[2],
                    points[6].x, points[6].y, points[6].z, color.toFloatBits(), topNormal[0], topNormal[1], topNormal[2]);
        
            indicies.addAll((short) (vertexOffset), (short) (1 + vertexOffset), (short) (2 + vertexOffset), (short) (2 + vertexOffset), (short) (3 + vertexOffset), (short) (vertexOffset));
        }
    }
        
    calculateVerts(x, y, z, WIDTH, HEIGHT, DEPTH);
    }*/

    public void addFront(Color color) {
        addFace(color, points[0], points[1], points[2], points[3], frontNormal);
    }

    public void addBack(Color color) {
        addFace(color, points[4], points[5], points[6], points[7], backNormal);
    }

    public void addRight(Color color) {
        addFace(color, points[1], points[4], points[7], points[2], rightNormal);
    }

    public void addLeft(Color color) {
        addFace(color, points[5], points[0], points[3], points[6], leftNormal);
    }

    public void addTop(Color color) {
        addFace(color, points[3], points[2], points[7], points[6], topNormal);
    }

    void addBottom(Color color) {
        addFace(color, points[5], points[4], points[1], points[0], topNormal);
    }

    public void rebuild() {
        try {
            synchronized (rebuilding) {
                rebuildingInProcess = true;
                mesh = new Mesh(true, VERTICES_PER_FACE * (vertices.size / NUM_COMPONENTS),
                        INDICES_PER_FACE * indicies.size,
                        new VertexAttribute(Usage.Position, POSITION_COMPONENTS, POSITION_ATTRIBUTE),
                        new VertexAttribute(Usage.ColorPacked, COLOR_COMPONENTS_EXPECTED, COLOR_ATTRIBUTE),
                        new VertexAttribute(Usage.Normal, NORMAL_COMPONENTS, NORMAL_ATTRIBUTE));
                mesh.setVertices(v);
                mesh.setIndices(i);

                // Clear everything so it can be garbage collected
                vertices.clear();
                indicies.clear();
                vertices = null;
                indicies = null;
                v = null;
                i = null;

                rebuildingInProcess = false;
                needsRebuild = false;
            }
        } catch (final Throwable e) {
            final String errorMessage = "Rebuilding mesh failed!";
            Gdx.app.error(TAG, errorMessage, e);
        }
    }

    @Override
    public void dispose() {
        mesh.dispose();
    }
}