diff --git a/src/main/java/de/bixilon/minosoft/data/text/RGBColor.java b/src/main/java/de/bixilon/minosoft/data/text/RGBColor.java
deleted file mode 100644
index ae2e052a1..000000000
--- a/src/main/java/de/bixilon/minosoft/data/text/RGBColor.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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 .
- *
- * This software is not affiliated with Mojang AB, the original developer of Minecraft.
- */
-
-package de.bixilon.minosoft.data.text;
-
-import org.checkerframework.common.value.qual.IntRange;
-
-public final class RGBColor implements ChatCode {
- private static final float COLOR_FLOAT_DIVIDER = 255.0f;
- private final int color;
-
- public RGBColor(int red, int green, int blue, int alpha) {
- this.color = (alpha) | (blue << 8) | (green << 16) | (red << 24);
- }
-
- public RGBColor(byte red, byte green, byte blue, byte alpha) {
- this(red & 0xFF, green & 0xFF, blue & 0xFF, alpha & 0xFF);
- }
-
- public RGBColor(int red, int green, int blue) {
- this(red, green, blue, 0xFF);
- }
-
- public RGBColor(int color) {
- this.color = color;
- }
-
- public RGBColor(String colorString) {
- if (colorString.startsWith("#")) {
- colorString = colorString.substring(1);
- }
- if (colorString.length() == 6) {
- this.color = Integer.parseUnsignedInt(colorString + "ff", 16);
- } else {
- this.color = Integer.parseUnsignedInt(colorString, 16);
- }
- }
-
- public static RGBColor noAlpha(int color) {
- return new RGBColor(color << 8 | 0xFF);
- }
-
- @IntRange(from = 0, to = 255)
- public int getAlpha() {
- return (this.color & 0xFF);
- }
-
- @IntRange(from = 0, to = 255)
- public int getRed() {
- return (this.color >>> 24) & 0xFF;
- }
-
- @IntRange(from = 0, to = 1)
- public float getFloatRed() {
- return getRed() / COLOR_FLOAT_DIVIDER;
- }
-
- @IntRange(from = 0, to = 255)
- public int getGreen() {
- return (this.color >>> 16) & 0xFF;
- }
-
- @IntRange(from = 0, to = 1)
- public float getFloatGreen() {
- return getGreen() / COLOR_FLOAT_DIVIDER;
- }
-
- @IntRange(from = 0, to = 255)
- public int getBlue() {
- return (this.color >>> 8) & 0xFF;
- }
-
- @IntRange(from = 0, to = 1)
- public float getFloatBlue() {
- return getBlue() / COLOR_FLOAT_DIVIDER;
- }
-
- @Override
- public int hashCode() {
- return this.color;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (super.equals(obj)) {
- return true;
- }
- RGBColor their = (RGBColor) obj;
- return getColor() == their.getColor();
- }
-
- @Override
- public String toString() {
- if (getAlpha() != 255) {
- return String.format("#%08X", this.color);
- }
- return String.format("#%06X", (0xFFFFFF & this.color));
- }
-
- public int getColor() {
- return this.color;
- }
-}
diff --git a/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt b/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt
new file mode 100644
index 000000000..d9e96f3da
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt
@@ -0,0 +1,96 @@
+/*
+ * Minosoft
+ * Copyright (C) 2021 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 .
+ *
+ * This software is not affiliated with Mojang AB, the original developer of Minecraft.
+ */
+package de.bixilon.minosoft.data.text
+
+import org.checkerframework.common.value.qual.IntRange
+
+class RGBColor(val color: Int) : ChatCode {
+
+ @JvmOverloads
+ constructor(red: Int, green: Int, blue: Int, alpha: Int = 0xFF) : this(alpha or (blue shl 8) or (green shl 16) or (red shl 24))
+
+ constructor(red: Byte, green: Byte, blue: Byte, alpha: Byte = 0xFF.toByte()) : this(red.toInt() and 0xFF, green.toInt() and 0xFF, blue.toInt() and 0xFF, alpha.toInt() and 0xFF)
+
+ constructor(colorString: String) : this(colorString.toColorInt())
+
+ val alpha: @IntRange(from = 0.toLong(), to = 255.toLong()) Int
+ get() = color and 0xFF
+ val red: @IntRange(from = 0.toLong(), to = 255.toLong()) Int
+ get() = color ushr 24 and 0xFF
+ val floatRed: @IntRange(from = 0.toLong(), to = 1.toLong()) Float
+ get() = red / COLOR_FLOAT_DIVIDER
+ val green: @IntRange(from = 0.toLong(), to = 255.toLong()) Int
+ get() = color ushr 16 and 0xFF
+ val floatGreen: @IntRange(from = 0.toLong(), to = 1.toLong()) Float
+ get() = green / COLOR_FLOAT_DIVIDER
+ val blue: @IntRange(from = 0.toLong(), to = 255.toLong()) Int
+ get() = color ushr 8 and 0xFF
+ val floatBlue: @IntRange(from = 0.toLong(), to = 1.toLong()) Float
+ get() = blue / COLOR_FLOAT_DIVIDER
+
+ override fun hashCode(): Int {
+ return color
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (super.equals(other)) {
+ return true
+ }
+ val their = other as RGBColor? ?: return false
+ return color == their.color
+ }
+
+ override fun toString(): String {
+ return if (alpha != 255) {
+ String.format("#%08X", color)
+ } else {
+ String.format("#%06X", 0xFFFFFF and color)
+ }
+ }
+
+ companion object {
+ private const val COLOR_FLOAT_DIVIDER = 255.0f
+ fun noAlpha(color: Int): RGBColor {
+ return RGBColor(color shl 8 or 0xFF)
+ }
+
+ fun String.toColor(): RGBColor {
+ return RGBColor(this)
+ }
+
+ fun String.toColorInt(): Int {
+ var colorString = this
+ if (colorString.startsWith("#")) {
+ colorString = colorString.substring(1)
+ }
+ return if (colorString.length == 6) {
+ Integer.parseUnsignedInt(colorString + "ff", 16)
+ } else {
+ Integer.parseUnsignedInt(colorString, 16)
+ }
+ }
+
+ fun mix(vararg colors: RGBColor): RGBColor {
+ var red = 0
+ var green = 0
+ var blue = 0
+
+ for (color in colors) {
+ red += color.red
+ green += color.green
+ blue += color.blue
+ }
+ return RGBColor(red / colors.size, green / colors.size, blue / colors.size)
+ }
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt
index 185083dfa..ada301ce4 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt
@@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.textures
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.assets.AssetsManager
import de.bixilon.minosoft.data.mappings.ResourceLocation
+import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
@@ -24,10 +25,9 @@ import de.matthiasmann.twl.utils.PNGDecoder
import glm_.vec2.Vec2
import glm_.vec2.Vec2i
import org.lwjgl.BufferUtils
-import org.lwjgl.opengl.GL12.glTexImage3D
-import org.lwjgl.opengl.GL12.glTexSubImage3D
import org.lwjgl.opengl.GL30.*
import org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER
+import org.lwjgl.opengl.GL31.glBindBuffer
import java.nio.ByteBuffer
class TextureArray(val allTextures: MutableList) {
@@ -119,17 +119,129 @@ class TextureArray(val allTextures: MutableList) {
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) // ToDo: This breaks transparency again
+ // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, 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)
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, resolution, resolution, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
-
- for (texture in textures) {
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, texture.arrayLayer, texture.size.x, texture.size.y, 1, GL_RGBA, GL_UNSIGNED_BYTE, texture.buffer!!)
- texture.buffer = null
+ for (i in 0 until MAX_MIPMAP_LEVELS) {
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA, resolution shr i, resolution shr i, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
}
- // glGenerateMipmap(GL_TEXTURE_2D_ARRAY)
+ for (texture in textures) {
+ var lastBuffer = texture.buffer!!
+ var lastSize = texture.size
+ for (i in 0 until MAX_MIPMAP_LEVELS) {
+ val size = Vec2i(texture.size.x shr i, texture.size.y shr i)
+ if (i != 0) {
+ lastBuffer = generateMipmap(lastBuffer, lastSize, size)
+ lastSize = size
+ }
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, texture.arrayLayer, size.x, size.y, i + 1, GL_RGBA, GL_UNSIGNED_BYTE, lastBuffer)
+ }
+ }
+ }
+
+ private fun ByteBuffer.getRGB(start: Int): RGBColor {
+ return RGBColor(get(start), get(start + 1), get(start + 2), get(start + 3))
+ }
+
+ private fun ByteBuffer.setRGB(start: Int, color: RGBColor) {
+ put(start, color.red.toByte())
+ put(start + 1, color.green.toByte())
+ put(start + 2, color.blue.toByte())
+ put(start + 3, color.alpha.toByte())
+ }
+
+ @Deprecated(message = "This is garbage, will be improved soon...")
+ private fun generateMipmap(biggerBuffer: ByteBuffer, oldSize: Vec2i, newSize: Vec2i): ByteBuffer {
+ val sizeFactor = oldSize / newSize
+ val buffer = BufferUtils.createByteBuffer(biggerBuffer.capacity() shr 1)
+ buffer.limit(buffer.capacity())
+
+ fun getRGB(x: Int, y: Int): RGBColor {
+ return biggerBuffer.getRGB((y * oldSize.x + x) * 4)
+ }
+
+ fun setRGB(x: Int, y: Int, color: RGBColor) {
+ buffer.setRGB((y * newSize.x + x) * 4, color)
+ }
+
+ for (y in 0 until newSize.y) {
+ for (x in 0 until newSize.x) {
+
+ // check what is the most used transparency
+ val transparencyPixelCount = IntArray(TextureTransparencies.VALUES.size)
+ for (mixY in 0 until sizeFactor.y) {
+ for (mixX in 0 until sizeFactor.x) {
+ val color = getRGB(x * sizeFactor.x + mixX, y * sizeFactor.y + mixY)
+ when (color.alpha) {
+ 255 -> transparencyPixelCount[TextureTransparencies.OPAQUE.ordinal]++
+ 0 -> transparencyPixelCount[TextureTransparencies.TRANSPARENT.ordinal]++
+ else -> transparencyPixelCount[TextureTransparencies.TRANSLUCENT.ordinal]++
+ }
+ }
+ }
+ var largest = 0
+ for (count in transparencyPixelCount) {
+ if (count > largest) {
+ largest = count
+ }
+ }
+ var transparency: TextureTransparencies = TextureTransparencies.OPAQUE
+ for ((index, count) in transparencyPixelCount.withIndex()) {
+ if (count >= largest) {
+ transparency = TextureTransparencies.VALUES[index]
+ break
+ }
+ }
+
+ var count = 0
+ var red = 0
+ var green = 0
+ var blue = 0
+ var alpha = 0
+
+ // make magic for the most used transparency
+ for (mixY in 0 until sizeFactor.y) {
+ for (mixX in 0 until sizeFactor.x) {
+ val color = getRGB(x * sizeFactor.x + mixX, y * sizeFactor.y + mixY)
+ when (transparency) {
+ TextureTransparencies.OPAQUE -> {
+ if (color.alpha != 0xFF) {
+ continue
+ }
+ red += color.red
+ green += color.green
+ blue += color.blue
+ alpha += color.alpha
+ count++
+ }
+ TextureTransparencies.TRANSPARENT -> {
+ }
+ TextureTransparencies.TRANSLUCENT -> {
+ red += color.red
+ green += color.green
+ blue += color.blue
+ alpha += color.alpha
+ count++
+ }
+ }
+ }
+ }
+
+
+
+
+
+ if (count == 0) {
+ count++
+ }
+ setRGB(x, y, RGBColor(red / count, green / count, blue / count, alpha / count))
+ }
+ }
+
+ buffer.rewind()
+ return buffer
}
@@ -146,6 +258,7 @@ class TextureArray(val allTextures: MutableList) {
companion object {
val TEXTURE_RESOLUTION_ID_MAP = arrayOf(16, 32, 64, 128, 256, 512, 1024) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
const val TEXTURE_MAX_RESOLUTION = 1024
+ const val MAX_MIPMAP_LEVELS = 5
private const val INTS_PER_ANIMATED_TEXTURE = 4
}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureTransparencies.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureTransparencies.kt
index 480e8021e..4a90d6b68 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureTransparencies.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureTransparencies.kt
@@ -13,8 +13,17 @@
package de.bixilon.minosoft.gui.rendering.textures
+import de.bixilon.minosoft.util.KUtil
+import de.bixilon.minosoft.util.enum.ValuesEnum
+
enum class TextureTransparencies {
OPAQUE,
TRANSPARENT,
TRANSLUCENT,
+ ;
+
+ companion object : ValuesEnum {
+ override val VALUES: Array = values()
+ override val NAME_MAP: Map = KUtil.getEnumValues(VALUES)
+ }
}
diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt
index 73cf540f0..691ae3e00 100644
--- a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt
+++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt
@@ -25,7 +25,6 @@ import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.protocol.network.connection.Connection
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes
-import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes.Companion.VALUES
import glm_.vec2.Vec2i
import glm_.vec3.Vec3
import glm_.vec3.Vec3i
@@ -370,7 +369,7 @@ open class InByteBuffer {
NBTTagTypes.COMPOUND -> {
val out: MutableMap = mutableMapOf()
while (true) {
- val compoundTagType = VALUES[readUnsignedByte()]
+ val compoundTagType = NBTTagTypes.VALUES[readUnsignedByte()]
if (compoundTagType === NBTTagTypes.END) {
// end tag
break
@@ -396,7 +395,7 @@ open class InByteBuffer {
InByteBuffer(Util.decompressGzip(readByteArray(length)), connection!!).readNBTTag(false)
}
}
- val type = VALUES[readUnsignedByte()]
+ val type = NBTTagTypes.VALUES[readUnsignedByte()]
if (type === NBTTagTypes.COMPOUND) {
var name = readString(readUnsignedShort()) // ToDo
}
@@ -406,5 +405,4 @@ open class InByteBuffer {
fun getBase64(): String {
return String(Base64.getEncoder().encode(readRest()))
}
-
}