load dynamic textures

This commit is contained in:
Bixilon 2022-05-02 20:00:19 +02:00
parent 888db361d9
commit e2df230da4
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 104 additions and 31 deletions

View File

@ -18,6 +18,7 @@ import de.bixilon.kutil.url.URLUtil.checkWeb
import de.bixilon.minosoft.assets.util.FileAssetsUtil
import de.bixilon.minosoft.assets.util.FileUtil
import java.net.URL
import java.util.*
open class PlayerTexture(
val url: URL,
@ -65,5 +66,9 @@ open class PlayerTexture(
}
return false
}
fun UUID.isSteve(): Boolean {
return hashCode() % 2 == 0
}
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
* Copyright (C) 2020-2022 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.
*

View File

@ -150,7 +150,8 @@ class RenderWindow(
val initLatch = CountUpAndDownLatch(1, latch)
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Generating font and gathering textures (${stopwatch.labTime()})..." }
textureManager.dynamicTextures.load(initLatch)
textureManager.loadDefaultTextures(connection.assetsManager)
textureManager.loadDefaultSkins(connection)
textureManager.loadDefaultTextures()
font = FontLoader.load(this, initLatch)

View File

@ -34,8 +34,11 @@ open class DynamicImageElement(
size: Vec2i = Vec2i.EMPTY,
tint: RGBColor = ChatColors.WHITE,
) : Element(guiRenderer, GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX * 6) {
var texture: DynamicTexture? = texture
set(value) {
field?.usages?.decrementAndGet()
value?.usages?.incrementAndGet()
field = value
cacheUpToDate = false
}
@ -71,6 +74,7 @@ open class DynamicImageElement(
init {
this.size = size
texture?.usages?.incrementAndGet()
}
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {

View File

@ -202,7 +202,7 @@ class TabListElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedE
fun update(uuid: UUID) {
val item = guiRenderer.renderWindow.connection.tabList.tabListItemsByUUID[uuid] ?: return
val entry = entries.getOrPut(uuid) { TabListEntryElement(guiRenderer, this, item, 0) }
val entry = entries.getOrPut(uuid) { TabListEntryElement(guiRenderer, this, uuid, item, 0) }
lock.lock()
entry.silentApply()
lock.unlock()

View File

@ -33,10 +33,12 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.util.KUtil.nullCompare
import java.lang.Integer.max
import java.util.*
class TabListEntryElement(
guiRenderer: GUIRenderer,
val tabList: TabListElement,
uuid: UUID,
val item: TabListItem,
width: Int,
) : Element(guiRenderer), Pollable, Comparable<TabListEntryElement> {
@ -47,7 +49,7 @@ class TabListEntryElement(
private val background: ColorElement
private val skinElement = DynamicImageElement(guiRenderer, guiRenderer.renderWindow.textureManager.alexTexture, uvStart = Vec2(0.125), uvEnd = Vec2(0.25), size = Vec2i(512, 512))
private val skinElement = DynamicImageElement(guiRenderer, renderWindow.textureManager.getSkin(uuid, item.properties.textures), uvStart = Vec2(0.125), uvEnd = Vec2(0.25), size = Vec2i(8, 8))
// private val skinElement = ImageElement(guiRenderer, guiRenderer.renderWindow.textureManager.steveTexture, uvStart = Vec2(0.125), uvEnd = Vec2(0.25), size = Vec2i(512, 512))
private val nameElement = TextElement(guiRenderer, "", background = false, parent = this)
@ -82,9 +84,8 @@ class TabListEntryElement(
}
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
skinElement
background.render(offset, consumer, options)
skinElement.render(offset, consumer, options)
skinElement.render(offset + Vec2i(PADDING, PADDING), consumer, options)
nameElement.render(offset + Vec2i(skinElement.size.x + PADDING, PADDING), consumer, options)
pingElement.render(offset + Vec2i(HorizontalAlignments.RIGHT.getOffset(maxSize.x, pingElement.size.x + PADDING), PADDING), consumer, options)
}

View File

@ -16,7 +16,8 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.uuid.UUIDUtil.toUUID
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.player.properties.textures.PlayerTexture.Companion.isSteve
import de.bixilon.minosoft.data.player.properties.textures.PlayerTextures
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLikeTexture
@ -25,7 +26,9 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicText
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import java.util.*
abstract class TextureManager {
abstract val staticTextures: StaticTextureArray
@ -39,21 +42,31 @@ abstract class TextureManager {
private set
lateinit var alexTexture: DynamicTexture
private set
lateinit var skin: DynamicTexture
private set
fun loadDefaultTextures(assetsManager: AssetsManager) {
fun loadDefaultTextures() {
if (this::debugTexture.isInitialized) {
throw IllegalStateException("Already initialized!")
}
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
// steveTexture = staticTextures.createTexture("minecraft:entity/steve".toResourceLocation().texture())
whiteTexture = TextureLikeTexture(texture = staticTextures.createTexture(ResourceLocation("minosoft:textures/white.png")), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
loadDefaultSkins(assetsManager)
}
private fun loadDefaultSkins(assetsManager: AssetsManager) {
fun loadDefaultSkins(connection: PlayConnection) {
// ToDo: For testing purposes only, they will be moved to static textures
steveTexture = dynamicTextures.push("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture().second }
alexTexture = dynamicTextures.push("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture().second }
steveTexture = dynamicTextures.pushBuffer("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { connection.assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture().second }
alexTexture = dynamicTextures.pushBuffer("3780a46b-a725-4b22-8366-01056c698386".toUUID()) { connection.assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture().second }
skin = getSkin(connection.player.uuid ?: UUID.randomUUID(), connection.account.properties?.textures)
}
fun getSkin(uuid: UUID, properties: PlayerTextures?): DynamicTexture {
properties?.skin?.let { return dynamicTextures.pushRawArray(uuid) { it.read() } }
if (uuid.isSteve()) {
return steveTexture
}
return alexTexture
}
}

View File

@ -14,13 +14,18 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureArray
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.readTexture
import java.io.ByteArrayInputStream
import java.nio.ByteBuffer
import java.util.*
interface DynamicTextureArray : TextureArray {
val size: Int
fun push(identifier: UUID, data: () -> ByteBuffer): DynamicTexture
fun remove(texture: DynamicTexture)
fun remove(uuid: UUID)
fun pushBuffer(identifier: UUID, data: () -> ByteBuffer): DynamicTexture
fun pushArray(identifier: UUID, data: () -> ByteArray): DynamicTexture
fun pushRawArray(identifier: UUID, data: () -> ByteArray): DynamicTexture {
return pushBuffer(identifier) { ByteArrayInputStream(data()).readTexture().second }
}
}

View File

@ -19,7 +19,15 @@ import java.util.concurrent.atomic.AtomicInteger
class OpenGLDynamicTexture(
override val uuid: UUID,
override val shaderId: Int,
shaderId: Int,
) : DynamicTexture {
override val usages = AtomicInteger()
override val shaderId: Int = shaderId
get() {
if (usages.get() == 0) {
throw IllegalStateException("Texture was eventually garbage collected")
}
return field
}
}

View File

@ -32,33 +32,42 @@ import java.util.*
class OpenGLDynamicTextureArray(
val renderWindow: RenderWindow,
val index: Int = 7,
initialSize: Int = 32,
val resolution: Int,
) : DynamicTextureArray {
private val textures: Array<OpenGLDynamicTexture?> = arrayOfNulls(2)
private val textures: Array<OpenGLDynamicTexture?> = arrayOfNulls(initialSize)
private var textureId = -1
@Deprecated("temp")
private var lastTextureId = 0
override val size: Int
get() = 2
get() {
var size = 0
for (texture in textures) {
if (texture == null) {
continue
}
size++
}
return size
}
override fun remove(uuid: UUID) {
TODO("Not yet implemented")
override fun pushArray(identifier: UUID, data: () -> ByteArray): DynamicTexture {
return pushBuffer(identifier) { ByteBuffer.wrap(data()) }
}
override fun remove(texture: DynamicTexture) {
TODO("Not yet implemented")
}
override fun push(identifier: UUID, data: () -> ByteBuffer): OpenGLDynamicTexture {
override fun pushBuffer(identifier: UUID, data: () -> ByteBuffer): OpenGLDynamicTexture {
check(textureId >= 0) { "Dynamic texture array not yet initialized!" }
cleanup()
for (texture in textures) {
if (texture?.uuid == identifier) {
return texture
}
}
val bytes = data()
check(bytes.limit() == resolution * resolution * 4) { "Texture must have a size of ${resolution}x${resolution}" }
val mipmaps = OpenGLTextureUtil.generateMipMaps(bytes, Vec2i(resolution, resolution))
val index = lastTextureId++
val index = getNextIndex()
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
@ -94,4 +103,31 @@ class OpenGLDynamicTextureArray(
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
shader.setTexture("$name[$index]", index)
}
private fun getNextIndex(): Int {
for ((index, texture) in textures.withIndex()) {
if (texture == null) {
return index
}
}
val nextIndex = textures.size
grow()
return nextIndex
}
private fun grow() {
TODO()
}
private fun cleanup() {
for ((index, texture) in textures.withIndex()) {
if (texture == null) {
continue
}
if (texture.usages.get() > 0) {
continue
}
textures[index] = null
}
}
}

View File

@ -186,7 +186,7 @@ class NettyClient(
if (RunConfiguration.DISABLE_EROS || connection !is StatusConnection) {
Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.WARN) { cause }
}
if (cause !is NetworkException || cause is CriticalNetworkException) {
if (cause !is NetworkException || cause is CriticalNetworkException || state == ProtocolStates.LOGIN) {
connection.error = cause
if (reportErrors && !ErosCrashReport.alreadyCrashed) {
cause.report()