mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 03:15:35 -04:00
wip mipmaps
This commit is contained in:
parent
8f2edfcc75
commit
134ec6207c
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
96
src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt
Normal file
96
src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Texture>) {
|
||||
@ -119,17 +119,129 @@ class TextureArray(val allTextures: MutableList<Texture>) {
|
||||
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<Texture>) {
|
||||
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
|
||||
}
|
||||
|
@ -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<TextureTransparencies> {
|
||||
override val VALUES: Array<TextureTransparencies> = values()
|
||||
override val NAME_MAP: Map<String, TextureTransparencies> = KUtil.getEnumValues(VALUES)
|
||||
}
|
||||
}
|
||||
|
@ -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<String, Any> = 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()))
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user