@@ -20,21 +20,20 @@ import com.lambda.module.modules.client.RenderSettings
2020import com.lambda.util.primitives.extension.max
2121import com.lambda.util.primitives.extension.min
2222import net.minecraft.util.math.Box
23+ import net.minecraft.util.math.Vec3d
2324import java.awt.Color
2425import java.util.concurrent.ConcurrentHashMap
25- import kotlin.math.abs
26- import kotlin.math.hypot
2726
2827class EspRenderer (
2928 usage : BufferUsage = BufferUsage .STATIC
3029) {
31- private val filled = VAO (VertexMode .TRIANGLES , VertexAttrib .Group .STATIC_RENDERER , usage, true )
32- private val filledVertices = ConcurrentHashMap <Vertex , Int >()
30+ private val faces = VAO (VertexMode .TRIANGLES , VertexAttrib .Group .STATIC_RENDERER , usage, true )
31+ private val faceVertices = ConcurrentHashMap <Vertex , Int >()
3332
34- private val outline = VAO (VertexMode .LINES , VertexAttrib .Group .STATIC_RENDERER , usage, true )
33+ private val outlines = VAO (VertexMode .LINES , VertexAttrib .Group .STATIC_RENDERER , usage, true )
3534 private val outlineVertices = ConcurrentHashMap <Vertex , Int >()
3635
37- private var updateFilled = false
36+ private var updateFaces = false
3837 private var updateOutline = false
3938
4039 fun build (
@@ -48,93 +47,8 @@ class EspRenderer(
4847 buildOutline(box, outlineColor, sides, outlineMode)
4948 }
5049
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-
136- fun buildFilled (box : Box , color : Color , sides : Int = DirectionMask .ALL ) = filled.use {
137- updateFilled = true
50+ fun buildFilled (box : Box , color : Color , sides : Int = DirectionMask .ALL ) = faces.use {
51+ updateFaces = true
13852 val pos1 = box.min
13953 val pos2 = box.max
14054
@@ -157,7 +71,12 @@ class EspRenderer(
15771 if (sides.hasDirection(NORTH )) putQuad(blb, tlb, trb, brb)
15872 }
15973
160- fun buildOutline (box : Box , color : Color , sides : Int = DirectionMask .ALL , outlineMode : DirectionMask .OutlineMode = DirectionMask .OutlineMode .OR ) = outline.use {
74+ fun buildOutline (
75+ box : Box ,
76+ color : Color ,
77+ sides : Int = DirectionMask .ALL ,
78+ outlineMode : DirectionMask .OutlineMode = DirectionMask .OutlineMode .OR
79+ ) = outlines.use {
16180 updateOutline = true
16281 val pos1 = box.min
16382 val pos2 = box.max
@@ -196,32 +115,112 @@ class EspRenderer(
196115 if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf)
197116 }
198117
118+ infix operator fun Vec3d.compareTo (other : Vec3d ) =
119+ lengthSquared().compareTo(other.lengthSquared())
120+
121+ fun buildMesh (boxes : Set <Box >, color : Color ) {
122+ val edges = hashMapOf<Edge , Int >()
123+ val faces = hashMapOf<Face , Int >()
124+
125+ fun addFace (v1 : Vec3d , v2 : Vec3d , v3 : Vec3d , v4 : Vec3d ) {
126+ val face = Face (v1, v2, v3, v4)
127+ faces[face] = faces.getOrDefault(face, 0 ) + 1
128+ }
129+
130+ fun addEdge (v1 : Vec3d , v2 : Vec3d ) {
131+ val edge = if (v1 < v2) Edge (v1, v2) else Edge (v2, v1)
132+ edges[edge] = edges.getOrDefault(edge, 0 ) + 1
133+ }
134+
135+ boxes.forEach { box ->
136+ val pos1 = box.min
137+ val pos2 = box.max
138+
139+ val blb = Vec3d (pos1.x, pos1.y, pos1.z)
140+ val blf = Vec3d (pos1.x, pos1.y, pos2.z)
141+ val brb = Vec3d (pos2.x, pos1.y, pos1.z)
142+ val brf = Vec3d (pos2.x, pos1.y, pos2.z)
143+ val tlb = Vec3d (pos1.x, pos2.y, pos1.z)
144+ val tlf = Vec3d (pos1.x, pos2.y, pos2.z)
145+ val trb = Vec3d (pos2.x, pos2.y, pos1.z)
146+ val trf = Vec3d (pos2.x, pos2.y, pos2.z)
147+
148+ addFace(blb, blf, brf, brb)
149+ addFace(tlb, tlf, trf, trb)
150+ addFace(blb, brb, trb, tlb)
151+ addFace(blf, brf, trf, tlf)
152+ addFace(blb, blf, tlf, tlb)
153+ addFace(brb, brf, trf, trb)
154+
155+ addEdge(tlb, trb)
156+ addEdge(tlf, trf)
157+ addEdge(tlb, tlf)
158+ addEdge(trf, trb)
159+
160+ addEdge(blb, brb)
161+ addEdge(blf, brf)
162+ addEdge(blb, blf)
163+ addEdge(brb, brf)
164+
165+ addEdge(tlb, blb)
166+ addEdge(trb, brb)
167+ addEdge(trf, brf)
168+ addEdge(tlf, blf)
169+ }
170+
171+ this .faces.use {
172+ updateFaces = true
173+ faces.forEach { (face, count) ->
174+ if (count % 2 == 0 ) return @forEach
175+ grow(1 )
176+ putQuad(
177+ vec3(face.v1.x, face.v1.y, face.v1.z).color(color).end(),
178+ vec3(face.v2.x, face.v2.y, face.v2.z).color(color).end(),
179+ vec3(face.v3.x, face.v3.y, face.v3.z).color(color).end(),
180+ vec3(face.v4.x, face.v4.y, face.v4.z).color(color).end()
181+ )
182+ }
183+ }
184+
185+ outlines.use {
186+ updateOutline = true
187+ edges.forEach { (edge, count) ->
188+ if (count % 2 == 0 ) return @forEach
189+ grow(1 )
190+ putLine(
191+ vec3(edge.start.x, edge.start.y, edge.start.z).color(color).end(),
192+ vec3(edge.end.x, edge.end.y, edge.end.z).color(color).end()
193+ )
194+ }
195+ }
196+ }
197+
199198 fun upload () {
200- if (updateFilled ) {
201- updateFilled = false
202- filled .upload()
199+ if (updateFaces ) {
200+ updateFaces = false
201+ faces .upload()
203202 }
204203
205204 if (updateOutline) {
206205 updateOutline = false
207- outline .upload()
206+ outlines .upload()
208207 }
209208 }
210209
211210 fun render () {
212211 shader.use()
213212 shader[" u_CameraPosition" ] = mc.gameRenderer.camera.pos
214213
215- withFaceCulling(filled ::render)
216- withLineWidth(RenderSettings .outlineWidth, outline ::render)
214+ withFaceCulling(faces ::render)
215+ withLineWidth(RenderSettings .outlineWidth, outlines ::render)
217216 }
218217
219218 fun clear () {
220- filledVertices .clear()
219+ faceVertices .clear()
221220 outlineVertices.clear()
222221
223- filled .clear()
224- outline .clear()
222+ faces .clear()
223+ outlines .clear()
225224 }
226225
227226 private fun IRenderContext.vertex (
@@ -233,12 +232,13 @@ class EspRenderer(
233232 }
234233
235234 if (RenderSettings .vertexMapping) {
236- filledVertices .getOrPut(Vertex (x, y, z, color), newVertex)
235+ faceVertices .getOrPut(Vertex (x, y, z, color), newVertex)
237236 } else newVertex()
238237 }
239238
240- 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 )
239+ data class Vertex (val x : Double , val y : Double , val z : Double , val color : Color )
240+ data class Edge (val start : Vec3d , val end : Vec3d )
241+ data class Face (val v1 : Vec3d , val v2 : Vec3d , val v3 : Vec3d , val v4 : Vec3d )
242242
243243 companion object {
244244 private val shader = Shader (" renderer/pos_color" , " renderer/box_static" )
0 commit comments