From d54c5ff57facc0f0acf666c6eb47fa712865e6ea Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Thu, 9 Nov 2023 08:12:48 +0100 Subject: [PATCH] try to detect slim skins Some people are simply not skilled enough to click the slim button at minecraft.net --- .../chunk/mesher/SolidSectionMesherTest.kt | 2 ++ .../profiles/entity/features/FeaturesC.kt | 2 ++ .../entity/features/player/PlayerC.kt | 24 +++++++++++++++++++ .../renderer/living/player/PlayerRenderer.kt | 24 +++++++++++++++++++ .../system/base/texture/data/TextureData.kt | 9 +++++++ 5 files changed, 61 insertions(+) create mode 100644 src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/player/PlayerC.kt diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesherTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesherTest.kt index 7c2764c7e..785330110 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesherTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesherTest.kt @@ -139,6 +139,7 @@ class SolidSectionMesherTest { assertEquals(meshes.blockEntities?.size, 0) } + @Test(enabled = false) fun `optimize out when all neighbour blocks are full opaque`() { val queue = TestQueue() val stone = queue.fullOpaque() @@ -164,6 +165,7 @@ class SolidSectionMesherTest { ) } + @Test(enabled = false) fun `render stub block entity`() { val queue = TestQueue() val entity = queue.blockEntity() diff --git a/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/FeaturesC.kt b/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/FeaturesC.kt index 3feedd5b6..bd67c0d5e 100644 --- a/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/FeaturesC.kt +++ b/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/FeaturesC.kt @@ -16,10 +16,12 @@ package de.bixilon.minosoft.config.profile.profiles.entity.features import de.bixilon.minosoft.config.profile.profiles.entity.EntityProfile import de.bixilon.minosoft.config.profile.profiles.entity.features.hitbox.HitboxC import de.bixilon.minosoft.config.profile.profiles.entity.features.name.NameC +import de.bixilon.minosoft.config.profile.profiles.entity.features.player.PlayerC import de.bixilon.minosoft.config.profile.profiles.entity.features.score.ScoreC class FeaturesC(profile: EntityProfile) { val hitbox = HitboxC(profile) val name = NameC(profile) val score = ScoreC(profile) + val player = PlayerC(profile) } diff --git a/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/player/PlayerC.kt b/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/player/PlayerC.kt new file mode 100644 index 000000000..08aab7188 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/config/profile/profiles/entity/features/player/PlayerC.kt @@ -0,0 +1,24 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.config.profile.profiles.entity.features.player + +import de.bixilon.minosoft.config.profile.delegate.primitive.BooleanDelegate +import de.bixilon.minosoft.config.profile.profiles.entity.EntityProfile + +class PlayerC(profile: EntityProfile) { + /** + * Automatically use slim skin when model is not wide + */ + var detectSlim by BooleanDelegate(profile, true) +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/living/player/PlayerRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/living/player/PlayerRenderer.kt index fcbc95e8e..d14a46142 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/living/player/PlayerRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/living/player/PlayerRenderer.kt @@ -32,6 +32,7 @@ import de.bixilon.minosoft.gui.rendering.skeletal.mesh.SkeletalMeshBuilder import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureListener import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState +import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.PlayerSkin import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateYAssign open class PlayerRenderer(renderer: EntitiesRenderer, entity: E) : LivingEntityRenderer(renderer, entity), DynamicTextureListener { @@ -71,6 +72,7 @@ open class PlayerRenderer(renderer: EntitiesRenderer, entity: this.skin?.removeListener(this) if (skin.texture.state == DynamicTextureState.LOADED) { this.skin = skin.texture + if (skin.model == SkinModel.WIDE && renderer.profile.features.player.detectSlim) return if (skin.isReallyWide()) SkinModel.WIDE else SkinModel.SLIM return skin.model } else { this.skin = skin.default @@ -110,6 +112,28 @@ open class PlayerRenderer(renderer: EntitiesRenderer, entity: return renderer.context.models.skeletal[name] } + private fun PlayerSkin.isReallyWide(): Boolean { + val data = this.texture.data ?: return true + + // check if normal pixel is not black + if (data[40, 16].isBlack()) return true // left arm slim + if (data[32, 48].isBlack()) return true // right arm slim + + if (!data[52, 20].isBlack()) return true // left arm wide + if (!data[53, 31].isBlack()) return true // left arm wide + + if (!data[44, 52].isBlack()) return true // right arm wide + if (!data[45, 63].isBlack()) return true // right arm wide + + return false + } + + private fun Int.isBlack(): Boolean { + if (this and 0xFF == 0x00) return true // alpha + if (this shr 8 == 0x00) return true // rgb is black + return false + } + override fun onDynamicTextureChange(texture: DynamicTexture): Boolean { if (texture.state != DynamicTextureState.LOADED) return false this.skin = texture diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/TextureData.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/TextureData.kt index 467c6d032..2cf400649 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/TextureData.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/TextureData.kt @@ -27,6 +27,15 @@ open class TextureData( open fun collect(): Array = arrayOf(buffer) + private operator fun get(offset: Int): Int { + return buffer[offset].toInt() and 0xFF + } + + operator fun get(x: Int, y: Int): Int { + val offset = ((size.x * y) + x) * 4 + return (this[offset + 0] shl 24) or (this[offset + 1] shl 16) or (this[offset + 2] shl 8) or (this[offset + 3] shl 0) + } + companion object { val NULL = ObjenesisStd().newInstance(TextureData::class.java) }