font loading

This commit is contained in:
Bixilon 2021-08-03 21:32:39 +02:00
parent 1a2b990bd7
commit faa85491a4
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
16 changed files with 390 additions and 2 deletions

View File

@ -20,6 +20,8 @@ import de.bixilon.minosoft.gui.rendering.block.WorldRenderer
import de.bixilon.minosoft.gui.rendering.block.chunk.ChunkBorderRenderer
import de.bixilon.minosoft.gui.rendering.block.outline.BlockOutlineRenderer
import de.bixilon.minosoft.gui.rendering.entity.EntityHitBoxRenderer
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.font.FontLoader
import de.bixilon.minosoft.gui.rendering.hud.atlas.TextureLike
import de.bixilon.minosoft.gui.rendering.hud.atlas.TextureLikeTexture
import de.bixilon.minosoft.gui.rendering.input.key.RenderWindowInputHandler
@ -72,6 +74,7 @@ class RenderWindow(
private val screenshotTaker = ScreenshotTaker(this)
val tintColorCalculator = TintColorCalculator(connection.world)
val textureManager = renderSystem.createTextureManager()
lateinit var font: Font
val rendererMap: MutableMap<ResourceLocation, Renderer> = synchronizedMapOf()
@ -143,6 +146,7 @@ class RenderWindow(
uvEnd = Vec2(1.0f, 1.0f),
size = Vec2i(16, 16)
)
font = FontLoader.load(this)
shaderManager.init()
@ -158,6 +162,7 @@ class RenderWindow(
Log.log(LogMessageType.RENDERING_LOADING) { "Loading textures (${stopwatch.labTime()})..." }
textureManager.staticTextures.load()
font.postInit()
Log.log(LogMessageType.RENDERING_LOADING) { "Post loading renderer (${stopwatch.labTime()})..." }
for (renderer in rendererMap.values) {

View File

@ -0,0 +1,30 @@
/*
* Minosoft
* Copyright (C) 2021 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
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import glm_.vec2.Vec2
class CharData(
val char: Char,
val texture: AbstractTexture,
val width: Int,
var uvStart: Vec2,
var uvEnd: Vec2,
) {
fun postInit() {
uvStart = uvStart * texture.textureArrayUV
uvEnd = uvEnd * texture.textureArrayUV
}
}

View File

@ -0,0 +1,33 @@
/*
* Minosoft
* Copyright (C) 2021 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
import de.bixilon.minosoft.gui.rendering.font.provider.FontProvider
class Font(
val providers: MutableList<FontProvider>,
) : FontProvider {
override fun postInit() {
for (provider in providers) {
provider.postInit()
}
}
companion object {
const val CHAR_HEIGHT = 8
}
}

View File

@ -0,0 +1,49 @@
/*
* Minosoft
* Copyright (C) 2021 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
import de.bixilon.minosoft.data.registries.factory.DefaultFactory
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.font.provider.BitmapFontProvider
import de.bixilon.minosoft.gui.rendering.font.provider.FontProvider
import de.bixilon.minosoft.gui.rendering.font.provider.FontProviderFactory
import de.bixilon.minosoft.gui.rendering.font.provider.LegacyUnicodeFontProvider
import de.bixilon.minosoft.util.KUtil.check
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
object FontLoader : DefaultFactory<FontProviderFactory<*>>(
BitmapFontProvider,
LegacyUnicodeFontProvider,
// ToDo: True type font
) {
private val FONT_INDEX = "font/default.json".toResourceLocation()
fun load(renderWindow: RenderWindow): Font {
val fontIndex = renderWindow.connection.assetsManager.readJsonAsset(FONT_INDEX)
val providers: MutableList<FontProvider> = mutableListOf()
for (provider in fontIndex["providers"].listCast<Map<String, Any>>()!!) {
val type = provider["type"].toResourceLocation()
providers += this[type].check { "Unknown font provider type $type" }.build(renderWindow, provider)
}
return Font(
providers = providers,
)
}
}

View File

@ -0,0 +1,84 @@
/*
* Minosoft
* Copyright (C) 2021 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.provider
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.font.CharData
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.util.KUtil.asList
import de.bixilon.minosoft.util.KUtil.toDouble
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
import glm_.vec2.Vec2
class BitmapFontProvider(
private val renderWindow: RenderWindow,
data: Map<String, Any>,
) : FontProvider {
val height = data["height"]?.toInt() ?: 8
val ascent = data["ascent"].toDouble()
private val chars: MutableMap<Char, CharData> = mutableMapOf()
init {
val texture = renderWindow.textureManager.staticTextures.createTexture(data["file"].toResourceLocation().texture())
texture.load(renderWindow.connection.assetsManager)
val pixel = Vec2(1.0f) / texture.size
for ((y, row) in data["chars"].asList().withIndex()) {
val yStart = pixel.y * y * height
val yEnd = pixel.y * (y + 1) * height
for ((x, char) in row.unsafeCast<String>().toCharArray().withIndex()) {
val charXStart = 0 // ToDo: Calculate dynamically
val charXEnd = CHAR_WIDTH // ToDo: Calculate dynamically
val xOffset = pixel.x * CHAR_WIDTH * x
val uvStart = Vec2(
x = xOffset + (pixel.x * charXStart),
y = yStart,
)
val uvEnd = Vec2(
x = xOffset + (pixel.x * charXEnd),
y = yEnd,
)
val charData = CharData(
char = char,
texture = texture,
width = charXEnd - charXStart,
uvStart = uvStart,
uvEnd = uvEnd,
)
chars[char] = charData
}
}
}
override fun postInit() {
for (char in chars.values) {
char.postInit()
}
}
companion object : FontProviderFactory<BitmapFontProvider> {
private const val CHAR_WIDTH = 8
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:bitmap".toResourceLocation()
override fun build(renderWindow: RenderWindow, data: Map<String, Any>): BitmapFontProvider {
return BitmapFontProvider(renderWindow, data)
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Minosoft
* Copyright (C) 2021 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.provider
interface FontProvider {
fun postInit()
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2021 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.provider
import de.bixilon.minosoft.data.registries.CompanionResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderWindow
interface FontProviderFactory<T : FontProvider> : CompanionResourceLocation {
fun build(renderWindow: RenderWindow, data: Map<String, Any>): T
}

View File

@ -0,0 +1,98 @@
/*
* Minosoft
* Copyright (C) 2021 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.provider
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.font.CharData
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
import glm_.vec2.Vec2
class LegacyUnicodeFontProvider(
private val renderWindow: RenderWindow,
data: Map<String, Any>,
) : FontProvider {
private val chars: Array<CharData?> = arrayOfNulls(1 shl Char.SIZE_BITS)
init {
val template = data["template"].unsafeCast<String>()
val sizes = renderWindow.connection.assetsManager.readAssetAsStream(data["sizes"].toResourceLocation())
var char = '\u0000'
for (page in 0 until UNICODE_PAGES) {
if (MISSING_UNICODE_PAGES.contains(page)) {
// This page somehow does not exist, but we are fine with it
// ToDo: Check if it really exist
sizes.skip(UNICODE_PAGE_SIZE.toLong()) // skip the sizes to not mess up
continue
}
val texture = renderWindow.textureManager.staticTextures.createTexture(template.format("%02x".format(page)).toResourceLocation().texture())
for (y in 0 until UNICODE_PAGE_SIZE / CHAR_SIZE) {
val yStart = PIXEL.y * y * CHAR_SIZE
val yEnd = PIXEL.y * (y + 1) * CHAR_SIZE
for (x in 0 until UNICODE_PAGE_SIZE / CHAR_SIZE) {
val widthByte = sizes.read()
val charXStart = (widthByte shr 4) and 0x0F
val charXEnd = widthByte and 0x0F
val xOffset = PIXEL.x * CHAR_SIZE * x
val uvStart = Vec2(
x = xOffset + (PIXEL.x * charXStart),
y = yStart,
)
val uvEnd = Vec2(
x = xOffset + (PIXEL.x * charXEnd),
y = yEnd,
)
val charData = CharData(
char = char,
texture = texture,
width = (charXEnd - charXStart) / (CHAR_SIZE / Font.CHAR_HEIGHT),
uvStart = uvStart,
uvEnd = uvEnd,
)
chars[(char++).code] = charData
}
}
}
}
override fun postInit() {
for (char in chars) {
char?.postInit()
}
}
companion object : FontProviderFactory<LegacyUnicodeFontProvider> {
private val MISSING_UNICODE_PAGES = listOf(0x08, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xEE, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8)
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:legacy_unicode".toResourceLocation()
private const val UNICODE_PAGE_SIZE = 256
private const val UNICODE_PAGES = 256
private const val CHAR_SIZE = 16
private val PIXEL = Vec2(1.0f) / UNICODE_PAGE_SIZE
override fun build(renderWindow: RenderWindow, data: Map<String, Any>): LegacyUnicodeFontProvider {
return LegacyUnicodeFontProvider(renderWindow, data)
}
}
}

View File

@ -28,6 +28,7 @@ import java.nio.ByteBuffer
interface AbstractTexture {
val resourceLocation: ResourceLocation
var textureArrayUV: Vec2
var singlePixelSize: Vec2
val state: TextureStates
val size: Vec2i

View File

@ -31,6 +31,7 @@ class MemoryTexture(
override var properties: ImageProperties = ImageProperties(),
generator: ((x: Int, y: Int) -> RGBColor)? = null,
) : AbstractTexture {
override lateinit var textureArrayUV: Vec2
override lateinit var singlePixelSize: Vec2
override var renderData: TextureRenderData? = null
override var transparency: TextureTransparencies = TextureTransparencies.OPAQUE

View File

@ -29,6 +29,7 @@ import java.nio.ByteBuffer
class PNGTexture(override val resourceLocation: ResourceLocation) : AbstractTexture {
override var renderData: TextureRenderData? = null
override lateinit var textureArrayUV: Vec2
override lateinit var singlePixelSize: Vec2
override var state: TextureStates = TextureStates.DECLARED
private set

View File

@ -25,6 +25,7 @@ import java.nio.ByteBuffer
class SpriteTexture(private val original: AbstractTexture) : AbstractTexture {
override val resourceLocation: ResourceLocation = original.resourceLocation
override var textureArrayUV: Vec2 by original::textureArrayUV
override var singlePixelSize: Vec2 by original::singlePixelSize
override var properties: ImageProperties by original::properties
override var renderData: TextureRenderData? by original::renderData

View File

@ -103,6 +103,7 @@ class OpenGLTextureArray(
val uvEnd = Vec2(texture.size) / arrayResolution
val singlePixelSize = Vec2(1.0f) / arrayResolution
val textureArrayUV = Vec2(texture.size) / arrayResolution
if (texture is SpriteTexture) {
val animationIndex = lastAnimationIndex++
@ -121,6 +122,7 @@ class OpenGLTextureArray(
texturesByResolution[arrayId] += texture
texture.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, -1)
texture.singlePixelSize = singlePixelSize
texture.textureArrayUV = textureArrayUV
}
}

View File

@ -0,0 +1,35 @@
/*
* Minosoft
* Copyright (C) 2021 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.textures
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.toResourceLocation
object TextureUtil {
fun ResourceLocation.texture(): ResourceLocation {
var path = ""
if (!this.path.startsWith("textures/")) {
path += "textures/"
}
path += this.path
if (!path.contains(".")) {
// ending
path += ".png"
}
return "$namespace:$path".toResourceLocation()
}
}

View File

@ -73,7 +73,7 @@ object KUtil {
return null
}
fun Any.toResourceLocation(): ResourceLocation {
fun Any?.toResourceLocation(): ResourceLocation {
return when (this) {
is String -> ResourceLocation(this)
is ResourceLocation -> this
@ -394,4 +394,11 @@ object KUtil {
return ret.toMap()
}
fun <T> T?.check(message: (() -> Any)? = null): T {
if (this == null) {
throw NullPointerException(message?.invoke()?.toString() ?: "Null check failed")
}
return this
}
}

View File

@ -44,7 +44,7 @@ object NBTUtil {
return this.compoundCast()!!
}
fun <T> Any.listCast(): MutableList<T>? {
fun <T> Any?.listCast(): MutableList<T>? {
try {
return this as MutableList<T>
} catch (ignored: ClassCastException) {