GLShader.java 6.6 KB
Newer Older
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
1 2
package com.projectseptember.RNGL;

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
3

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
import static android.opengl.GLES20.*;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;

public class GLShader {

    private final String name;
    private final String vert;
    private final String frag;
    private Map<String, Integer> uniformTypes;
    private int program; // Program of the shader
    private int buffer[]; // the buffer currently contains 2 static triangles covering the surface
    private int pointerLoc; // The "pointer" attribute is used to iterate over vertex
    private Map<String, Integer> uniformLocations; // The uniform locations cache

    public GLShader(String name, String vert, String frag) {
        this.name = name;
        this.vert = vert;
        this.frag = frag;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (buffer != null) {
            glDeleteProgram(program);
            glDeleteBuffers(1, buffer, 0);
        }
    }

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
39 40 41 42
    public void runtimeException (String msg) {
        throw new RuntimeException("Shader '"+name+"': "+msg);
    }

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
43
    public void bind () {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
44 45
        ensureCompile();

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
46
        if (!glIsProgram(program)) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
47
            runtimeException("not a program");
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
48 49 50 51 52 53 54 55 56 57 58 59 60
        }
        glUseProgram(program);
        glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
        glEnableVertexAttribArray(pointerLoc);
        glVertexAttribPointer(pointerLoc, 2, GL_FLOAT, false, 0, 0);
    }

    public void validate () {
        glValidateProgram(program);
        int[] validSuccess = new int[1];
        glGetProgramiv(program, GL_VALIDATE_STATUS, validSuccess, 0);
        if (validSuccess[0] == GL_FALSE) {
            glGetProgramInfoLog(program);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
61
            runtimeException("Validation failed " + glGetProgramInfoLog(program));
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
        }
    }

    public void setUniform (String name, Integer i) {
        glUniform1i(uniformLocations.get(name), i);
    }
    public void setUniform (String name, Float f) {
        glUniform1f(uniformLocations.get(name), f);
    }
    public void setUniform (String name, FloatBuffer buf, int type) {
        switch (type) {
            case GL_FLOAT_VEC2:
                glUniform2fv(uniformLocations.get(name), 1, buf);
                break;
            case GL_FLOAT_VEC3:
                glUniform3fv(uniformLocations.get(name), 1, buf);
                break;
            case GL_FLOAT_VEC4:
                glUniform4fv(uniformLocations.get(name), 1, buf);
                break;
            case GL_FLOAT_MAT2:
                glUniformMatrix2fv(uniformLocations.get(name), 1, false, buf);
                break;
            case GL_FLOAT_MAT3:
                glUniformMatrix3fv(uniformLocations.get(name), 1, false, buf);
                break;
            case GL_FLOAT_MAT4:
                glUniformMatrix4fv(uniformLocations.get(name), 1, false, buf);
                break;
            default:
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
92
                runtimeException("Unsupported case: uniform '" + name + "' type: " + type);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
        }
    }
    public void setUniform (String name, IntBuffer buf, int type) {
        switch (type) {
            case GL_INT_VEC2:
            case GL_BOOL_VEC2:
                glUniform2iv(uniformLocations.get(name), 1, buf);
                break;
            case GL_INT_VEC3:
            case GL_BOOL_VEC3:
                glUniform3iv(uniformLocations.get(name), 1, buf);
                break;
            case GL_INT_VEC4:
            case GL_BOOL_VEC4:
                glUniform4iv(uniformLocations.get(name), 1, buf);
                break;
            default:
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
110
                runtimeException("Unsupported case: uniform '"+name+"' type: "+type);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
111 112 113 114 115 116 117 118 119 120 121 122
        }
    }

    public String getName() {
        return name;
    }

    public Map<String, Integer> getUniformTypes() {
        return uniformTypes;
    }


Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
123
    private int compileShader (String code, int shaderType) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
124 125 126 127 128 129
        int shaderHandle = glCreateShader(shaderType);
        glShaderSource(shaderHandle, code);
        glCompileShader(shaderHandle);
        int compileSuccess[] = new int[1];
        glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, compileSuccess, 0);
        if (compileSuccess[0] == GL_FALSE) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
130
            runtimeException("failed to compile: " + glGetShaderInfoLog(shaderHandle));
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
            return -1;
        }
        return shaderHandle;
    }

    private void computeMeta () {
        Map<String, Integer> uniforms = new HashMap<>();
        Map<String, Integer> locations = new HashMap<>();

        int[] nbUniforms = new int[1];
        int[] type = new int[1];
        int[] size = new int[1];
        glGetProgramiv(program, GL_ACTIVE_UNIFORMS, nbUniforms, 0);
        for (int i=0; i < nbUniforms[0]; i++) {
            String uniformName = glGetActiveUniform(program, i, size, 0, type, 0);
            int location = glGetUniformLocation(program, uniformName);
            uniforms.put(uniformName, type[0]);
            locations.put(uniformName, location);
        }
        this.uniformTypes = uniforms;
        this.uniformLocations = locations;
    }

    private void makeProgram () {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
155
        int vertex = compileShader(vert, GL_VERTEX_SHADER);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
156 157
        if (vertex == -1) return;

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
158
        int fragment = compileShader(frag, GL_FRAGMENT_SHADER);
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
159 160 161 162 163 164 165 166 167 168
        if (fragment == -1) return;

        program = glCreateProgram();
        glAttachShader(program, vertex);
        glAttachShader(program, fragment);
        glLinkProgram(program);

        int[] linkSuccess = new int[1];
        glGetProgramiv(program, GL_LINK_STATUS, linkSuccess, 0);
        if (linkSuccess[0] == GL_FALSE) {
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
169
            runtimeException("Linking failed "+glGetProgramInfoLog(program));
Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
170 171 172 173
        }

        glUseProgram(program);

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
174 175
        validate();

Gaëtan Renaudeau's avatar
WIP  
Gaëtan Renaudeau committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
        computeMeta();

        pointerLoc = glGetAttribLocation(program, "position");

        buffer = new int[1];
        glGenBuffers(1, buffer, 0);
        glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

        float buf[] = {
                -1.0f, -1.0f,
                1.0f, -1.0f,
                -1.0f,  1.0f,
                -1.0f,  1.0f,
                1.0f, -1.0f,
                1.0f,  1.0f
        };
        FloatBuffer bufferData = ByteBuffer.allocateDirect(buf.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        bufferData.put(buf).position(0);

        glBufferData(GL_ARRAY_BUFFER, buf.length * 4, bufferData, GL_STATIC_DRAW);
    }

    public boolean isReady () {
        return buffer != null && uniformLocations != null;
    }

    public boolean ensureCompile() {
        if (!isReady()) makeProgram();
        return isReady();
    }
}