com.kauridev.lunarfever.graphics.ShaderProgram.java Source code

Java tutorial

Introduction

Here is the source code for com.kauridev.lunarfever.graphics.ShaderProgram.java

Source

/*
 * This file is part of the lunar-fever package.
 *
 * Copyright (c) 2014 Eric Fritz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.kauridev.lunarfever.graphics;

import java.nio.FloatBuffer;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.vector.Matrix4f;

/**
 * @author Eric Fritz
 */
public class ShaderProgram {
    private int program;
    private int vert;
    private int frag;

    public static boolean isSupported() {
        ContextCapabilities c = GLContext.getCapabilities();
        return c.GL_ARB_shader_objects && c.GL_ARB_vertex_shader && c.GL_ARB_fragment_shader;
    }

    public ShaderProgram(String vertexShaderSource, String fragShaderSource,
            List<VertexAttribute> attribLocations) {
        if (vertexShaderSource == null || fragShaderSource == null) {
            throw new IllegalArgumentException("shader source must be non-null");
        }

        if (!isSupported()) {
            throw new RuntimeException("no shader support found; shaders require OpenGL 2.0");
        }

        program = createProgram();
        vert = compileShader(GL20.GL_VERTEX_SHADER, vertexShaderSource);
        frag = compileShader(GL20.GL_FRAGMENT_SHADER, fragShaderSource);

        linkProgram(attribLocations);
    }

    public ShaderProgram(String vertexShaderSource, String fragShaderSource) {
        this(vertexShaderSource, fragShaderSource, null);
    }

    public int getID() {
        return program;
    }

    public boolean isValid() {
        return program != 0;
    }

    public void use() {
        if (!isValid()) {
            throw new IllegalStateException();
        }

        GL20.glUseProgram(program);
    }

    public void dispose() {
        if (isValid()) {
            disposeShaders();
            GL20.glDeleteProgram(program);
            program = 0;
        }
    }

    private int createProgram() {
        int program = GL20.glCreateProgram();

        if (program == 0) {
            throw new RuntimeException();
        }

        return program;
    }

    private int compileShader(int type, String source) {
        int shader = GL20.glCreateShader(type);

        if (shader == 0) {
            throw new RuntimeException();
        }

        GL20.glShaderSource(shader, source);
        GL20.glCompileShader(shader);

        int comp = GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS);
        int len = GL20.glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH);

        if (comp == GL11.GL_FALSE) {
            throw new RuntimeException(GL20.glGetShaderInfoLog(shader, len));
        }

        return shader;
    }

    private void linkProgram(List<VertexAttribute> attribLocations) {
        if (!isValid()) {
            throw new RuntimeException();
        }

        if (attribLocations != null) {
            for (VertexAttribute a : attribLocations) {
                if (a != null) {
                    GL20.glBindAttribLocation(program, a.location, a.name);
                }
            }
        }

        attachShaders();
        GL20.glLinkProgram(program);
        int comp = GL20.glGetProgrami(program, GL20.GL_LINK_STATUS);
        int len = GL20.glGetProgrami(program, GL20.GL_INFO_LOG_LENGTH);

        if (comp == GL11.GL_FALSE) {
            throw new RuntimeException(GL20.glGetProgramInfoLog(program, len));
        }
    }

    private void attachShaders() {
        GL20.glAttachShader(program, vert);
        GL20.glAttachShader(program, frag);
    }

    private void disposeShaders() {
        if (vert != 0) {
            GL20.glDetachShader(program, vert);
            GL20.glDeleteShader(vert);
            vert = 0;
        }

        if (frag != 0) {
            GL20.glDetachShader(program, frag);
            GL20.glDeleteShader(frag);
            frag = 0;
        }
    }

    //
    // Uniform setters

    public void setUniformi(String name, int i) {
        setUniformi(getUniformLocation(name), i);
    }

    private void setUniformi(int loc, int i) {
        if (loc == -1) {
            return;
        }

        GL20.glUniform1i(loc, i);
    }

    public void setUniformMatrix(String name, boolean transpose, Matrix4f m) {
        setUniformMatrix(getUniformLocation(name), transpose, m);
    }

    private void setUniformMatrix(int loc, boolean transpose, Matrix4f m) {
        if (loc == -1) {
            return;
        }

        FloatBuffer fbuf16 = BufferUtils.createFloatBuffer(16);

        m.store(fbuf16);
        fbuf16.flip();
        GL20.glUniformMatrix4(loc, transpose, fbuf16);
    }

    //
    // Helpers

    private int getUniformLocation(String name) {
        return GL20.glGetUniformLocation(program, name);
    }
}