@@ -22,6 +22,8 @@ import com.lambda.util.primitives.extension.min
2222import net.minecraft.util.math.Box
2323import java.awt.Color
2424import java.util.concurrent.ConcurrentHashMap
25+ import kotlin.math.abs
26+ import kotlin.math.hypot
2527
2628class 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" )
0 commit comments