Skip to content

Commit fc59c12

Browse files
committed
Better animations and code structure
1 parent a936f9c commit fc59c12

File tree

16 files changed

+213
-90
lines changed

16 files changed

+213
-90
lines changed

common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,38 @@ import com.lambda.module.modules.client.HUD
55
import com.lambda.util.math.MathUtils.ceilToInt
66
import com.lambda.util.math.MathUtils.floorToInt
77
import com.lambda.util.math.Rect
8-
import com.lambda.util.math.Vec2d
98
import com.mojang.blaze3d.systems.RenderSystem.disableScissor
109
import com.mojang.blaze3d.systems.RenderSystem.enableScissor
1110
import kotlin.math.max
12-
import kotlin.math.min
1311

1412
object Scissor {
15-
private var stack = ArrayDeque<Entry>()
13+
private var stack = ArrayDeque<Rect>()
1614

17-
fun scissor(rect: Rect, block: () -> Unit) = scissor(rect.leftTop, rect.rightBottom, block)
18-
19-
fun scissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) {
20-
stack.lastOrNull()?.let {
21-
registerScissor(
22-
// clamp corners so children scissor box can't overlap parent
23-
Vec2d(max(pos1.x, it.pos1.x), max(pos1.y, it.pos1.y)),
24-
Vec2d(min(pos2.x, it.pos2.x), min(pos2.y, it.pos2.y)),
25-
block
26-
)
27-
} ?: registerScissor(pos1, pos2, block)
15+
fun scissor(rect: Rect, block: () -> Unit) {
16+
// clamp corners so children scissor box can't overlap parent
17+
val processed = stack.lastOrNull()?.let { rect.clamp(it) } ?: rect
18+
registerScissor(processed, block)
2819
}
2920

30-
private fun registerScissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) {
31-
scissor(Entry(pos1, pos2))
21+
private fun registerScissor(rect: Rect, block: () -> Unit) {
22+
scissor(rect)
3223

3324
block()
3425

3526
stack.removeLast()
3627
stack.lastOrNull().apply(::scissor)
3728
}
3829

39-
private fun scissor(entry: Entry?) {
30+
private fun scissor(entry: Rect?) {
4031
if (entry == null) {
4132
disableScissor()
4233
return
4334
}
4435

4536
stack.add(entry)
4637

47-
val pos1 = entry.pos1 * HUD.scale
48-
val pos2 = entry.pos2 * HUD.scale
38+
val pos1 = entry.leftTop * HUD.scale
39+
val pos2 = entry.rightBottom * HUD.scale
4940

5041
val width = max(pos2.x - pos1.x, 0.0)
5142
val height = max(pos2.y - pos1.y, 0.0)
@@ -59,6 +50,4 @@ object Scissor {
5950
height.ceilToInt()
6051
)
6152
}
62-
63-
private data class Entry(val pos1: Vec2d, val pos2: Vec2d)
6453
}

common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ abstract class LambdaGui(override val name: String, private val owner: Module? =
5454

5555
final override fun removed() {
5656
onHide()
57+
58+
// quick crashfix (is there any other way to prevent gui being closed twice?)
59+
mc.currentScreen = null
5760
owner?.disable()
61+
mc.currentScreen = this
5862

5963
with(EventFlow.syncListeners) {
6064
unsubscribe(renderListener)

common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.lambda.gui.api.component
22

3+
import com.lambda.gui.api.component.core.IComponent
34
import com.lambda.gui.api.component.core.IRectComponent
45
import com.lambda.util.Mouse
56
import com.lambda.util.math.Vec2d
67

7-
abstract class InteractiveComponent : IRectComponent {
8+
abstract class InteractiveComponent : IComponent, IRectComponent {
89
protected var hovered = false
910
protected var pressed = false; set(value) {
1011
if (field == value) return

common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import com.lambda.graphics.animation.Animation.Companion.exp
44
import com.lambda.graphics.animation.AnimationTicker
55
import com.lambda.graphics.gl.Scissor.scissor
66
import com.lambda.graphics.renderer.gui.font.IFontEntry
7-
import com.lambda.gui.api.component.core.IListComponent
8-
import com.lambda.gui.api.component.core.IRectComponent
7+
import com.lambda.gui.api.component.core.IComponent
8+
import com.lambda.gui.api.component.core.list.IListComponent
9+
import com.lambda.gui.api.component.core.list.ChildComponent
910
import com.lambda.gui.api.layer.RenderLayer
1011
import com.lambda.module.modules.client.ClickGui
1112
import com.lambda.util.KeyCode
@@ -14,15 +15,15 @@ import com.lambda.util.math.MathUtils.toInt
1415
import com.lambda.util.math.Rect
1516
import com.lambda.util.math.Vec2d
1617

17-
abstract class WindowComponent <T : IRectComponent> : InteractiveComponent(), IListComponent<T> {
18+
abstract class WindowComponent <T : ChildComponent> : InteractiveComponent(), IListComponent<T> {
1819
abstract val title: String
1920

2021
abstract var width: Double
2122
abstract var height: Double
2223

2324
var position = Vec2d.ZERO
2425

25-
private var isOpen = false
26+
private var isOpen = true
2627
private var dragOffset: Vec2d? = null
2728
private val padding get() = ClickGui.windowPadding
2829

@@ -37,7 +38,7 @@ abstract class WindowComponent <T : IRectComponent> : InteractiveComponent(), IL
3738
private val animation = AnimationTicker()
3839

3940
override val children = mutableListOf<T>()
40-
val subLayer = RenderLayer()
41+
val subLayer = RenderLayer(true)
4142

4243
private val renderHeight by animation.exp({ 0.0 }, { height + padding * 2 * isOpen.toInt() }, 0.5, ::isOpen)
4344

@@ -69,7 +70,14 @@ abstract class WindowComponent <T : IRectComponent> : InteractiveComponent(), IL
6970

7071
override fun onTick() {
7172
animation.tick()
72-
super<IListComponent>.onTick()
73+
74+
children.forEach { child ->
75+
child.visible = isChildAccessible(child)
76+
}
77+
78+
children
79+
.filter(ChildComponent::visible)
80+
.forEach(IComponent::onTick)
7381
}
7482

7583
override fun onRender() {

common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ package com.lambda.gui.api.component.core
22

33
import com.lambda.util.math.Rect
44

5-
interface IRectComponent : IComponent {
5+
interface IRectComponent {
66
val rect: Rect
77
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.lambda.gui.api.component.core.list
2+
3+
import com.lambda.gui.api.component.InteractiveComponent
4+
5+
abstract class ChildComponent : InteractiveComponent(), IChildComponent {
6+
// mostly used to create an animation when an element appears
7+
var visible = false; set(value) {
8+
if (field == value) return
9+
field = value
10+
11+
if (value) onShow()
12+
else onHide()
13+
}
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.lambda.gui.api.component.core.list
2+
3+
import com.lambda.gui.api.component.core.IComponent
4+
5+
interface IChildComponent {
6+
val owner: IComponent
7+
}

common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt renamed to common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
package com.lambda.gui.api.component.core
1+
package com.lambda.gui.api.component.core.list
22

3+
import com.lambda.gui.api.component.core.IComponent
34
import com.lambda.util.KeyCode
45
import com.lambda.util.Mouse
56
import com.lambda.util.math.Vec2d

common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,95 @@ package com.lambda.gui.api.component.sub
22

33
import com.lambda.graphics.animation.Animation.Companion.exp
44
import com.lambda.graphics.animation.AnimationTicker
5-
import com.lambda.gui.api.component.InteractiveComponent
65
import com.lambda.gui.api.component.WindowComponent
6+
import com.lambda.gui.api.component.core.list.ChildComponent
77
import com.lambda.module.modules.client.ClickGui
88
import com.lambda.util.Mouse
9-
import com.lambda.util.math.ColorUtils.setAlpha
9+
import com.lambda.util.math.ColorUtils.multAlpha
1010
import com.lambda.util.math.MathUtils.lerp
11+
import com.lambda.util.math.MathUtils.toInt
1112
import com.lambda.util.math.Rect
1213
import com.lambda.util.math.Vec2d
1314
import java.awt.Color
15+
import kotlin.math.abs
1416

15-
abstract class ButtonComponent(private val base: WindowComponent<*>) : InteractiveComponent() {
17+
abstract class ButtonComponent(final override val owner: WindowComponent<*>) : ChildComponent() {
1618
abstract val position: Vec2d
1719
abstract val size: Vec2d
1820

1921
abstract val text: String
2022
open val active get() = pressed
2123

22-
private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) base.contentRect.size.x else size.x, size.y)
23-
final override val rect get() = Rect.basedOn(position, actualSize) + base.contentRect.leftTop
24+
private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.contentRect.size.x else size.x, size.y)
25+
final override val rect get() = Rect.basedOn(position, actualSize) + owner.contentRect.leftTop
2426

25-
private val layer = base.subLayer
27+
private val layer = owner.subLayer
2628
private val animation = AnimationTicker()
2729

28-
private val activeAnimation by animation.exp(0.0, 1.0, 0.5, ::active)
29-
private val hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (hovered) 0.5 else 0.1 }, ::hovered)
30-
private val pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed)
30+
private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active)
31+
private var hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered || active) 0.5 else 0.1 }, ::renderHovered)
32+
private var pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed)
3133
private val interactAnimation get() = lerp(hoverAnimation, 1.5, pressAnimation) * 0.4
3234

35+
private var lastHoveredTime = 0L
36+
private val renderHovered get() = hovered ||
37+
System.currentTimeMillis() - lastHoveredTime < 110 // a bit more than 2 ticks
38+
3339
init {
40+
// Active color
3441
layer.rect.build {
35-
position = Rect.basedOn(rect.leftTop, rect.size.x * activeAnimation, rect.size.y).shrink(interactAnimation)
36-
color(ClickGui.mainColor)
42+
position = rect.shrink(interactAnimation)
43+
color(ClickGui.mainColor.multAlpha(activeAnimation * 0.2))
3744
}
3845

46+
// Hover glint
3947
layer.rect.build {
40-
position = rect.shrink(interactAnimation)
41-
color(Color.WHITE.setAlpha(interactAnimation * 0.2))
48+
val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverAnimation, rect.size.y)
49+
position = hoverRect.shrink(interactAnimation)
50+
51+
val alpha = interactAnimation * 0.3
52+
color(ClickGui.mainColor.multAlpha(alpha))
4253
}
4354

55+
// Toggle fx
56+
layer.rect.build {
57+
val left = rect - Vec2d(rect.size.x, 0.0)
58+
val right = rect + Vec2d(rect.size.x, 0.0)
59+
60+
position = lerp(left, right, activeAnimation)
61+
.clamp(rect)
62+
.shrink(interactAnimation)
63+
64+
// 0.0 .. 1.0 .. 0.0 animation
65+
val alpha = 1.0 - (abs(activeAnimation - 0.5) * 2.0)
66+
val color = ClickGui.mainColor.multAlpha(alpha * 0.8)
67+
68+
// "Tail" effect
69+
val leftColor = color.multAlpha(1.0 - active.toInt())
70+
val rightColor = color.multAlpha(active.toInt().toDouble())
71+
72+
colorH(leftColor, rightColor)
73+
}
74+
75+
// Text
4476
layer.font.build {
4577
text = this@ButtonComponent.text
46-
scale = 1.0 - pressAnimation * 0.05
78+
scale = 1.0 - pressAnimation * 0.08
4779

48-
val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation * 0.5
80+
color = lerp(Color.WHITE, ClickGui.mainColor, activeAnimation)
81+
82+
val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation
4983
position = Vec2d(x, rect.center.y)
5084
}
5185
}
5286

5387
abstract fun performClickAction(mouse: Mouse.Button)
5488

89+
override fun onShow() {
90+
super.onShow()
91+
activeAnimation = 0.0
92+
}
93+
5594
override fun onTick() {
5695
animation.tick()
5796
}
@@ -60,6 +99,13 @@ abstract class ButtonComponent(private val base: WindowComponent<*>) : Interacti
6099
if (hovered) activeMouseButton?.let(::performClickAction)
61100
}
62101

102+
override fun onMouseMove(mouse: Vec2d) {
103+
super.onMouseMove(mouse)
104+
105+
val time = System.currentTimeMillis()
106+
if (hovered) lastHoveredTime = time
107+
}
108+
63109
companion object {
64110
const val FILL_PARENT = -1.0
65111
}
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
package com.lambda.gui.api.layer
22

3-
import com.lambda.graphics.renderer.IRenderer
43
import com.lambda.graphics.renderer.gui.font.FontRenderer
54
import com.lambda.graphics.renderer.gui.rect.RectRenderer
5+
import com.lambda.module.modules.client.ClickGui
6+
import com.mojang.blaze3d.systems.RenderSystem.blendFunc
7+
import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc
8+
import org.lwjgl.opengl.GL11.GL_ONE
9+
import org.lwjgl.opengl.GL11.GL_SRC_ALPHA
610

7-
class RenderLayer() {
8-
private val renderers = mutableListOf<IRenderer<*>>()
11+
class RenderLayer(private val allowGlowing: Boolean = false) {
12+
val rect = RectRenderer().asRenderer
13+
val font = FontRenderer().asRenderer
914

10-
val rect = RectRenderer().apply(::register).asRenderer
11-
val font = FontRenderer().apply(::register).asRenderer
15+
fun render() {
16+
rect.update()
17+
font.update()
1218

13-
fun register(renderer: IRenderer<*>) = renderers.add(renderer)
19+
if (allowGlowing && ClickGui.glow) blendFunc(GL_SRC_ALPHA, GL_ONE)
20+
rect.render()
21+
defaultBlendFunc()
1422

15-
fun render() {
16-
renderers.forEach(IRenderer<*>::update)
17-
renderers.forEach(IRenderer<*>::render)
23+
font.render()
1824
}
1925

20-
fun destroy() = renderers.forEach(IRenderer<*>::destroy)
26+
fun destroy() {
27+
rect.destroy()
28+
font.destroy()
29+
}
2130
}

0 commit comments

Comments
 (0)