mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
hud: draw all textElements in one mesh
This commit is contained in:
parent
38abd793f8
commit
284dd9835a
@ -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
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -111,4 +111,6 @@ object MMath {
|
||||
val multiplicationFactor = glm.pow(10, digits)
|
||||
return (this * multiplicationFactor).floor / multiplicationFactor
|
||||
}
|
||||
|
||||
val Long.nanosToMillis: Float get() = this / 1E6f
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user