mirror of
https://github.com/refactorinqq/SLC-1.8.9.git
synced 2024-11-10 06:41:31 +01:00
(feature) 3d skins mod
This commit is contained in:
parent
3444529aa7
commit
9aeb693478
@ -11,12 +11,11 @@ import net.silentclient.client.gui.SilentScreen;
|
|||||||
import net.silentclient.client.gui.animation.SimpleAnimation;
|
import net.silentclient.client.gui.animation.SimpleAnimation;
|
||||||
import net.silentclient.client.gui.animation.normal.Direction;
|
import net.silentclient.client.gui.animation.normal.Direction;
|
||||||
import net.silentclient.client.gui.elements.Button;
|
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.elements.*;
|
||||||
import net.silentclient.client.gui.font.SilentFontRenderer;
|
import net.silentclient.client.gui.font.SilentFontRenderer;
|
||||||
import net.silentclient.client.gui.hud.HUDConfigScreen;
|
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.modmenu.CellGrid;
|
||||||
import net.silentclient.client.gui.theme.Theme;
|
import net.silentclient.client.gui.theme.Theme;
|
||||||
import net.silentclient.client.gui.theme.input.DefaultInputTheme;
|
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.Setting;
|
||||||
import net.silentclient.client.mods.render.crosshair.CrosshairMod;
|
import net.silentclient.client.mods.render.crosshair.CrosshairMod;
|
||||||
import net.silentclient.client.mods.world.TimeChangerMod;
|
import net.silentclient.client.mods.world.TimeChangerMod;
|
||||||
import net.silentclient.client.utils.ColorUtils;
|
import net.silentclient.client.utils.*;
|
||||||
import net.silentclient.client.utils.MenuBlurUtils;
|
|
||||||
import net.silentclient.client.utils.MouseCursorHandler;
|
|
||||||
import net.silentclient.client.utils.Sounds;
|
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
@ -45,6 +41,7 @@ public class ModSettings extends SilentScreen {
|
|||||||
|
|
||||||
public double scrollY;
|
public double scrollY;
|
||||||
public static SimpleAnimation scrollAnimation = new SimpleAnimation(0.0F);
|
public static SimpleAnimation scrollAnimation = new SimpleAnimation(0.0F);
|
||||||
|
private ScrollHelper scrollHelper = new ScrollHelper();
|
||||||
|
|
||||||
public ModSettings(Mod mod, GuiScreen parent) {
|
public ModSettings(Mod mod, GuiScreen parent) {
|
||||||
if (mod == null) throw new IllegalArgumentException("Mod is null");
|
if (mod == null) throw new IllegalArgumentException("Mod is null");
|
||||||
@ -130,8 +127,13 @@ public class ModSettings extends SilentScreen {
|
|||||||
|
|
||||||
GL11.glPushMatrix();
|
GL11.glPushMatrix();
|
||||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
scrollHelper.setStep(5);
|
||||||
int settingY = (int) (y + 25 + scrollAnimation.getValue() + mod.customComponentLiteHeight());
|
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();
|
GL11.glPopMatrix();
|
||||||
|
|
||||||
@ -145,8 +147,8 @@ public class ModSettings extends SilentScreen {
|
|||||||
int translatedY = r.getScaledHeight() - y - height;
|
int translatedY = r.getScaledHeight() - y - height;
|
||||||
GL11.glScissor(x * s, translatedY * s, this.width * s, height * s);
|
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);
|
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 + scrollAnimation.getValue()), width, height, mouseX, mouseY);
|
MouseCursorHandler.CursorType cursorTypeCustom = mod.renderCustomLiteComponent(x + 100, (int) (y + 25 + scrollY), width, height, mouseX, mouseY);
|
||||||
if(cursorTypeCustom != null) {
|
if(cursorTypeCustom != null) {
|
||||||
cursorType = cursorTypeCustom;
|
cursorType = cursorTypeCustom;
|
||||||
}
|
}
|
||||||
@ -230,13 +232,13 @@ public class ModSettings extends SilentScreen {
|
|||||||
settingY += settingHeight;
|
settingY += settingHeight;
|
||||||
}
|
}
|
||||||
if(mod.getCategory() == ModCategory.MODS) {
|
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);
|
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 + scrollAnimation.getValue(), 10, 10, "Reset");
|
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 + scrollAnimation.getValue(), 10, 10)) {
|
if(MouseUtils.isInside(mouseX, mouseY, x + width - (10 + 8) - 15, y + 5 + scrollY, 10, 10)) {
|
||||||
cursorType = MouseCursorHandler.CursorType.POINTER;
|
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);
|
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 + scrollAnimation.getValue())) {
|
if(Switch.isHovered(mouseX, mouseY, x + width - (10 + 8), y + 6 + scrollY)) {
|
||||||
cursorType = MouseCursorHandler.CursorType.POINTER;
|
cursorType = MouseCursorHandler.CursorType.POINTER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,45 +247,45 @@ public class ModSettings extends SilentScreen {
|
|||||||
|
|
||||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
final Scroll scroll = MouseUtils.scroll();
|
// final Scroll scroll = MouseUtils.scroll();
|
||||||
|
//
|
||||||
if(scroll != null) {
|
// if(scroll != null) {
|
||||||
switch (scroll) {
|
// switch (scroll) {
|
||||||
case DOWN:
|
// case DOWN:
|
||||||
if(scrollY > -((settingIndex - 13.5) * 38)) {
|
// if(scrollY > -((settingIndex - 13.5) * 38)) {
|
||||||
scrollY -=12;
|
// scrollY -=12;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if(settingIndex > 13) {
|
// if(settingIndex > 13) {
|
||||||
if(scrollY < -((settingIndex - 15) * 38)) {
|
// if(scrollY < -((settingIndex - 15) * 38)) {
|
||||||
scrollY = -((settingIndex - 14.1) * 38);
|
// scrollY = -((settingIndex - 14.1) * 38);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if(mod.customComponentLiteHeight() > height - 30) {
|
// if(mod.customComponentLiteHeight() > height - 30) {
|
||||||
if(scrollY > -((mod.customComponentLiteHeight() - 13.5) * 38)) {
|
// if(scrollY > -((mod.customComponentLiteHeight() - 13.5) * 38)) {
|
||||||
scrollY -=12;
|
// scrollY -=12;
|
||||||
}
|
// }
|
||||||
if(scrollY < -((mod.customComponentLiteHeight() - 15) * 38)) {
|
// if(scrollY < -((mod.customComponentLiteHeight() - 15) * 38)) {
|
||||||
scrollY = -((mod.customComponentLiteHeight() - 14.1) * 38);
|
// scrollY = -((mod.customComponentLiteHeight() - 14.1) * 38);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
case UP:
|
// case UP:
|
||||||
if(scrollY < -10) {
|
// if(scrollY < -10) {
|
||||||
scrollY +=12;
|
// scrollY +=12;
|
||||||
}else {
|
// }else {
|
||||||
if(settingIndex > 13) {
|
// if(settingIndex > 13) {
|
||||||
scrollY = 0;
|
// scrollY = 0;
|
||||||
}
|
// }
|
||||||
if(mod.customComponentLiteHeight() > height - 30) {
|
// if(mod.customComponentLiteHeight() > height - 30) {
|
||||||
scrollY = 0;
|
// scrollY = 0;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
scrollAnimation.setAnimation((float) scrollY, 16);
|
// scrollAnimation.setAnimation((float) scrollY, 16);
|
||||||
|
|
||||||
if(ClickGUI.close) {
|
if(ClickGUI.close) {
|
||||||
ClickGUI.introAnimation.setDirection(Direction.BACKWARDS);
|
ClickGUI.introAnimation.setDirection(Direction.BACKWARDS);
|
||||||
@ -302,22 +304,22 @@ public class ModSettings extends SilentScreen {
|
|||||||
@Override
|
@Override
|
||||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
|
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
|
||||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||||
|
float scrollY = scrollHelper.getScroll();
|
||||||
int addX = 190;
|
int addX = 190;
|
||||||
int addY = 110;
|
int addY = 110;
|
||||||
|
|
||||||
int x = (width / 2) - addX;
|
int x = (width / 2) - addX;
|
||||||
int y = (height / 2) - addY;
|
int y = (height / 2) - addY;
|
||||||
int width = addX * 2;
|
int width = addX * 2;
|
||||||
int settingY = (int) (y + 25 + scrollAnimation.getValue() + mod.customComponentLiteHeight());
|
int settingY = (int) (y + 25 + scrollY + mod.customComponentLiteHeight());
|
||||||
String category = "";
|
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();
|
Sounds.playButtonSound();
|
||||||
mod.reset(false);
|
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();
|
Sounds.playButtonSound();
|
||||||
mod.toggle();
|
mod.toggle();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package net.silentclient.client.mixin.accessors.skins;
|
||||||
|
|
||||||
|
public interface SkullModelAccessor {
|
||||||
|
|
||||||
|
public void showHat(boolean val);
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<AbstractClientPlayer> 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 = "<init>*", 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();
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
@ -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<T extends EntityLivingBase> {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@ import net.silentclient.client.mods.hypixel.togglechat.ToggleChatMod;
|
|||||||
import net.silentclient.client.mods.player.*;
|
import net.silentclient.client.mods.player.*;
|
||||||
import net.silentclient.client.mods.render.*;
|
import net.silentclient.client.mods.render.*;
|
||||||
import net.silentclient.client.mods.render.crosshair.CrosshairMod;
|
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.CosmeticsMod;
|
||||||
import net.silentclient.client.mods.settings.FPSBoostMod;
|
import net.silentclient.client.mods.settings.FPSBoostMod;
|
||||||
import net.silentclient.client.mods.settings.GeneralMod;
|
import net.silentclient.client.mods.settings.GeneralMod;
|
||||||
@ -189,6 +190,7 @@ public class ModInstances {
|
|||||||
}
|
}
|
||||||
mods.add(new QuickPlayMod());
|
mods.add(new QuickPlayMod());
|
||||||
mods.add(new SoundsMod());
|
mods.add(new SoundsMod());
|
||||||
|
mods.add(new SkinsMod());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postInit() {
|
public void postInit() {
|
||||||
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<MinecraftProfileTexture.Type, MinecraftProfileTexture> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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<ItemStack, SkullSettings> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<CustomizableCube> 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<CustomizableCube> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<CustomizableCube> cubes;
|
||||||
|
|
||||||
|
public CustomizableModelPart(List<CustomizableCube> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<CustomizableCube> 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<CustomizableCube> cubes, float pixelSize, boolean onBorder, int u,
|
||||||
|
int v, float x, float y, float z, Direction dir) {
|
||||||
|
if (natImage.getLuminanceOrAlpha(u, v) != 0) {
|
||||||
|
Set<Direction> 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()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<AbstractClientPlayer> {
|
||||||
|
|
||||||
|
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<Layer> bodyLayers = new ArrayList<>();
|
||||||
|
|
||||||
|
class Layer{
|
||||||
|
int layersId;
|
||||||
|
boolean mirrored;
|
||||||
|
EnumPlayerModelParts modelPart;
|
||||||
|
Shape shape;
|
||||||
|
Supplier<ModelRenderer> vanillaGetter;
|
||||||
|
Supplier<Boolean> configGetter;
|
||||||
|
public Layer(int layersId, boolean mirrored, EnumPlayerModelParts modelPart, Shape shape,
|
||||||
|
Supplier<ModelRenderer> vanillaGetter, Supplier<Boolean> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<AbstractClientPlayer> {
|
||||||
|
|
||||||
|
private Set<Item> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -123,6 +123,9 @@
|
|||||||
"mixins.emotes.EntityPlayerMixin",
|
"mixins.emotes.EntityPlayerMixin",
|
||||||
"mixins.emotes.MinecraftMixin",
|
"mixins.emotes.MinecraftMixin",
|
||||||
"mixins.emotes.RenderPlayerMixin",
|
"mixins.emotes.RenderPlayerMixin",
|
||||||
"mixins.lwjgl.WindowsDisplayMixin"
|
"mixins.lwjgl.WindowsDisplayMixin",
|
||||||
|
"mixins.skins.PlayerMixin",
|
||||||
|
"mixins.skins.PlayerRendererMixin",
|
||||||
|
"mixins.skins.RendererLivingEntityMixin"
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user