rendering: detect transparent textures, calculate font width, improve hud renderer

This commit is contained in:
Bixilon 2021-02-11 19:27:56 +01:00
parent fda95f1d6e
commit b089ffb4a4
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
14 changed files with 125 additions and 59 deletions

View File

@ -1,4 +0,0 @@
Relative Chunk position: 3x 0-15 (3x 4bit = 12bit)
Side: 0-6 (3 bits)
Light level: 1x 0-15 (4bit)
Texture index: ?

View File

@ -128,7 +128,7 @@ public class BaseComponent extends ChatComponent {
}
}
BetterHashSet<ChatFormattingCode> formattingCodes;
if (parent != null && parent.getFormatting() != null) {
if (parent != null) {
formattingCodes = (BetterHashSet<ChatFormattingCode>) parent.getFormatting().clone();
} else {
formattingCodes = new BetterHashSet<>();
@ -213,9 +213,9 @@ public class BaseComponent extends ChatComponent {
}
@Override
public void addVerticies(Vec2 startPosition, Vec2 offset, Font font, HUDScale hudScale, List<Float> meshData) {
public void addVerticies(Vec2 startPosition, Vec2 offset, Font font, HUDScale hudScale, List<Float> meshData, Vec2 maxSize) {
for (var chatPart : this.parts) {
chatPart.addVerticies(startPosition, offset, font, hudScale, meshData);
chatPart.addVerticies(startPosition, offset, font, hudScale, meshData, maxSize);
}
}

View File

@ -91,5 +91,5 @@ public abstract class ChatComponent {
/**
* @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);
public abstract void addVerticies(Vec2 startPosition, Vec2 offset, Font font, HUDScale hudScale, List<Float> meshData, Vec2 maxSize);
}

View File

@ -19,11 +19,15 @@ public final class RGBColor implements ChatCode {
private final int color;
public RGBColor(int red, int green, int blue, int alpha) {
this.color = blue | (green << 8) | (red << 16) | (alpha << 24);
this.color = (alpha) | (blue << 8) | (green << 16) | (red << 24);
}
public RGBColor(byte red, byte green, byte blue, byte alpha) {
this(red & 0xFF, green & 0xFF, blue & 0xFF, alpha & 0xFF);
}
public RGBColor(int red, int green, int blue) {
this.color = blue | (green << 8) | (red << 16);
this(red, green, blue, 0xFF);
}
public RGBColor(int color) {
@ -39,22 +43,22 @@ public final class RGBColor implements ChatCode {
@IntRange(from = 0, to = 255)
public int getAlpha() {
return (this.color >> 24) & 0xFF;
return (this.color & 0xFF);
}
@IntRange(from = 0, to = 255)
public int getRed() {
return (this.color >> 16) & 0xFF;
return (this.color >>> 24) & 0xFF;
}
@IntRange(from = 0, to = 255)
public int getGreen() {
return (this.color >> 8) & 0xFF;
return (this.color >>> 16) & 0xFF;
}
@IntRange(from = 0, to = 255)
public int getBlue() {
return this.color & 0xFF;
return (this.color >>> 8) & 0xFF;
}
@Override

View File

@ -153,7 +153,7 @@ open class TextComponent : ChatComponent {
val text = Text(text)
text.fill = Color.WHITE
if (Minosoft.getConfig().getBoolean(ConfigurationPaths.BooleanPaths.CHAT_COLORED)) {
text.fill = Color.web(color.toString())
text.fill = Color.rgb(color.red, color.green, color.blue)
}
for (chatFormattingCode in formatting) {
if (chatFormattingCode is PreChatFormattingCodes) {
@ -199,7 +199,7 @@ open class TextComponent : ChatComponent {
return nodes
}
override fun addVerticies(startPosition: Vec2, offset: Vec2, font: Font, hudScale: HUDScale, meshData: MutableList<Float>) {
override fun addVerticies(startPosition: Vec2, offset: Vec2, font: Font, hudScale: HUDScale, meshData: MutableList<Float>, maxSize: Vec2) {
fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, color: RGBColor, meshData: MutableList<Float>) {
meshData.add(position.x)
meshData.add(position.y)
@ -211,20 +211,24 @@ open class TextComponent : ChatComponent {
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 drawLetter(position: Vec2, scaledWidth: Float, scaledHeight: Float, fontChar: FontChar, color: RGBColor, meshData: MutableList<Float>) {
drawLetterVertex(Vec2(position.x, position.y), fontChar.uvLeftUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.uvRightUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledWidth, position.y), fontChar.uvRightUp, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x, position.y + scaledHeight), fontChar.uvLeftDown, fontChar.atlasTextureIndex, color, meshData)
drawLetterVertex(Vec2(position.x + scaledWidth, position.y + scaledHeight), fontChar.uvRightDown, 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)
val scaledX = fontChar.width * (font.charHeight / fontChar.height.toFloat()) * hudScale.scale
val scaledHeight = font.charHeight * hudScale.scale
drawLetter(startPosition + offset, scaledX, scaledHeight, fontChar, color, meshData)
offset += Vec2(scaledX + (hudScale.scale / 2), 0f)
maxSize.x += scaledX + (hudScale.scale / 2)
if (maxSize.y < scaledHeight) {
maxSize.y = scaledHeight
}
}
}
}

View File

@ -31,7 +31,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
// all renderers
val chunkRenderer: ChunkRenderer = ChunkRenderer(connection.player.world, this)
val hudRenderer: HUDRenderer = HUDRenderer()
val hudRenderer: HUDRenderer = HUDRenderer(connection, this)
val renderQueue = ConcurrentLinkedQueue<Runnable>()
@ -78,7 +78,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
}
glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED)
glfwSetCursorPosCallback(windowId) { windowId: Long, xPos: Double, yPos: Double -> camera.mouseCallback(xPos, yPos) }
glfwSetCursorPosCallback(windowId) { _: Long, xPos: Double, yPos: Double -> camera.mouseCallback(xPos, yPos) }
MemoryStack.stackPush().let { stack ->
val pWidth = stack.mallocInt(1)
val pHeight = stack.mallocInt(1)
@ -165,9 +165,9 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
frameTimeLastCalc += glfwGetTime() - currentFrame
if (glfwGetTime() - lastCalcTime >= 0.5) {
glfwSetWindowTitle(windowId, "Minosoft | FPS: ${framesLastSecond * 2} (${(0.5 * framesLastSecond / (frameTimeLastCalc)).roundToInt()})")
hudRenderer.fps = framesLastSecond * 2
if (glfwGetTime() - lastCalcTime >= 0.25) {
glfwSetWindowTitle(windowId, "Minosoft | FPS: ${framesLastSecond * 4} (${(0.25 * framesLastSecond / (frameTimeLastCalc)).roundToInt()})")
hudRenderer.fps = framesLastSecond * 4
lastCalcTime = glfwGetTime()
framesLastSecond = 0
frameTimeLastCalc = 0.0

View File

@ -127,14 +127,15 @@ open class BlockModelElement(data: JsonObject) {
rotatedDirectionVector = rotateVector(rotatedDirectionVector, rotation.y.toDouble(), Axes.Y)
return Directions.byDirection(rotateVector(rotatedDirectionVector, rotation.x.toDouble(), Axes.X))
}
val realDirection = getRotatedDirection()
val positionTemplate = FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
val face = faces[realDirection] ?: return // Not our face
val texture = textureMapping[face.textureName] ?: TextureArray.DEBUG_TEXTURE
if (texture.isTransparent) {
return
}
// if (texture.isTransparent) {
// return // ToDo: force render transparent faces
// }
val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]])

View File

@ -6,6 +6,7 @@ import de.bixilon.minosoft.gui.rendering.textures.TextureArray
class Font {
lateinit var providers: List<FontProvider>
val charHeight = 8
fun load(assetsManager: AssetsManager) {
providers = FontLoader.loadFontProviders(assetsManager)
@ -36,7 +37,7 @@ class Font {
for (provider in providers) {
for (char in provider.chars.values) {
char.calculateUV(provider.width, atlasWidthSinglePixel, atlasHeightSinglePixel)
char.calculateUV(provider.width, atlasWidthSinglePixel, atlasHeightSinglePixel) // ToDo: Unicode: With should pe plus 1
}
}
return textureArray

View File

@ -7,11 +7,11 @@ data class FontChar(
val atlasTextureIndex: Int,
val row: Int,
val column: Int,
val startPixel: Int,
val endPixel: Int,
var startPixel: Int,
var endPixel: Int,
val height: Int,
) {
val width = endPixel - startPixel
var width = endPixel - startPixel
lateinit var uvLeftUp: Vec2
lateinit var uvRightUp: Vec2
lateinit var uvRightDown: Vec2

View File

@ -4,6 +4,7 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import de.bixilon.minosoft.data.assets.AssetsManager
import de.bixilon.minosoft.data.mappings.ModIdentifier
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
import java.io.InputStream
@ -21,7 +22,7 @@ object FontLoader {
}
private fun loadBitmapFontProvider(atlasPath: ModIdentifier, height: Int? = 8, ascent: Int, chars: List<Char>, assetsManager: AssetsManager, atlasOffset: Int): FontProvider {
val width = if (ascent == 7) {
val width = if (ascent == 7) { // ToDo: Why?
8
} else {
9
@ -29,12 +30,45 @@ object FontLoader {
val provider = FontProvider(width)
val atlasTexture = Texture((atlasPath.mod + "/textures/" + atlasPath.identifier), atlasOffset)
atlasTexture.load(assetsManager)
provider.atlasTextures.add(atlasTexture)
val height = height ?: atlasTexture.width / 16
provider.atlasTextures.add(atlasTexture)
val charsCoordinates: MutableList<MutableList<FontChar>> = mutableListOf() // ToDo: Remove this
for ((i, char) in chars.withIndex()) {
val fontChar = FontChar(0, atlasOffset, i / FONT_ATLAS_SIZE, i % FONT_ATLAS_SIZE, 0, width, height)
if (i % 16 == 0) {
charsCoordinates.add(mutableListOf())
}
val fontChar = FontChar(0, atlasOffset, i / FONT_ATLAS_SIZE, i % FONT_ATLAS_SIZE, width, 0, height)
provider.chars[char] = fontChar
charsCoordinates[i / 16].add(fontChar)
}
atlasTexture.buffer.rewind()
// calculate start and endpixel for every char
for (y in 0 until atlasTexture.height) {
for (x in 0 until atlasTexture.width) {
val color = RGBColor(atlasTexture.buffer.get(), atlasTexture.buffer.get(), atlasTexture.buffer.get(), atlasTexture.buffer.get())
if (color.alpha == 0) {
continue
}
val fontChar = charsCoordinates[y / height][x / width]
val pixel = x % width
if (fontChar.startPixel > pixel) {
fontChar.startPixel = pixel
}
if (fontChar.endPixel <= pixel) {
fontChar.endPixel = pixel + 1
}
}
}
for ((_, fontChar) in provider.chars) {
if (fontChar.startPixel == width && fontChar.endPixel == 0) {
// invisible char (like a space)
fontChar.startPixel = 0
fontChar.endPixel = width / 2 // <- Divide by 2, else spaces look really big...
}
fontChar.width = fontChar.endPixel - fontChar.startPixel
}
atlasTexture.buffer.flip()
return provider
}
@ -56,7 +90,7 @@ object FontLoader {
provider.atlasTextures.add(currentAtlasTexture)
}
val sizeByte = sizes.read()
val fontChar = FontChar((i / 256), atlasOffset + (i / 256), (i % 256) / FONT_ATLAS_SIZE, (i % 256) % FONT_ATLAS_SIZE, (sizeByte shr 4) and 0x0F, sizeByte and 0x0F, 16)
val fontChar = FontChar((i / 256), atlasOffset + (i / 256), (i % 256) / FONT_ATLAS_SIZE, (i % 256) % FONT_ATLAS_SIZE, (sizeByte shr 4) and 0x0F, (sizeByte and 0x0F) + 1, 16)
provider.chars[i.toChar()] = fontChar
i++
}

View File

@ -1,6 +1,7 @@
package de.bixilon.minosoft.gui.rendering.hud
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.shader.Shader
@ -12,7 +13,7 @@ import org.lwjgl.opengl.GL11.GL_DEPTH_TEST
import org.lwjgl.opengl.GL11.glDisable
import org.lwjgl.opengl.GL13.GL_TEXTURE0
class HUDRenderer : Renderer {
class HUDRenderer(private val connection: Connection, private val renderWindow: RenderWindow) : Renderer {
private val font = Font()
private val hudScale = HUDScale.MEDIUM
var fps: Int = 0
@ -20,6 +21,8 @@ class HUDRenderer : Renderer {
private lateinit var fontShader: Shader
private lateinit var fontAtlasTexture: TextureArray
private lateinit var hudMeshHUD: HUDFontMesh
private var screenWidth = 0
private var screenHeight = 0
override fun init(connection: Connection) {
@ -32,17 +35,18 @@ class HUDRenderer : Renderer {
hudMeshHUD = HUDFontMesh(floatArrayOf())
}
fun drawChatComponent(position: Vec2, text: ChatComponent) {
fun drawChatComponent(position: Vec2, text: ChatComponent, meshData: MutableList<Float>, maxSize: Vec2) {
hudMeshHUD.unload()
val data: MutableList<Float> = mutableListOf()
text.addVerticies(position, Vec2(0, 0), font, hudScale, data)
hudMeshHUD = HUDFontMesh(data.toFloatArray())
text.addVerticies(position, Vec2(0, 0), font, hudScale, meshData, maxSize)
}
fun screenChangeResizeCallback(width: Int, height: Int) {
fontShader.use()
screenWidth = width
screenHeight = height
fontShader.setMat4("projectionMatrix", glm.ortho(0.0f, width.toFloat(), 0.0f, height.toFloat()))
fontShader.setMat4("projectionMatrix", glm.ortho(0.0f, width.toFloat(), height.toFloat(), 0.0f))
prepare()
}
override fun draw() {
@ -52,10 +56,29 @@ class HUDRenderer : Renderer {
glDisable(GL_DEPTH_TEST)
frame++
if (frame % 30 == 0) {
drawChatComponent(Vec2(0, 0), ChatComponent.valueOf("§6FPS:§e$fps"))
if (frame % 15 == 0) {
prepare()
}
hudMeshHUD.draw()
}
fun prepare() {
val runtime = Runtime.getRuntime()!!
val meshData: MutableList<Float> = mutableListOf()
val components: List<ChatComponent> = mutableListOf(
ChatComponent.valueOf("§fMinosoft: §eUnreleased version (pre beta 1)"),
ChatComponent.valueOf("§fFPS: §e$fps"),
ChatComponent.valueOf("§fRAM: §c${(runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024)}M (${runtime.totalMemory() / (1024 * 1024)}M) / ${runtime.maxMemory() / (1024 * 1024)}M"),
ChatComponent.valueOf("§fXYZ §6${renderWindow.camera.cameraPosition.x} / ${renderWindow.camera.cameraPosition.y} / ${renderWindow.camera.cameraPosition.z}"),
ChatComponent.valueOf("§fConnected to: §a${connection.address}"),
)
val offset = Vec2(3, 3)
for ((_, component) in components.withIndex()) {
val currentOffset = Vec2()
drawChatComponent(offset, component, meshData, currentOffset)
offset += Vec2(0, currentOffset.y + 1)
}
hudMeshHUD = HUDFontMesh(meshData.toFloatArray())
}
}

View File

@ -1,6 +1,7 @@
package de.bixilon.minosoft.gui.rendering.textures
import de.bixilon.minosoft.data.assets.AssetsManager
import de.bixilon.minosoft.data.text.RGBColor
import de.matthiasmann.twl.utils.PNGDecoder
import org.lwjgl.BufferUtils
import java.nio.ByteBuffer
@ -30,9 +31,12 @@ class Texture(
decoder.decode(buffer, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
width = decoder.width
height = decoder.height
buffer.flip()
if (TextureArray.TRANSPARENT_TEXTURES.contains(name)) { // ToDo: this should not be hardcoded!
isTransparent = true
buffer.rewind()
for (i in 0 until buffer.limit() step 4) {
val color = RGBColor(buffer.get(), buffer.get(), buffer.get(), buffer.get())
if (color.alpha < 0xFF) {
isTransparent = true
}
}
buffer.flip()
loaded = true

View File

@ -38,7 +38,6 @@ class TextureArray(private val textures: List<Texture>, val maxWidth: Int, val m
companion object {
val DEBUG_TEXTURE = Texture("block/debug", 0)
val TRANSPARENT_TEXTURES = listOf("block/glass")
fun createTextureArray(assetsManager: AssetsManager? = null, textures: List<Texture>, maxWidth: Int = -1, maxHeight: Int = -1): TextureArray {
var calculatedMaxWidth = 0

View File

@ -11,10 +11,10 @@ uniform sampler2DArray texureArray;
void main() {
vec4 textureColor = texture(texureArray, passTextureCoordinates);
if (textureColor.a == 0) {
textureColor.a = 0.1f;
textureColor.r = 0.1f;
textureColor.g = 0.1f;
textureColor.b = 0.1f;
textureColor.a = 0.2f;
textureColor.r = 0.0f;
textureColor.g = 0.0f;
textureColor.b = 0.0f;
//discard;
} else {
textureColor.rgb = passCharColor * (vec3(1.0f) / textureColor.rgb);