commonMain.ru.casperix.opengl.renderer.shader.ShaderBuffer.kt Maven / Gradle / Ivy
package ru.casperix.opengl.renderer.shader
import ru.casperix.math.vector.int32.Vector2i
import ru.casperix.misc.Disposable
import ru.casperix.misc.toString
import ru.casperix.opengl.core.*
import ru.casperix.opengl.renderer.util.ConsoleMagic
class ShaderBuffer(val vertex: String, val fragment: String, val geom: String?) : Disposable {
private val GL_TRUE = 1
private val GL_GEOMETRY_SHADER = -1
val programId = glCreateProgram()
init {
val vertexShader = createShader(vertex, GL_VERTEX_SHADER)
val fragmentShader = createShader(fragment, GL_FRAGMENT_SHADER)
val geometryShader = if (geom != null) {
createShader(geom, GL_GEOMETRY_SHADER)
} else null
glAttachShader(programId, vertexShader)
if (geometryShader != null) glAttachShader(programId, geometryShader)
glAttachShader(programId, fragmentShader)
glLinkProgram(programId)
val status = glGetProgrami(programId, GL_LINK_STATUS)
if (status != GL_TRUE) {
val info = glGetProgramInfoLog(programId)
throw RuntimeException(info)
}
glDetachShader(programId, vertexShader)
glDetachShader(programId, fragmentShader)
glDeleteShader(vertexShader)
glDeleteShader(fragmentShader)
if (geometryShader != null) {
glDetachShader(programId, geometryShader)
glDeleteShader(geometryShader)
}
}
override fun dispose() {
glDeleteProgram(programId)
}
fun bind() {
glUseProgram(programId)
}
fun unbind() {
glUseProgram(0)
}
private fun createShader(source: String, gl_type: Int): Int {
val shader = glCreateShader(gl_type)
glShaderSource(shader, source)
glCompileShader(shader)
val status = glGetShaderi(shader, GL_COMPILE_STATUS)
if (status != 1) {
val info = glGetShaderInfoLog(shader) ?: "unknown gl-error"
printShaderError(source, info)
throw RuntimeException(info)
}
return shader
}
private fun parseErrorPosition(error: String): Vector2i? {
val errorFirstLine = error.split(Regex("\n"), 2).firstOrNull() ?: ""
val item = Regex("\\d+:\\d+").find(errorFirstLine)
if (item != null) {
val numbers = item.value.split(":").map { it.toInt() }
if (numbers.size != 2) return null
return Vector2i(numbers[0], numbers[1])
}
return null
}
private fun printShaderError(source: String, error: String) {
val errorPosition = parseErrorPosition(error) ?: Vector2i(-1)
val output = source.split("\n").mapIndexed { lineIndex, line ->
val lineIndexFormatted = lineIndex.toString(3, ' ')
if (lineIndex != errorPosition.y - 1) {
ConsoleMagic.makeWhite(lineIndexFormatted) +" $line"
} else {
ConsoleMagic.makeRed(lineIndexFormatted) + ConsoleMagic.makeRed(" $line <<<")
}
}
println(output.joinToString("\n"))
}
}