org.goko.viewer.jogl.utils.render.internal.AbstractVboJoglRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.goko.viewer.jogl.utils.render.internal.AbstractVboJoglRenderer.java

Source

/*
 *   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.viewer.jogl.utils.render.internal;

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

import javax.media.opengl.GL;
import javax.media.opengl.GL3;

import org.apache.commons.lang3.ObjectUtils;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.exception.GkFunctionalException;
import org.goko.core.common.exception.GkTechnicalException;
import org.goko.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;
    /** The id of the VBO in use for the vertices */
    private Integer verticesBufferObject;
    /** The id of the VBO in use for the colors */
    private Integer colorsBufferObject;
    /** The id of the VBO in use for the uvs */
    private Integer uvsBufferObject;
    /** The id of the VBO in use for the normals */
    private Integer normalsBufferObject;
    /** Request for buffer update */
    private boolean updateBuffer;

    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.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;
    }

    /**
     * Performs the update in the vertex buffers
     * @param gl the GL
     * @throws GkException GkException
     */
    protected void performUpdateBufferObjects(GL3 gl) throws GkException {
        buildGeometry();
        initializeBufferObjects(gl);
        this.updateBuffer = false;
    }

    /**
     * Initializes and bind the several vertex buffer objects
     * @param gl the GL
     * @throws GkException GkException
     */
    private void initializeBufferObjects(GL3 gl) throws GkException {
        initializeVerticesBufferObject(gl);
        initializeColorsBufferObject(gl);
        initializeUvsBufferObject(gl);
        initializeNormalsBufferObject(gl);
        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 buffer object for vertices
     * @param gl the GL
     * @throws GkException GkException
     */
    private void initializeVerticesBufferObject(GL3 gl) throws GkException {
        if (!useVerticesBuffer) {
            return; // Vertices not used, nothing to do
        }
        if (getVerticesBuffer() == null) {
            throw new GkTechnicalException(this + " Vertices buffer not initialized.");
        }
        // Everything looks fine, we can initialize
        if (this.verticesBufferObject == null) {
            int[] vbo = new int[1];
            gl.glGenBuffers(1, vbo, 0);
            this.verticesBufferObject = vbo[0];
        }
        // Make sure we take everything
        getVerticesBuffer().rewind();
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, verticesBufferObject);
        gl.glBufferData(GL.GL_ARRAY_BUFFER, getVerticesCount() * 4 * Buffers.SIZEOF_FLOAT, getVerticesBuffer(),
                GL.GL_DYNAMIC_DRAW);
        gl.glEnableVertexAttribArray(VERTICES_LAYOUT);
    }

    /**
     * Initializes and bind the buffer object for Colors
     * @param gl the GL
     * @throws GkException GkException
     */
    private void initializeColorsBufferObject(GL3 gl) throws GkException {
        if (!useColorsBuffer) {
            return; // Vertices not used, nothing to do
        }
        if (getColorsBuffer() == null) {
            throw new GkTechnicalException(" Colors buffer not initialized.");
        }
        // Everything looks fine, we can initialize
        if (this.colorsBufferObject == null) {
            int[] vbo = new int[1];
            gl.glGenBuffers(1, vbo, 0);
            this.colorsBufferObject = vbo[0];
        }
        // Make sure we take everything
        getColorsBuffer().rewind();
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, colorsBufferObject);
        gl.glBufferData(GL.GL_ARRAY_BUFFER, getVerticesCount() * 4 * Buffers.SIZEOF_FLOAT, getColorsBuffer(),
                GL.GL_DYNAMIC_DRAW);
        gl.glEnableVertexAttribArray(COLORS_LAYOUT);
    }

    /**
     * Initializes and bind the buffer object for UVs
     * @param gl the GL
     * @throws GkException GkException
     */
    private void initializeUvsBufferObject(GL3 gl) throws GkException {
        if (!useUvsBuffer) {
            return; // Vertices not used, nothing to do
        }
        if (getUvsBuffer() == null) {
            throw new GkTechnicalException(" Uvs buffer not initialized.");
        }
        // Everything looks fine, we can initialize
        if (this.uvsBufferObject == null) {
            int[] vbo = new int[1];
            gl.glGenBuffers(1, vbo, 0);
            this.uvsBufferObject = vbo[0];
        }
        // Make sure we take everything
        getUvsBuffer().rewind();
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, uvsBufferObject);
        gl.glBufferData(GL.GL_ARRAY_BUFFER, getVerticesCount() * 2 * Buffers.SIZEOF_FLOAT, getUvsBuffer(),
                GL.GL_DYNAMIC_DRAW);
        gl.glEnableVertexAttribArray(UVS_LAYOUT);
    }

    /**
     * Initializes and bind the buffer object for Normals
     * @param gl the GL
     * @throws GkException GkException
     */
    private void initializeNormalsBufferObject(GL3 gl) throws GkException {
        if (!useNormalsBuffer) {
            return; // Vertices not used, nothing to do
        }
        if (getNormalsBuffer() == null) {
            throw new GkTechnicalException(" Uvs buffer not initialized.");
        }
        // Everything looks fine, we can initialize
        if (this.normalsBufferObject == null) {
            int[] vbo = new int[1];
            gl.glGenBuffers(1, vbo, 0);
            this.normalsBufferObject = vbo[0];
        }
        // Make sure we take everything
        getNormalsBuffer().rewind();
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normalsBufferObject);
        gl.glBufferData(GL.GL_ARRAY_BUFFER, getVerticesCount() * 4 * Buffers.SIZEOF_FLOAT, getNormalsBuffer(),
                GL.GL_DYNAMIC_DRAW);
        gl.glEnableVertexAttribArray(NORMALS_LAYOUT);
    }

    /**
     * Initializes and bind the main vertex array object
     * @param gl the GL
     */
    private void initializeVertexArrayObject(GL3 gl) {
        IntBuffer vao = IntBuffer.allocate(1);
        gl.glGenVertexArrays(1, vao);
        this.vertexArrayObject = vao.get(0);
        gl.glBindVertexArray(this.vertexArrayObject);

    }

    /** (inheritDoc)
     * @see org.goko.viewer.jogl.service.AbstractCoreJoglRenderer#performRender(javax.media.opengl.GL3)
     */
    @Override
    protected void performRender(GL3 gl) throws GkException {
        if (!isInitialized()) {
            initialize(gl);
        }
        if (updateBuffer) {
            performUpdateBufferObjects(gl);
        }
        gl.glUseProgram(getShaderProgram());

        if (useVerticesBuffer) {
            gl.glEnableVertexAttribArray(VERTICES_LAYOUT);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, verticesBufferObject);
            gl.glVertexAttribPointer(VERTICES_LAYOUT, 4, GL.GL_FLOAT, false, 0, 0);
        }
        if (useColorsBuffer) {
            gl.glEnableVertexAttribArray(COLORS_LAYOUT);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, colorsBufferObject);
            gl.glVertexAttribPointer(COLORS_LAYOUT, 4, GL.GL_FLOAT, false, 0, 0);
        }
        if (useUvsBuffer) {
            gl.glEnableVertexAttribArray(UVS_LAYOUT);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, uvsBufferObject);
            gl.glVertexAttribPointer(UVS_LAYOUT, 2, GL.GL_FLOAT, false, 0, 0);
        }
        if (useNormalsBuffer) {
            gl.glEnableVertexAttribArray(NORMALS_LAYOUT);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normalsBufferObject);
            gl.glVertexAttribPointer(NORMALS_LAYOUT, 4, GL.GL_FLOAT, false, 0, 0);
        }
        enableAdditionalVertexAttribArray(gl);
        updateShaderData(gl);
        gl.glDrawArrays(getRenderPrimitive(), 0, getVerticesCount());

        disableAdditionalVertexAttribArray(gl);
        if (useVerticesBuffer) {
            gl.glDisableVertexAttribArray(VERTICES_LAYOUT);
        }
        if (useColorsBuffer) {
            gl.glDisableVertexAttribArray(COLORS_LAYOUT);
        }
        if (useUvsBuffer) {
            gl.glDisableVertexAttribArray(UVS_LAYOUT);
        }
        if (useNormalsBuffer) {
            gl.glDisableVertexAttribArray(NORMALS_LAYOUT);
        }
        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.viewer.jogl.service.ICoreJoglRenderer#performDestroy(javax.media.opengl.GL3)
     */
    @Override
    public void performDestroy(GL3 gl) throws GkException {
        if (!isInitialized()) {
            return;
        }
        List<Integer> lstBuffers = new ArrayList<Integer>();

        if (useVerticesBuffer) {
            lstBuffers.add(verticesBufferObject);
        }
        if (useColorsBuffer) {
            lstBuffers.add(colorsBufferObject);
        }
        if (useUvsBuffer) {
            lstBuffers.add(uvsBufferObject);
        }

        IntBuffer buffers = IntBuffer.allocate(lstBuffers.size());
        for (Integer integer : lstBuffers) {
            buffers.put(integer);
        }
        buffers.rewind();
        gl.glDeleteBuffers(lstBuffers.size(), buffers);
        IntBuffer intBuffer = IntBuffer.wrap(new int[] { vertexArrayObject });
        gl.glDeleteVertexArrays(1, intBuffer);
    }

    /**
     * Update the buffers
     */
    public void update() {
        this.updateBuffer = 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;
    }

}