From 48fe4f7dcd7b2d90dc9ed21ec52e07b9bb1b269a Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 15 Mar 2025 13:57:49 +0300 Subject: [PATCH 01/25] cleanup --- .../main/kotlin/com/lambda/gui/GuiManager.kt | 2 +- .../com/lambda/gui/component/window/Window.kt | 3 ++ .../gui/component/window/WindowContent.kt | 16 ++++++----- .../gui/impl/clickgui/core/SliderLayout.kt | 28 +++++++++++++------ .../clickgui/module/settings/KeybindPicker.kt | 18 ++++++------ .../clickgui/module/settings/SettingSlider.kt | 1 - .../{ => module}/settings/UnitButton.kt | 4 +-- 7 files changed, 43 insertions(+), 29 deletions(-) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/{ => module}/settings/UnitButton.kt (93%) diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index 822a8e3f2..eee41590e 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -29,7 +29,7 @@ import com.lambda.gui.impl.clickgui.module.settings.BooleanButton.Companion.bool import com.lambda.gui.impl.clickgui.module.settings.EnumSlider.Companion.enumSetting import com.lambda.gui.impl.clickgui.module.settings.KeybindPicker.Companion.keybindSetting import com.lambda.gui.impl.clickgui.module.settings.NumberSlider.Companion.numericSetting -import com.lambda.gui.impl.clickgui.settings.UnitButton.Companion.unitSetting +import com.lambda.gui.impl.clickgui.module.settings.UnitButton.Companion.unitSetting import kotlin.reflect.KClass object GuiManager : Loadable { diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt index 0843e6bed..2a61fef90 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt @@ -150,6 +150,9 @@ open class Window( properties.clampPosition = owner is ScreenLayout onUpdate { + // Update it here + content.updateHeight() + width = widthAnimation height = titleBar.height + when (minimizing) { Minimizing.Disabled -> targetHeight diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt index 9a9d2cc5c..af5ccd69e 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt @@ -73,13 +73,6 @@ class WindowContent( positionX = owner.titleBar.positionX positionY = owner.titleBar.let { it.positionY + it.height } + renderScrollOffset * scrollable.toInt() width = owner.width - - height = ClickGui.padding * 2 - - val lastIndex = children.lastIndex - children.forEachIndexed { i, it -> - height += layoutHeight(it, false, i == lastIndex) - } } onShow { @@ -113,6 +106,15 @@ class WindowContent( } } + fun updateHeight() { + height = ClickGui.padding * 2 + + val lastIndex = children.lastIndex + children.forEachIndexed { i, it -> + height += layoutHeight(it, false, i == lastIndex) + } + } + private fun layoutHeight(layout: Layout, animate: Boolean, isLast: Boolean = false): Double { var height = layout.height + ClickGui.listStep * (!isLast).toInt() val animated = layout as? AnimatedChild ?: return height diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt index 55464f924..ef8305517 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt @@ -27,6 +27,7 @@ import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.settings.SettingSlider import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse +import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d import com.lambda.util.math.multAlpha import com.lambda.util.math.setAlpha @@ -34,12 +35,12 @@ import com.lambda.util.math.transform import java.awt.Color class SliderLayout( - owner: Layout + owner: Layout, + private val isVertical: Boolean ) : AnimatedChild(owner, "") { // Not a great solution private val setting = owner as? SettingSlider<*, *> private val showAnim get() = setting?.showAnimation ?: showAnimation - private val hoverAnim get() = setting?.hoverAnimation ?: hoverAnimation private val pressedBut get() = setting?.pressedButton ?: pressedButton // Actions @@ -74,9 +75,9 @@ class SliderLayout( } private val dragProgress: Double get() = transform( - mousePosition.x - bg.positionX, - 0.0, bg.width, - 0.0, 1.0 + if (isVertical) mousePosition.y - bg.positionY else mousePosition.x - bg.positionX, + 0.0, if (isVertical) bg.height else bg.width, + isVertical.toInt().toDouble(), 1.0 - isVertical.toInt().toDouble() ).coerceIn(0.0, 1.0) init { @@ -93,7 +94,13 @@ class SliderLayout( rect { onUpdate { // progress rect = bg.rect - width *= renderProgress + + if (isVertical) { + height *= renderProgress + positionY = bg.positionY + bg.height - height + } else { + width *= renderProgress + } shade = ClickGui.backgroundShade setColor(Color.WHITE.setAlpha(0.25 * showAnim)) @@ -118,8 +125,9 @@ class SliderLayout( */ @UIBuilder fun Layout.slider( + isVertical: Boolean = false, block: SliderLayout.() -> Unit = {} - ) = SliderLayout(this).apply(children::add).apply(block) + ) = SliderLayout(this, isVertical).apply(children::add).apply(block) /** * Adds a [SliderLayout] behind given [layout] @@ -127,8 +135,9 @@ class SliderLayout( @UIBuilder fun Layout.sliderBehind( layout: Layout, + isVertical: Boolean = false, block: SliderLayout.() -> Unit = {} - ) = SliderLayout(this).insertLayout(this, layout, false).apply(block) + ) = SliderLayout(this, isVertical).insertLayout(this, layout, false).apply(block) /** * Adds a [SliderLayout] over given [layout] @@ -136,7 +145,8 @@ class SliderLayout( @UIBuilder fun Layout.sliderOver( layout: Layout, + isVertical: Boolean = false, block: SliderLayout.() -> Unit = {} - ) = SliderLayout(this).insertLayout(this, layout, true).apply(block) + ) = SliderLayout(this, isVertical).insertLayout(this, layout, true).apply(block) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt index ba786a44a..1a3a90a01 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt @@ -43,6 +43,15 @@ class KeybindPicker( } } + onShow { + isListening = false + } + + onTick { + val module = (owner.owner as? ModuleLayout) ?: return@onTick + if (module.isMinimized) isListening = false + } + onMouseAction(Mouse.Button.Left) { isListening = !isListening } @@ -53,15 +62,6 @@ class KeybindPicker( settingDelegate = key isListening = false } - - onShow { - isListening = false - } - - onTick { - val module = (owner.owner as? ModuleLayout) ?: return@onTick - if (module.isMinimized) isListening = false - } } companion object { diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt index 7b43339d7..46d62ef3c 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt @@ -34,7 +34,6 @@ abstract class SettingSlider >( abstract val settingValue: String private var changeAnimation by animation.exp(0.0, 1.0, 0.5) { true } - private val sliderHeight = 3.0 protected val slider = sliderBehind(titleBar) { diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/UnitButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt similarity index 93% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/UnitButton.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt index 290a5c7ea..43b3eb3bc 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/UnitButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.settings +package com.lambda.gui.impl.clickgui.module.settings import com.lambda.config.settings.FunctionSetting import com.lambda.gui.component.core.UIBuilder @@ -28,7 +28,7 @@ class UnitButton ( setting: FunctionSetting, ) : SettingLayout<() -> T, FunctionSetting>(owner, setting) { init { - onMouse(Mouse.Button.Left, Mouse.Action.Click) { + onMouseAction(Mouse.Button.Left) { setting.value() } } From 46b18c9c539a4116170b6aea54eca0e37336d929 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Mon, 17 Mar 2025 16:56:57 +0300 Subject: [PATCH 02/25] Popup windows --- .../renderer/gui/rect/FilledRectRenderer.kt | 2 +- .../main/kotlin/com/lambda/gui/GuiManager.kt | 16 +- .../com/lambda/gui/component/layout/Layout.kt | 38 +++-- .../com/lambda/gui/component/popup/Popup.kt | 159 ++++++++++++++++++ .../com/lambda/gui/component/window/Window.kt | 7 +- .../gui/impl/clickgui/core/AnimatedChild.kt | 2 +- .../gui/impl/clickgui/module/ModuleLayout.kt | 1 + .../module/{ => settings}/SettingLayout.kt | 6 +- .../clickgui/module/settings/SettingSlider.kt | 1 - .../settings/{ => impl}/BooleanButton.kt | 4 +- .../module/settings/impl/ColorPicker.kt | 53 ++++++ .../module/settings/{ => impl}/EnumSlider.kt | 3 +- .../settings/{ => impl}/KeybindPicker.kt | 4 +- .../settings/{ => impl}/NumberSlider.kt | 3 +- .../module/settings/{ => impl}/UnitButton.kt | 4 +- .../fragment/renderer/rect_filled.frag | 2 +- 16 files changed, 269 insertions(+), 36 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/gui/component/popup/Popup.kt rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{ => settings}/SettingLayout.kt (95%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/{ => impl}/BooleanButton.kt (96%) create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/{ => impl}/EnumSlider.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/{ => impl}/KeybindPicker.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/{ => impl}/NumberSlider.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/{ => impl}/UnitButton.kt (91%) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index 073108079..7583fff98 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -29,7 +29,7 @@ object FilledRectRenderer : AbstractGUIRenderer( ) { private const val MIN_SIZE = 0.5 private const val MIN_ALPHA = 1 - private const val EXPAND = 0.3 + private const val EXPAND = 0.35 fun filledRect( rect: Rect, diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index eee41590e..cbfd3a109 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -21,15 +21,17 @@ import com.lambda.config.settings.NumericSetting import com.lambda.config.settings.comparable.BooleanSetting import com.lambda.config.settings.comparable.EnumSetting import com.lambda.config.settings.FunctionSetting +import com.lambda.config.settings.complex.ColorSetting import com.lambda.config.settings.complex.KeyBindSetting import com.lambda.core.Loadable import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.BooleanButton.Companion.booleanSetting -import com.lambda.gui.impl.clickgui.module.settings.EnumSlider.Companion.enumSetting -import com.lambda.gui.impl.clickgui.module.settings.KeybindPicker.Companion.keybindSetting -import com.lambda.gui.impl.clickgui.module.settings.NumberSlider.Companion.numericSetting -import com.lambda.gui.impl.clickgui.module.settings.UnitButton.Companion.unitSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.BooleanButton.Companion.booleanSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.ColorPicker.Companion.colorPicker +import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider.Companion.enumSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.KeybindPicker.Companion.keybindSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numericSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.UnitButton.Companion.unitSetting import kotlin.reflect.KClass object GuiManager : Loadable { @@ -60,6 +62,10 @@ object GuiManager : Loadable { owner.keybindSetting(ref) } + typeAdapter { owner, ref -> + owner.colorPicker(ref) + } + return "Loaded ${typeMap.size} gui type adapters." } diff --git a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt index e7b946d1a..33ddb3fba 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt @@ -21,6 +21,7 @@ import com.lambda.graphics.RenderMain import com.lambda.graphics.animation.AnimationTicker import com.lambda.event.events.GuiEvent import com.lambda.graphics.pipeline.ScissorAdapter +import com.lambda.gui.ScreenLayout import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.* @@ -118,12 +119,22 @@ open class Layout( // Structure val children = mutableListOf() var selectedChild: Layout? = null + val root = run { + var own: Layout = owner ?: this + + while (true) { + own = own.owner ?: break + } + + own as? ScreenLayout ?: throw IllegalStateException("Root layout is not a ScreenLayout class") + } + protected open val renderSelf: Boolean get() = width > 1 && height > 1 protected open val scissorRect get() = rect // Inputs protected var mousePosition = Vec2d.ZERO; set(value) { - if (field == value) return + //if (field == value) return field = value selectedChild = if (isHovered) children.lastOrNull { @@ -157,8 +168,9 @@ open class Layout( * @param action The action to be performed. */ @LayoutBuilder - fun T.use(action: T.() -> Unit) { + fun T.use(action: T.() -> Unit): T { action(this) + return this@use } /** @@ -289,18 +301,6 @@ open class Layout( } } - init { - onUpdate { // Update the layout - screenSize = RenderMain.screenSize - - // Update relative position and bounds - ownerX = owner?.positionX ?: ownerX - ownerY = owner?.positionY ?: ownerY - ownerWidth = owner?.width ?: screenSize.x - ownerHeight = owner?.height ?: screenSize.y - } - } - fun onEvent(e: GuiEvent) { // Update self when (e) { @@ -314,6 +314,8 @@ open class Layout( hideActions.forEach { it(this) } } is GuiEvent.Tick -> { + // hack to update hover state once a tick if not moving the mouse + mousePosition = mousePosition tickActions.forEach { it(this) } } is GuiEvent.KeyPress -> { @@ -323,6 +325,14 @@ open class Layout( charTypedActions.forEach { it(this, e.char) } } is GuiEvent.Update -> { + screenSize = RenderMain.screenSize + + // Update relative position and bounds + ownerX = owner?.positionX ?: ownerX + ownerY = owner?.positionY ?: ownerY + ownerWidth = owner?.width ?: screenSize.x + ownerHeight = owner?.height ?: screenSize.y + updateActions.forEach { it(this) } } is GuiEvent.Render -> { diff --git a/common/src/main/kotlin/com/lambda/gui/component/popup/Popup.kt b/common/src/main/kotlin/com/lambda/gui/component/popup/Popup.kt new file mode 100644 index 000000000..0b6d202e5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/component/popup/Popup.kt @@ -0,0 +1,159 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.gui.component.popup + +import com.lambda.event.events.GuiEvent +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.layout.Layout +import com.lambda.gui.component.layout.Layout.Companion.animationTicker +import com.lambda.gui.component.layout.Layout.Companion.layout +import com.lambda.gui.component.window.Window +import com.lambda.gui.component.window.Window.Companion.window +import com.lambda.module.modules.client.ClickGui.backgroundTint +import com.lambda.util.Mouse +import com.lambda.util.math.multAlpha + +open class Popup( + requestor: Layout, + title: String +) { + private val baseHider = LayoutHideService { base } + protected val windowHider = LayoutHideService { window } + + private val animation = requestor.animationTicker() + private val tintAnimation by animation.exp(0.0, 1.0, 0.6) { windowHider.isShown } + private val root = requestor.root + + private val base: Layout = root.layout { + rect { + onUpdate { + width = this@layout.width + height = this@layout.height + setColor(backgroundTint.multAlpha(tintAnimation)) + } + } + + onUpdate { + properties.interactionPassthrough = !windowHider.isShown + } + }.apply { + destroy() + } + + /** + * The shown window on the popup screen + */ + val window = base.window( + title = title, + draggable = false, + scrollable = false, + minimizing = Window.Minimizing.Disabled, + resizable = false, + autoResize = Window.AutoResize.Disabled + ).apply { + destroy() + } + + fun show() { + windowHider.isShown = true + baseHider.isShown = true + window.onEvent(GuiEvent.Update) + } + + open fun hide() { + windowHider.isShown = false + } + + init { + requestor.apply { + root.onMouse(action = Mouse.Action.Click) { + if (!window.isHovered) hide() + } + + root.onTick { + if (tintAnimation == 0.0 && !windowHider.isShown) + baseHider.isShown = false + } + + root.onUpdate { + base.width = root.width + base.height = root.height + window.positionX = (base.width - window.width) * 0.5 + window.positionY = (base.height - window.height) * 0.5 - window.titleBar.height + + baseHider.update() + windowHider.update() + } + } + } + + abstract class Returnable ( + requestor: Layout, + title: String + ) : Popup(requestor, title) { + private var receiver: ((V) -> Unit)? = null + protected abstract fun buildOutput(): V + + override fun hide() { + if (windowHider.isShown) { + receiver?.invoke(buildOutput()) + receiver = null + } + super.hide() + } + + companion object { + /** + * Shows the given [popupLayout]. + * + * @param block The action to run on the popup exit. + */ + fun popup(popupLayout: Returnable, block: (V) -> Unit) = popupLayout.apply { + receiver = block + show() + } + } + } + + protected class LayoutHideService(val layoutBlock: () -> Layout) { + private var lastShown = false + var isShown = false + + fun update() { + val layout = layoutBlock() + val owner = layout.owner ?: throw IllegalStateException( + "Cannot hide root layout." + ) + + val prev = lastShown + val value = isShown + lastShown = value + + if (prev == value) return + + if (value) { + owner.children += layout + layout.onEvent(GuiEvent.Show) + } else { + owner.children -= layout + layout.onEvent(GuiEvent.Hide) + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt index 2a61fef90..3b55fc8e0 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt @@ -126,7 +126,12 @@ open class Window( get() = !isMinimized set(value) { isMinimized = !value } - var windowWidth = initialSize.x + var windowWidth = initialSize.x; set(value) { + if (field == value) return + field = value + + if (resizeX == null) widthAnimation = value + } var windowHeight = initialSize.y var widthAnimation by animation.exp(0.8, ::windowWidth) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt index 923065995..0674990cc 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt @@ -21,7 +21,7 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.gui.component.HAlign import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.window.Window -import com.lambda.gui.impl.clickgui.module.SettingLayout +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt index b07718345..f9bb3437a 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt @@ -29,6 +29,7 @@ import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.window.Window import com.lambda.gui.impl.clickgui.ModuleWindow import com.lambda.gui.impl.clickgui.core.AnimatedChild +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.Mouse import com.lambda.util.math.* import java.awt.Color diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/SettingLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt similarity index 95% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/SettingLayout.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt index 20183318b..c61c17ef6 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/SettingLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module +package com.lambda.gui.impl.clickgui.module.settings import com.lambda.config.AbstractSetting import com.lambda.module.modules.client.ClickGui @@ -30,7 +30,7 @@ import com.lambda.util.math.* abstract class SettingLayout > ( owner: Layout, val setting: T, - private val expandable: Boolean = false + expandable: Boolean = false ) : AnimatedChild( owner, setting.name, @@ -47,8 +47,6 @@ abstract class SettingLayout > ( override val isShown: Boolean get() = super.isShown && isVisible - - init { isMinimized = true diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt index 46d62ef3c..8b65d7615 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt @@ -24,7 +24,6 @@ import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.TextField.Companion.textField import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.core.SliderLayout.Companion.sliderBehind -import com.lambda.gui.impl.clickgui.module.SettingLayout import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.lerp diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt similarity index 96% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/BooleanButton.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt index 35fa632a0..8212df369 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/BooleanButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.comparable.BooleanSetting import com.lambda.graphics.animation.Animation.Companion.exp @@ -24,7 +24,7 @@ import com.lambda.gui.component.core.FilledRect.Companion.rect import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.SettingLayout +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.Mouse import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt new file mode 100644 index 000000000..e812db2f3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.gui.impl.clickgui.module.settings.impl + +import com.lambda.config.settings.complex.ColorSetting +import com.lambda.gui.component.core.UIBuilder +import com.lambda.gui.component.layout.Layout +import com.lambda.gui.component.popup.Popup +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.util.Mouse +import java.awt.Color + +class ColorPicker( + owner: Layout, + setting: ColorSetting +) : SettingLayout(owner, setting) { + private val popup = Popup(this, setting.name).apply { + window.use { + windowWidth = 200.0 + windowHeight = 100.0 + } + } + + init { + onMouseAction(Mouse.Button.Left) { + popup.show() + } + } + + companion object { + /** + * Creates a [ColorPicker] - visual representation of the [ColorSetting] + */ + @UIBuilder + fun Layout.colorPicker(setting: ColorSetting) = + ColorPicker(this, setting).apply(children::add) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/EnumSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/EnumSlider.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt index d1d2364ae..50aed2743 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/EnumSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt @@ -15,12 +15,13 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.comparable.EnumSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.util.NamedEnum +import com.lambda.gui.impl.clickgui.module.settings.SettingSlider import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.transform diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt index 1a3a90a01..9a2e5e69e 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/KeybindPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.complex.KeyBindSetting import com.lambda.gui.component.HAlign @@ -23,7 +23,7 @@ import com.lambda.gui.component.core.TextField.Companion.textField import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.ModuleLayout -import com.lambda.gui.impl.clickgui.module.SettingLayout +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.extension.displayValue diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/NumberSlider.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt index 6999a7950..ef5363733 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt @@ -15,11 +15,12 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.NumericSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout +import com.lambda.gui.impl.clickgui.module.settings.SettingSlider import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.math.MathUtils.typeConvert import com.lambda.util.math.lerp diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt similarity index 91% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt index 43b3eb3bc..ea26d2665 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/UnitButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.FunctionSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.SettingLayout +import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.Mouse class UnitButton ( diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag index 97c5b5d34..197b6392b 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag @@ -18,7 +18,7 @@ in vec4 v_Color; out vec4 color; -#define SMOOTHING 0.25 +#define SMOOTHING 0.2 #define NOISE_GRANULARITY 0.004 vec4 noise() { From d7acf052fbf99b8dda1015439c1ee2457e7bc120 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Tue, 18 Mar 2025 22:39:29 +0300 Subject: [PATCH 03/25] Lambda shader language --- .../buffer/vertex/attributes/VertexAttrib.kt | 3 +- .../graphics/renderer/esp/ESPRenderer.kt | 2 - .../graphics/renderer/gui/TextureRenderer.kt | 12 +- .../renderer/gui/font/FontRenderer.kt | 2 +- .../gui/font/sdf/DistanceFieldTexture.kt | 2 +- .../renderer/gui/rect/OutlineRectRenderer.kt | 2 - .../lambda/graphics/shader/ParsedShader.kt | 116 ++++++++++++++++ .../com/lambda/graphics/shader/Shader.kt | 25 ++-- .../lambda/graphics/shader/ShaderInterp.kt | 127 ++++++++++++++++++ .../com/lambda/graphics/shader/ShaderUtils.kt | 4 +- .../lambda/module/modules/render/Particles.kt | 2 +- .../shaders/fragment/post/cgui_animation.frag | 22 --- .../shaders/fragment/renderer/particle.frag | 11 -- .../shaders/fragment/renderer/pos_color.frag | 8 -- .../shaders/fragment/renderer/pos_tex.frag | 10 -- .../fragment/renderer/pos_tex_shady.frag | 23 ---- .../fragment/renderer/rect_filled.frag | 81 ----------- .../fragment/renderer/rect_outline.frag | 31 ----- .../assets/lambda/shaders/pos_color.glsl | 12 ++ .../assets/lambda/shaders/pos_tex.glsl | 16 +++ .../assets/lambda/shaders/pos_tex_color.glsl | 18 +++ .../assets/lambda/shaders/pos_tex_shady.glsl | 18 +++ .../sdf.glsl} | 26 ++-- .../lambda/shaders/renderer/box_dynamic.glsl | 20 +++ .../lambda/shaders/renderer/box_static.glsl | 18 +++ .../font/font.frag => renderer/font.glsl} | 30 +++-- .../lambda/shaders/renderer/particle.glsl | 20 +++ .../lambda/shaders/renderer/rect_filled.glsl | 53 ++++++++ .../lambda/shaders/renderer/rect_outline.glsl | 22 +++ .../shaders/renderer/tracer_dynamic.glsl | 27 ++++ .../shaders/renderer/tracer_static.glsl | 25 ++++ .../assets/lambda/shaders/shared/rect.glsl | 20 +++ .../assets/lambda/shaders/shared/shade.glsl | 22 +++ .../lambda/shaders/vertex/font/font.vert | 16 --- .../shaders/vertex/renderer/box_dynamic.vert | 19 --- .../shaders/vertex/renderer/box_static.vert | 16 --- .../shaders/vertex/renderer/particle.vert | 19 --- .../shaders/vertex/renderer/pos_tex.vert | 13 -- .../vertex/renderer/pos_tex_shady.vert | 15 --- .../shaders/vertex/renderer/rect_filled.vert | 19 --- .../shaders/vertex/renderer/rect_outline.vert | 22 --- .../vertex/renderer/tracer_dynamic.vert | 22 --- .../vertex/renderer/tracer_static.vert | 20 --- 43 files changed, 596 insertions(+), 415 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/post/cgui_animation.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_color.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex_shady.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_outline.frag create mode 100644 common/src/main/resources/assets/lambda/shaders/pos_color.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/pos_tex.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl rename common/src/main/resources/assets/lambda/shaders/{fragment/signed_distance_field.frag => post/sdf.glsl} (54%) create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/box_dynamic.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/box_static.glsl rename common/src/main/resources/assets/lambda/shaders/{fragment/font/font.frag => renderer/font.glsl} (52%) create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/particle.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/tracer_dynamic.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/tracer_static.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/shared/rect.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/shared/shade.glsl delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/font/font.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex_shady.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt index 3ece85693..d74ebaa2c 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt @@ -69,7 +69,8 @@ enum class VertexAttrib( Color ), - PARTICLE(Vec3, + PARTICLE( + Vec3, Vec2, // pos Color ); diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt index 3087e16c0..f5bb7a4cd 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt @@ -61,12 +61,10 @@ open class ESPRenderer(tickedMode: Boolean) { companion object { private val staticMode = shader( - "renderer/pos_color", "renderer/box_static" ) to VertexAttrib.Group.STATIC_RENDERER private val dynamicMode = shader( - "renderer/pos_color", "renderer/box_dynamic" ) to VertexAttrib.Group.DYNAMIC_RENDERER } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index b8a736f64..580239dfa 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -24,8 +24,10 @@ import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.graphics.texture.Texture import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d +import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW.glfwGetTime object TextureRenderer { @@ -45,10 +47,12 @@ object TextureRenderer { texture.bind() coloredShader.use() - coloredShader["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 - coloredShader["u_Color1"] = GuiSettings.shadeColor1 - coloredShader["u_Color2"] = GuiSettings.shadeColor2 - coloredShader["u_Size"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) + coloredShader["u_Shade"] = 1.0 + coloredShader["u_ShadeTime"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 + coloredShader["u_ShadeColor1"] = GuiSettings.shadeColor1 + coloredShader["u_ShadeColor2"] = GuiSettings.shadeColor2 + + coloredShader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) drawInternal(rect) } 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 9c0574bd0..64cadd647 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 @@ -38,7 +38,7 @@ import java.awt.Color * Renders text and emoji glyphs using a shader-based font rendering system. * This class handles text and emoji rendering, shadow effects, and text scaling. */ -object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/font")) { +object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("renderer/font")) { private val chars get() = RenderSettings.textFont private val emojis get() = RenderSettings.emojiFont diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt index 1f8eaf5e0..4a71b46ab 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt @@ -34,7 +34,7 @@ import java.awt.image.BufferedImage * @param image Image data to upload */ class DistanceFieldTexture(image: BufferedImage) : Texture(image, levels = 0) { - private val shader = shader("signed_distance_field", "renderer/pos_tex") + private val shader = shader("post/sdf") private val frame = CachedFrame(width, height).write { FrameBuffer.pipeline.use { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt index a43381723..415ff7d29 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt @@ -19,9 +19,7 @@ package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.IRenderContext import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.pipeline.ScissorAdapter import com.lambda.graphics.renderer.gui.AbstractGUIRenderer -import com.lambda.graphics.shader.Shader import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.util.math.lerp import com.lambda.util.math.MathUtils.toInt diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt new file mode 100644 index 000000000..d5640959d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.shader + +import com.lambda.util.stream +import org.apache.commons.io.IOUtils + +class ParsedShader(path: String) { + private val text = IOUtils.toString("shaders/$path.glsl".stream, Charsets.UTF_8) + + private val blocks = text.getBlocks() + private val dependencies = text.getDependencies() + + val methods = text.getMethods() + val definitions = text.getDefinitions() + + val attribs = blocks.getBlock("attributes") + val uniforms = blocks.getBlock("uniforms") + val exported = blocks.getBlock("export") + + init { + dependencies.forEach { dependencyName -> + val dependency = ParsedShader("shared/$dependencyName") + + //attribs.addAll(shader.blocks.getBlock("attributes")) + dependency.attribs.forEach { attrib -> + check(attribs.any { it.name == attrib.name && it.type == attrib.type }) { + "[${path}]: dependency \"$dependencyName\" requires \"${attrib.type} ${attrib.name}\" attribute to be present" + } + } + + uniforms.addAll(dependency.uniforms) + exported.addAll(dependency.exported) + + definitions.addAll(dependency.definitions) + methods.addAll(dependency.methods.filter { + it.name != "fragment" && it.name != "vertex" + }) + } + } + + data class Field(val type: String, val name: String, val flag: String?) + data class Method(val name: String, val returnType: String, val parameters: String, var code: String) { + fun construct(constructedName: String = name): String { + return "$returnType $constructedName($parameters) {\n $code\n}\n" + } + } + + companion object { + private val blockRegex = Regex("""\s*(\w+)\s*\{(.*?)}\s*""", RegexOption.DOT_MATCHES_ALL) + private val methodRegex = Regex("""^\s*(\w+)\s+(\w+)\s*\((.*?)\)\s*\{(.*?)}#""", setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.MULTILINE)) + + private val fieldRegex = Regex("""\s*(\w+(?:\s+\w+)*)\s+(\w+)\s*;\s*(#.*)?""") + private val includeRegex = Regex("""^#include\s+"([\w-]+)"""", RegexOption.MULTILINE) + private val defineRegex = Regex("""^#define\s+(\w+)\s+(.+)""", RegexOption.MULTILINE) + + private fun String.getBlocks() = mutableMapOf>().apply { + blockRegex.findAll(this@getBlocks).forEach { match -> + val blockName = match.groupValues[1] + val blockFieldsStr = match.groupValues[2] + + val fields = fieldRegex.findAll(blockFieldsStr).map { fieldMatch -> + val type = fieldMatch.groupValues[1] + val name = fieldMatch.groupValues[2] + + val flag = fieldMatch.groupValues[3] + .takeIf { it.isNotEmpty() } + ?.removePrefix("#") + + Field(type, name, flag) + }.toMutableSet() + + getOrPut(blockName, ::mutableSetOf).addAll(fields) + } + } + + private fun String.getMethods() = mutableSetOf().apply { + methodRegex.findAll(this@getMethods).forEach { match -> + val returnType = match.groupValues[1] + val methodName = match.groupValues[2] + val parameters = match.groupValues[3] + val code = match.groupValues[4].trim() + + add(Method(methodName, returnType, parameters, code)) + } + } + + private fun String.getDependencies() = includeRegex.findAll(this) + .map { it.groupValues[1] } + .toSet() + + private fun String.getDefinitions() = mutableSetOf().apply { + defineRegex.findAll(this@getDefinitions).forEach { match -> + add(Field(match.groupValues[2].trim(), match.groupValues[1], null)) + } + } + + private fun MutableMap>.getBlock(name: String) = + getOrDefault(name, mutableSetOf()) + } +} 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 c5b718a4d..8b42dfd93 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -29,13 +29,19 @@ import org.joml.Matrix4f import org.lwjgl.opengl.GL20C.* import java.awt.Color -class Shader private constructor(fragmentPath: String, vertexPath: String) { +class Shader private constructor(name: String) { private val uniformCache: Object2IntMap = Object2IntOpenHashMap() - private val id = createShaderProgram( - loadShader(ShaderType.VERTEX_SHADER, "shaders/vertex/$vertexPath.vert"), - loadShader(ShaderType.FRAGMENT_SHADER, "shaders/fragment/$fragmentPath.frag") - ) + private val id: Int + + init { + val texts = buildShaderSource(name) + + id = createShaderProgram( + loadShader(ShaderType.VERTEX_SHADER, texts.first), + loadShader(ShaderType.FRAGMENT_SHADER, texts.second) + ) + } fun use() { glUseProgram(id) @@ -79,14 +85,11 @@ class Shader private constructor(fragmentPath: String, vertexPath: String) { uniformMatrix(loc(name), mat) companion object { - private val shaderCache = hashMapOf, Shader>() + private val shaderCache = hashMapOf() fun shader(path: String) = - shader(path, path) - - fun shader(fragmentPath: String, vertexPath: String) = - shaderCache.getOrPut(fragmentPath to vertexPath) { - Shader(fragmentPath, vertexPath) + shaderCache.getOrPut(path) { + Shader(path) } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt new file mode 100644 index 000000000..e49905d2b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.shader + + +fun buildShaderSource(path: String): Pair { + val shader = ParsedShader(path) + + val vertexShader = StringBuilder().appendLine(HEADER) + val fragmentShader = StringBuilder().appendLine(HEADER) + val shaders = setOf(vertexShader, fragmentShader) + + /* Build attribs for vertex shader */ + shader.attribs.apply { + if (isNotEmpty()) vertexShader.appendLine() + + forEachIndexed { i, attribute -> + vertexShader.appendLine( + "layout (location = $i) in ${attribute.type} ${attribute.name};" + ) + } + } + + /* Build uniforms */ + shader.uniforms.apply { + val vertex = mutableSetOf() + val fragment = mutableSetOf() + + vertex.add("uniform mat4 u_ProjModel;") + forEach { uniform -> + val line = "uniform ${uniform.type} ${uniform.name};" + + when (uniform.flag?.trim()) { + "vertex" -> vertex += line + "fragment" -> fragment += line + else -> { + vertex += line; fragment += line + } + } + } + + vertex.apply { + vertexShader.appendLine() + forEach { vertexShader.appendLine(it) } + } + + fragment.apply { + if (isNotEmpty()) fragmentShader.appendLine() + forEach { fragmentShader.appendLine(it) } + } + } + + /* Does "v_TexCoord = uv" in the vertex shader for you */ + val autoAssignment = StringBuilder() + + /* Build in/out vars */ + shader.exported.apply { + if (isNotEmpty()) shaders.forEach(StringBuilder::appendLine) + + // Add gl_Position = u_ProjModel * pos; by default + val hasPosAttribute = shader.attribs.any { it.name == "pos" && it.type == "vec4" } + val glPosImplemented = any { it.name == "gl_Position" } + if (hasPosAttribute && !glPosImplemented) { + autoAssignment.appendLine(" gl_Position = u_ProjModel * pos;") + } + + forEach { export -> + if (export.type != "core") { + vertexShader.appendLine("out ${export.type} ${export.name};") + fragmentShader.appendLine("in ${export.type} ${export.name};") + } + + export.flag?.let { expr -> /* Add assignment if present */ + autoAssignment.appendLine(" ${export.name} = ${expr.trim()};") + } + } + + fragmentShader.appendLine() + fragmentShader.appendLine("out vec4 color;") + } + + /* Build definitions */ + shader.definitions.apply { + if (isNotEmpty()) fragmentShader.appendLine() + + forEach { definition -> + fragmentShader.appendLine("#define ${definition.name} ${definition.type}") + } + } + + /* Build methods */ + shaders.forEach(StringBuilder::appendLine) + val methods = shader.methods + + val vertexMain = methods.takeMain("vertex").apply { + code += autoAssignment.trim() + }.construct("main") + vertexShader.appendLine(vertexMain) + + val fragmentMain = methods.takeMain("fragment").construct("main") + methods.forEach { fragmentShader.appendLine(it.construct()) } + fragmentShader.appendLine(fragmentMain) + + return vertexShader.toString().trimEnd() to fragmentShader.toString().trimEnd() +} + +private fun MutableSet.takeMain(name: String) = firstOrNull { + it.name == name && it.returnType == "void" && it.parameters.isEmpty() +}.apply(this::remove) ?: ParsedShader.Method(name, "void", "", "") + + +private const val HEADER = "#version 330 core" diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt index 6cfda3c32..9053e5702 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt @@ -30,12 +30,11 @@ object ShaderUtils { private val matrixBuffer = BufferUtils.createFloatBuffer(4 * 4) private const val shaderInfoLogLength = 512 - fun loadShader(type: ShaderType, resource: LambdaResource): Int { + fun loadShader(type: ShaderType, text: String): Int { // Create new shader object val shader = glCreateShader(type.gl) // Attach source code and compile it - val text = IOUtils.toString(resource.stream, Charsets.UTF_8) GlStateManager.glShaderSource(shader, ImmutableList.of(text)) val error = compileShader(shader) @@ -43,7 +42,6 @@ object ShaderUtils { error?.let { err -> val builder = StringBuilder() .append("Failed to compile ${type.name} shader").appendLine() - .append("Path: $resource").appendLine() .append("Compiler output:").appendLine() .append(err) diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt index d77819dbd..809da8019 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -81,7 +81,7 @@ object Particles : Module( private var particles = mutableListOf() private val pipeline = VertexPipeline(VertexMode.TRIANGLES, VertexAttrib.Group.PARTICLE) - private val shader = shader("renderer/particle", "renderer/particle") + private val shader = shader("renderer/particle") init { listen { diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/post/cgui_animation.frag b/common/src/main/resources/assets/lambda/shaders/fragment/post/cgui_animation.frag deleted file mode 100644 index 7ca5b52e3..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/post/cgui_animation.frag +++ /dev/null @@ -1,22 +0,0 @@ -#version 330 core - -uniform sampler2D u_Texture; -uniform float u_Progress; - -in vec2 v_TexCoord; -out vec4 color; - -void main() { - vec2 coord = v_TexCoord; - - coord -= 0.5; - coord *= 2.0; - - coord *= mix(0.9, 1.0, u_Progress); - - coord *= 0.5; - coord += 0.5; - - vec4 tex = texture(u_Texture, coord); - color = vec4(tex.rgb * mix(1.1, 1.0, u_Progress), tex.a * u_Progress); -} diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag deleted file mode 100644 index 53b683d85..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/particle.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 core - -in vec2 v_TexCoord; -in vec4 v_Color; - -out vec4 color; - -void main() { - float a = 1.0 - length(v_TexCoord - 0.5) * 2.0; - color = v_Color * vec4(1.0, 1.0, 1.0, a); -} \ No newline at end of file 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 deleted file mode 100644 index 71f50775d..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_color.frag +++ /dev/null @@ -1,8 +0,0 @@ -#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/pos_tex.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex.frag deleted file mode 100644 index d89d3550a..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 330 core - -uniform sampler2D u_Texture; - -in vec2 v_TexCoord; -out vec4 color; - -void main() { - color = texture(u_Texture, v_TexCoord); -} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex_shady.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex_shady.frag deleted file mode 100644 index f6ec30c85..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/pos_tex_shady.frag +++ /dev/null @@ -1,23 +0,0 @@ -#version 330 core - -uniform sampler2D u_Texture; -uniform float u_Time; -uniform vec4 u_Color1; -uniform vec4 u_Color2; -uniform vec2 u_Size; - -in vec2 v_Position; -in vec2 v_TexCoord; - -out vec4 color; - -vec4 shade() { - 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); -} - -void main() { - color = texture(u_Texture, v_TexCoord) * shade(); -} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag deleted file mode 100644 index 197b6392b..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_filled.frag +++ /dev/null @@ -1,81 +0,0 @@ -#version 330 core - -uniform vec2 u_Size; -uniform float u_RoundLeftTop; -uniform float u_RoundLeftBottom; -uniform float u_RoundRightBottom; -uniform float u_RoundRightTop; - -uniform float u_Shade; -uniform float u_ShadeTime; -uniform vec4 u_ShadeColor1; -uniform vec4 u_ShadeColor2; -uniform vec2 u_ShadeSize; - -in vec2 v_Position; -in vec2 v_TexCoord; -in vec4 v_Color; - -out vec4 color; - -#define SMOOTHING 0.2 -#define NOISE_GRANULARITY 0.004 - -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 != 1.0) return v_Color; - - vec2 pos = v_Position * u_ShadeSize; - float p = sin(pos.x - pos.y - u_ShadeTime) * 0.5 + 0.5; - - return mix(u_ShadeColor1, u_ShadeColor2, p) * v_Color; -} - -float getRoundRadius() { - // ToDo: use step - bool xcmp = v_TexCoord.x > 0.5; - bool ycmp = v_TexCoord.y > 0.5; - - float r = 0.0; - - if (xcmp) { - if (ycmp) { - r = u_RoundRightBottom; - } else { - r = u_RoundRightTop; - } - } else { - if (ycmp) { - r = u_RoundLeftBottom; - } else { - r = u_RoundLeftTop; - } - } - - return r; -} - -vec4 round() { - vec2 halfSize = u_Size * 0.5; - - float radius = max(getRoundRadius(), SMOOTHING); - - vec2 smoothVec = vec2(SMOOTHING); - vec2 coord = mix(-smoothVec, u_Size + smoothVec, v_TexCoord); - - vec2 center = halfSize - coord; - float distance = length(max(abs(center) - halfSize + radius, 0.0)) - radius; - - float alpha = 1.0 - smoothstep(-SMOOTHING, SMOOTHING, distance); - return vec4(1.0, 1.0, 1.0, clamp(alpha, 0.0, 1.0)); -} - -void main() { - 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 deleted file mode 100644 index 71e8680fe..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect_outline.frag +++ /dev/null @@ -1,31 +0,0 @@ -#version 330 core - -uniform float u_Shade; -uniform float u_ShadeTime; -uniform vec4 u_ShadeColor1; -uniform vec4 u_ShadeColor2; -uniform vec2 u_ShadeSize; - -in vec2 v_Position; -in float v_Alpha; -in vec4 v_Color; - -out vec4 color; - -vec4 shade() { - if (u_Shade != 1.0) return v_Color; - - vec2 pos = v_Position * u_ShadeSize; - float p = sin(pos.x - pos.y - u_ShadeTime) * 0.5 + 0.5; - - return mix(u_ShadeColor1, u_ShadeColor2, p) * v_Color; -} - -vec4 glow() { - float newAlpha = min(1.0, v_Alpha * 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/pos_color.glsl b/common/src/main/resources/assets/lambda/shaders/pos_color.glsl new file mode 100644 index 000000000..c8536f01b --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/pos_color.glsl @@ -0,0 +1,12 @@ +attributes { + vec4 pos; + vec4 color; +}; + +export { + vec4 v_Color; # color +}; + +void fragment() { + color = v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl new file mode 100644 index 000000000..cb9f05b37 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl @@ -0,0 +1,16 @@ +attributes { + vec4 pos; + vec2 uv; +}; + +uniforms { + sampler2D u_Texture; # fragment +}; + +export { + vec4 v_TexCoord; # uv +}; + +void fragment() { + color = texture(u_Texture, v_TexCoord); +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl new file mode 100644 index 000000000..e9049bd05 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl @@ -0,0 +1,18 @@ +attributes { + vec4 pos; + vec2 uv; + vec4 color; +}; + +uniforms { + sampler2D u_Texture; # fragment +}; + +export { + vec4 v_TexCoord; # uv + vec4 v_Color; # color +}; + +void fragment() { + color = texture(u_Texture, v_TexCoord) * v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl new file mode 100644 index 000000000..652bcf2fb --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl @@ -0,0 +1,18 @@ +attributes { + vec4 pos; + vec2 uv; +}; + +uniforms { + sampler2D u_Texture; # fragment +}; + +export { + vec4 v_TexCoord; # uv +}; + +#include "shade" + +void fragment() { + color = texture(u_Texture, v_TexCoord) * shade; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/signed_distance_field.frag b/common/src/main/resources/assets/lambda/shaders/post/sdf.glsl similarity index 54% rename from common/src/main/resources/assets/lambda/shaders/fragment/signed_distance_field.frag rename to common/src/main/resources/assets/lambda/shaders/post/sdf.glsl index 5d4f485f1..fa3566da6 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/signed_distance_field.frag +++ b/common/src/main/resources/assets/lambda/shaders/post/sdf.glsl @@ -1,19 +1,25 @@ -#version 330 core +attributes { + vec4 pos; + vec2 uv; +}; -uniform sampler2D u_Texture; -uniform vec2 u_TexelSize; +uniforms { + sampler2D u_Texture; # fragment + vec2 u_TexelSize; # fragment +}; -in vec2 v_TexCoord; -out vec4 color; +export { + vec2 v_TexCoord; # uv +}; -#define SPHREAD 4 +#define SPREAD 4 -void main() { +void fragment() { vec4 colors = vec4(0.0); vec4 blurWeight = vec4(0.0); - for (int x = -SPHREAD; x <= SPHREAD; ++x) { - for (int y = -SPHREAD; y <= SPHREAD; ++y) { + for (int x = -SPREAD; x <= SPREAD; ++x) { + for (int y = -SPREAD; y <= SPREAD; ++y) { vec2 offset = vec2(x, y) * u_TexelSize; vec4 color = texture(u_Texture, v_TexCoord + offset); @@ -25,4 +31,4 @@ void main() { } color = colors / blurWeight; -} +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/box_dynamic.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/box_dynamic.glsl new file mode 100644 index 000000000..2bbfcdd0f --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/box_dynamic.glsl @@ -0,0 +1,20 @@ +attributes { + vec3 pos1; + vec3 pos2; + vec4 color; +}; + +uniforms { + mat4 u_ProjModel; # vertex + float u_TickDelta; # vertex + vec3 u_CameraPosition; # vertex +}; + +export { + core gl_Position; # u_ProjModel * vec4(mix(pos1, pos2, u_TickDelta) - u_CameraPosition, 1.0) + vec4 v_Color; # color +}; + +void fragment() { + color = v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/box_static.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/box_static.glsl new file mode 100644 index 000000000..3a97249d5 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/box_static.glsl @@ -0,0 +1,18 @@ +attributes { + vec3 pos; + vec4 color; +}; + +uniforms { + mat4 u_ProjModel; # vertex + vec3 u_CameraPosition; # vertex +}; + +export { + core gl_Position; # u_ProjModel * vec4(pos - u_CameraPosition, 1.0) + vec4 v_Color; # color +}; + +void fragment() { + color = v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/font/font.frag b/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl similarity index 52% rename from common/src/main/resources/assets/lambda/shaders/fragment/font/font.frag rename to common/src/main/resources/assets/lambda/shaders/renderer/font.glsl index 37aa5a897..e269c0043 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/font/font.frag +++ b/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl @@ -1,20 +1,26 @@ -#version 330 core +attributes { + vec4 pos; + vec2 uv; + vec4 color; +}; -uniform sampler2D u_FontTexture; -uniform sampler2D u_EmojiTexture; -uniform float u_SDFMin; -uniform float u_SDFMax; +uniforms { + sampler2D u_FontTexture; # fragment + sampler2D u_EmojiTexture; # fragment + float u_SDFMin; # fragment + float u_SDFMax; # fragment +}; -in vec2 v_TexCoord; -in vec4 v_Color; - -out vec4 color; +export { + vec2 v_TexCoord; # uv + vec4 v_Color; # color +}; float sdf(float channel) { return 1.0 - smoothstep(u_SDFMin, u_SDFMax, 1.0 - channel); -} +}# -void main() { +void fragment() { bool isEmoji = v_TexCoord.x < 0.0; if (isEmoji) { @@ -24,4 +30,4 @@ void main() { } color = vec4(1.0, 1.0, 1.0, sdf(texture(u_FontTexture, v_TexCoord).r)) * v_Color; -} +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/particle.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/particle.glsl new file mode 100644 index 000000000..9444708e9 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/particle.glsl @@ -0,0 +1,20 @@ +attributes { + vec3 pos; + vec2 uv; + vec4 color; +}; + +uniforms { + vec3 u_CameraPosition; # vertex +}; + +export { + core gl_Position; # u_ProjModel * vec4(pos - u_CameraPosition, 1.0) + vec2 v_TexCoord; # uv + vec4 v_Color; # color +}; + +void fragment() { + float a = 1.0 - length(v_TexCoord - 0.5) * 2.0; + color = v_Color * vec4(1.0, 1.0, 1.0, a); +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl new file mode 100644 index 000000000..cbf30ccbf --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl @@ -0,0 +1,53 @@ +attributes { + vec4 pos; + vec2 uv; + vec4 color; +}; + +uniforms { + vec2 u_Size; # fragment + float u_RoundLeftTop; # fragment + float u_RoundLeftBottom; # fragment + float u_RoundRightBottom; # fragment + float u_RoundRightTop; # fragment +}; + +#include "rect" +#define SMOOTHING 0.2 +#define round getRoundAlpha() + +float getRoundRadius() { + bool xcmp = v_TexCoord.x > 0.5; + bool ycmp = v_TexCoord.y > 0.5; + + float r = 0.0; + + if (xcmp) { + if (ycmp) { r = u_RoundRightBottom; } + else { r = u_RoundRightTop; } + } else { + if (ycmp) { r = u_RoundLeftBottom; } + else { r = u_RoundLeftTop; } + } + + return r; +}# + +vec4 getRoundAlpha() { + vec2 halfSize = u_Size * 0.5; + + float radius = max(getRoundRadius(), SMOOTHING); + + vec2 smoothVec = vec2(SMOOTHING); + vec2 coord = mix(-smoothVec, u_Size + smoothVec, v_TexCoord); + + vec2 center = halfSize - coord; + float distance = length(max(abs(center) - halfSize + radius, 0.0)) - radius; + + float alpha = 1.0 - smoothstep(-SMOOTHING, SMOOTHING, distance); + return vec4(1.0, 1.0, 1.0, clamp(alpha, 0.0, 1.0)); +}# + +void fragment() { + color = v_Color * shade * round + noise; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl new file mode 100644 index 000000000..a8b62b9c8 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl @@ -0,0 +1,22 @@ +attributes { + vec4 pos; + vec2 uv; + float alpha; + vec4 color; +}; + +export { + float v_Alpha; # alpha +}; + +#include "rect" +#define glow glowAlpha() + +vec4 glowAlpha() { + float newAlpha = min(1.0, v_Alpha * v_Alpha * v_Alpha); + return vec4(1.0, 1.0, 1.0, newAlpha); +}# + +void fragment() { + color = v_Color * shade * glow; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/tracer_dynamic.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/tracer_dynamic.glsl new file mode 100644 index 000000000..a178426fd --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/tracer_dynamic.glsl @@ -0,0 +1,27 @@ +attributes { + vec3 pos1; + vec3 pos2; + vec4 color; +}; + +uniforms { + mat4 u_ProjModel; # vertex + float u_TickDelta; # vertex + vec3 u_CameraPosition; # vertex +}; + +export { + core gl_Position; # vec4(0.0, 0.0, 0.0, 1.0) + vec4 v_Color; # color +}; + +void vertex() { + if (gl_VertexID % 2 == 0) { + vec3 VERTEX_POSITION = mix(pos1, pos2, u_TickDelta) - u_CameraPosition; + gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); + } +}# + +void fragment() { + color = v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/tracer_static.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/tracer_static.glsl new file mode 100644 index 000000000..8197af918 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/tracer_static.glsl @@ -0,0 +1,25 @@ +attributes { + vec3 pos; + vec4 color; +}; + +uniforms { + mat4 u_ProjModel; # vertex + vec3 u_CameraPosition; # vertex +}; + +export { + core gl_Position; # vec4(0.0, 0.0, 0.0, 1.0) + vec4 v_Color; # color +}; + +void vertex() { + if (gl_VertexID % 2 == 0) { + vec3 VERTEX_POSITION = pos - u_CameraPosition; + gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); + } +}# + +void fragment() { + color = v_Color; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl b/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl new file mode 100644 index 000000000..576b66c18 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl @@ -0,0 +1,20 @@ +attributes { + vec2 uv; +}; + +export { + vec2 v_TexCoord; # uv + vec4 v_Color; # color +}; + +#include "shade" + +#define NOISE_GRANULARITY 0.004 +#define noise getNoise() + +vec4 getNoise() { + // 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); +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/shared/shade.glsl b/common/src/main/resources/assets/lambda/shaders/shared/shade.glsl new file mode 100644 index 000000000..9371edc22 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/shared/shade.glsl @@ -0,0 +1,22 @@ +uniforms { + float u_Shade; # fragment + float u_ShadeTime; # fragment + vec4 u_ShadeColor1; # fragment + vec4 u_ShadeColor2; # fragment + vec2 u_ShadeSize; # fragment +}; + +export { + vec2 v_Position; # gl_Position.xy * 0.5 + 0.5 +}; + +#define shade getShadeColor() + +vec4 getShadeColor() { + if (u_Shade != 1.0) return vec4(1.0); + + vec2 pos = v_Position * u_ShadeSize; + float p = sin(pos.x - pos.y - u_ShadeTime) * 0.5 + 0.5; + + return mix(u_ShadeColor1, u_ShadeColor2, p); +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/font/font.vert b/common/src/main/resources/assets/lambda/shaders/vertex/font/font.vert deleted file mode 100644 index 5f16359df..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/font/font.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec4 color; - -uniform mat4 u_ProjModel; - -out vec2 v_TexCoord; -out vec4 v_Color; - -void main() { - gl_Position = u_ProjModel * pos; - v_TexCoord = uv; - v_Color = color; -} \ 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 deleted file mode 100644 index d33b24566..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_dynamic.vert +++ /dev/null @@ -1,19 +0,0 @@ -#version 330 core - -layout (location = 0) in vec3 pos1; -layout (location = 1) in vec3 pos2; -layout (location = 2) in vec4 color; - -uniform mat4 u_ProjModel; - -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_ProjModel * vec4(VERTEX_POSITION, 1.0); - v_Color = color; -} diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert deleted file mode 100644 index 197c14ca5..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/box_static.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 330 core - -layout (location = 0) in vec3 pos; -layout (location = 1) in vec4 color; - -uniform mat4 u_ProjModel; -uniform vec3 u_CameraPosition; - -out vec4 v_Color; - -#define VERTEX_POSITION pos - u_CameraPosition - -void main() { - gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); - v_Color = color; -} diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert deleted file mode 100644 index 6e3fed3d9..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/particle.vert +++ /dev/null @@ -1,19 +0,0 @@ -#version 330 core - -layout (location = 0) in vec3 pos; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec4 color; - -uniform mat4 u_ProjModel; -uniform vec3 u_CameraPosition; - -out vec2 v_TexCoord; -out vec4 v_Color; - -#define VERTEX_POSITION pos - u_CameraPosition - -void main() { - gl_Position = u_ProjModel * vec4(VERTEX_POSITION, 1.0); - v_TexCoord = uv; - v_Color = color; -} diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex.vert deleted file mode 100644 index 51ab11af5..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex.vert +++ /dev/null @@ -1,13 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec2 uv; - -uniform mat4 u_ProjModel; - -out vec2 v_TexCoord; - -void main() { - gl_Position = u_ProjModel * pos; - v_TexCoord = uv; -} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex_shady.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex_shady.vert deleted file mode 100644 index 082b85b24..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/pos_tex_shady.vert +++ /dev/null @@ -1,15 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec2 uv; - -uniform mat4 u_ProjModel; - -out vec2 v_Position; -out vec2 v_TexCoord; - -void main() { - gl_Position = u_ProjModel * pos; - v_Position = gl_Position.xy * 0.5 + 0.5; - v_TexCoord = uv; -} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert deleted file mode 100644 index 225d9dfd7..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_filled.vert +++ /dev/null @@ -1,19 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec4 color; - -uniform mat4 u_ProjModel; - -out vec2 v_Position; -out vec2 v_TexCoord; -out vec4 v_Color; - -void main() { - gl_Position = u_ProjModel * pos; - - v_Position = gl_Position.xy * 0.5 + 0.5; - v_TexCoord = uv; - v_Color = color; -} \ 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 deleted file mode 100644 index 42e8d4ac0..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect_outline.vert +++ /dev/null @@ -1,22 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec2 uv; -layout (location = 2) in float alpha; -layout (location = 3) in vec4 color; - -uniform mat4 u_ProjModel; - -out vec2 v_Position; -out vec2 v_TexCoord; -out float v_Alpha; -out vec4 v_Color; - -void main() { - gl_Position = u_ProjModel * pos; - - v_Position = gl_Position.xy * 0.5 + 0.5; - v_TexCoord = uv; - v_Alpha = alpha; - v_Color = color; -} \ 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 deleted file mode 100644 index 57c3accbd..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_dynamic.vert +++ /dev/null @@ -1,22 +0,0 @@ -#version 330 core - -layout (location = 0) in vec3 pos1; -layout (location = 1) in vec3 pos2; -layout (location = 2) in vec4 color; - -uniform mat4 u_ProjModel; - -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_ProjModel * 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 deleted file mode 100644 index 68b534c64..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/tracer_static.vert +++ /dev/null @@ -1,20 +0,0 @@ -#version 330 core - -layout (location = 0) in vec3 pos; -layout (location = 1) in vec4 color; - -uniform mat4 u_ProjModel; - -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_ProjModel * vec4(VERTEX_POSITION, 1.0) - -void main() { - gl_Position = gl_VertexID % 2 == 0 ? SCREEN_CENTER : PROJECTED; - v_Color = color; -} From fa73b08d065965356602999811f8e0f1f27207c1 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Wed, 19 Mar 2025 02:49:06 +0300 Subject: [PATCH 04/25] cleanup --- .../kotlin/com/lambda/graphics/shader/ParsedShader.kt | 2 +- .../kotlin/com/lambda/graphics/shader/ShaderInterp.kt | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt index d5640959d..5bf5e53d2 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ParsedShader.kt @@ -79,8 +79,8 @@ class ParsedShader(path: String) { val name = fieldMatch.groupValues[2] val flag = fieldMatch.groupValues[3] + .removePrefix("#").trim() .takeIf { it.isNotEmpty() } - ?.removePrefix("#") Field(type, name, flag) }.toMutableSet() diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt index e49905d2b..8c5d8cd61 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderInterp.kt @@ -45,12 +45,11 @@ fun buildShaderSource(path: String): Pair { forEach { uniform -> val line = "uniform ${uniform.type} ${uniform.name};" - when (uniform.flag?.trim()) { + when (uniform.flag) { "vertex" -> vertex += line "fragment" -> fragment += line - else -> { - vertex += line; fragment += line - } + "global" -> { vertex += line; fragment += line } + else -> throw IllegalStateException("Destination of \"$line\" is not specified") } } @@ -86,7 +85,7 @@ fun buildShaderSource(path: String): Pair { } export.flag?.let { expr -> /* Add assignment if present */ - autoAssignment.appendLine(" ${export.name} = ${expr.trim()};") + autoAssignment.appendLine(" ${export.name} = ${expr};") } } @@ -123,5 +122,4 @@ private fun MutableSet.takeMain(name: String) = firstOrNull it.name == name && it.returnType == "void" && it.parameters.isEmpty() }.apply(this::remove) ?: ParsedShader.Method(name, "void", "", "") - private const val HEADER = "#version 330 core" From eb6427286d027537eb6cc510d7b3a1d3e9540f03 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 22 Mar 2025 03:08:24 +0300 Subject: [PATCH 05/25] Setting independent ui components --- .../config/settings/comparable/EnumSetting.kt | 13 ++-- .../main/kotlin/com/lambda/gui/GuiManager.kt | 69 ++++++++++++++++--- .../gui/impl/clickgui/core/AnimatedChild.kt | 2 +- .../gui/impl/clickgui/core/SliderLayout.kt | 2 +- .../gui/impl/clickgui/module/ModuleLayout.kt | 10 ++- .../clickgui/module/settings/SettingLayout.kt | 32 ++++++--- .../clickgui/module/settings/SettingSlider.kt | 10 +-- .../module/settings/impl/BooleanButton.kt | 15 ++-- .../module/settings/impl/ColorPicker.kt | 15 ++-- .../module/settings/impl/EnumSlider.kt | 18 +++-- .../module/settings/impl/KeybindPicker.kt | 12 ++-- .../module/settings/impl/NumberSlider.kt | 25 ++++--- .../module/settings/impl/UnitButton.kt | 26 +++++-- 13 files changed, 175 insertions(+), 74 deletions(-) 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 6fbeb537e..de926c6c4 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 @@ -43,24 +43,27 @@ class EnumSetting>( description, visibility, ) { - val enumValues: Array = defaultValue.declaringJavaClass.enumConstants - fun next() { - value = enumValues[((value.ordinal + 1) % enumValues.size)] + value = value.enumValues[((value.ordinal + 1) % value.enumValues.size)] } override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { required(word(name)) { parameter -> suggests { _, builder -> - enumValues.forEach { builder.suggest(it.name.capitalize()) } + value.enumValues.forEach { builder.suggest(it.name.capitalize()) } builder.buildFuture() } executeWithResult { - val newValue = enumValues.find { it.name.equals(parameter().value(), true) } + val newValue = value.enumValues.find { it.name.equals(parameter().value(), true) } ?: return@executeWithResult failure("Invalid value") trySetValue(newValue) return@executeWithResult success() } } } + + companion object { + val > T.enumValues: Array get() = + declaringJavaClass.enumConstants + } } diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index cbfd3a109..6c015efdc 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -17,22 +17,27 @@ package com.lambda.gui -import com.lambda.config.settings.NumericSetting import com.lambda.config.settings.comparable.BooleanSetting import com.lambda.config.settings.comparable.EnumSetting import com.lambda.config.settings.FunctionSetting import com.lambda.config.settings.complex.ColorSetting import com.lambda.config.settings.complex.KeyBindSetting +import com.lambda.config.settings.numeric.DoubleSetting +import com.lambda.config.settings.numeric.FloatSetting +import com.lambda.config.settings.numeric.IntegerSetting +import com.lambda.config.settings.numeric.LongSetting import com.lambda.core.Loadable import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.settings.impl.BooleanButton.Companion.booleanSetting import com.lambda.gui.impl.clickgui.module.settings.impl.ColorPicker.Companion.colorPicker +import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider.Companion.enumSetting import com.lambda.gui.impl.clickgui.module.settings.impl.KeybindPicker.Companion.keybindSetting import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numericSetting -import com.lambda.gui.impl.clickgui.module.settings.impl.UnitButton.Companion.unitSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.UnitButton.Companion.unitButton import kotlin.reflect.KClass +import kotlin.reflect.KMutableProperty0 object GuiManager : Loadable { val typeMap = mutableMapOf, (owner: Layout, converted: Any) -> Layout>() @@ -43,27 +48,73 @@ object GuiManager : Loadable { override fun load(): String { typeAdapter { owner, ref -> - owner.booleanSetting(ref) + owner.booleanSetting(ref.name, ref::value).apply { + visibility { ref.visibility() } + } } typeAdapter> { owner, ref -> - owner.enumSetting(ref) + owner.enumSetting(ref.name, ref::value).apply { + visibility { ref.visibility() } + } } typeAdapter> { owner, ref -> - owner.unitSetting(ref) + owner.unitButton(ref.name, ref::value).apply { + visibility { ref.visibility() } + } } - typeAdapter> { owner, ref -> - owner.numericSetting(ref) + typeAdapter { owner, ref -> + owner.numericSetting( + ref.name, ref.unit, + ref.range.start, ref.range.endInclusive, ref.step, + ref::value + ).apply { + visibility { ref.visibility() } + } + } + + typeAdapter { owner, ref -> + owner.numericSetting( + ref.name, ref.unit, + ref.range.start, ref.range.endInclusive, ref.step, + ref::value + ).apply { + visibility { ref.visibility() } + } + } + + typeAdapter { owner, ref -> + owner.numericSetting( + ref.name, ref.unit, + ref.range.start, ref.range.endInclusive, ref.step, + ref::value + ).apply { + visibility { ref.visibility() } + } + } + + typeAdapter { owner, ref -> + owner.numericSetting( + ref.name, ref.unit, + ref.range.start, ref.range.endInclusive, ref.step, + ref::value + ).apply { + visibility { ref.visibility() } + } } typeAdapter { owner, ref -> - owner.keybindSetting(ref) + owner.keybindSetting(ref.name, ref::value).apply { + visibility { ref.visibility() } + } } typeAdapter { owner, ref -> - owner.colorPicker(ref) + owner.colorPicker(ref.name, ref::value).apply { + visibility { ref.visibility() } + } } return "Loaded ${typeMap.size} gui type adapters." diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt index 0674990cc..d69229c75 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt @@ -71,7 +71,7 @@ abstract class AnimatedChild( speed = transform(index.toDouble(), 0.0, lastIndex.toDouble(), start, end) } - if ((this as? SettingLayout<*, *>)?.isVisible == true) speed *= 0.8 + if ((this as? SettingLayout<*>)?.isVisible == true) speed *= 0.8 speed + isShownInternal.toInt() * 0.1 }) { isShownInternal }.apply { diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt index ef8305517..614ea6b1b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt @@ -39,7 +39,7 @@ class SliderLayout( private val isVertical: Boolean ) : AnimatedChild(owner, "") { // Not a great solution - private val setting = owner as? SettingSlider<*, *> + private val setting = owner as? SettingSlider<*> private val showAnim get() = setting?.showAnimation ?: showAnimation private val pressedBut get() = setting?.pressedButton ?: pressedButton diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt index f9bb3437a..57ca6a95d 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt @@ -152,9 +152,13 @@ class ModuleLayout( } } - val settings = module.settings.map { setting -> + val settings = module.settings.mapNotNull { setting -> content.layoutOf(setting) - }.filterIsInstance>() + }.map { it as SettingLayout<*> }.onEach { + it.onUpdate { + width = this@ModuleLayout.content.width + } + } val minimizeSettings = { settings.forEach { @@ -182,7 +186,7 @@ class ModuleLayout( */ @UIBuilder fun Window.backgroundTint(tintTitleBar: Boolean = false) { - check(this is SettingLayout<*, *> || this is ModuleLayout || this is ModuleWindow) + check(this is SettingLayout<*> || this is ModuleLayout || this is ModuleWindow) val base = this@backgroundTint diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt index c61c17ef6..4b86bd741 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt @@ -17,24 +17,26 @@ package com.lambda.gui.impl.clickgui.module.settings -import com.lambda.config.AbstractSetting +import com.lambda.gui.component.core.LayoutBuilder import com.lambda.module.modules.client.ClickGui import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.ModuleLayout.Companion.backgroundTint import com.lambda.gui.impl.clickgui.core.AnimatedChild import com.lambda.util.math.* +import kotlin.reflect.KMutableProperty0 /** * A base class for setting layouts. */ -abstract class SettingLayout > ( +abstract class SettingLayout ( owner: Layout, - val setting: T, + name: String, + field: KMutableProperty0, expandable: Boolean = false ) : AnimatedChild( owner, - setting.name, - Vec2d.ZERO, Vec2d.ZERO, + name, + Vec2d.ZERO, Vec2d(110.0, 0.0), false, false, if (expandable) Minimizing.Absolute else Minimizing.Disabled, false, @@ -42,18 +44,26 @@ abstract class SettingLayout > ( ) { protected val cursorController = cursorController() - var settingDelegate by setting - val isVisible get() = setting.visibility() + protected var settingDelegate by field + private var onValueSet = mutableListOf<(V) -> Unit>() + private var getVisibilityBlock = { true } + val isVisible get() = getVisibilityBlock() + + @LayoutBuilder + fun visibility(action: () -> Boolean) { + getVisibilityBlock = { action() } + } + + @LayoutBuilder + fun valueSet(action: (V) -> Unit) { + onValueSet += action + } override val isShown: Boolean get() = super.isShown && isVisible init { isMinimized = true - onUpdate { - width = owner.width - } - titleBar.onUpdate { height = ClickGui.settingsHeight } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt index 8b65d7615..54ad0b7ad 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt @@ -17,7 +17,6 @@ package com.lambda.gui.impl.clickgui.module.settings -import com.lambda.config.AbstractSetting import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign @@ -26,10 +25,13 @@ import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.core.SliderLayout.Companion.sliderBehind import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.lerp +import kotlin.reflect.KMutableProperty0 -abstract class SettingSlider >( - owner: Layout, setting: T -) : SettingLayout(owner, setting, false) { +abstract class SettingSlider ( + owner: Layout, + name: String, + field: KMutableProperty0 +) : SettingLayout(owner, name, field) { abstract val settingValue: String private var changeAnimation by animation.exp(0.0, 1.0, 0.5) { true } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt index 8212df369..c506d3948 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt @@ -17,7 +17,6 @@ package com.lambda.gui.impl.clickgui.module.settings.impl -import com.lambda.config.settings.comparable.BooleanSetting import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.module.modules.client.ClickGui import com.lambda.gui.component.core.FilledRect.Companion.rect @@ -31,11 +30,13 @@ import com.lambda.util.math.Vec2d import com.lambda.util.math.lerp import com.lambda.util.math.setAlpha import java.awt.Color +import kotlin.reflect.KMutableProperty0 class BooleanButton( owner: Layout, - setting: BooleanSetting -) : SettingLayout(owner, setting) { + name: String, + field: KMutableProperty0 +) : SettingLayout(owner, name, field) { private val activeAnimation by animation.exp(0.0, 1.0, 0.6, ::settingDelegate) init { @@ -91,16 +92,16 @@ class BooleanButton( } onMouseAction(Mouse.Button.Left) { - setting.value = !setting.value + settingDelegate = !settingDelegate } } companion object { /** - * Creates a [BooleanButton] - visual representation of the [BooleanSetting] + * Creates a [BooleanButton] */ @UIBuilder - fun Layout.booleanSetting(setting: BooleanSetting) = - BooleanButton(this, setting).apply(children::add) + fun Layout.booleanSetting(name: String, field: KMutableProperty0) = + BooleanButton(this, name, field).apply(children::add) } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt index e812db2f3..93eac678b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt @@ -17,19 +17,20 @@ package com.lambda.gui.impl.clickgui.module.settings.impl -import com.lambda.config.settings.complex.ColorSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.popup.Popup import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.Mouse import java.awt.Color +import kotlin.reflect.KMutableProperty0 class ColorPicker( owner: Layout, - setting: ColorSetting -) : SettingLayout(owner, setting) { - private val popup = Popup(this, setting.name).apply { + name: String, + field: KMutableProperty0, +) : SettingLayout(owner, name, field) { + private val popup = Popup(this, name).apply { window.use { windowWidth = 200.0 windowHeight = 100.0 @@ -44,10 +45,10 @@ class ColorPicker( companion object { /** - * Creates a [ColorPicker] - visual representation of the [ColorSetting] + * Creates a [ColorPicker] */ @UIBuilder - fun Layout.colorPicker(setting: ColorSetting) = - ColorPicker(this, setting).apply(children::add) + fun Layout.colorPicker(name: String, field: KMutableProperty0) = + ColorPicker(this, name, field).apply(children::add) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt index 50aed2743..9953e6de4 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt @@ -18,17 +18,20 @@ package com.lambda.gui.impl.clickgui.module.settings.impl import com.lambda.config.settings.comparable.EnumSetting +import com.lambda.config.settings.comparable.EnumSetting.Companion.enumValues import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.util.NamedEnum import com.lambda.gui.impl.clickgui.module.settings.SettingSlider import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.transform +import kotlin.reflect.KMutableProperty0 class EnumSlider >( owner: Layout, - setting: EnumSetting -) : SettingSlider>(owner, setting) { + name: String, + field: KMutableProperty0 +) : SettingSlider(owner, name, field) { override val settingValue: String get() = (settingDelegate as? NamedEnum)?.displayName ?: settingDelegate.name @@ -36,13 +39,13 @@ class EnumSlider >( slider.progress { transform( value = settingDelegate.ordinal.toDouble(), - ogStart = 0.0, ogEnd = setting.enumValues.lastIndex.toDouble(), + ogStart = 0.0, ogEnd = settingDelegate.enumValues.lastIndex.toDouble(), nStart = 0.0, nEnd = 1.0 ) } slider.onSlide { - settingDelegate = setting.enumValues.let { entries -> + settingDelegate = settingDelegate.enumValues.let { entries -> entries[(it * entries.size) .floorToInt() .coerceIn(0, entries.size - 1)] @@ -55,7 +58,10 @@ class EnumSlider >( * Creates an [EnumSlider] - visual representation of the [EnumSetting] */ @UIBuilder - fun > Layout.enumSetting(setting: EnumSetting) = - EnumSlider(this, setting).apply(children::add) + @Suppress("UNCHECKED_CAST") + fun > Layout.enumSetting( + name: String, + field: KMutableProperty0> + ) = EnumSlider(this, name, field as KMutableProperty0).apply(children::add) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt index 9a2e5e69e..7e9c1ae59 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt @@ -27,18 +27,20 @@ import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.extension.displayValue +import kotlin.reflect.KMutableProperty0 class KeybindPicker( owner: Layout, - setting: KeyBindSetting -) : SettingLayout(owner, setting) { + name: String, + field: KMutableProperty0 +) : SettingLayout(owner, name, field) { private var isListening = false init { textField { onUpdate { mergeFrom(titleBar.textField) - text = if (isListening) "..." else setting.value.displayValue + text = if (isListening) "..." else settingDelegate.displayValue textHAlignment = HAlign.RIGHT } } @@ -69,7 +71,7 @@ class KeybindPicker( * Creates a [KeybindPicker] - visual representation of the [KeyBindSetting] */ @UIBuilder - fun Layout.keybindSetting(setting: KeyBindSetting) = - KeybindPicker(this, setting).apply(children::add) + fun Layout.keybindSetting(name: String, field: KMutableProperty0) = + KeybindPicker(this, name, field).apply(children::add) } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt index ef5363733..6377c822e 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt @@ -25,15 +25,20 @@ import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.math.MathUtils.typeConvert import com.lambda.util.math.lerp import com.lambda.util.math.transform +import kotlin.reflect.KMutableProperty0 class NumberSlider ( - owner: Layout, setting: NumericSetting -) : SettingSlider>(owner, setting) where V : Number, V : Comparable { - private val min = setting.range.start.toDouble() - private val max = setting.range.endInclusive.toDouble() + owner: Layout, + name: String, private val unit: String, + minV: V, maxV: V, stepV: V, + field: KMutableProperty0 +) : SettingSlider(owner, name, field) where V : Number, V : Comparable { + private val min = minV.toDouble() + private val max = maxV.toDouble() + private val step = stepV.toDouble() override val settingValue: String - get() = "${setting.value}${setting.unit}" + get() = "${settingDelegate}${unit}" init { slider.progress { @@ -46,7 +51,7 @@ class NumberSlider ( slider.onSlide { settingDelegate = settingDelegate.typeConvert( - lerp(it, min, max).roundToStep(setting.step).toDouble() + lerp(it, min, max).roundToStep(step).toDouble() ) } } @@ -56,7 +61,11 @@ class NumberSlider ( * Creates an [NumberSlider] - visual representation of the [NumericSetting] */ @UIBuilder - fun Layout.numericSetting(setting: NumericSetting) where T : Number, T : Comparable = - NumberSlider(this, setting).apply(children::add) + fun Layout.numericSetting( + name: String, unit: String, + minV: T, maxV: T, stepV: T, + field: KMutableProperty0 + ) where T : Number, T : Comparable = + NumberSlider(this, name, unit, minV, maxV, stepV, field).apply(children::add) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt index ea26d2665..d52e69799 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt @@ -17,28 +17,40 @@ package com.lambda.gui.impl.clickgui.module.settings.impl -import com.lambda.config.settings.FunctionSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.util.Mouse +import kotlin.reflect.KMutableProperty0 class UnitButton ( owner: Layout, - setting: FunctionSetting, -) : SettingLayout<() -> T, FunctionSetting>(owner, setting) { + name: String, + field: KMutableProperty0<() -> T>, +) : SettingLayout<() -> T>(owner, name, field) { init { onMouseAction(Mouse.Button.Left) { - setting.value() + settingDelegate() } } companion object { /** - * Creates a [UnitButton] - visual representation of the [FunctionSetting] + * Creates a [UnitButton] */ @UIBuilder - fun Layout.unitSetting(setting: FunctionSetting) = - UnitButton(this, setting).apply(children::add) + fun Layout.unitButton(name: String, field: KMutableProperty0<() -> T>) = + UnitButton(this, name, field).apply(children::add) + + /** + * Creates a [UnitButton] + */ + @UIBuilder + fun Layout.unitButton(name: String, block: () -> T) = + UnitButton(this, name, CapturedUnit(block)::block).apply(children::add) + + private class CapturedUnit ( + var block: () -> T + ) } } From c0114f869a1bb37eb3ef65e4174981d96e078016 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 22 Mar 2025 03:17:18 +0300 Subject: [PATCH 06/25] Fixed instant ui scaling --- .../kotlin/com/lambda/module/modules/client/GuiSettings.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 52055ab17..58d30acc1 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,13 @@ package com.lambda.module.modules.client +import com.lambda.Lambda.mc import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.animation.AnimationTicker +import com.lambda.gui.LambdaScreen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import java.awt.Color @@ -59,7 +61,7 @@ object GuiSettings : Module( private var targetScale = 2.0 get() { - val update = System.currentTimeMillis() - lastChange > 200 || !ClickGui.SCREEN.isOpen + val update = System.currentTimeMillis() - lastChange > 200 || mc.currentScreen !is LambdaScreen if (update) field = scaleSetting / 100.0 * 2.0 return field } From 7f91a90598f0e6f7e9e2f0b4191c8c6eeac7cf0d Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:27:12 -0400 Subject: [PATCH 07/25] Moved the lambda outside --- common/src/main/kotlin/com/lambda/module/Module.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 48a42e148..fb8beff11 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -115,7 +115,7 @@ abstract class Module( enabledByDefault: Boolean = false, defaultKeybind: KeyCode = KeyCode.UNBOUND, ) : Nameable, Muteable, Configurable(ModuleConfig) { - private val isEnabledSetting = setting("Enabled", enabledByDefault, visibility = { false }) + private val isEnabledSetting = setting("Enabled", enabledByDefault) { false } private val keybindSetting = setting("Keybind", defaultKeybind) val isVisible = setting("Visible", true) { ModuleList.isEnabled } val reset by setting("Reset", { settings.forEach { it.reset() }; this@Module.info("Settings set to default") }) From 98af6f797207b70b742be4d259ffd55f5e9ee2e9 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:52:30 -0400 Subject: [PATCH 08/25] Added vec3d settings --- .../kotlin/com/lambda/config/Configurable.kt | 35 ++++++++++++ .../config/settings/complex/Vec3dSetting.kt | 54 +++++++++++++++++++ .../main/kotlin/com/lambda/util/Formatting.kt | 3 ++ .../kotlin/com/lambda/util/math/Vectors.kt | 7 +++ 4 files changed, 99 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index 3b6375842..b17e6f5b7 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -37,6 +37,7 @@ import com.lambda.util.KeyCode import com.lambda.util.Nameable import net.minecraft.block.Block import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d import java.awt.Color /** @@ -387,6 +388,40 @@ abstract class Configurable( visibility: () -> Boolean = { true }, ) = ColorSetting(name, defaultValue, description, visibility).register() + /** + * Creates a [Vec3dSetting] with the provided parameters and adds it to the [settings]. + * + * @param name The unique identifier for the setting. + * @param defaultValue The default [Vec3d] value of the setting. + * @param description A brief explanation of the setting's purpose and behavior. + * @param visibility A lambda expression that determines the visibility status of the setting. + * + * @return The created [Vec3dSetting]. + */ + fun setting( + name: String, + defaultValue: Vec3d, + description: String = "", + visibility: () -> Boolean = { true }, + ) = Vec3dSetting(name, defaultValue, description, visibility).register() + + /** + * Creates a [BlockPosSetting] with the provided parameters and adds it to the [settings]. + * + * @param name The unique identifier for the setting. + * @param defaultValue The default [BlockPos.Mutable] value of the setting. + * @param description A brief explanation of the setting's purpose and behavior. + * @param visibility A lambda expression that determines the visibility status of the setting. + * + * @return The created [BlockPosSetting]. + */ + fun setting( + name: String, + defaultValue: BlockPos.Mutable, + description: String = "", + visibility: () -> Boolean = { true }, + ) = BlockPosSetting(name, defaultValue, description, visibility).register() + /** * Creates a [BlockPosSetting] with the provided parameters and adds it to the [settings]. * diff --git a/common/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt new file mode 100644 index 000000000..05901c3a1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/settings/complex/Vec3dSetting.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config.settings.complex + +import com.google.gson.reflect.TypeToken +import com.lambda.brigadier.argument.double +import com.lambda.brigadier.argument.integer +import com.lambda.brigadier.argument.value +import com.lambda.brigadier.execute +import com.lambda.brigadier.required +import com.lambda.config.AbstractSetting +import com.lambda.util.extension.CommandBuilder +import net.minecraft.command.CommandRegistryAccess +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d + +class Vec3dSetting( + override val name: String, + defaultValue: Vec3d, + description: String, + visibility: () -> Boolean, +) : AbstractSetting( + defaultValue, + TypeToken.get(Vec3d::class.java).type, + description, + visibility +) { + override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) { + required(double("X", -30000000.0, 30000000.0)) { x -> + required(double("Y", -64.0, 255.0)) { y -> + required(double("Z", -30000000.0, 30000000.0)) { z -> + execute { + trySetValue(Vec3d(x().value(), y().value(), z().value())) + } + } + } + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/Formatting.kt b/common/src/main/kotlin/com/lambda/util/Formatting.kt index d5703e886..71940cf03 100644 --- a/common/src/main/kotlin/com/lambda/util/Formatting.kt +++ b/common/src/main/kotlin/com/lambda/util/Formatting.kt @@ -17,6 +17,7 @@ package com.lambda.util +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import java.time.LocalDateTime import java.time.ZoneId @@ -39,6 +40,8 @@ object Formatting { return "(${format.format(Locale.US, x)}, ${format.format(Locale.US, y)}, ${format.format(Locale.US, z)})" } + fun BlockPos.asString() = "($x, $y, $z)" + fun getTime(formatter: DateTimeFormatter = DateTimeFormatter.RFC_1123_DATE_TIME): String { val localDateTime = LocalDateTime.now() val zoneId = ZoneId.systemDefault() diff --git a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt index 8784cf45b..9d72823b6 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Vectors.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Vectors.kt @@ -88,6 +88,13 @@ infix operator fun Vec3d.div(other: Double): Vec3d = times(1 / other) infix operator fun Vec3d.div(other: Float): Vec3d = times(1 / other) infix operator fun Vec3d.div(other: Int): Vec3d = times(1 / other) +infix operator fun ClosedRange.rangeTo(other: Double) = Vec3d(start, endInclusive, other) +infix operator fun ClosedRange.rangeTo(other: Float) = Vec3d(start.toDouble(), endInclusive.toDouble(), other.toDouble()) +infix operator fun ClosedRange.rangeTo(other: Int) = Vec3d(start.toDouble(), endInclusive.toDouble(), other.toDouble()) +infix operator fun OpenEndRange.rangeTo(other: Double) = Vec3d(start, endExclusive, other) +infix operator fun OpenEndRange.rangeTo(other: Float) = Vec3d(start.toDouble(), endExclusive.toDouble(), other.toDouble()) +infix operator fun OpenEndRange.rangeTo(other: Int) = BlockPos.Mutable(start, endExclusive, other) + /* Vec3i */ val Vec3i.vec3d get() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) From 27c731bac2a3c9d3ecf480f888c714e9203085ed Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 22 Mar 2025 16:14:43 +0300 Subject: [PATCH 09/25] Color pickle --- .../graphics/renderer/gui/TextureRenderer.kt | 10 +- .../com/lambda/graphics/shader/ShaderUtils.kt | 2 + .../main/kotlin/com/lambda/gui/GuiManager.kt | 12 +- .../gui/impl/clickgui/core/AnimatedChild.kt | 21 +++ .../gui/impl/clickgui/module/ModuleLayout.kt | 15 -- .../clickgui/module/settings/SettingLayout.kt | 13 +- .../module/settings/impl/ColorPicker.kt | 161 +++++++++++++++++- .../module/settings/impl/NumberSlider.kt | 13 +- .../assets/lambda/shaders/pos_tex.glsl | 2 +- .../assets/lambda/shaders/pos_tex_color.glsl | 2 +- .../assets/lambda/shaders/pos_tex_shady.glsl | 2 +- .../assets/lambda/shaders/renderer/font.glsl | 9 +- .../lambda/shaders/renderer/hue_circle.glsl | 21 +++ .../assets/lambda/shaders/shared/hsb.glsl | 30 ++++ .../assets/lambda/shaders/shared/sdf.glsl | 3 + 15 files changed, 263 insertions(+), 53 deletions(-) create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/hue_circle.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/shared/hsb.glsl create mode 100644 common/src/main/resources/assets/lambda/shaders/shared/sdf.glsl diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index 580239dfa..7e2a902d5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -33,8 +33,8 @@ import org.lwjgl.glfw.GLFW.glfwGetTime object TextureRenderer { private val pipeline = VertexPipeline(VertexMode.TRIANGLES, VertexAttrib.Group.POS_UV) - private val mainShader = shader("renderer/pos_tex") - private val coloredShader = shader("renderer/pos_tex_shady") + private val mainShader = shader("pos_tex") + private val coloredShader = shader("pos_tex_shady") fun drawTexture(texture: Texture, rect: Rect) { texture.bind() @@ -57,7 +57,7 @@ object TextureRenderer { drawInternal(rect) } - private fun drawInternal(rect: Rect) { + fun drawInternal(rect: Rect) { val pos1 = rect.leftTop val pos2 = rect.rightBottom @@ -72,8 +72,6 @@ object TextureRenderer { ) } - pipeline.upload() - pipeline.render() - pipeline.clear() + pipeline.immediateDraw() } } diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt index 9053e5702..f1f349b87 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt @@ -44,6 +44,8 @@ object ShaderUtils { .append("Failed to compile ${type.name} shader").appendLine() .append("Compiler output:").appendLine() .append(err) + .appendLine().appendLine("CODE:") + .append(text) throw RuntimeException(builder.toString()) } diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index 6c015efdc..7ddf7f896 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -31,13 +31,11 @@ import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.settings.impl.BooleanButton.Companion.booleanSetting import com.lambda.gui.impl.clickgui.module.settings.impl.ColorPicker.Companion.colorPicker -import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider.Companion.enumSetting import com.lambda.gui.impl.clickgui.module.settings.impl.KeybindPicker.Companion.keybindSetting -import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numericSetting +import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numberSlider import com.lambda.gui.impl.clickgui.module.settings.impl.UnitButton.Companion.unitButton import kotlin.reflect.KClass -import kotlin.reflect.KMutableProperty0 object GuiManager : Loadable { val typeMap = mutableMapOf, (owner: Layout, converted: Any) -> Layout>() @@ -66,7 +64,7 @@ object GuiManager : Loadable { } typeAdapter { owner, ref -> - owner.numericSetting( + owner.numberSlider( ref.name, ref.unit, ref.range.start, ref.range.endInclusive, ref.step, ref::value @@ -76,7 +74,7 @@ object GuiManager : Loadable { } typeAdapter { owner, ref -> - owner.numericSetting( + owner.numberSlider( ref.name, ref.unit, ref.range.start, ref.range.endInclusive, ref.step, ref::value @@ -86,7 +84,7 @@ object GuiManager : Loadable { } typeAdapter { owner, ref -> - owner.numericSetting( + owner.numberSlider( ref.name, ref.unit, ref.range.start, ref.range.endInclusive, ref.step, ref::value @@ -96,7 +94,7 @@ object GuiManager : Loadable { } typeAdapter { owner, ref -> - owner.numericSetting( + owner.numberSlider( ref.name, ref.unit, ref.range.start, ref.range.endInclusive, ref.step, ref::value diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt index d69229c75..7c6327513 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt @@ -21,6 +21,7 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.gui.component.HAlign import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.window.Window +import com.lambda.gui.impl.clickgui.module.ModuleLayout import com.lambda.gui.impl.clickgui.module.settings.SettingLayout import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.MathUtils.toInt @@ -107,6 +108,26 @@ abstract class AnimatedChild( if (isHovered) lastHover = System.currentTimeMillis() } + onWindowExpand { + if (ClickGui.multipleSettingWindows) return@onWindowExpand + + val close = if (this !is ModuleLayout) { + owner.children.filterIsInstance() + } else { + val base = owner // window content + .owner // window + ?.owner // environment with windows + + base?.children?.filterIsInstance()?.flatMap { window -> + window.content.children.filterIsInstance() + } ?: mutableListOf() + } + + close.forEach { + if (it != this) it.isMinimized = true + } + } + titleBar.textField.use { textHAlignment = HAlign.LEFT diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt index 57ca6a95d..6d9b2d9dd 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt @@ -137,21 +137,6 @@ class ModuleLayout( cursorController.setCursor(cursor) } - onWindowExpand { - if (ClickGui.multipleSettingWindows) return@onWindowExpand - - val base = owner // window content - .owner // window - ?.owner // environment with windows - ?: return@onWindowExpand - - base.children.filterIsInstance().forEach { window -> - window.content.children.filterIsInstance().forEach { module -> - if (module != this) module.isMinimized = true - } - } - } - val settings = module.settings.mapNotNull { setting -> content.layoutOf(setting) }.map { it as SettingLayout<*> }.onEach { diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt index 4b86bd741..9630a7f63 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt @@ -31,7 +31,7 @@ import kotlin.reflect.KMutableProperty0 abstract class SettingLayout ( owner: Layout, name: String, - field: KMutableProperty0, + private val property: KMutableProperty0, expandable: Boolean = false ) : AnimatedChild( owner, @@ -44,7 +44,14 @@ abstract class SettingLayout ( ) { protected val cursorController = cursorController() - protected var settingDelegate by field + protected var settingDelegate: V + get() = property.get() + set(value) { + if (property.get() == value) return + property.set(value) + onValueSet.forEach { it(value) } + } + private var onValueSet = mutableListOf<(V) -> Unit>() private var getVisibilityBlock = { true } val isVisible get() = getVisibilityBlock() @@ -55,7 +62,7 @@ abstract class SettingLayout ( } @LayoutBuilder - fun valueSet(action: (V) -> Unit) { + fun onValueSet(action: (V) -> Unit) { onValueSet += action } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt index 93eac678b..49b7d1ffa 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt @@ -17,33 +17,176 @@ package com.lambda.gui.impl.clickgui.module.settings.impl +import com.lambda.graphics.renderer.gui.TextureRenderer +import com.lambda.graphics.shader.Shader.Companion.shader +import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.component.popup.Popup import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numberSlider +import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse +import com.lambda.util.StringUtils.capitalize +import com.lambda.util.math.MathUtils.toDegree +import com.lambda.util.math.MathUtils.toRadian +import com.lambda.util.math.Vec2d +import com.lambda.util.math.a +import com.lambda.util.math.lerp +import com.lambda.util.math.multAlpha +import com.lambda.util.math.setAlpha +import com.lambda.util.math.transform +import net.minecraft.util.math.Vec3d import java.awt.Color +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.hypot +import kotlin.math.sin +import kotlin.math.sqrt import kotlin.reflect.KMutableProperty0 class ColorPicker( owner: Layout, name: String, field: KMutableProperty0, -) : SettingLayout(owner, name, field) { - private val popup = Popup(this, name).apply { - window.use { - windowWidth = 200.0 - windowHeight = 100.0 - } +) : SettingLayout(owner, name, field, true) { + + private var hsb get() = Color.RGBtoHSB(settingDelegate.red, settingDelegate.green, settingDelegate.blue, null).let { + Vec3d(it[0].toDouble(), it[1].toDouble(), it[2].toDouble()) + }; set(value) { + settingDelegate = Color(Color.HSBtoRGB(value.x.toFloat(), value.y.toFloat(), value.z.toFloat())).setAlpha(alpha) + } + + private var hue get() = hsb.x; set(value) { + val cache = hsb + if (cache.x == value) return + hsb = Vec3d(value, cache.y, cache.z) + } + + private var saturation get() = hsb.y; set(value) { + val cache = hsb + if (cache.y == value) return + hsb = Vec3d(cache.x, value, cache.z) + } + + private var brightness get() = hsb.z; set(value) { + val cache = hsb + if (cache.z == value) return + hsb = Vec3d(cache.x, cache.y, value) + } + + private var alpha get() = settingDelegate.a; set(value) { + settingDelegate = settingDelegate.setAlpha(value) } init { - onMouseAction(Mouse.Button.Left) { - popup.show() + rect { + val shrink = 3.0 + + onUpdate { + rect = titleBar.rect + .moveFirst(Vec2d.RIGHT * (titleBar.width - titleBar.height)) + .shrink(shrink + (1.0 - showAnimation)) + + Vec2d.RIGHT * lerp(showAnimation, 5.0, -ClickGui.fontOffset + shrink) + + setColor(settingDelegate.multAlpha(showAnimation)) + setRadius(100.0) + } + } + + content.layout { + val getU = { transform(mousePosition.x, positionX, positionX + width, 0.0, 1.0) } + val getV = { transform(mousePosition.y, positionY, positionY + height, 0.0, 1.0) } + + onUpdate { + width = content.width * 0.5 + positionX = content.positionX + (content.width - width) * 0.5 + height = width + } + + onRender { + hueCircleShader.use() + TextureRenderer.drawInternal(rect) + } + + onMouseMove { + if (pressedButton != Mouse.Button.Left) return@onMouseMove + + val (u, v) = getU() to getV() + hue = uvToHue(u, v).div(360.0).coerceIn(0.0, 1.0) + saturation = hypot(u - 0.5, v - 0.5).coerceIn(0.0, 0.5) * 2 + } + + val circle = this + val knobSize = 2.0 + + outline { + width = knobSize; height = knobSize + + setColor(Color.BLACK) + glowRadius = 1.0 + roundRadius = 100.0 + + onUpdate { + val uv = if (circle.pressedButton == Mouse.Button.Left) clampToCircle(Vec2d(getU(), getV())) + else hueToUv(hue * 360, saturation) + + positionX = transform(uv.x, 0.0, 1.0, circle.positionX, circle.positionX + circle.width) - knobSize * 0.5 + positionY = transform(uv.y, 0.0, 1.0, circle.positionY, circle.positionY + circle.height) - knobSize * 0.5 + } + } + } + + listOf(::brightness, ::alpha).map { + content.numberSlider(it.name.capitalize(), "", 0.0, 1.0, 0.01, it).apply { + forceRoundDisplayValue = true + } + }.onEach { + it.onUpdate { + width = this@ColorPicker.content.width + } } + + content.listify() } companion object { + private val hueCircleShader = shader("renderer/hue_circle") + + private fun uvToHue(u: Double, v: Double): Double { + val x = (u - 0.5) * 2.0 + val y = ((1.0 - v) - 0.5) * -2.0 + + var hue = atan2(y, x).toDegree() + 90.0 + if (hue < 0) hue += 360.0 + return hue + } + + private fun hueToUv(hue: Double, radius: Double): Vec2d { + val angle = (hue - 90.0).toRadian() + + val x = cos(angle) * radius + val y = sin(angle) * radius + + val u = (x * 0.5f) + 0.5f + val v = (y * -0.5f) + 0.5f + + return Vec2d(u, 1.0 - v) + } + + private fun clampToCircle(uv: Vec2d): Vec2d { + val center = Vec2d(0.5f, 0.5f) + val radius = 0.5f + val dx = uv.x- center.x + val dy = uv.y - center.y + val dist = sqrt(dx * dx + dy * dy) + + return if (dist > radius) { + val scale = radius / dist + Vec2d(center.x + dx * scale, center.y + dy * scale) + } else uv + } + /** * Creates a [ColorPicker] */ diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt index 6377c822e..7f3f3d7de 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt @@ -33,12 +33,15 @@ class NumberSlider ( minV: V, maxV: V, stepV: V, field: KMutableProperty0 ) : SettingSlider(owner, name, field) where V : Number, V : Comparable { - private val min = minV.toDouble() - private val max = maxV.toDouble() - private val step = stepV.toDouble() + var min = minV.toDouble() + var max = maxV.toDouble() + var step = stepV.toDouble() + var forceRoundDisplayValue = false override val settingValue: String - get() = "${settingDelegate}${unit}" + get() = "${settingDelegate.let { + if (forceRoundDisplayValue) it.roundToStep(step) else it + }}${unit}" init { slider.progress { @@ -61,7 +64,7 @@ class NumberSlider ( * Creates an [NumberSlider] - visual representation of the [NumericSetting] */ @UIBuilder - fun Layout.numericSetting( + fun Layout.numberSlider( name: String, unit: String, minV: T, maxV: T, stepV: T, field: KMutableProperty0 diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl index cb9f05b37..b058e7bc2 100644 --- a/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex.glsl @@ -8,7 +8,7 @@ uniforms { }; export { - vec4 v_TexCoord; # uv + vec2 v_TexCoord; # uv }; void fragment() { diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl index e9049bd05..e10b9a4a8 100644 --- a/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex_color.glsl @@ -9,7 +9,7 @@ uniforms { }; export { - vec4 v_TexCoord; # uv + vec2 v_TexCoord; # uv vec4 v_Color; # color }; diff --git a/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl b/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl index 652bcf2fb..395642309 100644 --- a/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl +++ b/common/src/main/resources/assets/lambda/shaders/pos_tex_shady.glsl @@ -8,7 +8,7 @@ uniforms { }; export { - vec4 v_TexCoord; # uv + vec2 v_TexCoord; # uv }; #include "shade" diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl index e269c0043..5d4abd1dc 100644 --- a/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl +++ b/common/src/main/resources/assets/lambda/shaders/renderer/font.glsl @@ -16,18 +16,17 @@ export { vec4 v_Color; # color }; -float sdf(float channel) { - return 1.0 - smoothstep(u_SDFMin, u_SDFMax, 1.0 - channel); -}# +#include "sdf" void fragment() { bool isEmoji = v_TexCoord.x < 0.0; if (isEmoji) { vec4 c = texture(u_EmojiTexture, -v_TexCoord); - color = vec4(c.rgb, sdf(c.a)) * v_Color; + color = vec4(c.rgb, sdf(c.a, u_SDFMin, u_SDFMax)) * v_Color; return; } - color = vec4(1.0, 1.0, 1.0, sdf(texture(u_FontTexture, v_TexCoord).r)) * v_Color; + float sdf = sdf(texture(u_FontTexture, v_TexCoord).r, u_SDFMin, u_SDFMax); + color = vec4(1.0, 1.0, 1.0, sdf) * v_Color; }# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/hue_circle.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/hue_circle.glsl new file mode 100644 index 000000000..a4ead7707 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/hue_circle.glsl @@ -0,0 +1,21 @@ +attributes { + vec4 pos; + vec2 uv; +}; + +export { + vec2 v_TexCoord; # uv +}; + +#include "hsb" +#include "sdf" + +void fragment() { + float dst = 0.5 - length(v_TexCoord - 0.5); + float sdf = sdf(dst, 0.98, 1.0); + + float hue255 = hue(vec2(v_TexCoord.y, 1.0 - v_TexCoord.x)); + vec3 rgb = hsb2rgb(vec3(hue255, 1.0 - dst * 2.0, 1.0)); + + color = vec4(rgb, sdf); +}# diff --git a/common/src/main/resources/assets/lambda/shaders/shared/hsb.glsl b/common/src/main/resources/assets/lambda/shaders/shared/hsb.glsl new file mode 100644 index 000000000..25b9eb99d --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/shared/hsb.glsl @@ -0,0 +1,30 @@ +vec3 hsb2rgb(vec3 hsb) { + float C = hsb.z * hsb.y; + float X = C * (1.0 - abs(mod(hsb.x / 60.0, 2.0) - 1.0)); + float m = hsb.z - C; + + vec3 rgb; + + if (0.0 <= hsb.x && hsb.x < 60.0) { + rgb = vec3(C, X, 0.0); + } else if (60.0 <= hsb.x && hsb.x < 120.0) { + rgb = vec3(X, C, 0.0); + } else if (120.0 <= hsb.x && hsb.x < 180.0) { + rgb = vec3(0.0, C, X); + } else if (180.0 <= hsb.x && hsb.x < 240.0) { + rgb = vec3(0.0, X, C); + } else if (240.0 <= hsb.x && hsb.x < 300.0) { + rgb = vec3(X, 0.0, C); + } else { + rgb = vec3(C, 0.0, X); + } + + return (rgb + vec3(m)); +}# + +float hue(vec2 uv) { + vec2 centered = uv * 2.0 - 1.0; + float hue = degrees(atan(centered.y, centered.x)) + 180.0; + + return hue; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/shared/sdf.glsl b/common/src/main/resources/assets/lambda/shaders/shared/sdf.glsl new file mode 100644 index 000000000..165043f1c --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/shared/sdf.glsl @@ -0,0 +1,3 @@ +float sdf(float channel, float min, float max) { + return 1.0 - smoothstep(min, max, 1.0 - channel); +}# \ No newline at end of file From 6d2f856c0eca4372a886f6da33c08f382da571c1 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 22 Mar 2025 16:25:47 +0300 Subject: [PATCH 10/25] cleanup --- .../gui/impl/clickgui/module/settings/impl/ColorPicker.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt index 49b7d1ffa..23ccb8afd 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt @@ -129,7 +129,7 @@ class ColorPicker( onUpdate { val uv = if (circle.pressedButton == Mouse.Button.Left) clampToCircle(Vec2d(getU(), getV())) - else hueToUv(hue * 360, saturation) + else hueToUv(hue, saturation) positionX = transform(uv.x, 0.0, 1.0, circle.positionX, circle.positionX + circle.width) - knobSize * 0.5 positionY = transform(uv.y, 0.0, 1.0, circle.positionY, circle.positionY + circle.height) - knobSize * 0.5 @@ -163,7 +163,7 @@ class ColorPicker( } private fun hueToUv(hue: Double, radius: Double): Vec2d { - val angle = (hue - 90.0).toRadian() + val angle = (hue * 360 - 90.0).toRadian() val x = cos(angle) * radius val y = sin(angle) * radius @@ -177,7 +177,7 @@ class ColorPicker( private fun clampToCircle(uv: Vec2d): Vec2d { val center = Vec2d(0.5f, 0.5f) val radius = 0.5f - val dx = uv.x- center.x + val dx = uv.x - center.x val dy = uv.y - center.y val dist = sqrt(dx * dx + dy * dy) From 62ea752baa2d0b0abbd4b0c529cc36b0c7953396 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Thu, 27 Mar 2025 16:16:33 +0300 Subject: [PATCH 11/25] raw hud --- .../renderer/gui/AbstractGUIRenderer.kt | 8 +- .../graphics/renderer/gui/TextureRenderer.kt | 8 +- .../main/kotlin/com/lambda/gui/GuiManager.kt | 12 +- .../kotlin/com/lambda/gui/LambdaScreen.kt | 2 +- .../gui/{ScreenLayout.kt => RootLayout.kt} | 6 +- .../com/lambda/gui/component/layout/Layout.kt | 20 ++- .../com/lambda/gui/component/window/Window.kt | 4 +- .../gui/impl/clickgui/core/AnimatedChild.kt | 71 ++++++++- .../gui/impl/clickgui/core/SliderLayout.kt | 2 +- .../gui/impl/clickgui/module/ModuleLayout.kt | 60 +------ .../{settings => setting}/SettingLayout.kt | 2 +- .../{settings => setting}/SettingSlider.kt | 2 +- .../settings}/BooleanButton.kt | 4 +- .../impl => setting/settings}/ColorPicker.kt | 10 +- .../impl => setting/settings}/EnumSlider.kt | 3 +- .../settings}/KeybindPicker.kt | 4 +- .../impl => setting/settings}/NumberSlider.kt | 4 +- .../impl => setting/settings}/UnitButton.kt | 4 +- .../kotlin/com/lambda/module/HudModule.kt | 147 ++++++++++++++---- .../com/lambda/module/hud/Coordinates.kt | 3 +- .../kotlin/com/lambda/module/hud/GifTest.kt | 15 +- .../com/lambda/module/hud/ModuleList.kt | 19 +-- .../main/kotlin/com/lambda/module/hud/TPS.kt | 5 +- .../com/lambda/module/hud/TaskFlowHUD.kt | 13 +- .../com/lambda/module/hud/TickShiftCharge.kt | 11 +- .../kotlin/com/lambda/module/hud/Watermark.kt | 15 +- .../lambda/module/modules/client/ClickGui.kt | 102 ++++++++++-- .../module/modules/client/GuiSettings.kt | 9 +- 28 files changed, 371 insertions(+), 194 deletions(-) rename common/src/main/kotlin/com/lambda/gui/{ScreenLayout.kt => RootLayout.kt} (85%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings => setting}/SettingLayout.kt (97%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings => setting}/SettingSlider.kt (97%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/BooleanButton.kt (96%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/ColorPicker.kt (95%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/EnumSlider.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/KeybindPicker.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/NumberSlider.kt (94%) rename common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/{settings/impl => setting/settings}/UnitButton.kt (92%) 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 3bd9a33c0..8d41cbd6f 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 @@ -21,11 +21,11 @@ import com.lambda.graphics.RenderMain import com.lambda.graphics.buffer.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.pipeline.ScissorAdapter import com.lambda.graphics.shader.Shader import com.lambda.module.modules.client.GuiSettings +import com.lambda.module.modules.client.GuiSettings.primaryColor +import com.lambda.module.modules.client.GuiSettings.secondaryColor import com.lambda.util.math.MathUtils.toInt -import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d import org.lwjgl.glfw.GLFW @@ -47,8 +47,8 @@ abstract class AbstractGUIRenderer( shader["u_Shade"] = shade.toInt().toDouble() if (shade) { shader["u_ShadeTime"] = GLFW.glfwGetTime() * GuiSettings.colorSpeed * 5.0 - shader["u_ShadeColor1"] = GuiSettings.shadeColor1 - shader["u_ShadeColor2"] = GuiSettings.shadeColor2 + shader["u_ShadeColor1"] = primaryColor + shader["u_ShadeColor2"] = secondaryColor shader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index 7e2a902d5..e746ce1e6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -24,10 +24,10 @@ import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.graphics.texture.Texture import com.lambda.module.modules.client.GuiSettings -import com.lambda.util.math.MathUtils.toInt +import com.lambda.module.modules.client.GuiSettings.primaryColor +import com.lambda.module.modules.client.GuiSettings.secondaryColor import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d -import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW.glfwGetTime object TextureRenderer { @@ -49,8 +49,8 @@ object TextureRenderer { coloredShader["u_Shade"] = 1.0 coloredShader["u_ShadeTime"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 - coloredShader["u_ShadeColor1"] = GuiSettings.shadeColor1 - coloredShader["u_ShadeColor2"] = GuiSettings.shadeColor2 + coloredShader["u_ShadeColor1"] = primaryColor + coloredShader["u_ShadeColor2"] = secondaryColor coloredShader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index 7ddf7f896..c2efba21e 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -29,12 +29,12 @@ import com.lambda.config.settings.numeric.LongSetting import com.lambda.core.Loadable import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.impl.BooleanButton.Companion.booleanSetting -import com.lambda.gui.impl.clickgui.module.settings.impl.ColorPicker.Companion.colorPicker -import com.lambda.gui.impl.clickgui.module.settings.impl.EnumSlider.Companion.enumSetting -import com.lambda.gui.impl.clickgui.module.settings.impl.KeybindPicker.Companion.keybindSetting -import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numberSlider -import com.lambda.gui.impl.clickgui.module.settings.impl.UnitButton.Companion.unitButton +import com.lambda.gui.impl.clickgui.module.setting.settings.BooleanButton.Companion.booleanSetting +import com.lambda.gui.impl.clickgui.module.setting.settings.ColorPicker.Companion.colorPicker +import com.lambda.gui.impl.clickgui.module.setting.settings.EnumSlider.Companion.enumSetting +import com.lambda.gui.impl.clickgui.module.setting.settings.KeybindPicker.Companion.keybindSetting +import com.lambda.gui.impl.clickgui.module.setting.settings.NumberSlider.Companion.numberSlider +import com.lambda.gui.impl.clickgui.module.setting.settings.UnitButton.Companion.unitButton import kotlin.reflect.KClass object GuiManager : Loadable { diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt b/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt index cf9a58d83..b51555f48 100644 --- a/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt +++ b/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt @@ -38,7 +38,7 @@ import net.minecraft.text.Text */ class LambdaScreen( override val name: String, - val layout: ScreenLayout + val layout: RootLayout ) : Screen(Text.of(name)), Nameable, Muteable { override val isMuted: Boolean get() = !isOpen diff --git a/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt b/common/src/main/kotlin/com/lambda/gui/RootLayout.kt similarity index 85% rename from common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt rename to common/src/main/kotlin/com/lambda/gui/RootLayout.kt index 07c642ebc..d0a4f8df6 100644 --- a/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/RootLayout.kt @@ -21,7 +21,7 @@ import com.lambda.graphics.RenderMain import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -class ScreenLayout : Layout(owner = null) { +class RootLayout : Layout(owner = null) { init { onUpdate { size = RenderMain.screenSize @@ -33,7 +33,7 @@ class ScreenLayout : Layout(owner = null) { * Creates gui layout */ @UIBuilder - fun gui(name: String, block: ScreenLayout.() -> Unit) = - LambdaScreen(name, ScreenLayout().apply(block)) + fun gui(name: String, block: RootLayout.() -> Unit) = + LambdaScreen(name, RootLayout().apply(block)) } } diff --git a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt index 33ddb3fba..46138e7b3 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt @@ -21,12 +21,13 @@ import com.lambda.graphics.RenderMain import com.lambda.graphics.animation.AnimationTicker import com.lambda.event.events.GuiEvent import com.lambda.graphics.pipeline.ScissorAdapter -import com.lambda.gui.ScreenLayout +import com.lambda.gui.RootLayout import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.* import com.lambda.util.KeyCode import com.lambda.util.Mouse +import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d @@ -126,7 +127,7 @@ open class Layout( own = own.owner ?: break } - own as? ScreenLayout ?: throw IllegalStateException("Root layout is not a ScreenLayout class") + own// as? RootLayout ?: throw IllegalStateException("Root layout is not a ScreenLayout class") } protected open val renderSelf: Boolean get() = width > 1 && height > 1 @@ -392,6 +393,21 @@ open class Layout( } } + fun buildTree(builder: StringBuilder = StringBuilder(), level: Int = 0): String { + val space = " ".repeat(level) + builder.appendLine( + space + this::class.java.simpleName + " {".repeat(children.isNotEmpty().toInt()) + ) + + children.forEach { + it.buildTree(builder, level + 1) + } + + if (children.isNotEmpty()) builder.appendLine("$space}") + + return builder.toString() + } + companion object { /** * Creates an empty [Layout]. diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt index 3b55fc8e0..e2fff4c53 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt @@ -19,7 +19,7 @@ package com.lambda.gui.component.window import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.module.modules.client.ClickGui -import com.lambda.gui.ScreenLayout +import com.lambda.gui.RootLayout import com.lambda.gui.component.core.FilledRect.Companion.rect import com.lambda.gui.component.core.LayoutBuilder import com.lambda.gui.component.core.OutlineRect.Companion.outline @@ -152,7 +152,7 @@ open class Window( init { position = initialPosition - properties.clampPosition = owner is ScreenLayout + properties.clampPosition = owner is RootLayout onUpdate { // Update it here diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt index 7c6327513..de21ffc98 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt @@ -19,14 +19,18 @@ package com.lambda.gui.impl.clickgui.core import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.gui.component.HAlign +import com.lambda.gui.component.core.FilledRect +import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.FilledRect.Companion.rectBehind import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.window.Window import com.lambda.gui.impl.clickgui.module.ModuleLayout -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout import com.lambda.module.modules.client.ClickGui import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d import com.lambda.util.math.lerp +import com.lambda.util.math.multAlpha import com.lambda.util.math.setAlpha import com.lambda.util.math.transform import java.awt.Color @@ -152,4 +156,69 @@ abstract class AnimatedChild( outlineRect ).forEach(Layout::destroy) } + + companion object { + fun AnimatedChild.animatedBackground( + enableProgress: () -> Double = { hoverAnimation } + ) = rectBehind(titleBar) { + // base rect with lowest y to avoid children overlying + onUpdate { + val enableAnimation = enableProgress() + + rect = this@animatedBackground.rect.shrink(shrink) + shade = ClickGui.backgroundShade + + val openRev = 1.0 - openAnimation // 1.0 <-> 0.0 + val openRevSigned = openRev * 2 - 1 // 1.0 <-> -1.0 + val enableRev = 1.0 - enableAnimation // 1.0 <-> 0.0 + + var progress = enableAnimation + + // hover: +0.1 to alpha if minimized, -0.1 to alpha if maximized and enabled + progress += hoverAnimation * ClickGui.moduleHoverAccent * + lerp(enableAnimation, 1.0, openRevSigned) + + // +0.4 to alpha if opened and disabled + progress += openAnimation * ClickGui.moduleOpenAccent * enableRev + + // interpolate and set the color + setColor( + lerp(progress, + ClickGui.moduleDisabledColor, + ClickGui.moduleEnabledColor + ).multAlpha(showAnimation) + ) + + setRadius(hoverAnimation) + + if (isLast && ClickGui.autoResize) { + leftBottomRadius = ClickGui.roundRadius - (ClickGui.padding + shrink) + rightBottomRadius = leftBottomRadius + } + } + + rect { // hover fx + onUpdate { + val base = this@rect.owner as FilledRect + + position = base.position + size = base.size + shade = base.shade + + setRadius( + base.leftTopRadius, + base.rightTopRadius, + base.rightBottomRadius, + base.leftBottomRadius + ) + + val hoverColor = Color.WHITE.setAlpha( + ClickGui.moduleHoverAccent * hoverAnimation * (1.0 - openAnimation) * showAnimation + ) + + setColorH(hoverColor.setAlpha(0.0), hoverColor) + } + } + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt index 614ea6b1b..ca8ac731a 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt @@ -24,7 +24,7 @@ import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.core.insertLayout import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.SettingSlider +import com.lambda.gui.impl.clickgui.module.setting.SettingSlider import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.math.MathUtils.toInt diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt index 6d9b2d9dd..a50074d66 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/ModuleLayout.kt @@ -29,7 +29,7 @@ import com.lambda.gui.component.layout.Layout import com.lambda.gui.component.window.Window import com.lambda.gui.impl.clickgui.ModuleWindow import com.lambda.gui.impl.clickgui.core.AnimatedChild -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout import com.lambda.util.Mouse import com.lambda.util.math.* import java.awt.Color @@ -50,63 +50,7 @@ class ModuleLayout( private var enableAnimation by animation.exp(0.0, 1.0, 0.6, module::isEnabled) - val backgroundRect = rectBehind(titleBar) { // base rect with lowest y to avoid children overlying - onUpdate { - rect = this@ModuleLayout.rect.shrink(shrink) - shade = ClickGui.backgroundShade - - val openRev = 1.0 - openAnimation // 1.0 <-> 0.0 - val openRevSigned = openRev * 2 - 1 // 1.0 <-> -1.0 - val enableRev = 1.0 - enableAnimation // 1.0 <-> 0.0 - - var progress = enableAnimation - - // hover: +0.1 to alpha if minimized, -0.1 to alpha if maximized and enabled - progress += hoverAnimation * ClickGui.moduleHoverAccent * - lerp(enableAnimation, 1.0, openRevSigned) - - // +0.4 to alpha if opened and disabled - progress += openAnimation * ClickGui.moduleOpenAccent * enableRev - - // interpolate and set the color - setColor( - lerp(progress, - ClickGui.moduleDisabledColor, - ClickGui.moduleEnabledColor - ).multAlpha(showAnimation) - ) - - setRadius(hoverAnimation) - - if (isLast && ClickGui.autoResize) { - leftBottomRadius = ClickGui.roundRadius - (ClickGui.padding + shrink) - rightBottomRadius = leftBottomRadius - } - } - - rect { // hover fx - onUpdate { - val base = this@rect.owner as FilledRect - - position = base.position - size = base.size - shade = base.shade - - setRadius( - base.leftTopRadius, - base.rightTopRadius, - base.rightBottomRadius, - base.leftBottomRadius - ) - - val hoverColor = Color.WHITE.setAlpha( - ClickGui.moduleHoverAccent * hoverAnimation * (1.0 - openAnimation) * showAnimation - ) - - setColorH(hoverColor.setAlpha(0.0), hoverColor) - } - } - } + val backgroundRect = animatedBackground(::enableAnimation) init { backgroundTint() diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingLayout.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingLayout.kt index 9630a7f63..0e7059646 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingLayout.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.setting import com.lambda.gui.component.core.LayoutBuilder import com.lambda.module.modules.client.ClickGui diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingSlider.kt similarity index 97% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingSlider.kt index 54ad0b7ad..932fec471 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/SettingSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/SettingSlider.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings +package com.lambda.gui.impl.clickgui.module.setting import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.gui.component.HAlign diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt similarity index 96% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt index c506d3948..9ba0d5fe0 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/BooleanButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.module.modules.client.ClickGui @@ -23,7 +23,7 @@ import com.lambda.gui.component.core.FilledRect.Companion.rect import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout import com.lambda.util.Mouse import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt similarity index 95% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt index 23ccb8afd..ba150e36f 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.graphics.renderer.gui.TextureRenderer import com.lambda.graphics.shader.Shader.Companion.shader @@ -23,8 +23,8 @@ import com.lambda.gui.component.core.FilledRect.Companion.rect import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout -import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numberSlider +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.settings.NumberSlider.Companion.numberSlider import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.StringUtils.capitalize @@ -113,7 +113,7 @@ class ColorPicker( if (pressedButton != Mouse.Button.Left) return@onMouseMove val (u, v) = getU() to getV() - hue = uvToHue(u, v).div(360.0).coerceIn(0.0, 1.0) + hue = uvToHue(u, v) saturation = hypot(u - 0.5, v - 0.5).coerceIn(0.0, 0.5) * 2 } @@ -159,7 +159,7 @@ class ColorPicker( var hue = atan2(y, x).toDegree() + 90.0 if (hue < 0) hue += 360.0 - return hue + return hue.div(360.0).coerceIn(0.0, 1.0) } private fun hueToUv(hue: Double, radius: Double): Vec2d { diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt index 9953e6de4..585a3b35b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/EnumSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.config.settings.comparable.EnumSetting import com.lambda.config.settings.comparable.EnumSetting.Companion.enumValues @@ -23,6 +23,7 @@ import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.util.NamedEnum import com.lambda.gui.impl.clickgui.module.settings.SettingSlider +import com.lambda.gui.impl.clickgui.module.setting.SettingSlider import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.transform import kotlin.reflect.KMutableProperty0 diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/KeybindPicker.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/KeybindPicker.kt index 7e9c1ae59..f6f2d1b0d 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/KeybindPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/KeybindPicker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.config.settings.complex.KeyBindSetting import com.lambda.gui.component.HAlign @@ -23,7 +23,7 @@ import com.lambda.gui.component.core.TextField.Companion.textField import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.ModuleLayout -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.extension.displayValue diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/NumberSlider.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/NumberSlider.kt index 7f3f3d7de..e585c10f2 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/NumberSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/NumberSlider.kt @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.config.settings.NumericSetting import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.SettingSlider +import com.lambda.gui.impl.clickgui.module.setting.SettingSlider import com.lambda.util.math.MathUtils.roundToStep import com.lambda.util.math.MathUtils.typeConvert import com.lambda.util.math.lerp diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/UnitButton.kt similarity index 92% rename from common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt rename to common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/UnitButton.kt index d52e69799..b90c8eaeb 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/settings/impl/UnitButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/UnitButton.kt @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package com.lambda.gui.impl.clickgui.module.settings.impl +package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout -import com.lambda.gui.impl.clickgui.module.settings.SettingLayout +import com.lambda.gui.impl.clickgui.module.setting.SettingLayout import com.lambda.util.Mouse import kotlin.reflect.KMutableProperty0 diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 15cdb1ec0..91dd5ecc2 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -17,16 +17,24 @@ package com.lambda.module +import com.lambda.event.events.GuiEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.animation.AnimationTicker -import com.lambda.gui.component.DockingRect +import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.OutlineRect.Companion.outline +import com.lambda.gui.component.core.TextField.Companion.textField +import com.lambda.gui.component.core.UIBuilder +import com.lambda.gui.component.layout.Layout +import com.lambda.gui.component.layout.Layout.Companion.layout +import com.lambda.module.modules.client.ClickGui import com.lambda.module.tag.ModuleTag -import com.lambda.gui.component.HAlign -import com.lambda.gui.component.VAlign import com.lambda.util.KeyCode +import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d +import kotlin.math.max +import kotlin.math.min abstract class HudModule( name: String, @@ -35,47 +43,126 @@ abstract class HudModule( alwaysListening: Boolean = false, enabledByDefault: Boolean = false, defaultKeybind: KeyCode = KeyCode.UNBOUND, + background: Boolean = true ) : Module(name, description, defaultTags, alwaysListening, enabledByDefault, defaultKeybind) { - protected abstract val width: Double - protected abstract val height: Double - - private val rectHandler = object : DockingRect() { - private var relativePosX by setting("Position X", 0.0, -10000.0..10000.0, 0.1) { false } - private var relativePosY by setting("Position Y", 0.0, -10000.0..10000.0, 0.1) { false } - override var relativePos - get() = Vec2d(relativePosX, relativePosY) - set(value) { - relativePosX = value.x; relativePosY = value.y - } - override val width get() = this@HudModule.width - override val height get() = this@HudModule.height + protected val animation = AnimationTicker() + + private val base by lazy { + Layout(null).apply { + if (!background) return@apply + + rect { + onUpdate { + position = this@apply.position + size = this@apply.size - override val autoDocking by setting("Auto Docking", true).onValueChange { _, _ -> autoDocking() } + setColor(ClickGui.backgroundColor) + setRadius(ClickGui.roundRadius) - override var dockingH by setting("Docking H", HAlign.LEFT) { !autoDocking } - .onValueChange { from, to -> - val delta = to.multiplier - from.multiplier - relativePosX += delta * (size.x - screenSize.x) + shade = ClickGui.backgroundShade + } } - override var dockingV by setting("Docking V", VAlign.TOP) { !autoDocking } - .onValueChange { from, to -> - val delta = to.multiplier - from.multiplier - relativePosY += delta * (size.y - screenSize.y) + outline { + onUpdate { + position = this@apply.position + size = this@apply.size + + setColor(ClickGui.outlineColor) + + roundRadius = ClickGui.roundRadius + glowRadius = ClickGui.outlineWidth * ClickGui.outline.toInt().toDouble() + + shade = ClickGui.outlineShade + } } + } } - var position by rectHandler::position - val rect by rectHandler::rect - val animation = AnimationTicker() + protected val content by lazy { + base.layout { + onUpdate { + val maxRadius = min(size.x, size.y) + val w = ClickGui.roundRadius.coerceAtMost(maxRadius) + val h = if (this@HudModule is Text) 0.0 else ClickGui.roundRadius.coerceAtMost(maxRadius) + val padding = Vec2d(max(ClickGui.hudPadding, w), max(ClickGui.hudPadding, h)) + base.size = size + padding * 2 + position = base.position + padding + } + } + } - protected fun onRender(block: () -> Unit) = - listen { block() } + private val scheduled = mutableListOf<() -> Unit>() init { listen { animation.tick() } + + listen { + while (scheduled.isNotEmpty()) { + scheduled.removeFirst().invoke() + } + + base.onEvent(GuiEvent.Update) + base.onEvent(GuiEvent.Render) + } + + onEnable { + base.onEvent(GuiEvent.Show) + println(base.buildTree()) + } + + onDisable { + base.onEvent(GuiEvent.Hide) + } + } + + @UIBuilder + protected fun build(block: Layout.() -> Unit) { + scheduled += { content.apply(block) } + } + + @UIBuilder + protected fun Layout.customDrawable(render: Layout.() -> Unit) { + layout { + onUpdate { + width = this@customDrawable.width + height = this@customDrawable.height + } + + onRender { + render(this@layout) + } + } + } + + fun getRootLayout() = base + + abstract class Text( + name: String, + description: String = "", + defaultTags: Set = setOf(), + alwaysListening: Boolean = false, + enabledByDefault: Boolean = false, + defaultKeybind: KeyCode = KeyCode.UNBOUND + ) : HudModule(name, description, defaultTags, alwaysListening, enabledByDefault, defaultKeybind) { + abstract fun getText(): String + + init { + build { + val text = textField { + onUpdate { + text = getText() + } + } + + onUpdate { + content.width = text.textWidth + content.height = text.textHeight + } + } + } } } diff --git a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt index 837240367..0736c6ad7 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -20,6 +20,7 @@ package com.lambda.module.hud import com.lambda.context.SafeContext import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString +import com.lambda.gui.component.core.TextField.Companion.textField import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe @@ -30,7 +31,7 @@ import com.lambda.util.extension.isNether import com.lambda.util.math.netherCoord import com.lambda.util.math.overworldCoord -object Coordinates : HudModule( +object Coordinates : HudModule.Text( name = "Coordinates", description = "Show your coordinates", defaultTags = setOf(ModuleTag.CLIENT), diff --git a/common/src/main/kotlin/com/lambda/module/hud/GifTest.kt b/common/src/main/kotlin/com/lambda/module/hud/GifTest.kt index b4c8fe833..fe991b01d 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/GifTest.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/GifTest.kt @@ -19,6 +19,7 @@ package com.lambda.module.hud import com.lambda.graphics.renderer.gui.TextureRenderer.drawTexture import com.lambda.graphics.texture.TextureOwner.uploadGif +import com.lambda.gui.component.layout.Layout.Companion.layout import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag @@ -26,14 +27,16 @@ object GifTest : HudModule( name = "GifTest", defaultTags = setOf(ModuleTag.CLIENT), ) { - val test = uploadGif("chika.gif") - - override val width = 100.0 - override val height = 100.0 + private val test = uploadGif("chika.gif") init { - onRender { - drawTexture(test, rect) + build { + width = 100.0 + height = 100.0 + + customDrawable { + drawTexture(test, rect) + } } } } diff --git a/common/src/main/kotlin/com/lambda/module/hud/ModuleList.kt b/common/src/main/kotlin/com/lambda/module/hud/ModuleList.kt index ee2037817..236cdbe02 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/ModuleList.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/ModuleList.kt @@ -17,25 +17,20 @@ package com.lambda.module.hud -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString import com.lambda.module.HudModule import com.lambda.module.ModuleRegistry import com.lambda.module.tag.ModuleTag -import com.lambda.util.math.Vec2d -object ModuleList : HudModule( +object ModuleList : HudModule.Text( name = "ModuleList", defaultTags = setOf(ModuleTag.CLIENT), ) { - override val width = 200.0 - override val height = 200.0 - init { - onRender { - val enabled = ModuleRegistry.modules - .filter { it.isEnabled } - .filter { it.isVisible.value } - drawString(enabled.joinToString("\n") { "${it.name} [${it.keybind.name}]" }, Vec2d.ZERO) - } + override fun getText(): String { + val enabled = ModuleRegistry.modules + .filter { it.isEnabled } + .filter { it.isVisible.value } + + return enabled.joinToString("\n") { "${it.name} [${it.keybind.name}]" } } } diff --git a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt index a5cf622e5..e5a6fe516 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt @@ -25,14 +25,15 @@ import com.lambda.util.Formatting.string import com.lambda.util.NamedEnum import com.lambda.util.ServerTPS.averageMSPerTick -object TPS : HudModule( +object TPS : HudModule.Text( name = "TPS", description = "Display the server's tick rate", defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.NETWORK), ) { private val format by setting("Tick format", TickFormat.TPS) - private val text: String get() = "${format.displayName}: ${format.output().string}${format.unit}" + override fun getText() = + "${format.displayName}: ${format.output().string}${format.unit}" // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters diff --git a/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt b/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt index f4fd69552..0ff90fa70 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TaskFlowHUD.kt @@ -17,22 +17,13 @@ package com.lambda.module.hud -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask -import com.lambda.util.math.Vec2d -object TaskFlowHUD : HudModule( +object TaskFlowHUD : HudModule.Text( name = "TaskFlowHud", defaultTags = setOf(ModuleTag.CLIENT), ) { - override val width = 200.0 - override val height = 200.0 - - init { - onRender { - drawString(RootTask.toString(), Vec2d.ZERO) - } - } + override fun getText() = RootTask.toString() } diff --git a/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt index 600a21271..650995781 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt @@ -45,11 +45,12 @@ object TickShiftCharge : HudModule( (TickShift.balance / TickShift.maxBalance.toDouble()).coerceIn(0.0..1.0) } - override val width = 70.0 - override val height = 14.0 - init { - onRender { + build { + + } + + /*onRender { filledRect( rect = rect, roundRadius = ClickGui.roundRadius, @@ -74,6 +75,6 @@ object TickShiftCharge : HudModule( shade = true ) } - } + }*/ } } diff --git a/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt b/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt index 794738eb0..ef477296d 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Watermark.kt @@ -20,6 +20,7 @@ package com.lambda.module.hud import com.lambda.graphics.renderer.gui.TextureRenderer.drawTexture import com.lambda.graphics.renderer.gui.TextureRenderer.drawTextureShaded import com.lambda.graphics.texture.TextureOwner.upload +import com.lambda.gui.component.layout.Layout.Companion.layout import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag @@ -29,16 +30,18 @@ object Watermark : HudModule( ) { private val shade by setting("Shade", true) - override val width = 50.0 - override val height = 50.0 - private val normalTexture = upload("textures/lambda.png") private val monoTexture = upload("textures/lambda_mono.png") init { - onRender { - if (shade) drawTextureShaded(monoTexture, rect) - else drawTexture(normalTexture, rect) + build { + width = 50.0 + height = 50.0 + + customDrawable { + if (shade) drawTextureShaded(monoTexture, rect) + else drawTexture(normalTexture, rect) + } } } } 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 f3db6b239..e207e1d52 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 @@ -18,15 +18,25 @@ package com.lambda.module.modules.client import com.lambda.Lambda.mc +import com.lambda.gui.LambdaScreen import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.gui.ScreenLayout.Companion.gui +import com.lambda.gui.RootLayout.Companion.gui +import com.lambda.gui.component.HAlign +import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.ModuleWindow.Companion.moduleWindow +import com.lambda.gui.impl.clickgui.core.AnimatedChild.Companion.animatedBackground +import com.lambda.gui.impl.clickgui.module.setting.settings.UnitButton.Companion.unitButton +import com.lambda.module.HudModule +import com.lambda.module.ModuleRegistry import com.lambda.util.KeyCode +import com.lambda.util.Mouse import com.lambda.util.math.Vec2d import com.lambda.util.math.setAlpha import java.awt.Color +import kotlin.math.hypot object ClickGui : Module( name = "ClickGui", @@ -49,7 +59,7 @@ object ClickGui : Module( val backgroundShade by setting("Background Shade", true) val outline by setting("Outline", true) - val outlineWidth by setting("Outline Width", 6.0, 1.0..10.0, 0.1) { outline } + val outlineWidth by setting("Outline Width", 10.0, 1.0..20.0, 0.1) { outline } val outlineColor by setting("Outline Color", Color.WHITE.setAlpha(0.6)) { outline } val outlineShade by setting("Outline Shade", true) { outline } val fontScale by setting("Font Scale", 1.0, 0.5..2.0, 0.1) @@ -65,24 +75,65 @@ object ClickGui : Module( val animationCurve by setting("List Animation Curve", AnimationCurve.Normal) val smoothness by setting("Smoothness", 0.4, 0.3..0.7, 0.01) { animationCurve != AnimationCurve.Static } - val SCREEN get() = gui("Click Gui") { - onKeyPress { - if (it.keyCode != keybind.keyCode || keybind == KeyCode.UNBOUND) return@onKeyPress - mc.currentScreen?.close() - } + val hudPadding by setting("Hud Padding", 3.0, 0.0..10.0, 0.1) - rect { - onUpdate { - rect = owner!!.rect - setColor(backgroundTint) + val SCREEN: LambdaScreen by lazy { + gui("Click Gui") { + onKeyPress { + if (it.keyCode != keybind.keyCode || keybind == KeyCode.UNBOUND) return@onKeyPress + mc.currentScreen?.close() + } + + rect { + onUpdate { + rect = owner!!.rect + setColor(backgroundTint) + } + } + + var x = 10.0 + val y = x + + ModuleTag.defaults.forEach { tag -> + x += moduleWindow(tag, Vec2d(x, y)).width + 5 } + + switchButton("HUD", ::HUD) } + } + + val HUD: LambdaScreen by lazy { + gui("Hud GUI") { + switchButton("Back", ::SCREEN) + + var dragInfo: Pair? = null - var x = 10.0 - val y = x + onShow { + dragInfo = null + } + + onMouse(action = Mouse.Action.Click, button = Mouse.Button.Left) { + dragInfo = null + } - ModuleTag.defaults.forEach { tag -> - x += moduleWindow(tag, Vec2d(x, y)).width + 5 + onMouseMove { mouse -> + if (pressedButton != Mouse.Button.Left) return@onMouseMove + + ModuleRegistry.modules + .filterIsInstance() + .filter { mouse in it.getRootLayout().rect } + .minByOrNull { + val (x, y) = it.getRootLayout().rect.center + hypot(x - mouse.x, y - mouse.y) + }?.let { module -> + dragInfo = dragInfo ?: (module to (mouse - module.getRootLayout().position)) + + } + + dragInfo?.let { drag -> + drag.first.getRootLayout().position = mouse - drag.second + } + } } } @@ -98,4 +149,25 @@ object ClickGui : Module( toggle() } } + + private fun Layout.switchButton(text: String, gui: () -> LambdaScreen) { + unitButton(text) { + gui().show() + }.apply { + animatedBackground { + hoverAnimation * 0.5 + 0.5 + } + + horizontalAlignment = HAlign.RIGHT + verticalAlignment = VAlign.BOTTOM + titleBar.textField.textHAlignment = HAlign.CENTER + + positionX = owner!!.positionX - width - 10.0 + positionY = owner.positionY - height - 10.0 + + onUpdate { + width = height * 1.5 + } + } + } } 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 58d30acc1..cb314c591 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 @@ -42,18 +42,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 }) - val backgroundColor by setting("Background Color", Color(50, 50, 50, 150), 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("Shade Width", 200.0, 10.0..1000.0, 10.0, visibility = { page == Page.Colors }) val colorHeight by setting("Shade Height", 200.0, 10.0..1000.0, 10.0, visibility = { page == Page.Colors }) val colorSpeed by setting("Color Speed", 1.0, 0.1..5.0, 0.1, visibility = { page == Page.Colors }) - val mainColor: Color get() = if (shade) Color.WHITE else primaryColor - - val shadeColor1 get() = primaryColor - val shadeColor2 get() = secondaryColor - enum class Page { General, Colors @@ -61,7 +54,7 @@ object GuiSettings : Module( private var targetScale = 2.0 get() { - val update = System.currentTimeMillis() - lastChange > 200 || mc.currentScreen !is LambdaScreen + val update = System.currentTimeMillis() - lastChange > 500 || mc.currentScreen !is LambdaScreen if (update) field = scaleSetting / 100.0 * 2.0 return field } From 4a1b0a6b775ec99c58f1f2240d5e335b0214f2ba Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Thu, 27 Mar 2025 22:33:13 +0300 Subject: [PATCH 12/25] Persistent byte puffers for VertexPipeline --- .../mixin/render/VertexBufferMixin.java | 4 +- .../kotlin/com/lambda/graphics/RenderMain.kt | 1 + .../com/lambda/graphics/buffer/Buffer.kt | 54 +++- .../graphics/buffer/DynamicByteBuffer.kt | 187 ++++++++++++ .../lambda/graphics/buffer/IRenderContext.kt | 52 ---- .../lambda/graphics/buffer/VertexPipeline.kt | 209 -------------- .../graphics/buffer/frame/FrameBuffer.kt | 5 +- .../buffer/frame/ScreenFrameBuffer.kt | 76 ----- .../graphics/buffer/vertex/ElementBuffer.kt | 54 ---- .../graphics/buffer/vertex/VertexArray.kt | 24 +- .../graphics/buffer/vertex/VertexBuffer.kt | 43 --- .../buffer/vertex/attributes/VertexAttrib.kt | 28 +- .../buffer/vertex/attributes/VertexMode.kt | 7 +- .../kotlin/com/lambda/graphics/gl/Memory.kt | 78 ----- .../graphics/pipeline/PersistentBuffer.kt | 112 ++++++++ .../lambda/graphics/pipeline/VertexBuilder.kt | 267 ++++++++++++++++++ .../graphics/pipeline/VertexPipeline.kt | 152 ++++++++++ .../{pipeline => renderer}/ScissorAdapter.kt | 4 +- .../graphics/renderer/esp/ChunkedESP.kt | 2 +- .../graphics/renderer/esp/ESPRenderer.kt | 22 +- .../esp/builders/DynamicESPBuilders.kt | 79 +++--- .../esp/builders/StaticESPBuilders.kt | 91 +++--- .../renderer/esp/global/DynamicESP.kt | 1 + .../graphics/renderer/esp/global/StaticESP.kt | 3 +- .../renderer/esp/impl/DynamicESPRenderer.kt | 2 +- .../renderer/esp/impl/StaticESPRenderer.kt | 44 +-- .../renderer/gui/AbstractGUIRenderer.kt | 23 +- .../graphics/renderer/gui/TextureRenderer.kt | 30 +- .../renderer/gui/font/FontRenderer.kt | 34 ++- .../gui/font/sdf/DistanceFieldTexture.kt | 23 +- .../renderer/gui/rect/FilledRectRenderer.kt | 33 ++- .../renderer/gui/rect/OutlineRectRenderer.kt | 36 +-- .../com/lambda/gui/component/layout/Layout.kt | 8 +- .../gui/component/window/WindowContent.kt | 3 + .../module/modules/client/RenderSettings.kt | 4 + .../lambda/module/modules/render/Particles.kt | 33 ++- 36 files changed, 1071 insertions(+), 757 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/DynamicByteBuffer.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/pipeline/VertexBuilder.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt rename common/src/main/kotlin/com/lambda/graphics/{pipeline => renderer}/ScissorAdapter.kt (96%) diff --git a/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java b/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java index 84f0b9e21..9d5980c7f 100644 --- a/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java @@ -17,7 +17,7 @@ package com.lambda.mixin.render; -import com.lambda.graphics.buffer.vertex.ElementBuffer; +import com.lambda.graphics.buffer.Buffer; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.render.BufferBuilder; @@ -37,6 +37,6 @@ public class VertexBufferMixin { @Inject(method = "uploadIndexBuffer", at = @At("RETURN")) private void onConfigureIndexBuffer(BufferBuilder.DrawParameters parameters, ByteBuffer vertexBuffer, CallbackInfoReturnable cir) { RenderSystem.ShapeIndexBuffer value = cir.getReturnValue(); - ElementBuffer.lastIbo = value == null ? this.indexBufferId : value.id; + Buffer.lastIbo = value == null ? this.indexBufferId : value.id; } } diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index e448cd798..085dd205f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -24,6 +24,7 @@ import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrices import com.lambda.module.modules.client.GuiSettings +import com.lambda.util.Communication.info import com.lambda.util.math.Vec2d import com.mojang.blaze3d.systems.RenderSystem.getProjectionMatrix import org.joml.Matrix4f diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt index 7af4ae588..44057349f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt @@ -154,6 +154,30 @@ abstract class Buffer( return null } + /** + * Update the current buffer without re-allocating + * This function handles the buffer binding + * Alternative to [map] + */ + open fun update( + offset: Long, + size: Long, + dataPointer: Long + ): Throwable? { + if (!bufferValid(target, access)) + return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") + + repeat(buffers) { + bind() + nglBufferSubData(target, offset, size, dataPointer) + swap() + } + + bind(0) + + return null + } + /** * Allocates a region of memory for the buffer * This function handles the buffer binding @@ -332,4 +356,32 @@ abstract class Buffer( if (isVertexArray) glGenVertexArrays(bufferIds) // If there are more than 1 buffer you should expect undefined behavior, this is not the way to do it else glGenBuffers(bufferIds) } -} + + companion object { + @JvmField + var lastIbo = 0 + var prevIbo = 0 + + fun createPipelineBuffer(bufferTarget: Int) = object : Buffer(buffers = 1) { + override val target: Int = bufferTarget + + override val usage: Int = GL_STATIC_DRAW + override val access: Int = GL_MAP_WRITE_BIT + + override fun bind(id: Int) { + if (bufferTarget != GL_ELEMENT_ARRAY_BUFFER) { + super.bind(id) + return + } + + if (id != 0) prevIbo = lastIbo + super.bind(if (id != 0) id else prevIbo) + } + + override fun upload( + data: ByteBuffer, + offset: Long, + ): Throwable = UnsupportedOperationException() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/DynamicByteBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/DynamicByteBuffer.kt new file mode 100644 index 000000000..02ec17360 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/DynamicByteBuffer.kt @@ -0,0 +1,187 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.buffer + +import org.lwjgl.BufferUtils.createByteBuffer +import org.lwjgl.system.MemoryUtil.memAddress0 +import org.lwjgl.system.MemoryUtil.memCopy +import org.lwjgl.system.MemoryUtil.memPutByte +import org.lwjgl.system.MemoryUtil.memPutFloat +import org.lwjgl.system.MemoryUtil.memPutInt +import java.awt.Color +import java.nio.ByteBuffer + +/** + * Dynamically resizable byte buffer designed for efficient vertex building. + * Automatically grows capacity when needed, and provides + * convenience methods for common data types used in vertex attributes. + * + * @property data The underlying [java.nio.ByteBuffer] storing the vertex data + * @property capacity Current maximum capacity of the buffer in bytes + * @property pointer Base memory address of the buffer + * @property position Current write position in memory address space + */ +class DynamicByteBuffer private constructor(initialCapacity: Int) { + var data: ByteBuffer = createByteBuffer(initialCapacity); private set + var capacity = initialCapacity; private set + + var pointer = memAddress0(data); private set + var position = pointer; private set + + /** + * Gets the total number of bytes written to the buffer since last reset + */ + val bytesPut get() = position - pointer + + /** + * Resets the write position to the beginning of the buffer while maintaining current capacity, + * allowing for buffer reuse without reallocation + */ + fun resetPosition() { + position = pointer + } + + /** + * Writes a single byte value at the current position + * @param value The byte value to write + */ + fun putByte(value: Byte) { + require(1) + memPutByte(position, value) + position += 1 + } + + /** + * Writes a 4-byte integer value at the current position + * @param value The integer value to write + */ + fun putInt(value: Int) { + require(4) + memPutInt(position, value) + position += 4 + } + + /** + * Writes a 4-byte floating point value at the current position + * @param value The double-precision value to write (will be converted to float) + */ + fun putFloat(value: Double) { + require(4) + memPutFloat(position, value.toFloat()) + position += 4 + } + + /** + * Writes a 2-component vector as two consecutive 4-byte floats + * @param x X-axis component + * @param y Y-axis component + */ + fun putVec2(x: Double, y: Double) { + require(8) + memPutFloat(position + 0, x.toFloat()) + memPutFloat(position + 4, y.toFloat()) + position += 8 + } + + /** + * Writes a 3-component vector as three consecutive 4-byte floats + * @param x X-axis component + * @param y Y-axis component + * @param z Z-axis component + */ + fun putVec3(x: Double, y: Double, z: Double) { + require(12) + memPutFloat(position + 0, x.toFloat()) + memPutFloat(position + 4, y.toFloat()) + memPutFloat(position + 8, z.toFloat()) + position += 12 + } + + /** + * Writes a color as four consecutive bytes in RGBA format + * @param color The color to write + */ + fun putColor(color: Color) { + require(4) + memPutByte(position + 0, color.red.toByte()) + memPutByte(position + 1, color.green.toByte()) + memPutByte(position + 2, color.blue.toByte()) + memPutByte(position + 3, color.alpha.toByte()) + position += 4 + } + + /** + * Ensures the buffer has enough remaining space for the requested number of bytes. + * Automatically grows the buffer if insufficient space remains. + * @param size Number of bytes required for the next write operation + */ + fun require(size: Int) { + if (capacity - bytesPut > size) return + grow(capacity * 2) + } + + /** + * Increases buffer capacity while preserving existing data. New capacity must be greater than current. + * @param newCapacity New buffer capacity in bytes + * @throws IllegalArgumentException if newCapacity is not greater than current capacity + */ + fun grow(newCapacity: Int) { + check(newCapacity > capacity) { + "Cannot grow buffer beyond its capacity" + } + + val newBuffer = createByteBuffer(newCapacity) + val newPointer = memAddress0(newBuffer) + val offset = position - pointer + + memCopy(pointer, newPointer, offset) + data = newBuffer + + pointer = newPointer + position = newPointer + offset + capacity = newCapacity + } + + /** + * Reallocates the buffer with exact new capacity, resetting position and discarding existing data + * @param newCapacity New buffer capacity in bytes + */ + fun realloc(newCapacity: Int) { + if (newCapacity == capacity) { + resetPosition() + return + } + + val newBuffer = createByteBuffer(newCapacity) + val newPointer = memAddress0(newBuffer) + + data = newBuffer + pointer = newPointer + position = newPointer + capacity = newCapacity + } + + companion object { + /** + * Creates a new DynamicByteBuffer with specified initial capacity + * @param initialCapacity Starting buffer size in bytes + */ + fun dynamicByteBuffer(initialCapacity: Int) = + DynamicByteBuffer(initialCapacity) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt deleted file mode 100644 index 3bb15197e..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer - -import java.awt.Color - -interface IRenderContext { - fun vec3(x: Double, y: Double, z: Double): IRenderContext - fun vec2(x: Double, y: Double): IRenderContext - - fun vec3m(x: Double, y: Double, z: Double): IRenderContext - fun vec2m(x: Double, y: Double): IRenderContext - - fun float(v: Double): IRenderContext - fun color(color: Color): IRenderContext - fun end(): Int - - fun putLine(vertex1: Int, vertex2: Int) - fun putTriangle(vertex1: Int, vertex2: Int, vertex3: Int) - fun putQuad(vertex1: Int, vertex2: Int, vertex3: Int, vertex4: Int) - - fun render() - fun upload() - fun clear() - - fun immediateDraw() { - upload() - render() - clear() - } - - fun grow(amount: Int) - - fun use(block: IRenderContext.() -> Unit) { - block() - } -} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt deleted file mode 100644 index c8be117bf..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer - -import com.lambda.Lambda.LOG -import com.lambda.graphics.buffer.vertex.ElementBuffer -import com.lambda.graphics.buffer.vertex.VertexArray -import com.lambda.graphics.buffer.vertex.VertexBuffer -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.gl.Matrices -import com.lambda.graphics.gl.Memory -import com.lambda.graphics.gl.Memory.address -import com.lambda.graphics.gl.Memory.byteBuffer -import com.lambda.graphics.gl.Memory.int -import com.lambda.graphics.gl.Memory.vector2f -import com.lambda.graphics.gl.Memory.vector3f -import com.lambda.graphics.gl.kibibyte -import org.joml.Vector4d -import org.lwjgl.opengl.GL15C -import org.lwjgl.opengl.GL20C.* -import java.awt.Color - -class VertexPipeline( - private val mode: VertexMode, - attributes: VertexAttrib.Group, - usage: Int = GL_DYNAMIC_DRAW -) : IRenderContext { - private val stride = attributes.stride - private val size = stride * mode.indicesCount - - private val vao = VertexArray() - private val vbo = VertexBuffer(mode, attributes, usage) - private val ebo = ElementBuffer(mode, usage) - - private var vertices = byteBuffer(size * 1.kibibyte) - private var verticesPointer = address(vertices) - private var verticesPosition = verticesPointer - - private var indices = byteBuffer(mode.indicesCount * 2.kibibyte) - private var indicesPointer = address(indices) - private var indicesCount = 0 - private var uploadedIndices = 0 - - private var vertexIndex = 0 - - override fun vec3(x: Double, y: Double, z: Double): VertexPipeline { - verticesPosition += vector3f(verticesPosition, x, y, z) - return this - } - - override fun vec2(x: Double, y: Double): VertexPipeline { - verticesPosition += vector2f(verticesPosition, x, y) - return this - } - - override fun vec3m(x: Double, y: Double, z: Double): IRenderContext { - Matrices.vertexTransformer?.let { mat -> - val vec = Vector4d(x, y, z, 1.0).apply(mat::transform) - vec3(vec.x, vec.y, vec.z) - } ?: vec3(x, y, z) - - return this - } - - override fun vec2m(x: Double, y: Double): IRenderContext { - Matrices.vertexTransformer?.let { mat -> - val vec = Vector4d(x, y, 0.0, 1.0).apply(mat::transform) - vec2(vec.x, vec.y) - } ?: vec2(x, y) - return this - } - - override fun float(v: Double): VertexPipeline { - verticesPosition += Memory.float(verticesPosition, v) - return this - } - - override fun color(color: Color): VertexPipeline { - verticesPosition += Memory.color(verticesPosition, color) - return this - } - - override fun end() = vertexIndex++ - - override fun putLine(vertex1: Int, vertex2: Int) { - growIndices(2) - val p = indicesPointer + indicesCount * 4L - - int(p + 0, vertex1) - int(p + 4, vertex2) - indicesCount += 2 - } - - override fun putTriangle(vertex1: Int, vertex2: Int, vertex3: Int) { - growIndices(3) - val position = indicesPointer + indicesCount * 4L - - int(position + 0, vertex1) - int(position + 4, vertex2) - int(position + 8, vertex3) - indicesCount += 3 - } - - override fun putQuad(vertex1: Int, vertex2: Int, vertex3: Int, vertex4: Int) { - growIndices(6) - val position = indicesPointer + indicesCount * 4L - - int(position + 0, vertex1) - int(position + 4, vertex2) - int(position + 8, vertex3) - int(position + 12, vertex3) - int(position + 16, vertex4) - int(position + 20, vertex1) - indicesCount += 6 - } - - override fun grow(amount: Int) { - val requiredCapacity = (vertexIndex + amount + 1) * size - if (requiredCapacity < vertices.capacity()) return - - val offset = verticesPosition - verticesPointer - val newSize = vertices.capacity().let { it * 2 + it % size } - - val newVertices = byteBuffer(newSize) - Memory.copy(address(vertices), address(newVertices), offset) - - vertices = newVertices - verticesPointer = address(vertices) - verticesPosition = verticesPointer + offset - } - - private fun growIndices(amount: Int) { - val requiredCapacity = (indicesCount + amount) * 4 - if (requiredCapacity < indices.capacity()) return - - val newSize = indices.capacity().let { it * 2 + it % (mode.indicesCount * 4) } - val newIndices = byteBuffer(newSize) - - Memory.copy(address(indices), address(newIndices), indicesCount * 4L) - - indices = newIndices - indicesPointer = address(indices) - } - - override fun render() { - if (uploadedIndices <= 0) return - - vao.bind() - glDrawElements(mode.gl, uploadedIndices, GL_UNSIGNED_INT, 0) - vao.bind(0) - } - - override fun upload() { - if (indicesCount <= 0) return - - val vboData = vertices.limit((verticesPosition - verticesPointer).toInt()) - val eboData = indices.limit(indicesCount * 4) - - vbo.upload(vboData, 0)?.let(LOG::error) - ebo.upload(eboData, 0)?.let(LOG::error) - - uploadedIndices = indicesCount - } - - override fun clear() { - verticesPosition = verticesPointer - vertexIndex = 0 - indicesCount = 0 - uploadedIndices = 0 - } - - init { - // All the buffers have been generated, all we have to - // do now it bind them correctly and populate them - vao.bind() - vbo.bind() - ebo.bind() - - // Populate the buffers - attributes.attributes - .foldIndexed(0L) { index, pointer, attrib -> - glEnableVertexAttribArray(index) - glVertexAttribPointer(index, attrib.componentCount, attrib.gl, attrib.normalized, stride, pointer) - - pointer + attrib.size // I'm not sure why there's no accumulator indexed iterator, this cost me many hours - } - - // Unbind everything to avoid accidental modification - vao.bind(0) - vbo.bind(0) - ebo.bind(0) - } -} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt index d340b524c..468225cb7 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt @@ -18,13 +18,10 @@ package com.lambda.graphics.buffer.frame import com.lambda.Lambda.mc -import com.lambda.graphics.RenderMain.projectionMatrix -import com.lambda.graphics.buffer.VertexPipeline +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.gl.Matrices import com.lambda.graphics.texture.TextureUtils -import org.joml.Matrix4f import org.lwjgl.opengl.GL12C.GL_CLAMP_TO_EDGE import org.lwjgl.opengl.GL30C.* import java.nio.IntBuffer diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt deleted file mode 100644 index 466473a81..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.frame - -import com.lambda.Lambda.mc -import com.lambda.graphics.RenderMain -import com.lambda.graphics.gl.GlStateUtils.withBlendFunc -import com.lambda.graphics.shader.Shader -import com.lambda.util.math.Vec2d -import org.lwjgl.opengl.GL11C.* - -class ScreenFrameBuffer(depth: Boolean = false) : FrameBuffer(depth = depth) { - fun read( - shader: Shader, - pos1: Vec2d = Vec2d.ZERO, - pos2: Vec2d = RenderMain.screenSize, - shaderBlock: (Shader) -> Unit = {} - ): ScreenFrameBuffer { - bindColorTexture() - - shader.use() - shaderBlock(shader) - - pipeline.use { - grow(4) - - val uv1 = pos1 / RenderMain.screenSize - val uv2 = pos2 / RenderMain.screenSize - - putQuad( - vec2(pos1.x, pos1.y).vec2(uv1.x, 1.0 - uv1.y).end(), - vec2(pos1.x, pos2.y).vec2(uv1.x, 1.0 - uv2.y).end(), - vec2(pos2.x, pos2.y).vec2(uv2.x, 1.0 - uv2.y).end(), - vec2(pos2.x, pos1.y).vec2(uv2.x, 1.0 - uv1.y).end() - ) - } - - withBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) { - pipeline.upload() - pipeline.render() - pipeline.clear() - } - - return this - } - - override fun write(block: () -> Unit): ScreenFrameBuffer { - width = mc.window.framebufferWidth - height = mc.window.framebufferHeight - - return super.write { - withBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, block) - } as ScreenFrameBuffer - } - - override fun bindColorTexture(slot: Int) = - super.bindColorTexture(slot) as ScreenFrameBuffer - - override fun bindDepthTexture(slot: Int) = - super.bindDepthTexture(slot) as ScreenFrameBuffer -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt deleted file mode 100644 index 116c16a85..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.vertex - -import com.lambda.graphics.buffer.Buffer -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.gl.kibibyte -import org.lwjgl.opengl.GL30C.* -import java.nio.ByteBuffer - -class ElementBuffer( - mode: VertexMode, - override val usage: Int = GL_DYNAMIC_DRAW, -) : Buffer(buffers = 1) { - override val target: Int = GL_ELEMENT_ARRAY_BUFFER - override val access: Int = GL_MAP_WRITE_BIT - - override fun upload( - data: ByteBuffer, - offset: Long, - ): Throwable? = allocate(data) - - override fun bind(id: Int) { - if (id != 0) prevIbo = lastIbo - val target = if (id != 0) id else prevIbo - - super.bind(target) - } - - init { - allocate(mode.indicesCount * 2L.kibibyte) - } - - companion object { - @JvmField - var lastIbo = 0 - var prevIbo = 0 - } -} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt index 5362c6ad4..bd526ec7c 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt @@ -18,15 +18,37 @@ package com.lambda.graphics.buffer.vertex import com.lambda.graphics.buffer.Buffer +import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib +import com.lambda.graphics.buffer.vertex.attributes.VertexMode import net.minecraft.client.render.BufferRenderer import org.lwjgl.opengl.GL30C.* +import org.lwjgl.opengl.GL32C.glDrawElementsBaseVertex import java.nio.ByteBuffer -class VertexArray : Buffer(isVertexArray = true) { +class VertexArray( + private val vertexMode: VertexMode, + private val attributes: VertexAttrib.Group +) : Buffer(isVertexArray = true) { override val usage: Int = -1 override val target: Int = -1 override val access: Int = -1 + fun render( + indicesSize: Long, + indicesPointer: Long, + verticesOffset: Int + ) { + bind() + glDrawElementsBaseVertex( + vertexMode.mode, + indicesSize.toInt() / UInt.SIZE_BYTES, + GL_UNSIGNED_INT, + indicesPointer, + verticesOffset / attributes.stride + ) + bind(0) + } + override fun map( size: Long, offset: Long, diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt deleted file mode 100644 index 94358fb09..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.vertex - -import com.lambda.graphics.buffer.Buffer -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.gl.kibibyte -import org.lwjgl.opengl.GL30C.* -import java.nio.ByteBuffer - -class VertexBuffer( - mode: VertexMode, - attributes: VertexAttrib.Group, - override val usage: Int = GL_DYNAMIC_DRAW -) : Buffer(buffers = 1) { - override val target: Int = GL_ARRAY_BUFFER - override val access: Int = GL_MAP_WRITE_BIT - - override fun upload( - data: ByteBuffer, - offset: Long, - ): Throwable? = allocate(data) - - init { - allocate(attributes.stride * mode.indicesCount * 1L.kibibyte) - } -} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt index d74ebaa2c..42014cfa6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt @@ -17,16 +17,17 @@ package com.lambda.graphics.buffer.vertex.attributes -import com.lambda.graphics.gl.GLObject import org.lwjgl.opengl.GL11C.GL_FLOAT import org.lwjgl.opengl.GL11C.GL_UNSIGNED_BYTE +import org.lwjgl.opengl.GL20C.glEnableVertexAttribArray +import org.lwjgl.opengl.GL20C.glVertexAttribPointer enum class VertexAttrib( - val componentCount: Int, + private val componentCount: Int, componentSize: Int, - val normalized: Boolean, - override val gl: Int -) : GLObject { + private val normalized: Boolean, + private val type: Int +) { Float(1, 4, false, GL_FLOAT), Vec2(2, 4, false, GL_FLOAT), Vec3(3, 4, false, GL_FLOAT), @@ -34,6 +35,11 @@ enum class VertexAttrib( val size = componentCount * componentSize + fun pointer(index: Int, pointer: Long, stride: Int) { + glEnableVertexAttribArray(index) + glVertexAttribPointer(index, componentCount, type, normalized, stride, pointer) + } + enum class Group(vararg val attributes: VertexAttrib) { POS_UV(Vec2, Vec2), @@ -75,6 +81,16 @@ enum class VertexAttrib( Color ); - val stride = attributes.sumOf { attribute -> attribute.size } + val stride = attributes.sumOf { attribute -> + attribute.size + } + + fun link() { + attributes.foldIndexed(0L) { index, pointer, attrib -> + attrib.pointer(index, pointer, stride) + pointer + attrib.size + } + } } } + diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexMode.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexMode.kt index e0c6c45df..668c97cd2 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexMode.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexMode.kt @@ -17,11 +17,10 @@ package com.lambda.graphics.buffer.vertex.attributes -import com.lambda.graphics.gl.GLObject import org.lwjgl.opengl.GL11C.GL_LINES import org.lwjgl.opengl.GL11C.GL_TRIANGLES -enum class VertexMode(val indicesCount: Int, override val gl: Int) : GLObject { - LINES(2, GL_LINES), - TRIANGLES(3, GL_TRIANGLES) +enum class VertexMode(val mode: Int) { + LINES(GL_LINES), + TRIANGLES(GL_TRIANGLES) } 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 c99fe6d68..efc87727a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt @@ -17,86 +17,8 @@ package com.lambda.graphics.gl -import org.lwjgl.BufferUtils -import org.lwjgl.system.MemoryUtil -import org.lwjgl.system.MemoryUtil.memPutByte -import org.lwjgl.system.MemoryUtil.memPutFloat -import org.lwjgl.system.MemoryUtil.memPutInt -import java.awt.Color -import java.nio.Buffer import java.nio.ByteBuffer -object Memory { - /** - * Puts a float for each axis at the current buffer position - */ - fun vector2f(address: Long, x: Double, y: Double): Int { - float(address + 0, x) - float(address + 4, y) - return 8 - } - - /** - * Puts a float for each axis at the current buffer position - */ - fun vector3f(address: Long, x: Double, y: Double, z: Double): Int { - float(address + 0, x) - float(address + 4, y) - float(address + 8, z) - return 12 - } - - /** - * Puts a byte for each color channel at the current buffer position - */ - fun color(address: Long, color: Color): Int { - byte(address + 0, color.red.toByte()) - byte(address + 1, color.green.toByte()) - byte(address + 2, color.blue.toByte()) - byte(address + 3, color.alpha.toByte()) - return 4 - } - - /** - * Puts a byte at the current buffer position - */ - fun byte(address: Long, value: Byte): Int { - memPutByte(address, value) - return 1 - } - - /** - * Puts an integer at the current buffer position - */ - fun int(address: Long, value: Int): Int { - memPutInt(address, value) - return 4 - } - - /** - * Puts a float at the current buffer position - */ - fun float(address: Long, value: Double): Int { - memPutFloat(address, value.toFloat()) - return 4 - } - - /** - * Returns the address of the first element within the buffer - */ - fun address(buffer: Buffer): Long = MemoryUtil.memAddress0(buffer) - - /** - * Copies [bytes] bytes from [src] to [dst] - */ - fun copy(src: Long, dst: Long, bytes: Long) = MemoryUtil.memCopy(src, dst, bytes) - - /** - * Creates a new buffer of [cap] bytes - */ - fun byteBuffer(cap: Int) = BufferUtils.createByteBuffer(cap) -} - val Int.kilobyte get() = this * 1000 val Int.megabyte get() = this * 1000 * 1000 val Int.gigabyte get() = this * 1000 * 1000 * 1000 diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt new file mode 100644 index 000000000..e0e5eec6b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.pipeline + +import com.lambda.graphics.buffer.Buffer.Companion.createPipelineBuffer +import com.lambda.graphics.buffer.DynamicByteBuffer.Companion.dynamicByteBuffer +import com.lambda.graphics.gl.kibibyte +import org.lwjgl.system.MemoryUtil.memCopy +import java.nio.ByteBuffer + +/** + * Represents a persistent dynamic coherent buffer for fast opengl rendering purposes + */ +class PersistentBuffer( + target: Int, stride: Int +) { + /** + * Resizable byte buffer that stores all data used last frame + */ + val byteBuffer = dynamicByteBuffer(stride * 1.kibibyte) + + /** + * Data that has passed through the buffer within previous frame + */ + private val snapshot = dynamicByteBuffer(1) + private var cacheSize = 0 + + /** + * Represents a gpu-side buffer + */ + private val glBuffer = createPipelineBuffer(target) + private var glSize = 0 + + var uploadOffset = 0 + + fun upload() { + val dataStart = byteBuffer.pointer + uploadOffset + val dataCount = byteBuffer.bytesPut - uploadOffset + + if (glSize != byteBuffer.capacity) { + glSize = byteBuffer.capacity + + glBuffer.allocate(byteBuffer.data) + snapshot.realloc(byteBuffer.capacity) + cacheSize = 0 + return + + /* TODO: + Cache data in range min(snapshot.capacity, byteBuffer.bytesPut) + and force upload after byteBuffer.bytesPut + */ + } else if (cacheSize > 0 && snapshot.capacity >= byteBuffer.bytesPut) { + // TODO: precise compare-mapping to minimize uploaded data + // Split data by chunks of modified regions and upload them only + // Might be useful in cases when position updates but uv/color/etc doesn't + // Might be not... + if (memcmp(snapshot.data, byteBuffer.data, uploadOffset, dataCount.toInt())) return + } + + glBuffer.update(uploadOffset.toLong(), dataCount, dataStart) + println(dataCount) + } + + fun end() { + uploadOffset = byteBuffer.bytesPut.toInt() + } + + fun sync() { + memCopy(byteBuffer.pointer, snapshot.pointer, byteBuffer.bytesPut) + cacheSize = byteBuffer.bytesPut.toInt() + + byteBuffer.resetPosition() + uploadOffset = 0 + } + + fun clear() { + snapshot.resetPosition() + byteBuffer.resetPosition() + uploadOffset = 0 + cacheSize = 0 + } + + fun use(block: () -> Unit) { + glBuffer.bind() + block() + glBuffer.bind(0) + } + + private fun memcmp(a: ByteBuffer, b: ByteBuffer, pointer: Int, size: Int): Boolean { + for (i in pointer..<(pointer + size)) { + if (a[i] != b[i]) { + return false + } + } + return true + } +} diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexBuilder.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexBuilder.kt new file mode 100644 index 000000000..aec8b8c12 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexBuilder.kt @@ -0,0 +1,267 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.pipeline + +import com.lambda.graphics.buffer.DynamicByteBuffer +import com.lambda.graphics.gl.Matrices +import org.joml.Vector4d + +/** + * A builder class for constructing vertex buffer objects (VBOs) with associated vertex attributes and indices. + * Provides a DSL-like syntax for defining vertices and their attributes in a type-safe manner. + */ +@Suppress("DuplicatedCode") +class VertexBuilder( + private val direct: VertexPipeline? = null +) { + private val vertices by lazy { mutableListOf() } + private val indices by lazy { mutableListOf() } + + private var verticesCounter = 0 + + /** + * Adds multiple indices to the index buffer + * @param indices The indices to add to the element array buffer + */ + fun build(vararg indices: Int) { + direct?.let { + indices.forEach { i -> + it.indices.putInt(i) + } + + return + } + + indices.forEach { this.indices += it } + } + + /** + * Adds rectangle indices (2 triangles) using 4 vertices + */ + fun buildQuad( + index1: Int, index2: Int, index3: Int, index4: Int + ) { + direct?.let { + it.indices.putInt(index1) + it.indices.putInt(index2) + it.indices.putInt(index3) + it.indices.putInt(index3) + it.indices.putInt(index4) + it.indices.putInt(index1) + return + } + + this.indices += index1 + this.indices += index2 + this.indices += index3 + this.indices += index3 + this.indices += index4 + this.indices += index1 + } + + /** + * Adds line indices between 2 vertices + */ + fun buildLine( + index1: Int, index2: Int, + ) { + direct?.let { + it.indices.putInt(index1) + it.indices.putInt(index2) + return + } + + this.indices += index1 + this.indices += index2 + } + + /** + * Creates a collection of indices from varargs + * @return List of provided indices for element array buffer + */ + fun collect(vararg indices: Int) = + indices + + fun use(block: VertexBuilder.() -> Unit) { + apply(block) + } + + /** + * Creates a new vertex with specified attributes + * @param block Configuration lambda for defining vertex attributes + * @return Index of the created vertex in the vertex array + */ + fun vertex(block: Vertex.() -> Unit): Int { + Vertex(this).apply(block) + return verticesCounter++ + } + + /** + * Uploads constructed vertex data to a rendering pipeline + * @param pipeline The target pipeline for vertex and index data + */ + fun uploadTo(pipeline: VertexPipeline) { + check(direct == null) { + "Builder is already associated with a rendering pipeline. Cannot upload data again." + } + + /* Upload vertices */ + vertices.forEach { attribute -> + attribute.upload(pipeline.vertices) + } + + /* Upload indices */ + indices.forEach { + pipeline.indices.putInt(it) + } + } + + /** + * Represents a single vertex with multiple attributes. + * Attributes are stored in the order they're declared, which must match shader layout. + */ + class Vertex(private val builder: VertexBuilder) { + + /** + * Adds a single-precision floating point attribute + * @param value The scalar value to add + */ + fun float(value: Double): Vertex { + builder.direct?.let { + it.vertices.putFloat(value) + return this + } + + builder.vertices.add(Attribute.Float(value)) + return this + } + + /** + * Adds a 2-component vector attribute + * @param x X-axis component + * @param y Y-axis component + */ + fun vec2(x: Double, y: Double): Vertex { + builder.direct?.let { + it.vertices.putVec2(x, y) + return this + } + + builder.vertices.add(Attribute.Vec2(x, y)) + return this + } + + /** + * Adds a matrix-transformed 2-component vector + * @param x X-axis component + * @param y Y-axis component + */ + fun vec2m(x: Double, y: Double) = + Matrices.vertexTransformer?.let { mat -> + val vec = Vector4d(x, y, 0.0, 1.0).apply(mat::transform) + vec2(vec.x, vec.y) + } ?: vec2(x, y) + + /** + * Adds a 3-component vector attribute + * @param x X-axis component + * @param y Y-axis component + * @param z Z-axis component + */ + fun vec3(x: Double, y: Double, z: Double): Vertex { + builder.direct?.let { + it.vertices.putVec3(x, y, z) + return this + } + + builder.vertices.add(Attribute.Vec3(x, y, z)) + return this + } + + /** + * Adds a matrix-transformed 3-component vector attribute + * @param x X-axis component + * @param y Y-axis component + * @param z Z-axis component (defaults to 0.0) + */ + fun vec3m(x: Double, y: Double, z: Double = 0.0) = + Matrices.vertexTransformer?.let { mat -> + val vec = Vector4d(x, y, z, 1.0).apply(mat::transform) + vec3(vec.x, vec.y, vec.z) + } ?: vec3(x, y, z) + + /** + * Adds a color attribute in RGBA format + * @param color Color value using AWT Color class + */ + fun color(color: java.awt.Color): Vertex { + builder.direct?.let { + it.vertices.putColor(color) + return this + } + + builder.vertices.add(Attribute.Color(color)) + return this + } + } + + /** + * Sealed hierarchy representing different vertex attribute types. + * Each attribute knows how to upload itself to a [DynamicByteBuffer]. + * + * @property upload Lambda that handles writing the attribute data to a buffer + */ + sealed class Attribute( + val upload: DynamicByteBuffer.() -> Unit + ) { + /** + * Single-precision floating point attribute + * @property value Scalar floating point value + */ + data class Float( + var value: Double + ) : Attribute({ putFloat(value) }) + + /** + * 2-component vector attribute + * @property x X-axis component + * @property y Y-axis component + */ + data class Vec2( + var x: Double, var y: Double + ) : Attribute({ putVec2(x, y) }) + + /** + * 3-component vector attribute + * @property x X-axis component + * @property y Y-axis component + * @property z Z-axis component + */ + data class Vec3( + var x: Double, var y: Double, var z: Double + ) : Attribute({ putVec3(x, y, z) }) + + /** + * Color attribute in RGBA format + * @property color Color value using AWT Color class + */ + data class Color( + var color: java.awt.Color + ) : Attribute({ putColor(color) }) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt new file mode 100644 index 000000000..565c466ea --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.pipeline + +import com.lambda.graphics.buffer.vertex.VertexArray +import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib +import com.lambda.graphics.buffer.vertex.attributes.VertexMode +import org.lwjgl.opengl.GL32C.* + +/** + * A GPU vertex processing pipeline that manages Vertex Array Objects (VAO) and associated buffers. + * Handles vertex data storage, attribute binding, and rendering operations. + * + * @property vertexMode The primitive type used for rendering (e.g., triangles, lines) + * @property attributes Group of vertex attributes defining the data layout + * + * @see VertexMode for vertex configuration + * @see VertexAttrib.Group for attribute configuration + * @see PersistentBuffer for buffer management + */ +class VertexPipeline( + private val vertexMode: VertexMode, + private val attributes: VertexAttrib.Group +) { + private val vao = VertexArray(vertexMode, attributes) + private val vbo = PersistentBuffer(GL_ARRAY_BUFFER, attributes.stride) + private val ibo = PersistentBuffer(GL_ELEMENT_ARRAY_BUFFER, 4) + + /** + * Direct access to the vertex buffer's underlying byte storage + */ + val vertices get() = vbo.byteBuffer + + /** + * Direct access to the index buffer's underlying byte storage + */ + val indices get() = ibo.byteBuffer + + /** + * Submits a draw call to the GPU using currently uploaded data + * Binds VAO and issues glDrawElementsBaseVertex command + */ + fun render() = vao.render( + indicesSize = indices.bytesPut - ibo.uploadOffset, + indicesPointer = indices.pointer + ibo.uploadOffset, + verticesOffset = vbo.uploadOffset + ) + + /** + * Builds and renders data constructed by [VertexBuilder] + * + * It is recommended to use this method for direct data transfer + * to avoid the overhead caused by [VertexBuilder] + * + * Uploads buffered data to GPU memory + * + * Note: only one vertex builder could be built and uploaded within 1 batch + */ + fun immediate(block: VertexBuilder.() -> Unit) { + VertexBuilder(this).apply(block) + upload(); render(); clear() + } + + /** + * Builds data constructed by [VertexBuilder] + * + * It is recommended to use this method for direct data transfer + * to avoid the overhead caused by [VertexBuilder] + * + * Uploads buffered data to GPU memory + * + * Note: only one vertex builder could be built and uploaded within 1 batch + */ + fun upload(block: VertexBuilder.() -> Unit) { + VertexBuilder(this).apply(block) + upload() + } + + /** + * Builds data constructed by [VertexBuilder] + * + * Uploads buffered data to GPU memory + * + * Note: only one vertex builder could be built and uploaded within 1 batch + */ + fun upload(builder: VertexBuilder) { + builder.uploadTo(this) + upload() + } + + /** + * Creates a [VertexBuilder] + * + * Note: only one vertex builder could be built and uploaded within 1 batch + */ + fun build(block: VertexBuilder.() -> Unit = {}) = + VertexBuilder().apply(block) + + /** + * Uploads buffered data to GPU memory + */ + fun upload() { + vbo.upload() + ibo.upload() + } + + /** + * Finalizes the current draw batch and prepares for new data + */ + fun end() { + vbo.end() + ibo.end() + } + + /** + * Synchronizes buffer states between frames + * Should be called at the end of each frame + */ + fun sync() { + vbo.sync() + ibo.sync() + } + + /** + * Resets both vertex and index buffers + */ + fun clear() { + vbo.clear() + ibo.clear() + } + + init { + vao.bind() + vbo.use(attributes::link) + vao.bind(0) + } +} diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/ScissorAdapter.kt similarity index 96% rename from common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/ScissorAdapter.kt index dc87664b9..72ae91df8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/ScissorAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.lambda.graphics.pipeline +package com.lambda.graphics.renderer import com.lambda.Lambda.mc import com.lambda.graphics.RenderMain diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index 98ce0937a..fb19d147b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -118,7 +118,7 @@ class ChunkedESP private constructor( } suspend fun rebuild() { - val newRenderer = awaitMainThread { StaticESPRenderer(false) } + val newRenderer = awaitMainThread { StaticESPRenderer() } iterateChunk { x, y, z -> owner.update(newRenderer, chunk.world, x, y, z) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt index f5bb7a4cd..059b42692 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt @@ -18,10 +18,11 @@ package com.lambda.graphics.renderer.esp import com.lambda.Lambda -import com.lambda.graphics.buffer.VertexPipeline +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.gl.GlStateUtils +import com.lambda.graphics.pipeline.VertexBuilder import com.lambda.graphics.shader.Shader import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.module.modules.client.RenderSettings @@ -29,20 +30,27 @@ import com.lambda.util.extension.partialTicks open class ESPRenderer(tickedMode: Boolean) { val shader: Shader + val faces: VertexPipeline + var faceBuilder: VertexBuilder + val outlines: VertexPipeline + var outlineBuilder: VertexBuilder init { val mode = if (tickedMode) dynamicMode else staticMode - shader = mode.first + faces = VertexPipeline(VertexMode.TRIANGLES, mode.second) + faceBuilder = faces.build() + outlines = VertexPipeline(VertexMode.LINES, mode.second) + outlineBuilder = outlines.build() } - open fun upload() { - faces.upload() - outlines.upload() + fun upload() { + faces.upload(faceBuilder) + outlines.upload(outlineBuilder) } fun render() { @@ -54,9 +62,11 @@ open class ESPRenderer(tickedMode: Boolean) { GlStateUtils.withLineWidth(RenderSettings.outlineWidth, outlines::render) } - open fun clear() { + fun clear() { faces.clear() outlines.clear() + faceBuilder = faces.build() + outlineBuilder = outlines.build() } companion object { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt index 749648149..43675a1c1 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/DynamicESPBuilders.kt @@ -40,31 +40,28 @@ fun DynamicESPRenderer.buildFilled( box: DynamicAABB, color: Color, sides: Int = DirectionMask.ALL -) = faces.use { +) = faceBuilder.use { val boxes = box.getBoxPair() ?: return@use - val pos11 = boxes.first.min val pos12 = boxes.first.max val pos21 = boxes.second.min val pos22 = boxes.second.max - grow(8) - - val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } - val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } - val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } - val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } - val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } - val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } - val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } - val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } - - if (sides.hasDirection(DirectionMask.EAST)) putQuad(brb, trb, trf, brf) - 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, tlb, trb, brb) + val blb by lazy { vertex { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color) } } + val blf by lazy { vertex { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color) } } + val brb by lazy { vertex { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color) } } + val brf by lazy { vertex { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color) } } + val tlb by lazy { vertex { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color) } } + val tlf by lazy { vertex { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color) } } + val trb by lazy { vertex { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color) } } + val trf by lazy { vertex { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color) } } + + if (sides.hasDirection(DirectionMask.EAST)) buildQuad(brb, trb, trf, brf) + if (sides.hasDirection(DirectionMask.WEST)) buildQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(DirectionMask.UP)) buildQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DirectionMask.DOWN)) buildQuad(blb, brb, brf, blf) + if (sides.hasDirection(DirectionMask.SOUTH)) buildQuad(blf, brf, trf, tlf) + if (sides.hasDirection(DirectionMask.NORTH)) buildQuad(blb, tlb, trb, brb) } fun DynamicESPRenderer.buildOutline( @@ -72,7 +69,7 @@ fun DynamicESPRenderer.buildOutline( color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR -) = outlines.use { +) = outlineBuilder.use { val boxes = box.getBoxPair() ?: return@use val pos11 = boxes.first.min @@ -80,16 +77,14 @@ fun DynamicESPRenderer.buildOutline( val pos21 = boxes.second.min val pos22 = boxes.second.max - grow(8) - - val blb by lazy { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color).end() } - val blf by lazy { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color).end() } - val brb by lazy { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color).end() } - val brf by lazy { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color).end() } - val tlb by lazy { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color).end() } - val tlf by lazy { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color).end() } - val trb by lazy { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color).end() } - val trf by lazy { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color).end() } + val blb by lazy { vertex { vec3(pos11.x, pos11.y, pos11.z).vec3(pos21.x, pos21.y, pos21.z).color(color) } } + val blf by lazy { vertex { vec3(pos11.x, pos11.y, pos12.z).vec3(pos21.x, pos21.y, pos22.z).color(color) } } + val brb by lazy { vertex { vec3(pos12.x, pos11.y, pos11.z).vec3(pos22.x, pos21.y, pos21.z).color(color) } } + val brf by lazy { vertex { vec3(pos12.x, pos11.y, pos12.z).vec3(pos22.x, pos21.y, pos22.z).color(color) } } + val tlb by lazy { vertex { vec3(pos11.x, pos12.y, pos11.z).vec3(pos21.x, pos22.y, pos21.z).color(color) } } + val tlf by lazy { vertex { vec3(pos11.x, pos12.y, pos12.z).vec3(pos21.x, pos22.y, pos22.z).color(color) } } + val trb by lazy { vertex { vec3(pos12.x, pos12.y, pos11.z).vec3(pos22.x, pos22.y, pos21.z).color(color) } } + val trf by lazy { vertex { vec3(pos12.x, pos12.y, pos12.z).vec3(pos22.x, pos22.y, pos22.z).color(color) } } val hasEast = sides.hasDirection(DirectionMask.EAST) val hasWest = sides.hasDirection(DirectionMask.WEST) @@ -98,18 +93,18 @@ fun DynamicESPRenderer.buildOutline( val hasSouth = sides.hasDirection(DirectionMask.SOUTH) val hasNorth = sides.hasDirection(DirectionMask.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(hasUp, hasNorth)) buildLine(tlb, trb) + if (outlineMode.check(hasUp, hasSouth)) buildLine(tlf, trf) + if (outlineMode.check(hasUp, hasWest)) buildLine(tlb, tlf) + if (outlineMode.check(hasUp, hasEast)) buildLine(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(hasDown, hasNorth)) buildLine(blb, brb) + if (outlineMode.check(hasDown, hasSouth)) buildLine(blf, brf) + if (outlineMode.check(hasDown, hasWest)) buildLine(blb, blf) + if (outlineMode.check(hasDown, hasEast)) buildLine(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) + if (outlineMode.check(hasWest, hasNorth)) buildLine(tlb, blb) + if (outlineMode.check(hasNorth, hasEast)) buildLine(trb, brb) + if (outlineMode.check(hasEast, hasSouth)) buildLine(trf, brf) + if (outlineMode.check(hasSouth, hasWest)) buildLine(tlf, blf) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt index 8e70716f3..e82818916 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/builders/StaticESPBuilders.kt @@ -110,7 +110,9 @@ fun StaticESPRenderer.buildFilled( box: Box, color: Color, sides: Int = DirectionMask.ALL -) = buildFilled(box, color, color, sides) +) { + buildFilled(box, color, color, sides) +} fun StaticESPRenderer.buildOutlineShape( pos: BlockPos, @@ -148,36 +150,35 @@ fun StaticESPRenderer.buildOutline( color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR -) = buildOutline(box, color, color, sides, outlineMode) +) { + buildOutline(box, color, color, sides, outlineMode) +} fun StaticESPRenderer.buildFilled( box: Box, colorBottom: Color, colorTop: Color = colorBottom, sides: Int = DirectionMask.ALL -) = faces.use { +) = faceBuilder.use { val pos1 = box.min val pos2 = box.max - grow(8) - - val blb by vertex(faceVertices, pos1.x, pos1.y, pos1.z, colorBottom) - val blf by vertex(faceVertices, pos1.x, pos1.y, pos2.z, colorBottom) - val brb by vertex(faceVertices, pos2.x, pos1.y, pos1.z, colorBottom) - val brf by vertex(faceVertices, pos2.x, pos1.y, pos2.z, colorBottom) - val tlb by vertex(faceVertices, pos1.x, pos2.y, pos1.z, colorTop) - val tlf by vertex(faceVertices, pos1.x, pos2.y, pos2.z, colorTop) - val trb by vertex(faceVertices, pos2.x, pos2.y, pos1.z, colorTop) - val trf by vertex(faceVertices, pos2.x, pos2.y, pos2.z, colorTop) - - if (sides.hasDirection(DirectionMask.EAST)) putQuad(brb, trb, trf, brf) - 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, tlb, trb, brb) - - updateFaces = true + val blb by lazy { vertex { vec3(pos1.x, pos1.y, pos1.z).color(colorBottom) } } + val blf by lazy { vertex { vec3(pos1.x, pos1.y, pos2.z).color(colorBottom) } } + val brb by lazy { vertex { vec3(pos2.x, pos1.y, pos1.z).color(colorBottom) } } + val brf by lazy { vertex { vec3(pos2.x, pos1.y, pos2.z).color(colorBottom) } } + + val tlb by lazy { vertex { vec3(pos1.x, pos2.y, pos1.z).color(colorTop) } } + val tlf by lazy { vertex { vec3(pos1.x, pos2.y, pos2.z).color(colorTop) } } + val trb by lazy { vertex { vec3(pos2.x, pos2.y, pos1.z).color(colorTop) } } + val trf by lazy { vertex { vec3(pos2.x, pos2.y, pos2.z).color(colorTop) } } + + if (sides.hasDirection(DirectionMask.EAST)) buildQuad(brb, trb, trf, brf) + if (sides.hasDirection(DirectionMask.WEST)) buildQuad(blb, blf, tlf, tlb) + if (sides.hasDirection(DirectionMask.UP)) buildQuad(tlb, tlf, trf, trb) + if (sides.hasDirection(DirectionMask.DOWN)) buildQuad(blb, brb, brf, blf) + if (sides.hasDirection(DirectionMask.SOUTH)) buildQuad(blf, brf, trf, tlf) + if (sides.hasDirection(DirectionMask.NORTH)) buildQuad(blb, tlb, trb, brb) } fun StaticESPRenderer.buildOutline( @@ -186,20 +187,18 @@ fun StaticESPRenderer.buildOutline( colorTop: Color = colorBottom, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR -) = outlines.use { +) = outlineBuilder.use { val pos1 = box.min val pos2 = box.max - grow(8) - - val blb by vertex(outlineVertices, pos1.x, pos1.y, pos1.z, colorBottom) - val blf by vertex(outlineVertices, pos1.x, pos1.y, pos2.z, colorBottom) - val brb by vertex(outlineVertices, pos2.x, pos1.y, pos1.z, colorBottom) - val brf by vertex(outlineVertices, pos2.x, pos1.y, pos2.z, colorBottom) - val tlb by vertex(outlineVertices, pos1.x, pos2.y, pos1.z, colorTop) - val tlf by vertex(outlineVertices, pos1.x, pos2.y, pos2.z, colorTop) - val trb by vertex(outlineVertices, pos2.x, pos2.y, pos1.z, colorTop) - val trf by vertex(outlineVertices, pos2.x, pos2.y, pos2.z, colorTop) + val blb by lazy { vertex { vec3(pos1.x, pos1.y, pos1.z).color(colorBottom) } } + val blf by lazy { vertex { vec3(pos1.x, pos1.y, pos2.z).color(colorBottom) } } + val brb by lazy { vertex { vec3(pos2.x, pos1.y, pos1.z).color(colorBottom) } } + val brf by lazy { vertex { vec3(pos2.x, pos1.y, pos2.z).color(colorBottom) } } + val tlb by lazy { vertex { vec3(pos1.x, pos2.y, pos1.z).color(colorTop) } } + val tlf by lazy { vertex { vec3(pos1.x, pos2.y, pos2.z).color(colorTop) } } + val trb by lazy { vertex { vec3(pos2.x, pos2.y, pos1.z).color(colorTop) } } + val trf by lazy { vertex { vec3(pos2.x, pos2.y, pos2.z).color(colorTop) } } val hasEast = sides.hasDirection(DirectionMask.EAST) val hasWest = sides.hasDirection(DirectionMask.WEST) @@ -208,20 +207,18 @@ fun StaticESPRenderer.buildOutline( val hasSouth = sides.hasDirection(DirectionMask.SOUTH) val hasNorth = sides.hasDirection(DirectionMask.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(hasUp, hasNorth)) buildLine(tlb, trb) + if (outlineMode.check(hasUp, hasSouth)) buildLine(tlf, trf) + if (outlineMode.check(hasUp, hasWest)) buildLine(tlb, tlf) + if (outlineMode.check(hasUp, hasEast)) buildLine(trf, trb) - 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) + if (outlineMode.check(hasDown, hasNorth)) buildLine(blb, brb) + if (outlineMode.check(hasDown, hasSouth)) buildLine(blf, brf) + if (outlineMode.check(hasDown, hasWest)) buildLine(blb, blf) + if (outlineMode.check(hasDown, hasEast)) buildLine(brb, brf) - updateOutlines = true + if (outlineMode.check(hasWest, hasNorth)) buildLine(tlb, blb) + if (outlineMode.check(hasNorth, hasEast)) buildLine(trb, brb) + if (outlineMode.check(hasEast, hasSouth)) buildLine(trf, brf) + if (outlineMode.check(hasSouth, hasWest)) buildLine(tlf, blf) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt index ceea2084f..9c713562e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt @@ -21,6 +21,7 @@ import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.ESPRenderer import com.lambda.graphics.renderer.esp.impl.DynamicESPRenderer object DynamicESP : DynamicESPRenderer() { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt index ce00e73c3..237650fa0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt @@ -21,9 +21,10 @@ import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.ESPRenderer import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer -object StaticESP : StaticESPRenderer(false) { +object StaticESP : StaticESPRenderer() { init { listen { clear() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/DynamicESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/DynamicESPRenderer.kt index 34edd1ed5..9ba314bdd 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/DynamicESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/DynamicESPRenderer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt index 10fa1349b..d3d48deea 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/impl/StaticESPRenderer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,46 +17,6 @@ package com.lambda.graphics.renderer.esp.impl -import com.lambda.graphics.buffer.IRenderContext import com.lambda.graphics.renderer.esp.ESPRenderer -import java.awt.Color -import java.util.concurrent.ConcurrentHashMap -open class StaticESPRenderer(private val useVertexCaching: Boolean = true) : ESPRenderer(false) { - val faceVertices = ConcurrentHashMap() - val outlineVertices = ConcurrentHashMap() - - var updateFaces = false - var updateOutlines = false - - override fun upload() { - if (updateFaces) { - updateFaces = false - faces.upload() - } - - if (updateOutlines) { - updateOutlines = false - outlines.upload() - } - } - - override fun clear() { - faceVertices.clear() - outlineVertices.clear() - super.clear() - } - - fun IRenderContext.vertex( - storage: MutableMap, - x: Double, y: Double, z: Double, - color: Color - ) = lazy { - val vtx = { vec3(x, y, z).color(color).end() } - if (!useVertexCaching) return@lazy vtx() - - storage.getOrPut(Vertex(x, y, z, color), vtx) - } - - data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color) -} +open class StaticESPRenderer : ESPRenderer(false) \ 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 8d41cbd6f..cf4fe7153 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 @@ -17,14 +17,17 @@ package com.lambda.graphics.renderer.gui +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.RenderMain -import com.lambda.graphics.buffer.VertexPipeline +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.shader.Shader import com.lambda.module.modules.client.GuiSettings import com.lambda.module.modules.client.GuiSettings.primaryColor import com.lambda.module.modules.client.GuiSettings.secondaryColor +import com.lambda.module.modules.client.RenderSettings import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d import org.lwjgl.glfw.GLFW @@ -34,12 +37,22 @@ abstract class AbstractGUIRenderer( val shader: Shader ) { private val pipeline = VertexPipeline(VertexMode.TRIANGLES, attribGroup) + private var memoryMapping = true + + init { + listen(alwaysListen = true) { + memoryMapping = RenderSettings.useMemoryMapping + } + + listen(alwaysListen = true) { + if (memoryMapping) pipeline.sync() + } + } protected fun render( shade: Boolean = false, block: VertexPipeline.() -> Unit ) { - pipeline.clear() shader.use() block(pipeline) @@ -53,7 +66,9 @@ abstract class AbstractGUIRenderer( shader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) } - pipeline.upload() - pipeline.render() + pipeline.apply { + render() + if (memoryMapping) end() else clear() + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index e746ce1e6..0059aa2f4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -18,7 +18,7 @@ package com.lambda.graphics.renderer.gui import com.lambda.graphics.RenderMain -import com.lambda.graphics.buffer.VertexPipeline +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.shader.Shader.Companion.shader @@ -61,17 +61,25 @@ object TextureRenderer { val pos1 = rect.leftTop val pos2 = rect.rightBottom - pipeline.use { - grow(4) - - putQuad( - vec2(pos1.x, pos1.y).vec2(0.0, 0.0).end(), - vec2(pos1.x, pos2.y).vec2(0.0, 1.0).end(), - vec2(pos2.x, pos2.y).vec2(1.0, 1.0).end(), - vec2(pos2.x, pos1.y).vec2(1.0, 0.0).end() + pipeline.immediate { + buildQuad( + vertex { + vec2(pos1.x, pos1.y) + vec2(0.0, 0.0) + }, + vertex { + vec2(pos1.x, pos2.y) + vec2(0.0, 1.0) + }, + vertex { + vec2(pos2.x, pos2.y) + vec2(1.0, 1.0) + }, + vertex { + vec2(pos2.x, pos1.y) + vec2(1.0, 0.0) + } ) } - - pipeline.immediateDraw() } } 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 64cadd647..a4684da4d 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 @@ -17,8 +17,8 @@ package com.lambda.graphics.renderer.gui.font -import com.lambda.graphics.buffer.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib +import com.lambda.graphics.pipeline.VertexBuilder import com.lambda.graphics.renderer.gui.AbstractGUIRenderer import com.lambda.graphics.renderer.gui.font.core.GlyphInfo import com.lambda.graphics.renderer.gui.font.core.LambdaAtlas.get @@ -71,8 +71,10 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("rende bind(chars, emojis) - processText(text, color, scale, shadow, parseEmoji) { char, pos1, pos2, col, _ -> - buildGlyph(char, position, pos1, pos2, col) + upload { + processText(text, color, scale, shadow, parseEmoji) { char, pos1, pos2, col, _ -> + buildGlyph(char, position, pos1, pos2, col) + } } } @@ -104,7 +106,9 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("rende val pos1 = Vec2d(0.0, posY) * actualScale val pos2 = pos1 + scaledSize - buildGlyph(glyph, position, pos1, pos2, color) + upload { + buildGlyph(glyph, position, pos1, pos2, color) + } } /** @@ -116,7 +120,7 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("rende * @param pos2 The end position of the glyph * @param color The color of the glyph. */ - private fun VertexPipeline.buildGlyph( + private fun VertexBuilder.buildGlyph( glyph: GlyphInfo, origin: Vec2d = Vec2d.ZERO, pos1: Vec2d, @@ -128,13 +132,19 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("rende val x2 = pos2.x + origin.x val y2 = pos2.y + origin.y - grow(4) - - putQuad( - vec3m(x1, y1, 0.0).vec2(glyph.uv1.x, glyph.uv1.y).color(color).end(), - vec3m(x1, y2, 0.0).vec2(glyph.uv1.x, glyph.uv2.y).color(color).end(), - vec3m(x2, y2, 0.0).vec2(glyph.uv2.x, glyph.uv2.y).color(color).end(), - vec3m(x2, y1, 0.0).vec2(glyph.uv2.x, glyph.uv1.y).color(color).end() + buildQuad( + vertex { + vec3m(x1, y1).vec2(glyph.uv1.x, glyph.uv1.y).color(color) + }, + vertex { + vec3m(x1, y2).vec2(glyph.uv1.x, glyph.uv2.y).color(color) + }, + vertex { + vec3m(x2, y2).vec2(glyph.uv2.x, glyph.uv2.y).color(color) + }, + vertex { + vec3m(x2, y1).vec2(glyph.uv2.x, glyph.uv1.y).color(color) + } ) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt index 4a71b46ab..238cb2943 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt @@ -37,22 +37,27 @@ class DistanceFieldTexture(image: BufferedImage) : Texture(image, levels = 0) { private val shader = shader("post/sdf") private val frame = CachedFrame(width, height).write { - FrameBuffer.pipeline.use { + FrameBuffer.pipeline.immediate { val (pos1, pos2) = Vec2d.ZERO to Vec2d(width, height) - grow(4) - putQuad( - vec2(pos1.x, pos1.y).vec2(0.0, 1.0).end(), - vec2(pos1.x, pos2.y).vec2(0.0, 0.0).end(), - vec2(pos2.x, pos2.y).vec2(1.0, 0.0).end(), - vec2(pos2.x, pos1.y).vec2(1.0, 1.0).end() + buildQuad( + vertex { + vec2(pos1.x, pos1.y).vec2(0.0, 1.0) + }, + vertex { + vec2(pos1.x, pos2.y).vec2(0.0, 0.0) + }, + vertex { + vec2(pos2.x, pos2.y).vec2(1.0, 0.0) + }, + vertex { + vec2(pos2.x, pos1.y).vec2(1.0, 1.0) + } ) shader.use() shader["u_TexelSize"] = Vec2d.ONE / pos2 super.bind(0) - - immediateDraw() } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index 7583fff98..27af0d467 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -27,8 +27,6 @@ import kotlin.math.min object FilledRectRenderer : AbstractGUIRenderer( VertexAttrib.Group.RECT_FILLED, shader("renderer/rect_filled") ) { - private const val MIN_SIZE = 0.5 - private const val MIN_ALPHA = 1 private const val EXPAND = 0.35 fun filledRect( @@ -85,14 +83,6 @@ object FilledRectRenderer : AbstractGUIRenderer( val size = pos2 - pos1 - if (leftTop.alpha < MIN_ALPHA && - rightTop.alpha < MIN_ALPHA && - rightBottom.alpha < MIN_ALPHA && - leftBottom.alpha < MIN_ALPHA - ) return@render - - if (size.x < MIN_SIZE || size.y < MIN_SIZE) return@render - val halfSize = size * 0.5 val maxRadius = min(halfSize.x, halfSize.y) @@ -110,12 +100,21 @@ object FilledRectRenderer : AbstractGUIRenderer( shader["u_RoundRightBottom"] = rbr shader["u_RoundRightTop"] = rtr - grow(4) - putQuad( - vec3m(p1.x, p1.y, 0.0).vec2(0.0, 0.0).color(leftTop).end(), - vec3m(p1.x, p2.y, 0.0).vec2(0.0, 1.0).color(leftBottom).end(), - vec3m(p2.x, p2.y, 0.0).vec2(1.0, 1.0).color(rightBottom).end(), - vec3m(p2.x, p1.y, 0.0).vec2(1.0, 0.0).color(rightTop).end() - ) + upload { + buildQuad( + vertex { + vec3m(p1.x, p1.y, 0.0).vec2(0.0, 0.0).color(leftTop) + }, + vertex { + vec3m(p1.x, p2.y, 0.0).vec2(0.0, 1.0).color(leftBottom) + }, + vertex { + vec3m(p2.x, p2.y, 0.0).vec2(1.0, 1.0).color(rightBottom) + }, + vertex { + vec3m(p2.x, p1.y, 0.0).vec2(1.0, 0.0).color(rightTop) + } + ) + } } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt index 415ff7d29..55adb043a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt @@ -17,8 +17,8 @@ package com.lambda.graphics.renderer.gui.rect -import com.lambda.graphics.buffer.IRenderContext import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib +import com.lambda.graphics.pipeline.VertexBuilder import com.lambda.graphics.renderer.gui.AbstractGUIRenderer import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.util.math.lerp @@ -58,9 +58,7 @@ object OutlineRectRenderer : AbstractGUIRenderer( ) = render(shade) { if (glowRadius < 0.1) return@render - grow(VERTICES_COUNT * 3) - - fun IRenderContext.genVertices(size: Double, isGlow: Boolean): MutableList { + fun VertexBuilder.genIndices(size: Double, isGlow: Boolean): MutableList { val r = rect.expand(size) val a = (!isGlow).toInt().toDouble() @@ -68,10 +66,10 @@ object OutlineRectRenderer : AbstractGUIRenderer( val maxRadius = min(halfSize.x, halfSize.y) - 0.5 val round = (roundRadius + size).coerceAtMost(maxRadius).coerceAtLeast(0.0) - fun MutableList.buildCorners(base: Vec2d, c: Color, angleRange: IntRange) = repeat(QUALITY) { + fun MutableList.buildCorners(base: Vec2d, c: Color, angleRange: IntRange) = repeat(QUALITY) { i -> val min = angleRange.first.toDouble() val max = angleRange.last.toDouble() - val p = it.toDouble() / QUALITY + val p = i.toDouble() / QUALITY val angle = lerp(p, min, max).toRadian() val pos = base + Vec2d(cos(angle), -sin(angle)) * round @@ -79,7 +77,9 @@ object OutlineRectRenderer : AbstractGUIRenderer( val uvx = transform(pos.x, rect.left, rect.right, 0.0, 1.0) val uvy = transform(pos.y, rect.top, rect.bottom, 0.0, 1.0) - add(vec3m(pos.x, pos.y, 0.0).vec2(uvx, uvy).float(a).color(c).end()) + vertex { + vec3m(pos.x, pos.y, 0.0).vec2(uvx, uvy).float(a).color(c) + }.let { add(it) } } val rt = r.rightTop + Vec2d(-round, round) @@ -95,18 +95,20 @@ object OutlineRectRenderer : AbstractGUIRenderer( } } - val main = genVertices(0.0, false) + upload { + val main = genIndices(0.0, false) - fun drawStripWith(vertices: MutableList) { - var prev = main.last() to vertices.last() - repeat(VERTICES_COUNT) { - val new = main[it] to vertices[it] - putQuad(new.first, new.second, prev.second, prev.first) - prev = new + fun drawStripWith(vertices: MutableList) { + var prev = main.last() to vertices.last() + repeat(VERTICES_COUNT) { + val new = main[it] to vertices[it] + buildQuad(new.first, new.second, prev.second, prev.first) + prev = new + } } - } - drawStripWith(genVertices(-(glowRadius.coerceAtMost(1.0)), true)) - drawStripWith(genVertices(glowRadius, true)) + drawStripWith(genIndices(-(glowRadius.coerceAtMost(1.0)), true)) + drawStripWith(genIndices(glowRadius, true)) + } } } diff --git a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt index 46138e7b3..c6c4da26d 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt @@ -20,8 +20,7 @@ package com.lambda.gui.component.layout import com.lambda.graphics.RenderMain import com.lambda.graphics.animation.AnimationTicker import com.lambda.event.events.GuiEvent -import com.lambda.graphics.pipeline.ScissorAdapter -import com.lambda.gui.RootLayout +import com.lambda.graphics.renderer.ScissorAdapter import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.* @@ -131,6 +130,7 @@ open class Layout( } protected open val renderSelf: Boolean get() = width > 1 && height > 1 + protected open val updateChildren: Boolean get() = true protected open val scissorRect get() = rect // Inputs @@ -371,6 +371,8 @@ open class Layout( // Update children children.forEach { child -> if (e is GuiEvent.Render) return@forEach + if (e is GuiEvent.Update && !updateChildren) return@forEach + if (e is GuiEvent.MouseClick) { val newAction = if (child.isHovered) e.action else Mouse.Action.Release @@ -385,7 +387,7 @@ open class Layout( if (e is GuiEvent.Render) { val block = { renderActions.forEach { it(this) } - if (renderSelf) children.forEach { it.onEvent(e) } + children.forEach { it.onEvent(e) } } if (!properties.scissor) block() diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt index af5ccd69e..ad997f854 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt @@ -46,6 +46,9 @@ class WindowContent( override val renderSelf: Boolean get() = window.heightAnimation > 0.05 + override val updateChildren: Boolean + get() = window.heightAnimation > 0.05 + /** * Orders the children set vertically */ diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt index 08936c229..b7f228fb2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt @@ -30,6 +30,9 @@ object RenderSettings : Module( ) { private val page by setting("Page", Page.Font) + // General + val useMemoryMapping by setting("Use Memory Mapping", true) { page == Page.General} + // Font val textFont by setting("Text Font", LambdaFont.FiraSansRegular) { page == Page.Font } val emojiFont by setting("Emoji Font", LambdaEmoji.Twemoji) { page == Page.Font } @@ -49,6 +52,7 @@ object RenderSettings : Module( val outlineWidth by setting("Outline Width", 1.0, 0.1..5.0, 0.1, "Width of block outlines", unit = "px") { page == Page.ESP } private enum class Page { + General, Font, ESP, } diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt index 809da8019..7860c9ea4 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -24,7 +24,7 @@ import com.lambda.event.events.PlayerEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.buffer.VertexPipeline +import com.lambda.graphics.pipeline.VertexPipeline import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.gl.GlStateUtils.withBlendFunc @@ -32,6 +32,7 @@ import com.lambda.graphics.gl.GlStateUtils.withDepth import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.buildWorldProjection import com.lambda.graphics.gl.Matrices.withVertexTransform +import com.lambda.graphics.pipeline.VertexBuilder import com.lambda.graphics.shader.Shader.Companion.shader import com.lambda.interaction.request.rotation.Rotation import com.lambda.module.Module @@ -91,7 +92,10 @@ object Particles : Module( listen { // Todo: interpolated tickbased upload? - particles.forEach(Particle::build) + val builder = pipeline.build() + particles.forEach { + it.build(builder) + } withBlendFunc(GL_SRC_ALPHA, GL_ONE) { shader.use() @@ -178,7 +182,7 @@ object Particles : Module( return age > maxAge + fadeTicks * 2 + 5 } - fun build() { + fun build(builder: VertexBuilder) = builder.apply { val smoothAge = age + mc.partialTicks val colorTicks = smoothAge * 0.1 / colorSpeed @@ -199,15 +203,20 @@ object Particles : Module( val size = if (lay) environmentSize else sizeSetting * lerp(alpha, 0.5, 1.0) withVertexTransform(buildWorldProjection(position, size, projRotation)) { - pipeline.use { - grow(4) // DO NOT FUCKING FORGOTEOIJTOWKET TO GROW (cost me an hour) - putQuad( - vec3m(-1.0, -1.0, 0.0).vec2(0.0, 0.0).color(color).end(), - vec3m(-1.0, 1.0, 0.0).vec2(0.0, 1.0).color(color).end(), - vec3m(1.0, 1.0, 0.0).vec2(1.0, 1.0).color(color).end(), - vec3m(1.0, -1.0, 0.0).vec2(1.0, 0.0).color(color).end() - ) - } + buildQuad( + vertex { + vec3m(-1.0, -1.0, 0.0).vec2(0.0, 0.0).color(color) + }, + vertex { + vec3m(-1.0, 1.0, 0.0).vec2(0.0, 1.0).color(color) + }, + vertex { + vec3m(1.0, 1.0, 0.0).vec2(1.0, 1.0).color(color) + }, + vertex { + vec3m(1.0, -1.0, 0.0).vec2(1.0, 0.0).color(color) + } + ) } } } From 6bf2dcac20380bef8082fa6e6ef2d9263f703ae5 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Fri, 28 Mar 2025 21:01:38 +0300 Subject: [PATCH 13/25] Misc render stuff --- .../buffer/vertex/attributes/VertexAttrib.kt | 94 ++++++++++--------- .../graphics/pipeline/PersistentBuffer.kt | 2 +- .../graphics/pipeline/VertexPipeline.kt | 8 +- .../renderer/gui/rect/FilledRectRenderer.kt | 2 +- .../lambda/module/modules/client/ClickGui.kt | 2 +- .../lambda/module/modules/render/Particles.kt | 2 +- .../lambda/shaders/renderer/rect_filled.glsl | 41 ++------ 7 files changed, 67 insertions(+), 84 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt index 42014cfa6..3afc4cfff 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt @@ -21,65 +21,72 @@ import org.lwjgl.opengl.GL11C.GL_FLOAT import org.lwjgl.opengl.GL11C.GL_UNSIGNED_BYTE import org.lwjgl.opengl.GL20C.glEnableVertexAttribArray import org.lwjgl.opengl.GL20C.glVertexAttribPointer +import org.lwjgl.opengl.GL33.glVertexAttribDivisor -enum class VertexAttrib( +sealed class VertexAttrib( private val componentCount: Int, componentSize: Int, private val normalized: Boolean, + private val single: Boolean, private val type: Int ) { - 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); + open class Float( + normalized: Boolean = false, single: Boolean = false + ) : VertexAttrib(1, 4, normalized, single, GL_FLOAT) { + companion object : Float() + } + + open class Vec2( + normalized: Boolean = false, single: Boolean = false + ) : VertexAttrib(2, 4, normalized, single, GL_FLOAT) { + companion object : Vec2() + } + + open class Vec3( + normalized: Boolean = false, single: Boolean = false + ) : VertexAttrib(3, 4, normalized, single, GL_FLOAT) { + companion object : Vec3() + } + + open class Color( + normalized: Boolean = true, single: Boolean = false + ) : VertexAttrib(4, 1, normalized, single, GL_UNSIGNED_BYTE) { + companion object : Color() + } val size = componentCount * componentSize - fun pointer(index: Int, pointer: Long, stride: Int) { + fun link(index: Int, pointer: Long, stride: Int) { glEnableVertexAttribArray(index) glVertexAttribPointer(index, componentCount, type, normalized, stride, pointer) + if (single) glVertexAttribDivisor(index, 1) } - enum class Group(vararg val attributes: VertexAttrib) { - POS_UV(Vec2, Vec2), + @Suppress("ClassName") + open class Group(vararg val attributes: VertexAttrib) { + object POS_UV : Group(Vec2, Vec2) // GUI - FONT( - Vec3, // pos - Vec2, // uv - Color - ), - - RECT_FILLED( - Vec3, // pos - Vec2, // uv - Color - ), - - RECT_OUTLINE( - Vec3, // pos - Vec2, // uv - Float, // alpha - Color - ), + object FONT : Group(Vec3, Vec2, Color) + + object RECT_FILLED : Group( + Vec3, Vec2, Color + ) + + object RECT_OUTLINE : Group( + Vec3, Vec2, Float, Color + ) // WORLD - DYNAMIC_RENDERER( - Vec3, // prev pos - Vec3, // pos - Color - ), - - STATIC_RENDERER( - Vec3, // pos - Color - ), - - PARTICLE( - Vec3, - Vec2, // pos - Color - ); + object DYNAMIC_RENDERER : Group( + Vec3, Vec3, Color + ) + + object STATIC_RENDERER : Group( + Vec3, Color + ) + + object PARTICLE : Group(Vec3, Vec2, Color) val stride = attributes.sumOf { attribute -> attribute.size @@ -87,10 +94,9 @@ enum class VertexAttrib( fun link() { attributes.foldIndexed(0L) { index, pointer, attrib -> - attrib.pointer(index, pointer, stride) + attrib.link(index, pointer, stride) pointer + attrib.size } } } } - diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt index e0e5eec6b..ee8d3fbfc 100644 --- a/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt @@ -51,6 +51,7 @@ class PersistentBuffer( fun upload() { val dataStart = byteBuffer.pointer + uploadOffset val dataCount = byteBuffer.bytesPut - uploadOffset + if (dataCount <= 0) return if (glSize != byteBuffer.capacity) { glSize = byteBuffer.capacity @@ -73,7 +74,6 @@ class PersistentBuffer( } glBuffer.update(uploadOffset.toLong(), dataCount, dataStart) - println(dataCount) } fun end() { diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt index 565c466ea..31c1c3bf5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/VertexPipeline.kt @@ -73,7 +73,7 @@ class VertexPipeline( */ fun immediate(block: VertexBuilder.() -> Unit) { VertexBuilder(this).apply(block) - upload(); render(); clear() + uploadInternal(); render(); clear() } /** @@ -88,7 +88,7 @@ class VertexPipeline( */ fun upload(block: VertexBuilder.() -> Unit) { VertexBuilder(this).apply(block) - upload() + uploadInternal() } /** @@ -100,7 +100,7 @@ class VertexPipeline( */ fun upload(builder: VertexBuilder) { builder.uploadTo(this) - upload() + uploadInternal() } /** @@ -114,7 +114,7 @@ class VertexPipeline( /** * Uploads buffered data to GPU memory */ - fun upload() { + private fun uploadInternal() { vbo.upload() ibo.upload() } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index 27af0d467..0f75eea23 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -27,7 +27,7 @@ import kotlin.math.min object FilledRectRenderer : AbstractGUIRenderer( VertexAttrib.Group.RECT_FILLED, shader("renderer/rect_filled") ) { - private const val EXPAND = 0.35 + private const val EXPAND = 0.4 fun filledRect( rect: Rect, 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 e207e1d52..3f326670f 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 @@ -75,7 +75,7 @@ object ClickGui : Module( val animationCurve by setting("List Animation Curve", AnimationCurve.Normal) val smoothness by setting("Smoothness", 0.4, 0.3..0.7, 0.01) { animationCurve != AnimationCurve.Static } - val hudPadding by setting("Hud Padding", 3.0, 0.0..10.0, 0.1) + val hudPadding by setting("Hud Padding", 2.0, 0.0..10.0, 0.1) val SCREEN: LambdaScreen by lazy { gui("Click Gui") { diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt index 7860c9ea4..6484cff55 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/Particles.kt @@ -101,7 +101,7 @@ object Particles : Module( shader.use() shader["u_CameraPosition"] = mc.gameRenderer.camera.pos - pipeline.upload() + pipeline.upload(builder) withDepth(false, pipeline::render) pipeline.clear() } diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl index cbf30ccbf..6cb03c8a1 100644 --- a/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl @@ -13,41 +13,18 @@ uniforms { }; #include "rect" -#define SMOOTHING 0.2 -#define round getRoundAlpha() +#define SMOOTHING 0.5 -float getRoundRadius() { - bool xcmp = v_TexCoord.x > 0.5; - bool ycmp = v_TexCoord.y > 0.5; +float roundedRectSDF() { + vec4 r = vec4(u_RoundRightBottom, u_RoundRightTop, u_RoundLeftBottom, u_RoundLeftTop); + r.xy = (v_TexCoord.x > 0.5) ? r.xy : r.zw; + r.x = (v_TexCoord.y > 0.5) ? r.x : r.y; - float r = 0.0; - - if (xcmp) { - if (ycmp) { r = u_RoundRightBottom; } - else { r = u_RoundRightTop; } - } else { - if (ycmp) { r = u_RoundLeftBottom; } - else { r = u_RoundLeftTop; } - } - - return r; -}# - -vec4 getRoundAlpha() { - vec2 halfSize = u_Size * 0.5; - - float radius = max(getRoundRadius(), SMOOTHING); - - vec2 smoothVec = vec2(SMOOTHING); - vec2 coord = mix(-smoothVec, u_Size + smoothVec, v_TexCoord); - - vec2 center = halfSize - coord; - float distance = length(max(abs(center) - halfSize + radius, 0.0)) - radius; - - float alpha = 1.0 - smoothstep(-SMOOTHING, SMOOTHING, distance); - return vec4(1.0, 1.0, 1.0, clamp(alpha, 0.0, 1.0)); + vec2 q = u_Size * (abs(v_TexCoord - 0.5) - 0.5) + r.x; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x; }# void fragment() { - color = v_Color * shade * round + noise; + float a = 1.0 - smoothstep(-SMOOTHING, 0.0, roundedRectSDF()); + color = v_Color * vec4(shade.rgb, shade.a * a) + noise; }# \ No newline at end of file From a8f0743f482a4287bcd98f5233475f92131c4e01 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:26:12 -0400 Subject: [PATCH 14/25] Buffer api modification --- .../com/lambda/graphics/buffer/Buffer.kt | 307 +++++++++--------- .../graphics/buffer/pixel/PixelBuffer.kt | 18 +- .../graphics/buffer/vertex/VertexArray.kt | 14 +- .../kotlin/com/lambda/graphics/gl/Buffers.kt | 63 ---- 4 files changed, 158 insertions(+), 244 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/Buffers.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt index 44057349f..c92dd313a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt @@ -17,10 +17,7 @@ package com.lambda.graphics.buffer -import com.lambda.graphics.gl.bufferBound -import com.lambda.graphics.gl.bufferUsageValid -import com.lambda.graphics.gl.bufferValid -import org.lwjgl.opengl.GL44.* +import org.lwjgl.opengl.GL46.* import java.nio.ByteBuffer abstract class Buffer( @@ -29,15 +26,9 @@ abstract class Buffer( * * | Number of Buffers | Purpose | * |-------------------|---------------------------------------------------------------------------------------------------------------| - * | 1 Buffer | Simple operations like storing vertex data or a single texture. | - * | 2 Buffers | Double buffering for smooth rendering (alternating between writing to one buffer while reading from another). | - * | 3 Buffers | Triple buffering for improved frame rate and reduced screen tearing at the cost of memory usage. | - * - * In double buffering, you have a front buffer (A) and a back buffer (B). While one is displayed (A), the other (B) is being drawn to. After drawing, the buffers are swapped. However, during the swap (often synchronized with vertical retrace to avoid tearing), drawing cannot resume until it completes, potentially causing delays. - * - * In triple buffering, there is a front buffer (A) and two back buffers (B and C). This allows drawing to continue on the second back buffer (C) while waiting for the swap to finish between the front (A) and the first back buffer (B). This reduces idle time and improves frame rates, especially if the app runs slower than the monitor's refresh rate. - * - * Triple buffering helps maintain smoother frame rates, but if your app runs faster than the monitor's refresh rate, it offers little benefit as you eventually still wait for vblank synchronization. + * | 1 Buffer | Simple operations like storing vertex data, reading from the framebuffer, etc. | + * | 2 Buffers | Increase throughput by not having to explicitly sync memory. | + * | 3 Buffers | If the driver run alongside the CPU and GPU, then each must have their own buffer to avoid stalling. | */ val buffers: Int = 1, @@ -60,6 +51,8 @@ abstract class Buffer( * | GL_DYNAMIC_DRAW | Data is modified repeatedly and used many times for drawing. | * | GL_DYNAMIC_READ | Data is modified repeatedly and used many times for reading. | * | GL_DYNAMIC_COPY | Data is modified repeatedly and used many times for copying. | + * + * @see Buffer object */ abstract val usage: Int @@ -83,6 +76,8 @@ abstract class Buffer( * | GL_TEXTURE_BUFFER | Texture data buffer | * | GL_TRANSFORM_FEEDBACK_BUFFER | Transform feedback buffer | * | GL_UNIFORM_BUFFER | Uniform block storage | + * + * @see Buffer object */ abstract val target: Int @@ -100,96 +95,81 @@ abstract class Buffer( * | GL_MAP_INVALIDATE_BUFFER_BIT | Discards previous contents of the entire buffer. | Cannot be used with GL_MAP_READ_BIT. | * | GL_MAP_FLUSH_EXPLICIT_BIT | Requires explicit flushing of modified sub-ranges. | Only with GL_MAP_WRITE_BIT. Data may be undefined if skipped. | * | GL_MAP_UNSYNCHRONIZED_BIT | Skips synchronization before mapping. | May cause data corruption if regions overlap. | + * + * @see Buffer object */ abstract val access: Int /** - * Index of the current buffer + * Index of the current buffer. */ var index: Int = 0; private set /** - * List of all the buffers + * List of all the buffers. */ private val bufferIds = IntArray(buffers) /** - * Binds the buffer id to the [target] + * Binds the buffer id to the [target]. */ open fun bind(id: Int) = glBindBuffer(target, id) /** - * Binds current the buffer [index] to the [target] + * Binds current the buffer [index] to the [target]. */ fun bind() = bind(bufferAt(index)) /** - * Returns the id of the buffer based on the index + * Returns the id of the buffer based on the index. */ fun bufferAt(index: Int) = bufferIds[index] /** - * Swaps the buffer [index] if [buffers] is greater than 1 + * Swaps the buffer [index] if [buffers] is greater than 1. */ - fun swap() { - index = (index + 1) % buffers - } + fun swap() { index = (index + 1) % buffers } /** - * Update the current buffer without re-allocating - * Alternative to [map] + * Update the current buffer without re-allocating. + * + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferSubData */ - open fun update( - data: ByteBuffer, - offset: Long, - ): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferBound(target)) - return IllegalArgumentException("Target is zero bound for glBufferSubData") + open fun update(data: ByteBuffer, offset: Long) { + validate() + bind() glBufferSubData(target, offset, data) - - return null + bind(0) } /** - * Update the current buffer without re-allocating - * This function handles the buffer binding - * Alternative to [map] + * Update the current buffer without re-allocating. + * + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferSubData */ - open fun update( - offset: Long, - size: Long, - dataPointer: Long - ): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - repeat(buffers) { - bind() - nglBufferSubData(target, offset, size, dataPointer) - swap() - } + open fun update(offset: Long, size: Long, data: Long) { + validate() + bind() + nglBufferSubData(target, offset, size, data) bind(0) - - return null } /** - * Allocates a region of memory for the buffer - * This function handles the buffer binding + * Allocates each backing buffer with the specified data. * * @param data The data to put in the new allocated buffer + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferData */ - open fun allocate(data: ByteBuffer): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferUsageValid(usage)) - return IllegalArgumentException("Buffer usage is invalid") + open fun allocate(data: ByteBuffer) { + validate() repeat(buffers) { bind() @@ -198,23 +178,18 @@ abstract class Buffer( } bind(0) - - return null } /** - * Allocates memory for each backing buffer using the specified size - * This function handles the buffer binding + * Allocates memory for each backing buffer of specified size. * * @param size The size of the new buffer - * @return An [IllegalArgumentException] if validation fails; null if the allocation succeeds + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferData */ - open fun allocate(size: Long): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferUsageValid(usage)) - return IllegalArgumentException("Buffer usage is invalid") + open fun allocate(size: Long) { + validate() repeat(buffers) { bind() @@ -223,134 +198,138 @@ abstract class Buffer( } bind(0) - - return null } /** - * Allocates new storage for the OpenGL buffer using the provided data - * This function cannot be called twice for the same buffer - * This function handles the buffer binding + * Allocates new storage for the OpenGL buffer using the provided data. + * + * This function cannot be called twice for the same buffer. * - * @return [IllegalArgumentException] for an invalid target or usage; null if storage allocation is successful + * You cannot update the content of the buffer directly unless you are pinning memory + * or have GL_DYNAMIC_STORAGE_BIT in the access flags. + * + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferStorage */ - open fun storage(data: ByteBuffer): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferUsageValid(usage)) - return IllegalArgumentException("Buffer usage is invalid") + open fun storage(data: ByteBuffer) { + validate() repeat(buffers) { bind() - glBufferStorage(target, data, access or GL_DYNAMIC_STORAGE_BIT) + glBufferStorage(target, data, access) swap() } bind(0) - - return null } /** - * Allocates storage for the buffer object - * This function cannot be called twice for the same buffer - * This function handles the buffer binding + * Allocates storage for the buffer object. + * + * This function cannot be called twice for the same buffer. + * + * You cannot update the content of the buffer directly unless you are pinning memory + * or have GL_DYNAMIC_STORAGE_BIT in the access flags. * * @param size The size of the storage buffer - * @return [IllegalArgumentException] if the target or usage is invalid; null if storage allocation succeeds + * @throws [IllegalArgumentException] if the target or usage is invalid + * + * @see glBufferStorage */ - open fun storage(size: Long): Throwable? { - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferUsageValid(usage)) - return IllegalArgumentException("Buffer usage is invalid") + open fun storage(size: Long) { + validate() repeat(buffers) { bind() - glBufferStorage(target, size.coerceAtLeast(0), access or GL_DYNAMIC_STORAGE_BIT) + glBufferStorage(target, size.coerceAtLeast(0), access) swap() } bind(0) - - return null } + // TODO: + // GL_MAP_COHERENT_BIT makes it so changes in the mapped memory are automatically visible to the gpu, no memory barrier and syncing required, but a bit slower + // You still need to swap after each update or you will risk having conflicting reads and writes + // glFlushMappedBufferRange + /** - * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and then unmaps the buffer + * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and then unmaps the buffer. + * + * This function does not handle the binding. + * + * If [access] contains the `GL_MAP_PERSISTENT_BIT` flag, the buffer will not be unmapped. * * @param size Specifies the length of the range to be mapped. * @param offset Specifies the starting offset within the buffer of the range to be mapped. * @param block Lambda scope with the mapped buffer passed in - * @return [IllegalArgumentException] if there were errors during the validation, mapping or unmapping, null otherwise + * + * @see Direct memory access */ - open fun map( - size: Long, - offset: Long, - block: (ByteBuffer) -> Unit - ): Throwable? { - if ( - offset < 0 || - size < 0 - ) return IllegalArgumentException("Invalid offset or size parameter offset: $offset size: $size") - - if (!bufferValid(target, access)) - return IllegalArgumentException("Target is not valid. Refer to the table in the documentation") - - if (!bufferBound(target)) - return IllegalArgumentException("Target is zero bound for glMapBufferRange") - - if ( - offset + size > glGetBufferParameteri(target, GL_BUFFER_SIZE) - ) return IllegalArgumentException("Out of bound (is the buffer initialized?) $size + $offset > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}") - - if ( - glGetBufferParameteri(target, GL_BUFFER_MAPPED) - == GL_TRUE - ) return IllegalStateException("Buffer is already mapped, something wrong happened") - - if ( - access and GL_MAP_WRITE_BIT == 0 && - access and GL_MAP_READ_BIT == 0 - ) return IllegalArgumentException("Neither GL_MAP_READ_BIT nor GL_MAP_WRITE_BIT is set") - - if ( - access and GL_MAP_READ_BIT != 0 && - (access and GL_MAP_INVALIDATE_RANGE_BIT == 0 || - access and GL_MAP_INVALIDATE_BUFFER_BIT == 0 || - access and GL_MAP_UNSYNCHRONIZED_BIT == 0 - ) - ) return IllegalArgumentException("GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set.") - - // Map the buffer into the client's memory + open fun map(size: Long, offset: Long, block: (ByteBuffer) -> Unit = {}): ByteBuffer { + validate() + + check(offset >= 0 || size >= 0) + { "Invalid offset or size parameter offset: $offset size: $size." } + + check(offset + size <= glGetBufferParameteri(target, GL_BUFFER_SIZE)) + { "Out of bound (is the buffer initialized?) $size + $offset > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}." } + + check(glGetInteger(bindingCheckMappings.getValue(target)) == GL_TRUE) + { "Target is zero bound." } + + check(glGetBufferParameteri(target, GL_BUFFER_MAPPED) == GL_FALSE) + { "Buffer is already mapped." } + + check(access and GL_MAP_WRITE_BIT != 0 && access and GL_MAP_READ_BIT != 0) + { "Neither GL_MAP_READ_BIT nor GL_MAP_WRITE_BIT is set." } + + check(access and GL_MAP_READ_BIT != 0 && + (access and GL_MAP_INVALIDATE_RANGE_BIT != 0 || + access and GL_MAP_INVALIDATE_BUFFER_BIT != 0 || + access and GL_MAP_UNSYNCHRONIZED_BIT != 0) + ) + { "GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set." } + val sharedRegion = glMapBufferRange(target, offset, size, access) - ?: return IllegalStateException("Failed to map buffer") + ?: throw IllegalStateException("Failed to map buffer.") - // Update data on the shared buffer block(sharedRegion) - // Release the buffer - if (!glUnmapBuffer(target)) - return IllegalStateException("An unknown error occurred due to GPU memory availability of buffer corruption") + if (access and GL_MAP_PERSISTENT_BIT == 0) { + if (!glUnmapBuffer(target)) + throw IllegalStateException("An unknown error occurred due to GPU memory availability of buffer corruption.") + } - return null + return sharedRegion } /** - * Uploads the specified data to the buffer starting at the given offset + * Uploads the specified data to the buffer starting at the given offset. * - * This abstract function should be implemented to perform the actual data transfer into the buffer + * This abstract function should be implemented to perform the actual data transfer into the buffer. * * @param data Data to set in memory * @param offset The starting offset within the buffer of the range to be mapped - * @return Error encountered during the mapping process */ - abstract fun upload(data: ByteBuffer, offset: Long): Throwable? + abstract fun upload(data: ByteBuffer, offset: Long) + + private fun validate() { + check(usage in GL_STREAM_DRAW..GL_DYNAMIC_COPY) + { "Usage is invalid, refer to the documentation table." } + + check(target in bindingCheckMappings) + { "Target is invalid, refer to the documentation table." } + + check(access and GL_MAP_COHERENT_BIT == 0 || access and GL_MAP_PERSISTENT_BIT != 0) + { "GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT flag." } + + check(access and GL_MAP_PERSISTENT_BIT == 0 || (access and (GL_MAP_READ_BIT or GL_MAP_WRITE_BIT) != 0)) + { "GL_MAP_PERSISTENT_BIT requires GL_MAP_READ_BIT or GL_MAP_WRITE_BIT." } + } init { - // Special edge case for vertex arrays check(buffers > 0) { "Cannot generate less than one buffer" } if (isVertexArray) glGenVertexArrays(bufferIds) // If there are more than 1 buffer you should expect undefined behavior, this is not the way to do it @@ -358,6 +337,23 @@ abstract class Buffer( } companion object { + val bindingCheckMappings = mapOf( + GL_ARRAY_BUFFER to GL_ARRAY_BUFFER_BINDING, + GL_ATOMIC_COUNTER_BUFFER to GL_ATOMIC_COUNTER_BUFFER_BINDING, + GL_COPY_READ_BUFFER_BINDING to GL_COPY_READ_BUFFER_BINDING, + GL_COPY_WRITE_BUFFER_BINDING to GL_COPY_WRITE_BUFFER_BINDING, + GL_DISPATCH_INDIRECT_BUFFER to GL_DISPATCH_INDIRECT_BUFFER_BINDING, + GL_DRAW_INDIRECT_BUFFER to GL_DRAW_INDIRECT_BUFFER_BINDING, + GL_ELEMENT_ARRAY_BUFFER to GL_ELEMENT_ARRAY_BUFFER_BINDING, + GL_PIXEL_PACK_BUFFER to GL_PIXEL_PACK_BUFFER_BINDING, + GL_PIXEL_UNPACK_BUFFER to GL_PIXEL_UNPACK_BUFFER_BINDING, + GL_QUERY_BUFFER to GL_QUERY_BUFFER_BINDING, + GL_SHADER_STORAGE_BUFFER to GL_SHADER_STORAGE_BUFFER_BINDING, + GL_TEXTURE_BUFFER to GL_TEXTURE_BUFFER_BINDING, + GL_TRANSFORM_FEEDBACK_BUFFER to GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, + GL_UNIFORM_BUFFER to GL_UNIFORM_BUFFER_BINDING, + ) + @JvmField var lastIbo = 0 var prevIbo = 0 @@ -378,10 +374,7 @@ abstract class Buffer( super.bind(if (id != 0) id else prevIbo) } - override fun upload( - data: ByteBuffer, - offset: Long, - ): Throwable = UnsupportedOperationException() + override fun upload(data: ByteBuffer, offset: Long) = throw UnsupportedOperationException() } } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt index c8e590125..ea5d2f3cd 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt @@ -42,17 +42,14 @@ class PixelBuffer( private val asynchronous: Boolean = false, private val bufferMapping: Boolean = false, ) : Buffer(buffers = asynchronous.toInt() + 1) { - override val usage: Int = GL_STATIC_DRAW - override val target: Int = GL_PIXEL_UNPACK_BUFFER - override val access: Int = GL_MAP_WRITE_BIT + override val usage = GL_STATIC_DRAW + override val target = GL_PIXEL_UNPACK_BUFFER + override val access = GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT or GL_MAP_PERSISTENT_BIT private val channels = channelMapping[texture.format] ?: throw IllegalArgumentException("Invalid image format, expected OpenGL format, got ${texture.format} instead") private val size = texture.width * texture.height * channels * 1L - override fun upload( - data: ByteBuffer, - offset: Long, - ): Throwable? { + override fun upload(data: ByteBuffer, offset: Long) { bind() glBindTexture(GL_TEXTURE_2D, texture.id) @@ -72,13 +69,10 @@ class PixelBuffer( swap() bind() - val error = - if (bufferMapping) map(size, offset, data::putTo) - else update(data, offset) + if (bufferMapping) map(size, offset, data::putTo) + else update(data, offset) bind(0) - - return error } init { diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt index bd526ec7c..f494bdab6 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt @@ -49,18 +49,8 @@ class VertexArray( bind(0) } - override fun map( - size: Long, - offset: Long, - block: (ByteBuffer) -> Unit - ): Throwable = throw UnsupportedOperationException("Cannot map a vertex array object to memory") - - override fun upload( - data: ByteBuffer, - offset: Long, - ): Throwable = throw UnsupportedOperationException("Data cannot be uploaded to a vertex array object") - - override fun allocate(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object") + override fun map(size: Long, offset: Long, block: (ByteBuffer) -> Unit) = throw UnsupportedOperationException() + override fun upload(data: ByteBuffer, offset: Long) = throw UnsupportedOperationException() override fun bind(id: Int) { glBindVertexArray(id); BufferRenderer.currentVertexBuffer = null diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Buffers.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Buffers.kt deleted file mode 100644 index c1a1636c9..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Buffers.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.gl - -import org.lwjgl.opengl.GL44.* - -/** - * Map of valid buffer binding target to their respective binding check parameter - * using [bufferBound] - */ -val bindingCheckMappings = mapOf( - GL_ARRAY_BUFFER to GL_ARRAY_BUFFER_BINDING, - GL_ATOMIC_COUNTER_BUFFER to GL_ATOMIC_COUNTER_BUFFER_BINDING, - GL_COPY_READ_BUFFER_BINDING to GL_COPY_READ_BUFFER_BINDING, - GL_COPY_WRITE_BUFFER_BINDING to GL_COPY_WRITE_BUFFER_BINDING, - GL_DISPATCH_INDIRECT_BUFFER to GL_DISPATCH_INDIRECT_BUFFER_BINDING, - GL_DRAW_INDIRECT_BUFFER to GL_DRAW_INDIRECT_BUFFER_BINDING, - GL_ELEMENT_ARRAY_BUFFER to GL_ELEMENT_ARRAY_BUFFER_BINDING, - GL_PIXEL_PACK_BUFFER to GL_PIXEL_PACK_BUFFER_BINDING, - GL_PIXEL_UNPACK_BUFFER to GL_PIXEL_UNPACK_BUFFER_BINDING, - GL_QUERY_BUFFER to GL_QUERY_BUFFER_BINDING, - GL_SHADER_STORAGE_BUFFER to GL_SHADER_STORAGE_BUFFER_BINDING, - GL_TEXTURE_BUFFER to GL_TEXTURE_BUFFER_BINDING, - GL_TRANSFORM_FEEDBACK_BUFFER to GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, - GL_UNIFORM_BUFFER to GL_UNIFORM_BUFFER_BINDING, -) - -/** - * Returns whether the buffer target is valid - */ -fun bufferValid(target: Int, access: Int): Boolean = - target in bindingCheckMappings && - // If access contains GL_MAP_COHERENT_BIT, it must also contain GL_MAP_PERSISTENT_BIT. - (access and GL_MAP_COHERENT_BIT == 0 || access and GL_MAP_PERSISTENT_BIT != 0) && - // If access contains GL_MAP_PERSISTENT_BIT, it must also contain at least one of GL_MAP_READ_BIT or GL_MAP_WRITE_BIT. - (access and GL_MAP_PERSISTENT_BIT == 0 || (access and (GL_MAP_READ_BIT or GL_MAP_WRITE_BIT) != 0)) - -/** - * Returns whether the provided buffer target is bound - */ -fun bufferBound(target: Int): Boolean = - IntArray(1) - .apply { glGetIntegerv(bindingCheckMappings.getValue(target), this) }[0] != GL_FALSE - -/** - * Returns whether the provided buffer usage is valid - */ -fun bufferUsageValid(usage: Int) = usage >= GL_STREAM_DRAW && usage <= GL_DYNAMIC_DRAW From 65e12598f75a861e2a1da60199c6f3627df7be51 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:26:33 -0400 Subject: [PATCH 15/25] Added texture construtor --- .../com/lambda/graphics/texture/Texture.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt index fe56a853e..b7e9261f4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -20,6 +20,8 @@ package com.lambda.graphics.texture import com.lambda.graphics.texture.TextureUtils.bindTexture import com.lambda.graphics.texture.TextureUtils.readImage import com.lambda.graphics.texture.TextureUtils.setupTexture +import com.lambda.util.LambdaResource +import com.lambda.util.readImage import net.minecraft.client.texture.NativeImage import org.lwjgl.opengl.GL45C.* import java.awt.image.BufferedImage @@ -36,6 +38,21 @@ open class Texture { private val levels: Int private val nativeFormat: NativeImage.Format // For mojang native images + /** + * @param path Lambda resource path + * @param format The format of the image passed in + * @param levels Number of mipmap levels to generate for the texture + */ + constructor(path: LambdaResource, format: Int = GL_RGBA, levels: Int = 4) { + val image = path.readImage() + this.format = bufferedMapping[image.type] ?: format + this.levels = levels + this.nativeFormat = nativeMapping.getOrDefault(format, NativeImage.Format.RGBA) + + bindTexture(id) + upload(image) + } + /** * @param image Optional initial image to upload to the texture * @param format The format of the image passed in, if the [image] is null, then you must pass the appropriate format From 6bb677f7301e3df99e689a127de7265f3581fc75 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:26:43 -0400 Subject: [PATCH 16/25] Modified max texture slot --- .../com/lambda/graphics/texture/TextureOwner.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt index 98f51465b..ecc01a8ba 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt @@ -44,27 +44,27 @@ object TextureOwner { textureMap.getValue(this@texture)[index] as T /** - * Binds a list of textures to texture slots, ensuring no more than 32 textures + * Binds a list of textures to texture slots, ensuring no more than 96 textures * are bound at once (to fit within the typical GPU limitations) * * @param textures The list of objects that own textures to be bound. - * @throws IllegalArgumentException If more than 32 textures are provided. + * @throws IllegalArgumentException If more than 96 textures are provided. */ fun bind(vararg textures: Any) { - check(textures.size < 33) { "Texture slot overflow, expected to use less than 33 slots, got ${textures.size} slots" } + check(textures.size <= 96) { "Expected equal or less than 96 simultaneous textures, got ${textures.size} textures" } textures.forEachIndexed { index, texture -> texture.texture.bind(index) } } /** - * Binds a list of textures to texture slots, ensuring no more than 32 textures + * Binds a list of textures to texture slots, ensuring no more than 96 textures * are bound at once (to fit within the typical GPU limitations) * * @param textures The list of textures to be bound - * @throws IllegalArgumentException If more than 32 textures are provided + * @throws IllegalArgumentException If more than 96 textures are provided */ fun bind(vararg textures: Texture) { - check(textures.size < 33) { "Texture slot overflow, expected to use less than 33 slots, got ${textures.size} slots" } + check(textures.size <= 96) { "Expected equal or less than 96 simultaneous textures, got ${textures.size} textures" } textures.forEachIndexed { index, texture -> texture.bind(index) } } From fab9c66dd443e28d17a2617db28f6b072fe1f3e7 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:27:13 -0400 Subject: [PATCH 17/25] gif texture chores --- .../lambda/graphics/texture/AnimatedTexture.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt index 6229c2c17..4a2687756 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt @@ -18,7 +18,6 @@ package com.lambda.graphics.texture import com.lambda.graphics.buffer.pixel.PixelBuffer -import com.lambda.util.Communication.logError import com.lambda.util.LambdaResource import com.lambda.util.stream import org.lwjgl.BufferUtils @@ -29,7 +28,7 @@ import java.nio.ByteBuffer class AnimatedTexture(path: LambdaResource) : Texture(image = null) { private val pbo: PixelBuffer private val gif: ByteBuffer // Do NOT free this pointer - private val frameDurations: IntArray + private val frameDurations: IntArray // Array of frame duration milliseconds as ints val channels: Int val frames: Int @@ -39,13 +38,12 @@ class AnimatedTexture(path: LambdaResource) : Texture(image = null) { private var currentFrame = 0 private var lastUpload = 0L - override fun bind(slot: Int) { - update() - super.bind(slot) - } + override fun bind(slot: Int) { update(); super.bind(slot) } fun update() { - if (System.currentTimeMillis() - lastUpload >= frameDurations[currentFrame]) { + val now = System.currentTimeMillis() + + if (now - lastUpload >= frameDurations[currentFrame]) { // This is cool because instead of having a buffer for each frame we can // just move the frame's block on each update // 0 memory allocation and few cpu cycles @@ -54,12 +52,10 @@ class AnimatedTexture(path: LambdaResource) : Texture(image = null) { .limit(blockSize * (currentFrame + 1)) pbo.upload(slice, offset = 0) - ?.let { err -> logError("Error uploading to PBO", err) } - gif.clear() currentFrame = (currentFrame + 1) % frames - lastUpload = System.currentTimeMillis() + lastUpload = now } } From 7c7e87db5b35773798461167a5fa2835d34e139b Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 29 Mar 2025 02:51:23 +0100 Subject: [PATCH 18/25] Preview color outline --- .../clickgui/module/setting/settings/ColorPicker.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt index ba150e36f..d9bc65c2d 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt @@ -80,7 +80,7 @@ class ColorPicker( } init { - rect { + val preview = rect { val shrink = 3.0 onUpdate { @@ -94,6 +94,15 @@ class ColorPicker( } } + outline { + onUpdate { + rect = preview.rect + roundRadius = 100.0 + glowRadius = 5.0 + setColor(Color.BLACK.setAlpha(0.1 * showAnimation)) + } + } + content.layout { val getU = { transform(mousePosition.x, positionX, positionX + width, 0.0, 1.0) } val getV = { transform(mousePosition.y, positionY, positionY + height, 0.0, 1.0) } From 8246b32dc1d79e319f0eab79a5c8ba7600fba439 Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Sat, 29 Mar 2025 19:16:36 +0300 Subject: [PATCH 19/25] SDF Rect renderer --- .../buffer/vertex/attributes/VertexAttrib.kt | 14 +- .../renderer/gui/AbstractGUIRenderer.kt | 8 +- .../renderer/gui/{font => }/FontRenderer.kt | 5 +- .../graphics/renderer/gui/RectRenderer.kt | 209 ++++++++++++++++++ .../renderer/gui/rect/FilledRectRenderer.kt | 120 ---------- .../renderer/gui/rect/OutlineRectRenderer.kt | 114 ---------- .../lambda/gui/component/core/FilledRect.kt | 60 +---- .../com/lambda/gui/component/core/GlowRect.kt | 109 +++++++++ .../lambda/gui/component/core/OutlineRect.kt | 49 +--- .../lambda/gui/component/core/RectLayout.kt | 83 +++++++ .../lambda/gui/component/core/TextField.kt | 4 +- .../com/lambda/gui/component/window/Window.kt | 19 +- .../gui/impl/clickgui/core/AnimatedChild.kt | 15 +- .../gui/impl/clickgui/core/SliderLayout.kt | 3 +- .../module/setting/settings/BooleanButton.kt | 8 +- .../module/setting/settings/ColorPicker.kt | 8 +- .../kotlin/com/lambda/module/HudModule.kt | 20 +- .../com/lambda/module/hud/TickShiftCharge.kt | 10 - .../lambda/module/modules/client/ClickGui.kt | 29 ++- .../module/modules/client/LambdaMoji.kt | 2 +- .../module/modules/combat/CrystalAura.kt | 5 +- .../lambda/shaders/renderer/rect_filled.glsl | 23 +- .../lambda/shaders/renderer/rect_glow.glsl | 40 ++++ .../lambda/shaders/renderer/rect_outline.glsl | 16 +- .../assets/lambda/shaders/shared/rect.glsl | 27 ++- 25 files changed, 583 insertions(+), 417 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/renderer/gui/{font => }/FontRenderer.kt (98%) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/RectRenderer.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt delete mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/component/core/GlowRect.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/component/core/RectLayout.kt create mode 100644 common/src/main/resources/assets/lambda/shaders/renderer/rect_glow.glsl diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt index 3afc4cfff..bac651b5f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt @@ -64,12 +64,16 @@ sealed class VertexAttrib( @Suppress("ClassName") open class Group(vararg val attributes: VertexAttrib) { - object POS_UV : Group(Vec2, Vec2) + object POS_UV : Group( + Vec2, Vec2 + ) // GUI - object FONT : Group(Vec3, Vec2, Color) + object FONT : Group( + Vec3, Vec2, Color + ) - object RECT_FILLED : Group( + object RECT : Group( Vec3, Vec2, Color ) @@ -86,7 +90,9 @@ sealed class VertexAttrib( Vec3, Color ) - object PARTICLE : Group(Vec3, Vec2, Color) + object PARTICLE : Group( + Vec3, Vec2, Color + ) val stride = attributes.sumOf { attribute -> attribute.size 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 cf4fe7153..755031514 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 @@ -32,7 +32,7 @@ import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Vec2d import org.lwjgl.glfw.GLFW -abstract class AbstractGUIRenderer( +open class AbstractGUIRenderer( attribGroup: VertexAttrib.Group, val shader: Shader ) { @@ -49,13 +49,13 @@ abstract class AbstractGUIRenderer( } } - protected fun render( + fun render( shade: Boolean = false, - block: VertexPipeline.() -> Unit + block: VertexPipeline.(Shader) -> Unit ) { shader.use() - block(pipeline) + block(pipeline, shader) shader["u_Shade"] = shade.toInt().toDouble() if (shade) { 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/FontRenderer.kt similarity index 98% rename from common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/FontRenderer.kt index a4684da4d..53ee181d5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/FontRenderer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Lambda + * Copyright 2025 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,10 @@ * along with this program. If not, see . */ -package com.lambda.graphics.renderer.gui.font +package com.lambda.graphics.renderer.gui import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.pipeline.VertexBuilder -import com.lambda.graphics.renderer.gui.AbstractGUIRenderer import com.lambda.graphics.renderer.gui.font.core.GlyphInfo import com.lambda.graphics.renderer.gui.font.core.LambdaAtlas.get import com.lambda.graphics.renderer.gui.font.core.LambdaAtlas.height diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/RectRenderer.kt new file mode 100644 index 000000000..1956d957d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/RectRenderer.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.graphics.renderer.gui + +import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib +import com.lambda.graphics.shader.Shader.Companion.shader +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d +import java.awt.Color +import kotlin.math.min + +object RectRenderer { + private val filled = AbstractGUIRenderer(VertexAttrib.Group.RECT, shader("renderer/rect_filled")) + private val outline = AbstractGUIRenderer(VertexAttrib.Group.RECT, shader("renderer/rect_outline")) + private val glow = AbstractGUIRenderer(VertexAttrib.Group.RECT, shader("renderer/rect_glow")) + + fun filledRect( + rect: Rect, + leftTopRadius: Double = 0.0, + rightTopRadius: Double = 0.0, + rightBottomRadius: Double = 0.0, + leftBottomRadius: Double = 0.0, + leftTop: Color = Color.WHITE, + rightTop: Color = Color.WHITE, + rightBottom: Color = Color.WHITE, + leftBottom: Color = Color.WHITE, + shade: Boolean = false, + ) = filled.putRect( + rect, + 0.0, + shade, + leftTopRadius, + rightTopRadius, + rightBottomRadius, + leftBottomRadius, + leftTop, + rightTop, + rightBottom, + leftBottom, + ) + + fun outlineRect( + rect: Rect, + width: Double = 1.0, + leftTopRadius: Double = 0.0, + rightTopRadius: Double = 0.0, + rightBottomRadius: Double = 0.0, + leftBottomRadius: Double = 0.0, + leftTop: Color = Color.WHITE, + rightTop: Color = Color.WHITE, + rightBottom: Color = Color.WHITE, + leftBottom: Color = Color.WHITE, + shade: Boolean = false, + ) { + if (width < 0.01) return + + outline.putRect( + rect, + width * 0.5, + shade, + leftTopRadius, + rightTopRadius, + rightBottomRadius, + leftBottomRadius, + leftTop, + rightTop, + rightBottom, + leftBottom, + ) + } + + fun glowRect( + rect: Rect, + outerSpread: Double = 1.0, + innerSpread: Double = 1.0, + leftTopInnerRadius: Double = 0.0, + rightTopInnerRadius: Double = 0.0, + rightBottomInnerRadius: Double = 0.0, + leftBottomInnerRadius: Double = 0.0, + leftTopOuterRadius: Double = 0.0, + rightTopOuterRadius: Double = 0.0, + rightBottomOuterRadius: Double = 0.0, + leftBottomOuterRadius: Double = 0.0, + leftTop: Color = Color.WHITE, + rightTop: Color = Color.WHITE, + rightBottom: Color = Color.WHITE, + leftBottom: Color = Color.WHITE, + shade: Boolean = false, + ) { + if (outerSpread < 0.01 && innerSpread < 0.01) return + + val pos1 = rect.leftTop + val pos2 = rect.rightBottom + + val size = pos2 - pos1 + val halfSize = size * 0.5 + val maxRadius = min(halfSize.x, halfSize.y) + + fun Double.clampRadius() = + this.coerceAtMost(maxRadius) + .coerceAtLeast(0.0) + + glow.shader.use() + glow.shader["u_InnerRectWidth"] = innerSpread.coerceAtLeast(1.0) + glow.shader["u_InnerRoundLeftTop"] = leftTopInnerRadius .coerceAtLeast(leftTopOuterRadius) .clampRadius() + glow.shader["u_InnerRoundLeftBottom"] = leftBottomInnerRadius .coerceAtLeast(leftBottomOuterRadius) .clampRadius() + glow.shader["u_InnerRoundRightBottom"] = rightBottomInnerRadius.coerceAtLeast(rightBottomOuterRadius).clampRadius() + glow.shader["u_InnerRoundRightTop"] = rightTopInnerRadius .coerceAtLeast(rightTopOuterRadius) .clampRadius() + + glow.putRect( + rect, + outerSpread.coerceAtLeast(1.0), + shade, + leftTopOuterRadius, + rightTopOuterRadius, + rightBottomOuterRadius, + leftBottomOuterRadius, + leftTop, + rightTop, + rightBottom, + leftBottom, + ) + } + + private fun AbstractGUIRenderer.putRect( + rect: Rect, + expandIn: Double, + shade: Boolean, + leftTopRadius: Double, + rightTopRadius: Double, + rightBottomRadius: Double, + leftBottomRadius: Double, + leftTop: Color, + rightTop: Color, + rightBottom: Color, + leftBottom: Color, + ) = render(shade) { shader -> + val pos1 = rect.leftTop + val pos2 = rect.rightBottom + + val expand = expandIn.coerceAtLeast(0.0) + 1 + val smoothing = 0.5 + + val p1 = pos1 - expand - smoothing + val p2 = pos2 + expand + smoothing + + val size = pos2 - pos1 + val halfSize = size * 0.5 + val maxRadius = min(halfSize.x, halfSize.y) + + val uv1 = Vec2d( + -expand / size.x, + -expand / size.y + ) + + val uv2 = Vec2d( + 1.0 + (expand / size.x), + 1.0 + (expand / size.y) + ) + + fun Double.clampRadius() = + this.coerceAtMost(maxRadius) + .coerceAtLeast(0.0) + + // Size of the rectangle + shader["u_Size"] = size + + // Round radius + shader["u_RoundLeftTop"] = leftTopRadius.clampRadius() + shader["u_RoundLeftBottom"] = leftBottomRadius.clampRadius() + shader["u_RoundRightBottom"] = rightBottomRadius.clampRadius() + shader["u_RoundRightTop"] = rightTopRadius.clampRadius() + + // For glow & outline only + shader["u_RectWidth"] = expandIn + + upload { + buildQuad( + vertex { + vec3m(p1.x, p1.y, 0.0).vec2(uv1.x, uv1.y).color(leftTop) + }, + vertex { + vec3m(p1.x, p2.y, 0.0).vec2(uv1.x, uv2.y).color(leftBottom) + }, + vertex { + vec3m(p2.x, p2.y, 0.0).vec2(uv2.x, uv2.y).color(rightBottom) + }, + vertex { + vec3m(p2.x, p1.y, 0.0).vec2(uv2.x, uv1.y).color(rightTop) + } + ) + } + } +} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt deleted file mode 100644 index 0f75eea23..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.gui.rect - -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.renderer.gui.AbstractGUIRenderer -import com.lambda.graphics.shader.Shader.Companion.shader -import com.lambda.util.math.Rect -import java.awt.Color -import kotlin.math.min - -object FilledRectRenderer : AbstractGUIRenderer( - VertexAttrib.Group.RECT_FILLED, shader("renderer/rect_filled") -) { - private const val EXPAND = 0.4 - - fun filledRect( - rect: Rect, - roundRadius: Double = 0.0, - color: Color = Color.WHITE, - shade: Boolean = false, - ) = filledRect(rect, roundRadius, color, color, color, color, shade) - - fun filledRect( - rect: Rect, - roundRadius: Double = 0.0, - leftTop: Color = Color.WHITE, - rightTop: Color = Color.WHITE, - rightBottom: Color = Color.WHITE, - leftBottom: Color = Color.WHITE, - shade: Boolean = false, - ) = filledRect( - rect, - roundRadius, roundRadius, roundRadius, roundRadius, - leftTop, rightTop, rightBottom, leftBottom, - shade - ) - - fun filledRect( - rect: Rect, - leftTopRadius: Double = 0.0, - rightTopRadius: Double = 0.0, - rightBottomRadius: Double = 0.0, - leftBottomRadius: Double = 0.0, - color: Color = Color.WHITE, - shade: Boolean = false, - ) = filledRect( - rect, - leftTopRadius, rightTopRadius, rightBottomRadius, leftBottomRadius, - color, color, color, color, - shade - ) - - fun filledRect( - rect: Rect, - leftTopRadius: Double = 0.0, - rightTopRadius: Double = 0.0, - rightBottomRadius: Double = 0.0, - leftBottomRadius: Double = 0.0, - leftTop: Color = Color.WHITE, - rightTop: Color = Color.WHITE, - rightBottom: Color = Color.WHITE, - leftBottom: Color = Color.WHITE, - shade: Boolean = false, - ) = render(shade) { - val pos1 = rect.leftTop - val pos2 = rect.rightBottom - - val size = pos2 - pos1 - - val halfSize = size * 0.5 - val maxRadius = min(halfSize.x, halfSize.y) - - val ltr = leftTopRadius.coerceAtMost(maxRadius).coerceAtLeast(0.0) - val lbr = leftBottomRadius.coerceAtMost(maxRadius).coerceAtLeast(0.0) - val rbr = rightBottomRadius.coerceAtMost(maxRadius).coerceAtLeast(0.0) - val rtr = rightTopRadius.coerceAtMost(maxRadius).coerceAtLeast(0.0) - - val p1 = pos1 - EXPAND - val p2 = pos2 + EXPAND - - shader["u_Size"] = size - shader["u_RoundLeftTop"] = ltr - shader["u_RoundLeftBottom"] = lbr - shader["u_RoundRightBottom"] = rbr - shader["u_RoundRightTop"] = rtr - - upload { - buildQuad( - vertex { - vec3m(p1.x, p1.y, 0.0).vec2(0.0, 0.0).color(leftTop) - }, - vertex { - vec3m(p1.x, p2.y, 0.0).vec2(0.0, 1.0).color(leftBottom) - }, - vertex { - vec3m(p2.x, p2.y, 0.0).vec2(1.0, 1.0).color(rightBottom) - }, - vertex { - vec3m(p2.x, p1.y, 0.0).vec2(1.0, 0.0).color(rightTop) - } - ) - } - } -} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt deleted file mode 100644 index 55adb043a..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2024 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.gui.rect - -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.pipeline.VertexBuilder -import com.lambda.graphics.renderer.gui.AbstractGUIRenderer -import com.lambda.graphics.shader.Shader.Companion.shader -import com.lambda.util.math.lerp -import com.lambda.util.math.MathUtils.toInt -import com.lambda.util.math.MathUtils.toRadian -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.cos -import kotlin.math.min -import kotlin.math.sin - -object OutlineRectRenderer : AbstractGUIRenderer( - VertexAttrib.Group.RECT_OUTLINE, shader("renderer/rect_outline") -) { - private const val QUALITY = 8 - private const val VERTICES_COUNT = QUALITY * 4 - - fun outlineRect( - rect: Rect, - roundRadius: Double = 0.0, - glowRadius: Double = 1.0, - color: Color = Color.WHITE, - shade: Boolean = false, - ) = outlineRect(rect, roundRadius, glowRadius, color, color, color, color, shade) - - fun outlineRect( - rect: Rect, - roundRadius: Double = 0.0, - glowRadius: Double = 1.0, - leftTop: Color = Color.WHITE, - rightTop: Color = Color.WHITE, - rightBottom: Color = Color.WHITE, - leftBottom: Color = Color.WHITE, - shade: Boolean = false, - ) = render(shade) { - if (glowRadius < 0.1) return@render - - fun VertexBuilder.genIndices(size: Double, isGlow: Boolean): MutableList { - val r = rect.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).coerceAtLeast(0.0) - - fun MutableList.buildCorners(base: Vec2d, c: Color, angleRange: IntRange) = repeat(QUALITY) { i -> - val min = angleRange.first.toDouble() - val max = angleRange.last.toDouble() - val p = i.toDouble() / QUALITY - val angle = lerp(p, min, max).toRadian() - - val pos = base + Vec2d(cos(angle), -sin(angle)) * round - - val uvx = transform(pos.x, rect.left, rect.right, 0.0, 1.0) - val uvy = transform(pos.y, rect.top, rect.bottom, 0.0, 1.0) - - vertex { - vec3m(pos.x, pos.y, 0.0).vec2(uvx, uvy).float(a).color(c) - }.let { add(it) } - } - - 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) - } - } - - upload { - val main = genIndices(0.0, false) - - fun drawStripWith(vertices: MutableList) { - var prev = main.last() to vertices.last() - repeat(VERTICES_COUNT) { - val new = main[it] to vertices[it] - buildQuad(new.first, new.second, prev.second, prev.first) - prev = new - } - } - - drawStripWith(genIndices(-(glowRadius.coerceAtMost(1.0)), true)) - drawStripWith(genIndices(glowRadius, true)) - } - } -} diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt b/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt index 877430948..2d93f94c3 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt @@ -17,29 +17,13 @@ package com.lambda.gui.component.core -import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer.filledRect +import com.lambda.graphics.renderer.gui.RectRenderer.filledRect import com.lambda.gui.component.layout.Layout -import com.lambda.util.math.Rect -import java.awt.Color class FilledRect( owner: Layout -) : Layout(owner) { - @UIRenderPr0p3rty var leftTopRadius = 0.0 - @UIRenderPr0p3rty var rightTopRadius = 0.0 - @UIRenderPr0p3rty var rightBottomRadius = 0.0 - @UIRenderPr0p3rty var leftBottomRadius = 0.0 - - @UIRenderPr0p3rty var leftTopColor: Color = Color.WHITE - @UIRenderPr0p3rty var rightTopColor: Color = Color.WHITE - @UIRenderPr0p3rty var rightBottomColor: Color = Color.WHITE - @UIRenderPr0p3rty var leftBottomColor: Color = Color.WHITE - - @UIRenderPr0p3rty var shade = false - +) : RectLayout(owner) { init { - properties.interactionPassthrough = true - onRender { filledRect( rect, @@ -56,46 +40,6 @@ class FilledRect( } } - fun setRadius(radius: Double) { - leftTopRadius = radius - rightTopRadius = radius - rightBottomRadius = radius - leftBottomRadius = radius - } - - fun setRadius( - leftTopRadius: Double, - rightTopRadius: Double, - rightBottomRadius: Double, - leftBottomRadius: Double, - ) { - this.leftTopRadius = leftTopRadius - this.rightTopRadius = rightTopRadius - this.rightBottomRadius = rightBottomRadius - this.leftBottomRadius = leftBottomRadius - } - - fun setColor(color: Color) { - leftTopColor = color - rightTopColor = color - rightBottomColor = color - leftBottomColor = color - } - - fun setColorH(colorL: Color, colorR: Color) { - leftTopColor = colorL - rightTopColor = colorR - rightBottomColor = colorR - leftBottomColor = colorL - } - - fun setColorV(colorT: Color, colorB: Color) { - leftTopColor = colorT - rightTopColor = colorT - rightBottomColor = colorB - leftBottomColor = colorB - } - companion object { /** * Creates a [FilledRect] component - layout-based rect representation diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/GlowRect.kt b/common/src/main/kotlin/com/lambda/gui/component/core/GlowRect.kt new file mode 100644 index 000000000..f0f6f610a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/component/core/GlowRect.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.gui.component.core + +import com.lambda.graphics.renderer.gui.RectRenderer.glowRect +import com.lambda.gui.component.layout.Layout + +class GlowRect( + owner: Layout +) : RectLayout(owner) { + @UIRenderPr0p3rty var outerSpread = 1.0 + @UIRenderPr0p3rty var innerSpread = 0.0 + + @UIRenderPr0p3rty var leftTopInnerRadius = 0.0 + @UIRenderPr0p3rty var rightTopInnerRadius = 0.0 + @UIRenderPr0p3rty var rightBottomInnerRadius = 0.0 + @UIRenderPr0p3rty var leftBottomInnerRadius = 0.0 + + init { + onRender { + glowRect( + rect, + outerSpread, + innerSpread, + + leftTopInnerRadius, + rightTopInnerRadius, + rightBottomInnerRadius, + leftBottomInnerRadius, + + leftTopRadius, + rightTopRadius, + rightBottomRadius, + leftBottomRadius, + + leftTopColor, + rightTopColor, + rightBottomColor, + leftBottomColor, + + shade + ) + } + } + + fun setInnerRadius(radius: Double) { + leftTopInnerRadius = radius + rightTopInnerRadius = radius + rightBottomInnerRadius = radius + leftBottomInnerRadius = radius + } + + fun setInnerRadiusH(radiusL: Double, radiusR: Double) { + leftTopInnerRadius = radiusL + rightTopInnerRadius = radiusR + rightBottomInnerRadius = radiusR + leftBottomInnerRadius = radiusL + } + + fun setInnerRadiusV(radiusT: Double, radiusB: Double) { + leftTopInnerRadius = radiusT + rightTopInnerRadius = radiusT + rightBottomInnerRadius = radiusB + leftBottomInnerRadius = radiusB + } + + companion object { + /** + * Creates a [GlowRect] component - layout-based rect representation + */ + @UIBuilder + fun Layout.glow( + block: GlowRect.() -> Unit = {} + ) = GlowRect(this).apply(children::add).apply(block) + + /** + * Adds a [GlowRect] behind given [layout] + */ + @UIBuilder + fun Layout.glowBehind( + layout: Layout, + block: GlowRect.() -> Unit = {} + ) = GlowRect(this).insertLayout(this, layout, false).apply(block) + + /** + * Adds a [GlowRect] over given [layout] + */ + @UIBuilder + fun Layout.glowOver( + layout: Layout, + block: GlowRect.() -> Unit = {} + ) = GlowRect(this).insertLayout(this, layout, true).apply(block) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt b/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt index 014f0fd1c..2109096ca 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt @@ -17,31 +17,23 @@ package com.lambda.gui.component.core -import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer.outlineRect +import com.lambda.graphics.renderer.gui.RectRenderer.outlineRect import com.lambda.gui.component.layout.Layout -import java.awt.Color class OutlineRect( owner: Layout -) : Layout(owner) { - @UIRenderPr0p3rty var roundRadius = 0.0 - @UIRenderPr0p3rty var glowRadius = 1.0 - - @UIRenderPr0p3rty var leftTopColor: Color = Color.WHITE - @UIRenderPr0p3rty var rightTopColor: Color = Color.WHITE - @UIRenderPr0p3rty var rightBottomColor: Color = Color.WHITE - @UIRenderPr0p3rty var leftBottomColor: Color = Color.WHITE - - @UIRenderPr0p3rty var shade = false +) : RectLayout(owner) { + @UIRenderPr0p3rty var outlineWidth = 1.0 init { - properties.interactionPassthrough = true - onRender { outlineRect( rect, - roundRadius, - glowRadius, + outlineWidth, + leftTopRadius, + rightTopRadius, + rightBottomRadius, + leftBottomRadius, leftTopColor, rightTopColor, rightBottomColor, @@ -51,30 +43,9 @@ class OutlineRect( } } - fun setColor(color: Color) { - leftTopColor = color - rightTopColor = color - rightBottomColor = color - leftBottomColor = color - } - - fun setColorH(colorL: Color, colorR: Color) { - leftTopColor = colorL - rightTopColor = colorR - rightBottomColor = colorR - leftBottomColor = colorL - } - - fun setColorV(colorT: Color, colorB: Color) { - leftTopColor = colorT - rightTopColor = colorT - rightBottomColor = colorB - leftBottomColor = colorB - } - companion object { /** - * Creates an [OutlineRect] component - layout-based rect representation + * Creates a [OutlineRect] component - layout-based rect representation */ @UIBuilder fun Layout.outline( @@ -91,7 +62,7 @@ class OutlineRect( ) = OutlineRect(this).insertLayout(this, layout, false).apply(block) /** - * Creates an [OutlineRect] component - layout-based rect representation + * Adds a [OutlineRect] over given [layout] */ @UIBuilder fun Layout.outlineOver( diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/RectLayout.kt b/common/src/main/kotlin/com/lambda/gui/component/core/RectLayout.kt new file mode 100644 index 000000000..29e66f37c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/component/core/RectLayout.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.gui.component.core + +import com.lambda.gui.component.layout.Layout +import java.awt.Color + +abstract class RectLayout( + owner: Layout +) : Layout(owner) { + @UIRenderPr0p3rty var leftTopRadius = 0.0 + @UIRenderPr0p3rty var rightTopRadius = 0.0 + @UIRenderPr0p3rty var rightBottomRadius = 0.0 + @UIRenderPr0p3rty var leftBottomRadius = 0.0 + + @UIRenderPr0p3rty var leftTopColor: Color = Color.WHITE + @UIRenderPr0p3rty var rightTopColor: Color = Color.WHITE + @UIRenderPr0p3rty var rightBottomColor: Color = Color.WHITE + @UIRenderPr0p3rty var leftBottomColor: Color = Color.WHITE + + @UIRenderPr0p3rty var shade = false + + init { + properties.interactionPassthrough = true + } + + fun setRadius(radius: Double) { + leftTopRadius = radius + rightTopRadius = radius + rightBottomRadius = radius + leftBottomRadius = radius + } + + fun setRadiusH(radiusL: Double, radiusR: Double) { + leftTopRadius = radiusL + rightTopRadius = radiusR + rightBottomRadius = radiusR + leftBottomRadius = radiusL + } + + fun setRadiusV(radiusT: Double, radiusB: Double) { + leftTopRadius = radiusT + rightTopRadius = radiusT + rightBottomRadius = radiusB + leftBottomRadius = radiusB + } + + fun setColor(color: Color) { + leftTopColor = color + rightTopColor = color + rightBottomColor = color + leftBottomColor = color + } + + fun setColorH(colorL: Color, colorR: Color) { + leftTopColor = colorL + rightTopColor = colorR + rightBottomColor = colorR + leftBottomColor = colorL + } + + fun setColorV(colorT: Color, colorB: Color) { + leftTopColor = colorT + rightTopColor = colorT + rightBottomColor = colorB + leftBottomColor = colorB + } +} diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt b/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt index 0f871695a..86cbfc6c5 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt @@ -17,8 +17,8 @@ package com.lambda.gui.component.core -import com.lambda.graphics.renderer.gui.font.FontRenderer -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString +import com.lambda.graphics.renderer.gui.FontRenderer +import com.lambda.graphics.renderer.gui.FontRenderer.drawString import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign import com.lambda.gui.component.layout.Layout diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt index e2fff4c53..791de159c 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt @@ -21,6 +21,7 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.module.modules.client.ClickGui import com.lambda.gui.RootLayout import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.GlowRect.Companion.glow import com.lambda.gui.component.core.LayoutBuilder import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.layout.Layout @@ -69,16 +70,28 @@ open class Window( val content = windowContent(scrollable) + val glowRect = glow { + onUpdate { + position = this@Window.position + size = this@Window.size + + setColor(ClickGui.glowColor) + setRadius(ClickGui.roundRadius) + + outerSpread = ClickGui.glowWidth * ClickGui.glow.toInt().toDouble() + shade = ClickGui.glowShade + } + } + val outlineRect = outline { onUpdate { position = this@Window.position size = this@Window.size setColor(ClickGui.outlineColor) + setRadius(ClickGui.roundRadius) - roundRadius = ClickGui.roundRadius - glowRadius = ClickGui.outlineWidth * ClickGui.outline.toInt().toDouble() - + outlineWidth = ClickGui.outlineWidth * ClickGui.outline.toInt().toDouble() shade = ClickGui.outlineShade } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt index de21ffc98..69c6392d2 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/AnimatedChild.kt @@ -153,7 +153,8 @@ abstract class AnimatedChild( listOf( titleBarBackground, contentBackground, - outlineRect + outlineRect, + glowRect ).forEach(Layout::destroy) } @@ -161,7 +162,7 @@ abstract class AnimatedChild( fun AnimatedChild.animatedBackground( enableProgress: () -> Double = { hoverAnimation } ) = rectBehind(titleBar) { - // base rect with lowest y to avoid children overlying + // base rect with lowest z to avoid children overlying onUpdate { val enableAnimation = enableProgress() @@ -205,12 +206,10 @@ abstract class AnimatedChild( size = base.size shade = base.shade - setRadius( - base.leftTopRadius, - base.rightTopRadius, - base.rightBottomRadius, - base.leftBottomRadius - ) + leftTopRadius = base.leftTopRadius + rightTopRadius = base.rightTopRadius + rightBottomRadius = base.rightBottomRadius + leftBottomRadius = base.leftBottomRadius val hoverColor = Color.WHITE.setAlpha( ClickGui.moduleHoverAccent * hoverAnimation * (1.0 - openAnimation) * showAnimation diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt index ca8ac731a..407108631 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/core/SliderLayout.kt @@ -113,8 +113,9 @@ class SliderLayout( rect = bg.rect val c = Color.BLACK.setAlpha(0.3 * showAnim) val a = transform(renderProgress, 0.5, 1.0, 0.0, 1.0).coerceIn(0.0, 1.0) + outlineWidth = 0.5 setColorH(c, c.multAlpha(a)) - roundRadius = 100.0 + setRadius(100.0) } } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt index 9ba0d5fe0..8fd8c770e 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/BooleanButton.kt @@ -20,7 +20,7 @@ package com.lambda.gui.impl.clickgui.module.setting.settings import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.module.modules.client.ClickGui import com.lambda.gui.component.core.FilledRect.Companion.rect -import com.lambda.gui.component.core.OutlineRect.Companion.outline +import com.lambda.gui.component.core.GlowRect.Companion.glow import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.module.setting.SettingLayout @@ -82,11 +82,11 @@ class BooleanButton( } } - outline { + glow { onUpdate { rect = checkBox.rect - roundRadius = 100.0 - glowRadius = 5.0 + setRadius(100.0) + outerSpread = 5.0 setColor(Color.BLACK.setAlpha(0.1 * showAnimation)) } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt index d9bc65c2d..192fc0736 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/ColorPicker.kt @@ -97,8 +97,8 @@ class ColorPicker( outline { onUpdate { rect = preview.rect - roundRadius = 100.0 - glowRadius = 5.0 + setRadius(100.0) + outlineWidth = 0.5 setColor(Color.BLACK.setAlpha(0.1 * showAnimation)) } } @@ -133,8 +133,8 @@ class ColorPicker( width = knobSize; height = knobSize setColor(Color.BLACK) - glowRadius = 1.0 - roundRadius = 100.0 + outlineWidth = 1.0 + setRadius(100.0) onUpdate { val uv = if (circle.pressedButton == Mouse.Button.Left) clampToCircle(Vec2d(getU(), getV())) diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 91dd5ecc2..3a5644096 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -23,6 +23,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.animation.AnimationTicker import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.GlowRect.Companion.glow import com.lambda.gui.component.core.OutlineRect.Companion.outline import com.lambda.gui.component.core.TextField.Companion.textField import com.lambda.gui.component.core.UIBuilder @@ -64,16 +65,28 @@ abstract class HudModule( } } + glow { + onUpdate { + position = this@apply.position + size = this@apply.size + + setColor(ClickGui.glowColor) + setRadius(ClickGui.roundRadius) + + outerSpread = ClickGui.glowWidth * ClickGui.glow.toInt().toDouble() + shade = ClickGui.glowShade + } + } + outline { onUpdate { position = this@apply.position size = this@apply.size setColor(ClickGui.outlineColor) + setRadius(ClickGui.roundRadius) - roundRadius = ClickGui.roundRadius - glowRadius = ClickGui.outlineWidth * ClickGui.outline.toInt().toDouble() - + outlineWidth = ClickGui.outlineWidth * ClickGui.outline.toInt().toDouble() shade = ClickGui.outlineShade } } @@ -111,7 +124,6 @@ abstract class HudModule( onEnable { base.onEvent(GuiEvent.Show) - println(base.buildTree()) } onDisable { diff --git a/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt index 650995781..a29a087e2 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TickShiftCharge.kt @@ -18,19 +18,9 @@ package com.lambda.module.hud import com.lambda.graphics.animation.Animation.Companion.exp -import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer -import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer.filledRect -import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer -import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer.outlineRect import com.lambda.module.HudModule -import com.lambda.module.modules.client.ClickGui -import com.lambda.module.modules.client.GuiSettings -import com.lambda.module.modules.client.GuiSettings.primaryColor import com.lambda.module.modules.movement.TickShift import com.lambda.module.tag.ModuleTag -import com.lambda.util.math.Rect -import com.lambda.util.math.multAlpha -import java.awt.Color object TickShiftCharge : HudModule( name = "TickShiftCharge", 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 3f326670f..3c7ccacbe 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 @@ -25,6 +25,7 @@ import com.lambda.gui.RootLayout.Companion.gui import com.lambda.gui.component.HAlign import com.lambda.gui.component.VAlign import com.lambda.gui.component.core.FilledRect.Companion.rect +import com.lambda.gui.component.core.GlowRect.Companion.glow import com.lambda.gui.component.layout.Layout import com.lambda.gui.impl.clickgui.ModuleWindow.Companion.moduleWindow import com.lambda.gui.impl.clickgui.core.AnimatedChild.Companion.animatedBackground @@ -53,15 +54,24 @@ object ClickGui : Module( val roundRadius by setting("Round Radius", 3.0, 0.0..10.0, 0.1) val backgroundTint by setting("Background Tint", Color.BLACK.setAlpha(0.4)) + val backgroundCornerTint by setting("Background Corner Tint", Color.WHITE.setAlpha(0.4)) + val cornerTintWidth by setting("Corner Tint Width", 100.0, 1.0..500.0, 0.1) + val cornerTintShade by setting("Corner Tint Shade", true) val titleBackgroundColor by setting("Title Background Color", Color(80, 80, 80)) val backgroundColor by setting("Background Color", titleBackgroundColor) val backgroundShade by setting("Background Shade", true) val outline by setting("Outline", true) - val outlineWidth by setting("Outline Width", 10.0, 1.0..20.0, 0.1) { outline } - val outlineColor by setting("Outline Color", Color.WHITE.setAlpha(0.6)) { outline } + val outlineWidth by setting("Outline Width", 0.5, 0.5..5.0, 0.1) { outline } + val outlineColor by setting("Outline Color", Color.WHITE) { outline } val outlineShade by setting("Outline Shade", true) { outline } + + val glow by setting("Glow", true) + val glowWidth by setting("Glow Width", 8.0, 1.0..20.0, 0.1) { glow } + val glowColor by setting("Glow Color", Color.WHITE.setAlpha(0.35)) { glow } + val glowShade by setting("Glow Shade", true) { glow } + val fontScale by setting("Font Scale", 1.0, 0.5..2.0, 0.1) val fontOffset by setting("Font Offset", 4.0, 0.0..5.0, 0.1) val dockingGridSize by setting("Docking Grid Size", 1.0, 0.1..10.0, 0.1) @@ -72,10 +82,10 @@ object ClickGui : Module( val moduleOpenAccent by setting("Module Open Accent", 0.3, 0.0..0.5, 0.01) val multipleSettingWindows by setting("Multiple Setting Windows", false) - val animationCurve by setting("List Animation Curve", AnimationCurve.Normal) + val animationCurve by setting("List Animation Curve", AnimationCurve.Reverse) val smoothness by setting("Smoothness", 0.4, 0.3..0.7, 0.01) { animationCurve != AnimationCurve.Static } - val hudPadding by setting("Hud Padding", 2.0, 0.0..10.0, 0.1) + val hudPadding by setting("Hud Padding", 3.0, 0.0..10.0, 0.1) val SCREEN: LambdaScreen by lazy { gui("Click Gui") { @@ -91,6 +101,17 @@ object ClickGui : Module( } } + glow { + onUpdate { + rect = owner!!.rect + innerSpread = cornerTintWidth + shade = cornerTintShade + + setColor(backgroundCornerTint) + setInnerRadius(cornerTintWidth) + } + } + var x = 10.0 val y = x diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt index b97bca99b..f3a8d16ee 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt @@ -20,7 +20,7 @@ package com.lambda.module.modules.client import com.lambda.Lambda.mc import com.lambda.event.events.RenderEvent import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawGlyph +import com.lambda.graphics.renderer.gui.FontRenderer.drawGlyph import com.lambda.graphics.renderer.gui.font.core.GlyphInfo import com.lambda.graphics.renderer.gui.font.core.LambdaAtlas.get import com.lambda.module.Module diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 466fff405..9c2cd573c 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -18,7 +18,6 @@ package com.lambda.module.modules.combat import com.lambda.Lambda -import com.lambda.Lambda.mc import com.lambda.config.groups.RotationSettings import com.lambda.config.groups.Targeting import com.lambda.context.SafeContext @@ -29,8 +28,8 @@ import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.buildWorldProjection import com.lambda.graphics.gl.Matrices.withVertexTransform -import com.lambda.graphics.renderer.gui.font.FontRenderer -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString +import com.lambda.graphics.renderer.gui.FontRenderer +import com.lambda.graphics.renderer.gui.FontRenderer.drawString import com.lambda.interaction.request.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.request.rotation.RotationManager import com.lambda.interaction.request.rotation.visibilty.VisibilityChecker.getVisibleSurfaces diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl index 6cb03c8a1..7ee5ec204 100644 --- a/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_filled.glsl @@ -4,27 +4,10 @@ attributes { vec4 color; }; -uniforms { - vec2 u_Size; # fragment - float u_RoundLeftTop; # fragment - float u_RoundLeftBottom; # fragment - float u_RoundRightBottom; # fragment - float u_RoundRightTop; # fragment -}; - #include "rect" -#define SMOOTHING 0.5 - -float roundedRectSDF() { - vec4 r = vec4(u_RoundRightBottom, u_RoundRightTop, u_RoundLeftBottom, u_RoundLeftTop); - r.xy = (v_TexCoord.x > 0.5) ? r.xy : r.zw; - r.x = (v_TexCoord.y > 0.5) ? r.x : r.y; - - vec2 q = u_Size * (abs(v_TexCoord - 0.5) - 0.5) + r.x; - return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x; -}# void fragment() { - float a = 1.0 - smoothstep(-SMOOTHING, 0.0, roundedRectSDF()); - color = v_Color * vec4(shade.rgb, shade.a * a) + noise; + float distance = signedDistance(); + float alpha = 1 - smoothstep(-SMOOTHING, SMOOTHING, distance); + color = v_Color * vec4(1.0, 1.0, 1.0, alpha) * shade + noise; }# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_glow.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_glow.glsl new file mode 100644 index 000000000..094f9c373 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_glow.glsl @@ -0,0 +1,40 @@ +attributes { + vec4 pos; + vec2 uv; + vec4 color; +}; + +uniforms { + float u_RectWidth; # fragment + float u_InnerRectWidth; # fragment + + float u_InnerRoundLeftTop; # fragment + float u_InnerRoundLeftBottom; # fragment + float u_InnerRoundRightBottom; # fragment + float u_InnerRoundRightTop; # fragment +}; + +#include "rect" + +void fragment() { + float distance = signedDistance(); + float innerDistance = signedDistance(vec4( + u_InnerRoundRightBottom, + u_InnerRoundRightTop, + u_InnerRoundLeftBottom, + u_InnerRoundLeftTop + )); + + float bloomDistance = distance; + if (innerDistance > 0.0 && distance < 0.0) bloomDistance = 0.0; + if (innerDistance < 0.0) bloomDistance = innerDistance; + + float bloomSpread = ((bloomDistance > 0.0) ? u_RectWidth : -u_InnerRectWidth); + float bloomAlpha = 1.0 / (1.0 + bloomDistance / bloomSpread); + + float glowAlpha = + smoothstep(-u_InnerRectWidth, 0.0, bloomDistance) - + smoothstep(0.0, u_RectWidth, bloomDistance); + + color = v_Color * vec4(1.0, 1.0, 1.0, bloomAlpha * glowAlpha) * shade + noise; +}# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl b/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl index a8b62b9c8..d27ac5873 100644 --- a/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl +++ b/common/src/main/resources/assets/lambda/shaders/renderer/rect_outline.glsl @@ -1,22 +1,18 @@ attributes { vec4 pos; vec2 uv; - float alpha; vec4 color; }; -export { - float v_Alpha; # alpha +uniforms { + float u_RectWidth; # fragment }; #include "rect" -#define glow glowAlpha() - -vec4 glowAlpha() { - float newAlpha = min(1.0, v_Alpha * v_Alpha * v_Alpha); - return vec4(1.0, 1.0, 1.0, newAlpha); -}# void fragment() { - color = v_Color * shade * glow; + float distance = signedDistance(); + float innerAlpha = smoothstep(-u_RectWidth - SMOOTHING, -u_RectWidth + SMOOTHING, distance); + float outerAlpha = 1 - smoothstep(u_RectWidth - SMOOTHING, u_RectWidth + SMOOTHING, distance); + color = v_Color * vec4(1.0, 1.0, 1.0, min(innerAlpha, outerAlpha)) * shade + noise; }# \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl b/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl index 576b66c18..4fe1b2220 100644 --- a/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl +++ b/common/src/main/resources/assets/lambda/shaders/shared/rect.glsl @@ -1,5 +1,16 @@ attributes { + vec4 pos; vec2 uv; + vec4 color; +}; + +uniforms { + vec2 u_Size; # fragment + + float u_RoundLeftTop; # fragment + float u_RoundLeftBottom; # fragment + float u_RoundRightBottom; # fragment + float u_RoundRightTop; # fragment }; export { @@ -10,6 +21,8 @@ export { #include "shade" #define NOISE_GRANULARITY 0.004 +#define SMOOTHING 0.3 + #define noise getNoise() vec4 getNoise() { @@ -17,4 +30,16 @@ vec4 getNoise() { 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); -}# \ No newline at end of file +}# + +float signedDistance(in vec4 r) { + r.xy = (v_TexCoord.x > 0.5) ? r.xy : r.zw; + r.x = (v_TexCoord.y > 0.5) ? r.x : r.y; + + vec2 q = u_Size * (abs(v_TexCoord - 0.5) - 0.5) + r.x; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x; +}# + +float signedDistance() { + return signedDistance(vec4(u_RoundRightBottom, u_RoundRightTop, u_RoundLeftBottom, u_RoundLeftTop)); +}# From 5487fd7e1d912e66b19de0e9a07ea75ae8b2fe9a Mon Sep 17 00:00:00 2001 From: "blade.kt" Date: Mon, 31 Mar 2025 03:50:14 +0300 Subject: [PATCH 20/25] Fixed ESP rendering --- .../kotlin/com/lambda/graphics/RenderMain.kt | 18 ++++++++++++++++++ .../graphics/renderer/esp/global/DynamicESP.kt | 15 +-------------- .../graphics/renderer/esp/global/StaticESP.kt | 15 +-------------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 085dd205f..9681c93af 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -20,9 +20,13 @@ package com.lambda.graphics import com.lambda.Lambda.mc import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrices +import com.lambda.graphics.renderer.esp.global.DynamicESP +import com.lambda.graphics.renderer.esp.global.StaticESP import com.lambda.module.modules.client.GuiSettings import com.lambda.util.Communication.info import com.lambda.util.math.Vec2d @@ -58,6 +62,20 @@ object RenderMain { setupGL { RenderEvent.World().post() + StaticESP.render() + DynamicESP.render() + } + } + + init { + listen { + StaticESP.clear() + RenderEvent.StaticESP().post() + StaticESP.upload() + + DynamicESP.clear() + RenderEvent.DynamicESP().post() + DynamicESP.upload() } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt index 9c713562e..e7d33ad69 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/DynamicESP.kt @@ -17,19 +17,6 @@ package com.lambda.graphics.renderer.esp.global -import com.lambda.event.EventFlow.post -import com.lambda.event.events.RenderEvent -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.ESPRenderer import com.lambda.graphics.renderer.esp.impl.DynamicESPRenderer -object DynamicESP : DynamicESPRenderer() { - init { - listen { - clear() - RenderEvent.StaticESP().post() - upload() - } - } -} +object DynamicESP : DynamicESPRenderer() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt index 237650fa0..6d905dd5a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/global/StaticESP.kt @@ -17,19 +17,6 @@ package com.lambda.graphics.renderer.esp.global -import com.lambda.event.EventFlow.post -import com.lambda.event.events.RenderEvent -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.graphics.renderer.esp.ESPRenderer import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer -object StaticESP : StaticESPRenderer() { - init { - listen { - clear() - RenderEvent.StaticESP().post() - upload() - } - } -} +object StaticESP : StaticESPRenderer() From c41b849ce70b2e399b97b990068f414ec894d3f1 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 29 Mar 2025 12:52:18 -0400 Subject: [PATCH 21/25] Fixed emoji suggestion --- .../mixin/render/ChatInputSuggestorMixin.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java b/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java index 170b025e1..9f3cf4c9a 100644 --- a/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java @@ -40,6 +40,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -87,10 +88,22 @@ private void refreshEmojiSuggestion(CallbackInfo ci) { int start = neoLambda$getLastColon(textToCursor); if (start == -1) return; - String emojiString = typing.substring(start + 1); + Matcher emojiMatcher = EMOJI_PATTERN.matcher(textToCursor); + Map emojiKeys = LambdaAtlas.INSTANCE.getKeys(RenderSettings.INSTANCE.getEmojiFont()); + while (emojiMatcher.find()) { + int openingColon = emojiMatcher.start(1); + String key = emojiMatcher.group(2); + int closingColon = emojiMatcher.end(3); + + if (emojiKeys.containsKey(key) && start >= openingColon && start < closingColon) { + // If the colon is part of a previous valid emoji, return + return; + } + } + + String emojiString = textToCursor.substring(start + 1); - Stream results = LambdaAtlas.INSTANCE.getKeys(RenderSettings.INSTANCE.getEmojiFont()) - .keySet().stream() + Stream results = emojiKeys.keySet().stream() .filter(s -> s.startsWith(emojiString)) .map(s -> s + ":"); @@ -105,6 +118,9 @@ private void refreshEmojiSuggestion(CallbackInfo ci) { @Unique private static final Pattern COLON_PATTERN = Pattern.compile("(:[a-zA-Z0-9_]+)"); + @Unique + private static final Pattern EMOJI_PATTERN = Pattern.compile("(:)([a-zA-Z0-9_]+)(:)"); + @Unique private int neoLambda$getLastColon(String input) { if (Strings.isNullOrEmpty(input)) return -1; From 394a8bc4ce7a3beba59c590c5cacdcec3bf0e104 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 29 Mar 2025 12:52:39 -0400 Subject: [PATCH 22/25] Aligned emojis vertically --- .../lambda/module/modules/client/LambdaMoji.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt index f3a8d16ee..8ce70690d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt @@ -37,12 +37,8 @@ object LambdaMoji : Module( defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.RENDER), enabledByDefault = true, ) { - val scale by setting("Emoji Scale", 1.0, 0.5..1.5, 0.1) val suggestions by setting("Chat Suggestions", true) - private val emojiWhitespace: String - get() = " ".repeat(((mc.textRenderer.fontHeight / 2 / mc.textRenderer.getWidth(" ")) * scale).toInt()) - private val renderQueue = mutableListOf>() init { @@ -73,7 +69,6 @@ object LambdaMoji : Module( val index = raw.indexOf(emoji) if (index == -1) return@forEach - val height = mc.textRenderer.fontHeight val width = mc.textRenderer.getWidth(raw.substring(0, index)) // Dude I'm sick of working with the shitcode that is minecraft's codebase :sob: @@ -82,19 +77,19 @@ object LambdaMoji : Module( else -> Color(255, 255, 255, (color shr 24 and 0xFF)) } - val glyph = RenderSettings.emojiFont[emoji]!! - renderQueue.add(Triple(glyph, Vec2d(x + width, y + height / 2), trueColor)) + val glyph = RenderSettings.emojiFont[emoji] ?: return@forEach + renderQueue.add(Triple(glyph, Vec2d(x + width, y), trueColor)) // Replace the emoji with whitespaces depending on the player's settings - raw = raw.replaceFirst(emoji, emojiWhitespace) + raw = raw.replaceFirst(emoji, " ") } val constructed = mutableListOf() // Will not work properly if the emoji is part of the style - saved.forEach { (charIndex: Int, style: Style) -> - if (charIndex >= raw.length) return@forEach - constructed.add(OrderedText.styledForwardsVisitedString(raw.substring(charIndex, charIndex + 1), style)) + saved.forEach { (index, style) -> + if (index >= raw.length) return@forEach + constructed.add(OrderedText.styledForwardsVisitedString(raw.substring(index, index + 1), style)) } return OrderedText.concat(constructed) From 2b167e1e9fa84034651a63645eb18ce0e30a15b5 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:12:39 -0400 Subject: [PATCH 23/25] Fixed no method exception --- common/src/main/kotlin/com/lambda/module/HudModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 3a5644096..b22ee6c95 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -115,7 +115,7 @@ abstract class HudModule( listen { while (scheduled.isNotEmpty()) { - scheduled.removeFirst().invoke() + scheduled.removeAt(0).invoke() } base.onEvent(GuiEvent.Update) From 1eeddc0d724976561307b94ad83aa4fc3810e126 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 29 Mar 2025 17:24:11 -0400 Subject: [PATCH 24/25] Better use of pbo --- .../com/lambda/graphics/buffer/Buffer.kt | 91 ++++++++++--------- .../graphics/buffer/pixel/PixelBuffer.kt | 31 +++++-- .../kotlin/com/lambda/graphics/gl/Memory.kt | 2 +- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt index c92dd313a..28f7b4872 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt @@ -40,17 +40,17 @@ abstract class Buffer( /** * Specifies how the buffers are used * - * | Buffer Usage | Description | - * |--------------------------------|-----------------------------------------------------------------| - * | GL_STREAM_DRAW | Data is set once and used a few times for drawing. | - * | GL_STREAM_READ | Data is set once and used a few times for reading. | - * | GL_STREAM_COPY | Data is set once and used a few times for copying. | - * | GL_STATIC_DRAW | Data is set once and used many times for drawing. | - * | GL_STATIC_READ | Data is set once and used many times for reading. | - * | GL_STATIC_COPY | Data is set once and used many times for copying. | - * | GL_DYNAMIC_DRAW | Data is modified repeatedly and used many times for drawing. | - * | GL_DYNAMIC_READ | Data is modified repeatedly and used many times for reading. | - * | GL_DYNAMIC_COPY | Data is modified repeatedly and used many times for copying. | + * | Buffer Usage | Description | + * |----------------------------------|-----------------------------------------------------------------| + * | [GL_STREAM_DRAW] | Data is set once and used a few times for drawing. | + * | [GL_STREAM_READ] | Data is set once and used a few times for reading. | + * | [GL_STREAM_COPY] | Data is set once and used a few times for copying. | + * | [GL_STATIC_DRAW] | Data is set once and used many times for drawing. | + * | [GL_STATIC_READ] | Data is set once and used many times for reading. | + * | [GL_STATIC_COPY] | Data is set once and used many times for copying. | + * | [GL_DYNAMIC_DRAW] | Data is modified repeatedly and used many times for drawing. | + * | [GL_DYNAMIC_READ] | Data is modified repeatedly and used many times for reading. | + * | [GL_DYNAMIC_COPY] | Data is modified repeatedly and used many times for copying. | * * @see Buffer object */ @@ -60,22 +60,22 @@ abstract class Buffer( * Specifies the target to which the buffer object is bound which must be one * of the following: * - * | Buffer Binding Target | Purpose | - * |-------------------------------|--------------------------------------| - * | GL_ARRAY_BUFFER | Vertex attributes | - * | GL_ATOMIC_COUNTER_BUFFER | Atomic counter storage | - * | GL_COPY_READ_BUFFER | Buffer copy source | - * | GL_COPY_WRITE_BUFFER | Buffer copy destination | - * | GL_DISPATCH_INDIRECT_BUFFER | Indirect compute dispatch commands | - * | GL_DRAW_INDIRECT_BUFFER | Indirect command arguments | - * | GL_ELEMENT_ARRAY_BUFFER | Vertex array indices | - * | GL_PIXEL_PACK_BUFFER | Pixel read target | - * | GL_PIXEL_UNPACK_BUFFER | Texture data source | - * | GL_QUERY_BUFFER | Query result buffer | - * | GL_SHADER_STORAGE_BUFFER | Read-write storage for shaders | - * | GL_TEXTURE_BUFFER | Texture data buffer | - * | GL_TRANSFORM_FEEDBACK_BUFFER | Transform feedback buffer | - * | GL_UNIFORM_BUFFER | Uniform block storage | + * | Buffer Binding Target | Purpose | + * |---------------------------------|--------------------------------------| + * | [GL_ARRAY_BUFFER] | Vertex attributes | + * | [GL_ATOMIC_COUNTER_BUFFER] | Atomic counter storage | + * | [GL_COPY_READ_BUFFER] | Buffer copy source | + * | [GL_COPY_WRITE_BUFFER] | Buffer copy destination | + * | [GL_DISPATCH_INDIRECT_BUFFER] | Indirect compute dispatch commands | + * | [GL_DRAW_INDIRECT_BUFFER] | Indirect command arguments | + * | [GL_ELEMENT_ARRAY_BUFFER] | Vertex array indices | + * | [GL_PIXEL_PACK_BUFFER] | Pixel read target | + * | [GL_PIXEL_UNPACK_BUFFER] | Texture data source | + * | [GL_QUERY_BUFFER] | Query result buffer | + * | [GL_SHADER_STORAGE_BUFFER] | Read-write storage for shaders | + * | [GL_TEXTURE_BUFFER] | Texture data buffer | + * | [GL_TRANSFORM_FEEDBACK_BUFFER] | Transform feedback buffer | + * | [GL_UNIFORM_BUFFER] | Uniform block storage | * * @see Buffer object */ @@ -85,16 +85,18 @@ abstract class Buffer( * Specifies a combination of access flags indicating the desired * access to the mapping range and must contain one or more of the following: * - * | Flag | Description | Disclaimer | - * |-------------------------------|-----------------------------------------------------|---------------------------------------------------------------| - * | GL_MAP_READ_BIT | Allows reading buffer data. | Undefined if used without this flag. | - * | GL_MAP_WRITE_BIT | Allows modifying buffer data. | Undefined if used without this flag. | - * | GL_MAP_PERSISTENT_BIT | Enables persistent mapping during GL operations. | Requires proper allocation with GL_MAP_PERSISTENT_BIT. | - * | GL_MAP_COHERENT_BIT | Ensures changes are visible without extra steps. | Without this, explicit sync is needed. | - * | GL_MAP_INVALIDATE_RANGE_BIT | Discards previous contents of the mapped range. | Cannot be used with GL_MAP_READ_BIT. | - * | GL_MAP_INVALIDATE_BUFFER_BIT | Discards previous contents of the entire buffer. | Cannot be used with GL_MAP_READ_BIT. | - * | GL_MAP_FLUSH_EXPLICIT_BIT | Requires explicit flushing of modified sub-ranges. | Only with GL_MAP_WRITE_BIT. Data may be undefined if skipped. | - * | GL_MAP_UNSYNCHRONIZED_BIT | Skips synchronization before mapping. | May cause data corruption if regions overlap. | + * | Flag | Description | Information | + * |--------------------------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------| + * | [GL_MAP_READ_BIT] | Allows reading buffer data. | Buffer must be created with this flag. Undefined if not included in access. | + * | [GL_MAP_WRITE_BIT] | Allows modifying buffer data. | Buffer must be created with this flag. Undefined if not included in access. | + * | [GL_MAP_PERSISTENT_BIT] | Enables persistent mapping during GL operations. | Requires buffer to be created with [GL_MAP_PERSISTENT_BIT]. | + * | [GL_MAP_COHERENT_BIT] | Ensures changes are visible to the GPU. | Requires buffer creation with [GL_MAP_PERSISTENT_BIT] or explicit sync. | + * | [GL_MAP_INVALIDATE_RANGE_BIT] | Discards previous contents of the mapped range. | Cannot be used with [GL_MAP_READ_BIT]. | + * | [GL_MAP_INVALIDATE_BUFFER_BIT] | Discards previous contents of the entire buffer. | Cannot be used with [GL_MAP_READ_BIT]. | + * | [GL_MAP_FLUSH_EXPLICIT_BIT] | Requires explicit flushing of modified sub-ranges. | Only valid with [GL_MAP_WRITE_BIT]. Data may be undefined if flushing is skipped. | + * | [GL_MAP_UNSYNCHRONIZED_BIT] | Skips synchronization before mapping. | May cause data corruption if buffer is accessed concurrently. | + * | [GL_DYNAMIC_STORAGE_BIT] | Allows updates via [glBufferSubData]. | If omitted, [glBufferSubData] will fail. | + * | [GL_CLIENT_STORAGE_BIT] | Hints that the buffer should prefer client storage. | Implementation-dependent optimization. | * * @see Buffer object */ @@ -257,8 +259,6 @@ abstract class Buffer( /** * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and then unmaps the buffer. * - * This function does not handle the binding. - * * If [access] contains the `GL_MAP_PERSISTENT_BIT` flag, the buffer will not be unmapped. * * @param size Specifies the length of the range to be mapped. @@ -269,6 +269,7 @@ abstract class Buffer( */ open fun map(size: Long, offset: Long, block: (ByteBuffer) -> Unit = {}): ByteBuffer { validate() + bind() check(offset >= 0 || size >= 0) { "Invalid offset or size parameter offset: $offset size: $size." } @@ -276,19 +277,17 @@ abstract class Buffer( check(offset + size <= glGetBufferParameteri(target, GL_BUFFER_SIZE)) { "Out of bound (is the buffer initialized?) $size + $offset > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}." } - check(glGetInteger(bindingCheckMappings.getValue(target)) == GL_TRUE) - { "Target is zero bound." } - check(glGetBufferParameteri(target, GL_BUFFER_MAPPED) == GL_FALSE) { "Buffer is already mapped." } - check(access and GL_MAP_WRITE_BIT != 0 && access and GL_MAP_READ_BIT != 0) + check(access and GL_MAP_WRITE_BIT != 0 || access and GL_MAP_READ_BIT != 0) { "Neither GL_MAP_READ_BIT nor GL_MAP_WRITE_BIT is set." } - check(access and GL_MAP_READ_BIT != 0 && + check((access and GL_MAP_READ_BIT != 0 && (access and GL_MAP_INVALIDATE_RANGE_BIT != 0 || access and GL_MAP_INVALIDATE_BUFFER_BIT != 0 || - access and GL_MAP_UNSYNCHRONIZED_BIT != 0) + access and GL_MAP_UNSYNCHRONIZED_BIT != 0)) || + access and GL_MAP_WRITE_BIT != 0 ) { "GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set." } @@ -302,6 +301,8 @@ abstract class Buffer( throw IllegalStateException("An unknown error occurred due to GPU memory availability of buffer corruption.") } + bind(0) + return sharedRegion } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt index ea5d2f3cd..179f9477e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt @@ -20,6 +20,7 @@ package com.lambda.graphics.buffer.pixel import com.lambda.graphics.buffer.Buffer import com.lambda.graphics.gl.putTo import com.lambda.graphics.texture.Texture +import com.lambda.threading.runSafeGameScheduled import com.lambda.util.math.MathUtils.toInt import org.lwjgl.opengl.GL45C.* import java.nio.ByteBuffer @@ -31,25 +32,38 @@ import java.nio.ByteBuffer * Functions that perform an upload operation, a pixel unpack, will use the buffer object bound to the target GL_PIXEL_UNPACK_BUFFER. * If a buffer is bound, then the pointer value that those functions take is not a pointer, but an offset from the beginning of that buffer. * + * Asynchronous should only be used for medium-size blocks of data being updated one time or less per frame + * Persistent should only be used for streaming operations with large blocks of data updated once or more per frame + * * @property texture The [Texture] instance to use * @property asynchronous Whether to use 2 buffers or not - * @property bufferMapping Whether to map a block in memory to upload or not + * @property persistent Whether to map a block in memory to upload or not * - * @see Reference + * @see Pixel buffer object */ class PixelBuffer( private val texture: Texture, private val asynchronous: Boolean = false, - private val bufferMapping: Boolean = false, + private val persistent: Boolean = false, ) : Buffer(buffers = asynchronous.toInt() + 1) { override val usage = GL_STATIC_DRAW override val target = GL_PIXEL_UNPACK_BUFFER - override val access = GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT or GL_MAP_PERSISTENT_BIT + override val access = + if (persistent) GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT or GL_MAP_PERSISTENT_BIT or GL_MAP_COHERENT_BIT + else GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT private val channels = channelMapping[texture.format] ?: throw IllegalArgumentException("Invalid image format, expected OpenGL format, got ${texture.format} instead") private val size = texture.width * texture.height * channels * 1L +// private var sharedRegion: ByteBuffer? = null override fun upload(data: ByteBuffer, offset: Long) { + if (!asynchronous) { + glBindTexture(GL_TEXTURE_2D, texture.id) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture.width, texture.height, texture.format, GL_UNSIGNED_BYTE, data) + glBindTexture(GL_TEXTURE_2D, 0) + return + } + bind() glBindTexture(GL_TEXTURE_2D, texture.id) @@ -69,8 +83,9 @@ class PixelBuffer( swap() bind() - if (bufferMapping) map(size, offset, data::putTo) - else update(data, offset) +// if (persistent) data.putTo(sharedRegion) +// else update(data, offset) + update(data, offset) bind(0) } @@ -87,6 +102,10 @@ class PixelBuffer( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) storage(size) + +// bind() +// sharedRegion = if (persistent) map(size, 0) else null +// bind(0) } companion object { 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 efc87727a..4607d3f02 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt @@ -35,4 +35,4 @@ val Long.kibibyte get() = this * 1024 val Long.mebibyte get() = this * 1024 * 1024 val Long.gibibyte get() = this * 1024 * 1024 * 1024 -fun ByteBuffer.putTo(dst: ByteBuffer) { dst.put(this) } +fun ByteBuffer.putTo(dst: ByteBuffer?) { dst?.put(this) } From a19203f5f4b03190dac9904eb013334d5d8c6bd0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 3 Apr 2025 03:16:53 +0200 Subject: [PATCH 25/25] Fix rebase conflicts --- .../module/setting/settings/EnumSlider.kt | 1 - .../com/lambda/module/hud/Coordinates.kt | 18 ++---------------- .../main/kotlin/com/lambda/module/hud/TPS.kt | 13 ------------- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt index 585a3b35b..a0632337e 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/module/setting/settings/EnumSlider.kt @@ -22,7 +22,6 @@ import com.lambda.config.settings.comparable.EnumSetting.Companion.enumValues import com.lambda.gui.component.core.UIBuilder import com.lambda.gui.component.layout.Layout import com.lambda.util.NamedEnum -import com.lambda.gui.impl.clickgui.module.settings.SettingSlider import com.lambda.gui.impl.clickgui.module.setting.SettingSlider import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.transform diff --git a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt index 0736c6ad7..370ffaaad 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -18,9 +18,7 @@ package com.lambda.module.hud import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.gui.font.FontRenderer -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString -import com.lambda.gui.component.core.TextField.Companion.textField +import com.lambda.graphics.renderer.gui.FontRenderer import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe @@ -39,19 +37,7 @@ object Coordinates : HudModule.Text( private val showDimension by setting("Show Dimension", true) private val decimals by setting("Decimals", 2, 0..4, 1) - private val text: String - get() = runSafe { "XYZ ${if (showDimension) dimensionName else ""} ${positionForDimension()}" } ?: "" - - override val height: Double get() = FontRenderer.getHeight() - override val width: Double get() = FontRenderer.getWidth(text) - - init { - onRender { - runSafe { - drawString(text, position) - } - } - } + override fun getText() = runSafe { "XYZ ${if (showDimension) dimensionName else ""} ${positionForDimension()}" } ?: "" private fun SafeContext.positionForDimension() = when { diff --git a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt index e5a6fe516..f89ba5fcb 100644 --- a/common/src/main/kotlin/com/lambda/module/hud/TPS.kt +++ b/common/src/main/kotlin/com/lambda/module/hud/TPS.kt @@ -17,8 +17,6 @@ package com.lambda.module.hud -import com.lambda.graphics.renderer.gui.font.FontRenderer -import com.lambda.graphics.renderer.gui.font.FontRenderer.drawString import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.util.Formatting.string @@ -35,17 +33,6 @@ object TPS : HudModule.Text( override fun getText() = "${format.displayName}: ${format.output().string}${format.unit}" - // TODO: Replace by LambdaAtlas height cache and actually build a proper text with highlighted parameters - - override val height: Double get() = FontRenderer.getHeight() - override val width: Double get() = FontRenderer.getWidth(text) - - init { - onRender { - drawString(text, position) - } - } - @Suppress("unused") private enum class TickFormat( val output: () -> Double,