rework some mipmapping

This commit is contained in:
Moritz Zwerger 2023-11-25 19:46:00 +01:00
parent 66846d9abd
commit 31225196ee
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
20 changed files with 162 additions and 186 deletions

View File

@ -37,7 +37,7 @@ object BakedModelTestUtil {
private val connection by lazy { createConnection() }
private val rendering by lazy {
val rendering = Rendering(connection)
rendering.context.textures::debugTexture.forceSet(MemoryTexture(Vec2i.EMPTY_INSTANCE, buffer = RGBA8Buffer(Vec2i(0, 0))))
rendering.context.textures::debugTexture.forceSet(MemoryTexture(Vec2i.EMPTY_INSTANCE, mipmaps = 0, buffer = RGBA8Buffer(Vec2i(0, 0))))
return@lazy rendering
}

View File

@ -18,7 +18,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
class DummyDynamicTextureArray(context: RenderContext) : DynamicTextureArray(context, 1) {
class DummyDynamicTextureArray(context: RenderContext) : DynamicTextureArray(context, 1, 0) {
override fun createTexture(identifier: Any, index: Int) = DummyDynamicTexture
override fun unload() = Unit

View File

@ -13,34 +13,16 @@
package de.bixilon.minosoft.gui.rendering.system.dummy.texture
import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
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.TextureStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteAnimator
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
class DummyStaticTextureArray(renderSystem: RenderSystem) : StaticTextureArray {
private val textures: MutableMap<ResourceLocation, Texture> = synchronizedMapOf()
override val animator: SpriteAnimator = SpriteAnimator(renderSystem)
override val state: TextureArrayStates = TextureArrayStates.DECLARED
override fun get(resourceLocation: ResourceLocation): Texture? = textures[resourceLocation]
override fun pushTexture(texture: Texture) {
TODO("Not yet implemented")
}
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
return textures.getOrPut(resourceLocation) { DummyTexture() }
}
class DummyStaticTextureArray(context: RenderContext) : StaticTextureArray(context, false, 0) {
override fun load(latch: AbstractLatch) {
for (texture in textures.values) {
for (texture in this.named.values) {
(texture as DummyTexture).state = TextureStates.LOADED
}
}
@ -49,8 +31,6 @@ class DummyStaticTextureArray(renderSystem: RenderSystem) : StaticTextureArray {
animator.init()
}
override fun activate() {
}
override fun activate() = Unit
override fun use(shader: NativeShader, name: String) = Unit
}

View File

@ -33,7 +33,7 @@ class DummyTexture : Texture {
override var properties: ImageProperties = ImageProperties()
override var renderData: TextureRenderData = DummyTextureRenderData
override lateinit var data: TextureData
override var mipmaps: Boolean = false
override val mipmaps: Int get() = 0
override fun load(context: RenderContext) {
data = TextureData(RGBA8Buffer(size))

View File

@ -20,5 +20,5 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicText
class DummyTextureManager(val context: RenderContext) : TextureManager() {
override val dynamicTextures: DynamicTextureArray = DummyDynamicTextureArray(context)
override val staticTextures: StaticTextureArray = DummyStaticTextureArray(context.system)
override val staticTextures: StaticTextureArray = DummyStaticTextureArray(context)
}

View File

@ -27,6 +27,7 @@ import de.bixilon.minosoft.config.profile.profiles.rendering.movement.MovementC
import de.bixilon.minosoft.config.profile.profiles.rendering.overlay.OverlayC
import de.bixilon.minosoft.config.profile.profiles.rendering.performance.PerformanceC
import de.bixilon.minosoft.config.profile.profiles.rendering.sky.SkyC
import de.bixilon.minosoft.config.profile.profiles.rendering.textures.TexturesC
import de.bixilon.minosoft.config.profile.storage.ProfileStorage
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import org.kordamp.ikonli.Ikon
@ -51,6 +52,7 @@ class RenderingProfile(
val performance = PerformanceC(this)
val overlay = OverlayC(this)
val sky = SkyC(this)
val textures = TexturesC(this)
override fun toString(): String {

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* 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.
*
* 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.config.profile.profiles.rendering.textures
import de.bixilon.minosoft.config.profile.delegate.primitive.IntDelegate
import de.bixilon.minosoft.config.profile.profiles.rendering.RenderingProfile
class TexturesC(profile: RenderingProfile) {
/**
* Weather to use mipmaps for all static textures
*/
var mipmaps by IntDelegate(profile, 4, arrayOf(0..4))
}

View File

@ -36,7 +36,7 @@ class UnifontTexture(
override val size: Vec2i = Vec2i(resolution)
private val pixel = 1.0f / size.x
override val transparency: TextureTransparencies = TextureTransparencies.TRANSPARENT
override val mipmaps: Boolean get() = false
override val mipmaps: Int get() = 0
override lateinit var array: TextureArrayProperties
override lateinit var renderData: TextureRenderData

View File

@ -30,7 +30,7 @@ class AtlasTexture(
override val size: Vec2i,
) : Texture {
override val transparency: TextureTransparencies = TextureTransparencies.TRANSLUCENT
override val mipmaps: Boolean get() = false
override val mipmaps: Int get() = 0
private val pixel = Vec2(1.0f) / size
override lateinit var array: TextureArrayProperties

View File

@ -13,21 +13,85 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.array
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
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.assets.util.InputStreamUtil.readAsString
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteAnimator
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file.PNGTexture
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.json.Jackson
interface StaticTextureArray : TextureArray {
val animator: SpriteAnimator
val state: TextureArrayStates
abstract class StaticTextureArray(
val context: RenderContext,
val async: Boolean,
val mipmaps: Int,
) : TextureArray {
protected val named: MutableMap<ResourceLocation, Texture> = mutableMapOf()
protected val other: MutableSet<Texture> = mutableSetOf()
private val lock = SimpleLock()
val animator = SpriteAnimator(context.system)
var state: TextureArrayStates = TextureArrayStates.DECLARED
protected set
operator fun get(resourceLocation: ResourceLocation): Texture? {
lock.acquire()
val texture = this.named[resourceLocation]
lock.release()
return texture
}
operator fun get(resourceLocation: ResourceLocation): Texture?
operator fun plusAssign(texture: Texture) = pushTexture(texture)
fun pushTexture(texture: Texture)
fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, properties: Boolean = true, default: (mipmaps: Boolean) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture
fun load(latch: AbstractLatch)
fun pushTexture(texture: Texture) {
lock.lock()
other += texture
lock.unlock()
if (texture.state != TextureStates.LOADED && async) {
DefaultThreadPool += ForcePooledRunnable { texture.load(context) }
}
}
fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, properties: Boolean = true, factory: (mipmaps: Int) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture {
lock.lock()
named[resourceLocation]?.let { lock.unlock(); return it }
// load .mcmeta
val properties = if (properties) readImageProperties(resourceLocation) ?: ImageProperties.DEFAULT else ImageProperties.DEFAULT // TODO: That kills performance
val default = factory.invoke(if (mipmaps) this.mipmaps else 0)
val texture = if (properties.animation == null) default else SpriteTexture(default)
texture.properties = properties
named[resourceLocation] = texture
lock.unlock()
if (async) {
DefaultThreadPool += ForcePooledRunnable { texture.load(context) }
}
return texture
}
private fun readImageProperties(texture: ResourceLocation): ImageProperties? {
try {
val data = context.connection.assetsManager.getOrNull("$texture.mcmeta".toResourceLocation())?.readAsString() ?: return null
return Jackson.MAPPER.readValue(data, ImageProperties::class.java)
} catch (error: Throwable) {
error.printStackTrace()
}
return null
}
abstract fun load(latch: AbstractLatch)
}

View File

@ -14,12 +14,29 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.data
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil
open class MipmapTextureData(
buffer: TextureBuffer,
levels: Int,
) : TextureData(buffer) {
val mipmaps: Array<TextureBuffer> = OpenGLTextureUtil.generateMipMaps(buffer)
val mipmaps: Array<TextureBuffer> = generate(levels)
override fun collect(): Array<TextureBuffer> = mipmaps
fun generate(levels: Int): Array<TextureBuffer> {
val images: MutableList<TextureBuffer> = mutableListOf(buffer)
var data = buffer
for (i in 0 until levels) {
val size = data.size shr 1
if (size.x <= 0 || size.y <= 0) {
break
}
data = data.mipmap()
images += data
}
return images.toTypedArray()
}
}

View File

@ -30,6 +30,7 @@ import java.lang.ref.WeakReference
abstract class DynamicTextureArray(
val context: RenderContext,
val initialSize: Int,
val mipmaps: Int,
) : TextureArray {
protected var textures: Array<WeakReference<DynamicTexture>?> = arrayOfNulls(initialSize)
protected val shaders: MutableSet<NativeShader> = mutableSetOf()
@ -71,7 +72,7 @@ abstract class DynamicTextureArray(
private fun DynamicTexture.load(index: Int, creator: () -> TextureBuffer) {
val buffer = creator.invoke()
this.data = MipmapTextureData(buffer)
this.data = MipmapTextureData(buffer, mipmaps)
if (Thread.currentThread() == context.thread) {
upload(index, this)
} else {

View File

@ -30,7 +30,7 @@ class SpriteTexture(private val original: Texture) : Texture {
override var properties: ImageProperties by original::properties
override var renderData: TextureRenderData by original::renderData
override val transparency: TextureTransparencies by original::transparency
override var mipmaps: Boolean = true
override val mipmaps: Int get() = original.mipmaps
override var state: TextureStates = TextureStates.DECLARED
private set
@ -58,7 +58,7 @@ class SpriteTexture(private val original: Texture) : Texture {
val buffer = original.create(size)
buffer.put(original, Vec2i(0, size.y), Vec2i.EMPTY, size)
val splitTexture = MemoryTexture(size, mipmaps = true, buffer = buffer)
val splitTexture = MemoryTexture(size, mipmaps = this.original.mipmaps, buffer = buffer)
splitTextures += splitTexture
}

View File

@ -35,7 +35,7 @@ interface Texture : ShaderTexture {
var renderData: TextureRenderData
var data: TextureData
val mipmaps: Boolean
val mipmaps: Int
fun load(context: RenderContext)
@ -52,7 +52,8 @@ interface Texture : ShaderTexture {
return renderData.transformUV(end)
}
fun createData(mipmaps: Boolean = this.mipmaps, buffer: TextureBuffer): TextureData {
return if (mipmaps) MipmapTextureData(buffer) else TextureData(buffer)
fun createData(mipmaps: Int = this.mipmaps, buffer: TextureBuffer): TextureData {
if (mipmaps <= 0) return TextureData(buffer)
return MipmapTextureData(buffer, mipmaps)
}
}

View File

@ -20,7 +20,6 @@ import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayProperties
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.system.base.texture.texture.TextureRenderData
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
@ -33,7 +32,7 @@ import java.io.FileNotFoundException
class PNGTexture(
override val resourceLocation: ResourceLocation,
override var mipmaps: Boolean = true,
override var mipmaps: Int,
) : FileTexture {
override lateinit var renderData: TextureRenderData
@ -66,7 +65,7 @@ class PNGTexture(
}
assetsManager[RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION].readTexture()
}
val data = if (mipmaps) MipmapTextureData(buffer) else TextureData(buffer)
val data = createData(mipmaps, buffer)
this.size = data.size
transparency = TextureTransparencies.OPAQUE

View File

@ -27,7 +27,7 @@ import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
class MemoryTexture(
override val size: Vec2i,
override var properties: ImageProperties = ImageProperties(),
override var mipmaps: Boolean = true,
override var mipmaps: Int,
buffer: TextureBuffer,
) : Texture {
override lateinit var array: TextureArrayProperties

View File

@ -17,12 +17,9 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.concurrent.pool.ThreadPool
import de.bixilon.kutil.concurrent.pool.runnable.ForcePooledRunnable
import de.bixilon.kutil.concurrent.pool.runnable.SimplePoolRunnable
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.kutil.latch.SimpleLatch
import de.bixilon.minosoft.assets.util.InputStreamUtil.readAsString
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
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.TextureStates
@ -30,36 +27,26 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayProperties
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayStates
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteAnimator
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.glFormat
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.glType
import de.bixilon.minosoft.gui.rendering.textures.TextureAnimation
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.json.Jackson
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.GL12.*
import org.lwjgl.opengl.GL13.GL_TEXTURE0
import org.lwjgl.opengl.GL13.glActiveTexture
import org.lwjgl.opengl.GL13.*
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicInteger
class OpenGLTextureArray(
val context: RenderContext,
private val loadTexturesAsync: Boolean = true,
) : StaticTextureArray {
private val namedTextures: MutableMap<ResourceLocation, Texture> = mutableMapOf()
private val otherTextures: MutableSet<Texture> = mutableSetOf()
context: RenderContext,
async: Boolean,
mipmaps: Int,
) : StaticTextureArray(context, async, mipmaps) {
private var textureIds = IntArray(TEXTURE_RESOLUTION_ID_MAP.size) { -1 }
override val animator = SpriteAnimator(context.system)
override var state: TextureArrayStates = TextureArrayStates.DECLARED
private set
private val texturesByResolution = Array<MutableList<Texture>>(TEXTURE_RESOLUTION_ID_MAP.size) { mutableListOf() }
private val lastTextureId = IntArray(TEXTURE_RESOLUTION_ID_MAP.size)
@ -68,46 +55,6 @@ class OpenGLTextureArray(
context.system.unsafeCast<OpenGLRenderSystem>().textureBindingIndex += TEXTURE_RESOLUTION_ID_MAP.size
}
override fun get(resourceLocation: ResourceLocation): Texture? {
return this.namedTextures[resourceLocation]
}
@Synchronized
override fun pushTexture(texture: Texture) {
otherTextures += texture
if (texture.state != TextureStates.LOADED && loadTexturesAsync) {
DefaultThreadPool += ForcePooledRunnable { texture.load(context) }
}
}
@Synchronized
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
namedTextures[resourceLocation]?.let { return it }
// load .mcmeta
val properties = if (properties) readImageProperties(resourceLocation) ?: ImageProperties.DEFAULT else ImageProperties.DEFAULT // TODO: That kills performance
val texture = if (properties.animation == null) default(mipmaps) else SpriteTexture(default(false))
texture.properties = properties
namedTextures[resourceLocation] = texture
if (loadTexturesAsync) {
DefaultThreadPool += ForcePooledRunnable { texture.load(context) }
}
return texture
}
private fun readImageProperties(texture: ResourceLocation): ImageProperties? {
try {
val data = context.connection.assetsManager.getOrNull("$texture.mcmeta".toResourceLocation())?.readAsString() ?: return null
return Jackson.MAPPER.readValue(data, ImageProperties::class.java)
} catch (error: Throwable) {
error.printStackTrace()
}
return null
}
private fun preLoad(latch: AbstractLatch, textures: Collection<Texture>) {
for (texture in textures) {
@ -171,29 +118,30 @@ class OpenGLTextureArray(
if (state == TextureArrayStates.LOADED || state == TextureArrayStates.PRE_LOADED) {
return
}
val preLoadLatch = SimpleLatch(namedTextures.size + otherTextures.size)
preLoad(preLoadLatch, namedTextures.values)
preLoad(preLoadLatch, otherTextures)
val preLoadLatch = SimpleLatch(named.size + other.size)
preLoad(preLoadLatch, named.values)
preLoad(preLoadLatch, other)
preLoadLatch.await()
val animationIndex = AtomicInteger()
preLoad(animationIndex, namedTextures.values)
preLoad(animationIndex, otherTextures)
preLoad(animationIndex, named.values)
preLoad(animationIndex, other)
state = TextureArrayStates.PRE_LOADED
}
private fun loadSingleArray(resolution: Int, textures: List<Texture>): Int {
val textureId = OpenGLTextureUtil.createTextureArray()
val textureId = OpenGLTextureUtil.createTextureArray(mipmaps)
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
for (level in 0..mipmaps) {
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA8, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
}
for (texture in textures) {
val renderData = texture.renderData as OpenGLTextureData
for ((level, buffer) in texture.data.collect().withIndex()) {
if (level > this.mipmaps) break
buffer.data.flip()
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, renderData.index, buffer.size.x, buffer.size.y, 1, buffer.glFormat, buffer.glType, buffer.data)
}
@ -214,7 +162,7 @@ class OpenGLTextureArray(
textureIds[index] = loadSingleArray(TEXTURE_RESOLUTION_ID_MAP[index], textures)
totalLayers += textures.size
}
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { "Loaded ${namedTextures.size} textures containing ${animator.animations.size} animated ones, split into $totalLayers layers!" }
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { "Loaded ${named.size} textures containing ${animator.animations.size} animated ones, split into $totalLayers layers!" }
animator.init()
state = TextureArrayStates.LOADED

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicText
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.dynamic.OpenGLDynamicTextureArray
class OpenGLTextureManager(val context: RenderContext) : TextureManager() {
override val staticTextures: StaticTextureArray = OpenGLTextureArray(context)
override val dynamicTextures: DynamicTextureArray = OpenGLDynamicTextureArray(context, context.system.unsafeCast(), resolution = 64)
private val mipmaps = context.connection.profiles.rendering.textures.mipmaps
override val staticTextures: StaticTextureArray = OpenGLTextureArray(context, true, mipmaps)
override val dynamicTextures: DynamicTextureArray = OpenGLDynamicTextureArray(context, context.system.unsafeCast(), resolution = 64, mipmaps = mipmaps)
}

View File

@ -13,90 +13,27 @@
package de.bixilon.minosoft.gui.rendering.system.opengl.texture
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGB8Buffer
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGBA8Buffer
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
import example.jonathan2520.SRGBAverager
import org.lwjgl.BufferUtils
import org.lwjgl.opengl.GL11.*
import org.lwjgl.opengl.GL12.GL_TEXTURE_MAX_LEVEL
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
import java.nio.ByteBuffer
object OpenGLTextureUtil {
const val MAX_MIPMAP_LEVELS = 5
fun createTextureArray(): Int {
fun createTextureArray(mipmaps: Int): Int {
val textureId = glGenTextures()
glBindTexture(GL_TEXTURE_2D_ARRAY, textureId)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT)
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, if (mipmaps == 0) GL_NEAREST else GL_NEAREST_MIPMAP_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, MAX_MIPMAP_LEVELS - 1)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mipmaps)
return textureId
}
fun generateMipMaps(buffer: TextureBuffer): Array<TextureBuffer> {
val images: MutableList<TextureBuffer> = mutableListOf()
images += buffer
var data = buffer
for (i in 1 until MAX_MIPMAP_LEVELS) {
val size = Vec2i(buffer.size.x shr i, buffer.size.y shr i)
if (size.x <= 0 || size.y <= 0) {
break
}
data = data.mipmap()
images += data
}
return images.toTypedArray()
}
private fun generateMipmap(origin: ByteBuffer, oldSize: Vec2i): ByteBuffer {
// No Vec2i: performance reasons
val oldSizeX = oldSize.x
val newSizeX = oldSizeX shr 1
val buffer = BufferUtils.createByteBuffer(origin.capacity() shr 1)
buffer.limit(buffer.capacity())
fun getRGB(x: Int, y: Int): Int {
return origin.getInt((y * oldSizeX + x) * 4)
}
fun setRGB(x: Int, y: Int, color: Int) {
buffer.putInt((y * newSizeX + x) * 4, color)
}
for (y in 0 until (oldSize.y shr 1)) {
for (x in 0 until newSizeX) {
val xOffset = x * 2
val yOffset = y * 2
val output = SRGBAverager.average(
getRGB(xOffset + 0, yOffset + 0),
getRGB(xOffset + 1, yOffset + 0),
getRGB(xOffset + 0, yOffset + 1),
getRGB(xOffset + 1, yOffset + 1),
)
setRGB(x, y, output)
}
}
origin.position(0)
buffer.position(0)
return buffer
}
val TextureBuffer.glFormat: Int
get() = when (this) {
is RGBA8Buffer -> GL_RGBA

View File

@ -40,7 +40,8 @@ class OpenGLDynamicTextureArray(
val index: Int = renderSystem.textureBindingIndex++,
initialSize: Int = 32,
val resolution: Int,
) : DynamicTextureArray(context, initialSize) {
mipmaps: Int,
) : DynamicTextureArray(context, initialSize, mipmaps) {
private val empty = IntArray(resolution * resolution) { 0x00 }
private var handle = -1
@ -69,8 +70,8 @@ class OpenGLDynamicTextureArray(
override fun upload() {
if (handle >= 0) throw MemoryLeakException("Texture was not unloaded!")
val handle = OpenGLTextureUtil.createTextureArray()
for (level in 0 until OpenGLTextureUtil.MAX_MIPMAP_LEVELS) {
val handle = OpenGLTextureUtil.createTextureArray(mipmaps)
for (level in 0..mipmaps) {
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?)
}