de.ikosa.mars.viewer.glviewer.engine.GLMeshBuilder.java Source code

Java tutorial

Introduction

Here is the source code for de.ikosa.mars.viewer.glviewer.engine.GLMeshBuilder.java

Source

/*
 * Copyright (C) 2013 Clemens-Alexander Brust IT-Dienstleistungen
 *
 *     This program 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 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.ikosa.mars.viewer.glviewer.engine;

import de.ikosa.mars.util.ML;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GLMeshBuilder {
    public String getName() {
        return name;
    }

    private String name;
    private List<GLVertex> vertices;
    private Map<String, List<GLIndex>> indices;

    private boolean hasTexCoords;
    private boolean hasNormals;

    public GLMeshBuilder(String name, boolean hasTexCoords, boolean hasNormals) {
        this.name = name;
        this.hasTexCoords = hasTexCoords;
        this.hasNormals = hasNormals;

        vertices = new ArrayList<>();
        indices = new HashMap<>();
    }

    public void addVertex(float px, float py, float pz) {
        vertices.add(new GLVertex(px, py, pz));
    }

    public void addVertex(float px, float py, float pz, float tu, float tv) {
        vertices.add(new GLVertex(px, py, pz, tu, tv));
    }

    public void addVertex(float px, float py, float pz, float tu, float tv, float nx, float ny, float nz) {
        vertices.add(new GLVertex(px, py, pz, tu, tv, nx, ny, nz));
    }

    public void addVertex(float px, float py, float pz, float nx, float ny, float nz) {
        vertices.add(new GLVertex(px, py, pz, nx, ny, nz));
    }

    public void addFace(String material, short... index) {
        if (index.length > 3)
            ML.f("Face with more than three vertices.");
        // triangulate...
        else if (index.length < 3) {
            ML.e(String.format("Face with less than three vertices (%d, material %s)", index.length, material));

        } else {
            // check if material group exists
            if (!indices.containsKey(material))
                indices.put(material, new ArrayList<GLIndex>());

            for (int i = 1; i < (index.length - 1); i++) {
                indices.get(material).add(new GLIndex(index[0]));
                indices.get(material).add(new GLIndex(index[i]));
                indices.get(material).add(new GLIndex(index[i + 1]));
            }
        }
    }

    public GLMesh createMesh(GLResources resourceManager) {
        return createMesh(resourceManager, true);
    }

    protected GLMesh createMesh(GLResources resourceManager, boolean fetchMaterial) {
        // create VBO buffers
        FloatBuffer interleavedBuffer = BufferUtils.createFloatBuffer(vertices.size() * 8);
        int stride = 12 + (hasNormals ? 12 : 0) + (hasTexCoords ? 8 : 0);
        int normalPosition = 12;
        int texCoordPosition = 12 + (hasNormals ? 12 : 0);

        // fill buffers
        for (GLVertex vertex : vertices) {
            interleavedBuffer.put(vertex.px);
            interleavedBuffer.put(vertex.py);
            interleavedBuffer.put(vertex.pz);
            if (hasNormals) {
                interleavedBuffer.put(vertex.nx);
                interleavedBuffer.put(vertex.ny);
                interleavedBuffer.put(vertex.nz);
            }
            if (hasTexCoords) {
                interleavedBuffer.put(vertex.tu);
                interleavedBuffer.put(vertex.tv);
            }
        }

        interleavedBuffer.flip();

        // create VAO
        int vaoId = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vaoId);

        // create VBOs
        int interleavedVboId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, interleavedVboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW);

        // position should always be there
        GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, stride, 0);
        if (hasNormals)
            GL20.glVertexAttribPointer(1, 3, GL11.GL_FLOAT, false, stride, normalPosition);
        if (hasTexCoords)
            GL20.glVertexAttribPointer(2, 2, GL11.GL_FLOAT, false, stride, texCoordPosition);

        // unbind buffers
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL30.glBindVertexArray(0);

        // create mesh object
        GLMesh mesh = createInstance(vaoId, interleavedVboId);

        // create submeshes for material groups

        for (String material : indices.keySet()) {
            GLMaterial glMaterial = fetchMaterial ? resourceManager.getMaterial(material) : GLMaterial.nullMaterial;
            // create index buffer
            List<GLIndex> indicesForMaterial = indices.get(material);
            IntBuffer indexBuffer = BufferUtils.createIntBuffer(indicesForMaterial.size());

            for (GLIndex index : indicesForMaterial) {
                indexBuffer.put(index.index);
            }
            indexBuffer.flip();

            int indexVboId = GL15.glGenBuffers();
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexVboId);
            GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL15.GL_STATIC_DRAW);

            GLSubMesh subMesh = new GLSubMesh(indexVboId, indicesForMaterial.size(), glMaterial, mesh);
            mesh.addSubMesh(subMesh);

        }

        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);

        return mesh;
    }

    protected GLMesh createInstance(int vaoId, int interleavedVboId) {
        return new GLMesh(name, vaoId, interleavedVboId, hasNormals, hasTexCoords);
    }

    public static class GLVertex {
        public float px = 0.0f, py = 0.0f, pz = 0.0f, pw = 1.0f, tu = 0.0f, tv = 0.0f, nx = 0.0f, ny = 0.0f,
                nz = 0.0f;

        public GLVertex(float px, float py, float pz) {
            this.px = px;
            this.py = py;
            this.pz = pz;
        }

        public GLVertex(float px, float py, float pz, float tu, float tv) {
            this.px = px;
            this.py = py;
            this.pz = pz;
            this.tu = tu;
            this.tv = tv;
        }

        public GLVertex(float px, float py, float pz, float tu, float tv, float nx, float ny, float nz) {
            this.px = px;
            this.py = py;
            this.pz = pz;
            this.tu = tu;
            this.tv = tv;
            this.nx = nx;
            this.ny = ny;
            this.nz = nz;
        }

        public GLVertex(float px, float py, float pz, float nx, float ny, float nz) {
            this.px = px;
            this.py = py;
            this.pz = pz;
            this.nx = nx;
            this.ny = ny;
            this.nz = nz;
        }
    }

    public static class GLIndex {
        public int index = 0;

        public GLIndex(int index) {
            this.index = index;
        }
    }
}