Skip to content

Commit 97e9e91

Browse files
committed
Lambda shader language
1 parent 64bf167 commit 97e9e91

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+596
-415
lines changed

common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ enum class VertexAttrib(
6969
Color
7070
),
7171

72-
PARTICLE(Vec3,
72+
PARTICLE(
73+
Vec3,
7374
Vec2, // pos
7475
Color
7576
);

common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,10 @@ open class ESPRenderer(tickedMode: Boolean) {
6161

6262
companion object {
6363
private val staticMode = shader(
64-
"renderer/pos_color",
6564
"renderer/box_static"
6665
) to VertexAttrib.Group.STATIC_RENDERER
6766

6867
private val dynamicMode = shader(
69-
"renderer/pos_color",
7068
"renderer/box_dynamic"
7169
) to VertexAttrib.Group.DYNAMIC_RENDERER
7270
}

common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import com.lambda.graphics.buffer.vertex.attributes.VertexMode
2424
import com.lambda.graphics.shader.Shader.Companion.shader
2525
import com.lambda.graphics.texture.Texture
2626
import com.lambda.module.modules.client.GuiSettings
27+
import com.lambda.util.math.MathUtils.toInt
2728
import com.lambda.util.math.Rect
2829
import com.lambda.util.math.Vec2d
30+
import org.lwjgl.glfw.GLFW
2931
import org.lwjgl.glfw.GLFW.glfwGetTime
3032

3133
object TextureRenderer {
@@ -45,10 +47,12 @@ object TextureRenderer {
4547
texture.bind()
4648
coloredShader.use()
4749

48-
coloredShader["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0
49-
coloredShader["u_Color1"] = GuiSettings.shadeColor1
50-
coloredShader["u_Color2"] = GuiSettings.shadeColor2
51-
coloredShader["u_Size"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight)
50+
coloredShader["u_Shade"] = 1.0
51+
coloredShader["u_ShadeTime"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0
52+
coloredShader["u_ShadeColor1"] = GuiSettings.shadeColor1
53+
coloredShader["u_ShadeColor2"] = GuiSettings.shadeColor2
54+
55+
coloredShader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight)
5256

5357
drawInternal(rect)
5458
}

common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import java.awt.Color
3737
* Renders text and emoji glyphs using a shader-based font rendering system.
3838
* This class handles text and emoji rendering, shadow effects, and text scaling.
3939
*/
40-
object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/font")) {
40+
object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("renderer/font")) {
4141
private val chars get() = RenderSettings.textFont
4242
private val emojis get() = RenderSettings.emojiFont
4343

common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import java.awt.image.BufferedImage
3434
* @param image Image data to upload
3535
*/
3636
class DistanceFieldTexture(image: BufferedImage) : Texture(image, levels = 0) {
37-
private val shader = shader("signed_distance_field", "renderer/pos_tex")
37+
private val shader = shader("post/sdf")
3838

3939
private val frame = CachedFrame(width, height).write {
4040
FrameBuffer.pipeline.use {

common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ package com.lambda.graphics.renderer.gui.rect
1919

2020
import com.lambda.graphics.buffer.IRenderContext
2121
import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib
22-
import com.lambda.graphics.pipeline.ScissorAdapter
2322
import com.lambda.graphics.renderer.gui.AbstractGUIRenderer
24-
import com.lambda.graphics.shader.Shader
2523
import com.lambda.graphics.shader.Shader.Companion.shader
2624
import com.lambda.util.math.lerp
2725
import com.lambda.util.math.MathUtils.toInt
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.graphics.shader
19+
20+
import com.lambda.util.stream
21+
import org.apache.commons.io.IOUtils
22+
23+
class ParsedShader(path: String) {
24+
private val text = IOUtils.toString("shaders/$path.glsl".stream, Charsets.UTF_8)
25+
26+
private val blocks = text.getBlocks()
27+
private val dependencies = text.getDependencies()
28+
29+
val methods = text.getMethods()
30+
val definitions = text.getDefinitions()
31+
32+
val attribs = blocks.getBlock("attributes")
33+
val uniforms = blocks.getBlock("uniforms")
34+
val exported = blocks.getBlock("export")
35+
36+
init {
37+
dependencies.forEach { dependencyName ->
38+
val dependency = ParsedShader("shared/$dependencyName")
39+
40+
//attribs.addAll(shader.blocks.getBlock("attributes"))
41+
dependency.attribs.forEach { attrib ->
42+
check(attribs.any { it.name == attrib.name && it.type == attrib.type }) {
43+
"[${path}]: dependency \"$dependencyName\" requires \"${attrib.type} ${attrib.name}\" attribute to be present"
44+
}
45+
}
46+
47+
uniforms.addAll(dependency.uniforms)
48+
exported.addAll(dependency.exported)
49+
50+
definitions.addAll(dependency.definitions)
51+
methods.addAll(dependency.methods.filter {
52+
it.name != "fragment" && it.name != "vertex"
53+
})
54+
}
55+
}
56+
57+
data class Field(val type: String, val name: String, val flag: String?)
58+
data class Method(val name: String, val returnType: String, val parameters: String, var code: String) {
59+
fun construct(constructedName: String = name): String {
60+
return "$returnType $constructedName($parameters) {\n $code\n}\n"
61+
}
62+
}
63+
64+
companion object {
65+
private val blockRegex = Regex("""\s*(\w+)\s*\{(.*?)}\s*""", RegexOption.DOT_MATCHES_ALL)
66+
private val methodRegex = Regex("""^\s*(\w+)\s+(\w+)\s*\((.*?)\)\s*\{(.*?)}#""", setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.MULTILINE))
67+
68+
private val fieldRegex = Regex("""\s*(\w+(?:\s+\w+)*)\s+(\w+)\s*;\s*(#.*)?""")
69+
private val includeRegex = Regex("""^#include\s+"([\w-]+)"""", RegexOption.MULTILINE)
70+
private val defineRegex = Regex("""^#define\s+(\w+)\s+(.+)""", RegexOption.MULTILINE)
71+
72+
private fun String.getBlocks() = mutableMapOf<String, MutableSet<Field>>().apply {
73+
blockRegex.findAll(this@getBlocks).forEach { match ->
74+
val blockName = match.groupValues[1]
75+
val blockFieldsStr = match.groupValues[2]
76+
77+
val fields = fieldRegex.findAll(blockFieldsStr).map { fieldMatch ->
78+
val type = fieldMatch.groupValues[1]
79+
val name = fieldMatch.groupValues[2]
80+
81+
val flag = fieldMatch.groupValues[3]
82+
.takeIf { it.isNotEmpty() }
83+
?.removePrefix("#")
84+
85+
Field(type, name, flag)
86+
}.toMutableSet()
87+
88+
getOrPut(blockName, ::mutableSetOf).addAll(fields)
89+
}
90+
}
91+
92+
private fun String.getMethods() = mutableSetOf<Method>().apply {
93+
methodRegex.findAll(this@getMethods).forEach { match ->
94+
val returnType = match.groupValues[1]
95+
val methodName = match.groupValues[2]
96+
val parameters = match.groupValues[3]
97+
val code = match.groupValues[4].trim()
98+
99+
add(Method(methodName, returnType, parameters, code))
100+
}
101+
}
102+
103+
private fun String.getDependencies() = includeRegex.findAll(this)
104+
.map { it.groupValues[1] }
105+
.toSet()
106+
107+
private fun String.getDefinitions() = mutableSetOf<Field>().apply {
108+
defineRegex.findAll(this@getDefinitions).forEach { match ->
109+
add(Field(match.groupValues[2].trim(), match.groupValues[1], null))
110+
}
111+
}
112+
113+
private fun MutableMap<String, MutableSet<Field>>.getBlock(name: String) =
114+
getOrDefault(name, mutableSetOf())
115+
}
116+
}

common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,19 @@ import org.joml.Matrix4f
2929
import org.lwjgl.opengl.GL20C.*
3030
import java.awt.Color
3131

32-
class Shader private constructor(fragmentPath: String, vertexPath: String) {
32+
class Shader private constructor(name: String) {
3333
private val uniformCache: Object2IntMap<String> = Object2IntOpenHashMap()
3434

35-
private val id = createShaderProgram(
36-
loadShader(ShaderType.VERTEX_SHADER, "shaders/vertex/$vertexPath.vert"),
37-
loadShader(ShaderType.FRAGMENT_SHADER, "shaders/fragment/$fragmentPath.frag")
38-
)
35+
private val id: Int
36+
37+
init {
38+
val texts = buildShaderSource(name)
39+
40+
id = createShaderProgram(
41+
loadShader(ShaderType.VERTEX_SHADER, texts.first),
42+
loadShader(ShaderType.FRAGMENT_SHADER, texts.second)
43+
)
44+
}
3945

4046
fun use() {
4147
glUseProgram(id)
@@ -79,14 +85,11 @@ class Shader private constructor(fragmentPath: String, vertexPath: String) {
7985
uniformMatrix(loc(name), mat)
8086

8187
companion object {
82-
private val shaderCache = hashMapOf<Pair<String, String>, Shader>()
88+
private val shaderCache = hashMapOf<String, Shader>()
8389

8490
fun shader(path: String) =
85-
shader(path, path)
86-
87-
fun shader(fragmentPath: String, vertexPath: String) =
88-
shaderCache.getOrPut(fragmentPath to vertexPath) {
89-
Shader(fragmentPath, vertexPath)
91+
shaderCache.getOrPut(path) {
92+
Shader(path)
9093
}
9194
}
9295
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.graphics.shader
19+
20+
21+
fun buildShaderSource(path: String): Pair<String, String> {
22+
val shader = ParsedShader(path)
23+
24+
val vertexShader = StringBuilder().appendLine(HEADER)
25+
val fragmentShader = StringBuilder().appendLine(HEADER)
26+
val shaders = setOf(vertexShader, fragmentShader)
27+
28+
/* Build attribs for vertex shader */
29+
shader.attribs.apply {
30+
if (isNotEmpty()) vertexShader.appendLine()
31+
32+
forEachIndexed { i, attribute ->
33+
vertexShader.appendLine(
34+
"layout (location = $i) in ${attribute.type} ${attribute.name};"
35+
)
36+
}
37+
}
38+
39+
/* Build uniforms */
40+
shader.uniforms.apply {
41+
val vertex = mutableSetOf<String>()
42+
val fragment = mutableSetOf<String>()
43+
44+
vertex.add("uniform mat4 u_ProjModel;")
45+
forEach { uniform ->
46+
val line = "uniform ${uniform.type} ${uniform.name};"
47+
48+
when (uniform.flag?.trim()) {
49+
"vertex" -> vertex += line
50+
"fragment" -> fragment += line
51+
else -> {
52+
vertex += line; fragment += line
53+
}
54+
}
55+
}
56+
57+
vertex.apply {
58+
vertexShader.appendLine()
59+
forEach { vertexShader.appendLine(it) }
60+
}
61+
62+
fragment.apply {
63+
if (isNotEmpty()) fragmentShader.appendLine()
64+
forEach { fragmentShader.appendLine(it) }
65+
}
66+
}
67+
68+
/* Does "v_TexCoord = uv" in the vertex shader for you */
69+
val autoAssignment = StringBuilder()
70+
71+
/* Build in/out vars */
72+
shader.exported.apply {
73+
if (isNotEmpty()) shaders.forEach(StringBuilder::appendLine)
74+
75+
// Add gl_Position = u_ProjModel * pos; by default
76+
val hasPosAttribute = shader.attribs.any { it.name == "pos" && it.type == "vec4" }
77+
val glPosImplemented = any { it.name == "gl_Position" }
78+
if (hasPosAttribute && !glPosImplemented) {
79+
autoAssignment.appendLine(" gl_Position = u_ProjModel * pos;")
80+
}
81+
82+
forEach { export ->
83+
if (export.type != "core") {
84+
vertexShader.appendLine("out ${export.type} ${export.name};")
85+
fragmentShader.appendLine("in ${export.type} ${export.name};")
86+
}
87+
88+
export.flag?.let { expr -> /* Add assignment if present */
89+
autoAssignment.appendLine(" ${export.name} = ${expr.trim()};")
90+
}
91+
}
92+
93+
fragmentShader.appendLine()
94+
fragmentShader.appendLine("out vec4 color;")
95+
}
96+
97+
/* Build definitions */
98+
shader.definitions.apply {
99+
if (isNotEmpty()) fragmentShader.appendLine()
100+
101+
forEach { definition ->
102+
fragmentShader.appendLine("#define ${definition.name} ${definition.type}")
103+
}
104+
}
105+
106+
/* Build methods */
107+
shaders.forEach(StringBuilder::appendLine)
108+
val methods = shader.methods
109+
110+
val vertexMain = methods.takeMain("vertex").apply {
111+
code += autoAssignment.trim()
112+
}.construct("main")
113+
vertexShader.appendLine(vertexMain)
114+
115+
val fragmentMain = methods.takeMain("fragment").construct("main")
116+
methods.forEach { fragmentShader.appendLine(it.construct()) }
117+
fragmentShader.appendLine(fragmentMain)
118+
119+
return vertexShader.toString().trimEnd() to fragmentShader.toString().trimEnd()
120+
}
121+
122+
private fun MutableSet<ParsedShader.Method>.takeMain(name: String) = firstOrNull {
123+
it.name == name && it.returnType == "void" && it.parameters.isEmpty()
124+
}.apply(this::remove) ?: ParsedShader.Method(name, "void", "", "")
125+
126+
127+
private const val HEADER = "#version 330 core"

common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,18 @@ object ShaderUtils {
3030
private val matrixBuffer = BufferUtils.createFloatBuffer(4 * 4)
3131
private const val shaderInfoLogLength = 512
3232

33-
fun loadShader(type: ShaderType, resource: LambdaResource): Int {
33+
fun loadShader(type: ShaderType, text: String): Int {
3434
// Create new shader object
3535
val shader = glCreateShader(type.gl)
3636

3737
// Attach source code and compile it
38-
val text = IOUtils.toString(resource.stream, Charsets.UTF_8)
3938
GlStateManager.glShaderSource(shader, ImmutableList.of(text))
4039
val error = compileShader(shader)
4140

4241
// Handle error
4342
error?.let { err ->
4443
val builder = StringBuilder()
4544
.append("Failed to compile ${type.name} shader").appendLine()
46-
.append("Path: $resource").appendLine()
4745
.append("Compiler output:").appendLine()
4846
.append(err)
4947

0 commit comments

Comments
 (0)