Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize radar rendering #581

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 67 additions & 34 deletions src/main/kotlin/com/lambda/client/module/modules/render/NewChunks.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.lambda.client.module.modules.render

import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.lambda.client.LambdaMod
import com.lambda.client.event.SafeClientEvent
import com.lambda.client.event.events.PacketEvent
Expand Down Expand Up @@ -35,6 +37,7 @@ import java.nio.file.Path
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.hypot

object NewChunks : Module(
name = "NewChunks",
Expand All @@ -46,6 +49,14 @@ object NewChunks : Module(
private val chunkGridColor by setting("Grid Color", ColorHolder(255, 0, 0, 100), true, { renderMode != RenderMode.WORLD })
private val distantChunkColor by setting("Distant Chunk Color", ColorHolder(100, 100, 100, 100), true, { renderMode != RenderMode.WORLD }, "Chunks that are not in render distance and not in baritone cache")
private val newChunkColor by setting("New Chunk Color", ColorHolder(255, 0, 0, 100), true, { renderMode != RenderMode.WORLD })
private val radarUpdateFrequency by setting("Radar Update Frequency", 50, 1..250, 10,
description = "How often to update the rendered chunks (ms)", visibility = { renderMode != RenderMode.WORLD },
consumer = { _, new ->
radarRenderCache = CacheBuilder.newBuilder()
.expireAfterWrite(new.toLong(), java.util.concurrent.TimeUnit.MILLISECONDS)
.build()
new
})
private val saveNewChunks by setting("Save New Chunks", false)
private val saveOption by setting("Save Option", SaveOption.EXTRA_FOLDER, { saveNewChunks })
private val saveInRegionFolder by setting("In Region", false, { saveNewChunks })
Expand All @@ -71,6 +82,10 @@ object NewChunks : Module(
private val chunks = ConcurrentHashMap<ChunkPos, Long>()
private val timer = TickTimer(TimeUnit.SECONDS)

private var radarRenderCache: Cache<Int, List<Pair<Vec2d, Vec2d>>> = CacheBuilder.newBuilder()
.expireAfterWrite(radarUpdateFrequency.toLong(), java.util.concurrent.TimeUnit.MILLISECONDS)
.build()

init {
onDisable {
logWriterClose()
Expand Down Expand Up @@ -122,40 +137,62 @@ object NewChunks : Module(
val chunkDist = (it.radius * it.scale).toInt() shr 4
// at high zooms (further zoomed out) there will be thousands of rects being rendered
// buffering rects here to reduce GL calls and improve FPS
val distantChunkRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
val chunkGridRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
for (chunkX in -chunkDist..chunkDist) {
for (chunkZ in -chunkDist..chunkDist) {
val pos0 = getChunkPos(chunkX, chunkZ, playerOffset, it.scale)
val pos1 = getChunkPos(chunkX + 1, chunkZ + 1, playerOffset, it.scale)

if (isSquareInRadius(pos0, pos1, it.radius)) {
val chunk = world.getChunk(player.chunkCoordX + chunkX, player.chunkCoordZ + chunkZ)
val isCachedChunk =
BaritoneUtils.primary?.worldProvider?.currentWorld?.cachedWorld?.isCached(
(player.chunkCoordX + chunkX) shl 4, (player.chunkCoordZ + chunkZ) shl 4
) ?: false

if (!chunk.isLoaded && !isCachedChunk) {
distantChunkRects.add(Pair(pos0, pos1))

var distantChunkRectsCached = radarRenderCache.getIfPresent(0)
var chunkGridRectsCached = radarRenderCache.getIfPresent(1)

if (distantChunkRectsCached == null || chunkGridRectsCached == null) {
val distantChunkRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
val chunkGridRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
for (chunkX in -chunkDist..chunkDist) {
for (chunkZ in -chunkDist..chunkDist) {
val x1 = ((chunkX shl 4) - playerOffset.x) / it.scale
val z1 = ((chunkZ shl 4) - playerOffset.y) / it.scale
val x2 = (((chunkX + 1) shl 4) - playerOffset.x) / it.scale
val z2 = (((chunkZ + 1) shl 4) - playerOffset.y) / it.scale

if (isSquareInRadius(x1, x2, z1, z2, it.radius)) {
val chunk = world.getChunk(player.chunkCoordX + chunkX, player.chunkCoordZ + chunkZ)
val isCachedChunk =
BaritoneUtils.primary?.worldProvider?.currentWorld?.cachedWorld?.isCached(
(player.chunkCoordX + chunkX) shl 4, (player.chunkCoordZ + chunkZ) shl 4
) ?: false
val chunkPair = Pair(Vec2d(x1, z1), Vec2d(x2, z2))
if (!chunk.isLoaded && !isCachedChunk) {
distantChunkRects.add(chunkPair)
}
if (it.chunkLines) chunkGridRects.add(chunkPair)
}
chunkGridRects.add(Pair(pos0, pos1))
}
}
radarRenderCache.put(0, distantChunkRects)
radarRenderCache.put(1, chunkGridRects)
distantChunkRectsCached = distantChunkRects
chunkGridRectsCached = chunkGridRects
}
if (distantChunkRects.isNotEmpty()) RenderUtils2D.drawRectFilledList(it.vertexHelper, distantChunkRects, distantChunkColor)
if (it.chunkLines && chunkGridRects.isNotEmpty()) RenderUtils2D.drawRectOutlineList(it.vertexHelper, chunkGridRects, 0.3f, chunkGridColor)

val newChunkRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
chunks.keys.forEach { chunk ->
val pos0 = getChunkPos(chunk.x - player.chunkCoordX, chunk.z - player.chunkCoordZ, playerOffset, it.scale)
val pos1 = getChunkPos(chunk.x - player.chunkCoordX + 1, chunk.z - player.chunkCoordZ + 1, playerOffset, it.scale)
if (distantChunkRectsCached.isNotEmpty()) RenderUtils2D.drawRectFilledList(it.vertexHelper, distantChunkRectsCached, distantChunkColor)
if (it.chunkLines && chunkGridRectsCached.isNotEmpty()) RenderUtils2D.drawRectOutlineList(it.vertexHelper, chunkGridRectsCached, 0.3f, chunkGridColor)

var newChunkRectsCached = radarRenderCache.getIfPresent(2)

if (newChunkRectsCached == null) {
val newChunkRects: MutableList<Pair<Vec2d, Vec2d>> = mutableListOf()
chunks.keys.forEach { chunk ->
val x1 = (((chunk.x - player.chunkCoordX) shl 4) - playerOffset.x) / it.scale
val z1 = (((chunk.z - player.chunkCoordZ) shl 4) - playerOffset.y) / it.scale
val x2 = (((chunk.x - player.chunkCoordX + 1) shl 4) - playerOffset.x) / it.scale
val z2 = (((chunk.z - player.chunkCoordZ + 1) shl 4) - playerOffset.y) / it.scale

if (isSquareInRadius(pos0, pos1, it.radius)) {
newChunkRects.add(Pair(pos0, pos1))
if (isSquareInRadius(x1, x2, z1, z2, it.radius)) {
newChunkRects.add(Pair(Vec2d(x1, z1), Vec2d(x2, z2)))
}
}
radarRenderCache.put(2, newChunkRects)
newChunkRectsCached = newChunkRects
}
if (newChunkRects.isNotEmpty()) RenderUtils2D.drawRectFilledList(it.vertexHelper, newChunkRects, newChunkColor)

if (newChunkRectsCached.isNotEmpty()) RenderUtils2D.drawRectFilledList(it.vertexHelper, newChunkRectsCached, newChunkColor)
}

safeListener<PacketEvent.PostReceive> { event ->
Expand Down Expand Up @@ -303,14 +340,10 @@ object NewChunks : Module(
}

// p2.x > p1.x and p2.y > p1.y is assumed
private fun isSquareInRadius(p1: Vec2d, p2: Vec2d, radius: Float): Boolean {
val x = if (p1.x + p2.x > 0) p2.x else p1.x
val y = if (p1.y + p2.y > 0) p2.y else p1.y
return Vec2d(x, y).length() < radius
}

private fun getChunkPos(x: Int, z: Int, playerOffset: Vec2d, scale: Float): Vec2d {
return Vec2d((x shl 4).toDouble(), (z shl 4).toDouble()).minus(playerOffset).div(scale.toDouble())
private fun isSquareInRadius(x1: Double, x2: Double, z1: Double, z2: Double, radius: Float): Boolean {
val x = if (x1 + x2 > 0) x2 else x1
val z = if (z1 + z2 > 0) z2 else z1
return hypot(x, z) < radius
}

private fun saveNewChunk(log: PrintWriter?, data: String) {
Expand Down
36 changes: 14 additions & 22 deletions src/main/kotlin/com/lambda/client/util/graphics/RenderUtils2D.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,14 @@ object RenderUtils2D {
glLineWidth(lineWidth)
vertexHelper.begin(GL_LINES)
rects.forEach {
val pos1 = it.first // Top left
val pos2 = Vec2d(it.second.x, it.first.y) // Top right
val pos3 = it.second // Bottom right
val pos4 = Vec2d(it.first.x, it.second.y) // Bottom left
vertexHelper.put(pos1, color)
vertexHelper.put(pos2, color)
vertexHelper.put(pos2, color)
vertexHelper.put(pos3, color)
vertexHelper.put(pos3, color)
vertexHelper.put(pos4, color)
vertexHelper.put(pos4, color)
vertexHelper.put(pos1, color)
vertexHelper.put(it.first.x, it.first.y, color) // Top left
vertexHelper.put(it.second.x, it.first.y, color) // Top right
vertexHelper.put(it.second.x, it.first.y, color) // Top right
vertexHelper.put(it.second.x, it.second.y, color) // Bottom right
vertexHelper.put(it.second.x, it.second.y, color) // Bottom right
vertexHelper.put(it.first.x, it.second.y, color) // Bottom left
vertexHelper.put(it.first.x, it.second.y, color) // Bottom left
vertexHelper.put(it.first.x, it.first.y, color) // Top left
}
vertexHelper.end()
releaseGl()
Expand All @@ -125,16 +121,12 @@ object RenderUtils2D {
prepareGl()
vertexHelper.begin(GL_TRIANGLES)
rects.forEach {
val pos1 = it.first // Top left
val pos2 = Vec2d(it.second.x, it.first.y) // Top right
val pos3 = it.second // Bottom right
val pos4 = Vec2d(it.first.x, it.second.y) // Bottom left
vertexHelper.put(pos1, color)
vertexHelper.put(pos2, color)
vertexHelper.put(pos4, color)
vertexHelper.put(pos4, color)
vertexHelper.put(pos3, color)
vertexHelper.put(pos2, color)
vertexHelper.put(it.first.x, it.first.y, color) // Top left
vertexHelper.put(it.second.x, it.first.y, color) // Top right
vertexHelper.put(it.first.x, it.second.y, color) // Bottom left
vertexHelper.put(it.first.x, it.second.y, color) // Bottom left
vertexHelper.put(it.second.x, it.second.y, color) // Bottom right
vertexHelper.put(it.second.x, it.first.y, color) // Top right
}
vertexHelper.end()
releaseGl()
Expand Down