integrate font in ChatComponent api

This commit is contained in:
Bixilon 2021-02-10 21:15:10 +01:00
parent 96260aab44
commit c39ad18715
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 270 additions and 373 deletions

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.locale.minecraft;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@ -52,6 +53,13 @@ public class MinecraftLanguage {
}
public String translate(String key, Object... data) {
if (data.length == 1 && data[0] instanceof JsonArray jsonArray) {
data = new Object[jsonArray.size()];
int i = 0;
for (var element : jsonArray) {
data[i++] = element.getAsString();
}
}
String placeholder = this.data.get(key);
if (placeholder == null) {
return null;

View File

@ -18,9 +18,12 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import de.bixilon.minosoft.data.mappings.versions.Version;
import de.bixilon.minosoft.gui.rendering.font.Font;
import de.bixilon.minosoft.gui.rendering.hud.HUDScale;
import de.bixilon.minosoft.modding.event.events.annotations.Unsafe;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.hash.BetterHashSet;
import glm_.vec2.Vec2;
import javafx.collections.ObservableList;
import javafx.scene.Node;
@ -28,6 +31,7 @@ import javax.annotation.Nullable;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.List;
public class BaseComponent extends ChatComponent {
private static final String LEGACY_RESET_SUFFIX = String.valueOf(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR) + PostChatFormattingCodes.RESET.getChar();
@ -43,7 +47,7 @@ public class BaseComponent extends ChatComponent {
public BaseComponent(Version version, @Nullable ChatComponent parent, String text) {
// legacy String
StringBuilder currentText = new StringBuilder();
RGBColor color = null;
RGBColor color = ChatColors.WHITE;
BetterHashSet<ChatFormattingCode> formattingCodes = new BetterHashSet<>();
StringCharacterIterator iterator = new StringCharacterIterator(text);
while (iterator.current() != CharacterIterator.DONE) {
@ -165,7 +169,7 @@ public class BaseComponent extends ChatComponent {
}
if (json.has("translate")) {
this.parts.add(new TranslatableComponent(version, parentParameter, json.get("translate").getAsString(), json.getAsJsonArray("with")));
this.parts.add(new BaseComponent(version, version.getLocaleManager().translate(json.get("translate").getAsString(), json.getAsJsonArray("with"))));
}
} else if (data instanceof JsonPrimitive primitive) {
this.parts.add(new BaseComponent(version, parent, primitive.getAsString()));
@ -208,6 +212,13 @@ public class BaseComponent extends ChatComponent {
return nodes;
}
@Override
public void addVerticies(Vec2 startPosition, Vec2 offset, Font font, HUDScale hudScale, List<Float> meshData) {
for (var chatPart : this.parts) {
chatPart.addVerticies(startPosition, offset, font, hudScale, meshData);
}
}
@Unsafe
public ArrayList<ChatComponent> getParts() {
return this.parts;

View File

@ -18,11 +18,15 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import de.bixilon.minosoft.data.mappings.versions.Version;
import de.bixilon.minosoft.gui.rendering.font.Font;
import de.bixilon.minosoft.gui.rendering.hud.HUDScale;
import glm_.vec2.Vec2;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javax.annotation.Nullable;
import java.util.List;
public abstract class ChatComponent {
public static ChatComponent valueOf(Object raw) {
@ -83,4 +87,9 @@ public abstract class ChatComponent {
public ObservableList<Node> getJavaFXText() {
return getJavaFXText(FXCollections.observableArrayList());
}
/**
* @return Adds all verticies to the array (used in opengl)
*/
public abstract void addVerticies(Vec2 startPosition, Vec2 offset, Font font, HUDScale hudScale, List<Float> meshData);
}

View File

@ -1,203 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 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.data.text;
import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.config.ConfigurationPaths;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.hash.BetterHashSet;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
import java.util.Objects;
public class TextComponent extends ChatComponent {
private final String text;
private RGBColor color;
private BetterHashSet<ChatFormattingCode> formatting;
public TextComponent(String text, RGBColor color, BetterHashSet<ChatFormattingCode> formatting) {
this.text = text;
this.color = color;
this.formatting = formatting;
}
public TextComponent(String text, RGBColor color) {
this.text = text;
this.color = color;
}
public TextComponent(String text) {
this.text = text;
}
public TextComponent setObfuscated(boolean obfuscated) {
this.formatting.addOrRemove(PreChatFormattingCodes.OBFUSCATED, obfuscated);
return this;
}
public TextComponent setBold(boolean bold) {
this.formatting.addOrRemove(PreChatFormattingCodes.BOLD, bold);
return this;
}
public TextComponent setStrikethrough(boolean strikethrough) {
this.formatting.addOrRemove(PreChatFormattingCodes.STRIKETHROUGH, strikethrough);
return this;
}
public TextComponent setUnderlined(boolean underlined) {
this.formatting.addOrRemove(PreChatFormattingCodes.UNDERLINED, underlined);
return this;
}
public TextComponent setItalic(boolean italic) {
this.formatting.addOrRemove(PreChatFormattingCodes.ITALIC, italic);
return this;
}
public TextComponent setReset(boolean reset) {
this.formatting.addOrRemove(PostChatFormattingCodes.RESET, reset);
return this;
}
public RGBColor getColor() {
return this.color;
}
public TextComponent setColor(RGBColor color) {
this.color = color;
return this;
}
public BetterHashSet<ChatFormattingCode> getFormatting() {
return this.formatting;
}
public TextComponent setFormatting(BetterHashSet<ChatFormattingCode> formatting) {
this.formatting = formatting;
return this;
}
@Override
public int hashCode() {
return Objects.hash(this.text, this.color, this.formatting);
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
if (hashCode() != obj.hashCode()) {
return false;
}
TextComponent their = (TextComponent) obj;
return this.text.equals(their.getMessage()) && this.color.equals(their.getColor()) && this.formatting.equals(their.getFormatting());
}
@Override
public String toString() {
return getANSIColoredMessage();
}
@Override
public String getANSIColoredMessage() {
StringBuilder builder = new StringBuilder();
if (this.color != null) {
builder.append(ChatColors.getANSIColorByRGBColor(this.color));
}
if (this.formatting != null) {
this.formatting.forEach((chatFormattingCodes -> {
if (chatFormattingCodes instanceof PreChatFormattingCodes code) {
builder.append(code.getANSI());
}
}));
}
builder.append(this.text);
if (this.formatting != null) {
this.formatting.forEach((chatFormattingCodes -> {
if (chatFormattingCodes instanceof PostChatFormattingCodes code) {
builder.append(code.getANSI());
}
}));
}
builder.append(PostChatFormattingCodes.RESET);
return builder.toString();
}
@Override
public String getLegacyText() {
StringBuilder output = new StringBuilder();
Integer colorChar = ChatColors.getColorId(this.color);
if (colorChar != null) {
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(Integer.toHexString(colorChar));
}
this.formatting.forEach((chatFormattingCode -> output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(chatFormattingCode.getChar())));
output.append(this.text);
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(PostChatFormattingCodes.RESET.getChar());
return output.toString();
}
@Override
public String getMessage() {
return this.text;
}
@Override
public ObservableList<Node> getJavaFXText(ObservableList<Node> nodes) {
Text text = new Text(this.text);
text.setFill(Color.WHITE);
if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_COLORED) && this.color != null) {
text.setFill(Color.web(this.color.toString()));
}
this.formatting.forEach((chatFormattingCode -> {
if (chatFormattingCode instanceof PreChatFormattingCodes code) {
switch (code) {
case OBFUSCATED -> {
// ToDo: potential memory leak: Stop timeline, when TextComponent isn't shown anymore
Timeline obfuscatedTimeline;
if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_OBFUSCATED)) {
obfuscatedTimeline = new Timeline(new KeyFrame(Duration.millis(50), e -> {
char[] chars = text.getText().toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] = Util.getRandomChar(ProtocolDefinition.OBFUSCATED_CHARS);
}
text.setText(new String(chars));
}));
} else {
obfuscatedTimeline = new Timeline(new KeyFrame(Duration.millis(500), e -> text.setVisible(false)), new KeyFrame(Duration.millis(1000), e -> text.setVisible(true)));
}
obfuscatedTimeline.setCycleCount(Animation.INDEFINITE);
obfuscatedTimeline.play();
text.getStyleClass().add("obfuscated");
}
case BOLD -> text.setStyle("-fx-font-weight: bold;");
case STRIKETHROUGH -> text.setStyle("-fx-strikethrough: true;");
case UNDERLINED -> text.setStyle("-fx-underline: true;");
case ITALIC -> text.setStyle("-fx-font-weight: italic;");
}
}
}));
nodes.add(text);
return nodes;
}
}

View File

@ -0,0 +1,230 @@
/*
* Minosoft
* Copyright (C) 2020 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.data.text
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.config.ConfigurationPaths
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.font.FontChar
import de.bixilon.minosoft.gui.rendering.hud.HUDScale
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.hash.BetterHashSet
import glm_.vec2.Vec2
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.collections.ObservableList
import javafx.scene.Node
import javafx.scene.paint.Color
import javafx.scene.text.Text
import javafx.util.Duration
import java.util.*
import java.util.function.Consumer
open class TextComponent : ChatComponent {
private val text: String
var color: RGBColor = ChatColors.WHITE
var formatting: BetterHashSet<ChatFormattingCode> = BetterHashSet()
constructor(text: String, color: RGBColor?, formatting: BetterHashSet<ChatFormattingCode>) {
this.text = text
if (color != null) {
this.color = color
}
this.formatting = formatting
}
constructor(text: String, color: RGBColor) {
this.text = text
this.color = color
}
constructor(text: String) {
this.text = text
}
fun setObfuscated(obfuscated: Boolean): TextComponent {
formatting.addOrRemove(PreChatFormattingCodes.OBFUSCATED, obfuscated)
return this
}
fun setBold(bold: Boolean): TextComponent {
formatting.addOrRemove(PreChatFormattingCodes.BOLD, bold)
return this
}
fun setStrikethrough(strikethrough: Boolean): TextComponent {
formatting.addOrRemove(PreChatFormattingCodes.STRIKETHROUGH, strikethrough)
return this
}
fun setUnderlined(underlined: Boolean): TextComponent {
formatting.addOrRemove(PreChatFormattingCodes.UNDERLINED, underlined)
return this
}
fun setItalic(italic: Boolean): TextComponent {
formatting.addOrRemove(PreChatFormattingCodes.ITALIC, italic)
return this
}
fun setReset(reset: Boolean): TextComponent {
formatting.addOrRemove(PostChatFormattingCodes.RESET, reset)
return this
}
fun setColor(color: RGBColor): TextComponent {
this.color = color
return this
}
fun setFormatting(formatting: BetterHashSet<ChatFormattingCode>): TextComponent {
this.formatting = formatting
return this
}
override fun hashCode(): Int {
return Objects.hash(text, color, formatting)
}
override fun equals(other: Any?): Boolean {
if (super.equals(other)) {
return true
}
if (hashCode() != other.hashCode()) {
return false
}
val their = other as TextComponent?
return text == their!!.message && color == their.color && formatting == their.formatting
}
override fun toString(): String {
return ansiColoredMessage
}
override fun getANSIColoredMessage(): String {
val builder = StringBuilder()
builder.append(ChatColors.getANSIColorByRGBColor(this.color))
for (formattingCode in this.formatting) {
if (formattingCode is PreChatFormattingCodes) {
builder.append(formattingCode.getANSI())
}
}
builder.append(this.text)
for (formattingCode in this.formatting) {
if (formattingCode is PostChatFormattingCodes) {
builder.append(formattingCode.getANSI())
}
}
builder.append(PostChatFormattingCodes.RESET)
return builder.toString()
}
override fun getLegacyText(): String {
val output = StringBuilder()
val colorChar = ChatColors.getColorId(color)
if (colorChar != null) {
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(Integer.toHexString(colorChar))
}
formatting.forEach(Consumer { chatFormattingCode: ChatFormattingCode -> output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(chatFormattingCode.char) })
output.append(text)
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(PostChatFormattingCodes.RESET.char)
return output.toString()
}
override fun getMessage(): String {
return text
}
override fun getJavaFXText(nodes: ObservableList<Node>): ObservableList<Node> {
val text = Text(text)
text.fill = Color.WHITE
if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_COLORED)) {
text.fill = Color.web(color.toString())
}
for (chatFormattingCode in formatting) {
if (chatFormattingCode is PreChatFormattingCodes) {
when (chatFormattingCode) {
PreChatFormattingCodes.OBFUSCATED -> {
// ToDo: potential memory leak: Stop timeline, when TextComponent isn't shown anymore
val obfuscatedTimeline = if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_OBFUSCATED)) {
Timeline(KeyFrame(Duration.millis(50.0), {
val chars = text.text.toCharArray()
for (i in chars.indices) {
chars[i] = Util.getRandomChar(ProtocolDefinition.OBFUSCATED_CHARS)
}
text.text = String(chars)
}))
} else {
Timeline(KeyFrame(Duration.millis(500.0), {
text.isVisible = false
}), KeyFrame(Duration.millis(1000.0), {
text.isVisible = true
}))
}
obfuscatedTimeline.cycleCount = Animation.INDEFINITE
obfuscatedTimeline.play()
text.styleClass.add("obfuscated")
}
PreChatFormattingCodes.BOLD -> {
text.style = "-fx-font-weight: bold;"
}
PreChatFormattingCodes.STRIKETHROUGH -> {
text.style = "-fx-strikethrough: true;"
}
PreChatFormattingCodes.UNDERLINED -> {
text.style = "-fx-underline: true;"
}
PreChatFormattingCodes.ITALIC -> {
text.style = "-fx-font-weight: italic;"
}
}
}
}
nodes.add(text)
return nodes
}
override fun addVerticies(startPosition: Vec2, offset: Vec2, font: Font, hudScale: HUDScale, meshData: MutableList<Float>) {
fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, color: RGBColor, meshData: MutableList<Float>) {
meshData.add(position.x)
meshData.add(position.y)
meshData.add(uv.x)
meshData.add(uv.y)
meshData.add(atlasPage.toFloat())
meshData.add(color.red / 256f)
meshData.add(color.green / 256f)
meshData.add(color.blue / 256f)
}
fun drawLetter(position: Vec2, scaledX: Float, fontChar: FontChar, color: RGBColor, meshData: MutableList<Float>) {
val scaledHeight = fontChar.height * hudScale.scale
drawLetterVertex(Vec2(position.x, position.y), fontChar.uvLeftDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y), fontChar.uvRightDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y), fontChar.uvRightDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y + scaledHeight), fontChar.uvRightUp, fontChar.atlasTextureIndex, color, meshData)
}
for (c in text.toCharArray()) {
val fontChar = font.getChar(c)
val scaledX = fontChar.endPixel * hudScale.scale
drawLetter(startPosition + offset, scaledX, fontChar, color, meshData)
offset += Vec2(scaledX, 0f)
}
}
}

View File

@ -1,121 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 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.data.text;
import com.google.gson.JsonArray;
import de.bixilon.minosoft.data.mappings.versions.Version;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
public class TranslatableComponent extends ChatComponent {
private final ArrayList<ChatComponent> data = new ArrayList<>();
private final String key;
private final TextComponent parent;
private final Version version;
public TranslatableComponent(Version version, String key, JsonArray data) {
this(version, null, key, data);
}
public TranslatableComponent(Version version, @Nullable TextComponent parent, String key, JsonArray data) {
this.version = version;
this.parent = parent;
this.key = key;
if (data == null) {
return;
}
data.forEach((jsonElement -> {
if (jsonElement.isJsonPrimitive()) {
this.data.add(ChatComponent.valueOf(version, parent, jsonElement.getAsString()));
} else {
this.data.add(new BaseComponent(version, parent, jsonElement.getAsJsonObject()));
}
}));
}
@Override
public String getANSIColoredMessage() {
return getList("getANSIColoredMessage");
}
@Override
public String getLegacyText() {
return getList("getLegacyText");
}
@Override
public String getMessage() {
return getList("getMessage");
}
@Override
public ObservableList<Node> getJavaFXText(ObservableList<Node> nodes) {
// ToDo fix nested base component (formatting), not just a string
// This is just a dirty workaround to enable formatting and coloring. Still need to do hover, click, ... stuff
return new BaseComponent(this.version, getLegacyText()).getJavaFXText(nodes);
}
// just used reflections to not write this twice anc only change the method name
private String getList(String methodName) {
try {
Object[] data = new String[this.data.size()];
for (int i = 0; i < this.data.size(); i++) {
data[i] = this.data.get(i).getClass().getMethod(methodName).invoke(this.data.get(i));
}
if (this.parent != null) {
StringBuilder builder = new StringBuilder();
if (methodName.equals("getANSIColoredMessage")) {
builder.append(ChatColors.getANSIColorByRGBColor(this.parent.getColor()));
} else if (methodName.equals("getLegacyText")) {
builder.append(ChatColors.getColorChar(this.parent.getColor()));
}
for (ChatFormattingCode code : this.parent.getFormatting()) {
if (code instanceof PreChatFormattingCodes preCode) {
builder.append(switch (methodName) {
case "getANSIColoredMessage" -> preCode.getANSI();
case "getLegacyText" -> ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR + preCode.getChar();
default -> "";
});
}
}
builder.append(this.version.getLocaleManager().translate(this.key, data));
for (ChatFormattingCode code : this.parent.getFormatting()) {
if (code instanceof PostChatFormattingCodes postCode) {
builder.append(switch (methodName) {
case "getANSIColoredMessage" -> postCode.getANSI();
case "getLegacyText" -> ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR + postCode.getChar();
default -> "";
});
}
}
return builder.toString();
}
String text = this.version.getLocaleManager().translate(this.key, data);
if (text == null) {
// Error, can not translate
text = "{invalid=true, key=" + this.key + ", data=" + Arrays.toString(data);
}
return text;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -128,9 +128,9 @@ class ChunkRenderer(private val world: World, val renderWindow: RenderWindow) :
val data = prepareChunk(chunkLocation, sectionHeight, section)
val sectionMap = chunkSectionsToDraw[chunkLocation]!!
renderWindow.renderQueue.add {
val newMesh = WorldMesh(data, Vec3(chunkLocation.x, sectionHeight, chunkLocation.z))
sectionMap[sectionHeight]?.unload()
sectionMap.remove(sectionHeight)
sectionMap[sectionHeight] = WorldMesh(data, Vec3(chunkLocation.x, sectionHeight, chunkLocation.z))
sectionMap[sectionHeight] = newMesh
}
}
}

View File

@ -1,10 +1,8 @@
package de.bixilon.minosoft.gui.rendering.hud
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.font.FontChar
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
import de.bixilon.minosoft.protocol.network.Connection
@ -16,12 +14,12 @@ import org.lwjgl.opengl.GL13.GL_TEXTURE0
class HUDRenderer : Renderer {
private val font = Font()
private val hudScale = HUDScale.LARGE
private val hudScale = HUDScale.MEDIUM
var fps: Int = 0
var frame = 0
private lateinit var fontShader: Shader
private lateinit var fontAtlasTexture: TextureArray
lateinit var hudMeshHUD: HUDFontMesh
private lateinit var hudMeshHUD: HUDFontMesh
override fun init(connection: Connection) {
@ -31,49 +29,14 @@ class HUDRenderer : Renderer {
fontShader = Shader("font_vertex.glsl", "font_fragment.glsl")
fontShader.load()
drawString(Vec2(100, 100), "FPS: $fps")
hudMeshHUD = HUDFontMesh(floatArrayOf())
}
fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, color: RGBColor, meshData: MutableList<Float>) {
meshData.add(position.x)
meshData.add(position.y)
meshData.add(uv.x)
meshData.add(uv.y)
meshData.add(atlasPage.toFloat())
meshData.add(color.red / 256f)
meshData.add(color.green / 256f)
meshData.add(color.blue / 256f)
}
fun drawLetter(position: Vec2, scaledX: Float, fontChar: FontChar, color: RGBColor, meshData: MutableList<Float>) {
val scaledHeight = fontChar.height * hudScale.scale
drawLetterVertex(Vec2(position.x, position.y), fontChar.uvLeftDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y), fontChar.uvRightDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y), fontChar.uvRightDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledX, position.y + scaledHeight), fontChar.uvRightUp, fontChar.atlasTextureIndex, color, meshData)
}
fun drawString(position: Vec2, text: String) {
if (this::hudMeshHUD.isInitialized) {
hudMeshHUD.unload()
}
fun drawChatComponent(position: Vec2, text: ChatComponent) {
hudMeshHUD.unload()
val data: MutableList<Float> = mutableListOf()
val chars = text.toCharArray()
var xOffset = position.x
for (char in chars) {
val fontChar = font.getChar(char)
val scaledX = fontChar.endPixel * hudScale.scale
drawLetter(Vec2(xOffset, position.y), scaledX, fontChar, ChatColors.getRandomColor(), data)
xOffset += scaledX
}
text.addVerticies(position, Vec2(0, 0), font, hudScale, data)
hudMeshHUD = HUDFontMesh(data.toFloatArray())
}
fun screenChangeResizeCallback(width: Int, height: Int) {
@ -90,7 +53,7 @@ class HUDRenderer : Renderer {
frame++
if (frame % 30 == 0) {
drawString(Vec2(100, 100), "FPS: $fps")
drawChatComponent(Vec2(0, 0), ChatComponent.valueOf("§6FPS:§e$fps"))
}
hudMeshHUD.draw()