diff --git a/common/src/main/java/com/lambda/mixin/render/GameRendererMixin.java b/common/src/main/java/com/lambda/mixin/render/GameRendererMixin.java index 00149bcf1..683686ee7 100644 --- a/common/src/main/java/com/lambda/mixin/render/GameRendererMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/GameRendererMixin.java @@ -2,7 +2,9 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.RenderEvent; +import com.lambda.graphics.RenderMain; import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,4 +18,9 @@ private void updateTargetedEntityInvoke(float tickDelta, CallbackInfo info) { info.cancel(); } } + + @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;FJZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lnet/minecraft/client/render/LightmapTextureManager;Lorg/joml/Matrix4f;)V", shift = At.Shift.AFTER)) + private void onRenderWorld(float tickDelta, long limitTime, MatrixStack matrix, CallbackInfo ci) { + RenderMain.render3D(matrix.peek().getPositionMatrix()); + } } diff --git a/common/src/main/kotlin/com/lambda/Lambda.kt b/common/src/main/kotlin/com/lambda/Lambda.kt index 13822b7da..514532eb7 100644 --- a/common/src/main/kotlin/com/lambda/Lambda.kt +++ b/common/src/main/kotlin/com/lambda/Lambda.kt @@ -3,10 +3,12 @@ package com.lambda import com.google.gson.Gson import com.google.gson.GsonBuilder import com.lambda.config.serializer.* +import com.lambda.config.serializer.gui.CustomModuleWindowSerializer import com.lambda.config.serializer.gui.ModuleTagSerializer import com.lambda.config.serializer.gui.TagWindowSerializer import com.lambda.core.Loader -import com.lambda.gui.impl.clickgui.windows.TagWindow +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.KeyCode import com.mojang.authlib.GameProfile @@ -28,6 +30,7 @@ object Lambda { val gson: Gson = GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(ModuleTag::class.java, ModuleTagSerializer) + .registerTypeAdapter(CustomModuleWindow::class.java, CustomModuleWindowSerializer) .registerTypeAdapter(TagWindow::class.java, TagWindowSerializer) .registerTypeAdapter(KeyCode::class.java, KeyCodeSerializer) .registerTypeAdapter(Color::class.java, ColorSerializer) diff --git a/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt b/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt index 8b6a2fc59..5a7497587 100644 --- a/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt @@ -6,4 +6,4 @@ import com.lambda.util.FolderRegister object GuiConfig : Configuration() { override val configName = "gui" override val primary = FolderRegister.config.resolve("$configName.json") -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt new file mode 100644 index 000000000..ba71fda64 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/CustomModuleWindowSerializer.kt @@ -0,0 +1,56 @@ +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.CustomModuleWindow +import com.lambda.module.ModuleRegistry +import com.lambda.util.math.Vec2d +import java.lang.reflect.Type + +object CustomModuleWindowSerializer : JsonSerializer, JsonDeserializer { + override fun serialize( + src: CustomModuleWindow?, + typeOfSrc: Type?, + context: JsonSerializationContext?, + ): JsonElement = src?.let { + JsonObject().apply { + addProperty("title", it.title) + add("modules", JsonArray().apply { + it.modules.forEach { + add(it.name) + } + }) + addProperty("width", it.width) + addProperty("height", it.height) + addProperty("isOpen", it.isOpen) + add("position", JsonArray().apply { + add(it.position.x) + add(it.position.y) + }) + } + } ?: JsonNull.INSTANCE + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ) = json?.asJsonObject?.let { + CustomModuleWindow( + it["title"].asString, + it["modules"].asJsonArray.mapNotNull { name -> + ModuleRegistry.modules.firstOrNull { module -> + module.name == name.asString + } + } as MutableList, + LambdaClickGui + ).apply { + width = it["width"].asDouble + height = it["height"].asDouble + isOpen = it["isOpen"].asBoolean + position = Vec2d( + it["position"].asJsonArray[0].asDouble, + it["position"].asJsonArray[1].asDouble + ) + } + } ?: throw JsonParseException("Invalid window data") +} \ No newline at end of file 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 337e34474..e9a3731a3 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 @@ -1,7 +1,8 @@ package com.lambda.config.serializer.gui import com.google.gson.* -import com.lambda.gui.impl.clickgui.windows.TagWindow +import com.lambda.gui.impl.clickgui.LambdaClickGui +import com.lambda.gui.impl.clickgui.windows.tag.TagWindow import com.lambda.module.tag.ModuleTag import com.lambda.util.math.Vec2d import java.lang.reflect.Type @@ -13,12 +14,7 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer - add(tag.name) - } - }) + addProperty("tag", it.tag.name) addProperty("width", it.width) addProperty("height", it.height) addProperty("isOpen", it.isOpen) @@ -33,15 +29,10 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer - ModuleTag(tag.asString) - }.toSet(), - title = it["title"].asString, - width = it["width"].asDouble, + ) = json?.asJsonObject?.let { + TagWindow(ModuleTag(it["tag"].asString), LambdaClickGui).apply { + width = it["width"].asDouble height = it["height"].asDouble - ).apply { isOpen = it["isOpen"].asBoolean position = Vec2d( it["position"].asJsonArray[0].asDouble, 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 50553e401..c4719ad0f 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/NumericSetting.kt @@ -31,6 +31,6 @@ abstract class NumericSetting( override fun toString() = "$value$unit" override operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { - value = valueIn.coerceIn(range).roundToStep(step) + value = valueIn.coerceIn(range) } } \ 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 151215ecd..ed0287463 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,6 +1,8 @@ package com.lambda.config.settings.comparable import com.lambda.config.AbstractSetting +import com.lambda.util.Nameable +import java.util.* class EnumSetting>( override val name: String, @@ -12,7 +14,13 @@ class EnumSetting>( description, visibility, ) { - private val enumValues: Array = defaultValue.declaringJavaClass.enumConstants + val displayValue get() = (value as? Nameable)?.name ?: value.name.split('_').joinToString(" ") { low -> + low.lowercase().replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } + } + + val enumValues: Array = defaultValue.declaringJavaClass.enumConstants fun next() { value = enumValues[((value.ordinal + 1) % enumValues.size)] diff --git a/common/src/main/kotlin/com/lambda/core/LambdaSound.kt b/common/src/main/kotlin/com/lambda/core/LambdaSound.kt new file mode 100644 index 000000000..edd499847 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/core/LambdaSound.kt @@ -0,0 +1,20 @@ +package com.lambda.core + +import com.lambda.core.SoundManager.toIdentifier +import net.minecraft.sound.SoundEvent +import net.minecraft.util.Identifier + +enum class LambdaSound(val id: Identifier) { + BUTTON_CLICK("button_click".toIdentifier()), + + BOOLEAN_SETTING_ON("bool_on".toIdentifier()), + BOOLEAN_SETTING_OFF("bool_off".toIdentifier()), + + MODULE_ON("module_on".toIdentifier()), + MODULE_OFF("module_off".toIdentifier()), + + SETTINGS_OPEN("settings_open".toIdentifier()), + SETTINGS_CLOSE("settings_close".toIdentifier()); + + val event: SoundEvent = SoundEvent.of(id) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index a5d82ca3d..33bb10418 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -6,14 +6,11 @@ import com.lambda.command.CommandManager import com.lambda.config.configurations.GuiConfig import com.lambda.friend.FriendManager import com.lambda.graphics.renderer.gui.font.LambdaFont -import com.lambda.gui.impl.clickgui.GuiConfigurable -import com.lambda.gui.impl.clickgui.LambdaClickGui +import com.lambda.gui.GuiConfigurable import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry import com.lambda.util.Communication.ascii -import com.mojang.authlib.GameProfile -import java.util.* import kotlin.system.measureTimeMillis object Loader { @@ -25,6 +22,7 @@ object Loader { LambdaFont.Loader, GuiConfigurable, FriendManager, + SoundManager, ) fun initialize() { @@ -33,7 +31,7 @@ object Loader { val initTime = measureTimeMillis { loadables.forEach { loadable -> - var info: String + val info: String val phaseTime = measureTimeMillis { info = loadable.load() } diff --git a/common/src/main/kotlin/com/lambda/core/SoundManager.kt b/common/src/main/kotlin/com/lambda/core/SoundManager.kt new file mode 100644 index 000000000..31a418abc --- /dev/null +++ b/common/src/main/kotlin/com/lambda/core/SoundManager.kt @@ -0,0 +1,37 @@ +package com.lambda.core + +import com.lambda.Lambda +import com.lambda.Lambda.mc +import com.lambda.util.math.random +import net.minecraft.client.sound.PositionedSoundInstance +import net.minecraft.registry.Registries +import net.minecraft.registry.Registry +import net.minecraft.sound.SoundEvent +import net.minecraft.util.Identifier + +object SoundManager : Loadable { + + override fun load(): String { + LambdaSound.entries.forEach { + Registry.register(Registries.SOUND_EVENT, it.id, it.event) + } + + return "Loaded ${LambdaSound.entries.size} sounds" + } + + fun playSound(event: SoundEvent, pitch: Double = 1.0) { + mc.soundManager.play( + PositionedSoundInstance.master(event, pitch.toFloat()) + ) + } + + fun playSoundRandomly(event: SoundEvent, pitch: Double = 1.0, pitchRange: Double = 0.05) { + val actualPitch = (pitch - pitchRange..pitch + pitchRange).random() + + mc.soundManager.play( + PositionedSoundInstance.master(event, actualPitch.toFloat()) + ) + } + + fun String.toIdentifier() = Identifier(Lambda.MOD_ID, this) +} \ No newline at end of file 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 178163df5..d222a7caa 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -9,11 +9,11 @@ import com.lambda.util.math.Vec2d abstract class RenderEvent : Event { class World : RenderEvent() - abstract class GUI(val scaleFactor: Double) : RenderEvent() { + abstract class GUI(val scale: Double) : RenderEvent() { class Scaled(scaleFactor: Double) : GUI(scaleFactor) class Fixed : GUI(1.0) - val screenSize = Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) / scaleFactor + 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/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index c3a935af3..24594a991 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -8,11 +8,15 @@ import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.math.Vec2d +import com.mojang.blaze3d.systems.RenderSystem.getProjectionMatrix import org.joml.Matrix4f object RenderMain { val projectionMatrix = Matrix4f() - val modelViewMatrix: Matrix4f get() = Matrices.stack.peek().positionMatrix + val modelViewMatrix: Matrix4f get() = Matrices.peek() + + var screenSize = Vec2d.ZERO @JvmStatic fun render2D() { @@ -28,6 +32,16 @@ object RenderMain { } } + @JvmStatic + fun render3D(matrix: Matrix4f) { + resetMatrix(matrix) + projectionMatrix.set(getProjectionMatrix()) + + setupGL { + RenderEvent.World().post() + } + } + private fun rescale(factor: Double) { val width = mc.window.framebufferWidth.toFloat() val height = mc.window.framebufferHeight.toFloat() @@ -35,6 +49,7 @@ object RenderMain { val scaledWidth = width / factor val scaledHeight = height / factor + screenSize = Vec2d(scaledWidth, scaledHeight) projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, 1000f, 21000f) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt index 6afd4908f..fcba00c2a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt +++ b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt @@ -33,6 +33,9 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { fun AnimationTicker.exp(min: Double, max: Double, speed: Double, flag: () -> Boolean) = exp({ min }, { max }, { speed }, flag) + fun AnimationTicker.exp(target: () -> Double, speed: Double) = + exp(target, target, { speed }, { true }) + @Suppress("NAME_SHADOWING") fun AnimationTicker.exp(min: () -> Double, max: () -> Double, speed: () -> Double, flag: () -> Boolean) = Animation(min()) { @@ -43,8 +46,8 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { else lerp(it, target, speed()) }.apply(::register) - // Exponent animation will never reach target value - private const val CLAMP = 0.001 + // Exponent animation never reaches target value + private const val CLAMP = 0.01 } } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt index d19a7ec69..cf849d16b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt @@ -5,6 +5,7 @@ import java.awt.Color interface IRenderContext { fun vec3(x: Double, y: Double, z: Double): IRenderContext fun vec2(x: Double, y: Double): IRenderContext + fun float(v: Double): IRenderContext fun color(color: Color): IRenderContext fun end(): Int 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 dfec78dc7..340a2af64 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 @@ -7,6 +7,7 @@ import com.lambda.graphics.gl.Memory.byteBuffer import com.lambda.graphics.gl.Memory.capacity import com.lambda.graphics.gl.Memory.color import com.lambda.graphics.gl.Memory.copy +import com.lambda.graphics.gl.Memory.float import com.lambda.graphics.gl.Memory.int import com.lambda.graphics.gl.Memory.vec2 import com.lambda.graphics.gl.Memory.vec3 @@ -18,6 +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.* @@ -88,6 +90,11 @@ class VAO( return this } + override fun float(v: Double): VAO { + verticesPosition += float(verticesPosition, v) + return this + } + override fun color(color: Color): VAO { verticesPosition += color(verticesPosition, color) return this @@ -191,8 +198,10 @@ class VAO( } fun destroy() { - glDeleteBuffers(ibo) - glDeleteBuffers(vbo) - glDeleteVertexArrays(vao) + runOnGameThread { + glDeleteBuffers(ibo) + glDeleteBuffers(vbo) + glDeleteVertexArrays(vao) + } } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt index 8085a20c4..87c5700fd 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt @@ -2,8 +2,10 @@ package com.lambda.graphics.buffer.vao.vertex import com.lambda.graphics.gl.GLObject import org.lwjgl.opengl.GL11C.* +import org.lwjgl.opengl.GL20C.GL_MAX_VERTEX_ATTRIBS enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normalized: Boolean, override val gl: Int) : GLObject { + Float(1, 4, false, GL_FLOAT), Vec2(2, 4, false, GL_FLOAT), Vec3(3, 4, false, GL_FLOAT), Color(4, 1, true, GL_UNSIGNED_BYTE); @@ -11,8 +13,15 @@ enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normali val size = componentCount * componentSize enum class Group(vararg val attributes: VertexAttrib) { - FONT(Vec2, Vec2, Color), - RECT(Vec2, Vec2, Vec3, Color); + // GUI + FONT(Vec2, Vec2, Color), // pos, uv, color + RECT_FILLED(Vec2, Vec2, Vec2, Float, Float, Color), // pos, uv, size, roundRadius, shade, color + RECT_OUTLINE(Vec2, Float, Float, Color), // pos, alpha, shade, color + BLUR(Vec2, Vec2), // pos, uv + + // WORLD + DYNAMIC_RENDERER(Vec3, Vec3, Color), // prev pos, pos, color + STATIC_RENDERER(Vec3, Color); // pos, color val stride = attributes.sumOf { attribute -> attribute.size } } 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 b0e474eed..71b463f83 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -1,35 +1,33 @@ package com.lambda.graphics.gl -import com.mojang.blaze3d.systems.RenderSystem -import com.mojang.blaze3d.systems.RenderSystem.depthMask -import org.lwjgl.opengl.GL30C +import org.lwjgl.opengl.GL30C.* -@Suppress("NOTHING_TO_INLINE") object GlStateUtils { private var depthTestState = true - private var depthMaskState = true private var blendState = false private var cullState = true fun setupGL(block: () -> Unit) { val savedDepthTest = depthTestState - val savedDepthMask = depthMaskState val savedBlend = blendState val savedCull = cullState + glDepthMask(false) + lineSmooth(true) + depthTest(false) - depthMask(false) blend(true) cull(false) - lineSmooth(true) + block() + glDepthMask(true) + lineSmooth(false) + depthTest(savedDepthTest) - depthMask(savedDepthMask) blend(savedBlend) cull(savedCull) - lineSmooth(false) } fun withDepth(block: () -> Unit) { @@ -41,35 +39,34 @@ object GlStateUtils { @JvmStatic fun capSet(id: Int, flag: Boolean) { val field = when (id) { - GL30C.GL_DEPTH_TEST -> ::depthTestState - GL30C.GL_DEPTH -> ::depthMaskState - GL30C.GL_BLEND -> ::blendState - GL30C.GL_CULL_FACE -> ::cullState + GL_DEPTH_TEST -> ::depthTestState + GL_BLEND -> ::blendState + GL_CULL_FACE -> ::cullState else -> return } field.set(flag) } - private inline fun blend(flag: Boolean) { + private fun blend(flag: Boolean) { if (flag) { - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - } else RenderSystem.disableBlend() + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + } else glDisable(GL_BLEND) } - private inline fun cull(flag: Boolean) { - if (flag) RenderSystem.enableCull() - else RenderSystem.disableCull() + private fun cull(flag: Boolean) { + if (flag) glEnable(GL_CULL_FACE) + else glDisable(GL_CULL_FACE) } - private inline fun depthTest(flag: Boolean) { - if (flag) RenderSystem.enableDepthTest() - else RenderSystem.disableDepthTest() + private fun depthTest(flag: Boolean) { + if (flag) glEnable(GL_DEPTH_TEST) + else glDisable(GL_DEPTH_TEST) } - private inline fun lineSmooth(flag: Boolean) { - if (flag) GL30C.glEnable(GL30C.GL_LINE_SMOOTH) - else GL30C.glDisable(GL30C.GL_LINE_SMOOTH) + private fun lineSmooth(flag: Boolean) { + if (flag) glEnable(GL_LINE_SMOOTH) + else glDisable(GL_LINE_SMOOTH) } -} \ No newline at end of file +} 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 1932ec531..10e9cc60c 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -1,33 +1,45 @@ package com.lambda.graphics.gl -import net.minecraft.client.util.math.MatrixStack +import org.joml.Matrix4f +import org.joml.Quaternionf +import kotlin.collections.ArrayDeque object Matrices { - var stack = MatrixStack() - private val matrix get() = stack.peek().positionMatrix + private val stack = ArrayDeque(listOf(Matrix4f())) - fun pushMatrix() { - stack.push() + fun translate(x: Double, y: Double, z: Double) { + translate(x.toFloat(), y.toFloat(), z.toFloat()) } - fun popMatrix() { - stack.push() + fun translate(x: Float, y: Float, z: Float) { + stack.last().translate(x, y, z) } - fun resetMatrix() { - stack = MatrixStack() + fun scale(x: Float, y: Float, z: Float) { + stack.last().scale(x, y, z) } - fun translate(x: Double, y: Double, z: Double = 0.0) { - matrix.translate(x.toFloat(), y.toFloat(), z.toFloat()) + fun multiply(quaternion: Quaternionf) { + stack.last().rotate(quaternion) } - fun scale(x: Double, y: Double, z: Double = 1.0) { - matrix.scale(x.toFloat(), y.toFloat(), z.toFloat()) + fun multiply(quaternion: Quaternionf, originX: Float, originY: Float, originZ: Float) { + stack.last().rotateAround(quaternion, originX, originY, originZ) } - fun scale(value: Double) { - val valueFloat = value.toFloat() - matrix.scale(valueFloat, valueFloat, valueFloat) + fun push() { + val entry = stack.last() + stack.addLast(Matrix4f(entry)) + } + + fun pop() { + stack.removeLast() + } + + fun peek() = stack.last() + + fun resetMatrix(entry: Matrix4f = Matrix4f()) { + stack.clear() + stack.add(entry) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt index 95374e8b8..d199980f5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt @@ -8,6 +8,7 @@ import java.nio.Buffer import java.nio.ByteBuffer object Memory { + private val floatSize = VertexAttrib.Float.size private val vec2Size = VertexAttrib.Vec2.size private val vec3Size = VertexAttrib.Vec3.size private val colorSize = VertexAttrib.Color.size @@ -41,8 +42,9 @@ object Memory { MemoryUtil.memPutInt(address, value) } - fun float(address: Long, value: Double) { + fun float(address: Long, value: Double): Int { MemoryUtil.memPutFloat(address, value.toFloat()) + return floatSize } fun address(buffer: Buffer): Long { 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 248975925..e8f936185 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt @@ -5,8 +5,8 @@ import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.Rect -import com.mojang.blaze3d.systems.RenderSystem.disableScissor -import com.mojang.blaze3d.systems.RenderSystem.enableScissor +import com.mojang.blaze3d.systems.RenderSystem +import org.lwjgl.opengl.GL30C.* import kotlin.math.max object Scissor { @@ -14,13 +14,14 @@ object Scissor { fun scissor(rect: Rect, block: () -> Unit) { // clamp corners so children scissor box can't overlap parent - val processed = stack.lastOrNull()?.let { rect.clamp(it) } ?: rect + val processed = stack.lastOrNull()?.let(rect::clamp) ?: rect registerScissor(processed, block) } private fun registerScissor(rect: Rect, block: () -> Unit) { - scissor(rect) + stack.add(rect) + scissor(rect) block() stack.removeLast() @@ -29,25 +30,23 @@ object Scissor { private fun scissor(entry: Rect?) { if (entry == null) { - disableScissor() + RenderSystem.disableScissor() return } - stack.add(entry) - val pos1 = entry.leftTop * GuiSettings.scale val pos2 = entry.rightBottom * GuiSettings.scale - val width = max(pos2.x - pos1.x, 0.0) - val height = max(pos2.y - pos1.y, 0.0) + val width = max(pos2.x - pos1.x, 1.0) + val height = max(pos2.y - pos1.y, 1.0) val y = mc.window.framebufferHeight - pos1.y - height - enableScissor( + RenderSystem.enableScissor( pos1.x.floorToInt(), y.floorToInt(), width.ceilToInt(), height.ceilToInt() ) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt index 4aa7d91ce..614082bfc 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt @@ -1,11 +1,9 @@ package com.lambda.graphics.gl -import com.mojang.blaze3d.platform.GlStateManager import net.minecraft.client.render.BufferRenderer import org.lwjgl.opengl.GL30C.* import java.nio.ByteBuffer -@Suppress("NOTHING_TO_INLINE") object VaoUtils { @JvmField var lastIbo = 0 private var prevIbo = 0 @@ -33,12 +31,12 @@ object VaoUtils { fun unbindIndexBuffer() = bindIndexBuffer(0) - inline fun enableVertexAttribute(i: Int) = + fun enableVertexAttribute(i: Int) = glEnableVertexAttribArray(i) - inline fun vertexAttribute(index: Int, size: Int, type: Int, normalized: Boolean, stride: Int, pointer: Long) = + fun vertexAttribute(index: Int, size: Int, type: Int, normalized: Boolean, stride: Int, pointer: Long) = glVertexAttribPointer(index, size, type, normalized, stride, pointer) - inline fun bufferData(target: Int, data: ByteBuffer, usage: Int) = - GlStateManager._glBufferData(target, data, usage) -} \ No newline at end of file + fun bufferData(target: Int, data: ByteBuffer, usage: Int) = + glBufferData(target, data, usage) +} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt index a806576ae..0c6c38b83 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -1,31 +1,38 @@ package com.lambda.graphics.renderer import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.gl.Matrices +import com.lambda.graphics.shader.Shader +import com.lambda.util.math.Vec2d import kotlinx.coroutines.* import kotlin.properties.Delegates -abstract class Renderer > : IRenderer { +abstract class Renderer > ( + protected val shader: Shader +) : IRenderer { private val entrySet = mutableSetOf() - private var rebuild = false + protected var rebuild = false private var destroyed = false val asRenderer get() = this as IRenderer + // Optimization tweak to reduce build calls when whole renderer moves + // Instead of rebuilding all entries you translate the matrix + var matrixOffset = Vec2d.ZERO + abstract val vao: VAO protected abstract fun newEntry(block: T.() -> Unit): T override fun build(block: T.() -> Unit): T { - checkDestroyed() return newEntry(block).process(entrySet::add) } override fun remove(entry: T): T { - checkDestroyed() return entry.process(entrySet::remove) } override fun render() { - checkDestroyed() + if (destroyed) return if (rebuild) { rebuild = false @@ -35,23 +42,32 @@ abstract class Renderer > : IRenderer { vao.upload() } + Matrices.push() + Matrices.translate(matrixOffset.x, matrixOffset.y, 0.0) + + shader.use() + preRender() vao.render() + + Matrices.pop() } + protected open fun preRender() {} + override fun update() { - checkDestroyed() + if (destroyed) return entrySet.forEach(IRenderEntry::update) } override fun clear() { - checkDestroyed() + if (destroyed) return entrySet.clear() vao.clear() } override fun destroy() { - checkDestroyed() + if (destroyed) return entrySet.clear() vao.destroy() @@ -70,8 +86,4 @@ abstract class Renderer > : IRenderer { if (prev == curr) return@observable rebuild = true } - - private fun checkDestroyed() { - check(!destroyed) { "Using the renderer after it is destroyed" } - } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt index a074c5801..7ffe72e5a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt @@ -3,11 +3,37 @@ package com.lambda.graphics.renderer.gui 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.Matrices import com.lambda.graphics.renderer.Renderer import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.graphics.shader.Shader +import com.lambda.util.TransformedObservable +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d abstract class AbstractGuiRenderer > ( - vertexType: VertexAttrib.Group -) : Renderer() { + vertexType: VertexAttrib.Group, + shader: Shader +) : Renderer(shader) { override val vao = VAO(VertexMode.TRIANGLES, vertexType) + + fun positionVec2() = + object : TransformedObservable(Vec2d.ZERO) { + override fun transform(value: Vec2d) = + value - matrixOffset + + override fun onChange(oldValue: Vec2d, newValue: Vec2d) { + rebuild = true + } + } + + fun positionRect() = + object : TransformedObservable(Rect.ZERO) { + override fun transform(value: Rect) = + value - matrixOffset + + override fun onChange(oldValue: Rect, newValue: Rect) { + rebuild = true + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt index ade50fe8e..ea0da8fb7 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -13,7 +13,7 @@ class FontEntry( private val font: LambdaFont ) : IFontEntry { override var text by owner.field("") - override var position by owner.field(Vec2d.ZERO) + override var position by owner.positionVec2() override var color by owner.field(Color.WHITE) override var scale by owner.field(1.0) @@ -37,7 +37,7 @@ class FontEntry( return width * actualScale } - override val height: Double + override val stringHeight: Double get() = font.glyphs.fontHeight * actualScale * 0.7 override fun build(ctx: IRenderContext) = ctx.use { @@ -46,7 +46,7 @@ class FontEntry( val shadowColor = getShadowColor(color) var posX = 0.0 - val posY = height * -0.5 + baselineOffset * actualScale + val posY = stringHeight * -0.5 + baselineOffset * actualScale text.toCharArray().forEach { char -> val charInfo = font[char] ?: return@forEach @@ -109,9 +109,9 @@ interface IFontEntry : IRenderEntry { fun getWidth(text: String): Double - val width get() = getWidth(text) - val height: Double + val stringWidth get() = getWidth(text) + val stringHeight: Double - val widthVec get() = Vec2d(width, 0.0) - val heightVec get() = Vec2d(0.0, height) + val widthVec get() = Vec2d(stringWidth, 0.0) + val heightVec get() = Vec2d(0.0, stringHeight) } 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 a75513daf..754728d0e 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,12 +5,10 @@ import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader class FontRenderer(private val font: LambdaFont = LambdaFont.FiraSansRegular) : AbstractGuiRenderer( - VertexAttrib.Group.FONT + VertexAttrib.Group.FONT, shader ) { - override fun render() { - shader.use() + override fun preRender() { font.glyphs.bind() - super.render() } override fun newEntry(block: IFontEntry.() -> Unit) = 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 0e4a98466..b6abde4ec 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 @@ -30,13 +30,12 @@ class FontGlyphs(font: Font) { var y = 0 var rowHeight = 0 - // Because UTF16 takes 2 bytes per character, we can't use the full range of characters - (Char.MIN_VALUE.. + (Char.MIN_VALUE.. val charImage = getCharImage(font, char) ?: return@forEach - rowHeight = max(rowHeight, charImage.height) + rowHeight = max(rowHeight, charImage.height + STEP) - if (x + charImage.width >= TEXTURE_SIZE) { + if (x + charImage.width + STEP >= TEXTURE_SIZE) { y += rowHeight x = 0 rowHeight = 0 @@ -53,7 +52,7 @@ class FontGlyphs(font: Font) { charMap[char.code] = CharInfo(size, uv1, uv2) fontHeight = max(fontHeight, size.y) - x += charImage.width + x += charImage.width + STEP } fontTexture = MipmapTexture(image) @@ -73,13 +72,12 @@ class FontGlyphs(font: Font) { charMap[char.code] companion object { - // The size cannot be bigger than 2^15 because the rasterizer needs to be fed with dimensions that when multiplied together are less than 2^31 - // This can be bypassed by using a custom rasterizer, but it's not worth the effort - // The size is also limited by the java heap size, as the image is stored in memory - // and then uploaded to the GPU // 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 val TEXTURE_SIZE = FontSettings.amountOfGlyphs * 2 - private val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE + private const val CHAR_AMOUNT = 2048 + private const val TEXTURE_SIZE = 4096 + 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 } } 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 new file mode 100644 index 000000000..0a6541724 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/AbstractRectRenderer.kt @@ -0,0 +1,25 @@ +package com.lambda.graphics.renderer.gui.rect + +import com.lambda.Lambda.mc +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.graphics.renderer.gui.AbstractGuiRenderer +import com.lambda.graphics.shader.Shader +import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.math.Vec2d +import org.lwjgl.glfw.GLFW.glfwGetTime + +abstract class AbstractRectRenderer > ( + vertexType: VertexAttrib.Group, + shader: Shader +) : AbstractGuiRenderer(vertexType, shader) { + override fun preRender() { + 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 + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/IRectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/IRectEntry.kt new file mode 100644 index 000000000..ace898a07 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/IRectEntry.kt @@ -0,0 +1,23 @@ +package com.lambda.graphics.renderer.gui.rect + +import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.util.math.Rect +import java.awt.Color + +interface IRectEntry > : IRenderEntry { + var position: Rect + var roundRadius: Double + var shade: Boolean + + fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) + fun color(color: Color) = color(color, color, color, color) + fun colorH(left: Color, right: Color) = color(left, right, right, left) + fun colorV(top: Color, bottom: Color) = color(top, top, bottom, bottom) + + interface Filled : IRectEntry + + interface Outline : IRectEntry { + var outerGlow: Double + var innerGlow: Double + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt deleted file mode 100644 index 085aaf4eb..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.lambda.graphics.renderer.gui.rect - -import com.lambda.graphics.buffer.vao.IRenderContext -import com.lambda.graphics.renderer.IRenderEntry -import com.lambda.util.math.Rect -import java.awt.Color -import kotlin.math.min - -class RectEntry( - override val owner: RectRenderer, - override val updateBlock: IRectEntry.() -> Unit -) : IRectEntry { - override var position by owner.field(Rect.ZERO) - override var roundRadius by owner.field(0.0) - - private var leftTop by owner.field(Color.WHITE!!) - private var rightTop by owner.field(Color.WHITE!!) - private var rightBottom by owner.field(Color.WHITE!!) - private var leftBottom by owner.field(Color.WHITE!!) - - override fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) { - this.leftTop = leftTop - this.rightTop = rightTop - this.rightBottom = rightBottom - this.leftBottom = leftBottom - } - - override fun build(ctx: IRenderContext) = ctx.use { - if (leftTop .alpha < MIN_ALPHA && - rightTop .alpha < MIN_ALPHA && - rightBottom.alpha < MIN_ALPHA && - leftBottom .alpha < MIN_ALPHA - ) return@use - - val pos1 = position.leftTop - val pos2 = position.rightBottom - - val size = pos2 - pos1 - if (size.x < MIN_SIZE || size.y < MIN_SIZE) return@use - - val halfSize = size * 0.5 - val maxRadius = min(halfSize.x, halfSize.y) - - val round = min(roundRadius, maxRadius) - - val p1 = pos1 - 0.75 - val p2 = pos2 + 0.75 - - grow(4) - - putQuad( - vec2(p1.x, p1.y).vec2(0.0, 0.0).vec3(size.x, size.y, round).color(leftTop).end(), - vec2(p1.x, p2.y).vec2(0.0, 1.0).vec3(size.x, size.y, round).color(leftBottom).end(), - vec2(p2.x, p2.y).vec2(1.0, 1.0).vec3(size.x, size.y, round).color(rightBottom).end(), - vec2(p2.x, p1.y).vec2(1.0, 0.0).vec3(size.x, size.y, round).color(rightTop).end() - ) - } - - companion object { - private const val MIN_ALPHA = 5 - private const val MIN_SIZE = 0.5 - } -} - -interface IRectEntry : IRenderEntry { - var position: Rect - var roundRadius: Double - - fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) - - fun color(color: Color) = color(color, color, color, color) - - fun colorH(left: Color, right: Color) = color(left, right, right, left) - - fun colorV(top: Color, bottom: Color) = color(top, top, bottom, bottom) -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt deleted file mode 100644 index 5ba178377..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.lambda.graphics.renderer.gui.rect - -import com.lambda.graphics.buffer.vao.vertex.VertexAttrib -import com.lambda.graphics.renderer.gui.AbstractGuiRenderer -import com.lambda.graphics.shader.Shader -import com.lambda.module.modules.client.GuiSettings -import com.lambda.util.math.Vec2d -import com.mojang.blaze3d.systems.RenderSystem.blendFunc -import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc -import org.lwjgl.glfw.GLFW.glfwGetTime -import org.lwjgl.opengl.GL11.GL_ONE -import org.lwjgl.opengl.GL11.GL_SRC_ALPHA - -class RectRenderer : AbstractGuiRenderer( - VertexAttrib.Group.RECT -) { - var shadeColor = false - var fancyBlending = false - - override fun render() { - shader.use() - - shader["u_Shade"] = shadeColor - - if (shadeColor) { - shader["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 - shader["u_Color1"] = GuiSettings.shadeColor1 - shader["u_Color2"] = GuiSettings.shadeColor2 - shader["u_Size"] = Vec2d.ONE / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) - } - - if (fancyBlending) blendFunc(GL_SRC_ALPHA, GL_ONE) - super.render() - defaultBlendFunc() - } - - override fun build(block: IRectEntry.() -> Unit): IRectEntry { - shadeColor = false - return super.build(block) - } - - override fun newEntry(block: IRectEntry.() -> Unit) = - RectEntry(this, block) - - companion object { - private val shader = Shader("renderer/rect") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectEntry.kt new file mode 100644 index 000000000..431c089a1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectEntry.kt @@ -0,0 +1,60 @@ +package com.lambda.graphics.renderer.gui.rect.filled + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.renderer.gui.rect.IRectEntry +import com.lambda.util.math.MathUtils.toInt +import java.awt.Color +import kotlin.math.min + +class FilledRectEntry( + override val owner: FilledRectRenderer, + override val updateBlock: IRectEntry.Filled.() -> Unit +) : IRectEntry.Filled { + override var position by owner.positionRect() + override var roundRadius by owner.field(0.0) + override var shade by owner.field(false) + + private var leftTop by owner.field(Color.WHITE) + private var rightTop by owner.field(Color.WHITE) + private var rightBottom by owner.field(Color.WHITE) + private var leftBottom by owner.field(Color.WHITE) + + override fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) { + this.leftTop = leftTop + this.rightTop = rightTop + this.rightBottom = rightBottom + this.leftBottom = leftBottom + } + + override fun build(ctx: IRenderContext) = ctx.use { + val pos1 = position.leftTop + val pos2 = position.rightBottom + + val size = pos2 - pos1 + if (leftTop.alpha < MIN_ALPHA && rightTop.alpha < MIN_ALPHA && rightBottom.alpha < MIN_ALPHA && leftBottom.alpha < MIN_ALPHA) return@use + if (size.x < MIN_SIZE || size.y < MIN_SIZE) return@use + + val halfSize = size * 0.5 + val maxRadius = min(halfSize.x, halfSize.y) + + val round = min(roundRadius, maxRadius) + + val p1 = pos1 - 0.25 + val p2 = pos2 + 0.25 + val s = shade.toInt().toDouble() + + grow(4) + + putQuad( + vec2(p1.x, p1.y).vec2(0.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(leftTop).end(), + vec2(p1.x, p2.y).vec2(0.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(leftBottom).end(), + vec2(p2.x, p2.y).vec2(1.0, 1.0).vec2(size.x, size.y).float(round).float(s).color(rightBottom).end(), + vec2(p2.x, p1.y).vec2(1.0, 0.0).vec2(size.x, size.y).float(round).float(s).color(rightTop).end() + ) + } + + companion object { + private const val MIN_SIZE = 0.5 + private const val MIN_ALPHA = 3 + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectRenderer.kt new file mode 100644 index 000000000..3100e8e28 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/filled/FilledRectRenderer.kt @@ -0,0 +1,17 @@ +package com.lambda.graphics.renderer.gui.rect.filled + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.renderer.gui.rect.AbstractRectRenderer +import com.lambda.graphics.renderer.gui.rect.IRectEntry +import com.lambda.graphics.shader.Shader + +class FilledRectRenderer : AbstractRectRenderer( + VertexAttrib.Group.RECT_FILLED, shader +) { + override fun newEntry(block: IRectEntry.Filled.() -> Unit) = + FilledRectEntry(this, block) + + companion object { + private val shader = Shader("renderer/rect_filled") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectEntry.kt new file mode 100644 index 000000000..00a828496 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectEntry.kt @@ -0,0 +1,91 @@ +package com.lambda.graphics.renderer.gui.rect.outline + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.renderer.gui.rect.IRectEntry +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.MathUtils.toInt +import com.lambda.util.math.MathUtils.toRadian +import com.lambda.util.math.Vec2d +import java.awt.Color +import kotlin.math.cos +import kotlin.math.min +import kotlin.math.sin + +class OutlineRectEntry( + override val owner: OutlineRectRenderer, + override val updateBlock: IRectEntry.Outline.() -> Unit +) : IRectEntry.Outline { + override var position by owner.positionRect() + override var roundRadius by owner.field(0.0) + override var shade by owner.field(false) + + override var outerGlow by owner.field(1.0) + override var innerGlow by owner.field(1.0) + + private var leftTop by owner.field(Color.WHITE) + private var rightTop by owner.field(Color.WHITE) + private var rightBottom by owner.field(Color.WHITE) + private var leftBottom by owner.field(Color.WHITE) + + override fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) { + this.leftTop = leftTop + this.rightTop = rightTop + this.rightBottom = rightBottom + this.leftBottom = leftBottom + } + + private val quality = 8 + private val verticesCount = quality * 4 + + override fun build(ctx: IRenderContext) = ctx.use { + grow(verticesCount * 3) + + val main = genVertices(0.0, false) + val outer = genVertices(outerGlow, true) + val inner = genVertices(-innerGlow, true) + + fun drawStripWith(vertices: MutableList) { + var prev = main.last() to vertices.last() + repeat(verticesCount) { + val new = main[it] to vertices[it] + putQuad(new.first, new.second, prev.second, prev.first) + prev = new + } + } + + drawStripWith(outer) + drawStripWith(inner) + } + + private fun IRenderContext.genVertices(size: Double, isGlow: Boolean): MutableList { + val r = position.expand(size) + val a = (!isGlow).toInt().toDouble() + + val halfSize = r.size * 0.5 + val maxRadius = min(halfSize.x, halfSize.y) - 0.5 + val round = (roundRadius + size).coerceAtMost(maxRadius) + + fun MutableList.buildCorners(base: Vec2d, c: Color, angleRange: IntRange) = repeat(quality) { + val min = angleRange.first.toDouble() + val max = angleRange.last.toDouble() + val p = it.toDouble() / quality + val angle = lerp(min, max, p).toRadian() + + val pos = base + Vec2d(cos(angle), -sin(angle)) * round + val s = shade.toInt().toDouble() + add(vec2(pos.x, pos.y).float(a).float(s).color(c).end()) + } + + val rt = r.rightTop + Vec2d(-round, round) + val lt = r.leftTop + Vec2d( round, round) + val lb = r.leftBottom + Vec2d( round, -round) + val rb = r.rightBottom + Vec2d(-round, -round) + + return mutableListOf().apply { + buildCorners(rt, rightTop, 0..90) + buildCorners(lt, leftTop, 90..180) + buildCorners(lb, leftBottom, 180..270) + buildCorners(rb, rightBottom, 270..360) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectRenderer.kt new file mode 100644 index 000000000..9c2c550da --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/outline/OutlineRectRenderer.kt @@ -0,0 +1,17 @@ +package com.lambda.graphics.renderer.gui.rect.outline + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.renderer.gui.rect.AbstractRectRenderer +import com.lambda.graphics.renderer.gui.rect.IRectEntry +import com.lambda.graphics.shader.Shader + +class OutlineRectRenderer : AbstractRectRenderer ( + VertexAttrib.Group.RECT_OUTLINE, shader +) { + override fun newEntry(block: IRectEntry.Outline.() -> Unit) = + OutlineRectEntry(this, block) + + companion object { + private val shader = Shader("renderer/rect_outline") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/immediate/BlurPostProcessor.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/immediate/BlurPostProcessor.kt new file mode 100644 index 000000000..e4d8ff863 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/immediate/BlurPostProcessor.kt @@ -0,0 +1,54 @@ +package com.lambda.graphics.renderer.immediate + +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.shader.Shader +import com.lambda.graphics.texture.TextureUtils.bindTexture +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d + +object BlurPostProcessor { + private val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.BLUR) + private val shader = Shader("post/blur") + + fun render(rect: Rect, level: Int, alpha: Double) { + if (level <= 0 || alpha <= 0.1) return + renderPass(rect, Vec2d.RIGHT, level, alpha) + renderPass(rect, Vec2d.BOTTOM, level, alpha) + } + + private fun renderPass(rect: Rect, direction: Vec2d, level: Int, alpha: Double) { + val x1 = rect.leftTop.x + val y1 = rect.leftTop.y + val x2 = rect.rightBottom.x + val y2 = rect.rightBottom.y + + val screen = RenderMain.screenSize + val uv1x = x1 / screen.x + val uv1y = y1 / screen.y + val uv2x = x2 / screen.x + val uv2y = y2 / screen.y + + vao.use { + putQuad( + vec2(x1, y1).vec2(uv1x, 1.0 - uv1y).end(), + vec2(x2, y1).vec2(uv2x, 1.0 - uv1y).end(), + vec2(x2, y2).vec2(uv2x, 1.0 - uv2y).end(), + vec2(x1, y2).vec2(uv1x, 1.0 - uv2y).end() + ) + + shader.use() + shader["u_Direction"] = direction / Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) + shader["u_BlurLevel"] = level + shader["u_Alpha"] = alpha + + bindTexture(mc.framebuffer.colorAttachment) + upload() + render() + clear() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/AbstractEspRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/AbstractEspRenderer.kt new file mode 100644 index 000000000..6c168ef80 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/AbstractEspRenderer.kt @@ -0,0 +1,27 @@ +package com.lambda.graphics.renderer.world + +import com.lambda.Lambda.mc +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.IRenderEntry +import com.lambda.graphics.renderer.Renderer +import com.lambda.graphics.shader.Shader +import com.lambda.util.primitives.extension.partialTicks +import net.minecraft.util.math.Vec3d + +abstract class AbstractEspRenderer > ( + vertexMode: VertexMode, + shader: Shader, + private val dynamic: Boolean +) : Renderer(shader) { + override val vao = VAO(vertexMode, + if (dynamic) VertexAttrib.Group.DYNAMIC_RENDERER + else VertexAttrib.Group.STATIC_RENDERER + ) + + override fun preRender() { + if (dynamic) shader["u_TickDelta"] = mc.partialTicks + shader["u_CameraPosition"] = mc.gameRenderer.camera.pos + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/DirectionMask.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/DirectionMask.kt new file mode 100644 index 000000000..957f60aa4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/DirectionMask.kt @@ -0,0 +1,34 @@ +package com.lambda.graphics.renderer.world + +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) { + AND(Boolean::and), + OR(Boolean::or) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/IESPEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/IESPEntry.kt new file mode 100644 index 000000000..ef00cd14b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/IESPEntry.kt @@ -0,0 +1,25 @@ +package com.lambda.graphics.renderer.world.core + +import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.graphics.renderer.world.DirectionMask +import net.minecraft.util.math.Box +import net.minecraft.util.math.Vec3d +import java.awt.Color + +interface IESPEntry > : IRenderEntry { + var color: Color + + interface IBoxEntry > : IESPEntry { + var box: Box + var sides: Int + + interface Filled : IBoxEntry + interface Outline : IBoxEntry { + var outlineMode: DirectionMask.OutlineMode + } + } + + interface ITracerEntry : IESPEntry { + var position: Vec3d + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicFilledRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicFilledRenderer.kt new file mode 100644 index 000000000..4a43113ab --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicFilledRenderer.kt @@ -0,0 +1,70 @@ +package com.lambda.graphics.renderer.world.core.box + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.DirectionMask +import com.lambda.graphics.renderer.world.DirectionMask.hasDirection +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +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 + +class DynamicFilledRenderer : AbstractEspRenderer( + VertexMode.TRIANGLES, shader, true +) { + override fun newEntry(block: IESPEntry.IBoxEntry.Filled.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: DynamicFilledRenderer, + override val updateBlock: IESPEntry.IBoxEntry.Filled.() -> Unit + ) : IESPEntry.IBoxEntry.Filled { + override var box by owner.field(Box.from(Vec3d.ZERO)) + override var color by owner.field(Color.WHITE) + override var sides by owner.field(DirectionMask.ALL) + + private var prevBox by owner.field(Box.from(Vec3d.ZERO)) + + init { + update() + prevBox = box + } + + override fun update() { + prevBox = box + super.update() + } + + override fun build(ctx: IRenderContext) = ctx.use { + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + ctx.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(DirectionMask.EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(DirectionMask.WEST)) putQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(DirectionMask.UP)) putQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DirectionMask.DOWN)) putQuad(blb, brb, brf, blf) + if (sides.hasDirection(DirectionMask.SOUTH)) putQuad(blf, brf, trf, tlf) + if (sides.hasDirection(DirectionMask.NORTH)) putQuad(blb, brb, trb, tlb) + } + } + + companion object { + private val shader = Shader("renderer/pos_color", "renderer/box_dynamic") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicOutlineRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicOutlineRenderer.kt new file mode 100644 index 000000000..c6b0aebdd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/DynamicOutlineRenderer.kt @@ -0,0 +1,92 @@ +package com.lambda.graphics.renderer.world.core.box + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.DirectionMask +import com.lambda.graphics.renderer.world.DirectionMask.DOWN +import com.lambda.graphics.renderer.world.DirectionMask.EAST +import com.lambda.graphics.renderer.world.DirectionMask.NORTH +import com.lambda.graphics.renderer.world.DirectionMask.SOUTH +import com.lambda.graphics.renderer.world.DirectionMask.UP +import com.lambda.graphics.renderer.world.DirectionMask.WEST +import com.lambda.graphics.renderer.world.DirectionMask.hasDirection +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +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 + +class DynamicOutlineRenderer : AbstractEspRenderer( + VertexMode.LINES, shader, true +) { + override fun newEntry(block: IESPEntry.IBoxEntry.Outline.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: DynamicOutlineRenderer, + override val updateBlock: IESPEntry.IBoxEntry.Outline.() -> Unit + ) : IESPEntry.IBoxEntry.Outline { + override var box by owner.field(Box.from(Vec3d.ZERO)) + override var color by owner.field(Color.WHITE) + override var sides by owner.field(DirectionMask.ALL) + override var outlineMode by owner.field(DirectionMask.OutlineMode.OR) + + private var prevBox by owner.field(Box.from(Vec3d.ZERO)) + + init { + update() + prevBox = box + } + + override fun update() { + prevBox = box + super.update() + } + + override fun build(ctx: IRenderContext) = ctx.use { + val pos11 = prevBox.min + val pos12 = prevBox.max + val pos21 = box.min + val pos22 = box.max + + ctx.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) + } + } + + companion object { + private val shader = Shader("renderer/pos_color", "renderer/box_dynamic") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/StaticFilledRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/StaticFilledRenderer.kt new file mode 100644 index 000000000..e46675435 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/StaticFilledRenderer.kt @@ -0,0 +1,56 @@ +package com.lambda.graphics.renderer.world.core.box + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.DirectionMask +import com.lambda.graphics.renderer.world.DirectionMask.hasDirection +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +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 + +class StaticFilledRenderer : AbstractEspRenderer( + VertexMode.TRIANGLES, shader, false +) { + override fun newEntry(block: IESPEntry.IBoxEntry.Filled.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: StaticFilledRenderer, + override val updateBlock: IESPEntry.IBoxEntry.Filled.() -> Unit + ) : IESPEntry.IBoxEntry.Filled { + override var box by owner.field(Box.from(Vec3d.ZERO)) + override var sides by owner.field(DirectionMask.ALL) + override var color by owner.field(Color.WHITE) + + override fun build(ctx: IRenderContext) = ctx.use { + val pos1 = box.min + val pos2 = box.max + + ctx.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(DirectionMask.EAST)) putQuad(brb, brf, trf, trb) + if (sides.hasDirection(DirectionMask.WEST)) putQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(DirectionMask.UP)) putQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DirectionMask.DOWN)) putQuad(blb, brb, brf, blf) + if (sides.hasDirection(DirectionMask.SOUTH)) putQuad(blf, brf, trf, tlf) + if (sides.hasDirection(DirectionMask.NORTH)) putQuad(blb, brb, trb, tlb) + } + } + + 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/world/core/box/StaticOutlineRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/StaticOutlineRenderer.kt new file mode 100644 index 000000000..6160c3efa --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/box/StaticOutlineRenderer.kt @@ -0,0 +1,78 @@ +package com.lambda.graphics.renderer.world.core.box + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.DirectionMask +import com.lambda.graphics.renderer.world.DirectionMask.DOWN +import com.lambda.graphics.renderer.world.DirectionMask.EAST +import com.lambda.graphics.renderer.world.DirectionMask.NORTH +import com.lambda.graphics.renderer.world.DirectionMask.SOUTH +import com.lambda.graphics.renderer.world.DirectionMask.UP +import com.lambda.graphics.renderer.world.DirectionMask.WEST +import com.lambda.graphics.renderer.world.DirectionMask.hasDirection +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +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 + +class StaticOutlineRenderer : AbstractEspRenderer( + VertexMode.LINES, shader, false +) { + override fun newEntry(block: IESPEntry.IBoxEntry.Outline.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: StaticOutlineRenderer, + override val updateBlock: IESPEntry.IBoxEntry.Outline.() -> Unit + ) : IESPEntry.IBoxEntry.Outline { + override var box by owner.field(Box.from(Vec3d.ZERO)) + override var sides by owner.field(DirectionMask.ALL) + override var color by owner.field(Color.WHITE) + override var outlineMode by owner.field(DirectionMask.OutlineMode.OR) + + override fun build(ctx: IRenderContext) = ctx.use { + val pos1 = box.min + val pos2 = box.max + + ctx.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) + } + } + + 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/world/core/tracer/DynamicTracerRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/tracer/DynamicTracerRenderer.kt new file mode 100644 index 000000000..d63eaece5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/tracer/DynamicTracerRenderer.kt @@ -0,0 +1,45 @@ +package com.lambda.graphics.renderer.world.core.tracer + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +import net.minecraft.util.math.Vec3d +import java.awt.Color + +class DynamicTracerRenderer : AbstractEspRenderer( + VertexMode.LINES, shader, true +) { + override fun newEntry(block: IESPEntry.ITracerEntry.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: DynamicTracerRenderer, + override val updateBlock: IESPEntry.ITracerEntry.() -> Unit + ) : IESPEntry.ITracerEntry { + private var prevPos by owner.field(Vec3d.ZERO) + override var position by owner.field(Vec3d.ZERO) + + override var color by owner.field(Color.WHITE) + + init { + update() + prevPos = position + } + + override fun update() { + prevPos = position + super.update() + } + + override fun build(ctx: IRenderContext) = ctx.use { + // Each other vertex is moved into the center of the screen (provided by shader) + val i = vec3(prevPos.x, prevPos.y, prevPos.z).vec3(position.x, position.y, position.z).color(color).end() + putLine(i, i) + } + } + + companion object { + private val shader = Shader("renderer/pos_color", "renderer/tracer_dynamic") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/tracer/StaticTracerRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/tracer/StaticTracerRenderer.kt new file mode 100644 index 000000000..9ac61bc93 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/world/core/tracer/StaticTracerRenderer.kt @@ -0,0 +1,33 @@ +package com.lambda.graphics.renderer.world.core.tracer + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.world.AbstractEspRenderer +import com.lambda.graphics.renderer.world.core.IESPEntry +import com.lambda.graphics.shader.Shader +import net.minecraft.util.math.Vec3d +import java.awt.Color + +class StaticTracerRenderer : AbstractEspRenderer( + VertexMode.LINES, shader, false +) { + override fun newEntry(block: IESPEntry.ITracerEntry.() -> Unit) = Builder(this, block) + + class Builder( + override val owner: StaticTracerRenderer, + override val updateBlock: IESPEntry.ITracerEntry.() -> Unit + ) : IESPEntry.ITracerEntry { + override var position by owner.field(Vec3d.ZERO!!) + override var color by owner.field(Color.WHITE!!) + + override fun build(ctx: IRenderContext) = ctx.use { + // Each other vertex is moved into the center of the screen (provided by shader) + val i = vec3(position.x, position.y, position.z).color(color).end() + putLine(i, i) + } + } + + companion object { + private val shader = Shader("renderer/pos_color", "renderer/tracer_static") + } +} \ No newline at end of file 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 b0ec5c513..69c15e1d5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -9,6 +9,7 @@ import com.lambda.util.LambdaResource import com.lambda.util.math.Vec2d import it.unimi.dsi.fastutil.objects.Object2IntMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap +import net.minecraft.util.math.Vec3d import org.joml.Matrix4f import org.lwjgl.opengl.GL20C.* import java.awt.Color @@ -52,6 +53,9 @@ class Shader(fragmentPath: String, vertexPath: String) { operator fun set(name: String, vec: Vec2d) = glUniform2f(loc(name), vec.x.toFloat(), vec.y.toFloat()) + operator fun set(name: String, vec: Vec3d) = + glUniform3f(loc(name), vec.x.toFloat(), vec.y.toFloat(), vec.z.toFloat()) + operator fun set(name: String, color: Color) = glUniform4f( loc(name), 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 5361b4cc3..1142dec70 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt @@ -24,7 +24,32 @@ object TextureUtils { val width = bufferedImage.width val height = bufferedImage.height + // Here we cannot use GL_UNSIGNED_INT_8_8_8_8_REV or GL_UNSIGNED_INT_8_8_8_8 + // because the RGBA values are affected by the machine's endianness. + // On little-endian machines, you would read the data as + // 0xAABBGGRR and on big-endian machines as 0xRRGGBBAA. + // The solution is to use GL_UNSIGNED_BYTE and swap the bytes + // manually if necessary. (We won't need to) + // + // GL_UNSIGNED_BYTE -> [RR, GG, BB, AA] + // Array of floats normalized to [0.0, 1.0] -> [R, G, B, A] glTexImage2D(GL_TEXTURE_2D, lod, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, readImage(bufferedImage)) + + // I'd also like to use glTexSubImage2D, but we have an issue where the function + // would return an error about an invalid texture format. + // + // It would allow us to upload texture data asynchronously and is more efficient + // from testing we gain approximately 20% runtime performance. + // If someone with advanced OpenGL knowledge could help us out, that would be great. + // (Very unlikely to happen, but I can hope) + // + // I've also read online that glTexStorage2D can be used for the same purpose as + // glTexImage2D with NULL data. + // However, some users may have ancient hardware that does not support this function. + // as it was implemented in OpenGL 4.2 and ES 3.0. + // + // glTexSubImage2D(GL_TEXTURE_2D, lod, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readImage(bufferedImage)) + setupTexture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR) } @@ -84,7 +109,7 @@ object TextureUtils { } fun BufferedImage.rescale(targetWidth: Int, targetHeight: Int): BufferedImage { - val type = if (this.transparency == Transparency.OPAQUE) + val type = if (transparency == Transparency.OPAQUE) BufferedImage.TYPE_INT_RGB else BufferedImage.TYPE_INT_ARGB @@ -117,4 +142,4 @@ object TextureUtils { return image } -} \ 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 new file mode 100644 index 000000000..1266d531a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/GuiConfigurable.kt @@ -0,0 +1,35 @@ +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 + } + } +} diff --git a/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt b/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt new file mode 100644 index 000000000..a9daccdac --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/GuiEvent.kt @@ -0,0 +1,17 @@ +package com.lambda.gui.api + +import com.lambda.event.Event +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +abstract class GuiEvent : Event { + class Show : GuiEvent() + class Hide : GuiEvent() + class Tick : GuiEvent() + class Render : GuiEvent() + class KeyPress(val key: KeyCode) : GuiEvent() + class CharTyped(val char: Char) : GuiEvent() + class MouseClick(val button: Mouse.Button, val action: Mouse.Action, val mouse: Vec2d) : GuiEvent() + class MouseMove(val mouse: Vec2d) : GuiEvent() +} \ 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 1131da747..5700cba2c 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -5,11 +5,13 @@ import com.lambda.event.EventFlow.syncListeners import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.UnsafeListener +import com.lambda.graphics.animation.AnimationTicker import com.lambda.gui.api.component.core.IComponent import com.lambda.module.Module import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.Nameable +import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall import net.minecraft.client.gui.DrawContext @@ -22,15 +24,19 @@ abstract class LambdaGui( private val owner: Module? = null ) : Screen(Text.of(name)), IComponent, Nameable { private var screenSize = Vec2d.ZERO + override val rect get() = Rect(Vec2d.ZERO, screenSize) + + val animation = AnimationTicker() private val renderListener = UnsafeListener(0, this, false) { event -> event as RenderEvent.GUI.Scaled screenSize = event.screenSize - onRender() + onEvent(GuiEvent.Render()) } private val tickListener = UnsafeListener(0, this, false) { - onTick() + animation.tick() + onEvent(GuiEvent.Tick()) } /** @@ -47,7 +53,7 @@ abstract class LambdaGui( } final override fun onDisplayed() { - onShow() + onEvent(GuiEvent.Show()) with(syncListeners) { subscribe(renderListener) @@ -56,7 +62,7 @@ abstract class LambdaGui( } final override fun removed() { - onHide() + onEvent(GuiEvent.Hide()) // quick crashfix (is there any other way to prevent gui being closed twice?) mc.currentScreen = null @@ -74,7 +80,7 @@ abstract class LambdaGui( } final override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - onKey(KeyCode(keyCode)) + onEvent(GuiEvent.KeyPress(KeyCode(keyCode))) if (keyCode == KeyCode.Escape.key) { close() @@ -84,22 +90,22 @@ abstract class LambdaGui( } final override fun charTyped(chr: Char, modifiers: Int): Boolean { - onChar(chr) + onEvent(GuiEvent.CharTyped(chr)) return true } final override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouseClick(Mouse.Button(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY)) + onEvent(GuiEvent.MouseClick(Mouse.Button(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY))) return true } final override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouseClick(Mouse.Button(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY)) + onEvent(GuiEvent.MouseClick(Mouse.Button(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY))) return true } final override fun mouseMoved(mouseX: Double, mouseY: Double) { - onMouseMove(rescaleMouse(mouseX, mouseY)) + onEvent(GuiEvent.MouseMove(rescaleMouse(mouseX, mouseY))) } final override fun shouldPause() = false diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt index 0bd6e7555..b10817edf 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt @@ -1,40 +1,41 @@ package com.lambda.gui.api.component +import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.core.IComponent -import com.lambda.gui.api.component.core.IRectComponent import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -abstract class InteractiveComponent : IComponent, IRectComponent { - protected var hovered = false - protected var pressed = false; set(value) { - if (field == value) return - field = value +abstract class InteractiveComponent : IComponent { + protected open val hovered get() = rect.contains(lastMouse) + protected var activeButton: Mouse.Button? = null - if (value) onPress() - else onRelease() - } + protected open fun onPress(e: GuiEvent.MouseClick) {} + protected open fun onRelease(e: GuiEvent.MouseClick) {} - protected var activeMouseButton: Mouse.Button? = null + private var lastMouse = Vec2d.ZERO - protected open fun onPress() {} - protected open fun onRelease() {} + override fun onEvent(e: GuiEvent) { + when (e) { + is GuiEvent.Show -> { + lastMouse = Vec2d.ONE * -1000.0 + activeButton = null + } - override fun onShow() { - hovered = false - pressed = false - } + is GuiEvent.MouseMove -> { + lastMouse = e.mouse + } - override fun onMouseMove(mouse: Vec2d) { - hovered = rect.contains(mouse) - } + is GuiEvent.MouseClick -> { + lastMouse = e.mouse + + val prevPressed = activeButton != null + activeButton = if (hovered && e.button.isMainButton && e.action == Mouse.Action.Click) e.button else null + val pressed = activeButton != null - override fun onMouseClick( - button: Mouse.Button, action: Mouse.Action, mouse: Vec2d - ) { - activeMouseButton = button.takeUnless { - it.isMainButton && action == Mouse.Action.Click + if (prevPressed == pressed) return + if (pressed) onPress(e) + else onRelease(e) + } } - pressed = hovered && button.isMainButton && action == Mouse.Action.Click } } \ No newline at end of file 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 new file mode 100644 index 000000000..5f094779a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/ListWindow.kt @@ -0,0 +1,21 @@ +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 + +abstract class ListWindow ( + owner: AbstractClickGui +) : WindowComponent(owner) { + override fun onEvent(e: GuiEvent) { + if (e is GuiEvent.Tick) { + var y = 0.0 + contentComponents.children.forEach { button -> + button.heightOffset = y + y += button.size.y + button.listStep + } + } + + super.onEvent(e) + } +} \ No newline at end of file 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 f6e13ced9..1af9a2860 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 @@ -1,23 +1,28 @@ package com.lambda.gui.api.component import com.lambda.graphics.animation.Animation.Companion.exp -import com.lambda.graphics.animation.AnimationTicker import com.lambda.graphics.gl.Scissor.scissor import com.lambda.graphics.renderer.gui.font.IFontEntry -import com.lambda.gui.api.component.core.IComponent -import com.lambda.gui.api.component.core.list.IListComponent +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.layer.RenderLayer +import com.lambda.gui.impl.clickgui.AbstractClickGui import com.lambda.module.modules.client.ClickGui import com.lambda.module.modules.client.GuiSettings -import com.lambda.util.KeyCode import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d +import java.awt.Color import kotlin.math.abs -abstract class WindowComponent : InteractiveComponent(), IListComponent { +abstract class WindowComponent ( + val gui: AbstractClickGui +) : ChildComponent(gui.windows) { abstract val title: String abstract var width: Double @@ -26,114 +31,144 @@ abstract class WindowComponent : InteractiveComponent(), IL var position = Vec2d.ZERO var isOpen = true + override val isActive get() = isOpen + private var dragOffset: Vec2d? = null private val padding get() = ClickGui.windowPadding final override val rect get() = Rect.basedOn(position, width, renderHeight + titleBarHeight) - val contentRect get() = rect.shrink(padding).moveFirst(Vec2d(0.0, titleBarHeight - padding)) + private val contentRect get() = rect.shrink(padding).moveFirst(Vec2d(0.0, titleBarHeight - padding)) private val titleBar get() = Rect.basedOn(rect.leftTop, rect.size.x, titleBarHeight) - private val titleBarHeight get() = titleFont.height + 2 + padding * 2 + private val titleBarHeight get() = ClickGui.buttonHeight * 1.25 private val titleFont: IFontEntry private val layer = RenderLayer() - private val animation = AnimationTicker() + private val renderer = layer.entry() + private val contentLayer = RenderLayer() + + private val animation = gui.animation - override val children = mutableListOf() - val subLayer = RenderLayer(true) + private val showAnimation by animation.exp(0.0, 1.0, 0.6, ::isOpen) + override val childShowAnimation get() = lerp(0.0, showAnimation, gui.childShowAnimation) private val actualHeight get() = height + padding * 2 * isOpen.toInt() - private var renderHeight by animation.exp({ 0.0 }, ::actualHeight, 0.5, ::isOpen) + private var renderHeightAnimation by animation.exp({ 0.0 }, ::actualHeight, 0.6, ::isOpen) + private val renderHeight get() = lerp(0.0, renderHeightAnimation, childShowAnimation) + + open val contentComponents = ChildLayer.Drawable>(gui, this, contentLayer, ::contentRect) + + /*val titleBarComponents = ChildLayer { child -> + child.rect in titleBar && accessible + }*/ // TODO: window close button init { // Background - layer.rect.build { + renderer.filled { + position = rect + roundRadius = ClickGui.windowRadius + shade = GuiSettings.shadeBackground + + val alpha = (gui.childShowAnimation * 2.0).coerceIn(0.0, 1.0) + color(GuiSettings.backgroundColor.multAlpha(alpha)) + } + + renderer.outline { position = rect roundRadius = ClickGui.windowRadius - color(GuiSettings.backgroundColor) + outerGlow = ClickGui.windowRadius + shade = GuiSettings.shade + + val alpha = (gui.childShowAnimation * 2.0).coerceIn(0.0, 1.0) + color(GuiSettings.mainColor.multAlpha(alpha)) } // Title - titleFont = layer.font.build { + titleFont = renderer.font { text = title position = titleBar.center - widthVec * 0.5 + color = Color.WHITE.setAlpha(gui.childShowAnimation) } } - override fun onShow() { - super.onShow() - super.onShow() - - dragOffset = null - renderHeight = 0.0 - } - - override fun onHide() { - super.onHide() - } + override fun onEvent(e: GuiEvent) { + super.onEvent(e) - override fun onTick() { - animation.tick() + when (e) { + is GuiEvent.Show -> { + dragOffset = null + } - setChildrenAccessibility { child -> - child.rect in contentRect - } + is GuiEvent.Render -> { + layer.assignOffset(position) + contentLayer.assignOffset(position) - children - .filter(ChildComponent::accessible) - .forEach(IComponent::onTick) - } + // TODO: fix blur + // BlurPostProcessor.render(rect, ClickGui.windowBlur, guiAnimation) - override fun onRender() { - layer.render() + layer.render() - scissor(contentRect) { - subLayer.render() - super.onRender() - } - } + scissor(contentRect) { + contentLayer.render() + contentComponents.onEvent(e) + } - override fun onMouseMove(mouse: Vec2d) { - super.onMouseMove(mouse) - super.onMouseMove(mouse) + return + } - dragOffset?.let { - position = mouse - it - } - } + is GuiEvent.MouseMove -> { + dragOffset?.let { + position = e.mouse - it + } + } - override fun onKey(key: KeyCode) { - super.onKey(key) - } + is GuiEvent.MouseClick -> { + dragOffset = null - override fun onChar(char: Char) { - super.onChar(char) - } + if (e.mouse in titleBar && e.action == Mouse.Action.Click) { + when (e.button) { + Mouse.Button.Left -> dragOffset = e.mouse - position + Mouse.Button.Right -> { + // Don't let user spam + val targetHeight = if (isOpen) actualHeight else 0.0 + if (abs(targetHeight - renderHeight) > 1) return - override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { - super.onMouseClick(button, action, mouse) + isOpen = !isOpen - dragOffset = null + if (isOpen) onEvent(GuiEvent.Show()) + } + } + } + } + } - if (mouse in titleBar && action == Mouse.Action.Click) { - when(button) { - Mouse.Button.Left -> dragOffset = mouse - position - Mouse.Button.Right -> { - // Don't let user spam - val targetHeight = if (isOpen) actualHeight else 0.0 - if (abs(targetHeight - renderHeight) > 1) return + contentComponents.onEvent(e) + //titleBarComponents.onEvent(e) + } - isOpen = !isOpen + fun focus() { + // move window into foreground + gui.apply { + scheduleAction { + windows.children.apply { + this@WindowComponent + .apply(::remove) + .apply(::add) } } } - - super.onMouseClick(button, action, mouse) } - private fun setChildrenAccessibility(flag: (T) -> Boolean) { - children.forEach { child -> - child.accessible = flag(child) + fun destroy() { + gui.apply { + scheduleAction { + windows.removeChild(this@WindowComponent) + } } } -} \ No newline at end of file + + override fun onRemove() { + layer.destroy() + contentLayer.destroy() + } +} 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 new file mode 100644 index 000000000..54ee20a19 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/button/ButtonComponent.kt @@ -0,0 +1,115 @@ +package com.lambda.gui.api.component.button + +import com.lambda.core.LambdaSound +import com.lambda.core.SoundManager.playSoundRandomly +import com.lambda.graphics.animation.Animation.Companion.exp +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.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d +import java.awt.Color + +abstract class ButtonComponent( + owner: ChildLayer.Drawable<*, *> +) : ChildComponent(owner) { + abstract val position: Vec2d + abstract val size: Vec2d + + abstract val text: String + protected open val textColor get() = lerp(Color.WHITE, GuiSettings.mainColor, activeAnimation).multAlpha(showAnimation) + protected abstract var activeAnimation: Double + + private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.rect.size.x else size.x, size.y) + final override val rect get() = Rect.basedOn(position, actualSize) + owner.rect.leftTop + + val renderer = owner.renderer.entry() + protected val animation = owner.gui.animation + + private var hoverRectAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered) 0.6 else 0.07 }, ::renderHovered) + protected var hoverFontAnimation by animation.exp(0.0, 1.0, 0.5, ::renderHovered) + protected var pressAnimation by animation.exp(0.0, 1.0, 0.5) { activeButton != null } + protected val interactAnimation get() = lerp(hoverRectAnimation, 1.5, pressAnimation) * 0.4 + override val childShowAnimation: Double get() = owner.childShowAnimation + protected open val showAnimation get() = owner.childShowAnimation + + private var lastHoveredTime = 0L + private val renderHovered get() = hovered || System.currentTimeMillis() - lastHoveredTime < 110 + + // Removes button shrinking if there's no space between buttons + protected val shrinkAnimation get() = lerp(0.0, interactAnimation, ClickGui.buttonStep) + + init { + // Active color + renderer.filled { + position = rect.shrink(shrinkAnimation) + shade = GuiSettings.shade + color(GuiSettings.mainColor.multAlpha(activeAnimation * 0.3 * showAnimation)) + } + + // Hover glint + renderer.filled { + val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverRectAnimation, rect.size.y) + position = hoverRect.shrink(shrinkAnimation) + shade = GuiSettings.shade + + val alpha = interactAnimation * 0.2 * showAnimation + color(GuiSettings.mainColor.multAlpha(alpha)) + } + + // Text + renderer.font { + text = this@ButtonComponent.text + scale = 1.0 - pressAnimation * 0.08 + + color = textColor + + val x = ClickGui.windowPadding + interactAnimation + hoverFontAnimation + position = Vec2d(rect.left + x, rect.center.y) + } + } + + open fun performClickAction(e: GuiEvent.MouseClick) {} + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + + when (e) { + is GuiEvent.Show, is GuiEvent.Hide -> reset() + + is GuiEvent.MouseMove -> { + val time = System.currentTimeMillis() + if (hovered) lastHoveredTime = time + } + } + } + + override fun onPress(e: GuiEvent.MouseClick) { + val pitch = if (e.button == Mouse.Button.Left) 1.0 else 0.9 + playSoundRandomly(LambdaSound.BUTTON_CLICK.event, pitch) + } + + override fun onRelease(e: GuiEvent.MouseClick) { + if (hovered) performClickAction(e) + } + + override fun onRemove() { + renderer.destroy() + } + + private fun reset() { + activeAnimation = 0.0 + hoverRectAnimation = 0.0 + pressAnimation = 0.0 + lastHoveredTime = 0L + } + + companion object { + const val FILL_PARENT = -1.0 + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt b/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt new file mode 100644 index 000000000..11adbf1b4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/button/InputBarOverlay.kt @@ -0,0 +1,110 @@ +package com.lambda.gui.api.component.button + +import com.lambda.graphics.animation.Animation.Companion.exp +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.layer.LayerEntry +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.KeyCode +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d +import java.awt.Color +import kotlin.math.abs + +abstract class InputBarOverlay (renderer: LayerEntry, owner: ChildLayer.Drawable) : ChildComponent(owner) { + override val rect: Rect get() = owner.rect + override var isActive = false + + protected abstract val pressAnimation: Double + protected abstract val interactAnimation: Double + protected abstract val hoverFontAnimation: Double + protected abstract val showAnimation: Double + + val activeAnimation by owner.gui.animation.exp(0.0, 1.0, 0.7, ::isActive) + private var typeAnimation by owner.gui.animation.exp({ 0.0 }, 0.2) + + private var targetOffset = 0.0 + private var offset by owner.gui.animation.exp(::targetOffset, 0.4) + + abstract fun getText(): String + abstract fun setValue(string: String) + + open fun isCharAllowed(char: Char): Boolean = true + + private val field = renderer.font { + scale = lerp(0.5, 1.0, activeAnimation) - pressAnimation * 0.08 + color = Color.WHITE.setAlpha(lerp(0.0, activeAnimation, showAnimation)) + + val x = ClickGui.windowPadding + interactAnimation + hoverFontAnimation + position = Vec2d(rect.left + x, rect.center.y) + targetOffset = stringWidth + } + + init { + renderer.filled { + val shrink = lerp(rect.size.y * 0.5, 2 + abs(typeAnimation), activeAnimation) + val x = field.position.x + offset + 2 + + position = Rect( + Vec2d(0.0, rect.top + shrink), + Vec2d(1.0, rect.bottom - shrink) + ) + Vec2d(lerp(rect.right, x, activeAnimation), 0.0) + + color(field.color.multAlpha(0.8)) + } + + renderer.font { + text = getText() + + val progress = 1.0 - activeAnimation + scale = lerp(0.5, 1.0, progress) + position = Vec2d(rect.right, rect.center.y) - Vec2d(ClickGui.windowPadding + stringWidth, 0.0) + color = Color.WHITE.setAlpha(lerp(0.0, progress, showAnimation)) + } + } + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + + when (e) { + is GuiEvent.Show -> { + isActive = false + } + + is GuiEvent.CharTyped -> { + if (!isActive || !isCharAllowed(e.char)) return + field.text += e.char + typeAnimation = 1.0 + } + + is GuiEvent.KeyPress -> { + if (!isActive) return + + when (e.key) { + KeyCode.Enter -> { + setValue(field.text) + toggle() + } + + KeyCode.Backspace -> { + field.text = field.text.dropLast(1) + typeAnimation = -1.0 + } + } + } + } + } + + fun toggle() { + isActive = !isActive + + if (isActive) { + field.text = getText() + targetOffset = field.stringWidth + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/button/ListButton.kt b/common/src/main/kotlin/com/lambda/gui/api/component/button/ListButton.kt new file mode 100644 index 000000000..b66d8443d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/button/ListButton.kt @@ -0,0 +1,27 @@ +package com.lambda.gui.api.component.button + +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Vec2d + +abstract class ListButton(owner: ChildLayer.Drawable<*, *>) : ButtonComponent(owner) { + override val position get() = Vec2d(0.0, lerp(0.0, renderHeightOffset, owner.childShowAnimation)) + override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) + + open val listStep get() = ClickGui.buttonStep + + var heightOffset = 0.0 + protected var renderHeightAnimation by animation.exp(::heightOffset, 0.8) + protected open val renderHeightOffset get() = renderHeightAnimation + + override fun onEvent(e: GuiEvent) { + if (e is GuiEvent.Show) { + heightOffset = 0.0 + renderHeightAnimation = 0.0 + } + super.onEvent(e) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt index 7dc3504ae..751523395 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt @@ -1,23 +1,12 @@ package com.lambda.gui.api.component.core -import com.lambda.util.KeyCode -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d +import com.lambda.gui.api.GuiEvent +import com.lambda.util.math.Rect interface IComponent { - fun onShow() {} + val isActive: Boolean get() = true + val childShowAnimation: Double get() = 1.0 + val rect: Rect - fun onHide() {} - - fun onTick() {} - - fun onRender() {} - - fun onKey(key: KeyCode) {} - - fun onChar(char: Char) {} - - fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) {} - - fun onMouseMove(mouse: Vec2d) {} + fun onEvent(e: GuiEvent) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt deleted file mode 100644 index 0843a1ccb..000000000 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.lambda.gui.api.component.core - -import com.lambda.util.math.Rect - -interface IRectComponent { - val rect: Rect -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt index c8209122f..9c71aec5c 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt @@ -2,13 +2,10 @@ package com.lambda.gui.api.component.core.list import com.lambda.gui.api.component.InteractiveComponent -abstract class ChildComponent : InteractiveComponent(), IChildComponent { - // mostly used to create an animation when an element appears - var accessible = false; set(value) { - if (field == value) return - field = value +abstract class ChildComponent(open val owner: ChildLayer<*, *>) : InteractiveComponent() { + open var accessible = false + override val hovered; get() = super.hovered && accessible - if (value) onShow() - else onHide() - } + open fun onAdd() {} + open fun onRemove() {} } diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt new file mode 100644 index 000000000..ff493c911 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildLayer.kt @@ -0,0 +1,62 @@ +package com.lambda.gui.api.component.core.list + +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.LambdaGui +import com.lambda.gui.api.component.core.IComponent +import com.lambda.gui.api.layer.RenderLayer +import com.lambda.util.Mouse +import com.lambda.util.math.Rect + +open class ChildLayer ( + val gui: LambdaGui, + val ownerComponent: R, + private val childRect: () -> Rect, + private val childAccessible: (T) -> Boolean = { true } +) : IComponent { + override val isActive get() = ownerComponent.isActive + override val childShowAnimation get() = ownerComponent.childShowAnimation + override val rect get() = childRect() + val children = mutableListOf() + + fun addChild(child : T) { + children.add(child) + child.onAdd() + } + + fun removeChild(child : T) { + children.remove(child) + child.onRemove() + } + + override fun onEvent(e: GuiEvent) { + children.forEach { child -> + when (e) { + is GuiEvent.Tick -> { + val ownerAccessible = (ownerComponent as? ChildComponent)?.accessible ?: true + child.accessible = childAccessible(child) && child.rect in rect && ownerAccessible && ownerComponent.isActive + } + + is GuiEvent.KeyPress, is GuiEvent.CharTyped -> { + if (!child.accessible) return@forEach + } + + is GuiEvent.MouseClick -> { + val newAction = if (child.accessible) e.action else Mouse.Action.Release + val newEvent = GuiEvent.MouseClick(e.button, newAction, e.mouse) + child.onEvent(newEvent) + return@forEach + } + } + + child.onEvent(e) + } + } + + class Drawable ( + gui: LambdaGui, + owner: R, + val renderer: RenderLayer, + contentRect: () -> Rect, + childAccessible: (T) -> Boolean = { true } + ) : ChildLayer(gui, owner, contentRect, childAccessible) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt deleted file mode 100644 index 353ab501f..000000000 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.lambda.gui.api.component.core.list - -import com.lambda.gui.api.component.core.IComponent - -interface IChildComponent { - val owner: IComponent -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt deleted file mode 100644 index 303ea9ff5..000000000 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.lambda.gui.api.component.core.list - -import com.lambda.gui.api.component.core.IComponent -import com.lambda.util.KeyCode -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d - -interface IListComponent : IComponent { - val children: List - - fun isChildAccessible(child: T): Boolean = (child as? ChildComponent)?.accessible ?: true - - override fun onShow() { - children.forEach(IComponent::onShow) - } - - override fun onHide() { - children.forEach(IComponent::onHide) - } - - override fun onTick() { - children.forEach(IComponent::onTick) - } - - override fun onRender() { - children.forEach(IComponent::onRender) - } - - override fun onKey(key: KeyCode) { - children.filter(::isChildAccessible).forEach { child -> - child.onKey(key) - } - } - - override fun onChar(char: Char) { - children.filter(::isChildAccessible).forEach { child -> - child.onChar(char) - } - } - - override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { - children.filter(::isChildAccessible).forEach { child -> - child.onMouseClick(button, action, mouse) - } - } - - override fun onMouseMove(mouse: Vec2d) { - children.forEach { child -> - child.onMouseMove( - if (isChildAccessible(child)) mouse - else Vec2d(-1000.0, -1000.0) // junky but worky way to unfocus - ) - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt deleted file mode 100644 index 4e1265ead..000000000 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.lambda.gui.api.component.sub - -import com.lambda.graphics.animation.Animation.Companion.exp -import com.lambda.graphics.animation.AnimationTicker -import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.api.component.core.list.ChildComponent -import com.lambda.module.modules.client.ClickGui -import com.lambda.module.modules.client.GuiSettings -import com.lambda.util.Mouse -import com.lambda.util.math.ColorUtils.multAlpha -import com.lambda.util.math.MathUtils.lerp -import com.lambda.util.math.MathUtils.toInt -import com.lambda.util.math.Rect -import com.lambda.util.math.Vec2d -import java.awt.Color -import kotlin.math.abs - -abstract class ButtonComponent(final override val owner: WindowComponent<*>) : ChildComponent() { - abstract val position: Vec2d - abstract val size: Vec2d - - abstract val text: String - open val active get() = pressed - - private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.contentRect.size.x else size.x, size.y) - final override val rect get() = Rect.basedOn(position, actualSize) + owner.contentRect.leftTop - - private val layer = owner.subLayer - private val animation = AnimationTicker() - - private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active) - private var hoverRectAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered) 0.5 else 0.1 }, ::renderHovered) - private var hoverFontAnimation by animation.exp(0.0, 1.0, 0.5, ::renderHovered) - private var pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed) - private val interactAnimation get() = lerp(hoverRectAnimation, 1.5, pressAnimation) * 0.4 - - private var lastHoveredTime = 0L - private val renderHovered get() = hovered || - System.currentTimeMillis() - lastHoveredTime < 110 // a bit more than 2 ticks - - init { - // Active color - layer.rect.build { - position = rect.shrink(interactAnimation) - color(GuiSettings.mainColor.multAlpha(activeAnimation * 0.2)) - } - - // Hover glint - layer.rect.build { - val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverRectAnimation, rect.size.y) - position = hoverRect.shrink(interactAnimation) - - val alpha = interactAnimation * 0.3 - color(GuiSettings.mainColor.multAlpha(alpha)) - } - - // Toggle fx - layer.rect.build { - val left = rect - Vec2d(rect.size.x, 0.0) - val right = rect + Vec2d(rect.size.x, 0.0) - - position = lerp(left, right, activeAnimation) - .clamp(rect) - .shrink(interactAnimation) - - // 0.0 .. 1.0 .. 0.0 animation - val alpha = 1.0 - (abs(activeAnimation - 0.5) * 2.0) - val color = GuiSettings.mainColor.multAlpha(alpha * 0.8) - - // "Tail" effect - val leftColor = color.multAlpha(1.0 - active.toInt()) - val rightColor = color.multAlpha(active.toInt().toDouble()) - - colorH(leftColor, rightColor) - } - - // Text - layer.font.build { - text = this@ButtonComponent.text - scale = 1.0 - pressAnimation * 0.08 - - color = lerp(Color.WHITE, GuiSettings.mainColor, activeAnimation) - - val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverFontAnimation * 2.0 - position = Vec2d(x, rect.center.y) - } - } - - abstract fun performClickAction(mouse: Mouse.Button) - - override fun onShow() { - super.onShow() - reset() - } - - override fun onHide() { - super.onHide() - reset() - } - - override fun onTick() { - animation.tick() - } - - override fun onRelease() { - if (hovered) activeMouseButton?.let(::performClickAction) - } - - override fun onMouseMove(mouse: Vec2d) { - super.onMouseMove(mouse) - - val time = System.currentTimeMillis() - if (hovered) lastHoveredTime = time - } - - private fun reset() { - activeAnimation = 0.0 - hoverRectAnimation = 0.0 - pressAnimation = 0.0 - lastHoveredTime = 0L - } - - companion object { - const val FILL_PARENT = -1.0 - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/layer/LayerEntry.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/LayerEntry.kt new file mode 100644 index 000000000..34d29cf32 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/LayerEntry.kt @@ -0,0 +1,27 @@ +package com.lambda.gui.api.layer + +import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.graphics.renderer.IRenderer +import com.lambda.graphics.renderer.gui.font.IFontEntry +import com.lambda.graphics.renderer.gui.rect.IRectEntry + +// Used to group all render entries related to a component +class LayerEntry ( + private val filled: IRenderer, + private val outline: IRenderer, + private val font: IRenderer +) { + private val entries = mutableSetOf>() + + fun filled(block: IRectEntry.Filled.() -> Unit) = + filled.build(block).apply(entries::add) + + fun outline(block: IRectEntry.Outline.() -> Unit) = + outline.build(block).apply(entries::add) + + fun font(block: IFontEntry.() -> Unit) = + font.build(block).apply(entries::add) + + fun destroy() = + entries.forEach(IRenderEntry<*>::destroy) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt index fe871fc28..017d43700 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt @@ -1,30 +1,38 @@ package com.lambda.gui.api.layer import com.lambda.graphics.renderer.gui.font.FontRenderer -import com.lambda.graphics.renderer.gui.rect.RectRenderer +import com.lambda.graphics.renderer.gui.rect.AbstractRectRenderer +import com.lambda.graphics.renderer.gui.rect.filled.FilledRectRenderer +import com.lambda.graphics.renderer.gui.rect.outline.OutlineRectRenderer import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.math.Vec2d -class RenderLayer(private val allowEffects: Boolean = false) { - private val rectRenderer = RectRenderer() +class RenderLayer { + private val filled = FilledRectRenderer() + private val outline = OutlineRectRenderer() + private val font = FontRenderer() - val rect = rectRenderer.asRenderer - val font = FontRenderer().asRenderer + fun entry() = LayerEntry(filled, outline, font) + + fun assignOffset(ofs: Vec2d) { + filled.matrixOffset = ofs + outline.matrixOffset = ofs + font.matrixOffset = ofs + } fun render() { - rect.update() + filled.update() + outline.update() font.update() - rectRenderer.apply { - shadeColor = GuiSettings.shade && allowEffects - fancyBlending = GuiSettings.glow && allowEffects - render() - } - + filled.render() + outline.render() font.render() } fun destroy() { - rect.destroy() + filled.destroy() + outline.destroy() font.destroy() } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt new file mode 100644 index 000000000..41fdbf5a6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/AbstractClickGui.kt @@ -0,0 +1,76 @@ +package com.lambda.gui.impl.clickgui + +import com.lambda.Lambda.mc +import com.lambda.graphics.animation.Animation.Companion.exp +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.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 + + 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 + } + + private val actionPool = ArrayDeque<() -> Unit>() + fun scheduleAction(block: () -> Unit) = actionPool.add(block) + + override fun onEvent(e: GuiEvent) { + while (actionPool.isNotEmpty()) actionPool.removeLast().invoke() + + when (e) { + is GuiEvent.Show -> { + activeWindow = null + closing = false + childShowAnimation = 0.0 + } + + is GuiEvent.Tick -> { + if (closing && childShowAnimation < 0.01) mc.setScreen(null) + } + + is GuiEvent.MouseClick -> { + if (e.action == Mouse.Action.Click) activeWindow?.focus() + } + + is GuiEvent.MouseMove -> { + activeWindow = windows.children.lastOrNull { child -> + e.mouse in child.rect + } + } + } + + windows.onEvent(e) + } + + fun showWindow(window: WindowComponent<*>) { + // we have to wait some time to place this window over other ones + recordRenderCall { + windows.addChild(window) + } + } + + fun unfocusSettings() { + windows.children.filterIsInstance().forEach { moduleWindow -> + moduleWindow.contentComponents.children.forEach { moduleButton -> + moduleButton.settingsLayer.children.forEach(SettingButton<*, *>::unfocus) + } + } + } + + override fun close() { + closing = true + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt deleted file mode 100644 index 41c6ec57a..000000000 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.lambda.gui.impl.clickgui - -import com.lambda.config.Configurable -import com.lambda.config.configurations.GuiConfig -import com.lambda.core.Loadable -import com.lambda.gui.impl.clickgui.windows.TagWindow - -object GuiConfigurable : Configurable(GuiConfig), Loadable { - override val name = "gui" - val windows = setting("windows", listOf(TagWindow())) -} 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 b24ccf7a0..2fbad35ad 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 @@ -1,10 +1,23 @@ package com.lambda.gui.impl.clickgui -import com.lambda.gui.api.LambdaGui +import com.lambda.gui.GuiConfigurable +import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.api.component.core.list.IListComponent -import com.lambda.module.modules.client.ClickGui +import com.lambda.gui.impl.clickgui.windows.tag.CustomModuleWindow -object LambdaClickGui : LambdaGui("ClickGui", ClickGui), IListComponent> { - override val children: List> get() = GuiConfigurable.windows.value +object LambdaClickGui : AbstractClickGui() { + override fun onEvent(e: GuiEvent) { + if (e is GuiEvent.Show || e is GuiEvent.Tick) updateWindows() + super.onEvent(e) + } + + fun updateWindows() { + windows.apply { + val windows = GuiConfigurable.mainWindows + GuiConfigurable.customWindows + windows.subtract(children.toSet()).forEach(::showWindow) + + children.filter { it !in windows && it is CustomModuleWindow } + .forEach(WindowComponent<*>::destroy) + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt index 3649b06ec..2785ee916 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt @@ -1,27 +1,188 @@ package com.lambda.gui.impl.clickgui.buttons +import com.lambda.config.settings.NumericSetting +import com.lambda.config.settings.StringSetting +import com.lambda.config.settings.comparable.BooleanSetting +import com.lambda.config.settings.comparable.EnumSetting +import com.lambda.core.LambdaSound +import com.lambda.core.SoundManager.playSoundRandomly +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.graphics.gl.Scissor.scissor +import com.lambda.gui.api.GuiEvent import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.gui.api.component.button.ListButton +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.api.layer.RenderLayer +import com.lambda.gui.impl.clickgui.buttons.setting.BooleanButton +import com.lambda.gui.impl.clickgui.buttons.setting.EnumSlider +import com.lambda.gui.impl.clickgui.buttons.setting.NumberSlider +import com.lambda.gui.impl.clickgui.buttons.setting.StringButton import com.lambda.module.Module -import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.MathUtils.toInt +import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d +import com.lambda.util.math.transform +import java.awt.Color +import kotlin.math.abs -class ModuleButton(val module: Module, owner: WindowComponent<*>) : ButtonComponent(owner) { - override val position get() = Vec2d(0.0, heightOffset) - override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) +class ModuleButton( + val module: Module, + override val owner: ChildLayer.Drawable> +) : ListButton(owner) { + override val text get() = module.name + private val enabled get() = module.isEnabled - override val text: String get() = module.name - override val active: Boolean get() = module.isEnabled + override var activeAnimation by animation.exp(0.0, 1.0, 0.15, ::enabled) + private val toggleFxDirection by animation.exp(0.0, 1.0, 0.7, ::enabled) - var heightOffset = 0.0 + override val listStep: Double get() = super.listStep + renderHeight - override fun performClickAction(mouse: Mouse.Button) { - when (mouse) { - Mouse.Button.Left -> module.toggle() + private var isOpen = false + override val isActive get() = isOpen + + private val openAnimation by animation.exp(0.0, 1.0, 0.7, ::isOpen) + override val childShowAnimation get() = lerp(0.0, openAnimation, owner.childShowAnimation) + + private var settingsHeight = 0.0 + private var renderHeight by animation.exp(::settingsHeight, 0.6) + private val settingsRect get() = rect + .moveFirst(Vec2d(0.0, size.y + super.listStep)) + .moveSecond(Vec2d(0.0, renderHeight)) + + private val settingsRenderer = RenderLayer() + val settingsLayer = ChildLayer.Drawable, ModuleButton>(owner.gui, this, settingsRenderer, ::settingsRect) { + it.visible && abs(settingsHeight - renderHeight) <3 + } + + init { + // Toggle fx + renderer.filled { + val left = rect - Vec2d(rect.size.x, 0.0) + val right = rect + Vec2d(rect.size.x, 0.0) + + position = lerp(left, right, activeAnimation) + .clamp(rect) + .shrink(shrinkAnimation) + + // 0.0 .. 1.0 .. 0.0 animation + val alpha = 1.0 - (abs(activeAnimation - 0.5) * 2.0) + val color = GuiSettings.mainColor.multAlpha(alpha * 0.6 * showAnimation) + + // "Tail" effect + val leftColor = color.multAlpha(1.0 - toggleFxDirection) + val rightColor = color.multAlpha(toggleFxDirection) + + shade = GuiSettings.shade + colorH(leftColor, rightColor) + } + + // Shadow + renderer.filled { + position = Rect( + rect.leftTop + Vec2d(0.0, size.y), + rect.rightTop + Vec2d(0.0, size.y + 5.0) + ) + val progress = transform(renderHeight, 0.0, 10.0, 0.0, 1.0).coerceIn(0.0, 1.0) + colorV(Color.BLACK.setAlpha(0.2 * progress), Color.BLACK.setAlpha(0.0)) + } + + // Bottom shadow + renderer.filled { + val last = this@ModuleButton.owner.ownerComponent.contentComponents.children.lastOrNull() + val show = this@ModuleButton != last + + position = Rect(settingsRect.leftBottom - Vec2d(0.0, 5.0), settingsRect.rightBottom) + val progress = transform(renderHeight, 0.0, 10.0, 0.0, 1.0).coerceIn(0.0, 1.0) * show.toInt() + colorV(Color.BLACK.setAlpha(0.0), Color.BLACK.setAlpha(0.2 * progress)) + } + + // TODO: resort when all settings are implemented + module.settings.mapNotNull { + when (it) { + is BooleanSetting -> BooleanButton(it, settingsLayer) + is NumericSetting<*> -> NumberSlider(it, settingsLayer) + is StringSetting -> StringButton(it, settingsLayer) + is EnumSetting<*> -> EnumSlider(it, settingsLayer) + else -> null + } + }.forEach(settingsLayer::addChild) + } + + override fun onEvent(e: GuiEvent) { + when (e) { + is GuiEvent.Show -> { + isOpen = false + updateHeight() + renderHeight = settingsHeight + } + + is GuiEvent.Tick -> { + if (renderHeight < 0.5) return + updateHeight() + + var y = 0.0 + settingsLayer.children.filter(SettingButton<*, *>::visible).forEach { button -> + button.heightOffset = y + y += button.size.y + button.listStep + } + } + + is GuiEvent.Render -> { + if (renderHeight > 0.5) { + scissor(settingsRect) { + settingsLayer.onEvent(e) + settingsRenderer.render() + } + } + + return + } + } + + super.onEvent(e) + settingsLayer.onEvent(e) + } + + private fun updateHeight() { + settingsHeight = if (isOpen) { + var lastStep = 0.0 + settingsLayer.children + .filter(SettingButton<*, *>::visible) + .sumOf { lastStep = it.listStep; it.size.y + it.listStep } - lastStep + super.listStep * 2.0 + } else 0.0 + } + + override fun performClickAction(e: GuiEvent.MouseClick) { + val sound = when (e.button) { + Mouse.Button.Left -> { + module.toggle() + if (module.isEnabled) LambdaSound.MODULE_ON else LambdaSound.MODULE_OFF + } Mouse.Button.Right -> { - // open settings window + // Don't let user spam + val targetHeight = if (isOpen) settingsHeight else 0.0 + if (abs(targetHeight - renderHeight) > 1) return + + isOpen = !isOpen + if (isOpen) settingsLayer.onEvent(GuiEvent.Show()) + updateHeight() + + if (isOpen) LambdaSound.SETTINGS_OPEN else LambdaSound.SETTINGS_CLOSE } + else -> return } + + playSoundRandomly(sound.event) } + + override fun equals(other: Any?) = + (other as? ModuleButton)?.module == module + + override fun hashCode() = + module.hashCode() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/SettingButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/SettingButton.kt new file mode 100644 index 000000000..351e0fa76 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/SettingButton.kt @@ -0,0 +1,37 @@ +package com.lambda.gui.impl.clickgui.buttons + +import com.lambda.config.AbstractSetting +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.component.button.ListButton +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.util.math.MathUtils.lerp + +abstract class SettingButton > ( + val setting: T, + final override val owner: ChildLayer.Drawable, ModuleButton> +): ListButton(owner) { + override val text = setting.name + protected var value by setting + + val visible; get() = setting.visibility() + private var prevTickVisible = false + + private var visibilityAnimation by animation.exp(0.0, 1.0, 0.6, ::visible) + override val showAnimation get() = lerp(0.0, super.showAnimation, visibilityAnimation) + override val renderHeightOffset get() = renderHeightAnimation + lerp(-size.y, 0.0, visibilityAnimation) + override var activeAnimation = 0.0 + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + + if (e !is GuiEvent.Tick) return + + if (!prevTickVisible && visible) renderHeightAnimation = heightOffset + prevTickVisible = visible + + if (!visible) unfocus() + } + + open fun unfocus() {} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BooleanButton.kt new file mode 100644 index 000000000..27a55a353 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/BooleanButton.kt @@ -0,0 +1,59 @@ +package com.lambda.gui.impl.clickgui.buttons.setting + +import com.lambda.config.settings.comparable.BooleanSetting +import com.lambda.core.LambdaSound +import com.lambda.core.SoundManager.playSoundRandomly +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.gui.impl.clickgui.buttons.SettingButton +import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Rect +import com.lambda.util.math.Rect.Companion.inv +import com.lambda.util.math.Vec2d + +class BooleanButton( + setting: BooleanSetting, + owner: ChildLayer.Drawable, ModuleButton> +) : SettingButton(setting, owner) { + private var active by animation.exp(0.0, 1.0, 0.6, ::value) + private val zoomAnimation get() = lerp(2.0, 0.0, showAnimation) + + private val checkboxRect get() = Rect(rect.rightTop - Vec2d(rect.size.y * 1.75, 0.0), rect.rightBottom) + .shrink(1.0 + zoomAnimation) + + private val knobStart get() = Rect.basedOn(checkboxRect.leftTop, Vec2d.ONE * checkboxRect.size.y) + private val knobEnd get() = Rect.basedOn(checkboxRect.rightBottom, Vec2d.ONE * checkboxRect.size.y * -1.0).inv() + private val checkboxKnob get() = lerp(knobStart, knobEnd, active).shrink(1.0 + zoomAnimation + interactAnimation) + + init { + // Checkbox Background + renderer.filled { + position = checkboxRect + roundRadius = checkboxRect.size.y + shade = GuiSettings.shade + color(GuiSettings.mainColor.multAlpha(showAnimation * (0.2 + active * 0.2))) + } + + // Checkbox Knob + renderer.filled { + position = checkboxKnob + roundRadius = checkboxKnob.size.y + shade = GuiSettings.shadeBackground + color(GuiSettings.backgroundColor.multAlpha(showAnimation)) + } + } + + override fun performClickAction(e: GuiEvent.MouseClick) { + if (e.button != Mouse.Button.Left) return + value = !value + + val sound = if (value) LambdaSound.BOOLEAN_SETTING_ON else LambdaSound.BOOLEAN_SETTING_OFF + val pitch = if (value) 1.0 else 0.9 + playSoundRandomly(sound.event, pitch) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/EnumSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/EnumSlider.kt new file mode 100644 index 000000000..4ffca31b1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/EnumSlider.kt @@ -0,0 +1,51 @@ +package com.lambda.gui.impl.clickgui.buttons.setting + +import com.lambda.config.settings.comparable.EnumSetting +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.gui.impl.clickgui.buttons.SettingButton +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Vec2d +import com.lambda.util.math.transform +import java.awt.Color +import kotlin.math.floor + +class EnumSlider > ( + setting: EnumSetting, + owner: ChildLayer.Drawable, ModuleButton> +) : Slider>(setting, owner) { + private val values = setting.enumValues + private val enumSize = values.size + + override val progress get() = if (dragProgress != -1.0) dragProgress else + transform(value.ordinal.toDouble(), 0.0, enumSize - 1.0, 0.0, 1.0) + + private var dragProgress = -1.0 + + override fun setValueByProgress(progress: Double) { + val entryIndex = floor(progress * enumSize).toInt().coerceIn(0, enumSize - 1) + value = values[entryIndex] + dragProgress = progress + } + + override fun onPress(e: GuiEvent.MouseClick) {} + + override fun onRelease(e: GuiEvent.MouseClick) { + if (dragProgress == -1.0) setting.next() + dragProgress = -1.0 + } + + init { + renderer.font { + text = setting.displayValue + + val progress = 1.0 - activeAnimation + scale = lerp(0.5, 1.0, progress) + position = Vec2d(rect.right, rect.center.y) - Vec2d(ClickGui.windowPadding + stringWidth, 0.0) + color = Color.WHITE.setAlpha(lerp(0.0, progress, showAnimation)) + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..84541b64e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/NumberSlider.kt @@ -0,0 +1,72 @@ +package com.lambda.gui.impl.clickgui.buttons.setting + +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.clickgui.buttons.ModuleButton +import com.lambda.gui.impl.clickgui.buttons.SettingButton +import com.lambda.util.Mouse +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 ( + setting: NumericSetting, + owner: ChildLayer.Drawable, ModuleButton> +) : Slider>( + setting, owner +) where N : Number, N : Comparable { + private val doubleRange get() = setting.range.let { it.start.toDouble()..it.endInclusive.toDouble() } + override val progress get() = doubleRange.normalize(value.toDouble()) + + private val layer = ChildLayer.Drawable(owner.gui, this, owner.renderer, ::rect, InputBarOverlay::isActive) + private val inputBar: InputBarOverlay = object : InputBarOverlay(renderer, layer) { + override val pressAnimation get() = this@NumberSlider.pressAnimation + override val interactAnimation get() = this@NumberSlider.interactAnimation + override val hoverFontAnimation get() = this@NumberSlider.hoverFontAnimation + override val showAnimation get() = this@NumberSlider.showAnimation + + override fun getText() = value.let(Number::toString) + override fun setValue(string: String) { + string.toDoubleOrNull()?.let(::setValue) + } + }.apply(layer::addChild) + + override val textColor get() = super.textColor.multAlpha(1.0 - inputBar.activeAnimation) + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + layer.onEvent(e) + } + + override fun unfocus() { + inputBar.isActive = false + } + + override fun performClickAction(e: GuiEvent.MouseClick) { + if (e.button != Mouse.Button.Right) return + if (!inputBar.isActive) (owner.gui as? AbstractClickGui)?.unfocusSettings() + inputBar.toggle() + } + + override fun slide(mouse: Vec2d) { + if (!inputBar.isActive) super.slide(mouse) + } + + override fun setValueByProgress(progress: Double) { + setValue(lerp( + setting.range.start.toDouble(), + setting.range.endInclusive.toDouble(), + progress + )) + } + + private fun setValue(valueIn: Double) { + value = value.typeConvert(valueIn.roundToStep(setting.step.toDouble())) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/Slider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/Slider.kt new file mode 100644 index 000000000..df78bf16e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/Slider.kt @@ -0,0 +1,61 @@ +package com.lambda.gui.impl.clickgui.buttons.setting + +import com.lambda.config.AbstractSetting +import com.lambda.core.LambdaSound +import com.lambda.core.SoundManager.playSound +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.api.GuiEvent +import com.lambda.gui.api.component.core.list.ChildLayer +import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.gui.impl.clickgui.buttons.SettingButton +import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Vec2d +import com.lambda.util.math.transform + +abstract class Slider >( + setting: T, owner: ChildLayer.Drawable, ModuleButton> +) : SettingButton(setting, owner) { + protected abstract val progress: Double + private val progressAnimation by animation.exp(::progress, 0.6) + private val renderProgress get() = lerp(0.0, progressAnimation, showAnimation) + + protected abstract fun setValueByProgress(progress: Double) + private var lastPlayedValue = value + private var lastPlayedTiming = 0L + + init { + renderer.filled { + position = rect.moveSecond(Vec2d(-rect.size.x * (1.0 - renderProgress), 0.0)).shrink(shrinkAnimation) + shade = GuiSettings.shade + color(GuiSettings.mainColor.multAlpha(showAnimation * 0.3)) + } + } + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + if (e is GuiEvent.MouseMove) slide(e.mouse) + } + + override fun onPress(e: GuiEvent.MouseClick) { + super.onPress(e) + slide(e.mouse) + } + + protected open fun slide(mouse: Vec2d) { + if (activeButton != Mouse.Button.Left) return + + val p = transform(mouse.x, rect.left, rect.right, 0.0, 1.0).coerceIn(0.0, 1.0) + setValueByProgress(p) + + val time = System.currentTimeMillis() + if (lastPlayedValue == value || time - lastPlayedTiming < 50) return + + lastPlayedValue = value + lastPlayedTiming = time + + playSound(LambdaSound.BUTTON_CLICK.event, lerp(0.9, 1.2, p)) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..fa3a2d1b0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/setting/StringButton.kt @@ -0,0 +1,44 @@ +package com.lambda.gui.impl.clickgui.buttons.setting + +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.clickgui.buttons.ModuleButton +import com.lambda.gui.impl.clickgui.buttons.SettingButton +import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.multAlpha + +class StringButton( + setting: StringSetting, + owner: ChildLayer.Drawable, ModuleButton> +) : SettingButton(setting, owner) { + private val layer = ChildLayer.Drawable(owner.gui, this, owner.renderer, ::rect, InputBarOverlay::isActive) + private val inputBar: InputBarOverlay = object : InputBarOverlay(renderer, layer) { + override val pressAnimation get() = this@StringButton.pressAnimation + override val interactAnimation get() = this@StringButton.interactAnimation + override val hoverFontAnimation get() = this@StringButton.hoverFontAnimation + override val showAnimation get() = this@StringButton.showAnimation + + override fun getText() = value + override fun setValue(string: String) { value = string } + }.apply(layer::addChild) + + override val textColor get() = super.textColor.multAlpha(1.0 - inputBar.activeAnimation) + + override fun onEvent(e: GuiEvent) { + super.onEvent(e) + layer.onEvent(e) + } + + override fun unfocus() { + inputBar.isActive = false + } + + override fun performClickAction(e: GuiEvent.MouseClick) { + if (e.button != Mouse.Button.Right) return + if (!inputBar.isActive) (owner.gui as? AbstractClickGui)?.unfocusSettings() + inputBar.toggle() + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..1b7dce0c1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/ModuleWindow.kt @@ -0,0 +1,57 @@ +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.clickgui.buttons.ModuleButton +import com.lambda.module.Module + +abstract class ModuleWindow( + override var title: String, + override var width: Double = 110.0, + override var height: Double = 300.0, + gui: AbstractClickGui +) : ListWindow(gui) { + private var lastUpdate = 0L + + abstract fun getModuleList(): Collection + + private fun updateModules() { + val time = System.currentTimeMillis() + if (time - lastUpdate < 1000L) return + lastUpdate = time + + contentComponents.apply { + val modules = getModuleList() + + // Add missing module buttons + modules.filter { module -> + children.all { button -> + button.module != module + } + }.map { ModuleButton(it, contentComponents) } + .forEach(contentComponents::addChild) + + // Remove deleted modules + children.forEach { button -> + if (button.module !in modules) { + this@ModuleWindow.gui.scheduleAction { + removeChild(button) + } + } + } + } + } + + override fun onEvent(e: GuiEvent) { + if (e is GuiEvent.Show || e is GuiEvent.Tick) updateModules() + + if (e is GuiEvent.Tick) { + contentComponents.children.sortBy { + it.module.name + } + } + + super.onEvent(e) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt deleted file mode 100644 index 7249a0e11..000000000 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.lambda.gui.impl.clickgui.windows - -import com.lambda.gui.api.LambdaGui -import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.api.component.core.list.IChildComponent -import com.lambda.gui.impl.clickgui.LambdaClickGui -import com.lambda.gui.impl.clickgui.buttons.ModuleButton -import com.lambda.module.ModuleRegistry -import com.lambda.module.modules.client.ClickGui -import com.lambda.module.tag.ModuleTag - -class TagWindow( - val tags: Set = setOf(), - override var title: String = "Untitled", - override var width: Double = 110.0, - override var height: Double = 300.0, - override val owner: LambdaGui = LambdaClickGui -) : WindowComponent(), IChildComponent { - init { - ModuleRegistry.modules.filter { module -> - module.customTags.value.any(tags::contains) || tags.isEmpty() - }.forEach { - children.add(ModuleButton(it, this)) - } - } - - override fun onRender() { - updateModules() - super.onRender() - } - - private fun updateModules() { - children.sortBy { it.module.name } - - // ToDo: Update tag filter - - children.forEachIndexed { i, button -> - button.heightOffset = i * (ClickGui.buttonHeight + ClickGui.buttonStep) - } - } -} \ No newline at end of file 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 new file mode 100644 index 000000000..7d6103ea7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/CustomModuleWindow.kt @@ -0,0 +1,13 @@ +package com.lambda.gui.impl.clickgui.windows.tag + +import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.clickgui.windows.ModuleWindow +import com.lambda.module.Module + +class CustomModuleWindow( + override var title: String = "Untitled", + val modules: MutableList = mutableListOf(), + gui: AbstractClickGui +) : ModuleWindow(title, gui = gui) { + override fun getModuleList() = modules +} \ No newline at end of file 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 new file mode 100644 index 000000000..a418ba53e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/tag/TagWindow.kt @@ -0,0 +1,14 @@ +package com.lambda.gui.impl.clickgui.windows.tag + +import com.lambda.gui.impl.clickgui.AbstractClickGui +import com.lambda.gui.impl.clickgui.windows.ModuleWindow +import com.lambda.module.ModuleRegistry +import com.lambda.module.tag.ModuleTag + +class TagWindow( + val tag: ModuleTag, + owner: AbstractClickGui +) : ModuleWindow(tag.name, gui = owner) { + override fun getModuleList() = ModuleRegistry.modules + .filter { it.defaultTags.firstOrNull() == tag } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 5ead16485..86db99a0d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -63,8 +63,8 @@ object RotationManager : Loadable { private fun rotate(newContext: RotationContext?) = runSafe { prevRotation = currentRotation - (keepTicks--).coerceAtLeast(0) - (pauseTicks--).coerceAtLeast(0) + keepTicks-- + pauseTicks-- currentContext?.let { current -> if (keepTicks + current.config.resetTicks < 0 || pauseTicks >= 0) { @@ -156,7 +156,7 @@ object RotationManager : Loadable { baritoneContext = null } - listener { + listener(Int.MAX_VALUE) { processPlayerMovement(it) } } @@ -225,4 +225,4 @@ object RotationManager : Loadable { } } } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 168d4af88..30b48943b 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -79,7 +79,7 @@ import com.lambda.util.Nameable * @property name The name of the module, displayed in-game. * @property description The description of the module, * shown on hover over the module button in the GUI and in commands. - * @property defaultTags The set of [ModuleTag]s associated with the module. + * @property tag The leading module tag associated with the module. * @property alwaysListening If true, the module's listeners will be triggered even if the module is not enabled. * @property isEnabledSetting The setting that determines if the module is enabled. * @property keybindSetting The setting that determines the keybind for the module. 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 4b18b7b80..3a9af4480 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 @@ -5,19 +5,24 @@ import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.threading.mainThread -import java.awt.Color +import com.lambda.util.KeyCode object ClickGui : Module( name = "ClickGui", description = "Sexy", - defaultTags = setOf(ModuleTag.CLIENT) + defaultTags = setOf(ModuleTag.CLIENT), + defaultKeybind = KeyCode.RIGHT_SHIFT ) { // General val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1) val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1) val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) - val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1) + val buttonStep by setting("Button Step", 0.0, 0.0..5.0, 0.1) + val windowBlur by setting("Window Blur", 30, 0..100, 1) + + // Animation + val openSpeed by setting("Open Speed", 0.6, 0.1..1.0, 0.01) + val closeSpeed by setting("Close Speed", 0.7, 0.1..1.0, 0.01) init { onEnable { 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 ed0c42c82..e552225b5 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 @@ -13,7 +13,6 @@ object FontSettings : Module( val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05, visibility = { 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) - val amountOfGlyphs by setting("Glyph Count", 2048, 128..65536, 1, description = "Restart required") 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/GuiSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt index 20f04c5ad..9b341a941 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt @@ -17,11 +17,11 @@ object GuiSettings : Module( // Colors val primaryColor by setting("Primary Color", Color(130, 200, 255), visibility = { page == Page.Colors }) val secondaryColor by setting("Secondary Color", Color(225, 130, 225), visibility = { page == Page.Colors && shade }) - val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) - val glow by setting("Glow", true, visibility = { page == Page.Colors }) - val shade by setting("Shade Color", true, visibility = { page == Page.Colors }) - val colorWidth by setting("Color Width", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) - val colorHeight by setting("Color Height", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) + val backgroundColor by setting("Background Color", Color(50, 50, 50), visibility = { page == Page.Colors }) + val shade by setting("Shade", true, visibility = { page == Page.Colors }) + val shadeBackground by setting("Shade Background", true, visibility = { page == Page.Colors }) + val colorWidth by setting("Color Width", 400.0, 10.0..1000.0, 10.0, visibility = { page == Page.Colors && shade }) + val colorHeight by setting("Color Height", 400.0, 10.0..1000.0, 10.0, visibility = { page == Page.Colors && shade }) val colorSpeed by setting("Color Speed", 1.0, 0.1..10.0, 0.1, visibility = { page == Page.Colors && shade }) val mainColor: Color get() = if (shade) Color.WHITE else primaryColor diff --git a/common/src/main/kotlin/com/lambda/module/BaritoneTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BaritoneTest.kt similarity index 68% rename from common/src/main/kotlin/com/lambda/module/BaritoneTest.kt rename to common/src/main/kotlin/com/lambda/module/modules/debug/BaritoneTest.kt index 604b744f6..cb29bcfb3 100644 --- a/common/src/main/kotlin/com/lambda/module/BaritoneTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BaritoneTest.kt @@ -1,13 +1,16 @@ -package com.lambda.module +package com.lambda.module.modules.debug import baritone.api.BaritoneAPI import baritone.api.pathing.goals.GoalXZ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag object BaritoneTest : Module( name = "BaritoneTest", - description = "Test Baritone" + description = "Test Baritone", + defaultTags = setOf(ModuleTag.DEBUG) ) { init { listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt similarity index 80% rename from common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt rename to common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt index 3b898592c..9db4a1aba 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/EntityTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt @@ -1,15 +1,16 @@ -package com.lambda.module.modules +package com.lambda.module.modules.debug import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag import com.lambda.util.world.WorldUtils.getClosestEntity import net.minecraft.entity.Entity object EntityTest : Module( name = "EntityTest", description = "Test entity", - defaultTags = setOf() + defaultTags = setOf(ModuleTag.DEBUG) ) { init { listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt similarity index 91% rename from common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt rename to common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt index 7f89d2ca8..f24e0609f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt @@ -1,10 +1,10 @@ -package com.lambda.module.modules.render +package com.lambda.module.modules.debug import com.lambda.Lambda.LOG import com.lambda.event.events.PacketEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module -import com.lambda.util.Communication.info +import com.lambda.module.tag.ModuleTag import com.lambda.util.DynamicReflectionSerializer.dynamicString import net.minecraft.network.packet.c2s.play.* import net.minecraft.network.packet.s2c.play.InventoryS2CPacket @@ -13,7 +13,7 @@ import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket object InventoryDebug : Module( name = "InventoryDebug", description = "Debugs the inventory", - defaultTags = setOf() + defaultTags = setOf(ModuleTag.DEBUG) ) { init { listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt similarity index 93% rename from common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt rename to common/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt index 2efdabd38..cdfb981b6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt @@ -1,10 +1,11 @@ -package com.lambda.module.modules +package com.lambda.module.modules.debug import com.lambda.config.InteractionSettings import com.lambda.config.RotationSettings import com.lambda.event.events.RotationEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos @@ -13,7 +14,7 @@ import net.minecraft.util.math.Direction object RotationTest : Module( name = "RotationTest", description = "Test rotation", - defaultTags = setOf() + defaultTags = setOf(ModuleTag.DEBUG), ) { private val rotationConfig = RotationSettings(this) private val interactionConfig = InteractionSettings(this) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 0b1e24942..26511de32 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -14,7 +14,6 @@ import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.rotation.RotationMode import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.KeyCode import com.lambda.util.player.MovementUtils.cancel import com.lambda.util.player.MovementUtils.verticalMovement import com.lambda.util.primitives.extension.interpolate @@ -29,8 +28,7 @@ import net.minecraft.util.math.Vec3d object Freecam : Module( name = "Freecam", description = "Move your camera freely", - defaultTags = setOf(ModuleTag.RENDER), - defaultKeybind = KeyCode.G + defaultTags = setOf(ModuleTag.PLAYER) ) { private val speed by setting("Speed", 0.5f, 0.1f..1.0f, 0.1f) private val sprint by setting("Sprint Multiplier", 3.0f, 0.1f..10.0f, 0.1f, description = "Set below 1.0 to fly slower on sprint.") @@ -80,11 +78,11 @@ object Freecam : Module( it.context = RotationContext(rotation, rotationConfig) } - listener { + listener { event ->2 // Don't block baritone from working - if (player.input !is PlayerMovementInput) { + if (event.input !is PlayerMovementInput) { // Reset actual input - player.input.cancel() + event.input.cancel() } // Create new input for freecam diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 8af3303ec..5b13ab4c0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -4,6 +4,7 @@ import com.google.gson.* import com.lambda.brigadier.CommandResult import com.lambda.config.RotationSettings import com.lambda.context.SafeContext +import com.lambda.core.SoundManager.playSound import com.lambda.core.TimerManager import com.lambda.event.EventFlow.lambdaScope import com.lambda.event.events.KeyPressEvent @@ -25,9 +26,7 @@ import com.lambda.util.FolderRegister.locationBoundDirectory import com.lambda.util.Formatting.asString import com.lambda.util.Formatting.getTime import com.lambda.util.KeyCode -import com.lambda.util.SoundUtils.playSound import com.lambda.util.StringUtils.sanitizeForFilename -import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.primitives.extension.rotation import com.lambda.util.text.* import kotlinx.coroutines.Dispatchers diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/render/FakePlayer.kt index 9d2989c2b..ede08bc75 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/FakePlayer.kt @@ -15,19 +15,14 @@ object FakePlayer : Module( private val playerName by setting("Name", "Steve") private val uuid = UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77") - private var fakePlayer: OtherClientPlayerEntity? = null init { onEnable { - OtherClientPlayerEntity(world, GameProfile(uuid, playerName)).apply { - copyFrom(player) - id = -2024-4-20 - - fakePlayer = this - } - - world.addEntity(fakePlayer) + fakePlayer = OtherClientPlayerEntity(world, GameProfile(uuid, playerName)) + .apply(player::copyFrom) + .apply(world::addEntity) + .apply { id = -2024-4-20 } } onDisable { diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/render/RenderTest.kt new file mode 100644 index 000000000..6f0b44c98 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/render/RenderTest.kt @@ -0,0 +1,58 @@ +package com.lambda.module.modules.render + +import com.lambda.Lambda.mc +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.world.DirectionMask.mask +import com.lambda.graphics.renderer.world.core.box.DynamicFilledRenderer +import com.lambda.graphics.renderer.world.core.box.DynamicOutlineRenderer +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.ColorUtils.a +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.toIntSign +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.util.math.Box +import java.awt.Color + +object RenderTest : Module( + name = "RenderTest", + description = "RenderTest", + defaultTags = setOf(ModuleTag.DEBUG) +) { + private val test1 by setting("Toggle visibility", true) + private val test21 by setting("Hallo 1", true, visibility = ::test1) + private val test22 by setting("Hallo Slider", 1.0, 0.0..5.0, 0.5, visibility = ::test1) + private val test23 by setting("Hallo String", "bruh", visibility = ::test1) + 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 = this@RenderTest.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 = this@RenderTest.color.setAlpha(color.a + flag.toIntSign() * 0.2) + sides = block?.side?.mask ?: sides + } + + listener { + filled.update() + outline.update() + } + + listener { + filled.render() + outline.render() + } + } +} \ 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 6da4dd51c..3fd5ec5e2 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 @@ -7,6 +7,4 @@ object XRay : Module( name = "XRay", description = "Allows you to see ores through walls", defaultTags = setOf(ModuleTag.RENDER) -) { - -} \ No newline at end of file +) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/Timer.kt b/common/src/main/kotlin/com/lambda/module/modules/world/Timer.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/module/modules/Timer.kt rename to common/src/main/kotlin/com/lambda/module/modules/world/Timer.kt index 430df32cd..8094444cf 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Timer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/world/Timer.kt @@ -1,4 +1,4 @@ -package com.lambda.module.modules +package com.lambda.module.modules.world import com.lambda.event.events.ClientEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -8,7 +8,7 @@ import com.lambda.module.tag.ModuleTag object Timer : Module( name = "Timer", description = "Modify client tick speed.", - defaultTags = setOf(ModuleTag.CLIENT) + defaultTags = setOf(ModuleTag.WORLD) ) { private val timer by setting("Timer", 50, 0..1000, 5, unit = "ms/tick") 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 4e9e43981..c5b84bbe5 100644 --- a/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt +++ b/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt @@ -15,7 +15,7 @@ import com.lambda.util.Nameable * * @param name The name of the tag. */ -class ModuleTag(override val name: String) : Nameable { +data class ModuleTag(override val name: String) : Nameable { companion object { val COMBAT = ModuleTag("Combat") val MOVEMENT = ModuleTag("Movement") @@ -24,11 +24,15 @@ class ModuleTag(override val name: String) : Nameable { val WORLD = ModuleTag("World") val MISC = ModuleTag("Misc") val CLIENT = ModuleTag("Client") + + // Do something with this ? val HIDDEN = ModuleTag("Hidden") val NETWORK = ModuleTag("Network") val GRIM = ModuleTag("Grim") val BYPASS = ModuleTag("Bypass") val AUTOMATION = ModuleTag("Automation") val DEBUG = ModuleTag("Debug") + + val defaults = listOf(COMBAT, MOVEMENT, RENDER, PLAYER, WORLD, DEBUG, CLIENT) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/util/SoundUtils.kt b/common/src/main/kotlin/com/lambda/util/SoundUtils.kt deleted file mode 100644 index ff9ed8e64..000000000 --- a/common/src/main/kotlin/com/lambda/util/SoundUtils.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.lambda.util - -import com.lambda.context.SafeContext -import net.minecraft.client.sound.PositionedSoundInstance -import net.minecraft.sound.SoundEvent - -object SoundUtils { - fun SafeContext.playSound(event: SoundEvent, pitch: Float = 1.0f) { - mc.soundManager.play( - PositionedSoundInstance.master(event, pitch) - ) - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt b/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt new file mode 100644 index 000000000..750f3a61f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt @@ -0,0 +1,21 @@ +package com.lambda.util + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +abstract class TransformedObservable(initialValue: T) : ReadWriteProperty { + private var value = initialValue + + override fun getValue(thisRef: Any?, property: KProperty<*>) = value + + override fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: T) { + val oldValue = this.value + this.value = transform(valueIn) + if (oldValue != value) onChange(oldValue, value) + } + + protected open fun transform(value: T): T = value + protected open fun onChange(oldValue: T, newValue: T) {} + + override fun toString(): String = "TransformedObservableProperty(value=$value)" +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index 94abd79f5..5538ca020 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -59,6 +59,7 @@ object MathUtils { private fun decimalPlaces(value: Double) = BigDecimal.valueOf(value).scale() fun random(v1: Double, v2: Double): Double { + if (v1 == v2) return v1 val min = min(v1, v2) val max = max(v1, v2) return nextDouble(min, max) diff --git a/common/src/main/kotlin/com/lambda/util/math/Rect.kt b/common/src/main/kotlin/com/lambda/util/math/Rect.kt index 5b58cf6f4..7c222d5b7 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Rect.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Rect.kt @@ -51,5 +51,7 @@ data class Rect(private val pos1: Vec2d, private val pos2: Vec2d) { fun basedOn(base: Vec2d, size: Vec2d) = Rect(base, base + size) + + fun Rect.inv() = Rect(rightBottom, leftTop) } } diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/Box.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/Box.kt new file mode 100644 index 000000000..8afe25f01 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/Box.kt @@ -0,0 +1,8 @@ +package com.lambda.util.primitives.extension + +import net.minecraft.util.math.Box +import net.minecraft.util.math.Vec3d + +val Box.min get() = Vec3d(minX, minY, minZ) + +val Box.max get() = Vec3d(maxX, maxY, maxZ) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/Entity.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/Entity.kt index 7c61d51e4..86ed6f794 100644 --- a/common/src/main/kotlin/com/lambda/util/primitives/extension/Entity.kt +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/Entity.kt @@ -2,29 +2,13 @@ package com.lambda.util.primitives.extension import com.lambda.interaction.rotation.Rotation import com.lambda.util.math.MathUtils.lerp -import net.minecraft.client.MinecraftClient import net.minecraft.entity.Entity -import net.minecraft.util.math.Box import net.minecraft.util.math.Vec3d val Entity.prevPos get() = Vec3d(prevX, prevY, prevZ) -val Entity.interpolatedPos - get() = lerp(prevPos, pos, MinecraftClient.getInstance().partialTicks) - val Entity.rotation get() = Rotation(yaw, pitch) -val Entity.interpolatedBox: Box - get() { - val box = boundingBox - val xw = (box.maxX - box.minX) * 0.5 - val yw = box.maxY - box.minY - val zw = (box.maxZ - box.minZ) * 0.5 - - val pos = interpolatedPos - return Box(pos.x - xw, pos.y, pos.z - zw, pos.x + xw, pos.y + yw, pos.z + zw) - } - fun Vec3d.interpolate(other: Vec3d, t: Double) = lerp(this, other, t) \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/post/blur.frag b/common/src/main/resources/assets/lambda/shaders/fragment/post/blur.frag new file mode 100644 index 000000000..48dd85bb7 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/post/blur.frag @@ -0,0 +1,32 @@ +#version 330 core + +#define pow2(x) (x * x) + +uniform sampler2D u_Texture; +uniform vec2 u_Direction; +uniform int u_BlurLevel; +uniform float u_Alpha; + +in vec2 v_TexCoord; +out vec4 color; + +float gaussian(float x) { + return exp(-pow2(x) / (2.0 * pow2(u_BlurLevel) / (2.50662 * u_BlurLevel))); +} + +void main() { + float totalWeight = 1.0; + color = texture(u_Texture, v_TexCoord); + + for (int i = -u_BlurLevel; i <= u_BlurLevel; ++i) { + float offset = float(i); + float amount = gaussian(offset); + vec2 texOffset = offset * u_Direction; + + color += texture(u_Texture, v_TexCoord + texOffset) * amount; + totalWeight += amount; + } + + color /= totalWeight; + color *= vec4(1.0, 1.0, 1.0, u_Alpha); +} diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_color.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_color.frag new file mode 100644 index 000000000..71f50775d --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_color.frag @@ -0,0 +1,8 @@ +#version 330 core + +in vec4 v_Color; +out vec4 color; + +void main() { + color = v_Color; +} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag similarity index 58% rename from common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag rename to common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag index 08a07c8be..306a00247 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag @@ -1,6 +1,5 @@ #version 330 core -uniform bool u_Shade; uniform float u_Time; uniform vec4 u_Color1; uniform vec4 u_Color2; @@ -11,18 +10,27 @@ in vec2 v_TexCoord; in vec4 v_Color; in vec2 v_Size; in float v_RoundRadius; +in float v_Shade; out vec4 color; -#define SMOOTHING 0.5 +#define SMOOTHING 0.25 +#define NOISE_GRANULARITY 0.005 + +vec4 noise() { + // https://shader-tutorial.dev/advanced/color-banding-dithering/ + float random = fract(sin(dot(v_TexCoord, vec2(12.9898, 78.233))) * 43758.5453); + float ofs = mix(-NOISE_GRANULARITY, NOISE_GRANULARITY, random); + return vec4(ofs, ofs, ofs, 0.0); +} vec4 shade() { - if (!u_Shade) return v_Color; + if (v_Shade != 1.0) return v_Color; vec2 pos = v_Position * u_Size; - float p = sin(pos.x + pos.y - u_Time) * 0.5 + 0.5; + float p = sin(pos.x - pos.y - u_Time) * 0.5 + 0.5; - return mix(u_Color1, u_Color2, p) * vec4(1.0, 1.0, 1.0, v_Color.a); + return mix(u_Color1, u_Color2, p) * v_Color; } vec4 round() { @@ -41,5 +49,5 @@ vec4 round() { } void main() { - color = shade() * round(); + color = shade() * round() + noise(); } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_outline.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_outline.frag new file mode 100644 index 000000000..b6647a229 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_outline.frag @@ -0,0 +1,31 @@ +#version 330 core + +uniform float u_Time; +uniform vec4 u_Color1; +uniform vec4 u_Color2; +uniform vec2 u_Size; + +in vec2 v_Position; +in float v_Alpha; +in vec4 v_Color; +in float v_Shade; + +out vec4 color; + +vec4 shade() { + if (v_Shade != 1.0) return v_Color; + + vec2 pos = v_Position * u_Size; + float p = sin(pos.x - pos.y - u_Time) * 0.5 + 0.5; + + return mix(u_Color1, u_Color2, p) * v_Color; +} + +vec4 glow() { + float newAlpha = min(1.0, (v_Alpha * v_Alpha)); + return vec4(1.0, 1.0, 1.0, newAlpha); +} + +void main() { + color = shade() * glow(); +} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/post/blur.vert b/common/src/main/resources/assets/lambda/shaders/vertex/post/blur.vert new file mode 100644 index 000000000..8280235af --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/post/blur.vert @@ -0,0 +1,14 @@ +#version 330 core + +layout (location = 0) in vec4 pos; +layout (location = 1) in vec2 uv; + +uniform mat4 u_Projection; +uniform mat4 u_ModelView; + +out vec2 v_TexCoord; + +void main() { + gl_Position = u_Projection * u_ModelView * pos; + v_TexCoord = uv; +} \ No newline at end of file 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 new file mode 100644 index 000000000..7061f3df9 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert @@ -0,0 +1,20 @@ +#version 330 core + +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 float u_TickDelta; +uniform vec3 u_CameraPosition; + +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); + 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 new file mode 100644 index 000000000..8ab8e8660 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert @@ -0,0 +1,18 @@ +#version 330 core + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec4 color; + +uniform mat4 u_Projection; +uniform mat4 u_ModelView; + +uniform vec3 u_CameraPosition; + +out vec4 v_Color; + +#define VERTEX_POSITION pos - u_CameraPosition + +void main() { + gl_Position = u_Projection * u_ModelView * vec4(VERTEX_POSITION, 1.0); + v_Color = color; +} diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert similarity index 56% rename from common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert rename to common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert index 39940d4b9..0e3891429 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert @@ -2,8 +2,10 @@ layout (location = 0) in vec4 pos; layout (location = 1) in vec2 uv; -layout (location = 2) in vec3 data; -layout (location = 3) in vec4 color; +layout (location = 2) in vec2 size; +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; @@ -13,14 +15,16 @@ out vec2 v_TexCoord; out vec4 v_Color; out vec2 v_Size; out float v_RoundRadius; +out float v_Shade; void main() { gl_Position = u_Projection * u_ModelView * pos; - v_Position = pos.xy; + v_Position = gl_Position.xy * 0.5 + 0.5; v_TexCoord = uv; v_Color = color; - v_Size = data.xy; - v_RoundRadius = data.z; + v_Size = size; + v_RoundRadius = round; + v_Shade = shade; } \ No newline at end of file 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 new file mode 100644 index 000000000..192864a29 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert @@ -0,0 +1,23 @@ +#version 330 core + +layout (location = 0) in vec4 pos; +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; + +out vec2 v_Position; +out float v_Alpha; +out vec4 v_Color; +out float v_Shade; + +void main() { + gl_Position = u_Projection * u_ModelView * pos; + + v_Position = gl_Position.xy * 0.5 + 0.5; + v_Alpha = alpha; + v_Color = color; + v_Shade = shade; +} \ No newline at end of file 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 new file mode 100644 index 000000000..2ec823a2e --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert @@ -0,0 +1,23 @@ +#version 330 core + +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 float u_TickDelta; +uniform vec3 u_CameraPosition; + +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) + +void main() { + gl_Position = gl_VertexID % 2 == 0 ? SCREEN_CENTER : PROJECTED; + v_Color = color; +} 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 new file mode 100644 index 000000000..838117d38 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec4 color; + +uniform mat4 u_Projection; +uniform mat4 u_ModelView; + +uniform vec3 u_CameraPosition; + +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) + +void main() { + gl_Position = gl_VertexID % 2 == 0 ? SCREEN_CENTER : PROJECTED; + v_Color = color; +} diff --git a/common/src/main/resources/assets/lambda/sounds.json b/common/src/main/resources/assets/lambda/sounds.json new file mode 100644 index 000000000..e85566499 --- /dev/null +++ b/common/src/main/resources/assets/lambda/sounds.json @@ -0,0 +1,37 @@ +{ + "button_click": { + "sounds": [ + "lambda:ui/button_click" + ] + }, + "bool_on": { + "sounds": [ + "lambda:ui/bool_on" + ] + }, + "bool_off": { + "sounds": [ + "lambda:ui/bool_off" + ] + }, + "module_on": { + "sounds": [ + "lambda:ui/module_on" + ] + }, + "module_off": { + "sounds": [ + "lambda:ui/module_off" + ] + }, + "settings_open": { + "sounds": [ + "lambda:ui/settings_open" + ] + }, + "settings_close": { + "sounds": [ + "lambda:ui/settings_close" + ] + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/sounds/ui/bool_off.ogg b/common/src/main/resources/assets/lambda/sounds/ui/bool_off.ogg new file mode 100644 index 000000000..6ec76aff2 Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/bool_off.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/bool_on.ogg b/common/src/main/resources/assets/lambda/sounds/ui/bool_on.ogg new file mode 100644 index 000000000..73291d94f Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/bool_on.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/button_click.ogg b/common/src/main/resources/assets/lambda/sounds/ui/button_click.ogg new file mode 100644 index 000000000..8f3bb1f67 Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/button_click.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/module_off.ogg b/common/src/main/resources/assets/lambda/sounds/ui/module_off.ogg new file mode 100644 index 000000000..f0a8854c0 Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/module_off.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/module_on.ogg b/common/src/main/resources/assets/lambda/sounds/ui/module_on.ogg new file mode 100644 index 000000000..40d853b6f Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/module_on.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/settings_close.ogg b/common/src/main/resources/assets/lambda/sounds/ui/settings_close.ogg new file mode 100644 index 000000000..eaa9cb8b9 Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/settings_close.ogg differ diff --git a/common/src/main/resources/assets/lambda/sounds/ui/settings_open.ogg b/common/src/main/resources/assets/lambda/sounds/ui/settings_open.ogg new file mode 100644 index 000000000..02ebe61c0 Binary files /dev/null and b/common/src/main/resources/assets/lambda/sounds/ui/settings_open.ogg differ