org.craftmania.rendering.ChunkMeshBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.craftmania.rendering.ChunkMeshBuilder.java

Source

/*******************************************************************************
 * Copyright 2012 Martijn Courteaux <martijn.courteaux@skynet.be>
 * 
 * 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.craftmania.rendering;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.craftmania.blocks.Block;
import org.craftmania.blocks.BlockManager;
import org.craftmania.blocks.BlockType;
import org.craftmania.math.MathHelper;
import org.craftmania.math.Vec3f;
import org.craftmania.math.Vec3i;
import org.craftmania.utilities.IntList;
import org.craftmania.world.Chunk;
import org.craftmania.world.ChunkData;
import org.craftmania.world.LightBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;

public class ChunkMeshBuilder {
    private static final boolean DEBUG = false;

    public static final Vec3f COLOR_WHITE = new Vec3f(1, 1, 1);

    public static final int STRIDE = 8;
    public static final int POSITION_SIZE = 3;
    public static final int POSITION_OFFSET = 0;
    public static final int COLOR_SIZE = 3;
    public static final int COLOR_OFFSET = POSITION_OFFSET + POSITION_SIZE;
    public static final int TEX_COORD_SIZE = 2;
    public static final int TEX_COORD_OFFSET = COLOR_OFFSET + COLOR_SIZE;
    public static final int FLOAT_SIZE = 4;

    public static enum MeshType {
        OPAQUE, TRANSLUCENT
    }

    private static BlockManager _blockManager = BlockManager.getInstance();

    private static int USED_SIZE = 0;
    private static boolean SMOOTH_LIGHTING = true;

    public static void generateChunkMesh(Chunk chunk, MeshType meshType) {
        if (DEBUG)
            System.out.println("Building " + meshType.name() + " Mesh for " + chunk.toString() + "...");

        /* Make sure there are no list edits anymore */
        chunk.performListChanges();

        ChunkMesh mesh = chunk.getMesh();
        mesh.destroy(meshType);

        /* Compute vertex count */
        int vertexCount = chunk.getVertexCount(meshType);
        if (DEBUG)
            System.out.println("\tVertex Count = " + vertexCount);
        /*
         * If there are no faces visible yet (because of generating busy), don't
         * create a buffer
         */
        if (vertexCount == 0) {
            return;
        }
        mesh.setVertexCount(meshType, vertexCount);

        /* Create a buffer */
        int vbo = BufferManager.getInstance().createBuffer();
        mesh.setVBO(meshType, vbo);

        /* Bind the buffer */
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);

        /* Allocate size for the buffer */
        int size = vertexCount * STRIDE * FLOAT_SIZE;
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, size, GL15.GL_STATIC_DRAW);

        if (DEBUG)
            System.out.println(
                    "\tCreate VBO: " + vbo + " with size = " + size + " (ERROR: " + GL11.glGetError() + ")");

        /* Get the native buffer to write to */
        ByteBuffer byteBuffer = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY, size, null);
        if (byteBuffer == null) {
            System.out.println("\tCouldn't create a native VBO!: GL Error Code = " + GL11.glGetError());

            GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

            mesh.destroy(meshType);
            Thread.dumpStack();
            mesh.setVBO(meshType, -1);
            mesh.setVertexCount(meshType, 0);

            return;
        }

        FloatBuffer vertexBuffer = byteBuffer.asFloatBuffer();

        /* Store all vertices in the buffer */
        USED_SIZE = 0;

        /* Local temporary variables, used to speed up */
        IntList blockList = chunk.getVisibleBlocks();
        int blockIndex = -1;
        byte blockType = 0;
        boolean special = false;
        Vec3i vec = new Vec3i();
        BlockType type;
        Block block = null;
        LightBuffer lightBuffer = chunk.getLightBuffer();
        byte faceMask = 0;

        /* Iterate over the blocks */
        for (int i = 0; i < blockList.size(); ++i) {
            blockIndex = blockList.get(i);
            blockType = chunk.getChunkData().getBlockType(blockIndex);
            if (blockType == 0)
                continue;
            special = chunk.getChunkData().isSpecial(blockIndex);
            type = _blockManager.getBlockType(blockType);

            if ((meshType == MeshType.OPAQUE && !type.isTranslucent() && type.hasNormalAABB())
                    || (meshType == MeshType.TRANSLUCENT && (type.isTranslucent() || !type.hasNormalAABB()))) {

                ChunkData.indexToPosition(blockIndex, vec);

                /* Build the light buffer */

                vec.setX(vec.x() + chunk.getAbsoluteX());
                vec.setZ(vec.z() + chunk.getAbsoluteZ());

                lightBuffer.setReferencePoint(vec.x(), vec.y(), vec.z());

                if (special) {
                    block = chunk.getChunkData().getSpecialBlock(blockIndex);
                    if (block.isVisible()) {
                        block.storeInVBO(vertexBuffer, lightBuffer);
                    }
                } else {
                    if (type.isCrossed()) {
                        type.getCrossedBlockBrush().storeInVBO(vertexBuffer, vec.x() + 0.5f, vec.y() + 0.5f,
                                vec.z() + 0.5f, lightBuffer);
                    } else {
                        faceMask = chunk.getChunkData().getFaceMask(blockIndex);
                        type.getDefaultBlockBrush().setFaceMask(faceMask);
                        type.getBrush().storeInVBO(vertexBuffer, vec.x() + 0.5f, vec.y() + 0.5f, vec.z() + 0.5f,
                                lightBuffer);
                    }
                }
            }
        }

        /* Perform a check */
        if (USED_SIZE != STRIDE * FLOAT_SIZE * mesh.getVertexCount(meshType)) {
            System.out.println("\t[WARNING!]: Used size = " + USED_SIZE);
            System.out.println("\t[WARNING!]: Vertex count = " + USED_SIZE / STRIDE / FLOAT_SIZE);
            mesh.setVertexCount(meshType, USED_SIZE / STRIDE / FLOAT_SIZE);
        }

        byteBuffer.flip();

        GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

    }

    public static void putColorWithLight4(FloatBuffer vertexBuffer, Vec3f vec, byte light, byte light1, byte light2,
            byte light3) {
        float value;

        if (SMOOTH_LIGHTING) {
            value = light + light1 + light2 + light3;
            value /= 120.0001f;
        } else {
            value = light / 30.001f;
        }
        value = MathHelper.sin(value * MathHelper.f_PI_div_2);
        vertexBuffer.put(vec.x() * value);
        vertexBuffer.put(vec.y() * value);
        vertexBuffer.put(vec.z() * value);
        USED_SIZE += 3 * FLOAT_SIZE;
    }

    public static void putColorWithLight3(FloatBuffer vertexBuffer, Vec3f vec, byte light, byte light1,
            byte light2) {
        float value;

        if (SMOOTH_LIGHTING) {
            value = light + light1 + light2;
            value /= 90.0001f;
        } else {
            value = light / 30.001f;
        }

        value = MathHelper.sin(value * MathHelper.f_PI_div_2);
        vertexBuffer.put(vec.x() * value);
        vertexBuffer.put(vec.y() * value);
        vertexBuffer.put(vec.z() * value);
        USED_SIZE += 3 * FLOAT_SIZE;
    }

    public static void putColorWithLight(FloatBuffer vertexBuffer, Vec3f vec, byte light) {
        float value;
        value = light / 30.001f;
        vertexBuffer.put(vec.x() * value);
        vertexBuffer.put(vec.y() * value);
        vertexBuffer.put(vec.z() * value);
        USED_SIZE += 3 * FLOAT_SIZE;
    }

    public static void putVec3f(FloatBuffer vertexBuffer, Vec3f vec) {
        vertexBuffer.put(vec.x());
        vertexBuffer.put(vec.y());
        vertexBuffer.put(vec.z());
        USED_SIZE += 3 * FLOAT_SIZE;
    }

    public static void put3f(FloatBuffer vertexBuffer, float f0, float f1, float f2) {
        vertexBuffer.put(f0);
        vertexBuffer.put(f1);
        vertexBuffer.put(f2);
        USED_SIZE += 3 * FLOAT_SIZE;
    }

    public static void put2f(FloatBuffer vertexBuffer, float f0, float f1) {
        vertexBuffer.put(f0);
        vertexBuffer.put(f1);
        USED_SIZE += 2 * FLOAT_SIZE;

    }

    public static void generateChunkMeshes(Chunk chunk) {
        chunk.getLightBuffer().buffer(chunk);
        generateChunkMesh(chunk, MeshType.OPAQUE);
        generateChunkMesh(chunk, MeshType.TRANSLUCENT);
    }
}