mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 19:05:02 -04:00
refactor dynamic textures
Kinda broke, ik
This commit is contained in:
parent
61f54dacb9
commit
45bcfde492
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 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.
|
||||
*
|
||||
@ -13,34 +13,9 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.dummy.texture
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
object DummyDynamicTexture : DynamicTexture {
|
||||
override val uuid: UUID = UUID(0L, 0L)
|
||||
override val size: Vec2i = Vec2i(1, 1)
|
||||
override val usages: AtomicInteger = AtomicInteger(0)
|
||||
override val state: DynamicTextureState = DynamicTextureState.LOADED
|
||||
|
||||
override fun addListener(callback: DynamicStateChangeCallback) {
|
||||
}
|
||||
|
||||
override fun removeListener(callback: DynamicStateChangeCallback) {
|
||||
}
|
||||
|
||||
override fun transformUV(end: Vec2?): Vec2 {
|
||||
return end ?: Vec2(1.0f)
|
||||
}
|
||||
|
||||
override fun transformUV(end: FloatArray?): FloatArray {
|
||||
return end ?: floatArrayOf(1.0f, 1.0f)
|
||||
|
||||
}
|
||||
|
||||
object DummyDynamicTexture : DynamicTexture(UUID(0L, 0L)) {
|
||||
override val shaderId: Int = 0
|
||||
}
|
||||
|
@ -13,26 +13,17 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.dummy.texture
|
||||
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
|
||||
import java.util.*
|
||||
|
||||
class DummyDynamicTextureArray : DynamicTextureArray {
|
||||
override val size: Int = 0
|
||||
class DummyDynamicTextureArray(context: RenderContext) : DynamicTextureArray(context, 1) {
|
||||
override fun createTexture(identifier: Any, index: Int) = DummyDynamicTexture
|
||||
|
||||
override fun pushBuffer(identifier: UUID, force: Boolean, data: () -> TextureData): DynamicTexture {
|
||||
return DummyDynamicTexture
|
||||
}
|
||||
|
||||
override fun upload(latch: AbstractLatch?) {
|
||||
}
|
||||
|
||||
override fun activate() {
|
||||
}
|
||||
|
||||
override fun use(shader: NativeShader, name: String) {
|
||||
}
|
||||
override fun unload() = Unit
|
||||
override fun unsafeUse(shader: NativeShader, name: String) = Unit
|
||||
override fun activate() = Unit
|
||||
override fun upload() = Unit
|
||||
override fun upload(index: Int, texture: DynamicTexture) = Unit
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class RenderLoop(
|
||||
var paused = false
|
||||
context::state.observe(this) {
|
||||
paused = if (paused) {
|
||||
context.queue.clear()
|
||||
// context.queue.clear() // TODO: That might cause bugs and memory leaks
|
||||
false
|
||||
} else {
|
||||
it == RenderingStates.PAUSED
|
||||
|
@ -18,6 +18,7 @@ import de.bixilon.kutil.collections.iterator.async.ConcurrentIterator
|
||||
import de.bixilon.kutil.collections.map.LockMap
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPool
|
||||
import de.bixilon.kutil.exception.ExceptionUtil.ignoreAll
|
||||
import de.bixilon.kutil.observer.set.SetObserver.Companion.observeSet
|
||||
import de.bixilon.minosoft.data.entities.entities.Entity
|
||||
import de.bixilon.minosoft.gui.rendering.entities.factory.EntityModelFactory
|
||||
@ -46,15 +47,17 @@ class EntityRendererManager(val renderer: EntitiesRenderer) : Iterable<EntityRen
|
||||
}
|
||||
}
|
||||
|
||||
private fun Entity.createRenderer() = when {
|
||||
this is EntityModelFactory<*> -> create(this@EntityRendererManager.renderer)
|
||||
type.modelFactory != null -> type.modelFactory?.nullCast<RegisteredEntityModelFactory<Entity>>()?.create(this@EntityRendererManager.renderer, this)
|
||||
else -> DummyEntityRenderer(this@EntityRendererManager.renderer, this)
|
||||
}
|
||||
|
||||
operator fun plusAssign(entity: Entity) = add(entity)
|
||||
fun add(entity: Entity) {
|
||||
try {
|
||||
renderers.lock.lock()
|
||||
val renderer = when {
|
||||
entity is EntityModelFactory<*> -> entity.create(this.renderer)
|
||||
entity.type.modelFactory != null -> entity.type.modelFactory?.nullCast<RegisteredEntityModelFactory<Entity>>()?.create(this.renderer, entity)
|
||||
else -> DummyEntityRenderer(this.renderer, entity)
|
||||
} ?: return
|
||||
val renderer = ignoreAll { entity.createRenderer() } ?: return
|
||||
entity.renderer?.let { onReplace(it) }
|
||||
this.renderers.unsafe.put(entity, renderer)?.let { onReplace(it) }
|
||||
} finally {
|
||||
|
@ -39,9 +39,7 @@ open class DynamicImageElement(
|
||||
|
||||
var texture: DynamicTexture? = null
|
||||
set(value) {
|
||||
field?.usages?.decrementAndGet()
|
||||
field?.removeListener(this)
|
||||
value?.usages?.incrementAndGet()
|
||||
value?.addListener(this)
|
||||
field = value
|
||||
cacheUpToDate = false
|
||||
@ -97,11 +95,7 @@ open class DynamicImageElement(
|
||||
override fun forceSilentApply() = Unit
|
||||
override fun silentApply(): Boolean = false
|
||||
|
||||
protected fun finalize() {
|
||||
texture?.usages?.decrementAndGet()
|
||||
}
|
||||
|
||||
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
|
||||
override fun onDynamicTextureChange(texture: DynamicTexture) {
|
||||
if (texture === this.texture) {
|
||||
forceApply()
|
||||
}
|
||||
|
@ -251,6 +251,10 @@ class DebugHUDElement(guiRenderer: GUIRenderer) : Element(guiRenderer), Layouted
|
||||
|
||||
layout += LineSpacerElement(guiRenderer)
|
||||
|
||||
layout += AutoTextElement(guiRenderer, 20, properties = RIGHT) { "Dynamic textures ${context.textures.dynamicTextures.size.format()}/${context.textures.dynamicTextures.capacity.format()}" }
|
||||
|
||||
layout += LineSpacerElement(guiRenderer)
|
||||
|
||||
context.connection.camera.target.apply {
|
||||
layout += AutoTextElement(guiRenderer, 1, properties = RIGHT) {
|
||||
// ToDo: Tags
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 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.
|
||||
*
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
|
||||
|
||||
interface DynamicStateChangeCallback {
|
||||
fun interface DynamicStateChangeCallback {
|
||||
|
||||
fun onStateChange(texture: DynamicTexture, state: DynamicTextureState)
|
||||
fun onDynamicTextureChange(texture: DynamicTexture)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 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.
|
||||
*
|
||||
@ -13,22 +13,55 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.exception.ExceptionUtil.ignoreAll
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.MipmapTextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
interface DynamicTexture : ShaderTexture {
|
||||
val uuid: UUID
|
||||
val size: Vec2i
|
||||
val usages: AtomicInteger
|
||||
abstract class DynamicTexture(
|
||||
val identifier: Any,
|
||||
) : ShaderTexture {
|
||||
private val callbacks: MutableSet<DynamicStateChangeCallback> = mutableSetOf()
|
||||
private val lock = SimpleLock()
|
||||
|
||||
val state: DynamicTextureState
|
||||
var data: MipmapTextureData? = null
|
||||
var state: DynamicTextureState = DynamicTextureState.WAITING
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
lock.acquire()
|
||||
for (callback in callbacks) {
|
||||
ignoreAll { callback.onDynamicTextureChange(this) }
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return identifier.toString()
|
||||
}
|
||||
|
||||
override fun transformUV(end: Vec2?): Vec2 {
|
||||
return end ?: Vec2(1.0f) // TODO: memory
|
||||
}
|
||||
|
||||
override fun transformUV(end: FloatArray?): FloatArray {
|
||||
return end ?: floatArrayOf(1.0f, 1.0f) // TODO: memory
|
||||
}
|
||||
|
||||
fun addListener(callback: DynamicStateChangeCallback)
|
||||
operator fun plusAssign(callback: DynamicStateChangeCallback) = addListener(callback)
|
||||
fun addListener(callback: DynamicStateChangeCallback) {
|
||||
lock.lock()
|
||||
callbacks += callback
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
operator fun minusAssign(callback: DynamicStateChangeCallback) = removeListener(callback)
|
||||
fun removeListener(callback: DynamicStateChangeCallback)
|
||||
fun removeListener(callback: DynamicStateChangeCallback) {
|
||||
lock.lock()
|
||||
callbacks -= callback
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 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.
|
||||
*
|
||||
@ -13,18 +13,151 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
|
||||
|
||||
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.runnable.ForcePooledRunnable
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArray
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.MipmapTextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.util.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
abstract class DynamicTextureArray(
|
||||
val context: RenderContext,
|
||||
val initialSize: Int,
|
||||
) : TextureArray {
|
||||
protected var textures: Array<WeakReference<DynamicTexture>?> = arrayOfNulls(initialSize)
|
||||
protected val shaders: MutableSet<NativeShader> = mutableSetOf()
|
||||
private val lock = ThreadLock()
|
||||
private var reload = false
|
||||
|
||||
val capacity get() = textures.size
|
||||
|
||||
interface DynamicTextureArray : TextureArray {
|
||||
val size: Int
|
||||
get() {
|
||||
var size = 0
|
||||
for (texture in textures) {
|
||||
if (texture == null) {
|
||||
continue
|
||||
}
|
||||
size++
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun pushBuffer(identifier: UUID, force: Boolean = false, data: () -> TextureData): DynamicTexture
|
||||
|
||||
fun pushRawArray(identifier: UUID, force: Boolean = false, data: () -> ByteArray): DynamicTexture {
|
||||
return pushBuffer(identifier, force) { ByteArrayInputStream(data()).readTexture() }
|
||||
operator fun get(identifier: Any): DynamicTexture? {
|
||||
lock.acquire()
|
||||
val texture = unsafeGet(identifier)
|
||||
lock.release()
|
||||
return texture
|
||||
}
|
||||
|
||||
fun unsafeGet(identifier: Any): DynamicTexture? {
|
||||
for (reference in textures) {
|
||||
val texture = reference?.get() ?: continue
|
||||
if (texture.identifier == identifier) {
|
||||
return texture
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun DynamicTexture.load(index: Int, creator: () -> TextureData) {
|
||||
val data = creator.invoke()
|
||||
|
||||
this.data = MipmapTextureData(data.size, data.buffer)
|
||||
context.queue += { upload(index, this) }
|
||||
}
|
||||
|
||||
fun pushRaw(identifier: Any, async: Boolean = true, creator: () -> ByteArray): DynamicTexture {
|
||||
return push(identifier, async) { ByteArrayInputStream(creator()).readTexture() }
|
||||
}
|
||||
|
||||
fun push(identifier: Any, async: Boolean = true, creator: () -> TextureData): DynamicTexture {
|
||||
lock.lock()
|
||||
cleanup()
|
||||
unsafeGet(identifier)?.let { lock.unlock(); return it }
|
||||
|
||||
val index = getNextIndex()
|
||||
|
||||
val texture = createTexture(identifier, index)
|
||||
textures[index] = WeakReference(texture)
|
||||
texture.state = DynamicTextureState.LOADING
|
||||
|
||||
if (async) {
|
||||
DefaultThreadPool += ForcePooledRunnable { texture.load(index, creator) }
|
||||
} else {
|
||||
texture.load(index, creator)
|
||||
}
|
||||
|
||||
lock.unlock()
|
||||
return texture
|
||||
}
|
||||
|
||||
|
||||
override fun use(shader: NativeShader, name: String) {
|
||||
shaders += shader
|
||||
unsafeUse(shader, name)
|
||||
}
|
||||
|
||||
|
||||
private fun getNextIndex(): Int {
|
||||
lock.lock()
|
||||
for ((index, texture) in textures.withIndex()) {
|
||||
if (texture?.get() == null) {
|
||||
lock.unlock()
|
||||
return index
|
||||
}
|
||||
}
|
||||
val nextIndex = textures.size
|
||||
grow()
|
||||
lock.unlock()
|
||||
return nextIndex
|
||||
}
|
||||
|
||||
override fun upload(latch: AbstractLatch?) = upload()
|
||||
|
||||
private fun grow() {
|
||||
lock.lock()
|
||||
val textures: Array<WeakReference<DynamicTexture>?> = arrayOfNulls(textures.size + initialSize)
|
||||
for ((index, texture) in this.textures.withIndex()) {
|
||||
if (texture == null) continue
|
||||
textures[index] = texture
|
||||
}
|
||||
this.textures = textures
|
||||
|
||||
this.reload = true
|
||||
context.queue += { reload() }
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
private fun cleanup() {
|
||||
lock.lock()
|
||||
for ((index, reference) in textures.withIndex()) {
|
||||
if (reference == null) continue
|
||||
val texture = reference.get()
|
||||
texture?.state = DynamicTextureState.UNLOADED
|
||||
textures[index] = null
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
private fun reload() {
|
||||
unload()
|
||||
upload()
|
||||
this.reload = false
|
||||
}
|
||||
|
||||
|
||||
protected abstract fun upload(index: Int, texture: DynamicTexture)
|
||||
protected abstract fun upload()
|
||||
protected abstract fun unload()
|
||||
protected abstract fun unsafeUse(shader: NativeShader, name: String = ShaderUniforms.TEXTURES)
|
||||
protected abstract fun createTexture(identifier: Any, index: Int): DynamicTexture
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class SkinManager(private val textureManager: TextureManager) {
|
||||
fun initialize(account: Account, assets: AssetsManager) {
|
||||
default = DefaultSkinProvider(this.textureManager.dynamicTextures, assets)
|
||||
default.initialize()
|
||||
skin = getSkin(account.uuid, account.properties, fetch = true, force = true)?.apply { texture.usages.incrementAndGet() }
|
||||
skin = getSkin(account.uuid, account.properties, fetch = true, force = true)
|
||||
}
|
||||
|
||||
private fun getAccountProperties(uuid: UUID): PlayerProperties? {
|
||||
@ -51,7 +51,7 @@ class SkinManager(private val textureManager: TextureManager) {
|
||||
|
||||
private fun getSkin(uuid: UUID, properties: PlayerProperties?, force: Boolean = false): PlayerSkin? {
|
||||
val texture = properties?.textures?.skin ?: return default[uuid]
|
||||
return PlayerSkin(textureManager.dynamicTextures.pushRawArray(uuid, force) { texture.read() }, texture.metadata.model)
|
||||
return PlayerSkin(textureManager.dynamicTextures.pushRaw(uuid, force) { texture.read() }, texture.metadata.model)
|
||||
}
|
||||
|
||||
fun getSkin(player: PlayerEntity, properties: PlayerProperties? = null, fetch: Boolean = true, force: Boolean = false): PlayerSkin? {
|
||||
|
@ -29,7 +29,6 @@ class DefaultSkinProvider(
|
||||
private val array: DynamicTextureArray,
|
||||
private val assets: AssetsManager,
|
||||
) {
|
||||
private var defaultId = 0
|
||||
private val slim: MutableMap<ResourceLocation, DynamicTexture> = mutableMapOf()
|
||||
private val wide: MutableMap<ResourceLocation, DynamicTexture> = mutableMapOf()
|
||||
private var fallback: PlayerSkin? = null
|
||||
@ -66,9 +65,7 @@ class DefaultSkinProvider(
|
||||
|
||||
private fun load(path: ResourceLocation): DynamicTexture? {
|
||||
val data = assets.getOrNull(path)?.readTexture() ?: return null
|
||||
val texture = array.pushBuffer(UUID(0L, defaultId++.toLong()), true) { data }
|
||||
texture.usages.incrementAndGet()
|
||||
return texture
|
||||
return array.push(path, true) { data }
|
||||
}
|
||||
|
||||
private fun ResourceLocation.skin(prefix: String): ResourceLocation {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 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.
|
||||
*
|
||||
@ -13,75 +13,15 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class OpenGLDynamicTexture(
|
||||
override val uuid: UUID,
|
||||
shaderId: Int,
|
||||
) : DynamicTexture {
|
||||
var data: Array<ByteBuffer>? = null
|
||||
private val callbacks: MutableSet<DynamicStateChangeCallback> = mutableSetOf()
|
||||
private val callbackLock = SimpleLock()
|
||||
override val usages = AtomicInteger()
|
||||
override var state: DynamicTextureState = DynamicTextureState.WAITING
|
||||
set(value) {
|
||||
if (field == value) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
callbackLock.acquire()
|
||||
for (callback in callbacks) {
|
||||
callback.onStateChange(this, value)
|
||||
}
|
||||
callbackLock.release()
|
||||
}
|
||||
override var size: Vec2i = Vec2i.EMPTY
|
||||
class OpenGLDynamicTexture(identifier: Any, shaderId: Int) : DynamicTexture(identifier) {
|
||||
|
||||
override var shaderId: Int = shaderId
|
||||
get() {
|
||||
if (state == DynamicTextureState.UNLOADED) {
|
||||
throw IllegalStateException("Texture was garbage collected!")
|
||||
}
|
||||
if (usages.get() == 0) {
|
||||
throw IllegalStateException("Texture could be garbage collected every time!")
|
||||
}
|
||||
if (state == DynamicTextureState.LOADING) {
|
||||
throw IllegalStateException("Texture was not loaded yet!")
|
||||
}
|
||||
if (state == DynamicTextureState.UNLOADED) throw IllegalStateException("Texture was unloaded!")
|
||||
if (state == DynamicTextureState.LOADING) throw IllegalStateException("Texture was not loaded yet!")
|
||||
return field
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return uuid.toString()
|
||||
}
|
||||
|
||||
override fun transformUV(end: Vec2?): Vec2 {
|
||||
return end ?: Vec2(1.0f)
|
||||
}
|
||||
|
||||
override fun transformUV(end: FloatArray?): FloatArray {
|
||||
return end ?: floatArrayOf(1.0f, 1.0f)
|
||||
}
|
||||
|
||||
override fun addListener(callback: DynamicStateChangeCallback) {
|
||||
callbackLock.lock()
|
||||
callbacks += callback
|
||||
callbackLock.unlock()
|
||||
}
|
||||
|
||||
override fun removeListener(callback: DynamicStateChangeCallback) {
|
||||
callbackLock.lock()
|
||||
callbacks -= callback
|
||||
callbackLock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,107 +13,83 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic
|
||||
|
||||
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.MemoryLeakException
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import org.lwjgl.opengl.GL11.*
|
||||
import org.lwjgl.opengl.GL12.glTexImage3D
|
||||
import org.lwjgl.opengl.GL12.glTexSubImage3D
|
||||
import org.lwjgl.opengl.GL13.GL_TEXTURE0
|
||||
import org.lwjgl.opengl.GL13.glActiveTexture
|
||||
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
|
||||
import java.lang.ref.WeakReference
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class OpenGLDynamicTextureArray(
|
||||
val context: RenderContext,
|
||||
context: RenderContext,
|
||||
val renderSystem: OpenGLRenderSystem,
|
||||
val index: Int = renderSystem.textureBindingIndex++,
|
||||
val initialSize: Int = 32,
|
||||
initialSize: Int = 32,
|
||||
val resolution: Int,
|
||||
) : DynamicTextureArray {
|
||||
private var textures: Array<WeakReference<OpenGLDynamicTexture>?> = arrayOfNulls(initialSize)
|
||||
private val lock = ThreadLock()
|
||||
private var textureId = -1
|
||||
var shaders: MutableSet<NativeShader> = mutableSetOf()
|
||||
) : DynamicTextureArray(context, initialSize) {
|
||||
private var handle = -1
|
||||
|
||||
override val size: Int
|
||||
get() {
|
||||
var size = 0
|
||||
for (texture in textures) {
|
||||
if (texture == null) {
|
||||
continue
|
||||
}
|
||||
size++
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
private fun load(texture: OpenGLDynamicTexture, index: Int, mipmaps: Array<ByteBuffer>) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
|
||||
|
||||
for ((level, mipmap) in mipmaps.withIndex()) {
|
||||
// glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, texture.size.x shr level, texture.size.y shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap)
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, resolution shr level, resolution shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap)
|
||||
}
|
||||
override fun upload(index: Int, texture: DynamicTexture) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handle)
|
||||
|
||||
unsafeUpload(index, texture)
|
||||
texture.state = DynamicTextureState.LOADED
|
||||
context.textures.staticTextures.activate()
|
||||
}
|
||||
|
||||
override fun pushBuffer(identifier: UUID, force: Boolean, data: () -> TextureData): OpenGLDynamicTexture {
|
||||
lock.lock()
|
||||
check(textureId >= 0) { "Dynamic texture array not yet initialized!" }
|
||||
cleanup()
|
||||
for (textureReference in textures) {
|
||||
val texture = textureReference?.get()
|
||||
if (texture?.uuid == identifier) {
|
||||
lock.unlock()
|
||||
return texture
|
||||
}
|
||||
private fun unsafeUpload(index: Int, texture: DynamicTexture) {
|
||||
val data = texture.data ?: throw IllegalArgumentException("No texture data?")
|
||||
for ((level, buffer) in data.collect().withIndex()) {
|
||||
// glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, texture.size.x shr level, texture.size.y shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, mipmap)
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, index, resolution shr level, resolution shr level, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer)
|
||||
}
|
||||
val index = getNextIndex()
|
||||
val texture = OpenGLDynamicTexture(identifier, createShaderIdentifier(index = index))
|
||||
textures[index] = WeakReference(texture)
|
||||
texture.state = DynamicTextureState.LOADING
|
||||
}
|
||||
|
||||
fun load() {
|
||||
val data = data()
|
||||
override fun upload() {
|
||||
if (handle >= 0) throw MemoryLeakException("Texture was not unloaded!")
|
||||
val textureId = OpenGLTextureUtil.createTextureArray()
|
||||
|
||||
if (data.buffer.limit() > resolution * resolution * 4 || data.buffer.limit() < resolution * 4) { // allow anything in 1..resolution for y size
|
||||
Log.log(LogMessageType.ASSETS, LogLevels.WARN) { "Dynamic texture $texture, has not a size of ${resolution}x${resolution}!" }
|
||||
}
|
||||
|
||||
val mipmaps = OpenGLTextureUtil.generateMipMaps(data.buffer, data.size)
|
||||
texture.data = mipmaps
|
||||
texture.size = data.size
|
||||
if (force) {
|
||||
load(texture, index, mipmaps) // thread check already done
|
||||
} else {
|
||||
context.queue += { load(texture, index, mipmaps) }
|
||||
}
|
||||
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
|
||||
}
|
||||
|
||||
if (force) {
|
||||
renderSystem.assertThread()
|
||||
load()
|
||||
} else {
|
||||
DefaultThreadPool += { load() }
|
||||
for ((index, textureReference) in textures.withIndex()) {
|
||||
val texture = textureReference?.get() ?: continue
|
||||
unsafeUpload(index, texture)
|
||||
}
|
||||
lock.unlock()
|
||||
return texture
|
||||
|
||||
for (shader in shaders) {
|
||||
unsafeUse(shader)
|
||||
}
|
||||
|
||||
context.textures.staticTextures.activate()
|
||||
|
||||
this.handle = textureId
|
||||
}
|
||||
|
||||
override fun activate() {
|
||||
glActiveTexture(GL_TEXTURE0 + index)
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handle)
|
||||
}
|
||||
|
||||
override fun unsafeUse(shader: NativeShader, name: String) {
|
||||
shader.use()
|
||||
activate()
|
||||
shader.setTexture("$name[$index]", index)
|
||||
}
|
||||
|
||||
override fun unload() {
|
||||
if (handle < 0) throw IllegalStateException("Not loaded!")
|
||||
glDeleteTextures(handle)
|
||||
this.handle = -1
|
||||
}
|
||||
|
||||
private fun createShaderIdentifier(array: Int = this.index, index: Int): Int {
|
||||
@ -121,96 +97,7 @@ class OpenGLDynamicTextureArray(
|
||||
return (array shl 28) or (index shl 12) or 0
|
||||
}
|
||||
|
||||
|
||||
override fun upload(latch: AbstractLatch?) {
|
||||
val textureId = OpenGLTextureUtil.createTextureArray()
|
||||
this.textureId = textureId
|
||||
|
||||
|
||||
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
|
||||
}
|
||||
|
||||
this.textureId = textureId
|
||||
}
|
||||
|
||||
override fun activate() {
|
||||
glActiveTexture(GL_TEXTURE0 + index)
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
|
||||
}
|
||||
|
||||
override fun use(shader: NativeShader, name: String) {
|
||||
shaders += shader
|
||||
_use(shader, name)
|
||||
}
|
||||
|
||||
private fun _use(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) {
|
||||
shader.use()
|
||||
|
||||
activate()
|
||||
|
||||
shader.setTexture("$name[$index]", index)
|
||||
}
|
||||
|
||||
private fun getNextIndex(): Int {
|
||||
lock.lock()
|
||||
for ((index, texture) in textures.withIndex()) {
|
||||
if (texture == null) {
|
||||
lock.unlock()
|
||||
return index
|
||||
}
|
||||
}
|
||||
val nextIndex = textures.size
|
||||
grow()
|
||||
lock.unlock()
|
||||
return nextIndex
|
||||
}
|
||||
|
||||
private fun reload() {
|
||||
lock.lock()
|
||||
glDeleteTextures(textureId)
|
||||
upload(null)
|
||||
|
||||
for ((index, textureReference) in textures.withIndex()) {
|
||||
val texture = textureReference?.get() ?: continue
|
||||
load(texture, index, texture.data ?: continue)
|
||||
}
|
||||
|
||||
for (shader in shaders) {
|
||||
_use(shader)
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
private fun grow() {
|
||||
lock.lock()
|
||||
val textures: Array<WeakReference<OpenGLDynamicTexture>?> = arrayOfNulls(textures.size + initialSize)
|
||||
for ((index, texture) in this.textures.withIndex()) {
|
||||
textures[index] = texture
|
||||
}
|
||||
this.textures = textures
|
||||
context.queue += { reload() }
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun cleanup() {
|
||||
lock.lock()
|
||||
for ((index, reference) in textures.withIndex()) {
|
||||
if (reference == null) {
|
||||
continue
|
||||
}
|
||||
val texture = reference.get()
|
||||
if (texture == null) {
|
||||
textures[index] = null
|
||||
continue
|
||||
}
|
||||
if (texture.usages.get() > 0) {
|
||||
continue
|
||||
}
|
||||
texture.state = DynamicTextureState.UNLOADED
|
||||
textures[index] = null
|
||||
}
|
||||
lock.unlock()
|
||||
override fun createTexture(identifier: Any, index: Int): DynamicTexture {
|
||||
return OpenGLDynamicTexture(identifier, createShaderIdentifier(index = index))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#version 330 core
|
||||
|
||||
out vec4 foutColor;
|
||||
|
||||
#define FOG
|
||||
|
||||
|
||||
#include "minosoft:texture"
|
||||
#include "minosoft:alpha"
|
||||
#include "minosoft:fog"
|
||||
|
||||
flat in bool finSkinLayer;
|
||||
|
||||
uniform uint uTextureIndex;
|
||||
uniform vec3 uTextureCoordinates;
|
||||
|
||||
void main() {
|
||||
foutColor = getTexture(uTextureIndex, uTextureCoordinates);
|
||||
if (finSkinLayer) {
|
||||
if (foutColor.a < 0.5) discard;
|
||||
}
|
||||
foutColor.a = 1.0f;
|
||||
set_fog();
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 vinPosition;
|
||||
layout (location = 1) in vec2 vinUV;
|
||||
layout (location = 2) in float vinTransformNormal; // transform (0x7F000), normal (0xFFF)
|
||||
|
||||
#include "minosoft:animation/header_vertex"
|
||||
#include "minosoft:skeletal/vertex"
|
||||
|
||||
|
||||
#include "minosoft:animation/buffer"
|
||||
#include "minosoft:animation/main_vertex"
|
||||
|
||||
flat out bool finSkinLayer;
|
||||
|
||||
|
||||
void main() {
|
||||
run_skeletal(floatBitsToUint(vinTransformNormal), vinPosition);
|
||||
run_animation();
|
||||
finSkinLayer = false;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user