font rendering improvements, rasterized font renderer test

This commit is contained in:
Bixilon 2023-06-09 19:24:51 +02:00
parent 63b8c73150
commit a870d64d6f
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
17 changed files with 269 additions and 88 deletions

View File

@ -222,7 +222,7 @@ testing {
options { options {
val options = this as TestNGOptions val options = this as TestNGOptions
options.preserveOrder = true options.preserveOrder = true
// options.excludeGroups("pixlyzer", "light", "packet", "version", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering") options.excludeGroups("command", "registry", "biome", "input", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering")
} }
} }
} }

View File

@ -0,0 +1,26 @@
/*
* 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.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture
class DummyCodePointRenderer(
override val uvStart: Vec2 = Vec2(0.1f, 0.2f),
override val uvEnd: Vec2 = Vec2(0.6f, 0.7f),
override val width: Float = 5.0f,
) : RasterizedCodePointRenderer {
override val texture = DummyTexture(minosoft("test"))
}

View File

@ -0,0 +1,121 @@
/*
* 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.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.gui.mesh.DummyGUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import org.testng.Assert.assertEquals
import org.testng.Assert.assertNull
import org.testng.annotations.Test
@Test(groups = ["font"], priority = -1)
class RasterizedCodePointRendererTest {
fun verifySimpleSetup() {
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
this.char++
assertEquals(tint, ChatColors.BLUE)
assertEquals(uvStart, Vec2(0.1, 0.2))
assertEquals(uvEnd, Vec2(0.6, 0.7))
assertEquals(texture.resourceLocation, minosoft("test"))
assertNull(options)
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, false, false, false, 1.0f, consumer, null)
assertEquals(1, consumer.char)
}
fun verifyComplexSetup() {
var chars = 0
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
chars++
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, true, true, false, 1.0f, consumer, null)
assertEquals(4, chars)
}
fun unformatted() {
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, index: Int) {
assertEquals(start, Vec2(10.0f, 13.0f)) // top spacing
assertEquals(end, Vec2(15.0f, 21.0f)) // start + width | start + height
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, false, false, false, 1.0f, consumer, null)
}
fun scaled() {
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
assertEquals(start, Vec2(10.0f, 13.5f)) // top spacing
assertEquals(end, Vec2(17.5f, 25.5f)) // start + width | start + height
// uv stays the same
assertEquals(uvStart, Vec2(0.1, 0.2))
assertEquals(uvEnd, Vec2(0.6, 0.7))
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, false, false, false, 1.5f, consumer, null)
}
fun shadow() {
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, index: Int) {
if (index == 1) return
assertEquals(start, Vec2(11.0f, 14.0f))
assertEquals(end, Vec2(16.0f, 22.0f))
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, true, false, false, 1.0f, consumer, null)
assertEquals(consumer.char, 2)
}
fun bold() {
val consumer = object : DummyGUIVertexConsumer() {
override fun addChar(start: Vec2, end: Vec2, index: Int) {
if (index == 0) return
assertEquals(start, Vec2(10.5f, 13.0f))
assertEquals(end, Vec2(15.5f, 21.0f))
}
}
val char = DummyCodePointRenderer()
char.render(Vec2(10.0f, 12.0f), ChatColors.BLUE, false, true, false, 1.0f, consumer, null)
assertEquals(consumer.char, 2)
}
// TODO: ascent
}

View File

@ -0,0 +1,50 @@
/*
* 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.gui.mesh
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.gui.atlas.CodeTexturePart
import de.bixilon.minosoft.gui.rendering.system.base.texture.ShaderIdentifiable
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
open class DummyGUIVertexConsumer : GUIVertexConsumer {
override val whiteTexture = CodeTexturePart(texture = DummyTexture(minosoft("white")), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
override val order: Array<Pair<Int, Int>> get() = Mesh.QUAD_TO_QUAD_ORDER
var char = 0
override fun addCache(cache: GUIMeshCache) {
TODO("Not yet implemented")
}
override fun ensureSize(size: Int) {
TODO("Not yet implemented")
}
override fun addVertex(position: Vec2, texture: ShaderIdentifiable, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) {
TODO("Not yet implemented")
}
override fun addChar(start: Vec2, end: Vec2, texture: AbstractTexture, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
addChar(start, end, this.char++)
}
open fun addChar(start: Vec2, end: Vec2, index: Int) {
TODO("Abstract")
}
}

View File

@ -16,7 +16,6 @@ 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
@ -27,7 +26,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 whiteTexture = mesh.context.textureManager.whiteTexture
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

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.font.renderer.code 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.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.font.WorldGUIConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
@ -25,10 +24,10 @@ interface CodePointRenderer {
fun calculateWidth(scale: Float, shadow: Boolean): Float fun calculateWidth(scale: Float, shadow: Boolean): Float
fun render(position: Vec2, color: RGBColor, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) fun render(position: Vec2, color: RGBColor, shadow: Boolean, bold: Boolean, italic: Boolean, scale: Float, consumer: GUIVertexConsumer, options: GUIVertexOptions?)
fun render3d(consumer: WorldGUIConsumer, color: RGBColor, shadow: Boolean, formatting: TextFormatting, scale: Float): Float { fun render3d(color: RGBColor, shadow: Boolean, bold: Boolean, italic: Boolean, scale: Float, consumer: WorldGUIConsumer): Float {
render(Vec2.EMPTY, color, shadow, formatting, consumer, null, scale) render(Vec2.EMPTY, color, shadow, bold, italic, scale, consumer, null)
return calculateWidth(scale, shadow) return calculateWidth(scale, shadow)
} }

View File

@ -14,15 +14,11 @@
package de.bixilon.minosoft.gui.rendering.font.renderer.code 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.FormattingCodes
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.properties.FontProperties 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.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_COLOR
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties.SHADOW_OFFSET 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.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.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
@ -45,11 +41,11 @@ interface RasterizedCodePointRenderer : CodePointRenderer {
return width * scale return width * scale
} }
override fun render(position: Vec2, color: RGBColor, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) { override fun render(position: Vec2, color: RGBColor, shadow: Boolean, bold: Boolean, italic: Boolean, scale: Float, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
if (shadow) { if (shadow) {
render(position + (SHADOW_OFFSET * scale), color * SHADOW_COLOR, formatting, consumer, options, scale) render(position + (SHADOW_OFFSET * scale), color * SHADOW_COLOR, bold, italic, scale, consumer, options)
} }
render(position, color, formatting, consumer, options, scale) render(position, color, bold, italic, scale, consumer, options)
} }
fun calculateStart(base: Vec2, scale: Float): Vec2 { fun calculateStart(base: Vec2, scale: Float): Vec2 {
@ -67,61 +63,19 @@ interface RasterizedCodePointRenderer : CodePointRenderer {
return position return position
} }
private fun render(position: Vec2, color: RGBColor, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) { private fun render(position: Vec2, color: RGBColor, bold: Boolean, italic: Boolean, scale: Float, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
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 startPosition = calculateStart(position, scale)
val endPosition = calculateEnd(startPosition, scale) val endPosition = calculateEnd(startPosition, scale)
consumer.addChar(startPosition, endPosition, texture, uvStart, uvEnd, italic, color, options)
if (bold) {
consumer.addQuad(startPosition, endPosition, texture, uvStart, uvEnd, italic, color, options) // render char another time but offset in x direction
val boldOffset = BOLD_OFFSET * scale
if (FormattingCodes.BOLD in formatting) { consumer.addChar(
consumer.addQuad(startPosition + Vec2(boldOffset, 0.0f), endPosition + Vec2(boldOffset, 0.0f), texture, uvStart, uvEnd, italic, color, options) start = startPosition + Vec2(boldOffset, 0.0f),
} end = endPosition + Vec2(boldOffset, 0.0f),
val whiteTexture = consumer.context.textureManager.whiteTexture texture, uvStart, uvEnd, italic, color, options)
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

@ -40,8 +40,10 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
val color = text.color ?: ChatColors.WHITE val color = text.color ?: ChatColors.WHITE
val shadow = renderInfo.shadow val shadow = renderInfo.shadow
val bold: Boolean = text.formatting.contains(FormattingCodes.BOLD) val bold: Boolean = text.formatting.contains(FormattingCodes.BOLD)
val italic: Boolean = text.formatting.contains(FormattingCodes.ITALIC)
// ToDo: Only 1 quad for the underline and the strikethrough // ToDo: Only 1 quad for the underline and the strikethrough
// TODO: strike, underlined
var alignmentXOffset = 0.0f var alignmentXOffset = 0.0f
var currentLineText = "" var currentLineText = ""
@ -177,7 +179,7 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
// ToDo: Remove Font.HORIZONTAL_SPACING // ToDo: Remove Font.HORIZONTAL_SPACING
} }
consumer?.let { charData.render(letterOffset, color, shadow, text.formatting, it, options, renderInfo.scale) } consumer?.let { charData.render(letterOffset, color, shadow, bold, italic, renderInfo.scale, it, options) }
if (consumer == null) { if (consumer == null) {
currentLineText += char.toChar() currentLineText += char.toChar()
@ -209,13 +211,16 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
val font = context.font[text.font] val font = context.font[text.font]
// TODO: strike, underlined
for (char in text.message.codePoints()) { for (char in text.message.codePoints()) {
val data = font?.get(char) ?: context.font.default[char] ?: continue val data = font?.get(char) ?: context.font.default[char] ?: continue
val expectedWidth = ((data.calculateWidth(scale, false) + 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, text.formatting, scale = scale) + Font.HORIZONTAL_SPACING) * scale).toInt() val width = ((data.render3d(color, shadow = false, FormattingCodes.BOLD in text.formatting, FormattingCodes.ITALIC in text.formatting, scale = scale, consumer) + 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

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.font.types.empty 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.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
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.font.renderer.properties.FontProperties.MAX_CHAR_WIDTH
@ -31,5 +30,5 @@ data 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, shadow: Boolean, formatting: TextFormatting, consumer: GUIVertexConsumer, options: GUIVertexOptions?, scale: Float) = Unit override fun render(position: Vec2, color: RGBColor, shadow: Boolean, bold: Boolean, italic: Boolean, scale: Float, consumer: GUIVertexConsumer, options: GUIVertexOptions?) = Unit
} }

View File

@ -25,7 +25,7 @@ class AtlasElement(
val end: Vec2i, val end: Vec2i,
val slots: Int2ObjectOpenHashMap<AtlasSlot>, val slots: Int2ObjectOpenHashMap<AtlasSlot>,
val areas: Map<String, AtlasArea>, val areas: Map<String, AtlasArea>,
) : TextureLike { ) : TexturePart {
override val size: Vec2i = end - start override val size: Vec2i = end - start
override lateinit var uvStart: Vec2 override lateinit var uvStart: Vec2
override lateinit var uvEnd: Vec2 override lateinit var uvEnd: Vec2

View File

@ -17,9 +17,9 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
class TextureLikeTexture( class CodeTexturePart(
override val texture: AbstractTexture, override val texture: AbstractTexture,
override val uvStart: Vec2, override val uvStart: Vec2,
override val uvEnd: Vec2, override val uvEnd: Vec2,
override val size: Vec2i, override val size: Vec2i,
) : TextureLike ) : TexturePart

View File

@ -17,7 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
interface TextureLike { interface TexturePart {
val texture: AbstractTexture val texture: AbstractTexture
val uvStart: Vec2 val uvStart: Vec2
val uvEnd: Vec2 val uvEnd: Vec2

View File

@ -18,7 +18,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.formatting.color.ChatColors import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLike import de.bixilon.minosoft.gui.rendering.gui.atlas.TexturePart
import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh 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
@ -28,11 +28,11 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
open class AtlasImageElement( open class AtlasImageElement(
guiRenderer: GUIRenderer, guiRenderer: GUIRenderer,
textureLike: TextureLike?, texturePart: TexturePart?,
size: Vec2i = textureLike?.size ?: Vec2i.EMPTY, size: Vec2i = texturePart?.size ?: Vec2i.EMPTY,
tint: RGBColor = ChatColors.WHITE, tint: RGBColor = ChatColors.WHITE,
) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6) { ) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6) {
var texture: AbstractTexture? = textureLike?.texture var texture: AbstractTexture? = texturePart?.texture
set(value) { set(value) {
field = value field = value
cacheUpToDate = false cacheUpToDate = false
@ -67,7 +67,7 @@ open class AtlasImageElement(
cacheUpToDate = false cacheUpToDate = false
} }
var textureLike: TextureLike? = textureLike var texturePart: TexturePart? = texturePart
set(value) { set(value) {
if (field === value) { if (field === value) {
return return
@ -87,7 +87,7 @@ open class AtlasImageElement(
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
val texture = texture ?: return val texture = texture ?: return
val textureLike = textureLike ?: return val textureLike = texturePart ?: return
consumer.addQuad(offset, offset + size, texture, uvStart ?: textureLike.uvStart, uvEnd ?: textureLike.uvEnd, tint, options) consumer.addQuad(offset, offset + size, texture, uvStart ?: textureLike.uvStart, uvEnd ?: textureLike.uvEnd, tint, options)
} }

View File

@ -27,6 +27,7 @@ class GUIMesh(
val halfSize: Vec2, val halfSize: Vec2,
data: AbstractFloatList, data: AbstractFloatList,
) : Mesh(context, GUIMeshStruct, initialCacheSize = 40000, clearOnLoad = false, data = data), GUIVertexConsumer { ) : Mesh(context, GUIMeshStruct, initialCacheSize = 40000, clearOnLoad = false, data = data), GUIVertexConsumer {
override val whiteTexture = context.textureManager.whiteTexture
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?) {
addVertex(data, halfSize, position, texture, uv, tint, options) addVertex(data, halfSize, position, texture, uv, tint, options)

View File

@ -25,10 +25,12 @@ 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, val context: RenderContext,
initialCacheSize: Int = 1000, initialCacheSize: Int = 1000,
var data: AbstractFloatList = HeapArrayFloatList(initialCacheSize), var data: AbstractFloatList = HeapArrayFloatList(initialCacheSize),
) : GUIVertexConsumer { ) : GUIVertexConsumer {
override val whiteTexture = context.textureManager.whiteTexture
var revision: Long = 0 var revision: Long = 0
var offset: Vec2i = Vec2i.EMPTY var offset: Vec2i = Vec2i.EMPTY
var options: GUIVertexOptions? = null var options: GUIVertexOptions? = null

View File

@ -16,12 +16,15 @@ 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.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLike import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FormattingProperties
import de.bixilon.minosoft.gui.rendering.gui.atlas.CodeTexturePart
import de.bixilon.minosoft.gui.rendering.gui.atlas.TexturePart
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.system.base.texture.texture.AbstractTexture
interface GUIVertexConsumer { interface GUIVertexConsumer {
val context: RenderContext val whiteTexture: CodeTexturePart
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?)
@ -67,14 +70,36 @@ interface GUIVertexConsumer {
} }
} }
fun addQuad(start: Vec2, end: Vec2, texture: TextureLike, tint: RGBColor, options: GUIVertexOptions?) { fun addQuad(start: Vec2, end: Vec2, texture: TexturePart, tint: RGBColor, options: GUIVertexOptions?) {
addQuad(start, end, texture.texture, texture.uvStart, texture.uvEnd, tint, options) addQuad(start, end, texture.texture, texture.uvStart, texture.uvEnd, tint, options)
} }
fun addQuad(start: Vec2i, end: Vec2i, texture: TextureLike, tint: RGBColor, options: GUIVertexOptions?) { fun addQuad(start: Vec2i, end: Vec2i, texture: TexturePart, tint: RGBColor, options: GUIVertexOptions?) {
addQuad(start, end, texture.texture, texture.uvStart, texture.uvEnd, tint, options) addQuad(start, end, texture.texture, texture.uvStart, texture.uvEnd, tint, options)
} }
fun addChar(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 * FormattingProperties.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)
}
}
fun addCache(cache: GUIMeshCache) fun addCache(cache: GUIMeshCache)
fun ensureSize(size: Int) fun ensureSize(size: Int)

View File

@ -17,7 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLikeTexture import de.bixilon.minosoft.gui.rendering.gui.atlas.CodeTexturePart
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
@ -32,7 +32,7 @@ abstract class TextureManager {
lateinit var debugTexture: AbstractTexture lateinit var debugTexture: AbstractTexture
private set private set
lateinit var whiteTexture: TextureLikeTexture lateinit var whiteTexture: CodeTexturePart
private set private set
lateinit var skins: SkinManager lateinit var skins: SkinManager
private set private set
@ -42,7 +42,7 @@ abstract class TextureManager {
throw IllegalStateException("Already initialized!") throw IllegalStateException("Already initialized!")
} }
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION) debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
whiteTexture = TextureLikeTexture(texture = staticTextures.createTexture(minosoft("white").texture()), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16)) whiteTexture = CodeTexturePart(texture = staticTextures.createTexture(minosoft("white").texture()), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
} }
fun initializeSkins(connection: PlayConnection) { fun initializeSkins(connection: PlayConnection) {