mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-11 16:36:58 -04:00
improve billboard text rendering
This commit is contained in:
parent
b295905fb1
commit
deeb997541
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user