hud: draw all textElements in one mesh

This commit is contained in:
Lukas 2021-07-07 16:59:04 +02:00
parent 38abd793f8
commit 284dd9835a
7 changed files with 107 additions and 62 deletions

View File

@ -2,6 +2,7 @@ package de.bixilon.minosoft.gui.rendering.hud.elements.position
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import glm_.vec2.Vec2
import glm_.vec2.Vec2i
@ -25,9 +26,11 @@ class HUDElementVec2(
}
companion object {
private val ZERO_VECTOR = HUDElementVec2(Vec2.EMPTY, HUDElementPositionUnits.PIXELS)
fun deserialize(data: Any?): HUDElementVec2? {
if (data !is Map<*, *>) {
return null
return ZERO_VECTOR
}
val xString = data["x"] as String
val yString = data["y"] as String

View File

@ -18,9 +18,10 @@ import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.position.HUDElementPositionAnchors
import de.bixilon.minosoft.gui.rendering.hud.elements.position.HUDElementVec2
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec2
import de.bixilon.minosoft.gui.rendering.util.mesh.SimpleTextureMesh
import glm_.vec2.Vec2i
abstract class HUDElement(
open class HUDElement(
var position: HUDElementPosition,
val size: HUDElementVec2,
val z: Int,
@ -47,7 +48,7 @@ abstract class HUDElement(
prepareNext = false
}
fun getPositionAtAnchor(anchor: HUDElementPositionAnchors): Vec2i {
open fun getPositionAtAnchor(anchor: HUDElementPositionAnchors): Vec2i {
val realSize = size.getRealVector(hudRenderer).toVec2
val realPosition = position.getRealPosition(hudRenderer)
return realPosition - realSize * position.anchor.positionTransform + realSize * anchor.positionTransform
@ -76,4 +77,6 @@ abstract class HUDElement(
}
open fun update() {}
open fun prepare(mesh: SimpleTextureMesh) {}
}

View File

@ -17,7 +17,7 @@ import glm_.vec4.Vec4
import glm_.vec4.swizzle.xy
class HUDImageElement: HUDElement {
private var mesh = SimpleTextureMesh()
private var textureMesh = SimpleTextureMesh()
lateinit var texture: TextureLike
private val textureName: ResourceLocation?
@ -48,15 +48,10 @@ class HUDImageElement: HUDElement {
}
override fun draw() {
mesh.draw()
textureMesh.draw()
}
override fun prepare() {
super.prepare()
if (mesh.state == Mesh.MeshStates.LOADED) {
mesh.unload()
mesh = SimpleTextureMesh()
}
override fun prepare(mesh: SimpleTextureMesh) {
val realZ = RenderConstants.HUD_Z_COORDINATE + RenderConstants.HUD_Z_COORDINATE_Z_FACTOR * z
val positions = position.getPositions(hudRenderer, size.getRealVector(hudRenderer))
val uvs = texture.uvs
@ -70,7 +65,15 @@ class HUDImageElement: HUDElement {
for (position in DRAW_ORDER) {
addVertex((hudRenderer.orthographicMatrix * Vec4(positions[position], 1f, 1f)).xy, uvs[position])
}
mesh.load()
}
override fun prepare() {
if (textureMesh.state == Mesh.MeshStates.LOADED) {
textureMesh.unload()
textureMesh = SimpleTextureMesh()
}
prepare(textureMesh)
textureMesh.load()
}
companion object {

View File

@ -1,20 +0,0 @@
/*
* Minosoft
*
* Copyright (C) 2021 Lukas Eisenhauer
*
* 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.hud.elements.primitive
import de.bixilon.minosoft.gui.rendering.hud.elements.position.HUDElementVec2
class HUDSpacerElement(position: HUDElementPosition, size: HUDElementVec2, z: Int,):
HUDElement(position, size, z)

View File

@ -27,18 +27,20 @@ import de.bixilon.minosoft.gui.rendering.hud.elements.position.HUDElementVec2
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.HUDElementPosition
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.HUDImageElement
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.HUDSpacerElement
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec2
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.mesh.SimpleTextureMesh
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.glm
import glm_.vec2.Vec2
import glm_.vec2.Vec2i
class HUDTextElement : HUDElement {
val elements = mutableListOf<HUDElement>()
private var textureMesh = SimpleTextureMesh()
private val alignment: HUDElementPositionAnchors
private lateinit var contents: List<*>
private lateinit var contents: Any
private var currentText: ChatComponent? = null
@ -46,7 +48,7 @@ class HUDTextElement : HUDElement {
constructor(position: HUDElementPosition, size: HUDElementVec2, json: Map<String, Any>? = null, activeOnMenu: HUDMenus?) : super(position, size, (json?.get("z") as Double?)?.toInt() ?: 0, activeOnMenu) {
alignment = json?.get("alignment")?.let { HUDElementPositionAnchors.of(it as String) } ?: HUDElementPositionAnchors.TOP_LEFT
contents = json?.get("content") as List<*>
contents = json?.get("content")!!
}
constructor(position: HUDElementPosition, size: HUDElementVec2, z: Int) : super(position, size, z) {
@ -76,8 +78,8 @@ class HUDTextElement : HUDElement {
}
private fun addChars(chars: CharArray, color: RGBColor?, newLinePositions: MutableList<Int>): MutableList<Int> {
val maxRight = getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).x
val bottomLeftPosition = getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_LEFT).toVec2
val maxRight = super.getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).x
val bottomLeftPosition = super.getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_LEFT).toVec2
for (char in chars) {
if (char in ProtocolDefinition.LINE_BREAK_CHARS) {
lastElement = null
@ -88,11 +90,11 @@ class HUDTextElement : HUDElement {
HUDElementPosition(BETWEEN_CHARS_OFFSET, HUDElementPositionAnchors.BOTTOM_LEFT, it, HUDElementPositionAnchors.BOTTOM_RIGHT)
} ?: run {
newLinePositions += elements.size
HUDElementPosition(HUDElementVec2(getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_LEFT).toVec2, HUDElementPositionUnits.PIXELS), HUDElementPositionAnchors.BOTTOM_LEFT)
HUDElementPosition(HUDElementVec2(super.getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_LEFT).toVec2, HUDElementPositionUnits.PIXELS), HUDElementPositionAnchors.BOTTOM_LEFT)
}
val elementSize = HUDElementVec2(fontChar.size.toVec2, HUDElementPositionUnits.SCALED_PIXELS)
val newElement = if (char == ' ') {
HUDSpacerElement(elementPosition, elementSize, z)
HUDElement(elementPosition, elementSize, z)
} else {
HUDImageElement(elementPosition, elementSize, z, fontChar, tint = color)
}
@ -102,7 +104,7 @@ class HUDTextElement : HUDElement {
lastElement = newElement
if (elementRight > maxRight) {
val relevantIndex = getLastSpacerElementPosition(elements.lastIndex)
if (elements[relevantIndex] is HUDSpacerElement) {
if (elements[relevantIndex] !is HUDImageElement) {
elements.removeAt(relevantIndex)
}
newLinePositions += relevantIndex
@ -114,11 +116,11 @@ class HUDTextElement : HUDElement {
private fun getLastSpacerElementPosition(position: Int): Int {
for (i in 0..position) {
val j = position - i - 1
val j = glm.max(0, position - i - 1)
if (j == 0) {
return position
}
if (elements[j] is HUDSpacerElement) {
if (elements[j] !is HUDImageElement) {
return j
}
if (elements[j].position.parentElement == null) {
@ -130,35 +132,43 @@ class HUDTextElement : HUDElement {
private fun adjustPositionsForLineBreaks(newLinePositions: MutableList<Int>) {
var y = when (alignment) {
HUDElementPositionAnchors.TOP_LEFT, HUDElementPositionAnchors.BOTTOM_LEFT -> getPositionAtAnchor(HUDElementPositionAnchors.TOP_LEFT).y
HUDElementPositionAnchors.TOP_RIGHT, HUDElementPositionAnchors.BOTTOM_RIGHT -> getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).y - (newLinePositions.size - 2) * (Font.CHAR_HEIGHT + 1) * Minosoft.getConfig().config.game.hud.scale.toInt()
HUDElementPositionAnchors.TOP_LEFT, HUDElementPositionAnchors.BOTTOM_LEFT -> super.getPositionAtAnchor(HUDElementPositionAnchors.TOP_LEFT).y
HUDElementPositionAnchors.TOP_RIGHT, HUDElementPositionAnchors.BOTTOM_RIGHT -> super.getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).y - (newLinePositions.size - 2) * (Font.CHAR_HEIGHT + 1) * Minosoft.getConfig().config.game.hud.scale.toInt()
else -> TODO()
}
val left = getPositionAtAnchor(HUDElementPositionAnchors.TOP_LEFT).x
val right = glm.min(hudRenderer.renderWindow.screenDimensions.x, getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).x)
val left = super.getPositionAtAnchor(HUDElementPositionAnchors.TOP_LEFT).x
val right = glm.min(hudRenderer.renderWindow.screenDimensions.x, super.getPositionAtAnchor(HUDElementPositionAnchors.BOTTOM_RIGHT).x)
for (i in 0 until newLinePositions.lastIndex) {
elements[newLinePositions[i]].position = alignment.textAlignmentTransform.invoke(left, right, y, elements[newLinePositions[i + 1] - 1])
elements[newLinePositions[i]].position = alignment.textAlignmentTransform.invoke(left, right, y, elements[glm.max(0, newLinePositions[i + 1] - 1)])
y += ((Font.CHAR_HEIGHT + 1) * Minosoft.getConfig().config.game.hud.scale).toInt()
}
}
override fun draw() {
for (element in elements) {
element.draw()
}
textureMesh.draw()
}
override fun prepare() {
if (textureMesh.state == Mesh.MeshStates.LOADED) {
textureMesh.unload()
textureMesh = SimpleTextureMesh()
}
prepare(textureMesh)
textureMesh.load()
}
override fun prepare(mesh: SimpleTextureMesh) {
if (currentText == null) {
return
currentText = getChatComponent()
}
renderChatComponent(currentText!!)
for (element in elements) {
element.prepare()
element.prepare(mesh)
}
super.prepare()
}
fun getTextComponent(): ChatComponent {
fun getChatComponent(): ChatComponent {
return ChatComponent.of(contents, translator = hudRenderer.connection.version.localeManager.language, hudTextTranslator = hudRenderer.hudTextTranslator)
}
@ -166,13 +176,23 @@ class HUDTextElement : HUDElement {
if (!isEnabled) {
return
}
val newText = getTextComponent()
val newText = getChatComponent()
if (currentText != newText) {
currentText = newText
prepareNext = true
}
}
override fun getPositionAtAnchor(anchor: HUDElementPositionAnchors): Vec2i {
return when (anchor) {
HUDElementPositionAnchors.CENTER -> TODO()
HUDElementPositionAnchors.TOP_LEFT -> super.getPositionAtAnchor(anchor)
HUDElementPositionAnchors.TOP_RIGHT -> Vec2i(elements[elements.lastIndex].getPositionAtAnchor(anchor).y, super.getPositionAtAnchor(anchor).x)
HUDElementPositionAnchors.BOTTOM_LEFT -> Vec2i(super.getPositionAtAnchor(anchor).x, elements[elements.lastIndex].getPositionAtAnchor(anchor).y)
HUDElementPositionAnchors.BOTTOM_RIGHT -> elements[elements.lastIndex].getPositionAtAnchor(anchor)
}
}
companion object {
val BETWEEN_CHARS_OFFSET = HUDElementVec2(Vec2(1, 0), HUDElementPositionUnits.SCALED_PIXELS)
}

View File

@ -18,40 +18,74 @@ import de.bixilon.minosoft.data.locale.minecraft.Translator
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.gui.rendering.chunk.WorldRenderer
import de.bixilon.minosoft.gui.rendering.util.VecUtil.round
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.MMath.nanosToMillis
import de.bixilon.minosoft.util.MMath.round
class HUDTextTranslator(val connection: PlayConnection) : Translator {
class HUDTextTranslator(private val connection: PlayConnection) : Translator {
private lateinit var worldRenderer: WorldRenderer
override fun translate(key: String?, parent: TextComponent?, vararg data: Any?): ChatComponent {
if (key == null) {
return ChatComponent.of(key)
}
if (!this::worldRenderer.isInitialized) {
worldRenderer = connection.rendering!!.renderWindow.rendererMap[WorldRenderer.RESOURCE_LOCATION]!! as WorldRenderer
}
val resourceLocation = ResourceLocation(key)
if (resourceLocation !in insertRunnables) {
return ChatComponent.of(key)
}
val runnable = insertRunnables[resourceLocation]!! // checked before
return ChatComponent.of(runnable.invoke(connection))
return ChatComponent.of(runnable.invoke(connection, worldRenderer))
}
companion object {
private val insertRunnables = mutableMapOf<ResourceLocation, (PlayConnection) -> String>()
private val insertRunnables = mutableMapOf<ResourceLocation, (PlayConnection, WorldRenderer) -> String>()
init {
insertRunnables[ResourceLocation("minosoft:playerPosition_x")] = { connection ->
//position
insertRunnables[ResourceLocation("minosoft:playerPosition_x")] = { connection, _ ->
connection.player.position.x.round(ROUND_DIGITS).toString()
}
insertRunnables[ResourceLocation("minosoft:playerPosition_y")] = { connection ->
insertRunnables[ResourceLocation("minosoft:playerPosition_y")] = { connection, _ ->
connection.player.position.y.round(ROUND_DIGITS).toString()
}
insertRunnables[ResourceLocation("minosoft:playerPosition_z")] = { connection ->
insertRunnables[ResourceLocation("minosoft:playerPosition_z")] = { connection, _ ->
connection.player.position.z.round(ROUND_DIGITS).toString()
}
insertRunnables[ResourceLocation("minosoft:playerPosition_x")] = { connection ->
insertRunnables[ResourceLocation("minosoft:playerPosition_x")] = { connection, _ ->
connection.player.position.round(ROUND_DIGITS).toString()
}
// timings
insertRunnables[ResourceLocation("minosoft:fps")] = { connection, _ ->
connection.rendering!!.renderWindow.renderStats.fpsLastSecond.toString()
}
insertRunnables[ResourceLocation("minosoft:average_frame_time")] = { connection, _ ->
connection.rendering!!.renderWindow.renderStats.avgFrameTime.nanosToMillis.round(1).toString()
}
insertRunnables[ResourceLocation("minosoft:min_frame_time")] = { connection, _ ->
connection.rendering!!.renderWindow.renderStats.minFrameTime.nanosToMillis.round(1).toString()
}
insertRunnables[ResourceLocation("minosoft:max_frame_time")] = { connection, _ ->
connection.rendering!!.renderWindow.renderStats.maxFrameTime.nanosToMillis.round(1).toString()
}
// chunk data
insertRunnables[ResourceLocation("minosoft:queued_chunks_count")] = { _, worldRenderer ->
worldRenderer.queuedChunks.size.toString()
}
insertRunnables[ResourceLocation("minosoft:visible_chunks_count")] = { _, worldRenderer ->
worldRenderer.visibleChunks.size.toString()
}
insertRunnables[ResourceLocation("minosoft:all_chunk_secions_count")] = { _, worldRenderer ->
worldRenderer.allChunkSections.size.toString()
}
insertRunnables[ResourceLocation("minosoft:triangle_count")] = { _, worldRenderer ->
worldRenderer.triangles.toString()
}
}
const val ROUND_DIGITS = 2

View File

@ -111,4 +111,6 @@ object MMath {
val multiplicationFactor = glm.pow(10, digits)
return (this * multiplicationFactor).floor / multiplicationFactor
}
val Long.nanosToMillis: Float get() = this / 1E6f
}