Skip to content

Commit 405ff12

Browse files
committed
Fix rendering lines twice and more settings
1 parent bc9aa54 commit 405ff12

File tree

5 files changed

+194
-76
lines changed

5 files changed

+194
-76
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ object ChunkStorage : Loadable {
1313

1414
init {
1515
concurrentListener<WorldEvent.ChunkEvent.Load> { event ->
16-
chunkMap[event.chunk.pos] = event.chunk
16+
chunkMap[event.chunk.pos.toLong()] = event.chunk
1717
}
1818

1919
concurrentListener<WorldEvent.ChunkEvent.Unload> { event ->
20-
chunkMap.remove(event.chunk.pos)
20+
chunkMap.remove(event.chunk.pos.toLong())
2121
}
2222

2323
concurrentListener<ConnectionEvent.Disconnect> {
@@ -26,4 +26,4 @@ object ChunkStorage : Loadable {
2626
}
2727
}
2828

29-
typealias ChunkMap = ConcurrentHashMap<ChunkPos, WorldChunk>
29+
typealias ChunkMap = ConcurrentHashMap<Long, WorldChunk>

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

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,65 @@ import com.lambda.event.listener.SafeListener.Companion.listener
88
import com.lambda.module.modules.client.RenderSettings
99
import com.lambda.threading.runGameBlocking
1010
import net.minecraft.util.math.ChunkPos
11-
import net.minecraft.world.BlockView
11+
import net.minecraft.world.WorldView
1212
import net.minecraft.world.chunk.WorldChunk
1313
import java.util.concurrent.ConcurrentHashMap
1414
import java.util.concurrent.ConcurrentLinkedDeque
1515

1616
class ChunkedESP private constructor(
1717
owner: Any,
18-
private val update: EspRenderer.(BlockView, Int, Int, Int) -> Unit
18+
private val update: EspRenderer.(WorldView, Int, Int, Int) -> Unit
1919
) {
20-
private val rendererMap = ConcurrentHashMap<ChunkPos, EspChunk>()
21-
private val WorldChunk.renderer get() = rendererMap.getOrPut(pos) {
20+
private val rendererMap = ConcurrentHashMap<Long, EspChunk>()
21+
private val WorldChunk.renderer get() = rendererMap.getOrPut(pos.toLong()) {
2222
EspChunk(this, this@ChunkedESP)
2323
}
2424
private var ticks = 0
2525

26-
private val uploadPool = ConcurrentLinkedDeque<() -> Unit>()
27-
private val rebuildPool = ConcurrentLinkedDeque<EspChunk>()
26+
private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>()
27+
private val rebuildQueue = ConcurrentLinkedDeque<EspChunk>()
2828

2929
// i completely dont like to listen to enable events
3030

31-
fun clear() {
32-
rendererMap.clear()
31+
fun rebuild() {
32+
rebuildQueue.clear()
33+
rebuildQueue.addAll(rendererMap.values)
3334
}
3435

3536
init {
36-
owner.concurrentListener<WorldEvent.BlockUpdate> { event ->
37+
concurrentListener<WorldEvent.BlockUpdate> { event ->
3738
world.getWorldChunk(event.pos).renderer.apply {
3839
queueRebuild()
3940
notifyNeighbors()
4041
}
4142
}
4243

43-
owner.concurrentListener<WorldEvent.ChunkEvent.Load> { event ->
44+
concurrentListener<WorldEvent.ChunkEvent.Load> { event ->
4445
event.chunk.renderer.notifyNeighbors()
4546
}
4647

47-
owner.concurrentListener<WorldEvent.ChunkEvent.Unload> { event ->
48-
rendererMap.remove(event.chunk.pos)?.notifyNeighbors()
48+
concurrentListener<WorldEvent.ChunkEvent.Unload> { event ->
49+
rendererMap.remove(event.chunk.pos.toLong())?.notifyNeighbors()
4950
}
5051

5152
owner.concurrentListener<TickEvent.Pre> {
5253
if (++ticks % RenderSettings.updateFrequency == 0) {
53-
val polls = minOf(RenderSettings.rebuildsPerTick, rebuildPool.size)
54+
val polls = minOf(RenderSettings.rebuildsPerTick, rebuildQueue.size)
5455

5556
repeat(polls) {
56-
rebuildPool.poll()?.rebuild()
57+
rebuildQueue.poll()?.rebuild()
5758
}
5859
ticks = 0
5960
}
6061
}
6162

6263
owner.listener<TickEvent.Pre> {
63-
if (uploadPool.isEmpty()) return@listener
64+
if (uploadQueue.isEmpty()) return@listener
6465

65-
val polls = minOf(RenderSettings.uploadsPerTick, uploadPool.size)
66+
val polls = minOf(RenderSettings.uploadsPerTick, uploadQueue.size)
6667

6768
repeat(polls) {
68-
uploadPool.poll()?.invoke()
69+
uploadQueue.poll()?.invoke()
6970
}
7071
}
7172

@@ -78,7 +79,7 @@ class ChunkedESP private constructor(
7879

7980
companion object {
8081
fun Any.newChunkedESP(
81-
update: EspRenderer.(BlockView, Int, Int, Int) -> Unit
82+
update: EspRenderer.(WorldView, Int, Int, Int) -> Unit
8283
) = ChunkedESP(this, update)
8384
}
8485

@@ -94,12 +95,13 @@ class ChunkedESP private constructor(
9495
fun ChunkPos.isLoaded() = chunk.world.chunkManager.isChunkLoaded(x, z)
9596

9697
fun queueRebuild() {
97-
owner.rebuildPool.add(this)
98+
if (owner.rebuildQueue.contains(this)) return
99+
owner.rebuildQueue.add(this)
98100
}
99101

100102
fun notifyNeighbors() {
101103
neighbors.forEach {
102-
owner.rendererMap[it]?.queueRebuild()
104+
owner.rendererMap[it.toLong()]?.queueRebuild()
103105
}
104106
}
105107

@@ -114,6 +116,7 @@ class ChunkedESP private constructor(
114116

115117
val upload = {
116118
newRenderer.upload()
119+
renderer?.clear()
117120
renderer = newRenderer
118121
}
119122

@@ -124,7 +127,7 @@ class ChunkedESP private constructor(
124127
}
125128
}
126129
RenderSettings.UploadScheduler.Delayed -> {
127-
owner.uploadPool.add(upload)
130+
owner.uploadQueue.add(upload)
128131
}
129132
}
130133
}

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

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import com.lambda.util.primitives.extension.min
2222
import net.minecraft.util.math.Box
2323
import java.awt.Color
2424
import java.util.concurrent.ConcurrentHashMap
25+
import kotlin.math.abs
26+
import kotlin.math.hypot
2527

2628
class EspRenderer(
2729
usage: BufferUsage = BufferUsage.STATIC
@@ -35,26 +37,117 @@ class EspRenderer(
3537
private var updateFilled = false
3638
private var updateOutline = false
3739

38-
fun build(box: Box, filledColor: Color, outlineColor: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) {
40+
fun build(
41+
box: Box,
42+
filledColor: Color,
43+
outlineColor: Color,
44+
sides: Int = DirectionMask.ALL,
45+
outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR
46+
) {
3947
buildFilled(box, filledColor, sides)
4048
buildOutline(box, outlineColor, sides, outlineMode)
4149
}
4250

51+
fun buildConvexHull(
52+
boxes: Set<Box>,
53+
color: Color
54+
) = outline.use {
55+
updateOutline = true
56+
57+
// Step 1: Collect all vertices
58+
val vertices = mutableSetOf<Vertex>()
59+
boxes.forEach { box ->
60+
val pos1 = box.min
61+
val pos2 = box.max
62+
63+
vertices.add(Vertex(pos1.x, pos1.y, pos1.z, color))
64+
vertices.add(Vertex(pos1.x, pos1.y, pos2.z, color))
65+
vertices.add(Vertex(pos2.x, pos1.y, pos1.z, color))
66+
vertices.add(Vertex(pos2.x, pos1.y, pos2.z, color))
67+
vertices.add(Vertex(pos1.x, pos2.y, pos1.z, color))
68+
vertices.add(Vertex(pos1.x, pos2.y, pos2.z, color))
69+
vertices.add(Vertex(pos2.x, pos2.y, pos1.z, color))
70+
vertices.add(Vertex(pos2.x, pos2.y, pos2.z, color))
71+
}
72+
73+
// Step 2: Find the convex hull
74+
val convexHull = findConvexHull(vertices)
75+
76+
// Step 3: Build outline of convex hull
77+
for (i in convexHull.indices) {
78+
val v1 = convexHull[i]
79+
val v2 = convexHull[(i + 1) % convexHull.size]
80+
val intV1 = vec3(v1.x, v1.y, v1.z).color(v1.color).end()
81+
val intV2 = vec3(v2.x, v2.y, v2.z).color(v2.color).end()
82+
putLine(intV1, intV2)
83+
}
84+
}
85+
86+
// Utility function to find the convex hull using the QuickHull algorithm
87+
private fun findConvexHull(vertices: Set<Vertex>): List<Vertex> {
88+
if (vertices.size <= 1) return vertices.toList()
89+
90+
val points = vertices.toMutableList()
91+
points.sortWith(compareBy({ it.x }, { it.y }))
92+
val left = points.first()
93+
val right = points.last()
94+
95+
val (leftSet, rightSet) = points.partition { isLeft(left, right, it) }
96+
97+
val hull = mutableListOf<Vertex>()
98+
hull.add(left)
99+
hull.addAll(findHull(left, right, leftSet))
100+
hull.add(right)
101+
hull.addAll(findHull(right, left, rightSet))
102+
103+
return hull
104+
}
105+
106+
// Check if the point is on the left side of the line from start to end
107+
private fun isLeft(start: Vertex, end: Vertex, point: Vertex): Boolean {
108+
return (end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x) > 0
109+
}
110+
111+
// Recursively find the hull points
112+
private fun findHull(start: Vertex, end: Vertex, points: List<Vertex>): List<Vertex> {
113+
if (points.isEmpty()) return emptyList()
114+
115+
val farthest = points.maxByOrNull { distanceFromLine(start, end, it) } ?: return emptyList()
116+
117+
// Partition points into two sets: those to the left of the line (start, farthest) and those to the left of (farthest, end)
118+
val (leftSetStartFarthest, _) = points.partition { isLeft(start, farthest, it) }
119+
val (leftSetFarthestEnd, _) = points.partition { isLeft(farthest, end, it) }
120+
121+
val hull = mutableListOf<Vertex>()
122+
hull.addAll(findHull(start, farthest, leftSetStartFarthest))
123+
hull.add(farthest)
124+
hull.addAll(findHull(farthest, end, leftSetFarthestEnd))
125+
126+
return hull
127+
}
128+
129+
// Calculate the distance from the line
130+
private fun distanceFromLine(start: Vertex, end: Vertex, point: Vertex): Double {
131+
val area = abs((end.x - start.x) * (point.y - start.y) - (end.y - start.y) * (point.x - start.x))
132+
val base = Math.hypot((end.x - start.x), (end.y - start.y))
133+
return area / base
134+
}
135+
43136
fun buildFilled(box: Box, color: Color, sides: Int = DirectionMask.ALL) = filled.use {
44137
updateFilled = true
45138
val pos1 = box.min
46139
val pos2 = box.max
47140

48141
grow(8)
49142

50-
val blb by vertex(filledVertices, pos1.x, pos1.y, pos1.z, color)
51-
val blf by vertex(filledVertices, pos1.x, pos1.y, pos2.z, color)
52-
val brb by vertex(filledVertices, pos2.x, pos1.y, pos1.z, color)
53-
val brf by vertex(filledVertices, pos2.x, pos1.y, pos2.z, color)
54-
val tlb by vertex(filledVertices, pos1.x, pos2.y, pos1.z, color)
55-
val tlf by vertex(filledVertices, pos1.x, pos2.y, pos2.z, color)
56-
val trb by vertex(filledVertices, pos2.x, pos2.y, pos1.z, color)
57-
val trf by vertex(filledVertices, pos2.x, pos2.y, pos2.z, color)
143+
val blb by vertex(pos1.x, pos1.y, pos1.z, color)
144+
val blf by vertex(pos1.x, pos1.y, pos2.z, color)
145+
val brb by vertex(pos2.x, pos1.y, pos1.z, color)
146+
val brf by vertex(pos2.x, pos1.y, pos2.z, color)
147+
val tlb by vertex(pos1.x, pos2.y, pos1.z, color)
148+
val tlf by vertex(pos1.x, pos2.y, pos2.z, color)
149+
val trb by vertex(pos2.x, pos2.y, pos1.z, color)
150+
val trf by vertex(pos2.x, pos2.y, pos2.z, color)
58151

59152
if (sides.hasDirection(EAST)) putQuad(brb, trb, trf, brf)
60153
if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb)
@@ -71,14 +164,14 @@ class EspRenderer(
71164

72165
grow(8)
73166

74-
val blb by vertex(outlineVertices, pos1.x, pos1.y, pos1.z, color)
75-
val blf by vertex(outlineVertices, pos1.x, pos1.y, pos2.z, color)
76-
val brb by vertex(outlineVertices, pos2.x, pos1.y, pos1.z, color)
77-
val brf by vertex(outlineVertices, pos2.x, pos1.y, pos2.z, color)
78-
val tlb by vertex(outlineVertices, pos1.x, pos2.y, pos1.z, color)
79-
val tlf by vertex(outlineVertices, pos1.x, pos2.y, pos2.z, color)
80-
val trb by vertex(outlineVertices, pos2.x, pos2.y, pos1.z, color)
81-
val trf by vertex(outlineVertices, pos2.x, pos2.y, pos2.z, color)
167+
val blb by vertex(pos1.x, pos1.y, pos1.z, color)
168+
val blf by vertex(pos1.x, pos1.y, pos2.z, color)
169+
val brb by vertex(pos2.x, pos1.y, pos1.z, color)
170+
val brf by vertex(pos2.x, pos1.y, pos2.z, color)
171+
val tlb by vertex(pos1.x, pos2.y, pos1.z, color)
172+
val tlf by vertex(pos1.x, pos2.y, pos2.z, color)
173+
val trb by vertex(pos2.x, pos2.y, pos1.z, color)
174+
val trf by vertex(pos2.x, pos2.y, pos2.z, color)
82175

83176
val hasEast = sides.hasDirection(EAST)
84177
val hasWest = sides.hasDirection(WEST)
@@ -132,7 +225,6 @@ class EspRenderer(
132225
}
133226

134227
private fun IRenderContext.vertex(
135-
storage: MutableMap<Vertex, Int>,
136228
x: Double, y: Double, z: Double,
137229
color: Color
138230
) = lazy {
@@ -141,11 +233,12 @@ class EspRenderer(
141233
}
142234

143235
if (RenderSettings.vertexMapping) {
144-
storage.getOrPut(Vertex(x, y, z, color), newVertex)
236+
filledVertices.getOrPut(Vertex(x, y, z, color), newVertex)
145237
} else newVertex()
146238
}
147239

148240
private data class Vertex(val x: Double, val y: Double, val z: Double, val color: Color)
241+
private data class Edge(val vertex1: Vertex, val vertex2: Vertex)
149242

150243
companion object {
151244
private val shader = Shader("renderer/pos_color", "renderer/box_static")

common/src/main/kotlin/com/lambda/module/modules/client/RenderSettings.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ object RenderSettings : Module(
2020

2121
// ESP
2222
val uploadScheduler by setting("Upload Scheduler", UploadScheduler.Instant) { page == Page.ESP }
23-
val uploadsPerTick by setting("Uploads", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed }
24-
val rebuildsPerTick by setting("Rebuilds", 8, 1..32, 1, unit = " chunk/tick") { page == Page.ESP }
23+
val uploadsPerTick by setting("Uploads", 16, 1..256, 1, unit = " chunk/tick") { page == Page.ESP && uploadScheduler == UploadScheduler.Delayed }
24+
val rebuildsPerTick by setting("Rebuilds", 64, 1..256, 1, unit = " chunk/tick") { page == Page.ESP }
2525
val vertexMapping by setting("Vertex Mapping", true) { page == Page.ESP }
2626
val updateFrequency by setting("Update Frequency", 2, 1..10, 1, "Frequency of block updates", unit = " ticks") { page == Page.ESP }
2727
val outlineWidth by setting("Outline Width", 1.0, 0.1..5.0, 0.1, "Width of block outlines", unit = "px") { page == Page.ESP }

0 commit comments

Comments
 (0)