├── versions ├── mainProject ├── 1.19.3-fabric │ └── dependencies.gradle ├── 1.19.3-forge │ └── dependencies.gradle ├── 1.19.4-fabric │ └── dependencies.gradle ├── 1.19.4-forge │ └── dependencies.gradle ├── 1.20.1-fabric │ └── dependencies.gradle ├── 1.20.2-fabric │ └── dependencies.gradle ├── 1.20.2-neoforge │ └── dependencies.gradle ├── 1.20.4-fabric │ └── dependencies.gradle ├── 1.20.4-neoforge │ └── dependencies.gradle ├── 1.20.5-fabric │ └── dependencies.gradle ├── 1.20.6-fabric │ └── dependencies.gradle ├── 1.20.6-neoforge │ └── dependencies.gradle ├── 1.21.1-fabric │ └── dependencies.gradle ├── 1.21.1-neoforge │ └── dependencies.gradle ├── 1.21.10-fabric │ └── dependencies.gradle ├── 1.21.11-fabric │ └── dependencies.gradle ├── 1.21.3-fabric │ └── dependencies.gradle ├── 1.21.3-neoforge │ └── dependencies.gradle ├── 1.21.4-fabric │ └── dependencies.gradle ├── 1.21.4-neoforge │ └── dependencies.gradle ├── 1.21.5-fabric │ └── dependencies.gradle ├── 1.21.5-neoforge │ └── dependencies.gradle ├── 1.21.8-fabric │ └── dependencies.gradle ├── 1.21.8-neoforge │ └── dependencies.gradle ├── 1.21.10-neoforge │ └── dependencies.gradle ├── 1.21.11-neoforge │ └── dependencies.gradle ├── 1.16.5-forge │ └── src │ │ └── main │ │ └── resources │ │ └── META-INF │ │ └── accesstransformer.cfg └── 1.16.5-fabric │ └── src │ └── main │ └── resources │ ├── skinlayers3d.accesswidener │ └── fabric.mod.json ├── .github ├── FUNDING.yml └── workflows │ ├── tag.yml │ └── build.yml ├── img ├── MC_NU_BANNER1.png ├── MC_NU_BANNER2.png ├── MC_NU_BANNER3.png └── MC_NU_HEADER.png ├── gradle └── gradle-compose.jar ├── src ├── main │ ├── resources │ │ ├── icon.png │ │ ├── skinlayers3d.mixins.json │ │ └── assets │ │ │ └── skinlayers3d │ │ │ └── lang │ │ │ ├── cs_cz.json │ │ │ ├── uk_ua.json │ │ │ ├── pt_br.json │ │ │ ├── pt_pt.json │ │ │ ├── it_it.json │ │ │ ├── ja_jp.json │ │ │ ├── zh_tw.json │ │ │ ├── ko_kr.json │ │ │ ├── zh_cn.json │ │ │ ├── sv_se.json │ │ │ ├── fr_fr.json │ │ │ ├── pl_pl.json │ │ │ ├── es_mx.json │ │ │ ├── es_es.json │ │ │ ├── de_de.json │ │ │ ├── de_at.json │ │ │ ├── de_ch.json │ │ │ ├── tr_tr.json │ │ │ ├── ru_ru.json │ │ │ ├── vi_vn.json │ │ │ ├── en_us.json │ │ │ └── es_ar.json │ └── java │ │ └── dev │ │ └── tr7zw │ │ └── skinlayers │ │ ├── accessor │ │ ├── NativeImageAccessor.java │ │ ├── SkullModelAccessor.java │ │ ├── SkullModelStateAccessor.java │ │ ├── HttpTextureAccessor.java │ │ ├── PlayerEntityModelAccessor.java │ │ ├── ModelPartInjector.java │ │ ├── SkullSettings.java │ │ └── PlayerSettings.java │ │ ├── api │ │ ├── SkullData.java │ │ ├── MeshProvider.java │ │ ├── MeshTransformerProvider.java │ │ ├── Shape.java │ │ ├── PlayerData.java │ │ ├── LayerFeatureTransformerAPI.java │ │ ├── MeshTransformer.java │ │ ├── BoxBuilder.java │ │ ├── MeshHelper.java │ │ ├── SkinLayersAPI.java │ │ ├── Mesh.java │ │ └── OffsetProvider.java │ │ ├── config │ │ ├── SkinLayersModMenu.java │ │ └── ConfigScreenProvider.java │ │ ├── mixin │ │ ├── NativeImageMixin.java │ │ ├── SkullModelStateMixin.java │ │ ├── Deadmau5EarsLayerMixin.java │ │ ├── SkullBlockEntityMixin.java │ │ ├── HttpTextureMixin.java │ │ ├── SkullModelMixin.java │ │ ├── EMFModelPartMixin.java │ │ ├── PlayerMixin.java │ │ ├── ModelPartMixin.java │ │ ├── BlockEntityWithoutLevelRendererMixin.java │ │ ├── PlayerModelMixin.java │ │ └── CustomHeadLayerMixin.java │ │ ├── SkinLayersMod.java │ │ ├── util │ │ ├── SodiumWorkaround.java │ │ └── NMSWrapper.java │ │ ├── SkinLayersModBase.java │ │ ├── SkinLayersBootstrap.java │ │ ├── SkullRendererCache.java │ │ ├── render │ │ └── CustomizableCubeListBuilder.java │ │ ├── renderlayers │ │ └── CustomLayerFeatureRenderer.java │ │ └── SkinUtil.java └── test │ └── java │ └── dev │ └── tr7zw │ └── tests │ └── MixinTests.java ├── 3dSkinLayers-Versionless └── src │ └── main │ └── java │ └── dev │ └── tr7zw │ └── skinlayers │ └── versionless │ ├── util │ ├── wrapper │ │ ├── TextureData.java │ │ └── ModelBuilder.java │ ├── Direction.java │ └── Vector3.java │ ├── config │ └── Config.java │ ├── ModBase.java │ └── render │ ├── CustomModelPart.java │ └── CustomizableCube.java ├── .gitignore ├── LICENSE ├── settings.json ├── gradle-compose.yml ├── gradlecw.bat ├── gradlecw └── README.md /versions/mainProject: -------------------------------------------------------------------------------- 1 | 1.21.11-fabric -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | ko_fi: tr7zw -------------------------------------------------------------------------------- /img/MC_NU_BANNER1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/img/MC_NU_BANNER1.png -------------------------------------------------------------------------------- /img/MC_NU_BANNER2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/img/MC_NU_BANNER2.png -------------------------------------------------------------------------------- /img/MC_NU_BANNER3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/img/MC_NU_BANNER3.png -------------------------------------------------------------------------------- /img/MC_NU_HEADER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/img/MC_NU_HEADER.png -------------------------------------------------------------------------------- /gradle/gradle-compose.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/gradle/gradle-compose.jar -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/HEAD/src/main/resources/icon.png -------------------------------------------------------------------------------- /versions/1.19.3-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.19.3-forge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.19.4-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.19.4-forge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.1-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:QoWmvvjv' 3 | } -------------------------------------------------------------------------------- /versions/1.20.2-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.2-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.4-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.4-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.5-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.6-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.20.6-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:TkjKemrg' 3 | } -------------------------------------------------------------------------------- /versions/1.21.1-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.1-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.10-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.11-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.3-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.3-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.4-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.4-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.5-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.5-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.8-fabric/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.8-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.10-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /versions/1.21.11-neoforge/dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | modCompileOnlyApi 'maven.modrinth:entity-model-features:cnCqWodo' 3 | } -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/NativeImageAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | public interface NativeImageAccessor { 4 | 5 | boolean skinlayers$isAllocated(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/SkullData.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | public interface SkullData { 4 | 5 | /** 6 | * @return The Mesh or null if there is none. 7 | */ 8 | public Mesh getMesh(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/SkullModelAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | import dev.tr7zw.skinlayers.api.Mesh; 4 | 5 | public interface SkullModelAccessor { 6 | 7 | public void injectHatMesh(Mesh mesh); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/SkullModelStateAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | public interface SkullModelStateAccessor { 4 | 5 | public SkullSettings getSkullSettings(); 6 | 7 | public void setSkullSettings(SkullSettings settings); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/HttpTextureAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | import java.io.FileNotFoundException; 4 | 5 | import com.mojang.blaze3d.platform.NativeImage; 6 | 7 | public interface HttpTextureAccessor { 8 | 9 | public NativeImage getImage() throws FileNotFoundException; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/PlayerEntityModelAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | /** 4 | * Used to expose the thinArms setting of the player model 5 | * 6 | */ 7 | public interface PlayerEntityModelAccessor { 8 | public boolean hasThinArms(); 9 | 10 | void setIgnored(boolean ignore); 11 | } 12 | -------------------------------------------------------------------------------- /versions/1.16.5-forge/src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.renderer.model.ModelRenderer$ModelBox field_78254_i # polygons 2 | public net.minecraft.client.renderer.model.ModelRenderer$TexturedQuad 3 | public net.minecraft.client.renderer.model.ModelRenderer$PositionTextureVertex 4 | public net.minecraft.client.renderer.model.ModelRenderer field_78805_m # children -------------------------------------------------------------------------------- /versions/1.16.5-fabric/src/main/resources/skinlayers3d.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | accessible field net/minecraft/client/model/geom/ModelPart$Cube polygons [Lnet/minecraft/client/model/geom/ModelPart$Polygon; 3 | accessible class net/minecraft/client/model/geom/ModelPart$Polygon 4 | accessible class net/minecraft/client/model/geom/ModelPart$Vertex 5 | accessible field net/minecraft/client/model/geom/ModelPart children Lit/unimi/dsi/fastutil/objects/ObjectList; -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/MeshProvider.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import net.minecraft.client.player.AbstractClientPlayer; 4 | 5 | public interface MeshProvider { 6 | 7 | /** 8 | * Returns the PlayerData for the given player. 9 | * 10 | * @param abstractClientPlayerEntity 11 | * @param layer 12 | * @return 13 | */ 14 | public PlayerData getPlayerMesh(AbstractClientPlayer abstractClientPlayerEntity); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/config/SkinLayersModMenu.java: -------------------------------------------------------------------------------- 1 | //? if fabric { 2 | 3 | package dev.tr7zw.skinlayers.config; 4 | 5 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 6 | import com.terraformersmc.modmenu.api.ModMenuApi; 7 | 8 | public class SkinLayersModMenu implements ModMenuApi { 9 | 10 | @Override 11 | public ConfigScreenFactory getModConfigScreenFactory() { 12 | return parent -> { 13 | return ConfigScreenProvider.createConfigScreen(parent); 14 | }; 15 | } 16 | 17 | } 18 | //? } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/MeshTransformerProvider.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import net.minecraft.client.model.geom.ModelPart; 6 | 7 | /** 8 | * Interface for mods like bendy-lib to interface. 9 | * 10 | * @author tr7zw 11 | * 12 | */ 13 | public interface MeshTransformerProvider { 14 | 15 | public MeshTransformer prepareTransformer(@Nullable ModelPart vanillaModel); 16 | 17 | public static final MeshTransformerProvider EMPTY_PROVIDER = (cube) -> MeshTransformer.EMPTY_TRANSFORMER; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/ModelPartInjector.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | 5 | import dev.tr7zw.skinlayers.api.Mesh; 6 | import dev.tr7zw.skinlayers.api.OffsetProvider; 7 | 8 | public interface ModelPartInjector { 9 | 10 | public void setInjectedMesh(Mesh mesh, OffsetProvider offsetProvider); 11 | 12 | public boolean isVisible(); 13 | 14 | public Mesh getInjectedMesh(); 15 | 16 | public OffsetProvider getOffsetProvider(); 17 | 18 | public void prepareTranslateAndRotate(PoseStack poseStack); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/NativeImageMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | 6 | import com.mojang.blaze3d.platform.NativeImage; 7 | 8 | import dev.tr7zw.skinlayers.accessor.NativeImageAccessor; 9 | 10 | @Mixin(NativeImage.class) 11 | public abstract class NativeImageMixin implements NativeImageAccessor { 12 | 13 | @Shadow 14 | private long pixels; 15 | 16 | @Override 17 | public boolean skinlayers$isAllocated() { 18 | return pixels != 0L; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/util/wrapper/TextureData.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.util.wrapper; 2 | 3 | import dev.tr7zw.skinlayers.versionless.util.wrapper.SolidPixelWrapper.UV; 4 | 5 | public interface TextureData { 6 | 7 | public boolean isPresent(UV onTextureUV); 8 | 9 | public boolean isSolid(UV onTextureUV); 10 | 11 | public default int getWidth() { 12 | return 64; // Default width, can be overridden if needed 13 | } 14 | 15 | public default int getHeight() { 16 | return 64; // Default height, can be overridden if needed 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/util/wrapper/ModelBuilder.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.util.wrapper; 2 | 3 | import dev.tr7zw.skinlayers.versionless.util.Direction; 4 | 5 | public interface ModelBuilder { 6 | 7 | ModelBuilder textureSize(int width, int height); 8 | 9 | ModelBuilder uv(int u, int v); 10 | 11 | ModelBuilder mirror(boolean bl); 12 | 13 | ModelBuilder addBox(float x, float y, float z, float pixelSize, Direction[] hide, Direction[][] corners); 14 | 15 | ModelBuilder addVanillaBox(float x, float y, float z, float width, float height, float depth); 16 | 17 | boolean isEmpty(); 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/SkinLayersMod.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers; 2 | 3 | //? if fabric { 4 | 5 | import net.fabricmc.api.ClientModInitializer; 6 | 7 | public class SkinLayersMod extends SkinLayersModBase implements ClientModInitializer { 8 | 9 | @Override 10 | public void onInitializeClient() { 11 | this.onInitialize(); 12 | } 13 | 14 | //? } else { 15 | /* 16 | import dev.tr7zw.transition.loader.ModLoaderUtil; 17 | public class SkinLayersMod extends SkinLayersModBase { 18 | public SkinLayersMod() { 19 | ModLoaderUtil.registerClientSetupListener(this::onInitialize); 20 | } 21 | *///? } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/Shape.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import dev.tr7zw.skinlayers.versionless.util.wrapper.SolidPixelWrapper.Dimensions; 4 | 5 | public record Shape(float yOffsetMagicValue, Dimensions dimensions) { 6 | public static final Shape HEAD = new Shape(0.0f, new Dimensions(8, 8, 8)); 7 | public static final Shape BODY = new Shape(-0.2f, new Dimensions(8, 12, 4)); 8 | public static final Shape LEGS = new Shape(-0.2f, new Dimensions(4, 14, 4)); 9 | public static final Shape ARMS = new Shape(-0.1f, new Dimensions(4, 14, 4)); 10 | public static final Shape ARMS_SLIM = new Shape(-0.1f, new Dimensions(3, 14, 4)); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/skinlayers3d.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "dev.tr7zw.skinlayers.mixin", 5 | "refmap": "skinlayers3d.refmap.mixins.json", 6 | "mixins": [], 7 | "client": [ 8 | "PlayerRendererMixin", 9 | "PlayerMixin", 10 | "PlayerModelMixin", 11 | "SkullBlockEntityRendererMixin", 12 | "SkullModelMixin", 13 | "SkullBlockEntityMixin", 14 | "BlockEntityWithoutLevelRendererMixin", 15 | "CustomHeadLayerMixin", 16 | "HttpTextureMixin", 17 | "ModelPartMixin", 18 | "EMFModelPartMixin", 19 | "NativeImageMixin", 20 | "SkullModelStateMixin", 21 | "Deadmau5EarsLayerMixin" 22 | ], 23 | "injectors": { 24 | "defaultRequire": 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/SkullSettings.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | import dev.tr7zw.skinlayers.api.Mesh; 4 | import dev.tr7zw.skinlayers.api.SkullData; 5 | import net.minecraft.resources.*; 6 | 7 | public interface SkullSettings extends SkullData { 8 | 9 | public Mesh getHeadLayers(); 10 | 11 | public void setupHeadLayers(Mesh box); 12 | 13 | public boolean initialized(); 14 | 15 | public void setInitialized(boolean initialized); 16 | 17 | public void setLastTexture(/*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ texture); 18 | 19 | public /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ getLastTexture(); 20 | 21 | @Override 22 | default Mesh getMesh() { 23 | return getHeadLayers(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/PlayerData.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | public interface PlayerData { 4 | 5 | /** 6 | * @return The Mesh or null if there is none. 7 | */ 8 | public Mesh getHeadMesh(); 9 | 10 | /** 11 | * @return The Mesh or null if there is none. 12 | */ 13 | public Mesh getTorsoMesh(); 14 | 15 | /** 16 | * @return The Mesh or null if there is none. 17 | */ 18 | public Mesh getLeftArmMesh(); 19 | 20 | /** 21 | * @return The Mesh or null if there is none. 22 | */ 23 | public Mesh getRightArmMesh(); 24 | 25 | /** 26 | * @return The Mesh or null if there is none. 27 | */ 28 | public Mesh getLeftLegMesh(); 29 | 30 | /** 31 | * @return The Mesh or null if there is none. 32 | */ 33 | public Mesh getRightLegMesh(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/SkullModelStateMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | import dev.tr7zw.skinlayers.accessor.SkullModelStateAccessor; 6 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | //? if >= 1.21.9 { 10 | 11 | //? if >= 1.21.11 { 12 | 13 | import net.minecraft.client.model.object.skull.*; 14 | //? } 15 | import net.minecraft.client.model.*; 16 | 17 | @Mixin(SkullModel.State.class) 18 | public class SkullModelStateMixin implements SkullModelStateAccessor { 19 | 20 | @Getter 21 | @Setter 22 | private SkullSettings skullSettings = null; 23 | 24 | } 25 | //? } else { 26 | /* 27 | @Mixin(targets = "net.minecraft.client.Minecraft") // dummy for older versions 28 | public class SkullModelStateMixin {} 29 | *///? } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | logs/ 35 | 36 | # compose 37 | 38 | *.gradle 39 | *.properties 40 | gradle/wrapper 41 | VersionShared 42 | UtilityCode 43 | formatter.xml 44 | src/main/fabric-resources/fabric.mod.json 45 | src/main/forge-resources/META-INF/mods.toml 46 | src/main/neoforge-resources/META-INF/mods.toml 47 | **/src/main/java/dev/tr7zw/config/CustomConfigScreen.java 48 | **/src/main/java/dev/tr7zw/util/ComponentProvider.java 49 | src/main/resources/pack.mcmeta 50 | src/main/java/dev/tr7zw/util/ModLoaderUtil.java 51 | src/main/java/dev/tr7zw/util/NMSHelper.java 52 | src/main/neoforge-resources/META-INF/neoforge.mods.toml 53 | /src/main/java/dev/tr7zw/util 54 | -------------------------------------------------------------------------------- /versions/1.16.5-fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${mod_id}", 4 | "version": "${version}", 5 | "name": "${mod_name}", 6 | "description": "${description}", 7 | "authors": [ 8 | "${mod_author}" 9 | ], 10 | "contact": { 11 | "homepage": "${homepage_url}", 12 | "sources": "${sources_url}" 13 | }, 14 | "license": "${license_name}", 15 | "icon": "icon.png", 16 | "accessWidener": "${mod_id}.accesswidener", 17 | "entrypoints": { 18 | "client": [ 19 | "${fabric_entrypoint}" 20 | ], 21 | "modmenu": [ 22 | "${fabric_modmenu_entrypoint}" 23 | ] 24 | }, 25 | "mixins": [ 26 | "${mod_id}.mixins.json" 27 | ], 28 | "depends": { 29 | "minecraft": "${minecraft_version_range_fabric}", 30 | "${fabric_api_name}": "*" 31 | }, 32 | "breaks": { 33 | "tlskincape": "*" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/LayerFeatureTransformerAPI.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | 5 | import net.minecraft.client.model.geom.ModelPart; 6 | import net.minecraft.client.player.AbstractClientPlayer; 7 | 8 | public class LayerFeatureTransformerAPI { 9 | 10 | private static LayerTransformer layerTransformer = LayerTransformer.IDENTITY; 11 | 12 | public static LayerTransformer getTransformer() { 13 | return layerTransformer; 14 | } 15 | 16 | public static void setLayerTransformer(LayerTransformer layerTransformer) { 17 | LayerFeatureTransformerAPI.layerTransformer = layerTransformer; 18 | } 19 | 20 | @FunctionalInterface 21 | public interface LayerTransformer { 22 | LayerTransformer IDENTITY = (abstractClientPlayer, matrixStack, modelPart) -> { 23 | }; 24 | 25 | void transform(AbstractClientPlayer abstractClientPlayer, PoseStack matrixStack, ModelPart modelPart); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/MeshTransformer.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | //? if >= 1.19.3 { 4 | 5 | import org.joml.Vector3f; 6 | import org.joml.Vector4f; 7 | //? } else { 8 | 9 | // import com.mojang.math.Vector3f; 10 | // import com.mojang.math.Vector4f; 11 | //? } 12 | 13 | import net.minecraft.client.model.geom.ModelPart.Cube; 14 | 15 | /** 16 | * Interface for mods like bendy-lib to interface. 17 | * 18 | * @author tr7zw 19 | * 20 | */ 21 | public interface MeshTransformer { 22 | 23 | public void transform(Vector3f position, Vector4f[] vertexData); 24 | 25 | public void transform(Cube cube); 26 | 27 | public static final MeshTransformer EMPTY_TRANSFORMER = new MeshTransformer() { 28 | 29 | @Override 30 | public void transform(Cube cube) { 31 | // do nothing 32 | } 33 | 34 | @Override 35 | public void transform(Vector3f position, Vector4f[] vertexData) { 36 | // do nothing 37 | } 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | tr7zw Protective License 2 | 3 | Copyright (c) tr7zw, 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software (in source or binary form) and associated documentation files 7 | (the "Software"), to use, modify and compile the Software, subject to the 8 | following conditions: 9 | 10 | The Software may not be used to get a) a commercial advantage, or b) monetary 11 | compensation. 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/cs_cz.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Nastavení 3D Skin Layer", 3 | "text.skinlayers.enable.hat": "3D pokrývka hlavy", 4 | "text.skinlayers.enable.jacket": "3D bunda", 5 | "text.skinlayers.enable.leftsleeve": "3D levý rukáv", 6 | "text.skinlayers.enable.rightsleeve": "3D pravý rukáv", 7 | "text.skinlayers.enable.leftpants": "3D levá nohavice", 8 | "text.skinlayers.enable.rightpants": "3D pravá nohavice", 9 | "text.skinlayers.renderdistancelod": "Vzdálenost detailů", 10 | "text.skinlayers.basevoxelsize": "Velikost voxelů", 11 | "text.skinlayers.headvoxelsize": "Velikost voxelů hlavy", 12 | "text.skinlayers.bodyvoxelheightsize": "Výška voxelů hrudi", 13 | "text.skinlayers.bodyvoxelwidthsize": "Šířka voxelů hrudi", 14 | "text.skinlayers.skulls.enable": "3D lebky", 15 | "text.skinlayers.skullsitems.enable": "3D itemy lebek", 16 | "text.skinlayers.skulls.voxelsize": "Velikost voxelů lebek", 17 | "text.skinlayers.reset": "Výchozí", 18 | "text.skinlayers.fastrender.enable": "Rychlé vykreslování" 19 | } 20 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/config/Config.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.config; 2 | 3 | public class Config { 4 | 5 | public boolean enableHat = true; 6 | public boolean enableJacket = true; 7 | public boolean enableLeftSleeve = true; 8 | public boolean enableRightSleeve = true; 9 | public boolean enableLeftPants = true; 10 | public boolean enableRightPants = true; 11 | 12 | public float baseVoxelSize = 1.15f; 13 | public float bodyVoxelWidthSize = 1.05f; 14 | // public float bodyVoxelHeightSize = 1.02f; 15 | public float headVoxelSize = 1.18f; 16 | 17 | public int renderDistanceLOD = 14; 18 | 19 | public boolean enableSkulls = true; 20 | public boolean enableSkullsItems = true; 21 | public float skullVoxelSize = 1.1f; 22 | 23 | public boolean fastRender = true; 24 | public boolean compatibilityMode = true; 25 | public boolean irisCompatibilityMode = false; 26 | public boolean applySodiumWorkaround = false; 27 | 28 | public float firstPersonPixelScaling = 1.1f; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/uk_ua.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Параметри 3D Skin Layers", 3 | "text.skinlayers.enable.hat": "3D-капелюх", 4 | "text.skinlayers.enable.jacket": "3D-куртка", 5 | "text.skinlayers.enable.leftsleeve": "3D-лівий рукав", 6 | "text.skinlayers.enable.rightsleeve": "3D-правий рукав", 7 | "text.skinlayers.enable.leftpants": "3D-ліва штанина", 8 | "text.skinlayers.enable.rightpants": "3D-права штанина", 9 | "text.skinlayers.renderdistancelod": "Відст. зменш. деталізації", 10 | "text.skinlayers.basevoxelsize": "Розмір вокселя", 11 | "text.skinlayers.headvoxelsize": "Розмір вокселя голови", 12 | "text.skinlayers.bodyvoxelheightsize": "Висота вокселя тіла", 13 | "text.skinlayers.bodyvoxelwidthsize": "Ширина вокселя тіла", 14 | "text.skinlayers.skulls.enable": "3D-черепи", 15 | "text.skinlayers.skullsitems.enable": "3D-предмети черепів", 16 | "text.skinlayers.skulls.voxelsize": "Розмір вокселя черепа", 17 | "text.skinlayers.reset": "Скинути", 18 | "text.skinlayers.fastrender.enable": "Швидкий рендер" 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/tag.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: checkout repository 10 | uses: actions/checkout@v2 11 | - name: setup jdk 21 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 21 15 | - name: Get Release Changelog 16 | run: | 17 | # Read the event payload to get the changelog and save it to a file 18 | changelog=$(jq -r '.release.body' $GITHUB_EVENT_PATH) 19 | echo "$changelog" > changelog.md 20 | echo "Changelog saved to changelog.md" 21 | - name: make gradle wrapper executable 22 | run: chmod +x ./gradlecw 23 | - name: build 24 | run: ./gradlecw build publishMods -Pbuild.release=true --info 25 | env: 26 | CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} 27 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 28 | - name: Release 29 | uses: softprops/action-gh-release@v1 30 | with: 31 | files: 'versions/**/build/libs/!(*-@(dev|sources|javadoc|all)).jar' -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | "1.16.5-fabric", 4 | "1.18.2-forge", 5 | "1.18.2-fabric", 6 | "1.19.2-forge", 7 | "1.19.2-fabric", 8 | "1.19.4-forge", 9 | "1.19.4-fabric", 10 | "1.20.1-forge", 11 | "1.20.1-fabric", 12 | "1.20.2-forge", 13 | "1.20.2-neoforge", 14 | "1.20.2-fabric", 15 | "1.20.4-forge", 16 | "1.20.4-neoforge", 17 | "1.20.4-fabric", 18 | "1.20.6-forge", 19 | "1.20.6-neoforge", 20 | "1.20.6-fabric", 21 | "1.21.1-forge", 22 | "1.21.1-neoforge", 23 | "1.21.1-fabric", 24 | "1.21.3-forge", 25 | "1.21.3-neoforge", 26 | "1.21.3-fabric", 27 | "1.21.4-forge", 28 | "1.21.4-neoforge", 29 | "1.21.4-fabric", 30 | "1.21.5-forge", 31 | "1.21.5-neoforge", 32 | "1.21.5-fabric", 33 | "1.21.8-forge", 34 | "1.21.8-neoforge", 35 | "1.21.8-fabric", 36 | "1.21.10-forge", 37 | "1.21.10-neoforge", 38 | "1.21.10-fabric", 39 | "1.21.11-neoforge", 40 | "1.21.11-fabric" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/accessor/PlayerSettings.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.accessor; 2 | 3 | import dev.tr7zw.skinlayers.api.Mesh; 4 | import dev.tr7zw.skinlayers.api.PlayerData; 5 | import net.minecraft.resources.*; 6 | 7 | public interface PlayerSettings extends PlayerData { 8 | 9 | public void setHeadMesh(Mesh box); 10 | 11 | public void setTorsoMesh(Mesh box); 12 | 13 | public void setLeftArmMesh(Mesh box); 14 | 15 | public void setRightArmMesh(Mesh box); 16 | 17 | public void setLeftLegMesh(Mesh box); 18 | 19 | public void setRightLegMesh(Mesh box); 20 | 21 | public /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ getCurrentSkin(); 22 | 23 | public void setCurrentSkin(/*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ skin); 24 | 25 | public boolean hasThinArms(); 26 | 27 | public void setThinArms(boolean thin); 28 | 29 | public default void clearMeshes() { 30 | setHeadMesh(null); 31 | setTorsoMesh(null); 32 | setLeftArmMesh(null); 33 | setRightArmMesh(null); 34 | setLeftLegMesh(null); 35 | setRightLegMesh(null); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/util/SodiumWorkaround.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.util; 2 | 3 | import dev.tr7zw.skinlayers.*; 4 | 5 | public class SodiumWorkaround { 6 | 7 | public static final boolean IS_SODIUM_WORKAROUND_NEEDED = isSodiumWorkaroundNeeded(); // This is a workaround to ensure the ModelPartData is loaded 8 | private static final boolean IS_SODIUM_LOADED = isSodiumLoaded(); 9 | 10 | private static boolean isSodiumWorkaroundNeeded() { 11 | try { 12 | Class.forName("me.jellysquid.mods.sodium.client.render.immediate.model.ModelPartData"); 13 | return true; 14 | } catch (ClassNotFoundException e) { 15 | return false; 16 | } 17 | } 18 | 19 | private static boolean isSodiumLoaded() { 20 | try { 21 | Class.forName("net.caffeinemc.mods.sodium.client.render.immediate.model.ModelCuboid"); 22 | return true; 23 | } catch (ClassNotFoundException e) { 24 | return false; 25 | } 26 | } 27 | 28 | public static boolean applySodiumWorkaround() { 29 | return IS_SODIUM_LOADED && SkinLayersMod.config.applySodiumWorkaround; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/util/NMSWrapper.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.util; 2 | 3 | import com.mojang.blaze3d.platform.NativeImage; 4 | 5 | import dev.tr7zw.skinlayers.versionless.util.wrapper.SolidPixelWrapper.UV; 6 | import dev.tr7zw.skinlayers.versionless.util.wrapper.TextureData; 7 | 8 | public class NMSWrapper { 9 | 10 | public static class WrappedNativeImage implements TextureData { 11 | 12 | private final NativeImage natImage; 13 | 14 | public WrappedNativeImage(NativeImage natImage) { 15 | this.natImage = natImage; 16 | } 17 | 18 | @Override 19 | public boolean isPresent(UV onTextureUV) { 20 | return natImage.getLuminanceOrAlpha(onTextureUV.u(), onTextureUV.v()) != 0; 21 | } 22 | 23 | @Override 24 | public boolean isSolid(UV onTextureUV) { 25 | return natImage.getLuminanceOrAlpha(onTextureUV.u(), onTextureUV.v()) == -1; 26 | } 27 | 28 | @Override 29 | public int getWidth() { 30 | return natImage.getWidth(); 31 | } 32 | 33 | @Override 34 | public int getHeight() { 35 | return natImage.getHeight(); 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/pt_br.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Configurações do 3D Skin Layer", 3 | "text.skinlayers.enable.hat": "Chapéu 3D", 4 | "text.skinlayers.enable.jacket": "Casaco 3D", 5 | "text.skinlayers.enable.leftsleeve": "Manga Esquerda 3D", 6 | "text.skinlayers.enable.rightsleeve": "Manga Direita 3D", 7 | "text.skinlayers.enable.leftpants": "Perna Esquerda 3D", 8 | "text.skinlayers.enable.rightpants": "Perna Direita 3D", 9 | "text.skinlayers.renderdistancelod": "Distância do Nível de Detalhe", 10 | "text.skinlayers.basevoxelsize": "Tamanho do Voxel", 11 | "text.skinlayers.headvoxelsize": "Tamanho do Voxel na Cabeça", 12 | "text.skinlayers.bodyvoxelheightsize": "Altura do Voxel do Corpo", 13 | "text.skinlayers.bodyvoxelwidthsize": "Largura do Voxel do Tronco", 14 | "text.skinlayers.skulls.enable": "Cabeças em 3D", 15 | "text.skinlayers.skullsitems.enable": "Itens nas Cabeças em 3D", 16 | "text.skinlayers.skulls.voxelsize": "Tamanho do Voxel nas Cabeças", 17 | "text.skinlayers.reset": "Restaurar", 18 | "text.skinlayers.fastrender.enable": "Renderização Rápida", 19 | "text.skinlayers.firstperson.voxelsize": "Tamanho do Voxel em Primeira Pessoa" 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/pt_pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Configurações do 3D Skin Layer", 3 | "text.skinlayers.enable.hat": "Chapéu 3D", 4 | "text.skinlayers.enable.jacket": "Casaco 3D", 5 | "text.skinlayers.enable.leftsleeve": "Manga Esquerda 3D", 6 | "text.skinlayers.enable.rightsleeve": "Manga Direita 3D", 7 | "text.skinlayers.enable.leftpants": "Perna Esquerda 3D", 8 | "text.skinlayers.enable.rightpants": "Perna Direita 3D", 9 | "text.skinlayers.renderdistancelod": "Distância do Nível de Detalhe", 10 | "text.skinlayers.basevoxelsize": "Tamanho do Voxel", 11 | "text.skinlayers.headvoxelsize": "Tamanho do Voxel na Cabeça", 12 | "text.skinlayers.bodyvoxelheightsize": "Altura do Voxel do Corpo", 13 | "text.skinlayers.bodyvoxelwidthsize": "Largura do Voxel do Tronco", 14 | "text.skinlayers.skulls.enable": "Cabeças em 3D", 15 | "text.skinlayers.skullsitems.enable": "Itens nas Cabeças em 3D", 16 | "text.skinlayers.skulls.voxelsize": "Tamanho do Voxel nas Cabeças", 17 | "text.skinlayers.reset": "Restaurar", 18 | "text.skinlayers.fastrender.enable": "Renderização Rápida", 19 | "text.skinlayers.firstperson.voxelsize": "Tamanho do Voxel em Primeira Pessoa" 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/it_it.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Impostazioni di 3D Skin Layers", 3 | "text.skinlayers.enable.hat": "Cappello 3D", 4 | "text.skinlayers.enable.jacket": "Giacca 3D", 5 | "text.skinlayers.enable.leftsleeve": "Mano sinistra 3D", 6 | "text.skinlayers.enable.rightsleeve": "Mano destra 3D", 7 | "text.skinlayers.enable.leftpants": "Pantaloncini sinistri 3D", 8 | "text.skinlayers.enable.rightpants": "Pantaloncini destri 3D", 9 | "text.skinlayers.renderdistancelod": "Livello di distanza dettagliata", 10 | "text.skinlayers.basevoxelsize": "Grandezza del Voxel", 11 | "text.skinlayers.headvoxelsize": "Grandezza del Voxel in testa", 12 | "text.skinlayers.bodyvoxelheightsize": "Grandezza del Voxel in corpo", 13 | "text.skinlayers.bodyvoxelwidthsize": "Grandezza del Voxel nel torso", 14 | "text.skinlayers.skulls.enable": "Teschi 3D", 15 | "text.skinlayers.skullsitems.enable": "Articoli 3D Skull", 16 | "text.skinlayers.skulls.voxelsize": "Dimensione dei voxel del cranio", 17 | "text.skinlayers.reset": "Predefinito", 18 | "text.skinlayers.fastrender.enable": "Fast Render", 19 | "text.skinlayers.firstperson.voxelsize": "Dimenisione del Voxel in prima persona" 20 | } 21 | -------------------------------------------------------------------------------- /gradle-compose.yml: -------------------------------------------------------------------------------- 1 | version: '0.0.2' 2 | source: "https://github.com/tr7zw/ProcessedModTemplate/tree/stonecutter" 3 | replacements: 4 | name: "3d-Skin-Layers" 5 | id: "skinlayers3d" 6 | version: "1.10.1" 7 | description: "Renders the player skin layer in 3d" 8 | homepageUrl: "https://modrinth.com/mod/3dskinlayers" 9 | sourcesUrl: "https://github.com/tr7zw/3d-skin-layers" 10 | issuesUrl: "https://github.com/tr7zw/3d-skin-layers/issues" 11 | fabric_entrypoint: "dev.tr7zw.skinlayers.SkinLayersMod" 12 | fabric_modmenu_entrypoint: "dev.tr7zw.skinlayers.config.SkinLayersModMenu" 13 | relocationpackage: "dev.tr7zw.skinlayers" 14 | modrinthid: zV5r3pPn 15 | curseforgeid: 521480 16 | versionlessname: "3dSkinLayers-Versionless" 17 | enabledFlags: 18 | - autopublish 19 | - publishFabric 20 | - publishForge 21 | - publishNeo 22 | - modrinth 23 | - curseforge 24 | - versionless 25 | - includeLibs 26 | - mixinextras 27 | - addTRenderLib 28 | - addTRansitionLib 29 | rootProject: 30 | template: "." 31 | subProjects: 32 | 3dSkinLayers-Versionless: 33 | template: "Versionless" 34 | replacements: 35 | dependencies: ' 36 | compileOnly "com.google.code.gson:gson:2.10.1" 37 | 38 | compileOnly "org.apache.logging.log4j:log4j-core:2.20.0" 39 | ' -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/BoxBuilder.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import net.minecraft.client.model.geom.ModelPart.Cube; 4 | //? if >= 1.17.0 { 5 | 6 | import net.minecraft.client.model.geom.builders.*; 7 | //? } 8 | 9 | public interface BoxBuilder { 10 | 11 | Cube build(BoxDefinition box); 12 | 13 | record BoxDefinition(int u, int v, boolean mirror, float x, float y, float z, float width, float height, 14 | float depth, int textureWidth, int textureHeight) { 15 | } 16 | 17 | public static final BoxBuilder DEFAULT = new BoxBuilder() { 18 | @Override 19 | public Cube build(BoxDefinition box) { 20 | //? if >= 1.17.0 { 21 | 22 | CubeListBuilder cubeList = CubeListBuilder.create(); 23 | cubeList.texOffs(box.u(), box.v()).mirror(box.mirror()).addBox(box.x(), box.y(), box.z(), box.width(), 24 | box.height(), box.depth()); 25 | return cubeList.getCubes().get(0).bake(box.textureWidth(), box.textureHeight()); 26 | //? } else { 27 | /* 28 | return new Cube(box.u(), box.v(), box.x(), box.y(), box.z(), box.width(), box.height(), box.depth(), 0, 0, 0, 29 | box.mirror(), box.textureWidth(), box.textureHeight()); 30 | *///? } 31 | } 32 | }; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | name: build 6 | on: [pull_request, push] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: checkout repository 12 | uses: actions/checkout@v2 13 | - name: setup jdk 21 14 | uses: actions/setup-java@v4 15 | with: 16 | distribution: temurin 17 | java-version: 21 18 | # to cache gradle files 19 | - name: Setup Gradle 20 | uses: gradle/actions/setup-gradle@v4 21 | with: 22 | gradle-home-cache-cleanup: true 23 | dependency-graph: generate-and-submit 24 | - name: build 25 | run: ./gradlecw build --stacktrace 26 | - name: capture build artifacts 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: Artifacts 30 | path: 'versions/**/build/libs/*.jar' 31 | 32 | # - name: Test Report 33 | # uses: dorny/test-reporter@v1 34 | # if: success() || failure() 35 | # with: 36 | # name: JUnit Tests 37 | # path: '**/build/test-results/test/TEST-*.xml' 38 | # reporter: java-junit -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/SkinLayersModBase.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers; 2 | 3 | import java.util.Set; 4 | 5 | import com.google.common.collect.Sets; 6 | 7 | import dev.tr7zw.skinlayers.accessor.PlayerSettings; 8 | import dev.tr7zw.skinlayers.config.ConfigScreenProvider; 9 | import dev.tr7zw.skinlayers.versionless.ModBase; 10 | import dev.tr7zw.transition.loader.ModLoaderUtil; 11 | import net.minecraft.world.entity.player.Player; 12 | import net.minecraft.world.item.Item; 13 | import net.minecraft.world.item.Items; 14 | 15 | public abstract class SkinLayersModBase extends ModBase { 16 | 17 | public static SkinLayersModBase instance; 18 | // TODO: Move somewhere else 19 | public static final Set hideHeadLayers = Sets.newHashSet(Items.ZOMBIE_HEAD, Items.CREEPER_HEAD, 20 | Items.DRAGON_HEAD, Items.SKELETON_SKULL, Items.WITHER_SKELETON_SKULL); 21 | 22 | protected SkinLayersModBase() { 23 | instance = this; 24 | ModLoaderUtil.disableDisplayTest(); 25 | ModLoaderUtil.registerConfigScreen(ConfigScreenProvider::createConfigScreen); 26 | } 27 | 28 | public void refreshLayers(Player player) { 29 | if (player == null || !(player instanceof PlayerSettings)) 30 | return; 31 | PlayerSettings settings = (PlayerSettings) player; 32 | settings.clearMeshes(); 33 | settings.setCurrentSkin(null); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/Deadmau5EarsLayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Final; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import dev.tr7zw.skinlayers.accessor.PlayerEntityModelAccessor; 11 | import net.minecraft.client.model.HumanoidModel; 12 | import net.minecraft.client.model.*; 13 | import net.minecraft.client.model.geom.*; 14 | import net.minecraft.client.renderer.entity.RenderLayerParent; 15 | import net.minecraft.client.renderer.entity.layers.*; 16 | 17 | //? if >= 1.21.11 { 18 | 19 | import net.minecraft.client.model.player.*; 20 | //? } 21 | 22 | @Mixin(Deadmau5EarsLayer.class) 23 | public class Deadmau5EarsLayerMixin { 24 | 25 | //? if >= 1.21.9 { 26 | 27 | @Shadow 28 | @Final 29 | private HumanoidModel model; 30 | 31 | @Inject(method = "", at = @At("TAIL")) 32 | private void init( 33 | RenderLayerParent renderer, 34 | EntityModelSet modelSet, CallbackInfo ci) { 35 | ((PlayerEntityModelAccessor) model).setIgnored(true); 36 | } 37 | //? } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/SkullBlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 6 | import dev.tr7zw.skinlayers.api.Mesh; 7 | import net.minecraft.resources.*; 8 | import net.minecraft.world.level.block.entity.SkullBlockEntity; 9 | 10 | @Mixin(SkullBlockEntity.class) 11 | public class SkullBlockEntityMixin implements SkullSettings { 12 | 13 | private Mesh hatModel = null; 14 | private boolean initialized = false; 15 | private /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ lastTexture = null; 16 | 17 | @Override 18 | public Mesh getHeadLayers() { 19 | return hatModel; 20 | } 21 | 22 | @Override 23 | public void setupHeadLayers(Mesh box) { 24 | this.hatModel = box; 25 | } 26 | 27 | @Override 28 | public boolean initialized() { 29 | return initialized; 30 | } 31 | 32 | @Override 33 | public void setInitialized(boolean initialized) { 34 | this.initialized = initialized; 35 | } 36 | 37 | @Override 38 | public void setLastTexture(/*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ texture) { 39 | this.lastTexture = texture; 40 | } 41 | 42 | @Override 43 | public /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ getLastTexture() { 44 | return lastTexture; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/HttpTextureMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | import net.minecraft.client.Minecraft; 6 | 7 | //? if >= 1.21.4 { 8 | 9 | @Mixin(Minecraft.class) 10 | public abstract class HttpTextureMixin { 11 | // Not a thing in 1.21.4+ 12 | } 13 | //? } else { 14 | /* 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.FileNotFoundException; 18 | import java.io.InputStream; 19 | 20 | import org.spongepowered.asm.mixin.Shadow; 21 | 22 | import com.mojang.blaze3d.platform.NativeImage; 23 | 24 | import dev.tr7zw.skinlayers.accessor.HttpTextureAccessor; 25 | import net.minecraft.client.renderer.texture.AbstractTexture; 26 | import net.minecraft.client.renderer.texture.HttpTexture; 27 | 28 | @Mixin(HttpTexture.class) 29 | public abstract class HttpTextureMixin extends AbstractTexture implements HttpTextureAccessor { 30 | 31 | @Shadow 32 | private File file; 33 | 34 | @Override 35 | public NativeImage getImage() throws FileNotFoundException { 36 | if (this.file != null && this.file.isFile()) { 37 | FileInputStream fileInputStream = new FileInputStream(this.file); 38 | NativeImage nativeImage = load(fileInputStream); 39 | return nativeImage; 40 | } 41 | return null; 42 | } 43 | 44 | @Shadow 45 | public abstract NativeImage load(InputStream inputStream); 46 | 47 | } 48 | *///? } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/SkinLayersBootstrap.java: -------------------------------------------------------------------------------- 1 | //? if forge { 2 | /* 3 | package dev.tr7zw.skinlayers; 4 | 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.fml.DistExecutor; 7 | import net.minecraftforge.fml.common.Mod; 8 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 9 | import dev.tr7zw.transition.loader.ModLoaderUtil; 10 | 11 | @Mod("skinlayers3d") 12 | public class SkinLayersBootstrap { 13 | 14 | public SkinLayersBootstrap(FMLJavaModLoadingContext context) { 15 | ModLoaderUtil.setModLoadingContext(context); 16 | DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> new SkinLayersMod().onInitialize()); 17 | } 18 | public SkinLayersBootstrap() { 19 | this(FMLJavaModLoadingContext.get()); 20 | } 21 | 22 | } 23 | *///? } else if neoforge { 24 | /* 25 | package dev.tr7zw.skinlayers; 26 | 27 | import net.neoforged.fml.common.Mod; 28 | import net.neoforged.fml.loading.FMLEnvironment; 29 | import dev.tr7zw.transition.loader.ModLoaderEventUtil; 30 | import net.neoforged.api.distmarker.Dist; 31 | 32 | @Mod("skinlayers3d") 33 | public class SkinLayersBootstrap { 34 | 35 | public SkinLayersBootstrap() { 36 | //? if < 1.21.9 { 37 | /^ 38 | if(FMLEnvironment.dist == Dist.CLIENT) { 39 | ^///? } else { 40 | 41 | if(FMLEnvironment.getDist() == Dist.CLIENT) { 42 | //? } 43 | ModLoaderEventUtil.registerClientSetupListener(() -> new SkinLayersMod().onInitialize()); 44 | } 45 | } 46 | 47 | } 48 | *///? } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/SkullRendererCache.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers; 2 | 3 | import java.util.WeakHashMap; 4 | 5 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 6 | import dev.tr7zw.skinlayers.api.Mesh; 7 | import net.minecraft.resources.*; 8 | //? if >= 1.21.4 { 9 | 10 | import com.mojang.authlib.GameProfile; 11 | //? } else { 12 | /* 13 | import net.minecraft.world.item.ItemStack; 14 | *///? } 15 | 16 | public class SkullRendererCache { 17 | 18 | public static boolean renderNext = false; 19 | public static SkullSettings lastSkull = null; 20 | //? if >= 1.21.4 { 21 | 22 | public static WeakHashMap itemCache = new WeakHashMap<>(); 23 | //? } else { 24 | /* 25 | public static WeakHashMap itemCache = new WeakHashMap<>(); 26 | *///? } 27 | 28 | public static class ItemSettings implements SkullSettings { 29 | 30 | private Mesh hatModel = null; 31 | private boolean initialized = false; 32 | 33 | @Override 34 | public Mesh getHeadLayers() { 35 | return hatModel; 36 | } 37 | 38 | @Override 39 | public void setupHeadLayers(Mesh box) { 40 | this.hatModel = box; 41 | } 42 | 43 | @Override 44 | public boolean initialized() { 45 | return initialized; 46 | } 47 | 48 | @Override 49 | public void setInitialized(boolean initialized) { 50 | this.initialized = initialized; 51 | } 52 | 53 | @Override 54 | public void setLastTexture(/*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ texture) { 55 | // TODO Auto-generated method stub 56 | 57 | } 58 | 59 | @Override 60 | public /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ getLastTexture() { 61 | // TODO Auto-generated method stub 62 | return null; 63 | } 64 | 65 | } 66 | 67 | public static void clearCache() { 68 | itemCache.clear(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/ModBase.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import com.google.gson.Gson; 12 | import com.google.gson.GsonBuilder; 13 | 14 | import dev.tr7zw.skinlayers.versionless.config.Config; 15 | 16 | public abstract class ModBase { 17 | 18 | public static final Logger LOGGER = LogManager.getLogger(); 19 | public static Config config = null; 20 | private File settingsFile = new File("config", "skinlayers.json"); 21 | private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 22 | public static boolean disguiseHeadsCompatibility = false; 23 | 24 | public void onInitialize() { 25 | if (settingsFile.exists()) { 26 | try { 27 | config = new Gson().fromJson( 28 | new String(Files.readAllBytes(settingsFile.toPath()), StandardCharsets.UTF_8), Config.class); 29 | } catch (Exception ex) { 30 | ex.printStackTrace(); 31 | } 32 | } 33 | if (config == null) { 34 | config = new Config(); 35 | writeConfig(); 36 | } 37 | try { 38 | Class clientClass = Class.forName("dev.tr7zw.disguiseheads.DisguiseHeadsShared"); 39 | disguiseHeadsCompatibility = clientClass != null; // to shut up the compiler that the var is not used 40 | LOGGER.info("Found DisguiseHeads, enable compatibility!"); 41 | } catch (Throwable ex) { 42 | // not installed 43 | } 44 | } 45 | 46 | public void writeConfig() { 47 | if (settingsFile.exists()) 48 | settingsFile.delete(); 49 | try { 50 | Files.write(settingsFile.toPath(), gson.toJson(config).getBytes(StandardCharsets.UTF_8)); 51 | } catch (IOException e1) { 52 | e1.printStackTrace(); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/util/Direction.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.util; 2 | 3 | public enum Direction { 4 | DOWN(Axis.Y, 0, -1, 0), UP(Axis.Y, 0, 1, 0), NORTH(Axis.Z, 0, 0, -1), SOUTH(Axis.Z, 0, 0, 1), 5 | WEST(Axis.X, -1, 0, 0), EAST(Axis.X, 1, 0, 0); 6 | 7 | private static Direction[] opposite = new Direction[] { UP, DOWN, SOUTH, NORTH, EAST, WEST }; 8 | 9 | private final Axis axis; 10 | private final int x, y, z; 11 | 12 | Direction(Axis axis, int x, int y, int z) { 13 | this.axis = axis; 14 | this.x = x; 15 | this.y = y; 16 | this.z = z; 17 | } 18 | 19 | public Direction getOpposite() { 20 | return opposite[this.ordinal()]; 21 | } 22 | 23 | public Axis getAxis() { 24 | return axis; 25 | } 26 | 27 | public int getStepX() { 28 | return x; 29 | } 30 | 31 | public int getStepY() { 32 | return y; 33 | } 34 | 35 | public int getStepZ() { 36 | return z; 37 | } 38 | 39 | public int getDirStep() { 40 | return x + y + z; 41 | } 42 | 43 | public enum Axis { 44 | X { 45 | public int choose(int i, int j, int k) { 46 | return i; 47 | } 48 | 49 | public double choose(double d, double e, double f) { 50 | return d; 51 | } 52 | }, 53 | Y { 54 | public int choose(int i, int j, int k) { 55 | return j; 56 | } 57 | 58 | public double choose(double d, double e, double f) { 59 | return e; 60 | } 61 | }, 62 | Z { 63 | public int choose(int i, int j, int k) { 64 | return k; 65 | } 66 | 67 | public double choose(double d, double e, double f) { 68 | return f; 69 | } 70 | }; 71 | 72 | public static Axis[] VALUES = values(); 73 | 74 | public abstract int choose(int param1Int1, int param1Int2, int param1Int3); 75 | 76 | public abstract double choose(double param1Double1, double param1Double2, double param1Double3); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "3D Skin Layer", 3 | "text.skinlayers.enable.hat": "3D帽子", 4 | "text.skinlayers.enable.hat.tooltip": "プレイヤーの帽子レイヤーを3Dモデルにレンダリングする", 5 | "text.skinlayers.enable.jacket": "3D上着", 6 | "text.skinlayers.enable.jacket.tooltip": "プレイヤーの上着レイヤーを3Dモデルにレンダリングする", 7 | "text.skinlayers.enable.leftsleeve": "3D左袖", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "プレイヤーの左袖レイヤーを3Dモデルにレンダリングする", 9 | "text.skinlayers.enable.rightsleeve": "3D右袖", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "プレイヤーの右袖レイヤーを3Dモデルにレンダリングする", 11 | "text.skinlayers.enable.leftpants": "3D左股下", 12 | "text.skinlayers.enable.leftpants.tooltip": "プレイヤーの左股下レイヤーを3Dモデルにレンダリングする", 13 | "text.skinlayers.enable.rightpants": "3D右股下", 14 | "text.skinlayers.enable.rightpants.tooltip": "プレイヤーの右股下レイヤーを3Dモデルにレンダリングする", 15 | "text.skinlayers.renderdistancelod": "詳細度の距離", 16 | "text.skinlayers.renderdistancelod.tooltip": "モデルを低詳細(バニラのモデル)に切り替える距離", 17 | "text.skinlayers.basevoxelsize": "ボクセルのサイズ", 18 | "text.skinlayers.basevoxelsize.tooltip": "プレイヤーに適用するボクセルのサイズ", 19 | "text.skinlayers.headvoxelsize": "頭ボクセルのサイズ", 20 | "text.skinlayers.headvoxelsize.tooltip": "プレイヤーの頭部に適用するボクセルのサイズ", 21 | "text.skinlayers.bodyvoxelheightsize": "体ボクセルの高さ", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "プレイヤーの体に適用するボクセルの高さ(頭部へのクリッピング/腕からのはみ出し防止等に使用)", 23 | "text.skinlayers.bodyvoxelwidthsize": "胴体ボクセルの幅", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "プレイヤーの胴体に適用するボクセルの幅(腕にクリッピングするのを防止するのに使用)", 25 | "text.skinlayers.skulls.enable": "3D頭ブロック", 26 | "text.skinlayers.skulls.enable.tooltip": "プレイヤーの頭(ブロック)を3Dモデルにレンダリングする", 27 | "text.skinlayers.skullsitems.enable": "3D頭ブロック(アイテム)", 28 | "text.skinlayers.skullsitems.enable.tooltip": "プレイヤーの頭(アイテム)を3Dモデルにレンダリングする", 29 | "text.skinlayers.skulls.voxelsize": "頭ブロックボクセルのサイズ", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "プレイヤーの頭(ブロック/アイテム)に適用するボクセルのサイズ", 31 | "text.skinlayers.reset": "デフォルトにリセット", 32 | "text.skinlayers.fastrender.enable": "高速レンダリング", 33 | "text.skinlayers.fastrender.enable.tooltip": "3Dモデルを可能な限り速くレンダリングする", 34 | "text.skinlayers.firstperson.voxelsize": "一人称のボクセルサイズ", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "一人称視点の腕に適用するボクセルのサイズ" 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "將玩家的外觀渲染為3D效果", 3 | "text.skinlayers.title": "3D Skin Layer設定", 4 | "text.skinlayers.enable.hat": "3D帽子", 5 | "text.skinlayers.enable.hat.tooltip": "將玩家的帽子繪製為3D模型", 6 | "text.skinlayers.enable.jacket": "3D外套", 7 | "text.skinlayers.enable.jacket.tooltip": "將玩家的外套繪製為3D模型", 8 | "text.skinlayers.enable.leftsleeve": "3D左衣袖", 9 | "text.skinlayers.enable.leftsleeve.tooltip": "將玩家的左衣袖繪製為3D模型", 10 | "text.skinlayers.enable.rightsleeve": "3D右衣袖", 11 | "text.skinlayers.enable.rightsleeve.tooltip": "將玩家的右衣袖繪製為3D模型", 12 | "text.skinlayers.enable.leftpants": "3D左褲管", 13 | "text.skinlayers.enable.leftpants.tooltip": "將玩家的左褲管繪製為3D模型", 14 | "text.skinlayers.enable.rightpants": "3D右褲管", 15 | "text.skinlayers.enable.rightpants.tooltip": "將玩家的右褲管繪製為3D模型", 16 | "text.skinlayers.renderdistancelod": "細節層次距離", 17 | "text.skinlayers.renderdistancelod.tooltip": "依據距離來切換顯示低細節層次模型(原版的2D)", 18 | "text.skinlayers.basevoxelsize": "像素大小", 19 | "text.skinlayers.basevoxelsize.tooltip": "玩家所使用的像素大小", 20 | "text.skinlayers.headvoxelsize": "頭部像素大小", 21 | "text.skinlayers.headvoxelsize.tooltip": "玩家頭部所使用的像素大小", 22 | "text.skinlayers.bodyvoxelheightsize": "身體像素高", 23 | "text.skinlayers.bodyvoxelheightsize.tooltip": "玩家身體高所使用的像素大小(用於防止頭部與手臂穿透)", 24 | "text.skinlayers.bodyvoxelwidthsize": "軀幹像素寬", 25 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "玩家軀幹寬所使用的像素大小(用於防止穿透到手臂)", 26 | "text.skinlayers.skulls.enable": "3D頭顱", 27 | "text.skinlayers.skulls.enable.tooltip": "將玩家的頭顱與方塊頭顱繪製為3D模型", 28 | "text.skinlayers.skullsitems.enable": "3D頭顱物品", 29 | "text.skinlayers.skullsitems.enable.tooltip": "將玩家的物品頭顱繪製為3D模型", 30 | "text.skinlayers.skulls.voxelsize": "頭顱像素大小", 31 | "text.skinlayers.skulls.voxelsize.tooltip": "玩家頭顱所使用的像素大小(方塊/物品)", 32 | "text.skinlayers.reset": "預設", 33 | "text.skinlayers.fastrender.enable": "快速繪製", 34 | "text.skinlayers.fastrender.enable.tooltip": "以最快的速度繪製3D模型,這可能導致圖形的透明度錯誤", 35 | "text.skinlayers.firstperson.voxelsize": "第一人稱像素大小", 36 | "text.skinlayers.firstperson.voxelsize.tooltip": "第一人稱下玩家手部所使用的像素大小", 37 | "text.skinlayers.compatibilityMode.enable": "相容模式", 38 | "text.skinlayers.compatibilityMode.enable.tooltip": "繪製圖層的替代方法,可能會修復與其他模組的一些相容性問題" 39 | } 40 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/util/Vector3.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.util; 2 | 3 | public class Vector3 { 4 | public float x, y, z; 5 | 6 | public Vector3(float x, float y, float z) { 7 | this.x = x; 8 | this.y = y; 9 | this.z = z; 10 | } 11 | 12 | public Vector3 clone() { 13 | return new Vector3(x, y, z); 14 | } 15 | 16 | public void copy(Vector3 vec) { 17 | this.x = vec.x; 18 | this.y = vec.y; 19 | this.z = vec.z; 20 | } 21 | 22 | public Vector3 add(Vector3 vec) { 23 | this.x += vec.x; 24 | this.y += vec.y; 25 | this.z += vec.z; 26 | return this; 27 | } 28 | 29 | public Vector3 subtract(Vector3 vec) { 30 | this.x -= vec.x; 31 | this.y -= vec.y; 32 | this.z -= vec.z; 33 | return this; 34 | } 35 | 36 | public Vector3 div(float amount) { 37 | this.x /= amount; 38 | this.y /= amount; 39 | this.z /= amount; 40 | return this; 41 | } 42 | 43 | public Vector3 mul(float amount) { 44 | this.x *= amount; 45 | this.y *= amount; 46 | this.z *= amount; 47 | return this; 48 | } 49 | 50 | public Vector3 mul(float xa, float ya, float za) { 51 | this.x *= xa; 52 | this.y *= ya; 53 | this.z *= za; 54 | return this; 55 | } 56 | 57 | public Vector3 normalize() { 58 | float f = Mth.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 59 | if (f < 1.0E-4F) { 60 | this.x = 0; 61 | this.y = 0; 62 | this.z = 0; 63 | } else { 64 | this.x /= f; 65 | this.y /= f; 66 | this.z /= f; 67 | } 68 | return this; 69 | } 70 | 71 | public Vector3 rotateDegrees(float deg) { 72 | float ox = x; 73 | float oy = y; 74 | deg = (float) Math.toRadians(deg); 75 | x = Mth.cos(deg) * ox - Mth.sin(deg) * oy; 76 | y = Mth.sin(deg) * ox + Mth.cos(deg) * oy; 77 | return this; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "Vector2 [x=" + x + ", y=" + y + "]"; 83 | } 84 | 85 | public float sqrMagnitude() { 86 | return x * x + y * y + z * z; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/SkullModelMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import dev.tr7zw.skinlayers.accessor.ModelPartInjector; 10 | import dev.tr7zw.skinlayers.accessor.SkullModelAccessor; 11 | import dev.tr7zw.skinlayers.accessor.SkullModelStateAccessor; 12 | import dev.tr7zw.skinlayers.api.Mesh; 13 | import dev.tr7zw.skinlayers.api.OffsetProvider; 14 | 15 | import net.minecraft.client.model.geom.ModelPart; 16 | 17 | //? if >= 1.21.11 { 18 | 19 | import net.minecraft.client.model.object.skull.*; 20 | //? } else { 21 | /* 22 | import net.minecraft.client.model.*; 23 | *///? } 24 | 25 | //? if >= 1.17.0 { 26 | 27 | @Mixin(SkullModel.class) 28 | //? } else { 29 | 30 | // import net.minecraft.client.model.HumanoidHeadModel; 31 | // 32 | // @Mixin(HumanoidHeadModel.class) 33 | //? } 34 | public class SkullModelMixin implements SkullModelAccessor { 35 | 36 | @Shadow 37 | //? if >= 1.17.0 { 38 | 39 | private ModelPart head; 40 | //? } else { 41 | 42 | // private ModelPart hat; 43 | //? } 44 | 45 | @Override 46 | public void injectHatMesh(Mesh mesh) { 47 | //? if >= 1.17.0 { 48 | 49 | head.getAllParts().forEach(part -> { 50 | if (part != head && (Object) part instanceof ModelPartInjector inj) { // is the hat, not the head 51 | inj.setInjectedMesh(mesh, OffsetProvider.SKULL); 52 | } 53 | }); 54 | //? } else { 55 | 56 | // ((ModelPartInjector)(Object)hat).setInjectedMesh(mesh, OffsetProvider.SKULL); 57 | //? } 58 | } 59 | 60 | //? if >= 1.21.9 { 61 | 62 | @Inject(method = "setupAnim", at = @At("HEAD")) 63 | public void setupAnim(SkullModelBase.State state, CallbackInfo ci) { 64 | if (state instanceof SkullModelStateAccessor accessor) { 65 | if (accessor.getSkullSettings() != null) { 66 | injectHatMesh(accessor.getSkullSettings().getMesh()); 67 | return; 68 | } 69 | } 70 | // Otherwise clear it 71 | injectHatMesh(null); 72 | } 73 | //? } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/dev/tr7zw/tests/MixinTests.java: -------------------------------------------------------------------------------- 1 | //? if >= 1.18.0 { 2 | 3 | package dev.tr7zw.tests; 4 | 5 | import com.mojang.blaze3d.platform.*; 6 | import net.minecraft.*; 7 | import net.minecraft.client.model.*; 8 | import net.minecraft.client.model.geom.*; 9 | //? if >= 1.21.11 { 10 | 11 | import net.minecraft.client.model.object.skull.*; 12 | import net.minecraft.client.model.player.*; 13 | //? } else { 14 | /* 15 | import net.minecraft.client.model.*; 16 | *///? } 17 | import net.minecraft.client.renderer.*; 18 | import net.minecraft.client.renderer.blockentity.*; 19 | import net.minecraft.client.renderer.entity.*; 20 | import net.minecraft.client.renderer.entity.layers.*; 21 | import net.minecraft.server.*; 22 | import net.minecraft.world.level.block.entity.*; 23 | import org.junit.jupiter.api.*; 24 | import org.objenesis.*; 25 | 26 | import static org.junit.jupiter.api.Assertions.*; 27 | 28 | public class MixinTests { 29 | 30 | @BeforeAll 31 | public static void setup() { 32 | SharedConstants.tryDetectVersion(); 33 | Bootstrap.bootStrap(); 34 | } 35 | 36 | @Test 37 | void testMixins() { 38 | Objenesis objenesis = new ObjenesisStd(); 39 | //? if >= 1.21.6 { 40 | 41 | objenesis.newInstance(net.minecraft.client.renderer.special.PlayerHeadSpecialRenderer.class); 42 | //? } else if >= 1.21.4 { 43 | /* 44 | objenesis.newInstance(net.minecraft.client.renderer.special.SkullSpecialRenderer.class); 45 | *///? } else { 46 | /* 47 | objenesis.newInstance(net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer.class); 48 | *///? } 49 | objenesis.newInstance(CustomHeadLayer.class); 50 | objenesis.newInstance(Deadmau5EarsLayer.class); 51 | objenesis.newInstance(ModelPart.class); 52 | objenesis.newInstance(NativeImage.class); 53 | objenesis.newInstance(PlayerModel.class); 54 | objenesis.newInstance(SkullBlockEntity.class); 55 | objenesis.newInstance(SkullBlockRenderer.class); 56 | objenesis.newInstance(SkullModel.class); 57 | //? if >= 1.21.9 { 58 | 59 | objenesis.newInstance(net.minecraft.client.renderer.entity.player.AvatarRenderer.class); 60 | //? } else { 61 | /* 62 | objenesis.newInstance(net.minecraft.client.renderer.entity.player.PlayerRenderer.class); 63 | *///? } 64 | } 65 | 66 | } 67 | //? } 68 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/ko_kr.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "3D Skin Layer 설정", 3 | "text.skinlayers.enable.hat": "3D 모자", 4 | "text.skinlayers.enable.hat.tooltip": "플레이어의 모자 레이어를 3D 모델로 렌더합니다.", 5 | "text.skinlayers.enable.jacket": "3D 상의", 6 | "text.skinlayers.enable.jacket.tooltip": "플레이어의 상의 레이어를 3D 모델로 렌더합니다.", 7 | "text.skinlayers.enable.leftsleeve": "3D 왼소매", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "플레이어의 왼소매 레이어를 3D 모델로 렌더합니다.", 9 | "text.skinlayers.enable.rightsleeve": "3D 오른소매", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "플레이어의 오른소매 레이어를 3D 모델로 렌더합니다.", 11 | "text.skinlayers.enable.leftpants": "3D 왼쪽 바지", 12 | "text.skinlayers.enable.leftpants.tooltip": "플레이어의 왼쪽 바지 레이어를 3D 모델로 렌더합니다.", 13 | "text.skinlayers.enable.rightpants": "3D 오른쪽 바지", 14 | "text.skinlayers.enable.rightpants.tooltip": "플레이어의 모자 레이어를 3D 모델로 렌더합니다.", 15 | "text.skinlayers.renderdistancelod": "디테일 렌더 거리", 16 | "text.skinlayers.renderdistancelod.tooltip": "모델이 더 낮은 디테일(바닐라 2D)로 전환되는 거리를 조정합니다.", 17 | "text.skinlayers.basevoxelsize": "복셀 크기", 18 | "text.skinlayers.basevoxelsize.tooltip": "플레이어에게 사용되는 복셀의 크기입니다.", 19 | "text.skinlayers.headvoxelsize": "머리 복셀 크기", 20 | "text.skinlayers.headvoxelsize.tooltip": "플레이어의 머리에 사용되는 복셀의 크기입니다.", 21 | "text.skinlayers.bodyvoxelheightsize": "몸 복셀 높이", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "플레이어의 몸에 사용되는 복셀의 높이입니다. (머리에 잘리거나 팔에 끼는 것을 방지하기 위해 사용됩니다.)", 23 | "text.skinlayers.bodyvoxelwidthsize": "몸 복셀 너비", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "플레이어의 몸에 사용되는 복셀의 너비입니다. (팔에 잘리는 것을 방지하기 위해 사용됩니다.)", 25 | "text.skinlayers.skulls.enable": "3D 머리", 26 | "text.skinlayers.skulls.enable.tooltip": "플레이어 머리 블록을 3D 모델로 렌더합니다.", 27 | "text.skinlayers.skullsitems.enable": "3D 머리 아이템", 28 | "text.skinlayers.skullsitems.enable.tooltip": "플레이어 머리 아이템을 3D 모델로 렌더합니다.", 29 | "text.skinlayers.skulls.voxelsize": "머리 복셀 크기", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "플레이어 머리(블록/아이템)에 사용되는 복셀의 크기입니다.", 31 | "text.skinlayers.reset": "초기화", 32 | "text.skinlayers.fastrender.enable": "빠른 렌더", 33 | "text.skinlayers.fastrender.enable.tooltip": "3D 모델을 가능한 빨리 렌더합니다. 투명도에 따라 그래픽 오류가 발생할 수 있습니다.", 34 | "text.skinlayers.firstperson.voxelsize": "1인칭 복셀 크기", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "1인칭에서 플레이어의 팔에 사용되는 복셀의 크기입니다." 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "将玩家的皮肤渲染为3D效果", 3 | "text.skinlayers.title": "3D皮肤层设置", 4 | "text.skinlayers.tab.player": "玩家", 5 | "text.skinlayers.tab.heads": "玩家的头", 6 | "text.skinlayers.tab.other": "其他设置", 7 | "text.skinlayers.enable.hat": "3D帽子", 8 | "text.skinlayers.enable.hat.tooltip": "将玩家的帽子层渲染为3D模型", 9 | "text.skinlayers.enable.jacket": "3D外套", 10 | "text.skinlayers.enable.jacket.tooltip": "将玩家外套层渲染为3D模型", 11 | "text.skinlayers.enable.leftsleeve": "3D左袖筒", 12 | "text.skinlayers.enable.leftsleeve.tooltip": "将玩家左袖图层渲染为3D模型", 13 | "text.skinlayers.enable.rightsleeve": "3D右袖筒", 14 | "text.skinlayers.enable.rightsleeve.tooltip": "将玩家右袖图层渲染为3D模型", 15 | "text.skinlayers.enable.leftpants": "3D左裤筒", 16 | "text.skinlayers.enable.leftpants.tooltip": "将玩家左侧裤子层渲染为3D模型", 17 | "text.skinlayers.enable.rightpants": "3D右裤筒", 18 | "text.skinlayers.enable.rightpants.tooltip": "将玩家右侧裤子层渲染为3D模型", 19 | "text.skinlayers.renderdistancelod": "多层次细节距离", 20 | "text.skinlayers.renderdistancelod.tooltip": "模型切换到较低细节级别的距离(原版2D)", 21 | "text.skinlayers.basevoxelsize": "像素大小", 22 | "text.skinlayers.basevoxelsize.tooltip": "玩家皮肤使用的像素大小", 23 | "text.skinlayers.headvoxelsize": "头部像素大小", 24 | "text.skinlayers.headvoxelsize.tooltip": "玩家头部所用像素的大小", 25 | "text.skinlayers.bodyvoxelheightsize": "身体像素高度", 26 | "text.skinlayers.bodyvoxelheightsize.tooltip": "玩家身体所用像素的高度(用于防止剪切到头部或伸出手臂)", 27 | "text.skinlayers.bodyvoxelwidthsize": "躯干像素宽度", 28 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "玩家躯干所用像素的宽度(用于防止剪切到手臂)。", 29 | "text.skinlayers.skulls.enable": "3D头颅", 30 | "text.skinlayers.skulls.enable.tooltip": "将玩家的头(方块)渲染为3D模型", 31 | "text.skinlayers.skullsitems.enable": "3D头颅物品", 32 | "text.skinlayers.skullsitems.enable.tooltip": "将玩家的头(物品)渲染为3D模型", 33 | "text.skinlayers.skulls.voxelsize": "头颅体素大小", 34 | "text.skinlayers.skulls.voxelsize.tooltip": "玩家头部(区块/项目)所用体素的大小", 35 | "text.skinlayers.reset": "恢复为默认", 36 | "text.skinlayers.fastrender.enable": "快速渲染", 37 | "text.skinlayers.fastrender.enable.tooltip": "尽可能快地渲染3D模型,这可能会导致一些图形透明度问题", 38 | "text.skinlayers.firstperson.voxelsize": "第一人称体素大小", 39 | "text.skinlayers.firstperson.voxelsize.tooltip": "第一人称视角中玩家手部所使用的体素大小", 40 | "text.skinlayers.compatibilityMode.enable": "兼容模式", 41 | "text.skinlayers.compatibilityMode.enable.tooltip": "替换渲染图层的方法,可能会修复一些其他模组的兼容性问题" 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/EMFModelPartMixin.java: -------------------------------------------------------------------------------- 1 | 2 | package dev.tr7zw.skinlayers.mixin; 3 | 4 | import net.minecraft.client.model.geom.*; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Pseudo; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | //? if >= 1.20.0 && fabric || neoforge { 11 | 12 | import com.mojang.blaze3d.vertex.PoseStack; 13 | import com.mojang.blaze3d.vertex.VertexConsumer; 14 | 15 | import dev.tr7zw.skinlayers.accessor.ModelPartInjector; 16 | import traben.entity_model_features.models.parts.EMFModelPart; 17 | 18 | @Pseudo 19 | @Mixin(EMFModelPart.class) 20 | public abstract class EMFModelPartMixin implements ModelPartInjector { 21 | 22 | //? if >= 1.21.0 { 23 | 24 | @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;III)V", at = @At(value = "HEAD"), cancellable = true) 25 | public void render(PoseStack poseStack, VertexConsumer vertexConsumer, int light, int overlay, int color, 26 | CallbackInfo ci) { 27 | if (isVisible() && getInjectedMesh() != null) { 28 | poseStack.pushPose(); 29 | prepareTranslateAndRotate(poseStack); 30 | getOffsetProvider().applyOffset(poseStack, getInjectedMesh()); 31 | getInjectedMesh().render((ModelPart) (Object) this, poseStack, vertexConsumer, light, overlay, color); 32 | poseStack.popPose(); 33 | ci.cancel(); 34 | } 35 | } 36 | //? } else { 37 | /* 38 | @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;IIFFFF)V", at = @At(value = "HEAD"), cancellable = true) 39 | public void render(PoseStack poseStack, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha, 40 | CallbackInfo ci) { 41 | if (isVisible() && getInjectedMesh() != null) { 42 | poseStack.pushPose(); 43 | prepareTranslateAndRotate(poseStack); 44 | getOffsetProvider().applyOffset(poseStack, getInjectedMesh()); 45 | getInjectedMesh().render((ModelPart)(Object)this, poseStack, vertexConsumer, light, overlay, red, green, blue, alpha); 46 | poseStack.popPose(); 47 | ci.cancel(); 48 | } 49 | } 50 | 51 | *///? } 52 | 53 | } 54 | //? } else { 55 | /* 56 | @Mixin(net.minecraft.client.Minecraft.class) 57 | public abstract class EMFModelPartMixin{} // DUMMY 58 | *///? } 59 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/MeshHelper.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import com.mojang.blaze3d.platform.NativeImage; 4 | 5 | public interface MeshHelper { 6 | 7 | /** 8 | * Creates a renderable 3d cube mesh from a texture. Example values: 64x64 skin 9 | * player head layer: create3DMesh(skin, 8, 8, 8, 32, 0, false, 0.6f) 64x64 skin 10 | * player torso layer: create3DMesh(skin, 8, 12, 4, 16, 32, true, -0.8f) 11 | * 12 | * @param natImage Imaged used for the mesh generation 13 | * @param width width of the cube, seen from the front 14 | * @param height height of the cube 15 | * @param depth depth of the cube 16 | * @param textureU x offset of where the texture is located. Needs to 17 | * point to the top left corner 18 | * @param textureV y offset of where the texture is located. Needs to 19 | * point to the top left corner 20 | * @param topPivot decides where the piviot is placed. Arms/legs have it 21 | * at the top, the head has it at the bottom. 22 | * @param rotationOffset magic number to offset the piviot 23 | * @return 24 | */ 25 | public Mesh create3DMesh(NativeImage natImage, int width, int height, int depth, int textureU, int textureV, 26 | boolean topPivot, float rotationOffset); 27 | 28 | /** 29 | * Creates a renderable 3d cube mesh from a texture. Example values: 64x64 skin 30 | * player head layer: create3DMesh(skin, 8, 8, 8, 32, 0, false, 0.6f) 64x64 skin 31 | * player torso layer: create3DMesh(skin, 8, 12, 4, 16, 32, true, -0.8f) 32 | * 33 | * @param natImage Imaged used for the mesh generation 34 | * @param width width of the cube, seen from the front 35 | * @param height height of the cube 36 | * @param depth depth of the cube 37 | * @param textureU x offset of where the texture is located. Needs to 38 | * point to the top left corner 39 | * @param textureV y offset of where the texture is located. Needs to 40 | * point to the top left corner 41 | * @param topPivot decides where the piviot is placed. Arms/legs have it 42 | * at the top, the head has it at the bottom. 43 | * @param rotationOffset magic number to offset the piviot 44 | * @param mirror if the mesh should be mirrored, e.g. for the left arm 45 | * @return 46 | */ 47 | public Mesh create3DMesh(NativeImage natImage, int width, int height, int depth, int textureU, int textureV, 48 | boolean topPivot, float rotationOffset, boolean mirror); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/render/CustomModelPart.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.render; 2 | 3 | import java.util.List; 4 | 5 | import dev.tr7zw.skinlayers.versionless.render.CustomizableCube.Polygon; 6 | import dev.tr7zw.skinlayers.versionless.render.CustomizableCube.Vertex; 7 | import dev.tr7zw.skinlayers.versionless.util.Vector3; 8 | 9 | /** 10 | * Cut down copy of the Vanilla ModelPart to bypass Optifine/Sodium screwing 11 | * with the CustomizableCube class 12 | * 13 | */ 14 | public abstract class CustomModelPart { 15 | 16 | protected float x; 17 | protected float y; 18 | protected float z; 19 | protected float xRot; 20 | protected float yRot; 21 | protected float zRot; 22 | protected boolean visible = true; 23 | protected float[] polygonData = null; 24 | protected int polygonAmount = 0; 25 | protected final int polyDataSize = 23; 26 | 27 | public CustomModelPart(List customCubes) { 28 | compactCubes(customCubes); 29 | } 30 | 31 | private void compactCubes(List customCubes) { 32 | for (CustomizableCube cube : customCubes) { 33 | polygonAmount += cube.polygonCount; 34 | } 35 | polygonData = new float[polygonAmount * polyDataSize]; 36 | int offset = 0; 37 | Polygon polygon; 38 | for (CustomizableCube cube : customCubes) { 39 | for (int id = 0; id < cube.polygonCount; id++) { 40 | polygon = cube.polygons[id]; 41 | Vector3 vector3f = polygon.normal; 42 | polygonData[offset + 0] = vector3f.x; 43 | polygonData[offset + 1] = vector3f.y; 44 | polygonData[offset + 2] = vector3f.z; 45 | for (int i = 0; i < 4; i++) { 46 | Vertex vertex = polygon.vertices[i]; 47 | polygonData[offset + 3 + (i * 5) + 0] = vertex.scaledX; 48 | polygonData[offset + 3 + (i * 5) + 1] = vertex.scaledY; 49 | polygonData[offset + 3 + (i * 5) + 2] = vertex.scaledZ; 50 | polygonData[offset + 3 + (i * 5) + 3] = vertex.u; 51 | polygonData[offset + 3 + (i * 5) + 4] = vertex.v; 52 | } 53 | offset += polyDataSize; 54 | } 55 | } 56 | } 57 | 58 | public void setPosition(float f, float g, float h) { 59 | this.x = f; 60 | this.y = g; 61 | this.z = h; 62 | } 63 | 64 | public void setRotation(float f, float g, float h) { 65 | this.xRot = f; 66 | this.yRot = g; 67 | this.zRot = h; 68 | } 69 | 70 | public void setVisible(boolean visible) { 71 | this.visible = visible; 72 | } 73 | 74 | public boolean isVisible() { 75 | return visible; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/sv_se.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Inställningar för 3D Skin Layers", 3 | "text.skinlayers.enable.hat": "3D Hatt", 4 | "text.skinlayers.enable.hat.tooltip": "Rendera spelarens hatt som en 3D-modell", 5 | "text.skinlayers.enable.jacket": "3D Jacka", 6 | "text.skinlayers.enable.jacket.tooltip": "Rendera spelarens jacka som en 3D-modell", 7 | "text.skinlayers.enable.leftsleeve": "3D Vänster Ärm", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Rendera spelarens vänstra ärm som en 3D-modell", 9 | "text.skinlayers.enable.rightsleeve": "3D Höger Ärm", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Rendera spelarens högra ärm som en 3D-modell", 11 | "text.skinlayers.enable.leftpants": "3D Vänster Byxben", 12 | "text.skinlayers.enable.leftpants.tooltip": "Rendera spelarens vänstra byxben som en 3D-modell", 13 | "text.skinlayers.enable.rightpants": "3D Höger Byxben", 14 | "text.skinlayers.enable.rightpants.tooltip": "Rendera spelarens högra byxben som en 3D-modell", 15 | "text.skinlayers.renderdistancelod": "Detaljeringsnivå Avstånd", 16 | "text.skinlayers.renderdistancelod.tooltip": "Avståndet från spelaren där modellen byter till en lägre nivå av detalj(vanilla 2d)", 17 | "text.skinlayers.basevoxelsize": "Voxelstorlek", 18 | "text.skinlayers.basevoxelsize.tooltip": "Storleken på voxlarna som används för spelare", 19 | "text.skinlayers.headvoxelsize": "Huvud Voxelstorlek", 20 | "text.skinlayers.headvoxelsize.tooltip": "Storleken på voxlarna som används för spelarens Huvud", 21 | "text.skinlayers.bodyvoxelheightsize": "Kropp Voxelhöjd", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Höjden på voxlarna som används för spelarens kropp(används för att förhindra att den fastnar i huudet/sticker ut ur armarna)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Överkropp Voxelbredd", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Bredden på voxlarna som används för spelarens överkropp(används för att förhindra att den fastnar i armarna)", 25 | "text.skinlayers.skulls.enable": "3D Döskallar", 26 | "text.skinlayers.skulls.enable.tooltip": "Rendera spelares huvuden(blocket) som en 3D-modell", 27 | "text.skinlayers.skullsitems.enable": "3D Döskalleföremål", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Rendera spelares huvuden(föremålet) som en 3D-modell", 29 | "text.skinlayers.skulls.voxelsize": "Döskalle Voxelstorlek", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "Storleken på voxlarna som används för spelares huvuden(blocket/föremålet)", 31 | "text.skinlayers.reset": "Standard", 32 | "text.skinlayers.fastrender.enable": "Snabb Rendering", 33 | "text.skinlayers.fastrender.enable.tooltip": "Rendera 3D modellerna så fort som möjligt, det här kanske skapar några grafiska buggar med transperens", 34 | "text.skinlayers.firstperson.voxelsize": "Första Person Voxel Storlek", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "Storleken på voxlarna som används för spelarens hand i första person" 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/SkinLayersAPI.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import java.util.Collections; 4 | 5 | import com.mojang.blaze3d.platform.NativeImage; 6 | 7 | import dev.tr7zw.skinlayers.render.CustomizableCubeListBuilder; 8 | import dev.tr7zw.skinlayers.render.CustomizableModelPart; 9 | import dev.tr7zw.skinlayers.util.NMSWrapper.WrappedNativeImage; 10 | import dev.tr7zw.skinlayers.versionless.util.wrapper.SolidPixelWrapper; 11 | import lombok.*; 12 | import lombok.experimental.*; 13 | import net.minecraft.client.player.AbstractClientPlayer; 14 | 15 | @UtilityClass 16 | public class SkinLayersAPI { 17 | 18 | @Getter 19 | private static final MeshHelper meshHelper = new MeshHelperImplementation(); 20 | @Getter 21 | private static final MeshProvider meshProvider = new MeshProviderImplementation(); 22 | private static MeshTransformerProvider meshTransformerProvider = MeshTransformerProvider.EMPTY_PROVIDER; 23 | @Getter 24 | private static BoxBuilder boxBuilder = BoxBuilder.DEFAULT; 25 | 26 | public static void setupBoxBuilder(BoxBuilder builder) { 27 | SkinLayersAPI.boxBuilder = builder; 28 | } 29 | 30 | public static void setupMeshTransformerProvider(MeshTransformerProvider provider) { 31 | SkinLayersAPI.meshTransformerProvider = provider; 32 | } 33 | 34 | public static MeshTransformerProvider getMeshTransformerProvider() { 35 | return meshTransformerProvider; 36 | } 37 | 38 | private static class MeshHelperImplementation implements MeshHelper { 39 | 40 | @Override 41 | public Mesh create3DMesh(NativeImage natImage, int width, int height, int depth, int textureU, int textureV, 42 | boolean topPivot, float rotationOffset, boolean mirror) { 43 | CustomizableCubeListBuilder builder = new CustomizableCubeListBuilder(); 44 | builder.mirror(mirror); 45 | if (SolidPixelWrapper.wrapBox(builder, new WrappedNativeImage(natImage), width, height, depth, textureU, 46 | textureV, topPivot, rotationOffset) != null) { 47 | return new CustomizableModelPart(builder.getVanillaCubes(), builder.getCubes(), Collections.emptyMap()); 48 | } 49 | return Mesh.EMPTY; 50 | } 51 | 52 | @Override 53 | public Mesh create3DMesh(NativeImage natImage, int width, int height, int depth, int textureU, int textureV, 54 | boolean topPivot, float rotationOffset) { 55 | return create3DMesh(natImage, width, height, depth, textureU, textureV, topPivot, rotationOffset, false); 56 | } 57 | 58 | } 59 | 60 | private static class MeshProviderImplementation implements MeshProvider { 61 | 62 | @Override 63 | public PlayerData getPlayerMesh(AbstractClientPlayer abstractClientPlayerEntity) { 64 | if (abstractClientPlayerEntity instanceof PlayerData) { 65 | return (PlayerData) abstractClientPlayerEntity; 66 | } 67 | return null; 68 | } 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /gradlecw.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | set COMPOSE=%APP_HOME%\gradle\gradle-compose.jar 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" -jar "%COMPOSE%" %* 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if "%ERRORLEVEL%"=="0" goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 87 | exit /b 1 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/Mesh.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.blaze3d.vertex.VertexConsumer; 5 | 6 | import net.minecraft.client.model.geom.ModelPart; 7 | //? if >= 1.17.0 { 8 | 9 | import net.minecraft.client.model.geom.PartPose; 10 | //? } 11 | 12 | public interface Mesh { 13 | 14 | public static final Mesh EMPTY = new Mesh() { 15 | 16 | @Override 17 | public void setVisible(boolean visible) { 18 | } 19 | 20 | @Override 21 | public void setRotation(float xRot, float yRot, float zRot) { 22 | } 23 | 24 | @Override 25 | public void setPosition(float x, float y, float z) { 26 | } 27 | 28 | @Override 29 | public void render(ModelPart vanillaModel, PoseStack poseStack, VertexConsumer vertexConsumer, int light, 30 | int overlay, int color) { 31 | } 32 | 33 | @Override 34 | //? if >= 1.17.0 { 35 | 36 | public void loadPose(PartPose partPose) { 37 | //? } else { 38 | 39 | // public void loadPose(ModelPart partPose) { 40 | //? } 41 | } 42 | 43 | @Override 44 | public boolean isVisible() { 45 | return false; 46 | } 47 | 48 | @Override 49 | public void copyFrom(ModelPart modelPart) { 50 | } 51 | 52 | @Override 53 | public void reset() { 54 | } 55 | }; 56 | 57 | public default void render(PoseStack poseStack, VertexConsumer vertexConsumer, int light, int overlay) { 58 | render(null, poseStack, vertexConsumer, light, overlay, 0xFFFFFFFF); 59 | } 60 | 61 | /** 62 | * @param color The color, in ARGB format 63 | */ 64 | public void render(ModelPart vanillaModel, PoseStack poseStack, VertexConsumer vertexConsumer, int light, 65 | int overlay, int color); 66 | 67 | public default void render(ModelPart vanillaModel, PoseStack poseStack, VertexConsumer vertexConsumer, int light, 68 | int overlay, float red, float green, float blue, float alpha) { 69 | int color = 0; 70 | int a = (int) (alpha * 255) << 24; 71 | int r = (int) (red * 255) << 16; 72 | int g = (int) (green * 255) << 8; 73 | int b = (int) (blue * 255); 74 | 75 | // Combine them into a single int 76 | color = a | r | g | b; 77 | render(vanillaModel, poseStack, vertexConsumer, light, overlay, color); 78 | } 79 | 80 | public void setPosition(float x, float y, float z); 81 | 82 | public void setRotation(float xRot, float yRot, float zRot); 83 | 84 | //? if >= 1.17.0 { 85 | 86 | public void loadPose(PartPose partPose); 87 | //? } else { 88 | 89 | // public void loadPose(ModelPart partPose); 90 | //? } 91 | 92 | public void copyFrom(ModelPart modelPart); 93 | 94 | public void reset(); 95 | 96 | public void setVisible(boolean visible); 97 | 98 | public boolean isVisible(); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/fr_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Paramètres de la couche 3D des skins", 3 | "text.skinlayers.enable.hat": "Chapeau 3D", 4 | "text.skinlayers.enable.hat.tooltip": "Afficher la couche chapeau du joueur en tant que modèle 3D", 5 | "text.skinlayers.enable.jacket": "Veste 3D", 6 | "text.skinlayers.enable.jacket.tooltip": "Afficher la couche veste du joueur en tant que modèle 3D", 7 | "text.skinlayers.enable.leftsleeve": "Manche gauche 3D", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Afficher la couche manche gauche du joueur en tant que modèle 3D", 9 | "text.skinlayers.enable.rightsleeve": "Manche droite 3D", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Afficher la couche manche droite du joueur en tant que modèle 3D", 11 | "text.skinlayers.enable.leftpants": "Pantalon gauche 3D", 12 | "text.skinlayers.enable.leftpants.tooltip": "Afficher la couche pantalon gauche du joueur en tant que modèle 3D", 13 | "text.skinlayers.enable.rightpants": "Pantalon droit 3D", 14 | "text.skinlayers.enable.rightpants.tooltip": "Afficher la couche pantalon droit du joueur en tant que modèle 3D", 15 | "text.skinlayers.renderdistancelod": "Distance de niveau de détail", 16 | "text.skinlayers.renderdistancelod.tooltip": "La distance à laquelle le modèle passera à un niveau de détail inférieur (2D vanille)", 17 | "text.skinlayers.basevoxelsize": "Taille des voxels", 18 | "text.skinlayers.basevoxelsize.tooltip": "La taille des voxels utilisés pour les joueurs", 19 | "text.skinlayers.headvoxelsize": "Taille des voxels de la tête", 20 | "text.skinlayers.headvoxelsize.tooltip": "La taille des voxels utilisés pour la tête du joueur", 21 | "text.skinlayers.bodyvoxelheightsize": "Hauteur des voxels du corps", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "La hauteur des voxels utilisés pour le corps du joueur (pour éviter les chevauchements avec la tête ou les bras)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Largeur des voxels du torse", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "La largeur des voxels utilisés pour le torse du joueur (pour éviter les chevauchements avec les bras)", 25 | "text.skinlayers.skulls.enable": "Crânes 3D", 26 | "text.skinlayers.skulls.enable.tooltip": "Afficher les têtes des joueurs (le bloc) en tant que modèle 3D", 27 | "text.skinlayers.skullsitems.enable": "Objets crânes 3D", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Afficher les têtes des joueurs (l'objet) en tant que modèle 3D", 29 | "text.skinlayers.skulls.voxelsize": "Taille des voxels des crânes", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "La taille des voxels utilisés pour les têtes des joueurs (bloc/objet)", 31 | "text.skinlayers.reset": "Par défaut", 32 | "text.skinlayers.fastrender.enable": "Rendu rapide", 33 | "text.skinlayers.fastrender.enable.tooltip": "Rendre les modèles 3D aussi vite que possible, cela peut causer des problèmes graphiques avec la transparence", 34 | "text.skinlayers.firstperson.voxelsize": "Taille des voxels en première personne", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "La taille des voxels utilisés pour la main du joueur en première personne" 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/pl_pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "Renderuje warstwę skórki gracza w 3D", 3 | "text.skinlayers.title": "Ustawienia 3D Skin Layer", 4 | "text.skinlayers.enable.hat": "Nakrycie głowy 3D", 5 | "text.skinlayers.enable.hat.tooltip": "Renderuj warstwę nakrycia głowy gracza jako model 3D", 6 | "text.skinlayers.enable.jacket": "Kurtka 3D", 7 | "text.skinlayers.enable.jacket.tooltip": "Renderuj warstwę kurtki gracza jako model 3D", 8 | "text.skinlayers.enable.leftsleeve": "Lewy rękaw 3D", 9 | "text.skinlayers.enable.leftsleeve.tooltip": "Renderuj warstwę lewego rękawa gracza jako model 3D", 10 | "text.skinlayers.enable.rightsleeve": "Prawy rękaw 3D", 11 | "text.skinlayers.enable.rightsleeve.tooltip": "Renderuj warstwę prawego rękawa gracza jako model 3D", 12 | "text.skinlayers.enable.leftpants": "Lewa nogawka 3D", 13 | "text.skinlayers.enable.leftpants.tooltip": "Renderuj warstwę lewej nogawki gracza jako model 3D", 14 | "text.skinlayers.enable.rightpants": "Prawa nogawka 3D", 15 | "text.skinlayers.enable.rightpants.tooltip": "Renderuj warstwę prawej nogawki gracza jako model 3D", 16 | "text.skinlayers.renderdistancelod": "Odległość poziomu detali", 17 | "text.skinlayers.renderdistancelod.tooltip": "Odległość, na której model zmniejszy swój poziom detalu (na zwykłe 2D)", 18 | "text.skinlayers.basevoxelsize": "Rozmiar wokseli", 19 | "text.skinlayers.basevoxelsize.tooltip": "Rozmiar wokseli na graczach", 20 | "text.skinlayers.headvoxelsize": "Rozmiar wokseli głowy", 21 | "text.skinlayers.headvoxelsize.tooltip": "Rozmiar wokseli na głowach graczy", 22 | "text.skinlayers.bodyvoxelheightsize": "Wysokość wokseli ciała", 23 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Wysokość wokseli ciała gracza (unika kolizji z głową/wystawania z ramion)", 24 | "text.skinlayers.bodyvoxelwidthsize": "Szerokość wokseli tułowia", 25 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Szerokość wokseli tułowia gracza (unika kolizji z ramionami)", 26 | "text.skinlayers.skulls.enable": "Głowy 3D", 27 | "text.skinlayers.skulls.enable.tooltip": "Renderuj blok głowy gracza jako model 3D", 28 | "text.skinlayers.skullsitems.enable": "Przedmioty głowy 3D", 29 | "text.skinlayers.skullsitems.enable.tooltip": "Renderuj przedmiot głowy gracza jako model 3D", 30 | "text.skinlayers.skulls.voxelsize": "Rozmiar wokseli głowy", 31 | "text.skinlayers.skulls.voxelsize.tooltip": "Rozmiar wokseli głów graczy (przedmiotu/bloku)", 32 | "text.skinlayers.reset": "Zresetuj", 33 | "text.skinlayers.fastrender.enable": "Szybkie renderowanie", 34 | "text.skinlayers.fastrender.enable.tooltip": "Renderuj modele 3D tak szybko, jak to możliwe. Może to spowodować błędy graficzne z przezroczystością", 35 | "text.skinlayers.firstperson.voxelsize": "Rozmiar wokseli w widoku pierwszoosobowym", 36 | "text.skinlayers.firstperson.voxelsize.tooltip": "Rozmiar wokseli na ręce gracza w widoku pierwszoosobowym", 37 | "text.skinlayers.compatibilityMode.enable": "Tryb kompatybilności", 38 | "text.skinlayers.compatibilityMode.enable.tooltip": "Alternatywny sposób renderowania warstw, może poprawić kompatybilność z niektórymi modami" 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/es_mx.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Configuración de 3D Skin Layers", 3 | "text.skinlayers.enable.hat": "Sombrero 3D", 4 | "text.skinlayers.enable.hat.tooltip": "Renderiza la capa de sombrero del jugador como un modelo 3D", 5 | "text.skinlayers.enable.jacket": "Chaqueta 3D", 6 | "text.skinlayers.enable.jacket.tooltip": "Renderiza la capa de chaqueta del jugador como un modelo 3D", 7 | "text.skinlayers.enable.leftsleeve": "Manga Izquierda 3D", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Renderiza la capa de manga izquierda del jugador como un modelo 3D", 9 | "text.skinlayers.enable.rightsleeve": "Manga Derecha 3D", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Renderiza la capa de manga derecha del jugador como un modelo 3D", 11 | "text.skinlayers.enable.leftpants": "Pantalón Izquierdo 3D", 12 | "text.skinlayers.enable.leftpants.tooltip": "Renderiza la capa de pantalón izquierdo del jugador como un modelo 3D", 13 | "text.skinlayers.enable.rightpants": "Pantalón Derecho 3D", 14 | "text.skinlayers.enable.rightpants.tooltip": "Renderiza la capa de pantalón derecho del jugador como un modelo 3D", 15 | "text.skinlayers.renderdistancelod": "Distancia de Nivel de Detalle", 16 | "text.skinlayers.renderdistancelod.tooltip": "La distancia en la cual el modelo cambiará a un nivel de detalle más bajo (2D estándar)", 17 | "text.skinlayers.basevoxelsize": "Tamaño de Voxel Base", 18 | "text.skinlayers.basevoxelsize.tooltip": "El tamaño de los voxels utilizados para los jugadores", 19 | "text.skinlayers.headvoxelsize": "Tamaño de Voxel de Cabeza", 20 | "text.skinlayers.headvoxelsize.tooltip": "El tamaño de los voxels utilizados para la cabeza del jugador", 21 | "text.skinlayers.bodyvoxelheightsize": "Altura de Voxel de Cuerpo", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "La altura de los voxels utilizados para el cuerpo del jugador (se utiliza para evitar que se superpongan con la cabeza o sobresalgan de los brazos)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Ancho de Voxel de Torso", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "El ancho de los voxels utilizados para el torso del jugador (se utiliza para evitar que se superpongan con los brazos)", 25 | "text.skinlayers.skulls.enable": "Cráneos 3D", 26 | "text.skinlayers.skulls.enable.tooltip": "Renderiza las cabezas de los jugadores (el bloque) como un modelo 3D", 27 | "text.skinlayers.skullsitems.enable": "Objetos de Cráneos 3D", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Renderiza las cabezas de los jugadores (el objeto) como un modelo 3D", 29 | "text.skinlayers.skulls.voxelsize": "Tamaño de Voxel de Cráneo", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "El tamaño de los voxels utilizados para las cabezas de los jugadores (el bloque/objeto)", 31 | "text.skinlayers.reset": "Predeterminado", 32 | "text.skinlayers.fastrender.enable": "Renderizado Rápido", 33 | "text.skinlayers.fastrender.enable.tooltip": "Renderiza los modelos 3D lo más rápido posible, esto puede causar algunos problemas gráficos con la transparencia", 34 | "text.skinlayers.firstperson.voxelsize": "Tamaño de Voxel en Primera Persona", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "El tamaño de los voxels utilizados para la mano del jugador en primera persona" 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/es_es.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Configuracion de 3D Skin Layer", 3 | "text.skinlayers.enable.hat": "Sombrero 3D", 4 | "text.skinlayers.enable.hat.tooltip": "Renderiza la capa del sombrero del jugador como un modelo 3D", 5 | "text.skinlayers.enable.jacket": "Chaqueta 3D", 6 | "text.skinlayers.enable.jacket.tooltip": "Renderiza la capa de la chaqueta del jugador como un modelo 3D", 7 | "text.skinlayers.enable.leftsleeve": "Manga izquierda 3D", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Renderiza la capa de la manga izquierda del jugador como un modelo 3D", 9 | "text.skinlayers.enable.rightsleeve": "Manga derecha 3D", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Renderiza la capa de la manga derecha del jugador como un modelo 3D", 11 | "text.skinlayers.enable.leftpants": "Pantalón izquierdo 3D", 12 | "text.skinlayers.enable.leftpants.tooltip": "Renderiza la capa del pantalón izquierdo del jugador como un modelo 3D", 13 | "text.skinlayers.enable.rightpants": "Pantalón derecho 3D", 14 | "text.skinlayers.enable.rightpants.tooltip": "Renderiza la capa del pantalón derecho del jugador como un modelo 3D", 15 | "text.skinlayers.renderdistancelod": "Distancia de Nivel de Detalle", 16 | "text.skinlayers.renderdistancelod.tooltip": "La distancia en la cual el modelo cambiará a un nivel de detalle más bajo (2D por defecto)", 17 | "text.skinlayers.basevoxelsize": "Tamaño de Voxel Base", 18 | "text.skinlayers.basevoxelsize.tooltip": "El tamaño de los voxels utilizados para los jugadores", 19 | "text.skinlayers.headvoxelsize": "Tamaño de Voxel de la Cabeza", 20 | "text.skinlayers.headvoxelsize.tooltip": "El tamaño de los voxels utilizados para la cabeza del jugador", 21 | "text.skinlayers.bodyvoxelheightsize": "Altura del Voxel del Cuerpo", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "La altura de los voxels utilizados para el cuerpo del jugador (utilizado para evitar intersecciones con la cabeza/salida de los brazos)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Ancho del Voxel del Torso", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "El ancho de los voxels utilizados para el torso del jugador (utilizado para evitar intersecciones con los brazos)", 25 | "text.skinlayers.skulls.enable": "Cráneos 3D", 26 | "text.skinlayers.skulls.enable.tooltip": "Renderiza las cabezas de los jugadores (el bloque) como un modelo 3D", 27 | "text.skinlayers.skullsitems.enable": "Objetos de Cráneos 3D", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Renderiza las cabezas de los jugadores (el objeto) como un modelo 3D", 29 | "text.skinlayers.skulls.voxelsize": "Tamaño de Voxel del Cráneo", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "El tamaño de los voxels utilizados para las cabezas de los jugadores (el bloque/objeto)", 31 | "text.skinlayers.reset": "Predeterminado", 32 | "text.skinlayers.fastrender.enable": "Renderizado Rápido", 33 | "text.skinlayers.fastrender.enable.tooltip": "Renderiza los modelos 3D lo más rápido posible, esto puede causar algunos problemas gráficos con la transparencia", 34 | "text.skinlayers.firstperson.voxelsize": "Tamaño de Voxel en Primera Persona", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "El tamaño de los voxels utilizados para la mano del jugador en primera persona" 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/api/OffsetProvider.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.api; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | 5 | import dev.tr7zw.skinlayers.SkinLayersModBase; 6 | 7 | public interface OffsetProvider { 8 | 9 | public void applyOffset(PoseStack stack, Mesh mesh); 10 | 11 | public final static OffsetProvider SKULL = createVanilla(Shape.HEAD, false, false, true); 12 | public final static OffsetProvider HEAD = createVanilla(Shape.HEAD); 13 | public final static OffsetProvider LEFT_LEG = createVanilla(Shape.LEGS); 14 | public final static OffsetProvider RIGHT_LEG = createVanilla(Shape.LEGS); 15 | public final static OffsetProvider LEFT_ARM = createVanilla(Shape.ARMS); 16 | public final static OffsetProvider LEFT_ARM_SLIM = createVanilla(Shape.ARMS_SLIM); 17 | public final static OffsetProvider RIGHT_ARM = createVanilla(Shape.ARMS, true, false, false); 18 | public final static OffsetProvider RIGHT_ARM_SLIM = createVanilla(Shape.ARMS_SLIM, true, false, false); 19 | public final static OffsetProvider FIRSTPERSON_LEFT_ARM = createVanilla(Shape.ARMS, false, true, false); 20 | public final static OffsetProvider FIRSTPERSON_LEFT_ARM_SLIM = createVanilla(Shape.ARMS_SLIM, false, true, false); 21 | public final static OffsetProvider FIRSTPERSON_RIGHT_ARM = createVanilla(Shape.ARMS, true, true, false); 22 | public final static OffsetProvider FIRSTPERSON_RIGHT_ARM_SLIM = createVanilla(Shape.ARMS_SLIM, true, true, false); 23 | public final static OffsetProvider BODY = createVanilla(Shape.BODY); 24 | 25 | private static OffsetProvider createVanilla(Shape shape) { 26 | return createVanilla(shape, false, false, false); 27 | } 28 | 29 | private static OffsetProvider createVanilla(Shape shape, boolean mirrored, boolean firstperson, boolean skull) { 30 | return (stack, mesh) -> { 31 | float pixelScaling = SkinLayersModBase.config.baseVoxelSize; 32 | float heightScaling = 1.035f; 33 | float widthScaling = SkinLayersModBase.config.baseVoxelSize; 34 | if (firstperson) { 35 | pixelScaling = SkinLayersModBase.config.firstPersonPixelScaling; 36 | widthScaling = SkinLayersModBase.config.firstPersonPixelScaling; 37 | } 38 | 39 | float x = 0; 40 | float y = 0; 41 | if (shape == Shape.ARMS) { 42 | x = 0.998f; 43 | } else if (shape == Shape.ARMS_SLIM) { 44 | x = 0.499f; 45 | } 46 | if (shape == Shape.BODY) { 47 | widthScaling = SkinLayersModBase.config.bodyVoxelWidthSize; 48 | } 49 | if (mirrored) { 50 | x *= -1; 51 | } 52 | if (shape == Shape.HEAD) { 53 | float voxelSize = SkinLayersModBase.config.headVoxelSize; 54 | if (skull) { 55 | voxelSize = SkinLayersModBase.config.skullVoxelSize; 56 | } 57 | stack.translate(0, -0.25, 0); 58 | stack.scale(voxelSize, voxelSize, voxelSize); 59 | stack.translate(0, 0.25, 0); 60 | stack.translate(0, -0.04, 0); 61 | } else { 62 | stack.scale(widthScaling, heightScaling, pixelScaling); 63 | y = shape.yOffsetMagicValue(); 64 | } 65 | 66 | mesh.setPosition(x, y, 0); 67 | 68 | }; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/de_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "3D Skin Layer Einstellungen", 3 | "text.skinlayers.enable.hat": "3D Hut", 4 | "text.skinlayers.enable.hat.tooltip": "Die Hut-Ebene des Spielers als 3D-Modell rendern", 5 | "text.skinlayers.enable.jacket": "3D Jacke", 6 | "text.skinlayers.enable.jacket.tooltip": "Die Jacken-Ebene des Spielers als 3D-Modell rendern", 7 | "text.skinlayers.enable.leftsleeve": "3D Linker Ärmel", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Rendern der linken Ärmel-Ebene des Spielers als 3D-Modell", 9 | "text.skinlayers.enable.rightsleeve": "3D Rechter Ärmel", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Rendern der rechten Ärmel-Ebene des Spielers als 3D-Modell", 11 | "text.skinlayers.enable.leftpants": "3D Linkes Hosenbein", 12 | "text.skinlayers.enable.leftpants.tooltip": "Rendern der linken Hosen-Ebene des Spielers als 3D-Modell", 13 | "text.skinlayers.enable.rightpants": "3D Rechtes Hosenbein", 14 | "text.skinlayers.enable.rightpants.tooltip": "Rendern der rechten Hosen-Ebene des Spielers als 3D-Modell", 15 | "text.skinlayers.renderdistancelod": "Detaillierungsgrad auf Entfernung [LOD]", 16 | "text.skinlayers.renderdistancelod.tooltip": "Die Entfernung, bei der das Modell auf eine niedrigere Detailstufe wechselt (Vanilla 2D)", 17 | "text.skinlayers.basevoxelsize": "Voxelgröße", 18 | "text.skinlayers.basevoxelsize.tooltip": "Die Größe der Voxels, die für Spieler verwendet werden", 19 | "text.skinlayers.headvoxelsize": "Kopf Voxelgröße", 20 | "text.skinlayers.headvoxelsize.tooltip": "Die Größe der Voxels, die für den Kopf des Spielers verwendet werden", 21 | "text.skinlayers.bodyvoxelheightsize": "Körper Voxelhöhe", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Die Höhe der Voxels, die für den Körper des Spielers verwendet werden (um ein Eindringen in den Kopf oder ein Hervorstehen aus den Armen zu verhindern)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Torso Voxelbreite", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Die Breite der Voxels, die für den Rumpf des Spielers verwendet werden (um ein Eindringen in die Arme zu verhindern)", 25 | "text.skinlayers.skulls.enable": "3D Spieler-Köpfe", 26 | "text.skinlayers.skulls.enable.tooltip": "Die Köpfe der Spieler (als Block) als 3D Modell rendern", 27 | "text.skinlayers.skullsitems.enable": "3D Spieler-Kopfitems", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Die Köpfe der Spieler (als Item) als 3D Modell rendern", 29 | "text.skinlayers.skulls.voxelsize": "Spieler-Kopf Voxelgröße", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "Die Größe der Voxels, die für die Köpfe der Spieler (als Block/Item) verwendet werden", 31 | "text.skinlayers.reset": "Standard", 32 | "text.skinlayers.fastrender.enable": "Schnelles Rendern", 33 | "text.skinlayers.fastrender.enable.tooltip": "Die 3D Modelle so schnell wie möglich rendern. Dies kann zu einigen grafischen Fehlern mit Transparenz führen", 34 | "text.skinlayers.firstperson.voxelsize": "Voxelgröße für die Ego-Perspektive", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "Die Größe der Voxels, die für die Hand des Spielers in der Ego-Perspektive verwendet werden", 36 | "text.skinlayers.compatibilityMode.enable": "Kompatibilitätsmodus", 37 | "text.skinlayers.compatibilityMode.enable.tooltip": "Alternative Methode zum Rendern der Ebenen, könnte einige Kompatibilitätsprobleme mit anderen Mods beheben" 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/de_at.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "3D Skin Layer Einstellungen", 3 | "text.skinlayers.enable.hat": "3D Hut", 4 | "text.skinlayers.enable.hat.tooltip": "Die Hut-Ebene des Spielers als 3D-Modell rendern", 5 | "text.skinlayers.enable.jacket": "3D Jacke", 6 | "text.skinlayers.enable.jacket.tooltip": "Die Jacken-Ebene des Spielers als 3D-Modell rendern", 7 | "text.skinlayers.enable.leftsleeve": "3D Linker Ärmel", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Rendern der linken Ärmel-Ebene des Spielers als 3D-Modell", 9 | "text.skinlayers.enable.rightsleeve": "3D Rechter Ärmel", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Rendern der rechten Ärmel-Ebene des Spielers als 3D-Modell", 11 | "text.skinlayers.enable.leftpants": "3D Linkes Hosenbein", 12 | "text.skinlayers.enable.leftpants.tooltip": "Rendern der linken Hosen-Ebene des Spielers als 3D-Modell", 13 | "text.skinlayers.enable.rightpants": "3D Rechtes Hosenbein", 14 | "text.skinlayers.enable.rightpants.tooltip": "Rendern der rechten Hosen-Ebene des Spielers als 3D-Modell", 15 | "text.skinlayers.renderdistancelod": "Detaillierungsgrad auf Entfernung [LOD]", 16 | "text.skinlayers.renderdistancelod.tooltip": "Die Entfernung, bei der das Modell auf eine niedrigere Detailstufe wechselt (Vanilla 2D)", 17 | "text.skinlayers.basevoxelsize": "Voxelgröße", 18 | "text.skinlayers.basevoxelsize.tooltip": "Die Größe der Voxels, die für Spieler verwendet werden", 19 | "text.skinlayers.headvoxelsize": "Kopf Voxelgröße", 20 | "text.skinlayers.headvoxelsize.tooltip": "Die Größe der Voxels, die für den Kopf des Spielers verwendet werden", 21 | "text.skinlayers.bodyvoxelheightsize": "Körper Voxelhöhe", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Die Höhe der Voxels, die für den Körper des Spielers verwendet werden (um ein Eindringen in den Kopf oder ein Hervorstehen aus den Armen zu verhindern)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Torso Voxelbreite", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Die Breite der Voxels, die für den Rumpf des Spielers verwendet werden (um ein Eindringen in die Arme zu verhindern)", 25 | "text.skinlayers.skulls.enable": "3D Spieler-Köpfe", 26 | "text.skinlayers.skulls.enable.tooltip": "Die Köpfe der Spieler (als Block) als 3D Modell rendern", 27 | "text.skinlayers.skullsitems.enable": "3D Spieler-Kopfitems", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Die Köpfe der Spieler (als Item) als 3D Modell rendern", 29 | "text.skinlayers.skulls.voxelsize": "Spieler-Kopf Voxelgröße", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "Die Größe der Voxels, die für die Köpfe der Spieler (als Block/Item) verwendet werden", 31 | "text.skinlayers.reset": "Standard", 32 | "text.skinlayers.fastrender.enable": "Schnelles Rendern", 33 | "text.skinlayers.fastrender.enable.tooltip": "Die 3D Modelle so schnell wie möglich rendern. Dies kann zu einigen grafischen Fehlern mit Transparenz führen", 34 | "text.skinlayers.firstperson.voxelsize": "Voxelgröße für die Ego-Perspektive", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "Die Größe der Voxels, die für die Hand des Spielers in der Ego-Perspektive verwendet werden", 36 | "text.skinlayers.compatibilityMode.enable": "Kompatibilitätsmodus", 37 | "text.skinlayers.compatibilityMode.enable.tooltip": "Alternative Methode zum Rendern der Ebenen, könnte einige Kompatibilitätsprobleme mit anderen Mods beheben" 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/de_ch.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "3D Skin Layer Einstellungen", 3 | "text.skinlayers.enable.hat": "3D Hut", 4 | "text.skinlayers.enable.hat.tooltip": "Die Hut-Ebene des Spielers als 3D-Modell rendern", 5 | "text.skinlayers.enable.jacket": "3D Jacke", 6 | "text.skinlayers.enable.jacket.tooltip": "Die Jacken-Ebene des Spielers als 3D-Modell rendern", 7 | "text.skinlayers.enable.leftsleeve": "3D Linker Ärmel", 8 | "text.skinlayers.enable.leftsleeve.tooltip": "Rendern der linken Ärmel-Ebene des Spielers als 3D-Modell", 9 | "text.skinlayers.enable.rightsleeve": "3D Rechter Ärmel", 10 | "text.skinlayers.enable.rightsleeve.tooltip": "Rendern der rechten Ärmel-Ebene des Spielers als 3D-Modell", 11 | "text.skinlayers.enable.leftpants": "3D Linkes Hosenbein", 12 | "text.skinlayers.enable.leftpants.tooltip": "Rendern der linken Hosen-Ebene des Spielers als 3D-Modell", 13 | "text.skinlayers.enable.rightpants": "3D Rechtes Hosenbein", 14 | "text.skinlayers.enable.rightpants.tooltip": "Rendern der rechten Hosen-Ebene des Spielers als 3D-Modell", 15 | "text.skinlayers.renderdistancelod": "Detaillierungsgrad auf Entfernung [LOD]", 16 | "text.skinlayers.renderdistancelod.tooltip": "Die Entfernung, bei der das Modell auf eine niedrigere Detailstufe wechselt (Vanilla 2D)", 17 | "text.skinlayers.basevoxelsize": "Voxelgröße", 18 | "text.skinlayers.basevoxelsize.tooltip": "Die Größe der Voxels, die für Spieler verwendet werden", 19 | "text.skinlayers.headvoxelsize": "Kopf Voxelgröße", 20 | "text.skinlayers.headvoxelsize.tooltip": "Die Größe der Voxels, die für den Kopf des Spielers verwendet werden", 21 | "text.skinlayers.bodyvoxelheightsize": "Körper Voxelhöhe", 22 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Die Höhe der Voxels, die für den Körper des Spielers verwendet werden (um ein Eindringen in den Kopf oder ein Hervorstehen aus den Armen zu verhindern)", 23 | "text.skinlayers.bodyvoxelwidthsize": "Torso Voxelbreite", 24 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Die Breite der Voxels, die für den Rumpf des Spielers verwendet werden (um ein Eindringen in die Arme zu verhindern)", 25 | "text.skinlayers.skulls.enable": "3D Spieler-Köpfe", 26 | "text.skinlayers.skulls.enable.tooltip": "Die Köpfe der Spieler (als Block) als 3D Modell rendern", 27 | "text.skinlayers.skullsitems.enable": "3D Spieler-Kopfitems", 28 | "text.skinlayers.skullsitems.enable.tooltip": "Die Köpfe der Spieler (als Item) als 3D Modell rendern", 29 | "text.skinlayers.skulls.voxelsize": "Spieler-Kopf Voxelgröße", 30 | "text.skinlayers.skulls.voxelsize.tooltip": "Die Größe der Voxels, die für die Köpfe der Spieler (als Block/Item) verwendet werden", 31 | "text.skinlayers.reset": "Standard", 32 | "text.skinlayers.fastrender.enable": "Schnelles Rendern", 33 | "text.skinlayers.fastrender.enable.tooltip": "Die 3D Modelle so schnell wie möglich rendern. Dies kann zu einigen grafischen Fehlern mit Transparenz führen", 34 | "text.skinlayers.firstperson.voxelsize": "Voxelgröße für die Ego-Perspektive", 35 | "text.skinlayers.firstperson.voxelsize.tooltip": "Die Größe der Voxels, die für die Hand des Spielers in der Ego-Perspektive verwendet werden", 36 | "text.skinlayers.compatibilityMode.enable": "Kompatibilitätsmodus", 37 | "text.skinlayers.compatibilityMode.enable.tooltip": "Alternative Methode zum Rendern der Ebenen, könnte einige Kompatibilitätsprobleme mit anderen Mods beheben" 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/tr_tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "Oyuncunun kostüm katmanlarını 3d renderlar", 3 | "text.skinlayers.title": "3D Görünüm Ayarları", 4 | "text.skinlayers.tab.player": "Oyuncular", 5 | "text.skinlayers.tab.heads": "Oyuncu Kafaları", 6 | "text.skinlayers.tab.other": "Diğer Ayarlar", 7 | "text.skinlayers.enable.hat": "3D Şapka", 8 | "text.skinlayers.enable.hat.tooltip": "Oyuncunun şapka katmanını 3D model olarak renderla", 9 | "text.skinlayers.enable.jacket": "3D Ceket", 10 | "text.skinlayers.enable.jacket.tooltip": "Oyuncunun ceket katmanını 3D model olarak renderla", 11 | "text.skinlayers.enable.leftsleeve": "3D Sol Kol", 12 | "text.skinlayers.enable.leftsleeve.tooltip": "Oyuncunun sol kol katmanını 3D model olarak renderla", 13 | "text.skinlayers.enable.rightsleeve": "3D Sağ Kol", 14 | "text.skinlayers.enable.rightsleeve.tooltip": "Oyuncunun sağ kol katmanını 3D model olarak renderla", 15 | "text.skinlayers.enable.leftpants": "3D Sol Pantolon", 16 | "text.skinlayers.enable.leftpants.tooltip": "Oyuncunun sol pantolon katmanını 3D model olarak renderla", 17 | "text.skinlayers.enable.rightpants": "3D Sağ Pantolon", 18 | "text.skinlayers.enable.rightpants.tooltip": "Oyuncunun sağ pantolon katmanını 3D model olarak renderla", 19 | "text.skinlayers.renderdistancelod": "Ayrıntı Mesafesi", 20 | "text.skinlayers.renderdistancelod.tooltip": "Modelin daha düşük bir ayrıntı düzeyine geçeceği mesafe (vanilla 2d)", 21 | "text.skinlayers.basevoxelsize": "Voksel Boyutu", 22 | "text.skinlayers.basevoxelsize.tooltip": "Oyuncular için kullanılan voksellerin boyutu", 23 | "text.skinlayers.headvoxelsize": "Baş Voksel Boyutu", 24 | "text.skinlayers.headvoxelsize.tooltip": "Oyuncunun kafası için kullanılan voksellerin boyutu", 25 | "text.skinlayers.bodyvoxelheightsize": "Gövde Voksel Yüksekliği", 26 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Oyuncunun vücudu için kullanılan voksellerin yüksekliği (kafanın kırpılmasını/kolların dışarı çıkmasını önlemek için kullanılır)", 27 | "text.skinlayers.bodyvoxelwidthsize": "Gövde Voksel Genişliği", 28 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Oyuncunun gövdesi için kullanılan voksellerin genişliği (kolların kırpılmasını önlemek için kullanılır)", 29 | "text.skinlayers.skulls.enable": "3D Kafatası", 30 | "text.skinlayers.skulls.enable.tooltip": "Oyuncu kafalarını (blok) 3D model olarak renderla", 31 | "text.skinlayers.skullsitems.enable": "3D Kafatası Eşyaları", 32 | "text.skinlayers.skullsitems.enable.tooltip": "Oyuncu kafalarını (eşya) 3D model olarak renderla", 33 | "text.skinlayers.skulls.voxelsize": "Kafatası Voksel Boyutu", 34 | "text.skinlayers.skulls.voxelsize.tooltip": "Oyuncu kafaları için kullanılan voksellerin boyutu (blok/eşya)", 35 | "text.skinlayers.reset": "Varsayılan", 36 | "text.skinlayers.fastrender.enable": "Hızlı Renderla", 37 | "text.skinlayers.fastrender.enable.tooltip": "3D modelleri olabildiğince hızlı renderla, bu saydamlıkla ilgili bazı grafiksel hatalara neden olabilir", 38 | "text.skinlayers.firstperson.voxelsize": "Birinci Şahıs Voksel Boyutu", 39 | "text.skinlayers.firstperson.voxelsize.tooltip": "Birinci şahısta oyuncunun eli için kullanılan voksellerin boyutu", 40 | "text.skinlayers.compatibilityMode.enable": "Uyumluluk modu", 41 | "text.skinlayers.compatibilityMode.enable.tooltip": "Katmanları oluşturmanın alternatif yolu, diğer modlarla bazı uyumluluk sorunlarını çözebilir" 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.skinlayers.title": "Настройки 3D Skin Layers", 3 | "text.skinlayers.tab.player": "Игроки", 4 | "text.skinlayers.tab.heads": "Головы игроков", 5 | "text.skinlayers.tab.other": "Другие настройки", 6 | "text.skinlayers.enable.hat": "3D головной убор", 7 | "text.skinlayers.enable.hat.tooltip": "Отображает головной убор игрока как 3D-модель", 8 | "text.skinlayers.enable.jacket": "3D куртка", 9 | "text.skinlayers.enable.jacket.tooltip": "Отображает куртку игрока как 3D-модель", 10 | "text.skinlayers.enable.leftsleeve": "3D левый рукав", 11 | "text.skinlayers.enable.leftsleeve.tooltip": "Отображает левый рукав игрока как 3D-модель", 12 | "text.skinlayers.enable.rightsleeve": "3D правый рукав", 13 | "text.skinlayers.enable.rightsleeve.tooltip": "Отображает правый рукав игрока как 3D-модель", 14 | "text.skinlayers.enable.leftpants": "3D левая штанина", 15 | "text.skinlayers.enable.leftpants.tooltip": "Отображает левую штанину игрока как 3D-модель", 16 | "text.skinlayers.enable.rightpants": "3D правая штанина", 17 | "text.skinlayers.enable.rightpants.tooltip": "Отображает правую штанину игрока как 3D-модель", 18 | "text.skinlayers.renderdistancelod": "Дистанция детализации", 19 | "text.skinlayers.renderdistancelod.tooltip": "Расстояние, на котором модель переключается на упрощённый вид (стандартный 2D)", 20 | "text.skinlayers.basevoxelsize": "Размер вокселей", 21 | "text.skinlayers.basevoxelsize.tooltip": "Размер вокселей для моделей игроков", 22 | "text.skinlayers.headvoxelsize": "Размер вокселей головы", 23 | "text.skinlayers.headvoxelsize.tooltip": "Размер вокселей для головы игрока", 24 | "text.skinlayers.bodyvoxelheightsize": "Высота вокселей тела", 25 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Высота вокселей для тела игрока (предотвращает клиппинг в голову/руки)", 26 | "text.skinlayers.bodyvoxelwidthsize": "Ширина вокселей торса", 27 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Ширина вокселей для торса игрока (предотвращает клиппинг в руки)", 28 | "text.skinlayers.skulls.enable": "3D головы-блоки", 29 | "text.skinlayers.skulls.enable.tooltip": "Отображает головы игроков (блоки) как 3D-модели", 30 | "text.skinlayers.skullsitems.enable": "3D головы-предметы", 31 | "text.skinlayers.skullsitems.enable.tooltip": "Отображает головы игроков (предметы) как 3D-модели", 32 | "text.skinlayers.skulls.voxelsize": "Размер вокселей голов", 33 | "text.skinlayers.skulls.voxelsize.tooltip": "Размер вокселей для голов игроков (блоки/предметы)", 34 | "text.skinlayers.reset": "По умолчанию", 35 | "text.skinlayers.fastrender.enable": "Быстрая отрисовка", 36 | "text.skinlayers.fastrender.enable.tooltip": "Ускоренная отрисовка моделей (может вызвать артефакты с прозрачностью)", 37 | "text.skinlayers.firstperson.voxelsize": "Размер вокселей (от первого лица)", 38 | "text.skinlayers.firstperson.voxelsize.tooltip": "Размер вокселей для рук в режиме от первого лица", 39 | "text.skinlayers.compatibilityMode.enable": "Режим совместимости", 40 | "text.skinlayers.compatibilityMode.enable.tooltip": "Альтернативный метод отрисовки (может решить проблемы совместимости)", 41 | "modmenu.descriptionTranslation.skinlayers3d": "Отображает слои скина игрока в 3D", 42 | "modmenu.descriptionTranslation.skinlayers": "Замена плоским вторым слоям скинов игроков в виде их трёхмерных аналогов. Модель автоматически будет возвращаться к стандартному двухмерному виду на расстоянии для повышения производительности." 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/PlayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import java.util.UUID; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | import dev.tr7zw.skinlayers.accessor.PlayerSettings; 8 | import dev.tr7zw.skinlayers.api.Mesh; 9 | import net.minecraft.resources.*; 10 | import net.minecraft.world.entity.EntityType; 11 | import net.minecraft.world.entity.LivingEntity; 12 | import net.minecraft.world.level.Level; 13 | 14 | /** 15 | * Keep player specific settings, data and modifies the eye location when 16 | * enabled 17 | * 18 | */ 19 | @Mixin( 20 | //? if >= 1.21.9 { 21 | 22 | net.minecraft.world.entity.Avatar.class 23 | //? } else { 24 | /* 25 | net.minecraft.world.entity.player.Player.class 26 | *///? } 27 | ) 28 | public abstract class PlayerMixin extends LivingEntity implements PlayerSettings { 29 | 30 | protected PlayerMixin(EntityType entityType, Level world) { 31 | super(entityType, world); 32 | } 33 | 34 | private Mesh headMesh; 35 | private Mesh torsoMesh; 36 | private Mesh leftArmMesh; 37 | private Mesh rightArmMesh; 38 | private Mesh leftLegMesh; 39 | private Mesh rightLegMesh; 40 | private /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ currentSkin = null; 41 | private boolean thinarms = false; 42 | 43 | @Override 44 | public Mesh getHeadMesh() { 45 | return headMesh; 46 | } 47 | 48 | @Override 49 | public void setHeadMesh(Mesh headMesh) { 50 | this.headMesh = headMesh; 51 | } 52 | 53 | @Override 54 | public Mesh getTorsoMesh() { 55 | return torsoMesh; 56 | } 57 | 58 | @Override 59 | public void setTorsoMesh(Mesh torsoMesh) { 60 | this.torsoMesh = torsoMesh; 61 | } 62 | 63 | @Override 64 | public Mesh getLeftArmMesh() { 65 | return leftArmMesh; 66 | } 67 | 68 | @Override 69 | public void setLeftArmMesh(Mesh leftArmMesh) { 70 | this.leftArmMesh = leftArmMesh; 71 | } 72 | 73 | @Override 74 | public Mesh getRightArmMesh() { 75 | return rightArmMesh; 76 | } 77 | 78 | @Override 79 | public void setRightArmMesh(Mesh rightArmMesh) { 80 | this.rightArmMesh = rightArmMesh; 81 | } 82 | 83 | @Override 84 | public Mesh getLeftLegMesh() { 85 | return leftLegMesh; 86 | } 87 | 88 | @Override 89 | public void setLeftLegMesh(Mesh leftLegMesh) { 90 | this.leftLegMesh = leftLegMesh; 91 | } 92 | 93 | @Override 94 | public Mesh getRightLegMesh() { 95 | return rightLegMesh; 96 | } 97 | 98 | @Override 99 | public void setRightLegMesh(Mesh rightLegMesh) { 100 | this.rightLegMesh = rightLegMesh; 101 | } 102 | 103 | @Override 104 | public /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ getCurrentSkin() { 105 | return currentSkin; 106 | } 107 | 108 | @Override 109 | public void setCurrentSkin(/*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ skin) { 110 | this.currentSkin = skin; 111 | } 112 | 113 | @Override 114 | public UUID getUUID() { 115 | return super.uuid; 116 | } 117 | 118 | @Override 119 | public boolean hasThinArms() { 120 | return thinarms; 121 | } 122 | 123 | @Override 124 | public void setThinArms(boolean thin) { 125 | this.thinarms = thin; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/vi_vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "Kết xuất lớp da người chơi ở dạng 3D", 3 | "text.skinlayers.title": "Cài Đặt Lớp Da 3D", 4 | "text.skinlayers.tab.player": "Người chơi", 5 | "text.skinlayers.tab.heads": "Đầu người chơi", 6 | "text.skinlayers.tab.other": "Cài đặt khác", 7 | "text.skinlayers.enable.hat": "Mũ 3D", 8 | "text.skinlayers.enable.hat.tooltip": "Kết xuất lớp mũ của người chơi dưới dạng mô hình 3D", 9 | "text.skinlayers.enable.jacket": "Áo Khoác 3D", 10 | "text.skinlayers.enable.jacket.tooltip": "Kết xuất lớp áo khoác của người chơi dưới dạng mô hình 3D", 11 | "text.skinlayers.enable.leftsleeve": "Tay Áo Trái 3D", 12 | "text.skinlayers.enable.leftsleeve.tooltip": "Kết xuất lớp tay áo trái của người chơi dưới dạng mô hình 3D", 13 | "text.skinlayers.enable.rightsleeve": "Tay Áo Phải 3D", 14 | "text.skinlayers.enable.rightsleeve.tooltip": "Kết xuất lớp tay áo phải của người chơi dưới dạng mô hình 3D", 15 | "text.skinlayers.enable.leftpants": "Ống Quần Trái 3D", 16 | "text.skinlayers.enable.leftpants.tooltip": "Kết xuất lớp ống quần trái của người chơi dưới dạng mô hình 3D", 17 | "text.skinlayers.enable.rightpants": "Ống Quần Phải 3D", 18 | "text.skinlayers.enable.rightpants.tooltip": "Kết xuất lớp ống quần phải của người chơi dưới dạng mô hình 3D", 19 | "text.skinlayers.renderdistancelod": "Khoảng Cách Chi Tiết", 20 | "text.skinlayers.renderdistancelod.tooltip": "Khoảng cách mà mô hình sẽ chuyển sang mức chi tiết thấp hơn (2D vanilla)", 21 | "text.skinlayers.basevoxelsize": "Kích Thước Voxel", 22 | "text.skinlayers.basevoxelsize.tooltip": "Kích thước của các voxel được sử dụng cho người chơi", 23 | "text.skinlayers.headvoxelsize": "Kích Thước Voxel Đầu", 24 | "text.skinlayers.headvoxelsize.tooltip": "Kích thước của các voxel được sử dụng cho đầu người chơi", 25 | "text.skinlayers.bodyvoxelheightsize": "Chiều Cao Voxel Thân", 26 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Chiều cao của các voxel được sử dụng cho thân người chơi (được sử dụng để tránh cắt vào đầu/nhô ra khỏi tay)", 27 | "text.skinlayers.bodyvoxelwidthsize": "Chiều Rộng Voxel Thân", 28 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Chiều rộng của các voxel được sử dụng cho thân người chơi (được sử dụng để tránh cắt vào tay)", 29 | "text.skinlayers.skulls.enable": "Đầu Lâu 3D", 30 | "text.skinlayers.skulls.enable.tooltip": "Kết xuất đầu người chơi (khối) dưới dạng mô hình 3D", 31 | "text.skinlayers.skullsitems.enable": "Vật Phẩm Đầu Lâu 3D", 32 | "text.skinlayers.skullsitems.enable.tooltip": "Kết xuất đầu người chơi (vật phẩm) dưới dạng mô hình 3D", 33 | "text.skinlayers.skulls.voxelsize": "Kích Thước Voxel Đầu Lâu", 34 | "text.skinlayers.skulls.voxelsize.tooltip": "Kích thước của các voxel được sử dụng cho đầu người chơi (khối/vật phẩm)", 35 | "text.skinlayers.reset": "Mặc Định", 36 | "text.skinlayers.fastrender.enable": "Kết Xuất Nhanh", 37 | "text.skinlayers.fastrender.enable.tooltip": "Kết xuất các mô hình 3D nhanh nhất có thể, điều này có thể gây ra một số trục trặc đồ họa với độ trong suốt", 38 | "text.skinlayers.firstperson.voxelsize": "Kích Thước Voxel Góc Nhìn Thứ Nhất", 39 | "text.skinlayers.firstperson.voxelsize.tooltip": "Kích thước của các voxel được sử dụng cho tay người chơi ở góc nhìn thứ nhất", 40 | "text.skinlayers.compatibilityMode.enable": "Chế Độ Tương Thích", 41 | "text.skinlayers.compatibilityMode.enable.tooltip": "Cách kết xuất các lớp thay thế, có thể khắc phục một số vấn đề tương thích với các mod khác" 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "Renders the player skin layer in 3d", 3 | "text.skinlayers.title": "3D Skin Layer Settings", 4 | "text.skinlayers.tab.player": "Players", 5 | "text.skinlayers.tab.heads": "Player Heads", 6 | "text.skinlayers.tab.other": "Other Settings", 7 | "text.skinlayers.enable.hat": "3D Hat", 8 | "text.skinlayers.enable.hat.tooltip": "Render the player's hat layer as a 3D model", 9 | "text.skinlayers.enable.jacket": "3D Jacket", 10 | "text.skinlayers.enable.jacket.tooltip": "Render the player's jacket layer as a 3D model", 11 | "text.skinlayers.enable.leftsleeve": "3D Left Sleeve", 12 | "text.skinlayers.enable.leftsleeve.tooltip": "Render the player's left sleeve layer as a 3D model", 13 | "text.skinlayers.enable.rightsleeve": "3D Right Sleeve", 14 | "text.skinlayers.enable.rightsleeve.tooltip": "Render the player's right sleeve layer as a 3D model", 15 | "text.skinlayers.enable.leftpants": "3D Left Pants", 16 | "text.skinlayers.enable.leftpants.tooltip": "Render the player's left pants layer as a 3D model", 17 | "text.skinlayers.enable.rightpants": "3D Right Pants", 18 | "text.skinlayers.enable.rightpants.tooltip": "Render the player's right pants layer as a 3D model", 19 | "text.skinlayers.renderdistancelod": "Level Of Detail Distance", 20 | "text.skinlayers.renderdistancelod.tooltip": "The distance at which the model will switch to a lower level of detail(vanilla 2d)", 21 | "text.skinlayers.basevoxelsize": "Voxel Size", 22 | "text.skinlayers.basevoxelsize.tooltip": "The size of the voxels used for players", 23 | "text.skinlayers.headvoxelsize": "Head Voxel Size", 24 | "text.skinlayers.headvoxelsize.tooltip": "The size of the voxels used for the player's head", 25 | "text.skinlayers.bodyvoxelheightsize": "Body Voxel Height", 26 | "text.skinlayers.bodyvoxelheightsize.tooltip": "The height of the voxels used for the player's body(used to prevent clipping into the head/sticking out of the arms)", 27 | "text.skinlayers.bodyvoxelwidthsize": "Torso Voxel Width", 28 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "The width of the voxels used for the player's torso(used to prevent clipping into the arms)", 29 | "text.skinlayers.skulls.enable": "3D Skulls", 30 | "text.skinlayers.skulls.enable.tooltip": "Render players heads(the block) as a 3D model", 31 | "text.skinlayers.skullsitems.enable": "3D Skull Items", 32 | "text.skinlayers.skullsitems.enable.tooltip": "Render players heads(the item) as a 3D model", 33 | "text.skinlayers.skulls.voxelsize": "Skull Voxel Size", 34 | "text.skinlayers.skulls.voxelsize.tooltip": "The size of the voxels used for players heads(the block/item)", 35 | "text.skinlayers.reset": "Default", 36 | "text.skinlayers.fastrender.enable": "Fast Render", 37 | "text.skinlayers.fastrender.enable.tooltip": "Render the 3D models as fast as possible, this may cause some graphical glitches with transparency", 38 | "text.skinlayers.firstperson.voxelsize": "First Person Voxel Size", 39 | "text.skinlayers.firstperson.voxelsize.tooltip": "The size of the voxels used for the player's hand in first person", 40 | "text.skinlayers.compatibilityMode.enable": "Compatibility Mode", 41 | "text.skinlayers.compatibilityMode.enable.tooltip": "Alternative way to render the layers, might fix some compatibility issues with other mods", 42 | "text.skinlayers.irisCompatibilityMode.enable": "Iris Compatibility Mode", 43 | "text.skinlayers.irisCompatibilityMode.enable.tooltip": "Alternative way to render the layers, fixes weird shadows/colors when using Iris shaders. Only turn this setting on when needed. Will break the corner connections.", 44 | "text.skinlayers.applySodiumWorkaround.enable": "Sodium Workaround", 45 | "text.skinlayers.applySodiumWorkaround.enable.tooltip": "Workaround for Sodium having flipped Cube sides in some versions(?). Only enable this if you are using Sodium and notice visual issues with the skin layers sides missing." 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/assets/skinlayers3d/lang/es_ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.skinlayers3d": "Renderiza la capa de la skin del jugador en 3D", 3 | "text.skinlayers.title": "Configuración de Capas 3D de la Skin", 4 | "text.skinlayers.tab.player": "Jugadores", 5 | "text.skinlayers.tab.heads": "Cabezas de jugadores", 6 | "text.skinlayers.tab.other": "Otros ajustes", 7 | "text.skinlayers.enable.hat": "Sombrero 3D", 8 | "text.skinlayers.enable.hat.tooltip": "Renderizar la capa del sombrero del jugador como un modelo 3D", 9 | "text.skinlayers.enable.jacket": "Campera 3D", 10 | "text.skinlayers.enable.jacket.tooltip": "Renderizar la capa de la campera del jugador como un modelo 3D", 11 | "text.skinlayers.enable.leftsleeve": "Manga Izquierda 3D", 12 | "text.skinlayers.enable.leftsleeve.tooltip": "Renderizar la capa de la manga izquierda del jugador como un modelo 3D", 13 | "text.skinlayers.enable.rightsleeve": "Manga Derecha 3D", 14 | "text.skinlayers.enable.rightsleeve.tooltip": "Renderizar la capa de la manga derecha del jugador como un modelo 3D", 15 | "text.skinlayers.enable.leftpants": "Pantalón Izquierdo 3D", 16 | "text.skinlayers.enable.leftpants.tooltip": "Renderizar la capa del pantalón izquierdo del jugador como un modelo 3D", 17 | "text.skinlayers.enable.rightpants": "Pantalón Derecho 3D", 18 | "text.skinlayers.enable.rightpants.tooltip": "Renderizar la capa del pantalón derecho del jugador como un modelo 3D", 19 | "text.skinlayers.renderdistancelod": "Distancia de Nivel de Detalle", 20 | "text.skinlayers.renderdistancelod.tooltip": "La distancia a la cual el modelo cambia a un nivel de detalle más bajo (2D clásico)", 21 | "text.skinlayers.basevoxelsize": "Tamaño de Vóxel Base", 22 | "text.skinlayers.basevoxelsize.tooltip": "Tamaño de los vóxeles usados para los jugadores", 23 | "text.skinlayers.headvoxelsize": "Tamaño de Vóxel de Cabeza", 24 | "text.skinlayers.headvoxelsize.tooltip": "Tamaño de los vóxeles usados para la cabeza del jugador", 25 | "text.skinlayers.bodyvoxelheightsize": "Altura del Vóxel del Cuerpo", 26 | "text.skinlayers.bodyvoxelheightsize.tooltip": "Altura de los vóxeles usados para el cuerpo del jugador (evita que se sobrepongan con la cabeza o sobresalgan de los brazos)", 27 | "text.skinlayers.bodyvoxelwidthsize": "Ancho del Vóxel del Torso", 28 | "text.skinlayers.bodyvoxelwidthsize.tooltip": "Ancho de los vóxeles usados para el torso del jugador (evita superposiciones con los brazos)", 29 | "text.skinlayers.skulls.enable": "Cabezas 3D", 30 | "text.skinlayers.skulls.enable.tooltip": "Renderizar las cabezas de jugadores (el bloque) como un modelo 3D", 31 | "text.skinlayers.skullsitems.enable": "Ítems de Cabeza 3D", 32 | "text.skinlayers.skullsitems.enable.tooltip": "Renderizar las cabezas de jugadores (el ítem) como un modelo 3D", 33 | "text.skinlayers.skulls.voxelsize": "Tamaño de Vóxel de Cabeza", 34 | "text.skinlayers.skulls.voxelsize.tooltip": "Tamaño de los vóxeles usados para las cabezas de jugadores (bloque/ítem)", 35 | "text.skinlayers.reset": "Restablecer", 36 | "text.skinlayers.fastrender.enable": "Renderizado Rápido", 37 | "text.skinlayers.fastrender.enable.tooltip": "Renderizar los modelos 3D lo más rápido posible; puede causar errores gráficos con transparencias", 38 | "text.skinlayers.firstperson.voxelsize": "Tamaño de Vóxel en Primera Persona", 39 | "text.skinlayers.firstperson.voxelsize.tooltip": "Tamaño de los vóxeles usados para la mano del jugador en vista de primera persona", 40 | "text.skinlayers.compatibilityMode.enable": "Modo de Compatibilidad", 41 | "text.skinlayers.compatibilityMode.enable.tooltip": "Método alternativo para renderizar las capas, puede solucionar problemas de compatibilidad con otros mods", 42 | "text.skinlayers.irisCompatibilityMode.enable": "Modo de compatibilidad con Iris", 43 | "text.skinlayers.irisCompatibilityMode.enable.tooltip": "Método alternativo para renderizar las capas. Corrige sombras o colores extraños al usar shaders Iris. Activá esta opción solo si es necesario. Puede romper las conexiones en las esquinas." 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/ModelPartMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import java.util.Map; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | import com.mojang.blaze3d.vertex.PoseStack; 12 | import com.mojang.blaze3d.vertex.VertexConsumer; 13 | 14 | import dev.tr7zw.skinlayers.accessor.ModelPartInjector; 15 | import dev.tr7zw.skinlayers.api.Mesh; 16 | import dev.tr7zw.skinlayers.api.OffsetProvider; 17 | import lombok.Getter; 18 | import net.minecraft.client.model.geom.ModelPart; 19 | 20 | @Mixin(value = ModelPart.class, priority = 300) 21 | public class ModelPartMixin implements ModelPartInjector { 22 | 23 | @Shadow 24 | @Getter 25 | boolean visible; 26 | //? if >= 1.17.0 { 27 | 28 | @Shadow 29 | private Map children; 30 | //? } 31 | 32 | @Getter 33 | private Mesh injectedMesh = null; 34 | @Getter 35 | private OffsetProvider offsetProvider = null; 36 | 37 | //? if >= 1.21.0 { 38 | 39 | @Inject(method = "Lnet/minecraft/client/model/geom/ModelPart;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;III)V", at = @At(value = "HEAD"), cancellable = true) 40 | public void render(PoseStack poseStack, VertexConsumer vertexConsumer, int light, int overlay, int color, 41 | CallbackInfo ci) { 42 | if (visible && injectedMesh != null) { 43 | poseStack.pushPose(); 44 | translateAndRotate(poseStack); 45 | offsetProvider.applyOffset(poseStack, injectedMesh); 46 | injectedMesh.render((ModelPart) (Object) this, poseStack, vertexConsumer, light, overlay, color); 47 | poseStack.popPose(); 48 | ci.cancel(); 49 | } 50 | } 51 | //? } else { 52 | /* 53 | @Inject(method = "Lnet/minecraft/client/model/geom/ModelPart;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;IIFFFF)V", at = @At(value = "HEAD"), cancellable = true) 54 | public void render(PoseStack poseStack, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha, 55 | CallbackInfo ci) { 56 | if (visible && injectedMesh != null) { 57 | poseStack.pushPose(); 58 | translateAndRotate(poseStack); 59 | offsetProvider.applyOffset(poseStack, injectedMesh); 60 | injectedMesh.render((ModelPart)(Object)this, poseStack, vertexConsumer, light, overlay, red, green, blue, alpha); 61 | poseStack.popPose(); 62 | ci.cancel(); 63 | return; 64 | } 65 | //? if >= 1.17.0 { 66 | 67 | if(visible && dev.tr7zw.skinlayers.util.SodiumWorkaround.IS_SODIUM_WORKAROUND_NEEDED && (children.containsKey("head") || children.containsKey("hat"))) { 68 | poseStack.pushPose(); 69 | translateAndRotate(poseStack); 70 | compile(poseStack.last(), vertexConsumer, light, overlay, red, green, blue, alpha); 71 | for(java.util.Map.Entry child : this.children.entrySet()) { 72 | child.getValue().render(poseStack, vertexConsumer, light, overlay, red, green, blue, alpha); 73 | } 74 | poseStack.popPose(); 75 | ci.cancel(); 76 | } 77 | //? } 78 | } 79 | 80 | *///? } 81 | 82 | @Override 83 | public void setInjectedMesh(Mesh mesh, OffsetProvider offsetProvider) { 84 | this.injectedMesh = mesh; 85 | this.offsetProvider = offsetProvider; 86 | } 87 | 88 | @Shadow 89 | public void translateAndRotate(PoseStack poseStack) { 90 | 91 | } 92 | 93 | //? if >= 1.17.0 && < 1.21.0 { 94 | /* 95 | @Shadow 96 | public void compile(PoseStack.Pose pose, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, 97 | float red, float green, float blue, float alpha) { 98 | } 99 | *///? } 100 | 101 | @Override 102 | public void prepareTranslateAndRotate(PoseStack poseStack) { 103 | translateAndRotate(poseStack); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/BlockEntityWithoutLevelRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import static dev.tr7zw.skinlayers.SkullRendererCache.lastSkull; 4 | import static dev.tr7zw.skinlayers.SkullRendererCache.renderNext; 5 | import static dev.tr7zw.skinlayers.SkullRendererCache.itemCache; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | import dev.tr7zw.skinlayers.SkinUtil; 13 | import dev.tr7zw.skinlayers.SkullRendererCache.ItemSettings; 14 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 15 | import dev.tr7zw.transition.mc.ItemUtil; 16 | import net.minecraft.world.item.ItemStack; 17 | 18 | //? if < 1.21.6 { 19 | /* 20 | import com.mojang.authlib.GameProfile; 21 | import com.mojang.blaze3d.vertex.PoseStack; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | import dev.tr7zw.skinlayers.SkinLayersModBase; 24 | import dev.tr7zw.skinlayers.SkullRendererCache.ItemSettings; 25 | import net.minecraft.client.renderer.MultiBufferSource; 26 | 27 | //? if >= 1.19.4 { 28 | 29 | import net.minecraft.world.item.ItemDisplayContext; 30 | //? } else { 31 | 32 | // import net.minecraft.client.renderer.block.model.ItemTransforms; 33 | //? } 34 | //? if >= 1.21.4 { 35 | 36 | import net.minecraft.client.renderer.special.SkullSpecialRenderer; 37 | import net.minecraft.world.item.component.ResolvableProfile; 38 | //? } 39 | *///? } 40 | 41 | //? if >= 1.21.6 { 42 | 43 | @Mixin(net.minecraft.client.renderer.special.PlayerHeadSpecialRenderer.class) 44 | public class BlockEntityWithoutLevelRendererMixin { 45 | @Inject(method = "extractArgument", at = @At("HEAD")) 46 | public void extractArgument(ItemStack itemStack, 47 | //? if >= 1.21.9 { 48 | 49 | CallbackInfoReturnable cir) { 50 | //? } else { 51 | /* 52 | CallbackInfoReturnable cir) { 53 | *///? } 54 | var profile = ItemUtil.getGameProfile(itemStack); 55 | if (profile != null) { 56 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(profile, it -> new ItemSettings()); 57 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 58 | SkinUtil.setup3dLayers(profile, lastSkull); 59 | } 60 | renderNext = lastSkull.getHeadLayers() != null; 61 | } 62 | } 63 | } 64 | //? } else if >= 1.21.4 { 65 | /* 66 | @Mixin(net.minecraft.client.renderer.special.SkullSpecialRenderer.class) 67 | public class BlockEntityWithoutLevelRendererMixin { 68 | @Inject(method = "render", at = @At("HEAD")) 69 | public void render(ResolvableProfile resolvableProfile, ItemDisplayContext itemDisplayContext, PoseStack poseStack, 70 | MultiBufferSource multiBufferSource, int i, int j, boolean bl, CallbackInfo ci) { 71 | if (SkinLayersModBase.config.enableSkullsItems && resolvableProfile != null && resolvableProfile.isResolved()) { 72 | GameProfile gameProfile = resolvableProfile.gameProfile(); 73 | if (gameProfile != null) { 74 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(gameProfile, it -> new ItemSettings()); 75 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 76 | SkinUtil.setup3dLayers(gameProfile, lastSkull); 77 | } 78 | renderNext = lastSkull.getHeadLayers() != null; 79 | } 80 | } 81 | } 82 | } 83 | *///? } else { 84 | /* 85 | import net.minecraft.world.item.BlockItem; 86 | import net.minecraft.world.item.Item; 87 | 88 | import net.minecraft.world.item.ItemStack; 89 | import net.minecraft.world.level.block.AbstractSkullBlock; 90 | import net.minecraft.world.level.block.Block; 91 | import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; 92 | @Mixin(BlockEntityWithoutLevelRenderer.class) 93 | public class BlockEntityWithoutLevelRendererMixin { 94 | 95 | @Inject(method = "renderByItem", at = @At("HEAD")) 96 | //?if >= 1.19.4 { 97 | public void renderByItem(ItemStack itemStack, ItemDisplayContext itemDisplayContext, PoseStack poseStack, 98 | MultiBufferSource multiBufferSource, int i, int j, CallbackInfo info) { 99 | //? } else { 100 | // public void renderByItem(ItemStack itemStack, ItemTransforms.TransformType transformType, PoseStack poseStack, 101 | // MultiBufferSource multiBufferSource, int i, int j, CallbackInfo info) { 102 | //? } 103 | if (!SkinLayersModBase.config.enableSkullsItems) 104 | return; 105 | Item item = itemStack.getItem(); 106 | if (item instanceof BlockItem) { 107 | Block block = ((BlockItem) item).getBlock(); 108 | if (block instanceof AbstractSkullBlock) { 109 | GameProfile gameProfile = ItemUtil.getGameProfile(itemStack); 110 | if (gameProfile != null) { 111 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(itemStack, it -> new ItemSettings()); 112 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 113 | SkinUtil.setup3dLayers(gameProfile, lastSkull); 114 | } 115 | renderNext = lastSkull.getHeadLayers() != null; 116 | } 117 | } 118 | } 119 | } 120 | 121 | } 122 | *///? } 123 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/render/CustomizableCubeListBuilder.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.render; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | import com.google.common.collect.Lists; 9 | 10 | import dev.tr7zw.skinlayers.SkinLayersModBase; 11 | import dev.tr7zw.skinlayers.api.*; 12 | import dev.tr7zw.skinlayers.util.*; 13 | import dev.tr7zw.skinlayers.versionless.render.CustomizableCube; 14 | import dev.tr7zw.skinlayers.versionless.util.Direction; 15 | import dev.tr7zw.skinlayers.versionless.util.wrapper.ModelBuilder; 16 | import net.minecraft.client.model.geom.ModelPart.Cube; 17 | //? if >= 1.17.0 { 18 | 19 | import net.minecraft.client.model.geom.builders.CubeListBuilder; 20 | //? } 21 | 22 | public class CustomizableCubeListBuilder implements ModelBuilder { 23 | 24 | private final List cubes = Lists.newArrayList(); 25 | private final List vanillaCubes = Lists.newArrayList(); 26 | private int u; 27 | private int v; 28 | private boolean mirror; 29 | private int textureWidth = 64; 30 | private int textureHeight = 64; 31 | 32 | public static ModelBuilder create() { 33 | return new CustomizableCubeListBuilder(); 34 | } 35 | 36 | public ModelBuilder textureSize(int width, int height) { 37 | this.textureWidth = width; 38 | this.textureHeight = height; 39 | return this; 40 | } 41 | 42 | @Override 43 | public ModelBuilder uv(int u, int v) { 44 | this.u = u; 45 | this.v = v; 46 | return this; 47 | } 48 | 49 | @Override 50 | public ModelBuilder mirror(boolean bl) { 51 | this.mirror = bl; 52 | return this; 53 | } 54 | 55 | public List getCubes() { 56 | return cubes; 57 | } 58 | 59 | public List getVanillaCubes() { 60 | return vanillaCubes; 61 | } 62 | 63 | @Override 64 | public ModelBuilder addBox(float x, float y, float z, float pixelSize, Direction[] hide, Direction[][] corners) { 65 | if (!SkinLayersModBase.config.irisCompatibilityMode) { 66 | this.cubes.add(new CustomizableCube(this.u, this.v, (mirror ? -1 : 1) * x, y, z, pixelSize, pixelSize, 67 | pixelSize, 0, 0, 0, this.mirror, textureWidth, textureHeight, hide, corners)); 68 | } else { 69 | //? if >= 1.20.0 { 70 | 71 | // Hacky workaround for Iris compatibility 72 | dir: for (Direction dir : Direction.values()) { 73 | for (Direction hideDir : hide) { 74 | if (hideDir == dir) { 75 | continue dir; 76 | } 77 | } 78 | int uO = this.u; 79 | int vO = this.v; 80 | // Adjust UV coordinates based on the direction to use the correct pixel 81 | switch (dir) { 82 | case DOWN: 83 | uO -= pixelSize; 84 | break; 85 | case UP: 86 | uO -= pixelSize * 2; 87 | break; 88 | case NORTH: 89 | uO -= pixelSize; 90 | vO -= pixelSize; 91 | break; 92 | case SOUTH: 93 | uO -= pixelSize * 3; 94 | vO -= pixelSize; 95 | break; 96 | case WEST: 97 | 98 | vO -= pixelSize; 99 | break; 100 | case EAST: 101 | uO -= pixelSize * 2; 102 | vO -= pixelSize; 103 | break; 104 | } 105 | CubeListBuilder cubeList = CubeListBuilder.create(); 106 | var mcDir = switch (dir) { 107 | case UP: 108 | yield net.minecraft.core.Direction.UP; 109 | case DOWN: 110 | yield net.minecraft.core.Direction.DOWN; 111 | case NORTH: 112 | yield net.minecraft.core.Direction.NORTH; 113 | case EAST: 114 | yield SodiumWorkaround.applySodiumWorkaround() ? net.minecraft.core.Direction.WEST 115 | : net.minecraft.core.Direction.EAST; 116 | case WEST: 117 | yield SodiumWorkaround.applySodiumWorkaround() ? net.minecraft.core.Direction.EAST 118 | : net.minecraft.core.Direction.WEST; 119 | case SOUTH: 120 | yield net.minecraft.core.Direction.SOUTH; 121 | }; 122 | cubeList.texOffs(uO, vO).mirror(mirror).addBox(x, y, z, pixelSize, pixelSize, pixelSize, 123 | new HashSet<>(Arrays.asList(mcDir))); 124 | this.vanillaCubes.add(cubeList.getCubes().get(0).bake(textureWidth, textureHeight)); 125 | } 126 | //? } else { 127 | /* 128 | this.cubes.add(new CustomizableCube(this.u, this.v, (mirror ? -1 : 1) * x, y, z, pixelSize, pixelSize, 129 | pixelSize, 0, 0, 0, this.mirror, textureWidth, textureHeight, hide, corners)); 130 | *///? } 131 | } 132 | return this; 133 | } 134 | 135 | @Override 136 | public ModelBuilder addVanillaBox(float x, float y, float z, float width, float height, float depth) { 137 | if (mirror) { 138 | x = -1; // FIXME: Why 139 | } 140 | this.vanillaCubes.add(SkinLayersAPI.getBoxBuilder().build(new BoxBuilder.BoxDefinition(u, v, mirror, x, y, z, 141 | width, height, depth, textureWidth, textureHeight))); 142 | return this; 143 | } 144 | 145 | @Override 146 | public boolean isEmpty() { 147 | return getCubes().isEmpty() && getVanillaCubes().isEmpty(); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/PlayerModelMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import lombok.Setter; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import dev.tr7zw.skinlayers.SkinLayersModBase; 11 | import dev.tr7zw.skinlayers.SkinUtil; 12 | import dev.tr7zw.skinlayers.accessor.ModelPartInjector; 13 | import dev.tr7zw.skinlayers.accessor.PlayerEntityModelAccessor; 14 | import dev.tr7zw.skinlayers.accessor.PlayerSettings; 15 | import dev.tr7zw.skinlayers.api.OffsetProvider; 16 | import dev.tr7zw.transition.mc.entitywrapper.EntityRenderStateExtender; 17 | import net.minecraft.client.Minecraft; 18 | //? if >= 1.21.11 { 19 | 20 | import net.minecraft.client.model.player.*; 21 | //? } 22 | import net.minecraft.client.model.*; 23 | import net.minecraft.client.model.geom.ModelPart; 24 | import net.minecraft.world.entity.EquipmentSlot; 25 | import net.minecraft.world.entity.LivingEntity; 26 | import net.minecraft.world.item.ItemStack; 27 | 28 | @Mixin(PlayerModel.class) 29 | public class PlayerModelMixin extends HumanoidModel implements PlayerEntityModelAccessor { 30 | 31 | //? if >= 1.17.0 { 32 | 33 | public PlayerModelMixin(ModelPart modelPart) { 34 | super(modelPart); 35 | } 36 | //? } else { 37 | 38 | // public PlayerModelMixin(float f) { 39 | // super(f); 40 | // } 41 | //? } 42 | 43 | @Shadow 44 | public ModelPart leftSleeve; 45 | @Shadow 46 | public ModelPart rightSleeve; 47 | @Shadow 48 | public ModelPart leftPants; 49 | @Shadow 50 | public ModelPart rightPants; 51 | @Shadow 52 | public ModelPart jacket; 53 | @Shadow 54 | private boolean slim; 55 | 56 | @Setter 57 | private boolean ignored; 58 | 59 | @Override 60 | public boolean hasThinArms() { 61 | return slim; 62 | } 63 | 64 | //? if >= 1.21.2 { 65 | 66 | @Inject(method = "setupAnim", at = @At("TAIL"), cancellable = true) 67 | //? if >= 1.21.9 { 68 | 69 | public void setupAnim(net.minecraft.client.renderer.entity.state.AvatarRenderState playerRenderState, 70 | CallbackInfo ci) { 71 | net.minecraft.world.entity.Avatar abstractClientPlayer = null; 72 | if (ignored) 73 | return; 74 | if (!((Object) this instanceof PlayerCapeModel) 75 | && playerRenderState instanceof EntityRenderStateExtender extender 76 | && extender.getTransitionEntity() instanceof net.minecraft.world.entity.Avatar entity) { 77 | abstractClientPlayer = entity; 78 | } else { 79 | return; 80 | } 81 | //? } else { 82 | /* 83 | public void setupAnim( 84 | net.minecraft.client.renderer.entity.state.PlayerRenderState playerRenderState, 85 | CallbackInfo ci) { 86 | net.minecraft.client.player.AbstractClientPlayer abstractClientPlayer = null; 87 | if (playerRenderState instanceof EntityRenderStateExtender extender && extender.getTransitionEntity() instanceof net.minecraft.client.player.AbstractClientPlayer entity) { 88 | abstractClientPlayer = entity; 89 | } else { 90 | return; 91 | } 92 | *///? } 93 | 94 | PlayerSettings settings = (PlayerSettings) abstractClientPlayer; 95 | // reset all injected layers 96 | ((ModelPartInjector) (Object) hat).setInjectedMesh(null, null); 97 | ((ModelPartInjector) (Object) jacket).setInjectedMesh(null, null); 98 | ((ModelPartInjector) (Object) leftSleeve).setInjectedMesh(null, null); 99 | ((ModelPartInjector) (Object) rightSleeve).setInjectedMesh(null, null); 100 | ((ModelPartInjector) (Object) leftPants).setInjectedMesh(null, null); 101 | ((ModelPartInjector) (Object) rightPants).setInjectedMesh(null, null); 102 | if (Minecraft.getInstance().player == null || abstractClientPlayer.distanceToSqr(Minecraft 103 | .getInstance().gameRenderer.getMainCamera() 104 | /*? >= 1.21.11 {*/ .position() /*?} else {*/ /*.getPosition() *//*?}*/) > SkinLayersModBase.config.renderDistanceLOD 105 | * SkinLayersModBase.config.renderDistanceLOD) { 106 | return; 107 | } 108 | if (!SkinUtil.setup3dLayers(abstractClientPlayer, settings, slim)) { 109 | // fall back to vanilla 110 | return; 111 | } 112 | // Inject layers into the vanilla model 113 | ItemStack itemStack = abstractClientPlayer.getItemBySlot(EquipmentSlot.HEAD); 114 | if (SkinLayersModBase.config.enableHat 115 | && (itemStack == null || !SkinLayersModBase.hideHeadLayers.contains(itemStack.getItem()))) { 116 | ((ModelPartInjector) (Object) hat).setInjectedMesh(settings.getHeadMesh(), OffsetProvider.HEAD); 117 | } 118 | if (SkinLayersModBase.config.enableJacket) { 119 | ((ModelPartInjector) (Object) jacket).setInjectedMesh(settings.getTorsoMesh(), OffsetProvider.BODY); 120 | } 121 | if (SkinLayersModBase.config.enableLeftSleeve) { 122 | ((ModelPartInjector) (Object) leftSleeve).setInjectedMesh(settings.getLeftArmMesh(), 123 | slim ? OffsetProvider.LEFT_ARM_SLIM : OffsetProvider.LEFT_ARM); 124 | } 125 | if (SkinLayersModBase.config.enableRightSleeve) { 126 | ((ModelPartInjector) (Object) rightSleeve).setInjectedMesh(settings.getRightArmMesh(), 127 | slim ? OffsetProvider.RIGHT_ARM_SLIM : OffsetProvider.RIGHT_ARM); 128 | } 129 | if (SkinLayersModBase.config.enableLeftPants) { 130 | ((ModelPartInjector) (Object) leftPants).setInjectedMesh(settings.getLeftLegMesh(), 131 | OffsetProvider.LEFT_LEG); 132 | } 133 | if (SkinLayersModBase.config.enableRightPants) { 134 | ((ModelPartInjector) (Object) rightPants).setInjectedMesh(settings.getRightLegMesh(), 135 | OffsetProvider.RIGHT_LEG); 136 | } 137 | } 138 | //? } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /gradlecw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | COMPOSE=$APP_HOME/gradle/gradle-compose.jar 86 | 87 | # Determine the Java command to use to start the JVM. 88 | if [ -n "$JAVA_HOME" ] ; then 89 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 90 | # IBM's JDK on AIX uses strange locations for the executables 91 | JAVACMD="$JAVA_HOME/jre/sh/java" 92 | else 93 | JAVACMD="$JAVA_HOME/bin/java" 94 | fi 95 | if [ ! -x "$JAVACMD" ] ; then 96 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 97 | 98 | Please set the JAVA_HOME variable in your environment to match the 99 | location of your Java installation." 100 | fi 101 | else 102 | JAVACMD="java" 103 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 104 | 105 | Please set the JAVA_HOME variable in your environment to match the 106 | location of your Java installation." 107 | fi 108 | 109 | # Increase the maximum file descriptors if we can. 110 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 111 | MAX_FD_LIMIT=`ulimit -H -n` 112 | if [ $? -eq 0 ] ; then 113 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 114 | MAX_FD="$MAX_FD_LIMIT" 115 | fi 116 | ulimit -n $MAX_FD 117 | if [ $? -ne 0 ] ; then 118 | warn "Could not set maximum file descriptor limit: $MAX_FD" 119 | fi 120 | else 121 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 122 | fi 123 | fi 124 | 125 | # For Darwin, add options to specify how the application appears in the dock 126 | if $darwin; then 127 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 128 | fi 129 | 130 | # For Cygwin or MSYS, switch paths to Windows format before running java 131 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 132 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 133 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 134 | 135 | JAVACMD=`cygpath --unix "$JAVACMD"` 136 | 137 | # We build the pattern for arguments to be converted via cygpath 138 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 139 | SEP="" 140 | for dir in $ROOTDIRSRAW ; do 141 | ROOTDIRS="$ROOTDIRS$SEP$dir" 142 | SEP="|" 143 | done 144 | OURCYGPATTERN="(^($ROOTDIRS))" 145 | # Add a user-defined pattern to the cygpath arguments 146 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 147 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 148 | fi 149 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 150 | i=0 151 | for arg in "$@" ; do 152 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 153 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 154 | 155 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 156 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 157 | else 158 | eval `echo args$i`="\"$arg\"" 159 | fi 160 | i=`expr $i + 1` 161 | done 162 | case $i in 163 | 0) set -- ;; 164 | 1) set -- "$args0" ;; 165 | 2) set -- "$args0" "$args1" ;; 166 | 3) set -- "$args0" "$args1" "$args2" ;; 167 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 168 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 169 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 170 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 171 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 172 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 173 | esac 174 | fi 175 | 176 | 177 | 178 | "$JAVACMD" -jar $COMPOSE 179 | 180 | # Escape application args 181 | save () { 182 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 183 | echo " " 184 | } 185 | APP_ARGS=`save "$@"` 186 | 187 | # Collect all arguments for the java command, following the shell quoting and substitution rules 188 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 189 | 190 | exec "$JAVACMD" "$@" 191 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/mixin/CustomHeadLayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.mixin; 2 | 3 | import static dev.tr7zw.skinlayers.SkullRendererCache.itemCache; 4 | import static dev.tr7zw.skinlayers.SkullRendererCache.lastSkull; 5 | import static dev.tr7zw.skinlayers.SkullRendererCache.renderNext; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | import com.mojang.authlib.GameProfile; 14 | import com.mojang.blaze3d.vertex.PoseStack; 15 | 16 | import dev.tr7zw.skinlayers.SkinLayersModBase; 17 | import dev.tr7zw.skinlayers.SkinUtil; 18 | import dev.tr7zw.skinlayers.SkullRendererCache.ItemSettings; 19 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 20 | import dev.tr7zw.transition.mc.PlayerUtil; 21 | import net.minecraft.client.Minecraft; 22 | import net.minecraft.client.model.EntityModel; 23 | import net.minecraft.client.model.HeadedModel; 24 | import net.minecraft.client.renderer.MultiBufferSource; 25 | import net.minecraft.client.renderer.*; 26 | import net.minecraft.client.renderer.entity.layers.CustomHeadLayer; 27 | import net.minecraft.world.entity.LivingEntity; 28 | 29 | //? if >= 1.21.11 { 30 | 31 | import net.minecraft.client.renderer.rendertype.*; 32 | //? } 33 | 34 | //? if < 1.21.4 { 35 | /* 36 | import com.mojang.authlib.GameProfile; 37 | import dev.tr7zw.skinlayers.SkinUtil; 38 | import dev.tr7zw.skinlayers.SkullRendererCache.ItemSettings; 39 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 40 | import dev.tr7zw.transition.mc.ItemUtil; 41 | import net.minecraft.world.item.BlockItem; 42 | import net.minecraft.world.item.Item; 43 | import net.minecraft.world.item.ItemStack; 44 | import net.minecraft.world.level.block.AbstractSkullBlock; 45 | *///? } 46 | 47 | @Mixin(CustomHeadLayer.class) 48 | public class CustomHeadLayerMixin { 49 | 50 | //? if >= 1.21.9 { 51 | 52 | @Inject(method = "resolveSkullRenderType", at = @At("HEAD")) 53 | private void resolveSkullRenderType( 54 | net.minecraft.client.renderer.entity.state.LivingEntityRenderState livingEntityRenderState, 55 | net.minecraft.world.level.block.SkullBlock.Type type, CallbackInfoReturnable ci) { 56 | if (Minecraft.getInstance().player != null && Minecraft.getInstance().gameRenderer.getMainCamera() 57 | /*? >= 1.21.11 {*/ .position() /*?} else {*/ /*.getPosition() *//*?}*/ 58 | .distanceToSqr(livingEntityRenderState.x, livingEntityRenderState.y, 59 | livingEntityRenderState.z) > SkinLayersModBase.config.renderDistanceLOD 60 | * SkinLayersModBase.config.renderDistanceLOD) { 61 | return; // too far away 62 | } 63 | if ((!livingEntityRenderState.headItem.isEmpty() || livingEntityRenderState.wornHeadType != null) 64 | && livingEntityRenderState.wornHeadProfile != null) { 65 | GameProfile gameProfile = PlayerUtil.getProfile(livingEntityRenderState.wornHeadProfile); 66 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(gameProfile, it -> new ItemSettings()); 67 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 68 | SkinUtil.setup3dLayers(gameProfile, lastSkull); 69 | } 70 | renderNext = lastSkull.getHeadLayers() != null; 71 | } 72 | } 73 | //? } else if >= 1.21.2 { 74 | /* 75 | @SuppressWarnings("resource") 76 | @Inject(method = "render", at = @At("HEAD")) 77 | public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 78 | net.minecraft.client.renderer.entity.state.LivingEntityRenderState livingEntityRenderState, float f, 79 | float g, CallbackInfo info) { 80 | 81 | if (Minecraft.getInstance().player != null && Minecraft.getInstance().gameRenderer.getMainCamera().getPosition() 82 | .distanceToSqr(livingEntityRenderState.x, livingEntityRenderState.y, 83 | livingEntityRenderState.z) > SkinLayersModBase.config.renderDistanceLOD 84 | * SkinLayersModBase.config.renderDistanceLOD) { 85 | return; // too far away 86 | } 87 | //? if >= 1.21.4 { 88 | 89 | if ((!livingEntityRenderState.headItem.isEmpty() || livingEntityRenderState.wornHeadType != null) 90 | && livingEntityRenderState.wornHeadProfile != null 91 | && livingEntityRenderState.wornHeadProfile.isResolved()) { 92 | GameProfile gameProfile = livingEntityRenderState.wornHeadProfile.gameProfile(); 93 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(gameProfile, it -> new ItemSettings()); 94 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 95 | SkinUtil.setup3dLayers(gameProfile, lastSkull); 96 | } 97 | renderNext = lastSkull.getHeadLayers() != null; 98 | } 99 | //? } else { 100 | /^ 101 | setupHeadRendering(livingEntityRenderState.headItem); 102 | ^///? } 103 | } 104 | *///? } else { 105 | /* 106 | @SuppressWarnings("resource") 107 | @Inject(method = "render", at = @At("HEAD")) 108 | public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, float f, 109 | float g, float h, float j, float k, float l, CallbackInfo info) { 110 | if (Minecraft.getInstance().player != null && livingEntity.distanceToSqr(Minecraft.getInstance().gameRenderer 111 | .getMainCamera().getPosition()) > SkinLayersModBase.config.renderDistanceLOD 112 | * SkinLayersModBase.config.renderDistanceLOD) { 113 | return; // too far away 114 | } 115 | setupHeadRendering(livingEntity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.HEAD)); 116 | } 117 | *///? } 118 | 119 | //? if < 1.21.4 { 120 | /* 121 | private void setupHeadRendering(ItemStack itemStack) { 122 | if (!SkinLayersModBase.config.enableSkulls || itemStack.isEmpty()) 123 | return; 124 | Item item = itemStack.getItem(); 125 | if (item instanceof BlockItem && ((BlockItem) item).getBlock() instanceof AbstractSkullBlock) { 126 | GameProfile gameProfile = ItemUtil.getGameProfile(itemStack); 127 | if (gameProfile != null) { 128 | lastSkull = (SkullSettings) itemCache.computeIfAbsent(itemStack, it -> new ItemSettings()); 129 | if (!lastSkull.initialized() && lastSkull.getHeadLayers() == null) { 130 | SkinUtil.setup3dLayers(gameProfile, lastSkull); 131 | } 132 | renderNext = lastSkull.getHeadLayers() != null; 133 | } 134 | } 135 | } 136 | *///? } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/renderlayers/CustomLayerFeatureRenderer.java: -------------------------------------------------------------------------------- 1 | //? if < 1.21.2 { 2 | /* 3 | package dev.tr7zw.skinlayers.renderlayers; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Set; 8 | import java.util.function.Function; 9 | import java.util.function.Supplier; 10 | 11 | import com.google.common.collect.Sets; 12 | import com.mojang.blaze3d.vertex.PoseStack; 13 | import com.mojang.blaze3d.vertex.VertexConsumer; 14 | 15 | import dev.tr7zw.skinlayers.SkinLayersModBase; 16 | import dev.tr7zw.skinlayers.SkinUtil; 17 | import dev.tr7zw.skinlayers.accessor.PlayerEntityModelAccessor; 18 | import dev.tr7zw.skinlayers.accessor.PlayerSettings; 19 | import dev.tr7zw.skinlayers.api.LayerFeatureTransformerAPI; 20 | import dev.tr7zw.skinlayers.api.Mesh; 21 | import dev.tr7zw.skinlayers.api.OffsetProvider; 22 | import lombok.AllArgsConstructor; 23 | import net.minecraft.client.Minecraft; 24 | import net.minecraft.client.model.PlayerModel; 25 | import net.minecraft.client.model.geom.ModelPart; 26 | import net.minecraft.client.player.AbstractClientPlayer; 27 | import net.minecraft.client.renderer.MultiBufferSource; 28 | import net.minecraft.client.renderer.RenderType; 29 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 30 | import net.minecraft.client.renderer.entity.RenderLayerParent; 31 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 32 | import net.minecraft.world.entity.EquipmentSlot; 33 | import net.minecraft.world.entity.player.PlayerModelPart; 34 | import net.minecraft.world.item.Item; 35 | import net.minecraft.world.item.ItemStack; 36 | import net.minecraft.world.item.Items; 37 | 38 | public class CustomLayerFeatureRenderer extends RenderLayer> { 39 | 40 | private static final Minecraft mc = Minecraft.getInstance(); 41 | private final List bodyLayers = new ArrayList<>(); 42 | private final boolean thinArms; 43 | 44 | public CustomLayerFeatureRenderer( 45 | RenderLayerParent> renderLayerParent) { 46 | super(renderLayerParent); 47 | thinArms = ((PlayerEntityModelAccessor) getParentModel()).hasThinArms(); 48 | bodyLayers.add(new Layer(PlayerSettings::getHeadMesh, PlayerModelPart.HAT, OffsetProvider.HEAD, 49 | () -> this.getParentModel().head, (player) -> { 50 | ItemStack itemStack = player.getItemBySlot(EquipmentSlot.HEAD); 51 | if (itemStack != null && SkinLayersModBase.hideHeadLayers.contains(itemStack.getItem())) { 52 | return false; 53 | } 54 | return SkinLayersModBase.config.enableHat; 55 | })); 56 | bodyLayers 57 | .add(new Layer(PlayerSettings::getLeftLegMesh, PlayerModelPart.LEFT_PANTS_LEG, OffsetProvider.LEFT_LEG, 58 | () -> this.getParentModel().leftLeg, (player) -> SkinLayersModBase.config.enableLeftPants)); 59 | bodyLayers.add( 60 | new Layer(PlayerSettings::getRightLegMesh, PlayerModelPart.RIGHT_PANTS_LEG, OffsetProvider.RIGHT_LEG, 61 | () -> this.getParentModel().rightLeg, (player) -> SkinLayersModBase.config.enableRightPants)); 62 | bodyLayers.add(new Layer(PlayerSettings::getLeftArmMesh, PlayerModelPart.LEFT_SLEEVE, 63 | thinArms ? OffsetProvider.LEFT_ARM_SLIM : OffsetProvider.LEFT_ARM, () -> this.getParentModel().leftArm, 64 | (player) -> SkinLayersModBase.config.enableLeftSleeve)); 65 | bodyLayers.add(new Layer(PlayerSettings::getRightArmMesh, PlayerModelPart.RIGHT_SLEEVE, 66 | thinArms ? OffsetProvider.RIGHT_ARM_SLIM : OffsetProvider.RIGHT_ARM, 67 | () -> this.getParentModel().rightArm, (player) -> SkinLayersModBase.config.enableRightSleeve)); 68 | bodyLayers.add(new Layer(PlayerSettings::getTorsoMesh, PlayerModelPart.JACKET, OffsetProvider.BODY, 69 | () -> this.getParentModel().body, (player) -> SkinLayersModBase.config.enableJacket)); 70 | } 71 | 72 | public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, AbstractClientPlayer player, 73 | float f, float g, float h, float j, float k, float l) { 74 | if (SkinLayersModBase.config.compatibilityMode || player.isInvisible()) { 75 | return; 76 | } 77 | if (mc.level == null) { 78 | return; // in a menu or something and the model gets rendered 79 | } 80 | if (player.distanceToSqr( 81 | mc.gameRenderer.getMainCamera().getPosition()) > SkinLayersModBase.config.renderDistanceLOD 82 | * SkinLayersModBase.config.renderDistanceLOD) 83 | return; 84 | 85 | PlayerSettings settings = (PlayerSettings) player; 86 | // check for it being setup first to speedup the rendering 87 | if (!SkinUtil.setup3dLayers(player, settings, thinArms)) { 88 | return; // no head layer setup and wasn't able to setup 89 | } 90 | //?if >= 1.20.2 { 91 | VertexConsumer vertexConsumer = multiBufferSource 92 | .getBuffer(RenderType.entityTranslucent(player.getSkin().texture(), true)); 93 | //? } else { 94 | /^VertexConsumer vertexConsumer = multiBufferSource 95 | .getBuffer(RenderType.entityTranslucent(player.getSkinTextureLocation(), true)); 96 | ^///? } 97 | int m = LivingEntityRenderer.getOverlayCoords(player, 0.0f); 98 | renderLayers(player, settings, poseStack, vertexConsumer, i, m); 99 | } 100 | 101 | public void renderLayers(AbstractClientPlayer abstractClientPlayer, PlayerSettings settings, PoseStack matrixStack, 102 | VertexConsumer vertices, int light, int overlay) { 103 | for (Layer layer : bodyLayers) { 104 | Mesh mesh = layer.meshGetter.apply(settings); 105 | if (mesh != null && abstractClientPlayer.isModelPartShown(layer.modelPart) 106 | && layer.vanillaGetter.get().visible && layer.configGetter.apply(abstractClientPlayer)) { 107 | matrixStack.pushPose(); 108 | LayerFeatureTransformerAPI.getTransformer().transform(abstractClientPlayer, matrixStack, 109 | layer.vanillaGetter.get()); 110 | layer.vanillaGetter.get().translateAndRotate(matrixStack); 111 | layer.offset.applyOffset(matrixStack, mesh); 112 | 113 | mesh.render(layer.vanillaGetter.get(), matrixStack, vertices, light, overlay, 0xFFFFFFFF); 114 | matrixStack.popPose(); 115 | } 116 | } 117 | 118 | } 119 | 120 | @AllArgsConstructor 121 | private static class Layer { 122 | private final Function meshGetter; 123 | private final PlayerModelPart modelPart; 124 | private final OffsetProvider offset; 125 | private final Supplier vanillaGetter; 126 | private final Function configGetter; 127 | } 128 | 129 | } 130 | *///? } 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt text](https://tr7zw.github.io/uikit/banner/header_3d_skinlayers.png) 2 | 3 |

4 | Discord 5 | GitHub 6 | GitHub 7 | GitHub 8 | Ko-fi 9 |

10 | 11 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 12 | 13 | About 14 | 15 | **Replaces the normally flat second layer of player skins with a fully 3D-modeled version.** 16 | The mod automatically switches back to vanilla-style 2D rendering when players are more than 12 blocks away, helping maintain high framerates. It also supports transparent elements - perfect for glasses, visors, or other cosmetic details! 17 | 18 | Since this mod is purely visual and entirely client-side, **it doesn't need to be installed on servers** and works seamlessly in any environment. 19 | 20 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 21 | 22 |
![Features](https://tr7zw.github.io/uikit/headlines/large/Features.png) 23 | 24 | Give your player skin the depth it deserves—with style and performance in mind. 25 | 26 | ### 3D Extruded Skin Layer 27 | 28 | - Replaces the flat second skin layer with a full 3D model 29 | - Adds visual depth to cosmetic details like jackets, sleeves, or hats 30 | - Fully supports transparency for glasses, visors, etc. 31 | - Also applies to player head items 32 | 33 | ### Performance-Aware Rendering 34 | 35 | - Automatically switches to vanilla 2D rendering beyond 12 blocks 36 | - Maintains FPS even in multiplayer or crowded areas 37 | - Fully client-side and requires no server installation 38 | 39 |
40 | 41 | [![Essential](https://tr7zw.github.io/uikit/banner/essential_1.png)](http://essential.gg)

42 | ![Need a 24/7 Server? Check this out!](https://tr7zw.github.io/uikit/banner/shockbyte_divider.png) 43 | [![Shockbyte](https://tr7zw.github.io/uikit/banner/shockbyte_small.png)](http://bit.ly/4bczSJY) 44 | 45 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 46 | 47 |
![Compatibility & Dependancys](https://tr7zw.github.io/uikit/headlines/medium/Compatibility%20&%20Dependancys.png) 48 | 49 | | Minecraft | Loader | Status | Version | Note | 50 | |--------------|------------------------|-----------------|----------|------------------------------------------------------| 51 | | 1.16.5+ | Fabric/Forge*/NeoForge | ✅ Supported | Latest | | 52 | | *1.16.5 | Forge | ⚠️ Unsupported | Outdated | Unsupported, until it gets support for JarInJar mods | 53 | | 1.12.2/1.8.9 | Forge | ❌ Not supported | Outdated | Might get new updates at some point | 54 | 55 | | Mod | Status | Note | 56 | |---------------------------|--------------------|-------------------------------------------------------------------------------------------| 57 | | Essential | ✅ Supported | | 58 | | Shoulder Surfing Reloaded | ✅ Supported | | 59 | | Player Animator | ✅ Supported | This means mods that use it like Better Combat and Emotecraft | 60 | | CustomSkinLoader | ⚠️ Partial support | Requires [this](https://modrinth.com/mod/skinlayers3d-customskinloader-bridge) helper mod | 61 | | Lunar Client | ❌ Not supported | Will never get support! | 62 | 63 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 64 | 65 |
![Screenshots / Media](https://tr7zw.github.io/uikit/headlines/medium/Screenshots%20Media.png) 66 | 67 | ![Stage View](https://raw.githubusercontent.com/tr7zw/3d-Skin-Layers/1.19/img/MC_NU_HEADER.png) 68 | 69 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 70 | 71 |
![Known Issues](https://tr7zw.github.io/uikit/headlines/medium/Known%20Issues.png) 72 | 73 | - Because the outer layer is slightly larger than the player model, some skins may not align perfectly in 3D. 74 | - ZigZag patterns around corners will join up. This down to the math of making it 3d, but it can be somewhat prevented by placing slightly transparent pixels into the holes. 75 | - With shaders, some body part layer sides have weird shadows. I am trying to get this resolved. 76 | 77 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 78 | 79 |
![FAQ](https://tr7zw.github.io/uikit/headlines/medium/FAQ.png) 80 | 81 | ### Does this need to be installed on the server? 82 | 83 | No. This is a fully client-side mod and does **not** need to be installed on the server. 84 | 85 | ### Does this work with HD Skins? 86 | 87 | No. Since the pixels wouldn't have the correct shape, this is not possible. 88 | 89 | ### The layers stick through the modded armor 90 | 91 | Some modded armor is closer to the body than vanilla. Turn down the 3d size in the mod settings till it fits. 92 | 93 | ### The layers are flying offset to the body 94 | 95 | You are probably using a ETF/EMF resourcepack. Make sure you update it, fitting to the Minecraft version. 96 | 97 | ### Help the layers are gone! / The mod doesn't work! 98 | 99 | A few things to check before asking for help: 100 | - Check under Options > Skin Customization that you didn't accidentally disable your skin layers. 101 | - Check in the mod settings, that the features are turned on. 102 | - Does the mod show up in the modlist at all? 103 | - The skin should be loaded via Mojang, other skin loaders might not be supported. 104 | - Make sure your skin actually has things on the second layer. The mod can't magically invent layers. 105 | 106 | ### I'm having issues with my cracked version of Minecraft 107 | 108 | Please buy the game, illegal copies of the game are not supported. 109 | 110 |
![Divider](https://tr7zw.github.io/uikit/divider_faded/Divider_03.png) 111 | 112 |
![Credits & license](https://tr7zw.github.io/uikit/headlines/medium/Credits%20&%20License.png) 113 | 114 | 👤 Thanks to the awesome translators and contributors on Github! 115 | 116 | Link 117 |

118 | 📄 License: tr7zw Protective License
119 | Feel free to use this mod in your Modrinth/Curseforge hosted modpacks without asking for permission. Do not redistribute the jar files anywhere else! 120 | -------------------------------------------------------------------------------- /3dSkinLayers-Versionless/src/main/java/dev/tr7zw/skinlayers/versionless/render/CustomizableCube.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.versionless.render; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import dev.tr7zw.skinlayers.versionless.util.Direction; 7 | import dev.tr7zw.skinlayers.versionless.util.Vector3; 8 | 9 | public class CustomizableCube { 10 | 11 | private final Direction[] hidden; 12 | protected final Polygon[] polygons; 13 | protected int polygonCount = 0; 14 | public final float minX; 15 | public final float minY; 16 | public final float minZ; 17 | public final float maxX; 18 | public final float maxY; 19 | public final float maxZ; 20 | 21 | public CustomizableCube(int u, int v, float x, float y, float z, float sizeX, float sizeY, float sizeZ, 22 | float extraX, float extraY, float extraZ, boolean mirror, float textureWidth, float textureHeight, 23 | Direction[] hide, Direction[][] hideCorners) { 24 | this.hidden = hide; 25 | this.minX = x; 26 | this.minY = y; 27 | this.minZ = z; 28 | this.maxX = x + sizeX; 29 | this.maxY = y + sizeY; 30 | this.maxZ = z + sizeZ; 31 | this.polygons = new Polygon[6]; 32 | float pX = x + sizeX; 33 | float pY = y + sizeY; 34 | float pZ = z + sizeZ; 35 | x -= extraX; 36 | y -= extraY; 37 | z -= extraZ; 38 | pX += extraX; 39 | pY += extraY; 40 | pZ += extraZ; 41 | if (mirror) { 42 | float i = pX; 43 | pX = x; 44 | x = i; 45 | } 46 | Vertex vertexNNN = new Vertex(x, y, z, 0.0F, 0.0F); 47 | Vertex vertexPNN = new Vertex(pX, y, z, 0.0F, 8.0F); 48 | Vertex vertexPPN = new Vertex(pX, pY, z, 8.0F, 8.0F); 49 | Vertex vertexNPN = new Vertex(x, pY, z, 8.0F, 0.0F); 50 | Vertex vertexNNP = new Vertex(x, y, pZ, 0.0F, 0.0F); 51 | Vertex vertexPNP = new Vertex(pX, y, pZ, 0.0F, 8.0F); 52 | Vertex vertexPPP = new Vertex(pX, pY, pZ, 8.0F, 8.0F); 53 | Vertex vertexNPP = new Vertex(x, pY, pZ, 8.0F, 0.0F); 54 | 55 | float minU = u; 56 | float maxU = u + 1.0F; 57 | 58 | float minV = v; 59 | float maxV = v + 1.0F; 60 | 61 | Map axisToCorner = new HashMap<>(); 62 | nextCorner: for (Direction[] corner : hideCorners) { 63 | nextAxis: for (Direction.Axis axis : Direction.Axis.VALUES) { 64 | for (Direction dir : corner) { 65 | if (dir.getAxis() == axis) 66 | continue nextAxis; 67 | } 68 | 69 | axisToCorner.put(axis, corner); 70 | continue nextCorner; 71 | } 72 | } 73 | 74 | if (visibleFace(Direction.DOWN)) 75 | this.polygons[this.polygonCount++] = new Polygon( 76 | removeCornerVertex(new Vertex[] { vertexPNP, vertexNNP, vertexNNN, vertexPNN }, 77 | axisToCorner.get(Direction.Axis.Y)), 78 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.DOWN); 79 | if (visibleFace(Direction.UP)) 80 | this.polygons[this.polygonCount++] = new Polygon( 81 | removeCornerVertex(new Vertex[] { vertexPPN, vertexNPN, vertexNPP, vertexPPP }, 82 | axisToCorner.get(Direction.Axis.Y)), 83 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.UP); 84 | if (visibleFace(Direction.NORTH)) 85 | this.polygons[this.polygonCount++] = new Polygon( 86 | removeCornerVertex(new Vertex[] { vertexPNN, vertexNNN, vertexNPN, vertexPPN }, 87 | axisToCorner.get(Direction.Axis.Z)), 88 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.NORTH); 89 | if (visibleFace(Direction.SOUTH)) 90 | this.polygons[this.polygonCount++] = new Polygon( 91 | removeCornerVertex(new Vertex[] { vertexNNP, vertexPNP, vertexPPP, vertexNPP }, 92 | axisToCorner.get(Direction.Axis.Z)), 93 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.SOUTH); 94 | if (visibleFace(Direction.WEST)) 95 | this.polygons[this.polygonCount++] = new Polygon( 96 | removeCornerVertex(new Vertex[] { vertexNNN, vertexNNP, vertexNPP, vertexNPN }, 97 | axisToCorner.get(Direction.Axis.X)), 98 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.WEST); 99 | if (visibleFace(Direction.EAST)) 100 | this.polygons[this.polygonCount++] = new Polygon( 101 | removeCornerVertex(new Vertex[] { vertexPNP, vertexPNN, vertexPPN, vertexPPP }, 102 | axisToCorner.get(Direction.Axis.X)), 103 | minU, minV, maxU, maxV, textureWidth, textureHeight, mirror, Direction.EAST); 104 | } 105 | 106 | private boolean visibleFace(Direction face) { 107 | for (Direction dir : hidden) { 108 | if (dir == face) 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | private static Vertex[] removeCornerVertex(Vertex[] vertices, Direction[] corner) { 115 | if (corner == null) { 116 | return vertices; 117 | } 118 | 119 | Vertex except = vertices[0]; 120 | for (int i = 1; i < 4; i++) { 121 | except = compareVertices(except, vertices[i], corner); 122 | } 123 | 124 | int index = 0; 125 | for (int i = 0; i < 4; i++) { 126 | if (vertices[i] == except) 127 | continue; 128 | vertices[index++] = vertices[i]; 129 | } 130 | vertices[3] = vertices[2]; 131 | 132 | return vertices; 133 | } 134 | 135 | private static Vertex compareVertices(Vertex vertex1, Vertex vertex2, Direction[] corner) { 136 | for (Direction dir : corner) { 137 | double d = dir.getAxis().choose(vertex1.pos.x - vertex2.pos.x, vertex1.pos.y - vertex2.pos.y, 138 | vertex1.pos.z - vertex2.pos.z) * dir.getDirStep(); 139 | if (d > 0) { 140 | return vertex1; 141 | } else if (d < 0) { 142 | return vertex2; 143 | } 144 | } 145 | return vertex1; 146 | } 147 | 148 | protected static class Polygon { 149 | public final Vertex[] vertices; 150 | 151 | public final Vector3 normal; 152 | 153 | public Polygon(Vertex[] vertexs, float minU, float minV, float maxU, float maxV, float textureWidth, 154 | float textureHeight, boolean mirror, Direction direction) { 155 | this.vertices = vertexs; 156 | float zeroWidth = 0.0F / textureWidth; 157 | float zeroHeight = 0.0F / textureHeight; 158 | vertexs[0] = vertexs[0].remap(maxU / textureWidth - zeroWidth, minV / textureHeight + zeroHeight); 159 | vertexs[1] = vertexs[1].remap(minU / textureWidth + zeroWidth, minV / textureHeight + zeroHeight); 160 | vertexs[2] = vertexs[2].remap(minU / textureWidth + zeroWidth, maxV / textureHeight - zeroHeight); 161 | vertexs[3] = vertexs[3].remap(maxU / textureWidth - zeroWidth, maxV / textureHeight - zeroHeight); 162 | if (mirror) { 163 | int vertexCount = vertexs.length; 164 | for (int i = 0; i < vertexCount / 2; i++) { 165 | Vertex vertex = vertexs[i]; 166 | vertexs[i] = vertexs[vertexCount - 1 - i]; 167 | vertexs[vertexCount - 1 - i] = vertex; 168 | } 169 | } 170 | this.normal = new Vector3(direction.getStepX(), direction.getStepY(), direction.getStepZ()); 171 | if (mirror) 172 | this.normal.mul(-1.0F, 1.0F, 1.0F); 173 | } 174 | } 175 | 176 | protected static class Vertex { 177 | public final Vector3 pos; 178 | public final float u; 179 | public final float v; 180 | public final float scaledX, scaledY, scaledZ; 181 | 182 | public Vertex(float x, float y, float z, float u, float v) { 183 | this(new Vector3(x, y, z), u, v); 184 | } 185 | 186 | public Vertex remap(float u, float v) { 187 | return new Vertex(this.pos, u, v); 188 | } 189 | 190 | public Vertex(Vector3 vector3f, float u, float v) { 191 | this.pos = vector3f; 192 | this.u = u; 193 | this.v = v; 194 | this.scaledX = pos.x / 16.0F; 195 | this.scaledY = pos.y / 16.0F; 196 | this.scaledZ = pos.z / 16.0F; 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/config/ConfigScreenProvider.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dev.tr7zw.skinlayers.SkinLayersModBase; 7 | import dev.tr7zw.skinlayers.SkullRendererCache; 8 | import dev.tr7zw.skinlayers.versionless.ModBase; 9 | import dev.tr7zw.skinlayers.versionless.config.Config; 10 | import dev.tr7zw.transition.mc.ComponentProvider; 11 | import dev.tr7zw.trender.gui.client.AbstractConfigScreen; 12 | import dev.tr7zw.trender.gui.client.BackgroundPainter; 13 | import dev.tr7zw.trender.gui.widget.WButton; 14 | import dev.tr7zw.trender.gui.widget.WGridPanel; 15 | import dev.tr7zw.trender.gui.widget.WPlayerPreview; 16 | import dev.tr7zw.trender.gui.widget.WTabPanel; 17 | import dev.tr7zw.trender.gui.widget.data.Insets; 18 | import dev.tr7zw.trender.gui.widget.icon.ItemIcon; 19 | import net.minecraft.client.Minecraft; 20 | import net.minecraft.client.gui.screens.Screen; 21 | import net.minecraft.network.chat.CommonComponents; 22 | import net.minecraft.world.item.Items; 23 | 24 | public class ConfigScreenProvider { 25 | 26 | public static Screen createConfigScreen(Screen parent) { 27 | return new CustomConfigScreen(parent).createScreen(); 28 | } 29 | 30 | private static class CustomConfigScreen extends AbstractConfigScreen { 31 | 32 | public CustomConfigScreen(Screen previous) { 33 | super(ComponentProvider.translatable("text.skinlayers.title"), previous); 34 | 35 | WGridPanel root = new WGridPanel(8); 36 | root.setInsets(Insets.ROOT_PANEL); 37 | setRootPanel(root); 38 | 39 | WTabPanel wTabPanel = new WTabPanel(); 40 | 41 | WGridPanel playerSettings = new WGridPanel(); 42 | playerSettings.setInsets(new Insets(2, 4)); 43 | 44 | // options page 45 | List options = new ArrayList<>(); 46 | options.add(getOnOffOption("text.skinlayers.enable.hat", () -> SkinLayersModBase.config.enableHat, 47 | (b) -> SkinLayersModBase.config.enableHat = b)); 48 | options.add(getOnOffOption("text.skinlayers.enable.jacket", () -> SkinLayersModBase.config.enableJacket, 49 | (b) -> SkinLayersModBase.config.enableJacket = b)); 50 | options.add( 51 | getOnOffOption("text.skinlayers.enable.leftsleeve", () -> SkinLayersModBase.config.enableLeftSleeve, 52 | (b) -> SkinLayersModBase.config.enableLeftSleeve = b)); 53 | options.add(getOnOffOption("text.skinlayers.enable.rightsleeve", 54 | () -> SkinLayersModBase.config.enableRightSleeve, 55 | (b) -> SkinLayersModBase.config.enableRightSleeve = b)); 56 | options.add( 57 | getOnOffOption("text.skinlayers.enable.leftpants", () -> SkinLayersModBase.config.enableLeftPants, 58 | (b) -> SkinLayersModBase.config.enableLeftPants = b)); 59 | options.add( 60 | getOnOffOption("text.skinlayers.enable.rightpants", () -> SkinLayersModBase.config.enableRightPants, 61 | (b) -> SkinLayersModBase.config.enableRightPants = b)); 62 | options.add(getIntOption("text.skinlayers.renderdistancelod", 5, 40, 63 | () -> SkinLayersModBase.config.renderDistanceLOD, 64 | (i) -> SkinLayersModBase.config.renderDistanceLOD = i)); 65 | options.add(getDoubleOption("text.skinlayers.basevoxelsize", 1.001f, 1.4f, 0.001f, 66 | () -> SkinLayersModBase.config.baseVoxelSize, (i) -> { 67 | SkinLayersModBase.config.baseVoxelSize = (float) i; 68 | SkinLayersModBase.instance.refreshLayers(Minecraft.getInstance().player); 69 | })); 70 | options.add(getDoubleOption("text.skinlayers.headvoxelsize", 1.001f, 1.25f, 0.001f, 71 | () -> (double) SkinLayersModBase.config.headVoxelSize, (i) -> { 72 | SkinLayersModBase.config.headVoxelSize = (float) i; 73 | SkinLayersModBase.instance.refreshLayers(Minecraft.getInstance().player); 74 | })); 75 | options.add(getDoubleOption("text.skinlayers.bodyvoxelwidthsize", 1.001f, 1.4f, 0.001f, 76 | () -> (double) SkinLayersModBase.config.bodyVoxelWidthSize, (i) -> { 77 | SkinLayersModBase.config.bodyVoxelWidthSize = (float) i; 78 | SkinLayersModBase.instance.refreshLayers(Minecraft.getInstance().player); 79 | })); 80 | 81 | var optionList = createOptionList(options); 82 | optionList.setGap(-1); 83 | // optionList.setSize(14 * 20, 9 * 20); 84 | 85 | playerSettings.add(optionList, 0, 0, 12, 9); 86 | 87 | var playerPreview = new WPlayerPreview(); 88 | playerPreview.setShowBackground(true); 89 | playerSettings.add(playerPreview, 13, 2); 90 | 91 | wTabPanel.add(playerSettings, b -> b.title(ComponentProvider.translatable("text.skinlayers.tab.player")) 92 | .icon(new ItemIcon(Items.VILLAGER_SPAWN_EGG))); 93 | 94 | // Player Heads 95 | options = new ArrayList<>(); 96 | options.add(getOnOffOption("text.skinlayers.skulls.enable", () -> SkinLayersModBase.config.enableSkulls, 97 | (b) -> SkinLayersModBase.config.enableSkulls = b)); 98 | options.add(getOnOffOption("text.skinlayers.skullsitems.enable", 99 | () -> SkinLayersModBase.config.enableSkullsItems, 100 | (b) -> SkinLayersModBase.config.enableSkullsItems = b)); 101 | options.add(getDoubleOption("text.skinlayers.skulls.voxelsize", 1.001f, 1.2f, 0.001f, 102 | () -> (double) SkinLayersModBase.config.skullVoxelSize, (i) -> { 103 | SkinLayersModBase.config.skullVoxelSize = (float) i; 104 | })); 105 | optionList = createOptionList(options); 106 | optionList.setGap(-1); 107 | 108 | wTabPanel.add(optionList, b -> b.title(ComponentProvider.translatable("text.skinlayers.tab.heads")) 109 | .icon(new ItemIcon(Items.PLAYER_HEAD))); 110 | 111 | // Other Settings 112 | options = new ArrayList<>(); 113 | options.add(getOnOffOption("text.skinlayers.fastrender.enable", () -> SkinLayersModBase.config.fastRender, 114 | (b) -> SkinLayersModBase.config.fastRender = b)); 115 | //? if < 1.21.2 { 116 | /* 117 | options.add(getOnOffOption("text.skinlayers.compatibilityMode.enable", 118 | () -> SkinLayersModBase.config.compatibilityMode, 119 | (b) -> SkinLayersModBase.config.compatibilityMode = b)); 120 | *///? } 121 | //? if >= 1.20.0 { 122 | 123 | options.add(getOnOffOption("text.skinlayers.irisCompatibilityMode.enable", 124 | () -> SkinLayersModBase.config.irisCompatibilityMode, 125 | (b) -> SkinLayersModBase.config.irisCompatibilityMode = b)); 126 | options.add(getOnOffOption("text.skinlayers.applySodiumWorkaround.enable", 127 | () -> SkinLayersModBase.config.applySodiumWorkaround, 128 | (b) -> SkinLayersModBase.config.applySodiumWorkaround = b)); 129 | //? } 130 | options.add(getDoubleOption("text.skinlayers.firstperson.voxelsize", 1.02f, 1.3f, 0.001f, 131 | () -> (double) SkinLayersModBase.config.firstPersonPixelScaling, (i) -> { 132 | SkinLayersModBase.config.firstPersonPixelScaling = (float) i; 133 | })); 134 | optionList = createOptionList(options); 135 | optionList.setGap(-1); 136 | 137 | wTabPanel.add(optionList, b -> b.title(ComponentProvider.translatable("text.skinlayers.tab.other")) 138 | .icon(new ItemIcon(Items.COMMAND_BLOCK))); 139 | 140 | WButton doneButton = new WButton(CommonComponents.GUI_DONE); 141 | doneButton.setOnClick(() -> { 142 | save(); 143 | Minecraft.getInstance().setScreen(previous); 144 | }); 145 | root.add(doneButton, 0, 26, 6, 2); 146 | 147 | wTabPanel.layout(); 148 | root.add(wTabPanel, 0, 1); 149 | 150 | WButton resetButton = new WButton(ComponentProvider.translatable("controls.reset")); 151 | resetButton.setOnClick(() -> { 152 | reset(); 153 | root.layout(); 154 | }); 155 | root.add(resetButton, 23, 26, 6, 2); 156 | 157 | root.setBackgroundPainter(BackgroundPainter.VANILLA); 158 | 159 | root.validate(this); 160 | root.setHost(this); 161 | } 162 | 163 | @Override 164 | public void reset() { 165 | ModBase.config = new Config(); 166 | } 167 | 168 | @Override 169 | public void save() { 170 | SkinLayersModBase.instance.writeConfig(); 171 | SkinLayersModBase.instance.refreshLayers(Minecraft.getInstance().player); 172 | SkullRendererCache.clearCache(); 173 | } 174 | 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/skinlayers/SkinUtil.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.skinlayers; 2 | 3 | import java.util.Optional; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import com.google.common.cache.Cache; 7 | import com.google.common.cache.CacheBuilder; 8 | import com.google.common.cache.RemovalListener; 9 | import com.google.common.cache.RemovalNotification; 10 | import com.mojang.authlib.GameProfile; 11 | import com.mojang.blaze3d.platform.NativeImage; 12 | 13 | import dev.tr7zw.skinlayers.accessor.HttpTextureAccessor; 14 | import dev.tr7zw.skinlayers.accessor.NativeImageAccessor; 15 | import dev.tr7zw.skinlayers.accessor.PlayerSettings; 16 | import dev.tr7zw.skinlayers.accessor.SkullSettings; 17 | import dev.tr7zw.skinlayers.api.SkinLayersAPI; 18 | import dev.tr7zw.transition.mc.PlayerUtil; 19 | import net.minecraft.client.Minecraft; 20 | import net.minecraft.client.renderer.texture.AbstractTexture; 21 | import net.minecraft.client.renderer.texture.DynamicTexture; 22 | import net.minecraft.resources.*; 23 | import net.minecraft.server.packs.resources.Resource; 24 | 25 | public class SkinUtil { 26 | 27 | private static Cache cache = CacheBuilder.newBuilder() 28 | .expireAfterAccess(60L, TimeUnit.SECONDS) 29 | .removalListener(new RemovalListener() { 30 | 31 | @Override 32 | public void onRemoval(RemovalNotification notification) { 33 | try { 34 | notification.getValue().close(); 35 | } catch (Exception ex) { 36 | SkinLayersModBase.LOGGER.error("Error while closing a texture.", ex); 37 | } 38 | } 39 | }).build(); 40 | 41 | public static NativeImage getTexture( 42 | /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ resourceLocation, 43 | SkullSettings settings) { 44 | if (resourceLocation == null) { 45 | return null; 46 | } 47 | try { 48 | //? if >= 1.19.0 { 49 | 50 | Optional optionalRes = Minecraft.getInstance().getResourceManager().getResource(resourceLocation); 51 | if (optionalRes.isPresent()) { 52 | Resource resource = optionalRes.get(); 53 | NativeImage skin = NativeImage.read(resource.open()); 54 | //? } else { 55 | 56 | // if(Minecraft.getInstance().getResourceManager().hasResource(resourceLocation)) { 57 | // Resource resource = Minecraft.getInstance().getResourceManager().getResource(resourceLocation); 58 | // NativeImage skin = NativeImage.read(resource.getInputStream()); 59 | //? } 60 | return skin; 61 | } 62 | AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(resourceLocation); 63 | if (texture == null) { 64 | return null; 65 | } 66 | NativeImage cachedImage = cache.getIfPresent(texture); 67 | if (cachedImage != null && (Object) cachedImage instanceof NativeImageAccessor ac 68 | && ac.skinlayers$isAllocated()) { 69 | return cachedImage; 70 | } else { 71 | // got invalidated, remove from cache 72 | cache.invalidate(texture); 73 | } 74 | if (texture instanceof HttpTextureAccessor) { 75 | HttpTextureAccessor httpTexture = (HttpTextureAccessor) texture; 76 | try { 77 | NativeImage img = httpTexture.getImage(); 78 | if (img != null && (Object) img instanceof NativeImageAccessor ac && ac.skinlayers$isAllocated()) { 79 | cache.put(texture, img); 80 | return img; 81 | } 82 | } catch (Exception ex) { 83 | // not there 84 | } 85 | return null; // not yet initialized, but also not ready 86 | } 87 | if (texture instanceof DynamicTexture) { 88 | try { 89 | NativeImage img = ((DynamicTexture) texture).getPixels(); 90 | if (img != null && (Object) img instanceof NativeImageAccessor ac && ac.skinlayers$isAllocated()) { 91 | return img; 92 | } 93 | } catch (Exception ex) { 94 | // not backed by an image 95 | } 96 | return null; // not yet initialized, but also not ready 97 | } 98 | // This would work, but hd skins will crash the JVM. Only 99 | /* 100 | * try { NativeImage img = new NativeImage(Format.RGBA, 64, 64, true); 101 | * GlStateManager._bindTexture(texture.getId()); img.downloadTexture(0, false); 102 | * cache.put(texture, img); return img; }catch(Exception ex) { 103 | * SkinLayersModBase.LOGGER. 104 | * error("Error while trying to grab a texture from the GPU.", ex); } 105 | */ 106 | if (settings != null) { 107 | settings.setInitialized(false); // initialize as invalid 108 | } 109 | SkinLayersModBase.LOGGER.warn("Unable to handle skin " + resourceLocation 110 | + ". Potentially a conflict with another mod. (" + texture.getClass().getName() + ")"); 111 | return null; 112 | } catch (Exception ex) { 113 | SkinLayersModBase.LOGGER.error("Error while resolving a skin texture.", ex); 114 | return null; 115 | } 116 | } 117 | 118 | public static boolean setup3dLayers( 119 | //? if >= 1.21.9 { 120 | 121 | net.minecraft.world.entity.Avatar abstractClientPlayerEntity, 122 | //? } else { 123 | /* 124 | net.minecraft.client.player.AbstractClientPlayer abstractClientPlayerEntity, 125 | *///? } 126 | PlayerSettings settings, boolean thinArms) { 127 | var skinLocation = PlayerUtil.getPlayerSkin(abstractClientPlayerEntity); 128 | if (skinLocation == null) { 129 | return false;// this *should* never happen, but just to be sure 130 | } 131 | if (skinLocation.equals(settings.getCurrentSkin()) && thinArms == settings.hasThinArms()) { // if they are 132 | // equal, the skin 133 | // is processed and 134 | // either failed or 135 | // is ready 136 | return settings.getHeadMesh() != null; 137 | } 138 | // Starting here should only run in case the skin has changed by getting 139 | // loaded/another mod changed the skin 140 | NativeImage skin = getTexture(skinLocation, null); 141 | if (skin == null || skin.getWidth() != 64 || skin.getHeight() != 64) { // Skin is null or not a 64x64 skin, hd 142 | // skins won't work 143 | settings.setCurrentSkin(skinLocation); 144 | settings.setThinArms(thinArms); 145 | settings.clearMeshes(); 146 | return false; 147 | } 148 | settings.setLeftLegMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 4, 12, 4, 0, 48, true, 0f)); 149 | settings.setRightLegMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 4, 12, 4, 0, 32, true, 0f)); 150 | if (thinArms) { 151 | settings.setLeftArmMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 3, 12, 4, 48, 48, true, -2f)); 152 | settings.setRightArmMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 3, 12, 4, 40, 32, true, -2f)); 153 | } else { 154 | settings.setLeftArmMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 4, 12, 4, 48, 48, true, -2)); 155 | settings.setRightArmMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 4, 12, 4, 40, 32, true, -2)); 156 | } 157 | settings.setTorsoMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 8, 12, 4, 16, 32, true, 0)); 158 | settings.setHeadMesh(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 8, 8, 8, 32, 0, false, 0.6f)); 159 | settings.setCurrentSkin(skinLocation); 160 | settings.setThinArms(thinArms); 161 | return true; 162 | } 163 | 164 | public static boolean setup3dLayers(GameProfile gameprofile, SkullSettings settings) { 165 | if (gameprofile == null) { 166 | return false; // no gameprofile 167 | } 168 | var playerSkin = PlayerUtil.getPlayerSkin(gameprofile); 169 | if (playerSkin == null) { 170 | return false; // no skin 171 | } 172 | NativeImage skin = SkinUtil.getTexture(playerSkin, settings); 173 | if (skin == null || skin.getWidth() != 64 || skin.getHeight() != 64) { 174 | return false; 175 | } 176 | settings.setupHeadLayers(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 8, 8, 8, 32, 0, false, 0.6f)); 177 | settings.setInitialized(true); 178 | return true; 179 | } 180 | 181 | public static boolean setup3dLayers( 182 | /*? >= 1.21.11 {*/ Identifier /*?} else {*/ /*ResourceLocation *//*?}*/ playerSkin, 183 | SkullSettings settings) { 184 | if (playerSkin == null) { 185 | return false; // no skin 186 | } 187 | NativeImage skin = SkinUtil.getTexture(playerSkin, settings); 188 | if (skin == null || skin.getWidth() != 64 || skin.getHeight() != 64) { 189 | return false; 190 | } 191 | settings.setupHeadLayers(SkinLayersAPI.getMeshHelper().create3DMesh(skin, 8, 8, 8, 32, 0, false, 0.6f)); 192 | settings.setInitialized(true); 193 | return true; 194 | } 195 | 196 | } 197 | --------------------------------------------------------------------------------