mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-14 18:05:51 -04:00
rendering: improve texture loading, load font from minecraft assets, render single char
This commit is contained in:
parent
9a1c4aace2
commit
d7a10ae082
@ -56,10 +56,11 @@ object ChunkPreparer {
|
||||
section.getBlock(position.getLocationByDirection(Directions.EAST))
|
||||
}
|
||||
|
||||
fun drawBlock() {
|
||||
block.blockModel.render(position, data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
|
||||
}
|
||||
drawBlock()
|
||||
// if(block.fullIdentifier != "minecraft:dropper"){
|
||||
// continue
|
||||
// }
|
||||
|
||||
block.blockModel.render(position, data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
|
||||
}
|
||||
return data.toFloatArray()
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import de.bixilon.minosoft.gui.rendering.textures.TextureArray
|
||||
import de.bixilon.minosoft.protocol.network.Connection
|
||||
import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPlayerPositionAndRotationSending
|
||||
import de.bixilon.minosoft.util.CountUpAndDownLatch
|
||||
import glm_.vec2.Vec2
|
||||
import org.lwjgl.*
|
||||
import org.lwjgl.glfw.Callbacks
|
||||
import org.lwjgl.glfw.GLFW.*
|
||||
@ -110,17 +111,14 @@ class RenderWindow(private val connection: Connection) {
|
||||
|
||||
|
||||
// Make the window visible
|
||||
glfwShowWindow(windowId)
|
||||
GL.createCapabilities()
|
||||
glClearColor(137 / 256f, 207 / 256f, 240 / 256f, 1.0f)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
minecraftTextures = TextureArray(connection.version.assetsManager, resolveBlockTextureIds(connection.version.mapping.blockMap.values))
|
||||
minecraftTextures = TextureArray.createTextureArray(connection.version.assetsManager, resolveBlockTextureIds(connection.version.mapping.blockMap.values), 16, 16) // ToDo :Remove fixed size
|
||||
minecraftTextures.load()
|
||||
fontAtlasTexture = TextureArray(connection.version.assetsManager, listOf(Texture("font/unicode_page_00", 0)), 256)
|
||||
fontAtlasTexture.load()
|
||||
|
||||
chunkShader = Shader("chunk_vertex.glsl", "chunk_fragment.glsl")
|
||||
chunkShader.load()
|
||||
@ -132,18 +130,37 @@ class RenderWindow(private val connection: Connection) {
|
||||
fontShader = Shader("font_vertex.glsl", "font_fragment.glsl")
|
||||
fontShader.load()
|
||||
fontShader.use()
|
||||
fontShader.setFloat("atlasSize", 256f)
|
||||
|
||||
val font = Font()
|
||||
val char = font.chars['§']!!
|
||||
font2DToDraw.add(Font2DMesh(floatArrayOf(
|
||||
-0.5f, -0.5f, char.column * 16f, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
-0.5f, 0f, char.column * 16f, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
0f, -0.5f, char.column * 16f + char.width, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
0f, -0.5f, char.column * 16f + char.width, char.row * 16f + char.width, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
-0.5f, 0f, char.column * 16f, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
0f, 0f, char.column * 16f + char.width, char.row * 16f, font.atlasOffset + char.atlasPage.toFloat(),
|
||||
)))
|
||||
font.load(connection.version.assetsManager)
|
||||
|
||||
fun drawLetterVertex(position: Vec2, uv: Vec2, atlasPage: Int, meshData: MutableList<Float>) {
|
||||
meshData.add(position.x)
|
||||
meshData.add(position.y)
|
||||
meshData.add(uv.x)
|
||||
meshData.add(uv.y)
|
||||
meshData.add(atlasPage.toFloat())
|
||||
}
|
||||
|
||||
fontAtlasTexture = font.createAtlasTexture()
|
||||
fontAtlasTexture.load()
|
||||
|
||||
|
||||
fun drawLetter(position: Vec2, char: Char) {
|
||||
val fontChar = font.getChar(char)
|
||||
val meshData: MutableList<Float> = mutableListOf()
|
||||
|
||||
drawLetterVertex(Vec2(-0.5f, -0.5f), fontChar.uvLeftDown, fontChar.atlasTextureIndex, meshData)
|
||||
drawLetterVertex(Vec2(-0.5f, 0f), fontChar.uvLeftUp, fontChar.atlasTextureIndex, meshData)
|
||||
drawLetterVertex(Vec2(0f, -0.5f), fontChar.uvRightDown, fontChar.atlasTextureIndex, meshData)
|
||||
drawLetterVertex(Vec2(0f, -0.5f), fontChar.uvRightDown, fontChar.atlasTextureIndex, meshData)
|
||||
drawLetterVertex(Vec2(-0.5f, 0f), fontChar.uvLeftUp, fontChar.atlasTextureIndex, meshData)
|
||||
drawLetterVertex(Vec2(0f, 0f), fontChar.uvRightUp, fontChar.atlasTextureIndex, meshData)
|
||||
|
||||
font2DToDraw.add(Font2DMesh(meshData.toFloatArray()))
|
||||
}
|
||||
|
||||
drawLetter(Vec2(0, 0), 'ä')
|
||||
|
||||
|
||||
glfwSetWindowSizeCallback(windowId, object : GLFWWindowSizeCallback() {
|
||||
@ -155,6 +172,8 @@ class RenderWindow(private val connection: Connection) {
|
||||
}
|
||||
})
|
||||
|
||||
glfwShowWindow(windowId)
|
||||
|
||||
latch?.countDown()
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,44 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font
|
||||
|
||||
import de.bixilon.minosoft.data.assets.AssetsManager
|
||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
|
||||
|
||||
class Font {
|
||||
val chars: MutableMap<Char, FontChar> = mutableMapOf()
|
||||
val atlasOffset = 0
|
||||
lateinit var providers: List<FontProvider>
|
||||
|
||||
init {
|
||||
for (page in 0 until 256) {
|
||||
for (x in 0 until 16) {
|
||||
for (y in 0 until 16) {
|
||||
chars[(page * 256 + (x * 16 + y)).toChar()] = FontChar(page, x, y)
|
||||
}
|
||||
fun load(assetsManager: AssetsManager) {
|
||||
providers = FontLoader.loadFontProviders(assetsManager)
|
||||
}
|
||||
|
||||
fun getChar(char: Char): FontChar {
|
||||
for (provider in providers) {
|
||||
provider.chars[char]?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
throw IllegalStateException("$char can not be rendered!")
|
||||
}
|
||||
|
||||
fun createAtlasTexture(): TextureArray {
|
||||
val textures: MutableList<Texture> = mutableListOf()
|
||||
for (provider in providers) {
|
||||
for (atlasPage in provider.atlasTextures) {
|
||||
textures.add(atlasPage)
|
||||
}
|
||||
}
|
||||
|
||||
val textureArray = TextureArray.createTextureArray(textures = textures)
|
||||
|
||||
|
||||
val atlasWidthSinglePixel = 1f / textureArray.maxWidth
|
||||
val atlasHeightSinglePixel = 1f / textureArray.maxHeight
|
||||
|
||||
for (provider in providers) {
|
||||
for (char in provider.chars.values) {
|
||||
char.calculateUV(provider.width, atlasWidthSinglePixel, atlasHeightSinglePixel)
|
||||
}
|
||||
}
|
||||
return textureArray
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,25 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font
|
||||
|
||||
class FontChar(val atlasPage: Int, val row: Int, val column: Int) {
|
||||
val width = 16
|
||||
import glm_.vec2.Vec2
|
||||
|
||||
data class FontChar(
|
||||
val atlasPage: Int,
|
||||
val atlasTextureIndex: Int,
|
||||
val row: Int,
|
||||
val column: Int,
|
||||
val startPixel: Int,
|
||||
val endPixel: Int,
|
||||
val height: Int,
|
||||
) {
|
||||
lateinit var uvLeftUp: Vec2
|
||||
lateinit var uvRightUp: Vec2
|
||||
lateinit var uvRightDown: Vec2
|
||||
lateinit var uvLeftDown: Vec2
|
||||
|
||||
fun calculateUV(letterWidth: Int, atlasWidthSinglePixel: Float, atlasHeightSinglePixel: Float) {
|
||||
uvLeftUp = Vec2(atlasWidthSinglePixel * (letterWidth * column + startPixel), atlasHeightSinglePixel * (height * row))
|
||||
uvRightUp = Vec2(atlasWidthSinglePixel * (letterWidth * column + endPixel), atlasHeightSinglePixel * (height * row))
|
||||
uvRightDown = Vec2(atlasWidthSinglePixel * (letterWidth * column + endPixel), atlasHeightSinglePixel * (height * (row + 1)))
|
||||
uvLeftDown = Vec2(atlasWidthSinglePixel * (letterWidth * column + startPixel), atlasHeightSinglePixel * (height * (row + 1)))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font
|
||||
|
||||
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.gui.rendering.textures.Texture
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
|
||||
import java.io.InputStream
|
||||
|
||||
object FontLoader {
|
||||
private const val FONT_ATLAS_SIZE = 16
|
||||
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)
|
||||
|
||||
private fun getCharArray(data: JsonArray): List<Char> {
|
||||
val ret: MutableList<Char> = mutableListOf()
|
||||
for (string in data) {
|
||||
ret.addAll(string.asString.toCharArray().toList())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun loadBitmapFontProvider(atlasPath: ModIdentifier, height: Int? = 8, ascent: Int, chars: List<Char>, assetsManager: AssetsManager, atlasOffset: Int): FontProvider {
|
||||
val width = if (ascent == 7) {
|
||||
8
|
||||
} else {
|
||||
9
|
||||
}
|
||||
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
|
||||
for ((i, char) in chars.withIndex()) {
|
||||
val fontChar = FontChar(0, atlasOffset, i / FONT_ATLAS_SIZE, i % FONT_ATLAS_SIZE, 0, width, height)
|
||||
provider.chars[char] = fontChar
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
|
||||
private fun loadUnicodeFontProvider(template: ModIdentifier, sizes: InputStream, assetsManager: AssetsManager, atlasOffset: Int): FontProvider {
|
||||
val provider = FontProvider(16)
|
||||
var i = 0
|
||||
lateinit var currentAtlasTexture: Texture
|
||||
while (sizes.available() > 0) {
|
||||
if (i % 256 == 0) {
|
||||
currentAtlasTexture = if (MISSING_UNICODE_PAGES.contains(i / 256)) {
|
||||
// ToDo: Why is this texture missing in minecraft?
|
||||
Texture(TextureArray.DEBUG_TEXTURE.name, i / 256)
|
||||
} else {
|
||||
// new page (texture)
|
||||
Texture((template.mod + "/textures/" + template.identifier).format("%02x".format(i / 256)), atlasOffset + (i / 256))
|
||||
}
|
||||
currentAtlasTexture.load(assetsManager)
|
||||
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)
|
||||
provider.chars[i.toChar()] = fontChar
|
||||
i++
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
fun loadFontProvider(data: JsonObject, assetsManager: AssetsManager, atlasTextureOffset: Int): FontProvider {
|
||||
return when (data["type"].asString) {
|
||||
"bitmap" -> {
|
||||
loadBitmapFontProvider(ModIdentifier(data["file"].asString), data["height"]?.asInt, data["ascent"].asInt, getCharArray(data["chars"].asJsonArray), assetsManager, atlasTextureOffset)
|
||||
}
|
||||
"legacy_unicode" -> {
|
||||
loadUnicodeFontProvider(ModIdentifier(data["template"].asString), assetsManager.readAssetAsStream(ModIdentifier(data["sizes"].asString)), assetsManager, atlasTextureOffset)
|
||||
}
|
||||
else -> throw IllegalArgumentException("${data["type"]} is not a valid font provider type!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun loadFontProviders(assetsManager: AssetsManager): List<FontProvider> {
|
||||
val ret: MutableList<FontProvider> = mutableListOf()
|
||||
var atlasTextureOffset = 0
|
||||
for (providerElement in assetsManager.readJsonAsset("minecraft/font/default.json").asJsonObject["providers"].asJsonArray) {
|
||||
val provider = loadFontProvider(providerElement.asJsonObject, assetsManager, atlasTextureOffset)
|
||||
atlasTextureOffset += provider.atlasTextures.size
|
||||
ret.add(provider)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font
|
||||
|
||||
import de.bixilon.minosoft.gui.rendering.textures.Texture
|
||||
|
||||
class FontProvider(val width: Int) {
|
||||
val chars: MutableMap<Char, FontChar> = mutableMapOf()
|
||||
val atlasTextures: MutableList<Texture> = mutableListOf()
|
||||
}
|
@ -1,9 +1,40 @@
|
||||
package de.bixilon.minosoft.gui.rendering.textures
|
||||
|
||||
import de.bixilon.minosoft.data.assets.AssetsManager
|
||||
import de.matthiasmann.twl.utils.PNGDecoder
|
||||
import org.lwjgl.BufferUtils
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
|
||||
class Texture(
|
||||
val name: String,
|
||||
val id: Int,
|
||||
) {
|
||||
var width: Int = 0
|
||||
var height: Int = 0
|
||||
var isTransparent: Boolean = false
|
||||
lateinit var buffer: ByteBuffer
|
||||
var loaded = false
|
||||
|
||||
fun load(assetsManager: AssetsManager) {
|
||||
if (loaded) {
|
||||
return
|
||||
}
|
||||
val texturePath = if (name.endsWith(".png")) {
|
||||
name
|
||||
} else {
|
||||
"minecraft/textures/${name}.png"
|
||||
}
|
||||
val decoder = PNGDecoder(assetsManager.readAssetAsStream(texturePath))
|
||||
buffer = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
|
||||
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.flip()
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
|
||||
import org.lwjgl.opengl.GL30.glGenerateMipmap
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class TextureArray(private val assetsManager: AssetsManager, private val textures: List<Texture>, private val size: Int = 16) {
|
||||
class TextureArray(private val textures: List<Texture>, val maxWidth: Int, val maxHeight: Int) {
|
||||
var textureId = 0
|
||||
|
||||
fun load(): Int {
|
||||
@ -21,12 +21,10 @@ class TextureArray(private val assetsManager: AssetsManager, private val texture
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
||||
|
||||
|
||||
// load and generate the texture
|
||||
val textureMap: Map<Texture, ByteBuffer> = TextureLoader.loadTextureArray(assetsManager, textures)
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, size, size, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, maxWidth, maxHeight, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
|
||||
|
||||
for ((key, value) in textureMap) {
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, key.id, size, size, 1, GL_RGBA, GL_UNSIGNED_BYTE, value)
|
||||
for (texture in textures) {
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, texture.id, texture.width, texture.height, 1, GL_RGBA, GL_UNSIGNED_BYTE, texture.buffer)
|
||||
}
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY)
|
||||
return textureId
|
||||
@ -40,5 +38,29 @@ class TextureArray(private val assetsManager: AssetsManager, private val texture
|
||||
|
||||
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
|
||||
var calculatedMaxHeight = 0
|
||||
for (texture in textures) {
|
||||
if (!texture.loaded) {
|
||||
texture.load(assetsManager!!)
|
||||
}
|
||||
if (texture.width > calculatedMaxWidth) {
|
||||
calculatedMaxWidth = texture.width
|
||||
}
|
||||
if (texture.height > calculatedMaxHeight) {
|
||||
calculatedMaxHeight = texture.height
|
||||
}
|
||||
}
|
||||
if (maxWidth != -1) {
|
||||
calculatedMaxWidth = maxWidth
|
||||
}
|
||||
if (maxHeight != -1) {
|
||||
calculatedMaxHeight = maxWidth
|
||||
}
|
||||
return TextureArray(textures, calculatedMaxWidth, calculatedMaxHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package de.bixilon.minosoft.gui.rendering.textures
|
||||
|
||||
import de.bixilon.minosoft.data.assets.AssetsManager
|
||||
import de.matthiasmann.twl.utils.PNGDecoder
|
||||
import org.lwjgl.BufferUtils
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
|
||||
object TextureLoader {
|
||||
private val TRANSPARENT_TEXTURES = listOf("block/glass")
|
||||
fun loadTextureArray(assetsManager: AssetsManager, textures: List<Texture>): Map<Texture, ByteBuffer> {
|
||||
val result: MutableMap<Texture, ByteBuffer> = mutableMapOf()
|
||||
for (texture in textures) {
|
||||
result[texture] = loadTexture(assetsManager, texture)
|
||||
}
|
||||
return result.toMap()
|
||||
}
|
||||
|
||||
private fun loadTexture(assetsManager: AssetsManager, texture: Texture): ByteBuffer {
|
||||
val decoder = PNGDecoder(assetsManager.readAssetAsStream("minecraft/textures/${texture.name}.png"))
|
||||
val buffer = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
|
||||
decoder.decode(buffer, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
|
||||
buffer.flip()
|
||||
if (TRANSPARENT_TEXTURES.contains(texture.name)) { // ToDo: this should not be hardcoded!
|
||||
texture.isTransparent = true
|
||||
}
|
||||
buffer.flip()
|
||||
return buffer
|
||||
}
|
||||
}
|
@ -8,12 +8,13 @@ uniform sampler2DArray texureArray;
|
||||
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(texureArray, passTextureCoordinates);
|
||||
if (texColor.a == 0) {
|
||||
discard;
|
||||
vec4 textureColor = texture(texureArray, passTextureCoordinates);
|
||||
if (textureColor.a == 0) {
|
||||
textureColor.a = 0.4f;
|
||||
//discard;
|
||||
}
|
||||
texColor.r = 0.8f;
|
||||
texColor.g = 0.8f;
|
||||
texColor.b = 0.0f;
|
||||
outColor = texColor;
|
||||
textureColor.r = 0.8f;
|
||||
textureColor.g = 0.8f;
|
||||
textureColor.b = 0.0f;
|
||||
outColor = textureColor;
|
||||
}
|
||||
|
@ -5,9 +5,8 @@ layout (location = 2) in float textureLayer;
|
||||
|
||||
out vec3 passTextureCoordinates;
|
||||
|
||||
uniform float atlasSize;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(inPosition, 0.0f, 1.0f);
|
||||
passTextureCoordinates = vec3(textureIndex / atlasSize, textureLayer);
|
||||
passTextureCoordinates = vec3(textureIndex, textureLayer);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user