org.terasology.rendering.primitives.Mesh.java Source code

Java tutorial

Introduction

Here is the source code for org.terasology.rendering.primitives.Mesh.java

Source

/*
 * Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com>
 *
 * 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 org.terasology.rendering.primitives;

import static org.lwjgl.opengl.GL11.GL_COLOR_ARRAY;
import static org.lwjgl.opengl.GL11.GL_NORMAL_ARRAY;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_COORD_ARRAY;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL11.GL_VERTEX_ARRAY;
import static org.lwjgl.opengl.GL11.glColorPointer;
import static org.lwjgl.opengl.GL11.glDisableClientState;
import static org.lwjgl.opengl.GL11.glEnableClientState;
import static org.lwjgl.opengl.GL11.glNormalPointer;
import static org.lwjgl.opengl.GL11.glTexCoordPointer;
import static org.lwjgl.opengl.GL11.glVertexPointer;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.list.TFloatList;
import gnu.trove.list.TIntList;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.vecmath.Vector3f;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.terasology.asset.Asset;
import org.terasology.asset.AssetUri;
import org.terasology.logic.manager.VertexBufferObjectManager;
import org.terasology.math.AABB;

import com.bulletphysics.linearmath.Transform;

// TODO: Store mesh information in Mesh class in a usable format, for
public class Mesh implements Asset {

    public static final int VERTEX_SIZE = 3;
    public static final int TEX_COORD_0_SIZE = 2;
    public static final int TEX_COORD_1_SIZE = 3;
    public static final int COLOR_SIZE = 4;
    public static final int NORMAL_SIZE = 3;

    private AssetUri uri = new AssetUri("");
    private AABB aabb;

    private int stride;
    private int vertexOffset;
    private int texCoord0Offset;
    private int texCoord1Offset;
    private int colorOffset;
    private int normalOffset;

    private boolean hasTexCoord0 = false;
    private boolean hasTexCoord1 = false;
    private boolean hasColor = false;
    private boolean hasNormal = false;

    private int vboVertexBuffer;
    private int vboIndexBuffer;
    private int indexCount;

    private TFloatList vertices;
    private TFloatList texCoord0;
    private TFloatList texCoord1;
    private TFloatList normals;
    private TFloatList colors;
    private TIntList indices;

    public static Mesh buildMesh(AssetUri uri, TFloatList vertices, TFloatList texCoord0, TFloatList texCoord1,
            TFloatList normals, TFloatList colors, TIntList indices) {
        Mesh mesh = buildMesh(vertices, texCoord0, texCoord1, normals, colors, indices);
        mesh.uri = uri;
        return mesh;
    }

    public static Mesh buildMesh(TFloatList vertices, TFloatList texCoord0, TFloatList texCoord1,
            TFloatList normals, TFloatList colors, TIntList indices) {

        int vertexCount = vertices.size() / VERTEX_SIZE;
        boolean hasTexCoord0 = texCoord0 != null && texCoord0.size() / TEX_COORD_0_SIZE == vertexCount;
        boolean hasTexCoord1 = texCoord1 != null && texCoord1.size() / TEX_COORD_1_SIZE == vertexCount;
        boolean hasNormals = normals != null && normals.size() / NORMAL_SIZE == vertexCount;
        boolean hasColors = colors != null && colors.size() / COLOR_SIZE == vertexCount;

        int vertSize = VERTEX_SIZE + (hasTexCoord0 ? TEX_COORD_0_SIZE : 0) + (hasTexCoord1 ? TEX_COORD_1_SIZE : 0)
                + (hasNormals ? NORMAL_SIZE : 0) + (hasColors ? COLOR_SIZE : 0);
        FloatBuffer vertexBuffer = createVertexBuffer(vertices, texCoord0, texCoord1, normals, colors, vertexCount,
                hasTexCoord0, hasTexCoord1, hasNormals, hasColors, vertSize);

        IntBuffer indexBuffer = BufferUtils.createIntBuffer(indices.size());
        TIntIterator iterator = indices.iterator();
        while (iterator.hasNext()) {
            indexBuffer.put(iterator.next());
        }
        indexBuffer.flip();

        Mesh mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.texCoord0 = texCoord0;
        mesh.texCoord1 = texCoord1;
        mesh.normals = normals;
        mesh.colors = colors;
        mesh.indices = indices;
        mesh.indexCount = indices.size();
        mesh.aabb = calculateAABB(vertices, vertexCount);

        mesh.vboVertexBuffer = VertexBufferObjectManager.getInstance().getVboId();
        VertexBufferObjectManager.getInstance().bufferVboData(mesh.vboVertexBuffer, vertexBuffer,
                GL15.GL_STATIC_DRAW);
        mesh.vboIndexBuffer = VertexBufferObjectManager.getInstance().getVboId();
        VertexBufferObjectManager.getInstance().bufferVboElementData(mesh.vboIndexBuffer, indexBuffer,
                GL15.GL_STATIC_DRAW);

        mesh.stride = vertSize * 4;
        mesh.vertexOffset = 0;
        int offset = VERTEX_SIZE;

        if (hasTexCoord0) {
            mesh.hasTexCoord0 = hasTexCoord0;
            mesh.texCoord0Offset = 4 * offset;
            offset += TEX_COORD_0_SIZE;
        }
        if (hasTexCoord1) {
            mesh.hasTexCoord1 = hasTexCoord1;
            mesh.texCoord1Offset = 4 * offset;
            offset += TEX_COORD_1_SIZE;
        }
        if (hasNormals) {
            mesh.hasNormal = hasNormals;
            mesh.normalOffset = 4 * offset;
            offset += NORMAL_SIZE;
        }
        if (hasColors) {
            mesh.hasColor = hasColors;
            mesh.colorOffset = 4 * offset;
            offset += COLOR_SIZE;
        }

        indexBuffer.flip();
        return mesh;
    }

    private static AABB calculateAABB(TFloatList vertices, int vertexCount) {
        if (vertexCount == 0) {
            return AABB.createEmpty();
        }

        Vector3f min = new Vector3f(vertices.get(0), vertices.get(1), vertices.get(2));
        Vector3f max = new Vector3f(vertices.get(0), vertices.get(1), vertices.get(2));
        for (int index = 1; index < vertexCount; ++index) {
            min.x = Math.min(min.x, vertices.get(3 * index));
            max.x = Math.max(max.x, vertices.get(3 * index));
            min.y = Math.min(min.y, vertices.get(3 * index + 1));
            max.y = Math.max(max.y, vertices.get(3 * index + 1));
            min.z = Math.min(min.z, vertices.get(3 * index + 2));
            max.z = Math.max(max.z, vertices.get(3 * index + 2));
        }
        return AABB.createMinMax(min, max);

    }

    private static FloatBuffer createVertexBuffer(TFloatList vertices, TFloatList texcoord0, TFloatList texcoord1,
            TFloatList normals, TFloatList colors, int vertexCount, boolean hasTexCoord0, boolean hasTexCoord1,
            boolean hasNormal, boolean hasColor, int vertSize) {
        FloatBuffer vertexBuffer;
        vertexBuffer = BufferUtils.createFloatBuffer(vertSize * vertexCount);
        int texCoord0Size = (hasTexCoord0) ? TEX_COORD_0_SIZE : 0;
        int texCoord1Size = (hasTexCoord1) ? TEX_COORD_1_SIZE : 0;
        int normalSize = (hasNormal) ? NORMAL_SIZE : 0;
        int colorSize = (hasColor) ? COLOR_SIZE : 0;
        int uv1 = 0, uv2 = 0, n = 0, c = 0;
        for (int v = 0; v < vertices.size(); v += 3) {
            vertexBuffer.put(vertices.get(v)).put(vertices.get(v + 1)).put(vertices.get(v + 2));
            for (int i = 0; i < texCoord0Size; ++i) {
                vertexBuffer.put(texcoord0.get(uv1 + i));
            }
            for (int i = 0; i < texCoord1Size; ++i) {
                vertexBuffer.put(texcoord1.get(uv2 + i));
            }
            for (int i = 0; i < normalSize; ++i) {
                vertexBuffer.put(normals.get(n + i));
            }
            for (int i = 0; i < colorSize; ++i) {
                vertexBuffer.put(colors.get(c + i));
            }
            uv1 += texCoord0Size;
            uv2 += texCoord1Size;
            n += normalSize;
            c += colorSize;
        }
        vertexBuffer.flip();
        return vertexBuffer;
    }

    private Mesh() {
    }

    public AABB getAABB() {
        return aabb;
    }

    @Override
    public AssetUri getURI() {
        return uri;
    }

    @Override
    public void dispose() {
        if (vboVertexBuffer != 0) {
            VertexBufferObjectManager.getInstance().putVboId(vboVertexBuffer);
            vboVertexBuffer = 0;
        }
        if (vboIndexBuffer != 0) {
            VertexBufferObjectManager.getInstance().putVboId(vboIndexBuffer);
            vboIndexBuffer = 0;
        }
    }

    public boolean isDisposed() {
        return vboVertexBuffer == 0 || vboIndexBuffer == 0;
    }

    public TFloatList getVertices() {
        return vertices;
    }

    public void preRender() {
        glEnableClientState(GL_VERTEX_ARRAY);
        if (hasTexCoord0 || hasTexCoord1)
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        if (hasColor)
            glEnableClientState(GL_COLOR_ARRAY);
        if (hasNormal)
            glEnableClientState(GL_NORMAL_ARRAY);

        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboVertexBuffer);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboIndexBuffer);

        glVertexPointer(VERTEX_SIZE, GL11.GL_FLOAT, stride, vertexOffset);

        if (hasTexCoord0) {
            GL13.glClientActiveTexture(GL13.GL_TEXTURE0);
            glTexCoordPointer(TEX_COORD_0_SIZE, GL11.GL_FLOAT, stride, texCoord0Offset);
        }

        if (hasTexCoord1) {
            GL13.glClientActiveTexture(GL13.GL_TEXTURE1);
            glTexCoordPointer(TEX_COORD_1_SIZE, GL11.GL_FLOAT, stride, texCoord1Offset);
        }

        if (hasColor)
            glColorPointer(COLOR_SIZE, GL11.GL_FLOAT, stride, colorOffset);
        if (hasNormal)
            glNormalPointer(GL11.GL_FLOAT, stride, normalOffset);
    }

    public void postRender() {
        if (hasNormal)
            glDisableClientState(GL_NORMAL_ARRAY);
        if (hasColor)
            glDisableClientState(GL_COLOR_ARRAY);
        if (hasTexCoord0 || hasTexCoord1)
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);

        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void doRender() {
        GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
    }

    public void render() {
        preRender();
        doRender();
        postRender();
    }

    public int addToBatch(Transform transform, Transform normalTransform, TFloatList vertexData, TIntList indexData,
            int indexOffset) {
        int uv1 = 0, uv2 = 0, n = 0, c = 0;
        for (int v = 0; v < vertices.size(); v += 3) {
            Vector3f vert = new Vector3f(vertices.get(v), vertices.get(v + 1), vertices.get(v + 2));
            transform.transform(vert);
            vertexData.add(vert.x);
            vertexData.add(vert.y);
            vertexData.add(vert.z);
            for (int i = 0; i < TEX_COORD_0_SIZE; ++i) {
                vertexData.add(texCoord0.get(uv1 + i));
            }
            for (int i = 0; i < TEX_COORD_1_SIZE; ++i) {
                vertexData.add(texCoord1.get(uv2 + i));
            }
            Vector3f norm = new Vector3f(normals.get(n), normals.get(n + 1), normals.get(n + 2));
            normalTransform.transform(norm);
            vertexData.add(norm.x);
            vertexData.add(norm.y);
            vertexData.add(norm.z);
            for (int i = 0; i < COLOR_SIZE; ++i) {
                vertexData.add(colors.get(c + i));
            }
            uv1 += TEX_COORD_0_SIZE;
            uv2 += TEX_COORD_1_SIZE;
            n += NORMAL_SIZE;
            c += COLOR_SIZE;
        }
        TIntIterator indexIterator = indices.iterator();
        while (indexIterator.hasNext()) {
            indexData.add(indexIterator.next() + indexOffset);
        }
        return indexOffset + vertices.size() / 3;
    }
}