From 9aeb69347841890011c9435a4f64f077aa436a82 Mon Sep 17 00:00:00 2001 From: kirillsaint Date: Mon, 6 May 2024 14:07:48 +0600 Subject: [PATCH] (feature) 3d skins mod --- .../client/gui/lite/clickgui/ModSettings.java | 120 +++---- .../skins/PlayerEntityModelAccessor.java | 14 + .../mixin/accessors/skins/PlayerSettings.java | 15 + .../accessors/skins/SkullModelAccessor.java | 7 + .../mixin/accessors/skins/SkullSettings.java | 11 + .../mixin/mixins/skins/PlayerMixin.java | 47 +++ .../mixins/skins/PlayerRendererMixin.java | 177 +++++++++++ .../skins/RendererLivingEntityMixin.java | 52 ++++ .../client/mods/ModInstances.java | 2 + .../client/mods/render/SkinsMod.java | 10 - .../client/mods/render/skins/Direction.java | 33 ++ .../client/mods/render/skins/SkinUtil.java | 79 +++++ .../client/mods/render/skins/SkinsMod.java | 34 ++ .../mods/render/skins/SkullRendererCache.java | 31 ++ .../render/skins/opengl/GlStateManager.java | 15 + .../mods/render/skins/opengl/NativeImage.java | 294 ++++++++++++++++++ .../render/skins/render/CustomizableCube.java | 152 +++++++++ .../render/CustomizableCubeListBuilder.java | 41 +++ .../skins/render/CustomizableModelPart.java | 57 ++++ .../skins/render/SolidPixelWrapper.java | 108 +++++++ .../BodyLayerFeatureRenderer.java | 146 +++++++++ .../HeadLayerFeatureRenderer.java | 90 ++++++ src/main/resources/mixins.SilentClient.json | 5 +- 23 files changed, 1470 insertions(+), 70 deletions(-) create mode 100644 src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerEntityModelAccessor.java create mode 100644 src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerSettings.java create mode 100644 src/main/java/net/silentclient/client/mixin/accessors/skins/SkullModelAccessor.java create mode 100644 src/main/java/net/silentclient/client/mixin/accessors/skins/SkullSettings.java create mode 100644 src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerMixin.java create mode 100644 src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerRendererMixin.java create mode 100644 src/main/java/net/silentclient/client/mixin/mixins/skins/RendererLivingEntityMixin.java delete mode 100644 src/main/java/net/silentclient/client/mods/render/SkinsMod.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/Direction.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/SkinUtil.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/SkinsMod.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/SkullRendererCache.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/opengl/GlStateManager.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/opengl/NativeImage.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCube.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCubeListBuilder.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableModelPart.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/render/SolidPixelWrapper.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/renderlayers/BodyLayerFeatureRenderer.java create mode 100644 src/main/java/net/silentclient/client/mods/render/skins/renderlayers/HeadLayerFeatureRenderer.java diff --git a/src/main/java/net/silentclient/client/gui/lite/clickgui/ModSettings.java b/src/main/java/net/silentclient/client/gui/lite/clickgui/ModSettings.java index 3533066..413f75e 100644 --- a/src/main/java/net/silentclient/client/gui/lite/clickgui/ModSettings.java +++ b/src/main/java/net/silentclient/client/gui/lite/clickgui/ModSettings.java @@ -11,12 +11,11 @@ import net.silentclient.client.gui.SilentScreen; import net.silentclient.client.gui.animation.SimpleAnimation; import net.silentclient.client.gui.animation.normal.Direction; import net.silentclient.client.gui.elements.Button; -import net.silentclient.client.gui.lite.clickgui.utils.GlUtils; -import net.silentclient.client.gui.lite.clickgui.utils.MouseUtils; -import net.silentclient.client.gui.lite.clickgui.utils.MouseUtils.Scroll; import net.silentclient.client.gui.elements.*; import net.silentclient.client.gui.font.SilentFontRenderer; import net.silentclient.client.gui.hud.HUDConfigScreen; +import net.silentclient.client.gui.lite.clickgui.utils.GlUtils; +import net.silentclient.client.gui.lite.clickgui.utils.MouseUtils; import net.silentclient.client.gui.modmenu.CellGrid; import net.silentclient.client.gui.theme.Theme; import net.silentclient.client.gui.theme.input.DefaultInputTheme; @@ -27,10 +26,7 @@ import net.silentclient.client.mods.ModCategory; import net.silentclient.client.mods.Setting; import net.silentclient.client.mods.render.crosshair.CrosshairMod; import net.silentclient.client.mods.world.TimeChangerMod; -import net.silentclient.client.utils.ColorUtils; -import net.silentclient.client.utils.MenuBlurUtils; -import net.silentclient.client.utils.MouseCursorHandler; -import net.silentclient.client.utils.Sounds; +import net.silentclient.client.utils.*; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; @@ -45,6 +41,7 @@ public class ModSettings extends SilentScreen { public double scrollY; public static SimpleAnimation scrollAnimation = new SimpleAnimation(0.0F); + private ScrollHelper scrollHelper = new ScrollHelper(); public ModSettings(Mod mod, GuiScreen parent) { if (mod == null) throw new IllegalArgumentException("Mod is null"); @@ -130,8 +127,13 @@ public class ModSettings extends SilentScreen { GL11.glPushMatrix(); GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - - int settingY = (int) (y + 25 + scrollAnimation.getValue() + mod.customComponentLiteHeight()); + scrollHelper.setStep(5); + scrollHelper.setElementsHeight(25 + mod.customComponentHeight() + (Client.getInstance().getSettingsManager().getSettingByMod(mod).size() * 25)); + scrollHelper.setMaxScroll(height); + scrollHelper.setSpeed(200); + scrollHelper.setFlag(true); + float scrollY = scrollHelper.getScroll(); + int settingY = (int) (y + 25 + scrollY + mod.customComponentLiteHeight()); GL11.glPopMatrix(); @@ -145,8 +147,8 @@ public class ModSettings extends SilentScreen { int translatedY = r.getScaledHeight() - y - height; GL11.glScissor(x * s, translatedY * s, this.width * s, height * s); - Client.getInstance().getSilentFontRenderer().drawString(mod.getName(), x + 100, (int) (y + 5) + scrollAnimation.getValue(), 14, SilentFontRenderer.FontType.TITLE); - MouseCursorHandler.CursorType cursorTypeCustom = mod.renderCustomLiteComponent(x + 100, (int) (y + 25 + scrollAnimation.getValue()), width, height, mouseX, mouseY); + Client.getInstance().getSilentFontRenderer().drawString(mod.getName(), x + 100, (y + 5) + scrollY, 14, SilentFontRenderer.FontType.TITLE); + MouseCursorHandler.CursorType cursorTypeCustom = mod.renderCustomLiteComponent(x + 100, (int) (y + 25 + scrollY), width, height, mouseX, mouseY); if(cursorTypeCustom != null) { cursorType = cursorTypeCustom; } @@ -230,13 +232,13 @@ public class ModSettings extends SilentScreen { settingY += settingHeight; } if(mod.getCategory() == ModCategory.MODS) { - RenderUtil.drawImage(new ResourceLocation("silentclient/icons/reset_settings.png"), x + width - (10 + 8) - 15, y + 5 + scrollAnimation.getValue(), 10, 10); - Tooltip.render(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollAnimation.getValue(), 10, 10, "Reset"); - if(MouseUtils.isInside(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollAnimation.getValue(), 10, 10)) { + RenderUtil.drawImage(new ResourceLocation("silentclient/icons/reset_settings.png"), x + width - (10 + 8) - 15, y + 5 + scrollY, 10, 10); + Tooltip.render(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollY, 10, 10, "Reset"); + if(MouseUtils.isInside(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollY, 10, 10)) { cursorType = MouseCursorHandler.CursorType.POINTER; } - Switch.render(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollAnimation.getValue(), mod.simpleAnimation, mod.isEnabled(), mod.isForceDisabled(), mod.isForceDisabled() ? "Force disabled" : null); - if(Switch.isHovered(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollAnimation.getValue())) { + Switch.render(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollY, mod.simpleAnimation, mod.isEnabled(), mod.isForceDisabled(), mod.isForceDisabled() ? "Force disabled" : null); + if(Switch.isHovered(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollY)) { cursorType = MouseCursorHandler.CursorType.POINTER; } } @@ -245,45 +247,45 @@ public class ModSettings extends SilentScreen { super.drawScreen(mouseX, mouseY, partialTicks); - final Scroll scroll = MouseUtils.scroll(); - - if(scroll != null) { - switch (scroll) { - case DOWN: - if(scrollY > -((settingIndex - 13.5) * 38)) { - scrollY -=12; - } - - if(settingIndex > 13) { - if(scrollY < -((settingIndex - 15) * 38)) { - scrollY = -((settingIndex - 14.1) * 38); - } - } - if(mod.customComponentLiteHeight() > height - 30) { - if(scrollY > -((mod.customComponentLiteHeight() - 13.5) * 38)) { - scrollY -=12; - } - if(scrollY < -((mod.customComponentLiteHeight() - 15) * 38)) { - scrollY = -((mod.customComponentLiteHeight() - 14.1) * 38); - } - } - break; - case UP: - if(scrollY < -10) { - scrollY +=12; - }else { - if(settingIndex > 13) { - scrollY = 0; - } - if(mod.customComponentLiteHeight() > height - 30) { - scrollY = 0; - } - } - break; - } - } - - scrollAnimation.setAnimation((float) scrollY, 16); +// final Scroll scroll = MouseUtils.scroll(); +// +// if(scroll != null) { +// switch (scroll) { +// case DOWN: +// if(scrollY > -((settingIndex - 13.5) * 38)) { +// scrollY -=12; +// } +// +// if(settingIndex > 13) { +// if(scrollY < -((settingIndex - 15) * 38)) { +// scrollY = -((settingIndex - 14.1) * 38); +// } +// } +// if(mod.customComponentLiteHeight() > height - 30) { +// if(scrollY > -((mod.customComponentLiteHeight() - 13.5) * 38)) { +// scrollY -=12; +// } +// if(scrollY < -((mod.customComponentLiteHeight() - 15) * 38)) { +// scrollY = -((mod.customComponentLiteHeight() - 14.1) * 38); +// } +// } +// break; +// case UP: +// if(scrollY < -10) { +// scrollY +=12; +// }else { +// if(settingIndex > 13) { +// scrollY = 0; +// } +// if(mod.customComponentLiteHeight() > height - 30) { +// scrollY = 0; +// } +// } +// break; +// } +// } +// +// scrollAnimation.setAnimation((float) scrollY, 16); if(ClickGUI.close) { ClickGUI.introAnimation.setDirection(Direction.BACKWARDS); @@ -302,22 +304,22 @@ public class ModSettings extends SilentScreen { @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { super.mouseClicked(mouseX, mouseY, mouseButton); - + float scrollY = scrollHelper.getScroll(); int addX = 190; int addY = 110; int x = (width / 2) - addX; int y = (height / 2) - addY; int width = addX * 2; - int settingY = (int) (y + 25 + scrollAnimation.getValue() + mod.customComponentLiteHeight()); + int settingY = (int) (y + 25 + scrollY + mod.customComponentLiteHeight()); String category = ""; - if(mod.getCategory() == ModCategory.MODS && MouseUtils.isInside(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollAnimation.getValue(), 10, 10)) { + if(mod.getCategory() == ModCategory.MODS && MouseUtils.isInside(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollY, 10, 10)) { Sounds.playButtonSound(); mod.reset(false); } - if(mod.getCategory() == ModCategory.MODS && Switch.isHovered(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollAnimation.getValue())) { + if(mod.getCategory() == ModCategory.MODS && Switch.isHovered(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollY)) { Sounds.playButtonSound(); mod.toggle(); } diff --git a/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerEntityModelAccessor.java b/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerEntityModelAccessor.java new file mode 100644 index 0000000..281ebd2 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerEntityModelAccessor.java @@ -0,0 +1,14 @@ +package net.silentclient.client.mixin.accessors.skins; + +import net.silentclient.client.mods.render.skins.renderlayers.BodyLayerFeatureRenderer; +import net.silentclient.client.mods.render.skins.renderlayers.HeadLayerFeatureRenderer; + +/** + * Used to expose the thinArms setting of the player model + * + */ +public interface PlayerEntityModelAccessor { + public boolean client$hasThinArms(); + public HeadLayerFeatureRenderer client$getHeadLayer(); + public BodyLayerFeatureRenderer client$getBodyLayer(); +} \ No newline at end of file diff --git a/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerSettings.java b/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerSettings.java new file mode 100644 index 0000000..85d5177 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/accessors/skins/PlayerSettings.java @@ -0,0 +1,15 @@ +package net.silentclient.client.mixin.accessors.skins; + +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; + +public interface PlayerSettings { + + public CustomizableModelPart client$getHeadLayers(); + + public void client$setupHeadLayers(CustomizableModelPart box); + + public CustomizableModelPart[] client$getSkinLayers(); + + public void client$setupSkinLayers(CustomizableModelPart[] box); + +} diff --git a/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullModelAccessor.java b/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullModelAccessor.java new file mode 100644 index 0000000..ec47da1 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullModelAccessor.java @@ -0,0 +1,7 @@ +package net.silentclient.client.mixin.accessors.skins; + +public interface SkullModelAccessor { + + public void showHat(boolean val); + +} diff --git a/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullSettings.java b/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullSettings.java new file mode 100644 index 0000000..2fe6f00 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/accessors/skins/SkullSettings.java @@ -0,0 +1,11 @@ +package net.silentclient.client.mixin.accessors.skins; + +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; + +public interface SkullSettings { + + public CustomizableModelPart getHeadLayers(); + + public void setupHeadLayers(CustomizableModelPart box); + +} diff --git a/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerMixin.java b/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerMixin.java new file mode 100644 index 0000000..4452578 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerMixin.java @@ -0,0 +1,47 @@ +package net.silentclient.client.mixin.mixins.skins; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; +import net.silentclient.client.mixin.accessors.skins.PlayerSettings; +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +/** + * Keep player specific settings, data and modifies the eye location when enabled + * + */ +@Mixin(EntityPlayer.class) +public abstract class PlayerMixin extends EntityLivingBase implements PlayerSettings { + + public PlayerMixin(World p_i1594_1_) { + super(p_i1594_1_); + } + + @Unique + private CustomizableModelPart headLayer; + @Unique + private CustomizableModelPart[] skinLayer; + + @Override + public CustomizableModelPart[] client$getSkinLayers() { + return skinLayer; + } + + @Override + public void client$setupSkinLayers(CustomizableModelPart[] box) { + this.skinLayer = box; + } + + @Override + public CustomizableModelPart client$getHeadLayers() { + return headLayer; + } + + @Override + public void client$setupHeadLayers(CustomizableModelPart box) { + this.headLayer = box; + } + +} diff --git a/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerRendererMixin.java b/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerRendererMixin.java new file mode 100644 index 0000000..00315b4 --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/mixins/skins/PlayerRendererMixin.java @@ -0,0 +1,177 @@ +package net.silentclient.client.mixin.mixins.skins; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelPlayer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.silentclient.client.Client; +import net.silentclient.client.mixin.accessors.skins.PlayerEntityModelAccessor; +import net.silentclient.client.mixin.accessors.skins.PlayerSettings; +import net.silentclient.client.mods.render.skins.SkinUtil; +import net.silentclient.client.mods.render.skins.SkinsMod; +import net.silentclient.client.mods.render.skins.renderlayers.BodyLayerFeatureRenderer; +import net.silentclient.client.mods.render.skins.renderlayers.HeadLayerFeatureRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(RenderPlayer.class) +public abstract class PlayerRendererMixin extends RendererLivingEntity implements PlayerEntityModelAccessor { + + @Shadow + private boolean smallArms; + @Unique + private HeadLayerFeatureRenderer client$headLayer; + @Unique + private BodyLayerFeatureRenderer client$bodyLayer; + + public PlayerRendererMixin(RenderManager p_i46156_1_, ModelBase p_i46156_2_, float p_i46156_3_) { + super(p_i46156_1_, p_i46156_2_, p_i46156_3_); + } + + @Inject(method = "*", at = @At("RETURN")) + public void onCreate(CallbackInfo info) { + client$headLayer = new HeadLayerFeatureRenderer((RenderPlayer)(Object)this); + client$bodyLayer = new BodyLayerFeatureRenderer((RenderPlayer)(Object)this); + } + + @Inject(method = "setModelVisibilities", at = @At("HEAD")) + private void setModelProperties(AbstractClientPlayer abstractClientPlayer, CallbackInfo info) { + ModelPlayer playerModel = getMainModel(); + if(!Client.getInstance().getModInstances().getModByClass(SkinsMod.class).isEnabled()) { + playerModel.bipedHeadwear.isHidden = false; + playerModel.bipedBodyWear.isHidden = false; + playerModel.bipedLeftArmwear.isHidden = false; + playerModel.bipedRightArmwear.isHidden = false; + playerModel.bipedLeftLegwear.isHidden = false; + playerModel.bipedRightLegwear.isHidden = false; + return; + } + if(Minecraft.getMinecraft().thePlayer.getPositionVector().squareDistanceTo(abstractClientPlayer.getPositionVector()) < Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt()*Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt()) { + playerModel.bipedHeadwear.isHidden = playerModel.bipedHeadwear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Hat").getValBoolean(); + playerModel.bipedBodyWear.isHidden = playerModel.bipedBodyWear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Jacket").getValBoolean(); + playerModel.bipedLeftArmwear.isHidden = playerModel.bipedLeftArmwear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Left Sleeve").getValBoolean(); + playerModel.bipedRightArmwear.isHidden = playerModel.bipedRightArmwear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Right Sleeve").getValBoolean(); + playerModel.bipedLeftLegwear.isHidden = playerModel.bipedLeftLegwear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Left Pants").getValBoolean(); + playerModel.bipedRightLegwear.isHidden = playerModel.bipedRightLegwear.isHidden || Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Right Pants").getValBoolean(); + } else { + // not correct, but the correct way doesn't work cause 1.8 or whatever + if(!abstractClientPlayer.isSpectator()) { + playerModel.bipedHeadwear.isHidden = false; + playerModel.bipedBodyWear.isHidden = false; + playerModel.bipedLeftArmwear.isHidden = false; + playerModel.bipedRightArmwear.isHidden = false; + playerModel.bipedLeftLegwear.isHidden = false; + playerModel.bipedRightLegwear.isHidden = false; + } + } + } + + + + @Override + public HeadLayerFeatureRenderer client$getHeadLayer() { + return client$headLayer; + } + + @Override + public BodyLayerFeatureRenderer client$getBodyLayer() { + return client$bodyLayer; + } + + @Override + public boolean client$hasThinArms() { + return smallArms; + } + + @Shadow + public abstract ModelPlayer getMainModel(); + + @Inject(method = "renderRightArm", at = @At("RETURN")) + public void renderRightArm(AbstractClientPlayer player, CallbackInfo info) { + if(!Client.getInstance().getModInstances().getModByClass(SkinsMod.class).isEnabled()) { + return; + } + client$renderFirstPersonArm(player, 3); + } + + @Inject(method = "renderLeftArm", at = @At("RETURN")) + public void renderLeftArm(AbstractClientPlayer player, CallbackInfo info) { + if(!Client.getInstance().getModInstances().getModByClass(SkinsMod.class).isEnabled()) { + return; + } + client$renderFirstPersonArm(player, 2); + } + + @Unique + private void client$renderFirstPersonArm(AbstractClientPlayer player, int layerId) { + ModelPlayer modelplayer = getMainModel(); + float pixelScaling = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Voxel Size").getValFloat(); + PlayerSettings settings = (PlayerSettings) player; + if(settings.client$getSkinLayers() == null && !client$setupModel(player, settings)) { + return; + } + GlStateManager.pushMatrix(); + modelplayer.bipedRightArm.postRender(0.0625F); + GlStateManager.scale(0.0625, 0.0625, 0.0625); + GlStateManager.scale(pixelScaling, pixelScaling, pixelScaling); + if(!smallArms) { + settings.client$getSkinLayers()[layerId].x = -0.998f*16f; + } else { + settings.client$getSkinLayers()[layerId].x = -0.499f*16; + } + settings.client$getSkinLayers()[layerId].render(false); + GlStateManager.popMatrix(); + } + + @Unique + private boolean client$setupModel(AbstractClientPlayer abstractClientPlayerEntity, PlayerSettings settings) { + if(!SkinUtil.hasCustomSkin(abstractClientPlayerEntity)) { + return false; // default skin + } + SkinUtil.setup3dLayers(abstractClientPlayerEntity, settings, smallArms, null); + return true; + } + +// @Inject(method = "renderHand", at = @At("RETURN")) +// private void renderHand(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, +// AbstractClientPlayer abstractClientPlayer, ModelPart arm, ModelPart sleeve, CallbackInfo info) { +// if(sleeve.visible)return; // Vanilla one is active +// PlayerSettings settings = (PlayerSettings) abstractClientPlayer; +// float pixelScaling = 1.1f; +// float armHeightScaling = 1.1f; +// boolean thinArms = ((PlayerEntityModelAccessor)getModel()).hasThinArms(); +// if(settings.getSkinLayers() == null && !SkinUtil.setup3dLayers(abstractClientPlayer, settings, thinArms, getModel())) { +// return; +// } +// CustomizableModelPart part = null; +// if(sleeve == this.model.leftSleeve) { +// part = settings.getSkinLayers()[2]; +// }else { +// part = settings.getSkinLayers()[3]; +// } +// part.copyFrom(arm); +// poseStack.pushPose(); +// poseStack.scale(pixelScaling, armHeightScaling, pixelScaling); +// part.y -= 0.6; +// if(!thinArms) { +// part.x -= 0.4; +// } +// part.render(poseStack, +// multiBufferSource +// .getBuffer(RenderType.entityTranslucent(abstractClientPlayer.getSkinTextureLocation())), +// i, OverlayTexture.NO_OVERLAY); +// part.setPos(0, 0, 0); +// part.setRotation(0, 0, 0); +// poseStack.popPose(); +// +// } + +} diff --git a/src/main/java/net/silentclient/client/mixin/mixins/skins/RendererLivingEntityMixin.java b/src/main/java/net/silentclient/client/mixin/mixins/skins/RendererLivingEntityMixin.java new file mode 100644 index 0000000..5a31c4c --- /dev/null +++ b/src/main/java/net/silentclient/client/mixin/mixins/skins/RendererLivingEntityMixin.java @@ -0,0 +1,52 @@ +package net.silentclient.client.mixin.mixins.skins; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.entity.EntityLivingBase; +import net.silentclient.client.Client; +import net.silentclient.client.mixin.accessors.skins.PlayerEntityModelAccessor; +import net.silentclient.client.mods.render.skins.SkinsMod; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(RendererLivingEntity.class) +public class RendererLivingEntityMixin { + + @Inject(method = "renderModel", at = @At("TAIL")) + protected void renderModelLayers(T p_renderModel_1_, float p_renderModel_2_, float p_renderModel_3_, + float p_renderModel_4_, float p_renderModel_5_, float p_renderModel_6_, float p_renderModel_7_, CallbackInfo info) { + if(!Client.getInstance().getModInstances().getModByClass(SkinsMod.class).isEnabled()) { + return; + } + if(!(this instanceof PlayerEntityModelAccessor)) { + return; + } + boolean flag = !p_renderModel_1_.isInvisible(); + boolean flag1 = (!flag && !p_renderModel_1_.isInvisibleToPlayer((Minecraft.getMinecraft()).thePlayer)); + if (flag || flag1) { + PlayerEntityModelAccessor playerRenderer = (PlayerEntityModelAccessor) this; + if (flag1) { + GlStateManager.pushMatrix(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 0.15F); + GlStateManager.depthMask(false); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(770, 771); + GlStateManager.alphaFunc(516, 0.003921569F); + } + playerRenderer.client$getHeadLayer().doRenderLayer((AbstractClientPlayer) p_renderModel_1_, p_renderModel_2_, 0f, p_renderModel_3_, p_renderModel_4_, p_renderModel_5_, p_renderModel_6_, p_renderModel_7_); + playerRenderer.client$getBodyLayer().doRenderLayer((AbstractClientPlayer) p_renderModel_1_, p_renderModel_2_, 0f, p_renderModel_3_, p_renderModel_4_, p_renderModel_5_, p_renderModel_6_, p_renderModel_7_); + if (flag1) { + GlStateManager.disableBlend(); + GlStateManager.alphaFunc(516, 0.1F); + GlStateManager.popMatrix(); + GlStateManager.depthMask(true); + } + } + } + + +} diff --git a/src/main/java/net/silentclient/client/mods/ModInstances.java b/src/main/java/net/silentclient/client/mods/ModInstances.java index bb33580..3bd6f82 100644 --- a/src/main/java/net/silentclient/client/mods/ModInstances.java +++ b/src/main/java/net/silentclient/client/mods/ModInstances.java @@ -11,6 +11,7 @@ import net.silentclient.client.mods.hypixel.togglechat.ToggleChatMod; import net.silentclient.client.mods.player.*; import net.silentclient.client.mods.render.*; import net.silentclient.client.mods.render.crosshair.CrosshairMod; +import net.silentclient.client.mods.render.skins.SkinsMod; import net.silentclient.client.mods.settings.CosmeticsMod; import net.silentclient.client.mods.settings.FPSBoostMod; import net.silentclient.client.mods.settings.GeneralMod; @@ -189,6 +190,7 @@ public class ModInstances { } mods.add(new QuickPlayMod()); mods.add(new SoundsMod()); + mods.add(new SkinsMod()); } public void postInit() { diff --git a/src/main/java/net/silentclient/client/mods/render/SkinsMod.java b/src/main/java/net/silentclient/client/mods/render/SkinsMod.java deleted file mode 100644 index e1d4356..0000000 --- a/src/main/java/net/silentclient/client/mods/render/SkinsMod.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.silentclient.client.mods.render; - -import net.silentclient.client.mods.Mod; -import net.silentclient.client.mods.ModCategory; - -public class SkinsMod extends Mod { - public SkinsMod() { - super("3D Skins", ModCategory.MODS, "silentclient/icons/mods/3dskins.png"); - } -} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/Direction.java b/src/main/java/net/silentclient/client/mods/render/skins/Direction.java new file mode 100644 index 0000000..340f729 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/Direction.java @@ -0,0 +1,33 @@ +package net.silentclient.client.mods.render.skins; + +import org.lwjgl.util.vector.Vector3f; + +import net.minecraft.util.Vec3i; + +public enum Direction { + DOWN(new Vec3i(0, -1, 0)), UP(new Vec3i(0, 1, 0)), NORTH(new Vec3i(0, 0, -1)), SOUTH(new Vec3i(0, 0, 1)), + WEST(new Vec3i(-1, 0, 0)), EAST(new Vec3i(1, 0, 0)); + + private Direction(Vec3i normal) { + this.normal = normal; + } + + private final Vec3i normal; + + public int getStepX() { + return this.normal.getX(); + } + + public int getStepY() { + return this.normal.getY(); + } + + public int getStepZ() { + return this.normal.getZ(); + } + + public Vector3f step() { + return new Vector3f(getStepX(), getStepY(), getStepZ()); + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/SkinUtil.java b/src/main/java/net/silentclient/client/mods/render/skins/SkinUtil.java new file mode 100644 index 0000000..41dc943 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/SkinUtil.java @@ -0,0 +1,79 @@ +package net.silentclient.client.mods.render.skins; + +import com.mojang.authlib.GameProfile; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.model.ModelPlayer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.texture.ITextureObject; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.resources.DefaultPlayerSkin; +import net.minecraft.util.ResourceLocation; +import net.silentclient.client.mixin.accessors.skins.PlayerSettings; +import net.silentclient.client.mixin.accessors.skins.SkullSettings; +import net.silentclient.client.mods.render.skins.opengl.NativeImage; +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; +import net.silentclient.client.mods.render.skins.render.SolidPixelWrapper; + +public class SkinUtil { + + public static boolean hasCustomSkin(AbstractClientPlayer player) { + return !DefaultPlayerSkin.getDefaultSkin((player).getUniqueID()).equals((player).getLocationSkin()); + } + + private static NativeImage getSkinTexture(AbstractClientPlayer player) { + return getTexture(player.getLocationSkin()); + } + + private static NativeImage getTexture(ResourceLocation resource) { + NativeImage skin = new NativeImage(64, 64, false); + TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); + ITextureObject abstractTexture = textureManager.getTexture(resource); + if(abstractTexture == null)return null; // fail save + GlStateManager.bindTexture(abstractTexture.getGlTextureId()); + skin.downloadTexture(0, false); + return skin; + } + + public static boolean setup3dLayers(AbstractClientPlayer abstractClientPlayerEntity, PlayerSettings settings, boolean thinArms, ModelPlayer model) { + if(!SkinUtil.hasCustomSkin(abstractClientPlayerEntity)) { + return false; // default skin + } + NativeImage skin = SkinUtil.getSkinTexture(abstractClientPlayerEntity); + if(skin == null)return false; // fail save + CustomizableModelPart[] layers = new CustomizableModelPart[5]; + layers[0] = SolidPixelWrapper.wrapBox(skin, 4, 12, 4, 0, 48, true, 0f); + layers[1] = SolidPixelWrapper.wrapBox(skin, 4, 12, 4, 0, 32, true, 0f); + if(thinArms) { + layers[2] = SolidPixelWrapper.wrapBox(skin, 3, 12, 4, 48, 48, true, -2.5f); + layers[3] = SolidPixelWrapper.wrapBox(skin, 3, 12, 4, 40, 32, true, -2.5f); + } else { + layers[2] = SolidPixelWrapper.wrapBox(skin, 4, 12, 4, 48, 48, true, -2.5f); + layers[3] = SolidPixelWrapper.wrapBox(skin, 4, 12, 4, 40, 32, true, -2.5f); + } + layers[4] = SolidPixelWrapper.wrapBox(skin, 8, 12, 4, 16, 32, true, -0.8f); + settings.client$setupSkinLayers(layers); + settings.client$setupHeadLayers(SolidPixelWrapper.wrapBox(skin, 8, 8, 8, 32, 0, false, 0.6f)); + skin.close(); + return true; + } + + public static boolean setup3dLayers(GameProfile gameprofile, SkullSettings settings) { + if(gameprofile == null) { + return false; // no gameprofile + } + /*Map map = Minecraft.getMinecraft().getSkinManager() + .loadProfileTextures(gameprofile); + MinecraftProfileTexture texture = map.get(MinecraftProfileTexture.Type.SKIN); + if(texture == null) { + return false; // it's a gameprofile, but no skin. + } + NativeImage skin = SkinUtil.getTexture(Minecraft.getMinecraft().getSkinManager() + .registerTexture(texture, MinecraftProfileTexture.Type.SKIN)); + settings.setupHeadLayers(SolidPixelWrapper.wrapBox(skin, 8, 8, 8, 32, 0, false, 0.6f)); + skin.close(); + return true;*/ + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/net/silentclient/client/mods/render/skins/SkinsMod.java b/src/main/java/net/silentclient/client/mods/render/skins/SkinsMod.java new file mode 100644 index 0000000..f84f024 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/SkinsMod.java @@ -0,0 +1,34 @@ +package net.silentclient.client.mods.render.skins; + +import net.silentclient.client.mods.Mod; +import net.silentclient.client.mods.ModCategory; + +public class SkinsMod extends Mod { + public SkinsMod() { + super("3D Skins", ModCategory.MODS, "silentclient/icons/mods/skinsmod.png"); + } + + @Override + public void setup() { + super.setup(); + setNewMod(true); + this.addBooleanSetting("3D Hat", this, true); + this.addBooleanSetting("3D Jacket", this, true); + this.addBooleanSetting("3D Left Sleeve", this, true); + this.addBooleanSetting("3D Right Sleeve", this, true); + this.addBooleanSetting("3D Left Pants", this, true); + this.addBooleanSetting("3D Right Pants", this, true); + + this.addSliderSetting("Voxel Size", this, 1.15F, 1F, 1.4F, false); + this.addSliderSetting("Head Voxel Size", this, 1.18F, 1F, 1.25F, false); + this.addSliderSetting("Body Voxel Width Size", this, 1.05F, 1F, 1.4F, false); + + this.addBooleanSetting("3D Skulls", this, true); + this.addBooleanSetting("3D Skull Items", this, true); + this.addSliderSetting("Skull Voxel Size", this, 1.1F, 1F, 1.2F, false); + + this.addSliderSetting("Level Of Detail Distance", this, 14, 5, 40, true); + + this.addBooleanSetting("Fast Render", this, true); + } +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/SkullRendererCache.java b/src/main/java/net/silentclient/client/mods/render/skins/SkullRendererCache.java new file mode 100644 index 0000000..c88e439 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/SkullRendererCache.java @@ -0,0 +1,31 @@ +package net.silentclient.client.mods.render.skins; + +import net.minecraft.item.ItemStack; +import net.silentclient.client.mixin.accessors.skins.SkullSettings; +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; + +import java.util.WeakHashMap; + +public class SkullRendererCache { + + public static boolean renderNext = false; + public static SkullSettings lastSkull = null; + public static WeakHashMap itemCache = new WeakHashMap<>(); + + public static class ItemSettings implements SkullSettings { + + private CustomizableModelPart hatModel = null; + + @Override + public CustomizableModelPart getHeadLayers() { + return hatModel; + } + + @Override + public void setupHeadLayers(CustomizableModelPart box) { + this.hatModel = box; + } + + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/opengl/GlStateManager.java b/src/main/java/net/silentclient/client/mods/render/skins/opengl/GlStateManager.java new file mode 100644 index 0000000..6c8d8bb --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/opengl/GlStateManager.java @@ -0,0 +1,15 @@ +package net.silentclient.client.mods.render.skins.opengl; + +import org.lwjgl.opengl.GL11; + +import java.nio.ByteBuffer; + +public class GlStateManager { + public static void _getTexImage(int i, int j, int k, int l, ByteBuffer m) { + GL11.glGetTexImage(i, j, k, l, m); + } + + public static void _pixelStore(int i, int j) { + GL11.glPixelStorei(i, j); + } +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/opengl/NativeImage.java b/src/main/java/net/silentclient/client/mods/render/skins/opengl/NativeImage.java new file mode 100644 index 0000000..30333f9 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/opengl/NativeImage.java @@ -0,0 +1,294 @@ +package net.silentclient.client.mods.render.skins.opengl; + +import java.nio.ByteBuffer; + +public final class NativeImage implements AutoCloseable { + + private final Format format; + + private final int width; + + private final int height; + + private ByteBuffer buffer; + + private final int size; + + public NativeImage(int i, int j, boolean bl) { + this(Format.RGBA, i, j, bl); + } + + public NativeImage(Format format, int i, int j, boolean bl) { + if (i <= 0 || j <= 0) + throw new IllegalArgumentException("Invalid texture size: " + i + "x" + j); + this.format = format; + this.width = i; + this.height = j; + this.size = i * j * format.components(); + buffer = ByteBuffer.allocateDirect(this.size); + } + + private boolean isOutsideBounds(int i, int j) { + return (i < 0 || i >= this.width || j < 0 || j >= this.height); + } + + public void close() { + // nothing to do? + } + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + + public Format format() { + return this.format; + } + + public int getPixelRGBA(int i, int j) { + if (this.format != Format.RGBA) + throw new IllegalArgumentException( + String.format("getPixelRGBA only works on RGBA images; have %s", new Object[]{this.format})); + if (isOutsideBounds(i, j)) + throw new IllegalArgumentException( + String.format("(%s, %s) outside of image bounds (%s, %s)", new Object[]{Integer.valueOf(i), + Integer.valueOf(j), Integer.valueOf(this.width), Integer.valueOf(this.height)})); + int l = (i + j * this.width) * 4; + return buffer.getInt(l); + } + + public void setPixelRGBA(int i, int j, int k) { + if (this.format != Format.RGBA) + throw new IllegalArgumentException( + String.format("getPixelRGBA only works on RGBA images; have %s", new Object[]{this.format})); + if (isOutsideBounds(i, j)) + throw new IllegalArgumentException( + String.format("(%s, %s) outside of image bounds (%s, %s)", new Object[]{Integer.valueOf(i), + Integer.valueOf(j), Integer.valueOf(this.width), Integer.valueOf(this.height)})); + int l = (i + j * this.width) * 4; + buffer.putInt(l, k); + } + + public byte getLuminanceOrAlpha(int i, int j) { + if (!this.format.hasLuminanceOrAlpha()) + throw new IllegalArgumentException(String.format("no luminance or alpha in %s", new Object[]{this.format})); + if (isOutsideBounds(i, j)) + throw new IllegalArgumentException( + String.format("(%s, %s) outside of image bounds (%s, %s)", new Object[]{Integer.valueOf(i), + Integer.valueOf(j), Integer.valueOf(this.width), Integer.valueOf(this.height)})); + int k = (i + j * this.width) * this.format.components() + this.format.luminanceOrAlphaOffset() / 8; + return buffer.get(k); + } + + + public void downloadTexture(int i, boolean bl) { + //RenderSystem.assertOnRenderThread(); + this.format.setPackPixelStoreState(); + GlStateManager._getTexImage(3553, i, this.format.glFormat(), 5121, this.buffer); + if (bl && this.format.hasAlpha()) + for (int j = 0; j < getHeight(); j++) { + for (int k = 0; k < getWidth(); k++) + setPixelRGBA(k, j, getPixelRGBA(k, j) | 255 << this.format.alphaOffset()); + } + } + +// public void downloadDepthBuffer(float f) { +// //RenderSystem.assertOnRenderThread(); +// if (this.format.components() != 1) +// throw new IllegalStateException("Depth buffer must be stored in NativeImage with 1 component."); +// checkAllocated(); +// this.format.setPackPixelStoreState(); +// GlStateManager._readPixels(0, 0, this.width, this.height, 6402, 5121, this.pixels); +// } + + public static int getA(int i) { + return i >> 24 & 0xFF; + } + + public static int getR(int i) { + return i >> 0 & 0xFF; + } + + public static int getG(int i) { + return i >> 8 & 0xFF; + } + + public static int getB(int i) { + return i >> 16 & 0xFF; + } + + public static int combine(int i, int j, int k, int l) { + return (i & 0xFF) << 24 | (j & 0xFF) << 16 | (k & 0xFF) << 8 | (l & 0xFF) << 0; + } + + public enum InternalGlFormat { + RGBA(6408), RGB(6407), RG(33319), RED(6403); + + private final int glFormat; + + InternalGlFormat(int j) { + this.glFormat = j; + } + + public int glFormat() { + return this.glFormat; + } + } + + public enum Format { + RGBA(4, 6408, true, true, true, false, true, 0, 8, 16, 255, 24, true), RGB(3, 6407, true, true, true, false, + false, 0, 8, 16, 255, 255, true), LUMINANCE_ALPHA(2, 33319, false, false, false, true, true, 255, 255, + 255, 0, 8, true), LUMINANCE(1, 6403, false, false, false, true, false, 0, 0, 0, 0, 255, true); + + final int components; + + private final int glFormat; + + private final boolean hasRed; + + private final boolean hasGreen; + + private final boolean hasBlue; + + private final boolean hasLuminance; + + private final boolean hasAlpha; + + private final int redOffset; + + private final int greenOffset; + + private final int blueOffset; + + private final int luminanceOffset; + + private final int alphaOffset; + + private final boolean supportedByStb; + + Format(int j, int k, boolean bl, boolean bl2, boolean bl3, boolean bl4, boolean bl5, int l, int m, int n, int o, + int p, boolean bl6) { + this.components = j; + this.glFormat = k; + this.hasRed = bl; + this.hasGreen = bl2; + this.hasBlue = bl3; + this.hasLuminance = bl4; + this.hasAlpha = bl5; + this.redOffset = l; + this.greenOffset = m; + this.blueOffset = n; + this.luminanceOffset = o; + this.alphaOffset = p; + this.supportedByStb = bl6; + } + + public int components() { + return this.components; + } + + public void setPackPixelStoreState() { + //RenderSystem.assertOnRenderThread(); + GlStateManager._pixelStore(3333, components()); + } + + public void setUnpackPixelStoreState() { + GlStateManager._pixelStore(3317, components()); + } + + public int glFormat() { + return this.glFormat; + } + + public boolean hasRed() { + return this.hasRed; + } + + public boolean hasGreen() { + return this.hasGreen; + } + + public boolean hasBlue() { + return this.hasBlue; + } + + public boolean hasLuminance() { + return this.hasLuminance; + } + + public boolean hasAlpha() { + return this.hasAlpha; + } + + public int redOffset() { + return this.redOffset; + } + + public int greenOffset() { + return this.greenOffset; + } + + public int blueOffset() { + return this.blueOffset; + } + + public int luminanceOffset() { + return this.luminanceOffset; + } + + public int alphaOffset() { + return this.alphaOffset; + } + + public boolean hasLuminanceOrRed() { + return (this.hasLuminance || this.hasRed); + } + + public boolean hasLuminanceOrGreen() { + return (this.hasLuminance || this.hasGreen); + } + + public boolean hasLuminanceOrBlue() { + return (this.hasLuminance || this.hasBlue); + } + + public boolean hasLuminanceOrAlpha() { + return (this.hasLuminance || this.hasAlpha); + } + + public int luminanceOrRedOffset() { + return this.hasLuminance ? this.luminanceOffset : this.redOffset; + } + + public int luminanceOrGreenOffset() { + return this.hasLuminance ? this.luminanceOffset : this.greenOffset; + } + + public int luminanceOrBlueOffset() { + return this.hasLuminance ? this.luminanceOffset : this.blueOffset; + } + + public int luminanceOrAlphaOffset() { + return this.hasLuminance ? this.luminanceOffset : this.alphaOffset; + } + + public boolean supportedByStb() { + return this.supportedByStb; + } + + static Format getStbFormat(int i) { + switch (i) { + case 1 : + return LUMINANCE; + case 2 : + return LUMINANCE_ALPHA; + case 3 : + return RGB; + } + return RGBA; + } + } +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCube.java b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCube.java new file mode 100644 index 0000000..e173aa7 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCube.java @@ -0,0 +1,152 @@ +package net.silentclient.client.mods.render.skins.render; + +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.silentclient.client.mods.render.skins.Direction; +import org.lwjgl.util.vector.Vector3f; + +public class CustomizableCube { + + private final Direction[] hidden; + private final Polygon[] polygons; + private int polygonCount = 0; + public final float minX; + public final float minY; + public final float minZ; + public final float maxX; + public final float maxY; + public final float maxZ; + + public CustomizableCube(int u, int v, float x, float y, float z, float sizeX, float sizeY, float sizeZ, float extraX, float extraY, + float extraZ, boolean mirror, float textureWidth, float textureHeight, Direction[] hide) { + this.hidden = hide; + this.minX = x; + this.minY = y; + this.minZ = z; + this.maxX = x + sizeX; + this.maxY = y + sizeY; + this.maxZ = z + sizeZ; + this.polygons = new Polygon[6]; + float pX = x + sizeX; + float pY = y + sizeY; + float pZ = z + sizeZ; + x -= extraX; + y -= extraY; + z -= extraZ; + pX += extraX; + pY += extraY; + pZ += extraZ; + if (mirror) { + float i = pX; + pX = x; + x = i; + } + Vertex vertex = new Vertex(x, y, z, 0.0F, 0.0F); + Vertex vertex2 = new Vertex(pX, y, z, 0.0F, 8.0F); + Vertex vertex3 = new Vertex(pX, pY, z, 8.0F, 8.0F); + Vertex vertex4 = new Vertex(x, pY, z, 8.0F, 0.0F); + Vertex vertex5 = new Vertex(x, y, pZ, 0.0F, 0.0F); + Vertex vertex6 = new Vertex(pX, y, pZ, 0.0F, 8.0F); + Vertex vertex7 = new Vertex(pX, pY, pZ, 8.0F, 8.0F); + Vertex vertex8 = new Vertex(x, pY, pZ, 8.0F, 0.0F); + + float l = u + sizeZ + sizeX; + float n = u + sizeZ + sizeX + sizeZ; + + float q = v + sizeZ; + float r = v + sizeZ + sizeY; + + if(visibleFace(Direction.DOWN)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex6, vertex5, vertex, vertex2}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.DOWN); + if(visibleFace(Direction.UP)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex3, vertex4, vertex8, vertex7}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.UP); + if(visibleFace(Direction.WEST)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex, vertex5, vertex8, vertex4}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.WEST); + if(visibleFace(Direction.NORTH)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex2, vertex, vertex4, vertex3}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.NORTH); + if(visibleFace(Direction.EAST)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex6, vertex2, vertex3, vertex7}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.EAST); + if(visibleFace(Direction.SOUTH)) + this.polygons[polygonCount++] = new Polygon(new Vertex[]{vertex5, vertex6, vertex7, vertex8}, l, q, n, r, textureWidth, textureHeight, mirror, Direction.SOUTH); + } + + private boolean visibleFace(Direction face) { + for(Direction dir : hidden) { + if(dir == face)return false; + } + return true; + } + + public void render(WorldRenderer worldRenderer, boolean redTint) { + redTint = false; + worldRenderer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR_NORMAL); + Polygon polygon; + for (int id = 0; id < polygonCount; id++) { + polygon = polygons[id]; + + for (int i = 0; i < 4; i++) { + Vertex vertex = polygon.vertices[i]; + worldRenderer + .pos(vertex.pos.x, vertex.pos.y, + vertex.pos.z) + .tex(vertex.u, vertex.v).color(255, redTint ? 127 : 255, redTint ? 127 : 255, 255).normal(polygon.normal.x, polygon.normal.y, polygon.normal.z) + .endVertex(); + } + } + Tessellator.getInstance().draw(); + } + + private static class Polygon { + public final Vertex[] vertices; + + public final Vector3f normal; + + public Polygon(Vertex[] vertexs, float f, float g, float h, float i, float j, float k, boolean bl, + Direction dir) { + this.vertices = vertexs; + float l = 0.0F / j; + float m = 0.0F / k; + vertexs[0] = vertexs[0].remap(h / j - l, g / k + m); + vertexs[1] = vertexs[1].remap(f / j + l, g / k + m); + vertexs[2] = vertexs[2].remap(f / j + l, i / k - m); + vertexs[3] = vertexs[3].remap(h / j - l, i / k - m); + if (bl) { + int n = vertexs.length; + for (int o = 0; o < n / 2; o++) { + Vertex vertex = vertexs[o]; + vertexs[o] = vertexs[n - 1 - o]; + vertexs[n - 1 - o] = vertex; + } + } + this.normal = dir.step(); + if (bl) + this.normal.setX(this.normal.getX()*-1); + } + } + + private static class Vertex { + public final Vector3f pos; + public final float u; + public final float v; + public final float o,p,q; + + public Vertex(float f, float g, float h, float i, float j) { + this(new Vector3f(f, g, h), i, j); + } + + public Vertex remap(float f, float g) { + return new Vertex(this.pos, f, g); + } + + public Vertex(Vector3f vector3f, float f, float g) { + this.pos = vector3f; + this.u = f; + this.v = g; + o = pos.x / 16.0F; + p = pos.y / 16.0F; + q = pos.z / 16.0F; + } + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCubeListBuilder.java b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCubeListBuilder.java new file mode 100644 index 0000000..cd77d5c --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableCubeListBuilder.java @@ -0,0 +1,41 @@ +package net.silentclient.client.mods.render.skins.render; + +import com.google.common.collect.Lists; +import net.silentclient.client.mods.render.skins.Direction; + +import java.util.List; + +public class CustomizableCubeListBuilder { + + private final List cubes = Lists.newArrayList(); + private int xTexOffs; + private int yTexOffs; + private boolean mirror; + + public static CustomizableCubeListBuilder create() { + return new CustomizableCubeListBuilder(); + } + + public CustomizableCubeListBuilder texOffs(int i, int j) { + this.xTexOffs = i; + this.yTexOffs = j; + return this; + } + + public CustomizableCubeListBuilder mirror(boolean bl) { + this.mirror = bl; + return this; + } + + public List getCubes() { + return cubes; + } + + public CustomizableCubeListBuilder addBox(float x, float y, float z, float pixelSize, Direction[] hide) { + int textureSize = 64; + this.cubes.add(new CustomizableCube(xTexOffs, yTexOffs, x, y, z, pixelSize, pixelSize, pixelSize, 0, 0, 0, + this.mirror, textureSize, textureSize, hide)); + return this; + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableModelPart.java b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableModelPart.java new file mode 100644 index 0000000..37f80db --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/render/CustomizableModelPart.java @@ -0,0 +1,57 @@ +package net.silentclient.client.mods.render.skins.render; + +import net.minecraft.client.model.ModelBox; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; + +import java.util.List; + +/** + * Cut down copy of the Vanilla ModelPart to bypass Optifine/Sodium screwing + * with the CustomizableCube class + * + */ +public class CustomizableModelPart { + + public float x; + public float y; + public float z; + public boolean visible = true; + private final List cubes; + + public CustomizableModelPart(List list) { + this.cubes = list; + } + + public void copyFrom(ModelBox modelPart) { + this.x = modelPart.posX1; + this.y = modelPart.posY1; + this.z = modelPart.posZ1; + } + + public void setPos(float f, float g, float h) { + this.x = f; + this.y = g; + this.z = h; + } + + + public void render(boolean redTint) { + if (!this.visible) + return; + GlStateManager.pushMatrix(); + translateAndRotate(); + compile(redTint); + GlStateManager.popMatrix(); + } + + public void translateAndRotate() { + GlStateManager.translate((this.x / 16.0F), (this.y / 16.0F), (this.z / 16.0F)); + } + + private void compile(boolean redTint) { + for (CustomizableCube cube : this.cubes) + cube.render(Tessellator.getInstance().getWorldRenderer(), redTint); + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/render/SolidPixelWrapper.java b/src/main/java/net/silentclient/client/mods/render/skins/render/SolidPixelWrapper.java new file mode 100644 index 0000000..0dec952 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/render/SolidPixelWrapper.java @@ -0,0 +1,108 @@ +package net.silentclient.client.mods.render.skins.render; + +import net.silentclient.client.mods.render.skins.Direction; +import net.silentclient.client.mods.render.skins.opengl.NativeImage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class SolidPixelWrapper { + + public static CustomizableModelPart wrapBox(NativeImage natImage, int width, + int height, int depth, int textureU, int textureV, boolean topPivot, float rotationOffset) { + List cubes = new ArrayList<>(); + float pixelSize = 1f; + float staticXOffset = -width / 2f; + float staticYOffset = topPivot ? +rotationOffset : -height + rotationOffset; + float staticZOffset = -depth / 2f; + // Front/back + for (int u = 0; u < width; u++) { + for (int v = 0; v < height; v++) { + // front + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == width - 1 || v == height - 1, + textureU + depth + u, textureV + depth + v, staticXOffset + u, staticYOffset + v, staticZOffset, + Direction.SOUTH); + // back + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == width - 1 || v == height - 1, + textureU + 2 * depth + width + u, textureV + depth + v, staticXOffset + width - 1 - u, + staticYOffset + v, staticZOffset + depth - 1, Direction.NORTH); + } + } + + // sides + for (int u = 0; u < depth; u++) { + for (int v = 0; v < height; v++) { + // left + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == depth - 1 || v == height - 1, + textureU - 1 + depth - u, textureV + depth + v, staticXOffset, staticYOffset + v, + staticZOffset + u, Direction.EAST); + // right + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == depth - 1 || v == height - 1, + textureU + depth + width + u, textureV + depth + v, staticXOffset + width - 1f, + staticYOffset + v, staticZOffset + u, Direction.WEST); + + } + } + // top/bottom + for (int u = 0; u < width; u++) { + for (int v = 0; v < depth; v++) { + // top + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == width - 1 || v == depth - 1, + textureU + depth + u, textureV + depth - 1 - v, staticXOffset + u, staticYOffset, + staticZOffset + v, Direction.UP); // Sides are flipped cause ?!? + // bottom + addPixel(natImage, cubes, pixelSize, u == 0 || v == 0 || u == width - 1 || v == depth - 1, + textureU + depth + width + u, textureV + depth - 1 - v, staticXOffset + u, + staticYOffset + height - 1f, staticZOffset + v, Direction.DOWN); // Sides are flipped cause ?!? + } + } + + return new CustomizableModelPart(cubes); + } + + private static int[][] offsets = new int[][] { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } }; + private static Direction[] hiddenDirN = new Direction[] { Direction.WEST, Direction.EAST, Direction.UP, + Direction.DOWN }; + private static Direction[] hiddenDirS = new Direction[] { Direction.EAST, Direction.WEST, Direction.UP, + Direction.DOWN }; + private static Direction[] hiddenDirW = new Direction[] { Direction.SOUTH, Direction.NORTH, Direction.UP, + Direction.DOWN }; + private static Direction[] hiddenDirE = new Direction[] { Direction.NORTH, Direction.SOUTH, Direction.UP, + Direction.DOWN }; + private static Direction[] hiddenDirUD = new Direction[] { Direction.EAST, Direction.WEST, Direction.NORTH, + Direction.SOUTH }; + + private static void addPixel(NativeImage natImage, List cubes, float pixelSize, boolean onBorder, int u, + int v, float x, float y, float z, Direction dir) { + if (natImage.getLuminanceOrAlpha(u, v) != 0) { + Set hide = new HashSet<>(); + if (!onBorder) { + for (int i = 0; i < offsets.length; i++) { + int tU = u + offsets[i][1]; + int tV = v + offsets[i][0]; + if (tU >= 0 && tU < 64 && tV >= 0 && tV < 64 && natImage.getLuminanceOrAlpha(tU, tV) != 0) { + if (dir == Direction.NORTH) + hide.add(hiddenDirN[i]); + if (dir == Direction.SOUTH) + hide.add(hiddenDirS[i]); + if (dir == Direction.EAST) + hide.add(hiddenDirE[i]); + if (dir == Direction.WEST) + hide.add(hiddenDirW[i]); + if (dir == Direction.UP || dir == Direction.DOWN) + hide.add(hiddenDirUD[i]); + } + } + hide.add(dir); + } + cubes.addAll(CustomizableCubeListBuilder.create().texOffs(u - 2, v - 1) + .addBox(x, y, z, pixelSize, hide.toArray(new Direction[hide.size()])).getCubes()); + // wrapper.setTextureOffset(u-2, v-1); + // wrapper.addCustomCuboid(x, y, z, pixelSize, pixelSize, pixelSize, + // hide.toArray(new Direction[hide.size()])); + } + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/BodyLayerFeatureRenderer.java b/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/BodyLayerFeatureRenderer.java new file mode 100644 index 0000000..11b7537 --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/BodyLayerFeatureRenderer.java @@ -0,0 +1,146 @@ +package net.silentclient.client.mods.render.skins.renderlayers; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.client.renderer.entity.layers.LayerRenderer; +import net.minecraft.entity.player.EnumPlayerModelParts; +import net.silentclient.client.Client; +import net.silentclient.client.mixin.accessors.skins.PlayerEntityModelAccessor; +import net.silentclient.client.mixin.accessors.skins.PlayerSettings; +import net.silentclient.client.mods.render.skins.SkinUtil; +import net.silentclient.client.mods.render.skins.SkinsMod; +import net.silentclient.client.mods.render.skins.render.CustomizableModelPart; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class BodyLayerFeatureRenderer + implements LayerRenderer { + + private RenderPlayer playerRenderer; + private final boolean thinArms; + private static final Minecraft mc = Minecraft.getMinecraft(); + + public BodyLayerFeatureRenderer( + RenderPlayer playerRenderer) { + this.playerRenderer = playerRenderer; + thinArms = ((PlayerEntityModelAccessor)playerRenderer).client$hasThinArms(); + bodyLayers.add(new Layer(0, false, EnumPlayerModelParts.LEFT_PANTS_LEG, Shape.LEGS, () -> playerRenderer.getMainModel().bipedLeftLeg, () -> Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Left Pants").getValBoolean())); + bodyLayers.add(new Layer(1, false, EnumPlayerModelParts.RIGHT_PANTS_LEG, Shape.LEGS, () -> playerRenderer.getMainModel().bipedRightLeg, () -> Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Right Pants").getValBoolean())); + bodyLayers.add(new Layer(2, false, EnumPlayerModelParts.LEFT_SLEEVE, thinArms ? Shape.ARMS_SLIM : Shape.ARMS, () -> playerRenderer.getMainModel().bipedLeftArm, () -> Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Left Sleeve").getValBoolean())); + bodyLayers.add(new Layer(3, true, EnumPlayerModelParts.RIGHT_SLEEVE, thinArms ? Shape.ARMS_SLIM : Shape.ARMS, () -> playerRenderer.getMainModel().bipedRightArm, () -> Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Right Sleeve").getValBoolean())); + bodyLayers.add(new Layer(4, false, EnumPlayerModelParts.JACKET, Shape.BODY, () -> playerRenderer.getMainModel().bipedBody, () -> Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Jacket").getValBoolean())); + } + + @Override + public void doRenderLayer(AbstractClientPlayer player, float paramFloat1, float paramFloat2, float paramFloat3, + float deltaTick, float paramFloat5, float paramFloat6, float paramFloat7) { + if (!player.hasSkin() || player.isInvisible()) { + return; + } + if(mc.theWorld == null) { + return; // in a menu or something and the model gets rendered + } + if(mc.thePlayer.getPositionVector().squareDistanceTo(player.getPositionVector()) > Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt()*Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt())return; + + PlayerSettings settings = (PlayerSettings) player; + // check for it being setup first to speedup the rendering + if(settings.client$getSkinLayers() == null && !setupModel(player, settings)) { + return; // no head layer setup and wasn't able to setup + } + + + //this.playerRenderer.bindTexture(player.getLocationSkin()); + renderLayers(player, (CustomizableModelPart[]) settings.client$getSkinLayers(), deltaTick); + } + + private boolean setupModel(AbstractClientPlayer abstractClientPlayerEntity, PlayerSettings settings) { + if(!SkinUtil.hasCustomSkin(abstractClientPlayerEntity)) { + return false; // default skin + } + SkinUtil.setup3dLayers(abstractClientPlayerEntity, settings, thinArms, null); + return true; + } + + private final List bodyLayers = new ArrayList<>(); + + class Layer{ + int layersId; + boolean mirrored; + EnumPlayerModelParts modelPart; + Shape shape; + Supplier vanillaGetter; + Supplier configGetter; + public Layer(int layersId, boolean mirrored, EnumPlayerModelParts modelPart, Shape shape, + Supplier vanillaGetter, Supplier configGetter) { + this.layersId = layersId; + this.mirrored = mirrored; + this.modelPart = modelPart; + this.shape = shape; + this.vanillaGetter = vanillaGetter; + this.configGetter = configGetter; + } + + } + + + private enum Shape { + HEAD(0), BODY(0.6f), LEGS(-0.2f), ARMS(0.4f), ARMS_SLIM(0.4f) + ; + + private final float yOffsetMagicValue; + + private Shape(float yOffsetMagicValue) { + this.yOffsetMagicValue = yOffsetMagicValue; + } + + } + + public void renderLayers(AbstractClientPlayer abstractClientPlayer, CustomizableModelPart[] layers, float deltaTick) { + if(layers == null)return; + float pixelScaling = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Voxel Size").getValFloat(); + float heightScaling = 1.035f; + float widthScaling = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Voxel Size").getValFloat(); + // Overlay refuses to work correctly, this is a workaround for now + boolean redTint = abstractClientPlayer.hurtTime > 0 || abstractClientPlayer.deathTime > 0; + for(Layer layer : bodyLayers) { + if(abstractClientPlayer.isWearing(layer.modelPart) && !layer.vanillaGetter.get().isHidden && layer.configGetter.get()) { + GlStateManager.pushMatrix(); + if(abstractClientPlayer.isSneaking()) { + GlStateManager.translate(0.0F, 0.2F, 0.0F); + } + layer.vanillaGetter.get().postRender(0.0625F); + if(layer.shape == Shape.ARMS) { + layers[layer.layersId].x = 0.998f*16; + } else if(layer.shape == Shape.ARMS_SLIM) { + layers[layer.layersId].x = 0.499f*16; + } + if(layer.shape == Shape.BODY) { + widthScaling = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Body Voxel Width Size").getValFloat(); + }else { + widthScaling = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Voxel Size").getValFloat(); + } + if(layer.mirrored) { + layers[layer.layersId].x *= -1; + } + GlStateManager.scale(0.0625, 0.0625, 0.0625); + GlStateManager.scale(widthScaling, heightScaling, pixelScaling); + layers[layer.layersId].y = layer.shape.yOffsetMagicValue; + + layers[layer.layersId].render(redTint); + GlStateManager.popMatrix(); + } + } + + } + + @Override + public boolean shouldCombineTextures() { + return false; + } + +} diff --git a/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/HeadLayerFeatureRenderer.java b/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/HeadLayerFeatureRenderer.java new file mode 100644 index 0000000..cb0782f --- /dev/null +++ b/src/main/java/net/silentclient/client/mods/render/skins/renderlayers/HeadLayerFeatureRenderer.java @@ -0,0 +1,90 @@ +package net.silentclient.client.mods.render.skins.renderlayers; + +import com.google.common.collect.Sets; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.client.renderer.entity.layers.LayerRenderer; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.silentclient.client.Client; +import net.silentclient.client.mixin.accessors.skins.PlayerEntityModelAccessor; +import net.silentclient.client.mixin.accessors.skins.PlayerSettings; +import net.silentclient.client.mods.render.skins.SkinUtil; +import net.silentclient.client.mods.render.skins.SkinsMod; + +import java.util.Set; + +public class HeadLayerFeatureRenderer implements LayerRenderer { + + private Set hideHeadLayers = Sets.newHashSet(Items.skull); + private final boolean thinArms; + private static final Minecraft mc = Minecraft.getMinecraft(); + private RenderPlayer playerRenderer; + + public HeadLayerFeatureRenderer(RenderPlayer playerRenderer) { + thinArms = ((PlayerEntityModelAccessor)playerRenderer).client$hasThinArms(); + this.playerRenderer = playerRenderer; + } + + @Override + public void doRenderLayer(AbstractClientPlayer player, float paramFloat1, float paramFloat2, float paramFloat3, + float deltaTick, float paramFloat5, float paramFloat6, float paramFloat7) { + if (!player.hasSkin() || player.isInvisible() || !Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "3D Hat").getValBoolean()) { + return; + } + if(mc.thePlayer.getPositionVector().squareDistanceTo(player.getPositionVector()) > Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt()*Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Level Of Detail Distance").getValInt())return; + + ItemStack itemStack = player.getEquipmentInSlot(1); //TODO + if (itemStack != null && hideHeadLayers.contains(itemStack.getItem())) { + return; + } + + PlayerSettings settings = (PlayerSettings) player; + // check for it being setup first to speedup the rendering + if(settings.client$getHeadLayers() == null && !setupModel(player, settings)) { + return; // no head layer setup and wasn't able to setup + } + + //this.playerRenderer.bindTexture(player.getLocationSkin()); + renderCustomHelmet(settings, player, deltaTick); + } + + private boolean setupModel(AbstractClientPlayer abstractClientPlayerEntity, PlayerSettings settings) { + + if(!SkinUtil.hasCustomSkin(abstractClientPlayerEntity)) { + return false; // default skin + } + SkinUtil.setup3dLayers(abstractClientPlayerEntity, settings, thinArms, null); + return true; + } + + public void renderCustomHelmet(PlayerSettings settings, AbstractClientPlayer abstractClientPlayer, float deltaTick) { + if(settings.client$getHeadLayers() == null)return; + if(playerRenderer.getMainModel().bipedHead.isHidden)return; + float voxelSize = Client.getInstance().getSettingsManager().getSettingByClass(SkinsMod.class, "Head Voxel Size").getValFloat(); + GlStateManager.pushMatrix(); + if(abstractClientPlayer.isSneaking()) { + GlStateManager.translate(0.0F, 0.2F, 0.0F); + } + playerRenderer.getMainModel().bipedHead.postRender(0.0625F); + //this.getParentModel().head.translateAndRotate(matrixStack); + GlStateManager.scale(0.0625, 0.0625, 0.0625); + GlStateManager.scale(voxelSize, voxelSize, voxelSize); + + // Overlay refuses to work correctly, this is a workaround for now + boolean tintRed = abstractClientPlayer.hurtTime > 0 || abstractClientPlayer.deathTime > 0; + settings.client$getHeadLayers().render(tintRed); + GlStateManager.popMatrix(); + + } + + @Override + public boolean shouldCombineTextures() { + return false; + } + + +} diff --git a/src/main/resources/mixins.SilentClient.json b/src/main/resources/mixins.SilentClient.json index 6d794e7..9a232d5 100644 --- a/src/main/resources/mixins.SilentClient.json +++ b/src/main/resources/mixins.SilentClient.json @@ -123,6 +123,9 @@ "mixins.emotes.EntityPlayerMixin", "mixins.emotes.MinecraftMixin", "mixins.emotes.RenderPlayerMixin", - "mixins.lwjgl.WindowsDisplayMixin" + "mixins.lwjgl.WindowsDisplayMixin", + "mixins.skins.PlayerMixin", + "mixins.skins.PlayerRendererMixin", + "mixins.skins.RendererLivingEntityMixin" ] } \ No newline at end of file