Newer
Older
kotlin-webgl-test / src / com / persesgames / shader / ShaderProgram.kt
rnentjes on 17 Apr 2016 4 KB Initial commit
package com.persesgames.shader

import org.khronos.webgl.*

/**
 * User: rnentjes
 * Date: 17-4-16
 * Time: 15:15
 */

class VertextAttributeInfo(val locationName: String, val numElements: Int) {
    var location = 0
    var offset = 0
}

class ShaderProgram(val webgl: WebGLRenderingContext, val mode: Int, vertexShaderSource: String, fragmentShaderSource: String, val vainfo: Array<VertextAttributeInfo>) {

    var shaderProgram: WebGLProgram
    var vertex: WebGLShader
    var fragment: WebGLShader

    var verticesBlockSize = 0
    var currentIndex = 0
    var verticesLength = 0
    var vertices = Float32Array(0)

    var attribBuffer: WebGLBuffer

    init {
        vertex = webgl.createShader(WebGLRenderingContext.VERTEX_SHADER) ?: throw IllegalStateException("Unable to request vertex shader from webgl context!")
        webgl.shaderSource(vertex, vertexShaderSource)
        webgl.compileShader(vertex)

        fragment = webgl.createShader(WebGLRenderingContext.FRAGMENT_SHADER) ?: throw IllegalStateException("Unable to request fragment shader from webgl context!")
        webgl.shaderSource(fragment, fragmentShaderSource)
        webgl.compileShader(fragment)

        shaderProgram = webgl.createProgram() ?: throw IllegalStateException("Unable to request shader program from webgl context!")
        webgl.attachShader(shaderProgram, vertex)
        webgl.attachShader(shaderProgram, fragment)
        webgl.linkProgram(shaderProgram)

        if (webgl.getShaderParameter(vertex, WebGLRenderingContext.COMPILE_STATUS) == false) {
            println(webgl.getShaderInfoLog(vertex))
            throw IllegalStateException("Unable to compile vertex shader!")
        }

        if (webgl.getShaderParameter(fragment, WebGLRenderingContext.COMPILE_STATUS) == false) {
            println(webgl.getShaderInfoLog(fragment))
            throw IllegalStateException("Unable to compile fragment shader!")
        }

        if (webgl.getProgramParameter(shaderProgram, WebGLRenderingContext.LINK_STATUS) == false) {
            println(webgl.getProgramInfoLog(shaderProgram))
            throw IllegalStateException("Unable to compile program!")
        }

        webgl.useProgram(shaderProgram)

        this.verticesBlockSize = 0;

        // set attribute locations...
        for (info in vainfo.iterator()) {
            info.location = webgl.getAttribLocation(shaderProgram, info.locationName)
            info.offset = verticesBlockSize;

            verticesBlockSize += info.numElements;
            println("attrib: ${info.locationName}, info.location: ${info.location}, info.offset: ${info.offset}");
        }

        println("verticesBlockSize $verticesBlockSize");

        this.currentIndex = 0;

        // create vertices buffer
        verticesLength = 4096 - (4096 % verticesBlockSize);
        vertices = Float32Array(verticesLength);

        println("vertices.length ${vertices.length}");

        attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!")
        webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer);

        println("ShaderProgram constructor done");
    }

    fun queueVertices(verts: Float32Array) {
        if((currentIndex + verts.length) >= verticesLength) {
            flush();
        }

        vertices.set(verts, currentIndex)
        currentIndex += verts.length
    }

    fun begin() {
        webgl.useProgram(shaderProgram);
        webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer);
        currentIndex = 0;

        // set attribute locations...
        for (info in vainfo.iterator()) {
            webgl.enableVertexAttribArray(info.location);
            webgl.vertexAttribPointer(info.location, info.numElements, WebGLRenderingContext.FLOAT, false, verticesBlockSize * 4, info.offset * 4);
        }

    }

    fun flush() {
        if (currentIndex > 0) {
            webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, vertices, WebGLRenderingContext.DYNAMIC_DRAW);
            webgl.drawArrays(mode, 0, (currentIndex / verticesBlockSize).toInt());
            currentIndex = 0;
        }
    }

    fun end() {
        flush()
        webgl.useProgram(null)
    }

    fun getAttribLocation(location: String) = webgl.getAttribLocation(shaderProgram, location);

    fun getUniformLocation(location: String) = webgl.getUniformLocation(shaderProgram, location);

    fun setUniform1f(location: String, value: Float) { flush(); webgl.uniform1f(getUniformLocation(location), value); }
    fun setUniform4f(location: String, v1: Float, v2: Float, v3: Float, v4: Float) { flush(); webgl.uniform4f(getUniformLocation(location), v1, v2, v3, v4); }
    fun setUniform1i(location: String, value: Int) { flush(); webgl.uniform1i(getUniformLocation(location), value); }
    fun setUniformMatrix4fv(location: String, value: Float32Array) { flush(); webgl.uniformMatrix4fv(getUniformLocation(location), false, value); }

}