wrath.client.graphics.ShaderProgram.java Source code

Java tutorial

Introduction

Here is the source code for wrath.client.graphics.ShaderProgram.java

Source

/**
 *  Wrath Engine 
 *  Copyright (C) 2015  Trent Spears
 *
 *  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 wrath.client.graphics;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.HashMap;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import wrath.client.Game;
import wrath.common.Closeable;
import wrath.util.Logger;

/**
 * Class used to describe and load shader programs.
 * @author Trent Spears
 */
public class ShaderProgram implements Closeable {
    public static ShaderProgram DEFAULT_SHADER;
    public static ShaderProgram DEFAULT_TERRAIN_SHADER;

    /**
     * Reads the two specified shader files and compiles the shaders into an OpenGL program format.
     * It is recommended that shaders be stored in the 'assets/shaders' directory (which is not present by default).
     * @param vertFile The {@link java.io.File} to read the vert shader from.
     * @param fragFile The {@link java.io.File} to read the frag shader from.
     * @return Returns the ShaderProgram object.
     */
    public static ShaderProgram loadShaderProgram(File vertFile, File fragFile) {
        if (!vertFile.exists()) {
            System.err.println(
                    "Could not load shader from file '" + vertFile.getAbsolutePath() + "'! File not found!");
            return null;
        }

        if (!fragFile.exists()) {
            System.err.println(
                    "Could not load shader from file '" + fragFile.getAbsolutePath() + "'! File not found!");
            return null;
        }

        String vsrc = "";

        try {
            String inp;
            try (BufferedReader read = new BufferedReader(new FileReader(vertFile))) {
                while ((inp = read.readLine()) != null)
                    vsrc = vsrc + inp + '\n';
            }
        } catch (IOException e) {
            System.err.println("Could not load shader from file '" + vertFile.getAbsolutePath() + "'! I/O Error!");
            return null;
        }

        String fsrc = "";

        try {
            String inp;
            try (BufferedReader read = new BufferedReader(new FileReader(fragFile))) {
                while ((inp = read.readLine()) != null)
                    fsrc = fsrc + inp + '\n';
            }
        } catch (IOException e) {
            System.err.println("Could not load shader from file '" + fragFile.getAbsolutePath() + "'! I/O Error!");
            return null;
        }

        int prog = GL20.glCreateProgram();
        int vert = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        int frag = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);

        GL20.glShaderSource(vert, vsrc);
        GL20.glShaderSource(frag, fsrc);

        GL20.glCompileShader(vert);
        if (GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println(
                    "Could not load shader from file '" + vertFile.getAbsolutePath() + "'! Compile Error:");
            System.err.println(GL20.glGetShaderInfoLog(vert));
            return null;
        }

        GL20.glCompileShader(frag);
        if (GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println(
                    "Could not load shader from file '" + fragFile.getAbsolutePath() + "'! Compile Error:");
            System.err.println(GL20.glGetShaderInfoLog(frag));
            return null;
        }

        GL20.glAttachShader(prog, vert);
        GL20.glAttachShader(prog, frag);

        ShaderProgram ret = new ShaderProgram(prog, vert, frag);
        Game.getCurrentInstance().addToTrashCleanup(ret);
        return ret;
    }

    private boolean finalized = false;
    private final int programID, vertShaderID, fragShaderID;
    private static final FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
    private final HashMap<String, Integer> uniformMap = new HashMap<>();

    private ShaderProgram(int programID, int vertShaderID, int fragShaderID) {
        this.programID = programID;
        this.vertShaderID = vertShaderID;
        this.fragShaderID = fragShaderID;
    }

    /**
     * Binds an OpenGL attribute to the shader program.
     * This all must be done before the shader program is run.
     * @param attribute The OpenGL attribute ID.
     * @param variable The {@link java.lang.String} form of the attribute variable name.
     */
    public void bindAttribute(int attribute, String variable) {
        if (finalized)
            return;
        GL20.glBindAttribLocation(programID, attribute, variable);
    }

    /**
     * Binds OpenGL to use this shader program.
     */
    public void bindShader() {
        GL20.glUseProgram(programID);
    }

    @Override
    public void close() {
        GL20.glUseProgram(0);
        GL20.glDetachShader(programID, vertShaderID);
        GL20.glDetachShader(programID, fragShaderID);
        GL20.glDeleteShader(vertShaderID);
        GL20.glDeleteShader(fragShaderID);
        GL20.glDeleteProgram(programID);
        Game.getCurrentInstance().removeFromTrashCleanup(this);
    }

    /**
     * Gets the OpenGL integer ID of this shader program.
     * @return Returns the OpenGL integer ID of this shader program.
     */
    public int getProgramID() {
        return programID;
    }

    /**
     * Gets the integer location of a uniform variable.
     * @param variableName The {@link java.lang.String} name of the Uniform variable.
     * @return Returns the integer location of a uniform variable.
     */
    public int getUniformVariableLocation(String variableName) {
        if (uniformMap.containsKey(variableName)) {
            int ret = uniformMap.get(variableName);
            if (ret != -1)
                return ret;
        }

        GL20.glUseProgram(programID);
        int ret = GL20.glGetUniformLocation(programID, variableName);
        uniformMap.put(variableName, ret);
        return ret;
    }

    /**
     * If true, the program cannot be edited and is ready for rendering.
     * @return Returns true if the program cannot be edited and is ready for rendering.
     */
    public boolean isFinalized() {
        return finalized;
    }

    /**
     * Changes the shader's projection matrix to the one specified.
     * This will only work with the 3D shader!
     * @param value The {@link org.lwjgl.util.vector.Matrix4f} object containing the shader projection data.
     */
    public void setProjectionMatrix(Matrix4f value) {
        GL20.glUseProgram(programID);
        FloatBuffer pbuf = BufferUtils.createFloatBuffer(16);
        value.store(pbuf);
        pbuf.flip();
        GL20.glUniformMatrix4fv(getUniformVariableLocation("projectionMatrix"), false, pbuf);
    }

    /**
     * Changes the shader's transformation matrix to the one specified.
     * @param value The {@link org.lwjgl.util.vector.Matrix4f} object containing the shader transformation data.
     */
    public void setTransformationMatrix(Matrix4f value) {
        GL20.glUseProgram(programID);
        value.store(matrixBuf);
        matrixBuf.flip();
        GL20.glUniformMatrix4fv(getUniformVariableLocation("transformationMatrix"), false, matrixBuf);
    }

    /**
     * Sets the value of a uniform variable in the shader.
     * @param location The integer id of the Uniform variable.
     * @param value The value to set.
     */
    public void setUniformVariable(int location, float value) {
        GL20.glUseProgram(programID);
        GL20.glUniform1f(location, value);
    }

    /**
     * Sets the value of a uniform variable in the shader.
     * @param location The integer id of the Uniform variable.
     * @param value The value to set.
     */
    public void setUniformVariable(int location, Vector3f value) {
        GL20.glUseProgram(programID);
        GL20.glUniform3f(location, value.x, value.y, value.z);
    }

    /**
     * Sets the value of a uniform variable in the shader.
     * @param location The integer id of the Uniform variable.
     * @param value The value to set.
     */
    public void setUniformVariable(int location, boolean value) {
        GL20.glUseProgram(programID);
        GL20.glUniform1f(location, value ? 1f : 0f);
    }

    /**
     * Sets the value of a uniform variable in the shader.
     * @param location The integer id of the Uniform variable.
     * @param value The value to set.
     */
    public void setUniformVariable(int location, Matrix4f value) {
        GL20.glUseProgram(programID);
        value.store(matrixBuf);
        matrixBuf.flip();
        GL20.glUniformMatrix4fv(location, false, matrixBuf);
    }

    /**
     * Finalizes the shader and prepares it for rendering.
     * This is called automatically!
     */
    public void finish() {
        GL20.glUseProgram(programID);
        GL20.glLinkProgram(programID);
        GL20.glValidateProgram(programID);
        setProjectionMatrix(Game.getCurrentInstance().getRenderer().getProjectionMatrix());
        finalized = true;
    }

    /**
     * Updates to the specified camera's current View Matrix.
     * This is automatic.
     */
    protected void updateViewMatrix() {
        Game.getCurrentInstance().getPlayerCamera().updateViewMatrix(this);
    }

    /**
     * Unbinds any shader program currently being used by OpenGL.
     */
    public static void unbindShaders() {
        GL20.glUseProgram(0);
    }
}