Newer
Older
kotlin-webgl-test / src / main / kotlin / games / perses / shader / ShaderProgram.kt
rnentjes on 21 Mar 2018 4 KB Update kotlin version
package games.perses.shader

import org.khronos.webgl.Float32Array
import org.khronos.webgl.WebGLBuffer
import org.khronos.webgl.WebGLProgram
import org.khronos.webgl.WebGLRenderingContext
import org.khronos.webgl.WebGLShader

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

class ShaderProgram<T>(
    val webgl: WebGLRenderingContext,
    val drawType: Int,
    vertexShaderSource: String,
    fragmentShaderSource: String,
    val vainfo: Array<VertextAttributeInfo>,
    val setter: (program: ShaderProgram<T>, data: T) -> Unit
) {

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

  var verticesBlockSize = 0
  var drawLength = 0

  init {
    vertex = compileShader(vertexShaderSource, WebGLRenderingContext.VERTEX_SHADER)
    fragment = compileShader(fragmentShaderSource, WebGLRenderingContext.FRAGMENT_SHADER)

    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.getProgramParameter(shaderProgram, WebGLRenderingContext.LINK_STATUS) == false) {
      //println(webgl.getProgramInfoLog(shaderProgram))
      throw IllegalStateException("Unable to compile shader 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}")
    }

    when (drawType) {
      WebGLRenderingContext.TRIANGLES -> {
        drawLength = verticesBlockSize * 3
      }
      else -> {
        drawLength = verticesBlockSize
      }
    }

    //println("verticesBlockSize $verticesBlockSize")

    //println("ShaderProgram constructor done")
  }

  private fun compileShader(source: String, type: Int): WebGLShader {
    val result: WebGLShader = webgl.createShader(type)
        ?: throw IllegalStateException("Unable to request shader from webgl context!")

    webgl.shaderSource(result, source)
    webgl.compileShader(result)

    if (webgl.getShaderParameter(result, WebGLRenderingContext.COMPILE_STATUS) == false) {
      throw IllegalStateException("Unable to compile shader!\n${source}\n\n${webgl.getShaderInfoLog(result)}")
    }

    return result
  }

  fun begin(attribBuffer: WebGLBuffer, userdata: T) {
    webgl.useProgram(shaderProgram);
    webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer);

    // 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
      )
    }

    setter(this, userdata)
  }

  fun end() {
    for (info in vainfo.iterator()) {
      webgl.disableVertexAttribArray(info.location);
    }
    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) {
    webgl.uniform1f(getUniformLocation(location), value); }

  fun setUniform2f(location: String, v1: Float, v2: Float) {
    webgl.uniform2f(getUniformLocation(location), v1, v2); }

  fun setUniform3f(location: String, v1: Float, v2: Float, v3: Float) {
    webgl.uniform3f(getUniformLocation(location), v1, v2, v3); }

  fun setUniform4f(location: String, v1: Float, v2: Float, v3: Float, v4: Float) {
    webgl.uniform4f(getUniformLocation(location), v1, v2, v3, v4); }

  fun setUniform1i(location: String, value: Int) {
    webgl.uniform1i(getUniformLocation(location), value); }

  fun setUniformMatrix4fv(location: String, value: Float32Array) {
    webgl.uniformMatrix4fv(getUniformLocation(location), false, value); }

}