improve billboard text rendering

This commit is contained in:
Moritz Zwerger 2023-11-05 18:07:00 +01:00
parent b295905fb1
commit deeb997541
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 95 additions and 24 deletions

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<LocalPlayerEntity>()?.reachDistance ?: 3.0
if (target.distance > distance) return false
return true
}
}

View File

@ -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

View File

@ -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<T : ChatComponent> {
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
}

View File

@ -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
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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)
}
}