/* * This file is part of Goko. * * Goko 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. * * Goko 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 Goko. If not, see <http://www.gnu.org/licenses/>. */ package org.goko.tools.viewer.jogl.utils.render.internal; import java.nio.FloatBuffer; import java.nio.IntBuffer; import javax.media.opengl.GL; import javax.media.opengl.GL3; import org.goko.core.common.exception.GkException; import org.goko.core.common.exception.GkFunctionalException; import org.goko.core.common.exception.GkTechnicalException; import org.goko.tools.viewer.jogl.service.AbstractCoreJoglRenderer; import com.jogamp.common.nio.Buffers; public abstract class AbstractVboJoglRenderer extends AbstractCoreJoglRenderer{ protected static final int VERTICES = 1 << 1; private static final int VERTICES_LAYOUT = 0; protected static final int COLORS = 1 << 2; private static final int COLORS_LAYOUT = 1; protected static final int UVS = 1 << 3; private static final int UVS_LAYOUT = 2; protected static final int NORMALS = 1 << 4; private static final int NORMALS_LAYOUT = 3; private Integer verticesCount; private boolean useVerticesBuffer; private FloatBuffer verticesBuffer; private boolean useColorsBuffer; private FloatBuffer colorsBuffer; private boolean useUvsBuffer; private FloatBuffer uvsBuffer; private boolean useNormalsBuffer; private FloatBuffer normalsBuffer; /** The type of primitive used for render */ private Integer renderPrimitive; /** The id of the VAO in use */ private Integer vertexArrayObject; /** Request for buffer update */ private boolean updateBuffer; /** Request for geometry update */ private boolean updateGeometry; /** Request for shader update */ private boolean updateShader; /** Interleaved buffer */ private FloatBuffer interleavedBuffer; /** The id of the VBO in use for the vertices data */ private Integer interleavedBufferObject; /** Stride in interleaved buffer */ private int stride; protected AbstractVboJoglRenderer(int renderPrimitive, int usedBuffers){ this.renderPrimitive = renderPrimitive; this.useVerticesBuffer = (usedBuffers & VERTICES) == VERTICES; this.useColorsBuffer = (usedBuffers & COLORS) == COLORS; this.useUvsBuffer = (usedBuffers & UVS) == UVS; this.useNormalsBuffer = (usedBuffers & NORMALS) == NORMALS; } /** * Method allowing to build the geometry of this renderer * @throws GkException */ protected abstract void buildGeometry() throws GkException; /** * Method allowing to load the shader program * @return the identifier of the shader program * @throws GkException */ protected abstract int loadShaderProgram(GL3 gl) throws GkException; /** (inheritDoc) * @see org.goko.tools.viewer.jogl.service.AbstractCoreJoglRenderer#performInitialize(javax.media.opengl.GL3) */ @Override protected void performInitialize(GL3 gl) throws GkException { buildGeometry(); setShaderProgram(loadShaderProgram(gl)); initializeVertexArrayObject(gl); initializeBufferObjects(gl); } protected void updateBufferObjects() throws GkException { this.updateBuffer = true; } public void updateGeometry() throws GkException { this.updateGeometry = true; } /** * Performs the update of the geometry * @param gl the GL * @throws GkException GkException */ protected void performUpdateGeometry(GL3 gl) throws GkException { this.updateGeometry = false; buildGeometry(); performUpdateBufferObjects(gl); } /** * Performs the update in the vertex buffers * @param gl the GL * @throws GkException GkException */ protected void performUpdateBufferObjects(GL3 gl) throws GkException { initializeBufferObjects(gl); this.updateBuffer = false; } /** * Performs the update in the shader data * @param gl the GL * @throws GkException GkException */ protected void performUpdateShader(GL3 gl) throws GkException { updateShaderData(gl); this.updateShader = false; } /** * Initializes and bind the several vertex buffer objects * @param gl the GL * @throws GkException GkException */ protected void initializeBufferObjects(GL3 gl) throws GkException { if(this.interleavedBufferObject == null){ int[] vbo = new int[1]; gl.glGenBuffers(1, vbo, 0); this.interleavedBufferObject = vbo[0]; } buildInterleavedBuffer(); interleavedBuffer.rewind(); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, interleavedBufferObject); gl.glBufferData(GL.GL_ARRAY_BUFFER, getVerticesCount()*stride*Buffers.SIZEOF_FLOAT, interleavedBuffer, GL.GL_DYNAMIC_DRAW); gl.glEnableVertexAttribArray(VERTICES_LAYOUT); int offset = 0; if(useVerticesBuffer){ gl.glVertexAttribPointer(VERTICES_LAYOUT, 4, GL.GL_FLOAT, false, stride*Buffers.SIZEOF_FLOAT, offset); gl.glEnableVertexAttribArray(VERTICES_LAYOUT); offset += 4; } if(useColorsBuffer){ gl.glVertexAttribPointer(COLORS_LAYOUT, 4, GL.GL_FLOAT, false, stride*Buffers.SIZEOF_FLOAT, offset*Buffers.SIZEOF_FLOAT); gl.glEnableVertexAttribArray(COLORS_LAYOUT); offset += 4; } if(useUvsBuffer){ gl.glVertexAttribPointer(UVS_LAYOUT, 2, GL.GL_FLOAT, false, stride*Buffers.SIZEOF_FLOAT, offset*Buffers.SIZEOF_FLOAT); gl.glEnableVertexAttribArray(UVS_LAYOUT); offset += 2; } if(useNormalsBuffer){ gl.glVertexAttribPointer(NORMALS_LAYOUT, 4, GL.GL_FLOAT, false, stride*Buffers.SIZEOF_FLOAT, offset*Buffers.SIZEOF_FLOAT); gl.glEnableVertexAttribArray(NORMALS_LAYOUT); offset += 4; } initializeAdditionalBufferObjects(gl); } /** * Performs the initialisation of any additional buffer in subclasses * @param gl the GL context * @throws GkException GkException */ protected void initializeAdditionalBufferObjects(GL3 gl) throws GkException { } /** * Initializes and bind the main vertex array object * @param gl the GL */ private void initializeVertexArrayObject(GL3 gl) { if(this.vertexArrayObject == null){ IntBuffer vao = IntBuffer.allocate(1); gl.glGenVertexArrays(1, vao); this.vertexArrayObject = vao.get(0); gl.glBindVertexArray(this.vertexArrayObject); } } /** (inheritDoc) * @see org.goko.tools.viewer.jogl.service.AbstractCoreJoglRenderer#performRender(javax.media.opengl.GL3) */ @Override protected void performRender(GL3 gl) throws GkException { if(!isInitialized()){ initialize(gl); } gl.glBindVertexArray(this.vertexArrayObject); if(updateGeometry){ performUpdateGeometry(gl); } if(updateBuffer){ performUpdateBufferObjects(gl); } if(updateShader){ performUpdateShader(gl); } if(interleavedBuffer == null){ buildInterleavedBuffer(); } gl.glBindBuffer(GL.GL_ARRAY_BUFFER, interleavedBufferObject); gl.glUseProgram(getShaderProgram()); enableAdditionalVertexAttribArray(gl); updateShaderData(gl); gl.glDrawArrays(getRenderPrimitive(), 0, getVerticesCount()); disableAdditionalVertexAttribArray(gl); gl.glUseProgram(0); } protected void updateShaderData(GL3 gl) throws GkException { } protected void enableAdditionalVertexAttribArray(GL3 gl) throws GkException { } protected void disableAdditionalVertexAttribArray(GL3 gl) throws GkException { } /** (inheritDoc) * @see org.goko.tools.viewer.jogl.service.ICoreJoglRenderer#performDestroy(javax.media.opengl.GL3) */ @Override public void performDestroy(GL3 gl) throws GkException { if(!isInitialized()){ return; } IntBuffer buffers = IntBuffer.wrap(new int[]{interleavedBufferObject}); gl.glDeleteBuffers(1,buffers); IntBuffer intBuffer = IntBuffer.wrap(new int[]{vertexArrayObject}); gl.glDeleteVertexArrays(1, intBuffer); } protected void buildInterleavedBuffer() throws GkTechnicalException{ stride = 0; if(useVerticesBuffer){ stride += 4; // 4 if(getVerticesBuffer() == null){ throw new GkTechnicalException(this+" Vertices buffer not initialized."); } } if(useColorsBuffer){ stride +=4; // 4 if(getColorsBuffer() == null){ throw new GkTechnicalException(" Colors buffer not initialized."); } } if(useUvsBuffer){ stride += 2; //2 if(getUvsBuffer() == null){ throw new GkTechnicalException(" Uvs buffer not initialized."); } } if(useNormalsBuffer){ stride += 4; //4 if(getNormalsBuffer() == null){ throw new GkTechnicalException(" Normals buffer not initialized."); } } if(interleavedBuffer != null){ interleavedBuffer.clear(); } interleavedBuffer = FloatBuffer.allocate(getVerticesCount()*stride); for (int i = 0; i < verticesCount; i++) { if(useVerticesBuffer){ interleavedBuffer.put(verticesBuffer.get(i * 4 )); interleavedBuffer.put(verticesBuffer.get(i * 4 +1)); interleavedBuffer.put(verticesBuffer.get(i * 4 +2)); interleavedBuffer.put(verticesBuffer.get(i * 4 +3)); } if(useColorsBuffer){; interleavedBuffer.put(colorsBuffer.get(i * 4 )); interleavedBuffer.put(colorsBuffer.get(i * 4 +1)); interleavedBuffer.put(colorsBuffer.get(i * 4 +2)); interleavedBuffer.put(colorsBuffer.get(i * 4 +3)); } if(useUvsBuffer){ interleavedBuffer.put(uvsBuffer.get(i * 2 )); interleavedBuffer.put(uvsBuffer.get(i * 2 +1)); } if(useNormalsBuffer){ interleavedBuffer.put(normalsBuffer.get(i * 4 )); interleavedBuffer.put(normalsBuffer.get(i * 4 +1)); interleavedBuffer.put(normalsBuffer.get(i * 4 +2)); interleavedBuffer.put(normalsBuffer.get(i * 4 +3)); } } interleavedBuffer.rewind(); } /** * Update the buffers */ public void update(){ this.updateBuffer = true; } /** * Update the shader datas */ public void updateShaderData(){ this.updateShader = true; } /** * @return the verticesBuffer */ protected FloatBuffer getVerticesBuffer() { return verticesBuffer; } /** * @param verticesBuffer the verticesBuffer to set */ protected void setVerticesBuffer(FloatBuffer verticesBuffer) { this.verticesBuffer = verticesBuffer; } /** * @return the colorsBuffer */ protected FloatBuffer getColorsBuffer() { return colorsBuffer; } /** * @param colorsBuffer the colorsBuffer to set */ protected void setColorsBuffer(FloatBuffer colorsBuffer) { this.colorsBuffer = colorsBuffer; } /** * @return the uvsBuffer */ protected FloatBuffer getUvsBuffer() { return uvsBuffer; } /** * @param uvsBuffer the uvsBuffer to set */ protected void setUvsBuffer(FloatBuffer uvsBuffer) { this.uvsBuffer = uvsBuffer; } /** * @return the verticesCount */ protected Integer getVerticesCount() { return verticesCount; } /** * @param verticesCount the verticesCount to set * @throws GkFunctionalException GkFunctionalException */ protected void setVerticesCount(Integer verticesCount) throws GkFunctionalException { // if(!ObjectUtils.equals(verticesCount, this.verticesCount) && isInitialized()){ // throw new GkFunctionalException("Cannot change vertices count once initialized"); // } this.verticesCount = verticesCount; } /** * @return the renderPrimitive */ protected Integer getRenderPrimitive() { return renderPrimitive; } /** * @param renderPrimitive the renderPrimitive to set */ protected void setRenderPrimitive(Integer renderPrimitive) { this.renderPrimitive = renderPrimitive; } /** * @return the updateBuffer */ protected boolean isUpdateBuffer() { return updateBuffer; } /** * @param updateBuffer the updateBuffer to set */ protected void setUpdateBuffer(boolean updateBuffer) { this.updateBuffer = updateBuffer; } /** * @return the normalsBuffer */ public FloatBuffer getNormalsBuffer() { return normalsBuffer; } /** * @param normalsBuffer the normalsBuffer to set */ public void setNormalsBuffer(FloatBuffer normalsBuffer) { this.normalsBuffer = normalsBuffer; } }