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, mouse: Point) { drawTooltip(matrixStack, texts.map(Text::asOrderedText), mouse) } // Based on Screen.renderOrderedTooltip @JvmName("drawOrderedTooltip") fun drawTooltip(matrixStack: MatrixStack, texts: List, 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) } }