From deeb997541cbfc8ecaf4108bea8efd0c2cb0cce3 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Sun, 5 Nov 2023 18:07:00 +0100 Subject: [PATCH] improve billboard text rendering --- .../feature/text/BillboardTextFeature.kt | 29 ++++++++++++------- .../feature/text/BillboardTextMesh.kt | 11 +++---- .../feature/text/EntityNameFeature.kt | 20 +++++++++++++ .../code/AscentedCodePointRenderer.kt | 2 +- .../component/ChatComponentRenderer.kt | 12 ++++++-- .../gui/rendering/util/mat/mat4/Mat4Util.kt | 15 ++++++++++ .../shader/entities/features/text/text.fsh | 2 +- .../shader/entities/features/text/text.vsh | 10 +++---- .../rendering/util/mat/mat4/Mat4UtilTest.kt | 18 ++++++++++++ 9 files changed, 95 insertions(+), 24 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextFeature.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextFeature.kt index 0b15361c4..1091bd3db 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextFeature.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextFeature.kt @@ -16,7 +16,6 @@ package de.bixilon.minosoft.gui.rendering.entities.feature.text import de.bixilon.kotlinglm.func.rad import de.bixilon.kotlinglm.mat4x4.Mat4 import de.bixilon.kotlinglm.vec2.Vec2 -import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.gui.rendering.entities.feature.EntityRenderFeature @@ -24,8 +23,9 @@ import de.bixilon.minosoft.gui.rendering.entities.renderer.EntityRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderInfo import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderProperties +import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions import de.bixilon.minosoft.gui.rendering.system.base.DepthFunctions -import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.rotateRadAssign +import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateXAssign import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateYAssign import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh @@ -33,6 +33,7 @@ open class BillboardTextFeature( renderer: EntityRenderer<*>, text: ChatComponent?, ) : EntityRenderFeature(renderer) { + override val priority: Int get() = 10000 private var mesh: BillboardTextMesh? = null private var info: TextRenderInfo? = null private var matrix = Mat4() @@ -57,18 +58,18 @@ open class BillboardTextFeature( val mesh = BillboardTextMesh(renderer.renderer.context) val info = ChatComponentRenderer.render3d(renderer.renderer.context, PROPERTIES, MAX_SIZE, mesh, text) - this.mesh = mesh this.info = info } private fun updateMatrix() { - val rotation = Vec3(180.0f.rad, (EntityRotation.HALF_CIRCLE_DEGREE - renderer.entity.renderInfo.rotation.yaw).rad, 180.0f.rad) + val width = this.info?.size?.x ?: return + val mat = renderer.renderer.context.camera.view.view.rotation val matrix = Mat4() - .translateYAssign(renderer.entity.eyeHeight + EYE_OFFSET) - .rotateRadAssign(rotation) - - // TODO: rotate with camera (billboard) + .translateYAssign(renderer.entity.dimensions.y + EYE_OFFSET) + .rotateYassign((EntityRotation.HALF_CIRCLE_DEGREE - mat.yaw).rad) + .rotateXassign(180.0f.rad - mat.pitch.rad) + .translateXAssign(-width / 2.0f * BillboardTextMesh.SCALE).translateYAssign(-PROPERTIES.lineHeight * BillboardTextMesh.SCALE) this.matrix = renderer.matrix * matrix } @@ -76,7 +77,15 @@ open class BillboardTextFeature( override fun draw() { val mesh = this.mesh ?: return if (mesh.state != Mesh.MeshStates.LOADED) mesh.load() - renderer.renderer.context.system.reset(depth = DepthFunctions.ALWAYS) + renderer.renderer.context.system.reset( + blending = true, + sourceRGB = BlendingFunctions.SOURCE_ALPHA, + destinationRGB = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA, + sourceAlpha = BlendingFunctions.SOURCE_ALPHA, + destinationAlpha = BlendingFunctions.DESTINATION_ALPHA, + depth = DepthFunctions.ALWAYS, + faceCulling = false, + ) val shader = renderer.renderer.features.text.shader shader.use() shader.matrix = matrix @@ -97,6 +106,6 @@ open class BillboardTextFeature( private companion object { val PROPERTIES = TextRenderProperties(allowNewLine = false) val MAX_SIZE = Vec2(150.0f, PROPERTIES.lineHeight) - const val EYE_OFFSET = 0.5f + const val EYE_OFFSET = 0.4f } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextMesh.kt index 0f5f11193..0afb58cbc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/BillboardTextMesh.kt @@ -26,6 +26,7 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct class BillboardTextMesh(context: RenderContext) : Mesh(context, BillboardTextMeshStruct), GUIVertexConsumer { + override val order = context.system.quadOrder override fun ensureSize(size: Int) { data.ensureSize(size) @@ -34,8 +35,8 @@ class BillboardTextMesh(context: RenderContext) : Mesh(context, BillboardTextMes override fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) { data.add(x * SCALE, y * SCALE) data.add(u, v) - data.add(texture?.shaderId?.buffer() ?: 0.0f) - data.add(tint.rgb.buffer()) + data.add((texture?.shaderId ?: context.textures.whiteTexture.texture.shaderId).buffer()) + data.add(tint.rgba.buffer()) } override fun addCache(cache: GUIMeshCache) = Broken("This is not a text only consumer!") @@ -44,12 +45,12 @@ class BillboardTextMesh(context: RenderContext) : Mesh(context, BillboardTextMes val position: Vec2, val uv: Vec2, val indexLayerAnimation: Int, - val lightTint: Int, + val tint: RGBColor, ) { companion object : MeshStruct(BillboardTextMeshStruct::class) } - private companion object { - const val SCALE = 0.03f + companion object { + const val SCALE = 0.02f } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/EntityNameFeature.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/EntityNameFeature.kt index 0afb63d9c..c07edd789 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/EntityNameFeature.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/feature/text/EntityNameFeature.kt @@ -13,6 +13,10 @@ package de.bixilon.minosoft.gui.rendering.entities.feature.text +import de.bixilon.kutil.cast.CastUtil.nullCast +import de.bixilon.minosoft.camera.target.targets.EntityTarget +import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity +import de.bixilon.minosoft.data.entities.entities.player.local.LocalPlayerEntity import de.bixilon.minosoft.gui.rendering.entities.renderer.EntityRenderer class EntityNameFeature(renderer: EntityRenderer<*>) : BillboardTextFeature(renderer, null) { @@ -23,8 +27,24 @@ class EntityNameFeature(renderer: EntityRenderer<*>) : BillboardTextFeature(rend } private fun updateName() { + if (!isNameVisible()) { + this.text = null + return + } val name = renderer.entity.name if (name == this.text) return this.text = name } + + private fun isNameVisible(): Boolean { + if (renderer.entity is PlayerEntity) return true + + val camera = renderer.renderer.connection.camera + val target = camera.target.target + if (target !is EntityTarget || target.entity !== renderer.entity) return false + + val distance = camera.entity.nullCast()?.reachDistance ?: 3.0 + if (target.distance > distance) return false + return true + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/code/AscentedCodePointRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/code/AscentedCodePointRenderer.kt index 2a73c0a9b..e85d3ff28 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/code/AscentedCodePointRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/code/AscentedCodePointRenderer.kt @@ -27,7 +27,7 @@ interface AscentedCodePointRenderer : RasterizedCodePointRenderer { override fun calculateStart(properties: TextRenderProperties, base: Vec2, scale: Float): Vec2 { val position = Vec2(base) - val offset = properties.charSpacing.top - (height - ascent) + val offset = properties.charSpacing.top - (height - ascent - 1.0f) position.y += offset * scale return position diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/component/ChatComponentRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/component/ChatComponentRenderer.kt index 11d6cdffa..28b3c65e8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/component/ChatComponentRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/font/renderer/component/ChatComponentRenderer.kt @@ -20,6 +20,8 @@ import de.bixilon.minosoft.data.text.BaseComponent import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.EmptyComponent import de.bixilon.minosoft.data.text.TextComponent +import de.bixilon.minosoft.data.text.formatting.color.RGBColor +import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMesh import de.bixilon.minosoft.gui.rendering.font.WorldGUIConsumer @@ -62,18 +64,24 @@ interface ChatComponentRenderer { mesh.data.ensureSize(primitives * mesh.order.size * ChunkMesh.ChunkMeshStruct.FLOATS_PER_VERTEX) val consumer = WorldGUIConsumer(mesh, matrix, light) - return render3d(context, properties, maxSize, consumer, text) + return render3d(context, properties, maxSize, consumer, text, null) } - fun render3d(context: RenderContext, properties: TextRenderProperties, maxSize: Vec2, mesh: GUIVertexConsumer, text: ChatComponent): TextRenderInfo { + fun render3d(context: RenderContext, properties: TextRenderProperties, maxSize: Vec2, mesh: GUIVertexConsumer, text: ChatComponent, background: RGBColor? = RenderConstants.TEXT_BACKGROUND_COLOR): TextRenderInfo { val primitives = calculatePrimitiveCount(text) mesh.ensureSize(primitives) val info = TextRenderInfo(maxSize) render(TextOffset(), context.font, properties, info, null, null, text) info.rewind() + if (background != null) { + mesh.addQuad(Vec2(-1, 0), info.size + Vec2(1, 0), background, null) + } + val size = info.size.x info.size.x = maxSize.x // this allows font aligning + render(TextOffset(), context.font, properties, info, mesh, null, text) + info.size.x = size return info } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt index 46da6f2a7..b44b56a8f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4Util.kt @@ -151,6 +151,13 @@ object Mat4Util { m[0, 3] = res0w } + fun Mat4.translateXAssign(vX: Float): Mat4 { + this[3, 0] += this[0, 0] * vX + this[3, 1] += this[0, 1] * vX + this[3, 2] += this[0, 2] * vX + this[3, 3] += this[0, 3] * vX + return this + } fun Mat4.translateYAssign(vY: Float): Mat4 { this[3, 0] += this[1, 0] * vY this[3, 1] += this[1, 1] * vY @@ -158,4 +165,12 @@ object Mat4Util { this[3, 3] += this[1, 3] * vY return this } + + fun Mat4.translateZAssign(vX: Float): Mat4 { + this[3, 0] += this[2, 0] * vX + this[3, 1] += this[2, 1] * vX + this[3, 2] += this[2, 2] * vX + this[3, 3] += this[2, 3] * vX + return this + } } diff --git a/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.fsh b/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.fsh index b620c244a..a1cf0c7c7 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.fsh +++ b/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.fsh @@ -21,6 +21,7 @@ in vec3 finTextureCoordinates; in vec4 finTintColor; +#define TRANSPARENT #include "minosoft:texture" #include "minosoft:alpha" @@ -31,7 +32,6 @@ void main() { discard_if_0(texelColor.a); foutColor = texelColor * finTintColor; - set_alpha_transparent(); set_fog(); } diff --git a/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.vsh b/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.vsh index 20981a15d..13beb3030 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.vsh +++ b/src/main/resources/assets/minosoft/rendering/shader/entities/features/text/text.vsh @@ -16,7 +16,7 @@ layout (location = 0) in vec2 vinPosition; layout (location = 1) in vec2 vinUV; layout (location = 2) in float vinIndexLayerAnimation;// texture index (0xF0000000), texture layer (0x0FFFF000) -layout (location = 3) in float vinLightTint;// Light (0xFF000000); 3 bytes color (0x00FFFFFF) +layout (location = 3) in float vinTint; uniform mat4 uViewProjectionMatrix; uniform mat4 uMatrix; @@ -34,10 +34,10 @@ out vec4 finTintColor; #include "minosoft:light" void main() { - gl_Position = uViewProjectionMatrix * uMatrix * vec4(vinPosition, 0.0f, 1.0f); - uint lightTint = floatBitsToUint(vinLightTint); - finTintColor = getRGBColor(lightTint & 0xFFFFFFu);// * getLight(lightTint >> 24u); // TODO - finFragmentPosition = vec3(vinPosition, 0.0f); + vec4 position = uMatrix * vec4(vinPosition, 0.0f, 1.0f); + gl_Position = uViewProjectionMatrix * position; + finTintColor = getRGBAColor(floatBitsToUint(vinTint)); // * getLight(lightTint >> 24u); // TODO + finFragmentPosition = position.xyz; uint indexLayerAnimation = floatBitsToUint(vinIndexLayerAnimation); finTextureIndex = indexLayerAnimation >> 28u; diff --git a/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt b/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt index 5db2c4541..6bb986dd4 100644 --- a/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt +++ b/src/test/java/de/bixilon/minosoft/gui/rendering/util/mat/mat4/Mat4UtilTest.kt @@ -16,7 +16,9 @@ package de.bixilon.minosoft.gui.rendering.util.mat.mat4 import de.bixilon.kotlinglm.func.rad import de.bixilon.kotlinglm.mat4x4.Mat4 import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateXAssign import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateYAssign +import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateZAssign import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -46,6 +48,14 @@ class Mat4UtilTest { assertEquals(expected, actual) } + @Test + fun `custom translateXAssign`() { + val expected = Mat4().translateAssign(Vec3(123.0f, 0, 0)) + val actual = Mat4().translateXAssign(123.0f) + + assertEquals(expected, actual) + } + @Test fun `custom translateYAssign`() { val expected = Mat4().translateAssign(Vec3(0, 123.0f, 0)) @@ -53,4 +63,12 @@ class Mat4UtilTest { assertEquals(expected, actual) } + + @Test + fun `custom translateZAssign`() { + val expected = Mat4().translateAssign(Vec3(0, 0, 123.0f)) + val actual = Mat4().translateZAssign(123.0f) + + assertEquals(expected, actual) + } }