From 184203ee207b147f0d9f55bde77e19e4252c1d07 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 1 Oct 2024 15:13:42 +0700 Subject: [PATCH] Chart levels --- .../mc/otm/client/render/ChartRendering.kt | 75 ++++++++++++++++--- .../mc/otm/client/render/FontRendering.kt | 12 +-- .../screen/panels/DecimalHistoryChartPanel.kt | 23 +++++- 3 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt index 10e37c3b9..3489c654d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt @@ -1,13 +1,17 @@ package ru.dbotthepony.mc.otm.client.render import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.BufferBuilder import com.mojang.blaze3d.vertex.BufferUploader +import com.mojang.blaze3d.vertex.ByteBufferBuilder import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexFormat +import it.unimi.dsi.fastutil.floats.Float2ObjectMap import net.minecraft.client.gui.Font import net.minecraft.client.renderer.GameRenderer import net.minecraft.network.chat.Component +import org.lwjgl.opengl.GL11.GL_LESS import ru.dbotthepony.kommons.math.RGBAColor import kotlin.math.PI import kotlin.math.acos @@ -20,6 +24,8 @@ private const val LINE_WIDTH = 1f private const val HIGHLIGHT_WIDTH = 3f private val TEXT_BACKGROUND = RGBAColor(0f, 0f, 0f, 0.5f) +private val LINE_COLOR = RGBAColor(1f, 0f, 1f, 0.25f) +private val LINE_TEXT_COLOR = RGBAColor(1f, 0f, 1f, 0.5f) data class ChartMouseLabels( val mouseX: Float, @@ -32,6 +38,17 @@ data class ChartMouseLabels( val textBackgroundColor: RGBAColor = TEXT_BACKGROUND ) +data class ChartLevelLabels( + val labels: Map, + val font: Font, + val lineColor: RGBAColor = RGBAColor.DARK_GREEN, + val textColor: RGBAColor = RGBAColor.DARK_GREEN, + val textGravity: RenderGravity = RenderGravity.TOP_RIGHT, + val textOutline: RGBAColor = RGBAColor.BLACK +) + +private val BYTE_BUFFER_BUILDER = ByteBufferBuilder(786_432) + fun renderChart( poseStack: PoseStack, normalized: FloatArray, @@ -39,10 +56,11 @@ fun renderChart( height: Float, color: RGBAColor = RGBAColor.WHITE, labels: ChartMouseLabels? = null, + levelLabels: ChartLevelLabels? = null, x: Float = 0f, y: Float = 0f, ) { - val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION) + val builder = BufferBuilder(BYTE_BUFFER_BUILDER, VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) val step = width / normalized.size val pose = poseStack.last().pose @@ -52,6 +70,17 @@ fun renderChart( var drawPointX = 0f var drawPointY = 0f + if (levelLabels != null) { + for ((level, label) in levelLabels.labels) { + val y0 = y + (1f - level) * height + + builder.vertex(pose, x + width, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(pose, x + width, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(pose, x, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(pose, x, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) + } + } + for (i in 0 until normalized.size - 1) { val y0 = y + (1f - normalized[i]) * height val y1 = y + (1f - normalized[i + 1]) * height @@ -63,10 +92,10 @@ fun renderChart( val yDiff = y1 - y0 if (yDiff.sign == 0f) { - builder.vertex(pose, x0, y0 + LINE_WIDTH / 2f, 0f) - builder.vertex(pose, x0, y0 - LINE_WIDTH / 2f, 0f) - builder.vertex(pose, x1, y1 - LINE_WIDTH / 2f, 0f) - builder.vertex(pose, x1, y1 + LINE_WIDTH / 2f, 0f) + builder.vertex(pose, x0, y0 + LINE_WIDTH / 2f, 0f).color(color) + builder.vertex(pose, x0, y0 - LINE_WIDTH / 2f, 0f).color(color) + builder.vertex(pose, x1, y1 - LINE_WIDTH / 2f, 0f).color(color) + builder.vertex(pose, x1, y1 + LINE_WIDTH / 2f, 0f).color(color) } else { val length = (xDiff.pow(2f) + yDiff.pow(2f)).pow(0.5f) val angle = acos(xDiff / length) * yDiff.sign - PI / 2f // rotate 90 deg @@ -74,10 +103,10 @@ fun renderChart( val xDisp = cos(angle).toFloat() * LINE_WIDTH / 2f val yDisp = sin(angle).toFloat() * LINE_WIDTH / 2f - builder.vertex(pose, x0 + xDisp, y0 + yDisp, 0f) - builder.vertex(pose, x0 - xDisp, y0 - yDisp, 0f) - builder.vertex(pose, x1 - xDisp, y1 - yDisp, 0f) - builder.vertex(pose, x1 + xDisp, y1 + yDisp, 0f) + builder.vertex(pose, x0 + xDisp, y0 + yDisp, 0f).color(color) + builder.vertex(pose, x0 - xDisp, y0 - yDisp, 0f).color(color) + builder.vertex(pose, x1 - xDisp, y1 - yDisp, 0f).color(color) + builder.vertex(pose, x1 + xDisp, y1 + yDisp, 0f).color(color) } //graphics.renderRect(x0, y0, LINE_WIDTH, LINE_WIDTH) @@ -89,13 +118,37 @@ fun renderChart( } } - RenderSystem.setShader(GameRenderer::getPositionShader) - RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha) + RenderSystem.setShader(GameRenderer::getPositionColorShader) + // RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha) RenderSystem.disableDepthTest() RenderSystem.disableBlend() + RenderSystem.setShaderColor(1f, 1f, 1f, 1f) BufferUploader.drawWithShader(builder.buildOrThrow()) + RenderSystem.enableDepthTest() + RenderSystem.depthFunc(GL_LESS) + + if (levelLabels != null && levelLabels.labels.isNotEmpty()) { + for ((level, label) in levelLabels.labels) { + val y0 = y + (1f - level) * height + + val tX = x + levelLabels.textGravity.repositionX(width, levelLabels.font.width(label).toFloat()) - 1f + val tY = y0 - LINE_WIDTH - 1f - levelLabels.font.lineHeight + levelLabels.textGravity.repositionY(levelLabels.font.lineHeight * 2f + LINE_WIDTH + 2f, levelLabels.font.lineHeight.toFloat()) + 1f + + levelLabels.font.draw( + poseStack, + label, + x = tX, + y = tY, + color = levelLabels.textColor, + drawOutline = true, + outlineZ = -1f, + outlineColor = levelLabels.textOutline + ) + } + } + if (drawLabel != -1) { renderRect(pose, drawPointX - LINE_WIDTH / 2f, y, LINE_WIDTH, height, color = labels!!.pillarColor) renderRect(pose, drawPointX - HIGHLIGHT_WIDTH / 2f, drawPointY - HIGHLIGHT_WIDTH / 2f, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, color = labels.color) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt index fe3e49153..ff9ab2abc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt @@ -189,8 +189,8 @@ private fun Font.drawInternal( for ((_x, _y) in outlinePositions) { drawInBatch( text, - x * inv + _x, - y * inv + _y, + (x + _x) * inv, + (y + _y) * inv, outlineColor.toBGR(), drawShadow, poseStack.last().pose(), @@ -251,7 +251,7 @@ fun Font.draw( rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, drawOutline: Boolean = false, outlineColor: RGBAColor = RGBAColor.BLACK, - outlineZ: Float = 0.1f, + outlineZ: Float = -0.1f, ): Float { return drawInternal( poseStack = poseStack, @@ -294,7 +294,7 @@ data class TextIcon( val customShadow: Boolean = false, val drawOutline: Boolean = false, val outlineColor: RGBAColor = RGBAColor.BLACK, - val outlineZ: Float = 0.1f, + val outlineZ: Float = -0.1f, ) : IGUIRenderable { override val width: Float = font.width(text) * scale override val height: Float = font.lineHeight * scale @@ -352,7 +352,7 @@ fun Font.draw( rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, drawOutline: Boolean = false, outlineColor: RGBAColor = RGBAColor.BLACK, - outlineZ: Float = 0.1f, + outlineZ: Float = -0.1f, ): Float { return drawInternal( poseStack = poseStack, @@ -400,7 +400,7 @@ fun Font.draw( rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, drawOutline: Boolean = false, outlineColor: RGBAColor = RGBAColor.BLACK, - outlineZ: Float = 0.1f, + outlineZ: Float = -0.1f, ): Float { return drawInternal( poseStack = poseStack, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt index 1936ae50b..8d3d5f0c3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt @@ -1,7 +1,9 @@ package ru.dbotthepony.mc.otm.client.screen.panels +import it.unimi.dsi.fastutil.floats.Float2ObjectArrayMap import net.minecraft.network.chat.Component import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.render.ChartLevelLabels import ru.dbotthepony.mc.otm.client.render.ChartMouseLabels import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.renderChart @@ -23,13 +25,31 @@ open class DecimalHistoryChartPanel>( override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { val maximum = graph.max() val normalized: FloatArray + val levelLabels: ChartLevelLabels if (maximum.isZero || maximum.isInfinite) { normalized = FloatArray(graph.width) { if (graph[it].isInfinite) 0.8f else 0.0f } + + levelLabels = ChartLevelLabels( + labels = mapOf(0.8f to formatText(maximum)), + font = font + ) } else { normalized = FloatArray(graph.width) { (graph[it] / maximum).toFloat() * 0.9f } + + val map = Float2ObjectArrayMap() + + map[0.9f] = formatText(maximum) + map[0.9f * 0.75f] = formatText(maximum * 0.75) + map[0.9f * 0.5f] = formatText(maximum * 0.5) + map[0.9f * 0.25f] = formatText(maximum * 0.25) + + levelLabels = ChartLevelLabels( + labels = map, + font = font + ) } graphics.renderRect(0f, 0f, this.width, this.height, color = RGBAColor.BLACK) @@ -44,7 +64,8 @@ open class DecimalHistoryChartPanel>( mouseY - absoluteY, { formatText(graph[it]) }, font, - ) + ), + levelLabels = levelLabels ) }