akarnokd.opengl.experiment.ShaderProgram.java Source code

Java tutorial

Introduction

Here is the source code for akarnokd.opengl.experiment.ShaderProgram.java

Source

/**
 * Copyright 2013 David Karnok
 *
 * 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 akarnokd.opengl.experiment;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBFragmentShader;
import static org.lwjgl.opengl.ARBShaderObjects.*;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;
import org.newdawn.slick.openal.StreamSound;

/**
 * Load and compile a vertex and fragment shader program.
 */
public class ShaderProgram {
    /** The program identifier. */
    private int program;
    private int vertex;
    private int fragment;

    private ShaderProgram() {
    }

    /** The cached uniform locations. */
    private final Map<String, Integer> uniformCache = new HashMap<>();

    public static ShaderProgram createFromResource(String vertex, String fragment) {
        return create(() -> readResource(vertex), () -> readResource(fragment));
    }

    static String readResource(String name) {
        try (InputStream in = ShaderProgram.class.getResourceAsStream(name)) {
            byte[] buffer = new byte[8192];
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            while (true) {
                int r = in.read(buffer);
                if (r < 0) {
                    break;
                } else if (r > 0) {
                    bout.write(buffer, 0, r);
                }
            }
            return bout.toString("UTF-8");
        } catch (Throwable t) {
            t.printStackTrace();
            return "";
        }
    }

    /**
     * Creates a shader program from the vertex and fragment shaders.
     * @param vertex
     * @param fragment
     * @return 
     */
    public static ShaderProgram create(Supplier<String> vertex, Supplier<String> fragment) {

        ShaderProgram sp = new ShaderProgram();

        sp.vertex = createShader(ARBVertexShader.GL_VERTEX_SHADER_ARB, vertex);
        sp.fragment = createShader(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB, fragment);

        if (sp.vertex != 0 && sp.fragment != 0) {
            sp.program = glCreateProgramObjectARB();
            if (sp.program != 0) {

                glAttachObjectARB(sp.program, sp.vertex);
                glAttachObjectARB(sp.program, sp.fragment);

                glLinkProgramARB(sp.program);
                if (glGetObjectParameteriARB(sp.program, GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
                    String err = getLogInfo(sp.program);
                    sp.close();
                    throw new RuntimeException("Program failed: " + err);
                }
                glValidateProgramARB(sp.program);
                if (glGetObjectParameteriARB(sp.program, GL_OBJECT_VALIDATE_STATUS_ARB) == GL11.GL_FALSE) {
                    String err = getLogInfo(sp.program);
                    sp.close();
                    throw new RuntimeException("Program failed: " + err);
                }
            } else {
                sp.close();
                throw new RuntimeException("Unable to create program");
            }
        }

        return sp;
    }

    /** 
     * @return check if the program is valid
     */
    public boolean isValid() {
        return program != 0;
    }

    /** Releases the resources. */
    public void close() {
        if (program != 0) {
            glDeleteObjectARB(program);
            program = 0;
        }
        if (vertex != 0) {
            glDeleteObjectARB(vertex);
            vertex = 0;
        }
        if (fragment != 0) {
            glDeleteObjectARB(fragment);
            fragment = 0;
        }
    }

    /** Start using the program. */
    public void use() {
        glUseProgramObjectARB(program);
    }

    /** Stop using the program. */
    public void stop() {
        glUseProgramObjectARB(0);
    }

    /**
     * Create and compile a shader object
     * @param shaderType the shader type: ARBVertexShader.GL_VERTEX_SHADER_ARB or ARBFragmentShader.GL_FRAGMENT_SHADER_ARB
     * @param shaderCode the shader code supplier
     * @return the shader id or null if not supported
     */
    private static int createShader(int shaderType, Supplier<String> shaderCode) {
        int sid = glCreateShaderObjectARB(shaderType);
        if (sid == 0) {
            return 0;
        }
        glShaderSourceARB(sid, shaderCode.get());
        glCompileShaderARB(sid);

        if (glGetObjectParameteriARB(sid, GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE) {
            String err = getLogInfo(sid);
            glDeleteObjectARB(sid);
            throw new RuntimeException("Error creating shader: " + err);
        }
        return sid;
    }

    /** Return the log info. */
    static String getLogInfo(int obj) {
        int len = glGetObjectParameteriARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB);
        return glGetInfoLogARB(obj, len);
    }

    /**
     * Locate an uniform variable.
     * @param name
     * @return 
     */
    public int findUniform(String name) {
        Objects.requireNonNull(name);
        Integer r = uniformCache.get(name);
        if (r == null) {
            r = glGetUniformLocationARB(program, name);
            if (r < 0) {
                throw new IllegalArgumentException("Uniform not found: " + name);
            }
            uniformCache.put(name, r);
        }
        return r;
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y 
     */
    public void setUniformf(String name, float x, float y) {
        int index = findUniform(name);
        glUniform2fARB(index, x, y);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y 
     */
    public void setUniformi(String name, int x, int y) {
        int index = findUniform(name);
        glUniform2iARB(index, x, y);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y
     * @param z
     */
    public void setUniformf(String name, float x, float y, float z) {
        int index = findUniform(name);
        glUniform3fARB(index, x, y, z);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y
     * @param z
     */
    public void setUniformi(String name, int x, int y, int z) {
        int index = findUniform(name);
        glUniform3iARB(index, x, y, z);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y
     * @param z
     * @param w
     */
    public void setUniformf(String name, float x, float y, float z, float w) {
        int index = findUniform(name);
        glUniform4fARB(index, x, y, z, w);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     * @param y
     * @param z
     * @param w
     */
    public void setUniformi(String name, int x, int y, int z, int w) {
        int index = findUniform(name);
        glUniform4iARB(index, x, y, z, w);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     */
    public void setUniformf(String name, float x) {
        int index = findUniform(name);
        glUniform1fARB(index, x);
    }

    /**
     * Set the value of an uniform shader variable.
     * @param name
     * @param x
     */
    public void setUniformi(String name, int x) {
        int index = findUniform(name);
        glUniform1iARB(index, x);
    }

    /**
     * Set a Matrix4f
     * @param name
     * @param transposed
     * @param mat 
     */
    public void setUniformMatrix(String name, boolean transposed, Matrix4f mat) {
        int index = findUniform(name);
        FloatBuffer b16 = BufferUtils.createFloatBuffer(16);
        mat.store(b16);
        b16.flip();
        glUniformMatrix4ARB(index, transposed, b16);
    }

    /**
     * Set an array of values.
     * @param name
     * @param array 
     */
    public void setUniformi(String name, int[] array) {
        int index = findUniform(name);
        IntBuffer ib = BufferUtils.createIntBuffer(array.length);
        ib.put(array);
        ib.rewind();
        glUniform1ARB(index, ib);
    }

    /**
     * Set an array of values.
     * @param name
     * @param array 
     */
    public void setUniformf(String name, float[] array) {
        int index = findUniform(name);
        FloatBuffer fb = BufferUtils.createFloatBuffer(array.length);
        fb.put(array);
        fb.rewind();
        glUniform1ARB(index, fb);
    }
}