aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt
blob: 03b2fb3acddb3f35df5e9dda2e6beeba6f4f9cf3 (plain)
1
2
3
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package at.hannibal2.skyhanni.utils.shader

import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.LorenzUtils
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.OpenGlHelper
import net.minecraft.client.resources.IReloadableResourceManager
import net.minecraft.client.shader.ShaderLinkHelper
import org.apache.commons.lang3.StringUtils
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.OpenGLException
import java.util.function.Supplier

/**
 * Superclass for shader objects to compile and attach vertex and fragment shaders to the shader program
 *
 * Modified class from SkyblockAddons
 *
 * Credit: [Shader.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/Shader.java)
 */
abstract class Shader(val vertex: String, val fragment: String) {

    var shaderProgram: Int = -1
    private var vertexShaderID: Int = -1
    private var fragmentShaderID: Int = -1

    private val uniforms: MutableList<Uniform<*>> = mutableListOf()

    var created = false

    init {
        recompile()
        (Minecraft.getMinecraft().resourceManager as IReloadableResourceManager).registerReloadListener {
            recompile()
        }
    }

    fun deleteOldShaders() {
        if (vertexShaderID >= 0) {
            OpenGlHelper.glDeleteShader(vertexShaderID)
            vertexShaderID = -1
        }
        if (fragmentShaderID >= 0) {
            OpenGlHelper.glDeleteShader(fragmentShaderID)
            fragmentShaderID = -1
        }
        if (shaderProgram >= 0) {
            OpenGlHelper.glDeleteProgram(shaderProgram)
            shaderProgram = -1
        }
        uniforms.clear()
        created = false
    }

    fun recompile() {
        deleteOldShaders()
        shaderProgram = ShaderLinkHelper.getStaticShaderLinkHelper().createProgram()
        if (shaderProgram < 0) return

        vertexShaderID = ShaderManager.loadShader(ShaderType.VERTEX, vertex)
        if (vertexShaderID < 0) return
        ShaderManager.attachShader(shaderProgram, vertexShaderID)

        fragmentShaderID = ShaderManager.loadShader(ShaderType.FRAGMENT, fragment)
        if (fragmentShaderID < 0) return
        ShaderManager.attachShader(shaderProgram, fragmentShaderID)

        ShaderHelper.glLinkProgram(shaderProgram)

        if (ShaderHelper.glGetProgrami(shaderProgram, ShaderHelper.GL_LINK_STATUS) == GL11.GL_FALSE) {
            val errorMessage = "Failed to link vertex shader $vertex and fragment shader $fragment. Features that " +
                "utilise this shader will not work correctly, if at all"
            val errorLog = StringUtils.trim(ShaderHelper.glGetShaderInfoLog(shaderProgram, 1024))

            if (ShaderManager.inWorld()) {
                ErrorManager.logErrorWithData(
                    OpenGLException("Shader linking error."),
                    errorMessage,
                    "Link Error:\n" to errorLog
                )
            } else {
                LorenzUtils.consoleLog("$errorMessage $errorLog")
            }
            return
        }

        this.registerUniforms()
        created = true
    }

    abstract fun registerUniforms()

    fun updateUniforms() {
        for (uniform in uniforms) {
            uniform.update()
        }
    }

    fun enable() = ShaderHelper.glUseProgram(shaderProgram)

    fun disable() = ShaderHelper.glUseProgram(0)

    fun <T> registerUniform(uniformType: Uniform.UniformType<T>, name: String, uniformValuesSupplier: Supplier<T>) {
        uniforms.add(Uniform(this, uniformType, name, uniformValuesSupplier))
    }
}