From 16ef943b3c2ce8db2331332261143a12bdba61cf Mon Sep 17 00:00:00 2001 From: Vixid <52578495+VixidDev@users.noreply.github.com> Date: Sat, 14 Oct 2023 10:05:48 +0100 Subject: Feature: SBA Chroma but in SH (#487) Porting SBA's chroma into SkyHanni with many more options and chroma everything. #487 --- .../at/hannibal2/skyhanni/utils/shader/Shader.kt | 55 +++++++++ .../skyhanni/utils/shader/ShaderHelper.kt | 124 +++++++++++++++++++++ .../skyhanni/utils/shader/ShaderManager.kt | 87 +++++++++++++++ .../at/hannibal2/skyhanni/utils/shader/Uniform.kt | 45 ++++++++ 4 files changed, 311 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt (limited to 'src/main/java/at/hannibal2/skyhanni/utils/shader') diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt new file mode 100644 index 000000000..f198f7e7a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt @@ -0,0 +1,55 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.shader.ShaderLinkHelper +import org.apache.commons.lang3.StringUtils +import org.lwjgl.opengl.GL11 +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(vertex: String, fragment: String) { + + var shaderProgram: Int = ShaderLinkHelper.getStaticShaderLinkHelper().createProgram() + private val uniforms: MutableList> = mutableListOf() + + init { + val vertexShaderID = ShaderManager.loadShader(ShaderType.VERTEX, vertex) + ShaderManager.attachShader(shaderProgram, vertexShaderID) + val fragmentShaderID = ShaderManager.loadShader(ShaderType.FRAGMENT, fragment) + ShaderManager.attachShader(shaderProgram, fragmentShaderID) + + ShaderHelper.glLinkProgram(shaderProgram) + + val linkStatus = ShaderHelper.glGetProgrami(shaderProgram, ShaderHelper.GL_LINK_STATUS) + if (linkStatus == GL11.GL_FALSE) { + LorenzUtils.consoleLog( + "Error occurred when linking program with Vertex Shader: $vertex and Fragment Shader: $fragment : " + + StringUtils.trim(ShaderHelper.glGetProgramInfoLog(shaderProgram, 1024)) + ) + } + + this.registerUniforms() + } + + abstract fun registerUniforms() + + fun updateUniforms() { + for (uniform in uniforms) { + uniform.update() + } + } + + fun enable() = ShaderHelper.glUseProgram(shaderProgram) + + fun disable() = ShaderHelper.glUseProgram(0) + + fun registerUniform(uniformType: Uniform.UniformType, name: String, uniformValuesSupplier: Supplier) { + uniforms.add(Uniform(this, uniformType, name, uniformValuesSupplier)) + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt new file mode 100644 index 000000000..e554a4098 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt @@ -0,0 +1,124 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.utils.LorenzUtils +import org.lwjgl.opengl.* + +/** + * Class to check shaders support, OpenGL capabilities, and shader helper functions + * + * Modified class from SkyblockAddons + * + * Credit: [ShaderHelper.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/ShaderHelper.java) + */ +class ShaderHelper { + companion object { + private var SHADERS_SUPPORTED: Boolean + + private var USING_ARB_SHADERS: Boolean + + var GL_LINK_STATUS: Int + var GL_COMPILE_STATUS: Int + var GL_VERTEX_SHADER: Int + var GL_FRAGMENT_SHADER: Int + + init { + val capabilities: ContextCapabilities = GLContext.getCapabilities() + + // Check OpenGL 2.0 Capabilities + val openGL20supported = capabilities.OpenGL20 + SHADERS_SUPPORTED = openGL20supported || + capabilities.GL_ARB_vertex_shader && + capabilities.GL_ARB_fragment_shader && + capabilities.GL_ARB_shader_objects + + var log = "Shaders are" + if (!SHADERS_SUPPORTED) log += " not" + log += " available. " + + if (SHADERS_SUPPORTED) { + if (capabilities.OpenGL20) { + log += "OpenGL 2.0 is supported. " + USING_ARB_SHADERS = false + GL_LINK_STATUS = GL20.GL_LINK_STATUS + GL_COMPILE_STATUS = GL20.GL_COMPILE_STATUS + GL_VERTEX_SHADER = GL20.GL_VERTEX_SHADER + GL_FRAGMENT_SHADER = GL20.GL_FRAGMENT_SHADER + } else { + log += "ARB_shader_objects, ARB_vertex_shader, and ARB_fragment_shader are supported. " + USING_ARB_SHADERS = true + GL_LINK_STATUS = ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB + GL_COMPILE_STATUS = ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB + GL_VERTEX_SHADER = ARBVertexShader.GL_VERTEX_SHADER_ARB + GL_FRAGMENT_SHADER = ARBFragmentShader.GL_FRAGMENT_SHADER_ARB + } + } else { + log += "OpenGL 2.0 is not supported and ARB_shader_objects, ARB_vertex_shader, and ARB_fragment_shader are not supported." + USING_ARB_SHADERS = false + GL_LINK_STATUS = GL11.GL_FALSE + GL_COMPILE_STATUS = GL11.GL_FALSE + GL_VERTEX_SHADER = GL11.GL_FALSE + GL_FRAGMENT_SHADER = GL11.GL_FALSE + } + + LorenzUtils.consoleLog(log) + } + + fun glLinkProgram(program: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glLinkProgramARB(program) else GL20.glLinkProgram(program) + } + + fun glGetProgramInfoLog(program: Int, maxLength: Int) : String { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetInfoLogARB(program, maxLength) else GL20.glGetProgramInfoLog(program, maxLength) + } + + fun glGetProgrami(program: Int, pname: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetObjectParameteriARB(program, pname) else GL20.glGetProgrami(program, pname) + } + + fun glUseProgram(program: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUseProgramObjectARB(program) else GL20.glUseProgram(program) + } + + fun glAttachShader(program: Int, shaderIn: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glAttachObjectARB(program, shaderIn) else GL20.glAttachShader(program, shaderIn) + } + + fun glCreateShader(type: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glCreateShaderObjectARB(type) else GL20.glCreateShader(type) + } + + fun glShaderSource(shader: Int, source: CharSequence) { + if (USING_ARB_SHADERS) ARBShaderObjects.glShaderSourceARB(shader, source) else GL20.glShaderSource(shader, source) + } + + fun glCompileShader(shader: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glCompileShaderARB(shader) else GL20.glCompileShader(shader) + } + + fun glGetShaderi(shader: Int, pname: Int) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetObjectParameteriARB(shader, pname) else GL20.glGetShaderi(shader, pname) + } + + fun glGetShaderInfoLog(shader: Int, maxLength: Int) : String { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetInfoLogARB(shader, maxLength) else GL20.glGetShaderInfoLog(shader, maxLength) + } + + fun glDeleteShader(shader: Int) { + if (USING_ARB_SHADERS) ARBShaderObjects.glDeleteObjectARB(shader) else GL20.glDeleteShader(shader) + } + + fun glUniform1f(location: Int, v0: Float) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUniform1fARB(location, v0) else GL20.glUniform1f(location, v0) + } + + fun glUniform3f(location: Int, v0: Float, v1: Float, v2: Float) { + if (USING_ARB_SHADERS) ARBShaderObjects.glUniform3fARB(location, v0, v1, v2) else GL20.glUniform3f(location, v0, v1, v2) + } + + fun glGetUniformLocation(program: Int, name: CharSequence) : Int { + return if (USING_ARB_SHADERS) ARBShaderObjects.glGetUniformLocationARB(program, name) else GL20.glGetUniformLocation(program, name) + } + + fun areShadersSupported() = SHADERS_SUPPORTED + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt new file mode 100644 index 000000000..e7eb48f11 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt @@ -0,0 +1,87 @@ +package at.hannibal2.skyhanni.utils.shader + +import at.hannibal2.skyhanni.features.chroma.ChromaShader +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.Minecraft +import net.minecraft.util.ResourceLocation +import org.apache.commons.lang3.StringUtils +import java.io.BufferedReader +import java.io.InputStreamReader + +/** + * Object to handle shaders for SkyHanni + */ +object ShaderManager { + + /** + * For any future shaders add the object instance in this enum and + * in the when expression + */ + enum class Shaders(val shader: Shader) { + CHROMA(ChromaShader.INSTANCE); + + companion object { + fun getShaderInstance(shaderName: String) : Shader? = when (shaderName) { + "chroma" -> CHROMA.shader + else -> { + null + } + } + } + } + + private val shaders: MutableMap = mutableMapOf() + private var activeShader: Shader? = null + + fun enableShader(shaderName: String) { + var shader = shaders[shaderName] + + if (shader == null) { + shader = Shaders.getShaderInstance(shaderName) + if (shader == null) return + shaders[shaderName] = shader + } + + activeShader = shader + shader.enable() + shader.updateUniforms() + } + + fun attachShader(shaderProgram: Int, shaderID: Int) { + ShaderHelper.glAttachShader(shaderProgram, shaderID) + } + + fun disableShader() { + if (activeShader == null) return + + activeShader?.disable() + activeShader = null + } + + fun loadShader(type: ShaderType, fileName: String) : Int { + val resourceLocation = ResourceLocation("skyhanni:shaders/$fileName${type.extension}") + + val source = StringBuilder() + + val inputStream = Minecraft.getMinecraft().resourceManager.getResource(resourceLocation).inputStream + BufferedReader(InputStreamReader(inputStream)).forEachLine { + source.append(it).append("\n") + } + + val shaderID = ShaderHelper.glCreateShader(type.shaderType) + ShaderHelper.glShaderSource(shaderID, source.toString()) + ShaderHelper.glCompileShader(shaderID) + + if (ShaderHelper.glGetShaderi(shaderID, ShaderHelper.GL_COMPILE_STATUS) == 0) { + LorenzUtils.consoleLog("Error occurred when compiling shader $fileName${type.extension} : " + + StringUtils.trim(ShaderHelper.glGetShaderInfoLog(shaderID, 1024))) + } + + return shaderID + } +} + +enum class ShaderType(val extension: String, val shaderType: Int) { + VERTEX(".vsh", ShaderHelper.GL_VERTEX_SHADER), + FRAGMENT(".fsh", ShaderHelper.GL_FRAGMENT_SHADER) +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt new file mode 100644 index 000000000..e87ea3b22 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/Uniform.kt @@ -0,0 +1,45 @@ +package at.hannibal2.skyhanni.utils.shader + +import java.util.* +import java.util.function.Supplier + +/** + * Class to handle shader uniform types + * + * Modified from SkyblockAddons + * + * Credit: [Uniform.java](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/java/codes/biscuit/skyblockaddons/shader/Uniform.java) + */ +class Uniform( + shader: Shader, + private val uniformType: UniformType, + val name: String, + private val uniformValuesSupplier: Supplier +) { + + class UniformType { + companion object { + val FLOAT: UniformType = UniformType() + val VEC3: UniformType = UniformType() + val BOOL: UniformType = UniformType() + } + } + + private val uniformID: Int = ShaderHelper.glGetUniformLocation(shader.shaderProgram, name) + private var previousUniformValue: T? = null + + fun update() { + val newUniformValue: T = uniformValuesSupplier.get() + if (!Objects.deepEquals(previousUniformValue, newUniformValue)) { + when (uniformType) { + UniformType.FLOAT -> ShaderHelper.glUniform1f(uniformID, (newUniformValue as Float)) + UniformType.VEC3 -> { + val values = newUniformValue as FloatArray + ShaderHelper.glUniform3f(uniformID, values[0], values[1], values[2]) + } + UniformType.BOOL -> ShaderHelper.glUniform1f(uniformID, if (newUniformValue as Boolean) 1f else 0f) + } + previousUniformValue = newUniformValue + } + } +} \ No newline at end of file -- cgit