Java tutorial
/* * 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; } } }