refactor font rendering more

This commit is contained in:
Bixilon 2023-06-05 18:39:57 +02:00
parent 761ea0a582
commit d9c74a71d7
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
28 changed files with 298 additions and 181 deletions

View File

@ -57,6 +57,7 @@ class BaseComponent : ChatComponent {
json["strikethrough"]?.toBoolean()?.let { formatting[FormattingCodes.STRIKETHROUGH] = it } json["strikethrough"]?.toBoolean()?.let { formatting[FormattingCodes.STRIKETHROUGH] = it }
json["obfuscated"]?.toBoolean()?.let { formatting[FormattingCodes.OBFUSCATED] = it } json["obfuscated"]?.toBoolean()?.let { formatting[FormattingCodes.OBFUSCATED] = it }
val font = json["font"]?.toResourceLocation() ?: parent?.font
val clickEvent = json["clickEvent", "click_event"]?.toJsonObject()?.let { click -> ClickEvents.build(click, restricted) } ?: parent?.clickEvent val clickEvent = json["clickEvent", "click_event"]?.toJsonObject()?.let { click -> ClickEvents.build(click, restricted) } ?: parent?.clickEvent
val hoverEvent = json["hoverEvent", "hover_event"]?.toJsonObject()?.let { hover -> HoverEvents.build(hover, restricted) } ?: parent?.hoverEvent val hoverEvent = json["hoverEvent", "hover_event"]?.toJsonObject()?.let { hover -> HoverEvents.build(hover, restricted) } ?: parent?.hoverEvent
@ -66,6 +67,7 @@ class BaseComponent : ChatComponent {
message = text, message = text,
color = color, color = color,
formatting = formatting, formatting = formatting,
font = font,
clickEvent = clickEvent, clickEvent = clickEvent,
hoverEvent = hoverEvent, hoverEvent = hoverEvent,
) )

View File

@ -50,15 +50,15 @@ object LegacyComponentReader {
} }
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
// an url follows, push the previous part // an url follows, push the previous part
this += TextComponent(text, sequence.color, sequence.formatting.copy(), parent?.clickEvent, parent?.hoverEvent) this += TextComponent(text, sequence.color, sequence.formatting.copy(), null, parent?.clickEvent, parent?.hoverEvent)
text.clear() text.clear()
} }
this += TextComponent(part, sequence.color, sequence.formatting.copy(), parent?.clickEvent ?: event, parent?.hoverEvent) this += TextComponent(part, sequence.color, sequence.formatting.copy(), null, parent?.clickEvent ?: event, parent?.hoverEvent)
} }
if (text.isNotEmpty()) { if (text.isNotEmpty()) {
// data that was not pushed yet // data that was not pushed yet
this += TextComponent(text, sequence.color, sequence.formatting.copy(), parent?.clickEvent, parent?.hoverEvent) this += TextComponent(text, sequence.color, sequence.formatting.copy(), null, parent?.clickEvent, parent?.hoverEvent)
} }
sequence.reset() // clear it up again for next usage sequence.reset() // clear it up again for next usage

View File

@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.text
import de.bixilon.kutil.json.MutableJsonObject import de.bixilon.kutil.json.MutableJsonObject
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.events.click.ClickEvent import de.bixilon.minosoft.data.text.events.click.ClickEvent
import de.bixilon.minosoft.data.text.events.hover.HoverEvent import de.bixilon.minosoft.data.text.events.hover.HoverEvent
import de.bixilon.minosoft.data.text.formatting.FormattingCodes import de.bixilon.minosoft.data.text.formatting.FormattingCodes
@ -37,6 +38,7 @@ open class TextComponent(
message: Any? = "", message: Any? = "",
override var color: RGBColor? = null, override var color: RGBColor? = null,
override val formatting: TextFormatting = TextFormatting(), override val formatting: TextFormatting = TextFormatting(),
var font: ResourceLocation? = null,
var clickEvent: ClickEvent? = null, var clickEvent: ClickEvent? = null,
var hoverEvent: HoverEvent? = null, var hoverEvent: HoverEvent? = null,
) : ChatComponent, TextStyle { ) : ChatComponent, TextStyle {
@ -224,7 +226,7 @@ open class TextComponent(
} }
override fun copy(): ChatComponent { override fun copy(): ChatComponent {
return TextComponent(message, color, formatting.copy(), clickEvent, hoverEvent) return TextComponent(message, color, formatting.copy(), font, clickEvent, hoverEvent)
} }
override fun hashCode(): Int { override fun hashCode(): Int {

View File

@ -14,13 +14,7 @@
package de.bixilon.minosoft.gui.rendering.font package de.bixilon.minosoft.gui.rendering.font
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.math.simple.FloatMath.ceil
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.types.font.Font
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
@Deprecated("renderer") @Deprecated("renderer")
@ -31,106 +25,4 @@ class CharData(
val scaledWidth: Int, val scaledWidth: Int,
var uvStart: Vec2, var uvStart: Vec2,
var uvEnd: Vec2, var uvEnd: Vec2,
) { )
fun postInit() {
if (texture == null) {
return
}
uvStart = uvStart * texture.textureArrayUV
uvEnd = uvEnd * texture.textureArrayUV
}
fun render(position: Vec2i, color: RGBColor, shadow: Boolean, italic: Boolean, bold: Boolean, strikethrough: Boolean, underlined: Boolean, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) {
if (shadow) {
_render(position, color, true, italic, bold, strikethrough, underlined, consumer, options, scale)
}
_render(position, color, false, italic, bold, strikethrough, underlined, consumer, options, scale)
}
private fun GUIVertexConsumer.addQuad(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
val italicOffset = if (italic) (end.y - start.y) / Font.CHAR_HEIGHT.toFloat() * ITALIC_OFFSET else 0.0f
val positions = arrayOf(
Vec2(start.x + italicOffset, start.y),
Vec2(end.x + italicOffset, start.y),
end,
Vec2(start.x, end.y),
)
val texturePositions = arrayOf(
Vec2(uvEnd.x, uvStart.y),
uvStart,
Vec2(uvStart.x, uvEnd.y),
uvEnd,
)
for ((vertexIndex, textureIndex) in this.order) {
addVertex(positions[vertexIndex], texture, texturePositions[textureIndex], tint, options)
}
}
private fun _render(position: Vec2i, color: RGBColor, shadow: Boolean, italic: Boolean, bold: Boolean, strikethrough: Boolean, underlined: Boolean, vertexConsumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) {
if (texture == null) {
return
}
var color = color
var shadowOffset = 0.0f
if (shadow) {
shadowOffset = SHADOW_OFFSET
color *= 0.25f
}
var boldOffset = 0.0f
if (bold) {
boldOffset = BOLD_OFFSET * scale
}
val charHeight = Font.CHAR_HEIGHT * scale
val horizontalSpacing = Font.HORIZONTAL_SPACING * scale
val verticalSpacing = Font.VERTICAL_SPACING * scale
val startPosition = Vec2(position) + (shadowOffset * scale)
val endPosition = startPosition + (Vec2(scaledWidth * scale, charHeight))
vertexConsumer.addQuad(startPosition, endPosition, texture, uvStart, uvEnd, italic, color, options)
if (bold) {
vertexConsumer.addQuad(startPosition + Vec2(boldOffset, 0.0f), endPosition + Vec2(boldOffset, 0.0f), texture, uvStart, uvEnd, italic, color, options)
}
val whiteTexture = context.textureManager.whiteTexture
if (strikethrough) {
vertexConsumer.addQuad(startPosition + Vec2(-horizontalSpacing, charHeight / 2.0f - scale / 2), Vec2(endPosition.x + horizontalSpacing, startPosition.y + charHeight / 2.0f + scale / 2), whiteTexture.texture, whiteTexture.uvStart, whiteTexture.uvEnd, italic, color, options)
}
if (underlined) {
vertexConsumer.addQuad(startPosition + Vec2(-horizontalSpacing, charHeight), Vec2(endPosition.x + boldOffset + horizontalSpacing, startPosition.y + charHeight + verticalSpacing / 2.0f), whiteTexture.texture, whiteTexture.uvStart, whiteTexture.uvEnd, italic, color, options)
}
// ToDo: Obfuscated
}
fun calculateWidth(scale: Float, shadow: Boolean): Int {
var width = scaledWidth.toFloat()
if (shadow) {
width += SHADOW_OFFSET
}
return (width * scale).ceil
}
fun render3d(consumer: WorldGUIConsumer, color: RGBColor, shadow: Boolean, italic: Boolean, bold: Boolean, strikethrough: Boolean, underlined: Boolean, scale: Float): Float {
render(Vec2i(0, 0), color, shadow, italic, bold, strikethrough, underlined, consumer, null, scale)
return scaledWidth.toFloat()
}
companion object {
const val ITALIC_OFFSET = 2.5f
const val SHADOW_OFFSET = 1.0f
const val BOLD_OFFSET = 0.5f
}
}

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.font
import de.bixilon.kotlinglm.mat4x4.Mat4 import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
@ -26,6 +27,7 @@ import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
class WorldGUIConsumer(val mesh: SingleWorldMesh, val transform: Mat4, val light: Int) : GUIVertexConsumer { class WorldGUIConsumer(val mesh: SingleWorldMesh, val transform: Mat4, val light: Int) : GUIVertexConsumer {
override val context: RenderContext get() = mesh.context
override val order: Array<Pair<Int, Int>> get() = mesh.order override val order: Array<Pair<Int, Int>> get() = mesh.order
override fun addVertex(position: Vec2, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) { override fun addVertex(position: Vec2, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {

View File

@ -16,18 +16,31 @@ package de.bixilon.minosoft.gui.rendering.font.manager
import de.bixilon.kutil.latch.AbstractLatch import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.loader.DefaultFontIndices
import de.bixilon.minosoft.gui.rendering.font.loader.FontLoader
import de.bixilon.minosoft.gui.rendering.font.types.FontType import de.bixilon.minosoft.gui.rendering.font.types.FontType
import de.bixilon.minosoft.gui.rendering.font.types.PostInitFontType
import de.bixilon.minosoft.gui.rendering.font.types.font.EmptyFont
import de.bixilon.minosoft.gui.rendering.font.types.font.Font import de.bixilon.minosoft.gui.rendering.font.types.font.Font
class FontManager { class FontManager(
val default: FontType val default: FontType,
) {
fun postInit(latch: AbstractLatch) fun postInit(latch: AbstractLatch) {
if (default is PostInitFontType) {
default.postInit(latch)
}
}
operator fun get(font: ResourceLocation): Font? operator fun get(font: ResourceLocation?): Font? = null
companion object { companion object {
fun create(context: RenderContext, latch: AbstractLatch): FontManager {} fun create(context: RenderContext, latch: AbstractLatch): FontManager {
val font = FontLoader.load(context, DefaultFontIndices.DEFAULT, latch)
return FontManager(font ?: EmptyFont)
}
} }
} }

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.font.renderer.code
/**
* Font that is shifted vertically
* See the great explanation of @Suragch at https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font
*/
interface AscentedCodePointRenderer : CodePointRenderer {
val descent: Float
val ascent: Float
}

View File

@ -16,12 +16,20 @@ package de.bixilon.minosoft.gui.rendering.font.renderer.code
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.data.text.formatting.TextFormatting import de.bixilon.minosoft.data.text.formatting.TextFormatting
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.font.WorldGUIConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
interface CodePointRenderer { interface CodePointRenderer {
fun calculateWidth(scale: Float, shadow: Boolean): Float fun calculateWidth(scale: Float, shadow: Boolean): Float
fun render(position: Vec2, color: RGBColor, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) fun render(position: Vec2, color: RGBColor, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float)
fun render3d(consumer: WorldGUIConsumer, color: RGBColor, shadow: Boolean, formatting: TextFormatting, scale: Float): Float {
render(Vec2.EMPTY, color, shadow, formatting, consumer, null, scale)
return calculateWidth(scale, shadow)
}
} }

View File

@ -0,0 +1,127 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.font.renderer.code
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.data.text.formatting.FormattingCodes
import de.bixilon.minosoft.data.text.formatting.TextFormatting
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties.BOLD_OFFSET
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties.ITALIC_OFFSET
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties.SHADOW_COLOR
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties.SHADOW_OFFSET
import de.bixilon.minosoft.gui.rendering.font.types.font.Font
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
interface RasterizedCodePointRenderer : CodePointRenderer {
val texture: AbstractTexture
val uvStart: Vec2
val uvEnd: Vec2
val width: Float
override fun calculateWidth(scale: Float, shadow: Boolean): Float {
var width = width
if (shadow) {
width += SHADOW_OFFSET
}
return width * scale
}
override fun render(position: Vec2, color: RGBColor, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) {
if (shadow) {
render(position + (SHADOW_OFFSET * scale), color * SHADOW_COLOR, formatting, consumer, options, scale)
}
render(position, color, formatting, consumer, options, scale)
}
fun calculateStart(base: Vec2, scale: Float): Vec2 {
val position = Vec2(base)
position.y += (FontProperties.CHAR_SPACING_TOP * scale)
return position
}
fun calculateEnd(start: Vec2, scale: Float): Vec2 {
val position = Vec2(start)
position.y += (FontProperties.CHAR_BASE_HEIGHT * scale)
position.x += width * scale
return position
}
private fun render(position: Vec2, color: RGBColor, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) {
var boldOffset = 0.0f
val bold = FormattingCodes.BOLD in formatting
val italic = FormattingCodes.ITALIC in formatting
if (bold) {
boldOffset = BOLD_OFFSET * scale
}
val charHeight = FontProperties.CHAR_BASE_HEIGHT * scale
val horizontalSpacing = Font.HORIZONTAL_SPACING * scale
val verticalSpacing = Font.VERTICAL_SPACING * scale
val startPosition = calculateStart(position, scale)
val endPosition = calculateEnd(startPosition, scale)
consumer.addQuad(startPosition, endPosition, texture, uvStart, uvEnd, italic, color, options)
if (FormattingCodes.BOLD in formatting) {
consumer.addQuad(startPosition + Vec2(boldOffset, 0.0f), endPosition + Vec2(boldOffset, 0.0f), texture, uvStart, uvEnd, italic, color, options)
}
val whiteTexture = consumer.context.textureManager.whiteTexture
if (FormattingCodes.STRIKETHROUGH in formatting) {
consumer.addQuad(startPosition + Vec2(-horizontalSpacing, charHeight / 2.0f - scale / 2), Vec2(endPosition.x + horizontalSpacing, startPosition.y + charHeight / 2.0f + scale / 2), whiteTexture.texture, whiteTexture.uvStart, whiteTexture.uvEnd, italic, color, options)
}
if (FormattingCodes.UNDERLINED in formatting) {
consumer.addQuad(startPosition + Vec2(-horizontalSpacing, charHeight), Vec2(endPosition.x + boldOffset + horizontalSpacing, startPosition.y + charHeight + verticalSpacing / 2.0f), whiteTexture.texture, whiteTexture.uvStart, whiteTexture.uvEnd, italic, color, options)
}
}
private fun GUIVertexConsumer.addQuad(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
val topOffset = if (italic) (end.y - start.y) / FontProperties.CHAR_BASE_HEIGHT * ITALIC_OFFSET else 0.0f
val positions = arrayOf(
Vec2(start.x + topOffset, start.y),
Vec2(end.x + topOffset, start.y),
end,
Vec2(start.x, end.y),
)
val texturePositions = arrayOf(
Vec2(uvEnd.x, uvStart.y),
uvStart,
Vec2(uvStart.x, uvEnd.y),
uvEnd,
)
for ((vertexIndex, textureIndex) in this.order) {
addVertex(positions[vertexIndex], texture, texturePositions[textureIndex], tint, options)
}
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.font.renderer.component package de.bixilon.minosoft.gui.rendering.font.renderer.component
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.BaseComponent import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
@ -24,7 +25,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
object BaseComponentRenderer : ChatComponentRenderer<BaseComponent> { object BaseComponentRenderer : ChatComponentRenderer<BaseComponent> {
override fun render(initialOffset: Vec2i, offset: Vec2i, size: Vec2i, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: BaseComponent): Boolean { override fun render(initialOffset: Vec2, offset: Vec2, size: Vec2, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: BaseComponent): Boolean {
for (part in text.parts) { for (part in text.parts) {
if (ChatComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, part)) { if (ChatComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, part)) {
return true return true

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.rendering.font.renderer.component package de.bixilon.minosoft.gui.rendering.font.renderer.component
import de.bixilon.kotlinglm.mat4x4.Mat4 import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.text.BaseComponent import de.bixilon.minosoft.data.text.BaseComponent
@ -35,7 +36,7 @@ interface ChatComponentRenderer<T : ChatComponent> {
/** /**
* Returns true if the text exceeded the maximum size * Returns true if the text exceeded the maximum size
*/ */
fun render(initialOffset: Vec2i, offset: Vec2i, size: Vec2i, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: T): Boolean fun render(initialOffset: Vec2, offset: Vec2, size: Vec2, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: T): Boolean
fun render3dFlat(context: RenderContext, offset: Vec2i, scale: Float, maxSize: Vec2i, consumer: WorldGUIConsumer, text: T, light: Int) fun render3dFlat(context: RenderContext, offset: Vec2i, scale: Float, maxSize: Vec2i, consumer: WorldGUIConsumer, text: T, light: Int)
@ -44,7 +45,7 @@ interface ChatComponentRenderer<T : ChatComponent> {
companion object : ChatComponentRenderer<ChatComponent> { companion object : ChatComponentRenderer<ChatComponent> {
const val TEXT_BLOCK_RESOLUTION = 128 const val TEXT_BLOCK_RESOLUTION = 128
override fun render(initialOffset: Vec2i, offset: Vec2i, size: Vec2i, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: ChatComponent): Boolean { override fun render(initialOffset: Vec2, offset: Vec2, size: Vec2, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: ChatComponent): Boolean {
return when (text) { return when (text) {
is BaseComponent -> BaseComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, text) is BaseComponent -> BaseComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, text)
is TextComponent -> TextComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, text) is TextComponent -> TextComponentRenderer.render(initialOffset, offset, size, element, context, consumer, options, renderInfo, text)

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.font.renderer.component package de.bixilon.minosoft.gui.rendering.font.renderer.component
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.data.text.formatting.FormattingCodes import de.bixilon.minosoft.data.text.formatting.FormattingCodes
@ -29,22 +30,20 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
object TextComponentRenderer : ChatComponentRenderer<TextComponent> { object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
override fun render(initialOffset: Vec2i, offset: Vec2i, size: Vec2i, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: TextComponent): Boolean { override fun render(initialOffset: Vec2, offset: Vec2, size: Vec2, element: Element, context: RenderContext, consumer: GUIVertexConsumer?, options: GUIVertexOptions?, renderInfo: TextRenderInfo, text: TextComponent): Boolean {
if (text.message.isEmpty()) { if (text.message.isEmpty()) {
return false return false
} }
val font = context.font[text.font]
val elementMaxSize = element.maxSize val elementMaxSize = element.maxSize
val elementSize = element.size val elementSize = element.size
val color = text.color ?: ChatColors.WHITE val color = text.color ?: ChatColors.WHITE
val shadow = renderInfo.shadow val shadow = renderInfo.shadow
val italic: Boolean = text.formatting.contains(FormattingCodes.ITALIC)
val bold: Boolean = text.formatting.contains(FormattingCodes.BOLD) val bold: Boolean = text.formatting.contains(FormattingCodes.BOLD)
val strikethrough: Boolean = text.formatting.contains(FormattingCodes.STRIKETHROUGH)
val underlined: Boolean = text.formatting.contains(FormattingCodes.UNDERLINED)
// ToDo: Only 1 quad for the underline and the strikethrough // ToDo: Only 1 quad for the underline and the strikethrough
var alignmentXOffset = 0 var alignmentXOffset = 0.0f
var currentLineText = "" var currentLineText = ""
if (size.x > elementMaxSize.x || size.y > elementMaxSize.y) { if (size.x > elementMaxSize.x || size.y > elementMaxSize.y) {
// The size is already bigger/equals the maximum size // The size is already bigger/equals the maximum size
@ -65,15 +64,15 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
fun applyOffset() { fun applyOffset() {
val lastLine = renderInfo.lines.getOrNull(renderInfo.lineIndex) val lastLine = renderInfo.lines.getOrNull(renderInfo.lineIndex)
if (consumer == null && offset.x == initialOffset.x + renderInfo.charMargin && (lastLine == null || lastLine.width != 0)) { if (consumer == null && offset.x == initialOffset.x + renderInfo.charMargin && (lastLine == null || lastLine.width != 0.0f)) {
// preparing phase // preparing phase
renderInfo.lines += TextLineInfo() renderInfo.lines += TextLineInfo()
} else { } else {
alignmentXOffset = renderInfo.fontAlignment.getOffset(elementSize.x, renderInfo.currentLine.width) alignmentXOffset = renderInfo.fontAlignment.getOffset(elementSize.x.toFloat(), renderInfo.currentLine.width)
} }
} }
fun addY(height: Int): Boolean { fun addY(height: Float): Boolean {
val nextY = offset.y + height val nextY = offset.y + height
val nextSizeY = nextY - initialOffset.y + renderInfo.charHeight // add initial height for chars + end margin val nextSizeY = nextY - initialOffset.y + renderInfo.charHeight // add initial height for chars + end margin
if (nextSizeY > elementMaxSize.y) { if (nextSizeY > elementMaxSize.y) {
@ -97,7 +96,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
return false return false
} }
fun addX(width: Int, wrap: Boolean = true): Boolean { fun addX(width: Float, wrap: Boolean = true): Boolean {
val nextX = offset.x + width val nextX = offset.x + width
val nextSizeX = nextX - initialOffset.x - renderInfo.charMargin // end margin val nextSizeX = nextX - initialOffset.x - renderInfo.charMargin // end margin
if (nextSizeX > elementMaxSize.x) { if (nextSizeX > elementMaxSize.x) {
@ -121,7 +120,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
} }
if (size.y == 0) { if (size.y == 0.0f) {
// Add initial height of the letter for the first line // Add initial height of the letter for the first line
val nextSizeY = renderInfo.charHeight val nextSizeY = renderInfo.charHeight
if (nextSizeY > elementMaxSize.y) { if (nextSizeY > elementMaxSize.y) {
@ -147,7 +146,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
continue continue
} }
val charData = context.font[char] ?: continue val charData = font?.get(char) ?: context.font.default[char] ?: continue
val charWidth = charData.calculateWidth(renderInfo.scale, renderInfo.shadow) val charWidth = charData.calculateWidth(renderInfo.scale, renderInfo.shadow)
var width = charWidth var width = charWidth
@ -168,7 +167,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
return true return true
} }
val letterOffset = Vec2i(offset.x + alignmentXOffset, offset.y) val letterOffset = Vec2(offset.x + alignmentXOffset, offset.y)
// remove width from the offset again // remove width from the offset again
letterOffset.x -= charWidth letterOffset.x -= charWidth
@ -178,7 +177,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
// ToDo: Remove Font.HORIZONTAL_SPACING // ToDo: Remove Font.HORIZONTAL_SPACING
} }
consumer?.let { charData.render(letterOffset, color, shadow, italic, bold, strikethrough, underlined, it, options, renderInfo.scale) } consumer?.let { charData.render(letterOffset, color, shadow, text.formatting, it, options, renderInfo.scale) }
if (consumer == null) { if (consumer == null) {
currentLineText += char.toChar() currentLineText += char.toChar()
@ -207,18 +206,16 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
override fun render3dFlat(context: RenderContext, offset: Vec2i, scale: Float, maxSize: Vec2i, consumer: WorldGUIConsumer, text: TextComponent, light: Int) { override fun render3dFlat(context: RenderContext, offset: Vec2i, scale: Float, maxSize: Vec2i, consumer: WorldGUIConsumer, text: TextComponent, light: Int) {
val color = text.color ?: ChatColors.BLACK val color = text.color ?: ChatColors.BLACK
val italic = text.formatting.contains(FormattingCodes.ITALIC)
val bold = text.formatting.contains(FormattingCodes.BOLD) val font = context.font[text.font]
val strikethrough = text.formatting.contains(FormattingCodes.STRIKETHROUGH)
val underlined = text.formatting.contains(FormattingCodes.UNDERLINED)
for (char in text.message.codePoints()) { for (char in text.message.codePoints()) {
val data = context.font[char] ?: continue val data = font?.get(char) ?: context.font.default[char] ?: continue
val expectedWidth = ((data.width + Font.HORIZONTAL_SPACING) * scale).toInt() val expectedWidth = ((data.calculateWidth(scale, false) + Font.HORIZONTAL_SPACING) * scale).toInt()
if (maxSize.x - offset.x < expectedWidth) { // ToDo if (maxSize.x - offset.x < expectedWidth) { // ToDo
return return
} }
val width = ((data.render3d(consumer, color, shadow = false, italic = italic, bold = bold, strikethrough = strikethrough, underlined = underlined, scale = scale) + Font.HORIZONTAL_SPACING) * scale).toInt() val width = ((data.render3d(consumer, color, shadow = false, text.formatting, scale = scale) + Font.HORIZONTAL_SPACING) * scale).toInt()
offset.x += width offset.x += width
consumer.offset((width / ChatComponentRenderer.TEXT_BLOCK_RESOLUTION.toFloat())) consumer.offset((width / ChatComponentRenderer.TEXT_BLOCK_RESOLUTION.toFloat()))
} }

View File

@ -17,5 +17,5 @@ import de.bixilon.minosoft.data.text.BaseComponent
data class TextLineInfo( data class TextLineInfo(
val text: BaseComponent = BaseComponent(), val text: BaseComponent = BaseComponent(),
var width: Int = 0, var width: Float = 0.0f,
) )

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.util.logging.LogMessageType
class TextRenderInfo( class TextRenderInfo(
val fontAlignment: HorizontalAlignments, val fontAlignment: HorizontalAlignments,
val charHeight: Int, val charHeight: Float,
val charMargin: Int, val charMargin: Int,
val scale: Float, val scale: Float,
val shadow: Boolean, val shadow: Boolean,

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.font.renderer.properties
object FontProperties {
const val MAX_CHAR_WIDTH = 16 // maximum width that a char can have
const val CHAR_BASE_HEIGHT = 8 // base height of every char, it is allowed to take up the spacing if needed
const val CHAR_SPACING_TOP = 1
const val CHAR_SPACING_BOTTOM = 2 // larger to not break underlined text
const val LINE_HEIGHT = CHAR_SPACING_TOP + CHAR_BASE_HEIGHT + CHAR_SPACING_BOTTOM
}

View File

@ -11,10 +11,13 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.font.renderer package de.bixilon.minosoft.gui.rendering.font.renderer.properties
object FontProperties { object FormattingProperties {
const val MAX_CHAR_WIDTH = 16 // maximum width that a char can have const val ITALIC_OFFSET = 2.5f
const val CHAR_HEIGHT = 12 // height every char has const val SHADOW_OFFSET = 1.0f
const val CHAR_SPACING = 1 // spacing every char has around itself const val BOLD_OFFSET = 0.5f
const val SHADOW_COLOR = 0.25f
} }

View File

@ -14,16 +14,20 @@
package de.bixilon.minosoft.gui.rendering.font.types.bitmap package de.bixilon.minosoft.gui.rendering.font.types.bitmap
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.code.AscentedCodePointRenderer
import de.bixilon.minosoft.gui.rendering.font.renderer.code.RasterizedCodePointRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
class BitmapCodeRenderer( class BitmapCodeRenderer(
val texture: AbstractTexture, override val texture: AbstractTexture,
var uvStart: Vec2, override var uvStart: Vec2,
var uvEnd: Vec2, override var uvEnd: Vec2,
val width: Float, override val width: Float,
) : CodePointRenderer { ) : RasterizedCodePointRenderer, AscentedCodePointRenderer {
override val ascent: Float
get() = 1.0f
override val descent: Float
get() = 1.0f
fun updateArray() { fun updateArray() {
uvStart = uvStart * texture.textureArrayUV uvStart = uvStart * texture.textureArrayUV

View File

@ -16,8 +16,8 @@ package de.bixilon.minosoft.gui.rendering.font.types.empty
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.data.text.formatting.TextFormatting import de.bixilon.minosoft.data.text.formatting.TextFormatting
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.font.renderer.FontProperties.MAX_CHAR_WIDTH
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties.MAX_CHAR_WIDTH
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
@ -31,5 +31,5 @@ class EmptyCodeRenderer(
} }
override fun calculateWidth(scale: Float, shadow: Boolean): Float = width * scale override fun calculateWidth(scale: Float, shadow: Boolean): Float = width * scale
override fun render(position: Vec2, color: RGBColor, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) = Unit override fun render(position: Vec2, color: RGBColor, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) = Unit
} }

View File

@ -57,7 +57,7 @@ class EmptyFontType(
fun load(data: JsonObject): Int2ObjectOpenHashMap<EmptyCodeRenderer>? { fun load(data: JsonObject): Int2ObjectOpenHashMap<EmptyCodeRenderer>? {
val advances = data["advances"]?.toJsonObject() ?: return null val advances = data["advances"]?.toJsonObject() ?: return null
val spaces = Int2ObjectOpenHashMap<EmptyCodeRenderer>(advances.size, 0.0f) val spaces = Int2ObjectOpenHashMap<EmptyCodeRenderer>(advances.size, 0.01f)
for ((char, spacing) in advances) { for ((char, spacing) in advances) {
val codePoint = char.codePointAt(0) val codePoint = char.codePointAt(0)

View File

@ -14,15 +14,15 @@
package de.bixilon.minosoft.gui.rendering.font.types.unicode.legacy package de.bixilon.minosoft.gui.rendering.font.types.unicode.legacy
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.code.RasterizedCodePointRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
class LegacyUnicodeCodeRenderer( class LegacyUnicodeCodeRenderer(
val texture: AbstractTexture, override val texture: AbstractTexture,
var uvStart: Vec2, override var uvStart: Vec2,
var uvEnd: Vec2, override var uvEnd: Vec2,
val width: Float, override val width: Float,
) : CodePointRenderer { ) : RasterizedCodePointRenderer {
fun updateArray() { fun updateArray() {

View File

@ -21,8 +21,8 @@ import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.renderer.FontProperties import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.types.FontType import de.bixilon.minosoft.gui.rendering.font.types.PostInitFontType
import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory
import de.bixilon.minosoft.gui.rendering.system.base.texture.StaticTextureArray import de.bixilon.minosoft.gui.rendering.system.base.texture.StaticTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
@ -32,7 +32,7 @@ import java.io.InputStream
class LegacyUnicodeFontType( class LegacyUnicodeFontType(
val chars: Array<LegacyUnicodeCodeRenderer?>, val chars: Array<LegacyUnicodeCodeRenderer?>,
) : FontType { ) : PostInitFontType {
override fun postInit(latch: AbstractLatch) { override fun postInit(latch: AbstractLatch) {
for (char in chars) { for (char in chars) {
@ -51,7 +51,7 @@ class LegacyUnicodeFontType(
private const val CHAR_ROW = 0x0F private const val CHAR_ROW = 0x0F
private const val CHAR_SIZE = 0x0F private const val CHAR_SIZE = 0x0F
private const val PIXEL = 1.0f / (CHAR_SIZE * CHAR_ROW) private const val PIXEL = 1.0f / (CHAR_SIZE * CHAR_ROW)
private const val WIDTH_SCALE = FontProperties.CHAR_HEIGHT / CHAR_SIZE.toFloat() private const val WIDTH_SCALE = FontProperties.CHAR_BASE_HEIGHT / CHAR_SIZE.toFloat()
override fun build(context: RenderContext, data: Map<String, Any>): LegacyUnicodeFontType? { override fun build(context: RenderContext, data: Map<String, Any>): LegacyUnicodeFontType? {
val assets = context.connection.assetsManager val assets = context.connection.assetsManager

View File

@ -67,7 +67,7 @@ abstract class Element(val guiRenderer: GUIRenderer, initialCacheSize: Int = 100
} }
@Deprecated("Warning: Should not be directly accessed!") @Deprecated("Warning: Should not be directly accessed!")
open val cache = GUIMeshCache(guiRenderer.halfSize, context.renderSystem.primitiveMeshOrder, initialCacheSize) open val cache = GUIMeshCache(guiRenderer.halfSize, context.renderSystem.primitiveMeshOrder, context, initialCacheSize)
private var previousMaxSize = Vec2i.EMPTY private var previousMaxSize = Vec2i.EMPTY

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.gui.elements package de.bixilon.minosoft.gui.rendering.gui.elements
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
enum class HorizontalAlignments { enum class HorizontalAlignments {
@ -22,6 +23,14 @@ enum class HorizontalAlignments {
; ;
companion object { companion object {
fun HorizontalAlignments.getOffset(width: Float, childWidth: Float): Float {
return when (this) {
LEFT -> 0.0f
RIGHT -> width - childWidth
CENTER -> (width - childWidth) / 2
}
}
fun HorizontalAlignments.getOffset(width: Int, childWidth: Int): Int { fun HorizontalAlignments.getOffset(width: Int, childWidth: Int): Int {
return when (this) { return when (this) {
LEFT -> 0 LEFT -> 0
@ -33,5 +42,9 @@ enum class HorizontalAlignments {
fun HorizontalAlignments.getOffset(size: Vec2i, childSize: Vec2i): Vec2i { fun HorizontalAlignments.getOffset(size: Vec2i, childSize: Vec2i): Vec2i {
return Vec2i(getOffset(size.x, childSize.x), 0) return Vec2i(getOffset(size.x, childSize.x), 0)
} }
fun HorizontalAlignments.getOffset(size: Vec2, childSize: Vec2): Vec2 {
return Vec2(getOffset(size.x, childSize.x), 0)
}
} }
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.gui.elements.text package de.bixilon.minosoft.gui.rendering.gui.elements.text
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.cast.CastUtil.unsafeNull import de.bixilon.kutil.cast.CastUtil.unsafeNull
import de.bixilon.kutil.primitive.BooleanUtil.decide import de.bixilon.kutil.primitive.BooleanUtil.decide
@ -36,6 +37,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.window.CursorShapes import de.bixilon.minosoft.gui.rendering.system.window.CursorShapes
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.offset import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.offset
import de.bixilon.minosoft.util.KUtil.charCount import de.bixilon.minosoft.util.KUtil.charCount
@ -118,12 +120,12 @@ open class TextElement(
if (!emptyMessage) { if (!emptyMessage) {
val renderInfo = TextRenderInfo( val renderInfo = TextRenderInfo(
fontAlignment = fontAlignment, fontAlignment = fontAlignment,
charHeight = charHeight, charHeight = charHeight.toFloat(),
charMargin = charMargin, charMargin = charMargin,
scale = scale, scale = scale,
shadow = shadow, shadow = shadow,
) )
ChatComponentRenderer.render(Vec2i.EMPTY, Vec2i.EMPTY, prefSize, InfiniteSizeElement(guiRenderer), context, null, null, renderInfo, value) ChatComponentRenderer.render(Vec2.EMPTY, Vec2.EMPTY, Vec2(prefSize), InfiniteSizeElement(guiRenderer), context, null, null, renderInfo, value)
} }
_prefSize = prefSize _prefSize = prefSize
} }
@ -151,13 +153,13 @@ open class TextElement(
val size = Vec2i.EMPTY val size = Vec2i.EMPTY
val renderInfo = TextRenderInfo( val renderInfo = TextRenderInfo(
fontAlignment = fontAlignment, fontAlignment = fontAlignment,
charHeight = charHeight, charHeight = charHeight.toFloat(),
charMargin = charMargin, charMargin = charMargin,
scale = scale, scale = scale,
shadow = shadow, shadow = shadow,
) )
if (!emptyMessage) { if (!emptyMessage) {
ChatComponentRenderer.render(Vec2i.EMPTY, Vec2i.EMPTY, size, this, context, null, null, renderInfo, chatComponent) ChatComponentRenderer.render(Vec2.EMPTY, Vec2.EMPTY, Vec2(size), this, context, null, null, renderInfo, chatComponent)
renderInfo.lineIndex = 0 renderInfo.lineIndex = 0
} }
if (renderInfo.lines.size > 1 && size.y > Font.CHAR_HEIGHT) { if (renderInfo.lines.size > 1 && size.y > Font.CHAR_HEIGHT) {
@ -180,7 +182,7 @@ open class TextElement(
if (background) { if (background) {
for ((line, info) in renderInfo.lines.withIndex()) { for ((line, info) in renderInfo.lines.withIndex()) {
val start = initialOffset + Vec2i(fontAlignment.getOffset(size.x, info.width), line * charHeight) val start = initialOffset + Vec2i(fontAlignment.getOffset(size.x.toFloat(), info.width), line * charHeight)
consumer.addQuad(start, start + Vec2i(info.width + charMargin, charHeight), context.textureManager.whiteTexture, backgroundColor, options) consumer.addQuad(start, start + Vec2i(info.width + charMargin, charHeight), context.textureManager.whiteTexture, backgroundColor, options)
} }
} }
@ -190,7 +192,7 @@ open class TextElement(
vertices *= 2 vertices *= 2
} }
consumer.ensureSize(vertices) consumer.ensureSize(vertices)
ChatComponentRenderer.render(initialOffset, Vec2i(initialOffset), Vec2i.EMPTY, this, context, consumer, options, renderInfo, chatComponent) ChatComponentRenderer.render(Vec2(initialOffset), Vec2(initialOffset), Vec2.EMPTY, this, context, consumer, options, renderInfo, chatComponent)
renderInfo.lineIndex = 0 renderInfo.lineIndex = 0
} }
@ -262,10 +264,10 @@ open class TextElement(
charToCheck++ charToCheck++
} }
val text = line.text.getTextAt(charToCheck) val text = line.text.getTextAt(charToCheck)
offset.x -= line0.width // ToDo: Not 100% correct offset.x -= line0.width.toInt() // ToDo: Not 100% correct
offset.x += fontAlignment.getOffset(size.x, line.width) offset.x += fontAlignment.getOffset(size.x, line.width.toInt())
return Pair(text, offset) return Pair(text, offset)
} }

View File

@ -83,7 +83,7 @@ class MarkTextElement(
val preMark = TextElement(guiRenderer, message.substring(0, markStartPosition), scale = scale, parent = _parent) val preMark = TextElement(guiRenderer, message.substring(0, markStartPosition), scale = scale, parent = _parent)
val mark = TextElement(guiRenderer, message.substring(markStartPosition, markEndPosition), scale = scale, parent = _parent) val mark = TextElement(guiRenderer, message.substring(markStartPosition, markEndPosition), scale = scale, parent = _parent)
val markOffset = Vec2i(preMark.renderInfo.lines.lastOrNull()?.width ?: 0, preMark.size.y) val markOffset = Vec2i(preMark.renderInfo.lines.lastOrNull()?.width ?: 0, preMark.size.y)
if (markOffset.y > 0 && (preMark.renderInfo.lines.lastOrNull()?.width ?: 0) <= (renderInfo.lines.lastOrNull()?.width ?: 0)) { if (markOffset.y > 0 && (preMark.renderInfo.lines.lastOrNull()?.width ?: 0.0f) <= (renderInfo.lines.lastOrNull()?.width ?: 0.0f)) {
markOffset.y -= (Font.TOTAL_CHAR_HEIGHT * scale).toInt() markOffset.y -= (Font.TOTAL_CHAR_HEIGHT * scale).toInt()
} }

View File

@ -344,8 +344,8 @@ open class TextInputElement(
continue continue
} }
val charDelta = position.x - line.width val charDelta = position.x - line.width
val width = guiRenderer.context.font[value.codePointAtOrNull(pointer) ?: break]?.width ?: break val width = guiRenderer.context.font.default[value.codePointAtOrNull(pointer) ?: break]?.calculateWidth(1.0f, true) ?: break
if (charDelta != 0 && charDelta >= width / 2) { if (charDelta != 0.0f && charDelta >= width / 2) {
pointer++ pointer++
} }
break break

View File

@ -18,12 +18,14 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.collections.primitive.floats.AbstractFloatList import de.bixilon.kutil.collections.primitive.floats.AbstractFloatList
import de.bixilon.kutil.collections.primitive.floats.HeapArrayFloatList import de.bixilon.kutil.collections.primitive.floats.HeapArrayFloatList
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
class GUIMeshCache( class GUIMeshCache(
var halfSize: Vec2, var halfSize: Vec2,
override val order: Array<Pair<Int, Int>>, override val order: Array<Pair<Int, Int>>,
override val context: RenderContext,
initialCacheSize: Int = 1000, initialCacheSize: Int = 1000,
var data: AbstractFloatList = HeapArrayFloatList(initialCacheSize), var data: AbstractFloatList = HeapArrayFloatList(initialCacheSize),
) : GUIVertexConsumer { ) : GUIVertexConsumer {

View File

@ -16,10 +16,12 @@ package de.bixilon.minosoft.gui.rendering.gui.mesh
import de.bixilon.kotlinglm.vec2.Vec2 import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLike import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLike
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
interface GUIVertexConsumer { interface GUIVertexConsumer {
val context: RenderContext
val order: Array<Pair<Int, Int>> val order: Array<Pair<Int, Int>>
fun addVertex(position: Vec2, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) fun addVertex(position: Vec2, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?)