wip mixed TextureFormats

This is the first step for "compressing" texture data from RGBA8 to RGBA2. That should theoretically reduce font vram usage to 1/4 (~100MB -> ~25MB). Needs to be tested and actually used.
This commit is contained in:
Bixilon 2023-07-19 12:02:46 +02:00
parent d818661289
commit 03ffa29074
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
30 changed files with 166 additions and 59 deletions

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.font.types.bitmap
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture
@ -35,7 +36,7 @@ class BitmapFontTypeTest {
val rows = (start.size / 16) + if (start.size % 16 == 0) 0 else 1
val size = Vec2i(width * 16, rows * height)
val buffer = ByteBuffer.allocate(size.x * size.y * 4)
val buffer = ByteBuffer.allocate(size.x * size.y)
for (row in 0 until rows) {
for (height in 0 until height) {
@ -44,10 +45,6 @@ class BitmapFontTypeTest {
val end = end.getOrNull((row * 16) + char) ?: width
for (pixel in 0 until width) {
buffer.put(0xFF.toByte())
buffer.put(0xFF.toByte())
buffer.put(0xFF.toByte())
if (pixel in start..end) {
buffer.put(0xFF.toByte())
} else {
@ -61,7 +58,7 @@ class BitmapFontTypeTest {
val texture = DummyTexture()
texture.size = size
texture.data = TextureData(size, buffer)
texture.data = TextureData(size, TextureFormats.RGBA2, buffer)
return texture
}

View File

@ -78,9 +78,7 @@ class UnihexFontTypeTest {
}
private fun ByteArray.assertPixel(index: Int, set: Boolean = true) {
val offset = index * 4
val value = this[offset + 0].toInt() or this[offset + 1].toInt() or this[offset + 2].toInt() or this[offset + 3].toInt()
val value = this[index + 0].toInt()
if (set) {
assertTrue(value != 0, "Did expect pixel at $index")

View File

@ -17,11 +17,18 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.minosoft.gui.rendering.gui.test.GuiRenderTestUtil
import de.bixilon.minosoft.gui.rendering.gui.test.GuiTestConsumer
import org.testng.Assert.assertEquals
import org.testng.Assert.assertFalse
import org.testng.annotations.Test
@Test(groups = ["gui"])
class ButtonElementTest {
fun `initial clean`() {
val button = ButtonElement(GuiRenderTestUtil.create(), "bc") { }
assertFalse(button.update)
}
fun `basic verification`() {
var invoked = 0
val button = ButtonElement(GuiRenderTestUtil.create(), "bc") { invoked++ }

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.gui.rendering.gui.test.GuiRenderTestUtil.assetSize
import de.bixilon.minosoft.gui.rendering.gui.test.GuiTestConsumer
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.marginOf
import org.testng.Assert.assertEquals
import org.testng.Assert.assertFalse
import org.testng.annotations.Test
@Test(groups = ["font", "gui"])
@ -33,6 +34,11 @@ class TextElementTest {
element.assetSize(Vec2(0, 0))
}
fun `initial clean`() {
val element = TextElement(GuiRenderTestUtil.create(), "")
assertFalse(element.update)
}
fun `size of single char`() {
val element = TextElement(GuiRenderTestUtil.create(), "b", background = null, properties = TextRenderProperties(shadow = false))
element.assetSize(Vec2(0.5f, 11.0f))

View File

@ -18,6 +18,7 @@ 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.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -35,7 +36,7 @@ class DummyStaticTextureArray(renderSystem: RenderSystem) : StaticTextureArray {
TODO("Not yet implemented")
}
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, format: TextureFormats, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
return textures.getOrPut(resourceLocation) { DummyTexture() }
}

View File

@ -16,6 +16,7 @@ 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.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -36,6 +37,6 @@ class DummyTexture : Texture {
override var mipmaps: Boolean = false
override fun load(context: RenderContext) {
data = TextureData(size, TextureGenerator.allocate(size))
data = TextureData(size, TextureFormats.RGBA8, TextureGenerator.allocate(size, TextureFormats.RGBA8))
}
}

View File

@ -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.
*
@ -33,4 +33,14 @@ object ColorUtil {
val color = (this * RGBColor.COLOR_FLOAT_DIVIDER).toInt()
return color shl 16 or color shl 8 or color
}
fun rgba8ToRgba2(rgba8: Int): Byte {
val red = (rgba8 shr 24 + 6) and 0x03
val green = (rgba8 shr 16 + 6) and 0x03
val blue = (rgba8 shr 8 + 6) and 0x03
val alpha = (rgba8 shr 0 + 6) and 0x03
return ((red shl 6) or (green shl 4) or (blue shl 2) or alpha).toByte()
}
}

View File

@ -27,6 +27,7 @@ import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.types.PostInitFontType
import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer
import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@ -73,7 +74,7 @@ class BitmapFontType(
private fun load(file: ResourceLocation, height: Int, ascent: Int, chars: List<String>, context: RenderContext): BitmapFontType? {
if (chars.isEmpty() || height <= 0) return null
val texture = context.textures.staticTextures.createTexture(file, mipmaps = false, properties = false)
val texture = context.textures.staticTextures.createTexture(file, mipmaps = false, format = TextureFormats.RGBA2, properties = false)
texture.load(context) // force load it, we need to calculate the width of every char
return load(texture, height, ascent, chars.codePoints())
@ -82,7 +83,7 @@ class BitmapFontType(
private fun ByteBuffer.scanLine(y: Int, width: Int, start: IntArray, end: IntArray) {
for (index in 0 until (width * ROW)) {
val pixelIndex = ((ROW * width * y) + index)
val alpha = this[pixelIndex * 4 + 3].toInt() // index * rgba + a
val alpha = this[pixelIndex].toInt() and 0x03 // index * rgba + a
if (alpha == 0) {
// transparent
continue

View File

@ -26,6 +26,7 @@ import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.types.PostInitFontType
import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory
import de.bixilon.minosoft.gui.rendering.font.types.unicode.UnicodeCodeRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
@ -84,7 +85,7 @@ class LegacyUnicodeFontType(
sizes.skip(PAGE_SIZE.toLong())
return
}
val texture = textures.createTexture(textureFile, mipmaps = false, properties = false)
val texture = textures.createTexture(textureFile, mipmaps = false, format = TextureFormats.RGBA2, properties = false)
loadPage(pageId, texture, chars, sizes)
}

View File

@ -20,6 +20,7 @@ import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer
import de.bixilon.minosoft.gui.rendering.font.renderer.properties.FontProperties
import de.bixilon.minosoft.gui.rendering.font.types.unicode.UnicodeCodeRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -39,7 +40,7 @@ class UnifontTexture(
override lateinit var array: TextureArrayProperties
override lateinit var renderData: TextureRenderData
override var data: TextureData = TextureData(size)
override var data: TextureData = TextureData(size, TextureFormats.RGBA2)
override var properties = ImageProperties.DEFAULT
override val state: TextureStates = TextureStates.LOADED
@ -60,9 +61,9 @@ class UnifontTexture(
}
private fun TextureData.set(row: Int, offset: Int, x: Int, y: Int) {
val index = ((row * UnifontRasterizer.HEIGHT + y) * resolution + offset + x) * 4
val index = (row * UnifontRasterizer.HEIGHT + y) * resolution + offset + x
buffer.putInt(index, 0xFFFFFFFF.toInt())
buffer.put(index, 0xFF.toByte())
}
private fun rasterize(row: Int, offset: Int, start: Int, end: Int, dataWidth: Int, data: ByteArray): CodePointRenderer {

View File

@ -60,7 +60,7 @@ open class ButtonElement(
init {
padding = Vec4(4.0f)
updateSize()
tryUpdate()
}
protected fun updateSize() {

View File

@ -17,12 +17,12 @@ import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.collections.CollectionUtil.lockMapOf
import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedMap
import de.bixilon.kutil.collections.map.LockMap
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.latch.SimpleLatch
import de.bixilon.minosoft.config.key.KeyActions
import de.bixilon.minosoft.config.key.KeyBinding
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderUtil.runAsync
import de.bixilon.minosoft.gui.rendering.gui.GUIElementDrawer
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.gui.LayoutedGUIElement
@ -69,7 +69,7 @@ class HUDManager(
for (builder in DEFAULT_ELEMENTS) {
latch.inc()
DefaultThreadPool += { registerElement(builder); latch.dec() }
context.runAsync { registerElement(builder); latch.dec() }
}
latch.dec()
latch.await()

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.sky.clouds
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.config.DebugOptions
import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import java.util.*
@ -25,7 +26,7 @@ class CloudMatrix {
fun load(assetsManager: AssetsManager) {
val data = assetsManager[CLOUD_MATRIX].readTexture()
val data = assetsManager[CLOUD_MATRIX].readTexture(TextureFormats.RGBA2) // TODO: luminance
if (data.size.x != CLOUD_MATRIX_SIZE || data.size.y != CLOUD_MATRIX_SIZE) {
throw IllegalStateException("Cloud matrix has invalid size: ${data.size}")
@ -35,7 +36,7 @@ class CloudMatrix {
if (DebugOptions.CLOUD_RASTER) {
matrix[i] = if ((i / CLOUD_MATRIX_SIZE) % 2 == 0) (i + 1) % 2 == 0 else (i % 2) == 0
} else {
matrix[i] = data.buffer.getInt(i * 4) ushr 24 == 0xFF
matrix[i] = data.buffer.getInt(i) != 0x00
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.gui.rendering.system.base.texture
enum class TextureFormats(val bytes: Int) {
RGBA8(4),
RGBA2(1),
;
}

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.array
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.sprite.SpriteAnimator
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file.PNGTexture
@ -26,7 +27,7 @@ interface StaticTextureArray : TextureArray {
fun get(resourceLocation: ResourceLocation): 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 createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, format: TextureFormats = TextureFormats.RGBA8, properties: Boolean = true, default: (mipmaps: Boolean) -> Texture = { PNGTexture(resourceLocation, format = format, mipmaps = it) }): Texture
fun preLoad(latch: AbstractLatch)
}

View File

@ -14,14 +14,16 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.data
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil
import java.nio.ByteBuffer
open class MipmapTextureData(
size: Vec2i,
format: TextureFormats,
buffer: ByteBuffer,
) : TextureData(size, buffer) {
val mipmaps: Array<ByteBuffer> = OpenGLTextureUtil.generateMipMaps(buffer, size)
) : TextureData(size, format, buffer) {
val mipmaps: Array<ByteBuffer> = OpenGLTextureUtil.generateMipMaps(buffer, format, size)
override fun collect(): Array<ByteBuffer> = mipmaps
}

View File

@ -14,15 +14,17 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.data
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory.TextureGenerator
import org.objenesis.ObjenesisStd
import java.nio.ByteBuffer
open class TextureData(
val size: Vec2i,
val buffer: ByteBuffer = TextureGenerator.allocate(size),
val format: TextureFormats,
val buffer: ByteBuffer = TextureGenerator.allocate(size, format),
) {
constructor(size: Vec2i, array: ByteArray) : this(size, ByteBuffer.wrap(array))
constructor(size: Vec2i, format: TextureFormats, array: ByteArray) : this(size, format, ByteBuffer.wrap(array))
open fun collect(): Array<ByteBuffer> = arrayOf(buffer)

View File

@ -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,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
@ -25,6 +26,6 @@ interface DynamicTextureArray : TextureArray {
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() }
return pushBuffer(identifier, force) { ByteArrayInputStream(data()).readTexture(TextureFormats.RGBA8) }
}
}

View File

@ -18,6 +18,7 @@ import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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.skin.PlayerSkin
@ -65,7 +66,7 @@ class DefaultSkinProvider(
}
private fun load(path: ResourceLocation): DynamicTexture? {
val data = assets.getOrNull(path)?.readTexture() ?: return null
val data = assets.getOrNull(path)?.readTexture(TextureFormats.RGBA8) ?: return null
val texture = array.pushBuffer(UUID(0L, defaultId++.toLong()), true) { data }
texture.usages.incrementAndGet()
return texture

View File

@ -59,11 +59,11 @@ class SpriteTexture(private val original: Texture) : Texture {
val bytesPerTexture = size.x * size.y * PNGDecoder.Format.RGBA.numComponents
for (i in 0 until animationProperties.frameCount) {
val buffer = TextureGenerator.allocate(size)
val buffer = TextureGenerator.allocate(size, data.format)
buffer.copyFrom(original, bytesPerTexture * i, 0, bytesPerTexture)
buffer.flip()
val splitTexture = MemoryTexture(size, mipmaps = true, buffer = buffer)
val splitTexture = MemoryTexture(size, mipmaps = true, format = data.format, buffer = buffer)
splitTextures += splitTexture
}

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.texture
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -52,7 +53,7 @@ interface Texture : ShaderTexture {
return renderData.transformUV(end)
}
fun createData(mipmaps: Boolean = this.mipmaps, size: Vec2i, buffer: ByteBuffer): TextureData {
return if (mipmaps) MipmapTextureData(size, buffer) else TextureData(size, buffer)
fun createData(mipmaps: Boolean = this.mipmaps, size: Vec2i, format: TextureFormats, buffer: ByteBuffer): TextureData {
return if (mipmaps) MipmapTextureData(size, format, buffer) else TextureData(size, format, buffer)
}
}

View File

@ -17,6 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -34,6 +35,7 @@ import java.io.FileNotFoundException
class PNGTexture(
override val resourceLocation: ResourceLocation,
override var mipmaps: Boolean = true,
val format: TextureFormats = TextureFormats.RGBA8,
) : FileTexture {
override lateinit var renderData: TextureRenderData
@ -57,17 +59,17 @@ class PNGTexture(
}
var data = try {
assetsManager[resourceLocation].readTexture()
assetsManager[resourceLocation].readTexture(format)
} catch (error: Throwable) {
state = TextureStates.ERRORED
Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Can not load texture $resourceLocation: $error" }
if (error !is FileNotFoundException) {
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { error }
}
assetsManager[RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION].readTexture()
assetsManager[RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION].readTexture(format)
}
data.buffer.rewind()
if (mipmaps) data = MipmapTextureData(data.size, data.buffer)
if (mipmaps) data = MipmapTextureData(data.size, format, data.buffer)
this.size = data.size
transparency = TextureTransparencies.OPAQUE

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
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
@ -28,16 +29,17 @@ class MemoryTexture(
override val size: Vec2i,
override var properties: ImageProperties = ImageProperties(),
override var mipmaps: Boolean = true,
format: TextureFormats,
buffer: ByteBuffer,
) : Texture {
override lateinit var array: TextureArrayProperties
override lateinit var renderData: TextureRenderData
override var transparency: TextureTransparencies = TextureTransparencies.OPAQUE
private set
override var data: TextureData = createData(mipmaps, size, buffer)
override var data: TextureData = createData(mipmaps, size, format, buffer)
init {
if (buffer.limit() != TextureGenerator.getBufferSize(size)) throw IllegalArgumentException("Invalid buffer size: ${buffer.limit()}")
if (buffer.limit() != TextureGenerator.getBufferSize(size, format)) throw IllegalArgumentException("Invalid buffer size: ${buffer.limit()}")
}
override val state: TextureStates = TextureStates.LOADED

View File

@ -14,12 +14,14 @@
package de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory
import de.bixilon.kotlinglm.vec2.Vec2i
import de.matthiasmann.twl.utils.PNGDecoder
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import org.lwjgl.BufferUtils
object TextureGenerator {
fun getBufferSize(size: Vec2i) = size.x * size.y * PNGDecoder.Format.RGBA.numComponents
fun getBufferSize(size: Vec2i, bytes: Int) = size.x * size.y * bytes
fun getBufferSize(size: Vec2i, format: TextureFormats) = getBufferSize(size, format.bytes)
fun allocate(size: Vec2i) = BufferUtils.createByteBuffer(getBufferSize(size))
fun allocate(size: Vec2i, bytes: Int) = BufferUtils.createByteBuffer(getBufferSize(size, bytes))
fun allocate(size: Vec2i, format: TextureFormats) = allocate(size, format.bytes)
}

View File

@ -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.
*
@ -24,7 +24,7 @@ abstract class OpenGLTexture {
abstract fun init()
fun bind(target: Int) {
check(target in 0 until 12)
check(target in 0 until 16)
glActiveTexture(GL_TEXTURE0 + target)
glBindTexture(GL_TEXTURE_2D, id)
}

View File

@ -24,6 +24,7 @@ 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.TextureFormats
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.TextureArrayProperties
@ -32,6 +33,7 @@ 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.texture.OpenGLTextureUtil.gl
import de.bixilon.minosoft.gui.rendering.textures.TextureAnimation
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@ -73,7 +75,7 @@ class OpenGLTextureArray(
}
@Synchronized
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
override fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean, format: TextureFormats, properties: Boolean, default: (mipmaps: Boolean) -> Texture): Texture {
namedTextures[resourceLocation]?.let { return it }
// load .mcmeta
@ -189,7 +191,7 @@ class OpenGLTextureArray(
for ((level, data) in texture.data.collect().withIndex()) {
val size = texture.size shr level
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, renderData.index, size.x, size.y, 1, GL_RGBA, GL_UNSIGNED_BYTE, data)
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, renderData.index, size.x, size.y, 1, texture.data.format.gl, GL_UNSIGNED_BYTE, data)
}
texture.data = TextureData.NULL

View File

@ -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.
*
@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.rendering.system.opengl.texture
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import example.jonathan2520.SRGBAverager
import org.lwjgl.BufferUtils
import org.lwjgl.opengl.GL11.*
@ -38,7 +39,8 @@ object OpenGLTextureUtil {
return textureId
}
fun generateMipMaps(data: ByteBuffer, size: Vec2i): Array<ByteBuffer> {
fun generateMipMaps(data: ByteBuffer, format: TextureFormats, size: Vec2i): Array<ByteBuffer> {
if (format != TextureFormats.RGBA8) TODO("Mipmap only supports rgba8 atm.")
val images: MutableList<ByteBuffer> = mutableListOf()
images += data
@ -93,4 +95,11 @@ object OpenGLTextureUtil {
buffer.position(0)
return buffer
}
val TextureFormats.gl: Int
get() = when (this) {
TextureFormats.RGBA8 -> GL_RGBA8
TextureFormats.RGBA2 -> GL_RGBA2
}
}

View File

@ -96,7 +96,7 @@ class OpenGLDynamicTextureArray(
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)
val mipmaps = OpenGLTextureUtil.generateMipMaps(data.buffer, data.format, data.size)
texture.data = mipmaps
texture.size = data.size
if (force) {

View File

@ -15,12 +15,15 @@ package de.bixilon.minosoft.gui.rendering.textures
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.formatting.color.ColorUtil
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureFormats
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import de.bixilon.minosoft.util.KUtil.getRGBA
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.matthiasmann.twl.utils.PNGDecoder
import org.lwjgl.BufferUtils
@ -29,6 +32,7 @@ import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import java.io.InputStream
import java.nio.ByteBuffer
import javax.imageio.ImageIO
object TextureUtil {
@ -91,7 +95,7 @@ object TextureUtil {
* Only happens to some weird textures (seen in PureBDCraft)
* Ignores the alpha channel
*/
private fun InputStream.readTexture2(): TextureData {
private fun InputStream.readTexture2(format: TextureFormats): TextureData {
val image: BufferedImage = ImageIO.read(this)
val rgb = image.getRGB(0, 0, image.width, image.height, null, 0, image.width)
@ -99,25 +103,46 @@ object TextureUtil {
val dataOutput = DataOutputStream(byteOutput)
for (color in rgb) {
dataOutput.writeInt((color shl 8) or 0xFF)
val rgba = (color shl 8) or 0xFF
when (format) {
TextureFormats.RGBA8 -> dataOutput.writeInt(rgba)
TextureFormats.RGBA2 -> dataOutput.writeByte(ColorUtil.rgba8ToRgba2(rgba).toInt())
}
}
val buffer = MemoryUtil.memAlloc(byteOutput.size())
buffer.put(byteOutput.toByteArray())
return TextureData(Vec2i(image.width, image.height), buffer)
return TextureData(Vec2i(image.width, image.height), format, buffer)
}
fun InputStream.readTexture(): TextureData {
return try {
fun InputStream.readTexture(format: TextureFormats): TextureData {
try {
val decoder = PNGDecoder(this)
val data = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
decoder.decode(data, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
TextureData(Vec2i(decoder.width, decoder.height), data)
} catch (exception: Throwable) {
this.reset()
readTexture2()
val converted = when (format) {
TextureFormats.RGBA8 -> data
TextureFormats.RGBA2 -> data.copyRgba8ToRgba2()
}
return TextureData(Vec2i(decoder.width, decoder.height), format, converted)
} catch (_: Throwable) {
}
this.reset()
return readTexture2(format)
}
private fun ByteBuffer.copyRgba8ToRgba2(): ByteBuffer {
val next = BufferUtils.createByteBuffer(this.limit() / 4)
for (index in 0..next.limit()) {
val rgba = this.getRGBA(index)
next.put(index, ColorUtil.rgba8ToRgba2(rgba))
}
return next
}
}

View File

@ -60,6 +60,7 @@ import de.bixilon.minosoft.util.url.ResourceURLHandler
import io.netty.channel.SimpleChannelInboundHandler
import javafx.application.Platform
import org.kamranzafar.jtar.TarHeader
import java.nio.ByteBuffer
import java.security.SecureRandom
import java.util.*
import javax.net.ssl.SSLContext
@ -352,4 +353,16 @@ object KUtil {
inline fun <reified T : Enum<T>> ValuesEnum<T>.set(): EnumSet<T> {
return EnumSetUtil.create(T::class.java, VALUES)
}
fun ByteBuffer.getRGB(index: Int): Int {
val red: Int = this[index].toInt() and 0xFF
val green: Int = this[index + 1].toInt() and 0xFF
val blue: Int = this[index + 2].toInt() and 0xFF
return 0xFF shl 24 or (red shl 16) or (green shl 8) or blue
}
fun ByteBuffer.getRGBA(index: Int): Int {
return (getRGB(index) shl 8) or (this[index + 3].toInt() and 0x0FF)
}
}