Java tutorial
/* * Copyright 2013 MovingBlocks * * 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.opengl; import com.bulletphysics.linearmath.Transform; import com.google.common.collect.Lists; import gnu.trove.iterator.TFloatIterator; import gnu.trove.iterator.TIntIterator; import gnu.trove.list.TFloatList; import gnu.trove.list.TIntList; import gnu.trove.list.array.TIntArrayList; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.asset.AbstractAsset; import org.terasology.asset.AssetUri; import org.terasology.engine.subsystem.lwjgl.GLBufferPool; import org.terasology.math.AABB; import org.terasology.rendering.VertexBufferObjectUtil; import org.terasology.rendering.assets.mesh.Mesh; import org.terasology.rendering.assets.mesh.MeshData; import javax.vecmath.Vector3f; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.List; 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; /** * @author Immortius */ public class OpenGLMesh extends AbstractAsset<MeshData> implements Mesh { private static final Logger logger = LoggerFactory.getLogger(OpenGLMesh.class); private static final int FLOAT_SIZE = 4; private AABB aabb; private MeshData data; private int stride; private int vertexOffset; private int texCoord0Offset; private int texCoord1Offset; private int colorOffset; private int normalOffset; private boolean hasTexCoord0; private boolean hasTexCoord1; private boolean hasColor; private boolean hasNormal; private int vboVertexBuffer; private int vboIndexBuffer; private int indexCount; private GLBufferPool bufferPool; public OpenGLMesh(AssetUri uri, MeshData data, GLBufferPool bufferPool) { super(uri); this.bufferPool = bufferPool; reload(data); } @Override public void reload(MeshData newData) { buildMesh(newData); } @Override public void dispose() { hasTexCoord0 = false; hasTexCoord1 = false; hasColor = false; hasNormal = false; indexCount = 0; if (vboVertexBuffer != 0) { bufferPool.dispose(vboVertexBuffer); vboVertexBuffer = 0; } if (vboIndexBuffer != 0) { bufferPool.dispose(vboIndexBuffer); vboIndexBuffer = 0; } } @Override public boolean isDisposed() { return vboVertexBuffer == 0 || vboIndexBuffer == 0; } @Override public AABB getAABB() { return aabb; } @Override public TFloatList getVertices() { return data.getVertices(); } public void preRender() { if (!isDisposed()) { 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); } } else { logger.error("Attempted to render disposed mesh: {}", getURI()); } } public void postRender() { if (!isDisposed()) { 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); } else { logger.error("Attempted to render disposed mesh: {}", getURI()); } } public void doRender() { if (!isDisposed()) { GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); } else { logger.error("Attempted to render disposed mesh: {}", getURI()); } } public void render() { if (!isDisposed()) { preRender(); doRender(); postRender(); } else { logger.error("Attempted to render disposed mesh: {}", getURI()); } } public int addToBatch(Transform transform, Transform normalTransform, TFloatList vertexData, TIntList indexData, int indexOffset) { int uv1 = 0; int uv2 = 0; int n = 0; int c = 0; for (int v = 0; v < data.getVertices().size(); v += VERTEX_SIZE) { Vector3f vert = new Vector3f(data.getVertices().get(v), data.getVertices().get(v + 1), data.getVertices().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(data.getTexCoord0().get(uv1 + i)); } for (int i = 0; i < TEX_COORD_1_SIZE; ++i) { vertexData.add(data.getTexCoord1().get(uv2 + i)); } Vector3f norm = new Vector3f(data.getNormals().get(n), data.getNormals().get(n + 1), data.getNormals().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(data.getColors().get(c + i)); } uv1 += TEX_COORD_0_SIZE; uv2 += TEX_COORD_1_SIZE; n += NORMAL_SIZE; c += COLOR_SIZE; } TIntIterator indexIterator = data.getIndices().iterator(); while (indexIterator.hasNext()) { indexData.add(indexIterator.next() + indexOffset); } return indexOffset + data.getVertices().size() / VERTEX_SIZE; } private void buildMesh(MeshData newData) { this.data = newData; List<TFloatIterator> parts = Lists.newArrayList(); TIntList partSizes = new TIntArrayList(); int vertexCount = newData.getVertices().size() / VERTEX_SIZE; int vertexSize = VERTEX_SIZE; parts.add(newData.getVertices().iterator()); partSizes.add(VERTEX_SIZE); if (newData.getTexCoord0() != null && newData.getTexCoord0().size() / TEX_COORD_0_SIZE == vertexCount) { parts.add(newData.getTexCoord0().iterator()); partSizes.add(TEX_COORD_0_SIZE); texCoord0Offset = vertexSize * FLOAT_SIZE; vertexSize += TEX_COORD_0_SIZE; hasTexCoord0 = true; } if (newData.getTexCoord1() != null && newData.getTexCoord1().size() / TEX_COORD_1_SIZE == vertexCount) { parts.add(newData.getTexCoord1().iterator()); partSizes.add(TEX_COORD_1_SIZE); texCoord1Offset = vertexSize * FLOAT_SIZE; vertexSize += TEX_COORD_1_SIZE; hasTexCoord1 = true; } if (newData.getNormals() != null && newData.getNormals().size() / NORMAL_SIZE == vertexCount) { parts.add(newData.getNormals().iterator()); partSizes.add(NORMAL_SIZE); normalOffset = vertexSize * FLOAT_SIZE; vertexSize += NORMAL_SIZE; hasNormal = true; } if (newData.getColors() != null && newData.getColors().size() / COLOR_SIZE == vertexCount) { parts.add(newData.getColors().iterator()); partSizes.add(COLOR_SIZE); colorOffset = vertexSize * FLOAT_SIZE; vertexSize += COLOR_SIZE; hasColor = true; } stride = vertexSize * FLOAT_SIZE; indexCount = newData.getIndices().size(); createVertexBuffer(parts, partSizes, vertexCount, vertexSize); createIndexBuffer(newData.getIndices()); aabb = AABB.createEncompasing(newData.getVertices()); } private void createVertexBuffer(List<TFloatIterator> parts, TIntList partSizes, int vertexCount, int vertexSize) { FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertexSize * vertexCount); for (int v = 0; v < vertexCount; ++v) { for (int partIndex = 0; partIndex < parts.size(); ++partIndex) { TFloatIterator part = parts.get(partIndex); for (int i = 0; i < partSizes.get(partIndex); ++i) { vertexBuffer.put(part.next()); } } } vertexBuffer.flip(); if (vboVertexBuffer == 0) { vboVertexBuffer = bufferPool.get(getURI().toSimpleString()); } VertexBufferObjectUtil.bufferVboData(vboVertexBuffer, vertexBuffer, GL15.GL_STATIC_DRAW); vertexBuffer.flip(); } private void createIndexBuffer(TIntList indexList) { IntBuffer indexBuffer = BufferUtils.createIntBuffer(indexList.size()); TIntIterator iterator = indexList.iterator(); while (iterator.hasNext()) { indexBuffer.put(iterator.next()); } indexBuffer.flip(); if (vboIndexBuffer == 0) { vboIndexBuffer = bufferPool.get(getURI().toSimpleString()); } VertexBufferObjectUtil.bufferVboElementData(vboIndexBuffer, indexBuffer, GL15.GL_STATIC_DRAW); indexBuffer.flip(); } }