217 lines
7.1 KiB
Kotlin
217 lines
7.1 KiB
Kotlin
package net.shadowfacts.cacao.util
|
|
|
|
import com.mojang.blaze3d.platform.GlStateManager
|
|
import com.mojang.blaze3d.systems.RenderSystem
|
|
import net.minecraft.client.MinecraftClient
|
|
import net.minecraft.client.gui.DrawableHelper
|
|
import net.minecraft.client.render.*
|
|
import net.minecraft.client.sound.PositionedSoundInstance
|
|
import net.minecraft.client.util.math.MatrixStack
|
|
import net.minecraft.sound.SoundEvent
|
|
import net.minecraft.text.OrderedText
|
|
import net.minecraft.text.Text
|
|
import net.minecraft.util.math.Matrix4f
|
|
import net.shadowfacts.cacao.geometry.Point
|
|
import net.shadowfacts.cacao.geometry.Rect
|
|
import net.shadowfacts.cacao.util.texture.Texture
|
|
import org.lwjgl.opengl.GL11
|
|
|
|
/**
|
|
* Helper methods for rendering using Minecraft's utilities from Cacao views.
|
|
* For unit testing, all drawing and OpenGL interaction can be disabled by setting the `cacao.drawing.disabled` JVM property to `true`.
|
|
*
|
|
* @author shadowfacts
|
|
*/
|
|
object RenderHelper: DrawableHelper() {
|
|
|
|
val disabled = (System.getProperty("cacao.drawing.disabled") ?: "false").toBoolean()
|
|
|
|
// TODO: find a better place for this
|
|
fun playSound(event: SoundEvent) {
|
|
if (disabled) return
|
|
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(event, 1f))
|
|
}
|
|
|
|
/**
|
|
* Draws a solid [rect] filled with the given [color].
|
|
*/
|
|
fun fill(matrixStack: MatrixStack, rect: Rect, color: Color) {
|
|
if (disabled) return
|
|
fill(matrixStack, rect.left.toInt(), rect.top.toInt(), rect.right.toInt(), rect.bottom.toInt(), color.argb)
|
|
}
|
|
|
|
/**
|
|
* Binds and draws the given [texture] filling the [rect].
|
|
*/
|
|
fun draw(matrixStack: MatrixStack, rect: Rect, texture: Texture) {
|
|
if (disabled) return
|
|
color(1f, 1f, 1f, 1f)
|
|
MinecraftClient.getInstance().textureManager.bindTexture(texture.location)
|
|
draw(matrixStack, rect.left, rect.top, texture.u, texture.v, rect.width, rect.height, texture.width, texture.height)
|
|
}
|
|
|
|
fun drawLine(start: Point, end: Point, z: Double, width: Float, color: Color) {
|
|
if (disabled) return
|
|
|
|
GlStateManager.lineWidth(width)
|
|
val tessellator = Tessellator.getInstance()
|
|
val buffer = tessellator.buffer
|
|
buffer.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR)
|
|
buffer.vertex(start.x, start.y, z).color(color).next()
|
|
buffer.vertex(end.x, end.y, z).color(color).next()
|
|
tessellator.draw()
|
|
}
|
|
|
|
/**
|
|
* Draws the bound texture with the given screen and texture position and size.
|
|
*/
|
|
fun draw(matrixStack: MatrixStack, x: Double, y: Double, u: Int, v: Int, width: Double, height: Double, textureWidth: Int, textureHeight: Int) {
|
|
if (disabled) return
|
|
val uStart = u.toFloat() / textureWidth
|
|
val uEnd = (u + width).toFloat() / textureWidth
|
|
val vStart = v.toFloat() / textureHeight
|
|
val vEnd = (v + height).toFloat() / textureHeight
|
|
drawTexturedQuad(matrixStack.peek().model, x, x + width, y, y + height, 0.0, uStart, uEnd, vStart, vEnd)
|
|
}
|
|
|
|
// Copied from net.minecraft.client.gui.DrawableHelper
|
|
private fun drawTexturedQuad(matrix: Matrix4f, x0: Double, x1: Double, y0: Double, y1: Double, z: Double, u0: Float, u1: Float, v0: Float, v1: Float) {
|
|
val bufferBuilder = Tessellator.getInstance().buffer
|
|
bufferBuilder.begin(GL11.GL_QUADS, VertexFormats.POSITION_TEXTURE)
|
|
bufferBuilder.vertex(matrix, x0.toFloat(), y1.toFloat(), z.toFloat()).texture(u0, v1).next()
|
|
bufferBuilder.vertex(matrix, x1.toFloat(), y1.toFloat(), z.toFloat()).texture(u1, v1).next()
|
|
bufferBuilder.vertex(matrix, x1.toFloat(), y0.toFloat(), z.toFloat()).texture(u1, v0).next()
|
|
bufferBuilder.vertex(matrix, x0.toFloat(), y0.toFloat(), z.toFloat()).texture(u0, v0).next()
|
|
bufferBuilder.end()
|
|
RenderSystem.enableAlphaTest()
|
|
BufferRenderer.draw(bufferBuilder)
|
|
}
|
|
|
|
fun drawTooltip(matrixStack: MatrixStack, text: Text, mouse: Point) {
|
|
drawTooltip(matrixStack, listOf(text.asOrderedText()), mouse)
|
|
}
|
|
|
|
fun drawTooltip(matrixStack: MatrixStack, texts: List<Text>, mouse: Point) {
|
|
drawTooltip(matrixStack, texts.map(Text::asOrderedText), mouse)
|
|
}
|
|
|
|
// Based on Screen.renderOrderedTooltip
|
|
@JvmName("drawOrderedTooltip")
|
|
fun drawTooltip(matrixStack: MatrixStack, texts: List<OrderedText>, mouse: Point) {
|
|
if (disabled) return
|
|
if (texts.isEmpty()) return
|
|
|
|
val client = MinecraftClient.getInstance()
|
|
val textRenderer = client.textRenderer
|
|
|
|
val maxWidth = texts.maxOf(textRenderer::getWidth)
|
|
|
|
var x = mouse.x.toInt() + 12
|
|
var y = mouse.y.toInt() - 12
|
|
var p = 8
|
|
if (texts.size > 1) {
|
|
p += 2 + (texts.size - 1) * 8
|
|
}
|
|
|
|
if (x + maxWidth > client.window.scaledWidth) {
|
|
x -= 28 + maxWidth
|
|
}
|
|
|
|
if (y + p + 6 > client.window.scaledHeight) {
|
|
y = client.window.scaledHeight - p - 6
|
|
}
|
|
|
|
matrixStack.push()
|
|
val q = -267386864
|
|
val r = 1347420415
|
|
val s = 1344798847
|
|
val t = 1
|
|
val tessellator = Tessellator.getInstance()
|
|
val buffer = tessellator.buffer
|
|
buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR)
|
|
val matrix = matrixStack.peek().model
|
|
val z = 400
|
|
fillGradient(matrix, buffer, x - 3, y - 4, x + maxWidth + 3, y - 3, z, q, q)
|
|
fillGradient(matrix, buffer, x - 3, y + p + 3, x + maxWidth + 3, y + p + 4, z, q, q)
|
|
fillGradient(matrix, buffer, x - 3, y - 3, x + maxWidth + 3, y + p + 3, z, q, q)
|
|
fillGradient(matrix, buffer, x - 4, y - 3, x - 3, y + p + 3, z, q, q)
|
|
fillGradient(matrix, buffer, x + maxWidth + 3, y - 3, x + maxWidth + 4, y + p + 3, z, q, q)
|
|
fillGradient(matrix, buffer, x - 3, y - 3 + 1, x - 3 + 1, y + p + 3 - 1, z, r, s)
|
|
fillGradient(matrix, buffer, x + maxWidth + 2, y - 3 + 1, x + maxWidth + 3, y + p + 3 - 1, z, r, s)
|
|
fillGradient(matrix, buffer, x - 3, y - 3, x + maxWidth + 3, y - 3 + 1, z, r, r)
|
|
fillGradient(matrix, buffer, x - 3, y + p + 2, x + maxWidth + 3, y + p + 3, z, s, s)
|
|
RenderSystem.enableDepthTest()
|
|
RenderSystem.disableTexture()
|
|
RenderSystem.enableBlend()
|
|
RenderSystem.defaultBlendFunc()
|
|
RenderSystem.shadeModel(7425)
|
|
buffer.end()
|
|
BufferRenderer.draw(buffer)
|
|
RenderSystem.shadeModel(7424)
|
|
RenderSystem.disableBlend()
|
|
RenderSystem.enableTexture()
|
|
val immediate = VertexConsumerProvider.immediate(buffer)
|
|
|
|
matrixStack.translate(0.0, 0.0, 400.0)
|
|
|
|
for (i in texts.indices) {
|
|
val text = texts[i]
|
|
textRenderer.draw(text, x.toFloat(), y.toFloat(), -1, true, matrix, immediate, false, 0, 15728880)
|
|
|
|
if (i == 0) {
|
|
y += 2
|
|
}
|
|
|
|
y += 10
|
|
}
|
|
|
|
immediate.draw()
|
|
matrixStack.pop()
|
|
}
|
|
|
|
/**
|
|
* @see org.lwjgl.opengl.GL11.glPushMatrix
|
|
*/
|
|
fun pushMatrix() {
|
|
if (disabled) return
|
|
RenderSystem.pushMatrix()
|
|
}
|
|
|
|
/**
|
|
* @see org.lwjgl.opengl.GL11.glPopMatrix
|
|
*/
|
|
fun popMatrix() {
|
|
if (disabled) return
|
|
RenderSystem.popMatrix()
|
|
}
|
|
|
|
/**
|
|
* @see org.lwjgl.opengl.GL11.glTranslated
|
|
*/
|
|
fun translate(x: Double, y: Double, z: Double = 0.0) {
|
|
if (disabled) return
|
|
RenderSystem.translated(x, y, z)
|
|
}
|
|
|
|
/**
|
|
* @see org.lwjgl.opengl.GL11.glScaled
|
|
*/
|
|
fun scale(x: Double, y: Double, z: Double = 1.0) {
|
|
if (disabled) return
|
|
RenderSystem.scaled(x, y, z)
|
|
}
|
|
|
|
/**
|
|
* @see org.lwjgl.opengl.GL11.glColor4f
|
|
*/
|
|
fun color(r: Float, g: Float, b: Float, alpha: Float) {
|
|
if (disabled) return
|
|
RenderSystem.color4f(r, g, b, alpha)
|
|
}
|
|
|
|
private fun VertexConsumer.color(color: Color): VertexConsumer {
|
|
return color(color.red, color.green, color.blue, color.alpha)
|
|
}
|
|
|
|
}
|