Skip to content

Commit 968de6a

Browse files
committed
ESP
1 parent 50f2496 commit 968de6a

File tree

8 files changed

+228
-65
lines changed

8 files changed

+228
-65
lines changed

common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.lambda.graphics.buffer.vao
22

3+
import com.lambda.graphics.buffer.vao.vertex.BufferUsage
34
import com.lambda.graphics.buffer.vao.vertex.VertexAttrib
45
import com.lambda.graphics.buffer.vao.vertex.VertexMode
56
import com.lambda.graphics.gl.Memory.address
@@ -25,8 +26,9 @@ import java.awt.Color
2526
import java.nio.ByteBuffer
2627

2728
class VAO(
28-
private val drawMode: VertexMode,
29+
private val vertexMode: VertexMode,
2930
attribGroup: VertexAttrib.Group,
31+
private val bufferUsage: BufferUsage = BufferUsage.DYNAMIC
3032
) : IRenderContext {
3133
private var vao = 0
3234
private var vbo = 0
@@ -41,19 +43,20 @@ class VAO(
4143
private lateinit var indices: ByteBuffer
4244
private var indicesPointer = 0L
4345
private var indicesCount = 0
46+
private var uploadedIndices = 0
4447

4548
private var vertexIndex = 0
4649

4750
init {
4851
val stride = attribGroup.stride
49-
objectSize = stride * drawMode.indicesCount
52+
objectSize = stride * vertexMode.indicesCount
5053

5154
runOnGameThread {
5255
vertices = byteBuffer(objectSize * 256 * 4)
5356
verticesPointer = address(vertices)
5457
verticesPosition = verticesPointer
5558

56-
indices = byteBuffer(drawMode.indicesCount * 512 * 4)
59+
indices = byteBuffer(vertexMode.indicesCount * 512 * 4)
5760
indicesPointer = address(indices)
5861

5962
vao = glGenVertexArrays()
@@ -155,7 +158,7 @@ class VAO(
155158
if ((indicesCount + amount) * 4 < cap) return
156159

157160
var newSize = cap * 2
158-
if (newSize % drawMode.indicesCount != 0) newSize += newSize % (drawMode.indicesCount * 4)
161+
if (newSize % vertexMode.indicesCount != 0) newSize += newSize % (vertexMode.indicesCount * 4)
159162
val newIndices = byteBuffer(newSize)
160163

161164
val from = address(indices)
@@ -167,10 +170,10 @@ class VAO(
167170
}
168171

169172
override fun render() {
170-
if (indicesCount <= 0) return
173+
if (uploadedIndices <= 0) return
171174

172175
bindVertexArray(vao)
173-
glDrawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT, 0)
176+
glDrawElements(vertexMode.gl, uploadedIndices, GL_UNSIGNED_INT, 0)
174177
unbindVertexArray()
175178
}
176179

@@ -181,18 +184,21 @@ class VAO(
181184
val iboData = indices.limit(indicesCount * 4)
182185

183186
bindVertexBuffer(vbo)
184-
bufferData(GL_ARRAY_BUFFER, vboData, GL_DYNAMIC_DRAW)
187+
bufferData(GL_ARRAY_BUFFER, vboData, bufferUsage.gl)
185188
unbindVertexBuffer()
186189

187190
bindIndexBuffer(ibo)
188-
bufferData(GL_ELEMENT_ARRAY_BUFFER, iboData, GL_DYNAMIC_DRAW)
191+
bufferData(GL_ELEMENT_ARRAY_BUFFER, iboData, bufferUsage.gl)
189192
unbindIndexBuffer()
193+
194+
uploadedIndices = indicesCount
190195
}
191196

192197
override fun clear() {
193198
verticesPosition = verticesPointer
194199
vertexIndex = 0
195200
indicesCount = 0
201+
uploadedIndices = 0
196202
}
197203

198204
protected fun finalize() {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.lambda.graphics.buffer.vao.vertex
2+
3+
import com.lambda.graphics.gl.GLObject
4+
import org.lwjgl.opengl.GL30C.GL_DYNAMIC_DRAW
5+
import org.lwjgl.opengl.GL30C.GL_STATIC_DRAW
6+
7+
enum class BufferUsage(override val gl: Int) : GLObject {
8+
STATIC(GL_STATIC_DRAW),
9+
DYNAMIC(GL_DYNAMIC_DRAW)
10+
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ object GlStateUtils {
1919
blend(true)
2020
cull(false)
2121

22-
2322
block()
2423

2524
glDepthMask(true)
@@ -36,6 +35,12 @@ object GlStateUtils {
3635
depthTest(false)
3736
}
3837

38+
fun withFaceCulling(block: () -> Unit) {
39+
cull(true)
40+
block()
41+
cull(false)
42+
}
43+
3944
fun withLineWidth(width: Double, block: () -> Unit) {
4045
glLineWidth(width.toFloat())
4146
block()
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package com.lambda.graphics.renderer.esp
2+
3+
import com.lambda.event.Muteable
4+
import com.lambda.event.events.RenderEvent
5+
import com.lambda.event.events.WorldEvent
6+
import com.lambda.event.listener.SafeListener.Companion.listener
7+
import com.lambda.graphics.renderer.esp.DirectionMask.exclude
8+
import com.lambda.graphics.renderer.esp.DirectionMask.mask
9+
import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall
10+
import kotlinx.coroutines.*
11+
import net.minecraft.util.math.BlockPos
12+
import net.minecraft.util.math.Box
13+
import net.minecraft.util.math.Direction
14+
import net.minecraft.world.BlockView
15+
import net.minecraft.world.chunk.WorldChunk
16+
import java.awt.Color
17+
import java.util.concurrent.ConcurrentHashMap
18+
import java.util.concurrent.Executors.newSingleThreadExecutor
19+
20+
class BlockEspRenderer private constructor(
21+
private val owner: Any,
22+
private val filter: (BlockView, BlockPos) -> Boolean,
23+
private val painter: (BlockView, BlockPos) -> Pair<Color, Color>
24+
) {
25+
private val rendererMap = ConcurrentHashMap<WorldChunk, EspChunk>()
26+
private val WorldChunk.renderer get() = rendererMap.getOrPut(this) {
27+
EspChunk(this, this@BlockEspRenderer)
28+
}
29+
30+
init {
31+
listener<WorldEvent.BlockUpdate> { event ->
32+
world.getWorldChunk(event.pos).renderer.update = true
33+
}
34+
35+
listener<WorldEvent.ChunkEvent.Load> { event ->
36+
event.chunk.renderer.updateNeighbors()
37+
}
38+
39+
listener<WorldEvent.ChunkEvent.Unload> { event ->
40+
rendererMap.remove(event.chunk)?.updateNeighbors()
41+
}
42+
43+
owner.listener<RenderEvent.World> {
44+
rendererMap.values.forEach {
45+
it.renderer?.render()
46+
}
47+
}
48+
49+
CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher()).launch {
50+
while (true) {
51+
delay(100)
52+
53+
if ((owner as? Muteable)?.isMuted == true) continue
54+
rendererMap.values.forEach {
55+
it.tick()
56+
delay(1)
57+
}
58+
}
59+
}
60+
}
61+
62+
companion object {
63+
fun Any.newEspRenderer(
64+
filter: (BlockView, BlockPos) -> Boolean,
65+
painter: (BlockView, BlockPos) -> Pair<Color, Color>
66+
) = BlockEspRenderer(this, filter, painter)
67+
}
68+
69+
private class EspChunk(val chunk: WorldChunk, val owner: BlockEspRenderer) {
70+
private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher())
71+
72+
var renderer: EspRenderer? = null
73+
74+
var update = true
75+
private val chunkOffsets = listOf(1 to 0, 0 to 1, -1 to 0, 0 to -1)
76+
77+
fun updateNeighbors() {
78+
scope.launch {
79+
chunkOffsets.forEach { ofs ->
80+
chunk.world.chunkManager.getWorldChunk(
81+
chunk.pos.x + ofs.first,
82+
chunk.pos.z + ofs.second
83+
)?.let {
84+
owner.rendererMap[it]?.update = true
85+
}
86+
}
87+
}
88+
}
89+
90+
fun tick() {
91+
if (!update) return
92+
93+
// Chunk could only be drawn when all neighbors are loaded
94+
if (chunkOffsets.count {
95+
chunk.world.isChunkLoaded(
96+
chunk.pos.x + it.first,
97+
chunk.pos.z + it.second
98+
)
99+
} < 4) return
100+
101+
update = false
102+
103+
scope.launch {
104+
val newRenderer = runOnMainThreadAndWait(::EspRenderer)
105+
106+
iterateChunk { x, y, z ->
107+
checkAndDraw(newRenderer, BlockPos(x, y, z))
108+
}
109+
110+
runOnMainThreadAndWait {
111+
newRenderer.upload()
112+
renderer = newRenderer
113+
}
114+
}
115+
}
116+
117+
private suspend fun <R: Any> runOnMainThreadAndWait(block: () -> R): R {
118+
var result: R? = null
119+
120+
recordRenderCall {
121+
result = block()
122+
}
123+
124+
while (result == null) delay(1)
125+
return result as R
126+
}
127+
128+
private fun checkAndDraw(renderer: EspRenderer, blockPos: BlockPos): Boolean {
129+
if (!owner.filter(chunk, blockPos)) return false
130+
131+
var sides = DirectionMask.ALL
132+
133+
Direction.entries.forEach {
134+
if (!owner.filter(chunk.world, blockPos.add(it.vector))) return@forEach
135+
sides = sides.exclude(it.mask)
136+
}
137+
138+
val (filledColor, outlineColor) = owner.painter(chunk, blockPos)
139+
renderer.build(Box(blockPos), filledColor, outlineColor, sides, DirectionMask.OutlineMode.AND)
140+
return true
141+
}
142+
143+
private fun iterateChunk(block: (Int, Int, Int) -> Unit) = chunk.apply {
144+
for (x in pos.startX..pos.endX) {
145+
for (z in pos.startZ..pos.endZ) {
146+
for (y in bottomY..topY) {
147+
block(x, y, z)
148+
}
149+
}
150+
}
151+
}
152+
}
153+
}

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

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.lambda.graphics.renderer.esp
22

3-
import com.lambda.event.events.RenderEvent
4-
import com.lambda.event.listener.SafeListener.Companion.listener
3+
import com.lambda.Lambda.mc
54
import com.lambda.graphics.buffer.vao.VAO
5+
import com.lambda.graphics.buffer.vao.vertex.BufferUsage
66
import com.lambda.graphics.buffer.vao.vertex.VertexAttrib
77
import com.lambda.graphics.buffer.vao.vertex.VertexMode
8+
import com.lambda.graphics.gl.GlStateUtils.withFaceCulling
89
import com.lambda.graphics.gl.GlStateUtils.withLineWidth
910
import com.lambda.graphics.renderer.esp.DirectionMask.DOWN
1011
import com.lambda.graphics.renderer.esp.DirectionMask.EAST
@@ -14,17 +15,16 @@ import com.lambda.graphics.renderer.esp.DirectionMask.UP
1415
import com.lambda.graphics.renderer.esp.DirectionMask.WEST
1516
import com.lambda.graphics.renderer.esp.DirectionMask.hasDirection
1617
import com.lambda.graphics.shader.Shader
17-
import com.lambda.module.Module
1818
import com.lambda.util.primitives.extension.max
1919
import com.lambda.util.primitives.extension.min
2020
import net.minecraft.util.math.Box
2121
import java.awt.Color
2222

23-
class CachedEspRenderer(val owner: Any) {
24-
private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER)
23+
class EspRenderer {
24+
private val filled = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC)
2525
private var updateFilled = false
2626

27-
private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER)
27+
private val outline = VAO(VertexMode.LINES, VertexAttrib.Group.STATIC_RENDERER, BufferUsage.STATIC)
2828
private var updateOutline = false
2929

3030
var outlineWidth = 1.0
@@ -50,12 +50,12 @@ class CachedEspRenderer(val owner: Any) {
5050
val trb by lazy { vec3(pos2.x, pos2.y, pos1.z).color(color).end() }
5151
val trf by lazy { vec3(pos2.x, pos2.y, pos2.z).color(color).end() }
5252

53-
if (sides.hasDirection(EAST)) putQuad(brb, brf, trf, trb)
53+
if (sides.hasDirection(EAST)) putQuad(brb, trb, trf, brf)
5454
if (sides.hasDirection(WEST)) putQuad(blb, blf, tlf, tlb)
5555
if (sides.hasDirection(UP)) putQuad(tlb, tlf, trf, trb)
5656
if (sides.hasDirection(DOWN)) putQuad(blb, brb, brf, blf)
5757
if (sides.hasDirection(SOUTH)) putQuad(blf, brf, trf, tlf)
58-
if (sides.hasDirection(NORTH)) putQuad(blb, brb, trb, tlb)
58+
if (sides.hasDirection(NORTH)) putQuad(blb, tlb, trb, brb)
5959
}
6060

6161
fun buildOutline(box: Box, color: Color, sides: Int = DirectionMask.ALL, outlineMode: DirectionMask.OutlineMode = DirectionMask.OutlineMode.OR) = outline.use {
@@ -97,29 +97,29 @@ class CachedEspRenderer(val owner: Any) {
9797
if (outlineMode.check(hasSouth, hasWest)) putLine(tlf, blf)
9898
}
9999

100-
fun clear() {
101-
filled.clear()
102-
outline.clear()
103-
}
100+
fun upload() {
101+
if (updateFilled) {
102+
updateFilled = false
103+
filled.upload()
104+
}
104105

105-
init {
106-
owner.listener<RenderEvent.World> {
107-
if (updateFilled) {
108-
updateFilled = false
109-
filled.upload()
110-
}
106+
if (updateOutline) {
107+
updateOutline = false
108+
outline.upload()
109+
}
110+
}
111111

112-
if (updateOutline) {
113-
updateOutline = false
114-
outline.upload()
115-
}
112+
fun render() {
113+
shader.use()
114+
shader["u_CameraPosition"] = mc.gameRenderer.camera.pos
116115

117-
shader.use()
118-
shader["u_CameraPosition"] = mc.gameRenderer.camera.pos
116+
withFaceCulling(filled::render)
117+
withLineWidth(outlineWidth, outline::render)
118+
}
119119

120-
filled.render()
121-
withLineWidth(outlineWidth, outline::render)
122-
}
120+
fun clear() {
121+
filled.clear()
122+
outline.clear()
123123
}
124124

125125
companion object {

0 commit comments

Comments
 (0)