se.angergard.engine.graphics.ShaderProgram.java Source code

Java tutorial

Introduction

Here is the source code for se.angergard.engine.graphics.ShaderProgram.java

Source

/********************************************************************************
 *
 *   Copyright 2014 Theodor Angergard
 *
 *   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 se.angergard.engine.graphics;

import java.util.*;

import org.lwjgl.opengl.*;

import se.angergard.engine.color.*;
import se.angergard.engine.interfaces.*;
import se.angergard.engine.math.matrix.*;
import se.angergard.engine.math.vector.*;
import se.angergard.engine.util.Util;

/** A GLSL Shader class that creates a program and attaches a vertex and fragment shader. Supports uniforms. Every method return
 * it's self to be able to create a shader in one line for example new
 * ShaderProgram().addVertexShader("code").addFragmentShader("code").compileShader().addUniform("uniform");
 * @author Theodor */
public class ShaderProgram implements Disposable {

    /** Creates a new ShaderProgram, the constructor creates an empty program and creates */
    public ShaderProgram() {
        program = GL20.glCreateProgram();
        uniforms = new HashMap<String, Integer>();

        fragmentShader = vertexShader = geometryShader = -1;
    }

    /** The shader program, will never be re created */
    private final int program;

    /** The shaders, wil be detached as soon as the program has been linked. */
    private int fragmentShader, vertexShader, geometryShader;

    /** A hashmap that consists of the name and the location. */
    private final HashMap<String, Integer> uniforms;

    /** Binds the program to the graphic card.
     * @return This ShaderProgram */
    public ShaderProgram bind() {
        GL20.glUseProgram(program);
        return this;
    }

    /** Links the program and validates it
     * @return This ShaderProgram */
    public ShaderProgram compileProgram() {
        GL20.glLinkProgram(program);

        if (program == -1) {
            System.err.println("ShaderProgram wasn't succsessfully created");
        }

        String error = GL20.glGetProgramInfoLog(program, GL20.GL_INFO_LOG_LENGTH);
        if (!error.equals("")) {
            System.out.println(error);
        }

        GL20.glValidateProgram(program);

        if (vertexShader != -1) {
            GL20.glDetachShader(program, vertexShader);
            GL20.glDeleteShader(vertexShader);
        }
        if (fragmentShader != -1) {
            GL20.glDetachShader(program, fragmentShader);
            GL20.glDeleteShader(fragmentShader);
        }
        if (geometryShader != -1) {
            GL20.glDetachShader(program, geometryShader);
            GL20.glDeleteShader(geometryShader);
        }
        return this;
    }

    /** Add the uniform to the uniforms hashmap
     * @param Uniform name
     * @return This ShaderProgram */
    public ShaderProgram addUniform(String uniform) {
        int location = GL20.glGetUniformLocation(program, uniform);
        if (location == -1) {
            System.err.println("Can't find uniform location :" + uniform);
        } else {
            uniforms.put(uniform, location);
        }
        return this;
    }

    /** Sets the uniform with the given name with the given vector2f. You need the {@link #addUniform(String) addUniform} before
     * being able to set the uniform.
     *
     * @param uniform The uniform name
     * @param v Vector2f
     * @return This ShaderProgram */
    public ShaderProgram setUniformVec2(String uniform, Vector2f v) {
        GL20.glUniform2f(uniforms.get(uniform), v.getX(), v.getY());
        return this;
    }

    public ShaderProgram setUniformVec3(String uniform, Vector3f v) {
        GL20.glUniform3f(uniforms.get(uniform), v.getX(), v.getY(), v.getZ());
        return this;
    }

    public ShaderProgram setUniformVec4(String uniform, Color color) {
        GL20.glUniform4f(uniforms.get(uniform), color.getRed(), color.getGreen(), color.getBlue(),
                color.getAlpha());
        return this;
    }

    /** Sets the uniform with the given name with the given Matrix2f. You need the {@link #addUniform(String) addUniform} before
     * being able to set the uniform.
     *
     * @param uniform The uniform name
     * @param mat Matrix2f
     * @return This ShaderProgram */
    public ShaderProgram setUniformMat2f(String uniform, Matrix2f mat) {
        GL20.glUniformMatrix2(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat));
        return this;
    }

    /** Sets the uniform with the given name with the given Matrix3f. You need the {@link #addUniform(String) addUniform} before
     * being able to set the uniform.
     *
     * @param uniform The uniform name
     * @param mat Matrix3f
     * @return This ShaderProgram */
    public ShaderProgram setUniformMat3f(String uniform, Matrix3f mat) {
        GL20.glUniformMatrix3(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat));
        return this;
    }

    /** Sets the uniform with the given name with the given Matrix4f. You need the {@link #addUniform(String) addUniform} before
     * being able to set the uniform.
     *
     * @param uniform The uniform name
     * @param mat Matrix4f
     * @return This ShaderProgram */
    public ShaderProgram setUniformMat4f(String uniform, Matrix4f mat) {
        // Invalid enum
        GL20.glUniformMatrix4(uniforms.get(uniform), false, Util.toFlippedFloatBuffer(mat));
        return this;
    }

    /** Sets the uniform with the given name with the given float. You need the {@link #addUniform(String) addUniform} before being
     * able to set the uniform.
     *
     * @param uniform The uniform name
     * @param f Float value
     * @return This ShaderProgram */
    public ShaderProgram setUniform1f(String uniform, float f) {
        GL20.glUniform1f(uniforms.get(uniform), f);
        return this;
    }

    /** Sets the uniform with the given name with the given Integer. You need the {@link #addUniform(String) addUniform} before
     * being able to set the uniform.
     *
     * @param uniform The uniform name
     * @param i Integer
     * @return This ShaderProgram */
    public ShaderProgram setUniform1i(String uniform, int i) {
        GL20.glUniform1i(uniforms.get(uniform), i);
        return this;
    }

    public ShaderProgram createDefault2DShader() {
        addVertexShader(Default2DShader.getVertexShader());
        addFragmentShader(Default2DShader.getFragmentShader());
        compileProgram();
        bind();
        addUniform("projection");
        addUniform("color");
        return this;
    }

    public ShaderProgram createDefault3DShader() {
        addVertexShader(Default3DShader.getVertexShader());
        addFragmentShader(Default3DShader.getFragmentShader());
        compileProgram();
        bind();
        addUniform("camera");
        addUniform("color");
        return this;
    }

    public ShaderProgram addVertexShader(String code) {
        return addShader(code, GL20.GL_VERTEX_SHADER);
    }

    public ShaderProgram addFragmentShader(String code) {
        return addShader(code, GL20.GL_FRAGMENT_SHADER);
    }

    @Override
    public void dispose() {
        GL20.glDeleteProgram(program);
    }

    private ShaderProgram addShader(String code, int type) {
        int shader = GL20.glCreateShader(type);
        GL20.glShaderSource(shader, code);
        GL20.glCompileShader(shader);

        if (type == GL20.GL_VERTEX_SHADER) {
            vertexShader = shader;
        }

        else if (type == GL20.GL_FRAGMENT_SHADER) {
            fragmentShader = shader;
        }

        else if (type == GL32.GL_GEOMETRY_SHADER) {
            geometryShader = shader;
        }

        String error = GL20.glGetShaderInfoLog(shader, GL20.GL_INFO_LOG_LENGTH);
        if (!error.equals("")) {
            System.out.println(error);
        }

        if (shader == -1) {
            System.err.println("Shader wasn't succsessfully created");
        }

        GL20.glAttachShader(program, shader);
        return this;
    }

    //@formatter-off
    protected static class Default2DShader {
        protected static final String getVertexShader() {
            return "#version 330 \n" + "layout(location = 0) in vec2 pos; \n"
                    + "layout(location = 1) in vec2 texCoord \n;" + "uniform mat4 projection; \n"
                    + "out vec2 texCoord0; \n" + "void main(){ \n"
                    + "gl_Position = vec4(pos.xy, 0, 1) * projection; \n" + "texCoord0 = texCoord; \n" + "} \n";
        }

        protected final static String getFragmentShader() {
            return "#version 330 \n" + "out vec4 fragColor; \n" + "in vec2 texCoord0; \n" + "uniform vec4 color; \n"
                    + "uniform sampler2D sampler; \n" + "void main(){ \n"
                    + "fragColor = texture2D(sampler, texCoord0.xy) * color;  \n" + "} \n";
        }
    }
    //@formatter-on

    //@formatter-off
    protected static class Default3DShader {
        protected static final String getVertexShader() {
            return "#version 330 \n" + "layout(location = 0) in vec3 pos; \n"
                    + "layout(location = 1) in vec2 texCoord \n;" + "uniform mat4 camera; \n"
                    + "out vec2 texCoord0; \n" + "void main(){ \n" + "gl_Position = vec4(pos.xyz, 1) * camera; \n"
                    + "texCoord0 = texCoord; \n" + "} \n";
        }

        protected final static String getFragmentShader() {
            return "#version 330 \n" + "out vec4 fragColor; \n" + "in vec2 texCoord0; \n"
                    + "uniform vec4 color = {1, 1, 1, 0.5}; \n" + "uniform sampler2D sampler; \n"
                    + "void main(){ \n" + "fragColor = texture2D(sampler, texCoord0.xy) * color;  \n" + "} \n";
        }
    }
    //@formatter-on

}