From 4dcb5e562bd3492717a0113373464df9db066b01 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 31 May 2024 19:18:23 -0400 Subject: [PATCH 01/42] do not use mojang gl --- common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index e843d58f9..7954aff16 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -19,9 +19,7 @@ import com.lambda.graphics.gl.VaoUtils.bufferData import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer -import com.lambda.threading.mainThread import com.lambda.threading.runOnGameThread -import com.mojang.blaze3d.systems.RenderSystem.drawElements import org.lwjgl.opengl.GL30C.* import java.awt.Color import java.nio.ByteBuffer @@ -172,7 +170,7 @@ class VAO( if (indicesCount <= 0) return bindVertexArray(vao) - drawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT) + glDrawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT, 0) unbindVertexArray() } From 4aa06d78276660d4e02c4c56513140999393dabb Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:27:07 -0400 Subject: [PATCH 02/42] Performance enhancement on texture utils --- .../lambda/graphics/texture/TextureUtils.kt | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt index 1142dec70..5bb814180 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt @@ -4,10 +4,7 @@ import com.mojang.blaze3d.systems.RenderSystem import net.minecraft.client.texture.NativeImage import org.lwjgl.BufferUtils import org.lwjgl.opengl.GL13C.* -import java.awt.Color -import java.awt.Font -import java.awt.RenderingHints -import java.awt.Transparency +import java.awt.* import java.awt.image.BufferedImage import java.io.ByteArrayOutputStream import javax.imageio.ImageIO @@ -15,6 +12,9 @@ import kotlin.math.roundToInt import kotlin.math.sqrt object TextureUtils { + private val metricCache = mutableMapOf() + + fun bindTexture(id: Int, slot: Int = 0) { RenderSystem.activeTexture(GL_TEXTURE0 + slot) RenderSystem.bindTexture(id) @@ -85,24 +85,31 @@ object TextureUtils { return NativeImage.read(buffer).pointer } - fun getCharImage(font: Font, char: Char): BufferedImage? { - if (!font.canDisplay(char)) return null + fun getCharImage(font: Font, codePoint: Char): BufferedImage? { + if (!font.canDisplay(codePoint)) return null + + val fontMetrics = metricCache.getOrPut(font) { + val image = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) + val graphics2D = image.createGraphics() - val tempGraphics2D = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).createGraphics() - tempGraphics2D.font = font - val fontMetrics = tempGraphics2D.fontMetrics - tempGraphics2D.dispose() + graphics2D.font = font + graphics2D.dispose() + + image.graphics.getFontMetrics(font) + } - val charWidth = if (fontMetrics.charWidth(char) > 0) fontMetrics.charWidth(char) else 8 + val charWidth = if (fontMetrics.charWidth(codePoint) > 0) fontMetrics.charWidth(codePoint) else 8 val charHeight = if (fontMetrics.height > 0) fontMetrics.height else font.size val charImage = BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB) val graphics2D = charImage.createGraphics() graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT) + graphics2D.font = font graphics2D.color = Color.WHITE - graphics2D.drawString(char.toString(), 0, fontMetrics.ascent) + graphics2D.drawString(codePoint.toString(), 0, fontMetrics.ascent) graphics2D.dispose() return charImage From d4210162f4cc6d700dc3c230f4e613a3d53bc349 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:29:36 -0400 Subject: [PATCH 03/42] More comments --- .../graphics/renderer/gui/font/glyph/FontGlyphs.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index b6abde4ec..7cf42a628 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -72,12 +72,17 @@ class FontGlyphs(font: Font) { charMap[char.code] companion object { + // The space between glyphs is necessary to prevent artifacts from appearing when the font texture is blurred + private const val STEP = 2 + // Since most Lambda users probably have bad pc, the default size is 2048, which includes latin, cyrillic, greek and arabic // and in the future we could grow the textures when needed private const val CHAR_AMOUNT = 2048 - private const val TEXTURE_SIZE = 4096 + + // The size of the texture in pixels + private const val TEXTURE_SIZE = CHAR_AMOUNT * 2 + + // The size of one texel in UV coordinates private const val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE - // The space between glyphs is necessary to prevent artifacts from appearing when the font texture is blurred - private const val STEP = 2 } } From b38439d12bac489a1f14d243998ee325884a710b Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sat, 1 Jun 2024 16:47:18 -0400 Subject: [PATCH 04/42] Test: Emoji glyphs --- .../src/main/kotlin/com/lambda/core/Loader.kt | 2 + .../graphics/renderer/gui/font/LambdaMoji.kt | 23 +++++++ .../renderer/gui/font/glyph/EmojiGlyphs.kt | 69 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 54a73eba7..5ce9fbade 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -5,6 +5,7 @@ import com.lambda.Lambda.LOG import com.lambda.command.CommandRegistry import com.lambda.friend.FriendRegistry import com.lambda.graphics.renderer.gui.font.LambdaFont +import com.lambda.graphics.renderer.gui.font.LambdaMoji import com.lambda.gui.GuiConfigurable import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager @@ -20,6 +21,7 @@ object Loader { RotationManager, PlayerPacketManager, LambdaFont.Loader, + LambdaMoji.Loader, GuiConfigurable, FriendRegistry, SoundRegistry, diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt new file mode 100644 index 000000000..3c525c5ed --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt @@ -0,0 +1,23 @@ +package com.lambda.graphics.renderer.gui.font + +import com.lambda.core.Loadable +import com.lambda.graphics.renderer.gui.font.glyph.EmojiGlyphs + +enum class LambdaMoji(private val zipUrl: String) { + Twemoji("https://github.com/Edouard127/emoji-generator/releases/latest/download/emojis.zip"); + + lateinit var glyphs: EmojiGlyphs + + operator fun get(emoji: String) = glyphs.getEmoji(emoji) + + fun loadGlyphs() { + glyphs = EmojiGlyphs(zipUrl) + } + + object Loader : Loadable { + override fun load(): String { + LambdaMoji.entries.forEach(LambdaMoji::loadGlyphs) + return "Loaded ${LambdaMoji.entries.size} emoji sets" + } + } +} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt new file mode 100644 index 000000000..5aafa83b4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -0,0 +1,69 @@ +package com.lambda.graphics.renderer.gui.font.glyph + +import com.lambda.graphics.texture.MipmapTexture +import com.lambda.module.modules.client.FontSettings +import com.lambda.util.math.Vec2d +import java.awt.Color +import java.awt.Graphics2D +import java.awt.image.BufferedImage +import java.net.URL +import java.nio.file.Files +import java.util.zip.ZipFile +import javax.imageio.ImageIO +import kotlin.math.ceil +import kotlin.math.sqrt + +class EmojiGlyphs(zipUrl: String) { + private val emojiMap = mutableMapOf() + private val fontTexture: MipmapTexture + + init { + val file = Files.createTempFile("emoji", ".zip").toFile() + val url = URL(zipUrl) + file.writeBytes(url.readBytes()) + + ZipFile(file).use { zip -> + val size = zip.entries().asSequence().count() + val dimensions = Vec2d(72.0, 72.0) + val texelSize = 1.0 / size + val width = 72 * ceil(sqrt(size.toDouble())).toInt() + + val image = BufferedImage(width, width, BufferedImage.TYPE_INT_ARGB) + val graphics = image.graphics as Graphics2D + graphics.background = Color(0, 0, 0, 0) + + var x = 0 + var y = 0 + + zip.entries().asSequence().forEach { entry -> + val name = entry.name.substringAfterLast("/").substringBeforeLast(".") + val charImage = ImageIO.read(zip.getInputStream(entry)) + + if (x + 72 >= width) { + y += 72 + x = 0 + } + + graphics.drawImage(charImage, x, y, null) + + val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize + val uv2 = Vec2d(x, y).plus(dimensions) * texelSize + emojiMap[name] = CharInfo(dimensions, uv1, uv2) + + x += 72 + } + + fontTexture = MipmapTexture(image) + } + } + + fun bind() { + with(fontTexture) { + bind() + setLOD(FontSettings.lodBias.toFloat()) + } + } + + fun getEmoji(emoji: String): CharInfo? = + emojiMap[emoji] +} From cbe6de2b0e31238df8a5ad40f7f837e6be0752a8 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sat, 1 Jun 2024 16:53:09 -0400 Subject: [PATCH 05/42] Take the dimension of the first emoji --- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index 5aafa83b4..2e29eeb1c 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -6,6 +6,7 @@ import com.lambda.util.math.Vec2d import java.awt.Color import java.awt.Graphics2D import java.awt.image.BufferedImage +import java.io.File import java.net.URL import java.nio.file.Files import java.util.zip.ZipFile @@ -23,12 +24,16 @@ class EmojiGlyphs(zipUrl: String) { file.writeBytes(url.readBytes()) ZipFile(file).use { zip -> + // someone please refactor this + val first = ImageIO.read(zip.getInputStream(zip.entries().nextElement())) + val size = zip.entries().asSequence().count() - val dimensions = Vec2d(72.0, 72.0) + val dimensions = Vec2d(first.width.toDouble(), first.height.toDouble()) val texelSize = 1.0 / size - val width = 72 * ceil(sqrt(size.toDouble())).toInt() + val width = first.width * ceil(sqrt(size.toDouble())).toInt() + val height = first.height * ceil(sqrt(size.toDouble())).toInt() - val image = BufferedImage(width, width, BufferedImage.TYPE_INT_ARGB) + val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) val graphics = image.graphics as Graphics2D graphics.background = Color(0, 0, 0, 0) @@ -37,20 +42,20 @@ class EmojiGlyphs(zipUrl: String) { zip.entries().asSequence().forEach { entry -> val name = entry.name.substringAfterLast("/").substringBeforeLast(".") - val charImage = ImageIO.read(zip.getInputStream(entry)) + val emoji = ImageIO.read(zip.getInputStream(entry)) - if (x + 72 >= width) { - y += 72 + if (x + emoji.width >= width) { + y += emoji.height x = 0 } - graphics.drawImage(charImage, x, y, null) + graphics.drawImage(emoji, x, y, null) val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize val uv2 = Vec2d(x, y).plus(dimensions) * texelSize emojiMap[name] = CharInfo(dimensions, uv1, uv2) - x += 72 + x += emoji.width } fontTexture = MipmapTexture(image) From cb610b56839c1100a35f61608f7801efb4c0b7ed Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sat, 1 Jun 2024 19:55:34 -0400 Subject: [PATCH 06/42] Parse emojis from strings --- .../renderer/gui/font/FontRenderer.kt | 64 +++++++++++++++++-- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 31 +++++---- .../kotlin/com/lambda/gui/api/RenderLayer.kt | 8 ++- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 03fb15ac6..f9a85da7e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -10,8 +10,31 @@ import com.lambda.module.modules.client.FontSettings import com.lambda.util.math.Vec2d import java.awt.Color -class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) { +class FontRenderer( + private val font: LambdaFont, + private val emojis: LambdaMoji, +) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) { var scaleMultiplier = 1.0 + private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") + + /** + * Parses the emojis in the given text. + * + * @param text The text to parse. + * @return A list of triples containing the emoji text, start index, and end index. + */ + private fun parseEmojis(text: String): List< + Triple> { + val result = mutableListOf>() + val matches = emojiRegex.findAll(text) + + matches.forEach { + val index = it.value.substring(1, it.value.length - 1) + result.add(Triple(emojis[index] ?: return@forEach, it.range.first, it.range.last)) + } + + return result + } fun build( text: String, @@ -28,7 +51,25 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES var posX = 0.0 val posY = getHeight(scale) * -0.5 + baselineOffset * actualScale - text.toCharArray().forEach { char -> + val emojis = parseEmojis(text) + + val subText = emojis.asReversed().fold(text) { acc, ( + charInfo, start, end + ) -> + val emojiWidth = charInfo.size.x * actualScale + val emojiHeight = charInfo.size.y * actualScale + + val startPos = Vec2d(posX, posY) + val endPos = startPos + Vec2d(emojiWidth, emojiHeight) + + putChar(position, startPos, endPos, color, charInfo) + + posX += emojiWidth + scaledGap + + acc.replaceRange(start, end, " ") + } + + subText.toCharArray().forEach { char -> val charInfo = font[char] ?: return@forEach val scaledSize = charInfo.size * actualScale @@ -50,8 +91,20 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES fun getWidth(text: String, scale: Double = 1.0): Double { var width = 0.0 - text.forEach { char -> - val glyph = font[char] ?: return@forEach + val emojis = parseEmojis(text) + + val subText = emojis.asReversed().fold(text) { acc, ( + charInfo, start, end + ) -> + val emojiWidth = charInfo.size.x + + width += emojiWidth + gap + + acc.replaceRange(start, end, " ") + } + + subText.forEach { + val glyph = font[it] ?: return@forEach width += glyph.size.x + gap } @@ -88,6 +141,7 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES override fun render() { shader.use() font.glyphs.bind() + //emojis.glyphs.bind() // You have to modify the uniform in the shader to use the correct texture super.render() } @@ -98,4 +152,4 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index 2e29eeb1c..ba0c8f684 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -1,18 +1,19 @@ package com.lambda.graphics.renderer.gui.font.glyph +import com.lambda.Lambda.LOG import com.lambda.graphics.texture.MipmapTexture import com.lambda.module.modules.client.FontSettings import com.lambda.util.math.Vec2d import java.awt.Color import java.awt.Graphics2D import java.awt.image.BufferedImage -import java.io.File import java.net.URL import java.nio.file.Files import java.util.zip.ZipFile import javax.imageio.ImageIO import kotlin.math.ceil import kotlin.math.sqrt +import kotlin.system.measureTimeMillis class EmojiGlyphs(zipUrl: String) { private val emojiMap = mutableMapOf() @@ -40,25 +41,29 @@ class EmojiGlyphs(zipUrl: String) { var x = 0 var y = 0 - zip.entries().asSequence().forEach { entry -> - val name = entry.name.substringAfterLast("/").substringBeforeLast(".") - val emoji = ImageIO.read(zip.getInputStream(entry)) + val time = measureTimeMillis { + zip.entries().asSequence().forEach { entry -> + val name = entry.name.substringAfterLast("/").substringBeforeLast(".") + val emoji = ImageIO.read(zip.getInputStream(entry)) - if (x + emoji.width >= width) { - y += emoji.height - x = 0 - } + if (x + emoji.width >= width) { + y += emoji.height + x = 0 + } - graphics.drawImage(emoji, x, y, null) + graphics.drawImage(emoji, x, y, null) - val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize - val uv2 = Vec2d(x, y).plus(dimensions) * texelSize - emojiMap[name] = CharInfo(dimensions, uv1, uv2) + val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize + val uv2 = Vec2d(x, y).plus(dimensions) * texelSize + emojiMap[name] = CharInfo(dimensions, uv1, uv2) - x += emoji.width + x += emoji.width + } } fontTexture = MipmapTexture(image) + + LOG.info("Emoji loaded with ${emojiMap.size} characters (${time}ms)") } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt index 7ca93d3c8..0d8e2b2cf 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt @@ -2,17 +2,21 @@ package com.lambda.gui.api import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaFont +import com.lambda.graphics.renderer.gui.font.LambdaMoji import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer class RenderLayer { val filled = FilledRectRenderer() val outline = OutlineRectRenderer() - val font = FontRenderer(LambdaFont.FiraSansRegular) + val font = FontRenderer( + LambdaFont.FiraSansRegular, + LambdaMoji.Twemoji, + ) fun render() { filled.render() outline.render() font.render() } -} \ No newline at end of file +} From 1dab763f13cfcc3d6d4ff61b5d0184feb786ccfe Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:46:07 -0400 Subject: [PATCH 07/42] Test: Font renderer --- .../graphics/renderer/gui/font/FontRenderer.kt | 7 ++++++- .../graphics/renderer/gui/font/glyph/CharInfo.kt | 2 +- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 8 ++++++-- .../renderer/gui/font/glyph/FontGlyphs.kt | 5 ++++- .../com/lambda/graphics/texture/Texture.kt | 4 ++-- .../lambda/shaders/fragment/renderer/font.frag | 16 ++++++++++++---- 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index f9a85da7e..1bfaa116f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -140,8 +140,13 @@ class FontRenderer( override fun render() { shader.use() + font.glyphs.bind() - //emojis.glyphs.bind() // You have to modify the uniform in the shader to use the correct texture + shader["u_FontTexture"] = 0 + + emojis.glyphs.bind() + shader["u_EmojiTexture"] = 1 + super.render() } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt index ab5791219..5ab3adee4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt @@ -6,4 +6,4 @@ class CharInfo( val size: Vec2d, val uv1: Vec2d, val uv2: Vec2d -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index ba0c8f684..482423c67 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -55,7 +55,7 @@ class EmojiGlyphs(zipUrl: String) { val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize val uv2 = Vec2d(x, y).plus(dimensions) * texelSize - emojiMap[name] = CharInfo(dimensions, uv1, uv2) + emojiMap[name] = CharInfo(dimensions, uv1 * -1.0, uv2 * -1.0) x += emoji.width } @@ -69,11 +69,15 @@ class EmojiGlyphs(zipUrl: String) { fun bind() { with(fontTexture) { - bind() + bind(GL_TEXTURE_SLOT) setLOD(FontSettings.lodBias.toFloat()) } } fun getEmoji(emoji: String): CharInfo? = emojiMap[emoji] + + companion object { + private const val GL_TEXTURE_SLOT = 1 + } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 7cf42a628..439d8501a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -63,7 +63,7 @@ class FontGlyphs(font: Font) { fun bind() { with(fontTexture) { - bind() + bind(GL_TEXTURE_SLOT) setLOD(FontSettings.lodBias.toFloat()) } } @@ -72,6 +72,9 @@ class FontGlyphs(font: Font) { charMap[char.code] companion object { + // The allocated texture slot + private const val GL_TEXTURE_SLOT = 0 + // The space between glyphs is necessary to prevent artifacts from appearing when the font texture is blurred private const val STEP = 2 diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt index 0ff1b10f2..b23dcf23a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -14,7 +14,7 @@ abstract class Texture { protected abstract fun init() - open fun bind() = bindTexture(id) + open fun bind(slot: Int = 0) = bindTexture(id) override fun equals(other: Any?): Boolean { if (this === other) return true @@ -26,4 +26,4 @@ abstract class Texture { } override fun hashCode() = id -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag index 3522a98c5..5772167d6 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag @@ -1,6 +1,7 @@ #version 330 core -uniform sampler2D u_Texture; +uniform sampler2D u_FontTexture; +uniform sampler2D u_EmojiTexture; in vec2 v_TexCoord; in vec4 v_Color; @@ -8,6 +9,13 @@ in vec4 v_Color; out vec4 color; void main() { - float alpha = texture(u_Texture, v_TexCoord).a; - color = vec4(v_Color.rgb, v_Color.a * alpha); -} \ No newline at end of file + vec4 tex; + + if (v_TexCoord.x > 0.0) { + tex = texture(u_FontTexture, v_TexCoord); + } else { + tex = texture(u_EmojiTexture, v_TexCoord); + } + + color = tex * v_Color; +} From 951fde8a2e064572bf63d9a0129d07ed2b52a8d2 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 2 Jun 2024 19:16:44 +0300 Subject: [PATCH 08/42] Font texture fix --- .../lambda/graphics/renderer/gui/font/FontRenderer.kt | 9 +++------ .../main/kotlin/com/lambda/graphics/texture/Texture.kt | 2 +- .../assets/lambda/shaders/fragment/renderer/font.frag | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 1bfaa116f..d7f699f07 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -56,15 +56,14 @@ class FontRenderer( val subText = emojis.asReversed().fold(text) { acc, ( charInfo, start, end ) -> - val emojiWidth = charInfo.size.x * actualScale - val emojiHeight = charInfo.size.y * actualScale + val emojiSize = charInfo.size * actualScale val startPos = Vec2d(posX, posY) - val endPos = startPos + Vec2d(emojiWidth, emojiHeight) + val endPos = startPos + emojiSize putChar(position, startPos, endPos, color, charInfo) - posX += emojiWidth + scaledGap + posX += emojiSize.x + scaledGap acc.replaceRange(start, end, " ") } @@ -142,8 +141,6 @@ class FontRenderer( shader.use() font.glyphs.bind() - shader["u_FontTexture"] = 0 - emojis.glyphs.bind() shader["u_EmojiTexture"] = 1 diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt index b23dcf23a..8c48ed6c4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -14,7 +14,7 @@ abstract class Texture { protected abstract fun init() - open fun bind(slot: Int = 0) = bindTexture(id) + open fun bind(slot: Int = 0) = bindTexture(id, slot) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag index 5772167d6..25cd3d0f7 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag @@ -14,7 +14,7 @@ void main() { if (v_TexCoord.x > 0.0) { tex = texture(u_FontTexture, v_TexCoord); } else { - tex = texture(u_EmojiTexture, v_TexCoord); + tex = texture(u_EmojiTexture, -v_TexCoord); } color = tex * v_Color; From 48b27e8c1f71717938de646fd338a15326b4ffd8 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 14:26:18 -0400 Subject: [PATCH 09/42] Fix: Emoji parsing --- .../renderer/gui/font/FontRenderer.kt | 83 ++++++++++++------- .../renderer/gui/font/glyph/CharInfo.kt | 12 ++- 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index d7f699f07..39b1d26ea 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -53,24 +53,33 @@ class FontRenderer( val emojis = parseEmojis(text) - val subText = emojis.asReversed().fold(text) { acc, ( - charInfo, start, end - ) -> - val emojiSize = charInfo.size * actualScale - - val startPos = Vec2d(posX, posY) - val endPos = startPos + emojiSize - - putChar(position, startPos, endPos, color, charInfo) - - posX += emojiSize.x + scaledGap - - acc.replaceRange(start, end, " ") - } + var index = 0 + while (index < text.length) { + emojis + .firstOrNull { index in it.second..it.third } + ?.let { emoji -> + val scaledSize = emoji.first.size * actualScale + val pos1 = Vec2d(posX, posY) + val pos2 = pos1 + scaledSize + + putChar(position, pos1, pos2, color, emoji.first) + + posX += scaledSize.x + scaledGap + index += emoji.third - emoji.second + 1 + + if (index >= text.length) { + // This means we've reached the end of the text + return@use + } + } + + val char = text[index] + val glyph = font[char] ?: run { + index++ + return@use + } - subText.toCharArray().forEach { char -> - val charInfo = font[char] ?: return@forEach - val scaledSize = charInfo.size * actualScale + val scaledSize = glyph.size * actualScale val pos1 = Vec2d(posX, posY) val pos2 = pos1 + scaledSize @@ -78,12 +87,13 @@ class FontRenderer( if (shadow && FontSettings.shadow) { val shadowPos1 = pos1 + scaledShadowShift val shadowPos2 = shadowPos1 + scaledSize - putChar(position, shadowPos1, shadowPos2, shadowColor, charInfo) + putChar(position, shadowPos1, shadowPos2, shadowColor, glyph) } - putChar(position, pos1, pos2, color, charInfo) + putChar(position, pos1, pos2, color, glyph) posX += scaledSize.x + scaledGap + index++ } } @@ -92,19 +102,30 @@ class FontRenderer( val emojis = parseEmojis(text) - val subText = emojis.asReversed().fold(text) { acc, ( - charInfo, start, end - ) -> - val emojiWidth = charInfo.size.x - - width += emojiWidth + gap - - acc.replaceRange(start, end, " ") - } + var index = 0 + while (index < text.length) { + emojis + .firstOrNull { index in it.second..it.third } + ?.let { emoji -> + val scaledSize = emoji.first.size * getScaleFactor(scale) + width += scaledSize.x + gap + + index += emoji.third - emoji.second + 1 + + if (index >= text.length) { + // This means we've reached the end of the text + return width * getScaleFactor(scale) + } + } + + val char = text[index] + val glyph = font[char] ?: run { + index++ + return width * getScaleFactor(scale) + } - subText.forEach { - val glyph = font[it] ?: return@forEach - width += glyph.size.x + gap + width += glyph.width + gap + index++ } return width * getScaleFactor(scale) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt index 5ab3adee4..eddd13a76 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt @@ -2,8 +2,16 @@ package com.lambda.graphics.renderer.gui.font.glyph import com.lambda.util.math.Vec2d -class CharInfo( +data class CharInfo( val size: Vec2d, val uv1: Vec2d, val uv2: Vec2d -) +) { + val width get() = size.x + val height get() = size.y + + val u1 get() = uv1.x + val v1 get() = uv1.y + val u2 get() = uv2.x + val v2 get() = uv2.y +} From a5a99e7499de5486f251f29b79c21233fa35a0e5 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 14:28:39 -0400 Subject: [PATCH 10/42] Loader message --- .../kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt | 2 +- .../com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt | 2 +- .../com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt index 3c525c5ed..12c392457 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt @@ -17,7 +17,7 @@ enum class LambdaMoji(private val zipUrl: String) { object Loader : Loadable { override fun load(): String { LambdaMoji.entries.forEach(LambdaMoji::loadGlyphs) - return "Loaded ${LambdaMoji.entries.size} emoji sets" + return "Loaded ${LambdaMoji.entries.size} emoji pools" } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index 482423c67..5e95af895 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -63,7 +63,7 @@ class EmojiGlyphs(zipUrl: String) { fontTexture = MipmapTexture(image) - LOG.info("Emoji loaded with ${emojiMap.size} characters (${time}ms)") + LOG.info("Loaded $size emojis in $time ms") } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 439d8501a..eb6aa1f99 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -58,7 +58,7 @@ class FontGlyphs(font: Font) { fontTexture = MipmapTexture(image) } - Lambda.LOG.info("Font ${font.fontName} loaded with ${charMap.size} characters (${time}ms)") + Lambda.LOG.info("Font ${font.fontName} loaded with ${charMap.size} characters in $time ms") } fun bind() { From 1908d4ba232e6a24e16673ce3a41158031e54d53 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 15:46:08 -0400 Subject: [PATCH 11/42] Better emoji loading --- .../graphics/renderer/gui/font/LambdaMoji.kt | 2 +- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 91 +++++++++++-------- .../main/kotlin/com/lambda/util/math/Vec2d.kt | 4 +- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt index 12c392457..16fd6b098 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt @@ -6,7 +6,7 @@ import com.lambda.graphics.renderer.gui.font.glyph.EmojiGlyphs enum class LambdaMoji(private val zipUrl: String) { Twemoji("https://github.com/Edouard127/emoji-generator/releases/latest/download/emojis.zip"); - lateinit var glyphs: EmojiGlyphs + lateinit var glyphs: EmojiGlyphs // TODO: Support multiple emoji pools operator fun get(emoji: String) = glyphs.getEmoji(emoji) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index 5e95af895..f51144fbe 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -7,8 +7,8 @@ import com.lambda.util.math.Vec2d import java.awt.Color import java.awt.Graphics2D import java.awt.image.BufferedImage +import java.io.File import java.net.URL -import java.nio.file.Files import java.util.zip.ZipFile import javax.imageio.ImageIO import kotlin.math.ceil @@ -19,52 +19,63 @@ class EmojiGlyphs(zipUrl: String) { private val emojiMap = mutableMapOf() private val fontTexture: MipmapTexture - init { - val file = Files.createTempFile("emoji", ".zip").toFile() - val url = URL(zipUrl) - file.writeBytes(url.readBytes()) - - ZipFile(file).use { zip -> - // someone please refactor this - val first = ImageIO.read(zip.getInputStream(zip.entries().nextElement())) - - val size = zip.entries().asSequence().count() - val dimensions = Vec2d(first.width.toDouble(), first.height.toDouble()) - val texelSize = 1.0 / size - val width = first.width * ceil(sqrt(size.toDouble())).toInt() - val height = first.height * ceil(sqrt(size.toDouble())).toInt() - - val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) - val graphics = image.graphics as Graphics2D - graphics.background = Color(0, 0, 0, 0) - - var x = 0 - var y = 0 - - val time = measureTimeMillis { - zip.entries().asSequence().forEach { entry -> - val name = entry.name.substringAfterLast("/").substringBeforeLast(".") - val emoji = ImageIO.read(zip.getInputStream(entry)) - - if (x + emoji.width >= width) { - y += emoji.height - x = 0 - } + private val image: BufferedImage + private val graphics: Graphics2D - graphics.drawImage(emoji, x, y, null) + init { + var x = 0 + var y = 0 - val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize - val uv2 = Vec2d(x, y).plus(dimensions) * texelSize - emojiMap[name] = CharInfo(dimensions, uv1 * -1.0, uv2 * -1.0) + val time = measureTimeMillis { + val file = File.createTempFile("emoji", ".zip") + file.deleteOnExit() - x += emoji.width + file.outputStream().use { output -> + URL(zipUrl).openStream().use { input -> + input.copyTo(output) } } - fontTexture = MipmapTexture(image) + ZipFile(file).use { zip -> + val firstImage = ImageIO.read(zip.getInputStream(zip.entries().nextElement())) + + val length = zip.size().toDouble() + val squaredLength = ceil(sqrt(length)).toInt() + + val texelSize = 1.0 / length + val width = firstImage.width + val height = firstImage.height + + image = BufferedImage(width * squaredLength, height * squaredLength, BufferedImage.TYPE_INT_ARGB) + graphics = image.graphics as Graphics2D + graphics.color = Color(0, 0, 0, 0) + + zip.entries().asSequence() + .forEach { entry -> + val name = entry.name.substringAfterLast("/").substringBeforeLast(".") + val emoji = ImageIO.read(zip.getInputStream(entry)) + + if (x + emoji.width >= image.width) { + y += emoji.height + x = 0 + } - LOG.info("Loaded $size emojis in $time ms") + graphics.drawImage(emoji, x, y, null) + + val size = Vec2d(emoji.width, emoji.height) + val uv1 = Vec2d(-x, -y) * texelSize + val uv2 = Vec2d(-x, -y).minus(size) * texelSize + + emojiMap[name] = CharInfo(size, uv1, uv2) + + x += emoji.width + } + } + + fontTexture = MipmapTexture(image) } + + LOG.info("Loaded ${emojiMap.size} emojis in $time ms") } fun bind() { @@ -78,6 +89,6 @@ class EmojiGlyphs(zipUrl: String) { emojiMap[emoji] companion object { - private const val GL_TEXTURE_SLOT = 1 + private const val GL_TEXTURE_SLOT = 1 // TODO: Texture slot borrowing } } diff --git a/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt index 31f85bf9b..966d59c95 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt @@ -4,10 +4,12 @@ data class Vec2d(val x: Double, val y: Double) { constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) constructor(x: Float, y: Float) : this(x.toDouble(), y.toDouble()) + operator fun unaryPlus() = this operator fun plus(vec2d: Vec2d) = plus(vec2d.x, vec2d.y) operator fun plus(add: Double) = plus(add, add) fun plus(x: Double, y: Double) = Vec2d(this.x + x, this.y + y) + operator fun unaryMinus() = Vec2d(-x, -y) operator fun minus(vec2d: Vec2d) = minus(vec2d.x, vec2d.y) operator fun minus(sub: Double) = minus(sub, sub) fun minus(x: Double, y: Double) = plus(-x, -y) @@ -29,4 +31,4 @@ data class Vec2d(val x: Double, val y: Double) { val TOP = Vec2d(0.0, 1.0) val BOTTOM = Vec2d(0.0, -1.0) } -} \ No newline at end of file +} From 7b4e22c00e183ffb14086bbc5eee1f7a7b9efb4e Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:53:17 -0400 Subject: [PATCH 12/42] Feature: Emoji renderer --- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 45 ++++++++++--------- .../renderer/gui/font/glyph/FontGlyphs.kt | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index f51144fbe..f1fcf41b4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -1,5 +1,6 @@ package com.lambda.graphics.renderer.gui.font.glyph +import com.google.common.math.IntMath.pow import com.lambda.Lambda.LOG import com.lambda.graphics.texture.MipmapTexture import com.lambda.module.modules.client.FontSettings @@ -12,6 +13,7 @@ import java.net.URL import java.util.zip.ZipFile import javax.imageio.ImageIO import kotlin.math.ceil +import kotlin.math.log2 import kotlin.math.sqrt import kotlin.system.measureTimeMillis @@ -40,38 +42,41 @@ class EmojiGlyphs(zipUrl: String) { val firstImage = ImageIO.read(zip.getInputStream(zip.entries().nextElement())) val length = zip.size().toDouble() - val squaredLength = ceil(sqrt(length)).toInt() - val texelSize = 1.0 / length - val width = firstImage.width - val height = firstImage.height + // TODO: This is a hack but it works + val width = pow(2, ceil(log2(firstImage.width * sqrt(length))).toInt()) + val height = pow(2, ceil(log2(firstImage.height * sqrt(length))).toInt()) + val texelSize = 1.0 / width // This assumes that the texture is a power of 2 - image = BufferedImage(width * squaredLength, height * squaredLength, BufferedImage.TYPE_INT_ARGB) + image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) graphics = image.graphics as Graphics2D graphics.color = Color(0, 0, 0, 0) - zip.entries().asSequence() - .forEach { entry -> - val name = entry.name.substringAfterLast("/").substringBeforeLast(".") - val emoji = ImageIO.read(zip.getInputStream(entry)) + for (entry in zip.entries()) { + val name = entry.name.substringAfterLast("/").substringBeforeLast(".") + val emoji = ImageIO.read(zip.getInputStream(entry)) - if (x + emoji.width >= image.width) { - y += emoji.height - x = 0 - } + if (x + emoji.width >= image.width) { + y += emoji.height + x = 0 + } - graphics.drawImage(emoji, x, y, null) + check(y + emoji.height < image.height) { "Can't load emoji glyphs. Texture size is too small" } - val size = Vec2d(emoji.width, emoji.height) - val uv1 = Vec2d(-x, -y) * texelSize - val uv2 = Vec2d(-x, -y).minus(size) * texelSize + graphics.drawImage(emoji, x, y, null) - emojiMap[name] = CharInfo(size, uv1, uv2) + val size = Vec2d(emoji.width, emoji.height) + val uv1 = Vec2d(x, y) * texelSize + val uv2 = Vec2d(x, y).plus(size) * texelSize - x += emoji.width - } + emojiMap[name] = CharInfo(size, -uv1, -uv2) + + x += emoji.width + } } + ImageIO.write(image, "png", File("emoji.png")) + fontTexture = MipmapTexture(image) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index eb6aa1f99..4de8d5ec0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -41,7 +41,7 @@ class FontGlyphs(font: Font) { rowHeight = 0 } - check(y + charImage.height < TEXTURE_SIZE) { "Can't load font glyphs. Texture size is too small" } + check(y + charImage.height <= TEXTURE_SIZE) { "Can't load font glyphs. Texture size is too small" } graphics.drawImage(charImage, x, y, null) From c9b1863f7d7c526dbff17cb5b264c001d5319092 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:10:58 -0400 Subject: [PATCH 13/42] Public emoji parsing --- .../com/lambda/graphics/renderer/gui/font/FontRenderer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 39b1d26ea..77dba7a8a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -23,7 +23,7 @@ class FontRenderer( * @param text The text to parse. * @return A list of triples containing the emoji text, start index, and end index. */ - private fun parseEmojis(text: String): List< + fun parseEmojis(text: String): List< Triple> { val result = mutableListOf>() val matches = emojiRegex.findAll(text) From 944a9bc23e032bdee81b5c5757e0be389c14deab Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:56:50 -0400 Subject: [PATCH 14/42] Refactor: Code readability --- .../renderer/gui/font/FontRenderer.kt | 169 +++++++++--------- .../renderer/gui/font/glyph/CharInfo.kt | 29 +++ .../module/modules/client/FontSettings.kt | 4 +- 3 files changed, 116 insertions(+), 86 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 77dba7a8a..23fae731b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -12,9 +12,9 @@ import java.awt.Color class FontRenderer( private val font: LambdaFont, - private val emojis: LambdaMoji, + private val emojis: LambdaMoji ) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) { - var scaleMultiplier = 1.0 + private val scaleMultiplier = 1.0 private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") /** @@ -23,19 +23,22 @@ class FontRenderer( * @param text The text to parse. * @return A list of triples containing the emoji text, start index, and end index. */ - fun parseEmojis(text: String): List< - Triple> { + fun parseEmojis(text: String): List> { val result = mutableListOf>() val matches = emojiRegex.findAll(text) - matches.forEach { - val index = it.value.substring(1, it.value.length - 1) - result.add(Triple(emojis[index] ?: return@forEach, it.range.first, it.range.last)) + for (match in matches) { + val emojiKey = match.value.substring(1, match.value.length - 1) + val charInfo = emojis[emojiKey] ?: continue + result.add(Triple(charInfo, match.range.first, match.range.last)) } return result } + /** + * Builds the vertex array for rendering the text. + */ fun build( text: String, position: Vec2d, @@ -43,10 +46,49 @@ class FontRenderer( scale: Double = 1.0, shadow: Boolean = true ) = vao.use { + iterateText(text, scale, shadow, color) { char, pos1, pos2, color -> + putChar(position, pos1, pos2, color, char) + } + } + + /** + * Calculates the width of the given text. + */ + fun getWidth(text: String, scale: Double = 1.0): Double { + var width = 0.0 + iterateText(text, scale, false) { char, _, _, _ -> width += char.width + gap } + return width * getScaleFactor(scale) + } + + /** + * Calculates the height of the text. + * + * The values are hardcoded + * We do not need to ask the emoji font since the height is smaller + */ + private fun getHeight(scale: Double = 1.0) = font.glyphs.fontHeight * getScaleFactor(scale) * 0.7 + + /** + * Iterates over each character and emoji in the text. + * + * @param text The text to iterate over. + * @param scale The scale of the text. + * @param shadow Whether to render a shadow. + * @param color The color of the text. + * @param block The block to execute for each character. + * + * @see CharInfo + */ + private fun iterateText( + text: String, + scale: Double, + shadow: Boolean, + color: Color = Color.WHITE, + block: (CharInfo, Vec2d, Vec2d, Color) -> Unit + ) { val actualScale = getScaleFactor(scale) val scaledShadowShift = shadowShift * actualScale val scaledGap = gap * actualScale - val shadowColor = getShadowColor(color) var posX = 0.0 val posY = getHeight(scale) * -0.5 + baselineOffset * actualScale @@ -55,85 +97,43 @@ class FontRenderer( var index = 0 while (index < text.length) { - emojis - .firstOrNull { index in it.second..it.third } - ?.let { emoji -> - val scaledSize = emoji.first.size * actualScale - val pos1 = Vec2d(posX, posY) - val pos2 = pos1 + scaledSize - - putChar(position, pos1, pos2, color, emoji.first) - - posX += scaledSize.x + scaledGap - index += emoji.third - emoji.second + 1 - - if (index >= text.length) { - // This means we've reached the end of the text - return@use + run { // Because continue is not allowed in lambda + emojis + .firstOrNull { index in it.second..it.third } + ?.let { emoji -> + val scaledSize = emoji.first.size * actualScale + val pos1 = Vec2d(posX, posY) + val pos2 = pos1 + scaledSize + + block(emoji.first, pos1, pos2, color) + + posX += scaledSize.x + scaledGap + index += emoji.third - emoji.second + 1 + return@run } - } - val char = text[index] - val glyph = font[char] ?: run { - index++ - return@use - } + val char = text[index] + val glyph = font[char] ?: return@run - val scaledSize = glyph.size * actualScale + val scaledSize = glyph.size * actualScale + val pos1 = Vec2d(posX, posY) + val pos2 = pos1 + scaledSize - val pos1 = Vec2d(posX, posY) - val pos2 = pos1 + scaledSize - - if (shadow && FontSettings.shadow) { - val shadowPos1 = pos1 + scaledShadowShift - val shadowPos2 = shadowPos1 + scaledSize - putChar(position, shadowPos1, shadowPos2, shadowColor, glyph) - } - - putChar(position, pos1, pos2, color, glyph) - - posX += scaledSize.x + scaledGap - index++ - } - } - - fun getWidth(text: String, scale: Double = 1.0): Double { - var width = 0.0 - - val emojis = parseEmojis(text) - - var index = 0 - while (index < text.length) { - emojis - .firstOrNull { index in it.second..it.third } - ?.let { emoji -> - val scaledSize = emoji.first.size * getScaleFactor(scale) - width += scaledSize.x + gap - - index += emoji.third - emoji.second + 1 - - if (index >= text.length) { - // This means we've reached the end of the text - return width * getScaleFactor(scale) - } + if (shadow && FontSettings.shadow) { + val shadowPos1 = pos1 + scaledShadowShift + val shadowPos2 = shadowPos1 + scaledSize + block(glyph, shadowPos1, shadowPos2, getShadowColor(color)) } - val char = text[index] - val glyph = font[char] ?: run { - index++ - return width * getScaleFactor(scale) + block(glyph, pos1, pos2, color) + + posX += scaledSize.x + scaledGap } - width += glyph.width + gap index++ } - - return width * getScaleFactor(scale) } - fun getHeight(scale: Double = 1.0) = - font.glyphs.fontHeight * getScaleFactor(scale) * 0.7 - private fun IRenderContext.putChar(pos: Vec2d, lt: Vec2d, rb: Vec2d, color: Color, ci: CharInfo) { val x = pos.x val y = pos.y @@ -148,20 +148,22 @@ class FontRenderer( ) } - private fun getScaleFactor(scale: Double) = - scaleMultiplier * scale * 0.12 + private fun getScaleFactor(scale: Double) = scaleMultiplier * scale * 0.12 - private fun getShadowColor(color: Color) = Color( - (color.red * FontSettings.shadowBrightness).toInt(), - (color.green * FontSettings.shadowBrightness).toInt(), - (color.blue * FontSettings.shadowBrightness).toInt(), - color.alpha - ) + private fun getShadowColor(color: Color): Color { + return Color( + (color.red * FontSettings.shadowBrightness).toInt(), + (color.green * FontSettings.shadowBrightness).toInt(), + (color.blue * FontSettings.shadowBrightness).toInt(), + color.alpha + ) + } override fun render() { shader.use() font.glyphs.bind() + emojis.glyphs.bind() shader["u_EmojiTexture"] = 1 @@ -170,7 +172,6 @@ class FontRenderer( companion object { private val shader = Shader("renderer/font") - private val shadowShift get() = FontSettings.shadowShift * 4.0 private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt index eddd13a76..02e379d99 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt @@ -2,16 +2,45 @@ package com.lambda.graphics.renderer.gui.font.glyph import com.lambda.util.math.Vec2d +/** + * Represents information about a character (glyph) in a font. + * + * @property size The size of the character in a 2D vector. + * @property uv1 The top-left UV coordinates of the character texture. + * @property uv2 The bottom-right UV coordinates of the character texture. + */ data class CharInfo( val size: Vec2d, val uv1: Vec2d, val uv2: Vec2d ) { + /** + * The width of the character. + */ val width get() = size.x + + /** + * The height of the character. + */ val height get() = size.y + /** + * The U coordinate of the top-left corner of the character texture. + */ val u1 get() = uv1.x + + /** + * The V coordinate of the top-left corner of the character texture. + */ val v1 get() = uv1.y + + /** + * The U coordinate of the bottom-right corner of the character texture. + */ val u2 get() = uv2.x + + /** + * The V coordinate of the bottom-right corner of the character texture. + */ val v2 get() = uv2.y } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt index e552225b5..560d3e784 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt @@ -9,8 +9,8 @@ object FontSettings : Module( defaultTags = setOf(ModuleTag.CLIENT) ) { val shadow by setting("Shadow", true) - val shadowBrightness by setting("Shadow Brightness", 0.35, 0.0..0.5, 0.01, visibility = { shadow }) - val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05, visibility = { shadow }) + val shadowBrightness by setting("Shadow Brightness", 0.35, 0.0..0.5, 0.01) { shadow } + val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05) { shadow } val gapSetting by setting("Gap", 1.5, -10.0..10.0, 0.5) val baselineOffset by setting("Vertical Offset", 0.0, -10.0..10.0, 0.5) private val lodBiasSetting by setting("Smoothing", 0.0, -10.0..10.0, 0.5) From 8921bf7caf75f192e4d8d4221dda811393c0aec9 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 6 Jun 2024 17:36:00 +0300 Subject: [PATCH 15/42] Emoji artifact & space fixes --- .../renderer/gui/font/FontRenderer.kt | 30 +++++++++++-------- .../graphics/renderer/gui/font/LambdaMoji.kt | 4 +-- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 25 +++++++++------- .../renderer/gui/font/glyph/FontGlyphs.kt | 8 ++--- .../font/glyph/{CharInfo.kt => GlyphInfo.kt} | 2 +- .../lambda/module/modules/debug/RenderTest.kt | 2 +- 6 files changed, 40 insertions(+), 31 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/{CharInfo.kt => GlyphInfo.kt} (97%) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 23fae731b..11e5dcced 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -4,9 +4,11 @@ import com.lambda.graphics.buffer.vao.IRenderContext import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.Renderer -import com.lambda.graphics.renderer.gui.font.glyph.CharInfo +import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo import com.lambda.graphics.shader.Shader import com.lambda.module.modules.client.FontSettings +import com.lambda.util.math.ColorUtils.a +import com.lambda.util.math.ColorUtils.setAlpha import com.lambda.util.math.Vec2d import java.awt.Color @@ -23,14 +25,14 @@ class FontRenderer( * @param text The text to parse. * @return A list of triples containing the emoji text, start index, and end index. */ - fun parseEmojis(text: String): List> { - val result = mutableListOf>() + fun parseEmojis(text: String): List> { + val result = mutableListOf>() val matches = emojiRegex.findAll(text) for (match in matches) { val emojiKey = match.value.substring(1, match.value.length - 1) val charInfo = emojis[emojiKey] ?: continue - result.add(Triple(charInfo, match.range.first, match.range.last)) + result.add(charInfo to match.range) } return result @@ -77,18 +79,20 @@ class FontRenderer( * @param color The color of the text. * @param block The block to execute for each character. * - * @see CharInfo + * @see GlyphInfo */ private fun iterateText( text: String, scale: Double, shadow: Boolean, color: Color = Color.WHITE, - block: (CharInfo, Vec2d, Vec2d, Color) -> Unit + block: (GlyphInfo, Vec2d, Vec2d, Color) -> Unit ) { val actualScale = getScaleFactor(scale) val scaledShadowShift = shadowShift * actualScale val scaledGap = gap * actualScale + val shadowColor = getShadowColor(color) + val emojiColor = Color.WHITE.setAlpha(color.a) var posX = 0.0 val posY = getHeight(scale) * -0.5 + baselineOffset * actualScale @@ -99,16 +103,16 @@ class FontRenderer( while (index < text.length) { run { // Because continue is not allowed in lambda emojis - .firstOrNull { index in it.second..it.third } + .firstOrNull { index in it.second } ?.let { emoji -> val scaledSize = emoji.first.size * actualScale val pos1 = Vec2d(posX, posY) val pos2 = pos1 + scaledSize - block(emoji.first, pos1, pos2, color) + block(emoji.first, pos1, pos2, emojiColor) posX += scaledSize.x + scaledGap - index += emoji.third - emoji.second + 1 + index = emoji.second.last return@run } @@ -122,7 +126,7 @@ class FontRenderer( if (shadow && FontSettings.shadow) { val shadowPos1 = pos1 + scaledShadowShift val shadowPos2 = shadowPos1 + scaledSize - block(glyph, shadowPos1, shadowPos2, getShadowColor(color)) + block(glyph, shadowPos1, shadowPos2, shadowColor) } block(glyph, pos1, pos2, color) @@ -134,7 +138,7 @@ class FontRenderer( } } - private fun IRenderContext.putChar(pos: Vec2d, lt: Vec2d, rb: Vec2d, color: Color, ci: CharInfo) { + private fun IRenderContext.putChar(pos: Vec2d, lt: Vec2d, rb: Vec2d, color: Color, ci: GlyphInfo) { val x = pos.x val y = pos.y @@ -161,17 +165,17 @@ class FontRenderer( override fun render() { shader.use() + shader["u_EmojiTexture"] = 1 font.glyphs.bind() - emojis.glyphs.bind() - shader["u_EmojiTexture"] = 1 super.render() } companion object { private val shader = Shader("renderer/font") + private val shadowShift get() = FontSettings.shadowShift * 4.0 private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt index 16fd6b098..39e9bae9e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt @@ -16,8 +16,8 @@ enum class LambdaMoji(private val zipUrl: String) { object Loader : Loadable { override fun load(): String { - LambdaMoji.entries.forEach(LambdaMoji::loadGlyphs) - return "Loaded ${LambdaMoji.entries.size} emoji pools" + entries.forEach(LambdaMoji::loadGlyphs) + return "Loaded ${entries.size} emoji pools" } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index f1fcf41b4..4d8f948d8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -17,8 +17,9 @@ import kotlin.math.log2 import kotlin.math.sqrt import kotlin.system.measureTimeMillis +// TODO: AbstractGlyphs to use for both Font & Emoji glyphs? class EmojiGlyphs(zipUrl: String) { - private val emojiMap = mutableMapOf() + private val emojiMap = mutableMapOf() private val fontTexture: MipmapTexture private val image: BufferedImage @@ -43,10 +44,12 @@ class EmojiGlyphs(zipUrl: String) { val length = zip.size().toDouble() - // TODO: This is a hack but it works - val width = pow(2, ceil(log2(firstImage.width * sqrt(length))).toInt()) - val height = pow(2, ceil(log2(firstImage.height * sqrt(length))).toInt()) - val texelSize = 1.0 / width // This assumes that the texture is a power of 2 + fun getTextureDimensionLength(dimLength: Int) = + pow(2, ceil(log2((dimLength + STEP) * sqrt(length))).toInt()) + + val width = getTextureDimensionLength(firstImage.width) + val height = getTextureDimensionLength(firstImage.height) + val texelSize = Vec2d.ONE / Vec2d(width, height) image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) graphics = image.graphics as Graphics2D @@ -57,7 +60,7 @@ class EmojiGlyphs(zipUrl: String) { val emoji = ImageIO.read(zip.getInputStream(entry)) if (x + emoji.width >= image.width) { - y += emoji.height + y += emoji.height + STEP x = 0 } @@ -69,13 +72,13 @@ class EmojiGlyphs(zipUrl: String) { val uv1 = Vec2d(x, y) * texelSize val uv2 = Vec2d(x, y).plus(size) * texelSize - emojiMap[name] = CharInfo(size, -uv1, -uv2) + emojiMap[name] = GlyphInfo(size, -uv1, -uv2) - x += emoji.width + x += emoji.width + STEP } } - ImageIO.write(image, "png", File("emoji.png")) + //ImageIO.write(image, "png", File("emoji.png")) fontTexture = MipmapTexture(image) } @@ -90,10 +93,12 @@ class EmojiGlyphs(zipUrl: String) { } } - fun getEmoji(emoji: String): CharInfo? = + fun getEmoji(emoji: String): GlyphInfo? = emojiMap[emoji] companion object { + private const val STEP = 2 + private const val GL_TEXTURE_SLOT = 1 // TODO: Texture slot borrowing } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 4de8d5ec0..45bfe150b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -14,7 +14,7 @@ import kotlin.math.max import kotlin.system.measureTimeMillis class FontGlyphs(font: Font) { - private val charMap = Int2ObjectOpenHashMap() + private val charMap = Int2ObjectOpenHashMap() private val fontTexture: MipmapTexture var fontHeight = 0.0; private set @@ -35,7 +35,7 @@ class FontGlyphs(font: Font) { rowHeight = max(rowHeight, charImage.height + STEP) - if (x + charImage.width + STEP >= TEXTURE_SIZE) { + if (x + charImage.width >= TEXTURE_SIZE) { y += rowHeight x = 0 rowHeight = 0 @@ -49,7 +49,7 @@ class FontGlyphs(font: Font) { val uv1 = Vec2d(x, y) * ONE_TEXEL_SIZE val uv2 = Vec2d(x, y).plus(size) * ONE_TEXEL_SIZE - charMap[char.code] = CharInfo(size, uv1, uv2) + charMap[char.code] = GlyphInfo(size, uv1, uv2) fontHeight = max(fontHeight, size.y) x += charImage.width + STEP @@ -68,7 +68,7 @@ class FontGlyphs(font: Font) { } } - fun getChar(char: Char): CharInfo? = + fun getChar(char: Char): GlyphInfo? = charMap[char.code] companion object { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/GlyphInfo.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/GlyphInfo.kt index 02e379d99..281bc8a3a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/GlyphInfo.kt @@ -9,7 +9,7 @@ import com.lambda.util.math.Vec2d * @property uv1 The top-left UV coordinates of the character texture. * @property uv2 The bottom-right UV coordinates of the character texture. */ -data class CharInfo( +data class GlyphInfo( val size: Vec2d, val uv1: Vec2d, val uv2: Vec2d diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index c6c73c6eb..9ba72725d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -4,7 +4,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag object RenderTest : Module( - name = "RenderTest", + name = "Render:shrimp:Test:canned_food:", description = "RenderTest", defaultTags = setOf(ModuleTag.DEBUG) ) { From 33095957033610b62c0e4b5d965f036b7cdcdee1 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 6 Jun 2024 18:27:33 +0300 Subject: [PATCH 16/42] Kotlinize yourself --- .../renderer/gui/font/FontRenderer.kt | 97 ++++++++----------- 1 file changed, 39 insertions(+), 58 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 11e5dcced..58f53092d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -1,6 +1,5 @@ package com.lambda.graphics.renderer.gui.font -import com.lambda.graphics.buffer.vao.IRenderContext import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.Renderer @@ -25,19 +24,15 @@ class FontRenderer( * @param text The text to parse. * @return A list of triples containing the emoji text, start index, and end index. */ - fun parseEmojis(text: String): List> { - val result = mutableListOf>() - val matches = emojiRegex.findAll(text) - - for (match in matches) { - val emojiKey = match.value.substring(1, match.value.length - 1) - val charInfo = emojis[emojiKey] ?: continue - result.add(charInfo to match.range) + fun parseEmojis(text: String) = + mutableListOf>().apply { + emojiRegex.findAll(text).forEach { match -> + val emojiKey = match.value.substring(1, match.value.length - 1) + val charInfo = emojis[emojiKey] ?: return@forEach + add(charInfo to match.range) + } } - return result - } - /** * Builds the vertex array for rendering the text. */ @@ -49,7 +44,13 @@ class FontRenderer( shadow: Boolean = true ) = vao.use { iterateText(text, scale, shadow, color) { char, pos1, pos2, color -> - putChar(position, pos1, pos2, color, char) + grow(4) + putQuad( + vec2(pos1.x + position.x, pos1.y + position.y).vec2(char.uv1.x, char.uv1.y).color(color).end(), + vec2(pos1.x + position.x, pos2.y + position.y).vec2(char.uv1.x, char.uv2.y).color(color).end(), + vec2(pos2.x + position.x, pos2.y + position.y).vec2(char.uv2.x, char.uv2.y).color(color).end(), + vec2(pos2.x + position.x, pos1.y + position.y).vec2(char.uv2.x, char.uv1.y).color(color).end() + ) } } @@ -89,8 +90,8 @@ class FontRenderer( block: (GlyphInfo, Vec2d, Vec2d, Color) -> Unit ) { val actualScale = getScaleFactor(scale) - val scaledShadowShift = shadowShift * actualScale val scaledGap = gap * actualScale + val shadowColor = getShadowColor(color) val emojiColor = Color.WHITE.setAlpha(color.a) @@ -99,59 +100,39 @@ class FontRenderer( val emojis = parseEmojis(text) - var index = 0 - while (index < text.length) { - run { // Because continue is not allowed in lambda - emojis - .firstOrNull { index in it.second } - ?.let { emoji -> - val scaledSize = emoji.first.size * actualScale - val pos1 = Vec2d(posX, posY) - val pos2 = pos1 + scaledSize - - block(emoji.first, pos1, pos2, emojiColor) + repeat(text.length) { index -> + fun draw(info: GlyphInfo, color: Color, offset: Double = 0.0) { + val scaledSize = info.size * actualScale + val pos1 = Vec2d(posX, posY) + offset * actualScale + val pos2 = pos1 + scaledSize - posX += scaledSize.x + scaledGap - index = emoji.second.last - return@run - } + block(info, pos1, pos2, color) + if (offset == 0.0) posX += scaledSize.x + scaledGap + } - val char = text[index] - val glyph = font[char] ?: return@run + // Check if there's an emoji + emojis.firstOrNull { index in it.second }?.let { emoji -> + // Replace first emoji char by an emoji glyph and skip the other ones + if (index == emoji.second.first) { + draw(emoji.first, emojiColor) + } - val scaledSize = glyph.size * actualScale - val pos1 = Vec2d(posX, posY) - val pos2 = pos1 + scaledSize + return@repeat + } - if (shadow && FontSettings.shadow) { - val shadowPos1 = pos1 + scaledShadowShift - val shadowPos2 = shadowPos1 + scaledSize - block(glyph, shadowPos1, shadowPos2, shadowColor) + // Render chars + font[text[index]]?.let { info -> + // Draw a shadow before + if (shadow && FontSettings.shadow && shadowShift > 0.0) { + draw(info, shadowColor, shadowShift) } - block(glyph, pos1, pos2, color) - - posX += scaledSize.x + scaledGap + // Draw actual char over the shadow + draw(info, color) } - - index++ } } - private fun IRenderContext.putChar(pos: Vec2d, lt: Vec2d, rb: Vec2d, color: Color, ci: GlyphInfo) { - val x = pos.x - val y = pos.y - - grow(4) - - putQuad( - vec2(lt.x + x, lt.y + y).vec2(ci.uv1.x, ci.uv1.y).color(color).end(), - vec2(lt.x + x, rb.y + y).vec2(ci.uv1.x, ci.uv2.y).color(color).end(), - vec2(rb.x + x, rb.y + y).vec2(ci.uv2.x, ci.uv2.y).color(color).end(), - vec2(rb.x + x, lt.y + y).vec2(ci.uv2.x, ci.uv1.y).color(color).end() - ) - } - private fun getScaleFactor(scale: Double) = scaleMultiplier * scale * 0.12 private fun getShadowColor(color: Color): Color { @@ -176,7 +157,7 @@ class FontRenderer( companion object { private val shader = Shader("renderer/font") - private val shadowShift get() = FontSettings.shadowShift * 4.0 + private val shadowShift get() = FontSettings.shadowShift * 5.0 private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f } From 7c179d1e0f741448bc0ac8dbe254f10722db8e92 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Fri, 7 Jun 2024 19:50:47 +0300 Subject: [PATCH 17/42] Renderer cleanup --- .../kotlin/com/lambda/graphics/gl/Scissor.kt | 4 ++-- .../com/lambda/graphics/renderer/Renderer.kt | 15 --------------- .../renderer/gui/font/FontRenderer.kt | 12 ++++++++---- .../renderer/gui/rect/AbstractRectRenderer.kt | 19 +++++++++++-------- .../renderer/gui/rect/FilledRectRenderer.kt | 1 - .../com/lambda/graphics/texture/Texture.kt | 11 ----------- 6 files changed, 21 insertions(+), 41 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt index e8f936185..0a8c581d9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt @@ -13,7 +13,7 @@ object Scissor { private var stack = ArrayDeque() fun scissor(rect: Rect, block: () -> Unit) { - // clamp corners so children scissor box can't overlap parent + // clamp corners so children scissor boxes can't overlap parent val processed = stack.lastOrNull()?.let(rect::clamp) ?: rect registerScissor(processed, block) } @@ -25,7 +25,7 @@ object Scissor { block() stack.removeLast() - stack.lastOrNull().apply(::scissor) + scissor(stack.lastOrNull()) } private fun scissor(entry: Rect?) { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt deleted file mode 100644 index 4151bd754..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.lambda.graphics.renderer - -import com.lambda.graphics.buffer.vao.VAO -import com.lambda.graphics.buffer.vao.vertex.VertexAttrib -import com.lambda.graphics.buffer.vao.vertex.VertexMode - -abstract class Renderer(drawMode: VertexMode, attribGroup: VertexAttrib.Group) { - protected val vao = VAO(drawMode, attribGroup) - - open fun render() { - vao.upload() - vao.render() - vao.clear() - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index 58f53092d..db5a4b13e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -1,8 +1,8 @@ package com.lambda.graphics.renderer.gui.font +import com.lambda.graphics.buffer.vao.VAO import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode -import com.lambda.graphics.renderer.Renderer import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo import com.lambda.graphics.shader.Shader import com.lambda.module.modules.client.FontSettings @@ -14,7 +14,9 @@ import java.awt.Color class FontRenderer( private val font: LambdaFont, private val emojis: LambdaMoji -) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) { +) { + private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) + private val scaleMultiplier = 1.0 private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") @@ -144,14 +146,16 @@ class FontRenderer( ) } - override fun render() { + fun render() { shader.use() shader["u_EmojiTexture"] = 1 font.glyphs.bind() emojis.glyphs.bind() - super.render() + vao.upload() + vao.render() + vao.clear() } companion object { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/AbstractRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/AbstractRectRenderer.kt index b32b95970..9e893e36a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/AbstractRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/AbstractRectRenderer.kt @@ -1,9 +1,9 @@ package com.lambda.graphics.renderer.gui.rect -import com.lambda.Lambda.mc +import com.lambda.graphics.RenderMain +import com.lambda.graphics.buffer.vao.VAO import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode -import com.lambda.graphics.renderer.Renderer import com.lambda.graphics.shader.Shader import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.Vec2d @@ -12,16 +12,19 @@ import org.lwjgl.glfw.GLFW.glfwGetTime abstract class AbstractRectRenderer( attribGroup: VertexAttrib.Group, val shader: Shader -) : Renderer(VertexMode.TRIANGLES, attribGroup) { - override fun render() { +) { + protected val vao = VAO(VertexMode.TRIANGLES, attribGroup) + + open fun render() { shader.use() shader["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 shader["u_Color1"] = GuiSettings.shadeColor1 shader["u_Color2"] = GuiSettings.shadeColor2 - val screen = Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) - val size = Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) - shader["u_Size"] = screen / size - super.render() + shader["u_Size"] = RenderMain.screenSize /Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) + + vao.upload() + vao.render() + vao.clear() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index 7294fa779..6ea38949f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -59,7 +59,6 @@ class FilledRectRenderer : AbstractRectRenderer( override fun render() { shader.use() - super.render() } diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt index 8c48ed6c4..b32302118 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -15,15 +15,4 @@ abstract class Texture { protected abstract fun init() open fun bind(slot: Int = 0) = bindTexture(id, slot) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Texture - - return id == other.id - } - - override fun hashCode() = id } From 631ad184e1beaa3d9fc4cee04e7568c5e824d9cb Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Fri, 7 Jun 2024 21:49:12 +0300 Subject: [PATCH 18/42] ESP (pushing but needs to be refactored) --- .../com/lambda/event/events/RenderEvent.kt | 4 + .../com/lambda/graphics/gl/GlStateUtils.kt | 6 + .../renderer/esp/CachedEspRenderer.kt | 128 ++++++++++++++++ .../graphics/renderer/esp/DirectionMask.kt | 37 +++++ .../renderer/esp/EntityEspRegistry.kt | 139 ++++++++++++++++++ .../lambda/module/modules/debug/RenderTest.kt | 55 +++---- 6 files changed, 342 insertions(+), 27 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index d222a7caa..b1776b7e7 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -5,6 +5,9 @@ import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import com.lambda.util.math.Vec2d +import net.minecraft.entity.LivingEntity +import net.minecraft.util.math.BlockPos +import java.awt.Color abstract class RenderEvent : Event { class World : RenderEvent() @@ -15,5 +18,6 @@ abstract class RenderEvent : Event { val screenSize = Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) / scale } + class UpdateTarget : RenderEvent(), ICancellable by Cancellable() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt index 71b463f83..3ff10e021 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -36,6 +36,12 @@ object GlStateUtils { depthTest(false) } + fun withLineWidth(width: Double, block: () -> Unit) { + glLineWidth(width.toFloat()) + block() + glLineWidth(1f) + } + @JvmStatic fun capSet(id: Int, flag: Boolean) { val field = when (id) { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt new file mode 100644 index 000000000..742ccec46 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt @@ -0,0 +1,128 @@ +package com.lambda.graphics.renderer.esp + +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.gl.GlStateUtils.withLineWidth +import com.lambda.graphics.renderer.esp.DirectionMask.DOWN +import com.lambda.graphics.renderer.esp.DirectionMask.EAST +import com.lambda.graphics.renderer.esp.DirectionMask.NORTH +import com.lambda.graphics.renderer.esp.DirectionMask.SOUTH +import com.lambda.graphics.renderer.esp.DirectionMask.UP +import com.lambda.graphics.renderer.esp.DirectionMask.WEST +import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection +import com.lambda.graphics.shader.Shader +import com.lambda.module.Module +import com.lambda.util.primitives.extension.max +import com.lambda.util.primitives.extension.min +import net.minecraft.util.math.Box +import java.awt.Color + +class CachedEspRenderer(val owner: Any) { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER) + private var updateFilled = false + + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER) + private var updateOutline = false + + var outlineWidth = 1.0 + + fun build(box: Box, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) { + buildFilled(box, filledColor, sides) + buildOutline(box, outlineColor, sides, outlineMode) + } + + fun buildFilled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = filled.use { + updateFilled = true + val pos1 = box.min + val pos2 = box.max + + grow(8) + + val blb by lazy { vec3(pos1.x, pos1.y, pos1.z).color(color).end() } + val blf by lazy { vec3(pos1.x, pos1.y, pos2.z).color(color).end() } + val brb by lazy { vec3(pos2.x, pos1.y, pos1.z).color(color).end() } + val brf by lazy { vec3(pos2.x, pos1.y, pos2.z).color(color).end() } + val tlb by lazy { vec3(pos1.x, pos2.y, pos1.z).color(color).end() } + val tlf by lazy { vec3(pos1.x, pos2.y, pos2.z).color(color).end() } + val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() } + val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() } + + if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf) + if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf) + if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb) + } + + fun buildOutline(box: Box, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use { + updateOutline = true + val pos1 = box.min + val pos2 = box.max + + grow(8) + + val blb by lazy { vec3(pos1.x, pos1.y, pos1.z).color(color).end() } + val blf by lazy { vec3(pos1.x, pos1.y, pos2.z).color(color).end() } + val brb by lazy { vec3(pos2.x, pos1.y, pos1.z).color(color).end() } + val brf by lazy { vec3(pos2.x, pos1.y, pos2.z).color(color).end() } + val tlb by lazy { vec3(pos1.x, pos2.y, pos1.z).color(color).end() } + val tlf by lazy { vec3(pos1.x, pos2.y, pos2.z).color(color).end() } + val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() } + val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() } + + val hasEast = sides.hasDirection(EAST) + val hasWest = sides.hasDirection(WEST) + val hasUp = sides.hasDirection(UP) + val hasDown = sides.hasDirection(DOWN) + val hasSouth = sides.hasDirection(SOUTH) + val hasNorth = sides.hasDirection(NORTH) + + if (outlineMode.check(hasUp, hasNorth)) putLine(tlb, trb) + if (outlineMode.check(hasUp, hasSouth)) putLine(tlf, trf) + if (outlineMode.check(hasUp, hasWest)) putLine(tlb, tlf) + if (outlineMode.check(hasUp, hasEast)) putLine(trf, trb) + + if (outlineMode.check(hasDown, hasNorth)) putLine(blb, brb) + if (outlineMode.check(hasDown, hasSouth)) putLine(blf, brf) + if (outlineMode.check(hasDown, hasWest)) putLine(blb, blf) + if (outlineMode.check(hasDown, hasEast)) putLine(brb, brf) + + if (outlineMode.check(hasWest, hasNorth)) putLine(tlb, blb) + if (outlineMode.check(hasNorth, hasEast)) putLine(trb, brb) + if (outlineMode.check(hasEast, hasSouth)) putLine(trf, brf) + if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) + } + + fun clear() { + filled.clear() + outline.clear() + } + + init { + owner.listener { + if (updateFilled) { + updateFilled = false + filled.upload() + } + + if (updateOutline) { + updateOutline = false + outline.upload() + } + + shader.use() + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + + filled.render() + withLineWidth(outlineWidth, outline::render) + } + } + + companion object { + private val shader = Shader("renderer/pos_color", "renderer/box_static") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt new file mode 100644 index 000000000..31d338b6a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt @@ -0,0 +1,37 @@ +package com.lambda.graphics.renderer.esp + +import net.minecraft.util.math.Direction + +object DirectionMask { + const val EAST = 1 // X + + const val WEST = 2 // X - + + const val UP = 4 // Y + + const val DOWN = 8 // Y - + + const val SOUTH = 16 // Z + + const val NORTH = 32 // Z - + + const val ALL = EAST or WEST or UP or DOWN or SOUTH or NORTH + const val NONE = 0 + + fun Int.exclude(dir: Int) = this xor dir + fun Int.hasDirection(dir: Int) = (this and dir) != 0 + + val Direction.mask get() = when (this) { + Direction.DOWN -> DOWN + Direction.UP -> UP + Direction.NORTH -> NORTH + Direction.SOUTH -> SOUTH + Direction.WEST -> WEST + Direction.EAST -> EAST + } + + enum class OutlineMode(val check: (Boolean, Boolean) -> Boolean) { + // Render engine will add a line if BOTH touching sides are included into the mask + AND(Boolean::and), + + // Render engine will add a line if ANY OF touching sides is included into the mask + OR(Boolean::or) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt new file mode 100644 index 000000000..8be710f1e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt @@ -0,0 +1,139 @@ +package com.lambda.graphics.renderer.esp + +import com.lambda.context.SafeContext +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.esp.DirectionMask.DOWN +import com.lambda.graphics.renderer.esp.DirectionMask.EAST +import com.lambda.graphics.renderer.esp.DirectionMask.NORTH +import com.lambda.graphics.renderer.esp.DirectionMask.SOUTH +import com.lambda.graphics.renderer.esp.DirectionMask.UP +import com.lambda.graphics.renderer.esp.DirectionMask.WEST +import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection +import com.lambda.graphics.shader.Shader +import com.lambda.util.primitives.extension.max +import com.lambda.util.primitives.extension.min +import com.lambda.util.primitives.extension.partialTicks +import com.lambda.util.primitives.extension.prevPos +import net.minecraft.entity.Entity +import net.minecraft.util.math.Box +import java.awt.Color + +object EntityEspRegistry { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.DYNAMIC_RENDERER) + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.DYNAMIC_RENDERER) + private val shader = Shader("renderer/pos_color", "renderer/box_dynamic") + + fun renderEntityESP(owner: Any, block: SafeContext.(Renderer) -> Unit) { + owner.listener { + block(Renderer) + } + } + + object Renderer { + fun build(entity: Entity, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) { + buildFilled(entity, filledColor, sides) + buildOutline(entity, outlineColor, sides, outlineMode) + } + + fun buildFilled(entity: Entity, color: Color, sides: Int = DirectionMask.ALL) = filled.use { + val box = entity.boundingBox + + val delta = entity.prevPos.subtract(entity.pos) + val prevBox = Box(box.min.add(delta), box.max.add(delta)) + + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + grow(8) + + val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } + val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } + val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } + val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } + val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } + val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } + val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } + val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } + + if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf) + if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf) + if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb) + } + + fun buildOutline(entity: Entity, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use { + val box = entity.boundingBox + + val delta = entity.prevPos.subtract(entity.pos) + val prevBox = Box(box.min.add(delta), box.max.add(delta)) + + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + grow(8) + + val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } + val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } + val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } + val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } + val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } + val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } + val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } + val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } + + val hasEast = sides.hasDirection(EAST) + val hasWest = sides.hasDirection(WEST) + val hasUp = sides.hasDirection(UP) + val hasDown = sides.hasDirection(DOWN) + val hasSouth = sides.hasDirection(SOUTH) + val hasNorth = sides.hasDirection(NORTH) + + if (outlineMode.check(hasUp, hasNorth)) putLine(tlb, trb) + if (outlineMode.check(hasUp, hasSouth)) putLine(tlf, trf) + if (outlineMode.check(hasUp, hasWest)) putLine(tlb, tlf) + if (outlineMode.check(hasUp, hasEast)) putLine(trf, trb) + + if (outlineMode.check(hasDown, hasNorth)) putLine(blb, brb) + if (outlineMode.check(hasDown, hasSouth)) putLine(blf, brf) + if (outlineMode.check(hasDown, hasWest)) putLine(blb, blf) + if (outlineMode.check(hasDown, hasEast)) putLine(brb, brf) + + if (outlineMode.check(hasWest, hasNorth)) putLine(tlb, blb) + if (outlineMode.check(hasNorth, hasEast)) putLine(trb, brb) + if (outlineMode.check(hasEast, hasSouth)) putLine(trf, brf) + if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) + } + } + + init { + listener(Int.MAX_VALUE) { + filled.clear() + outline.clear() + } + + listener(Int.MIN_VALUE) { + filled.upload() + outline.upload() + } + + listener { + shader.use() + shader["u_TickDelta"] = mc.partialTicks + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + + filled.render() + outline.render() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index 9ba72725d..abfac53a0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -1,7 +1,16 @@ package com.lambda.module.modules.debug +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.esp.CachedEspRenderer +import com.lambda.graphics.renderer.esp.EntityEspRegistry.renderEntityESP import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.world.WorldUtils.getClosestEntity +import net.minecraft.entity.LivingEntity +import net.minecraft.util.math.Box +import java.awt.Color object RenderTest : Module( name = "Render:shrimp:Test:canned_food:", @@ -15,31 +24,23 @@ object RenderTest : Module( private val test31 by setting("Holla huh 1", true, visibility = { !test1 }) private val test32 by setting("Holla buh 2", true, visibility = { !test1 }) -// private val filled = DynamicFilledRenderer() -// private val outline = DynamicOutlineRenderer() -// private val color = Color(60, 200, 60) -// -// init { -// filled.build { -// val flag = mc.crosshairTarget?.blockResult?.blockPos?.let { box = Box(it) } != null -// color = color.setAlpha((color.a + flag.toIntSign() * 0.05).coerceAtMost(0.2)) -// } -// -// outline.build { -// val block = mc.crosshairTarget?.blockResult -// val flag = block?.blockPos?.let { box = Box(it) } != null -// color = color.setAlpha(color.a + flag.toIntSign() * 0.2) -// sides = block?.side?.mask ?: sides -// } -// -// listener { -// filled.update() -// outline.update() -// } -// -// listener { -// filled.render() -// outline.render() -// } -// } + private val outlineColor = Color(100, 150, 255).setAlpha(0.5) + private val filledColor = outlineColor.setAlpha(0.2) + + private val rendeer = CachedEspRenderer(this) + + init { + renderEntityESP(this) { renderer -> + val entity = getClosestEntity(player.pos, 8.0) ?: return@renderEntityESP + renderer.build(entity, filledColor, outlineColor) + } + + listener { + rendeer.build(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) + } + + onEnable { + rendeer.clear() + } + } } \ No newline at end of file From 04b8a1df94e2af6313e061f5246177dac95e8631 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:39:36 -0400 Subject: [PATCH 19/42] Fixed comment --- .../com/lambda/graphics/renderer/gui/font/FontRenderer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index db5a4b13e..20e785bdd 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -24,7 +24,7 @@ class FontRenderer( * Parses the emojis in the given text. * * @param text The text to parse. - * @return A list of triples containing the emoji text, start index, and end index. + * @return A list of pairs containing the glyph info and the range of the emoji in the text. */ fun parseEmojis(text: String) = mutableListOf>().apply { From 46b9eab9133879b48909fdad9a5408fbb9d1237a Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 8 Jun 2024 07:24:00 +0300 Subject: [PATCH 20/42] EntityESP refactor --- .../com/lambda/event/events/RenderEvent.kt | 31 +++- .../kotlin/com/lambda/graphics/RenderMain.kt | 2 + .../renderer/esp/EntityEspRegistry.kt | 139 ------------------ .../renderer/esp/EntityEspRenderer.kt | 127 ++++++++++++++++ .../lambda/module/modules/debug/RenderTest.kt | 8 +- 5 files changed, 162 insertions(+), 145 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRenderer.kt diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index b1776b7e7..64531e2e5 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -4,14 +4,41 @@ import com.lambda.Lambda.mc import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.EntityEspRenderer import com.lambda.util.math.Vec2d -import net.minecraft.entity.LivingEntity -import net.minecraft.util.math.BlockPos +import net.minecraft.entity.Entity import java.awt.Color abstract class RenderEvent : Event { class World : RenderEvent() + class EntityESP : RenderEvent() { + fun build( + entity: Entity, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR + ) { + buildFilled(entity, filledColor, sides) + buildOutline(entity, outlineColor, sides, outlineMode) + } + + fun buildFilled( + entity: Entity, + color: Color, + sides: Int = DirectionMask.ALL + ) = EntityEspRenderer.buildFilled(entity, color, sides) + + fun buildOutline( + entity: Entity, + color: Color, + sides: Int = DirectionMask.ALL, + outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR + ) = EntityEspRenderer.buildOutline(entity, color, sides, outlineMode) + } + abstract class GUI(val scale: Double) : RenderEvent() { class Scaled(scaleFactor: Double) : GUI(scaleFactor) class Fixed : GUI(1.0) diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 24594a991..6ef093cb4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -7,6 +7,7 @@ import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate +import com.lambda.graphics.renderer.esp.EntityEspRenderer import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.Vec2d import com.mojang.blaze3d.systems.RenderSystem.getProjectionMatrix @@ -39,6 +40,7 @@ object RenderMain { setupGL { RenderEvent.World().post() + EntityEspRenderer.render() } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt deleted file mode 100644 index 8be710f1e..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRegistry.kt +++ /dev/null @@ -1,139 +0,0 @@ -package com.lambda.graphics.renderer.esp - -import com.lambda.context.SafeContext -import com.lambda.event.events.RenderEvent -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.graphics.buffer.vao.VAO -import com.lambda.graphics.buffer.vao.vertex.VertexAttrib -import com.lambda.graphics.buffer.vao.vertex.VertexMode -import com.lambda.graphics.renderer.esp.DirectionMask.DOWN -import com.lambda.graphics.renderer.esp.DirectionMask.EAST -import com.lambda.graphics.renderer.esp.DirectionMask.NORTH -import com.lambda.graphics.renderer.esp.DirectionMask.SOUTH -import com.lambda.graphics.renderer.esp.DirectionMask.UP -import com.lambda.graphics.renderer.esp.DirectionMask.WEST -import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection -import com.lambda.graphics.shader.Shader -import com.lambda.util.primitives.extension.max -import com.lambda.util.primitives.extension.min -import com.lambda.util.primitives.extension.partialTicks -import com.lambda.util.primitives.extension.prevPos -import net.minecraft.entity.Entity -import net.minecraft.util.math.Box -import java.awt.Color - -object EntityEspRegistry { - private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.DYNAMIC_RENDERER) - private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.DYNAMIC_RENDERER) - private val shader = Shader("renderer/pos_color", "renderer/box_dynamic") - - fun renderEntityESP(owner: Any, block: SafeContext.(Renderer) -> Unit) { - owner.listener { - block(Renderer) - } - } - - object Renderer { - fun build(entity: Entity, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) { - buildFilled(entity, filledColor, sides) - buildOutline(entity, outlineColor, sides, outlineMode) - } - - fun buildFilled(entity: Entity, color: Color, sides: Int = DirectionMask.ALL) = filled.use { - val box = entity.boundingBox - - val delta = entity.prevPos.subtract(entity.pos) - val prevBox = Box(box.min.add(delta), box.max.add(delta)) - - val pos11 = prevBox.min - val pos12 = prevBox.max - val pos21 = box.min - val pos22 = box.max - - grow(8) - - val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } - val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } - val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } - val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } - val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } - val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } - val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } - val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } - - if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb) - if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) - if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb) - if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf) - if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf) - if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb) - } - - fun buildOutline(entity: Entity, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use { - val box = entity.boundingBox - - val delta = entity.prevPos.subtract(entity.pos) - val prevBox = Box(box.min.add(delta), box.max.add(delta)) - - val pos11 = prevBox.min - val pos12 = prevBox.max - val pos21 = box.min - val pos22 = box.max - - grow(8) - - val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } - val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } - val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } - val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } - val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } - val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } - val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } - val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } - - val hasEast = sides.hasDirection(EAST) - val hasWest = sides.hasDirection(WEST) - val hasUp = sides.hasDirection(UP) - val hasDown = sides.hasDirection(DOWN) - val hasSouth = sides.hasDirection(SOUTH) - val hasNorth = sides.hasDirection(NORTH) - - if (outlineMode.check(hasUp, hasNorth)) putLine(tlb, trb) - if (outlineMode.check(hasUp, hasSouth)) putLine(tlf, trf) - if (outlineMode.check(hasUp, hasWest)) putLine(tlb, tlf) - if (outlineMode.check(hasUp, hasEast)) putLine(trf, trb) - - if (outlineMode.check(hasDown, hasNorth)) putLine(blb, brb) - if (outlineMode.check(hasDown, hasSouth)) putLine(blf, brf) - if (outlineMode.check(hasDown, hasWest)) putLine(blb, blf) - if (outlineMode.check(hasDown, hasEast)) putLine(brb, brf) - - if (outlineMode.check(hasWest, hasNorth)) putLine(tlb, blb) - if (outlineMode.check(hasNorth, hasEast)) putLine(trb, brb) - if (outlineMode.check(hasEast, hasSouth)) putLine(trf, brf) - if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) - } - } - - init { - listener(Int.MAX_VALUE) { - filled.clear() - outline.clear() - } - - listener(Int.MIN_VALUE) { - filled.upload() - outline.upload() - } - - listener { - shader.use() - shader["u_TickDelta"] = mc.partialTicks - shader["u_CameraPosition"] = mc.gameRenderer.camera.pos - - filled.render() - outline.render() - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRenderer.kt new file mode 100644 index 000000000..2c4f3adba --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EntityEspRenderer.kt @@ -0,0 +1,127 @@ +package com.lambda.graphics.renderer.esp + +import com.lambda.Lambda.mc +import com.lambda.event.EventFlow.post +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.esp.DirectionMask.DOWN +import com.lambda.graphics.renderer.esp.DirectionMask.EAST +import com.lambda.graphics.renderer.esp.DirectionMask.NORTH +import com.lambda.graphics.renderer.esp.DirectionMask.SOUTH +import com.lambda.graphics.renderer.esp.DirectionMask.UP +import com.lambda.graphics.renderer.esp.DirectionMask.WEST +import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection +import com.lambda.graphics.shader.Shader +import com.lambda.util.primitives.extension.max +import com.lambda.util.primitives.extension.min +import com.lambda.util.primitives.extension.partialTicks +import com.lambda.util.primitives.extension.prevPos +import net.minecraft.entity.Entity +import net.minecraft.util.math.Box +import java.awt.Color + +object EntityEspRenderer { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.DYNAMIC_RENDERER) + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.DYNAMIC_RENDERER) + private val shader = Shader("renderer/pos_color", "renderer/box_dynamic") + + fun buildFilled(entity: Entity, color: Color, sides: Int) = filled.use { + val box = entity.boundingBox + + val delta = entity.prevPos.subtract(entity.pos) + val prevBox = Box(box.min.add(delta), box.max.add(delta)) + + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + grow(8) + + val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } + val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } + val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } + val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } + val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } + val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } + val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } + val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } + + if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf) + if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf) + if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb) + } + + fun buildOutline(entity: Entity, color: Color, sides: Int, outlineMode: DirectionMask.OutlineMode) = outline.use { + val box = entity.boundingBox + + val delta = entity.prevPos.subtract(entity.pos) + val prevBox = Box(box.min.add(delta), box.max.add(delta)) + + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + grow(8) + + val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } + val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } + val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } + val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } + val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } + val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } + val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } + val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } + + val hasEast = sides.hasDirection(EAST) + val hasWest = sides.hasDirection(WEST) + val hasUp = sides.hasDirection(UP) + val hasDown = sides.hasDirection(DOWN) + val hasSouth = sides.hasDirection(SOUTH) + val hasNorth = sides.hasDirection(NORTH) + + if (outlineMode.check(hasUp, hasNorth)) putLine(tlb, trb) + if (outlineMode.check(hasUp, hasSouth)) putLine(tlf, trf) + if (outlineMode.check(hasUp, hasWest)) putLine(tlb, tlf) + if (outlineMode.check(hasUp, hasEast)) putLine(trf, trb) + + if (outlineMode.check(hasDown, hasNorth)) putLine(blb, brb) + if (outlineMode.check(hasDown, hasSouth)) putLine(blf, brf) + if (outlineMode.check(hasDown, hasWest)) putLine(blb, blf) + if (outlineMode.check(hasDown, hasEast)) putLine(brb, brf) + + if (outlineMode.check(hasWest, hasNorth)) putLine(tlb, blb) + if (outlineMode.check(hasNorth, hasEast)) putLine(trb, brb) + if (outlineMode.check(hasEast, hasSouth)) putLine(trf, brf) + if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) + } + + fun render() { + shader.use() + shader["u_TickDelta"] = mc.partialTicks + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + + filled.render() + outline.render() + } + + init { + listener { + filled.clear() + outline.clear() + + RenderEvent.EntityESP().post() + + filled.upload() + outline.upload() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index abfac53a0..ddaef8848 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -1,9 +1,9 @@ package com.lambda.module.modules.debug +import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.CachedEspRenderer -import com.lambda.graphics.renderer.esp.EntityEspRegistry.renderEntityESP import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.ColorUtils.setAlpha @@ -30,9 +30,9 @@ object RenderTest : Module( private val rendeer = CachedEspRenderer(this) init { - renderEntityESP(this) { renderer -> - val entity = getClosestEntity(player.pos, 8.0) ?: return@renderEntityESP - renderer.build(entity, filledColor, outlineColor) + listener { + val entity = getClosestEntity(player.pos, 8.0) ?: return@listener + it.build(entity, filledColor, outlineColor) } listener { From 78b157fce6e90e096c4db45865430567101729fa Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 8 Jun 2024 10:55:15 +0300 Subject: [PATCH 21/42] Camera Noclip: Added module state check --- common/src/main/java/com/lambda/mixin/render/CameraMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java index 108af5ef5..393adc7d6 100644 --- a/common/src/main/java/com/lambda/mixin/render/CameraMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/CameraMixin.java @@ -42,7 +42,7 @@ private void injectQuickPerspectiveSwap(BlockView area, Entity focusedEntity, bo @Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true) private void onClipToSpace(double desiredCameraDistance, CallbackInfoReturnable info) { - if (CameraTweaks.getNoClipCam()) { + if (CameraTweaks.INSTANCE.isEnabled() && CameraTweaks.getNoClipCam()) { info.setReturnValue(desiredCameraDistance); } } From 4d4ec6a532ead8619d0415812dff55d68ccac814 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 8 Jun 2024 17:47:31 +0300 Subject: [PATCH 22/42] Timer refactor --- .../main/kotlin/com/lambda/module/modules/movement/Timer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt index 5aa0670fc..4168bc82f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt @@ -10,11 +10,11 @@ object Timer : Module( description = "Modify client tick speed.", defaultTags = setOf(ModuleTag.MOVEMENT, ModuleTag.WORLD) ) { - private val timer by setting("Timer", 50, 0..1000, 5, unit = "ms/tick") + private val timer by setting("Timer", 1.0, 0.0..10.0, 0.01) init { listener { - it.speed = timer / 50.0 + it.speed = timer.coerceAtLeast(0.05) } } } \ No newline at end of file From 5d745593789c84ff607737a56c0f08d0c3a65f64 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 9 Jun 2024 16:20:56 +0300 Subject: [PATCH 23/42] HUD & many cgui refactorings --- .../serializer/gui/TagWindowSerializer.kt | 11 ++- .../settings/collections/ListSetting.kt | 1 - .../src/main/kotlin/com/lambda/core/Loader.kt | 2 + .../com/lambda/event/events/RenderEvent.kt | 2 + .../kotlin/com/lambda/graphics/RenderMain.kt | 7 +- .../com/lambda/gui/AbstractGuiConfigurable.kt | 27 +++++++ .../kotlin/com/lambda/gui/GuiConfigurable.kt | 32 +------- .../com/lambda/gui/HudGuiConfigurable.kt | 8 ++ .../kotlin/com/lambda/gui/api/LambdaGui.kt | 67 ++++++++++------- .../lambda/gui/api/component/ListWindow.kt | 2 +- .../gui/api/component/WindowComponent.kt | 10 +-- .../api/component/button/ButtonComponent.kt | 8 +- .../impl/{clickgui => }/AbstractClickGui.kt | 55 ++++++++++++-- .../gui/impl/clickgui/LambdaClickGui.kt | 75 ++++++++++++++++--- .../clickgui/buttons/setting/BindButton.kt | 2 +- .../clickgui/buttons/setting/NumberSlider.kt | 3 +- .../clickgui/buttons/setting/StringButton.kt | 2 +- .../gui/impl/clickgui/windows/ModuleWindow.kt | 12 +-- .../windows/tag/CustomModuleWindow.kt | 2 +- .../impl/clickgui/windows/tag/TagWindow.kt | 2 +- .../lambda/gui/impl/hudgui/LambdaHudGui.kt | 55 ++++++++++++++ .../kotlin/com/lambda/module/HudModule.kt | 43 +++++++++++ .../com/lambda/module/ModuleRegistry.kt | 2 +- .../com/lambda/module/hud/TestHudModule.kt | 19 +++++ .../lambda/module/modules/client/ClickGui.kt | 4 + .../kotlin/com/lambda/module/tag/ModuleTag.kt | 6 +- 26 files changed, 352 insertions(+), 107 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/HudGuiConfigurable.kt rename common/src/main/kotlin/com/lambda/gui/impl/{clickgui => }/AbstractClickGui.kt (54%) create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/hudgui/LambdaHudGui.kt create mode 100644 common/src/main/kotlin/com/lambda/module/HudModule.kt create mode 100644 common/src/main/kotlin/com/lambda/module/hud/TestHudModule.kt diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt index e9a3731a3..45b288cad 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -3,6 +3,7 @@ package com.lambda.config.serializer.gui import com.google.gson.* import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.windows.tag.TagWindow +import com.lambda.gui.impl.hudgui.LambdaHudGui import com.lambda.module.tag.ModuleTag import com.lambda.util.math.Vec2d import java.lang.reflect.Type @@ -30,7 +31,15 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer LambdaClickGui + in ModuleTag.hudDefaults -> LambdaHudGui + else -> return@let null + } + + TagWindow(tag, gui).apply { width = it["width"].asDouble height = it["height"].asDouble isOpen = it["isOpen"].asBoolean diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index 2d7d7494d..424a35007 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -21,7 +21,6 @@ class ListSetting( } override fun toJson(): JsonElement { - value = defaultValue // Hack the Delegates.observable return gson.toJsonTree(value) } } diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 5ce9fbade..533360a56 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -7,6 +7,7 @@ import com.lambda.friend.FriendRegistry import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.graphics.renderer.gui.font.LambdaMoji import com.lambda.gui.GuiConfigurable +import com.lambda.gui.HudGuiConfigurable import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry @@ -23,6 +24,7 @@ object Loader { LambdaFont.Loader, LambdaMoji.Loader, GuiConfigurable, + HudGuiConfigurable, FriendRegistry, SoundRegistry, ) diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index 64531e2e5..0b4bb423a 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -40,6 +40,8 @@ abstract class RenderEvent : Event { } abstract class GUI(val scale: Double) : RenderEvent() { + class HUD(scaleFactor: Double) : GUI(scaleFactor) + class Scaled(scaleFactor: Double) : GUI(scaleFactor) class Fixed : GUI(1.0) diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 6ef093cb4..e7dd001f0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -25,11 +25,12 @@ object RenderMain { translate(0.0, 0.0, -3000.0) setupGL { - rescale(GuiSettings.scale) - RenderEvent.GUI.Scaled(GuiSettings.scale).post() - rescale(1.0) RenderEvent.GUI.Fixed().post() + + rescale(GuiSettings.scale) + RenderEvent.GUI.HUD(GuiSettings.scale).post() + RenderEvent.GUI.Scaled(GuiSettings.scale).post() } } diff --git a/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt new file mode 100644 index 000000000..878b57ee9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/AbstractGuiConfigurable.kt @@ -0,0 +1,27 @@ +package com.lambda.gui + +import com.lambda.config.Configurable +import com.lambda.config.configurations.GuiConfig +import com.lambda.core.Loadable +import com.lambda.gui.impl.AbstractClickGui +import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow +import com.lambda.gui.impl.clickgui.windows.tag.TagWindow +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.Vec2d + +abstract class AbstractGuiConfigurable( + private val ownerGui: AbstractClickGui, + private val tags: Set, + override val name: String +) : Configurable(GuiConfig), Loadable { + var mainWindows by setting("windows", defaultWindows) + open var customWindows = mutableListOf() + + private val defaultWindows get() = + tags.mapIndexed { index, tag -> + TagWindow(tag, ownerGui).apply { + val step = 5.0 + position = Vec2d((width + step) * index, 0.0) + step + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt index 1266d531a..81c09a471 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt @@ -1,35 +1,11 @@ package com.lambda.gui -import com.lambda.config.Configurable -import com.lambda.config.configurations.GuiConfig -import com.lambda.core.Loadable import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow -import com.lambda.gui.impl.clickgui.windows.tag.TagWindow import com.lambda.module.tag.ModuleTag -import com.lambda.util.math.Vec2d -object GuiConfigurable : Configurable(GuiConfig), Loadable { - override val name = "gui" - private val ownerGui = LambdaClickGui - - val mainWindows by setting("windows", defaultWindows).apply { - onValueChange { _, _ -> - ownerGui.updateWindows() - } - } - - val customWindows by setting("custom windows", listOf()).apply { - onValueChange { _, _ -> - ownerGui.updateWindows() - } - } - - private val defaultWindows get() = - ModuleTag.defaults.mapIndexed { index, tag -> - TagWindow(tag, ownerGui).apply { - val step = 5.0 - position = Vec2d((width + step) * index, 0.0) + step - } - } +object GuiConfigurable : AbstractGuiConfigurable( + LambdaClickGui, ModuleTag.defaults, "gui" +) { + override var customWindows by setting("custom windows", listOf()) } diff --git a/common/src/main/kotlin/com/lambda/gui/HudGuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/HudGuiConfigurable.kt new file mode 100644 index 000000000..d69ac70d2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/HudGuiConfigurable.kt @@ -0,0 +1,8 @@ +package com.lambda.gui + +import com.lambda.gui.impl.hudgui.LambdaHudGui +import com.lambda.module.tag.ModuleTag + +object HudGuiConfigurable : AbstractGuiConfigurable( + LambdaHudGui, ModuleTag.hudDefaults, "hudgui" +) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index 542ab0f0e..1e4126e4e 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -1,12 +1,13 @@ package com.lambda.gui.api import com.lambda.Lambda.mc -import com.lambda.event.EventFlow.syncListeners +import com.lambda.event.Muteable import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent -import com.lambda.event.listener.UnsafeListener +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.animation.AnimationTicker import com.lambda.gui.api.component.core.IComponent +import com.lambda.gui.impl.AbstractClickGui import com.lambda.module.Module import com.lambda.util.KeyCode import com.lambda.util.Mouse @@ -18,27 +19,29 @@ import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.screen.Screen import net.minecraft.text.Text -@Suppress("LeakingThis") abstract class LambdaGui( override val name: String, private val owner: Module? = null -) : Screen(Text.of(name)), IComponent, Nameable { - private var screenSize = Vec2d.ZERO +) : Screen(Text.of(name)), IComponent, Nameable, Muteable { + protected var screenSize = Vec2d.ZERO override val rect get() = Rect(Vec2d.ZERO, screenSize) val isOpen get() = mc.currentScreen == this + override val isMuted: Boolean get() = !isOpen + private var closingAction: (() -> Unit)? = null val animation = AnimationTicker() - private val renderListener = UnsafeListener(0, this, false) { event -> - event as RenderEvent.GUI.Scaled - screenSize = event.screenSize - onEvent(GuiEvent.Render()) - } + init { + listener { event -> + screenSize = event.screenSize + onEvent(GuiEvent.Render()) + } - private val tickListener = UnsafeListener(0, this, false) { - animation.tick() - onEvent(GuiEvent.Tick()) + listener { + animation.tick() + onEvent(GuiEvent.Tick()) + } } /** @@ -47,24 +50,33 @@ abstract class LambdaGui( * No safe context required (TODO: let user open clickgui via main menu) */ fun show() { + owner?.enable() if (isOpen) return - mc.currentScreen?.close() - recordRenderCall { // wait for the previous screen to be closed - mc.setScreen(this) + when (val screen = mc.currentScreen) { + is AbstractClickGui -> { + screen.close() + + screen.setCloseTask { + mc.setScreen(this) + } + } + + else -> { + screen?.close() + + recordRenderCall { + mc.setScreen(this) + } + } } } final override fun onDisplayed() { onEvent(GuiEvent.Show()) - - with(syncListeners) { - subscribe(renderListener) - subscribe(tickListener) - } } - final override fun removed() { + override fun removed() { onEvent(GuiEvent.Hide()) // quick crashfix (is there any other way to prevent gui being closed twice?) @@ -72,12 +84,16 @@ abstract class LambdaGui( owner?.disable() mc.currentScreen = this - with(syncListeners) { - unsubscribe(renderListener) - unsubscribe(tickListener) + closingAction?.let { + recordRenderCall(it) + closingAction = null } } + fun setCloseTask(block: () -> Unit) { + closingAction = block + } + final override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { // Let's remove background tint } @@ -93,7 +109,6 @@ abstract class LambdaGui( return true } - final override fun charTyped(chr: Char, modifiers: Int): Boolean { onEvent(GuiEvent.CharTyped(chr)) return true diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt b/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt index 5f094779a..a8fff98b1 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt @@ -2,7 +2,7 @@ package com.lambda.gui.api.component import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.button.ListButton -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui abstract class ListWindow ( owner: AbstractClickGui diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 39c83e5af..1a3f07566 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -6,7 +6,7 @@ import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.gui.api.component.core.list.ChildLayer import com.lambda.gui.api.RenderLayer -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.module.modules.client.ClickGui import com.lambda.module.modules.client.GuiSettings import com.lambda.util.Mouse @@ -145,12 +145,4 @@ abstract class WindowComponent ( } } } - - fun destroy() { - gui.apply { - scheduleAction { - windows.children.remove(this@WindowComponent) - } - } - } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/button/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/button/ButtonComponent.kt index bed55691f..6092f58f1 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/button/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/button/ButtonComponent.kt @@ -23,6 +23,8 @@ abstract class ButtonComponent( abstract val text: String protected open val textColor get() = lerp(Color.WHITE, GuiSettings.mainColor, activeAnimation).multAlpha(showAnimation) + protected open val centerText = false + protected abstract var activeAnimation: Double private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.rect.size.x else size.x, size.y) @@ -69,12 +71,14 @@ abstract class ButtonComponent( ) // Text + val textScale = 1.0 - pressAnimation * 0.08 val textX = ClickGui.windowPadding + interactAnimation + hoverFontAnimation + val textXCentered = rect.size.x * 0.5 - renderer.font.getWidth(text, textScale) * 0.5 renderer.font.build( text = text, - position = Vec2d(rect.left + textX, rect.center.y), + position = Vec2d(rect.left + if (!centerText) textX else textXCentered, rect.center.y), color = textColor, - scale = 1.0 - pressAnimation * 0.08 + scale = textScale ) } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt similarity index 54% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt rename to common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt index 984a1e66c..2696da2d4 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt @@ -1,29 +1,39 @@ -package com.lambda.gui.impl.clickgui +package com.lambda.gui.impl import com.lambda.Lambda.mc import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.AbstractGuiConfigurable import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.ChildLayer import com.lambda.gui.impl.clickgui.buttons.SettingButton import com.lambda.gui.impl.clickgui.windows.ModuleWindow +import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow +import com.lambda.gui.impl.clickgui.windows.tag.TagWindow +import com.lambda.gui.impl.hudgui.LambdaHudGui +import com.lambda.module.Module import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall -abstract class AbstractClickGui(name: String = "ClickGui") : LambdaGui(name, ClickGui) { - private var activeWindow: WindowComponent<*>? = null - private var closing = false +abstract class AbstractClickGui(name: String, owner: Module? = null) : LambdaGui(name, owner) { + protected var hoveredWindow: WindowComponent<*>? = null + protected var closing = false final override var childShowAnimation by animation.exp(0.0, 1.0, { if (closing) ClickGui.closeSpeed else ClickGui.openSpeed }) { !closing }; private set val windows = ChildLayer, AbstractClickGui>(this, this, ::rect) { child -> - child == activeWindow && !closing + child == hoveredWindow && !closing } + abstract val moduleFilter: (Module) -> Boolean + abstract val configurable: AbstractGuiConfigurable + + private var lastTickedUpdate = 0L + private val actionPool = ArrayDeque<() -> Unit>() fun scheduleAction(block: () -> Unit) = actionPool.add(block) @@ -32,21 +42,28 @@ abstract class AbstractClickGui(name: String = "ClickGui") : LambdaGui(name, Cli when (e) { is GuiEvent.Show -> { - activeWindow = null + hoveredWindow = null closing = false childShowAnimation = 0.0 + updateWindows() } is GuiEvent.Tick -> { + val time = System.currentTimeMillis() + if (time - lastTickedUpdate > 1000L) { + lastTickedUpdate = time + updateWindows() + } + if (closing && childShowAnimation < 0.01) mc.setScreen(null) } is GuiEvent.MouseClick -> { - if (e.action == Mouse.Action.Click) activeWindow?.focus() + if (e.action == Mouse.Action.Click) hoveredWindow?.focus() } is GuiEvent.MouseMove -> { - activeWindow = windows.children.lastOrNull { child -> + hoveredWindow = windows.children.lastOrNull { child -> e.mouse in child.rect } } @@ -70,6 +87,28 @@ abstract class AbstractClickGui(name: String = "ClickGui") : LambdaGui(name, Cli } } + private inline fun syncWindows(configWindows: MutableList) = windows.apply { + // Add windows from config + configWindows.filter { it !in children }.forEach(children::add) + + // Remove outdated/deleted windows + children.removeIf { + it is T && it !in configWindows + } + + // Update config + configWindows.clear() + configWindows.addAll(children.filterIsInstance()) + } + + fun updateWindows() { + syncWindows(configurable.mainWindows) + + if (this != LambdaHudGui) { + syncWindows(configurable.customWindows) + } + } + override fun close() { if (!isOpen) return closing = true diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index 2fbad35ad..38fe0c278 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -2,22 +2,75 @@ package com.lambda.gui.impl.clickgui import com.lambda.gui.GuiConfigurable import com.lambda.gui.api.GuiEvent -import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow +import com.lambda.gui.api.RenderLayer +import com.lambda.gui.api.component.button.ButtonComponent +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.impl.AbstractClickGui +import com.lambda.gui.impl.hudgui.LambdaHudGui +import com.lambda.module.HudModule +import com.lambda.module.Module +import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.Vec2d + +object LambdaClickGui : AbstractClickGui("ClickGui", ClickGui) { + override val moduleFilter: (Module) -> Boolean = { + it !is HudModule + } + + override val configurable get() = GuiConfigurable + + private val buttonRenderer = RenderLayer() + private val buttons = ChildLayer.Drawable(this, this, buttonRenderer, ::rect) { + hoveredWindow == null && !closing + } -object LambdaClickGui : AbstractClickGui() { override fun onEvent(e: GuiEvent) { - if (e is GuiEvent.Show || e is GuiEvent.Tick) updateWindows() + buttons.onEvent(e) + if (e is GuiEvent.Render) buttonRenderer.render() + super.onEvent(e) } - fun updateWindows() { - windows.apply { - val windows = GuiConfigurable.mainWindows + GuiConfigurable.customWindows - windows.subtract(children.toSet()).forEach(::showWindow) + init { + buttons.children.add(object : ButtonComponent(buttons) { + override val position: Vec2d get() = screenSize - size - Vec2d.ONE * 5.0 + override val size = Vec2d(30.0, 15.0) + override val text = "HUD" + override val centerText = true + + override var activeAnimation; get() = pressAnimation; set(_) {} + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + + if (e is GuiEvent.Render) { + val rect = rect.shrink(interactAnimation) + + // Background + renderer.filled.build( + rect = rect, + roundRadius = ClickGui.windowRadius, + color = GuiSettings.backgroundColor.multAlpha(childShowAnimation), + shade = GuiSettings.shadeBackground + ) + + // Outline + renderer.outline.build( + rect = rect, + roundRadius = ClickGui.windowRadius, + innerGlow = ClickGui.windowRadius.coerceAtMost(1.0), + outerGlow = ClickGui.windowRadius, + color = GuiSettings.mainColor.multAlpha(childShowAnimation), + shade = GuiSettings.shadeBackground + ) + } + } - children.filter { it !in windows && it is CustomModuleWindow } - .forEach(WindowComponent<*>::destroy) - } + override fun performClickAction(e: GuiEvent.MouseClick) { + LambdaHudGui.show() + } + }) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BindButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BindButton.kt index 9980bc06a..929887a49 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BindButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BindButton.kt @@ -4,7 +4,7 @@ import com.lambda.config.settings.complex.KeyBindSetting import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.button.InputBarOverlay import com.lambda.gui.api.component.core.list.ChildLayer -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.gui.impl.clickgui.buttons.SettingButton import com.lambda.util.KeyCode diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt index e00065ec0..edbbc125b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt @@ -4,7 +4,7 @@ import com.lambda.config.settings.NumericSetting import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.button.InputBarOverlay import com.lambda.gui.api.component.core.list.ChildLayer -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.gui.impl.clickgui.buttons.SettingButton import com.lambda.util.Mouse @@ -12,7 +12,6 @@ import com.lambda.util.math.ColorUtils.multAlpha import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.math.MathUtils.typeConvert -import com.lambda.util.math.Vec2d import com.lambda.util.math.normalize class NumberSlider ( diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/StringButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/StringButton.kt index 6c5d88ea9..a6b32a699 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/StringButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/StringButton.kt @@ -4,7 +4,7 @@ import com.lambda.config.settings.StringSetting import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.button.InputBarOverlay import com.lambda.gui.api.component.core.list.ChildLayer -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.gui.impl.clickgui.buttons.SettingButton import com.lambda.util.math.ColorUtils.multAlpha diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/ModuleWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/ModuleWindow.kt index 76a846884..2bf9e14ad 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/ModuleWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/ModuleWindow.kt @@ -2,7 +2,7 @@ package com.lambda.gui.impl.clickgui.windows import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.ListWindow -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.module.Module @@ -22,7 +22,7 @@ abstract class ModuleWindow( lastUpdate = time contentComponents.apply { - val modules = getModuleList() + val modules = getModuleList().filter((gui as AbstractClickGui).moduleFilter) // Add missing module buttons modules.filter { module -> @@ -33,12 +33,8 @@ abstract class ModuleWindow( .forEach(contentComponents.children::add) // Remove deleted modules - children.forEach { button -> - if (button.module !in modules) { - this@ModuleWindow.gui.scheduleAction { - children.remove(button) - } - } + children.removeIf { + it.module !in modules } } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/CustomModuleWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/CustomModuleWindow.kt index 7d6103ea7..a967ca61d 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/CustomModuleWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/CustomModuleWindow.kt @@ -1,6 +1,6 @@ package com.lambda.gui.impl.clickgui.windows.tag -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.windows.ModuleWindow import com.lambda.module.Module diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt index a418ba53e..c651a8f78 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt @@ -1,6 +1,6 @@ package com.lambda.gui.impl.clickgui.windows.tag -import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.AbstractClickGui import com.lambda.gui.impl.clickgui.windows.ModuleWindow import com.lambda.module.ModuleRegistry import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/gui/impl/hudgui/LambdaHudGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/hudgui/LambdaHudGui.kt new file mode 100644 index 000000000..83484eec5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/hudgui/LambdaHudGui.kt @@ -0,0 +1,55 @@ +package com.lambda.gui.impl.hudgui + +import com.lambda.gui.HudGuiConfigurable +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.impl.AbstractClickGui +import com.lambda.gui.impl.clickgui.LambdaClickGui +import com.lambda.module.HudModule +import com.lambda.module.Module +import com.lambda.module.ModuleRegistry +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +object LambdaHudGui : AbstractClickGui("HudGui") { + override val moduleFilter: (Module) -> Boolean = { + it is HudModule + } + + override val configurable get() = HudGuiConfigurable + private val hudModules get() = ModuleRegistry.modules.filterIsInstance() + + private var dragInfo: Pair? = null + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + + when (e) { + is GuiEvent.Show -> { + dragInfo = null + + setCloseTask { + LambdaClickGui.show() + } + } + + is GuiEvent.MouseMove -> { + if (closing) dragInfo = null + + dragInfo?.let { + it.second.position = e.mouse - it.first + } + } + + is GuiEvent.MouseClick -> { + dragInfo = null + + if (hoveredWindow == null && + e.action == Mouse.Action.Click && + e.button == Mouse.Button.Left + ) hudModules.firstOrNull { e.mouse in it.rect }?.let { + dragInfo = e.mouse - it.position to it + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt new file mode 100644 index 000000000..8c2af2c35 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -0,0 +1,43 @@ +package com.lambda.module + +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.gui.api.RenderLayer +import com.lambda.module.tag.ModuleTag +import com.lambda.util.KeyCode +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d + +abstract class HudModule( + name: String, + description: String = "", + alwaysListening: Boolean = false, + enabledByDefault: Boolean = false, + defaultKeybind: KeyCode = KeyCode.UNBOUND, +) : Module(name, description, setOf(ModuleTag.HUD), alwaysListening, enabledByDefault, defaultKeybind) { + private val renderCallables = mutableListOf Unit>() + + protected abstract val width: Double + protected abstract val height: Double + + private var px by setting("Position X", 10.0, 0.0..10000.0, 1.0) { false } + private var py by setting("Position Y", 10.0, 0.0..10000.0, 1.0) { false } + + var position get() = Vec2d(px, py); set(value) { px = value.x; py = value.y } + val rect get() = Rect.basedOn(position, width, height) + + private val renderer = RenderLayer() + + protected fun onRender(block: RenderLayer.() -> Unit) = + renderCallables.add(block) + + init { + listener { event -> + renderCallables.forEach { function -> + function.invoke(renderer) + } + + renderer.render() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/ModuleRegistry.kt b/common/src/main/kotlin/com/lambda/module/ModuleRegistry.kt index a15834c10..262626365 100644 --- a/common/src/main/kotlin/com/lambda/module/ModuleRegistry.kt +++ b/common/src/main/kotlin/com/lambda/module/ModuleRegistry.kt @@ -17,7 +17,7 @@ object ModuleRegistry : Loadable { val moduleNames: Set get() = modules.map { it.name }.toSet() - private val paths = mutableSetOf("com.lambda.module.modules") + private val paths = mutableSetOf("com.lambda.module.modules", "com.lambda.module.hud") fun injectPath(path: String) = paths.add(path) diff --git a/common/src/main/kotlin/com/lambda/module/hud/TestHudModule.kt b/common/src/main/kotlin/com/lambda/module/hud/TestHudModule.kt new file mode 100644 index 000000000..c1355b932 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/hud/TestHudModule.kt @@ -0,0 +1,19 @@ +package com.lambda.module.hud + +import com.lambda.module.HudModule +import java.awt.Color + +object TestHudModule : HudModule( + "TestHudModule", + enabledByDefault = true +) { + override val width = 100.0 + override val height = 30.0 + + init { + onRender { + filled.build(rect, 2.0, Color(180, 180, 180, 180), true) + outline.build(rect, 2.0, shade = true) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 5b0ea7481..80864eaa7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -1,8 +1,11 @@ package com.lambda.module.modules.client import com.lambda.event.events.ClientEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener import com.lambda.gui.impl.clickgui.LambdaClickGui +import com.lambda.gui.impl.hudgui.LambdaHudGui import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode @@ -30,6 +33,7 @@ object ClickGui : Module( onDisable { LambdaClickGui.close() + LambdaHudGui.close() } unsafeListener { diff --git a/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt b/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt index 502330c02..c60a01a20 100644 --- a/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt +++ b/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt @@ -24,12 +24,14 @@ data class ModuleTag(override val name: String) : Nameable { val CLIENT = ModuleTag("Client") val NETWORK = ModuleTag("Network") val DEBUG = ModuleTag("Debug") + val defaults = setOf(COMBAT, MOVEMENT, RENDER, PLAYER, NETWORK, DEBUG, CLIENT) + + val HUD = ModuleTag("Hud") // omg + val hudDefaults = setOf(HUD) // currently secondary tags val WORLD = ModuleTag("World") val AUTOMATION = ModuleTag("Automation") val GRIM = ModuleTag("Grim") - - val defaults = listOf(COMBAT, MOVEMENT, RENDER, PLAYER, NETWORK, DEBUG, CLIENT) } } From 11237cacccdd6f0736cc81ff7ae202b28b64a1da Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 9 Jun 2024 16:56:56 +0300 Subject: [PATCH 24/42] Clamped position --- .../com/lambda/gui/impl/AbstractClickGui.kt | 7 ------- .../main/kotlin/com/lambda/module/HudModule.kt | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt index 2696da2d4..cd2208426 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/AbstractClickGui.kt @@ -72,13 +72,6 @@ abstract class AbstractClickGui(name: String, owner: Module? = null) : LambdaGui windows.onEvent(e) } - fun showWindow(window: WindowComponent<*>) { - // we have to wait some time to place this window over other ones - recordRenderCall { - windows.children.add(window) - } - } - fun unfocusSettings() { windows.children.filterIsInstance().forEach { moduleWindow -> moduleWindow.contentComponents.children.forEach { moduleButton -> diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 8c2af2c35..1eea9e3b6 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -20,10 +20,11 @@ abstract class HudModule( protected abstract val width: Double protected abstract val height: Double - private var px by setting("Position X", 10.0, 0.0..10000.0, 1.0) { false } - private var py by setting("Position Y", 10.0, 0.0..10000.0, 1.0) { false } + private var px by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } + private var py by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } - var position get() = Vec2d(px, py); set(value) { px = value.x; py = value.y } + private var screenSize = Vec2d.ZERO + var position get() = Vec2d(px, py); set(value) { setPos(value.x, value.y) } val rect get() = Rect.basedOn(position, width, height) private val renderer = RenderLayer() @@ -31,8 +32,18 @@ abstract class HudModule( protected fun onRender(block: RenderLayer.() -> Unit) = renderCallables.add(block) + private fun setPos(x: Double, y: Double) { + val xRange = 0.0..screenSize.x - width + val yRange = 0.0..screenSize.y - height + + px = x.coerceIn(xRange) + py = y.coerceIn(yRange) + } + init { listener { event -> + screenSize = event.screenSize + renderCallables.forEach { function -> function.invoke(renderer) } From 63788649a0b66c0173dac54d39215ff9cbc5e496 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 9 Jun 2024 11:59:59 -0400 Subject: [PATCH 25/42] todo --- common/src/main/kotlin/com/lambda/friend/FriendRegistry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/friend/FriendRegistry.kt b/common/src/main/kotlin/com/lambda/friend/FriendRegistry.kt index 81481e908..3d35caaec 100644 --- a/common/src/main/kotlin/com/lambda/friend/FriendRegistry.kt +++ b/common/src/main/kotlin/com/lambda/friend/FriendRegistry.kt @@ -8,7 +8,7 @@ import com.mojang.authlib.GameProfile object FriendRegistry : Configurable(FriendConfig), Loadable { override val name = "friends" - val friends by setting("friends", listOf()) + val friends by setting("friends", listOf()) // Todo: Fix the fucking delegates override fun load(): String { return "Loaded ${friends.size} friends." From ff99934e1ca85bde130d552ececd2cd0bc149309 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 9 Jun 2024 19:25:20 +0300 Subject: [PATCH 26/42] Fuck it, HUD docking --- .../kotlin/com/lambda/module/HudModule.kt | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 1eea9e3b6..e7424390b 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -19,27 +19,47 @@ abstract class HudModule( protected abstract val width: Double protected abstract val height: Double + private val size get() = Vec2d(width, height) - private var px by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } - private var py by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } + private var relativePosX by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } + private var relativePosY by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } + private var relativePos get() = Vec2d(relativePosX, relativePosY); set(value) { + val vec = absToRelative(relativeToAbs(value)) + relativePosX = vec.x; relativePosY = vec.y + } + + var position get() = relativeToAbs(relativePos).let { + val x = it.x.coerceIn(0.0, screenSize.x - width) + val y = it.y.coerceIn(0.0, screenSize.y - height) + Vec2d(x, y) + }; set(value) { relativePos = absToRelative(value) } + + private fun relativeToAbs(posIn: Vec2d) = posIn + (screenSize - size) * dockingMultiplier + private fun absToRelative(posIn: Vec2d) = posIn - (screenSize - size) * dockingMultiplier + + private val dockingH by setting("Docking H", HAlign.LEFT).apply { + onValueChange { from, to -> + val delta = to.multiplier - from.multiplier + relativePosX += delta * (size.x - screenSize.x) + } + } + private val dockingV by setting("Docking V", VAlign.TOP).apply { + onValueChange { from, to -> + val delta = to.multiplier - from.multiplier + relativePosY += delta * (size.y - screenSize.y) + } + } + + private val dockingMultiplier get() = Vec2d(dockingH.multiplier, dockingV.multiplier) - private var screenSize = Vec2d.ZERO - var position get() = Vec2d(px, py); set(value) { setPos(value.x, value.y) } val rect get() = Rect.basedOn(position, width, height) + private var screenSize = Vec2d.ZERO private val renderer = RenderLayer() protected fun onRender(block: RenderLayer.() -> Unit) = renderCallables.add(block) - private fun setPos(x: Double, y: Double) { - val xRange = 0.0..screenSize.x - width - val yRange = 0.0..screenSize.y - height - - px = x.coerceIn(xRange) - py = y.coerceIn(yRange) - } - init { listener { event -> screenSize = event.screenSize @@ -51,4 +71,18 @@ abstract class HudModule( renderer.render() } } + + @Suppress("UNUSED") + enum class HAlign(val multiplier: Float) { + LEFT(0.0f), + CENTER(0.5f), + RIGHT(1.0f) + } + + @Suppress("UNUSED") + enum class VAlign(val multiplier: Float) { + TOP(0.0f), + CENTER(0.5f), + BOTTOM(1.0f) + } } \ No newline at end of file From 69023d6d690c67182fb848206874948da44a51ec Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:39:17 -0400 Subject: [PATCH 27/42] Fix: Gson type token --- .../com/lambda/config/AbstractSetting.kt | 8 +++++--- .../kotlin/com/lambda/config/Configurable.kt | 1 - .../com/lambda/config/settings/CharSetting.kt | 4 +++- .../lambda/config/settings/NumericSetting.kt | 4 +++- .../lambda/config/settings/StringSetting.kt | 4 +++- .../config/settings/collections/ListSetting.kt | 2 ++ .../config/settings/collections/MapSetting.kt | 18 ++++-------------- .../config/settings/collections/SetSetting.kt | 18 ++++-------------- .../settings/comparable/BooleanSetting.kt | 4 +++- .../config/settings/comparable/EnumSetting.kt | 4 +++- .../config/settings/complex/BlockPosSetting.kt | 4 +++- .../config/settings/complex/BlockSetting.kt | 4 +++- .../config/settings/complex/ColorSetting.kt | 2 ++ .../config/settings/complex/KeyBindSetting.kt | 4 +++- .../com/lambda/module/modules/render/XRay.kt | 6 +++--- 15 files changed, 44 insertions(+), 43 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt b/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt index 2c01cf0df..8afe2baa5 100644 --- a/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt @@ -5,6 +5,7 @@ import com.lambda.Lambda.gson import com.lambda.context.SafeContext import com.lambda.threading.runSafe import com.lambda.util.Nameable +import java.lang.reflect.Type import kotlin.properties.Delegates import kotlin.reflect.KProperty @@ -53,6 +54,7 @@ import kotlin.reflect.KProperty */ abstract class AbstractSetting( private val defaultValue: T, + private val type: Type, val description: String, val visibility: () -> Boolean, ) : Jsonable, Nameable { @@ -74,10 +76,10 @@ abstract class AbstractSetting( } override fun toJson(): JsonElement = - gson.toJsonTree(value) + gson.toJsonTree(value, type) override fun loadFromJson(serialized: JsonElement) { - value = gson.fromJson(serialized, value::class.java) + value = gson.fromJson(serialized, type) } fun onValueChange(block: SafeContext.(from: T, to: T) -> Unit) { @@ -101,4 +103,4 @@ abstract class AbstractSetting( } class ValueListener (val requiresValueChange: Boolean, val execute: (from: T, to: T) -> Unit) -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index f3649052d..88057b475 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -22,7 +22,6 @@ import com.lambda.util.Nameable import net.minecraft.block.Block import net.minecraft.util.math.BlockPos import java.awt.Color -import java.lang.reflect.Type /** * Represents a set of [AbstractSetting]s that are associated with the [name] of the [Configurable]. diff --git a/common/src/main/kotlin/com/lambda/config/settings/CharSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/CharSetting.kt index f4c0e4c2a..f068bc7ac 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/CharSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/CharSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting /** @@ -17,6 +18,7 @@ class CharSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(Char::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt index c4719ad0f..0daa4e999 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import com.lambda.util.math.MathUtils.roundToStep import kotlin.reflect.KProperty @@ -25,6 +26,7 @@ abstract class NumericSetting( val unit: String, ) : AbstractSetting( value, + TypeToken.get(value::class.java).type, description, visibility ) where T : Number, T : Comparable { @@ -33,4 +35,4 @@ abstract class NumericSetting( override operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { value = valueIn.coerceIn(range) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/settings/StringSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/StringSetting.kt index 7c9e45cdf..5bd47e7df 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/StringSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/StringSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting /** @@ -17,6 +18,7 @@ class StringSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(String::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index 424a35007..1c002df86 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -1,6 +1,7 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement +import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting import java.lang.reflect.Type @@ -13,6 +14,7 @@ class ListSetting( visibility: () -> Boolean, ) : AbstractSetting>( defaultValue, + type, description, visibility ) { diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt index 08fb931c3..b8c36c63f 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt @@ -1,27 +1,17 @@ package com.lambda.config.settings.collections -import com.google.gson.JsonElement -import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting import java.lang.reflect.Type class MapSetting( override val name: String, - private val defaultValue: MutableMap, - private val type: Type, + defaultValue: MutableMap, + type: Type, description: String, visibility: () -> Boolean, ) : AbstractSetting>( defaultValue, + type, description, visibility -) { - override fun loadFromJson(serialized: JsonElement) { - value = gson.fromJson(serialized, type) - } - - override fun toJson(): JsonElement { - value = defaultValue // Hack the Delegates.observable - return gson.toJsonTree(value) - } -} +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt index bbce58e21..d4da0afd8 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt @@ -1,27 +1,17 @@ package com.lambda.config.settings.collections -import com.google.gson.JsonElement -import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting import java.lang.reflect.Type class SetSetting( override val name: String, - private val defaultValue: MutableSet, - private val type: Type, + defaultValue: MutableSet, + type: Type, description: String, visibility: () -> Boolean, ) : AbstractSetting>( defaultValue, + type, description, visibility -) { - override fun loadFromJson(serialized: JsonElement) { - value = gson.fromJson(serialized, type) - } - - override fun toJson(): JsonElement { - value = defaultValue // Hack the Delegates.observable - return gson.toJsonTree(value) - } -} +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt index 6ada06326..272680124 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/comparable/BooleanSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.comparable +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting class BooleanSetting( @@ -9,6 +10,7 @@ class BooleanSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(Boolean::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt index b52bef99a..aa48a162b 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/comparable/EnumSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.comparable +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting class EnumSetting>( @@ -9,6 +10,7 @@ class EnumSetting>( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(defaultValue.declaringJavaClass).type, description, visibility, ) { @@ -17,4 +19,4 @@ class EnumSetting>( fun next() { value = enumValues[((value.ordinal + 1) % enumValues.size)] } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt index 46ece0360..7c21776a6 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/complex/BlockPosSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.complex +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import net.minecraft.util.math.BlockPos @@ -10,6 +11,7 @@ class BlockPosSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(BlockPos::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt index 5fc9cd1f6..ac5b9e365 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/complex/BlockSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.complex +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import net.minecraft.block.Block @@ -10,6 +11,7 @@ class BlockSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(Block::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt index 179b88596..caef2e0ae 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/complex/ColorSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.complex +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import java.awt.Color @@ -10,6 +11,7 @@ class ColorSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(Color::class.java).type, description, visibility ) diff --git a/common/src/main/kotlin/com/lambda/config/settings/complex/KeyBindSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/complex/KeyBindSetting.kt index fccd4fec0..df19f8668 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/complex/KeyBindSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/complex/KeyBindSetting.kt @@ -1,5 +1,6 @@ package com.lambda.config.settings.complex +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import com.lambda.util.KeyCode @@ -10,6 +11,7 @@ class KeyBindSetting( visibility: () -> Boolean, ) : AbstractSetting( defaultValue, + TypeToken.get(KeyCode::class.java).type, description, visibility -) \ No newline at end of file +) diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt b/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt index 5d8a67c54..4a1b8b012 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt @@ -23,14 +23,14 @@ object XRay : Module( Blocks.ANCIENT_DEBRIS ) -// private val selection by setting("Block Selection", defaultBlocks, "Block selection that will be shown (whitelist) or hidden (blacklist)") + private val selection by setting("Block Selection", defaultBlocks, "Block selection that will be shown (whitelist) or hidden (blacklist)") private val mode by setting("Selection Mode", Selection.WHITELIST, "The mode of the block selection") @JvmStatic fun isSelected(blockState: BlockState) = mode.select(blockState) enum class Selection(val select: (BlockState) -> Boolean) { - WHITELIST({ it.block in defaultBlocks }), - BLACKLIST({ it.block !in defaultBlocks }) + WHITELIST({ it.block in selection }), + BLACKLIST({ it.block !in selection }) } init { From 44f30d96600df072a496daa63d53268431399721 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 9 Jun 2024 19:39:48 +0300 Subject: [PATCH 28/42] Crashfix when gui gets closed by kicking from the server --- .../main/kotlin/com/lambda/gui/api/LambdaGui.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index 1e4126e4e..c8dad409e 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -9,6 +9,7 @@ import com.lambda.graphics.animation.AnimationTicker import com.lambda.gui.api.component.core.IComponent import com.lambda.gui.impl.AbstractClickGui import com.lambda.module.Module +import com.lambda.threading.runSafe import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.Nameable @@ -79,14 +80,16 @@ abstract class LambdaGui( override fun removed() { onEvent(GuiEvent.Hide()) - // quick crashfix (is there any other way to prevent gui being closed twice?) - mc.currentScreen = null - owner?.disable() - mc.currentScreen = this + runSafe { + // quick crashfix (is there any other way to prevent gui being closed twice?) + mc.currentScreen = null + owner?.disable() + mc.currentScreen = this@LambdaGui - closingAction?.let { - recordRenderCall(it) - closingAction = null + closingAction?.let { + recordRenderCall(it) + closingAction = null + } } } From 9cf861671f02c454cab1d421b4ed9bd6683d2b17 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:39:59 -0400 Subject: [PATCH 29/42] Update AbstractSetting.kt --- common/src/main/kotlin/com/lambda/config/AbstractSetting.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt b/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt index 8afe2baa5..b14f8f5b3 100644 --- a/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/AbstractSetting.kt @@ -50,6 +50,7 @@ import kotlin.reflect.KProperty * * @property defaultValue The default value of the setting. * @property description A description of the setting. + * @property type The type reflection of the setting. * @property visibility A function that determines whether the setting is visible. */ abstract class AbstractSetting( From ce946f2a065902122eaaa2f6dc9fc7745cbbbd68 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Mon, 10 Jun 2024 21:46:21 +0300 Subject: [PATCH 30/42] Shader optimization --- common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt | 3 +-- .../assets/lambda/shaders/vertex/renderer/box_dynamic.vert | 5 ++--- .../assets/lambda/shaders/vertex/renderer/box_static.vert | 6 ++---- .../assets/lambda/shaders/vertex/renderer/font.vert | 5 ++--- .../assets/lambda/shaders/vertex/renderer/rect_filled.vert | 5 ++--- .../assets/lambda/shaders/vertex/renderer/rect_outline.vert | 5 ++--- .../lambda/shaders/vertex/renderer/tracer_dynamic.vert | 5 ++--- .../lambda/shaders/vertex/renderer/tracer_static.vert | 5 ++--- 8 files changed, 15 insertions(+), 24 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt index 69c15e1d5..45cd06ed1 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -28,8 +28,7 @@ class Shader(fragmentPath: String, vertexPath: String) { fun use() { glUseProgram(id) - set("u_Projection", RenderMain.projectionMatrix) - set("u_ModelView", RenderMain.modelViewMatrix) + set("u_ProjModel", Matrix4f(RenderMain.projectionMatrix).mul(RenderMain.modelViewMatrix)) } private fun loc(name: String) = diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert index 7061f3df9..d33b24566 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert @@ -4,8 +4,7 @@ layout (location = 0) in vec3 pos1; layout (location = 1) in vec3 pos2; layout (location = 2) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; uniform float u_TickDelta; uniform vec3 u_CameraPosition; @@ -15,6 +14,6 @@ out vec4 v_Color; #define VERTEX_POSITION mix(pos1, pos2, u_TickDelta) - u_CameraPosition void main() { - gl_Position = u_Projection * u_ModelView * vec4(VERTEX_POSITION, 1.0); + gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); v_Color = color; } diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert index 8ab8e8660..197c14ca5 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert @@ -3,9 +3,7 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; - +uniform mat4 u_ProjModel; uniform vec3 u_CameraPosition; out vec4 v_Color; @@ -13,6 +11,6 @@ out vec4 v_Color; #define VERTEX_POSITION pos - u_CameraPosition void main() { - gl_Position = u_Projection * u_ModelView * vec4(VERTEX_POSITION, 1.0); + gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); v_Color = color; } diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/font.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/font.vert index 96ad268b9..14d1bb437 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/font.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/font.vert @@ -4,14 +4,13 @@ layout (location = 0) in vec4 pos; layout (location = 1) in vec2 uv; layout (location = 2) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; out vec2 v_TexCoord; out vec4 v_Color; void main() { - gl_Position = u_Projection * u_ModelView * pos; + gl_Position = u_ProjModel * pos; v_TexCoord = uv; v_Color = color; diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert index 0e3891429..e52b0bda8 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert @@ -7,8 +7,7 @@ layout (location = 3) in float round; layout (location = 4) in float shade; layout (location = 5) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; out vec2 v_Position; out vec2 v_TexCoord; @@ -18,7 +17,7 @@ out float v_RoundRadius; out float v_Shade; void main() { - gl_Position = u_Projection * u_ModelView * pos; + gl_Position = u_ProjModel * pos; v_Position = gl_Position.xy * 0.5 + 0.5; v_TexCoord = uv; diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert index 192864a29..03650690f 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert @@ -5,8 +5,7 @@ layout (location = 1) in float alpha; layout (location = 2) in float shade; layout (location = 3) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; out vec2 v_Position; out float v_Alpha; @@ -14,7 +13,7 @@ out vec4 v_Color; out float v_Shade; void main() { - gl_Position = u_Projection * u_ModelView * pos; + gl_Position = u_ProjModel * pos; v_Position = gl_Position.xy * 0.5 + 0.5; v_Alpha = alpha; diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert index 2ec823a2e..57c3accbd 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert @@ -4,8 +4,7 @@ layout (location = 0) in vec3 pos1; layout (location = 1) in vec3 pos2; layout (location = 2) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; uniform float u_TickDelta; uniform vec3 u_CameraPosition; @@ -15,7 +14,7 @@ out vec4 v_Color; #define VERTEX_POSITION mix(pos1, pos2, u_TickDelta) - u_CameraPosition #define SCREEN_CENTER vec4(0.0, 0.0, 0.0, 1.0) -#define PROJECTED u_Projection * u_ModelView * vec4(VERTEX_POSITION, 1.0) +#define PROJECTED u_ProjModel * vec4(VERTEX_POSITION, 1.0) void main() { gl_Position = gl_VertexID % 2 == 0 ? SCREEN_CENTER : PROJECTED; diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert index 838117d38..68b534c64 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert @@ -3,8 +3,7 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec4 color; -uniform mat4 u_Projection; -uniform mat4 u_ModelView; +uniform mat4 u_ProjModel; uniform vec3 u_CameraPosition; @@ -13,7 +12,7 @@ out vec4 v_Color; #define VERTEX_POSITION pos - u_CameraPosition #define SCREEN_CENTER vec4(0.0, 0.0, 0.0, 1.0) -#define PROJECTED u_Projection * u_ModelView * vec4(VERTEX_POSITION, 1.0) +#define PROJECTED u_ProjModel * vec4(VERTEX_POSITION, 1.0) void main() { gl_Position = gl_VertexID % 2 == 0 ? SCREEN_CENTER : PROJECTED; From 4f0d71318bddc520c958888ff8108e248e6c94cf Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:43:45 -0400 Subject: [PATCH 31/42] Changed matrices list size --- common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt index 7428cde2c..245d9dc35 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -4,7 +4,7 @@ import org.joml.Matrix4f import org.joml.Quaternionf object Matrices { - private val stack = ArrayDeque(listOf(Matrix4f())) + private val stack = ArrayDeque(1) fun translate(x: Double, y: Double, z: Double) { translate(x.toFloat(), y.toFloat(), z.toFloat()) @@ -51,4 +51,4 @@ object Matrices { stack.clear() stack.add(entry) } -} \ No newline at end of file +} From 968de6a8d2731849877151541577cfbbdf778242 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 12 Jun 2024 21:56:56 +0300 Subject: [PATCH 32/42] ESP --- .../com/lambda/graphics/buffer/vao/VAO.kt | 22 ++- .../graphics/buffer/vao/vertex/BufferUsage.kt | 10 ++ .../com/lambda/graphics/gl/GlStateUtils.kt | 7 +- .../graphics/renderer/esp/BlockEspRenderer.kt | 153 ++++++++++++++++++ .../{CachedEspRenderer.kt => EspRenderer.kt} | 54 +++---- .../lambda/module/modules/debug/RenderTest.kt | 8 +- .../lambda/module/modules/debug/UpdateTest.kt | 27 ---- .../lambda/module/modules/render/BlockESP.kt | 12 ++ 8 files changed, 228 insertions(+), 65 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/BufferUsage.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt rename common/src/main/kotlin/com/lambda/graphics/renderer/esp/{CachedEspRenderer.kt => EspRenderer.kt} (84%) delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/UpdateTest.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 0cd90feb2..ed64c6ec6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -1,5 +1,6 @@ package com.lambda.graphics.buffer.vao +import com.lambda.graphics.buffer.vao.vertex.BufferUsage import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.gl.Memory.address @@ -25,8 +26,9 @@ import java.awt.Color import java.nio.ByteBuffer class VAO( - private val drawMode: VertexMode, + private val vertexMode: VertexMode, attribGroup: VertexAttrib.Group, + private val bufferUsage: BufferUsage = BufferUsage.DYNAMIC ) : IRenderContext { private var vao = 0 private var vbo = 0 @@ -41,19 +43,20 @@ class VAO( private lateinit var indices: ByteBuffer private var indicesPointer = 0L private var indicesCount = 0 + private var uploadedIndices = 0 private var vertexIndex = 0 init { val stride = attribGroup.stride - objectSize = stride * drawMode.indicesCount + objectSize = stride * vertexMode.indicesCount runOnGameThread { vertices = byteBuffer(objectSize * 256 * 4) verticesPointer = address(vertices) verticesPosition = verticesPointer - indices = byteBuffer(drawMode.indicesCount * 512 * 4) + indices = byteBuffer(vertexMode.indicesCount * 512 * 4) indicesPointer = address(indices) vao = glGenVertexArrays() @@ -155,7 +158,7 @@ class VAO( if ((indicesCount + amount) * 4 < cap) return var newSize = cap * 2 - if (newSize % drawMode.indicesCount != 0) newSize += newSize % (drawMode.indicesCount * 4) + if (newSize % vertexMode.indicesCount != 0) newSize += newSize % (vertexMode.indicesCount * 4) val newIndices = byteBuffer(newSize) val from = address(indices) @@ -167,10 +170,10 @@ class VAO( } override fun render() { - if (indicesCount <= 0) return + if (uploadedIndices <= 0) return bindVertexArray(vao) - glDrawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT, 0) + glDrawElements(vertexMode.gl, uploadedIndices, GL_UNSIGNED_INT, 0) unbindVertexArray() } @@ -181,18 +184,21 @@ class VAO( val iboData = indices.limit(indicesCount * 4) bindVertexBuffer(vbo) - bufferData(GL_ARRAY_BUFFER, vboData, GL_DYNAMIC_DRAW) + bufferData(GL_ARRAY_BUFFER, vboData, bufferUsage.gl) unbindVertexBuffer() bindIndexBuffer(ibo) - bufferData(GL_ELEMENT_ARRAY_BUFFER, iboData, GL_DYNAMIC_DRAW) + bufferData(GL_ELEMENT_ARRAY_BUFFER, iboData, bufferUsage.gl) unbindIndexBuffer() + + uploadedIndices = indicesCount } override fun clear() { verticesPosition = verticesPointer vertexIndex = 0 indicesCount = 0 + uploadedIndices = 0 } protected fun finalize() { diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/BufferUsage.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/BufferUsage.kt new file mode 100644 index 000000000..1cd56273d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/BufferUsage.kt @@ -0,0 +1,10 @@ +package com.lambda.graphics.buffer.vao.vertex + +import com.lambda.graphics.gl.GLObject +import org.lwjgl.opengl.GL30C.GL_DYNAMIC_DRAW +import org.lwjgl.opengl.GL30C.GL_STATIC_DRAW + +enum class BufferUsage(override val gl: Int) : GLObject { + STATIC(GL_STATIC_DRAW), + DYNAMIC(GL_DYNAMIC_DRAW) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt index 3ff10e021..919d90610 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -19,7 +19,6 @@ object GlStateUtils { blend(true) cull(false) - block() glDepthMask(true) @@ -36,6 +35,12 @@ object GlStateUtils { depthTest(false) } + fun withFaceCulling(block: () -> Unit) { + cull(true) + block() + cull(false) + } + fun withLineWidth(width: Double, block: () -> Unit) { glLineWidth(width.toFloat()) block() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt new file mode 100644 index 000000000..5381d05b9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt @@ -0,0 +1,153 @@ +package com.lambda.graphics.renderer.esp + +import com.lambda.event.Muteable +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.esp.DirectionMask.exclude +import com.lambda.graphics.renderer.esp.DirectionMask.mask +import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall +import kotlinx.coroutines.* +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction +import net.minecraft.world.BlockView +import net.minecraft.world.chunk.WorldChunk +import java.awt.Color +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.Executors.newSingleThreadExecutor + +class BlockEspRenderer private constructor( + private val owner: Any, + private val filter: (BlockView, BlockPos) -> Boolean, + private val painter: (BlockView, BlockPos) -> Pair +) { + private val rendererMap = ConcurrentHashMap() + private val WorldChunk.renderer get() = rendererMap.getOrPut(this) { + EspChunk(this, this@BlockEspRenderer) + } + + init { + listener { event -> + world.getWorldChunk(event.pos).renderer.update = true + } + + listener { event -> + event.chunk.renderer.updateNeighbors() + } + + listener { event -> + rendererMap.remove(event.chunk)?.updateNeighbors() + } + + owner.listener { + rendererMap.values.forEach { + it.renderer?.render() + } + } + + CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()).launch { + while (true) { + delay(100) + + if ((owner as? Muteable)?.isMuted == true) continue + rendererMap.values.forEach { + it.tick() + delay(1) + } + } + } + } + + companion object { + fun Any.newEspRenderer( + filter: (BlockView, BlockPos) -> Boolean, + painter: (BlockView, BlockPos) -> Pair + ) = BlockEspRenderer(this, filter, painter) + } + + private class EspChunk(val chunk: WorldChunk, val owner: BlockEspRenderer) { + private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()) + + var renderer: EspRenderer? = null + + var update = true + private val chunkOffsets = listOf(1 to 0, 0 to 1, -1 to 0, 0 to -1) + + fun updateNeighbors() { + scope.launch { + chunkOffsets.forEach { ofs -> + chunk.world.chunkManager.getWorldChunk( + chunk.pos.x + ofs.first, + chunk.pos.z + ofs.second + )?.let { + owner.rendererMap[it]?.update = true + } + } + } + } + + fun tick() { + if (!update) return + + // Chunk could only be drawn when all neighbors are loaded + if (chunkOffsets.count { + chunk.world.isChunkLoaded( + chunk.pos.x + it.first, + chunk.pos.z + it.second + ) + } < 4) return + + update = false + + scope.launch { + val newRenderer = runOnMainThreadAndWait(::EspRenderer) + + iterateChunk { x, y, z -> + checkAndDraw(newRenderer, BlockPos(x, y, z)) + } + + runOnMainThreadAndWait { + newRenderer.upload() + renderer = newRenderer + } + } + } + + private suspend fun runOnMainThreadAndWait(block: () -> R): R { + var result: R? = null + + recordRenderCall { + result = block() + } + + while (result == null) delay(1) + return result as R + } + + private fun checkAndDraw(renderer: EspRenderer, blockPos: BlockPos): Boolean { + if (!owner.filter(chunk, blockPos)) return false + + var sides = DirectionMask.ALL + + Direction.entries.forEach { + if (!owner.filter(chunk.world, blockPos.add(it.vector))) return@forEach + sides = sides.exclude(it.mask) + } + + val (filledColor, outlineColor) = owner.painter(chunk, blockPos) + renderer.build(Box(blockPos), filledColor, outlineColor, sides, DirectionMask.OutlineMode.AND) + return true + } + + private fun iterateChunk(block: (Int, Int, Int) -> Unit) = chunk.apply { + for (x in pos.startX..pos.endX) { + for (z in pos.startZ..pos.endZ) { + for (y in bottomY..topY) { + block(x, y, z) + } + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt index 742ccec46..fc566b557 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/CachedEspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt @@ -1,10 +1,11 @@ package com.lambda.graphics.renderer.esp -import com.lambda.event.events.RenderEvent -import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.Lambda.mc import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.BufferUsage import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.gl.GlStateUtils.withFaceCulling import com.lambda.graphics.gl.GlStateUtils.withLineWidth import com.lambda.graphics.renderer.esp.DirectionMask.DOWN import com.lambda.graphics.renderer.esp.DirectionMask.EAST @@ -14,17 +15,16 @@ import com.lambda.graphics.renderer.esp.DirectionMask.UP import com.lambda.graphics.renderer.esp.DirectionMask.WEST import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection import com.lambda.graphics.shader.Shader -import com.lambda.module.Module import com.lambda.util.primitives.extension.max import com.lambda.util.primitives.extension.min import net.minecraft.util.math.Box import java.awt.Color -class CachedEspRenderer(val owner: Any) { - private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER) +class EspRenderer { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC) private var updateFilled = false - private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER) + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC) private var updateOutline = false var outlineWidth = 1.0 @@ -50,12 +50,12 @@ class CachedEspRenderer(val owner: Any) { val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() } val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() } - if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(EAST)) putQuad(brb, trb, trf, brf) if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb) if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf) if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf) - if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb) + if (sides.hasDirection(NORTH)) putQuad(blb, tlb, trb, brb) } fun buildOutline(box: Box, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use { @@ -97,29 +97,29 @@ class CachedEspRenderer(val owner: Any) { if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) } - fun clear() { - filled.clear() - outline.clear() - } + fun upload() { + if (updateFilled) { + updateFilled = false + filled.upload() + } - init { - owner.listener { - if (updateFilled) { - updateFilled = false - filled.upload() - } + if (updateOutline) { + updateOutline = false + outline.upload() + } + } - if (updateOutline) { - updateOutline = false - outline.upload() - } + fun render() { + shader.use() + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos - shader.use() - shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + withFaceCulling(filled::render) + withLineWidth(outlineWidth, outline::render) + } - filled.render() - withLineWidth(outlineWidth, outline::render) - } + fun clear() { + filled.clear() + outline.clear() } companion object { diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index ddaef8848..1c405f3b0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -3,7 +3,7 @@ package com.lambda.module.modules.debug import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.graphics.renderer.esp.CachedEspRenderer +import com.lambda.graphics.renderer.esp.EspRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.ColorUtils.setAlpha @@ -27,7 +27,7 @@ object RenderTest : Module( private val outlineColor = Color(100, 150, 255).setAlpha(0.5) private val filledColor = outlineColor.setAlpha(0.2) - private val rendeer = CachedEspRenderer(this) + private val rendeer = EspRenderer() init { listener { @@ -39,6 +39,10 @@ object RenderTest : Module( rendeer.build(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) } + listener { + rendeer.render() + } + onEnable { rendeer.clear() } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/UpdateTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/UpdateTest.kt deleted file mode 100644 index f1af171ab..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/UpdateTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lambda.module.modules.debug - -import com.lambda.event.events.WorldEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag -import com.lambda.util.Communication.info - -object UpdateTest : Module( - name = "UpdateTest", - description = "A module for testing updates", - defaultTags = setOf(ModuleTag.DEBUG) -) { - init { - listener { - info("Block update at ${it.pos} with state ${it.state} and flags ${it.flags}") - } - - listener { - info("Chunk load at ${it.chunk.pos}") - } - - listener { - info("Chunk unload at ${it.chunk.pos}") - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index cb55bf893..17f03b1fa 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -1,9 +1,13 @@ package com.lambda.module.modules.render import com.lambda.Lambda.mc +import com.lambda.graphics.renderer.esp.BlockEspRenderer.Companion.newEspRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.ColorUtils.setAlpha +import net.minecraft.block.Blocks import net.minecraft.client.render.model.BakedModel +import java.awt.Color object BlockESP : Module( name = "BlockESP", @@ -24,5 +28,13 @@ object BlockESP : Module( onToggle { mc.worldRenderer.reload() } + + val outlineColor = Color(100, 150, 255).setAlpha(0.5) + val filledColor = outlineColor.setAlpha(0.2) + + newEspRenderer( + { view, pos -> view.getBlockState(pos).block.defaultState == Blocks.GRASS_BLOCK.defaultState }, + { _, _ -> filledColor to outlineColor } + ) } } \ No newline at end of file From 9d46ccb3a00f8130e94ed2c38ba798e69a283f10 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 13 Jun 2024 21:18:47 +0300 Subject: [PATCH 33/42] Vertex Mapping and misc --- .../{BlockEspRenderer.kt => ChunkedESP.kt} | 38 ++++++++--- .../graphics/renderer/esp/EspRenderer.kt | 65 +++++++++++++------ .../renderer/gui/font/FontRenderer.kt | 16 ++--- .../renderer/gui/font/glyph/EmojiGlyphs.kt | 4 +- .../renderer/gui/font/glyph/FontGlyphs.kt | 4 +- .../module/modules/client/FontSettings.kt | 19 ------ .../module/modules/client/RenderSettings.kt | 37 +++++++++++ .../lambda/module/modules/render/BlockESP.kt | 4 +- 8 files changed, 126 insertions(+), 61 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/renderer/esp/{BlockEspRenderer.kt => ChunkedESP.kt} (80%) delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt similarity index 80% rename from common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index 5381d05b9..f2cf5ca0f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/BlockEspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -2,10 +2,12 @@ package com.lambda.graphics.renderer.esp import com.lambda.event.Muteable import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.graphics.renderer.esp.DirectionMask.mask +import com.lambda.module.modules.client.RenderSettings import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import kotlinx.coroutines.* import net.minecraft.util.math.BlockPos @@ -15,21 +17,27 @@ import net.minecraft.world.BlockView import net.minecraft.world.chunk.WorldChunk import java.awt.Color import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentLinkedDeque import java.util.concurrent.Executors.newSingleThreadExecutor -class BlockEspRenderer private constructor( +class ChunkedESP private constructor( private val owner: Any, private val filter: (BlockView, BlockPos) -> Boolean, private val painter: (BlockView, BlockPos) -> Pair ) { private val rendererMap = ConcurrentHashMap() private val WorldChunk.renderer get() = rendererMap.getOrPut(this) { - EspChunk(this, this@BlockEspRenderer) + EspChunk(this, this@ChunkedESP) } + private val uploadPool = ConcurrentLinkedDeque<() -> Unit>() + init { listener { event -> - world.getWorldChunk(event.pos).renderer.update = true + world.getWorldChunk(event.pos).renderer.apply { + update = true + updateNeighbors() + } } listener { event -> @@ -40,6 +48,12 @@ class BlockEspRenderer private constructor( rendererMap.remove(event.chunk)?.updateNeighbors() } + owner.listener { + repeat(RenderSettings.chunksPerTick) { + uploadPool.poll()?.invoke() + } + } + owner.listener { rendererMap.values.forEach { it.renderer?.render() @@ -53,20 +67,19 @@ class BlockEspRenderer private constructor( if ((owner as? Muteable)?.isMuted == true) continue rendererMap.values.forEach { it.tick() - delay(1) } } } } companion object { - fun Any.newEspRenderer( + fun Any.newChunkedESP( filter: (BlockView, BlockPos) -> Boolean, painter: (BlockView, BlockPos) -> Pair - ) = BlockEspRenderer(this, filter, painter) + ) = ChunkedESP(this, filter, painter) } - private class EspChunk(val chunk: WorldChunk, val owner: BlockEspRenderer) { + private class EspChunk(val chunk: WorldChunk, val owner: ChunkedESP) { private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()) var renderer: EspRenderer? = null @@ -107,10 +120,19 @@ class BlockEspRenderer private constructor( checkAndDraw(newRenderer, BlockPos(x, y, z)) } - runOnMainThreadAndWait { + val upload = { newRenderer.upload() renderer = newRenderer } + + when (RenderSettings.uploadScheduler) { + RenderSettings.UploadScheduler.Instant -> { + runOnMainThreadAndWait(upload) + } + RenderSettings.UploadScheduler.Delayed -> { + owner.uploadPool.add(upload) + } + } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt index fc566b557..8b942a7f6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt @@ -1,6 +1,7 @@ package com.lambda.graphics.renderer.esp import com.lambda.Lambda.mc +import com.lambda.graphics.buffer.vao.IRenderContext import com.lambda.graphics.buffer.vao.VAO import com.lambda.graphics.buffer.vao.vertex.BufferUsage import com.lambda.graphics.buffer.vao.vertex.VertexAttrib @@ -15,16 +16,21 @@ import com.lambda.graphics.renderer.esp.DirectionMask.UP import com.lambda.graphics.renderer.esp.DirectionMask.WEST import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection import com.lambda.graphics.shader.Shader +import com.lambda.module.modules.client.RenderSettings import com.lambda.util.primitives.extension.max import com.lambda.util.primitives.extension.min import net.minecraft.util.math.Box import java.awt.Color +import java.util.concurrent.ConcurrentHashMap -class EspRenderer { - private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC) - private var updateFilled = false +class EspRenderer(usage: BufferUsage = BufferUsage.STATIC) { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, usage) + private val filledVertices = ConcurrentHashMap() + + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, usage) + private val outlineVertices = ConcurrentHashMap() - private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC) + private var updateFilled = false private var updateOutline = false var outlineWidth = 1.0 @@ -41,14 +47,14 @@ class EspRenderer { grow(8) - val blb by lazy { vec3(pos1.x, pos1.y, pos1.z).color(color).end() } - val blf by lazy { vec3(pos1.x, pos1.y, pos2.z).color(color).end() } - val brb by lazy { vec3(pos2.x, pos1.y, pos1.z).color(color).end() } - val brf by lazy { vec3(pos2.x, pos1.y, pos2.z).color(color).end() } - val tlb by lazy { vec3(pos1.x, pos2.y, pos1.z).color(color).end() } - val tlf by lazy { vec3(pos1.x, pos2.y, pos2.z).color(color).end() } - val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() } - val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() } + val blb by vertex(filledVertices, pos1.x, pos1.y, pos1.z, color) + val blf by vertex(filledVertices, pos1.x, pos1.y, pos2.z, color) + val brb by vertex(filledVertices, pos2.x, pos1.y, pos1.z, color) + val brf by vertex(filledVertices, pos2.x, pos1.y, pos2.z, color) + val tlb by vertex(filledVertices, pos1.x, pos2.y, pos1.z, color) + val tlf by vertex(filledVertices, pos1.x, pos2.y, pos2.z, color) + val trb by vertex(filledVertices, pos2.x, pos2.y, pos1.z, color) + val trf by vertex(filledVertices, pos2.x, pos2.y, pos2.z, color) if (sides.hasDirection(EAST)) putQuad(brb, trb, trf, brf) if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) @@ -65,14 +71,14 @@ class EspRenderer { grow(8) - val blb by lazy { vec3(pos1.x, pos1.y, pos1.z).color(color).end() } - val blf by lazy { vec3(pos1.x, pos1.y, pos2.z).color(color).end() } - val brb by lazy { vec3(pos2.x, pos1.y, pos1.z).color(color).end() } - val brf by lazy { vec3(pos2.x, pos1.y, pos2.z).color(color).end() } - val tlb by lazy { vec3(pos1.x, pos2.y, pos1.z).color(color).end() } - val tlf by lazy { vec3(pos1.x, pos2.y, pos2.z).color(color).end() } - val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() } - val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() } + val blb by vertex(outlineVertices, pos1.x, pos1.y, pos1.z, color) + val blf by vertex(outlineVertices, pos1.x, pos1.y, pos2.z, color) + val brb by vertex(outlineVertices, pos2.x, pos1.y, pos1.z, color) + val brf by vertex(outlineVertices, pos2.x, pos1.y, pos2.z, color) + val tlb by vertex(outlineVertices, pos1.x, pos2.y, pos1.z, color) + val tlf by vertex(outlineVertices, pos1.x, pos2.y, pos2.z, color) + val trb by vertex(outlineVertices, pos2.x, pos2.y, pos1.z, color) + val trf by vertex(outlineVertices, pos2.x, pos2.y, pos2.z, color) val hasEast = sides.hasDirection(EAST) val hasWest = sides.hasDirection(WEST) @@ -118,10 +124,29 @@ class EspRenderer { } fun clear() { + filledVertices.clear() + outlineVertices.clear() + filled.clear() outline.clear() } + private fun IRenderContext.vertex( + storage: MutableMap, + x: Double, y: Double, z: Double, + color: Color + ) = lazy { + val newVertex = { + vec3(x, y, z).color(color).end() + } + + if (RenderSettings.vertexMapping) { + storage.getOrPut(Vertex(x, y, z, color), newVertex) + } else newVertex() + } + + private data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) + companion object { private val shader = Shader("renderer/pos_color", "renderer/box_static") } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index b829107a2..d176e5f2a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -5,7 +5,7 @@ import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo import com.lambda.graphics.shader.Shader -import com.lambda.module.modules.client.FontSettings +import com.lambda.module.modules.client.RenderSettings import com.lambda.util.math.ColorUtils.a import com.lambda.util.math.ColorUtils.setAlpha import com.lambda.util.math.Vec2d @@ -125,7 +125,7 @@ class FontRenderer( // Render chars font[text[index]]?.let { info -> // Draw a shadow before - if (shadow && FontSettings.shadow && shadowShift > 0.0) { + if (shadow && RenderSettings.shadow && shadowShift > 0.0) { draw(info, shadowColor, shadowShift) } @@ -139,9 +139,9 @@ class FontRenderer( private fun getShadowColor(color: Color): Color { return Color( - (color.red * FontSettings.shadowBrightness).toInt(), - (color.green * FontSettings.shadowBrightness).toInt(), - (color.blue * FontSettings.shadowBrightness).toInt(), + (color.red * RenderSettings.shadowBrightness).toInt(), + (color.green * RenderSettings.shadowBrightness).toInt(), + (color.blue * RenderSettings.shadowBrightness).toInt(), color.alpha ) } @@ -161,8 +161,8 @@ class FontRenderer( companion object { private val shader = Shader("renderer/font") - private val shadowShift get() = FontSettings.shadowShift * 5.0 - private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f - private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f + private val shadowShift get() = RenderSettings.shadowShift * 5.0 + private val baselineOffset get() = RenderSettings.baselineOffset * 2.0f - 10f + private val gap get() = RenderSettings.gap * 0.5f - 0.8f } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt index 4d8f948d8..97cf936b9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt @@ -3,7 +3,7 @@ package com.lambda.graphics.renderer.gui.font.glyph import com.google.common.math.IntMath.pow import com.lambda.Lambda.LOG import com.lambda.graphics.texture.MipmapTexture -import com.lambda.module.modules.client.FontSettings +import com.lambda.module.modules.client.RenderSettings import com.lambda.util.math.Vec2d import java.awt.Color import java.awt.Graphics2D @@ -89,7 +89,7 @@ class EmojiGlyphs(zipUrl: String) { fun bind() { with(fontTexture) { bind(GL_TEXTURE_SLOT) - setLOD(FontSettings.lodBias.toFloat()) + setLOD(RenderSettings.lodBias.toFloat()) } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 45bfe150b..e00cbb284 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -3,7 +3,7 @@ package com.lambda.graphics.renderer.gui.font.glyph import com.lambda.Lambda import com.lambda.graphics.texture.MipmapTexture import com.lambda.graphics.texture.TextureUtils.getCharImage -import com.lambda.module.modules.client.FontSettings +import com.lambda.module.modules.client.RenderSettings import com.lambda.util.math.Vec2d import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import java.awt.Color @@ -64,7 +64,7 @@ class FontGlyphs(font: Font) { fun bind() { with(fontTexture) { bind(GL_TEXTURE_SLOT) - setLOD(FontSettings.lodBias.toFloat()) + setLOD(RenderSettings.lodBias.toFloat()) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt deleted file mode 100644 index 560d3e784..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lambda.module.modules.client - -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag - -object FontSettings : Module( - name = "FontSettings", - description = "Font renderer configuration", - defaultTags = setOf(ModuleTag.CLIENT) -) { - val shadow by setting("Shadow", true) - val shadowBrightness by setting("Shadow Brightness", 0.35, 0.0..0.5, 0.01) { shadow } - val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05) { shadow } - val gapSetting by setting("Gap", 1.5, -10.0..10.0, 0.5) - val baselineOffset by setting("Vertical Offset", 0.0, -10.0..10.0, 0.5) - private val lodBiasSetting by setting("Smoothing", 0.0, -10.0..10.0, 0.5) - - val lodBias get() = lodBiasSetting * 0.25f - 0.75f -} diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt new file mode 100644 index 000000000..2584e32d0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt @@ -0,0 +1,37 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag + +object RenderSettings : Module( + name = "RenderSettings", + description = "Renderer configuration", + defaultTags = setOf(ModuleTag.CLIENT) +) { + private val page by setting("Page", Page.Font) + + // Font + val shadow by setting("Shadow", true) { page == Page.Font } + val shadowBrightness by setting("Shadow Brightness", 0.35, 0.0..0.5, 0.01) { page == Page.Font && shadow } + val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05) { page == Page.Font && shadow } + val gap by setting("Gap", 1.5, -10.0..10.0, 0.5) { page == Page.Font } + val baselineOffset by setting("Vertical Offset", 0.0, -10.0..10.0, 0.5) { page == Page.Font } + private val lodBiasSetting by setting("Smoothing", 0.0, -10.0..10.0, 0.5) { page == Page.Font } + + // ESP + val uploadScheduler by setting("Upload Scheduler", UploadScheduler.Instant) { page == Page.ESP } + val chunksPerTick by setting("Chunks", 8, 1..32, 1, unit = " / tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } + val vertexMapping by setting("Vertex Mapping", true) { page == Page.ESP } + + val lodBias get() = lodBiasSetting * 0.25f - 0.75f + + private enum class Page { + Font, + ESP + } + + enum class UploadScheduler { + Instant, + Delayed + } +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 17f03b1fa..cde3b7203 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -1,7 +1,7 @@ package com.lambda.module.modules.render import com.lambda.Lambda.mc -import com.lambda.graphics.renderer.esp.BlockEspRenderer.Companion.newEspRenderer +import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.ColorUtils.setAlpha @@ -32,7 +32,7 @@ object BlockESP : Module( val outlineColor = Color(100, 150, 255).setAlpha(0.5) val filledColor = outlineColor.setAlpha(0.2) - newEspRenderer( + newChunkedESP( { view, pos -> view.getBlockState(pos).block.defaultState == Blocks.GRASS_BLOCK.defaultState }, { _, _ -> filledColor to outlineColor } ) From d75461cf65e5be115255ad2ae5879d76c512b359 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:20:23 -0400 Subject: [PATCH 34/42] Better emoji parsing --- .../src/main/kotlin/com/lambda/core/Loader.kt | 4 +- .../renderer/gui/font/FontRenderer.kt | 57 +++++++++++-------- .../font/{LambdaMoji.kt => LambdaEmoji.kt} | 4 +- .../kotlin/com/lambda/gui/api/RenderLayer.kt | 4 +- .../module/modules/client/LambdaMoji.kt | 11 ++++ 5 files changed, 50 insertions(+), 30 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/{LambdaMoji.kt => LambdaEmoji.kt} (84%) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 0b6513329..3adc51e43 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -5,7 +5,7 @@ import com.lambda.Lambda.LOG import com.lambda.command.CommandRegistry import com.lambda.friend.FriendRegistry import com.lambda.graphics.renderer.gui.font.LambdaFont -import com.lambda.graphics.renderer.gui.font.LambdaMoji +import com.lambda.graphics.renderer.gui.font.LambdaEmoji import com.lambda.gui.GuiConfigurable import com.lambda.gui.HudGuiConfigurable import com.lambda.interaction.PlayerPacketManager @@ -23,7 +23,7 @@ object Loader { RotationManager, PlayerPacketManager, LambdaFont.Loader, - LambdaMoji.Loader, + LambdaEmoji.Loader, GuiConfigurable, HudGuiConfigurable, FriendRegistry, diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index d176e5f2a..f53001eae 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -5,6 +5,7 @@ import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo import com.lambda.graphics.shader.Shader +import com.lambda.module.modules.client.LambdaMoji import com.lambda.module.modules.client.RenderSettings import com.lambda.util.math.ColorUtils.a import com.lambda.util.math.ColorUtils.setAlpha @@ -13,7 +14,7 @@ import java.awt.Color class FontRenderer( private val font: LambdaFont, - private val emojis: LambdaMoji + private val emojis: LambdaEmoji ) { private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) @@ -24,7 +25,7 @@ class FontRenderer( * Parses the emojis in the given text. * * @param text The text to parse. - * @return A list of pairs containing the glyph info and the range of the emoji in the text. + * @return A list of pairs containing the glyph info and the range of the emoji in the text. */ fun parseEmojis(text: String) = mutableListOf>().apply { @@ -102,36 +103,44 @@ class FontRenderer( val emojis = parseEmojis(text) - repeat(text.length) { index -> - fun draw(info: GlyphInfo, color: Color, offset: Double = 0.0) { - val scaledSize = info.size * actualScale - val pos1 = Vec2d(posX, posY) + offset * actualScale - val pos2 = pos1 + scaledSize + fun draw(info: GlyphInfo, color: Color, offset: Double = 0.0) { + val scaledSize = info.size * actualScale + val pos1 = Vec2d(posX, posY) + offset * actualScale + val pos2 = pos1 + scaledSize - block(info, pos1, pos2, color) - if (offset == 0.0) posX += scaledSize.x + scaledGap - } + block(info, pos1, pos2, color) + if (offset == 0.0) posX += scaledSize.x + scaledGap + } - // Check if there's an emoji - emojis.firstOrNull { index in it.second }?.let { emoji -> - // Replace first emoji char by an emoji glyph and skip the other ones - if (index == emoji.second.first) { - draw(emoji.first, emojiColor) - } + var index = 0 + textProcessor@ while (index < text.length) { + var innerLoopContact = false // Instead of using BreakContinueInInlineLambdas, we use this - return@repeat + if (LambdaMoji.isEnabled) { + // Check if there are emojis to render + emojis.firstOrNull { index in it.second }?.let { emoji -> + if (index == emoji.second.first) draw(emoji.first, emojiColor) + + // Skip the emoji + index = emoji.second.last + 1 + innerLoopContact = true + } } + if (innerLoopContact) continue@textProcessor + // Render chars - font[text[index]]?.let { info -> - // Draw a shadow before - if (shadow && RenderSettings.shadow && shadowShift > 0.0) { - draw(info, shadowColor, shadowShift) - } + val charInfo = font[text[index]] ?: continue@textProcessor - // Draw actual char over the shadow - draw(info, color) + // Draw a shadow before + if (shadow && RenderSettings.shadow && shadowShift > 0.0) { + draw(charInfo, shadowColor, shadowShift) } + + // Draw actual char over the shadow + draw(charInfo, color) + + index++ } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt index 39e9bae9e..b8d22be9a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt @@ -3,7 +3,7 @@ package com.lambda.graphics.renderer.gui.font import com.lambda.core.Loadable import com.lambda.graphics.renderer.gui.font.glyph.EmojiGlyphs -enum class LambdaMoji(private val zipUrl: String) { +enum class LambdaEmoji(private val zipUrl: String) { Twemoji("https://github.com/Edouard127/emoji-generator/releases/latest/download/emojis.zip"); lateinit var glyphs: EmojiGlyphs // TODO: Support multiple emoji pools @@ -16,7 +16,7 @@ enum class LambdaMoji(private val zipUrl: String) { object Loader : Loadable { override fun load(): String { - entries.forEach(LambdaMoji::loadGlyphs) + entries.forEach(LambdaEmoji::loadGlyphs) return "Loaded ${entries.size} emoji pools" } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt index 0d8e2b2cf..034df4ff7 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt @@ -2,7 +2,7 @@ package com.lambda.gui.api import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaFont -import com.lambda.graphics.renderer.gui.font.LambdaMoji +import com.lambda.graphics.renderer.gui.font.LambdaEmoji import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer @@ -11,7 +11,7 @@ class RenderLayer { val outline = OutlineRectRenderer() val font = FontRenderer( LambdaFont.FiraSansRegular, - LambdaMoji.Twemoji, + LambdaEmoji.Twemoji, ) fun render() { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt new file mode 100644 index 000000000..36b90b28c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt @@ -0,0 +1,11 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag + +object LambdaMoji : Module( + name = "LambdaMoji", + description = "", + defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.RENDER), + enabledByDefault = true, +) From 20e47773e2163ff75df421566591f4ab5a9854a0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 14 Jun 2024 20:41:42 +0200 Subject: [PATCH 35/42] Smol refactors --- common/src/main/kotlin/com/lambda/Lambda.kt | 2 + .../kotlin/com/lambda/config/Configuration.kt | 1 + .../main/kotlin/com/lambda/event/EventFlow.kt | 4 +- .../com/lambda/event/listener/SafeListener.kt | 7 +- .../com/lambda/graphics/buffer/vao/VAO.kt | 54 ++++--- .../graphics/renderer/esp/ChunkedESP.kt | 134 +++++++++--------- .../graphics/renderer/esp/EspRenderer.kt | 12 +- .../module/modules/client/RenderSettings.kt | 4 +- .../lambda/module/modules/debug/RenderTest.kt | 6 +- .../lambda/module/modules/render/BlockESP.kt | 41 ++++-- .../src/main/resources/lambda.accesswidener | 2 + 11 files changed, 154 insertions(+), 113 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/Lambda.kt b/common/src/main/kotlin/com/lambda/Lambda.kt index 4cd45128a..9571b5fa2 100644 --- a/common/src/main/kotlin/com/lambda/Lambda.kt +++ b/common/src/main/kotlin/com/lambda/Lambda.kt @@ -10,8 +10,10 @@ import com.lambda.core.Loader import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow import com.lambda.gui.impl.clickgui.windows.tag.TagWindow import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runGameScheduled import com.lambda.util.KeyCode import com.mojang.authlib.GameProfile +import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import net.minecraft.block.Block import net.minecraft.client.MinecraftClient import net.minecraft.util.math.BlockPos diff --git a/common/src/main/kotlin/com/lambda/config/Configuration.kt b/common/src/main/kotlin/com/lambda/config/Configuration.kt index a58e24769..eaa64b2c3 100644 --- a/common/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/common/src/main/kotlin/com/lambda/config/Configuration.kt @@ -131,6 +131,7 @@ abstract class Configuration : Jsonable { } .onFailure { val message = "Failed to save ${configName.capitalize()} config" + LOG.error(message, it) logError(message) } } diff --git a/common/src/main/kotlin/com/lambda/event/EventFlow.kt b/common/src/main/kotlin/com/lambda/event/EventFlow.kt index f9f5f616b..b14290673 100644 --- a/common/src/main/kotlin/com/lambda/event/EventFlow.kt +++ b/common/src/main/kotlin/com/lambda/event/EventFlow.kt @@ -159,9 +159,7 @@ object EventFlow { private fun Event.executeListenerConcurrently() { concurrentListeners[this::class]?.forEach { listener -> if (shouldNotNotify(listener, this)) return@forEach - runConcurrent { - listener.execute(this) - } + listener.execute(this) } } diff --git a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt index 7f24f3430..20ccca517 100644 --- a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt +++ b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt @@ -5,6 +5,7 @@ import com.lambda.event.Event import com.lambda.event.EventFlow import com.lambda.event.Muteable import com.lambda.task.Task +import com.lambda.threading.runConcurrent import com.lambda.threading.runSafe @@ -162,10 +163,12 @@ class SafeListener( inline fun Any.concurrentListener( priority: Int = 0, alwaysListen: Boolean = false, - noinline function: SafeContext.(T) -> Unit, + noinline function: suspend SafeContext.(T) -> Unit, ): SafeListener { val listener = SafeListener(priority, this, alwaysListen) { event -> - function(event as T) + runConcurrent { + function(event as T) + } } EventFlow.concurrentListeners.subscribe(listener) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 022c0c41a..d822e3f94 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -21,7 +21,6 @@ import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer import com.lambda.threading.runGameScheduled -import com.mojang.blaze3d.systems.RenderSystem.drawElements import org.lwjgl.opengl.GL30C.* import java.awt.Color import java.nio.ByteBuffer @@ -29,7 +28,8 @@ import java.nio.ByteBuffer class VAO( private val vertexMode: VertexMode, attribGroup: VertexAttrib.Group, - private val bufferUsage: BufferUsage = BufferUsage.DYNAMIC + private val bufferUsage: BufferUsage = BufferUsage.DYNAMIC, + initializeInstantly: Boolean = false ) : IRenderContext { private var vao = 0 private var vbo = 0 @@ -52,34 +52,42 @@ class VAO( val stride = attribGroup.stride objectSize = stride * vertexMode.indicesCount - runGameScheduled { - vertices = byteBuffer(objectSize * 256 * 4) - verticesPointer = address(vertices) - verticesPosition = verticesPointer + if (initializeInstantly) { + initialize(attribGroup, stride) + } else { + runGameScheduled { + initialize(attribGroup, stride) + } + } + } - indices = byteBuffer(vertexMode.indicesCount * 512 * 4) - indicesPointer = address(indices) + private fun initialize(attribGroup: VertexAttrib.Group, stride: Int) { + vertices = byteBuffer(objectSize * 256 * 4) + verticesPointer = address(vertices) + verticesPosition = verticesPointer - vao = glGenVertexArrays() - bindVertexArray(vao) + indices = byteBuffer(vertexMode.indicesCount * 512 * 4) + indicesPointer = address(indices) - vbo = glGenBuffers() - bindVertexBuffer(vbo) + vao = glGenVertexArrays() + bindVertexArray(vao) - ibo = glGenBuffers() - bindIndexBuffer(ibo) + vbo = glGenBuffers() + bindVertexBuffer(vbo) - var pointer = 0L - attribGroup.attributes.forEachIndexed { index, attrib -> - VaoUtils.enableVertexAttribute(index) - VaoUtils.vertexAttribute(index, attrib.componentCount, attrib.gl, attrib.normalized, stride, pointer) - pointer += attrib.size - } + ibo = glGenBuffers() + bindIndexBuffer(ibo) - unbindVertexArray() - unbindVertexBuffer() - unbindIndexBuffer() + var pointer = 0L + attribGroup.attributes.forEachIndexed { index, attrib -> + VaoUtils.enableVertexAttribute(index) + VaoUtils.vertexAttribute(index, attrib.componentCount, attrib.gl, attrib.normalized, stride, pointer) + pointer += attrib.size } + + unbindVertexArray() + unbindVertexBuffer() + unbindIndexBuffer() } override fun vec3(x: Double, y: Double, z: Double): VAO { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index f2cf5ca0f..a319574ac 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -1,75 +1,82 @@ package com.lambda.graphics.renderer.esp -import com.lambda.event.Muteable import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.module.modules.client.RenderSettings +import com.lambda.threading.runGameBlocking import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import kotlinx.coroutines.* import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box +import net.minecraft.util.math.ChunkPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockView import net.minecraft.world.chunk.WorldChunk import java.awt.Color import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque -import java.util.concurrent.Executors.newSingleThreadExecutor class ChunkedESP private constructor( - private val owner: Any, + owner: Any, private val filter: (BlockView, BlockPos) -> Boolean, private val painter: (BlockView, BlockPos) -> Pair ) { - private val rendererMap = ConcurrentHashMap() - private val WorldChunk.renderer get() = rendererMap.getOrPut(this) { + private val rendererMap = ConcurrentHashMap() + private val WorldChunk.renderer get() = rendererMap.getOrPut(pos) { EspChunk(this, this@ChunkedESP) } + private var ticks = 0 private val uploadPool = ConcurrentLinkedDeque<() -> Unit>() + private val rebuildPool = ConcurrentLinkedDeque<() -> Unit>() + + fun clear() { + rendererMap.clear() + } init { - listener { event -> + owner.concurrentListener { event -> world.getWorldChunk(event.pos).renderer.apply { - update = true - updateNeighbors() + markOutdated() + notifyNeighbors() } } - listener { event -> - event.chunk.renderer.updateNeighbors() + owner.concurrentListener { event -> + event.chunk.renderer.notifyNeighbors() } - listener { event -> - rendererMap.remove(event.chunk)?.updateNeighbors() + owner.concurrentListener { event -> + rendererMap.remove(event.chunk.pos)?.notifyNeighbors() } - owner.listener { - repeat(RenderSettings.chunksPerTick) { - uploadPool.poll()?.invoke() + owner.concurrentListener { + if (++ticks % RenderSettings.updateFrequency == 0) { + rendererMap.values + .filter { it.outdated && it.neighborsLoaded } + .forEach { it.rebuild() } + ticks = 0 } } + owner.listener { + if (uploadPool.isEmpty()) return@listener + + uploadPool + .take(RenderSettings.uploadsPerTick) + .forEach { it() } + } + owner.listener { rendererMap.values.forEach { it.renderer?.render() } } - - CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()).launch { - while (true) { - delay(100) - - if ((owner as? Muteable)?.isMuted == true) continue - rendererMap.values.forEach { - it.tick() - } - } - } } companion object { @@ -80,59 +87,52 @@ class ChunkedESP private constructor( } private class EspChunk(val chunk: WorldChunk, val owner: ChunkedESP) { - private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()) - var renderer: EspRenderer? = null + var outdated = true - var update = true private val chunkOffsets = listOf(1 to 0, 0 to 1, -1 to 0, 0 to -1) + val neighbors = chunkOffsets.map { + ChunkPos(chunk.pos.x + it.first, chunk.pos.z + it.second) + }.toTypedArray() + val neighborsLoaded get() = neighbors.all { it.isLoaded() } - fun updateNeighbors() { - scope.launch { - chunkOffsets.forEach { ofs -> - chunk.world.chunkManager.getWorldChunk( - chunk.pos.x + ofs.first, - chunk.pos.z + ofs.second - )?.let { - owner.rendererMap[it]?.update = true - } - } - } - } + fun ChunkPos.isLoaded() = chunk.world.chunkManager.isChunkLoaded(x, z) - fun tick() { - if (!update) return + fun markOutdated() { + outdated = true + } - // Chunk could only be drawn when all neighbors are loaded - if (chunkOffsets.count { - chunk.world.isChunkLoaded( - chunk.pos.x + it.first, - chunk.pos.z + it.second - ) - } < 4) return + fun notifyNeighbors() { + neighbors.forEach { + owner.rendererMap[it]?.markOutdated() + } + } - update = false + suspend fun rebuild() { + outdated = false - scope.launch { - val newRenderer = runOnMainThreadAndWait(::EspRenderer) + val newRenderer = runGameBlocking { + EspRenderer() + } - iterateChunk { x, y, z -> - checkAndDraw(newRenderer, BlockPos(x, y, z)) - } + iterateChunk { x, y, z -> + checkAndDraw(newRenderer, BlockPos(x, y, z)) + } - val upload = { - newRenderer.upload() - renderer = newRenderer - } + val upload = { + newRenderer.upload() + renderer = newRenderer + } - when (RenderSettings.uploadScheduler) { - RenderSettings.UploadScheduler.Instant -> { - runOnMainThreadAndWait(upload) - } - RenderSettings.UploadScheduler.Delayed -> { - owner.uploadPool.add(upload) + when (RenderSettings.uploadScheduler) { + RenderSettings.UploadScheduler.Instant -> { + runGameBlocking { + upload() } } + RenderSettings.UploadScheduler.Delayed -> { + owner.uploadPool.add(upload) + } } } @@ -145,7 +145,7 @@ class ChunkedESP private constructor( while (result == null) delay(1) return result as R - } + } private fun checkAndDraw(renderer: EspRenderer, blockPos: BlockPos): Boolean { if (!owner.filter(chunk, blockPos)) return false diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt index 8b942a7f6..0050621e0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt @@ -23,18 +23,18 @@ import net.minecraft.util.math.Box import java.awt.Color import java.util.concurrent.ConcurrentHashMap -class EspRenderer(usage: BufferUsage = BufferUsage.STATIC) { - private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, usage) +class EspRenderer( + usage: BufferUsage = BufferUsage.STATIC +) { + private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, usage, true) private val filledVertices = ConcurrentHashMap() - private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, usage) + private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, usage, true) private val outlineVertices = ConcurrentHashMap() private var updateFilled = false private var updateOutline = false - var outlineWidth = 1.0 - fun build(box: Box, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) { buildFilled(box, filledColor, sides) buildOutline(box, outlineColor, sides, outlineMode) @@ -120,7 +120,7 @@ class EspRenderer(usage: BufferUsage = BufferUsage.STATIC) { shader["u_CameraPosition"] = mc.gameRenderer.camera.pos withFaceCulling(filled::render) - withLineWidth(outlineWidth, outline::render) + withLineWidth(RenderSettings.outlineWidth, outline::render) } fun clear() { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt index 2584e32d0..a2f139459 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt @@ -20,8 +20,10 @@ object RenderSettings : Module( // ESP val uploadScheduler by setting("Upload Scheduler", UploadScheduler.Instant) { page == Page.ESP } - val chunksPerTick by setting("Chunks", 8, 1..32, 1, unit = " / tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } + val uploadsPerTick by setting("Uploads", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } val vertexMapping by setting("Vertex Mapping", true) { page == Page.ESP } + val updateFrequency by setting("Update Frequency", 2, 1..10, 1, "Frequency of block updates", unit = " ticks") { page == Page.ESP } + val outlineWidth by setting("Outline Width", 1.0, 0.1..5.0, 0.1, "Width of block outlines", unit = "px") { page == Page.ESP } val lodBias get() = lodBiasSetting * 0.25f - 0.75f diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index 1c405f3b0..4344def28 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -6,8 +6,10 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.EspRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.mainThread import com.lambda.util.math.ColorUtils.setAlpha import com.lambda.util.world.WorldUtils.getClosestEntity +import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import net.minecraft.entity.LivingEntity import net.minecraft.util.math.Box import java.awt.Color @@ -27,7 +29,9 @@ object RenderTest : Module( private val outlineColor = Color(100, 150, 255).setAlpha(0.5) private val filledColor = outlineColor.setAlpha(0.2) - private val rendeer = EspRenderer() + private val rendeer by mainThread { + EspRenderer() + } init { listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index cde3b7203..e3743eb0a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -4,7 +4,6 @@ import com.lambda.Lambda.mc import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.math.ColorUtils.setAlpha import net.minecraft.block.Blocks import net.minecraft.client.render.model.BakedModel import java.awt.Color @@ -14,6 +13,31 @@ object BlockESP : Module( description = "Render block ESP", defaultTags = setOf(ModuleTag.RENDER) ) { + private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks").apply { + onValueSet { _, to -> + if (!to) drawOutlines = true + } + } + val faceColor by setting("Face Color", Color(100, 150, 255, 128), "Color of the surfaces") { + drawFaces + } + private var drawOutlines by setting("Draw Outlines", true, "Draw outlines of blocks").apply { + onValueSet { _, to -> + if (!to) drawFaces = true + } + } + private val outlineColor by setting("Outline Color", Color(100, 150, 255), "Color of the outlines") { + drawOutlines + } + private var clearRender: Boolean by setting("Clear Render", false, "Clear render after rendering blocks").apply { + onValueSet { _, to -> + if (to) { + esp.clear() + clearRender = false + } + } + } + @JvmStatic val barrier by setting("Solid Barrier Block", true, "Render barrier blocks") @@ -24,17 +48,14 @@ object BlockESP : Module( @JvmStatic val model: BakedModel get() = mc.bakedModelManager.missingModel + private val esp = newChunkedESP( + { view, pos -> view.getBlockState(pos).block == Blocks.BEDROCK }, + { _, _ -> faceColor to outlineColor } + ) + init { onToggle { - mc.worldRenderer.reload() + if (barrier) mc.worldRenderer.reload() } - - val outlineColor = Color(100, 150, 255).setAlpha(0.5) - val filledColor = outlineColor.setAlpha(0.2) - - newChunkedESP( - { view, pos -> view.getBlockState(pos).block.defaultState == Blocks.GRASS_BLOCK.defaultState }, - { _, _ -> filledColor to outlineColor } - ) } } \ No newline at end of file diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index cd9d8a090..69ab51fc2 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -10,6 +10,8 @@ accessible field net/minecraft/client/MinecraftClient thread Ljava/lang/Thread; accessible field net/minecraft/client/world/ClientWorld entityManager Lnet/minecraft/client/world/ClientEntityManager; accessible field net/minecraft/client/world/ClientEntityManager cache Lnet/minecraft/world/entity/SectionedEntityCache; accessible field net/minecraft/world/entity/EntityTrackingSection collection Lnet/minecraft/util/collection/TypeFilterableList; +accessible field net/minecraft/client/world/ClientChunkManager chunks Lnet/minecraft/client/world/ClientChunkManager$ClientChunkMap; +accessible field net/minecraft/client/world/ClientChunkManager$ClientChunkMap chunks Ljava/util/concurrent/atomic/AtomicReferenceArray; # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; From 0e8fe57c0648d0a2629fac56906c34c80eced5a1 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Fri, 14 Jun 2024 22:09:40 +0300 Subject: [PATCH 36/42] ChunkStorage --- .../graphics/renderer/esp/ChunkStorage.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt new file mode 100644 index 000000000..63d752834 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt @@ -0,0 +1,29 @@ +package com.lambda.graphics.renderer.esp + +import com.lambda.core.Loadable +import com.lambda.event.events.ConnectionEvent +import com.lambda.event.events.WorldEvent +import com.lambda.event.listener.SafeListener.Companion.concurrentListener +import net.minecraft.util.math.ChunkPos +import net.minecraft.world.chunk.WorldChunk +import java.util.concurrent.ConcurrentHashMap + +object ChunkStorage : Loadable { + val chunkMap = ChunkMap() + + init { + concurrentListener { event -> + chunkMap[event.chunk.pos] = event.chunk + } + + concurrentListener { event -> + chunkMap.remove(event.chunk.pos) + } + + concurrentListener { + chunkMap.clear() + } + } +} + +typealias ChunkMap = ConcurrentHashMap \ No newline at end of file From 4931fa2a9d7c49b858f1cd462289997a0e10e1a2 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Fri, 14 Jun 2024 22:48:41 +0300 Subject: [PATCH 37/42] ChillyWeelly --- .../graphics/renderer/esp/ChunkedESP.kt | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index a319574ac..8a7dfa9a9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -5,16 +5,12 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.graphics.renderer.esp.DirectionMask.exclude -import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.module.modules.client.RenderSettings import com.lambda.threading.runGameBlocking import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import kotlinx.coroutines.* import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Box import net.minecraft.util.math.ChunkPos -import net.minecraft.util.math.Direction import net.minecraft.world.BlockView import net.minecraft.world.chunk.WorldChunk import java.awt.Color @@ -23,8 +19,7 @@ import java.util.concurrent.ConcurrentLinkedDeque class ChunkedESP private constructor( owner: Any, - private val filter: (BlockView, BlockPos) -> Boolean, - private val painter: (BlockView, BlockPos) -> Pair + private val update: EspRenderer.(BlockView, BlockPos) -> Boolean ) { private val rendererMap = ConcurrentHashMap() private val WorldChunk.renderer get() = rendererMap.getOrPut(pos) { @@ -33,7 +28,9 @@ class ChunkedESP private constructor( private var ticks = 0 private val uploadPool = ConcurrentLinkedDeque<() -> Unit>() - private val rebuildPool = ConcurrentLinkedDeque<() -> Unit>() + private val rebuildPool = ConcurrentLinkedDeque() + + // i completely dont like to listen to enable events fun clear() { rendererMap.clear() @@ -116,7 +113,7 @@ class ChunkedESP private constructor( } iterateChunk { x, y, z -> - checkAndDraw(newRenderer, BlockPos(x, y, z)) + draw(newRenderer, BlockPos(x, y, z)) } val upload = { @@ -147,19 +144,8 @@ class ChunkedESP private constructor( return result as R } - private fun checkAndDraw(renderer: EspRenderer, blockPos: BlockPos): Boolean { - if (!owner.filter(chunk, blockPos)) return false - - var sides = DirectionMask.ALL - - Direction.entries.forEach { - if (!owner.filter(chunk.world, blockPos.add(it.vector))) return@forEach - sides = sides.exclude(it.mask) - } - - val (filledColor, outlineColor) = owner.painter(chunk, blockPos) - renderer.build(Box(blockPos), filledColor, outlineColor, sides, DirectionMask.OutlineMode.AND) - return true + private fun draw(renderer: EspRenderer, x: Int, y: Int, z: Int) { + if (!owner.update(chunk, blockPos)) return false } private fun iterateChunk(block: (Int, Int, Int) -> Unit) = chunk.apply { From 974d9cd9bfcc267f2f83aa2d7db3d92687e01258 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Fri, 14 Jun 2024 22:48:54 +0300 Subject: [PATCH 38/42] ChillyWeelly --- .../graphics/renderer/esp/ChunkedESP.kt | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index 8a7dfa9a9..a1602d8f6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -7,8 +7,6 @@ import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.modules.client.RenderSettings import com.lambda.threading.runGameBlocking -import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall -import kotlinx.coroutines.* import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkPos import net.minecraft.world.BlockView @@ -39,7 +37,7 @@ class ChunkedESP private constructor( init { owner.concurrentListener { event -> world.getWorldChunk(event.pos).renderer.apply { - markOutdated() + queueRebuild() notifyNeighbors() } } @@ -55,7 +53,7 @@ class ChunkedESP private constructor( owner.concurrentListener { if (++ticks % RenderSettings.updateFrequency == 0) { rendererMap.values - .filter { it.outdated && it.neighborsLoaded } + .filter { it.neighborsLoaded } .forEach { it.rebuild() } ticks = 0 } @@ -95,13 +93,13 @@ class ChunkedESP private constructor( fun ChunkPos.isLoaded() = chunk.world.chunkManager.isChunkLoaded(x, z) - fun markOutdated() { - outdated = true + fun queueRebuild() { + owner.rebuildPool.add(this) } fun notifyNeighbors() { neighbors.forEach { - owner.rendererMap[it]?.markOutdated() + owner.rendererMap[it]?.queueRebuild() } } @@ -133,17 +131,6 @@ class ChunkedESP private constructor( } } - private suspend fun runOnMainThreadAndWait(block: () -> R): R { - var result: R? = null - - recordRenderCall { - result = block() - } - - while (result == null) delay(1) - return result as R - } - private fun draw(renderer: EspRenderer, x: Int, y: Int, z: Int) { if (!owner.update(chunk, blockPos)) return false } From 5212a3bde0a88b6c8bd66778d909fdbfd35e0c16 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 15 Jun 2024 01:21:08 +0200 Subject: [PATCH 39/42] Update scheduler --- .../graphics/renderer/esp/ChunkedESP.kt | 34 +++++++--------- .../module/modules/client/RenderSettings.kt | 1 + .../lambda/module/modules/render/BlockESP.kt | 40 +++++++++++++++++-- .../main/kotlin/com/lambda/util/BlockUtils.kt | 7 ++-- 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index a1602d8f6..5752a3a87 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -7,17 +7,15 @@ import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.modules.client.RenderSettings import com.lambda.threading.runGameBlocking -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkPos import net.minecraft.world.BlockView import net.minecraft.world.chunk.WorldChunk -import java.awt.Color import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque class ChunkedESP private constructor( owner: Any, - private val update: EspRenderer.(BlockView, BlockPos) -> Boolean + private val update: EspRenderer.(BlockView, Int, Int, Int) -> Unit ) { private val rendererMap = ConcurrentHashMap() private val WorldChunk.renderer get() = rendererMap.getOrPut(pos) { @@ -52,9 +50,11 @@ class ChunkedESP private constructor( owner.concurrentListener { if (++ticks % RenderSettings.updateFrequency == 0) { - rendererMap.values - .filter { it.neighborsLoaded } - .forEach { it.rebuild() } + val polls = minOf(RenderSettings.rebuildsPerTick, rebuildPool.size) + + repeat(polls) { + rebuildPool.poll()?.rebuild() + } ticks = 0 } } @@ -62,9 +62,11 @@ class ChunkedESP private constructor( owner.listener { if (uploadPool.isEmpty()) return@listener - uploadPool - .take(RenderSettings.uploadsPerTick) - .forEach { it() } + val polls = minOf(RenderSettings.uploadsPerTick, uploadPool.size) + + repeat(polls) { + uploadPool.poll()?.invoke() + } } owner.listener { @@ -76,14 +78,12 @@ class ChunkedESP private constructor( companion object { fun Any.newChunkedESP( - filter: (BlockView, BlockPos) -> Boolean, - painter: (BlockView, BlockPos) -> Pair - ) = ChunkedESP(this, filter, painter) + update: EspRenderer.(BlockView, Int, Int, Int) -> Unit + ) = ChunkedESP(this, update) } private class EspChunk(val chunk: WorldChunk, val owner: ChunkedESP) { var renderer: EspRenderer? = null - var outdated = true private val chunkOffsets = listOf(1 to 0, 0 to 1, -1 to 0, 0 to -1) val neighbors = chunkOffsets.map { @@ -104,14 +104,12 @@ class ChunkedESP private constructor( } suspend fun rebuild() { - outdated = false - val newRenderer = runGameBlocking { EspRenderer() } iterateChunk { x, y, z -> - draw(newRenderer, BlockPos(x, y, z)) + owner.update(newRenderer, chunk.world, x, y, z) } val upload = { @@ -131,10 +129,6 @@ class ChunkedESP private constructor( } } - private fun draw(renderer: EspRenderer, x: Int, y: Int, z: Int) { - if (!owner.update(chunk, blockPos)) return false - } - private fun iterateChunk(block: (Int, Int, Int) -> Unit) = chunk.apply { for (x in pos.startX..pos.endX) { for (z in pos.startZ..pos.endZ) { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt index a2f139459..d8e974bff 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt @@ -21,6 +21,7 @@ object RenderSettings : Module( // ESP val uploadScheduler by setting("Upload Scheduler", UploadScheduler.Instant) { page == Page.ESP } val uploadsPerTick by setting("Uploads", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } + val rebuildsPerTick by setting("Rebuilds", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP } val vertexMapping by setting("Vertex Mapping", true) { page == Page.ESP } val updateFrequency by setting("Update Frequency", 2, 1..10, 1, "Frequency of block updates", unit = " ticks") { page == Page.ESP } val outlineWidth by setting("Outline Width", 1.0, 0.1..5.0, 0.1, "Width of block outlines", unit = "px") { page == Page.ESP } diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index e3743eb0a..275dc653b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -1,11 +1,19 @@ package com.lambda.module.modules.render import com.lambda.Lambda.mc +import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.exclude +import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.BlockUtils.blockState +import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.client.render.model.BakedModel +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction import java.awt.Color object BlockESP : Module( @@ -37,6 +45,7 @@ object BlockESP : Module( } } } + private val blocks by setting("Blocks", setOf(Blocks.BEDROCK), "Render blocks") @JvmStatic val barrier by setting("Solid Barrier Block", true, "Render barrier blocks") @@ -48,10 +57,33 @@ object BlockESP : Module( @JvmStatic val model: BakedModel get() = mc.bakedModelManager.missingModel - private val esp = newChunkedESP( - { view, pos -> view.getBlockState(pos).block == Blocks.BEDROCK }, - { _, _ -> faceColor to outlineColor } - ) + private val esp = newChunkedESP { view, x, y, z -> + val blockPos = BlockPos(x, y, z) + val state = view.getBlockState(blockPos) + if (state.isAir) return@newChunkedESP + if (state.block !in blocks) return@newChunkedESP + + val shape = state.getOutlineShape(view, blockPos) + if (shape.isEmpty) return@newChunkedESP + + var sides = DirectionMask.ALL + + Direction.entries + .filter { blockPos.offset(it).blockState(view).block in blocks } + .forEach { sides = sides.exclude(it.mask) } + + shape.boundingBoxes.forEach { box -> + val offsetBox = box.offset(blockPos) + if (drawFaces) build(offsetBox, faceColor, outlineColor, sides, DirectionMask.OutlineMode.AND) + if (drawOutlines) buildOutline(offsetBox, outlineColor, sides, DirectionMask.OutlineMode.AND) + } + } + + fun SafeContext.getBlock(x: Int, y: Int, z: Int): Block { + val chunk = world.getChunk(x shr 4, z shr 4) + val section = chunk.getSection(y shr 4) + return section.getBlockState(x and 15, y and 15, z and 15).block + } init { onToggle { diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 581e60c7a..f81bfdfa0 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -11,6 +11,7 @@ import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids import net.minecraft.item.Item import net.minecraft.util.math.* +import net.minecraft.world.BlockView import kotlin.math.floor object BlockUtils { @@ -97,9 +98,9 @@ object BlockUtils { val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns - fun BlockPos.blockState(world: ClientWorld): BlockState = world.getBlockState(this) - fun BlockPos.fluidState(world: ClientWorld): FluidState = world.getFluidState(this) - fun BlockPos.blockEntity(world: ClientWorld) = world.getBlockEntity(this) + fun BlockPos.blockState(world: BlockView): BlockState = world.getBlockState(this) + fun BlockPos.fluidState(world: BlockView): FluidState = world.getFluidState(this) + fun BlockPos.blockEntity(world: BlockView) = world.getBlockEntity(this) fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative From bc9aa54ede805676e65e12de6737ac575144dbba Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 14 Jun 2024 19:21:46 -0400 Subject: [PATCH 40/42] Todo: Emoji chat renderer --- .../lambda/mixin/render/ChatScreenMixin.java | 30 ++++++++++++++ .../renderer/gui/font/FontRenderer.kt | 41 ++++++++++--------- .../graphics/renderer/gui/font/LambdaEmoji.kt | 2 +- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java b/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java index 545db5c37..874a45dd2 100644 --- a/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java @@ -1,14 +1,44 @@ package com.lambda.mixin.render; import com.lambda.command.CommandManager; +import com.lambda.graphics.renderer.gui.font.FontRenderer; +import com.lambda.graphics.renderer.gui.font.LambdaEmoji; +import com.lambda.graphics.renderer.gui.font.glyph.GlyphInfo; +import kotlin.Pair; +import kotlin.ranges.IntRange; import net.minecraft.client.gui.screen.ChatScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Collections; +import java.util.List; + @Mixin(ChatScreen.class) public abstract class ChatScreenMixin { + @ModifyArg(method = "sendMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendChatMessage(Ljava/lang/String;)V"), index = 0) + private String modifyChatText(String chatText) { + List> emojis = FontRenderer.Companion.parseEmojis(chatText, LambdaEmoji.Twemoji); + Collections.reverse(emojis); + + for (Pair emoji : emojis) { + String emojiString = chatText.substring(emoji.getSecond().getStart() + 1, emoji.getSecond().getEndInclusive()); + if (LambdaEmoji.Twemoji.get(emojiString) == null) + continue; + + // Because the width of a char is bigger than an emoji + // we can simply replace the matches string by a space + // and render it after the text + chatText = chatText.substring(0, emoji.getSecond().getStart()) + " " + chatText.substring(emoji.getSecond().getEndInclusive() + 1); + + // TODO: Build a renderer for the emojis + // TODO: Render the emojis at their correct position + } + + return chatText; + } @Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true) void sendMessageInject(String chatText, boolean addToHistory, CallbackInfoReturnable cir) { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index f53001eae..a70638a70 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -19,22 +19,6 @@ class FontRenderer( private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) private val scaleMultiplier = 1.0 - private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") - - /** - * Parses the emojis in the given text. - * - * @param text The text to parse. - * @return A list of pairs containing the glyph info and the range of the emoji in the text. - */ - fun parseEmojis(text: String) = - mutableListOf>().apply { - emojiRegex.findAll(text).forEach { match -> - val emojiKey = match.value.substring(1, match.value.length - 1) - val charInfo = emojis[emojiKey] ?: return@forEach - add(charInfo to match.range) - } - } /** * Builds the vertex array for rendering the text. @@ -101,7 +85,7 @@ class FontRenderer( var posX = 0.0 val posY = getHeight(scale) * -0.5 + baselineOffset * actualScale - val emojis = parseEmojis(text) + val emojis = parseEmojis(text, emojis) fun draw(info: GlyphInfo, color: Color, offset: Double = 0.0) { val scaledSize = info.size * actualScale @@ -170,8 +154,25 @@ class FontRenderer( companion object { private val shader = Shader("renderer/font") - private val shadowShift get() = RenderSettings.shadowShift * 5.0 - private val baselineOffset get() = RenderSettings.baselineOffset * 2.0f - 10f - private val gap get() = RenderSettings.gap * 0.5f - 0.8f + val shadowShift get() = RenderSettings.shadowShift * 5.0 + val baselineOffset get() = RenderSettings.baselineOffset * 2.0f - 10f + val gap get() = RenderSettings.gap * 0.5f - 0.8f + + private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") + + /** + * Parses the emojis in the given text. + * + * @param text The text to parse. + * @return A list of pairs containing the glyph info and the range of the emoji in the text. + */ + fun parseEmojis(text: String, emojis: LambdaEmoji) = + mutableListOf>().apply { + emojiRegex.findAll(text).forEach { match -> + val emojiKey = match.value.substring(1, match.value.length - 1) + val charInfo = emojis[emojiKey] ?: return@forEach + add(charInfo to match.range) + } + } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt index b8d22be9a..99ae00c4b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt @@ -6,7 +6,7 @@ import com.lambda.graphics.renderer.gui.font.glyph.EmojiGlyphs enum class LambdaEmoji(private val zipUrl: String) { Twemoji("https://github.com/Edouard127/emoji-generator/releases/latest/download/emojis.zip"); - lateinit var glyphs: EmojiGlyphs // TODO: Support multiple emoji pools + lateinit var glyphs: EmojiGlyphs operator fun get(emoji: String) = glyphs.getEmoji(emoji) From 405ff129abe0a36fb59654a3f65e2803e6672d96 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 15 Jun 2024 06:11:39 +0200 Subject: [PATCH 41/42] Fix rendering lines twice and more settings --- .../graphics/renderer/esp/ChunkStorage.kt | 6 +- .../graphics/renderer/esp/ChunkedESP.kt | 45 +++--- .../graphics/renderer/esp/EspRenderer.kt | 131 +++++++++++++++--- .../module/modules/client/RenderSettings.kt | 4 +- .../lambda/module/modules/render/BlockESP.kt | 84 ++++++----- 5 files changed, 194 insertions(+), 76 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt index 63d752834..1f7352d5b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkStorage.kt @@ -13,11 +13,11 @@ object ChunkStorage : Loadable { init { concurrentListener { event -> - chunkMap[event.chunk.pos] = event.chunk + chunkMap[event.chunk.pos.toLong()] = event.chunk } concurrentListener { event -> - chunkMap.remove(event.chunk.pos) + chunkMap.remove(event.chunk.pos.toLong()) } concurrentListener { @@ -26,4 +26,4 @@ object ChunkStorage : Loadable { } } -typealias ChunkMap = ConcurrentHashMap \ No newline at end of file +typealias ChunkMap = ConcurrentHashMap \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index 5752a3a87..2894a2664 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -8,64 +8,65 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.modules.client.RenderSettings import com.lambda.threading.runGameBlocking import net.minecraft.util.math.ChunkPos -import net.minecraft.world.BlockView +import net.minecraft.world.WorldView import net.minecraft.world.chunk.WorldChunk import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque class ChunkedESP private constructor( owner: Any, - private val update: EspRenderer.(BlockView, Int, Int, Int) -> Unit + private val update: EspRenderer.(WorldView, Int, Int, Int) -> Unit ) { - private val rendererMap = ConcurrentHashMap() - private val WorldChunk.renderer get() = rendererMap.getOrPut(pos) { + private val rendererMap = ConcurrentHashMap() + private val WorldChunk.renderer get() = rendererMap.getOrPut(pos.toLong()) { EspChunk(this, this@ChunkedESP) } private var ticks = 0 - private val uploadPool = ConcurrentLinkedDeque<() -> Unit>() - private val rebuildPool = ConcurrentLinkedDeque() + private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>() + private val rebuildQueue = ConcurrentLinkedDeque() // i completely dont like to listen to enable events - fun clear() { - rendererMap.clear() + fun rebuild() { + rebuildQueue.clear() + rebuildQueue.addAll(rendererMap.values) } init { - owner.concurrentListener { event -> + concurrentListener { event -> world.getWorldChunk(event.pos).renderer.apply { queueRebuild() notifyNeighbors() } } - owner.concurrentListener { event -> + concurrentListener { event -> event.chunk.renderer.notifyNeighbors() } - owner.concurrentListener { event -> - rendererMap.remove(event.chunk.pos)?.notifyNeighbors() + concurrentListener { event -> + rendererMap.remove(event.chunk.pos.toLong())?.notifyNeighbors() } owner.concurrentListener { if (++ticks % RenderSettings.updateFrequency == 0) { - val polls = minOf(RenderSettings.rebuildsPerTick, rebuildPool.size) + val polls = minOf(RenderSettings.rebuildsPerTick, rebuildQueue.size) repeat(polls) { - rebuildPool.poll()?.rebuild() + rebuildQueue.poll()?.rebuild() } ticks = 0 } } owner.listener { - if (uploadPool.isEmpty()) return@listener + if (uploadQueue.isEmpty()) return@listener - val polls = minOf(RenderSettings.uploadsPerTick, uploadPool.size) + val polls = minOf(RenderSettings.uploadsPerTick, uploadQueue.size) repeat(polls) { - uploadPool.poll()?.invoke() + uploadQueue.poll()?.invoke() } } @@ -78,7 +79,7 @@ class ChunkedESP private constructor( companion object { fun Any.newChunkedESP( - update: EspRenderer.(BlockView, Int, Int, Int) -> Unit + update: EspRenderer.(WorldView, Int, Int, Int) -> Unit ) = ChunkedESP(this, update) } @@ -94,12 +95,13 @@ class ChunkedESP private constructor( fun ChunkPos.isLoaded() = chunk.world.chunkManager.isChunkLoaded(x, z) fun queueRebuild() { - owner.rebuildPool.add(this) + if (owner.rebuildQueue.contains(this)) return + owner.rebuildQueue.add(this) } fun notifyNeighbors() { neighbors.forEach { - owner.rendererMap[it]?.queueRebuild() + owner.rendererMap[it.toLong()]?.queueRebuild() } } @@ -114,6 +116,7 @@ class ChunkedESP private constructor( val upload = { newRenderer.upload() + renderer?.clear() renderer = newRenderer } @@ -124,7 +127,7 @@ class ChunkedESP private constructor( } } RenderSettings.UploadScheduler.Delayed -> { - owner.uploadPool.add(upload) + owner.uploadQueue.add(upload) } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt index 0050621e0..c48351a89 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt @@ -22,6 +22,8 @@ import com.lambda.util.primitives.extension.min import net.minecraft.util.math.Box import java.awt.Color import java.util.concurrent.ConcurrentHashMap +import kotlin.math.abs +import kotlin.math.hypot class EspRenderer( usage: BufferUsage = BufferUsage.STATIC @@ -35,11 +37,102 @@ class EspRenderer( private var updateFilled = false private var updateOutline = false - fun build(box: Box, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) { + fun build( + box: Box, + filledColor: Color, + outlineColor: Color, + sides: Int = DirectionMask.ALL, + outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR + ) { buildFilled(box, filledColor, sides) buildOutline(box, outlineColor, sides, outlineMode) } + fun buildConvexHull( + boxes: Set, + color: Color + ) = outline.use { + updateOutline = true + + // Step 1: Collect all vertices + val vertices = mutableSetOf() + boxes.forEach { box -> + val pos1 = box.min + val pos2 = box.max + + vertices.add(Vertex(pos1.x, pos1.y, pos1.z, color)) + vertices.add(Vertex(pos1.x, pos1.y, pos2.z, color)) + vertices.add(Vertex(pos2.x, pos1.y, pos1.z, color)) + vertices.add(Vertex(pos2.x, pos1.y, pos2.z, color)) + vertices.add(Vertex(pos1.x, pos2.y, pos1.z, color)) + vertices.add(Vertex(pos1.x, pos2.y, pos2.z, color)) + vertices.add(Vertex(pos2.x, pos2.y, pos1.z, color)) + vertices.add(Vertex(pos2.x, pos2.y, pos2.z, color)) + } + + // Step 2: Find the convex hull + val convexHull = findConvexHull(vertices) + + // Step 3: Build outline of convex hull + for (i in convexHull.indices) { + val v1 = convexHull[i] + val v2 = convexHull[(i + 1) % convexHull.size] + val intV1 = vec3(v1.x, v1.y, v1.z).color(v1.color).end() + val intV2 = vec3(v2.x, v2.y, v2.z).color(v2.color).end() + putLine(intV1, intV2) + } + } + + // Utility function to find the convex hull using the QuickHull algorithm + private fun findConvexHull(vertices: Set): List { + if (vertices.size <= 1) return vertices.toList() + + val points = vertices.toMutableList() + points.sortWith(compareBy({ it.x }, { it.y })) + val left = points.first() + val right = points.last() + + val (leftSet, rightSet) = points.partition { isLeft(left, right, it) } + + val hull = mutableListOf() + hull.add(left) + hull.addAll(findHull(left, right, leftSet)) + hull.add(right) + hull.addAll(findHull(right, left, rightSet)) + + return hull + } + + // Check if the point is on the left side of the line from start to end + private fun isLeft(start: Vertex, end: Vertex, point: Vertex): Boolean { + return (end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x) > 0 + } + + // Recursively find the hull points + private fun findHull(start: Vertex, end: Vertex, points: List): List { + if (points.isEmpty()) return emptyList() + + val farthest = points.maxByOrNull { distanceFromLine(start, end, it) } ?: return emptyList() + + // Partition points into two sets: those to the left of the line (start, farthest) and those to the left of (farthest, end) + val (leftSetStartFarthest, _) = points.partition { isLeft(start, farthest, it) } + val (leftSetFarthestEnd, _) = points.partition { isLeft(farthest, end, it) } + + val hull = mutableListOf() + hull.addAll(findHull(start, farthest, leftSetStartFarthest)) + hull.add(farthest) + hull.addAll(findHull(farthest, end, leftSetFarthestEnd)) + + return hull + } + + // Calculate the distance from the line + private fun distanceFromLine(start: Vertex, end: Vertex, point: Vertex): Double { + val area = abs((end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x)) + val base = Math.hypot((end.x - start.x), (end.y - start.y)) + return area / base + } + fun buildFilled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = filled.use { updateFilled = true val pos1 = box.min @@ -47,14 +140,14 @@ class EspRenderer( grow(8) - val blb by vertex(filledVertices, pos1.x, pos1.y, pos1.z, color) - val blf by vertex(filledVertices, pos1.x, pos1.y, pos2.z, color) - val brb by vertex(filledVertices, pos2.x, pos1.y, pos1.z, color) - val brf by vertex(filledVertices, pos2.x, pos1.y, pos2.z, color) - val tlb by vertex(filledVertices, pos1.x, pos2.y, pos1.z, color) - val tlf by vertex(filledVertices, pos1.x, pos2.y, pos2.z, color) - val trb by vertex(filledVertices, pos2.x, pos2.y, pos1.z, color) - val trf by vertex(filledVertices, pos2.x, pos2.y, pos2.z, color) + val blb by vertex(pos1.x, pos1.y, pos1.z, color) + val blf by vertex(pos1.x, pos1.y, pos2.z, color) + val brb by vertex(pos2.x, pos1.y, pos1.z, color) + val brf by vertex(pos2.x, pos1.y, pos2.z, color) + val tlb by vertex(pos1.x, pos2.y, pos1.z, color) + val tlf by vertex(pos1.x, pos2.y, pos2.z, color) + val trb by vertex(pos2.x, pos2.y, pos1.z, color) + val trf by vertex(pos2.x, pos2.y, pos2.z, color) if (sides.hasDirection(EAST)) putQuad(brb, trb, trf, brf) if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb) @@ -71,14 +164,14 @@ class EspRenderer( grow(8) - val blb by vertex(outlineVertices, pos1.x, pos1.y, pos1.z, color) - val blf by vertex(outlineVertices, pos1.x, pos1.y, pos2.z, color) - val brb by vertex(outlineVertices, pos2.x, pos1.y, pos1.z, color) - val brf by vertex(outlineVertices, pos2.x, pos1.y, pos2.z, color) - val tlb by vertex(outlineVertices, pos1.x, pos2.y, pos1.z, color) - val tlf by vertex(outlineVertices, pos1.x, pos2.y, pos2.z, color) - val trb by vertex(outlineVertices, pos2.x, pos2.y, pos1.z, color) - val trf by vertex(outlineVertices, pos2.x, pos2.y, pos2.z, color) + val blb by vertex(pos1.x, pos1.y, pos1.z, color) + val blf by vertex(pos1.x, pos1.y, pos2.z, color) + val brb by vertex(pos2.x, pos1.y, pos1.z, color) + val brf by vertex(pos2.x, pos1.y, pos2.z, color) + val tlb by vertex(pos1.x, pos2.y, pos1.z, color) + val tlf by vertex(pos1.x, pos2.y, pos2.z, color) + val trb by vertex(pos2.x, pos2.y, pos1.z, color) + val trf by vertex(pos2.x, pos2.y, pos2.z, color) val hasEast = sides.hasDirection(EAST) val hasWest = sides.hasDirection(WEST) @@ -132,7 +225,6 @@ class EspRenderer( } private fun IRenderContext.vertex( - storage: MutableMap, x: Double, y: Double, z: Double, color: Color ) = lazy { @@ -141,11 +233,12 @@ class EspRenderer( } if (RenderSettings.vertexMapping) { - storage.getOrPut(Vertex(x, y, z, color), newVertex) + filledVertices.getOrPut(Vertex(x, y, z, color), newVertex) } else newVertex() } private data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) + private data class Edge(val vertex1: Vertex, val vertex2: Vertex) companion object { private val shader = Shader("renderer/pos_color", "renderer/box_static") diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt index d8e974bff..11a15a235 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt @@ -20,8 +20,8 @@ object RenderSettings : Module( // ESP val uploadScheduler by setting("Upload Scheduler", UploadScheduler.Instant) { page == Page.ESP } - val uploadsPerTick by setting("Uploads", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } - val rebuildsPerTick by setting("Rebuilds", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP } + val uploadsPerTick by setting("Uploads", 16, 1..256, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed } + val rebuildsPerTick by setting("Rebuilds", 64, 1..256, 1, unit = " chunk/tick") { page == Page.ESP } val vertexMapping by setting("Vertex Mapping", true) { page == Page.ESP } val updateFrequency by setting("Update Frequency", 2, 1..10, 1, "Frequency of block updates", unit = " ticks") { page == Page.ESP } val outlineWidth by setting("Outline Width", 1.0, 0.1..5.0, 0.1, "Width of block outlines", unit = "px") { page == Page.ESP } diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 275dc653b..9cee9c422 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -1,11 +1,11 @@ package com.lambda.module.modules.render import com.lambda.Lambda.mc -import com.lambda.context.SafeContext import com.lambda.graphics.renderer.esp.ChunkedESP.Companion.newChunkedESP import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.graphics.renderer.esp.DirectionMask.mask +import com.lambda.graphics.renderer.esp.EspRenderer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.blockState @@ -13,7 +13,9 @@ import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.client.render.model.BakedModel import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box import net.minecraft.util.math.Direction +import net.minecraft.world.WorldView import java.awt.Color object BlockESP : Module( @@ -23,29 +25,38 @@ object BlockESP : Module( ) { private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks").apply { onValueSet { _, to -> + esp.rebuild() if (!to) drawOutlines = true } } - val faceColor by setting("Face Color", Color(100, 150, 255, 128), "Color of the surfaces") { + private val faceColor: Color by setting("Face Color", Color(100, 150, 255, 51), "Color of the surfaces") { drawFaces + }.apply { + onValueSet { _, _ -> esp.rebuild() } } - private var drawOutlines by setting("Draw Outlines", true, "Draw outlines of blocks").apply { + private var drawOutlines: Boolean by setting("Draw Outlines", true, "Draw outlines of blocks").apply { onValueSet { _, to -> + esp.rebuild() if (!to) drawFaces = true } } - private val outlineColor by setting("Outline Color", Color(100, 150, 255), "Color of the outlines") { + private val outlineColor: Color by setting("Outline Color", Color(100, 150, 255, 128), "Color of the outlines") { drawOutlines + }.apply { + onValueSet { _, _ -> esp.rebuild() } } - private var clearRender: Boolean by setting("Clear Render", false, "Clear render after rendering blocks").apply { - onValueSet { _, to -> - if (to) { - esp.clear() - clearRender = false - } - } + private val outlineMode: DirectionMask.OutlineMode by setting("Outline Mode", DirectionMask.OutlineMode.AND, "Outline mode").apply { + onValueSet { _, _ -> esp.rebuild() } + } + private val mesh: Boolean by setting("Mesh", true, "Connect similar adjacent blocks").apply { + onValueSet { _, _ -> esp.rebuild() } + } + private val shaped: Boolean by setting("Shaped", false, "Render outline shape").apply { + onValueSet { _, _ -> esp.rebuild() } + } + private val blocks: Set by setting("Blocks", setOf(Blocks.ANVIL), "Render blocks").apply { + onValueSet { _, _ -> esp.rebuild() } } - private val blocks by setting("Blocks", setOf(Blocks.BEDROCK), "Render blocks") @JvmStatic val barrier by setting("Solid Barrier Block", true, "Render barrier blocks") @@ -57,37 +68,48 @@ object BlockESP : Module( @JvmStatic val model: BakedModel get() = mc.bakedModelManager.missingModel + init { + onToggle { + if (barrier) mc.worldRenderer.reload() + } + } + private val esp = newChunkedESP { view, x, y, z -> val blockPos = BlockPos(x, y, z) val state = view.getBlockState(blockPos) - if (state.isAir) return@newChunkedESP if (state.block !in blocks) return@newChunkedESP - val shape = state.getOutlineShape(view, blockPos) - if (shape.isEmpty) return@newChunkedESP - var sides = DirectionMask.ALL - Direction.entries - .filter { blockPos.offset(it).blockState(view).block in blocks } - .forEach { sides = sides.exclude(it.mask) } + if (mesh) { + Direction.entries + .filter { blockPos.offset(it).blockState(view).block in blocks } + .forEach { sides = sides.exclude(it.mask) } + } - shape.boundingBoxes.forEach { box -> - val offsetBox = box.offset(blockPos) - if (drawFaces) build(offsetBox, faceColor, outlineColor, sides, DirectionMask.OutlineMode.AND) - if (drawOutlines) buildOutline(offsetBox, outlineColor, sides, DirectionMask.OutlineMode.AND) + if (shaped) { + val shape = state.getOutlineShape(view, blockPos) + if (shape.isEmpty) return@newChunkedESP + val boxes = shape.boundingBoxes + .map { it.offset(blockPos) } + .toSet() + + buildConvexHull(boxes, outlineColor) + return@newChunkedESP } - } - fun SafeContext.getBlock(x: Int, y: Int, z: Int): Block { - val chunk = world.getChunk(x shr 4, z shr 4) - val section = chunk.getSection(y shr 4) - return section.getBlockState(x and 15, y and 15, z and 15).block + build(Box(blockPos), sides) } - init { - onToggle { - if (barrier) mc.worldRenderer.reload() + private fun EspRenderer.build( + box: Box, + sides: Int, + ) { + if (drawFaces) { + buildFilled(box, faceColor, sides) + } + if (drawOutlines) { + buildOutline(box, outlineColor, sides, outlineMode) } } } \ No newline at end of file From 16d51be0cd91a96fd0813ae667938d325c9429cd Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 16 Jun 2024 01:14:33 +0200 Subject: [PATCH 42/42] Shape mesh prototype --- .../graphics/renderer/esp/EspRenderer.kt | 212 +++++++++--------- .../lambda/module/modules/render/BlockESP.kt | 20 +- 2 files changed, 116 insertions(+), 116 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt index c48351a89..b0ef61969 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/EspRenderer.kt @@ -20,21 +20,20 @@ import com.lambda.module.modules.client.RenderSettings import com.lambda.util.primitives.extension.max import com.lambda.util.primitives.extension.min import net.minecraft.util.math.Box +import net.minecraft.util.math.Vec3d import java.awt.Color import java.util.concurrent.ConcurrentHashMap -import kotlin.math.abs -import kotlin.math.hypot class EspRenderer( usage: BufferUsage = BufferUsage.STATIC ) { - private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, usage, true) - private val filledVertices = ConcurrentHashMap() + private val faces = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, usage, true) + private val faceVertices = ConcurrentHashMap() - private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, usage, true) + private val outlines = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, usage, true) private val outlineVertices = ConcurrentHashMap() - private var updateFilled = false + private var updateFaces = false private var updateOutline = false fun build( @@ -48,93 +47,8 @@ class EspRenderer( buildOutline(box, outlineColor, sides, outlineMode) } - fun buildConvexHull( - boxes: Set, - color: Color - ) = outline.use { - updateOutline = true - - // Step 1: Collect all vertices - val vertices = mutableSetOf() - boxes.forEach { box -> - val pos1 = box.min - val pos2 = box.max - - vertices.add(Vertex(pos1.x, pos1.y, pos1.z, color)) - vertices.add(Vertex(pos1.x, pos1.y, pos2.z, color)) - vertices.add(Vertex(pos2.x, pos1.y, pos1.z, color)) - vertices.add(Vertex(pos2.x, pos1.y, pos2.z, color)) - vertices.add(Vertex(pos1.x, pos2.y, pos1.z, color)) - vertices.add(Vertex(pos1.x, pos2.y, pos2.z, color)) - vertices.add(Vertex(pos2.x, pos2.y, pos1.z, color)) - vertices.add(Vertex(pos2.x, pos2.y, pos2.z, color)) - } - - // Step 2: Find the convex hull - val convexHull = findConvexHull(vertices) - - // Step 3: Build outline of convex hull - for (i in convexHull.indices) { - val v1 = convexHull[i] - val v2 = convexHull[(i + 1) % convexHull.size] - val intV1 = vec3(v1.x, v1.y, v1.z).color(v1.color).end() - val intV2 = vec3(v2.x, v2.y, v2.z).color(v2.color).end() - putLine(intV1, intV2) - } - } - - // Utility function to find the convex hull using the QuickHull algorithm - private fun findConvexHull(vertices: Set): List { - if (vertices.size <= 1) return vertices.toList() - - val points = vertices.toMutableList() - points.sortWith(compareBy({ it.x }, { it.y })) - val left = points.first() - val right = points.last() - - val (leftSet, rightSet) = points.partition { isLeft(left, right, it) } - - val hull = mutableListOf() - hull.add(left) - hull.addAll(findHull(left, right, leftSet)) - hull.add(right) - hull.addAll(findHull(right, left, rightSet)) - - return hull - } - - // Check if the point is on the left side of the line from start to end - private fun isLeft(start: Vertex, end: Vertex, point: Vertex): Boolean { - return (end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x) > 0 - } - - // Recursively find the hull points - private fun findHull(start: Vertex, end: Vertex, points: List): List { - if (points.isEmpty()) return emptyList() - - val farthest = points.maxByOrNull { distanceFromLine(start, end, it) } ?: return emptyList() - - // Partition points into two sets: those to the left of the line (start, farthest) and those to the left of (farthest, end) - val (leftSetStartFarthest, _) = points.partition { isLeft(start, farthest, it) } - val (leftSetFarthestEnd, _) = points.partition { isLeft(farthest, end, it) } - - val hull = mutableListOf() - hull.addAll(findHull(start, farthest, leftSetStartFarthest)) - hull.add(farthest) - hull.addAll(findHull(farthest, end, leftSetFarthestEnd)) - - return hull - } - - // Calculate the distance from the line - private fun distanceFromLine(start: Vertex, end: Vertex, point: Vertex): Double { - val area = abs((end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x)) - val base = Math.hypot((end.x - start.x), (end.y - start.y)) - return area / base - } - - fun buildFilled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = filled.use { - updateFilled = true + fun buildFilled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = faces.use { + updateFaces = true val pos1 = box.min val pos2 = box.max @@ -157,7 +71,12 @@ class EspRenderer( if (sides.hasDirection(NORTH)) putQuad(blb, tlb, trb, brb) } - fun buildOutline(box: Box, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use { + fun buildOutline( + box: Box, + color: Color, + sides: Int = DirectionMask.ALL, + outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR + ) = outlines.use { updateOutline = true val pos1 = box.min val pos2 = box.max @@ -196,15 +115,95 @@ class EspRenderer( if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf) } + infix operator fun Vec3d.compareTo(other: Vec3d) = + lengthSquared().compareTo(other.lengthSquared()) + + fun buildMesh(boxes: Set, color: Color) { + val edges = hashMapOf() + val faces = hashMapOf() + + fun addFace(v1: Vec3d, v2: Vec3d, v3: Vec3d, v4: Vec3d) { + val face = Face(v1, v2, v3, v4) + faces[face] = faces.getOrDefault(face, 0) + 1 + } + + fun addEdge(v1: Vec3d, v2: Vec3d) { + val edge = if (v1 < v2) Edge(v1, v2) else Edge(v2, v1) + edges[edge] = edges.getOrDefault(edge, 0) + 1 + } + + boxes.forEach { box -> + val pos1 = box.min + val pos2 = box.max + + val blb = Vec3d(pos1.x, pos1.y, pos1.z) + val blf = Vec3d(pos1.x, pos1.y, pos2.z) + val brb = Vec3d(pos2.x, pos1.y, pos1.z) + val brf = Vec3d(pos2.x, pos1.y, pos2.z) + val tlb = Vec3d(pos1.x, pos2.y, pos1.z) + val tlf = Vec3d(pos1.x, pos2.y, pos2.z) + val trb = Vec3d(pos2.x, pos2.y, pos1.z) + val trf = Vec3d(pos2.x, pos2.y, pos2.z) + + addFace(blb, blf, brf, brb) + addFace(tlb, tlf, trf, trb) + addFace(blb, brb, trb, tlb) + addFace(blf, brf, trf, tlf) + addFace(blb, blf, tlf, tlb) + addFace(brb, brf, trf, trb) + + addEdge(tlb, trb) + addEdge(tlf, trf) + addEdge(tlb, tlf) + addEdge(trf, trb) + + addEdge(blb, brb) + addEdge(blf, brf) + addEdge(blb, blf) + addEdge(brb, brf) + + addEdge(tlb, blb) + addEdge(trb, brb) + addEdge(trf, brf) + addEdge(tlf, blf) + } + + this.faces.use { + updateFaces = true + faces.forEach { (face, count) -> + if (count % 2 == 0) return@forEach + grow(1) + putQuad( + vec3(face.v1.x, face.v1.y, face.v1.z).color(color).end(), + vec3(face.v2.x, face.v2.y, face.v2.z).color(color).end(), + vec3(face.v3.x, face.v3.y, face.v3.z).color(color).end(), + vec3(face.v4.x, face.v4.y, face.v4.z).color(color).end() + ) + } + } + + outlines.use { + updateOutline = true + edges.forEach { (edge, count) -> + if (count % 2 == 0) return@forEach + grow(1) + putLine( + vec3(edge.start.x, edge.start.y, edge.start.z).color(color).end(), + vec3(edge.end.x, edge.end.y, edge.end.z).color(color).end() + ) + } + } + } + fun upload() { - if (updateFilled) { - updateFilled = false - filled.upload() + if (updateFaces) { + updateFaces = false + faces.upload() } if (updateOutline) { updateOutline = false - outline.upload() + outlines.upload() } } @@ -212,16 +211,16 @@ class EspRenderer( shader.use() shader["u_CameraPosition"] = mc.gameRenderer.camera.pos - withFaceCulling(filled::render) - withLineWidth(RenderSettings.outlineWidth, outline::render) + withFaceCulling(faces::render) + withLineWidth(RenderSettings.outlineWidth, outlines::render) } fun clear() { - filledVertices.clear() + faceVertices.clear() outlineVertices.clear() - filled.clear() - outline.clear() + faces.clear() + outlines.clear() } private fun IRenderContext.vertex( @@ -233,12 +232,13 @@ class EspRenderer( } if (RenderSettings.vertexMapping) { - filledVertices.getOrPut(Vertex(x, y, z, color), newVertex) + faceVertices.getOrPut(Vertex(x, y, z, color), newVertex) } else newVertex() } - private data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) - private data class Edge(val vertex1: Vertex, val vertex2: Vertex) + data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) + data class Edge(val start: Vec3d, val end: Vec3d) + data class Face(val v1: Vec3d, val v2: Vec3d, val v3: Vec3d, val v4: Vec3d) companion object { private val shader = Shader("renderer/pos_color", "renderer/box_static") diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 9cee9c422..6627129ff 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -54,7 +54,7 @@ object BlockESP : Module( private val shaped: Boolean by setting("Shaped", false, "Render outline shape").apply { onValueSet { _, _ -> esp.rebuild() } } - private val blocks: Set by setting("Blocks", setOf(Blocks.ANVIL), "Render blocks").apply { + private val blocks: Set by setting("Blocks", setOf(Blocks.BEDROCK), "Render blocks").apply { onValueSet { _, _ -> esp.rebuild() } } @@ -79,14 +79,6 @@ object BlockESP : Module( val state = view.getBlockState(blockPos) if (state.block !in blocks) return@newChunkedESP - var sides = DirectionMask.ALL - - if (mesh) { - Direction.entries - .filter { blockPos.offset(it).blockState(view).block in blocks } - .forEach { sides = sides.exclude(it.mask) } - } - if (shaped) { val shape = state.getOutlineShape(view, blockPos) if (shape.isEmpty) return@newChunkedESP @@ -94,10 +86,18 @@ object BlockESP : Module( .map { it.offset(blockPos) } .toSet() - buildConvexHull(boxes, outlineColor) + buildMesh(boxes, outlineColor) return@newChunkedESP } + var sides = DirectionMask.ALL + + if (mesh) { + Direction.entries + .filter { blockPos.offset(it).blockState(view).block in blocks } + .forEach { sides = sides.exclude(it.mask) } + } + build(Box(blockPos), sides) }